sync to trunk head (37853) (except rbuild changes)
[reactos.git] / reactos / ntoskrnl / include / internal / i386 / asmmacro.S
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/include/i386/asmmacro.S
5 * PURPOSE: Assembly Macros for Spinlocks and common Trap Code
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ndk/asm.h>
12
13 // Arguments for TRAP_EPILOG
14 #define FromSystemCall 1
15 #define DoRestorePreviousMode 1
16 #define DoRestoreEverything 1
17 #define DoRestoreSegments 1
18 #define DoRestoreVolatiles 1
19 #define DoPushFakeErrorCode 1
20 #define DoFixupV86 1
21 #define DoFixupAbios 1
22 #define NotFromSystemCall 0
23 #define DoNotRestorePreviousMode 0
24 #define DoNotRestoreEverything 0
25 #define DoNotRestoreSegments 0
26 #define DoNotRestoreVolatiles 0
27 #define DoNotPushFakeErrorCode 0
28 #define DoNotFixupV86 0
29 #define DoNotFixupAbios 0
30
31 // Arguments for idt
32 #define INT_32_DPL0 0x8E00
33 #define INT_32_DPL3 0xEE00
34
35 //
36 // This table contains the prefix flags that are used by V86 emulation
37 //
38 .equ PREFIX_FLAG_ES, 0x00000100
39 .equ PREFIX_FLAG_CS, 0x00000200
40 .equ PREFIX_FLAG_SS, 0x00000400
41 .equ PREFIX_FLAG_DS, 0x00000800
42 .equ PREFIX_FLAG_FS, 0x00001000
43 .equ PREFIX_FLAG_GS, 0x00002000
44 .equ PREFIX_FLAG_OPER32, 0x00004000
45 .equ PREFIX_FLAG_ADDR32, 0x00008000
46 .equ PREFIX_FLAG_LOCK, 0x00010000
47 .equ PREFIX_FLAG_REPNE, 0x00020000
48 .equ PREFIX_FLAG_REP, 0x00040000
49
50 .intel_syntax noprefix
51
52 //
53 // These macros are inlined equivalents of KiAcquire/ReleaseSpinlock, that is,
54 // they will not be compiled into non-SMP builds. Usage is as follows:
55 //
56 // .BeginYourFunction
57 // mov reg, lockaddr
58 // ACQUIRE_SPINLOCK(reg, .spin)
59 // <thread-safe code here>
60 // RELEASE_SPINLOCK(reg)
61 // <misc code here>
62 // retn
63 // #IFDEF CONFIG_SMP
64 // .spin
65 // <any necessary steps to be able to jump back safely>
66 / SPIN_ON_LOCK(reg, .BeginYourFunction)
67 // #ENDIF
68 //
69 #ifdef CONFIG_SMP
70 #define LOCK lock
71 #define ACQUIRE_SPINLOCK(x, y) \
72 lock bts dword ptr [x], 0; \
73 jb y
74 #define RELEASE_SPINLOCK(x) mov byte ptr [x], 0
75 #define SPIN_ON_LOCK(x, y) \
76 1: \
77 test dword ptr [x], 1; \
78 jz y; \
79 pause; \
80 jmp 1b
81 #else
82 #define LOCK
83 #define ACQUIRE_SPINLOCK(x, y)
84 #define RELEASE_SPINLOCK(x)
85 #endif
86
87 //
88 // @name UNHANDLED_PATH
89 //
90 // This macro TODO
91 //
92 // @param None
93 //
94 // @remark None.
95 //
96 .macro UNHANDLED_PATH
97 /* Get EIP */
98 call $+5
99 pop eax
100
101 /* Print debug message */
102 push eax
103 push offset _UnhandledMsg
104 call _DbgPrint
105 add esp, 8
106
107 /* Loop indefinitely */
108 jmp $
109 .endm
110
111 //
112 // @name IDT
113 //
114 // This macro creates an IDT entry for the given handler
115 //
116 // @param Handler
117 // Pointer to the IDT handler
118 //
119 // @param Bits
120 // Descriptor Bits to associate
121 //
122 // @remark None.
123 //
124 .macro idt Handler, Bits
125 .long \Handler
126 .short \Bits
127 .short KGDT_R0_CODE
128 .endm
129
130 //
131 // @name GENERATE_IDT_STUB
132 //
133 // This macro creates an IDT entry for an unexpected interrupt handler.
134 //
135 // @param None.
136 //
137 // @remark None.
138 //
139 .macro GENERATE_IDT_STUB Number
140 idt _KiUnexpectedInterrupt&Number, INT_32_DPL0
141 .endm
142
143 //
144 // @name GENERATE_IDT_STUBS
145 //
146 // This macro creates unexpected interrupt IDT entries.
147 //
148 // @param None.
149 //
150 // @remark None.
151 //
152 .altmacro
153 .macro GENERATE_IDT_STUBS
154 .set i, 0
155 .rept 208
156 GENERATE_IDT_STUB %i
157 .set i, i + 1
158 .endr
159 .endm
160
161 //
162 // @name GENERATE_INT_HANDLER
163 //
164 // This macro creates an unexpected interrupt handler.
165 //
166 // @param None.
167 //
168 // @remark None.
169 //
170 .macro GENERATE_INT_HANDLER Number
171 .func KiUnexpectedInterrupt&Number
172 _KiUnexpectedInterrupt&Number:
173 push PRIMARY_VECTOR_BASE + Number
174 jmp _KiEndUnexpectedRange@0
175 .endfunc
176 .endm
177
178 //
179 // @name GENERATE_INT_HANDLERS
180 //
181 // This macro creates the unexpected interrupt handlers.
182 //
183 // @param None.
184 //
185 // @remark None.
186 //
187 .altmacro
188 .macro GENERATE_INT_HANDLERS
189 .set i, 0
190 .rept 208
191 GENERATE_INT_HANDLER %i
192 .set i, i + 1
193 .endr
194 .endm
195
196 //
197 // @name INVALID_V86_OPCODE
198 //
199 // This macro creates one or more entries for unhandled V86 Opcodes
200 // in the V86 Opcode Table.
201 //
202 // @param count.
203 // Number of entries to generate.
204 //
205 // @remark None.
206 //
207 .macro INVALID_V86_OPCODE count
208 .rept \count
209 .byte 0
210 .endr
211 .endm
212
213 //
214 // @name GENERATE_PREFIX_HANDLER
215 //
216 // This macro creates a prefix opcode handler.
217 //
218 // @param None.
219 //
220 // @remark None.
221 //
222 .macro GENERATE_PREFIX_HANDLER Name
223 .func Opcode&Name&PrefixV86
224 _Opcode&Name&PrefixV86:
225 or ebx, PREFIX_FLAG_&Name
226 jmp _OpcodeGenericPrefixV86
227 .endfunc
228 .endm
229
230 //
231 // @name INVALID_V86_OPCODE
232 //
233 // This macro prints out visible message and hangs the computer.
234 //
235 // @param None.
236 //
237 // @remark Temporary debugging use.
238 //
239 .macro UNHANDLED_V86_OPCODE
240 /* Print debug message, breakpoint and freeze */
241 push ecx
242 push offset V86DebugMsg
243 call _DbgPrint
244 add esp, 8
245 jmp $
246 .endm
247
248 //
249 // @name TRAP_FIXUPS
250 //
251 // This macro contains out-of-line code for various Trap Frame Fixups, such as:
252 //
253 // - DR Fixup: Loads and restores DR registers.
254 // - V86 Fixup: Loads and restores V86 segments.
255 // - ABIOS Fixup: Loads and restores the ABIOS state and stack.
256 //
257 // @param None.
258 //
259 // @remark ebp = PKTRAP_FRAME
260 //
261 .macro TRAP_FIXUPS Label, EndLabel, V86Fix, AbiosFix
262 Dr_&Label:
263
264 /* Check if this was V86 mode */
265 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
266 jnz 2f
267
268 /* Check if it was user mode */
269 test byte ptr [ebp+KTRAP_FRAME_CS], MODE_MASK
270 jz Dr_&EndLabel
271
272 2:
273 /* Get DR0, 1, 2 */
274 mov ebx, dr0
275 mov ecx, dr1
276 mov edi, dr2
277
278 /* Save them */
279 mov [ebp+KTRAP_FRAME_DR0], ebx
280 mov [ebp+KTRAP_FRAME_DR1], ecx
281 mov [ebp+KTRAP_FRAME_DR2], edi
282
283 /* Get DR3, 6, 7 */
284 mov ebx, dr3
285 mov ecx, dr6
286 mov edi, dr7
287
288 /* Save them */
289 mov [ebp+KTRAP_FRAME_DR3], ebx
290 mov [ebp+KTRAP_FRAME_DR6], ecx
291 mov [ebp+KTRAP_FRAME_DR7], edi
292
293 /* Clear DR7 */
294 xor ebx, ebx
295 mov dr7, ebx
296
297 /* Get the PRCB */
298 mov edi, fs:[KPCR_PRCB]
299
300 /* Get DR0, 1 */
301 mov ebx, [edi+KPRCB_DR0]
302 mov ecx, [edi+KPRCB_DR1]
303
304 /* Set them */
305 mov dr0, ebx
306 mov dr1, ecx
307
308 /* Get DR2, 3 */
309 mov ebx, [edi+KPRCB_DR2]
310 mov ecx, [edi+KPRCB_DR3]
311
312 /* Set them */
313 mov dr2, ebx
314 mov dr3, ecx
315
316 /* Get DR6, 7 */
317 mov ebx, [edi+KPRCB_DR6]
318 mov ecx, [edi+KPRCB_DR7]
319
320 /* Set them */
321 mov dr6, ebx
322 mov dr7, ecx
323 jmp Dr_&EndLabel
324
325 .if \AbiosFix
326 Abios_&Label:
327 UNHANDLED_PATH
328 .endif
329
330 .if \V86Fix
331 V86_&Label:
332
333 /* Get V86 segment registers */
334 mov eax, [ebp+KTRAP_FRAME_V86_FS]
335 mov ebx, [ebp+KTRAP_FRAME_V86_GS]
336 mov ecx, [ebp+KTRAP_FRAME_V86_ES]
337 mov edx, [ebp+KTRAP_FRAME_V86_DS]
338
339 /* Restore them into Protected Mode trap frame */
340 mov [ebp+KTRAP_FRAME_FS], ax
341 mov [ebp+KTRAP_FRAME_GS], bx
342 mov [ebp+KTRAP_FRAME_ES], cx
343 mov [ebp+KTRAP_FRAME_DS], dx
344
345 /* Go back to mainline code */
346 jmp V86_&EndLabel
347 .endif
348 .endm
349
350 //
351 // @name SET_TF_DEBUG_HEADER
352 //
353 // This macro sets up the debug header in the trap frame.
354 //
355 // @param None.
356 //
357 // @remark ebp = PKTRAP_FRAME.
358 // edi/ebx = Have been saved and can be used.
359 //
360 .macro SET_TF_DEBUG_HEADER
361 /* Get the Debug Trap Frame EBP/EIP */
362 mov ebx, [ebp+KTRAP_FRAME_EBP]
363 mov edi, [ebp+KTRAP_FRAME_EIP]
364
365 /* Write the debug data */
366 mov [ebp+KTRAP_FRAME_DEBUGPOINTER], edx
367 mov dword ptr [ebp+KTRAP_FRAME_DEBUGARGMARK], 0xBADB0D00
368 mov [ebp+KTRAP_FRAME_DEBUGEBP], ebx
369 mov [ebp+KTRAP_FRAME_DEBUGEIP], edi
370 .endm
371
372 //
373 // @name CHECK_FOR_APC_DELIVER
374 //
375 // This macro checks if the trapframe indicates a return to user-mode,
376 // and, if so, checks if user-mode APCs should be delivered.
377 //
378 // @param PreserveEax
379 // Determines if EAX should be preserved. Implies that the segment
380 // registers will also be saved.
381 //
382 // @remark ebp = PKTRAP_FRAME.
383 // ebx = Saved and will be used.
384 //
385 .macro CHECK_FOR_APC_DELIVER PreserveEax
386 /* Check for V86 mode */
387 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
388 jnz 1f
389
390 /* Deliver APCs only if we were called from user mode */
391 test byte ptr [ebp+KTRAP_FRAME_CS], 1
392 je 2f
393
394 /* Get the current thread */
395 1:
396 mov ebx, PCR[KPCR_CURRENT_THREAD]
397
398 /* Make it non-alerted */
399 mov byte ptr [ebx+KTHREAD_ALERTED], 0
400
401 /* And only if any are actually pending */
402 cmp byte ptr [ebx+KTHREAD_PENDING_USER_APC], 0
403 je 2f
404
405 /* Save pointer to Trap Frame */
406 mov ebx, ebp
407
408 .if \PreserveEax
409 /* Save some stuff that raising IRQL will kill */
410 mov [ebx+KTRAP_FRAME_EAX], eax
411 mov dword ptr [ebx+KTRAP_FRAME_FS], KGDT_R3_TEB + RPL_MASK
412 mov dword ptr [ebx+KTRAP_FRAME_DS], KGDT_R3_DATA + RPL_MASK
413 mov dword ptr [ebx+KTRAP_FRAME_ES], KGDT_R3_DATA + RPL_MASK
414 mov dword ptr [ebx+KTRAP_FRAME_GS], 0
415 .endif
416
417 /* Raise IRQL to APC_LEVEL */
418 mov ecx, 1
419 call @KfRaiseIrql@4
420
421 /* Save old IRQL */
422 push eax
423
424 /* Deliver APCs */
425 sti
426 push ebx
427 push 0
428 push UserMode
429 call _KiDeliverApc@12
430
431 /* Return to old IRQL */
432 pop ecx
433 call @KfLowerIrql@4
434
435 /* Restore EAX (only in volatile case) */
436 .if \PreserveEax
437 mov eax, [ebx+KTRAP_FRAME_EAX]
438 .endif
439 cli
440 jmp 1b
441 2:
442 .endm
443
444 //
445 // @name TRAP_PROLOG
446 //
447 // This macro creates a standard trap entry prologue.
448 // It should be used for entry into any kernel trap (KiTrapXx), but not for
449 // system calls, which require special handling.
450 //
451 // @param Label
452 // Identifying name of the caller function; will be used to append
453 // to the name V86 and DR helper functions, which must already exist.
454 //
455 // @remark Use as follows:
456 // _KiTrap00:
457 // /* Push fake error code */
458 // push 0
459 //
460 // /* Enter common prologue */
461 // TRAP_PROLOG(0)
462 //
463 // /* Handle trap */
464 // <Your Trap Code Here>
465 //
466 .macro TRAP_PROLOG Label EndLabel
467 /* Just to be safe, clear out the HIWORD, since it's reserved */
468 mov word ptr [esp+2], 0
469
470 /* Save the non-volatiles */
471 push ebp
472 push ebx
473 push esi
474 push edi
475
476 /* Save FS and set it to PCR */
477 push fs
478 mov ebx, KGDT_R0_PCR
479 .byte 0x66
480 mov fs, bx
481
482 /* Save exception list and bogus previous mode */
483 push fs:[KPCR_EXCEPTION_LIST]
484 push -1
485
486 /* Save volatiles and segment registers */
487 push eax
488 push ecx
489 push edx
490 push ds
491 push es
492 push gs
493
494 /* Set the R3 data segment */
495 mov ax, KGDT_R3_DATA + RPL_MASK
496
497 /* Skip debug registers and debug stuff */
498 sub esp, 0x30
499
500 /* Load the segment registers */
501 .byte 0x66
502 mov ds, ax
503 .byte 0x66
504 mov es, ax
505
506 /* Check if this interrupt happened in 16-bit mode */
507 cmp esp, 0x10000
508 jb _Ki16BitStackException
509
510 /* Set up frame */
511 mov ebp, esp
512
513 /* Check if this was from V86 Mode */
514 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
515 jnz V86_&Label
516
517 V86_&EndLabel:
518 /* Get current thread */
519 mov ecx, fs:[KPCR_CURRENT_THREAD]
520 cld
521
522 /* Flush DR7 */
523 and dword ptr [ebp+KTRAP_FRAME_DR7], 0
524
525 /* Check if the thread was being debugged */
526 test byte ptr [ecx+KTHREAD_DEBUG_ACTIVE], 0xFF
527 jnz Dr_&Label
528
529 /* Set the Trap Frame Debug Header */
530 Dr_&EndLabel:
531 SET_TF_DEBUG_HEADER
532 .endm
533
534 //
535 // @name INT_PROLOG
536 //
537 // This macro creates a standard interrupt entry prologue.
538 // It should be used for entry into any interrupt, including software.
539 //
540 // @param Label
541 // Identifying name of the caller function; will be used to append
542 // to the name V86, ABIOS and DR helper functions, which must exist.
543 //
544 // @remark For software interrupts, make sure that a fake INT stack is created.
545 //
546 .macro INT_PROLOG Label EndLabel FakeErrorCode
547
548 .if \FakeErrorCode
549 /* Save fake error code */
550 push esp
551 .endif
552
553 /* Save the non-volatiles */
554 push ebp
555 push ebx
556 push esi
557 push edi
558
559 /* Skip debug registers and other stuff */
560 sub esp, 0x54
561
562 /* Set up frame */
563 mov ebp, esp
564
565 /* Save volatiles */
566 mov [esp+KTRAP_FRAME_EAX], eax
567 mov [esp+KTRAP_FRAME_ECX], ecx
568 mov [esp+KTRAP_FRAME_EDX], edx
569 mov dword ptr [esp+KTRAP_FRAME_PREVIOUS_MODE], -1
570
571 /* Check if this was from V86 Mode */
572 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
573 jnz V86_&Label
574
575 /* Check if this was kernel mode */
576 V86_&EndLabel:
577 cmp word ptr [esp+KTRAP_FRAME_CS], KGDT_R0_CODE
578 jz 1f
579
580 /* Set segments */
581 mov word ptr [esp+KTRAP_FRAME_FS], fs
582 mov word ptr [esp+KTRAP_FRAME_DS], ds
583 mov word ptr [esp+KTRAP_FRAME_ES], es
584 mov [esp+KTRAP_FRAME_GS], gs
585
586 /* Load the segment registers (use OVERRIDE (0x66)) */
587 mov ebx, KGDT_R0_PCR
588 mov eax, KGDT_R3_DATA | RPL_MASK
589 .byte 0x66
590 mov fs, bx
591 .byte 0x66
592 mov ds, ax
593 .byte 0x66
594 mov es, ax
595
596 1:
597 /* Get the previous exception list */
598 mov ebx, fs:[KPCR_EXCEPTION_LIST]
599
600 /* Set the exception handler chain terminator */
601 mov dword ptr fs:[KPCR_EXCEPTION_LIST], -1
602
603 /* Save the previous exception list */
604 mov [esp+KTRAP_FRAME_EXCEPTION_LIST], ebx
605
606 .ifeq \FakeErrorCode
607 /* Setup the 16-bit stack */
608 lea eax, [esp+KTRAP_FRAME_ERROR_CODE]
609 lea ecx, [esp+KTRAP_FRAME_EIP]
610 mov ebx, ss:[eax]
611 mov ss:[eax], ecx
612 .endif
613
614 /* Check if this is the ABIOS stack */
615 /* cmp esp, 0x10000*/
616 /* jb Abios_Label*/
617
618 /* Delete error code */
619 and dword ptr [esp+KTRAP_FRAME_ERROR_CODE], 0
620
621 /* Get the current thread and clear direction flag */
622 mov ecx, PCR[KPCR_CURRENT_THREAD]
623 cld
624
625 /* Flush DR7 */
626 and dword ptr [ebp+KTRAP_FRAME_DR7], 0
627
628 /* Check if the thread was being debugged */
629 test byte ptr [ecx+KTHREAD_DEBUG_ACTIVE], 0xFF
630
631 .ifeq \FakeErrorCode
632 /* Push parameter */
633 push ebx
634 .endif
635
636 /* Save DR registers if needed */
637 jnz Dr_&Label
638
639 /* Set the trap frame debug header */
640 Dr_&EndLabel:
641 SET_TF_DEBUG_HEADER
642 .endm
643
644 //
645 // @name SYSCALL_PROLOG
646 //
647 // This macro creates a system call entry prologue.
648 // It should be used for entry into any fast-system call (KiGetTickCount,
649 // KiCallbackReturn, KiRaiseAssertion) and the generic system call handler
650 // (KiSystemService)
651 //
652 // @param Label
653 // Unique label identifying the name of the caller function; will be
654 // used to append to the name of the DR helper function, which must
655 // already exist.
656 //
657 // @remark None.
658 //
659 .macro SYSCALL_PROLOG Label EndLabel
660 /* Create a trap frame */
661 push 0
662 push ebp
663 push ebx
664 push esi
665 push edi
666 push fs
667
668 /* Load PCR Selector into fs */
669 mov ebx, KGDT_R0_PCR
670 .byte 0x66
671 mov fs, bx
672
673 /* Get a pointer to the current thread */
674 mov esi, PCR[KPCR_CURRENT_THREAD]
675
676 /* Save the previous exception list */
677 push PCR[KPCR_EXCEPTION_LIST]
678
679 /* Set the exception handler chain terminator */
680 mov dword ptr PCR[KPCR_EXCEPTION_LIST], -1
681
682 /* Save the old previous mode */
683 push [esi+KTHREAD_PREVIOUS_MODE]
684
685 /* Skip the other registers */
686 sub esp, 0x48
687
688 /* Set the new previous mode based on the saved CS selector */
689 mov ebx, [esp+0x6C]
690 and ebx, 1
691 mov byte ptr [esi+KTHREAD_PREVIOUS_MODE], bl
692
693 /* Go on the Kernel stack frame */
694 mov ebp, esp
695
696 /* Save the old trap frame pointer where EDX would be saved */
697 mov ebx, [esi+KTHREAD_TRAP_FRAME]
698 mov [ebp+KTRAP_FRAME_EDX], ebx
699
700 /* Flush DR7 */
701 and dword ptr [ebp+KTRAP_FRAME_DR7], 0
702
703 /* Check if the thread was being debugged */
704 test byte ptr [esi+KTHREAD_DEBUG_ACTIVE], 0xFF
705
706 /* Set the thread's trap frame and clear direction flag */
707 mov [esi+KTHREAD_TRAP_FRAME], ebp
708 cld
709
710 /* Save DR registers if needed */
711 jnz Dr_&Label
712
713 /* Set the trap frame debug header */
714 Dr_&EndLabel:
715 SET_TF_DEBUG_HEADER
716
717 /* Enable interrupts */
718 sti
719 .endm
720
721 //
722 // @name FASTCALL_PROLOG
723 //
724 // TODO
725 //
726 // @param Label
727 // Unique label identifying the name of the caller function; will be
728 // used to append to the name of the DR helper function, which must
729 // already exist.
730 //
731 // @remark None.
732 //
733 .macro FASTCALL_PROLOG Label EndLabel
734
735 /* Set user selector */
736 mov ecx, KGDT_R3_DATA | RPL_MASK
737
738 /* Set FS to PCR */
739 push KGDT_R0_PCR
740 pop fs
741
742 /* Set DS/ES to User Selector */
743 mov ds, cx
744 mov es, cx
745
746 /* Set the current stack to Kernel Stack */
747 mov ecx, PCR[KPCR_TSS]
748 mov esp, [ecx+KTSS_ESP0]
749
750 /* Set up a fake INT Stack. */
751 push KGDT_R3_DATA + RPL_MASK
752 push edx /* Ring 3 SS:ESP */
753 pushf /* Ring 3 EFLAGS */
754 push 2 /* Ring 0 EFLAGS */
755 add edx, 8 /* Skip user parameter list */
756 popf /* Set our EFLAGS */
757 or dword ptr [esp], EFLAGS_INTERRUPT_MASK /* Re-enable IRQs in EFLAGS, to fake INT */
758 push KGDT_R3_CODE + RPL_MASK
759 push dword ptr ds:KUSER_SHARED_SYSCALL_RET
760
761 /* Setup the Trap Frame stack */
762 push 0
763 push ebp
764 push ebx
765 push esi
766 push edi
767 push KGDT_R3_TEB + RPL_MASK
768
769 /* Save pointer to our PCR */
770 mov ebx, PCR[KPCR_SELF]
771
772 /* Get a pointer to the current thread */
773 mov esi, [ebx+KPCR_CURRENT_THREAD]
774
775 /* Set the exception handler chain terminator */
776 push [ebx+KPCR_EXCEPTION_LIST]
777 mov dword ptr [ebx+KPCR_EXCEPTION_LIST], -1
778
779 /* Use the thread's stack */
780 mov ebp, [esi+KTHREAD_INITIAL_STACK]
781
782 /* Push previous mode */
783 push UserMode
784
785 /* Skip the other registers */
786 sub esp, 0x48
787
788 /* Make space for us on the stack */
789 sub ebp, 0x29C
790
791 /* Write the previous mode */
792 mov byte ptr [esi+KTHREAD_PREVIOUS_MODE], UserMode
793
794 /* Sanity check */
795 cmp ebp, esp
796 jnz BadStack
797
798 /* Flush DR7 */
799 and dword ptr [ebp+KTRAP_FRAME_DR7], 0
800
801 /* Check if the thread was being debugged */
802 test byte ptr [esi+KTHREAD_DEBUG_ACTIVE], 0xFF
803
804 /* Set the thread's trap frame */
805 mov [esi+KTHREAD_TRAP_FRAME], ebp
806
807 /* Save DR registers if needed */
808 jnz Dr_&Label
809
810 /* Set the trap frame debug header */
811 Dr_&EndLabel:
812 SET_TF_DEBUG_HEADER
813
814 /* Enable interrupts */
815 sti
816 .endm
817
818 //
819 // @name V86_TRAP_PROLOG
820 //
821 // This macro creates a V86 Trap entry prologue.
822 // It should be used for entry into any fast-system call (KiGetTickCount,
823 // KiCallbackReturn, KiRaiseAssertion) and the generic system call handler
824 // (KiSystemService)
825 //
826 // @param Label
827 // Unique label identifying the name of the caller function; will be
828 // used to append to the name of the DR helper function, which must
829 // already exist.
830 //
831 // @remark None.
832 //
833 .macro V86_TRAP_PROLOG Label EndLabel
834
835 /* Skip everything to the error code */
836 sub esp, KTRAP_FRAME_ERROR_CODE
837
838 /* Clear the error code */
839 mov word ptr [esp+KTRAP_FRAME_ERROR_CODE+2], 0
840
841 /* Save the registers we'll trample */
842 mov [esp+KTRAP_FRAME_EBX], ebx
843 mov [esp+KTRAP_FRAME_EAX], eax
844 mov [esp+KTRAP_FRAME_EBP], ebp
845 mov [esp+KTRAP_FRAME_ESI], esi
846 mov [esp+KTRAP_FRAME_EDI], edi
847
848 /* Save PCR and Ring 3 segments */
849 mov ebx, KGDT_R0_PCR
850 mov eax, KGDT_R3_DATA + RPL_MASK
851
852 /* Save ECX and EDX too */
853 mov [esp+KTRAP_FRAME_ECX], ecx
854 mov [esp+KTRAP_FRAME_EDX], edx
855
856 /* Set debugging markers */
857 mov dword ptr [esp+KTRAP_FRAME_PREVIOUS_MODE], -1
858 mov dword ptr [esp+KTRAP_FRAME_DEBUGARGMARK], 0xBADB0D00
859
860 /* Now set segments (use OVERRIDE, 0x66) */
861 .byte 0x66
862 mov fs, bx
863 .byte 0x66
864 mov ds, ax
865 .byte 0x66
866 mov es, ax
867
868 /* Set the trap frame in the stack and clear the direction flag */
869 mov ebp, esp
870 cld
871
872 /* Save the exception list */
873 mov eax, fs:[KPCR_EXCEPTION_LIST]
874 mov [esp+KTRAP_FRAME_EXCEPTION_LIST], eax
875
876 /* Check if we need debugging */
877 mov eax, dr7
878 test eax, ~DR7_RESERVED_MASK
879 mov [esp+KTRAP_FRAME_DR7], eax
880 jnz Dr_&Label
881
882 Dr_&EndLabel:
883 .endm
884
885 //
886 // @name V86_TRAP_EPILOG
887 //
888 // This macro creates an epilogue for leaving V86 traps
889 //
890 // @param None.
891 //
892 // @remark None.
893 //
894 .macro V86_TRAP_EPILOG
895
896 /* Get the current thread and make it unalerted */
897 ExitBegin:
898 mov ebx, PCR[KPCR_CURRENT_THREAD]
899 mov byte ptr [ebx+KTHREAD_ALERTED], 0
900
901 /* Check if it has User-mode APCs pending */
902 cmp byte ptr [ebx+KTHREAD_PENDING_USER_APC], 0
903 jne PendingUserApc
904
905 /* It doesn't, pop the frame */
906 add esp, KTRAP_FRAME_EDX
907 pop edx
908 pop ecx
909 pop eax
910
911 /* Check if DR registers should be restored */
912 test dword ptr [ebp+KTRAP_FRAME_DR7], ~DR7_RESERVED_MASK
913 jnz V86DebugRestore
914
915 /* Finish popping the rest of the frame, and return to P-mode */
916 V86DebugContinue:
917 add esp, 12
918 pop edi
919 pop esi
920 pop ebx
921 pop ebp
922 add esp, 4
923 iretd
924
925 V86DebugRestore:
926
927 /* Get DR0, 1 */
928 xor ebx, ebx
929 mov esi, [ebp+KTRAP_FRAME_DR0]
930 mov edi, [ebp+KTRAP_FRAME_DR1]
931
932 /* Clear DR 7 */
933 mov dr7, ebx
934
935 /* Get DR2 and load DR0-2 */
936 mov ebx, [ebp+KTRAP_FRAME_DR2]
937 mov dr0, esi
938 mov dr1, edi
939 mov dr2, ebx
940
941 /* Get DR3-7 */
942 mov esi, [ebp+KTRAP_FRAME_DR0]
943 mov edi, [ebp+KTRAP_FRAME_DR1]
944 mov ebx, [ebp+KTRAP_FRAME_DR7]
945
946 /* Load them */
947 mov dr3, esi
948 mov dr6, edi
949 mov dr7, ebx
950 jmp V86DebugContinue
951
952 PendingUserApc:
953
954 /* Raise to APC level */
955 mov ecx, APC_LEVEL
956 call @KfRaiseIrql@4
957
958 /* Save KIRQL and deliver APCs */
959 push eax
960 sti
961 push ebp
962 push 0
963 push UserMode
964 call _KiDeliverApc@12
965
966 /* Restore IRQL */
967 pop ecx
968 call @KfLowerIrql@4
969 cli
970
971 /* Check if we're not in V86 anymore */
972 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
973 jnz ExitBegin
974 .endm
975
976 //
977 // @name TRAP_EPILOG
978 //
979 // This macro creates an epilogue for leaving any system trap.
980 // It is used for exiting system calls, exceptions, interrupts and generic
981 // traps.
982 //
983 // @param SystemCall
984 // Specifies whether this trap will exit a system call. If so, special
985 // code will be assembled to potentially use SYSEXIT instead of IRETD.
986 //
987 // @param RestorePreviousMode
988 // Specifies if the previous mode should be restored.
989 //
990 // @param RestoreSegments
991 // Specifies if the segment registers should be restored.
992 //
993 // @param RestoreVolatiles
994 // Specifies if the volatile registers should be restored.
995 //
996 // @param RestoreAllRegs
997 // Specifies if volatiles and segments should both be restored.
998 //
999 // @remark
1000 //
1001 .macro TRAP_EPILOG SystemCall, RestorePreviousMode, RestoreSegments, RestoreVolatiles, RestoreAllRegs
1002 #ifdef DBG
1003 /* Assert the flags */
1004 pushfd
1005 pop edx
1006 test edx, EFLAGS_INTERRUPT_MASK
1007 jnz 6f
1008
1009 /* Assert the stack */
1010 cmp esp, ebp
1011 jnz 6f
1012
1013 /* Assert the trap frame */
1014 #endif
1015 5:
1016 #ifdef DBG
1017 sub dword ptr [esp+KTRAP_FRAME_DEBUGARGMARK], 0xBADB0D00
1018 jnz 0f
1019
1020 /* Assert FS */
1021 mov bx, fs
1022 cmp bx, KGDT_R0_PCR
1023 jnz 1f
1024
1025 /* Assert exception list */
1026 cmp dword ptr PCR[KPCR_EXCEPTION_LIST], 0
1027 jnz 2f
1028
1029 1:
1030 push -1
1031 call _KeBugCheck@4
1032 #endif
1033
1034 2:
1035 /* Get exception list */
1036 mov edx, [esp+KTRAP_FRAME_EXCEPTION_LIST]
1037
1038 #ifdef DBG
1039 /* Assert the saved exception list */
1040 or edx, edx
1041 jnz 1f
1042 UNHANDLED_PATH
1043 1:
1044 #endif
1045
1046 /* Restore it */
1047 mov PCR[KPCR_EXCEPTION_LIST], edx
1048
1049 .if \RestorePreviousMode
1050 /* Get previous mode */
1051 mov ecx, [esp+KTRAP_FRAME_PREVIOUS_MODE]
1052
1053 #ifdef DBG
1054 /* Assert the saved previous mode */
1055 cmp ecx, -1
1056 jnz 1f
1057 UNHANDLED_PATH
1058 1:
1059 #endif
1060
1061 /* Restore the previous mode */
1062 mov esi, PCR[KPCR_CURRENT_THREAD]
1063 mov byte ptr [esi+KTHREAD_PREVIOUS_MODE], cl
1064 .else
1065
1066 #ifdef DBG
1067 /* Assert the saved previous mode */
1068 mov ecx, [esp+KTRAP_FRAME_PREVIOUS_MODE]
1069 cmp ecx, -1
1070 jz 1f
1071 UNHANDLED_PATH
1072 1:
1073 #endif
1074 .endif
1075
1076 /* Check for debug registers */
1077 test dword ptr [esp+KTRAP_FRAME_DR7], ~DR7_RESERVED_MASK
1078 jnz 2f
1079
1080 /* Check for V86 */
1081 4:
1082 test dword ptr [esp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
1083 jnz V86_Exit
1084
1085 /* Check if the frame was edited */
1086 test word ptr [esp+KTRAP_FRAME_CS], FRAME_EDITED
1087 jz 7f
1088
1089 .if \RestoreAllRegs
1090 /* Check the old mode */
1091 cmp word ptr [esp+KTRAP_FRAME_CS], KGDT_R3_CODE + RPL_MASK
1092 bt word ptr [esp+KTRAP_FRAME_CS], 0
1093 cmc
1094 ja 8f
1095 .endif
1096
1097 .if \RestoreVolatiles
1098 /* Restore volatiles */
1099 mov edx, [esp+KTRAP_FRAME_EDX]
1100 mov ecx, [esp+KTRAP_FRAME_ECX]
1101 mov eax, [esp+KTRAP_FRAME_EAX]
1102 .endif
1103
1104 /* Check if we were called from kernel mode */
1105 cmp word ptr [ebp+KTRAP_FRAME_CS], KGDT_R0_CODE
1106 jz 9f
1107
1108 .if \RestoreSegments
1109 /* Restore segment registers */
1110 lea esp, [ebp+KTRAP_FRAME_GS]
1111 pop gs
1112 pop es
1113 pop ds
1114 .endif
1115
1116 /* Restore FS */
1117 3:
1118 lea esp, [ebp+KTRAP_FRAME_FS]
1119 pop fs
1120
1121 9:
1122 /* Skip debug information and unsaved registers */
1123 lea esp, [ebp+KTRAP_FRAME_EDI]
1124 pop edi
1125 pop esi
1126 pop ebx
1127 pop ebp
1128
1129 /* Check for ABIOS */
1130 cmp word ptr [esp+8], 0x80
1131 ja AbiosExit
1132
1133 /* Pop error code */
1134 add esp, 4
1135
1136 .if \SystemCall
1137 /* Check if previous CS is from user-mode */
1138 test dword ptr [esp+4], 1
1139
1140 /* It is, so use Fast Exit */
1141 jnz FastExit
1142
1143 /* Jump back to stub */
1144 pop edx
1145 pop ecx
1146 popf
1147 jmp edx
1148
1149 .ret:
1150 .endif
1151 iret
1152
1153 .if \SystemCall
1154 FastExit:
1155 /* Is SYSEXIT Supported/Wanted? */
1156 cmp dword ptr ss:[_KiFastSystemCallDisable], 0
1157 jnz .ret
1158 test dword ptr [esp+8], EFLAGS_TF
1159 jnz .ret
1160
1161 /* Restore FS to TIB */
1162 mov ecx, KGDT_R3_TEB + RPL_MASK
1163 mov fs, ecx
1164
1165 /* We will be cleaning up the stack ourselves */
1166 pop edx /* New Ring 3 EIP */
1167 add esp, 4 /* Skip Ring 3 DS */
1168 and dword ptr [esp], 0xfffffdff /* Remove EFLAGS_INTERRUPT_MASK from EFLAGS */
1169 popf /* Restore old EFLAGS */
1170 pop ecx /* Old Ring 3 SS:ESP */
1171
1172 /*
1173 * At this point:
1174 * ECX points to the old User Stack.
1175 * EDX points to the instruction to execute in usermode after the sysenter
1176 */
1177 sti
1178 sysexit
1179 .endif
1180
1181 .if \RestoreAllRegs
1182 8:
1183 /* Restore EAX */
1184 mov eax, [esp+KTRAP_FRAME_EAX]
1185
1186 /* Skip registers */
1187 add esp, 0x30
1188
1189 /* Restore segments and volatiles */
1190 pop gs
1191 pop es
1192 pop ds
1193 pop edx
1194 pop ecx
1195
1196 /* Jump back to mainline code */
1197 jmp 3b
1198 .endif
1199
1200 #if DBG
1201 0:
1202 /* Fix up the mask */
1203 add dword ptr [esp+KTRAP_FRAME_DEBUGARGMARK], 0xBADB0D00
1204
1205 6:
1206 UNHANDLED_PATH
1207 jmp 5b
1208 #endif
1209
1210 2:
1211 /* Check if this was V86 mode */
1212 test dword ptr [esp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
1213 jnz 1f
1214
1215 /* Check if it was user mode */
1216 test word ptr [esp+KTRAP_FRAME_CS], MODE_MASK
1217 jz 4b
1218
1219 1:
1220 /* Clear DR7 */
1221 xor ebx, ebx
1222 mov dr7, ebx
1223
1224 /* Get DR0, 1, 2 */
1225 mov esi, [ebp+KTRAP_FRAME_DR0]
1226 mov edi, [ebp+KTRAP_FRAME_DR1]
1227 mov ebx, [ebp+KTRAP_FRAME_DR2]
1228
1229 /* Set them */
1230 mov dr0, esi
1231 mov dr1, edi
1232 mov dr2, ebx
1233
1234 /* Get DR3, 6, 7 */
1235 mov esi, [ebp+KTRAP_FRAME_DR3]
1236 mov edi, [ebp+KTRAP_FRAME_DR6]
1237 mov ebx, [ebp+KTRAP_FRAME_DR7]
1238
1239 /* Set them */
1240 mov dr3, esi
1241 mov dr6, edi
1242 mov dr7, ebx
1243 jmp 4b
1244
1245 7:
1246 /* Restore real CS value */
1247 mov ebx, [esp+KTRAP_FRAME_TEMPCS]
1248 mov [esp+KTRAP_FRAME_CS], ebx
1249
1250 /*
1251 * If ESP was modified, then a special interrupt exit stack
1252 * must be created to "update" ESP's value in a legal manner
1253 */
1254 mov ebx, [esp+KTRAP_FRAME_TEMPESP]
1255 sub ebx, 0xC
1256 mov [esp+KTRAP_FRAME_ERROR_CODE], ebx
1257
1258 /* Copy Interrupt Stack */
1259 mov esi, [esp+KTRAP_FRAME_EFLAGS]
1260 mov [ebx+8], esi
1261 mov esi, [esp+KTRAP_FRAME_CS]
1262 mov [ebx+4], esi
1263 mov esi, [esp+KTRAP_FRAME_EIP]
1264 mov [ebx], esi
1265
1266 .if \RestoreVolatiles
1267 /* Restore volatiles */
1268 mov eax, [esp+KTRAP_FRAME_EAX]
1269 mov edx, [esp+KTRAP_FRAME_EDX]
1270 mov ecx, [esp+KTRAP_FRAME_ECX]
1271 .endif
1272
1273 /* Return */
1274 add esp, KTRAP_FRAME_EDI
1275 pop edi
1276 pop esi
1277 pop ebx
1278 pop ebp
1279 mov esp, [esp]
1280 iret
1281 .endm
1282
1283 //
1284 // @name INT_EPILOG
1285 //
1286 // This macro creates an epilogue for leaving any system trap.
1287 // It is used for exiting system calls, exceptions, interrupts and generic
1288 // traps.
1289 //
1290 // @param Spurious - TRUE if the interrupt was unexpected and spurious.
1291 //
1292 // @remark None.
1293 //
1294 .macro INT_EPILOG Spurious
1295
1296 .if \Spurious
1297 /* Just exit the trap */
1298 jmp _Kei386EoiHelper@0
1299 .else
1300 /* Disable interrupts */
1301 cli
1302
1303 /* End the interrupt and do EOI */
1304 call _HalEndSystemInterrupt@8
1305 jmp _Kei386EoiHelper@0
1306 .endif
1307 .endm
1308
1309 #ifdef DBG
1310
1311 .macro VERIFY_INT Label
1312 /* Get the current time and mask it to 192 ticks */
1313 mov eax, _KeTickCount
1314 and eax, 0xC0
1315
1316 /* Check if we're in the same tick area */
1317 cmp eax, dword ptr [edi+KINTERRUPT_TICK_COUNT]
1318 jg VfRst_&Label
1319 jl VfWrap_&Label
1320
1321 /* If we got here, then our count is too large */
1322 dec word ptr [edi+KINTERRUPT_DISPATCH_COUNT]
1323 jz VfOvr_&Label
1324 Vf_&Label:
1325 .endm
1326
1327 .macro VERIFY_INT_END Label, Info
1328 VfOvr_&Label:
1329
1330 /* Decrement the dispatch count and check if we should bug check */
1331 dec word ptr [edi+KINTERRUPT_DISPATCH_COUNT+2]
1332 jz 1f
1333
1334 /* Update the tick count */
1335 add eax, 0x40
1336 mov [edi+KINTERRUPT_TICK_COUNT], eax
1337 jmp VfRstDef_&Label
1338
1339 .1:
1340 /* Check if the debugger is enabled */
1341 cmp byte ptr __KdDebuggerEnabled, 0
1342 jnz 1f
1343
1344 /* It isn't, bugcheck */
1345 push Info
1346 push edi
1347 push [edi+KINTERRUPT_SERVICE_CONTEXT]
1348 push [edi+KINTERRUPT_SERVICE_ROUTINE]
1349 push HARDWARE_INTERRUPT_STORM
1350 call _KeBugCheckEx@20
1351
1352 1:
1353 /* Debugger enabled, do a debug print + break instead */
1354 push [edi+KINTERRUPT_SERVICE_ROUTINE]
1355 push offset _IsrOverflowMsg
1356 call _DbgPrint
1357 add esp, 8
1358 int 3
1359
1360 /* Breakpoint handled, get the new tick count */
1361 mov eax, _KeTickCount
1362 and eax, 0xC0
1363
1364 VfRst_&Label:
1365 /* Reset tick count */
1366 mov dword ptr [edi+KINTERRUPT_TICK_COUNT], eax
1367 mov word ptr [edi+KINTERRUPT_DISPATCH_COUNT+2], 64
1368
1369 VfRstDef_&Label:
1370 /* Put default overflow count and continue */
1371 mov ax, _KiISROverflow
1372 mov word ptr [edi+KINTERRUPT_DISPATCH_COUNT], ax
1373 jmp Vf_&Label
1374
1375 VfWrap_&Label:
1376 /* Check if we wrapped */
1377 add eax, 0x40
1378 cmp eax, [edi+KINTERRUPT_TICK_COUNT]
1379 je Vf_&Label
1380
1381 /* We did, start over */
1382 mov eax, _KeTickCount
1383 jmp VfRst_&Label
1384 .endm
1385
1386 #else
1387
1388 /* We don't verify interrupts on retail builds */
1389 .macro VERIFY_INT Label
1390 .endm
1391 .macro VERIFY_INT_END Label, Info
1392 .endm
1393
1394 #endif
1395
1396