hw/arm, target/arm: nested virtualisation on HVF
Add hvf_arm_el2_supported for querying EL2 availability. An hvf_nested_virt_enable workaround is added as nested virt has to be enabled early on HVF. And adds hvf_nested_virt_enabled. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-id: 20260429190532.26538-6-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Mohamed Mediouni committed
May 5, 2026 at 09:25 UTC
d7a75ea483a57add78b559cd181293e0136cc8c8
5 files changed
+67
-2
accel/hvf/hvf-all.c
+6
@@ -24,6 +24,12 @@
24
25
bool hvf_allowed;
26
bool hvf_kernel_irqchip;
27
+bool hvf_nested_virt;
28
+
29
+void hvf_nested_virt_enable(bool nested_virt)
30
+{
31
+ hvf_nested_virt = nested_virt;
32
+}
33
34
const char *hvf_return_string(hv_return_t ret)
35
{
accel/stubs/hvf-stub.c
+11
@@ -11,3 +11,14 @@
11
12
bool hvf_allowed;
13
bool hvf_kernel_irqchip;
14
+bool hvf_nested_virt;
15
+
16
+void hvf_nested_virt_enable(bool nested_virt)
17
+{
18
+ /*
19
+ * This is called unconditionally from hw/arm/virt.c
20
+ * because we don't know if HVF is going to be used
21
+ * as that step of initialisation happens later.
22
+ * As such, do nothing here instead of marking as unreachable.
23
+ */
24
+}
hw/arm/virt.c
+5
@@ -2987,6 +2987,11 @@ static void virt_set_virt(Object *obj, bool value, Error **errp)
2987
VirtMachineState *vms = VIRT_MACHINE(obj);
2988
2989
vms->virt = value;
2990
+ /*
2991
+ * At this point, HVF is not initialised yet.
2992
+ * However, it needs to know if nested virt is enabled at init time.
2993
+ */
2994
+ hvf_nested_virt_enable(value);
2995
}
2996
2997
static bool virt_get_highmem(Object *obj, Error **errp)
include/system/hvf.h
+5
@@ -28,11 +28,16 @@ extern bool hvf_allowed;
28
#define hvf_enabled() (hvf_allowed)
29
extern bool hvf_kernel_irqchip;
30
#define hvf_irqchip_in_kernel() (hvf_kernel_irqchip)
31
+extern bool hvf_nested_virt;
32
+#define hvf_nested_virt_enabled() (hvf_nested_virt)
33
#else /* !CONFIG_HVF_IS_POSSIBLE */
34
#define hvf_enabled() 0
35
#define hvf_irqchip_in_kernel() 0
36
+#define hvf_nested_virt_enabled() 0
37
#endif /* !CONFIG_HVF_IS_POSSIBLE */
38
39
+void hvf_nested_virt_enable(bool nested_virt);
40
+
41
#define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf")
42
43
typedef struct HVFState HVFState;
target/arm/hvf/hvf.c
+40
-2
@@ -27,6 +27,7 @@
27
#include "system/memory.h"
28
#include "hw/core/boards.h"
29
#include "hw/core/irq.h"
30
+#include "hw/arm/virt.h"
31
#include "qemu/main-loop.h"
32
#include "system/cpus.h"
33
#include "arm-powerctl.h"
@@ -1103,6 +1104,10 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
1104
(1ULL << ARM_FEATURE_PMU) |
1105
(1ULL << ARM_FEATURE_GENERIC_TIMER);
1106
1107
+ if (hvf_nested_virt_enabled()) {
1108
+ ahcf->features |= 1ULL << ARM_FEATURE_EL2;
1109
+ }
1110
+
1111
for (i = 0; i < ARRAY_SIZE(regs); i++) {
1112
r |= hv_vcpu_config_get_feature_reg(config, regs[i].reg,
1113
&host_isar.idregs[regs[i].index]);
@@ -1218,6 +1223,19 @@ void hvf_arch_vcpu_destroy(CPUState *cpu)
1223
assert_hvf_ok(ret);
1224
}
1225
1226
+static bool hvf_arm_el2_supported(void)
1227
+{
1228
+ bool is_nested_virt_supported;
1229
+ if (__builtin_available(macOS 15.0, *)) {
1230
+ hv_return_t ret = hv_vm_config_get_el2_supported(&is_nested_virt_supported);
1231
+ assert_hvf_ok(ret);
1232
+ } else {
1233
+ return false;
1234
+ }
1235
+ return is_nested_virt_supported;
1236
+}
1237
+
1238
+
1239
hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range)
1240
{
1241
hv_return_t ret;
@@ -1229,6 +1247,20 @@ hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range)
1247
}
1248
chosen_ipa_bit_size = pa_range;
1249
1250
+ if (__builtin_available(macOS 15.0, *)) {
1251
+ if (hvf_nested_virt_enabled()) {
1252
+ if (!hvf_arm_el2_supported()) {
1253
+ error_report("Nested virtualization not supported on this system.");
1254
+ goto cleanup;
1255
+ }
1256
+ ret = hv_vm_config_set_el2_enabled(config, true);
1257
+ if (ret != HV_SUCCESS) {
1258
+ error_report("Failed to enable nested virtualization.");
1259
+ goto cleanup;
1260
+ }
1261
+ }
1262
+ }
1263
+
1264
ret = hv_vm_create(config);
1265
if (hvf_irqchip_in_kernel()) {
1266
if (__builtin_available(macOS 15.0, *)) {
@@ -1420,6 +1452,13 @@ static void hvf_psci_cpu_off(ARMCPU *arm_cpu)
1452
assert(ret == QEMU_ARM_POWERCTL_RET_SUCCESS);
1453
}
1454
1455
+static int hvf_psci_get_target_el(void)
1456
+{
1457
+ if (hvf_nested_virt_enabled()) {
1458
+ return 2;
1459
+ }
1460
+ return 1;
1461
+}
1462
/*
1463
* Handle a PSCI call.
1464
*
@@ -1441,7 +1480,6 @@ static bool hvf_handle_psci_call(CPUState *cpu, int *excp_ret)
1480
CPUState *target_cpu_state;
1481
ARMCPU *target_cpu;
1482
uint64_t entry;
1444
- int target_el = 1;
1483
int32_t ret = 0;
1484
1485
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)
1533
entry = param[2];
1534
context_id = param[3];
1535
ret = arm_set_cpu_on(mpidr, entry, context_id,
1498
- target_el, target_aarch64);
1536
+ hvf_psci_get_target_el(), target_aarch64);
1537
break;
1538
case QEMU_PSCI_0_1_FN_CPU_OFF:
1539
case QEMU_PSCI_0_2_FN_CPU_OFF: