Reactos
at master 715 lines 15 kB view raw
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 18void __cdecl HalpTrap0D(); 19 20/* GLOBALS ********************************************************************/ 21 22// 23// PTE Data 24// 25ULONG HalpSavedPfn; 26HARDWARE_PTE HalpSavedPte; 27 28// 29// IDT Data 30// 31PVOID HalpGpfHandler; 32PVOID HalpBopHandler; 33 34// 35// TSS Data 36// 37ULONG HalpSavedEsp0; 38USHORT HalpSavedTss; 39 40// 41// IOPM Data 42// 43USHORT HalpSavedIopmBase; 44PUSHORT HalpSavedIoMap; 45USHORT HalpSavedIoMapData[IOPM_SIZE / sizeof(USHORT)][2]; 46ULONG HalpSavedIoMapEntries; 47 48/* Where the protected mode stack is */ 49ULONG_PTR HalpSavedEsp; 50 51/* Where the real mode code ends */ 52extern PVOID HalpRealModeEnd; 53 54/* Context saved for return from v86 mode */ 55jmp_buf HalpSavedContext; 56 57 58/* V86 OPCODE HANDLERS ********************************************************/ 59 60BOOLEAN 61FASTCALL 62HalpOpcodeInvalid(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 78BOOLEAN 79FASTCALL 80HalpPushInt(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 118BOOLEAN 119FASTCALL 120HalpOpcodeINTnn(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 156BOOLEAN 157FASTCALL 158HalpDispatchV86Opcode(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 205DECLSPEC_NORETURN 206VOID 207FASTCALL 208HalpTrap0DHandler(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 229VOID 230DECLSPEC_NORETURN 231HalpTrap06(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 248VOID 249NTAPI 250HalpBiosCall(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 290VOID 291NTAPI 292HalpBorrowTss(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 349VOID 350NTAPI 351HalpReturnTss(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 382VOID 383NTAPI 384HalpStoreAndClearIopm(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 427VOID 428NTAPI 429HalpRestoreIopm(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 444VOID 445NTAPI 446HalpMapRealModeMemory(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 511VOID 512NTAPI 513HalpSwitchToRealModeTrapHandlers(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 532VOID 533NTAPI 534HalpSetupRealModeIoPermissionsAndTask(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 559VOID 560NTAPI 561HalpRestoreTrapHandlers(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 580VOID 581NTAPI 582HalpRestoreIoPermissionsAndTask(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 605VOID 606NTAPI 607HalpUnmapRealModeMemory(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 640BOOLEAN 641NTAPI 642HalpBiosDisplayReset(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 */