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