windows-nt/Source/XPSP1/NT/base/mvdm/wow16/toolhelp/int2.asm
2020-09-26 16:20:57 +08:00

1089 lines
48 KiB
NASM

PAGE 60,150
;***************************************************************************
;* INT2.ASM
;*
;* Assembly code support routines used for the TOOLHELP.DLL interrupt
;* trapping API
;*
;***************************************************************************
INCLUDE TOOLPRIV.INC
INCLUDE WINDOWS.INC
include vint.inc
.286p
;** Symbols
I_EXCEPTION EQU 0
I_INTERRUPT EQU 1
MAX_INTERRUPT EQU 7
GIVE_WDEB386 EQU 8000h
BAD_STACK_FLAG EQU 8000h
MIN_STACK_ALLOWED EQU 128
;** Local types
INT_INFO STRUC
ii_wNumber DW ? ;INT nn
ii_wType DW ? ;I_EXCEPTION or I_INTERRUPT
ii_dwChain DD ?
ii_wHandler DW ? ;Note that this is CS relative
INT_INFO ENDS
;** Data
sBegin DATA
IntInfo LABEL BYTE
public IntInfo
UD_Info DW 6 ;Undefined opcode
DW I_EXCEPTION ;This should be DPMI-hooked
DD 0 ;Chain address (will be initialized)
DW OFFSET _TEXT:UD_Handler
Div0_Info DW 0 ;Divide by zero
DW I_EXCEPTION ;Hook with DPMI
DW OFFSET _TEXT:Div0_Handler
DW 0
DW OFFSET _TEXT:Div0_Handler
Int1_Info DW 1 ;Single step + debug register
DW I_INTERRUPT ;Hook with DOS
DD 0 ;Chain address
DW OFFSET _TEXT:Int1_Handler
Int3_Info DW 3 ;Software debug int
DW I_INTERRUPT ;Hook with DOS
DD 0 ;Chain address
DW OFFSET _TEXT:Int3_Handler
GP_Info DW 13 ;GP Fault
DW I_EXCEPTION ;This should be DPMI-hooked
;** This entry is a special case entry for the Win30 std mode
;* handler. This is a separate entry point into the
;** interrupt handler routine
DW OFFSET _TEXT:GP_StdModeHandler
DW 0
DW OFFSET _TEXT:GP_Handler
SF_Info DW 12 ;Stack fault
DW I_EXCEPTION ;This should be DPMI-hooked
;** This entry is a special case entry for the Win30 std mode
;* handler. This is a separate entry point into the
;** interrupt handler routine
DW OFFSET _TEXT:SF_StdModeHandler
DW 0
DW OFFSET _TEXT:SF_Handler
PF_Info DW 14 ;Page fault
DW I_EXCEPTION ;This should be DPMI-hooked
;** This entry is a special case entry for the Win30 std mode
;* handler. This is a separate entry point into the
;** interrupt handler routine
DW OFFSET _TEXT:PF_StdModeHandler
DW 0
DW OFFSET _TEXT:PF_Handler
CASRq_Info DW 256 ;CtlAltSysRq (fake interrupt)
DW I_INTERRUPT ;Hook with DOS
DD 0 ;Chain address
DW OFFSET _TEXT:CASRq_Handler
;** The following data is used to see if GDI wants the
;** Div0 we have trapped
lpGDIFlag DD 0
hGDI DW 0
szGDI DB 'GDI', 0
public lpGDIFlag, hGDI
;** Points to a KERNEL routine to see if it wants the
;** GP fault first
lpfnPV DD 0 ;Call to see if PV GP fault
public lpfnPV
;** Globals used for DPMI emulation
lpOldHandler DD 0 ;Previous DPMI exception handler
lpChainCSIP DD 0 ;Next exception handler on chain
wException DW 0
public lpOldHandler, lpChainCSIP
externW wCASRqFlag ;Set when an CASRq INT3 has been set
externD dwCASRqCSIP ;Holds the CS:IP of the CASRq INT3
sEnd
;** Imports
externNP TerminateApp
externNP HelperHandleToSel
externNP HelperVerifySeg
externFP AllocCStoDSAlias
externFP FreeSelector
externFP GetModuleHandle
externFP GetProcAddress
externFP GlobalEntryHandle
externFP _wsprintf
externFP OutputDebugString
externA __WinFlags
;** Functions
sBegin CODE
assumes CS,CODE
assumes DS,DATA
;** Interrupt trapping API
; InterruptInit
; Hooks all necessary interrupts and exceptions to allow an API
; for app-level interrupt hooks.
cProc InterruptInit, <NEAR,PUBLIC>, <si,di>
cBegin
;** Loop through all possible handlers
mov cx,MAX_INTERRUPT ;Number of ints to hook
lea si,IntInfo ;Get the address of the array
DII_HandlerLoop:
push cx ;Save loop counter
cmp [si].ii_wNumber,256 ;Fake exception?
jae DII_Continue ;Yes, don't hook anything!
cmp [si].ii_wType,I_EXCEPTION ;Exception?
jnz DII_Interrupt ;Nope, hook as interrupt
;** Do a special case for 3.0 Std Mode
test wTHFlags,TH_WIN30STDMODE ;Are we in Win30 std mode?
jz DII_NotStdMode ;No.
mov ax,WORD PTR [si].ii_dwChain ;Get the secondary handler
mov [si].ii_wHandler,ax ;Make sure we use it instead!
DII_NotStdMode:
;** Hook as an exception (DPMI)
mov ax,0202h ;Get exception handler - DPMI
mov bl,BYTE PTR [si].ii_wNumber ;Interrupt number
int 31h ;Call DPMI
mov WORD PTR [si].ii_dwChain,dx ;Save the old offset
mov WORD PTR [si].ii_dwChain + 2,cx ;Save the old selector
mov ax,0203h ;Set exception handler - DPMI
mov bl,BYTE PTR [si].ii_wNumber ;Interrupt number
mov dx,[si].ii_wHandler ;Address of exception handler
mov cx,cs ;Selector value of handler
int 31h ;Call DPMI
jmp SHORT DII_Continue
;** Hook as an interrupt (DOS)
DII_Interrupt:
mov ah,35h ;Get interrrupt handler - DOS
mov al,BYTE PTR [si].ii_wNumber ;Interrupt number
int 21h ;Call DOS
mov WORD PTR [si].ii_dwChain,bx ;Save the old offset
mov WORD PTR [si].ii_dwChain + 2,es ;Save the old selector
mov ah,25h ;Set interrupt handler - DOS
mov al,BYTE PTR [si].ii_wNumber ;Interrupt number
mov dx,[si].ii_wHandler ;Address of exception handler
push ds ;Save static DS for later
push cs ;DS = CS
pop ds
int 21h ;Call DOS
pop ds ;Get segment back
;** Prepare for next in table
DII_Continue:
add si,SIZE INT_INFO ;Bump to next entry
pop cx ;Get loop counter back
loop DII_HandlerLoop ;Loop back
;** Prepare the linked list
mov npIntHead,0 ;Put a NULL in the list head
;** Get information so we can check the GDI flag
lea ax,szGDI ;Get the string
cCall GetModuleHandle, <ds,ax> ;Get GDI's module handle
cCall HelperHandleToSel, <ax> ;Convert the owner to a selector
mov hGDI,ax ;Save it for later
cCall GetProcAddress, <ax,0,355> ;The flag is ordinal 355
mov WORD PTR lpGDIFlag[0],ax ;Save it for later
mov WORD PTR lpGDIFlag[2],dx
DII_End:
;** Return TRUE
mov ax,1
cEnd
; InterruptUnInit
; Unhooks all interrupts and exceptions hooked by DebugInterruptUnInit.
cProc InterruptUnInit, <NEAR,PUBLIC>, <si,di>
cBegin
;** Loop through all possible handlers
mov cx,MAX_INTERRUPT ;Number of ints to hook
lea si,IntInfo ;Get the address of the array
DIU_HandlerLoop:
push cx ;Save loop counter
cmp [si].ii_wNumber,256 ;Fake exception?
jae DIU_Continue ;Yes, don't unhook anything!
cmp [si].ii_wType,I_EXCEPTION ;Exception?
jnz DIU_Interrupt ;Nope, hook as interrupt
;** Unhook exception (DPMI)
mov ax,0203h ;Set exception handler - DPMI
mov bl,BYTE PTR [si].ii_wNumber ;Interrupt number
mov dx,WORD PTR [si].ii_dwChain ;Put back the old offset
mov cx,WORD PTR [si].ii_dwChain + 2 ;Put back the old selector
int 31h ;Call DPMI
jmp SHORT DIU_Continue
;** Unhook interrupt (DOS)
DIU_Interrupt:
mov ah,35h ;Get interrrupt handler - DOS
mov al,BYTE PTR [si].ii_wNumber ;Interrupt number
int 21h ;Call DOS
mov ah,25h ;Set interrupt handler - DOS
mov al,BYTE PTR [si].ii_wNumber ;Interrupt number
mov dx,WORD PTR [si].ii_dwChain ;Put back the old offset
push ds
mov ds,WORD PTR [si].ii_dwChain + 2 ;Put back the old selector
int 21h ;Call DOS
pop ds
;** Prepare for next in table
DIU_Continue:
add si,SIZE INT_INFO ;Bump to next entry
pop cx ;Get loop counter back
loop DIU_HandlerLoop ;Loop back
;** Prepare the linked list
mov npIntHead,0 ;Put a NULL in the list head
DIU_End:
cEnd
InterruptEntry MACRO Name, wBytes
labelFP Name ;;Start at this address
PUBLIC Name
sub sp,wBytes ;;Leave room on stack for return val
push bx ;;Save for the info pointer
ENDM
InterruptJump MACRO pInfo
mov bx,OFFSET pInfo ;;Point to interrupt info
jmp DIH_Main
ENDM
; InterruptHandler
; This routine is used to handle interrupts as they come in. This
; routine has multiple entry points; a seperate one for each int/
; exception trapped. Because interrupts and exceptions have
; different stack frames, they are handled by two different code
; sections.
cProc InterruptHandler, <FAR,PUBLIC>
cBegin NOGEN
;** All interrupt entry points here
InterruptEntry GP_Handler, 14 ;Normal GP fault
InterruptJump GP_Info
InterruptEntry GP_StdModeHandler, 12 ;3.0 Std mode GP fault
InterruptJump GP_Info
InterruptEntry SF_Handler, 14 ;Normal Stack Fault
InterruptJump SF_Info
InterruptEntry SF_StdModeHandler, 12 ;3.0 Std mode Stack Fault
InterruptJump SF_Info
InterruptEntry PF_Handler, 14 ;Page fault
InterruptJump PF_Info
InterruptEntry PF_StdModeHandler, 10 ;3.0 Std mode Page fault
InterruptJump PF_Info
InterruptEntry UD_Handler, 14 ;Undefined opcode
InterruptJump UD_Info
InterruptEntry Int1_Handler, 14 ;Int 1
InterruptJump Int1_Info
InterruptEntry Int3_Handler, 14 ;Int 3
InterruptJump Int3_Info
InterruptEntry CASRq_Handler, 14 ;Ctrl-Alt-SysRq (not really an int)
InterruptJump CASRq_Info
;** The divide by zero case has to include checking to make sure
;** that this isn't GDI's divide by zero.
InterruptEntry Div0_Handler, 14
;** Check to see if GDI wants this Div0
push ds ;Save some registers
push es
mov bx,_DATA ;Point to our data
mov ds,bx ; with DS
mov bx,WORD PTR lpGDIFlag[0];Get the low word
push bx
or bx,WORD PTR lpGDIFlag[2];Do we have a flag to look at?
pop bx
jz DIH_NoFlag ;No. Do this the hard way
;** Since we have a pointer to GDI's flag to look at, use it
mov es,WORD PTR lpGDIFlag[2];Get the seg value
cmp WORD PTR es:[bx],0 ;The flag is nonzero if GDI wants it
je DIH_NormalDiv0 ;GDI doesn't want it
;** GDI wants the Div0 so chain to it
DIH_ChainToGDI:
pop es ;Restore registers
pop ds ; (Doesn't trash flags)
push bp ;Make the same stack frame for compat
mov bp,sp
pusha ;Save all registers
push ds
push es
mov ax,_DATA ;Get the data segment
mov ds,ax ;Point with DS
mov bx,OFFSET Div0_Info ;This fault's info
jmp DIH_DPMIChainOn ;Chain on (ignore the int)
DIH_NormalDiv0:
pop es ;Restore registers
pop ds
InterruptJump Div0_Info
;** We didn't get a GDI flag (only present in 3.1) so instead, we
;* check the owner of the CS where the fault occurred. If
;** the owner is GDI, we ignore the Div0.
DIH_NoFlag:
push bp ;Make a stack frame
mov bp,sp
sub sp,SIZE GLOBALENTRY ;Make room for a structure
Global EQU [bp - SIZE GLOBALENTRY] ;Point to our structure
pusha ;Save all registers
mov WORD PTR Global.ge_dwSize[0],SIZE GLOBALENTRY ;Size of struct
mov WORD PTR Global.ge_dwSize[2],0
lea bx,Global ;Point to the structure
test wTHFlags,TH_WIN30STDMODE ;3.0 std mode?
jnz DIH_Div0_StdMode ;Yes
mov ax,[bp + 1ah + 4] ;Get the CS value (4 is extra BP,BX
jmp SHORT @F ; pushed by InterruptEntry macro)
DIH_Div0_StdMode:
mov ax,[bp + 14h + 4] ;Get the CS value
@@: cCall GlobalEntryHandle, <ss,bx,ax> ;Get info about the CS
or ax,ax ;Did the call succeed?
jne @F ;Yes, go on
popa ;No, clear stack frame and do normal
mov sp,bp
pop bp
jmp DIH_NormalDiv0 ;Jump to normal processing
@@: mov ax,Global.ge_hOwner ;Get the owner
cCall HelperHandleToSel, <ax> ;Make it a selector
cmp hGDI,ax ;Is this owned by GDI?
popa ;Restore the registers
mov sp,bp ;Clear stack frame
pop bp
je DIH_ChainToGDI ;Yes, so give the interrupt to it
jmp DIH_NormalDiv0 ;No, do normal stuff
;** We now have to first find the item on the block to see if we
;** want to handle the interrupt.
PubLabel CommonInterruptEntry
DIH_Main:
push bp ;Make a stack frame
mov bp,sp
pusha ;Save all registers
push ds
push es
;** We check first to see if this was a GP fault received from the
;* parameter validation code. If it was, we just chain on
;* just as if we don't find any handlers.
;**
mov ax,_DATA ;Get our data segment
mov ds,ax
FSTI ;Must have interrupts on
cmp bx,OFFSET GP_Info ;GP Fault?
jnz DIH_NotPVGPFault ;No.
mov cx,WORD PTR lpfnPV[0] ;Get the low word
or cx,WORD PTR lpfnPV[2] ;Param Validation stuff present?
jz DIH_NotPVGPFault ;No, skip this
;** Check to see if the parameter validation code wants the fault
push ds
push bx
push [bp + 1Ah] ;Push faulting CS:IP
push [bp + 18h]
call [lpfnPV] ;Call it
pop bx
pop ds
or ax,ax ;Non-zero means this was PV fault
je DIH_NotPVGPFault ;Not a PV GP fault
;** It is a parameter validation fault so ignore it
jmp DIH_DPMIChainOn ;Chain the fault on--we don't want it
;** We check here to see if the INT3 we received is from the CASRq
;* handler. If it was, we have to replace the INT3 with the
;* previous byte and tell the user this was actually a CASRq
;** event (not an INT3).
PubLabel DIH_NotPVGPFault
cmp bx,OFFSET Int3_Info ;INT3?
jnz DIH_NotCASRq ;Nope, ignore all this
cmp wCASRqFlag,0 ;Was this because of CASRq?
je DIH_NotCASRq ;No.
mov ax,[bp + 12h] ;INT3 is an IRET frame. Get bkpt IP
dec ax ;Breaks AFTER instruction
cmp WORD PTR dwCASRqCSIP[0],ax ;Is this the right CASRq address?
jne DIH_NotCASRq ;Nope
mov dx,[bp + 14h] ;Get the breakpoint CS
cmp WORD PTR dwCASRqCSIP[2],dx ;Is this correct?
jne DIH_NotCASRq ;Nope
push ax ;Save the IP value
cCall AllocCStoDSAlias, <dx> ;Get a data alias to the CS
mov es,ax ;Point with ES
pop si ;Restore the IP value
mov [bp + 12h],si ;Back to instr where INT3 was
mov al,BYTE PTR wCASRqFlag ;Get the saved byte
mov es:[si],al ;Put it back in the code
mov wCASRqFlag,0 ;Clear the flag
cCall FreeSelector, <es> ;Get rid of the alias
mov bx,OFFSET CASRq_Info ;Point to the CASRq information
;** See if we have at least one handler. We should always have one.
PubLabel DIH_NotCASRq
mov si,npIntHead ;Get the list start
or si,si ;Are there any routines hooked?
jnz DIH_Found ;There should ALWAYS be at least one
; routine hooked (otherwise, the
; interrupt hooks should have
; already been removed)
;** Return the stack to its prior state and chain on.
;* We only get here in an erroneous state. We keep the code in
;* to avoid GP faulting if things get wierd.
;* The stack looks like this:
;* ------------
;* | ES |
;* | DS |
;* | PUSHA |
;* BP-->| Old BP | [BP + 00h]
;* | BX | [BP + 02h]
;* | Empty | [BP + 04h]
;* | Empty | [BP + 06h]
;* | Empty | [BP + 08h]
;* | Empty | [BP + 0Ah]
;* | Empty | [BP + 0Ch]
;* |Our Ret IP| [BP + 0Eh]
;* |Our Ret CS| [BP + 10h]
;* |Original |
;* | Frame |
;* | .... |
;* ------------
;**
PubLabel DIH_DPMIChainOn
mov ax,WORD PTR [bx].ii_dwChain ;Get the LOWORD
mov [bp + 0eh],ax ;Put into the frame we created
mov ax,WORD PTR [bx].ii_dwChain + 2 ;Get the HIWORD
mov [bp + 10h],ax ;Put into the frame
pop es ;Clear the stack
pop ds
popa
pop bp
pop bx
add sp,10 ;Clear extra space
retf ;This uses our own "return" frame
; to chain on
;** Since we found the entry, we have to call the user callback.
;* Because we must be reentrant at this state, we have to make
;* sure that we're safe. To do so, we must do different
;** actions for DPMI and for DOS frames.
PubLabel DIH_Found
cmp [bx].ii_wType,I_EXCEPTION ;DPMI Exception frame?
je @F
jmp DIH_SkipDPMI ;No. Skip DPMI processing
@@:
;** If we are in Win3.0 Std Mode, the DPMI frame was broken. It
;* simply left the normal IRET frame on the stack *AND* the
;** error code.
test wTHFlags,TH_Win30StdMode ;3.0 Std mode?
jz @F
jmp DIH_SkipDPMI ;Yes
@@:
;** Tell DPMI that the exception is over. Before we do this,
;* however, save information we'll need later on the user stack.
;* The stack currently looks like this:
;* ------------
;* | ES |
;* | DS |
;* | PUSHA |
;* BP-->| Old BP | [BP + 00h]
;* | BX | [BP + 02h]
;* | Empty | [BP + 04h]
;* | Empty | [BP + 06h]
;* | Empty | [BP + 08h]
;* | Empty | [BP + 0Ah]
;* | Empty | [BP + 0Ch]
;* | Empty | [BP + 0Eh]
;* | Empty | [BP + 10h]
;* | Ret IP | [BP + 12h] <-
;* | Ret CS | [BP + 14h] |
;* |Error Code| [BP + 16h] | Pushed by DPMI
;* | IP | [BP + 18h] |
;* | CS | [BP + 1Ah] | (Locked stack)
;* | Flags | [BP + 1Ch] |
;* | SP | [BP + 1Eh] |
;* | SS | [BP + 20h] <-
;* ------------
;*
;* Before returning to DPMI, however, we want to create a
;* stack frame on the user's stack that we will be returning
;* to so we can preserve information in a reentrant fashion.
;* The user's stack will appear like this:
;* ------------
;* BP---->| Old BP | [BP + 00h]
;* | BX | [BP + 02h]
;* |Our Ret IP| [BP + 04h]
;* |Our Ret CS| [BP + 06h]
;* | Ret IP | [BP + 08h]
;* | Ret CS | [BP + 0Ah]
;* | AX | [BP + 0Ch]
;* |Exception#| [BP + 0Eh]
;* | Handle | [BP + 10h]
;* | IP | [BP + 12h]
;* | CS | [BP + 14h]
;* | Flags | [BP + 16h]
;* ------------
;**
PubLabel DIH_Exception
;** Check to see if we're already on the faulting stack. If we are,
;** we want to shift everything up on this stack so that we
;** have room for the TOOLHELP user frame
mov ax,ss ;Get the current SS
cmp ax,WORD PTR ss:[bp + 20h] ;Is it the same as the user frame?
jne DIH_EnoughRoomOnStack ;No, ignore all of this
;** Move everything up by copy everything that's on stack now to
;** above where SP starts at. This actually uses too much
;** stack, but it's safe and easy.
push bp ;We use BP to do copy
lea bp,[bp + 20h] ;Point to lowest WORD to copy
mov ax,sp ;Point to position to copy to
dec ax
dec ax
DIH_CopyLoop:
push WORD PTR [bp] ;Copy a WORD
dec bp ;Point to next WORD to copy
dec bp
cmp bp,ax ;Done yet?
jne DIH_CopyLoop ;No
pop bp ;Yes, compute new BP value
sub bp,56 ;Point BP to right place
;** Put stuff on DPMI's stack
PubLabel DIH_EnoughRoomOnStack
mov di,[bp + 1Eh] ;Get the old SP value
mov cx,[bp + 20h] ; and the SS value
cmp di,MIN_STACK_ALLOWED ;Are we going to stack fault?
jb DIH_BadStack ;Yes, so swich
mov ax,__WinFlags ;Make sure we have a 386 or higher
test ax,WF_CPU286
jnz DIH_SkipBigCheck ;No need to check big bit
.386p
push eax ;Make sure we don't trash EAX
lar eax,ecx ;Get the access rights DWORD
test eax,00400000h ;Check the BIG bit
pop eax
jnz DIH_BadStack ;Don't use this stack if BIG
.286p
DIH_SkipBigCheck:
mov ax,di ;Get the stack pointer
add ax,2 ;Point just beyond
cCall HelperVerifySeg, <cx,ax> ;Is this seg OK?
or ax,ax ;Check for success
jz DIH_BadStack ;Stack is bogus, don't change to it
PubLabel DIH_StackOK
sub di,20 ;Reserve space for the user frame
mov ds,cx ;Get stack value in DS
mov dx,[bp + 1Ah] ;Get the old CS value
mov cx,[bp + 18h] ;Get the old IP value
mov ax,[bp + 1Ch] ;Get the old flags
mov [bp + 1Eh],di ;Save as new SP value
sub di,4 ;Make DI equal to what BP will be
mov [bp + 1Ah],cs ;Prepare to IRET to ourself
mov [bp + 18h],OFFSET _TEXT:DIH_DPMIRestart
;** Save some things on the user's stack before returning
mov [di + 16h],ax ;Save the flags
mov [di + 14h],dx ;Save the old CS value
mov [di + 12h],cx ;Save the old IP value
mov [di + 0Eh],bx ;INT_INFO pointer to new stack
mov [di + 10h],si ;Handle to new stack
;** Clear the Trace and Ints Enabled flags
and [bp + 1Ch],NOT 0100h ;Clear TF. We don't want to trace here
pop es ;Clear the DPMI stack
pop ds
popa
pop bp
pop bx
add sp,14 ;Clear extra allocated space
retf
;** The user stack is bad, so we want to stay on the fault handler
;** stack. In order to do this, we have to build a frame for
;** the callback directly on the fault handler stack.
;** We build the frame here and jump to it.
;** ------------
;** | ES |
;** | DS |
;** | PUSHA |
;** BP-->| Old BP | [BP + 00h]
;** | BX | [BP + 02h]
;** | Empty | [BP + 04h]
;** |Our Ret IP| [BP + 06h] ; Client callback addr
;** |Our Ret CS| [BP + 08h]
;** | Ret IP | [BP + 0Ah] ; TOOLHELP restart addr
;** | Ret CS | [BP + 0Ch]
;** | AX | [BP + 0Eh] ; Saved AX for MPI
;** |Exception#| [BP + 10h] ; Exception number
;** | Handle | [BP + 12h] ; TOOLHELP handle
;** | IP | [BP + 14h] ; IRET frame of fault
;** | CS | [BP + 16h]
;** | Flags | [BP + 18h]
;** | SP | [BP + 1Ah] ; Faulting SS:SP
;** | SS | [BP + 1Ch]
;** | Ret IP | [BP + 1Eh] ; DPMI return address
;** | Ret CS | [BP + 20h]
;** ------------
PubLabel DIH_BadStack
mov dx,[bp + 12h] ;DPMI return CS:IP
mov cx,[bp + 14h] ; stored in CX:DX
mov ax,[bp + 18h] ;Faulting IP
mov [bp + 14h],ax
mov ax,[bp + 1Ah] ;Faulting CS
mov [bp + 16h],ax
mov ax,[bp + 1Ch] ;Flags
mov [bp + 18h],ax
mov ax,[bp + 1Eh] ;Faulting SP
mov [bp + 1Ah],ax
mov ax,[bp + 20h] ;Faulting SS
mov [bp + 1Ch],ax
mov [bp + 1Eh],dx ;DPMI ret IP
mov [bp + 20h],cx ;DPMI ret CS
mov [bp + 12h],si ;Point to INTERRUPT struct
mov ax,[bx].ii_wNumber ;Get the interrupt number
or ax,BAD_STACK_FLAG ;Flag the client that stack is bad
mov [bp + 10h],ax
mov ax,[bp - 02h] ;Get the AX value from the PUSHA frame
mov [bp + 0Eh],ax
mov [bp + 0Ch],cs ;Point to callback return address
mov [bp + 0Ah],OFFSET _TEXT:DIH_CallbackRestart
mov ax,WORD PTR [si].i_lpfn ;Point to the user callback OFFSET
mov [bp + 06h],ax
mov ax,WORD PTR [si].i_lpfn + 2 ;Point to callback segment
mov [bp + 08h],ax
pop es ;Clear the stack
pop ds
popa
pop bp
pop bx
add sp,2
retf ;Jump to the user callback
;** At this point, DPMI IRETs back to us instead of to the faulting
;** app. We have to now create a stack frame identical to the
;** frame used by interrupt-style hooks. Note that we have
;** already allocated the frame space (but not initialized it)
;** before returning to DPMI.
;** It will look like this:
;** ------------
;** | ES | [BP - 14h]
;** | DS | [BP - 12h]
;** | DI | [BP - 10h]
;** | SI | [BP - 0Eh]
;** | BP | [BP - 0Ch]
;** | SP | [BP - 0Ah]
;** | BX | [BP - 08h]
;** | DX | [BP - 06h]
;** | CX | [BP - 04h]
;** | AX | [BP - 02h]
;** BP---->| Old BP | [BP + 00h]
;** | BX | [BP + 02h]
;** |Our Ret IP| [BP + 04h]
;** |Our Ret CS| [BP + 06h]
;** | Ret IP | [BP + 08h]
;** | Ret CS | [BP + 0Ah]
;** | AX | [BP + 0Ch]
;** |Exception#| [BP + 0Eh]
;** | Handle | [BP + 10h]
;** | IP | [BP + 12h]
;** | CS | [BP + 14h]
;** | Flags | [BP + 16h]
;** ------------
;**
PubLabel DIH_DPMIRestart
push bx ;Save this register we're using
push bp ;Make a stack frame
mov bp,sp
pusha ;Save all the registers
push ds
push es
mov bx,[bp + 0Eh] ;Get the INT_INFO pointer back
mov si,[bp + 10h] ;Get the INTERRUPT structure back
mov ax,_DATA ;Get our data segment
mov ds,ax
;** We can now proceed with joint processing as we've matched the
;** DOS interrupt frame
PubLabel DIH_SkipDPMI
;** Build our return frame and jump to the user callback
mov [bp + 10h],si ;Point to INTERRUPT struct
mov ax,[bx].ii_wNumber ;Get the interrupt number
mov [bp + 0Eh],ax ;Put on frame
mov ax,[bp - 02h] ;Get the AX value from the PUSHA frame
mov [bp + 0Ch],ax ;Put on frame
mov [bp + 0Ah],cs ;Point to callback return address
mov [bp + 08h],OFFSET _TEXT:DIH_CallbackRestart
mov ax,WORD PTR [si].i_lpfn ;Point to the user callback OFFSET
mov [bp + 04h],ax
mov ax,WORD PTR [si].i_lpfn + 2 ;Point to callback segment
mov [bp + 06h],ax
pop es ;Clear the stack
pop ds
popa
pop bp
pop bx
retf ;Jump to the user callback
;** When the callback returns, we have to know how to call the
;* next matching callback or to chain on the interrupt list.
;* We have to do a raft of special stuff if this was an
;* exception so that the chained on handlers think it was
;** DPMI that called them.
PubLabel DIH_CallbackRestart
sub sp,8 ;Leave room for ret addresses
push bx ;For compat. with the above code
push bp ;Make the same stack frame
mov bp,sp
pusha
push ds
push es
;** Get the next matching item on the list
mov ax,_DATA ;Get our data segment
mov ds,ax
mov ax,[bp + 0Ch] ;Get the saved AX value
mov [bp - 02h],ax ;Put in PUSHA frame
mov si,[bp + 10h] ;Get the last handle used
or si,si ;If NULL, app has messed with it
jz DIH_NukeIt ;Nuke the app--it did a no-no
mov si,[si].i_pNext ;Get the next item in the list
or si,si ;End of the line?
jz DIH_NextNotFound ;Yes, chain on
mov ax,[bp + 0Eh] ;Get the exception number
cCall InterruptInfo ;Get the INT_INFO structure
or ax,ax ;If NULL return, user messed with #
jz DIH_NukeIt ; so nuke it
mov bx,ax ;Point with BX
jmp DIH_SkipDPMI ;Process this one
;** If we don't find a match, we pass on to previous handler
PubLabel DIH_NextNotFound
mov ax,[bp + 0Eh] ;Get the exception number
and ax,7fffh ;Clear the new stack bit
cCall InterruptInfo ;Find the INT_INFO structure
or ax,ax ;If the user messed with it,
jz DIH_NukeIt ; nuke the app.
mov si,ax ;Get the pointer in AX
test wTHFlags,TH_Win30StdMode ;3.0 Std mode?
jnz DIH_30StdChainOn ;Always do normal chain on in 3.0sm
cmp [si].ii_wType,I_INTERRUPT ;Was this an interrupt?
je DIH_ChainOn ;Yes, do normal chain on
jmp DIH_EmulateDPMI ;No, do the DPMI chain on
PubLabel DIH_NukeIt
push [bp + 16h] ;Copy the IRET frame for KERNEL
push [bp + 14h]
push [bp + 12h]
push 0 ;Nuke current task
push UAE_BOX OR GIVE_WDEB386 ;UAE box + give to wdeb
push cs ;Simulate a far jump
call NEAR PTR TerminateApp ;Nuke the app
;** We only get here if WDEB386 is installed. We tell it to set
;* a breakpoint, then restart the app, in effect giving
;* control to WDEB386. Unfortunately, at this point, all
;** fault handlers for this task have been removed
add sp,6 ;Clear fake IRET frame
mov cx,[bp + 14h] ;Faulting CS
mov bx,[bp + 12h] ;Faulting IP
mov ax, 40h ;16 bit forced go command
int 41h ;Call debugger
pop es ;Restore registers and clear stack
pop ds
popa
pop bp
pop bx
add sp,14 ;Clear extra words
; all that remains is IRET frame
iret ;WDEB386 will get control
PubLabel DIH_NukeApp
push 0 ;Nuke current task
push UAE_BOX ;Draw the UAE box
push cs ;Simulate a far jump
call NEAR PTR TerminateApp ;Nuke the app
int 1 ;Should never return
jmp SHORT DIH_ChainOn
;** In 3.0 standard mode we have to put an error code on the stack
;** if it's a GP fault or. If not, we just chain on normally
PubLabel DIH_30StdChainOn
cmp si,OFFSET GP_Info ;Is this a GP fault?
jne DIH_ChainOn ;No, handle normally
mov ax,WORD PTR [si].ii_dwChain ;Get the LOWORD
mov dx,WORD PTR [si].ii_dwChain + 2 ;Get HIWORD
mov bx,ax ;Save the LOWORD
or ax,dx ;Is there a chain on address?
jz DIH_NoChainAddr ;No, just restart the instruction
mov [bp + 0Ch],bx ;Put on stack so we can retf to it
mov [bp + 0Eh],dx
mov WORD PTR [bp + 10h],0 ;Zero the error code
pop es ;Restore registers and clear stack
pop ds
popa
pop bp
pop bx
add sp,8 ;Clear extra words
retf
PubLabel DIH_ChainOn
mov ax,WORD PTR [si].ii_dwChain ;Get the LOWORD
mov dx,WORD PTR [si].ii_dwChain + 2 ;Get HIWORD
mov bx,ax ;Save the LOWORD
or ax,dx ;Is there a chain on address?
jz DIH_NoChainAddr ;No, just restart the instruction
mov [bp + 0Eh],bx ;Put on stack so we can retf to it
mov [bp + 10h],dx
pop es ;Restore registers and clear stack
pop ds
popa
pop bp
pop bx
add sp,10 ;Clear extra words
retf
;** No chain on address was recorded so just restart the instruction
PubLabel DIH_NoChainAddr
pop es
pop ds
popa
pop bp
pop bx
add sp,14 ;Clear all extra words
iret ; and restart instruction
;** Chain on a DPMI-style exception:
;**
;** The goal here is to make a fault frame that appears that DPMI
;** has passed the next exception handler the interrupt. We
;** have only two important cases here:
;** 1) We have already told DPMI the int was finished.
;** 2) We have not told DPMI the int was finished and
;** have not switched off the fault handler stack
;** We handle the cases differently:
;** -If we have already told DPMI that the fault was handled,
;** we have to make a new fault so that the next handler can see
;** the frame. This can be best accomplished by restarting the
;** faulting instruction. This will cause the same fault to
;** happen and will make the same frame.
;** -In the case of us still being on the fh stack, we have to
;** rebuild the frame and chain on.
;** The stack we're given looks like this:
;** ------------
;** | ES | [BP - 14h]
;** | DS | [BP - 12h]
;** | DI | [BP - 10h]
;** | SI | [BP - 0Eh]
;** | BP | [BP - 0Ch]
;** | SP | [BP - 0Ah]
;** | BX | [BP - 08h]
;** | DX | [BP - 06h]
;** | CX | [BP - 04h]
;** | AX | [BP - 02h]
;** BP---->| Old BP | [BP + 00h]
;** | BX | [BP + 02h]
;** |Our Ret IP| [BP + 04h]
;** |Our Ret CS| [BP + 06h]
;** | Ret IP | [BP + 08h]
;** | Ret CS | [BP + 0Ah]
;** | AX | [BP + 0Ch]
;** |Exception#| [BP + 0Eh]
;** | Handle | [BP + 10h]
;** | IP | [BP + 12h]
;** | CS | [BP + 14h]
;** | Flags | [BP + 16h]
;** | SP | [BP + 18h] ;Only here if on fh stack
;** | SS | [BP + 1Ah]
;** | Ret IP | [BP + 1Ch] ;DPMI return address
;** | Ret CS | [BP + 1Eh]
;** ------------
PubLabel DIH_EmulateDPMI
mov ax,[bp + 0Eh] ;Get the exception number
test ax,BAD_STACK_FLAG ;Still on fh stack?
jnz DIH_RebuildDPMIFrame ;Yes, rebuild the frame
;** Rehook the exception so we're sure to get it first
push si ;Preserve handle
mov bx,ax ;Fault number in bx
mov wException,bx ;Save as a static also
mov ax,0202h ;Get exception handler - DPMI
int 31h ;Call DPMI
mov WORD PTR lpOldHandler[0],dx ;Save the old exception handler
mov WORD PTR lpOldHandler[2],cx
mov ax,0203h ;Set exception handler - DPMI
mov dx,OFFSET DIH_EmDPMIRestart
mov cx,cs ;Selector value of handler
int 31h ;Call DPMI
pop si
;** Save the address of the next exception handler
mov ax,WORD PTR [si].ii_dwChain[0] ;Address to chain fault to
mov WORD PTR lpChainCSIP[0],ax
mov ax,WORD PTR [si].ii_dwChain[2]
mov WORD PTR lpChainCSIP[2],ax
;** Restart the instruction. This will fault and jump to our
;** newly-established handler at DIH_EmDPMIRestart
pop es
pop ds
popa
pop bp
pop bx
add sp,14 ;Clear all extra words
iret ; and restart instruction
;** Now we're on the fault handler stack with a DPMI frame. Throw
;** it to the next handler on the chain
PubLabel DIH_EmDPMIRestart
sub sp,4 ;Enough room for a RETF frame
push bp
mov bp,sp
pusha
push ds
push es
mov ax,_DATA ;Point to TOOLHELP's DS
mov ds,ax
;** Restore the exception handler
mov ax,0203h ;Set exception handler - DPMI
mov bx,wException ;Get exception number
mov dx,WORD PTR lpOldHandler[0] ;Get the exception handler address
mov cx,WORD PTR lpOldHandler[2]
int 31h ;Call DPMI
;** Put the chain address on the stack so we can return to it
mov ax,WORD PTR lpChainCSIP[0] ;Get chain address
mov [bp + 02h],ax
mov ax,WORD PTR lpChainCSIP[2]
mov [bp + 04h],ax
;** Restore registers and jump to the handler
pop es
pop ds
popa
pop bp
retf
;** Since we are already on the fault handler stack, there is no
;** need to fault again. All we have to do here is recreate the
;** DPMI fault stack as if the fault had just occurred. It would
;** be nice to clear the exception and then make it fault again,
;** but since we only get here in potentially stack-faulting
;** conditions, we cannot do this. We just build a reasonable
;** facsimile of the frame and chain on. This frame should
;** look as follows when we're done:
;** ------------
;** | ES | [BP - 14h]
;** | DS | [BP - 12h]
;** | DI | [BP - 10h]
;** | SI | [BP - 0Eh]
;** | BP | [BP - 0Ch]
;** | SP | [BP - 0Ah]
;** | BX | [BP - 08h]
;** | DX | [BP - 06h]
;** | CX | [BP - 04h]
;** | AX | [BP - 02h]
;** BP---->| Old BP | [BP + 00h]
;** | BX | [BP + 02h]
;** | Empty | [BP + 04h]
;** | Empty | [BP + 06h]
;** | Empty | [BP + 08h]
;** | Empty | [BP + 0Ah]
;** | Chain IP | [BP + 0Ch]
;** | Chain CS | [BP + 0Eh]
;** | Ret IP | [BP + 10h]
;** | Ret CS | [BP + 12h]
;** |Error Code| [BP + 14h] ;Always return zero
;** | IP | [BP + 16h]
;** | CS | [BP + 18h] ;Only here if on fh stack
;** | Flags | [BP + 1Ah]
;** | SP | [BP + 1Ch] ;DPMI return address
;** | SS | [BP + 1Eh]
;** ------------
PubLabel DIH_RebuildDPMIFrame
mov dx,[bp + 1Ch] ;DPMI return CS:IP
mov cx,[bp + 1Eh] ; stored in CX:DX
mov ax,[bp + 1Ah] ;Faulting SS
mov [bp + 1Eh],ax
mov ax,[bp + 18h] ;Faulting SP
mov [bp + 1Ch],ax
mov ax,[bp + 16h] ;Flags
mov [bp + 1Ah],ax
mov ax,[bp + 14h] ;Faulting CS
mov [bp + 18h],ax
mov ax,[bp + 12h] ;Faulting IP
mov [bp + 16h],ax
xor ax,ax ;Error code
mov [bp + 14h],ax
mov [bp + 12h],cx ;DPMI ret CS
mov [bp + 10h],dx ;DPMI ret IP
mov ax,WORD PTR [si].ii_dwChain[2] ;Address to chain fault to
mov [bp + 0Eh],ax
mov ax,WORD PTR [si].ii_dwChain[0]
mov [bp + 0Ch],ax
pop es
pop ds
popa
pop bp
pop bx
add sp,8 ;Clear all extra words
retf
cEnd NOGEN
;** Helper functions
; InterruptInfo
; Gets a pointer to the INT_INFO structure given the interrupt
; number. Accepts the int number in AX and returns the pointer in AX.
; Preserves all other registers
cProc InterruptInfo, <NEAR,PUBLIC>, <si,cx>
cBegin
;** Loop through all possible handlers
mov cx,MAX_INTERRUPT + 1 ;Number of ints to hook
lea si,IntInfo ;Get the address of the array
;** Is this a match?
II_HandlerLoop:
cmp [si].ii_wNumber,ax ;Match?
jz II_End ;Yes, return the pointer
;** Prepare for next in table
II_Continue:
add si,SIZE INT_INFO ;Bump to next entry
loop II_HandlerLoop ;Loop back
xor si,si ;Return NULL for not found
II_End:
mov ax,si ;Get return value
cEnd
sEnd
END