Put #endif where it should be.
[reactos.git] / reactos / ntoskrnl / include / internal / trap_x.h
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/include/trap_x.h
5 * PURPOSE: Internal Inlined Functions for the Trap Handling Code
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 #ifdef __GNUC__
10 #if __GNUC__ * 100 + __GNUC_MINOR__ >= 405
11 #define UNREACHABLE __builtin_unreachable()
12 #else
13 #define UNREACHABLE __builtin_trap()
14 #endif
15 #else /* not __GNUC__ */
16 #define UNREACHABLE
17 #endif
18
19 //
20 // Debug Macros
21 //
22 VOID
23 FORCEINLINE
24 KiDumpTrapFrame(IN PKTRAP_FRAME TrapFrame)
25 {
26 /* Dump the whole thing */
27 DPRINT1("DbgEbp: %x\n", TrapFrame->DbgEbp);
28 DPRINT1("DbgEip: %x\n", TrapFrame->DbgEip);
29 DPRINT1("DbgArgMark: %x\n", TrapFrame->DbgArgMark);
30 DPRINT1("DbgArgPointer: %x\n", TrapFrame->DbgArgPointer);
31 DPRINT1("TempSegCs: %x\n", TrapFrame->TempSegCs);
32 DPRINT1("TempEsp: %x\n", TrapFrame->TempEsp);
33 DPRINT1("Dr0: %x\n", TrapFrame->Dr0);
34 DPRINT1("Dr1: %x\n", TrapFrame->Dr1);
35 DPRINT1("Dr2: %x\n", TrapFrame->Dr2);
36 DPRINT1("Dr3: %x\n", TrapFrame->Dr3);
37 DPRINT1("Dr6: %x\n", TrapFrame->Dr6);
38 DPRINT1("Dr7: %x\n", TrapFrame->Dr7);
39 DPRINT1("SegGs: %x\n", TrapFrame->SegGs);
40 DPRINT1("SegEs: %x\n", TrapFrame->SegEs);
41 DPRINT1("SegDs: %x\n", TrapFrame->SegDs);
42 DPRINT1("Edx: %x\n", TrapFrame->Edx);
43 DPRINT1("Ecx: %x\n", TrapFrame->Ecx);
44 DPRINT1("Eax: %x\n", TrapFrame->Eax);
45 DPRINT1("PreviousPreviousMode: %x\n", TrapFrame->PreviousPreviousMode);
46 DPRINT1("ExceptionList: %x\n", TrapFrame->ExceptionList);
47 DPRINT1("SegFs: %x\n", TrapFrame->SegFs);
48 DPRINT1("Edi: %x\n", TrapFrame->Edi);
49 DPRINT1("Esi: %x\n", TrapFrame->Esi);
50 DPRINT1("Ebx: %x\n", TrapFrame->Ebx);
51 DPRINT1("Ebp: %x\n", TrapFrame->Ebp);
52 DPRINT1("ErrCode: %x\n", TrapFrame->ErrCode);
53 DPRINT1("Eip: %x\n", TrapFrame->Eip);
54 DPRINT1("SegCs: %x\n", TrapFrame->SegCs);
55 DPRINT1("EFlags: %x\n", TrapFrame->EFlags);
56 DPRINT1("HardwareEsp: %x\n", TrapFrame->HardwareEsp);
57 DPRINT1("HardwareSegSs: %x\n", TrapFrame->HardwareSegSs);
58 DPRINT1("V86Es: %x\n", TrapFrame->V86Es);
59 DPRINT1("V86Ds: %x\n", TrapFrame->V86Ds);
60 DPRINT1("V86Fs: %x\n", TrapFrame->V86Fs);
61 DPRINT1("V86Gs: %x\n", TrapFrame->V86Gs);
62 }
63
64 #if YDEBUG
65 FORCEINLINE
66 VOID
67 KiFillTrapFrameDebug(IN PKTRAP_FRAME TrapFrame)
68 {
69 /* Set the debug information */
70 TrapFrame->DbgArgPointer = TrapFrame->Edx;
71 TrapFrame->DbgArgMark = 0xBADB0D00;
72 TrapFrame->DbgEip = TrapFrame->Eip;
73 TrapFrame->DbgEbp = TrapFrame->Ebp;
74 }
75
76 FORCEINLINE
77 VOID
78 KiExitTrapDebugChecks(IN PKTRAP_FRAME TrapFrame,
79 IN KTRAP_STATE_BITS SkipBits)
80 {
81 /* Make sure interrupts are disabled */
82 if (__readeflags() & EFLAGS_INTERRUPT_MASK)
83 {
84 DPRINT1("Exiting with interrupts enabled: %lx\n", __readeflags());
85 while (TRUE);
86 }
87
88 /* Make sure this is a real trap frame */
89 if (TrapFrame->DbgArgMark != 0xBADB0D00)
90 {
91 DPRINT1("Exiting with an invalid trap frame? (No MAGIC in trap frame)\n");
92 KiDumpTrapFrame(TrapFrame);
93 while (TRUE);
94 }
95
96 /* Make sure we're not in user-mode or something */
97 if (Ke386GetFs() != KGDT_R0_PCR)
98 {
99 DPRINT1("Exiting with an invalid FS: %lx\n", Ke386GetFs());
100 while (TRUE);
101 }
102
103 /* Make sure we have a valid SEH chain */
104 if (KeGetPcr()->Tib.ExceptionList == 0)
105 {
106 DPRINT1("Exiting with NULL exception chain: %p\n", KeGetPcr()->Tib.ExceptionList);
107 while (TRUE);
108 }
109
110 /* Make sure we're restoring a valid SEH chain */
111 if (TrapFrame->ExceptionList == 0)
112 {
113 DPRINT1("Entered a trap with a NULL exception chain: %p\n", TrapFrame->ExceptionList);
114 while (TRUE);
115 }
116
117 /* If we're ignoring previous mode, make sure caller doesn't actually want it */
118 if ((SkipBits.SkipPreviousMode) && (TrapFrame->PreviousPreviousMode != -1))
119 {
120 DPRINT1("Exiting a trap witout restoring previous mode, yet previous mode seems valid: %lx", TrapFrame->PreviousPreviousMode);
121 while (TRUE);
122 }
123 }
124
125 FORCEINLINE
126 VOID
127 KiExitSystemCallDebugChecks(IN ULONG SystemCall,
128 IN PKTRAP_FRAME TrapFrame)
129 {
130 KIRQL OldIrql;
131
132 /* Check if this was a user call */
133 if (KiUserMode(TrapFrame))
134 {
135 /* Make sure we are not returning with elevated IRQL */
136 OldIrql = KeGetCurrentIrql();
137 if (OldIrql != PASSIVE_LEVEL)
138 {
139 /* Forcibly put us in a sane state */
140 KeGetPcr()->CurrentIrql = PASSIVE_LEVEL;
141 _disable();
142
143 /* Fail */
144 KeBugCheckEx(IRQL_GT_ZERO_AT_SYSTEM_SERVICE,
145 SystemCall,
146 OldIrql,
147 0,
148 0);
149 }
150
151 /* Make sure we're not attached and that APCs are not disabled */
152 if ((KeGetCurrentThread()->ApcStateIndex != CurrentApcEnvironment) ||
153 (KeGetCurrentThread()->CombinedApcDisable != 0))
154 {
155 /* Fail */
156 KeBugCheckEx(APC_INDEX_MISMATCH,
157 SystemCall,
158 KeGetCurrentThread()->ApcStateIndex,
159 KeGetCurrentThread()->CombinedApcDisable,
160 0);
161 }
162 }
163 }
164 #else
165 #define KiExitTrapDebugChecks(x, y)
166 #define KiFillTrapFrameDebug(x)
167 #define KiExitSystemCallDebugChecks(x, y)
168 #endif
169
170 //
171 // Helper Code
172 //
173 BOOLEAN
174 FORCEINLINE
175 KiUserTrap(IN PKTRAP_FRAME TrapFrame)
176 {
177 /* Anything else but Ring 0 is Ring 3 */
178 return (TrapFrame->SegCs & MODE_MASK);
179 }
180
181 BOOLEAN
182 FORCEINLINE
183 KiVdmTrap(IN PKTRAP_FRAME TrapFrame)
184 {
185 /* Either the V8086 flag is on, or this is user-mode with a VDM */
186 return ((TrapFrame->EFlags & EFLAGS_V86_MASK) ||
187 ((KiUserTrap(TrapFrame)) && (PsGetCurrentProcess()->VdmObjects)));
188 }
189
190 VOID
191 FORCEINLINE
192 KiCheckForApcDelivery(IN PKTRAP_FRAME TrapFrame)
193 {
194 PKTHREAD Thread;
195 KIRQL OldIrql;
196
197 /* Check for V8086 or user-mode trap */
198 if ((TrapFrame->EFlags & EFLAGS_V86_MASK) ||
199 (KiUserTrap(TrapFrame)))
200 {
201 /* Get the thread */
202 Thread = KeGetCurrentThread();
203 while (TRUE)
204 {
205 /* Turn off the alerted state for kernel mode */
206 Thread->Alerted[KernelMode] = FALSE;
207
208 /* Are there pending user APCs? */
209 if (!Thread->ApcState.UserApcPending) break;
210
211 /* Raise to APC level and enable interrupts */
212 OldIrql = KfRaiseIrql(APC_LEVEL);
213 _enable();
214
215 /* Deliver APCs */
216 KiDeliverApc(UserMode, NULL, TrapFrame);
217
218 /* Restore IRQL and disable interrupts once again */
219 KfLowerIrql(OldIrql);
220 _disable();
221 }
222 }
223 }
224
225 VOID
226 FORCEINLINE
227 DECLSPEC_NORETURN
228 KiDispatchException0Args(IN NTSTATUS Code,
229 IN ULONG_PTR Address,
230 IN PKTRAP_FRAME TrapFrame)
231 {
232 /* Helper for exceptions with no arguments */
233 KiDispatchExceptionFromTrapFrame(Code, Address, 0, 0, 0, 0, TrapFrame);
234 }
235
236 VOID
237 FORCEINLINE
238 DECLSPEC_NORETURN
239 KiDispatchException1Args(IN NTSTATUS Code,
240 IN ULONG_PTR Address,
241 IN ULONG P1,
242 IN PKTRAP_FRAME TrapFrame)
243 {
244 /* Helper for exceptions with no arguments */
245 KiDispatchExceptionFromTrapFrame(Code, Address, 1, P1, 0, 0, TrapFrame);
246 }
247
248 VOID
249 FORCEINLINE
250 DECLSPEC_NORETURN
251 KiDispatchException2Args(IN NTSTATUS Code,
252 IN ULONG_PTR Address,
253 IN ULONG P1,
254 IN ULONG P2,
255 IN PKTRAP_FRAME TrapFrame)
256 {
257 /* Helper for exceptions with no arguments */
258 KiDispatchExceptionFromTrapFrame(Code, Address, 2, P1, P2, 0, TrapFrame);
259 }
260
261 FORCEINLINE
262 VOID
263 DECLSPEC_NORETURN
264 KiSystemCallReturn(IN PKTRAP_FRAME TrapFrame)
265 {
266 /* Restore nonvolatiles, EAX, and do a "jump" back to the kernel caller */
267 __asm__ __volatile__
268 (
269 "movl %0, %%esp\n"
270 "movl %c[b](%%esp), %%ebx\n"
271 "movl %c[s](%%esp), %%esi\n"
272 "movl %c[i](%%esp), %%edi\n"
273 "movl %c[p](%%esp), %%ebp\n"
274 "movl %c[a](%%esp), %%eax\n"
275 "movl %c[e](%%esp), %%edx\n"
276 "addl $%c[v],%%esp\n" /* A WHOLE *KERNEL* frame since we're not IRET'ing */
277 "jmp *%%edx\n"
278 :
279 : "r"(TrapFrame),
280 [b] "i"(KTRAP_FRAME_EBX),
281 [s] "i"(KTRAP_FRAME_ESI),
282 [i] "i"(KTRAP_FRAME_EDI),
283 [p] "i"(KTRAP_FRAME_EBP),
284 [a] "i"(KTRAP_FRAME_EAX),
285 [e] "i"(KTRAP_FRAME_EIP),
286 [v] "i"(KTRAP_FRAME_ESP)
287 : "%esp"
288 );
289 UNREACHABLE;
290 }
291
292 FORCEINLINE
293 VOID
294 DECLSPEC_NORETURN
295 KiSystemCallTrapReturn(IN PKTRAP_FRAME TrapFrame)
296 {
297 /* Regular interrupt exit, but we only restore EAX as a volatile */
298 __asm__ __volatile__
299 (
300 "movl %0, %%esp\n"
301 "movl %c[b](%%esp), %%ebx\n"
302 "movl %c[s](%%esp), %%esi\n"
303 "movl %c[i](%%esp), %%edi\n"
304 "movl %c[p](%%esp), %%ebp\n"
305 "movl %c[a](%%esp), %%eax\n"
306 "addl $%c[e],%%esp\n"
307 "iret\n"
308 :
309 : "r"(TrapFrame),
310 [b] "i"(KTRAP_FRAME_EBX),
311 [s] "i"(KTRAP_FRAME_ESI),
312 [i] "i"(KTRAP_FRAME_EDI),
313 [p] "i"(KTRAP_FRAME_EBP),
314 [a] "i"(KTRAP_FRAME_EAX),
315 [e] "i"(KTRAP_FRAME_EIP)
316 : "%esp"
317 );
318 UNREACHABLE;
319 }
320
321 FORCEINLINE
322 VOID
323 DECLSPEC_NORETURN
324 KiSystemCallSysExitReturn(IN PKTRAP_FRAME TrapFrame)
325 {
326 /* Restore nonvolatiles, EAX, and do a SYSEXIT back to the user caller */
327 __asm__ __volatile__
328 (
329 "movl %0, %%esp\n"
330 "movl %c[s](%%esp), %%esi\n"
331 "movl %c[b](%%esp), %%ebx\n"
332 "movl %c[i](%%esp), %%edi\n"
333 "movl %c[p](%%esp), %%ebp\n"
334 "movl %c[a](%%esp), %%eax\n"
335 "movl %c[e](%%esp), %%edx\n" /* SYSEXIT says EIP in EDX */
336 "movl %c[x](%%esp), %%ecx\n" /* SYSEXIT says ESP in ECX */
337 "addl $%c[v],%%esp\n" /* A WHOLE *USER* frame since we're not IRET'ing */
338 "sti\nsysexit\n"
339 :
340 : "r"(TrapFrame),
341 [b] "i"(KTRAP_FRAME_EBX),
342 [s] "i"(KTRAP_FRAME_ESI),
343 [i] "i"(KTRAP_FRAME_EDI),
344 [p] "i"(KTRAP_FRAME_EBP),
345 [a] "i"(KTRAP_FRAME_EAX),
346 [e] "i"(KTRAP_FRAME_EIP),
347 [x] "i"(KTRAP_FRAME_ESP),
348 [v] "i"(KTRAP_FRAME_V86_ES)
349 : "%esp"
350 );
351 UNREACHABLE;
352 }
353
354 FORCEINLINE
355 VOID
356 DECLSPEC_NORETURN
357 KiTrapReturn(IN PKTRAP_FRAME TrapFrame)
358 {
359 /* Regular interrupt exit */
360 __asm__ __volatile__
361 (
362 "movl %0, %%esp\n"
363 "movl %c[a](%%esp), %%eax\n"
364 "movl %c[b](%%esp), %%ebx\n"
365 "movl %c[c](%%esp), %%ecx\n"
366 "movl %c[d](%%esp), %%edx\n"
367 "movl %c[s](%%esp), %%esi\n"
368 "movl %c[i](%%esp), %%edi\n"
369 "movl %c[p](%%esp), %%ebp\n"
370 "addl $%c[e],%%esp\n"
371 "iret\n"
372 :
373 : "r"(TrapFrame),
374 [a] "i"(KTRAP_FRAME_EAX),
375 [b] "i"(KTRAP_FRAME_EBX),
376 [c] "i"(KTRAP_FRAME_ECX),
377 [d] "i"(KTRAP_FRAME_EDX),
378 [s] "i"(KTRAP_FRAME_ESI),
379 [i] "i"(KTRAP_FRAME_EDI),
380 [p] "i"(KTRAP_FRAME_EBP),
381 [e] "i"(KTRAP_FRAME_EIP)
382 : "%esp"
383 );
384 UNREACHABLE;
385 }
386
387 FORCEINLINE
388 VOID
389 DECLSPEC_NORETURN
390 KiEditedTrapReturn(IN PKTRAP_FRAME TrapFrame)
391 {
392 /* Regular interrupt exit */
393 __asm__ __volatile__
394 (
395 "movl %0, %%esp\n"
396 "movl %c[a](%%esp), %%eax\n"
397 "movl %c[b](%%esp), %%ebx\n"
398 "movl %c[c](%%esp), %%ecx\n"
399 "movl %c[d](%%esp), %%edx\n"
400 "movl %c[s](%%esp), %%esi\n"
401 "movl %c[i](%%esp), %%edi\n"
402 "movl %c[p](%%esp), %%ebp\n"
403 "addl $%c[e],%%esp\n"
404 "movl (%%esp), %%esp\n"
405 "iret\n"
406 :
407 : "r"(TrapFrame),
408 [a] "i"(KTRAP_FRAME_EAX),
409 [b] "i"(KTRAP_FRAME_EBX),
410 [c] "i"(KTRAP_FRAME_ECX),
411 [d] "i"(KTRAP_FRAME_EDX),
412 [s] "i"(KTRAP_FRAME_ESI),
413 [i] "i"(KTRAP_FRAME_EDI),
414 [p] "i"(KTRAP_FRAME_EBP),
415 [e] "i"(KTRAP_FRAME_ERROR_CODE) /* We *WANT* the error code since ESP is there! */
416 : "%esp"
417 );
418 UNREACHABLE;
419 }
420
421 NTSTATUS
422 FORCEINLINE
423 KiSystemCallTrampoline(IN PVOID Handler,
424 IN PVOID Arguments,
425 IN ULONG StackBytes)
426 {
427 NTSTATUS Result;
428
429 /*
430 * This sequence does a RtlCopyMemory(Stack - StackBytes, Arguments, StackBytes)
431 * and then calls the function associated with the system call.
432 *
433 * It's done in assembly for two reasons: we need to muck with the stack,
434 * and the call itself restores the stack back for us. The only way to do
435 * this in C is to do manual C handlers for every possible number of args on
436 * the stack, and then have the handler issue a call by pointer. This is
437 * wasteful since it'll basically push the values twice and require another
438 * level of call indirection.
439 *
440 * The ARM kernel currently does this, but it should probably be changed
441 * later to function like this as well.
442 *
443 */
444 __asm__ __volatile__
445 (
446 "subl %1, %%esp\n"
447 "movl %%esp, %%edi\n"
448 "movl %2, %%esi\n"
449 "shrl $2, %1\n"
450 "rep movsd\n"
451 "call *%3\n"
452 "movl %%eax, %0\n"
453 : "=r"(Result)
454 : "c"(StackBytes),
455 "d"(Arguments),
456 "r"(Handler)
457 : "%esp", "%esi", "%edi"
458 );
459
460 return Result;
461 }
462
463 NTSTATUS
464 FORCEINLINE
465 KiConvertToGuiThread(VOID)
466 {
467 NTSTATUS Result;
468 PVOID StackFrame;
469
470 /*
471 * Converting to a GUI thread safely updates ESP in-place as well as the
472 * current Thread->TrapFrame and EBP when KeSwitchKernelStack is called.
473 *
474 * However, PsConvertToGuiThread "helpfully" restores EBP to the original
475 * caller's value, since it is considered a nonvolatile register. As such,
476 * as soon as we're back after the conversion and we try to store the result
477 * which will probably be in some stack variable (EBP-based), we'll crash as
478 * we are touching the de-allocated non-expanded stack.
479 *
480 * Thus we need a way to update our EBP before EBP is touched, and the only
481 * way to guarantee this is to do the call itself in assembly, use the EAX
482 * register to store the result, fixup EBP, and then let the C code continue
483 * on its merry way.
484 *
485 */
486 __asm__ __volatile__
487 (
488 "movl %%ebp, %1\n"
489 "subl %%esp, %1\n"
490 "call _PsConvertToGuiThread@0\n"
491 "addl %%esp, %1\n"
492 "movl %1, %%ebp\n"
493 "movl %%eax, %0\n"
494 : "=r"(Result), "=r"(StackFrame)
495 :
496 : "%esp", "%ecx", "%edx"
497 );
498
499 return Result;
500 }
501
502 VOID
503 FORCEINLINE
504 KiSwitchToBootStack(IN ULONG_PTR InitialStack)
505 {
506 /* We have to switch to a new stack before continuing kernel initialization */
507 __asm__ __volatile__
508 (
509 "movl %0, %%esp\n"
510 "subl %1, %%esp\n"
511 "pushl %2\n"
512 "jmp _KiSystemStartupBootStack@0\n"
513 :
514 : "c"(InitialStack),
515 "i"(NPX_FRAME_LENGTH + KTRAP_FRAME_ALIGN + KTRAP_FRAME_LENGTH),
516 "i"(CR0_EM | CR0_TS | CR0_MP)
517 : "%esp"
518 );
519 }