diff --git a/hw/char/pl011.c b/hw/char/pl011.c index c5621a195f..dc85527a5f 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -322,10 +322,18 @@ static const MemoryRegionOps pl011_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +static bool pl011_clock_needed(void *opaque) +{ + PL011State *s = PL011(opaque); + + return s->migrate_clk; +} + static const VMStateDescription vmstate_pl011_clock = { .name = "pl011/clock", .version_id = 1, .minimum_version_id = 1, + .needed = pl011_clock_needed, .fields = (VMStateField[]) { VMSTATE_CLOCK(clk, PL011State), VMSTATE_END_OF_LIST() @@ -363,6 +371,7 @@ static const VMStateDescription vmstate_pl011 = { static Property pl011_properties[] = { DEFINE_PROP_CHR("chardev", PL011State, chr), + DEFINE_PROP_BOOL("migrate-clk", PL011State, migrate_clk, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/core/loader.c b/hw/core/loader.c index 9feca32de9..d3e5f3b423 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -1383,6 +1383,81 @@ void *rom_ptr(hwaddr addr, size_t size) return rom->data + (addr - rom->addr); } +typedef struct FindRomCBData { + size_t size; /* Amount of data we want from ROM, in bytes */ + MemoryRegion *mr; /* MR at the unaliased guest addr */ + hwaddr xlat; /* Offset of addr within mr */ + void *rom; /* Output: rom data pointer, if found */ +} FindRomCBData; + +static bool find_rom_cb(Int128 start, Int128 len, const MemoryRegion *mr, + hwaddr offset_in_region, void *opaque) +{ + FindRomCBData *cbdata = opaque; + hwaddr alias_addr; + + if (mr != cbdata->mr) { + return false; + } + + alias_addr = int128_get64(start) + cbdata->xlat - offset_in_region; + cbdata->rom = rom_ptr(alias_addr, cbdata->size); + if (!cbdata->rom) { + return false; + } + /* Found a match, stop iterating */ + return true; +} + +void *rom_ptr_for_as(AddressSpace *as, hwaddr addr, size_t size) +{ + /* + * Find any ROM data for the given guest address range. If there + * is a ROM blob then return a pointer to the host memory + * corresponding to 'addr'; otherwise return NULL. + * + * We look not only for ROM blobs that were loaded directly to + * addr, but also for ROM blobs that were loaded to aliases of + * that memory at other addresses within the AddressSpace. + * + * Note that we do not check @as against the 'as' member in the + * 'struct Rom' returned by rom_ptr(). The Rom::as is the + * AddressSpace which the rom blob should be written to, whereas + * our @as argument is the AddressSpace which we are (effectively) + * reading from, and the same underlying RAM will often be visible + * in multiple AddressSpaces. (A common example is a ROM blob + * written to the 'system' address space but then read back via a + * CPU's cpu->as pointer.) This does mean we might potentially + * return a false-positive match if a ROM blob was loaded into an + * AS which is entirely separate and distinct from the one we're + * querying, but this issue exists also for rom_ptr() and hasn't + * caused any problems in practice. + */ + FlatView *fv; + void *rom; + hwaddr len_unused; + FindRomCBData cbdata = {}; + + /* Easy case: there's data at the actual address */ + rom = rom_ptr(addr, size); + if (rom) { + return rom; + } + + RCU_READ_LOCK_GUARD(); + + fv = address_space_to_flatview(as); + cbdata.mr = flatview_translate(fv, addr, &cbdata.xlat, &len_unused, + false, MEMTXATTRS_UNSPECIFIED); + if (!cbdata.mr) { + /* Nothing at this address, so there can't be any aliasing */ + return NULL; + } + cbdata.size = size; + flatview_for_each_range(fv, find_rom_cb, &cbdata); + return cbdata.rom; +} + void hmp_info_roms(Monitor *mon, const QDict *qdict) { Rom *rom; diff --git a/hw/core/machine.c b/hw/core/machine.c index 257a664ea2..9935c6ddd5 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -52,6 +52,7 @@ GlobalProperty hw_compat_5_1[] = { { "virtio-scsi-device", "num_queues", "1"}, { "nvme", "use-intel-id", "on"}, { "pvpanic", "events", "1"}, /* PVPANIC_PANICKED */ + { "pl011", "migrate-clk", "off" }, }; const size_t hw_compat_5_1_len = G_N_ELEMENTS(hw_compat_5_1); diff --git a/include/exec/memory.h b/include/exec/memory.h index 260ddd8ade..5728a681b2 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -776,11 +776,35 @@ static inline FlatView *address_space_to_flatview(AddressSpace *as) return qatomic_rcu_read(&as->current_map); } -typedef int (*flatview_cb)(Int128 start, - Int128 len, - const MemoryRegion*, void*); +/** + * typedef flatview_cb: callback for flatview_for_each_range() + * + * @start: start address of the range within the FlatView + * @len: length of the range in bytes + * @mr: MemoryRegion covering this range + * @offset_in_region: offset of the first byte of the range within @mr + * @opaque: data pointer passed to flatview_for_each_range() + * + * Returns: true to stop the iteration, false to keep going. + */ +typedef bool (*flatview_cb)(Int128 start, + Int128 len, + const MemoryRegion *mr, + hwaddr offset_in_region, + void *opaque); -void flatview_for_each_range(FlatView *fv, flatview_cb cb , void *opaque); +/** + * flatview_for_each_range: Iterate through a FlatView + * @fv: the FlatView to iterate through + * @cb: function to call for each range + * @opaque: opaque data pointer to pass to @cb + * + * A FlatView is made up of a list of non-overlapping ranges, each of + * which is a slice of a MemoryRegion. This function iterates through + * each range in @fv, calling @cb. The callback function can terminate + * iteration early by returning 'true'. + */ +void flatview_for_each_range(FlatView *fv, flatview_cb cb, void *opaque); /** * struct MemoryRegionSection: describes a fragment of a #MemoryRegion diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h index 33e5e5317b..dc2c90eedc 100644 --- a/include/hw/char/pl011.h +++ b/include/hw/char/pl011.h @@ -50,6 +50,7 @@ struct PL011State { CharBackend chr; qemu_irq irq[6]; Clock *clk; + bool migrate_clk; const unsigned char *id; }; diff --git a/include/hw/loader.h b/include/hw/loader.h index a9eeea3952..cbfc184873 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -290,6 +290,37 @@ void rom_transaction_end(bool commit); int rom_copy(uint8_t *dest, hwaddr addr, size_t size); void *rom_ptr(hwaddr addr, size_t size); +/** + * rom_ptr_for_as: Return a pointer to ROM blob data for the address + * @as: AddressSpace to look for the ROM blob in + * @addr: Address within @as + * @size: size of data required in bytes + * + * Returns: pointer into the data which backs the matching ROM blob, + * or NULL if no blob covers the address range. + * + * This function looks for a ROM blob which covers the specified range + * of bytes of length @size starting at @addr within the address space + * @as. This is useful for code which runs as part of board + * initialization or CPU reset which wants to read data that is part + * of a user-supplied guest image or other guest memory contents, but + * which runs before the ROM loader's reset function has copied the + * blobs into guest memory. + * + * rom_ptr_for_as() will look not just for blobs loaded directly to + * the specified address, but also for blobs which were loaded to an + * alias of the region at a different location in the AddressSpace. + * In other words, if a machine model has RAM at address 0x0000_0000 + * which is aliased to also appear at 0x1000_0000, rom_ptr_for_as() + * will return the correct data whether the guest image was linked and + * loaded at 0x0000_0000 or 0x1000_0000. Contrast rom_ptr(), which + * will only return data if the image load address is an exact match + * with the queried address. + * + * New code should prefer to use rom_ptr_for_as() instead of + * rom_ptr(). + */ +void *rom_ptr_for_as(AddressSpace *as, hwaddr addr, size_t size); void hmp_info_roms(Monitor *mon, const QDict *qdict); #define rom_add_file_fixed(_f, _a, _i) \ diff --git a/softmmu/memory.c b/softmmu/memory.c index c4730ec47a..d4493ef9e4 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -671,8 +671,10 @@ void flatview_for_each_range(FlatView *fv, flatview_cb cb , void *opaque) assert(cb); FOR_EACH_FLAT_RANGE(fr, fv) { - if (cb(fr->addr.start, fr->addr.size, fr->mr, opaque)) + if (cb(fr->addr.start, fr->addr.size, fr->mr, + fr->offset_in_region, opaque)) { break; + } } } diff --git a/target/arm/cpu.c b/target/arm/cpu.c index ae04884408..0dd623e590 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -331,7 +331,7 @@ static void arm_cpu_reset(DeviceState *dev) /* Load the initial SP and PC from offset 0 and 4 in the vector table */ vecbase = env->v7m.vecbase[env->v7m.secure]; - rom = rom_ptr(vecbase, 8); + rom = rom_ptr_for_as(s->as, vecbase, 8); if (rom) { /* Address zero is covered by ROM which hasn't yet been * copied into physical memory. diff --git a/target/arm/tlb_helper.c b/target/arm/tlb_helper.c index 9609333cbd..3107f9823e 100644 --- a/target/arm/tlb_helper.c +++ b/target/arm/tlb_helper.c @@ -163,6 +163,7 @@ bool arm_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } else { fi.type = ARMFault_Translation; } + fi.level = 3; /* now we have a real cpu fault */ cpu_restore_state(cs, retaddr, true); diff --git a/tests/qtest/fuzz/generic_fuzz.c b/tests/qtest/fuzz/generic_fuzz.c index b5fe27aae1..ae219540b4 100644 --- a/tests/qtest/fuzz/generic_fuzz.c +++ b/tests/qtest/fuzz/generic_fuzz.c @@ -98,19 +98,22 @@ struct get_io_cb_info { address_range result; }; -static int get_io_address_cb(Int128 start, Int128 size, - const MemoryRegion *mr, void *opaque) { +static bool get_io_address_cb(Int128 start, Int128 size, + const MemoryRegion *mr, + hwaddr offset_in_region, + void *opaque) +{ struct get_io_cb_info *info = opaque; if (g_hash_table_lookup(fuzzable_memoryregions, mr)) { if (info->index == 0) { info->result.addr = (ram_addr_t)start; info->result.size = (ram_addr_t)size; info->found = 1; - return 1; + return true; } info->index--; } - return 0; + return false; } /*