提交 799810fb 编写于 作者: P Peter Maydell

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: NPeter Maydell <peter.maydell@linaro.org>
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "cpu.h" #include "cpu.h"
#include "qemu/sockets.h" #include "qemu/sockets.h"
#include "sysemu/kvm.h" #include "sysemu/kvm.h"
#include "exec/semihost.h"
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
#define GDB_ATTACHED "0" #define GDB_ATTACHED "0"
...@@ -323,8 +324,6 @@ static GDBState *gdbserver_state; ...@@ -323,8 +324,6 @@ static GDBState *gdbserver_state;
bool gdb_has_xml; bool gdb_has_xml;
int semihosting_target = SEMIHOSTING_TARGET_AUTO;
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
/* XXX: This is not thread safe. Do we care? */ /* XXX: This is not thread safe. Do we care? */
static int gdbserver_fd = -1; static int gdbserver_fd = -1;
...@@ -362,10 +361,11 @@ static enum { ...@@ -362,10 +361,11 @@ static enum {
/* Decide if either remote gdb syscalls or native file IO should be used. */ /* Decide if either remote gdb syscalls or native file IO should be used. */
int use_gdb_syscalls(void) 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 */ /* -semihosting-config target=native */
return false; return false;
} else if (semihosting_target == SEMIHOSTING_TARGET_GDB) { } else if (target == SEMIHOSTING_TARGET_GDB) {
/* -semihosting-config target=gdb */ /* -semihosting-config target=gdb */
return true; return true;
} }
......
...@@ -26,6 +26,9 @@ ...@@ -26,6 +26,9 @@
#include "sysemu/device_tree.h" #include "sysemu/device_tree.h"
#include "hw/platform-bus.h" #include "hw/platform-bus.h"
#include "sysemu/sysemu.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 * internal struct that contains the information to create dynamic
...@@ -53,11 +56,81 @@ typedef struct NodeCreationPair { ...@@ -53,11 +56,81 @@ typedef struct NodeCreationPair {
int (*add_fdt_node_fn)(SysBusDevice *sbdev, void *opaque); int (*add_fdt_node_fn)(SysBusDevice *sbdev, void *opaque);
} NodeCreationPair; } 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 */ /* list of supported dynamic sysbus devices */
static const NodeCreationPair add_fdt_node_functions[] = { static const NodeCreationPair add_fdt_node_functions[] = {
{TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node},
{"", NULL}, /* last element */ {"", NULL}, /* last element */
}; };
/* Generic Code */
/** /**
* add_fdt_node - add the device tree node of a dynamic sysbus device * add_fdt_node - add the device tree node of a dynamic sysbus device
* *
......
...@@ -47,21 +47,11 @@ ...@@ -47,21 +47,11 @@
#include "hw/arm/virt-acpi-build.h" #include "hw/arm/virt-acpi-build.h"
#include "hw/arm/sysbus-fdt.h" #include "hw/arm/sysbus-fdt.h"
#include "hw/platform-bus.h" #include "hw/platform-bus.h"
#include "hw/arm/fdt.h"
/* Number of external interrupt lines to configure the GIC with */ /* Number of external interrupt lines to configure the GIC with */
#define NUM_IRQS 256 #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 #define PLATFORM_BUS_NUM_IRQS 64
static ARMPlatformBusSystemParams platform_bus_params; static ARMPlatformBusSystemParams platform_bus_params;
......
...@@ -65,7 +65,7 @@ static void xlnx_ep108_init(MachineState *machine) ...@@ -65,7 +65,7 @@ static void xlnx_ep108_init(MachineState *machine)
xlnx_ep108_binfo.kernel_cmdline = machine->kernel_cmdline; xlnx_ep108_binfo.kernel_cmdline = machine->kernel_cmdline;
xlnx_ep108_binfo.initrd_filename = machine->initrd_filename; xlnx_ep108_binfo.initrd_filename = machine->initrd_filename;
xlnx_ep108_binfo.loader_start = 0; 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 = { static QEMUMachine xlnx_ep108_machine = {
......
...@@ -64,10 +64,17 @@ static void xlnx_zynqmp_init(Object *obj) ...@@ -64,10 +64,17 @@ static void xlnx_zynqmp_init(Object *obj)
XlnxZynqMPState *s = XLNX_ZYNQMP(obj); XlnxZynqMPState *s = XLNX_ZYNQMP(obj);
int i; int i;
for (i = 0; i < XLNX_ZYNQMP_NUM_CPUS; i++) { for (i = 0; i < XLNX_ZYNQMP_NUM_APU_CPUS; i++) {
object_initialize(&s->cpu[i], sizeof(s->cpu[i]), object_initialize(&s->apu_cpu[i], sizeof(s->apu_cpu[i]),
"cortex-a53-" TYPE_ARM_CPU); "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); &error_abort);
} }
...@@ -90,12 +97,13 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) ...@@ -90,12 +97,13 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
XlnxZynqMPState *s = XLNX_ZYNQMP(dev); XlnxZynqMPState *s = XLNX_ZYNQMP(dev);
MemoryRegion *system_memory = get_system_memory(); MemoryRegion *system_memory = get_system_memory();
uint8_t i; uint8_t i;
const char *boot_cpu = s->boot_cpu ? s->boot_cpu : "apu-cpu[0]";
qemu_irq gic_spi[GIC_NUM_SPI_INTR]; qemu_irq gic_spi[GIC_NUM_SPI_INTR];
Error *err = NULL; Error *err = NULL;
qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", GIC_NUM_SPI_INTR + 32); 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), "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); object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
if (err) { if (err) {
error_propagate((errp), (err)); error_propagate((errp), (err));
...@@ -121,38 +129,77 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) ...@@ -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; 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); "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 */ /* 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); "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); "reset-cbar", &err);
if (err) { if (err) {
error_propagate((errp), (err)); error_propagate((errp), (err));
return; 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) { if (err) {
error_propagate((errp), (err)); error_propagate((errp), (err));
return; return;
} }
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i, 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), irq = qdev_get_gpio_in(DEVICE(&s->gic),
arm_gic_ppi_index(i, ARM_PHYS_TIMER_PPI)); 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), irq = qdev_get_gpio_in(DEVICE(&s->gic),
arm_gic_ppi_index(i, ARM_VIRT_TIMER_PPI)); 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++) { for (i = 0; i < GIC_NUM_SPI_INTR; i++) {
...@@ -188,10 +235,16 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) ...@@ -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) static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data)
{ {
DeviceClass *dc = DEVICE_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc);
dc->props = xlnx_zynqmp_props;
dc->realize = xlnx_zynqmp_realize; dc->realize = xlnx_zynqmp_realize;
} }
......
...@@ -95,10 +95,4 @@ extern bool gdb_has_xml; ...@@ -95,10 +95,4 @@ extern bool gdb_has_xml;
/* in gdbstub-xml.c, generated by scripts/feature_to_c.sh */ /* in gdbstub-xml.c, generated by scripts/feature_to_c.sh */
extern const char *const xml_builtin[][2]; 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 #endif
/*
* 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
/*
*
* 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 @@ ...@@ -27,7 +27,8 @@
#define XLNX_ZYNQMP(obj) OBJECT_CHECK(XlnxZynqMPState, (obj), \ #define XLNX_ZYNQMP(obj) OBJECT_CHECK(XlnxZynqMPState, (obj), \
TYPE_XLNX_ZYNQMP) 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_GEMS 4
#define XLNX_ZYNQMP_NUM_UARTS 2 #define XLNX_ZYNQMP_NUM_UARTS 2
...@@ -47,11 +48,15 @@ typedef struct XlnxZynqMPState { ...@@ -47,11 +48,15 @@ typedef struct XlnxZynqMPState {
DeviceState parent_obj; DeviceState parent_obj;
/*< public >*/ /*< 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; GICState gic;
MemoryRegion gic_mr[XLNX_ZYNQMP_GIC_REGIONS][XLNX_ZYNQMP_GIC_ALIASES]; MemoryRegion gic_mr[XLNX_ZYNQMP_GIC_REGIONS][XLNX_ZYNQMP_GIC_ALIASES];
CadenceGEMState gem[XLNX_ZYNQMP_NUM_GEMS]; CadenceGEMState gem[XLNX_ZYNQMP_NUM_GEMS];
CadenceUARTState uart[XLNX_ZYNQMP_NUM_UARTS]; CadenceUARTState uart[XLNX_ZYNQMP_NUM_UARTS];
char *boot_cpu;
ARMCPU *boot_cpu_ptr;
} XlnxZynqMPState; } XlnxZynqMPState;
#define XLNX_ZYNQMP_H #define XLNX_ZYNQMP_H
......
...@@ -126,7 +126,6 @@ extern int cursor_hide; ...@@ -126,7 +126,6 @@ extern int cursor_hide;
extern int graphic_rotate; extern int graphic_rotate;
extern int no_quit; extern int no_quit;
extern int no_shutdown; extern int no_shutdown;
extern int semihosting_enabled;
extern int old_param; extern int old_param;
extern int boot_menu; extern int boot_menu;
extern bool boot_strict; extern bool boot_strict;
......
...@@ -3351,14 +3351,25 @@ STEXI ...@@ -3351,14 +3351,25 @@ STEXI
Enable semihosting mode (ARM, M68K, Xtensa only). Enable semihosting mode (ARM, M68K, Xtensa only).
ETEXI ETEXI
DEF("semihosting-config", HAS_ARG, QEMU_OPTION_semihosting_config, 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) QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32)
STEXI 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 @findex -semihosting-config
Enable semihosting and define where the semihosting calls will be addressed, Enable and configure semihosting (ARM, M68K, Xtensa only).
to QEMU (@code{native}) or to GDB (@code{gdb}). The default is @code{auto}, which means @table @option
@code{gdb} during debug sessions and @code{native} otherwise (ARM, M68K, Xtensa only). @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 ETEXI
DEF("old-param", 0, QEMU_OPTION_old_param, DEF("old-param", 0, QEMU_OPTION_old_param,
"-old-param old param mode\n", QEMU_ARCH_ARM) "-old-param old param mode\n", QEMU_ARCH_ARM)
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <time.h> #include <time.h>
#include "cpu.h" #include "cpu.h"
#include "exec/semihost.h"
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
#include "qemu.h" #include "qemu.h"
...@@ -440,10 +441,7 @@ uint32_t do_arm_semihosting(CPUARMState *env) ...@@ -440,10 +441,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
input_size = arg1; input_size = arg1;
/* Compute the size of the output string. */ /* Compute the size of the output string. */
#if !defined(CONFIG_USER_ONLY) #if !defined(CONFIG_USER_ONLY)
output_size = strlen(ts->boot_info->kernel_filename) output_size = strlen(semihosting_get_cmdline()) + 1;
+ 1 /* Separating space. */
+ strlen(ts->boot_info->kernel_cmdline)
+ 1; /* Terminating null byte. */
#else #else
unsigned int i; unsigned int i;
...@@ -474,9 +472,7 @@ uint32_t do_arm_semihosting(CPUARMState *env) ...@@ -474,9 +472,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
/* Copy the command-line arguments. */ /* Copy the command-line arguments. */
#if !defined(CONFIG_USER_ONLY) #if !defined(CONFIG_USER_ONLY)
pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename); pstrcpy(output_buffer, output_size, semihosting_get_cmdline());
pstrcat(output_buffer, output_size, " ");
pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline);
#else #else
if (output_size == 1) { if (output_size == 1) {
/* Empty command-line. */ /* Empty command-line. */
......
...@@ -105,6 +105,8 @@ typedef struct ARMCPU { ...@@ -105,6 +105,8 @@ typedef struct ARMCPU {
/* CPU has memory protection unit */ /* CPU has memory protection unit */
bool has_mpu; bool has_mpu;
/* PMSAv7 MPU number of supported regions */
uint32_t pmsav7_dregion;
/* PSCI conduit used to invoke PSCI methods /* PSCI conduit used to invoke PSCI methods
* 0 - disabled, 1 - smc, 2 - hvc * 0 - disabled, 1 - smc, 2 - hvc
......
...@@ -55,7 +55,7 @@ static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) ...@@ -55,7 +55,7 @@ static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque)
ARMCPRegInfo *ri = value; ARMCPRegInfo *ri = value;
ARMCPU *cpu = opaque; ARMCPU *cpu = opaque;
if (ri->type & ARM_CP_SPECIAL) { if (ri->type & (ARM_CP_SPECIAL | ARM_CP_ALIAS)) {
return; return;
} }
...@@ -457,6 +457,9 @@ static Property arm_cpu_has_el3_property = ...@@ -457,6 +457,9 @@ static Property arm_cpu_has_el3_property =
static Property arm_cpu_has_mpu_property = static Property arm_cpu_has_mpu_property =
DEFINE_PROP_BOOL("has-mpu", ARMCPU, has_mpu, true); 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) static void arm_cpu_post_init(Object *obj)
{ {
ARMCPU *cpu = ARM_CPU(obj); ARMCPU *cpu = ARM_CPU(obj);
...@@ -488,6 +491,11 @@ static void arm_cpu_post_init(Object *obj) ...@@ -488,6 +491,11 @@ static void arm_cpu_post_init(Object *obj)
if (arm_feature(&cpu->env, ARM_FEATURE_MPU)) { if (arm_feature(&cpu->env, ARM_FEATURE_MPU)) {
qdev_property_add_static(DEVICE(obj), &arm_cpu_has_mpu_property, qdev_property_add_static(DEVICE(obj), &arm_cpu_has_mpu_property,
&error_abort); &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) ...@@ -580,6 +588,22 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
unset_feature(env, ARM_FEATURE_MPU); 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); register_cp_regs_for_features(cpu);
arm_cpu_register_gdb_regs_for_features(cpu); arm_cpu_register_gdb_regs_for_features(cpu);
...@@ -812,6 +836,15 @@ static void cortex_m3_initfn(Object *obj) ...@@ -812,6 +836,15 @@ static void cortex_m3_initfn(Object *obj)
cpu->midr = 0x410fc231; 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) static void arm_v7m_class_init(ObjectClass *oc, void *data)
{ {
CPUClass *cc = CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc);
...@@ -823,6 +856,43 @@ static void arm_v7m_class_init(ObjectClass *oc, void *data) ...@@ -823,6 +856,43 @@ static void arm_v7m_class_init(ObjectClass *oc, void *data)
cc->cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt; 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[] = { static const ARMCPRegInfo cortexa8_cp_reginfo[] = {
{ .name = "L2LOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 0, { .name = "L2LOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 0,
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
...@@ -1214,6 +1284,9 @@ static const ARMCPUInfo arm_cpus[] = { ...@@ -1214,6 +1284,9 @@ static const ARMCPUInfo arm_cpus[] = {
{ .name = "arm11mpcore", .initfn = arm11mpcore_initfn }, { .name = "arm11mpcore", .initfn = arm11mpcore_initfn },
{ .name = "cortex-m3", .initfn = cortex_m3_initfn, { .name = "cortex-m3", .initfn = cortex_m3_initfn,
.class_init = arm_v7m_class_init }, .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-a8", .initfn = cortex_a8_initfn },
{ .name = "cortex-a9", .initfn = cortex_a9_initfn }, { .name = "cortex-a9", .initfn = cortex_a9_initfn },
{ .name = "cortex-a15", .initfn = cortex_a15_initfn }, { .name = "cortex-a15", .initfn = cortex_a15_initfn },
......
...@@ -284,6 +284,9 @@ typedef struct CPUARMState { ...@@ -284,6 +284,9 @@ typedef struct CPUARMState {
}; };
uint64_t par_el[4]; uint64_t par_el[4];
}; };
uint32_t c6_rgnr;
uint32_t c9_insn; /* Cache lockdown registers. */ uint32_t c9_insn; /* Cache lockdown registers. */
uint32_t c9_data; uint32_t c9_data;
uint64_t c9_pmcr; /* performance monitor control register */ uint64_t c9_pmcr; /* performance monitor control register */
...@@ -482,6 +485,13 @@ typedef struct CPUARMState { ...@@ -482,6 +485,13 @@ typedef struct CPUARMState {
/* Internal CPU feature flags. */ /* Internal CPU feature flags. */
uint64_t features; uint64_t features;
/* PMSAv7 MPU */
struct {
uint32_t *drbar;
uint32_t *drsr;
uint32_t *dracr;
} pmsav7;
void *nvic; void *nvic;
const struct arm_boot_info *boot_info; const struct arm_boot_info *boot_info;
} CPUARMState; } CPUARMState;
...@@ -550,6 +560,7 @@ void pmccntr_sync(CPUARMState *env); ...@@ -550,6 +560,7 @@ void pmccntr_sync(CPUARMState *env);
#define SCTLR_DT (1U << 16) /* up to ??, RAO in v6 and v7 */ #define SCTLR_DT (1U << 16) /* up to ??, RAO in v6 and v7 */
#define SCTLR_nTWI (1U << 16) /* v8 onward */ #define SCTLR_nTWI (1U << 16) /* v8 onward */
#define SCTLR_HA (1U << 17) #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_IT (1U << 18) /* up to ??, RAO in v6 and v7 */
#define SCTLR_nTWE (1U << 18) /* v8 onward */ #define SCTLR_nTWE (1U << 18) /* v8 onward */
#define SCTLR_WXN (1U << 19) #define SCTLR_WXN (1U << 19)
...@@ -1116,8 +1127,8 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) ...@@ -1116,8 +1127,8 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid)
* old must have the OVERRIDE bit set. * old must have the OVERRIDE bit set.
* ALIAS indicates that this register is an alias view of some underlying * ALIAS indicates that this register is an alias view of some underlying
* state which is also visible via another register, and that the other * state which is also visible via another register, and that the other
* register is handling migration; registers marked ALIAS will not be migrated * register is handling migration and reset; registers marked ALIAS will not be
* but may have their state set by syncing of register state from KVM. * 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 * 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 * support raw access for state saving/loading; it will not be used for either
* migration or KVM state synchronization. (Typically this is for "registers" * migration or KVM state synchronization. (Typically this is for "registers"
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "exec/cpu_ldst.h" #include "exec/cpu_ldst.h"
#include "arm_ldst.h" #include "arm_ldst.h"
#include <zlib.h> /* For crc32 */ #include <zlib.h> /* For crc32 */
#include "exec/semihost.h"
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
static inline bool get_phys_addr(CPUARMState *env, target_ulong address, static inline bool get_phys_addr(CPUARMState *env, target_ulong address,
...@@ -984,7 +985,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { ...@@ -984,7 +985,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
{ .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2, { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2,
.access = PL1_RW, .type = ARM_CP_ALIAS, .access = PL1_RW, .type = ARM_CP_ALIAS,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
.resetvalue = 0, .writefn = pmintenclr_write, }, .writefn = pmintenclr_write, },
{ .name = "VBAR", .state = ARM_CP_STATE_BOTH, { .name = "VBAR", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0, .opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .writefn = vbar_write, .access = PL1_RW, .writefn = vbar_write,
...@@ -1323,7 +1324,6 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { ...@@ -1323,7 +1324,6 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
.type = ARM_CP_ALIAS, .type = ARM_CP_ALIAS,
.access = PL1_RW | PL0_R, .accessfn = gt_cntfrq_access, .access = PL1_RW | PL0_R, .accessfn = gt_cntfrq_access,
.fieldoffset = offsetoflow32(CPUARMState, cp15.c14_cntfrq), .fieldoffset = offsetoflow32(CPUARMState, cp15.c14_cntfrq),
.resetfn = arm_cp_reset_ignore,
}, },
{ .name = "CNTFRQ_EL0", .state = ARM_CP_STATE_AA64, { .name = "CNTFRQ_EL0", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 0, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 0,
...@@ -1344,7 +1344,6 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { ...@@ -1344,7 +1344,6 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
.accessfn = gt_ptimer_access, .accessfn = gt_ptimer_access,
.fieldoffset = offsetoflow32(CPUARMState, .fieldoffset = offsetoflow32(CPUARMState,
cp15.c14_timer[GTIMER_PHYS].ctl), cp15.c14_timer[GTIMER_PHYS].ctl),
.resetfn = arm_cp_reset_ignore,
.writefn = gt_ctl_write, .raw_writefn = raw_write, .writefn = gt_ctl_write, .raw_writefn = raw_write,
}, },
{ .name = "CNTP_CTL_EL0", .state = ARM_CP_STATE_AA64, { .name = "CNTP_CTL_EL0", .state = ARM_CP_STATE_AA64,
...@@ -1360,7 +1359,6 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { ...@@ -1360,7 +1359,6 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
.accessfn = gt_vtimer_access, .accessfn = gt_vtimer_access,
.fieldoffset = offsetoflow32(CPUARMState, .fieldoffset = offsetoflow32(CPUARMState,
cp15.c14_timer[GTIMER_VIRT].ctl), cp15.c14_timer[GTIMER_VIRT].ctl),
.resetfn = arm_cp_reset_ignore,
.writefn = gt_ctl_write, .raw_writefn = raw_write, .writefn = gt_ctl_write, .raw_writefn = raw_write,
}, },
{ .name = "CNTV_CTL_EL0", .state = ARM_CP_STATE_AA64, { .name = "CNTV_CTL_EL0", .state = ARM_CP_STATE_AA64,
...@@ -1422,7 +1420,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { ...@@ -1422,7 +1420,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
.access = PL1_RW | PL0_R, .access = PL1_RW | PL0_R,
.type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), .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, .writefn = gt_cval_write, .raw_writefn = raw_write,
}, },
{ .name = "CNTP_CVAL_EL0", .state = ARM_CP_STATE_AA64, { .name = "CNTP_CVAL_EL0", .state = ARM_CP_STATE_AA64,
...@@ -1437,7 +1435,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { ...@@ -1437,7 +1435,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
.access = PL1_RW | PL0_R, .access = PL1_RW | PL0_R,
.type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), .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, .writefn = gt_cval_write, .raw_writefn = raw_write,
}, },
{ .name = "CNTV_CVAL_EL0", .state = ARM_CP_STATE_AA64, { .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) ...@@ -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); 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[] = { static const ARMCPRegInfo pmsav5_cp_reginfo[] = {
{ .name = "DATA_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, { .name = "DATA_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .type = ARM_CP_ALIAS, .access = PL1_RW, .type = ARM_CP_ALIAS,
.fieldoffset = offsetof(CPUARMState, cp15.pmsav5_data_ap), .fieldoffset = offsetof(CPUARMState, cp15.pmsav5_data_ap),
.resetvalue = 0,
.readfn = pmsav5_data_ap_read, .writefn = pmsav5_data_ap_write, }, .readfn = pmsav5_data_ap_read, .writefn = pmsav5_data_ap_write, },
{ .name = "INSN_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1, { .name = "INSN_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1,
.access = PL1_RW, .type = ARM_CP_ALIAS, .access = PL1_RW, .type = ARM_CP_ALIAS,
.fieldoffset = offsetof(CPUARMState, cp15.pmsav5_insn_ap), .fieldoffset = offsetof(CPUARMState, cp15.pmsav5_insn_ap),
.resetvalue = 0,
.readfn = pmsav5_insn_ap_read, .writefn = pmsav5_insn_ap_write, }, .readfn = pmsav5_insn_ap_read, .writefn = pmsav5_insn_ap_write, },
{ .name = "DATA_EXT_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 2, { .name = "DATA_EXT_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 2,
.access = PL1_RW, .access = PL1_RW,
...@@ -1851,8 +1922,7 @@ static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = { ...@@ -1851,8 +1922,7 @@ static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = {
{ .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, { .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .type = ARM_CP_ALIAS, .access = PL1_RW, .type = ARM_CP_ALIAS,
.bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dfsr_s), .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dfsr_s),
offsetoflow32(CPUARMState, cp15.dfsr_ns) }, offsetoflow32(CPUARMState, cp15.dfsr_ns) }, },
.resetfn = arm_cp_reset_ignore, },
{ .name = "IFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1, { .name = "IFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1,
.access = PL1_RW, .resetvalue = 0, .access = PL1_RW, .resetvalue = 0,
.bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.ifsr_s), .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.ifsr_s),
...@@ -1890,7 +1960,7 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { ...@@ -1890,7 +1960,7 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
.fieldoffset = offsetof(CPUARMState, cp15.tcr_el[1]) }, .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[1]) },
{ .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, { .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2,
.access = PL1_RW, .type = ARM_CP_ALIAS, .writefn = vmsa_ttbcr_write, .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]), .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tcr_el[3]),
offsetoflow32(CPUARMState, cp15.tcr_el[1])} }, offsetoflow32(CPUARMState, cp15.tcr_el[1])} },
REGINFO_SENTINEL REGINFO_SENTINEL
...@@ -2109,12 +2179,12 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { ...@@ -2109,12 +2179,12 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = {
.access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS, .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS,
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s), .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s),
offsetof(CPUARMState, cp15.ttbr0_ns) }, 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, { .name = "TTBR1", .cp = 15, .crm = 2, .opc1 = 1,
.access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS, .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS,
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s), .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s),
offsetof(CPUARMState, cp15.ttbr1_ns) }, offsetof(CPUARMState, cp15.ttbr1_ns) },
.writefn = vmsa_ttbr_write, .resetfn = arm_cp_reset_ignore }, .writefn = vmsa_ttbr_write, },
REGINFO_SENTINEL REGINFO_SENTINEL
}; };
...@@ -2641,7 +2711,6 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { ...@@ -2641,7 +2711,6 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) }, .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) },
{ .name = "HTTBR", .cp = 15, .opc1 = 4, .crm = 2, { .name = "HTTBR", .cp = 15, .opc1 = 4, .crm = 2,
.access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS, .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS,
.resetvalue = 0,
.fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) }, .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) },
{ .name = "TLBI_ALLE2", .state = ARM_CP_STATE_AA64, { .name = "TLBI_ALLE2", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0,
...@@ -2666,7 +2735,7 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { ...@@ -2666,7 +2735,7 @@ static const ARMCPRegInfo el3_cp_reginfo[] = {
{ .name = "SCR", .type = ARM_CP_ALIAS, { .name = "SCR", .type = ARM_CP_ALIAS,
.cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 0, .cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 0,
.access = PL3_RW, .fieldoffset = offsetoflow32(CPUARMState, cp15.scr_el3), .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, { .name = "SDER32_EL3", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 6, .crn = 1, .crm = 1, .opc2 = 1, .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 1, .opc2 = 1,
.access = PL3_RW, .resetvalue = 0, .access = PL3_RW, .resetvalue = 0,
...@@ -2761,8 +2830,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { ...@@ -2761,8 +2830,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = {
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0,
.type = ARM_CP_ALIAS, .type = ARM_CP_ALIAS,
.access = PL1_R, .access = PL1_R,
.fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), },
.resetfn = arm_cp_reset_ignore },
/* We define a dummy WI OSLAR_EL1, because Linux writes to it. */ /* We define a dummy WI OSLAR_EL1, because Linux writes to it. */
{ .name = "OSLAR_EL1", .state = ARM_CP_STATE_BOTH, { .name = "OSLAR_EL1", .state = ARM_CP_STATE_BOTH,
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 4, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 4,
...@@ -3345,13 +3413,14 @@ void register_cp_regs_for_features(ARMCPU *cpu) ...@@ -3345,13 +3413,14 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_one_arm_cp_reg(cpu, &rvbar); define_one_arm_cp_reg(cpu, &rvbar);
} }
if (arm_feature(env, ARM_FEATURE_MPU)) { if (arm_feature(env, ARM_FEATURE_MPU)) {
/* These are the MPU registers prior to PMSAv6. Any new if (arm_feature(env, ARM_FEATURE_V6)) {
* PMSA core later than the ARM946 will require that we /* PMSAv6 not implemented */
* implement the PMSAv6 or PMSAv7 registers, which are assert(arm_feature(env, ARM_FEATURE_V7));
* completely different. define_arm_cp_regs(cpu, vmsa_pmsa_cp_reginfo);
*/ define_arm_cp_regs(cpu, pmsav7_cp_reginfo);
assert(!arm_feature(env, ARM_FEATURE_V6)); } else {
define_arm_cp_regs(cpu, pmsav5_cp_reginfo); define_arm_cp_regs(cpu, pmsav5_cp_reginfo);
}
} else { } else {
define_arm_cp_regs(cpu, vmsa_pmsa_cp_reginfo); define_arm_cp_regs(cpu, vmsa_pmsa_cp_reginfo);
define_arm_cp_regs(cpu, vmsa_cp_reginfo); define_arm_cp_regs(cpu, vmsa_cp_reginfo);
...@@ -3465,6 +3534,13 @@ void register_cp_regs_for_features(ARMCPU *cpu) ...@@ -3465,6 +3534,13 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 3, .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 3,
.access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0, .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 = { ARMCPRegInfo crn0_wi_reginfo = {
.name = "CRN0_WI", .cp = 15, .crn = 0, .crm = CP_ANY, .name = "CRN0_WI", .cp = 15, .crn = 0, .crm = CP_ANY,
.opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_W, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_W,
...@@ -3487,6 +3563,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) ...@@ -3487,6 +3563,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
r->access = PL1_RW; r->access = PL1_RW;
} }
id_tlbtr_reginfo.access = PL1_RW; id_tlbtr_reginfo.access = PL1_RW;
id_tlbtr_reginfo.access = PL1_RW;
} }
if (arm_feature(env, ARM_FEATURE_V8)) { if (arm_feature(env, ARM_FEATURE_V8)) {
define_arm_cp_regs(cpu, id_v8_midr_cp_reginfo); define_arm_cp_regs(cpu, id_v8_midr_cp_reginfo);
...@@ -3496,6 +3573,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) ...@@ -3496,6 +3573,8 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_arm_cp_regs(cpu, id_cp_reginfo); define_arm_cp_regs(cpu, id_cp_reginfo);
if (!arm_feature(env, ARM_FEATURE_MPU)) { if (!arm_feature(env, ARM_FEATURE_MPU)) {
define_one_arm_cp_reg(cpu, &id_tlbtr_reginfo); 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, ...@@ -3721,14 +3800,12 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r,
if ((r->state == ARM_CP_STATE_BOTH && ns) || if ((r->state == ARM_CP_STATE_BOTH && ns) ||
(arm_feature(&cpu->env, ARM_FEATURE_V8) && !ns)) { (arm_feature(&cpu->env, ARM_FEATURE_V8) && !ns)) {
r2->type |= ARM_CP_ALIAS; r2->type |= ARM_CP_ALIAS;
r2->resetfn = arm_cp_reset_ignore;
} }
} else if ((secstate != r->secure) && !ns) { } else if ((secstate != r->secure) && !ns) {
/* The register is not banked so we only want to allow migration of /* The register is not banked so we only want to allow migration of
* the non-secure instance. * the non-secure instance.
*/ */
r2->type |= ARM_CP_ALIAS; r2->type |= ARM_CP_ALIAS;
r2->resetfn = arm_cp_reset_ignore;
} }
if (r->state == ARM_CP_STATE_BOTH) { if (r->state == ARM_CP_STATE_BOTH) {
...@@ -4478,7 +4555,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) ...@@ -4478,7 +4555,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM); armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM);
return; return;
case EXCP_BKPT: case EXCP_BKPT:
if (semihosting_enabled) { if (semihosting_enabled()) {
int nr; int nr;
nr = arm_lduw_code(env, env->regs[15], env->bswap_code) & 0xff; nr = arm_lduw_code(env, env->regs[15], env->bswap_code) & 0xff;
if (nr == 0xab) { if (nr == 0xab) {
...@@ -4790,7 +4867,7 @@ void arm_cpu_do_interrupt(CPUState *cs) ...@@ -4790,7 +4867,7 @@ void arm_cpu_do_interrupt(CPUState *cs)
offset = 4; offset = 4;
break; break;
case EXCP_SWI: case EXCP_SWI:
if (semihosting_enabled) { if (semihosting_enabled()) {
/* Check for semihosting interrupt. */ /* Check for semihosting interrupt. */
if (env->thumb) { if (env->thumb) {
mask = arm_lduw_code(env, env->regs[15] - 2, env->bswap_code) mask = arm_lduw_code(env, env->regs[15] - 2, env->bswap_code)
...@@ -4817,7 +4894,7 @@ void arm_cpu_do_interrupt(CPUState *cs) ...@@ -4817,7 +4894,7 @@ void arm_cpu_do_interrupt(CPUState *cs)
break; break;
case EXCP_BKPT: case EXCP_BKPT:
/* See if this is a semihosting syscall. */ /* 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; mask = arm_lduw_code(env, env->regs[15], env->bswap_code) & 0xff;
if (mask == 0xab if (mask == 0xab
&& (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) { && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) {
...@@ -5759,6 +5836,167 @@ do_fault: ...@@ -5759,6 +5836,167 @@ do_fault:
return true; 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, static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address,
int access_type, ARMMMUIdx mmu_idx, int access_type, ARMMMUIdx mmu_idx,
hwaddr *phys_ptr, int *prot, uint32_t *fsr) hwaddr *phys_ptr, int *prot, uint32_t *fsr)
...@@ -5844,7 +6082,7 @@ static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address, ...@@ -5844,7 +6082,7 @@ static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address,
* DFSR/IFSR fault register, with the following caveats: * DFSR/IFSR fault register, with the following caveats:
* * we honour the short vs long DFSR format differences. * * we honour the short vs long DFSR format differences.
* * the WnR bit is never set (the caller must do this). * * 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. * value.
* *
* @env: CPUARMState * @env: CPUARMState
...@@ -5892,6 +6130,16 @@ static inline bool get_phys_addr(CPUARMState *env, target_ulong address, ...@@ -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)) { if (regime_translation_disabled(env, mmu_idx)) {
/* MMU/MPU disabled. */ /* MMU/MPU disabled. */
*phys_ptr = address; *phys_ptr = address;
...@@ -5901,6 +6149,7 @@ static inline bool get_phys_addr(CPUARMState *env, target_ulong address, ...@@ -5901,6 +6149,7 @@ static inline bool get_phys_addr(CPUARMState *env, target_ulong address,
} }
if (arm_feature(env, ARM_FEATURE_MPU)) { if (arm_feature(env, ARM_FEATURE_MPU)) {
/* Pre-v7 MPU */
*page_size = TARGET_PAGE_SIZE; *page_size = TARGET_PAGE_SIZE;
return get_phys_addr_pmsav5(env, address, access_type, mmu_idx, return get_phys_addr_pmsav5(env, address, access_type, mmu_idx,
phys_ptr, prot, fsr); phys_ptr, prot, fsr);
......
...@@ -125,6 +125,39 @@ static const VMStateDescription vmstate_thumb2ee = { ...@@ -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) static int get_cpsr(QEMUFile *f, void *opaque, size_t size)
{ {
ARMCPU *cpu = opaque; ARMCPU *cpu = opaque;
...@@ -291,6 +324,7 @@ const VMStateDescription vmstate_arm_cpu = { ...@@ -291,6 +324,7 @@ const VMStateDescription vmstate_arm_cpu = {
&vmstate_iwmmxt, &vmstate_iwmmxt,
&vmstate_m, &vmstate_m,
&vmstate_thumb2ee, &vmstate_thumb2ee,
&vmstate_pmsav7,
NULL NULL
} }
}; };
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "cpu.h" #include "cpu.h"
#include "qemu/host-utils.h" #include "qemu/host-utils.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "exec/semihost.h"
int lm32_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int lm32_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
int mmu_idx) int mmu_idx)
...@@ -162,7 +163,7 @@ void lm32_cpu_do_interrupt(CPUState *cs) ...@@ -162,7 +163,7 @@ void lm32_cpu_do_interrupt(CPUState *cs)
switch (cs->exception_index) { switch (cs->exception_index) {
case EXCP_SYSTEMCALL: case EXCP_SYSTEMCALL:
if (unlikely(semihosting_enabled)) { if (unlikely(semihosting_enabled())) {
/* do_semicall() returns true if call was handled. Otherwise /* do_semicall() returns true if call was handled. Otherwise
* do the normal exception handling. */ * do the normal exception handling. */
if (lm32_cpu_do_semihosting(cs)) { if (lm32_cpu_do_semihosting(cs)) {
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "cpu.h" #include "cpu.h"
#include "exec/helper-proto.h" #include "exec/helper-proto.h"
#include "exec/cpu_ldst.h" #include "exec/cpu_ldst.h"
#include "exec/semihost.h"
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
...@@ -33,8 +34,6 @@ static inline void do_interrupt_m68k_hardirq(CPUM68KState *env) ...@@ -33,8 +34,6 @@ static inline void do_interrupt_m68k_hardirq(CPUM68KState *env)
#else #else
extern int semihosting_enabled;
/* Try to fill the TLB and return an exception if error. If retaddr is /* 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 NULL, it means that the function was called in C code (i.e. not
from generated code or from helper.c) */ from generated code or from helper.c) */
...@@ -85,7 +84,7 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw) ...@@ -85,7 +84,7 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw)
do_rte(env); do_rte(env);
return; return;
case EXCP_HALT_INSN: case EXCP_HALT_INSN:
if (semihosting_enabled if (semihosting_enabled()
&& (env->sr & SR_S) != 0 && (env->sr & SR_S) != 0
&& (env->pc & 3) == 0 && (env->pc & 3) == 0
&& cpu_lduw_code(env, env->pc - 4) == 0x4e71 && cpu_lduw_code(env, env->pc - 4) == 0x4e71
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "qemu/log.h" #include "qemu/log.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "exec/cpu_ldst.h" #include "exec/cpu_ldst.h"
#include "exec/semihost.h"
#include "exec/helper-proto.h" #include "exec/helper-proto.h"
#include "exec/helper-gen.h" #include "exec/helper-gen.h"
...@@ -1216,7 +1217,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) ...@@ -1216,7 +1217,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
break; break;
case 1: /*SIMCALL*/ case 1: /*SIMCALL*/
if (semihosting_enabled) { if (semihosting_enabled()) {
if (gen_check_privilege(dc)) { if (gen_check_privilege(dc)) {
gen_helper_simcall(cpu_env); gen_helper_simcall(cpu_env);
} }
......
...@@ -119,6 +119,7 @@ int main(int argc, char **argv) ...@@ -119,6 +119,7 @@ int main(int argc, char **argv)
#include "qapi/opts-visitor.h" #include "qapi/opts-visitor.h"
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
#include "qapi-event.h" #include "qapi-event.h"
#include "exec/semihost.h"
#define MAX_VIRTIO_CONSOLES 1 #define MAX_VIRTIO_CONSOLES 1
#define MAX_SCLP_CONSOLES 1 #define MAX_SCLP_CONSOLES 1
...@@ -169,7 +170,6 @@ int graphic_rotate = 0; ...@@ -169,7 +170,6 @@ int graphic_rotate = 0;
const char *watchdog; const char *watchdog;
QEMUOptionRom option_rom[MAX_OPTION_ROMS]; QEMUOptionRom option_rom[MAX_OPTION_ROMS];
int nb_option_roms; int nb_option_roms;
int semihosting_enabled = 0;
int old_param = 0; int old_param = 0;
const char *qemu_name; const char *qemu_name;
int alt_grab = 0; int alt_grab = 0;
...@@ -488,6 +488,9 @@ static QemuOptsList qemu_semihosting_config_opts = { ...@@ -488,6 +488,9 @@ static QemuOptsList qemu_semihosting_config_opts = {
}, { }, {
.name = "target", .name = "target",
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
}, {
.name = "arg",
.type = QEMU_OPT_STRING,
}, },
{ /* end of list */ } { /* end of list */ }
}, },
...@@ -1245,6 +1248,81 @@ static void configure_msg(QemuOpts *opts) ...@@ -1245,6 +1248,81 @@ static void configure_msg(QemuOpts *opts)
enable_timestamp_msg = qemu_opt_get_bool(opts, "timestamp", true); 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 */ /* USB devices */
...@@ -3622,24 +3700,24 @@ int main(int argc, char **argv, char **envp) ...@@ -3622,24 +3700,24 @@ int main(int argc, char **argv, char **envp)
nb_option_roms++; nb_option_roms++;
break; break;
case QEMU_OPTION_semihosting: case QEMU_OPTION_semihosting:
semihosting_enabled = 1; semihosting.enabled = true;
semihosting_target = SEMIHOSTING_TARGET_AUTO; semihosting.target = SEMIHOSTING_TARGET_AUTO;
break; break;
case QEMU_OPTION_semihosting_config: case QEMU_OPTION_semihosting_config:
semihosting_enabled = 1; semihosting.enabled = true;
opts = qemu_opts_parse(qemu_find_opts("semihosting-config"), opts = qemu_opts_parse(qemu_find_opts("semihosting-config"),
optarg, 0); optarg, 0);
if (opts != NULL) { if (opts != NULL) {
semihosting_enabled = qemu_opt_get_bool(opts, "enable", semihosting.enabled = qemu_opt_get_bool(opts, "enable",
true); true);
const char *target = qemu_opt_get(opts, "target"); const char *target = qemu_opt_get(opts, "target");
if (target != NULL) { if (target != NULL) {
if (strcmp("native", target) == 0) { if (strcmp("native", target) == 0) {
semihosting_target = SEMIHOSTING_TARGET_NATIVE; semihosting.target = SEMIHOSTING_TARGET_NATIVE;
} else if (strcmp("gdb", target) == 0) { } else if (strcmp("gdb", target) == 0) {
semihosting_target = SEMIHOSTING_TARGET_GDB; semihosting.target = SEMIHOSTING_TARGET_GDB;
} else if (strcmp("auto", target) == 0) { } else if (strcmp("auto", target) == 0) {
semihosting_target = SEMIHOSTING_TARGET_AUTO; semihosting.target = SEMIHOSTING_TARGET_AUTO;
} else { } else {
fprintf(stderr, "Unsupported semihosting-config" fprintf(stderr, "Unsupported semihosting-config"
" %s\n", " %s\n",
...@@ -3647,8 +3725,11 @@ int main(int argc, char **argv, char **envp) ...@@ -3647,8 +3725,11 @@ int main(int argc, char **argv, char **envp)
exit(1); exit(1);
} }
} else { } 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 { } else {
fprintf(stderr, "Unsupported semihosting-config %s\n", fprintf(stderr, "Unsupported semihosting-config %s\n",
optarg); optarg);
...@@ -4217,6 +4298,11 @@ int main(int argc, char **argv, char **envp) ...@@ -4217,6 +4298,11 @@ int main(int argc, char **argv, char **envp)
exit(1); 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(); os_set_line_buffering();
#ifdef CONFIG_SPICE #ifdef CONFIG_SPICE
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册