aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/virt.c2
-rw-r--r--hw/core/machine.c1
-rw-r--r--hw/display/xlnx_dp.c15
-rw-r--r--hw/ssi/mss-spi.c8
-rw-r--r--hw/ssi/xilinx_spips.c43
-rw-r--r--hw/timer/pl031.c92
6 files changed, 139 insertions, 22 deletions
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 0b5138cb22..d9496c9363 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1674,7 +1674,7 @@ static void machvirt_init(MachineState *machine)
&machine->device_memory->mr);
}
- virt_flash_fdt(vms, sysmem, secure_sysmem);
+ virt_flash_fdt(vms, sysmem, secure_sysmem ?: sysmem);
create_gic(vms, pic);
diff --git a/hw/core/machine.c b/hw/core/machine.c
index c4ead16010..c58a8e594e 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -35,6 +35,7 @@ GlobalProperty hw_compat_4_0[] = {
{ "virtio-gpu-pci", "edid", "false" },
{ "virtio-device", "use-started", "false" },
{ "virtio-balloon-device", "qemu-4-0-config-size", "true" },
+ { "pl031", "migrate-tick-offset", "false" },
};
const size_t hw_compat_4_0_len = G_N_ELEMENTS(hw_compat_4_0);
diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c
index cfd4c700b7..cc5b650df0 100644
--- a/hw/display/xlnx_dp.c
+++ b/hw/display/xlnx_dp.c
@@ -427,11 +427,18 @@ static uint8_t xlnx_dp_aux_pop_rx_fifo(XlnxDPState *s)
uint8_t ret;
if (fifo8_is_empty(&s->rx_fifo)) {
- DPRINTF("rx_fifo underflow..\n");
- abort();
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Reading empty RX_FIFO\n",
+ __func__);
+ /*
+ * The datasheet is not clear about the reset value, it seems
+ * to be unspecified. We choose to return '0'.
+ */
+ ret = 0;
+ } else {
+ ret = fifo8_pop(&s->rx_fifo);
+ DPRINTF("pop 0x%" PRIX8 " from rx_fifo.\n", ret);
}
- ret = fifo8_pop(&s->rx_fifo);
- DPRINTF("pop 0x%" PRIX8 " from rx_fifo.\n", ret);
return ret;
}
diff --git a/hw/ssi/mss-spi.c b/hw/ssi/mss-spi.c
index 918b1f3e82..4c9da5d2b2 100644
--- a/hw/ssi/mss-spi.c
+++ b/hw/ssi/mss-spi.c
@@ -165,7 +165,13 @@ spi_read(void *opaque, hwaddr addr, unsigned int size)
case R_SPI_RX:
s->regs[R_SPI_STATUS] &= ~S_RXFIFOFUL;
s->regs[R_SPI_STATUS] &= ~S_RXCHOVRF;
- ret = fifo32_pop(&s->rx_fifo);
+ if (fifo32_is_empty(&s->rx_fifo)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Reading empty RX_FIFO\n",
+ __func__);
+ } else {
+ ret = fifo32_pop(&s->rx_fifo);
+ }
if (fifo32_is_empty(&s->rx_fifo)) {
s->regs[R_SPI_STATUS] |= S_RXFIFOEMP;
}
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index 8115bb6d46..b29e0a4a89 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -1202,28 +1202,47 @@ static void lqspi_load_cache(void *opaque, hwaddr addr)
}
}
-static uint64_t
-lqspi_read(void *opaque, hwaddr addr, unsigned int size)
+static MemTxResult lqspi_read(void *opaque, hwaddr addr, uint64_t *value,
+ unsigned size, MemTxAttrs attrs)
{
- XilinxQSPIPS *q = opaque;
- uint32_t ret;
+ XilinxQSPIPS *q = XILINX_QSPIPS(opaque);
if (addr >= q->lqspi_cached_addr &&
addr <= q->lqspi_cached_addr + LQSPI_CACHE_SIZE - 4) {
uint8_t *retp = &q->lqspi_buf[addr - q->lqspi_cached_addr];
- ret = cpu_to_le32(*(uint32_t *)retp);
- DB_PRINT_L(1, "addr: %08x, data: %08x\n", (unsigned)addr,
- (unsigned)ret);
- return ret;
- } else {
- lqspi_load_cache(opaque, addr);
- return lqspi_read(opaque, addr, size);
+ *value = cpu_to_le32(*(uint32_t *)retp);
+ DB_PRINT_L(1, "addr: %08" HWADDR_PRIx ", data: %08" PRIx64 "\n",
+ addr, *value);
+ return MEMTX_OK;
}
+
+ lqspi_load_cache(opaque, addr);
+ return lqspi_read(opaque, addr, value, size, attrs);
+}
+
+static MemTxResult lqspi_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size, MemTxAttrs attrs)
+{
+ /*
+ * From UG1085, Chapter 24 (Quad-SPI controllers):
+ * - Writes are ignored
+ * - AXI writes generate an external AXI slave error (SLVERR)
+ */
+ qemu_log_mask(LOG_GUEST_ERROR, "%s Unexpected %u-bit access to 0x%" PRIx64
+ " (value: 0x%" PRIx64 "\n",
+ __func__, size << 3, offset, value);
+
+ return MEMTX_ERROR;
}
static const MemoryRegionOps lqspi_ops = {
- .read = lqspi_read,
+ .read_with_attrs = lqspi_read,
+ .write_with_attrs = lqspi_write,
.endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
.valid = {
.min_access_size = 1,
.max_access_size = 4
diff --git a/hw/timer/pl031.c b/hw/timer/pl031.c
index 3378084f4a..1a7e2ee06b 100644
--- a/hw/timer/pl031.c
+++ b/hw/timer/pl031.c
@@ -199,29 +199,94 @@ static int pl031_pre_save(void *opaque)
{
PL031State *s = opaque;
- /* tick_offset is base_time - rtc_clock base time. Instead, we want to
- * store the base time relative to the QEMU_CLOCK_VIRTUAL for backwards-compatibility. */
+ /*
+ * The PL031 device model code uses the tick_offset field, which is
+ * the offset between what the guest RTC should read and what the
+ * QEMU rtc_clock reads:
+ * guest_rtc = rtc_clock + tick_offset
+ * and so
+ * tick_offset = guest_rtc - rtc_clock
+ *
+ * We want to migrate this offset, which sounds straightforward.
+ * Unfortunately older versions of QEMU migrated a conversion of this
+ * offset into an offset from the vm_clock. (This was in turn an
+ * attempt to be compatible with even older QEMU versions, but it
+ * has incorrect behaviour if the rtc_clock is not the same as the
+ * vm_clock.) So we put the actual tick_offset into a migration
+ * subsection, and the backwards-compatible time-relative-to-vm_clock
+ * in the main migration state.
+ *
+ * Calculate base time relative to QEMU_CLOCK_VIRTUAL:
+ */
int64_t delta = qemu_clock_get_ns(rtc_clock) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
s->tick_offset_vmstate = s->tick_offset + delta / NANOSECONDS_PER_SECOND;
return 0;
}
+static int pl031_pre_load(void *opaque)
+{
+ PL031State *s = opaque;
+
+ s->tick_offset_migrated = false;
+ return 0;
+}
+
static int pl031_post_load(void *opaque, int version_id)
{
PL031State *s = opaque;
- int64_t delta = qemu_clock_get_ns(rtc_clock) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- s->tick_offset = s->tick_offset_vmstate - delta / NANOSECONDS_PER_SECOND;
+ /*
+ * If we got the tick_offset subsection, then we can just use
+ * the value in that. Otherwise the source is an older QEMU and
+ * has given us the offset from the vm_clock; convert it back to
+ * an offset from the rtc_clock. This will cause time to incorrectly
+ * go backwards compared to the host RTC, but this is unavoidable.
+ */
+
+ if (!s->tick_offset_migrated) {
+ int64_t delta = qemu_clock_get_ns(rtc_clock) -
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ s->tick_offset = s->tick_offset_vmstate -
+ delta / NANOSECONDS_PER_SECOND;
+ }
pl031_set_alarm(s);
return 0;
}
+static int pl031_tick_offset_post_load(void *opaque, int version_id)
+{
+ PL031State *s = opaque;
+
+ s->tick_offset_migrated = true;
+ return 0;
+}
+
+static bool pl031_tick_offset_needed(void *opaque)
+{
+ PL031State *s = opaque;
+
+ return s->migrate_tick_offset;
+}
+
+static const VMStateDescription vmstate_pl031_tick_offset = {
+ .name = "pl031/tick-offset",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = pl031_tick_offset_needed,
+ .post_load = pl031_tick_offset_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(tick_offset, PL031State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_pl031 = {
.name = "pl031",
.version_id = 1,
.minimum_version_id = 1,
.pre_save = pl031_pre_save,
+ .pre_load = pl031_pre_load,
.post_load = pl031_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32(tick_offset_vmstate, PL031State),
@@ -231,14 +296,33 @@ static const VMStateDescription vmstate_pl031 = {
VMSTATE_UINT32(im, PL031State),
VMSTATE_UINT32(is, PL031State),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_pl031_tick_offset,
+ NULL
}
};
+static Property pl031_properties[] = {
+ /*
+ * True to correctly migrate the tick offset of the RTC. False to
+ * obtain backward migration compatibility with older QEMU versions,
+ * at the expense of the guest RTC going backwards compared with the
+ * host RTC when the VM is saved/restored if using -rtc host.
+ * (Even if set to 'true' older QEMU can migrate forward to newer QEMU;
+ * 'false' also permits newer QEMU to migrate to older QEMU.)
+ */
+ DEFINE_PROP_BOOL("migrate-tick-offset",
+ PL031State, migrate_tick_offset, true),
+ DEFINE_PROP_END_OF_LIST()
+};
+
static void pl031_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &vmstate_pl031;
+ dc->props = pl031_properties;
}
static const TypeInfo pl031_info = {