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