/** @file
Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions
of the BSD License which accompanies this distribution. The
full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "LegacyBiosInterface.h"
#include
#define BOOT_LEGACY_OS 0
#define BOOT_EFI_OS 1
#define BOOT_UNCONVENTIONAL_DEVICE 2
UINT32 mLoadOptionsSize = 0;
UINTN mBootMode = BOOT_LEGACY_OS;
VOID *mLoadOptions = NULL;
BBS_BBS_DEVICE_PATH *mBbsDevicePathPtr = NULL;
BBS_BBS_DEVICE_PATH mBbsDevicePathNode;
UDC_ATTRIBUTES mAttributes = { 0, 0, 0, 0 };
UINTN mBbsEntry = 0;
VOID *mBeerData = NULL;
VOID *mServiceAreaData = NULL;
UINT64 mLowWater = 0xffffffffffffffffULL;
extern BBS_TABLE *mBbsTable;
/**
Print the BBS Table.
@param BbsTable The BBS table.
**/
VOID
PrintBbsTable (
IN BBS_TABLE *BbsTable
)
{
UINT16 Index;
UINT16 SubIndex;
CHAR8 *String;
DEBUG ((EFI_D_INFO, "\n"));
DEBUG ((EFI_D_INFO, " NO Prio bb/dd/ff cl/sc Type Stat segm:offs mfgs:mfgo dess:deso\n"));
DEBUG ((EFI_D_INFO, "=================================================================\n"));
for (Index = 0; Index < MAX_BBS_ENTRIES; Index++) {
//
// Filter
//
if (BbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) {
continue;
}
DEBUG ((
EFI_D_INFO,
" %02x: %04x %02x/%02x/%02x %02x/%02x %04x %04x",
(UINTN) Index,
(UINTN) BbsTable[Index].BootPriority,
(UINTN) BbsTable[Index].Bus,
(UINTN) BbsTable[Index].Device,
(UINTN) BbsTable[Index].Function,
(UINTN) BbsTable[Index].Class,
(UINTN) BbsTable[Index].SubClass,
(UINTN) BbsTable[Index].DeviceType,
(UINTN) * (UINT16 *) &BbsTable[Index].StatusFlags
));
DEBUG ((
EFI_D_INFO,
" %04x:%04x %04x:%04x %04x:%04x",
(UINTN) BbsTable[Index].BootHandlerSegment,
(UINTN) BbsTable[Index].BootHandlerOffset,
(UINTN) BbsTable[Index].MfgStringSegment,
(UINTN) BbsTable[Index].MfgStringOffset,
(UINTN) BbsTable[Index].DescStringSegment,
(UINTN) BbsTable[Index].DescStringOffset
));
//
// Print DescString
//
String = (CHAR8 *)(UINTN)((BbsTable[Index].DescStringSegment << 4) + BbsTable[Index].DescStringOffset);
if (String != NULL) {
DEBUG ((EFI_D_INFO," ("));
for (SubIndex = 0; String[SubIndex] != 0; SubIndex++) {
DEBUG ((EFI_D_INFO, "%c", String[SubIndex]));
}
DEBUG ((EFI_D_INFO,")"));
}
DEBUG ((EFI_D_INFO,"\n"));
}
DEBUG ((EFI_D_INFO, "\n"));
return ;
}
/**
Print the BBS Table.
@param HddInfo The HddInfo table.
**/
VOID
PrintHddInfo (
IN HDD_INFO *HddInfo
)
{
UINTN Index;
DEBUG ((EFI_D_INFO, "\n"));
for (Index = 0; Index < MAX_IDE_CONTROLLER; Index++) {
DEBUG ((EFI_D_INFO, "Index - %04x\n", Index));
DEBUG ((EFI_D_INFO, " Status - %04x\n", (UINTN)HddInfo[Index].Status));
DEBUG ((EFI_D_INFO, " B/D/F - %02x/%02x/%02x\n", (UINTN)HddInfo[Index].Bus, (UINTN)HddInfo[Index].Device, (UINTN)HddInfo[Index].Function));
DEBUG ((EFI_D_INFO, " Command - %04x\n", HddInfo[Index].CommandBaseAddress));
DEBUG ((EFI_D_INFO, " Control - %04x\n", HddInfo[Index].ControlBaseAddress));
DEBUG ((EFI_D_INFO, " BusMaster - %04x\n", HddInfo[Index].BusMasterAddress));
DEBUG ((EFI_D_INFO, " HddIrq - %02x\n", HddInfo[Index].HddIrq));
DEBUG ((EFI_D_INFO, " IdentifyDrive[0].Raw[0] - %x\n", HddInfo[Index].IdentifyDrive[0].Raw[0]));
DEBUG ((EFI_D_INFO, " IdentifyDrive[1].Raw[0] - %x\n", HddInfo[Index].IdentifyDrive[1].Raw[0]));
}
DEBUG ((EFI_D_INFO, "\n"));
return ;
}
/**
Print the PCI Interrupt Line and Interrupt Pin registers.
**/
VOID
PrintPciInterruptRegister (
VOID
)
{
EFI_STATUS Status;
UINTN Index;
EFI_HANDLE *Handles;
UINTN HandleNum;
EFI_PCI_IO_PROTOCOL *PciIo;
UINT8 Interrupt[2];
UINTN Segment;
UINTN Bus;
UINTN Device;
UINTN Function;
gBS->LocateHandleBuffer (
ByProtocol,
&gEfiPciIoProtocolGuid,
NULL,
&HandleNum,
&Handles
);
Bus = 0;
Device = 0;
Function = 0;
DEBUG ((EFI_D_INFO, "\n"));
DEBUG ((EFI_D_INFO, " bb/dd/ff interrupt line interrupt pin\n"));
DEBUG ((EFI_D_INFO, "======================================\n"));
for (Index = 0; Index < HandleNum; Index++) {
Status = gBS->HandleProtocol (Handles[Index], &gEfiPciIoProtocolGuid, (VOID **) &PciIo);
if (!EFI_ERROR (Status)) {
Status = PciIo->Pci.Read (
PciIo,
EfiPciIoWidthUint8,
PCI_INT_LINE_OFFSET,
2,
Interrupt
);
}
if (!EFI_ERROR (Status)) {
Status = PciIo->GetLocation (
PciIo,
&Segment,
&Bus,
&Device,
&Function
);
}
if (!EFI_ERROR (Status)) {
DEBUG ((EFI_D_INFO, " %02x/%02x/%02x 0x%02x 0x%02x\n",
Bus, Device, Function, Interrupt[0], Interrupt[1]));
}
}
DEBUG ((EFI_D_INFO, "\n"));
if (Handles != NULL) {
FreePool (Handles);
}
}
/**
Identify drive data must be updated to actual parameters before boot.
@param IdentifyDriveData ATA Identify Data
**/
VOID
UpdateIdentifyDriveData (
IN UINT8 *IdentifyDriveData
);
/**
Update SIO data.
@param Private Legacy BIOS Instance data
@retval EFI_SUCCESS Removable media not present
**/
EFI_STATUS
UpdateSioData (
IN LEGACY_BIOS_INSTANCE *Private
)
{
EFI_STATUS Status;
UINTN Index;
UINTN Index1;
UINT8 LegacyInterrupts[16];
EFI_LEGACY_IRQ_ROUTING_ENTRY *RoutingTable;
UINTN RoutingTableEntries;
EFI_LEGACY_IRQ_PRIORITY_TABLE_ENTRY *IrqPriorityTable;
UINTN NumberPriorityEntries;
EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable;
UINT8 HddIrq;
UINT16 LegacyInt;
UINT16 LegMask;
UINT32 Register;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
EFI_ISA_IO_PROTOCOL *IsaIo;
LegacyInt = 0;
HandleBuffer = NULL;
EfiToLegacy16BootTable = &Private->IntThunk->EfiToLegacy16BootTable;
LegacyBiosBuildSioData (Private);
SetMem (LegacyInterrupts, sizeof (LegacyInterrupts), 0);
//
// Create list of legacy interrupts.
//
for (Index = 0; Index < 4; Index++) {
LegacyInterrupts[Index] = EfiToLegacy16BootTable->SioData.Serial[Index].Irq;
}
for (Index = 4; Index < 7; Index++) {
LegacyInterrupts[Index] = EfiToLegacy16BootTable->SioData.Parallel[Index - 4].Irq;
}
LegacyInterrupts[7] = EfiToLegacy16BootTable->SioData.Floppy.Irq;
//
// Get Legacy Hdd IRQs. If native mode treat as PCI
//
for (Index = 0; Index < 2; Index++) {
HddIrq = EfiToLegacy16BootTable->HddInfo[Index].HddIrq;
if ((HddIrq != 0) && ((HddIrq == 15) || (HddIrq == 14))) {
LegacyInterrupts[Index + 8] = HddIrq;
}
}
Private->LegacyBiosPlatform->GetRoutingTable (
Private->LegacyBiosPlatform,
(VOID *) &RoutingTable,
&RoutingTableEntries,
NULL,
NULL,
(VOID **) &IrqPriorityTable,
&NumberPriorityEntries
);
//
// Remove legacy interrupts from the list of PCI interrupts available.
//
for (Index = 0; Index <= 0x0b; Index++) {
for (Index1 = 0; Index1 <= NumberPriorityEntries; Index1++) {
if (LegacyInterrupts[Index] != 0) {
LegacyInt = (UINT16) (LegacyInt | (1 << LegacyInterrupts[Index]));
if (LegacyInterrupts[Index] == IrqPriorityTable[Index1].Irq) {
IrqPriorityTable[Index1].Used = LEGACY_USED;
}
}
}
}
Private->Legacy8259->GetMask (
Private->Legacy8259,
&LegMask,
NULL,
NULL,
NULL
);
//
// Set SIO interrupts and disable mouse. Let mouse driver
// re-enable it.
//
LegMask = (UINT16) ((LegMask &~LegacyInt) | 0x1000);
Private->Legacy8259->SetMask (
Private->Legacy8259,
&LegMask,
NULL,
NULL,
NULL
);
//
// Disable mouse in keyboard controller
//
Register = 0xA7;
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiIsaIoProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
for (Index = 0; Index < HandleCount; Index++) {
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiIsaIoProtocolGuid,
(VOID **) &IsaIo
);
ASSERT_EFI_ERROR (Status);
IsaIo->Io.Write (IsaIo, EfiIsaIoWidthUint8, 0x64, 1, &Register);
}
if (HandleBuffer != NULL) {
FreePool (HandleBuffer);
}
return EFI_SUCCESS;
}
/**
Identify drive data must be updated to actual parameters before boot.
This requires updating the checksum, if it exists.
@param IdentifyDriveData ATA Identify Data
@param Checksum checksum of the ATA Identify Data
@retval EFI_SUCCESS checksum calculated
@retval EFI_SECURITY_VIOLATION IdentifyData invalid
**/
EFI_STATUS
CalculateIdentifyDriveChecksum (
IN UINT8 *IdentifyDriveData,
OUT UINT8 *Checksum
)
{
UINTN Index;
UINT8 LocalChecksum;
LocalChecksum = 0;
*Checksum = 0;
if (IdentifyDriveData[510] != 0xA5) {
return EFI_SECURITY_VIOLATION;
}
for (Index = 0; Index < 512; Index++) {
LocalChecksum = (UINT8) (LocalChecksum + IdentifyDriveData[Index]);
}
*Checksum = LocalChecksum;
return EFI_SUCCESS;
}
/**
Identify drive data must be updated to actual parameters before boot.
@param IdentifyDriveData ATA Identify Data
**/
VOID
UpdateIdentifyDriveData (
IN UINT8 *IdentifyDriveData
)
{
UINT16 NumberCylinders;
UINT16 NumberHeads;
UINT16 NumberSectorsTrack;
UINT32 CapacityInSectors;
UINT8 OriginalChecksum;
UINT8 FinalChecksum;
EFI_STATUS Status;
ATAPI_IDENTIFY *ReadInfo;
//
// Status indicates if Integrity byte is correct. Checksum should be
// 0 if valid.
//
ReadInfo = (ATAPI_IDENTIFY *) IdentifyDriveData;
Status = CalculateIdentifyDriveChecksum (IdentifyDriveData, &OriginalChecksum);
if (OriginalChecksum != 0) {
Status = EFI_SECURITY_VIOLATION;
}
//
// If NumberCylinders = 0 then do data(Controller present but don drive attached).
//
NumberCylinders = ReadInfo->Raw[1];
if (NumberCylinders != 0) {
ReadInfo->Raw[54] = NumberCylinders;
NumberHeads = ReadInfo->Raw[3];
ReadInfo->Raw[55] = NumberHeads;
NumberSectorsTrack = ReadInfo->Raw[6];
ReadInfo->Raw[56] = NumberSectorsTrack;
//
// Copy Multisector info and set valid bit.
//
ReadInfo->Raw[59] = (UINT16) (ReadInfo->Raw[47] + 0x100);
CapacityInSectors = (UINT32) ((UINT32) (NumberCylinders) * (UINT32) (NumberHeads) * (UINT32) (NumberSectorsTrack));
ReadInfo->Raw[57] = (UINT16) (CapacityInSectors >> 16);
ReadInfo->Raw[58] = (UINT16) (CapacityInSectors & 0xffff);
if (Status == EFI_SUCCESS) {
//
// Forece checksum byte to 0 and get new checksum.
//
ReadInfo->Raw[255] &= 0xff;
CalculateIdentifyDriveChecksum (IdentifyDriveData, &FinalChecksum);
//
// Force new checksum such that sum is 0.
//
FinalChecksum = (UINT8) ((UINT8)0 - FinalChecksum);
ReadInfo->Raw[255] = (UINT16) (ReadInfo->Raw[255] | (FinalChecksum << 8));
}
}
}
/**
Identify drive data must be updated to actual parameters before boot.
Do for all drives.
@param Private Legacy BIOS Instance data
**/
VOID
UpdateAllIdentifyDriveData (
IN LEGACY_BIOS_INSTANCE *Private
)
{
UINTN Index;
HDD_INFO *HddInfo;
HddInfo = &Private->IntThunk->EfiToLegacy16BootTable.HddInfo[0];
for (Index = 0; Index < MAX_IDE_CONTROLLER; Index++) {
//
// Each controller can have 2 devices. Update for each device
//
if ((HddInfo[Index].Status & HDD_MASTER_IDE) != 0) {
UpdateIdentifyDriveData ((UINT8 *) (&HddInfo[Index].IdentifyDrive[0].Raw[0]));
}
if ((HddInfo[Index].Status & HDD_SLAVE_IDE) != 0) {
UpdateIdentifyDriveData ((UINT8 *) (&HddInfo[Index].IdentifyDrive[1].Raw[0]));
}
}
}
/**
Enable ide controller. This gets disabled when LegacyBoot.c is about
to run the Option ROMs.
@param Private Legacy BIOS Instance data
**/
VOID
EnableIdeController (
IN LEGACY_BIOS_INSTANCE *Private
)
{
EFI_PCI_IO_PROTOCOL *PciIo;
EFI_STATUS Status;
EFI_HANDLE IdeController;
UINT8 ByteBuffer;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
Status = Private->LegacyBiosPlatform->GetPlatformHandle (
Private->LegacyBiosPlatform,
EfiGetPlatformIdeHandle,
0,
&HandleBuffer,
&HandleCount,
NULL
);
if (!EFI_ERROR (Status)) {
IdeController = HandleBuffer[0];
Status = gBS->HandleProtocol (
IdeController,
&gEfiPciIoProtocolGuid,
(VOID **) &PciIo
);
ByteBuffer = 0x1f;
if (!EFI_ERROR (Status)) {
PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x04, 1, &ByteBuffer);
}
}
}
/**
Enable ide controller. This gets disabled when LegacyBoot.c is about
to run the Option ROMs.
@param Private Legacy BIOS Instance data
**/
VOID
EnableAllControllers (
IN LEGACY_BIOS_INSTANCE *Private
)
{
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
UINTN Index;
EFI_PCI_IO_PROTOCOL *PciIo;
PCI_TYPE01 PciConfigHeader;
EFI_STATUS Status;
//
//
//
EnableIdeController (Private);
//
// Assumption is table is built from low bus to high bus numbers.
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiPciIoProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
ASSERT_EFI_ERROR (Status);
for (Index = 0; Index < HandleCount; Index++) {
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiPciIoProtocolGuid,
(VOID **) &PciIo
);
ASSERT_EFI_ERROR (Status);
PciIo->Pci.Read (
PciIo,
EfiPciIoWidthUint32,
0,
sizeof (PciConfigHeader) / sizeof (UINT32),
&PciConfigHeader
);
//
// We do not enable PPB here. This is for HotPlug Consideration.
// The Platform HotPlug Driver is responsible for Padding enough hot plug
// resources. It is also responsible for enable this bridge. If it
// does not pad it. It will cause some early Windows fail to installation.
// If the platform driver does not pad resource for PPB, PPB should be in
// un-enabled state to let Windows know that this PPB is not configured by
// BIOS. So Windows will allocate default resource for PPB.
//
// The reason for why we enable the command register is:
// The CSM will use the IO bar to detect some IRQ status, if the command
// is disabled, the IO resource will be out of scope.
// For example:
// We installed a legacy IRQ handle for a PCI IDE controller. When IRQ
// comes up, the handle will check the IO space to identify is the
// controller generated the IRQ source.
// If the IO command is not enabled, the IRQ handler will has wrong
// information. It will cause IRQ storm when the correctly IRQ handler fails
// to run.
//
if (!(IS_PCI_VGA (&PciConfigHeader) ||
IS_PCI_OLD_VGA (&PciConfigHeader) ||
IS_PCI_IDE (&PciConfigHeader) ||
IS_PCI_P2P (&PciConfigHeader) ||
IS_PCI_P2P_SUB (&PciConfigHeader) ||
IS_PCI_LPC (&PciConfigHeader) )) {
PciConfigHeader.Hdr.Command |= 0x1f;
PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 4, 1, &PciConfigHeader.Hdr.Command);
}
}
}
/**
The following routines are identical in operation, so combine
for code compaction:
EfiGetPlatformBinaryGetMpTable
EfiGetPlatformBinaryGetOemIntData
EfiGetPlatformBinaryGetOem32Data
EfiGetPlatformBinaryGetOem16Data
@param This Protocol instance pointer.
@param Id Table/Data identifier
@retval EFI_SUCCESS Success
@retval EFI_INVALID_PARAMETER Invalid ID
@retval EFI_OUT_OF_RESOURCES no resource to get data or table
**/
EFI_STATUS
LegacyGetDataOrTable (
IN EFI_LEGACY_BIOS_PROTOCOL *This,
IN EFI_GET_PLATFORM_INFO_MODE Id
)
{
VOID *Table;
UINT32 TablePtr;
UINTN TableSize;
UINTN Alignment;
UINTN Location;
EFI_STATUS Status;
EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform;
EFI_COMPATIBILITY16_TABLE *Legacy16Table;
EFI_IA32_REGISTER_SET Regs;
LEGACY_BIOS_INSTANCE *Private;
Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
LegacyBiosPlatform = Private->LegacyBiosPlatform;
Legacy16Table = Private->Legacy16Table;
//
// Phase 1 - get an address allocated in 16-bit code
//
while (TRUE) {
switch (Id) {
case EfiGetPlatformBinaryMpTable:
case EfiGetPlatformBinaryOemIntData:
case EfiGetPlatformBinaryOem32Data:
case EfiGetPlatformBinaryOem16Data:
{
Status = LegacyBiosPlatform->GetPlatformInfo (
LegacyBiosPlatform,
Id,
(VOID *) &Table,
&TableSize,
&Location,
&Alignment,
0,
0
);
DEBUG ((EFI_D_INFO, "LegacyGetDataOrTable - ID: %x, %r\n", (UINTN)Id, Status));
DEBUG ((EFI_D_INFO, " Table - %x, Size - %x, Location - %x, Alignment - %x\n", (UINTN)Table, (UINTN)TableSize, (UINTN)Location, (UINTN)Alignment));
break;
}
default:
{
return EFI_INVALID_PARAMETER;
}
}
if (EFI_ERROR (Status)) {
return Status;
}
ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
Regs.X.AX = Legacy16GetTableAddress;
Regs.X.CX = (UINT16) TableSize;
Regs.X.BX = (UINT16) Location;
Regs.X.DX = (UINT16) Alignment;
Private->LegacyBios.FarCall86 (
This,
Private->Legacy16CallSegment,
Private->Legacy16CallOffset,
&Regs,
NULL,
0
);
if (Regs.X.AX != 0) {
DEBUG ((EFI_D_ERROR, "Table ID %x length insufficient\n", Id));
return EFI_OUT_OF_RESOURCES;
} else {
break;
}
}
//
// Phase 2 Call routine second time with address to allow address adjustment
//
Status = LegacyBiosPlatform->GetPlatformInfo (
LegacyBiosPlatform,
Id,
(VOID *) &Table,
&TableSize,
&Location,
&Alignment,
Regs.X.DS,
Regs.X.BX
);
switch (Id) {
case EfiGetPlatformBinaryMpTable:
{
Legacy16Table->MpTablePtr = (UINT32) (Regs.X.DS * 16 + Regs.X.BX);
Legacy16Table->MpTableLength = (UINT32)TableSize;
DEBUG ((EFI_D_INFO, "MP table in legacy region - %x\n", (UINTN)Legacy16Table->MpTablePtr));
break;
}
case EfiGetPlatformBinaryOemIntData:
{
Legacy16Table->OemIntSegment = Regs.X.DS;
Legacy16Table->OemIntOffset = Regs.X.BX;
DEBUG ((EFI_D_INFO, "OemInt table in legacy region - %04x:%04x\n", (UINTN)Legacy16Table->OemIntSegment, (UINTN)Legacy16Table->OemIntOffset));
break;
}
case EfiGetPlatformBinaryOem32Data:
{
Legacy16Table->Oem32Segment = Regs.X.DS;
Legacy16Table->Oem32Offset = Regs.X.BX;
DEBUG ((EFI_D_INFO, "Oem32 table in legacy region - %04x:%04x\n", (UINTN)Legacy16Table->Oem32Segment, (UINTN)Legacy16Table->Oem32Offset));
break;
}
case EfiGetPlatformBinaryOem16Data:
{
//
// Legacy16Table->Oem16Segment = Regs.X.DS;
// Legacy16Table->Oem16Offset = Regs.X.BX;
DEBUG ((EFI_D_INFO, "Oem16 table in legacy region - %04x:%04x\n", (UINTN)Legacy16Table->Oem16Segment, (UINTN)Legacy16Table->Oem16Offset));
break;
}
default:
{
return EFI_INVALID_PARAMETER;
}
}
if (EFI_ERROR (Status)) {
return Status;
}
//
// Phase 3 Copy table to final location
//
TablePtr = (UINT32) (Regs.X.DS * 16 + Regs.X.BX);
CopyMem (
(VOID *) (UINTN)TablePtr,
Table,
TableSize
);
return EFI_SUCCESS;
}
/**
Assign drive number to legacy HDD drives prior to booting an EFI
aware OS so the OS can access drives without an EFI driver.
Note: BBS compliant drives ARE NOT available until this call by
either shell or EFI.
@param This Protocol instance pointer.
@retval EFI_SUCCESS Drive numbers assigned
**/
EFI_STATUS
GenericLegacyBoot (
IN EFI_LEGACY_BIOS_PROTOCOL *This
)
{
EFI_STATUS Status;
LEGACY_BIOS_INSTANCE *Private;
EFI_IA32_REGISTER_SET Regs;
EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable;
EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform;
UINTN CopySize;
VOID *AcpiPtr;
HDD_INFO *HddInfo;
HDD_INFO *LocalHddInfo;
UINTN Index;
EFI_COMPATIBILITY16_TABLE *Legacy16Table;
UINT32 *BdaPtr;
UINT16 HddCount;
UINT16 BbsCount;
BBS_TABLE *LocalBbsTable;
UINT32 *BaseVectorMaster;
EFI_TIME BootTime;
UINT32 LocalTime;
EFI_HANDLE IdeController;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
VOID *SmbiosTable;
VOID *AcpiTable;
UINTN ShadowAddress;
UINT32 Granularity;
LocalHddInfo = NULL;
HddCount = 0;
BbsCount = 0;
LocalBbsTable = NULL;
Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
DEBUG_CODE (
DEBUG ((EFI_D_ERROR, "Start of legacy boot\n"));
);
Legacy16Table = Private->Legacy16Table;
EfiToLegacy16BootTable = &Private->IntThunk->EfiToLegacy16BootTable;
HddInfo = &EfiToLegacy16BootTable->HddInfo[0];
LegacyBiosPlatform = Private->LegacyBiosPlatform;
EfiToLegacy16BootTable->MajorVersion = EFI_TO_LEGACY_MAJOR_VERSION;
EfiToLegacy16BootTable->MinorVersion = EFI_TO_LEGACY_MINOR_VERSION;
//
// If booting to a legacy OS then force HDD drives to the appropriate
// boot mode by calling GetIdeHandle.
// A reconnect -r can force all HDDs back to native mode.
//
IdeController = NULL;
if ((mBootMode == BOOT_LEGACY_OS) || (mBootMode == BOOT_UNCONVENTIONAL_DEVICE)) {
Status = LegacyBiosPlatform->GetPlatformHandle (
Private->LegacyBiosPlatform,
EfiGetPlatformIdeHandle,
0,
&HandleBuffer,
&HandleCount,
NULL
);
if (!EFI_ERROR (Status)) {
IdeController = HandleBuffer[0];
}
}
//
// Unlock the Legacy BIOS region
//
Private->LegacyRegion->UnLock (
Private->LegacyRegion,
0xE0000,
0x20000,
&Granularity
);
//
// Reconstruct the Legacy16 boot memory map
//
LegacyBiosBuildE820 (Private, &CopySize);
if (CopySize > Private->Legacy16Table->E820Length) {
ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
Regs.X.AX = Legacy16GetTableAddress;
Regs.X.CX = (UINT16) CopySize;
Private->LegacyBios.FarCall86 (
&Private->LegacyBios,
Private->Legacy16Table->Compatibility16CallSegment,
Private->Legacy16Table->Compatibility16CallOffset,
&Regs,
NULL,
0
);
Private->Legacy16Table->E820Pointer = (UINT32) (Regs.X.DS * 16 + Regs.X.BX);
Private->Legacy16Table->E820Length = (UINT32) CopySize;
if (Regs.X.AX != 0) {
DEBUG ((EFI_D_ERROR, "Legacy16 E820 length insufficient\n"));
} else {
CopyMem (
(VOID *)(UINTN) Private->Legacy16Table->E820Pointer,
Private->E820Table,
CopySize
);
}
} else {
CopyMem (
(VOID *)(UINTN) Private->Legacy16Table->E820Pointer,
Private->E820Table,
CopySize
);
Private->Legacy16Table->E820Length = (UINT32) CopySize;
}
//
// Get SMBIOS and ACPI table pointers
//
SmbiosTable = NULL;
EfiGetSystemConfigurationTable (
&gEfiSmbiosTableGuid,
&SmbiosTable
);
//
// We do not ASSERT if SmbiosTable not found. It is possbile that a platform does not produce SmbiosTable.
//
if (SmbiosTable == NULL) {
DEBUG ((EFI_D_INFO, "Smbios table is not found!\n"));
}
EfiToLegacy16BootTable->SmbiosTable = (UINT32)(UINTN)SmbiosTable;
AcpiTable = NULL;
Status = EfiGetSystemConfigurationTable (
&gEfiAcpi20TableGuid,
&AcpiTable
);
if (EFI_ERROR (Status)) {
Status = EfiGetSystemConfigurationTable (
&gEfiAcpi10TableGuid,
&AcpiTable
);
}
//
// We do not ASSERT if AcpiTable not found. It is possbile that a platform does not produce AcpiTable.
//
if (AcpiTable == NULL) {
DEBUG ((EFI_D_INFO, "ACPI table is not found!\n"));
}
EfiToLegacy16BootTable->AcpiTable = (UINT32)(UINTN)AcpiTable;
//
// Get RSD Ptr table rev at offset 15 decimal
// Rev = 0 Length is 20 decimal
// Rev != 0 Length is UINT32 at offset 20 decimal
//
if (AcpiTable != NULL) {
AcpiPtr = AcpiTable;
if (*((UINT8 *) AcpiPtr + 15) == 0) {
CopySize = 20;
} else {
AcpiPtr = ((UINT8 *) AcpiPtr + 20);
CopySize = (*(UINT32 *) AcpiPtr);
}
CopyMem (
(VOID *)(UINTN) Private->Legacy16Table->AcpiRsdPtrPointer,
AcpiTable,
CopySize
);
}
//
// Make sure all PCI Interrupt Line register are programmed to match 8259
//
PciProgramAllInterruptLineRegisters (Private);
//
// Unlock the Legacy BIOS region as PciProgramAllInterruptLineRegisters
// can lock it.
//
Private->LegacyRegion->UnLock (
Private->LegacyRegion,
Private->BiosStart,
Private->LegacyBiosImageSize,
&Granularity
);
//
// Configure Legacy Device Magic
//
// Only do this code if booting legacy OS
//
if ((mBootMode == BOOT_LEGACY_OS) || (mBootMode == BOOT_UNCONVENTIONAL_DEVICE)) {
UpdateSioData (Private);
}
//
// Setup BDA and EBDA standard areas before Legacy Boot
//
LegacyBiosCompleteBdaBeforeBoot (Private);
LegacyBiosCompleteStandardCmosBeforeBoot (Private);
//
// We must build IDE data, if it hasn't been done, before PciShadowRoms
// to insure EFI drivers are connected.
//
LegacyBiosBuildIdeData (Private, &HddInfo, 1);
UpdateAllIdentifyDriveData (Private);
//
// Clear IO BAR, if IDE controller in legacy mode.
//
InitLegacyIdeController (IdeController);
//
// Generate number of ticks since midnight for BDA. DOS requires this
// for its time. We have to make assumptions as to how long following
// code takes since after PciShadowRoms PciIo is gone. Place result in
// 40:6C-6F
//
// Adjust value by 1 second.
//
gRT->GetTime (&BootTime, NULL);
LocalTime = BootTime.Hour * 3600 + BootTime.Minute * 60 + BootTime.Second;
LocalTime += 1;
//
// Multiply result by 18.2 for number of ticks since midnight.
// Use 182/10 to avoid floating point math.
//
LocalTime = (LocalTime * 182) / 10;
BdaPtr = (UINT32 *) (UINTN)0x46C;
*BdaPtr = LocalTime;
//
// Shadow PCI ROMs. We must do this near the end since this will kick
// of Native EFI drivers that may be needed to collect info for Legacy16
//
// WARNING: PciIo is gone after this call.
//
PciShadowRoms (Private);
//
// Shadow PXE base code, BIS etc.
//
Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xc0000, 0x40000, &Granularity);
ShadowAddress = Private->OptionRom;
Private->LegacyBiosPlatform->PlatformHooks (
Private->LegacyBiosPlatform,
EfiPlatformHookShadowServiceRoms,
0,
0,
&ShadowAddress,
Legacy16Table,
NULL
);
Private->OptionRom = (UINT32)ShadowAddress;
//
// Register Legacy SMI Handler
//
LegacyBiosPlatform->SmmInit (
LegacyBiosPlatform,
EfiToLegacy16BootTable
);
//
// Let platform code know the boot options
//
LegacyBiosGetBbsInfo (
This,
&HddCount,
&LocalHddInfo,
&BbsCount,
&LocalBbsTable
);
DEBUG_CODE (
PrintPciInterruptRegister ();
PrintBbsTable (LocalBbsTable);
PrintHddInfo (LocalHddInfo);
);
//
// If drive wasn't spun up then BuildIdeData may have found new drives.
// Need to update BBS boot priority.
//
for (Index = 0; Index < MAX_IDE_CONTROLLER; Index++) {
if ((LocalHddInfo[Index].IdentifyDrive[0].Raw[0] != 0) &&
(LocalBbsTable[2 * Index + 1].BootPriority == BBS_IGNORE_ENTRY)
) {
LocalBbsTable[2 * Index + 1].BootPriority = BBS_UNPRIORITIZED_ENTRY;
}
if ((LocalHddInfo[Index].IdentifyDrive[1].Raw[0] != 0) &&
(LocalBbsTable[2 * Index + 2].BootPriority == BBS_IGNORE_ENTRY)
) {
LocalBbsTable[2 * Index + 2].BootPriority = BBS_UNPRIORITIZED_ENTRY;
}
}
Private->LegacyRegion->UnLock (
Private->LegacyRegion,
0xc0000,
0x40000,
&Granularity
);
LegacyBiosPlatform->PrepareToBoot (
LegacyBiosPlatform,
mBbsDevicePathPtr,
mBbsTable,
mLoadOptionsSize,
mLoadOptions,
(VOID *) &Private->IntThunk->EfiToLegacy16BootTable
);
//
// If no boot device return to BDS
//
if ((mBootMode == BOOT_LEGACY_OS) || (mBootMode == BOOT_UNCONVENTIONAL_DEVICE)) {
for (Index = 0; Index < BbsCount; Index++){
if ((LocalBbsTable[Index].BootPriority != BBS_DO_NOT_BOOT_FROM) &&
(LocalBbsTable[Index].BootPriority != BBS_UNPRIORITIZED_ENTRY) &&
(LocalBbsTable[Index].BootPriority != BBS_IGNORE_ENTRY)) {
break;
}
}
if (Index == BbsCount) {
return EFI_DEVICE_ERROR;
}
}
//
// Let the Legacy16 code know the device path type for legacy boot
//
EfiToLegacy16BootTable->DevicePathType = mBbsDevicePathPtr->DeviceType;
//
// Copy MP table, if it exists.
//
LegacyGetDataOrTable (This, EfiGetPlatformBinaryMpTable);
if (!Private->LegacyBootEntered) {
//
// Copy OEM INT Data, if it exists. Note: This code treats any data
// as a bag of bits and knows nothing of the contents nor cares.
// Contents are IBV specific.
//
LegacyGetDataOrTable (This, EfiGetPlatformBinaryOemIntData);
//
// Copy OEM16 Data, if it exists.Note: This code treats any data
// as a bag of bits and knows nothing of the contents nor cares.
// Contents are IBV specific.
//
LegacyGetDataOrTable (This, EfiGetPlatformBinaryOem16Data);
//
// Copy OEM32 Data, if it exists.Note: This code treats any data
// as a bag of bits and knows nothing of the contents nor cares.
// Contents are IBV specific.
//
LegacyGetDataOrTable (This, EfiGetPlatformBinaryOem32Data);
}
//
// Call into Legacy16 code to prepare for INT 19h
//
ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
Regs.X.AX = Legacy16PrepareToBoot;
//
// Pass in handoff data
//
Regs.X.ES = NORMALIZE_EFI_SEGMENT ((UINTN)EfiToLegacy16BootTable);
Regs.X.BX = NORMALIZE_EFI_OFFSET ((UINTN)EfiToLegacy16BootTable);
Private->LegacyBios.FarCall86 (
This,
Private->Legacy16CallSegment,
Private->Legacy16CallOffset,
&Regs,
NULL,
0
);
if (Regs.X.AX != 0) {
return EFI_DEVICE_ERROR;
}
//
// Lock the Legacy BIOS region
//
Private->LegacyRegion->Lock (
Private->LegacyRegion,
0xc0000,
0x40000,
&Granularity
);
//
// Lock attributes of the Legacy Region if chipset supports
//
Private->LegacyRegion->BootLock (
Private->LegacyRegion,
0xc0000,
0x40000,
&Granularity
);
//
// Call into Legacy16 code to do the INT 19h
//
EnableAllControllers (Private);
if ((mBootMode == BOOT_LEGACY_OS) || (mBootMode == BOOT_UNCONVENTIONAL_DEVICE)) {
//
// Report Status Code to indicate legacy boot event will be signalled
//
REPORT_STATUS_CODE (
EFI_PROGRESS_CODE,
(EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_LEGACY_BOOT_EVENT)
);
//
// Signal all the events that are waiting on EVT_SIGNAL_LEGACY_BOOT
//
EfiSignalEventLegacyBoot ();
DEBUG ((EFI_D_INFO, "Legacy INT19 Boot...\n"));
//
// Disable DXE Timer while executing in real mode
//
Private->Timer->SetTimerPeriod (Private->Timer, 0);
//
// Save and disable interrupt of debug timer
//
SaveAndSetDebugTimerInterrupt (FALSE);
//
// Put the 8259 into its legacy mode by reprogramming the vector bases
//
Private->Legacy8259->SetVectorBase (Private->Legacy8259, LEGACY_MODE_BASE_VECTOR_MASTER, LEGACY_MODE_BASE_VECTOR_SLAVE);
//
// PC History
// The original PC used INT8-F for master PIC. Since these mapped over
// processor exceptions TIANO moved the master PIC to INT68-6F.
// We need to set these back to the Legacy16 unexpected interrupt(saved
// in LegacyBios.c) since some OS see that these have values different from
// what is expected and invoke them. Since the legacy OS corrupts EFI
// memory, there is no handler for these interrupts and OS blows up.
//
// We need to save the TIANO values for the rare case that the Legacy16
// code cannot boot but knows memory hasn't been destroyed.
//
// To compound the problem, video takes over one of these INTS and must be
// be left.
// @bug - determine if video hooks INT(in which case we must find new
// set of TIANO vectors) or takes it over.
//
//
BaseVectorMaster = (UINT32 *) (sizeof (UINT32) * PROTECTED_MODE_BASE_VECTOR_MASTER);
for (Index = 0; Index < 8; Index++) {
Private->ThunkSavedInt[Index] = BaseVectorMaster[Index];
if (Private->ThunkSeg == (UINT16) (BaseVectorMaster[Index] >> 16)) {
BaseVectorMaster[Index] = (UINT32) (Private->BiosUnexpectedInt);
}
}
ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
Regs.X.AX = Legacy16Boot;
Private->LegacyBios.FarCall86 (
This,
Private->Legacy16CallSegment,
Private->Legacy16CallOffset,
&Regs,
NULL,
0
);
BaseVectorMaster = (UINT32 *) (sizeof (UINT32) * PROTECTED_MODE_BASE_VECTOR_MASTER);
for (Index = 0; Index < 8; Index++) {
BaseVectorMaster[Index] = Private->ThunkSavedInt[Index];
}
}
Private->LegacyBootEntered = TRUE;
if ((mBootMode == BOOT_LEGACY_OS) || (mBootMode == BOOT_UNCONVENTIONAL_DEVICE)) {
//
// Should never return unless never passed control to 0:7c00(first stage
// OS loader) and only then if no bootable device found.
//
return EFI_DEVICE_ERROR;
} else {
//
// If boot to EFI then expect to return to caller
//
return EFI_SUCCESS;
}
}
/**
Assign drive number to legacy HDD drives prior to booting an EFI
aware OS so the OS can access drives without an EFI driver.
Note: BBS compliant drives ARE NOT available until this call by
either shell or EFI.
@param This Protocol instance pointer.
@param BbsCount Number of BBS_TABLE structures
@param BbsTable List BBS entries
@retval EFI_SUCCESS Drive numbers assigned
**/
EFI_STATUS
EFIAPI
LegacyBiosPrepareToBootEfi (
IN EFI_LEGACY_BIOS_PROTOCOL *This,
OUT UINT16 *BbsCount,
OUT BBS_TABLE **BbsTable
)
{
EFI_STATUS Status;
EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable;
LEGACY_BIOS_INSTANCE *Private;
Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
EfiToLegacy16BootTable = &Private->IntThunk->EfiToLegacy16BootTable;
mBootMode = BOOT_EFI_OS;
mBbsDevicePathPtr = NULL;
Status = GenericLegacyBoot (This);
*BbsTable = (BBS_TABLE*)(UINTN)EfiToLegacy16BootTable->BbsTable;
*BbsCount = (UINT16) (sizeof (Private->IntThunk->BbsTable) / sizeof (BBS_TABLE));
return Status;
}
/**
To boot from an unconventional device like parties and/or execute HDD diagnostics.
@param This Protocol instance pointer.
@param Attributes How to interpret the other input parameters
@param BbsEntry The 0-based index into the BbsTable for the parent
device.
@param BeerData Pointer to the 128 bytes of ram BEER data.
@param ServiceAreaData Pointer to the 64 bytes of raw Service Area data. The
caller must provide a pointer to the specific Service
Area and not the start all Service Areas.
@retval EFI_INVALID_PARAMETER if error. Does NOT return if no error.
***/
EFI_STATUS
EFIAPI
LegacyBiosBootUnconventionalDevice (
IN EFI_LEGACY_BIOS_PROTOCOL *This,
IN UDC_ATTRIBUTES Attributes,
IN UINTN BbsEntry,
IN VOID *BeerData,
IN VOID *ServiceAreaData
)
{
EFI_STATUS Status;
EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable;
LEGACY_BIOS_INSTANCE *Private;
UD_TABLE *UcdTable;
UINTN Index;
UINT16 BootPriority;
BBS_TABLE *BbsTable;
BootPriority = 0;
Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
mBootMode = BOOT_UNCONVENTIONAL_DEVICE;
mBbsDevicePathPtr = &mBbsDevicePathNode;
mAttributes = Attributes;
mBbsEntry = BbsEntry;
mBeerData = BeerData, mServiceAreaData = ServiceAreaData;
EfiToLegacy16BootTable = &Private->IntThunk->EfiToLegacy16BootTable;
//
// Do input parameter checking
//
if ((Attributes.DirectoryServiceValidity == 0) &&
(Attributes.RabcaUsedFlag == 0) &&
(Attributes.ExecuteHddDiagnosticsFlag == 0)
) {
return EFI_INVALID_PARAMETER;
}
if (((Attributes.DirectoryServiceValidity != 0) && (ServiceAreaData == NULL)) ||
(((Attributes.DirectoryServiceValidity | Attributes.RabcaUsedFlag) != 0) && (BeerData == NULL))
) {
return EFI_INVALID_PARAMETER;
}
UcdTable = (UD_TABLE *) AllocatePool (
sizeof (UD_TABLE)
);
if (NULL == UcdTable) {
return EFI_OUT_OF_RESOURCES;
}
EfiToLegacy16BootTable->UnconventionalDeviceTable = (UINT32)(UINTN)UcdTable;
UcdTable->Attributes = Attributes;
UcdTable->BbsTableEntryNumberForParentDevice = (UINT8) BbsEntry;
//
// Force all existing BBS entries to DoNotBoot. This allows 16-bit CSM
// to assign drive numbers but bot boot from. Only newly created entries
// will be valid.
//
BbsTable = (BBS_TABLE*)(UINTN)EfiToLegacy16BootTable->BbsTable;
for (Index = 0; Index < EfiToLegacy16BootTable->NumberBbsEntries; Index++) {
BbsTable[Index].BootPriority = BBS_DO_NOT_BOOT_FROM;
}
//
// If parent is onboard IDE then assign controller & device number
// else they are 0.
//
if (BbsEntry < MAX_IDE_CONTROLLER * 2) {
UcdTable->DeviceNumber = (UINT8) ((BbsEntry - 1) % 2);
}
if (BeerData != NULL) {
CopyMem (
(VOID *) UcdTable->BeerData,
BeerData,
(UINTN) 128
);
}
if (ServiceAreaData != NULL) {
CopyMem (
(VOID *) UcdTable->ServiceAreaData,
ServiceAreaData,
(UINTN) 64
);
}
//
// For each new entry do the following:
// 1. Increment current number of BBS entries
// 2. Copy parent entry to new entry.
// 3. Zero out BootHandler Offset & segment
// 4. Set appropriate device type. BEV(0x80) for HDD diagnostics
// and Floppy(0x01) for PARTIES boot.
// 5. Assign new priority.
//
if ((Attributes.ExecuteHddDiagnosticsFlag) != 0) {
EfiToLegacy16BootTable->NumberBbsEntries += 1;
CopyMem (
(VOID *) &BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootPriority,
(VOID *) &BbsTable[BbsEntry].BootPriority,
sizeof (BBS_TABLE)
);
BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootHandlerOffset = 0;
BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootHandlerSegment = 0;
BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].DeviceType = 0x80;
UcdTable->BbsTableEntryNumberForHddDiag = (UINT8) (EfiToLegacy16BootTable->NumberBbsEntries - 1);
BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootPriority = BootPriority;
BootPriority += 1;
//
// Set device type as BBS_TYPE_DEV for PARTIES diagnostic
//
mBbsDevicePathNode.DeviceType = BBS_TYPE_BEV;
}
if (((Attributes.DirectoryServiceValidity | Attributes.RabcaUsedFlag)) != 0) {
EfiToLegacy16BootTable->NumberBbsEntries += 1;
CopyMem (
(VOID *) &BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootPriority,
(VOID *) &BbsTable[BbsEntry].BootPriority,
sizeof (BBS_TABLE)
);
BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootHandlerOffset = 0;
BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootHandlerSegment = 0;
BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].DeviceType = 0x01;
UcdTable->BbsTableEntryNumberForBoot = (UINT8) (EfiToLegacy16BootTable->NumberBbsEntries - 1);
BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootPriority = BootPriority;
//
// Set device type as BBS_TYPE_FLOPPY for PARTIES boot as floppy
//
mBbsDevicePathNode.DeviceType = BBS_TYPE_FLOPPY;
}
//
// Build the BBS Device Path for this boot selection
//
mBbsDevicePathNode.Header.Type = BBS_DEVICE_PATH;
mBbsDevicePathNode.Header.SubType = BBS_BBS_DP;
SetDevicePathNodeLength (&mBbsDevicePathNode.Header, sizeof (BBS_BBS_DEVICE_PATH));
mBbsDevicePathNode.StatusFlag = 0;
mBbsDevicePathNode.String[0] = 0;
Status = GenericLegacyBoot (This);
return Status;
}
/**
Attempt to legacy boot the BootOption. If the EFI contexted has been
compromised this function will not return.
@param This Protocol instance pointer.
@param BbsDevicePath EFI Device Path from BootXXXX variable.
@param LoadOptionsSize Size of LoadOption in size.
@param LoadOptions LoadOption from BootXXXX variable
@retval EFI_SUCCESS Removable media not present
**/
EFI_STATUS
EFIAPI
LegacyBiosLegacyBoot (
IN EFI_LEGACY_BIOS_PROTOCOL *This,
IN BBS_BBS_DEVICE_PATH *BbsDevicePath,
IN UINT32 LoadOptionsSize,
IN VOID *LoadOptions
)
{
EFI_STATUS Status;
mBbsDevicePathPtr = BbsDevicePath;
mLoadOptionsSize = LoadOptionsSize;
mLoadOptions = LoadOptions;
mBootMode = BOOT_LEGACY_OS;
Status = GenericLegacyBoot (This);
return Status;
}
/**
Convert EFI Memory Type to E820 Memory Type.
@param Type EFI Memory Type
@return ACPI Memory Type for EFI Memory Type
**/
EFI_ACPI_MEMORY_TYPE
EfiMemoryTypeToE820Type (
IN UINT32 Type
)
{
switch (Type) {
case EfiLoaderCode:
case EfiLoaderData:
case EfiBootServicesCode:
case EfiBootServicesData:
case EfiConventionalMemory:
case EfiRuntimeServicesCode:
case EfiRuntimeServicesData:
return EfiAcpiAddressRangeMemory;
case EfiACPIReclaimMemory:
return EfiAcpiAddressRangeACPI;
case EfiACPIMemoryNVS:
return EfiAcpiAddressRangeNVS;
//
// All other types map to reserved.
// Adding the code just waists FLASH space.
//
// case EfiReservedMemoryType:
// case EfiUnusableMemory:
// case EfiMemoryMappedIO:
// case EfiMemoryMappedIOPortSpace:
// case EfiPalCode:
//
default:
return EfiAcpiAddressRangeReserved;
}
}
/**
Build the E820 table.
@param Private Legacy BIOS Instance data
@param Size Size of E820 Table
@retval EFI_SUCCESS It should always work.
**/
EFI_STATUS
LegacyBiosBuildE820 (
IN LEGACY_BIOS_INSTANCE *Private,
OUT UINTN *Size
)
{
EFI_STATUS Status;
EFI_E820_ENTRY64 *E820Table;
EFI_MEMORY_DESCRIPTOR *EfiMemoryMap;
EFI_MEMORY_DESCRIPTOR *EfiMemoryMapEnd;
EFI_MEMORY_DESCRIPTOR *EfiEntry;
EFI_MEMORY_DESCRIPTOR *NextEfiEntry;
EFI_MEMORY_DESCRIPTOR TempEfiEntry;
UINTN EfiMemoryMapSize;
UINTN EfiMapKey;
UINTN EfiDescriptorSize;
UINT32 EfiDescriptorVersion;
UINTN Index;
EFI_PEI_HOB_POINTERS Hob;
EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob;
UINTN TempIndex;
UINTN IndexSort;
UINTN TempNextIndex;
EFI_E820_ENTRY64 TempE820;
EFI_ACPI_MEMORY_TYPE TempType;
BOOLEAN ChangedFlag;
UINTN Above1MIndex;
UINT64 MemoryBlockLength;
E820Table = (EFI_E820_ENTRY64 *) Private->E820Table;
//
// Get the EFI memory map.
//
EfiMemoryMapSize = 0;
EfiMemoryMap = NULL;
Status = gBS->GetMemoryMap (
&EfiMemoryMapSize,
EfiMemoryMap,
&EfiMapKey,
&EfiDescriptorSize,
&EfiDescriptorVersion
);
ASSERT (Status == EFI_BUFFER_TOO_SMALL);
do {
//
// Use size returned back plus 1 descriptor for the AllocatePool.
// We don't just multiply by 2 since the "for" loop below terminates on
// EfiMemoryMapEnd which is dependent upon EfiMemoryMapSize. Otherwize
// we process bogus entries and create bogus E820 entries.
//
EfiMemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (EfiMemoryMapSize);
ASSERT (EfiMemoryMap != NULL);
Status = gBS->GetMemoryMap (
&EfiMemoryMapSize,
EfiMemoryMap,
&EfiMapKey,
&EfiDescriptorSize,
&EfiDescriptorVersion
);
if (EFI_ERROR (Status)) {
FreePool (EfiMemoryMap);
}
} while (Status == EFI_BUFFER_TOO_SMALL);
ASSERT_EFI_ERROR (Status);
//
// Punch in the E820 table for memory less than 1 MB.
// Assume ZeroMem () has been done on data structure.
//
//
// First entry is 0 to (640k - EBDA)
//
E820Table[0].BaseAddr = 0;
E820Table[0].Length = (UINT64) ((*(UINT16 *) (UINTN)0x40E) << 4);
E820Table[0].Type = EfiAcpiAddressRangeMemory;
//
// Second entry is (640k - EBDA) to 640k
//
E820Table[1].BaseAddr = E820Table[0].Length;
E820Table[1].Length = (UINT64) ((640 * 1024) - E820Table[0].Length);
E820Table[1].Type = EfiAcpiAddressRangeReserved;
//
// Third Entry is legacy BIOS
// DO NOT CLAIM region from 0xA0000-0xDFFFF. OS can use free areas
// to page in memory under 1MB.
// Omit region from 0xE0000 to start of BIOS, if any. This can be
// used for a multiple reasons including OPROMS.
//
//
// The CSM binary image size is not the actually size that CSM binary used,
// to avoid memory corrupt, we declare the 0E0000 - 0FFFFF is used by CSM binary.
//
E820Table[2].BaseAddr = 0xE0000;
E820Table[2].Length = 0x20000;
E820Table[2].Type = EfiAcpiAddressRangeReserved;
Above1MIndex = 2;
//
// Process the EFI map to produce E820 map;
//
//
// Sort memory map from low to high
//
EfiEntry = EfiMemoryMap;
NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) EfiMemoryMap + EfiMemoryMapSize);
while (EfiEntry < EfiMemoryMapEnd) {
while (NextEfiEntry < EfiMemoryMapEnd) {
if (EfiEntry->PhysicalStart > NextEfiEntry->PhysicalStart) {
CopyMem (&TempEfiEntry, EfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
CopyMem (EfiEntry, NextEfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
CopyMem (NextEfiEntry, &TempEfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
}
NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (NextEfiEntry, EfiDescriptorSize);
}
EfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
}
EfiEntry = EfiMemoryMap;
EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) EfiMemoryMap + EfiMemoryMapSize);
for (Index = Above1MIndex; (EfiEntry < EfiMemoryMapEnd) && (Index < EFI_MAX_E820_ENTRY - 1); ) {
MemoryBlockLength = (UINT64) (LShiftU64 (EfiEntry->NumberOfPages, 12));
if ((EfiEntry->PhysicalStart + MemoryBlockLength) < 0x100000) {
//
// Skip the memory block is under 1MB
//
} else {
if (EfiEntry->PhysicalStart < 0x100000) {
//
// When the memory block spans below 1MB, ensure the memory block start address is at least 1MB
//
MemoryBlockLength -= 0x100000 - EfiEntry->PhysicalStart;
EfiEntry->PhysicalStart = 0x100000;
}
//
// Convert memory type to E820 type
//
TempType = EfiMemoryTypeToE820Type (EfiEntry->Type);
if ((E820Table[Index].Type == TempType) && (EfiEntry->PhysicalStart == (E820Table[Index].BaseAddr + E820Table[Index].Length))) {
//
// Grow an existing entry
//
E820Table[Index].Length += MemoryBlockLength;
} else {
//
// Make a new entry
//
++Index;
E820Table[Index].BaseAddr = EfiEntry->PhysicalStart;
E820Table[Index].Length = MemoryBlockLength;
E820Table[Index].Type = TempType;
}
}
EfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
}
FreePool (EfiMemoryMap);
//
// Process the reserved memory map to produce E820 map ;
//
for (Hob.Raw = GetHobList (); !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) {
if (Hob.Raw != NULL && GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
ResourceHob = Hob.ResourceDescriptor;
if (((ResourceHob->ResourceType == EFI_RESOURCE_MEMORY_MAPPED_IO) ||
(ResourceHob->ResourceType == EFI_RESOURCE_FIRMWARE_DEVICE) ||
(ResourceHob->ResourceType == EFI_RESOURCE_MEMORY_RESERVED) ) &&
(ResourceHob->PhysicalStart > 0x100000) &&
(Index < EFI_MAX_E820_ENTRY - 1)) {
++Index;
E820Table[Index].BaseAddr = ResourceHob->PhysicalStart;
E820Table[Index].Length = ResourceHob->ResourceLength;
E820Table[Index].Type = EfiAcpiAddressRangeReserved;
}
}
}
Index ++;
Private->IntThunk->EfiToLegacy16InitTable.NumberE820Entries = (UINT32)Index;
Private->IntThunk->EfiToLegacy16BootTable.NumberE820Entries = (UINT32)Index;
Private->NumberE820Entries = (UINT32)Index;
*Size = (UINTN) (Index * sizeof (EFI_E820_ENTRY64));
//
// Sort E820Table from low to high
//
for (TempIndex = 0; TempIndex < Index; TempIndex++) {
ChangedFlag = FALSE;
for (TempNextIndex = 1; TempNextIndex < Index - TempIndex; TempNextIndex++) {
if (E820Table[TempNextIndex - 1].BaseAddr > E820Table[TempNextIndex].BaseAddr) {
ChangedFlag = TRUE;
TempE820.BaseAddr = E820Table[TempNextIndex - 1].BaseAddr;
TempE820.Length = E820Table[TempNextIndex - 1].Length;
TempE820.Type = E820Table[TempNextIndex - 1].Type;
E820Table[TempNextIndex - 1].BaseAddr = E820Table[TempNextIndex].BaseAddr;
E820Table[TempNextIndex - 1].Length = E820Table[TempNextIndex].Length;
E820Table[TempNextIndex - 1].Type = E820Table[TempNextIndex].Type;
E820Table[TempNextIndex].BaseAddr = TempE820.BaseAddr;
E820Table[TempNextIndex].Length = TempE820.Length;
E820Table[TempNextIndex].Type = TempE820.Type;
}
}
if (!ChangedFlag) {
break;
}
}
//
// Remove the overlap range
//
for (TempIndex = 1; TempIndex < Index; TempIndex++) {
if (E820Table[TempIndex - 1].BaseAddr <= E820Table[TempIndex].BaseAddr &&
((E820Table[TempIndex - 1].BaseAddr + E820Table[TempIndex - 1].Length) >=
(E820Table[TempIndex].BaseAddr +E820Table[TempIndex].Length))) {
//
//Overlap range is found
//
ASSERT (E820Table[TempIndex - 1].Type == E820Table[TempIndex].Type);
if (TempIndex == Index - 1) {
E820Table[TempIndex].BaseAddr = 0;
E820Table[TempIndex].Length = 0;
E820Table[TempIndex].Type = (EFI_ACPI_MEMORY_TYPE) 0;
Index--;
break;
} else {
for (IndexSort = TempIndex; IndexSort < Index - 1; IndexSort ++) {
E820Table[IndexSort].BaseAddr = E820Table[IndexSort + 1].BaseAddr;
E820Table[IndexSort].Length = E820Table[IndexSort + 1].Length;
E820Table[IndexSort].Type = E820Table[IndexSort + 1].Type;
}
Index--;
}
}
}
Private->IntThunk->EfiToLegacy16InitTable.NumberE820Entries = (UINT32)Index;
Private->IntThunk->EfiToLegacy16BootTable.NumberE820Entries = (UINT32)Index;
Private->NumberE820Entries = (UINT32)Index;
*Size = (UINTN) (Index * sizeof (EFI_E820_ENTRY64));
//
// Determine OS usable memory above 1Mb
//
Private->IntThunk->EfiToLegacy16BootTable.OsMemoryAbove1Mb = 0x0000;
for (TempIndex = Above1MIndex; TempIndex < Index; TempIndex++) {
if (E820Table[TempIndex].BaseAddr >= 0x100000 && E820Table[TempIndex].BaseAddr < 0x100000000ULL) { // not include above 4G memory
//
// ACPIReclaimMemory is also usable memory for ACPI OS, after OS dumps all ACPI tables.
//
if ((E820Table[TempIndex].Type == EfiAcpiAddressRangeMemory) || (E820Table[TempIndex].Type == EfiAcpiAddressRangeACPI)) {
Private->IntThunk->EfiToLegacy16BootTable.OsMemoryAbove1Mb += (UINT32) (E820Table[TempIndex].Length);
} else {
break; // break at first not normal memory, because SMM may use reserved memory.
}
}
}
Private->IntThunk->EfiToLegacy16InitTable.OsMemoryAbove1Mb = Private->IntThunk->EfiToLegacy16BootTable.OsMemoryAbove1Mb;
//
// Print DEBUG information
//
for (TempIndex = 0; TempIndex < Index; TempIndex++) {
DEBUG((EFI_D_INFO, "E820[%2d]: 0x%16lx ---- 0x%16lx, Type = 0x%x \n",
TempIndex,
E820Table[TempIndex].BaseAddr,
(E820Table[TempIndex].BaseAddr + E820Table[TempIndex].Length),
E820Table[TempIndex].Type
));
}
return EFI_SUCCESS;
}
/**
Fill in the standard BDA and EBDA stuff prior to legacy Boot
@param Private Legacy BIOS Instance data
@retval EFI_SUCCESS It should always work.
**/
EFI_STATUS
LegacyBiosCompleteBdaBeforeBoot (
IN LEGACY_BIOS_INSTANCE *Private
)
{
BDA_STRUC *Bda;
UINT16 MachineConfig;
DEVICE_PRODUCER_DATA_HEADER *SioPtr;
Bda = (BDA_STRUC *) ((UINTN) 0x400);
MachineConfig = 0;
SioPtr = &(Private->IntThunk->EfiToLegacy16BootTable.SioData);
Bda->Com1 = SioPtr->Serial[0].Address;
Bda->Com2 = SioPtr->Serial[1].Address;
Bda->Com3 = SioPtr->Serial[2].Address;
Bda->Com4 = SioPtr->Serial[3].Address;
if (SioPtr->Serial[0].Address != 0x00) {
MachineConfig += 0x200;
}
if (SioPtr->Serial[1].Address != 0x00) {
MachineConfig += 0x200;
}
if (SioPtr->Serial[2].Address != 0x00) {
MachineConfig += 0x200;
}
if (SioPtr->Serial[3].Address != 0x00) {
MachineConfig += 0x200;
}
Bda->Lpt1 = SioPtr->Parallel[0].Address;
Bda->Lpt2 = SioPtr->Parallel[1].Address;
Bda->Lpt3 = SioPtr->Parallel[2].Address;
if (SioPtr->Parallel[0].Address != 0x00) {
MachineConfig += 0x4000;
}
if (SioPtr->Parallel[1].Address != 0x00) {
MachineConfig += 0x4000;
}
if (SioPtr->Parallel[2].Address != 0x00) {
MachineConfig += 0x4000;
}
Bda->NumberOfDrives = (UINT8) (Bda->NumberOfDrives + Private->IdeDriveCount);
if (SioPtr->Floppy.NumberOfFloppy != 0x00) {
MachineConfig = (UINT16) (MachineConfig + 0x01 + (SioPtr->Floppy.NumberOfFloppy - 1) * 0x40);
Bda->FloppyXRate = 0x07;
}
Bda->Lpt1_2Timeout = 0x1414;
Bda->Lpt3_4Timeout = 0x1414;
Bda->Com1_2Timeout = 0x0101;
Bda->Com3_4Timeout = 0x0101;
//
// Force VGA and Coprocessor, indicate 101/102 keyboard
//
MachineConfig = (UINT16) (MachineConfig + 0x00 + 0x02 + (SioPtr->MousePresent * 0x04));
Bda->MachineConfig = MachineConfig;
return EFI_SUCCESS;
}
/**
Fill in the standard BDA for Keyboard LEDs
@param This Protocol instance pointer.
@param Leds Current LED status
@retval EFI_SUCCESS It should always work.
**/
EFI_STATUS
EFIAPI
LegacyBiosUpdateKeyboardLedStatus (
IN EFI_LEGACY_BIOS_PROTOCOL *This,
IN UINT8 Leds
)
{
LEGACY_BIOS_INSTANCE *Private;
BDA_STRUC *Bda;
UINT8 LocalLeds;
EFI_IA32_REGISTER_SET Regs;
Bda = (BDA_STRUC *) ((UINTN) 0x400);
Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
LocalLeds = Leds;
Bda->LedStatus = (UINT8) ((Bda->LedStatus &~0x07) | LocalLeds);
LocalLeds = (UINT8) (LocalLeds << 4);
Bda->ShiftStatus = (UINT8) ((Bda->ShiftStatus &~0x70) | LocalLeds);
LocalLeds = (UINT8) (Leds & 0x20);
Bda->KeyboardStatus = (UINT8) ((Bda->KeyboardStatus &~0x20) | LocalLeds);
//
// Call into Legacy16 code to allow it to do any processing
//
ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
Regs.X.AX = Legacy16SetKeyboardLeds;
Regs.H.CL = Leds;
Private->LegacyBios.FarCall86 (
&Private->LegacyBios,
Private->Legacy16Table->Compatibility16CallSegment,
Private->Legacy16Table->Compatibility16CallOffset,
&Regs,
NULL,
0
);
return EFI_SUCCESS;
}
/**
Fill in the standard CMOS stuff prior to legacy Boot
@param Private Legacy BIOS Instance data
@retval EFI_SUCCESS It should always work.
**/
EFI_STATUS
LegacyBiosCompleteStandardCmosBeforeBoot (
IN LEGACY_BIOS_INSTANCE *Private
)
{
UINT8 Bda;
UINT8 Floppy;
UINT32 Size;
//
// Update CMOS locations
// 10 floppy
// 12,19,1A - ignore as OS don't use them and there is no standard due
// to large capacity drives
// CMOS 14 = BDA 40:10 plus bit 3(display enabled)
//
Bda = (UINT8)(*((UINT8 *)((UINTN)0x410)) | BIT3);
//
// Force display enabled
//
Floppy = 0x00;
if ((Bda & BIT0) != 0) {
Floppy = BIT6;
}
//
// Check if 2.88MB floppy set
//
if ((Bda & (BIT7 | BIT6)) != 0) {
Floppy = (UINT8)(Floppy | BIT1);
}
LegacyWriteStandardCmos (CMOS_10, Floppy);
LegacyWriteStandardCmos (CMOS_14, Bda);
//
// Force Status Register A to set rate selection bits and divider
//
LegacyWriteStandardCmos (CMOS_0A, 0x26);
//
// redo memory size since it can change
//
Size = 15 * SIZE_1MB;
if (Private->IntThunk->EfiToLegacy16InitTable.OsMemoryAbove1Mb < (15 * SIZE_1MB)) {
Size = Private->IntThunk->EfiToLegacy16InitTable.OsMemoryAbove1Mb >> 10;
}
LegacyWriteStandardCmos (CMOS_17, (UINT8)(Size & 0xFF));
LegacyWriteStandardCmos (CMOS_30, (UINT8)(Size & 0xFF));
LegacyWriteStandardCmos (CMOS_18, (UINT8)(Size >> 8));
LegacyWriteStandardCmos (CMOS_31, (UINT8)(Size >> 8));
LegacyCalculateWriteStandardCmosChecksum ();
return EFI_SUCCESS;
}
/**
Relocate this image under 4G memory for IPF.
@param ImageHandle Handle of driver image.
@param SystemTable Pointer to system table.
@retval EFI_SUCCESS Image successfully relocated.
@retval EFI_ABORTED Failed to relocate image.
**/
EFI_STATUS
RelocateImageUnder4GIfNeeded (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
return EFI_SUCCESS;
}