include intmac.inc public rgw032Stack dw 64 dup (?) ; DOSX Ring -> Ring 0 transition stack ; ; Interrupts in the range 0-1fh cause a ring transition and leave ; an outer ring IRET frame right here. ; Ring0_EH_DS dw ? ; place to put user DS Ring0_EH_AX dw ? ; place to put user AX Ring0_EH_BX dw ? ; place to put user BX Ring0_EH_CX dw ? ; place to put user CX Ring0_EH_BP dw ? ; place to put user BP Ring0_EH_PEC dw ? ; lsw of error code for 386 page fault ; also near return to PMFaultEntryVector Ring0_EH_EC dw ? ; error code passed to EH Ring0_EH_IP dw ? ; interrupted code IP dw ? Ring0_EH_CS dw ? ; interrupted code CS dw ? Ring0_EH_Flags dw ? ; interrupted code flags dw ? Ring0_EH_SP dw ? ; interrupted code SP dw ? Ring0_EH_SS dw ? ; interrupted code SS dw ? rgw032Stack label word ; ------------------------------------------------------------------ ; PMFaultAnalyzer -- This routine is the entry point for ; the protected mode fault/trap/exception handler. It tries ; to distinguish between bona fide processor faults and ; hardware/software interrupts which use the range of ; interrupts that is reserved by Intel. If a fault is ; detected, then format the stack for a DPMI fault handler, ; then vector to the handler whose address is stored in ; PMFaultVector. If it looks more like an interrupt, then ; set up the stack for an interrupt handler, jump to the ; handler whose address is stored in PMIntelVector. ; ; Input: none ; Output: none assume ds:NOTHING,es:NOTHING,ss:DGROUP public PMFaultAnalyzer PMFaultAnalyzer proc near ; ; Make sure we are on the right stack. Else, something fishy is going on. ; Note that stack faults won't do too well here. ; push ax mov ax,ss cmp ax,SEL_DXDATA or STD_RING pop ax je pmfa_stack_passed jmp pmfa_bad_stack pmfa_stack_passed: ; ; Is the stack pointer pointing at a word error code (minus 2)? ; cmp sp,offset DGROUP:Ring0_EH_PEC je pmfa_fault ; Yes, processor fault. ; ; Is it pointing to where it is supposed to be for a hardware or ; software interrupt? ; cmp sp,offset DGROUP:Ring0_EH_EC je pmfa_20 jmp pmfa_bad_stack pmfa_20:jmp pmfa_inspect pmfa_fault: ; ; Getting here, we have a known exception with a word error code of some ; sort on the stack. Perform an outward ring transition, switch to the ; client stack, then vector through the exception handler vector to the ; appropriate handler. ; push bp push cx push bx push ax push ds lea bp,Ring0_EH_SS mov ax,word ptr [bp] mov cx,selEHStack cmp ax,cx jne pmfa_stack_OK mov bx,[bp-2] jmp pmfa_copy_stack pmfa_stack_OK: mov bx,npEHStackLimit pmfa_copy_stack: mov ds,cx ; DS:BX = user SS:SP mov cx,13 add bp,2 ; put both halves of ss pmfa_copy_stack_loop: dec bx dec bx mov ax,word ptr [bp] mov word ptr [bx],ax dec bp dec bp loop pmfa_copy_stack_loop ; DS:BX points to stack on entry to PMFaultReflector ; ; Build a far return frame on user stack, switch to user stack, and return ; lea bp,Ring0_EH_PEC mov word ptr [bp+6],ds mov word ptr [bp+4],bx sub bx,2 mov word ptr ds:[bx],SEL_DXPMCODE OR STD_RING; push cs sub bx,2 mov ds:[bx],offset DXPMCODE:PMFaultReflector; push ip lea bp,Ring0_EH_PEC mov ax,Ring0_EH_CX ; get BP value sub bx,2 mov ds:[bx],ax ; push bp mov Ring0_EH_PEC,bx ; sp for lss mov bx,ds mov Ring0_EH_EC,bx ; ss for lss pop ds pop ax pop bx pop cx .386p lss sp,[bp] ; switch stack .286p pop bp retf pmfa_inspect: ; ; Stack is set up as for an interrupt or exception without error code. ; Adjust the stack pointer and put an error code of zero. Then try to ; determine whether we have an exception or an interrupt. First test ; is the interrupt number. ; push Ring0_EH_EC mov Ring0_EH_EC,0 cmp Ring0_EH_PEC,offset PMFaultEntryVector + ((7 + 1) * 3) ja pfma_50 push Ring0_EH_PEC mov Ring0_EH_PEC,0 jmp pmfa_fault ; Yes, definitely a fault. pfma_50: ; ; At this point, all valid exceptions have been eliminated except for ; exception 9, coprocessor segment overrun, and exception 16, coprocessor ; error. ; ; ********************************************** ; * Your code to detect these exceptions here. * ; ********************************************** push bp push cx push bx push ax push ds ; SP -> Ring0_EH_DS ; ; Point to the user's stack. ; lea bp,Ring0_EH_SS mov cx,[bp] mov ds,cx mov bx,[bp-2] ; DS:[BX] -> user stack ; ; Copy the IRET frame to the user stack. ; lea bp,Ring0_EH_Flags mov cx,6 pmfa_copy_IRET: mov ax,[bp] dec bx dec bx mov [bx],ax dec bp dec bp loop pmfa_copy_IRET ; ; Point BP at vector entry for this (reserved) interrupt. ; mov ax,Ring0_EH_PEC ; fetch near return address sub ax,offset DXPMCODE:PMFaultEntryVector+3 mov cl,3 div cl ; AX = interrupt number shl ax,2 ; AX = vector entry offset lea bp,PMIntelVector add bp,ax ; BP -> interrupt handler address mov ax,[bp] ; AX = IP of handler mov cx,[bp+2] ; CX = CS of handler ; ; Build a far return frame on user stack, switch to user stack, and return ; sub bx,2 mov ds:[bx],cx ; push cs sub bx,2 mov ds:[bx],ax ; push ip lea bp,Ring0_EH_PEC mov ax,Ring0_EH_BP sub bx,2 mov ds:[bx],ax ; push bp mov Ring0_EH_PEC,bx ; sp for lss mov bx,ds mov Ring0_EH_EC,bx ; ss for lss pop ds pop ax pop bx pop cx .386p lss sp,[bp] ; switch stack .286p pop bp retf ; Out of here. pmfa_bad_stack: if DEBUG mov ax,ss mov bx,sp Trace_Out "Fault Handler Aborting with SS:SP = #AX:#BX" pop ax sub ax, (offset DXPMCODE:PMFaultEntryVector) + 3 mov bx,3 div bl Trace_Out "Fault Number #AX" pop ax pop bx pop cx pop dx Trace_Out "First four stack words: #AX #BX #CX #DX." endif push selDgroupPM push offset DGROUP:rgwStack rpushf push SEL_DXPMCODE or STD_RING push offset DXPMCODE:pmfr_death riret pmfr_death: mov ax,cs mov ds,ax mov dx,offset szRing0FaultMessage pmdossvc 09h jmp PMAbort PMFaultAnalyzer endp FR_Stack Struc FR_BP dd ? FR_AX dd ? FR_BX dd ? FR_DS dw ? FR_ENTRY dd ? ; SS:[SP] points here on entry ; to PMReservedReflector FR_Toss dd ? ; DPMI return IP FR_Ret_IP dd ? ; actual fault handler gets put FR_Ret_CS dd ? ; here to return to FR_IP dd ? FR_CS dd ? FR_FL dd ? FR_SP dd ? FR_SS dd ? FR_Stack Ends ; ; Alternate names so the structure above makes more sense to ; PMFaultReflector. ; FR_Handler_IP equ FR_DS FR_Handler_CS equ FR_ENTRY FR_Handler_Ret_IP equ FR_Toss FR_Handler_Ret_CS equ FR_Ret_IP FR_Handler_Entry equ FR_Handler_Ret_CS FR_EC equ FR_Ret_CS ; ------------------------------------------------------------------ ; PMFaultReflector -- Dispatch a fault to a fault handler installed ; in PMFaultVector. When the fault handler returns, return ; to the faulting code, using the addresses placed on the ; DPMI fault handler stack by the last called fault handler. ; ; Input: ; Entry is by a NEAR call, with an IP within the range ; of PMFaultEntryVector on the stack. The stack has been ; set up for use by a DPMI fault handler. ; ; Output: ; Controlled by fault handler. ; ; Uses: ; Controlled by fault handler. ; ; Notes: ; Fault handlers are called on a static stack. This routine ; is NOT REENTRANT. ; public PMFaultReflector public PMFaultReflectorIRET PMFaultReflector proc near assume ss:nothing,ds:nothing,es:nothing sub sp,6 push bx push eax push bp mov bp,sp push ds mov ax,SEL_DXDATA or STD_RING mov ds,ax assume ds:dgroup mov ax,[bp.FR_Handler_Entry] sub ax,offset DXPMCODE:PMFaultEntryVector+3 mov bl,3 div bl ; AX = interrupt number shl ax,3 ; AX = offset of fault handler lea bx,PMFaultVector add bx,ax ; SS:[BX] -> fault vector entry mov eax,word ptr ds:[bx] mov [bp.FR_Handler_IP],eax mov ax,word ptr ds:[bx+2] mov [bp.FR_Handler_CS],ax lea ax,pmfr_cleanup movzx eax,ax mov [bp.FR_Handler_Ret_IP],eax push cs pop [bp.FR_Handler_Ret_CS] pop ds assume ds:nothing pop bp pop ax pop bx db 066h,0CBh ; This calls the fault handler. PMFaultReflectorIRETCall: dd (SEL_RZIRET or STD_RING) shl 10h pmfr_cleanup: ; ; Unwind the fault handler stack. Return to the faulting code. ; This works by calling a Ring 0 procedure to do the actual IRET. ; If we do it that way, we can return to the faulting code without ; actually touching the faulting code's stack. ; PMFaultReflectorIRET: ; BUGBUG Daveh using user stack this way is less robust!!! .386p add sp,4 ; pop error code push bp mov bp,sp push ebx push ds push eax mov ax,[bp + 18] mov ds,ax mov ebx,[bp + 14] ; ds:bx -> user stack sub ebx,4 mov eax,[bp + 10] mov ds:[ebx],eax ; push flags sub ebx,4 mov eax,[bp + 4] mov ds:[ebx],eax ; push cs sub ebx,4 mov eax,[bp + 2] mov ds:[ebx],eax ; push ip sub ebx,2 mov ax,[bp] mov ds:[ebx],ax ; push bp mov [bp + 8],bx pop eax pop ds pop ebx pop bp add sp,6 ; point to ss:sp mov bp,sp lss esp,[bp] .286p pop bp ; restore bp riretd endif PMFaultReflector endp ; ; ------------------------------------------------------- ; PMReservedReflector -- This routine is for reflecting ; exceptions to a protected mode interrupt handler. ; The default for exceptions 1, 2, and 3 is to have ; a near call to this routine placed in the PMFaultVector. ; ; This routine strips off the fault handler stack set ; up by PMFaultAnalyzer, switches to the stack pointed ; to by the pushed SS and SP values, sets up an IRET ; frame for use by PMIntrReflector, and jumps to ; PMIntrReflector. Eventual return is via an IRET ; from PMIntrReflector. ; ; Input: ; Entry is by a NEAR call, with an IP within the range ; of PMReservedEntryVector on the stack. The stack has been ; set up for use by a DPMI fault handler. ; ; Output: ; Switch to stack registers set up by any previous fault ; handler, jump to PMIntrReflector with an IRET frame set up ; for direct return to the interrupted code. ; ; Errors: none ; ; Uses: Modifies SS, SP. Does not return to caller. ; assume ds:NOTHING,es:NOTHING,ss:NOTHING public PMReservedReflector PMReservedReflector: push ds push ebx push eax push ebp mov bp,sp ; ; BP now points to a stack frame described by the structure ; above. This will be copied to a stack frame on the stack pointed to ; by FR_SS:FR_SS. In most cases, the destination stack is actually ; the same as the present stack, offset by four bytes. The following ; block of code is therefore very likely an overlapping copy. Think ; carefully before modifying how it works. ; mov bx,[bp.FR_SS] mov ds,bx mov ebx,[bp.FR_SP] ; DS:[BX] -> interrupted code's stack sub bx, (size FR_Stack) - 8 ; (not copying SP or SS) ; DS:[BX] -> place to copy our stack frame mov eax,[bp.FR_FL] ; Push user IRET frame onto the destination mov [ebx.FR_FL],eax ; stack. mov eax,[bp.FR_CS] mov [ebx.FR_CS],eax mov eax,[bp.FR_IP] mov [ebx.FR_IP],eax mov eax,[bp.FR_ENTRY] ; Copy our caller's near return. mov [ebx.FR_ENTRY],eax mov ax,[bp.FR_DS] ; Copy saved registers. mov [ebx.FR_DS],ax mov eax,[bp.FR_BX] mov [ebx.FR_BX],eax mov eax,[bp.FR_AX] mov [ebx.FR_AX],eax mov eax,[bp.FR_BP] mov [ebx.FR_BP],eax mov ax,ds ; Switch to user stack. mov ss,ax mov esp,ebx mov ebp,esp mov ax,[ebp.FR_ENTRY] ; AX = offset of caller sub ax,offset DXPMCODE:PMReservedEntryVector + 3 mov bl,3 div bl ; AX = interrupt number shl ax,2 ; AX = offset into PMIntelVector mov ds,SelDgroupPM assume ds:DGROUP lea bx,PMIntelVector add bx,ax ; DS:[BX] -> interrupt handler mov eax,[bx] ; Place vector entry just below mov [ebp.FR_Ret_IP],eax ; IRET frame. mov eax,[bx+4] mov [ebp.FR_Ret_CS],eax lea esp,[ebp.FR_BP] ; Point to saved registers. pop ebp ; Pop 'em. pop eax pop ebx pop ds add esp,4 ; Fix up stack. db 066h,0CBh ; jump to interrupt handler via far return DXPMCODE ends ; ------------------------------------------------------- subttl Real Mode Interrupt Reflector page ; ------------------------------------------------------- ; REAL MODE INTERRUPT REFLECTOR ; ------------------------------------------------------- DXCODE segment assume cs:DXCODE ; ------------------------------------------------------- ; RMIntrEntryVector -- This table contains a vector of ; near jump instructions to the real mode interrupt ; reflector. Real mode interrupts that have been hooked ; by the protected mode application have their vector ; set to entry the real mode reflector through this table. public RMIntrEntryVector RMIntrEntryVector: rept 256 call RMIntrReflector endm ; ------------------------------------------------------- ; RMIntrReflector -- This routine is the entry point for ; the real mode interrupt reflector. This routine ; is entered when an interrupt occurs (either software ; or hardware) that has been hooked by the protected mode ; application. It switches the processor to protected mode ; and transfers control to the appropriate interrupt ; service routine for the interrupt. After the interrupt ; service routine completes, it switches back to real ; mode and returns control to the originally interrupted ; real mode code. ; Entry to this routine comes from the RMIntrEntryVector, ; which contains a vector of near call instructions, which ; all call here. The interrupt number is determined from ; the return address of the near call from the interrupt ; entry vector. ; The address of the protected mode interrupt service routine ; to execute is determined from the protected mode interrupt ; descriptor tabel and the interrupt number. ; ; Input: none ; Output: none ; Errors: none ; Uses: The segment registers are explicitly preserved by ; this routine. Other registers are as preserved or ; modified by the interrutp service routine. assume ds:NOTHING,es:NOTHING,ss:NOTHING public RMIntrReflector RMIntrReflector: ; ; On entry, the stack layout is: ; [6] FLAGS - " ; [4] CS - " ; [2] IP - from original interrupt ; [0] IP - from interrupt entry vector call ; FCLI cld push ds IFDEF ROM SetRMDataSeg ELSE mov ds,selDgroup ENDIF assume ds:DGROUP if DEBUG ; ; Are we on a DOSX interrupt reflector stack? ; push ax push cx mov ax,ss mov cx,ds cmp ax,cx pop cx jne @F cmp sp,offset bReflStack jb @F cmp sp,offset pbReflStack jnb @F ; ; If so, have we overflowed a stacklet? ; mov ax,pbReflStack cmp sp,ax ja @F add ax,CB_STKFRAME cmp sp,ax jb @F pop ax Real_Debug_Out "DOSX:RMIntrReflector--Reflector stack overflow." push ax @@: pop ax endif ;DEBUG mov regUserAX,ax ;save user AX for later push bp ;stack -> BP DS IP IP CS FL mov bp,sp ; [0] [2] [4] [6] [8] [A] mov ax,[bp+0Ah] ;get the interrupted routine's flags and ax,NOT 4100h ;clear the trace flag in case we got ; an interrupt on an instruction about ; to be single stepped mov regUserFL,ax ;and save for later mov ax,es xchg ax,[bp+4] ;save ES and get entry vector address pop bp ; Some software (like older versions of Smartdrv.sys) may enable A20 on ; their own, and get very 'suprised' to find it turned off by our PM->RM ; mode switch. If they used Himem.sys, this wouldn't be necessary, but... if VCPI cmp fVCPI,0 jnz @f endif push ax ;get/save current A20 state on stack push bx xmssvc 7 mov regUserSP,ax ;use regUserSP as a temp var pop bx pop ax @@: push regUserSP ; The state that we want to save on the user's stack has been set up. ; Convert the entry vector return address into an interrupt number. sub ax,offset RMIntrEntryVector+3 push cx mov cl,3 div cl pop cx if DEBUG mov PMIntNo,ax endif ; Allocate a new stack frame, and then switch to the reflector stack ; frame. mov regUserSP,sp ;save entry stack pointer so we can mov regUSerSS,ss ; switch to our own stack IFDEF ROM push ds pop ss ELSE mov ss,selDgroup ;switch to the reflector stack frame ENDIF mov sp,pbReflStack push pbReflStack ;save stack frame ptr on stack sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame ; We are now running on our own stack, so we can switch into protected mode. push ax ;save interrupt vector table offset SwitchToProtectedMode pop ax if DEBUG ;-------------------------------------------------------- push 0DEADh ;debugging id & interrupt number push PMIntNo cmp fTraceReflect,0 jz @f push ax mov ax,PMIntNo Trace_Out "(rp#AL)",x pop ax @@: ; Perform a too-late-to-save-us-now-but-we-want-to-know check on the ; reflector stack. cmp StackGuard,1022h jz @f Debug_Out "DOSX:RMIntrReflector--Global reflector stack overflow." @@: endif ;DEBUG --------------------------------------------------------- ; Build an IRET frame on the stack so that the protected mode interrupt service ; routine will return to us when it is finished. push regUserSS ;save user stack address on our own stack push regUserSP ; frame so we can restore it later push ds push regUserFL push cs push offset rmrf50 ; Build an IRET frame on the stack to use to transfer control to the ; protected mode ISR and byte ptr regUserFL+1,not 02h ;use entry flags less the push 0 ; high half esp push regUserFL ; interrupt flag (IF) xchg bx,ax ;interrupt vector offset to BX, preserve BX cmp bx,CRESERVED ;Interrupt in reserved range? jc rmrf_reserved shl bx,3 mov es,selIDT jmp rmrf_setISR rmrf_reserved: shl bx,2 mov es,SelDgroupPM add bx,offset DGROUP:PMIntelVector rmrf_setISR: push dword ptr es:[bx+4] ;push segment of isr push dword ptr es:[bx] ;push offset of isr xchg bx,ax mov ax,regUserAX ;restore entry value of AX push ds pop es ; At this point the interrupt reflector stack looks like this: ; ; [18] previous stack frame pointer ; [16] stack segment of original stack ; [14] stack pointer of original stack ; [12] protected mode dos extender data segment ; [10] dos extender flags ; [8] segment of return address back to interupt reflector ; [6] offset of return address back to interrupt reflector ; [4] user flags as on entry from original interrupt ; [2] segment of protected mode ISR ; [0] offset of protected mode ISR ; ; Execute the protected mode interrupt service routine iretd ; The protected mode ISR will return here after it is finsished. rmrf50: pop ds pushf ;save flags as returned by PM Int routine FCLI ;We have to clear interrupts here, because cld ; the interrupt routine may have returned ; with interrupts on and our code that uses ; static variables must be protected. We ; turn them off after to pushf instruction so ; that we can preserve the state of the ; interrupt flag as returned by the ISR. mov regUserAX,ax pop ax pop regUserSP pop regUserSS if DEBUG add sp,4 ;'pop' off debugging info endif pop pbReflStack ;deallocate stack frame(s) ; Switch back to real mode. push ax ;preserve AX SwitchToRealMode pop ax ; Switch back to the original stack. mov ss,regUserSS mov sp,regUserSP ; Make sure the A20 line matches whatever state it was when the int occured. ; This is for the benefit of any software that diddles A20 without using ; an XMS driver pop regUserSP ;A20 state at time of interrupt to temp var if VCPI cmp fVCPI,0 jnz rmrf75 endif push ax ;save current ax mov ax,regUserSP ;ax = A20 state at time of interrupt or ax,ax ;if it was off, don't sweat it jz rmrf70 push bx ;save bx (XMS calls destroy bl) push ax xmssvc 7 ;ax = current A20 state pop bx ;bx = old A20 state cmp ax,bx ;if A20 is still on, don't need to diddle jz @f xmssvc 5 ;force A20 back on inc A20EnableCount ; and remember that we did this if DEBUG or fA20,04h endif @@: pop bx rmrf70: pop ax rmrf75: ; Put the flags returned by the real mode interrupt routine back into ; the caller's stack so that they will be returned properly. push bp ;stack -> BP DS ES IP CS FL mov bp,sp ; [0] [2] [4] [6] [8] [10] and [bp+10],0300h ;clear all but the interrupt and trace flags ; in the caller's original flags or [bp+10],ax ;combine in the flags returned by the ; interrupt service routine. This will cause ; us to return to the original routine with ; interrupts on if they were on when the ; interrupt occured, or if the ISR returned ; with them on. pop bp ; And return to the original interrupted program. mov ax,regUserAX pop ds pop es iret DXCODE ends WowIntr3216 proc FCLI push ebp mov ebp,esp mov regUserAX,eax mov regUserBX,ebx mov regUserDS,ds mov regUserES,es mov regUserFlags,[ebp + 16] mov ax,SEL_DXDATA OR STD_RING mov ds,ax assume ds:dgroup mov ebx,esp mov regUserSp,eax mov ax,ss mov regUserSs,ax mov bx,pbReflStack ; get pointer to new frame sub bpReflStack,CB_STKFRAME ; ; put user stack pointer on new stack ; sub bx,4 mov [bx],regUserSs sub bx,4 mov [bx],dword ptr regUserSp mov ax,[ebp + 4] ; ; switch to new stack ; push ds pop ss movzx esp,bx ; ; Save ss:esp for lss esp ; push regUserSs push regUserSp ; ; Create an int frame ; pushf push cs push offset wi30 ; ; Put handler address on stack ; shl ax,3 mov es,selDgroupPM add bx,offset DGROUP:Intr16Vector push [bx + 2] push [bx] ; ; Restore Registers ; mov eax,regUserAx mov ebx,regUserBX mov es,regUserES mov ds,regUserDS assume ds:nothing ; ; call handler ; retf wi30: ; ; handler will return here ; ; ; Switch stacks ; mov ebp,esp lss esp,[ebp] mov ebp,esp push eax push ds mov ds,SEL_DXDATA OR STD_RING assume ds:DGROUP ; ; Deallocate stack frame ; add pbReflStack,CB_STKFRAME pop eax pop ds ; ; Return flags from int handler ; pushf pop [ebp + 16] ; ; Return to interrupted code ; pop ebp riretd WowIntr3216 endp