diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/arm/virt.c | 2 | ||||
-rw-r--r-- | hw/core/machine.c | 1 | ||||
-rw-r--r-- | hw/display/xlnx_dp.c | 15 | ||||
-rw-r--r-- | hw/ssi/mss-spi.c | 8 | ||||
-rw-r--r-- | hw/ssi/xilinx_spips.c | 43 | ||||
-rw-r--r-- | hw/timer/pl031.c | 92 |
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 = { |