diff options
Diffstat (limited to 'arch/x86/entry/entry_64.S')
| -rw-r--r-- | arch/x86/entry/entry_64.S | 223 | 
1 files changed, 100 insertions, 123 deletions
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S index 30c8c5344c4a..18ed349b4f83 100644 --- a/arch/x86/entry/entry_64.S +++ b/arch/x86/entry/entry_64.S @@ -55,7 +55,7 @@ END(native_usergs_sysret64)  .macro TRACE_IRQS_FLAGS flags:req  #ifdef CONFIG_TRACE_IRQFLAGS -	bt	$9, \flags		/* interrupts off? */ +	btl	$9, \flags		/* interrupts off? */  	jnc	1f  	TRACE_IRQS_ON  1: @@ -213,7 +213,7 @@ ENTRY(entry_SYSCALL_64)  	swapgs  	/* -	 * This path is not taken when PAGE_TABLE_ISOLATION is disabled so it +	 * This path is only taken when PAGE_TABLE_ISOLATION is disabled so it  	 * is not required to switch CR3.  	 */  	movq	%rsp, PER_CPU_VAR(rsp_scratch) @@ -227,22 +227,8 @@ ENTRY(entry_SYSCALL_64)  	pushq	%rcx				/* pt_regs->ip */  GLOBAL(entry_SYSCALL_64_after_hwframe)  	pushq	%rax				/* pt_regs->orig_ax */ -	pushq	%rdi				/* pt_regs->di */ -	pushq	%rsi				/* pt_regs->si */ -	pushq	%rdx				/* pt_regs->dx */ -	pushq	%rcx				/* pt_regs->cx */ -	pushq	$-ENOSYS			/* pt_regs->ax */ -	pushq	%r8				/* pt_regs->r8 */ -	pushq	%r9				/* pt_regs->r9 */ -	pushq	%r10				/* pt_regs->r10 */ -	pushq	%r11				/* pt_regs->r11 */ -	pushq	%rbx				/* pt_regs->rbx */ -	pushq	%rbp				/* pt_regs->rbp */ -	pushq	%r12				/* pt_regs->r12 */ -	pushq	%r13				/* pt_regs->r13 */ -	pushq	%r14				/* pt_regs->r14 */ -	pushq	%r15				/* pt_regs->r15 */ -	UNWIND_HINT_REGS + +	PUSH_AND_CLEAR_REGS rax=$-ENOSYS  	TRACE_IRQS_OFF @@ -321,15 +307,7 @@ GLOBAL(entry_SYSCALL_64_after_hwframe)  syscall_return_via_sysret:  	/* rcx and r11 are already restored (see code above) */  	UNWIND_HINT_EMPTY -	POP_EXTRA_REGS -	popq	%rsi	/* skip r11 */ -	popq	%r10 -	popq	%r9 -	popq	%r8 -	popq	%rax -	popq	%rsi	/* skip rcx */ -	popq	%rdx -	popq	%rsi +	POP_REGS pop_rdi=0 skip_r11rcx=1  	/*  	 * Now all regs are restored except RSP and RDI. @@ -386,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 */ @@ -471,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 @@ -519,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  /* @@ -542,29 +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: -	ALLOC_PT_GPREGS_ON_STACK -	SAVE_C_REGS -	SAVE_EXTRA_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  	/* @@ -572,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. @@ -582,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 @@ -595,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) @@ -622,15 +658,7 @@ GLOBAL(swapgs_restore_regs_and_return_to_usermode)  	ud2  1:  #endif -	POP_EXTRA_REGS -	popq	%r11 -	popq	%r10 -	popq	%r9 -	popq	%r8 -	popq	%rax -	popq	%rcx -	popq	%rdx -	popq	%rsi +	POP_REGS pop_rdi=0  	/*  	 * The stack is now user RDI, orig_ax, RIP, CS, EFLAGS, RSP, SS. @@ -688,8 +716,7 @@ GLOBAL(restore_regs_and_return_to_kernel)  	ud2  1:  #endif -	POP_EXTRA_REGS -	POP_C_REGS +	POP_REGS  	addq	$8, %rsp	/* skip regs->orig_ax */  	/*  	 * ARCH_HAS_MEMBARRIER_SYNC_CORE rely on IRET core serialization @@ -799,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 @@ -865,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 @@ -908,10 +908,8 @@ ENTRY(\sym)  	pushq	$-1				/* ORIG_RAX: no syscall to restart */  	.endif -	ALLOC_PT_GPREGS_ON_STACK -  	.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 @@ -1121,9 +1119,7 @@ ENTRY(xen_failsafe_callback)  	addq	$0x30, %rsp  	UNWIND_HINT_IRET_REGS  	pushq	$-1 /* orig_ax = -1 => not a system call */ -	ALLOC_PT_GPREGS_ON_STACK -	SAVE_C_REGS -	SAVE_EXTRA_REGS +	PUSH_AND_CLEAR_REGS  	ENCODE_FRAME_POINTER  	jmp	error_exit  END(xen_failsafe_callback) @@ -1142,7 +1138,7 @@ apicinterrupt3 HYPERV_REENLIGHTENMENT_VECTOR \  #endif /* CONFIG_HYPERV */  idtentry debug			do_debug		has_error_code=0	paranoid=1 shift_ist=DEBUG_STACK -idtentry int3			do_int3			has_error_code=0	paranoid=1 shift_ist=DEBUG_STACK +idtentry int3			do_int3			has_error_code=0  idtentry stack_segment		do_stack_segment	has_error_code=1  #ifdef CONFIG_XEN @@ -1170,8 +1166,7 @@ idtentry machine_check		do_mce			has_error_code=0	paranoid=1  ENTRY(paranoid_entry)  	UNWIND_HINT_FUNC  	cld -	SAVE_C_REGS 8 -	SAVE_EXTRA_REGS 8 +	PUSH_AND_CLEAR_REGS save_ret=1  	ENCODE_FRAME_POINTER 8  	movl	$1, %ebx  	movl	$MSR_GS_BASE, %ecx @@ -1211,21 +1206,20 @@ ENTRY(paranoid_exit)  	jmp	.Lparanoid_exit_restore  .Lparanoid_exit_no_swapgs:  	TRACE_IRQS_IRETQ_DEBUG +	RESTORE_CR3	scratch_reg=%rbx save_reg=%r14  .Lparanoid_exit_restore:  	jmp restore_regs_and_return_to_kernel  END(paranoid_exit)  /* - * Save all registers in pt_regs, and 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_FUNC  	cld -	SAVE_C_REGS 8 -	SAVE_EXTRA_REGS 8 +	PUSH_AND_CLEAR_REGS save_ret=1  	ENCODE_FRAME_POINTER 8 -	xorl	%ebx, %ebx  	testb	$3, CS+8(%rsp)  	jz	.Lerror_kernelspace @@ -1406,22 +1400,7 @@ ENTRY(nmi)  	pushq	1*8(%rdx)	/* pt_regs->rip */  	UNWIND_HINT_IRET_REGS  	pushq   $-1		/* pt_regs->orig_ax */ -	pushq   %rdi		/* pt_regs->di */ -	pushq   %rsi		/* pt_regs->si */ -	pushq   (%rdx)		/* pt_regs->dx */ -	pushq   %rcx		/* pt_regs->cx */ -	pushq   %rax		/* pt_regs->ax */ -	pushq   %r8		/* pt_regs->r8 */ -	pushq   %r9		/* pt_regs->r9 */ -	pushq   %r10		/* pt_regs->r10 */ -	pushq   %r11		/* pt_regs->r11 */ -	pushq	%rbx		/* pt_regs->rbx */ -	pushq	%rbp		/* pt_regs->rbp */ -	pushq	%r12		/* pt_regs->r12 */ -	pushq	%r13		/* pt_regs->r13 */ -	pushq	%r14		/* pt_regs->r14 */ -	pushq	%r15		/* pt_regs->r15 */ -	UNWIND_HINT_REGS +	PUSH_AND_CLEAR_REGS rdx=(%rdx)  	ENCODE_FRAME_POINTER  	/* @@ -1631,7 +1610,6 @@ end_repeat_nmi:  	 * frame to point back to repeat_nmi.  	 */  	pushq	$-1				/* ORIG_RAX: no syscall to restart */ -	ALLOC_PT_GPREGS_ON_STACK  	/*  	 * Use paranoid_entry to handle SWAPGS, but no need to use paranoid_exit @@ -1655,8 +1633,7 @@ end_repeat_nmi:  nmi_swapgs:  	SWAPGS_UNSAFE_STACK  nmi_restore: -	POP_EXTRA_REGS -	POP_C_REGS +	POP_REGS  	/*  	 * Skip orig_ax and the "outermost" frame to point RSP at the "iret"  | 

