[NTOS]: Implement the KiInterruptTemplate in C since we now have a C entrry macro...
[reactos.git] / reactos / ntoskrnl / include / internal / i386 / ke.h
1 #ifndef __NTOSKRNL_INCLUDE_INTERNAL_I386_KE_H
2 #define __NTOSKRNL_INCLUDE_INTERNAL_I386_KE_H
3
4 #ifndef __ASM__
5
6 #include "intrin_i.h"
7 #include "v86m.h"
8
9 //
10 // Thread Dispatcher Header DebugActive Mask
11 //
12 #define DR_MASK(x) (1 << (x))
13 #define DR_REG_MASK 0x4F
14
15 #define IMAGE_FILE_MACHINE_ARCHITECTURE IMAGE_FILE_MACHINE_I386
16
17 //
18 // INT3 is 1 byte long
19 //
20 #define KD_BREAKPOINT_TYPE UCHAR
21 #define KD_BREAKPOINT_SIZE sizeof(UCHAR)
22 #define KD_BREAKPOINT_VALUE 0xCC
23
24 //
25 // Macros for getting and setting special purpose registers in portable code
26 //
27 #define KeGetContextPc(Context) \
28 ((Context)->Eip)
29
30 #define KeSetContextPc(Context, ProgramCounter) \
31 ((Context)->Eip = (ProgramCounter))
32
33 #define KeGetTrapFramePc(TrapFrame) \
34 ((TrapFrame)->Eip)
35
36 #define KeGetContextReturnRegister(Context) \
37 ((Context)->Eax)
38
39 #define KeSetContextReturnRegister(Context, ReturnValue) \
40 ((Context)->Eax = (ReturnValue))
41
42 //
43 // Macro to get trap and exception frame from a thread stack
44 //
45 #define KeGetTrapFrame(Thread) \
46 (PKTRAP_FRAME)((ULONG_PTR)((Thread)->InitialStack) - \
47 sizeof(KTRAP_FRAME) - \
48 sizeof(FX_SAVE_AREA))
49
50 #define KeGetExceptionFrame(Thread) \
51 NULL
52
53 //
54 // Macro to get context switches from the PRCB
55 // All architectures but x86 have it in the PRCB's KeContextSwitches
56 //
57 #define KeGetContextSwitches(Prcb) \
58 CONTAINING_RECORD(Prcb, KIPCR, PrcbData)->ContextSwitches
59
60 //
61 // Returns the Interrupt State from a Trap Frame.
62 // ON = TRUE, OFF = FALSE
63 //
64 #define KeGetTrapFrameInterruptState(TrapFrame) \
65 BooleanFlagOn((TrapFrame)->EFlags, EFLAGS_INTERRUPT_MASK)
66
67 //
68 // Flags for exiting a trap
69 //
70 #define KTE_SKIP_PM_BIT (((KTRAP_EXIT_SKIP_BITS) { { .SkipPreviousMode = TRUE } }).Bits)
71 #define KTE_SKIP_SEG_BIT (((KTRAP_EXIT_SKIP_BITS) { { .SkipSegments = TRUE } }).Bits)
72 #define KTE_SKIP_VOL_BIT (((KTRAP_EXIT_SKIP_BITS) { { .SkipVolatiles = TRUE } }).Bits)
73
74 typedef union _KTRAP_EXIT_SKIP_BITS
75 {
76 struct
77 {
78 UCHAR SkipPreviousMode:1;
79 UCHAR SkipSegments:1;
80 UCHAR SkipVolatiles:1;
81 UCHAR Reserved:5;
82 };
83 UCHAR Bits;
84 } KTRAP_EXIT_SKIP_BITS, *PKTRAP_EXIT_SKIP_BITS;
85
86
87 //
88 // Flags used by the VDM/V8086 emulation engine for determining instruction prefixes
89 //
90 #define PFX_FLAG_ES 0x00000100
91 #define PFX_FLAG_CS 0x00000200
92 #define PFX_FLAG_SS 0x00000400
93 #define PFX_FLAG_DS 0x00000800
94 #define PFX_FLAG_FS 0x00001000
95 #define PFX_FLAG_GS 0x00002000
96 #define PFX_FLAG_OPER32 0x00004000
97 #define PFX_FLAG_ADDR32 0x00008000
98 #define PFX_FLAG_LOCK 0x00010000
99 #define PFX_FLAG_REPNE 0x00020000
100 #define PFX_FLAG_REP 0x00040000
101
102 //
103 // VDM Helper Macros
104 //
105 // All VDM/V8086 opcode emulators have the same FASTCALL function definition.
106 // We need to keep 2 parameters while the original ASM implementation uses 4:
107 // TrapFrame, PrefixFlags, Eip, InstructionSize;
108 //
109 // We pass the trap frame, and prefix flags, in our two parameters.
110 //
111 // We then realize that since the smallest prefix flag is 0x100, this gives us
112 // a count of up to 0xFF. So we OR in the instruction size with the prefix flags
113 //
114 // We further realize that we always have access to EIP from the trap frame, and
115 // that if we want the *current instruction* EIP, we simply have to add the
116 // instruction size *MINUS ONE*, and that gives us the EIP we should be looking
117 // at now, so we don't need to use the stack to push this parameter.
118 //
119 // We actually only care about the *current instruction* EIP in one location,
120 // so although it may be slightly more expensive to re-calculate the EIP one
121 // more time, this way we don't redefine ALL opcode handlers to have 3 parameters,
122 // which would be forcing stack usage in all other scenarios.
123 //
124 #define KiVdmSetVdmEFlags(x) InterlockedOr((PLONG)KiNtVdmState, (x));
125 #define KiVdmClearVdmEFlags(x) InterlockedAnd((PLONG)KiNtVdmState, ~(x))
126 #define KiCallVdmHandler(x) KiVdmOpcode##x(TrapFrame, Flags)
127 #define KiCallVdmPrefixHandler(x) KiVdmOpcodePrefix(TrapFrame, Flags | x)
128 #define KiVdmUnhandledOpcode(x) \
129 BOOLEAN \
130 FASTCALL \
131 KiVdmOpcode##x(IN PKTRAP_FRAME TrapFrame, \
132 IN ULONG Flags) \
133 { \
134 /* Not yet handled */ \
135 UNIMPLEMENTED; \
136 while (TRUE); \
137 return TRUE; \
138 }
139
140 C_ASSERT(NPX_FRAME_LENGTH == sizeof(FX_SAVE_AREA));
141
142 //
143 // Local parameters
144 //
145 typedef struct _KV86_FRAME
146 {
147 PVOID ThreadStack;
148 PVOID ThreadTeb;
149 PVOID PcrTeb;
150 } KV86_FRAME, *PKV86_FRAME;
151
152 //
153 // Virtual Stack Frame
154 //
155 typedef struct _KV8086_STACK_FRAME
156 {
157 KTRAP_FRAME TrapFrame;
158 FX_SAVE_AREA NpxArea;
159 KV86_FRAME V86Frame;
160 } KV8086_STACK_FRAME, *PKV8086_STACK_FRAME;
161
162 //
163 // Registers an interrupt handler with an IDT vector
164 //
165 FORCEINLINE
166 VOID
167 KeRegisterInterruptHandler(IN ULONG Vector,
168 IN PVOID Handler)
169 {
170 UCHAR Entry;
171 ULONG_PTR Address;
172 PKIPCR Pcr = (PKIPCR)KeGetPcr();
173
174 //
175 // Get the entry from the HAL
176 //
177 Entry = HalVectorToIDTEntry(Vector);
178 Address = PtrToUlong(Handler);
179
180 //
181 // Now set the data
182 //
183 Pcr->IDT[Entry].ExtendedOffset = (USHORT)(Address >> 16);
184 Pcr->IDT[Entry].Offset = (USHORT)Address;
185 }
186
187 //
188 // Returns the registered interrupt handler for a given IDT vector
189 //
190 FORCEINLINE
191 PVOID
192 KeQueryInterruptHandler(IN ULONG Vector)
193 {
194 PKIPCR Pcr = (PKIPCR)KeGetPcr();
195 UCHAR Entry;
196
197 //
198 // Get the entry from the HAL
199 //
200 Entry = HalVectorToIDTEntry(Vector);
201
202 //
203 // Read the entry from the IDT
204 //
205 return (PVOID)(((Pcr->IDT[Entry].ExtendedOffset << 16) & 0xFFFF0000) |
206 (Pcr->IDT[Entry].Offset & 0xFFFF));
207 }
208
209 //
210 // Invalidates the TLB entry for a specified address
211 //
212 FORCEINLINE
213 VOID
214 KeInvalidateTlbEntry(IN PVOID Address)
215 {
216 /* Invalidate the TLB entry for this address */
217 __invlpg(Address);
218 }
219
220 FORCEINLINE
221 VOID
222 KeFlushProcessTb(VOID)
223 {
224 /* Flush the TLB by resetting CR3 */
225 __writecr3(__readcr3());
226 }
227
228 FORCEINLINE
229 PRKTHREAD
230 KeGetCurrentThread(VOID)
231 {
232 /* Return the current thread */
233 return ((PKIPCR)KeGetPcr())->PrcbData.CurrentThread;
234 }
235
236 FORCEINLINE
237 VOID
238 KiRundownThread(IN PKTHREAD Thread)
239 {
240 #ifndef CONFIG_SMP
241 /* Check if this is the NPX Thread */
242 if (KeGetCurrentPrcb()->NpxThread == Thread)
243 {
244 /* Clear it */
245 KeGetCurrentPrcb()->NpxThread = NULL;
246 Ke386FnInit();
247 }
248 #else
249 /* Nothing to do */
250 #endif
251 }
252
253 VOID
254 FASTCALL
255 Ki386InitializeTss(
256 IN PKTSS Tss,
257 IN PKIDTENTRY Idt,
258 IN PKGDTENTRY Gdt
259 );
260
261 VOID
262 NTAPI
263 KiSetCR0Bits(VOID);
264
265 VOID
266 NTAPI
267 KiGetCacheInformation(VOID);
268
269 BOOLEAN
270 NTAPI
271 KiIsNpxPresent(
272 VOID
273 );
274
275 BOOLEAN
276 NTAPI
277 KiIsNpxErrataPresent(
278 VOID
279 );
280
281 VOID
282 NTAPI
283 KiSetProcessorType(VOID);
284
285 ULONG
286 NTAPI
287 KiGetFeatureBits(VOID);
288
289 VOID
290 NTAPI
291 KiThreadStartup(VOID);
292
293 NTSTATUS
294 NTAPI
295 Ke386GetGdtEntryThread(
296 IN PKTHREAD Thread,
297 IN ULONG Offset,
298 IN PKGDTENTRY Descriptor
299 );
300
301 VOID
302 NTAPI
303 KiFlushNPXState(
304 IN FLOATING_SAVE_AREA *SaveArea
305 );
306
307 VOID
308 NTAPI
309 Ki386AdjustEsp0(
310 IN PKTRAP_FRAME TrapFrame
311 );
312
313 VOID
314 NTAPI
315 Ki386SetupAndExitToV86Mode(
316 OUT PTEB VdmTeb
317 );
318
319 VOID
320 NTAPI
321 KeI386VdmInitialize(
322 VOID
323 );
324
325 ULONG_PTR
326 NTAPI
327 Ki386EnableGlobalPage(
328 IN volatile ULONG_PTR Context
329 );
330
331 VOID
332 NTAPI
333 KiI386PentiumLockErrataFixup(
334 VOID
335 );
336
337 VOID
338 NTAPI
339 KiInitializePAT(
340 VOID
341 );
342
343 VOID
344 NTAPI
345 KiInitializeMTRR(
346 IN BOOLEAN FinalCpu
347 );
348
349 VOID
350 NTAPI
351 KiAmdK6InitializeMTRR(
352 VOID
353 );
354
355 VOID
356 NTAPI
357 KiRestoreFastSyscallReturnState(
358 VOID
359 );
360
361 ULONG_PTR
362 NTAPI
363 Ki386EnableDE(
364 IN ULONG_PTR Context
365 );
366
367 ULONG_PTR
368 NTAPI
369 Ki386EnableFxsr(
370 IN ULONG_PTR Context
371 );
372
373 ULONG_PTR
374 NTAPI
375 Ki386EnableXMMIExceptions(
376 IN ULONG_PTR Context
377 );
378
379 BOOLEAN
380 NTAPI
381 VdmDispatchBop(
382 IN PKTRAP_FRAME TrapFrame
383 );
384
385 BOOLEAN
386 FASTCALL
387 KiVdmOpcodePrefix(
388 IN PKTRAP_FRAME TrapFrame,
389 IN ULONG Flags
390 );
391
392 BOOLEAN
393 FASTCALL
394 Ki386HandleOpcodeV86(
395 IN PKTRAP_FRAME TrapFrame
396 );
397
398 VOID
399 FASTCALL
400 DECLSPEC_NORETURN
401 KiEoiHelper(
402 IN PKTRAP_FRAME TrapFrame
403 );
404
405 VOID
406 FASTCALL
407 Ki386BiosCallReturnAddress(
408 IN PKTRAP_FRAME TrapFrame
409 );
410
411 ULONG_PTR
412 FASTCALL
413 KiExitV86Mode(
414 IN PKTRAP_FRAME TrapFrame
415 );
416
417 VOID
418 NTAPI
419 DECLSPEC_NORETURN
420 KiDispatchExceptionFromTrapFrame(
421 IN NTSTATUS Code,
422 IN ULONG_PTR Address,
423 IN ULONG ParameterCount,
424 IN ULONG_PTR Parameter1,
425 IN ULONG_PTR Parameter2,
426 IN ULONG_PTR Parameter3,
427 IN PKTRAP_FRAME TrapFrame
428 );
429
430 //
431 // Global x86 only Kernel data
432 //
433 extern PVOID Ki386IopmSaveArea;
434 extern ULONG KeI386EFlagsAndMaskV86;
435 extern ULONG KeI386EFlagsOrMaskV86;
436 extern BOOLEAN KeI386VirtualIntExtensions;
437 extern KIDTENTRY KiIdt[MAXIMUM_IDTVECTOR];
438 extern KDESCRIPTOR KiIdtDescriptor;
439 extern ULONG Ke386GlobalPagesEnabled;
440 extern BOOLEAN KiI386PentiumLockErrataPresent;
441 extern ULONG KeI386NpxPresent;
442 extern ULONG KeI386XMMIPresent;
443 extern ULONG KeI386FxsrPresent;
444 extern ULONG KiMXCsrMask;
445 extern ULONG KeI386CpuType;
446 extern ULONG KeI386CpuStep;
447 extern ULONG Ke386CacheAlignment;
448 extern ULONG KiFastSystemCallDisable;
449 extern UCHAR KiDebugRegisterTrapOffsets[9];
450 extern UCHAR KiDebugRegisterContextOffsets[9];
451 extern VOID __cdecl KiTrap02(VOID);
452 extern VOID __cdecl KiTrap08(VOID);
453 extern VOID __cdecl KiTrap13(VOID);
454 extern VOID __cdecl KiInterruptTemplate(VOID);
455 extern VOID __cdecl KiFastCallEntry(VOID);
456 extern VOID NTAPI ExpInterlockedPopEntrySListFault(VOID);
457 extern VOID __cdecl CopyParams(VOID);
458 extern VOID __cdecl ReadBatch(VOID);
459 extern VOID __cdecl FrRestore(VOID);
460 extern CHAR KiSystemCallExitBranch[];
461 extern CHAR KiSystemCallExit[];
462 extern CHAR KiSystemCallExit2[];
463
464 //
465 // Trap Macros
466 //
467 #include "../trap_x.h"
468
469 //
470 // Returns a thread's FPU save area
471 //
472 PFX_SAVE_AREA
473 FORCEINLINE
474 KiGetThreadNpxArea(IN PKTHREAD Thread)
475 {
476 return (PFX_SAVE_AREA)((ULONG_PTR)Thread->InitialStack - sizeof(FX_SAVE_AREA));
477 }
478
479 //
480 // Sanitizes a selector
481 //
482 FORCEINLINE
483 ULONG
484 Ke386SanitizeSeg(IN ULONG Cs,
485 IN KPROCESSOR_MODE Mode)
486 {
487 //
488 // Check if we're in kernel-mode, and force CPL 0 if so.
489 // Otherwise, force CPL 3.
490 //
491 return ((Mode == KernelMode) ?
492 (Cs & (0xFFFF & ~RPL_MASK)) :
493 (RPL_MASK | (Cs & 0xFFFF)));
494 }
495
496 //
497 // Sanitizes EFLAGS
498 //
499 FORCEINLINE
500 ULONG
501 Ke386SanitizeFlags(IN ULONG Eflags,
502 IN KPROCESSOR_MODE Mode)
503 {
504 //
505 // Check if we're in kernel-mode, and sanitize EFLAGS if so.
506 // Otherwise, also force interrupt mask on.
507 //
508 return ((Mode == KernelMode) ?
509 (Eflags & (EFLAGS_USER_SANITIZE | EFLAGS_INTERRUPT_MASK)) :
510 (EFLAGS_INTERRUPT_MASK | (Eflags & EFLAGS_USER_SANITIZE)));
511 }
512
513 //
514 // Gets a DR register from a CONTEXT structure
515 //
516 FORCEINLINE
517 PVOID
518 KiDrFromContext(IN ULONG Dr,
519 IN PCONTEXT Context)
520 {
521 return *(PVOID*)((ULONG_PTR)Context + KiDebugRegisterContextOffsets[Dr]);
522 }
523
524 //
525 // Gets a DR register from a KTRAP_FRAME structure
526 //
527 FORCEINLINE
528 PVOID*
529 KiDrFromTrapFrame(IN ULONG Dr,
530 IN PKTRAP_FRAME TrapFrame)
531 {
532 return (PVOID*)((ULONG_PTR)TrapFrame + KiDebugRegisterTrapOffsets[Dr]);
533 }
534
535 //
536 // Sanitizes a Debug Register
537 //
538 FORCEINLINE
539 PVOID
540 Ke386SanitizeDr(IN PVOID DrAddress,
541 IN KPROCESSOR_MODE Mode)
542 {
543 //
544 // Check if we're in kernel-mode, and return the address directly if so.
545 // Otherwise, make sure it's not inside the kernel-mode address space.
546 // If it is, then clear the address.
547 //
548 return ((Mode == KernelMode) ? DrAddress :
549 (DrAddress <= MM_HIGHEST_USER_ADDRESS) ? DrAddress : 0);
550 }
551
552 //
553 // Exception with no arguments
554 //
555 VOID
556 FORCEINLINE
557 DECLSPEC_NORETURN
558 KiDispatchException0Args(IN NTSTATUS Code,
559 IN ULONG_PTR Address,
560 IN PKTRAP_FRAME TrapFrame)
561 {
562 /* Helper for exceptions with no arguments */
563 KiDispatchExceptionFromTrapFrame(Code, Address, 0, 0, 0, 0, TrapFrame);
564 }
565
566 //
567 // Exception with one argument
568 //
569 VOID
570 FORCEINLINE
571 DECLSPEC_NORETURN
572 KiDispatchException1Args(IN NTSTATUS Code,
573 IN ULONG_PTR Address,
574 IN ULONG P1,
575 IN PKTRAP_FRAME TrapFrame)
576 {
577 /* Helper for exceptions with no arguments */
578 KiDispatchExceptionFromTrapFrame(Code, Address, 1, P1, 0, 0, TrapFrame);
579 }
580
581 //
582 // Exception with two arguments
583 //
584 VOID
585 FORCEINLINE
586 DECLSPEC_NORETURN
587 KiDispatchException2Args(IN NTSTATUS Code,
588 IN ULONG_PTR Address,
589 IN ULONG P1,
590 IN ULONG P2,
591 IN PKTRAP_FRAME TrapFrame)
592 {
593 /* Helper for exceptions with no arguments */
594 KiDispatchExceptionFromTrapFrame(Code, Address, 2, P1, P2, 0, TrapFrame);
595 }
596
597 //
598 // Performs a system call
599 //
600 NTSTATUS
601 FORCEINLINE
602 KiSystemCallTrampoline(IN PVOID Handler,
603 IN PVOID Arguments,
604 IN ULONG StackBytes)
605 {
606 NTSTATUS Result;
607
608 /*
609 * This sequence does a RtlCopyMemory(Stack - StackBytes, Arguments, StackBytes)
610 * and then calls the function associated with the system call.
611 *
612 * It's done in assembly for two reasons: we need to muck with the stack,
613 * and the call itself restores the stack back for us. The only way to do
614 * this in C is to do manual C handlers for every possible number of args on
615 * the stack, and then have the handler issue a call by pointer. This is
616 * wasteful since it'll basically push the values twice and require another
617 * level of call indirection.
618 *
619 * The ARM kernel currently does this, but it should probably be changed
620 * later to function like this as well.
621 *
622 */
623 __asm__ __volatile__
624 (
625 "subl %1, %%esp\n"
626 "movl %%esp, %%edi\n"
627 "movl %2, %%esi\n"
628 "shrl $2, %1\n"
629 "rep movsd\n"
630 "call *%3\n"
631 "movl %%eax, %0\n"
632 : "=r"(Result)
633 : "c"(StackBytes),
634 "d"(Arguments),
635 "r"(Handler)
636 : "%esp", "%esi", "%edi"
637 );
638
639 return Result;
640 }
641
642 //
643 // Checks for pending APCs
644 //
645 VOID
646 FORCEINLINE
647 KiCheckForApcDelivery(IN PKTRAP_FRAME TrapFrame)
648 {
649 PKTHREAD Thread;
650 KIRQL OldIrql;
651
652 /* Check for V8086 or user-mode trap */
653 if ((TrapFrame->EFlags & EFLAGS_V86_MASK) || (KiUserTrap(TrapFrame)))
654 {
655 /* Get the thread */
656 Thread = KeGetCurrentThread();
657 while (TRUE)
658 {
659 /* Turn off the alerted state for kernel mode */
660 Thread->Alerted[KernelMode] = FALSE;
661
662 /* Are there pending user APCs? */
663 if (!Thread->ApcState.UserApcPending) break;
664
665 /* Raise to APC level and enable interrupts */
666 OldIrql = KfRaiseIrql(APC_LEVEL);
667 _enable();
668
669 /* Deliver APCs */
670 KiDeliverApc(UserMode, NULL, TrapFrame);
671
672 /* Restore IRQL and disable interrupts once again */
673 KfLowerIrql(OldIrql);
674 _disable();
675 }
676 }
677 }
678
679 //
680 // Converts a base thread to a GUI thread
681 //
682 NTSTATUS
683 FORCEINLINE
684 KiConvertToGuiThread(VOID)
685 {
686 NTSTATUS Result;
687 PVOID StackFrame;
688
689 /*
690 * Converting to a GUI thread safely updates ESP in-place as well as the
691 * current Thread->TrapFrame and EBP when KeSwitchKernelStack is called.
692 *
693 * However, PsConvertToGuiThread "helpfully" restores EBP to the original
694 * caller's value, since it is considered a nonvolatile register. As such,
695 * as soon as we're back after the conversion and we try to store the result
696 * which will probably be in some stack variable (EBP-based), we'll crash as
697 * we are touching the de-allocated non-expanded stack.
698 *
699 * Thus we need a way to update our EBP before EBP is touched, and the only
700 * way to guarantee this is to do the call itself in assembly, use the EAX
701 * register to store the result, fixup EBP, and then let the C code continue
702 * on its merry way.
703 *
704 */
705 __asm__ __volatile__
706 (
707 "movl %%ebp, %1\n"
708 "subl %%esp, %1\n"
709 "call _PsConvertToGuiThread@0\n"
710 "addl %%esp, %1\n"
711 "movl %1, %%ebp\n"
712 "movl %%eax, %0\n"
713 : "=r"(Result), "=r"(StackFrame)
714 :
715 : "%esp", "%ecx", "%edx"
716 );
717
718 return Result;
719 }
720
721 //
722 // Switches from boot loader to initial kernel stack
723 //
724 VOID
725 FORCEINLINE
726 KiSwitchToBootStack(IN ULONG_PTR InitialStack)
727 {
728 /* We have to switch to a new stack before continuing kernel initialization */
729 __asm__ __volatile__
730 (
731 "movl %0, %%esp\n"
732 "subl %1, %%esp\n"
733 "pushl %2\n"
734 "jmp _KiSystemStartupBootStack@0\n"
735 :
736 : "c"(InitialStack),
737 "i"(NPX_FRAME_LENGTH + KTRAP_FRAME_ALIGN + KTRAP_FRAME_LENGTH),
738 "i"(CR0_EM | CR0_TS | CR0_MP)
739 : "%esp"
740 );
741 }
742
743 #endif
744 #endif /* __NTOSKRNL_INCLUDE_INTERNAL_I386_KE_H */