[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 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? > + /* 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? > + if ( status ) > + dprintk(XENLOG_WARNING, "PSCI system suspend failed, err=%d\n", > status); > + } > > system_state = SYS_STATE_resume; > update_boot_mapping(false); -- WBR, Volodymyr
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |