mirror of
https://github.com/reactos/reactos.git
synced 2024-09-03 17:19:20 +00:00
![Thomas Faber](/assets/img/avatar_default.png)
If SEH is used in a C trap handler, the exception frame will be registered before the call to KiEnterTrap, which means we save the wrong trap handler. We'll therefore also restore this wrong frame for the excepting code, resulting in a stale SEH chain. We avoid this problem by saving the handler in the assembly trap entry code instead of from C. While SEH in a C trap handler should now theoretically be safe, we still forbid it through asserts in the C KiEnterTrap variants to make any potential future problems more obvious. Should this functionality be needed at some point and deemed safe, these asserts can then be removed.
440 lines
11 KiB
ArmAsm
440 lines
11 KiB
ArmAsm
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Kernel
|
|
* FILE: ntoskrnl/include/internal/i386/asmmacro.S
|
|
* PURPOSE: Assembly Macros for Spinlocks and common Trap Code
|
|
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
|
|
* Timo Kreuzer (timo.kreuzer@reactos.org)
|
|
*/
|
|
|
|
// Arguments for idt
|
|
#define INT_32_DPL0 HEX(08E00)
|
|
#define INT_32_DPL3 HEX(0EE00)
|
|
|
|
//
|
|
// These macros are inlined equivalents of KiAcquire/ReleaseSpinlock, that is,
|
|
// they will not be compiled into non-SMP builds. Usage is as follows:
|
|
//
|
|
// .BeginYourFunction
|
|
// mov reg, lockaddr
|
|
// ACQUIRE_SPINLOCK(reg, .spin)
|
|
// <thread-safe code here>
|
|
// RELEASE_SPINLOCK(reg)
|
|
// <misc code here>
|
|
// retn
|
|
// #IFDEF CONFIG_SMP
|
|
// .spin
|
|
// <any necessary steps to be able to jump back safely>
|
|
// SPIN_ON_LOCK(reg, .BeginYourFunction)
|
|
// #ENDIF
|
|
//
|
|
#ifdef CONFIG_SMP
|
|
#define LOCK lock
|
|
#define ACQUIRE_SPINLOCK(x, y) \
|
|
lock bts dword ptr [x], 0; \
|
|
jb y
|
|
#define RELEASE_SPINLOCK(x) mov byte ptr [x], 0
|
|
#define SPIN_ON_LOCK(x, y) \
|
|
1: \
|
|
test dword ptr [x], 1; \
|
|
jz y; \
|
|
pause; \
|
|
jmp 1b
|
|
#else
|
|
#define LOCK
|
|
#define ACQUIRE_SPINLOCK(x, y)
|
|
#define RELEASE_SPINLOCK(x)
|
|
#endif
|
|
|
|
//
|
|
// @name IDT
|
|
//
|
|
// This macro creates an IDT entry for the given handler
|
|
//
|
|
// @param Handler
|
|
// Pointer to the IDT handler
|
|
//
|
|
// @param Bits
|
|
// Descriptor Bits to associate
|
|
//
|
|
// @remark None.
|
|
//
|
|
MACRO(idt, Handler, Bits)
|
|
.long VAL(Handler)
|
|
.short VAL(Bits)
|
|
.short KGDT_R0_CODE
|
|
ENDM
|
|
|
|
|
|
#define KI_PUSH_FAKE_ERROR_CODE HEX(0001)
|
|
#define KI_UNUSED HEX(0002)
|
|
#define KI_NONVOLATILES_ONLY HEX(0004)
|
|
#define KI_FAST_SYSTEM_CALL HEX(0008)
|
|
#define KI_SOFTWARE_TRAP HEX(0010)
|
|
#define KI_HARDWARE_INT HEX(0020)
|
|
#define KI_DONT_SAVE_SEGS HEX(0100)
|
|
|
|
MACRO(KiEnterTrap, Flags)
|
|
LOCAL not_v86_trap
|
|
LOCAL set_sane_segs
|
|
|
|
/* Check what kind of trap frame this trap requires */
|
|
if (Flags AND KI_FAST_SYSTEM_CALL)
|
|
|
|
/* SYSENTER requires us to build a complete ring transition trap frame */
|
|
FrameSize = KTRAP_FRAME_EIP
|
|
|
|
/* Fixup fs. cx is free to clobber */
|
|
mov cx, KGDT_R0_PCR
|
|
mov fs, cx
|
|
|
|
/* Get pointer to the TSS */
|
|
mov ecx, fs:[KPCR_TSS]
|
|
|
|
/* Get a stack pointer */
|
|
mov esp, [ecx + KTSS_ESP0]
|
|
|
|
/* Set up a fake hardware trap frame */
|
|
push KGDT_R3_DATA or RPL_MASK
|
|
push edx
|
|
pushfd
|
|
push KGDT_R3_CODE or RPL_MASK
|
|
push dword ptr ds:[KUSER_SHARED_SYSCALL_RET]
|
|
|
|
elseif (Flags AND KI_SOFTWARE_TRAP)
|
|
|
|
/* Software traps need a complete non-ring transition trap frame */
|
|
FrameSize = KTRAP_FRAME_ESP
|
|
|
|
/* Software traps need to get their EIP from the caller's frame */
|
|
pop eax
|
|
|
|
elseif (Flags AND KI_PUSH_FAKE_ERROR_CODE)
|
|
|
|
/* If the trap doesn't have an error code, we'll make space for it */
|
|
FrameSize = KTRAP_FRAME_EIP
|
|
|
|
else
|
|
|
|
/* The trap already has an error code, so just make space for the rest */
|
|
FrameSize = KTRAP_FRAME_ERROR_CODE
|
|
|
|
endif
|
|
|
|
/* Make space for this frame */
|
|
sub esp, FrameSize
|
|
|
|
/* Save nonvolatile registers */
|
|
mov [esp + KTRAP_FRAME_EBP], ebp
|
|
mov [esp + KTRAP_FRAME_EBX], ebx
|
|
mov [esp + KTRAP_FRAME_ESI], esi
|
|
mov [esp + KTRAP_FRAME_EDI], edi
|
|
|
|
/* Save eax for system calls, for use by the C handler */
|
|
mov [esp + KTRAP_FRAME_EAX], eax
|
|
|
|
/* Does the caller want nonvolatiles only? */
|
|
if (NOT (Flags AND KI_NONVOLATILES_ONLY))
|
|
/* Otherwise, save the volatiles as well */
|
|
mov [esp + KTRAP_FRAME_ECX], ecx
|
|
mov [esp + KTRAP_FRAME_EDX], edx
|
|
endif
|
|
|
|
/* Save segment registers? */
|
|
if (Flags AND KI_DONT_SAVE_SEGS)
|
|
|
|
/* Initialize TrapFrame segment registers with sane values */
|
|
mov eax, KGDT_R3_DATA OR RPL_MASK
|
|
mov ecx, fs
|
|
mov [esp + KTRAP_FRAME_DS], eax
|
|
mov [esp + KTRAP_FRAME_ES], eax
|
|
mov [esp + KTRAP_FRAME_FS], ecx
|
|
mov dword ptr [esp + KTRAP_FRAME_GS], 0
|
|
|
|
else
|
|
|
|
/* Check for V86 mode */
|
|
test byte ptr [esp + KTRAP_FRAME_EFLAGS + 2], (EFLAGS_V86_MASK / HEX(10000))
|
|
jz not_v86_trap
|
|
|
|
/* Restore V8086 segments into Protected Mode segments */
|
|
mov eax, [esp + KTRAP_FRAME_V86_DS]
|
|
mov ecx, [esp + KTRAP_FRAME_V86_ES]
|
|
mov [esp + KTRAP_FRAME_DS], eax
|
|
mov [esp + KTRAP_FRAME_ES], ecx
|
|
mov eax, [esp + KTRAP_FRAME_V86_FS]
|
|
mov ecx, [esp + KTRAP_FRAME_V86_GS]
|
|
mov [esp + KTRAP_FRAME_FS], eax
|
|
mov [esp + KTRAP_FRAME_GS], ecx
|
|
jmp set_sane_segs
|
|
|
|
not_v86_trap:
|
|
|
|
/* Save segment selectors */
|
|
mov eax, ds
|
|
mov ecx, es
|
|
mov [esp + KTRAP_FRAME_DS], eax
|
|
mov [esp + KTRAP_FRAME_ES], ecx
|
|
mov eax, fs
|
|
mov ecx, gs
|
|
mov [esp + KTRAP_FRAME_FS], eax
|
|
mov [esp + KTRAP_FRAME_GS], ecx
|
|
|
|
endif
|
|
|
|
set_sane_segs:
|
|
/* Load correct data segments */
|
|
mov ax, KGDT_R3_DATA OR RPL_MASK
|
|
mov ds, ax
|
|
mov es, ax
|
|
|
|
/* Fast system calls have fs already fixed */
|
|
if (Flags AND KI_FAST_SYSTEM_CALL)
|
|
|
|
/* Enable interrupts and set a sane FS value */
|
|
or dword ptr [esp + KTRAP_FRAME_EFLAGS], EFLAGS_INTERRUPT_MASK
|
|
mov dword ptr [esp + KTRAP_FRAME_FS], KGDT_R3_TEB or RPL_MASK
|
|
|
|
/* Set sane active EFLAGS */
|
|
push 2
|
|
popfd
|
|
|
|
/* Point edx to the usermode parameters */
|
|
add edx, 8
|
|
else
|
|
/* Otherwise fix fs now */
|
|
mov ax, KGDT_R0_PCR
|
|
mov fs, ax
|
|
endif
|
|
|
|
/* Save ExceptionList, since the C handler might use SEH */
|
|
mov eax, fs:[KPCR_EXCEPTION_LIST]
|
|
mov [esp + KTRAP_FRAME_EXCEPTION_LIST], eax
|
|
|
|
#if DBG
|
|
/* Keep the frame chain intact */
|
|
mov eax, [esp + KTRAP_FRAME_EIP]
|
|
mov [esp + KTRAP_FRAME_DEBUGEIP], eax
|
|
mov [esp + KTRAP_FRAME_DEBUGEBP], ebp
|
|
mov ebp, esp
|
|
#endif
|
|
|
|
/* Set parameter 1 (ECX) to point to the frame */
|
|
mov ecx, esp
|
|
|
|
/* Clear direction flag */
|
|
cld
|
|
|
|
ENDM
|
|
|
|
MACRO(KiCallHandler, Handler)
|
|
#if DBG
|
|
/* Use a call to get the return address for back traces */
|
|
call Handler
|
|
#else
|
|
/* Use the faster jmp */
|
|
jmp Handler
|
|
#endif
|
|
nop
|
|
ENDM
|
|
|
|
MACRO(TRAP_ENTRY, Trap, Flags)
|
|
EXTERN @&Trap&Handler@4 :PROC
|
|
PUBLIC _&Trap
|
|
.PROC _&Trap
|
|
/* Generate proper debugging symbols */
|
|
FPO 0, 0, 0, 0, 1, FRAME_TRAP
|
|
|
|
/* Common code to create the trap frame */
|
|
KiEnterTrap Flags
|
|
|
|
/* Call the C handler */
|
|
KiCallHandler @&Trap&Handler@4
|
|
.ENDP
|
|
ENDM
|
|
|
|
#define KI_NMI HEX(0001)
|
|
|
|
MACRO(TASK_ENTRY, Trap, Flags)
|
|
EXTERN _&Trap&Handler :PROC
|
|
PUBLIC _&Trap
|
|
.PROC _&Trap
|
|
/* Generate proper debugging symbols */
|
|
FPO 0, 0, 0, 0, 0, FRAME_TSS
|
|
|
|
/* Call the C handler */
|
|
call _&Trap&Handler
|
|
|
|
if (Flags AND KI_NMI)
|
|
/* Return from NMI: return with iret and handle NMI recursion */
|
|
iretd
|
|
jmp _&Trap
|
|
endif
|
|
|
|
.ENDP
|
|
ENDM
|
|
|
|
#define KI_RESTORE_EAX HEX(0001)
|
|
#define KI_RESTORE_ECX_EDX HEX(0002)
|
|
#define KI_RESTORE_FS HEX(0004)
|
|
#define KI_RESTORE_SEGMENTS HEX(0008)
|
|
#define KI_RESTORE_EFLAGS HEX(0010)
|
|
#define KI_EXIT_SYSCALL HEX(0020)
|
|
#define KI_EXIT_JMP HEX(0040)
|
|
#define KI_EXIT_RET HEX(0080)
|
|
#define KI_EXIT_IRET HEX(0100)
|
|
#define KI_EDITED_FRAME HEX(0200)
|
|
#define KI_EXIT_RET8 HEX(0400)
|
|
#define KI_RESTORE_VOLATILES (KI_RESTORE_EAX OR KI_RESTORE_ECX_EDX)
|
|
|
|
MACRO(KiTrapExitStub, Name, Flags)
|
|
LOCAL ret8_instruction
|
|
LOCAL not_nested_int
|
|
|
|
PUBLIC @&Name&@4
|
|
@&Name&@4:
|
|
|
|
if (Flags AND KI_EXIT_RET8) OR (Flags AND KI_EXIT_IRET)
|
|
|
|
/* This is the IRET frame */
|
|
OffsetEsp = KTRAP_FRAME_EIP
|
|
|
|
elseif (Flags AND KI_RESTORE_EFLAGS)
|
|
|
|
/* We will pop EFlags off the stack */
|
|
OffsetEsp = KTRAP_FRAME_EFLAGS
|
|
|
|
else
|
|
|
|
OffsetEsp = 0
|
|
|
|
endif
|
|
|
|
if (Flags AND KI_EDITED_FRAME)
|
|
|
|
/* Load the requested ESP */
|
|
mov esp, [ecx + KTRAP_FRAME_TEMPESP]
|
|
|
|
/* Put return address on the new stack */
|
|
push [ecx + KTRAP_FRAME_EIP]
|
|
|
|
/* Put EFLAGS on the new stack */
|
|
push [ecx + KTRAP_FRAME_EFLAGS]
|
|
|
|
else
|
|
|
|
/* Point esp to an appropriate member of the frame */
|
|
lea esp, [ecx + OffsetEsp]
|
|
|
|
endif
|
|
|
|
/* Restore non volatiles */
|
|
mov ebx, [ecx + KTRAP_FRAME_EBX]
|
|
mov esi, [ecx + KTRAP_FRAME_ESI]
|
|
mov edi, [ecx + KTRAP_FRAME_EDI]
|
|
mov ebp, [ecx + KTRAP_FRAME_EBP]
|
|
|
|
if (Flags AND KI_RESTORE_EAX)
|
|
|
|
/* Restore eax */
|
|
mov eax, [ecx + KTRAP_FRAME_EAX]
|
|
|
|
endif
|
|
|
|
if (Flags AND KI_RESTORE_ECX_EDX)
|
|
|
|
/* Restore volatiles */
|
|
mov edx, [ecx + KTRAP_FRAME_EDX]
|
|
mov ecx, [ecx + KTRAP_FRAME_ECX]
|
|
|
|
elseif (Flags AND KI_EXIT_JMP)
|
|
|
|
/* Load return address into edx */
|
|
mov edx, [esp - OffsetEsp + KTRAP_FRAME_EIP]
|
|
|
|
elseif (Flags AND KI_EXIT_SYSCALL)
|
|
|
|
/* Set sysexit parameters */
|
|
mov edx, [esp - OffsetEsp + KTRAP_FRAME_EIP]
|
|
mov ecx, [esp - OffsetEsp + KTRAP_FRAME_ESP]
|
|
|
|
/* Keep interrupts disabled until the sti / sysexit */
|
|
and byte ptr [esp - OffsetEsp + KTRAP_FRAME_EFLAGS + 1], NOT (EFLAGS_INTERRUPT_MASK / HEX(100))
|
|
|
|
endif
|
|
|
|
if (Flags AND KI_RESTORE_SEGMENTS)
|
|
|
|
/* Restore segments for user mode */
|
|
mov ds, [esp - OffsetEsp + KTRAP_FRAME_DS]
|
|
mov es, [esp - OffsetEsp + KTRAP_FRAME_ES]
|
|
mov gs, [esp - OffsetEsp + KTRAP_FRAME_GS]
|
|
|
|
endif
|
|
|
|
if ((Flags AND KI_RESTORE_FS) OR (Flags AND KI_RESTORE_SEGMENTS))
|
|
|
|
/* Restore user mode FS */
|
|
mov fs, [esp - OffsetEsp + KTRAP_FRAME_FS]
|
|
|
|
endif
|
|
|
|
if (Flags AND KI_RESTORE_EFLAGS)
|
|
|
|
if (Flags AND KI_EXIT_RET8)
|
|
|
|
/* Check if we return from a nested interrupt, i.e. an interrupt
|
|
that occurred in the ret8 return path between restoring
|
|
EFLAGS and returning with the ret instruction. */
|
|
cmp dword ptr [esp], offset ret8_instruction
|
|
jne not_nested_int
|
|
|
|
/* This is a nested interrupt, so we have 2 IRET frames.
|
|
Skip the first, and go directly to the previous return address.
|
|
Do not pass Go. Do not collect $200 */
|
|
add esp, 12
|
|
|
|
not_nested_int:
|
|
/* We are at the IRET frame, so push EFLAGS first */
|
|
push dword ptr [esp + 8]
|
|
|
|
endif
|
|
|
|
/* Restore EFLAGS */
|
|
popfd
|
|
|
|
endif
|
|
|
|
if (Flags AND KI_EXIT_SYSCALL)
|
|
|
|
/* Enable interrupts and return to user mode.
|
|
Both must follow directly after another to be "atomic". */
|
|
sti
|
|
sysexit
|
|
|
|
elseif (Flags AND KI_EXIT_IRET)
|
|
|
|
/* Return with iret */
|
|
iretd
|
|
|
|
elseif (Flags AND KI_EXIT_JMP)
|
|
|
|
/* Return to kernel mode with a jmp */
|
|
jmp edx
|
|
|
|
elseif (Flags AND KI_EXIT_RET8)
|
|
|
|
/* Return to kernel mode with a ret 8 */
|
|
ret8_instruction:
|
|
ret 8
|
|
|
|
elseif (Flags AND KI_EXIT_RET)
|
|
|
|
/* Return to kernel mode with a ret */
|
|
ret
|
|
|
|
endif
|
|
|
|
ENDM
|
|
|