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
> 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
>
>
© 2016 - 2026 Red Hat, Inc.