* [PATCH 1/9] target/arm: hvf: stubbing writes to LORC_EL1
2025-07-25 8:34 [PATCH 0/9] HVF: Add support for platform vGIC and nested virtualisation Mohamed Mediouni
@ 2025-07-25 8:34 ` Mohamed Mediouni
2025-07-25 8:34 ` [PATCH 2/9] accel, hw/arm, include/system/hvf: plumbing changes for HVF vGIC Mohamed Mediouni
` (7 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Mohamed Mediouni @ 2025-07-25 8:34 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Igor Mammedov, Mads Ynddal, Alexander Graf,
Shannon Zhao, Michael S. Tsirkin, Peter Maydell, Cameron Esfahani,
Roman Bolshakov, Phil Dennis-Jordan, qemu-arm, Ani Sinha,
Mohamed Mediouni
Linux zeroes LORC_EL1 on boot at EL2, without further interaction with FEAT_LOR afterwards.
Stub out LORC_EL1 accesses as FEAT_LOR is a mandatory extension on Armv8.1+.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/arm/hvf/hvf.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
index c9cfcdc08b..edfd29de75 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -186,6 +186,7 @@ void hvf_arm_init_debug(void)
#define SYSREG_OSLAR_EL1 SYSREG(2, 0, 1, 0, 4)
#define SYSREG_OSLSR_EL1 SYSREG(2, 0, 1, 1, 4)
#define SYSREG_OSDLR_EL1 SYSREG(2, 0, 1, 3, 4)
+#define SYSREG_LORC_EL1 SYSREG(3, 0, 10, 4, 3)
#define SYSREG_CNTPCT_EL0 SYSREG(3, 3, 14, 0, 1)
#define SYSREG_CNTP_CTL_EL0 SYSREG(3, 3, 14, 2, 1)
#define SYSREG_PMCR_EL0 SYSREG(3, 3, 9, 12, 0)
@@ -1650,6 +1651,9 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val)
case SYSREG_OSDLR_EL1:
/* Dummy register */
return 0;
+ case SYSREG_LORC_EL1:
+ /* Dummy register */
+ return 0;
case SYSREG_ICC_AP0R0_EL1:
case SYSREG_ICC_AP0R1_EL1:
case SYSREG_ICC_AP0R2_EL1:
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 2/9] accel, hw/arm, include/system/hvf: plumbing changes for HVF vGIC
2025-07-25 8:34 [PATCH 0/9] HVF: Add support for platform vGIC and nested virtualisation Mohamed Mediouni
2025-07-25 8:34 ` [PATCH 1/9] target/arm: hvf: stubbing writes to LORC_EL1 Mohamed Mediouni
@ 2025-07-25 8:34 ` Mohamed Mediouni
2025-07-25 8:34 ` [PATCH 3/9] target/arm: hvf: instantiate GIC early Mohamed Mediouni
` (6 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Mohamed Mediouni @ 2025-07-25 8:34 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Igor Mammedov, Mads Ynddal, Alexander Graf,
Shannon Zhao, Michael S. Tsirkin, Peter Maydell, Cameron Esfahani,
Roman Bolshakov, Phil Dennis-Jordan, qemu-arm, Ani Sinha,
Mohamed Mediouni
Misc changes for HVF vGIC enablement.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
accel/hvf/hvf-all.c | 44 ++++++++++++++++++++++++++++++++++++++
accel/stubs/hvf-stub.c | 1 +
hw/arm/virt.c | 17 +++++++++------
hw/intc/arm_gicv3_common.c | 3 +++
include/system/hvf.h | 3 +++
system/vl.c | 2 ++
6 files changed, 64 insertions(+), 6 deletions(-)
diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c
index e67a8105a6..d9408e259f 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 "qapi/error.h"
+#include "qapi/qapi-visit-common.h"
#include "accel/accel-ops.h"
#include "system/address-spaces.h"
#include "system/memory.h"
@@ -20,6 +22,7 @@
#include "trace.h"
bool hvf_allowed;
+bool hvf_kernel_irqchip;
struct mac_slot {
int present;
@@ -290,6 +293,41 @@ static int hvf_gdbstub_sstep_flags(AccelState *as)
return SSTEP_ENABLE | SSTEP_NOIRQ;
}
+static void hvf_set_kernel_irqchip(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+#ifdef __aarch64__
+ OnOffSplit mode;
+ if (!visit_type_OnOffSplit(v, name, &mode, errp)) {
+ return;
+ }
+
+ switch (mode) {
+ case ON_OFF_SPLIT_ON:
+ hvf_kernel_irqchip = true;
+ break;
+
+ case ON_OFF_SPLIT_OFF:
+ hvf_kernel_irqchip = false;
+ break;
+
+ case ON_OFF_SPLIT_SPLIT:
+ error_setg(errp, "HVF: split irqchip is not supported on Arm.");
+ break;
+
+ default:
+ /*
+ * The value was checked in visit_type_OnOffSplit() above. If
+ * we get here, then something is wrong in QEMU.
+ */
+ abort();
+ }
+#else
+ error_setg(errp, "HVF: setting irqchip configuration not supported on x86_64.");
+#endif
+}
+
static void hvf_accel_class_init(ObjectClass *oc, const void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
@@ -297,6 +335,12 @@ 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;
+ hvf_kernel_irqchip = true;
+ object_class_property_add(oc, "kernel-irqchip", "on|off|split",
+ NULL, hvf_set_kernel_irqchip,
+ NULL, NULL);
+ object_class_property_set_description(oc, "kernel-irqchip",
+ "Configure HVF irqchip");
}
static const TypeInfo hvf_accel_type = {
diff --git a/accel/stubs/hvf-stub.c b/accel/stubs/hvf-stub.c
index 42eadc5ca9..6bd08759ba 100644
--- a/accel/stubs/hvf-stub.c
+++ b/accel/stubs/hvf-stub.c
@@ -10,3 +10,4 @@
#include "system/hvf.h"
bool hvf_allowed;
+bool hvf_kernel_irqchip;
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index ef6be3660f..a26bde4c75 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -830,7 +830,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
* interrupts; there are always 32 of the former (mandated by GIC spec).
*/
qdev_prop_set_uint32(vms->gic, "num-irq", NUM_IRQS + 32);
- if (!kvm_irqchip_in_kernel()) {
+ if (!kvm_irqchip_in_kernel() && !hvf_irqchip_in_kernel()) {
qdev_prop_set_bit(vms->gic, "has-security-extensions", vms->secure);
}
@@ -853,8 +853,8 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
qdev_prop_set_array(vms->gic, "redist-region-count",
redist_region_count);
- if (!kvm_irqchip_in_kernel()) {
- if (vms->tcg_its) {
+ if (!kvm_irqchip_in_kernel() && !hvf_irqchip_in_kernel()) {
+ if (vms->its && vms->tcg_its) {
object_property_set_link(OBJECT(vms->gic), "sysmem",
OBJECT(mem), &error_fatal);
qdev_prop_set_bit(vms->gic, "has-lpi", true);
@@ -864,7 +864,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
ARCH_GIC_MAINT_IRQ);
}
} else {
- if (!kvm_irqchip_in_kernel()) {
+ if (!kvm_irqchip_in_kernel() && !hvf_irqchip_in_kernel()) {
qdev_prop_set_bit(vms->gic, "has-virtualization-extensions",
vms->virt);
}
@@ -2058,11 +2058,16 @@ static void finalize_gic_version(VirtMachineState *vms)
/* KVM w/o kernel irqchip can only deal with GICv2 */
gics_supported |= VIRT_GIC_VERSION_2_MASK;
accel_name = "KVM with kernel-irqchip=off";
- } else if (tcg_enabled() || hvf_enabled() || qtest_enabled()) {
+ } else if (hvf_enabled()) {
+ if (!hvf_irqchip_in_kernel()) {
+ gics_supported |= VIRT_GIC_VERSION_2_MASK;
+ }
+ gics_supported |= VIRT_GIC_VERSION_3_MASK;
+ } else if (tcg_enabled() || qtest_enabled()) {
gics_supported |= VIRT_GIC_VERSION_2_MASK;
if (module_object_class_by_name("arm-gicv3")) {
gics_supported |= VIRT_GIC_VERSION_3_MASK;
- if (vms->virt) {
+ if (vms->virt && !hvf_enabled()) {
/* GICv4 only makes sense if CPU has EL2 */
gics_supported |= VIRT_GIC_VERSION_4_MASK;
}
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index e438d8c042..7c0eb5eb1e 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -32,6 +32,7 @@
#include "gicv3_internal.h"
#include "hw/arm/linux-boot-if.h"
#include "system/kvm.h"
+#include "system/hvf.h"
static void gicv3_gicd_no_migration_shift_bug_post_load(GICv3State *cs)
@@ -662,6 +663,8 @@ const char *gicv3_class_name(void)
{
if (kvm_irqchip_in_kernel()) {
return "kvm-arm-gicv3";
+ } else if (hvf_irqchip_in_kernel()) {
+ return "hvf-arm-gicv3";
} else {
if (kvm_enabled()) {
error_report("Userspace GICv3 is not supported with KVM");
diff --git a/include/system/hvf.h b/include/system/hvf.h
index d3dcf088b3..dc8da85979 100644
--- a/include/system/hvf.h
+++ b/include/system/hvf.h
@@ -26,8 +26,11 @@
#ifdef CONFIG_HVF_IS_POSSIBLE
extern bool hvf_allowed;
#define hvf_enabled() (hvf_allowed)
+extern bool hvf_kernel_irqchip;
+#define hvf_irqchip_in_kernel() (hvf_kernel_irqchip)
#else /* !CONFIG_HVF_IS_POSSIBLE */
#define hvf_enabled() 0
+#define hvf_irqchip_in_kernel() 0
#endif /* !CONFIG_HVF_IS_POSSIBLE */
#define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf")
diff --git a/system/vl.c b/system/vl.c
index 3b7057e6c6..1c072d15a4 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -1773,6 +1773,8 @@ static void qemu_apply_legacy_machine_options(QDict *qdict)
false);
object_register_sugar_prop(ACCEL_CLASS_NAME("whpx"), "kernel-irqchip", value,
false);
+ object_register_sugar_prop(ACCEL_CLASS_NAME("hvf"), "kernel-irqchip", value,
+ false);
qdict_del(qdict, "kernel-irqchip");
}
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 3/9] target/arm: hvf: instantiate GIC early
2025-07-25 8:34 [PATCH 0/9] HVF: Add support for platform vGIC and nested virtualisation Mohamed Mediouni
2025-07-25 8:34 ` [PATCH 1/9] target/arm: hvf: stubbing writes to LORC_EL1 Mohamed Mediouni
2025-07-25 8:34 ` [PATCH 2/9] accel, hw/arm, include/system/hvf: plumbing changes for HVF vGIC Mohamed Mediouni
@ 2025-07-25 8:34 ` Mohamed Mediouni
2025-07-25 8:34 ` [PATCH 4/9] target/arm: add asserts for code paths not leveraged when using the vGIC Mohamed Mediouni
` (5 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Mohamed Mediouni @ 2025-07-25 8:34 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Igor Mammedov, Mads Ynddal, Alexander Graf,
Shannon Zhao, Michael S. Tsirkin, Peter Maydell, Cameron Esfahani,
Roman Bolshakov, Phil Dennis-Jordan, qemu-arm, Ani Sinha,
Mohamed Mediouni
While figuring out a better spot for it, put it in hv_arch_vm_create().
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/arm/hvf/hvf.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
index edfd29de75..bb31df90b8 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -1001,6 +1001,21 @@ hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range)
chosen_ipa_bit_size = pa_range;
ret = hv_vm_create(config);
+ if (hvf_irqchip_in_kernel()) {
+ /*
+ * Instantiate GIC.
+ * This must be done prior to the creation of any vCPU
+ * but past hv_vm_create()
+ */
+ hv_gic_config_t cfg = hv_gic_config_create();
+ hv_gic_config_set_distributor_base(cfg, 0x08000000);
+ hv_gic_config_set_redistributor_base(cfg, 0x080A0000);
+ hv_return_t err = hv_gic_create(cfg);
+ if (err != HV_SUCCESS) {
+ error_report("error creating platform VGIC");
+ goto cleanup;
+ }
+ }
cleanup:
os_release(config);
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 4/9] target/arm: add asserts for code paths not leveraged when using the vGIC
2025-07-25 8:34 [PATCH 0/9] HVF: Add support for platform vGIC and nested virtualisation Mohamed Mediouni
` (2 preceding siblings ...)
2025-07-25 8:34 ` [PATCH 3/9] target/arm: hvf: instantiate GIC early Mohamed Mediouni
@ 2025-07-25 8:34 ` Mohamed Mediouni
2025-07-25 8:34 ` [PATCH 5/9] hw/intc: Add hvf vGIC interrupt controller support Mohamed Mediouni
` (4 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Mohamed Mediouni @ 2025-07-25 8:34 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Igor Mammedov, Mads Ynddal, Alexander Graf,
Shannon Zhao, Michael S. Tsirkin, Peter Maydell, Cameron Esfahani,
Roman Bolshakov, Phil Dennis-Jordan, qemu-arm, Ani Sinha,
Mohamed Mediouni
When using the vGIC, timers are directly handled by the platform, so no vmexits ought to happen in that case.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/arm/hvf/hvf.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
index bb31df90b8..7b4e8297af 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -1378,6 +1378,9 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val)
case SYSREG_ICC_SGI1R_EL1:
case SYSREG_ICC_SRE_EL1:
case SYSREG_ICC_CTLR_EL1:
+ if (hvf_irqchip_in_kernel()) {
+ abort();
+ }
/* Call the TCG sysreg handler. This is only safe for GICv3 regs. */
if (hvf_sysreg_read_cp(cpu, reg, val)) {
return 0;
@@ -1694,6 +1697,9 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val)
case SYSREG_ICC_SGI0R_EL1:
case SYSREG_ICC_SGI1R_EL1:
case SYSREG_ICC_SRE_EL1:
+ if (hvf_irqchip_in_kernel()) {
+ abort();
+ }
/* Call the TCG sysreg handler. This is only safe for GICv3 regs. */
if (hvf_sysreg_write_cp(cpu, reg, val)) {
return 0;
@@ -1957,6 +1963,10 @@ int hvf_vcpu_exec(CPUState *cpu)
/* This is the main one, handle below. */
break;
case HV_EXIT_REASON_VTIMER_ACTIVATED:
+ /* This is only for when a user-mode irqchip is used. */
+ if (hvf_irqchip_in_kernel()) {
+ assert("vtimer activated vmexit when using platform GIC");
+ }
qemu_set_irq(arm_cpu->gt_timer_outputs[GTIMER_VIRT], 1);
cpu->accel->vtimer_masked = true;
return 0;
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 5/9] hw/intc: Add hvf vGIC interrupt controller support
2025-07-25 8:34 [PATCH 0/9] HVF: Add support for platform vGIC and nested virtualisation Mohamed Mediouni
` (3 preceding siblings ...)
2025-07-25 8:34 ` [PATCH 4/9] target/arm: add asserts for code paths not leveraged when using the vGIC Mohamed Mediouni
@ 2025-07-25 8:34 ` Mohamed Mediouni
2025-07-25 8:34 ` [PATCH 6/9] hw/arm, target/arm: nested virtualisation on HVF Mohamed Mediouni
` (3 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Mohamed Mediouni @ 2025-07-25 8:34 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Igor Mammedov, Mads Ynddal, Alexander Graf,
Shannon Zhao, Michael S. Tsirkin, Peter Maydell, Cameron Esfahani,
Roman Bolshakov, Phil Dennis-Jordan, qemu-arm, Ani Sinha,
Mohamed Mediouni
This opens up the door to nested virtualisation support.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
hw/intc/arm_gicv3_hvf.c | 624 ++++++++++++++++++++++++++++++++++++++++
hw/intc/meson.build | 1 +
2 files changed, 625 insertions(+)
create mode 100644 hw/intc/arm_gicv3_hvf.c
diff --git a/hw/intc/arm_gicv3_hvf.c b/hw/intc/arm_gicv3_hvf.c
new file mode 100644
index 0000000000..23f1641318
--- /dev/null
+++ b/hw/intc/arm_gicv3_hvf.c
@@ -0,0 +1,624 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * ARM Generic Interrupt Controller using HVF platform support
+ *
+ * Copyright (c) 2025 Mohamed Mediouni
+ * Based on vGICv3 KVM code by Pavel Fedin
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/intc/arm_gicv3_common.h"
+#include "qemu/error-report.h"
+#include "qemu/module.h"
+#include "system/runstate.h"
+#include "system/hvf.h"
+#include "system/hvf_int.h"
+#include "gicv3_internal.h"
+#include "vgic_common.h"
+#include "qom/object.h"
+#include "target/arm/cpregs.h"
+#include <Hypervisor/Hypervisor.h>
+
+struct HVFARMGICv3Class {
+ ARMGICv3CommonClass parent_class;
+ DeviceRealize parent_realize;
+ ResettablePhases parent_phases;
+};
+
+#define TYPE_HVF_GICV3 "hvf-arm-gicv3"
+typedef struct HVFARMGICv3Class HVFARMGICv3Class;
+
+/* This is reusing the GICv3State typedef from ARM_GICV3_ITS_COMMON */
+DECLARE_OBJ_CHECKERS(GICv3State, HVFARMGICv3Class,
+ HVF_GICV3, TYPE_HVF_GICV3);
+
+/*
+ * Loop through each distributor IRQ related register; since bits
+ * corresponding to SPIs and PPIs are RAZ/WI when affinity routing
+ * is enabled, we skip those.
+ */
+#define for_each_dist_irq_reg(_irq, _max, _field_width) \
+ for (_irq = GIC_INTERNAL; _irq < _max; _irq += (32 / _field_width))
+
+static void hvf_dist_get_priority(GICv3State *s, hv_gic_distributor_reg_t offset
+ , uint8_t *bmp)
+{
+ uint64_t reg;
+ uint32_t *field;
+ int irq;
+ field = (uint32_t *)(bmp);
+
+ for_each_dist_irq_reg(irq, s->num_irq, 8) {
+ hv_gic_get_distributor_reg(offset, ®);
+ *field = reg;
+ offset += 4;
+ field++;
+ }
+}
+
+static void hvf_dist_put_priority(GICv3State *s, hv_gic_distributor_reg_t offset
+ , uint8_t *bmp)
+{
+ uint32_t reg, *field;
+ int irq;
+ field = (uint32_t *)(bmp);
+
+ for_each_dist_irq_reg(irq, s->num_irq, 8) {
+ reg = *field;
+ hv_gic_set_distributor_reg(offset, reg);
+ offset += 4;
+ field++;
+ }
+}
+
+static void hvf_dist_get_edge_trigger(GICv3State *s, hv_gic_distributor_reg_t offset,
+ uint32_t *bmp)
+{
+ uint64_t reg;
+ int irq;
+
+ for_each_dist_irq_reg(irq, s->num_irq, 2) {
+ hv_gic_get_distributor_reg(offset, ®);
+ reg = half_unshuffle32(reg >> 1);
+ if (irq % 32 != 0) {
+ reg = (reg << 16);
+ }
+ *gic_bmp_ptr32(bmp, irq) |= reg;
+ offset += 4;
+ }
+}
+
+static void hvf_dist_put_edge_trigger(GICv3State *s, hv_gic_distributor_reg_t offset,
+ uint32_t *bmp)
+{
+ uint32_t reg;
+ int irq;
+
+ for_each_dist_irq_reg(irq, s->num_irq, 2) {
+ reg = *gic_bmp_ptr32(bmp, irq);
+ if (irq % 32 != 0) {
+ reg = (reg & 0xffff0000) >> 16;
+ } else {
+ reg = reg & 0xffff;
+ }
+ reg = half_shuffle32(reg) << 1;
+ hv_gic_set_distributor_reg(offset, reg);
+ offset += 4;
+ }
+}
+
+/* Read a bitmap register group from the kernel VGIC. */
+static void hvf_dist_getbmp(GICv3State *s, hv_gic_distributor_reg_t offset, uint32_t *bmp)
+{
+ uint64_t reg;
+ int irq;
+
+ for_each_dist_irq_reg(irq, s->num_irq, 1) {
+
+ hv_gic_get_distributor_reg(offset, ®);
+ *gic_bmp_ptr32(bmp, irq) = reg;
+ offset += 4;
+ }
+}
+
+static void hvf_dist_putbmp(GICv3State *s, hv_gic_distributor_reg_t offset,
+ hv_gic_distributor_reg_t clroffset, uint32_t *bmp)
+{
+ uint32_t reg;
+ int irq;
+
+ for_each_dist_irq_reg(irq, s->num_irq, 1) {
+ /*
+ * If this bitmap is a set/clear register pair, first write to the
+ * clear-reg to clear all bits before using the set-reg to write
+ * the 1 bits.
+ */
+ if (clroffset != 0) {
+ reg = 0;
+ hv_gic_set_distributor_reg(clroffset, reg);
+ clroffset += 4;
+ }
+ reg = *gic_bmp_ptr32(bmp, irq);
+ hv_gic_set_distributor_reg(offset, reg);
+ offset += 4;
+ }
+}
+
+static void hvf_gicv3_check(GICv3State *s)
+{
+ uint64_t reg;
+ uint32_t num_irq;
+
+ /* Sanity checking s->num_irq */
+ hv_gic_get_distributor_reg(HV_GIC_DISTRIBUTOR_REG_GICD_TYPER, ®);
+ num_irq = ((reg & 0x1f) + 1) * 32;
+
+ if (num_irq < s->num_irq) {
+ error_report("Model requests %u IRQs, but HVF supports max %u",
+ s->num_irq, num_irq);
+ abort();
+ }
+}
+
+static void hvf_gicv3_put(GICv3State *s)
+{
+ uint32_t reg;
+ uint64_t reg64, redist_typer;
+ int ncpu, i;
+
+ hvf_gicv3_check(s);
+
+ hv_vcpu_t vcpu0 = s->cpu[0].cpu->accel->fd;
+ hv_gic_get_redistributor_reg(vcpu0, HV_GIC_REDISTRIBUTOR_REG_GICR_TYPER
+ , &redist_typer);
+
+ reg = s->gicd_ctlr;
+ hv_gic_set_distributor_reg(HV_GIC_DISTRIBUTOR_REG_GICD_CTLR, reg);
+
+ if (redist_typer & GICR_TYPER_PLPIS) {
+ error_report("ITS is not supported on HVF.");
+ abort();
+ }
+
+ /* Redistributor state (one per CPU) */
+
+ for (ncpu = 0; ncpu < s->num_cpu; ncpu++) {
+ GICv3CPUState *c = &s->cpu[ncpu];
+ hv_vcpu_t vcpu = c->cpu->accel->fd;
+
+ reg = c->gicr_waker;
+ hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_IGROUPR0, reg);
+
+ reg = c->gicr_igroupr0;
+ hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_IGROUPR0, reg);
+
+ reg = ~0;
+ hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ICENABLER0, reg);
+ reg = c->gicr_ienabler0;
+ hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ISENABLER0, reg);
+
+ /* Restore config before pending so we treat level/edge correctly */
+ reg = half_shuffle32(c->edge_trigger >> 16) << 1;
+ hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ICFGR1, reg);
+
+ reg = ~0;
+ hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ICPENDR0, reg);
+ reg = c->gicr_ipendr0;
+ hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ISPENDR0, reg);
+
+ reg = ~0;
+ hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ICACTIVER0, reg);
+ reg = c->gicr_iactiver0;
+ hv_gic_set_redistributor_reg(vcpu, HV_GIC_REDISTRIBUTOR_REG_GICR_ISACTIVER0, reg);
+
+ for (i = 0; i < GIC_INTERNAL; i += 4) {
+ reg = c->gicr_ipriorityr[i] |
+ (c->gicr_ipriorityr[i + 1] << 8) |
+ (c->gicr_ipriorityr[i + 2] << 16) |
+ (c->gicr_ipriorityr[i + 3] << 24);
+ hv_gic_set_redistributor_reg(vcpu,
+ HV_GIC_REDISTRIBUTOR_REG_GICR_IPRIORITYR0 + i, reg);
+ }
+ }
+
+ /* s->enable bitmap -> GICD_ISENABLERn */
+ hvf_dist_putbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_ISENABLER0
+ , GICD_ICENABLER, s->enabled);
+
+ /* s->group bitmap -> GICD_IGROUPRn */
+ hvf_dist_putbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_IGROUPR0
+ , 0, s->group);
+
+ /* Restore targets before pending to ensure the pending state is set on
+ * the appropriate CPU interfaces in the kernel
+ */
+
+ /* s->gicd_irouter[irq] -> GICD_IROUTERn */
+ for (i = GIC_INTERNAL; i < s->num_irq; i++) {
+ uint32_t offset = HV_GIC_DISTRIBUTOR_REG_GICD_IROUTER32 + (sizeof(uint32_t) * i)
+ - (sizeof(uint32_t) * GIC_INTERNAL);
+ hv_gic_set_distributor_reg(offset, s->gicd_irouter[i]);
+ }
+
+
+ /*
+ * s->trigger bitmap -> GICD_ICFGRn
+ * (restore configuration registers before pending IRQs so we treat
+ * level/edge correctly)
+ */
+ hvf_dist_put_edge_trigger(s, HV_GIC_DISTRIBUTOR_REG_GICD_ICFGR0, s->edge_trigger);
+
+ /* s->pending bitmap -> GICD_ISPENDRn */
+ hvf_dist_putbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_ISPENDR0,
+ HV_GIC_DISTRIBUTOR_REG_GICD_ICPENDR0, s->pending);
+
+ /* s->active bitmap -> GICD_ISACTIVERn */
+ hvf_dist_putbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_ISACTIVER0,
+ HV_GIC_DISTRIBUTOR_REG_GICD_ICACTIVER0, s->active);
+
+ /* s->gicd_ipriority[] -> GICD_IPRIORITYRn */
+ hvf_dist_put_priority(s, HV_GIC_DISTRIBUTOR_REG_GICD_IPRIORITYR0, s->gicd_ipriority);
+
+ /* CPU interface state (one per CPU) */
+
+ for (ncpu = 0; ncpu < s->num_cpu; ncpu++) {
+ GICv3CPUState *c = &s->cpu[ncpu];
+ int num_pri_bits;
+ hv_vcpu_t vcpu = c->cpu->accel->fd;
+ hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_SRE_EL1, c->icc_sre_el1);
+
+ hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_CTLR_EL1,
+ c->icc_ctlr_el1[GICV3_NS]);
+ hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_IGRPEN0_EL1,
+ c->icc_igrpen[GICV3_G0]);
+ hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_IGRPEN1_EL1,
+ c->icc_igrpen[GICV3_G1NS]);
+ hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_PMR_EL1, c->icc_pmr_el1);
+ hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_BPR0_EL1, c->icc_bpr[GICV3_G0]);
+ hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_BPR1_EL1, c->icc_bpr[GICV3_G1NS]);
+
+ num_pri_bits = ((c->icc_ctlr_el1[GICV3_NS] &
+ ICC_CTLR_EL1_PRIBITS_MASK) >>
+ ICC_CTLR_EL1_PRIBITS_SHIFT) + 1;
+
+ switch (num_pri_bits) {
+ case 7:
+ reg64 = c->icc_apr[GICV3_G0][3];
+ hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1 + 3, reg64);
+ reg64 = c->icc_apr[GICV3_G0][2];
+ hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1 + 2, reg64);
+ /* fall through */
+ case 6:
+ reg64 = c->icc_apr[GICV3_G0][1];
+ hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1 + 1, reg64);
+ /* fall through */
+ default:
+ reg64 = c->icc_apr[GICV3_G0][0];
+ hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1, reg64);
+ }
+
+ switch (num_pri_bits) {
+ case 7:
+ reg64 = c->icc_apr[GICV3_G1NS][3];
+ hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1 + 3, reg64);
+ reg64 = c->icc_apr[GICV3_G1NS][2];
+ hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1 + 2, reg64);
+ /* fall through */
+ case 6:
+ reg64 = c->icc_apr[GICV3_G1NS][1];
+ hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1 + 1, reg64);
+ /* fall through */
+ default:
+ reg64 = c->icc_apr[GICV3_G1NS][0];
+ hv_gic_set_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1, reg64);
+ }
+ }
+}
+
+static void hvf_gicv3_get(GICv3State *s)
+{
+ uint64_t reg, redist_typer;
+ int ncpu, i;
+
+ hvf_gicv3_check(s);
+
+ hv_vcpu_t vcpu0 = s->cpu[0].cpu->accel->fd;
+ hv_gic_get_redistributor_reg(vcpu0,
+ HV_GIC_REDISTRIBUTOR_REG_GICR_TYPER, &redist_typer);
+
+ hv_gic_get_distributor_reg(HV_GIC_DISTRIBUTOR_REG_GICD_CTLR, ®);
+ s->gicd_ctlr = reg;
+
+ /* Redistributor state (one per CPU) */
+
+ for (ncpu = 0; ncpu < s->num_cpu; ncpu++) {
+ GICv3CPUState *c = &s->cpu[ncpu];
+ hv_vcpu_t vcpu = c->cpu->accel->fd;
+
+ hv_gic_get_redistributor_reg(vcpu,
+ HV_GIC_REDISTRIBUTOR_REG_GICR_IGROUPR0, ®);
+ c->gicr_igroupr0 = reg;
+ hv_gic_get_redistributor_reg(vcpu,
+ HV_GIC_REDISTRIBUTOR_REG_GICR_ISENABLER0, ®);
+ c->gicr_ienabler0 = reg;
+ hv_gic_get_redistributor_reg(vcpu,
+ HV_GIC_REDISTRIBUTOR_REG_GICR_ICFGR1, ®);
+ c->edge_trigger = half_unshuffle32(reg >> 1) << 16;
+ hv_gic_get_redistributor_reg(vcpu,
+ HV_GIC_REDISTRIBUTOR_REG_GICR_ISPENDR0, ®);
+ c->gicr_ipendr0 = reg;
+ hv_gic_get_redistributor_reg(vcpu,
+ HV_GIC_REDISTRIBUTOR_REG_GICR_ISACTIVER0, ®);
+ c->gicr_iactiver0 = reg;
+
+ for (i = 0; i < GIC_INTERNAL; i += 4) {
+ hv_gic_get_redistributor_reg(vcpu,
+ HV_GIC_REDISTRIBUTOR_REG_GICR_IPRIORITYR0 + i, ®);
+ c->gicr_ipriorityr[i] = extract32(reg, 0, 8);
+ c->gicr_ipriorityr[i + 1] = extract32(reg, 8, 8);
+ c->gicr_ipriorityr[i + 2] = extract32(reg, 16, 8);
+ c->gicr_ipriorityr[i + 3] = extract32(reg, 24, 8);
+ }
+ }
+
+ if (redist_typer & GICR_TYPER_PLPIS) {
+ error_report("ITS is not supported on HVF.");
+ abort();
+ }
+
+ /* GICD_IGROUPRn -> s->group bitmap */
+ hvf_dist_getbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_IGROUPR0, s->group);
+
+ /* GICD_ISENABLERn -> s->enabled bitmap */
+ hvf_dist_getbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_ISENABLER0, s->enabled);
+
+ /* GICD_ISPENDRn -> s->pending bitmap */
+ hvf_dist_getbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_ISPENDR0, s->pending);
+
+ /* GICD_ISACTIVERn -> s->active bitmap */
+ hvf_dist_getbmp(s, HV_GIC_DISTRIBUTOR_REG_GICD_ISACTIVER0, s->active);
+
+ /* GICD_ICFGRn -> s->trigger bitmap */
+ hvf_dist_get_edge_trigger(s, HV_GIC_DISTRIBUTOR_REG_GICD_ICFGR0
+ , s->edge_trigger);
+
+ /* GICD_IPRIORITYRn -> s->gicd_ipriority[] */
+ hvf_dist_get_priority(s, HV_GIC_DISTRIBUTOR_REG_GICD_IPRIORITYR0
+ , s->gicd_ipriority);
+
+ /* GICD_IROUTERn -> s->gicd_irouter[irq] */
+ for (i = GIC_INTERNAL; i < s->num_irq; i++) {
+ uint32_t offset = HV_GIC_DISTRIBUTOR_REG_GICD_IROUTER32
+ + (sizeof(uint32_t) * i) - (sizeof(uint32_t) * GIC_INTERNAL);
+ hv_gic_get_distributor_reg(offset, &s->gicd_irouter[i]);
+ }
+
+ /* CPU interface state (one per CPU) */
+
+ for (ncpu = 0; ncpu < s->num_cpu; ncpu++) {
+ GICv3CPUState *c = &s->cpu[ncpu];
+ hv_vcpu_t vcpu = c->cpu->accel->fd;
+ int num_pri_bits;
+
+ hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_SRE_EL1, &c->icc_sre_el1);
+
+ hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_CTLR_EL1,
+ &c->icc_ctlr_el1[GICV3_NS]);
+ hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_IGRPEN0_EL1,
+ &c->icc_igrpen[GICV3_G0]);
+ hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_IGRPEN1_EL1,
+ &c->icc_igrpen[GICV3_G1NS]);
+ hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_PMR_EL1
+ , &c->icc_pmr_el1);
+ hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_BPR0_EL1
+ , &c->icc_bpr[GICV3_G0]);
+ hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_BPR1_EL1
+ , &c->icc_bpr[GICV3_G1NS]);
+ num_pri_bits = ((c->icc_ctlr_el1[GICV3_NS] &
+ ICC_CTLR_EL1_PRIBITS_MASK) >>
+ ICC_CTLR_EL1_PRIBITS_SHIFT) + 1;
+
+ switch (num_pri_bits) {
+ case 7:
+ hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1 + 3
+ , &c->icc_apr[GICV3_G0][3]);
+ hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1 + 2
+ , &c->icc_apr[GICV3_G0][2]);
+ /* fall through */
+ case 6:
+ hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1 + 1
+ , &c->icc_apr[GICV3_G0][1]);
+ /* fall through */
+ default:
+ hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP0R0_EL1
+ , &c->icc_apr[GICV3_G0][0]);
+ }
+
+ switch (num_pri_bits) {
+ case 7:
+ hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1 + 3
+ , &c->icc_apr[GICV3_G1NS][3]);
+ hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1 + 2
+ , &c->icc_apr[GICV3_G1NS][2]);
+ /* fall through */
+ case 6:
+ hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1 + 1
+ , &c->icc_apr[GICV3_G1NS][1]);
+ /* fall through */
+ default:
+ hv_gic_get_icc_reg(vcpu, HV_GIC_ICC_REG_AP1R0_EL1
+ , &c->icc_apr[GICV3_G1NS][0]);
+ }
+ }
+}
+
+static void hvf_gicv3_set_irq(void *opaque, int irq, int level)
+{
+ GICv3State *s = (GICv3State *)opaque;
+ if (irq > s->num_irq) {
+ return;
+ }
+ hv_gic_set_spi(GIC_INTERNAL + irq, !!level);
+}
+
+static void hvf_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+ GICv3State *s;
+ GICv3CPUState *c;
+
+ c = (GICv3CPUState *)env->gicv3state;
+ s = c->gic;
+
+ c->icc_pmr_el1 = 0;
+ /*
+ * Architecturally the reset value of the ICC_BPR registers
+ * is UNKNOWN. We set them all to 0 here; when the kernel
+ * uses these values to program the ICH_VMCR_EL2 fields that
+ * determine the guest-visible ICC_BPR register values, the
+ * hardware's "writing a value less than the minimum sets
+ * the field to the minimum value" behaviour will result in
+ * them effectively resetting to the correct minimum value
+ * for the host GIC.
+ */
+ c->icc_bpr[GICV3_G0] = 0;
+ c->icc_bpr[GICV3_G1] = 0;
+ c->icc_bpr[GICV3_G1NS] = 0;
+
+ c->icc_sre_el1 = 0x7;
+ memset(c->icc_apr, 0, sizeof(c->icc_apr));
+ memset(c->icc_igrpen, 0, sizeof(c->icc_igrpen));
+
+ if (s->migration_blocker) {
+ return;
+ }
+
+ /* Initialize to actual HW supported configuration */
+ hv_gic_get_icc_reg(c->cpu->accel->fd,
+ HV_GIC_ICC_REG_CTLR_EL1, &c->icc_ctlr_el1[GICV3_NS]);
+
+ c->icc_ctlr_el1[GICV3_S] = c->icc_ctlr_el1[GICV3_NS];
+}
+
+static void hvf_gicv3_reset_hold(Object *obj, ResetType type)
+{
+ GICv3State *s = ARM_GICV3_COMMON(obj);
+ HVFARMGICv3Class *kgc = HVF_GICV3_GET_CLASS(s);
+
+ if (kgc->parent_phases.hold) {
+ kgc->parent_phases.hold(obj, type);
+ }
+
+ hvf_gicv3_put(s);
+}
+
+
+/*
+ * CPU interface registers of GIC needs to be reset on CPU reset.
+ * For the calling arm_gicv3_icc_reset() on CPU reset, we register
+ * below ARMCPRegInfo. As we reset the whole cpu interface under single
+ * register reset, we define only one register of CPU interface instead
+ * of defining all the registers.
+ */
+static const ARMCPRegInfo gicv3_cpuif_reginfo[] = {
+ { .name = "ICC_CTLR_EL1", .state = ARM_CP_STATE_BOTH,
+ .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 4,
+ /*
+ * If ARM_CP_NOP is used, resetfn is not called,
+ * So ARM_CP_NO_RAW is appropriate type.
+ */
+ .type = ARM_CP_NO_RAW,
+ .access = PL1_RW,
+ .readfn = arm_cp_read_zero,
+ .writefn = arm_cp_write_ignore,
+ /*
+ * We hang the whole cpu interface reset routine off here
+ * rather than parcelling it out into one little function
+ * per register
+ */
+ .resetfn = hvf_gicv3_icc_reset,
+ },
+};
+
+static void hvf_gicv3_realize(DeviceState *dev, Error **errp)
+{
+ GICv3State *s = HVF_GICV3(dev);
+ HVFARMGICv3Class *kgc = HVF_GICV3_GET_CLASS(s);
+ Error *local_err = NULL;
+ int i;
+
+ kgc->parent_realize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (s->revision != 3) {
+ error_setg(errp, "unsupported GIC revision %d for platform GIC",
+ s->revision);
+ }
+
+ if (s->security_extn) {
+ error_setg(errp, "the platform vGICv3 does not implement the "
+ "security extensions");
+ return;
+ }
+
+ if (s->nmi_support) {
+ error_setg(errp, "NMI is not supported with the platform GIC");
+ return;
+ }
+
+ if (s->nb_redist_regions > 1) {
+ error_setg(errp, "Multiple VGICv3 redistributor regions are not "
+ "supported by HVF");
+ error_append_hint(errp, "A maximum of %d VCPUs can be used",
+ s->redist_region_count[0]);
+ return;
+ }
+
+ gicv3_init_irqs_and_mmio(s, hvf_gicv3_set_irq, NULL);
+
+ for (i = 0; i < s->num_cpu; i++) {
+ ARMCPU *cpu = ARM_CPU(qemu_get_cpu(i));
+
+ define_arm_cp_regs(cpu, gicv3_cpuif_reginfo);
+ }
+
+ if (s->maint_irq && s->maint_irq != HV_GIC_INT_MAINTENANCE) {
+ error_setg(errp, "vGIC maintenance IRQ mismatch with the hardcoded one in HVF.");
+ return;
+ }
+}
+
+static void hvf_gicv3_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+ ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass);
+ HVFARMGICv3Class *kgc = HVF_GICV3_CLASS(klass);
+
+ agcc->pre_save = hvf_gicv3_get;
+ agcc->post_load = hvf_gicv3_put;
+
+ device_class_set_parent_realize(dc, hvf_gicv3_realize,
+ &kgc->parent_realize);
+ resettable_class_set_parent_phases(rc, NULL, hvf_gicv3_reset_hold, NULL,
+ &kgc->parent_phases);
+}
+
+static const TypeInfo hvf_arm_gicv3_info = {
+ .name = TYPE_HVF_GICV3,
+ .parent = TYPE_ARM_GICV3_COMMON,
+ .instance_size = sizeof(GICv3State),
+ .class_init = hvf_gicv3_class_init,
+ .class_size = sizeof(HVFARMGICv3Class),
+};
+
+static void hvf_gicv3_register_types(void)
+{
+ type_register_static(&hvf_arm_gicv3_info);
+}
+
+type_init(hvf_gicv3_register_types)
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index 3137521a4a..f446e966e3 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -42,6 +42,7 @@ specific_ss.add(when: 'CONFIG_ARM_GIC', if_true: files('arm_gicv3_cpuif_common.c
specific_ss.add(when: 'CONFIG_ARM_GICV3', if_true: files('arm_gicv3_cpuif.c'))
specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gic_kvm.c'))
specific_ss.add(when: ['CONFIG_ARM_GIC_KVM', 'TARGET_AARCH64'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c'))
+specific_ss.add(when: ['CONFIG_HVF', 'CONFIG_ARM_GICV3'], if_true: files('arm_gicv3_hvf.c'))
specific_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c'))
specific_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_irqmp.c'))
specific_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic.c'))
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 6/9] hw/arm, target/arm: nested virtualisation on HVF
2025-07-25 8:34 [PATCH 0/9] HVF: Add support for platform vGIC and nested virtualisation Mohamed Mediouni
` (4 preceding siblings ...)
2025-07-25 8:34 ` [PATCH 5/9] hw/intc: Add hvf vGIC interrupt controller support Mohamed Mediouni
@ 2025-07-25 8:34 ` Mohamed Mediouni
2025-07-25 8:34 ` [PATCH 7/9] target/arm: hvf: pass through CNTHCTL_EL2 and MDCCINT_EL1 Mohamed Mediouni
` (2 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Mohamed Mediouni @ 2025-07-25 8:34 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Igor Mammedov, Mads Ynddal, Alexander Graf,
Shannon Zhao, Michael S. Tsirkin, Peter Maydell, Cameron Esfahani,
Roman Bolshakov, Phil Dennis-Jordan, qemu-arm, Ani Sinha,
Mohamed Mediouni
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
hw/arm/virt.c | 6 +++---
target/arm/hvf-stub.c | 15 +++++++++++++++
target/arm/hvf/hvf.c | 35 +++++++++++++++++++++++++++++++++++
target/arm/hvf_arm.h | 3 +++
4 files changed, 56 insertions(+), 3 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index a26bde4c75..df77a1d911 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);
}
@@ -2278,7 +2277,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()
+ && !qtest_enabled()) {
error_report("mach-virt: %s does not support providing "
"Virtualization extensions to the guest CPU",
current_accel_name());
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 7b4e8297af..c32e6ab289 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;
+ }
+
/* We set up a small vcpu to extract host registers */
if (hv_vcpu_create(&fd, &exit, NULL) != HV_SUCCESS) {
@@ -964,6 +969,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) {
@@ -1000,6 +1024,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);
if (hvf_irqchip_in_kernel()) {
/*
@@ -1146,6 +1177,10 @@ static bool hvf_handle_psci_call(CPUState *cpu)
int target_el = 1;
int32_t ret = 0;
+ if (hvf_arm_el2_enabled()) {
+ target_el = 2;
+ }
+
trace_hvf_psci_call(param[0], param[1], param[2], param[3],
arm_cpu_mp_affinity(arm_cpu));
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)
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 7/9] target/arm: hvf: pass through CNTHCTL_EL2 and MDCCINT_EL1
2025-07-25 8:34 [PATCH 0/9] HVF: Add support for platform vGIC and nested virtualisation Mohamed Mediouni
` (5 preceding siblings ...)
2025-07-25 8:34 ` [PATCH 6/9] hw/arm, target/arm: nested virtualisation on HVF Mohamed Mediouni
@ 2025-07-25 8:34 ` Mohamed Mediouni
2025-07-25 8:34 ` [PATCH 8/9] hw/arm: virt: add GICv2m for the case when ITS is not available Mohamed Mediouni
2025-07-25 8:34 ` [PATCH 9/9] target/arm: hvf: use LOG_UNIMP for CNTP_CVAL_EL0/SYSREG_CNTP_CTL_EL0 Mohamed Mediouni
8 siblings, 0 replies; 10+ messages in thread
From: Mohamed Mediouni @ 2025-07-25 8:34 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Igor Mammedov, Mads Ynddal, Alexander Graf,
Shannon Zhao, Michael S. Tsirkin, Peter Maydell, Cameron Esfahani,
Roman Bolshakov, Phil Dennis-Jordan, qemu-arm, Ani Sinha,
Mohamed Mediouni
HVF traps accesses to CNTHCTL_EL2. For nested guests, HVF traps accesses to MDCCINT_EL1.
Pass through those accesses to the Hypervisor.framework library.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/arm/hvf/hvf.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
index c32e6ab289..5344e23db1 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -21,6 +21,7 @@
#include "cpregs.h"
#include "cpu-sysregs.h"
+#include <Hypervisor/hv_vcpu_types.h>
#include <mach/mach_time.h>
#include "system/address-spaces.h"
@@ -296,6 +297,10 @@ void hvf_arm_init_debug(void)
#define SYSREG_DBGWVR15_EL1 SYSREG(2, 0, 0, 15, 6)
#define SYSREG_DBGWCR15_EL1 SYSREG(2, 0, 0, 15, 7)
+/* EL2 registers */
+#define SYSREG_CNTHCTL_EL2 SYSREG(3, 4, 14, 1, 0)
+#define SYSREG_MDCCINT_EL1 SYSREG(2, 0, 0, 2, 0)
+
#define WFX_IS_WFE (1 << 0)
#define TMR_CTL_ENABLE (1 << 0)
@@ -1388,6 +1393,12 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val)
case SYSREG_OSDLR_EL1:
/* Dummy register */
return 0;
+ case SYSREG_CNTHCTL_EL2:
+ assert_hvf_ok(hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_CNTHCTL_EL2, val));
+ return 0;
+ case SYSREG_MDCCINT_EL1:
+ assert_hvf_ok(hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_MDCCINT_EL1, val));
+ return 0;
case SYSREG_ICC_AP0R0_EL1:
case SYSREG_ICC_AP0R1_EL1:
case SYSREG_ICC_AP0R2_EL1:
@@ -1704,6 +1715,12 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val)
case SYSREG_OSDLR_EL1:
/* Dummy register */
return 0;
+ case SYSREG_CNTHCTL_EL2:
+ assert_hvf_ok(hv_vcpu_set_sys_reg(cpu->accel->fd, HV_SYS_REG_CNTHCTL_EL2, val));
+ return 0;
+ case SYSREG_MDCCINT_EL1:
+ assert_hvf_ok(hv_vcpu_set_sys_reg(cpu->accel->fd, HV_SYS_REG_MDCCINT_EL1, val));
+ return 0;
case SYSREG_LORC_EL1:
/* Dummy register */
return 0;
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 8/9] hw/arm: virt: add GICv2m for the case when ITS is not available
2025-07-25 8:34 [PATCH 0/9] HVF: Add support for platform vGIC and nested virtualisation Mohamed Mediouni
` (6 preceding siblings ...)
2025-07-25 8:34 ` [PATCH 7/9] target/arm: hvf: pass through CNTHCTL_EL2 and MDCCINT_EL1 Mohamed Mediouni
@ 2025-07-25 8:34 ` Mohamed Mediouni
2025-07-25 8:34 ` [PATCH 9/9] target/arm: hvf: use LOG_UNIMP for CNTP_CVAL_EL0/SYSREG_CNTP_CTL_EL0 Mohamed Mediouni
8 siblings, 0 replies; 10+ messages in thread
From: Mohamed Mediouni @ 2025-07-25 8:34 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Igor Mammedov, Mads Ynddal, Alexander Graf,
Shannon Zhao, Michael S. Tsirkin, Peter Maydell, Cameron Esfahani,
Roman Bolshakov, Phil Dennis-Jordan, qemu-arm, Ani Sinha,
Mohamed Mediouni
On Hypervisor.framework for macOS and WHPX for Windows, the provided environment is a GICv3 without ITS.
As such, support a GICv3 w/ GICv2m for that scenario.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
hw/arm/virt-acpi-build.c | 4 +++-
hw/arm/virt.c | 8 ++++++++
include/hw/arm/virt.h | 2 ++
3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index b01fc4f8ef..969fa3f686 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -848,7 +848,9 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
build_append_int_noprefix(table_data, memmap[VIRT_GIC_ITS].base, 8);
build_append_int_noprefix(table_data, 0, 4); /* Reserved */
}
- } else {
+ }
+
+ if (!vms->its && !vms->no_gicv3_with_gicv2m) {
const uint16_t spi_base = vms->irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE;
/* 5.2.12.16 GIC MSI Frame Structure */
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index df77a1d911..3ac1e129b5 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -952,6 +952,8 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
if (vms->gic_version != VIRT_GIC_VERSION_2 && vms->its) {
create_its(vms);
+ } else if (vms->gic_version != VIRT_GIC_VERSION_2 && !vms->no_gicv3_with_gicv2m) {
+ create_v2m(vms);
} else if (vms->gic_version == VIRT_GIC_VERSION_2) {
create_v2m(vms);
}
@@ -2407,6 +2409,8 @@ static void machvirt_init(MachineState *machine)
vms->ns_el2_virt_timer_irq = ns_el2_virt_timer_present() &&
!vmc->no_ns_el2_virt_timer_irq;
+ vms->no_gicv3_with_gicv2m = vmc->no_gicv3_with_gicv2m;
+
fdt_add_timer_nodes(vms);
fdt_add_cpu_nodes(vms);
@@ -3415,6 +3419,7 @@ static void virt_instance_init(Object *obj)
vms->its = true;
/* Allow ITS emulation if the machine version supports it */
vms->tcg_its = !vmc->no_tcg_its;
+ vms->no_gicv3_with_gicv2m = false;
/* Default disallows iommu instantiation */
vms->iommu = VIRT_IOMMU_NONE;
@@ -3467,8 +3472,11 @@ DEFINE_VIRT_MACHINE_AS_LATEST(10, 1)
static void virt_machine_10_0_options(MachineClass *mc)
{
+ VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc));
+
virt_machine_10_1_options(mc);
compat_props_add(mc->compat_props, hw_compat_10_0, hw_compat_10_0_len);
+ vmc->no_gicv3_with_gicv2m = true;
}
DEFINE_VIRT_MACHINE(10, 0)
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 365a28b082..725ec18fd2 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -131,6 +131,7 @@ struct VirtMachineClass {
bool no_cpu_topology;
bool no_tcg_lpa2;
bool no_ns_el2_virt_timer_irq;
+ bool no_gicv3_with_gicv2m;
bool no_nested_smmu;
};
@@ -178,6 +179,7 @@ struct VirtMachineState {
char *oem_id;
char *oem_table_id;
bool ns_el2_virt_timer_irq;
+ bool no_gicv3_with_gicv2m;
CXLState cxl_devices_state;
};
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 9/9] target/arm: hvf: use LOG_UNIMP for CNTP_CVAL_EL0/SYSREG_CNTP_CTL_EL0
2025-07-25 8:34 [PATCH 0/9] HVF: Add support for platform vGIC and nested virtualisation Mohamed Mediouni
` (7 preceding siblings ...)
2025-07-25 8:34 ` [PATCH 8/9] hw/arm: virt: add GICv2m for the case when ITS is not available Mohamed Mediouni
@ 2025-07-25 8:34 ` Mohamed Mediouni
8 siblings, 0 replies; 10+ messages in thread
From: Mohamed Mediouni @ 2025-07-25 8:34 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Igor Mammedov, Mads Ynddal, Alexander Graf,
Shannon Zhao, Michael S. Tsirkin, Peter Maydell, Cameron Esfahani,
Roman Bolshakov, Phil Dennis-Jordan, qemu-arm, Ani Sinha,
Mohamed Mediouni
Instead of considering reads there to be fatal, mark it as unimplemented.
This is to allow experimentation on using configurations other than the Apple vGIC.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/arm/hvf/hvf.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
index 5344e23db1..d01b9b4dcd 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -300,6 +300,7 @@ void hvf_arm_init_debug(void)
/* EL2 registers */
#define SYSREG_CNTHCTL_EL2 SYSREG(3, 4, 14, 1, 0)
#define SYSREG_MDCCINT_EL1 SYSREG(2, 0, 0, 2, 0)
+#define SYSREG_CNTP_CVAL_EL0 SYSREG(3, 3, 14, 2, 2)
#define WFX_IS_WFE (1 << 0)
@@ -1396,6 +1397,12 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val)
case SYSREG_CNTHCTL_EL2:
assert_hvf_ok(hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_CNTHCTL_EL2, val));
return 0;
+ case SYSREG_CNTP_CTL_EL0:
+ qemu_log_mask(LOG_UNIMP, "Unsupported read from CNTP_CTL_EL0\n");
+ return 0;
+ case SYSREG_CNTP_CVAL_EL0:
+ qemu_log_mask(LOG_UNIMP, "Unsupported read from CNTP_CVAL_EL0\n");
+ return 0;
case SYSREG_MDCCINT_EL1:
assert_hvf_ok(hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_MDCCINT_EL1, val));
return 0;
@@ -1712,6 +1719,9 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val)
*/
qemu_log_mask(LOG_UNIMP, "Unsupported write to CNTP_CTL_EL0\n");
return 0;
+ case SYSREG_CNTP_CVAL_EL0:
+ qemu_log_mask(LOG_UNIMP, "Unsupported write to CNTP_CVAL_EL0\n");
+ return 0;
case SYSREG_OSDLR_EL1:
/* Dummy register */
return 0;
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 10+ messages in thread