aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/acpi/piix4.c2
-rw-r--r--hw/arm/boot.c11
-rw-r--r--hw/arm/fsl-imx6ul.c4
-rw-r--r--hw/arm/fsl-imx7.c4
-rw-r--r--hw/arm/highbank.c6
-rw-r--r--hw/arm/iotkit.c114
-rw-r--r--hw/arm/mps2-tz.c142
-rw-r--r--hw/arm/mps2.c17
-rw-r--r--hw/arm/pxa2xx.c2
-rw-r--r--hw/arm/vexpress.c64
-rw-r--r--hw/audio/cs4231a.c1
-rw-r--r--hw/audio/es1370.c235
-rw-r--r--hw/audio/gusemu_hal.c1
-rw-r--r--hw/audio/sb16.c11
-rw-r--r--hw/cpu/a15mpcore.c31
-rw-r--r--hw/display/bcm2835_fb.c218
-rw-r--r--hw/display/cg3.c1
-rw-r--r--hw/display/cirrus_vga.c3
-rw-r--r--hw/i2c/pm_smbus.c254
-rw-r--r--hw/i2c/smbus.c37
-rw-r--r--hw/i2c/smbus_ich9.c26
-rw-r--r--hw/i386/pc.c16
-rw-r--r--hw/intc/apic.c42
-rw-r--r--hw/intc/arm_gic.c2
-rw-r--r--hw/ipmi/isa_ipmi_bt.c68
-rw-r--r--hw/isa/vt82c686.c2
-rw-r--r--hw/mem/pc-dimm.c61
-rw-r--r--hw/misc/Makefile.objs3
-rw-r--r--hw/misc/bcm2835_property.c123
-rw-r--r--hw/misc/iotkit-secctl.c73
-rw-r--r--hw/misc/iotkit-sysctl.c261
-rw-r--r--hw/misc/iotkit-sysinfo.c128
-rw-r--r--hw/misc/mps2-fpgaio.c146
-rw-r--r--hw/misc/trace-events16
-rw-r--r--hw/misc/tz-msc.c308
-rw-r--r--hw/misc/vmcoreinfo.c6
-rw-r--r--hw/ppc/prep.c3
-rw-r--r--hw/ppc/spapr.c30
-rw-r--r--hw/scsi/lsi53c895a.c4
-rw-r--r--hw/scsi/megasas.c2
-rw-r--r--hw/scsi/mptsas.c1
-rw-r--r--hw/scsi/vhost-scsi-common.c3
-rw-r--r--hw/scsi/vhost-scsi.c3
-rw-r--r--hw/scsi/vhost-user-scsi.c28
-rw-r--r--hw/ssi/pl022.c57
-rw-r--r--hw/timer/Makefile.objs1
-rw-r--r--hw/timer/cmsdk-apb-dualtimer.c515
-rw-r--r--hw/timer/mc146818rtc.c20
-rw-r--r--hw/timer/sh_timer.c1
-rw-r--r--hw/timer/trace-events5
-rw-r--r--hw/usb/dev-mtp.c93
-rw-r--r--hw/usb/hcd-ohci.c3
52 files changed, 2563 insertions, 645 deletions
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index 6404af5f33..e330f24c71 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -512,7 +512,7 @@ static void piix4_pm_realize(PCIDevice *dev, Error **errp)
pci_conf[0x90] = s->smb_io_base | 1;
pci_conf[0x91] = s->smb_io_base >> 8;
pci_conf[0xd2] = 0x09;
- pm_smbus_init(DEVICE(dev), &s->smb);
+ pm_smbus_init(DEVICE(dev), &s->smb, true);
memory_region_set_enabled(&s->smb.io, pci_conf[0xd2] & 1);
memory_region_add_subregion(pci_address_space_io(dev),
s->smb_io_base, &s->smb.io);
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index ca9467e583..20c71d7d96 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -736,6 +736,17 @@ static void do_cpu_reset(void *opaque)
}
}
+ if (!env->aarch64 && !info->secure_boot &&
+ arm_feature(env, ARM_FEATURE_EL2)) {
+ /*
+ * This is an AArch32 boot not to Secure state, and
+ * we have Hyp mode available, so boot the kernel into
+ * Hyp mode. This is not how the CPU comes out of reset,
+ * so we need to manually put it there.
+ */
+ cpsr_write(env, ARM_CPU_MODE_HYP, CPSR_M, CPSRWriteRaw);
+ }
+
if (cs == first_cpu) {
AddressSpace *as = arm_boot_address_space(cpu, info);
diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c
index 258f470623..4b56bfa8d1 100644
--- a/hw/arm/fsl-imx6ul.c
+++ b/hw/arm/fsl-imx6ul.c
@@ -207,6 +207,10 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp)
irq = qdev_get_gpio_in(d, ARM_CPU_IRQ);
sysbus_connect_irq(sbd, i, irq);
sysbus_connect_irq(sbd, i + smp_cpus, qdev_get_gpio_in(d, ARM_CPU_FIQ));
+ sysbus_connect_irq(sbd, i + 2 * smp_cpus,
+ qdev_get_gpio_in(d, ARM_CPU_VIRQ));
+ sysbus_connect_irq(sbd, i + 3 * smp_cpus,
+ qdev_get_gpio_in(d, ARM_CPU_VFIQ));
}
/*
diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c
index d5e26855a5..7663ad6861 100644
--- a/hw/arm/fsl-imx7.c
+++ b/hw/arm/fsl-imx7.c
@@ -209,6 +209,10 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(sbd, i, irq);
irq = qdev_get_gpio_in(d, ARM_CPU_FIQ);
sysbus_connect_irq(sbd, i + smp_cpus, irq);
+ irq = qdev_get_gpio_in(d, ARM_CPU_VIRQ);
+ sysbus_connect_irq(sbd, i + 2 * smp_cpus, irq);
+ irq = qdev_get_gpio_in(d, ARM_CPU_VFIQ);
+ sysbus_connect_irq(sbd, i + 3 * smp_cpus, irq);
}
/*
diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c
index 6d42fce2c3..fb9efa02c3 100644
--- a/hw/arm/highbank.c
+++ b/hw/arm/highbank.c
@@ -243,6 +243,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
int n;
qemu_irq cpu_irq[4];
qemu_irq cpu_fiq[4];
+ qemu_irq cpu_virq[4];
+ qemu_irq cpu_vfiq[4];
MemoryRegion *sysram;
MemoryRegion *dram;
MemoryRegion *sysmem;
@@ -282,6 +284,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
object_property_set_bool(cpuobj, true, "realized", &error_fatal);
cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ);
cpu_fiq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ);
+ cpu_virq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_VIRQ);
+ cpu_vfiq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_VFIQ);
}
sysmem = get_system_memory();
@@ -329,6 +333,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
for (n = 0; n < smp_cpus; n++) {
sysbus_connect_irq(busdev, n, cpu_irq[n]);
sysbus_connect_irq(busdev, n + smp_cpus, cpu_fiq[n]);
+ sysbus_connect_irq(busdev, n + 2 * smp_cpus, cpu_virq[n]);
+ sysbus_connect_irq(busdev, n + 3 * smp_cpus, cpu_vfiq[n]);
}
for (n = 0; n < 128; n++) {
diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c
index 8cadc8b160..8742200fb4 100644
--- a/hw/arm/iotkit.c
+++ b/hw/arm/iotkit.c
@@ -16,9 +16,11 @@
#include "hw/sysbus.h"
#include "hw/registerfields.h"
#include "hw/arm/iotkit.h"
-#include "hw/misc/unimp.h"
#include "hw/arm/arm.h"
+/* Clock frequency in HZ of the 32KHz "slow clock" */
+#define S32KCLK (32 * 1000)
+
/* Create an alias region of @size bytes starting at @base
* which mirrors the memory starting at @orig.
*/
@@ -138,8 +140,23 @@ static void iotkit_init(Object *obj)
TYPE_CMSDK_APB_TIMER);
sysbus_init_child_obj(obj, "timer1", &s->timer1, sizeof(s->timer1),
TYPE_CMSDK_APB_TIMER);
+ sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer),
+ TYPE_CMSDK_APB_TIMER);
sysbus_init_child_obj(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer),
- TYPE_UNIMPLEMENTED_DEVICE);
+ TYPE_CMSDK_APB_DUALTIMER);
+ sysbus_init_child_obj(obj, "s32kwatchdog", &s->s32kwatchdog,
+ sizeof(s->s32kwatchdog), TYPE_CMSDK_APB_WATCHDOG);
+ sysbus_init_child_obj(obj, "nswatchdog", &s->nswatchdog,
+ sizeof(s->nswatchdog), TYPE_CMSDK_APB_WATCHDOG);
+ sysbus_init_child_obj(obj, "swatchdog", &s->swatchdog,
+ sizeof(s->swatchdog), TYPE_CMSDK_APB_WATCHDOG);
+ sysbus_init_child_obj(obj, "iotkit-sysctl", &s->sysctl,
+ sizeof(s->sysctl), TYPE_IOTKIT_SYSCTL);
+ sysbus_init_child_obj(obj, "iotkit-sysinfo", &s->sysinfo,
+ sizeof(s->sysinfo), TYPE_IOTKIT_SYSINFO);
+ object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate,
+ sizeof(s->nmi_orgate), TYPE_OR_IRQ,
+ &error_abort, NULL);
object_initialize_child(obj, "ppc-irq-orgate", &s->ppc_irq_orgate,
sizeof(s->ppc_irq_orgate), TYPE_OR_IRQ,
&error_abort, NULL);
@@ -154,8 +171,6 @@ static void iotkit_init(Object *obj)
TYPE_SPLIT_IRQ, &error_abort, NULL);
g_free(name);
}
- sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer),
- TYPE_UNIMPLEMENTED_DEVICE);
}
static void iotkit_exp_irq(void *opaque, int n, int level)
@@ -390,13 +405,15 @@ static void iotkit_realize(DeviceState *dev, Error **errp)
return;
}
- qdev_prop_set_string(DEVICE(&s->dualtimer), "name", "Dual timer");
- qdev_prop_set_uint64(DEVICE(&s->dualtimer), "size", 0x1000);
+
+ qdev_prop_set_uint32(DEVICE(&s->dualtimer), "pclk-frq", s->mainclk_frq);
object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->dualtimer), 0,
+ qdev_get_gpio_in(DEVICE(&s->armv7m), 5));
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0);
object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err);
if (err) {
@@ -462,13 +479,14 @@ static void iotkit_realize(DeviceState *dev, Error **errp)
/* Devices behind APB PPC1:
* 0x4002f000: S32K timer
*/
- qdev_prop_set_string(DEVICE(&s->s32ktimer), "name", "S32KTIMER");
- qdev_prop_set_uint64(DEVICE(&s->s32ktimer), "size", 0x1000);
+ qdev_prop_set_uint32(DEVICE(&s->s32ktimer), "pclk-frq", S32KCLK);
object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32ktimer), 0,
+ qdev_get_gpio_in(DEVICE(&s->armv7m), 2));
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0);
object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err);
if (err) {
@@ -501,19 +519,66 @@ static void iotkit_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in_named(dev_apb_ppc1,
"cfg_sec_resp", 0));
- /* Using create_unimplemented_device() maps the stub into the
- * system address space rather than into our container, but the
- * overall effect to the guest is the same.
- */
- create_unimplemented_device("SYSINFO", 0x40020000, 0x1000);
+ object_property_set_bool(OBJECT(&s->sysinfo), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ /* System information registers */
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, 0x40020000);
+ /* System control registers */
+ object_property_set_bool(OBJECT(&s->sysctl), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctl), 0, 0x50021000);
- create_unimplemented_device("SYSCONTROL", 0x50021000, 0x1000);
- create_unimplemented_device("S32KWATCHDOG", 0x5002e000, 0x1000);
+ /* This OR gate wires together outputs from the secure watchdogs to NMI */
+ object_property_set_int(OBJECT(&s->nmi_orgate), 2, "num-lines", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ object_property_set_bool(OBJECT(&s->nmi_orgate), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ qdev_connect_gpio_out(DEVICE(&s->nmi_orgate), 0,
+ qdev_get_gpio_in_named(DEVICE(&s->armv7m), "NMI", 0));
+
+ qdev_prop_set_uint32(DEVICE(&s->s32kwatchdog), "wdogclk-frq", S32KCLK);
+ object_property_set_bool(OBJECT(&s->s32kwatchdog), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32kwatchdog), 0,
+ qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 0));
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, 0x5002e000);
/* 0x40080000 .. 0x4008ffff : IoTKit second Base peripheral region */
- create_unimplemented_device("NS watchdog", 0x40081000, 0x1000);
- create_unimplemented_device("S watchdog", 0x50081000, 0x1000);
+ qdev_prop_set_uint32(DEVICE(&s->nswatchdog), "wdogclk-frq", s->mainclk_frq);
+ object_property_set_bool(OBJECT(&s->nswatchdog), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->nswatchdog), 0,
+ qdev_get_gpio_in(DEVICE(&s->armv7m), 1));
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->nswatchdog), 0, 0x40081000);
+
+ qdev_prop_set_uint32(DEVICE(&s->swatchdog), "wdogclk-frq", s->mainclk_frq);
+ object_property_set_bool(OBJECT(&s->swatchdog), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->swatchdog), 0,
+ qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 1));
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->swatchdog), 0, 0x50081000);
for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) {
Object *splitter = OBJECT(&s->ppc_irq_splitter[i]);
@@ -602,6 +667,21 @@ static void iotkit_realize(DeviceState *dev, Error **errp)
iotkit_forward_sec_resp_cfg(s);
+ /* Forward the MSC related signals */
+ qdev_pass_gpios(dev_secctl, dev, "mscexp_status");
+ qdev_pass_gpios(dev_secctl, dev, "mscexp_clear");
+ qdev_pass_gpios(dev_secctl, dev, "mscexp_ns");
+ qdev_connect_gpio_out_named(dev_secctl, "msc_irq", 0,
+ qdev_get_gpio_in(DEVICE(&s->armv7m), 11));
+
+ /*
+ * Expose our container region to the board model; this corresponds
+ * to the AHB Slave Expansion ports which allow bus master devices
+ * (eg DMA controllers) in the board model to make transactions into
+ * devices in the IoTKit.
+ */
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container);
+
system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq;
}
diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c
index dc0f34abe5..6dd02ae47e 100644
--- a/hw/arm/mps2-tz.c
+++ b/hw/arm/mps2-tz.c
@@ -45,7 +45,10 @@
#include "hw/misc/mps2-scc.h"
#include "hw/misc/mps2-fpgaio.h"
#include "hw/misc/tz-mpc.h"
+#include "hw/misc/tz-msc.h"
#include "hw/arm/iotkit.h"
+#include "hw/dma/pl080.h"
+#include "hw/ssi/pl022.h"
#include "hw/devices.h"
#include "net/net.h"
#include "hw/core/split-irq.h"
@@ -71,12 +74,13 @@ typedef struct {
MPS2FPGAIO fpgaio;
TZPPC ppc[5];
TZMPC ssram_mpc[3];
- UnimplementedDeviceState spi[5];
+ PL022State spi[5];
UnimplementedDeviceState i2c[4];
UnimplementedDeviceState i2s_audio;
UnimplementedDeviceState gpio[4];
- UnimplementedDeviceState dma[4];
UnimplementedDeviceState gfx;
+ PL080State dma[4];
+ TZMSC msc[4];
CMSDKAPBUART uart[5];
SplitIRQ sec_resp_splitter;
qemu_or_irq uart_irq_orgate;
@@ -188,7 +192,7 @@ static MemoryRegion *make_scc(MPS2TZMachineState *mms, void *opaque,
sccdev = DEVICE(scc);
qdev_set_parent_bus(sccdev, sysbus_get_default());
qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2);
- qdev_prop_set_uint32(sccdev, "scc-aid", 0x02000008);
+ qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008);
qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id);
object_property_set_bool(OBJECT(scc), true, "realized", &error_fatal);
return sysbus_mmio_get_region(SYS_BUS_DEVICE(sccdev), 0);
@@ -263,6 +267,89 @@ static MemoryRegion *make_mpc(MPS2TZMachineState *mms, void *opaque,
return sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 0);
}
+static MemoryRegion *make_dma(MPS2TZMachineState *mms, void *opaque,
+ const char *name, hwaddr size)
+{
+ PL080State *dma = opaque;
+ int i = dma - &mms->dma[0];
+ SysBusDevice *s;
+ char *mscname = g_strdup_printf("%s-msc", name);
+ TZMSC *msc = &mms->msc[i];
+ DeviceState *iotkitdev = DEVICE(&mms->iotkit);
+ MemoryRegion *msc_upstream;
+ MemoryRegion *msc_downstream;
+
+ /*
+ * Each DMA device is a PL081 whose transaction master interface
+ * is guarded by a Master Security Controller. The downstream end of
+ * the MSC connects to the IoTKit AHB Slave Expansion port, so the
+ * DMA devices can see all devices and memory that the CPU does.
+ */
+ sysbus_init_child_obj(OBJECT(mms), mscname, msc, sizeof(*msc), TYPE_TZ_MSC);
+ msc_downstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(&mms->iotkit), 0);
+ object_property_set_link(OBJECT(msc), OBJECT(msc_downstream),
+ "downstream", &error_fatal);
+ object_property_set_link(OBJECT(msc), OBJECT(mms),
+ "idau", &error_fatal);
+ object_property_set_bool(OBJECT(msc), true, "realized", &error_fatal);
+
+ qdev_connect_gpio_out_named(DEVICE(msc), "irq", 0,
+ qdev_get_gpio_in_named(iotkitdev,
+ "mscexp_status", i));
+ qdev_connect_gpio_out_named(iotkitdev, "mscexp_clear", i,
+ qdev_get_gpio_in_named(DEVICE(msc),
+ "irq_clear", 0));
+ qdev_connect_gpio_out_named(iotkitdev, "mscexp_ns", i,
+ qdev_get_gpio_in_named(DEVICE(msc),
+ "cfg_nonsec", 0));
+ qdev_connect_gpio_out(DEVICE(&mms->sec_resp_splitter),
+ ARRAY_SIZE(mms->ppc) + i,
+ qdev_get_gpio_in_named(DEVICE(msc),
+ "cfg_sec_resp", 0));
+ msc_upstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(msc), 0);
+
+ sysbus_init_child_obj(OBJECT(mms), name, dma, sizeof(*dma), TYPE_PL081);
+ object_property_set_link(OBJECT(dma), OBJECT(msc_upstream),
+ "downstream", &error_fatal);
+ object_property_set_bool(OBJECT(dma), true, "realized", &error_fatal);
+
+ s = SYS_BUS_DEVICE(dma);
+ /* Wire up DMACINTR, DMACINTERR, DMACINTTC */
+ sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev,
+ "EXP_IRQ", 58 + i * 3));
+ sysbus_connect_irq(s, 1, qdev_get_gpio_in_named(iotkitdev,
+ "EXP_IRQ", 56 + i * 3));
+ sysbus_connect_irq(s, 2, qdev_get_gpio_in_named(iotkitdev,
+ "EXP_IRQ", 57 + i * 3));
+
+ return sysbus_mmio_get_region(s, 0);
+}
+
+static MemoryRegion *make_spi(MPS2TZMachineState *mms, void *opaque,
+ const char *name, hwaddr size)
+{
+ /*
+ * The AN505 has five PL022 SPI controllers.
+ * One of these should have the LCD controller behind it; the others
+ * are connected only to the FPGA's "general purpose SPI connector"
+ * or "shield" expansion connectors.
+ * Note that if we do implement devices behind SPI, the chip select
+ * lines are set via the "MISC" register in the MPS2 FPGAIO device.
+ */
+ PL022State *spi = opaque;
+ int i = spi - &mms->spi[0];
+ DeviceState *iotkitdev = DEVICE(&mms->iotkit);
+ SysBusDevice *s;
+
+ sysbus_init_child_obj(OBJECT(mms), name, spi, sizeof(mms->spi[0]),
+ TYPE_PL022);
+ object_property_set_bool(OBJECT(spi), true, "realized", &error_fatal);
+ s = SYS_BUS_DEVICE(spi);
+ sysbus_connect_irq(s, 0,
+ qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 51 + i));
+ return sysbus_mmio_get_region(s, 0);
+}
+
static void mps2tz_common_init(MachineState *machine)
{
MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine);
@@ -289,13 +376,14 @@ static void mps2tz_common_init(MachineState *machine)
&error_fatal);
/* The sec_resp_cfg output from the IoTKit must be split into multiple
- * lines, one for each of the PPCs we create here.
+ * lines, one for each of the PPCs we create here, plus one per MSC.
*/
object_initialize(&mms->sec_resp_splitter, sizeof(mms->sec_resp_splitter),
TYPE_SPLIT_IRQ);
object_property_add_child(OBJECT(machine), "sec-resp-splitter",
OBJECT(&mms->sec_resp_splitter), &error_abort);
- object_property_set_int(OBJECT(&mms->sec_resp_splitter), 5,
+ object_property_set_int(OBJECT(&mms->sec_resp_splitter),
+ ARRAY_SIZE(mms->ppc) + ARRAY_SIZE(mms->msc),
"num-lines", &error_fatal);
object_property_set_bool(OBJECT(&mms->sec_resp_splitter), true,
"realized", &error_fatal);
@@ -360,11 +448,11 @@ static void mps2tz_common_init(MachineState *machine)
}, {
.name = "apb_ppcexp1",
.ports = {
- { "spi0", make_unimp_dev, &mms->spi[0], 0x40205000, 0x1000 },
- { "spi1", make_unimp_dev, &mms->spi[1], 0x40206000, 0x1000 },
- { "spi2", make_unimp_dev, &mms->spi[2], 0x40209000, 0x1000 },
- { "spi3", make_unimp_dev, &mms->spi[3], 0x4020a000, 0x1000 },
- { "spi4", make_unimp_dev, &mms->spi[4], 0x4020b000, 0x1000 },
+ { "spi0", make_spi, &mms->spi[0], 0x40205000, 0x1000 },
+ { "spi1", make_spi, &mms->spi[1], 0x40206000, 0x1000 },
+ { "spi2", make_spi, &mms->spi[2], 0x40209000, 0x1000 },
+ { "spi3", make_spi, &mms->spi[3], 0x4020a000, 0x1000 },
+ { "spi4", make_spi, &mms->spi[4], 0x4020b000, 0x1000 },
{ "uart0", make_uart, &mms->uart[0], 0x40200000, 0x1000 },
{ "uart1", make_uart, &mms->uart[1], 0x40201000, 0x1000 },
{ "uart2", make_uart, &mms->uart[2], 0x40202000, 0x1000 },
@@ -396,10 +484,10 @@ static void mps2tz_common_init(MachineState *machine)
}, {
.name = "ahb_ppcexp1",
.ports = {
- { "dma0", make_unimp_dev, &mms->dma[0], 0x40110000, 0x1000 },
- { "dma1", make_unimp_dev, &mms->dma[1], 0x40111000, 0x1000 },
- { "dma2", make_unimp_dev, &mms->dma[2], 0x40112000, 0x1000 },
- { "dma3", make_unimp_dev, &mms->dma[3], 0x40113000, 0x1000 },
+ { "dma0", make_dma, &mms->dma[0], 0x40110000, 0x1000 },
+ { "dma1", make_dma, &mms->dma[1], 0x40111000, 0x1000 },
+ { "dma2", make_dma, &mms->dma[2], 0x40112000, 0x1000 },
+ { "dma3", make_dma, &mms->dma[3], 0x40113000, 0x1000 },
},
},
};
@@ -480,12 +568,32 @@ static void mps2tz_common_init(MachineState *machine)
armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0x400000);
}
+static void mps2_tz_idau_check(IDAUInterface *ii, uint32_t address,
+ int *iregion, bool *exempt, bool *ns, bool *nsc)
+{
+ /*
+ * The MPS2 TZ FPGA images have IDAUs in them which are connected to
+ * the Master Security Controllers. Thes have the same logic as
+ * is used by the IoTKit for the IDAU connected to the CPU, except
+ * that MSCs don't care about the NSC attribute.
+ */
+ int region = extract32(address, 28, 4);
+
+ *ns = !(region & 1);
+ *nsc = false;
+ /* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */
+ *exempt = (address & 0xeff00000) == 0xe0000000;
+ *iregion = region;
+}
+
static void mps2tz_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
+ IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(oc);
mc->init = mps2tz_common_init;
mc->max_cpus = 1;
+ iic->check = mps2_tz_idau_check;
}
static void mps2tz_an505_class_init(ObjectClass *oc, void *data)
@@ -496,7 +604,7 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data)
mc->desc = "ARM MPS2 with AN505 FPGA image for Cortex-M33";
mmc->fpga_type = FPGA_AN505;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33");
- mmc->scc_id = 0x41040000 | (505 << 4);
+ mmc->scc_id = 0x41045050;
}
static const TypeInfo mps2tz_info = {
@@ -506,6 +614,10 @@ static const TypeInfo mps2tz_info = {
.instance_size = sizeof(MPS2TZMachineState),
.class_size = sizeof(MPS2TZMachineClass),
.class_init = mps2tz_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_IDAU_INTERFACE },
+ { }
+ },
};
static const TypeInfo mps2tz_an505_info = {
diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c
index 0a0ae867d9..e3d698ba6c 100644
--- a/hw/arm/mps2.c
+++ b/hw/arm/mps2.c
@@ -34,6 +34,7 @@
#include "hw/misc/unimp.h"
#include "hw/char/cmsdk-apb-uart.h"
#include "hw/timer/cmsdk-apb-timer.h"
+#include "hw/timer/cmsdk-apb-dualtimer.h"
#include "hw/misc/mps2-scc.h"
#include "hw/devices.h"
#include "net/net.h"
@@ -64,6 +65,7 @@ typedef struct {
MemoryRegion blockram_m3;
MemoryRegion sram;
MPS2SCC scc;
+ CMSDKAPBDualTimer dualtimer;
} MPS2MachineState;
#define TYPE_MPS2_MACHINE "mps2"
@@ -297,11 +299,20 @@ static void mps2_common_init(MachineState *machine)
cmsdk_apb_timer_create(0x40000000, qdev_get_gpio_in(armv7m, 8), SYSCLK_FRQ);
cmsdk_apb_timer_create(0x40001000, qdev_get_gpio_in(armv7m, 9), SYSCLK_FRQ);
+ sysbus_init_child_obj(OBJECT(mms), "dualtimer", &mms->dualtimer,
+ sizeof(mms->dualtimer), TYPE_CMSDK_APB_DUALTIMER);
+ qdev_prop_set_uint32(DEVICE(&mms->dualtimer), "pclk-frq", SYSCLK_FRQ);
+ object_property_set_bool(OBJECT(&mms->dualtimer), true, "realized",
+ &error_fatal);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&mms->dualtimer), 0,
+ qdev_get_gpio_in(armv7m, 10));
+ sysbus_mmio_map(SYS_BUS_DEVICE(&mms->dualtimer), 0, 0x40002000);
+
object_initialize(&mms->scc, sizeof(mms->scc), TYPE_MPS2_SCC);
sccdev = DEVICE(&mms->scc);
qdev_set_parent_bus(sccdev, sysbus_get_default());
qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2);
- qdev_prop_set_uint32(sccdev, "scc-aid", 0x02000008);
+ qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008);
qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id);
object_property_set_bool(OBJECT(&mms->scc), true, "realized",
&error_fatal);
@@ -336,7 +347,7 @@ static void mps2_an385_class_init(ObjectClass *oc, void *data)
mc->desc = "ARM MPS2 with AN385 FPGA image for Cortex-M3";
mmc->fpga_type = FPGA_AN385;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3");
- mmc->scc_id = 0x41040000 | (385 << 4);
+ mmc->scc_id = 0x41043850;
}
static void mps2_an511_class_init(ObjectClass *oc, void *data)
@@ -347,7 +358,7 @@ static void mps2_an511_class_init(ObjectClass *oc, void *data)
mc->desc = "ARM MPS2 with AN511 DesignStart FPGA image for Cortex-M3";
mmc->fpga_type = FPGA_AN511;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3");
- mmc->scc_id = 0x4104000 | (511 << 4);
+ mmc->scc_id = 0x41045110;
}
static const TypeInfo mps2_info = {
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index b67b0cefb6..f598a1c053 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -409,7 +409,7 @@ static uint64_t pxa2xx_mm_read(void *opaque, hwaddr addr,
case MDCNFG ... SA1110:
if ((addr & 3) == 0)
return s->mm_regs[addr >> 2];
-
+ /* fall through */
default:
printf("%s: Bad register " REG_FMT "\n", __func__, addr);
break;
diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
index 5bfe2e4348..c02d18ee61 100644
--- a/hw/arm/vexpress.c
+++ b/hw/arm/vexpress.c
@@ -172,6 +172,7 @@ typedef struct {
typedef struct {
MachineState parent;
bool secure;
+ bool virt;
} VexpressMachineState;
#define TYPE_VEXPRESS_MACHINE "vexpress"
@@ -203,7 +204,7 @@ struct VEDBoardInfo {
};
static void init_cpus(const char *cpu_type, const char *privdev,
- hwaddr periphbase, qemu_irq *pic, bool secure)
+ hwaddr periphbase, qemu_irq *pic, bool secure, bool virt)
{
DeviceState *dev;
SysBusDevice *busdev;
@@ -216,6 +217,11 @@ static void init_cpus(const char *cpu_type, const char *privdev,
if (!secure) {
object_property_set_bool(cpuobj, false, "has_el3", NULL);
}
+ if (!virt) {
+ if (object_property_find(cpuobj, "has_el2", NULL)) {
+ object_property_set_bool(cpuobj, false, "has_el2", NULL);
+ }
+ }
if (object_property_find(cpuobj, "reset-cbar", NULL)) {
object_property_set_int(cpuobj, periphbase,
@@ -251,6 +257,10 @@ static void init_cpus(const char *cpu_type, const char *privdev,
sysbus_connect_irq(busdev, n, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
sysbus_connect_irq(busdev, n + smp_cpus,
qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
+ sysbus_connect_irq(busdev, n + 2 * smp_cpus,
+ qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
+ sysbus_connect_irq(busdev, n + 3 * smp_cpus,
+ qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
}
}
@@ -285,7 +295,8 @@ static void a9_daughterboard_init(const VexpressMachineState *vms,
memory_region_add_subregion(sysmem, 0x60000000, ram);
/* 0x1e000000 A9MPCore (SCU) private memory region */
- init_cpus(cpu_type, TYPE_A9MPCORE_PRIV, 0x1e000000, pic, vms->secure);
+ init_cpus(cpu_type, TYPE_A9MPCORE_PRIV, 0x1e000000, pic,
+ vms->secure, vms->virt);
/* Daughterboard peripherals : 0x10020000 .. 0x20000000 */
@@ -366,7 +377,8 @@ static void a15_daughterboard_init(const VexpressMachineState *vms,
memory_region_add_subregion(sysmem, 0x80000000, ram);
/* 0x2c000000 A15MPCore private memory region (GIC) */
- init_cpus(cpu_type, TYPE_A15MPCORE_PRIV, 0x2c000000, pic, vms->secure);
+ init_cpus(cpu_type, TYPE_A15MPCORE_PRIV, 0x2c000000, pic, vms->secure,
+ vms->virt);
/* A15 daughterboard peripherals: */
@@ -701,8 +713,8 @@ static void vexpress_common_init(MachineState *machine)
daughterboard->bootinfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
daughterboard->bootinfo.gic_cpu_if_addr = daughterboard->gic_cpu_if_addr;
daughterboard->bootinfo.modify_dtb = vexpress_modify_dtb;
- /* Indicate that when booting Linux we should be in secure state */
- daughterboard->bootinfo.secure_boot = true;
+ /* When booting Linux we should be in secure state if the CPU has one. */
+ daughterboard->bootinfo.secure_boot = vms->secure;
arm_load_kernel(ARM_CPU(first_cpu), &daughterboard->bootinfo);
}
@@ -720,6 +732,20 @@ static void vexpress_set_secure(Object *obj, bool value, Error **errp)
vms->secure = value;
}
+static bool vexpress_get_virt(Object *obj, Error **errp)
+{
+ VexpressMachineState *vms = VEXPRESS_MACHINE(obj);
+
+ return vms->virt;
+}
+
+static void vexpress_set_virt(Object *obj, bool value, Error **errp)
+{
+ VexpressMachineState *vms = VEXPRESS_MACHINE(obj);
+
+ vms->virt = value;
+}
+
static void vexpress_instance_init(Object *obj)
{
VexpressMachineState *vms = VEXPRESS_MACHINE(obj);
@@ -734,6 +760,32 @@ static void vexpress_instance_init(Object *obj)
NULL);
}
+static void vexpress_a15_instance_init(Object *obj)
+{
+ VexpressMachineState *vms = VEXPRESS_MACHINE(obj);
+
+ /*
+ * For the vexpress-a15, EL2 is by default enabled if EL3 is,
+ * but can also be specifically set to on or off.
+ */
+ vms->virt = true;
+ object_property_add_bool(obj, "virtualization", vexpress_get_virt,
+ vexpress_set_virt, NULL);
+ object_property_set_description(obj, "virtualization",
+ "Set on/off to enable/disable the ARM "
+ "Virtualization Extensions "
+ "(defaults to same as 'secure')",
+ NULL);
+}
+
+static void vexpress_a9_instance_init(Object *obj)
+{
+ VexpressMachineState *vms = VEXPRESS_MACHINE(obj);
+
+ /* The A9 doesn't have the virt extensions */
+ vms->virt = false;
+}
+
static void vexpress_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
@@ -780,12 +832,14 @@ static const TypeInfo vexpress_a9_info = {
.name = TYPE_VEXPRESS_A9_MACHINE,
.parent = TYPE_VEXPRESS_MACHINE,
.class_init = vexpress_a9_class_init,
+ .instance_init = vexpress_a9_instance_init,
};
static const TypeInfo vexpress_a15_info = {
.name = TYPE_VEXPRESS_A15_MACHINE,
.parent = TYPE_VEXPRESS_MACHINE,
.class_init = vexpress_a15_class_init,
+ .instance_init = vexpress_a15_instance_init,
};
static void vexpress_machine_init(void)
diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c
index aaebec1839..9089dcb47e 100644
--- a/hw/audio/cs4231a.c
+++ b/hw/audio/cs4231a.c
@@ -305,6 +305,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
case 6:
as.endianness = 1;
+ /* fall through */
case 2:
as.fmt = AUD_FMT_S16;
s->shift = as.nchannels;
diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c
index 59cf252754..dd75c9e8f5 100644
--- a/hw/audio/es1370.c
+++ b/hw/audio/es1370.c
@@ -474,82 +474,7 @@ static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr)
return addr;
}
-static void es1370_writeb(void *opaque, uint32_t addr, uint32_t val)
-{
- ES1370State *s = opaque;
- uint32_t shift, mask;
-
- addr = es1370_fixup (s, addr);
-
- switch (addr) {
- case ES1370_REG_CONTROL:
- case ES1370_REG_CONTROL + 1:
- case ES1370_REG_CONTROL + 2:
- case ES1370_REG_CONTROL + 3:
- shift = (addr - ES1370_REG_CONTROL) << 3;
- mask = 0xff << shift;
- val = (s->ctl & ~mask) | ((val & 0xff) << shift);
- es1370_update_voices (s, val, s->sctl);
- print_ctl (val);
- break;
- case ES1370_REG_MEMPAGE:
- s->mempage = val;
- break;
- case ES1370_REG_SERIAL_CONTROL:
- case ES1370_REG_SERIAL_CONTROL + 1:
- case ES1370_REG_SERIAL_CONTROL + 2:
- case ES1370_REG_SERIAL_CONTROL + 3:
- shift = (addr - ES1370_REG_SERIAL_CONTROL) << 3;
- mask = 0xff << shift;
- val = (s->sctl & ~mask) | ((val & 0xff) << shift);
- es1370_maybe_lower_irq (s, val);
- es1370_update_voices (s, s->ctl, val);
- print_sctl (val);
- break;
- default:
- lwarn ("writeb %#x <- %#x\n", addr, val);
- break;
- }
-}
-
-static void es1370_writew(void *opaque, uint32_t addr, uint32_t val)
-{
- ES1370State *s = opaque;
- addr = es1370_fixup (s, addr);
- uint32_t shift, mask;
- struct chan *d = &s->chan[0];
-
- switch (addr) {
- case ES1370_REG_CODEC:
- dolog ("ignored codec write address %#x, data %#x\n",
- (val >> 8) & 0xff, val & 0xff);
- s->codec = val;
- break;
-
- case ES1370_REG_CONTROL:
- case ES1370_REG_CONTROL + 2:
- shift = (addr != ES1370_REG_CONTROL) << 4;
- mask = 0xffff << shift;
- val = (s->ctl & ~mask) | ((val & 0xffff) << shift);
- es1370_update_voices (s, val, s->sctl);
- print_ctl (val);
- break;
-
- case ES1370_REG_ADC_SCOUNT:
- d++;
- case ES1370_REG_DAC2_SCOUNT:
- d++;
- case ES1370_REG_DAC1_SCOUNT:
- d->scount = (d->scount & ~0xffff) | (val & 0xffff);
- break;
-
- default:
- lwarn ("writew %#x <- %#x\n", addr, val);
- break;
- }
-}
-
-static void es1370_writel(void *opaque, uint32_t addr, uint32_t val)
+static void es1370_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
ES1370State *s = opaque;
struct chan *d = &s->chan[0];
@@ -572,21 +497,19 @@ static void es1370_writel(void *opaque, uint32_t addr, uint32_t val)
print_sctl (val);
break;
- case ES1370_REG_ADC_SCOUNT:
- d++;
- case ES1370_REG_DAC2_SCOUNT:
- d++;
case ES1370_REG_DAC1_SCOUNT:
+ case ES1370_REG_DAC2_SCOUNT:
+ case ES1370_REG_ADC_SCOUNT:
+ d += (addr - ES1370_REG_DAC1_SCOUNT) >> 2;
d->scount = (val & 0xffff) | (d->scount & ~0xffff);
ldebug ("chan %td CURR_SAMP_CT %d, SAMP_CT %d\n",
d - &s->chan[0], val >> 16, (val & 0xffff));
break;
- case ES1370_REG_ADC_FRAMEADR:
- d++;
- case ES1370_REG_DAC2_FRAMEADR:
- d++;
case ES1370_REG_DAC1_FRAMEADR:
+ case ES1370_REG_DAC2_FRAMEADR:
+ case ES1370_REG_ADC_FRAMEADR:
+ d += (addr - ES1370_REG_DAC1_FRAMEADR) >> 3;
d->frame_addr = val;
ldebug ("chan %td frame address %#x\n", d - &s->chan[0], val);
break;
@@ -598,11 +521,10 @@ static void es1370_writel(void *opaque, uint32_t addr, uint32_t val)
lwarn ("writing to phantom frame address %#x\n", val);
break;
- case ES1370_REG_ADC_FRAMECNT:
- d++;
- case ES1370_REG_DAC2_FRAMECNT:
- d++;
case ES1370_REG_DAC1_FRAMECNT:
+ case ES1370_REG_DAC2_FRAMECNT:
+ case ES1370_REG_ADC_FRAMECNT:
+ d += (addr - ES1370_REG_DAC1_FRAMECNT) >> 3;
d->frame_cnt = val;
d->leftover = 0;
ldebug ("chan %td frame count %d, buffer size %d\n",
@@ -615,84 +537,7 @@ static void es1370_writel(void *opaque, uint32_t addr, uint32_t val)
}
}
-static uint32_t es1370_readb(void *opaque, uint32_t addr)
-{
- ES1370State *s = opaque;
- uint32_t val;
-
- addr = es1370_fixup (s, addr);
-
- switch (addr) {
- case 0x1b: /* Legacy */
- lwarn ("Attempt to read from legacy register\n");
- val = 5;
- break;
- case ES1370_REG_MEMPAGE:
- val = s->mempage;
- break;
- case ES1370_REG_CONTROL + 0:
- case ES1370_REG_CONTROL + 1:
- case ES1370_REG_CONTROL + 2:
- case ES1370_REG_CONTROL + 3:
- val = s->ctl >> ((addr - ES1370_REG_CONTROL) << 3);
- break;
- case ES1370_REG_STATUS + 0:
- case ES1370_REG_STATUS + 1:
- case ES1370_REG_STATUS + 2:
- case ES1370_REG_STATUS + 3:
- val = s->status >> ((addr - ES1370_REG_STATUS) << 3);
- break;
- default:
- val = ~0;
- lwarn ("readb %#x -> %#x\n", addr, val);
- break;
- }
- return val;
-}
-
-static uint32_t es1370_readw(void *opaque, uint32_t addr)
-{
- ES1370State *s = opaque;
- struct chan *d = &s->chan[0];
- uint32_t val;
-
- addr = es1370_fixup (s, addr);
-
- switch (addr) {
- case ES1370_REG_ADC_SCOUNT + 2:
- d++;
- case ES1370_REG_DAC2_SCOUNT + 2:
- d++;
- case ES1370_REG_DAC1_SCOUNT + 2:
- val = d->scount >> 16;
- break;
-
- case ES1370_REG_ADC_FRAMECNT:
- d++;
- case ES1370_REG_DAC2_FRAMECNT:
- d++;
- case ES1370_REG_DAC1_FRAMECNT:
- val = d->frame_cnt & 0xffff;
- break;
-
- case ES1370_REG_ADC_FRAMECNT + 2:
- d++;
- case ES1370_REG_DAC2_FRAMECNT + 2:
- d++;
- case ES1370_REG_DAC1_FRAMECNT + 2:
- val = d->frame_cnt >> 16;
- break;
-
- default:
- val = ~0;
- lwarn ("readw %#x -> %#x\n", addr, val);
- break;
- }
-
- return val;
-}
-
-static uint32_t es1370_readl(void *opaque, uint32_t addr)
+static uint64_t es1370_read(void *opaque, hwaddr addr, unsigned size)
{
ES1370State *s = opaque;
uint32_t val;
@@ -717,11 +562,10 @@ static uint32_t es1370_readl(void *opaque, uint32_t addr)
val = s->sctl;
break;
- case ES1370_REG_ADC_SCOUNT:
- d++;
- case ES1370_REG_DAC2_SCOUNT:
- d++;
case ES1370_REG_DAC1_SCOUNT:
+ case ES1370_REG_DAC2_SCOUNT:
+ case ES1370_REG_ADC_SCOUNT:
+ d += (addr - ES1370_REG_DAC1_SCOUNT) >> 2;
val = d->scount;
#ifdef DEBUG_ES1370
{
@@ -735,11 +579,10 @@ static uint32_t es1370_readl(void *opaque, uint32_t addr)
#endif
break;
- case ES1370_REG_ADC_FRAMECNT:
- d++;
- case ES1370_REG_DAC2_FRAMECNT:
- d++;
case ES1370_REG_DAC1_FRAMECNT:
+ case ES1370_REG_DAC2_FRAMECNT:
+ case ES1370_REG_ADC_FRAMECNT:
+ d += (addr - ES1370_REG_DAC1_FRAMECNT) >> 3;
val = d->frame_cnt;
#ifdef DEBUG_ES1370
{
@@ -753,11 +596,10 @@ static uint32_t es1370_readl(void *opaque, uint32_t addr)
#endif
break;
- case ES1370_REG_ADC_FRAMEADR:
- d++;
- case ES1370_REG_DAC2_FRAMEADR:
- d++;
case ES1370_REG_DAC1_FRAMEADR:
+ case ES1370_REG_DAC2_FRAMEADR:
+ case ES1370_REG_ADC_FRAMEADR:
+ d += (addr - ES1370_REG_DAC1_FRAMEADR) >> 3;
val = d->frame_addr;
break;
@@ -908,44 +750,17 @@ static void es1370_adc_callback (void *opaque, int avail)
es1370_run_channel (s, ADC_CHANNEL, avail);
}
-static uint64_t es1370_read(void *opaque, hwaddr addr,
- unsigned size)
-{
- switch (size) {
- case 1:
- return es1370_readb(opaque, addr);
- case 2:
- return es1370_readw(opaque, addr);
- case 4:
- return es1370_readl(opaque, addr);
- default:
- return -1;
- }
-}
-
-static void es1370_write(void *opaque, hwaddr addr, uint64_t val,
- unsigned size)
-{
- switch (size) {
- case 1:
- es1370_writeb(opaque, addr, val);
- break;
- case 2:
- es1370_writew(opaque, addr, val);
- break;
- case 4:
- es1370_writel(opaque, addr, val);
- break;
- }
-}
-
static const MemoryRegionOps es1370_io_ops = {
.read = es1370_read,
.write = es1370_write,
- .impl = {
+ .valid = {
.min_access_size = 1,
.max_access_size = 4,
},
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
.endianness = DEVICE_LITTLE_ENDIAN,
};
diff --git a/hw/audio/gusemu_hal.c b/hw/audio/gusemu_hal.c
index 1150fc4426..ae40ca341c 100644
--- a/hw/audio/gusemu_hal.c
+++ b/hw/audio/gusemu_hal.c
@@ -261,6 +261,7 @@ void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
GUSregb(IRQStatReg2x6) = 0x10;
GUS_irqrequest(state, state->gusirq, 1);
}
+ /* fall through */
case 0x20D: /* SB2xCd no IRQ */
GUSregb(SB2xCd) = (uint8_t) data;
break;
diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c
index 5a4d32364e..c5b9bf79e8 100644
--- a/hw/audio/sb16.c
+++ b/hw/audio/sb16.c
@@ -741,10 +741,15 @@ static void complete (SB16State *s)
ldebug ("set time const %d\n", s->time_const);
break;
- case 0x42: /* FT2 sets output freq with this, go figure */
- qemu_log_mask(LOG_UNIMP, "cmd 0x42 might not do what it think it"
- " should\n");
case 0x41:
+ case 0x42:
+ /*
+ * 0x41 is documented as setting the output sample rate,
+ * and 0x42 the input sample rate, but in fact SB16 hardware
+ * seems to have only a single sample rate under the hood,
+ * and FT2 sets output freq with this (go figure). Compare:
+ * http://homepages.cae.wisc.edu/~brodskye/sb16doc/sb16doc.html#SamplingRate
+ */
s->freq = dsp_get_hilo (s);
ldebug ("set freq %d\n", s->freq);
break;
diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c
index 43c1079493..5649843cd8 100644
--- a/hw/cpu/a15mpcore.c
+++ b/hw/cpu/a15mpcore.c
@@ -53,6 +53,7 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
int i;
Error *err = NULL;
bool has_el3;
+ bool has_el2 = false;
Object *cpuobj;
gicdev = DEVICE(&s->gic);
@@ -67,6 +68,10 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
has_el3 = object_property_find(cpuobj, "has_el3", NULL) &&
object_property_get_bool(cpuobj, "has_el3", &error_abort);
qdev_prop_set_bit(gicdev, "has-security-extensions", has_el3);
+ /* Similarly for virtualization support */
+ has_el2 = object_property_find(cpuobj, "has_el2", NULL) &&
+ object_property_get_bool(cpuobj, "has_el2", &error_abort);
+ qdev_prop_set_bit(gicdev, "has-virtualization-extensions", has_el2);
}
object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
@@ -103,20 +108,40 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in(gicdev,
ppibase + timer_irq[irq]));
}
+ if (has_el2) {
+ /* Connect the GIC maintenance interrupt to PPI ID 25 */
+ sysbus_connect_irq(SYS_BUS_DEVICE(gicdev), i + 4 * s->num_cpu,
+ qdev_get_gpio_in(gicdev, ppibase + 25));
+ }
}
/* Memory map (addresses are offsets from PERIPHBASE):
* 0x0000-0x0fff -- reserved
* 0x1000-0x1fff -- GIC Distributor
* 0x2000-0x3fff -- GIC CPU interface
- * 0x4000-0x4fff -- GIC virtual interface control (not modelled)
- * 0x5000-0x5fff -- GIC virtual interface control (not modelled)
- * 0x6000-0x7fff -- GIC virtual CPU interface (not modelled)
+ * 0x4000-0x4fff -- GIC virtual interface control for this CPU
+ * 0x5000-0x51ff -- GIC virtual interface control for CPU 0
+ * 0x5200-0x53ff -- GIC virtual interface control for CPU 1
+ * 0x5400-0x55ff -- GIC virtual interface control for CPU 2
+ * 0x5600-0x57ff -- GIC virtual interface control for CPU 3
+ * 0x6000-0x7fff -- GIC virtual CPU interface
*/
memory_region_add_subregion(&s->container, 0x1000,
sysbus_mmio_get_region(busdev, 0));
memory_region_add_subregion(&s->container, 0x2000,
sysbus_mmio_get_region(busdev, 1));
+ if (has_el2) {
+ memory_region_add_subregion(&s->container, 0x4000,
+ sysbus_mmio_get_region(busdev, 2));
+ memory_region_add_subregion(&s->container, 0x6000,
+ sysbus_mmio_get_region(busdev, 3));
+ for (i = 0; i < s->num_cpu; i++) {
+ hwaddr base = 0x5000 + i * 0x200;
+ MemoryRegion *mr = sysbus_mmio_get_region(busdev,
+ 4 + s->num_cpu + i);
+ memory_region_add_subregion(&s->container, base, mr);
+ }
+ }
}
static Property a15mp_priv_properties[] = {
diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c
index 3355f4c131..d534d00a65 100644
--- a/hw/display/bcm2835_fb.c
+++ b/hw/display/bcm2835_fb.c
@@ -34,6 +34,13 @@
#define DEFAULT_VCRAM_SIZE 0x4000000
#define BCM2835_FB_OFFSET 0x00100000
+/* Maximum permitted framebuffer size; experimentally determined on an rpi2 */
+#define XRES_MAX 3840
+#define YRES_MAX 2560
+/* Framebuffer size used if guest requests zero size */
+#define XRES_SMALL 592
+#define YRES_SMALL 488
+
static void fb_invalidate_display(void *opaque)
{
BCM2835FBState *s = BCM2835_FB(opaque);
@@ -52,7 +59,7 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src,
int bpp = surface_bits_per_pixel(surface);
while (width--) {
- switch (s->bpp) {
+ switch (s->config.bpp) {
case 8:
/* lookup palette starting at video ram base
* TODO: cache translation, rather than doing this each time!
@@ -91,7 +98,7 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src,
break;
}
- if (s->pixo == 0) {
+ if (s->config.pixo == 0) {
/* swap to BGR pixel format */
uint8_t tmp = r;
r = b;
@@ -126,6 +133,18 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src,
}
}
+static bool fb_use_offsets(BCM2835FBConfig *config)
+{
+ /*
+ * Return true if we should use the viewport offsets.
+ * Experimentally, the hardware seems to do this only if the
+ * viewport size is larger than the physical screen. (It doesn't
+ * prevent the guest setting this silly viewport setting, though...)
+ */
+ return config->xres_virtual > config->xres &&
+ config->yres_virtual > config->yres;
+}
+
static void fb_update_display(void *opaque)
{
BCM2835FBState *s = opaque;
@@ -134,13 +153,19 @@ static void fb_update_display(void *opaque)
int last = 0;
int src_width = 0;
int dest_width = 0;
+ uint32_t xoff = 0, yoff = 0;
- if (s->lock || !s->xres) {
+ if (s->lock || !s->config.xres) {
return;
}
- src_width = s->xres * (s->bpp >> 3);
- dest_width = s->xres;
+ src_width = bcm2835_fb_get_pitch(&s->config);
+ if (fb_use_offsets(&s->config)) {
+ xoff = s->config.xoffset;
+ yoff = s->config.yoffset;
+ }
+
+ dest_width = s->config.xres;
switch (surface_bits_per_pixel(surface)) {
case 0:
@@ -165,89 +190,104 @@ static void fb_update_display(void *opaque)
}
if (s->invalidate) {
- framebuffer_update_memory_section(&s->fbsection, s->dma_mr, s->base,
- s->yres, src_width);
+ hwaddr base = s->config.base + xoff + yoff * src_width;
+ framebuffer_update_memory_section(&s->fbsection, s->dma_mr,
+ base,
+ s->config.yres, src_width);
}
- framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
+ framebuffer_update_display(surface, &s->fbsection,
+ s->config.xres, s->config.yres,
src_width, dest_width, 0, s->invalidate,
draw_line_src16, s, &first, &last);
if (first >= 0) {
- dpy_gfx_update(s->con, 0, first, s->xres, last - first + 1);
+ dpy_gfx_update(s->con, 0, first, s->config.xres,
+ last - first + 1);
}
s->invalidate = false;
}
-static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value)
+void bcm2835_fb_validate_config(BCM2835FBConfig *config)
{
- value &= ~0xf;
-
- s->lock = true;
-
- s->xres = ldl_le_phys(&s->dma_as, value);
- s->yres = ldl_le_phys(&s->dma_as, value + 4);
- s->xres_virtual = ldl_le_phys(&s->dma_as, value + 8);
- s->yres_virtual = ldl_le_phys(&s->dma_as, value + 12);
- s->bpp = ldl_le_phys(&s->dma_as, value + 20);
- s->xoffset = ldl_le_phys(&s->dma_as, value + 24);
- s->yoffset = ldl_le_phys(&s->dma_as, value + 28);
-
- s->base = s->vcram_base | (value & 0xc0000000);
- s->base += BCM2835_FB_OFFSET;
+ /*
+ * Validate the config, and clip any bogus values into range,
+ * as the hardware does. Note that fb_update_display() relies on
+ * this happening to prevent it from performing out-of-range
+ * accesses on redraw.
+ */
+ config->xres = MIN(config->xres, XRES_MAX);
+ config->xres_virtual = MIN(config->xres_virtual, XRES_MAX);
+ config->yres = MIN(config->yres, YRES_MAX);
+ config->yres_virtual = MIN(config->yres_virtual, YRES_MAX);
+
+ /*
+ * These are not minima: a 40x40 framebuffer will be accepted.
+ * They're only used as defaults if the guest asks for zero size.
+ */
+ if (config->xres == 0) {
+ config->xres = XRES_SMALL;
+ }
+ if (config->yres == 0) {
+ config->yres = YRES_SMALL;
+ }
+ if (config->xres_virtual == 0) {
+ config->xres_virtual = config->xres;
+ }
+ if (config->yres_virtual == 0) {
+ config->yres_virtual = config->yres;
+ }
- /* TODO - Manage properly virtual resolution */
+ if (fb_use_offsets(config)) {
+ /* Clip the offsets so the viewport is within the physical screen */
+ config->xoffset = MIN(config->xoffset,
+ config->xres_virtual - config->xres);
+ config->yoffset = MIN(config->yoffset,
+ config->yres_virtual - config->yres);
+ }
+}
- s->pitch = s->xres * (s->bpp >> 3);
- s->size = s->yres * s->pitch;
+void bcm2835_fb_reconfigure(BCM2835FBState *s, BCM2835FBConfig *newconfig)
+{
+ s->lock = true;
- stl_le_phys(&s->dma_as, value + 16, s->pitch);
- stl_le_phys(&s->dma_as, value + 32, s->base);
- stl_le_phys(&s->dma_as, value + 36, s->size);
+ s->config = *newconfig;
s->invalidate = true;
- qemu_console_resize(s->con, s->xres, s->yres);
+ qemu_console_resize(s->con, s->config.xres, s->config.yres);
s->lock = false;
}
-void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres,
- uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp,
- uint32_t *pixo, uint32_t *alpha)
+static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value)
{
- s->lock = true;
+ uint32_t pitch;
+ uint32_t size;
+ BCM2835FBConfig newconf;
- /* TODO: input validation! */
- if (xres) {
- s->xres = *xres;
- }
- if (yres) {
- s->yres = *yres;
- }
- if (xoffset) {
- s->xoffset = *xoffset;
- }
- if (yoffset) {
- s->yoffset = *yoffset;
- }
- if (bpp) {
- s->bpp = *bpp;
- }
- if (pixo) {
- s->pixo = *pixo;
- }
- if (alpha) {
- s->alpha = *alpha;
- }
+ value &= ~0xf;
- /* TODO - Manage properly virtual resolution */
+ newconf.xres = ldl_le_phys(&s->dma_as, value);
+ newconf.yres = ldl_le_phys(&s->dma_as, value + 4);
+ newconf.xres_virtual = ldl_le_phys(&s->dma_as, value + 8);
+ newconf.yres_virtual = ldl_le_phys(&s->dma_as, value + 12);
+ newconf.bpp = ldl_le_phys(&s->dma_as, value + 20);
+ newconf.xoffset = ldl_le_phys(&s->dma_as, value + 24);
+ newconf.yoffset = ldl_le_phys(&s->dma_as, value + 28);
- s->pitch = s->xres * (s->bpp >> 3);
- s->size = s->yres * s->pitch;
+ newconf.base = s->vcram_base | (value & 0xc0000000);
+ newconf.base += BCM2835_FB_OFFSET;
- s->invalidate = true;
- qemu_console_resize(s->con, s->xres, s->yres);
- s->lock = false;
+ bcm2835_fb_validate_config(&newconf);
+
+ pitch = bcm2835_fb_get_pitch(&newconf);
+ size = bcm2835_fb_get_size(&newconf);
+
+ stl_le_phys(&s->dma_as, value + 16, pitch);
+ stl_le_phys(&s->dma_as, value + 32, newconf.base);
+ stl_le_phys(&s->dma_as, value + 36, size);
+
+ bcm2835_fb_reconfigure(s, &newconf);
}
static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size)
@@ -312,18 +352,17 @@ static const VMStateDescription vmstate_bcm2835_fb = {
VMSTATE_BOOL(lock, BCM2835FBState),
VMSTATE_BOOL(invalidate, BCM2835FBState),
VMSTATE_BOOL(pending, BCM2835FBState),
- VMSTATE_UINT32(xres, BCM2835FBState),
- VMSTATE_UINT32(yres, BCM2835FBState),
- VMSTATE_UINT32(xres_virtual, BCM2835FBState),
- VMSTATE_UINT32(yres_virtual, BCM2835FBState),
- VMSTATE_UINT32(xoffset, BCM2835FBState),
- VMSTATE_UINT32(yoffset, BCM2835FBState),
- VMSTATE_UINT32(bpp, BCM2835FBState),
- VMSTATE_UINT32(base, BCM2835FBState),
- VMSTATE_UINT32(pitch, BCM2835FBState),
- VMSTATE_UINT32(size, BCM2835FBState),
- VMSTATE_UINT32(pixo, BCM2835FBState),
- VMSTATE_UINT32(alpha, BCM2835FBState),
+ VMSTATE_UINT32(config.xres, BCM2835FBState),
+ VMSTATE_UINT32(config.yres, BCM2835FBState),
+ VMSTATE_UINT32(config.xres_virtual, BCM2835FBState),
+ VMSTATE_UINT32(config.yres_virtual, BCM2835FBState),
+ VMSTATE_UINT32(config.xoffset, BCM2835FBState),
+ VMSTATE_UINT32(config.yoffset, BCM2835FBState),
+ VMSTATE_UINT32(config.bpp, BCM2835FBState),
+ VMSTATE_UINT32(config.base, BCM2835FBState),
+ VMSTATE_UNUSED(8), /* Was pitch and size */
+ VMSTATE_UINT32(config.pixo, BCM2835FBState),
+ VMSTATE_UINT32(config.alpha, BCM2835FBState),
VMSTATE_END_OF_LIST()
}
};
@@ -349,13 +388,7 @@ static void bcm2835_fb_reset(DeviceState *dev)
s->pending = false;
- s->xres_virtual = s->xres;
- s->yres_virtual = s->yres;
- s->xoffset = 0;
- s->yoffset = 0;
- s->base = s->vcram_base + BCM2835_FB_OFFSET;
- s->pitch = s->xres * (s->bpp >> 3);
- s->size = s->yres * s->pitch;
+ s->config = s->initial_config;
s->invalidate = true;
s->lock = false;
@@ -379,24 +412,33 @@ static void bcm2835_fb_realize(DeviceState *dev, Error **errp)
return;
}
+ /* Fill in the parts of initial_config that are not set by QOM properties */
+ s->initial_config.xres_virtual = s->initial_config.xres;
+ s->initial_config.yres_virtual = s->initial_config.yres;
+ s->initial_config.xoffset = 0;
+ s->initial_config.yoffset = 0;
+ s->initial_config.base = s->vcram_base + BCM2835_FB_OFFSET;
+
s->dma_mr = MEMORY_REGION(obj);
address_space_init(&s->dma_as, s->dma_mr, NULL);
bcm2835_fb_reset(dev);
s->con = graphic_console_init(dev, 0, &vgafb_ops, s);
- qemu_console_resize(s->con, s->xres, s->yres);
+ qemu_console_resize(s->con, s->config.xres, s->config.yres);
}
static Property bcm2835_fb_props[] = {
DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/
DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size,
DEFAULT_VCRAM_SIZE),
- DEFINE_PROP_UINT32("xres", BCM2835FBState, xres, 640),
- DEFINE_PROP_UINT32("yres", BCM2835FBState, yres, 480),
- DEFINE_PROP_UINT32("bpp", BCM2835FBState, bpp, 16),
- DEFINE_PROP_UINT32("pixo", BCM2835FBState, pixo, 1), /* 1=RGB, 0=BGR */
- DEFINE_PROP_UINT32("alpha", BCM2835FBState, alpha, 2), /* alpha ignored */
+ DEFINE_PROP_UINT32("xres", BCM2835FBState, initial_config.xres, 640),
+ DEFINE_PROP_UINT32("yres", BCM2835FBState, initial_config.yres, 480),
+ DEFINE_PROP_UINT32("bpp", BCM2835FBState, initial_config.bpp, 16),
+ DEFINE_PROP_UINT32("pixo", BCM2835FBState,
+ initial_config.pixo, 1), /* 1=RGB, 0=BGR */
+ DEFINE_PROP_UINT32("alpha", BCM2835FBState,
+ initial_config.alpha, 2), /* alpha ignored */
DEFINE_PROP_END_OF_LIST()
};
diff --git a/hw/display/cg3.c b/hw/display/cg3.c
index 6fff4852c5..1c199ab369 100644
--- a/hw/display/cg3.c
+++ b/hw/display/cg3.c
@@ -232,6 +232,7 @@ static void cg3_reg_write(void *opaque, hwaddr addr, uint64_t val,
s->b[s->dac_index] = regval;
/* Index autoincrement */
s->dac_index = (s->dac_index + 1) & 0xff;
+ /* fall through */
default:
s->dac_state = 0;
break;
diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
index 7583b18c29..04c87c8e8d 100644
--- a/hw/display/cirrus_vga.c
+++ b/hw/display/cirrus_vga.c
@@ -1426,7 +1426,8 @@ static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val)
s->vga.hw_cursor_y = (val << 3) | (s->vga.sr_index >> 5);
break;
case 0x07: // Extended Sequencer Mode
- cirrus_update_memory_access(s);
+ cirrus_update_memory_access(s);
+ /* fall through */
case 0x08: // EEPROM Control
case 0x09: // Scratch Register 0
case 0x0a: // Scratch Register 1
diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c
index 0d26e0f6b5..685a2378ed 100644
--- a/hw/i2c/pm_smbus.c
+++ b/hw/i2c/pm_smbus.c
@@ -22,8 +22,6 @@
#include "hw/i2c/pm_smbus.h"
#include "hw/i2c/smbus.h"
-/* no save/load? */
-
#define SMBHSTSTS 0x00
#define SMBHSTCNT 0x02
#define SMBHSTCMD 0x03
@@ -31,20 +29,40 @@
#define SMBHSTDAT0 0x05
#define SMBHSTDAT1 0x06
#define SMBBLKDAT 0x07
+#define SMBAUXCTL 0x0d
-#define STS_HOST_BUSY (1)
-#define STS_INTR (1<<1)
-#define STS_DEV_ERR (1<<2)
-#define STS_BUS_ERR (1<<3)
-#define STS_FAILED (1<<4)
-#define STS_SMBALERT (1<<5)
-#define STS_INUSE_STS (1<<6)
-#define STS_BYTE_DONE (1<<7)
+#define STS_HOST_BUSY (1 << 0)
+#define STS_INTR (1 << 1)
+#define STS_DEV_ERR (1 << 2)
+#define STS_BUS_ERR (1 << 3)
+#define STS_FAILED (1 << 4)
+#define STS_SMBALERT (1 << 5)
+#define STS_INUSE_STS (1 << 6)
+#define STS_BYTE_DONE (1 << 7)
/* Signs of successfully transaction end :
* ByteDoneStatus = 1 (STS_BYTE_DONE) and INTR = 1 (STS_INTR )
*/
-//#define DEBUG
+#define CTL_INTREN (1 << 0)
+#define CTL_KILL (1 << 1)
+#define CTL_LAST_BYTE (1 << 5)
+#define CTL_START (1 << 6)
+#define CTL_PEC_EN (1 << 7)
+#define CTL_RETURN_MASK 0x1f
+
+#define PROT_QUICK 0
+#define PROT_BYTE 1
+#define PROT_BYTE_DATA 2
+#define PROT_WORD_DATA 3
+#define PROT_PROC_CALL 4
+#define PROT_BLOCK_DATA 5
+#define PROT_I2C_BLOCK_READ 6
+
+#define AUX_PEC (1 << 0)
+#define AUX_BLK (1 << 1)
+#define AUX_MASK 0x3
+
+/*#define DEBUG*/
#ifdef DEBUG
# define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
@@ -62,19 +80,17 @@ static void smb_transaction(PMSMBus *s)
I2CBus *bus = s->smbus;
int ret;
- assert(s->smb_stat & STS_HOST_BUSY);
- s->smb_stat &= ~STS_HOST_BUSY;
-
SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot);
/* Transaction isn't exec if STS_DEV_ERR bit set */
if ((s->smb_stat & STS_DEV_ERR) != 0) {
goto error;
}
+
switch(prot) {
- case 0x0:
+ case PROT_QUICK:
ret = smbus_quick_command(bus, addr, read);
goto done;
- case 0x1:
+ case PROT_BYTE:
if (read) {
ret = smbus_receive_byte(bus, addr);
goto data8;
@@ -82,7 +98,7 @@ static void smb_transaction(PMSMBus *s)
ret = smbus_send_byte(bus, addr, cmd);
goto done;
}
- case 0x2:
+ case PROT_BYTE_DATA:
if (read) {
ret = smbus_read_byte(bus, addr, cmd);
goto data8;
@@ -91,22 +107,73 @@ static void smb_transaction(PMSMBus *s)
goto done;
}
break;
- case 0x3:
+ case PROT_WORD_DATA:
if (read) {
ret = smbus_read_word(bus, addr, cmd);
goto data16;
} else {
- ret = smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0);
+ ret = smbus_write_word(bus, addr, cmd,
+ (s->smb_data1 << 8) | s->smb_data0);
goto done;
}
break;
- case 0x5:
+ case PROT_I2C_BLOCK_READ:
if (read) {
- ret = smbus_read_block(bus, addr, cmd, s->smb_data);
+ int xfersize = s->smb_data0;
+ if (xfersize > sizeof(s->smb_data)) {
+ xfersize = sizeof(s->smb_data);
+ }
+ ret = smbus_read_block(bus, addr, s->smb_data1, s->smb_data,
+ xfersize, false, true);
goto data8;
} else {
- ret = smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0);
- goto done;
+ /* The manual says the behavior is undefined, just set DEV_ERR. */
+ goto error;
+ }
+ break;
+ case PROT_BLOCK_DATA:
+ if (read) {
+ ret = smbus_read_block(bus, addr, cmd, s->smb_data,
+ sizeof(s->smb_data), !s->i2c_enable,
+ !s->i2c_enable);
+ if (ret < 0) {
+ goto error;
+ }
+ s->smb_index = 0;
+ s->op_done = false;
+ if (s->smb_auxctl & AUX_BLK) {
+ s->smb_stat |= STS_INTR;
+ } else {
+ s->smb_blkdata = s->smb_data[0];
+ s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE;
+ }
+ s->smb_data0 = ret;
+ goto out;
+ } else {
+ if (s->smb_auxctl & AUX_BLK) {
+ if (s->smb_index != s->smb_data0) {
+ s->smb_index = 0;
+ goto error;
+ }
+ /* Data is already all written to the queue, just do
+ the operation. */
+ s->smb_index = 0;
+ ret = smbus_write_block(bus, addr, cmd, s->smb_data,
+ s->smb_data0, !s->i2c_enable);
+ if (ret < 0) {
+ goto error;
+ }
+ s->op_done = true;
+ s->smb_stat |= STS_INTR;
+ s->smb_stat &= ~STS_HOST_BUSY;
+ } else {
+ s->op_done = false;
+ s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE;
+ s->smb_data[0] = s->smb_blkdata;
+ s->smb_index = 0;
+ ret = 0;
+ }
+ goto out;
}
break;
default:
@@ -128,20 +195,35 @@ done:
if (ret < 0) {
goto error;
}
- s->smb_stat |= STS_BYTE_DONE | STS_INTR;
+ s->smb_stat |= STS_INTR;
+out:
return;
error:
s->smb_stat |= STS_DEV_ERR;
return;
-
}
static void smb_transaction_start(PMSMBus *s)
{
- /* Do not execute immediately the command ; it will be
- * executed when guest will read SMB_STAT register */
- s->smb_stat |= STS_HOST_BUSY;
+ if (s->smb_ctl & CTL_INTREN) {
+ smb_transaction(s);
+ } else {
+ /* Do not execute immediately the command; it will be
+ * executed when guest will read SMB_STAT register. This
+ * is to work around a bug in AMIBIOS (that is working
+ * around another bug in some specific hardware) where
+ * it waits for STS_HOST_BUSY to be set before waiting
+ * checking for status. If STS_HOST_BUSY doesn't get
+ * set, it gets stuck. */
+ s->smb_stat |= STS_HOST_BUSY;
+ }
+}
+
+static bool
+smb_irq_value(PMSMBus *s)
+{
+ return ((s->smb_stat & ~STS_HOST_BUSY) != 0) && (s->smb_ctl & CTL_INTREN);
}
static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
@@ -153,13 +235,61 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
" val=0x%02" PRIx64 "\n", addr, val);
switch(addr) {
case SMBHSTSTS:
- s->smb_stat = (~(val & 0xff)) & s->smb_stat;
- s->smb_index = 0;
+ s->smb_stat &= ~(val & ~STS_HOST_BUSY);
+ if (!s->op_done && !(s->smb_auxctl & AUX_BLK)) {
+ uint8_t read = s->smb_addr & 0x01;
+
+ s->smb_index++;
+ if (!read && s->smb_index == s->smb_data0) {
+ uint8_t prot = (s->smb_ctl >> 2) & 0x07;
+ uint8_t cmd = s->smb_cmd;
+ uint8_t addr = s->smb_addr >> 1;
+ int ret;
+
+ if (prot == PROT_I2C_BLOCK_READ) {
+ s->smb_stat |= STS_DEV_ERR;
+ goto out;
+ }
+
+ ret = smbus_write_block(s->smbus, addr, cmd, s->smb_data,
+ s->smb_data0, !s->i2c_enable);
+ if (ret < 0) {
+ s->smb_stat |= STS_DEV_ERR;
+ goto out;
+ }
+ s->op_done = true;
+ s->smb_stat |= STS_INTR;
+ s->smb_stat &= ~STS_HOST_BUSY;
+ } else if (!read) {
+ s->smb_data[s->smb_index] = s->smb_blkdata;
+ s->smb_stat |= STS_BYTE_DONE;
+ } else if (s->smb_ctl & CTL_LAST_BYTE) {
+ s->op_done = true;
+ s->smb_blkdata = s->smb_data[s->smb_index];
+ s->smb_index = 0;
+ s->smb_stat |= STS_INTR;
+ s->smb_stat &= ~STS_HOST_BUSY;
+ } else {
+ s->smb_blkdata = s->smb_data[s->smb_index];
+ s->smb_stat |= STS_BYTE_DONE;
+ }
+ }
break;
case SMBHSTCNT:
- s->smb_ctl = val;
- if (val & 0x40)
+ s->smb_ctl = val & ~CTL_START; /* CTL_START always reads 0 */
+ if (val & CTL_START) {
+ if (!s->op_done) {
+ s->smb_index = 0;
+ s->op_done = true;
+ }
smb_transaction_start(s);
+ }
+ if (s->smb_ctl & CTL_KILL) {
+ s->op_done = true;
+ s->smb_index = 0;
+ s->smb_stat |= STS_FAILED;
+ s->smb_stat &= ~STS_HOST_BUSY;
+ }
break;
case SMBHSTCMD:
s->smb_cmd = val;
@@ -174,13 +304,26 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
s->smb_data1 = val;
break;
case SMBBLKDAT:
- s->smb_data[s->smb_index++] = val;
- if (s->smb_index > 31)
+ if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
s->smb_index = 0;
+ }
+ if (s->smb_auxctl & AUX_BLK) {
+ s->smb_data[s->smb_index++] = val;
+ } else {
+ s->smb_blkdata = val;
+ }
+ break;
+ case SMBAUXCTL:
+ s->smb_auxctl = val & AUX_MASK;
break;
default:
break;
}
+
+ out:
+ if (s->set_irq) {
+ s->set_irq(s, smb_irq_value(s));
+ }
}
static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
@@ -193,12 +336,12 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
val = s->smb_stat;
if (s->smb_stat & STS_HOST_BUSY) {
/* execute command now */
+ s->smb_stat &= ~STS_HOST_BUSY;
smb_transaction(s);
}
break;
case SMBHSTCNT:
- s->smb_index = 0;
- val = s->smb_ctl & 0x1f;
+ val = s->smb_ctl & CTL_RETURN_MASK;
break;
case SMBHSTCMD:
val = s->smb_cmd;
@@ -213,18 +356,44 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
val = s->smb_data1;
break;
case SMBBLKDAT:
- val = s->smb_data[s->smb_index++];
- if (s->smb_index > 31)
+ if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
s->smb_index = 0;
+ }
+ if (s->smb_auxctl & AUX_BLK) {
+ val = s->smb_data[s->smb_index++];
+ if (!s->op_done && s->smb_index == s->smb_data0) {
+ s->op_done = true;
+ s->smb_index = 0;
+ s->smb_stat &= ~STS_HOST_BUSY;
+ }
+ } else {
+ val = s->smb_blkdata;
+ }
+ break;
+ case SMBAUXCTL:
+ val = s->smb_auxctl;
break;
default:
val = 0;
break;
}
- SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n", addr, val);
+ SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n",
+ addr, val);
+
+ if (s->set_irq) {
+ s->set_irq(s, smb_irq_value(s));
+ }
+
return val;
}
+static void pm_smbus_reset(PMSMBus *s)
+{
+ s->op_done = true;
+ s->smb_index = 0;
+ s->smb_stat = 0;
+}
+
static const MemoryRegionOps pm_smbus_ops = {
.read = smb_ioport_readb,
.write = smb_ioport_writeb,
@@ -233,9 +402,14 @@ static const MemoryRegionOps pm_smbus_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
-void pm_smbus_init(DeviceState *parent, PMSMBus *smb)
+void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk)
{
+ smb->op_done = true;
+ smb->reset = pm_smbus_reset;
smb->smbus = i2c_init_bus(parent, "i2c");
+ if (force_aux_blk) {
+ smb->smb_auxctl |= AUX_BLK;
+ }
memory_region_init_io(&smb->io, OBJECT(parent), &pm_smbus_ops, smb,
"pm-smbus", 64);
}
diff --git a/hw/i2c/smbus.c b/hw/i2c/smbus.c
index 587ce1ab7f..6ff77c582f 100644
--- a/hw/i2c/smbus.c
+++ b/hw/i2c/smbus.c
@@ -293,33 +293,42 @@ int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data)
return 0;
}
-int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data)
+int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
+ int len, bool recv_len, bool send_cmd)
{
- int len;
+ int rlen;
int i;
- if (i2c_start_transfer(bus, addr, 0)) {
- return -1;
+ if (send_cmd) {
+ if (i2c_start_transfer(bus, addr, 0)) {
+ return -1;
+ }
+ i2c_send(bus, command);
}
- i2c_send(bus, command);
if (i2c_start_transfer(bus, addr, 1)) {
- i2c_end_transfer(bus);
+ if (send_cmd) {
+ i2c_end_transfer(bus);
+ }
return -1;
}
- len = i2c_recv(bus);
- if (len > 32) {
- len = 0;
+ if (recv_len) {
+ rlen = i2c_recv(bus);
+ } else {
+ rlen = len;
}
- for (i = 0; i < len; i++) {
+ if (rlen > len) {
+ rlen = 0;
+ }
+ for (i = 0; i < rlen; i++) {
data[i] = i2c_recv(bus);
}
i2c_nack(bus);
i2c_end_transfer(bus);
- return len;
+ return rlen;
}
int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
- int len)
+ int len, bool send_len)
{
int i;
@@ -330,7 +339,9 @@ int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
return -1;
}
i2c_send(bus, command);
- i2c_send(bus, len);
+ if (send_len) {
+ i2c_send(bus, len);
+ }
for (i = 0; i < len; i++) {
i2c_send(bus, data[i]);
}
diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c
index 007cb6701d..2a8b49e02f 100644
--- a/hw/i2c/smbus_ich9.c
+++ b/hw/i2c/smbus_ich9.c
@@ -40,6 +40,8 @@
typedef struct ICH9SMBState {
PCIDevice dev;
+ bool irq_enabled;
+
PMSMBus smb;
} ICH9SMBState;
@@ -61,12 +63,16 @@ static void ich9_smbus_write_config(PCIDevice *d, uint32_t address,
pci_default_write_config(d, address, val, len);
if (range_covers_byte(address, len, ICH9_SMB_HOSTC)) {
uint8_t hostc = s->dev.config[ICH9_SMB_HOSTC];
- if ((hostc & ICH9_SMB_HOSTC_HST_EN) &&
- !(hostc & ICH9_SMB_HOSTC_I2C_EN)) {
+ if (hostc & ICH9_SMB_HOSTC_HST_EN) {
memory_region_set_enabled(&s->smb.io, true);
} else {
memory_region_set_enabled(&s->smb.io, false);
}
+ s->smb.i2c_enable = (hostc & ICH9_SMB_HOSTC_I2C_EN) != 0;
+ if (hostc & ICH9_SMB_HOSTC_SSRESET) {
+ s->smb.reset(&s->smb);
+ s->dev.config[ICH9_SMB_HOSTC] &= ~ICH9_SMB_HOSTC_SSRESET;
+ }
}
}
@@ -80,7 +86,7 @@ static void ich9_smbus_realize(PCIDevice *d, Error **errp)
pci_set_byte(d->config + ICH9_SMB_HOSTC, 0);
/* TODO bar0, bar1: 64bit BAR support*/
- pm_smbus_init(&d->qdev, &s->smb);
+ pm_smbus_init(&d->qdev, &s->smb, false);
pci_register_bar(d, ICH9_SMB_SMB_BASE_BAR, PCI_BASE_ADDRESS_SPACE_IO,
&s->smb.io);
}
@@ -105,11 +111,25 @@ static void ich9_smb_class_init(ObjectClass *klass, void *data)
dc->user_creatable = false;
}
+static void ich9_smb_set_irq(PMSMBus *pmsmb, bool enabled)
+{
+ ICH9SMBState *s = pmsmb->opaque;
+
+ if (enabled == s->irq_enabled) {
+ return;
+ }
+
+ s->irq_enabled = enabled;
+ pci_set_irq(&s->dev, enabled);
+}
+
I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base)
{
PCIDevice *d =
pci_create_simple_multifunction(bus, devfn, true, TYPE_ICH9_SMB_DEVICE);
ICH9SMBState *s = ICH9_SMB_DEVICE(d);
+ s->smb.set_irq = ich9_smb_set_irq;
+ s->smb.opaque = s;
return s->smb.smbus;
}
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 83a444472b..03148450c8 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1679,7 +1679,9 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
const PCMachineState *pcms = PC_MACHINE(hotplug_dev);
+ const PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
+ const uint64_t legacy_align = TARGET_PAGE_SIZE;
/*
* When -no-acpi is used with Q35 machine type, no ACPI is built,
@@ -1696,6 +1698,9 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
error_setg(errp, "nvdimm is not enabled: missing 'nvdimm' in '-M'");
return;
}
+
+ pc_dimm_pre_plug(dev, MACHINE(hotplug_dev),
+ pcmc->enforce_aligned_dimm ? NULL : &legacy_align, errp);
}
static void pc_memory_plug(HotplugHandler *hotplug_dev,
@@ -1704,18 +1709,9 @@ static void pc_memory_plug(HotplugHandler *hotplug_dev,
HotplugHandlerClass *hhc;
Error *local_err = NULL;
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
- PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
- PCDIMMDevice *dimm = PC_DIMM(dev);
- PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
- MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort);
- uint64_t align = TARGET_PAGE_SIZE;
bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
- if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) {
- align = memory_region_get_alignment(mr);
- }
-
- pc_dimm_plug(dev, MACHINE(pcms), align, &local_err);
+ pc_dimm_plug(dev, MACHINE(pcms), &local_err);
if (local_err) {
goto out;
}
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index 6fda52b86c..97ffdd820f 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -650,31 +650,17 @@ static void apic_timer(void *opaque)
apic_timer_update(s, s->next_time);
}
-static uint32_t apic_mem_readb(void *opaque, hwaddr addr)
-{
- return 0;
-}
-
-static uint32_t apic_mem_readw(void *opaque, hwaddr addr)
-{
- return 0;
-}
-
-static void apic_mem_writeb(void *opaque, hwaddr addr, uint32_t val)
-{
-}
-
-static void apic_mem_writew(void *opaque, hwaddr addr, uint32_t val)
-{
-}
-
-static uint32_t apic_mem_readl(void *opaque, hwaddr addr)
+static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size)
{
DeviceState *dev;
APICCommonState *s;
uint32_t val;
int index;
+ if (size < 4) {
+ return 0;
+ }
+
dev = cpu_get_current_apic();
if (!dev) {
return 0;
@@ -765,11 +751,17 @@ static void apic_send_msi(MSIMessage *msi)
apic_deliver_irq(dest, dest_mode, delivery, vector, trigger_mode);
}
-static void apic_mem_writel(void *opaque, hwaddr addr, uint32_t val)
+static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
{
DeviceState *dev;
APICCommonState *s;
int index = (addr >> 4) & 0xff;
+
+ if (size < 4) {
+ return;
+ }
+
if (addr > 0xfff || !index) {
/* MSI and MMIO APIC are at the same memory location,
* but actually not on the global bus: MSI is on PCI bus
@@ -880,10 +872,12 @@ static void apic_post_load(APICCommonState *s)
}
static const MemoryRegionOps apic_io_ops = {
- .old_mmio = {
- .read = { apic_mem_readb, apic_mem_readw, apic_mem_readl, },
- .write = { apic_mem_writeb, apic_mem_writew, apic_mem_writel, },
- },
+ .read = apic_mem_read,
+ .write = apic_mem_write,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 4,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index c1b35fc1ee..542b4b93ea 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -2084,7 +2084,7 @@ static void arm_gic_realize(DeviceState *dev, Error **errp)
for (i = 0; i < s->num_cpu; i++) {
memory_region_init_io(&s->vifaceiomem[i + 1], OBJECT(s),
&gic_viface_ops, &s->backref[i],
- "gic_viface", 0x1000);
+ "gic_viface", 0x200);
sysbus_init_mmio(sbd, &s->vifaceiomem[i + 1]);
}
}
diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c
index e946030e84..8bbb1fa785 100644
--- a/hw/ipmi/isa_ipmi_bt.c
+++ b/hw/ipmi/isa_ipmi_bt.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
+#include "qemu/log.h"
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/ipmi/ipmi.h"
@@ -450,22 +451,63 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base);
}
-static const VMStateDescription vmstate_ISAIPMIBTDevice = {
- .name = TYPE_IPMI_INTERFACE,
+static int ipmi_bt_vmstate_post_load(void *opaque, int version)
+{
+ IPMIBT *ib = opaque;
+
+ /* Make sure all the values are sane. */
+ if (ib->outpos >= MAX_IPMI_MSG_SIZE || ib->outlen >= MAX_IPMI_MSG_SIZE ||
+ ib->outpos >= ib->outlen) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "ipmi:bt: vmstate transfer received bad out values: %d %d\n",
+ ib->outpos, ib->outlen);
+ ib->outpos = 0;
+ ib->outlen = 0;
+ }
+
+ if (ib->inlen >= MAX_IPMI_MSG_SIZE) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "ipmi:bt: vmstate transfer received bad in value: %d\n",
+ ib->inlen);
+ ib->inlen = 0;
+ }
+
+ return 0;
+}
+
+const VMStateDescription vmstate_IPMIBT = {
+ .name = TYPE_IPMI_INTERFACE_PREFIX "bt",
.version_id = 1,
.minimum_version_id = 1,
+ .post_load = ipmi_bt_vmstate_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(obf_irq_set, IPMIBT),
+ VMSTATE_BOOL(atn_irq_set, IPMIBT),
+ VMSTATE_BOOL(irqs_enabled, IPMIBT),
+ VMSTATE_UINT32(outpos, IPMIBT),
+ VMSTATE_UINT32(outlen, IPMIBT),
+ VMSTATE_UINT8_ARRAY(outmsg, IPMIBT, MAX_IPMI_MSG_SIZE),
+ VMSTATE_UINT32(inlen, IPMIBT),
+ VMSTATE_UINT8_ARRAY(inmsg, IPMIBT, MAX_IPMI_MSG_SIZE),
+ VMSTATE_UINT8(control_reg, IPMIBT),
+ VMSTATE_UINT8(mask_reg, IPMIBT),
+ VMSTATE_UINT8(waiting_rsp, IPMIBT),
+ VMSTATE_UINT8(waiting_seq, IPMIBT),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_ISAIPMIBTDevice = {
+ .name = TYPE_IPMI_INTERFACE_PREFIX "isa-bt",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ /*
+ * Version 1 had messed up the array transfer, it's not even usable
+ * because it used VMSTATE_VBUFFER_UINT32, but it did not transfer
+ * the buffer length, so random things would happen.
+ */
.fields = (VMStateField[]) {
- VMSTATE_BOOL(bt.obf_irq_set, ISAIPMIBTDevice),
- VMSTATE_BOOL(bt.atn_irq_set, ISAIPMIBTDevice),
- VMSTATE_BOOL(bt.use_irq, ISAIPMIBTDevice),
- VMSTATE_BOOL(bt.irqs_enabled, ISAIPMIBTDevice),
- VMSTATE_UINT32(bt.outpos, ISAIPMIBTDevice),
- VMSTATE_VBUFFER_UINT32(bt.outmsg, ISAIPMIBTDevice, 1, NULL, bt.outlen),
- VMSTATE_VBUFFER_UINT32(bt.inmsg, ISAIPMIBTDevice, 1, NULL, bt.inlen),
- VMSTATE_UINT8(bt.control_reg, ISAIPMIBTDevice),
- VMSTATE_UINT8(bt.mask_reg, ISAIPMIBTDevice),
- VMSTATE_UINT8(bt.waiting_rsp, ISAIPMIBTDevice),
- VMSTATE_UINT8(bt.waiting_seq, ISAIPMIBTDevice),
+ VMSTATE_STRUCT(bt, ISAIPMIBTDevice, 1, vmstate_IPMIBT, IPMIBT),
VMSTATE_END_OF_LIST()
}
};
diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c
index cff1946232..7302f6d74b 100644
--- a/hw/isa/vt82c686.c
+++ b/hw/isa/vt82c686.c
@@ -370,7 +370,7 @@ static void vt82c686b_pm_realize(PCIDevice *dev, Error **errp)
pci_conf[0x90] = s->smb_io_base | 1;
pci_conf[0x91] = s->smb_io_base >> 8;
pci_conf[0xd2] = 0x90;
- pm_smbus_init(&s->dev.qdev, &s->smb);
+ pm_smbus_init(&s->dev.qdev, &s->smb, false);
memory_region_add_subregion(get_system_io(), s->smb_io_base, &s->smb.io);
apm_init(dev, &s->apm, NULL, s);
diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c
index 65843bc52a..fb6bcaedc4 100644
--- a/hw/mem/pc-dimm.c
+++ b/hw/mem/pc-dimm.c
@@ -29,57 +29,60 @@
static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp);
-void pc_dimm_plug(DeviceState *dev, MachineState *machine, uint64_t align,
- Error **errp)
+void pc_dimm_pre_plug(DeviceState *dev, MachineState *machine,
+ const uint64_t *legacy_align, Error **errp)
{
- int slot;
PCDIMMDevice *dimm = PC_DIMM(dev);
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
- MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm,
- &error_abort);
- MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort);
Error *local_err = NULL;
- uint64_t addr;
+ MemoryRegion *mr;
+ uint64_t addr, align;
+ int slot;
- addr = object_property_get_uint(OBJECT(dimm),
- PC_DIMM_ADDR_PROP, &local_err);
+ slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
+ &error_abort);
+ slot = pc_dimm_get_free_slot(slot == PC_DIMM_UNASSIGNED_SLOT ? NULL : &slot,
+ machine->ram_slots, &local_err);
if (local_err) {
goto out;
}
+ object_property_set_int(OBJECT(dev), slot, PC_DIMM_SLOT_PROP, &error_abort);
+ trace_mhp_pc_dimm_assigned_slot(slot);
- addr = memory_device_get_free_addr(machine, !addr ? NULL : &addr, align,
- memory_region_size(mr), &local_err);
+ mr = ddc->get_memory_region(dimm, &local_err);
if (local_err) {
goto out;
}
- object_property_set_uint(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, &local_err);
+ align = legacy_align ? *legacy_align : memory_region_get_alignment(mr);
+ addr = object_property_get_uint(OBJECT(dev), PC_DIMM_ADDR_PROP,
+ &error_abort);
+ addr = memory_device_get_free_addr(machine, !addr ? NULL : &addr, align,
+ memory_region_size(mr), &local_err);
if (local_err) {
goto out;
}
trace_mhp_pc_dimm_assigned_address(addr);
+ object_property_set_uint(OBJECT(dev), addr, PC_DIMM_ADDR_PROP,
+ &error_abort);
+out:
+ error_propagate(errp, local_err);
+}
- slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, &local_err);
- if (local_err) {
- goto out;
- }
+void pc_dimm_plug(DeviceState *dev, MachineState *machine, Error **errp)
+{
+ PCDIMMDevice *dimm = PC_DIMM(dev);
+ PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
+ MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm,
+ &error_abort);
+ MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort);
+ uint64_t addr;
- slot = pc_dimm_get_free_slot(slot == PC_DIMM_UNASSIGNED_SLOT ? NULL : &slot,
- machine->ram_slots, &local_err);
- if (local_err) {
- goto out;
- }
- object_property_set_int(OBJECT(dev), slot, PC_DIMM_SLOT_PROP, &local_err);
- if (local_err) {
- goto out;
- }
- trace_mhp_pc_dimm_assigned_slot(slot);
+ addr = object_property_get_uint(OBJECT(dev), PC_DIMM_ADDR_PROP,
+ &error_abort);
memory_device_plug_region(machine, mr, addr);
vmstate_register_ram(vmstate_mr, dev);
-
-out:
- error_propagate(errp, local_err);
}
void pc_dimm_unplug(DeviceState *dev, MachineState *machine)
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 22714b0851..6d50b03cfd 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -64,8 +64,11 @@ obj-$(CONFIG_MPS2_FPGAIO) += mps2-fpgaio.o
obj-$(CONFIG_MPS2_SCC) += mps2-scc.o
obj-$(CONFIG_TZ_MPC) += tz-mpc.o
+obj-$(CONFIG_TZ_MSC) += tz-msc.o
obj-$(CONFIG_TZ_PPC) += tz-ppc.o
obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o
+obj-$(CONFIG_IOTKIT_SYSCTL) += iotkit-sysctl.o
+obj-$(CONFIG_IOTKIT_SYSINFO) += iotkit-sysinfo.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c
index 70eaafd325..145427ae0f 100644
--- a/hw/misc/bcm2835_property.c
+++ b/hw/misc/bcm2835_property.c
@@ -21,11 +21,14 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
uint32_t tmp;
int n;
uint32_t offset, length, color;
- uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha;
- uint32_t tmp_xres, tmp_yres, tmp_xoffset, tmp_yoffset;
- uint32_t tmp_bpp, tmp_pixo, tmp_alpha;
- uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL,
- *newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL;
+
+ /*
+ * Copy the current state of the framebuffer config; we will update
+ * this copy as we process tags and then ask the framebuffer to use
+ * it at the end.
+ */
+ BCM2835FBConfig fbconfig = s->fbdev->config;
+ bool fbconfig_updated = false;
value &= ~0xf;
@@ -141,12 +144,9 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
/* Frame buffer */
case 0x00040001: /* Allocate buffer */
- stl_le_phys(&s->dma_as, value + 12, s->fbdev->base);
- tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres;
- tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres;
- tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp;
+ stl_le_phys(&s->dma_as, value + 12, fbconfig.base);
stl_le_phys(&s->dma_as, value + 16,
- tmp_xres * tmp_yres * tmp_bpp / 8);
+ bcm2835_fb_get_size(&fbconfig));
resplen = 8;
break;
case 0x00048001: /* Release buffer */
@@ -155,86 +155,85 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
case 0x00040002: /* Blank screen */
resplen = 4;
break;
- case 0x00040003: /* Get display width/height */
- case 0x00040004:
- tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres;
- tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres;
- stl_le_phys(&s->dma_as, value + 12, tmp_xres);
- stl_le_phys(&s->dma_as, value + 16, tmp_yres);
+ case 0x00044003: /* Test physical display width/height */
+ case 0x00044004: /* Test virtual display width/height */
resplen = 8;
break;
- case 0x00044003: /* Test display width/height */
- case 0x00044004:
+ case 0x00048003: /* Set physical display width/height */
+ fbconfig.xres = ldl_le_phys(&s->dma_as, value + 12);
+ fbconfig.yres = ldl_le_phys(&s->dma_as, value + 16);
+ bcm2835_fb_validate_config(&fbconfig);
+ fbconfig_updated = true;
+ /* fall through */
+ case 0x00040003: /* Get physical display width/height */
+ stl_le_phys(&s->dma_as, value + 12, fbconfig.xres);
+ stl_le_phys(&s->dma_as, value + 16, fbconfig.yres);
resplen = 8;
break;
- case 0x00048003: /* Set display width/height */
- case 0x00048004:
- xres = ldl_le_phys(&s->dma_as, value + 12);
- newxres = &xres;
- yres = ldl_le_phys(&s->dma_as, value + 16);
- newyres = &yres;
+ case 0x00048004: /* Set virtual display width/height */
+ fbconfig.xres_virtual = ldl_le_phys(&s->dma_as, value + 12);
+ fbconfig.yres_virtual = ldl_le_phys(&s->dma_as, value + 16);
+ bcm2835_fb_validate_config(&fbconfig);
+ fbconfig_updated = true;
+ /* fall through */
+ case 0x00040004: /* Get virtual display width/height */
+ stl_le_phys(&s->dma_as, value + 12, fbconfig.xres_virtual);
+ stl_le_phys(&s->dma_as, value + 16, fbconfig.yres_virtual);
resplen = 8;
break;
- case 0x00040005: /* Get depth */
- tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp;
- stl_le_phys(&s->dma_as, value + 12, tmp_bpp);
- resplen = 4;
- break;
case 0x00044005: /* Test depth */
resplen = 4;
break;
case 0x00048005: /* Set depth */
- bpp = ldl_le_phys(&s->dma_as, value + 12);
- newbpp = &bpp;
- resplen = 4;
- break;
- case 0x00040006: /* Get pixel order */
- tmp_pixo = newpixo != NULL ? *newpixo : s->fbdev->pixo;
- stl_le_phys(&s->dma_as, value + 12, tmp_pixo);
+ fbconfig.bpp = ldl_le_phys(&s->dma_as, value + 12);
+ bcm2835_fb_validate_config(&fbconfig);
+ fbconfig_updated = true;
+ /* fall through */
+ case 0x00040005: /* Get depth */
+ stl_le_phys(&s->dma_as, value + 12, fbconfig.bpp);
resplen = 4;
break;
case 0x00044006: /* Test pixel order */
resplen = 4;
break;
case 0x00048006: /* Set pixel order */
- pixo = ldl_le_phys(&s->dma_as, value + 12);
- newpixo = &pixo;
- resplen = 4;
- break;
- case 0x00040007: /* Get alpha */
- tmp_alpha = newalpha != NULL ? *newalpha : s->fbdev->alpha;
- stl_le_phys(&s->dma_as, value + 12, tmp_alpha);
+ fbconfig.pixo = ldl_le_phys(&s->dma_as, value + 12);
+ bcm2835_fb_validate_config(&fbconfig);
+ fbconfig_updated = true;
+ /* fall through */
+ case 0x00040006: /* Get pixel order */
+ stl_le_phys(&s->dma_as, value + 12, fbconfig.pixo);
resplen = 4;
break;
case 0x00044007: /* Test pixel alpha */
resplen = 4;
break;
case 0x00048007: /* Set alpha */
- alpha = ldl_le_phys(&s->dma_as, value + 12);
- newalpha = &alpha;
+ fbconfig.alpha = ldl_le_phys(&s->dma_as, value + 12);
+ bcm2835_fb_validate_config(&fbconfig);
+ fbconfig_updated = true;
+ /* fall through */
+ case 0x00040007: /* Get alpha */
+ stl_le_phys(&s->dma_as, value + 12, fbconfig.alpha);
resplen = 4;
break;
case 0x00040008: /* Get pitch */
- tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres;
- tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp;
- stl_le_phys(&s->dma_as, value + 12, tmp_xres * tmp_bpp / 8);
+ stl_le_phys(&s->dma_as, value + 12,
+ bcm2835_fb_get_pitch(&fbconfig));
resplen = 4;
break;
- case 0x00040009: /* Get virtual offset */
- tmp_xoffset = newxoffset != NULL ? *newxoffset : s->fbdev->xoffset;
- tmp_yoffset = newyoffset != NULL ? *newyoffset : s->fbdev->yoffset;
- stl_le_phys(&s->dma_as, value + 12, tmp_xoffset);
- stl_le_phys(&s->dma_as, value + 16, tmp_yoffset);
- resplen = 8;
- break;
case 0x00044009: /* Test virtual offset */
resplen = 8;
break;
case 0x00048009: /* Set virtual offset */
- xoffset = ldl_le_phys(&s->dma_as, value + 12);
- newxoffset = &xoffset;
- yoffset = ldl_le_phys(&s->dma_as, value + 16);
- newyoffset = &yoffset;
+ fbconfig.xoffset = ldl_le_phys(&s->dma_as, value + 12);
+ fbconfig.yoffset = ldl_le_phys(&s->dma_as, value + 16);
+ bcm2835_fb_validate_config(&fbconfig);
+ fbconfig_updated = true;
+ /* fall through */
+ case 0x00040009: /* Get virtual offset */
+ stl_le_phys(&s->dma_as, value + 12, fbconfig.xoffset);
+ stl_le_phys(&s->dma_as, value + 16, fbconfig.yoffset);
resplen = 8;
break;
case 0x0004000a: /* Get/Test/Set overscan */
@@ -285,10 +284,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
}
/* Reconfigure framebuffer if required */
- if (newxres || newyres || newxoffset || newyoffset || newbpp || newpixo
- || newalpha) {
- bcm2835_fb_reconfigure(s->fbdev, newxres, newyres, newxoffset,
- newyoffset, newbpp, newpixo, newalpha);
+ if (fbconfig_updated) {
+ bcm2835_fb_reconfigure(s->fbdev, &fbconfig);
}
/* Buffer response code */
diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c
index de4fd8e36d..2222b3e147 100644
--- a/hw/misc/iotkit-secctl.c
+++ b/hw/misc/iotkit-secctl.c
@@ -190,12 +190,13 @@ static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr,
r = s->apbexp[offset_to_ppc_idx(offset)].sp;
break;
case A_SECMSCINTSTAT:
+ r = s->secmscintstat;
+ break;
case A_SECMSCINTEN:
+ r = s->secmscinten;
+ break;
case A_NSMSCEXP:
- qemu_log_mask(LOG_UNIMP,
- "IoTKit SecCtl S block read: "
- "unimplemented offset 0x%x\n", offset);
- r = 0;
+ r = s->nsmscexp;
break;
case A_PID4:
case A_PID5:
@@ -291,6 +292,23 @@ static void iotkit_secctl_ppc_update_irq_enable(IoTKitSecCtlPPC *ppc)
qemu_set_irq(ppc->irq_enable, extract32(value, ppc->irq_bit_offset, 1));
}
+static void iotkit_secctl_update_mscexp_irqs(qemu_irq *msc_irqs, uint32_t value)
+{
+ int i;
+
+ for (i = 0; i < IOTS_NUM_EXP_MSC; i++) {
+ qemu_set_irq(msc_irqs[i], extract32(value, i + 16, 1));
+ }
+}
+
+static void iotkit_secctl_update_msc_irq(IoTKitSecCtl *s)
+{
+ /* Update the combined MSC IRQ, based on S_MSCEXP_STATUS and S_MSCEXP_EN */
+ bool level = s->secmscintstat & s->secmscinten;
+
+ qemu_set_irq(s->msc_irq, level);
+}
+
static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr,
uint64_t value,
unsigned size, MemTxAttrs attrs)
@@ -370,10 +388,15 @@ static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr,
iotkit_secctl_ppc_sp_write(ppc, value);
break;
case A_SECMSCINTCLR:
+ iotkit_secctl_update_mscexp_irqs(s->mscexp_clear, value);
+ break;
case A_SECMSCINTEN:
- qemu_log_mask(LOG_UNIMP,
- "IoTKit SecCtl S block write: "
- "unimplemented offset 0x%x\n", offset);
+ s->secmscinten = value;
+ iotkit_secctl_update_msc_irq(s);
+ break;
+ case A_NSMSCEXP:
+ s->nsmscexp = value;
+ iotkit_secctl_update_mscexp_irqs(s->mscexp_ns, value);
break;
case A_SECMPCINTSTATUS:
case A_SECPPCINTSTAT:
@@ -381,7 +404,6 @@ static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr,
case A_BRGINTSTAT:
case A_AHBNSPPC0:
case A_AHBSPPPC0:
- case A_NSMSCEXP:
case A_PID4:
case A_PID5:
case A_PID6:
@@ -588,6 +610,14 @@ static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level)
s->mpcintstatus = deposit32(s->mpcintstatus, n + 16, 1, !!level);
}
+static void iotkit_secctl_mscexp_status(void *opaque, int n, int level)
+{
+ IoTKitSecCtl *s = IOTKIT_SECCTL(opaque);
+
+ s->secmscintstat = deposit32(s->secmscintstat, n + 16, 1, !!level);
+ iotkit_secctl_update_msc_irq(s);
+}
+
static void iotkit_secctl_ppc_irqstatus(void *opaque, int n, int level)
{
IoTKitSecCtlPPC *ppc = opaque;
@@ -660,6 +690,14 @@ static void iotkit_secctl_init(Object *obj)
qdev_init_gpio_in_named(dev, iotkit_secctl_mpcexp_status,
"mpcexp_status", IOTS_NUM_EXP_MPC);
+ qdev_init_gpio_in_named(dev, iotkit_secctl_mscexp_status,
+ "mscexp_status", IOTS_NUM_EXP_MSC);
+ qdev_init_gpio_out_named(dev, s->mscexp_clear, "mscexp_clear",
+ IOTS_NUM_EXP_MSC);
+ qdev_init_gpio_out_named(dev, s->mscexp_ns, "mscexp_ns",
+ IOTS_NUM_EXP_MSC);
+ qdev_init_gpio_out_named(dev, &s->msc_irq, "msc_irq", 1);
+
memory_region_init_io(&s->s_regs, obj, &iotkit_secctl_s_ops,
s, "iotkit-secctl-s-regs", 0x1000);
memory_region_init_io(&s->ns_regs, obj, &iotkit_secctl_ns_ops,
@@ -690,6 +728,24 @@ static const VMStateDescription iotkit_secctl_mpcintstatus_vmstate = {
}
};
+static bool needed_always(void *opaque)
+{
+ return true;
+}
+
+static const VMStateDescription iotkit_secctl_msc_vmstate = {
+ .name = "iotkit-secctl/msc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = needed_always,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(secmscintstat, IoTKitSecCtl),
+ VMSTATE_UINT32(secmscinten, IoTKitSecCtl),
+ VMSTATE_UINT32(nsmscexp, IoTKitSecCtl),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription iotkit_secctl_vmstate = {
.name = "iotkit-secctl",
.version_id = 1,
@@ -710,6 +766,7 @@ static const VMStateDescription iotkit_secctl_vmstate = {
},
.subsections = (const VMStateDescription*[]) {
&iotkit_secctl_mpcintstatus_vmstate,
+ &iotkit_secctl_msc_vmstate,
NULL
},
};
diff --git a/hw/misc/iotkit-sysctl.c b/hw/misc/iotkit-sysctl.c
new file mode 100644
index 0000000000..a21d8bd678
--- /dev/null
+++ b/hw/misc/iotkit-sysctl.c
@@ -0,0 +1,261 @@
+/*
+ * ARM IoTKit system control element
+ *
+ * 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 "system control element" which is part of the
+ * Arm IoTKit and documented in
+ * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html
+ * Specifically, it implements the "system control register" blocks.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "qapi/error.h"
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/misc/iotkit-sysctl.h"
+
+REG32(SECDBGSTAT, 0x0)
+REG32(SECDBGSET, 0x4)
+REG32(SECDBGCLR, 0x8)
+REG32(RESET_SYNDROME, 0x100)
+REG32(RESET_MASK, 0x104)
+REG32(SWRESET, 0x108)
+ FIELD(SWRESET, SWRESETREQ, 9, 1)
+REG32(GRETREG, 0x10c)
+REG32(INITSVRTOR0, 0x110)
+REG32(CPUWAIT, 0x118)
+REG32(BUSWAIT, 0x11c)
+REG32(WICCTRL, 0x120)
+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 sysctl_id[] = {
+ 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
+ 0x54, 0xb8, 0x0b, 0x00, /* PID0..PID3 */
+ 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
+};
+
+static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ IoTKitSysCtl *s = IOTKIT_SYSCTL(opaque);
+ uint64_t r;
+
+ switch (offset) {
+ case A_SECDBGSTAT:
+ r = s->secure_debug;
+ break;
+ case A_RESET_SYNDROME:
+ r = s->reset_syndrome;
+ break;
+ case A_RESET_MASK:
+ r = s->reset_mask;
+ break;
+ case A_GRETREG:
+ r = s->gretreg;
+ break;
+ case A_INITSVRTOR0:
+ r = s->initsvrtor0;
+ break;
+ case A_CPUWAIT:
+ r = s->cpuwait;
+ break;
+ case A_BUSWAIT:
+ /* In IoTKit BUSWAIT is reserved, R/O, zero */
+ r = 0;
+ break;
+ case A_WICCTRL:
+ r = s->wicctrl;
+ break;
+ case A_PID4 ... A_CID3:
+ r = sysctl_id[(offset - A_PID4) / 4];
+ break;
+ case A_SECDBGSET:
+ case A_SECDBGCLR:
+ case A_SWRESET:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "IoTKit SysCtl read: read of WO offset %x\n",
+ (int)offset);
+ r = 0;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "IoTKit SysCtl read: bad offset %x\n", (int)offset);
+ r = 0;
+ break;
+ }
+ trace_iotkit_sysctl_read(offset, r, size);
+ return r;
+}
+
+static void iotkit_sysctl_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ IoTKitSysCtl *s = IOTKIT_SYSCTL(opaque);
+
+ trace_iotkit_sysctl_write(offset, value, size);
+
+ /*
+ * Most of the state here has to do with control of reset and
+ * similar kinds of power up -- for instance the guest can ask
+ * what the reason for the last reset was, or forbid reset for
+ * some causes (like the non-secure watchdog). Most of this is
+ * not relevant to QEMU, which doesn't really model anything other
+ * than a full power-on reset.
+ * We just model the registers as reads-as-written.
+ */
+
+ switch (offset) {
+ case A_RESET_SYNDROME:
+ qemu_log_mask(LOG_UNIMP,
+ "IoTKit SysCtl RESET_SYNDROME unimplemented\n");
+ s->reset_syndrome = value;
+ break;
+ case A_RESET_MASK:
+ qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl RESET_MASK unimplemented\n");
+ s->reset_mask = value;
+ break;
+ case A_GRETREG:
+ /*
+ * General retention register, which is only reset by a power-on
+ * reset. Technically this implementation is complete, since
+ * QEMU only supports power-on resets...
+ */
+ s->gretreg = value;
+ break;
+ case A_INITSVRTOR0:
+ qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl INITSVRTOR0 unimplemented\n");
+ s->initsvrtor0 = value;
+ break;
+ case A_CPUWAIT:
+ qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl CPUWAIT unimplemented\n");
+ s->cpuwait = value;
+ break;
+ case A_WICCTRL:
+ qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl WICCTRL unimplemented\n");
+ s->wicctrl = value;
+ break;
+ case A_SECDBGSET:
+ /* write-1-to-set */
+ qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl SECDBGSET unimplemented\n");
+ s->secure_debug |= value;
+ break;
+ case A_SECDBGCLR:
+ /* write-1-to-clear */
+ s->secure_debug &= ~value;
+ break;
+ case A_SWRESET:
+ /* One w/o bit to request a reset; all other bits reserved */
+ if (value & R_SWRESET_SWRESETREQ_MASK) {
+ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ }
+ break;
+ case A_BUSWAIT: /* In IoTKit BUSWAIT is reserved, R/O, zero */
+ case A_SECDBGSTAT:
+ case A_PID4 ... A_CID3:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "IoTKit SysCtl write: write of RO offset %x\n",
+ (int)offset);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "IoTKit SysCtl write: bad offset %x\n", (int)offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps iotkit_sysctl_ops = {
+ .read = iotkit_sysctl_read,
+ .write = iotkit_sysctl_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 iotkit_sysctl_reset(DeviceState *dev)
+{
+ IoTKitSysCtl *s = IOTKIT_SYSCTL(dev);
+
+ trace_iotkit_sysctl_reset();
+ s->secure_debug = 0;
+ s->reset_syndrome = 1;
+ s->reset_mask = 0;
+ s->gretreg = 0;
+ s->initsvrtor0 = 0x10000000;
+ s->cpuwait = 0;
+ s->wicctrl = 0;
+}
+
+static void iotkit_sysctl_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ IoTKitSysCtl *s = IOTKIT_SYSCTL(obj);
+
+ memory_region_init_io(&s->iomem, obj, &iotkit_sysctl_ops,
+ s, "iotkit-sysctl", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription iotkit_sysctl_vmstate = {
+ .name = "iotkit-sysctl",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(secure_debug, IoTKitSysCtl),
+ VMSTATE_UINT32(reset_syndrome, IoTKitSysCtl),
+ VMSTATE_UINT32(reset_mask, IoTKitSysCtl),
+ VMSTATE_UINT32(gretreg, IoTKitSysCtl),
+ VMSTATE_UINT32(initsvrtor0, IoTKitSysCtl),
+ VMSTATE_UINT32(cpuwait, IoTKitSysCtl),
+ VMSTATE_UINT32(wicctrl, IoTKitSysCtl),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void iotkit_sysctl_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &iotkit_sysctl_vmstate;
+ dc->reset = iotkit_sysctl_reset;
+}
+
+static const TypeInfo iotkit_sysctl_info = {
+ .name = TYPE_IOTKIT_SYSCTL,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IoTKitSysCtl),
+ .instance_init = iotkit_sysctl_init,
+ .class_init = iotkit_sysctl_class_init,
+};
+
+static void iotkit_sysctl_register_types(void)
+{
+ type_register_static(&iotkit_sysctl_info);
+}
+
+type_init(iotkit_sysctl_register_types);
diff --git a/hw/misc/iotkit-sysinfo.c b/hw/misc/iotkit-sysinfo.c
new file mode 100644
index 0000000000..78955bc45f
--- /dev/null
+++ b/hw/misc/iotkit-sysinfo.c
@@ -0,0 +1,128 @@
+/*
+ * ARM IoTKit system information block
+ *
+ * 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 "system information block" which is part of the
+ * Arm IoTKit and documented in
+ * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html
+ * It consists of 2 read-only version/config registers, plus the
+ * usual ID registers.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "qapi/error.h"
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/misc/iotkit-sysinfo.h"
+
+REG32(SYS_VERSION, 0x0)
+REG32(SYS_CONFIG, 0x4)
+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 sysinfo_id[] = {
+ 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
+ 0x58, 0xb8, 0x0b, 0x00, /* PID0..PID3 */
+ 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
+};
+
+static uint64_t iotkit_sysinfo_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ uint64_t r;
+
+ switch (offset) {
+ case A_SYS_VERSION:
+ r = 0x41743;
+ break;
+
+ case A_SYS_CONFIG:
+ r = 0x31;
+ break;
+ case A_PID4 ... A_CID3:
+ r = sysinfo_id[(offset - A_PID4) / 4];
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "IoTKit SysInfo read: bad offset %x\n", (int)offset);
+ r = 0;
+ break;
+ }
+ trace_iotkit_sysinfo_read(offset, r, size);
+ return r;
+}
+
+static void iotkit_sysinfo_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ trace_iotkit_sysinfo_write(offset, value, size);
+
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "IoTKit SysInfo: write to RO offset 0x%x\n", (int)offset);
+}
+
+static const MemoryRegionOps iotkit_sysinfo_ops = {
+ .read = iotkit_sysinfo_read,
+ .write = iotkit_sysinfo_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 iotkit_sysinfo_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ IoTKitSysInfo *s = IOTKIT_SYSINFO(obj);
+
+ memory_region_init_io(&s->iomem, obj, &iotkit_sysinfo_ops,
+ s, "iotkit-sysinfo", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void iotkit_sysinfo_class_init(ObjectClass *klass, void *data)
+{
+ /*
+ * This device has no guest-modifiable state and so it
+ * does not need a reset function or VMState.
+ */
+}
+
+static const TypeInfo iotkit_sysinfo_info = {
+ .name = TYPE_IOTKIT_SYSINFO,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IoTKitSysInfo),
+ .instance_init = iotkit_sysinfo_init,
+ .class_init = iotkit_sysinfo_class_init,
+};
+
+static void iotkit_sysinfo_register_types(void)
+{
+ type_register_static(&iotkit_sysinfo_info);
+}
+
+type_init(iotkit_sysinfo_register_types);
diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c
index 7394a057d8..5cf10ebd66 100644
--- a/hw/misc/mps2-fpgaio.c
+++ b/hw/misc/mps2-fpgaio.c
@@ -22,6 +22,7 @@
#include "hw/sysbus.h"
#include "hw/registerfields.h"
#include "hw/misc/mps2-fpgaio.h"
+#include "qemu/timer.h"
REG32(LED0, 0)
REG32(BUTTON, 8)
@@ -32,10 +33,92 @@ REG32(PRESCALE, 0x1c)
REG32(PSCNTR, 0x20)
REG32(MISC, 0x4c)
+static uint32_t counter_from_tickoff(int64_t now, int64_t tick_offset, int frq)
+{
+ return muldiv64(now - tick_offset, frq, NANOSECONDS_PER_SECOND);
+}
+
+static int64_t tickoff_from_counter(int64_t now, uint32_t count, int frq)
+{
+ return now - muldiv64(count, NANOSECONDS_PER_SECOND, frq);
+}
+
+static void resync_counter(MPS2FPGAIO *s)
+{
+ /*
+ * Update s->counter and s->pscntr to their true current values
+ * by calculating how many times PSCNTR has ticked since the
+ * last time we did a resync.
+ */
+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ int64_t elapsed = now - s->pscntr_sync_ticks;
+
+ /*
+ * Round elapsed down to a whole number of PSCNTR ticks, so we don't
+ * lose time if we do multiple resyncs in a single tick.
+ */
+ uint64_t ticks = muldiv64(elapsed, s->prescale_clk, NANOSECONDS_PER_SECOND);
+
+ /*
+ * Work out what PSCNTR and COUNTER have moved to. We assume that
+ * PSCNTR reloads from PRESCALE one tick-period after it hits zero,
+ * and that COUNTER increments at the same moment.
+ */
+ if (ticks == 0) {
+ /* We haven't ticked since the last time we were asked */
+ return;
+ } else if (ticks < s->pscntr) {
+ /* We haven't yet reached zero, just reduce the PSCNTR */
+ s->pscntr -= ticks;
+ } else {
+ if (s->prescale == 0) {
+ /*
+ * If the reload value is zero then the PSCNTR will stick
+ * at zero once it reaches it, and so we will increment
+ * COUNTER every tick after that.
+ */
+ s->counter += ticks - s->pscntr;
+ s->pscntr = 0;
+ } else {
+ /*
+ * This is the complicated bit. This ASCII art diagram gives an
+ * example with PRESCALE==5 PSCNTR==7:
+ *
+ * ticks 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
+ * PSCNTR 7 6 5 4 3 2 1 0 5 4 3 2 1 0 5
+ * cinc 1 2
+ * y 0 1 2 3 4 5 6 7 8 9 10 11 12
+ * x 0 1 2 3 4 5 0 1 2 3 4 5 0
+ *
+ * where x = y % (s->prescale + 1)
+ * and so PSCNTR = s->prescale - x
+ * and COUNTER is incremented by y / (s->prescale + 1)
+ *
+ * The case where PSCNTR < PRESCALE works out the same,
+ * though we must be careful to calculate y as 64-bit unsigned
+ * for all parts of the expression.
+ * y < 0 is not possible because that implies ticks < s->pscntr.
+ */
+ uint64_t y = ticks - s->pscntr + s->prescale;
+ s->pscntr = s->prescale - (y % (s->prescale + 1));
+ s->counter += y / (s->prescale + 1);
+ }
+ }
+
+ /*
+ * Only advance the sync time to the timestamp of the last PSCNTR tick,
+ * not all the way to 'now', so we don't lose time if we do multiple
+ * resyncs in a single tick.
+ */
+ s->pscntr_sync_ticks += muldiv64(ticks, NANOSECONDS_PER_SECOND,
+ s->prescale_clk);
+}
+
static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size)
{
MPS2FPGAIO *s = MPS2_FPGAIO(opaque);
uint64_t r;
+ int64_t now;
switch (offset) {
case A_LED0:
@@ -54,12 +137,20 @@ static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size)
r = s->misc;
break;
case A_CLK1HZ:
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ r = counter_from_tickoff(now, s->clk1hz_tick_offset, 1);
+ break;
case A_CLK100HZ:
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ r = counter_from_tickoff(now, s->clk100hz_tick_offset, 100);
+ break;
case A_COUNTER:
+ resync_counter(s);
+ r = s->counter;
+ break;
case A_PSCNTR:
- /* These are all upcounters of various frequencies. */
- qemu_log_mask(LOG_UNIMP, "MPS2 FPGAIO: counters unimplemented\n");
- r = 0;
+ resync_counter(s);
+ r = s->pscntr;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
@@ -76,6 +167,7 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
MPS2FPGAIO *s = MPS2_FPGAIO(opaque);
+ int64_t now;
trace_mps2_fpgaio_write(offset, value, size);
@@ -89,6 +181,7 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value,
s->led0 = value & 0x3;
break;
case A_PRESCALE:
+ resync_counter(s);
s->prescale = value;
break;
case A_MISC:
@@ -100,6 +193,22 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value,
"MPS2 FPGAIO: MISC control bits unimplemented\n");
s->misc = value;
break;
+ case A_CLK1HZ:
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ s->clk1hz_tick_offset = tickoff_from_counter(now, value, 1);
+ break;
+ case A_CLK100HZ:
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ s->clk100hz_tick_offset = tickoff_from_counter(now, value, 100);
+ break;
+ case A_COUNTER:
+ resync_counter(s);
+ s->counter = value;
+ break;
+ case A_PSCNTR:
+ resync_counter(s);
+ s->pscntr = value;
+ break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"MPS2 FPGAIO write: bad offset 0x%x\n", (int) offset);
@@ -116,11 +225,17 @@ static const MemoryRegionOps mps2_fpgaio_ops = {
static void mps2_fpgaio_reset(DeviceState *dev)
{
MPS2FPGAIO *s = MPS2_FPGAIO(dev);
+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
trace_mps2_fpgaio_reset();
s->led0 = 0;
s->prescale = 0;
s->misc = 0;
+ s->clk1hz_tick_offset = tickoff_from_counter(now, 0, 1);
+ s->clk100hz_tick_offset = tickoff_from_counter(now, 0, 100);
+ s->counter = 0;
+ s->pscntr = 0;
+ s->pscntr_sync_ticks = now;
}
static void mps2_fpgaio_init(Object *obj)
@@ -133,6 +248,27 @@ static void mps2_fpgaio_init(Object *obj)
sysbus_init_mmio(sbd, &s->iomem);
}
+static bool mps2_fpgaio_counters_needed(void *opaque)
+{
+ /* Currently vmstate.c insists all subsections have a 'needed' function */
+ return true;
+}
+
+static const VMStateDescription mps2_fpgaio_counters_vmstate = {
+ .name = "mps2-fpgaio/counters",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .needed = mps2_fpgaio_counters_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT64(clk1hz_tick_offset, MPS2FPGAIO),
+ VMSTATE_INT64(clk100hz_tick_offset, MPS2FPGAIO),
+ VMSTATE_UINT32(counter, MPS2FPGAIO),
+ VMSTATE_UINT32(pscntr, MPS2FPGAIO),
+ VMSTATE_INT64(pscntr_sync_ticks, MPS2FPGAIO),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription mps2_fpgaio_vmstate = {
.name = "mps2-fpgaio",
.version_id = 1,
@@ -142,6 +278,10 @@ static const VMStateDescription mps2_fpgaio_vmstate = {
VMSTATE_UINT32(prescale, MPS2FPGAIO),
VMSTATE_UINT32(misc, MPS2FPGAIO),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &mps2_fpgaio_counters_vmstate,
+ NULL
}
};
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 1341508b33..52466c77c4 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -92,6 +92,15 @@ tz_mpc_mem_blocked_write(uint64_t addr, uint64_t data, unsigned size, bool secur
tz_mpc_translate(uint64_t addr, int flags, const char *idx, const char *res) "TZ MPC translate: addr 0x%" PRIx64 " flags 0x%x iommu_idx %s: %s"
tz_mpc_iommu_notify(uint64_t addr) "TZ MPC iommu: notifying UNMAP/MAP for 0x%" PRIx64
+# hw/misc/tz-msc.c
+tz_msc_reset(void) "TZ MSC: reset"
+tz_msc_cfg_nonsec(int level) "TZ MSC: cfg_nonsec = %d"
+tz_msc_cfg_sec_resp(int level) "TZ MSC: cfg_sec_resp = %d"
+tz_msc_irq_enable(int level) "TZ MSC: int_enable = %d"
+tz_msc_irq_clear(int level) "TZ MSC: int_clear = %d"
+tz_msc_update_irq(int level) "TZ MSC: setting irq line to %d"
+tz_msc_access_blocked(uint64_t offset) "TZ MSC: offset 0x%" PRIx64 " access blocked"
+
# hw/misc/tz-ppc.c
tz_ppc_reset(void) "TZ PPC: reset"
tz_ppc_cfg_nonsec(int n, int level) "TZ PPC: cfg_nonsec[%d] = %d"
@@ -116,3 +125,10 @@ ccm_freq(uint32_t freq) "freq = %d\n"
ccm_clock_freq(uint32_t clock, uint32_t freq) "(Clock = %d) = %d\n"
ccm_read_reg(const char *reg_name, uint32_t value) "reg[%s] <= 0x%" PRIx32 "\n"
ccm_write_reg(const char *reg_name, uint32_t value) "reg[%s] => 0x%" PRIx32 "\n"
+
+# hw/misc/iotkit-sysctl.c
+iotkit_sysinfo_read(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysInfo read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+iotkit_sysinfo_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysInfo write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+iotkit_sysctl_read(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+iotkit_sysctl_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+iotkit_sysctl_reset(void) "IoTKit SysCtl: reset"
diff --git a/hw/misc/tz-msc.c b/hw/misc/tz-msc.c
new file mode 100644
index 0000000000..9e352044ea
--- /dev/null
+++ b/hw/misc/tz-msc.c
@@ -0,0 +1,308 @@
+/*
+ * ARM TrustZone master security controller 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.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/misc/tz-msc.h"
+
+static void tz_msc_update_irq(TZMSC *s)
+{
+ bool level = s->irq_status;
+
+ trace_tz_msc_update_irq(level);
+ qemu_set_irq(s->irq, level);
+}
+
+static void tz_msc_cfg_nonsec(void *opaque, int n, int level)
+{
+ TZMSC *s = TZ_MSC(opaque);
+
+ trace_tz_msc_cfg_nonsec(level);
+ s->cfg_nonsec = level;
+}
+
+static void tz_msc_cfg_sec_resp(void *opaque, int n, int level)
+{
+ TZMSC *s = TZ_MSC(opaque);
+
+ trace_tz_msc_cfg_sec_resp(level);
+ s->cfg_sec_resp = level;
+}
+
+static void tz_msc_irq_clear(void *opaque, int n, int level)
+{
+ TZMSC *s = TZ_MSC(opaque);
+
+ trace_tz_msc_irq_clear(level);
+
+ s->irq_clear = level;
+ if (level) {
+ s->irq_status = false;
+ tz_msc_update_irq(s);
+ }
+}
+
+/* The MSC may either block a transaction by aborting it, block a
+ * transaction by making it RAZ/WI, allow it through with
+ * MemTxAttrs indicating a secure transaction, or allow it with
+ * MemTxAttrs indicating a non-secure transaction.
+ */
+typedef enum MSCAction {
+ MSCBlockAbort,
+ MSCBlockRAZWI,
+ MSCAllowSecure,
+ MSCAllowNonSecure,
+} MSCAction;
+
+static MSCAction tz_msc_check(TZMSC *s, hwaddr addr)
+{
+ /*
+ * Check whether to allow an access from the bus master, returning
+ * an MSCAction indicating the required behaviour. If the transaction
+ * is blocked, the caller must check cfg_sec_resp to determine
+ * whether to abort or RAZ/WI the transaction.
+ */
+ IDAUInterfaceClass *iic = IDAU_INTERFACE_GET_CLASS(s->idau);
+ IDAUInterface *ii = IDAU_INTERFACE(s->idau);
+ bool idau_exempt = false, idau_ns = true, idau_nsc = true;
+ int idau_region = IREGION_NOTVALID;
+
+ iic->check(ii, addr, &idau_region, &idau_exempt, &idau_ns, &idau_nsc);
+
+ if (idau_exempt) {
+ /*
+ * Uncheck region -- OK, transaction type depends on
+ * whether bus master is configured as Secure or NonSecure
+ */
+ return s->cfg_nonsec ? MSCAllowNonSecure : MSCAllowSecure;
+ }
+
+ if (idau_ns) {
+ /* NonSecure region -- always forward as NS transaction */
+ return MSCAllowNonSecure;
+ }
+
+ if (!s->cfg_nonsec) {
+ /* Access to Secure region by Secure bus master: OK */
+ return MSCAllowSecure;
+ }
+
+ /* Attempted access to Secure region by NS bus master: block */
+ trace_tz_msc_access_blocked(addr);
+ if (!s->cfg_sec_resp) {
+ return MSCBlockRAZWI;
+ }
+
+ /*
+ * The TRM isn't clear on behaviour if irq_clear is high when a
+ * transaction is blocked. We assume that the MSC behaves like the
+ * PPC, where holding irq_clear high suppresses the interrupt.
+ */
+ if (!s->irq_clear) {
+ s->irq_status = true;
+ tz_msc_update_irq(s);
+ }
+ return MSCBlockAbort;
+}
+
+static MemTxResult tz_msc_read(void *opaque, hwaddr addr, uint64_t *pdata,
+ unsigned size, MemTxAttrs attrs)
+{
+ TZMSC *s = opaque;
+ AddressSpace *as = &s->downstream_as;
+ uint64_t data;
+ MemTxResult res;
+
+ switch (tz_msc_check(s, addr)) {
+ case MSCBlockAbort:
+ return MEMTX_ERROR;
+ case MSCBlockRAZWI:
+ *pdata = 0;
+ return MEMTX_OK;
+ case MSCAllowSecure:
+ attrs.secure = 1;
+ attrs.unspecified = 0;
+ break;
+ case MSCAllowNonSecure:
+ attrs.secure = 0;
+ attrs.unspecified = 0;
+ break;
+ }
+
+ switch (size) {
+ case 1:
+ data = address_space_ldub(as, addr, attrs, &res);
+ break;
+ case 2:
+ data = address_space_lduw_le(as, addr, attrs, &res);
+ break;
+ case 4:
+ data = address_space_ldl_le(as, addr, attrs, &res);
+ break;
+ case 8:
+ data = address_space_ldq_le(as, addr, attrs, &res);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ *pdata = data;
+ return res;
+}
+
+static MemTxResult tz_msc_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size, MemTxAttrs attrs)
+{
+ TZMSC *s = opaque;
+ AddressSpace *as = &s->downstream_as;
+ MemTxResult res;
+
+ switch (tz_msc_check(s, addr)) {
+ case MSCBlockAbort:
+ return MEMTX_ERROR;
+ case MSCBlockRAZWI:
+ return MEMTX_OK;
+ case MSCAllowSecure:
+ attrs.secure = 1;
+ attrs.unspecified = 0;
+ break;
+ case MSCAllowNonSecure:
+ attrs.secure = 0;
+ attrs.unspecified = 0;
+ break;
+ }
+
+ switch (size) {
+ case 1:
+ address_space_stb(as, addr, val, attrs, &res);
+ break;
+ case 2:
+ address_space_stw_le(as, addr, val, attrs, &res);
+ break;
+ case 4:
+ address_space_stl_le(as, addr, val, attrs, &res);
+ break;
+ case 8:
+ address_space_stq_le(as, addr, val, attrs, &res);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return res;
+}
+
+static const MemoryRegionOps tz_msc_ops = {
+ .read_with_attrs = tz_msc_read,
+ .write_with_attrs = tz_msc_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void tz_msc_reset(DeviceState *dev)
+{
+ TZMSC *s = TZ_MSC(dev);
+
+ trace_tz_msc_reset();
+ s->cfg_sec_resp = false;
+ s->cfg_nonsec = false;
+ s->irq_clear = 0;
+ s->irq_status = 0;
+}
+
+static void tz_msc_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ TZMSC *s = TZ_MSC(obj);
+
+ qdev_init_gpio_in_named(dev, tz_msc_cfg_nonsec, "cfg_nonsec", 1);
+ qdev_init_gpio_in_named(dev, tz_msc_cfg_sec_resp, "cfg_sec_resp", 1);
+ qdev_init_gpio_in_named(dev, tz_msc_irq_clear, "irq_clear", 1);
+ qdev_init_gpio_out_named(dev, &s->irq, "irq", 1);
+}
+
+static void tz_msc_realize(DeviceState *dev, Error **errp)
+{
+ Object *obj = OBJECT(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ TZMSC *s = TZ_MSC(dev);
+ const char *name = "tz-msc-downstream";
+ uint64_t size;
+
+ /*
+ * We can't create the upstream end of the port until realize,
+ * as we don't know the size of the MR used as the downstream until then.
+ * We insist on having a downstream, to avoid complicating the
+ * code with handling the "don't know how big this is" case. It's easy
+ * enough for the user to create an unimplemented_device as downstream
+ * if they have nothing else to plug into this.
+ */
+ if (!s->downstream) {
+ error_setg(errp, "MSC 'downstream' link not set");
+ return;
+ }
+ if (!s->idau) {
+ error_setg(errp, "MSC 'idau' link not set");
+ return;
+ }
+
+ size = memory_region_size(s->downstream);
+ address_space_init(&s->downstream_as, s->downstream, name);
+ memory_region_init_io(&s->upstream, obj, &tz_msc_ops, s, name, size);
+ sysbus_init_mmio(sbd, &s->upstream);
+}
+
+static const VMStateDescription tz_msc_vmstate = {
+ .name = "tz-msc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(cfg_nonsec, TZMSC),
+ VMSTATE_BOOL(cfg_sec_resp, TZMSC),
+ VMSTATE_BOOL(irq_clear, TZMSC),
+ VMSTATE_BOOL(irq_status, TZMSC),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property tz_msc_properties[] = {
+ DEFINE_PROP_LINK("downstream", TZMSC, downstream,
+ TYPE_MEMORY_REGION, MemoryRegion *),
+ DEFINE_PROP_LINK("idau", TZMSC, idau,
+ TYPE_IDAU_INTERFACE, IDAUInterface *),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void tz_msc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = tz_msc_realize;
+ dc->vmsd = &tz_msc_vmstate;
+ dc->reset = tz_msc_reset;
+ dc->props = tz_msc_properties;
+}
+
+static const TypeInfo tz_msc_info = {
+ .name = TYPE_TZ_MSC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(TZMSC),
+ .instance_init = tz_msc_init,
+ .class_init = tz_msc_class_init,
+};
+
+static void tz_msc_register_types(void)
+{
+ type_register_static(&tz_msc_info);
+}
+
+type_init(tz_msc_register_types);
diff --git a/hw/misc/vmcoreinfo.c b/hw/misc/vmcoreinfo.c
index a2805527cb..304c6287c7 100644
--- a/hw/misc/vmcoreinfo.c
+++ b/hw/misc/vmcoreinfo.c
@@ -19,7 +19,7 @@ static void fw_cfg_vmci_write(void *dev, off_t offset, size_t len)
VMCoreInfoState *s = VMCOREINFO(dev);
s->has_vmcoreinfo = offset == 0 && len == sizeof(s->vmcoreinfo)
- && s->vmcoreinfo.guest_format != VMCOREINFO_FORMAT_NONE;
+ && s->vmcoreinfo.guest_format != FW_CFG_VMCOREINFO_FORMAT_NONE;
}
static void vmcoreinfo_reset(void *dev)
@@ -28,7 +28,7 @@ static void vmcoreinfo_reset(void *dev)
s->has_vmcoreinfo = false;
memset(&s->vmcoreinfo, 0, sizeof(s->vmcoreinfo));
- s->vmcoreinfo.host_format = cpu_to_le16(VMCOREINFO_FORMAT_ELF);
+ s->vmcoreinfo.host_format = cpu_to_le16(FW_CFG_VMCOREINFO_FORMAT_ELF);
}
static void vmcoreinfo_realize(DeviceState *dev, Error **errp)
@@ -53,7 +53,7 @@ static void vmcoreinfo_realize(DeviceState *dev, Error **errp)
return;
}
- fw_cfg_add_file_callback(fw_cfg, "etc/vmcoreinfo",
+ fw_cfg_add_file_callback(fw_cfg, FW_CFG_VMCOREINFO_FILENAME,
NULL, fw_cfg_vmci_write, s,
&s->vmcoreinfo, sizeof(s->vmcoreinfo), false);
diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c
index 47146ba12a..162b27a3b8 100644
--- a/hw/ppc/prep.c
+++ b/hw/ppc/prep.c
@@ -608,6 +608,9 @@ static int prep_set_cmos_checksum(DeviceState *dev, void *opaque)
rtc_set_memory(rtc, 0x3e, checksum & 0xff);
rtc_set_memory(rtc, 0x2f, checksum >> 8);
rtc_set_memory(rtc, 0x3f, checksum >> 8);
+
+ object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(rtc),
+ "date", NULL);
}
return 0;
}
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index ddd4478a34..4edb6c7d16 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -562,9 +562,12 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr)
{
+ CPUState **rev;
CPUState *cs;
+ int n_cpus;
int cpus_offset;
char *nodename;
+ int i;
cpus_offset = fdt_add_subnode(fdt, 0, "cpus");
_FDT(cpus_offset);
@@ -575,8 +578,19 @@ static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr)
* We walk the CPUs in reverse order to ensure that CPU DT nodes
* created by fdt_add_subnode() end up in the right order in FDT
* for the guest kernel the enumerate the CPUs correctly.
+ *
+ * The CPU list cannot be traversed in reverse order, so we need
+ * to do extra work.
*/
- CPU_FOREACH_REVERSE(cs) {
+ n_cpus = 0;
+ rev = NULL;
+ CPU_FOREACH(cs) {
+ rev = g_renew(CPUState *, rev, n_cpus + 1);
+ rev[n_cpus++] = cs;
+ }
+
+ for (i = n_cpus - 1; i >= 0; i--) {
+ CPUState *cs = rev[i];
PowerPCCPU *cpu = POWERPC_CPU(cs);
int index = spapr_get_vcpu_id(cpu);
DeviceClass *dc = DEVICE_GET_CLASS(cs);
@@ -3113,13 +3127,12 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
PCDIMMDevice *dimm = PC_DIMM(dev);
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort);
- uint64_t align, size, addr;
+ uint64_t size, addr;
uint32_t node;
- align = memory_region_get_alignment(mr);
size = memory_region_size(mr);
- pc_dimm_plug(dev, MACHINE(ms), align, &local_err);
+ pc_dimm_plug(dev, MACHINE(ms), &local_err);
if (local_err) {
goto out;
}
@@ -3154,6 +3167,7 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_dev);
PCDIMMDevice *dimm = PC_DIMM(dev);
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
+ Error *local_err = NULL;
MemoryRegion *mr;
uint64_t size;
Object *memdev;
@@ -3179,7 +3193,13 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
memdev = object_property_get_link(OBJECT(dimm), PC_DIMM_MEMDEV_PROP,
&error_abort);
pagesize = host_memory_backend_pagesize(MEMORY_BACKEND(memdev));
- spapr_check_pagesize(spapr, pagesize, errp);
+ spapr_check_pagesize(spapr, pagesize, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ pc_dimm_pre_plug(dev, MACHINE(hotplug_dev), NULL, errp);
}
struct sPAPRDIMMState {
diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c
index 160657f4b9..955ba94800 100644
--- a/hw/scsi/lsi53c895a.c
+++ b/hw/scsi/lsi53c895a.c
@@ -959,6 +959,10 @@ static void lsi_do_msgout(LSIState *s)
DPRINTF("WDTR (ignored)\n");
lsi_skip_msgbytes(s, 1);
break;
+ case 4:
+ DPRINTF("PPR (ignored)\n");
+ lsi_skip_msgbytes(s, 5);
+ break;
default:
goto bad;
}
diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c
index ba1afa3c1e..a56317e026 100644
--- a/hw/scsi/megasas.c
+++ b/hw/scsi/megasas.c
@@ -464,6 +464,7 @@ static void megasas_unmap_frame(MegasasState *s, MegasasCmd *cmd)
cmd->frame = NULL;
cmd->pa = 0;
cmd->pa_size = 0;
+ qemu_sglist_destroy(&cmd->qsg);
clear_bit(cmd->index, s->frame_map);
}
@@ -580,7 +581,6 @@ static void megasas_complete_frame(MegasasState *s, uint64_t context)
static void megasas_complete_command(MegasasCmd *cmd)
{
- qemu_sglist_destroy(&cmd->qsg);
cmd->iov_size = 0;
cmd->iov_offset = 0;
diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c
index 4176e871e1..929404fb48 100644
--- a/hw/scsi/mptsas.c
+++ b/hw/scsi/mptsas.c
@@ -1431,6 +1431,7 @@ static void mptsas1068_class_init(ObjectClass *oc, void *data)
dc->reset = mptsas_reset;
dc->vmsd = &vmstate_mptsas;
dc->desc = "LSI SAS 1068";
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
}
static const TypeInfo mptsas_info = {
diff --git a/hw/scsi/vhost-scsi-common.c b/hw/scsi/vhost-scsi-common.c
index e2a5828af1..b7fbab65dd 100644
--- a/hw/scsi/vhost-scsi-common.c
+++ b/hw/scsi/vhost-scsi-common.c
@@ -96,6 +96,9 @@ uint64_t vhost_scsi_common_get_features(VirtIODevice *vdev, uint64_t features,
{
VHostSCSICommon *vsc = VHOST_SCSI_COMMON(vdev);
+ /* Turn on predefined features supported by this device */
+ features |= vsc->host_features;
+
return vhost_get_features(&vsc->dev, vsc->feature_bits, features);
}
diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c
index 9c1bea8ff3..becf550085 100644
--- a/hw/scsi/vhost-scsi.c
+++ b/hw/scsi/vhost-scsi.c
@@ -238,6 +238,9 @@ static Property vhost_scsi_properties[] = {
DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors,
0xFFFF),
DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128),
+ DEFINE_PROP_BIT64("t10_pi", VHostSCSICommon, host_features,
+ VIRTIO_SCSI_F_T10_PI,
+ false),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c
index 9355cfdf07..2e1ba4a87b 100644
--- a/hw/scsi/vhost-user-scsi.c
+++ b/hw/scsi/vhost-user-scsi.c
@@ -137,17 +137,6 @@ static void vhost_user_scsi_unrealize(DeviceState *dev, Error **errp)
}
}
-static uint64_t vhost_user_scsi_get_features(VirtIODevice *vdev,
- uint64_t features, Error **errp)
-{
- VHostUserSCSI *s = VHOST_USER_SCSI(vdev);
-
- /* Turn on predefined features supported by this device */
- features |= s->host_features;
-
- return vhost_scsi_common_get_features(vdev, features, errp);
-}
-
static Property vhost_user_scsi_properties[] = {
DEFINE_PROP_CHR("chardev", VirtIOSCSICommon, conf.chardev),
DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0),
@@ -157,12 +146,15 @@ static Property vhost_user_scsi_properties[] = {
DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors,
0xFFFF),
DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128),
- DEFINE_PROP_BIT64("hotplug", VHostUserSCSI, host_features,
- VIRTIO_SCSI_F_HOTPLUG,
- true),
- DEFINE_PROP_BIT64("param_change", VHostUserSCSI, host_features,
- VIRTIO_SCSI_F_CHANGE,
- true),
+ DEFINE_PROP_BIT64("hotplug", VHostSCSICommon, host_features,
+ VIRTIO_SCSI_F_HOTPLUG,
+ true),
+ DEFINE_PROP_BIT64("param_change", VHostSCSICommon, host_features,
+ VIRTIO_SCSI_F_CHANGE,
+ true),
+ DEFINE_PROP_BIT64("t10_pi", VHostSCSICommon, host_features,
+ VIRTIO_SCSI_F_T10_PI,
+ false),
DEFINE_PROP_END_OF_LIST(),
};
@@ -187,7 +179,7 @@ static void vhost_user_scsi_class_init(ObjectClass *klass, void *data)
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
vdc->realize = vhost_user_scsi_realize;
vdc->unrealize = vhost_user_scsi_unrealize;
- vdc->get_features = vhost_user_scsi_get_features;
+ vdc->get_features = vhost_scsi_common_get_features;
vdc->set_config = vhost_scsi_common_set_config;
vdc->set_status = vhost_user_scsi_set_status;
fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path;
diff --git a/hw/ssi/pl022.c b/hw/ssi/pl022.c
index c1368018ee..e58247554c 100644
--- a/hw/ssi/pl022.c
+++ b/hw/ssi/pl022.c
@@ -9,6 +9,7 @@
#include "qemu/osdep.h"
#include "hw/sysbus.h"
+#include "hw/ssi/pl022.h"
#include "hw/ssi/ssi.h"
#include "qemu/log.h"
@@ -37,35 +38,10 @@ do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__);} while (0)
#define PL022_SR_BSY 0x10
#define PL022_INT_ROR 0x01
-#define PL022_INT_RT 0x04
+#define PL022_INT_RT 0x02
#define PL022_INT_RX 0x04
#define PL022_INT_TX 0x08
-#define TYPE_PL022 "pl022"
-#define PL022(obj) OBJECT_CHECK(PL022State, (obj), TYPE_PL022)
-
-typedef struct PL022State {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
- uint32_t cr0;
- uint32_t cr1;
- uint32_t bitmask;
- uint32_t sr;
- uint32_t cpsr;
- uint32_t is;
- uint32_t im;
- /* The FIFO head points to the next empty entry. */
- int tx_fifo_head;
- int rx_fifo_head;
- int tx_fifo_len;
- int rx_fifo_len;
- uint16_t tx_fifo[8];
- uint16_t rx_fifo[8];
- qemu_irq irq;
- SSIBus *ssi;
-} PL022State;
-
static const unsigned char pl022_id[8] =
{ 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
@@ -170,7 +146,7 @@ static uint64_t pl022_read(void *opaque, hwaddr offset,
return s->is;
case 0x1c: /* MIS */
return s->im & s->is;
- case 0x20: /* DMACR */
+ case 0x24: /* DMACR */
/* Not implemented. */
return 0;
default:
@@ -216,7 +192,15 @@ static void pl022_write(void *opaque, hwaddr offset,
s->im = value;
pl022_update(s);
break;
- case 0x20: /* DMACR */
+ case 0x20: /* ICR */
+ /*
+ * write-1-to-clear: bit 0 clears ROR, bit 1 clears RT;
+ * RX and TX interrupts cannot be cleared this way.
+ */
+ value &= PL022_INT_ROR | PL022_INT_RT;
+ s->is &= ~value;
+ break;
+ case 0x24: /* DMACR */
if (value) {
qemu_log_mask(LOG_UNIMP, "pl022: DMA not implemented\n");
}
@@ -227,8 +211,10 @@ static void pl022_write(void *opaque, hwaddr offset,
}
}
-static void pl022_reset(PL022State *s)
+static void pl022_reset(DeviceState *dev)
{
+ PL022State *s = PL022(dev);
+
s->rx_fifo_len = 0;
s->tx_fifo_len = 0;
s->im = 0;
@@ -292,25 +278,24 @@ static const VMStateDescription vmstate_pl022 = {
}
};
-static int pl022_init(SysBusDevice *sbd)
+static void pl022_realize(DeviceState *dev, Error **errp)
{
- DeviceState *dev = DEVICE(sbd);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
PL022State *s = PL022(dev);
memory_region_init_io(&s->iomem, OBJECT(s), &pl022_ops, s, "pl022", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
s->ssi = ssi_create_bus(dev, "ssi");
- pl022_reset(s);
- vmstate_register(dev, -1, &vmstate_pl022, s);
- return 0;
}
static void pl022_class_init(ObjectClass *klass, void *data)
{
- SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
- sdc->init = pl022_init;
+ dc->reset = pl022_reset;
+ dc->vmsd = &vmstate_pl022;
+ dc->realize = pl022_realize;
}
static const TypeInfo pl022_info = {
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index e16b2b913c..b32194d153 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -44,4 +44,5 @@ common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o
common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o
common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o
+common-obj-$(CONFIG_CMSDK_APB_DUALTIMER) += cmsdk-apb-dualtimer.o
common-obj-$(CONFIG_MSF2) += mss-timer.o
diff --git a/hw/timer/cmsdk-apb-dualtimer.c b/hw/timer/cmsdk-apb-dualtimer.c
new file mode 100644
index 0000000000..ccd49753b7
--- /dev/null
+++ b/hw/timer/cmsdk-apb-dualtimer.c
@@ -0,0 +1,515 @@
+/*
+ * ARM CMSDK APB dual-timer 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 dual-input timer" 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 "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/timer/cmsdk-apb-dualtimer.h"
+
+REG32(TIMER1LOAD, 0x0)
+REG32(TIMER1VALUE, 0x4)
+REG32(TIMER1CONTROL, 0x8)
+ FIELD(CONTROL, ONESHOT, 0, 1)
+ FIELD(CONTROL, SIZE, 1, 1)
+ FIELD(CONTROL, PRESCALE, 2, 2)
+ FIELD(CONTROL, INTEN, 5, 1)
+ FIELD(CONTROL, MODE, 6, 1)
+ FIELD(CONTROL, ENABLE, 7, 1)
+#define R_CONTROL_VALID_MASK (R_CONTROL_ONESHOT_MASK | R_CONTROL_SIZE_MASK | \
+ R_CONTROL_PRESCALE_MASK | R_CONTROL_INTEN_MASK | \
+ R_CONTROL_MODE_MASK | R_CONTROL_ENABLE_MASK)
+REG32(TIMER1INTCLR, 0xc)
+REG32(TIMER1RIS, 0x10)
+REG32(TIMER1MIS, 0x14)
+REG32(TIMER1BGLOAD, 0x18)
+REG32(TIMER2LOAD, 0x20)
+REG32(TIMER2VALUE, 0x24)
+REG32(TIMER2CONTROL, 0x28)
+REG32(TIMER2INTCLR, 0x2c)
+REG32(TIMER2RIS, 0x30)
+REG32(TIMER2MIS, 0x34)
+REG32(TIMER2BGLOAD, 0x38)
+REG32(TIMERITCR, 0xf00)
+ FIELD(TIMERITCR, ENABLE, 0, 1)
+#define R_TIMERITCR_VALID_MASK R_TIMERITCR_ENABLE_MASK
+REG32(TIMERITOP, 0xf04)
+ FIELD(TIMERITOP, TIMINT1, 0, 1)
+ FIELD(TIMERITOP, TIMINT2, 1, 1)
+#define R_TIMERITOP_VALID_MASK (R_TIMERITOP_TIMINT1_MASK | \
+ R_TIMERITOP_TIMINT2_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 timer_id[] = {
+ 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
+ 0x23, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
+ 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
+};
+
+static bool cmsdk_dualtimermod_intstatus(CMSDKAPBDualTimerModule *m)
+{
+ /* Return masked interrupt status for the timer module */
+ return m->intstatus && (m->control & R_CONTROL_INTEN_MASK);
+}
+
+static void cmsdk_apb_dualtimer_update(CMSDKAPBDualTimer *s)
+{
+ bool timint1, timint2, timintc;
+
+ if (s->timeritcr) {
+ /* Integration test mode: outputs driven directly from TIMERITOP bits */
+ timint1 = s->timeritop & R_TIMERITOP_TIMINT1_MASK;
+ timint2 = s->timeritop & R_TIMERITOP_TIMINT2_MASK;
+ } else {
+ timint1 = cmsdk_dualtimermod_intstatus(&s->timermod[0]);
+ timint2 = cmsdk_dualtimermod_intstatus(&s->timermod[1]);
+ }
+
+ timintc = timint1 || timint2;
+
+ qemu_set_irq(s->timermod[0].timerint, timint1);
+ qemu_set_irq(s->timermod[1].timerint, timint2);
+ qemu_set_irq(s->timerintc, timintc);
+}
+
+static void cmsdk_dualtimermod_write_control(CMSDKAPBDualTimerModule *m,
+ uint32_t newctrl)
+{
+ /* Handle a write to the CONTROL register */
+ uint32_t changed;
+
+ newctrl &= R_CONTROL_VALID_MASK;
+
+ changed = m->control ^ newctrl;
+
+ if (changed & ~newctrl & R_CONTROL_ENABLE_MASK) {
+ /* ENABLE cleared, stop timer before any further changes */
+ ptimer_stop(m->timer);
+ }
+
+ if (changed & R_CONTROL_PRESCALE_MASK) {
+ int divisor;
+
+ switch (FIELD_EX32(newctrl, CONTROL, PRESCALE)) {
+ case 0:
+ divisor = 1;
+ break;
+ case 1:
+ divisor = 16;
+ break;
+ case 2:
+ divisor = 256;
+ break;
+ case 3:
+ /* UNDEFINED; complain, and arbitrarily treat like 2 */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CMSDK APB dual-timer: CONTROL.PRESCALE==0b11"
+ " is undefined behaviour\n");
+ divisor = 256;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ ptimer_set_freq(m->timer, m->parent->pclk_frq / divisor);
+ }
+
+ if (changed & R_CONTROL_MODE_MASK) {
+ uint32_t load;
+ if (newctrl & R_CONTROL_MODE_MASK) {
+ /* Periodic: the limit is the LOAD register value */
+ load = m->load;
+ } else {
+ /* Free-running: counter wraps around */
+ load = ptimer_get_limit(m->timer);
+ if (!(m->control & R_CONTROL_SIZE_MASK)) {
+ load = deposit32(m->load, 0, 16, load);
+ }
+ m->load = load;
+ load = 0xffffffff;
+ }
+ if (!(m->control & R_CONTROL_SIZE_MASK)) {
+ load &= 0xffff;
+ }
+ ptimer_set_limit(m->timer, load, 0);
+ }
+
+ if (changed & R_CONTROL_SIZE_MASK) {
+ /* Timer switched between 16 and 32 bit count */
+ uint32_t value, load;
+
+ value = ptimer_get_count(m->timer);
+ load = ptimer_get_limit(m->timer);
+ if (newctrl & R_CONTROL_SIZE_MASK) {
+ /* 16 -> 32, top half of VALUE is in struct field */
+ value = deposit32(m->value, 0, 16, value);
+ } else {
+ /* 32 -> 16: save top half to struct field and truncate */
+ m->value = value;
+ value &= 0xffff;
+ }
+
+ if (newctrl & R_CONTROL_MODE_MASK) {
+ /* Periodic, timer limit has LOAD value */
+ if (newctrl & R_CONTROL_SIZE_MASK) {
+ load = deposit32(m->load, 0, 16, load);
+ } else {
+ m->load = load;
+ load &= 0xffff;
+ }
+ } else {
+ /* Free-running, timer limit is set to give wraparound */
+ if (newctrl & R_CONTROL_SIZE_MASK) {
+ load = 0xffffffff;
+ } else {
+ load = 0xffff;
+ }
+ }
+ ptimer_set_count(m->timer, value);
+ ptimer_set_limit(m->timer, load, 0);
+ }
+
+ if (newctrl & R_CONTROL_ENABLE_MASK) {
+ /*
+ * ENABLE is set; start the timer after all other changes.
+ * We start it even if the ENABLE bit didn't actually change,
+ * in case the timer was an expired one-shot timer that has
+ * now been changed into a free-running or periodic timer.
+ */
+ ptimer_run(m->timer, !!(newctrl & R_CONTROL_ONESHOT_MASK));
+ }
+
+ m->control = newctrl;
+}
+
+static uint64_t cmsdk_apb_dualtimer_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque);
+ uint64_t r;
+
+ if (offset >= A_TIMERITCR) {
+ switch (offset) {
+ case A_TIMERITCR:
+ r = s->timeritcr;
+ break;
+ case A_PID4 ... A_CID3:
+ r = timer_id[(offset - A_PID4) / 4];
+ break;
+ default:
+ bad_offset:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CMSDK APB dual-timer read: bad offset %x\n",
+ (int) offset);
+ r = 0;
+ break;
+ }
+ } else {
+ int timer = offset >> 5;
+ CMSDKAPBDualTimerModule *m;
+
+ if (timer >= ARRAY_SIZE(s->timermod)) {
+ goto bad_offset;
+ }
+
+ m = &s->timermod[timer];
+
+ switch (offset & 0x1F) {
+ case A_TIMER1LOAD:
+ case A_TIMER1BGLOAD:
+ if (m->control & R_CONTROL_MODE_MASK) {
+ /*
+ * Periodic: the ptimer limit is the LOAD register value, (or
+ * just the low 16 bits of it if the timer is in 16-bit mode)
+ */
+ r = ptimer_get_limit(m->timer);
+ if (!(m->control & R_CONTROL_SIZE_MASK)) {
+ r = deposit32(m->load, 0, 16, r);
+ }
+ } else {
+ /* Free-running: LOAD register value is just in m->load */
+ r = m->load;
+ }
+ break;
+ case A_TIMER1VALUE:
+ r = ptimer_get_count(m->timer);
+ if (!(m->control & R_CONTROL_SIZE_MASK)) {
+ r = deposit32(m->value, 0, 16, r);
+ }
+ break;
+ case A_TIMER1CONTROL:
+ r = m->control;
+ break;
+ case A_TIMER1RIS:
+ r = m->intstatus;
+ break;
+ case A_TIMER1MIS:
+ r = cmsdk_dualtimermod_intstatus(m);
+ break;
+ default:
+ goto bad_offset;
+ }
+ }
+
+ trace_cmsdk_apb_dualtimer_read(offset, r, size);
+ return r;
+}
+
+static void cmsdk_apb_dualtimer_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque);
+
+ trace_cmsdk_apb_dualtimer_write(offset, value, size);
+
+ if (offset >= A_TIMERITCR) {
+ switch (offset) {
+ case A_TIMERITCR:
+ s->timeritcr = value & R_TIMERITCR_VALID_MASK;
+ cmsdk_apb_dualtimer_update(s);
+ case A_TIMERITOP:
+ s->timeritop = value & R_TIMERITOP_VALID_MASK;
+ cmsdk_apb_dualtimer_update(s);
+ default:
+ bad_offset:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CMSDK APB dual-timer write: bad offset %x\n",
+ (int) offset);
+ break;
+ }
+ } else {
+ int timer = offset >> 5;
+ CMSDKAPBDualTimerModule *m;
+
+ if (timer >= ARRAY_SIZE(s->timermod)) {
+ goto bad_offset;
+ }
+
+ m = &s->timermod[timer];
+
+ switch (offset & 0x1F) {
+ case A_TIMER1LOAD:
+ /* Set the limit, and immediately reload the count from it */
+ m->load = value;
+ m->value = value;
+ if (!(m->control & R_CONTROL_SIZE_MASK)) {
+ value &= 0xffff;
+ }
+ if (!(m->control & R_CONTROL_MODE_MASK)) {
+ /*
+ * In free-running mode this won't set the limit but will
+ * still change the current count value.
+ */
+ ptimer_set_count(m->timer, value);
+ } else {
+ if (!value) {
+ ptimer_stop(m->timer);
+ }
+ ptimer_set_limit(m->timer, value, 1);
+ if (value && (m->control & R_CONTROL_ENABLE_MASK)) {
+ /* Force possibly-expired oneshot timer to restart */
+ ptimer_run(m->timer, 1);
+ }
+ }
+ break;
+ case A_TIMER1BGLOAD:
+ /* Set the limit, but not the current count */
+ m->load = value;
+ if (!(m->control & R_CONTROL_MODE_MASK)) {
+ /* In free-running mode there is no limit */
+ break;
+ }
+ if (!(m->control & R_CONTROL_SIZE_MASK)) {
+ value &= 0xffff;
+ }
+ ptimer_set_limit(m->timer, value, 0);
+ break;
+ case A_TIMER1CONTROL:
+ cmsdk_dualtimermod_write_control(m, value);
+ cmsdk_apb_dualtimer_update(s);
+ break;
+ case A_TIMER1INTCLR:
+ m->intstatus = 0;
+ cmsdk_apb_dualtimer_update(s);
+ break;
+ default:
+ goto bad_offset;
+ }
+ }
+}
+
+static const MemoryRegionOps cmsdk_apb_dualtimer_ops = {
+ .read = cmsdk_apb_dualtimer_read,
+ .write = cmsdk_apb_dualtimer_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_dualtimermod_tick(void *opaque)
+{
+ CMSDKAPBDualTimerModule *m = opaque;
+
+ m->intstatus = 1;
+ cmsdk_apb_dualtimer_update(m->parent);
+}
+
+static void cmsdk_dualtimermod_reset(CMSDKAPBDualTimerModule *m)
+{
+ m->control = R_CONTROL_INTEN_MASK;
+ m->intstatus = 0;
+ m->load = 0;
+ m->value = 0xffffffff;
+ ptimer_stop(m->timer);
+ /*
+ * We start in free-running mode, with VALUE at 0xffffffff, and
+ * in 16-bit counter mode. This means that the ptimer count and
+ * limit must both be set to 0xffff, so we wrap at 16 bits.
+ */
+ ptimer_set_limit(m->timer, 0xffff, 1);
+ ptimer_set_freq(m->timer, m->parent->pclk_frq);
+}
+
+static void cmsdk_apb_dualtimer_reset(DeviceState *dev)
+{
+ CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(dev);
+ int i;
+
+ trace_cmsdk_apb_dualtimer_reset();
+
+ for (i = 0; i < ARRAY_SIZE(s->timermod); i++) {
+ cmsdk_dualtimermod_reset(&s->timermod[i]);
+ }
+ s->timeritcr = 0;
+ s->timeritop = 0;
+}
+
+static void cmsdk_apb_dualtimer_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(obj);
+ int i;
+
+ memory_region_init_io(&s->iomem, obj, &cmsdk_apb_dualtimer_ops,
+ s, "cmsdk-apb-dualtimer", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->timerintc);
+
+ for (i = 0; i < ARRAY_SIZE(s->timermod); i++) {
+ sysbus_init_irq(sbd, &s->timermod[i].timerint);
+ }
+}
+
+static void cmsdk_apb_dualtimer_realize(DeviceState *dev, Error **errp)
+{
+ CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(dev);
+ int i;
+
+ if (s->pclk_frq == 0) {
+ error_setg(errp, "CMSDK APB timer: pclk-frq property must be set");
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(s->timermod); i++) {
+ CMSDKAPBDualTimerModule *m = &s->timermod[i];
+ QEMUBH *bh = qemu_bh_new(cmsdk_dualtimermod_tick, m);
+
+ m->parent = s;
+ m->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);
+ }
+}
+
+static const VMStateDescription cmsdk_dualtimermod_vmstate = {
+ .name = "cmsdk-apb-dualtimer-module",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PTIMER(timer, CMSDKAPBDualTimerModule),
+ VMSTATE_UINT32(load, CMSDKAPBDualTimerModule),
+ VMSTATE_UINT32(value, CMSDKAPBDualTimerModule),
+ VMSTATE_UINT32(control, CMSDKAPBDualTimerModule),
+ VMSTATE_UINT32(intstatus, CMSDKAPBDualTimerModule),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription cmsdk_apb_dualtimer_vmstate = {
+ .name = "cmsdk-apb-dualtimer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT_ARRAY(timermod, CMSDKAPBDualTimer,
+ CMSDK_APB_DUALTIMER_NUM_MODULES,
+ 1, cmsdk_dualtimermod_vmstate,
+ CMSDKAPBDualTimerModule),
+ VMSTATE_UINT32(timeritcr, CMSDKAPBDualTimer),
+ VMSTATE_UINT32(timeritop, CMSDKAPBDualTimer),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property cmsdk_apb_dualtimer_properties[] = {
+ DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBDualTimer, pclk_frq, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void cmsdk_apb_dualtimer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = cmsdk_apb_dualtimer_realize;
+ dc->vmsd = &cmsdk_apb_dualtimer_vmstate;
+ dc->reset = cmsdk_apb_dualtimer_reset;
+ dc->props = cmsdk_apb_dualtimer_properties;
+}
+
+static const TypeInfo cmsdk_apb_dualtimer_info = {
+ .name = TYPE_CMSDK_APB_DUALTIMER,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(CMSDKAPBDualTimer),
+ .instance_init = cmsdk_apb_dualtimer_init,
+ .class_init = cmsdk_apb_dualtimer_class_init,
+};
+
+static void cmsdk_apb_dualtimer_register_types(void)
+{
+ type_register_static(&cmsdk_apb_dualtimer_info);
+}
+
+type_init(cmsdk_apb_dualtimer_register_types);
diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c
index 6f1f723b1f..a504f0308d 100644
--- a/hw/timer/mc146818rtc.c
+++ b/hw/timer/mc146818rtc.c
@@ -120,7 +120,7 @@ static void rtc_coalesced_timer_update(RTCState *s)
timer_del(s->coalesced_timer);
} else {
/* divide each RTC interval to 2 - 8 smaller intervals */
- int c = MIN(s->irq_coalesced, 7) + 1;
+ int c = MIN(s->irq_coalesced, 7) + 1;
int64_t next_clock = qemu_clock_get_ns(rtc_clock) +
periodic_clock_to_ns(s->period / c);
timer_mod(s->coalesced_timer, next_clock);
@@ -485,7 +485,7 @@ static void cmos_ioport_write(void *opaque, hwaddr addr,
s->cmos_data[s->cmos_index] = data;
check_update_timer(s);
break;
- case RTC_IBM_PS2_CENTURY_BYTE:
+ case RTC_IBM_PS2_CENTURY_BYTE:
s->cmos_index = RTC_CENTURY;
/* fall through */
case RTC_CENTURY:
@@ -713,7 +713,7 @@ static uint64_t cmos_ioport_read(void *opaque, hwaddr addr,
return 0xff;
} else {
switch(s->cmos_index) {
- case RTC_IBM_PS2_CENTURY_BYTE:
+ case RTC_IBM_PS2_CENTURY_BYTE:
s->cmos_index = RTC_CENTURY;
/* fall through */
case RTC_CENTURY:
@@ -915,7 +915,7 @@ static void rtc_reset(void *opaque)
if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) {
s->irq_coalesced = 0;
- s->irq_reinject_on_ack_count = 0;
+ s->irq_reinject_on_ack_count = 0;
}
}
@@ -995,9 +995,6 @@ static void rtc_realizefn(DeviceState *dev, Error **errp)
object_property_add_tm(OBJECT(s), "date", rtc_get_date, NULL);
- object_property_add_alias(qdev_get_machine(), "rtc-time",
- OBJECT(s), "date", NULL);
-
qdev_init_gpio_out(dev, &s->irq, 1);
}
@@ -1019,6 +1016,9 @@ ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq)
}
QLIST_INSERT_HEAD(&rtc_devices, s, link);
+ object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(s),
+ "date", NULL);
+
return isadev;
}
@@ -1052,17 +1052,11 @@ static void rtc_class_initfn(ObjectClass *klass, void *data)
dc->user_creatable = false;
}
-static void rtc_finalize(Object *obj)
-{
- object_property_del(qdev_get_machine(), "rtc", NULL);
-}
-
static const TypeInfo mc146818rtc_info = {
.name = TYPE_MC146818_RTC,
.parent = TYPE_ISA_DEVICE,
.instance_size = sizeof(RTCState),
.class_init = rtc_class_initfn,
- .instance_finalize = rtc_finalize,
};
static void mc146818rtc_register_types(void)
diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c
index 5f8736cf10..91b18ba312 100644
--- a/hw/timer/sh_timer.c
+++ b/hw/timer/sh_timer.c
@@ -74,6 +74,7 @@ static uint32_t sh_timer_read(void *opaque, hwaddr offset)
case OFFSET_TCPR:
if (s->feat & TIMER_FEAT_CAPT)
return s->tcpr;
+ /* fall through */
default:
hw_error("sh_timer_read: Bad offset %x\n", (int)offset);
return 0;
diff --git a/hw/timer/trace-events b/hw/timer/trace-events
index e6e042fddb..fa4213df5b 100644
--- a/hw/timer/trace-events
+++ b/hw/timer/trace-events
@@ -61,5 +61,10 @@ cmsdk_apb_timer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB t
cmsdk_apb_timer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
cmsdk_apb_timer_reset(void) "CMSDK APB timer: reset"
+# hw/timer/cmsdk_apb_dualtimer.c
+cmsdk_apb_dualtimer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+cmsdk_apb_dualtimer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset"
+
# hw/timer/xlnx-zynqmp-rtc.c
xlnx_zynqmp_rtc_gettime(int year, int month, int day, int hour, int min, int sec) "Get time from host: %d-%d-%d %2d:%02d:%02d"
diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c
index 1ded7ac9a3..3fdc4b0da1 100644
--- a/hw/usb/dev-mtp.c
+++ b/hw/usb/dev-mtp.c
@@ -82,6 +82,7 @@ enum mtp_code {
FMT_ASSOCIATION = 0x3001,
/* event codes */
+ EVT_CANCEL_TRANSACTION = 0x4001,
EVT_OBJ_ADDED = 0x4002,
EVT_OBJ_REMOVED = 0x4003,
EVT_OBJ_INFO_CHANGED = 0x4007,
@@ -146,9 +147,12 @@ struct MTPData {
uint32_t trans;
uint64_t offset;
uint64_t length;
- uint32_t alloc;
+ uint64_t alloc;
uint8_t *data;
bool first;
+ /* Used for >4G file sizes */
+ bool pending;
+ uint64_t cached_length;
int fd;
};
@@ -1551,14 +1555,35 @@ static void usb_mtp_handle_control(USBDevice *dev, USBPacket *p,
int length, uint8_t *data)
{
int ret;
+ MTPState *s = USB_MTP(dev);
+ uint16_t *event = (uint16_t *)data;
- ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
- if (ret >= 0) {
- return;
+ switch (request) {
+ case ClassInterfaceOutRequest | 0x64:
+ if (*event == EVT_CANCEL_TRANSACTION) {
+ g_free(s->result);
+ s->result = NULL;
+ usb_mtp_data_free(s->data_in);
+ s->data_in = NULL;
+ if (s->write_pending) {
+ g_free(s->dataset.filename);
+ s->write_pending = false;
+ }
+ usb_mtp_data_free(s->data_out);
+ s->data_out = NULL;
+ } else {
+ p->status = USB_RET_STALL;
+ }
+ break;
+ default:
+ ret = usb_desc_handle_control(dev, p, request,
+ value, index, length, data);
+ if (ret >= 0) {
+ return;
+ }
}
trace_usb_mtp_stall(dev->addr, "unknown control request");
- p->status = USB_RET_STALL;
}
static void usb_mtp_cancel_packet(USBDevice *dev, USBPacket *p)
@@ -1580,13 +1605,31 @@ static void utf16_to_str(uint8_t len, uint16_t *arr, char *name)
g_free(wstr);
}
+/* Wrapper around write, returns 0 on failure */
+static uint64_t write_retry(int fd, void *buf, uint64_t size)
+{
+ uint64_t bytes_left = size, ret;
+
+ while (bytes_left > 0) {
+ ret = write(fd, buf, bytes_left);
+ if ((ret == -1) && (errno != EINTR || errno != EAGAIN ||
+ errno != EWOULDBLOCK)) {
+ break;
+ }
+ bytes_left -= ret;
+ buf += ret;
+ }
+
+ return size - bytes_left;
+}
+
static void usb_mtp_write_data(MTPState *s)
{
MTPData *d = s->data_out;
MTPObject *parent =
usb_mtp_object_lookup(s, s->dataset.parent_handle);
char *path = NULL;
- int rc = -1;
+ uint64_t rc;
mode_t mask = 0644;
assert(d != NULL);
@@ -1603,7 +1646,7 @@ static void usb_mtp_write_data(MTPState *s)
d->fd = mkdir(path, mask);
goto free;
}
- if (s->dataset.size < d->length) {
+ if ((s->dataset.size != 0xFFFFFFFF) && (s->dataset.size < d->length)) {
usb_mtp_queue_result(s, RES_STORE_FULL, d->trans,
0, 0, 0, 0);
goto done;
@@ -1622,8 +1665,8 @@ static void usb_mtp_write_data(MTPState *s)
goto success;
}
- rc = write(d->fd, d->data, s->dataset.size);
- if (rc == -1) {
+ rc = write_retry(d->fd, d->data, s->dataset.size);
+ if (!rc) {
usb_mtp_queue_result(s, RES_STORE_FULL, d->trans,
0, 0, 0, 0);
goto done;
@@ -1699,6 +1742,7 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container,
MTPData *d = s->data_out;
uint64_t dlen;
uint32_t data_len = p->iov.size;
+ uint64_t total_len;
if (!d) {
usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, 0,
@@ -1707,18 +1751,33 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container,
}
if (d->first) {
/* Total length of incoming data */
- d->length = cpu_to_le32(container->length) - sizeof(mtp_container);
+ total_len = cpu_to_le32(container->length) - sizeof(mtp_container);
/* Length of data in this packet */
data_len -= sizeof(mtp_container);
- usb_mtp_realloc(d, d->length);
+ usb_mtp_realloc(d, total_len);
+ d->length += total_len;
d->offset = 0;
+ d->cached_length = total_len;
d->first = false;
+ d->pending = false;
+ }
+
+ if (d->pending) {
+ usb_mtp_realloc(d, d->cached_length);
+ d->length += d->cached_length;
+ d->pending = false;
}
if (d->length - d->offset > data_len) {
dlen = data_len;
} else {
dlen = d->length - d->offset;
+ /* Check for cached data for large files */
+ if ((s->dataset.size == 0xFFFFFFFF) && (dlen < p->iov.size)) {
+ usb_mtp_realloc(d, p->iov.size - dlen);
+ d->length += p->iov.size - dlen;
+ dlen = p->iov.size;
+ }
}
switch (d->code) {
@@ -1738,12 +1797,18 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container,
case CMD_SEND_OBJECT:
usb_packet_copy(p, d->data + d->offset, dlen);
d->offset += dlen;
- if (d->offset == d->length) {
+ if ((p->iov.size % 64) || !p->iov.size) {
+ assert((s->dataset.size == 0xFFFFFFFF) ||
+ (s->dataset.size == d->length));
+
usb_mtp_write_data(s);
usb_mtp_data_free(s->data_out);
s->data_out = NULL;
return;
}
+ if (d->offset == d->length) {
+ d->pending = true;
+ }
break;
default:
p->status = USB_RET_STALL;
@@ -1953,7 +2018,7 @@ static void usb_mtp_realize(USBDevice *dev, Error **errp)
QTAILQ_INIT(&s->objects);
if (s->desc == NULL) {
if (s->root == NULL) {
- error_setg(errp, "usb-mtp: x-root property must be configured");
+ error_setg(errp, "usb-mtp: rootdir property must be configured");
return;
}
s->desc = strrchr(s->root, '/');
@@ -1982,7 +2047,7 @@ static const VMStateDescription vmstate_usb_mtp = {
};
static Property mtp_properties[] = {
- DEFINE_PROP_STRING("x-root", MTPState, root),
+ DEFINE_PROP_STRING("rootdir", MTPState, root),
DEFINE_PROP_STRING("desc", MTPState, desc),
DEFINE_PROP_BOOL("readonly", MTPState, readonly, true),
DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index d4c0293db5..98da5f0f04 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -1156,6 +1156,9 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
OHCI_SET_BM(td.flags, TD_EC, 3);
break;
}
+ /* An error occured so we have to clear the interrupt counter. See
+ * spec at 6.4.4 on page 104 */
+ ohci->done_count = 0;
}
ed->head |= OHCI_ED_H;
}