reactos/ntoskrnl/include/internal/i386/asmmacro.S
2021-03-02 14:37:39 +01:00

463 lines
12 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
/* Tell GDB what just happened */
CFI_DEF_CFA_REGISTER ebp
CFI_ADJUST_CFA_OFFSET FrameSize
CFI_REL_OFFSET ss, KTRAP_FRAME_SS
CFI_REL_OFFSET gs, KTRAP_FRAME_GS
CFI_REL_OFFSET fs, KTRAP_FRAME_FS
CFI_REL_OFFSET es, KTRAP_FRAME_ES
CFI_REL_OFFSET ds, KTRAP_FRAME_DS
CFI_REL_OFFSET cs, KTRAP_FRAME_CS
CFI_REL_OFFSET edi, KTRAP_FRAME_EDI
CFI_REL_OFFSET esi, KTRAP_FRAME_ESI
CFI_REL_OFFSET ebx, KTRAP_FRAME_EBX
CFI_REL_OFFSET ebp, KTRAP_FRAME_EBP
CFI_REL_OFFSET eip, KTRAP_FRAME_EIP
CFI_REL_OFFSET esp, KTRAP_FRAME_ESP
if (NOT (Flags AND KI_NONVOLATILES_ONLY))
CFI_REL_OFFSET eax, KTRAP_FRAME_EAX
CFI_REL_OFFSET ecx, KTRAP_FRAME_ECX
CFI_REL_OFFSET edx, KTRAP_FRAME_EDX
endif
#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