3 Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions
7 of the BSD License which accompanies this distribution. The
8 full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include "LegacyBiosInterface.h"
18 #define PHYSICAL_ADDRESS_TO_POINTER(Address) ((VOID *) ((UINTN) Address))
21 // define maximum number of HDD system supports
23 #define MAX_HDD_ENTRIES 0x30
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.
30 LEGACY_BIOS_INSTANCE mPrivateData
;
33 // The SMBIOS table in EfiRuntimeServicesData memory
35 VOID
*mRuntimeSmbiosEntryPoint
= NULL
;
38 // The SMBIOS table in EfiReservedMemoryType memory
40 EFI_PHYSICAL_ADDRESS mReserveSmbiosEntryPoint
= 0;
41 EFI_PHYSICAL_ADDRESS mStructureTableAddress
= 0;
42 UINTN mStructureTablePages
= 0;
43 BOOLEAN mEndOfDxe
= FALSE
;
46 Do an AllocatePages () of type AllocateMaxAddress for EfiBootServicesCode
49 @param AllocateType Allocated Legacy Memory Type
50 @param StartPageAddress Start address of range
51 @param Pages Number of pages to allocate
52 @param Result Result of allocation
54 @retval EFI_SUCCESS Legacy16 code loaded
55 @retval Other No protocol installed, unload driver.
59 AllocateLegacyMemory (
60 IN EFI_ALLOCATE_TYPE AllocateType
,
61 IN EFI_PHYSICAL_ADDRESS StartPageAddress
,
63 OUT EFI_PHYSICAL_ADDRESS
*Result
67 EFI_PHYSICAL_ADDRESS MemPage
;
70 // Allocate Pages of memory less <= StartPageAddress
72 MemPage
= (EFI_PHYSICAL_ADDRESS
) (UINTN
) StartPageAddress
;
73 Status
= gBS
->AllocatePages (
80 // Do not ASSERT on Status error but let caller decide since some cases
81 // memory is already taken but that is ok.
83 if (!EFI_ERROR (Status
)) {
84 *Result
= (EFI_PHYSICAL_ADDRESS
) (UINTN
) MemPage
;
87 // If reach here the status = EFI_SUCCESS
94 This function is called when EFI needs to reserve an area in the 0xE0000 or 0xF0000
97 Note: inconsistency with the Framework CSM spec. Per the spec, this function may be
98 invoked only once. This limitation is relaxed to allow multiple calls in this implemenation.
100 @param This Protocol instance pointer.
101 @param LegacyMemorySize Size of required region
102 @param Region Region to use. 00 = Either 0xE0000 or 0xF0000
103 block Bit0 = 1 0xF0000 block Bit1 = 1 0xE0000
105 @param Alignment Address alignment. Bit mapped. First non-zero
106 bit from right is alignment.
107 @param LegacyMemoryAddress Region Assigned
109 @retval EFI_SUCCESS Region assigned
110 @retval EFI_ACCESS_DENIED Procedure previously invoked
111 @retval Other Region not assigned
116 LegacyBiosGetLegacyRegion (
117 IN EFI_LEGACY_BIOS_PROTOCOL
*This
,
118 IN UINTN LegacyMemorySize
,
121 OUT VOID
**LegacyMemoryAddress
125 LEGACY_BIOS_INSTANCE
*Private
;
126 EFI_IA32_REGISTER_SET Regs
;
130 Private
= LEGACY_BIOS_INSTANCE_FROM_THIS (This
);
131 Private
->LegacyRegion
->UnLock (Private
->LegacyRegion
, 0xE0000, 0x20000, &Granularity
);
133 ZeroMem (&Regs
, sizeof (EFI_IA32_REGISTER_SET
));
134 Regs
.X
.AX
= Legacy16GetTableAddress
;
135 Regs
.X
.BX
= (UINT16
) Region
;
136 Regs
.X
.CX
= (UINT16
) LegacyMemorySize
;
137 Regs
.X
.DX
= (UINT16
) Alignment
;
138 Private
->LegacyBios
.FarCall86 (
139 &Private
->LegacyBios
,
140 Private
->Legacy16CallSegment
,
141 Private
->Legacy16CallOffset
,
147 if (Regs
.X
.AX
== 0) {
148 *LegacyMemoryAddress
= (VOID
*) (((UINTN
) Regs
.X
.DS
<< 4) + Regs
.X
.BX
);
149 Status
= EFI_SUCCESS
;
151 Status
= EFI_OUT_OF_RESOURCES
;
154 Private
->Cpu
->FlushDataCache (Private
->Cpu
, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate
);
155 Private
->LegacyRegion
->Lock (Private
->LegacyRegion
, 0xE0000, 0x20000, &Granularity
);
162 This function is called when copying data to the region assigned by
163 EFI_LEGACY_BIOS_PROTOCOL.GetLegacyRegion().
165 @param This Protocol instance pointer.
166 @param LegacyMemorySize Size of data to copy
167 @param LegacyMemoryAddress Legacy Region destination address Note: must
168 be in region assigned by
169 LegacyBiosGetLegacyRegion
170 @param LegacyMemorySourceAddress Source of data
172 @retval EFI_SUCCESS The data was copied successfully.
173 @retval EFI_ACCESS_DENIED Either the starting or ending address is out of bounds.
177 LegacyBiosCopyLegacyRegion (
178 IN EFI_LEGACY_BIOS_PROTOCOL
*This
,
179 IN UINTN LegacyMemorySize
,
180 IN VOID
*LegacyMemoryAddress
,
181 IN VOID
*LegacyMemorySourceAddress
185 LEGACY_BIOS_INSTANCE
*Private
;
188 if ((LegacyMemoryAddress
< (VOID
*)(UINTN
)0xE0000 ) ||
189 ((UINTN
) LegacyMemoryAddress
+ LegacyMemorySize
> (UINTN
) 0x100000)
191 return EFI_ACCESS_DENIED
;
194 // There is no protection from writes over lapping if this function is
195 // called multiple times.
197 Private
= LEGACY_BIOS_INSTANCE_FROM_THIS (This
);
198 Private
->LegacyRegion
->UnLock (Private
->LegacyRegion
, 0xE0000, 0x20000, &Granularity
);
199 CopyMem (LegacyMemoryAddress
, LegacyMemorySourceAddress
, LegacyMemorySize
);
201 Private
->Cpu
->FlushDataCache (Private
->Cpu
, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate
);
202 Private
->LegacyRegion
->Lock (Private
->LegacyRegion
, 0xE0000, 0x20000, &Granularity
);
209 Find Legacy16 BIOS image in the FLASH device and shadow it into memory. Find
210 the $EFI table in the shadow area. Thunk into the Legacy16 code after it had
213 @param Private Legacy BIOS context data
215 @retval EFI_SUCCESS Legacy16 code loaded
216 @retval Other No protocol installed, unload driver.
220 ShadowAndStartLegacy16 (
221 IN LEGACY_BIOS_INSTANCE
*Private
228 EFI_COMPATIBILITY16_TABLE
*Table
;
230 EFI_IA32_REGISTER_SET Regs
;
231 EFI_TO_COMPATIBILITY16_INIT_TABLE
*EfiToLegacy16InitTable
;
232 EFI_TO_COMPATIBILITY16_BOOT_TABLE
*EfiToLegacy16BootTable
;
233 VOID
*LegacyBiosImage
;
234 UINTN LegacyBiosImageSize
;
238 LEGACY_EFI_HDD_TABLE
*LegacyEfiHddTable
;
241 VOID
*TpmBinaryImage
;
242 UINTN TpmBinaryImageSize
;
246 EFI_PHYSICAL_ADDRESS Address
;
250 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor
;
256 // we allocate the C/D/E/F segment as RT code so no one will use it any more.
259 gDS
->GetMemorySpaceDescriptor (Address
, &Descriptor
);
260 if (Descriptor
.GcdMemoryType
== EfiGcdMemoryTypeSystemMemory
) {
262 // If it is already reserved, we should be safe, or else we allocate it.
264 Status
= gBS
->AllocatePages (
266 EfiRuntimeServicesCode
,
267 0x40000/EFI_PAGE_SIZE
,
270 if (EFI_ERROR (Status
)) {
272 // Bugbug: need to figure out whether C/D/E/F segment should be marked as reserved memory.
274 DEBUG ((DEBUG_ERROR
, "Failed to allocate the C/D/E/F segment Status = %r", Status
));
280 // GetTimerValue (&Ticker);
282 // gRT->SetVariable (L"StartLegacy",
283 // &gEfiGlobalVariableGuid,
284 // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
290 EfiToLegacy16BootTable
= &Private
->IntThunk
->EfiToLegacy16BootTable
;
291 Status
= Private
->LegacyBiosPlatform
->GetPlatformInfo (
292 Private
->LegacyBiosPlatform
,
293 EfiGetPlatformBinarySystemRom
,
295 &LegacyBiosImageSize
,
301 if (EFI_ERROR (Status
)) {
305 Private
->BiosStart
= (UINT32
) (0x100000 - LegacyBiosImageSize
);
306 Private
->OptionRom
= 0xc0000;
307 Private
->LegacyBiosImageSize
= (UINT32
) LegacyBiosImageSize
;
310 // Can only shadow into memory allocated for legacy useage.
312 ASSERT (Private
->BiosStart
> Private
->OptionRom
);
315 // Shadow Legacy BIOS. Turn on memory and copy image
317 Private
->LegacyRegion
->UnLock (Private
->LegacyRegion
, 0xc0000, 0x40000, &Granularity
);
319 ClearPtr
= (VOID
*) ((UINTN
) 0xc0000);
322 // Initialize region from 0xc0000 to start of BIOS to all ffs. This allows unused
323 // regions to be used by EMM386 etc.
325 SetMem ((VOID
*) ClearPtr
, (UINTN
) (0x40000 - LegacyBiosImageSize
), 0xff);
327 TempData
= Private
->BiosStart
;
332 (UINTN
) LegacyBiosImageSize
335 Private
->Cpu
->FlushDataCache (Private
->Cpu
, 0xc0000, 0x40000, EfiCpuFlushTypeWriteBackInvalidate
);
338 // Search for Legacy16 table in Shadowed ROM
342 for (Ptr
= (UINT8
*) TempData
; Ptr
< (UINT8
*) ((UINTN
) 0x100000) && !Done
; Ptr
+= 0x10) {
343 if (*(UINT32
*) Ptr
== SIGNATURE_32 ('I', 'F', 'E', '$')) {
344 Table
= (EFI_COMPATIBILITY16_TABLE
*) Ptr
;
345 PtrEnd
= Ptr
+ Table
->TableLength
;
346 for (CheckSum
= 0; Ptr
< PtrEnd
; Ptr
++) {
347 CheckSum
= (UINT8
) (CheckSum
+*Ptr
);
355 DEBUG ((EFI_D_ERROR
, "No Legacy16 table found\n"));
356 return EFI_NOT_FOUND
;
361 // Legacy16 table header checksum error.
363 DEBUG ((EFI_D_ERROR
, "Legacy16 table found with bad talbe header checksum\n"));
367 // Remember location of the Legacy16 table
369 Private
->Legacy16Table
= Table
;
370 Private
->Legacy16CallSegment
= Table
->Compatibility16CallSegment
;
371 Private
->Legacy16CallOffset
= Table
->Compatibility16CallOffset
;
372 EfiToLegacy16InitTable
= &Private
->IntThunk
->EfiToLegacy16InitTable
;
373 Private
->Legacy16InitPtr
= EfiToLegacy16InitTable
;
374 Private
->Legacy16BootPtr
= &Private
->IntThunk
->EfiToLegacy16BootTable
;
375 Private
->InternalIrqRoutingTable
= NULL
;
376 Private
->NumberIrqRoutingEntries
= 0;
377 Private
->BbsTablePtr
= NULL
;
378 Private
->LegacyEfiHddTable
= NULL
;
379 Private
->DiskEnd
= 0;
380 Private
->Disk4075
= 0;
381 Private
->HddTablePtr
= &Private
->IntThunk
->EfiToLegacy16BootTable
.HddInfo
;
382 Private
->NumberHddControllers
= MAX_IDE_CONTROLLER
;
383 Private
->Dump
[0] = 'D';
384 Private
->Dump
[1] = 'U';
385 Private
->Dump
[2] = 'M';
386 Private
->Dump
[3] = 'P';
389 Private
->Legacy16BootPtr
,
390 sizeof (EFI_TO_COMPATIBILITY16_BOOT_TABLE
)
394 // Store away a copy of the EFI System Table
396 Table
->EfiSystemTable
= (UINT32
) (UINTN
) gST
;
399 // IPF CSM integration -Bug
401 // Construct the Legacy16 boot memory map. This sets up number of
404 LegacyBiosBuildE820 (Private
, &E820Size
);
406 // Initialize BDA and EBDA standard values needed to load Legacy16 code
408 LegacyBiosInitBda (Private
);
409 LegacyBiosInitCmos (Private
);
412 // All legacy interrupt should be masked when do initialization work from legacy 16 code.
414 Private
->Legacy8259
->GetMask(Private
->Legacy8259
, &OldMask
, NULL
, NULL
, NULL
);
416 Private
->Legacy8259
->SetMask(Private
->Legacy8259
, &NewMask
, NULL
, NULL
, NULL
);
419 // Call into Legacy16 code to do an INIT
421 ZeroMem (&Regs
, sizeof (EFI_IA32_REGISTER_SET
));
422 Regs
.X
.AX
= Legacy16InitializeYourself
;
423 Regs
.X
.ES
= EFI_SEGMENT (*((UINT32
*) &EfiToLegacy16InitTable
));
424 Regs
.X
.BX
= EFI_OFFSET (*((UINT32
*) &EfiToLegacy16InitTable
));
426 Private
->LegacyBios
.FarCall86 (
427 &Private
->LegacyBios
,
428 Table
->Compatibility16CallSegment
,
429 Table
->Compatibility16CallOffset
,
436 // Restore original legacy interrupt mask value
438 Private
->Legacy8259
->SetMask(Private
->Legacy8259
, &OldMask
, NULL
, NULL
, NULL
);
440 if (Regs
.X
.AX
!= 0) {
441 return EFI_DEVICE_ERROR
;
446 // GetTimerValue (&Ticker);
448 // gRT->SetVariable (L"BackFromInitYourself",
449 // &gEfiGlobalVariableGuid,
450 // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
456 // Copy E820 table after InitializeYourself is completed
458 ZeroMem (&Regs
, sizeof (EFI_IA32_REGISTER_SET
));
459 Regs
.X
.AX
= Legacy16GetTableAddress
;
460 Regs
.X
.CX
= (UINT16
) E820Size
;
462 Private
->LegacyBios
.FarCall86 (
463 &Private
->LegacyBios
,
464 Table
->Compatibility16CallSegment
,
465 Table
->Compatibility16CallOffset
,
471 Table
->E820Pointer
= (UINT32
) (Regs
.X
.DS
* 16 + Regs
.X
.BX
);
472 Table
->E820Length
= (UINT32
) E820Size
;
473 if (Regs
.X
.AX
!= 0) {
474 DEBUG ((EFI_D_ERROR
, "Legacy16 E820 length insufficient\n"));
476 TempData
= Table
->E820Pointer
;
477 CopyMem ((VOID
*) TempData
, Private
->E820Table
, E820Size
);
480 // Get PnPInstallationCheck Info.
482 Private
->PnPInstallationCheckSegment
= Table
->PnPInstallationCheckSegment
;
483 Private
->PnPInstallationCheckOffset
= Table
->PnPInstallationCheckOffset
;
486 // Check if PCI Express is supported. If yes, Save base address.
488 Status
= Private
->LegacyBiosPlatform
->GetPlatformInfo (
489 Private
->LegacyBiosPlatform
,
490 EfiGetPlatformPciExpressBase
,
498 if (!EFI_ERROR (Status
)) {
499 Private
->Legacy16Table
->PciExpressBase
= (UINT32
)Location
;
503 // Check if TPM is supported. If yes get a region in E0000,F0000 to copy it
504 // into, copy it and update pointer to binary image. This needs to be
505 // done prior to any OPROM for security purposes.
507 Status
= Private
->LegacyBiosPlatform
->GetPlatformInfo (
508 Private
->LegacyBiosPlatform
,
509 EfiGetPlatformBinaryTpmBinary
,
517 if (!EFI_ERROR (Status
)) {
519 ZeroMem (&Regs
, sizeof (EFI_IA32_REGISTER_SET
));
520 Regs
.X
.AX
= Legacy16GetTableAddress
;
521 Regs
.X
.CX
= (UINT16
) TpmBinaryImageSize
;
523 Private
->LegacyBios
.FarCall86 (
524 &Private
->LegacyBios
,
525 Table
->Compatibility16CallSegment
,
526 Table
->Compatibility16CallOffset
,
532 TpmPointer
= (UINT32
) (Regs
.X
.DS
* 16 + Regs
.X
.BX
);
533 if (Regs
.X
.AX
!= 0) {
534 DEBUG ((EFI_D_ERROR
, "TPM cannot be loaded\n"));
536 CopyMem ((VOID
*) (UINTN
)TpmPointer
, TpmBinaryImage
, TpmBinaryImageSize
);
537 Table
->TpmSegment
= Regs
.X
.DS
;
538 Table
->TpmOffset
= Regs
.X
.BX
;
543 // Lock the Legacy BIOS region
545 Private
->Cpu
->FlushDataCache (Private
->Cpu
, Private
->BiosStart
, (UINT32
) LegacyBiosImageSize
, EfiCpuFlushTypeWriteBackInvalidate
);
546 Private
->LegacyRegion
->Lock (Private
->LegacyRegion
, Private
->BiosStart
, (UINT32
) LegacyBiosImageSize
, &Granularity
);
549 // Get the BbsTable from LOW_MEMORY_THUNK
551 BbsTable
= (BBS_TABLE
*)(UINTN
)Private
->IntThunk
->BbsTable
;
552 ZeroMem ((VOID
*)BbsTable
, sizeof (Private
->IntThunk
->BbsTable
));
554 EfiToLegacy16BootTable
->BbsTable
= (UINT32
)(UINTN
)BbsTable
;
555 Private
->BbsTablePtr
= (VOID
*) BbsTable
;
557 // Skip Floppy and possible onboard IDE drives
559 EfiToLegacy16BootTable
->NumberBbsEntries
= 1 + 2 * MAX_IDE_CONTROLLER
;
561 for (Index
= 0; Index
< (sizeof (Private
->IntThunk
->BbsTable
) / sizeof (BBS_TABLE
)); Index
++) {
562 BbsTable
[Index
].BootPriority
= BBS_IGNORE_ENTRY
;
565 // Allocate space for Legacy HDD table
567 LegacyEfiHddTable
= (LEGACY_EFI_HDD_TABLE
*) AllocateZeroPool ((UINTN
) MAX_HDD_ENTRIES
* sizeof (LEGACY_EFI_HDD_TABLE
));
568 ASSERT (LegacyEfiHddTable
);
570 Private
->LegacyEfiHddTable
= LegacyEfiHddTable
;
571 Private
->LegacyEfiHddTableIndex
= 0x00;
575 // GetTimerValue (&Ticker);
577 // gRT->SetVariable (L"EndOfLoadFv",
578 // &gEfiGlobalVariableGuid,
579 // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
589 Shadow all legacy16 OPROMs that haven't been shadowed.
590 Warning: Use this with caution. This routine disconnects all EFI
591 drivers. If used externally then caller must re-connect EFI
594 @param This Protocol instance pointer.
596 @retval EFI_SUCCESS OPROMs shadowed
601 LegacyBiosShadowAllLegacyOproms (
602 IN EFI_LEGACY_BIOS_PROTOCOL
*This
605 LEGACY_BIOS_INSTANCE
*Private
;
608 // EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform;
609 // EFI_LEGACY16_TABLE *Legacy16Table;
611 Private
= LEGACY_BIOS_INSTANCE_FROM_THIS (This
);
614 // LegacyBiosPlatform = Private->LegacyBiosPlatform;
615 // Legacy16Table = Private->Legacy16Table;
617 // Shadow PCI ROMs. We must do this near the end since this will kick
618 // of Native EFI drivers that may be needed to collect info for Legacy16
620 // WARNING: PciIo is gone after this call.
622 PciProgramAllInterruptLineRegisters (Private
);
624 PciShadowRoms (Private
);
627 // Shadow PXE base code, BIS etc.
629 // LegacyBiosPlatform->ShadowServiceRoms (LegacyBiosPlatform,
630 // &Private->OptionRom,
637 Get the PCI BIOS interface version.
639 @param Private Driver private data.
641 @return The PCI interface version number in Binary Coded Decimal (BCD) format.
642 E.g.: 0x0210 indicates 2.10, 0x0300 indicates 3.00
646 GetPciInterfaceVersion (
647 IN LEGACY_BIOS_INSTANCE
*Private
650 EFI_IA32_REGISTER_SET Reg
;
652 UINT16 PciInterfaceVersion
;
654 PciInterfaceVersion
= 0;
659 ThunkFailed
= Private
->LegacyBios
.Int86 (&Private
->LegacyBios
, 0x1A, &Reg
);
662 // From PCI Firmware 3.0 Specification:
663 // If the CARRY FLAG [CF] is cleared and AH is set to 00h, it is still necessary to examine the
664 // contents of [EDX] for the presence of the string "PCI" + (trailing space) to fully validate the
665 // presence of the PCI function set. [BX] will further indicate the version level, with enough
666 // granularity to allow for incremental changes in the code that don't affect the function interface.
667 // Version numbers are stored as Binary Coded Decimal (BCD) values. For example, Version 2.10
668 // would be returned as a 02h in the [BH] registers and 10h in the [BL] registers.
670 if ((Reg
.X
.Flags
.CF
== 0) && (Reg
.H
.AH
== 0) && (Reg
.E
.EDX
== SIGNATURE_32 ('P', 'C', 'I', ' '))) {
671 PciInterfaceVersion
= Reg
.X
.BX
;
674 return PciInterfaceVersion
;
678 Callback function to calculate SMBIOS table size, and allocate memory for SMBIOS table.
679 SMBIOS table will be copied into EfiReservedMemoryType memory in legacy boot path.
681 @param Event Event whose notification function is being invoked.
682 @param Context The pointer to the notification function's context,
683 which is implementation-dependent.
688 InstallSmbiosEventCallback (
694 SMBIOS_TABLE_ENTRY_POINT
*EntryPointStructure
;
697 // Get SMBIOS table from EFI configuration table
699 Status
= EfiGetSystemConfigurationTable (
700 &gEfiSmbiosTableGuid
,
701 &mRuntimeSmbiosEntryPoint
703 if ((EFI_ERROR (Status
)) || (mRuntimeSmbiosEntryPoint
== NULL
)) {
707 EntryPointStructure
= (SMBIOS_TABLE_ENTRY_POINT
*) mRuntimeSmbiosEntryPoint
;
710 // Allocate memory for SMBIOS Entry Point Structure.
711 // CSM framework spec requires SMBIOS table below 4GB in EFI_TO_COMPATIBILITY16_BOOT_TABLE.
713 if (mReserveSmbiosEntryPoint
== 0) {
715 // Entrypoint structure with fixed size is allocated only once.
717 mReserveSmbiosEntryPoint
= SIZE_4GB
- 1;
718 Status
= gBS
->AllocatePages (
720 EfiReservedMemoryType
,
721 EFI_SIZE_TO_PAGES ((UINTN
) (EntryPointStructure
->EntryPointLength
)),
722 &mReserveSmbiosEntryPoint
724 if (EFI_ERROR (Status
)) {
725 mReserveSmbiosEntryPoint
= 0;
728 DEBUG ((EFI_D_INFO
, "Allocate memory for Smbios Entry Point Structure\n"));
731 if ((mStructureTableAddress
!= 0) &&
732 (mStructureTablePages
< EFI_SIZE_TO_PAGES ((UINT32
)EntryPointStructure
->TableLength
))) {
734 // If original buffer is not enough for the new SMBIOS table, free original buffer and re-allocate
736 gBS
->FreePages (mStructureTableAddress
, mStructureTablePages
);
737 mStructureTableAddress
= 0;
738 mStructureTablePages
= 0;
739 DEBUG ((EFI_D_INFO
, "Original size is not enough. Re-allocate the memory.\n"));
742 if (mStructureTableAddress
== 0) {
744 // Allocate reserved memory below 4GB.
745 // Smbios spec requires the structure table is below 4GB.
747 mStructureTableAddress
= SIZE_4GB
- 1;
748 mStructureTablePages
= EFI_SIZE_TO_PAGES (EntryPointStructure
->TableLength
);
749 Status
= gBS
->AllocatePages (
751 EfiReservedMemoryType
,
752 mStructureTablePages
,
753 &mStructureTableAddress
755 if (EFI_ERROR (Status
)) {
757 mReserveSmbiosEntryPoint
,
758 EFI_SIZE_TO_PAGES ((UINTN
) (EntryPointStructure
->EntryPointLength
))
760 mReserveSmbiosEntryPoint
= 0;
761 mStructureTableAddress
= 0;
762 mStructureTablePages
= 0;
765 DEBUG ((EFI_D_INFO
, "Allocate memory for Smbios Structure Table\n"));
770 Callback function to toggle EndOfDxe status. NULL pointer detection needs
771 this status to decide if it's necessary to change attributes of page 0.
773 @param Event Event whose notification function is being invoked.
774 @param Context The pointer to the notification function's context,
775 which is implementation-dependent.
780 ToggleEndOfDxeStatus (
790 // Legacy BIOS needs to access memory between 0-4095, which will cause page
791 // fault exception if NULL pointer detection mechanism is enabled. Following
792 // functions can be used to disable/enable NULL pointer detection before/after
793 // accessing those memory.
797 Enable NULL pointer detection
800 EnableNullDetection (
805 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Desc
;
807 if (((PcdGet8 (PcdNullPointerDetectionPropertyMask
) & BIT0
) == 0)
809 ((mEndOfDxe
== TRUE
) &&
810 ((PcdGet8 (PcdNullPointerDetectionPropertyMask
) & (BIT7
|BIT0
))
817 // Check current capabilities and attributes
819 Status
= gDS
->GetMemorySpaceDescriptor (0, &Desc
);
820 ASSERT_EFI_ERROR (Status
);
823 // Try to add EFI_MEMORY_RP support if necessary
825 if ((Desc
.Capabilities
& EFI_MEMORY_RP
) == 0) {
826 Desc
.Capabilities
|= EFI_MEMORY_RP
;
827 Status
= gDS
->SetMemorySpaceCapabilities (0, EFI_PAGES_TO_SIZE(1),
829 ASSERT_EFI_ERROR (Status
);
830 if (EFI_ERROR (Status
)) {
836 // Don't bother if EFI_MEMORY_RP is already set.
838 if ((Desc
.Attributes
& EFI_MEMORY_RP
) == 0) {
839 Desc
.Attributes
|= EFI_MEMORY_RP
;
840 Status
= gDS
->SetMemorySpaceAttributes (0, EFI_PAGES_TO_SIZE(1),
842 ASSERT_EFI_ERROR (Status
);
847 Disable NULL pointer detection
850 DisableNullDetection (
855 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Desc
;
857 if (((PcdGet8 (PcdNullPointerDetectionPropertyMask
) & BIT0
) == 0)
859 ((mEndOfDxe
== TRUE
) &&
860 ((PcdGet8 (PcdNullPointerDetectionPropertyMask
) & (BIT7
|BIT0
))
867 // Check current capabilities and attributes
869 Status
= gDS
->GetMemorySpaceDescriptor (0, &Desc
);
870 ASSERT_EFI_ERROR (Status
);
873 // Try to add EFI_MEMORY_RP support if necessary
875 if ((Desc
.Capabilities
& EFI_MEMORY_RP
) == 0) {
876 Desc
.Capabilities
|= EFI_MEMORY_RP
;
877 Status
= gDS
->SetMemorySpaceCapabilities (0, EFI_PAGES_TO_SIZE(1),
879 ASSERT_EFI_ERROR (Status
);
880 if (EFI_ERROR (Status
)) {
886 // Don't bother if EFI_MEMORY_RP is already cleared.
888 if ((Desc
.Attributes
& EFI_MEMORY_RP
) != 0) {
889 Desc
.Attributes
&= ~EFI_MEMORY_RP
;
890 Status
= gDS
->SetMemorySpaceAttributes (0, EFI_PAGES_TO_SIZE(1),
892 ASSERT_EFI_ERROR (Status
);
894 DEBUG ((DEBUG_WARN
, "!!! Page 0 is supposed to be disabled !!!\r\n"));
899 Install Driver to produce Legacy BIOS protocol.
901 @param ImageHandle Handle of driver image.
902 @param SystemTable Pointer to system table.
904 @retval EFI_SUCCESS Legacy BIOS protocol installed
905 @retval No protocol installed, unload driver.
911 IN EFI_HANDLE ImageHandle
,
912 IN EFI_SYSTEM_TABLE
*SystemTable
916 LEGACY_BIOS_INSTANCE
*Private
;
917 EFI_TO_COMPATIBILITY16_INIT_TABLE
*EfiToLegacy16InitTable
;
918 EFI_PHYSICAL_ADDRESS MemoryAddress
;
919 EFI_PHYSICAL_ADDRESS EbdaReservedBaseAddress
;
921 EFI_PHYSICAL_ADDRESS MemoryAddressUnder1MB
;
923 UINT32
*BaseVectorMaster
;
924 EFI_PHYSICAL_ADDRESS StartAddress
;
926 EFI_PHYSICAL_ADDRESS MemStart
;
931 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor
;
934 EFI_EVENT InstallSmbiosEvent
;
935 EFI_EVENT EndOfDxeEvent
;
938 // Load this driver's image to memory
940 Status
= RelocateImageUnder4GIfNeeded (ImageHandle
, SystemTable
);
941 if (EFI_ERROR (Status
)) {
946 // When UEFI Secure Boot is enabled, CSM module will not start any more.
949 GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME
, (VOID
**)&SecureBoot
, NULL
);
950 if ((SecureBoot
!= NULL
) && (*SecureBoot
== SECURE_BOOT_MODE_ENABLE
)) {
951 FreePool (SecureBoot
);
952 return EFI_SECURITY_VIOLATION
;
955 if (SecureBoot
!= NULL
) {
956 FreePool (SecureBoot
);
959 Private
= &mPrivateData
;
960 ZeroMem (Private
, sizeof (LEGACY_BIOS_INSTANCE
));
963 // Grab a copy of all the protocols we depend on. Any error would
964 // be a dispatcher bug!.
966 Status
= gBS
->LocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**) &Private
->Cpu
);
967 ASSERT_EFI_ERROR (Status
);
969 Status
= gBS
->LocateProtocol (&gEfiTimerArchProtocolGuid
, NULL
, (VOID
**) &Private
->Timer
);
970 ASSERT_EFI_ERROR (Status
);
972 Status
= gBS
->LocateProtocol (&gEfiLegacyRegion2ProtocolGuid
, NULL
, (VOID
**) &Private
->LegacyRegion
);
973 ASSERT_EFI_ERROR (Status
);
975 Status
= gBS
->LocateProtocol (&gEfiLegacyBiosPlatformProtocolGuid
, NULL
, (VOID
**) &Private
->LegacyBiosPlatform
);
976 ASSERT_EFI_ERROR (Status
);
978 Status
= gBS
->LocateProtocol (&gEfiLegacy8259ProtocolGuid
, NULL
, (VOID
**) &Private
->Legacy8259
);
979 ASSERT_EFI_ERROR (Status
);
981 Status
= gBS
->LocateProtocol (&gEfiLegacyInterruptProtocolGuid
, NULL
, (VOID
**) &Private
->LegacyInterrupt
);
982 ASSERT_EFI_ERROR (Status
);
985 // Locate Memory Test Protocol if exists
987 Status
= gBS
->LocateProtocol (
988 &gEfiGenericMemTestProtocolGuid
,
990 (VOID
**) &Private
->GenericMemoryTest
992 ASSERT_EFI_ERROR (Status
);
995 // Make sure all memory from 0-640K is tested
997 for (StartAddress
= 0; StartAddress
< 0xa0000; ) {
998 gDS
->GetMemorySpaceDescriptor (StartAddress
, &Descriptor
);
999 if (Descriptor
.GcdMemoryType
!= EfiGcdMemoryTypeReserved
) {
1000 StartAddress
= Descriptor
.BaseAddress
+ Descriptor
.Length
;
1003 Length
= MIN (Descriptor
.Length
, 0xa0000 - StartAddress
);
1004 Private
->GenericMemoryTest
->CompatibleRangeTest (
1005 Private
->GenericMemoryTest
,
1009 StartAddress
= StartAddress
+ Length
;
1012 // Make sure all memory from 1MB to 16MB is tested and added to memory map
1014 for (StartAddress
= BASE_1MB
; StartAddress
< BASE_16MB
; ) {
1015 gDS
->GetMemorySpaceDescriptor (StartAddress
, &Descriptor
);
1016 if (Descriptor
.GcdMemoryType
!= EfiGcdMemoryTypeReserved
) {
1017 StartAddress
= Descriptor
.BaseAddress
+ Descriptor
.Length
;
1020 Length
= MIN (Descriptor
.Length
, BASE_16MB
- StartAddress
);
1021 Private
->GenericMemoryTest
->CompatibleRangeTest (
1022 Private
->GenericMemoryTest
,
1026 StartAddress
= StartAddress
+ Length
;
1029 Private
->Signature
= LEGACY_BIOS_INSTANCE_SIGNATURE
;
1031 Private
->LegacyBios
.Int86
= LegacyBiosInt86
;
1032 Private
->LegacyBios
.FarCall86
= LegacyBiosFarCall86
;
1033 Private
->LegacyBios
.CheckPciRom
= LegacyBiosCheckPciRom
;
1034 Private
->LegacyBios
.InstallPciRom
= LegacyBiosInstallPciRom
;
1035 Private
->LegacyBios
.LegacyBoot
= LegacyBiosLegacyBoot
;
1036 Private
->LegacyBios
.UpdateKeyboardLedStatus
= LegacyBiosUpdateKeyboardLedStatus
;
1037 Private
->LegacyBios
.GetBbsInfo
= LegacyBiosGetBbsInfo
;
1038 Private
->LegacyBios
.ShadowAllLegacyOproms
= LegacyBiosShadowAllLegacyOproms
;
1039 Private
->LegacyBios
.PrepareToBootEfi
= LegacyBiosPrepareToBootEfi
;
1040 Private
->LegacyBios
.GetLegacyRegion
= LegacyBiosGetLegacyRegion
;
1041 Private
->LegacyBios
.CopyLegacyRegion
= LegacyBiosCopyLegacyRegion
;
1042 Private
->LegacyBios
.BootUnconventionalDevice
= LegacyBiosBootUnconventionalDevice
;
1044 Private
->ImageHandle
= ImageHandle
;
1047 // Enable read attribute of legacy region.
1050 Private
->LegacyRegion
->Decode (
1051 Private
->LegacyRegion
,
1058 // Set Cachebility for legacy region
1059 // BUGBUG: Comments about this legacy region cacheability setting
1060 // This setting will make D865GCHProduction CSM Unhappy
1062 if (PcdGetBool (PcdLegacyBiosCacheLegacyRegion
)) {
1063 gDS
->SetMemorySpaceAttributes (
1068 gDS
->SetMemorySpaceAttributes (
1075 gDS
->SetMemorySpaceAttributes (
1082 // Allocate 0 - 4K for real mode interupt vectors and BDA.
1084 AllocateLegacyMemory (
1090 ASSERT (MemoryAddress
== 0x000000000);
1092 ClearPtr
= (VOID
*) ((UINTN
) 0x0000);
1095 // Initialize region from 0x0000 to 4k. This initializes interrupt vector
1098 DisableNullDetection ();
1099 gBS
->SetMem ((VOID
*) ClearPtr
, 0x400, INITIAL_VALUE_BELOW_1K
);
1100 ZeroMem ((VOID
*) ((UINTN
)ClearPtr
+ 0x400), 0xC00);
1101 EnableNullDetection ();
1104 // Allocate pages for OPROM usage
1106 MemorySize
= PcdGet32 (PcdEbdaReservedMemorySize
);
1107 ASSERT ((MemorySize
& 0xFFF) == 0);
1109 Status
= AllocateLegacyMemory (
1111 CONVENTIONAL_MEMORY_TOP
- MemorySize
,
1112 EFI_SIZE_TO_PAGES (MemorySize
),
1115 ASSERT_EFI_ERROR (Status
);
1117 ZeroMem ((VOID
*) ((UINTN
) MemoryAddress
), MemorySize
);
1120 // Allocate all 32k chunks from 0x60000 ~ 0x88000 for Legacy OPROMs that
1121 // don't use PMM but look for zeroed memory. Note that various non-BBS
1122 // OpROMs expect different areas to be free
1124 EbdaReservedBaseAddress
= MemoryAddress
;
1125 MemoryAddress
= PcdGet32 (PcdOpromReservedMemoryBase
);
1126 MemorySize
= PcdGet32 (PcdOpromReservedMemorySize
);
1128 // Check if base address and size for reserved memory are 4KB aligned.
1130 ASSERT ((MemoryAddress
& 0xFFF) == 0);
1131 ASSERT ((MemorySize
& 0xFFF) == 0);
1133 // Check if the reserved memory is below EBDA reserved range.
1135 ASSERT ((MemoryAddress
< EbdaReservedBaseAddress
) && ((MemoryAddress
+ MemorySize
- 1) < EbdaReservedBaseAddress
));
1136 for (MemStart
= MemoryAddress
; MemStart
< MemoryAddress
+ MemorySize
; MemStart
+= 0x1000) {
1137 Status
= AllocateLegacyMemory (
1143 if (!EFI_ERROR (Status
)) {
1144 MemoryPtr
= (VOID
*) ((UINTN
) StartAddress
);
1145 ZeroMem (MemoryPtr
, 0x1000);
1147 DEBUG ((EFI_D_ERROR
, "WARNING: Allocate legacy memory fail for SCSI card - %x\n", MemStart
));
1152 // Allocate low PMM memory and zero it out
1154 MemorySize
= PcdGet32 (PcdLowPmmMemorySize
);
1155 ASSERT ((MemorySize
& 0xFFF) == 0);
1156 Status
= AllocateLegacyMemory (
1158 CONVENTIONAL_MEMORY_TOP
,
1159 EFI_SIZE_TO_PAGES (MemorySize
),
1160 &MemoryAddressUnder1MB
1162 ASSERT_EFI_ERROR (Status
);
1164 ZeroMem ((VOID
*) ((UINTN
) MemoryAddressUnder1MB
), MemorySize
);
1167 // Allocate space for thunker and Init Thunker
1169 Status
= AllocateLegacyMemory (
1171 CONVENTIONAL_MEMORY_TOP
,
1172 (sizeof (LOW_MEMORY_THUNK
) / EFI_PAGE_SIZE
) + 2,
1175 ASSERT_EFI_ERROR (Status
);
1176 Private
->IntThunk
= (LOW_MEMORY_THUNK
*) (UINTN
) MemoryAddress
;
1177 EfiToLegacy16InitTable
= &Private
->IntThunk
->EfiToLegacy16InitTable
;
1178 EfiToLegacy16InitTable
->ThunkStart
= (UINT32
) (EFI_PHYSICAL_ADDRESS
) (UINTN
) MemoryAddress
;
1179 EfiToLegacy16InitTable
->ThunkSizeInBytes
= (UINT32
) (sizeof (LOW_MEMORY_THUNK
));
1181 Status
= LegacyBiosInitializeThunk (Private
);
1182 ASSERT_EFI_ERROR (Status
);
1185 // Init the legacy memory map in memory < 1 MB.
1187 EfiToLegacy16InitTable
->BiosLessThan1MB
= (UINT32
) MemoryAddressUnder1MB
;
1188 EfiToLegacy16InitTable
->LowPmmMemory
= (UINT32
) MemoryAddressUnder1MB
;
1189 EfiToLegacy16InitTable
->LowPmmMemorySizeInBytes
= MemorySize
;
1191 MemorySize
= PcdGet32 (PcdHighPmmMemorySize
);
1192 ASSERT ((MemorySize
& 0xFFF) == 0);
1194 // Allocate high PMM Memory under 16 MB
1196 Status
= AllocateLegacyMemory (
1199 EFI_SIZE_TO_PAGES (MemorySize
),
1202 if (EFI_ERROR (Status
)) {
1204 // If it fails, allocate high PMM Memory under 4GB
1206 Status
= AllocateLegacyMemory (
1209 EFI_SIZE_TO_PAGES (MemorySize
),
1213 if (!EFI_ERROR (Status
)) {
1214 EfiToLegacy16InitTable
->HiPmmMemory
= (UINT32
) (EFI_PHYSICAL_ADDRESS
) (UINTN
) MemoryAddress
;
1215 EfiToLegacy16InitTable
->HiPmmMemorySizeInBytes
= MemorySize
;
1221 // Start the Legacy BIOS;
1223 Status
= ShadowAndStartLegacy16 (Private
);
1224 if (EFI_ERROR (Status
)) {
1228 // Initialize interrupt redirection code and entries;
1229 // IDT Vectors 0x68-0x6f must be redirected to IDT Vectors 0x08-0x0f.
1232 Private
->IntThunk
->InterruptRedirectionCode
,
1233 (VOID
*) (UINTN
) InterruptRedirectionTemplate
,
1234 sizeof (Private
->IntThunk
->InterruptRedirectionCode
)
1238 // Save Unexpected interrupt vector so can restore it just prior to boot
1240 DisableNullDetection ();
1242 BaseVectorMaster
= (UINT32
*) (sizeof (UINT32
) * PROTECTED_MODE_BASE_VECTOR_MASTER
);
1243 Private
->BiosUnexpectedInt
= BaseVectorMaster
[0];
1244 IntRedirCode
= (UINT32
) (UINTN
) Private
->IntThunk
->InterruptRedirectionCode
;
1245 for (Index
= 0; Index
< 8; Index
++) {
1246 BaseVectorMaster
[Index
] = (EFI_SEGMENT (IntRedirCode
+ Index
* 4) << 16) | EFI_OFFSET (IntRedirCode
+ Index
* 4);
1249 EnableNullDetection ();
1254 Private
->ThunkSeg
= (UINT16
) (EFI_SEGMENT (IntRedirCode
));
1257 // Allocate reserved memory for SMBIOS table used in legacy boot if SMBIOS table exists
1259 InstallSmbiosEventCallback (NULL
, NULL
);
1262 // Create callback function to update the size of reserved memory after LegacyBiosDxe starts
1264 Status
= gBS
->CreateEventEx (
1267 InstallSmbiosEventCallback
,
1269 &gEfiSmbiosTableGuid
,
1272 ASSERT_EFI_ERROR (Status
);
1275 // Create callback to update status of EndOfDxe, which is needed by NULL
1276 // pointer detection
1278 Status
= gBS
->CreateEventEx (
1281 ToggleEndOfDxeStatus
,
1283 &gEfiEndOfDxeEventGroupGuid
,
1286 ASSERT_EFI_ERROR (Status
);
1289 // Make a new handle and install the protocol
1291 Private
->Handle
= NULL
;
1292 Status
= gBS
->InstallProtocolInterface (
1294 &gEfiLegacyBiosProtocolGuid
,
1295 EFI_NATIVE_INTERFACE
,
1296 &Private
->LegacyBios
1298 Private
->Csm16PciInterfaceVersion
= GetPciInterfaceVersion (Private
);
1300 DEBUG ((EFI_D_INFO
, "CSM16 PCI BIOS Interface Version: %02x.%02x\n",
1301 (UINT8
) (Private
->Csm16PciInterfaceVersion
>> 8),
1302 (UINT8
) Private
->Csm16PciInterfaceVersion
1304 ASSERT (Private
->Csm16PciInterfaceVersion
!= 0);