[PATCH v7 13/24] whpx: copy over memory management logic from hvf

Mohamed Mediouni
2025-10-16T16:57:22Z

This allows edk2 to work, although u-boot is still not functional.

Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
accel/whpx/whpx-common.c | 201 ++++++++++++++++++++++++++++-----------
1 file changed, 147 insertions(+), 54 deletions(-)

diff --git a/accel/whpx/whpx-common.c b/accel/whpx/whpx-common.c
index c69792e638..f324f5c146 100644
--- a/accel/whpx/whpx-common.c
+++ b/accel/whpx/whpx-common.c
@@ -258,89 +258,174 @@ void whpx_vcpu_kick(CPUState *cpu)
* Memory support.
*/

-static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size,
- void *host_va, int add, int rom,
- const char *name)
+ /* whpx_slot flags */
+#define WHPX_SLOT_LOG (1 << 0)
+typedef struct whpx_slot {
+ uint64_t start;
+ uint64_t size;
+ uint8_t *mem;
+ int slot_id;
+ uint32_t flags;
+ MemoryRegion *region;
+} whpx_slot;
+
+typedef struct WHPXState {
+ whpx_slot slots[32];
+ int num_slots;
+} WHPXState;
+
+ WHPXState *whpx_state;
+
+ struct mac_slot {
+ int present;
+ uint64_t size;
+ uint64_t gpa_start;
+ uint64_t gva;
+};
+
+struct mac_slot mac_slots[32];
+
+static int do_whpx_set_memory(whpx_slot *slot, WHV_MAP_GPA_RANGE_FLAGS flags)
{
struct whpx_state *whpx = &whpx_global;
+ struct mac_slot *macslot;
HRESULT hr;

- /*
- if (add) {
- printf("WHPX: ADD PA:%p Size:%p, Host:%p, %s, '%s'\n",
- (void*)start_pa, (void*)size, host_va,
- (rom ? "ROM" : "RAM"), name);
- } else {
- printf("WHPX: DEL PA:%p Size:%p, Host:%p, '%s'\n",
- (void*)start_pa, (void*)size, host_va, name);
+ macslot = &mac_slots[slot->slot_id];
+
+ if (macslot->present) {
+ if (macslot->size != slot->size) {
+ macslot->present = 0;
+ hr = whp_dispatch.WHvUnmapGpaRange(whpx->partition,
+ macslot->gpa_start, macslot->size);
+ if (FAILED(hr)) {
+ abort();
+ }
+ }
}
- */
-
- if (add) {
- hr = whp_dispatch.WHvMapGpaRange(whpx->partition,
- host_va,
- start_pa,
- size,
- (WHvMapGpaRangeFlagRead |
- WHvMapGpaRangeFlagExecute |
- (rom ? 0 : WHvMapGpaRangeFlagWrite)));
- } else {
- hr = whp_dispatch.WHvUnmapGpaRange(whpx->partition,
- start_pa,
- size);
+
+ if (!slot->size) {
+ return 0;
}

- if (FAILED(hr)) {
- error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes,"
- " Host:%p, hr=%08lx",
- (add ? "MAP" : "UNMAP"), name,
- (void *)(uintptr_t)start_pa, (void *)size, host_va, hr);
+ macslot->present = 1;
+ macslot->gpa_start = slot->start;
+ macslot->size = slot->size;
+ hr = whp_dispatch.WHvMapGpaRange(whpx->partition,
+ slot->mem, slot->start, slot->size, flags);
+ return 0;
+}
+
+static whpx_slot *whpx_find_overlap_slot(uint64_t start, uint64_t size)
+{
+ whpx_slot *slot;
+ int x;
+ for (x = 0; x < whpx_state->num_slots; ++x) {
+ slot = &whpx_state->slots[x];
+ if (slot->size && start < (slot->start + slot->size) &&
+ (start + size) > slot->start) {
+ return slot;
+ }
}
+ return NULL;
}

-static void whpx_process_section(MemoryRegionSection *section, int add)
+static void whpx_set_phys_mem(MemoryRegionSection *section, bool add)
{
- MemoryRegion *mr = section->mr;
- hwaddr start_pa = section->offset_within_address_space;
- ram_addr_t size = int128_get64(section->size);
- unsigned int delta;
- uint64_t host_va;
+ whpx_slot *mem;
+ MemoryRegion *area = section->mr;
+ bool writable = !area->readonly && !area->rom_device;
+ WHV_MAP_GPA_RANGE_FLAGS flags;
+ uint64_t page_size = qemu_real_host_page_size();
+
+ if (!memory_region_is_ram(area)) {
+ if (writable) {
+ return;
+ } else if (!memory_region_is_romd(area)) {
+ /*
+ * If the memory device is not in romd_mode, then we actually want
+ * to remove the whpx memory slot so all accesses will trap.
+ */
+ add = false;
+ }
+ }

- if (!memory_region_is_ram(mr)) {
- return;
+ if (!QEMU_IS_ALIGNED(int128_get64(section->size), page_size) ||
+ !QEMU_IS_ALIGNED(section->offset_within_address_space, page_size)) {
+ /* Not page aligned, so we can not map as RAM */
+ add = false;
}

- delta = qemu_real_host_page_size() - (start_pa & ~qemu_real_host_page_mask());
- delta &= ~qemu_real_host_page_mask();
- if (delta > size) {
- return;
+ mem = whpx_find_overlap_slot(
+ section->offset_within_address_space,
+ int128_get64(section->size));
+
+ if (mem && add) {
+ if (mem->size == int128_get64(section->size) &&
+ mem->start == section->offset_within_address_space &&
+ mem->mem == (memory_region_get_ram_ptr(area) +
+ section->offset_within_region)) {
+ return; /* Same region was attempted to register, go away. */
+ }
+ }
+
+ /* Region needs to be reset. set the size to 0 and remap it. */
+ if (mem) {
+ mem->size = 0;
+ if (do_whpx_set_memory(mem, 0)) {
+ error_report("Failed to reset overlapping slot");
+ abort();
+ }
}
- start_pa += delta;
- size -= delta;
- size &= qemu_real_host_page_mask();
- if (!size || (start_pa & ~qemu_real_host_page_mask())) {
+
+ if (!add) {
return;
}

- host_va = (uintptr_t)memory_region_get_ram_ptr(mr)
- + section->offset_within_region + delta;
+ if (area->readonly ||
+ (!memory_region_is_ram(area) && memory_region_is_romd(area))) {
+ flags = WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagExecute;
+ } else {
+ flags = WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagWrite
+ | WHvMapGpaRangeFlagExecute;
+ }
+
+ /* Now make a new slot. */
+ int x;
+
+ for (x = 0; x < whpx_state->num_slots; ++x) {
+ mem = &whpx_state->slots[x];
+ if (!mem->size) {
+ break;
+ }
+ }
+
+ if (x == whpx_state->num_slots) {
+ error_report("No free slots");
+ abort();
+ }

- whpx_update_mapping(start_pa, size, (void *)(uintptr_t)host_va, add,
- memory_region_is_rom(mr), mr->name);
+ mem->size = int128_get64(section->size);
+ mem->mem = memory_region_get_ram_ptr(area) + section->offset_within_region;
+ mem->start = section->offset_within_address_space;
+ mem->region = area;
+
+ if (do_whpx_set_memory(mem, flags)) {
+ error_report("Error registering new memory slot");
+ abort();
+ }
}

static void whpx_region_add(MemoryListener *listener,
MemoryRegionSection *section)
{
- memory_region_ref(section->mr);
- whpx_process_section(section, 1);
+ whpx_set_phys_mem(section, true);
}

static void whpx_region_del(MemoryListener *listener,
MemoryRegionSection *section)
{
- whpx_process_section(section, 0);
- memory_region_unref(section->mr);
+ whpx_set_phys_mem(section, false);
}

static void whpx_transaction_begin(MemoryListener *listener)
@@ -524,6 +609,14 @@ static void whpx_accel_instance_init(Object *obj)
memset(whpx, 0, sizeof(struct whpx_state));
/* Turn on kernel-irqchip, by default */
whpx->kernel_irqchip_allowed = true;
+
+ int x;
+ whpx_state = malloc(sizeof(WHPXState));
+ whpx_state->num_slots = ARRAY_SIZE(whpx_state->slots);
+ for (x = 0; x < whpx_state->num_slots; ++x) {
+ whpx_state->slots[x].size = 0;
+ whpx_state->slots[x].slot_id = x;
+ }
}

static const TypeInfo whpx_accel_type = {
--
2.50.1 (Apple Git-155)