summaryrefslogtreecommitdiffstats
path: root/sys/boot
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2009-02-24 23:11:15 +0000
committerjhb <jhb@FreeBSD.org>2009-02-24 23:11:15 +0000
commitc9807908ae312d90405593cf8c4f91f56e0f3ffb (patch)
tree798fa2876735ea7c4584899bc076e68aa729079b /sys/boot
parent66c697aade1d10f1d217e0bd2c794cc04d3ce45d (diff)
downloadFreeBSD-src-c9807908ae312d90405593cf8c4f91f56e0f3ffb.zip
FreeBSD-src-c9807908ae312d90405593cf8c4f91f56e0f3ffb.tar.gz
Fix some more issues with the real mode BTX.
The old BTX passed the general purpose registers from the 32-bit client to the routines called via virtual 86 mode. The new BTX did the same thing. However, it turns out that some instructions behave differently in virtual 86 mode and real mode (even though this is under-documented). For example, the LEAVE instruction will cause an exception in real mode if any of the upper 16-bits of %ebp are non-zero after it executes. In virtual 8086 mode the upper 16-bits are simply ignored. This could cause faults in hardware interrupt handlers that inherited an %ebp larger than 0xffff from the 32-bit client (loader, boot2, etc.) while running in real mode. To fix, when executing hardware interrupt handlers provide an explicit clean state where all the general purpose and segment registers are zero upon entry to the interrupt handler. While here, I attempted to simplify the control flow in the 'intusr' code that sets up the various stack frames and exits protected mode to invoke the requested routine via real mode. A huge thanks to Tor Egge (tegge@) for debugging this issue. Submitted by: tegge Reviewed by: tegge Tested by: bz MFC after: 1 week
Diffstat (limited to 'sys/boot')
-rw-r--r--sys/boot/i386/btx/btx/btx.S95
1 files changed, 60 insertions, 35 deletions
diff --git a/sys/boot/i386/btx/btx/btx.S b/sys/boot/i386/btx/btx/btx.S
index 2f917e0..e762e4b 100644
--- a/sys/boot/i386/btx/btx/btx.S
+++ b/sys/boot/i386/btx/btx/btx.S
@@ -36,6 +36,7 @@
/*
* Fields in %eflags.
*/
+ .set PSL_RESERVED_DEFAULT,0x00000002
.set PSL_T,0x00000100 # Trap flag
.set PSL_I,0x00000200 # Interrupt enable flag
.set PSL_VM,0x00020000 # Virtual 8086 mode flag
@@ -455,6 +456,18 @@ intx31: pushl $-1 # Dummy int no for btx_v86
* -0x3c %fs
* -0x40 %ds
* -0x44 %es
+ * -0x48 zero %eax (hardware int only)
+ * -0x4c zero %ecx (hardware int only)
+ * -0x50 zero %edx (hardware int only)
+ * -0x54 zero %ebx (hardware int only)
+ * -0x58 zero %esp (hardware int only)
+ * -0x5c zero %ebp (hardware int only)
+ * -0x60 zero %esi (hardware int only)
+ * -0x64 zero %edi (hardware int only)
+ * -0x68 zero %gs (hardware int only)
+ * -0x6c zero %fs (hardware int only)
+ * -0x70 zero %ds (hardware int only)
+ * -0x74 zero %es (hardware int only)
*/
int_hw: cld # String ops inc
pusha # Save gp regs
@@ -467,12 +480,15 @@ int_hw: cld # String ops inc
pushl %ds # address
popl %es # data
leal 0x44(%esp,1),%esi # Base of frame
+ movl %esp,MEM_ESPR-0x04 # Save kernel stack pointer
movl -0x14(%esi),%eax # Get Int no
cmpl $-1,%eax # Hardware interrupt?
- jne intusr.2 # Yes
+ jne intusr.1 # Yes
/*
- * v86 calls save the btx_v86 pointer on the real mode stack and read the
- * address and flags from the btx_v86 structure.
+ * v86 calls save the btx_v86 pointer on the real mode stack and read
+ * the address and flags from the btx_v86 structure. For interrupt
+ * handler invocations (VM86 INTx requests), disable interrupts,
+ * tracing, and alignment checking while the handler runs.
*/
movl $MEM_USR,%ebx # User base
movl %ebx,%edx # address
@@ -482,35 +498,36 @@ int_hw: cld # String ops inc
movl %edx,MEM_ESPR-0x08 # Save btx_v86 ptr
movl V86_ADDR(%edx),%eax # Get int no/address
movl V86_CTL(%edx),%edx # Get control flags
+ movl -0x08(%esi),%ebx # Save user flags in %ebx
+ testl $V86F_ADDR,%edx # Segment:offset?
+ jnz intusr.4 # Yes
+ andl $~(PSL_I|PSL_T|PSL_AC),%ebx # Disable interrupts, tracing,
+ # and alignment checking for
+ # interrupt handler
jmp intusr.3 # Skip hardware interrupt
/*
- * Hardware interrupts store a NULL btx_v86 pointer and use the address
- * (interrupt number) from the stack with empty flags. Also, we clear
- * the segment registers for the interrupt handler.
+ * Hardware interrupts store a NULL btx_v86 pointer and use the
+ * address (interrupt number) from the stack with empty flags. Also,
+ * push a dummy frame of zeros onto the stack for all the general
+ * purpose and segment registers and clear %eflags. This gives the
+ * hardware interrupt handler a clean slate.
*/
-intusr.2: xorl %edx,%edx # Control flags
+intusr.1: xorl %edx,%edx # Control flags
movl %edx,MEM_ESPR-0x08 # NULL btx_v86 ptr
- movl %edx,-0x38(%esi) # Real mode %gs of 0
- movl %edx,-0x3c(%esi) # Real mode %fs of 0
- movl %edx,-0x40(%esi) # Real mode %ds of 0
- movl %edx,-0x44(%esi) # Real mode %es of 0
+ movl $12,%ecx # Frame is 12 dwords
+intusr.2: pushl $0x0 # Fill frame
+ loop intusr.2 # with zeros
+ movl $PSL_RESERVED_DEFAULT,%ebx # Set clean %eflags
/*
- * %eax now holds either the interrupt number or segment:offset of function.
- * %edx now holds the V86F_* flags.
- *
- * For interrupt handler invocations (either hardware interrupts or VM86
- * INTx requests) we also disable interrupts, tracing, and alignment checking
- * while the handler runs.
+ * Look up real mode IDT entry for hardware interrupts and VM86 INTx
+ * requests.
*/
-intusr.3: movl -0x08(%esi),%ebx # Save user flags in %ebx
- testl $V86F_ADDR,%edx # Segment:offset?
- jnz intusr.4 # Yes
- shll $0x2,%eax # Scale
+intusr.3: shll $0x2,%eax # Scale
movl (%eax),%eax # Load int vector
- andl $~(PSL_I|PSL_T|PSL_AC),%ebx # Disable interrupts, tracing,
- # and alignment checking for
- # interrupt handler
jmp intusr.5 # Skip CALLF test
+/*
+ * Panic if V86F_CALLF isn't set with V86F_ADDR.
+ */
intusr.4: testl $V86F_CALLF,%edx # Far call?
jnz intusr.5 # Ok
movl %edx,0x30(%esp,1) # Place VM86 flags in int no
@@ -522,6 +539,11 @@ intusr.4: testl $V86F_CALLF,%edx # Far call?
popl %gs
popal # Restore gp regs
jmp ex_noc # Panic
+/*
+ * %eax now holds the segment:offset of the function.
+ * %ebx now holds the %eflags to pass to real mode.
+ * %edx now holds the V86F_* flags.
+ */
intusr.5: movw %bx,MEM_ESPR-0x12 # Pass user flags to real mode
# target
/*
@@ -536,8 +558,7 @@ intusr.5: movw %bx,MEM_ESPR-0x12 # Pass user flags to real mode
rep # from btx_v86
movsl # to kernel stack
popl %esi # Restore
-intusr.6: movl %esp,MEM_ESPR-0x04 # Save kernel stack pointer
- movl -0x08(%esi),%ebx # Copy user flags to real
+intusr.6: movl -0x08(%esi),%ebx # Copy user flags to real
movl %ebx,MEM_ESPR-0x0c # mode return trampoline
movl $rret_tramp,%ebx # Set return trampoline
movl %ebx,MEM_ESPR-0x10 # CS:IP
@@ -611,9 +632,16 @@ rret_tramp.1: xorl %ecx,%ecx # Zero
movb $SEL_TSS,%cl # Set task
ltr %cx # register
/*
- * Now we are back in protected mode. Copy the registers off of the real
- * mode stack onto the kernel stack. Also, initialize all the seg regs on
- * the kernel stack.
+ * Now we are back in protected mode. The kernel stack frame set up
+ * before entering real mode is still intact. For hardware interrupts,
+ * leave the frame unchanged.
+ */
+ cmpl $0,MEM_ESPR-0x08 # Leave saved regs unchanged
+ jz rret_tramp.3 # for hardware ints
+/*
+ * For V86 calls, copy the registers off of the real mode stack onto
+ * the kernel stack as we want their updated values. Also, initialize
+ * the segment registers on the kernel stack.
*
* Note that the %esp in the kernel stack after this is garbage, but popa
* ignores it, so we don't have to fix it up.
@@ -624,20 +652,17 @@ rret_tramp.1: xorl %ecx,%ecx # Zero
movl $8,%ecx # Copy GP regs from
rep # real mode stack
movsl # to kernel stack
- popl %esi # Restore
movl $SEL_UDATA,%eax # Selector for data seg regs
movl $4,%ecx # Initialize %ds,
rep # %es, %fs, and
stosl # %gs
/*
- * If this was a V86 call, copy the saved seg regs on the real mode stack
- * back over to the btx_v86 structure. Also, conditionally update the saved
- * eflags on the kernel stack based on the flags from the user.
+ * For V86 calls, copy the saved seg regs on the real mode stack back
+ * over to the btx_v86 structure. Also, conditionally update the
+ * saved eflags on the kernel stack based on the flags from the user.
*/
movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr
- jecxz rret_tramp.3 # Skip for hardware ints
leal V86_GS(%ecx),%edi # %edi => btx_v86 seg regs
- pushl %esi # Save
leal MEM_ESPR-0x2c,%esi # %esi => real mode seg regs
xchgl %ecx,%edx # Save btx_v86 ptr
movl $4,%ecx # Copy seg regs
OpenPOWER on IntegriCloud