title "Trap Processing" ; Copyright (c) 1989 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: .386p .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 ..\..\..\inc\vdmtib.inc include fastsys.inc .list FAST_BOP equ 1 FAST_V86_TRAP 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 _KiNMITSS:dword extrn _KeServiceDescriptorTable:dword extrn _KiHardwareTrigger:dword extrn _KiBugCheckData:dword extrn _KdpOweBreakpoint:dword extrn Ki386BiosCallReturnAddress:near extrn _PoHiberInProgress:byte extrn _KiI386PentiumLockErrataPresent:BYTE EXTRNP _KiDeliverApc,3 EXTRNP KfRaiseIrql,1,IMPORT,FASTCALL EXTRNP KfLowerIrql,1,IMPORT,FASTCALL EXTRNP _KeGetCurrentIrql,0,IMPORT EXTRNP _PsConvertToGuiThread,0 EXTRNP _ZwUnmapViewOfSection,2 EXTRNP _HalHandleNMI,1,IMPORT EXTRNP _HalBeginSystemInterrupt,3,IMPORT EXTRNP _HalEndSystemInterrupt,2,IMPORT EXTRNP _KiDispatchException,5 if DEVL EXTRNP _PsWatchWorkingSet,3 extrn _PsWatchEnabled:byte endif EXTRNP _MmAccessFault,4 extrn _MmUserProbeAddress:DWORD EXTRNP _KeBugCheck,1 EXTRNP _KeBugCheckEx,5 EXTRNP _KeTestAlertThread,1 EXTRNP _KiContinue,3 EXTRNP _KiRaiseException,5 EXTRNP _Ki386DispatchOpcode,0 EXTRNP _Ki386DispatchOpcodeV86,0 EXTRNP _VdmDispatchPageFault,3 EXTRNP _Ki386VdmReflectException,1 EXTRNP _Ki386VdmSegmentNotPresent,0 extrn _DbgPrint:proc EXTRNP _KdSetOwedBreakpoints extrn _KiFreezeFlag:dword EXTRNP _Ki386CheckDivideByZeroTrap,1 EXTRNP _Ki386CheckDelayedNpxTrap,2 extrn SwapContext:near EXTRNP _VdmDispatchIRQ13, 1 extrn VdmDispatchBop:near extrn _KeI386VdmIoplAllowed:dword extrn _KeI386VirtualIntExtensions:dword EXTRNP _NTFastDOSIO,2 EXTRNP _NtSetLdtEntries,6 extrn OpcodeIndex:byte extrn _KeFeatureBits:DWORD extrn _KeServiceDescriptorTableShadow:dword extrn _KiIgnoreUnexpectedTrap07:byte ; JAPAN - SUPPORT Intel CPU/Non PC/AT machine extrn _VdmFixedStateLinear:dword ; 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 ; page fault read/write mask ERR_0E_STORE EQU 2 ; Debug register 6 (dr6) BS (single step) bit mask DR6_BS_MASK EQU 4000H ; EFLAGS single step bit EFLAGS_TF_BIT EQU 100h 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 386 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 KiNMIMessage db 0ah,'Non-Maskable-Interrupt (NMI) EIP = %08lx',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 ; 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 IFDEF STD_CALL public _KiStartUnexpectedRange@0 _KiStartUnexpectedRange@0 equ $ ELSE public _KiStartUnexpectedRange _KiStartUnexpectedRange equ $ ENDIF _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 IFDEF STD_CALL public _KiEndUnexpectedRange@0 _KiEndUnexpectedRange@0 equ $ ELSE public _KiEndUnexpectedRange _KiEndUnexpectedRange equ $ ENDIF ;; 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 if FAST_V86_TRAP ALIGN 4 ; V86DispatchTable - table of routines used to emulate instructions ; in v86 mode. dtBEGIN V86DispatchTable,V86PassThrough dtS VDM_INDEX_PUSHF , V86Pushf dtS VDM_INDEX_POPF , V86Popf dtS VDM_INDEX_INTnn , V86Intnn dtS VDM_INDEX_IRET , V86Iret dtS VDM_INDEX_CLI , V86Cli dtS VDM_INDEX_STI , V86Sti dtEND MAX_VDM_INDEX endif ; FAST_V86_TRAP ; 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 ifdef NT_UP else mov eax, KGDT_R0_PCR mov fs, ax endif mov byte ptr PCR[PcVdmAlert], 6 mov ax, word ptr [esp+TRAP6_CS] ; [eax] = v86 user cs shl eax, 4 add eax, [esp+TRAP6_IP] ; [eax] = addr of BOP 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[PcPrcbData+PbCurrentThread] shr edx, 16 mov eax,[eax]+ThApcState+AsProcess mov eax,[eax].EpVdmObjects mov eax,[eax].VpVdmTib ; get pointer to VdmTib 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 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) = iser 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 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 _KeI386VdmIoplAllowed, 0ffffffffh jnz short a mov ebx, _VdmFixedStateLinear ; load ntvdm address test ds:[ebx], 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].CsSegSs mov esi, [eax].CsEsp mov edi, [eax].CsEFlags mov edx, [eax].CsSegCs mov ecx, [eax].CsEip sub esp, 20 ; allocate stack space mov [esp + 16], ebx ; Build Iret frame (can not single step!) mov [esp + 12], esi mov [esp + 8], edi mov [esp + 4], edx mov [esp + 0], ecx 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. ; fs, ds are set up already. ; Adjust Tss esp0 value and set return value to SUCCESS mov ecx, PCR[PcPrcbData+PbCurrentThread] mov ecx, [ecx].thInitialStack mov edx, PCR[PcTss] sub ecx, NPX_FRAME_LENGTH + TsV86Gs - TsHardwareSegSs xor eax, eax ; ret status = SUCCESS mov [edx].TssEsp0, ecx mov byte ptr PCR[PcVdmAlert], al mov edx, KGDT_R3_TEB OR RPL_MASK mov fs, dx iretd DoFastIo: xor eax, eax mov edx, [esp]+TRAP6_EDX ; Restore edx add esp, 7 * 4 ; leave eax in the TsErrCode xchg [esp], eax ; Restore eax, sore 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 ifdef NT_UP mov ebx, KGDT_R0_PCR mov fs, bx endif mov byte ptr PCR[PcVdmAlert], 0 mov ebp, esp cld test byte ptr PCR[PcDebugActive], -1 jz 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 mov [ebp]+TsDr6,esi mov [ebp]+TsDr7,edi ; Load KernelDr* into processor mov edi,dword ptr fs:[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 @@: ; Raise Irql to APC level before enabling interrupts mov ecx, APC_LEVEL fstCall KfRaiseIrql push eax ; Save OldIrql sti xor edx, edx mov dx, word ptr [ebp].TsSegCs shl edx, 4 xor ebx, ebx add edx, [ebp].TsEip mov bl, [edx+3] ; [bl] = minor BOP code push ebx push ebp ; (ebp)->TrapFrame call _NTFastDOSIO@8 jmp Kt061i V86Trap6PassThrough: mov byte ptr PCR[PcVdmAlert], 0 V86Trap6Recovery: popad jmp Kt6SlowBop ; Fall through endm page , 132 subttl "Macro to Handle v86 trap d" ; Macro Description: ; This macro is a fast way to handle SOME v86 mode sensitive 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. Write user memory (prefer do read here) ; 2. Update VDM state flags ; 3. Update trap frame ; Arguments: ; interrupts disabled ; Return Value: FAST_V86_TRAP_D MACRO local V86Exit, V86Exit_1, IntnnExit sub esp, TsErrCode mov [esp].TsEdx, edx mov [esp].TsEcx, ecx mov [esp].TsEbx, ebx mov [esp].TsEax, eax mov ebx, _VdmFixedStateLinear ifdef NT_UP mov byte ptr SS:[P0PCRADDRESS][PcVdmAlert], 0dh else mov eax, KGDT_R0_PCR mov fs, ax mov byte ptr PCR[PcVdmAlert], 0dh endif mov eax, [esp].TsSegCs ; (eax) = H/W Cs shl eax,4 add eax,[esp].TsEip ; (eax) -> flat faulted addr xor edx, edx mov ecx, ss:[eax] ; (ecx) = faulted instruction mov dl, cl mov dl, ss:OpcodeIndex[edx] ; (edx) = opcode index jmp ss:V86DispatchTable[edx * type V86DispatchTable] ; (ecx) = faulted instructions ; (eax) = Flat faulted addr ; (edx) = Opcode Index ALIGN 4 V86Pushf: mov eax, ss:[ebx] ; get ntvdm address mov edx, dword ptr [esp].TsEflags ; (edx) = Hardware Eflags and eax,VDM_VIRTUAL_INTERRUPTS OR VDM_VIRTUAL_AC OR VDM_VIRTUAL_NT or eax,EFLAGS_IOPL_MASK and edx,NOT EFLAGS_INTERRUPT_MASK mov [esp].TsSegDs, ecx ; save ecx or eax,edx ; (ax) = client flags xor ecx, ecx mov cx, word ptr [esp].TsHardwareSegSs ; (edx)= hardware SS xor edx, edx shl ecx,4 mov dx, word ptr [esp].TsHardwareEsp ; (edx)= Hardware sp sub edx, 2 mov ss:[ecx + edx],ax mov ecx, [esp].TsSegDs ; restore ecx ; Usually, pushf is followed by cli. So, here we check for this case. ; If yes, we will handle the cli to save a round trip. ; It is very important that we first update user stack, Fixed VDM state and ; finally hardware esp. cmp cx, (CLI_OP SHL 8) OR PUSHF_OP ; Is there a cli following pushf? jnz short @f MPLOCK and dword ptr ss:[ebx],NOT VDM_VIRTUAL_INTERRUPTS inc dword ptr [esp].TsEip ; skip cli @@: mov word ptr [esp].TsHardwareEsp, dx ; update client esp V86Exit: inc dword ptr [esp].TsEip ; skip pushf V86Exit_1: mov ecx, [esp].TsEcx mov ebx, [esp].TsEbx mov eax, [esp].TsEax mov edx, [esp].TsEdx add esp, TsEip ifdef NT_UP mov byte ptr SS:[P0PCRADDRESS][PcVdmAlert], 0 else mov byte ptr PCR[PcVdmAlert], 0 endif iretd ALIGN 4 V86Cli: MPLOCK and dword ptr ss:[ebx],NOT VDM_VIRTUAL_INTERRUPTS jmp short V86Exit ALIGN 4 V86Sti: test ss:[ebx], VDM_INTERRUPT_PENDING ; Can we handle it in fast way? jnz V86PassThrough ; if nz, no, we need to dispatch int ;; Pentium CPU traps sti if ;; 1). client's TF is ON or 2). VIP is ON ;; we must set EFLAGS_VIF in this case. test dword ptr _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS jz short v86_sti_01 or dword ptr [esp].TsEflags, EFLAGS_VIF v86_sti_01: MPLOCK or dword ptr ss:[ebx], EFLAGS_INTERRUPT_MASK jmp short V86Exit ALIGN 4 V86Popf: test ss:[ebx], VDM_INTERRUPT_PENDING ; Can we handle it in fast way? jnz V86PassThrough xor edx, edx mov dx, word ptr [esp].TsHardwareSegSs ; (edx)= hardware SS xor eax, eax shl edx,4 mov ax, word ptr [esp].TsHardwareEsp ; (ecx)= Hardware sp mov edx, ss:[edx + eax] ; (edx) = Client flags add ax, 2 ; (ax) = Client sp and edx, 0FFFFH AND (NOT EFLAGS_IOPL_MASK) MPLOCK and ss:[ebx],NOT (EFLAGS_INTERRUPT_MASK OR EFLAGS_ALIGN_CHECK OR EFLAGS_NT_MASK) mov ecx, edx and edx, (EFLAGS_INTERRUPT_MASK OR EFLAGS_ALIGN_CHECK OR EFLAGS_NT_MASK) and ecx, NOT EFLAGS_NT_MASK MPLOCK or ss:[ebx],edx or ecx, (EFLAGS_INTERRUPT_MASK OR EFLAGS_V86_MASK) ;; Pentium CPU traps popf if ;; 1)client's TF is ON or 2) VIP is on and the client's IF is ON ;; We have to propagate IF to VIF if virtual interrupt extension is ;; enabled. test dword ptr _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS jz v86_popf_01 and ecx, NOT EFLAGS_VIF ;clear it first and edx, EFLAGS_INTERRUPT_MASK ;isolate and move IF to VIF rol edx, 10 ;position .errnz (EFLAGS_INTERRUPT_MASK SHL 10) - EFLAGS_VIF or ecx, edx ;propagate it! v86_popf_01: mov [esp].TsEflags,ecx mov [esp].TsHardwareEsp, eax ; update client esp jmp V86Exit ALIGN 4 V86Intnn: shr ecx, 8 and ecx, 0FFH ; ecx is int# mov eax,PCR[PcPrcbData+PbCurrentThread] mov eax,[eax]+ThApcState+AsProcess mov eax,[eax].EpVdmObjects mov eax,[eax].VpVdmTib ; get pointer to VdmTib lea eax,[eax].VtInterruptHandlers[ecx*8] test [eax].ViFlags, VDM_INT_HOOKED ; need to reflect to PM? jnz V86PassThrough ; if nz, no, we need to dispatch int xor eax, eax mov ecx, ss:[ecx*4] ; (ecx) = Int nn handler xor edx, edx mov [esp].TsSegDs, ecx ; [esp].Ds = intnn handler mov ax, word ptr [esp].TsHardwareSegSs ; (eax)= hardware SS shl eax,4 mov dx, word ptr [esp].TsHardwareEsp ; (edx)= Hardware sp sub dx, 6 add eax, edx ; (eax) = User stack mov ecx, [esp].TsEflags test ss:_KeI386VdmIoplAllowed,1 jnz short @f mov edx, ss:[ebx] ; get vdm states and ecx, NOT EFLAGS_INTERRUPT_MASK .errnz (EFLAGS_INTERRUPT_MASK - VDM_VIRTUAL_INTERRUPTS) and edx,VDM_VIRTUAL_INTERRUPTS OR VDM_VIRTUAL_AC test _KeI386VirtualIntExtensions, dword ptr V86_VIRTUAL_INT_EXTENSIONS jz IntnnMergeIF ;VIF extension is enabled, we should migrate EFLAGS_VIF instead of ;VDM_VIRTUAL_INTERRUPT to the iret frame eflags IF. ;When VIF extension is enabled, RI_BIT_MASK is turned on. This in turn, ;redirects the FCLI/FSTI macro to execute cli/sti directly instead ;of simulation. Without this, we might disable v86 mode interrupt ;without the applications knowing it. and edx, VDM_VIRTUAL_AC ;keep VDM_VIRTUAL_AC only or ecx, edx ;eflags + ac (IF is off) mov edx, ecx and edx, EFLAGS_VIF .errnz ((EFLAGS_VIF SHR 10) - EFLAGS_INTERRUPT_MASK) ror edx, 10 ;VIF -> IF IntnnMergeIF: or ecx, edx ;merge IF or ecx, IOPL_MASK mov word ptr ss:[eax+4], cx ; push flags mov ecx, [esp].TsSegCs mov edx, [esp].TsEip mov word ptr ss:[eax+2], cx ; push cs add dx, 2 MPLOCK and ss:[ebx], NOT VDM_VIRTUAL_INTERRUPTS mov word ptr ss:[eax], dx ; push ip (skip int nn) and [esp].TsEflags, NOT (EFLAGS_NT_MASK OR EFLAGS_TF_MASK) IntnnExit: mov ecx, [esp].TsSegDs ; (ecx) = V86 intnn handler sub word ptr [esp].TsHardwareEsp, 6 mov [esp].TsEip, ecx shr ecx,16 mov [esp].TsSegCs, cx ; cs:ip on trap frame is updated jmp V86Exit_1 @@: or ecx, IOPL_MASK mov word ptr ss:[eax+4], cx ; push flags mov ecx, [esp].TsSegCs mov edx, [esp].TsEip mov word ptr ss:[eax+2], cx ; push cs add dx, 2 mov word ptr ss:[eax], dx ; push ip (skip int nn) and [esp].TsEflags, NOT (EFLAGS_INTERRUPT_MASK OR EFLAGS_NT_MASK OR EFLAGS_TF_MASK) jmp short IntnnExit ALIGN 4 V86Iret: test ss:[ebx], VDM_INTERRUPT_PENDING jnz V86PassThrough xor ecx, ecx mov cx,word ptr [esp].TsHardwareSegSS xor edx, edx shl ecx,4 mov dx,word ptr [esp].TsHardwareEsp add ecx,edx ; (ecx) -> User stack mov dx,word ptr ss:[ecx+4] ; get flag value mov ecx, ss:[ecx] ; (ecx) = ret cs:ip and edx, NOT (EFLAGS_IOPL_MASK OR EFLAGS_NT_MASK) mov eax,edx or edx, (EFLAGS_V86_MASK OR EFLAGS_INTERRUPT_MASK) and eax, EFLAGS_INTERRUPT_MASK MPLOCK and ss:[ebx],NOT VDM_VIRTUAL_INTERRUPTS MPLOCK or ss:[ebx],eax ;; Pentium CPU traps iret if ;; 1)client's TF is ON or 2) VIP is on and the client's IF is ON ;; We have to propagate IF to VIF if virtual interrupt extension is ;; enabled test dword ptr _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS jz v86_iret_01 and edx, NOT EFLAGS_VIF ;eax must contain ONLY ;INTERRUPT_MASK!!!! .errnz (EFLAGS_INTERRUPT_MASK SHL 10) - EFLAGS_VIF rol eax, 10 or edx, eax v86_iret_01: mov [esp].TsEFlags,edx ; update flags in trap frame mov eax, ecx shr ecx, 16 and eax, 0ffffh add word ptr [esp].TsHardwareEsp, 6 ; update sp on trap frame mov [esp].TsSegCs,ecx ; update cs mov [esp].TsEip, eax ; update ip ; at this point cx:ax is the addr of the ip where v86 mode ; will return. Now we will check if this returning instruction ; is a bop. if so we will directly dispatch the bop from here ; saving a full round trip. This will be really helpful to ; com apps. ifdef NT_UP mov byte ptr SS:[P0PCRADDRESS][PcVdmAlert], 10h else mov byte ptr PCR[PcVdmAlert], 010h endif shl ecx, 4 mov ax, ss:[ecx+eax] ; Could fault cmp ax, 0c4c4h jne V86Exit_1 mov ecx, [esp].TsEcx mov ebx, [esp].TsEbx mov eax, [esp].TsEax mov edx, [esp].TsEdx add esp, TsEip if FAST_BOP eq 0 ifdef NT_UP mov byte ptr SS:[P0PCRADDRESS][PcVdmAlert], 0 else mov byte ptr PCR[PcVdmAlert], 0 endif endif jmp _KiTrap06 V86Trap10Recovery: jmp V86Exit_1 ; If we come here, it means we hit some kind of trap while processing the ; V86 trap in a fast way. We need to process the instruction in a normal ; way. For this case, we simply abort the current processing and restart ; it via reguar v86 trap processing. (This is a very rare case.) ;; public V86TrapDRecovery V86TrapDRecovery: mov ecx, [esp].TsEcx mov ebx, [esp].TsEbx mov eax, [esp].TsEax mov edx, [esp].TsEdx add esp, TsErrCode jmp KtdV86Slow ; If we come here, it means we can not process the trapped instruction. ; We will build a trap frame and use regular way to process the instruction. ; Since this could happen if interrupt is pending or the instruction trapped ; is not in the list of instructions which we handle. We need to "continue" ; the processing instead of abort it. ALIGN 4 V86PassThrough: ifdef NT_UP mov byte ptr SS:[P0PCRADDRESS][PcVdmAlert], 0 else mov byte ptr PCR[PcVdmAlert], 0 endif ; eax, ebx, ecx, edx have been saved already ; Note, we don't want to destroy ecx, and edx mov [esp].TsEbp, ebp mov [esp].TsEsi, esi mov [esp].TsEdi, edi mov ebx, KGDT_R0_PCR mov esi, KGDT_R3_DATA OR RPL_MASK if DBG mov [esp].TsPreviousPreviousMode, -1 mov [esp]+TsDbgArgMark, 0BADB0D00h endif mov fs, bx mov ds, esi mov es, esi mov ebp, esp cld test byte ptr PCR[PcDebugActive], -1 jz 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 mov [ebp]+TsDr6,esi mov [ebp]+TsDr7,edi ; Load KernelDr* into processor mov edi,dword ptr fs:[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 @@: jmp KtdV86Slow2 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 test dword ptr [TFrame]+TsEflags, EFLAGS_V86_MASK ; 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 short 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 short a ; if eq, no user APC pending mov ebx, TFrame ifnb 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 mov ecx, APC_LEVEL fstCall KfRaiseIrql 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 stdCall _KiDeliverApc, <1, 0, ebx> pop ecx ; (ecx) = OldIrql fstCall KfLowerIrql ifnb mov eax, [ebx].TsEax ; Restore eax, just in case endif cli jmp short 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, esp add eax, fs:PcstackLimit mov esp, eax 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=kernl jnz kss60 ; nz, prevmode=user, go return ; raise bugcheck if prevmode=kernel stdCall _KeBugCheck, 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 ; ENTER_DR_ASSIST kfce_a, kfce_t,NoAbiosAssist,NoV86Assist align 16 PUBLIC _KiFastCallEntry _KiFastCallEntry proc ; At entry: ; EAX = service number ; EDX = Pointer to caller's arguments ; ECX = User return address ; Create a stack frame like a call to inner privilege ; Load ESP from Tss.Esp0. ints are disabled and esp is not loaded. ifndef NT_UP mov esp, KGDT_R0_PCR mov fs, sp mov esp, fs:PCR[PcTss] else mov esp, ss:PCR[PcTss] endif ;; NT_UP mov esp, ss:[esp].TssEsp0 push KGDT_R3_DATA OR RPL_MASK ; Push user SS push edx ; Push ESP+4 sub dword ptr [esp], 4 pushfd ; Push EFlags or dword ptr [esp], EFLAGS_INTERRUPT_MASK push KGDT_R3_CODE OR RPL_MASK ; Push user CS push ecx ; Push Return EIP ifndef NT_UP ; For the MP case, FS is already loaded above ENTER_SYSCALL kfce_a, kfce_t, NoFSLoad else ; set up trap frame and save state ENTER_SYSCALL kfce_a, kfce_t endif ;; NT_UP jmp _KiSystemServiceRepeat _KiFastCallEntry endp ENTER_DR_ASSIST kss_a, kss_t,NoAbiosAssist,NoV86Assist ; General System service entrypoint align 16 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 or ebx, [ecx]+TbGdiBatchCount ; 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 zero argument. Copy ; is alway done reguardless of number of arguments. This is because zero ; argument 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]+ThKernelApcDisable ; (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 stdCall _KeGetCurrentIrql or al, al ; bogus irql, go bugcheck jnz kss100 kss50a: endif ; Make actual call to system service kssdoit: call ebx ; call system service kss60: if DBG mov ebx,PCR[PcPrcbData+PbCurrentThread] ; (ebx)-> Current Thread ; Check for return to user mode at elevated IRQL. test byte ptr [ebp]+TsSegCs,MODE_MASK jz short kss50b mov esi, eax stdCall _KeGetCurrentIrql 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, [ebx]+ThApcStateIndex jne kss120 mov edx, [ebp-8] cmp dl, [ebx]+ThKernelApcDisable jne kss120 endif ; 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 stdCall _KeBugCheck, kss120: stdCall _KeBugCheck, endif ret _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 test dword ptr [esp+8], EFLAGS_V86_MASK jnz ktgc20 ; if kernel mode, must be get tick count test [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 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 or 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,PCR[PcInitialStack] ; 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 ; 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].FpCr0NpxState ; mov [ebx].FpCr0NpxState,edx ; mov edx,PCR[PcTss] ; get address of task switch segment mov PCR[PcInitialStack],ebx ; restore stack check base address sub ebx,TsV86Gs - TsHardwareSegSs ; bias for missing V86 fields mov [edx].TssEsp0,ebx ; restore kernel entry stack address mov esp,ecx ; trim stack back to callback frame add esp,4 ; sti ; enable interrupts pop [eax].ThTrapFrame ; restore current trap frame address 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 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 ; cli ; disable interrupts DISPATCH_USER_APC ebp, ReturnCurrentEax EXIT_ALL NoRestoreSegs, NoRestoreVolatile _KiSetLowWaitHighThread endp page ,132 subttl "Common Trap Exit" ; 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. align 4 public _KiExceptionExit _KiExceptionExit proc .FPO (0, 0, 0, 0, 0, FPO_TRAPFRAME) cli ; disable interrupts DISPATCH_USER_APC ebp ; Exit from Exception EXIT_ALL ,,NoPreviousMode _KiExceptionExit endp ; 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 cPublicProc Kei386EoiHelper, 0 .FPO (0, 0, 0, 0, 0, FPO_TRAPFRAME) ASSERT_FS DISPATCH_USER_APC esp EXIT_ALL ,,NoPreviousMode stdENDP Kei386EoiHelper ; 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, or eax, eax jnz kui10 ; spurious interrupt add esp, 8 EXIT_ALL ,,NoPreviousMode 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 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, 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 test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK 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, 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 test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK 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, 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 returing to UserMode that we take an exception. ; Any expection 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. ; assumses 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. VERIFY_BASE_TRAP_FRAME macro local vbfdone mov eax, esp sub eax, PCR[PcInitialStack] ; Bias out this stack add eax, KTRAP_FRAME_LENGTH ; adjust for base frame 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 - 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 VERIFY_BASE_TRAP_FRAME macro. ; Makes lots of assumptions about esp & trap frames ; Arguments: ; Stack: ; +-------------------------+ ; | | ; | | ; | Npx save area | ; | | ; | | ; +-------------------------+ ; | (possible mvdm regs) | ; +-------------------------+ <- fs: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) | ; +-------------------------+ <- fs: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 edi, PCR[PcInitialStack] sub edi, 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 destoryed 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, PCR[PcInitialStack] sub edi, 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 retores 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 test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK 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, @@: endif sti ; Flat mode ; The intel processor raises a divide by zero expcetion on DIV instruction ; which overflows. To be compatible we 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, 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 test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process? jz 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 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 ; 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 test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK jnz kit01_30 ; fault occured in V86 mode => Usermode test word 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_BIT 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 test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process? jz kit01_05 stdCall _Ki386VdmReflectException_A, <01h> test ax,0FFFFh jz Kit01_20 jmp _KiExceptionExit _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 ; 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 ; Let the HAL have a crack at it before we crash stdCall _HalHandleNMI,<0> mov eax, offset FLAT:_ZwUnmapViewOfSection@8 sub eax, esp cmp eax, 0a00h jnc short @f ; not on a real stack, crash stdCall _KeBugCheckEx, @@: ; We're back, therefore the Hal has dealt with the NMI. (Crashing ; is done in the Hal for this special case.) 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 tast switch popfd iretd ; Return from NMI jmp short _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 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 test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK jnz kit03_30 ; fault occured in V86 mode => Usermode test word 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 test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process? jz 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 test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK jnz short Kt0430 ; in a vdm, reflect to vdm 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 test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process? jz 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 test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK jnz Kt0530 ; fault in V86 mode 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 test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process? jz 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 386. 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 ; sudeepb 08-Dec-1992 KiTrap06 is performance critical for VDMs, ; while it hardly ever gets executed in native mode. So this whole ; code is tuned for VDMs. test dword ptr [esp]+8h,EFLAGS_V86_MASK jz Kt060i if FAST_BOP FAST_V86_TRAP_6 endif Kt6SlowBop: push 0 ; push dummy error code ENTER_TRAPV86 kit6_a, kit6_v Kt06VMpf: ; Raise Irql to APC level before enabling interrupts mov ecx, APC_LEVEL fstCall KfRaiseIrql push eax ; Save OldIrql sti call VdmDispatchBop test al,0fh jnz short Kt061i stdCall _Ki386VdmReflectException,<6> test al,0fh jnz Kt061i pop ecx ; (TOS) = OldIrql fstCall KfLowerIrql jmp Kt0635 Kt061i: pop ecx ; (TOS) = OldIrql fstCall KfLowerIrql cli test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK jz Kt062i 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) Kt062i: jmp _KiExceptionExit Kt060i: 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 ; Check to see if this process is a vdm mov ebx,PCR[PcPrcbData+PbCurrentThread] mov ebx,[ebx]+ThApcState+AsProcess test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process? jnz Kt0650 ; Invalid Opcode exception could be either INVALID_LOCK_SEQUENCE or ; ILLEGAL_INSTRUCTION. kt0605: sti mov eax, [ebp]+TsSegCs push ds mov ds, ax mov esi, [ebp]+TsEip ; (ds:esi) -> address of faulting instruction mov ecx, MAX_INSTRUCTION_PREFIX_LENGTH Kt0610: lods byte ptr [esi] ; (al)= instruction byte cmp al, MI_LOCK_PREFIX ; Is it a lock prefix? je short Kt0640 ; Yes, raise Invalid_lock exception loop short Kt0610 ; keep on looping ; Set up exception record for raising Illegal instruction exception Kt0630: pop ds 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 ds 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 mov ecx, APC_LEVEL fstCall KfRaiseIrql push eax ; SaveOldIrql sti call VdmDispatchBop test al,0fh jnz short Kt0660 stdCall _Ki386VdmReflectException,<6> test al,0fh jnz Kt0660 pop ecx ; (TOS) = OldIrql fstCall KfLowerIrql jmp short Kt0635 Kt0660: pop ecx ; (TOS) = OldIrql fstCall KfLowerIrql jmp _KiExceptionExit _KiTrap06 endp page ,132 subttl "Coprocessor Not Avalaible" ; Routine Description: ; Handle Coprocessor not avaliable exception. ; If we are REALLY emulating the 80387, the trap 07 vector is edited ; to point directly at the emulator's entry point. So this code is ; only hit when an 80387 DOES exist. ; The current threads coprocessor state is loaded into the ; coprocessor. If the coprocessor has a different threads state ; in it (UP only) it is first saved away. The thread is then continued. ; Note: the threads 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 threads 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, PCR[PcInitialStack] ; (ecx) -> top of kernel stack cli ; don't context switch test dword ptr [ecx].FpCr0NpxState,CR0_EM jnz Kt07140 Kt0701: cmp byte ptr [eax].ThNpxState, NPX_STATE_LOADED je Kt0715 ; Trap occured and this threads NPX state is not loaded. Load it now ; and resume the application. If someone elses state is in the coprocessor ; (uniprocessor implementation only) then save it first. mov ebx, cr0 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 threads coprocessor state Kt0703b: mov byte ptr [edx].ThNpxState, NPX_STATE_NOT_LOADED Kt0704: endif ; Load current threads coprocessor state into the coprocessor ; (eax) - CurrentThread ; (ecx) - CurrentThreads NPX save area ; (ebx) - CR0 ; (ebp) - trap frame ; Interrupts disabled ; frstor might generate a NPX execption 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 threads CR0 NPX state sti test ebx, CR0_TS ; Setting TS? (delayed error) jz _KiExceptionExit ; No - continue 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) - CurrentThreads 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: mov eax, PCR[PcPrcbData+PbCurrentThread] mov ecx, PCR[PcInitialStack] ; (ecx) -> top of kernel stack Kt0715: test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK jnz Kt07130 ; v86 mode 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 suppose to ; cause an exception or not. ; (ecx) - CurrentThreads NPX save area Kt0716: stdCall _Ki386CheckDelayedNpxTrap, or al, al jnz _KiExceptionExit ; Already handled mov eax, PCR[PcPrcbData+PbCurrentThread] mov ecx, PCR[PcInitialStack] ; (ecx) -> top of kernel stack Kt0720: ; Some type of coprocessor exception has occured for the current thread. ; (eax) - CurrentThread ; (ecx) - CurrentThreads 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 threads 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 threads 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 ; 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 test eax, FSW_INVALID_OPERATION ; Is it an invalid op exception? jz short Kt0740 ; if z, no, go Kt0740 test eax, 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 test eax, 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 test eax, 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 test eax, 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 test eax, 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 test eax, 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 _KeBugCheck, Kt07110: ; Check to see if this process is a vdm mov ebx,PCR[PcPrcbData+PbCurrentThread] mov ebx,[ebx]+ThApcState+AsProcess test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process? jz Kt0720 ; no, dispatch exception Kt07130: ; 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 mov ecx, APC_LEVEL fstCall KfRaiseIrql push eax ; Save OldIrql sti stdCall _VdmDispatchIRQ13, ; ebp - Trapframe test al,0fh jnz Kt07135 pop ecx ; (TOS) = OldIrql fstCall KfLowerIrql jmp Kt0720 ; could not reflect, gen exception Kt07135: pop ecx ; (TOS) = OldIrql fstCall KfLowerIrql 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 test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process? jz 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 _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 align dword public _KiTrap08 _KiTrap08 proc .FPO (0, 0, 0, 0, 0, 2) cli ; 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[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] mov PCR[PcTss], ecx ; 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 ; The original machine context is in original task's TSS @@: stdCall _KeBugCheckEx, 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. test dword ptr [ebp]+TsEFlags, EFLAGS_V86_MASK jnz short Kt0a10 test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previous mode = USER jz short Kt0a20 Kt0a10: test dword ptr [ebp]+TsEFlags, EFLAGS_OF_BIT 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 test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process? jz short Kt0b20 ; Raise Irql to APC level before enabling interrupts mov ecx, APC_LEVEL fstCall KfRaiseIrql push eax ; Save OldIrql sti stdCall _Ki386VdmSegmentNotPresent test eax, 0ffffh jz short Kt0b15 pop ecx ; (TOS) = OldIrql fstCall KfLowerIrql jmp _KiExceptionExit Kt0b15: pop ecx ; (TOS) = OldIrql fstCall KfLowerIrql 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. sti 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 test dword 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 ; 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 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: mov dword ptr [edx], 0 ; set the segment reg to 0 such that ; we will trap in user mode. EXIT_ALL NoRestoreSegs,,NoPreviousMode ; RETURN Kt0b199: mov eax, EXCEPTION_SEGMENT_NOT_PRESENT ; (eax) = exception type jmp _KiSystemFatalException ; terminate the system _KiTrap0B endp 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 vioalation 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 occurs 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 test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK jnz Kt0c30 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: sti ; 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 mov eax, [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 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 involed, 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 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: 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 test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process? jz Kt0c00 Kt0c30: stdCall _Ki386VdmReflectException_A,<0ch> test al,0fh jz Kt0c00 jmp _KiExceptionExit _KiTrap0C 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: ; 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=kernl jnz Kt0d103 ; nz, prevmode=user, go return ; raise bugcheck if prevmode=kernel stdCall _KeBugCheck, 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 its super critical to ; VDMs. So here we are doing every thing to make v86 mode most efficient. test dword ptr [esp]+0ch,EFLAGS_V86_MASK jz Ktdi if FAST_V86_TRAP FAST_V86_TRAP_D endif KtdV86Slow: ENTER_TRAPV86 kitd_a, kitd_v KtdV86Slow2: ; Raise Irql to APC level, before enabling interrupts mov ecx, APC_LEVEL fstCall KfRaiseIrql push eax ; Save OldIrql sti stdCall _Ki386DispatchOpcodeV86 KtdV86Exit: test al,0FFh jnz short Ktdi2 stdCall _Ki386VdmReflectException,<0dh> test al,0fh jnz short Ktdi2 pop ecx ; (TOS) = OldIrql fstCall KfLowerIrql jmp Kt0d105 Ktdi2: pop ecx ; (TOS) = OldIrql fstCall KfLowerIrql cli test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK 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 or FAST_V86_TRAP cmp byte ptr PCR[PcVdmAlert], 0 ; See Kt0eVdmAlert 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 ; add edx, 5 * 4 + NPX_FRAME_LENGTH + (TsV86Gs - TsHardwareSegSs) ; cmp [ebp]+TsEsi, edx ; esi should be the esp of ; Ki386SetupAndExitToV86Code mov edx, [ebp].TsEsi ; jne short Kt0d0005 mov esp, edx jmp Ki386BiosCallReturnAddress ; with interrupts off Kt0d0005: ; Since the CS is bogus, we can not 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 sti ; 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 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: 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 test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process? jnz Kt0d110 sti test 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 fs,gs by setting to Zero kt0d0202: add dword ptr [ebp].TsEip, 2 jmp Kt0d005 kt0d0201: test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process? jz short Kt0d03 mov eax, [ebp]+TsEip ; (eax)->faulted Instruction mov eax, [eax] ; (eax)= opcode of faulted instruction mov edx, ebp ; (edx)-> trap frame add edx, TsSegFs ; [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 push ds ; 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 mov ax, [ebp]+TsSegCs mov ds, ax mov ecx, MAX_INSTRUCTION_LENGTH Kt0d05: 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 Kt0d05 ; go check for prefix byte again pop PCR[PcExceptionList] add esp, 8 ; clear stack jmp Kt0630 ; 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 short Kt0d80 ; if e, yes, go Kt0d80 cmp al, MI_LIDT_MASK ; Is it a LIDT instruction? je short Kt0d80 ; if e, yes, go Kt0d80 cmp al, MI_LMSW_MASK ; Is it a LMSW instruction? je short Kt0d80 ; if e, yes, go Kt0d80 jmp Kt0d100 ; else, raise access violation except Kt0d30: and al, MI_SPECIAL_MOV_MASK ; Is it a special mov instruction? jnz kt0d80 ; if nz, yes, go raise priv instr ; (Even though the regular mov may ; have the special_mov_mask bit set, ; they are NOT 2 byte opcode instr.) 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, fs: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 pop ds 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: pop ds 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 mov ecx, APC_LEVEL fstCall KfRaiseIrql push eax ; Save OldIrql sti stdCall _Ki386DispatchOpcode test eax,0FFFFh jnz short Kt0d120 stdCall _Ki386VdmReflectException,<0dh> test al,0fh jnz short Kt0d120 pop ecx ; (TOS) = OldIrql fstCall KfLowerIrql jmp short Kt0d105 Kt0d120: pop ecx ; (TOS) = OldIrql fstCall KfLowerIrql 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 test dword ptr [ebp]+TsEFlags, EFLAGS_V86_MASK ; 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_V86_TRAP or FAST_BOP cmp byte ptr PCR[PcVdmAlert], 0 jne Kt0eVdmAlert endif VERIFY_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 sub esp, 16 mov eax,[ebp]+TsSegCs and eax,MODE_MASK ; (eax) = arg3: PreviousMode mov [esp+12], ebp ; pass in the trap frame base mov [esp+8], eax mov [esp+4], edi mov eax, [ebp]+TsErrCode ; (eax)= error code and eax, ERR_0E_STORE ; (eax)= 0 if fault caused by read ; = 2 if fault caused by write shr eax, 1 ; (eax) = 0 if read fault, 1 if write fault mov [esp+0], eax ; arg3: load/store indicator call _MmAccessFault@16 if DEVL cmp _PsWatchEnabled,0 jz short xxktskip mov ebx, [ebp]+TsEip stdCall _PsWatchWorkingSet, xxktskip: endif or eax, eax ; sucessful? jge Kt0e10 ; yes, go exit mov ecx,PCR[PcGdt] ; Form Ldt Base movzx ebx,byte ptr [ecx + KGDT_LDT].KgdtBaseHi shl ebx,8 or bl,byte ptr [ecx + KGDT_LDT].KgdtBaseMid shl ebx,16 or bx,word ptr [ecx + KGDT_LDT].KgdtBaseLow or ebx,ebx ; check for zero jz short Kt0e05 ; no ldt cmp edi,ebx jb short Kt0e05 ; address not in LDT ; Form Ldt limit movzx edx,byte ptr [ecx + KGDT_LDT].KgdtLimitHi and edx,000000FFh shl edx,16 or dx,word ptr [ecx + KGDT_LDT].KgdtLimitLow add ebx,edx cmp edi,ebx jae short Kt0e05 ; too high to be an ldt address sldt cx ; Verify this process has an LDT test ecx, 0ffffh ; Check CX jz short Kt0e05 ; If ZY, no LDT mov eax, [ebp]+TsErrCode ; (eax)= error code and eax, ERR_0E_STORE ; (eax)= 0 if fault caused by read ; = 2 if fault caused by write shr eax, 1 ; (eax) = 0 if read fault, 1 if write fault ; call page fault handler stdCall _MmAccessFault, or eax, eax ; successful? jge Kt0e10 ; if z, yes, go exit mov ebx,[ebp]+TsSegCs ; restore previous mode and ebx,MODE_MASK mov [esp + 8],ebx ; 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 jne short @f mov ecx, [ebp].TsEbp ; (eax)->TrapFrame of SysService test [ecx].TsSegCs, MODE_MASK jz short @f ; caller of SysService is k mode, we ; will let it bugchecked. mov [ebp].TsEip, offset FLAT:kss60 mov eax, STATUS_ACCESS_VIOLATION mov [ebp].TsEax, eax jmp _KiExceptionExit @@: mov ecx, [ebp]+TsErrCode ; (ecx) = error code and ecx, ERR_0E_STORE ; (ecx) = 0 if fault caused by read ; 2 if fault caused by write shr ecx,1 ; (ecx) = load/store indicator ; Did the fault occur in a VDM? test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK jnz Kt0e7 ; Did the fault occur in a VDM while running in protected mode? mov esi,PCR[PcPrcbData+PbCurrentThread] mov esi,[esi]+ThApcState+AsProcess test byte ptr [esi]+PrVdmFlag,0fh ; is this a vdm process? jz short Kt0e9 ; z -> not vdm test dword ptr [ebp]+TsSegCs, MODE_MASK jz short kt0e8 cmp word ptr [ebp]+TsSegCs, KGDT_R3_CODE OR RPL_MASK jz kt0e9 ; z -> not vdm Kt0e7: mov esi, eax stdCall _VdmDispatchPageFault, test al,0fh ; returns TRUE, if success jnz Kt0e11 ; Exit,No need to call the debugger mov eax, esi jmp short Kt0e9 Kt0e8: ; Did the fault occur in our kernel VDM support code? ; At this point, we know: ; . Current process is VDM process ; . this is a unresolvable pagefault ; . the fault occurred in kernel mode. ; First make sure this is not pagefault at irql > 1 ; which should be bugchecked. cmp eax, STATUS_IN_PAGE_ERROR or 10000000h je Kt0e12 cmp word ptr [ebp]+TsSegCs, KGDT_R0_CODE ; Did fault occur in kernel? jnz short Kt0e9 ; if nz, no, not ours cmp PCR[PcExceptionList], EXCEPTION_CHAIN_END jnz short Kt0e9 ; there is at least a handler to ; handle this exception mov ebp, PCR[PcInitialStack] xor ecx, ecx ; set to fault-at-read sub ebp, KTRAP_FRAME_LENGTH mov esp, ebp ; clear stack (ebp)=(esp)->User trap frame mov esi, [ebp]+TsEip ; (esi)-> faulting instruction jmp Kt0e9b ; go dispatching the exception to user 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: if DEVL 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 endif Kt0e11: mov esp,ebp ; (esp) -> trap frame jmp _KiExceptionExit ; join common code Kt0e12: stdCall _KeGetCurrentIrql ; (eax) = OldIrql Kt0e12a: lock inc ds:_KiHardwareTrigger ; trip hardware analyzer ; bugcheck a, addr, irql, load/store, pc mov ecx, [ebp]+TsErrCode ; (ecx)= error code and ecx, ERR_0E_STORE ; (ecx)= 0 if fault caused by read shr ecx, 1 ; (ecx) = 0 if read fault, 1 if write fault mov esi, [ebp]+TsEip ; [esi] = faulting instruction stdCall _KeBugCheckEx, Kt0e12b: ; In V86 mode with iopl allowed it is OK to handle ; a page fault with interrupts disabled test dword ptr [ebp]+TsEFlags, EFLAGS_V86_MASK jz short Kt0e12c cmp _KeI386VdmIoplAllowed, 0 jnz Kt0e01 Kt0e12c: cmp _KiFreezeFlag,0 ; during boot we can take jnz Kt0e01 ; 'transistion failts' 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 or FAST_V86_TRAP 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 byte ptr PCR[PcVdmAlert], 0 IF FAST_V86_TRAP cmp al, 0Dh jne short @f mov eax, offset FLAT:V86TrapDRecovery jmp short KtvaExit @@: cmp al, 10h jne short @f mov eax, offset FLAT:V86Trap10Recovery jmp short KtvaExit @@: ENDIF ; FAST_V86_TRAP IF FAST_BOP cmp al, 6 jne short @f mov eax, offset FLAT:V86Trap6Recovery jmp short KtvaExit @@: ENDIF ; FAST_BOP mov eax, [ebp].TsEip KtvaExit: mov [ebp].TsEip, eax mov esp,ebp ; (esp) -> trap frame jmp _KiExceptionExit ; join common code if DBG @@: int 3 endif ENDIF ; FAST_BOP OR FAST_V86_TRAP _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 inviked 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+PbNpxThread] ; Correct context for fault? cmp eax, PCR[PcPrcbData+PbCurrentThread] je Kt0710 ; 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... mov eax, [eax].ThInitialStack sub eax, NPX_FRAME_LENGTH ; Space for NPX_FRAME or dword ptr [eax].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. ; This exception will only occur on 486 machines. The 386 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 test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK jnz Kt11_01 ; v86 mode => usermode 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, Kt13_10: mov ecx, PCR[PcInitialStack] ; (ecx) -> top of kernel stack ; TrapFrame is built by ENTER_TRAP. ; XMMI are accessible from all IA execution modes: ; Protected Mode, Real address mode, Virtual 8086 mode Kt13_15: test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK jnz Kt13_130 ; v86 mode ; eflags.vm=0 (not v86 mode) 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) - CurrentThreads 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 threads 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 test eax, 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 test eax, 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 test eax, 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 test eax, 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 test eax, 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 test eax, 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, ; 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 test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process? jz 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 mov ecx, APC_LEVEL fstCall KfRaiseIrql push eax ; Save OldIrql sti stdCall _VdmDispatchIRQ13, ; ebp - Trapframe test al,0fh jnz Kt13_135 pop ecx ; (TOS) = OldIrql fstCall KfLowerIrql jmp Kt13_20 ; could not reflect, gen exception Kt13_135: pop ecx ; (TOS) = OldIrql fstCall KfLowerIrql jmp _KiExceptionExit ; eflags.vm=0 (not v86 mode) ; previousMode=SYSTEM Kt13_05: ; stop the system stdCall _KeBugCheckEx, 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 80387 detects an error, it raises its error line. This ; was supposed to be routed directly to the 386 to cause a trap 16 ; (which would actually occur when the 386 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 387 ; 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 386 will ; generate the trap 07 before the 387 gets a chance to raise another ; error interrupt. We'll want to save the 387 state in the trap 07 ; handler WITH the error information. ; Note the caller must clear the 387 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 threads NPX context is requested (most likely by a debugger) ; this function is called to flush the threads 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 test ebx, 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 threads NPX state mov ecx, [edi].PcInitialStack ; (ecx) -> top of kernel stack 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 test ebx, 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].PcInitialStack ; (ecx) -> top of kernel stack 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+PcInitialStack] ; (ecx) -> top of kernel stack 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 threads 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, 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 stdCall _KiContinue, 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. stdCall _KeTestAlertThread, ; test alert for current thread Nc10: 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 (epb+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 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) stdCall _KiRaiseException, 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, we'll return to our caller, who ; will raise a new exception. 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 mov ecx, APC_LEVEL fstCall KfRaiseIrql 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 fstCall KfLowerIrql pop eax ; pops second dword ret 4 stdENDP _Ki386VdmReflectException_A _TEXT$00 ends end