arm64: sdei: Add trampoline code for remapping the kernel
When CONFIG_UNMAP_KERNEL_AT_EL0 is set the SDEI entry point and the rest of the kernel may be unmapped when we take an event. If this may be the case, use an entry trampoline that can switch to the kernel page tables. We can't use the provided PSTATE to determine whether to switch page tables as we may have interrupted the kernel's entry trampoline, (or a normal-priority event that interrupted the kernel's entry trampoline). Instead test for a user ASID in ttbr1_el1. Save a value in regs->addr_limit to indicate whether we need to restore the original ASID when returning from this event. This value is only used by do_page_fault(), which we don't call with the SDEI regs. Signed-off-by: James Morse <james.morse@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
This commit is contained in:

committed by
Catalin Marinas

parent
83f8ee3a73
commit
79e9aa59dc
@@ -1159,6 +1159,78 @@ NOKPROBE(ret_from_fork)
|
||||
#include <asm/sdei.h>
|
||||
#include <uapi/linux/arm_sdei.h>
|
||||
|
||||
.macro sdei_handler_exit exit_mode
|
||||
/* On success, this call never returns... */
|
||||
cmp \exit_mode, #SDEI_EXIT_SMC
|
||||
b.ne 99f
|
||||
smc #0
|
||||
b .
|
||||
99: hvc #0
|
||||
b .
|
||||
.endm
|
||||
|
||||
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
|
||||
/*
|
||||
* The regular SDEI entry point may have been unmapped along with the rest of
|
||||
* the kernel. This trampoline restores the kernel mapping to make the x1 memory
|
||||
* argument accessible.
|
||||
*
|
||||
* This clobbers x4, __sdei_handler() will restore this from firmware's
|
||||
* copy.
|
||||
*/
|
||||
.ltorg
|
||||
.pushsection ".entry.tramp.text", "ax"
|
||||
ENTRY(__sdei_asm_entry_trampoline)
|
||||
mrs x4, ttbr1_el1
|
||||
tbz x4, #USER_ASID_BIT, 1f
|
||||
|
||||
tramp_map_kernel tmp=x4
|
||||
isb
|
||||
mov x4, xzr
|
||||
|
||||
/*
|
||||
* Use reg->interrupted_regs.addr_limit to remember whether to unmap
|
||||
* the kernel on exit.
|
||||
*/
|
||||
1: str x4, [x1, #(SDEI_EVENT_INTREGS + S_ORIG_ADDR_LIMIT)]
|
||||
|
||||
#ifdef CONFIG_RANDOMIZE_BASE
|
||||
adr x4, tramp_vectors + PAGE_SIZE
|
||||
add x4, x4, #:lo12:__sdei_asm_trampoline_next_handler
|
||||
ldr x4, [x4]
|
||||
#else
|
||||
ldr x4, =__sdei_asm_handler
|
||||
#endif
|
||||
br x4
|
||||
ENDPROC(__sdei_asm_entry_trampoline)
|
||||
NOKPROBE(__sdei_asm_entry_trampoline)
|
||||
|
||||
/*
|
||||
* Make the exit call and restore the original ttbr1_el1
|
||||
*
|
||||
* x0 & x1: setup for the exit API call
|
||||
* x2: exit_mode
|
||||
* x4: struct sdei_registered_event argument from registration time.
|
||||
*/
|
||||
ENTRY(__sdei_asm_exit_trampoline)
|
||||
ldr x4, [x4, #(SDEI_EVENT_INTREGS + S_ORIG_ADDR_LIMIT)]
|
||||
cbnz x4, 1f
|
||||
|
||||
tramp_unmap_kernel tmp=x4
|
||||
|
||||
1: sdei_handler_exit exit_mode=x2
|
||||
ENDPROC(__sdei_asm_exit_trampoline)
|
||||
NOKPROBE(__sdei_asm_exit_trampoline)
|
||||
.ltorg
|
||||
.popsection // .entry.tramp.text
|
||||
#ifdef CONFIG_RANDOMIZE_BASE
|
||||
.pushsection ".rodata", "a"
|
||||
__sdei_asm_trampoline_next_handler:
|
||||
.quad __sdei_asm_handler
|
||||
.popsection // .rodata
|
||||
#endif /* CONFIG_RANDOMIZE_BASE */
|
||||
#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */
|
||||
|
||||
/*
|
||||
* Software Delegated Exception entry point.
|
||||
*
|
||||
@@ -1166,6 +1238,7 @@ NOKPROBE(ret_from_fork)
|
||||
* x1: struct sdei_registered_event argument from registration time.
|
||||
* x2: interrupted PC
|
||||
* x3: interrupted PSTATE
|
||||
* x4: maybe clobbered by the trampoline
|
||||
*
|
||||
* Firmware has preserved x0->x17 for us, we must save/restore the rest to
|
||||
* follow SMC-CC. We save (or retrieve) all the registers as the handler may
|
||||
@@ -1231,10 +1304,11 @@ ENTRY(__sdei_asm_handler)
|
||||
|
||||
msr sp_el0, x28
|
||||
/* restore regs >x17 that we clobbered */
|
||||
ldp x28, x29, [x19, #SDEI_EVENT_INTREGS + 16 * 14]
|
||||
ldp lr, x4, [x19, #SDEI_EVENT_INTREGS + S_LR]
|
||||
mov sp, x4
|
||||
ldp x18, x19, [x19, #SDEI_EVENT_INTREGS + 16 * 9]
|
||||
mov x4, x19 // keep x4 for __sdei_asm_exit_trampoline
|
||||
ldp x28, x29, [x4, #SDEI_EVENT_INTREGS + 16 * 14]
|
||||
ldp x18, x19, [x4, #SDEI_EVENT_INTREGS + 16 * 9]
|
||||
ldp lr, x1, [x4, #SDEI_EVENT_INTREGS + S_LR]
|
||||
mov sp, x1
|
||||
|
||||
mov x1, x0 // address to complete_and_resume
|
||||
/* x0 = (x0 <= 1) ? EVENT_COMPLETE:EVENT_COMPLETE_AND_RESUME */
|
||||
@@ -1243,14 +1317,16 @@ ENTRY(__sdei_asm_handler)
|
||||
mov_q x3, SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME
|
||||
csel x0, x2, x3, ls
|
||||
|
||||
/* On success, this call never returns... */
|
||||
ldr_l x2, sdei_exit_mode
|
||||
cmp x2, #SDEI_EXIT_SMC
|
||||
b.ne 1f
|
||||
smc #0
|
||||
b .
|
||||
1: hvc #0
|
||||
b .
|
||||
|
||||
alternative_if_not ARM64_UNMAP_KERNEL_AT_EL0
|
||||
sdei_handler_exit exit_mode=x2
|
||||
alternative_else_nop_endif
|
||||
|
||||
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
|
||||
tramp_alias dst=x5, sym=__sdei_asm_exit_trampoline
|
||||
br x5
|
||||
#endif
|
||||
ENDPROC(__sdei_asm_handler)
|
||||
NOKPROBE(__sdei_asm_handler)
|
||||
#endif /* CONFIG_ARM_SDE_INTERFACE */
|
||||
|
Reference in New Issue
Block a user