hw/intc: arm_gicv3_hvf: save/restore Apple GIC state
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> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Message-id: 20260429190532.26538-3-mohamed@unpredictable.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Mohamed Mediouni committed
May 5, 2026 at 09:25 UTC
48396ad6ce9ac4ec5ec97d45435c947b582c7f39
5 files changed
+120
-4
hw/intc/arm_gicv3_common.c
+1
@@ -305,6 +305,7 @@ static const VMStateDescription vmstate_gicv3 = {
305
.subsections = (const VMStateDescription * const []) {
306
&vmstate_gicv3_gicd_no_migration_shift_bug,
307
&vmstate_gicv3_gicd_nmi,
308
+ &vmstate_gicv3_hvf,
309
NULL
310
}
311
};
hw/intc/arm_gicv3_hvf.c
+90
-4
@@ -13,6 +13,7 @@
13
#include "qemu/error-report.h"
14
#include "qemu/module.h"
15
#include "system/runstate.h"
16
+#include "migration/vmstate.h"
17
#include "system/hvf.h"
18
#include "system/hvf_int.h"
19
#include "hvf_arm.h"
@@ -37,8 +38,13 @@ struct HVFARMGICv3Class {
38
39
typedef struct HVFARMGICv3Class HVFARMGICv3Class;
40
40
-/* This is reusing the GICv3State typedef from ARM_GICV3_ITS_COMMON */
41
-DECLARE_OBJ_CHECKERS(GICv3State, HVFARMGICv3Class,
41
+typedef struct HVFGICv3State {
42
+ GICv3State parent_obj;
43
+ uint32_t size;
44
+ void *state;
45
+} HVFGICv3State;
46
+
47
+DECLARE_OBJ_CHECKERS(HVFGICv3State, HVFARMGICv3Class,
48
HVF_GICV3, TYPE_HVF_GICV3);
49
50
/*
@@ -668,7 +674,7 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = {
674
static void hvf_gicv3_realize(DeviceState *dev, Error **errp)
675
{
676
ERRP_GUARD();
671
- GICv3State *s = HVF_GICV3(dev);
677
+ GICv3State *s = (GICv3State *)HVF_GICV3(dev);
678
HVFARMGICv3Class *kgc = HVF_GICV3_GET_CLASS(s);
679
int i;
680
@@ -715,6 +721,86 @@ static void hvf_gicv3_realize(DeviceState *dev, Error **errp)
721
}
722
}
723
724
+/*
725
+ * HVF doesn't have a way to save the RDIST pending tables
726
+ * to guest memory, only to an opaque data structure.
727
+ */
728
+static bool gicv3_is_hvf(void *opaque)
729
+{
730
+ return hvf_enabled() && hvf_irqchip_in_kernel();
731
+}
732
+
733
+static int hvf_gic_opaque_state_save(void *opaque)
734
+{
735
+ HVFGICv3State *gic = opaque;
736
+ hv_gic_state_t gic_state;
737
+ hv_return_t err;
738
+ size_t size;
739
+
740
+ gic_state = hv_gic_state_create();
741
+ if (gic_state == NULL) {
742
+ error_report("hvf: vgic: failed to create hv_gic_state_create.");
743
+ return 1;
744
+ }
745
+ err = hv_gic_state_get_size(gic_state, &size);
746
+ gic->size = size;
747
+ if (err != HV_SUCCESS) {
748
+ error_report("hvf: vgic: failed to get GIC state size.");
749
+ os_release(gic_state);
750
+ return 1;
751
+ }
752
+ gic->state = g_malloc0(gic->size);
753
+ err = hv_gic_state_get_data(gic_state, gic->state);
754
+ if (err != HV_SUCCESS) {
755
+ error_report("hvf: vgic: failed to get GIC state.");
756
+ os_release(gic_state);
757
+ return 1;
758
+ }
759
+
760
+ os_release(gic_state);
761
+ return 0;
762
+}
763
+
764
+static void hvf_gic_opaque_state_free(void *opaque)
765
+{
766
+ HVFGICv3State *gic = opaque;
767
+ free(gic->state);
768
+}
769
+
770
+static int hvf_gic_opaque_state_restore(void *opaque, int version_id)
771
+{
772
+ HVFGICv3State *gic = opaque;
773
+ hv_return_t err;
774
+ if (!gic->size) {
775
+ return 0;
776
+ }
777
+ err = hv_gic_set_state(gic->state, gic->size);
778
+ if (err != HV_SUCCESS) {
779
+ error_report("hvf: vgic: failed to restore GIC state.");
780
+ return 1;
781
+ }
782
+ return 0;
783
+}
784
+
785
+const VMStateDescription vmstate_gicv3_hvf = {
786
+ .name = "arm_gicv3/hvf_gic_state",
787
+ .version_id = 1,
788
+ .minimum_version_id = 1,
789
+ .needed = gicv3_is_hvf,
790
+ .pre_save = hvf_gic_opaque_state_save,
791
+ .post_save = hvf_gic_opaque_state_free,
792
+ .post_load = hvf_gic_opaque_state_restore,
793
+ .version_id = 1,
794
+ .minimum_version_id = 1,
795
+ .fields = (const VMStateField[]) {
796
+ VMSTATE_UINT32(size, HVFGICv3State),
797
+ VMSTATE_VBUFFER_ALLOC_UINT32(state,
798
+ HVFGICv3State, 0, 0,
799
+ size),
800
+ VMSTATE_END_OF_LIST()
801
+ },
802
+};
803
+
804
static void hvf_gicv3_class_init(ObjectClass *klass, const void *data)
805
{
806
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -734,7 +820,7 @@ static void hvf_gicv3_class_init(ObjectClass *klass, const void *data)
820
static const TypeInfo hvf_arm_gicv3_info = {
821
.name = TYPE_HVF_GICV3,
822
.parent = TYPE_ARM_GICV3_COMMON,
737
- .instance_size = sizeof(GICv3State),
823
+ .instance_size = sizeof(HVFGICv3State),
824
.class_init = hvf_gicv3_class_init,
825
.class_size = sizeof(HVFARMGICv3Class),
826
};
hw/intc/arm_gicv3_hvf_stub.c
new
+25
@@ -0,0 +1,25 @@
1
+/* SPDX-License-Identifier: GPL-2.0-or-later */
2
+/*
3
+ * ARM Generic Interrupt Controller using HVF platform support stub
4
+ *
5
+ * Copyright (c) 2026 Mohamed Mediouni
6
+ *
7
+ */
8
+#include "qemu/osdep.h"
9
+#include "hw/intc/arm_gicv3_common.h"
10
+#include "migration/vmstate.h"
11
+#include "qemu/typedefs.h"
12
+
13
+static bool needed_never(void *opaque)
14
+{
15
+ return false;
16
+}
17
+
18
+const VMStateDescription vmstate_gicv3_hvf = {
19
+ .name = "arm_gicv3/hvf_gic_state",
20
+ .version_id = 1,
21
+ .minimum_version_id = 1,
22
+ .needed = needed_never,
23
+ .version_id = 1,
24
+ .minimum_version_id = 1,
25
+};
hw/intc/meson.build
+1
@@ -43,6 +43,7 @@ arm_common_ss.add(when: 'CONFIG_ARM_GICV3', if_true: files('arm_gicv3_cpuif.c'))
43
specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gic_kvm.c'))
44
specific_ss.add(when: ['CONFIG_WHPX', 'TARGET_AARCH64'], if_true: files('arm_gicv3_whpx.c'))
45
specific_ss.add(when: ['CONFIG_HVF', 'CONFIG_ARM_GICV3'], if_true: files('arm_gicv3_hvf.c'))
46
+specific_ss.add(when: ['CONFIG_HVF', 'CONFIG_ARM_GICV3'], if_false: files('arm_gicv3_hvf_stub.c'))
47
specific_ss.add(when: ['CONFIG_ARM_GIC_KVM', 'TARGET_AARCH64'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c'))
48
arm_common_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c'))
49
specific_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_irqmp.c'))
include/hw/intc/arm_gicv3_common.h
+3
@@ -339,4 +339,7 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler,
339
*/
340
const char *gicv3_class_name(void);
341
342
+/* HVF vGIC-specific state: stubbed out on a build with HVF disabled */
343
+extern const VMStateDescription vmstate_gicv3_hvf;
344
+
345
#endif