| /** @file |
| |
| Copyright (c) 2004 - 2007, 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. |
| |
| Module Name: |
| |
| BdsBoot.c |
| |
| Abstract: |
| |
| BDS Lib functions which relate with create or process the boot |
| option. |
| |
| |
| **/ |
| |
| #include "InternalBdsLib.h" |
| |
| BOOLEAN mEnumBootDevice = FALSE; |
| |
| // |
| // This GUID is used for an EFI Variable that stores the front device pathes |
| // for a partial device path that starts with the HD node. |
| // |
| EFI_GUID mHdBootVariablePrivateGuid = { 0xfab7e9e1, 0x39dd, 0x4f2b, { 0x84, 0x8, 0xe2, 0xe, 0x90, 0x6c, 0xb6, 0xde } }; |
| |
| |
| |
| /** |
| Boot the legacy system with the boot option |
| |
| @param Option The legacy boot option which have BBS device path |
| |
| @retval EFI_UNSUPPORTED There is no legacybios protocol, do not support |
| legacy boot. |
| @retval EFI_STATUS Return the status of LegacyBios->LegacyBoot (). |
| |
| **/ |
| EFI_STATUS |
| BdsLibDoLegacyBoot ( |
| IN BDS_COMMON_OPTION *Option |
| ) |
| { |
| EFI_STATUS Status; |
| EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; |
| |
| Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); |
| if (EFI_ERROR (Status)) { |
| // |
| // If no LegacyBios protocol we do not support legacy boot |
| // |
| return EFI_UNSUPPORTED; |
| } |
| // |
| // Notes: if we seperate the int 19, then we don't need to refresh BBS |
| // |
| BdsRefreshBbsTableForBoot (Option); |
| |
| // |
| // Write boot to OS performance data to a file |
| // |
| PERF_CODE ( |
| WriteBootToOsPerformanceData (); |
| ); |
| |
| DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Legacy Boot: %S\n", Option->Description)); |
| return LegacyBios->LegacyBoot ( |
| LegacyBios, |
| (BBS_BBS_DEVICE_PATH *) Option->DevicePath, |
| Option->LoadOptionsSize, |
| Option->LoadOptions |
| ); |
| } |
| |
| |
| /** |
| Process the boot option follow the EFI 1.1 specification and |
| special treat the legacy boot option with BBS_DEVICE_PATH. |
| |
| @param Option The boot option need to be processed |
| @param DevicePath The device path which describe where to load the |
| boot image or the legcy BBS device path to boot |
| the legacy OS |
| @param ExitDataSize Returned directly from gBS->StartImage () |
| @param ExitData Returned directly from gBS->StartImage () |
| |
| @retval EFI_SUCCESS Status from gBS->StartImage () |
| @retval EFI_NOT_FOUND If the Device Path is not found in the system |
| |
| **/ |
| EFI_STATUS |
| BdsLibBootViaBootOption ( |
| IN BDS_COMMON_OPTION * Option, |
| IN EFI_DEVICE_PATH_PROTOCOL * DevicePath, |
| OUT UINTN *ExitDataSize, |
| OUT CHAR16 **ExitData OPTIONAL |
| ) |
| { |
| EFI_STATUS Status; |
| EFI_HANDLE Handle; |
| EFI_HANDLE ImageHandle; |
| EFI_DEVICE_PATH_PROTOCOL *FilePath; |
| EFI_LOADED_IMAGE_PROTOCOL *ImageInfo; |
| EFI_DEVICE_PATH_PROTOCOL *WorkingDevicePath; |
| EFI_ACPI_S3_SAVE_PROTOCOL *AcpiS3Save; |
| LIST_ENTRY TempBootLists; |
| |
| // |
| // Record the performance data for End of BDS |
| // |
| PERF_END (0, BDS_TOK, NULL, 0); |
| |
| *ExitDataSize = 0; |
| *ExitData = NULL; |
| |
| // |
| // Notes: put EFI64 ROM Shadow Solution |
| // |
| EFI64_SHADOW_ALL_LEGACY_ROM (); |
| |
| // |
| // Notes: this code can be remove after the s3 script table |
| // hook on the event EFI_EVENT_SIGNAL_READY_TO_BOOT or |
| // EFI_EVENT_SIGNAL_LEGACY_BOOT |
| // |
| Status = gBS->LocateProtocol (&gEfiAcpiS3SaveProtocolGuid, NULL, (VOID **) &AcpiS3Save); |
| if (!EFI_ERROR (Status)) { |
| AcpiS3Save->S3Save (AcpiS3Save, NULL); |
| } |
| // |
| // If it's Device Path that starts with a hard drive path, append it with the front part to compose a |
| // full device path |
| // |
| WorkingDevicePath = NULL; |
| if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) && |
| (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)) { |
| WorkingDevicePath = BdsExpandPartitionPartialDevicePathToFull ( |
| (HARDDRIVE_DEVICE_PATH *)DevicePath |
| ); |
| if (WorkingDevicePath != NULL) { |
| DevicePath = WorkingDevicePath; |
| } |
| } |
| // |
| // Signal the EFI_EVENT_SIGNAL_READY_TO_BOOT event |
| // |
| EfiSignalEventReadyToBoot(); |
| |
| |
| // |
| // Set Boot Current |
| // |
| gRT->SetVariable ( |
| L"BootCurrent", |
| &gEfiGlobalVariableGuid, |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, |
| sizeof (UINT16), |
| &Option->BootCurrent |
| ); |
| |
| if ((DevicePathType (Option->DevicePath) == BBS_DEVICE_PATH) && |
| (DevicePathSubType (Option->DevicePath) == BBS_BBS_DP) |
| ) { |
| // |
| // Check to see if we should legacy BOOT. If yes then do the legacy boot |
| // |
| return BdsLibDoLegacyBoot (Option); |
| } |
| |
| // |
| // If the boot option point to Internal FV shell, make sure it is valid |
| // |
| Status = BdsLibUpdateFvFileDevicePath (&DevicePath, &gEfiShellFileGuid); |
| if (!EFI_ERROR(Status)) { |
| if (Option->DevicePath != NULL) { |
| SafeFreePool(Option->DevicePath); |
| } |
| Option->DevicePath = AllocateZeroPool (GetDevicePathSize (DevicePath)); |
| CopyMem (Option->DevicePath, DevicePath, GetDevicePathSize (DevicePath)); |
| // |
| // Update the shell boot option |
| // |
| InitializeListHead (&TempBootLists); |
| BdsLibRegisterNewOption (&TempBootLists, DevicePath, L"EFI Internal Shell", L"BootOrder"); |
| } |
| |
| // |
| // Drop the TPL level from TPL_APPLICATION to TPL_APPLICATION |
| // |
| gBS->RestoreTPL (TPL_APPLICATION); |
| |
| DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Booting EFI way %S\n", Option->Description)); |
| |
| Status = gBS->LoadImage ( |
| TRUE, |
| mBdsImageHandle, |
| DevicePath, |
| NULL, |
| 0, |
| &ImageHandle |
| ); |
| |
| // |
| // If we didn't find an image directly, we need to try as if it is a removable device boot opotion |
| // and load the image according to the default boot behavior for removable device. |
| // |
| if (EFI_ERROR (Status)) { |
| // |
| // check if there is a bootable removable media could be found in this device path , |
| // and get the bootable media handle |
| // |
| Handle = BdsLibGetBootableHandle(DevicePath); |
| if (Handle == NULL) { |
| goto Done; |
| } |
| // |
| // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media |
| // machinename is ia32, ia64, x64, ... |
| // |
| FilePath = FileDevicePath (Handle, DEFAULT_REMOVABLE_FILE_NAME); |
| if (FilePath) { |
| Status = gBS->LoadImage ( |
| TRUE, |
| mBdsImageHandle, |
| FilePath, |
| NULL, |
| 0, |
| &ImageHandle |
| ); |
| if (EFI_ERROR (Status)) { |
| // |
| // The DevicePath failed, and it's not a valid |
| // removable media device. |
| // |
| goto Done; |
| } |
| } |
| } |
| |
| if (EFI_ERROR (Status)) { |
| // |
| // It there is any error from the Boot attempt exit now. |
| // |
| goto Done; |
| } |
| // |
| // Provide the image with it's load options |
| // |
| Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo); |
| ASSERT_EFI_ERROR (Status); |
| |
| if (Option->LoadOptionsSize != 0) { |
| ImageInfo->LoadOptionsSize = Option->LoadOptionsSize; |
| ImageInfo->LoadOptions = Option->LoadOptions; |
| } |
| // |
| // Before calling the image, enable the Watchdog Timer for |
| // the 5 Minute period |
| // |
| gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL); |
| |
| Status = gBS->StartImage (ImageHandle, ExitDataSize, ExitData); |
| DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status)); |
| |
| // |
| // Clear the Watchdog Timer after the image returns |
| // |
| gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL); |
| |
| Done: |
| // |
| // Clear Boot Current |
| // |
| gRT->SetVariable ( |
| L"BootCurrent", |
| &gEfiGlobalVariableGuid, |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, |
| 0, |
| &Option->BootCurrent |
| ); |
| |
| // |
| // Raise the TPL level back to TPL_APPLICATION |
| // |
| gBS->RaiseTPL (TPL_APPLICATION); |
| |
| return Status; |
| } |
| |
| |
| /** |
| Expand a device path that starts with a hard drive media device path node to be a |
| full device path that includes the full hardware path to the device. We need |
| to do this so it can be booted. As an optimizaiton the front match (the part point |
| to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable |
| so a connect all is not required on every boot. All successful history device path |
| which point to partition node (the front part) will be saved. |
| |
| @param HardDriveDevicePath EFI Device Path to boot, if it starts with a hard |
| drive media device path. |
| A Pointer to the full device path. |
| @retval NULL Cannot find a valid Hard Drive devic path |
| |
| **/ |
| EFI_DEVICE_PATH_PROTOCOL * |
| BdsExpandPartitionPartialDevicePathToFull ( |
| IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath |
| ) |
| { |
| EFI_STATUS Status; |
| UINTN BlockIoHandleCount; |
| EFI_HANDLE *BlockIoBuffer; |
| EFI_DEVICE_PATH_PROTOCOL *FullDevicePath; |
| EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath; |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; |
| UINTN Index; |
| UINTN InstanceNum; |
| EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath; |
| EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath; |
| UINTN CachedDevicePathSize; |
| BOOLEAN DeviceExist; |
| BOOLEAN NeedAdjust; |
| EFI_DEVICE_PATH_PROTOCOL *Instance; |
| UINTN Size; |
| |
| FullDevicePath = NULL; |
| // |
| // Check if there is prestore 'HDDP' variable. |
| // If exist, search the front path which point to partition node in the variable instants. |
| // If fail to find or 'HDDP' not exist, reconnect all and search in all system |
| // |
| CachedDevicePath = BdsLibGetVariableAndSize ( |
| L"HDDP", |
| &mHdBootVariablePrivateGuid, |
| &CachedDevicePathSize |
| ); |
| if (CachedDevicePath != NULL) { |
| TempNewDevicePath = CachedDevicePath; |
| DeviceExist = FALSE; |
| NeedAdjust = FALSE; |
| do { |
| // |
| // Check every instance of the variable |
| // First, check wheather the instance contain the partition node, which is needed for distinguishing multi |
| // partial partition boot option. Second, check wheather the instance could be connected. |
| // |
| Instance = GetNextDevicePathInstance (&TempNewDevicePath, &Size); |
| if (MatchPartitionDevicePathNode (Instance, HardDriveDevicePath)) { |
| // |
| // Connect the device path instance, the device path point to hard drive media device path node |
| // e.g. ACPI() /PCI()/ATA()/Partition() |
| // |
| Status = BdsLibConnectDevicePath (Instance); |
| if (!EFI_ERROR (Status)) { |
| DeviceExist = TRUE; |
| break; |
| } |
| } |
| // |
| // Come here means the first instance is not matched |
| // |
| NeedAdjust = TRUE; |
| SafeFreePool(Instance); |
| } while (TempNewDevicePath != NULL); |
| |
| if (DeviceExist) { |
| // |
| // Find the matched device path. |
| // Append the file path infomration from the boot option and return the fully expanded device path. |
| // |
| DevicePath = NextDevicePathNode ((EFI_DEVICE_PATH_PROTOCOL *) HardDriveDevicePath); |
| FullDevicePath = AppendDevicePath (Instance, DevicePath); |
| |
| // |
| // Adjust the 'HDDP' instances sequense if the matched one is not first one. |
| // |
| if (NeedAdjust) { |
| // |
| // First delete the matched instance. |
| // |
| TempNewDevicePath = CachedDevicePath; |
| CachedDevicePath = BdsLibDelPartMatchInstance ( CachedDevicePath, Instance ); |
| SafeFreePool (TempNewDevicePath); |
| // |
| // Second, append the remaining parth after the matched instance |
| // |
| TempNewDevicePath = CachedDevicePath; |
| CachedDevicePath = AppendDevicePathInstance ( Instance, CachedDevicePath ); |
| SafeFreePool (TempNewDevicePath); |
| // |
| // Save the matching Device Path so we don't need to do a connect all next time |
| // |
| Status = gRT->SetVariable ( |
| L"HDDP", |
| &mHdBootVariablePrivateGuid, |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, |
| GetDevicePathSize (CachedDevicePath), |
| CachedDevicePath |
| ); |
| } |
| SafeFreePool(Instance); |
| gBS->FreePool (CachedDevicePath); |
| return FullDevicePath; |
| } |
| } |
| |
| // |
| // If we get here we fail to find or 'HDDP' not exist, and now we need |
| // to seach all devices in the system for a matched partition |
| // |
| BdsLibConnectAllDriversToAllControllers (); |
| Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer); |
| if (EFI_ERROR (Status) || BlockIoHandleCount == 0) { |
| // |
| // If there was an error or there are no device handles that support |
| // the BLOCK_IO Protocol, then return. |
| // |
| return NULL; |
| } |
| // |
| // Loop through all the device handles that support the BLOCK_IO Protocol |
| // |
| for (Index = 0; Index < BlockIoHandleCount; Index++) { |
| |
| Status = gBS->HandleProtocol (BlockIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID *) &BlockIoDevicePath); |
| if (EFI_ERROR (Status) || BlockIoDevicePath == NULL) { |
| continue; |
| } |
| |
| if (MatchPartitionDevicePathNode (BlockIoDevicePath, HardDriveDevicePath)) { |
| // |
| // Find the matched partition device path |
| // |
| DevicePath = NextDevicePathNode ((EFI_DEVICE_PATH_PROTOCOL *) HardDriveDevicePath); |
| FullDevicePath = AppendDevicePath (BlockIoDevicePath, DevicePath); |
| |
| // |
| // Save the matched patition device path in 'HDDP' variable |
| // |
| if (CachedDevicePath != NULL) { |
| // |
| // Save the matched patition device path as first instance of 'HDDP' variable |
| // |
| if (BdsLibMatchDevicePaths (CachedDevicePath, BlockIoDevicePath)) { |
| TempNewDevicePath = CachedDevicePath; |
| CachedDevicePath = BdsLibDelPartMatchInstance (CachedDevicePath, BlockIoDevicePath); |
| SafeFreePool(TempNewDevicePath); |
| |
| TempNewDevicePath = CachedDevicePath; |
| CachedDevicePath = AppendDevicePathInstance (BlockIoDevicePath, CachedDevicePath); |
| SafeFreePool(TempNewDevicePath); |
| } else { |
| TempNewDevicePath = CachedDevicePath; |
| CachedDevicePath = AppendDevicePathInstance (BlockIoDevicePath, CachedDevicePath); |
| SafeFreePool(TempNewDevicePath); |
| } |
| // |
| // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller |
| // If the user try to boot many OS in different HDs or partitions, in theary, the 'HDDP' variable maybe become larger and larger. |
| // |
| InstanceNum = 0; |
| TempNewDevicePath = CachedDevicePath; |
| while (!IsDevicePathEnd (TempNewDevicePath)) { |
| TempNewDevicePath = NextDevicePathNode (TempNewDevicePath); |
| // |
| // Parse one instance |
| // |
| while (!IsDevicePathEndType (TempNewDevicePath)) { |
| TempNewDevicePath = NextDevicePathNode (TempNewDevicePath); |
| } |
| InstanceNum++; |
| // |
| // If the CachedDevicePath variable contain too much instance, only remain 12 instances. |
| // |
| if (InstanceNum >= 12) { |
| SetDevicePathEndNode (TempNewDevicePath); |
| break; |
| } |
| } |
| } else { |
| CachedDevicePath = DuplicateDevicePath (BlockIoDevicePath); |
| } |
| |
| // |
| // Save the matching Device Path so we don't need to do a connect all next time |
| // |
| Status = gRT->SetVariable ( |
| L"HDDP", |
| &mHdBootVariablePrivateGuid, |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, |
| GetDevicePathSize (CachedDevicePath), |
| CachedDevicePath |
| ); |
| |
| break; |
| } |
| } |
| gBS->FreePool (CachedDevicePath); |
| gBS->FreePool (BlockIoBuffer); |
| return FullDevicePath; |
| } |
| |
| |
| /** |
| Check whether there is a instance in BlockIoDevicePath, which contain multi device path |
| instances, has the same partition node with HardDriveDevicePath device path |
| |
| @param BlockIoDevicePath Multi device path instances which need to check |
| @param HardDriveDevicePath A device path which starts with a hard drive media |
| device path. |
| |
| @retval TRUE There is a matched device path instance FALSE |
| -There is no matched device path instance |
| |
| **/ |
| BOOLEAN |
| MatchPartitionDevicePathNode ( |
| IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath, |
| IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath |
| ) |
| { |
| HARDDRIVE_DEVICE_PATH *TmpHdPath; |
| HARDDRIVE_DEVICE_PATH *TempPath; |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; |
| BOOLEAN Match; |
| EFI_DEVICE_PATH_PROTOCOL *BlockIoHdDevicePathNode; |
| |
| if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) { |
| return FALSE; |
| } |
| // |
| // Make PreviousDevicePath == the device path node before the end node |
| // |
| DevicePath = BlockIoDevicePath; |
| BlockIoHdDevicePathNode = NULL; |
| |
| // |
| // find the partition device path node |
| // |
| while (!IsDevicePathEnd (DevicePath)) { |
| if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) && |
| (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP) |
| ) { |
| BlockIoHdDevicePathNode = DevicePath; |
| break; |
| } |
| |
| DevicePath = NextDevicePathNode (DevicePath); |
| } |
| |
| if (BlockIoHdDevicePathNode == NULL) { |
| return FALSE; |
| } |
| // |
| // See if the harddrive device path in blockio matches the orig Hard Drive Node |
| // |
| TmpHdPath = (HARDDRIVE_DEVICE_PATH *) BlockIoHdDevicePathNode; |
| TempPath = (HARDDRIVE_DEVICE_PATH *) BdsLibUnpackDevicePath ((EFI_DEVICE_PATH_PROTOCOL *) HardDriveDevicePath); |
| Match = FALSE; |
| // |
| // Check for the match |
| // |
| if ((TmpHdPath->MBRType == TempPath->MBRType) && |
| (TmpHdPath->SignatureType == TempPath->SignatureType)) { |
| switch (TmpHdPath->SignatureType) { |
| case SIGNATURE_TYPE_GUID: |
| Match = CompareGuid ((EFI_GUID *)TmpHdPath->Signature, (EFI_GUID *)TempPath->Signature); |
| break; |
| case SIGNATURE_TYPE_MBR: |
| Match = (BOOLEAN)(*((UINT32 *)(&(TmpHdPath->Signature[0]))) == *(UINT32 *)(&(TempPath->Signature[0]))); |
| break; |
| default: |
| Match = FALSE; |
| break; |
| } |
| } |
| |
| return Match; |
| } |
| |
| |
| /** |
| Delete the boot option associated with the handle passed in |
| |
| @param Handle The handle which present the device path to create |
| boot option |
| |
| @retval EFI_SUCCESS Delete the boot option success |
| @retval EFI_NOT_FOUND If the Device Path is not found in the system |
| @retval EFI_OUT_OF_RESOURCES Lack of memory resource |
| @retval Other Error return value from SetVariable() |
| |
| **/ |
| EFI_STATUS |
| BdsLibDeleteOptionFromHandle ( |
| IN EFI_HANDLE Handle |
| ) |
| { |
| UINT16 *BootOrder; |
| UINT8 *BootOptionVar; |
| UINTN BootOrderSize; |
| UINTN BootOptionSize; |
| EFI_STATUS Status; |
| UINTN Index; |
| UINT16 BootOption[BOOT_OPTION_MAX_CHAR]; |
| UINTN DevicePathSize; |
| UINTN OptionDevicePathSize; |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; |
| EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath; |
| UINT8 *TempPtr; |
| |
| Status = EFI_SUCCESS; |
| BootOrder = NULL; |
| BootOrderSize = 0; |
| |
| BootOrder = BdsLibGetVariableAndSize ( |
| L"BootOrder", |
| &gEfiGlobalVariableGuid, |
| &BootOrderSize |
| ); |
| if (NULL == BootOrder) { |
| return EFI_NOT_FOUND; |
| } |
| |
| DevicePath = DevicePathFromHandle (Handle); |
| if (DevicePath == NULL) { |
| return EFI_NOT_FOUND; |
| } |
| DevicePathSize = GetDevicePathSize (DevicePath); |
| |
| Index = 0; |
| while (Index < BootOrderSize / sizeof (UINT16)) { |
| UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]); |
| BootOptionVar = BdsLibGetVariableAndSize ( |
| BootOption, |
| &gEfiGlobalVariableGuid, |
| &BootOptionSize |
| ); |
| if (NULL == BootOptionVar) { |
| gBS->FreePool (BootOrder); |
| return EFI_OUT_OF_RESOURCES; |
| } |
| |
| TempPtr = BootOptionVar; |
| TempPtr += sizeof (UINT32) + sizeof (UINT16); |
| TempPtr += StrSize ((CHAR16 *) TempPtr); |
| OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr; |
| OptionDevicePathSize = GetDevicePathSize (OptionDevicePath); |
| |
| // |
| // Check whether the device path match |
| // |
| if ((OptionDevicePathSize == DevicePathSize) && |
| (CompareMem (DevicePath, OptionDevicePath, DevicePathSize) == 0)) { |
| BdsDeleteBootOption (BootOrder[Index], BootOrder, &BootOrderSize); |
| gBS->FreePool (BootOptionVar); |
| break; |
| } |
| |
| gBS->FreePool (BootOptionVar); |
| Index++; |
| } |
| |
| Status = gRT->SetVariable ( |
| L"BootOrder", |
| &gEfiGlobalVariableGuid, |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, |
| BootOrderSize, |
| BootOrder |
| ); |
| |
| gBS->FreePool (BootOrder); |
| |
| return Status; |
| } |
| |
| |
| /** |
| Delete all invalid EFI boot options. The probable invalid boot option could |
| be Removable media or Network boot device. |
| |
| VOID |
| |
| @retval EFI_SUCCESS Delete all invalid boot option success |
| @retval EFI_NOT_FOUND Variable "BootOrder" is not found |
| @retval EFI_OUT_OF_RESOURCES Lack of memory resource |
| @retval Other Error return value from SetVariable() |
| |
| **/ |
| EFI_STATUS |
| BdsDeleteAllInvalidEfiBootOption ( |
| VOID |
| ) |
| { |
| UINT16 *BootOrder; |
| UINT8 *BootOptionVar; |
| UINTN BootOrderSize; |
| UINTN BootOptionSize; |
| EFI_STATUS Status; |
| UINTN Index; |
| UINTN Index2; |
| UINT16 BootOption[BOOT_OPTION_MAX_CHAR]; |
| EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath; |
| UINT8 *TempPtr; |
| |
| Status = EFI_SUCCESS; |
| BootOrder = NULL; |
| BootOrderSize = 0; |
| |
| BootOrder = BdsLibGetVariableAndSize ( |
| L"BootOrder", |
| &gEfiGlobalVariableGuid, |
| &BootOrderSize |
| ); |
| if (NULL == BootOrder) { |
| return EFI_NOT_FOUND; |
| } |
| |
| Index = 0; |
| while (Index < BootOrderSize / sizeof (UINT16)) { |
| UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]); |
| BootOptionVar = BdsLibGetVariableAndSize ( |
| BootOption, |
| &gEfiGlobalVariableGuid, |
| &BootOptionSize |
| ); |
| if (NULL == BootOptionVar) { |
| gBS->FreePool (BootOrder); |
| return EFI_OUT_OF_RESOURCES; |
| } |
| |
| TempPtr = BootOptionVar; |
| TempPtr += sizeof (UINT32) + sizeof (UINT16); |
| TempPtr += StrSize ((CHAR16 *) TempPtr); |
| OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr; |
| |
| // |
| // Skip legacy boot option (BBS boot device) |
| // |
| if ((DevicePathType (OptionDevicePath) == BBS_DEVICE_PATH) && |
| (DevicePathSubType (OptionDevicePath) == BBS_BBS_DP)) { |
| gBS->FreePool (BootOptionVar); |
| Index++; |
| continue; |
| } |
| |
| if (!BdsLibIsValidEFIBootOptDevicePath (OptionDevicePath, FALSE)) { |
| // |
| // Delete this invalid boot option "Boot####" |
| // |
| Status = gRT->SetVariable ( |
| BootOption, |
| &gEfiGlobalVariableGuid, |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, |
| 0, |
| NULL |
| ); |
| // |
| // Mark this boot option in boot order as deleted |
| // |
| BootOrder[Index] = 0xffff; |
| } |
| |
| gBS->FreePool (BootOptionVar); |
| Index++; |
| } |
| |
| // |
| // Adjust boot order array |
| // |
| Index2 = 0; |
| for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) { |
| if (BootOrder[Index] != 0xffff) { |
| BootOrder[Index2] = BootOrder[Index]; |
| Index2 ++; |
| } |
| } |
| Status = gRT->SetVariable ( |
| L"BootOrder", |
| &gEfiGlobalVariableGuid, |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, |
| Index2 * sizeof (UINT16), |
| BootOrder |
| ); |
| |
| gBS->FreePool (BootOrder); |
| |
| return Status; |
| } |
| |
| |
| /** |
| This function will enumerate all possible boot device in the system, |
| it will only excute once of every boot. |
| |
| @param BdsBootOptionList The header of the link list which indexed all |
| current boot options |
| |
| @retval EFI_SUCCESS Finished all the boot device enumerate and create |
| the boot option base on that boot device |
| |
| **/ |
| EFI_STATUS |
| BdsLibEnumerateAllBootOption ( |
| IN OUT LIST_ENTRY *BdsBootOptionList |
| ) |
| { |
| EFI_STATUS Status; |
| UINT16 FloppyNumber; |
| UINT16 CdromNumber; |
| UINT16 UsbNumber; |
| UINT16 MiscNumber; |
| UINT16 NonBlockNumber; |
| UINTN NumberBlockIoHandles; |
| EFI_HANDLE *BlockIoHandles; |
| EFI_BLOCK_IO_PROTOCOL *BlkIo; |
| UINTN Index; |
| UINTN NumberSimpleNetworkHandles; |
| EFI_HANDLE *SimpleNetworkHandles; |
| UINTN FvHandleCount; |
| EFI_HANDLE *FvHandleBuffer; |
| EFI_FV_FILETYPE Type; |
| UINTN Size; |
| EFI_FV_FILE_ATTRIBUTES Attributes; |
| UINT32 AuthenticationStatus; |
| #if (PI_SPECIFICATION_VERSION < 0x00010000) |
| EFI_FIRMWARE_VOLUME_PROTOCOL *Fv; |
| #else |
| EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; |
| #endif |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; |
| UINTN DevicePathType; |
| CHAR16 Buffer[40]; |
| EFI_HANDLE *FileSystemHandles; |
| UINTN NumberFileSystemHandles; |
| BOOLEAN NeedDelete; |
| EFI_IMAGE_DOS_HEADER DosHeader; |
| EFI_IMAGE_OPTIONAL_HEADER_UNION HdrData; |
| EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; |
| |
| FloppyNumber = 0; |
| CdromNumber = 0; |
| UsbNumber = 0; |
| MiscNumber = 0; |
| ZeroMem (Buffer, sizeof (Buffer)); |
| // |
| // If the boot device enumerate happened, just get the boot |
| // device from the boot order variable |
| // |
| if (mEnumBootDevice) { |
| BdsLibBuildOptionFromVar (BdsBootOptionList, L"BootOrder"); |
| return EFI_SUCCESS; |
| } |
| // |
| // Notes: this dirty code is to get the legacy boot option from the |
| // BBS table and create to variable as the EFI boot option, it should |
| // be removed after the CSM can provide legacy boot option directly |
| // |
| REFRESH_LEGACY_BOOT_OPTIONS; |
| |
| // |
| // Delete invalid boot option |
| // |
| BdsDeleteAllInvalidEfiBootOption (); |
| // |
| // Parse removable media |
| // |
| gBS->LocateHandleBuffer ( |
| ByProtocol, |
| &gEfiBlockIoProtocolGuid, |
| NULL, |
| &NumberBlockIoHandles, |
| &BlockIoHandles |
| ); |
| for (Index = 0; Index < NumberBlockIoHandles; Index++) { |
| Status = gBS->HandleProtocol ( |
| BlockIoHandles[Index], |
| &gEfiBlockIoProtocolGuid, |
| (VOID **) &BlkIo |
| ); |
| if (!EFI_ERROR (Status)) { |
| if (!BlkIo->Media->RemovableMedia) { |
| // |
| // skip the non-removable block devices |
| // |
| continue; |
| } |
| } |
| DevicePath = DevicePathFromHandle (BlockIoHandles[Index]); |
| DevicePathType = BdsGetBootTypeFromDevicePath (DevicePath); |
| |
| switch (DevicePathType) { |
| case BDS_EFI_ACPI_FLOPPY_BOOT: |
| if (FloppyNumber == 0) { |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Floppy"); |
| } else { |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Floppy %d", FloppyNumber); |
| } |
| BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer); |
| FloppyNumber++; |
| break; |
| |
| case BDS_EFI_MESSAGE_ATAPI_BOOT: |
| if (CdromNumber == 0) { |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI DVD/CDROM"); |
| } else { |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI DVD/CDROM %d", CdromNumber); |
| } |
| BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer); |
| CdromNumber++; |
| break; |
| |
| case BDS_EFI_MESSAGE_USB_DEVICE_BOOT: |
| if (UsbNumber == 0) { |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI USB Device"); |
| } else { |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI USB Device %d", UsbNumber); |
| } |
| BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer); |
| UsbNumber++; |
| break; |
| |
| case BDS_EFI_MESSAGE_SCSI_BOOT: |
| if (UsbNumber == 0) { |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI SCSI Device"); |
| } else { |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI SCSI Device %d", UsbNumber); |
| } |
| BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer); |
| UsbNumber++; |
| break; |
| |
| case BDS_EFI_MESSAGE_MISC_BOOT: |
| if (MiscNumber == 0) { |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Misc Device"); |
| } else { |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Misc Device %d", MiscNumber); |
| } |
| BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer); |
| MiscNumber++; |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| if (NumberBlockIoHandles) { |
| gBS->FreePool (BlockIoHandles); |
| } |
| |
| // |
| // If there is simple file protocol which does not consume block Io protocol, create a boot option for it here. |
| // |
| NonBlockNumber = 0; |
| gBS->LocateHandleBuffer ( |
| ByProtocol, |
| &gEfiSimpleFileSystemProtocolGuid, |
| NULL, |
| &NumberFileSystemHandles, |
| &FileSystemHandles |
| ); |
| for (Index = 0; Index < NumberFileSystemHandles; Index++) { |
| Status = gBS->HandleProtocol ( |
| FileSystemHandles[Index], |
| &gEfiBlockIoProtocolGuid, |
| (VOID **) &BlkIo |
| ); |
| if (!EFI_ERROR (Status)) { |
| // |
| // Skip if the file system handle supports a BlkIo protocol, |
| // |
| continue; |
| } |
| |
| // |
| // Do the removable Media thing. \EFI\BOOT\boot{machinename}.EFI |
| // machinename is ia32, ia64, x64, ... |
| // |
| Hdr.Union = &HdrData; |
| NeedDelete = TRUE; |
| Status = BdsLibGetImageHeader ( |
| FileSystemHandles[Index], |
| DEFAULT_REMOVABLE_FILE_NAME, |
| &DosHeader, |
| Hdr |
| ); |
| if (!EFI_ERROR (Status) && |
| EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Hdr.Pe32->FileHeader.Machine) && |
| Hdr.Pe32->OptionalHeader.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) { |
| NeedDelete = FALSE; |
| } |
| |
| if (NeedDelete) { |
| // |
| // No such file or the file is not a EFI application, delete this boot option |
| // |
| BdsLibDeleteOptionFromHandle (FileSystemHandles[Index]); |
| } else { |
| if (NonBlockNumber == 0) { |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Non-Block Boot Device"); |
| } else { |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Non-Block Boot Device %d", NonBlockNumber); |
| } |
| BdsLibBuildOptionFromHandle (FileSystemHandles[Index], BdsBootOptionList, Buffer); |
| NonBlockNumber++; |
| } |
| } |
| |
| if (NumberFileSystemHandles) { |
| gBS->FreePool (FileSystemHandles); |
| } |
| |
| // |
| // Parse Network Boot Device |
| // |
| gBS->LocateHandleBuffer ( |
| ByProtocol, |
| &gEfiSimpleNetworkProtocolGuid, |
| NULL, |
| &NumberSimpleNetworkHandles, |
| &SimpleNetworkHandles |
| ); |
| for (Index = 0; Index < NumberSimpleNetworkHandles; Index++) { |
| if (Index == 0) { |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Network"); |
| } else { |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Network %d", Index); |
| } |
| BdsLibBuildOptionFromHandle (SimpleNetworkHandles[Index], BdsBootOptionList, Buffer); |
| } |
| |
| if (NumberSimpleNetworkHandles) { |
| gBS->FreePool (SimpleNetworkHandles); |
| } |
| |
| // |
| // Check if we have on flash shell |
| // |
| gBS->LocateHandleBuffer ( |
| ByProtocol, |
| #if (PI_SPECIFICATION_VERSION < 0x00010000) |
| &gEfiFirmwareVolumeProtocolGuid, |
| #else |
| &gEfiFirmwareVolume2ProtocolGuid, |
| #endif |
| NULL, |
| &FvHandleCount, |
| &FvHandleBuffer |
| ); |
| for (Index = 0; Index < FvHandleCount; Index++) { |
| gBS->HandleProtocol ( |
| FvHandleBuffer[Index], |
| #if (PI_SPECIFICATION_VERSION < 0x00010000) |
| &gEfiFirmwareVolumeProtocolGuid, |
| #else |
| &gEfiFirmwareVolume2ProtocolGuid, |
| #endif |
| (VOID **) &Fv |
| ); |
| |
| Status = Fv->ReadFile ( |
| Fv, |
| &gEfiShellFileGuid, |
| NULL, |
| &Size, |
| &Type, |
| &Attributes, |
| &AuthenticationStatus |
| ); |
| if (EFI_ERROR (Status)) { |
| // |
| // Skip if no shell file in the FV |
| // |
| continue; |
| } |
| // |
| // Build the shell boot option |
| // |
| BdsLibBuildOptionFromShell (FvHandleBuffer[Index], BdsBootOptionList); |
| } |
| |
| if (FvHandleCount) { |
| gBS->FreePool (FvHandleBuffer); |
| } |
| // |
| // Make sure every boot only have one time |
| // boot device enumerate |
| // |
| BdsLibBuildOptionFromVar (BdsBootOptionList, L"BootOrder"); |
| mEnumBootDevice = TRUE; |
| |
| return EFI_SUCCESS; |
| } |
| |
| |
| /** |
| Build the boot option with the handle parsed in |
| |
| @param Handle The handle which present the device path to create |
| boot option |
| @param BdsBootOptionList The header of the link list which indexed all |
| current boot options |
| |
| @return VOID |
| |
| **/ |
| VOID |
| BdsLibBuildOptionFromHandle ( |
| IN EFI_HANDLE Handle, |
| IN LIST_ENTRY *BdsBootOptionList, |
| IN CHAR16 *String |
| ) |
| { |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; |
| |
| DevicePath = DevicePathFromHandle (Handle); |
| |
| // |
| // Create and register new boot option |
| // |
| BdsLibRegisterNewOption (BdsBootOptionList, DevicePath, String, L"BootOrder"); |
| } |
| |
| |
| /** |
| Build the on flash shell boot option with the handle parsed in |
| |
| @param Handle The handle which present the device path to create |
| on flash shell boot option |
| @param BdsBootOptionList The header of the link list which indexed all |
| current boot options |
| |
| @return None |
| |
| **/ |
| VOID |
| BdsLibBuildOptionFromShell ( |
| IN EFI_HANDLE Handle, |
| IN OUT LIST_ENTRY *BdsBootOptionList |
| ) |
| { |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; |
| MEDIA_FW_VOL_FILEPATH_DEVICE_PATH ShellNode; |
| |
| DevicePath = DevicePathFromHandle (Handle); |
| |
| // |
| // Build the shell device path |
| // |
| EfiInitializeFwVolDevicepathNode (&ShellNode, &gEfiShellFileGuid); |
| // |
| //ShellNode.Header.Type = MEDIA_DEVICE_PATH; |
| //ShellNode.Header.SubType = MEDIA_FV_FILEPATH_DP; |
| //SetDevicePathNodeLength (&ShellNode.Header, sizeof (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH)); |
| //CopyMem (&ShellNode.NameGuid, &gEfiShellFileGuid, sizeof (EFI_GUID)); |
| // |
| DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &ShellNode); |
| |
| // |
| // Create and register the shell boot option |
| // |
| BdsLibRegisterNewOption (BdsBootOptionList, DevicePath, L"EFI Internal Shell", L"BootOrder"); |
| |
| } |
| |
| |
| /** |
| Boot from the EFI1.1 spec defined "BootNext" variable |
| |
| None |
| |
| @return None |
| |
| **/ |
| VOID |
| BdsLibBootNext ( |
| VOID |
| ) |
| { |
| UINT16 *BootNext; |
| UINTN BootNextSize; |
| CHAR16 Buffer[20]; |
| BDS_COMMON_OPTION *BootOption; |
| LIST_ENTRY TempList; |
| UINTN ExitDataSize; |
| CHAR16 *ExitData; |
| |
| // |
| // Init the boot option name buffer and temp link list |
| // |
| InitializeListHead (&TempList); |
| ZeroMem (Buffer, sizeof (Buffer)); |
| |
| BootNext = BdsLibGetVariableAndSize ( |
| L"BootNext", |
| &gEfiGlobalVariableGuid, |
| &BootNextSize |
| ); |
| |
| // |
| // Clear the boot next variable first |
| // |
| if (BootNext != NULL) { |
| gRT->SetVariable ( |
| L"BootNext", |
| &gEfiGlobalVariableGuid, |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, |
| 0, |
| BootNext |
| ); |
| |
| // |
| // Start to build the boot option and try to boot |
| // |
| UnicodeSPrint (Buffer, sizeof (Buffer), L"Boot%04x", *BootNext); |
| BootOption = BdsLibVariableToOption (&TempList, Buffer); |
| BdsLibConnectDevicePath (BootOption->DevicePath); |
| BdsLibBootViaBootOption (BootOption, BootOption->DevicePath, &ExitDataSize, &ExitData); |
| } |
| |
| } |
| |
| |
| |
| /** |
| Return the bootable media handle. |
| First, check the device is connected |
| Second, check whether the device path point to a device which support SimpleFileSystemProtocol, |
| Third, detect the the default boot file in the Media, and return the removable Media handle. |
| |
| @param DevicePath Device Path to a bootable device |
| |
| @retval NULL The device path points to an EFI bootable Media |
| @retval NULL The media on the DevicePath is not bootable |
| |
| **/ |
| EFI_HANDLE |
| BdsLibGetBootableHandle ( |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath |
| ) |
| { |
| EFI_STATUS Status; |
| EFI_DEVICE_PATH_PROTOCOL *UpdatedDevicePath; |
| EFI_DEVICE_PATH_PROTOCOL *DupDevicePath; |
| EFI_HANDLE Handle; |
| EFI_BLOCK_IO_PROTOCOL *BlockIo; |
| VOID *Buffer; |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; |
| UINTN Size; |
| UINTN TempSize; |
| EFI_HANDLE ReturnHandle; |
| EFI_HANDLE *SimpleFileSystemHandles; |
| |
| UINTN NumberSimpleFileSystemHandles; |
| UINTN Index; |
| EFI_IMAGE_DOS_HEADER DosHeader; |
| EFI_IMAGE_OPTIONAL_HEADER_UNION HdrData; |
| EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; |
| |
| UpdatedDevicePath = DevicePath; |
| // |
| // Check whether the device is connected |
| // |
| Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &UpdatedDevicePath, &Handle); |
| if (EFI_ERROR (Status)) { |
| // |
| // Skip the case that the boot option point to a simple file protocol which does not consume block Io protocol, |
| // |
| Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &UpdatedDevicePath, &Handle); |
| if (EFI_ERROR (Status)) { |
| // |
| // Fail to find the proper BlockIo and simple file protocol, maybe because device not present, we need to connect it firstly |
| // |
| UpdatedDevicePath = DevicePath; |
| Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &UpdatedDevicePath, &Handle); |
| gBS->ConnectController (Handle, NULL, NULL, TRUE); |
| } |
| } else { |
| // |
| // Get BlockIo protocal and check removable attribute |
| // |
| Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo); |
| // |
| // Issue a dummy read to the device to check for media change. |
| // When the removable media is changed, any Block IO read/write will |
| // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is |
| // returned. After the Block IO protocol is reinstalled, subsequent |
| // Block IO read/write will success. |
| // |
| Buffer = AllocatePool (BlockIo->Media->BlockSize); |
| if (Buffer != NULL) { |
| BlockIo->ReadBlocks ( |
| BlockIo, |
| BlockIo->Media->MediaId, |
| 0, |
| BlockIo->Media->BlockSize, |
| Buffer |
| ); |
| gBS->FreePool (Buffer); |
| } |
| } |
| |
| // |
| // Detect the the default boot file from removable Media |
| // |
| |
| // |
| // If fail to get bootable handle specified by a USB boot option, the BDS should try to find other bootable device in the same USB bus |
| // Try to locate the USB node device path first, if fail then use its previour PCI node to search |
| // |
| DupDevicePath = DuplicateDevicePath (DevicePath); |
| UpdatedDevicePath = DupDevicePath; |
| Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &UpdatedDevicePath, &Handle); |
| // |
| // if the resulting device path point to a usb node, and the usb node is a dummy node, should only let device path only point to the previous Pci node |
| // Acpi()/Pci()/Usb() --> Acpi()/Pci() |
| // |
| if ((DevicePathType (UpdatedDevicePath) == MESSAGING_DEVICE_PATH) && |
| (DevicePathSubType (UpdatedDevicePath) == MSG_USB_DP)) { |
| // |
| // Remove the usb node, let the device path only point to PCI node |
| // |
| SetDevicePathEndNode (UpdatedDevicePath); |
| UpdatedDevicePath = DupDevicePath; |
| } else { |
| UpdatedDevicePath = DevicePath; |
| } |
| |
| // |
| // Get the device path size of boot option |
| // |
| Size = GetDevicePathSize(UpdatedDevicePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL); // minus the end node |
| ReturnHandle = NULL; |
| gBS->LocateHandleBuffer ( |
| ByProtocol, |
| &gEfiSimpleFileSystemProtocolGuid, |
| NULL, |
| &NumberSimpleFileSystemHandles, |
| &SimpleFileSystemHandles |
| ); |
| for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) { |
| // |
| // Get the device path size of SimpleFileSystem handle |
| // |
| TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]); |
| TempSize = GetDevicePathSize (TempDevicePath)- sizeof (EFI_DEVICE_PATH_PROTOCOL); // minus the end node |
| // |
| // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path |
| // |
| if (Size <= TempSize && CompareMem (TempDevicePath, UpdatedDevicePath, Size)==0) { |
| // |
| // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media |
| // machinename is ia32, ia64, x64, ... |
| // |
| Hdr.Union = &HdrData; |
| Status = BdsLibGetImageHeader ( |
| SimpleFileSystemHandles[Index], |
| DEFAULT_REMOVABLE_FILE_NAME, |
| &DosHeader, |
| Hdr |
| ); |
| if (!EFI_ERROR (Status) && |
| EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Hdr.Pe32->FileHeader.Machine) && |
| Hdr.Pe32->OptionalHeader.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) { |
| ReturnHandle = SimpleFileSystemHandles[Index]; |
| break; |
| } |
| } |
| } |
| |
| if (DupDevicePath != NULL) { |
| SafeFreePool(DupDevicePath); |
| } |
| if (SimpleFileSystemHandles !=NULL ) { |
| gBS->FreePool (SimpleFileSystemHandles); |
| } |
| |
| return ReturnHandle; |
| } |
| |
| |
| |
| |
| /** |
| Check to see if the network cable is plugged in. If the DevicePath is not |
| connected it will be connected. |
| |
| @param DevicePath Device Path to check |
| |
| @retval TRUE DevicePath points to an Network that is connected |
| @retval FALSE DevicePath does not point to a bootable network |
| |
| **/ |
| BOOLEAN |
| BdsLibNetworkBootWithMediaPresent ( |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath |
| ) |
| { |
| EFI_STATUS Status; |
| EFI_DEVICE_PATH_PROTOCOL *UpdatedDevicePath; |
| EFI_HANDLE Handle; |
| EFI_SIMPLE_NETWORK_PROTOCOL *Snp; |
| BOOLEAN MediaPresent; |
| |
| MediaPresent = FALSE; |
| |
| UpdatedDevicePath = DevicePath; |
| Status = gBS->LocateDevicePath (&gEfiSimpleNetworkProtocolGuid, &UpdatedDevicePath, &Handle); |
| if (EFI_ERROR (Status)) { |
| // |
| // Device not present so see if we need to connect it |
| // |
| Status = BdsLibConnectDevicePath (DevicePath); |
| if (!EFI_ERROR (Status)) { |
| // |
| // This one should work after we did the connect |
| // |
| Status = gBS->LocateDevicePath (&gEfiSimpleNetworkProtocolGuid, &UpdatedDevicePath, &Handle); |
| } |
| } |
| |
| if (!EFI_ERROR (Status)) { |
| Status = gBS->HandleProtocol (Handle, &gEfiSimpleNetworkProtocolGuid, (VOID **)&Snp); |
| if (!EFI_ERROR (Status)) { |
| if (Snp->Mode->MediaPresentSupported) { |
| if (Snp->Mode->State == EfiSimpleNetworkInitialized) { |
| // |
| // In case some one else is using the SNP check to see if it's connected |
| // |
| MediaPresent = Snp->Mode->MediaPresent; |
| } else { |
| // |
| // No one is using SNP so we need to Start and Initialize so |
| // MediaPresent will be valid. |
| // |
| Status = Snp->Start (Snp); |
| if (!EFI_ERROR (Status)) { |
| Status = Snp->Initialize (Snp, 0, 0); |
| if (!EFI_ERROR (Status)) { |
| MediaPresent = Snp->Mode->MediaPresent; |
| Snp->Shutdown (Snp); |
| } |
| Snp->Stop (Snp); |
| } |
| } |
| } else { |
| MediaPresent = TRUE; |
| } |
| } |
| } |
| |
| return MediaPresent; |
| } |
| |
| |
| |
| /** |
| For a bootable Device path, return its boot type |
| |
| @param DevicePath The bootable device Path to check |
| |
| @return UINT32 Boot type : |
| @return // |
| @return // If the device path contains any media deviec path node, it is media boot type |
| @return // For the floppy node, handle it as media node |
| @return // |
| @return BDS_EFI_MEDIA_HD_BOOT |
| @return BDS_EFI_MEDIA_CDROM_BOOT |
| @return BDS_EFI_ACPI_FLOPPY_BOOT |
| @return // |
| @return // If the device path not contains any media deviec path node, and |
| @return // its last device path node point to a message device path node, it is |
| @return // a message boot type |
| @return // |
| @return BDS_EFI_MESSAGE_ATAPI_BOOT |
| @return BDS_EFI_MESSAGE_SCSI_BOOT |
| @return BDS_EFI_MESSAGE_USB_DEVICE_BOOT |
| @return BDS_EFI_MESSAGE_MISC_BOOT |
| @return // |
| @return // Legacy boot type |
| @return // |
| @return BDS_LEGACY_BBS_BOOT |
| @return // |
| @return // If a EFI Removable BlockIO device path not point to a media and message devie, |
| @return // it is unsupported |
| @return // |
| @return BDS_EFI_UNSUPPORT |
| |
| **/ |
| UINT32 |
| BdsGetBootTypeFromDevicePath ( |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath |
| ) |
| { |
| ACPI_HID_DEVICE_PATH *Acpi; |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; |
| EFI_DEVICE_PATH_PROTOCOL *LastDeviceNode; |
| |
| |
| if (NULL == DevicePath) { |
| return BDS_EFI_UNSUPPORT; |
| } |
| |
| TempDevicePath = DevicePath; |
| |
| while (!IsDevicePathEndType (TempDevicePath)) { |
| switch (DevicePathType (TempDevicePath)) { |
| case BBS_DEVICE_PATH: |
| return BDS_LEGACY_BBS_BOOT; |
| case MEDIA_DEVICE_PATH: |
| if (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP) { |
| return BDS_EFI_MEDIA_HD_BOOT; |
| } else if (DevicePathSubType (TempDevicePath) == MEDIA_CDROM_DP) { |
| return BDS_EFI_MEDIA_CDROM_BOOT; |
| } |
| break; |
| case ACPI_DEVICE_PATH: |
| Acpi = (ACPI_HID_DEVICE_PATH *) TempDevicePath; |
| if (EISA_ID_TO_NUM (Acpi->HID) == 0x0604) { |
| return BDS_EFI_ACPI_FLOPPY_BOOT; |
| } |
| break; |
| case MESSAGING_DEVICE_PATH: |
| // |
| // if the device path not only point to driver device, it is not a messaging device path. |
| // |
| LastDeviceNode = NextDevicePathNode (TempDevicePath); |
| if (!IsDevicePathEndType (LastDeviceNode)) { |
| break; |
| } |
| |
| if (DevicePathSubType(TempDevicePath) == MSG_ATAPI_DP) { |
| return BDS_EFI_MESSAGE_ATAPI_BOOT; |
| } else if (DevicePathSubType(TempDevicePath) == MSG_USB_DP) { |
| return BDS_EFI_MESSAGE_USB_DEVICE_BOOT; |
| } else if (DevicePathSubType(TempDevicePath) == MSG_SCSI_DP) { |
| return BDS_EFI_MESSAGE_SCSI_BOOT; |
| } |
| return BDS_EFI_MESSAGE_MISC_BOOT; |
| default: |
| break; |
| } |
| TempDevicePath = NextDevicePathNode (TempDevicePath); |
| } |
| |
| return BDS_EFI_UNSUPPORT; |
| } |
| |
| |
| /** |
| Check whether the Device path in a boot option point to a valide bootable device, |
| And if CheckMedia is true, check the device is ready to boot now. |
| |
| DevPath -- the Device path in a boot option |
| CheckMedia -- if true, check the device is ready to boot now. |
| |
| @return TRUE -- the Device path is valide |
| @return FALSE -- the Device path is invalide . |
| |
| **/ |
| BOOLEAN |
| BdsLibIsValidEFIBootOptDevicePath ( |
| IN EFI_DEVICE_PATH_PROTOCOL *DevPath, |
| IN BOOLEAN CheckMedia |
| ) |
| { |
| EFI_STATUS Status; |
| EFI_HANDLE Handle; |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; |
| EFI_DEVICE_PATH_PROTOCOL *LastDeviceNode; |
| EFI_BLOCK_IO_PROTOCOL *BlockIo; |
| |
| TempDevicePath = DevPath; |
| LastDeviceNode = DevPath; |
| // |
| // Check if it's a valid boot option for network boot device |
| // Only check if there is SimpleNetworkProtocol installed. If yes, that means |
| // there is the network card there. |
| // |
| Status = gBS->LocateDevicePath ( |
| &gEfiSimpleNetworkProtocolGuid, |
| &TempDevicePath, |
| &Handle |
| ); |
| if (EFI_ERROR (Status)) { |
| // |
| // Device not present so see if we need to connect it |
| // |
| TempDevicePath = DevPath; |
| BdsLibConnectDevicePath (TempDevicePath); |
| Status = gBS->LocateDevicePath ( |
| &gEfiSimpleNetworkProtocolGuid, |
| &TempDevicePath, |
| &Handle |
| ); |
| } |
| if (!EFI_ERROR (Status)) { |
| if (CheckMedia) { |
| // |
| // Test if it is ready to boot now |
| // |
| if (BdsLibNetworkBootWithMediaPresent(DevPath)) { |
| return TRUE; |
| } |
| } else { |
| return TRUE; |
| } |
| } |
| |
| // |
| // If the boot option point to a file, it is a valid EFI boot option, |
| // and assume it is ready to boot now |
| // |
| while (!EfiIsDevicePathEnd (TempDevicePath)) { |
| LastDeviceNode = TempDevicePath; |
| TempDevicePath = EfiNextDevicePathNode (TempDevicePath); |
| } |
| if ((DevicePathType (LastDeviceNode) == MEDIA_DEVICE_PATH) && |
| (DevicePathSubType (LastDeviceNode) == MEDIA_FILEPATH_DP)) { |
| return TRUE; |
| } |
| |
| // |
| // If the boot option point to a internal Shell, it is a valid EFI boot option, |
| // and assume it is ready to boot now |
| // |
| if (EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode) != NULL) { |
| return TRUE; |
| } |
| |
| // |
| // If the boot option point to a blockIO device, no matter whether or not it is a removeable device, it is a valid EFI boot option |
| // |
| TempDevicePath = DevPath; |
| Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle); |
| if (EFI_ERROR (Status)) { |
| // |
| // Device not present so see if we need to connect it |
| // |
| Status = BdsLibConnectDevicePath (DevPath); |
| if (!EFI_ERROR (Status)) { |
| // |
| // Try again to get the Block Io protocol after we did the connect |
| // |
| TempDevicePath = DevPath; |
| Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle); |
| } |
| } |
| if (!EFI_ERROR (Status)) { |
| Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo); |
| if (!EFI_ERROR (Status)) { |
| if (CheckMedia) { |
| // |
| // Test if it is ready to boot now |
| // |
| if (BdsLibGetBootableHandle (DevPath) != NULL) { |
| return TRUE; |
| } |
| } else { |
| return TRUE; |
| } |
| } |
| } else { |
| // |
| // if the boot option point to a simple file protocol which does not consume block Io protocol, it is also a valid EFI boot option, |
| // |
| Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle); |
| if (!EFI_ERROR (Status)) { |
| if (CheckMedia) { |
| // |
| // Test if it is ready to boot now |
| // |
| if (BdsLibGetBootableHandle (DevPath) != NULL) { |
| return TRUE; |
| } |
| } else { |
| return TRUE; |
| } |
| } |
| } |
| |
| return FALSE; |
| } |
| |
| |
| /** |
| According to a file guild, check a Fv file device path is valid. If it is invalid, |
| try to return the valid device path. |
| FV address maybe changes for memory layout adjust from time to time, use this funciton |
| could promise the Fv file device path is right. |
| |
| @param DevicePath on input, the Fv file device path need to check on |
| output, the updated valid Fv file device path |
| @param FileGuid the Fv file guild |
| |
| @retval EFI_INVALID_PARAMETER the input DevicePath or FileGuid is invalid |
| parameter |
| @retval EFI_UNSUPPORTED the input DevicePath does not contain Fv file |
| guild at all |
| @retval EFI_ALREADY_STARTED the input DevicePath has pointed to Fv file, it is |
| valid |
| @retval EFI_SUCCESS has successfully updated the invalid DevicePath, |
| and return the updated device path in DevicePath |
| |
| **/ |
| EFI_STATUS |
| EFIAPI |
| BdsLibUpdateFvFileDevicePath ( |
| IN OUT EFI_DEVICE_PATH_PROTOCOL ** DevicePath, |
| IN EFI_GUID *FileGuid |
| ) |
| { |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; |
| EFI_DEVICE_PATH_PROTOCOL *LastDeviceNode; |
| EFI_STATUS Status; |
| EFI_GUID *GuidPoint; |
| UINTN Index; |
| UINTN FvHandleCount; |
| EFI_HANDLE *FvHandleBuffer; |
| EFI_FV_FILETYPE Type; |
| UINTN Size; |
| EFI_FV_FILE_ATTRIBUTES Attributes; |
| UINT32 AuthenticationStatus; |
| BOOLEAN FindFvFile; |
| EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; |
| #if (PI_SPECIFICATION_VERSION < 0x00010000) |
| EFI_FIRMWARE_VOLUME_PROTOCOL *Fv; |
| #else |
| EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; |
| #endif |
| MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FvFileNode; |
| EFI_HANDLE FoundFvHandle; |
| EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; |
| |
| if ((DevicePath == NULL) || (*DevicePath == NULL)) { |
| return EFI_INVALID_PARAMETER; |
| } |
| if (FileGuid == NULL) { |
| return EFI_INVALID_PARAMETER; |
| } |
| // |
| // Check whether the device path point to the default the input Fv file |
| // |
| TempDevicePath = *DevicePath; |
| LastDeviceNode = TempDevicePath; |
| while (!EfiIsDevicePathEnd (TempDevicePath)) { |
| LastDeviceNode = TempDevicePath; |
| TempDevicePath = EfiNextDevicePathNode (TempDevicePath); |
| } |
| GuidPoint = EfiGetNameGuidFromFwVolDevicePathNode ( |
| (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode |
| ); |
| if (GuidPoint == NULL) { |
| // |
| // if this option does not points to a Fv file, just return EFI_UNSUPPORTED |
| // |
| return EFI_UNSUPPORTED; |
| } |
| if (!CompareGuid (GuidPoint, FileGuid)) { |
| // |
| // If the Fv file is not the input file guid, just return EFI_UNSUPPORTED |
| // |
| return EFI_UNSUPPORTED; |
| } |
| |
| // |
| // Check whether the input Fv file device path is valid |
| // |
| TempDevicePath = *DevicePath; |
| FoundFvHandle = NULL; |
| Status = gBS->LocateDevicePath ( |
| #if (PI_SPECIFICATION_VERSION < 0x00010000) |
| &gEfiFirmwareVolumeProtocolGuid, |
| #else |
| &gEfiFirmwareVolume2ProtocolGuid, |
| #endif |
| &TempDevicePath, |
| &FoundFvHandle |
| ); |
| if (!EFI_ERROR (Status)) { |
| Status = gBS->HandleProtocol ( |
| FoundFvHandle, |
| #if (PI_SPECIFICATION_VERSION < 0x00010000) |
| &gEfiFirmwareVolumeProtocolGuid, |
| #else |
| &gEfiFirmwareVolume2ProtocolGuid, |
| #endif |
| (VOID **) &Fv |
| ); |
| if (!EFI_ERROR (Status)) { |
| // |
| // Set FV ReadFile Buffer as NULL, only need to check whether input Fv file exist there |
| // |
| Status = Fv->ReadFile ( |
| Fv, |
| FileGuid, |
| NULL, |
| &Size, |
| &Type, |
| &Attributes, |
| &AuthenticationStatus |
| ); |
| if (!EFI_ERROR (Status)) { |
| return EFI_ALREADY_STARTED; |
| } |
| } |
| } |
| |
| // |
| // Look for the input wanted FV file in current FV |
| // First, try to look for in Bds own FV. Bds and input wanted FV file usually are in the same FV |
| // |
| FindFvFile = FALSE; |
| FoundFvHandle = NULL; |
| Status = gBS->HandleProtocol ( |
| mBdsImageHandle, |
| &gEfiLoadedImageProtocolGuid, |
| (VOID **) &LoadedImage |
| ); |
| if (!EFI_ERROR (Status)) { |
| Status = gBS->HandleProtocol ( |
| LoadedImage->DeviceHandle, |
| #if (PI_SPECIFICATION_VERSION < 0x00010000) |
| &gEfiFirmwareVolumeProtocolGuid, |
| #else |
| &gEfiFirmwareVolume2ProtocolGuid, |
| #endif |
| (VOID **) &Fv |
| ); |
| if (!EFI_ERROR (Status)) { |
| Status = Fv->ReadFile ( |
| Fv, |
| FileGuid, |
| NULL, |
| &Size, |
| &Type, |
| &Attributes, |
| &AuthenticationStatus |
| ); |
| if (!EFI_ERROR (Status)) { |
| FindFvFile = TRUE; |
| FoundFvHandle = LoadedImage->DeviceHandle; |
| } |
| } |
| } |
| // |
| // Second, if fail to find, try to enumerate all FV |
| // |
| if (!FindFvFile) { |
| gBS->LocateHandleBuffer ( |
| ByProtocol, |
| #if (PI_SPECIFICATION_VERSION < 0x00010000) |
| &gEfiFirmwareVolumeProtocolGuid, |
| #else |
| &gEfiFirmwareVolume2ProtocolGuid, |
| #endif |
| NULL, |
| &FvHandleCount, |
| &FvHandleBuffer |
| ); |
| for (Index = 0; Index < FvHandleCount; Index++) { |
| gBS->HandleProtocol ( |
| FvHandleBuffer[Index], |
| #if (PI_SPECIFICATION_VERSION < 0x00010000) |
| &gEfiFirmwareVolumeProtocolGuid, |
| #else |
| &gEfiFirmwareVolume2ProtocolGuid, |
| #endif |
| (VOID **) &Fv |
| ); |
| |
| Status = Fv->ReadFile ( |
| Fv, |
| FileGuid, |
| NULL, |
| &Size, |
| &Type, |
| &Attributes, |
| &AuthenticationStatus |
| ); |
| if (EFI_ERROR (Status)) { |
| // |
| // Skip if input Fv file not in the FV |
| // |
| continue; |
| } |
| FindFvFile = TRUE; |
| FoundFvHandle = FvHandleBuffer[Index]; |
| break; |
| } |
| } |
| |
| if (FindFvFile) { |
| // |
| // Build the shell device path |
| // |
| NewDevicePath = DevicePathFromHandle (FoundFvHandle); |
| EfiInitializeFwVolDevicepathNode (&FvFileNode, FileGuid); |
| NewDevicePath = AppendDevicePathNode (NewDevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &FvFileNode); |
| *DevicePath = NewDevicePath; |
| return EFI_SUCCESS; |
| } |
| return EFI_NOT_FOUND; |
| } |