xbox-kernel/private/ntos/ke/i386/kimacro.inc
2020-09-30 17:17:25 +02:00

754 lines
18 KiB
PHP

;++
;
; 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_
ifdef DEVKIT
EXTRNP _KdPollBreakIn,0
EXTRNP _DbgBreakPointWithStatus,1
stdCall _KdPollBreakIn
or al,al
jz short c_
stdCall _DbgBreakPointWithStatus,<DBG_STATUS_CONTROL_C>
c_:
endif
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
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_SYSCALL
;
; 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:
; None.
;
; 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
.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 ;
;
; 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
;
; Allocate the remainder of trap frame,
;
sub esp,TsExceptionList
mov ebp,esp ; set trap frame address
cld ; make sure direction is forward
SET_DEBUG_DATA ; Note this destroys edi
sti ; enable interrupts
else
%out ENTER_SYSCAL outside of kernel
.err
endif
endm
;++
;
; ENTER_INTERRUPT PassParm
;
; 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:
; PassParm -
;
; 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 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 <PassParm>
push esp ; put pad dword for error on stack
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
mov ebx, PCR[PcExceptionList] ;Save, set ExceptionList
mov PCR[PcExceptionList],EXCEPTION_CHAIN_END
mov [esp]+TsExceptionList, ebx
cld
SET_DEBUG_DATA
endm
;++
;
; ENTER_TRAP
;
; 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:
; None.
;
; 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
local b
.FPO ( FPO_LOCALS, FPO_PARAMS, FPO_PROLOG, FPO_REGS, FPO_USE_EBP, FPO_TRAPFRAME )
;
; Fill in parts of frame we care about
;
mov word ptr [esp+2], 0 ; Clear upper word of ErrorCode
push ebp ; Save the non-volatile registers
push ebx
push esi
push edi
mov ebx, PCR[PcExceptionList] ;Save ExceptionList
push ebx
push eax ; Save the volatile registers
push ecx
push edx
;
; Skip allocate reset of trap frame.
;
sub esp,TsEdx
mov ebp,esp
cld
SET_DEBUG_DATA
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 Db_NotATrapFrame, Db_A, Db_NotValidEntry
local DbgHalt, DbgNoHalt, DbgDoneHalt
;
; Sanity check some values and setup globals for macro
;
?adjesp = TsEdx
?RestoreAll = 1
ifnb <NoRestoreSegs>
?RestoreAll = 0
endif
ifnb <NoRestoreVolatiles>
if ?RestoreAll eq 1
%out "EXIT_ALL NoRestoreVolatiles requires NoRestoreSegs"
.err
endif
?adjesp = ?adjesp + 12
endif
ifb <NoPreviousMode>
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 PCR[PcExceptionList], edx ; Restore ExceptionList
test word ptr [esp]+TsSegCs,FRAME_EDITED
jz b ; Edited frame pop out.
ifb <NoRestoreVolatiles>
ifb <NoRestoreSegs> ; must restore eax before any
mov eax, [esp].TsEax ; selectors! (see trap0e handler)
endif
endif
ifb <NoRestoreVolatiles>
mov edx, [ebp]+TsEdx ; Restore volitales
mov ecx, [ebp]+TsEcx
ifb <NoRestoreSegs>
else
mov eax, [ebp]+TsEax
endif
endif ; NoRestoreVolatiles
lea esp, [ebp]+TsEdi ; Skip PreMode, ExceptList and fs
ifdef DEVKIT
; check for requested halt
mov edi, PCR[PcPrcbData+PbDebugHaltThread]
test edi, edi
jnz short DbgHalt
DbgNoHalt:
; EBP is toast here, so we'd better not need it
endif
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.
;
add esp, 4 ; remove error code from trap frame
iretd ; return
ifdef DEVKIT
DbgHalt:
; There is a requested halt, so let's see if now's the time to halt
push eax
mov eax, PCR[PcIrql]
push ecx
push edx
test eax, eax ; only break if irql = 0
jne DbgDoneHalt
call edi
test al, al
je DbgDoneHalt
sti
mov ebp, [esp+24] ; load up the saved EBP so the debugger
int 3h ; shows a reasonable trace
cli
DbgDoneHalt:
pop edx
pop ecx
pop eax
jmp DbgNoHalt
endif
if DBG
Db_NotATrapFrame:
add [esp]+TsDbgArgMark,0BADB0D00h ; put back the orig value
Db_NotValidEntry:
int 3
jmp Db_A
endif
;
; 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 <NoRestoreVolatiles>
mov eax,[esp].TsEax
endif
; add esp,TsSegGs
;
;ifb <NoRestoreSegs>
; pop gs
; pop es
; pop ds
;else
; add esp,12
;endif
ifb <NoRestoreVolatiles>
mov edx, [esp]+TsEdx
mov ecx, [esp]+TsEcx
endif
;ifnb <NoPreviousMode>
; 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 ...) = machine_state frame
; (ebp)-> machine state frame (trap frame)
;
;--
INTERRUPT_EXIT macro
local a
if DBG ; save current eip for
a: mov esi, offset a ; debugging bad trap frames
endif
cli
pop ecx
jmp @HalEndSystemInterrupt@4
endm
;++
;
; LEVEL_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:
;
; (eax)-> bus interrupt level
; (TOS) = previous irql
; (TOS+4 ...) = machine_state frame
; (ebp)-> machine state frame (trap frame)
;
;--
LEVEL_INTERRUPT_EXIT macro
local a
if DBG ; save current eip for
a: mov esi, offset a ; debugging bad trap frames
endif
cli
pop ecx
jmp @HalEndSystemLevelInterrupt@4
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
jmp Kei386EoiHelper@0
endm