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#if DBG
211    /* Keep the frame chain intact */
212    mov eax, [esp + KTRAP_FRAME_EIP]
213    mov [esp + KTRAP_FRAME_DEBUGEIP], eax
214    mov [esp + KTRAP_FRAME_DEBUGEBP], ebp
215    mov ebp, esp
216#endif
217
218    /* Set parameter 1 (ECX) to point to the frame */
219    mov ecx, esp
220
221    /* Clear direction flag */
222    cld
223
224ENDM
225
226MACRO(KiCallHandler, Handler)
227#if DBG
228    /* Use a call to get the return address for back traces */
229    call Handler
230#else
231    /* Use the faster jmp */
232    jmp Handler
233#endif
234    nop
235ENDM
236
237MACRO(TRAP_ENTRY, Trap, Flags)
238    EXTERN @&Trap&Handler@4 :PROC
239    PUBLIC _&Trap
240    .PROC _&Trap
241        /* Generate proper debugging symbols */
242        FPO 0, 0, 0, 0, 1, FRAME_TRAP
243
244        /* Common code to create the trap frame */
245        KiEnterTrap Flags
246
247        /* Call the C handler */
248        KiCallHandler @&Trap&Handler@4
249    .ENDP
250ENDM
251
252#define KI_NMI  HEX(0001)
253
254MACRO(TASK_ENTRY, Trap, Flags)
255    EXTERN _&Trap&Handler :PROC
256    PUBLIC _&Trap
257    .PROC _&Trap
258        /* Generate proper debugging symbols */
259        FPO 0, 0, 0, 0, 0, FRAME_TSS
260
261        /* Call the C handler */
262        call _&Trap&Handler
263
264        if (Flags AND KI_NMI)
265            /* Return from NMI: return with iret and handle NMI recursion */
266            iretd
267            jmp _&Trap
268        endif
269
270    .ENDP
271ENDM
272
273#define KI_RESTORE_EAX        HEX(0001)
274#define KI_RESTORE_ECX_EDX    HEX(0002)
275#define KI_RESTORE_FS         HEX(0004)
276#define KI_RESTORE_SEGMENTS   HEX(0008)
277#define KI_RESTORE_EFLAGS     HEX(0010)
278#define KI_EXIT_SYSCALL       HEX(0020)
279#define KI_EXIT_JMP           HEX(0040)
280#define KI_EXIT_RET           HEX(0080)
281#define KI_EXIT_IRET          HEX(0100)
282#define KI_EDITED_FRAME       HEX(0200)
283#define KI_EXIT_RET8          HEX(0400)
284#define KI_RESTORE_VOLATILES  (KI_RESTORE_EAX OR KI_RESTORE_ECX_EDX)
285
286MACRO(KiTrapExitStub, Name, Flags)
287    LOCAL ret8_instruction
288    LOCAL not_nested_int
289
290PUBLIC @&Name&@4
291@&Name&@4:
292
293    if (Flags AND KI_EXIT_RET8) OR (Flags AND KI_EXIT_IRET)
294
295        /* This is the IRET frame */
296        OffsetEsp = KTRAP_FRAME_EIP
297
298    elseif (Flags AND KI_RESTORE_EFLAGS)
299
300        /* We will pop EFlags off the stack */
301        OffsetEsp = KTRAP_FRAME_EFLAGS
302
303    else
304
305        OffsetEsp = 0
306
307    endif
308
309    if (Flags AND KI_EDITED_FRAME)
310
311        /* Load the requested ESP */
312        mov esp, [ecx + KTRAP_FRAME_TEMPESP]
313
314        /* Put return address on the new stack */
315        push [ecx + KTRAP_FRAME_EIP]
316
317        /* Put EFLAGS on the new stack */
318        push [ecx + KTRAP_FRAME_EFLAGS]
319
320    else
321
322        /* Point esp to an appropriate member of the frame */
323        lea esp, [ecx + OffsetEsp]
324
325    endif
326
327    /* Restore non volatiles */
328    mov ebx, [ecx + KTRAP_FRAME_EBX]
329    mov esi, [ecx + KTRAP_FRAME_ESI]
330    mov edi, [ecx + KTRAP_FRAME_EDI]
331    mov ebp, [ecx + KTRAP_FRAME_EBP]
332
333    if (Flags AND KI_RESTORE_EAX)
334
335        /* Restore eax */
336        mov eax, [ecx + KTRAP_FRAME_EAX]
337
338    endif
339
340    if (Flags AND KI_RESTORE_ECX_EDX)
341
342        /* Restore volatiles */
343        mov edx, [ecx + KTRAP_FRAME_EDX]
344        mov ecx, [ecx + KTRAP_FRAME_ECX]
345
346    elseif (Flags AND KI_EXIT_JMP)
347
348        /* Load return address into edx */
349        mov edx, [esp - OffsetEsp + KTRAP_FRAME_EIP]
350
351    elseif (Flags AND KI_EXIT_SYSCALL)
352
353        /* Set sysexit parameters */
354        mov edx, [esp - OffsetEsp + KTRAP_FRAME_EIP]
355        mov ecx, [esp - OffsetEsp + KTRAP_FRAME_ESP]
356
357        /* Keep interrupts disabled until the sti / sysexit */
358        and byte ptr [esp - OffsetEsp + KTRAP_FRAME_EFLAGS + 1], NOT (EFLAGS_INTERRUPT_MASK / HEX(100))
359
360    endif
361
362    if (Flags AND KI_RESTORE_SEGMENTS)
363
364        /* Restore segments for user mode */
365        mov ds, [esp - OffsetEsp + KTRAP_FRAME_DS]
366        mov es, [esp - OffsetEsp + KTRAP_FRAME_ES]
367        mov gs, [esp - OffsetEsp + KTRAP_FRAME_GS]
368
369    endif
370
371    if ((Flags AND KI_RESTORE_FS) OR (Flags AND KI_RESTORE_SEGMENTS))
372
373        /* Restore user mode FS */
374        mov fs, [esp - OffsetEsp + KTRAP_FRAME_FS]
375
376    endif
377
378    if (Flags AND KI_RESTORE_EFLAGS)
379
380        if (Flags AND KI_EXIT_RET8)
381
382            /* Check if we return from a nested interrupt, i.e. an interrupt
383               that occurred in the ret8 return path between restoring
384               EFLAGS and returning with the ret instruction. */
385            cmp dword ptr [esp], offset ret8_instruction
386            jne not_nested_int
387
388            /* This is a nested interrupt, so we have 2 IRET frames.
389               Skip the first, and go directly to the previous return address.
390               Do not pass Go. Do not collect $200 */
391            add esp, 12
392
393not_nested_int:
394            /* We are at the IRET frame, so push EFLAGS first */
395            push dword ptr [esp + 8]
396
397        endif
398
399        /* Restore EFLAGS */
400        popfd
401
402    endif
403
404    if (Flags AND KI_EXIT_SYSCALL)
405
406        /* Enable interrupts and return to user mode.
407           Both must follow directly after another to be "atomic". */
408        sti
409        sysexit
410
411    elseif (Flags AND KI_EXIT_IRET)
412
413        /* Return with iret */
414        iretd
415
416    elseif (Flags AND KI_EXIT_JMP)
417
418        /* Return to kernel mode with a jmp */
419        jmp edx
420
421    elseif (Flags AND KI_EXIT_RET8)
422
423        /* Return to kernel mode with a ret 8 */
424ret8_instruction:
425        ret 8
426
427    elseif (Flags AND KI_EXIT_RET)
428
429        /* Return to kernel mode with a ret */
430        ret
431
432    endif
433
434ENDM
435
436