x86/asm/entry/64: Disentangle error_entry/exit gsbase/ebx/usermode code
The error_entry/error_exit code to handle gsbase and whether we return to user mdoe was a mess: - error_sti was misnamed. In particular, it did not enable interrupts. - Error handling for gs_change was hopelessly tangled the normal usermode path. Separate it out. This saves a branch in normal entries from kernel mode. - The comments were bad. Fix it up. As a nice side effect, there's now a code path that happens on error entries from user mode. We'll use it soon. Signed-off-by: Andy Lutomirski <luto@kernel.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/f1be898ab93360169fb845ab85185948832209ee.1433878454.git.luto@kernel.org [ Prettified it, clarified comments some more. ] Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
a92fde2523
commit
539f511365
|
@ -1135,7 +1135,7 @@ END(paranoid_exit)
|
|||
|
||||
/*
|
||||
* Save all registers in pt_regs, and switch gs if needed.
|
||||
* Return: ebx=0: need swapgs on exit, ebx=1: otherwise
|
||||
* Return: EBX=0: came from user mode; EBX=1: otherwise
|
||||
*/
|
||||
ENTRY(error_entry)
|
||||
cld
|
||||
|
@ -1144,9 +1144,11 @@ ENTRY(error_entry)
|
|||
xorl %ebx, %ebx
|
||||
testb $3, CS+8(%rsp)
|
||||
jz error_kernelspace
|
||||
error_swapgs:
|
||||
|
||||
/* We entered from user mode */
|
||||
SWAPGS
|
||||
error_sti:
|
||||
|
||||
error_entry_done:
|
||||
TRACE_IRQS_OFF
|
||||
ret
|
||||
|
||||
|
@ -1165,8 +1167,15 @@ error_kernelspace:
|
|||
cmpq %rax, RIP+8(%rsp)
|
||||
je bstep_iret
|
||||
cmpq $gs_change, RIP+8(%rsp)
|
||||
je error_swapgs
|
||||
jmp error_sti
|
||||
jne error_entry_done
|
||||
|
||||
/*
|
||||
* hack: gs_change can fail with user gsbase. If this happens, fix up
|
||||
* gsbase and proceed. We'll fix up the exception and land in
|
||||
* gs_change's error handler with kernel gsbase.
|
||||
*/
|
||||
SWAPGS
|
||||
jmp error_entry_done
|
||||
|
||||
bstep_iret:
|
||||
/* Fix truncated RIP */
|
||||
|
@ -1174,16 +1183,30 @@ bstep_iret:
|
|||
/* fall through */
|
||||
|
||||
error_bad_iret:
|
||||
/*
|
||||
* We came from an IRET to user mode, so we have user gsbase.
|
||||
* Switch to kernel gsbase:
|
||||
*/
|
||||
SWAPGS
|
||||
|
||||
/*
|
||||
* Pretend that the exception came from user mode: set up pt_regs
|
||||
* as if we faulted immediately after IRET and clear EBX so that
|
||||
* error_exit knows that we will be returning to user mode.
|
||||
*/
|
||||
mov %rsp, %rdi
|
||||
call fixup_bad_iret
|
||||
mov %rax, %rsp
|
||||
decl %ebx /* Return to usergs */
|
||||
jmp error_sti
|
||||
decl %ebx
|
||||
jmp error_entry_done
|
||||
END(error_entry)
|
||||
|
||||
|
||||
/* On entry, ebx is "no swapgs" flag (1: don't need swapgs, 0: need it) */
|
||||
/*
|
||||
* On entry, EBS is a "return to kernel mode" flag:
|
||||
* 1: already in kernel mode, don't need SWAPGS
|
||||
* 0: user gsbase is loaded, we need SWAPGS and standard preparation for return to usermode
|
||||
*/
|
||||
ENTRY(error_exit)
|
||||
movl %ebx, %eax
|
||||
RESTORE_EXTRA_REGS
|
||||
|
|
Loading…
Reference in a new issue