[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v12 02/13] xen/arm: gic-v2: Implement GIC suspend/resume functions


  • To: Mykola Kvach <mykola_kvach@xxxxxxxx>
  • From: Bertrand Marquis <Bertrand.Marquis@xxxxxxx>
  • Date: Wed, 23 Sep 2026 15:27:25 +0000
  • Accept-language: en-GB, en-US
  • Arc-authentication-results: i=2; mx.microsoft.com 1; spf=pass (sender ip is 4.158.2.129) smtp.rcpttodomain=epam.com smtp.mailfrom=arm.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=arm.com; dkim=pass (signature was verified) header.d=arm.com; arc=pass (0 oda=1 ltdi=1 spf=[1,1,smtp.mailfrom=arm.com] dkim=[1,1,header.d=arm.com] dmarc=[1,1,header.from=arm.com])
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=arm.com; dmarc=pass action=none header.from=arm.com; dkim=pass header.d=arm.com; arc=none
  • Arc-message-signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=dPnnU/bovfMLMFk7Fz37gjc4NKrWmTRVmNUudIt/WoA=; b=GNcFOHz/+KIUxIAXMG8eTbbYEa9RCpIHHxRXcjAtBTBtZIgY1/whpiE3k9asWzaSsJ3RqYbx2SdoI784LpG8FKz+khea8C/tF61kcw+kth+mHRtnIthsf+a4dKMg6Wtr0kc8WUi1l+TRQAu7IZd0NhqebtzVwpCA+DkG9mviWQGm+XiWEDJe5WM25awlxueaO6LI+XFY+yBXeg+c75Fsp1qP+Y3KZLJbbqJkGU831OTFswLIKGEUTzaa5GwyUOxKXLlGElcpk3hiZy87WuDrDBg5wVU2w1f78yodIuuYpYSMLLcRqK1Vbr1wb8s67bCxazj1jswgrL49UWVCUD4GHg==
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=dPnnU/bovfMLMFk7Fz37gjc4NKrWmTRVmNUudIt/WoA=; b=iibPoN+F4tTtAOcBhIRxdARRmPrNvPDQY77U5HHFH5izY7mJcMuwCb30rteud6JZLIkq5nqMyN35e/xMDG3fS0VVE/JKybyWGtFq2QUWkBpO5CJSDs1x8XhV97ZEcwMACdH/9rcYV4LrC0ZOSnZxAxHV/dcvaylKQMTloXiO0EvQQvS5IMXdGqEC5685hBOgTo7A/36fUmNMhoM04Sh3ZL7KwGd47kuyqbHhfKjG8gQIFAFPgUwR4Aj5Ph4JoRs2YAUpeOBPKXwRsgVWHpPT37c2t8x4u/g7mFJS1YYd1t7hroxKADFjod+qWwgUXZgqXJpJELlEycc7Ryncu0pNlg==
  • Arc-seal: i=2; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=pass; b=kmFto8GYScB4qlt4+Nn5noaewkw9K5aJpLweigDZJlfOhT9sqtm6Zp2BReMoLsfcAWkVUfyHjpmf25CBlllCmA9fDv7tkBDIyEtGJuZPX3L35NZmuO+Wsa7Mo6YEU2YIE/zyedE6ZEHww4GRfQoQpyzcG9C2Q4wxMEDvwzkwB+R6Y60viNnsEmPcDHqflRxeTbwNdKWRdAk31aQhls4sC0Nb2t3/ixmC3h5iSx1acCXUqcEU7OF+SeDggIPtdmcWfOarlcPsciAmKJt4OeiX9U6Iz70I8vshaKeQZSYR3ltXgJXlRQgpECyDat0rVvjR5iaozvnr8e1F5PmZ24dMxQ==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=mmK6I3X/+Q4duqDQ/4XiE2rOkie0xM1AQWk+1ERk30/0cPMyiiR+ZG+0M7KoAJ3kRgF2t3pOIA+1MI3xvQUAcL1ZAk++DA1C5Sbvllzvp6Uz0guNrRLI2dUjv8QrkPDVzmxI3g5p9fVoZnAoPwcJdycvgwjtSpPiArCl/OCIZUT8uj9W0w4nc1/aoqHDxMeT2cSARj4GpT9ojr3ktaa8FmcpKSTORPbWLTQSZ2jGeDYrmX7e8PwoofgAEJVU8JRbx+owlcLQh4udjIZQEy7R6fKmGhQEaNNk8tQfXcnbRRsOLbbqDX9QLDjRv0wgchOpHY+24VxbPetUNO3hkbUWKg==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=arm.com header.i="@arm.com" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck"; dkim=pass header.s=selector1 header.d=arm.com header.i="@arm.com" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck"
  • Authentication-results-original: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=arm.com;
  • Cc: "xen-devel@xxxxxxxxxxxxxxxxxxxx" <xen-devel@xxxxxxxxxxxxxxxxxxxx>, Stefano Stabellini <sstabellini@xxxxxxxxxx>, Julien Grall <julien@xxxxxxx>, Michal Orzel <michal.orzel@xxxxxxx>, Volodymyr Babchuk <Volodymyr_Babchuk@xxxxxxxx>, Luca Fancellu <Luca.Fancellu@xxxxxxx>
  • Delivery-date: Wed, 23 Sep 2026 15:28:16 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>
  • Nodisclaimer: true
  • Thread-index: AQHdNjDkV92bkh6okkqfhA392pMLzbbcc6SA
  • Thread-topic: [PATCH v12 02/13] xen/arm: gic-v2: Implement GIC suspend/resume functions

Hi Mykola,

Sorry for the delay to review this serie.

> On 27 Aug 2026, at 16:31, Mykola Kvach <mykola_kvach@xxxxxxxx> wrote:
> 
> From: Mirela Simonovic <mirela.simonovic@xxxxxxxxxx>
> 
> 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@xxxxxxxxxx>
> Signed-off-by: Saeed Nowshadi <saeed.nowshadi@xxxxxxxxxx>
> Signed-off-by: Mykyta Poturai <mykyta_poturai@xxxxxxxx>
> Signed-off-by: Mykola Kvach <mykola_kvach@xxxxxxxx>
> Reviewed-by: Luca Fancellu <luca.fancellu@xxxxxxx>
> ---
> 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
> 




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.