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 <[email protected]> --- hw/intc/arm_gicv3_common.c | 1 + hw/intc/arm_gicv3_hvf.c | 91 ++++++++++++++++++++++++++++-- hw/intc/arm_gicv3_hvf_stub.c | 25 ++++++++ hw/intc/meson.build | 1 + include/hw/intc/arm_gicv3_common.h | 3 + 5 files changed, 117 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..9e4d974cd1 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,83 @@ 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."); + 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."); + return 1; + } + 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 +805,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..dd49fe1b9d 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. s*/ +extern const VMStateDescription vmstate_gicv3_hvf; + #endif -- 2.50.1 (Apple Git-155)
