Skip to content

Commit ba82c54

Browse files
Sia Jee Henghal-feng
authored andcommitted
RISC-V: Add arch functions to support hibernation/suspend-to-disk
Low level Arch functions were created to support hibernation. swsusp_arch_suspend() relies code from __cpu_suspend_enter() to write cpu state onto the stack, then calling swsusp_save() to save the memory image. Arch specific hibernation header is implemented and is utilized by the arch_hibernation_header_restore() and arch_hibernation_header_save() functions. The arch specific hibernation header consists of satp, hartid, and the cpu_resume address. The kernel built version is also need to be saved into the hibernation image header to making sure only the same kernel is restore when resume. swsusp_arch_resume() creates a temporary page table that covering only the linear map. It copies the restore code to a 'safe' page, then start to restore the memory image. Once completed, it restores the original kernel's page table. It then calls into __hibernate_cpu_resume() to restore the CPU context. Finally, it follows the normal hibernation path back to the hibernation core. To enable hibernation/suspend to disk into RISCV, the below config need to be enabled: - CONFIG_HIBERNATION - CONFIG_ARCH_HIBERNATION_HEADER - CONFIG_ARCH_HIBERNATION_POSSIBLE Signed-off-by: Sia Jee Heng <[email protected]> Reviewed-by: Ley Foon Tan <[email protected]> Reviewed-by: Mason Huo <[email protected]> Reviewed-by: Conor Dooley <[email protected]> Reviewed-by: Andrew Jones <[email protected]>
1 parent b57a005 commit ba82c54

File tree

7 files changed

+556
-1
lines changed

7 files changed

+556
-1
lines changed

arch/riscv/Kconfig

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ config RISCV
5454
select CLINT_TIMER if !MMU
5555
select CLONE_BACKWARDS
5656
select COMMON_CLK
57-
select CPU_PM if CPU_IDLE
57+
select CPU_PM if CPU_IDLE || HIBERNATION
5858
select EDAC_SUPPORT
5959
select GENERIC_ARCH_TOPOLOGY
6060
select GENERIC_ATOMIC64 if !64BIT
@@ -707,6 +707,12 @@ menu "Power management options"
707707

708708
source "kernel/power/Kconfig"
709709

710+
config ARCH_HIBERNATION_POSSIBLE
711+
def_bool y
712+
713+
config ARCH_HIBERNATION_HEADER
714+
def_bool HIBERNATION
715+
710716
endmenu # "Power management options"
711717

712718
menu "CPU Power Management"

arch/riscv/include/asm/assembler.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,24 @@
5959
REG_L s11, (SUSPEND_CONTEXT_REGS + PT_S11)(a0)
6060
.endm
6161

62+
/*
63+
* copy_page - copy 1 page (4KB) of data from source to destination
64+
* @a0 - destination
65+
* @a1 - source
66+
*/
67+
.macro copy_page a0, a1
68+
lui a2, 0x1
69+
add a2, a2, a0
70+
1 :
71+
REG_L t0, 0(a1)
72+
REG_L t1, SZREG(a1)
73+
74+
REG_S t0, 0(a0)
75+
REG_S t1, SZREG(a0)
76+
77+
addi a0, a0, 2 * SZREG
78+
addi a1, a1, 2 * SZREG
79+
bne a2, a0, 1b
80+
.endm
81+
6282
#endif /* __ASM_ASSEMBLER_H */

arch/riscv/include/asm/suspend.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ struct suspend_context {
2121
#endif
2222
};
2323

24+
/*
25+
* Used by hibernation core and cleared during resume sequence
26+
*/
27+
extern int in_suspend;
28+
2429
/* Low-level CPU suspend entry function */
2530
int __cpu_suspend_enter(struct suspend_context *context);
2631

@@ -36,4 +41,18 @@ int __cpu_resume_enter(unsigned long hartid, unsigned long context);
3641
/* Used to save and restore the CSRs */
3742
void suspend_save_csrs(struct suspend_context *context);
3843
void suspend_restore_csrs(struct suspend_context *context);
44+
45+
/* Low-level API to support hibernation */
46+
int swsusp_arch_suspend(void);
47+
int swsusp_arch_resume(void);
48+
int arch_hibernation_header_save(void *addr, unsigned int max_size);
49+
int arch_hibernation_header_restore(void *addr);
50+
int __hibernate_cpu_resume(void);
51+
52+
/* Used to resume on the CPU we hibernated on */
53+
int hibernate_resume_nonboot_cpu_disable(void);
54+
55+
asmlinkage void hibernate_restore_image(unsigned long resume_satp, unsigned long satp_temp,
56+
unsigned long cpu_resume);
57+
asmlinkage int hibernate_core_restore_code(void);
3958
#endif

arch/riscv/kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ obj-$(CONFIG_MODULES) += module.o
6464
obj-$(CONFIG_MODULE_SECTIONS) += module-sections.o
6565

6666
obj-$(CONFIG_CPU_PM) += suspend_entry.o suspend.o
67+
obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate-asm.o
6768

6869
obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o
6970
obj-$(CONFIG_DYNAMIC_FTRACE) += mcount-dyn.o

arch/riscv/kernel/asm-offsets.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/kbuild.h>
1010
#include <linux/mm.h>
1111
#include <linux/sched.h>
12+
#include <linux/suspend.h>
1213
#include <asm/kvm_host.h>
1314
#include <asm/thread_info.h>
1415
#include <asm/ptrace.h>
@@ -116,6 +117,10 @@ void asm_offsets(void)
116117

117118
OFFSET(SUSPEND_CONTEXT_REGS, suspend_context, regs);
118119

120+
OFFSET(HIBERN_PBE_ADDR, pbe, address);
121+
OFFSET(HIBERN_PBE_ORIG, pbe, orig_address);
122+
OFFSET(HIBERN_PBE_NEXT, pbe, next);
123+
119124
OFFSET(KVM_ARCH_GUEST_ZERO, kvm_vcpu_arch, guest_context.zero);
120125
OFFSET(KVM_ARCH_GUEST_RA, kvm_vcpu_arch, guest_context.ra);
121126
OFFSET(KVM_ARCH_GUEST_SP, kvm_vcpu_arch, guest_context.sp);

arch/riscv/kernel/hibernate-asm.S

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Hibernation low level support for RISCV.
4+
*
5+
* Copyright (C) 2023 StarFive Technology Co., Ltd.
6+
*
7+
* Author: Jee Heng Sia <[email protected]>
8+
*/
9+
10+
#include <asm/asm.h>
11+
#include <asm/asm-offsets.h>
12+
#include <asm/assembler.h>
13+
#include <asm/csr.h>
14+
15+
#include <linux/linkage.h>
16+
17+
/*
18+
* int __hibernate_cpu_resume(void)
19+
* Switch back to the hibernated image's page table prior to restoring the CPU
20+
* context.
21+
*
22+
* Always returns 0
23+
*/
24+
ENTRY(__hibernate_cpu_resume)
25+
/* switch to hibernated image's page table. */
26+
csrw CSR_SATP, s0
27+
sfence.vma
28+
29+
REG_L a0, hibernate_cpu_context
30+
31+
suspend_restore_csrs
32+
suspend_restore_regs
33+
34+
/* Return zero value. */
35+
mv a0, zero
36+
37+
ret
38+
END(__hibernate_cpu_resume)
39+
40+
/*
41+
* Prepare to restore the image.
42+
* a0: satp of saved page tables.
43+
* a1: satp of temporary page tables.
44+
* a2: cpu_resume.
45+
*/
46+
ENTRY(hibernate_restore_image)
47+
mv s0, a0
48+
mv s1, a1
49+
mv s2, a2
50+
REG_L s4, restore_pblist
51+
REG_L a1, relocated_restore_code
52+
53+
jalr a1
54+
END(hibernate_restore_image)
55+
56+
/*
57+
* The below code will be executed from a 'safe' page.
58+
* It first switches to the temporary page table, then starts to copy the pages
59+
* back to the original memory location. Finally, it jumps to __hibernate_cpu_resume()
60+
* to restore the CPU context.
61+
*/
62+
ENTRY(hibernate_core_restore_code)
63+
/* switch to temp page table. */
64+
csrw satp, s1
65+
sfence.vma
66+
.Lcopy:
67+
/* The below code will restore the hibernated image. */
68+
REG_L a1, HIBERN_PBE_ADDR(s4)
69+
REG_L a0, HIBERN_PBE_ORIG(s4)
70+
71+
copy_page a0, a1
72+
73+
REG_L s4, HIBERN_PBE_NEXT(s4)
74+
bnez s4, .Lcopy
75+
76+
jalr s2
77+
END(hibernate_core_restore_code)

0 commit comments

Comments
 (0)