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[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 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 ASSERT(j < 32); 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 ULONG Flags; 650 PHARDWARE_PTE IdtPte; 651 BOOLEAN RestoreWriteProtection = FALSE; 652 653 // 654 // Disable interrupts 655 // 656 Flags = __readeflags(); 657 _disable(); 658 659 // 660 // Map memory available to the V8086 real-mode code 661 // 662 HalpMapRealModeMemory(); 663 664 // 665 // On P5, the first 7 entries of the IDT are write protected to work around 666 // the cmpxchg8b lock errata. Unprotect them here so we can set our custom 667 // invalid op-code handler. 668 // 669 IdtPte = HalAddressToPte(((PKIPCR)KeGetPcr())->IDT); 670 RestoreWriteProtection = IdtPte->Write != 0; 671 IdtPte->Write = 1; 672 673 // 674 // Use special invalid opcode and GPF trap handlers 675 // 676 HalpSwitchToRealModeTrapHandlers(); 677 678 // 679 // Configure the IOPM and TSS 680 // 681 HalpSetupRealModeIoPermissionsAndTask(); 682 683 // 684 // Now jump to real mode 685 // 686 HalpBiosCall(); 687 688 // 689 // Restore kernel trap handlers 690 // 691 HalpRestoreTrapHandlers(); 692 693 // 694 // Restore write permission 695 // 696 IdtPte->Write = RestoreWriteProtection; 697 698 // 699 // Restore TSS and IOPM 700 // 701 HalpRestoreIoPermissionsAndTask(); 702 703 // 704 // Restore low memory mapping 705 // 706 HalpUnmapRealModeMemory(); 707 708 // 709 // Restore interrupts if they were previously enabled 710 // 711 __writeeflags(Flags); 712 return TRUE; 713 } 714 #endif 715 716 /* EOF */ 717