trap.s
来自「一个类似windows」· S 代码 · 共 1,043 行 · 第 1/2 页
S
1,043 行
/*
* FILE: ntoskrnl/ke/i386/trap.S
* COPYRIGHT: See COPYING in the top level directory
* PURPOSE: System Traps, Entrypoints and Exitpoints
* PROGRAMMER: Alex Ionescu (alex@relsoft.net)
* NOTE: See asmmacro.S for the shared entry/exit code.
*/
/* INCLUDES ******************************************************************/
#include <asm.h>
#include <internal/i386/asmmacro.S>
.intel_syntax noprefix
/*
* FIXMEs:
* - Figure out why ES/DS gets messed up in VMWare, when doing KiServiceExit only,
* and only when called from user-mode, and returning to user-mode.
* - Figure out what the DEBUGEIP hack is for and how it can be moved away.
* - Add DR macro/save and VM macro/save.
* - Implement KiCallbackReturn, KiGetTickCount, KiRaiseAssertion.
*/
/* GLOBALS ******************************************************************/
.globl _KiIdt
_KiIdt:
/* This is the Software Interrupt Table that we handle in this file: */
idt _KiTrap0, INT_32_DPL0 /* INT 00: Divide Error (#DE) */
idt _KiTrap1, INT_32_DPL0 /* INT 01: Debug Exception (#DB) */
idt _KiTrap2, INT_32_DPL0 /* INT 02: NMI Interrupt */
idt _KiTrap3, INT_32_DPL3 /* INT 03: Breakpoint Exception (#BP) */
idt _KiTrap4, INT_32_DPL3 /* INT 04: Overflow Exception (#OF) */
idt _KiTrap5, INT_32_DPL0 /* INT 05: BOUND Range Exceeded (#BR) */
idt _KiTrap6, INT_32_DPL0 /* INT 06: Invalid Opcode Code (#UD) */
idt _KiTrap7, INT_32_DPL0 /* INT 07: Device Not Available (#NM) */
idt _KiTrap8, INT_32_DPL0 /* INT 08: Double Fault Exception (#DF) */
idt _KiTrap9, INT_32_DPL0 /* INT 09: RESERVED */
idt _KiTrap10, INT_32_DPL0 /* INT 0A: Invalid TSS Exception (#TS) */
idt _KiTrap11, INT_32_DPL0 /* INT 0B: Segment Not Present (#NP) */
idt _KiTrap12, INT_32_DPL0 /* INT 0C: Stack Fault Exception (#SS) */
idt _KiTrap13, INT_32_DPL0 /* INT 0D: General Protection (#GP) */
idt _KiTrap14, INT_32_DPL0 /* INT 0E: Page-Fault Exception (#PF) */
idt _KiTrap15, INT_32_DPL0 /* INT 0F: RESERVED [FIXME: HBIRR HACK] */
idt _KiTrap16, INT_32_DPL0 /* INT 10: x87 FPU Error (#MF) */
idt _KiTrap17, INT_32_DPL0 /* INT 11: Align Check Exception (#AC) */
idt _KiTrap0F, INT_32_DPL0 /* INT 12: Machine Check Exception (#MC)*/
idt _KiTrap0F, INT_32_DPL0 /* INT 13: SIMD FPU Exception (#XF) */
.rept 22
idt _KiTrap0F, INT_32_DPL0 /* INT 14-29: UNDEFINED INTERRUPTS */
.endr
idt _KiGetTickCount, INT_32_DPL3 /* INT 2A: Get Tick Count Handler */
idt _KiCallbackReturn, INT_32_DPL3 /* INT 2B: User-Mode Callback Return */
idt _KiRaiseAssertion, INT_32_DPL3 /* INT 2C: Debug Assertion Handler */
idt _KiDebugService, INT_32_DPL3 /* INT 2D: Debug Service Handler */
idt _KiSystemService, INT_32_DPL3 /* INT 2E: System Call Service Handler */
idt _KiTrap0F, INT_32_DPL0 /* INT 2F: RESERVED */
.rept 220
idt _KiTrap0F, INT_32_DPL0 /* INT 30-FF: UNDEFINED INTERRUPTS */
.endr
/* System call entrypoints: */
.globl _KiFastCallEntry
.globl _KiSystemService
/* And special system-defined software traps: */
.globl _NtRaiseException@12
.globl _NtContinue@8
/* We implement the following trap exit points: */
.globl _KiServiceExit /* Exit from syscall */
.globl _KiServiceExit2 /* Exit from syscall with complete frame*/
.globl _Kei386EoiHelper@0 /* Exit from interrupt or H/W trap */
.globl _KiIdtDescriptor
_KiIdtDescriptor:
.short 0x800
.long _KiIdt
/* FUNCTIONS ****************************************************************/
_KiGetTickCount:
_KiCallbackReturn:
_KiRaiseAssertion:
int 3
.func KiSystemService
_KiSystemService:
/* Enter the shared system call prolog */
SYSCALL_PROLOG
/* Jump to the actual handler */
jmp SharedCode
.endfunc
.func KiFastCallEntry
_KiFastCallEntry:
/* Set FS to PCR */
mov ecx, KGDT_R0_PCR
mov fs, cx
/* Set DS/ES to Kernel Selector */
mov ecx, KGDT_R0_DATA
mov ds, cx
mov es, cx
/* Set the current stack to Kernel Stack */
mov ecx, [fs:KPCR_TSS]
mov esp, ss:[ecx+KTSS_ESP0]
/* Set up a fake INT Stack. */
push KGDT_R3_DATA + RPL_MASK
push edx /* Ring 3 SS:ESP */
pushf /* Ring 3 EFLAGS */
push 2 /* Ring 0 EFLAGS */
add edx, 8 /* Skip user parameter list */
popf /* Set our EFLAGS */
or dword ptr [esp], EFLAGS_INTERRUPT_MASK /* Re-enable IRQs in EFLAGS, to fake INT */
push KGDT_R3_CODE + RPL_MASK
push KUSER_SHARED_SYSCALL_RET
/* Setup the Trap Frame stack */
push 0
push ebp
push ebx
push esi
push edi
push KGDT_R3_TEB + RPL_MASK
/* Save pointer to our PCR */
mov ebx, [fs:KPCR_SELF]
/* Get a pointer to the current thread */
mov esi, [ebx+KPCR_CURRENT_THREAD]
/* Set the exception handler chain terminator */
push [ebx+KPCR_EXCEPTION_LIST]
mov dword ptr [ebx+KPCR_EXCEPTION_LIST], -1
/* Use the thread's stack */
mov ebp, [esi+KTHREAD_INITIAL_STACK]
/* Push previous mode */
push UserMode
/* Skip the other registers */
sub esp, 0x48
/* Hack: it seems that on VMWare someone damages ES/DS on exit. Investigate! */
mov dword ptr [esp+KTRAP_FRAME_DS], KGDT_R3_DATA + RPL_MASK
mov dword ptr [esp+KTRAP_FRAME_ES], KGDT_R3_DATA + RPL_MASK
/* Make space for us on the stack */
sub ebp, 0x29C
/* Write the previous mode */
mov byte ptr [esi+KTHREAD_PREVIOUS_MODE], UserMode
/* Sanity check */
cmp ebp, esp
jnz BadStack
/* Flush DR7 */
and dword ptr [ebp+KTRAP_FRAME_DR7], 0
/* Check if the thread was being debugged */
test byte ptr [esi+KTHREAD_DEBUG_ACTIVE], 0xFF
/* Set the thread's trap frame */
mov [esi+KTHREAD_TRAP_FRAME], ebp
/* Save DR registers if needed */
//jnz Dr_FastCallDrSave
/* Set the trap frame debug header */
SET_TF_DEBUG_HEADER
#ifdef DBG // FIXME: Is this for GDB? Can it be moved in the stub?
/*
* We want to know the address from where the syscall stub was called.
* If PrevMode is KernelMode, that address is stored in our own (kernel)
* stack, at location KTRAP_FRAME_ESP.
* If we're coming from UserMode, we load the usermode stack pointer
* and go back two frames (first frame is the syscall stub, second call
* is the caller of the stub).
*/
mov edi, [ebp+KTRAP_FRAME_ESP]
test byte ptr [esi+KTHREAD_PREVIOUS_MODE], 0x01
jz PrevWasKernelMode
mov edi, [edi+4]
PrevWasKernelMode:
mov [ebp+KTRAP_FRAME_DEBUGEIP], edi
#endif
/* Enable interrupts */
sti
SharedCode:
/*
* Find out which table offset to use. Converts 0x1124 into 0x10.
* The offset is related to the Table Index as such: Offset = TableIndex x 10
*/
mov edi, eax
shr edi, SERVICE_TABLE_SHIFT
and edi, SERVICE_TABLE_MASK
mov ecx, edi
/* Now add the thread's base system table to the offset */
add edi, [esi+KTHREAD_SERVICE_TABLE]
/* Get the true syscall ID and check it */
mov ebx, eax
and eax, SERVICE_NUMBER_MASK
cmp eax, [edi+SERVICE_DESCRIPTOR_LIMIT]
/* Invalid ID, try to load Win32K Table */
jnb KiBBTUnexpectedRange
#if 0 // <== Disabled for two reasons: We don't save TEB in 0x18, but KPCR.
// <== We don't have a KeGdiFlushUserBatch callback yet (needs to be
// sent through the PsInitializeWin32Callouts structure)
/* Check if this was Win32K */
cmp ecx, SERVICE_TABLE_TEST
jnz NotWin32K
/* Get the TEB */
mov ecx, [fs:KPCR_TEB]
/* Check if we should flush the User Batch */
xor ebx, ebx
or ebx, [ecx+TEB_GDI_BATCH_COUNT]
jz NoWin32K
/* Flush it */
push edx
push eax
call [_KeGdiFlushUserBatch]
pop eax
pop edx
#endif
NotWin32K:
/* Increase total syscall count */
inc dword ptr fs:[KPCR_SYSTEM_CALLS]
#ifdef DBG
/* Increase per-syscall count */
mov ecx, [edi+SERVICE_DESCRIPTOR_COUNT]
jecxz NoCountTable
inc dword ptr [ecx+eax*4]
#endif
/* Users's current stack frame pointer is source */
NoCountTable:
mov esi, edx
/* Allocate room for argument list from kernel stack */
mov ebx, [edi+SERVICE_DESCRIPTOR_NUMBER]
xor ecx, ecx
mov cl, [eax+ebx]
/* Get pointer to function */
mov edi, [edi+SERVICE_DESCRIPTOR_BASE]
mov ebx, [edi+eax*4]
/* Allocate space on our stack */
sub esp, ecx
/* Set the size of the arguments and the destination */
shr ecx, 2
mov edi, esp
/* Make sure we're within the User Probe Address */
cmp esi, _MmUserProbeAddress
jnb AccessViolation
CopyParams:
/* Copy the parameters */
rep movsd
#ifdef DBG
/*
* The following lines are for the benefit of GDB. It will see the return
* address of the "call ebx" below, find the last label before it and
* thinks that that's the start of the function. It will then check to see
* if it starts with a standard function prolog (push ebp, mov ebp,esp1).
* When that standard function prolog is not found, it will stop the
* stack backtrace. Since we do want to backtrace into usermode, let's
* make GDB happy and create a standard prolog.
*/
KiSystemService:
push ebp
mov ebp,esp
pop ebp
#endif
/* Do the System Call */
call ebx
AfterSysCall:
#ifdef DBG
/* Make sure the user-mode call didn't return at elevated IRQL */
test byte ptr [ebp+KTRAP_FRAME_CS], MODE_MASK
jz SkipCheck
mov esi, eax /* We need to save the syscall's return val */
call _KeGetCurrentIrql@0
or al, al
jnz InvalidIrql
mov eax, esi /* Restore it */
/* Get our temporary current thread pointer for sanity check */
mov ecx, fs:[KPCR_CURRENT_THREAD]
/* Make sure that we are not attached and that APCs are not disabled */
mov dl, [ecx+KTHREAD_APC_STATE_INDEX]
or dl, dl
jnz InvalidIndex
mov edx, [ecx+KTHREAD_COMBINED_APC_DISABLE]
or edx, edx
jnz InvalidIndex
#endif
SkipCheck:
/* Deallocate the kernel stack frame */
mov esp, ebp
KeReturnFromSystemCall:
/* Get the Current Thread */
mov ecx, [fs:KPCR_CURRENT_THREAD]
/* Restore the old trap frame pointer */
mov edx, [ebp+KTRAP_FRAME_EDX]
mov [ecx+KTHREAD_TRAP_FRAME], edx
.endfunc
.func KiServiceExit
_KiServiceExit:
/* Disable interrupts */
cli
/* Check for, and deliver, User-Mode APCs if needed */
CHECK_FOR_APC_DELIVER 1
/* Hack for VMWare: Sometimes ES/DS seem to be invalid when returning to user-mode. Investigate! */
mov es, [ebp+KTRAP_FRAME_ES]
mov ds, [ebp+KTRAP_FRAME_DS]
/* Exit and cleanup */
TRAP_EPILOG FromSystemCall, DoRestorePreviousMode, DoNotRestoreSegments, DoNotRestoreVolatiles, DoRestoreEverything
.endfunc
KiBBTUnexpectedRange:
/* If this isn't a Win32K call, fail */
cmp ecx, SERVICE_TABLE_TEST
jne InvalidCall
/* Set up Win32K Table */
push edx
push ebx
call _PsConvertToGuiThread@0
/* Check return code */
or eax, eax
/* Restore registers */
pop eax
pop edx
/* Reset trap frame address */
mov ebp, esp
mov [esi+KTHREAD_TRAP_FRAME], ebp
/* Try the Call again, if we suceeded */
jz SharedCode
/*
* The Shadow Table should have a special byte table which tells us
* whether we should return FALSE, -1 or STATUS_INVALID_SYSTEM_SERVICE.
*/
/* Get the table limit and base */
lea edx, _KeServiceDescriptorTableShadow + SERVICE_TABLE_TEST
mov ecx, [edx+SERVICE_DESCRIPTOR_LIMIT]
mov edx, [edx+SERVICE_DESCRIPTOR_BASE]
/* Get the table address and add our index into the array */
lea edx, [edx+ecx*4]
and eax, SERVICE_NUMBER_MASK
add edx, eax
/* Find out what we should return */
movsx eax, byte ptr [edx]
or eax, eax
/* Return either 0 or -1, we've set it in EAX */
jle KeReturnFromSystemCall
/* Set STATUS_INVALID_SYSTEM_SERVICE */
mov eax, STATUS_INVALID_SYSTEM_SERVICE
jmp KeReturnFromSystemCall
InvalidCall:
/* Invalid System Call */
mov eax, STATUS_INVALID_SYSTEM_SERVICE
jmp KeReturnFromSystemCall
AccessViolation:
/* Check if this came from kernel-mode */
test byte ptr [ebp+KTRAP_FRAME_CS], MODE_MASK
/* It's fine, go ahead with it */
jz CopyParams
/* Caller sent invalid parameters, fail here */
mov eax, STATUS_ACCESS_VIOLATION
jmp AfterSysCall
BadStack:
/* Restore ESP0 stack */
mov ecx, [fs:KPCR_TSS]
mov esp, ss:[ecx+KTSS_ESP0]
/* Generate V86M Stack for Trap 6 */
push 0
push 0
push 0
push 0
/* Generate interrupt stack for Trap 6 */
push KGDT_R3_DATA + RPL_MASK
push 0
push 0x20202
push KGDT_R3_CODE + RPL_MASK
push 0
jmp _KiTrap6
#ifdef DBG
InvalidIrql:
/* Save current IRQL */
push fs:[KPCR_IRQL]
/* Set us at passive */
mov dword ptr fs:[KPCR_IRQL], 0
cli
/* Bugcheck */
push 0
push 0
push eax
push ebx
push IRQL_GT_ZERO_AT_SYSTEM_SERVICE
call _KeBugCheckEx@20
InvalidIndex:
/* Get the index and APC state */
movzx eax, byte ptr [ecx+KTHREAD_APC_STATE_INDEX]
mov edx, [ecx+KTHREAD_COMBINED_APC_DISABLE]
/* Bugcheck */
push 0
push edx
push eax
push ebx
push APC_INDEX_MISMATCH
call _KeBugCheckEx@20
ret
#endif
.func KiServiceExit2
_KiServiceExit2:
/* Disable interrupts */
cli
/* Check for, and deliver, User-Mode APCs if needed */
CHECK_FOR_APC_DELIVER 0
/* Exit and cleanup */
TRAP_EPILOG NotFromSystemCall, DoRestorePreviousMode, DoRestoreSegments, DoRestoreVolatiles, DoNotRestoreEverything
.endfunc
.func Kei386EoiHelper@0
_Kei386EoiHelper@0:
/* Disable interrupts */
cli
/* Check for, and deliver, User-Mode APCs if needed */
CHECK_FOR_APC_DELIVER 0
/* Exit and cleanup */
TRAP_EPILOG NotFromSystemCall, DoNotRestorePreviousMode, DoRestoreSegments, DoRestoreVolatiles, DoNotRestoreEverything
.endfunc
V86_Exit:
/* Move to EDX position */
add esp, KTRAP_FRAME_EDX
/* Restore volatiles */
pop edx
pop ecx
pop eax
/* Move to non-volatiles */
lea esp, [ebp+KTRAP_FRAME_EDI]
pop edi
pop esi
pop ebx
pop ebp
/* Skip error code and return */
add esp, 4
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?