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