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