Change the BTX kernel to drop all the way out to real mode to invoke BIOS

routines (V86 requests from the client and hardware interrupt handlers):
- Install trampoline real mode interrupt handlers at IDT vectors 0x20-0x2f
  to handle hardware interrupts by invoking the appropriate vector (0x8-0xf
  or 0x70-0x78).  This allows the 8259As to use vectors 0x20-0x2f in real
  mode as well as protected mode will ensuring that the master 8259A
  doesn't share IDT space with CPU exceptions in protected mode.
- Since we don't need to reserve space for page tables and a page directory
  anymore since dropping paging support, move the TSS and protected mode
  IDT up by 16k.  Grow the ring 1 link stack by 16k as a result.
- Repurpose the ring 1 link stack to be used as a real mode stack when
  invoking real mode routines either via a V86 request or a hardware
  interrupts.  This simplifies a few things as we avoid disturbing the
  original user stack.
- Add some more block comments to explain how the code interacts with the
  V86 structure as this wasn't immediately obvious from the prior comments
  (e.g. that we explicitly copy the seg regs for real mode out of the V86
  struct onto the stack to be popped off when going into real mode, etc.).
  Also, document some of the stack frames we create going to real mode and
  back.
- Remove all of the virtual 86 related code including having to simulate
  various instructions and BIOS calls on a trap from virtual 86 mode.
- Explicitly panic if a user client attempts to perform a V86 CALL
  request that isn't a far call.
- Bump version to 1.2.

Assuming this works ok this should fix some of the long standing issues
with USB booting as well as etherboot.

MFC after:	2 weeks
Submitted by:	kib (some parts from his original real mode patch)
This commit is contained in:
John Baldwin 2008-03-10 21:43:31 +00:00
parent a818f1140f
commit 4937cb2d30
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=177039

View File

@ -21,11 +21,11 @@
.set MEM_BTX,0x1000 # Start of BTX memory
.set MEM_ESP0,0x1800 # Supervisor stack
.set MEM_BUF,0x1800 # Scratch buffer
.set MEM_ESP1,0x1e00 # Link stack
.set MEM_IDT,0x1e00 # IDT
.set MEM_TSS,0x1f98 # TSS
.set MEM_MAP,0x2000 # I/O bit map
.set MEM_TSS_END,0x3fff # Page directory
.set MEM_ESPR,0x5e00 # Real mode stack
.set MEM_IDT,0x5e00 # IDT
.set MEM_TSS,0x5f98 # TSS
.set MEM_MAP,0x6000 # I/O bit map
.set MEM_TSS_END,0x7fff # End of TSS
.set MEM_ORG,0x9000 # BTX code
.set MEM_USR,0xa000 # Start of user memory
/*
@ -48,7 +48,6 @@
*/
.set TSS_ESP0,0x4 # PL 0 ESP
.set TSS_SS0,0x8 # PL 0 SS
.set TSS_ESP1,0xc # PL 1 ESP
.set TSS_MAP,0x66 # I/O bit map base
/*
* System calls.
@ -56,10 +55,20 @@
.set SYS_EXIT,0x0 # Exit
.set SYS_EXEC,0x1 # Exec
/*
* V86 constants.
* Fields in V86 interface structure.
*/
.set V86_FLG,0x208eff # V86 flag mask
.set V86_STK,0x400 # V86 stack allowance
.set V86_CTL,0x0 # Control flags
.set V86_ADDR,0x4 # Int number/address
.set V86_ES,0x8 # V86 ES
.set V86_DS,0xc # V86 DS
.set V86_FS,0x10 # V86 FS
.set V86_GS,0x14 # V86 GS
/*
* V86 control flags.
*/
.set V86F_ADDR,0x10000 # Segment:offset address
.set V86F_CALLF,0x20000 # Emulate far call
.set V86F_FLAGS,0x40000 # Return flags
/*
* Dump format control bytes.
*/
@ -77,7 +86,6 @@
* BIOS Data Area locations.
*/
.set BDA_MEM,0x413 # Free memory
.set BDA_KEYFLAGS,0x417 # Keyboard shift-state flags
.set BDA_SCR,0x449 # Video mode
.set BDA_POS,0x450 # Cursor position
.set BDA_BOOT,0x472 # Boot howto flag
@ -85,7 +93,6 @@
* Derivations, for brevity.
*/
.set _ESP0H,MEM_ESP0>>0x8 # Byte 1 of ESP0
.set _ESP1H,MEM_ESP1>>0x8 # Byte 1 of ESP1
.set _TSSIO,MEM_MAP-MEM_TSS # TSS I/O base
.set _TSSLM,MEM_TSS_END-MEM_TSS # TSS limit
.set _IDTLM,MEM_TSS-MEM_IDT-1 # IDT limit
@ -102,7 +109,7 @@ btx_hdr: .byte 0xeb # Machine ID
.byte 0xe # Header size
.ascii "BTX" # Magic
.byte 0x1 # Major version
.byte 0x1 # Minor version
.byte 0x2 # Minor version
.byte BTX_FLAGS # Flags
.word PAG_CNT-MEM_ORG>>0xc # Paging control
.word break-start # Text size
@ -123,13 +130,24 @@ init: cli # Disable interrupts
*/
mov $MEM_IDT,%di # Memory to initialize
mov $(MEM_ORG-MEM_IDT)/2,%cx # Words to zero
push %di # Save
rep # Zero-fill
stosw # memory
pop %di # Restore
/*
* Update real mode IDT for reflecting hardware interrupts.
*/
mov $intr20,%bx # Address first handler
mov $0x10,%cx # Number of handlers
mov $0x20*4,%di # First real mode IDT entry
init.0: mov %bx,(%di) # Store IP
inc %di # Address next
inc %di # entry
stosw # Store CS
add $4,%bx # Next handler
loop init.0 # Next IRQ
/*
* Create IDT.
*/
mov $MEM_IDT,%di
mov $idtctl,%si # Control string
init.1: lodsb # Get entry
cbw # count
@ -155,7 +173,6 @@ init.3: lea 0x8(%di),%di # Next entry
*/
init.4: movb $_ESP0H,TSS_ESP0+1(%di) # Set ESP0
movb $SEL_SDATA,TSS_SS0(%di) # Set SS0
movb $_ESP1H,TSS_ESP1+1(%di) # Set ESP1
movb $_TSSIO,TSS_MAP(%di) # Set I/O bit map base
/*
* Bring up the system.
@ -280,10 +297,6 @@ setpic: in $0x21,%al # Save master
outb %al,$0x21 # IMR
retw # To caller
.code32
/*
* Initiate return from V86 mode to user mode.
*/
inthlt: hlt # To supervisor mode
/*
* Exception jump table.
*/
@ -310,17 +323,11 @@ intx00: push $0x0 # Int 0x0: #DE
push $0xc # Int 0xc: #SS
jmp except # Stack segment fault
push $0xd # Int 0xd: #GP
jmp ex_v86 # General protection
jmp except # General protection
push $0xe # Int 0xe: #PF
jmp except # Page fault
intx10: push $0x10 # Int 0x10: #MF
jmp ex_noc # Floating-point error
/*
* Handle #GP exception.
*/
ex_v86: testb $0x2,0x12(%esp,1) # V86 mode?
jz except # No
jmp v86mon # To monitor
/*
* Save a zero error code.
*/
@ -333,24 +340,17 @@ except: cld # String ops inc
pushl %ds # Save
pushl %es # most
pusha # registers
movb $0x6,%al # Push loop count
testb $0x2,0x3a(%esp,1) # V86 mode?
jnz except.1 # Yes
pushl %gs # Set GS
pushl %fs # Set FS
pushl %ds # Set DS
pushl %es # Set ES
movb $0x2,%al # Push loop count
cmpw $SEL_SCODE,0x44(%esp,1) # Supervisor mode?
jne except.1 # No
pushl %ss # Set SS
leal 0x50(%esp,1),%eax # Set
pushl %eax # ESP
jmp except.2 # Join common code
except.1: pushl 0x50(%esp,1) # Set GS, FS, DS, ES
decb %al # (if V86 mode), and
jne except.1 # SS, ESP
except.2: push $SEL_SDATA # Set up
except.1: pushl 0x50(%esp,1) # Set SS
except.2: pushl 0x50(%esp,1) # Set ESP
push $SEL_SDATA # Set up
popl %ds # to
pushl %ds # address
popl %es # data
@ -374,234 +374,6 @@ except.2: push $SEL_SDATA # Set up
except.2a: jmp exit # Exit
except.3: leal 0x8(%esp,1),%esp # Discard err, int no
iret # From interrupt
/*
* Return to user mode from V86 mode.
*/
intrtn: cld # String ops inc
pushl %ds # Address
popl %es # data
leal 0x3c(%ebp),%edx # V86 Segment registers
movl MEM_TSS+TSS_ESP1,%esi # Link stack pointer
lodsl # INT_V86 args pointer
movl %esi,%ebx # Saved exception frame
testl %eax,%eax # INT_V86 args?
jz intrtn.2 # No
movl $MEM_USR,%edi # User base
movl 0x1c(%esi),%ebx # User ESP
movl %eax,(%edi,%ebx,1) # Restore to user stack
leal 0x8(%edi,%eax,1),%edi # Arg segment registers
testb $0x4,-0x6(%edi) # Return flags?
jz intrtn.1 # No
movl 0x30(%ebp),%eax # Get V86 flags
movw %ax,0x18(%esi) # Set user flags
intrtn.1: leal 0x10(%esi),%ebx # Saved exception frame
xchgl %edx,%esi # Segment registers
movb $0x4,%cl # Update seg regs
rep # in INT_V86
movsl # args
intrtn.2: xchgl %edx,%esi # Segment registers
leal 0x28(%ebp),%edi # Set up seg
movb $0x4,%cl # regs for
rep # later
movsl # pop
xchgl %ebx,%esi # Restore exception
movb $0x5,%cl # frame to
rep # supervisor
movsl # stack
movl %esi,MEM_TSS+TSS_ESP1 # Link stack pointer
popa # Restore
leal 0x8(%esp,1),%esp # Discard err, int no
popl %es # Restore
popl %ds # user
popl %fs # segment
popl %gs # registers
iret # To user mode
/*
* V86 monitor.
*/
v86mon: cld # String ops inc
pushl $SEL_SDATA # Set up for
popl %ds # flat addressing
pusha # Save registers
movl %esp,%ebp # Address stack frame
movzwl 0x2c(%ebp),%edi # Load V86 CS
shll $0x4,%edi # To linear
movl 0x28(%ebp),%esi # Load V86 IP
addl %edi,%esi # Code pointer
xorl %ecx,%ecx # Zero
movb $0x2,%cl # 16-bit operands
xorl %eax,%eax # Zero
v86mon.1: lodsb # Get opcode
cmpb $0x66,%al # Operand size prefix?
jne v86mon.2 # No
movb $0x4,%cl # 32-bit operands
jmp v86mon.1 # Continue
v86mon.2: cmpb $0xf4,%al # HLT?
jne v86mon.3 # No
cmpl $inthlt+0x1,%esi # Is inthlt?
jne v86mon.7 # No (ignore)
jmp intrtn # Return to user mode
v86mon.3: cmpb $0xf,%al # Prefixed instruction?
jne v86mon.4 # No
cmpb $0x09,(%esi) # Is it a WBINVD?
je v86wbinvd # Yes
cmpb $0x30,(%esi) # Is it a WRMSR?
je v86wrmsr # Yes
cmpb $0x32,(%esi) # Is it a RDMSR?
je v86rdmsr # Yes
cmpb $0x20,(%esi) # Is this a MOV reg,CRx?
je v86mov # Yes
v86mon.4: cmpb $0xfa,%al # CLI?
je v86cli # Yes
cmpb $0xfb,%al # STI?
je v86sti # Yes
cmpb $0xcc,%al # INT3?
je v86mon.7 # Yes, ignore
movzwl 0x38(%ebp),%ebx # Load V86 SS
shll $0x4,%ebx # To offset
pushl %ebx # Save
addl 0x34(%ebp),%ebx # Add V86 SP
movl 0x30(%ebp),%edx # Load V86 flags
cmpb $0x9c,%al # PUSHF/PUSHFD?
je v86pushf # Yes
cmpb $0x9d,%al # POPF/POPFD?
je v86popf # Yes
cmpb $0xcd,%al # INT imm8?
je v86intn # Yes
cmpb $0xcf,%al # IRET/IRETD?
je v86iret # Yes
popl %ebx # Restore
popa # Restore
jmp except # Handle exception
v86mon.5: movl %edx,0x30(%ebp) # Save V86 flags
v86mon.6: popl %edx # V86 SS adjustment
subl %edx,%ebx # Save V86
movl %ebx,0x34(%ebp) # SP
v86mon.7: subl %edi,%esi # From linear
movl %esi,0x28(%ebp) # Save V86 IP
popa # Restore
leal 0x8(%esp,1),%esp # Discard int no, error
iret # To V86 mode
/*
* Emulate MOV reg,CRx.
*/
v86mov: movb 0x1(%esi),%bl # Fetch Mod R/M byte
testb $0x10,%bl # Read CR2 or CR3?
jnz v86mov.1 # Yes
movl %cr0,%eax # Read CR0
testb $0x20,%bl # Read CR4 instead?
jz v86mov.2 # No
movl %cr4,%eax # Read CR4
jmp v86mov.2
v86mov.1: movl %cr2,%eax # Read CR2
testb $0x08,%bl # Read CR3 instead?
jz v86mov.2 # No
movl %cr3,%eax # Read CR3
v86mov.2: andl $0x7,%ebx # Compute offset in
shl $2,%ebx # frame of destination
neg %ebx # register
movl %eax,0x1c(%ebp,%ebx,1) # Store CR to reg
incl %esi # Adjust IP
/*
* Return from emulating a 0x0f prefixed instruction
*/
v86preret: incl %esi # Adjust IP
jmp v86mon.7 # Finish up
/*
* Emulate WBINVD
*/
v86wbinvd: wbinvd # Write back and invalidate
# cache
jmp v86preret # Finish up
/*
* Emulate WRMSR
*/
v86wrmsr: movl 0x18(%ebp),%ecx # Get user's %ecx (MSR to write)
movl 0x14(%ebp),%edx # Load the value
movl 0x1c(%ebp),%eax # to write
wrmsr # Write MSR
jmp v86preret # Finish up
/*
* Emulate RDMSR
*/
v86rdmsr: movl 0x18(%ebp),%ecx # MSR to read
rdmsr # Read the MSR
movl %eax,0x1c(%ebp) # Return the value of
movl %edx,0x14(%ebp) # the MSR to the user
jmp v86preret # Finish up
/*
* Emulate CLI.
*/
v86cli: andb $~0x2,0x31(%ebp) # Clear IF
jmp v86mon.7 # Finish up
/*
* Emulate STI.
*/
v86sti: orb $0x2,0x31(%ebp) # Set IF
jmp v86mon.7 # Finish up
/*
* Emulate PUSHF/PUSHFD.
*/
v86pushf: subl %ecx,%ebx # Adjust SP
cmpb $0x4,%cl # 32-bit
je v86pushf.1 # Yes
data16 # 16-bit
v86pushf.1: movl %edx,(%ebx) # Save flags
jmp v86mon.6 # Finish up
/*
* Emulate IRET/IRETD.
*/
v86iret: movzwl (%ebx),%esi # Load V86 IP
movzwl 0x2(%ebx),%edi # Load V86 CS
leal 0x4(%ebx),%ebx # Adjust SP
movl %edi,0x2c(%ebp) # Save V86 CS
xorl %edi,%edi # No ESI adjustment
/*
* Emulate POPF/POPFD (and remainder of IRET/IRETD).
*/
v86popf: cmpb $0x4,%cl # 32-bit?
je v86popf.1 # Yes
movl %edx,%eax # Initialize
data16 # 16-bit
v86popf.1: movl (%ebx),%eax # Load flags
addl %ecx,%ebx # Adjust SP
andl $V86_FLG,%eax # Merge
andl $~V86_FLG,%edx # the
orl %eax,%edx # flags
jmp v86mon.5 # Finish up
/*
* trap int 15, function 87
* reads %es:%si from saved registers on stack to find a GDT containing
* source and destination locations
* reads count of words from saved %cx
* returns success by setting %ah to 0
*/
int15_87: pushl %esi # Save
pushl %edi # registers
movl 0x3C(%ebp),%edi # Load ES
movzwl 0x4(%ebp),%eax # Load user's SI
shll $0x4,%edi # EDI = (ES << 4) +
addl %eax,%edi # SI
movl 0x11(%edi),%eax # Read base of
movb 0x17(%edi),%al # GDT entry
ror $8,%eax # for source
xchgl %eax,%esi # into %esi
movl 0x19(%edi),%eax # Read base of
movb 0x1f(%edi),%al # GDT entry for
ror $8,%eax # destination
xchgl %eax,%edi # into %edi
pushl %ds # Make:
popl %es # es = ds
movzwl 0x18(%ebp),%ecx # Get user's CX
shll $0x1,%ecx # Convert count from words
rep # repeat...
movsb # perform copy.
popl %edi # Restore
popl %esi # registers
movb $0x0,0x1d(%ebp) # set ah = 0 to indicate
# success
andb $0xfe,%dl # clear CF
jmp v86mon.5 # Finish up
/*
* Reboot the machine by setting the reboot flag and exiting
@ -610,36 +382,7 @@ reboot: orb $0x1,btx_hdr+0x7 # Set the reboot flag
jmp exit # Terminate BTX and reboot
/*
* Emulate INT imm8... also make sure to check if it's int 15/87
*/
v86intn: lodsb # Get int no
cmpb $0x19,%al # is it int 19?
je reboot # yes, reboot the machine
cmpb $0x15,%al # is it int 15?
jne v86intn.1 # no, skip parse
cmpb $0x87,0x1d(%ebp) # is it the memcpy subfunction?
je int15_87 # yes
cmpw $0x4f53,0x1c(%ebp) # is it the delete key callout?
jne v86intn.1 # no, handle the int normally
movb BDA_KEYFLAGS,%ch # get the shift key state
andb $0xc,%ch # mask off just Ctrl and Alt
cmpb $0xc,%ch # are both Ctrl and Alt down?
je reboot # yes, reboot the machine
v86intn.1: subl %edi,%esi # From
shrl $0x4,%edi # linear
movw %dx,-0x2(%ebx) # Save flags
movw %di,-0x4(%ebx) # Save CS
leal -0x6(%ebx),%ebx # Adjust SP
movw %si,(%ebx) # Save IP
shll $0x2,%eax # Scale
movzwl (%eax),%esi # Load IP
movzwl 0x2(%eax),%edi # Load CS
movl %edi,0x2c(%ebp) # Save CS
xorl %edi,%edi # No ESI adjustment
andb $~0x1,%dh # Clear TF
jmp v86mon.5 # Finish up
/*
* Hardware interrupt jump table.
* Protected Mode Hardware interrupt jump table.
*/
intx20: push $0x8 # Int 0x20: IRQ0
jmp int_hw # V86 int 0x8
@ -673,127 +416,239 @@ intx20: push $0x8 # Int 0x20: IRQ0
jmp int_hw # V86 int 0x76
push $0x77 # Int 0x2f: IRQ15
jmp int_hw # V86 int 0x77
/*
* Reflect hardware interrupts.
* Invoke real mode interrupt/function call from user mode with arguments.
*/
int_hw: testb $0x2,0xe(%esp,1) # V86 mode?
jz intusr # No
pushl $SEL_SDATA # Address
popl %ds # data
xchgl %eax,(%esp,1) # Swap EAX, int no
pushl %ebp # Address
movl %esp,%ebp # stack frame
pushl %ebx # Save
shll $0x2,%eax # Get int
movl (%eax),%eax # vector
subl $0x6,0x14(%ebp) # Adjust V86 ESP
movzwl 0x18(%ebp),%ebx # V86 SS
shll $0x4,%ebx # * 0x10
addl 0x14(%ebp),%ebx # + V86 ESP
xchgw %ax,0x8(%ebp) # Swap V86 IP
rorl $0x10,%eax # Swap words
xchgw %ax,0xc(%ebp) # Swap V86 CS
roll $0x10,%eax # Swap words
movl %eax,(%ebx) # CS:IP for IRET
movl 0x10(%ebp),%eax # V86 flags
movw %ax,0x4(%ebx) # Flags for IRET
andb $~0x3,0x11(%ebp) # Clear IF, TF
popl %ebx # Restore
popl %ebp # saved
popl %eax # registers
iret # To V86 mode
intx31: pushl $-1 # Dummy int no for btx_v86
/*
* Invoke V86 interrupt from user mode, with arguments.
* Invoke real mode interrupt/function call from protected mode.
*
* We place a trampoline on the user stack that will return to rret_tramp
* which will reenter protected mode and then finally return to the user
* client.
*
* Kernel frame %esi points to: Real mode stack frame at MEM_ESPR:
*
* -0x00 user %ss -0x04 kernel %esp (with full frame)
* -0x04 user %esp -0x08 btx_v86 pointer
* -0x08 user %eflags -0x0c flags (only used if interrupt)
* -0x0c user %cs -0x10 real mode CS:IP return trampoline
* -0x10 user %eip -0x12 real mode flags
* -0x14 int no -0x16 real mode CS:IP (target)
* -0x18 %eax
* -0x1c %ecx
* -0x20 %edx
* -0x24 %ebx
* -0x28 %esp
* -0x2c %ebp
* -0x30 %esi
* -0x34 %edi
* -0x38 %gs
* -0x3c %fs
* -0x40 %ds
* -0x44 %es
*/
intx31: stc # Have btx_v86
pushl %eax # Missing int no
/*
* Invoke V86 interrupt from user mode.
*/
intusr: std # String ops dec
pushl %eax # Expand
pushl %eax # stack
pushl %eax # frame
pusha # Save
int_hw: cld # String ops inc
pusha # Save gp regs
pushl %gs # Save
movl %esp,%eax # seg regs
pushl %fs # and
pushl %ds # point
pushl %es # to them
pushl %fs # seg
pushl %ds # regs
pushl %es
push $SEL_SDATA # Set up
popl %ds # to
pushl %ds # address
popl %es # data
leal 0x44(%esp,1),%esi # Base of frame
movl -0x14(%esi),%eax # Get Int no
cmpl $-1,%eax # Hardware interrupt?
jne intusr.2 # Yes
/*
* v86 calls save the btx_v86 pointer on the real mode stack and read the
* address and flags from the btx_v86 structure.
*/
movl $MEM_USR,%ebx # User base
movl %ebx,%edx # address
jc intusr.1 # If btx_v86
xorl %edx,%edx # Control flags
xorl %ebp,%ebp # btx_v86 pointer
intusr.1: leal 0x50(%esp,1),%esi # Base of frame
pushl %esi # Save
addl -0x4(%esi),%ebx # User ESP
movl MEM_TSS+TSS_ESP1,%edi # Link stack pointer
leal -0x4(%edi),%edi # Adjust for push
xorl %ecx,%ecx # Zero
movb $0x5,%cl # Push exception
rep # frame on
movsl # link stack
xchgl %eax,%esi # Saved seg regs
movl 0x40(%esp,1),%eax # Get int no
testl %edx,%edx # Have btx_v86?
jz intusr.2 # No
movl (%ebx),%ebp # btx_v86 pointer
movb $0x4,%cl # Count
addl %ecx,%ebx # Adjust for pop
rep # Push saved seg regs
movsl # on link stack
addl %ebp,%edx # Flatten btx_v86 ptr
leal 0x14(%edx),%esi # Seg regs pointer
movl 0x4(%edx),%eax # Get int no/address
movzwl 0x2(%edx),%edx # Get control flags
intusr.2: movl %ebp,(%edi) # Push btx_v86 and
movl %edi,MEM_TSS+TSS_ESP1 # save link stack ptr
popl %edi # Base of frame
xchgl %eax,%ebp # Save intno/address
movl 0x48(%esp,1),%eax # Get flags
testb $0x2,%dl # Simulate CALLF?
jnz intusr.3 # Yes
decl %ebx # Push flags
decl %ebx # on V86
movw %ax,(%ebx) # stack
intusr.3: movb $0x4,%cl # Count
subl %ecx,%ebx # Push return address
movl $inthlt,(%ebx) # on V86 stack
rep # Copy seg regs to
movsl # exception frame
xchgl %eax,%ecx # Save flags
movl %ebx,%eax # User ESP
subl $V86_STK,%eax # Less bytes
ja intusr.4 # to
xorl %eax,%eax # keep
intusr.4: shrl $0x4,%eax # Gives segment
stosl # Set SS
shll $0x4,%eax # To bytes
xchgl %eax,%ebx # Swap
subl %ebx,%eax # Gives offset
stosl # Set ESP
xchgl %eax,%ecx # Get flags
btsl $0x11,%eax # Set VM
andb $~0x1,%ah # Clear TF
stosl # Set EFL
xchgl %eax,%ebp # Get int no/address
testb $0x1,%dl # Address?
jnz intusr.5 # Yes
movl %edx,MEM_ESPR-0x08 # Save btx_v86 ptr
movl -0x08(%esi),%ebx # Pass user flags to
movw %bx,MEM_ESPR-0x12 # real mode target
movl V86_ADDR(%edx),%eax # Get int no/address
movl V86_CTL(%edx),%edx # Get control flags
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 and ensure interrupts
* are disabled when the interrupt handler is invoked.
*/
intusr.2: 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 -0x08(%esi),%ebx # Pass user flags with
andl $~0x200,%ebx # interrupts disabled
movw %bx,MEM_ESPR-0x12 # to real mode target
/*
* %eax now holds either the interrupt number or segment:offset of function.
* %edx now holds the V86F_* flags.
*/
intusr.3: testl $V86F_ADDR,%edx # Segment:offset?
jnz intusr.4 # Yes
shll $0x2,%eax # Scale
movl (%eax),%eax # Load int vector
intusr.5: movl %eax,%ecx # Save
shrl $0x10,%eax # Gives segment
stosl # Set CS
movw %cx,%ax # Restore
stosl # Set EIP
leal 0x10(%esp,1),%esp # Discard seg regs
popa # Restore
iret # To V86 mode
jmp intusr.5 # Skip CALLF test
intusr.4: testl $V86F_CALLF,%edx # Far call?
jnz intusr.5 # Ok
movl %edx,0x30(%esp,1) # Place VM86 flags in int no
movl $badvm86,%esi # Display bad
call putstr # VM86 call
popl %es # Restore
popl %ds # seg
popl %fs # regs
popl %gs
popal # Restore gp regs
jmp ex_noc # Panic
/*
* If this is a v86 call, copy the seg regs out of the btx_v86 structure.
*/
intusr.5: movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr
jecxz intusr.6 # Skip for hardware ints
leal -0x44(%esi),%edi # %edi => kernel stack seg regs
pushl %esi # Save
leal V86_ES(%ecx),%esi # %esi => btx_v86 seg regs
movl $4,%ecx # Copy seg regs
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
movl %ebx,MEM_ESPR-0x0c # mode return trampoline
movl $rret_tramp,%ebx # Set return trampoline
movl %ebx,MEM_ESPR-0x10 # CS:IP
movl %eax,MEM_ESPR-0x16 # Real mode target CS:IP
ljmpw $SEL_RCODE,$intusr.7 # Change to 16-bit segment
.code16
intusr.7: movl %cr0,%eax # Leave
dec %al # protected
movl %eax,%cr0 # mode
ljmpw $0x0,$intusr.8
intusr.8: xorw %ax,%ax # Reset %ds
movw %ax,%ds # and
movw %ax,%ss # %ss
lidt ivtdesc # Set IVT
popl %es # Restore
popl %ds # seg
popl %fs # regs
popl %gs
popal # Restore gp regs
movw $MEM_ESPR-0x16,%sp # Switch to real mode stack
iret # Call target routine
/*
* For the return to real mode we setup a stack frame like this on the real
* mode stack. Note that callf calls won't pop off the flags, but we just
* ignore that by repositioning %sp to be just above the btx_v86 pointer
* so it is aligned. The stack is relative to MEM_ESPR.
*
* -0x04 kernel %esp
* -0x08 btx_v86
* -0x0c %eax
* -0x10 %ecx
* -0x14 %edx
* -0x18 %ebx
* -0x1c %esp
* -0x20 %ebp
* -0x24 %esi
* -0x28 %edi
* -0x2c %gs
* -0x30 %fs
* -0x34 %ds
* -0x38 %es
* -0x3c %eflags
*/
rret_tramp: movw $MEM_ESPR-0x08,%sp # Reset stack pointer
pushal # Save gp regs
pushl %gs # Save
pushl %fs # seg
pushl %ds # regs
pushl %es
pushfl # Save %eflags
cli # Disable interrupts
std # String ops dec
xorw %ax,%ax # Reset seg
movw %ax,%ds # regs
movw %ax,%es # (%ss is already 0)
lidt idtdesc # Set IDT
lgdt gdtdesc # Set GDT
mov %cr0,%eax # Switch to protected
inc %ax # mode
mov %eax,%cr0 #
ljmp $SEL_SCODE,$rret_tramp.1 # To 32-bit code
.code32
rret_tramp.1: xorl %ecx,%ecx # Zero
movb $SEL_SDATA,%cl # Setup
movw %cx,%ss # 32-bit
movw %cx,%ds # seg
movw %cx,%es # regs
movl MEM_ESPR-0x04,%esp # Switch to kernel stack
leal 0x44(%esp,1),%esi # Base of frame
andb $~0x2,tss_desc+0x5 # Clear TSS busy
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.
*
* 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.
*/
leal -0x18(%esi),%edi # Kernel stack GP regs
pushl %esi # Save
movl $MEM_ESPR-0x0c,%esi # Real mode stack GP regs
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.
*/
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
rep # from real mode stack
movsl # to btx_v86
popl %esi # Restore
movl V86_CTL(%edx),%edx # Read V86 control flags
testl $V86F_FLAGS,%edx # User wants flags?
jz rret_tramp.3 # No
movl MEM_ESPR-0x3c,%eax # Read real mode flags
movw %ax,-0x08(%esi) # Update user flags (low 16)
/*
* Return to the user task
*/
rret_tramp.3: popl %es # Restore
popl %ds # seg
popl %fs # regs
popl %gs
popal # Restore gp regs
addl $4,%esp # Discard int no
iret # Return to user mode
/*
* System Call.
*/
@ -1035,6 +890,61 @@ putchr.4: movw %dx,(%ebx) # Update position
ret # To caller
#endif
.code16
/*
* Real Mode Hardware interrupt jump table.
*/
intr20: push $0x8 # Int 0x20: IRQ0
jmp int_hwr # V86 int 0x8
push $0x9 # Int 0x21: IRQ1
jmp int_hwr # V86 int 0x9
push $0xa # Int 0x22: IRQ2
jmp int_hwr # V86 int 0xa
push $0xb # Int 0x23: IRQ3
jmp int_hwr # V86 int 0xb
push $0xc # Int 0x24: IRQ4
jmp int_hwr # V86 int 0xc
push $0xd # Int 0x25: IRQ5
jmp int_hwr # V86 int 0xd
push $0xe # Int 0x26: IRQ6
jmp int_hwr # V86 int 0xe
push $0xf # Int 0x27: IRQ7
jmp int_hwr # V86 int 0xf
push $0x70 # Int 0x28: IRQ8
jmp int_hwr # V86 int 0x70
push $0x71 # Int 0x29: IRQ9
jmp int_hwr # V86 int 0x71
push $0x72 # Int 0x2a: IRQ10
jmp int_hwr # V86 int 0x72
push $0x73 # Int 0x2b: IRQ11
jmp int_hwr # V86 int 0x73
push $0x74 # Int 0x2c: IRQ12
jmp int_hwr # V86 int 0x74
push $0x75 # Int 0x2d: IRQ13
jmp int_hwr # V86 int 0x75
push $0x76 # Int 0x2e: IRQ14
jmp int_hwr # V86 int 0x76
push $0x77 # Int 0x2f: IRQ15
jmp int_hwr # V86 int 0x77
/*
* Reflect hardware interrupts in real mode.
*/
int_hwr: push %ax # Save
push %ds # Save
push %bp # Save
mov %sp,%bp # Address stack frame
xchg %bx,6(%bp) # Swap BX, int no
xor %ax,%ax # Set %ds:%bx to
shl $2,%bx # point to
mov %ax,%ds # IDT entry
mov (%bx),%ax # Load IP
mov 2(%bx),%bx # Load CS
xchg %ax,4(%bp) # Swap saved %ax,%bx with
xchg %bx,6(%bp) # CS:IP of handler
pop %bp # Restore
pop %ds # Restore
lret # Jump to handler
.p2align 4
/*
* Global descriptor table.
@ -1046,7 +956,7 @@ gdt: .word 0x0,0x0,0x0,0x0 # Null entry
.word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
.word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE
.word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA
.word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS
tss_desc: .word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS
gdt.1:
/*
* Pseudo-descriptors.
@ -1114,6 +1024,11 @@ dmpfmt: .byte '\n' # "\n"
.ascii "ss:esp" # "ss:esp="
.byte 0x80|DMP_MEM|DMP_EOL,0x0 # "00 00 ... 00 00\n"
.asciz "BTX halted\n" # End
/*
* Bad VM86 call panic
*/
badvm86: .asciz "Invalid VM86 Request\n"
/*
* End of BTX memory.
*/