ce4f06d3feec47c18edf52f3ff3c7efad60749af
[reactos.git] / reactos / ntoskrnl / ke / i386 / exp.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/i386/exp.c
5 * PURPOSE: Handling exceptions
6 *
7 * PROGRAMMERS: David Welch (welch@cwcom.net)
8 * Skywing (skywing@valhallalegends.com)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14
15 #define NDEBUG
16 #include <internal/debug.h>
17
18 /*
19 * FIXMES:
20 * - Put back VEH.
21 * - Clean up file.
22 * - Sanitize some context fields.
23 * - Add PSEH handler when an exception occurs in an exception (KiCopyExceptionRecord).
24 * - Implement official stack trace functions (exported) and remove stuff here.
25 * - Forward exceptions to user-mode debugger.
26 */
27
28 VOID
29 NTAPI
30 Ki386AdjustEsp0(IN PKTRAP_FRAME TrapFrame);
31
32 /* GLOBALS *****************************************************************/
33
34 #define FLAG_IF (1<<9)
35
36 #define _STR(x) #x
37 #define STR(x) _STR(x)
38
39 #ifndef ARRAY_SIZE
40 # define ARRAY_SIZE(x) (sizeof (x) / sizeof (x[0]))
41 #endif
42
43 extern void KiSystemService(void);
44 extern void KiDebugService(void);
45
46 extern VOID KiTrap0(VOID);
47 extern VOID KiTrap1(VOID);
48 extern VOID KiTrap2(VOID);
49 extern VOID KiTrap3(VOID);
50 extern VOID KiTrap4(VOID);
51 extern VOID KiTrap5(VOID);
52 extern VOID KiTrap6(VOID);
53 extern VOID KiTrap7(VOID);
54 extern VOID KiTrap8(VOID);
55 extern VOID KiTrap9(VOID);
56 extern VOID KiTrap10(VOID);
57 extern VOID KiTrap11(VOID);
58 extern VOID KiTrap12(VOID);
59 extern VOID KiTrap13(VOID);
60 extern VOID KiTrap14(VOID);
61 extern VOID KiTrap15(VOID);
62 extern VOID KiTrap16(VOID);
63 extern VOID KiTrap17(VOID);
64 extern VOID KiTrap18(VOID);
65 extern VOID KiTrap19(VOID);
66 extern VOID KiTrapUnknown(VOID);
67
68 extern ULONG init_stack;
69 extern ULONG init_stack_top;
70
71 extern BOOLEAN Ke386NoExecute;
72
73 static char *ExceptionTypeStrings[] =
74 {
75 "Divide Error",
76 "Debug Trap",
77 "NMI",
78 "Breakpoint",
79 "Overflow",
80 "BOUND range exceeded",
81 "Invalid Opcode",
82 "No Math Coprocessor",
83 "Double Fault",
84 "Unknown(9)",
85 "Invalid TSS",
86 "Segment Not Present",
87 "Stack Segment Fault",
88 "General Protection",
89 "Page Fault",
90 "Reserved(15)",
91 "Math Fault",
92 "Alignment Check",
93 "Machine Check",
94 "SIMD Fault"
95 };
96
97 NTSTATUS ExceptionToNtStatus[] =
98 {
99 STATUS_INTEGER_DIVIDE_BY_ZERO,
100 STATUS_SINGLE_STEP,
101 STATUS_ACCESS_VIOLATION,
102 STATUS_BREAKPOINT,
103 STATUS_INTEGER_OVERFLOW,
104 STATUS_ARRAY_BOUNDS_EXCEEDED,
105 STATUS_ILLEGAL_INSTRUCTION,
106 STATUS_FLOAT_INVALID_OPERATION,
107 STATUS_ACCESS_VIOLATION,
108 STATUS_ACCESS_VIOLATION,
109 STATUS_ACCESS_VIOLATION,
110 STATUS_ACCESS_VIOLATION,
111 STATUS_STACK_OVERFLOW,
112 STATUS_ACCESS_VIOLATION,
113 STATUS_ACCESS_VIOLATION,
114 STATUS_ACCESS_VIOLATION, /* RESERVED */
115 STATUS_FLOAT_INVALID_OPERATION, /* Should not be used, the FPU can give more specific info */
116 STATUS_DATATYPE_MISALIGNMENT,
117 STATUS_ACCESS_VIOLATION,
118 STATUS_FLOAT_MULTIPLE_TRAPS,
119 };
120
121 /* FUNCTIONS ****************************************************************/
122
123 BOOLEAN STDCALL
124 KiRosPrintAddress(PVOID address)
125 {
126 PLIST_ENTRY current_entry;
127 PLDR_DATA_TABLE_ENTRY current;
128 extern LIST_ENTRY ModuleListHead;
129 ULONG_PTR RelativeAddress;
130 ULONG i = 0;
131
132 do
133 {
134 current_entry = ModuleListHead.Flink;
135
136 while (current_entry != &ModuleListHead)
137 {
138 current =
139 CONTAINING_RECORD(current_entry, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList);
140
141 if (address >= (PVOID)current->DllBase &&
142 address < (PVOID)((ULONG_PTR)current->DllBase + current->SizeOfImage))
143 {
144 RelativeAddress = (ULONG_PTR) address - (ULONG_PTR) current->DllBase;
145 DbgPrint("<%wZ: %x>", &current->FullDllName, RelativeAddress);
146 return(TRUE);
147 }
148 current_entry = current_entry->Flink;
149 }
150
151 address = (PVOID)((ULONG_PTR)address & ~(ULONG_PTR)MmSystemRangeStart);
152 } while(++i <= 1);
153
154 return(FALSE);
155 }
156
157 ULONG
158 KiKernelTrapHandler(PKTRAP_FRAME Tf, ULONG ExceptionNr, PVOID Cr2)
159 {
160 EXCEPTION_RECORD Er;
161
162 Er.ExceptionFlags = 0;
163 Er.ExceptionRecord = NULL;
164 Er.ExceptionAddress = (PVOID)Tf->Eip;
165
166 if (ExceptionNr == 14)
167 {
168 Er.ExceptionCode = STATUS_ACCESS_VIOLATION;
169 Er.NumberParameters = 2;
170 Er.ExceptionInformation[0] = Tf->ErrorCode & 0x1;
171 Er.ExceptionInformation[1] = (ULONG)Cr2;
172 }
173 else
174 {
175 if (ExceptionNr < ARRAY_SIZE(ExceptionToNtStatus))
176 {
177 Er.ExceptionCode = ExceptionToNtStatus[ExceptionNr];
178 }
179 else
180 {
181 Er.ExceptionCode = STATUS_ACCESS_VIOLATION;
182 }
183 Er.NumberParameters = 0;
184 }
185
186 /* FIXME: Which exceptions are noncontinuable? */
187 Er.ExceptionFlags = 0;
188
189 KiDispatchException(&Er, NULL, Tf, KernelMode, TRUE);
190
191 return(0);
192 }
193
194 VOID
195 KiDoubleFaultHandler(VOID)
196 {
197 unsigned int cr2;
198 ULONG StackLimit;
199 ULONG StackBase;
200 ULONG Esp0;
201 ULONG ExceptionNr = 8;
202 KTSS* OldTss;
203 PULONG Frame;
204 ULONG OldCr3;
205 #if 0
206 ULONG i, j;
207 static PVOID StackTrace[MM_STACK_SIZE / sizeof(PVOID)];
208 static ULONG StackRepeatCount[MM_STACK_SIZE / sizeof(PVOID)];
209 static ULONG StackRepeatLength[MM_STACK_SIZE / sizeof(PVOID)];
210 ULONG TraceLength;
211 BOOLEAN FoundRepeat;
212 #endif
213
214 OldTss = KeGetCurrentKPCR()->TSS;
215 Esp0 = OldTss->Esp;
216
217 /* Get CR2 */
218 cr2 = Ke386GetCr2();
219 if (PsGetCurrentThread() != NULL &&
220 PsGetCurrentThread()->ThreadsProcess != NULL)
221 {
222 OldCr3 = (ULONG)
223 PsGetCurrentThread()->ThreadsProcess->Pcb.DirectoryTableBase.QuadPart;
224 }
225 else
226 {
227 OldCr3 = 0xBEADF0AL;
228 }
229
230 /*
231 * Check for stack underflow
232 */
233 if (PsGetCurrentThread() != NULL &&
234 Esp0 < (ULONG)PsGetCurrentThread()->Tcb.StackLimit)
235 {
236 DbgPrint("Stack underflow (tf->esp %x Limit %x)\n",
237 Esp0, (ULONG)PsGetCurrentThread()->Tcb.StackLimit);
238 ExceptionNr = 12;
239 }
240
241 /*
242 * Print out the CPU registers
243 */
244 if (ExceptionNr < ARRAY_SIZE(ExceptionTypeStrings))
245 {
246 DbgPrint("%s Exception: %d(%x)\n", ExceptionTypeStrings[ExceptionNr],
247 ExceptionNr, 0);
248 }
249 else
250 {
251 DbgPrint("Exception: %d(%x)\n", ExceptionNr, 0);
252 }
253 DbgPrint("CS:EIP %x:%x ", OldTss->Cs, OldTss->Eip);
254 KeRosPrintAddress((PVOID)OldTss->Eip);
255 DbgPrint("\n");
256 DbgPrint("cr2 %x cr3 %x ", cr2, OldCr3);
257 DbgPrint("Proc: %x ",PsGetCurrentProcess());
258 if (PsGetCurrentProcess() != NULL)
259 {
260 DbgPrint("Pid: %x <", PsGetCurrentProcess()->UniqueProcessId);
261 DbgPrint("%.16s> ", PsGetCurrentProcess()->ImageFileName);
262 }
263 if (PsGetCurrentThread() != NULL)
264 {
265 DbgPrint("Thrd: %x Tid: %x",
266 PsGetCurrentThread(),
267 PsGetCurrentThread()->Cid.UniqueThread);
268 }
269 DbgPrint("\n");
270 DbgPrint("DS %x ES %x FS %x GS %x\n", OldTss->Ds, OldTss->Es,
271 OldTss->Fs, OldTss->Gs);
272 DbgPrint("EAX: %.8x EBX: %.8x ECX: %.8x\n", OldTss->Eax, OldTss->Ebx,
273 OldTss->Ecx);
274 DbgPrint("EDX: %.8x EBP: %.8x ESI: %.8x\nESP: %.8x ", OldTss->Edx,
275 OldTss->Ebp, OldTss->Esi, Esp0);
276 DbgPrint("EDI: %.8x EFLAGS: %.8x ", OldTss->Edi, OldTss->Eflags);
277 if (OldTss->Cs == KERNEL_CS)
278 {
279 DbgPrint("kESP %.8x ", Esp0);
280 if (PsGetCurrentThread() != NULL)
281 {
282 DbgPrint("kernel stack base %x\n",
283 PsGetCurrentThread()->Tcb.StackLimit);
284
285 }
286 }
287 else
288 {
289 DbgPrint("User ESP %.8x\n", OldTss->Esp);
290 }
291 if ((OldTss->Cs & 0xffff) == KERNEL_CS)
292 {
293 if (PsGetCurrentThread() != NULL)
294 {
295 StackLimit = (ULONG)PsGetCurrentThread()->Tcb.StackBase;
296 StackBase = (ULONG)PsGetCurrentThread()->Tcb.StackLimit;
297 }
298 else
299 {
300 StackLimit = (ULONG)init_stack_top;
301 StackBase = (ULONG)init_stack;
302 }
303
304 /*
305 Change to an #if 0 to reduce the amount of information printed on
306 a recursive stack trace.
307 */
308 #if 1
309 DbgPrint("Frames: ");
310 Frame = (PULONG)OldTss->Ebp;
311 while (Frame != NULL && (ULONG)Frame >= StackBase)
312 {
313 KeRosPrintAddress((PVOID)Frame[1]);
314 Frame = (PULONG)Frame[0];
315 DbgPrint("\n");
316 }
317 #else
318 DbgPrint("Frames: ");
319 i = 0;
320 Frame = (PULONG)OldTss->Ebp;
321 while (Frame != NULL && (ULONG)Frame >= StackBase)
322 {
323 StackTrace[i] = (PVOID)Frame[1];
324 Frame = (PULONG)Frame[0];
325 i++;
326 }
327 TraceLength = i;
328
329 i = 0;
330 while (i < TraceLength)
331 {
332 StackRepeatCount[i] = 0;
333 j = i + 1;
334 FoundRepeat = FALSE;
335 while ((j - i) <= (TraceLength - j) && FoundRepeat == FALSE)
336 {
337 if (memcmp(&StackTrace[i], &StackTrace[j],
338 (j - i) * sizeof(PVOID)) == 0)
339 {
340 StackRepeatCount[i] = 2;
341 StackRepeatLength[i] = j - i;
342 FoundRepeat = TRUE;
343 }
344 else
345 {
346 j++;
347 }
348 }
349 if (FoundRepeat == FALSE)
350 {
351 i++;
352 continue;
353 }
354 j = j + StackRepeatLength[i];
355 while ((TraceLength - j) >= StackRepeatLength[i] &&
356 FoundRepeat == TRUE)
357 {
358 if (memcmp(&StackTrace[i], &StackTrace[j],
359 StackRepeatLength[i] * sizeof(PVOID)) == 0)
360 {
361 StackRepeatCount[i]++;
362 j = j + StackRepeatLength[i];
363 }
364 else
365 {
366 FoundRepeat = FALSE;
367 }
368 }
369 i = j;
370 }
371
372 i = 0;
373 while (i < TraceLength)
374 {
375 if (StackRepeatCount[i] == 0)
376 {
377 KeRosPrintAddress(StackTrace[i]);
378 i++;
379 }
380 else
381 {
382 DbgPrint("{");
383 if (StackRepeatLength[i] == 0)
384 {
385 for(;;);
386 }
387 for (j = 0; j < StackRepeatLength[i]; j++)
388 {
389 KeRosPrintAddress(StackTrace[i + j]);
390 }
391 DbgPrint("}*%d", StackRepeatCount[i]);
392 i = i + StackRepeatLength[i] * StackRepeatCount[i];
393 }
394 }
395 #endif
396 }
397
398 DbgPrint("\n");
399 for(;;);
400 }
401
402 VOID
403 NTAPI
404 KiDumpTrapFrame(PKTRAP_FRAME Tf, ULONG Parameter1, ULONG Parameter2)
405 {
406 ULONG cr3_;
407 ULONG StackLimit;
408 ULONG Esp0;
409 ULONG ExceptionNr = (ULONG)Tf->DebugArgMark;
410 ULONG cr2 = (ULONG)Tf->DebugPointer;
411
412 Esp0 = (ULONG)Tf;
413
414 /*
415 * Print out the CPU registers
416 */
417 if (ExceptionNr < ARRAY_SIZE(ExceptionTypeStrings))
418 {
419 DbgPrint("%s Exception: %d(%x)\n", ExceptionTypeStrings[ExceptionNr],
420 ExceptionNr, Tf->ErrorCode&0xffff);
421 }
422 else
423 {
424 DbgPrint("Exception: %d(%x)\n", ExceptionNr, Tf->ErrorCode&0xffff);
425 }
426 DbgPrint("Processor: %d CS:EIP %x:%x ", KeGetCurrentProcessorNumber(),
427 Tf->Cs&0xffff, Tf->Eip);
428 KeRosPrintAddress((PVOID)Tf->Eip);
429 DbgPrint("\n");
430 Ke386GetPageTableDirectory(cr3_);
431 DbgPrint("cr2 %x cr3 %x ", cr2, cr3_);
432 DbgPrint("Proc: %x ",PsGetCurrentProcess());
433 if (PsGetCurrentProcess() != NULL)
434 {
435 DbgPrint("Pid: %x <", PsGetCurrentProcess()->UniqueProcessId);
436 DbgPrint("%.16s> ", PsGetCurrentProcess()->ImageFileName);
437 }
438 if (PsGetCurrentThread() != NULL)
439 {
440 DbgPrint("Thrd: %x Tid: %x",
441 PsGetCurrentThread(),
442 PsGetCurrentThread()->Cid.UniqueThread);
443 }
444 DbgPrint("\n");
445 DbgPrint("DS %x ES %x FS %x GS %x\n", Tf->Ds&0xffff, Tf->Es&0xffff,
446 Tf->Fs&0xffff, Tf->Gs&0xfff);
447 DbgPrint("EAX: %.8x EBX: %.8x ECX: %.8x\n", Tf->Eax, Tf->Ebx, Tf->Ecx);
448 DbgPrint("EDX: %.8x EBP: %.8x ESI: %.8x ESP: %.8x\n", Tf->Edx,
449 Tf->Ebp, Tf->Esi, Esp0);
450 DbgPrint("EDI: %.8x EFLAGS: %.8x ", Tf->Edi, Tf->Eflags);
451 if ((Tf->Cs&0xffff) == KERNEL_CS)
452 {
453 DbgPrint("kESP %.8x ", Esp0);
454 if (PsGetCurrentThread() != NULL)
455 {
456 DbgPrint("kernel stack base %x\n",
457 PsGetCurrentThread()->Tcb.StackLimit);
458
459 }
460 }
461
462 if (PsGetCurrentThread() != NULL)
463 {
464 StackLimit = (ULONG)PsGetCurrentThread()->Tcb.StackBase;
465 }
466 else
467 {
468 StackLimit = (ULONG)init_stack_top;
469 }
470
471 /*
472 * Dump the stack frames
473 */
474 KeDumpStackFrames((PULONG)Tf->Ebp);
475 }
476
477 ULONG
478 KiTrapHandler(PKTRAP_FRAME Tf, ULONG ExceptionNr)
479 /*
480 * FUNCTION: Called by the lowlevel execption handlers to print an amusing
481 * message and halt the computer
482 * ARGUMENTS:
483 * Complete CPU context
484 */
485 {
486 ULONG_PTR cr2;
487 NTSTATUS Status;
488 ULONG Esp0;
489
490 ASSERT(ExceptionNr != 14);
491
492 /* Store the exception number in an unused field in the trap frame. */
493 Tf->DebugArgMark = ExceptionNr;
494
495 /* Use the address of the trap frame as approximation to the ring0 esp */
496 Esp0 = (ULONG)&Tf->Eip;
497
498 /* Get CR2 */
499 cr2 = Ke386GetCr2();
500 Tf->DebugPointer = cr2;
501
502 /*
503 * If this was a V86 mode exception then handle it specially
504 */
505 if (Tf->Eflags & (1 << 17))
506 {
507 DPRINT("Tf->Eflags, %x, Tf->Eip %x, ExceptionNr: %d\n", Tf->Eflags, Tf->Eip, ExceptionNr);
508 return(KeV86Exception(ExceptionNr, Tf, cr2));
509 }
510
511 /*
512 * Check for stack underflow, this may be obsolete
513 */
514 if (PsGetCurrentThread() != NULL &&
515 Esp0 < (ULONG)PsGetCurrentThread()->Tcb.StackLimit)
516 {
517 DPRINT1("Stack underflow (tf->esp %x Limit %x Eip %x)\n",
518 Esp0, (ULONG)PsGetCurrentThread()->Tcb.StackLimit, Tf->Eip);
519 ExceptionNr = 12;
520 }
521
522 if (ExceptionNr == 15)
523 {
524 /*
525 * FIXME:
526 * This exception should never occur. The P6 has a bug, which does sometimes deliver
527 * the apic spurious interrupt as exception 15. On an athlon64, I get one exception
528 * in the early boot phase in apic mode (using the smp build). I've looked to the linux
529 * sources. Linux does ignore this exception.
530 *
531 * Hartmut Birr
532 */
533 DPRINT1("Ignoring P6 Local APIC Spurious Interrupt Bug...\n");
534 return(0);
535 }
536
537 /*
538 * Check for a breakpoint that was only for the attention of the debugger.
539 */
540 if (ExceptionNr == 3 && Tf->Eip == ((ULONG)DbgBreakPointNoBugCheck) + 1)
541 {
542 /*
543 EIP is already adjusted by the processor to point to the instruction
544 after the breakpoint.
545 */
546 return(0);
547 }
548
549 /*
550 * Try to handle device-not-present, math-fault and xmm-fault exceptions.
551 */
552 if (ExceptionNr == 7 || ExceptionNr == 16 || ExceptionNr == 19)
553 {
554 Status = KiHandleFpuFault(Tf, ExceptionNr);
555 if (NT_SUCCESS(Status))
556 {
557 return(0);
558 }
559 }
560
561 /*
562 * Handle user exceptions differently
563 */
564 if ((Tf->Cs & 0xFFFF) == USER_CS)
565 {
566 return(KiUserTrapHandler(Tf, ExceptionNr, (PVOID)cr2));
567 }
568 else
569 {
570 return(KiKernelTrapHandler(Tf, ExceptionNr, (PVOID)cr2));
571 }
572 }
573
574 ULONG
575 NTAPI
576 KiEspFromTrapFrame(IN PKTRAP_FRAME TrapFrame)
577 {
578 /* Check if this is user-mode or V86 */
579 if ((TrapFrame->Cs & 1) || (TrapFrame->Eflags & X86_EFLAGS_VM))
580 {
581 /* Return it directly */
582 return TrapFrame->Esp;
583 }
584 else
585 {
586 /* Edited frame */
587 if (!(TrapFrame->Cs & FRAME_EDITED))
588 {
589 /* Return edited value */
590 return TrapFrame->TempEsp;
591 }
592 else
593 {
594 /* Virgin frame, calculate */
595 return (ULONG)&TrapFrame->Esp;
596 }
597 }
598 }
599
600 VOID
601 NTAPI
602 KiEspToTrapFrame(IN PKTRAP_FRAME TrapFrame,
603 IN ULONG Esp)
604 {
605 ULONG Previous = KiEspFromTrapFrame(TrapFrame);
606
607 /* Check if this is user-mode or V86 */
608 if ((TrapFrame->Cs & 1) || (TrapFrame->Eflags & X86_EFLAGS_VM))
609 {
610 /* Write it directly */
611 TrapFrame->Esp = Esp;
612 }
613 else
614 {
615 /* Don't allow ESP to be lowered, this is illegal */
616 if (Esp < Previous)
617 {
618 KeBugCheck(SET_OF_INVALID_CONTEXT);
619 }
620
621 /* Create an edit frame, check if it was alrady */
622 if (!(TrapFrame->Cs & FRAME_EDITED))
623 {
624 /* Update the value */
625 TrapFrame->TempEsp = Esp;
626 }
627 else
628 {
629 /* Check if ESP changed */
630 if (Previous != Esp)
631 {
632 /* Save CS */
633 TrapFrame->TempCs = TrapFrame->Cs;
634 TrapFrame->Cs &= ~FRAME_EDITED;
635
636 /* Save ESP */
637 TrapFrame->TempEsp = Esp;
638 }
639 }
640 }
641 }
642
643 ULONG
644 NTAPI
645 KiSsFromTrapFrame(IN PKTRAP_FRAME TrapFrame)
646 {
647 /* If this was V86 Mode */
648 if (TrapFrame->Eflags & X86_EFLAGS_VM)
649 {
650 /* Just return it */
651 return TrapFrame->Ss;
652 }
653 else if (TrapFrame->Cs & 1)
654 {
655 /* Usermode, return the User SS */
656 return TrapFrame->Ss | 3;
657 }
658 else
659 {
660 /* Kernel mode */
661 return KERNEL_DS;
662 }
663 }
664
665 VOID
666 NTAPI
667 KiSsToTrapFrame(IN PKTRAP_FRAME TrapFrame,
668 IN ULONG Ss)
669 {
670 /* Remove the high-bits */
671 Ss &= 0xFFFF;
672
673 /* If this was V86 Mode */
674 if (TrapFrame->Eflags & X86_EFLAGS_VM)
675 {
676 /* Just write it */
677 TrapFrame->Ss = Ss;
678 }
679 else if (TrapFrame->Cs & 1)
680 {
681 /* Usermode, save the User SS */
682 TrapFrame->Ss = Ss | 3;
683 }
684 }
685
686 BOOLEAN
687 NTAPI
688 KeContextToTrapFrame(IN PCONTEXT Context,
689 IN OUT PKEXCEPTION_FRAME ExceptionFrame,
690 IN OUT PKTRAP_FRAME TrapFrame,
691 IN KPROCESSOR_MODE PreviousMode)
692 {
693 BOOLEAN V86Switch = FALSE;
694
695 /* Start with the basic Registers */
696 if ((Context->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
697 {
698 /* Check if we went through a V86 switch */
699 if ((Context->EFlags & X86_EFLAGS_VM) !=
700 (TrapFrame->Eflags & X86_EFLAGS_VM))
701 {
702 /* We did, remember this for later */
703 V86Switch = TRUE;
704 }
705
706 /* Copy EFLAGS. FIXME: Needs to be sanitized */
707 TrapFrame->Eflags = Context->EFlags;
708
709 /* Copy EBP and EIP */
710 TrapFrame->Ebp = Context->Ebp;
711 TrapFrame->Eip = Context->Eip;
712
713 /* Check if we were in V86 Mode */
714 if (TrapFrame->Eflags & X86_EFLAGS_VM)
715 {
716 /* Simply copy the CS value */
717 TrapFrame->Cs = Context->SegCs;
718 }
719 else
720 {
721 /* We weren't in V86, so sanitize the CS (FIXME!) */
722 TrapFrame->Cs = Context->SegCs;
723
724 /* Don't let it under 8, that's invalid */
725 if ((PreviousMode !=KernelMode) && (TrapFrame->Cs < 8))
726 {
727 /* Force it to User CS */
728 TrapFrame->Cs = USER_CS;
729 }
730 }
731
732 /* Handle SS Specially for validation */
733 KiSsToTrapFrame(TrapFrame, Context->SegSs);
734
735 /* Write ESP back; take into account Edited Trap Frames */
736 KiEspToTrapFrame(TrapFrame, Context->Esp);
737
738 /* Handle our V86 Bias if we went through a switch */
739 if (V86Switch) Ki386AdjustEsp0(TrapFrame);
740 }
741
742 /* Process the Integer Registers */
743 if ((Context->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER)
744 {
745 TrapFrame->Eax = Context->Eax;
746 TrapFrame->Ebx = Context->Ebx;
747 TrapFrame->Ecx = Context->Ecx;
748 TrapFrame->Edx = Context->Edx;
749 TrapFrame->Esi = Context->Esi;
750 TrapFrame->Edi = Context->Edi;
751 }
752
753 /* Process the Context Segments */
754 if ((Context->ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS)
755 {
756 /* Check if we were in V86 Mode */
757 if (TrapFrame->Eflags & X86_EFLAGS_VM)
758 {
759 /* Copy the V86 Segments directlry */
760 TrapFrame->V86_Ds = Context->SegDs;
761 TrapFrame->V86_Es = Context->SegEs;
762 TrapFrame->V86_Fs = Context->SegFs;
763 TrapFrame->V86_Gs = Context->SegGs;
764 }
765 else if (!(TrapFrame->Cs & 1))
766 {
767 /* For user mode, write the values directly */
768 TrapFrame->Ds = USER_DS;
769 TrapFrame->Es = USER_DS;
770 TrapFrame->Fs = Context->SegFs;
771 TrapFrame->Gs = 0;
772 }
773 else
774 {
775 /* For kernel-mode, return the values */
776 TrapFrame->Ds = Context->SegDs;
777 TrapFrame->Es = Context->SegEs;
778 TrapFrame->Fs = Context->SegFs;
779
780 /* Handle GS specially */
781 if (TrapFrame->Cs == USER_CS)
782 {
783 /* Don't use it, if user */
784 TrapFrame->Gs = 0;
785 }
786 else
787 {
788 /* Copy it if kernel */
789 TrapFrame->Gs = Context->SegGs;
790 }
791 }
792 }
793
794 /* Handle the Debug Registers */
795 if ((Context->ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS)
796 {
797 /* FIXME: All these should be sanitized */
798 TrapFrame->Dr0 = Context->Dr0;
799 TrapFrame->Dr1 = Context->Dr1;
800 TrapFrame->Dr2 = Context->Dr2;
801 TrapFrame->Dr3 = Context->Dr3;
802 TrapFrame->Dr6 = Context->Dr6;
803 TrapFrame->Dr7 = Context->Dr7;
804
805 /* Check if usermode */
806 if (PreviousMode != KernelMode)
807 {
808 /* Set the Debug Flag */
809 KeGetCurrentThread()->DebugActive = (Context->Dr7 & DR7_ACTIVE);
810 }
811 }
812
813 /* Handle FPU and Extended Registers */
814 return KiContextToFxSaveArea((PFX_SAVE_AREA)(TrapFrame + 1), Context);
815 }
816
817 VOID
818 NTAPI
819 KeTrapFrameToContext(IN PKTRAP_FRAME TrapFrame,
820 IN PKEXCEPTION_FRAME ExceptionFrame,
821 IN OUT PCONTEXT Context)
822 {
823 /* Start with the Control flags */
824 if ((Context->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
825 {
826 /* EBP, EIP and EFLAGS */
827 Context->Ebp = TrapFrame->Ebp;
828 Context->Eip = TrapFrame->Eip;
829 Context->EFlags = TrapFrame->Eflags;
830
831 /* Return the correct CS */
832 if (!(TrapFrame->Cs & FRAME_EDITED) &&
833 !(TrapFrame->Eflags & X86_EFLAGS_VM))
834 {
835 /* Get it from the Temp location */
836 Context->SegCs = TrapFrame->TempCs & 0xFFFF;
837 }
838 else
839 {
840 /* Return it directly */
841 Context->SegCs = TrapFrame->Cs & 0xFFFF;
842 }
843
844 /* Get the Ss and ESP */
845 Context->SegSs = KiSsFromTrapFrame(TrapFrame);
846 Context->Esp = KiEspFromTrapFrame(TrapFrame);
847 }
848
849 /* Handle the Segments */
850 if ((Context->ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS)
851 {
852 /* Do V86 Mode first */
853 if (TrapFrame->Eflags & X86_EFLAGS_VM)
854 {
855 /* Return from the V86 location */
856 Context->SegGs = TrapFrame->V86_Gs & 0xFFFF;
857 Context->SegFs = TrapFrame->V86_Fs & 0xFFFF;
858 Context->SegEs = TrapFrame->V86_Es & 0xFFFF;
859 Context->SegDs = TrapFrame->V86_Ds & 0xFFFF;
860 }
861 else
862 {
863 /* Check if this was a Kernel Trap */
864 if (TrapFrame->Cs == KERNEL_CS)
865 {
866 /* Set valid selectors */
867 TrapFrame->Gs = 0;
868 TrapFrame->Fs = PCR_SELECTOR;
869 TrapFrame->Es = USER_DS;
870 TrapFrame->Ds = USER_DS;
871 }
872
873 /* Return the segments */
874 Context->SegGs = TrapFrame->Gs & 0xFFFF;
875 Context->SegFs = TrapFrame->Fs & 0xFFFF;
876 Context->SegEs = TrapFrame->Es & 0xFFFF;
877 Context->SegDs = TrapFrame->Ds & 0xFFFF;
878 }
879 }
880
881 /* Handle the simple registers */
882 if ((Context->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER)
883 {
884 /* Return them directly */
885 Context->Eax = TrapFrame->Eax;
886 Context->Ebx = TrapFrame->Ebx;
887 Context->Ecx = TrapFrame->Ecx;
888 Context->Edx = TrapFrame->Edx;
889 Context->Esi = TrapFrame->Esi;
890 Context->Edi = TrapFrame->Edi;
891 }
892
893 if ((Context->ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS)
894 {
895 /*
896 * FIXME: Implement this case
897 */
898 Context->ContextFlags &= (~CONTEXT_DEBUG_REGISTERS) | CONTEXT_i386;
899 }
900 if ((Context->ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT)
901 {
902 /*
903 * FIXME: Implement this case
904 *
905 * I think this should only be filled for FPU exceptions, otherwise I
906 * would not know where to get it from as it can be the current state
907 * of the FPU or already saved in the thread's FPU save area.
908 * -blight
909 */
910 Context->ContextFlags &= (~CONTEXT_FLOATING_POINT) | CONTEXT_i386;
911 }
912 #if 0
913 if ((Context->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS)
914 {
915 /*
916 * FIXME: Investigate this
917 *
918 * This is the XMM state (first 512 bytes of FXSAVE_FORMAT/FX_SAVE_AREA)
919 * This should only be filled in case of a SIMD exception I think, so
920 * this is not the right place (like for FPU the state could already be
921 * saved in the thread's FX_SAVE_AREA or still be in the CPU)
922 * -blight
923 */
924 Context->ContextFlags &= ~CONTEXT_EXTENDED_REGISTERS;
925 }
926 #endif
927 }
928
929 VOID
930 NTAPI
931 KeDumpStackFrames(PULONG Frame)
932 {
933 PULONG StackBase, StackEnd;
934 MEMORY_BASIC_INFORMATION mbi;
935 ULONG ResultLength = sizeof(mbi);
936 NTSTATUS Status;
937
938 DbgPrint("Frames:\n");
939 _SEH_TRY
940 {
941 Status = MiQueryVirtualMemory (
942 (HANDLE)-1,
943 Frame,
944 MemoryBasicInformation,
945 &mbi,
946 sizeof(mbi),
947 &ResultLength );
948 if ( !NT_SUCCESS(Status) )
949 {
950 DPRINT1("Can't dump stack frames: MiQueryVirtualMemory() failed: %x\n", Status );
951 return;
952 }
953
954 StackBase = Frame;
955 StackEnd = (PULONG)((ULONG_PTR)mbi.BaseAddress + mbi.RegionSize);
956
957 while ( Frame >= StackBase && Frame < StackEnd )
958 {
959 ULONG Addr = Frame[1];
960 if (!KeRosPrintAddress((PVOID)Addr))
961 DbgPrint("<%X>", Addr);
962 if ( Addr == 0 || Addr == 0xDEADBEEF )
963 break;
964 StackBase = Frame;
965 Frame = (PULONG)Frame[0];
966 DbgPrint("\n");
967 }
968 }
969 _SEH_HANDLE
970 {
971 }
972 _SEH_END;
973 DbgPrint("\n");
974 }
975
976 VOID STDCALL
977 KeRosDumpStackFrames ( PULONG Frame, ULONG FrameCount )
978 {
979 ULONG i=0;
980 PULONG StackBase, StackEnd;
981 MEMORY_BASIC_INFORMATION mbi;
982 ULONG ResultLength = sizeof(mbi);
983 NTSTATUS Status;
984
985 DbgPrint("Frames: ");
986 _SEH_TRY
987 {
988 if ( !Frame )
989 {
990 #if defined __GNUC__
991 __asm__("mov %%ebp, %%ebx" : "=b" (Frame) : );
992 #elif defined(_MSC_VER)
993 __asm mov [Frame], ebp
994 #endif
995 //Frame = (PULONG)Frame[0]; // step out of KeRosDumpStackFrames
996 }
997
998 Status = MiQueryVirtualMemory (
999 (HANDLE)-1,
1000 Frame,
1001 MemoryBasicInformation,
1002 &mbi,
1003 sizeof(mbi),
1004 &ResultLength );
1005 if ( !NT_SUCCESS(Status) )
1006 {
1007 DPRINT1("Can't dump stack frames: MiQueryVirtualMemory() failed: %x\n", Status );
1008 return;
1009 }
1010
1011 StackBase = Frame;
1012 StackEnd = (PULONG)((ULONG_PTR)mbi.BaseAddress + mbi.RegionSize);
1013
1014 while ( Frame >= StackBase && Frame < StackEnd && i++ < FrameCount )
1015 {
1016 ULONG Addr = Frame[1];
1017 if (!KeRosPrintAddress((PVOID)Addr))
1018 DbgPrint("<%X>", Addr);
1019 if ( Addr == 0 || Addr == 0xDEADBEEF )
1020 break;
1021 StackBase = Frame;
1022 Frame = (PULONG)Frame[0];
1023 DbgPrint(" ");
1024 }
1025 }
1026 _SEH_HANDLE
1027 {
1028 }
1029 _SEH_END;
1030 DbgPrint("\n");
1031 }
1032
1033 ULONG STDCALL
1034 KeRosGetStackFrames ( PULONG Frames, ULONG FrameCount )
1035 {
1036 ULONG Count = 0;
1037 PULONG StackBase, StackEnd, Frame;
1038 MEMORY_BASIC_INFORMATION mbi;
1039 ULONG ResultLength = sizeof(mbi);
1040 NTSTATUS Status;
1041
1042 _SEH_TRY
1043 {
1044 #if defined __GNUC__
1045 __asm__("mov %%ebp, %%ebx" : "=b" (Frame) : );
1046 #elif defined(_MSC_VER)
1047 __asm mov [Frame], ebp
1048 #endif
1049
1050 Status = MiQueryVirtualMemory (
1051 (HANDLE)-1,
1052 Frame,
1053 MemoryBasicInformation,
1054 &mbi,
1055 sizeof(mbi),
1056 &ResultLength );
1057 if ( !NT_SUCCESS(Status) )
1058 {
1059 DPRINT1("Can't get stack frames: MiQueryVirtualMemory() failed: %x\n", Status );
1060 return 0;
1061 }
1062
1063 StackBase = Frame;
1064 StackEnd = (PULONG)((ULONG_PTR)mbi.BaseAddress + mbi.RegionSize);
1065
1066 while ( Count < FrameCount && Frame >= StackBase && Frame < StackEnd )
1067 {
1068 Frames[Count++] = Frame[1];
1069 StackBase = Frame;
1070 Frame = (PULONG)Frame[0];
1071 }
1072 }
1073 _SEH_HANDLE
1074 {
1075 }
1076 _SEH_END;
1077 return Count;
1078 }
1079
1080 static void
1081 set_system_call_gate(unsigned int sel, unsigned int func)
1082 {
1083 DPRINT("sel %x %d\n",sel,sel);
1084 KiIdt[sel].a = (((int)func)&0xffff) +
1085 (KERNEL_CS << 16);
1086 KiIdt[sel].b = 0xef00 + (((int)func)&0xffff0000);
1087 DPRINT("idt[sel].b %x\n",KiIdt[sel].b);
1088 }
1089
1090 static void set_interrupt_gate(unsigned int sel, unsigned int func)
1091 {
1092 DPRINT("set_interrupt_gate(sel %d, func %x)\n",sel,func);
1093 KiIdt[sel].a = (((int)func)&0xffff) +
1094 (KERNEL_CS << 16);
1095 KiIdt[sel].b = 0x8e00 + (((int)func)&0xffff0000);
1096 }
1097
1098 static void set_trap_gate(unsigned int sel, unsigned int func, unsigned int dpl)
1099 {
1100 DPRINT("set_trap_gate(sel %d, func %x, dpl %d)\n",sel, func, dpl);
1101 ASSERT(dpl <= 3);
1102 KiIdt[sel].a = (((int)func)&0xffff) +
1103 (KERNEL_CS << 16);
1104 KiIdt[sel].b = 0x8f00 + (dpl << 13) + (((int)func)&0xffff0000);
1105 }
1106
1107 static void
1108 set_task_gate(unsigned int sel, unsigned task_sel)
1109 {
1110 KiIdt[sel].a = task_sel << 16;
1111 KiIdt[sel].b = 0x8500;
1112 }
1113
1114 VOID
1115 INIT_FUNCTION
1116 NTAPI
1117 KeInitExceptions(VOID)
1118 /*
1119 * FUNCTION: Initalize CPU exception handling
1120 */
1121 {
1122 int i;
1123
1124 DPRINT("KeInitExceptions()\n");
1125
1126 /*
1127 * Set up the other gates
1128 */
1129 set_trap_gate(0, (ULONG)KiTrap0, 0);
1130 set_trap_gate(1, (ULONG)KiTrap1, 0);
1131 set_trap_gate(2, (ULONG)KiTrap2, 0);
1132 set_trap_gate(3, (ULONG)KiTrap3, 3);
1133 set_trap_gate(4, (ULONG)KiTrap4, 0);
1134 set_trap_gate(5, (ULONG)KiTrap5, 0);
1135 set_trap_gate(6, (ULONG)KiTrap6, 0);
1136 set_trap_gate(7, (ULONG)KiTrap7, 0);
1137 set_task_gate(8, TRAP_TSS_SELECTOR);
1138 set_trap_gate(9, (ULONG)KiTrap9, 0);
1139 set_trap_gate(10, (ULONG)KiTrap10, 0);
1140 set_trap_gate(11, (ULONG)KiTrap11, 0);
1141 set_trap_gate(12, (ULONG)KiTrap12, 0);
1142 set_trap_gate(13, (ULONG)KiTrap13, 0);
1143 set_interrupt_gate(14, (ULONG)KiTrap14);
1144 set_trap_gate(15, (ULONG)KiTrap15, 0);
1145 set_trap_gate(16, (ULONG)KiTrap16, 0);
1146 set_trap_gate(17, (ULONG)KiTrap17, 0);
1147 set_trap_gate(18, (ULONG)KiTrap18, 0);
1148 set_trap_gate(19, (ULONG)KiTrap19, 0);
1149
1150 for (i = 20; i < 256; i++)
1151 {
1152 set_trap_gate(i,(int)KiTrapUnknown, 0);
1153 }
1154
1155 set_system_call_gate(0x2d,(int)KiDebugService);
1156 set_system_call_gate(0x2e,(int)KiSystemService);
1157 }
1158
1159 VOID
1160 NTAPI
1161 KiDispatchException(PEXCEPTION_RECORD ExceptionRecord,
1162 PKEXCEPTION_FRAME ExceptionFrame,
1163 PKTRAP_FRAME TrapFrame,
1164 KPROCESSOR_MODE PreviousMode,
1165 BOOLEAN FirstChance)
1166 {
1167 CONTEXT Context;
1168 KD_CONTINUE_TYPE Action;
1169 ULONG_PTR Stack, NewStack;
1170 ULONG Size;
1171 BOOLEAN UserDispatch = FALSE;
1172 DPRINT("KiDispatchException() called\n");
1173
1174 /* Increase number of Exception Dispatches */
1175 KeGetCurrentPrcb()->KeExceptionDispatchCount++;
1176
1177 /* Set the context flags */
1178 Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
1179
1180 /* Check if User Mode */
1181 if (PreviousMode == UserMode)
1182 {
1183 /* Add the FPU Flag */
1184 Context.ContextFlags |= CONTEXT_FLOATING_POINT;
1185 }
1186
1187 /* Get a Context */
1188 KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
1189
1190 /* Handle kernel-mode first, it's simpler */
1191 if (PreviousMode == KernelMode)
1192 {
1193 /* Check if this is a first-chance exception */
1194 if (FirstChance == TRUE)
1195 {
1196 /* Break into the debugger for the first time */
1197 Action = KdpEnterDebuggerException(ExceptionRecord,
1198 PreviousMode,
1199 &Context,
1200 TrapFrame,
1201 TRUE,
1202 TRUE);
1203
1204 /* If the debugger said continue, then continue */
1205 if (Action == kdContinue) goto Handled;
1206
1207 /* If the Debugger couldn't handle it, dispatch the exception */
1208 if (RtlDispatchException(ExceptionRecord, &Context))
1209 {
1210 /* It was handled by an exception handler, continue */
1211 goto Handled;
1212 }
1213 }
1214
1215 /* This is a second-chance exception, only for the debugger */
1216 Action = KdpEnterDebuggerException(ExceptionRecord,
1217 PreviousMode,
1218 &Context,
1219 TrapFrame,
1220 FALSE,
1221 FALSE);
1222
1223 /* If the debugger said continue, then continue */
1224 if (Action == kdContinue) goto Handled;
1225
1226 /* Third strike; you're out */
1227 KEBUGCHECKWITHTF(KMODE_EXCEPTION_NOT_HANDLED,
1228 ExceptionRecord->ExceptionCode,
1229 (ULONG_PTR)ExceptionRecord->ExceptionAddress,
1230 ExceptionRecord->ExceptionInformation[0],
1231 ExceptionRecord->ExceptionInformation[1],
1232 TrapFrame);
1233 }
1234 else
1235 {
1236 /* User mode exception, was it first-chance? */
1237 if (FirstChance)
1238 {
1239 /* Enter Debugger if available */
1240 Action = KdpEnterDebuggerException(ExceptionRecord,
1241 PreviousMode,
1242 &Context,
1243 TrapFrame,
1244 TRUE,
1245 TRUE);
1246
1247 /* Exit if we're continuing */
1248 if (Action == kdContinue) goto Handled;
1249
1250 /* FIXME: Forward exception to user mode debugger */
1251
1252 /* Set up the user-stack */
1253 _SEH_TRY
1254 {
1255 /* Align context size and get stack pointer */
1256 Size = (sizeof(CONTEXT) + 3) & ~3;
1257 Stack = (Context.Esp & ~3) - Size;
1258 DPRINT("Stack: %lx\n", Stack);
1259
1260 /* Probe stack and copy Context */
1261 ProbeForWrite((PVOID)Stack, Size, sizeof(ULONG));
1262 RtlMoveMemory((PVOID)Stack, &Context, sizeof(CONTEXT));
1263
1264 /* Align exception record size and get stack pointer */
1265 Size = (sizeof(EXCEPTION_RECORD) -
1266 (EXCEPTION_MAXIMUM_PARAMETERS - ExceptionRecord->NumberParameters) *
1267 sizeof(ULONG) + 3) & ~3;
1268 NewStack = Stack - Size;
1269 DPRINT("NewStack: %lx\n", NewStack);
1270
1271 /* Probe stack and copy exception record. Don't forget to add the two params */
1272 ProbeForWrite((PVOID)(NewStack - 2 * sizeof(ULONG_PTR)),
1273 Size + 2 * sizeof(ULONG_PTR),
1274 sizeof(ULONG));
1275 RtlMoveMemory((PVOID)NewStack, ExceptionRecord, Size);
1276
1277 /* Now write the two params for the user-mode dispatcher */
1278 *(PULONG_PTR)(NewStack - 1 * sizeof(ULONG_PTR)) = Stack;
1279 *(PULONG_PTR)(NewStack - 2 * sizeof(ULONG_PTR)) = NewStack;
1280
1281 /* Set new Stack Pointer */
1282 KiEspToTrapFrame(TrapFrame, NewStack - 2 * sizeof(ULONG_PTR));
1283
1284 /* Set EIP to the User-mode Dispathcer */
1285 TrapFrame->Eip = (ULONG)KeUserExceptionDispatcher;
1286 UserDispatch = TRUE;
1287 _SEH_LEAVE;
1288 }
1289 _SEH_HANDLE
1290 {
1291 /* Do second-chance */
1292 }
1293 _SEH_END;
1294 }
1295
1296 /* If we dispatch to user, return now */
1297 if (UserDispatch) return;
1298
1299 /* FIXME: Forward the exception to the debugger for 2nd chance */
1300
1301 /* 3rd strike, kill the thread */
1302 DPRINT1("Unhandled UserMode exception, terminating thread\n");
1303 ZwTerminateThread(NtCurrentThread(), ExceptionRecord->ExceptionCode);
1304 KEBUGCHECKWITHTF(KMODE_EXCEPTION_NOT_HANDLED,
1305 ExceptionRecord->ExceptionCode,
1306 (ULONG_PTR)ExceptionRecord->ExceptionAddress,
1307 ExceptionRecord->ExceptionInformation[0],
1308 ExceptionRecord->ExceptionInformation[1],
1309 TrapFrame);
1310 }
1311
1312 Handled:
1313 /* Convert the context back into Trap/Exception Frames */
1314 KeContextToTrapFrame(&Context, NULL, TrapFrame, PreviousMode);
1315 return;
1316 }
1317
1318 /*
1319 * @implemented
1320 */
1321 NTSTATUS STDCALL
1322 KeRaiseUserException(IN NTSTATUS ExceptionCode)
1323 {
1324 ULONG OldEip;
1325 PKTHREAD Thread = KeGetCurrentThread();
1326
1327 _SEH_TRY {
1328 Thread->Teb->ExceptionCode = ExceptionCode;
1329 } _SEH_HANDLE {
1330 return(ExceptionCode);
1331 } _SEH_END;
1332
1333 OldEip = Thread->TrapFrame->Eip;
1334 Thread->TrapFrame->Eip = (ULONG_PTR)KeRaiseUserExceptionDispatcher;
1335 return((NTSTATUS)OldEip);
1336 }
1337