|
@@ -364,8 +364,7 @@ ENTRY(__switch_to_asm)
|
|
|
* exist, overwrite the RSB with entries which capture
|
|
|
* speculative execution to prevent attack.
|
|
|
*/
|
|
|
- /* Clobbers %rbx */
|
|
|
- FILL_RETURN_BUFFER RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
|
|
|
+ FILL_RETURN_BUFFER %r12, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
|
|
|
#endif
|
|
|
|
|
|
/* restore callee-saved registers */
|
|
@@ -449,9 +448,19 @@ END(irq_entries_start)
|
|
|
*
|
|
|
* The invariant is that, if irq_count != -1, then the IRQ stack is in use.
|
|
|
*/
|
|
|
-.macro ENTER_IRQ_STACK regs=1 old_rsp
|
|
|
+.macro ENTER_IRQ_STACK regs=1 old_rsp save_ret=0
|
|
|
DEBUG_ENTRY_ASSERT_IRQS_OFF
|
|
|
+
|
|
|
+ .if \save_ret
|
|
|
+ /*
|
|
|
+ * If save_ret is set, the original stack contains one additional
|
|
|
+ * entry -- the return address. Therefore, move the address one
|
|
|
+ * entry below %rsp to \old_rsp.
|
|
|
+ */
|
|
|
+ leaq 8(%rsp), \old_rsp
|
|
|
+ .else
|
|
|
movq %rsp, \old_rsp
|
|
|
+ .endif
|
|
|
|
|
|
.if \regs
|
|
|
UNWIND_HINT_REGS base=\old_rsp
|
|
@@ -497,6 +506,15 @@ END(irq_entries_start)
|
|
|
.if \regs
|
|
|
UNWIND_HINT_REGS indirect=1
|
|
|
.endif
|
|
|
+
|
|
|
+ .if \save_ret
|
|
|
+ /*
|
|
|
+ * Push the return address to the stack. This return address can
|
|
|
+ * be found at the "real" original RSP, which was offset by 8 at
|
|
|
+ * the beginning of this macro.
|
|
|
+ */
|
|
|
+ pushq -8(\old_rsp)
|
|
|
+ .endif
|
|
|
.endm
|
|
|
|
|
|
/*
|
|
@@ -520,27 +538,65 @@ END(irq_entries_start)
|
|
|
.endm
|
|
|
|
|
|
/*
|
|
|
- * Interrupt entry/exit.
|
|
|
- *
|
|
|
- * Interrupt entry points save only callee clobbered registers in fast path.
|
|
|
+ * Interrupt entry helper function.
|
|
|
*
|
|
|
- * Entry runs with interrupts off.
|
|
|
+ * Entry runs with interrupts off. Stack layout at entry:
|
|
|
+ * +----------------------------------------------------+
|
|
|
+ * | regs->ss |
|
|
|
+ * | regs->rsp |
|
|
|
+ * | regs->eflags |
|
|
|
+ * | regs->cs |
|
|
|
+ * | regs->ip |
|
|
|
+ * +----------------------------------------------------+
|
|
|
+ * | regs->orig_ax = ~(interrupt number) |
|
|
|
+ * +----------------------------------------------------+
|
|
|
+ * | return address |
|
|
|
+ * +----------------------------------------------------+
|
|
|
*/
|
|
|
-
|
|
|
-/* 0(%rsp): ~(interrupt number) */
|
|
|
- .macro interrupt func
|
|
|
+ENTRY(interrupt_entry)
|
|
|
+ UNWIND_HINT_FUNC
|
|
|
+ ASM_CLAC
|
|
|
cld
|
|
|
|
|
|
- testb $3, CS-ORIG_RAX(%rsp)
|
|
|
+ testb $3, CS-ORIG_RAX+8(%rsp)
|
|
|
jz 1f
|
|
|
SWAPGS
|
|
|
- call switch_to_thread_stack
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Switch to the thread stack. The IRET frame and orig_ax are
|
|
|
+ * on the stack, as well as the return address. RDI..R12 are
|
|
|
+ * not (yet) on the stack and space has not (yet) been
|
|
|
+ * allocated for them.
|
|
|
+ */
|
|
|
+ pushq %rdi
|
|
|
+
|
|
|
+ /* Need to switch before accessing the thread stack. */
|
|
|
+ SWITCH_TO_KERNEL_CR3 scratch_reg=%rdi
|
|
|
+ movq %rsp, %rdi
|
|
|
+ movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We have RDI, return address, and orig_ax on the stack on
|
|
|
+ * top of the IRET frame. That means offset=24
|
|
|
+ */
|
|
|
+ UNWIND_HINT_IRET_REGS base=%rdi offset=24
|
|
|
+
|
|
|
+ pushq 7*8(%rdi) /* regs->ss */
|
|
|
+ pushq 6*8(%rdi) /* regs->rsp */
|
|
|
+ pushq 5*8(%rdi) /* regs->eflags */
|
|
|
+ pushq 4*8(%rdi) /* regs->cs */
|
|
|
+ pushq 3*8(%rdi) /* regs->ip */
|
|
|
+ pushq 2*8(%rdi) /* regs->orig_ax */
|
|
|
+ pushq 8(%rdi) /* return address */
|
|
|
+ UNWIND_HINT_FUNC
|
|
|
+
|
|
|
+ movq (%rdi), %rdi
|
|
|
1:
|
|
|
|
|
|
- PUSH_AND_CLEAR_REGS
|
|
|
- ENCODE_FRAME_POINTER
|
|
|
+ PUSH_AND_CLEAR_REGS save_ret=1
|
|
|
+ ENCODE_FRAME_POINTER 8
|
|
|
|
|
|
- testb $3, CS(%rsp)
|
|
|
+ testb $3, CS+8(%rsp)
|
|
|
jz 1f
|
|
|
|
|
|
/*
|
|
@@ -548,7 +604,7 @@ END(irq_entries_start)
|
|
|
*
|
|
|
* We need to tell lockdep that IRQs are off. We can't do this until
|
|
|
* we fix gsbase, and we should do it before enter_from_user_mode
|
|
|
- * (which can take locks). Since TRACE_IRQS_OFF idempotent,
|
|
|
+ * (which can take locks). Since TRACE_IRQS_OFF is idempotent,
|
|
|
* the simplest way to handle it is to just call it twice if
|
|
|
* we enter from user mode. There's no reason to optimize this since
|
|
|
* TRACE_IRQS_OFF is a no-op if lockdep is off.
|
|
@@ -558,12 +614,15 @@ END(irq_entries_start)
|
|
|
CALL_enter_from_user_mode
|
|
|
|
|
|
1:
|
|
|
- ENTER_IRQ_STACK old_rsp=%rdi
|
|
|
+ ENTER_IRQ_STACK old_rsp=%rdi save_ret=1
|
|
|
/* We entered an interrupt context - irqs are off: */
|
|
|
TRACE_IRQS_OFF
|
|
|
|
|
|
- call \func /* rdi points to pt_regs */
|
|
|
- .endm
|
|
|
+ ret
|
|
|
+END(interrupt_entry)
|
|
|
+
|
|
|
+
|
|
|
+/* Interrupt entry/exit. */
|
|
|
|
|
|
/*
|
|
|
* The interrupt stubs push (~vector+0x80) onto the stack and
|
|
@@ -571,9 +630,10 @@ END(irq_entries_start)
|
|
|
*/
|
|
|
.p2align CONFIG_X86_L1_CACHE_SHIFT
|
|
|
common_interrupt:
|
|
|
- ASM_CLAC
|
|
|
addq $-0x80, (%rsp) /* Adjust vector to [-256, -1] range */
|
|
|
- interrupt do_IRQ
|
|
|
+ call interrupt_entry
|
|
|
+ UNWIND_HINT_REGS indirect=1
|
|
|
+ call do_IRQ /* rdi points to pt_regs */
|
|
|
/* 0(%rsp): old RSP */
|
|
|
ret_from_intr:
|
|
|
DISABLE_INTERRUPTS(CLBR_ANY)
|
|
@@ -766,10 +826,11 @@ END(common_interrupt)
|
|
|
.macro apicinterrupt3 num sym do_sym
|
|
|
ENTRY(\sym)
|
|
|
UNWIND_HINT_IRET_REGS
|
|
|
- ASM_CLAC
|
|
|
pushq $~(\num)
|
|
|
.Lcommon_\sym:
|
|
|
- interrupt \do_sym
|
|
|
+ call interrupt_entry
|
|
|
+ UNWIND_HINT_REGS indirect=1
|
|
|
+ call \do_sym /* rdi points to pt_regs */
|
|
|
jmp ret_from_intr
|
|
|
END(\sym)
|
|
|
.endm
|
|
@@ -832,34 +893,6 @@ apicinterrupt IRQ_WORK_VECTOR irq_work_interrupt smp_irq_work_interrupt
|
|
|
*/
|
|
|
#define CPU_TSS_IST(x) PER_CPU_VAR(cpu_tss_rw) + (TSS_ist + ((x) - 1) * 8)
|
|
|
|
|
|
-/*
|
|
|
- * Switch to the thread stack. This is called with the IRET frame and
|
|
|
- * orig_ax on the stack. (That is, RDI..R12 are not on the stack and
|
|
|
- * space has not been allocated for them.)
|
|
|
- */
|
|
|
-ENTRY(switch_to_thread_stack)
|
|
|
- UNWIND_HINT_FUNC
|
|
|
-
|
|
|
- pushq %rdi
|
|
|
- /* Need to switch before accessing the thread stack. */
|
|
|
- SWITCH_TO_KERNEL_CR3 scratch_reg=%rdi
|
|
|
- movq %rsp, %rdi
|
|
|
- movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
|
|
|
- UNWIND_HINT sp_offset=16 sp_reg=ORC_REG_DI
|
|
|
-
|
|
|
- pushq 7*8(%rdi) /* regs->ss */
|
|
|
- pushq 6*8(%rdi) /* regs->rsp */
|
|
|
- pushq 5*8(%rdi) /* regs->eflags */
|
|
|
- pushq 4*8(%rdi) /* regs->cs */
|
|
|
- pushq 3*8(%rdi) /* regs->ip */
|
|
|
- pushq 2*8(%rdi) /* regs->orig_ax */
|
|
|
- pushq 8(%rdi) /* return address */
|
|
|
- UNWIND_HINT_FUNC
|
|
|
-
|
|
|
- movq (%rdi), %rdi
|
|
|
- ret
|
|
|
-END(switch_to_thread_stack)
|
|
|
-
|
|
|
.macro idtentry sym do_sym has_error_code:req paranoid=0 shift_ist=-1
|
|
|
ENTRY(\sym)
|
|
|
UNWIND_HINT_IRET_REGS offset=\has_error_code*8
|
|
@@ -875,12 +908,8 @@ ENTRY(\sym)
|
|
|
pushq $-1 /* ORIG_RAX: no syscall to restart */
|
|
|
.endif
|
|
|
|
|
|
- /* Save all registers in pt_regs */
|
|
|
- PUSH_AND_CLEAR_REGS
|
|
|
- ENCODE_FRAME_POINTER
|
|
|
-
|
|
|
.if \paranoid < 2
|
|
|
- testb $3, CS(%rsp) /* If coming from userspace, switch stacks */
|
|
|
+ testb $3, CS-ORIG_RAX(%rsp) /* If coming from userspace, switch stacks */
|
|
|
jnz .Lfrom_usermode_switch_stack_\@
|
|
|
.endif
|
|
|
|
|
@@ -1130,13 +1159,15 @@ idtentry machine_check do_mce has_error_code=0 paranoid=1
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
|
- * Switch gs if needed.
|
|
|
+ * Save all registers in pt_regs, and switch gs if needed.
|
|
|
* Use slow, but surefire "are we in kernel?" check.
|
|
|
* Return: ebx=0: need swapgs on exit, ebx=1: otherwise
|
|
|
*/
|
|
|
ENTRY(paranoid_entry)
|
|
|
UNWIND_HINT_FUNC
|
|
|
cld
|
|
|
+ PUSH_AND_CLEAR_REGS save_ret=1
|
|
|
+ ENCODE_FRAME_POINTER 8
|
|
|
movl $1, %ebx
|
|
|
movl $MSR_GS_BASE, %ecx
|
|
|
rdmsr
|
|
@@ -1181,12 +1212,14 @@ ENTRY(paranoid_exit)
|
|
|
END(paranoid_exit)
|
|
|
|
|
|
/*
|
|
|
- * Switch gs if needed.
|
|
|
+ * Save all registers in pt_regs, and switch GS if needed.
|
|
|
* Return: EBX=0: came from user mode; EBX=1: otherwise
|
|
|
*/
|
|
|
ENTRY(error_entry)
|
|
|
- UNWIND_HINT_REGS offset=8
|
|
|
+ UNWIND_HINT_FUNC
|
|
|
cld
|
|
|
+ PUSH_AND_CLEAR_REGS save_ret=1
|
|
|
+ ENCODE_FRAME_POINTER 8
|
|
|
testb $3, CS+8(%rsp)
|
|
|
jz .Lerror_kernelspace
|
|
|
|
|
@@ -1577,8 +1610,6 @@ end_repeat_nmi:
|
|
|
* frame to point back to repeat_nmi.
|
|
|
*/
|
|
|
pushq $-1 /* ORIG_RAX: no syscall to restart */
|
|
|
- PUSH_AND_CLEAR_REGS
|
|
|
- ENCODE_FRAME_POINTER
|
|
|
|
|
|
/*
|
|
|
* Use paranoid_entry to handle SWAPGS, but no need to use paranoid_exit
|