diff options
Diffstat (limited to 'arch/x86/kernel/entry_32.S')
| -rw-r--r-- | arch/x86/kernel/entry_32.S | 66 | 
1 files changed, 44 insertions, 22 deletions
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index c929add475c..c097e7d607c 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -48,7 +48,6 @@  #include <asm/segment.h>  #include <asm/smp.h>  #include <asm/page_types.h> -#include <asm/desc.h>  #include <asm/percpu.h>  #include <asm/dwarf2.h>  #include <asm/processor-flags.h> @@ -84,7 +83,7 @@  #define preempt_stop(clobbers)	DISABLE_INTERRUPTS(clobbers); TRACE_IRQS_OFF  #else  #define preempt_stop(clobbers) -#define resume_kernel		restore_nocheck +#define resume_kernel		restore_all  #endif  .macro TRACE_IRQS_IRET @@ -372,7 +371,7 @@ END(ret_from_exception)  ENTRY(resume_kernel)  	DISABLE_INTERRUPTS(CLBR_ANY)  	cmpl $0,TI_preempt_count(%ebp)	# non-zero preempt_count ? -	jnz restore_nocheck +	jnz restore_all  need_resched:  	movl TI_flags(%ebp), %ecx	# need_resched set ?  	testb $_TIF_NEED_RESCHED, %cl @@ -540,6 +539,8 @@ syscall_exit:  	jne syscall_exit_work  restore_all: +	TRACE_IRQS_IRET +restore_all_notrace:  	movl PT_EFLAGS(%esp), %eax	# mix EFLAGS, SS and CS  	# Warning: PT_OLDSS(%esp) contains the wrong/random values if we  	# are returning to the kernel. @@ -551,8 +552,6 @@ restore_all:  	CFI_REMEMBER_STATE  	je ldt_ss			# returning to user-space with LDT SS  restore_nocheck: -	TRACE_IRQS_IRET -restore_nocheck_notrace:  	RESTORE_REGS 4			# skip orig_eax/error_code  	CFI_ADJUST_CFA_OFFSET -4  irq_return: @@ -588,22 +587,34 @@ ldt_ss:  	jne restore_nocheck  #endif -	/* If returning to userspace with 16bit stack, -	 * try to fix the higher word of ESP, as the CPU -	 * won't restore it. -	 * This is an "official" bug of all the x86-compatible -	 * CPUs, which we can try to work around to make -	 * dosemu and wine happy. */ -	movl PT_OLDESP(%esp), %eax -	movl %esp, %edx -	call patch_espfix_desc +/* + * Setup and switch to ESPFIX stack + * + * We're returning to userspace with a 16 bit stack. The CPU will not + * restore the high word of ESP for us on executing iret... This is an + * "official" bug of all the x86-compatible CPUs, which we can work + * around to make dosemu and wine happy. We do this by preloading the + * high word of ESP with the high word of the userspace ESP while + * compensating for the offset by changing to the ESPFIX segment with + * a base address that matches for the difference. + */ +	mov %esp, %edx			/* load kernel esp */ +	mov PT_OLDESP(%esp), %eax	/* load userspace esp */ +	mov %dx, %ax			/* eax: new kernel esp */ +	sub %eax, %edx			/* offset (low word is 0) */ +	PER_CPU(gdt_page, %ebx) +	shr $16, %edx +	mov %dl, GDT_ENTRY_ESPFIX_SS * 8 + 4(%ebx) /* bits 16..23 */ +	mov %dh, GDT_ENTRY_ESPFIX_SS * 8 + 7(%ebx) /* bits 24..31 */  	pushl $__ESPFIX_SS  	CFI_ADJUST_CFA_OFFSET 4 -	pushl %eax +	push %eax			/* new kernel esp */  	CFI_ADJUST_CFA_OFFSET 4 +	/* Disable interrupts, but do not irqtrace this section: we +	 * will soon execute iret and the tracer was already set to +	 * the irqstate after the iret */  	DISABLE_INTERRUPTS(CLBR_EAX) -	TRACE_IRQS_OFF -	lss (%esp), %esp +	lss (%esp), %esp		/* switch to espfix segment */  	CFI_ADJUST_CFA_OFFSET -8  	jmp restore_nocheck  	CFI_ENDPROC @@ -716,15 +727,24 @@ PTREGSCALL(vm86)  PTREGSCALL(vm86old)  .macro FIXUP_ESPFIX_STACK -	/* since we are on a wrong stack, we cant make it a C code :( */ +/* + * Switch back for ESPFIX stack to the normal zerobased stack + * + * We can't call C functions using the ESPFIX stack. This code reads + * the high word of the segment base from the GDT and swiches to the + * normal stack and adjusts ESP with the matching offset. + */ +	/* fixup the stack */  	PER_CPU(gdt_page, %ebx) -	GET_DESC_BASE(GDT_ENTRY_ESPFIX_SS, %ebx, %eax, %ax, %al, %ah) -	addl %esp, %eax +	mov GDT_ENTRY_ESPFIX_SS * 8 + 4(%ebx), %al /* bits 16..23 */ +	mov GDT_ENTRY_ESPFIX_SS * 8 + 7(%ebx), %ah /* bits 24..31 */ +	shl $16, %eax +	addl %esp, %eax			/* the adjusted stack pointer */  	pushl $__KERNEL_DS  	CFI_ADJUST_CFA_OFFSET 4  	pushl %eax  	CFI_ADJUST_CFA_OFFSET 4 -	lss (%esp), %esp +	lss (%esp), %esp		/* switch to the normal stack segment */  	CFI_ADJUST_CFA_OFFSET -8  .endm  .macro UNWIND_ESPFIX_STACK @@ -1154,6 +1174,7 @@ ENTRY(ftrace_graph_caller)  	pushl %edx  	movl 0xc(%esp), %edx  	lea 0x4(%ebp), %eax +	movl (%ebp), %ecx  	subl $MCOUNT_INSN_SIZE, %edx  	call prepare_ftrace_return  	popl %edx @@ -1168,6 +1189,7 @@ return_to_handler:  	pushl %eax  	pushl %ecx  	pushl %edx +	movl %ebp, %eax  	call ftrace_return_to_handler  	movl %eax, 0xc(%esp)  	popl %edx @@ -1329,7 +1351,7 @@ nmi_stack_correct:  	xorl %edx,%edx		# zero error code  	movl %esp,%eax		# pt_regs pointer  	call do_nmi -	jmp restore_nocheck_notrace +	jmp restore_all_notrace  	CFI_ENDPROC  nmi_stack_fixup:  |