MdeModulePkg/UefiBootManagerLib: Enhance short-form expanding logic
Old implementation only finds first matched full device path for a given short-form device path. The patch adds internal function BmGetNextLoadOptionBuffer() to finds all matched full device path for a given short-form device path. There are 6 kinds of device paths. Some of them match to multiple load options, some of them don't. 1. Media device path: Returns multiple load options: The media device path may point to a physical BlockIo which contains multiple logic partitions, each logic partitions contains \EFI\BOOT\BOOT${ARCH}.EFI. 2. Short-form hard-drive device path: Returns one load option because the partition signature is unique. 3. Short-form file-path device path: Returns multiple load options: There are multiple SimpleFileSystem instances and each contains the same file. 4. Short-form URI device path: Returns multiple load options: There are multiple LoadFile instances and each can boot. 5. Short-form USB device path: Returns multiple load options: There are multiple UsbIo instances and each contains the boot-able file. 6. FV device path, device path pointing to SimpleFileSystem, device path pointing to LoadFile Returns one load option. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ruiyu Ni <ruiyu.ni@intel.com> Reviewed-by: Feng Tian <feng.tian@intel.com> Cc: Eric Dong <eric.dong@intel.com> Cc: Jeff Fan <jeff.fan@intel.com>
This commit is contained in:
@ -1167,6 +1167,10 @@ EfiBootManagerFreeLoadOptions (
|
||||
Return whether the PE header of the load option is valid or not.
|
||||
|
||||
@param[in] Type The load option type.
|
||||
It's used to check whether the load option is valid.
|
||||
When it's LoadOptionTypeMax, the routine only guarantees
|
||||
the load option is a valid PE image but doesn't guarantee
|
||||
the PE's subsystem type is valid.
|
||||
@param[in] FileBuffer The PE file buffer of the load option.
|
||||
@param[in] FileSize The size of the load option file.
|
||||
|
||||
@ -1217,7 +1221,8 @@ BmIsLoadOptionPeHeaderValid (
|
||||
// SysPrep####, Boot####, OsRecovery####, PlatformRecovery#### must be of type Application
|
||||
//
|
||||
Subsystem = OptionalHeader->Subsystem;
|
||||
if ((Type == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) ||
|
||||
if ((Type == LoadOptionTypeMax) ||
|
||||
(Type == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) ||
|
||||
(Type == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) ||
|
||||
(Type == LoadOptionTypeSysPrep && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) ||
|
||||
(Type == LoadOptionTypeBoot && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) ||
|
||||
@ -1232,6 +1237,91 @@ BmIsLoadOptionPeHeaderValid (
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
Return the next matched load option buffer.
|
||||
The routine keeps calling BmGetNextLoadOptionDevicePath() until a valid
|
||||
load option is read.
|
||||
|
||||
@param Type The load option type.
|
||||
It's used to check whether the load option is valid.
|
||||
When it's LoadOptionTypeMax, the routine only guarantees
|
||||
the load option is a valid PE image but doesn't guarantee
|
||||
the PE's subsystem type is valid.
|
||||
@param FilePath The device path pointing to a load option.
|
||||
It could be a short-form device path.
|
||||
@param FullPath Return the next full device path of the load option after
|
||||
short-form device path expanding.
|
||||
Caller is responsible to free it.
|
||||
NULL to return the first matched full device path.
|
||||
@param FileSize Return the load option size.
|
||||
|
||||
@return The load option buffer. Caller is responsible to free the memory.
|
||||
**/
|
||||
VOID *
|
||||
BmGetNextLoadOptionBuffer (
|
||||
IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,
|
||||
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
|
||||
OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
|
||||
OUT UINTN *FileSize
|
||||
)
|
||||
{
|
||||
VOID *FileBuffer;
|
||||
EFI_DEVICE_PATH_PROTOCOL *PreFullPath;
|
||||
EFI_DEVICE_PATH_PROTOCOL *CurFullPath;
|
||||
UINTN LocalFileSize;
|
||||
UINT32 AuthenticationStatus;
|
||||
EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;
|
||||
|
||||
LocalFileSize = 0;
|
||||
FileBuffer = NULL;
|
||||
CurFullPath = *FullPath;
|
||||
do {
|
||||
PreFullPath = CurFullPath;
|
||||
CurFullPath = BmGetNextLoadOptionDevicePath (FilePath, CurFullPath);
|
||||
//
|
||||
// Only free the full path created *inside* this routine
|
||||
//
|
||||
if ((PreFullPath != NULL) && (PreFullPath != *FullPath)) {
|
||||
FreePool (PreFullPath);
|
||||
}
|
||||
if (CurFullPath == NULL) {
|
||||
break;
|
||||
}
|
||||
FileBuffer = GetFileBufferByFilePath (TRUE, CurFullPath, &LocalFileSize, &AuthenticationStatus);
|
||||
if ((FileBuffer != NULL) && !BmIsLoadOptionPeHeaderValid (Type, FileBuffer, LocalFileSize)) {
|
||||
//
|
||||
// Free the RAM disk file system if the load option is invalid.
|
||||
//
|
||||
RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath);
|
||||
if (RamDiskDevicePath != NULL) {
|
||||
BmDestroyRamDisk (RamDiskDevicePath);
|
||||
FreePool (RamDiskDevicePath);
|
||||
}
|
||||
|
||||
//
|
||||
// Free the invalid load option buffer.
|
||||
//
|
||||
FreePool (FileBuffer);
|
||||
FileBuffer = NULL;
|
||||
}
|
||||
} while (FileBuffer == NULL);
|
||||
|
||||
if (FileBuffer == NULL) {
|
||||
CurFullPath = NULL;
|
||||
LocalFileSize = 0;
|
||||
}
|
||||
|
||||
DEBUG ((DEBUG_INFO, "[Bds] Expand "));
|
||||
BmPrintDp (FilePath);
|
||||
DEBUG ((DEBUG_INFO, " -> "));
|
||||
BmPrintDp (CurFullPath);
|
||||
DEBUG ((DEBUG_INFO, "\n"));
|
||||
|
||||
*FullPath = CurFullPath;
|
||||
*FileSize = LocalFileSize;
|
||||
return FileBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
Process (load and execute) the load option.
|
||||
|
||||
@ -1249,7 +1339,8 @@ EfiBootManagerProcessLoadOption (
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_DEVICE_PATH_PROTOCOL *FilePath;
|
||||
EFI_DEVICE_PATH_PROTOCOL *PreFullPath;
|
||||
EFI_DEVICE_PATH_PROTOCOL *CurFullPath;
|
||||
EFI_HANDLE ImageHandle;
|
||||
EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
|
||||
VOID *FileBuffer;
|
||||
@ -1271,8 +1362,6 @@ EfiBootManagerProcessLoadOption (
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
|
||||
//
|
||||
// Load and start the load option.
|
||||
//
|
||||
@ -1282,54 +1371,62 @@ EfiBootManagerProcessLoadOption (
|
||||
LoadOption->Description
|
||||
));
|
||||
ImageHandle = NULL;
|
||||
FileBuffer = EfiBootManagerGetLoadOptionBuffer (LoadOption->FilePath, &FilePath, &FileSize);
|
||||
DEBUG_CODE (
|
||||
if (FileBuffer != NULL && CompareMem (LoadOption->FilePath, FilePath, GetDevicePathSize (FilePath)) != 0) {
|
||||
DEBUG ((EFI_D_INFO, "[Bds] DevicePath expand: "));
|
||||
BmPrintDp (LoadOption->FilePath);
|
||||
DEBUG ((EFI_D_INFO, " -> "));
|
||||
BmPrintDp (FilePath);
|
||||
DEBUG ((EFI_D_INFO, "\n"));
|
||||
CurFullPath = NULL;
|
||||
EfiBootManagerConnectDevicePath (LoadOption->FilePath, NULL);
|
||||
|
||||
//
|
||||
// while() loop is to keep starting next matched load option if the PlatformRecovery#### returns failure status.
|
||||
//
|
||||
while (TRUE) {
|
||||
Status = EFI_INVALID_PARAMETER;
|
||||
PreFullPath = CurFullPath;
|
||||
FileBuffer = BmGetNextLoadOptionBuffer (LoadOption->OptionType, LoadOption->FilePath, &CurFullPath, &FileSize);
|
||||
if (PreFullPath != NULL) {
|
||||
FreePool (PreFullPath);
|
||||
}
|
||||
if (FileBuffer == NULL) {
|
||||
break;
|
||||
}
|
||||
);
|
||||
if (BmIsLoadOptionPeHeaderValid (LoadOption->OptionType, FileBuffer, FileSize)) {
|
||||
Status = gBS->LoadImage (
|
||||
FALSE,
|
||||
gImageHandle,
|
||||
FilePath,
|
||||
CurFullPath,
|
||||
FileBuffer,
|
||||
FileSize,
|
||||
&ImageHandle
|
||||
);
|
||||
}
|
||||
if (FilePath != NULL) {
|
||||
FreePool (FilePath);
|
||||
}
|
||||
if (FileBuffer != NULL) {
|
||||
FreePool (FileBuffer);
|
||||
}
|
||||
|
||||
if (!EFI_ERROR (Status)) {
|
||||
Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);
|
||||
ASSERT_EFI_ERROR (Status);
|
||||
if (!EFI_ERROR (Status)) {
|
||||
Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&ImageInfo);
|
||||
ASSERT_EFI_ERROR (Status);
|
||||
|
||||
ImageInfo->LoadOptionsSize = LoadOption->OptionalDataSize;
|
||||
ImageInfo->LoadOptions = LoadOption->OptionalData;
|
||||
//
|
||||
// Before calling the image, enable the Watchdog Timer for the 5-minute period
|
||||
//
|
||||
gBS->SetWatchdogTimer (5 * 60, 0, 0, NULL);
|
||||
ImageInfo->LoadOptionsSize = LoadOption->OptionalDataSize;
|
||||
ImageInfo->LoadOptions = LoadOption->OptionalData;
|
||||
//
|
||||
// Before calling the image, enable the Watchdog Timer for the 5-minute period
|
||||
//
|
||||
gBS->SetWatchdogTimer (5 * 60, 0, 0, NULL);
|
||||
|
||||
LoadOption->Status = gBS->StartImage (ImageHandle, &LoadOption->ExitDataSize, &LoadOption->ExitData);
|
||||
DEBUG ((
|
||||
DEBUG_INFO | DEBUG_LOAD, "%s%04x Return Status = %r\n",
|
||||
mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber, LoadOption->Status
|
||||
LoadOption->Status = gBS->StartImage (ImageHandle, &LoadOption->ExitDataSize, &LoadOption->ExitData);
|
||||
DEBUG ((
|
||||
DEBUG_INFO | DEBUG_LOAD, "%s%04x Return Status = %r\n",
|
||||
mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber, LoadOption->Status
|
||||
));
|
||||
|
||||
//
|
||||
// Clear the Watchdog Timer after the image returns
|
||||
//
|
||||
gBS->SetWatchdogTimer (0, 0, 0, NULL);
|
||||
//
|
||||
// Clear the Watchdog Timer after the image returns
|
||||
//
|
||||
gBS->SetWatchdogTimer (0, 0, 0, NULL);
|
||||
|
||||
if ((LoadOption->OptionType != LoadOptionTypePlatformRecovery) || (LoadOption->Status == EFI_SUCCESS)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CurFullPath != NULL) {
|
||||
FreePool (CurFullPath);
|
||||
}
|
||||
|
||||
return Status;
|
||||
|
Reference in New Issue
Block a user