Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
hw/arm/virt.c | 9 ++++++---
target/arm/hvf-stub.c | 15 +++++++++++++++
target/arm/hvf/hvf.c | 41 +++++++++++++++++++++++++++++++++++++++--
target/arm/hvf_arm.h | 3 +++
4 files changed, 63 insertions(+), 5 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 59e6c2b9df..91d8cd9363 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -817,8 +817,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
g_assert_not_reached();
}
- if (kvm_enabled() && vms->virt &&
- (revision != 3 || !kvm_irqchip_in_kernel())) {
+ if (kvm_enabled() && vms->virt && (revision != 3 || !kvm_irqchip_in_kernel())) {
error_report("KVM EL2 is only supported with in-kernel GICv3");
exit(1);
}
@@ -2281,7 +2280,8 @@ static void machvirt_init(MachineState *machine)
exit(1);
}
- if (vms->virt && !kvm_enabled() && !tcg_enabled() && !qtest_enabled()) {
+ if (vms->virt && !kvm_enabled() && !tcg_enabled()
+ && !hvf_enabled() && !qtest_enabled()) {
error_report("mach-virt: %s does not support providing "
"Virtualization extensions to the guest CPU",
current_accel_name());
@@ -2553,6 +2553,9 @@ static void virt_set_virt(Object *obj, bool value, Error **errp)
VirtMachineState *vms = VIRT_MACHINE(obj);
vms->virt = value;
+#if defined(CONFIG_HVF) && defined(__aarch64__)
+ hvf_arm_el2_enable(value);
+#endif
}
static bool virt_get_highmem(Object *obj, Error **errp)
diff --git a/target/arm/hvf-stub.c b/target/arm/hvf-stub.c
index ff137267a0..95ec4ea62f 100644
--- a/target/arm/hvf-stub.c
+++ b/target/arm/hvf-stub.c
@@ -18,3 +18,18 @@ uint32_t hvf_arm_get_max_ipa_bit_size(void)
{
g_assert_not_reached();
}
+
+bool hvf_arm_el2_supported(void)
+{
+ g_assert_not_reached();
+}
+
+bool hvf_arm_el2_enabled(void)
+{
+ g_assert_not_reached();
+}
+
+void hvf_arm_el2_enable(bool)
+{
+ g_assert_not_reached();
+}
diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
index 460782dbc0..483a50329b 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -26,6 +26,7 @@
#include "system/address-spaces.h"
#include "system/memory.h"
#include "hw/boards.h"
+#include "hw/arm/virt.h"
#include "hw/irq.h"
#include "qemu/main-loop.h"
#include "system/cpus.h"
@@ -891,6 +892,10 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
(1ULL << ARM_FEATURE_PMU) |
(1ULL << ARM_FEATURE_GENERIC_TIMER);
+ if (hvf_arm_el2_enabled()) {
+ ahcf->features |= 1ULL << ARM_FEATURE_EL2;
+ }
+
for (i = 0; i < ARRAY_SIZE(regs); i++) {
r |= hv_vcpu_config_get_feature_reg(hv_vcpu_config, regs[i].reg, regs[i].val);
}
@@ -958,6 +963,25 @@ uint32_t hvf_arm_get_max_ipa_bit_size(void)
return round_down_to_parange_bit_size(max_ipa_size);
}
+bool hvf_arm_el2_supported(void)
+{
+ bool is_nested_virt_supported;
+ hv_return_t ret = hv_vm_config_get_el2_supported(&is_nested_virt_supported);
+ assert_hvf_ok(ret);
+ return is_nested_virt_supported;
+}
+
+static bool is_nested_virt_enabled = false;
+bool hvf_arm_el2_enabled(void)
+{
+ return is_nested_virt_enabled;
+}
+
+void hvf_arm_el2_enable(bool enable)
+{
+ is_nested_virt_enabled = enable;
+}
+
void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu)
{
if (!arm_host_cpu_features.dtb_compatible) {
@@ -994,6 +1018,13 @@ hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range)
}
chosen_ipa_bit_size = pa_range;
+ if (hvf_arm_el2_enabled()) {
+ ret = hv_vm_config_set_el2_enabled(config, true);
+ if (ret != HV_SUCCESS) {
+ goto cleanup;
+ }
+ }
+
ret = hv_vm_create(config);
cleanup:
@@ -1101,6 +1132,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_arm_el2_enabled()) {
+ return 2;
+ }
+ return 1;
+}
/*
* Handle a PSCI call.
*
@@ -1122,7 +1160,6 @@ static bool hvf_handle_psci_call(CPUState *cpu)
CPUState *target_cpu_state;
ARMCPU *target_cpu;
target_ulong entry;
- int target_el = 1;
int32_t ret = 0;
trace_hvf_psci_call(param[0], param[1], param[2], param[3],
@@ -1176,7 +1213,7 @@ static bool hvf_handle_psci_call(CPUState *cpu)
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:
diff --git a/target/arm/hvf_arm.h b/target/arm/hvf_arm.h
index ea82f2691d..bf55e7ae28 100644
--- a/target/arm/hvf_arm.h
+++ b/target/arm/hvf_arm.h
@@ -24,5 +24,8 @@ void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu);
uint32_t hvf_arm_get_default_ipa_bit_size(void);
uint32_t hvf_arm_get_max_ipa_bit_size(void);
+bool hvf_arm_el2_supported(void);
+bool hvf_arm_el2_enabled(void);
+void hvf_arm_el2_enable(bool);
#endif
--
2.39.5 (Apple Git-154)
On 8/8/25 09:01, Mohamed Mediouni wrote:
> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
> ---
> hw/arm/virt.c | 9 ++++++---
> target/arm/hvf-stub.c | 15 +++++++++++++++
> target/arm/hvf/hvf.c | 41 +++++++++++++++++++++++++++++++++++++++--
> target/arm/hvf_arm.h | 3 +++
> 4 files changed, 63 insertions(+), 5 deletions(-)
I'm getting HV_UNSUPPORTED on Silicon M1, I suppose because this
method is not being called in this series (is there a patch missing?):
> +bool hvf_arm_el2_supported(void)
> +{
> + bool is_nested_virt_supported;
> + hv_return_t ret = hv_vm_config_get_el2_supported(&is_nested_virt_supported);
> + assert_hvf_ok(ret);
> + return is_nested_virt_supported;
> +}
> On 11. Aug 2025, at 14:47, Philippe Mathieu-Daudé <philmd@linaro.org> wrote: > > I'm getting HV_UNSUPPORTED on Silicon M1, I suppose because this > method is not being called in this series (is there a patch missing?): Nested virt is only supported by macOS on Apple M3 and later
On 11/8/25 15:35, Mohamed Mediouni wrote: > > >> On 11. Aug 2025, at 14:47, Philippe Mathieu-Daudé <philmd@linaro.org> >> wrote: >> >> I'm getting HV_UNSUPPORTED on Silicon M1, I suppose because this >> method is not being called in this series (is there a patch missing?): > Nested virt is only supported by macOS on Apple M3 and later I know, but maybe this is what we need to report to users trying to enable it, rather than aborting ;)
On 8/8/25 09:01, Mohamed Mediouni wrote:
> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
> ---
> hw/arm/virt.c | 9 ++++++---
> target/arm/hvf-stub.c | 15 +++++++++++++++
> target/arm/hvf/hvf.c | 41 +++++++++++++++++++++++++++++++++++++++--
> target/arm/hvf_arm.h | 3 +++
> 4 files changed, 63 insertions(+), 5 deletions(-)
>
> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> index 59e6c2b9df..91d8cd9363 100644
> --- a/hw/arm/virt.c
> +++ b/hw/arm/virt.c
> @@ -817,8 +817,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
> g_assert_not_reached();
> }
>
> - if (kvm_enabled() && vms->virt &&
> - (revision != 3 || !kvm_irqchip_in_kernel())) {
> + if (kvm_enabled() && vms->virt && (revision != 3 || !kvm_irqchip_in_kernel())) {
> error_report("KVM EL2 is only supported with in-kernel GICv3");
> exit(1);
> }
> @@ -2281,7 +2280,8 @@ static void machvirt_init(MachineState *machine)
> exit(1);
> }
>
> - if (vms->virt && !kvm_enabled() && !tcg_enabled() && !qtest_enabled()) {
> + if (vms->virt && !kvm_enabled() && !tcg_enabled()
> + && !hvf_enabled() && !qtest_enabled()) {
> error_report("mach-virt: %s does not support providing "
> "Virtualization extensions to the guest CPU",
> current_accel_name());
> @@ -2553,6 +2553,9 @@ static void virt_set_virt(Object *obj, bool value, Error **errp)
> VirtMachineState *vms = VIRT_MACHINE(obj);
>
> vms->virt = value;
> +#if defined(CONFIG_HVF) && defined(__aarch64__)
> + hvf_arm_el2_enable(value);
> +#endif
> }
We don't have hvf_arm_el1_enable(). I think we just want to always
provide the most performant configuration to users.
IOW, we don't need hvf_arm_el2_enable(). I'd just always enable EL2
(if supported) in hvf_arm_get_host_cpu_features().
> On 11. Aug 2025, at 14:56, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>
> On 8/8/25 09:01, Mohamed Mediouni wrote:
>> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
>> ---
>> hw/arm/virt.c | 9 ++++++---
>> target/arm/hvf-stub.c | 15 +++++++++++++++
>> target/arm/hvf/hvf.c | 41 +++++++++++++++++++++++++++++++++++++++--
>> target/arm/hvf_arm.h | 3 +++
>> 4 files changed, 63 insertions(+), 5 deletions(-)
>> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
>> index 59e6c2b9df..91d8cd9363 100644
>> --- a/hw/arm/virt.c
>> +++ b/hw/arm/virt.c
>> @@ -817,8 +817,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
>> g_assert_not_reached();
>> }
>> - if (kvm_enabled() && vms->virt &&
>> - (revision != 3 || !kvm_irqchip_in_kernel())) {
>> + if (kvm_enabled() && vms->virt && (revision != 3 || !kvm_irqchip_in_kernel())) {
>> error_report("KVM EL2 is only supported with in-kernel GICv3");
>> exit(1);
>> }
>> @@ -2281,7 +2280,8 @@ static void machvirt_init(MachineState *machine)
>> exit(1);
>> }
>> - if (vms->virt && !kvm_enabled() && !tcg_enabled() && !qtest_enabled()) {
>> + if (vms->virt && !kvm_enabled() && !tcg_enabled()
>> + && !hvf_enabled() && !qtest_enabled()) {
>> error_report("mach-virt: %s does not support providing "
>> "Virtualization extensions to the guest CPU",
>> current_accel_name());
>> @@ -2553,6 +2553,9 @@ static void virt_set_virt(Object *obj, bool value, Error **errp)
>> VirtMachineState *vms = VIRT_MACHINE(obj);
>> vms->virt = value;
>> +#if defined(CONFIG_HVF) && defined(__aarch64__)
>> + hvf_arm_el2_enable(value);
>> +#endif
>> }
> We don't have hvf_arm_el1_enable(). I think we just want to always
> provide the most performant configuration to users.
> IOW, we don't need hvf_arm_el2_enable(). I'd just always enable EL2
> (if supported) in hvf_arm_get_host_cpu_features().
A different feature set is exposed when EL2 is enabled. For example, SME is not currently exposed in that mode.
(We currently disable SME in Qemu anyway but that’s something I plan to take care of)
On 11/8/25 14:56, Philippe Mathieu-Daudé wrote:
> On 8/8/25 09:01, Mohamed Mediouni wrote:
>> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
>> ---
>> hw/arm/virt.c | 9 ++++++---
>> target/arm/hvf-stub.c | 15 +++++++++++++++
>> target/arm/hvf/hvf.c | 41 +++++++++++++++++++++++++++++++++++++++--
>> target/arm/hvf_arm.h | 3 +++
>> 4 files changed, 63 insertions(+), 5 deletions(-)
>>
>> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
>> index 59e6c2b9df..91d8cd9363 100644
>> --- a/hw/arm/virt.c
>> +++ b/hw/arm/virt.c
>> @@ -817,8 +817,7 @@ static void create_gic(VirtMachineState *vms,
>> MemoryRegion *mem)
>> g_assert_not_reached();
>> }
>> - if (kvm_enabled() && vms->virt &&
>> - (revision != 3 || !kvm_irqchip_in_kernel())) {
>> + if (kvm_enabled() && vms->virt && (revision != 3 || !
>> kvm_irqchip_in_kernel())) {
>> error_report("KVM EL2 is only supported with in-kernel GICv3");
>> exit(1);
>> }
>> @@ -2281,7 +2280,8 @@ static void machvirt_init(MachineState *machine)
>> exit(1);
>> }
>> - if (vms->virt && !kvm_enabled() && !tcg_enabled() && !
>> qtest_enabled()) {
>> + if (vms->virt && !kvm_enabled() && !tcg_enabled()
>> + && !hvf_enabled() && !qtest_enabled()) {
>> error_report("mach-virt: %s does not support providing "
>> "Virtualization extensions to the guest CPU",
>> current_accel_name());
>> @@ -2553,6 +2553,9 @@ static void virt_set_virt(Object *obj, bool
>> value, Error **errp)
>> VirtMachineState *vms = VIRT_MACHINE(obj);
>> vms->virt = value;
>> +#if defined(CONFIG_HVF) && defined(__aarch64__)
>> + hvf_arm_el2_enable(value);
>> +#endif
>> }
> We don't have hvf_arm_el1_enable(). I think we just want to always
> provide the most performant configuration to users.
> IOW, we don't need hvf_arm_el2_enable(). I'd just always enable EL2
> (if supported) in hvf_arm_get_host_cpu_features().
We create vCPUs in machvirt_init(). Upon vCPU INITialization, we check
host features. Then during REALIZation, we check the QOM properties.
If 'virt' flag is disabled, machvirt_init() sets "has_el2" property
to false, effectively setting ARMCPU::has_el2 field to %false.
In arm_cpu_realizefn(), if ARMCPU::has_el2 we unset ARM_FEATURE_EL2.
© 2016 - 2025 Red Hat, Inc.