On HVF, some of the GIC state is in an opaque Apple-provided structure.
Save/restore that state to be able to save/restore VMs that use the hardware GIC.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
hw/intc/arm_gicv3_common.c | 1 +
hw/intc/arm_gicv3_hvf.c | 95 ++++++++++++++++++++++++++++--
hw/intc/arm_gicv3_hvf_stub.c | 25 ++++++++
hw/intc/meson.build | 1 +
include/hw/intc/arm_gicv3_common.h | 3 +
5 files changed, 121 insertions(+), 4 deletions(-)
create mode 100644 hw/intc/arm_gicv3_hvf_stub.c
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index 9200671c7a..9c3fb2f4bf 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -305,6 +305,7 @@ static const VMStateDescription vmstate_gicv3 = {
.subsections = (const VMStateDescription * const []) {
&vmstate_gicv3_gicd_no_migration_shift_bug,
&vmstate_gicv3_gicd_nmi,
+ &vmstate_gicv3_hvf,
NULL
}
};
diff --git a/hw/intc/arm_gicv3_hvf.c b/hw/intc/arm_gicv3_hvf.c
index d6a46b7d53..f2249a7e7c 100644
--- a/hw/intc/arm_gicv3_hvf.c
+++ b/hw/intc/arm_gicv3_hvf.c
@@ -13,6 +13,7 @@
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "system/runstate.h"
+#include "migration/vmstate.h"
#include "system/hvf.h"
#include "system/hvf_int.h"
#include "hvf_arm.h"
@@ -30,8 +31,13 @@ struct HVFARMGICv3Class {
typedef struct HVFARMGICv3Class HVFARMGICv3Class;
-/* This is reusing the GICv3State typedef from ARM_GICV3_ITS_COMMON */
-DECLARE_OBJ_CHECKERS(GICv3State, HVFARMGICv3Class,
+typedef struct HVFGICv3State {
+ GICv3State gicv3_state;
+ uint32_t size;
+ void *state;
+} HVFGICv3State;
+
+DECLARE_OBJ_CHECKERS(HVFGICv3State, HVFARMGICv3Class,
HVF_GICV3, TYPE_HVF_GICV3);
/*
@@ -656,7 +662,7 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = {
static void hvf_gicv3_realize(DeviceState *dev, Error **errp)
{
ERRP_GUARD();
- GICv3State *s = HVF_GICV3(dev);
+ GICv3State *s = (GICv3State *)HVF_GICV3(dev);
HVFARMGICv3Class *kgc = HVF_GICV3_GET_CLASS(s);
int i;
@@ -703,6 +709,87 @@ static void hvf_gicv3_realize(DeviceState *dev, Error **errp)
}
}
+/*
+ * HVF doesn't have a way to save the RDIST pending tables
+ * to guest memory, only to an opaque data structure.
+ */
+static bool gicv3_is_hvf(void *opaque)
+{
+ return hvf_enabled() && hvf_irqchip_in_kernel();
+}
+
+static int hvf_gic_opaque_state_save(void* opaque)
+{
+ HVFGICv3State* gic = opaque;
+ hv_gic_state_t gic_state;
+ hv_return_t err;
+ size_t size;
+
+ gic_state = hv_gic_state_create();
+ if (gic_state == NULL) {
+ error_report("hvf: vgic: failed to create hv_gic_state_create.");
+ return 1;
+ }
+ err = hv_gic_state_get_size(gic_state, &size);
+ gic->size = size;
+ if (err != HV_SUCCESS) {
+ error_report("hvf: vgic: failed to get GIC state size.");
+ os_release(gic_state);
+ return 1;
+ }
+ gic->state = g_malloc(gic->size);
+ err = hv_gic_state_get_data(gic_state, gic->state);
+ if (err != HV_SUCCESS) {
+ error_report("hvf: vgic: failed to get GIC state.");
+ os_release(gic_state);
+ return 1;
+ }
+
+ os_release(gic_state);
+ return 0;
+}
+
+static int hvf_gic_opaque_state_free(void* opaque)
+{
+ HVFGICv3State* gic = opaque;
+ free(gic->state);
+ return 0;
+}
+
+static int hvf_gic_opaque_state_restore(void* opaque, int version_id)
+{
+ HVFGICv3State* gic = opaque;
+ hv_return_t err;
+ if (!gic->size) {
+ return 0;
+ }
+ err = hv_gic_set_state(gic->state, gic->size);
+ if (err != HV_SUCCESS) {
+ error_report("hvf: vgic: failed to restore GIC state.");
+ return 1;
+ }
+ return 0;
+}
+
+const VMStateDescription vmstate_gicv3_hvf = {
+ .name = "arm_gicv3/hvf_gic_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = gicv3_is_hvf,
+ .pre_save = hvf_gic_opaque_state_save,
+ .post_save = hvf_gic_opaque_state_free,
+ .post_load = hvf_gic_opaque_state_restore,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(size, HVFGICv3State),
+ VMSTATE_VBUFFER_ALLOC_UINT32(state,
+ HVFGICv3State, 0, 0,
+ size),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
static void hvf_gicv3_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -722,7 +809,7 @@ static void hvf_gicv3_class_init(ObjectClass *klass, const void *data)
static const TypeInfo hvf_arm_gicv3_info = {
.name = TYPE_HVF_GICV3,
.parent = TYPE_ARM_GICV3_COMMON,
- .instance_size = sizeof(GICv3State),
+ .instance_size = sizeof(HVFGICv3State),
.class_init = hvf_gicv3_class_init,
.class_size = sizeof(HVFARMGICv3Class),
};
diff --git a/hw/intc/arm_gicv3_hvf_stub.c b/hw/intc/arm_gicv3_hvf_stub.c
new file mode 100644
index 0000000000..a587332c7c
--- /dev/null
+++ b/hw/intc/arm_gicv3_hvf_stub.c
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * ARM Generic Interrupt Controller using HVF platform support stub
+ *
+ * Copyright (c) 2026 Mohamed Mediouni
+ *
+ */
+#include "qemu/osdep.h"
+#include "hw/intc/arm_gicv3_common.h"
+#include "migration/vmstate.h"
+#include "qemu/typedefs.h"
+
+static bool needed_never(void *opaque)
+{
+ return false;
+}
+
+const VMStateDescription vmstate_gicv3_hvf = {
+ .name = "arm_gicv3/hvf_gic_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = needed_never,
+ .version_id = 1,
+ .minimum_version_id = 1,
+};
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index b7baf8a0f6..c6de2d9d00 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -43,6 +43,7 @@ arm_common_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_WHPX', 'TARGET_AARCH64'], if_true: files('arm_gicv3_whpx.c'))
specific_ss.add(when: ['CONFIG_HVF', 'CONFIG_ARM_GICV3'], if_true: files('arm_gicv3_hvf.c'))
+specific_ss.add(when: ['CONFIG_HVF', 'CONFIG_ARM_GICV3'], if_false: files('arm_gicv3_hvf_stub.c'))
specific_ss.add(when: ['CONFIG_ARM_GIC_KVM', 'TARGET_AARCH64'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c'))
arm_common_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c'))
specific_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_irqmp.c'))
diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
index 9adcab0a0c..03ab3e8f2f 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -339,4 +339,7 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler,
*/
const char *gicv3_class_name(void);
+/* HVF vGIC-specific state: stubbed out on a build with HVF disabled */
+extern const VMStateDescription vmstate_gicv3_hvf;
+
#endif
--
2.50.1 (Apple Git-155)