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