Skip to content

Commit cabe1c8

Browse files
James Morsewildea01
authored andcommitted
arm64: Change cpu_resume() to enable mmu early then access sleep_sp by va
By enabling the MMU early in cpu_resume(), the sleep_save_sp and stack can be accessed by VA, which avoids the need to convert-addresses and clean to PoC on the suspend path. MMU setup is shared with the boot path, meaning the swapper_pg_dir is restored directly: ttbr1_el1 is no longer saved/restored. struct sleep_save_sp is removed, replacing it with a single array of pointers. cpu_do_{suspend,resume} could be further reduced to not restore: cpacr_el1, mdscr_el1, tcr_el1, vbar_el1 and sctlr_el1, all of which are set by __cpu_setup(). However these values all contain res0 bits that may be used to enable future features. Signed-off-by: James Morse <james.morse@arm.com> Reviewed-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
1 parent adc9b2d commit cabe1c8

File tree

7 files changed

+57
-115
lines changed

7 files changed

+57
-115
lines changed

arch/arm64/include/asm/suspend.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#ifndef __ASM_SUSPEND_H
22
#define __ASM_SUSPEND_H
33

4-
#define NR_CTX_REGS 11
4+
#define NR_CTX_REGS 10
55
#define NR_CALLEE_SAVED_REGS 12
66

77
/*
@@ -17,11 +17,6 @@ struct cpu_suspend_ctx {
1717
u64 sp;
1818
} __aligned(16);
1919

20-
struct sleep_save_sp {
21-
phys_addr_t *save_ptr_stash;
22-
phys_addr_t save_ptr_stash_phys;
23-
};
24-
2520
/*
2621
* Memory to save the cpu state is allocated on the stack by
2722
* __cpu_suspend_enter()'s caller, and populated by __cpu_suspend_enter().
@@ -39,6 +34,8 @@ struct sleep_stack_data {
3934
unsigned long callee_saved_regs[NR_CALLEE_SAVED_REGS];
4035
};
4136

37+
extern unsigned long *sleep_save_stash;
38+
4239
extern int cpu_suspend(unsigned long arg, int (*fn)(unsigned long));
4340
extern void cpu_resume(void);
4441
int __cpu_suspend_enter(struct sleep_stack_data *state);

arch/arm64/kernel/asm-offsets.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,6 @@ int main(void)
119119
DEFINE(CPU_CTX_SP, offsetof(struct cpu_suspend_ctx, sp));
120120
DEFINE(MPIDR_HASH_MASK, offsetof(struct mpidr_hash, mask));
121121
DEFINE(MPIDR_HASH_SHIFTS, offsetof(struct mpidr_hash, shift_aff));
122-
DEFINE(SLEEP_SAVE_SP_SZ, sizeof(struct sleep_save_sp));
123-
DEFINE(SLEEP_SAVE_SP_PHYS, offsetof(struct sleep_save_sp, save_ptr_stash_phys));
124-
DEFINE(SLEEP_SAVE_SP_VIRT, offsetof(struct sleep_save_sp, save_ptr_stash));
125122
DEFINE(SLEEP_STACK_DATA_SYSTEM_REGS, offsetof(struct sleep_stack_data, system_regs));
126123
DEFINE(SLEEP_STACK_DATA_CALLEE_REGS, offsetof(struct sleep_stack_data, callee_saved_regs));
127124
#endif

arch/arm64/kernel/head.S

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ ENTRY(__early_cpu_boot_status)
716716
* If it isn't, park the CPU
717717
*/
718718
.section ".idmap.text", "ax"
719-
__enable_mmu:
719+
ENTRY(__enable_mmu)
720720
mrs x22, sctlr_el1 // preserve old SCTLR_EL1 value
721721
mrs x1, ID_AA64MMFR0_EL1
722722
ubfx x2, x1, #ID_AA64MMFR0_TGRAN_SHIFT, 4

arch/arm64/kernel/setup.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ static void __init smp_build_mpidr_hash(void)
176176
*/
177177
if (mpidr_hash_size() > 4 * num_possible_cpus())
178178
pr_warn("Large number of MPIDR hash buckets detected\n");
179-
__flush_dcache_area(&mpidr_hash, sizeof(struct mpidr_hash));
180179
}
181180

182181
static void __init setup_machine_fdt(phys_addr_t dt_phys)

arch/arm64/kernel/sleep.S

Lines changed: 26 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ ENTRY(__cpu_suspend_enter)
7373
str x2, [x0, #SLEEP_STACK_DATA_SYSTEM_REGS + CPU_CTX_SP]
7474

7575
/* find the mpidr_hash */
76-
ldr x1, =sleep_save_sp
77-
ldr x1, [x1, #SLEEP_SAVE_SP_VIRT]
76+
ldr x1, =sleep_save_stash
77+
ldr x1, [x1]
7878
mrs x7, mpidr_el1
7979
ldr x9, =mpidr_hash
8080
ldr x10, [x9, #MPIDR_HASH_MASK]
@@ -87,44 +87,27 @@ ENTRY(__cpu_suspend_enter)
8787
compute_mpidr_hash x8, x3, x4, x5, x6, x7, x10
8888
add x1, x1, x8, lsl #3
8989

90+
str x0, [x1]
91+
add x0, x0, #SLEEP_STACK_DATA_SYSTEM_REGS
9092
stp x29, lr, [sp, #-16]!
91-
bl __cpu_suspend_save
93+
bl cpu_do_suspend
9294
ldp x29, lr, [sp], #16
9395
mov x0, #1
9496
ret
9597
ENDPROC(__cpu_suspend_enter)
9698
.ltorg
9799

98-
/*
99-
* x0 must contain the sctlr value retrieved from restored context
100-
*/
101-
.pushsection ".idmap.text", "ax"
102-
ENTRY(cpu_resume_mmu)
103-
ldr x3, =cpu_resume_after_mmu
104-
msr sctlr_el1, x0 // restore sctlr_el1
105-
isb
106-
/*
107-
* Invalidate the local I-cache so that any instructions fetched
108-
* speculatively from the PoC are discarded, since they may have
109-
* been dynamically patched at the PoU.
110-
*/
111-
ic iallu
112-
dsb nsh
113-
isb
114-
br x3 // global jump to virtual address
115-
ENDPROC(cpu_resume_mmu)
116-
.popsection
117-
cpu_resume_after_mmu:
118-
#ifdef CONFIG_KASAN
119-
mov x0, sp
120-
bl kasan_unpoison_remaining_stack
121-
#endif
122-
mov x0, #0 // return zero on success
123-
ret
124-
ENDPROC(cpu_resume_after_mmu)
125-
126100
ENTRY(cpu_resume)
127101
bl el2_setup // if in EL2 drop to EL1 cleanly
102+
/* enable the MMU early - so we can access sleep_save_stash by va */
103+
adr_l lr, __enable_mmu /* __cpu_setup will return here */
104+
ldr x27, =_cpu_resume /* __enable_mmu will branch here */
105+
adrp x25, idmap_pg_dir
106+
adrp x26, swapper_pg_dir
107+
b __cpu_setup
108+
ENDPROC(cpu_resume)
109+
110+
ENTRY(_cpu_resume)
128111
mrs x1, mpidr_el1
129112
adrp x8, mpidr_hash
130113
add x8, x8, #:lo12:mpidr_hash // x8 = struct mpidr_hash phys address
@@ -134,29 +117,32 @@ ENTRY(cpu_resume)
134117
ldp w5, w6, [x8, #(MPIDR_HASH_SHIFTS + 8)]
135118
compute_mpidr_hash x7, x3, x4, x5, x6, x1, x2
136119
/* x7 contains hash index, let's use it to grab context pointer */
137-
ldr_l x0, sleep_save_sp + SLEEP_SAVE_SP_PHYS
120+
ldr_l x0, sleep_save_stash
138121
ldr x0, [x0, x7, lsl #3]
139122
add x29, x0, #SLEEP_STACK_DATA_CALLEE_REGS
140123
add x0, x0, #SLEEP_STACK_DATA_SYSTEM_REGS
141124
/* load sp from context */
142125
ldr x2, [x0, #CPU_CTX_SP]
143-
/* load physical address of identity map page table in x1 */
144-
adrp x1, idmap_pg_dir
145126
mov sp, x2
146127
/* save thread_info */
147128
and x2, x2, #~(THREAD_SIZE - 1)
148129
msr sp_el0, x2
149130
/*
150-
* cpu_do_resume expects x0 to contain context physical address
151-
* pointer and x1 to contain physical address of 1:1 page tables
131+
* cpu_do_resume expects x0 to contain context address pointer
152132
*/
153-
bl cpu_do_resume // PC relative jump, MMU off
154-
/* Can't access these by physical address once the MMU is on */
133+
bl cpu_do_resume
134+
135+
#ifdef CONFIG_KASAN
136+
mov x0, sp
137+
bl kasan_unpoison_remaining_stack
138+
#endif
139+
155140
ldp x19, x20, [x29, #16]
156141
ldp x21, x22, [x29, #32]
157142
ldp x23, x24, [x29, #48]
158143
ldp x25, x26, [x29, #64]
159144
ldp x27, x28, [x29, #80]
160145
ldp x29, lr, [x29]
161-
b cpu_resume_mmu // Resume MMU, never returns
162-
ENDPROC(cpu_resume)
146+
mov x0, #0
147+
ret
148+
ENDPROC(_cpu_resume)

arch/arm64/kernel/suspend.c

Lines changed: 6 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,11 @@
1010
#include <asm/suspend.h>
1111
#include <asm/tlbflush.h>
1212

13-
1413
/*
15-
* This is called by __cpu_suspend_enter() to save the state, and do whatever
16-
* flushing is required to ensure that when the CPU goes to sleep we have
17-
* the necessary data available when the caches are not searched.
18-
*
19-
* ptr: sleep_stack_data containing cpu state virtual address.
20-
* save_ptr: address of the location where the context physical address
21-
* must be saved
14+
* This is allocated by cpu_suspend_init(), and used to store a pointer to
15+
* the 'struct sleep_stack_data' the contains a particular CPUs state.
2216
*/
23-
void notrace __cpu_suspend_save(struct sleep_stack_data *ptr,
24-
phys_addr_t *save_ptr)
25-
{
26-
*save_ptr = virt_to_phys(ptr);
27-
28-
cpu_do_suspend(&ptr->system_regs);
29-
/*
30-
* Only flush the context that must be retrieved with the MMU
31-
* off. VA primitives ensure the flush is applied to all
32-
* cache levels so context is pushed to DRAM.
33-
*/
34-
__flush_dcache_area(ptr, sizeof(*ptr));
35-
__flush_dcache_area(save_ptr, sizeof(*save_ptr));
36-
}
17+
unsigned long *sleep_save_stash;
3718

3819
/*
3920
* This hook is provided so that cpu_suspend code can restore HW
@@ -131,22 +112,15 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
131112
return ret;
132113
}
133114

134-
struct sleep_save_sp sleep_save_sp;
135-
136115
static int __init cpu_suspend_init(void)
137116
{
138-
void *ctx_ptr;
139-
140117
/* ctx_ptr is an array of physical addresses */
141-
ctx_ptr = kcalloc(mpidr_hash_size(), sizeof(phys_addr_t), GFP_KERNEL);
118+
sleep_save_stash = kcalloc(mpidr_hash_size(), sizeof(*sleep_save_stash),
119+
GFP_KERNEL);
142120

143-
if (WARN_ON(!ctx_ptr))
121+
if (WARN_ON(!sleep_save_stash))
144122
return -ENOMEM;
145123

146-
sleep_save_sp.save_ptr_stash = ctx_ptr;
147-
sleep_save_sp.save_ptr_stash_phys = virt_to_phys(ctx_ptr);
148-
__flush_dcache_area(&sleep_save_sp, sizeof(struct sleep_save_sp));
149-
150124
return 0;
151125
}
152126
early_initcall(cpu_suspend_init);

arch/arm64/mm/proc.S

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <asm/asm-offsets.h>
2525
#include <asm/hwcap.h>
2626
#include <asm/pgtable.h>
27+
#include <asm/pgtable-hwdef.h>
2728
#include <asm/cpufeature.h>
2829
#include <asm/alternative.h>
2930

@@ -63,62 +64,50 @@ ENTRY(cpu_do_suspend)
6364
mrs x2, tpidr_el0
6465
mrs x3, tpidrro_el0
6566
mrs x4, contextidr_el1
66-
mrs x5, mair_el1
67-
mrs x6, cpacr_el1
68-
mrs x7, ttbr1_el1
69-
mrs x8, tcr_el1
70-
mrs x9, vbar_el1
71-
mrs x10, mdscr_el1
72-
mrs x11, oslsr_el1
73-
mrs x12, sctlr_el1
67+
mrs x5, cpacr_el1
68+
mrs x6, tcr_el1
69+
mrs x7, vbar_el1
70+
mrs x8, mdscr_el1
71+
mrs x9, oslsr_el1
72+
mrs x10, sctlr_el1
7473
stp x2, x3, [x0]
75-
stp x4, x5, [x0, #16]
76-
stp x6, x7, [x0, #32]
77-
stp x8, x9, [x0, #48]
78-
stp x10, x11, [x0, #64]
79-
str x12, [x0, #80]
74+
stp x4, xzr, [x0, #16]
75+
stp x5, x6, [x0, #32]
76+
stp x7, x8, [x0, #48]
77+
stp x9, x10, [x0, #64]
8078
ret
8179
ENDPROC(cpu_do_suspend)
8280

8381
/**
8482
* cpu_do_resume - restore CPU register context
8583
*
86-
* x0: Physical address of context pointer
87-
* x1: ttbr0_el1 to be restored
88-
*
89-
* Returns:
90-
* sctlr_el1 value in x0
84+
* x0: Address of context pointer
9185
*/
9286
ENTRY(cpu_do_resume)
93-
/*
94-
* Invalidate local tlb entries before turning on MMU
95-
*/
96-
tlbi vmalle1
9787
ldp x2, x3, [x0]
9888
ldp x4, x5, [x0, #16]
99-
ldp x6, x7, [x0, #32]
100-
ldp x8, x9, [x0, #48]
101-
ldp x10, x11, [x0, #64]
102-
ldr x12, [x0, #80]
89+
ldp x6, x8, [x0, #32]
90+
ldp x9, x10, [x0, #48]
91+
ldp x11, x12, [x0, #64]
10392
msr tpidr_el0, x2
10493
msr tpidrro_el0, x3
10594
msr contextidr_el1, x4
106-
msr mair_el1, x5
10795
msr cpacr_el1, x6
108-
msr ttbr0_el1, x1
109-
msr ttbr1_el1, x7
110-
tcr_set_idmap_t0sz x8, x7
96+
97+
/* Don't change t0sz here, mask those bits when restoring */
98+
mrs x5, tcr_el1
99+
bfi x8, x5, TCR_T0SZ_OFFSET, TCR_TxSZ_WIDTH
100+
111101
msr tcr_el1, x8
112102
msr vbar_el1, x9
113103
msr mdscr_el1, x10
104+
msr sctlr_el1, x12
114105
/*
115106
* Restore oslsr_el1 by writing oslar_el1
116107
*/
117108
ubfx x11, x11, #1, #1
118109
msr oslar_el1, x11
119110
reset_pmuserenr_el0 x0 // Disable PMU access from EL0
120-
mov x0, x12
121-
dsb nsh // Make sure local tlb invalidation completed
122111
isb
123112
ret
124113
ENDPROC(cpu_do_resume)

0 commit comments

Comments
 (0)