     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
        .list


        page ,132
        extrn   ExpInterlockedPopEntrySListFault:DWORD
        extrn   ExpInterlockedPopEntrySListResume:DWORD
        extrn   _KiHardwareTrigger:dword
        extrn   _KiBugCheckData:dword
        EXTRNP  _KeGetCurrentIrql,0

        EXTRNP  _HalHandleNMI,0
        EXTRNP  HalBeginSystemInterrupt,2,,FASTCALL
        EXTRNP  HalEndSystemInterrupt,1,,FASTCALL
        EXTRNP  _KiDispatchException,4
        EXTRNP  _MmAccessFault,3
        EXTRNP  _KeBugCheck,1
        EXTRNP  _KeBugCheckEx,5
        EXTRNP  _KeTestAlertThread,1
        EXTRNP  _KiContinue,3
        EXTRNP  _KiRaiseException,5
        extrn   _DbgPrint:proc
        EXTRNP  _Ki386CheckDivideByZeroTrap,1
        extrn   SwapContext:near
        extrn   _KiGDT:BYTE
        extrn   _KiPCR:DWORD
        extrn   _KiDoubleFaultTSS:DWORD
ifdef DEVKIT
        EXTRNP  _KdSetOwedBreakpoints
        extrn   _KdpOweBreakpoint:dword
endif

;
; Equates for exceptions which cause system fatal error
;

EXCEPTION_DIVIDED_BY_ZERO       EQU     0
EXCEPTION_DEBUG                 EQU     1
EXCEPTION_NMI                   EQU     2
EXCEPTION_INT3                  EQU     3
EXCEPTION_BOUND_CHECK           EQU     5
EXCEPTION_INVALID_OPCODE        EQU     6
EXCEPTION_NPX_NOT_AVAILABLE     EQU     7
EXCEPTION_DOUBLE_FAULT          EQU     8
EXCEPTION_NPX_OVERRUN           EQU     9
EXCEPTION_INVALID_TSS           EQU     0AH
EXCEPTION_SEGMENT_NOT_PRESENT   EQU     0BH
EXCEPTION_STACK_FAULT           EQU     0CH
EXCEPTION_GP_FAULT              EQU     0DH
EXCEPTION_RESERVED_TRAP         EQU     0FH
EXCEPTION_NPX_ERROR             EQU     010H
EXCEPTION_ALIGNMENT_CHECK       EQU     011H

;
; Exception flags
;

EXCEPT_UNKNOWN_ACCESS           EQU     0H
EXCEPT_LIMIT_ACCESS             EQU     10H

;
; page fault read/write mask
;

ERR_0E_STORE                    EQU     2

;
; EFLAGS single step bit
;

EFLAGS_TF_BIT                   EQU     100h
EFLAGS_OF_BIT                   EQU     4000H

;
;   Force assume into place
;

_TEXT   SEGMENT PARA PUBLIC 'CODE'
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING
_TEXT   ENDS

_DATA   SEGMENT PARA 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
BadInterruptMessage db 0ah,7,7,'!!! Unexpected Interrupt %02lx !!!',0ah,00
endif

;++
;
;   DEFINE_SINGLE_EMPTY_VECTOR - helper for DEFINE_EMPTY_VECTORS
;
;--

DEFINE_SINGLE_EMPTY_VECTOR macro    number
IDTEntry    _KiUnexpectedInterrupt&number, D_INT032
_TEXT   SEGMENT
        public  _KiUnexpectedInterrupt&number
_KiUnexpectedInterrupt&number proc
        push    dword ptr (&number)
        jmp     _KiUnexpectedInterruptTail
_KiUnexpectedInterrupt&number endp
_TEXT   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 3f, which is the hardware
;   vector set.
;
;--

NUMBER_OF_IDT_VECTOR    EQU     03fH

DEFINE_EMPTY_VECTORS macro

;
;   Set up
;

        empty_vector = 00H

_TEXT   SEGMENT
IFDEF STD_CALL
        public  _KiStartUnexpectedRange@0
_KiStartUnexpectedRange@0   equ     $
ELSE
        public  _KiStartUnexpectedRange
_KiStartUnexpectedRange     equ     $
ENDIF
_TEXT   ENDS

        rept (NUMBER_OF_IDT_VECTOR - (($ - _KiIDT)/8)) + 1

        DEFINE_SINGLE_EMPTY_VECTOR  %empty_vector
        empty_vector = empty_vector + 1

        endm    ;; rept

_TEXT   SEGMENT
IFDEF STD_CALL
        public  _KiEndUnexpectedRange@0
_KiEndUnexpectedRange@0     equ     $
ELSE
        public  _KiEndUnexpectedRange
_KiEndUnexpectedRange       equ     $
ENDIF
_TEXT   ENDS

        endm    ;; DEFINE_EMPTY_VECTORS macro

IDTEntry macro  name,access
        dd      offset FLAT:name
        dw      access
        dw      KGDT_R0_CODE
        endm

IDTEntryEx macro  name,access,sel
        dd      offset FLAT:name
        dw      access
        dw      sel
        endm

ALIGN 8
                public  _KiIDT, _KiIDTLEN, _KiIDTEnd
_KiIDT          label byte

IDTEntry        _KiTrap00, D_INT032             ; 0: Divide Error
IDTEntry        _KiTrap01, D_INT032             ; 1: DEBUG TRAP
IDTEntryEx      _KiTrap02, D_TASK, KGDT_NMI_TSS ; 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
IDTEntryEx      _KiTrap08, D_TASK, KGDT_DF_TSS  ; 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: Machine-Check
IDTEntry        _KiTrap13, 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

        rept 28H - (($ - _KiIDT)/8)
IDTEntry        0, 0                            ;invalid IDT entry
        endm
IDTEntry        _KiContinueService, D_INT332        ;28: NtContinue service
IDTEntry        _KiRaiseExceptionService, D_INT332  ;29: NtRaiseException service
IDTEntry        0, 0                                ;2A: OBSOLETE: KiGetTickCount service
IDTEntry        0, 0                                ;2B: OBSOLETE: KiCallbackReturn
IDTEntry        0, 0                                ;2C: OBSOLETE: KiSetLowWaitHighThread service
IDTEntry        _KiDebugService,  D_INT332          ;2D: debugger calls
IDTEntry        0, 0                                ;2E: OBSOLETE: system service calls
IDTEntry        _KiTrap0F, D_INT032                 ;2F: Reserved for APIC

;
;   Generate per-vector unexpected interrupt entries for 30 - 3f
;
        DEFINE_EMPTY_VECTORS

_KiIDTLEN       equ     $ - _KiIDT
_KiIDTEnd       equ     $

                public  _KiUnexpectedEntrySize
_KiUnexpectedEntrySize          dd  _KiUnexpectedInterrupt1 - _KiUnexpectedInterrupt0

;
; 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   SEGMENT
        ASSUME  DS:NOTHING, ES:NOTHING, SS:FLAT, FS:NOTHING, GS:NOTHING

        page    ,132
        subttl "NtContinue service"
;++
;
; Routine Description:
;
;    This routine gains control when trap occurs via vector 28H.
;
;    After the trap frame is constructed, control is transferred to
;    NtContinue.  The call will not return.
;
; Arguments:
;
;    eax - ContextRecord parameter to NtContinue.
;    ecx - TestAlert parameter to NtContinue.
;
; Return Value:
;
;    Does not return.
;
;--

align 16
        PUBLIC  _KiContinueService
_KiContinueService proc

        ENTER_SYSCALL
        stdCall _NtContinue, <eax, ecx>

_KiContinueService endp

        page    ,132
        subttl "NtRaiseException service"
;++
;
; Routine Description:
;
;    This routine gains control when trap occurs via vector 29H.
;
;    After the trap frame is constructed, control is transferred to
;    NtRaiseException.  The call will not return.
;
; Arguments:
;
;    eax - ExceptionRecord parameter to NtRaiseException.
;    ecx - ContextRecord parameter to NtRaiseException.
;    edx - FirstChance parameter to NtRaiseException.
;
; Return Value:
;
;    Does not return.
;
;--

align 16
        PUBLIC  _KiRaiseExceptionService
_KiRaiseExceptionService proc

        ENTER_SYSCALL
        stdcall _NtRaiseException, <eax, ecx, edx>

_KiRaiseExceptionService endp

;
;   System service's private version of KiExceptionExit
;   (Also used by KiDebugService)
;
        public  _KiServiceExit
_KiServiceExit:

        cli                                         ; disable interrupts

;
; Exit from SystemService
;

        EXIT_ALL    NoRestoreSegs, NoRestoreVolatile

;++
;
;   _KiServiceExit2 - same as _KiServiceExit BUT the full trap_frame
;       context is restored
;
;--
        public  _KiServiceExit2
_KiServiceExit2:

        cli                             ; disable interrupts

;
; Exit from SystemService
;
        EXIT_ALL                            ; RestoreAll

        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

;
; Exit from Exception
;

        EXIT_ALL    ,,NoPreviousMode

_KiExceptionExit endp


;++
;
;   KiUnexpectedInterruptTail
;
;   Routine Description:
;       This function is jumped to by an IDT entry who has no interrupt
;       handler.
;
;   Arguments:
;
;       (esp) -> base of trap frame.
;       (ebp) -> base of trap frame.
;
;--

        public  _KiUnexpectedInterruptTail
_KiUnexpectedInterruptTail  proc
        ENTER_INTERRUPT PassDwordParm

        inc     dword ptr PCR[PcPrcbData+PbInterruptCount]

        mov     ecx, [ebp]+TsErrCode    ; get IRQ passed through error code
        mov     edx, HIGH_LEVEL
        push    0                       ; make space for OldIrql

        fstCall HalBeginSystemInterrupt

if DBG
        push    dword ptr [ebp]+TsErrCode
        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 "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

; 1 - first chance TRUE
; ebp - trap frame addr
; 0 - Null exception frame
; ecx - exception record addr

        stdCall _KiDispatchException,<ecx, 0, ebp, 1>

        mov     esp, ebp                ; (esp) -> trap frame
        jmp     _KiExceptionExit

CommonDispatchException 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
align dword
        public  _KiTrap00
_KiTrap00       proc

        push    0                       ; push dummy error code
        ENTER_TRAP

;
; Set up exception record for raising Integer_Divided_by_zero exception
; and call _KiDispatchException
;

if DBG
        test    [ebp]+TsEFlags, EFLAGS_INTERRUPT_MASK   ; faulted with
        jnz     short @f                                ; interrupts disabled?

        xor     eax, eax
        mov     esi, [ebp]+TsEip        ; [esi] = faulting instruction
        stdCall _KeBugCheckEx,<IRQL_NOT_LESS_OR_EQUAL,eax,-1,eax,esi>
@@:
endif

        sti

;
; Flat mode
;
; The intel processor raises a divide by zero 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,<ebp>
        mov     ebx, [ebp]+TsEip        ; (ebx)-> faulting instruction
        jmp     CommonDispatchException0Args ; Won't return

_KiTrap00       endp

        page ,132
        subttl "Debug Exception"
;++
;
; Routine Description:
;
;    Handle debug exception.
;
;    The processor triggers this exception for any of the following
;    conditions:
;
;    1. Instruction breakpoint fault.
;    2. Data address breakpoint trap.
;    3. General detect fault.
;    4. Single-step trap.
;    5. Task-switch breadkpoint trap.
;
;
; Arguments:
;
;    At entry, the values of saved CS and EIP depend on whether the
;    exception is a fault or a trap.
;    No error code is provided with the divide error.
;
; Return value:
;
;    None
;
;--
        ASSUME  DS:NOTHING, SS:NOTHING, ES:NOTHING
align dword
        public  _KiTrap01
_KiTrap01       proc

; Set up machine state frame for displaying

        push    0                       ; push dummy error code
        ENTER_TRAP

;
; Set up exception record for raising single step exception
; and call _KiDispatchException
;

        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

_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
align dword
        public  _KiTrap02
_KiTrap02       proc
.FPO (1, 0, 0, 0, 0, 2)
        cli

;
; Clear Nested Task bit in EFLAGS
;
        pushfd
        and     [esp], not 04000h
        popfd

;
; Clear the busy bit in the TSS selector
;
        mov     byte ptr [_KiGDT+KGDT_NMI_TSS+5],089h

;
; Let the HAL have a crack at it before we crash
;
        stdCall _HalHandleNMI

        stdCall _KeBugCheckEx,<UNEXPECTED_KERNEL_MODE_TRAP,2,0,0,0>

_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

align dword
        public  _KiDebugService
_KiDebugService  proc
        push    0                           ; push dummy error code
        ENTER_TRAP
;       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

align dword
        public  _KiTrap03
_KiTrap03       proc
        push    0                       ; push dummy error code
        ENTER_TRAP

   lock inc     ds:_KiHardwareTrigger   ; trip hardware analyzer

        mov     eax, BREAKPOINT_BREAK

KiTrap03DebugService:

;
; 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

_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

align dword
        public  _KiTrap04
_KiTrap04       proc

        push    0                       ; push dummy error code
        ENTER_TRAP

;
; set exception record and arguments and call _KiDispatchException
;
        sti
        mov     ebx, [ebp]+TsEip        ; (ebx)-> instr. after INTO
        dec     ebx                     ; (ebx)-> INTO
        mov     eax, STATUS_INTEGER_OVERFLOW
        jmp     CommonDispatchException0Args ; Never return

_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
align dword
        public  _KiTrap05
_KiTrap05       proc

        push    0                       ; push dummy error code
        ENTER_TRAP

;
; set exception record and arguments and call _KiDispatchException
;
        sti
        mov     ebx, [ebp]+TsEip        ; (ebx)->BOUND instruction
        mov     eax, STATUS_ARRAY_BOUNDS_EXCEEDED
        jmp     CommonDispatchException0Args ; Won't return

_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

align dword
        public  _KiTrap06
_KiTrap06       proc

        push    0                       ; Push dummy error code
        ENTER_TRAP

;
; set exception record and arguments and call _KiDispatchException
;
        sti
        mov     ebx, [ebp]+TsEip        ; (ebx)-> invalid instruction
        mov     eax, STATUS_ILLEGAL_INSTRUCTION
        jmp     CommonDispatchException0Args ; Won't return

_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

align dword
        public  _KiTrap07
_KiTrap07       proc

        push    0                       ; push dummy error code
        ENTER_TRAP
Kt0700:
        mov     eax, PCR[PcPrcbData+PbCurrentThread]
        mov     ecx, PCR[PcStackBase] ; (ecx) -> top of kernel stack
        cli                             ; don't context switch

;
; Bugcheck if attempting to use the FPU from a DPC without first calling
; KeSaveFloatingPointState.
;

if DBG
        cmp     PCR[PcPrcbData.PbDpcRoutineActive],0
        jne     Kt07110
endif

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

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].ThStackBase
        sub     esi, NPX_FRAME_LENGTH   ; Space for NPX_FRAME

        FXSAVE_ESI
        mov     byte ptr [edx].ThNpxState, NPX_STATE_NOT_LOADED
Kt0704:

;
; Load current threads coprocessor state into the coprocessor
;
; (eax) - CurrentThread
; (ecx) - CurrentThreads NPX save area
; (ebx) - CR0
; (ebp) - trap frame
; Interrupts disabled
;

        FXRSTOR_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

;
; 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
;

Kt0710:
        mov     eax, PCR[PcPrcbData+PbCurrentThread]
        mov     ecx, PCR[PcStackBase] ; (ecx) -> top of kernel stack

Kt0715:
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

Kt0716: mov     eax, PCR[PcPrcbData+PbCurrentThread]
        mov     ecx, PCR[PcStackBase] ; (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
;

        FXSAVE_ECX

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.
;

        mov     ebx, [ecx] + FxErrorOffset
        movzx   eax, word ptr [ecx] + FxControlWord
        movzx   edx, word ptr [ecx] + FxStatusWord
        mov     esi, [ecx] + FxDataOffset ; (esi) = operand addr

        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:
        sti                             ; stop the system
        stdCall _KeBugCheck, <TRAP_CAUSE_UNKNOWN>

if DBG
Kt07110:
        cmp     byte ptr [eax].ThNpxIrql, DISPATCH_LEVEL
        je      Kt0701
        stdCall _KeBugCheck, <ATTEMPTED_FPU_USE_FROM_DPC>
endif

_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

ifdef DEVKIT
;
; If the debugger can handle a double fault now, then we do so
;
        lea     edi, PCR[PcPrcbData+PbDebugDoubleFault]
        mov     eax, [edi]
        test    eax, eax
        je      Kt0810

;
; we're only going to take one double fault at a time
;
        xor     edx, edx
        mov     [edi], edx

;
; find the old TSS
;
        mov     dx, word ptr [_KiDoubleFaultTSS]
        lea     ecx, _KiGDT
        mov     edi, [edx+ecx+2]
        push    edi
        mov     dl, [edx+ecx+7]
        mov     [esp+3], dl

;
; ask the debugger whether we can handle things
;
        call    eax
        test    al, al
        je      Kt0810

;
; we're good to go
;
        iretd
        jmp     _KiTrap08   ; in case we dbl-fault again
endif ; DEVKIT

Kt0810:
;
; Clear the busy bit in the TSS selector
;
        mov     byte ptr [_KiGDT+KGDT_DF_TSS+5],089h

;
; Clear Nested Task bit in EFLAGS
;
        pushfd
        and     [esp], not 04000h
        popfd

;
; The original machine context is in original task's TSS
;
@@:     stdCall _KeBugCheckEx,<UNEXPECTED_KERNEL_MODE_TRAP,8,0,0,0>
        jmp     short @b        ; do not remove - for debugger

_KiTrap08       endp

        page ,132
        subttl "Coprocessor Segment Overrun"
;++
;
; Routine Description:
;
;    Handle Coprocessor Segment Overrun exception.
;
;    This exception only occurs on the 80286 (it's a trap 0d on the 80386),
;    so choke if we get here.
;
; Arguments:
;
;    At entry, the saved CS:EIP point to the aborted instruction.
;    No error code is provided with the error.
;
; Return value:
;
;    None
;
;--
        ASSUME  DS:NOTHING, SS:NOTHING, ES:NOTHING

align dword
        public  _KiTrap09
_KiTrap09       proc

        push    0                       ; push dummy error code
        ENTER_TRAP

        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

align dword
        public  _KiTrap0A
_KiTrap0A       proc

        ENTER_TRAP

        sti
        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
;
;--
        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
;
;--
        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:NOTHING, SS:NOTHING, ES:NOTHING

align dword
        public  _KiTrap0B
_KiTrap0B       proc
_KiTrap0C       label byte
_KiTrap0D       label byte

; Set up machine state frame for displaying

        ENTER_TRAP

        sti
        mov     ebx, [ebp]+TsEip        ; (ebx)->faulting instruction
        mov     esi, [ebp]+TsErrCode
        mov     eax, STATUS_ACCESS_VIOLATION
        jmp     CommonDispatchException2Args0d ; Won't return

_KiTrap0B       endp

        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
align dword
        public  _KiTrap0E
_KiTrap0E       proc

        ENTER_TRAP

        mov     edi,cr2
        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, 12
        mov     [esp+8], ebp            ; pass in the trap frame base
        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@12

        or      eax, eax                ; sucessful?
        jge     Kt0e10                  ; yes, go exit

;
; 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

        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

;
; 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:

ifdef DEVKIT
        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,<IRQL_NOT_LESS_OR_EQUAL,edi,eax,ecx,esi>

Kt0e12b:
        cmp     _KiBugCheckData, 0      ; If crashed, handle trap in
        jnz     Kt0e01                  ; normal manner


        mov     eax, 0ffh               ; OldIrql = -1
        jmp     short Kt0e12a

_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

align dword
        public  _KiTrap0F
_KiTrap0F       proc

        push    0                       ; push dummy error code
        ENTER_TRAP
        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

align dword
        public  _KiTrap10
_KiTrap10       proc

        push    0                       ; push dummy error code
        ENTER_TRAP

        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].ThStackBase
        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

align dword
        public  _KiTrap11
_KiTrap11       proc

        ENTER_TRAP
        sti

;
; 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

align dword
        public  _KiTrap13
_KiTrap13       proc

        push    0                       ; push dummy error code
        ENTER_TRAP

        mov     eax, PCR[PcPrcbData+PbNpxThread]  ; Correct context for fault?
        cmp     eax, PCR[PcPrcbData+PbCurrentThread]
        je      Kt13_10                 ; Yes - go try to dispatch it

;
;       Katmai New Instruction exceptions are precise and occur immediately.
;       if we are in the wrong NPX context, bugcheck the system.
;
        ; stop the system
        stdCall _KeBugCheckEx,<TRAP_CAUSE_UNKNOWN,13,eax,0,0>

Kt13_10:
        mov     ecx, PCR[PcStackBase] ; (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:
;
; We are about to dispatch a XMMI floating point exception to user mode.
;
; (ebp) - Trap frame
; (ecx) - CurrentThreads NPX save area (PCR[PcStackBase])
; (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,<TRAP_CAUSE_UNKNOWN,13,eax,0,1>

if DBG
Kt13_dbg1:    int 3
Kt13_dbg2:    int 3
Kt13_dbg3:    int 3
        sti
        jmp short $-2
endif

_KiTrap13       endp

;++
;
; VOID
; KiFlushNPXState (
;     VOID
;     )
;
; 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:
;
;    None.
;
; Return value:
;
;    None
;
;--
        ASSUME  DS:FLAT, SS:NOTHING, ES:NOTHING
align dword

cPublicProc _KiFlushNPXState    ,0
cPublicFpo 1, 1

        pushfd
        cli                             ; don't context switch

        mov     edx, PCR[PcPrcbData+PbCurrentThread]

        cmp     byte ptr [edx].ThNpxState, NPX_STATE_LOADED
        jne     short fnpx70

fnpx20:
    ;
    ; Current thread has NPX state in the coprocessor, flush it
    ;
        mov     eax, cr0
        test    eax, CR0_MP+CR0_TS+CR0_EM
        jz      short fnpx30
        and     eax, NOT (CR0_MP+CR0_TS+CR0_EM)
        mov     cr0, eax                ; allow frstor (& fnsave) to work
fnpx30:
        mov     ecx, PCR[PcStackBase]   ; (ecx) -> top of kernel stack
        FXSAVE_ECX

        mov     byte ptr [edx].ThNpxState, NPX_STATE_NOT_LOADED
        mov     PCR[PcPrcbData+PbNpxThread], 0      ; clear npx owner

        or      eax, NPX_STATE_NOT_LOADED           ; or in new threads cr0
        or      eax, [ecx].FpCr0NpxState            ; merge new thread setable state
        mov     cr0, eax

fnpx70:
        popfd                           ; enable interrupts
        stdRET    _KiFlushNPXState

stdENDP _KiFlushNPXState

;++
;
; VOID
; KiSetHardwareTrigger (
;     VOID
;     )
;
; Routine Description:
;
;   This function sets KiHardwareTrigger such that an analyzer can sniff
;   for this access.   It needs to occur with a lock cycle such that
;   the processor won't speculatively read this value.   Interlocked
;   functions can't be used as in a UP build they do not use a
;   lock prefix.
;
; Arguments:
;
;    None
;
; Return value:
;
;    None
;
;--
        ASSUME  DS:FLAT, SS:NOTHING, ES:NOTHING
cPublicProc _KiSetHardwareTrigger,0
   lock inc     ds:_KiHardwareTrigger   ; trip hardware analyzer
        stdRet  _KiSetHardwareTrigger
stdENDP _KiSetHardwareTrigger


        page ,132
        subttl "Processing System Fatal Exceptions"
;++
;
; Routine Description:
;
;    This routine processes the system fatal exceptions.
;    The machine state and trap type will be displayed and
;    System will be stopped.
;
; Arguments:
;
;    (eax) = Trap type
;    (ebp) -> machine state frame
;
; Return value:
;
;    system stopped.
;
;--
        assume  ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

align dword
        public  _KiSystemFatalException
_KiSystemFatalException proc
.FPO (0, 0, 0, 0, 0, FPO_TRAPFRAME)

        stdCall _KeBugCheckEx,<UNEXPECTED_KERNEL_MODE_TRAP, eax, 0, 0, 0>
        ret

_KiSystemFatalException endp

        page
        subttl  "Continue Execution System Service"
;++
;
; NTSTATUS
; NtContinue (
;    IN PCONTEXT ContextRecord,
;    IN BOOLEAN TestAlert
;    )
;
; Routine Description:
;
;    This routine is called as a system service to continue execution after
;    an exception has occurred. Its function is to transfer information from
;    the specified context record into the trap frame that was built when the
;    system service was executed, and then exit the system as if an exception
;    had occurred.
;
;   WARNING - Do not call this routine directly, always call it as
;             ZwContinue!!!  This is required because it needs the
;             trapframe built by KiSystemService.
;
; Arguments:
;
;    KTrapFrame (ebp+0: after setup) -> base of KTrapFrame
;
;    ContextRecord (ebp+8: after setup) = Supplies a pointer to a context rec.
;
;    TestAlert (esp+12: after setup) = Supplies a boolean value that specifies
;       whether alert should be tested for the previous processor mode.
;
; Return Value:
;
;    Normally there is no return from this routine. However, if the specified
;    context record is misaligned or is not accessible, then the appropriate
;    status code is returned.
;
;--

NcTrapFrame             equ     [ebp + 0]
NcContextRecord         equ     [ebp + 8]
NcTestAlert             equ     [ebp + 12]

align dword
cPublicProc _NtContinue     ,2

        push    ebp

;
; 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, <ecx, 0, eax>
        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
        stdCall _KeTestAlertThread, <0>  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

align dword
cPublicProc _ZwContinue     ,2

;
; Must go through an interrupt trap in order to build the trap frame that
; NtContinue requires.
;
        mov     eax, [esp+4]            ; (eax) -> ContextRecord
        mov     ecx, [esp+8]            ; (ecx) -> TestAlert
        int     28h
        stdRet  _ZwContinue

stdENDP _ZwContinue

        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

;
;   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,<eax, ecx, 0, ebx, edx>

        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

align dword
cPublicProc _ZwRaiseException     ,3

;
; Must go through an interrupt trap in order to build the trap frame that
; NtRaiseException requires.
;
        mov     eax, [esp+4]            ; (eax) -> ExceptionRecord
        mov     ecx, [esp+8]            ; (ecx) -> ContextRecord
        mov     edx, [esp+12]           ; (edx) -> FirstChance
        int     29h
        stdRet  _ZwRaiseException

stdENDP _ZwRaiseException

_TEXT   ends
        end
