1/*
2 * COPYRIGHT:       See COPYING in the top level directory
3 * PROJECT:         ReactOS Kernel
4 * FILE:            ntoskrnl/include/internal/i386/asmmacro.S
5 * PURPOSE:         Assembly Macros for Spinlocks and common Trap Code
6 * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
7 *                  Timo Kreuzer (timo.kreuzer@reactos.org)
8 */
9
10// Arguments for idt
11#define INT_32_DPL0                 HEX(08E00)
12#define INT_32_DPL3                 HEX(0EE00)
13
14//
15// These macros are inlined equivalents of KiAcquire/ReleaseSpinlock, that is,
16// they will not be compiled into non-SMP builds. Usage is as follows:
17//
18// .BeginYourFunction
19//      mov reg, lockaddr
20//      ACQUIRE_SPINLOCK(reg, .spin)
21//      <thread-safe code here>
22//      RELEASE_SPINLOCK(reg)
23//      <misc code here>
24//  retn
25//  #IFDEF CONFIG_SMP
26//  .spin
27//      <any necessary steps to be able to jump back safely>
28//       SPIN_ON_LOCK(reg, .BeginYourFunction)
29//  #ENDIF
30//
31#ifdef CONFIG_SMP
32#define LOCK lock
33#define ACQUIRE_SPINLOCK(x, y) \
34    lock bts dword ptr [x], 0; \
35    jb y
36#define RELEASE_SPINLOCK(x) mov byte ptr [x], 0
37#define SPIN_ON_LOCK(x, y) \
381: \
39    test dword ptr [x], 1; \
40    jz y; \
41    pause; \
42    jmp 1b
43#else
44#define LOCK
45#define ACQUIRE_SPINLOCK(x, y)
46#define RELEASE_SPINLOCK(x)
47#endif
48
49//
50// @name IDT
51//
52// This macro creates an IDT entry for the given handler
53//
54// @param Handler
55//        Pointer to the IDT handler
56//
57// @param Bits
58//        Descriptor Bits to associate
59//
60// @remark None.
61//
62MACRO(idt, Handler, Bits)
63    .long VAL(Handler)
64    .short VAL(Bits)
65    .short KGDT_R0_CODE
66ENDM
67
68
69#define KI_PUSH_FAKE_ERROR_CODE HEX(0001)
70#define KI_UNUSED               HEX(0002)
71#define KI_NONVOLATILES_ONLY    HEX(0004)
72#define KI_FAST_SYSTEM_CALL     HEX(0008)
73#define KI_SOFTWARE_TRAP        HEX(0010)
74#define KI_HARDWARE_INT         HEX(0020)
75#define KI_DONT_SAVE_SEGS       HEX(0100)
76
77MACRO(KiEnterTrap, Flags)
78    LOCAL not_v86_trap
79    LOCAL set_sane_segs
80
81    /* Check what kind of trap frame this trap requires */
82    if (Flags AND KI_FAST_SYSTEM_CALL)
83
84        /* SYSENTER requires us to build a complete ring transition trap frame */
85        FrameSize = KTRAP_FRAME_EIP
86
87        /* Fixup fs. cx is free to clobber */
88        mov cx, KGDT_R0_PCR
89        mov fs, cx
90
91        /* Get pointer to the TSS */
92        mov ecx, fs:[KPCR_TSS]
93
94        /* Get a stack pointer */
95        mov esp, [ecx + KTSS_ESP0]
96
97        /* Set up a fake hardware trap frame */
98        push KGDT_R3_DATA or RPL_MASK
99        push edx
100        pushfd
101        push KGDT_R3_CODE or RPL_MASK
102        push dword ptr ds:[KUSER_SHARED_SYSCALL_RET]
103
104    elseif (Flags AND KI_SOFTWARE_TRAP)
105
106        /* Software traps need a complete non-ring transition trap frame */
107        FrameSize = KTRAP_FRAME_ESP
108
109        /* Software traps need to get their EIP from the caller's frame */
110        pop eax
111
112    elseif (Flags AND KI_PUSH_FAKE_ERROR_CODE)
113
114        /* If the trap doesn't have an error code, we'll make space for it */
115        FrameSize = KTRAP_FRAME_EIP
116
117    else
118
119        /* The trap already has an error code, so just make space for the rest */
120        FrameSize = KTRAP_FRAME_ERROR_CODE
121
122    endif
123
124    /* Make space for this frame */
125    sub esp, FrameSize
126
127    /* Save nonvolatile registers */
128    mov [esp + KTRAP_FRAME_EBP], ebp
129    mov [esp + KTRAP_FRAME_EBX], ebx
130    mov [esp + KTRAP_FRAME_ESI], esi
131    mov [esp + KTRAP_FRAME_EDI], edi
132
133    /* Save eax for system calls, for use by the C handler */
134    mov [esp + KTRAP_FRAME_EAX], eax
135
136    /* Does the caller want nonvolatiles only? */
137    if (NOT (Flags AND KI_NONVOLATILES_ONLY))
138        /* Otherwise, save the volatiles as well */
139        mov [esp + KTRAP_FRAME_ECX], ecx
140        mov [esp + KTRAP_FRAME_EDX], edx
141    endif
142
143    /* Save segment registers? */
144    if (Flags AND KI_DONT_SAVE_SEGS)
145
146        /* Initialize TrapFrame segment registers with sane values */
147        mov eax, KGDT_R3_DATA OR RPL_MASK
148        mov ecx, fs
149        mov [esp + KTRAP_FRAME_DS], eax
150        mov [esp + KTRAP_FRAME_ES], eax
151        mov [esp + KTRAP_FRAME_FS], ecx
152        mov dword ptr [esp + KTRAP_FRAME_GS], 0
153
154    else
155
156        /* Check for V86 mode */
157        test byte ptr [esp + KTRAP_FRAME_EFLAGS + 2], (EFLAGS_V86_MASK / HEX(10000))
158        jz not_v86_trap
159
160            /* Restore V8086 segments into Protected Mode segments */
161            mov eax, [esp + KTRAP_FRAME_V86_DS]
162            mov ecx, [esp + KTRAP_FRAME_V86_ES]
163            mov [esp + KTRAP_FRAME_DS], eax
164            mov [esp + KTRAP_FRAME_ES], ecx
165            mov eax, [esp + KTRAP_FRAME_V86_FS]
166            mov ecx, [esp + KTRAP_FRAME_V86_GS]
167            mov [esp + KTRAP_FRAME_FS], eax
168            mov [esp + KTRAP_FRAME_GS], ecx
169            jmp set_sane_segs
170
171        not_v86_trap:
172
173            /* Save segment selectors */
174            mov eax, ds
175            mov ecx, es
176            mov [esp + KTRAP_FRAME_DS], eax
177            mov [esp + KTRAP_FRAME_ES], ecx
178            mov eax, fs
179            mov ecx, gs
180            mov [esp + KTRAP_FRAME_FS], eax
181            mov [esp + KTRAP_FRAME_GS], ecx
182
183    endif
184
185set_sane_segs:
186    /* Load correct data segments */
187    mov ax, KGDT_R3_DATA OR RPL_MASK
188    mov ds, ax
189    mov es, ax
190
191    /* Fast system calls have fs already fixed */
192    if (Flags AND KI_FAST_SYSTEM_CALL)
193
194        /* Enable interrupts and set a sane FS value */
195        or dword ptr [esp + KTRAP_FRAME_EFLAGS], EFLAGS_INTERRUPT_MASK
196        mov dword ptr [esp + KTRAP_FRAME_FS], KGDT_R3_TEB or RPL_MASK
197
198        /* Set sane active EFLAGS */
199        push 2
200        popfd
201
202        /* Point edx to the usermode parameters */
203        add edx, 8
204    else
205        /* Otherwise fix fs now */
206        mov ax, KGDT_R0_PCR
207        mov fs, ax
208    endif
209
210    /* Save ExceptionList, since the C handler might use SEH */
211    mov eax, fs:[KPCR_EXCEPTION_LIST]
212    mov [esp + KTRAP_FRAME_EXCEPTION_LIST], eax
213
214#if DBG
215    /* Keep the frame chain intact */
216    mov eax, [esp + KTRAP_FRAME_EIP]
217    mov [esp + KTRAP_FRAME_DEBUGEIP], eax
218    mov [esp + KTRAP_FRAME_DEBUGEBP], ebp
219    mov ebp, esp
220
221    /* Tell GDB what just happened */
222    CFI_DEF_CFA_REGISTER ebp
223    CFI_ADJUST_CFA_OFFSET FrameSize
224    CFI_REL_OFFSET ss, KTRAP_FRAME_SS
225    CFI_REL_OFFSET gs, KTRAP_FRAME_GS
226    CFI_REL_OFFSET fs, KTRAP_FRAME_FS
227    CFI_REL_OFFSET es, KTRAP_FRAME_ES
228    CFI_REL_OFFSET ds, KTRAP_FRAME_DS
229    CFI_REL_OFFSET cs, KTRAP_FRAME_CS
230
231    CFI_REL_OFFSET edi, KTRAP_FRAME_EDI
232    CFI_REL_OFFSET esi, KTRAP_FRAME_ESI
233    CFI_REL_OFFSET ebx, KTRAP_FRAME_EBX
234    CFI_REL_OFFSET ebp, KTRAP_FRAME_EBP
235    CFI_REL_OFFSET eip, KTRAP_FRAME_EIP
236    CFI_REL_OFFSET esp, KTRAP_FRAME_ESP
237
238if (NOT (Flags AND KI_NONVOLATILES_ONLY))
239    CFI_REL_OFFSET eax, KTRAP_FRAME_EAX
240    CFI_REL_OFFSET ecx, KTRAP_FRAME_ECX
241    CFI_REL_OFFSET edx, KTRAP_FRAME_EDX
242endif
243#endif
244
245    /* Set parameter 1 (ECX) to point to the frame */
246    mov ecx, esp
247
248    /* Clear direction flag */
249    cld
250
251ENDM
252
253MACRO(KiCallHandler, Handler)
254#if DBG
255    /* Use a call to get the return address for back traces */
256    call Handler
257#else
258    /* Use the faster jmp */
259    jmp Handler
260#endif
261    nop
262ENDM
263
264MACRO(TRAP_ENTRY, Trap, Flags)
265    EXTERN @&Trap&Handler@4 :PROC
266    PUBLIC _&Trap
267    .PROC _&Trap
268        /* Generate proper debugging symbols */
269        FPO 0, 0, 0, 0, 1, FRAME_TRAP
270
271        /* Common code to create the trap frame */
272        KiEnterTrap Flags
273
274        /* Call the C handler */
275        KiCallHandler @&Trap&Handler@4
276    .ENDP
277ENDM
278
279#define KI_NMI  HEX(0001)
280
281MACRO(TASK_ENTRY, Trap, Flags)
282    EXTERN _&Trap&Handler :PROC
283    PUBLIC _&Trap
284    .PROC _&Trap
285        /* Generate proper debugging symbols */
286        FPO 0, 0, 0, 0, 0, FRAME_TSS
287
288        /* Call the C handler */
289        call _&Trap&Handler
290
291        if (Flags AND KI_NMI)
292            /* Return from NMI: return with iret and handle NMI recursion */
293            iretd
294            jmp _&Trap
295        endif
296
297    .ENDP
298ENDM
299
300#define KI_RESTORE_EAX        HEX(0001)
301#define KI_RESTORE_ECX_EDX    HEX(0002)
302#define KI_RESTORE_FS         HEX(0004)
303#define KI_RESTORE_SEGMENTS   HEX(0008)
304#define KI_RESTORE_EFLAGS     HEX(0010)
305#define KI_EXIT_SYSCALL       HEX(0020)
306#define KI_EXIT_JMP           HEX(0040)
307#define KI_EXIT_RET           HEX(0080)
308#define KI_EXIT_IRET          HEX(0100)
309#define KI_EDITED_FRAME       HEX(0200)
310#define KI_EXIT_RET8          HEX(0400)
311#define KI_RESTORE_VOLATILES  (KI_RESTORE_EAX OR KI_RESTORE_ECX_EDX)
312
313MACRO(KiTrapExitStub, Name, Flags)
314    LOCAL ret8_instruction
315    LOCAL not_nested_int
316
317PUBLIC @&Name&@4
318@&Name&@4:
319
320    if (Flags AND KI_EXIT_RET8) OR (Flags AND KI_EXIT_IRET)
321
322        /* This is the IRET frame */
323        OffsetEsp = KTRAP_FRAME_EIP
324
325    elseif (Flags AND KI_RESTORE_EFLAGS)
326
327        /* We will pop EFlags off the stack */
328        OffsetEsp = KTRAP_FRAME_EFLAGS
329
330    else
331
332        OffsetEsp = 0
333
334    endif
335
336    if (Flags AND KI_EDITED_FRAME)
337
338        /* Load the requested ESP */
339        mov esp, [ecx + KTRAP_FRAME_TEMPESP]
340
341        /* Put return address on the new stack */
342        push [ecx + KTRAP_FRAME_EIP]
343
344        /* Put EFLAGS on the new stack */
345        push [ecx + KTRAP_FRAME_EFLAGS]
346
347    else
348
349        /* Point esp to an appropriate member of the frame */
350        lea esp, [ecx + OffsetEsp]
351
352    endif
353
354    /* Restore non volatiles */
355    mov ebx, [ecx + KTRAP_FRAME_EBX]
356    mov esi, [ecx + KTRAP_FRAME_ESI]
357    mov edi, [ecx + KTRAP_FRAME_EDI]
358    mov ebp, [ecx + KTRAP_FRAME_EBP]
359
360    if (Flags AND KI_RESTORE_EAX)
361
362        /* Restore eax */
363        mov eax, [ecx + KTRAP_FRAME_EAX]
364
365    endif
366
367    if (Flags AND KI_RESTORE_ECX_EDX)
368
369        /* Restore volatiles */
370        mov edx, [ecx + KTRAP_FRAME_EDX]
371        mov ecx, [ecx + KTRAP_FRAME_ECX]
372
373    elseif (Flags AND KI_EXIT_JMP)
374
375        /* Load return address into edx */
376        mov edx, [esp - OffsetEsp + KTRAP_FRAME_EIP]
377
378    elseif (Flags AND KI_EXIT_SYSCALL)
379
380        /* Set sysexit parameters */
381        mov edx, [esp - OffsetEsp + KTRAP_FRAME_EIP]
382        mov ecx, [esp - OffsetEsp + KTRAP_FRAME_ESP]
383
384        /* Keep interrupts disabled until the sti / sysexit */
385        and byte ptr [esp - OffsetEsp + KTRAP_FRAME_EFLAGS + 1], NOT (EFLAGS_INTERRUPT_MASK / HEX(100))
386
387    endif
388
389    if (Flags AND KI_RESTORE_SEGMENTS)
390
391        /* Restore segments for user mode */
392        mov ds, [esp - OffsetEsp + KTRAP_FRAME_DS]
393        mov es, [esp - OffsetEsp + KTRAP_FRAME_ES]
394        mov gs, [esp - OffsetEsp + KTRAP_FRAME_GS]
395
396    endif
397
398    if ((Flags AND KI_RESTORE_FS) OR (Flags AND KI_RESTORE_SEGMENTS))
399
400        /* Restore user mode FS */
401        mov fs, [esp - OffsetEsp + KTRAP_FRAME_FS]
402
403    endif
404
405    if (Flags AND KI_RESTORE_EFLAGS)
406
407        if (Flags AND KI_EXIT_RET8)
408
409            /* Check if we return from a nested interrupt, i.e. an interrupt
410               that occurred in the ret8 return path between restoring
411               EFLAGS and returning with the ret instruction. */
412            cmp dword ptr [esp], offset ret8_instruction
413            jne not_nested_int
414
415            /* This is a nested interrupt, so we have 2 IRET frames.
416               Skip the first, and go directly to the previous return address.
417               Do not pass Go. Do not collect $200 */
418            add esp, 12
419
420not_nested_int:
421            /* We are at the IRET frame, so push EFLAGS first */
422            push dword ptr [esp + 8]
423
424        endif
425
426        /* Restore EFLAGS */
427        popfd
428
429    endif
430
431    if (Flags AND KI_EXIT_SYSCALL)
432
433        /* Enable interrupts and return to user mode.
434           Both must follow directly after another to be "atomic". */
435        sti
436        sysexit
437
438    elseif (Flags AND KI_EXIT_IRET)
439
440        /* Return with iret */
441        iretd
442
443    elseif (Flags AND KI_EXIT_JMP)
444
445        /* Return to kernel mode with a jmp */
446        jmp edx
447
448    elseif (Flags AND KI_EXIT_RET8)
449
450        /* Return to kernel mode with a ret 8 */
451ret8_instruction:
452        ret 8
453
454    elseif (Flags AND KI_EXIT_RET)
455
456        /* Return to kernel mode with a ret */
457        ret
458
459    endif
460
461ENDM
462
463