You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
705 lines
18 KiB
705 lines
18 KiB
;++
|
|
;
|
|
; 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
|