target-arm queue:
* support --semihosting-config,arg=value * Cortex-R5 support (including implementing them on the Zynq board) * Cortex-M4 support (without FPU) * enable vfio-calxeda-xgmac * don't reset ALIAS sysregs -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJVhBwiAAoJEDwlJe0UNgzebgAQAKi29rPmzj2aC/nVpW/SafH1 ZK3b8aaq7vj68wavlmE7l1pXx6d7DkKftp28LWgH+2FQ6pjDqqRQz9V5KE+jUPI9 ZbEIRWlNTr4kGsGiJqla5LEfxlamvjoY0PmeY2GZaU9xdq0z39uB8tofnk5V7bDG 3oK20TV596mgurSclbtyWV8gdtBak/kr0ABfQ/ndc8rZKDUS4ASQqZ57VunbWfwZ qVBaosjJuQkP/5kEmhWuLnl0EESB2b11dim52EJKLtdYU/4o5q5eCJ/hf1vQrr1V ax7ZP+7IYGT1CWry/T4SD9Oy9A/5HTa9xT0C3qurgH9sQ+61VSqqZgTpYfOoNTBp bKB9/0ujRMD/tcJICV0TU2HdVCrgEgIAFMDGquT/KRe/TTvW/KU1ByIEIByxJ+uT cqNnGCikfk42bVqeQmENDfKJJKhvhord7mogE6QG1IiSAliukOstl/Kbh/Rz5sDB 7h/eXEI16bTtCHKTIpMIN9lVfaM9/H7jAPy4DmmMxwmOao4FdEo8czVR4Jf2haF0 zkhnyxywDBsXW+69Q/BRLHByK8y2U8wPFyl6039VtE7LwEpSoHlAHrhsgIWa6ItV G0gL8dWEJJOwkmufYoj9VHggBAuYJVcZv6nadO+3uwpo5u5y6vR7wqYmwdM8Q7yC UrlPqpZs4yz9JO8hbDoZ =Utk4 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20150619' into staging target-arm queue: * support --semihosting-config,arg=value * Cortex-R5 support (including implementing them on the Zynq board) * Cortex-M4 support (without FPU) * enable vfio-calxeda-xgmac * don't reset ALIAS sysregs # gpg: Signature made Fri Jun 19 14:41:54 2015 BST using RSA key ID 14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" * remotes/pmaydell/tags/pull-target-arm-20150619: semihosting: add --semihosting-config arg sub-argument semihosting: create SemihostingConfig structure and semihost.h arm: xlnx-zynqmp: Add 2xCortexR5 CPUs arm: xlnx-zynqmp: Add boot-cpu property arm: xlnx-zynqmp: Preface CPU variables with "apu" target-arm: Add support for Cortex-R5 target-arm: Implement PMSAv7 MPU target-arm: Add registers for PMSAv7 target-arm/helper.c: define MPUIR register target-arm: Do not reset sysregs marked as ALIAS hw/arm/sysbus-fdt: enable vfio-calxeda-xgmac dynamic instantiation target-arm: Add the Cortex-M4 CPU Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
799810fb28
@ -40,6 +40,7 @@
|
||||
#include "cpu.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "exec/semihost.h"
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#define GDB_ATTACHED "0"
|
||||
@ -323,8 +324,6 @@ static GDBState *gdbserver_state;
|
||||
|
||||
bool gdb_has_xml;
|
||||
|
||||
int semihosting_target = SEMIHOSTING_TARGET_AUTO;
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* XXX: This is not thread safe. Do we care? */
|
||||
static int gdbserver_fd = -1;
|
||||
@ -362,10 +361,11 @@ static enum {
|
||||
/* Decide if either remote gdb syscalls or native file IO should be used. */
|
||||
int use_gdb_syscalls(void)
|
||||
{
|
||||
if (semihosting_target == SEMIHOSTING_TARGET_NATIVE) {
|
||||
SemihostingTarget target = semihosting_get_target();
|
||||
if (target == SEMIHOSTING_TARGET_NATIVE) {
|
||||
/* -semihosting-config target=native */
|
||||
return false;
|
||||
} else if (semihosting_target == SEMIHOSTING_TARGET_GDB) {
|
||||
} else if (target == SEMIHOSTING_TARGET_GDB) {
|
||||
/* -semihosting-config target=gdb */
|
||||
return true;
|
||||
}
|
||||
|
@ -26,6 +26,9 @@
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/vfio/vfio-platform.h"
|
||||
#include "hw/vfio/vfio-calxeda-xgmac.h"
|
||||
#include "hw/arm/fdt.h"
|
||||
|
||||
/*
|
||||
* internal struct that contains the information to create dynamic
|
||||
@ -53,11 +56,81 @@ typedef struct NodeCreationPair {
|
||||
int (*add_fdt_node_fn)(SysBusDevice *sbdev, void *opaque);
|
||||
} NodeCreationPair;
|
||||
|
||||
/* Device Specific Code */
|
||||
|
||||
/**
|
||||
* add_calxeda_midway_xgmac_fdt_node
|
||||
*
|
||||
* Generates a simple node with following properties:
|
||||
* compatible string, regs, interrupts, dma-coherent
|
||||
*/
|
||||
static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque)
|
||||
{
|
||||
PlatformBusFDTData *data = opaque;
|
||||
PlatformBusDevice *pbus = data->pbus;
|
||||
void *fdt = data->fdt;
|
||||
const char *parent_node = data->pbus_node_name;
|
||||
int compat_str_len, i, ret = -1;
|
||||
char *nodename;
|
||||
uint32_t *irq_attr, *reg_attr;
|
||||
uint64_t mmio_base, irq_number;
|
||||
VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
|
||||
VFIODevice *vbasedev = &vdev->vbasedev;
|
||||
|
||||
mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
|
||||
nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node,
|
||||
vbasedev->name, mmio_base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
|
||||
compat_str_len = strlen(vdev->compat) + 1;
|
||||
qemu_fdt_setprop(fdt, nodename, "compatible",
|
||||
vdev->compat, compat_str_len);
|
||||
|
||||
qemu_fdt_setprop(fdt, nodename, "dma-coherent", "", 0);
|
||||
|
||||
reg_attr = g_new(uint32_t, vbasedev->num_regions * 2);
|
||||
for (i = 0; i < vbasedev->num_regions; i++) {
|
||||
mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
|
||||
reg_attr[2 * i] = cpu_to_be32(mmio_base);
|
||||
reg_attr[2 * i + 1] = cpu_to_be32(
|
||||
memory_region_size(&vdev->regions[i]->mem));
|
||||
}
|
||||
ret = qemu_fdt_setprop(fdt, nodename, "reg", reg_attr,
|
||||
vbasedev->num_regions * 2 * sizeof(uint32_t));
|
||||
if (ret) {
|
||||
error_report("could not set reg property of node %s", nodename);
|
||||
goto fail_reg;
|
||||
}
|
||||
|
||||
irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3);
|
||||
for (i = 0; i < vbasedev->num_irqs; i++) {
|
||||
irq_number = platform_bus_get_irqn(pbus, sbdev , i)
|
||||
+ data->irq_start;
|
||||
irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI);
|
||||
irq_attr[3 * i + 1] = cpu_to_be32(irq_number);
|
||||
irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI);
|
||||
}
|
||||
ret = qemu_fdt_setprop(fdt, nodename, "interrupts",
|
||||
irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t));
|
||||
if (ret) {
|
||||
error_report("could not set interrupts property of node %s",
|
||||
nodename);
|
||||
}
|
||||
g_free(irq_attr);
|
||||
fail_reg:
|
||||
g_free(reg_attr);
|
||||
g_free(nodename);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* list of supported dynamic sysbus devices */
|
||||
static const NodeCreationPair add_fdt_node_functions[] = {
|
||||
{TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node},
|
||||
{"", NULL}, /* last element */
|
||||
};
|
||||
|
||||
/* Generic Code */
|
||||
|
||||
/**
|
||||
* add_fdt_node - add the device tree node of a dynamic sysbus device
|
||||
*
|
||||
|
@ -47,21 +47,11 @@
|
||||
#include "hw/arm/virt-acpi-build.h"
|
||||
#include "hw/arm/sysbus-fdt.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "hw/arm/fdt.h"
|
||||
|
||||
/* Number of external interrupt lines to configure the GIC with */
|
||||
#define NUM_IRQS 256
|
||||
|
||||
#define GIC_FDT_IRQ_TYPE_SPI 0
|
||||
#define GIC_FDT_IRQ_TYPE_PPI 1
|
||||
|
||||
#define GIC_FDT_IRQ_FLAGS_EDGE_LO_HI 1
|
||||
#define GIC_FDT_IRQ_FLAGS_EDGE_HI_LO 2
|
||||
#define GIC_FDT_IRQ_FLAGS_LEVEL_HI 4
|
||||
#define GIC_FDT_IRQ_FLAGS_LEVEL_LO 8
|
||||
|
||||
#define GIC_FDT_IRQ_PPI_CPU_START 8
|
||||
#define GIC_FDT_IRQ_PPI_CPU_WIDTH 8
|
||||
|
||||
#define PLATFORM_BUS_NUM_IRQS 64
|
||||
|
||||
static ARMPlatformBusSystemParams platform_bus_params;
|
||||
|
@ -65,7 +65,7 @@ static void xlnx_ep108_init(MachineState *machine)
|
||||
xlnx_ep108_binfo.kernel_cmdline = machine->kernel_cmdline;
|
||||
xlnx_ep108_binfo.initrd_filename = machine->initrd_filename;
|
||||
xlnx_ep108_binfo.loader_start = 0;
|
||||
arm_load_kernel(&s->soc.cpu[0], &xlnx_ep108_binfo);
|
||||
arm_load_kernel(s->soc.boot_cpu_ptr, &xlnx_ep108_binfo);
|
||||
}
|
||||
|
||||
static QEMUMachine xlnx_ep108_machine = {
|
||||
|
@ -64,10 +64,17 @@ static void xlnx_zynqmp_init(Object *obj)
|
||||
XlnxZynqMPState *s = XLNX_ZYNQMP(obj);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_CPUS; i++) {
|
||||
object_initialize(&s->cpu[i], sizeof(s->cpu[i]),
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_APU_CPUS; i++) {
|
||||
object_initialize(&s->apu_cpu[i], sizeof(s->apu_cpu[i]),
|
||||
"cortex-a53-" TYPE_ARM_CPU);
|
||||
object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpu[i]),
|
||||
object_property_add_child(obj, "apu-cpu[*]", OBJECT(&s->apu_cpu[i]),
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_RPU_CPUS; i++) {
|
||||
object_initialize(&s->rpu_cpu[i], sizeof(s->rpu_cpu[i]),
|
||||
"cortex-r5-" TYPE_ARM_CPU);
|
||||
object_property_add_child(obj, "rpu-cpu[*]", OBJECT(&s->rpu_cpu[i]),
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
@ -90,12 +97,13 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||
XlnxZynqMPState *s = XLNX_ZYNQMP(dev);
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
uint8_t i;
|
||||
const char *boot_cpu = s->boot_cpu ? s->boot_cpu : "apu-cpu[0]";
|
||||
qemu_irq gic_spi[GIC_NUM_SPI_INTR];
|
||||
Error *err = NULL;
|
||||
|
||||
qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", GIC_NUM_SPI_INTR + 32);
|
||||
qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2);
|
||||
qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", XLNX_ZYNQMP_NUM_CPUS);
|
||||
qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", XLNX_ZYNQMP_NUM_APU_CPUS);
|
||||
object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate((errp), (err));
|
||||
@ -121,38 +129,77 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_CPUS; i++) {
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_APU_CPUS; i++) {
|
||||
qemu_irq irq;
|
||||
char *name;
|
||||
|
||||
object_property_set_int(OBJECT(&s->cpu[i]), QEMU_PSCI_CONDUIT_SMC,
|
||||
object_property_set_int(OBJECT(&s->apu_cpu[i]), QEMU_PSCI_CONDUIT_SMC,
|
||||
"psci-conduit", &error_abort);
|
||||
if (i > 0) {
|
||||
|
||||
name = object_get_canonical_path_component(OBJECT(&s->apu_cpu[i]));
|
||||
if (strcmp(name, boot_cpu)) {
|
||||
/* Secondary CPUs start in PSCI powered-down state */
|
||||
object_property_set_bool(OBJECT(&s->cpu[i]), true,
|
||||
object_property_set_bool(OBJECT(&s->apu_cpu[i]), true,
|
||||
"start-powered-off", &error_abort);
|
||||
} else {
|
||||
s->boot_cpu_ptr = &s->apu_cpu[i];
|
||||
}
|
||||
|
||||
object_property_set_int(OBJECT(&s->cpu[i]), GIC_BASE_ADDR,
|
||||
object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR,
|
||||
"reset-cbar", &err);
|
||||
if (err) {
|
||||
error_propagate((errp), (err));
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->cpu[i]), true, "realized", &err);
|
||||
object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized",
|
||||
&err);
|
||||
if (err) {
|
||||
error_propagate((errp), (err));
|
||||
return;
|
||||
}
|
||||
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i,
|
||||
qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ));
|
||||
qdev_get_gpio_in(DEVICE(&s->apu_cpu[i]),
|
||||
ARM_CPU_IRQ));
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->gic),
|
||||
arm_gic_ppi_index(i, ARM_PHYS_TIMER_PPI));
|
||||
qdev_connect_gpio_out(DEVICE(&s->cpu[i]), 0, irq);
|
||||
qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), 0, irq);
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->gic),
|
||||
arm_gic_ppi_index(i, ARM_VIRT_TIMER_PPI));
|
||||
qdev_connect_gpio_out(DEVICE(&s->cpu[i]), 1, irq);
|
||||
qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), 1, irq);
|
||||
}
|
||||
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_RPU_CPUS; i++) {
|
||||
char *name;
|
||||
|
||||
name = object_get_canonical_path_component(OBJECT(&s->rpu_cpu[i]));
|
||||
if (strcmp(name, boot_cpu)) {
|
||||
/* Secondary CPUs start in PSCI powered-down state */
|
||||
object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true,
|
||||
"start-powered-off", &error_abort);
|
||||
} else {
|
||||
s->boot_cpu_ptr = &s->rpu_cpu[i];
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "reset-hivecs",
|
||||
&err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "realized",
|
||||
&err);
|
||||
if (err) {
|
||||
error_propagate((errp), (err));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!s->boot_cpu_ptr) {
|
||||
error_setg(errp, "ZynqMP Boot cpu %s not found\n", boot_cpu);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < GIC_NUM_SPI_INTR; i++) {
|
||||
@ -188,10 +235,16 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
static Property xlnx_zynqmp_props[] = {
|
||||
DEFINE_PROP_STRING("boot-cpu", XlnxZynqMPState, boot_cpu),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->props = xlnx_zynqmp_props;
|
||||
dc->realize = xlnx_zynqmp_realize;
|
||||
}
|
||||
|
||||
|
@ -95,10 +95,4 @@ extern bool gdb_has_xml;
|
||||
/* in gdbstub-xml.c, generated by scripts/feature_to_c.sh */
|
||||
extern const char *const xml_builtin[][2];
|
||||
|
||||
/* Command line option defining whether semihosting should go via gdb or not */
|
||||
extern int semihosting_target;
|
||||
#define SEMIHOSTING_TARGET_AUTO 0
|
||||
#define SEMIHOSTING_TARGET_NATIVE 1
|
||||
#define SEMIHOSTING_TARGET_GDB 2
|
||||
|
||||
#endif
|
||||
|
62
include/exec/semihost.h
Normal file
62
include/exec/semihost.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Semihosting support
|
||||
*
|
||||
* Copyright (c) 2015 Imagination Technologies
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SEMIHOST_H
|
||||
#define SEMIHOST_H
|
||||
|
||||
typedef enum SemihostingTarget {
|
||||
SEMIHOSTING_TARGET_AUTO = 0,
|
||||
SEMIHOSTING_TARGET_NATIVE,
|
||||
SEMIHOSTING_TARGET_GDB
|
||||
} SemihostingTarget;
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
static inline bool semihosting_enabled(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline SemihostingTarget semihosting_get_target(void)
|
||||
{
|
||||
return SEMIHOSTING_TARGET_AUTO;
|
||||
}
|
||||
|
||||
static inline const char *semihosting_get_arg(int i)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int semihosting_get_argc(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline const char *semihosting_get_cmdline(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
bool semihosting_enabled(void);
|
||||
SemihostingTarget semihosting_get_target(void);
|
||||
const char *semihosting_get_arg(int i);
|
||||
int semihosting_get_argc(void);
|
||||
const char *semihosting_get_cmdline(void);
|
||||
#endif
|
||||
|
||||
#endif
|
34
include/hw/arm/fdt.h
Normal file
34
include/hw/arm/fdt.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2015 Linaro Limited
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Define macros useful when building ARM device tree nodes
|
||||
*/
|
||||
|
||||
#ifndef QEMU_ARM_FDT_H
|
||||
#define QEMU_ARM_FDT_H
|
||||
|
||||
#define GIC_FDT_IRQ_TYPE_SPI 0
|
||||
#define GIC_FDT_IRQ_TYPE_PPI 1
|
||||
|
||||
#define GIC_FDT_IRQ_FLAGS_EDGE_LO_HI 1
|
||||
#define GIC_FDT_IRQ_FLAGS_EDGE_HI_LO 2
|
||||
#define GIC_FDT_IRQ_FLAGS_LEVEL_HI 4
|
||||
#define GIC_FDT_IRQ_FLAGS_LEVEL_LO 8
|
||||
|
||||
#define GIC_FDT_IRQ_PPI_CPU_START 8
|
||||
#define GIC_FDT_IRQ_PPI_CPU_WIDTH 8
|
||||
|
||||
#endif
|
@ -27,7 +27,8 @@
|
||||
#define XLNX_ZYNQMP(obj) OBJECT_CHECK(XlnxZynqMPState, (obj), \
|
||||
TYPE_XLNX_ZYNQMP)
|
||||
|
||||
#define XLNX_ZYNQMP_NUM_CPUS 4
|
||||
#define XLNX_ZYNQMP_NUM_APU_CPUS 4
|
||||
#define XLNX_ZYNQMP_NUM_RPU_CPUS 2
|
||||
#define XLNX_ZYNQMP_NUM_GEMS 4
|
||||
#define XLNX_ZYNQMP_NUM_UARTS 2
|
||||
|
||||
@ -47,11 +48,15 @@ typedef struct XlnxZynqMPState {
|
||||
DeviceState parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
ARMCPU cpu[XLNX_ZYNQMP_NUM_CPUS];
|
||||
ARMCPU apu_cpu[XLNX_ZYNQMP_NUM_APU_CPUS];
|
||||
ARMCPU rpu_cpu[XLNX_ZYNQMP_NUM_RPU_CPUS];
|
||||
GICState gic;
|
||||
MemoryRegion gic_mr[XLNX_ZYNQMP_GIC_REGIONS][XLNX_ZYNQMP_GIC_ALIASES];
|
||||
CadenceGEMState gem[XLNX_ZYNQMP_NUM_GEMS];
|
||||
CadenceUARTState uart[XLNX_ZYNQMP_NUM_UARTS];
|
||||
|
||||
char *boot_cpu;
|
||||
ARMCPU *boot_cpu_ptr;
|
||||
} XlnxZynqMPState;
|
||||
|
||||
#define XLNX_ZYNQMP_H
|
||||
|
@ -126,7 +126,6 @@ extern int cursor_hide;
|
||||
extern int graphic_rotate;
|
||||
extern int no_quit;
|
||||
extern int no_shutdown;
|
||||
extern int semihosting_enabled;
|
||||
extern int old_param;
|
||||
extern int boot_menu;
|
||||
extern bool boot_strict;
|
||||
|
@ -3351,14 +3351,25 @@ STEXI
|
||||
Enable semihosting mode (ARM, M68K, Xtensa only).
|
||||
ETEXI
|
||||
DEF("semihosting-config", HAS_ARG, QEMU_OPTION_semihosting_config,
|
||||
"-semihosting-config [enable=on|off,]target=native|gdb|auto semihosting configuration\n",
|
||||
"-semihosting-config [enable=on|off][,target=native|gdb|auto][,arg=str[,...]]\n" \
|
||||
" semihosting configuration\n",
|
||||
QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32)
|
||||
STEXI
|
||||
@item -semihosting-config [enable=on|off,]target=native|gdb|auto
|
||||
@item -semihosting-config [enable=on|off][,target=native|gdb|auto][,arg=str[,...]]
|
||||
@findex -semihosting-config
|
||||
Enable semihosting and define where the semihosting calls will be addressed,
|
||||
to QEMU (@code{native}) or to GDB (@code{gdb}). The default is @code{auto}, which means
|
||||
@code{gdb} during debug sessions and @code{native} otherwise (ARM, M68K, Xtensa only).
|
||||
Enable and configure semihosting (ARM, M68K, Xtensa only).
|
||||
@table @option
|
||||
@item target=@code{native|gdb|auto}
|
||||
Defines where the semihosting calls will be addressed, to QEMU (@code{native})
|
||||
or to GDB (@code{gdb}). The default is @code{auto}, which means @code{gdb}
|
||||
during debug sessions and @code{native} otherwise.
|
||||
@item arg=@var{str1},arg=@var{str2},...
|
||||
Allows the user to pass input arguments, and can be used multiple times to build
|
||||
up a list. The old-style @code{-kernel}/@code{-append} method of passing a
|
||||
command line is still supported for backward compatibility. If both the
|
||||
@code{--semihosting-config arg} and the @code{-kernel}/@code{-append} are
|
||||
specified, the former is passed to semihosting as it always takes precedence.
|
||||
@end table
|
||||
ETEXI
|
||||
DEF("old-param", 0, QEMU_OPTION_old_param,
|
||||
"-old-param old param mode\n", QEMU_ARCH_ARM)
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <time.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "exec/semihost.h"
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#include "qemu.h"
|
||||
|
||||
@ -440,10 +441,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
input_size = arg1;
|
||||
/* Compute the size of the output string. */
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
output_size = strlen(ts->boot_info->kernel_filename)
|
||||
+ 1 /* Separating space. */
|
||||
+ strlen(ts->boot_info->kernel_cmdline)
|
||||
+ 1; /* Terminating null byte. */
|
||||
output_size = strlen(semihosting_get_cmdline()) + 1;
|
||||
#else
|
||||
unsigned int i;
|
||||
|
||||
@ -474,9 +472,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
|
||||
|
||||
/* Copy the command-line arguments. */
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename);
|
||||
pstrcat(output_buffer, output_size, " ");
|
||||
pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline);
|
||||
pstrcpy(output_buffer, output_size, semihosting_get_cmdline());
|
||||
#else
|
||||
if (output_size == 1) {
|
||||
/* Empty command-line. */
|
||||
|
@ -105,6 +105,8 @@ typedef struct ARMCPU {
|
||||
|
||||
/* CPU has memory protection unit */
|
||||
bool has_mpu;
|
||||
/* PMSAv7 MPU number of supported regions */
|
||||
uint32_t pmsav7_dregion;
|
||||
|
||||
/* PSCI conduit used to invoke PSCI methods
|
||||
* 0 - disabled, 1 - smc, 2 - hvc
|
||||
|
@ -55,7 +55,7 @@ static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque)
|
||||
ARMCPRegInfo *ri = value;
|
||||
ARMCPU *cpu = opaque;
|
||||
|
||||
if (ri->type & ARM_CP_SPECIAL) {
|
||||
if (ri->type & (ARM_CP_SPECIAL | ARM_CP_ALIAS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -457,6 +457,9 @@ static Property arm_cpu_has_el3_property =
|
||||
static Property arm_cpu_has_mpu_property =
|
||||
DEFINE_PROP_BOOL("has-mpu", ARMCPU, has_mpu, true);
|
||||
|
||||
static Property arm_cpu_pmsav7_dregion_property =
|
||||
DEFINE_PROP_UINT32("pmsav7-dregion", ARMCPU, pmsav7_dregion, 16);
|
||||
|
||||
static void arm_cpu_post_init(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
@ -488,6 +491,11 @@ static void arm_cpu_post_init(Object *obj)
|
||||
if (arm_feature(&cpu->env, ARM_FEATURE_MPU)) {
|
||||
qdev_property_add_static(DEVICE(obj), &arm_cpu_has_mpu_property,
|
||||
&error_abort);
|
||||
if (arm_feature(&cpu->env, ARM_FEATURE_V7)) {
|
||||
qdev_property_add_static(DEVICE(obj),
|
||||
&arm_cpu_pmsav7_dregion_property,
|
||||
&error_abort);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -580,6 +588,22 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
unset_feature(env, ARM_FEATURE_MPU);
|
||||
}
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_MPU) &&
|
||||
arm_feature(env, ARM_FEATURE_V7)) {
|
||||
uint32_t nr = cpu->pmsav7_dregion;
|
||||
|
||||
if (nr > 0xff) {
|
||||
error_setg(errp, "PMSAv7 MPU #regions invalid %" PRIu32 "\n", nr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nr) {
|
||||
env->pmsav7.drbar = g_new0(uint32_t, nr);
|
||||
env->pmsav7.drsr = g_new0(uint32_t, nr);
|
||||
env->pmsav7.dracr = g_new0(uint32_t, nr);
|
||||
}
|
||||
}
|
||||
|
||||
register_cp_regs_for_features(cpu);
|
||||
arm_cpu_register_gdb_regs_for_features(cpu);
|
||||
|
||||
@ -812,6 +836,15 @@ static void cortex_m3_initfn(Object *obj)
|
||||
cpu->midr = 0x410fc231;
|
||||
}
|
||||
|
||||
static void cortex_m4_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
||||
set_feature(&cpu->env, ARM_FEATURE_V7);
|
||||
set_feature(&cpu->env, ARM_FEATURE_M);
|
||||
set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP);
|
||||
cpu->midr = 0x410fc240; /* r0p0 */
|
||||
}
|
||||
static void arm_v7m_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
CPUClass *cc = CPU_CLASS(oc);
|
||||
@ -823,6 +856,43 @@ static void arm_v7m_class_init(ObjectClass *oc, void *data)
|
||||
cc->cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt;
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo cortexr5_cp_reginfo[] = {
|
||||
/* Dummy the TCM region regs for the moment */
|
||||
{ .name = "ATCM", .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 0,
|
||||
.access = PL1_RW, .type = ARM_CP_CONST },
|
||||
{ .name = "BTCM", .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 1,
|
||||
.access = PL1_RW, .type = ARM_CP_CONST },
|
||||
REGINFO_SENTINEL
|
||||
};
|
||||
|
||||
static void cortex_r5_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
||||
set_feature(&cpu->env, ARM_FEATURE_V7);
|
||||
set_feature(&cpu->env, ARM_FEATURE_THUMB_DIV);
|
||||
set_feature(&cpu->env, ARM_FEATURE_ARM_DIV);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V7MP);
|
||||
set_feature(&cpu->env, ARM_FEATURE_MPU);
|
||||
cpu->midr = 0x411fc153; /* r1p3 */
|
||||
cpu->id_pfr0 = 0x0131;
|
||||
cpu->id_pfr1 = 0x001;
|
||||
cpu->id_dfr0 = 0x010400;
|
||||
cpu->id_afr0 = 0x0;
|
||||
cpu->id_mmfr0 = 0x0210030;
|
||||
cpu->id_mmfr1 = 0x00000000;
|
||||
cpu->id_mmfr2 = 0x01200000;
|
||||
cpu->id_mmfr3 = 0x0211;
|
||||
cpu->id_isar0 = 0x2101111;
|
||||
cpu->id_isar1 = 0x13112111;
|
||||
cpu->id_isar2 = 0x21232141;
|
||||
cpu->id_isar3 = 0x01112131;
|
||||
cpu->id_isar4 = 0x0010142;
|
||||
cpu->id_isar5 = 0x0;
|
||||
cpu->mp_is_up = true;
|
||||
define_arm_cp_regs(cpu, cortexr5_cp_reginfo);
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo cortexa8_cp_reginfo[] = {
|
||||
{ .name = "L2LOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 0,
|
||||
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
|
||||
@ -1214,6 +1284,9 @@ static const ARMCPUInfo arm_cpus[] = {
|
||||
{ .name = "arm11mpcore", .initfn = arm11mpcore_initfn },
|
||||
{ .name = "cortex-m3", .initfn = cortex_m3_initfn,
|
||||
.class_init = arm_v7m_class_init },
|
||||
{ .name = "cortex-m4", .initfn = cortex_m4_initfn,
|
||||
.class_init = arm_v7m_class_init },
|
||||
{ .name = "cortex-r5", .initfn = cortex_r5_initfn },
|
||||
{ .name = "cortex-a8", .initfn = cortex_a8_initfn },
|
||||
{ .name = "cortex-a9", .initfn = cortex_a9_initfn },
|
||||
{ .name = "cortex-a15", .initfn = cortex_a15_initfn },
|
||||
|
@ -284,6 +284,9 @@ typedef struct CPUARMState {
|
||||
};
|
||||
uint64_t par_el[4];
|
||||
};
|
||||
|
||||
uint32_t c6_rgnr;
|
||||
|
||||
uint32_t c9_insn; /* Cache lockdown registers. */
|
||||
uint32_t c9_data;
|
||||
uint64_t c9_pmcr; /* performance monitor control register */
|
||||
@ -482,6 +485,13 @@ typedef struct CPUARMState {
|
||||
/* Internal CPU feature flags. */
|
||||
uint64_t features;
|
||||
|
||||
/* PMSAv7 MPU */
|
||||
struct {
|
||||
uint32_t *drbar;
|
||||
uint32_t *drsr;
|
||||
uint32_t *dracr;
|
||||
} pmsav7;
|
||||
|
||||
void *nvic;
|
||||
const struct arm_boot_info *boot_info;
|
||||
} CPUARMState;
|
||||
@ -550,6 +560,7 @@ void pmccntr_sync(CPUARMState *env);
|
||||
#define SCTLR_DT (1U << 16) /* up to ??, RAO in v6 and v7 */
|
||||
#define SCTLR_nTWI (1U << 16) /* v8 onward */
|
||||
#define SCTLR_HA (1U << 17)
|
||||
#define SCTLR_BR (1U << 17) /* PMSA only */
|
||||
#define SCTLR_IT (1U << 18) /* up to ??, RAO in v6 and v7 */
|
||||
#define SCTLR_nTWE (1U << 18) /* v8 onward */
|
||||
#define SCTLR_WXN (1U << 19)
|
||||
@ -1116,8 +1127,8 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid)
|
||||
* old must have the OVERRIDE bit set.
|
||||
* ALIAS indicates that this register is an alias view of some underlying
|
||||
* state which is also visible via another register, and that the other
|
||||
* register is handling migration; registers marked ALIAS will not be migrated
|
||||
* but may have their state set by syncing of register state from KVM.
|
||||
* register is handling migration and reset; registers marked ALIAS will not be
|
||||
* migrated but may have their state set by syncing of register state from KVM.
|
||||
* NO_RAW indicates that this register has no underlying state and does not
|
||||
* support raw access for state saving/loading; it will not be used for either
|
||||
* migration or KVM state synchronization. (Typically this is for "registers"
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "arm_ldst.h"
|
||||
#include <zlib.h> /* For crc32 */
|
||||
#include "exec/semihost.h"
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static inline bool get_phys_addr(CPUARMState *env, target_ulong address,
|
||||
@ -984,7 +985,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
|
||||
{ .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2,
|
||||
.access = PL1_RW, .type = ARM_CP_ALIAS,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
|
||||
.resetvalue = 0, .writefn = pmintenclr_write, },
|
||||
.writefn = pmintenclr_write, },
|
||||
{ .name = "VBAR", .state = ARM_CP_STATE_BOTH,
|
||||
.opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0,
|
||||
.access = PL1_RW, .writefn = vbar_write,
|
||||
@ -1323,7 +1324,6 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
.type = ARM_CP_ALIAS,
|
||||
.access = PL1_RW | PL0_R, .accessfn = gt_cntfrq_access,
|
||||
.fieldoffset = offsetoflow32(CPUARMState, cp15.c14_cntfrq),
|
||||
.resetfn = arm_cp_reset_ignore,
|
||||
},
|
||||
{ .name = "CNTFRQ_EL0", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 0,
|
||||
@ -1344,7 +1344,6 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
.accessfn = gt_ptimer_access,
|
||||
.fieldoffset = offsetoflow32(CPUARMState,
|
||||
cp15.c14_timer[GTIMER_PHYS].ctl),
|
||||
.resetfn = arm_cp_reset_ignore,
|
||||
.writefn = gt_ctl_write, .raw_writefn = raw_write,
|
||||
},
|
||||
{ .name = "CNTP_CTL_EL0", .state = ARM_CP_STATE_AA64,
|
||||
@ -1360,7 +1359,6 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
.accessfn = gt_vtimer_access,
|
||||
.fieldoffset = offsetoflow32(CPUARMState,
|
||||
cp15.c14_timer[GTIMER_VIRT].ctl),
|
||||
.resetfn = arm_cp_reset_ignore,
|
||||
.writefn = gt_ctl_write, .raw_writefn = raw_write,
|
||||
},
|
||||
{ .name = "CNTV_CTL_EL0", .state = ARM_CP_STATE_AA64,
|
||||
@ -1422,7 +1420,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
.access = PL1_RW | PL0_R,
|
||||
.type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval),
|
||||
.accessfn = gt_ptimer_access, .resetfn = arm_cp_reset_ignore,
|
||||
.accessfn = gt_ptimer_access,
|
||||
.writefn = gt_cval_write, .raw_writefn = raw_write,
|
||||
},
|
||||
{ .name = "CNTP_CVAL_EL0", .state = ARM_CP_STATE_AA64,
|
||||
@ -1437,7 +1435,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
.access = PL1_RW | PL0_R,
|
||||
.type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval),
|
||||
.accessfn = gt_vtimer_access, .resetfn = arm_cp_reset_ignore,
|
||||
.accessfn = gt_vtimer_access,
|
||||
.writefn = gt_cval_write, .raw_writefn = raw_write,
|
||||
},
|
||||
{ .name = "CNTV_CVAL_EL0", .state = ARM_CP_STATE_AA64,
|
||||
@ -1710,16 +1708,89 @@ static uint64_t pmsav5_insn_ap_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
return simple_mpu_ap_bits(env->cp15.pmsav5_insn_ap);
|
||||
}
|
||||
|
||||
static uint64_t pmsav7_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
uint32_t *u32p = *(uint32_t **)raw_ptr(env, ri);
|
||||
|
||||
if (!u32p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32p += env->cp15.c6_rgnr;
|
||||
return *u32p;
|
||||
}
|
||||
|
||||
static void pmsav7_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
ARMCPU *cpu = arm_env_get_cpu(env);
|
||||
uint32_t *u32p = *(uint32_t **)raw_ptr(env, ri);
|
||||
|
||||
if (!u32p) {
|
||||
return;
|
||||
}
|
||||
|
||||
u32p += env->cp15.c6_rgnr;
|
||||
tlb_flush(CPU(cpu), 1); /* Mappings may have changed - purge! */
|
||||
*u32p = value;
|
||||
}
|
||||
|
||||
static void pmsav7_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
ARMCPU *cpu = arm_env_get_cpu(env);
|
||||
uint32_t *u32p = *(uint32_t **)raw_ptr(env, ri);
|
||||
|
||||
if (!u32p) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(u32p, 0, sizeof(*u32p) * cpu->pmsav7_dregion);
|
||||
}
|
||||
|
||||
static void pmsav7_rgnr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
ARMCPU *cpu = arm_env_get_cpu(env);
|
||||
uint32_t nrgs = cpu->pmsav7_dregion;
|
||||
|
||||
if (value >= nrgs) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"PMSAv7 RGNR write >= # supported regions, %" PRIu32
|
||||
" > %" PRIu32 "\n", (uint32_t)value, nrgs);
|
||||
return;
|
||||
}
|
||||
|
||||
raw_write(env, ri, value);
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo pmsav7_cp_reginfo[] = {
|
||||
{ .name = "DRBAR", .cp = 15, .crn = 6, .opc1 = 0, .crm = 1, .opc2 = 0,
|
||||
.access = PL1_RW, .type = ARM_CP_NO_RAW,
|
||||
.fieldoffset = offsetof(CPUARMState, pmsav7.drbar),
|
||||
.readfn = pmsav7_read, .writefn = pmsav7_write, .resetfn = pmsav7_reset },
|
||||
{ .name = "DRSR", .cp = 15, .crn = 6, .opc1 = 0, .crm = 1, .opc2 = 2,
|
||||
.access = PL1_RW, .type = ARM_CP_NO_RAW,
|
||||
.fieldoffset = offsetof(CPUARMState, pmsav7.drsr),
|
||||
.readfn = pmsav7_read, .writefn = pmsav7_write, .resetfn = pmsav7_reset },
|
||||
{ .name = "DRACR", .cp = 15, .crn = 6, .opc1 = 0, .crm = 1, .opc2 = 4,
|
||||
.access = PL1_RW, .type = ARM_CP_NO_RAW,
|
||||
.fieldoffset = offsetof(CPUARMState, pmsav7.dracr),
|
||||
.readfn = pmsav7_read, .writefn = pmsav7_write, .resetfn = pmsav7_reset },
|
||||
{ .name = "RGNR", .cp = 15, .crn = 6, .opc1 = 0, .crm = 2, .opc2 = 0,
|
||||
.access = PL1_RW,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c6_rgnr),
|
||||
.writefn = pmsav7_rgnr_write },
|
||||
REGINFO_SENTINEL
|
||||
};
|
||||
|
||||
static const ARMCPRegInfo pmsav5_cp_reginfo[] = {
|
||||
{ .name = "DATA_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0,
|
||||
.access = PL1_RW, .type = ARM_CP_ALIAS,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.pmsav5_data_ap),
|
||||
.resetvalue = 0,
|
||||
.readfn = pmsav5_data_ap_read, .writefn = pmsav5_data_ap_write, },
|
||||
{ .name = "INSN_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1,
|
||||
.access = PL1_RW, .type = ARM_CP_ALIAS,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.pmsav5_insn_ap),
|
||||
.resetvalue = 0,
|
||||
.readfn = pmsav5_insn_ap_read, .writefn = pmsav5_insn_ap_write, },
|
||||
{ .name = "DATA_EXT_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 2,
|
||||
.access = PL1_RW,
|
||||
@ -1851,8 +1922,7 @@ static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = {
|
||||
{ .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0,
|
||||
.access = PL1_RW, .type = ARM_CP_ALIAS,
|
||||
.bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dfsr_s),
|
||||
offsetoflow32(CPUARMState, cp15.dfsr_ns) },
|
||||
.resetfn = arm_cp_reset_ignore, },
|
||||
offsetoflow32(CPUARMState, cp15.dfsr_ns) }, },
|
||||
{ .name = "IFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1,
|
||||
.access = PL1_RW, .resetvalue = 0,
|
||||
.bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.ifsr_s),
|
||||
@ -1890,7 +1960,7 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.tcr_el[1]) },
|
||||
{ .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2,
|
||||
.access = PL1_RW, .type = ARM_CP_ALIAS, .writefn = vmsa_ttbcr_write,
|
||||
.resetfn = arm_cp_reset_ignore, .raw_writefn = vmsa_ttbcr_raw_write,
|
||||
.raw_writefn = vmsa_ttbcr_raw_write,
|
||||
.bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tcr_el[3]),
|
||||
offsetoflow32(CPUARMState, cp15.tcr_el[1])} },
|
||||
REGINFO_SENTINEL
|
||||
@ -2109,12 +2179,12 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = {
|
||||
.access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS,
|
||||
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s),
|
||||
offsetof(CPUARMState, cp15.ttbr0_ns) },
|
||||
.writefn = vmsa_ttbr_write, .resetfn = arm_cp_reset_ignore },
|
||||
.writefn = vmsa_ttbr_write, },
|
||||
{ .name = "TTBR1", .cp = 15, .crm = 2, .opc1 = 1,
|
||||
.access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS,
|
||||
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s),
|
||||
offsetof(CPUARMState, cp15.ttbr1_ns) },
|
||||
.writefn = vmsa_ttbr_write, .resetfn = arm_cp_reset_ignore },
|
||||
.writefn = vmsa_ttbr_write, },
|
||||
REGINFO_SENTINEL
|
||||
};
|
||||
|
||||
@ -2641,7 +2711,6 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) },
|
||||
{ .name = "HTTBR", .cp = 15, .opc1 = 4, .crm = 2,
|
||||
.access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS,
|
||||
.resetvalue = 0,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) },
|
||||
{ .name = "TLBI_ALLE2", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0,
|
||||
@ -2666,7 +2735,7 @@ static const ARMCPRegInfo el3_cp_reginfo[] = {
|
||||
{ .name = "SCR", .type = ARM_CP_ALIAS,
|
||||
.cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 0,
|
||||
.access = PL3_RW, .fieldoffset = offsetoflow32(CPUARMState, cp15.scr_el3),
|
||||
.resetfn = arm_cp_reset_ignore, .writefn = scr_write },
|
||||
.writefn = scr_write },
|
||||
{ .name = "SDER32_EL3", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 6, .crn = 1, .crm = 1, .opc2 = 1,
|
||||
.access = PL3_RW, .resetvalue = 0,
|
||||
@ -2761,8 +2830,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = {
|
||||
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0,
|
||||
.type = ARM_CP_ALIAS,
|
||||
.access = PL1_R,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1),
|
||||
.resetfn = arm_cp_reset_ignore },
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), },
|
||||
/* We define a dummy WI OSLAR_EL1, because Linux writes to it. */
|
||||
{ .name = "OSLAR_EL1", .state = ARM_CP_STATE_BOTH,
|
||||
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 4,
|
||||
@ -3345,13 +3413,14 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
||||
define_one_arm_cp_reg(cpu, &rvbar);
|
||||
}
|
||||
if (arm_feature(env, ARM_FEATURE_MPU)) {
|
||||
/* These are the MPU registers prior to PMSAv6. Any new
|
||||
* PMSA core later than the ARM946 will require that we
|
||||
* implement the PMSAv6 or PMSAv7 registers, which are
|
||||
* completely different.
|
||||
*/
|
||||
assert(!arm_feature(env, ARM_FEATURE_V6));
|
||||
define_arm_cp_regs(cpu, pmsav5_cp_reginfo);
|
||||
if (arm_feature(env, ARM_FEATURE_V6)) {
|
||||
/* PMSAv6 not implemented */
|
||||
assert(arm_feature(env, ARM_FEATURE_V7));
|
||||
define_arm_cp_regs(cpu, vmsa_pmsa_cp_reginfo);
|
||||
define_arm_cp_regs(cpu, pmsav7_cp_reginfo);
|
||||
} else {
|
||||
define_arm_cp_regs(cpu, pmsav5_cp_reginfo);
|
||||
}
|
||||
} else {
|
||||
define_arm_cp_regs(cpu, vmsa_pmsa_cp_reginfo);
|
||||
define_arm_cp_regs(cpu, vmsa_cp_reginfo);
|
||||
@ -3465,6 +3534,13 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
||||
.cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 3,
|
||||
.access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0,
|
||||
};
|
||||
/* MPUIR is specific to PMSA V6+ */
|
||||
ARMCPRegInfo id_mpuir_reginfo = {
|
||||
.name = "MPUIR",
|
||||
.cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 4,
|
||||
.access = PL1_R, .type = ARM_CP_CONST,
|
||||
.resetvalue = cpu->pmsav7_dregion << 8
|
||||
};
|
||||
ARMCPRegInfo crn0_wi_reginfo = {
|
||||
.name = "CRN0_WI", .cp = 15, .crn = 0, .crm = CP_ANY,
|
||||
.opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_W,
|
||||
@ -3487,6 +3563,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
||||
r->access = PL1_RW;
|
||||
}
|
||||
id_tlbtr_reginfo.access = PL1_RW;
|
||||
id_tlbtr_reginfo.access = PL1_RW;
|
||||
}
|
||||
if (arm_feature(env, ARM_FEATURE_V8)) {
|
||||
define_arm_cp_regs(cpu, id_v8_midr_cp_reginfo);
|
||||
@ -3496,6 +3573,8 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
||||
define_arm_cp_regs(cpu, id_cp_reginfo);
|
||||
if (!arm_feature(env, ARM_FEATURE_MPU)) {
|
||||
define_one_arm_cp_reg(cpu, &id_tlbtr_reginfo);
|
||||
} else if (arm_feature(env, ARM_FEATURE_V7)) {
|
||||
define_one_arm_cp_reg(cpu, &id_mpuir_reginfo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3721,14 +3800,12 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r,
|
||||
if ((r->state == ARM_CP_STATE_BOTH && ns) ||
|
||||
(arm_feature(&cpu->env, ARM_FEATURE_V8) && !ns)) {
|
||||
r2->type |= ARM_CP_ALIAS;
|
||||
r2->resetfn = arm_cp_reset_ignore;
|
||||
}
|
||||
} else if ((secstate != r->secure) && !ns) {
|
||||
/* The register is not banked so we only want to allow migration of
|
||||
* the non-secure instance.
|
||||
*/
|
||||
r2->type |= ARM_CP_ALIAS;
|
||||
r2->resetfn = arm_cp_reset_ignore;
|
||||
}
|
||||
|
||||
if (r->state == ARM_CP_STATE_BOTH) {
|
||||
@ -4478,7 +4555,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
|
||||
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM);
|
||||
return;
|
||||
case EXCP_BKPT:
|
||||
if (semihosting_enabled) {
|
||||
if (semihosting_enabled()) {
|
||||
int nr;
|
||||
nr = arm_lduw_code(env, env->regs[15], env->bswap_code) & 0xff;
|
||||
if (nr == 0xab) {
|
||||
@ -4790,7 +4867,7 @@ void arm_cpu_do_interrupt(CPUState *cs)
|
||||
offset = 4;
|
||||
break;
|
||||
case EXCP_SWI:
|
||||
if (semihosting_enabled) {
|
||||
if (semihosting_enabled()) {
|
||||
/* Check for semihosting interrupt. */
|
||||
if (env->thumb) {
|
||||
mask = arm_lduw_code(env, env->regs[15] - 2, env->bswap_code)
|
||||
@ -4817,7 +4894,7 @@ void arm_cpu_do_interrupt(CPUState *cs)
|
||||
break;
|
||||
case EXCP_BKPT:
|
||||
/* See if this is a semihosting syscall. */
|
||||
if (env->thumb && semihosting_enabled) {
|
||||
if (env->thumb && semihosting_enabled()) {
|
||||
mask = arm_lduw_code(env, env->regs[15], env->bswap_code) & 0xff;
|
||||
if (mask == 0xab
|
||||
&& (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) {
|
||||
@ -5759,6 +5836,167 @@ do_fault:
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void get_phys_addr_pmsav7_default(CPUARMState *env,
|
||||
ARMMMUIdx mmu_idx,
|
||||
int32_t address, int *prot)
|
||||
{
|
||||
*prot = PAGE_READ | PAGE_WRITE;
|
||||
switch (address) {
|
||||
case 0xF0000000 ... 0xFFFFFFFF:
|
||||
if (regime_sctlr(env, mmu_idx) & SCTLR_V) { /* hivecs execing is ok */
|
||||
*prot |= PAGE_EXEC;
|
||||
}
|
||||
break;
|
||||
case 0x00000000 ... 0x7FFFFFFF:
|
||||
*prot |= PAGE_EXEC;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address,
|
||||
int access_type, ARMMMUIdx mmu_idx,
|
||||
hwaddr *phys_ptr, int *prot, uint32_t *fsr)
|
||||
{
|
||||
ARMCPU *cpu = arm_env_get_cpu(env);
|
||||
int n;
|
||||
bool is_user = regime_is_user(env, mmu_idx);
|
||||
|
||||
*phys_ptr = address;
|
||||
*prot = 0;
|
||||
|
||||
if (regime_translation_disabled(env, mmu_idx)) { /* MPU disabled */
|
||||
get_phys_addr_pmsav7_default(env, mmu_idx, address, prot);
|
||||
} else { /* MPU enabled */
|
||||
for (n = (int)cpu->pmsav7_dregion - 1; n >= 0; n--) {
|
||||
/* region search */
|
||||
uint32_t base = env->pmsav7.drbar[n];
|
||||
uint32_t rsize = extract32(env->pmsav7.drsr[n], 1, 5);
|
||||
uint32_t rmask;
|
||||
bool srdis = false;
|
||||
|
||||
if (!(env->pmsav7.drsr[n] & 0x1)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!rsize) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "DRSR.Rsize field can not be 0");
|
||||
continue;
|
||||
}
|
||||
rsize++;
|
||||
rmask = (1ull << rsize) - 1;
|
||||
|
||||
if (base & rmask) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "DRBAR %" PRIx32 " misaligned "
|
||||
"to DRSR region size, mask = %" PRIx32,
|
||||
base, rmask);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (address < base || address > base + rmask) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Region matched */
|
||||
|
||||
if (rsize >= 8) { /* no subregions for regions < 256 bytes */
|
||||
int i, snd;
|
||||
uint32_t srdis_mask;
|
||||
|
||||
rsize -= 3; /* sub region size (power of 2) */
|
||||
snd = ((address - base) >> rsize) & 0x7;
|
||||
srdis = extract32(env->pmsav7.drsr[n], snd + 8, 1);
|
||||
|
||||
srdis_mask = srdis ? 0x3 : 0x0;
|
||||
for (i = 2; i <= 8 && rsize < TARGET_PAGE_BITS; i *= 2) {
|
||||
/* This will check in groups of 2, 4 and then 8, whether
|
||||
* the subregion bits are consistent. rsize is incremented
|
||||
* back up to give the region size, considering consistent
|
||||
* adjacent subregions as one region. Stop testing if rsize
|
||||
* is already big enough for an entire QEMU page.
|
||||
*/
|
||||
int snd_rounded = snd & ~(i - 1);
|
||||
uint32_t srdis_multi = extract32(env->pmsav7.drsr[n],
|
||||
snd_rounded + 8, i);
|
||||
if (srdis_mask ^ srdis_multi) {
|
||||
break;
|
||||
}
|
||||
srdis_mask = (srdis_mask << i) | srdis_mask;
|
||||
rsize++;
|
||||
}
|
||||
}
|
||||
if (rsize < TARGET_PAGE_BITS) {
|
||||
qemu_log_mask(LOG_UNIMP, "No support for MPU (sub)region"
|
||||
"alignment of %" PRIu32 " bits. Minimum is %d\n",
|
||||
rsize, TARGET_PAGE_BITS);
|
||||
continue;
|
||||
}
|
||||
if (srdis) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (n == -1) { /* no hits */
|
||||
if (cpu->pmsav7_dregion &&
|
||||
(is_user || !(regime_sctlr(env, mmu_idx) & SCTLR_BR))) {
|
||||
/* background fault */
|
||||
*fsr = 0;
|
||||
return true;
|
||||
}
|
||||
get_phys_addr_pmsav7_default(env, mmu_idx, address, prot);
|
||||
} else { /* a MPU hit! */
|
||||
uint32_t ap = extract32(env->pmsav7.dracr[n], 8, 3);
|
||||
|
||||
if (is_user) { /* User mode AP bit decoding */
|
||||
switch (ap) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 5:
|
||||
break; /* no access */
|
||||
case 3:
|
||||
*prot |= PAGE_WRITE;
|
||||
/* fall through */
|
||||
case 2:
|
||||
case 6:
|
||||
*prot |= PAGE_READ | PAGE_EXEC;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"Bad value for AP bits in DRACR %"
|
||||
PRIx32 "\n", ap);
|
||||
}
|
||||
} else { /* Priv. mode AP bits decoding */
|
||||
switch (ap) {
|
||||
case 0:
|
||||
break; /* no access */
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
*prot |= PAGE_WRITE;
|
||||
/* fall through */
|
||||
case 5:
|
||||
case 6:
|
||||
*prot |= PAGE_READ | PAGE_EXEC;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"Bad value for AP bits in DRACR %"
|
||||
PRIx32 "\n", ap);
|
||||
}
|
||||
}
|
||||
|
||||
/* execute never */
|
||||
if (env->pmsav7.dracr[n] & (1 << 12)) {
|
||||
*prot &= ~PAGE_EXEC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*fsr = 0x00d; /* Permission fault */
|
||||
return !(*prot & (1 << access_type));
|
||||
}
|
||||
|
||||
static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address,
|
||||
int access_type, ARMMMUIdx mmu_idx,
|
||||
hwaddr *phys_ptr, int *prot, uint32_t *fsr)
|
||||
@ -5844,7 +6082,7 @@ static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address,
|
||||
* DFSR/IFSR fault register, with the following caveats:
|
||||
* * we honour the short vs long DFSR format differences.
|
||||
* * the WnR bit is never set (the caller must do this).
|
||||
* * for MPU based systems we don't bother to return a full FSR format
|
||||
* * for PSMAv5 based systems we don't bother to return a full FSR format
|
||||
* value.
|
||||
*
|
||||
* @env: CPUARMState
|
||||
@ -5892,6 +6130,16 @@ static inline bool get_phys_addr(CPUARMState *env, target_ulong address,
|
||||
}
|
||||
}
|
||||
|
||||
/* pmsav7 has special handling for when MPU is disabled so call it before
|
||||
* the common MMU/MPU disabled check below.
|
||||
*/
|
||||
if (arm_feature(env, ARM_FEATURE_MPU) &&
|
||||
arm_feature(env, ARM_FEATURE_V7)) {
|
||||
*page_size = TARGET_PAGE_SIZE;
|
||||
return get_phys_addr_pmsav7(env, address, access_type, mmu_idx,
|
||||
phys_ptr, prot, fsr);
|
||||
}
|
||||
|
||||
if (regime_translation_disabled(env, mmu_idx)) {
|
||||
/* MMU/MPU disabled. */
|
||||
*phys_ptr = address;
|
||||
@ -5901,6 +6149,7 @@ static inline bool get_phys_addr(CPUARMState *env, target_ulong address,
|
||||
}
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_MPU)) {
|
||||
/* Pre-v7 MPU */
|
||||
*page_size = TARGET_PAGE_SIZE;
|
||||
return get_phys_addr_pmsav5(env, address, access_type, mmu_idx,
|
||||
phys_ptr, prot, fsr);
|
||||
|
@ -125,6 +125,39 @@ static const VMStateDescription vmstate_thumb2ee = {
|
||||
}
|
||||
};
|
||||
|
||||
static bool pmsav7_needed(void *opaque)
|
||||
{
|
||||
ARMCPU *cpu = opaque;
|
||||
CPUARMState *env = &cpu->env;
|
||||
|
||||
return arm_feature(env, ARM_FEATURE_MPU) &&
|
||||
arm_feature(env, ARM_FEATURE_V7);
|
||||
}
|
||||
|
||||
static bool pmsav7_rgnr_vmstate_validate(void *opaque, int version_id)
|
||||
{
|
||||
ARMCPU *cpu = opaque;
|
||||
|
||||
return cpu->env.cp15.c6_rgnr < cpu->pmsav7_dregion;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_pmsav7 = {
|
||||
.name = "cpu/pmsav7",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = pmsav7_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_VARRAY_UINT32(env.pmsav7.drbar, ARMCPU, pmsav7_dregion, 0,
|
||||
vmstate_info_uint32, uint32_t),
|
||||
VMSTATE_VARRAY_UINT32(env.pmsav7.drsr, ARMCPU, pmsav7_dregion, 0,
|
||||
vmstate_info_uint32, uint32_t),
|
||||
VMSTATE_VARRAY_UINT32(env.pmsav7.dracr, ARMCPU, pmsav7_dregion, 0,
|
||||
vmstate_info_uint32, uint32_t),
|
||||
VMSTATE_VALIDATE("rgnr is valid", pmsav7_rgnr_vmstate_validate),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static int get_cpsr(QEMUFile *f, void *opaque, size_t size)
|
||||
{
|
||||
ARMCPU *cpu = opaque;
|
||||
@ -291,6 +324,7 @@ const VMStateDescription vmstate_arm_cpu = {
|
||||
&vmstate_iwmmxt,
|
||||
&vmstate_m,
|
||||
&vmstate_thumb2ee,
|
||||
&vmstate_pmsav7,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "cpu.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "exec/semihost.h"
|
||||
|
||||
int lm32_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
|
||||
int mmu_idx)
|
||||
@ -162,7 +163,7 @@ void lm32_cpu_do_interrupt(CPUState *cs)
|
||||
|
||||
switch (cs->exception_index) {
|
||||
case EXCP_SYSTEMCALL:
|
||||
if (unlikely(semihosting_enabled)) {
|
||||
if (unlikely(semihosting_enabled())) {
|
||||
/* do_semicall() returns true if call was handled. Otherwise
|
||||
* do the normal exception handling. */
|
||||
if (lm32_cpu_do_semihosting(cs)) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "cpu.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "exec/semihost.h"
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
|
||||
@ -33,8 +34,6 @@ static inline void do_interrupt_m68k_hardirq(CPUM68KState *env)
|
||||
|
||||
#else
|
||||
|
||||
extern int semihosting_enabled;
|
||||
|
||||
/* Try to fill the TLB and return an exception if error. If retaddr is
|
||||
NULL, it means that the function was called in C code (i.e. not
|
||||
from generated code or from helper.c) */
|
||||
@ -85,7 +84,7 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw)
|
||||
do_rte(env);
|
||||
return;
|
||||
case EXCP_HALT_INSN:
|
||||
if (semihosting_enabled
|
||||
if (semihosting_enabled()
|
||||
&& (env->sr & SR_S) != 0
|
||||
&& (env->pc & 3) == 0
|
||||
&& cpu_lduw_code(env, env->pc - 4) == 0x4e71
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "qemu/log.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "exec/semihost.h"
|
||||
|
||||
#include "exec/helper-proto.h"
|
||||
#include "exec/helper-gen.h"
|
||||
@ -1216,7 +1217,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
|
||||
break;
|
||||
|
||||
case 1: /*SIMCALL*/
|
||||
if (semihosting_enabled) {
|
||||
if (semihosting_enabled()) {
|
||||
if (gen_check_privilege(dc)) {
|
||||
gen_helper_simcall(cpu_env);
|
||||
}
|
||||
|
104
vl.c
104
vl.c
@ -119,6 +119,7 @@ int main(int argc, char **argv)
|
||||
#include "qapi/opts-visitor.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qapi-event.h"
|
||||
#include "exec/semihost.h"
|
||||
|
||||
#define MAX_VIRTIO_CONSOLES 1
|
||||
#define MAX_SCLP_CONSOLES 1
|
||||
@ -169,7 +170,6 @@ int graphic_rotate = 0;
|
||||
const char *watchdog;
|
||||
QEMUOptionRom option_rom[MAX_OPTION_ROMS];
|
||||
int nb_option_roms;
|
||||
int semihosting_enabled = 0;
|
||||
int old_param = 0;
|
||||
const char *qemu_name;
|
||||
int alt_grab = 0;
|
||||
@ -488,6 +488,9 @@ static QemuOptsList qemu_semihosting_config_opts = {
|
||||
}, {
|
||||
.name = "target",
|
||||
.type = QEMU_OPT_STRING,
|
||||
}, {
|
||||
.name = "arg",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
@ -1245,6 +1248,81 @@ static void configure_msg(QemuOpts *opts)
|
||||
enable_timestamp_msg = qemu_opt_get_bool(opts, "timestamp", true);
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* Semihosting */
|
||||
|
||||
typedef struct SemihostingConfig {
|
||||
bool enabled;
|
||||
SemihostingTarget target;
|
||||
const char **argv;
|
||||
int argc;
|
||||
const char *cmdline; /* concatenated argv */
|
||||
} SemihostingConfig;
|
||||
|
||||
static SemihostingConfig semihosting;
|
||||
|
||||
bool semihosting_enabled(void)
|
||||
{
|
||||
return semihosting.enabled;
|
||||
}
|
||||
|
||||
SemihostingTarget semihosting_get_target(void)
|
||||
{
|
||||
return semihosting.target;
|
||||
}
|
||||
|
||||
const char *semihosting_get_arg(int i)
|
||||
{
|
||||
if (i >= semihosting.argc) {
|
||||
return NULL;
|
||||
}
|
||||
return semihosting.argv[i];
|
||||
}
|
||||
|
||||
int semihosting_get_argc(void)
|
||||
{
|
||||
return semihosting.argc;
|
||||
}
|
||||
|
||||
const char *semihosting_get_cmdline(void)
|
||||
{
|
||||
if (semihosting.cmdline == NULL && semihosting.argc > 0) {
|
||||
semihosting.cmdline = g_strjoinv(" ", (gchar **)semihosting.argv);
|
||||
}
|
||||
return semihosting.cmdline;
|
||||
}
|
||||
|
||||
static int add_semihosting_arg(void *opaque,
|
||||
const char *name, const char *val,
|
||||
Error **errp)
|
||||
{
|
||||
SemihostingConfig *s = opaque;
|
||||
if (strcmp(name, "arg") == 0) {
|
||||
s->argc++;
|
||||
/* one extra element as g_strjoinv() expects NULL-terminated array */
|
||||
s->argv = g_realloc(s->argv, (s->argc + 1) * sizeof(void *));
|
||||
s->argv[s->argc - 1] = val;
|
||||
s->argv[s->argc] = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Use strings passed via -kernel/-append to initialize semihosting.argv[] */
|
||||
static inline void semihosting_arg_fallback(const char *file, const char *cmd)
|
||||
{
|
||||
char *cmd_token;
|
||||
|
||||
/* argv[0] */
|
||||
add_semihosting_arg(&semihosting, "arg", file, NULL);
|
||||
|
||||
/* split -append and initialize argv[1..n] */
|
||||
cmd_token = strtok(g_strdup(cmd), " ");
|
||||
while (cmd_token) {
|
||||
add_semihosting_arg(&semihosting, "arg", cmd_token, NULL);
|
||||
cmd_token = strtok(NULL, " ");
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* USB devices */
|
||||
|
||||
@ -3622,24 +3700,24 @@ int main(int argc, char **argv, char **envp)
|
||||
nb_option_roms++;
|
||||
break;
|
||||
case QEMU_OPTION_semihosting:
|
||||
semihosting_enabled = 1;
|
||||
semihosting_target = SEMIHOSTING_TARGET_AUTO;
|
||||
semihosting.enabled = true;
|
||||
semihosting.target = SEMIHOSTING_TARGET_AUTO;
|
||||
break;
|
||||
case QEMU_OPTION_semihosting_config:
|
||||
semihosting_enabled = 1;
|
||||
semihosting.enabled = true;
|
||||
opts = qemu_opts_parse(qemu_find_opts("semihosting-config"),
|
||||
optarg, 0);
|
||||
if (opts != NULL) {
|
||||
semihosting_enabled = qemu_opt_get_bool(opts, "enable",
|
||||
semihosting.enabled = qemu_opt_get_bool(opts, "enable",
|
||||
true);
|
||||
const char *target = qemu_opt_get(opts, "target");
|
||||
if (target != NULL) {
|
||||
if (strcmp("native", target) == 0) {
|
||||
semihosting_target = SEMIHOSTING_TARGET_NATIVE;
|
||||
semihosting.target = SEMIHOSTING_TARGET_NATIVE;
|
||||
} else if (strcmp("gdb", target) == 0) {
|
||||
semihosting_target = SEMIHOSTING_TARGET_GDB;
|
||||
semihosting.target = SEMIHOSTING_TARGET_GDB;
|
||||
} else if (strcmp("auto", target) == 0) {
|
||||
semihosting_target = SEMIHOSTING_TARGET_AUTO;
|
||||
semihosting.target = SEMIHOSTING_TARGET_AUTO;
|
||||
} else {
|
||||
fprintf(stderr, "Unsupported semihosting-config"
|
||||
" %s\n",
|
||||
@ -3647,8 +3725,11 @@ int main(int argc, char **argv, char **envp)
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
semihosting_target = SEMIHOSTING_TARGET_AUTO;
|
||||
semihosting.target = SEMIHOSTING_TARGET_AUTO;
|
||||
}
|
||||
/* Set semihosting argument count and vector */
|
||||
qemu_opt_foreach(opts, add_semihosting_arg,
|
||||
&semihosting, NULL);
|
||||
} else {
|
||||
fprintf(stderr, "Unsupported semihosting-config %s\n",
|
||||
optarg);
|
||||
@ -4217,6 +4298,11 @@ int main(int argc, char **argv, char **envp)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (semihosting_enabled() && !semihosting_get_argc() && kernel_filename) {
|
||||
/* fall back to the -kernel/-append */
|
||||
semihosting_arg_fallback(kernel_filename, kernel_cmdline);
|
||||
}
|
||||
|
||||
os_set_line_buffering();
|
||||
|
||||
#ifdef CONFIG_SPICE
|
||||
|
Loading…
Reference in New Issue
Block a user