Author: jhb
Date: Tue Feb 24 23:11:15 2009
New Revision: 189017
URL: http://svn.freebsd.org/changeset/base/189017

Log:
  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

Modified:
  head/sys/boot/i386/btx/btx/btx.S

Modified: head/sys/boot/i386/btx/btx/btx.S
==============================================================================
--- head/sys/boot/i386/btx/btx/btx.S    Tue Feb 24 22:25:08 2009        
(r189016)
+++ head/sys/boot/i386/btx/btx/btx.S    Tue Feb 24 23:11:15 2009        
(r189017)
@@ -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 
  * -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 
                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
                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
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to