[HAL]: Add debugging feature to HalpTrap06 error that sometimes happen. Please post...
[reactos.git] / reactos / hal / halx86 / generic / bios.c
1 /*
2 * PROJECT: ReactOS Hardware Abstraction Layer (HAL)
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: halx86/generic/bios.c
5 * PURPOSE: BIOS Access Routines
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 * Alex Ionescu (alex.ionescu@reactos.org)
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #include <hal.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS ********************************************************************/
17
18 //
19 // PTE Data
20 //
21 ULONG HalpSavedPfn;
22 HARDWARE_PTE HalpSavedPte;
23
24 //
25 // IDT Data
26 //
27 PVOID HalpGpfHandler;
28 PVOID HalpBopHandler;
29
30 //
31 // TSS Data
32 //
33 ULONG HalpSavedEsp0;
34 USHORT HalpSavedTss;
35
36 //
37 // IOPM Data
38 //
39 USHORT HalpSavedIopmBase;
40 PUSHORT HalpSavedIoMap;
41 USHORT HalpSavedIoMapData[32][2];
42 ULONG HalpSavedIoMapEntries;
43
44 /* Where the protected mode stack is */
45 ULONG_PTR HalpSavedEsp;
46
47 /* Where the real mode code ends */
48 extern PVOID HalpRealModeEnd;
49
50 /* REAL MODE CODE AND STACK START HERE ****************************************/
51
52 VOID
53 DECLSPEC_NORETURN
54 HalpRealModeStart(VOID)
55 {
56 /* Do the video BIOS interrupt */
57 HalpCallBiosInterrupt(VIDEO_SERVICES, (SET_VIDEO_MODE << 8) | (GRAPHICS_MODE_12));
58
59 /* Issue the BOP */
60 KiIssueBop();
61
62 /* We want the stack to be inside this function so we can map real mode */
63 HalpRealModeStack(sizeof(ULONG), PAGE_SIZE / 2);
64 UNREACHABLE;
65 }
66
67 /* REAL MODE CODE AND STACK END HERE ******************************************/
68
69 /* V86 OPCODE HANDLERS ********************************************************/
70
71 BOOLEAN
72 FASTCALL
73 HalpOpcodeInvalid(IN PHAL_BIOS_FRAME BiosFrame)
74 {
75 /* Print error message */
76 DPRINT1("HAL: An invalid V86 opcode was encountered at address %x:%x\n",
77 BiosFrame->SegCs, BiosFrame->Eip);
78
79 /* Break */
80 DbgBreakPoint();
81 return FALSE;
82 }
83
84 BOOLEAN
85 FASTCALL
86 HalpPushInt(IN PHAL_BIOS_FRAME BiosFrame,
87 IN ULONG Interrupt)
88 {
89 PUSHORT Stack;
90 ULONG Eip;
91
92 /* Calculate stack address (SP) */
93 Stack = (PUSHORT)(BiosFrame->SsBase + (BiosFrame->Esp & 0xFFFF));
94
95 /* Push EFlags */
96 Stack--;
97 *Stack = BiosFrame->EFlags & 0xFFFF;
98
99 /* Push CS */
100 Stack--;
101 *Stack = BiosFrame->SegCs & 0xFFFF;
102
103 /* Push IP */
104 Stack--;
105 *Stack = BiosFrame->Eip & 0xFFFF;
106
107 /* Compute new CS:IP from the IVT address for this interrupt entry */
108 Eip = *(PULONG)(Interrupt * 4);
109 BiosFrame->Eip = Eip & 0xFFFF;
110 BiosFrame->SegCs = Eip >> 16;
111
112 /* Update stack address */
113 BiosFrame->Esp = (ULONG_PTR)Stack & 0xFFFF;
114
115 /* Update CS to linear */
116 BiosFrame->CsBase = BiosFrame->SegCs << 4;
117 BiosFrame->CsLimit = 0xFFFF;
118 BiosFrame->CsFlags = 0;
119
120 /* We're done */
121 return TRUE;
122 }
123
124 BOOLEAN
125 FASTCALL
126 HalpOpcodeINTnn(IN PHAL_BIOS_FRAME BiosFrame)
127 {
128 UCHAR Interrupt;
129 PKTRAP_FRAME TrapFrame;
130
131 /* Convert SS to linear */
132 BiosFrame->SsBase = BiosFrame->SegSs << 4;
133 BiosFrame->SsLimit = 0xFFFF;
134 BiosFrame->SsFlags = 0;
135
136 /* Increase EIP and validate */
137 BiosFrame->Eip++;
138 if (BiosFrame->Eip > BiosFrame->CsLimit) return FALSE;
139
140 /* Read interrupt number */
141 Interrupt = *(PUCHAR)(BiosFrame->CsBase + BiosFrame->Eip);
142
143 /* Increase EIP and push the interrupt */
144 BiosFrame->Eip++;
145 if (HalpPushInt(BiosFrame, Interrupt))
146 {
147 /* Update the trap frame */
148 TrapFrame = BiosFrame->TrapFrame;
149 TrapFrame->HardwareSegSs = BiosFrame->SegSs;
150 TrapFrame->HardwareEsp = BiosFrame->Esp;
151 TrapFrame->SegCs = BiosFrame->SegCs;
152 TrapFrame->EFlags = BiosFrame->EFlags;
153
154 /* Success */
155 return TRUE;
156 }
157
158 /* Failure */
159 return FALSE;
160 }
161
162 BOOLEAN
163 FASTCALL
164 HalpDispatchV86Opcode(IN PKTRAP_FRAME TrapFrame)
165 {
166 UCHAR Instruction;
167 HAL_BIOS_FRAME BiosFrame;
168
169 /* Fill out the BIOS frame */
170 BiosFrame.TrapFrame = TrapFrame;
171 BiosFrame.SegSs = TrapFrame->HardwareSegSs;
172 BiosFrame.Esp = TrapFrame->HardwareEsp;
173 BiosFrame.EFlags = TrapFrame->EFlags;
174 BiosFrame.SegCs = TrapFrame->SegCs;
175 BiosFrame.Eip = TrapFrame->Eip;
176 BiosFrame.Prefix = 0;
177
178 /* Convert CS to linear */
179 BiosFrame.CsBase = BiosFrame.SegCs << 4;
180 BiosFrame.CsLimit = 0xFFFF;
181 BiosFrame.CsFlags = 0;
182
183 /* Validate IP */
184 if (BiosFrame.Eip > BiosFrame.CsLimit) return FALSE;
185
186 /* Read IP */
187 Instruction = *(PUCHAR)(BiosFrame.CsBase + BiosFrame.Eip);
188 if (Instruction != 0xCD)
189 {
190 /* We only support INT */
191 HalpOpcodeInvalid(&BiosFrame);
192 return FALSE;
193 }
194
195 /* Handle the interrupt */
196 if (HalpOpcodeINTnn(&BiosFrame))
197 {
198 /* Update EIP */
199 TrapFrame->Eip = BiosFrame.Eip;
200
201 /* We're done */
202 return TRUE;
203 }
204
205 /* Failure */
206 return FALSE;
207 }
208
209 /* V86 TRAP HANDLERS **********************************************************/
210
211 VOID
212 FASTCALL
213 DECLSPEC_NORETURN
214 HalpTrap0DHandler(IN PKTRAP_FRAME TrapFrame)
215 {
216 /* Enter the trap */
217 KiEnterTrap(TrapFrame);
218
219 /* Check if this is a V86 trap */
220 if (TrapFrame->EFlags & EFLAGS_V86_MASK)
221 {
222 /* Dispatch the opcode and exit the trap */
223 HalpDispatchV86Opcode(TrapFrame);
224 KiEoiHelper(TrapFrame);
225 }
226
227 /* Strange, it isn't! This can happen during NMI */
228 DPRINT1("HAL: Trap0D while not in V86 mode\n");
229 KiDumpTrapFrame(TrapFrame);
230 while (TRUE);
231 }
232
233 KiTrap(HalpTrap0D, 0);
234
235 VOID
236 DECLSPEC_NORETURN
237 HalpTrap06(VOID)
238 {
239 PKTRAP_FRAME TrapFrame;
240
241 /* Restore ES/DS to known good values first */
242 Ke386SetEs(KGDT_R3_DATA | RPL_MASK);
243 Ke386SetDs(KGDT_R3_DATA | RPL_MASK);
244
245 /* Read trap frame address */
246 TrapFrame = (PKTRAP_FRAME)HalpSavedEsp;
247
248 /* Restore segments from the trap frame */
249 Ke386SetGs(TrapFrame->SegGs);
250 Ke386SetFs(TrapFrame->SegFs);
251 Ke386SetEs(TrapFrame->SegEs);
252 Ke386SetDs(TrapFrame->SegDs);
253
254 /* Restore EFlags */
255 __writeeflags(TrapFrame->EFlags);
256
257 /* Exit the V86 mode trap frame */
258 KiCallReturn(TrapFrame);
259 }
260
261 /* V8086 ENTER ****************************************************************/
262
263 VOID
264 FASTCALL
265 DECLSPEC_NORETURN
266 HalpBiosCallHandler(IN PKTRAP_FRAME TrapFrame)
267 {
268 /* Must be volatile so it doesn't get optimized away! */
269 volatile KTRAP_FRAME V86TrapFrame;
270 ULONG_PTR StackOffset, CodeOffset;
271
272 /* Fill out the quick-n-dirty trap frame */
273 TrapFrame->EFlags = __readeflags();
274 TrapFrame->SegGs = Ke386GetGs();
275 TrapFrame->SegFs = Ke386GetFs();
276 TrapFrame->SegEs = Ke386GetEs();
277 TrapFrame->SegDs = Ke386GetDs();
278
279 /* Our stack (the frame) */
280 HalpSavedEsp = (ULONG_PTR)TrapFrame;
281
282 /* Kill alignment faults */
283 __writecr0(__readcr0() & ~CR0_AM);
284
285 /* Set new stack address */
286 KeGetPcr()->TSS->Esp0 = HalpSavedEsp - sizeof(FX_SAVE_AREA);
287
288 /* Compute segmented IP and SP offsets */
289 StackOffset = (ULONG_PTR)&HalpRealModeEnd - 4 - (ULONG_PTR)HalpRealModeStart;
290 CodeOffset = (ULONG_PTR)HalpRealModeStart & 0xFFF;
291
292 /* Now build the V86 trap frame */
293 V86TrapFrame.V86Es = 0;
294 V86TrapFrame.V86Ds = 0;
295 V86TrapFrame.V86Gs = 0;
296 V86TrapFrame.V86Fs = 0;
297 V86TrapFrame.HardwareSegSs = 0x2000;
298 V86TrapFrame.HardwareEsp = StackOffset + CodeOffset;
299 V86TrapFrame.EFlags = __readeflags() | EFLAGS_V86_MASK | EFLAGS_IOPL;
300 V86TrapFrame.SegCs = 0x2000;
301 V86TrapFrame.Eip = CodeOffset;
302
303 /* Exit to V86 mode */
304 KiDirectTrapReturn((PKTRAP_FRAME)&V86TrapFrame);
305 }
306
307 KiTrampoline(HalpBiosCall, KI_PUSH_FAKE_ERROR_CODE | KI_NONVOLATILES_ONLY);
308
309 /* FUNCTIONS ******************************************************************/
310
311 VOID
312 NTAPI
313 HalpBorrowTss(VOID)
314 {
315 USHORT Tss;
316 PKGDTENTRY TssGdt;
317 ULONG_PTR TssLimit;
318 PKTSS TssBase;
319
320 //
321 // Get the current TSS and its GDT entry
322 //
323 Tss = Ke386GetTr();
324 TssGdt = &((PKIPCR)KeGetPcr())->GDT[Tss / sizeof(KGDTENTRY)];
325
326 //
327 // Get the KTSS limit and check if it has IOPM space
328 //
329 TssLimit = TssGdt->LimitLow | TssGdt->HighWord.Bits.LimitHi << 16;
330
331 //
332 // If the KTSS doesn't have enough space this is probably an NMI or DF
333 //
334 if (TssLimit > IOPM_SIZE)
335 {
336 //
337 // We are good to go
338 //
339 HalpSavedTss = 0;
340 return;
341 }
342
343 //
344 // Get the "real" TSS
345 //
346 TssGdt = &((PKIPCR)KeGetPcr())->GDT[KGDT_TSS / sizeof(KGDTENTRY)];
347 TssBase = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow |
348 TssGdt->HighWord.Bytes.BaseMid << 16 |
349 TssGdt->HighWord.Bytes.BaseHi << 24);
350
351 //
352 // Switch to it
353 //
354 KeGetPcr()->TSS = TssBase;
355
356 //
357 // Set it up
358 //
359 TssGdt->HighWord.Bits.Type = I386_TSS;
360 TssGdt->HighWord.Bits.Pres = 1;
361 TssGdt->HighWord.Bits.Dpl = 0;
362
363 //
364 // Load new TSS and return old one
365 //
366 Ke386SetTr(KGDT_TSS);
367 HalpSavedTss = Tss;
368 }
369
370 VOID
371 NTAPI
372 HalpReturnTss(VOID)
373 {
374 PKGDTENTRY TssGdt;
375 PKTSS TssBase;
376
377 //
378 // Get the original TSS
379 //
380 TssGdt = &((PKIPCR)KeGetPcr())->GDT[HalpSavedTss / sizeof(KGDTENTRY)];
381 TssBase = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow |
382 TssGdt->HighWord.Bytes.BaseMid << 16 |
383 TssGdt->HighWord.Bytes.BaseHi << 24);
384
385 //
386 // Switch to it
387 //
388 KeGetPcr()->TSS = TssBase;
389
390 //
391 // Set it up
392 //
393 TssGdt->HighWord.Bits.Type = I386_TSS;
394 TssGdt->HighWord.Bits.Pres = 1;
395 TssGdt->HighWord.Bits.Dpl = 0;
396
397 //
398 // Load old TSS
399 //
400 Ke386SetTr(HalpSavedTss);
401 }
402
403 VOID
404 NTAPI
405 HalpStoreAndClearIopm(VOID)
406 {
407 ULONG i, j;
408 PUSHORT Entry = HalpSavedIoMap;
409
410 //
411 // Loop the I/O Map
412 //
413 for (i = j = 0; i < (IOPM_SIZE) / 2; i++)
414 {
415 //
416 // Check for non-FFFF entry
417 //
418 if (*Entry != 0xFFFF)
419 {
420 //
421 // Save it
422 //
423 ASSERT(j < 32);
424 HalpSavedIoMapData[j][0] = i;
425 HalpSavedIoMapData[j][1] = *Entry;
426 }
427
428 //
429 // Clear it
430 //
431 *Entry++ = 0;
432 }
433
434 //
435 // Terminate it
436 //
437 while (i++ < (IOPM_FULL_SIZE / 2)) *Entry++ = 0xFFFF;
438
439 //
440 // Return the entries we saved
441 //
442 HalpSavedIoMapEntries = j;
443 }
444
445 VOID
446 NTAPI
447 HalpRestoreIopm(VOID)
448 {
449 ULONG i = HalpSavedIoMapEntries;
450
451 //
452 // Set default state
453 //
454 RtlFillMemory(HalpSavedIoMap, 0xFF, IOPM_FULL_SIZE);
455
456 //
457 // Restore the backed up copy, and initialize it
458 //
459 while (i--) HalpSavedIoMap[HalpSavedIoMapData[i][0]] = HalpSavedIoMapData[i][1];
460 }
461
462 VOID
463 NTAPI
464 HalpMapRealModeMemory(VOID)
465 {
466 PHARDWARE_PTE Pte, V86Pte;
467 ULONG i;
468
469 //
470 // Get the page table directory for the lowest meg of memory
471 //
472 Pte = HalAddressToPde(0);
473 HalpSavedPfn = Pte->PageFrameNumber;
474 HalpSavedPte = *Pte;
475
476 //
477 // Map it to the HAL reserved region and make it valid
478 //
479 Pte->Valid = 1;
480 Pte->Write = 1;
481 Pte->Owner = 1;
482 Pte->PageFrameNumber = (HalAddressToPde(0xFFC00000))->PageFrameNumber;
483
484 //
485 // Flush the TLB
486 //
487 HalpFlushTLB();
488
489 //
490 // Now loop the first meg of memory
491 //
492 for (i = 0; i < 0x100000; i += PAGE_SIZE)
493 {
494 //
495 // Identity map it
496 //
497 Pte = HalAddressToPte(i);
498 Pte->PageFrameNumber = i >> PAGE_SHIFT;
499 Pte->Valid = 1;
500 Pte->Write = 1;
501 Pte->Owner = 1;
502 }
503
504 //
505 // Now get the entry for our real mode V86 code and the target
506 //
507 Pte = HalAddressToPte(0x20000);
508 V86Pte = HalAddressToPte(&HalpRealModeStart);
509 do
510 {
511 //
512 // Map the physical address into our real-mode region
513 //
514 Pte->PageFrameNumber = V86Pte->PageFrameNumber;
515
516 //
517 // Keep going until we've reached the end of our region
518 //
519 Pte++;
520 V86Pte++;
521 } while (V86Pte <= HalAddressToPte(&HalpRealModeEnd));
522
523 //
524 // Flush the TLB
525 //
526 HalpFlushTLB();
527 }
528
529 VOID
530 NTAPI
531 HalpSwitchToRealModeTrapHandlers(VOID)
532 {
533 //
534 // Save the current Invalid Opcode and General Protection Fault Handlers
535 //
536 HalpGpfHandler = KeQueryInterruptHandler(13);
537 HalpBopHandler = KeQueryInterruptHandler(6);
538
539 //
540 // Now set our own GPF handler to handle exceptions while in real mode
541 //
542 KeRegisterInterruptHandler(13, HalpTrap0D);
543
544 //
545 // And our own invalid opcode handler to detect the BOP to get us out
546 //
547 KeRegisterInterruptHandler(6, HalpTrap06);
548 }
549
550 VOID
551 NTAPI
552 HalpSetupRealModeIoPermissionsAndTask(VOID)
553 {
554 //
555 // Switch to valid TSS
556 //
557 HalpBorrowTss();
558
559 //
560 // Save a copy of the I/O Map and delete it
561 //
562 HalpSavedIoMap = (PUSHORT)&(KeGetPcr()->TSS->IoMaps[0]);
563 HalpStoreAndClearIopm();
564
565 //
566 // Save the IOPM and switch to the real-mode one
567 //
568 HalpSavedIopmBase = KeGetPcr()->TSS->IoMapBase;
569 KeGetPcr()->TSS->IoMapBase = KiComputeIopmOffset(1);
570
571 //
572 // Save our stack pointer
573 //
574 HalpSavedEsp0 = KeGetPcr()->TSS->Esp0;
575 }
576
577 VOID
578 NTAPI
579 HalpRestoreTrapHandlers(VOID)
580 {
581 //
582 // Keep dummy GPF handler in case we get an NMI during V8086
583 //
584 if (!HalpNMIInProgress)
585 {
586 //
587 // Not an NMI -- put back the original handler
588 //
589 KeRegisterInterruptHandler(13, HalpGpfHandler);
590 }
591
592 //
593 // Restore invalid opcode handler
594 //
595 KeRegisterInterruptHandler(6, HalpBopHandler);
596 }
597
598 VOID
599 NTAPI
600 HalpRestoreIoPermissionsAndTask(VOID)
601 {
602 //
603 // Restore the stack pointer
604 //
605 KeGetPcr()->TSS->Esp0 = HalpSavedEsp0;
606
607 //
608 // Restore the I/O Map
609 //
610 HalpRestoreIopm();
611
612 //
613 // Restore the IOPM
614 //
615 KeGetPcr()->TSS->IoMapBase = HalpSavedIopmBase;
616
617 //
618 // Restore the TSS
619 //
620 if (HalpSavedTss) HalpReturnTss();
621 }
622
623 VOID
624 NTAPI
625 HalpUnmapRealModeMemory(VOID)
626 {
627 ULONG i;
628 PHARDWARE_PTE Pte;
629
630 //
631 // Loop the first meg of memory
632 //
633 for (i = 0; i < 0x100000; i += PAGE_SIZE)
634 {
635 //
636 // Invalidate each PTE
637 //
638 Pte = HalAddressToPte(i);
639 Pte->Valid = 0;
640 Pte->Write = 0;
641 Pte->Owner = 0;
642 Pte->PageFrameNumber = 0;
643 }
644
645 //
646 // Restore the PDE for the lowest megabyte of memory
647 //
648 Pte = HalAddressToPde(0);
649 *Pte = HalpSavedPte;
650 Pte->PageFrameNumber = HalpSavedPfn;
651
652 //
653 // Flush the TLB
654 //
655 HalpFlushTLB();
656 }
657
658 BOOLEAN
659 NTAPI
660 HalpBiosDisplayReset(VOID)
661 {
662 ULONG Flags;
663 PHARDWARE_PTE IdtPte;
664 BOOLEAN RestoreWriteProtection = FALSE;
665
666 //
667 // Disable interrupts
668 //
669 Flags = __readeflags();
670 _disable();
671
672 //
673 // Map memory available to the V8086 real-mode code
674 //
675 HalpMapRealModeMemory();
676
677 //
678 // On P5, the first 7 entries of the IDT are write protected to work around
679 // the cmpxchg8b lock errata. Unprotect them here so we can set our custom
680 // invalid op-code handler.
681 //
682 IdtPte = HalAddressToPte(((PKIPCR)KeGetPcr())->IDT);
683 RestoreWriteProtection = IdtPte->Write;
684 IdtPte->Write = 1;
685
686 //
687 // Use special invalid opcode and GPF trap handlers
688 //
689 HalpSwitchToRealModeTrapHandlers();
690
691 //
692 // Configure the IOPM and TSS
693 //
694 HalpSetupRealModeIoPermissionsAndTask();
695
696 //
697 // Now jump to real mode
698 //
699 HalpBiosCall();
700
701 //
702 // Restore kernel trap handlers
703 //
704 HalpRestoreTrapHandlers();
705
706 //
707 // Restore write permission
708 //
709 IdtPte->Write = RestoreWriteProtection;
710
711 //
712 // Restore TSS and IOPM
713 //
714 HalpRestoreIoPermissionsAndTask();
715
716 //
717 // Restore low memory mapping
718 //
719 HalpUnmapRealModeMemory();
720
721 //
722 // Restore interrupts if they were previously enabled
723 //
724 __writeeflags(Flags);
725 return TRUE;
726 }
727
728 /* EOF */