From: Bertrand Marquis <Bertrand.Marquis@arm.com>
To: Mykola Kvach <mykola_kvach@epam.com>
Cc: "xen-devel@lists.xenproject.org" <xen-devel@lists.xenproject.org>,
Stefano Stabellini <sstabellini@kernel.org>,
Julien Grall <julien@xen.org>,
Michal Orzel <michal.orzel@amd.com>,
Volodymyr Babchuk <Volodymyr_Babchuk@epam.com>,
Luca Fancellu <Luca.Fancellu@arm.com>
Subject: Re: [PATCH v12 02/13] xen/arm: gic-v2: Implement GIC suspend/resume functions
Date: Wed, 23 Sep 2026 15:27:25 +0000 [thread overview]
Message-ID: <DD46DB7E-3D4A-46BB-9AD0-D529CDDA4113@arm.com> (raw)
In-Reply-To: <dbdba04cd531cf2f3ccfc0b7e8dbcb4925b25055.1787838455.git.mykola_kvach@epam.com>
Hi Mykola,
Sorry for the delay to review this serie.
> On 27 Aug 2026, at 16:31, Mykola Kvach <mykola_kvach@epam.com> wrote:
>
> From: Mirela Simonovic <mirela.simonovic@aggios.com>
>
> System suspend may lead to a state where GIC would be powered down.
> Therefore, Xen should save/restore the context of GIC on suspend/resume.
>
> Note that the context consists of states of registers which are
> controlled by the hypervisor. Other GIC registers which are accessible
> by guests are saved/restored on context switch.
>
> Transient physical SGI pending state (GICD_CPENDSGIRn/GICD_SPENDSGIRn)
> is intentionally excluded. CPU-interface active-priority state is also
> not restored across suspend/resume. Xen reaches the final suspend path
> at a quiescent point, so there is no active-priority execution context
> to replay after resume. Enforce this with a runtime check after
> disabling the CPU interface: if any implemented GICC_APRn word is still
> non-zero, restore GICC_CTLR and abort suspend with -EBUSY.
You mention SGI pending state but you do not say what would happen for PPI/SPI
pending state, and the patch does not look at or save/restore GICD_ISPENDR.
Can you clarify what is expected for those?
Cheers
Bertrand
>
> This does not apply to distributor active state. With GICv2 EOImode==1,
> EOIR only drops the interrupt priority; final deactivation is a separate
> step. For guest-routed interrupts, Xen can have already EOIed the physical
> IRQ while deactivation is still pending on the vGIC/GICV path. Therefore
> GICD_ISACTIVER is preserved as architectural in-flight interrupt state.
>
> Signed-off-by: Mirela Simonovic <mirela.simonovic@aggios.com>
> Signed-off-by: Saeed Nowshadi <saeed.nowshadi@xilinx.com>
> Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com>
> Signed-off-by: Mykola Kvach <mykola_kvach@epam.com>
> Reviewed-by: Luca Fancellu <luca.fancellu@arm.com>
> ---
> Changes in V10:
> - Limit GICC_APR<n> active-priority checks to APR bits visible from
> the Xen CPU-interface view.
> - Avoid touching reserved GICD_IPRIORITYR/GICD_ITARGETSR words when the
> last implemented interrupt block is partial.
> - Restore distributor configuration before restoring interrupt enable
> state, so GICD_ICFGR is written while the corresponding interrupts are
> disabled.
>
> Changes in V9:
> - Skip saving/restoring GICD_ITARGETSR0..7 because SGI/PPI target
> registers hold no state (read-only on MP, RAZ/WI on UP).
> - Add a runtime GICC_APRn quiescence check after disabling the CPU
> interface, and restore GICC_CTLR before returning -EBUSY.
>
> Changes in V8:
> - disable cpu interface + distributor before suspend
> - change 0xffffffff to GENMASK;
> - cosmetic changes;
>
> Changes in V7:
> - Allocate one contiguous memory block for the GICv2 dist suspend context.
> - gicv2_resume() no longer unconditionally re-enables the distributor/CPU
> interface; it now writes back the saved CTLR values as-is.
> - gicv2_alloc_context() now returns 0 on success and panics on failure,
> since suspend context allocation is not recoverable.
> ---
> xen/arch/arm/gic-v2.c | 226 +++++++++++++++++++++++++++++++++
> xen/arch/arm/gic.c | 29 +++++
> xen/arch/arm/include/asm/gic.h | 12 ++
> 3 files changed, 267 insertions(+)
>
> diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
> index 43a379fdda..a0ef6ffc7f 100644
> --- a/xen/arch/arm/gic-v2.c
> +++ b/xen/arch/arm/gic-v2.c
> @@ -1108,6 +1108,223 @@ static int gicv2_iomem_deny_access(struct domain *d)
> return iomem_deny_access(d, mfn, mfn + nr - 1);
> }
>
> +#ifdef CONFIG_SYSTEM_SUSPEND
> +
> +/* This struct represents block of 32 IRQs */
> +struct irq_block {
> + uint32_t icfgr[2]; /* 2 registers of 16 IRQs each */
> + uint32_t ipriorityr[8];
> + uint32_t isenabler;
> + uint32_t isactiver;
> + uint32_t itargetsr[8];
> +};
> +
> +/* GICv2 registers to be saved/restored on system suspend/resume */
> +struct gicv2_context {
> + /* GICC context */
> + struct cpu_ctx {
> + uint32_t ctlr;
> + uint32_t pmr;
> + uint32_t bpr;
> + } cpu;
> +
> + /* GICD context */
> + struct dist_ctx {
> + uint32_t ctlr;
> + /* Includes banked SGI/PPI state for the boot CPU. */
> + struct irq_block *irqs;
> + } dist;
> +};
> +
> +static struct gicv2_context gic_ctx;
> +
> +#define GICV2_NR_APRS 4
> +#define GICV2_APR_BITS_PER_REG 32U
> +
> +static int gicv2_check_active_priorities(uint32_t bpr)
> +{
> + unsigned int i, apr_bits, nr_aprs;
> +
> + /*
> + * Xen writes GICC_BPR to 0 during CPU init and does not change it. Per
> + * IHI0048B.b, a write below the implementation minimum reads back as the
> + * minimum supported BPR value. Table 4-47 maps that Xen-visible BPR value
> + * to the visible GICC_APR<n> bits. Avoid reading APR registers outside
> + * that visible range.
> + *
> + * This covers both GICv2 with and without Security Extensions.
> + */
> + apr_bits = 1U << (7 - (bpr & 0x7));
> + nr_aprs = DIV_ROUND_UP(apr_bits, GICV2_APR_BITS_PER_REG);
> +
> + ASSERT(nr_aprs <= GICV2_NR_APRS);
> +
> + for ( i = 0; i < nr_aprs; i++ )
> + {
> + unsigned int bits = min(GICV2_APR_BITS_PER_REG,
> + apr_bits - i * GICV2_APR_BITS_PER_REG);
> + uint32_t mask = GENMASK(bits - 1, 0);
> + uint32_t apr = readl_gicc(GICC_APR + i * 4) & mask;
> +
> + if ( !apr )
> + continue;
> +
> + printk(XENLOG_ERR "GICv2: suspend aborted: GICC_APR%u=%#08x\n",
> + i, apr);
> + return -EBUSY;
> + }
> +
> + return 0;
> +}
> +
> +static int gicv2_suspend(void)
> +{
> + unsigned int i, blocks = DIV_ROUND_UP(gicv2_info.nr_lines, 32);
> + int ret;
> +
> + /* Save GICC_CTLR configuration. */
> + gic_ctx.cpu.ctlr = readl_gicc(GICC_CTLR);
> +
> + /* Quiesce the GIC CPU interface before suspend. */
> + gicv2_cpu_disable();
> +
> + gic_ctx.cpu.bpr = readl_gicc(GICC_BPR);
> +
> + /*
> + * Check the active-priority state for the group Xen drives through the
> + * CPU interface. GICC_CTL_ENABLE enables Group 0 without SecurityExtn and
> + * Group 1 in Xen's Non-secure view with SecurityExtn, and in both cases
> + * the relevant state is visible through GICC_APRn. The APR layout is
> + * implementation-defined, so only test the bits visible from Xen's CPU
> + * interface view instead of reading every possible APR register.
> + */
> + ret = gicv2_check_active_priorities(gic_ctx.cpu.bpr);
> + if ( ret )
> + {
> + writel_gicc(gic_ctx.cpu.ctlr, GICC_CTLR);
> + return ret;
> + }
> +
> + gic_ctx.cpu.pmr = readl_gicc(GICC_PMR);
> +
> + /* Save GICD configuration */
> + gic_ctx.dist.ctlr = readl_gicd(GICD_CTLR);
> + writel_gicd(0, GICD_CTLR);
> +
> + for ( i = 0; i < blocks; i++ )
> + {
> + struct irq_block *irqs = gic_ctx.dist.irqs + i;
> + size_t j, off = i * sizeof(irqs->isenabler);
> + size_t nr_regs = ARRAY_SIZE(irqs->ipriorityr);
> +
> + if ( i == blocks - 1 )
> + nr_regs = DIV_ROUND_UP(gicv2_info.nr_lines - i * 32, 4);
> +
> + irqs->isenabler = readl_gicd(GICD_ISENABLER + off);
> +
> + /*
> + * Save distributor active state as part of the hypervisor-owned
> + * physical interrupt state. In GICv2 EOImode==1, EOIR only drops the
> + * priority; final deactivation is separate. For guest-routed
> + * interrupts, Xen may have EOIed the physical IRQ while the guest/vGIC
> + * side still owns the deactivate step. Therefore GICD_ISACTIVER can
> + * legitimately remain set even though transient SGI pending state and
> + * CPU-interface active-priority state are expected to be quiesced here.
> + */
> + irqs->isactiver = readl_gicd(GICD_ISACTIVER + off);
> +
> + off = i * sizeof(irqs->ipriorityr);
> + for ( j = 0; j < nr_regs; j++ )
> + irqs->ipriorityr[j] = readl_gicd(GICD_IPRIORITYR + off + j * 4);
> +
> + /*
> + * GICD_ITARGETSR0..7 cover SGIs/PPIs and hold no state to save:
> + * they are read-only on multiprocessor implementations and RAZ/WI
> + * on uniprocessor implementations.
> + */
> + if ( i )
> + {
> + off = i * sizeof(irqs->itargetsr);
> + for ( j = 0; j < nr_regs; j++ )
> + irqs->itargetsr[j] = readl_gicd(GICD_ITARGETSR + off + j * 4);
> + }
> +
> + off = i * sizeof(irqs->icfgr);
> + for ( j = 0; j < ARRAY_SIZE(irqs->icfgr); j++ )
> + irqs->icfgr[j] = readl_gicd(GICD_ICFGR + off + j * 4);
> + }
> +
> + return 0;
> +}
> +
> +static void gicv2_resume(void)
> +{
> + unsigned int i, blocks = DIV_ROUND_UP(gicv2_info.nr_lines, 32);
> +
> + gicv2_cpu_disable();
> + /* Disable distributor */
> + writel_gicd(0, GICD_CTLR);
> +
> + for ( i = 0; i < blocks; i++ )
> + {
> + struct irq_block *irqs = gic_ctx.dist.irqs + i;
> + size_t j, off = i * sizeof(irqs->isenabler);
> + size_t nr_regs = ARRAY_SIZE(irqs->ipriorityr);
> +
> + if ( i == blocks - 1 )
> + nr_regs = DIV_ROUND_UP(gicv2_info.nr_lines - i * 32, 4);
> +
> + writel_gicd(GENMASK(31, 0), GICD_ICENABLER + off);
> +
> + off = i * sizeof(irqs->icfgr);
> + for ( j = 0; j < ARRAY_SIZE(irqs->icfgr); j++ )
> + writel_gicd(irqs->icfgr[j], GICD_ICFGR + off + j * 4);
> +
> + off = i * sizeof(irqs->ipriorityr);
> + for ( j = 0; j < nr_regs; j++ )
> + writel_gicd(irqs->ipriorityr[j], GICD_IPRIORITYR + off + j * 4);
> +
> + /*
> + * GICD_ITARGETSR0..7 cover SGIs/PPIs and hold no state to save:
> + * they are read-only on multiprocessor implementations and RAZ/WI
> + * on uniprocessor implementations.
> + */
> + if ( i )
> + {
> + off = i * sizeof(irqs->itargetsr);
> + for ( j = 0; j < nr_regs; j++ )
> + writel_gicd(irqs->itargetsr[j], GICD_ITARGETSR + off + j * 4);
> + }
> +
> + off = i * sizeof(irqs->isenabler);
> + writel_gicd(irqs->isenabler, GICD_ISENABLER + off);
> +
> + writel_gicd(GENMASK(31, 0), GICD_ICACTIVER + off);
> + writel_gicd(irqs->isactiver, GICD_ISACTIVER + off);
> + }
> +
> + /* Restore distributor control state. */
> + writel_gicd(gic_ctx.dist.ctlr, GICD_CTLR);
> +
> + /* Restore GIC CPU interface configuration */
> + writel_gicc(gic_ctx.cpu.pmr, GICC_PMR);
> + writel_gicc(gic_ctx.cpu.bpr, GICC_BPR);
> +
> + /* Enable GIC CPU interface */
> + writel_gicc(gic_ctx.cpu.ctlr, GICC_CTLR);
> +}
> +
> +static void __init gicv2_alloc_context(void)
> +{
> + uint32_t blocks = DIV_ROUND_UP(gicv2_info.nr_lines, 32);
> +
> + gic_ctx.dist.irqs = xzalloc_array(struct irq_block, blocks);
> + if ( !gic_ctx.dist.irqs )
> + panic("Failed to allocate memory for GICv2 suspend context\n");
> +}
> +
> +#endif /* CONFIG_SYSTEM_SUSPEND */
> +
> #ifdef CONFIG_ACPI
> static unsigned long gicv2_get_hwdom_extra_madt_size(const struct domain *d)
> {
> @@ -1312,6 +1529,11 @@ static int __init gicv2_init(void)
>
> spin_unlock(&gicv2.lock);
>
> +#ifdef CONFIG_SYSTEM_SUSPEND
> + /* Allocate memory to be used for saving GIC context during the suspend */
> + gicv2_alloc_context();
> +#endif /* CONFIG_SYSTEM_SUSPEND */
> +
> return 0;
> }
>
> @@ -1355,6 +1577,10 @@ static const struct gic_hw_operations gicv2_ops = {
> .map_hwdom_extra_mappings = gicv2_map_hwdom_extra_mappings,
> .iomem_deny_access = gicv2_iomem_deny_access,
> .do_LPI = gicv2_do_LPI,
> +#ifdef CONFIG_SYSTEM_SUSPEND
> + .suspend = gicv2_suspend,
> + .resume = gicv2_resume,
> +#endif /* CONFIG_SYSTEM_SUSPEND */
> };
>
> /* Set up the GIC */
> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
> index 078049e741..ffc11f36a1 100644
> --- a/xen/arch/arm/gic.c
> +++ b/xen/arch/arm/gic.c
> @@ -438,6 +438,35 @@ int gic_iomem_deny_access(struct domain *d)
> return gic_hw_ops->iomem_deny_access(d);
> }
>
> +#ifdef CONFIG_SYSTEM_SUSPEND
> +
> +int gic_suspend(void)
> +{
> + /* Must be called by boot CPU#0 with interrupts disabled */
> + ASSERT(!local_irq_is_enabled());
> + ASSERT(!smp_processor_id());
> +
> + if ( !gic_hw_ops->suspend || !gic_hw_ops->resume )
> + return -ENOSYS;
> +
> + return gic_hw_ops->suspend();
> +}
> +
> +void gic_resume(void)
> +{
> + /*
> + * Must be called by boot CPU#0 with interrupts disabled after gic_suspend
> + * has returned successfully.
> + */
> + ASSERT(!local_irq_is_enabled());
> + ASSERT(!smp_processor_id());
> + ASSERT(gic_hw_ops->resume);
> +
> + gic_hw_ops->resume();
> +}
> +
> +#endif /* CONFIG_SYSTEM_SUSPEND */
> +
> static int cpu_gic_callback(struct notifier_block *nfb,
> unsigned long action,
> void *hcpu)
> diff --git a/xen/arch/arm/include/asm/gic.h b/xen/arch/arm/include/asm/gic.h
> index ee2c26adb4..29bb9a89a4 100644
> --- a/xen/arch/arm/include/asm/gic.h
> +++ b/xen/arch/arm/include/asm/gic.h
> @@ -301,6 +301,12 @@ extern int gicv_setup(struct domain *d);
> extern void gic_save_state(struct vcpu *v);
> extern void gic_restore_state(struct vcpu *v);
>
> +#ifdef CONFIG_SYSTEM_SUSPEND
> +/* Suspend/resume */
> +extern int gic_suspend(void);
> +extern void gic_resume(void);
> +#endif /* CONFIG_SYSTEM_SUSPEND */
> +
> /* SGI (AKA IPIs) */
> enum gic_sgi {
> GIC_SGI_EVENT_CHECK,
> @@ -444,6 +450,12 @@ struct gic_hw_operations {
> int (*iomem_deny_access)(struct domain *d);
> /* Handle LPIs, which require special handling */
> void (*do_LPI)(unsigned int lpi);
> +#ifdef CONFIG_SYSTEM_SUSPEND
> + /* Save GIC configuration due to the system suspend */
> + int (*suspend)(void);
> + /* Restore GIC configuration due to the system resume */
> + void (*resume)(void);
> +#endif /* CONFIG_SYSTEM_SUSPEND */
> };
>
> extern const struct gic_hw_operations *gic_hw_ops;
> --
> 2.43.0
>
next prev parent reply other threads:[~2026-09-23 15:28 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-27 14:31 [PATCH v12 00/13] Add initial Xen Suspend-to-RAM support on ARM64 Mykola Kvach
2026-08-27 14:31 ` [PATCH v12 01/13] xen/arm: Add suspend and resume timer helpers Mykola Kvach
2026-08-27 14:31 ` [PATCH v12 02/13] xen/arm: gic-v2: Implement GIC suspend/resume functions Mykola Kvach
2026-09-23 15:27 ` Bertrand Marquis [this message]
2026-09-24 22:23 ` Mykola Kvach
2026-09-28 7:36 ` Bertrand Marquis
2026-08-27 14:31 ` [PATCH v12 03/13] xen/arm: gic-v3: tolerate retained redistributor LPI state across CPU_OFF Mykola Kvach
2026-09-23 15:34 ` Bertrand Marquis
2026-09-24 23:22 ` Mykola Kvach
2026-09-28 7:37 ` Bertrand Marquis
2026-08-27 14:31 ` [PATCH v12 04/13] xen/arm: gic-v3: Implement GICv3 suspend/resume functions Mykola Kvach
2026-09-23 15:35 ` Bertrand Marquis
2026-09-25 0:03 ` Mykola Kvach
2026-09-28 7:39 ` Bertrand Marquis
2026-08-27 14:31 ` [PATCH v12 05/13] xen/arm: gic-v3: add ITS suspend/resume support Mykola Kvach
2026-09-23 15:36 ` Bertrand Marquis
2026-08-27 14:31 ` [PATCH v12 06/13] xen/arm: tee: keep init_tee_secondary() for hotplug and resume Mykola Kvach
2026-08-27 14:31 ` [PATCH v12 07/13] xen/arm: ffa: fix notification SRI across CPU hotplug/suspend Mykola Kvach
2026-08-27 14:31 ` [PATCH v12 08/13] iommu/ipmmu-vmsa: Implement suspend/resume callbacks Mykola Kvach
2026-09-28 8:01 ` Mykola Kvach
2026-09-28 9:33 ` Bertrand Marquis
2026-08-27 14:31 ` [PATCH v12 09/13] xen/arm: smmu-v3: add suspend/resume handlers Mykola Kvach
2026-09-28 16:17 ` Bertrand Marquis
2026-09-30 14:44 ` Mykola Kvach
2026-08-27 14:31 ` [PATCH v12 10/13] xen/arm64: Save/restore CPU context across SYSTEM_SUSPEND Mykola Kvach
2026-09-28 16:17 ` Bertrand Marquis
2026-08-27 14:31 ` [PATCH v12 11/13] xen/arm: Implement PSCI SYSTEM_SUSPEND call (host interface) Mykola Kvach
2026-09-28 16:18 ` Bertrand Marquis
2026-09-30 17:32 ` Mykola Kvach
2026-08-27 14:32 ` [PATCH v12 12/13] xen/arm: Add vPSCI SYSTEM_SUSPEND policy Mykola Kvach
2026-09-28 16:18 ` Bertrand Marquis
2026-09-30 20:42 ` Mykola Kvach
2026-08-27 14:32 ` [PATCH v12 13/13] xen/arm: Add host system suspend backend Mykola Kvach
2026-08-27 21:59 ` Volodymyr Babchuk
2026-09-28 16:19 ` Bertrand Marquis
2026-09-30 22:10 ` Mykola Kvach
2026-09-22 7:04 ` Ping: [PATCH v12 00/13] Add initial Xen Suspend-to-RAM support on ARM64 Mykola Kvach
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DD46DB7E-3D4A-46BB-9AD0-D529CDDA4113@arm.com \
--to=bertrand.marquis@arm.com \
--cc=Luca.Fancellu@arm.com \
--cc=Volodymyr_Babchuk@epam.com \
--cc=julien@xen.org \
--cc=michal.orzel@amd.com \
--cc=mykola_kvach@epam.com \
--cc=sstabellini@kernel.org \
--cc=xen-devel@lists.xenproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.