[HAL]: Use Bus Handler support for HalFindBusAddressTranslation. Implement HalpContex...
[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 VOID
201 FASTCALL
202 DECLSPEC_NORETURN
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 ULONG 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 }
400
401 //
402 // Clear it
403 //
404 *Entry++ = 0;
405 }
406
407 //
408 // Terminate it
409 //
410 while (i++ < (IOPM_FULL_SIZE / 2)) *Entry++ = 0xFFFF;
411
412 //
413 // Return the entries we saved
414 //
415 HalpSavedIoMapEntries = j;
416 }
417
418 VOID
419 NTAPI
420 HalpRestoreIopm(VOID)
421 {
422 ULONG i = HalpSavedIoMapEntries;
423
424 //
425 // Set default state
426 //
427 RtlFillMemory(HalpSavedIoMap, 0xFF, IOPM_FULL_SIZE);
428
429 //
430 // Restore the backed up copy, and initialize it
431 //
432 while (i--) HalpSavedIoMap[HalpSavedIoMapData[i][0]] = HalpSavedIoMapData[i][1];
433 }
434
435 #ifndef _MINIHAL_
436 VOID
437 NTAPI
438 HalpMapRealModeMemory(VOID)
439 {
440 PHARDWARE_PTE Pte, V86Pte;
441 ULONG i;
442
443 //
444 // Get the page table directory for the lowest meg of memory
445 //
446 Pte = HalAddressToPde(0);
447 HalpSavedPfn = Pte->PageFrameNumber;
448 HalpSavedPte = *Pte;
449
450 //
451 // Map it to the HAL reserved region and make it valid
452 //
453 Pte->Valid = 1;
454 Pte->Write = 1;
455 Pte->Owner = 1;
456 Pte->PageFrameNumber = (HalAddressToPde(0xFFC00000))->PageFrameNumber;
457
458 //
459 // Flush the TLB
460 //
461 HalpFlushTLB();
462
463 //
464 // Now loop the first meg of memory
465 //
466 for (i = 0; i < 0x100000; i += PAGE_SIZE)
467 {
468 //
469 // Identity map it
470 //
471 Pte = HalAddressToPte(i);
472 Pte->PageFrameNumber = i >> PAGE_SHIFT;
473 Pte->Valid = 1;
474 Pte->Write = 1;
475 Pte->Owner = 1;
476 }
477
478 //
479 // Now get the entry for our real mode V86 code and the target
480 //
481 Pte = HalAddressToPte(0x20000);
482 V86Pte = HalAddressToPte(&HalpRealModeStart);
483 do
484 {
485 //
486 // Map the physical address into our real-mode region
487 //
488 Pte->PageFrameNumber = V86Pte->PageFrameNumber;
489
490 //
491 // Keep going until we've reached the end of our region
492 //
493 Pte++;
494 V86Pte++;
495 } while (V86Pte <= HalAddressToPte(&HalpRealModeEnd));
496
497 //
498 // Flush the TLB
499 //
500 HalpFlushTLB();
501 }
502
503 VOID
504 NTAPI
505 HalpSwitchToRealModeTrapHandlers(VOID)
506 {
507 //
508 // Save the current Invalid Opcode and General Protection Fault Handlers
509 //
510 HalpGpfHandler = KeQueryInterruptHandler(13);
511 HalpBopHandler = KeQueryInterruptHandler(6);
512
513 //
514 // Now set our own GPF handler to handle exceptions while in real mode
515 //
516 KeRegisterInterruptHandler(13, HalpTrap0D);
517
518 //
519 // And our own invalid opcode handler to detect the BOP to get us out
520 //
521 KeRegisterInterruptHandler(6, HalpTrap06);
522 }
523 #endif
524
525 VOID
526 NTAPI
527 HalpSetupRealModeIoPermissionsAndTask(VOID)
528 {
529 //
530 // Switch to valid TSS
531 //
532 HalpBorrowTss();
533
534 //
535 // Save a copy of the I/O Map and delete it
536 //
537 HalpSavedIoMap = (PUSHORT)&(KeGetPcr()->TSS->IoMaps[0]);
538 HalpStoreAndClearIopm();
539
540 //
541 // Save the IOPM and switch to the real-mode one
542 //
543 HalpSavedIopmBase = KeGetPcr()->TSS->IoMapBase;
544 KeGetPcr()->TSS->IoMapBase = KiComputeIopmOffset(1);
545
546 //
547 // Save our stack pointer
548 //
549 HalpSavedEsp0 = KeGetPcr()->TSS->Esp0;
550 }
551
552 VOID
553 NTAPI
554 HalpRestoreTrapHandlers(VOID)
555 {
556 //
557 // Keep dummy GPF handler in case we get an NMI during V8086
558 //
559 if (!HalpNMIInProgress)
560 {
561 //
562 // Not an NMI -- put back the original handler
563 //
564 KeRegisterInterruptHandler(13, HalpGpfHandler);
565 }
566
567 //
568 // Restore invalid opcode handler
569 //
570 KeRegisterInterruptHandler(6, HalpBopHandler);
571 }
572
573 VOID
574 NTAPI
575 HalpRestoreIoPermissionsAndTask(VOID)
576 {
577 //
578 // Restore the stack pointer
579 //
580 KeGetPcr()->TSS->Esp0 = HalpSavedEsp0;
581
582 //
583 // Restore the I/O Map
584 //
585 HalpRestoreIopm();
586
587 //
588 // Restore the IOPM
589 //
590 KeGetPcr()->TSS->IoMapBase = HalpSavedIopmBase;
591
592 //
593 // Restore the TSS
594 //
595 if (HalpSavedTss) HalpReturnTss();
596 }
597
598 VOID
599 NTAPI
600 HalpUnmapRealModeMemory(VOID)
601 {
602 ULONG i;
603 PHARDWARE_PTE Pte;
604
605 //
606 // Loop the first meg of memory
607 //
608 for (i = 0; i < 0x100000; i += PAGE_SIZE)
609 {
610 //
611 // Invalidate each PTE
612 //
613 Pte = HalAddressToPte(i);
614 Pte->Valid = 0;
615 Pte->Write = 0;
616 Pte->Owner = 0;
617 Pte->PageFrameNumber = 0;
618 }
619
620 //
621 // Restore the PDE for the lowest megabyte of memory
622 //
623 Pte = HalAddressToPde(0);
624 *Pte = HalpSavedPte;
625 Pte->PageFrameNumber = HalpSavedPfn;
626
627 //
628 // Flush the TLB
629 //
630 HalpFlushTLB();
631 }
632
633 #ifndef _MINIHAL_
634 BOOLEAN
635 NTAPI
636 HalpBiosDisplayReset(VOID)
637 {
638 ULONG Flags;
639 PHARDWARE_PTE IdtPte;
640 BOOLEAN RestoreWriteProtection = FALSE;
641
642 //
643 // Disable interrupts
644 //
645 Flags = __readeflags();
646 _disable();
647
648 //
649 // Map memory available to the V8086 real-mode code
650 //
651 HalpMapRealModeMemory();
652
653 //
654 // On P5, the first 7 entries of the IDT are write protected to work around
655 // the cmpxchg8b lock errata. Unprotect them here so we can set our custom
656 // invalid op-code handler.
657 //
658 IdtPte = HalAddressToPte(((PKIPCR)KeGetPcr())->IDT);
659 RestoreWriteProtection = IdtPte->Write;
660 IdtPte->Write = 1;
661
662 //
663 // Use special invalid opcode and GPF trap handlers
664 //
665 HalpSwitchToRealModeTrapHandlers();
666
667 //
668 // Configure the IOPM and TSS
669 //
670 HalpSetupRealModeIoPermissionsAndTask();
671
672 //
673 // Now jump to real mode
674 //
675 HalpBiosCall();
676
677 //
678 // Restore kernel trap handlers
679 //
680 HalpRestoreTrapHandlers();
681
682 //
683 // Restore write permission
684 //
685 IdtPte->Write = RestoreWriteProtection;
686
687 //
688 // Restore TSS and IOPM
689 //
690 HalpRestoreIoPermissionsAndTask();
691
692 //
693 // Restore low memory mapping
694 //
695 HalpUnmapRealModeMemory();
696
697 //
698 // Restore interrupts if they were previously enabled
699 //
700 __writeeflags(Flags);
701 return TRUE;
702 }
703 #endif
704
705 /* EOF */