1/** @file 2 3Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR> 4 5This program and the accompanying materials 6are licensed and made available under the terms and conditions 7of the BSD License which accompanies this distribution. The 8full text of the license may be found at 9http://opensource.org/licenses/bsd-license.php 10 11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 13 14**/ 15 16#include "LegacyBiosInterface.h" 17 18#define PHYSICAL_ADDRESS_TO_POINTER(Address) ((VOID *) ((UINTN) Address)) 19 20// 21// define maximum number of HDD system supports 22// 23#define MAX_HDD_ENTRIES 0x30 24 25// 26// Module Global: 27// Since this driver will only ever produce one instance of the Private Data 28// protocol you are not required to dynamically allocate the PrivateData. 29// 30LEGACY_BIOS_INSTANCE mPrivateData; 31 32// 33// The SMBIOS table in EfiRuntimeServicesData memory 34// 35VOID *mRuntimeSmbiosEntryPoint = NULL; 36 37// 38// The SMBIOS table in EfiReservedMemoryType memory 39// 40EFI_PHYSICAL_ADDRESS mReserveSmbiosEntryPoint = 0; 41EFI_PHYSICAL_ADDRESS mStructureTableAddress = 0; 42UINTN mStructureTablePages = 0; 43 44/** 45 Do an AllocatePages () of type AllocateMaxAddress for EfiBootServicesCode 46 memory. 47 48 @param AllocateType Allocated Legacy Memory Type 49 @param StartPageAddress Start address of range 50 @param Pages Number of pages to allocate 51 @param Result Result of allocation 52 53 @retval EFI_SUCCESS Legacy16 code loaded 54 @retval Other No protocol installed, unload driver. 55 56**/ 57EFI_STATUS 58AllocateLegacyMemory ( 59 IN EFI_ALLOCATE_TYPE AllocateType, 60 IN EFI_PHYSICAL_ADDRESS StartPageAddress, 61 IN UINTN Pages, 62 OUT EFI_PHYSICAL_ADDRESS *Result 63 ) 64{ 65 EFI_STATUS Status; 66 EFI_PHYSICAL_ADDRESS MemPage; 67 68 // 69 // Allocate Pages of memory less <= StartPageAddress 70 // 71 MemPage = (EFI_PHYSICAL_ADDRESS) (UINTN) StartPageAddress; 72 Status = gBS->AllocatePages ( 73 AllocateType, 74 EfiBootServicesCode, 75 Pages, 76 &MemPage 77 ); 78 // 79 // Do not ASSERT on Status error but let caller decide since some cases 80 // memory is already taken but that is ok. 81 // 82 if (!EFI_ERROR (Status)) { 83 *Result = (EFI_PHYSICAL_ADDRESS) (UINTN) MemPage; 84 } 85 // 86 // If reach here the status = EFI_SUCCESS 87 // 88 return Status; 89} 90 91 92/** 93 This function is called when EFI needs to reserve an area in the 0xE0000 or 0xF0000 94 64 KB blocks. 95 96 Note: inconsistency with the Framework CSM spec. Per the spec, this function may be 97 invoked only once. This limitation is relaxed to allow multiple calls in this implemenation. 98 99 @param This Protocol instance pointer. 100 @param LegacyMemorySize Size of required region 101 @param Region Region to use. 00 = Either 0xE0000 or 0xF0000 102 block Bit0 = 1 0xF0000 block Bit1 = 1 0xE0000 103 block 104 @param Alignment Address alignment. Bit mapped. First non-zero 105 bit from right is alignment. 106 @param LegacyMemoryAddress Region Assigned 107 108 @retval EFI_SUCCESS Region assigned 109 @retval EFI_ACCESS_DENIED Procedure previously invoked 110 @retval Other Region not assigned 111 112**/ 113EFI_STATUS 114EFIAPI 115LegacyBiosGetLegacyRegion ( 116 IN EFI_LEGACY_BIOS_PROTOCOL *This, 117 IN UINTN LegacyMemorySize, 118 IN UINTN Region, 119 IN UINTN Alignment, 120 OUT VOID **LegacyMemoryAddress 121 ) 122{ 123 124 LEGACY_BIOS_INSTANCE *Private; 125 EFI_IA32_REGISTER_SET Regs; 126 EFI_STATUS Status; 127 UINT32 Granularity; 128 129 Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); 130 Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity); 131 132 ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); 133 Regs.X.AX = Legacy16GetTableAddress; 134 Regs.X.BX = (UINT16) Region; 135 Regs.X.CX = (UINT16) LegacyMemorySize; 136 Regs.X.DX = (UINT16) Alignment; 137 Private->LegacyBios.FarCall86 ( 138 &Private->LegacyBios, 139 Private->Legacy16CallSegment, 140 Private->Legacy16CallOffset, 141 &Regs, 142 NULL, 143 0 144 ); 145 146 if (Regs.X.AX == 0) { 147 *LegacyMemoryAddress = (VOID *) (UINTN) ((Regs.X.DS << 4) + Regs.X.BX); 148 Status = EFI_SUCCESS; 149 } else { 150 Status = EFI_OUT_OF_RESOURCES; 151 } 152 153 Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate); 154 Private->LegacyRegion->Lock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity); 155 156 return Status; 157} 158 159 160/** 161 This function is called when copying data to the region assigned by 162 EFI_LEGACY_BIOS_PROTOCOL.GetLegacyRegion(). 163 164 @param This Protocol instance pointer. 165 @param LegacyMemorySize Size of data to copy 166 @param LegacyMemoryAddress Legacy Region destination address Note: must 167 be in region assigned by 168 LegacyBiosGetLegacyRegion 169 @param LegacyMemorySourceAddress Source of data 170 171 @retval EFI_SUCCESS The data was copied successfully. 172 @retval EFI_ACCESS_DENIED Either the starting or ending address is out of bounds. 173**/ 174EFI_STATUS 175EFIAPI 176LegacyBiosCopyLegacyRegion ( 177 IN EFI_LEGACY_BIOS_PROTOCOL *This, 178 IN UINTN LegacyMemorySize, 179 IN VOID *LegacyMemoryAddress, 180 IN VOID *LegacyMemorySourceAddress 181 ) 182{ 183 184 LEGACY_BIOS_INSTANCE *Private; 185 UINT32 Granularity; 186 187 if ((LegacyMemoryAddress < (VOID *)(UINTN)0xE0000 ) || 188 ((UINTN) LegacyMemoryAddress + LegacyMemorySize > (UINTN) 0x100000) 189 ) { 190 return EFI_ACCESS_DENIED; 191 } 192 // 193 // There is no protection from writes over lapping if this function is 194 // called multiple times. 195 // 196 Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); 197 Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity); 198 CopyMem (LegacyMemoryAddress, LegacyMemorySourceAddress, LegacyMemorySize); 199 200 Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate); 201 Private->LegacyRegion->Lock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity); 202 203 return EFI_SUCCESS; 204} 205 206 207/** 208 Find Legacy16 BIOS image in the FLASH device and shadow it into memory. Find 209 the $EFI table in the shadow area. Thunk into the Legacy16 code after it had 210 been shadowed. 211 212 @param Private Legacy BIOS context data 213 214 @retval EFI_SUCCESS Legacy16 code loaded 215 @retval Other No protocol installed, unload driver. 216 217**/ 218EFI_STATUS 219ShadowAndStartLegacy16 ( 220 IN LEGACY_BIOS_INSTANCE *Private 221 ) 222{ 223 EFI_STATUS Status; 224 UINT8 *Ptr; 225 UINT8 *PtrEnd; 226 BOOLEAN Done; 227 EFI_COMPATIBILITY16_TABLE *Table; 228 UINT8 CheckSum; 229 EFI_IA32_REGISTER_SET Regs; 230 EFI_TO_COMPATIBILITY16_INIT_TABLE *EfiToLegacy16InitTable; 231 EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable; 232 VOID *LegacyBiosImage; 233 UINTN LegacyBiosImageSize; 234 UINTN E820Size; 235 UINT32 *ClearPtr; 236 BBS_TABLE *BbsTable; 237 LEGACY_EFI_HDD_TABLE *LegacyEfiHddTable; 238 UINTN Index; 239 UINT32 TpmPointer; 240 VOID *TpmBinaryImage; 241 UINTN TpmBinaryImageSize; 242 UINTN Location; 243 UINTN Alignment; 244 UINTN TempData; 245 EFI_PHYSICAL_ADDRESS Address; 246 UINT16 OldMask; 247 UINT16 NewMask; 248 UINT32 Granularity; 249 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor; 250 251 Location = 0; 252 Alignment = 0; 253 254 // 255 // we allocate the C/D/E/F segment as RT code so no one will use it any more. 256 // 257 Address = 0xC0000; 258 gDS->GetMemorySpaceDescriptor (Address, &Descriptor); 259 if (Descriptor.GcdMemoryType == EfiGcdMemoryTypeSystemMemory) { 260 // 261 // If it is already reserved, we should be safe, or else we allocate it. 262 // 263 Status = gBS->AllocatePages ( 264 AllocateAddress, 265 EfiRuntimeServicesCode, 266 0x40000/EFI_PAGE_SIZE, 267 &Address 268 ); 269 if (EFI_ERROR (Status)) { 270 // 271 // Bugbug: need to figure out whether C/D/E/F segment should be marked as reserved memory. 272 // 273 DEBUG ((DEBUG_ERROR, "Failed to allocate the C/D/E/F segment Status = %r", Status)); 274 } 275 } 276 277 // 278 // start testtest 279 // GetTimerValue (&Ticker); 280 // 281 // gRT->SetVariable (L"StartLegacy", 282 // &gEfiGlobalVariableGuid, 283 // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 284 // sizeof (UINT64), 285 // (VOID *)&Ticker 286 // ); 287 // end testtest 288 // 289 EfiToLegacy16BootTable = &Private->IntThunk->EfiToLegacy16BootTable; 290 Status = Private->LegacyBiosPlatform->GetPlatformInfo ( 291 Private->LegacyBiosPlatform, 292 EfiGetPlatformBinarySystemRom, 293 &LegacyBiosImage, 294 &LegacyBiosImageSize, 295 &Location, 296 &Alignment, 297 0, 298 0 299 ); 300 if (EFI_ERROR (Status)) { 301 return Status; 302 } 303 304 Private->BiosStart = (UINT32) (0x100000 - LegacyBiosImageSize); 305 Private->OptionRom = 0xc0000; 306 Private->LegacyBiosImageSize = (UINT32) LegacyBiosImageSize; 307 308 // 309 // Can only shadow into memory allocated for legacy useage. 310 // 311 ASSERT (Private->BiosStart > Private->OptionRom); 312 313 // 314 // Shadow Legacy BIOS. Turn on memory and copy image 315 // 316 Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xc0000, 0x40000, &Granularity); 317 318 ClearPtr = (VOID *) ((UINTN) 0xc0000); 319 320 // 321 // Initialize region from 0xc0000 to start of BIOS to all ffs. This allows unused 322 // regions to be used by EMM386 etc. 323 // 324 SetMem ((VOID *) ClearPtr, (UINTN) (0x40000 - LegacyBiosImageSize), 0xff); 325 326 TempData = Private->BiosStart; 327 328 CopyMem ( 329 (VOID *) TempData, 330 LegacyBiosImage, 331 (UINTN) LegacyBiosImageSize 332 ); 333 334 Private->Cpu->FlushDataCache (Private->Cpu, 0xc0000, 0x40000, EfiCpuFlushTypeWriteBackInvalidate); 335 336 // 337 // Search for Legacy16 table in Shadowed ROM 338 // 339 Done = FALSE; 340 Table = NULL; 341 for (Ptr = (UINT8 *) TempData; Ptr < (UINT8 *) ((UINTN) 0x100000) && !Done; Ptr += 0x10) { 342 if (*(UINT32 *) Ptr == SIGNATURE_32 ('I', 'F', 'E', '$')) { 343 Table = (EFI_COMPATIBILITY16_TABLE *) Ptr; 344 PtrEnd = Ptr + Table->TableLength; 345 for (CheckSum = 0; Ptr < PtrEnd; Ptr++) { 346 CheckSum = (UINT8) (CheckSum +*Ptr); 347 } 348 349 Done = TRUE; 350 } 351 } 352 353 if (Table == NULL) { 354 DEBUG ((EFI_D_ERROR, "No Legacy16 table found\n")); 355 return EFI_NOT_FOUND; 356 } 357 358 if (!Done) { 359 // 360 // Legacy16 table header checksum error. 361 // 362 DEBUG ((EFI_D_ERROR, "Legacy16 table found with bad talbe header checksum\n")); 363 } 364 365 // 366 // Remember location of the Legacy16 table 367 // 368 Private->Legacy16Table = Table; 369 Private->Legacy16CallSegment = Table->Compatibility16CallSegment; 370 Private->Legacy16CallOffset = Table->Compatibility16CallOffset; 371 EfiToLegacy16InitTable = &Private->IntThunk->EfiToLegacy16InitTable; 372 Private->Legacy16InitPtr = EfiToLegacy16InitTable; 373 Private->Legacy16BootPtr = &Private->IntThunk->EfiToLegacy16BootTable; 374 Private->InternalIrqRoutingTable = NULL; 375 Private->NumberIrqRoutingEntries = 0; 376 Private->BbsTablePtr = NULL; 377 Private->LegacyEfiHddTable = NULL; 378 Private->DiskEnd = 0; 379 Private->Disk4075 = 0; 380 Private->HddTablePtr = &Private->IntThunk->EfiToLegacy16BootTable.HddInfo; 381 Private->NumberHddControllers = MAX_IDE_CONTROLLER; 382 Private->Dump[0] = 'D'; 383 Private->Dump[1] = 'U'; 384 Private->Dump[2] = 'M'; 385 Private->Dump[3] = 'P'; 386 387 ZeroMem ( 388 Private->Legacy16BootPtr, 389 sizeof (EFI_TO_COMPATIBILITY16_BOOT_TABLE) 390 ); 391 392 // 393 // Store away a copy of the EFI System Table 394 // 395 Table->EfiSystemTable = (UINT32) (UINTN) gST; 396 397 // 398 // IPF CSM integration -Bug 399 // 400 // Construct the Legacy16 boot memory map. This sets up number of 401 // E820 entries. 402 // 403 LegacyBiosBuildE820 (Private, &E820Size); 404 // 405 // Initialize BDA and EBDA standard values needed to load Legacy16 code 406 // 407 LegacyBiosInitBda (Private); 408 LegacyBiosInitCmos (Private); 409 410 // 411 // All legacy interrupt should be masked when do initialization work from legacy 16 code. 412 // 413 Private->Legacy8259->GetMask(Private->Legacy8259, &OldMask, NULL, NULL, NULL); 414 NewMask = 0xFFFF; 415 Private->Legacy8259->SetMask(Private->Legacy8259, &NewMask, NULL, NULL, NULL); 416 417 // 418 // Call into Legacy16 code to do an INIT 419 // 420 ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); 421 Regs.X.AX = Legacy16InitializeYourself; 422 Regs.X.ES = EFI_SEGMENT (*((UINT32 *) &EfiToLegacy16InitTable)); 423 Regs.X.BX = EFI_OFFSET (*((UINT32 *) &EfiToLegacy16InitTable)); 424 425 Private->LegacyBios.FarCall86 ( 426 &Private->LegacyBios, 427 Table->Compatibility16CallSegment, 428 Table->Compatibility16CallOffset, 429 &Regs, 430 NULL, 431 0 432 ); 433 434 // 435 // Restore original legacy interrupt mask value 436 // 437 Private->Legacy8259->SetMask(Private->Legacy8259, &OldMask, NULL, NULL, NULL); 438 439 if (Regs.X.AX != 0) { 440 return EFI_DEVICE_ERROR; 441 } 442 443 // 444 // start testtest 445 // GetTimerValue (&Ticker); 446 // 447 // gRT->SetVariable (L"BackFromInitYourself", 448 // &gEfiGlobalVariableGuid, 449 // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 450 // sizeof (UINT64), 451 // (VOID *)&Ticker 452 // ); 453 // end testtest 454 // 455 // Copy E820 table after InitializeYourself is completed 456 // 457 ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); 458 Regs.X.AX = Legacy16GetTableAddress; 459 Regs.X.CX = (UINT16) E820Size; 460 Regs.X.DX = 1; 461 Private->LegacyBios.FarCall86 ( 462 &Private->LegacyBios, 463 Table->Compatibility16CallSegment, 464 Table->Compatibility16CallOffset, 465 &Regs, 466 NULL, 467 0 468 ); 469 470 Table->E820Pointer = (UINT32) (Regs.X.DS * 16 + Regs.X.BX); 471 Table->E820Length = (UINT32) E820Size; 472 if (Regs.X.AX != 0) { 473 DEBUG ((EFI_D_ERROR, "Legacy16 E820 length insufficient\n")); 474 } else { 475 TempData = Table->E820Pointer; 476 CopyMem ((VOID *) TempData, Private->E820Table, E820Size); 477 } 478 // 479 // Get PnPInstallationCheck Info. 480 // 481 Private->PnPInstallationCheckSegment = Table->PnPInstallationCheckSegment; 482 Private->PnPInstallationCheckOffset = Table->PnPInstallationCheckOffset; 483 484 // 485 // Check if PCI Express is supported. If yes, Save base address. 486 // 487 Status = Private->LegacyBiosPlatform->GetPlatformInfo ( 488 Private->LegacyBiosPlatform, 489 EfiGetPlatformPciExpressBase, 490 NULL, 491 NULL, 492 &Location, 493 &Alignment, 494 0, 495 0 496 ); 497 if (!EFI_ERROR (Status)) { 498 Private->Legacy16Table->PciExpressBase = (UINT32)Location; 499 Location = 0; 500 } 501 // 502 // Check if TPM is supported. If yes get a region in E0000,F0000 to copy it 503 // into, copy it and update pointer to binary image. This needs to be 504 // done prior to any OPROM for security purposes. 505 // 506 Status = Private->LegacyBiosPlatform->GetPlatformInfo ( 507 Private->LegacyBiosPlatform, 508 EfiGetPlatformBinaryTpmBinary, 509 &TpmBinaryImage, 510 &TpmBinaryImageSize, 511 &Location, 512 &Alignment, 513 0, 514 0 515 ); 516 if (!EFI_ERROR (Status)) { 517 518 ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); 519 Regs.X.AX = Legacy16GetTableAddress; 520 Regs.X.CX = (UINT16) TpmBinaryImageSize; 521 Regs.X.DX = 1; 522 Private->LegacyBios.FarCall86 ( 523 &Private->LegacyBios, 524 Table->Compatibility16CallSegment, 525 Table->Compatibility16CallOffset, 526 &Regs, 527 NULL, 528 0 529 ); 530 531 TpmPointer = (UINT32) (Regs.X.DS * 16 + Regs.X.BX); 532 if (Regs.X.AX != 0) { 533 DEBUG ((EFI_D_ERROR, "TPM cannot be loaded\n")); 534 } else { 535 CopyMem ((VOID *) (UINTN)TpmPointer, TpmBinaryImage, TpmBinaryImageSize); 536 Table->TpmSegment = Regs.X.DS; 537 Table->TpmOffset = Regs.X.BX; 538 539 } 540 } 541 // 542 // Lock the Legacy BIOS region 543 // 544 Private->Cpu->FlushDataCache (Private->Cpu, Private->BiosStart, (UINT32) LegacyBiosImageSize, EfiCpuFlushTypeWriteBackInvalidate); 545 Private->LegacyRegion->Lock (Private->LegacyRegion, Private->BiosStart, (UINT32) LegacyBiosImageSize, &Granularity); 546 547 // 548 // Get the BbsTable from LOW_MEMORY_THUNK 549 // 550 BbsTable = (BBS_TABLE *)(UINTN)Private->IntThunk->BbsTable; 551 ZeroMem ((VOID *)BbsTable, sizeof (Private->IntThunk->BbsTable)); 552 553 EfiToLegacy16BootTable->BbsTable = (UINT32)(UINTN)BbsTable; 554 Private->BbsTablePtr = (VOID *) BbsTable; 555 // 556 // Skip Floppy and possible onboard IDE drives 557 // 558 EfiToLegacy16BootTable->NumberBbsEntries = 1 + 2 * MAX_IDE_CONTROLLER; 559 560 for (Index = 0; Index < (sizeof (Private->IntThunk->BbsTable) / sizeof (BBS_TABLE)); Index++) { 561 BbsTable[Index].BootPriority = BBS_IGNORE_ENTRY; 562 } 563 // 564 // Allocate space for Legacy HDD table 565 // 566 LegacyEfiHddTable = (LEGACY_EFI_HDD_TABLE *) AllocateZeroPool ((UINTN) MAX_HDD_ENTRIES * sizeof (LEGACY_EFI_HDD_TABLE)); 567 ASSERT (LegacyEfiHddTable); 568 569 Private->LegacyEfiHddTable = LegacyEfiHddTable; 570 Private->LegacyEfiHddTableIndex = 0x00; 571 572 // 573 // start testtest 574 // GetTimerValue (&Ticker); 575 // 576 // gRT->SetVariable (L"EndOfLoadFv", 577 // &gEfiGlobalVariableGuid, 578 // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 579 // sizeof (UINT64), 580 // (VOID *)&Ticker 581 // ); 582 // end testtest 583 // 584 return EFI_SUCCESS; 585} 586 587/** 588 Shadow all legacy16 OPROMs that haven't been shadowed. 589 Warning: Use this with caution. This routine disconnects all EFI 590 drivers. If used externally then caller must re-connect EFI 591 drivers. 592 593 @param This Protocol instance pointer. 594 595 @retval EFI_SUCCESS OPROMs shadowed 596 597**/ 598EFI_STATUS 599EFIAPI 600LegacyBiosShadowAllLegacyOproms ( 601 IN EFI_LEGACY_BIOS_PROTOCOL *This 602 ) 603{ 604 LEGACY_BIOS_INSTANCE *Private; 605 606 // 607 // EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform; 608 // EFI_LEGACY16_TABLE *Legacy16Table; 609 // 610 Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); 611 612 // 613 // LegacyBiosPlatform = Private->LegacyBiosPlatform; 614 // Legacy16Table = Private->Legacy16Table; 615 // 616 // Shadow PCI ROMs. We must do this near the end since this will kick 617 // of Native EFI drivers that may be needed to collect info for Legacy16 618 // 619 // WARNING: PciIo is gone after this call. 620 // 621 PciProgramAllInterruptLineRegisters (Private); 622 623 PciShadowRoms (Private); 624 625 // 626 // Shadow PXE base code, BIS etc. 627 // 628 // LegacyBiosPlatform->ShadowServiceRoms (LegacyBiosPlatform, 629 // &Private->OptionRom, 630 // Legacy16Table); 631 // 632 return EFI_SUCCESS; 633} 634 635/** 636 Get the PCI BIOS interface version. 637 638 @param Private Driver private data. 639 640 @return The PCI interface version number in Binary Coded Decimal (BCD) format. 641 E.g.: 0x0210 indicates 2.10, 0x0300 indicates 3.00 642 643**/ 644UINT16 645GetPciInterfaceVersion ( 646 IN LEGACY_BIOS_INSTANCE *Private 647 ) 648{ 649 EFI_IA32_REGISTER_SET Reg; 650 BOOLEAN ThunkFailed; 651 UINT16 PciInterfaceVersion; 652 653 PciInterfaceVersion = 0; 654 655 Reg.X.AX = 0xB101; 656 Reg.E.EDI = 0; 657 658 ThunkFailed = Private->LegacyBios.Int86 (&Private->LegacyBios, 0x1A, &Reg); 659 if (!ThunkFailed) { 660 // 661 // From PCI Firmware 3.0 Specification: 662 // If the CARRY FLAG [CF] is cleared and AH is set to 00h, it is still necessary to examine the 663 // contents of [EDX] for the presence of the string "PCI" + (trailing space) to fully validate the 664 // presence of the PCI function set. [BX] will further indicate the version level, with enough 665 // granularity to allow for incremental changes in the code that don't affect the function interface. 666 // Version numbers are stored as Binary Coded Decimal (BCD) values. For example, Version 2.10 667 // would be returned as a 02h in the [BH] registers and 10h in the [BL] registers. 668 // 669 if ((Reg.X.Flags.CF == 0) && (Reg.H.AH == 0) && (Reg.E.EDX == SIGNATURE_32 ('P', 'C', 'I', ' '))) { 670 PciInterfaceVersion = Reg.X.BX; 671 } 672 } 673 return PciInterfaceVersion; 674} 675 676/** 677 Callback function to calculate SMBIOS table size, and allocate memory for SMBIOS table. 678 SMBIOS table will be copied into EfiReservedMemoryType memory in legacy boot path. 679 680 @param Event Event whose notification function is being invoked. 681 @param Context The pointer to the notification function's context, 682 which is implementation-dependent. 683 684**/ 685VOID 686EFIAPI 687InstallSmbiosEventCallback ( 688 IN EFI_EVENT Event, 689 IN VOID *Context 690 ) 691{ 692 EFI_STATUS Status; 693 SMBIOS_TABLE_ENTRY_POINT *EntryPointStructure; 694 695 // 696 // Get SMBIOS table from EFI configuration table 697 // 698 Status = EfiGetSystemConfigurationTable ( 699 &gEfiSmbiosTableGuid, 700 &mRuntimeSmbiosEntryPoint 701 ); 702 if ((EFI_ERROR (Status)) || (mRuntimeSmbiosEntryPoint == NULL)) { 703 return; 704 } 705 706 EntryPointStructure = (SMBIOS_TABLE_ENTRY_POINT *) mRuntimeSmbiosEntryPoint; 707 708 // 709 // Allocate memory for SMBIOS Entry Point Structure. 710 // CSM framework spec requires SMBIOS table below 4GB in EFI_TO_COMPATIBILITY16_BOOT_TABLE. 711 // 712 if (mReserveSmbiosEntryPoint == 0) { 713 // 714 // Entrypoint structure with fixed size is allocated only once. 715 // 716 mReserveSmbiosEntryPoint = SIZE_4GB - 1; 717 Status = gBS->AllocatePages ( 718 AllocateMaxAddress, 719 EfiReservedMemoryType, 720 EFI_SIZE_TO_PAGES ((UINTN) (EntryPointStructure->EntryPointLength)), 721 &mReserveSmbiosEntryPoint 722 ); 723 if (EFI_ERROR (Status)) { 724 mReserveSmbiosEntryPoint = 0; 725 return; 726 } 727 DEBUG ((EFI_D_INFO, "Allocate memory for Smbios Entry Point Structure\n")); 728 } 729 730 if ((mStructureTableAddress != 0) && 731 (mStructureTablePages < (UINTN) EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength))) { 732 // 733 // If original buffer is not enough for the new SMBIOS table, free original buffer and re-allocate 734 // 735 gBS->FreePages (mStructureTableAddress, mStructureTablePages); 736 mStructureTableAddress = 0; 737 mStructureTablePages = 0; 738 DEBUG ((EFI_D_INFO, "Original size is not enough. Re-allocate the memory.\n")); 739 } 740 741 if (mStructureTableAddress == 0) { 742 // 743 // Allocate reserved memory below 4GB. 744 // Smbios spec requires the structure table is below 4GB. 745 // 746 mStructureTableAddress = SIZE_4GB - 1; 747 mStructureTablePages = EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength); 748 Status = gBS->AllocatePages ( 749 AllocateMaxAddress, 750 EfiReservedMemoryType, 751 mStructureTablePages, 752 &mStructureTableAddress 753 ); 754 if (EFI_ERROR (Status)) { 755 gBS->FreePages ( 756 mReserveSmbiosEntryPoint, 757 EFI_SIZE_TO_PAGES ((UINTN) (EntryPointStructure->EntryPointLength)) 758 ); 759 mReserveSmbiosEntryPoint = 0; 760 mStructureTableAddress = 0; 761 mStructureTablePages = 0; 762 return; 763 } 764 DEBUG ((EFI_D_INFO, "Allocate memory for Smbios Structure Table\n")); 765 } 766} 767 768/** 769 Install Driver to produce Legacy BIOS protocol. 770 771 @param ImageHandle Handle of driver image. 772 @param SystemTable Pointer to system table. 773 774 @retval EFI_SUCCESS Legacy BIOS protocol installed 775 @retval No protocol installed, unload driver. 776 777**/ 778EFI_STATUS 779EFIAPI 780LegacyBiosInstall ( 781 IN EFI_HANDLE ImageHandle, 782 IN EFI_SYSTEM_TABLE *SystemTable 783 ) 784{ 785 EFI_STATUS Status; 786 LEGACY_BIOS_INSTANCE *Private; 787 EFI_TO_COMPATIBILITY16_INIT_TABLE *EfiToLegacy16InitTable; 788 EFI_PHYSICAL_ADDRESS MemoryAddress; 789 EFI_PHYSICAL_ADDRESS EbdaReservedBaseAddress; 790 VOID *MemoryPtr; 791 EFI_PHYSICAL_ADDRESS MemoryAddressUnder1MB; 792 UINTN Index; 793 UINT32 *BaseVectorMaster; 794 EFI_PHYSICAL_ADDRESS StartAddress; 795 UINT32 *ClearPtr; 796 EFI_PHYSICAL_ADDRESS MemStart; 797 UINT32 IntRedirCode; 798 UINT32 Granularity; 799 BOOLEAN DecodeOn; 800 UINT32 MemorySize; 801 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor; 802 UINT64 Length; 803 UINT8 *SecureBoot; 804 EFI_EVENT InstallSmbiosEvent; 805 806 // 807 // Load this driver's image to memory 808 // 809 Status = RelocateImageUnder4GIfNeeded (ImageHandle, SystemTable); 810 if (EFI_ERROR (Status)) { 811 return Status; 812 } 813 814 // 815 // When UEFI Secure Boot is enabled, CSM module will not start any more. 816 // 817 SecureBoot = NULL; 818 GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME, (VOID**)&SecureBoot, NULL); 819 if ((SecureBoot != NULL) && (*SecureBoot == SECURE_BOOT_MODE_ENABLE)) { 820 FreePool (SecureBoot); 821 return EFI_SECURITY_VIOLATION; 822 } 823 824 if (SecureBoot != NULL) { 825 FreePool (SecureBoot); 826 } 827 828 Private = &mPrivateData; 829 ZeroMem (Private, sizeof (LEGACY_BIOS_INSTANCE)); 830 831 // 832 // Grab a copy of all the protocols we depend on. Any error would 833 // be a dispatcher bug!. 834 // 835 Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Private->Cpu); 836 ASSERT_EFI_ERROR (Status); 837 838 Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **) &Private->Timer); 839 ASSERT_EFI_ERROR (Status); 840 841 Status = gBS->LocateProtocol (&gEfiLegacyRegion2ProtocolGuid, NULL, (VOID **) &Private->LegacyRegion); 842 ASSERT_EFI_ERROR (Status); 843 844 Status = gBS->LocateProtocol (&gEfiLegacyBiosPlatformProtocolGuid, NULL, (VOID **) &Private->LegacyBiosPlatform); 845 ASSERT_EFI_ERROR (Status); 846 847 Status = gBS->LocateProtocol (&gEfiLegacy8259ProtocolGuid, NULL, (VOID **) &Private->Legacy8259); 848 ASSERT_EFI_ERROR (Status); 849 850 Status = gBS->LocateProtocol (&gEfiLegacyInterruptProtocolGuid, NULL, (VOID **) &Private->LegacyInterrupt); 851 ASSERT_EFI_ERROR (Status); 852 853 // 854 // Locate Memory Test Protocol if exists 855 // 856 Status = gBS->LocateProtocol ( 857 &gEfiGenericMemTestProtocolGuid, 858 NULL, 859 (VOID **) &Private->GenericMemoryTest 860 ); 861 ASSERT_EFI_ERROR (Status); 862 863 // 864 // Make sure all memory from 0-640K is tested 865 // 866 for (StartAddress = 0; StartAddress < 0xa0000; ) { 867 gDS->GetMemorySpaceDescriptor (StartAddress, &Descriptor); 868 if (Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) { 869 StartAddress = Descriptor.BaseAddress + Descriptor.Length; 870 continue; 871 } 872 Length = MIN (Descriptor.Length, 0xa0000 - StartAddress); 873 Private->GenericMemoryTest->CompatibleRangeTest ( 874 Private->GenericMemoryTest, 875 StartAddress, 876 Length 877 ); 878 StartAddress = StartAddress + Length; 879 } 880 // 881 // Make sure all memory from 1MB to 16MB is tested and added to memory map 882 // 883 for (StartAddress = BASE_1MB; StartAddress < BASE_16MB; ) { 884 gDS->GetMemorySpaceDescriptor (StartAddress, &Descriptor); 885 if (Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) { 886 StartAddress = Descriptor.BaseAddress + Descriptor.Length; 887 continue; 888 } 889 Length = MIN (Descriptor.Length, BASE_16MB - StartAddress); 890 Private->GenericMemoryTest->CompatibleRangeTest ( 891 Private->GenericMemoryTest, 892 StartAddress, 893 Length 894 ); 895 StartAddress = StartAddress + Length; 896 } 897 898 Private->Signature = LEGACY_BIOS_INSTANCE_SIGNATURE; 899 900 Private->LegacyBios.Int86 = LegacyBiosInt86; 901 Private->LegacyBios.FarCall86 = LegacyBiosFarCall86; 902 Private->LegacyBios.CheckPciRom = LegacyBiosCheckPciRom; 903 Private->LegacyBios.InstallPciRom = LegacyBiosInstallPciRom; 904 Private->LegacyBios.LegacyBoot = LegacyBiosLegacyBoot; 905 Private->LegacyBios.UpdateKeyboardLedStatus = LegacyBiosUpdateKeyboardLedStatus; 906 Private->LegacyBios.GetBbsInfo = LegacyBiosGetBbsInfo; 907 Private->LegacyBios.ShadowAllLegacyOproms = LegacyBiosShadowAllLegacyOproms; 908 Private->LegacyBios.PrepareToBootEfi = LegacyBiosPrepareToBootEfi; 909 Private->LegacyBios.GetLegacyRegion = LegacyBiosGetLegacyRegion; 910 Private->LegacyBios.CopyLegacyRegion = LegacyBiosCopyLegacyRegion; 911 Private->LegacyBios.BootUnconventionalDevice = LegacyBiosBootUnconventionalDevice; 912 913 Private->ImageHandle = ImageHandle; 914 915 // 916 // Enable read attribute of legacy region. 917 // 918 DecodeOn = TRUE; 919 Private->LegacyRegion->Decode ( 920 Private->LegacyRegion, 921 0xc0000, 922 0x40000, 923 &Granularity, 924 &DecodeOn 925 ); 926 // 927 // Set Cachebility for legacy region 928 // BUGBUG: Comments about this legacy region cacheability setting 929 // This setting will make D865GCHProduction CSM Unhappy 930 // 931 if (PcdGetBool (PcdLegacyBiosCacheLegacyRegion)) { 932 gDS->SetMemorySpaceAttributes ( 933 0x0, 934 0xA0000, 935 EFI_MEMORY_WB 936 ); 937 gDS->SetMemorySpaceAttributes ( 938 0xc0000, 939 0x40000, 940 EFI_MEMORY_WB 941 ); 942 } 943 944 gDS->SetMemorySpaceAttributes ( 945 0xA0000, 946 0x20000, 947 EFI_MEMORY_UC 948 ); 949 950 // 951 // Allocate 0 - 4K for real mode interupt vectors and BDA. 952 // 953 AllocateLegacyMemory ( 954 AllocateAddress, 955 0, 956 1, 957 &MemoryAddress 958 ); 959 ASSERT (MemoryAddress == 0x000000000); 960 961 ClearPtr = (VOID *) ((UINTN) 0x0000); 962 963 // 964 // Initialize region from 0x0000 to 4k. This initializes interrupt vector 965 // range. 966 // 967 gBS->SetMem ((VOID *) ClearPtr, 0x400, INITIAL_VALUE_BELOW_1K); 968 ZeroMem ((VOID *) ((UINTN)ClearPtr + 0x400), 0xC00); 969 970 // 971 // Allocate pages for OPROM usage 972 // 973 MemorySize = PcdGet32 (PcdEbdaReservedMemorySize); 974 ASSERT ((MemorySize & 0xFFF) == 0); 975 976 Status = AllocateLegacyMemory ( 977 AllocateAddress, 978 CONVENTIONAL_MEMORY_TOP - MemorySize, 979 EFI_SIZE_TO_PAGES (MemorySize), 980 &MemoryAddress 981 ); 982 ASSERT_EFI_ERROR (Status); 983 984 ZeroMem ((VOID *) ((UINTN) MemoryAddress), MemorySize); 985 986 // 987 // Allocate all 32k chunks from 0x60000 ~ 0x88000 for Legacy OPROMs that 988 // don't use PMM but look for zeroed memory. Note that various non-BBS 989 // OpROMs expect different areas to be free 990 // 991 EbdaReservedBaseAddress = MemoryAddress; 992 MemoryAddress = PcdGet32 (PcdOpromReservedMemoryBase); 993 MemorySize = PcdGet32 (PcdOpromReservedMemorySize); 994 // 995 // Check if base address and size for reserved memory are 4KB aligned. 996 // 997 ASSERT ((MemoryAddress & 0xFFF) == 0); 998 ASSERT ((MemorySize & 0xFFF) == 0); 999 // 1000 // Check if the reserved memory is below EBDA reserved range. 1001 // 1002 ASSERT ((MemoryAddress < EbdaReservedBaseAddress) && ((MemoryAddress + MemorySize - 1) < EbdaReservedBaseAddress)); 1003 for (MemStart = MemoryAddress; MemStart < MemoryAddress + MemorySize; MemStart += 0x1000) { 1004 Status = AllocateLegacyMemory ( 1005 AllocateAddress, 1006 MemStart, 1007 1, 1008 &StartAddress 1009 ); 1010 if (!EFI_ERROR (Status)) { 1011 MemoryPtr = (VOID *) ((UINTN) StartAddress); 1012 ZeroMem (MemoryPtr, 0x1000); 1013 } else { 1014 DEBUG ((EFI_D_ERROR, "WARNING: Allocate legacy memory fail for SCSI card - %x\n", MemStart)); 1015 } 1016 } 1017 1018 // 1019 // Allocate low PMM memory and zero it out 1020 // 1021 MemorySize = PcdGet32 (PcdLowPmmMemorySize); 1022 ASSERT ((MemorySize & 0xFFF) == 0); 1023 Status = AllocateLegacyMemory ( 1024 AllocateMaxAddress, 1025 CONVENTIONAL_MEMORY_TOP, 1026 EFI_SIZE_TO_PAGES (MemorySize), 1027 &MemoryAddressUnder1MB 1028 ); 1029 ASSERT_EFI_ERROR (Status); 1030 1031 ZeroMem ((VOID *) ((UINTN) MemoryAddressUnder1MB), MemorySize); 1032 1033 // 1034 // Allocate space for thunker and Init Thunker 1035 // 1036 Status = AllocateLegacyMemory ( 1037 AllocateMaxAddress, 1038 CONVENTIONAL_MEMORY_TOP, 1039 (sizeof (LOW_MEMORY_THUNK) / EFI_PAGE_SIZE) + 2, 1040 &MemoryAddress 1041 ); 1042 ASSERT_EFI_ERROR (Status); 1043 Private->IntThunk = (LOW_MEMORY_THUNK *) (UINTN) MemoryAddress; 1044 EfiToLegacy16InitTable = &Private->IntThunk->EfiToLegacy16InitTable; 1045 EfiToLegacy16InitTable->ThunkStart = (UINT32) (EFI_PHYSICAL_ADDRESS) (UINTN) MemoryAddress; 1046 EfiToLegacy16InitTable->ThunkSizeInBytes = (UINT32) (sizeof (LOW_MEMORY_THUNK)); 1047 1048 Status = LegacyBiosInitializeThunk (Private); 1049 ASSERT_EFI_ERROR (Status); 1050 1051 // 1052 // Init the legacy memory map in memory < 1 MB. 1053 // 1054 EfiToLegacy16InitTable->BiosLessThan1MB = (UINT32) MemoryAddressUnder1MB; 1055 EfiToLegacy16InitTable->LowPmmMemory = (UINT32) MemoryAddressUnder1MB; 1056 EfiToLegacy16InitTable->LowPmmMemorySizeInBytes = MemorySize; 1057 1058 MemorySize = PcdGet32 (PcdHighPmmMemorySize); 1059 ASSERT ((MemorySize & 0xFFF) == 0); 1060 // 1061 // Allocate high PMM Memory under 16 MB 1062 // 1063 Status = AllocateLegacyMemory ( 1064 AllocateMaxAddress, 1065 0x1000000, 1066 EFI_SIZE_TO_PAGES (MemorySize), 1067 &MemoryAddress 1068 ); 1069 if (EFI_ERROR (Status)) { 1070 // 1071 // If it fails, allocate high PMM Memory under 4GB 1072 // 1073 Status = AllocateLegacyMemory ( 1074 AllocateMaxAddress, 1075 0xFFFFFFFF, 1076 EFI_SIZE_TO_PAGES (MemorySize), 1077 &MemoryAddress 1078 ); 1079 } 1080 if (!EFI_ERROR (Status)) { 1081 EfiToLegacy16InitTable->HiPmmMemory = (UINT32) (EFI_PHYSICAL_ADDRESS) (UINTN) MemoryAddress; 1082 EfiToLegacy16InitTable->HiPmmMemorySizeInBytes = MemorySize; 1083 } 1084 1085 // 1086 // ShutdownAPs(); 1087 // 1088 // Start the Legacy BIOS; 1089 // 1090 Status = ShadowAndStartLegacy16 (Private); 1091 if (EFI_ERROR (Status)) { 1092 return Status; 1093 } 1094 // 1095 // Initialize interrupt redirection code and entries; 1096 // IDT Vectors 0x68-0x6f must be redirected to IDT Vectors 0x08-0x0f. 1097 // 1098 CopyMem ( 1099 Private->IntThunk->InterruptRedirectionCode, 1100 (VOID *) (UINTN) InterruptRedirectionTemplate, 1101 sizeof (Private->IntThunk->InterruptRedirectionCode) 1102 ); 1103 1104 // 1105 // Save Unexpected interrupt vector so can restore it just prior to boot 1106 // 1107 BaseVectorMaster = (UINT32 *) (sizeof (UINT32) * PROTECTED_MODE_BASE_VECTOR_MASTER); 1108 Private->BiosUnexpectedInt = BaseVectorMaster[0]; 1109 IntRedirCode = (UINT32) (UINTN) Private->IntThunk->InterruptRedirectionCode; 1110 for (Index = 0; Index < 8; Index++) { 1111 BaseVectorMaster[Index] = (EFI_SEGMENT (IntRedirCode + Index * 4) << 16) | EFI_OFFSET (IntRedirCode + Index * 4); 1112 } 1113 // 1114 // Save EFI value 1115 // 1116 Private->ThunkSeg = (UINT16) (EFI_SEGMENT (IntRedirCode)); 1117 1118 // 1119 // Allocate reserved memory for SMBIOS table used in legacy boot if SMBIOS table exists 1120 // 1121 InstallSmbiosEventCallback (NULL, NULL); 1122 1123 // 1124 // Create callback function to update the size of reserved memory after LegacyBiosDxe starts 1125 // 1126 Status = gBS->CreateEventEx ( 1127 EVT_NOTIFY_SIGNAL, 1128 TPL_NOTIFY, 1129 InstallSmbiosEventCallback, 1130 NULL, 1131 &gEfiSmbiosTableGuid, 1132 &InstallSmbiosEvent 1133 ); 1134 ASSERT_EFI_ERROR (Status); 1135 1136 // 1137 // Make a new handle and install the protocol 1138 // 1139 Private->Handle = NULL; 1140 Status = gBS->InstallProtocolInterface ( 1141 &Private->Handle, 1142 &gEfiLegacyBiosProtocolGuid, 1143 EFI_NATIVE_INTERFACE, 1144 &Private->LegacyBios 1145 ); 1146 Private->Csm16PciInterfaceVersion = GetPciInterfaceVersion (Private); 1147 1148 DEBUG ((EFI_D_INFO, "CSM16 PCI BIOS Interface Version: %02x.%02x\n", 1149 (UINT8) (Private->Csm16PciInterfaceVersion >> 8), 1150 (UINT8) Private->Csm16PciInterfaceVersion 1151 )); 1152 ASSERT (Private->Csm16PciInterfaceVersion != 0); 1153 return Status; 1154} 1155