3 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include "LegacyBiosInterface.h"
11 #define PHYSICAL_ADDRESS_TO_POINTER(Address) ((VOID *) ((UINTN) Address))
14 // define maximum number of HDD system supports
16 #define MAX_HDD_ENTRIES 0x30
20 // Since this driver will only ever produce one instance of the Private Data
21 // protocol you are not required to dynamically allocate the PrivateData.
23 LEGACY_BIOS_INSTANCE mPrivateData
;
26 // The SMBIOS table in EfiRuntimeServicesData memory
28 VOID
*mRuntimeSmbiosEntryPoint
= NULL
;
31 // The SMBIOS table in EfiReservedMemoryType memory
33 EFI_PHYSICAL_ADDRESS mReserveSmbiosEntryPoint
= 0;
34 EFI_PHYSICAL_ADDRESS mStructureTableAddress
= 0;
35 UINTN mStructureTablePages
= 0;
36 BOOLEAN mEndOfDxe
= FALSE
;
39 Allocate memory for legacy usage. The memory is executable.
41 @param AllocateType The type of allocation to perform.
42 @param MemoryType The type of memory to allocate.
43 @param StartPageAddress Start address of range
44 @param Pages Number of pages to allocate
45 @param Result Result of allocation
47 @retval EFI_SUCCESS Legacy memory is allocated successfully.
48 @retval Other Legacy memory is not allocated.
52 AllocateLegacyMemory (
53 IN EFI_ALLOCATE_TYPE AllocateType
,
54 IN EFI_MEMORY_TYPE MemoryType
,
55 IN EFI_PHYSICAL_ADDRESS StartPageAddress
,
57 OUT EFI_PHYSICAL_ADDRESS
*Result
61 EFI_PHYSICAL_ADDRESS MemPage
;
62 EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc
;
65 // Allocate Pages of memory less <= StartPageAddress
67 MemPage
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)StartPageAddress
;
68 Status
= gBS
->AllocatePages (
75 // Do not ASSERT on Status error but let caller decide since some cases
76 // memory is already taken but that is ok.
78 if (!EFI_ERROR (Status
)) {
79 if (MemoryType
!= EfiBootServicesCode
) {
81 // Make sure that the buffer can be used to store code.
83 Status
= gDS
->GetMemorySpaceDescriptor (MemPage
, &MemDesc
);
84 if (!EFI_ERROR (Status
) && ((MemDesc
.Attributes
& EFI_MEMORY_XP
) != 0)) {
85 Status
= gDS
->SetMemorySpaceAttributes (
87 EFI_PAGES_TO_SIZE (Pages
),
88 MemDesc
.Attributes
& (~EFI_MEMORY_XP
)
92 if (EFI_ERROR (Status
)) {
93 gBS
->FreePages (MemPage
, Pages
);
98 if (!EFI_ERROR (Status
)) {
99 *Result
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)MemPage
;
106 This function is called when EFI needs to reserve an area in the 0xE0000 or 0xF0000
109 Note: inconsistency with the Framework CSM spec. Per the spec, this function may be
110 invoked only once. This limitation is relaxed to allow multiple calls in this implementation.
112 @param This Protocol instance pointer.
113 @param LegacyMemorySize Size of required region
114 @param Region Region to use. 00 = Either 0xE0000 or 0xF0000
115 block Bit0 = 1 0xF0000 block Bit1 = 1 0xE0000
117 @param Alignment Address alignment. Bit mapped. First non-zero
118 bit from right is alignment.
119 @param LegacyMemoryAddress Region Assigned
121 @retval EFI_SUCCESS Region assigned
122 @retval EFI_ACCESS_DENIED Procedure previously invoked
123 @retval Other Region not assigned
128 LegacyBiosGetLegacyRegion (
129 IN EFI_LEGACY_BIOS_PROTOCOL
*This
,
130 IN UINTN LegacyMemorySize
,
133 OUT VOID
**LegacyMemoryAddress
136 LEGACY_BIOS_INSTANCE
*Private
;
137 EFI_IA32_REGISTER_SET Regs
;
141 Private
= LEGACY_BIOS_INSTANCE_FROM_THIS (This
);
142 Private
->LegacyRegion
->UnLock (Private
->LegacyRegion
, 0xE0000, 0x20000, &Granularity
);
144 ZeroMem (&Regs
, sizeof (EFI_IA32_REGISTER_SET
));
145 Regs
.X
.AX
= Legacy16GetTableAddress
;
146 Regs
.X
.BX
= (UINT16
)Region
;
147 Regs
.X
.CX
= (UINT16
)LegacyMemorySize
;
148 Regs
.X
.DX
= (UINT16
)Alignment
;
149 Private
->LegacyBios
.FarCall86 (
150 &Private
->LegacyBios
,
151 Private
->Legacy16CallSegment
,
152 Private
->Legacy16CallOffset
,
158 if (Regs
.X
.AX
== 0) {
159 *LegacyMemoryAddress
= (VOID
*)(((UINTN
)Regs
.X
.DS
<< 4) + Regs
.X
.BX
);
160 Status
= EFI_SUCCESS
;
162 Status
= EFI_OUT_OF_RESOURCES
;
165 Private
->Cpu
->FlushDataCache (Private
->Cpu
, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate
);
166 Private
->LegacyRegion
->Lock (Private
->LegacyRegion
, 0xE0000, 0x20000, &Granularity
);
172 This function is called when copying data to the region assigned by
173 EFI_LEGACY_BIOS_PROTOCOL.GetLegacyRegion().
175 @param This Protocol instance pointer.
176 @param LegacyMemorySize Size of data to copy
177 @param LegacyMemoryAddress Legacy Region destination address Note: must
178 be in region assigned by
179 LegacyBiosGetLegacyRegion
180 @param LegacyMemorySourceAddress Source of data
182 @retval EFI_SUCCESS The data was copied successfully.
183 @retval EFI_ACCESS_DENIED Either the starting or ending address is out of bounds.
187 LegacyBiosCopyLegacyRegion (
188 IN EFI_LEGACY_BIOS_PROTOCOL
*This
,
189 IN UINTN LegacyMemorySize
,
190 IN VOID
*LegacyMemoryAddress
,
191 IN VOID
*LegacyMemorySourceAddress
194 LEGACY_BIOS_INSTANCE
*Private
;
197 if ((LegacyMemoryAddress
< (VOID
*)(UINTN
)0xE0000) ||
198 ((UINTN
)LegacyMemoryAddress
+ LegacyMemorySize
> (UINTN
)0x100000)
201 return EFI_ACCESS_DENIED
;
205 // There is no protection from writes over lapping if this function is
206 // called multiple times.
208 Private
= LEGACY_BIOS_INSTANCE_FROM_THIS (This
);
209 Private
->LegacyRegion
->UnLock (Private
->LegacyRegion
, 0xE0000, 0x20000, &Granularity
);
210 CopyMem (LegacyMemoryAddress
, LegacyMemorySourceAddress
, LegacyMemorySize
);
212 Private
->Cpu
->FlushDataCache (Private
->Cpu
, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate
);
213 Private
->LegacyRegion
->Lock (Private
->LegacyRegion
, 0xE0000, 0x20000, &Granularity
);
219 Find Legacy16 BIOS image in the FLASH device and shadow it into memory. Find
220 the $EFI table in the shadow area. Thunk into the Legacy16 code after it had
223 @param Private Legacy BIOS context data
225 @retval EFI_SUCCESS Legacy16 code loaded
226 @retval Other No protocol installed, unload driver.
230 ShadowAndStartLegacy16 (
231 IN LEGACY_BIOS_INSTANCE
*Private
238 EFI_COMPATIBILITY16_TABLE
*Table
;
240 EFI_IA32_REGISTER_SET Regs
;
241 EFI_TO_COMPATIBILITY16_INIT_TABLE
*EfiToLegacy16InitTable
;
242 EFI_TO_COMPATIBILITY16_BOOT_TABLE
*EfiToLegacy16BootTable
;
243 VOID
*LegacyBiosImage
;
244 UINTN LegacyBiosImageSize
;
248 LEGACY_EFI_HDD_TABLE
*LegacyEfiHddTable
;
251 VOID
*TpmBinaryImage
;
252 UINTN TpmBinaryImageSize
;
256 EFI_PHYSICAL_ADDRESS Address
;
260 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor
;
266 // we allocate the C/D/E/F segment as RT code so no one will use it any more.
269 gDS
->GetMemorySpaceDescriptor (Address
, &Descriptor
);
270 if (Descriptor
.GcdMemoryType
== EfiGcdMemoryTypeSystemMemory
) {
272 // If it is already reserved, we should be safe, or else we allocate it.
274 Status
= gBS
->AllocatePages (
276 EfiRuntimeServicesCode
,
277 0x40000/EFI_PAGE_SIZE
,
280 if (EFI_ERROR (Status
)) {
282 // Bugbug: need to figure out whether C/D/E/F segment should be marked as reserved memory.
284 DEBUG ((DEBUG_ERROR
, "Failed to allocate the C/D/E/F segment Status = %r", Status
));
290 // GetTimerValue (&Ticker);
292 // gRT->SetVariable (L"StartLegacy",
293 // &gEfiGlobalVariableGuid,
294 // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
300 EfiToLegacy16BootTable
= &Private
->IntThunk
->EfiToLegacy16BootTable
;
301 Status
= Private
->LegacyBiosPlatform
->GetPlatformInfo (
302 Private
->LegacyBiosPlatform
,
303 EfiGetPlatformBinarySystemRom
,
305 &LegacyBiosImageSize
,
311 if (EFI_ERROR (Status
)) {
315 Private
->BiosStart
= (UINT32
)(0x100000 - LegacyBiosImageSize
);
316 Private
->OptionRom
= 0xc0000;
317 Private
->LegacyBiosImageSize
= (UINT32
)LegacyBiosImageSize
;
320 // Can only shadow into memory allocated for legacy usage.
322 ASSERT (Private
->BiosStart
> Private
->OptionRom
);
325 // Shadow Legacy BIOS. Turn on memory and copy image
327 Private
->LegacyRegion
->UnLock (Private
->LegacyRegion
, 0xc0000, 0x40000, &Granularity
);
329 ClearPtr
= (VOID
*)((UINTN
)0xc0000);
332 // Initialize region from 0xc0000 to start of BIOS to all ffs. This allows unused
333 // regions to be used by EMM386 etc.
335 SetMem ((VOID
*)ClearPtr
, (UINTN
)(0x40000 - LegacyBiosImageSize
), 0xff);
337 TempData
= Private
->BiosStart
;
342 (UINTN
)LegacyBiosImageSize
345 Private
->Cpu
->FlushDataCache (Private
->Cpu
, 0xc0000, 0x40000, EfiCpuFlushTypeWriteBackInvalidate
);
348 // Search for Legacy16 table in Shadowed ROM
352 for (Ptr
= (UINT8
*)TempData
; Ptr
< (UINT8
*)((UINTN
)0x100000) && !Done
; Ptr
+= 0x10) {
353 if (*(UINT32
*)Ptr
== SIGNATURE_32 ('I', 'F', 'E', '$')) {
354 Table
= (EFI_COMPATIBILITY16_TABLE
*)Ptr
;
355 PtrEnd
= Ptr
+ Table
->TableLength
;
356 for (CheckSum
= 0; Ptr
< PtrEnd
; Ptr
++) {
357 CheckSum
= (UINT8
)(CheckSum
+*Ptr
);
365 DEBUG ((DEBUG_ERROR
, "No Legacy16 table found\n"));
366 return EFI_NOT_FOUND
;
371 // Legacy16 table header checksum error.
373 DEBUG ((DEBUG_ERROR
, "Legacy16 table found with bad talbe header checksum\n"));
377 // Remember location of the Legacy16 table
379 Private
->Legacy16Table
= Table
;
380 Private
->Legacy16CallSegment
= Table
->Compatibility16CallSegment
;
381 Private
->Legacy16CallOffset
= Table
->Compatibility16CallOffset
;
382 EfiToLegacy16InitTable
= &Private
->IntThunk
->EfiToLegacy16InitTable
;
383 Private
->Legacy16InitPtr
= EfiToLegacy16InitTable
;
384 Private
->Legacy16BootPtr
= &Private
->IntThunk
->EfiToLegacy16BootTable
;
385 Private
->InternalIrqRoutingTable
= NULL
;
386 Private
->NumberIrqRoutingEntries
= 0;
387 Private
->BbsTablePtr
= NULL
;
388 Private
->LegacyEfiHddTable
= NULL
;
389 Private
->DiskEnd
= 0;
390 Private
->Disk4075
= 0;
391 Private
->HddTablePtr
= &Private
->IntThunk
->EfiToLegacy16BootTable
.HddInfo
;
392 Private
->NumberHddControllers
= MAX_IDE_CONTROLLER
;
393 Private
->Dump
[0] = 'D';
394 Private
->Dump
[1] = 'U';
395 Private
->Dump
[2] = 'M';
396 Private
->Dump
[3] = 'P';
399 Private
->Legacy16BootPtr
,
400 sizeof (EFI_TO_COMPATIBILITY16_BOOT_TABLE
)
404 // Store away a copy of the EFI System Table
406 Table
->EfiSystemTable
= (UINT32
)(UINTN
)gST
;
409 // IPF CSM integration -Bug
411 // Construct the Legacy16 boot memory map. This sets up number of
414 LegacyBiosBuildE820 (Private
, &E820Size
);
416 // Initialize BDA and EBDA standard values needed to load Legacy16 code
418 LegacyBiosInitBda (Private
);
419 LegacyBiosInitCmos (Private
);
422 // All legacy interrupt should be masked when do initialization work from legacy 16 code.
424 Private
->Legacy8259
->GetMask (Private
->Legacy8259
, &OldMask
, NULL
, NULL
, NULL
);
426 Private
->Legacy8259
->SetMask (Private
->Legacy8259
, &NewMask
, NULL
, NULL
, NULL
);
429 // Call into Legacy16 code to do an INIT
431 ZeroMem (&Regs
, sizeof (EFI_IA32_REGISTER_SET
));
432 Regs
.X
.AX
= Legacy16InitializeYourself
;
433 Regs
.X
.ES
= EFI_SEGMENT (*((UINT32
*)&EfiToLegacy16InitTable
));
434 Regs
.X
.BX
= EFI_OFFSET (*((UINT32
*)&EfiToLegacy16InitTable
));
436 Private
->LegacyBios
.FarCall86 (
437 &Private
->LegacyBios
,
438 Table
->Compatibility16CallSegment
,
439 Table
->Compatibility16CallOffset
,
446 // Restore original legacy interrupt mask value
448 Private
->Legacy8259
->SetMask (Private
->Legacy8259
, &OldMask
, NULL
, NULL
, NULL
);
450 if (Regs
.X
.AX
!= 0) {
451 return EFI_DEVICE_ERROR
;
456 // GetTimerValue (&Ticker);
458 // gRT->SetVariable (L"BackFromInitYourself",
459 // &gEfiGlobalVariableGuid,
460 // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
466 // Copy E820 table after InitializeYourself is completed
468 ZeroMem (&Regs
, sizeof (EFI_IA32_REGISTER_SET
));
469 Regs
.X
.AX
= Legacy16GetTableAddress
;
470 Regs
.X
.CX
= (UINT16
)E820Size
;
472 Private
->LegacyBios
.FarCall86 (
473 &Private
->LegacyBios
,
474 Table
->Compatibility16CallSegment
,
475 Table
->Compatibility16CallOffset
,
481 Table
->E820Pointer
= (UINT32
)(Regs
.X
.DS
* 16 + Regs
.X
.BX
);
482 Table
->E820Length
= (UINT32
)E820Size
;
483 if (Regs
.X
.AX
!= 0) {
484 DEBUG ((DEBUG_ERROR
, "Legacy16 E820 length insufficient\n"));
486 TempData
= Table
->E820Pointer
;
487 CopyMem ((VOID
*)TempData
, Private
->E820Table
, E820Size
);
491 // Get PnPInstallationCheck Info.
493 Private
->PnPInstallationCheckSegment
= Table
->PnPInstallationCheckSegment
;
494 Private
->PnPInstallationCheckOffset
= Table
->PnPInstallationCheckOffset
;
497 // Check if PCI Express is supported. If yes, Save base address.
499 Status
= Private
->LegacyBiosPlatform
->GetPlatformInfo (
500 Private
->LegacyBiosPlatform
,
501 EfiGetPlatformPciExpressBase
,
509 if (!EFI_ERROR (Status
)) {
510 Private
->Legacy16Table
->PciExpressBase
= (UINT32
)Location
;
515 // Check if TPM is supported. If yes get a region in E0000,F0000 to copy it
516 // into, copy it and update pointer to binary image. This needs to be
517 // done prior to any OPROM for security purposes.
519 Status
= Private
->LegacyBiosPlatform
->GetPlatformInfo (
520 Private
->LegacyBiosPlatform
,
521 EfiGetPlatformBinaryTpmBinary
,
529 if (!EFI_ERROR (Status
)) {
530 ZeroMem (&Regs
, sizeof (EFI_IA32_REGISTER_SET
));
531 Regs
.X
.AX
= Legacy16GetTableAddress
;
532 Regs
.X
.CX
= (UINT16
)TpmBinaryImageSize
;
534 Private
->LegacyBios
.FarCall86 (
535 &Private
->LegacyBios
,
536 Table
->Compatibility16CallSegment
,
537 Table
->Compatibility16CallOffset
,
543 TpmPointer
= (UINT32
)(Regs
.X
.DS
* 16 + Regs
.X
.BX
);
544 if (Regs
.X
.AX
!= 0) {
545 DEBUG ((DEBUG_ERROR
, "TPM cannot be loaded\n"));
547 CopyMem ((VOID
*)(UINTN
)TpmPointer
, TpmBinaryImage
, TpmBinaryImageSize
);
548 Table
->TpmSegment
= Regs
.X
.DS
;
549 Table
->TpmOffset
= Regs
.X
.BX
;
554 // Lock the Legacy BIOS region
556 Private
->Cpu
->FlushDataCache (Private
->Cpu
, Private
->BiosStart
, (UINT32
)LegacyBiosImageSize
, EfiCpuFlushTypeWriteBackInvalidate
);
557 Private
->LegacyRegion
->Lock (Private
->LegacyRegion
, Private
->BiosStart
, (UINT32
)LegacyBiosImageSize
, &Granularity
);
560 // Get the BbsTable from LOW_MEMORY_THUNK
562 BbsTable
= (BBS_TABLE
*)(UINTN
)Private
->IntThunk
->BbsTable
;
563 ZeroMem ((VOID
*)BbsTable
, sizeof (Private
->IntThunk
->BbsTable
));
565 EfiToLegacy16BootTable
->BbsTable
= (UINT32
)(UINTN
)BbsTable
;
566 Private
->BbsTablePtr
= (VOID
*)BbsTable
;
569 // Populate entire table with BBS_IGNORE_ENTRY
571 EfiToLegacy16BootTable
->NumberBbsEntries
= MAX_BBS_ENTRIES
;
573 for (Index
= 0; Index
< MAX_BBS_ENTRIES
; Index
++) {
574 BbsTable
[Index
].BootPriority
= BBS_IGNORE_ENTRY
;
578 // Allocate space for Legacy HDD table
580 LegacyEfiHddTable
= (LEGACY_EFI_HDD_TABLE
*)AllocateZeroPool ((UINTN
)MAX_HDD_ENTRIES
* sizeof (LEGACY_EFI_HDD_TABLE
));
581 ASSERT (LegacyEfiHddTable
);
583 Private
->LegacyEfiHddTable
= LegacyEfiHddTable
;
584 Private
->LegacyEfiHddTableIndex
= 0x00;
588 // GetTimerValue (&Ticker);
590 // gRT->SetVariable (L"EndOfLoadFv",
591 // &gEfiGlobalVariableGuid,
592 // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
602 Shadow all legacy16 OPROMs that haven't been shadowed.
603 Warning: Use this with caution. This routine disconnects all EFI
604 drivers. If used externally then caller must re-connect EFI
607 @param This Protocol instance pointer.
609 @retval EFI_SUCCESS OPROMs shadowed
614 LegacyBiosShadowAllLegacyOproms (
615 IN EFI_LEGACY_BIOS_PROTOCOL
*This
618 LEGACY_BIOS_INSTANCE
*Private
;
621 // EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform;
622 // EFI_LEGACY16_TABLE *Legacy16Table;
624 Private
= LEGACY_BIOS_INSTANCE_FROM_THIS (This
);
627 // LegacyBiosPlatform = Private->LegacyBiosPlatform;
628 // Legacy16Table = Private->Legacy16Table;
630 // Shadow PCI ROMs. We must do this near the end since this will kick
631 // of Native EFI drivers that may be needed to collect info for Legacy16
633 // WARNING: PciIo is gone after this call.
635 PciProgramAllInterruptLineRegisters (Private
);
637 PciShadowRoms (Private
);
640 // Shadow PXE base code, BIS etc.
642 // LegacyBiosPlatform->ShadowServiceRoms (LegacyBiosPlatform,
643 // &Private->OptionRom,
650 Get the PCI BIOS interface version.
652 @param Private Driver private data.
654 @return The PCI interface version number in Binary Coded Decimal (BCD) format.
655 E.g.: 0x0210 indicates 2.10, 0x0300 indicates 3.00
659 GetPciInterfaceVersion (
660 IN LEGACY_BIOS_INSTANCE
*Private
663 EFI_IA32_REGISTER_SET Reg
;
665 UINT16 PciInterfaceVersion
;
667 PciInterfaceVersion
= 0;
672 ThunkFailed
= Private
->LegacyBios
.Int86 (&Private
->LegacyBios
, 0x1A, &Reg
);
675 // From PCI Firmware 3.0 Specification:
676 // If the CARRY FLAG [CF] is cleared and AH is set to 00h, it is still necessary to examine the
677 // contents of [EDX] for the presence of the string "PCI" + (trailing space) to fully validate the
678 // presence of the PCI function set. [BX] will further indicate the version level, with enough
679 // granularity to allow for incremental changes in the code that don't affect the function interface.
680 // Version numbers are stored as Binary Coded Decimal (BCD) values. For example, Version 2.10
681 // would be returned as a 02h in the [BH] registers and 10h in the [BL] registers.
683 if ((Reg
.X
.Flags
.CF
== 0) && (Reg
.H
.AH
== 0) && (Reg
.E
.EDX
== SIGNATURE_32 ('P', 'C', 'I', ' '))) {
684 PciInterfaceVersion
= Reg
.X
.BX
;
688 return PciInterfaceVersion
;
692 Callback function to calculate SMBIOS table size, and allocate memory for SMBIOS table.
693 SMBIOS table will be copied into EfiReservedMemoryType memory in legacy boot path.
695 @param Event Event whose notification function is being invoked.
696 @param Context The pointer to the notification function's context,
697 which is implementation-dependent.
702 InstallSmbiosEventCallback (
708 SMBIOS_TABLE_ENTRY_POINT
*EntryPointStructure
;
711 // Get SMBIOS table from EFI configuration table
713 Status
= EfiGetSystemConfigurationTable (
714 &gEfiSmbiosTableGuid
,
715 &mRuntimeSmbiosEntryPoint
717 if ((EFI_ERROR (Status
)) || (mRuntimeSmbiosEntryPoint
== NULL
)) {
721 EntryPointStructure
= (SMBIOS_TABLE_ENTRY_POINT
*)mRuntimeSmbiosEntryPoint
;
724 // Allocate memory for SMBIOS Entry Point Structure.
725 // CSM framework spec requires SMBIOS table below 4GB in EFI_TO_COMPATIBILITY16_BOOT_TABLE.
727 if (mReserveSmbiosEntryPoint
== 0) {
729 // Entrypoint structure with fixed size is allocated only once.
731 mReserveSmbiosEntryPoint
= SIZE_4GB
- 1;
732 Status
= gBS
->AllocatePages (
734 EfiReservedMemoryType
,
735 EFI_SIZE_TO_PAGES ((UINTN
)(EntryPointStructure
->EntryPointLength
)),
736 &mReserveSmbiosEntryPoint
738 if (EFI_ERROR (Status
)) {
739 mReserveSmbiosEntryPoint
= 0;
743 DEBUG ((DEBUG_INFO
, "Allocate memory for Smbios Entry Point Structure\n"));
746 if ((mStructureTableAddress
!= 0) &&
747 (mStructureTablePages
< EFI_SIZE_TO_PAGES ((UINT32
)EntryPointStructure
->TableLength
)))
750 // If original buffer is not enough for the new SMBIOS table, free original buffer and re-allocate
752 gBS
->FreePages (mStructureTableAddress
, mStructureTablePages
);
753 mStructureTableAddress
= 0;
754 mStructureTablePages
= 0;
755 DEBUG ((DEBUG_INFO
, "Original size is not enough. Re-allocate the memory.\n"));
758 if (mStructureTableAddress
== 0) {
760 // Allocate reserved memory below 4GB.
761 // Smbios spec requires the structure table is below 4GB.
763 mStructureTableAddress
= SIZE_4GB
- 1;
764 mStructureTablePages
= EFI_SIZE_TO_PAGES (EntryPointStructure
->TableLength
);
765 Status
= gBS
->AllocatePages (
767 EfiReservedMemoryType
,
768 mStructureTablePages
,
769 &mStructureTableAddress
771 if (EFI_ERROR (Status
)) {
773 mReserveSmbiosEntryPoint
,
774 EFI_SIZE_TO_PAGES ((UINTN
)(EntryPointStructure
->EntryPointLength
))
776 mReserveSmbiosEntryPoint
= 0;
777 mStructureTableAddress
= 0;
778 mStructureTablePages
= 0;
782 DEBUG ((DEBUG_INFO
, "Allocate memory for Smbios Structure Table\n"));
787 Callback function to toggle EndOfDxe status. NULL pointer detection needs
788 this status to decide if it's necessary to change attributes of page 0.
790 @param Event Event whose notification function is being invoked.
791 @param Context The pointer to the notification function's context,
792 which is implementation-dependent.
797 ToggleEndOfDxeStatus (
807 Install Driver to produce Legacy BIOS protocol.
809 @param ImageHandle Handle of driver image.
810 @param SystemTable Pointer to system table.
812 @retval EFI_SUCCESS Legacy BIOS protocol installed
813 @retval No protocol installed, unload driver.
819 IN EFI_HANDLE ImageHandle
,
820 IN EFI_SYSTEM_TABLE
*SystemTable
824 LEGACY_BIOS_INSTANCE
*Private
;
825 EFI_TO_COMPATIBILITY16_INIT_TABLE
*EfiToLegacy16InitTable
;
826 EFI_PHYSICAL_ADDRESS MemoryAddress
;
827 EFI_PHYSICAL_ADDRESS EbdaReservedBaseAddress
;
829 EFI_PHYSICAL_ADDRESS MemoryAddressUnder1MB
;
831 UINT32
*BaseVectorMaster
;
832 EFI_PHYSICAL_ADDRESS StartAddress
;
834 EFI_PHYSICAL_ADDRESS MemStart
;
839 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor
;
842 EFI_EVENT InstallSmbiosEvent
;
843 EFI_EVENT EndOfDxeEvent
;
846 // Load this driver's image to memory
848 Status
= RelocateImageUnder4GIfNeeded (ImageHandle
, SystemTable
);
849 if (EFI_ERROR (Status
)) {
854 // When UEFI Secure Boot is enabled, CSM module will not start any more.
857 GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME
, (VOID
**)&SecureBoot
, NULL
);
858 if ((SecureBoot
!= NULL
) && (*SecureBoot
== SECURE_BOOT_MODE_ENABLE
)) {
859 FreePool (SecureBoot
);
860 return EFI_SECURITY_VIOLATION
;
863 if (SecureBoot
!= NULL
) {
864 FreePool (SecureBoot
);
867 Private
= &mPrivateData
;
868 ZeroMem (Private
, sizeof (LEGACY_BIOS_INSTANCE
));
871 // Grab a copy of all the protocols we depend on. Any error would
872 // be a dispatcher bug!.
874 Status
= gBS
->LocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (VOID
**)&Private
->Cpu
);
875 ASSERT_EFI_ERROR (Status
);
877 Status
= gBS
->LocateProtocol (&gEfiTimerArchProtocolGuid
, NULL
, (VOID
**)&Private
->Timer
);
878 ASSERT_EFI_ERROR (Status
);
880 Status
= gBS
->LocateProtocol (&gEfiLegacyRegion2ProtocolGuid
, NULL
, (VOID
**)&Private
->LegacyRegion
);
881 ASSERT_EFI_ERROR (Status
);
883 Status
= gBS
->LocateProtocol (&gEfiLegacyBiosPlatformProtocolGuid
, NULL
, (VOID
**)&Private
->LegacyBiosPlatform
);
884 ASSERT_EFI_ERROR (Status
);
886 Status
= gBS
->LocateProtocol (&gEfiLegacy8259ProtocolGuid
, NULL
, (VOID
**)&Private
->Legacy8259
);
887 ASSERT_EFI_ERROR (Status
);
889 Status
= gBS
->LocateProtocol (&gEfiLegacyInterruptProtocolGuid
, NULL
, (VOID
**)&Private
->LegacyInterrupt
);
890 ASSERT_EFI_ERROR (Status
);
893 // Locate Memory Test Protocol if exists
895 Status
= gBS
->LocateProtocol (
896 &gEfiGenericMemTestProtocolGuid
,
898 (VOID
**)&Private
->GenericMemoryTest
900 ASSERT_EFI_ERROR (Status
);
903 // Make sure all memory from 0-640K is tested
905 for (StartAddress
= 0; StartAddress
< 0xa0000; ) {
906 gDS
->GetMemorySpaceDescriptor (StartAddress
, &Descriptor
);
907 if (Descriptor
.GcdMemoryType
!= EfiGcdMemoryTypeReserved
) {
908 StartAddress
= Descriptor
.BaseAddress
+ Descriptor
.Length
;
912 Length
= MIN (Descriptor
.Length
, 0xa0000 - StartAddress
);
913 Private
->GenericMemoryTest
->CompatibleRangeTest (
914 Private
->GenericMemoryTest
,
918 StartAddress
= StartAddress
+ Length
;
922 // Make sure all memory from 1MB to 16MB is tested and added to memory map
924 for (StartAddress
= BASE_1MB
; StartAddress
< BASE_16MB
; ) {
925 gDS
->GetMemorySpaceDescriptor (StartAddress
, &Descriptor
);
926 if (Descriptor
.GcdMemoryType
!= EfiGcdMemoryTypeReserved
) {
927 StartAddress
= Descriptor
.BaseAddress
+ Descriptor
.Length
;
931 Length
= MIN (Descriptor
.Length
, BASE_16MB
- StartAddress
);
932 Private
->GenericMemoryTest
->CompatibleRangeTest (
933 Private
->GenericMemoryTest
,
937 StartAddress
= StartAddress
+ Length
;
940 Private
->Signature
= LEGACY_BIOS_INSTANCE_SIGNATURE
;
942 Private
->LegacyBios
.Int86
= LegacyBiosInt86
;
943 Private
->LegacyBios
.FarCall86
= LegacyBiosFarCall86
;
944 Private
->LegacyBios
.CheckPciRom
= LegacyBiosCheckPciRom
;
945 Private
->LegacyBios
.InstallPciRom
= LegacyBiosInstallPciRom
;
946 Private
->LegacyBios
.LegacyBoot
= LegacyBiosLegacyBoot
;
947 Private
->LegacyBios
.UpdateKeyboardLedStatus
= LegacyBiosUpdateKeyboardLedStatus
;
948 Private
->LegacyBios
.GetBbsInfo
= LegacyBiosGetBbsInfo
;
949 Private
->LegacyBios
.ShadowAllLegacyOproms
= LegacyBiosShadowAllLegacyOproms
;
950 Private
->LegacyBios
.PrepareToBootEfi
= LegacyBiosPrepareToBootEfi
;
951 Private
->LegacyBios
.GetLegacyRegion
= LegacyBiosGetLegacyRegion
;
952 Private
->LegacyBios
.CopyLegacyRegion
= LegacyBiosCopyLegacyRegion
;
953 Private
->LegacyBios
.BootUnconventionalDevice
= LegacyBiosBootUnconventionalDevice
;
955 Private
->ImageHandle
= ImageHandle
;
958 // Enable read attribute of legacy region.
961 Private
->LegacyRegion
->Decode (
962 Private
->LegacyRegion
,
969 // Set Cachebility for legacy region
970 // BUGBUG: Comments about this legacy region cacheability setting
971 // This setting will make D865GCHProduction CSM Unhappy
973 if (PcdGetBool (PcdLegacyBiosCacheLegacyRegion
)) {
974 gDS
->SetMemorySpaceAttributes (
979 gDS
->SetMemorySpaceAttributes (
986 gDS
->SetMemorySpaceAttributes (
993 // Allocate 0 - 4K for real mode interrupt vectors and BDA.
995 AllocateLegacyMemory (
997 EfiReservedMemoryType
,
1002 ASSERT (MemoryAddress
== 0x000000000);
1004 ClearPtr
= (VOID
*)((UINTN
)0x0000);
1007 // Initialize region from 0x0000 to 4k. This initializes interrupt vector
1011 gBS
->SetMem ((VOID
*)ClearPtr
, 0x400, INITIAL_VALUE_BELOW_1K
);
1012 ZeroMem ((VOID
*)((UINTN
)ClearPtr
+ 0x400), 0xC00);
1016 // Allocate pages for OPROM usage
1018 MemorySize
= PcdGet32 (PcdEbdaReservedMemorySize
);
1019 ASSERT ((MemorySize
& 0xFFF) == 0);
1021 Status
= AllocateLegacyMemory (
1023 EfiReservedMemoryType
,
1024 CONVENTIONAL_MEMORY_TOP
- MemorySize
,
1025 EFI_SIZE_TO_PAGES (MemorySize
),
1028 ASSERT_EFI_ERROR (Status
);
1030 ZeroMem ((VOID
*)((UINTN
)MemoryAddress
), MemorySize
);
1033 // Allocate all 32k chunks from 0x60000 ~ 0x88000 for Legacy OPROMs that
1034 // don't use PMM but look for zeroed memory. Note that various non-BBS
1035 // OpROMs expect different areas to be free
1037 EbdaReservedBaseAddress
= MemoryAddress
;
1038 MemoryAddress
= PcdGet32 (PcdOpromReservedMemoryBase
);
1039 MemorySize
= PcdGet32 (PcdOpromReservedMemorySize
);
1041 // Check if base address and size for reserved memory are 4KB aligned.
1043 ASSERT ((MemoryAddress
& 0xFFF) == 0);
1044 ASSERT ((MemorySize
& 0xFFF) == 0);
1046 // Check if the reserved memory is below EBDA reserved range.
1048 ASSERT ((MemoryAddress
< EbdaReservedBaseAddress
) && ((MemoryAddress
+ MemorySize
- 1) < EbdaReservedBaseAddress
));
1049 for (MemStart
= MemoryAddress
; MemStart
< MemoryAddress
+ MemorySize
; MemStart
+= 0x1000) {
1050 Status
= AllocateLegacyMemory (
1052 EfiBootServicesCode
,
1057 if (!EFI_ERROR (Status
)) {
1058 MemoryPtr
= (VOID
*)((UINTN
)StartAddress
);
1059 ZeroMem (MemoryPtr
, 0x1000);
1061 DEBUG ((DEBUG_ERROR
, "WARNING: Allocate legacy memory fail for SCSI card - %x\n", MemStart
));
1066 // Allocate low PMM memory and zero it out
1068 MemorySize
= PcdGet32 (PcdLowPmmMemorySize
);
1069 ASSERT ((MemorySize
& 0xFFF) == 0);
1070 Status
= AllocateLegacyMemory (
1072 EfiBootServicesCode
,
1073 CONVENTIONAL_MEMORY_TOP
,
1074 EFI_SIZE_TO_PAGES (MemorySize
),
1075 &MemoryAddressUnder1MB
1077 ASSERT_EFI_ERROR (Status
);
1079 ZeroMem ((VOID
*)((UINTN
)MemoryAddressUnder1MB
), MemorySize
);
1082 // Allocate space for thunker and Init Thunker
1084 Status
= AllocateLegacyMemory (
1086 EfiReservedMemoryType
,
1087 CONVENTIONAL_MEMORY_TOP
,
1088 (sizeof (LOW_MEMORY_THUNK
) / EFI_PAGE_SIZE
) + 2,
1091 ASSERT_EFI_ERROR (Status
);
1092 Private
->IntThunk
= (LOW_MEMORY_THUNK
*)(UINTN
)MemoryAddress
;
1093 EfiToLegacy16InitTable
= &Private
->IntThunk
->EfiToLegacy16InitTable
;
1094 EfiToLegacy16InitTable
->ThunkStart
= (UINT32
)(EFI_PHYSICAL_ADDRESS
)(UINTN
)MemoryAddress
;
1095 EfiToLegacy16InitTable
->ThunkSizeInBytes
= (UINT32
)(sizeof (LOW_MEMORY_THUNK
));
1097 Status
= LegacyBiosInitializeThunk (Private
);
1098 ASSERT_EFI_ERROR (Status
);
1101 // Init the legacy memory map in memory < 1 MB.
1103 EfiToLegacy16InitTable
->BiosLessThan1MB
= (UINT32
)MemoryAddressUnder1MB
;
1104 EfiToLegacy16InitTable
->LowPmmMemory
= (UINT32
)MemoryAddressUnder1MB
;
1105 EfiToLegacy16InitTable
->LowPmmMemorySizeInBytes
= MemorySize
;
1107 MemorySize
= PcdGet32 (PcdHighPmmMemorySize
);
1108 ASSERT ((MemorySize
& 0xFFF) == 0);
1110 // Allocate high PMM Memory under 16 MB
1112 Status
= AllocateLegacyMemory (
1114 EfiBootServicesCode
,
1116 EFI_SIZE_TO_PAGES (MemorySize
),
1119 if (EFI_ERROR (Status
)) {
1121 // If it fails, allocate high PMM Memory under 4GB
1123 Status
= AllocateLegacyMemory (
1125 EfiBootServicesCode
,
1127 EFI_SIZE_TO_PAGES (MemorySize
),
1132 if (!EFI_ERROR (Status
)) {
1133 EfiToLegacy16InitTable
->HiPmmMemory
= (UINT32
)(EFI_PHYSICAL_ADDRESS
)(UINTN
)MemoryAddress
;
1134 EfiToLegacy16InitTable
->HiPmmMemorySizeInBytes
= MemorySize
;
1140 // Start the Legacy BIOS;
1142 Status
= ShadowAndStartLegacy16 (Private
);
1143 if (EFI_ERROR (Status
)) {
1148 // Initialize interrupt redirection code and entries;
1149 // IDT Vectors 0x68-0x6f must be redirected to IDT Vectors 0x08-0x0f.
1152 Private
->IntThunk
->InterruptRedirectionCode
,
1153 (VOID
*)(UINTN
)InterruptRedirectionTemplate
,
1154 sizeof (Private
->IntThunk
->InterruptRedirectionCode
)
1158 // Save Unexpected interrupt vector so can restore it just prior to boot
1161 BaseVectorMaster
= (UINT32
*)(sizeof (UINT32
) * PROTECTED_MODE_BASE_VECTOR_MASTER
);
1162 Private
->BiosUnexpectedInt
= BaseVectorMaster
[0];
1163 IntRedirCode
= (UINT32
)(UINTN
)Private
->IntThunk
->InterruptRedirectionCode
;
1164 for (Index
= 0; Index
< 8; Index
++) {
1165 BaseVectorMaster
[Index
] = (EFI_SEGMENT (IntRedirCode
+ Index
* 4) << 16) | EFI_OFFSET (IntRedirCode
+ Index
* 4);
1173 Private
->ThunkSeg
= (UINT16
)(EFI_SEGMENT (IntRedirCode
));
1176 // Allocate reserved memory for SMBIOS table used in legacy boot if SMBIOS table exists
1178 InstallSmbiosEventCallback (NULL
, NULL
);
1181 // Create callback function to update the size of reserved memory after LegacyBiosDxe starts
1183 Status
= gBS
->CreateEventEx (
1186 InstallSmbiosEventCallback
,
1188 &gEfiSmbiosTableGuid
,
1191 ASSERT_EFI_ERROR (Status
);
1194 // Create callback to update status of EndOfDxe, which is needed by NULL
1195 // pointer detection
1197 Status
= gBS
->CreateEventEx (
1200 ToggleEndOfDxeStatus
,
1202 &gEfiEndOfDxeEventGroupGuid
,
1205 ASSERT_EFI_ERROR (Status
);
1208 // Make a new handle and install the protocol
1210 Private
->Handle
= NULL
;
1211 Status
= gBS
->InstallProtocolInterface (
1213 &gEfiLegacyBiosProtocolGuid
,
1214 EFI_NATIVE_INTERFACE
,
1215 &Private
->LegacyBios
1217 Private
->Csm16PciInterfaceVersion
= GetPciInterfaceVersion (Private
);
1221 "CSM16 PCI BIOS Interface Version: %02x.%02x\n",
1222 (UINT8
)(Private
->Csm16PciInterfaceVersion
>> 8),
1223 (UINT8
)Private
->Csm16PciInterfaceVersion
1225 ASSERT (Private
->Csm16PciInterfaceVersion
!= 0);