Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
accel/hvf/hvf-all.c | 1 +
accel/stubs/hvf-stub.c | 1 +
hw/arm/virt.c | 5 +++++
include/system/hvf.h | 5 +++++
target/arm/hvf/hvf.c | 42 ++++++++++++++++++++++++++++++++++++++++--
5 files changed, 52 insertions(+), 2 deletions(-)
diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c
index a296b108bc..1c1437b467 100644
--- a/accel/hvf/hvf-all.c
+++ b/accel/hvf/hvf-all.c
@@ -24,6 +24,7 @@
bool hvf_allowed;
bool hvf_kernel_irqchip;
+bool hvf_nested_virt;
const char *hvf_return_string(hv_return_t ret)
{
diff --git a/accel/stubs/hvf-stub.c b/accel/stubs/hvf-stub.c
index 6bd08759ba..cec1cbb056 100644
--- a/accel/stubs/hvf-stub.c
+++ b/accel/stubs/hvf-stub.c
@@ -11,3 +11,4 @@
bool hvf_allowed;
bool hvf_kernel_irqchip;
+bool hvf_nested_virt;
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 7a6fad1094..90769936d0 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2661,6 +2661,11 @@ static void virt_set_virt(Object *obj, bool value, Error **errp)
VirtMachineState *vms = VIRT_MACHINE(obj);
vms->virt = value;
+ /*
+ * At this point, HVF is not initialised yet.
+ * However, it needs to know if nested virt is enabled at init time.
+ */
+ hvf_nested_virt_enable(value);
}
static bool virt_get_highmem(Object *obj, Error **errp)
diff --git a/include/system/hvf.h b/include/system/hvf.h
index dc8da85979..0f0632f7ae 100644
--- a/include/system/hvf.h
+++ b/include/system/hvf.h
@@ -28,9 +28,14 @@ extern bool hvf_allowed;
#define hvf_enabled() (hvf_allowed)
extern bool hvf_kernel_irqchip;
#define hvf_irqchip_in_kernel() (hvf_kernel_irqchip)
+extern bool hvf_nested_virt;
+#define hvf_nested_virt_enabled() (hvf_nested_virt)
+#define hvf_nested_virt_enable(enable) hvf_nested_virt = enable
#else /* !CONFIG_HVF_IS_POSSIBLE */
#define hvf_enabled() 0
#define hvf_irqchip_in_kernel() 0
+#define hvf_nested_virt_enabled() 0
+#define hvf_nested_virt_enable(enable) 0
#endif /* !CONFIG_HVF_IS_POSSIBLE */
#define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf")
diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
index d431d96ba3..d6ef4488fa 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -27,6 +27,7 @@
#include "system/memory.h"
#include "hw/core/boards.h"
#include "hw/core/irq.h"
+#include "hw/arm/virt.h"
#include "qemu/main-loop.h"
#include "system/cpus.h"
#include "arm-powerctl.h"
@@ -1103,6 +1104,10 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
(1ULL << ARM_FEATURE_PMU) |
(1ULL << ARM_FEATURE_GENERIC_TIMER);
+ if (hvf_nested_virt_enabled()) {
+ ahcf->features |= 1ULL << ARM_FEATURE_EL2;
+ }
+
for (i = 0; i < ARRAY_SIZE(regs); i++) {
r |= hv_vcpu_config_get_feature_reg(config, regs[i].reg,
&host_isar.idregs[regs[i].index]);
@@ -1218,6 +1223,19 @@ void hvf_arch_vcpu_destroy(CPUState *cpu)
assert_hvf_ok(ret);
}
+static bool hvf_arm_el2_supported(void)
+{
+ bool is_nested_virt_supported;
+ if (__builtin_available(macOS 15.0, *)) {
+ hv_return_t ret = hv_vm_config_get_el2_supported(&is_nested_virt_supported);
+ assert_hvf_ok(ret);
+ } else {
+ return false;
+ }
+ return is_nested_virt_supported;
+}
+
+
hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range)
{
hv_return_t ret;
@@ -1229,6 +1247,20 @@ hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range)
}
chosen_ipa_bit_size = pa_range;
+ if (__builtin_available(macOS 15.0, *)) {
+ if (hvf_nested_virt_enabled()) {
+ if (!hvf_arm_el2_supported()) {
+ error_report("Nested virtualization not supported on this system.");
+ goto cleanup;
+ }
+ ret = hv_vm_config_set_el2_enabled(config, true);
+ if (ret != HV_SUCCESS) {
+ error_report("Failed to enable nested virtualization.");
+ goto cleanup;
+ }
+ }
+ }
+
ret = hv_vm_create(config);
if (hvf_irqchip_in_kernel()) {
if (__builtin_available(macOS 15.0, *)) {
@@ -1420,6 +1452,13 @@ static void hvf_psci_cpu_off(ARMCPU *arm_cpu)
assert(ret == QEMU_ARM_POWERCTL_RET_SUCCESS);
}
+static int hvf_psci_get_target_el(void)
+{
+ if (hvf_nested_virt_enabled()) {
+ return 2;
+ }
+ return 1;
+}
/*
* Handle a PSCI call.
*
@@ -1441,7 +1480,6 @@ static bool hvf_handle_psci_call(CPUState *cpu, int *excp_ret)
CPUState *target_cpu_state;
ARMCPU *target_cpu;
target_ulong entry;
- int target_el = 1;
int32_t ret = 0;
trace_arm_psci_call(param[0], param[1], param[2], param[3],
@@ -1495,7 +1533,7 @@ static bool hvf_handle_psci_call(CPUState *cpu, int *excp_ret)
entry = param[2];
context_id = param[3];
ret = arm_set_cpu_on(mpidr, entry, context_id,
- target_el, target_aarch64);
+ hvf_psci_get_target_el(), target_aarch64);
break;
case QEMU_PSCI_0_1_FN_CPU_OFF:
case QEMU_PSCI_0_2_FN_CPU_OFF:
--
2.50.1 (Apple Git-155)
On 7/3/26 16:31, Mohamed Mediouni wrote: > Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> > --- > accel/hvf/hvf-all.c | 1 + > accel/stubs/hvf-stub.c | 1 + > hw/arm/virt.c | 5 +++++ > include/system/hvf.h | 5 +++++ > target/arm/hvf/hvf.c | 42 ++++++++++++++++++++++++++++++++++++++++-- > 5 files changed, 52 insertions(+), 2 deletions(-) > diff --git a/include/system/hvf.h b/include/system/hvf.h > index dc8da85979..0f0632f7ae 100644 > --- a/include/system/hvf.h > +++ b/include/system/hvf.h > @@ -28,9 +28,14 @@ extern bool hvf_allowed; > #define hvf_enabled() (hvf_allowed) > extern bool hvf_kernel_irqchip; > #define hvf_irqchip_in_kernel() (hvf_kernel_irqchip) > +extern bool hvf_nested_virt; > +#define hvf_nested_virt_enabled() (hvf_nested_virt) > +#define hvf_nested_virt_enable(enable) hvf_nested_virt = enable Unsafe macro. Better expose the method and add a stub. > #else /* !CONFIG_HVF_IS_POSSIBLE */ > #define hvf_enabled() 0 > #define hvf_irqchip_in_kernel() 0 > +#define hvf_nested_virt_enabled() 0 > +#define hvf_nested_virt_enable(enable) 0 > #endif /* !CONFIG_HVF_IS_POSSIBLE */
> On 7. Mar 2026, at 16:48, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>
> On 7/3/26 16:31, Mohamed Mediouni wrote:
>> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
>> ---
>> accel/hvf/hvf-all.c | 1 +
>> accel/stubs/hvf-stub.c | 1 +
>> hw/arm/virt.c | 5 +++++
>> include/system/hvf.h | 5 +++++
>> target/arm/hvf/hvf.c | 42 ++++++++++++++++++++++++++++++++++++++++--
>> 5 files changed, 52 insertions(+), 2 deletions(-)
>
>
>> diff --git a/include/system/hvf.h b/include/system/hvf.h
>> index dc8da85979..0f0632f7ae 100644
>> --- a/include/system/hvf.h
>> +++ b/include/system/hvf.h
>> @@ -28,9 +28,14 @@ extern bool hvf_allowed;
>> #define hvf_enabled() (hvf_allowed)
>> extern bool hvf_kernel_irqchip;
>> #define hvf_irqchip_in_kernel() (hvf_kernel_irqchip)
>> +extern bool hvf_nested_virt;
>> +#define hvf_nested_virt_enabled() (hvf_nested_virt)
>> +#define hvf_nested_virt_enable(enable) hvf_nested_virt = enable
>
> Unsafe macro. Better expose the method and add a stub.
>
Does this look saner? If so can send a rev with it…
diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c
index 1c1437b467..66c3b48608 100644
--- a/accel/hvf/hvf-all.c
+++ b/accel/hvf/hvf-all.c
@@ -26,6 +26,10 @@ bool hvf_allowed;
bool hvf_kernel_irqchip;
bool hvf_nested_virt;
+void hvf_nested_virt_enable(bool nested_virt) {
+ hvf_nested_virt = nested_virt;
+}
+
const char *hvf_return_string(hv_return_t ret)
{
switch (ret) {
diff --git a/accel/stubs/hvf-stub.c b/accel/stubs/hvf-stub.c
index cec1cbb056..dc365c5307 100644
--- a/accel/stubs/hvf-stub.c
+++ b/accel/stubs/hvf-stub.c
@@ -12,3 +12,12 @@
bool hvf_allowed;
bool hvf_kernel_irqchip;
bool hvf_nested_virt;
+
+void hvf_nested_virt_enable(bool nested_virt) {
+ /*
+ * This is called unconditionally from hw/arm/virt.c
+ * because we don't know if HVF is going to be used
+ * as that step of initialisation happens later.
+ * As such, do nothing here instead of marking as unreachable.
+ */
+}
diff --git a/include/system/hvf.h b/include/system/hvf.h
index 0f0632f7ae..a961df8b95 100644
--- a/include/system/hvf.h
+++ b/include/system/hvf.h
@@ -30,14 +30,14 @@ extern bool hvf_kernel_irqchip;
#define hvf_irqchip_in_kernel() (hvf_kernel_irqchip)
extern bool hvf_nested_virt;
#define hvf_nested_virt_enabled() (hvf_nested_virt)
-#define hvf_nested_virt_enable(enable) hvf_nested_virt = enable
#else /* !CONFIG_HVF_IS_POSSIBLE */
#define hvf_enabled() 0
#define hvf_irqchip_in_kernel() 0
#define hvf_nested_virt_enabled() 0
-#define hvf_nested_virt_enable(enable) 0
#endif /* !CONFIG_HVF_IS_POSSIBLE */
+void hvf_nested_virt_enable(bool nested_virt);
+
#define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf")
typedef struct HVFState HVFState;
>> #else /* !CONFIG_HVF_IS_POSSIBLE */
>> #define hvf_enabled() 0
>> #define hvf_irqchip_in_kernel() 0
>> +#define hvf_nested_virt_enabled() 0
>> +#define hvf_nested_virt_enable(enable) 0
>> #endif /* !CONFIG_HVF_IS_POSSIBLE */
>
>
On 7/3/26 17:21, Mohamed Mediouni wrote:
>
>
>> On 7. Mar 2026, at 16:48, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>>
>> On 7/3/26 16:31, Mohamed Mediouni wrote:
>>> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
>>> ---
>>> accel/hvf/hvf-all.c | 1 +
>>> accel/stubs/hvf-stub.c | 1 +
>>> hw/arm/virt.c | 5 +++++
>>> include/system/hvf.h | 5 +++++
>>> target/arm/hvf/hvf.c | 42 ++++++++++++++++++++++++++++++++++++++++--
>>> 5 files changed, 52 insertions(+), 2 deletions(-)
>>
>>
>>> diff --git a/include/system/hvf.h b/include/system/hvf.h
>>> index dc8da85979..0f0632f7ae 100644
>>> --- a/include/system/hvf.h
>>> +++ b/include/system/hvf.h
>>> @@ -28,9 +28,14 @@ extern bool hvf_allowed;
>>> #define hvf_enabled() (hvf_allowed)
>>> extern bool hvf_kernel_irqchip;
>>> #define hvf_irqchip_in_kernel() (hvf_kernel_irqchip)
>>> +extern bool hvf_nested_virt;
>>> +#define hvf_nested_virt_enabled() (hvf_nested_virt)
>>> +#define hvf_nested_virt_enable(enable) hvf_nested_virt = enable
>>
>> Unsafe macro. Better expose the method and add a stub.
>>
> Does this look saner? If so can send a rev with it…
LGTM!
>
> diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c
> index 1c1437b467..66c3b48608 100644
> --- a/accel/hvf/hvf-all.c
> +++ b/accel/hvf/hvf-all.c
> @@ -26,6 +26,10 @@ bool hvf_allowed;
> bool hvf_kernel_irqchip;
> bool hvf_nested_virt;
>
> +void hvf_nested_virt_enable(bool nested_virt) {
> + hvf_nested_virt = nested_virt;
> +}
> +
> const char *hvf_return_string(hv_return_t ret)
> {
> switch (ret) {
> diff --git a/accel/stubs/hvf-stub.c b/accel/stubs/hvf-stub.c
> index cec1cbb056..dc365c5307 100644
> --- a/accel/stubs/hvf-stub.c
> +++ b/accel/stubs/hvf-stub.c
> @@ -12,3 +12,12 @@
> bool hvf_allowed;
> bool hvf_kernel_irqchip;
> bool hvf_nested_virt;
> +
> +void hvf_nested_virt_enable(bool nested_virt) {
> + /*
> + * This is called unconditionally from hw/arm/virt.c
> + * because we don't know if HVF is going to be used
> + * as that step of initialisation happens later.
> + * As such, do nothing here instead of marking as unreachable.
> + */
> +}
> diff --git a/include/system/hvf.h b/include/system/hvf.h
> index 0f0632f7ae..a961df8b95 100644
> --- a/include/system/hvf.h
> +++ b/include/system/hvf.h
> @@ -30,14 +30,14 @@ extern bool hvf_kernel_irqchip;
> #define hvf_irqchip_in_kernel() (hvf_kernel_irqchip)
> extern bool hvf_nested_virt;
> #define hvf_nested_virt_enabled() (hvf_nested_virt)
> -#define hvf_nested_virt_enable(enable) hvf_nested_virt = enable
> #else /* !CONFIG_HVF_IS_POSSIBLE */
> #define hvf_enabled() 0
> #define hvf_irqchip_in_kernel() 0
> #define hvf_nested_virt_enabled() 0
> -#define hvf_nested_virt_enable(enable) 0
> #endif /* !CONFIG_HVF_IS_POSSIBLE */
>
> +void hvf_nested_virt_enable(bool nested_virt);
> +
> #define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf")
>
> typedef struct HVFState HVFState;
>
>>> #else /* !CONFIG_HVF_IS_POSSIBLE */
>>> #define hvf_enabled() 0
>>> #define hvf_irqchip_in_kernel() 0
>>> +#define hvf_nested_virt_enabled() 0
>>> +#define hvf_nested_virt_enable(enable) 0
>>> #endif /* !CONFIG_HVF_IS_POSSIBLE */
>>
>>
>
© 2016 - 2026 Red Hat, Inc.