mirror of https://github.com/xemu-project/xemu.git
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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJbepg0AAoJEDwlJe0UNgzeV6wP/Rla7p/lEZxndkNN68+bgXFY PF7E+YVZjYok791PJC6sSGq/RfrbBjwhXLvnHdlbIxYbgjjc3TMHX57aey8fQ7b4 Ardo8eyIIbEIH6cvvl9n140Uuab2rFtZ62YSoLqWaFk2xEqcl7p54l+7AlRtw3j3 QJJxXrpnP5waGHbdQDjXf7XMUiJ7MDIszb2xirnteAy+MK+4zPBDdex3oO4seX4D DKvL42gpkj0sRuXKKF+GTxBE5AyTlS5/zdbTy4Iu8F1hOQsNYvzRyJqrvS6o1rCk IHTEOLBu9PRYrJwagDKs+AKArqcRoKHrgvzh+OMEJBD15bvRJLHNAV63poOmImHd kCU6ZIixJjMMfa74Uc0CuR7Bzlawtl4pIPCYETQ59JvUlxBJhhq6MxuJHj1stbV3 btapojLgfrVULriqYOPgSVP6iNkKNxT/dZxYyqQNKFmGi3EjERUVFqT9RxSptX+L hJs5lNzieJVlGfwcVf6MDyxDEw6nW+EqTYSV2zugmnfCxHeYGAoPJ8/0eG45kl+W 4Iz9KmJ35UXfUUgulpFMhNqZEK6H0MLf7LKSlbjZHWl3JlOohOZYjlYOgvB++94M hF04nTK3QHhVuIb7u2aVpSl08zZzCBUBduJO0mq4+atEEL1agF+F1DBBJPickmm6 zwUP05vgdzvAVaoyy6q5 =iGgV -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
62c34848ef
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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]);
|
||||
|
||||
|
|
|
@ -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_0_options(MachineClass *mc)
|
||||
static void virt_machine_3_1_options(MachineClass *mc)
|
||||
{
|
||||
}
|
||||
DEFINE_VIRT_MACHINE_AS_LATEST(3, 0)
|
||||
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(3, 0)
|
||||
|
||||
#define VIRT_COMPAT_2_12 \
|
||||
HW_COMPAT_2_12
|
||||
|
||||
static void virt_2_12_instance_init(Object *obj)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
113
hw/dma/pl080.c
113
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,
|
||||
};
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
static void nvram_write(void *opaque, hwaddr addr, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -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"
|
|
@ -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: */
|
||||
|
@ -1667,32 +1658,6 @@ void memory_global_dirty_log_stop(void);
|
|||
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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
110
memory.c
110
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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue