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

Re: [PATCH v5 10/12] xen/arm: Save/restore context on suspend/resume



Hi Volodymyr,

On Sat, Aug 23, 2025 at 8:34 PM Volodymyr Babchuk
<Volodymyr_Babchuk@xxxxxxxx> wrote:
>
>
> Hi Mykola,
>
> Mykola Kvach <xakep.amatop@xxxxxxxxx> writes:
>
> > From: Mirela Simonovic <mirela.simonovic@xxxxxxxxxx>
> >
> > The context of CPU general purpose and system control registers
> > has to be saved on suspend and restored on resume. This is
> > implemented in hyp_suspend and before the return from hyp_resume
> > function. The hyp_suspend is invoked just before the PSCI system
> > suspend call is issued to the ATF. The hyp_suspend has to return a
> > non-zero value so that the calling 'if' statement evaluates to true,
> > causing the system suspend to be invoked. Upon the resume, context
> > saved on suspend will be restored, including the link register.
> > Therefore, after restoring the context the control flow will
> > return to the address pointed by the saved link register, which
> > is the place from which the hyp_suspend was called. To ensure
> > that the calling 'if' statement doesn't again evaluate to true
> > and initiate system suspend, hyp_resume has to return a zero value
> > after restoring the context.
> >
> > Note that the order of saving register context into cpu_context
> > structure has to match the order of restoring.
> >
> > Support for ARM32 is not implemented. Instead, compilation fails with a
> > build-time error if suspend is enabled for ARM32.
> >
> > 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>
> > ---
> > Changes in v4:
> > - produce build-time error for ARM32 when CONFIG_SYSTEM_SUSPEND is enabled
> > - use register_t instead of uint64_t in cpu_context structure
> > ---
> >  xen/arch/arm/arm64/head.S          | 91 +++++++++++++++++++++++++++++-
> >  xen/arch/arm/include/asm/suspend.h | 20 +++++++
> >  xen/arch/arm/suspend.c             | 23 +++++++-
> >  3 files changed, 130 insertions(+), 4 deletions(-)
> >
> > diff --git a/xen/arch/arm/arm64/head.S b/xen/arch/arm/arm64/head.S
> > index 596e960152..ad8b48de3a 100644
> > --- a/xen/arch/arm/arm64/head.S
> > +++ b/xen/arch/arm/arm64/head.S
> > @@ -562,6 +562,52 @@ END(efi_xen_start)
> >  #endif /* CONFIG_ARM_EFI */
> >
> >  #ifdef CONFIG_SYSTEM_SUSPEND
> > +/*
> > + * int hyp_suspend(struct cpu_context *ptr)
> > + *
> > + * x0 - pointer to the storage where callee's context will be saved
> > + *
> > + * CPU context saved here will be restored on resume in hyp_resume 
> > function.
> > + * hyp_suspend shall return a non-zero value. Upon restoring context
> > + * hyp_resume shall return value zero instead. From C code that invokes
> > + * hyp_suspend, the return value is interpreted to determine whether the 
> > context
> > + * is saved (hyp_suspend) or restored (hyp_resume).
> > + */
> > +FUNC(hyp_suspend)
>
> I don't think that hyp_suspend is the correct name, as this function in
> fact suspend_nothing. Maybe "prepare_resume_ctx" will be better?

Not a problem to rename it.

>
> > +        /* Store callee-saved registers */
> > +        stp     x19, x20, [x0], #16
>
> If you have struct cpu_context defined, then you probably should use
> define provided by <asm-offsets.h> to access struct fields. Otherwise,
> it will be really easy to get desync between struct definition and this
> asm code.
>
> > +        stp     x21, x22, [x0], #16
> > +        stp     x23, x24, [x0], #16
> > +        stp     x25, x26, [x0], #16
> > +        stp     x27, x28, [x0], #16
> > +        stp     x29, lr, [x0], #16
> > +
> > +        /* Store stack-pointer */
> > +        mov     x2, sp
> > +        str     x2, [x0], #8
> > +
> > +        /* Store system control registers */
> > +        mrs     x2, VBAR_EL2
> > +        str     x2, [x0], #8
> > +        mrs     x2, VTCR_EL2
> > +        str     x2, [x0], #8
> > +        mrs     x2, VTTBR_EL2
> > +        str     x2, [x0], #8
> > +        mrs     x2, TPIDR_EL2
> > +        str     x2, [x0], #8
> > +        mrs     x2, MDCR_EL2
> > +        str     x2, [x0], #8
> > +        mrs     x2, HSTR_EL2
> > +        str     x2, [x0], #8
> > +        mrs     x2, CPTR_EL2
> > +        str     x2, [x0], #8
> > +        mrs     x2, HCR_EL2
> > +        str     x2, [x0], #8
> > +
> > +        /* hyp_suspend must return a non-zero value */
> > +        mov     x0, #1
> > +        ret
> > +END(hyp_suspend)
> >
> >  FUNC(hyp_resume)
> >          /* Initialize the UART if earlyprintk has been enabled. */
> > @@ -580,7 +626,50 @@ FUNC(hyp_resume)
> >          b     enable_secondary_cpu_mm
> >
> >  mmu_resumed:
> > -        b .
> > +        /*
> > +         * Now we can access the cpu_context, so restore the context here
> > +         * TODO: can we reuse __context_switch and saved_context struct 
> > here ?
> > +         */
>
> This is a great idea and I like it very much, but sadly saved_context
> struct has no fields for system _EL2 registers.
>
> > +        ldr     x0, =cpu_context
> > +
> > +        /* Restore callee-saved registers */
> > +        ldp     x19, x20, [x0], #16
> > +        ldp     x21, x22, [x0], #16
> > +        ldp     x23, x24, [x0], #16
> > +        ldp     x25, x26, [x0], #16
> > +        ldp     x27, x28, [x0], #16
> > +        ldp     x29, lr, [x0], #16
> > +
> > +        /* Restore stack pointer */
> > +        ldr     x2, [x0], #8
> > +        mov     sp, x2
> > +
> > +        /* Restore system control registers */
> > +        ldr     x2, [x0], #8
> > +        msr     VBAR_EL2, x2
> > +        ldr     x2, [x0], #8
> > +        msr     VTCR_EL2, x2
> > +        ldr     x2, [x0], #8
> > +        msr     VTTBR_EL2, x2
> > +        ldr     x2, [x0], #8
> > +        msr     TPIDR_EL2, x2
> > +        ldr     x2, [x0], #8
> > +        msr     MDCR_EL2, x2
> > +        ldr     x2, [x0], #8
> > +        msr     HSTR_EL2, x2
> > +        ldr     x2, [x0], #8
> > +        msr     CPTR_EL2, x2
> > +        ldr     x2, [x0], #8
> > +        msr     HCR_EL2, x2
> > +        isb
> > +
> > +        /* Since context is restored return from this function will appear 
> > as
> > +         * return from hyp_suspend. To distinguish a return from 
> > hyp_suspend
> > +         * which is called upon finalizing the suspend, as opposed to 
> > return
> > +         * from this function which executes on resume, we need to return 
> > zero
> > +         * value here. */
> > +        mov x0, #0
> > +        ret
> >  END(hyp_resume)
> >
> >  #endif /* CONFIG_SYSTEM_SUSPEND */
> > diff --git a/xen/arch/arm/include/asm/suspend.h 
> > b/xen/arch/arm/include/asm/suspend.h
> > index 55041a5d06..ae71ccb87b 100644
> > --- a/xen/arch/arm/include/asm/suspend.h
> > +++ b/xen/arch/arm/include/asm/suspend.h
> > @@ -5,9 +5,29 @@
> >
> >  #ifdef CONFIG_SYSTEM_SUSPEND
> >
> > +#ifdef CONFIG_ARM_64
> > +struct cpu_context {
> > +    register_t callee_regs[12];
> > +    register_t sp;
> > +    register_t vbar_el2;
> > +    register_t vtcr_el2;
> > +    register_t vttbr_el2;
> > +    register_t tpidr_el2;
> > +    register_t mdcr_el2;
> > +    register_t hstr_el2;
> > +    register_t cptr_el2;
> > +    register_t hcr_el2;
> > +} __aligned(16);
> > +#else
> > +#error "Define cpu_context structure for arm32"
> > +#endif
> > +
> > +extern struct cpu_context cpu_context;
> > +
> >  int host_system_suspend(void);
> >
> >  void hyp_resume(void);
> > +int hyp_suspend(struct cpu_context *ptr);
> >
> >  #endif /* CONFIG_SYSTEM_SUSPEND */
> >
> > diff --git a/xen/arch/arm/suspend.c b/xen/arch/arm/suspend.c
> > index 08b6acaede..b5398e5ca6 100644
> > --- a/xen/arch/arm/suspend.c
> > +++ b/xen/arch/arm/suspend.c
> > @@ -1,6 +1,7 @@
> >  /* SPDX-License-Identifier: GPL-2.0-only */
> >
> >  #include <asm/psci.h>
> > +#include <asm/suspend.h>
> >  #include <xen/console.h>
> >  #include <xen/cpu.h>
> >  #include <xen/llc-coloring.h>
> > @@ -17,6 +18,8 @@
> >   *  - Investigate feasibility and need for implementing system suspend on 
> > ARM32
> >   */
> >
> > +struct cpu_context cpu_context;
> > +
> >  /* Xen suspend. Note: data is not used (suspend is the suspend to RAM) */
> >  static long system_suspend(void *data)
> >  {
> > @@ -73,9 +76,23 @@ static long system_suspend(void *data)
> >       */
> >      update_boot_mapping(true);
> >
> > -    status = call_psci_system_suspend();
> > -    if ( status )
> > -        dprintk(XENLOG_WARNING, "PSCI system suspend failed, err=%d\n", 
> > status);
> > +    if ( hyp_suspend(&cpu_context) )
> > +    {
> > +        status = call_psci_system_suspend();
> > +        /*
> > +         * If suspend is finalized properly by above system suspend PSCI 
> > call,
> > +         * the code below in this 'if' branch will never execute. Execution
> > +         * will continue from hyp_resume which is the hypervisor's resume 
> > point.
> > +         * In hyp_resume CPU context will be restored and since 
> > link-register is
> > +         * restored as well, it will appear to return from hyp_suspend. The
> > +         * difference in returning from hyp_suspend on system suspend 
> > versus
> > +         * resume is in function's return value: on suspend, the return 
> > value is
> > +         * a non-zero value, on resume it is zero. That is why the control 
> > flow
> > +         * will not re-enter this 'if' branch on resume.
> > +         */
>
> Looks like this comment is misplaced. It should be before "if (
> hyp_suspend() )", right?

Actually, the comment is correctly placed here and does not need to be moved.

>
> > +        if ( status )
> > +            dprintk(XENLOG_WARNING, "PSCI system suspend failed, 
> > err=%d\n", status);
> > +    }
> >
> >      system_state = SYS_STATE_resume;
> >      update_boot_mapping(false);
>
> --
> WBR, Volodymyr

Best regards,
Mykola



 


Rackspace

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