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