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.
6774 lines
209 KiB
6774 lines
209 KiB
title "Trap Processing"
|
|
;++
|
|
;
|
|
; Copyright (c) 1989-2000 Microsoft Corporation
|
|
;
|
|
; Module Name:
|
|
;
|
|
; trap.asm
|
|
;
|
|
; Abstract:
|
|
;
|
|
; This module implements the code necessary to field and process i386
|
|
; trap conditions.
|
|
;
|
|
; Author:
|
|
;
|
|
; Shie-Lin Tzong (shielint) 4-Feb-1990
|
|
;
|
|
; Environment:
|
|
;
|
|
; Kernel mode only.
|
|
;
|
|
; Revision History:
|
|
;
|
|
;--
|
|
.586p
|
|
.xlist
|
|
KERNELONLY equ 1
|
|
include ks386.inc
|
|
include callconv.inc ; calling convention macros
|
|
include i386\kimacro.inc
|
|
include mac386.inc
|
|
include i386\mi.inc
|
|
include ..\..\vdm\i386\vdm.inc
|
|
include vdmtib.inc
|
|
include fastsys.inc
|
|
include irqli386.inc
|
|
.list
|
|
|
|
FAST_BOP equ 1
|
|
|
|
|
|
page ,132
|
|
extrn _KeI386FxsrPresent:BYTE
|
|
extrn ExpInterlockedPopEntrySListFault:DWORD
|
|
extrn ExpInterlockedPopEntrySListResume:DWORD
|
|
extrn _KeGdiFlushUserBatch:DWORD
|
|
extrn _KeTickCount:DWORD
|
|
extrn _ExpTickCountMultiplier:DWORD
|
|
extrn _KiDoubleFaultTSS:dword
|
|
extrn _KeErrorMask:dword
|
|
extrn _KiNMITSS:dword
|
|
extrn _KeServiceDescriptorTable:dword
|
|
if DBG
|
|
extrn _MmInjectUserInpageErrors:dword
|
|
EXTRNP _MmTrimProcessMemory,1
|
|
endif
|
|
extrn _KiHardwareTrigger:dword
|
|
extrn _KiBugCheckData:dword
|
|
extrn _KdpOweBreakpoint:byte
|
|
extrn Ki386BiosCallReturnAddress:near
|
|
extrn _PoHiberInProgress:byte
|
|
extrn _KiI386PentiumLockErrataPresent:BYTE
|
|
EXTRNP _KiDeliverApc,3
|
|
EXTRNP _PsConvertToGuiThread,0
|
|
EXTRNP _ZwUnmapViewOfSection,2
|
|
|
|
EXTRNP _KiHandleNmi,0
|
|
EXTRNP _HalHandleNMI,1,IMPORT
|
|
EXTRNP _HalBeginSystemInterrupt,3,IMPORT
|
|
EXTRNP _HalEndSystemInterrupt,2,IMPORT
|
|
EXTRNP _KiDispatchException,5
|
|
EXTRNP _PsWatchWorkingSet,3
|
|
extrn _PsWatchEnabled:byte
|
|
EXTRNP _MmAccessFault,4
|
|
extrn _MmUserProbeAddress:DWORD
|
|
EXTRNP _KeBugCheck,1
|
|
EXTRNP _KeBugCheckEx,5
|
|
EXTRNP _KeTestAlertThread,1
|
|
EXTRNP _KiContinue,3
|
|
EXTRNP _KiRaiseException,5
|
|
EXTRNP _VdmDispatchOpcodeV86_try,1
|
|
EXTRNP _VdmDispatchOpcode_try,1
|
|
EXTRNP _VdmDispatchPageFault,3
|
|
EXTRNP _Ki386VdmReflectException,1
|
|
EXTRNP _Ki386VdmSegmentNotPresent,0
|
|
extrn _DbgPrint:proc
|
|
EXTRNP _KdSetOwedBreakpoints
|
|
extrn _KiFreezeFlag:dword
|
|
EXTRNP _Ki386CheckDivideByZeroTrap,1
|
|
EXTRNP _Ki386CheckDelayedNpxTrap,2
|
|
EXTRNP _VdmDispatchIRQ13, 1
|
|
|
|
EXTRNP _VdmDispatchBop,1
|
|
EXTRNP _VdmFetchBop1,1
|
|
EXTRNP _VdmFetchBop4,1
|
|
EXTRNP _VdmTibPass1,3
|
|
extrn _KeI386VirtualIntExtensions:dword
|
|
EXTRNP _NTFastDOSIO,2
|
|
EXTRNP _NtSetLdtEntries,6
|
|
extrn OpcodeIndex:byte
|
|
extrn _KeFeatureBits:DWORD
|
|
extrn _KeServiceDescriptorTableShadow:dword
|
|
extrn _KiIgnoreUnexpectedTrap07:byte
|
|
|
|
ifndef NT_UP
|
|
|
|
EXTRNP KiAcquireQueuedSpinLockCheckForFreeze,2,,FASTCALL
|
|
EXTRNP KeReleaseQueuedSpinLockFromDpcLevel,1,,FASTCALL
|
|
|
|
endif
|
|
|
|
ifdef _CAPKERN
|
|
extrn __CAP_SetCPU@0:PROC
|
|
extrn __CAP_ThreadID@0:PROC
|
|
endif
|
|
|
|
;
|
|
; Equates for exceptions which cause system fatal error
|
|
;
|
|
|
|
EXCEPTION_DIVIDED_BY_ZERO EQU 0
|
|
EXCEPTION_DEBUG EQU 1
|
|
EXCEPTION_NMI EQU 2
|
|
EXCEPTION_INT3 EQU 3
|
|
EXCEPTION_BOUND_CHECK EQU 5
|
|
EXCEPTION_INVALID_OPCODE EQU 6
|
|
EXCEPTION_NPX_NOT_AVAILABLE EQU 7
|
|
EXCEPTION_DOUBLE_FAULT EQU 8
|
|
EXCEPTION_NPX_OVERRUN EQU 9
|
|
EXCEPTION_INVALID_TSS EQU 0AH
|
|
EXCEPTION_SEGMENT_NOT_PRESENT EQU 0BH
|
|
EXCEPTION_STACK_FAULT EQU 0CH
|
|
EXCEPTION_GP_FAULT EQU 0DH
|
|
EXCEPTION_RESERVED_TRAP EQU 0FH
|
|
EXCEPTION_NPX_ERROR EQU 010H
|
|
EXCEPTION_ALIGNMENT_CHECK EQU 011H
|
|
|
|
;
|
|
; Exception flags
|
|
;
|
|
|
|
EXCEPT_UNKNOWN_ACCESS EQU 0H
|
|
EXCEPT_LIMIT_ACCESS EQU 10H
|
|
|
|
;
|
|
; Equates for some opcodes and instruction prefixes
|
|
;
|
|
|
|
IOPL_MASK EQU 3000H
|
|
IOPL_SHIFT_COUNT EQU 12
|
|
|
|
;
|
|
; Debug register 6 (dr6) BS (single step) bit mask
|
|
;
|
|
|
|
DR6_BS_MASK EQU 4000H
|
|
|
|
;
|
|
; EFLAGS overflow bit
|
|
;
|
|
|
|
EFLAGS_OF_BIT EQU 4000H
|
|
|
|
;
|
|
; The mask of selecot's table indicator (ldt or gdt)
|
|
;
|
|
|
|
TABLE_INDICATOR_MASK EQU 4
|
|
|
|
;
|
|
; Opcode for Pop SegReg and iret instructions
|
|
;
|
|
|
|
POP_DS EQU 1FH
|
|
POP_ES EQU 07h
|
|
POP_FS EQU 0A10FH
|
|
POP_GS EQU 0A90FH
|
|
IRET_OP EQU 0CFH
|
|
CLI_OP EQU 0FAH
|
|
STI_OP EQU 0FBH
|
|
PUSHF_OP EQU 9CH
|
|
POPF_OP EQU 9DH
|
|
INTNN_OP EQU 0CDH
|
|
FRSTOR_ECX EQU 021DD9Bh
|
|
FWAIT_OP EQU 09bh
|
|
|
|
;
|
|
; Force assume into place
|
|
;
|
|
|
|
_TEXT$00 SEGMENT PARA PUBLIC 'CODE'
|
|
ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING
|
|
_TEXT$00 ENDS
|
|
|
|
_DATA SEGMENT DWORD PUBLIC 'DATA'
|
|
|
|
;
|
|
; Definitions for gate descriptors
|
|
;
|
|
|
|
GATE_TYPE_386INT EQU 0E00H
|
|
GATE_TYPE_386TRAP EQU 0F00H
|
|
GATE_TYPE_TASK EQU 0500H
|
|
D_GATE EQU 0
|
|
D_PRESENT EQU 8000H
|
|
D_DPL_3 EQU 6000H
|
|
D_DPL_0 EQU 0
|
|
|
|
;
|
|
; Definitions for present x86 trap and interrupt gate attributes
|
|
;
|
|
|
|
D_TRAP032 EQU D_PRESENT+D_DPL_0+D_GATE+GATE_TYPE_386TRAP
|
|
D_TRAP332 EQU D_PRESENT+D_DPL_3+D_GATE+GATE_TYPE_386TRAP
|
|
D_INT032 EQU D_PRESENT+D_DPL_0+D_GATE+GATE_TYPE_386INT
|
|
D_INT332 EQU D_PRESENT+D_DPL_3+D_GATE+GATE_TYPE_386INT
|
|
D_TASK EQU D_PRESENT+D_DPL_0+D_GATE+GATE_TYPE_TASK
|
|
|
|
;
|
|
; This is the protected mode interrupt descriptor table.
|
|
;
|
|
|
|
if DBG
|
|
;
|
|
; NOTE - embedded enlish messages won't fly for NLS! (OK for debug code only)
|
|
;
|
|
|
|
BadInterruptMessage db 0ah,7,7,'!!! Unexpected Interrupt %02lx !!!',0ah,00
|
|
|
|
Ki16BitStackTrapMessage db 0ah,'Exception inside of 16bit stack',0ah,00
|
|
|
|
public KiBiosReenteredAssert
|
|
KiBiosReenteredAssert db 0ah,'Bios has been re-entered. Not safe. ',0ah,00
|
|
endif
|
|
|
|
;
|
|
; NMI only one processor at a time. This is handled in the kernel
|
|
; using a queued spinlock to avoid thrashing the lock in case a
|
|
; crash dump is underway.
|
|
;
|
|
|
|
KiLockNMI dd 0
|
|
|
|
|
|
;++
|
|
;
|
|
; DEFINE_SINGLE_EMPTY_VECTOR - helper for DEFINE_EMPTY_VECTORS
|
|
;
|
|
;--
|
|
|
|
DEFINE_SINGLE_EMPTY_VECTOR macro number
|
|
IDTEntry _KiUnexpectedInterrupt&number, D_INT032
|
|
_TEXT$00 SEGMENT
|
|
public _KiUnexpectedInterrupt&number
|
|
_KiUnexpectedInterrupt&number proc
|
|
push dword ptr (&number + PRIMARY_VECTOR_BASE)
|
|
;; jmp _KiUnexpectedInterruptTail ; replaced with following jmp which will then jump
|
|
jmp _KiAfterUnexpectedRange ; to the _KiUnexpectedInterruptTail location
|
|
; in a manner suitable for BBT, which needs to treat
|
|
; this whole set of KiUnexpectedInterrupt&number
|
|
; vectors as DATA, meaning a relative jmp will not
|
|
; be adjusted properly in the BBT Instrumented or
|
|
; Optimized code.
|
|
;
|
|
|
|
_KiUnexpectedInterrupt&number endp
|
|
_TEXT$00 ENDS
|
|
|
|
endm
|
|
|
|
FPOFRAME macro a, b
|
|
.FPO ( a, b, 0, 0, 0, FPO_TRAPFRAME )
|
|
endm
|
|
|
|
FXSAVE_ESI macro
|
|
db 0FH, 0AEH, 06
|
|
endm
|
|
|
|
FXSAVE_ECX macro
|
|
db 0FH, 0AEH, 01
|
|
endm
|
|
|
|
FXRSTOR_ECX macro
|
|
db 0FH, 0AEH, 09
|
|
endm
|
|
|
|
;++
|
|
;
|
|
; DEFINE_EMPTY_VECTORS emits an IDTEntry macro (and thus and IDT entry)
|
|
; into the data segment. It then emits an unexpected interrupt target
|
|
; with push of a constant into the code segment. Labels in the code
|
|
; segment are defined to bracket the unexpected interrupt targets so
|
|
; that KeConnectInterrupt can correctly test for them.
|
|
;
|
|
; Empty vectors will be defined from 30 to ff, which is the hardware
|
|
; vector set.
|
|
;
|
|
;--
|
|
|
|
NUMBER_OF_IDT_VECTOR EQU 0ffH
|
|
|
|
DEFINE_EMPTY_VECTORS macro
|
|
|
|
;
|
|
; Set up
|
|
;
|
|
|
|
empty_vector = 00H
|
|
|
|
_TEXT$00 SEGMENT
|
|
public _KiStartUnexpectedRange@0
|
|
_KiStartUnexpectedRange@0 equ $
|
|
_TEXT$00 ENDS
|
|
|
|
rept (NUMBER_OF_IDT_VECTOR - (($ - _IDT)/8)) + 1
|
|
|
|
DEFINE_SINGLE_EMPTY_VECTOR %empty_vector
|
|
empty_vector = empty_vector + 1
|
|
|
|
endm ;; rept
|
|
|
|
_TEXT$00 SEGMENT
|
|
public _KiEndUnexpectedRange@0
|
|
_KiEndUnexpectedRange@0 equ $
|
|
|
|
|
|
;; added by to handle BBT unexpected interrupt problem
|
|
;;
|
|
_KiAfterUnexpectedRange equ $ ;; BBT
|
|
jmp [KiUnexpectedInterruptTail] ;; BBT
|
|
|
|
KiUnexpectedInterruptTail dd offset _KiUnexpectedInterruptTail ;; BBT
|
|
|
|
public _KiBBTUnexpectedRange
|
|
_KiBBTUnexpectedRange equ $ ;; BBT
|
|
|
|
_TEXT$00 ENDS
|
|
|
|
endm ;; DEFINE_EMPTY_VECTORS macro
|
|
|
|
IDTEntry macro name,access
|
|
dd offset FLAT:name
|
|
dw access
|
|
dw KGDT_R0_CODE
|
|
endm
|
|
|
|
INIT SEGMENT DWORD PUBLIC 'CODE'
|
|
|
|
;
|
|
; The IDT table is put into the INIT code segment so the memory
|
|
; can be reclaimed afer bootup
|
|
;
|
|
|
|
ALIGN 4
|
|
public _IDT, _IDTLEN, _IDTEnd
|
|
_IDT label byte
|
|
|
|
IDTEntry _KiTrap00, D_INT032 ; 0: Divide Error
|
|
IDTEntry _KiTrap01, D_INT032 ; 1: DEBUG TRAP
|
|
IDTEntry _KiTrap02, D_INT032 ; 2: NMI/NPX Error
|
|
IDTEntry _KiTrap03, D_INT332 ; 3: Breakpoint
|
|
IDTEntry _KiTrap04, D_INT332 ; 4: INTO
|
|
IDTEntry _KiTrap05, D_INT032 ; 5: BOUND/Print Screen
|
|
IDTEntry _KiTrap06, D_INT032 ; 6: Invalid Opcode
|
|
IDTEntry _KiTrap07, D_INT032 ; 7: NPX Not Available
|
|
IDTEntry _KiTrap08, D_INT032 ; 8: Double Exception
|
|
IDTEntry _KiTrap09, D_INT032 ; 9: NPX Segment Overrun
|
|
IDTEntry _KiTrap0A, D_INT032 ; A: Invalid TSS
|
|
IDTEntry _KiTrap0B, D_INT032 ; B: Segment Not Present
|
|
IDTEntry _KiTrap0C, D_INT032 ; C: Stack Fault
|
|
IDTEntry _KiTrap0D, D_INT032 ; D: General Protection
|
|
IDTEntry _KiTrap0E, D_INT032 ; E: Page Fault
|
|
IDTEntry _KiTrap0F, D_INT032 ; F: Intel Reserved
|
|
|
|
IDTEntry _KiTrap10, D_INT032 ;10: 486 coprocessor error
|
|
IDTEntry _KiTrap11, D_INT032 ;11: 486 alignment
|
|
IDTEntry _KiTrap0F, D_INT032 ;12: Intel Reserved
|
|
IDTEntry _KiTrap0F, D_INT032 ;13: XMMI unmasked numeric exception
|
|
IDTEntry _KiTrap0F, D_INT032 ;14: Intel Reserved
|
|
IDTEntry _KiTrap0F, D_INT032 ;15: Intel Reserved
|
|
IDTEntry _KiTrap0F, D_INT032 ;16: Intel Reserved
|
|
IDTEntry _KiTrap0F, D_INT032 ;17: Intel Reserved
|
|
|
|
IDTEntry _KiTrap0F, D_INT032 ;18: Intel Reserved
|
|
IDTEntry _KiTrap0F, D_INT032 ;19: Intel Reserved
|
|
IDTEntry _KiTrap0F, D_INT032 ;1A: Intel Reserved
|
|
IDTEntry _KiTrap0F, D_INT032 ;1B: Intel Reserved
|
|
IDTEntry _KiTrap0F, D_INT032 ;1C: Intel Reserved
|
|
IDTEntry _KiTrap0F, D_INT032 ;1D: Intel Reserved
|
|
IDTEntry _KiTrap0F, D_INT032 ;1E: Intel Reserved
|
|
IDTEntry _KiTrap0F, D_INT032 ;1F: Reserved for APIC
|
|
|
|
;
|
|
; Note IDTEntry 0x21 is reserved for WOW apps.
|
|
;
|
|
|
|
rept 2AH - (($ - _IDT)/8)
|
|
IDTEntry 0, 0 ;invalid IDT entry
|
|
endm
|
|
IDTEntry _KiGetTickCount, D_INT332 ;2A: KiGetTickCount service
|
|
IDTEntry _KiCallbackReturn, D_INT332 ;2B: KiCallbackReturn
|
|
IDTEntry _KiSetLowWaitHighThread, D_INT332 ;2C: KiSetLowWaitHighThread service
|
|
IDTEntry _KiDebugService, D_INT332 ;2D: debugger calls
|
|
IDTEntry _KiSystemService, D_INT332 ;2E: system service calls
|
|
IDTEntry _KiTrap0F, D_INT032 ;2F: Reserved for APIC
|
|
|
|
;
|
|
; Generate per-vector unexpected interrupt entries for 30 - ff
|
|
;
|
|
DEFINE_EMPTY_VECTORS
|
|
|
|
_IDTLEN equ $ - _IDT
|
|
_IDTEnd equ $
|
|
|
|
INIT ends
|
|
|
|
public _KiUnexpectedEntrySize
|
|
_KiUnexpectedEntrySize dd _KiUnexpectedInterrupt1 - _KiUnexpectedInterrupt0
|
|
|
|
;
|
|
; defines all the possible instruction prefix
|
|
;
|
|
|
|
PrefixTable label byte
|
|
db 0f2h ; rep prefix
|
|
db 0f3h ; rep ins/outs prefix
|
|
db 67h ; addr prefix
|
|
db 0f0h ; lock prefix
|
|
db 66h ; operand prefix
|
|
db 2eh ; segment override prefix:cs
|
|
db 3eh ; ds
|
|
db 26h ; es
|
|
db 64h ; fs
|
|
db 65h ; gs
|
|
db 36h ; ss
|
|
|
|
PREFIX_REPEAT_COUNT EQU 11 ; Prefix table length
|
|
|
|
;
|
|
; defines all the possible IO privileged IO instructions
|
|
;
|
|
|
|
IOInstructionTable label byte
|
|
; db 0fah ; cli
|
|
; db 0fdh ; sti
|
|
db 0e4h, 0e5h, 0ech, 0edh ; IN
|
|
db 6ch, 6dh ; INS
|
|
db 0e6h, 0e7h, 0eeh, 0efh ; OUT
|
|
db 6eh, 6fh ; OUTS
|
|
|
|
IO_INSTRUCTION_TABLE_LENGTH EQU 12
|
|
|
|
;
|
|
; definition for floating status word error mask
|
|
;
|
|
|
|
FSW_INVALID_OPERATION EQU 1
|
|
FSW_DENORMAL EQU 2
|
|
FSW_ZERO_DIVIDE EQU 4
|
|
FSW_OVERFLOW EQU 8
|
|
FSW_UNDERFLOW EQU 16
|
|
FSW_PRECISION EQU 32
|
|
FSW_STACK_FAULT EQU 64
|
|
FSW_CONDITION_CODE_0 EQU 100H
|
|
FSW_CONDITION_CODE_1 EQU 200H
|
|
FSW_CONDITION_CODE_2 EQU 400H
|
|
FSW_CONDITION_CODE_3 EQU 4000H
|
|
|
|
_DATA ENDS
|
|
|
|
_TEXT$00 SEGMENT
|
|
ASSUME DS:NOTHING, ES:NOTHING, SS:FLAT, FS:NOTHING, GS:NOTHING
|
|
|
|
page , 132
|
|
subttl "Macro to Handle v86 trap d"
|
|
;++
|
|
;
|
|
; Macro Description:
|
|
;
|
|
; This macro is a fast way to handle v86 bop instructions.
|
|
; Note, all the memory write operations in this macro are done in such a
|
|
; way that if a page fault occurs the memory will still be in a consistent
|
|
; state.
|
|
;
|
|
; That is, we must process the trapped instruction in the following order:
|
|
;
|
|
; 1. Read and Write user memory
|
|
; 2. Update VDM state flags
|
|
; 3. Update trap frame
|
|
;
|
|
; Arguments:
|
|
;
|
|
; interrupts disabled
|
|
;
|
|
; Return Value:
|
|
;
|
|
;--
|
|
|
|
FAST_V86_TRAP_6 MACRO
|
|
|
|
local DoFastIo, a, b
|
|
|
|
BOP_FOR_FASTWRITE EQU 4350C4C4H
|
|
BOP_FOR_FASTREAD EQU 4250C4C4H
|
|
TRAP6_IP EQU 32 ; 8 * 4
|
|
TRAP6_CS EQU 36 ; 8 * 4 + 4
|
|
TRAP6_FLAGS EQU 40 ; 8 * 4 + 8
|
|
TRAP6_SP EQU 44 ; 8 * 4 + 12
|
|
TRAP6_SS EQU 48 ; 8 * 4 + 16
|
|
TRAP6_ES EQU 52
|
|
TRAP6_DS EQU 56
|
|
TRAP6_FS EQU 60
|
|
TRAP6_GS EQU 64
|
|
TRAP6_EAX EQU 28
|
|
TRAP6_EDX EQU 20
|
|
|
|
pushad ;eax, ecx, edx, ebx, old esp, ebp, esi, edi
|
|
mov eax, KGDT_R3_DATA OR RPL_MASK
|
|
mov ds, ax
|
|
mov es, ax
|
|
|
|
ifndef NT_UP
|
|
mov eax, KGDT_R0_PCR
|
|
mov fs, ax
|
|
endif
|
|
|
|
mov ax, word ptr [esp+TRAP6_CS] ; [eax] = v86 user cs
|
|
shl eax, 4
|
|
and dword ptr [esp+TRAP6_IP], 0FFFFH
|
|
add eax, [esp+TRAP6_IP]; [eax] = addr of BOP
|
|
|
|
;
|
|
; Set the magic PCR bit indicating we are executing VDM management code
|
|
; so faults on potentially invalid or plain bad user addresses do
|
|
; not bugcheck the system. Note both interrupts are disabled and
|
|
; (for performance reasons) we do not have any exception handlers
|
|
; set up.
|
|
;
|
|
|
|
mov dword ptr PCR[PcVdmAlert], offset FLAT:V86Trap6Recovery
|
|
|
|
;
|
|
; Fetch the actual opcode from user space.
|
|
;
|
|
|
|
mov edx, [eax] ; [edx] = xxxxc4c4 bop + maj bop # + mi #
|
|
|
|
cmp edx, BOP_FOR_FASTREAD
|
|
je DoFastIo
|
|
|
|
cmp edx, BOP_FOR_FASTWRITE
|
|
je DoFastIo
|
|
|
|
cmp dx, 0c4c4h ; Is it a bop?
|
|
jne V86Trap6PassThrough ; It's an error condition
|
|
|
|
mov eax,PCR[PcTeb]
|
|
shr edx, 16
|
|
mov eax,[eax].TeVdm
|
|
|
|
cmp eax, _MmUserProbeAddress ; check if user address
|
|
jae V86Trap6PassThrough ; if ae, then not user address
|
|
|
|
and edx, 0ffh
|
|
mov dword ptr [eax].VtEIEvent, VdmBop
|
|
mov dword ptr [eax].VtEIBopNumber, edx
|
|
mov dword ptr [eax].VtEIInstSize, 3
|
|
lea eax, [eax].VtVdmContext
|
|
|
|
;
|
|
; Save V86 state to Vdm structure
|
|
;
|
|
|
|
mov edx, [esp+TRAP6_EDX] ; get edx
|
|
|
|
cmp eax, _MmUserProbeAddress ; check if user address
|
|
jae V86Trap6PassThrough ; if ae, then not user address
|
|
|
|
mov [eax].CsEcx, ecx
|
|
mov [eax].CsEbx, ebx ; Save non-volatile registers
|
|
mov [eax].CsEsi, esi
|
|
mov [eax].CsEdi, edi
|
|
mov ecx, [esp+TRAP6_EAX] ; Get eax
|
|
mov [eax].CsEbp, ebp
|
|
mov [eax].CsEdx, edx
|
|
mov [eax].CsEax, ecx
|
|
|
|
mov ebx, [esp]+TRAP6_IP ; (ebx) = user ip
|
|
mov ecx, [esp]+TRAP6_CS ; (ecx) = user cs
|
|
mov esi, [esp]+TRAP6_SP ; (esi) = user esp
|
|
mov edi, [esp]+TRAP6_SS ; (edi) = user ss
|
|
mov edx, [esp]+TRAP6_FLAGS; (edx) = user eflags
|
|
mov [eax].CsEip, ebx
|
|
and esi, 0ffffh
|
|
mov [eax].CsSegCs, ecx
|
|
mov [eax].CsEsp, esi
|
|
mov [eax].CsSegSs, edi
|
|
test _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS
|
|
jz short @f
|
|
|
|
test edx, EFLAGS_VIF
|
|
jnz short a
|
|
|
|
and edx, NOT EFLAGS_INTERRUPT_MASK
|
|
jmp short a
|
|
|
|
@@: test ds:FIXED_NTVDMSTATE_LINEAR, VDM_VIRTUAL_INTERRUPTS ; check interrupt
|
|
jnz short a
|
|
|
|
and edx, NOT EFLAGS_INTERRUPT_MASK
|
|
a:
|
|
mov [eax].CsEFlags, edx
|
|
mov ebx, [esp]+TRAP6_DS ; (ebx) = user ds
|
|
mov ecx, [esp]+TRAP6_ES ; (ecx) = user es
|
|
mov edx, [esp]+TRAP6_FS ; (edx) = user fs
|
|
mov esi, [esp]+TRAP6_GS ; (esi) = user gs
|
|
mov [eax].CsSegDs, ebx
|
|
mov [eax].CsSegEs, ecx
|
|
mov [eax].CsSegFs, edx
|
|
mov [eax].CsSegGs, esi
|
|
|
|
;
|
|
; Load Monitor context
|
|
;
|
|
|
|
add eax, VtMonitorContext - VtVdmContext ; (eax)->monitor context
|
|
|
|
mov ebx, [eax].CsEbx ; We don't need to load volatile registers.
|
|
mov esi, [eax].CsEsi ; because monitor uses SystemCall to return
|
|
mov edi, [eax].CsEdi ; back to v86. C compiler knows that
|
|
mov ebp, [eax].CsEbp ; SystemCall does not preserve volatile
|
|
; registers.
|
|
; es, ds are set up already.
|
|
|
|
;
|
|
; Note these push instructions won't fail. Do NOT combine the
|
|
; 'move ebx, [eax].CsEbx' with 'push ebx' to 'push [eax].CsEbx'
|
|
;
|
|
|
|
push ebx ; note, these push instructions won't fail
|
|
push esi
|
|
push edi
|
|
push ebp
|
|
mov dword ptr PCR[PcVdmAlert], offset FLAT:V86Trap6Recovery2
|
|
|
|
mov ebx, [eax].CsSegSs
|
|
mov esi, [eax].CsEsp
|
|
mov edi, [eax].CsEFlags
|
|
mov edx, [eax].CsSegCs
|
|
mov ecx, [eax].CsEip
|
|
|
|
;
|
|
; after this point, we don't need to worry about instruction fault
|
|
; Clear magic flag as no more potentially bogus references will be made.
|
|
;
|
|
|
|
mov dword ptr PCR[PcVdmAlert], 0
|
|
|
|
|
|
and edi, EFLAGS_USER_SANITIZE
|
|
or edi, EFLAGS_INTERRUPT_MASK
|
|
mov [esp + TRAP6_SS + 16], ebx ; Build Iret frame (can not single step!)
|
|
mov [esp + TRAP6_SP + 16], esi
|
|
mov [esp + TRAP6_FLAGS + 16], edi
|
|
cmp edx, 8
|
|
jge @f
|
|
test edi, EFLAGS_V86_MASK
|
|
jne @f
|
|
mov edx, KGDT_R3_CODE OR RPL_MASK
|
|
@@: mov [esp + TRAP6_CS + 16], edx
|
|
mov [esp + TRAP6_IP + 16], ecx
|
|
|
|
pop ebp
|
|
pop edi
|
|
pop esi
|
|
pop ebx
|
|
|
|
add esp, 32
|
|
|
|
;
|
|
; Adjust Tss esp0 value and set return value to SUCCESS
|
|
;
|
|
|
|
mov ecx, PCR[PcPrcbData+PbCurrentThread]
|
|
mov ecx, [ecx].thInitialStack
|
|
mov edx, PCR[PcTss]
|
|
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
|
|
test byte ptr [esp+8+2],EFLAGS_V86_MASK/010000h ; is this a V86 frame?
|
|
jnz short @f
|
|
sub ecx, TsV86Gs - TsHardwareSegSs
|
|
@@:
|
|
sub ecx, NPX_FRAME_LENGTH
|
|
xor eax, eax ; ret status = SUCCESS
|
|
mov [edx].TssEsp0, ecx
|
|
|
|
mov edx, KGDT_R3_TEB OR RPL_MASK
|
|
mov fs, dx
|
|
iretd
|
|
|
|
DoFastIo:
|
|
|
|
;
|
|
; Clear magic flag as no bogus references are going to be made.
|
|
;
|
|
|
|
mov dword ptr PCR[PcVdmAlert], 0
|
|
|
|
xor eax, eax
|
|
mov edx, [esp]+TRAP6_EDX ; Restore edx
|
|
add esp, 7 * 4 ; leave eax in the TsErrCode
|
|
xchg [esp], eax ; Restore eax, store a zero errcode
|
|
sub esp, TsErrcode ; build a trap frame
|
|
mov [esp].TsEbx, ebx
|
|
mov [esp].TsEax, eax
|
|
mov [esp].TsEbp, ebp
|
|
mov [esp].TsEsi, esi
|
|
mov [esp].TsEdi, edi
|
|
mov [esp].TsEcx, ecx
|
|
mov [esp].TsEdx, edx
|
|
if DBG
|
|
mov [esp].TsPreviousPreviousMode, -1
|
|
mov [esp]+TsDbgArgMark, 0BADB0D00h
|
|
endif
|
|
mov edi, PCR[PcExceptionList]
|
|
mov [esp]+TsExceptionList, edi
|
|
|
|
ifdef NT_UP
|
|
mov ebx, KGDT_R0_PCR
|
|
mov fs, bx
|
|
endif
|
|
mov ebx, PCR[PcPrcbData+PbCurrentThread] ; fetch current thread
|
|
test [ebx].ThDebugActive, 0ffh ; See if debug registers are active
|
|
mov ebp, esp
|
|
cld
|
|
.errnz (DR7_ACTIVE AND 0FFFFFF00h)
|
|
mov [ebp]+TsDr7,0
|
|
je short @f
|
|
|
|
mov ebx,dr0
|
|
mov esi,dr1
|
|
mov edi,dr2
|
|
mov [ebp]+TsDr0,ebx
|
|
mov [ebp]+TsDr1,esi
|
|
mov [ebp]+TsDr2,edi
|
|
mov ebx,dr3
|
|
mov esi,dr6
|
|
mov edi,dr7
|
|
mov [ebp]+TsDr3,ebx
|
|
xor ebx, ebx
|
|
mov [ebp]+TsDr6,esi
|
|
mov [ebp]+TsDr7,edi
|
|
mov dr7, ebx ; Clear out control before reloading
|
|
;
|
|
; Load KernelDr* into processor
|
|
;
|
|
mov edi,dword ptr PCR[PcPrcb]
|
|
mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr0
|
|
mov esi,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr1
|
|
mov dr0,ebx
|
|
mov dr1,esi
|
|
mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr2
|
|
mov esi,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr3
|
|
mov dr2,ebx
|
|
mov dr3,esi
|
|
mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr6
|
|
mov esi,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr7
|
|
mov dr6,ebx
|
|
mov dr7,esi
|
|
@@:
|
|
xor edx, edx
|
|
mov dx, word ptr [ebp].TsSegCs
|
|
shl edx, 4
|
|
xor ebx, ebx
|
|
add edx, [ebp].TsEip
|
|
|
|
;
|
|
; Set the magic PCR bit indicating we are executing VDM management code
|
|
; so faults on potentially invalid or plain bad user addresses do
|
|
; not bugcheck the system. Note both interrupts are disabled and
|
|
; (for performance reasons) we do not have any exception handlers
|
|
; set up.
|
|
;
|
|
|
|
mov dword ptr PCR[PcVdmAlert], offset FLAT:V86Trap6Recovery1
|
|
|
|
;
|
|
; Fetch the actual opcode from user space.
|
|
;
|
|
|
|
mov bl, [edx+3] ; [bl] = minor BOP code
|
|
|
|
;
|
|
; Clear magic flag as no bogus references are going to be made.
|
|
;
|
|
|
|
mov dword ptr PCR[PcVdmAlert], 0
|
|
|
|
;
|
|
; Raise Irql to APC level before enabling interrupts.
|
|
;
|
|
|
|
RaiseIrql APC_LEVEL
|
|
push eax ; Save OldIrql
|
|
sti
|
|
|
|
push ebx
|
|
push ebp ; (ebp)->TrapFrame
|
|
call _NTFastDOSIO@8
|
|
jmp Kt061i
|
|
|
|
V86Trap6PassThrough:
|
|
|
|
;
|
|
; Clear magic flag as no bogus references are going to be made.
|
|
;
|
|
|
|
mov dword ptr PCR[PcVdmAlert], 0
|
|
|
|
V86Trap6Recovery:
|
|
popad
|
|
jmp Kt6SlowBop ; Fall through
|
|
|
|
V86Trap6Recovery1:
|
|
jmp Kt6SlowBop1 ; Fall through
|
|
|
|
V86Trap6Recovery2:
|
|
add esp, 16
|
|
popad
|
|
jmp Kt6SlowBop ; Fall through
|
|
endm
|
|
page , 132
|
|
subttl "Macro to dispatch user APC"
|
|
|
|
;++
|
|
;
|
|
; Macro Description:
|
|
;
|
|
; This macro is called before returning to user mode. It dispatches
|
|
; any pending user mode APCs.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; TFrame - TrapFrame
|
|
; interrupts disabled
|
|
;
|
|
; Return Value:
|
|
;
|
|
;--
|
|
|
|
DISPATCH_USER_APC macro TFrame, ReturnCurrentEax
|
|
local a, b, c
|
|
c:
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
|
|
test byte ptr [TFrame]+TsEflags+2, EFLAGS_V86_MASK/010000h ; is previous mode v86?
|
|
jnz short b ; if nz, yes, go check for APC
|
|
test byte ptr [TFrame]+TsSegCs,MODE_MASK ; is previous mode user mode?
|
|
jz a ; No, previousmode=Kernel, jump out
|
|
b: mov ebx, PCR[PcPrcbData+PbCurrentThread]; get addr of current thread
|
|
mov byte ptr [ebx]+ThAlerted, 0 ; clear kernel mode alerted
|
|
cmp byte ptr [ebx]+ThApcState.AsUserApcPending, 0
|
|
je a ; if eq, no user APC pending
|
|
|
|
mov ebx, TFrame
|
|
ifnb <ReturnCurrentEax>
|
|
mov [ebx].TsEax, eax ; Store return code in trap frame
|
|
mov dword ptr [ebx]+TsSegFs, KGDT_R3_TEB OR RPL_MASK
|
|
mov dword ptr [ebx]+TsSegDs, KGDT_R3_DATA OR RPL_MASK
|
|
mov dword ptr [ebx]+TsSegEs, KGDT_R3_DATA OR RPL_MASK
|
|
mov dword ptr [ebx]+TsSegGs, 0
|
|
endif
|
|
|
|
;
|
|
; Save previous IRQL and set new priority level
|
|
;
|
|
RaiseIrql APC_LEVEL
|
|
push eax ; Save OldIrql
|
|
|
|
sti ; Allow higher priority ints
|
|
|
|
;
|
|
; call the APC delivery routine.
|
|
;
|
|
; ebx - Trap frame
|
|
; 0 - Null exception frame
|
|
; 1 - Previous mode
|
|
;
|
|
; call APC deliver routine
|
|
;
|
|
|
|
CAPSTART <c,_KiDeliverApc@12>
|
|
stdCall _KiDeliverApc, <1, 0, ebx>
|
|
CAPEND <c>
|
|
|
|
pop ecx ; (ecx) = OldIrql
|
|
LowerIrql ecx
|
|
|
|
ifnb <ReturnCurrentEax>
|
|
mov eax, [ebx].TsEax ; Restore eax, just in case
|
|
endif
|
|
|
|
cli
|
|
jmp b
|
|
|
|
ALIGN 4
|
|
a:
|
|
endm
|
|
|
|
|
|
if DBG
|
|
page ,132
|
|
subttl "Processing Exception occurred in a 16 bit stack"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine is called after an exception being detected during
|
|
; a 16 bit stack. The system will switch 16 stack to 32 bit
|
|
; stack and bugcheck.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None.
|
|
;
|
|
; Return value:
|
|
;
|
|
; system stopped.
|
|
;
|
|
;--
|
|
|
|
align dword
|
|
public _Ki16BitStackException
|
|
_Ki16BitStackException proc
|
|
|
|
.FPO (2, 0, 0, 0, 0, FPO_TRAPFRAME)
|
|
|
|
push ss
|
|
push esp
|
|
mov eax, PCR[PcPrcbData+PbCurrentThread] ; get current thread address
|
|
add esp, [eax]+ThStackLimit ; compute 32-bit stack address
|
|
mov eax, KGDT_R0_DATA
|
|
mov ss, ax
|
|
|
|
lea ebp, [esp+8]
|
|
cld
|
|
SET_DEBUG_DATA
|
|
|
|
if DBG
|
|
push offset FLAT:Ki16BitStackTrapMessage
|
|
call _dbgPrint
|
|
add esp, 4
|
|
endif
|
|
stdCall _KeBugCheck, <0F000FFFFh> ; Never return
|
|
ret
|
|
|
|
_Ki16BitStackException endp
|
|
|
|
endif
|
|
|
|
|
|
page ,132
|
|
subttl "System Service Call"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine gains control when trap occurs via vector 2EH.
|
|
; INT 2EH is reserved for system service calls.
|
|
;
|
|
; The system service is executed by locating its routine address in
|
|
; system service dispatch table and calling the specified function.
|
|
; On return necessary state is restored.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; eax - System service number.
|
|
; edx - Pointer to arguments
|
|
;
|
|
; Return Value:
|
|
;
|
|
; eax - System service status code.
|
|
;
|
|
;--
|
|
|
|
if 0
|
|
;
|
|
; Error and exception blocks for KiSystemService
|
|
;
|
|
|
|
Kss_ExceptionHandler:
|
|
|
|
;
|
|
; WARNING: Here we directly unlink the exception handler from the
|
|
; exception registration chain. NO unwind is performed.
|
|
;
|
|
|
|
mov eax, [esp+4] ; (eax)-> ExceptionRecord
|
|
mov eax, [eax].ErExceptionCode ; (eax) = Exception code
|
|
mov esp, [esp+8] ; (esp)-> ExceptionList
|
|
|
|
pop eax
|
|
mov PCR[PcExceptionList],eax
|
|
|
|
add esp, 4
|
|
pop ebp
|
|
test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
|
|
jnz kss60 ; v86 mode => usermode
|
|
|
|
test dword ptr [ebp].TsSegCs, MODE_MASK ; if premode=kernel
|
|
jnz kss60 ; nz, prevmode=user, go return
|
|
|
|
; raise bugcheck if prevmode=kernel
|
|
stdCall _KeBugCheck, <KMODE_EXCEPTION_NOT_HANDLED>
|
|
endif
|
|
|
|
;
|
|
; The specified system service number is not within range. Attempt to
|
|
; convert the thread to a GUI thread if the specified system service is
|
|
; not a base service and the thread has not already been converted to a
|
|
; GUI thread.
|
|
;
|
|
|
|
Kss_ErrorHandler:
|
|
cmp ecx, SERVICE_TABLE_TEST ; test if GUI service
|
|
jne short Kss_LimitError ; if ne, not GUI service
|
|
push edx ; save argument registers
|
|
push ebx ;
|
|
stdcall _PsConvertToGuiThread ; attempt to convert to GUI thread
|
|
or eax, eax ; check if service was successful
|
|
pop eax ; restore argument registers
|
|
pop edx ;
|
|
mov ebp, esp ; reset trap frame address
|
|
mov [esi]+ThTrapFrame, ebp ; save address of trap frame
|
|
jz _KiSystemServiceRepeat ; if eq, successful conversion
|
|
|
|
;
|
|
; The conversion to a GUI thread failed. The correct return value is encoded
|
|
; in a byte table indexed by the service number that is at the end of the
|
|
; service address table. The encoding is as follows:
|
|
;
|
|
; 0 - return 0.
|
|
; -1 - return -1.
|
|
; 1 - return status code.
|
|
;
|
|
|
|
lea edx, _KeServiceDescriptorTableShadow + SERVICE_TABLE_TEST ;
|
|
mov ecx, [edx]+SdLimit ; get service number limit
|
|
mov edx, [edx]+SdBase ; get service table base
|
|
lea edx, [edx][ecx*4] ; get ending service table address
|
|
and eax, SERVICE_NUMBER_MASK ; isolate service number
|
|
add edx, eax ; compute return value address
|
|
movsx eax, byte ptr [edx] ; get status byte
|
|
or eax, eax ; check for 0 or -1
|
|
jle Kss70 ; if le, return value set
|
|
|
|
Kss_LimitError: ;
|
|
mov eax, STATUS_INVALID_SYSTEM_SERVICE ; set return status
|
|
jmp kss70 ;
|
|
|
|
|
|
ifndef NT_UP
|
|
ENTER_DR_ASSIST kfce_a, kfce_t,NoAbiosAssist,NoV86Assist
|
|
endif
|
|
|
|
ENTER_DR_ASSIST kss_a, kss_t,NoAbiosAssist,NoV86Assist
|
|
|
|
;
|
|
; Fast System Call entry point
|
|
;
|
|
; At entry:
|
|
; EAX = service number
|
|
; EDX = Pointer to caller's arguments
|
|
; ECX = unused
|
|
; ESP = DPC stack for this processor
|
|
;
|
|
; Create a stack frame like a call to inner privilege then continue
|
|
; in KiSystemService.
|
|
;
|
|
|
|
;
|
|
; Normal entry is at KiFastCallEntry, not KiFastCallEntry2. Entry
|
|
; is via KiFastCallEntry2 if a double fault (trap08) occured and EIP
|
|
; was KiFastCallEntry. This happens if a single step exception occurs
|
|
; on the instruction following SYSENTER instruction because there is
|
|
; no kernel stack fot the debug exception (trap01) to run on.
|
|
;
|
|
; This is NOT a performance path.
|
|
|
|
PUBLIC _KiFastCallEntry2
|
|
_KiFastCallEntry2:
|
|
|
|
ifndef NT_UP
|
|
|
|
mov ecx, KGDT_R0_PCR
|
|
else
|
|
mov ecx, KGDT_R3_TEB OR RPL_MASK
|
|
|
|
endif
|
|
mov fs, ecx
|
|
|
|
mov ecx, PCR[PcPrcbData+PbCurrentThread] ; get current thread address
|
|
;
|
|
; Calculate initial stack pointer from thread initial stack.
|
|
; If this isn't the same as esp0 then we are a VX86 thread and we are rejected
|
|
;
|
|
|
|
mov ecx, [ecx].ThInitialStack
|
|
lea esp, [ecx-(NPX_FRAME_LENGTH + (TsV86Gs - TsHardwareSegSS))]
|
|
mov ecx, PCR[PcTss]
|
|
|
|
cmp esp, [ecx].TssEsp0
|
|
jne Kfsc90
|
|
|
|
|
|
; adjust return address in user mode to renable EFLAGS TF so
|
|
; single step is turned back on.
|
|
|
|
mov ecx, MM_SHARED_USER_DATA_VA+UsSystemCall+fscrOffset+1
|
|
jmp short Kfsc10
|
|
|
|
align 16
|
|
|
|
PUBLIC _KiFastCallEntry
|
|
_KiFastCallEntry proc
|
|
|
|
;
|
|
; Return to the instruction immediately following the sysenter
|
|
; instruction which is at a known location in the shared user
|
|
; data structure (this is so we can dynamically place the right
|
|
; code for the processor at system init).
|
|
;
|
|
|
|
ifndef NT_UP
|
|
|
|
mov ecx, KGDT_R0_PCR
|
|
mov fs, ecx
|
|
|
|
endif ;; NT_UP
|
|
|
|
mov ecx, PCR[PcPrcbData+PbCurrentThread] ; get current thread address
|
|
;
|
|
; Calculate initial stack pointer from thread initial stack.
|
|
; If this isn't the same as esp0 then we are a VX86 thread and we are rejected
|
|
;
|
|
|
|
mov ecx, [ecx].ThInitialStack
|
|
lea esp, [ecx-(NPX_FRAME_LENGTH + (TsV86Gs - TsHardwareSegSS))]
|
|
mov ecx, PCR[PcTss]
|
|
|
|
cmp esp, [ecx].TssEsp0
|
|
jne Kfsc90
|
|
;
|
|
; Set ecx to return address in user mode
|
|
;
|
|
|
|
mov ecx, MM_SHARED_USER_DATA_VA+UsSystemCall+fscrOffset
|
|
Kfsc10:
|
|
push KGDT_R3_DATA OR RPL_MASK ; Push user SS
|
|
push edx ; Push ESP
|
|
pushfd
|
|
push 2 ; Sanitize eflags
|
|
popfd ;
|
|
add edx, 8 ; (edx) -> arguments
|
|
or dword ptr [esp], EFLAGS_INTERRUPT_MASK ; Enable interrupts
|
|
push KGDT_R3_CODE OR RPL_MASK ; Push user CS
|
|
push ecx ; push return address
|
|
|
|
ifndef NT_UP
|
|
|
|
; For the MP case, FS is already loaded above
|
|
|
|
ENTER_SYSCALL kfce_a, kfce_t, NoFSLoad
|
|
jmp _KiSystemServiceRepeat
|
|
|
|
endif ;; NT_UP
|
|
|
|
_KiFastCallEntry endp
|
|
|
|
|
|
|
|
;
|
|
; General System service entrypoint
|
|
;
|
|
|
|
PUBLIC _KiSystemService
|
|
_KiSystemService proc
|
|
|
|
ENTER_SYSCALL kss_a, kss_t ; set up trap frame and save state
|
|
|
|
?FpoValue = 0
|
|
|
|
;
|
|
; (eax) = Service number
|
|
; (edx) = Callers stack pointer
|
|
; (esi) = Current thread address
|
|
;
|
|
; All other registers have been saved and are free.
|
|
;
|
|
; Check if the service number within valid range
|
|
;
|
|
|
|
_KiSystemServiceRepeat:
|
|
mov edi, eax ; copy system service number
|
|
shr edi, SERVICE_TABLE_SHIFT ; isolate service table number
|
|
and edi, SERVICE_TABLE_MASK ;
|
|
mov ecx, edi ; save service table number
|
|
add edi, [esi]+ThServiceTable ; compute service descriptor address
|
|
mov ebx, eax ; save system service number
|
|
and eax, SERVICE_NUMBER_MASK ; isolate service table offset
|
|
|
|
;
|
|
; If the specified system service number is not within range, then attempt
|
|
; to convert the thread to a GUI thread and retry the service dispatch.
|
|
;
|
|
|
|
cmp eax, [edi]+SdLimit ; check if valid service
|
|
jae Kss_ErrorHandler ; if ae, try to convert to GUI thread
|
|
|
|
;
|
|
; If the service is a GUI service and the GDI user batch queue is not empty,
|
|
; then call the appropriate service to flush the user batch.
|
|
;
|
|
|
|
cmp ecx, SERVICE_TABLE_TEST ; test if GUI service
|
|
jne short Kss40 ; if ne, not GUI service
|
|
mov ecx, PCR[PcTeb] ; get current thread TEB address
|
|
xor ebx, ebx ; get number of batched GDI calls
|
|
|
|
KiSystemServiceAccessTeb:
|
|
or ebx, [ecx]+TbGdiBatchCount ; may cause an inpage exception
|
|
|
|
jz short Kss40 ; if z, no batched calls
|
|
push edx ; save address of user arguments
|
|
push eax ; save service number
|
|
call [_KeGdiFlushUserBatch] ; flush GDI user batch
|
|
pop eax ; restore service number
|
|
pop edx ; restore address of user arguments
|
|
|
|
;
|
|
; The arguments are passed on the stack. Therefore they always need to get
|
|
; copied since additional space has been allocated on the stack for the
|
|
; machine state frame. Note that we don't check for the zero argument case -
|
|
; copy is always done regardless of the number of arguments because the
|
|
; zero argument case is very rare.
|
|
;
|
|
|
|
Kss40: inc dword ptr PCR[PcPrcbData+PbSystemCalls] ; system calls
|
|
|
|
if DBG
|
|
mov ecx, [edi]+SdCount ; get count table address
|
|
jecxz Kss45 ; if zero, table not specified
|
|
inc dword ptr [ecx+eax*4] ; increment service count
|
|
Kss45: push dword ptr [esi]+ThApcStateIndex ; (ebp-4)
|
|
push dword ptr [esi]+ThCombinedApcDisable ; (ebp-8)
|
|
|
|
;
|
|
; work around errata 19 which can in some cases cause an
|
|
; extra dword to be moved in the rep movsd below. In the DBG
|
|
; build, this will usually case a bugcheck 1 where ebp-8 is no longer
|
|
; the kernel apc disable count
|
|
;
|
|
|
|
sub esp,4
|
|
|
|
|
|
?FpoValue = ?FpoValue+3
|
|
endif
|
|
|
|
FPOFRAME ?FpoValue, 0
|
|
|
|
mov esi, edx ; (esi)->User arguments
|
|
mov ebx, [edi]+SdNumber ; get argument table address
|
|
xor ecx, ecx
|
|
mov cl, byte ptr [ebx+eax] ; (ecx) = argument size
|
|
mov edi, [edi]+SdBase ; get service table address
|
|
mov ebx, [edi+eax*4] ; (ebx)-> service routine
|
|
sub esp, ecx ; allocate space for arguments
|
|
shr ecx, 2 ; (ecx) = number of argument DWORDs
|
|
mov edi, esp ; (es:edi)->location to receive 1st arg
|
|
cmp esi, _MmUserProbeAddress ; check if user address
|
|
jae kss80 ; if ae, then not user address
|
|
|
|
KiSystemServiceCopyArguments:
|
|
rep movsd ; copy the arguments to top of stack.
|
|
; Since we usually copy more than 3
|
|
; arguments. rep movsd is faster than
|
|
; mov instructions.
|
|
if DBG
|
|
;
|
|
; Check for user mode call into system at elevated IRQL.
|
|
;
|
|
|
|
test byte ptr [ebp]+TsSegCs,MODE_MASK
|
|
jz short kss50a ; kernel mode, skip test
|
|
CurrentIrql
|
|
or al, al ; bogus irql, go bugcheck
|
|
jnz kss100
|
|
kss50a:
|
|
|
|
test _MmInjectUserInpageErrors, 2
|
|
jz short @f
|
|
stdCall _MmTrimProcessMemory, <0>
|
|
jmp short kssdoit
|
|
@@:
|
|
|
|
mov eax,PCR[PcPrcbData+PbCurrentThread]
|
|
mov eax,[eax]+ThApcState+AsProcess
|
|
test dword ptr [eax]+PrFlags,0100000h ; is this a inpage-err process?
|
|
je short @f
|
|
stdCall _MmTrimProcessMemory, <0>
|
|
@@:
|
|
endif
|
|
|
|
;
|
|
; Make actual call to system service
|
|
;
|
|
kssdoit:
|
|
CAPSTARTX <_KiSystemService,ebx>
|
|
call ebx ; call system service
|
|
CAPENDX <_KiSystemService>
|
|
|
|
kss60:
|
|
|
|
if DBG
|
|
mov ecx,PCR[PcPrcbData+PbCurrentThread] ; (ecx)-> Current Thread
|
|
|
|
;
|
|
; Check for return to user mode at elevated IRQL.
|
|
;
|
|
test byte ptr [ebp]+TsSegCs,MODE_MASK
|
|
jz short kss50b
|
|
mov esi, eax
|
|
CurrentIrql
|
|
or al, al
|
|
jnz kss100 ; bogus irql, go bugcheck
|
|
mov eax, esi
|
|
kss50b:
|
|
|
|
;
|
|
; Check that APC state has not changed
|
|
;
|
|
mov edx, [ebp-4]
|
|
cmp dl, [ecx]+ThApcStateIndex
|
|
jne kss120
|
|
|
|
mov edx, [ebp-8]
|
|
cmp edx, [ecx]+ThCombinedApcDisable
|
|
jne kss120
|
|
|
|
endif
|
|
|
|
kss61:
|
|
|
|
;
|
|
; Upon return, (eax)= status code
|
|
;
|
|
|
|
mov esp, ebp ; deallocate stack space for arguments
|
|
|
|
;
|
|
; Restore old trap frame address from the current trap frame.
|
|
;
|
|
|
|
kss70: mov ecx, PCR[PcPrcbData+PbCurrentThread] ; get current thread address
|
|
mov edx, [ebp].TsEdx ; restore previous trap frame address
|
|
mov [ecx].ThTrapFrame, edx ;
|
|
|
|
;
|
|
; System service's private version of KiExceptionExit
|
|
; (Also used by KiDebugService)
|
|
;
|
|
; Check for pending APC interrupts, if found, dispatch to them
|
|
; (saving eax in frame first).
|
|
;
|
|
public _KiServiceExit
|
|
_KiServiceExit:
|
|
|
|
cli ; disable interrupts
|
|
DISPATCH_USER_APC ebp, ReturnCurrentEax
|
|
|
|
;
|
|
; Exit from SystemService
|
|
;
|
|
|
|
EXIT_ALL NoRestoreSegs, NoRestoreVolatile
|
|
|
|
;
|
|
; The address of the argument list is not a user address. If the previous mode
|
|
; is user, then return an access violation as the status of the system service.
|
|
; Otherwise, copy the argument list and execute the system service.
|
|
;
|
|
|
|
kss80: test byte ptr [ebp].TsSegCs, MODE_MASK ; test previous mode
|
|
jz KiSystemServiceCopyArguments ; if z, previous mode kernel
|
|
mov eax, STATUS_ACCESS_VIOLATION ; set service status
|
|
jmp kss60 ;
|
|
|
|
;++
|
|
;
|
|
; _KiServiceExit2 - same as _KiServiceExit BUT the full trap_frame
|
|
; context is restored
|
|
;
|
|
;--
|
|
public _KiServiceExit2
|
|
_KiServiceExit2:
|
|
|
|
cli ; disable interrupts
|
|
DISPATCH_USER_APC ebp
|
|
|
|
;
|
|
; Exit from SystemService
|
|
;
|
|
|
|
EXIT_ALL ; RestoreAll
|
|
|
|
|
|
|
|
|
|
if DBG
|
|
kss100: push PCR[PcIrql] ; put bogus value on stack for dbg
|
|
?FpoValue = ?FpoValue + 1
|
|
FPOFRAME ?FpoValue, 0
|
|
mov byte ptr PCR[PcIrql],0 ; avoid recursive trap
|
|
cli
|
|
|
|
;
|
|
; IRQL_GT_ZERO_AT_SYSTEM_SERVICE Returning to usermode at elevated IRQL.
|
|
;
|
|
; KeBugCheckEx(IRQL_GT_ZERO_AT_SYSTEM_SERVICE,
|
|
; System Call Handler (address of system routine),
|
|
; Irql,
|
|
; 0,
|
|
; 0,
|
|
; );
|
|
;
|
|
stdCall _KeBugCheckEx,<IRQL_GT_ZERO_AT_SYSTEM_SERVICE, ebx, eax, 0, 0>
|
|
|
|
;
|
|
; APC_INDEX_MISMATCH This is probably caused by the system call entering
|
|
; critical regions and not leaving them. This is fatal. Include as
|
|
; much information in the bugcheck as possible.
|
|
;
|
|
; KeBugCheckEx(APC_INDEX_MISMATCH,
|
|
; System Call Handler (address of system routine),
|
|
; Thread->ApcStateIndex << 8 | Saved ApcStateIndex,
|
|
; Thread->CombinedApcDisable,
|
|
; Saved CombinedApcDisable
|
|
; );
|
|
;
|
|
|
|
kss120: mov eax, [ebp]-4
|
|
mov ah, [ecx]+ThApcStateIndex
|
|
stdCall _KeBugCheckEx,<APC_INDEX_MISMATCH, ebx, eax, [ecx]+ThCombinedApcDisable, dword ptr [ebp]-8>
|
|
|
|
endif
|
|
ret
|
|
|
|
;
|
|
; If the sysenter instruction was executed in 16 bit mode, generate
|
|
; an error rather than trying to process the system call. There is
|
|
; no way to return to the correct code in user mode.
|
|
;
|
|
|
|
Kfsc90:
|
|
mov esp, [ecx].TssEsp0
|
|
push 0 ; save VX86 Es, Ds, Fs, Gs
|
|
push 0
|
|
push 0
|
|
push 0
|
|
|
|
push 01bh ; SS
|
|
push 0 ; can't know user esp
|
|
push EFLAGS_INTERRUPT_MASK+EFLAGS_V86_MASK+2h; eflags with VX86 set
|
|
push 01bh ; CS
|
|
push 0 ; don't know original EIP
|
|
jmp _KiTrap06 ; turn exception into illegal op.
|
|
|
|
|
|
_KiSystemService endp
|
|
|
|
;
|
|
; BBT cannot instrument code between this label and BBT_Exclude_Trap_Code_End
|
|
;
|
|
public _BBT_Exclude_Trap_Code_Begin
|
|
_BBT_Exclude_Trap_Code_Begin equ $
|
|
int 3
|
|
|
|
;
|
|
; Fast path NtGetTickCount
|
|
;
|
|
|
|
align 16
|
|
ENTER_DR_ASSIST kitx_a, kitx_t,NoAbiosAssist
|
|
PUBLIC _KiGetTickCount
|
|
_KiGetTickCount proc
|
|
|
|
cmp [esp+4], KGDT_R3_CODE OR RPL_MASK
|
|
jnz short @f
|
|
|
|
Kgtc00:
|
|
mov eax,dword ptr cs:[_KeTickCount]
|
|
mul dword ptr cs:[_ExpTickCountMultiplier]
|
|
shrd eax,edx,24 ; compute resultant tick count
|
|
|
|
iretd
|
|
@@:
|
|
;
|
|
; if v86 mode, we dont handle it
|
|
;
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
|
|
test byte ptr [esp+8+2], EFLAGS_V86_MASK/010000h
|
|
jnz ktgc20
|
|
|
|
;
|
|
; if kernel mode, must be get tick count
|
|
;
|
|
.errnz (MODE_MASK AND 0FFFFFF00h)
|
|
test byte ptr [esp+4], MODE_MASK
|
|
jz short Kgtc00
|
|
|
|
;
|
|
; else check if the caller is USER16
|
|
; if eax = ebp = 0xf0f0f0f0 it is get-tick-count
|
|
; if eax = ebp = 0xf0f0f0f1 it is set-ldt-entry
|
|
;
|
|
|
|
cmp eax, ebp ; if eax != ebp, not USER16
|
|
jne ktgc20
|
|
|
|
and eax, 0fffffff0h
|
|
cmp eax, 0f0f0f0f0h
|
|
jne ktgc20
|
|
|
|
cmp ebp, 0f0f0f0f0h ; Is it user16 gettickcount?
|
|
je short Kgtc00 ; if z, yes
|
|
|
|
|
|
cmp ebp, 0f0f0f0f1h ; If this is setldt entry
|
|
jne ktgc20 ; if nz, we don't know what
|
|
; it is.
|
|
|
|
;
|
|
; The idea here is that user16 can call 32 bit api to
|
|
; update LDT entry without going through the penalty
|
|
; of DPMI. For Daytona beta.
|
|
;
|
|
|
|
push 0 ; push dummy error code
|
|
ENTER_TRAP kitx_a, kitx_t
|
|
sti
|
|
|
|
xor eax, eax
|
|
mov ebx, [ebp+TsEbx]
|
|
mov ecx, [ebp+TsEcx]
|
|
mov edx, [ebp+TsEdx]
|
|
stdCall _NtSetLdtEntries <ebx, ecx, edx, eax, eax, eax>
|
|
mov [ebp+TsEax], eax
|
|
and dword ptr [ebp+TsEflags], 0FFFFFFFEH ; clear carry flag
|
|
cmp eax, 0 ; success?
|
|
je short ktgc10
|
|
|
|
or dword ptr [ebp+TsEflags], 1 ; set carry flag
|
|
ktgc10:
|
|
jmp _KiExceptionExit
|
|
|
|
ktgc20:
|
|
;
|
|
; We need to *trap* this int 2a. For exception, the eip should
|
|
; point to the int 2a instruction not the instruction after it.
|
|
;
|
|
|
|
sub word ptr [esp], 2
|
|
push 0
|
|
jmp _KiTrap0D
|
|
|
|
_KiGetTickCount endp
|
|
|
|
page ,132
|
|
subttl "Return from User Mode Callback"
|
|
;++
|
|
;
|
|
; NTSTATUS
|
|
; NtCallbackReturn (
|
|
; IN PVOID OutputBuffer OPTIONAL,
|
|
; IN ULONG OutputLength,
|
|
; IN NTSTATUS Status
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function returns from a user mode callout to the kernel mode
|
|
; caller of the user mode callback function.
|
|
;
|
|
; N.B. This service uses a nonstandard calling sequence.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; OutputBuffer (ecx) - Supplies an optional pointer to an output buffer.
|
|
;
|
|
; OutputLength (edx) - Supplies the length of the output buffer.
|
|
;
|
|
; Status (esp + 4) - Supplies the status value returned to the caller of
|
|
; the callback function.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; If the callback return cannot be executed, then an error status is
|
|
; returned. Otherwise, the specified callback status is returned to
|
|
; the caller of the callback function.
|
|
;
|
|
; N.B. This function returns to the function that called out to user
|
|
; mode is a callout is currently active.
|
|
;
|
|
;--
|
|
|
|
align 16
|
|
PUBLIC _KiCallbackReturn
|
|
_KiCallbackReturn proc
|
|
|
|
push fs ; save segment register
|
|
push ecx ; save buffer address and return status
|
|
push eax ;
|
|
mov ecx,KGDT_R0_PCR ; set PCR segment number
|
|
mov fs,cx ;
|
|
mov eax,PCR[PcPrcbData + PbCurrentThread] ; get current thread address
|
|
mov ecx,[eax].ThCallbackStack ; get callback stack address
|
|
test ecx,ecx ; check if callback active
|
|
jz _KiCbExit ; if z, no callback active
|
|
mov edi,[esp] + 4 ; set output buffer address
|
|
mov esi,edx ; set output buffer length
|
|
mov ebp,[esp] + 0 ; set return status
|
|
|
|
;
|
|
; N.B. The following code is entered with:
|
|
;
|
|
; eax - The address of the current thread.
|
|
; ecx - The callback stack address.
|
|
; edi - The output buffer address.
|
|
; esi - The output buffer length.
|
|
; ebp - The callback service status.
|
|
;
|
|
; Restore the trap frame and callback stack addresses,
|
|
; store the output buffer address and length, and set the service status.
|
|
;
|
|
|
|
cld ; clear the direction flag
|
|
mov ebx,[ecx].CuOutBf ; get address to store output buffer
|
|
mov [ebx],edi ; store output buffer address
|
|
mov ebx,[ecx].CuOutLn ; get address to store output length
|
|
mov [ebx],esi ; store output buffer length
|
|
mov esi,[eax].ThInitialStack ; get source NPX save area address
|
|
mov ebx,[ecx] ; get previous initial stack address
|
|
mov [eax].ThInitialStack,ebx ; restore initial stack address
|
|
sub ebx,NPX_FRAME_LENGTH ; compute destination NPX save area
|
|
sub esi,NPX_FRAME_LENGTH ; compute source NPX save area
|
|
|
|
; All we want to do is to copy ControlWord, StatusWord and TagWord from
|
|
; the source NPX save area. So we always copy first 3 dwords, irrespective
|
|
; of the fact whether the save was done using fxsave or fnsave.
|
|
|
|
mov edx,[esi].FpControlWord ; copy NPX state to previous frame
|
|
mov [ebx].FpControlWord,edx ;
|
|
mov edx,[esi].FpStatusWord ;
|
|
mov [ebx].FpStatusWord,edx ;
|
|
mov edx,[esi].FpTagWord ;
|
|
mov [ebx].FpTagWord,edx ;
|
|
mov edx,[esi].FxMXCsr ;
|
|
mov [ebx].FxMXCsr,edx ;
|
|
mov edx,[esi].FpCr0NpxState ;
|
|
mov [ebx].FpCr0NpxState,edx ;
|
|
lea esp, [ecx]+4 ; trim stack back to callback frame
|
|
pop edx
|
|
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
|
|
test byte ptr [edx]+TsEFlags+2,EFLAGS_V86_MASK/010000h ; is this a V86 frame?
|
|
mov [eax].ThTrapFrame, edx ; restore current trap frame address
|
|
mov edx,PCR[PcTss] ; get address of task switch segment
|
|
jne @f
|
|
sub ebx, TsV86Gs-TsHardwareSegSs ; bias missing V86 fields
|
|
@@: mov [edx].TssEsp0,ebx ; restore kernel entry stack address
|
|
test [eax].ThDebugActive, 0ffh ; Are debug registers active?
|
|
.errnz (DR7_ACTIVE AND 0FFFFFF00h)
|
|
jnz short _KiCbDebugRegs ; restore kernel Debug Registers
|
|
_KiCbRet:
|
|
mov edx, [eax].ThTrapFrame ; Get current trap frame address
|
|
test [edx].TsDr7, DR7_ACTIVE
|
|
setnz byte ptr [eax].ThDebugActive ; Set debug active to match saved DR7
|
|
sti ; enable interrupts
|
|
pop [eax].ThCallbackStack ; restore callback stack address
|
|
mov eax,ebp ; set callback service status
|
|
|
|
;
|
|
; Restore nonvolatile registers, clean call parameters from stack, and
|
|
; return to callback caller.
|
|
;
|
|
|
|
pop edi ; restore nonvolatile registers
|
|
pop esi ;
|
|
pop ebx ;
|
|
pop ebp ;
|
|
pop edx ; save return address
|
|
add esp,8 ; remove parameters from stack
|
|
jmp edx ; return to callback caller
|
|
|
|
;
|
|
; Restore Kernel Mode debug registers.
|
|
;
|
|
|
|
_KiCbDebugRegs:
|
|
mov edi, PCR[PcPrcb]
|
|
xor ecx, ecx ; Make Dr7 safe
|
|
mov dr7, ecx
|
|
mov ebx, [edi].PbProcessorState.PsSpecialRegisters.SrKernelDr0
|
|
mov ecx, [edi].PbProcessorState.PsSpecialRegisters.SrKernelDr1
|
|
mov dr0, ebx
|
|
mov dr1, ecx
|
|
mov ebx, [edi].PbProcessorState.PsSpecialRegisters.SrKernelDr2
|
|
mov ecx, [edi].PbProcessorState.PsSpecialRegisters.SrKernelDr3
|
|
mov dr2, ebx
|
|
mov dr3, ecx
|
|
mov ebx, [edi].PbProcessorState.PsSpecialRegisters.SrKernelDr6
|
|
mov ecx, [edi].PbProcessorState.PsSpecialRegisters.SrKernelDr7
|
|
mov dr6, ebx
|
|
mov dr7, ecx
|
|
jmp short _KiCbRet
|
|
|
|
;
|
|
; Restore segment register, set systerm service status, and return.
|
|
;
|
|
|
|
_KiCbExit: ;
|
|
add esp, 2 * 4 ; remove saved registers from stack
|
|
pop fs ; restore segment register
|
|
mov eax,STATUS_NO_CALLBACK_ACTIVE ; set service status
|
|
iretd ;
|
|
|
|
_KiCallbackReturn endp
|
|
|
|
;
|
|
; Fast path Nt/Zw SetLowWaitHighThread
|
|
;
|
|
|
|
ENTER_DR_ASSIST kslwh_a, kslwh_t,NoAbiosAssist,NoV86Assist
|
|
align 16
|
|
PUBLIC _KiSetLowWaitHighThread
|
|
_KiSetLowWaitHighThread proc
|
|
|
|
ENTER_SYSCALL kslwh_a, kslwh_t ; Set up trap frame
|
|
|
|
mov eax,STATUS_NO_EVENT_PAIR ; set service status
|
|
mov edx,[ebp].TsEdx ; restore old trap frame address
|
|
mov [esi].ThTrapFrame,edx ;
|
|
jmp _KiServiceExit
|
|
|
|
_KiSetLowWaitHighThread endp
|
|
|
|
page ,132
|
|
subttl "Common Trap Exit"
|
|
|
|
|
|
|
|
;++
|
|
;
|
|
; KiUnexpectedInterruptTail
|
|
;
|
|
; Routine Description:
|
|
; This function is jumped to by an IDT entry who has no interrupt
|
|
; handler.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; (esp) - Dword, vector
|
|
; (esp+4) - Processor generated IRet frame
|
|
;
|
|
;--
|
|
|
|
ENTER_DR_ASSIST kui_a, kui_t
|
|
|
|
public _KiUnexpectedInterruptTail
|
|
_KiUnexpectedInterruptTail proc
|
|
ENTER_INTERRUPT kui_a, kui_t, PassDwordParm
|
|
|
|
inc dword ptr PCR[PcPrcbData+PbInterruptCount]
|
|
|
|
mov ebx, [esp] ; get vector & leave it on the stack
|
|
sub esp, 4 ; make space for OldIrql
|
|
|
|
; esp - ptr to OldIrql
|
|
; ebx - Vector
|
|
; HIGH_LEVEL - Irql
|
|
stdCall _HalBeginSystemInterrupt, <HIGH_LEVEL,ebx,esp>
|
|
or eax, eax
|
|
jnz short kui10
|
|
|
|
;
|
|
; spurious interrupt
|
|
;
|
|
add esp, 8
|
|
jmp kee99
|
|
|
|
kui10:
|
|
if DBG
|
|
push dword ptr [esp+4] ; Vector #
|
|
push offset FLAT:BadInterruptMessage
|
|
call _DbgPrint ; display unexpected interrupt message
|
|
add esp, 8
|
|
endif
|
|
;
|
|
; end this interrupt
|
|
;
|
|
INTERRUPT_EXIT
|
|
|
|
_KiUnexpectedInterruptTail endp
|
|
|
|
|
|
|
|
;++
|
|
;
|
|
; N.B. KiExceptionExit and Kei386EoiHelper are identical and have
|
|
; been combined.
|
|
;
|
|
; KiExceptionExit
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This code is transfered to at the end of the processing for
|
|
; an exception. Its function is to restore machine state, and
|
|
; continue thread execution. If control is returning to user mode
|
|
; and there is a user APC pending, then control is transfered to
|
|
; the user APC delivery routine.
|
|
;
|
|
; N.B. It is assumed that this code executes at IRQL zero or APC_LEVEL.
|
|
; Therefore page faults and access violations can be taken.
|
|
;
|
|
; NOTE: This code is jumped to, not called.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; (ebp) -> base of trap frame.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
;++
|
|
;
|
|
; Kei386EoiHelper
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This code is transfered to at the end of an interrupt. (via the
|
|
; exit_interrupt macro). It checks for user APC dispatching and
|
|
; performs the exit_all for the interrupt.
|
|
;
|
|
; NOTE: This code is jumped to, not called.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; (esp) -> base of trap frame.
|
|
; interrupts are disabled
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
align 4
|
|
public _KiExceptionExit
|
|
cPublicProc Kei386EoiHelper, 0
|
|
_KiExceptionExit:
|
|
.FPO (0, 0, 0, 0, 0, FPO_TRAPFRAME)
|
|
|
|
cli ; disable interrupts
|
|
DISPATCH_USER_APC ebp
|
|
|
|
;
|
|
; Exit from Exception
|
|
;
|
|
|
|
kee99:
|
|
|
|
EXIT_ALL ,,NoPreviousMode
|
|
|
|
stdENDP Kei386EoiHelper
|
|
|
|
|
|
;
|
|
; V86ExitHelp
|
|
;
|
|
; Restore volatiles for V86 mode, and move seg regs
|
|
;
|
|
; Arguments:
|
|
;
|
|
; esp = ebp = &TrapFrame
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None, returns to previous mode using IRETD.
|
|
;
|
|
|
|
align dword
|
|
V86ExitHelp:
|
|
|
|
add esp,TsEdx
|
|
pop edx
|
|
pop ecx
|
|
pop eax
|
|
|
|
lea esp, [ebp]+TsEdi ; Skip PreMode, ExceptList and fs
|
|
|
|
pop edi ; restore non-volatiles
|
|
pop esi
|
|
pop ebx
|
|
pop ebp
|
|
|
|
;
|
|
; Esp MUST point to the Error Code on the stack. Because we use it to
|
|
; store the entering esp.
|
|
;
|
|
|
|
cmp word ptr [esp+8], 80h ; check for abios code segment?
|
|
ja AbiosExitHelp
|
|
|
|
v86eh90:
|
|
|
|
add esp, 4 ; remove error code from trap frame
|
|
iretd ; return
|
|
|
|
Abios_ExitHelp_Target2:
|
|
|
|
;
|
|
; End of ABIOS stack check
|
|
;
|
|
;
|
|
; AbiosExit:
|
|
;
|
|
; This routine remaps current 32bit stack to 16bit stack at return
|
|
; from interrupt time and returns from interrupt.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; (esp) -> TrapFrame
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None, returns to previous mode using IRETD.
|
|
; Note: May use above exit to remove error code from stack.
|
|
;
|
|
|
|
|
|
align dword
|
|
AbiosExitHelp:
|
|
cmp word ptr [esp+2], 0 ; (esp+2) = Low word of error code
|
|
jz short v86eh90
|
|
cmp word ptr [esp], 0 ; (esp) = High word of error code
|
|
jnz short v86eh90
|
|
|
|
shr dword ptr [esp], 16
|
|
mov word ptr [esp + 2], KGDT_STACK16
|
|
lss sp, dword ptr [esp]
|
|
movzx esp, sp
|
|
iretd ; return
|
|
|
|
page , 132
|
|
subttl "trap processing"
|
|
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; _KiTrapxx - protected mode trap entry points
|
|
;
|
|
; These entry points are for internally generated exceptions,
|
|
; such as a general protection fault. They do not handle
|
|
; external hardware interrupts, or user software interrupts.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; On entry the stack looks like:
|
|
;
|
|
; [ss]
|
|
; [esp]
|
|
; eflags
|
|
; cs
|
|
; eip
|
|
; ss:sp-> [error]
|
|
;
|
|
; The cpu saves the previous SS:ESP, eflags, and CS:EIP on
|
|
; the new stack if there was a privilige transition. If no
|
|
; priviledge level transition occurred, then there is no
|
|
; saved SS:ESP.
|
|
;
|
|
; Some exceptions save an error code, others do not.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
|
|
page , 132
|
|
subttl "Macro to dispatch exception"
|
|
|
|
;++
|
|
;
|
|
; Macro Description:
|
|
;
|
|
; This macro allocates exception record on stack, sets up exception
|
|
; record using specified parameters and finally sets up arguments
|
|
; and calls _KiDispatchException.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; ExcepCode - Exception code to put into exception record
|
|
; ExceptFlags - Exception flags to put into exception record
|
|
; ExceptRecord - Associated exception record
|
|
; ExceptAddress - Addr of instruction which the hardware exception occurs
|
|
; NumParms - Number of additional parameters
|
|
; ParameterList - the additional parameter list
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
DISPATCH_EXCEPTION macro ExceptCode, ExceptFlags, ExceptRecord, ExceptAddress,\
|
|
NumParms, ParameterList
|
|
local de10, de20
|
|
|
|
.FPO ( ExceptionRecordSize/4+NumParms, 0, 0, 0, 0, FPO_TRAPFRAME )
|
|
|
|
; Set up exception record for raising exception
|
|
|
|
?i = 0
|
|
sub esp, ExceptionRecordSize + NumParms * 4
|
|
; allocate exception record
|
|
mov dword ptr [esp]+ErExceptionCode, ExceptCode
|
|
; set up exception code
|
|
mov dword ptr [esp]+ErExceptionFlags, ExceptFlags
|
|
; set exception flags
|
|
mov dword ptr [esp]+ErExceptionRecord, ExceptRecord
|
|
; set associated exception record
|
|
mov dword ptr [esp]+ErExceptionAddress, ExceptAddress
|
|
mov dword ptr [esp]+ErNumberParameters, NumParms
|
|
; set number of parameters
|
|
IRP z, <ParameterList>
|
|
mov dword ptr [esp]+(ErExceptionInformation+?i*4), z
|
|
?i = ?i + 1
|
|
ENDM
|
|
|
|
; set up arguments and call _KiDispatchException
|
|
|
|
mov ecx, esp ; (ecx)->exception record
|
|
mov eax,[ebp]+TsSegCs
|
|
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
|
|
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
|
|
jz de10
|
|
|
|
mov eax,0FFFFh
|
|
de10: and eax,MODE_MASK
|
|
|
|
; 1 - first chance TRUE
|
|
; eax - PreviousMode
|
|
; ebp - trap frame addr
|
|
; 0 - Null exception frame
|
|
; ecx - exception record addr
|
|
|
|
; dispatchexception as appropriate
|
|
stdCall _KiDispatchException, <ecx, 0, ebp, eax, 1>
|
|
|
|
mov esp, ebp ; (esp) -> trap frame
|
|
|
|
ENDM
|
|
|
|
page , 132
|
|
subttl "dispatch exception"
|
|
|
|
;++
|
|
;
|
|
; CommonDispatchException
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine allocates exception record on stack, sets up exception
|
|
; record using specified parameters and finally sets up arguments
|
|
; and calls _KiDispatchException.
|
|
;
|
|
; NOTE:
|
|
;
|
|
; The purpose of this routine is to save code space. Use this routine
|
|
; only if:
|
|
; 1. ExceptionRecord is NULL
|
|
; 2. ExceptionFlags is 0
|
|
; 3. Number of parameters is less or equal than 3.
|
|
;
|
|
; Otherwise, you should use DISPATCH_EXCEPTION macro to set up your special
|
|
; exception record.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; (eax) = ExcepCode - Exception code to put into exception record
|
|
; (ebx) = ExceptAddress - Addr of instruction which the hardware exception occurs
|
|
; (ecx) = NumParms - Number of additional parameters
|
|
; (edx) = Parameter1
|
|
; (esi) = Parameter2
|
|
; (edi) = Parameter3
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
CommonDispatchException0Args:
|
|
xor ecx, ecx ; zero arguments
|
|
call CommonDispatchException
|
|
|
|
CommonDispatchException1Arg0d:
|
|
xor edx, edx ; zero edx
|
|
CommonDispatchException1Arg:
|
|
mov ecx, 1 ; one argument
|
|
call CommonDispatchException ; there is no return
|
|
|
|
CommonDispatchException2Args0d:
|
|
xor edx, edx ; zero edx
|
|
CommonDispatchException2Args:
|
|
mov ecx, 2 ; two arguments
|
|
call CommonDispatchException ; there is no return
|
|
|
|
public CommonDispatchException
|
|
align dword
|
|
CommonDispatchException proc
|
|
cPublicFpo 0, ExceptionRecordLength/4
|
|
;
|
|
; Set up exception record for raising exception
|
|
;
|
|
|
|
sub esp, ExceptionRecordLength
|
|
; allocate exception record
|
|
mov dword ptr [esp]+ErExceptionCode, eax
|
|
; set up exception code
|
|
xor eax, eax
|
|
mov dword ptr [esp]+ErExceptionFlags, eax
|
|
; set exception flags
|
|
mov dword ptr [esp]+ErExceptionRecord, eax
|
|
; set associated exception record
|
|
mov dword ptr [esp]+ErExceptionAddress, ebx
|
|
mov dword ptr [esp]+ErNumberParameters, ecx
|
|
; set number of parameters
|
|
cmp ecx, 0
|
|
je short de00
|
|
|
|
lea ebx, [esp + ErExceptionInformation]
|
|
mov [ebx], edx
|
|
mov [ebx+4], esi
|
|
mov [ebx+8], edi
|
|
de00:
|
|
;
|
|
; set up arguments and call _KiDispatchException
|
|
;
|
|
|
|
mov ecx, esp ; (ecx)->exception record
|
|
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
|
|
jz short de10
|
|
|
|
mov eax,0FFFFh
|
|
jmp short de20
|
|
|
|
de10: mov eax,[ebp]+TsSegCs
|
|
de20: and eax,MODE_MASK
|
|
|
|
; 1 - first chance TRUE
|
|
; eax - PreviousMode
|
|
; ebp - trap frame addr
|
|
; 0 - Null exception frame
|
|
; ecx - exception record addr
|
|
|
|
stdCall _KiDispatchException,<ecx, 0, ebp, eax, 1>
|
|
|
|
mov esp, ebp ; (esp) -> trap frame
|
|
jmp _KiExceptionExit
|
|
|
|
CommonDispatchException endp
|
|
|
|
page , 132
|
|
subttl "Macro to verify base trap frame"
|
|
|
|
;++
|
|
;
|
|
; Macro Description:
|
|
;
|
|
; This macro verifies the base trap frame is intact.
|
|
;
|
|
; It is possible while returning to UserMode that we take an exception.
|
|
; Any exception which may block, such as not-present, needs to verify
|
|
; that the base trap frame is not partially dismantled.
|
|
;
|
|
; Arguments:
|
|
; The macro MUST be used directly after ENTER_TRAP macro
|
|
; as it assumes all sorts of stuff about ESP!
|
|
;
|
|
; Return Value:
|
|
;
|
|
; If the base frame was incomplete it is totally restored and the
|
|
; return EIP of the current frame is (virtually) backed up to the
|
|
; begining of the exit_all - the effect is that the base frame
|
|
; will be completely exited again. (ie, the exit_all of the base
|
|
; frame is atomic, if it's interrupted we restore it and do it over).
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
MODIFY_BASE_TRAP_FRAME macro
|
|
local vbfdone
|
|
|
|
mov edi, PCR[PcPrcbData+PbCurrentThread] ; Get current thread
|
|
lea eax, [esp]+KTRAP_FRAME_LENGTH + NPX_FRAME_LENGTH ; adjust for base frame
|
|
sub eax, [edi].ThInitialStack ; Bias out this stack
|
|
je short vbfdone ; if eq, then this is the base frame
|
|
|
|
cmp eax, -TsEflags ; second frame is only this big
|
|
jc short vbfdone ; is stack deeper then 2 frames?
|
|
; yes, then done
|
|
;
|
|
; Stack usage is not exactly one frame, and it's not large enough
|
|
; to be two complete frames; therefore, we may have a partial base
|
|
; frame. (unless it's a kernel thread)
|
|
;
|
|
; See if this is a kernel thread as kernel threads don't have a base
|
|
; frame (and therefore don't need correcting).
|
|
;
|
|
|
|
mov eax, PCR[PcTeb]
|
|
or eax, eax ; Any Teb?
|
|
jle short vbfdone ; Br if zero or kernel thread address
|
|
|
|
call KiRestoreBaseFrame
|
|
|
|
align 4
|
|
vbfdone:
|
|
ENDM
|
|
|
|
;++ KiRestoreBaseFrame
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Only to be used from MODIFY_BASE_TRAP_FRAME macro.
|
|
; Makes lots of assumptions about esp & trap frames
|
|
;
|
|
; Arguments:
|
|
;
|
|
; Stack:
|
|
; +-------------------------+
|
|
; | |
|
|
; | |
|
|
; | Npx save area |
|
|
; | |
|
|
; | |
|
|
; +-------------------------+
|
|
; | (possible mvdm regs) |
|
|
; +-------------------------+ <- PCR[PcInitialStack]
|
|
; | |
|
|
; | Partial base trap frame |
|
|
; | |
|
|
; | ------------+
|
|
; +------------/ | <- Esp @ time of current frame. Location
|
|
; | | where base trap frame is incomplete
|
|
; | Completed 'current' |
|
|
; | trap frame |
|
|
; | |
|
|
; | |
|
|
; | |
|
|
; | |
|
|
; +-------------------------+ <- EBP
|
|
; | return address (dword) |
|
|
; +-------------------------+ <- current ESP
|
|
; | |
|
|
; | |
|
|
;
|
|
; Return:
|
|
;
|
|
; Stack:
|
|
; +-------------------------+
|
|
; | |
|
|
; | |
|
|
; | Npx save area |
|
|
; | |
|
|
; | |
|
|
; +-------------------------+
|
|
; | (possible mvdm regs) |
|
|
; +-------------------------+ <- PCR[PcInitialStack]
|
|
; | |
|
|
; | Base trap frame |
|
|
; | |
|
|
; | |
|
|
; | |
|
|
; | |
|
|
; | |
|
|
; +-------------------------+ <- return esp & ebp
|
|
; | |
|
|
; | Current trap frame |
|
|
; | | EIP set to begining of
|
|
; | | exit_all code
|
|
; | |
|
|
; | |
|
|
; | |
|
|
; +-------------------------+ <- EBP, ESP
|
|
; | |
|
|
; | |
|
|
;
|
|
;--
|
|
|
|
KiRestoreBaseFrame proc
|
|
pop ebx ; Get return address
|
|
IF DBG
|
|
mov eax, [esp].TsEip ; EIP of trap
|
|
|
|
;
|
|
; This code is to handle a very specific problem of a not-present
|
|
; fault during an exit_all. If it's not this problem then stop.
|
|
;
|
|
cmp word ptr [eax], POP_GS
|
|
je short @f
|
|
cmp byte ptr [eax], POP_ES
|
|
je short @f
|
|
cmp byte ptr [eax], POP_DS
|
|
je short @f
|
|
cmp word ptr [eax], POP_FS
|
|
je short @f
|
|
cmp byte ptr [eax], IRET_OP
|
|
je short @f
|
|
int 3
|
|
@@:
|
|
ENDIF
|
|
;
|
|
; Move current trap frame out of the way to make space for
|
|
; a full base trap frame
|
|
;
|
|
mov eax, PCR[PcPrcbData+PbCurrentThread] ; Get current thread
|
|
mov edi, [eax].ThInitialStack
|
|
sub edi, NPX_FRAME_LENGTH + KTRAP_FRAME_LENGTH + TsEFlags + 4 ; (edi) = bottom of target
|
|
mov esi, esp ; (esi) = bottom of source
|
|
mov esp, edi ; make space before copying the data
|
|
mov ebp, edi ; update location of our trap frame
|
|
push ebx ; put return address back on stack
|
|
|
|
mov ecx, (TsEFlags+4)/4 ; # of dword to move
|
|
rep movsd ; Move current trap frame
|
|
|
|
;
|
|
; Part of the base frame was destroyed when the current frame was
|
|
; originally pushed. Now that the current frame has been moved out of
|
|
; the way restore the base frame. We know that any missing data from
|
|
; the base frame was reloaded into it's corrisponding registers which
|
|
; were then pushed into the current frame. So we can restore the missing
|
|
; data from the current frame.
|
|
;
|
|
mov ecx, esi ; Location of esp at time of fault
|
|
mov edi, [eax].ThInitialStack
|
|
sub edi, NPX_FRAME_LENGTH + KTRAP_FRAME_LENGTH ; (edi) = base trap frame
|
|
mov ebx, edi
|
|
|
|
sub ecx, edi ; (ecx) = # of bytes which were
|
|
; removed from base frame before
|
|
; trap occured
|
|
IF DBG
|
|
test ecx, 3
|
|
jz short @f ; assume dword alignments only
|
|
int 3
|
|
@@:
|
|
ENDIF
|
|
mov esi, ebp ; (esi) = current frame
|
|
shr ecx, 2 ; copy in dwords
|
|
rep movsd
|
|
;
|
|
; The base frame is restored. Instead of backing EIP up to the
|
|
; start of the interrupted EXIT_ALL, we simply move the EIP to a
|
|
; well known EXIT_ALL. However, this causes a couple of problems
|
|
; since this exit_all restores every register whereas the original
|
|
; one may not. So:
|
|
;
|
|
; - When exiting from a system call, eax is normally returned by
|
|
; simply not restoring it. We 'know' that the current trap frame's
|
|
; EAXs is always the correct one to return. (We know this because
|
|
; exit_all always restores eax (if it's going to) before any other
|
|
; instruction which may cause a fault).
|
|
;
|
|
; - Not all enter's push the PreviousPreviousMode. Since this is
|
|
; the base trap frame we know that this must be UserMode.
|
|
;
|
|
mov eax, [ebp].TsEax ; make sure correct
|
|
mov [ebx].TsEax, eax ; eax is in base frame
|
|
mov byte ptr [ebx].TsPreviousPreviousMode, 1 ; UserMode
|
|
|
|
mov [ebp].TsEbp, ebx
|
|
mov [ebp].TsEip, offset _KiServiceExit2 ; ExitAll which
|
|
|
|
; restores everything
|
|
;
|
|
; Since we backed up Eip we need to reset some of the kernel selector
|
|
; values in case they were already restored by the attempted base frame pop
|
|
;
|
|
mov dword ptr [ebp].TsSegDs, KGDT_R3_DATA OR RPL_MASK
|
|
mov dword ptr [ebp].TsSegEs, KGDT_R3_DATA OR RPL_MASK
|
|
mov dword ptr [ebp].TsSegFs, KGDT_R0_PCR
|
|
|
|
;
|
|
; The backed up EIP is before interrupts were disabled. Re-enable
|
|
; interrupts for the current trap frame
|
|
;
|
|
or [ebp].TsEFlags, EFLAGS_INTERRUPT_MASK
|
|
|
|
ret
|
|
|
|
KiRestoreBaseFrame endp
|
|
|
|
page ,132
|
|
subttl "Divide error processing"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle divide error fault.
|
|
;
|
|
; The divide error fault occurs if a DIV or IDIV instructions is
|
|
; executed with a divisor of 0, or if the quotient is too big to
|
|
; fit in the result operand.
|
|
;
|
|
; An INTEGER DIVIDED BY ZERO exception will be raised for the fault.
|
|
; If the fault occurs in kernel mode, the system will be terminated.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; At entry, the saved CS:EIP point to the faulting instruction.
|
|
; No error code is provided with the divide error.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
ENTER_DR_ASSIST kit0_a, kit0_t,NoAbiosAssist
|
|
align dword
|
|
public _KiTrap00
|
|
_KiTrap00 proc
|
|
|
|
push 0 ; push dummy error code
|
|
ENTER_TRAP kit0_a, kit0_t
|
|
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
|
|
jnz Kt0040 ; trap occured in V86 mode
|
|
|
|
test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previous mode = USER
|
|
jz short Kt0000
|
|
|
|
cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
|
|
jne Kt0020
|
|
;
|
|
; Set up exception record for raising Integer_Divided_by_zero exception
|
|
; and call _KiDispatchException
|
|
;
|
|
|
|
Kt0000:
|
|
|
|
if DBG
|
|
test [ebp]+TsEFlags, EFLAGS_INTERRUPT_MASK ; faulted with
|
|
jnz short @f ; interrupts disabled?
|
|
|
|
xor eax, eax
|
|
mov esi, [ebp]+TsEip ; [esi] = faulting instruction
|
|
stdCall _KeBugCheckEx,<IRQL_NOT_LESS_OR_EQUAL,eax,-1,eax,esi>
|
|
@@:
|
|
endif
|
|
|
|
sti
|
|
|
|
|
|
;
|
|
; Flat mode
|
|
;
|
|
; The intel processor raises a divide by zero exception on DIV instructions
|
|
; which overflow. To be compatible with other processors we want to
|
|
; return overflows as such and not as divide by zero's. The operand
|
|
; on the div instruction is tested to see if it's zero or not.
|
|
;
|
|
stdCall _Ki386CheckDivideByZeroTrap,<ebp>
|
|
mov ebx, [ebp]+TsEip ; (ebx)-> faulting instruction
|
|
jmp CommonDispatchException0Args ; Won't return
|
|
|
|
Kt0010:
|
|
;
|
|
; 16:16 mode
|
|
;
|
|
sti
|
|
mov ebx, [ebp]+TsEip ; (ebx)-> faulting instruction
|
|
mov eax, STATUS_INTEGER_DIVIDE_BY_ZERO
|
|
jmp CommonDispatchException0Args ; never return
|
|
|
|
Kt0020:
|
|
; Check to see if this process is a vdm
|
|
mov ebx,PCR[PcPrcbData+PbCurrentThread]
|
|
mov ebx,[ebx]+ThApcState+AsProcess
|
|
cmp dword ptr [ebx]+PrVdmObjects,0 ; is this a vdm process?
|
|
je Kt0010
|
|
|
|
Kt0040:
|
|
stdCall _Ki386VdmReflectException_A, <0>
|
|
|
|
or al,al
|
|
jz short Kt0010 ; couldn't reflect, gen exception
|
|
jmp _KiExceptionExit
|
|
|
|
_KiTrap00 endp
|
|
|
|
|
|
page ,132
|
|
subttl "Debug Exception"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle debug exception.
|
|
;
|
|
; The processor triggers this exception for any of the following
|
|
; conditions:
|
|
;
|
|
; 1. Instruction breakpoint fault.
|
|
; 2. Data address breakpoint trap.
|
|
; 3. General detect fault.
|
|
; 4. Single-step trap.
|
|
; 5. Task-switch breadkpoint trap.
|
|
;
|
|
;
|
|
; Arguments:
|
|
;
|
|
; At entry, the values of saved CS and EIP depend on whether the
|
|
; exception is a fault or a trap.
|
|
; No error code is provided with the divide error.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
align dword
|
|
;
|
|
; We branch here to handle a trap01 fromt he fast system call entry
|
|
; we need to propogate the trap bit to the return eflags etc and
|
|
; continue.
|
|
;
|
|
|
|
|
|
Kt0100: mov [ebp].TsEip, _KiFastCallEntry2
|
|
and dword ptr [ebp].TsEflags, NOT EFLAGS_TF
|
|
jmp _KiExceptionExit ; join common code
|
|
|
|
ENTER_DR_ASSIST kit1_a, kit1_t, NoAbiosAssist
|
|
align dword
|
|
public _KiTrap01
|
|
_KiTrap01 proc
|
|
|
|
; Set up machine state frame for displaying
|
|
|
|
push 0 ; push dummy error code
|
|
ENTER_TRAP kit1_a, kit1_t
|
|
|
|
;
|
|
; Inspect old EIP in case this is a single stepped
|
|
; sysenter instruction.
|
|
;
|
|
|
|
mov ecx, [ebp]+TsEip
|
|
cmp ecx, _KiFastCallEntry
|
|
je Kt0100
|
|
|
|
;
|
|
; See if were doing the fast bap optimization of touching user memory
|
|
; with ints disabled. If we are then ignore data breakpoints.
|
|
;
|
|
;
|
|
if FAST_BOP
|
|
|
|
cmp dword ptr PCR[PcVdmAlert], 0
|
|
|
|
jne Kt01VdmAlert
|
|
|
|
endif
|
|
|
|
|
|
;
|
|
; If caller is user mode, we want interrupts back on.
|
|
; . all relevent state has already been saved
|
|
; . user mode code always runs with ints on
|
|
;
|
|
; If caller is kernel mode, we want them off!
|
|
; . some state still in registers, must prevent races
|
|
; . kernel mode code can run with ints off
|
|
;
|
|
;
|
|
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
|
|
jnz kit01_30 ; fault occured in V86 mode => Usermode
|
|
|
|
.errnz (MODE_MASK AND 0FFFFFF00h)
|
|
test byte ptr [ebp]+TsSegCs,MODE_MASK
|
|
jz kit01_10
|
|
|
|
cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
|
|
jne kit01_30
|
|
kit01_05:
|
|
sti
|
|
kit01_10:
|
|
|
|
;
|
|
; Set up exception record for raising single step exception
|
|
; and call _KiDispatchException
|
|
;
|
|
|
|
kit01_20:
|
|
and dword ptr [ebp]+TsEflags, not EFLAGS_TF
|
|
mov ebx, [ebp]+TsEip ; (ebx)-> faulting instruction
|
|
mov eax, STATUS_SINGLE_STEP
|
|
jmp CommonDispatchException0Args ; Never return
|
|
|
|
kit01_30:
|
|
|
|
; Check to see if this process is a vdm
|
|
|
|
mov ebx,PCR[PcPrcbData+PbCurrentThread]
|
|
mov ebx,[ebx]+ThApcState+AsProcess
|
|
cmp dword ptr [ebx]+PrVdmObjects,0 ; is this a vdm process?
|
|
je kit01_05
|
|
|
|
stdCall _Ki386VdmReflectException_A, <01h>
|
|
test ax,0FFFFh
|
|
jz Kit01_20
|
|
jmp _KiExceptionExit
|
|
|
|
if FAST_BOP
|
|
|
|
Kt01VdmAlert:
|
|
|
|
;
|
|
; If a DEBUG trap occured while we are in VDM alert mode (processing
|
|
; v86 trap without building trap frame), we will restore all the
|
|
; registers and return to its recovery routine.
|
|
;
|
|
|
|
mov eax, PCR[PcVdmAlert]
|
|
mov dword ptr PCR[PcVdmAlert], 0
|
|
|
|
mov [ebp].TsEip, eax
|
|
mov esp,ebp ; (esp) -> trap frame
|
|
jmp _KiExceptionExit ; join common code
|
|
|
|
endif ; FAST_BOP
|
|
|
|
_KiTrap01 endp
|
|
|
|
page ,132
|
|
subttl "Nonmaskable Interrupt"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle Nonmaskable interrupt.
|
|
;
|
|
; An NMI is typically used to signal serious system conditions
|
|
; such as bus time-out, memory parity error, and so on.
|
|
;
|
|
; Upon detection of the NMI, the system will be terminated, ie a
|
|
; bugcheck will be raised, no matter what previous mode is.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; No error code is provided with the error.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
; ENTER_DR_ASSIST kit2_a, kit2_t, NoAbiosAssist
|
|
align dword
|
|
public _KiTrap02
|
|
_KiTrap02 proc
|
|
.FPO (1, 0, 0, 0, 0, 2)
|
|
cli
|
|
|
|
;
|
|
; Set CR3, the I/O map base address, and LDT segment selector
|
|
; in the old TSS since they are not set on context
|
|
; switches. These values will be needed to return to the
|
|
; interrupted task.
|
|
|
|
mov eax, PCR[PcTss] ; get old TSS address
|
|
mov ecx, PCR[PcPrcbData+PbCurrentThread] ; get thread address
|
|
mov edi, [ecx].ThApcState.AsProcess ; get process address
|
|
mov ecx, [edi]+PrDirectoryTableBase ; get directory base
|
|
mov [eax]+TssCR3, ecx ; set previous cr3
|
|
|
|
mov cx, [edi]+PrIopmOffset ; get IOPM offset
|
|
mov [eax]+TssIoMapBase, cx ; set IOPM offset
|
|
|
|
mov ecx, [edi]+PrLdtDescriptor ; get LDT descriptor
|
|
test ecx, ecx ; does task use LDT?
|
|
jz @f
|
|
mov cx, KGDT_LDT
|
|
|
|
@@: mov [eax]+TssLDT, cx ; set LDT into old TSS
|
|
|
|
;
|
|
; Update the TSS pointer in the PCR to point to the NMI TSS
|
|
; (which is what we're running on, or else we wouldn't be here)
|
|
;
|
|
push dword ptr PCR[PcTss]
|
|
|
|
mov eax, PCR[PcGdt]
|
|
mov ch, [eax+KGDT_NMI_TSS+KgdtBaseHi]
|
|
mov cl, [eax+KGDT_NMI_TSS+KgdtBaseMid]
|
|
shl ecx, 16
|
|
mov cx, [eax+KGDT_NMI_TSS+KgdtBaseLow]
|
|
mov PCR[PcTss], ecx
|
|
|
|
;
|
|
; Clear Nested Task bit in EFLAGS
|
|
;
|
|
pushfd
|
|
and [esp], not 04000h
|
|
popfd
|
|
|
|
;
|
|
; Clear the busy bit in the TSS selector
|
|
;
|
|
mov ecx, PCR[PcGdt]
|
|
lea eax, [ecx] + KGDT_NMI_TSS
|
|
mov byte ptr [eax+5], 089h ; 32bit, dpl=0, present, TSS32, not busy
|
|
|
|
;
|
|
; Allow only one processor at a time to enter the NMI path. While
|
|
; waiting, spin on a LOCAL data structure (to avoid cache thrashing
|
|
; during a crashdump which can cause the dump to hang), and poll for
|
|
; Freeze IPI requests so that the correct state for this processor
|
|
; appears in the crashdump.
|
|
;
|
|
;
|
|
; (1) make a trap frame,
|
|
; (2) acquire lock
|
|
; (3) while not lock owner
|
|
; (4) if (IPI_FREEZE)
|
|
; (5) KeFreezeExecutionTarget(&TrapFrame, NULL)
|
|
; (6) let the HAL have it
|
|
; (7) release lock to next in line
|
|
;
|
|
; Build trap frame from the data in the previous TSS.
|
|
;
|
|
|
|
mov eax, [esp] ; get saved TSS address
|
|
push 0 ; build trap frame, starting with
|
|
push 0 ; faked V86Gs thru V86Es
|
|
push 0
|
|
push 0
|
|
push [eax].TssSs ; copy fields from TSS to
|
|
push [eax].TssEsp ; trap frame.
|
|
push [eax].TssEflags
|
|
push [eax].TssCs
|
|
push [eax].TssEip
|
|
push 0
|
|
push [eax].TssEbp
|
|
push [eax].TssEbx
|
|
push [eax].TssEsi
|
|
push [eax].TssEdi
|
|
push [eax].TssFs
|
|
push PCR[PcExceptionList]
|
|
push -1 ; previous mode
|
|
push [eax].TssEax
|
|
push [eax].TssEcx
|
|
push [eax].TssEdx
|
|
push [eax].TssDs
|
|
push [eax].TssEs
|
|
push [eax].TssGs
|
|
push 0 ; fake out the debug registers
|
|
push 0
|
|
push 0
|
|
push 0
|
|
push 0
|
|
push 0
|
|
push 0 ; temp ESP
|
|
push 0 ; temp CS
|
|
push 0
|
|
push 0
|
|
push [eax].TssEip
|
|
push [eax].TssEbp
|
|
mov ebp, esp ; ebp -> TrapFrame
|
|
|
|
.FPO ( 0, 0, 0, 0, 0, FPO_TRAPFRAME )
|
|
|
|
stdCall _KiHandleNmi ; Rundown the list of NMI handlers
|
|
or al, al ; did someone handle it?
|
|
jne short Kt0210 ; jif handled
|
|
|
|
ifndef NT_UP
|
|
|
|
;
|
|
; Acquire the NMI Lock. If it is not available, check for freeze
|
|
; execution in case another processor is trying to dump memory.
|
|
;
|
|
|
|
lea eax, KiLockNMI ; build in stack queued spin lock.
|
|
push eax
|
|
push 0
|
|
mov ecx, esp
|
|
mov edx, ebp
|
|
fstCall KiAcquireQueuedSpinLockCheckForFreeze
|
|
|
|
endif
|
|
|
|
;
|
|
; This processor now owns the NMI lock. See if the HAL will
|
|
; handle the NMI. If the HAL does not handle it, it will NOT
|
|
; return, so if we get back here, it's handled.
|
|
;
|
|
|
|
stdCall _HalHandleNMI,<0>
|
|
|
|
;
|
|
; We're back, therefore the Hal has dealt with the NMI.
|
|
;
|
|
|
|
ifndef NT_UP
|
|
|
|
mov ecx, esp ; release queued spinlock.
|
|
fstCall KeReleaseQueuedSpinLockFromDpcLevel
|
|
add esp, 8 ; free queued spinlock context
|
|
|
|
endif
|
|
|
|
Kt0210: add esp, KTRAP_FRAME_LENGTH ; free trap frame and qlock
|
|
|
|
pop dword ptr PCR[PcTss] ; restore PcTss
|
|
|
|
mov ecx, PCR[PcGdt]
|
|
lea eax, [ecx] + KGDT_TSS
|
|
mov byte ptr [eax+5], 08bh ; 32bit, dpl=0, present, TSS32, *busy*
|
|
|
|
pushfd ; Set Nested Task bit in EFLAGS
|
|
or [esp], 04000h ; so iretd will do a task switch
|
|
popfd
|
|
|
|
iretd ; Return from NMI
|
|
jmp _KiTrap02 ; in case we NMI again
|
|
|
|
_KiTrap02 endp
|
|
|
|
page ,132
|
|
subttl "DebugService Breakpoint"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle INT 2d DebugService
|
|
;
|
|
; The trap is caused by an INT 2d. This is used instead of a
|
|
; BREAKPOINT exception so that parameters can be passed for the
|
|
; requested debug service. A BREAKPOINT instruction is assumed
|
|
; to be right after the INT 2d - this allows this code to share code
|
|
; with the breakpoint handler.
|
|
;
|
|
; Arguments:
|
|
; eax - ServiceClass - which call is to be performed
|
|
; ecx - Arg1 - generic first argument
|
|
; edx - Arg2 - generic second argument
|
|
; ebx - Arg3 - generic third argument
|
|
;
|
|
;--
|
|
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
|
|
ENTER_DR_ASSIST kids_a, kids_t, NoAbiosAssist
|
|
align dword
|
|
public _KiDebugService
|
|
_KiDebugService proc
|
|
push 0 ; push dummy error code
|
|
ENTER_TRAP kids_a, kids_t
|
|
; sti ; *NEVER sti here*
|
|
|
|
inc dword ptr [ebp]+TsEip
|
|
mov eax, [ebp]+TsEax ; ServiceClass
|
|
mov ecx, [ebp]+TsEcx ; Arg1 (already loaded)
|
|
mov edx, [ebp]+TsEdx ; Arg2 (already loaded)
|
|
jmp KiTrap03DebugService
|
|
|
|
_KiDebugService endp
|
|
|
|
page ,132
|
|
subttl "Single Byte INT3 Breakpoin"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle INT 3 breakpoint.
|
|
;
|
|
; The trap is caused by a single byte INT 3 instruction. A
|
|
; BREAKPOINT exception with additional parameter indicating
|
|
; READ access is raised for this trap if previous mode is user.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; At entry, the saved CS:EIP point to the instruction immediately
|
|
; following the INT 3 instruction.
|
|
; No error code is provided with the error.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
|
|
ENTER_DR_ASSIST kit3_a, kit3_t, NoAbiosAssist
|
|
align dword
|
|
public _KiTrap03
|
|
_KiTrap03 proc
|
|
push 0 ; push dummy error code
|
|
ENTER_TRAP kit3_a, kit3_t
|
|
|
|
cmp ds:_PoHiberInProgress, 0
|
|
jnz short kit03_01
|
|
|
|
lock inc ds:_KiHardwareTrigger ; trip hardware analyzer
|
|
|
|
kit03_01:
|
|
mov eax, BREAKPOINT_BREAK
|
|
|
|
KiTrap03DebugService:
|
|
;
|
|
; If caller is user mode, we want interrupts back on.
|
|
; . all relevent state has already been saved
|
|
; . user mode code always runs with ints on
|
|
;
|
|
; If caller is kernel mode, we want them off!
|
|
; . some state still in registers, must prevent races
|
|
; . kernel mode code can run with ints off
|
|
;
|
|
;
|
|
; Arguments:
|
|
; eax - ServiceClass - which call is to be performed
|
|
; ecx - Arg1 - generic first argument
|
|
; edx - Arg2 - generic second argument
|
|
;
|
|
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
|
|
jnz kit03_30 ; fault occured in V86 mode => Usermode
|
|
|
|
.errnz (MODE_MASK AND 0FFFFFF00h)
|
|
test byte ptr [ebp]+TsSegCs,MODE_MASK
|
|
jz kit03_10
|
|
|
|
cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
|
|
jne kit03_30
|
|
|
|
kit03_05:
|
|
sti
|
|
kit03_10:
|
|
|
|
|
|
;
|
|
; Set up exception record and arguments for raising breakpoint exception
|
|
;
|
|
|
|
mov esi, ecx ; ExceptionInfo 2
|
|
mov edi, edx ; ExceptionInfo 3
|
|
mov edx, eax ; ExceptionInfo 1
|
|
|
|
mov ebx, [ebp]+TsEip
|
|
dec ebx ; (ebx)-> int3 instruction
|
|
mov ecx, 3
|
|
mov eax, STATUS_BREAKPOINT
|
|
call CommonDispatchException ; Never return
|
|
|
|
kit03_30:
|
|
; Check to see if this process is a vdm
|
|
|
|
mov ebx,PCR[PcPrcbData+PbCurrentThread]
|
|
mov ebx,[ebx]+ThApcState+AsProcess
|
|
cmp dword ptr [ebx]+PrVdmObjects,0 ; is this a vdm process?
|
|
je kit03_05
|
|
|
|
stdCall _Ki386VdmReflectException_A, <03h>
|
|
test ax,0FFFFh
|
|
jz Kit03_10
|
|
|
|
jmp _KiExceptionExit
|
|
|
|
_KiTrap03 endp
|
|
|
|
page ,132
|
|
subttl "Integer Overflow"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle INTO overflow.
|
|
;
|
|
; The trap occurs when the processor encounters an INTO instruction
|
|
; and the OF flag is set.
|
|
;
|
|
; An INTEGER_OVERFLOW exception will be raised for this fault.
|
|
;
|
|
; N.B. i386 will not generate fault if only OF flag is set.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; At entry, the saved CS:EIP point to the instruction immediately
|
|
; following the INTO instruction.
|
|
; No error code is provided with the error.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
|
|
ENTER_DR_ASSIST kit4_a, kit4_t, NoAbiosAssist
|
|
align dword
|
|
public _KiTrap04
|
|
_KiTrap04 proc
|
|
|
|
push 0 ; push dummy error code
|
|
ENTER_TRAP kit4_a, kit4_t
|
|
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
|
|
jnz short Kt0430 ; in a vdm, reflect to vdm
|
|
|
|
.errnz (MODE_MASK AND 0FFFFFF00h)
|
|
test byte ptr [ebp]+TsSegCs,MODE_MASK
|
|
jz short Kt0410 ; in kernel mode, gen exception
|
|
|
|
cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
|
|
jne short Kt0420 ; maybe in a vdm
|
|
|
|
; Set up exception record and arguments for raising exception
|
|
|
|
Kt0410: sti
|
|
mov ebx, [ebp]+TsEip ; (ebx)-> instr. after INTO
|
|
dec ebx ; (ebx)-> INTO
|
|
mov eax, STATUS_INTEGER_OVERFLOW
|
|
jmp CommonDispatchException0Args ; Never return
|
|
|
|
Kt0430:
|
|
stdCall _Ki386VdmReflectException_A, <04h>
|
|
test al,0fh
|
|
jz Kt0410 ; couldn't reflect, gen exception
|
|
jmp _KiExceptionExit
|
|
|
|
Kt0420:
|
|
; Check to see if this process is a vdm
|
|
|
|
mov ebx,PCR[PcPrcbData+PbCurrentThread]
|
|
mov ebx,[ebx]+ThApcState+AsProcess
|
|
cmp dword ptr [ebx]+PrVdmObjects,0 ; is this a vdm process?
|
|
je Kt0410
|
|
jmp Kt0430
|
|
|
|
_KiTrap04 endp
|
|
|
|
page ,132
|
|
subttl "Bound Check fault"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle bound check fault.
|
|
;
|
|
; The bound check fault occurs if a BOUND instruction finds that
|
|
; the tested value is outside the specified range.
|
|
;
|
|
; For bound check fault, an ARRAY BOUND EXCEEDED exception will be
|
|
; raised.
|
|
; For kernel mode exception, it causes system to be terminated.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; At entry, the saved CS:EIP point to the faulting BOUND
|
|
; instruction.
|
|
; No error code is provided with the error.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
ENTER_DR_ASSIST kit5_a, kit5_t, NoAbiosAssist
|
|
align dword
|
|
public _KiTrap05
|
|
_KiTrap05 proc
|
|
|
|
push 0 ; push dummy error code
|
|
ENTER_TRAP kit5_a, kit5_t
|
|
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
|
|
jnz short Kt0530 ; fault in V86 mode
|
|
|
|
.errnz (MODE_MASK AND 0FFFFFF00h)
|
|
test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previous mode = USER
|
|
jnz short Kt0500 ; if nz, previous mode = user
|
|
mov eax, EXCEPTION_BOUND_CHECK ; (eax) = exception type
|
|
jmp _KiSystemFatalException ; go terminate the system
|
|
|
|
|
|
kt0500: cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
|
|
jne short Kt0520 ; maybe in a vdm
|
|
;
|
|
; set exception record and arguments and call _KiDispatchException
|
|
;
|
|
Kt0510: sti
|
|
mov ebx, [ebp]+TsEip ; (ebx)->BOUND instruction
|
|
mov eax, STATUS_ARRAY_BOUNDS_EXCEEDED
|
|
jmp CommonDispatchException0Args ; Won't return
|
|
|
|
Kt0520:
|
|
; Check to see if this process is a vdm
|
|
|
|
mov ebx,PCR[PcPrcbData+PbCurrentThread]
|
|
mov ebx,[ebx]+ThApcState+AsProcess
|
|
cmp dword ptr [ebx]+PrVdmObjects,0 ; is this a vdm process?
|
|
je Kt0510
|
|
|
|
Kt0530:
|
|
|
|
stdCall _Ki386VdmReflectException_A, <05h>
|
|
test al,0fh
|
|
jz Kt0510 ; couldn't reflect, gen exception
|
|
jmp _KiExceptionExit
|
|
|
|
_KiTrap05 endp
|
|
|
|
page ,132
|
|
subttl "Invalid OP code"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle invalid op code fault.
|
|
;
|
|
; The invalid opcode fault occurs if CS:EIP point to a bit pattern which
|
|
; is not recognized as an instruction by the x86. This may happen if:
|
|
;
|
|
; 1. the opcode is not a valid 80386 instruction
|
|
; 2. a register operand is specified for an instruction which requires
|
|
; a memory operand
|
|
; 3. the LOCK prefix is used on an instruction that cannot be locked
|
|
;
|
|
; If fault occurs in USER mode:
|
|
; an Illegal_Instruction exception will be raised
|
|
; if fault occurs in KERNEL mode:
|
|
; system will be terminated.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; At entry, the saved CS:EIP point to the first byte of the invalid
|
|
; instruction.
|
|
; No error code is provided with the error.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:FLAT, SS:NOTHING, ES:NOTHING
|
|
|
|
ENTER_DR_ASSIST kit6_a, kit6_t, NoAbiosAssist,, kit6_v
|
|
align dword
|
|
public _KiTrap06
|
|
_KiTrap06 proc
|
|
|
|
;
|
|
; KiTrap06 is performance critical for VDMs and rarely executed in
|
|
; native mode. So this routine is tuned for the VDM case.
|
|
;
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
|
|
test byte ptr [esp]+8h+2,EFLAGS_V86_MASK/010000h
|
|
jz Kt060i
|
|
|
|
if FAST_BOP
|
|
FAST_V86_TRAP_6
|
|
endif
|
|
|
|
Kt6SlowBop:
|
|
push 0 ; push dummy error code
|
|
ENTER_TRAPV86 kit6_a, kit6_v
|
|
Kt6SlowBop1:
|
|
Kt06VMpf:
|
|
|
|
;
|
|
; If the current process is NOT a VDM just hand it an
|
|
; illegal instruction exception.
|
|
;
|
|
|
|
mov ecx,PCR[PcPrcbData+PbCurrentThread]
|
|
mov ecx,[ecx]+ThApcState+AsProcess
|
|
cmp dword ptr [ecx]+PrVdmObjects,0 ; if not a vdm process,
|
|
je Kt0635 ; then deliver exception
|
|
|
|
;
|
|
; Raise Irql to APC level before enabling interrupts to prevent
|
|
; a setcontext from editing the trapframe.
|
|
;
|
|
|
|
RaiseIrql APC_LEVEL
|
|
push eax ; Save OldIrql
|
|
if DBG
|
|
cmp eax, PASSIVE_LEVEL
|
|
je @f
|
|
int 3
|
|
@@:
|
|
endif
|
|
sti
|
|
|
|
;
|
|
; Call VdmDispatchBop to try and handle the opcode immediately.
|
|
; If it returns FALSE (meaning too complicated for us to quickly parse)
|
|
; then reflect the opcode off to the ntvdm monitor to handle it.
|
|
;
|
|
|
|
stdCall _VdmDispatchBop, <ebp>
|
|
|
|
;
|
|
; If the operation was processed directly above then return back now.
|
|
;
|
|
|
|
test al,0fh
|
|
jnz short Kt061i
|
|
|
|
;
|
|
; The operation could not be processed directly above so reflect it
|
|
; back to the ntvdm monitor now.
|
|
;
|
|
|
|
|
|
stdCall _Ki386VdmReflectException,<6>
|
|
|
|
test al,0fh
|
|
jnz Kt061i
|
|
pop ecx ; (TOS) = OldIrql
|
|
LowerIrql ecx
|
|
jmp Kt0635
|
|
Kt061i:
|
|
pop ecx ; (TOS) = OldIrql
|
|
LowerIrql ecx
|
|
cli
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
|
|
jz Kt062i
|
|
|
|
;
|
|
; EXIT_TRAPv86 does not exit if a user mode apc has switched
|
|
; the context from V86 mode to flat mode (VDM monitor context)
|
|
;
|
|
|
|
EXIT_TRAPV86
|
|
|
|
Kt062i:
|
|
jmp _KiExceptionExit
|
|
|
|
Kt060i:
|
|
|
|
;
|
|
; Non-v86 (user or kernel) executing code arrives here for
|
|
; invalid opcode traps.
|
|
;
|
|
|
|
push 0 ; Push dummy error code
|
|
ENTER_TRAP kit6_a, kit6_t
|
|
Kt06pf:
|
|
|
|
test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previous mode = USER
|
|
jz short Kt0635 ; if z, kernel mode - go dispatch exception
|
|
|
|
;
|
|
; UserMode. Did the fault happen in a vdm running in protected mode?
|
|
;
|
|
|
|
cmp word ptr [ebp]+TsSegCs, KGDT_R3_CODE OR RPL_MASK
|
|
jz short kt0605 ; normal 32-bit mode so give exception
|
|
|
|
;
|
|
; The code segment is not a normal flat 32-bit entity, so see if
|
|
; this process is a vdm. If so, then try to handle it. If not,
|
|
; give this process an exception.
|
|
;
|
|
|
|
mov ebx,PCR[PcPrcbData+PbCurrentThread]
|
|
mov ebx,[ebx]+ThApcState+AsProcess
|
|
cmp dword ptr [ebx]+PrVdmObjects,0 ; is this a vdm process?
|
|
jne Kt0650
|
|
|
|
kt0605:
|
|
|
|
;
|
|
; Invalid Opcode exception could be either INVALID_LOCK_SEQUENCE or
|
|
; ILLEGAL_INSTRUCTION.
|
|
;
|
|
|
|
mov eax, [ebp]+TsSegCs
|
|
mov esi, [ebp]+TsEip
|
|
|
|
sti
|
|
|
|
mov ecx, MAX_INSTRUCTION_PREFIX_LENGTH
|
|
|
|
;
|
|
; Set up an exception handler in case we fault
|
|
; while reading the user mode instruction.
|
|
;
|
|
|
|
push es
|
|
|
|
push ebp ; pass trapframe to handler
|
|
push offset FLAT:Kt6_ExceptionHandler
|
|
; set up exception registration record
|
|
push PCR[PcExceptionList]
|
|
mov PCR[PcExceptionList], esp
|
|
|
|
; (es:esi) -> address of faulting instruction
|
|
|
|
mov es, ax
|
|
|
|
@@:
|
|
mov al, byte ptr es:[esi] ; (al)= instruction byte
|
|
cmp al, MI_LOCK_PREFIX ; Is it a lock prefix?
|
|
je short Kt0640 ; Yes, raise Invalid_lock exception
|
|
add esi, 1
|
|
loop short @b ; keep on looping
|
|
|
|
pop PCR[PcExceptionList]
|
|
add esp, 8 ; clear stack
|
|
|
|
Kt0630:
|
|
pop es
|
|
|
|
;
|
|
; Set up exception record for raising Illegal instruction exception
|
|
;
|
|
|
|
Kt0635:
|
|
sti
|
|
mov ebx, [ebp]+TsEip ; (ebx)-> invalid instruction
|
|
mov eax, STATUS_ILLEGAL_INSTRUCTION
|
|
jmp CommonDispatchException0Args ; Won't return
|
|
|
|
;
|
|
; Set up exception record for raising Invalid lock sequence exception
|
|
;
|
|
|
|
Kt0640:
|
|
pop PCR[PcExceptionList]
|
|
add esp, 8 ; clear stack
|
|
pop es
|
|
|
|
mov ebx, [ebp]+TsEip ; (ebx)-> invalid instruction
|
|
mov eax, STATUS_INVALID_LOCK_SEQUENCE
|
|
jmp CommonDispatchException0Args ; Won't return
|
|
|
|
Kt0650:
|
|
|
|
; Raise Irql to APC level before enabling interrupts
|
|
RaiseIrql APC_LEVEL
|
|
push eax ; SaveOldIrql
|
|
sti
|
|
|
|
stdCall _VdmDispatchBop, <ebp>
|
|
|
|
test al,0fh
|
|
jnz short Kt0660
|
|
|
|
stdCall _Ki386VdmReflectException,<6>
|
|
test al,0fh
|
|
jnz Kt0660
|
|
pop ecx ; (TOS) = OldIrql
|
|
LowerIrql ecx
|
|
jmp short Kt0635
|
|
|
|
Kt0660:
|
|
pop ecx ; (TOS) = OldIrql
|
|
LowerIrql ecx
|
|
jmp _KiExceptionExit
|
|
|
|
_KiTrap06 endp
|
|
|
|
;
|
|
; Error and exception blocks for KiTrap06
|
|
;
|
|
|
|
Kt6_ExceptionHandler proc
|
|
|
|
;
|
|
; WARNING: Here we directly unlink the exception handler from the
|
|
; exception registration chain. NO unwind is performed.
|
|
;
|
|
|
|
mov esp, [esp+8] ; (esp)-> ExceptionList
|
|
pop PCR[PcExceptionList]
|
|
add esp, 4 ; pop out except handler
|
|
pop ebp ; (ebp)-> trap frame
|
|
|
|
test dword ptr [ebp].TsSegCs, MODE_MASK ; if premode=kernel
|
|
jnz Kt0630 ; nz, prevmode=user, go return
|
|
|
|
;
|
|
; Raise bugcheck if prevmode=kernel
|
|
;
|
|
|
|
stdCall _KeBugCheck, <KMODE_EXCEPTION_NOT_HANDLED>
|
|
Kt6_ExceptionHandler endp
|
|
|
|
page ,132
|
|
subttl "Coprocessor Not Avalaible"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle Coprocessor not available exception.
|
|
;
|
|
; If we are REALLY emulating the FPU, the trap 07 vector is edited
|
|
; to point directly at the emulator's entry point. So this code is
|
|
; only hit when FPU hardware DOES exist.
|
|
;
|
|
; The current thread's coprocessor state is loaded into the
|
|
; coprocessor. If the coprocessor has a different thread's state
|
|
; in it (UP only) it is first saved away. The thread is then continued.
|
|
; Note: the thread's state may contian the TS bit - In this case the
|
|
; code loops back to the top of the Trap07 handler. (which is where
|
|
; we would end up if we let the thread return to user code anyway).
|
|
;
|
|
; If the thread's NPX context is in the coprocessor and we hit a Trap07
|
|
; there is an NPX error which needs to be processed. If the trap was
|
|
; from usermode the error is dispatched. If the trap was from kernelmode
|
|
; the error is remembered, but we clear CR0 so the kernel code can
|
|
; continue. We can do this because the kernel mode code will restore
|
|
; CR0 (and set TS) to signal a delayed error for this thread.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; At entry, the saved CS:EIP point to the first byte of the faulting
|
|
; instruction.
|
|
; No error code is provided with the error.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
|
|
ENTER_DR_ASSIST kit7_a, kit7_t, NoAbiosAssist
|
|
align dword
|
|
public _KiTrap07
|
|
_KiTrap07 proc
|
|
|
|
push 0 ; push dummy error code
|
|
ENTER_TRAP kit7_a, kit7_t
|
|
|
|
Kt0700:
|
|
mov eax, PCR[PcPrcbData+PbCurrentThread]
|
|
mov ecx, [eax].ThInitialStack ; (ecx) -> top of kernel stack
|
|
sub ecx, NPX_FRAME_LENGTH
|
|
cli ; don't context switch
|
|
.errnz (CR0_EM AND 0FFFFFF00h)
|
|
test byte ptr [ecx].FpCr0NpxState,CR0_EM
|
|
jnz Kt07140
|
|
|
|
Kt0701: cmp byte ptr [eax].ThNpxState, NPX_STATE_LOADED
|
|
mov ebx, cr0
|
|
je Kt0710
|
|
|
|
;
|
|
; Trap occured and this thread's NPX state is not loaded. Load it now
|
|
; and resume the application. If someone else's state is in the coprocessor
|
|
; (uniprocessor implementation only) then save it first.
|
|
;
|
|
|
|
and ebx, NOT (CR0_MP+CR0_TS+CR0_EM)
|
|
mov cr0, ebx ; allow frstor (& fnsave) to work
|
|
|
|
ifdef NT_UP
|
|
Kt0702:
|
|
mov edx, PCR[PcPrcbData+PbNpxThread] ; Owner of NPX state
|
|
or edx, edx ; NULL?
|
|
jz Kt0704 ; Yes - skip save
|
|
|
|
;
|
|
; Due to an hardware errata we need to know that the coprocessor
|
|
; doesn't generate an error condition once interrupts are disabled and
|
|
; trying to perform an fnsave which could wait for the error condition
|
|
; to be handled.
|
|
;
|
|
; The fix for this errata is that we "know" that the coprocessor is
|
|
; being used by a different thread then the one which may have caused
|
|
; the error condition. The round trip time to swap to a new thread
|
|
; is longer then ANY floating point instruction. We therefore know
|
|
; that any possible coprocessor error has already occured and been
|
|
; handled.
|
|
;
|
|
mov esi,[edx].ThInitialStack
|
|
sub esi, NPX_FRAME_LENGTH ; Space for NPX_FRAME
|
|
|
|
test byte ptr _KeI386FxsrPresent, 1 ; Is FXSR feature present
|
|
jz short Kt0703a
|
|
FXSAVE_ESI
|
|
jmp short Kt0703b
|
|
Kt0703a:
|
|
fnsave [esi] ; Save thread's coprocessor state
|
|
Kt0703b:
|
|
mov byte ptr [edx].ThNpxState, NPX_STATE_NOT_LOADED
|
|
Kt0704:
|
|
endif
|
|
|
|
;
|
|
; Load current thread's coprocessor state into the coprocessor
|
|
;
|
|
; (eax) - CurrentThread
|
|
; (ecx) - CurrentThread's NPX save area
|
|
; (ebx) - CR0
|
|
; (ebp) - trap frame
|
|
; Interrupts disabled
|
|
;
|
|
|
|
;
|
|
; frstor might generate a NPX exception if there's an error image being
|
|
; loaded. The handler will simply set the TS bit for this context an iret.
|
|
;
|
|
|
|
test byte ptr _KeI386FxsrPresent, 1 ; Is FXSR feature present
|
|
jz short Kt0704b
|
|
|
|
ifndef NT_UP
|
|
if 0 ; FpNpxSavedCpu is broken - disable it
|
|
;
|
|
; We need not load the NPX state if
|
|
; - PCR[PbNpxThread] matches new thread AND
|
|
; - FpNpxSavedCpu matches the current processor
|
|
|
|
mov edx, PCR[PcSelfPcr]
|
|
cmp [edx+PcPrcbData+PbNpxThread], eax
|
|
jne Kt0704a
|
|
|
|
cmp dword ptr [ecx].FpNpxSavedCpu, edx
|
|
jne short Kt0704a
|
|
jmp short Kt0704c
|
|
Kt0704a:
|
|
mov dword ptr [ecx].FpNpxSavedCpu, edx ; Remember processor
|
|
endif
|
|
endif
|
|
FXRSTOR_ECX ; reload NPX context
|
|
jmp short Kt0704c
|
|
Kt0704b:
|
|
frstor [ecx] ; reload NPX context
|
|
|
|
Kt0704c:
|
|
mov byte ptr [eax].ThNpxState, NPX_STATE_LOADED
|
|
mov PCR[PcPrcbData+PbNpxThread], eax ; owner of coprocessors state
|
|
|
|
sti ; Allow interrupts & context switches
|
|
nop ; sti needs one cycle
|
|
|
|
cmp dword ptr [ecx].FpCr0NpxState, 0
|
|
jz _KiExceptionExit ; nothing to set, skip CR0 reload
|
|
|
|
;
|
|
; Note: we have to get the CR0 value again to insure that we have the
|
|
; correct state for TS. We may have context switched since
|
|
; the last move from CR0, and our npx state may have been moved off
|
|
; of the npx.
|
|
;
|
|
cli
|
|
if DBG
|
|
test dword ptr [ecx].FpCr0NpxState, NOT (CR0_MP+CR0_EM+CR0_TS)
|
|
jnz short Kt07dbg1
|
|
endif
|
|
mov ebx,CR0
|
|
or ebx, [ecx].FpCr0NpxState
|
|
mov cr0, ebx ; restore thread's CR0 NPX state
|
|
sti
|
|
.errnz (CR0_TS AND 0FFFFFF00h)
|
|
test bl, CR0_TS ; Setting TS? (delayed error)
|
|
jz _KiExceptionExit ; No - continue
|
|
|
|
clts
|
|
jmp Kt0700 ; Dispatch delayed exception
|
|
if DBG
|
|
Kt07dbg1: int 3
|
|
Kt07dbg2: int 3
|
|
Kt07dbg3: int 3
|
|
sti
|
|
jmp short $-2
|
|
endif
|
|
|
|
Kt0705:
|
|
;
|
|
; A Trap07 or Trap10 has occured from a ring 0 ESCAPE instruction. This
|
|
; may occur when trying to load the coprocessors state. These
|
|
; code paths rely on Cr0NpxState to signal a delayed error (not CR0) - we
|
|
; set CR0_TS in Cr0NpxState to get a delayed error, and make sure CR0 CR0_TS
|
|
; is not set so the R0 ESC instruction(s) can complete.
|
|
;
|
|
; (ecx) - CurrentThread's NPX save area
|
|
; (ebp) - trap frame
|
|
; Interrupts disabled
|
|
;
|
|
|
|
if DBG
|
|
mov eax, cr0 ; Did we fault because some bit in CR0
|
|
test eax, (CR0_TS+CR0_MP+CR0_EM)
|
|
jnz short Kt07dbg3
|
|
endif
|
|
|
|
or dword ptr [ecx].FpCr0NpxState, CR0_TS ; signal a delayed error
|
|
|
|
cmp dword ptr [ebp]+TsEip, Kt0704b ; Is this fault on reload a thread's context?
|
|
jne short Kt0716 ; No, dispatch exception
|
|
|
|
add dword ptr [ebp]+TsEip, 3 ; Skip frstor ecx instruction
|
|
jmp _KiExceptionExit
|
|
|
|
Kt0710:
|
|
.errnz (CR0_TS AND 0FFFFFF00h)
|
|
test bl, CR0_TS ; check for task switch
|
|
jnz Kt07150
|
|
|
|
;
|
|
; WARNING: May enter here from the trap 10 handler.
|
|
;
|
|
|
|
Kt0715:
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
|
|
jnz Kt07110 ; v86 mode
|
|
|
|
.errnz (MODE_MASK AND 0FFFFFF00h)
|
|
test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previousMode=USER?
|
|
jz Kt0705 ; if z, previousmode=SYSTEM
|
|
|
|
cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
|
|
jne Kt07110
|
|
|
|
;
|
|
; We are about to dispatch a floating point exception to user mode.
|
|
; We need to check to see if the user's NPX instruction is supposed to
|
|
; cause an exception or not.
|
|
;
|
|
; (ecx) - CurrentThread's NPX save area
|
|
;
|
|
|
|
Kt0716: stdCall _Ki386CheckDelayedNpxTrap,<ebp,ecx>
|
|
or al, al
|
|
jnz _KiExceptionExit ; Already handled
|
|
|
|
mov eax, PCR[PcPrcbData+PbCurrentThread]
|
|
mov ecx, [eax].ThInitialStack ; (ecx) -> top of kernel stack
|
|
sub ecx, NPX_FRAME_LENGTH
|
|
|
|
Kt0720:
|
|
;
|
|
; Some type of coprocessor exception has occured for the current thread.
|
|
;
|
|
; (eax) - CurrentThread
|
|
; (ecx) - CurrentThread's NPX save area
|
|
; (ebp) - TrapFrame
|
|
; Interrupts disabled
|
|
;
|
|
mov ebx, cr0
|
|
and ebx, NOT (CR0_MP+CR0_EM+CR0_TS)
|
|
mov cr0, ebx ; Clear MP+TS+EM to do fnsave & fwait
|
|
|
|
;
|
|
; Save the faulting state so we can inspect the cause of the floating
|
|
; point fault
|
|
;
|
|
|
|
test byte ptr _KeI386FxsrPresent, 1 ; Is FXSR feature present
|
|
jz short Kt0725a
|
|
FXSAVE_ECX
|
|
jmp short Kt0725b
|
|
Kt0725a:
|
|
fnsave [ecx] ; Save thread's coprocessor state
|
|
fwait ; in case fnsave hasn't finished yet
|
|
Kt0725b:
|
|
|
|
if DBG
|
|
test dword ptr [ecx].FpCr0NpxState, NOT (CR0_MP+CR0_EM+CR0_TS)
|
|
jnz Kt07dbg2
|
|
endif
|
|
or ebx, NPX_STATE_NOT_LOADED
|
|
or ebx,[ecx]+FpCr0NpxState ; restore this thread's CR0 NPX state
|
|
mov cr0, ebx ; set TS so next ESC access causes trap
|
|
|
|
;
|
|
; Clear TS bit in Cr0NpxFlags in case it was set to trigger this trap.
|
|
;
|
|
and dword ptr [ecx].FpCr0NpxState, NOT CR0_TS
|
|
|
|
;
|
|
; The state is no longer in the coprocessor. Clear ThNpxState and
|
|
; re-enable interrupts to allow context switching.
|
|
;
|
|
mov byte ptr [eax].ThNpxState, NPX_STATE_NOT_LOADED
|
|
mov dword ptr PCR[PcPrcbData+PbNpxThread], 0 ; No state in coprocessor
|
|
Kt0726: sti
|
|
|
|
;
|
|
; According to the floating error priority, we test what is the cause of
|
|
; the NPX error and raise an appropriate exception.
|
|
;
|
|
|
|
test byte ptr _KeI386FxsrPresent, 1 ; Is FXSR feature present
|
|
jz short Kt0727a
|
|
|
|
mov ebx, [ecx] + FxErrorOffset
|
|
movzx eax, word ptr [ecx] + FxControlWord
|
|
movzx edx, word ptr [ecx] + FxStatusWord
|
|
mov esi, [ecx] + FxDataOffset ; (esi) = operand addr
|
|
jmp short Kt0727b
|
|
|
|
Kt0727a:
|
|
mov ebx, [ecx] + FpErrorOffset
|
|
movzx eax, word ptr [ecx] + FpControlWord
|
|
movzx edx, word ptr [ecx] + FpStatusWord
|
|
mov esi, [ecx] + FpDataOffset ; (esi) = operand addr
|
|
|
|
Kt0727b:
|
|
|
|
and eax, FSW_INVALID_OPERATION + FSW_DENORMAL + FSW_ZERO_DIVIDE + FSW_OVERFLOW + FSW_UNDERFLOW + FSW_PRECISION
|
|
not eax ; ax = mask of enabled exceptions
|
|
and eax, edx
|
|
.errnz (FSW_INVALID_OPERATION AND 0FFFFFF00h)
|
|
test al, FSW_INVALID_OPERATION ; Is it an invalid op exception?
|
|
jz short Kt0740 ; if z, no, go Kt0740
|
|
.errnz (FSW_STACK_FAULT AND 0FFFFFF00h)
|
|
test al, FSW_STACK_FAULT ; Is it caused by stack fault?
|
|
jnz short Kt0730 ; if nz, yes, go Kt0730
|
|
|
|
; Raise Floating reserved operand exception
|
|
;
|
|
|
|
mov eax, STATUS_FLOAT_INVALID_OPERATION
|
|
jmp CommonDispatchException1Arg0d ; Won't return
|
|
|
|
Kt0730:
|
|
;
|
|
; Raise Access Violation exception for stack overflow/underflow
|
|
;
|
|
|
|
mov eax, STATUS_FLOAT_STACK_CHECK
|
|
jmp CommonDispatchException2Args0d ; Won't return
|
|
|
|
Kt0740:
|
|
|
|
; Check for floating zero divide exception
|
|
|
|
.errnz (FSW_ZERO_DIVIDE AND 0FFFFFF00h)
|
|
test al, FSW_ZERO_DIVIDE ; Is it a zero divide error?
|
|
jz short Kt0750 ; if z, no, go Kt0750
|
|
|
|
; Raise Floating divided by zero exception
|
|
|
|
mov eax, STATUS_FLOAT_DIVIDE_BY_ZERO
|
|
jmp CommonDispatchException1Arg0d ; Won't return
|
|
|
|
Kt0750:
|
|
|
|
; Check for denormal error
|
|
|
|
.errnz (FSW_DENORMAL AND 0FFFFFF00h)
|
|
test al, FSW_DENORMAL ; Is it a denormal error?
|
|
jz short Kt0760 ; if z, no, go Kt0760
|
|
|
|
; Raise floating reserved operand exception
|
|
|
|
mov eax, STATUS_FLOAT_INVALID_OPERATION
|
|
jmp CommonDispatchException1Arg0d ; Won't return
|
|
|
|
Kt0760:
|
|
|
|
; Check for floating overflow error
|
|
|
|
.errnz (FSW_OVERFLOW AND 0FFFFFF00h)
|
|
test al, FSW_OVERFLOW ; Is it an overflow error?
|
|
jz short Kt0770 ; if z, no, go Kt0770
|
|
|
|
; Raise floating overflow exception
|
|
|
|
mov eax, STATUS_FLOAT_OVERFLOW
|
|
jmp CommonDispatchException1Arg0d ; Won't return
|
|
|
|
Kt0770:
|
|
|
|
; Check for floating underflow error
|
|
.errnz (FSW_UNDERFLOW AND 0FFFFFF00h)
|
|
test al, FSW_UNDERFLOW ; Is it a underflow error?
|
|
jz short Kt0780 ; if z, no, go Kt0780
|
|
|
|
; Raise floating underflow exception
|
|
|
|
mov eax, STATUS_FLOAT_UNDERFLOW
|
|
jmp CommonDispatchException1Arg0d ; Won't return
|
|
|
|
Kt0780:
|
|
|
|
; Check for precision (IEEE inexact) error
|
|
|
|
.errnz (FSW_PRECISION AND 0FFFFFF00h)
|
|
test al, FSW_PRECISION ; Is it a precision error
|
|
jz short Kt07100 ; if z, no, go Kt07100
|
|
|
|
mov eax, STATUS_FLOAT_INEXACT_RESULT
|
|
jmp CommonDispatchException1Arg0d ; Won't return
|
|
|
|
Kt07100:
|
|
|
|
; If status word does not indicate error, then something is wrong...
|
|
;
|
|
; There is a known bug on Cyrix processors, upto and including
|
|
; the MII that causes Trap07 for no real reason (INTR is asserted
|
|
; during an FP instruction and is held high too long, we end up
|
|
; in the Trap07 handler with not exception set). Bugchecking seems
|
|
; a little heavy handed, if this is a Cyrix processor, just ignore
|
|
; the error.
|
|
|
|
cmp _KiIgnoreUnexpectedTrap07, 0
|
|
jnz _KiExceptionExit
|
|
|
|
; stop the system
|
|
sti
|
|
stdCall _KeBugCheckEx, <TRAP_CAUSE_UNKNOWN, 1, eax, 0, 0>
|
|
|
|
Kt07110:
|
|
; Check to see if this process is a vdm
|
|
|
|
mov eax,PCR[PcPrcbData+PbCurrentThread]
|
|
mov ebx,[eax]+ThApcState+AsProcess
|
|
cmp dword ptr [ebx]+PrVdmObjects,0 ; is this a vdm process?
|
|
je Kt0720 ; no, dispatch exception
|
|
|
|
Kt07130:
|
|
clts ; Turn off TS
|
|
and dword ptr [ecx]+FpCr0NpxState,NOT CR0_TS
|
|
|
|
|
|
; Reflect the exception to the vdm, the VdmHandler enables interrupts
|
|
|
|
; Raise Irql to APC level before enabling interrupts
|
|
RaiseIrql APC_LEVEL
|
|
push eax ; Save OldIrql
|
|
sti
|
|
|
|
stdCall _VdmDispatchIRQ13, <ebp> ; ebp - Trapframe
|
|
test al,0fh
|
|
jnz Kt07135
|
|
pop ecx ; (TOS) = OldIrql
|
|
LowerIrql ecx
|
|
|
|
; Could not reflect, generate exception.
|
|
;
|
|
; FP context may or may not still be loaded. If loaded, it needs to
|
|
; be saved now, if not loaded, skip save.
|
|
|
|
mov eax, PCR[PcPrcbData+PbCurrentThread]
|
|
mov ecx, [eax].ThInitialStack ; ecx -> top of kernel stack
|
|
sub ecx, NPX_FRAME_LENGTH ; ecx -> NPX save area
|
|
cli
|
|
cmp byte ptr [eax].ThNpxState, NPX_STATE_LOADED
|
|
je Kt0720 ; jif NPX state needs to be saved
|
|
jmp Kt0726 ; NPX state already saved
|
|
|
|
Kt07135:
|
|
pop ecx ; (TOS) = OldIrql
|
|
LowerIrql ecx
|
|
jmp _KiExceptionExit
|
|
|
|
Kt07140:
|
|
|
|
;
|
|
; Insure that this is not an NPX instruction in the kernel. (If
|
|
; an app, such as C7, sets the EM bit after executing NPX instructions,
|
|
; the fsave in SwapContext will catch an NPX exception
|
|
;
|
|
cmp [ebp].TsSegCS, word ptr KGDT_R0_CODE
|
|
je Kt0701
|
|
|
|
;
|
|
; Check to see if it really is a VDM
|
|
;
|
|
mov ebx,PCR[PcPrcbData+PbCurrentThread]
|
|
mov ebx,[ebx]+ThApcState+AsProcess
|
|
cmp dword ptr [ebx]+PrVdmObjects,0 ; is this a vdm process?
|
|
je Kt07100
|
|
|
|
; A vdm is emulating NPX instructions on a machine with an NPX.
|
|
|
|
stdCall _Ki386VdmReflectException_A, <07h>
|
|
test al,0fh
|
|
jnz _KiExceptionExit
|
|
|
|
mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
|
|
mov eax, STATUS_ACCESS_VIOLATION
|
|
mov esi, -1
|
|
jmp CommonDispatchException2Args0d ; Won't return
|
|
|
|
;
|
|
; If the processor took an NMI (or other task switch) CR0_TS will have
|
|
; been set. If this occured while the FP state was loaded it would
|
|
; appear that it is no longer valid. This is not actually true, the
|
|
; state is perfectly valid.
|
|
;
|
|
; Sanity: make sure CR0_MP is clear, if the OS had set CR0_TS, then
|
|
; CR0_MP should also be set.
|
|
;
|
|
|
|
Kt07150:
|
|
.errnz (CR0_MP AND 0FFFFFF00h)
|
|
test bl, CR0_MP
|
|
jnz short Kt07151
|
|
|
|
clts ; clear CR0_TS and continue
|
|
jmp _KiExceptionExit
|
|
|
|
Kt07151:
|
|
stdCall _KeBugCheckEx, <TRAP_CAUSE_UNKNOWN, 2, ebx, 0, 0>
|
|
jmp short Kt07151
|
|
|
|
_KiTrap07 endp
|
|
|
|
|
|
page ,132
|
|
subttl "Double Fault"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle double exception fault.
|
|
;
|
|
; Normally, when the processor detects an exception while trying to
|
|
; invoke the handler for a prior exception, the two exception can be
|
|
; handled serially. If, however, the processor cannot handle them
|
|
; serially, it signals the double-fault exception instead.
|
|
;
|
|
; If double exception is detected, no matter previous mode is USER
|
|
; or kernel, a bugcheck will be raised and the system will be terminated.
|
|
;
|
|
;
|
|
; Arguments:
|
|
;
|
|
; error code, which is always zero, is pushed on stack.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
|
|
|
|
|
|
public _KiTrap08
|
|
_KiTrap08 proc
|
|
.FPO (0, 0, 0, 0, 0, 2)
|
|
|
|
cli
|
|
|
|
|
|
;
|
|
; Set CR3, the I/O map base address, and LDT segment selector
|
|
; in the old TSS since they are not set on context
|
|
; switches. These values will be needed to return to the
|
|
; interrupted task.
|
|
|
|
mov eax, PCR[PcTss] ; get old TSS address
|
|
mov ecx, PCR[PcPrcbData+PbCurrentThread] ; get thread address
|
|
mov edi, [ecx].ThApcState.AsProcess ; get process address
|
|
mov ecx, [edi]+PrDirectoryTableBase ; get directory base
|
|
mov [eax]+TssCR3, ecx ; set previous cr3
|
|
|
|
mov cx, [edi]+PrIopmOffset ; get IOPM offset
|
|
mov [eax]+TssIoMapBase, cx ; set IOPM offset
|
|
|
|
mov ecx, [edi]+PrLdtDescriptor ; get LDT descriptor
|
|
test ecx, ecx ; does task use LDT?
|
|
jz @f
|
|
mov cx, KGDT_LDT
|
|
|
|
@@: mov [eax]+TssLDT, cx ; set LDT into old TSS
|
|
|
|
;
|
|
; Clear the busy bit in the TSS selector
|
|
;
|
|
|
|
mov ecx, PCR[PcGdt]
|
|
lea eax, [ecx] + KGDT_DF_TSS
|
|
mov byte ptr [eax+5], 089h ; 32bit, dpl=0, present, TSS32, not busy
|
|
|
|
;
|
|
; Clear Nested Task bit in EFLAGS
|
|
;
|
|
pushfd
|
|
and [esp], not 04000h
|
|
popfd
|
|
|
|
;
|
|
; Get address of the double-fault TSS which we are now running on.
|
|
;
|
|
mov eax, PCR[PcGdt]
|
|
mov ch, [eax+KGDT_DF_TSS+KgdtBaseHi]
|
|
mov cl, [eax+KGDT_DF_TSS+KgdtBaseMid]
|
|
shl ecx, 16
|
|
mov cx, [eax+KGDT_DF_TSS+KgdtBaseLow]
|
|
;
|
|
; Update the TSS pointer in the PCR to point to the double-fault TSS
|
|
; (which is what we're running on, or else we wouldn't be here)
|
|
;
|
|
mov eax, PCR[PcTss]
|
|
mov PCR[PcTss], ecx
|
|
|
|
;
|
|
; The original machine context is in original task's TSS
|
|
;
|
|
@@: stdCall _KeBugCheckEx,<UNEXPECTED_KERNEL_MODE_TRAP,8,eax,0,0>
|
|
jmp short @b ; do not remove - for debugger
|
|
|
|
_KiTrap08 endp
|
|
|
|
page ,132
|
|
subttl "Coprocessor Segment Overrun"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle Coprocessor Segment Overrun exception.
|
|
;
|
|
; This exception only occurs on the 80286 (it's a trap 0d on the 80386),
|
|
; so choke if we get here.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; At entry, the saved CS:EIP point to the aborted instruction.
|
|
; No error code is provided with the error.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
|
|
ENTER_DR_ASSIST kit9_a, kit9_t, NoAbiosAssist
|
|
align dword
|
|
public _KiTrap09
|
|
_KiTrap09 proc
|
|
|
|
|
|
push 0 ; push dummy error code
|
|
ENTER_TRAP kit9_a, kit9_t
|
|
|
|
|
|
sti
|
|
|
|
mov eax, EXCEPTION_NPX_OVERRUN ; (eax) = exception type
|
|
jmp _KiSystemFatalException ; go terminate the system
|
|
|
|
_KiTrap09 endp
|
|
|
|
page ,132
|
|
subttl "Invalid TSS exception"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle Invalid TSS fault.
|
|
;
|
|
; This exception occurs if a segment exception other than the
|
|
; not-present exception is detected when loading a selector
|
|
; from the TSS.
|
|
;
|
|
; If the exception is caused as a result of the kernel, device
|
|
; drivers, or user incorrectly setting the NT bit in the flags
|
|
; while the back-link selector in the TSS is invalid and the
|
|
; IRET instruction being executed, in this case, this routine
|
|
; will clear the NT bit in the trap frame and restart the iret
|
|
; instruction. For other causes of the fault, the user process
|
|
; will be terminated if previous mode is user and the system
|
|
; will stop if the exception occurs in kernel mode. No exception
|
|
; is raised.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; At entry, the saved CS:EIP point to the faulting instruction or
|
|
; the first instruction of the task if the fault occurs as part of
|
|
; a task switch.
|
|
; Error code containing the segment causing the exception is provided.
|
|
;
|
|
; NT386 does not use TSS for context switching. So, the invalid tss
|
|
; fault should NEVER occur. If it does, something is wrong with
|
|
; the kernel. We simply shutdown the system.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
|
|
ENTER_DR_ASSIST kita_a, kita_t, NoAbiosAssist
|
|
align dword
|
|
public _KiTrap0A
|
|
_KiTrap0A proc
|
|
|
|
|
|
ENTER_TRAP kita_a, kita_t
|
|
|
|
|
|
; We can not enable interrupt here. If we came here because DOS/WOW
|
|
; iret with NT bit set, it is possible that vdm will swap the trap frame
|
|
; with their monitor context. If this happen before we check the NT bit
|
|
; we will bugcheck.
|
|
|
|
; sti
|
|
|
|
;
|
|
; If the trap occur in USER mode and is caused by iret instruction with
|
|
; OF bit set, we simply clear the OF bit and restart the iret.
|
|
; Any other causes of Invalid TSS cause system to be shutdown.
|
|
;
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
test byte ptr [ebp]+TsEFlags+2, EFLAGS_V86_MASK/010000h
|
|
jnz short Kt0a10
|
|
|
|
.errnz (MODE_MASK AND 0FFFFFF00h)
|
|
test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previous mode = USER
|
|
jz short Kt0a20
|
|
|
|
Kt0a10:
|
|
|
|
.errnz (EFLAGS_OF_BIT AND 0FFFF00FFh)
|
|
test byte ptr [ebp]+TsEFlags+1, EFLAGS_OF_BIT/0100h
|
|
sti
|
|
jz short Kt0a20
|
|
|
|
and dword ptr [ebp]+TsEFlags, NOT EFLAGS_OF_BIT
|
|
jmp _KiExceptionExit ; restart the instruction
|
|
|
|
Kt0a20:
|
|
mov eax, EXCEPTION_INVALID_TSS ; (eax) = trap type
|
|
jmp _KiSystemFatalException ; go terminate the system
|
|
|
|
_KiTrap0A endp
|
|
|
|
page ,132
|
|
subttl "Segment Not Present"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle Segment Not Present fault.
|
|
;
|
|
; This exception occurs when the processor finds the P bit 0
|
|
; when accessing an otherwise valid descriptor that is not to
|
|
; be loaded in SS register.
|
|
;
|
|
; The only place the fault can occur (in kernel mode) is Trap/Exception
|
|
; exit code. Otherwise, this exception causes system to be terminated.
|
|
; NT386 uses flat mode, the segment not present fault in Kernel mode
|
|
; indicates system malfunction.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; At entry, the saved CS:EIP point to the faulting instruction or
|
|
; the first instruction of the task if the fault occurs as part of
|
|
; a task switch.
|
|
; Error code containing the segment causing the exception is provided.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
|
|
ENTER_DR_ASSIST kitb_a, kitb_t, NoAbiosAssist
|
|
|
|
align dword
|
|
public _KiTrap0B
|
|
_KiTrap0B proc
|
|
|
|
; Set up machine state frame for displaying
|
|
|
|
ENTER_TRAP kitb_a, kitb_t
|
|
|
|
;
|
|
; Did the trap occur in a VDM?
|
|
;
|
|
|
|
test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previous mode = USER
|
|
jz Kt0b30
|
|
|
|
cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
|
|
je Kt0b20
|
|
|
|
Kt0b10:
|
|
|
|
; Check to see if this process is a vdm
|
|
|
|
mov ebx,PCR[PcPrcbData+PbCurrentThread]
|
|
mov ebx,[ebx]+ThApcState+AsProcess
|
|
cmp dword ptr [ebx]+PrVdmObjects,0 ; is this a vdm process?
|
|
je short Kt0b20
|
|
|
|
; Raise Irql to APC level before enabling interrupts
|
|
RaiseIrql APC_LEVEL
|
|
push eax ; Save OldIrql
|
|
sti
|
|
stdCall _Ki386VdmSegmentNotPresent
|
|
test eax, 0ffffh
|
|
jz short Kt0b15
|
|
|
|
pop ecx ; (TOS) = OldIrql
|
|
LowerIrql ecx
|
|
jmp _KiExceptionExit
|
|
|
|
Kt0b15:
|
|
pop ecx ; (TOS) = OldIrql
|
|
LowerIrql ecx
|
|
|
|
Kt0b20: sti
|
|
mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
|
|
mov esi, [ebp]+TsErrCode
|
|
and esi, 0FFFFh
|
|
or esi, RPL_MASK
|
|
mov eax, STATUS_ACCESS_VIOLATION
|
|
jmp CommonDispatchException2Args0d ; Won't return
|
|
|
|
Kt0b30:
|
|
;
|
|
; Check if the exception is caused by pop SegmentRegister.
|
|
; We need to deal with the case that user puts a NP selector in fs, ds, cs
|
|
; or es through kernel debugger. (kernel will trap while popping segment
|
|
; registers in trap exit code.)
|
|
; Note: We assume if the faulted instruction is pop segreg. It MUST be
|
|
; in trap exit code. So there MUST be a valid trap frame for the trap exit.
|
|
;
|
|
|
|
mov eax, [ebp]+TsEip ; (eax)->faulted Instruction
|
|
mov eax, [eax] ; (eax)= opcode of faulted instruction
|
|
mov edx, [ebp]+TsEbp ; (edx)->previous trap exit trapframe
|
|
|
|
add edx, TsSegDs ; [edx] = prev trapframe + TsSegDs
|
|
cmp al, POP_DS ; Is it pop ds instruction?
|
|
jz Kt0b90 ; if z, yes, go Kt0b90
|
|
|
|
add edx, TsSegEs - TsSegDs ; [edx] = prev trapframe + TsSegEs
|
|
cmp al, POP_ES ; Is it pop es instruction?
|
|
jz Kt0b90 ; if z, yes, go Kt0b90
|
|
|
|
add edx, TsSegFs - TsSegEs ; [edx] = prev trapframe + TsSegFs
|
|
cmp ax, POP_FS ; Is it pop fs (2-byte) instruction?
|
|
jz Kt0b90 ; If z, yes, go Kt0b90
|
|
|
|
add edx, TsSegGs - TsSegFs ; [edx] = prev trapframe + TsSegGs
|
|
cmp ax, POP_GS ; Is it pop gs (2-byte) instruction?
|
|
jz Kt0b90 ; If z, yes, go Kt0b90
|
|
|
|
;
|
|
; The exception is not caused by pop instruction. We still need to check
|
|
; if it is caused by iret (to user mode.) Because user may have a NP
|
|
; cs and we will trap at iret in trap exit code.
|
|
;
|
|
|
|
cmp al, IRET_OP ; Is it an iret instruction?
|
|
jne Kt0b199 ; if ne, not iret, go bugcheck
|
|
|
|
lea edx, [ebp]+TsHardwareEsp ; (edx)->trapped iret frame
|
|
.errnz (RPL_MASK AND 0FFFFFF00h)
|
|
test byte ptr [edx]+4, RPL_MASK ; Check CS of iret addr
|
|
; Does the iret have ring transition?
|
|
jz Kt0b199 ; if z, it's a real fault
|
|
;
|
|
; Before enabling interrupts we need to save off any debug registers again
|
|
; and build a good trap frame.
|
|
;
|
|
lea ecx, [ebp+12] ; get original trap frame address
|
|
call SaveDebugReg
|
|
|
|
;
|
|
; we trapped at iret while returning back to user mode. We will dispatch
|
|
; the exception back to user program.
|
|
;
|
|
|
|
mov ecx, (KTRAP_FRAME_LENGTH - 12) / 4
|
|
lea edx, [ebp]+TsErrCode
|
|
Kt0b40:
|
|
mov eax, [edx]
|
|
mov [edx+12], eax
|
|
sub edx, 4
|
|
loop Kt0b40
|
|
|
|
sti
|
|
|
|
add esp, 12 ; adjust esp and ebp
|
|
add ebp, 12
|
|
jmp Kt0b10
|
|
|
|
; mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
|
|
; xor edx, edx
|
|
; mov esi, [ebp]+TsErrCode
|
|
; or esi, RPL_MASK
|
|
; and esi, 0FFFFh
|
|
; mov ecx, 2
|
|
; mov eax, STATUS_ACCESS_VIOLATION
|
|
; call CommonDispatchException ; WOn't return
|
|
|
|
;
|
|
; The faulted instruction is pop seg
|
|
;
|
|
|
|
Kt0b90:
|
|
if DBG
|
|
lea eax, [ebp].TsHardwareEsp
|
|
cmp eax, edx
|
|
je @f
|
|
int 3
|
|
@@:
|
|
endif
|
|
mov dword ptr [edx], 0 ; set the segment reg to 0 such that
|
|
; we will trap in user mode.
|
|
jmp Kt0d01 ; continue in common code
|
|
|
|
Kt0b199:
|
|
sti
|
|
mov eax, EXCEPTION_SEGMENT_NOT_PRESENT ; (eax) = exception type
|
|
jmp _KiSystemFatalException ; terminate the system
|
|
|
|
_KiTrap0B endp
|
|
|
|
SaveDebugReg:
|
|
push ebx
|
|
mov ebx, dr7
|
|
test bl, DR7_ACTIVE
|
|
mov [ebp]+TsDr7,ebx
|
|
je @f
|
|
push edi
|
|
push esi
|
|
mov ebx,dr0
|
|
mov esi,dr1
|
|
mov edi,dr2
|
|
mov [ebp]+TsDr0,ebx
|
|
mov [ebp]+TsDr1,esi
|
|
mov [ebp]+TsDr2,edi
|
|
mov ebx,dr3
|
|
mov esi,dr6
|
|
mov [ebp]+TsDr3,ebx
|
|
xor ebx, ebx
|
|
mov [ebp]+TsDr6,esi
|
|
mov dr7, ebx
|
|
;
|
|
; Load KernelDr* into processor
|
|
;
|
|
mov edi,dword ptr PCR[PcPrcb]
|
|
mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr0
|
|
mov esi,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr1
|
|
mov dr0,ebx
|
|
mov dr1,esi
|
|
mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr2
|
|
mov esi,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr3
|
|
mov dr2,ebx
|
|
mov dr3,esi
|
|
mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr6
|
|
mov esi,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr7
|
|
mov dr6,ebx
|
|
mov dr7,esi
|
|
pop esi
|
|
pop edi
|
|
@@: pop ebx
|
|
ret
|
|
|
|
|
|
page ,132
|
|
subttl "Stack segment fault"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle Stack Segment fault.
|
|
;
|
|
; This exception occurs when the processor detects certain problem
|
|
; with the segment addressed by the SS segment register:
|
|
;
|
|
; 1. A limit violation in the segment addressed by the SS (error
|
|
; code = 0)
|
|
; 2. A limit violation in the inner stack during an interlevel
|
|
; call or interrupt (error code = selector for the inner stack)
|
|
; 3. If the descriptor to be loaded into SS has its present bit 0
|
|
; (error code = selector for the not-present segment)
|
|
;
|
|
; The exception should never occur in kernel mode except when we
|
|
; perform the iret back to user mode.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; At entry, the saved CS:EIP point to the faulting instruction or
|
|
; the first instruction of the task if the fault occurs as part of
|
|
; a task switch.
|
|
; Error code (whose value depends on detected condition) is provided.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
|
|
ENTER_DR_ASSIST kitc_a, kitc_t, NoAbiosAssist
|
|
align dword
|
|
public _KiTrap0C
|
|
_KiTrap0C proc
|
|
|
|
|
|
|
|
ENTER_TRAP kitc_a, kitc_t
|
|
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
|
|
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
|
|
jnz Kt0c20
|
|
|
|
.errnz (MODE_MASK AND 0FFFFFF00h)
|
|
test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previous mode = USER
|
|
jz Kt0c10
|
|
|
|
cmp word ptr [ebp]+TsSegCs, KGDT_R3_CODE OR RPL_MASK
|
|
jne Kt0c20 ; maybe in a vdm
|
|
|
|
Kt0c00: sti
|
|
mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
|
|
mov edx, EXCEPT_LIMIT_ACCESS; assume it is limit violation
|
|
mov esi, [ebp]+TsHardwareEsp; (ecx) = User Stack pointer
|
|
cmp word ptr [ebp]+TsErrCode, 0 ; Is errorcode = 0?
|
|
jz short kt0c05 ; if z, yes, go dispatch exception
|
|
|
|
mov esi, [ebp]+TsErrCode ; Otherwise, set SS segment value
|
|
; to be the address causing the fault
|
|
mov edx, EXCEPT_UNKNOWN_ACCESS
|
|
or esi, RPL_MASK
|
|
and esi, 0FFFFh
|
|
kt0c05: mov eax, STATUS_ACCESS_VIOLATION
|
|
jmp CommonDispatchException2Args ; Won't return
|
|
|
|
kt0c10:
|
|
|
|
;
|
|
; Check if the exception is caused by kernel mode iret to user code.
|
|
; We need to deal with the case that user puts a bogus value in ss
|
|
; through SetContext call. (kernel will trap while iret to user code
|
|
; in trap exit code.)
|
|
; Note: We assume if the faulted instruction is iret. It MUST be in
|
|
; trap/exception exit code. So there MUST be a valid trap frame for
|
|
; the trap exit.
|
|
;
|
|
|
|
mov eax, [ebp]+TsEip ; (eax)->faulted Instruction
|
|
|
|
;
|
|
; Note the eip is captured above before enabling interrupts to
|
|
; prevent a setcontext from editing a bogus eip into our trapframe.
|
|
;
|
|
|
|
|
|
movzx eax, byte ptr[eax]
|
|
|
|
; (eax)= opcode of faulted instruction
|
|
|
|
;
|
|
; Check if the exception is caused by iret (to user mode.)
|
|
; Because user may have a NOT PRESENT ss and we will trap at iret
|
|
; in trap exit code. (If user put a bogus/not valid SS in trap frame, we
|
|
; will catch it in trap 0D handler.
|
|
;
|
|
|
|
cmp al, IRET_OP ; Is it an iret instruction?
|
|
jne Kt0c15 ; if ne, not iret, go bugcheck
|
|
|
|
;
|
|
; Before enabling interrupts we need to save off any debug registers again
|
|
;
|
|
lea ecx, [ebp+12] ; get original trap frame address
|
|
call SaveDebugReg
|
|
|
|
lea edx, [ebp]+TsHardwareEsp ; (edx)->trapped iret frame
|
|
test dword ptr [edx]+4, RPL_MASK ; Check CS of iret addr
|
|
; Does the iret have ring transition?
|
|
jz Kt0c15 ; if z, no SS involved, it's a real fault
|
|
|
|
;
|
|
; we trapped at iret while returning back to user mode. We will dispatch
|
|
; the exception back to user program.
|
|
;
|
|
|
|
mov ecx, (KTRAP_FRAME_LENGTH - 12) / 4
|
|
lea edx, [ebp]+TsErrCode
|
|
@@:
|
|
mov eax, [edx]
|
|
mov [edx+12], eax
|
|
sub edx, 4
|
|
loop @b
|
|
|
|
sti
|
|
|
|
add esp, 12 ; adjust esp and ebp
|
|
add ebp, 12
|
|
|
|
;
|
|
; Now, we have user mode trap frame set up
|
|
;
|
|
|
|
mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
|
|
mov edx, EXCEPT_LIMIT_ACCESS; assume it is limit violation
|
|
mov esi, [ebp]+TsHardwareEsp; (ecx) = User Stack pointer
|
|
cmp word ptr [ebp]+TsErrCode, 0 ; Is errorcode = 0?
|
|
jz short @f ; if z, yes, go dispatch exception
|
|
|
|
mov esi, [ebp]+TsErrCode ; Otherwise, set SS segment value
|
|
; to be the address causing the fault
|
|
and esi, 0FFFFh
|
|
mov edx, EXCEPT_UNKNOWN_ACCESS
|
|
or esi, RPL_MASK
|
|
@@:
|
|
mov eax, STATUS_ACCESS_VIOLATION
|
|
jmp CommonDispatchException2Args ; Won't return
|
|
|
|
Kt0c15:
|
|
sti
|
|
mov eax, EXCEPTION_STACK_FAULT ; (eax) = trap type
|
|
jmp _KiSystemFatalException
|
|
|
|
Kt0c20:
|
|
; Check to see if this process is a vdm
|
|
|
|
mov ebx,PCR[PcPrcbData+PbCurrentThread]
|
|
mov ebx,[ebx]+ThApcState+AsProcess
|
|
cmp dword ptr [ebx]+PrVdmObjects,0 ; is this a vdm process?
|
|
je Kt0c00
|
|
|
|
Kt0c30:
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
|
|
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
|
|
jnz short @f
|
|
|
|
;
|
|
; Raise Irql to APC level before enabling interrupt
|
|
;
|
|
|
|
RaiseIrql APC_LEVEL
|
|
sti
|
|
mov edi, eax ; [edi] = OldIrql
|
|
|
|
call VdmFixEspEbp
|
|
|
|
push eax
|
|
mov ecx, edi
|
|
LowerIrql ecx
|
|
cli
|
|
pop eax
|
|
test al, 0fh
|
|
jz short @f
|
|
jmp _KiExceptionExit
|
|
|
|
@@:
|
|
stdCall _Ki386VdmReflectException_A,<0ch>
|
|
test al,0fh
|
|
jz Kt0c00
|
|
jmp _KiExceptionExit
|
|
|
|
_KiTrap0C endp
|
|
|
|
|
|
page ,132
|
|
subttl "TrapC handler for NTVDM"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Some protected dos games (ex, Duke3D) while running in a BIG code
|
|
; segment with SMALL Stack segment will hit trap c with higher 16bit
|
|
; of esp containing kernel esp higher 16 bit. The question it should
|
|
; not trapped because cpu should only look at sp. So, here we try
|
|
; to deal with this problem.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; EBP - Trap frame
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
|
|
public VdmFixEspEbp
|
|
VdmFixEspEbp proc
|
|
|
|
;
|
|
; First check if user SS is small. If it is big, do nothing
|
|
;
|
|
|
|
mov eax, [ebp].TsHardwareSegSs
|
|
lar eax, eax ; [eax]= ss access right
|
|
jnz Vth_err
|
|
|
|
test eax, 400000h ; is SS a big segment?
|
|
jnz Vth_err ; nz, yes, do nothing
|
|
|
|
xor edx, edx ; [edx] = 0 no need to update tframe
|
|
mov eax, esp
|
|
and eax, 0ffff0000h ; [eax]=kernel esp higher 16bit
|
|
mov ecx, [ebp].TsHardwareEsp
|
|
and ecx, 0ffff0000h ; [ecx]=user esp higher 16bit
|
|
cmp ecx, eax ; are they the same
|
|
jnz short @f ; if nz, no, go check ebp
|
|
|
|
and dword ptr [ebp].TsHardwareEsp, 0ffffH ; zero higher 16 bit of user esp
|
|
mov edx, 1 ; [edx]=1 indicates we need to update trap frame
|
|
@@:
|
|
mov ecx, [ebp].TsEbp
|
|
and ecx, 0ffff0000h ; [ecx]=user ebp higher 16bit
|
|
cmp ecx, eax ; are they the same as kernel's
|
|
jnz short @f ; if nz, no, go check if we need to update tframe
|
|
|
|
and dword ptr [ebp].TsEbp, 0ffffH ; zero higher 16bit of user ebp
|
|
mov edx, 1 ; update kernel trap frame
|
|
@@: cmp edx, 1 ; do we need to update trap frame?
|
|
jnz short Vth_err ; if nz, no, do nothing
|
|
|
|
Vth_ok:
|
|
|
|
;
|
|
; copy user's cs:eip and ss:esp to vdmtib, note this needs to be done
|
|
; with proper probing and exception handling.
|
|
;
|
|
|
|
mov ebx, PCR[PcTeb]
|
|
|
|
mov eax, [ebp].TsSegCs
|
|
mov ecx, [ebp].TsEip
|
|
mov edx, [ebp].TsEbx
|
|
|
|
stdCall _VdmTibPass1, <eax,ecx,edx>
|
|
|
|
; eax now points at the VDM tib (or NULL if anything went wrong)
|
|
|
|
;
|
|
; Move the vdmtib to the trap frame such that we return back to ntvdm
|
|
; [ebx]->vdmtib
|
|
;
|
|
|
|
mov [ebp].TsEbx, eax
|
|
|
|
;
|
|
; dispatch control to ntvdm trapc handler which will load ss:esp
|
|
; and jump to the trapped cs:eip
|
|
;
|
|
|
|
mov eax,PCR[PcPrcbData+PbCurrentThread]
|
|
mov eax,[eax]+ThApcState+AsProcess
|
|
mov eax,[eax]+PrVdmTrapcHandler
|
|
|
|
mov dword ptr [ebp].TsSegCs, KGDT_R3_CODE OR RPL_MASK
|
|
mov dword ptr [ebp].TsEip, eax
|
|
|
|
mov eax, 1
|
|
ret
|
|
|
|
Vth_err:
|
|
xor eax, eax
|
|
ret
|
|
VdmFixEspEbp endp
|
|
|
|
|
|
page ,132
|
|
subttl "General Protection Fault"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle General protection fault.
|
|
;
|
|
; First, check to see if the fault occured in kernel mode with
|
|
; incorrect selector values. If so, this is a lazy segment load.
|
|
; Correct the selector values and restart the instruction. Otherwise,
|
|
; parse out various kinds of faults and report as exceptions.
|
|
;
|
|
; All protection violations that do not cause another exception
|
|
; cause a general exception. If the exception indicates a violation
|
|
; of the protection model by an application program executing a
|
|
; previleged instruction or I/O reference, a PRIVILEGED INSTRUCTION
|
|
; exception will be raised. All other causes of general protection
|
|
; fault cause a ACCESS VIOLATION exception to be raised.
|
|
;
|
|
; If previous mode = Kernel;
|
|
; the system will be terminated (assuming not lazy segment load)
|
|
; Else previous mode = USER
|
|
; the process will be terminated if the exception was not caused
|
|
; by privileged instruction.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; At entry, the saved CS:EIP point to the faulting instruction or
|
|
; the first instruction of the task if the fault occurs as part of
|
|
; a task switch.
|
|
; Error code (whose value depends on detected condition) is provided.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:FLAT, SS:NOTHING, ES:FLAT
|
|
|
|
;
|
|
; Error and exception blocks for KiTrap0d
|
|
;
|
|
|
|
Ktd_ExceptionHandler proc
|
|
|
|
;
|
|
; WARNING: Here we directly unlink the exception handler from the
|
|
; exception registration chain. NO unwind is performed.
|
|
;
|
|
|
|
mov esp, [esp+8] ; (esp)-> ExceptionList
|
|
pop PCR[PcExceptionList]
|
|
add esp, 4 ; pop out except handler
|
|
pop ebp ; (ebp)-> trap frame
|
|
|
|
test dword ptr [ebp].TsSegCs, MODE_MASK ; if premode=kernel
|
|
jnz Kt0d103 ; nz, prevmode=user, go return
|
|
|
|
; raise bugcheck if prevmode=kernel
|
|
stdCall _KeBugCheck, <KMODE_EXCEPTION_NOT_HANDLED>
|
|
Ktd_ExceptionHandler endp
|
|
|
|
|
|
ENTER_DR_ASSIST kitd_a, kitd_t, NoAbiosAssist,, kitd_v
|
|
align dword
|
|
|
|
public _KiTrap0D
|
|
_KiTrap0D proc
|
|
|
|
|
|
;
|
|
; Did the trap occur in a VDM in V86 mode? Trap0d is not critical from
|
|
; performance point of view to native NT, but it's super critical to
|
|
; VDMs. So here we are doing every thing to make v86 mode most
|
|
; efficient.
|
|
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
test byte ptr [esp]+0ch+2,EFLAGS_V86_MASK/010000h
|
|
jz Ktdi
|
|
|
|
KtdV86Slow:
|
|
ENTER_TRAPV86 kitd_a, kitd_v
|
|
|
|
KtdV86Slow2:
|
|
mov ecx,PCR[PcPrcbData+PbCurrentThread]
|
|
mov ecx,[ecx]+ThApcState+AsProcess
|
|
cmp dword ptr [ecx]+PrVdmObjects,0 ; is this a vdm process?
|
|
jnz short @f ; if nz, yes
|
|
|
|
sti ; else, enable ints
|
|
jmp Kt0d105 ; and dispatch exception
|
|
|
|
@@:
|
|
; Raise Irql to APC level, before enabling interrupts
|
|
RaiseIrql APC_LEVEL
|
|
push eax ; Save OldIrql
|
|
sti
|
|
|
|
stdCall _VdmDispatchOpcodeV86_try,<ebp>
|
|
KtdV86Exit:
|
|
test al,0FFh
|
|
jnz short Ktdi2
|
|
|
|
stdCall _Ki386VdmReflectException,<0dh>
|
|
test al,0fh
|
|
jnz short Ktdi2
|
|
pop ecx ; (TOS) = OldIrql
|
|
LowerIrql ecx
|
|
jmp Kt0d105
|
|
Ktdi2:
|
|
pop ecx ; (TOS) = OldIrql
|
|
LowerIrql ecx
|
|
cli
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
|
|
jz Ktdi3
|
|
|
|
EXIT_TRAPV86
|
|
;
|
|
; EXIT_TRAPv86 does not exit if a user mode apc has switched
|
|
; the context from V86 mode to flat mode (VDM monitor context)
|
|
;
|
|
|
|
Ktdi3:
|
|
jmp _KiExceptionExit
|
|
|
|
Ktdi:
|
|
ENTER_TRAP kitd_a, kitd_t
|
|
|
|
;
|
|
; DO NOT TURN INTERRUPTS ON! If we're doing a lazy segment load,
|
|
; could be in an ISR or other code that needs ints off!
|
|
;
|
|
|
|
;
|
|
; Is this just a lazy segment load? First make sure the exception occurred
|
|
; in kernel mode.
|
|
;
|
|
|
|
test dword ptr [ebp]+TsSegCs,MODE_MASK
|
|
jnz Kt0d02 ; not kernel mode, go process normally
|
|
|
|
;
|
|
; Before handling kernel mode trap0d, we need to do some checks to make
|
|
; sure the kernel mode code is the one to blame.
|
|
;
|
|
|
|
if FAST_BOP
|
|
cmp dword ptr PCR[PcVdmAlert], 0
|
|
jne Kt0eVdmAlert
|
|
endif
|
|
|
|
;
|
|
; Check if the exception is caused by the handler trying to examine offending
|
|
; instruction. If yes, we raise exception to user mode program. This occurs
|
|
; when user cs is bogus. Note if cs is valid and eip is bogus, the exception
|
|
; will be caught by page fault and out Ktd_ExceptionHandler will be invoked.
|
|
; Both cases, the exception is dispatched back to user mode.
|
|
;
|
|
|
|
mov eax, [ebp]+TsEip
|
|
cmp eax, offset FLAT:Kt0d03
|
|
jbe short Kt0d000
|
|
cmp eax, offset FLAT:Kt0d60
|
|
jae short Kt0d000
|
|
|
|
sti
|
|
mov ebp, [ebp]+TsEbp ; remove the current trap frame
|
|
mov esp, ebp ; set ebp, esp to previous trap frame
|
|
jmp Kt0d105 ; and dispatch exception to user mode.
|
|
|
|
;
|
|
; Check if the exception is caused by pop SegmentRegister.
|
|
; We need to deal with the case that user puts a bogus value in fs, ds,
|
|
; or es through kernel debugger. (kernel will trap while popping segment
|
|
; registers in trap exit code.)
|
|
; Note: We assume if the faulted instruction is pop segreg. It MUST be
|
|
; in trap exit code. So there MUST be a valid trap frame for the trap exit.
|
|
;
|
|
|
|
Kt0d000:
|
|
mov eax, [ebp]+TsEip ; (eax)->faulted Instruction
|
|
mov eax, [eax] ; (eax)= opcode of faulted instruction
|
|
mov edx, [ebp]+TsEbp ; (edx)->previous trap exit trapframe
|
|
|
|
add edx, TsSegDs ; [edx] = prev trapframe + TsSegDs
|
|
cmp al, POP_DS ; Is it pop ds instruction?
|
|
jz Kt0d005 ; if z, yes, go Kt0d005
|
|
|
|
add edx, TsSegEs - TsSegDs ; [edx] = prev trapframe + TsSegEs
|
|
cmp al, POP_ES ; Is it pop es instruction?
|
|
jz Kt0d005 ; if z, yes, go Kt0d005
|
|
|
|
add edx, TsSegFs - TsSegEs ; [edx] = prev trapframe + TsSegFs
|
|
cmp ax, POP_FS ; Is it pop fs (2-byte) instruction?
|
|
jz Kt0d005 ; If z, yes, go Kt0d005
|
|
|
|
add edx, TsSegGs - TsSegFs ; [edx] = prev trapframe + TsSegGs
|
|
cmp ax, POP_GS ; Is it pop gs (2-byte) instruction?
|
|
jz Kt0d005 ; If z, yes, go Kt0d005
|
|
|
|
;
|
|
; The exception is not caused by pop instruction. We still need to check
|
|
; if it is caused by iret (to user mode.) Because user may have a bogus
|
|
; ss and we will trap at iret in trap exit code.
|
|
;
|
|
|
|
cmp al, IRET_OP ; Is it an iret instruction?
|
|
jne Kt0d002 ; if ne, not iret, go check lazy load
|
|
|
|
lea edx, [ebp]+TsHardwareEsp ; (edx)->trapped iret frame
|
|
mov ax, [ebp]+TsErrCode ; (ax) = Error Code
|
|
and ax, NOT RPL_MASK ; No need to do this ...
|
|
mov cx, word ptr [edx]+4 ; [cx] = cs selector
|
|
and cx, NOT RPL_MASK
|
|
cmp cx, ax ; is it faulted in CS?
|
|
jne short Kt0d0008 ; No
|
|
|
|
;
|
|
; Check if this is the code which we use to return to Ki386CallBios
|
|
; (see biosa.asm):
|
|
; cs should be KGDT_R0_CODE OR RPL_MASK
|
|
; eip should be Ki386BiosCallReturnAddress
|
|
; esi should be the esp of function Ki386SetUpAndExitToV86Code
|
|
; (edx) -> trapped iret frame
|
|
;
|
|
|
|
mov eax, OFFSET FLAT:Ki386BiosCallReturnAddress
|
|
cmp eax, [edx] ; [edx]= trapped eip
|
|
; Is eip what we're expecting?
|
|
jne short Kt0d0005 ; No, continue
|
|
|
|
mov eax, [edx]+4 ; (eax) = trapped cs
|
|
cmp ax, KGDT_R0_CODE OR RPL_MASK ; Is Cs what we're exptecting?
|
|
jne short Kt0d0005 ; No
|
|
|
|
jmp Ki386BiosCallReturnAddress ; with interrupts off
|
|
|
|
Kt0d0005:
|
|
;
|
|
; Since the CS is bogus, we cannot tell if we are going back to user mode...
|
|
;
|
|
|
|
mov ebx,PCR[PcPrcbData+PbCurrentThread] ; if previous mode is
|
|
test byte ptr [ebx]+ThPreviousMode, 0ffh ; kernel, we bugcheck
|
|
jz Kt0d02
|
|
|
|
or word ptr [edx]+4, RPL_MASK
|
|
Kt0d0008:
|
|
test dword ptr [edx]+4, RPL_MASK ; Check CS of iret addr
|
|
; Does the iret have ring transition?
|
|
jz Kt0d02 ; if z, no SS involed, it's a real fault
|
|
;
|
|
; Before enabling interrupts we need to save off any debug registers again
|
|
;
|
|
lea ecx, [ebp+12] ; get original trap frame address
|
|
call SaveDebugReg
|
|
|
|
;
|
|
; we trapped at iret while returning back to user mode. We will dispatch
|
|
; the exception back to user program.
|
|
;
|
|
|
|
mov ecx, (KTRAP_FRAME_LENGTH - 12) / 4
|
|
lea edx, [ebp]+TsErrCode
|
|
Kt0d001:
|
|
mov eax, [edx]
|
|
mov [edx+12], eax
|
|
sub edx, 4
|
|
loop Kt0d001
|
|
|
|
sti
|
|
|
|
add esp, 12 ; adjust esp and ebp
|
|
add ebp, 12
|
|
mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
|
|
mov esi, [ebp]+TsErrCode
|
|
and esi, 0FFFFh
|
|
mov eax, STATUS_ACCESS_VIOLATION
|
|
jmp CommonDispatchException2Args0d ; Won't return
|
|
|
|
;
|
|
; Kernel mode, first opcode byte is 0f, check for rdmsr or wrmsr instruction
|
|
;
|
|
|
|
Kt0d001a:
|
|
shr eax, 8
|
|
cmp al, 30h
|
|
je short Kt0d001b
|
|
cmp al, 32h
|
|
jne short Kt0d002a
|
|
|
|
Kt0d001b:
|
|
mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
|
|
mov eax, STATUS_ACCESS_VIOLATION
|
|
jmp CommonDispatchException0Args ; Won't return
|
|
|
|
;
|
|
; The Exception is not caused by pop instruction. Check to see
|
|
; if the instruction is a rdmsr or wrmsr
|
|
;
|
|
Kt0d002:
|
|
cmp al, 0fh
|
|
je short Kt0d001a
|
|
|
|
;
|
|
; We now check if DS and ES contain correct value. If not, this is lazy
|
|
; segment load, we simply set them to valid selector.
|
|
;
|
|
|
|
Kt0d002a:
|
|
cmp word ptr [ebp]+TsSegDs, KGDT_R3_DATA OR RPL_MASK
|
|
je short Kt0d003
|
|
|
|
mov dword ptr [ebp]+TsSegDs, KGDT_R3_DATA OR RPL_MASK
|
|
jmp short Kt0d01
|
|
|
|
Kt0d003:
|
|
cmp word ptr [ebp]+TsSegEs, KGDT_R3_DATA OR RPL_MASK
|
|
je Kt0d02 ; Real fault, go process it
|
|
|
|
mov dword ptr [ebp]+TsSegEs, KGDT_R3_DATA OR RPL_MASK
|
|
jmp short Kt0d01
|
|
|
|
;
|
|
; The faulted instruction is pop seg
|
|
;
|
|
|
|
Kt0d005:
|
|
if DBG
|
|
lea eax, [ebp].TsHardwareEsp
|
|
cmp edx, eax
|
|
je @f
|
|
int 3
|
|
@@:
|
|
endif
|
|
xor eax, eax
|
|
mov dword ptr [edx], eax ; set the segment reg to 0 such that
|
|
; we will trap in user mode.
|
|
Kt0d01:
|
|
|
|
EXIT_ALL NoRestoreSegs,,NoPreviousMode ; RETURN
|
|
|
|
;
|
|
; Caller is not kernel mode, or DS and ES are OK. Therefore this
|
|
; is a real fault rather than a lazy segment load. Process as such.
|
|
; Since this is not a lazy segment load is now safe to turn interrupts on.
|
|
;
|
|
Kt0d02: mov eax, EXCEPTION_GP_FAULT ; (eax) = trap type
|
|
test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is prevmode=User?
|
|
jz _KiSystemFatalException ; If z, prevmode=kernel, stop...
|
|
|
|
|
|
; preload pointer to process
|
|
mov ebx,PCR[PcPrcbData+PbCurrentThread]
|
|
mov ebx,[ebx]+ThApcState+AsProcess
|
|
|
|
; flat or protect mode ?
|
|
cmp word ptr [ebp]+TsSegCs, KGDT_R3_CODE OR RPL_MASK
|
|
jz kt0d0201
|
|
|
|
;
|
|
; if vdm running in protected mode, handle instruction
|
|
;
|
|
cmp dword ptr [ebx]+PrVdmObjects,0 ; is this a vdm process?
|
|
jne Kt0d110
|
|
|
|
sti
|
|
cmp word ptr [ebp]+TsErrCode, 0 ; if errcode<>0, raise access
|
|
; violation exception
|
|
jnz Kt0d105 ; if nz, raise access violation
|
|
jmp short Kt0d03
|
|
|
|
|
|
;
|
|
; if vdm running in flat mode, handle pop es,fs,gs by setting to Zero
|
|
;
|
|
kt0d0202:
|
|
add dword ptr [ebp].TsEip, 1
|
|
kt0d02021:
|
|
mov dword ptr [edx], 0
|
|
add dword ptr [ebp].TsEip, 1
|
|
add dword ptr [ebp].TsHardwareEsp, 4
|
|
jmp _KiExceptionExit
|
|
|
|
kt0d0201:
|
|
cmp dword ptr [ebx]+PrVdmObjects,0 ; is this a vdm process?
|
|
je short Kt0d03
|
|
|
|
sti
|
|
mov eax, [ebp]+TsEip ; (eax)->faulted Instruction
|
|
stdCall _VdmFetchBop4, <eax> ; (eax)= opcode of faulted instruction
|
|
mov edx, ebp ; (edx)-> trap frame
|
|
|
|
add edx, TsSegEs ; [edx] = prev trapframe + TsSegEs
|
|
cmp al, POP_ES ; Is it pop es instruction?
|
|
jz short Kt0d02021
|
|
|
|
@@:
|
|
add edx, TsSegFs - TsSegEs ; [edx] = prev trapframe + TsSegFs
|
|
cmp ax, POP_FS ; Is it pop fs (2-byte) instruction?
|
|
jz short kt0d0202
|
|
|
|
add edx, TsSegGs - TsSegFs ; [edx] = prev trapframe + TsSegGs
|
|
cmp ax, POP_GS ; Is it pop gs (2-byte) instruction?
|
|
jz short kt0d0202
|
|
|
|
;
|
|
; we need to determine if the trap0d was caused by privileged instruction.
|
|
; First, we need to skip all the instruction prefix bytes
|
|
;
|
|
|
|
Kt0d03: sti
|
|
;
|
|
; First we need to set up an exception handler to handle the case that
|
|
; we fault while reading user mode instruction.
|
|
;
|
|
|
|
push ebp ; pass trapframe to handler
|
|
push offset FLAT:Ktd_ExceptionHandler
|
|
; set up exception registration record
|
|
push PCR[PcExceptionList]
|
|
mov PCR[PcExceptionList], esp
|
|
|
|
mov esi, [ebp]+TsEip ; (esi) -> flat address of faulting instruction
|
|
cmp esi, _MmUserProbeAddress ; Probe captured EIP
|
|
jb short @f
|
|
mov esi, _MmUserProbeAddress ; Use address of the GAP to force exception
|
|
@@: mov ecx, MAX_INSTRUCTION_LENGTH
|
|
|
|
@@:
|
|
push ecx ; save ecx for loop count
|
|
lods byte ptr [esi] ; (al)= instruction byte
|
|
mov ecx, PREFIX_REPEAT_COUNT
|
|
mov edi, offset FLAT:PrefixTable ; (ES:EDI)->prefix table
|
|
repnz scasb ; search for matching (al)
|
|
pop ecx ; restore loop count
|
|
jnz short Kt0d10 ; (al) not a prefix byte, go kt0d10
|
|
loop short @b ; go check for prefix byte again
|
|
pop PCR[PcExceptionList]
|
|
add esp, 8 ; clear stack
|
|
jmp Kt0635 ; exceed max instruction length,
|
|
; raise ILLEGALINSTRUCTION exception
|
|
|
|
;
|
|
; (al) = first opcode which is NOT prefix byte
|
|
; (ds:esi)= points to the first opcode which is not prefix byte + 1
|
|
; We need to check if it is one of the privileged instructions
|
|
;
|
|
|
|
Kt0d10: cmp al, MI_HLT ; Is it a HLT instruction?
|
|
je Kt0d80 ; if e, yes, go kt0d80
|
|
|
|
cmp al, MI_TWO_BYTE ; Is it a two-byte instruction?
|
|
jne short Kt0d50 ; if ne, no, go check for IO inst.
|
|
|
|
lods byte ptr [esi] ; (al)= next instruction byte
|
|
cmp al, MI_LTR_LLDT ; Is it a LTR or LLDT ?
|
|
jne short Kt0d20 ; if ne, no, go kt0d20
|
|
|
|
lods byte ptr [esi] ; (al)= ModRM byte of instruction
|
|
and al, MI_MODRM_MASK ; (al)= bit 3-5 of ModRM byte
|
|
cmp al, MI_LLDT_MASK ; Is it a LLDT instruction?
|
|
je Kt0d80 ; if e, yes, go Kt0d80
|
|
|
|
cmp al, MI_LTR_MASK ; Is it a LTR instruction?
|
|
je Kt0d80 ; if e, yes, go Kt0d80
|
|
|
|
jmp Kt0d100 ; if ne, go raise access vioalation
|
|
|
|
Kt0d20: cmp al, MI_LGDT_LIDT_LMSW ; Is it one of these instructions?
|
|
jne short Kt0d30 ; if ne, no, go check special mov inst.
|
|
|
|
lods byte ptr [esi] ; (al)= ModRM byte of instruction
|
|
and al, MI_MODRM_MASK ; (al)= bit 3-5 of ModRM byte
|
|
cmp al, MI_LGDT_MASK ; Is it a LGDT instruction?
|
|
je Kt0d80 ; if e, yes, go Kt0d80
|
|
|
|
cmp al, MI_LIDT_MASK ; Is it a LIDT instruction?
|
|
je Kt0d80 ; if e, yes, go Kt0d80
|
|
|
|
cmp al, MI_LMSW_MASK ; Is it a LMSW instruction?
|
|
je Kt0d80 ; if e, yes, go Kt0d80
|
|
|
|
jmp Kt0d100 ; else, raise access violation except
|
|
|
|
Kt0d30:
|
|
|
|
;
|
|
; Check some individual privileged instructions
|
|
;
|
|
cmp al, MI_INVD
|
|
je kt0d80
|
|
cmp al, MI_WBINVD
|
|
je kt0d80
|
|
cmp al, MI_SYSEXIT
|
|
je short kt0d80
|
|
cmp al, MI_MOV_TO_TR ; mov tx, rx
|
|
je short kt0d80
|
|
cmp al, MI_CLTS
|
|
je short kt0d80
|
|
|
|
;
|
|
; Test ordering of these compares
|
|
;
|
|
if MI_MOV_FROM_CR GT MI_WRMSR
|
|
.err
|
|
endif
|
|
;
|
|
; Test for the mov instructions as a block with a single unused gap
|
|
;
|
|
.errnz (MI_MOV_FROM_CR + 1 - MI_MOV_FROM_DR)
|
|
.errnz (MI_MOV_FROM_DR + 1 - MI_MOV_TO_CR)
|
|
.errnz (MI_MOV_TO_CR + 1 - MI_MOV_TO_DR)
|
|
.errnz (MI_MOV_TO_DR + 1 - MI_MOV_FROM_TR)
|
|
|
|
cmp al, MI_MOV_FROM_CR ; See if we are a mov rx, cx
|
|
jb Kt0d100 ; mov cx, rx
|
|
cmp al, MI_MOV_FROM_TR ; mov rx, dx
|
|
jbe short kt0d80 ; mov dx, rx
|
|
; mov rx, tx
|
|
;
|
|
; Test the counter instructions as a block
|
|
;
|
|
.errnz (MI_WRMSR + 1 - MI_RDTSC)
|
|
.errnz (MI_RDTSC + 1 - MI_RDMSR)
|
|
.errnz (MI_RDMSR + 1 - MI_RDPMC)
|
|
|
|
cmp al, MI_WRMSR
|
|
jb Kt0d100
|
|
cmp al, MI_RDPMC
|
|
jbe short kt0d80
|
|
jmp Kt0d100 ; else, no, raise access violation
|
|
|
|
;
|
|
; Now, we need to check if the trap 0d was caused by IO privileged instruct.
|
|
; (al) = first opcode which is NOT prefix byte
|
|
; Also note, if we come here, the instruction has 1 byte opcode (still need to
|
|
; check REP case.)
|
|
;
|
|
|
|
Kt0d50: mov ebx, [ebp]+TsEflags ; (ebx) = client's eflags
|
|
and ebx, IOPL_MASK ;
|
|
shr ebx, IOPL_SHIFT_COUNT ; (ebx) = client's IOPL
|
|
mov ecx, [ebp]+TsSegCs
|
|
and ecx, RPL_MASK ; RPL_MASK NOT MODE_MASK!!!
|
|
; (ecx) = CPL, 1/2 of computation of
|
|
; whether IOPL applies.
|
|
|
|
cmp ebx,ecx ; compare IOPL with CPL of caller
|
|
jge short Kt0d100 ; if ge, not IO privileged,
|
|
; go raise access violation
|
|
|
|
Kt0d60: cmp al, CLI_OP ; Is it a CLI instruction
|
|
je short Kt0d80 ; if e, yes. Report it.
|
|
|
|
cmp al, STI_OP ; Is it a STI?
|
|
je short Kt0d80 ; if e, yes, report it.
|
|
|
|
mov ecx, IO_INSTRUCTION_TABLE_LENGTH
|
|
mov edi, offset FLAT:IOInstructionTable
|
|
repnz scasb ; Is it a IO instruction?
|
|
jnz short Kt0d100 ; if nz, not io instrct.
|
|
|
|
;
|
|
; We know the instr is an IO instr without IOPL. But, this doesn't mean
|
|
; this is a privileged instruction exception. We need to make sure the
|
|
; IO port access is not granted in the bit map
|
|
;
|
|
|
|
|
|
mov edi, PCR[PcSelfPcr] ; (edi)->Pcr
|
|
mov esi, [edi]+PcGdt ; (esi)->Gdt addr
|
|
add esi, KGDT_TSS
|
|
movzx ebx, word ptr [esi] ; (ebx) = Tss limit
|
|
|
|
mov edx, [ebp].TsEdx ; [edx] = port addr
|
|
mov ecx, edx
|
|
and ecx, 07 ; [ecx] = Bit position
|
|
shr edx, 3 ; [edx] = offset to the IoMap
|
|
|
|
mov edi, [edi]+PcTss ; (edi)->TSS
|
|
movzx eax, word ptr [edi + TssIoMapBase] ; [eax] = Iomap offset
|
|
add edx, eax
|
|
cmp edx, ebx ; is the offset addr beyond tss limit?
|
|
ja short Kt0d80 ; yes, no I/O priv.
|
|
|
|
add edi, edx ; (edi)-> byte correspons to the port addr
|
|
mov edx, 1
|
|
shl edx, cl
|
|
test dword ptr [edi], edx ; Is the bit of the port disabled?
|
|
jz short Kt0d100 ; if z, no, then it is access violation
|
|
|
|
Kt0d80:
|
|
pop PCR[PcExceptionList]
|
|
add esp, 8 ; clear stack
|
|
mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
|
|
mov eax, STATUS_PRIVILEGED_INSTRUCTION
|
|
jmp CommonDispatchException0Args ; Won't return
|
|
|
|
;
|
|
; NOTE All the GP fault (except the ones we can
|
|
; easily detect now) will cause access violation exception
|
|
; AND, all the access violation will be raised with additional
|
|
; parameters set to "read" and "virtual address which caused
|
|
; the violation = unknown (-1)"
|
|
;
|
|
|
|
Kt0d100:
|
|
pop PCR[PcExceptionList]
|
|
add esp, 8 ; clear stack
|
|
Kt0d103:
|
|
Kt0d105:
|
|
mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
|
|
mov esi, -1
|
|
mov eax, STATUS_ACCESS_VIOLATION
|
|
jmp CommonDispatchException2Args0d ; Won't return
|
|
|
|
Kt0d110:
|
|
; Raise Irql to APC level, before enabling interrupts
|
|
RaiseIrql APC_LEVEL
|
|
push eax ; Save OldIrql
|
|
sti
|
|
|
|
stdCall _VdmDispatchOpcode_try <ebp>
|
|
test eax,0FFFFh
|
|
jnz short Kt0d120
|
|
|
|
stdCall _Ki386VdmReflectException,<0dh>
|
|
test al,0fh
|
|
jnz short Kt0d120
|
|
pop ecx ; (TOS) = OldIrql
|
|
LowerIrql ecx
|
|
jmp short Kt0d105
|
|
|
|
Kt0d120:
|
|
pop ecx ; (TOS) = OldIrql
|
|
LowerIrql ecx
|
|
jmp _KiExceptionExit
|
|
|
|
_KiTrap0D endp
|
|
|
|
page ,132
|
|
subttl "Page faults on invalid addresses allowed in certain instances"
|
|
;++
|
|
; BOOLEAN
|
|
; FASTCALL
|
|
; KeInvalidAccessAllowed (
|
|
; IN PVOID TrapInformation
|
|
; )
|
|
;
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Mm will pass a pointer to a trap frame prior to issuing a bug check on
|
|
; a pagefault. This routine lets Mm know if it is ok to bugcheck. The
|
|
; specific case we must protect are the interlocked pop sequences which can
|
|
; blindly access memory that may have been freed and/or reused prior to the
|
|
; access. We don't want to bugcheck the system in these cases, so we check
|
|
; the instruction pointer here.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; (ecx) - Trap frame pointer. NULL means return False.
|
|
;
|
|
; Return value:
|
|
;
|
|
; True if the invalid access should be ignored.
|
|
; False which will usually trigger a bugcheck.
|
|
;
|
|
;--
|
|
|
|
cPublicFastCall KeInvalidAccessAllowed, 1
|
|
cPublicFpo 0,0
|
|
|
|
cmp ecx, 0 ; caller gave us a trap frame?
|
|
jne Ktia01 ; yes, so examine the frame
|
|
mov al, 0
|
|
jmp Ktia02 ; no frame, go bugcheck
|
|
Ktia01:
|
|
mov edx, offset FLAT:ExpInterlockedPopEntrySListFault
|
|
cmp [ecx].TsEip, edx ; check if fault at pop code address
|
|
sete al ; if yes, then don't bugcheck
|
|
Ktia02:
|
|
fstRET KeInvalidAccessAllowed
|
|
|
|
fstENDP KeInvalidAccessAllowed
|
|
|
|
|
|
;
|
|
; The following code it to fix a bug in the Pentium Proccesor dealing with
|
|
; Invalid Opcodes.
|
|
;
|
|
|
|
|
|
PentiumTest: ; Is this access to the write protect
|
|
; IDT page?
|
|
test [ebp]+TsErrCode, 04h ; Do not allow user mode access to trap 6
|
|
jne NoPentiumFix ; vector. Let page fault code deal with it
|
|
mov eax, PCR[PcIDT] ; Get address of trap 6 IDT entry
|
|
add eax, (6 * 8)
|
|
cmp eax, edi ; Is that the faulting address?
|
|
jne NoPentiumFix ; No. Back to page fault code
|
|
|
|
; Yes. We have accessed the write
|
|
; protect page of the IDT
|
|
mov [ebp]+TsErrCode, 0 ; Overwrite error code
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
test byte ptr [ebp]+TsEFlags+2, EFLAGS_V86_MASK/010000h ; Was it a VM?
|
|
jne Kt06VMpf ; Yes. Go to VM part of trap 6
|
|
jmp Kt06pf ; Not from a VM
|
|
|
|
|
|
page ,132
|
|
subttl "Page fault processing"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle page fault.
|
|
;
|
|
; The page fault occurs if paging is enabled and any one of the
|
|
; conditions is true:
|
|
;
|
|
; 1. page not present
|
|
; 2. the faulting procedure does not have sufficient privilege to
|
|
; access the indicated page.
|
|
;
|
|
; For case 1, the referenced page will be loaded to memory and
|
|
; execution continues.
|
|
; For case 2, registered exception handler will be invoked with
|
|
; appropriate error code (in most cases STATUS_ACCESS_VIOLATION)
|
|
;
|
|
; N.B. It is assumed that no page fault is allowed during task
|
|
; switches.
|
|
;
|
|
; N.B. INTERRUPTS MUST REMAIN OFF UNTIL AFTER CR2 IS CAPTURED.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; Error code left on stack.
|
|
; CR2 contains faulting address.
|
|
; Interrupts are turned off at entry by use of an interrupt gate.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
ENTER_DR_ASSIST kite_a, kite_t, NoAbiosAssist
|
|
align dword
|
|
public _KiTrap0E
|
|
_KiTrap0E proc
|
|
|
|
ENTER_TRAP kite_a, kite_t
|
|
|
|
|
|
if FAST_BOP
|
|
cmp dword ptr PCR[PcVdmAlert], 0
|
|
jne Kt0eVdmAlert
|
|
endif
|
|
|
|
MODIFY_BASE_TRAP_FRAME
|
|
|
|
mov edi,cr2
|
|
;
|
|
; Now that everything is in a sane state check for rest of the Pentium
|
|
; Processor bug work around for illegal operands
|
|
;
|
|
|
|
cmp _KiI386PentiumLockErrataPresent, 0
|
|
jne PentiumTest ; Check for special problems
|
|
|
|
NoPentiumFix: ; No. Skip it
|
|
sti
|
|
|
|
|
|
test [ebp]+TsEFlags, EFLAGS_INTERRUPT_MASK ; faulted with
|
|
jz Kt0e12b ; interrupts disabled?
|
|
|
|
Kt0e01:
|
|
|
|
;
|
|
; call _MmAccessFault to page in the not present page. If the cause
|
|
; of the fault is 2, _MmAccessFault will return approriate error code
|
|
;
|
|
|
|
push ebp ; set trap frame address
|
|
mov eax, [ebp]+TsSegCs ; set previous mode
|
|
and eax, MODE_MASK ;
|
|
push eax ;
|
|
push edi ; set virtual address of page fault
|
|
mov eax, [ebp]+TsErrCode ; set load/store indicator
|
|
shr eax, 1 ;
|
|
and eax, _KeErrorMask ;
|
|
push eax ;
|
|
|
|
CAPSTART <_KiTrap0E,_MmAccessFault@16>
|
|
call _MmAccessFault@16
|
|
CAPEND <_KiTrap0E>
|
|
|
|
test eax, eax ; check if page fault resolved
|
|
jl short Kt0e05 ; if l, page fault not resolved
|
|
cmp _PsWatchEnabled, 0 ; check if working set watch enabled
|
|
je short @f ; if e, working set watch not enabled
|
|
mov ebx, [ebp]+TsEip ; set exception address
|
|
stdCall _PsWatchWorkingSet, <eax, ebx, edi> ; record working set information
|
|
@@: cmp _KdpOweBreakpoint, 0 ; check for owed breakpoints
|
|
je _KiExceptionExit ; if e, no owed breakpoints
|
|
stdCall _KdSetOwedBreakpoints ; notify the debugger
|
|
jmp _KiExceptionExit ; join common code
|
|
|
|
;
|
|
; Memory management could not resolve the page fault.
|
|
;
|
|
; Check to determine if the fault occured in the interlocked pop entry slist
|
|
; code. There is a case where a fault may occur in this code when the right
|
|
; set of circumstances occurs. The fault can be ignored by simply skipping
|
|
; the faulting instruction.
|
|
;
|
|
|
|
Kt0e05: mov ecx, offset FLAT:ExpInterlockedPopEntrySListFault ; get pop code address
|
|
cmp [ebp].TsEip, ecx ; check if fault at pop code address
|
|
je Kt0e10a ; if eq, skip faulting instruction
|
|
|
|
;
|
|
; Did the fault occur in KiSystemService while copying arguments from
|
|
; user stack to kernel stack?
|
|
;
|
|
mov ecx, offset FLAT:KiSystemServiceCopyArguments
|
|
cmp [ebp].TsEip, ecx
|
|
je short Kt0e06
|
|
|
|
mov ecx, offset FLAT:KiSystemServiceAccessTeb
|
|
cmp [ebp].TsEip, ecx
|
|
jne short Kt0e07
|
|
|
|
mov ecx, [ebp].TsEbp ; (eax)->TrapFrame of SysService
|
|
test [ecx].TsSegCs, MODE_MASK
|
|
jz short Kt0e07 ; caller of SysService is k mode, we
|
|
; will let it bugcheck.
|
|
mov [ebp].TsEip, offset FLAT:kss61
|
|
mov eax, STATUS_ACCESS_VIOLATION
|
|
mov [ebp].TsEax, eax
|
|
jmp _KiExceptionExit
|
|
|
|
Kt0e06:
|
|
mov ecx, [ebp].TsEbp ; (eax)->TrapFrame of SysService
|
|
test [ecx].TsSegCs, MODE_MASK
|
|
jz short Kt0e07 ; caller of SysService is k mode, we
|
|
; will let it bugcheck.
|
|
mov [ebp].TsEip, offset FLAT:kss60
|
|
mov eax, STATUS_ACCESS_VIOLATION
|
|
mov [ebp].TsEax, eax
|
|
jmp _KiExceptionExit
|
|
Kt0e07:
|
|
|
|
mov ecx, [ebp]+TsErrCode ; (ecx) = error code
|
|
shr ecx, 1 ; isolate read/write bit
|
|
and ecx, _KeErrorMask ;
|
|
;
|
|
; Did the fault occur in a VDM?
|
|
;
|
|
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
|
|
jnz Kt0e7
|
|
|
|
;
|
|
; Did the fault occur in a VDM while running in protected mode?
|
|
;
|
|
|
|
mov esi,PCR[PcPrcbData+PbCurrentThread]
|
|
mov esi,[esi]+ThApcState+AsProcess
|
|
cmp dword ptr [esi]+PrVdmObjects,0 ; is this a vdm process?
|
|
je short Kt0e9 ; z -> not vdm
|
|
|
|
.errnz (MODE_MASK AND 0FFFFFF00h)
|
|
test byte ptr [ebp]+TsSegCs, MODE_MASK
|
|
jz short Kt0e9 ; kernel mode, just dispatch exception.
|
|
|
|
cmp word ptr [ebp]+TsSegCs, KGDT_R3_CODE OR RPL_MASK
|
|
jz kt0e9 ; z -> not vdm
|
|
|
|
Kt0e7: mov esi, eax
|
|
stdCall _VdmDispatchPageFault, <ebp,ecx,edi>
|
|
test al,0fh ; returns TRUE, if success
|
|
jnz Kt0e11 ; Exit,No need to call the debugger
|
|
mov eax, esi
|
|
|
|
Kt0e9:
|
|
; Set up exception record and arguments and call _KiDispatchException
|
|
mov esi, [ebp]+TsEip ; (esi)-> faulting instruction
|
|
|
|
cmp eax, STATUS_ACCESS_VIOLATION ; dispatch access violation or
|
|
je short Kt0e9b ; or in_page_error?
|
|
|
|
cmp eax, STATUS_GUARD_PAGE_VIOLATION
|
|
je short Kt0e9b
|
|
|
|
cmp eax, STATUS_STACK_OVERFLOW
|
|
je short Kt0e9b
|
|
|
|
|
|
;
|
|
; test to see if davec's reserved status code bit is set. If so, then bugchecka
|
|
;
|
|
|
|
cmp eax, STATUS_IN_PAGE_ERROR or 10000000h
|
|
je Kt0e12 ; bugchecka
|
|
|
|
;
|
|
; (ecx) = ExceptionInfo 1
|
|
; (edi) = ExceptionInfo 2
|
|
; (eax) = ExceptionInfo 3
|
|
; (esi) -> Exception Addr
|
|
;
|
|
|
|
mov edx, ecx
|
|
mov ebx, esi
|
|
mov esi, edi
|
|
mov ecx, 3
|
|
mov edi, eax
|
|
mov eax, STATUS_IN_PAGE_ERROR
|
|
call CommonDispatchException ; Won't return
|
|
|
|
Kt0e9b:
|
|
mov ebx, esi
|
|
mov edx, ecx
|
|
mov esi, edi
|
|
jmp CommonDispatchException2Args ; Won't return
|
|
|
|
.FPO ( 0, 0, 0, 0, 0, FPO_TRAPFRAME )
|
|
|
|
;
|
|
; The fault occured in the interlocked pop slist function and the faulting
|
|
; instruction should be skipped.
|
|
;
|
|
|
|
Kt0e10a:mov ecx, offset FLAT:ExpInterlockedPopEntrySListResume ; get resume address
|
|
mov [ebp].TsEip, ecx ; set continuation address
|
|
|
|
Kt0e10:
|
|
|
|
mov esp,ebp ; (esp) -> trap frame
|
|
test _KdpOweBreakpoint, 1 ; do we have any owed breakpoints?
|
|
jz _KiExceptionExit ; No, all done
|
|
|
|
stdCall _KdSetOwedBreakpoints ; notify the debugger
|
|
|
|
Kt0e11: mov esp,ebp ; (esp) -> trap frame
|
|
jmp _KiExceptionExit ; join common code
|
|
|
|
|
|
Kt0e12:
|
|
CurrentIrql ; (eax) = OldIrql
|
|
Kt0e12a:
|
|
lock inc ds:_KiHardwareTrigger ; trip hardware analyzer
|
|
|
|
;
|
|
; bugcheck a, addr, irql, load/store, pc
|
|
;
|
|
mov ecx, [ebp]+TsErrCode ; (ecx)= error code
|
|
shr ecx, 1 ; isolate read/write bit
|
|
and ecx, _KeErrorMask ;
|
|
mov esi, [ebp]+TsEip ; [esi] = faulting instruction
|
|
|
|
stdCall _KeBugCheckEx,<IRQL_NOT_LESS_OR_EQUAL,edi,eax,ecx,esi>
|
|
|
|
Kt0e12b:cmp _KiFreezeFlag,0 ; during boot we can take
|
|
jnz Kt0e01 ; 'transition faults' on the
|
|
; debugger before it's been locked
|
|
|
|
cmp _KiBugCheckData, 0 ; If crashed, handle trap in
|
|
jnz Kt0e01 ; normal manner
|
|
|
|
|
|
mov eax, 0ffh ; OldIrql = -1
|
|
jmp short Kt0e12a
|
|
|
|
if FAST_BOP
|
|
|
|
Kt0eVdmAlert:
|
|
|
|
;
|
|
; If a page fault occured while we are in VDM alert mode (processing
|
|
; v86 trap without building trap frame), we will restore all the
|
|
; registers and return to its recovery routine which is stored in
|
|
; the TsSegGs of original trap frame.
|
|
;
|
|
|
|
mov eax, PCR[PcVdmAlert]
|
|
mov dword ptr PCR[PcVdmAlert], 0
|
|
|
|
mov [ebp].TsEip, eax
|
|
mov esp,ebp ; (esp) -> trap frame
|
|
jmp _KiExceptionExit ; join common code
|
|
|
|
endif ; FAST_BOP
|
|
|
|
|
|
_KiTrap0E endp
|
|
|
|
page ,132
|
|
subttl "Trap0F -- Intel Reserved"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; The trap 0F should never occur. If, however, the exception occurs in
|
|
; USER mode, the current process will be terminated. If the exception
|
|
; occurs in KERNEL mode, a bugcheck will be raised. NO registered
|
|
; handler, if any, will be invoked to handle the exception.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
|
|
ENTER_DR_ASSIST kitf_a, kitf_t, NoAbiosAssist
|
|
align dword
|
|
public _KiTrap0F
|
|
_KiTrap0F proc
|
|
|
|
|
|
push 0 ; push dummy error code
|
|
ENTER_TRAP kitf_a, kitf_t
|
|
|
|
sti
|
|
|
|
mov eax, EXCEPTION_RESERVED_TRAP ; (eax) = trap type
|
|
jmp _KiSystemFatalException ; go terminate the system
|
|
|
|
_KiTrap0F endp
|
|
|
|
|
|
page ,132
|
|
subttl "Coprocessor Error"
|
|
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle Coprocessor Error.
|
|
;
|
|
; This exception is used on 486 or above only. For i386, it uses
|
|
; IRQ 13 instead.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; At entry, the saved CS:EIP point to the aborted instruction.
|
|
; No error code is provided with the error.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
|
|
ENTER_DR_ASSIST kit10_a, kit10_t, NoAbiosAssist
|
|
|
|
align dword
|
|
public _KiTrap10
|
|
_KiTrap10 proc
|
|
|
|
|
|
push 0 ; push dummy error code
|
|
ENTER_TRAP kit10_a, kit10_t
|
|
|
|
|
|
mov eax, PCR[PcPrcbData+PbCurrentThread] ; Correct context for
|
|
cmp eax, PCR[PcPrcbData+PbNpxThread] ; fault?
|
|
mov ecx, [eax].ThInitialStack
|
|
lea ecx, [ecx]-NPX_FRAME_LENGTH
|
|
je Kt0715 ; Yes - go try to dispatch it
|
|
|
|
;
|
|
; We are in the wrong NPX context and can not dispatch the exception right now.
|
|
; Set up the target thread for a delay exception.
|
|
;
|
|
; Note: we don't think this is a possible case, but just to be safe...
|
|
;
|
|
|
|
or dword ptr [ecx].FpCr0NpxState, CR0_TS ; Set for delayed error
|
|
jmp _KiExceptionExit
|
|
|
|
_KiTrap10 endp
|
|
|
|
page ,132
|
|
subttl "Alignment fault"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle alignment faults.
|
|
;
|
|
; This exception occurs when an unaligned data access is made by a thread
|
|
; with alignment checking turned on.
|
|
;
|
|
; The x86 will not do any alignment checking. Only threads which have
|
|
; the appropriate bit set in EFLAGS will generate alignment faults.
|
|
;
|
|
; The exception will never occur in kernel mode. (hardware limitation)
|
|
;
|
|
; Arguments:
|
|
;
|
|
; At entry, the saved CS:EIP point to the faulting instruction.
|
|
; Error code is provided.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
|
|
ENTER_DR_ASSIST kit11_a, kit11_t, NoAbiosAssist
|
|
align dword
|
|
public _KiTrap11
|
|
_KiTrap11 proc
|
|
|
|
|
|
ENTER_TRAP kit11_a, kit11_t
|
|
|
|
sti
|
|
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
|
|
jnz Kt11_01 ; v86 mode => usermode
|
|
|
|
.errnz (MODE_MASK AND 0FFFFFF00h)
|
|
test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previous mode = USER
|
|
jz Kt11_10
|
|
|
|
;
|
|
; Check to make sure that the AutoAlignment state of this thread is FALSE.
|
|
; If not, this fault occurred because the thread messed with its own EFLAGS.
|
|
; In order to "fixup" this fault, we just clear the ALIGN_CHECK bit in the
|
|
; EFLAGS and restart the instruction. Exceptions will only be generated if
|
|
; AutoAlignment is FALSE.
|
|
;
|
|
Kt11_01:
|
|
mov ebx,PCR[PcPrcbData+PbCurrentThread] ; (ebx)-> Current Thread
|
|
test byte ptr [ebx].ThAutoAlignment, -1
|
|
jz kt11_00
|
|
;
|
|
; This fault was generated even though the thread had AutoAlignment set to
|
|
; TRUE. So we fix it up by setting the correct state in his EFLAGS and
|
|
; restarting the instruction.
|
|
;
|
|
and dword ptr [ebp]+TsEflags, NOT EFLAGS_ALIGN_CHECK
|
|
jmp _KiExceptionExit
|
|
|
|
kt11_00:
|
|
mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
|
|
mov edx, EXCEPT_LIMIT_ACCESS; assume it is limit violation
|
|
mov esi, [ebp]+TsHardwareEsp; (esi) = User Stack pointer
|
|
cmp word ptr [ebp]+TsErrCode, 0 ; Is errorcode = 0?
|
|
jz short kt11_05 ; if z, yes, go dispatch exception
|
|
|
|
mov edx, EXCEPT_UNKNOWN_ACCESS
|
|
kt11_05:
|
|
mov eax, STATUS_DATATYPE_MISALIGNMENT
|
|
jmp CommonDispatchException2Args ; Won't return
|
|
|
|
kt11_10:
|
|
;
|
|
; We should never be here, since the 486 will not generate alignment faults
|
|
; in kernel mode.
|
|
;
|
|
mov eax, EXCEPTION_ALIGNMENT_CHECK ; (eax) = trap type
|
|
jmp _KiSystemFatalException
|
|
|
|
_KiTrap11 endp
|
|
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Handle XMMI Exception.
|
|
;;
|
|
; Arguments:
|
|
;
|
|
; At entry, the saved CS:EIP point to the aborted instruction.
|
|
; No error code is provided with the error.
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
|
|
|
|
ENTER_DR_ASSIST kit13_a, kit13_t, NoAbiosAssist
|
|
|
|
align dword
|
|
public _KiTrap13
|
|
_KiTrap13 proc
|
|
|
|
|
|
push 0 ; push dummy error code
|
|
ENTER_TRAP kit13_a, kit13_t
|
|
|
|
mov eax, PCR[PcPrcbData+PbNpxThread] ; Correct context for fault?
|
|
cmp eax, PCR[PcPrcbData+PbCurrentThread]
|
|
je Kt13_10 ; Yes - go try to dispatch it
|
|
|
|
;
|
|
; Katmai New Instruction exceptions are precise and occur immediately.
|
|
; if we are in the wrong NPX context, bugcheck the system.
|
|
;
|
|
; stop the system
|
|
stdCall _KeBugCheckEx,<TRAP_CAUSE_UNKNOWN,13,eax,0,0>
|
|
|
|
Kt13_10:
|
|
mov ecx, [eax].ThInitialStack ; (ecx) -> top of kernel stack
|
|
sub ecx, NPX_FRAME_LENGTH
|
|
|
|
;
|
|
; TrapFrame is built by ENTER_TRAP.
|
|
; XMMI are accessible from all IA execution modes:
|
|
; Protected Mode, Real address mode, Virtual 8086 mode
|
|
;
|
|
Kt13_15:
|
|
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
|
|
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
|
|
jnz Kt13_130 ; v86 mode
|
|
|
|
;
|
|
; eflags.vm=0 (not v86 mode)
|
|
;
|
|
.errnz (MODE_MASK AND 0FFFFFF00h)
|
|
test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previousMode=USER?
|
|
jz Kt13_05 ; if z, previousmode=SYSTEM
|
|
|
|
;
|
|
; eflags.vm=0 (not v86 mode)
|
|
; previousMode=USER
|
|
;
|
|
cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
|
|
jne Kt13_110 ; May still be a vdm...
|
|
|
|
;
|
|
; eflags.vm=0 (not v86 mode)
|
|
; previousMode=USER
|
|
; Cs=00011011
|
|
;
|
|
; We are about to dispatch a XMMI floating point exception to user mode.
|
|
;
|
|
; (ebp) - Trap frame
|
|
; (ecx) - CurrentThread's NPX save area (PCR[PcInitialStack])
|
|
; (eax) - CurrentThread
|
|
|
|
; Dispatch
|
|
Kt13_20:
|
|
;
|
|
; Some type of coprocessor exception has occured for the current thread.
|
|
;
|
|
; Interrupts disabled
|
|
;
|
|
mov ebx, cr0
|
|
and ebx, NOT (CR0_MP+CR0_EM+CR0_TS)
|
|
mov cr0, ebx ; Clear MP+TS+EM to do fxsave
|
|
|
|
;
|
|
; Save the faulting state so we can inspect the cause of the floating
|
|
; point fault
|
|
;
|
|
FXSAVE_ECX
|
|
|
|
if DBG
|
|
test dword ptr [ecx].FpCr0NpxState, NOT (CR0_MP+CR0_EM+CR0_TS)
|
|
jnz Kt13_dbg2
|
|
endif
|
|
|
|
or ebx, NPX_STATE_NOT_LOADED ; CR0_TS | CR0_MP
|
|
or ebx,[ecx]+FpCr0NpxState ; restore this thread's CR0 NPX state
|
|
mov cr0, ebx ; set TS so next ESC access causes trap
|
|
|
|
;
|
|
; Clear TS bit in Cr0NpxFlags in case it was set to trigger this trap.
|
|
;
|
|
and dword ptr [ecx].FpCr0NpxState, NOT CR0_TS
|
|
|
|
;
|
|
; The state is no longer in the coprocessor. Clear ThNpxState and
|
|
; re-enable interrupts to allow context switching.
|
|
;
|
|
mov byte ptr [eax].ThNpxState, NPX_STATE_NOT_LOADED
|
|
mov dword ptr PCR[PcPrcbData+PbNpxThread], 0 ; No state in coprocessor
|
|
sti
|
|
|
|
; (eax) = ExcepCode - Exception code to put into exception record
|
|
; (ebx) = ExceptAddress - Addr of instruction which the hardware exception occurs
|
|
; (ecx) = NumParms - Number of additional parameters
|
|
; (edx) = Parameter1
|
|
; (esi) = Parameter2
|
|
; (edi) = Parameter3
|
|
mov ebx, [ebp].TsEip ; Eip is from trap frame, not from FxErrorOffset
|
|
movzx eax, word ptr [ecx] + FxMXCsr
|
|
mov edx, eax
|
|
shr edx, 7 ; get the mask
|
|
not edx
|
|
mov esi, 0 ; (esi) = operand addr, addr is computed from
|
|
; trap frame, not from FxDataOffset
|
|
;
|
|
; Exception will be handled in user's handler if there is one declared.
|
|
;
|
|
and eax, FSW_INVALID_OPERATION + FSW_DENORMAL + FSW_ZERO_DIVIDE + FSW_OVERFLOW + FSW_UNDERFLOW + FSW_PRECISION
|
|
and eax, edx
|
|
.errnz (FSW_INVALID_OPERATION AND 0FFFFFF00h)
|
|
test al, FSW_INVALID_OPERATION ; Is it an invalid op exception?
|
|
jz short Kt13_40 ; if z, no, go Kt13_40
|
|
;
|
|
; Invalid Operation Exception - Invalid arithmetic operand
|
|
; Raise exception
|
|
;
|
|
mov eax, STATUS_FLOAT_MULTIPLE_TRAPS
|
|
jmp CommonDispatchException1Arg0d ; Won't return
|
|
|
|
Kt13_40:
|
|
; Check for floating zero divide exception
|
|
;
|
|
.errnz (FSW_ZERO_DIVIDE AND 0FFFFFF00h)
|
|
test al, FSW_ZERO_DIVIDE ; Is it a zero divide error?
|
|
jz short Kt13_50 ; if z, no, go Kt13_50
|
|
;
|
|
; Division-By-Zero Exception
|
|
; Raise exception
|
|
;
|
|
mov eax, STATUS_FLOAT_MULTIPLE_TRAPS
|
|
jmp CommonDispatchException1Arg0d ; Won't return
|
|
|
|
Kt13_50:
|
|
; Check for denormal error
|
|
;
|
|
.errnz (FSW_DENORMAL AND 0FFFFFF00h)
|
|
test al, FSW_DENORMAL ; Is it a denormal error?
|
|
jz short Kt13_60 ; if z, no, go Kt13_60
|
|
;
|
|
; Denormal Operand Excpetion
|
|
; Raise exception
|
|
;
|
|
mov eax, STATUS_FLOAT_MULTIPLE_TRAPS
|
|
jmp CommonDispatchException1Arg0d ; Won't return
|
|
|
|
Kt13_60:
|
|
; Check for floating overflow error
|
|
;
|
|
.errnz (FSW_OVERFLOW AND 0FFFFFF00h)
|
|
test al, FSW_OVERFLOW ; Is it an overflow error?
|
|
jz short Kt13_70 ; if z, no, go Kt13_70
|
|
;
|
|
; Numeric Overflow Exception
|
|
; Raise exception
|
|
;
|
|
mov eax, STATUS_FLOAT_MULTIPLE_FAULTS
|
|
jmp CommonDispatchException1Arg0d ; Won't return
|
|
|
|
Kt13_70:
|
|
; Check for floating underflow error
|
|
;
|
|
.errnz (FSW_UNDERFLOW AND 0FFFFFF00h)
|
|
test al, FSW_UNDERFLOW ; Is it a underflow error?
|
|
jz short Kt13_80 ; if z, no, go Kt13_80
|
|
;
|
|
; Numeric Underflow Exception
|
|
; Raise exception
|
|
;
|
|
mov eax, STATUS_FLOAT_MULTIPLE_FAULTS
|
|
jmp CommonDispatchException1Arg0d ; Won't return
|
|
|
|
Kt13_80:
|
|
; Check for precision (IEEE inexact) error
|
|
;
|
|
.errnz (FSW_PRECISION AND 0FFFFFF00h)
|
|
test al, FSW_PRECISION ; Is it a precision error
|
|
jz short Kt13_100 ; if z, no, go Kt13_100
|
|
;
|
|
; Inexact-Result (Precision) Exception
|
|
; Raise exception
|
|
;
|
|
mov eax, STATUS_FLOAT_MULTIPLE_FAULTS
|
|
jmp CommonDispatchException1Arg0d ; Won't return
|
|
|
|
; Huh?
|
|
Kt13_100:
|
|
; If status word does not indicate error, then something is wrong...
|
|
; (Note: that we have done a sti, before the status is examined)
|
|
sti
|
|
; stop the system
|
|
stdCall _KeBugCheckEx,<TRAP_CAUSE_UNKNOWN,13,eax,0,1>
|
|
|
|
;
|
|
; eflags.vm=0 (not v86 mode)
|
|
; previousMode=USER
|
|
; Cs=!00011011
|
|
; (We should have (eax) -> CurrentThread)
|
|
;
|
|
Kt13_110:
|
|
; Check to see if this process is a vdm
|
|
mov ebx,PCR[PcPrcbData+PbCurrentThread] ; (ebx) -> CurrentThread
|
|
mov ebx,[ebx]+ThApcState+AsProcess ; (ebx) -> CurrentProcess
|
|
cmp dword ptr [ebx]+PrVdmObjects,0 ; is this a vdm process?
|
|
je Kt13_20 ; no, dispatch exception
|
|
; yes, drop down to v86 mode
|
|
;
|
|
; eflags.vm=1 (v86 mode)
|
|
;
|
|
Kt13_130:
|
|
; Turn off TS
|
|
mov ebx,CR0
|
|
and ebx,NOT CR0_TS
|
|
mov CR0,ebx
|
|
and dword ptr [ecx]+FpCr0NpxState,NOT CR0_TS
|
|
|
|
;
|
|
; Reflect the exception to the vdm, the VdmHandler enables interrupts
|
|
;
|
|
|
|
; Raise Irql to APC level before enabling interrupts
|
|
RaiseIrql APC_LEVEL
|
|
push eax ; Save OldIrql
|
|
sti
|
|
|
|
stdCall _VdmDispatchIRQ13, <ebp> ; ebp - Trapframe
|
|
test al,0fh
|
|
jnz Kt13_135
|
|
pop ecx ; (TOS) = OldIrql
|
|
LowerIrql ecx
|
|
jmp Kt13_20 ; could not reflect, gen exception
|
|
|
|
Kt13_135:
|
|
pop ecx ; (TOS) = OldIrql
|
|
LowerIrql ecx
|
|
jmp _KiExceptionExit
|
|
|
|
;
|
|
; eflags.vm=0 (not v86 mode)
|
|
; previousMode=SYSTEM
|
|
;
|
|
Kt13_05:
|
|
; stop the system
|
|
stdCall _KeBugCheckEx,<TRAP_CAUSE_UNKNOWN,13,0,0,2>
|
|
|
|
if DBG
|
|
Kt13_dbg1: int 3
|
|
Kt13_dbg2: int 3
|
|
Kt13_dbg3: int 3
|
|
sti
|
|
jmp short $-2
|
|
endif
|
|
|
|
_KiTrap13 endp
|
|
|
|
|
|
page ,132
|
|
subttl "Coprocessor Error Handler"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; When the FPU detects an error, it raises its error line. This
|
|
; was supposed to be routed directly to the x86 to cause a trap 16
|
|
; (which would actually occur when the x86 encountered the next FP
|
|
; instruction).
|
|
;
|
|
; However, the ISA design routes the error line to IRQ13 on the
|
|
; slave 8259. So an interrupt will be generated whenever the FPU
|
|
; discovers an error. Unfortunately, we could be executing system
|
|
; code at the time, in which case we can't dispatch the exception.
|
|
;
|
|
; So we force emulation of the intended behaviour. This interrupt
|
|
; handler merely sets TS and Cr0NpxState TS and dismisses the interrupt.
|
|
; Then, on the next user FP instruction, a trap 07 will be generated, and
|
|
; the exception can be dispatched then.
|
|
;
|
|
; Note that we don't have to clear the FP exeception here,
|
|
; since that will be done in the trap 07 handler. The x86 will
|
|
; generate the trap 07 before the FPU gets a chance to raise another
|
|
; error interrupt. We'll want to save the FPU state in the trap 07
|
|
; handler WITH the error information.
|
|
;
|
|
; Note the caller must clear the FPU error latch. (this is done in
|
|
; the hal).
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:FLAT, SS:NOTHING, ES:NOTHING
|
|
|
|
align dword
|
|
cPublicProc _KiCoprocessorError ,0
|
|
|
|
;
|
|
; Set TS in Cr0NpxState - the next time this thread runs an ESC
|
|
; instruction the error will be dispatched. We also need to set TS
|
|
; in CR0 in case the owner of the NPX is currently running.
|
|
;
|
|
; Bit must be set in FpCr0NpxState before CR0.
|
|
;
|
|
mov eax, PCR[PcPrcbData+PbNpxThread]
|
|
mov eax, [eax].ThInitialStack
|
|
sub eax, NPX_FRAME_LENGTH ; Space for NPX_FRAME
|
|
or dword ptr [eax].FpCr0NpxState, CR0_TS
|
|
|
|
mov eax, cr0
|
|
or eax, CR0_TS
|
|
mov cr0, eax
|
|
stdRET _KiCoprocessorError
|
|
|
|
stdENDP _KiCoprocessorError
|
|
|
|
;
|
|
; BBT cannot instrument code between BBT_Exclude_Trap_Code_Begin and this label
|
|
;
|
|
public _BBT_Exclude_Trap_Code_End
|
|
_BBT_Exclude_Trap_Code_End equ $
|
|
int 3
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; KiFlushNPXState (
|
|
; PFLOATING_SAVE_AREA SaveArea
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; When a thread's NPX context is requested (most likely by a debugger)
|
|
; this function is called to flush the thread's NPX context out of the
|
|
; compressor if required.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; Pointer to a location where this function must do fnsave for the
|
|
; current thread.
|
|
;
|
|
; NOTE that this pointer can be NON-NULL only if KeI386FxsrPresent is
|
|
; set (FXSR feature is present)
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:FLAT, SS:NOTHING, ES:NOTHING
|
|
align dword
|
|
|
|
SaveArea equ [esp + 20]
|
|
|
|
cPublicProc _KiFlushNPXState ,1
|
|
cPublicFpo 1, 4
|
|
|
|
push esi
|
|
push edi
|
|
push ebx
|
|
pushfd
|
|
cli ; don't context switch
|
|
|
|
mov edi, PCR[PcSelfPcr]
|
|
mov esi, [edi].PcPrcbData+PbCurrentThread
|
|
|
|
cmp byte ptr [esi].ThNpxState, NPX_STATE_LOADED
|
|
je short fnpx20
|
|
|
|
fnpx00:
|
|
; NPX state is not loaded. If SaveArea is non-null, we need to return
|
|
; the saved FP state in fnsave format.
|
|
|
|
cmp dword ptr SaveArea, 0
|
|
je fnpx70
|
|
|
|
if DBG
|
|
;
|
|
; SaveArea can be NON-NULL ONLY when FXSR feature is present
|
|
;
|
|
|
|
test byte ptr _KeI386FxsrPresent, 1
|
|
jnz @f
|
|
int 3
|
|
@@:
|
|
endif
|
|
;
|
|
; Need to convert the (not loaded) NPX state of the current thread
|
|
; to FNSAVE format into the SaveArea
|
|
;
|
|
mov ebx, cr0
|
|
.errnz ((CR0_MP+CR0_TS+CR0_EM) AND 0FFFFFF00h)
|
|
test bl, CR0_MP+CR0_TS+CR0_EM
|
|
jz short fnpx07
|
|
and ebx, NOT (CR0_MP+CR0_TS+CR0_EM)
|
|
mov cr0, ebx ; allow frstor (& fnsave) to work
|
|
fnpx07:
|
|
;
|
|
; If NPX state is for some other thread, save it away
|
|
;
|
|
|
|
mov eax, [edi].PcPrcbData+PbNpxThread ; Owner of NPX state
|
|
or eax, eax
|
|
jz short fnpx10 ; no - skip save
|
|
|
|
cmp byte ptr [eax].ThNpxState, NPX_STATE_LOADED
|
|
jne short fnpx10 ; not loaded, skip save
|
|
|
|
ifndef NT_UP
|
|
if DBG
|
|
; This can only happen UP where the context is not unloaded on a swap
|
|
int 3
|
|
endif
|
|
endif
|
|
|
|
;
|
|
; Save current owners NPX state
|
|
;
|
|
|
|
mov ecx, [eax].ThInitialStack
|
|
sub ecx, NPX_FRAME_LENGTH ; Space for NPX_FRAME
|
|
FXSAVE_ECX
|
|
mov byte ptr [eax].ThNpxState, NPX_STATE_NOT_LOADED
|
|
|
|
fnpx10:
|
|
;
|
|
; Load current thread's NPX state
|
|
;
|
|
mov ecx, [edi].PcPrcbData+PbCurrentThread
|
|
|
|
mov ecx, [ecx].ThInitialStack ; (ecx) -> top of kernel stack
|
|
sub ecx, NPX_FRAME_LENGTH
|
|
FXRSTOR_ECX ; reload NPX context
|
|
mov ecx, SaveArea
|
|
jmp short fnpx40
|
|
|
|
fnpx20:
|
|
;
|
|
; Current thread has NPX state in the coprocessor, flush it
|
|
;
|
|
mov ebx, cr0
|
|
.errnz ((CR0_MP+CR0_TS+CR0_EM) AND 0FFFFFF00h)
|
|
test bl, CR0_MP+CR0_TS+CR0_EM
|
|
jz short fnpx30
|
|
and ebx, NOT (CR0_MP+CR0_TS+CR0_EM)
|
|
mov cr0, ebx ; allow frstor (& fnsave) to work
|
|
fnpx30:
|
|
mov ecx, [edi].PcPrcbData+PbCurrentThread
|
|
mov ecx, [ecx].ThInitialStack ; (ecx) -> top of kernel stack
|
|
sub ecx, NPX_FRAME_LENGTH
|
|
|
|
test byte ptr _KeI386FxsrPresent, 1
|
|
jz short fnpx40
|
|
FXSAVE_ECX
|
|
|
|
; Do fnsave to SaveArea if it is non-null
|
|
mov ecx, SaveArea
|
|
jecxz short fnpx50
|
|
|
|
fnpx40:
|
|
fnsave [ecx] ; NPX state to save area
|
|
fwait ; Make sure data is in save area
|
|
fnpx50:
|
|
xor eax, eax
|
|
mov ecx, [edi].PcPrcbData+PbCurrentThread
|
|
mov ecx, [ecx].ThInitialStack ; (ecx) -> top of kernel stack
|
|
sub ecx, NPX_FRAME_LENGTH
|
|
mov byte ptr [esi].ThNpxState, NPX_STATE_NOT_LOADED
|
|
mov [edi].PcPrcbData+PbNpxThread, eax ; clear npx owner
|
|
;; mov [ecx].FpNpxSavedCpu, eax ; clear last npx processor
|
|
|
|
or ebx, NPX_STATE_NOT_LOADED ; or in new thread's cr0
|
|
or ebx, [ecx]+FpCr0NpxState ; merge new thread setable state
|
|
mov cr0, ebx
|
|
|
|
fnpx70:
|
|
popfd ; enable interrupts
|
|
pop ebx
|
|
pop edi
|
|
pop esi
|
|
stdRET _KiFlushNPXState
|
|
|
|
stdENDP _KiFlushNPXState
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; KiSetHardwareTrigger (
|
|
; VOID
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function sets KiHardwareTrigger such that an analyzer can sniff
|
|
; for this access. It needs to occur with a lock cycle such that
|
|
; the processor won't speculatively read this value. Interlocked
|
|
; functions can't be used as in a UP build they do not use a
|
|
; lock prefix.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
;
|
|
; Return value:
|
|
;
|
|
; None
|
|
;
|
|
;--
|
|
ASSUME DS:FLAT, SS:NOTHING, ES:NOTHING
|
|
cPublicProc _KiSetHardwareTrigger,0
|
|
lock inc ds:_KiHardwareTrigger ; trip hardware analyzer
|
|
stdRet _KiSetHardwareTrigger
|
|
stdENDP _KiSetHardwareTrigger
|
|
|
|
|
|
page ,132
|
|
subttl "Processing System Fatal Exceptions"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine processes the system fatal exceptions.
|
|
; The machine state and trap type will be displayed and
|
|
; System will be stopped.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; (eax) = Trap type
|
|
; (ebp) -> machine state frame
|
|
;
|
|
; Return value:
|
|
;
|
|
; system stopped.
|
|
;
|
|
;--
|
|
assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
|
|
|
|
align dword
|
|
public _KiSystemFatalException
|
|
_KiSystemFatalException proc
|
|
.FPO (0, 0, 0, 0, 0, FPO_TRAPFRAME)
|
|
|
|
stdCall _KeBugCheckEx,<UNEXPECTED_KERNEL_MODE_TRAP, eax, 0, 0, 0>
|
|
ret
|
|
|
|
_KiSystemFatalException endp
|
|
|
|
page
|
|
subttl "Continue Execution System Service"
|
|
;++
|
|
;
|
|
; NTSTATUS
|
|
; NtContinue (
|
|
; IN PCONTEXT ContextRecord,
|
|
; IN BOOLEAN TestAlert
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine is called as a system service to continue execution after
|
|
; an exception has occurred. Its function is to transfer information from
|
|
; the specified context record into the trap frame that was built when the
|
|
; system service was executed, and then exit the system as if an exception
|
|
; had occurred.
|
|
;
|
|
; WARNING - Do not call this routine directly, always call it as
|
|
; ZwContinue!!! This is required because it needs the
|
|
; trapframe built by KiSystemService.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; KTrapFrame (ebp+0: after setup) -> base of KTrapFrame
|
|
;
|
|
; ContextRecord (ebp+8: after setup) = Supplies a pointer to a context rec.
|
|
;
|
|
; TestAlert (esp+12: after setup) = Supplies a boolean value that specifies
|
|
; whether alert should be tested for the previous processor mode.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; Normally there is no return from this routine. However, if the specified
|
|
; context record is misaligned or is not accessible, then the appropriate
|
|
; status code is returned.
|
|
;
|
|
;--
|
|
|
|
NcTrapFrame equ [ebp + 0]
|
|
NcContextRecord equ [ebp + 8]
|
|
NcTestAlert equ [ebp + 12]
|
|
|
|
align dword
|
|
cPublicProc _NtContinue ,2
|
|
|
|
push ebp
|
|
|
|
;
|
|
; Restore old trap frame address since this service exits directly rather
|
|
; than returning.
|
|
;
|
|
|
|
mov ebx, PCR[PcPrcbData+PbCurrentThread] ; get current thread address
|
|
mov edx, [ebp].TsEdx ; restore old trap frame address
|
|
mov [ebx].ThTrapFrame, edx ;
|
|
|
|
;
|
|
; Call KiContinue to load ContextRecord into TrapFrame. On x86 TrapFrame
|
|
; is an atomic entity, so we don't need to allocate any other space here.
|
|
;
|
|
; KiContinue(NcContextRecord, 0, NcTrapFrame)
|
|
;
|
|
|
|
mov ebp,esp
|
|
mov eax, NcTrapFrame
|
|
mov ecx, NcContextRecord
|
|
CAPSTART <_NtContinue,_KiContinue@12>
|
|
stdCall _KiContinue, <ecx, 0, eax>
|
|
CAPEND <_NtContinue>
|
|
or eax,eax ; return value 0?
|
|
jnz short Nc20 ; KiContinue failed, go report error
|
|
|
|
;
|
|
; Check to determine if alert should be tested for the previous processor mode.
|
|
;
|
|
|
|
cmp byte ptr NcTestAlert,0 ; Check test alert flag
|
|
je short Nc10 ; if z, don't test alert, go Nc10
|
|
mov al,byte ptr [ebx]+ThPreviousMode ; No need to xor eax, eax.
|
|
CAPSTART <_NtContinue,_KeTestAlertThread@4>
|
|
stdCall _KeTestAlertThread, <eax> ; test alert for current thread
|
|
CAPEND <_NtContinue>
|
|
Nc10: CAPEND <_KiSystemService>
|
|
pop ebp ; (ebp) -> TrapFrame
|
|
mov esp,ebp ; (esp) = (ebp) -> trapframe
|
|
jmp _KiServiceExit2 ; common exit
|
|
|
|
Nc20: pop ebp ; (ebp) -> TrapFrame
|
|
mov esp,ebp ; (esp) = (ebp) -> trapframe
|
|
jmp _KiServiceExit ; common exit
|
|
|
|
stdENDP _NtContinue
|
|
|
|
page
|
|
subttl "Raise Exception System Service"
|
|
;++
|
|
;
|
|
; NTSTATUS
|
|
; NtRaiseException (
|
|
; IN PEXCEPTION_RECORD ExceptionRecord,
|
|
; IN PCONTEXT ContextRecord,
|
|
; IN BOOLEAN FirstChance
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine is called as a system service to raise an exception. Its
|
|
; function is to transfer information from the specified context record
|
|
; into the trap frame that was built when the system service was executed.
|
|
; The exception may be raised as a first or second chance exception.
|
|
;
|
|
; WARNING - Do not call this routine directly, always call it as
|
|
; ZwRaiseException!!! This is required because it needs the
|
|
; trapframe built by KiSystemService.
|
|
;
|
|
; NOTE - KiSystemService will terminate the ExceptionList, which is
|
|
; not what we want for this case, so we will fish it out of
|
|
; the trap frame and restore it.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; TrapFrame (ebp+0: before setup) -> System trap frame for this call
|
|
;
|
|
; ExceptionRecord (ebp+8: after setup) -> An exception record.
|
|
;
|
|
; ContextRecord (ebp+12: after setup) -> Points to a context record.
|
|
;
|
|
; FirstChance (ebp+16: after setup) -> Supplies a boolean value that
|
|
; specifies whether the exception is to be raised as a first (TRUE)
|
|
; or second chance (FALSE) exception.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;--
|
|
align dword
|
|
cPublicProc _NtRaiseException ,3
|
|
NtRaiseException:
|
|
|
|
push ebp
|
|
|
|
;
|
|
; Restore old trap frame address since this service exits directly rather
|
|
; than returning.
|
|
;
|
|
|
|
mov ebx, PCR[PcPrcbData+PbCurrentThread] ; get current thread address
|
|
mov edx, [ebp].TsEdx ; restore old trap frame address
|
|
mov [ebx].ThTrapFrame, edx ;
|
|
|
|
;
|
|
; Put back the ExceptionList so the exception can be properly
|
|
; dispatched.
|
|
;
|
|
|
|
mov ebp,esp ; [ebp+0] -> TrapFrame
|
|
mov ebx, [ebp+0] ; (ebx)->TrapFrame
|
|
mov edx, [ebp+16] ; (edx) = First chance indicator
|
|
mov eax, [ebx]+TsExceptionList ; Old exception list
|
|
mov ecx, [ebp+12] ; (ecx)->ContextRecord
|
|
mov PCR[PcExceptionList],eax
|
|
mov eax, [ebp+8] ; (eax)->ExceptionRecord
|
|
|
|
;
|
|
; KiRaiseException(ExceptionRecord, ContextRecord, ExceptionFrame,
|
|
; TrapFrame, FirstChance)
|
|
;
|
|
|
|
CAPSTART <NtRaiseException,_KiRaiseException@20>
|
|
stdCall _KiRaiseException,<eax, ecx, 0, ebx, edx>
|
|
CAPEND <NtRaiseException>
|
|
|
|
pop ebp
|
|
mov esp,ebp ; (esp) = (ebp) -> trap frame
|
|
|
|
;
|
|
; If the exception was handled, then the trap frame has been edited to
|
|
; reflect new state, and we'll simply exit the system service to get
|
|
; the effect of a continue.
|
|
;
|
|
; If the exception was not handled, exit via KiServiceExit so the
|
|
; return status is propagated back to the caller.
|
|
;
|
|
or eax, eax ; test exception handled
|
|
jnz _KiServiceExit ; if exception not handled
|
|
jmp _KiServiceExit2
|
|
|
|
stdENDP _NtRaiseException
|
|
|
|
|
|
|
|
page
|
|
subttl "Reflect Exception to a Vdm"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
; Local stub which reflects an exception to a VDM using
|
|
; Ki386VdmReflectException,
|
|
;
|
|
; Arguments:
|
|
;
|
|
; ebp -> Trap frame
|
|
; ss:esp + 4 = trap number
|
|
;
|
|
; Returns
|
|
; ret value from Ki386VdmReflectException
|
|
; interrupts are disabled uppon return
|
|
;
|
|
cPublicProc _Ki386VdmReflectException_A, 1
|
|
|
|
sub esp, 4*2
|
|
|
|
RaiseIrql APC_LEVEL
|
|
|
|
sti
|
|
mov [esp+4], eax ; Save OldIrql
|
|
mov eax, [esp+12] ; Pick up trap number
|
|
mov [esp+0], eax
|
|
|
|
call _Ki386VdmReflectException@4 ; pops one dword
|
|
|
|
mov ecx, [esp+0] ; (ecx) = OldIrql
|
|
mov [esp+0], eax ; Save return code
|
|
|
|
LowerIrql ecx
|
|
|
|
pop eax ; pops second dword
|
|
ret 4
|
|
|
|
stdENDP _Ki386VdmReflectException_A
|
|
|
|
|
|
|
|
|
|
|
|
page
|
|
subttl "Fast System Call code templates"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; One of the following sets of code will be copied to
|
|
; SharedUserData->SystemCall to be executed for system
|
|
; calls from user mode.
|
|
;
|
|
; In the fast call case, if an attempt is made to single
|
|
; step over the system call instruction, the processor
|
|
; will double fault because no valid stack address is
|
|
; set on which to handle the debug exception. The double
|
|
; fault handler will arrange to return one byte later
|
|
; than normal allowing the following code to re-enable
|
|
; single step mode.
|
|
;
|
|
; Arguments:
|
|
;
|
|
;
|
|
; Returns
|
|
;
|
|
;--
|
|
|
|
public _KiDefaultSystemCall, _KiDefaultSystemCallEnd
|
|
_KiDefaultSystemCall:
|
|
lea edx, [esp]+8 ; (edx) -> arguments
|
|
int 2eh
|
|
ret
|
|
_KiDefaultSystemCallEnd:
|
|
|
|
public _KiFastSystemCallIa32, _KiFastSystemCallIa32End
|
|
_KiFastSystemCallIa32:
|
|
mov edx, esp ; (edx) -> arguments-8
|
|
iSYSENTER
|
|
fscrr0: ret
|
|
pushfd ; reset single step mode
|
|
or [esp], EFLAGS_TF_MASK
|
|
popfd
|
|
ret
|
|
_KiFastSystemCallIa32End:
|
|
|
|
public _KiFastSystemCallAmdK6, _KiFastSystemCallAmdK6End
|
|
_KiFastSystemCallAmdK6:
|
|
mov edx, esp ; (edx) -> arguments-8
|
|
iSYSCALL
|
|
fscrr1: ret
|
|
pushfd ; reset single step mode
|
|
or [esp], EFLAGS_TF_MASK
|
|
popfd
|
|
ret
|
|
_KiFastSystemCallAmdK6End:
|
|
|
|
;
|
|
; For Fast System Call return, the EIP saved in the trap frame should
|
|
; correspond to the RET instruction in the above two syscall sequences.
|
|
; Note: It is much faster to return to the return instruction than to
|
|
; pop the return address off the stack and use that because doing so
|
|
; unbalances the call/return stack the processor uses for branch prediction.
|
|
;
|
|
|
|
fscrOffset EQU fscrr0-_KiFastSystemCallIa32
|
|
fscrOffset2 EQU fscrr1-_KiFastSystemCallAmdK6
|
|
.errnz fscrOffset2-fscrOffset
|
|
|
|
_TEXT$00 ends
|
|
end
|