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