;++ ; ; Copyright (c) 1989 Microsoft Corporation ; ; Module Name: ; ; kimacro.inc ; ; Abstract: ; ; This module contains the macros used by kernel assembler code. ; It includes macros to manipulate interrupts, support system ; entry and exit for syscalls, faults, and interrupts, and ; manipulate floating point state. ; ; Author: ; ; Shie-Lin (shielint) 24-Jan-1990 ; ; Revision History: ; ; BryanWi 17-Aug-90 ; Replace GENERATE_MACHINE... and RESTORE... with ENTER_... ; and EXIT_ALL macros. ; ;-- ;++ ; ; These constants are used by the fpo directives in this file. ; This directive causes the assembler to output a .debug$f segment ; in the obj file. The segment will contain 1 fpo record for each ; directive present during assembly. ; ; Although the assembler will accept all valid values, the value of 7 ; in the FPO_REGS field indicates to the debugger that a trap frame is ; generated by the function. The value of 7 can be used because the ; C/C++ compiler puts a maximum value of 3 in the field. ; FPO_LOCALS equ 0 ; 32 bits, size of locals in dwords FPO_PARAMS equ 0 ; 32 bits, size of parameters in dwords FPO_PROLOG equ 0 ; 12 bits, 0-4095, # of bytes in prolog FPO_REGS equ 0 ; 3 bits, 0-7, # regs saved in prolog FPO_USE_EBP equ 0 ; 1 bit, 0-1, is ebp used? FPO_TRAPFRAME equ 1 ; 2 bits, 0=fpo, 1=trap frame, 2=tss ; ;-- ;++ ; ; POLL_DEBUGGER ; ; Macro Description: ; ; Call the debugger so it can check for control-c. If it finds ; it, it will report our iret address as address of break-in. ; ; N.B. This macro should be used when all the caller's registers ; have been restored. (Otherwise, the kernel debugger register ; dump will not have correct state.) The only exception is ; fs. This is because Kd may need to access PCR or PRCB. ; ; Arguments: ; ; There MUST be an iret frame on the stack when this macro ; is invoked. ; ; Exit: ; ; Debugger will iret for us, so we don't usually return from ; this macro, but remember that it generates nothing for non-DEVL ; kernels. ;-- POLL_DEBUGGER macro local a, b, c_ if DEVL EXTRNP _DbgBreakPointWithStatus,1 stdCall _KdPollBreakIn or al,al jz short c_ stdCall _DbgBreakPointWithStatus, c_: endif ; DEVL endm ;++ ; ; ASSERT_FS ; ; Try to catch funky condition wherein we get FS=r3 value while ; running in kernel mode. ; ;-- ASSERT_FS macro local a,b if DBG EXTRNP _KeBugCheck,1 mov bx,fs cmp bx,KGDT_R0_PCR jnz short a cmp dword ptr fs:[0], 0 jne short b a: stdCall _KeBugCheck,<-1> align 4 b: endif endm ;++ ; ; ; Copy data from various places into base of TrapFrame, net effect ; is to allow dbg KB command to trace accross trap frame, and to ; allow user to find arguments to system calls. ; ; USE ebx and edi. ;-- SET_DEBUG_DATA macro ife FPO ; ; This macro is used by ENTER_SYSTEM_CALL, ENTER_TRAP and ENTER_INTERRUPT ; and is used at the end of above macros. It is safe to destroy ebx, edi. ; mov ebx,[ebp]+TsEbp mov edi,[ebp]+TsEip mov [ebp]+TsDbgArgPointer,edx mov [ebp]+TsDbgArgMark,0BADB0D00h mov [ebp]+TsDbgEbp,ebx mov [ebp]+TsDbgEip,edi endif endm ;++ ; ; ENTER_DR_ASSIST EnterLabel, ExitLabel, NoAbiosAssist, NoV86Assist ; ; Macro Description: ; ; Jumped to by ENTER_ macros to deal with DR register work, ; abios work and v86 work. The main purpose of this macro is ; that interrupt/trap/systemCall EnterMacros can jump here to ; deal with some special cases such that most of the times the ; main ENTER_ execution flow can proceed without being branched. ; ; If (previousmode == usermode) { ; save DR* in trapframe ; load DR* from Prcb ; } ; ; Arguments: ; EnterLabel - label to emit ; ExitLabel - label to branch to when done ; ; Entry-conditions: ; Dr work: ; DebugActive == TRUE ; (esi)->Thread object ; (esp)->base of trap frame ; (ebp)->base of trap frame ; ; Abios work: ; v86 work: ; ; Exit-conditions: ; Dr work: ; Interrupts match input state (this routine doesn't change IEF) ; (esp)->base of trap frame ; (ebp)->base of trap frame ; Preserves entry eax, edx ; Abios work: ; v86 work: ; ;-- ENTER_DR_ASSIST macro EnterLabel, ExitLabel, NoAbiosAssist, NoV86Assist, V86R local a,b public Dr_&EnterLabel align 4 Dr_&EnterLabel: ; ; Test if we came from user-mode. If not, do nothing. ; test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK jnz short a test dword ptr [ebp]+TsSegCs,MODE_MASK jz Dr_&ExitLabel ; called from kmode, go continue ; ; Save user-mode Dr* regs in TrapFrame ; ; We are safe to destroy ebx, ecx, edi because in ENTER_INTERRUPT and ; ENTER_TRAP these registers are saved already. In ENTER_SYSTEMCALL ; ebx, edi is saved and ecx is don't-care. ; a: mov ebx,dr0 mov ecx,dr1 mov edi,dr2 mov [ebp]+TsDr0,ebx mov [ebp]+TsDr1,ecx mov [ebp]+TsDr2,edi mov ebx,dr3 mov ecx,dr6 mov edi,dr7 mov [ebp]+TsDr3,ebx mov [ebp]+TsDr6,ecx mov ebx,0 mov [ebp]+TsDr7,edi ; ; Make Dr7 safe before loading junk from save area ; mov dr7,ebx ; ; Load KernelDr* into processor ; mov edi,dword ptr fs:[PcPrcb] mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr0 mov ecx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr1 mov dr0,ebx mov dr1,ecx mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr2 mov ecx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr3 mov dr2,ebx mov dr3,ecx mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr6 mov ecx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr7 mov dr6,ebx mov dr7,ecx ifnb test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK jz short b jmp Dr_&V86R endif b: jmp Dr_&ExitLabel ifb public Abios_&EnterLabel align 4 Abios_&EnterLabel: ; ; INTERRUPT_STACK16_TO_STACK32 ; ; This macro remaps current 32bit stack to 16bit stack at interrupt ; time. ; ; Arguments: ; ; (esp)->trap frame. ; (eax)->Entry Esp. ; mov eax, [esp].TsErrCode ; (eax) = Entry Esp mov ecx, KGDT_R0_DATA mov edx, esp shl eax, 16 add edx, fs:[PcstackLimit] mov [esp].TsErrCode, eax mov ss, cx mov esp, edx ; Interrupts are off mov ebp, edx jmp Abios_&ExitLabel endif ; NoAbiosAssist ifb public V86_&EnterLabel align 4 V86_&EnterLabel: ; ; Move the V86 segment registers to the correct place in the frame ; mov eax,dword ptr [ebp].TsV86Fs mov ebx,dword ptr [ebp].TsV86Gs mov ecx,dword ptr [ebp].TsV86Es mov edx,dword ptr [ebp].TsV86Ds mov [ebp].TsSegFs,ax mov [ebp].TsSegGs,bx mov [ebp].TsSegEs,cx mov [ebp].TsSegDs,dx jmp V86_&ExitLabel endif ; NoV86Assist endm ;++ ; ; ENTER_SYSCALL AssistLabel, TagetLabel, NoFSLoad ; ; Macro Description: ; ; Build the frame and set registers needed by a system call. ; ; Save: ; Errorpad, ; Non-volatile regs, ; FS, ; ExceptionList, ; PreviousMode ; ; Don't Save: ; Volatile regs ; Seg regs ; Floating point state ; ; Set: ; FS, ; ExceptionList, ; PreviousMode, ; Direction ; ; Arguments: ; AssistLabel - label ENTER_ASSIST macro is at ; TargetLabel - label to emit for ENTER_ASSIST to jump to ; NoFSLoad - Don't set FS(it is already set to KGDT_R0_PCR at entry). ; ; Exit-conditions: ; Interrupts match input state (this routine doesn't change IEF) ; (esp)->base of trap frame ; (ebp)->base of trap frame ; Preserves entry eax, edx ; ; Note: ; The DS: reference to PreviousMode is *required* for correct ; functioning of lazy selector loads. If you remove this use ; of DS:, put a DS: override on something. ; ;-- ENTER_SYSCALL macro AssistLabel, TargetLabel, NoFSLoad .FPO ( FPO_LOCALS, FPO_PARAMS, FPO_PROLOG, FPO_REGS, FPO_USE_EBP, FPO_TRAPFRAME ) ifdef KERNELONLY ; ; Construct trap frame. ; ; N.B. The initial part of the trap frame is constructed by pushing values ; on the stack. If the format of the trap frame is changed, then the ; following code must alos be changed. ; push 0 ; put pad dword for error on stack push ebp ; save the non-volatile registers push ebx ; push esi ; push edi ; ifb push fs ; save and set FS to PCR. mov ebx,KGDT_R0_PCR ; set PCR segment number mov fs,bx ; else ; FS already contains KGDT_R0_PCR(entry via PentiumPro fast system call) push KGDT_R3_TEB OR RPL_MASK endif ; NoFSLoad ; ; Save the old exception list in trap frame and initialize a new empty ; exception list. ; push PCR[PcExceptionList] ; save old exception list mov PCR[PcExceptionList],EXCEPTION_CHAIN_END ; set new empty list ; ; Save the old previous mode in trap frame, allocate remainder of trap frame, ; and set the new previous mode. ; mov esi,PCR[PcPrcbData+PbCurrentThread] ; get current thread address push [esi]+ThPreviousMode ; save old previous mode sub esp,TsPreviousPreviousMode ; allocate remainder of trap frame mov ebx,[esp+TsSegCS] ; compute new previous mode and ebx,MODE_MASK ; mov [esi]+ThPreviousMode,bl ; set new previous mode ; ; Save the old trap frame address and set the new trap frame address. ; mov ebp,esp ; set trap frame address mov ebx,[esi].ThTrapFrame ; save current trap frame address mov [ebp].TsEdx,ebx ; mov [esi].ThTrapFrame,ebp ; set new trap frame address cld ; make sure direction is forward SET_DEBUG_DATA ; Note this destroys edi test byte ptr [esi]+ThDebugActive,-1 ; test if debugging active jnz Dr_&AssistLabel ; if nz, debugging is active on thread Dr_&TargetLabel: ; sti ; enable interrupts else %out ENTER_SYSCAL outside of kernel .err endif endm ;++ ; ; ENTER_INTERRUPT AssistLabel, TargetLabel ; ; Macro Description: ; ; Build the frame and set registers needed by an interrupt. ; ; Save: ; Errorpad, ; Non-volatile regs, ; FS, ; ExceptionList, ; PreviousMode ; Volatile regs ; Seg regs from V86 mode ; DS, ES, GS ; ; Don't Save: ; Floating point state ; ; Set: ; FS, ; ExceptionList, ; Direction, ; DS, ES ; ; Don't Set: ; PreviousMode ; ; Arguments: ; AssistLabel - label ENTER_ASSIST macro is at ; TargetLabel - label to emit for ENTER_ASSIST to jump to ; ; Exit-conditions: ; Interrupts match input state (this routine doesn't change IEF) ; (esp)->base of trap frame ; (ebp)->base of trap frame ; Preserves entry eax, ecx, edx ; ;-- ENTER_INTERRUPT macro AssistLabel, TargetLabel, PassParm local b .FPO ( FPO_LOCALS+2, FPO_PARAMS, FPO_PROLOG, FPO_REGS, FPO_USE_EBP, FPO_TRAPFRAME ) ; ; Fill in parts of frame we care about ; ifb push esp ; Use Error code field to save 16bit esp endif push ebp ; Save the non-volatile registers push ebx push esi push edi sub esp, TsEdi mov ebp,esp mov [esp]+TsEax, eax ; Save volatile registers mov [esp]+TsEcx, ecx mov [esp]+TsEdx, edx if DBG mov dword ptr [esp]+TsPreviousPreviousMode, -1 ; ThPreviousMode not pushed on interrupt endif test dword ptr [esp].TsEflags,EFLAGS_V86_MASK jnz V86_&AssistLabel cmp word ptr [esp]+TsSegCs, KGDT_R0_CODE jz short @f mov [esp]+TsSegFs, fs ; Save and set FS to PCR. mov [esp]+TsSegDs, ds mov [esp]+TsSegEs, es mov [esp]+TsSegGs, gs V86_&TargetLabel: mov ebx,KGDT_R0_PCR mov eax,KGDT_R3_DATA OR RPL_MASK mov fs, bx mov ds, ax mov es, ax @@: mov ebx, fs:[PcExceptionList] ;Save, set ExceptionList mov fs:[PcExceptionList],EXCEPTION_CHAIN_END mov [esp]+TsExceptionList, ebx ifnb lea eax, [esp].TsErrCode lea ecx, [esp].TsEip ; Move eax to EIP field mov ebx, ss:[eax] ; (ebx) = parameter to pass mov ss:[eax], ecx ; save 16bit esp endif ; ; Remap ABIOS 16 bit stack to 32 bit stack, if necessary. ; cmp esp, 10000h jb Abios_&AssistLabel mov dword ptr [esp].TsErrCode, 0 ; Indicate no remapping. Abios_&TargetLabel: ; ; end of Abios stack checking ; cld ifnb push ebx ; push parameter as argument endif SET_DEBUG_DATA test byte ptr PCR[PcDebugActive], -1 jnz Dr_&AssistLabel Dr_&TargetLabel: endm ;++ ; ; ENTER_INTERRUPT_FORCE_STATE AssistLabel, TargetLabel ; ; Macro Description: ; ; Build the frame and set registers needed by an interrupt. ; ; This macro is the same as ENTER_INTERRUPT except that it forces the ; needed state and does not save previous state. ; ; This macro is currently only used by HalpApicRebootService which does not ; return; ; ; Save: ; Errorpad, ; Non-volatile regs, ; ExceptionList, ; PreviousMode ; Volatile regs ; Seg regs from V86 mode ; ; Don't Save: ; FS, ; DS, ES, GS ; Floating point state ; ; Set: ; FS, ; ExceptionList, ; Direction, ; DS, ES ; ; Don't Set: ; PreviousMode ; ; Arguments: ; AssistLabel - label ENTER_ASSIST macro is at ; TargetLabel - label to emit for ENTER_ASSIST to jump to ; ; Exit-conditions: ; Interrupts match input state (this routine doesn't change IEF) ; (esp)->base of trap frame ; (ebp)->base of trap frame ; Preserves entry eax, ecx, edx ; ;-- ENTER_INTERRUPT_FORCE_STATE macro AssistLabel, TargetLabel, PassParm local b .FPO ( FPO_LOCALS+2, FPO_PARAMS, FPO_PROLOG, FPO_REGS, FPO_USE_EBP, FPO_TRAPFRAME ) ; ; Fill in parts of frame we care about ; ifb push esp ; Use Error code field to save 16bit esp endif push ebp ; Save the non-volatile registers push ebx push esi push edi sub esp, TsEdi mov ebp,esp mov [esp]+TsEax, eax ; Save volatile registers mov [esp]+TsEcx, ecx mov [esp]+TsEdx, edx if DBG mov dword ptr [esp]+TsPreviousPreviousMode, -1 ; ThPreviousMode not pushed on interrupt endif test dword ptr [esp].TsEflags,EFLAGS_V86_MASK jnz V86_&AssistLabel V86_&TargetLabel: mov ebx,KGDT_R0_PCR mov eax,KGDT_R3_DATA OR RPL_MASK mov fs, bx mov ds, ax mov es, ax @@: mov ebx, fs:[PcExceptionList] ;Save, set ExceptionList mov fs:[PcExceptionList],EXCEPTION_CHAIN_END mov [esp]+TsExceptionList, ebx ifnb lea eax, [esp].TsErrCode lea ecx, [esp].TsEip ; Move eax to EIP field mov ebx, ss:[eax] ; (ebx) = parameter to pass mov ss:[eax], ecx ; save 16bit esp endif ; ; Remap ABIOS 16 bit stack to 32 bit stack, if necessary. ; cmp esp, 10000h jb Abios_&AssistLabel mov dword ptr [esp].TsErrCode, 0 ; Indicate no remapping. Abios_&TargetLabel: ; ; end of Abios stack checking ; cld ifnb push ebx ; push parameter as argument endif SET_DEBUG_DATA test byte ptr PCR[PcDebugActive], -1 jnz Dr_&AssistLabel Dr_&TargetLabel: endm ;++ ; ; ENTER_TRAP AssistLabel, TargetLabel ; ; Macro Description: ; ; Build the frame and set registers needed by a trap or exception. ; ; Save: ; Non-volatile regs, ; FS, ; ExceptionList, ; PreviousMode, ; Volatile regs ; Seg Regs from V86 mode ; DS, ES, GS ; ; Don't Save: ; Floating point state ; ; Set: ; FS, ; Direction, ; DS, ES ; ; Don't Set: ; PreviousMode, ; ExceptionList ; ; Arguments: ; AssistLabel - label ENTER_ASSIST macro is at ; TargetLabel - label to emit for ENTER_ASSIST to jump to ; ; Exit-conditions: ; Interrupts match input state (this routine doesn't change IEF) ; (esp)->base of trap frame ; (ebp)->base of trap frame ; Preserves entry eax ; ;-- ENTER_TRAP macro AssistLabel, TargetLabel local b .FPO ( FPO_LOCALS, FPO_PARAMS, FPO_PROLOG, FPO_REGS, FPO_USE_EBP, FPO_TRAPFRAME ) ; ; Fill in parts of frame we care about ; if DBG ifndef _Ki16BitStackException EXTRNP _Ki16BitStackException endif endif ; DBG mov word ptr [esp+2], 0 ; Clear upper word of ErrorCode push ebp ; Save the non-volatile registers push ebx push esi push edi push fs ; Save and set FS to PCR. mov ebx,KGDT_R0_PCR mov fs,bx mov ebx, fs:[PcExceptionList] ;Save ExceptionList push ebx if DBG push -1 ; Don't need to save ThPreviousMode from trap else sub esp, 4 ; pad dword endif push eax ; Save the volatile registers push ecx push edx push ds ; Save segments push es push gs ; ; Skip allocate reset of trap frame and Set up DS/ES, they may be trash ; mov ax,KGDT_R3_DATA OR RPL_MASK sub esp,TsSegGs mov ds,ax mov es,ax if DBG ; ; The code here check if the exception occurred in ring 0 ; ABIOS code. If yes, this is a fatal condition. We will ; put out message and bugcheck. ; cmp esp, 10000h ; Is the trap in abios? jb _Ki16BitStackException ; if b, yes, switch stack and bugcheck. endif ; DBG mov ebp,esp test dword ptr [esp].TsEflags,EFLAGS_V86_MASK jnz V86_&AssistLabel V86_&TargetLabel: cld SET_DEBUG_DATA test byte ptr PCR[PcDebugActive], -1 jnz Dr_&AssistLabel Dr_&TargetLabel: endm ;++ ; ; EXIT_ALL NoRestoreSegs, NoRestoreVolatiles, NoPreviousMode ; ; Macro Description: ; ; Load a syscall frame back into the machine. ; ; Restore: ; Volatile regs, IF NoRestoreVolatiles blank ; NoPreviousMode, ; ExceptionList, ; FS, ; Non-volatile regs ; ; If the frame is a kernel mode frame, AND esp has been edited, ; then TsSegCs will have a special value. Test for that value ; and execute special code for that case. ; ; N.B. This macro generates an IRET! (i.e. It exits!) ; ; Arguments: ; ; NoRestoreSegs - non-blank if DS, ES, GS are NOT to be restored ; ; NoRestoreVolatiles - non-blank if Volatile regs are NOT to be restored ; ; NoPreviousMode - if nb pop ThPreviousMode ; ; Entry-conditions: ; ; (esp)->base of trap frame ; (ebp)->Base of trap frame ; ; Exit-conditions: ; ; Does not exit, returns. ; Preserves eax, ecx, edx, IFF NoRestoreVolatiles is set ; ;-- ?adjesp = 0 ?RestoreAll = 1 EXIT_ALL macro NoRestoreSegs, NoRestoreVolatiles, NoPreviousMode local a, b, f, x local Dr_ExitHelp, Dr_ExitHelp_Target local Db_NotATrapFrame, Db_A, Db_NotValidEntry, NonFlatPm_Target ; ; Sanity check some values and setup globals for macro ; ?adjesp = TsSegGs ?RestoreAll = 1 ifnb ?RestoreAll = 0 ?adjesp = ?adjesp + 12 endif ifnb if ?RestoreAll eq 1 %out "EXIT_ALL NoRestoreVolatiles requires NoRestoreSegs" .err endif ?adjesp = ?adjesp + 12 endif ifb ifndef KERNELONLY %out EXIT_ALL can not restore previousmode outside kernel .err endif endif ; All callers are responsible for getting here with interrupts disabled. if DBG pushfd pop edx test edx, EFLAGS_INTERRUPT_MASK jnz Db_NotValidEntry cmp esp, ebp ; make sure esp = ebp jne Db_NotValidEntry ; Make sure BADB0D00 sig is present. If not this isn't a trap frame! Db_A: sub [esp]+TsDbgArgMark,0BADB0D00h jne Db_NotATrapFrame endif ASSERT_FS mov edx, [esp]+TsExceptionList if DBG or edx, edx jnz short @f int 3 @@: endif mov ebx, fs:[PcDebugActive] ; (ebx) = DebugActive flag mov fs:[PcExceptionList], edx ; Restore ExceptionList ifb mov ecx, [esp]+TsPreviousPreviousMode ; Restore PreviousMode if DBG cmp ecx, -1 ; temporary debugging code jne @f ; to make sure no one tries to pop ThPreviousMode int 3 ; when it wasn't saved @@: endif mov esi,fs:[PcPrcbData+PbCurrentThread] mov [esi]+ThPreviousMode,cl else if DBG mov ecx, [esp]+TsPreviousPreviousMode cmp ecx, -1 ; temporary debugging code je @f ; to make sure no one pushed ThPreviousMode and int 3 ; is now exiting without restoreing it @@: endif endif test ebx, 0fh jnz Dr_ExitHelp Dr_ExitHelp_Target: test dword ptr [esp].TsEflags,EFLAGS_V86_MASK jnz V86ExitHelp test word ptr [esp]+TsSegCs,FRAME_EDITED jz b ; Edited frame pop out. if ?RestoreAll eq 0 .errnz MODE_MASK-1 cmp word ptr [esp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK ; set/clear ZF bt word ptr [esp]+TsSegCs,0 ; test MODE_MASK set/clear CF cmc ; (CF=1 and ZF=0) ja f ; jmp if CF=0 and ZF=0 endif ifb mov edx, [esp]+TsEdx ; Restore volitales mov ecx, [esp]+TsEcx ; must restore eax before any mov eax, [esp].TsEax ; selectors! (see trap0e handler) endif cmp word ptr [ebp]+TsSegCs, KGDT_R0_CODE jz short @f ifb lea esp, [ebp]+TsSegGs pop gs ; Restore Segs pop es pop ds endif NonFlatPm_Target: lea esp, [ebp]+TsSegFs pop fs @@: lea esp, [ebp]+TsEdi ; Skip PreMode, ExceptList and fs pop edi ; restore non-volatiles pop esi pop ebx pop ebp ; ; Esp MUST point to the Error Code on the stack. Because we use it to ; store the entering esp. ; cmp word ptr [esp+8], 80h ; check for abios code segment? ja AbiosExitHelp add esp, 4 ; remove error code from trap frame ifnb public _KiSystemCallExitBranch public _KiSystemCallExit public _KiSystemCallExit2 public _KiSystemCallExit3 ; NoRestoreVolatiles is only used for return from System Service. ; If returning to Kernel mode, the processor state does not need ; to be altered (CS, CPL stays the same etc), so simply unwind the ; kernel frame and branch to the saved EIP. test dword ptr [esp+4], MODE_MASK ; If the following branch is taken, we are returning to usermode. ; If this processor supports the SYSEXIT instruction, the branch ; will be adjusted at boot time to use the appropriate code sequence. _KiSystemCallExitBranch: jnz short _KiSystemCallExit ; Exit to kernel mode from system call, faster than IRETD, ; unwind the frame and branch to return address. pop edx ; get eip pop ecx ; remove CS from stack popfd ; restore eflags jmp edx if 0 ; one day we should test and see if the following is faster ; than the above (and still valid). sti ; reenable interrupts ret 8 ; return to @esp and pop CS and EFLAGs endif _KiSystemCallExit: iretd ; return _KiSystemCallExit2: pop edx ; pop EIP add esp, 8 ; Remove CS & Eflags pop ecx ; pop ESP sti ; sysexit does not reload flags iSYSEXIT _KiSystemCallExit3: ; AMD pop ecx ; pop EIP add esp, 8 pop esp ; mov esp, [esp+8] ; remove CS & Eflags, get ESP iSYSRET endif ;; iretd ; return if DBG Db_NotATrapFrame: add [esp]+TsDbgArgMark,0BADB0D00h ; put back the orig value Db_NotValidEntry: int 3 jmp Db_A endif ; ; EXIT_HELPER ; ; if (PreviousMode == UserMode) { ; DR* regs = TF.Dr* regs ; } ; ; Entry-Conditions: ; ; DebugActive == TRUE ; (ebp)->TrapFrame ; ;-- align dword Dr_ExitHelp: test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK jnz short x test dword ptr [ebp]+TsSegCs,MODE_MASK jz Dr_ExitHelp_Target x: mov ebx,0 mov esi,[ebp]+TsDr0 mov edi,[ebp]+TsDr1 mov dr7,ebx mov dr0,esi mov ebx,[ebp]+TsDr2 mov dr1,edi mov dr2,ebx mov esi,[ebp]+TsDr3 mov edi,[ebp]+TsDr6 mov ebx,[ebp]+TsDr7 mov dr3,esi mov dr6,edi mov dr7,ebx jmp Dr_ExitHelp_Target ; if ?RestoreAll eq 0 ; ; Restore segs and volatiles for non-flat R3 PM (VDM in PM) ; f: mov eax,[esp].TsEax ; restore eax before any selectors ; (see trap0e handler) add esp,TsSegGs pop gs pop es pop ds pop edx pop ecx jmp NonFlatPm_Target endif ; not ?RestoreAll ; ; TsSegCs contains the special value that means the frame was edited ; in a way that affected esp, AND it's a kernel mode frame. ; (Special value is null selector except for RPL.) ; ; Put back the real CS. ; push eflags, eip onto target stack ; restore ; switch to target stack ; iret ; b: mov ebx,[esp]+TsTempSegCs mov [esp]+TsSegCs,ebx ; ; There is no instruction that will load esp with an arbitrary value ; (i.e. one out of a frame) and do a return, if no privledge transition ; is occuring. Therefore, if we are returning to kernel mode, and ; esp has been edited, we must "emulate" a kind of iretd. ; ; We do this by logically pushing the eip,cs,eflags onto the new ; logical stack, loading that stack, and doing an iretd. This ; requires that the new logical stack is at least 1 dword higher ; than the unedited esp would have been. (i.e. It is not legal ; to edit esp to have a new value < the old value.) ; ; KeContextToKframes enforces this rule. ; ; ; Compute new logical stack address ; mov ebx,[esp]+TsTempEsp sub ebx,12 mov [esp]+TsErrCode,ebx ; ; Copy eip,cs,eflags to new stack. note we do this high to low ; mov esi,[esp]+TsEflags mov [ebx+8],esi mov esi,[esp]+TsSegCs mov [ebx+4],esi mov esi,[esp]+TsEip mov [ebx],esi ; ; Do a standard restore sequence. ; ; Observe that RestoreVolatiles is honored. Editing a volatile ; register has no effect when returning from a system call. ; ifb mov eax,[esp].TsEax endif ; add esp,TsSegGs ; ;ifb ; pop gs ; pop es ; pop ds ;else ; add esp,12 ;endif ifb mov edx, [esp]+TsEdx mov ecx, [esp]+TsEcx endif ;ifnb ; add esp, 4 ; Skip previous mode ;else ; pop ebx ; Restore PreviousMode ; mov esi,fs:[PcPrcbData+PbCurrentThread] ; mov ss:[esi]+ThPreviousMode,bl ;endif ; ; pop ebx ; ; mov fs:[PcExceptionList], ebx ;Restore ExceptionList ; pop fs add esp, TsEdi pop edi ; restore non-volatiles pop esi pop ebx pop ebp ; ; (esp)->TsErrCode, where we saved the new esp ; mov esp,[esp] ; Do move not push to avoid increment iretd endm ;++ ; ; INTERRUPT_EXIT ; ; Macro Description: ; ; This macro is executed on return from an interrupt vector service ; service routine. Its function is to restore privileged processor ; state, and continue thread execution. If control is returning to ; user mode and there is a user APC pending, then APC level interupt ; will be requested and control is transfered to the user APC delivery ; routine, if no higher level interrupt pending. ; ; Arguments: ; ; (TOS) = previous irql ; (TOS+4) = irq vector to eoi ; (TOS+8 ...) = machine_state frame ; (ebp)-> machine state frame (trap frame) ; ;-- INTERRUPT_EXIT macro DebugCheck local a ifnb POLL_DEBUGGER endif if DBG ; save current eip for a: mov esi, offset a ; debugging bad trap frames endif ifdef __imp_Kei386EoiHelper@0 cli call _HalEndSystemInterrupt@8 jmp dword ptr [__imp_Kei386EoiHelper@0] else cli call dword ptr [__imp__HalEndSystemInterrupt@8] jmp Kei386EoiHelper@0 endif endm ;++ ; ; SPURIOUS_INTERRUPT_EXIT ; ; Macro Description: ; ; To exit an interrupt without performing the EOI. ; ; Arguments: ; ; (TOS) = machine_state frame ; (ebp)-> machine state frame (trap frame) ; ;-- SPURIOUS_INTERRUPT_EXIT macro local a if DBG ; save current eip for a: mov esi, offset a ; debugging bad trap frames endif ifdef __imp_Kei386EoiHelper@0 jmp dword ptr [__imp_Kei386EoiHelper@0] else jmp Kei386EoiHelper@0 endif endm ;++ ; ; ENTER_TRAPV86 ; ; Macro Description: ; ; Construct trap frame for v86 mode traps. ; ;-- ENTER_TRAPV86 macro DRENTER,V86ENTER sub esp, TsErrCode mov word ptr [esp].TsErrCode + 2, 0 mov [esp].TsEbx, ebx mov [esp].TsEax, eax mov [esp].TsEbp, ebp mov [esp].TsEsi, esi mov [esp].TsEdi, edi mov ebx, KGDT_R0_PCR mov eax, KGDT_R3_DATA OR RPL_MASK mov [esp].TsEcx, ecx mov [esp].TsEdx, edx if DBG mov [esp].TsPreviousPreviousMode, -1 mov [esp]+TsDbgArgMark, 0BADB0D00h endif mov fs, bx mov ds, ax mov es, ax mov ebp, esp cld ; CHECKIT_SUDEEP ; do we really need it test byte ptr PCR[PcDebugActive], -1 jnz Dr_&DRENTER Dr_&V86ENTER: endm ; ; Taken from ntos\vdm\i386\vdmtb.inc ; FIXED_NTVDMSTATE_LINEAR_PC_AT equ 0714H FIXED_NTVDMSTATE_LINEAR_PC_98 equ 0614H MACHINE_TYPE_MASK equ 0ff00H VDM_VIRTUAL_INTERRUPTS equ 0200H ;++ ; ; EXIT_TRAPV86 ; ; Macro Description: ; ; if UserApc is pending deliver it ; if User Context is v86 mode ; Exit from kernel (does not return) ; else ; return (expected to execute EXIT_ALL) ;-- EXIT_TRAPV86 macro local w, x, y, z z: mov ebx, PCR[PcPrcbData+PbCurrentThread] mov byte ptr [ebx]+ThAlerted, 0 cmp byte ptr [ebx]+ThApcState.AsUserApcPending, 0 jne short w ; ; Kernel exit to V86 mode ; add esp,TsEdx pop edx pop ecx pop eax test byte ptr PCR[PcDebugActive], -1 jnz short x y: add esp,12 ; unused fields pop edi pop esi pop ebx pop ebp add esp,4 ; clear error code iretd x: mov esi,[ebp]+TsDr0 mov edi,[ebp]+TsDr1 mov ebx,[ebp]+TsDr2 mov dr0,esi mov dr1,edi mov dr2,ebx mov esi,[ebp]+TsDr3 mov edi,[ebp]+TsDr6 mov ebx,[ebp]+TsDr7 mov dr3,esi mov dr6,edi mov dr7,ebx jmp short y w: ; ; Dispatch user mode APC ; The APC routine runs with interrupts on and at APC level ; mov ecx, APC_LEVEL fstCall KfRaiseIrql push eax ; Save OldIrql sti stdCall _KiDeliverApc, <1, 0, ebp> ; ebp - Trap frame ; 0 - Null exception frame ; 1 - Previous mode pop ecx ; (TOS) = OldIrql fstCall KfLowerIrql cli ; ; UserApc may have changed to vdm Monitor context (user flat 32) ; If it has cannot use the v86 only kernel exit ; test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK jnz short z ; Exit to do EXIT_ALL endm ;++ ; ; KERNEL ICECAP PROBE MACROS ; ; Macro Description: ; ; Used to wrap selected calls in .asm routines with the same ; probe calls inserted by the C compiler when /fastcap is used. ; The X-suffix versions of the probe calls are used only in ; KiSystemService and log additional information such as ; Pid, Tid, image file name, etc. ; ; Arguments: ; ; Current Function ; Called Function ; ;-- IFDEF _CAPKERN extrn __CAP_Start_Profiling@8:PROC extrn __CAP_End_Profiling@4:PROC CAPSTART macro ArgList stdCall __CAP_Start_Profiling, endm CAPEND macro ArgList stdCall __CAP_End_Profiling, endm CAPSTARTX macro ArgList push eax stdCall __CAP_ThreadID pop eax stdCall __CAP_Start_Profiling, endm CAPENDX macro ArgList stdCall __CAP_End_Profiling, push eax stdCall __CAP_SetCPU pop eax endm ELSE CAPSTART macro ArgList endm CAPEND macro ArgList endm CAPSTARTX macro ArgList endm CAPENDX macro ArgList endm ENDIF ;++ ; ; PERF_GET_TIMESTAMP ; ; Macro Description: ; ; ; Return a time stamp that for event tracing in EDX:EAX ; ; NOTE: This may trash ECX ; ; In retail, get the clock value from WmiGetCpuClock. Else if using ; reserved memory for logging, get cycle counter. ; ;-- PERF_GET_TIMESTAMP macro extrn _WmiGetCpuClock:DWORD call [_WmiGetCpuClock] endm