aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2018-08-20 13:22:21 +0100
committerPeter Maydell <peter.maydell@linaro.org>2018-08-20 13:22:21 +0100
commit62c34848efb41f0e81af0c6b4f1d5d577039eec9 (patch)
tree85f6f2cb120c87ae287e195b082b024212e5229f
parent627fce617868df87b3757375a2a0318ad2beb381 (diff)
parentb85fad1588e812566f897f747e38da345a7016d6 (diff)
downloadqemu-62c34848efb41f0e81af0c6b4f1d5d577039eec9.zip
qemu-62c34848efb41f0e81af0c6b4f1d5d577039eec9.tar.gz
qemu-62c34848efb41f0e81af0c6b4f1d5d577039eec9.tar.bz2
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20180820' into staging
target-arm queue: * Fix crash on conditional instruction in an IT block * docs/generic-loader: mention U-Boot and Intel HEX executable formats * hw/intc/arm_gicv3_its: downgrade error_report to warn_report in kvm_arm_its_reset * imx_serial: Generate interrupt on receive data ready if enabled * Fix various minor bugs in AArch32 Hyp related coprocessor registers * Permit accesses to ELR_Hyp from Hyp mode via MSR/MRS (banked) * Implement AArch32 ERET instruction * hw/arm/virt: Add virt-3.1 machine type * sdhci: add i.MX SD Stable Clock bit * Remove now-obsolete MMIO request_ptr APIs * hw/timer/m48t59: Move away from old_mmio accessors * hw/watchdog/cmsdk_apb_watchdog: Implement CMSDK APB watchdog module * nvic: Expose NMI line * hw/dma/pl080: cleanups and new features required for use in MPS boards # gpg: Signature made Mon 20 Aug 2018 11:30:12 BST # gpg: using RSA key 3C2525ED14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" # gpg: aka "Peter Maydell <pmaydell@gmail.com>" # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20180820: (25 commits) hw/dma/pl080: Remove hw_error() if DMA is enabled hw/dma/pl080: Correct bug in register address decode logic hw/dma/pl080: Provide device reset function hw/dma/pl080: Don't use CPU address space for DMA accesses hw/dma/pl080: Support all three interrupt lines hw/dma/pl080: Allow use as embedded-struct device nvic: Expose NMI line hw/watchdog/cmsdk_apb_watchdog: Implement CMSDK APB watchdog module hw/timer/m48t59: Move away from old_mmio accessors hw/misc: Remove mmio_interface device memory: Remove MMIO request_ptr APIs hw/ssi/xilinx_spips: Remove unneeded MMIO request_ptr code sdhci: add i.MX SD Stable Clock bit hw/arm/virt: Add virt-3.1 machine type target/arm: Implement AArch32 ERET instruction target/arm: Permit accesses to ELR_Hyp from Hyp mode via MSR/MRS (banked) target/arm: Implement ESR_EL2/HSR for AArch32 and no-EL2 target/arm: Implement AArch32 Hyp FARs target/arm: Implement AArch32 HVBAR target/arm: Add missing .cp = 15 to HMAIR1 and HAMAIR1 regdefs ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--MAINTAINERS3
-rw-r--r--Makefile.objs1
-rw-r--r--default-configs/arm-softmmu.mak1
-rw-r--r--docs/generic-loader.txt20
-rw-r--r--hw/arm/armv7m.c1
-rw-r--r--hw/arm/realview.c8
-rw-r--r--hw/arm/versatilepb.c9
-rw-r--r--hw/arm/virt.c21
-rw-r--r--hw/char/imx_serial.c3
-rw-r--r--hw/dma/pl080.c113
-rw-r--r--hw/intc/arm_gicv3_its_kvm.c2
-rw-r--r--hw/intc/armv7m_nvic.c19
-rw-r--r--hw/intc/trace-events1
-rw-r--r--hw/misc/Makefile.objs1
-rw-r--r--hw/misc/mmio_interface.c135
-rw-r--r--hw/sd/sdhci-internal.h2
-rw-r--r--hw/sd/sdhci.c8
-rw-r--r--hw/ssi/xilinx_spips.c46
-rw-r--r--hw/timer/m48t59.c59
-rw-r--r--hw/watchdog/Makefile.objs1
-rw-r--r--hw/watchdog/cmsdk-apb-watchdog.c326
-rw-r--r--hw/watchdog/trace-events6
-rw-r--r--include/exec/memory.h35
-rw-r--r--include/hw/char/imx_serial.h1
-rw-r--r--include/hw/dma/pl080.h71
-rw-r--r--include/hw/misc/mmio_interface.h49
-rw-r--r--include/hw/watchdog/cmsdk-apb-watchdog.h59
-rw-r--r--memory.c110
-rw-r--r--target/arm/helper.c36
-rw-r--r--target/arm/op_helper.c22
-rw-r--r--target/arm/translate.c76
31 files changed, 716 insertions, 529 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 70651f7..6902a56 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -445,6 +445,7 @@ F: hw/char/pl011.c
F: include/hw/char/pl011.h
F: hw/display/pl110*
F: hw/dma/pl080.c
+F: include/hw/dma/pl080.h
F: hw/dma/pl330.c
F: hw/gpio/pl061.c
F: hw/input/pl050.c
@@ -456,6 +457,8 @@ F: hw/timer/cmsdk-apb-timer.c
F: include/hw/timer/cmsdk-apb-timer.h
F: hw/char/cmsdk-apb-uart.c
F: include/hw/char/cmsdk-apb-uart.h
+F: hw/watchdog/cmsdk-apb-watchdog.c
+F: include/hw/watchdog/cmsdk-apb-watchdog.h
F: hw/misc/tz-ppc.c
F: include/hw/misc/tz-ppc.h
F: hw/misc/tz-mpc.c
diff --git a/Makefile.objs b/Makefile.objs
index 7a9828d..ce9c792 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -240,6 +240,7 @@ trace-events-subdirs += hw/tpm
trace-events-subdirs += hw/usb
trace-events-subdirs += hw/vfio
trace-events-subdirs += hw/virtio
+trace-events-subdirs += hw/watchdog
trace-events-subdirs += hw/xen
trace-events-subdirs += io
trace-events-subdirs += linux-user
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 311584f..a92bc34 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -104,6 +104,7 @@ CONFIG_STM32F205_SOC=y
CONFIG_CMSDK_APB_TIMER=y
CONFIG_CMSDK_APB_UART=y
+CONFIG_CMSDK_APB_WATCHDOG=y
CONFIG_MPS2_FPGAIO=y
CONFIG_MPS2_SCC=y
diff --git a/docs/generic-loader.txt b/docs/generic-loader.txt
index 31bbcd4..a9603a2 100644
--- a/docs/generic-loader.txt
+++ b/docs/generic-loader.txt
@@ -56,25 +56,25 @@ An example of setting CPU 0's PC to 0x8000 is:
Loading Files
-------------
-The loader device also allows files to be loaded into memory. It can load raw
-files and ELF executable files. Raw files are loaded verbatim. ELF executable
-files are loaded by an ELF loader. The syntax is shown below:
+The loader device also allows files to be loaded into memory. It can load ELF,
+U-Boot, and Intel HEX executable formats as well as raw images. The syntax is
+shown below:
-device loader,file=<file>[,addr=<addr>][,cpu-num=<cpu-num>][,force-raw=<raw>]
<file> - A file to be loaded into memory
- <addr> - The addr in memory that the file should be loaded. This is
- ignored if you are using an ELF (unless force-raw is true).
- This is required if you aren't loading an ELF.
+ <addr> - The memory address where the file should be loaded. This is
+ required for raw images and ignored for non-raw files.
<cpu-num> - This specifies the CPU that should be used. This is an
optional argument and will cause the CPU's PC to be set to
- where the image is stored or in the case of an ELF file to
- the value in the header. This option should only be used
- for the boot image.
+ the memory address where the raw file is loaded or the entry
+ point specified in the executable format header. This option
+ should only be used for the boot image.
This will also cause the image to be written to the specified
CPU's address space. If not specified, the default is CPU 0.
<force-raw> - Setting force-raw=on forces the file to be treated as a raw
- image. This can be used to load ELF files as if they were raw.
+ image. This can be used to load supported executable formats
+ as if they were raw.
All values are parsed using the standard QemuOps parsing. This allows the user
to specify any values in any format supported. By default the values
diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c
index 8786139..4bf9131 100644
--- a/hw/arm/armv7m.c
+++ b/hw/arm/armv7m.c
@@ -202,6 +202,7 @@ static void armv7m_realize(DeviceState *dev, Error **errp)
*/
qdev_pass_gpios(DEVICE(&s->nvic), dev, NULL);
qdev_pass_gpios(DEVICE(&s->nvic), dev, "SYSRESETREQ");
+ qdev_pass_gpios(DEVICE(&s->nvic), dev, "NMI");
/* Wire the NVIC up to the CPU */
sbd = SYS_BUS_DEVICE(&s->nvic);
diff --git a/hw/arm/realview.c b/hw/arm/realview.c
index cd585d9..ab8c14f 100644
--- a/hw/arm/realview.c
+++ b/hw/arm/realview.c
@@ -201,7 +201,13 @@ static void realview_init(MachineState *machine,
pl011_create(0x1000c000, pic[15], serial_hd(3));
/* DMA controller is optional, apparently. */
- sysbus_create_simple("pl081", 0x10030000, pic[24]);
+ dev = qdev_create(NULL, "pl081");
+ object_property_set_link(OBJECT(dev), OBJECT(sysmem), "downstream",
+ &error_fatal);
+ qdev_init_nofail(dev);
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(busdev, 0, 0x10030000);
+ sysbus_connect_irq(busdev, 0, pic[24]);
sysbus_create_simple("sp804", 0x10011000, pic[4]);
sysbus_create_simple("sp804", 0x10012000, pic[5]);
diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c
index a5a06b6..8b74857 100644
--- a/hw/arm/versatilepb.c
+++ b/hw/arm/versatilepb.c
@@ -287,7 +287,14 @@ static void versatile_init(MachineState *machine, int board_id)
pl011_create(0x101f3000, pic[14], serial_hd(2));
pl011_create(0x10009000, sic[6], serial_hd(3));
- sysbus_create_simple("pl080", 0x10130000, pic[17]);
+ dev = qdev_create(NULL, "pl080");
+ object_property_set_link(OBJECT(dev), OBJECT(sysmem), "downstream",
+ &error_fatal);
+ qdev_init_nofail(dev);
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(busdev, 0, 0x10130000);
+ sysbus_connect_irq(busdev, 0, pic[17]);
+
sysbus_create_simple("sp804", 0x101e2000, pic[4]);
sysbus_create_simple("sp804", 0x101e3000, pic[5]);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 0807be9..0b57f87 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1791,10 +1791,7 @@ static void machvirt_machine_init(void)
}
type_init(machvirt_machine_init);
-#define VIRT_COMPAT_2_12 \
- HW_COMPAT_2_12
-
-static void virt_3_0_instance_init(Object *obj)
+static void virt_3_1_instance_init(Object *obj)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
@@ -1864,10 +1861,24 @@ static void virt_3_0_instance_init(Object *obj)
vms->irqmap = a15irqmap;
}
+static void virt_machine_3_1_options(MachineClass *mc)
+{
+}
+DEFINE_VIRT_MACHINE_AS_LATEST(3, 1)
+
+static void virt_3_0_instance_init(Object *obj)
+{
+ virt_3_1_instance_init(obj);
+}
+
static void virt_machine_3_0_options(MachineClass *mc)
{
+ virt_machine_3_1_options(mc);
}
-DEFINE_VIRT_MACHINE_AS_LATEST(3, 0)
+DEFINE_VIRT_MACHINE(3, 0)
+
+#define VIRT_COMPAT_2_12 \
+ HW_COMPAT_2_12
static void virt_2_12_instance_init(Object *obj)
{
diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c
index 0747db9..1e36319 100644
--- a/hw/char/imx_serial.c
+++ b/hw/char/imx_serial.c
@@ -74,8 +74,9 @@ static void imx_update(IMXSerialState *s)
mask = (s->ucr1 & UCR1_TXMPTYEN) ? USR2_TXFE : 0;
/*
* TCEN and TXDC are both bit 3
+ * RDR and DREN are both bit 0
*/
- mask |= s->ucr4 & UCR4_TCEN;
+ mask |= s->ucr4 & (UCR4_TCEN | UCR4_DREN);
usr2 = s->usr2 & mask;
diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c
index 7724c93..ef15d3e 100644
--- a/hw/dma/pl080.c
+++ b/hw/dma/pl080.c
@@ -11,8 +11,9 @@
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
#include "qemu/log.h"
+#include "hw/dma/pl080.h"
+#include "qapi/error.h"
-#define PL080_MAX_CHANNELS 8
#define PL080_CONF_E 0x1
#define PL080_CONF_M1 0x2
#define PL080_CONF_M2 0x4
@@ -30,36 +31,6 @@
#define PL080_CCTRL_D 0x02000000
#define PL080_CCTRL_S 0x01000000
-typedef struct {
- uint32_t src;
- uint32_t dest;
- uint32_t lli;
- uint32_t ctrl;
- uint32_t conf;
-} pl080_channel;
-
-#define TYPE_PL080 "pl080"
-#define PL080(obj) OBJECT_CHECK(PL080State, (obj), TYPE_PL080)
-
-typedef struct PL080State {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
- uint8_t tc_int;
- uint8_t tc_mask;
- uint8_t err_int;
- uint8_t err_mask;
- uint32_t conf;
- uint32_t sync;
- uint32_t req_single;
- uint32_t req_burst;
- pl080_channel chan[PL080_MAX_CHANNELS];
- int nchannels;
- /* Flag to avoid recursive DMA invocations. */
- int running;
- qemu_irq irq;
-} PL080State;
-
static const VMStateDescription vmstate_pl080_channel = {
.name = "pl080_channel",
.version_id = 1,
@@ -105,11 +76,12 @@ static const unsigned char pl081_id[] =
static void pl080_update(PL080State *s)
{
- if ((s->tc_int & s->tc_mask)
- || (s->err_int & s->err_mask))
- qemu_irq_raise(s->irq);
- else
- qemu_irq_lower(s->irq);
+ bool tclevel = (s->tc_int & s->tc_mask);
+ bool errlevel = (s->err_int & s->err_mask);
+
+ qemu_set_irq(s->interr, errlevel);
+ qemu_set_irq(s->inttc, tclevel);
+ qemu_set_irq(s->irq, errlevel || tclevel);
}
static void pl080_run(PL080State *s)
@@ -138,7 +110,6 @@ static void pl080_run(PL080State *s)
if ((s->conf & PL080_CONF_E) == 0)
return;
-hw_error("DMA active\n");
/* If we are already in the middle of a DMA operation then indicate that
there may be new DMA requests and return immediately. */
if (s->running) {
@@ -190,14 +161,16 @@ again:
swidth = 1 << ((ch->ctrl >> 18) & 7);
dwidth = 1 << ((ch->ctrl >> 21) & 7);
for (n = 0; n < dwidth; n+= swidth) {
- cpu_physical_memory_read(ch->src, buff + n, swidth);
+ address_space_read(&s->downstream_as, ch->src,
+ MEMTXATTRS_UNSPECIFIED, buff + n, swidth);
if (ch->ctrl & PL080_CCTRL_SI)
ch->src += swidth;
}
xsize = (dwidth < swidth) ? swidth : dwidth;
/* ??? This may pad the value incorrectly for dwidth < 32. */
for (n = 0; n < xsize; n += dwidth) {
- cpu_physical_memory_write(ch->dest + n, buff + n, dwidth);
+ address_space_write(&s->downstream_as, ch->dest + n,
+ MEMTXATTRS_UNSPECIFIED, buff + n, dwidth);
if (ch->ctrl & PL080_CCTRL_DI)
ch->dest += swidth;
}
@@ -207,19 +180,19 @@ again:
if (size == 0) {
/* Transfer complete. */
if (ch->lli) {
- ch->src = address_space_ldl_le(&address_space_memory,
+ ch->src = address_space_ldl_le(&s->downstream_as,
ch->lli,
MEMTXATTRS_UNSPECIFIED,
NULL);
- ch->dest = address_space_ldl_le(&address_space_memory,
+ ch->dest = address_space_ldl_le(&s->downstream_as,
ch->lli + 4,
MEMTXATTRS_UNSPECIFIED,
NULL);
- ch->ctrl = address_space_ldl_le(&address_space_memory,
+ ch->ctrl = address_space_ldl_le(&s->downstream_as,
ch->lli + 12,
MEMTXATTRS_UNSPECIFIED,
NULL);
- ch->lli = address_space_ldl_le(&address_space_memory,
+ ch->lli = address_space_ldl_le(&s->downstream_as,
ch->lli + 8,
MEMTXATTRS_UNSPECIFIED,
NULL);
@@ -255,7 +228,7 @@ static uint64_t pl080_read(void *opaque, hwaddr offset,
i = (offset & 0xe0) >> 5;
if (i >= s->nchannels)
goto bad_offset;
- switch (offset >> 2) {
+ switch ((offset >> 2) & 7) {
case 0: /* SrcAddr */
return s->chan[i].src;
case 1: /* DestAddr */
@@ -316,7 +289,7 @@ static void pl080_write(void *opaque, hwaddr offset,
i = (offset & 0xe0) >> 5;
if (i >= s->nchannels)
goto bad_offset;
- switch (offset >> 2) {
+ switch ((offset >> 2) & 7) {
case 0: /* SrcAddr */
s->chan[i].src = value;
break;
@@ -334,6 +307,7 @@ static void pl080_write(void *opaque, hwaddr offset,
pl080_run(s);
break;
}
+ return;
}
switch (offset >> 2) {
case 2: /* IntTCClear */
@@ -374,6 +348,30 @@ static const MemoryRegionOps pl080_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
+static void pl080_reset(DeviceState *dev)
+{
+ PL080State *s = PL080(dev);
+ int i;
+
+ s->tc_int = 0;
+ s->tc_mask = 0;
+ s->err_int = 0;
+ s->err_mask = 0;
+ s->conf = 0;
+ s->sync = 0;
+ s->req_single = 0;
+ s->req_burst = 0;
+ s->running = 0;
+
+ for (i = 0; i < s->nchannels; i++) {
+ s->chan[i].src = 0;
+ s->chan[i].dest = 0;
+ s->chan[i].lli = 0;
+ s->chan[i].ctrl = 0;
+ s->chan[i].conf = 0;
+ }
+}
+
static void pl080_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
@@ -382,9 +380,23 @@ static void pl080_init(Object *obj)
memory_region_init_io(&s->iomem, OBJECT(s), &pl080_ops, s, "pl080", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
+ sysbus_init_irq(sbd, &s->interr);
+ sysbus_init_irq(sbd, &s->inttc);
s->nchannels = 8;
}
+static void pl080_realize(DeviceState *dev, Error **errp)
+{
+ PL080State *s = PL080(dev);
+
+ if (!s->downstream) {
+ error_setg(errp, "PL080 'downstream' link not set");
+ return;
+ }
+
+ address_space_init(&s->downstream_as, s->downstream, "pl080-downstream");
+}
+
static void pl081_init(Object *obj)
{
PL080State *s = PL080(obj);
@@ -392,11 +404,20 @@ static void pl081_init(Object *obj)
s->nchannels = 2;
}
+static Property pl080_properties[] = {
+ DEFINE_PROP_LINK("downstream", PL080State, downstream,
+ TYPE_MEMORY_REGION, MemoryRegion *),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void pl080_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->vmsd = &vmstate_pl080;
+ dc->realize = pl080_realize;
+ dc->props = pl080_properties;
+ dc->reset = pl080_reset;
}
static const TypeInfo pl080_info = {
@@ -408,7 +429,7 @@ static const TypeInfo pl080_info = {
};
static const TypeInfo pl081_info = {
- .name = "pl081",
+ .name = TYPE_PL081,
.parent = TYPE_PL080,
.instance_init = pl081_init,
};
diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c
index 271ebe4..01573ab 100644
--- a/hw/intc/arm_gicv3_its_kvm.c
+++ b/hw/intc/arm_gicv3_its_kvm.c
@@ -211,7 +211,7 @@ static void kvm_arm_its_reset(DeviceState *dev)
return;
}
- error_report("ITS KVM: full reset is not supported by the host kernel");
+ warn_report("ITS KVM: full reset is not supported by the host kernel");
if (!kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CTLR)) {
diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index 351b69a..0d816fd 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -774,6 +774,24 @@ static void set_irq_level(void *opaque, int n, int level)
}
}
+/* callback when external NMI line is changed */
+static void nvic_nmi_trigger(void *opaque, int n, int level)
+{
+ NVICState *s = opaque;
+
+ trace_nvic_set_nmi_level(level);
+
+ /*
+ * The architecture doesn't specify whether NMI should share
+ * the normal-interrupt behaviour of being resampled on
+ * exception handler return. We choose not to, so just
+ * set NMI pending here and don't track the current level.
+ */
+ if (level) {
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI, false);
+ }
+}
+
static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs)
{
ARMCPU *cpu = s->cpu;
@@ -2382,6 +2400,7 @@ static void armv7m_nvic_instance_init(Object *obj)
qdev_init_gpio_out_named(dev, &nvic->sysresetreq, "SYSRESETREQ", 1);
qdev_init_gpio_in_named(dev, nvic_systick_trigger, "systick-trigger",
M_REG_NUM_BANKS);
+ qdev_init_gpio_in_named(dev, nvic_nmi_trigger, "NMI", 1);
}
static void armv7m_nvic_class_init(ObjectClass *klass, void *data)
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 81c7c39..7769869 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -192,6 +192,7 @@ nvic_acknowledge_irq(int irq, int prio) "NVIC acknowledge IRQ: %d now active (pr
nvic_get_pending_irq_info(int irq, bool secure) "NVIC next IRQ %d: targets_secure: %d"
nvic_complete_irq(int irq, bool secure) "NVIC complete IRQ %d (secure %d)"
nvic_set_irq_level(int irq, int level) "NVIC external irq %d level set to %d"
+nvic_set_nmi_level(int level) "NVIC external NMI level set to %d"
nvic_sysreg_read(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
nvic_sysreg_write(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 51d27b3..22714b0 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -71,5 +71,4 @@ obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
obj-$(CONFIG_AUX) += auxbus.o
obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o
-obj-y += mmio_interface.o
obj-$(CONFIG_MSF2) += msf2-sysreg.o
diff --git a/hw/misc/mmio_interface.c b/hw/misc/mmio_interface.c
deleted file mode 100644
index 3b0e203..0000000
--- a/hw/misc/mmio_interface.c
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * mmio_interface.c
- *
- * Copyright (C) 2017 : GreenSocs
- * http://www.greensocs.com/ , email: info@greensocs.com
- *
- * Developed by :
- * Frederic Konrad <fred.konrad@greensocs.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option)any later version.
- *
- * This program 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 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/>.
- *
- */
-
-#include "qemu/osdep.h"
-#include "qemu/log.h"
-#include "trace.h"
-#include "hw/qdev-properties.h"
-#include "hw/misc/mmio_interface.h"
-#include "qapi/error.h"
-
-#ifndef DEBUG_MMIO_INTERFACE
-#define DEBUG_MMIO_INTERFACE 0
-#endif
-
-static uint64_t mmio_interface_counter;
-
-#define DPRINTF(fmt, ...) do { \
- if (DEBUG_MMIO_INTERFACE) { \
- qemu_log("mmio_interface: 0x%" PRIX64 ": " fmt, s->id, ## __VA_ARGS__);\
- } \
-} while (0)
-
-static void mmio_interface_init(Object *obj)
-{
- MMIOInterface *s = MMIO_INTERFACE(obj);
-
- if (DEBUG_MMIO_INTERFACE) {
- s->id = mmio_interface_counter++;
- }
-
- DPRINTF("interface created\n");
- s->host_ptr = 0;
- s->subregion = 0;
-}
-
-static void mmio_interface_realize(DeviceState *dev, Error **errp)
-{
- MMIOInterface *s = MMIO_INTERFACE(dev);
-
- DPRINTF("realize from 0x%" PRIX64 " to 0x%" PRIX64 " map host pointer"
- " %p\n", s->start, s->end, s->host_ptr);
-
- if (!s->host_ptr) {
- error_setg(errp, "host_ptr property must be set");
- return;
- }
-
- if (!s->subregion) {
- error_setg(errp, "subregion property must be set");
- return;
- }
-
- memory_region_init_ram_ptr(&s->ram_mem, OBJECT(s), "ram",
- s->end - s->start + 1, s->host_ptr);
- memory_region_set_readonly(&s->ram_mem, s->ro);
- memory_region_add_subregion(s->subregion, s->start, &s->ram_mem);
-}
-
-static void mmio_interface_unrealize(DeviceState *dev, Error **errp)
-{
- MMIOInterface *s = MMIO_INTERFACE(dev);
-
- DPRINTF("unrealize from 0x%" PRIX64 " to 0x%" PRIX64 " map host pointer"
- " %p\n", s->start, s->end, s->host_ptr);
- memory_region_del_subregion(s->subregion, &s->ram_mem);
-}
-
-static void mmio_interface_finalize(Object *obj)
-{
- MMIOInterface *s = MMIO_INTERFACE(obj);
-
- DPRINTF("finalize from 0x%" PRIX64 " to 0x%" PRIX64 " map host pointer"
- " %p\n", s->start, s->end, s->host_ptr);
- object_unparent(OBJECT(&s->ram_mem));
-}
-
-static Property mmio_interface_properties[] = {
- DEFINE_PROP_UINT64("start", MMIOInterface, start, 0),
- DEFINE_PROP_UINT64("end", MMIOInterface, end, 0),
- DEFINE_PROP_PTR("host_ptr", MMIOInterface, host_ptr),
- DEFINE_PROP_BOOL("ro", MMIOInterface, ro, false),
- DEFINE_PROP_MEMORY_REGION("subregion", MMIOInterface, subregion),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void mmio_interface_class_init(ObjectClass *oc, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(oc);
-
- dc->realize = mmio_interface_realize;
- dc->unrealize = mmio_interface_unrealize;
- dc->props = mmio_interface_properties;
- /* Reason: pointer property "host_ptr", and this device
- * is an implementation detail of the memory subsystem,
- * not intended to be created directly by the user.
- */
- dc->user_creatable = false;
-}
-
-static const TypeInfo mmio_interface_info = {
- .name = TYPE_MMIO_INTERFACE,
- .parent = TYPE_DEVICE,
- .instance_size = sizeof(MMIOInterface),
- .instance_init = mmio_interface_init,
- .instance_finalize = mmio_interface_finalize,
- .class_init = mmio_interface_class_init,
-};
-
-static void mmio_interface_register_types(void)
-{
- type_register_static(&mmio_interface_info);
-}
-
-type_init(mmio_interface_register_types)
diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h
index 756ef3f..19665fd 100644
--- a/hw/sd/sdhci-internal.h
+++ b/hw/sd/sdhci-internal.h
@@ -302,4 +302,6 @@ extern const VMStateDescription sdhci_vmstate;
#define ESDHC_CTRL_4BITBUS (0x1 << 1)
#define ESDHC_CTRL_8BITBUS (0x2 << 1)
+#define ESDHC_PRNSTS_SDSTB (1 << 3)
+
#endif
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index 8f58c31..81bbf03 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -1651,6 +1651,14 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
break;
+ case SDHC_PRNSTS:
+ /* Add SDSTB (SD Clock Stable) bit to PRNSTS */
+ ret = sdhci_read(opaque, offset, size) & ~ESDHC_PRNSTS_SDSTB;
+ if (s->clkcon & SDHC_CLOCK_INT_STABLE) {
+ ret |= ESDHC_PRNSTS_SDSTB;
+ }
+ break;
+
case ESDHC_DLL_CTRL:
case ESDHC_TUNE_CTRL_STATUS:
case ESDHC_UNDOCUMENTED_REG27:
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index c052bfc..16f88f7 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -1031,14 +1031,6 @@ static const MemoryRegionOps spips_ops = {
static void xilinx_qspips_invalidate_mmio_ptr(XilinxQSPIPS *q)
{
- XilinxSPIPS *s = &q->parent_obj;
-
- if ((q->mmio_execution_enabled) && (q->lqspi_cached_addr != ~0ULL)) {
- /* Invalidate the current mapped mmio */
- memory_region_invalidate_mmio_ptr(&s->mmlqspi, q->lqspi_cached_addr,
- LQSPI_CACHE_SIZE);
- }
-
q->lqspi_cached_addr = ~0ULL;
}
@@ -1207,23 +1199,6 @@ static void lqspi_load_cache(void *opaque, hwaddr addr)
}
}
-static void *lqspi_request_mmio_ptr(void *opaque, hwaddr addr, unsigned *size,
- unsigned *offset)
-{
- XilinxQSPIPS *q = opaque;
- hwaddr offset_within_the_region;
-
- if (!q->mmio_execution_enabled) {
- return NULL;
- }
-
- offset_within_the_region = addr & ~(LQSPI_CACHE_SIZE - 1);
- lqspi_load_cache(opaque, offset_within_the_region);
- *size = LQSPI_CACHE_SIZE;
- *offset = offset_within_the_region;
- return q->lqspi_buf;
-}
-
static uint64_t
lqspi_read(void *opaque, hwaddr addr, unsigned int size)
{
@@ -1245,7 +1220,6 @@ lqspi_read(void *opaque, hwaddr addr, unsigned int size)
static const MemoryRegionOps lqspi_ops = {
.read = lqspi_read,
- .request_ptr = lqspi_request_mmio_ptr,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 1,
@@ -1322,15 +1296,6 @@ static void xilinx_qspips_realize(DeviceState *dev, Error **errp)
sysbus_init_mmio(sbd, &s->mmlqspi);
q->lqspi_cached_addr = ~0ULL;
-
- /* mmio_execution breaks migration better aborting than having strange
- * bugs.
- */
- if (q->mmio_execution_enabled) {
- error_setg(&q->migration_blocker,
- "enabling mmio_execution breaks migration");
- migrate_add_blocker(q->migration_blocker, &error_fatal);
- }
}
static void xlnx_zynqmp_qspips_realize(DeviceState *dev, Error **errp)
@@ -1427,16 +1392,6 @@ static Property xilinx_zynqmp_qspips_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
-static Property xilinx_qspips_properties[] = {
- /* We had to turn this off for 2.10 as it is not compatible with migration.
- * It can be enabled but will prevent the device to be migrated.
- * This will go aways when a fix will be released.
- */
- DEFINE_PROP_BOOL("x-mmio-exec", XilinxQSPIPS, mmio_execution_enabled,
- false),
- DEFINE_PROP_END_OF_LIST(),
-};
-
static Property xilinx_spips_properties[] = {
DEFINE_PROP_UINT8("num-busses", XilinxSPIPS, num_busses, 1),
DEFINE_PROP_UINT8("num-ss-bits", XilinxSPIPS, num_cs, 4),
@@ -1450,7 +1405,6 @@ static void xilinx_qspips_class_init(ObjectClass *klass, void * data)
XilinxSPIPSClass *xsc = XILINX_SPIPS_CLASS(klass);
dc->realize = xilinx_qspips_realize;
- dc->props = xilinx_qspips_properties;
xsc->reg_ops = &qspips_ops;
xsc->rx_fifo_size = RXFF_A_Q;
xsc->tx_fifo_size = TXFF_A_Q;
diff --git a/hw/timer/m48t59.c b/hw/timer/m48t59.c
index f299176..ca3ed44 100644
--- a/hw/timer/m48t59.c
+++ b/hw/timer/m48t59.c
@@ -493,66 +493,29 @@ static uint64_t NVRAM_readb(void *opaque, hwaddr addr, unsigned size)
return retval;
}
-static void nvram_writeb (void *opaque, hwaddr addr, uint32_t value)
-{
- M48t59State *NVRAM = opaque;
-
- m48t59_write(NVRAM, addr, value & 0xff);
-}
-
-static void nvram_writew (void *opaque, hwaddr addr, uint32_t value)
-{
- M48t59State *NVRAM = opaque;
-
- m48t59_write(NVRAM, addr, (value >> 8) & 0xff);
- m48t59_write(NVRAM, addr + 1, value & 0xff);
-}
-
-static void nvram_writel (void *opaque, hwaddr addr, uint32_t value)
-{
- M48t59State *NVRAM = opaque;
-
- m48t59_write(NVRAM, addr, (value >> 24) & 0xff);
- m48t59_write(NVRAM, addr + 1, (value >> 16) & 0xff);
- m48t59_write(NVRAM, addr + 2, (value >> 8) & 0xff);
- m48t59_write(NVRAM, addr + 3, value & 0xff);
-}
-
-static uint32_t nvram_readb (void *opaque, hwaddr addr)
+static uint64_t nvram_read(void *opaque, hwaddr addr, unsigned size)
{
M48t59State *NVRAM = opaque;
return m48t59_read(NVRAM, addr);
}
-static uint32_t nvram_readw (void *opaque, hwaddr addr)
-{
- M48t59State *NVRAM = opaque;
- uint32_t retval;
-
- retval = m48t59_read(NVRAM, addr) << 8;
- retval |= m48t59_read(NVRAM, addr + 1);
- return retval;
-}
-
-static uint32_t nvram_readl (void *opaque, hwaddr addr)
+static void nvram_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
{
M48t59State *NVRAM = opaque;
- uint32_t retval;
- retval = m48t59_read(NVRAM, addr) << 24;
- retval |= m48t59_read(NVRAM, addr + 1) << 16;
- retval |= m48t59_read(NVRAM, addr + 2) << 8;
- retval |= m48t59_read(NVRAM, addr + 3);
- return retval;
+ return m48t59_write(NVRAM, addr, value);
}
static const MemoryRegionOps nvram_ops = {
- .old_mmio = {
- .read = { nvram_readb, nvram_readw, nvram_readl, },
- .write = { nvram_writeb, nvram_writew, nvram_writel, },
- },
- .endianness = DEVICE_NATIVE_ENDIAN,
+ .read = nvram_read,
+ .write = nvram_write,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 1,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_BIG_ENDIAN,
};
static const VMStateDescription vmstate_m48t59 = {
diff --git a/hw/watchdog/Makefile.objs b/hw/watchdog/Makefile.objs
index 9589bed..3f536d1 100644
--- a/hw/watchdog/Makefile.objs
+++ b/hw/watchdog/Makefile.objs
@@ -1,4 +1,5 @@
common-obj-y += watchdog.o
+common-obj-$(CONFIG_CMSDK_APB_WATCHDOG) += cmsdk-apb-watchdog.o
common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o
common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o
common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o
diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c
new file mode 100644
index 0000000..eb79a73
--- /dev/null
+++ b/hw/watchdog/cmsdk-apb-watchdog.c
@@ -0,0 +1,326 @@
+/*
+ * ARM CMSDK APB watchdog emulation
+ *
+ * Copyright (c) 2018 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+/*
+ * This is a model of the "APB watchdog" which is part of the Cortex-M
+ * System Design Kit (CMSDK) and documented in the Cortex-M System
+ * Design Kit Technical Reference Manual (ARM DDI0479C):
+ * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "qapi/error.h"
+#include "qemu/main-loop.h"
+#include "sysemu/watchdog.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/watchdog/cmsdk-apb-watchdog.h"
+
+REG32(WDOGLOAD, 0x0)
+REG32(WDOGVALUE, 0x4)
+REG32(WDOGCONTROL, 0x8)
+ FIELD(WDOGCONTROL, INTEN, 0, 1)
+ FIELD(WDOGCONTROL, RESEN, 1, 1)
+#define R_WDOGCONTROL_VALID_MASK (R_WDOGCONTROL_INTEN_MASK | \
+ R_WDOGCONTROL_RESEN_MASK)
+REG32(WDOGINTCLR, 0xc)
+REG32(WDOGRIS, 0x10)
+ FIELD(WDOGRIS, INT, 0, 1)
+REG32(WDOGMIS, 0x14)
+REG32(WDOGLOCK, 0xc00)
+#define WDOG_UNLOCK_VALUE 0x1ACCE551
+REG32(WDOGITCR, 0xf00)
+ FIELD(WDOGITCR, ENABLE, 0, 1)
+#define R_WDOGITCR_VALID_MASK R_WDOGITCR_ENABLE_MASK
+REG32(WDOGITOP, 0xf04)
+ FIELD(WDOGITOP, WDOGRES, 0, 1)
+ FIELD(WDOGITOP, WDOGINT, 1, 1)
+#define R_WDOGITOP_VALID_MASK (R_WDOGITOP_WDOGRES_MASK | \
+ R_WDOGITOP_WDOGINT_MASK)
+REG32(PID4, 0xfd0)
+REG32(PID5, 0xfd4)
+REG32(PID6, 0xfd8)
+REG32(PID7, 0xfdc)
+REG32(PID0, 0xfe0)
+REG32(PID1, 0xfe4)
+REG32(PID2, 0xfe8)
+REG32(PID3, 0xfec)
+REG32(CID0, 0xff0)
+REG32(CID1, 0xff4)
+REG32(CID2, 0xff8)
+REG32(CID3, 0xffc)
+
+/* PID/CID values */
+static const int watchdog_id[] = {
+ 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
+ 0x24, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
+ 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
+};
+
+static bool cmsdk_apb_watchdog_intstatus(CMSDKAPBWatchdog *s)
+{
+ /* Return masked interrupt status */
+ return s->intstatus && (s->control & R_WDOGCONTROL_INTEN_MASK);
+}
+
+static bool cmsdk_apb_watchdog_resetstatus(CMSDKAPBWatchdog *s)
+{
+ /* Return masked reset status */
+ return s->resetstatus && (s->control & R_WDOGCONTROL_RESEN_MASK);
+}
+
+static void cmsdk_apb_watchdog_update(CMSDKAPBWatchdog *s)
+{
+ bool wdogint;
+ bool wdogres;
+
+ if (s->itcr) {
+ wdogint = s->itop & R_WDOGITOP_WDOGINT_MASK;
+ wdogres = s->itop & R_WDOGITOP_WDOGRES_MASK;
+ } else {
+ wdogint = cmsdk_apb_watchdog_intstatus(s);
+ wdogres = cmsdk_apb_watchdog_resetstatus(s);
+ }
+
+ qemu_set_irq(s->wdogint, wdogint);
+ if (wdogres) {
+ watchdog_perform_action();
+ }
+}
+
+static uint64_t cmsdk_apb_watchdog_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque);
+ uint64_t r;
+
+ switch (offset) {
+ case A_WDOGLOAD:
+ r = ptimer_get_limit(s->timer);
+ break;
+ case A_WDOGVALUE:
+ r = ptimer_get_count(s->timer);
+ break;
+ case A_WDOGCONTROL:
+ r = s->control;
+ break;
+ case A_WDOGRIS:
+ r = s->intstatus;
+ break;
+ case A_WDOGMIS:
+ r = cmsdk_apb_watchdog_intstatus(s);
+ break;
+ case A_WDOGLOCK:
+ r = s->lock;
+ break;
+ case A_WDOGITCR:
+ r = s->itcr;
+ break;
+ case A_PID4 ... A_CID3:
+ r = watchdog_id[(offset - A_PID4) / 4];
+ break;
+ case A_WDOGINTCLR:
+ case A_WDOGITOP:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CMSDK APB watchdog read: read of WO offset %x\n",
+ (int)offset);
+ r = 0;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CMSDK APB watchdog read: bad offset %x\n", (int)offset);
+ r = 0;
+ break;
+ }
+ trace_cmsdk_apb_watchdog_read(offset, r, size);
+ return r;
+}
+
+static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque);
+
+ trace_cmsdk_apb_watchdog_write(offset, value, size);
+
+ if (s->lock && offset != A_WDOGLOCK) {
+ /* Write access is disabled via WDOGLOCK */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CMSDK APB watchdog write: write to locked watchdog\n");
+ return;
+ }
+
+ switch (offset) {
+ case A_WDOGLOAD:
+ /*
+ * Reset the load value and the current count, and make sure
+ * we're counting.
+ */
+ ptimer_set_limit(s->timer, value, 1);
+ ptimer_run(s->timer, 0);
+ break;
+ case A_WDOGCONTROL:
+ s->control = value & R_WDOGCONTROL_VALID_MASK;
+ cmsdk_apb_watchdog_update(s);
+ break;
+ case A_WDOGINTCLR:
+ s->intstatus = 0;
+ ptimer_set_count(s->timer, ptimer_get_limit(s->timer));
+ cmsdk_apb_watchdog_update(s);
+ break;
+ case A_WDOGLOCK:
+ s->lock = (value != WDOG_UNLOCK_VALUE);
+ break;
+ case A_WDOGITCR:
+ s->itcr = value & R_WDOGITCR_VALID_MASK;
+ cmsdk_apb_watchdog_update(s);
+ break;
+ case A_WDOGITOP:
+ s->itop = value & R_WDOGITOP_VALID_MASK;
+ cmsdk_apb_watchdog_update(s);
+ break;
+ case A_WDOGVALUE:
+ case A_WDOGRIS:
+ case A_WDOGMIS:
+ case A_PID4 ... A_CID3:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CMSDK APB watchdog write: write to RO offset 0x%x\n",
+ (int)offset);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CMSDK APB watchdog write: bad offset 0x%x\n",
+ (int)offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps cmsdk_apb_watchdog_ops = {
+ .read = cmsdk_apb_watchdog_read,
+ .write = cmsdk_apb_watchdog_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ /* byte/halfword accesses are just zero-padded on reads and writes */
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+};
+
+static void cmsdk_apb_watchdog_tick(void *opaque)
+{
+ CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque);
+
+ if (!s->intstatus) {
+ /* Count expired for the first time: raise interrupt */
+ s->intstatus = R_WDOGRIS_INT_MASK;
+ } else {
+ /* Count expired for the second time: raise reset and stop clock */
+ s->resetstatus = 1;
+ ptimer_stop(s->timer);
+ }
+ cmsdk_apb_watchdog_update(s);
+}
+
+static void cmsdk_apb_watchdog_reset(DeviceState *dev)
+{
+ CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(dev);
+
+ trace_cmsdk_apb_watchdog_reset();
+ s->control = 0;
+ s->intstatus = 0;
+ s->lock = 0;
+ s->itcr = 0;
+ s->itop = 0;
+ s->resetstatus = 0;
+ /* Set the limit and the count */
+ ptimer_set_limit(s->timer, 0xffffffff, 1);
+ ptimer_run(s->timer, 0);
+}
+
+static void cmsdk_apb_watchdog_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(obj);
+
+ memory_region_init_io(&s->iomem, obj, &cmsdk_apb_watchdog_ops,
+ s, "cmsdk-apb-watchdog", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->wdogint);
+}
+
+static void cmsdk_apb_watchdog_realize(DeviceState *dev, Error **errp)
+{
+ CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(dev);
+ QEMUBH *bh;
+
+ if (s->wdogclk_frq == 0) {
+ error_setg(errp,
+ "CMSDK APB watchdog: wdogclk-frq property must be set");
+ return;
+ }
+
+ bh = qemu_bh_new(cmsdk_apb_watchdog_tick, s);
+ s->timer = ptimer_init(bh,
+ PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
+ PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT |
+ PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
+ PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
+
+ ptimer_set_freq(s->timer, s->wdogclk_frq);
+}
+
+static const VMStateDescription cmsdk_apb_watchdog_vmstate = {
+ .name = "cmsdk-apb-watchdog",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PTIMER(timer, CMSDKAPBWatchdog),
+ VMSTATE_UINT32(control, CMSDKAPBWatchdog),
+ VMSTATE_UINT32(intstatus, CMSDKAPBWatchdog),
+ VMSTATE_UINT32(lock, CMSDKAPBWatchdog),
+ VMSTATE_UINT32(itcr, CMSDKAPBWatchdog),
+ VMSTATE_UINT32(itop, CMSDKAPBWatchdog),
+ VMSTATE_UINT32(resetstatus, CMSDKAPBWatchdog),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property cmsdk_apb_watchdog_properties[] = {
+ DEFINE_PROP_UINT32("wdogclk-frq", CMSDKAPBWatchdog, wdogclk_frq, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void cmsdk_apb_watchdog_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = cmsdk_apb_watchdog_realize;
+ dc->vmsd = &cmsdk_apb_watchdog_vmstate;
+ dc->reset = cmsdk_apb_watchdog_reset;
+ dc->props = cmsdk_apb_watchdog_properties;
+}
+
+static const TypeInfo cmsdk_apb_watchdog_info = {
+ .name = TYPE_CMSDK_APB_WATCHDOG,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(CMSDKAPBWatchdog),
+ .instance_init = cmsdk_apb_watchdog_init,
+ .class_init = cmsdk_apb_watchdog_class_init,
+};
+
+static void cmsdk_apb_watchdog_register_types(void)
+{
+ type_register_static(&cmsdk_apb_watchdog_info);
+}
+
+type_init(cmsdk_apb_watchdog_register_types);
diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events
new file mode 100644
index 0000000..fee9584
--- /dev/null
+++ b/hw/watchdog/trace-events
@@ -0,0 +1,6 @@
+# See docs/devel/tracing.txt for syntax documentation.
+
+# hw/char/cmsdk_apb_watchdog.c
+cmsdk_apb_watchdog_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+cmsdk_apb_watchdog_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+cmsdk_apb_watchdog_reset(void) "CMSDK APB watchdog: reset"
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 448d41a..6863656 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -141,15 +141,6 @@ struct MemoryRegionOps {
uint64_t data,
unsigned size,
MemTxAttrs attrs);
- /* Instruction execution pre-callback:
- * @addr is the address of the access relative to the @mr.
- * @size is the size of the area returned by the callback.
- * @offset is the location of the pointer inside @mr.
- *
- * Returns a pointer to a location which contains guest code.
- */
- void *(*request_ptr)(void *opaque, hwaddr addr, unsigned *size,
- unsigned *offset);
enum device_endian endianness;
/* Guest-visible constraints: */
@@ -1668,32 +1659,6 @@ void mtree_info(fprintf_function mon_printf, void *f, bool flatview,
bool dispatch_tree, bool owner);
/**
- * memory_region_request_mmio_ptr: request a pointer to an mmio
- * MemoryRegion. If it is possible map a RAM MemoryRegion with this pointer.
- * When the device wants to invalidate the pointer it will call
- * memory_region_invalidate_mmio_ptr.
- *
- * @mr: #MemoryRegion to check
- * @addr: address within that region
- *
- * Returns true on success, false otherwise.
- */
-bool memory_region_request_mmio_ptr(MemoryRegion *mr, hwaddr addr);
-
-/**
- * memory_region_invalidate_mmio_ptr: invalidate the pointer to an mmio
- * previously requested.
- * In the end that means that if something wants to execute from this area it
- * will need to request the pointer again.
- *
- * @mr: #MemoryRegion associated to the pointer.
- * @offset: offset within the memory region
- * @size: size of that area.
- */
-void memory_region_invalidate_mmio_ptr(MemoryRegion *mr, hwaddr offset,
- unsigned size);
-
-/**
* memory_region_dispatch_read: perform a read directly to the specified
* MemoryRegion.
*
diff --git a/include/hw/char/imx_serial.h b/include/hw/char/imx_serial.h
index ee80da1..c8b7428 100644
--- a/include/hw/char/imx_serial.h
+++ b/include/hw/char/imx_serial.h
@@ -68,6 +68,7 @@
#define UCR2_RXEN (1<<1) /* Receiver enable */
#define UCR2_SRST (1<<0) /* Reset complete */
+#define UCR4_DREN BIT(0) /* Receive Data Ready interrupt enable */
#define UCR4_TCEN BIT(3) /* TX complete interrupt enable */
#define UTS1_TXEMPTY (1<<6)
diff --git a/include/hw/dma/pl080.h b/include/hw/dma/pl080.h
new file mode 100644
index 0000000..9d4b3df
--- /dev/null
+++ b/include/hw/dma/pl080.h
@@ -0,0 +1,71 @@
+/*
+ * ARM PrimeCell PL080/PL081 DMA controller
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2018 Linaro Limited
+ * Written by Paul Brook, Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+/* This is a model of the Arm PrimeCell PL080/PL081 DMA controller:
+ * The PL080 TRM is:
+ * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0196g/DDI0196.pdf
+ * and the PL081 TRM is:
+ * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0218e/DDI0218.pdf
+ *
+ * QEMU interface:
+ * + sysbus IRQ 0: DMACINTR combined interrupt line
+ * + sysbus IRQ 1: DMACINTERR error interrupt request
+ * + sysbus IRQ 2: DMACINTTC count interrupt request
+ * + sysbus MMIO region 0: MemoryRegion for the device's registers
+ * + QOM property "downstream": MemoryRegion defining where DMA
+ * bus master transactions are made
+ */
+
+#ifndef HW_DMA_PL080_H
+#define HW_DMA_PL080_H
+
+#include "hw/sysbus.h"
+
+#define PL080_MAX_CHANNELS 8
+
+typedef struct {
+ uint32_t src;
+ uint32_t dest;
+ uint32_t lli;
+ uint32_t ctrl;
+ uint32_t conf;
+} pl080_channel;
+
+#define TYPE_PL080 "pl080"
+#define TYPE_PL081 "pl081"
+#define PL080(obj) OBJECT_CHECK(PL080State, (obj), TYPE_PL080)
+
+typedef struct PL080State {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ uint8_t tc_int;
+ uint8_t tc_mask;
+ uint8_t err_int;
+ uint8_t err_mask;
+ uint32_t conf;
+ uint32_t sync;
+ uint32_t req_single;
+ uint32_t req_burst;
+ pl080_channel chan[PL080_MAX_CHANNELS];
+ int nchannels;
+ /* Flag to avoid recursive DMA invocations. */
+ int running;
+ qemu_irq irq;
+ qemu_irq interr;
+ qemu_irq inttc;
+
+ MemoryRegion *downstream;
+ AddressSpace downstream_as;
+} PL080State;
+
+#endif
diff --git a/include/hw/misc/mmio_interface.h b/include/hw/misc/mmio_interface.h
deleted file mode 100644
index 90d34fb..0000000
--- a/include/hw/misc/mmio_interface.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * mmio_interface.h
- *
- * Copyright (C) 2017 : GreenSocs
- * http://www.greensocs.com/ , email: info@greensocs.com
- *
- * Developed by :
- * Frederic Konrad <fred.konrad@greensocs.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option)any later version.
- *
- * This program 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 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/>.
- *
- */
-
-#ifndef MMIO_INTERFACE_H
-#define MMIO_INTERFACE_H
-
-#include "exec/memory.h"
-
-#define TYPE_MMIO_INTERFACE "mmio_interface"
-#define MMIO_INTERFACE(obj) OBJECT_CHECK(MMIOInterface, (obj), \
- TYPE_MMIO_INTERFACE)
-
-typedef struct MMIOInterface {
- DeviceState parent_obj;
-
- MemoryRegion *subregion;
- MemoryRegion ram_mem;
- uint64_t start;
- uint64_t end;
- bool ro;
- uint64_t id;
- void *host_ptr;
-} MMIOInterface;
-
-void mmio_interface_map(MMIOInterface *s);
-void mmio_interface_unmap(MMIOInterface *s);
-
-#endif /* MMIO_INTERFACE_H */
diff --git a/include/hw/watchdog/cmsdk-apb-watchdog.h b/include/hw/watchdog/cmsdk-apb-watchdog.h
new file mode 100644
index 0000000..ab8b598
--- /dev/null
+++ b/include/hw/watchdog/cmsdk-apb-watchdog.h
@@ -0,0 +1,59 @@
+/*
+ * ARM CMSDK APB watchdog emulation
+ *
+ * Copyright (c) 2018 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+/*
+ * This is a model of the "APB watchdog" which is part of the Cortex-M
+ * System Design Kit (CMSDK) and documented in the Cortex-M System
+ * Design Kit Technical Reference Manual (ARM DDI0479C):
+ * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit
+ *
+ * QEMU interface:
+ * + QOM property "wdogclk-frq": frequency at which the watchdog is clocked
+ * + sysbus MMIO region 0: the register bank
+ * + sysbus IRQ 0: watchdog interrupt
+ *
+ * In real hardware the watchdog's reset output is just a GPIO line
+ * which can then be masked by the board or treated as a simple interrupt.
+ * (For instance the IoTKit does this with the non-secure watchdog, so that
+ * secure code can control whether non-secure code can perform a system
+ * reset via its watchdog.) In QEMU, we just wire up the watchdog reset
+ * to watchdog_perform_action(), at least for the moment.
+ */
+
+#ifndef CMSDK_APB_WATCHDOG_H
+#define CMSDK_APB_WATCHDOG_H
+
+#include "hw/sysbus.h"
+#include "hw/ptimer.h"
+
+#define TYPE_CMSDK_APB_WATCHDOG "cmsdk-apb-watchdog"
+#define CMSDK_APB_WATCHDOG(obj) OBJECT_CHECK(CMSDKAPBWatchdog, (obj), \
+ TYPE_CMSDK_APB_WATCHDOG)
+
+typedef struct CMSDKAPBWatchdog {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ MemoryRegion iomem;
+ qemu_irq wdogint;
+ uint32_t wdogclk_frq;
+ struct ptimer_state *timer;
+
+ uint32_t control;
+ uint32_t intstatus;
+ uint32_t lock;
+ uint32_t itcr;
+ uint32_t itop;
+ uint32_t resetstatus;
+} CMSDKAPBWatchdog;
+
+#endif
diff --git a/memory.c b/memory.c
index 2ea16e7..8b44672 100644
--- a/memory.c
+++ b/memory.c
@@ -29,7 +29,6 @@
#include "exec/ram_addr.h"
#include "sysemu/kvm.h"
#include "sysemu/sysemu.h"
-#include "hw/misc/mmio_interface.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
@@ -2680,115 +2679,6 @@ void memory_listener_unregister(MemoryListener *listener)
listener->address_space = NULL;
}
-bool memory_region_request_mmio_ptr(MemoryRegion *mr, hwaddr addr)
-{
- void *host;
- unsigned size = 0;
- unsigned offset = 0;
- Object *new_interface;
-
- if (!mr || !mr->ops->request_ptr) {
- return false;
- }
-
- /*
- * Avoid an update if the request_ptr call
- * memory_region_invalidate_mmio_ptr which seems to be likely when we use
- * a cache.
- */
- memory_region_transaction_begin();
-
- host = mr->ops->request_ptr(mr->opaque, addr - mr->addr, &size, &offset);
-
- if (!host || !size) {
- memory_region_transaction_commit();
- return false;
- }
-
- new_interface = object_new("mmio_interface");
- qdev_prop_set_uint64(DEVICE(new_interface), "start", offset);
- qdev_prop_set_uint64(DEVICE(new_interface), "end", offset + size - 1);
- qdev_prop_set_bit(DEVICE(new_interface), "ro", true);
- qdev_prop_set_ptr(DEVICE(new_interface), "host_ptr", host);
- qdev_prop_set_ptr(DEVICE(new_interface), "subregion", mr);
- object_property_set_bool(OBJECT(new_interface), true, "realized", NULL);
-
- memory_region_transaction_commit();
- return true;
-}
-
-typedef struct MMIOPtrInvalidate {
- MemoryRegion *mr;
- hwaddr offset;
- unsigned size;
- int busy;
- int allocated;
-} MMIOPtrInvalidate;
-
-#define MAX_MMIO_INVALIDATE 10
-static MMIOPtrInvalidate mmio_ptr_invalidate_list[MAX_MMIO_INVALIDATE];
-
-static void memory_region_do_invalidate_mmio_ptr(CPUState *cpu,
- run_on_cpu_data data)
-{
- MMIOPtrInvalidate *invalidate_data = (MMIOPtrInvalidate *)data.host_ptr;
- MemoryRegion *mr = invalidate_data->mr;
- hwaddr offset = invalidate_data->offset;
- unsigned size = invalidate_data->size;
- MemoryRegionSection section = memory_region_find(mr, offset, size);
-
- qemu_mutex_lock_iothread();
-
- /* Reset dirty so this doesn't happen later. */
- cpu_physical_memory_test_and_clear_dirty(offset, size, 1);
-
- if (section.mr != mr) {
- /* memory_region_find add a ref on section.mr */
- memory_region_unref(section.mr);
- if (MMIO_INTERFACE(section.mr->owner)) {
- /* We found the interface just drop it. */
- object_property_set_bool(section.mr->owner, false, "realized",
- NULL);
- object_unref(section.mr->owner);
- object_unparent(section.mr->owner);
- }
- }
-
- qemu_mutex_unlock_iothread();
-
- if (invalidate_data->allocated) {
- g_free(invalidate_data);
- } else {
- invalidate_data->busy = 0;
- }
-}
-
-void memory_region_invalidate_mmio_ptr(MemoryRegion *mr, hwaddr offset,
- unsigned size)
-{
- size_t i;
- MMIOPtrInvalidate *invalidate_data = NULL;
-
- for (i = 0; i < MAX_MMIO_INVALIDATE; i++) {
- if (atomic_cmpxchg(&(mmio_ptr_invalidate_list[i].busy), 0, 1) == 0) {
- invalidate_data = &mmio_ptr_invalidate_list[i];
- break;
- }
- }
-
- if (!invalidate_data) {
- invalidate_data = g_malloc0(sizeof(MMIOPtrInvalidate));
- invalidate_data->allocated = 1;
- }
-
- invalidate_data->mr = mr;
- invalidate_data->offset = offset;
- invalidate_data->size = size;
-
- async_safe_run_on_cpu(first_cpu, memory_region_do_invalidate_mmio_ptr,
- RUN_ON_CPU_HOST_PTR(invalidate_data));
-}
-
void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
{
memory_region_ref(root);
diff --git a/target/arm/helper.c b/target/arm/helper.c
index a312408..c9bce1e 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -3750,7 +3750,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
/* Used to describe the behaviour of EL2 regs when EL2 does not exist. */
static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = {
- { .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64,
+ { .name = "VBAR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0,
.access = PL2_RW,
.readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore },
@@ -3759,6 +3759,10 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = {
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0,
.access = PL2_RW,
.readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore },
+ { .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH,
+ .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0,
+ .access = PL2_RW,
+ .type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "CPTR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 2,
.access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
@@ -3767,14 +3771,14 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = {
.access = PL2_RW, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "HMAIR1", .state = ARM_CP_STATE_AA32,
- .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1,
+ .cp = 15, .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1,
.access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "AMAIR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 0,
.access = PL2_RW, .type = ARM_CP_CONST,
.resetvalue = 0 },
- { .name = "HMAIR1", .state = ARM_CP_STATE_AA32,
- .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1,
+ { .name = "HAMAIR1", .state = ARM_CP_STATE_AA32,
+ .cp = 15, .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1,
.access = PL2_RW, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "AFSR0_EL2", .state = ARM_CP_STATE_BOTH,
@@ -3843,6 +3847,13 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = {
{ .name = "HSTR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 3,
.access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ { .name = "FAR_EL2", .state = ARM_CP_STATE_BOTH,
+ .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0,
+ .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ { .name = "HIFAR", .state = ARM_CP_STATE_AA32,
+ .type = ARM_CP_CONST,
+ .cp = 15, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 2,
+ .access = PL2_RW, .resetvalue = 0 },
REGINFO_SENTINEL
};
@@ -3888,18 +3899,23 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1,
.access = PL2_RW,
.fieldoffset = offsetof(CPUARMState, elr_el[2]) },
- { .name = "ESR_EL2", .state = ARM_CP_STATE_AA64,
+ { .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) },
- { .name = "FAR_EL2", .state = ARM_CP_STATE_AA64,
+ { .name = "FAR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) },
+ { .name = "HIFAR", .state = ARM_CP_STATE_AA32,
+ .type = ARM_CP_ALIAS,
+ .cp = 15, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 2,
+ .access = PL2_RW,
+ .fieldoffset = offsetofhigh32(CPUARMState, cp15.far_el[2]) },
{ .name = "SPSR_EL2", .state = ARM_CP_STATE_AA64,
.type = ARM_CP_ALIAS,
.opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0,
.access = PL2_RW,
.fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_HYP]) },
- { .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64,
+ { .name = "VBAR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0,
.access = PL2_RW, .writefn = vbar_write,
.fieldoffset = offsetof(CPUARMState, cp15.vbar_el[2]),
@@ -3917,7 +3933,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el[2]),
.resetvalue = 0 },
{ .name = "HMAIR1", .state = ARM_CP_STATE_AA32,
- .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1,
+ .cp = 15, .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1,
.access = PL2_RW, .type = ARM_CP_ALIAS,
.fieldoffset = offsetofhigh32(CPUARMState, cp15.mair_el[2]) },
{ .name = "AMAIR_EL2", .state = ARM_CP_STATE_BOTH,
@@ -3925,8 +3941,8 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.access = PL2_RW, .type = ARM_CP_CONST,
.resetvalue = 0 },
/* HAMAIR1 is mapped to AMAIR_EL2[63:32] */
- { .name = "HMAIR1", .state = ARM_CP_STATE_AA32,
- .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1,
+ { .name = "HAMAIR1", .state = ARM_CP_STATE_AA32,
+ .cp = 15, .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1,
.access = PL2_RW, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "AFSR0_EL2", .state = ARM_CP_STATE_BOTH,
diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c
index d550978..952b8d1 100644
--- a/target/arm/op_helper.c
+++ b/target/arm/op_helper.c
@@ -611,6 +611,14 @@ static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode,
*/
int curmode = env->uncached_cpsr & CPSR_M;
+ if (regno == 17) {
+ /* ELR_Hyp: a special case because access from tgtmode is OK */
+ if (curmode != ARM_CPU_MODE_HYP && curmode != ARM_CPU_MODE_MON) {
+ goto undef;
+ }
+ return;
+ }
+
if (curmode == tgtmode) {
goto undef;
}
@@ -638,17 +646,9 @@ static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode,
}
if (tgtmode == ARM_CPU_MODE_HYP) {
- switch (regno) {
- case 17: /* ELR_Hyp */
- if (curmode != ARM_CPU_MODE_HYP && curmode != ARM_CPU_MODE_MON) {
- goto undef;
- }
- break;
- default:
- if (curmode != ARM_CPU_MODE_MON) {
- goto undef;
- }
- break;
+ /* SPSR_Hyp, r13_hyp: accessible from Monitor mode only */
+ if (curmode != ARM_CPU_MODE_MON) {
+ goto undef;
}
}
diff --git a/target/arm/translate.c b/target/arm/translate.c
index f845da7..bcfc29c 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -4506,10 +4506,14 @@ static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn,
}
break;
case ARM_CPU_MODE_HYP:
- /* Note that we can forbid accesses from EL2 here because they
- * must be from Hyp mode itself
+ /*
+ * SPSR_hyp and r13_hyp can only be accessed from Monitor mode
+ * (and so we can forbid accesses from EL2 or below). elr_hyp
+ * can be accessed also from Hyp mode, so forbid accesses from
+ * EL0 or EL1.
*/
- if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 3) {
+ if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 2 ||
+ (s->current_el < 3 && *regno != 17)) {
goto undef;
}
break;
@@ -8480,6 +8484,22 @@ static void gen_srs(DisasContext *s,
s->base.is_jmp = DISAS_UPDATE;
}
+/* Generate a label used for skipping this instruction */
+static void arm_gen_condlabel(DisasContext *s)
+{
+ if (!s->condjmp) {
+ s->condlabel = gen_new_label();
+ s->condjmp = 1;
+ }
+}
+
+/* Skip this instruction if the ARM condition is false */
+static void arm_skip_unless(DisasContext *s, uint32_t cond)
+{
+ arm_gen_condlabel(s);
+ arm_gen_test_cc(cond ^ 1, s->condlabel);
+}
+
static void disas_arm_insn(DisasContext *s, unsigned int insn)
{
unsigned int cond, val, op1, i, shift, rm, rs, rn, rd, sh;
@@ -8709,9 +8729,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
if (cond != 0xe) {
/* if not always execute, we generate a conditional jump to
next instruction */
- s->condlabel = gen_new_label();
- arm_gen_test_cc(cond ^ 1, s->condlabel);
- s->condjmp = 1;
+ arm_skip_unless(s, cond);
}
if ((insn & 0x0f900000) == 0x03000000) {
if ((insn & (1 << 21)) == 0) {
@@ -8883,6 +8901,25 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
tcg_temp_free_i32(tmp2);
store_reg(s, rd, tmp);
break;
+ case 0x6: /* ERET */
+ if (op1 != 3) {
+ goto illegal_op;
+ }
+ if (!arm_dc_feature(s, ARM_FEATURE_V7VE)) {
+ goto illegal_op;
+ }
+ if ((insn & 0x000fff0f) != 0x0000000e) {
+ /* UNPREDICTABLE; we choose to UNDEF */
+ goto illegal_op;
+ }
+
+ if (s->current_el == 2) {
+ tmp = load_cpu_field(elr_el[2]);
+ } else {
+ tmp = load_reg(s, 14);
+ }
+ gen_exception_return(s, tmp);
+ break;
case 7:
{
int imm16 = extract32(insn, 0, 4) | (extract32(insn, 8, 12) << 4);
@@ -11140,8 +11177,16 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
if (rn != 14 || rd != 15) {
goto illegal_op;
}
- tmp = load_reg(s, rn);
- tcg_gen_subi_i32(tmp, tmp, insn & 0xff);
+ if (s->current_el == 2) {
+ /* ERET from Hyp uses ELR_Hyp, not LR */
+ if (insn & 0xff) {
+ goto illegal_op;
+ }
+ tmp = load_cpu_field(elr_el[2]);
+ } else {
+ tmp = load_reg(s, rn);
+ tcg_gen_subi_i32(tmp, tmp, insn & 0xff);
+ }
gen_exception_return(s, tmp);
break;
case 6: /* MRS */
@@ -11205,9 +11250,7 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
/* Conditional branch. */
op = (insn >> 22) & 0xf;
/* Generate a conditional jump to next instruction. */
- s->condlabel = gen_new_label();
- arm_gen_test_cc(op ^ 1, s->condlabel);
- s->condjmp = 1;
+ arm_skip_unless(s, op);
/* offset[11:1] = insn[10:0] */
offset = (insn & 0x7ff) << 1;
@@ -12131,8 +12174,7 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn)
case 1: case 3: case 9: case 11: /* czb */
rm = insn & 7;
tmp = load_reg(s, rm);
- s->condlabel = gen_new_label();
- s->condjmp = 1;
+ arm_gen_condlabel(s);
if (insn & (1 << 11))
tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, s->condlabel);
else
@@ -12295,9 +12337,7 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn)
break;
}
/* generate a conditional jump to next instruction */
- s->condlabel = gen_new_label();
- arm_gen_test_cc(cond ^ 1, s->condlabel);
- s->condjmp = 1;
+ arm_skip_unless(s, cond);
/* jump to the offset */
val = (uint32_t)s->pc + 2;
@@ -12676,9 +12716,7 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
uint32_t cond = dc->condexec_cond;
if (cond != 0x0e) { /* Skip conditional when condition is AL. */
- dc->condlabel = gen_new_label();
- arm_gen_test_cc(cond ^ 1, dc->condlabel);
- dc->condjmp = 1;
+ arm_skip_unless(dc, cond);
}
}