706 lines
17 KiB
NASM
706 lines
17 KiB
NASM
;++
|
||
;
|
||
; Copyright (c) 1991 Microsoft Corporation
|
||
;
|
||
; Module Name:
|
||
;
|
||
; xxbiosa.asm
|
||
;
|
||
; Abstract:
|
||
;
|
||
; This implements the necessary code to put the processor into
|
||
; V86 mode, make a BIOS call, and return safely to protected mode.
|
||
;
|
||
; Author:
|
||
;
|
||
; John Vert (jvert) 29-Oct-1991
|
||
;
|
||
; Environment:
|
||
;
|
||
; Kernel mode
|
||
;
|
||
; Notes:
|
||
;
|
||
; This module is intended for use in panic situations, such as a bugcheck.
|
||
; As a result, we cannot rely on the integrity of the system so we must
|
||
; handle everything ourselves. Notably, we must map our own memory by
|
||
; adding our own page tables and PTEs.
|
||
;
|
||
; We also cannot call KeBugCheck when we notice something has gone wrong.
|
||
;
|
||
; Revision History:
|
||
;
|
||
;--
|
||
.386p
|
||
.xlist
|
||
include hal386.inc
|
||
include callconv.inc ; calling convention macros
|
||
include i386\kimacro.inc
|
||
.list
|
||
|
||
extrn _DbgPrint:proc
|
||
EXTRNP _DbgBreakPoint,0,IMPORT
|
||
EXTRNP Kei386EoiHelper,0,IMPORT
|
||
|
||
public _HalpRealModeStart
|
||
public _HalpRealModeEnd
|
||
;
|
||
; 32-bit override
|
||
;
|
||
OVERRIDE equ 66h
|
||
|
||
;
|
||
; Reginfo structure
|
||
;
|
||
|
||
RegInfo struc
|
||
RiSegSs dd 0
|
||
RiEsp dd 0
|
||
RiEFlags dd 0
|
||
RiSegCs dd 0
|
||
RiEip dd 0
|
||
RiTrapFrame dd 0
|
||
RiCsLimit dd 0
|
||
RiCsBase dd 0
|
||
RiCsFlags dd 0
|
||
RiSsLimit dd 0
|
||
RiSsBase dd 0
|
||
RiSsFlags dd 0
|
||
RiPrefixFlags dd 0
|
||
RegInfo ends
|
||
REGINFOSIZE EQU 52
|
||
|
||
INT_NN_OPCODE EQU 0CDH
|
||
|
||
page ,132
|
||
_DATA SEGMENT DWORD PUBLIC 'DATA'
|
||
|
||
;
|
||
; In order to return to the calling function after we've trapped out of
|
||
; V86 mode, we save our ESP value here.
|
||
;
|
||
HalpSavedEsp dd 0
|
||
|
||
_DATA ENDS
|
||
|
||
|
||
_TEXT SEGMENT DWORD PUBLIC 'CODE'
|
||
ASSUME DS:NOTHING, ES:NOTHING, SS:FLAT, FS:NOTHING, GS:NOTHING
|
||
|
||
if DBG
|
||
page ,132
|
||
subttl "Processing Exception occurred in ABIOS code"
|
||
;++
|
||
; VOID
|
||
; KiAbiosException (
|
||
; VOID
|
||
; )
|
||
;
|
||
; Routine Description:
|
||
;
|
||
; This routine is called after an exception being detected
|
||
; in ABIOS ROM code. The system will switch 16 stack to 32 bit
|
||
; stack and bugcheck.
|
||
;
|
||
; N.B. In fact this routine is simply used to resolve a reference
|
||
; to KiAbiosException routine in the Kimacro.inc ENTER_TRAP
|
||
; macro.
|
||
;
|
||
;
|
||
; Arguments:
|
||
;
|
||
; None.
|
||
;
|
||
; Return value:
|
||
;
|
||
; system stopped.
|
||
;
|
||
;--
|
||
public _KiAbiosException
|
||
_KiAbiosException proc
|
||
_Ki16BitStackException:
|
||
ret
|
||
|
||
_KiAbiosException endp
|
||
|
||
endif
|
||
|
||
|
||
;++
|
||
; ULONG
|
||
; HalpBorrowTss (
|
||
; VOID
|
||
; )
|
||
;
|
||
; Routine Description:
|
||
;
|
||
; This routine checks if the current TSS has IO MAP space.
|
||
; if yes, it simply returns. Otherwise, it switches to use
|
||
; the regular TSS.
|
||
;
|
||
; Arguments:
|
||
;
|
||
; None.
|
||
;
|
||
; Return value:
|
||
;
|
||
; Return original TSS selector if the regular Tss is borrowed by us.
|
||
;
|
||
;--
|
||
cPublicProc _HalpBorrowTss, 0
|
||
cPublicFpo 0, 0
|
||
|
||
xor eax, eax
|
||
str ax
|
||
mov edx, PCR[PcGdt]
|
||
add edx, eax ; (edx)->Gdt Entry of current
|
||
; TSS
|
||
xor ecx, ecx
|
||
mov cl, [edx].KgdtLimitHi
|
||
shl ecx, 16
|
||
mov cx, [edx].KgdtLimitLow ; (ecx) = TSS limit
|
||
cmp ecx, 2000H ; Is Io map space available?
|
||
ja short Hbt99 ; if a, yes, return
|
||
|
||
sub edx, eax ; (edx)->GDT table
|
||
mov ch, [edx+KGDT_TSS+KgdtBaseHi]
|
||
mov cl, [edx+KGDT_TSS+KgdtBaseMid]
|
||
shl ecx, 16
|
||
mov cx, [edx+KGDT_TSS+KgdtBaseLow]
|
||
mov PCR[PcTss], ecx
|
||
mov ecx, KGDT_TSS ; switch to use regular TSS
|
||
mov byte ptr [edx+KGDT_TSS+5], 089h ; 32bit, dpl=0, present, TSS32,
|
||
; not busy.
|
||
ltr cx
|
||
stdRET _HalpBorrowTss ; (eax) = Original TSS sel
|
||
|
||
Hbt99:
|
||
xor eax, eax ; No TSS swapped
|
||
stdRET _HalpBorrowTss
|
||
|
||
stdENDP _HalpBorrowTss
|
||
|
||
|
||
;++
|
||
; VOID
|
||
; HalpReturnTss (
|
||
; ULONG TssSelector
|
||
; )
|
||
;
|
||
; Routine Description:
|
||
;
|
||
; This routine switches the current TSS from regular TSS back to
|
||
; the panic TSS (NMI TSS or Double fault TSS).
|
||
;
|
||
; Arguments:
|
||
;
|
||
; TssSelector - the TSS selector to return to.
|
||
;
|
||
; Return value:
|
||
;
|
||
; None.
|
||
;
|
||
;--
|
||
cPublicProc _HalpReturnTss, 1
|
||
cPublicFpo 1, 0
|
||
|
||
mov edx, PCR[PcGdt] ; (edx)-> Gdt table
|
||
mov eax, [esp + 4]
|
||
and eax, 0FFFFh ; (eax)= New TSS sel
|
||
add edx, eax ; (edx)->Gdt Entry of new TSS
|
||
|
||
mov ch, [edx+KgdtBaseHi]
|
||
mov cl, [edx+KgdtBaseMid]
|
||
shl ecx, 16
|
||
mov cx, [edx+KgdtBaseLow]
|
||
mov PCR[PcTss], ecx
|
||
mov byte ptr [edx+5], 089h ; 32bit, dpl=0, present, TSS32,
|
||
ltr ax
|
||
stdRET _HalpReturnTss ; return and clear stack
|
||
|
||
stdENDP _HalpReturnTss
|
||
|
||
;++
|
||
;
|
||
; VOID
|
||
; HalpBiosCall
|
||
; VOID
|
||
; )
|
||
;
|
||
; Routine Description:
|
||
;
|
||
; This routine completes the transition to real mode, calls BIOS, and
|
||
; returns to protected mode.
|
||
;
|
||
; Arguments:
|
||
;
|
||
; None.
|
||
;
|
||
; Return Value:
|
||
;
|
||
; None.
|
||
;
|
||
;--
|
||
;;ALIGN 4096
|
||
cPublicProc _HalpBiosCall ,0
|
||
|
||
push ebp
|
||
mov ebp, esp
|
||
pushfd
|
||
push edi
|
||
push esi
|
||
push ebx
|
||
push ds
|
||
push es
|
||
push fs
|
||
push gs
|
||
push offset FLAT:HbcProtMode ; address where we will start
|
||
; protected mode again once
|
||
; V86 has completed.
|
||
mov HalpSavedEsp, esp
|
||
|
||
mov eax, cr0 ; make sure alignment
|
||
and eax, not CR0_AM ; checks are disabled
|
||
mov cr0, eax
|
||
|
||
;
|
||
; Create space for the V86 trap frame and update the ESP0 value in the TSS
|
||
; to use this space. We will set this up just below our current stack pointer.
|
||
; The stuff we push on the stack after we set ESP0 is irrelevant once we
|
||
; make it to V86 mode, so it's ok to blast it.
|
||
;
|
||
mov esi, fs:PcTss ; (esi) -> TSS
|
||
mov eax, esp
|
||
sub eax, NPX_FRAME_LENGTH ; skip FP save area
|
||
mov [esi]+TssEsp0, eax
|
||
|
||
push dword ptr 0h ; V86 GS
|
||
push dword ptr 0h ; V86 FS
|
||
push dword ptr 0h ; V86 DS
|
||
push dword ptr 0h ; V86 ES
|
||
push dword ptr 2000h ; V86 SS
|
||
|
||
;
|
||
; We calculate the V86 sp by adding the difference between the linear address
|
||
; of the V86 ip (HbcReal) and the linear address of the V86 sp (HbcV86Stack)
|
||
; to the offset of the V86 ip (HbcReal & 0xfff).
|
||
;
|
||
|
||
mov eax, offset FLAT:HbcV86Stack-4
|
||
sub eax, offset FLAT:HbcReal
|
||
mov edx, offset HbcReal
|
||
and edx, 0fffh
|
||
add eax, edx
|
||
push eax ; V86 esp
|
||
|
||
pushfd
|
||
or dword ptr [esp], EFLAGS_V86_MASK; V86 eflags
|
||
or [esp], 03000h ; Give IOPL3
|
||
push dword ptr 2000h ; V86 CS
|
||
mov eax, offset HbcReal
|
||
and eax, 0fffh
|
||
|
||
push edx ; V86-mode EIP is offset
|
||
; into CS.
|
||
iretd
|
||
|
||
_HalpRealModeStart label byte
|
||
|
||
HbcReal:
|
||
db OVERRIDE ; make mov 32-bits
|
||
mov eax, 12h ; 640x480x16 colors
|
||
int 10h
|
||
|
||
db 0c4h, 0c4h ; BOP to indicate V86 mode is done.
|
||
|
||
;
|
||
; V86-mode stack
|
||
;
|
||
align 4
|
||
db 2048 dup(0)
|
||
HbcV86Stack:
|
||
|
||
_HalpRealModeEnd label byte
|
||
|
||
HbcProtMode:
|
||
;
|
||
; We are back from V86 mode, so restore everything we saved and we are done.
|
||
;
|
||
pop gs
|
||
pop fs
|
||
pop es
|
||
pop ds
|
||
pop ebx
|
||
pop esi
|
||
pop edi
|
||
popfd
|
||
pop ebp
|
||
stdRET _HalpBiosCall
|
||
|
||
public _HalpBiosCallEnd
|
||
_HalpBiosCallEnd label byte
|
||
|
||
|
||
|
||
_HalpBiosCall endp
|
||
|
||
|
||
subttl "HAL General Protection Fault"
|
||
;++
|
||
;
|
||
; Routine Description:
|
||
;
|
||
; Handle General protection fault.
|
||
;
|
||
; This fault handler is used by the HAL for V86 mode faults only.
|
||
; It should NEVER be used except when running in V86 mode. The HAL
|
||
; replaces the general-purpose KiTrap0D handler entry in the IDT with
|
||
; this routine. This allows us to emulate V86-mode instructions which
|
||
; cause a fault. After we return from V86 mode, we can restore the
|
||
; KiTrap0D handler in the IDT.
|
||
;
|
||
; Arguments:
|
||
;
|
||
; At entry, the saved CS:EIP point to the faulting instruction
|
||
; Error code (whose value depends on detected condition) is provided.
|
||
;
|
||
; Return value:
|
||
;
|
||
; None
|
||
;
|
||
;--
|
||
ASSUME DS:FLAT, SS:NOTHING, ES:FLAT
|
||
|
||
ENTER_DR_ASSIST Htd_a, Htd_t, NoAbiosAssist
|
||
cPublicProc _HalpTrap0D ,0
|
||
|
||
ENTER_TRAP Htd_a, Htd_t
|
||
|
||
;
|
||
; Did the trap occur in V86 mode? If not, something is completely messed up.
|
||
;
|
||
test dword ptr [ebp]+TsEFlags,00020000H
|
||
jnz Ht0d10
|
||
|
||
;
|
||
; The trap was not from V86 mode, so something is very wrong. We cannot
|
||
; BugCheck, since we are probably already in a BugCheck. So just stop.
|
||
;
|
||
|
||
if DBG
|
||
_TEXT segment
|
||
MsgBadHalTrap db 'HAL: Trap0D while not in V86 mode',0ah,0dh,0
|
||
_TEXT ends
|
||
|
||
push offset FLAT:MsgBadHalTrap
|
||
call _DbgPrint
|
||
add esp,4
|
||
stdCall _DbgBreakPoint
|
||
endif
|
||
;
|
||
; We can't bugcheck, so just commit suicide. Maybe we should reboot?
|
||
;
|
||
jmp $
|
||
|
||
Ht0d10:
|
||
stdCall HalpDispatchV86Opcode
|
||
SPURIOUS_INTERRUPT_EXIT
|
||
stdENDP _HalpTrap0d
|
||
|
||
subttl "HAL Invalid Opcode Fault"
|
||
;++
|
||
;
|
||
; Routine Description:
|
||
;
|
||
; Handle invalid opcode fault
|
||
;
|
||
; This fault handler is used by the HAL to indicate when V86 mode
|
||
; execution is finished. The V86 code attempts to execute an invalid
|
||
; instruction (BOP) when it is done, and that brings us here.
|
||
; This routine just removes the trap frame from the stack and does
|
||
; a RET. Note that this assumes that ESP0 in the TSS has been set
|
||
; up to point to the top of the stack that we want to be running on
|
||
; when the V86 call has completed.
|
||
;
|
||
; This should NEVER be used except when running in V86 mode. The HAL
|
||
; replaces the general-purpose KiTrap06 handler entry in the IDT with
|
||
; this routine. It also sets up ESP0 in the TSS appropriately. After
|
||
; the V86 call has completed, it restores these to their previous values.
|
||
;
|
||
; Arguments:
|
||
;
|
||
; At entry, the saved CS:EIP point to the faulting instruction
|
||
; Error code (whose value depends on detected condition) is provided.
|
||
;
|
||
; Return value:
|
||
;
|
||
; None
|
||
;
|
||
;--
|
||
ASSUME DS:FLAT, SS:NOTHING, ES:FLAT
|
||
|
||
cPublicProc _HalpTrap06 ,0
|
||
mov eax,KGDT_R3_DATA OR RPL_MASK
|
||
mov ds,ax
|
||
mov es,ax
|
||
mov esp, HalpSavedEsp
|
||
ret
|
||
|
||
stdENDP _HalpTrap06
|
||
|
||
subttl "Instruction Emulation Dispatcher"
|
||
;++
|
||
;
|
||
; Routine Description:
|
||
;
|
||
; This routine dispatches to the opcode specific emulation routine,
|
||
; based on the first byte of the opcode. Two byte opcodes, and prefixes
|
||
; result in another level of dispatching, from the handling routine.
|
||
;
|
||
; This code blatantly stolen from ke\i386\instemul.asm
|
||
;
|
||
; Arguments:
|
||
;
|
||
; ebp = pointer to trap frame
|
||
;
|
||
; Returns:
|
||
;
|
||
; Nothing
|
||
;
|
||
|
||
cPublicProc HalpDispatchV86Opcode ,0
|
||
|
||
RI equ [ebp - REGINFOSIZE]
|
||
push ebp
|
||
mov ebp,esp
|
||
sub esp,REGINFOSIZE
|
||
push esi
|
||
push edi
|
||
|
||
; Initialize RegInfo
|
||
|
||
mov esi,[ebp]
|
||
mov RI.RiTrapFrame,esi
|
||
movzx eax,word ptr [esi].TsHardwareSegSs
|
||
mov RI.RiSegSs,eax
|
||
mov eax,[esi].TsHardwareEsp
|
||
mov RI.RiEsp,eax
|
||
mov eax,[esi].TsEFlags
|
||
mov RI.RiEFlags,eax
|
||
movzx eax,word ptr [esi].TsSegCs
|
||
mov RI.RiSegCs,eax
|
||
mov eax,[esi].TsEip
|
||
mov RI.RiEip,eax
|
||
|
||
xor eax,eax
|
||
mov RI.RiPrefixFlags,eax
|
||
lea esi,RI
|
||
|
||
;
|
||
; Convert CS to a linear address
|
||
;
|
||
|
||
mov eax,[esi].RiSegCs
|
||
shl eax,4
|
||
mov [esi].RiCsBase,eax
|
||
mov [esi].RiCsLimit,0FFFFh
|
||
mov [esi].RiCsFlags,0
|
||
|
||
mov edi,RI.RiEip
|
||
cmp edi,RI.RiCsLimit
|
||
ja doerr
|
||
|
||
add edi,RI.RiCsBase
|
||
mov dl, [edi] ; get faulting opcode
|
||
cmp dl, INT_NN_OPCODE
|
||
je short @f
|
||
|
||
stdCall HalpOpcodeInvalid
|
||
jmp short doerr
|
||
|
||
@@:
|
||
stdCall HalpOpcodeINTnn
|
||
test eax,0FFFFh
|
||
jz do20
|
||
|
||
mov edi,RI.RiTrapFrame
|
||
mov eax,RI.RiEip ; advance eip
|
||
mov [edi].TsEip,eax
|
||
mov eax,1
|
||
do20: pop edi
|
||
pop esi
|
||
mov esp,ebp
|
||
pop ebp
|
||
ret
|
||
|
||
doerr: xor eax,eax
|
||
jmp do20
|
||
stdENDP HalpDispatchV86Opcode
|
||
|
||
page ,132
|
||
subttl "Invalid Opcode Handler"
|
||
;++
|
||
;
|
||
; Routine Description:
|
||
;
|
||
; This routine handles invalid opcodes. It prints the invalid
|
||
; opcode message, and breaks into the kernel debugger.
|
||
;
|
||
; Arguments:
|
||
;
|
||
; esi = address of reg info
|
||
; edx = opcode
|
||
;
|
||
; Returns:
|
||
;
|
||
; nothing
|
||
;
|
||
|
||
_TEXT segment
|
||
HalpMsgInvalidOpcode db 'HAL: An invalid V86 opcode was encountered at '
|
||
db 'address %x:%x',0ah, 0dh, 0
|
||
_TEXT ends
|
||
|
||
cPublicProc HalpOpcodeInvalid ,0
|
||
|
||
push [esi].RiEip
|
||
push [esi].RiSegCs
|
||
push offset FLAT:HalpMsgInvalidOpcode
|
||
call _DbgPrint ; display invalid opcode message
|
||
add esp,12
|
||
int 3
|
||
xor eax,eax
|
||
stdRET HalpOpcodeInvalid
|
||
|
||
stdENDP HalpOpcodeInvalid
|
||
|
||
subttl "INTnn Opcode Handler"
|
||
;++
|
||
;
|
||
; Routine Description:
|
||
;
|
||
; This routine emulates an INTnn opcode. It retrieves the handler
|
||
; from the IVT, pushes the current cs:ip and flags on the stack,
|
||
; and dispatches to the handler.
|
||
;
|
||
; Arguments:
|
||
;
|
||
; esi = address of reg info
|
||
; edx = opcode
|
||
;
|
||
; Returns:
|
||
;
|
||
; Current CS:IP on user stack
|
||
; RiCs:RiEip -> handler from IVT
|
||
;
|
||
|
||
cPublicProc HalpOpcodeINTnn ,0
|
||
|
||
push ebp
|
||
push edi
|
||
push ebx
|
||
|
||
;
|
||
; Convert SS to linear address
|
||
;
|
||
mov eax,[esi].RiSegSs
|
||
shl eax,4
|
||
mov [esi].RiSsBase,eax
|
||
mov [esi].RiSsLimit,0FFFFh
|
||
mov [esi].RiSsFlags,0
|
||
|
||
inc [esi].RiEip ; point to int #
|
||
mov edi,[esi].RiEip
|
||
cmp edi,[esi].RiCsLimit
|
||
ja oinerr
|
||
|
||
add edi,[esi].RiCsBase
|
||
movzx ecx,byte ptr [edi] ; get int #
|
||
inc [esi].RiEip ; inc past end of instruction
|
||
stdCall HalpPushInt
|
||
test eax,0FFFFh
|
||
jz oin20 ; error!
|
||
;
|
||
; Note: Some sort of check for BOP should go here, or in push int.
|
||
;
|
||
|
||
mov ebp,[esi].RiTrapFrame
|
||
mov eax,[esi].RiSegSs
|
||
mov [ebp].TsHardwareSegSs,eax
|
||
mov eax,[esi].RiEsp
|
||
mov [ebp].TsHardwareEsp,eax
|
||
mov eax,[esi].RiSegCs
|
||
mov [ebp].TsSegCs,eax
|
||
mov eax,[esi].RiEFlags
|
||
mov [ebp].TsEFlags,eax
|
||
mov eax,1
|
||
oin20: pop ebx
|
||
pop edi
|
||
pop ebp
|
||
stdRET HalpOpcodeINTnn
|
||
|
||
oinerr: xor eax,eax
|
||
jmp oin20
|
||
|
||
stdENDP HalpOpcodeINTnn
|
||
|
||
page ,132
|
||
subttl "Push Interrupt frame on user stack"
|
||
;++
|
||
;
|
||
; Routine Description:
|
||
;
|
||
; This routine pushes an interrupt frame on the user stack
|
||
;
|
||
; Arguments:
|
||
;
|
||
; ecx = interrupt #
|
||
; esi = address of reg info
|
||
; Returns:
|
||
;
|
||
; interrupt frame pushed on stack
|
||
; reg info updated
|
||
;
|
||
cPublicProc HalpPushInt ,0
|
||
push ebx
|
||
|
||
mov edx,[esi].RiEsp
|
||
mov ebx,[esi].RiSsBase
|
||
and edx,0FFFFh ; only use a 16 bit sp
|
||
sub dx,2
|
||
mov ax,word ptr [esi].RiEFlags
|
||
mov [ebx+edx],ax ; push flags
|
||
sub dx,2
|
||
mov ax,word ptr [esi].RiSegCs
|
||
mov [ebx+edx],ax ; push cs
|
||
sub dx,2
|
||
mov ax,word ptr [esi].RiEip
|
||
mov [ebx+edx],ax ; push ip
|
||
mov eax,[ecx*4] ; get new cs:ip value
|
||
push eax
|
||
movzx eax,ax
|
||
mov [esi].RiEip,eax
|
||
pop eax
|
||
shr eax,16
|
||
mov [esi].RiSegCs,eax
|
||
mov word ptr [esi].RiEsp,dx
|
||
|
||
;
|
||
; Convert CS to a linear address
|
||
;
|
||
|
||
mov eax,[esi].RiSegCs
|
||
shl eax,4
|
||
mov [esi].RiCsBase,eax
|
||
mov [esi].RiCsLimit,0FFFFh
|
||
mov [esi].RiCsFlags,0
|
||
|
||
mov eax,1 ; success
|
||
pi80: pop ebx
|
||
stdRET HalpPushInt
|
||
stdENDP HalpPushInt
|
||
|
||
|
||
_TEXT ends
|
||
end
|