[PATCH v2 1/2] accel/hvf: introduce map granule abstraction and IPA property

Lucas Amaral posted 2 patches 4 weeks ago
Maintainers: Cameron Esfahani <dirty@apple.com>, Roman Bolshakov <rbolshakov@ddn.com>, Phil Dennis-Jordan <phil@philjordan.eu>, Alexander Graf <agraf@csgraf.de>, Peter Maydell <peter.maydell@linaro.org>
[PATCH v2 1/2] accel/hvf: introduce map granule abstraction and IPA property
Posted by Lucas Amaral 4 weeks ago
Replace hardcoded qemu_real_host_page_size() in HVF memory mapping
with a configurable map granule.  Non-aligned regions now return
early instead of proceeding with add=false.

Add an 'ipa-granule' property (auto, 4k, 16k) on the HVF accelerator
object for aarch64, allowing the user to select the IPA page granule
for HVF stage-2 address translation:

  -accel hvf,ipa-granule=4k

'auto' (the default) resolves to the host page size (16KB on macOS
ARM64).  The actual HVF configuration is handled by the arch-specific
hvf_arch_vm_create() in a subsequent patch.

Signed-off-by: Lucas Amaral <lucaaamaral@gmail.com>
---
 accel/hvf/hvf-all.c      | 67 ++++++++++++++++++++++++++++++++++++++--
 include/system/hvf.h     | 15 +++++++++
 include/system/hvf_int.h |  1 +
 3 files changed, 80 insertions(+), 3 deletions(-)

diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c
index 033c677..d94926b 100644
--- a/accel/hvf/hvf-all.c
+++ b/accel/hvf/hvf-all.c
@@ -10,6 +10,8 @@
 
 #include "qemu/osdep.h"
 #include "qemu/error-report.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
 #include "accel/accel-ops.h"
 #include "exec/cpu-common.h"
 #include "system/address-spaces.h"
@@ -23,6 +25,21 @@
 
 bool hvf_allowed;
 
+static uint64_t hvf_map_granule;
+
+void hvf_set_map_granule(uint64_t size)
+{
+    hvf_map_granule = size;
+}
+
+uint64_t hvf_get_map_granule(void)
+{
+    if (!hvf_map_granule) {
+        return qemu_real_host_page_size();
+    }
+    return hvf_map_granule;
+}
+
 const char *hvf_return_string(hv_return_t ret)
 {
     switch (ret) {
@@ -54,7 +71,7 @@ void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line,
 static void do_hv_vm_protect(hwaddr start, size_t size,
                              hv_memory_flags_t flags)
 {
-    intptr_t page_mask = qemu_real_host_page_mask();
+    intptr_t page_mask = -(intptr_t)hvf_get_map_granule();
     hv_return_t ret;
 
     trace_hvf_vm_protect(start, size, flags,
@@ -84,7 +101,7 @@ static void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
     MemoryRegion *area = section->mr;
     bool writable = !area->readonly && !area->rom_device;
     hv_memory_flags_t flags;
-    uint64_t page_size = qemu_real_host_page_size();
+    uint64_t page_size = hvf_get_map_granule();
     uint64_t gpa = section->offset_within_address_space;
     uint64_t size = int128_get64(section->size);
     hv_return_t ret;
@@ -105,7 +122,7 @@ static void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
     if (!QEMU_IS_ALIGNED(size, page_size) ||
         !QEMU_IS_ALIGNED(gpa, page_size)) {
         /* Not page aligned, so we can not map as RAM */
-        add = false;
+        return;
     }
 
     if (!add) {
@@ -187,6 +204,11 @@ static int hvf_accel_init(AccelState *as, MachineState *ms)
     int pa_range = 36;
     MachineClass *mc = MACHINE_GET_CLASS(ms);
 
+    /* Resolve ipa-granule=auto → host page size */
+    if (!s->ipa_granule) {
+        s->ipa_granule = qemu_real_host_page_size();
+    }
+    hvf_set_map_granule(s->ipa_granule);
 
     if (mc->get_physical_address_range) {
         pa_range = mc->get_physical_address_range(ms,
@@ -217,6 +239,37 @@ static int hvf_gdbstub_sstep_flags(AccelState *as)
     return SSTEP_ENABLE | SSTEP_NOIRQ;
 }
 
+#ifdef __aarch64__
+static char *hvf_get_ipa_granule(Object *obj, Error **errp)
+{
+    HVFState *s = HVF_STATE(obj);
+
+    if (s->ipa_granule == 4 * KiB) {
+        return g_strdup("4k");
+    }
+    if (s->ipa_granule == 16 * KiB) {
+        return g_strdup("16k");
+    }
+    return g_strdup("auto");
+}
+
+static void hvf_set_ipa_granule(Object *obj, const char *value, Error **errp)
+{
+    HVFState *s = HVF_STATE(obj);
+
+    if (!strcmp(value, "auto")) {
+        s->ipa_granule = 0;
+    } else if (!strcmp(value, "4k")) {
+        s->ipa_granule = 4 * KiB;
+    } else if (!strcmp(value, "16k")) {
+        s->ipa_granule = 16 * KiB;
+    } else {
+        error_setg(errp, "invalid ipa-granule: '%s' (use auto, 4k, 16k)",
+                   value);
+    }
+}
+#endif /* __aarch64__ */
+
 static void hvf_accel_class_init(ObjectClass *oc, const void *data)
 {
     AccelClass *ac = ACCEL_CLASS(oc);
@@ -224,6 +277,14 @@ 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;
+
+#ifdef __aarch64__
+    object_class_property_add_str(oc, "ipa-granule",
+                                  hvf_get_ipa_granule,
+                                  hvf_set_ipa_granule);
+    object_class_property_set_description(oc, "ipa-granule",
+        "IPA granule for HVF stage-2 translation (auto, 4k, 16k)");
+#endif
 }
 
 static const TypeInfo hvf_accel_type = {
diff --git a/include/system/hvf.h b/include/system/hvf.h
index d3dcf08..d1b702b 100644
--- a/include/system/hvf.h
+++ b/include/system/hvf.h
@@ -36,4 +36,19 @@ typedef struct HVFState HVFState;
 DECLARE_INSTANCE_CHECKER(HVFState, HVF_STATE,
                          TYPE_HVF_ACCEL)
 
+#ifdef CONFIG_HVF_IS_POSSIBLE
+/*
+ * Minimum alignment for hv_vm_map(). Returns host page size (16KB on
+ * macOS ARM64), or 4KB when HVF 4KB IPA granule is configured (macOS 26+).
+ */
+void hvf_set_map_granule(uint64_t size);
+uint64_t hvf_get_map_granule(void);
+#else
+static inline void hvf_set_map_granule(uint64_t size) {}
+static inline uint64_t hvf_get_map_granule(void)
+{
+    return qemu_real_host_page_size();
+}
+#endif
+
 #endif
diff --git a/include/system/hvf_int.h b/include/system/hvf_int.h
index 2621164..4c1caba 100644
--- a/include/system/hvf_int.h
+++ b/include/system/hvf_int.h
@@ -38,6 +38,7 @@ struct HVFState {
 
     hvf_vcpu_caps *hvf_caps;
     uint64_t vtimer_offset;
+    uint32_t ipa_granule;
     QTAILQ_HEAD(, hvf_sw_breakpoint) hvf_sw_breakpoints;
 };
 extern HVFState *hvf_state;
-- 
2.52.0


Re: [PATCH v2 1/2] accel/hvf: introduce map granule abstraction and IPA property
Posted by Mohamed Mediouni 4 weeks ago

> On 11. Mar 2026, at 03:27, Lucas Amaral <lucaaamaral@gmail.com> wrote:
> 
> Replace hardcoded qemu_real_host_page_size() in HVF memory mapping
> with a configurable map granule.  Non-aligned regions now return
> early instead of proceeding with add=false.
> 
> Add an 'ipa-granule' property (auto, 4k, 16k) on the HVF accelerator
> object for aarch64, allowing the user to select the IPA page granule
> for HVF stage-2 address translation:
> 
>  -accel hvf,ipa-granule=4k
> 
> 'auto' (the default) resolves to the host page size (16KB on macOS
> ARM64).  The actual HVF configuration is handled by the arch-specific
> hvf_arch_vm_create() in a subsequent patch.
> 
> Signed-off-by: Lucas Amaral <lucaaamaral@gmail.com>
> ---
> accel/hvf/hvf-all.c      | 67 ++++++++++++++++++++++++++++++++++++++--
> include/system/hvf.h     | 15 +++++++++
> include/system/hvf_int.h |  1 +
> 3 files changed, 80 insertions(+), 3 deletions(-)
> 
> diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c
> index 033c677..d94926b 100644
> --- a/accel/hvf/hvf-all.c
> +++ b/accel/hvf/hvf-all.c
> @@ -10,6 +10,8 @@
> 
> #include "qemu/osdep.h"
> #include "qemu/error-report.h"
> +#include "qemu/units.h"
> +#include "qapi/error.h"
> #include "accel/accel-ops.h"
> #include "exec/cpu-common.h"
> #include "system/address-spaces.h"
> @@ -23,6 +25,21 @@
> 
> bool hvf_allowed;
> 
> +static uint64_t hvf_map_granule;
> +
> +void hvf_set_map_granule(uint64_t size)
> +{
> +    hvf_map_granule = size;
> +}
> +
> +uint64_t hvf_get_map_granule(void)
> +{
> +    if (!hvf_map_granule) {
> +        return qemu_real_host_page_size();
> +    }
> +    return hvf_map_granule;
> +}
> +
> const char *hvf_return_string(hv_return_t ret)
> {
>     switch (ret) {
> @@ -54,7 +71,7 @@ void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line,
> static void do_hv_vm_protect(hwaddr start, size_t size,
>                              hv_memory_flags_t flags)
> {
> -    intptr_t page_mask = qemu_real_host_page_mask();
> +    intptr_t page_mask = -(intptr_t)hvf_get_map_granule();
>     hv_return_t ret;
> 
>     trace_hvf_vm_protect(start, size, flags,
> @@ -84,7 +101,7 @@ static void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
>     MemoryRegion *area = section->mr;
>     bool writable = !area->readonly && !area->rom_device;
>     hv_memory_flags_t flags;
> -    uint64_t page_size = qemu_real_host_page_size();
> +    uint64_t page_size = hvf_get_map_granule();
>     uint64_t gpa = section->offset_within_address_space;
>     uint64_t size = int128_get64(section->size);
>     hv_return_t ret;
> @@ -105,7 +122,7 @@ static void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
>     if (!QEMU_IS_ALIGNED(size, page_size) ||
>         !QEMU_IS_ALIGNED(gpa, page_size)) {
>         /* Not page aligned, so we can not map as RAM */
> -        add = false;
> +        return;
>     }
> 
>     if (!add) {
> @@ -187,6 +204,11 @@ static int hvf_accel_init(AccelState *as, MachineState *ms)
>     int pa_range = 36;
>     MachineClass *mc = MACHINE_GET_CLASS(ms);
> 
> +    /* Resolve ipa-granule=auto → host page size */
> +    if (!s->ipa_granule) {
> +        s->ipa_granule = qemu_real_host_page_size();
> +    }
> +    hvf_set_map_granule(s->ipa_granule);
> 
>     if (mc->get_physical_address_range) {
>         pa_range = mc->get_physical_address_range(ms,
> @@ -217,6 +239,37 @@ static int hvf_gdbstub_sstep_flags(AccelState *as)
>     return SSTEP_ENABLE | SSTEP_NOIRQ;
> }
> 
> +#ifdef __aarch64__
Hello,

checkpatch will warn on this, maybe this should be in a separate potential hvf_arch_accel_init?

> +static char *hvf_get_ipa_granule(Object *obj, Error **errp)
> +{
> +    HVFState *s = HVF_STATE(obj);
> +
> +    if (s->ipa_granule == 4 * KiB) {
> +        return g_strdup("4k");
> +    }
> +    if (s->ipa_granule == 16 * KiB) {
> +        return g_strdup("16k");
> +    }
> +    return g_strdup("auto");
> +}
> +
> +static void hvf_set_ipa_granule(Object *obj, const char *value, Error **errp)
> +{
> +    HVFState *s = HVF_STATE(obj);
> +
> +    if (!strcmp(value, "auto")) {
> +        s->ipa_granule = 0;
> +    } else if (!strcmp(value, "4k")) {
> +        s->ipa_granule = 4 * KiB;
> +    } else if (!strcmp(value, "16k")) {
> +        s->ipa_granule = 16 * KiB;
> +    } else {
> +        error_setg(errp, "invalid ipa-granule: '%s' (use auto, 4k, 16k)",
> +                   value);
> +    }
> +}
> +#endif /* __aarch64__ */
> +
> static void hvf_accel_class_init(ObjectClass *oc, const void *data)
> {
>     AccelClass *ac = ACCEL_CLASS(oc);
> @@ -224,6 +277,14 @@ 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;
> +
> +#ifdef __aarch64__
> +    object_class_property_add_str(oc, "ipa-granule",
> +                                  hvf_get_ipa_granule,
> +                                  hvf_set_ipa_granule);
> +    object_class_property_set_description(oc, "ipa-granule",
> +        "IPA granule for HVF stage-2 translation (auto, 4k, 16k)");
> +#endif
> }
> 
> static const TypeInfo hvf_accel_type = {
> diff --git a/include/system/hvf.h b/include/system/hvf.h
> index d3dcf08..d1b702b 100644
> --- a/include/system/hvf.h
> +++ b/include/system/hvf.h
> @@ -36,4 +36,19 @@ typedef struct HVFState HVFState;
> DECLARE_INSTANCE_CHECKER(HVFState, HVF_STATE,
>                          TYPE_HVF_ACCEL)
> 
> +#ifdef CONFIG_HVF_IS_POSSIBLE
> +/*
> + * Minimum alignment for hv_vm_map(). Returns host page size (16KB on
> + * macOS ARM64), or 4KB when HVF 4KB IPA granule is configured (macOS 26+).
> + */
> +void hvf_set_map_granule(uint64_t size);
> +uint64_t hvf_get_map_granule(void);
> +#else
> +static inline void hvf_set_map_granule(uint64_t size) {}
> +static inline uint64_t hvf_get_map_granule(void)
> +{
> +    return qemu_real_host_page_size();
> +}
> +#endif
> +
> #endif
> diff --git a/include/system/hvf_int.h b/include/system/hvf_int.h
> index 2621164..4c1caba 100644
> --- a/include/system/hvf_int.h
> +++ b/include/system/hvf_int.h
> @@ -38,6 +38,7 @@ struct HVFState {
> 
>     hvf_vcpu_caps *hvf_caps;
>     uint64_t vtimer_offset;
> +    uint32_t ipa_granule;
>     QTAILQ_HEAD(, hvf_sw_breakpoint) hvf_sw_breakpoints;
> };
> extern HVFState *hvf_state;
> -- 
> 2.52.0
> 
>