reactos/ntoskrnl/include/internal/i386/asmmacro.S
Thomas Faber 4f49a9c792
[NTOS:KE] Save ExceptionList in the assembly version of KiEnterTrap. CORE-15723
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.
2020-06-06 08:44:30 +02:00

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