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 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 288 /* FUNCTIONS ******************************************************************/ 289 290 VOID 291 NTAPI 292 HalpBorrowTss(VOID) 293 { 294 USHORT Tss; 295 PKGDTENTRY TssGdt; 296 ULONG_PTR TssLimit; 297 PKTSS TssBase; 298 299 // 300 // Get the current TSS and its GDT entry 301 // 302 Tss = Ke386GetTr(); 303 TssGdt = &KeGetPcr()->GDT[Tss / sizeof(KGDTENTRY)]; 304 305 // 306 // Get the KTSS limit and check if it has IOPM space 307 // 308 TssLimit = TssGdt->LimitLow | TssGdt->HighWord.Bits.LimitHi << 16; 309 310 // 311 // If the KTSS doesn't have enough space this is probably an NMI or DF 312 // 313 if (TssLimit > IOPM_SIZE) 314 { 315 // 316 // We are good to go 317 // 318 HalpSavedTss = 0; 319 return; 320 } 321 322 // 323 // Get the "real" TSS 324 // 325 TssGdt = &KeGetPcr()->GDT[KGDT_TSS / sizeof(KGDTENTRY)]; 326 TssBase = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow | 327 TssGdt->HighWord.Bytes.BaseMid << 16 | 328 TssGdt->HighWord.Bytes.BaseHi << 24); 329 330 // 331 // Switch to it 332 // 333 KeGetPcr()->TSS = TssBase; 334 335 // 336 // Set it up 337 // 338 TssGdt->HighWord.Bits.Type = I386_TSS; 339 TssGdt->HighWord.Bits.Pres = 1; 340 TssGdt->HighWord.Bits.Dpl = 0; 341 342 // 343 // Load new TSS and return old one 344 // 345 Ke386SetTr(KGDT_TSS); 346 HalpSavedTss = Tss; 347 } 348 349 VOID 350 NTAPI 351 HalpReturnTss(VOID) 352 { 353 PKGDTENTRY TssGdt; 354 PKTSS TssBase; 355 356 // 357 // Get the original TSS 358 // 359 TssGdt = &KeGetPcr()->GDT[HalpSavedTss / sizeof(KGDTENTRY)]; 360 TssBase = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow | 361 TssGdt->HighWord.Bytes.BaseMid << 16 | 362 TssGdt->HighWord.Bytes.BaseHi << 24); 363 364 // 365 // Switch to it 366 // 367 KeGetPcr()->TSS = TssBase; 368 369 // 370 // Set it up 371 // 372 TssGdt->HighWord.Bits.Type = I386_TSS; 373 TssGdt->HighWord.Bits.Pres = 1; 374 TssGdt->HighWord.Bits.Dpl = 0; 375 376 // 377 // Load old TSS 378 // 379 Ke386SetTr(HalpSavedTss); 380 } 381 382 VOID 383 NTAPI 384 HalpStoreAndClearIopm(VOID) 385 { 386 USHORT i, j; 387 PUSHORT Entry = HalpSavedIoMap; 388 389 // 390 // Loop the I/O Map 391 // 392 for (i = j = 0; i < IOPM_SIZE / sizeof(USHORT); i++) 393 { 394 // 395 // Check for non-FFFF entry 396 // 397 if (*Entry != 0xFFFF) 398 { 399 // 400 // Save it 401 // 402 HalpSavedIoMapData[j][0] = i; 403 HalpSavedIoMapData[j][1] = *Entry; 404 j++; 405 } 406 407 // 408 // Clear it 409 // 410 *Entry++ = 0; 411 } 412 413 // 414 // Terminate it 415 // 416 while (i++ < IOPM_FULL_SIZE / sizeof(USHORT)) 417 { 418 *Entry++ = 0xFFFF; 419 } 420 421 // 422 // Return the entries we saved 423 // 424 HalpSavedIoMapEntries = j; 425 } 426 427 VOID 428 NTAPI 429 HalpRestoreIopm(VOID) 430 { 431 ULONG i = HalpSavedIoMapEntries; 432 433 // 434 // Set default state 435 // 436 RtlFillMemory(HalpSavedIoMap, IOPM_FULL_SIZE, 0xFF); 437 438 // 439 // Restore the backed up copy, and initialize it 440 // 441 while (i--) HalpSavedIoMap[HalpSavedIoMapData[i][0]] = HalpSavedIoMapData[i][1]; 442 } 443 444 VOID 445 NTAPI 446 HalpMapRealModeMemory(VOID) 447 { 448 PHARDWARE_PTE Pte, V86Pte; 449 ULONG i; 450 451 // 452 // Get the page table directory for the lowest meg of memory 453 // 454 Pte = HalAddressToPde(0); 455 HalpSavedPfn = Pte->PageFrameNumber; 456 HalpSavedPte = *Pte; 457 458 // 459 // Map it to the HAL reserved region and make it valid 460 // 461 Pte->Valid = 1; 462 Pte->Write = 1; 463 Pte->Owner = 1; 464 Pte->PageFrameNumber = (HalAddressToPde(0xFFC00000))->PageFrameNumber; 465 466 // 467 // Flush the TLB 468 // 469 HalpFlushTLB(); 470 471 // 472 // Now loop the first meg of memory 473 // 474 for (i = 0; i < 0x100000; i += PAGE_SIZE) 475 { 476 // 477 // Identity map it 478 // 479 Pte = HalAddressToPte(i); 480 Pte->PageFrameNumber = i >> PAGE_SHIFT; 481 Pte->Valid = 1; 482 Pte->Write = 1; 483 Pte->Owner = 1; 484 } 485 486 // 487 // Now get the entry for our real mode V86 code and the target 488 // 489 Pte = HalAddressToPte(0x20000); 490 V86Pte = HalAddressToPte(&HalpRealModeStart); 491 do 492 { 493 // 494 // Map the physical address into our real-mode region 495 // 496 Pte->PageFrameNumber = V86Pte->PageFrameNumber; 497 498 // 499 // Keep going until we've reached the end of our region 500 // 501 Pte++; 502 V86Pte++; 503 } while (V86Pte <= HalAddressToPte(&HalpRealModeEnd)); 504 505 // 506 // Flush the TLB 507 // 508 HalpFlushTLB(); 509 } 510 511 VOID 512 NTAPI 513 HalpSwitchToRealModeTrapHandlers(VOID) 514 { 515 // 516 // Save the current Invalid Opcode and General Protection Fault Handlers 517 // 518 HalpGpfHandler = KeQueryInterruptHandler(13); 519 HalpBopHandler = KeQueryInterruptHandler(6); 520 521 // 522 // Now set our own GPF handler to handle exceptions while in real mode 523 // 524 KeRegisterInterruptHandler(13, HalpTrap0D); 525 526 // 527 // And our own invalid opcode handler to detect the BOP to get us out 528 // 529 KeRegisterInterruptHandler(6, HalpTrap06); 530 } 531 532 VOID 533 NTAPI 534 HalpSetupRealModeIoPermissionsAndTask(VOID) 535 { 536 // 537 // Switch to valid TSS 538 // 539 HalpBorrowTss(); 540 541 // 542 // Save a copy of the I/O Map and delete it 543 // 544 HalpSavedIoMap = (PUSHORT)KeGetPcr()->TSS->IoMaps[0].IoMap; 545 HalpStoreAndClearIopm(); 546 547 // 548 // Save the IOPM and switch to the real-mode one 549 // 550 HalpSavedIopmBase = KeGetPcr()->TSS->IoMapBase; 551 KeGetPcr()->TSS->IoMapBase = KiComputeIopmOffset(1); 552 553 // 554 // Save our stack pointer 555 // 556 HalpSavedEsp0 = KeGetPcr()->TSS->Esp0; 557 } 558 559 VOID 560 NTAPI 561 HalpRestoreTrapHandlers(VOID) 562 { 563 // 564 // Keep dummy GPF handler in case we get an NMI during V8086 565 // 566 if (!HalpNMIInProgress) 567 { 568 // 569 // Not an NMI -- put back the original handler 570 // 571 KeRegisterInterruptHandler(13, HalpGpfHandler); 572 } 573 574 // 575 // Restore invalid opcode handler 576 // 577 KeRegisterInterruptHandler(6, HalpBopHandler); 578 } 579 580 VOID 581 NTAPI 582 HalpRestoreIoPermissionsAndTask(VOID) 583 { 584 // 585 // Restore the stack pointer 586 // 587 KeGetPcr()->TSS->Esp0 = HalpSavedEsp0; 588 589 // 590 // Restore the I/O Map 591 // 592 HalpRestoreIopm(); 593 594 // 595 // Restore the IOPM 596 // 597 KeGetPcr()->TSS->IoMapBase = HalpSavedIopmBase; 598 599 // 600 // Restore the TSS 601 // 602 if (HalpSavedTss) HalpReturnTss(); 603 } 604 605 VOID 606 NTAPI 607 HalpUnmapRealModeMemory(VOID) 608 { 609 ULONG i; 610 PHARDWARE_PTE Pte; 611 612 // 613 // Loop the first meg of memory 614 // 615 for (i = 0; i < 0x100000; i += PAGE_SIZE) 616 { 617 // 618 // Invalidate each PTE 619 // 620 Pte = HalAddressToPte(i); 621 Pte->Valid = 0; 622 Pte->Write = 0; 623 Pte->Owner = 0; 624 Pte->PageFrameNumber = 0; 625 } 626 627 // 628 // Restore the PDE for the lowest megabyte of memory 629 // 630 Pte = HalAddressToPde(0); 631 *Pte = HalpSavedPte; 632 Pte->PageFrameNumber = HalpSavedPfn; 633 634 // 635 // Flush the TLB 636 // 637 HalpFlushTLB(); 638 } 639 640 BOOLEAN 641 NTAPI 642 HalpBiosDisplayReset(VOID) 643 { 644 #if defined(SARCH_XBOX) || defined(SARCH_PC98) 645 /* There is no VGA BIOS on these machine types */ 646 return FALSE; 647 #else 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(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 #endif 713 } 714 715 /* EOF */ 716