|
@@ -588,24 +588,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)
|
|
|
- lss (%esp), %esp
|
|
|
+ lss (%esp), %esp /* switch to espfix segment */
|
|
|
CFI_ADJUST_CFA_OFFSET -8
|
|
|
jmp restore_nocheck
|
|
|
CFI_ENDPROC
|
|
@@ -718,15 +728,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
|