diff options
author | Alex Bennée <alex.bennee@linaro.org> | 2022-02-25 17:20:20 +0000 |
---|---|---|
committer | Alex Bennée <alex.bennee@linaro.org> | 2022-02-28 16:42:35 +0000 |
commit | 5fc983af8b20d69279f819efef2b340565d0e9a3 (patch) | |
tree | 3cfdbdf77a57ce019760f325aaef7c1ef3db5ba5 /hw/core | |
parent | 04e90c1313fc6422fc3a5066fcc84ef3ef030ebf (diff) |
semihosting/arm-compat: replace heuristic for softmmu SYS_HEAPINFO
The previous numbers were a guess at best and rather arbitrary without
taking into account anything that might be loaded. Instead of using
guesses based on the state of registers implement a new function that:
a) scans the MemoryRegions for the largest RAM block
b) iterates through all "ROM" blobs looking for the biggest gap
The "ROM" blobs include all code loaded via -kernel and the various
-device loader techniques.
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Cc: Andrew Strauss <astrauss11@gmail.com>
Cc: Keith Packard <keithp@keithp.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Message-Id: <20220225172021.3493923-18-alex.bennee@linaro.org>
Diffstat (limited to 'hw/core')
-rw-r--r-- | hw/core/loader.c | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/hw/core/loader.c b/hw/core/loader.c index 19edb928e9..ca2f2431fb 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -1333,6 +1333,92 @@ static Rom *find_rom(hwaddr addr, size_t size) return NULL; } +typedef struct RomSec { + hwaddr base; + int se; /* start/end flag */ +} RomSec; + + +/* + * Sort into address order. We break ties between rom-startpoints + * and rom-endpoints in favour of the startpoint, by sorting the 0->1 + * transition before the 1->0 transition. Either way round would + * work, but this way saves a little work later by avoiding + * dealing with "gaps" of 0 length. + */ +static gint sort_secs(gconstpointer a, gconstpointer b) +{ + RomSec *ra = (RomSec *) a; + RomSec *rb = (RomSec *) b; + + if (ra->base == rb->base) { + return ra->se - rb->se; + } + return ra->base > rb->base ? 1 : -1; +} + +static GList *add_romsec_to_list(GList *secs, hwaddr base, int se) +{ + RomSec *cand = g_new(RomSec, 1); + cand->base = base; + cand->se = se; + return g_list_prepend(secs, cand); +} + +RomGap rom_find_largest_gap_between(hwaddr base, size_t size) +{ + Rom *rom; + RomSec *cand; + RomGap res = {0, 0}; + hwaddr gapstart = base; + GList *it, *secs = NULL; + int count = 0; + + QTAILQ_FOREACH(rom, &roms, next) { + /* Ignore blobs being loaded to special places */ + if (rom->mr || rom->fw_file) { + continue; + } + /* ignore anything finishing bellow base */ + if (rom->addr + rom->romsize <= base) { + continue; + } + /* ignore anything starting above the region */ + if (rom->addr >= base + size) { + continue; + } + + /* Save the start and end of each relevant ROM */ + secs = add_romsec_to_list(secs, rom->addr, 1); + + if (rom->addr + rom->romsize < base + size) { + secs = add_romsec_to_list(secs, rom->addr + rom->romsize, -1); + } + } + + /* sentinel */ + secs = add_romsec_to_list(secs, base + size, 1); + + secs = g_list_sort(secs, sort_secs); + + for (it = g_list_first(secs); it; it = g_list_next(it)) { + cand = (RomSec *) it->data; + if (count == 0 && count + cand->se == 1) { + size_t gap = cand->base - gapstart; + if (gap > res.size) { + res.base = gapstart; + res.size = gap; + } + } else if (count == 1 && count + cand->se == 0) { + gapstart = cand->base; + } + count += cand->se; + } + + g_list_free_full(secs, g_free); + return res; +} + /* * Copies memory from registered ROMs to dest. Any memory that is contained in * a ROM between addr and addr + size is copied. Note that this can involve |