4658e2e02402250ce3e54d49a0b9481be7e82da8
[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 while (TRUE);
230 }
231
232 KiTrap(HalpTrap0D, 0);
233
234 VOID
235 DECLSPEC_NORETURN
236 HalpTrap06(VOID)
237 {
238 PKTRAP_FRAME TrapFrame;
239
240 /* Restore ES/DS to known good values first */
241 Ke386SetEs(KGDT_R3_DATA | RPL_MASK);
242 Ke386SetDs(KGDT_R3_DATA | RPL_MASK);
243
244 /* Read trap frame address */
245 TrapFrame = (PKTRAP_FRAME)HalpSavedEsp;
246
247 /* Restore segments from the trap frame */
248 Ke386SetGs(TrapFrame->SegGs);
249 Ke386SetFs(TrapFrame->SegFs);
250 Ke386SetEs(TrapFrame->SegEs);
251 Ke386SetDs(TrapFrame->SegDs);
252
253 /* Restore EFlags */
254 __writeeflags(TrapFrame->EFlags);
255
256 /* Exit the V86 mode trap frame */
257 KiCallReturn(TrapFrame);
258 }
259
260 /* V8086 ENTER ****************************************************************/
261
262 VOID
263 FASTCALL
264 DECLSPEC_NORETURN
265 HalpBiosCallHandler(IN PKTRAP_FRAME TrapFrame)
266 {
267 /* Must be volatile so it doesn't get optimized away! */
268 volatile KTRAP_FRAME V86TrapFrame;
269 ULONG_PTR StackOffset, CodeOffset;
270
271 /* Fill out the quick-n-dirty trap frame */
272 TrapFrame->EFlags = __readeflags();
273 TrapFrame->SegGs = Ke386GetGs();
274 TrapFrame->SegFs = Ke386GetFs();
275 TrapFrame->SegEs = Ke386GetEs();
276 TrapFrame->SegDs = Ke386GetDs();
277
278 /* Our stack (the frame) */
279 HalpSavedEsp = (ULONG_PTR)TrapFrame;
280
281 /* Kill alignment faults */
282 __writecr0(__readcr0() & ~CR0_AM);
283
284 /* Set new stack address */
285 KeGetPcr()->TSS->Esp0 = HalpSavedEsp - sizeof(FX_SAVE_AREA);
286
287 /* Compute segmented IP and SP offsets */
288 StackOffset = (ULONG_PTR)&HalpRealModeEnd - 4 - (ULONG_PTR)HalpRealModeStart;
289 CodeOffset = (ULONG_PTR)HalpRealModeStart & 0xFFF;
290
291 /* Now build the V86 trap frame */
292 V86TrapFrame.V86Es = 0;
293 V86TrapFrame.V86Ds = 0;
294 V86TrapFrame.V86Gs = 0;
295 V86TrapFrame.V86Fs = 0;
296 V86TrapFrame.HardwareSegSs = 0x2000;
297 V86TrapFrame.HardwareEsp = StackOffset + CodeOffset;
298 V86TrapFrame.EFlags = __readeflags() | EFLAGS_V86_MASK | EFLAGS_IOPL;
299 V86TrapFrame.SegCs = 0x2000;
300 V86TrapFrame.Eip = CodeOffset;
301
302 /* Exit to V86 mode */
303 KiDirectTrapReturn((PKTRAP_FRAME)&V86TrapFrame);
304 }
305
306 KiTrampoline(HalpBiosCall, KI_PUSH_FAKE_ERROR_CODE | KI_NONVOLATILES_ONLY);
307
308 /* FUNCTIONS ******************************************************************/
309
310 VOID
311 NTAPI
312 HalpBorrowTss(VOID)
313 {
314 USHORT Tss;
315 PKGDTENTRY TssGdt;
316 ULONG_PTR TssLimit;
317 PKTSS TssBase;
318
319 //
320 // Get the current TSS and its GDT entry
321 //
322 Tss = Ke386GetTr();
323 TssGdt = &((PKIPCR)KeGetPcr())->GDT[Tss / sizeof(KGDTENTRY)];
324
325 //
326 // Get the KTSS limit and check if it has IOPM space
327 //
328 TssLimit = TssGdt->LimitLow | TssGdt->HighWord.Bits.LimitHi << 16;
329
330 //
331 // If the KTSS doesn't have enough space this is probably an NMI or DF
332 //
333 if (TssLimit > IOPM_SIZE)
334 {
335 //
336 // We are good to go
337 //
338 HalpSavedTss = 0;
339 return;
340 }
341
342 //
343 // Get the "real" TSS
344 //
345 TssGdt = &((PKIPCR)KeGetPcr())->GDT[KGDT_TSS / sizeof(KGDTENTRY)];
346 TssBase = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow |
347 TssGdt->HighWord.Bytes.BaseMid << 16 |
348 TssGdt->HighWord.Bytes.BaseHi << 24);
349
350 //
351 // Switch to it
352 //
353 KeGetPcr()->TSS = TssBase;
354
355 //
356 // Set it up
357 //
358 TssGdt->HighWord.Bits.Type = I386_TSS;
359 TssGdt->HighWord.Bits.Pres = 1;
360 TssGdt->HighWord.Bits.Dpl = 0;
361
362 //
363 // Load new TSS and return old one
364 //
365 Ke386SetTr(KGDT_TSS);
366 HalpSavedTss = Tss;
367 }
368
369 VOID
370 NTAPI
371 HalpReturnTss(VOID)
372 {
373 PKGDTENTRY TssGdt;
374 PKTSS TssBase;
375
376 //
377 // Get the original TSS
378 //
379 TssGdt = &((PKIPCR)KeGetPcr())->GDT[HalpSavedTss / sizeof(KGDTENTRY)];
380 TssBase = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow |
381 TssGdt->HighWord.Bytes.BaseMid << 16 |
382 TssGdt->HighWord.Bytes.BaseHi << 24);
383
384 //
385 // Switch to it
386 //
387 KeGetPcr()->TSS = TssBase;
388
389 //
390 // Set it up
391 //
392 TssGdt->HighWord.Bits.Type = I386_TSS;
393 TssGdt->HighWord.Bits.Pres = 1;
394 TssGdt->HighWord.Bits.Dpl = 0;
395
396 //
397 // Load old TSS
398 //
399 Ke386SetTr(HalpSavedTss);
400 }
401
402 VOID
403 NTAPI
404 HalpStoreAndClearIopm(VOID)
405 {
406 ULONG i, j;
407 PUSHORT Entry = HalpSavedIoMap;
408
409 //
410 // Loop the I/O Map
411 //
412 for (i = j = 0; i < (IOPM_SIZE) / 2; i++)
413 {
414 //
415 // Check for non-FFFF entry
416 //
417 if (*Entry != 0xFFFF)
418 {
419 //
420 // Save it
421 //
422 ASSERT(j < 32);
423 HalpSavedIoMapData[j][0] = i;
424 HalpSavedIoMapData[j][1] = *Entry;
425 }
426
427 //
428 // Clear it
429 //
430 *Entry++ = 0;
431 }
432
433 //
434 // Terminate it
435 //
436 while (i++ < (IOPM_FULL_SIZE / 2)) *Entry++ = 0xFFFF;
437
438 //
439 // Return the entries we saved
440 //
441 HalpSavedIoMapEntries = j;
442 }
443
444 VOID
445 NTAPI
446 HalpRestoreIopm(VOID)
447 {
448 ULONG i = HalpSavedIoMapEntries;
449
450 //
451 // Set default state
452 //
453 RtlFillMemory(HalpSavedIoMap, 0xFF, IOPM_FULL_SIZE);
454
455 //
456 // Restore the backed up copy, and initialize it
457 //
458 while (i--) HalpSavedIoMap[HalpSavedIoMapData[i][0]] = HalpSavedIoMapData[i][1];
459 }
460
461 VOID
462 NTAPI
463 HalpMapRealModeMemory(VOID)
464 {
465 PHARDWARE_PTE Pte, V86Pte;
466 ULONG i;
467
468 //
469 // Get the page table directory for the lowest meg of memory
470 //
471 Pte = HalAddressToPde(0);
472 HalpSavedPfn = Pte->PageFrameNumber;
473 HalpSavedPte = *Pte;
474
475 //
476 // Map it to the HAL reserved region and make it valid
477 //
478 Pte->Valid = 1;
479 Pte->Write = 1;
480 Pte->Owner = 1;
481 Pte->PageFrameNumber = (HalAddressToPde(0xFFC00000))->PageFrameNumber;
482
483 //
484 // Flush the TLB
485 //
486 HalpFlushTLB();
487
488 //
489 // Now loop the first meg of memory
490 //
491 for (i = 0; i < 0x100000; i += PAGE_SIZE)
492 {
493 //
494 // Identity map it
495 //
496 Pte = HalAddressToPte(i);
497 Pte->PageFrameNumber = i >> PAGE_SHIFT;
498 Pte->Valid = 1;
499 Pte->Write = 1;
500 Pte->Owner = 1;
501 }
502
503 //
504 // Now get the entry for our real mode V86 code and the target
505 //
506 Pte = HalAddressToPte(0x20000);
507 V86Pte = HalAddressToPte(&HalpRealModeStart);
508 do
509 {
510 //
511 // Map the physical address into our real-mode region
512 //
513 Pte->PageFrameNumber = V86Pte->PageFrameNumber;
514
515 //
516 // Keep going until we've reached the end of our region
517 //
518 Pte++;
519 V86Pte++;
520 } while (V86Pte <= HalAddressToPte(&HalpRealModeEnd));
521
522 //
523 // Flush the TLB
524 //
525 HalpFlushTLB();
526 }
527
528 VOID
529 NTAPI
530 HalpSwitchToRealModeTrapHandlers(VOID)
531 {
532 //
533 // Save the current Invalid Opcode and General Protection Fault Handlers
534 //
535 HalpGpfHandler = KeQueryInterruptHandler(13);
536 HalpBopHandler = KeQueryInterruptHandler(6);
537
538 //
539 // Now set our own GPF handler to handle exceptions while in real mode
540 //
541 KeRegisterInterruptHandler(13, HalpTrap0D);
542
543 //
544 // And our own invalid opcode handler to detect the BOP to get us out
545 //
546 KeRegisterInterruptHandler(6, HalpTrap06);
547 }
548
549 VOID
550 NTAPI
551 HalpSetupRealModeIoPermissionsAndTask(VOID)
552 {
553 //
554 // Switch to valid TSS
555 //
556 HalpBorrowTss();
557
558 //
559 // Save a copy of the I/O Map and delete it
560 //
561 HalpSavedIoMap = (PUSHORT)&(KeGetPcr()->TSS->IoMaps[0]);
562 HalpStoreAndClearIopm();
563
564 //
565 // Save the IOPM and switch to the real-mode one
566 //
567 HalpSavedIopmBase = KeGetPcr()->TSS->IoMapBase;
568 KeGetPcr()->TSS->IoMapBase = KiComputeIopmOffset(1);
569
570 //
571 // Save our stack pointer
572 //
573 HalpSavedEsp0 = KeGetPcr()->TSS->Esp0;
574 }
575
576 VOID
577 NTAPI
578 HalpRestoreTrapHandlers(VOID)
579 {
580 //
581 // Keep dummy GPF handler in case we get an NMI during V8086
582 //
583 if (!HalpNMIInProgress)
584 {
585 //
586 // Not an NMI -- put back the original handler
587 //
588 KeRegisterInterruptHandler(13, HalpGpfHandler);
589 }
590
591 //
592 // Restore invalid opcode handler
593 //
594 KeRegisterInterruptHandler(6, HalpBopHandler);
595 }
596
597 VOID
598 NTAPI
599 HalpRestoreIoPermissionsAndTask(VOID)
600 {
601 //
602 // Restore the stack pointer
603 //
604 KeGetPcr()->TSS->Esp0 = HalpSavedEsp0;
605
606 //
607 // Restore the I/O Map
608 //
609 HalpRestoreIopm();
610
611 //
612 // Restore the IOPM
613 //
614 KeGetPcr()->TSS->IoMapBase = HalpSavedIopmBase;
615
616 //
617 // Restore the TSS
618 //
619 if (HalpSavedTss) HalpReturnTss();
620 }
621
622 VOID
623 NTAPI
624 HalpUnmapRealModeMemory(VOID)
625 {
626 ULONG i;
627 PHARDWARE_PTE Pte;
628
629 //
630 // Loop the first meg of memory
631 //
632 for (i = 0; i < 0x100000; i += PAGE_SIZE)
633 {
634 //
635 // Invalidate each PTE
636 //
637 Pte = HalAddressToPte(i);
638 Pte->Valid = 0;
639 Pte->Write = 0;
640 Pte->Owner = 0;
641 Pte->PageFrameNumber = 0;
642 }
643
644 //
645 // Restore the PDE for the lowest megabyte of memory
646 //
647 Pte = HalAddressToPde(0);
648 *Pte = HalpSavedPte;
649 Pte->PageFrameNumber = HalpSavedPfn;
650
651 //
652 // Flush the TLB
653 //
654 HalpFlushTLB();
655 }
656
657 BOOLEAN
658 NTAPI
659 HalpBiosDisplayReset(VOID)
660 {
661 ULONG Flags;
662 PHARDWARE_PTE IdtPte;
663 BOOLEAN RestoreWriteProtection = FALSE;
664
665 //
666 // Disable interrupts
667 //
668 Flags = __readeflags();
669 _disable();
670
671 //
672 // Map memory available to the V8086 real-mode code
673 //
674 HalpMapRealModeMemory();
675
676 //
677 // On P5, the first 7 entries of the IDT are write protected to work around
678 // the cmpxchg8b lock errata. Unprotect them here so we can set our custom
679 // invalid op-code handler.
680 //
681 IdtPte = HalAddressToPte(((PKIPCR)KeGetPcr())->IDT);
682 RestoreWriteProtection = IdtPte->Write;
683 IdtPte->Write = 1;
684
685 //
686 // Use special invalid opcode and GPF trap handlers
687 //
688 HalpSwitchToRealModeTrapHandlers();
689
690 //
691 // Configure the IOPM and TSS
692 //
693 HalpSetupRealModeIoPermissionsAndTask();
694
695 //
696 // Now jump to real mode
697 //
698 HalpBiosCall();
699
700 //
701 // Restore kernel trap handlers
702 //
703 HalpRestoreTrapHandlers();
704
705 //
706 // Restore write permission
707 //
708 IdtPte->Write = RestoreWriteProtection;
709
710 //
711 // Restore TSS and IOPM
712 //
713 HalpRestoreIoPermissionsAndTask();
714
715 //
716 // Restore low memory mapping
717 //
718 HalpUnmapRealModeMemory();
719
720 //
721 // Restore interrupts if they were previously enabled
722 //
723 __writeeflags(Flags);
724 return TRUE;
725 }
726
727 /* EOF */