diff options
Diffstat (limited to 'hw/core/loader.c')
-rw-r--r-- | hw/core/loader.c | 75 |
1 files changed, 75 insertions, 0 deletions
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; |