The IPA granule is the smallest page size hv_vm_map() support. For Venus, we
need to support 4KiB pages. macOS 26 introduces a public API for setting
the granule size. We can only use this when compiled with macOS 26 SDK and
run on macOS 26+. Otherwise, we fall back to an older, private, API which
achieves the same purpose.

Signed-off-by: Joelle van Dyne <[email protected]>
---
 include/system/hvf_int.h |  4 +++-
 accel/hvf/hvf-all.c      | 42 ++++++++++++++++++++++++++++++++-
 target/arm/hvf/hvf.c     | 50 +++++++++++++++++++++++++++++++++++++++-
 target/i386/hvf/hvf.c    | 10 +++++++-
 4 files changed, 102 insertions(+), 4 deletions(-)

diff --git a/include/system/hvf_int.h b/include/system/hvf_int.h
index 1d2616595cd..881e16e31b6 100644
--- a/include/system/hvf_int.h
+++ b/include/system/hvf_int.h
@@ -63,6 +63,7 @@ struct HVFState {
 
     hvf_vcpu_caps *hvf_caps;
     uint64_t vtimer_offset;
+    uint32_t ipa_granule_size;
     QTAILQ_HEAD(, hvf_sw_breakpoint) hvf_sw_breakpoints;
 };
 extern HVFState *hvf_state;
@@ -82,7 +83,8 @@ void assert_hvf_ok_impl(hv_return_t ret, const char *file, 
unsigned int line,
 #define assert_hvf_ok(EX) assert_hvf_ok_impl((EX), __FILE__, __LINE__, #EX)
 const char *hvf_return_string(hv_return_t ret);
 int hvf_arch_init(void);
-hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range);
+hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range,
+                               uint32_t ipa_granule_size);
 hvf_slot *hvf_find_overlap_slot(uint64_t, uint64_t);
 void hvf_kick_vcpu_thread(CPUState *cpu);
 
diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c
index a898359777c..7ebfc9bbe58 100644
--- a/accel/hvf/hvf-all.c
+++ b/accel/hvf/hvf-all.c
@@ -17,6 +17,8 @@
 #include "system/hvf_int.h"
 #include "hw/core/cpu.h"
 #include "hw/boards.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
 #include "trace.h"
 
 bool hvf_allowed;
@@ -152,6 +154,10 @@ static void hvf_set_phys_mem(MemoryRegionSection *section, 
bool add)
         }
     }
 
+    if (hvf_state->ipa_granule_size) {
+        page_size = hvf_state->ipa_granule_size;
+    }
+
     if (!QEMU_IS_ALIGNED(int128_get64(section->size), page_size) ||
         !QEMU_IS_ALIGNED(section->offset_within_address_space, page_size)) {
         if (add) {
@@ -316,7 +322,7 @@ static int hvf_accel_init(AccelState *as, MachineState *ms)
         }
     }
 
-    ret = hvf_arch_vm_create(ms, (uint32_t)pa_range);
+    ret = hvf_arch_vm_create(ms, (uint32_t)pa_range, s->ipa_granule_size);
     if (ret == HV_DENIED) {
         error_report("Could not access HVF. Is the executable signed"
                      " with com.apple.security.hypervisor entitlement?");
@@ -340,6 +346,34 @@ static int hvf_gdbstub_sstep_flags(AccelState *as)
     return SSTEP_ENABLE | SSTEP_NOIRQ;
 }
 
+static void hvf_get_ipa_granule_size(Object *obj, Visitor *v,
+                                    const char *name, void *opaque,
+                                    Error **errp)
+{
+    HVFState *s = HVF_STATE(obj);
+    uint32_t value = s->ipa_granule_size;
+
+    visit_type_uint32(v, name, &value, errp);
+}
+
+static void hvf_set_ipa_granule_size(Object *obj, Visitor *v,
+                                     const char *name, void *opaque,
+                                     Error **errp)
+{
+    HVFState *s = HVF_STATE(obj);
+    uint32_t value;
+
+    if (!visit_type_uint32(v, name, &value, errp)) {
+        return;
+    }
+    if (value & (value - 1)) {
+        error_setg(errp, "ipa-granule-size must be a power of two.");
+        return;
+    }
+
+    s->ipa_granule_size = value;
+}
+
 static void hvf_accel_class_init(ObjectClass *oc, const void *data)
 {
     AccelClass *ac = ACCEL_CLASS(oc);
@@ -347,6 +381,12 @@ static void hvf_accel_class_init(ObjectClass *oc, const 
void *data)
     ac->init_machine = hvf_accel_init;
     ac->allowed = &hvf_allowed;
     ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags;
+
+    object_class_property_add(oc, "ipa-granule-size", "uint32",
+        hvf_get_ipa_granule_size, hvf_set_ipa_granule_size,
+        NULL, NULL);
+    object_class_property_set_description(oc, "ipa-granule-size",
+        "Size of a single guest page");
 }
 
 static const TypeInfo hvf_accel_type = {
diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
index de1e8fb8a05..7c44325ca64 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -12,6 +12,9 @@
 #include "qemu/osdep.h"
 #include "qemu/error-report.h"
 #include "qemu/log.h"
+#include <dlfcn.h>
+#include <AvailabilityMacros.h>
+#include <TargetConditionals.h>
 
 #include "system/runstate.h"
 #include "system/hvf.h"
@@ -880,7 +883,45 @@ void hvf_arch_vcpu_destroy(CPUState *cpu)
     assert_hvf_ok(ret);
 }
 
-hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range)
+static hv_return_t hvf_set_ipa_granule(hv_vm_config_t config,
+                                uint32_t ipa_granule_size)
+{
+    static hv_return_t (*set_ipa_granule)(hv_vm_config_t, uint32_t);
+    uint64_t page_size = qemu_real_host_page_size();
+
+    /* macOS 26 introduces a public API for setting granule size */
+#if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && defined(MAC_OS_VERSION_26_0) && \
+    MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_26_0
+    if (__builtin_available(macOS 26, *)) {
+        hv_ipa_granule_t granule = HV_IPA_GRANULE_16KB;
+
+        if (ipa_granule_size == 4096) {
+            granule = HV_IPA_GRANULE_4KB;
+        } else if (ipa_granule_size != 16384) {
+            error_report("Unsupported granule size: 0x%x", ipa_granule_size);
+            return HV_UNSUPPORTED;
+        }
+
+        return hv_vm_config_set_ipa_granule(config, granule);
+    }
+#endif
+
+    /* older macOS need to use a private API */
+    if (!set_ipa_granule) {
+        set_ipa_granule = dlsym(RTLD_NEXT, "_hv_vm_config_set_ipa_granule");
+    }
+    if (set_ipa_granule) {
+        return set_ipa_granule(config, ipa_granule_size);
+    } else if (ipa_granule_size != page_size) {
+        error_report("Failed to find _hv_vm_config_set_ipa_granule");
+        return HV_UNSUPPORTED;
+    }
+
+    return HV_SUCCESS;
+}
+
+hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range,
+                               uint32_t ipa_granule_size)
 {
     hv_return_t ret;
     hv_vm_config_t config = hv_vm_config_create();
@@ -891,6 +932,13 @@ hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t 
pa_range)
     }
     chosen_ipa_bit_size = pa_range;
 
+    if (ipa_granule_size) {
+        ret = hvf_set_ipa_granule(config, ipa_granule_size);
+        if (ret != HV_SUCCESS) {
+            goto cleanup;
+        }
+    }
+
     ret = hv_vm_create(config);
 
 cleanup:
diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c
index 16febbac48f..395e13f467e 100644
--- a/target/i386/hvf/hvf.c
+++ b/target/i386/hvf/hvf.c
@@ -225,8 +225,16 @@ int hvf_arch_init(void)
     return 0;
 }
 
-hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range)
+hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range,
+                               uint32_t ipa_granule_size)
 {
+    uint64_t page_size = qemu_real_host_page_size();
+
+    if (ipa_granule_size != 0 && ipa_granule_size != page_size) {
+        error_report("Only supported IPA granule size: 0x%llx", page_size);
+        return HV_UNSUPPORTED;
+    }
+
     return hv_vm_create(HV_VM_DEFAULT);
 }
 
-- 
2.50.1 (Apple Git-155)


Reply via email to