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:
Ruiyu Ni 2017-03-29 10:55:43 +08:00
parent dd56374250
commit 08eff91788
3 changed files with 508 additions and 301 deletions

View File

@ -131,21 +131,16 @@ BmFindBootOptionInVariable (
}
/**
Get the file buffer using a Memory Mapped Device Path.
Return the correct FV file path.
FV address may change across reboot. This routine promises the FV file device path is right.
@param FilePath The Memory Mapped Device Path to get the file buffer.
@param FullPath Receive the updated FV Device Path pointint to the file.
@param FileSize Receive the file buffer size.
@return The file buffer.
@return The updated FV Device Path pointint to the file.
**/
VOID *
BmGetFileBufferByFvFilePath (
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
OUT UINTN *FileSize
EFI_DEVICE_PATH_PROTOCOL *
BmAdjustFvFilePath (
IN EFI_DEVICE_PATH_PROTOCOL *FilePath
)
{
EFI_STATUS Status;
@ -153,11 +148,10 @@ BmGetFileBufferByFvFilePath (
EFI_DEVICE_PATH_PROTOCOL *FvFileNode;
EFI_HANDLE FvHandle;
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
UINT32 AuthenticationStatus;
UINTN FvHandleCount;
EFI_HANDLE *FvHandles;
EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
VOID *FileBuffer;
EFI_DEVICE_PATH_PROTOCOL *FullPath;
//
// Get the file buffer by using the exactly FilePath.
@ -165,11 +159,7 @@ BmGetFileBufferByFvFilePath (
FvFileNode = FilePath;
Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &FvFileNode, &FvHandle);
if (!EFI_ERROR (Status)) {
FileBuffer = GetFileBufferByFilePath (TRUE, FilePath, FileSize, &AuthenticationStatus);
if (FileBuffer != NULL) {
*FullPath = DuplicateDevicePath (FilePath);
}
return FileBuffer;
return DuplicateDevicePath (FilePath);
}
//
@ -190,11 +180,10 @@ BmGetFileBufferByFvFilePath (
(VOID **) &LoadedImage
);
NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), FvFileNode);
FileBuffer = BmGetFileBufferByFvFilePath (NewDevicePath, FullPath, FileSize);
FullPath = BmAdjustFvFilePath (NewDevicePath);
FreePool (NewDevicePath);
if (FileBuffer != NULL) {
return FileBuffer;
if (FullPath != NULL) {
return FullPath;
}
//
@ -207,22 +196,25 @@ BmGetFileBufferByFvFilePath (
&FvHandleCount,
&FvHandles
);
for (Index = 0; (Index < FvHandleCount) && (FileBuffer == NULL); Index++) {
for (Index = 0; Index < FvHandleCount; Index++) {
if (FvHandles[Index] == LoadedImage->DeviceHandle) {
//
// Skip current FV
// Skip current FV, it was handed in first step.
//
continue;
}
NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandles[Index]), FvFileNode);
FileBuffer = BmGetFileBufferByFvFilePath (NewDevicePath, FullPath, FileSize);
FullPath = BmAdjustFvFilePath (NewDevicePath);
FreePool (NewDevicePath);
if (FullPath != NULL) {
break;
}
}
if (FvHandles != NULL) {
FreePool (FvHandles);
}
return FileBuffer;
return FullPath;
}
/**
@ -555,46 +547,64 @@ BmFindUsbDevice (
@param FilePath The device path pointing to a load option.
It could be a short-form device path.
@param FullPath Return the full device path of the load option after
short-form device path expanding.
Caller is responsible to free it.
@param FileSize Return the load option size.
@param FullPath The full path returned by the routine in last call.
Set to NULL in first call.
@param ShortformNode Pointer to the USB short-form device path node in the FilePath buffer.
@return The load option buffer. Caller is responsible to free the memory.
@return The next possible full path pointing to the load option.
Caller is responsible to free the memory.
**/
VOID *
EFI_DEVICE_PATH_PROTOCOL *
BmExpandUsbDevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
OUT UINTN *FileSize,
IN EFI_DEVICE_PATH_PROTOCOL *ShortformNode
IN EFI_DEVICE_PATH_PROTOCOL *FullPath,
IN EFI_DEVICE_PATH_PROTOCOL *ShortformNode
)
{
UINTN ParentDevicePathSize;
EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;
EFI_DEVICE_PATH_PROTOCOL *FullDevicePath;
EFI_DEVICE_PATH_PROTOCOL *NextFullPath;
EFI_HANDLE *Handles;
UINTN HandleCount;
UINTN Index;
VOID *FileBuffer;
BOOLEAN GetNext;
NextFullPath = NULL;
GetNext = (BOOLEAN)(FullPath == NULL);
ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) FilePath;
RemainingDevicePath = NextDevicePathNode (ShortformNode);
FileBuffer = NULL;
Handles = BmFindUsbDevice (FilePath, ParentDevicePathSize, &HandleCount);
for (Index = 0; (Index < HandleCount) && (FileBuffer == NULL); Index++) {
FullDevicePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath);
FileBuffer = EfiBootManagerGetLoadOptionBuffer (FullDevicePath, FullPath, FileSize);
FreePool (FullDevicePath);
for (Index = 0; Index < HandleCount; Index++) {
FilePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath);
if (FilePath == NULL) {
//
// Out of memory.
//
continue;
}
NextFullPath = BmGetNextLoadOptionDevicePath (FilePath, NULL);
FreePool (FilePath);
if (NextFullPath == NULL) {
//
// No BlockIo or SimpleFileSystem under FilePath.
//
continue;
}
if (GetNext) {
break;
} else {
GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
FreePool (NextFullPath);
NextFullPath = NULL;
}
}
if (Handles != NULL) {
FreePool (Handles);
}
return FileBuffer;
return NextFullPath;
}
/**
@ -602,18 +612,16 @@ BmExpandUsbDevicePath (
@param FilePath The device path pointing to a load option.
It could be a short-form device path.
@param FullPath Return the full device path of the load option after
short-form device path expanding.
Caller is responsible to free it.
@param FileSize Return the load option size.
@param FullPath The full path returned by the routine in last call.
Set to NULL in first call.
@return The load option buffer. Caller is responsible to free the memory.
@return The next possible full path pointing to the load option.
Caller is responsible to free the memory.
**/
VOID *
EFI_DEVICE_PATH_PROTOCOL *
BmExpandFileDevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
OUT UINTN *FileSize
IN EFI_DEVICE_PATH_PROTOCOL *FullPath
)
{
EFI_STATUS Status;
@ -622,9 +630,8 @@ BmExpandFileDevicePath (
EFI_HANDLE *Handles;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
UINTN MediaType;
EFI_DEVICE_PATH_PROTOCOL *FullDevicePath;
VOID *FileBuffer;
UINT32 AuthenticationStatus;
EFI_DEVICE_PATH_PROTOCOL *NextFullPath;
BOOLEAN GetNext;
EfiBootManagerConnectAll ();
Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &HandleCount, &Handles);
@ -633,6 +640,8 @@ BmExpandFileDevicePath (
Handles = NULL;
}
GetNext = (BOOLEAN)(FullPath == NULL);
NextFullPath = NULL;
//
// Enumerate all removable media devices followed by all fixed media devices,
// followed by media devices which don't layer on block io.
@ -647,62 +656,17 @@ BmExpandFileDevicePath (
(MediaType == 1 && BlockIo != NULL && !BlockIo->Media->RemovableMedia) ||
(MediaType == 2 && BlockIo == NULL)
) {
FullDevicePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), FilePath);
FileBuffer = GetFileBufferByFilePath (TRUE, FullDevicePath, FileSize, &AuthenticationStatus);
if (FileBuffer != NULL) {
*FullPath = FullDevicePath;
FreePool (Handles);
return FileBuffer;
NextFullPath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), FilePath);
if (GetNext) {
break;
} else {
GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
FreePool (NextFullPath);
NextFullPath = NULL;
}
FreePool (FullDevicePath);
}
}
}
if (Handles != NULL) {
FreePool (Handles);
}
*FullPath = NULL;
return NULL;
}
/**
Expand URI device path node to be full device path in platform.
@param FilePath The device path pointing to a load option.
It could be a short-form device path.
@param FullPath Return the full device path of the load option after
short-form device path expanding.
Caller is responsible to free it.
@param FileSize Return the load option size.
@return The load option buffer. Caller is responsible to free the memory.
**/
VOID *
BmExpandUriDevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
OUT UINTN *FileSize
)
{
EFI_STATUS Status;
UINTN Index;
UINTN HandleCount;
EFI_HANDLE *Handles;
VOID *FileBuffer;
EfiBootManagerConnectAll ();
Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &HandleCount, &Handles);
if (EFI_ERROR (Status)) {
HandleCount = 0;
Handles = NULL;
}
FileBuffer = NULL;
for (Index = 0; Index < HandleCount; Index++) {
FileBuffer = BmGetFileBufferFromLoadFile (Handles[Index], FilePath, FullPath, FileSize);
if (FileBuffer != NULL) {
if (NextFullPath != NULL) {
break;
}
}
@ -711,7 +675,72 @@ BmExpandUriDevicePath (
FreePool (Handles);
}
return FileBuffer;
return NextFullPath;
}
/**
Expand URI device path node to be full device path in platform.
@param FilePath The device path pointing to a load option.
It could be a short-form device path.
@param FullPath The full path returned by the routine in last call.
Set to NULL in first call.
@return The next possible full path pointing to the load option.
Caller is responsible to free the memory.
**/
EFI_DEVICE_PATH_PROTOCOL *
BmExpandUriDevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
IN EFI_DEVICE_PATH_PROTOCOL *FullPath
)
{
EFI_STATUS Status;
UINTN Index;
UINTN HandleCount;
EFI_HANDLE *Handles;
EFI_DEVICE_PATH_PROTOCOL *NextFullPath;
EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;
BOOLEAN GetNext;
EfiBootManagerConnectAll ();
Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &HandleCount, &Handles);
if (EFI_ERROR (Status)) {
HandleCount = 0;
Handles = NULL;
}
NextFullPath = NULL;
GetNext = (BOOLEAN)(FullPath == NULL);
for (Index = 0; Index < HandleCount; Index++) {
NextFullPath = BmExpandLoadFile (Handles[Index], FilePath);
if (NextFullPath == NULL) {
continue;
}
if (GetNext) {
break;
} else {
GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
//
// Free the resource occupied by the RAM disk.
//
RamDiskDevicePath = BmGetRamDiskDevicePath (NextFullPath);
if (RamDiskDevicePath != NULL) {
BmDestroyRamDisk (RamDiskDevicePath);
FreePool (RamDiskDevicePath);
}
FreePool (NextFullPath);
NextFullPath = NULL;
}
}
if (Handles != NULL) {
FreePool (Handles);
}
return NextFullPath;
}
/**
@ -781,35 +810,28 @@ BmCachePartitionDevicePath (
@param FilePath The device path pointing to a load option.
It could be a short-form device path.
@param FullPath Return the full device path of the load option after
short-form device path expanding.
Caller is responsible to free it.
@param FileSize Return the load option size.
@return The load option buffer. Caller is responsible to free the memory.
@return The full device path pointing to the load option.
**/
VOID *
EFI_DEVICE_PATH_PROTOCOL *
BmExpandPartitionDevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
OUT UINTN *FileSize
IN EFI_DEVICE_PATH_PROTOCOL *FilePath
)
{
EFI_STATUS Status;
UINTN BlockIoHandleCount;
EFI_HANDLE *BlockIoBuffer;
VOID *FileBuffer;
EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath;
UINTN Index;
EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath;
EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath;
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
EFI_DEVICE_PATH_PROTOCOL *FullPath;
UINTN CachedDevicePathSize;
BOOLEAN NeedAdjust;
EFI_DEVICE_PATH_PROTOCOL *Instance;
UINTN Size;
FileBuffer = NULL;
//
// Check if there is prestore 'HDDP' variable.
// If exist, search the front path which point to partition node in the variable instants.
@ -833,6 +855,7 @@ BmExpandPartitionDevicePath (
ASSERT_EFI_ERROR (Status);
}
FullPath = NULL;
if (CachedDevicePath != NULL) {
TempNewDevicePath = CachedDevicePath;
NeedAdjust = FALSE;
@ -851,10 +874,20 @@ BmExpandPartitionDevicePath (
Status = EfiBootManagerConnectDevicePath (Instance, NULL);
if (!EFI_ERROR (Status)) {
TempDevicePath = AppendDevicePath (Instance, NextDevicePathNode (FilePath));
FileBuffer = EfiBootManagerGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize);
//
// TempDevicePath = ACPI()/PCI()/ATA()/Partition()
// or = ACPI()/PCI()/ATA()/Partition()/.../A.EFI
//
// When TempDevicePath = ACPI()/PCI()/ATA()/Partition(),
// it may expand to two potienal full paths (nested partition, rarely happen):
// 1. ACPI()/PCI()/ATA()/Partition()/Partition(A1)/EFI/BootX64.EFI
// 2. ACPI()/PCI()/ATA()/Partition()/Partition(A2)/EFI/BootX64.EFI
// For simplicity, only #1 is returned.
//
FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL);
FreePool (TempDevicePath);
if (FileBuffer != NULL) {
if (FullPath != NULL) {
//
// Adjust the 'HDDP' instances sequence if the matched one is not first one.
//
@ -875,7 +908,7 @@ BmExpandPartitionDevicePath (
FreePool (Instance);
FreePool (CachedDevicePath);
return FileBuffer;
return FullPath;
}
}
}
@ -911,10 +944,10 @@ BmExpandPartitionDevicePath (
// Find the matched partition device path
//
TempDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (FilePath));
FileBuffer = EfiBootManagerGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize);
FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL);
FreePool (TempDevicePath);
if (FileBuffer != NULL) {
if (FullPath != NULL) {
BmCachePartitionDevicePath (&CachedDevicePath, BlockIoDevicePath);
//
@ -940,7 +973,7 @@ BmExpandPartitionDevicePath (
if (BlockIoBuffer != NULL) {
FreePool (BlockIoBuffer);
}
return FileBuffer;
return FullPath;
}
/**
@ -948,16 +981,16 @@ BmExpandPartitionDevicePath (
by appending EFI_REMOVABLE_MEDIA_FILE_NAME.
@param DevicePath The media device path pointing to a BlockIo or SimpleFileSystem instance.
@param FullPath Return the full device path pointing to the load option.
@param FileSize Return the size of the load option.
@param FullPath The full path returned by the routine in last call.
Set to NULL in first call.
@return The load option buffer.
@return The next possible full path pointing to the load option.
Caller is responsible to free the memory.
**/
VOID *
EFI_DEVICE_PATH_PROTOCOL *
BmExpandMediaDevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
OUT UINTN *FileSize
IN EFI_DEVICE_PATH_PROTOCOL *FullPath
)
{
EFI_STATUS Status;
@ -965,14 +998,15 @@ BmExpandMediaDevicePath (
EFI_BLOCK_IO_PROTOCOL *BlockIo;
VOID *Buffer;
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
EFI_DEVICE_PATH_PROTOCOL *NextFullPath;
UINTN Size;
UINTN TempSize;
EFI_HANDLE *SimpleFileSystemHandles;
UINTN NumberSimpleFileSystemHandles;
UINTN Index;
VOID *FileBuffer;
UINT32 AuthenticationStatus;
BOOLEAN GetNext;
GetNext = (BOOLEAN)(FullPath == NULL);
//
// Check whether the device is connected
//
@ -981,14 +1015,16 @@ BmExpandMediaDevicePath (
if (!EFI_ERROR (Status)) {
ASSERT (IsDevicePathEnd (TempDevicePath));
TempDevicePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus);
if (FileBuffer == NULL) {
FreePool (TempDevicePath);
TempDevicePath = NULL;
NextFullPath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
//
// For device path pointing to simple file system, it only expands to one full path.
//
if (GetNext) {
return NextFullPath;
} else {
FreePool (NextFullPath);
return NULL;
}
*FullPath = TempDevicePath;
return FileBuffer;
}
Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);
@ -1025,8 +1061,7 @@ BmExpandMediaDevicePath (
//
// Detect the the default boot file from removable Media
//
FileBuffer = NULL;
*FullPath = NULL;
NextFullPath = NULL;
Size = GetDevicePathSize (DevicePath) - END_DEVICE_PATH_LENGTH;
gBS->LocateHandleBuffer (
ByProtocol,
@ -1045,13 +1080,14 @@ BmExpandMediaDevicePath (
// Check whether the device path of boot option is part of the SimpleFileSystem handle's device path
//
if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) {
TempDevicePath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);
FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus);
if (FileBuffer != NULL) {
*FullPath = TempDevicePath;
NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);
if (GetNext) {
break;
} else {
GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
FreePool (NextFullPath);
NextFullPath = NULL;
}
FreePool (TempDevicePath);
}
}
@ -1059,7 +1095,7 @@ BmExpandMediaDevicePath (
FreePool (SimpleFileSystemHandles);
}
return FileBuffer;
return NextFullPath;
}
/**
@ -1098,17 +1134,14 @@ BmMatchHttpBootDevicePath (
Get the file buffer from the file system produced by Load File instance.
@param LoadFileHandle The handle of LoadFile instance.
@param FullPath Return the full device path pointing to the load option.
@param FileSize Return the size of the load option.
@param RamDiskHandle Return the RAM Disk handle.
@return The load option buffer.
@return The next possible full path pointing to the load option.
Caller is responsible to free the memory.
**/
VOID *
BmGetFileBufferFromLoadFileSystem (
EFI_DEVICE_PATH_PROTOCOL *
BmExpandNetworkFileSystem (
IN EFI_HANDLE LoadFileHandle,
OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
OUT UINTN *FileSize,
OUT EFI_HANDLE *RamDiskHandle
)
{
@ -1138,6 +1171,9 @@ BmGetFileBufferFromLoadFileSystem (
if (!EFI_ERROR (Status) &&
(Handle == LoadFileHandle) &&
(DevicePathType (Node) == MEDIA_DEVICE_PATH) && (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP)) {
//
// Find the BlockIo instance populated from the LoadFile.
//
Handle = Handles[Index];
break;
}
@ -1154,13 +1190,16 @@ BmGetFileBufferFromLoadFileSystem (
*RamDiskHandle = Handle;
if (Handle != NULL) {
return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), FullPath, FileSize);
//
// Re-use BmExpandMediaDevicePath() to get the full device path of load option.
// But assume only one SimpleFileSystem can be found under the BlockIo.
//
return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), NULL);
} else {
return NULL;
}
}
/**
Return the RAM Disk device path created by LoadFile.
@ -1274,27 +1313,21 @@ BmDestroyRamDisk (
@param LoadFileHandle The specified Load File instance.
@param FilePath The file path which will pass to LoadFile().
@param FullPath Return the full device path pointing to the load option.
@param FileSize Return the size of the load option.
@return The load option buffer or NULL if fails.
@return The full device path pointing to the load option buffer.
**/
VOID *
BmGetFileBufferFromLoadFile (
EFI_DEVICE_PATH_PROTOCOL *
BmExpandLoadFile (
IN EFI_HANDLE LoadFileHandle,
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
OUT UINTN *FileSize
IN EFI_DEVICE_PATH_PROTOCOL *FilePath
)
{
EFI_STATUS Status;
EFI_LOAD_FILE_PROTOCOL *LoadFile;
VOID *FileBuffer;
BOOLEAN LoadFileSystem;
EFI_HANDLE RamDiskHandle;
UINTN BufferSize;
*FileSize = 0;
EFI_DEVICE_PATH_PROTOCOL *FullPath;
Status = gBS->OpenProtocol (
LoadFileHandle,
@ -1313,52 +1346,60 @@ BmGetFileBufferFromLoadFile (
return NULL;
}
LoadFileSystem = (BOOLEAN) (Status == EFI_WARN_FILE_SYSTEM);
FileBuffer = LoadFileSystem ? AllocateReservedPages (EFI_SIZE_TO_PAGES (BufferSize)) : AllocatePool (BufferSize);
if (Status == EFI_BUFFER_TOO_SMALL) {
//
// The load option buffer is directly returned by LoadFile.
//
return DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle));
}
//
// The load option resides in a RAM disk.
//
FileBuffer = AllocateReservedPages (EFI_SIZE_TO_PAGES (BufferSize));
if (FileBuffer == NULL) {
return NULL;
}
Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);
if (EFI_ERROR (Status)) {
if (LoadFileSystem) {
FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize));
} else {
FreePool (FileBuffer);
}
FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize));
return NULL;
}
if (LoadFileSystem) {
FileBuffer = BmGetFileBufferFromLoadFileSystem (LoadFileHandle, FullPath, FileSize, &RamDiskHandle);
if (FileBuffer == NULL) {
//
// If there is no bootable executable in the populated
//
BmDestroyRamDisk (DevicePathFromHandle (RamDiskHandle));
}
} else {
*FileSize = BufferSize;
*FullPath = DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle));
FullPath = BmExpandNetworkFileSystem (LoadFileHandle, &RamDiskHandle);
if (FullPath == NULL) {
//
// Free the memory occupied by the RAM disk if there is no BlockIo or SimpleFileSystem instance.
//
BmDestroyRamDisk (DevicePathFromHandle (RamDiskHandle));
}
return FileBuffer;
return FullPath;
}
/**
Get the file buffer from all the Load File instances.
Return the full device path pointing to the load option.
FilePath may:
1. Exactly matches to a LoadFile instance.
2. Cannot match to any LoadFile instance. Wide match is required.
In either case, the routine may return:
1. A copy of FilePath when FilePath matches to a LoadFile instance and
the LoadFile returns a load option buffer.
2. A new device path with IP and URI information updated when wide match
happens.
3. A new device path pointing to a load option in RAM disk.
In either case, only one full device path is returned for a specified
FilePath.
@param FilePath The media device path pointing to a LoadFile instance.
@param FullPath Return the full device path pointing to the load option.
@param FileSize Return the size of the load option.
@return The load option buffer.
**/
VOID *
BmGetFileBufferFromLoadFiles (
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
OUT UINTN *FileSize
EFI_DEVICE_PATH_PROTOCOL *
BmExpandLoadFiles (
IN EFI_DEVICE_PATH_PROTOCOL *FilePath
)
{
EFI_STATUS Status;
@ -1411,7 +1452,7 @@ BmGetFileBufferFromLoadFiles (
return NULL;
}
return BmGetFileBufferFromLoadFile (Handle, FilePath, FullPath, FileSize);
return BmExpandLoadFile (Handle, FilePath);
}
/**
@ -1433,20 +1474,38 @@ EfiBootManagerGetLoadOptionBuffer (
OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
OUT UINTN *FileSize
)
{
*FullPath = NULL;
EfiBootManagerConnectDevicePath (FilePath, NULL);
return BmGetNextLoadOptionBuffer (LoadOptionTypeMax, FilePath, FullPath, FileSize);
}
/**
Get the next possible full path pointing to the load option.
The routine doesn't guarantee the returned full path points to an existing
file, and it also doesn't guarantee the existing file is a valid load option.
BmGetNextLoadOptionBuffer() guarantees.
@param FilePath The device path pointing to a load option.
It could be a short-form device path.
@param FullPath The full path returned by the routine in last call.
Set to NULL in first call.
@return The next possible full path pointing to the load option.
Caller is responsible to free the memory.
**/
EFI_DEVICE_PATH_PROTOCOL *
BmGetNextLoadOptionDevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
IN EFI_DEVICE_PATH_PROTOCOL *FullPath
)
{
EFI_HANDLE Handle;
VOID *FileBuffer;
UINT32 AuthenticationStatus;
EFI_DEVICE_PATH_PROTOCOL *Node;
EFI_STATUS Status;
ASSERT ((FilePath != NULL) && (FullPath != NULL) && (FileSize != NULL));
EfiBootManagerConnectDevicePath (FilePath, NULL);
*FullPath = NULL;
*FileSize = 0;
FileBuffer = NULL;
ASSERT (FilePath != NULL);
//
// Boot from media device by adding a default file name \EFI\BOOT\BOOT{machine type short-name}.EFI
@ -1458,7 +1517,7 @@ EfiBootManagerGetLoadOptionBuffer (
}
if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) {
return BmExpandMediaDevicePath (FilePath, FullPath, FileSize);
return BmExpandMediaDevicePath (FilePath, FullPath);
}
//
@ -1469,19 +1528,23 @@ EfiBootManagerGetLoadOptionBuffer (
//
// Expand the Harddrive device path
//
return BmExpandPartitionDevicePath (FilePath, FullPath, FileSize);
if (FullPath == NULL) {
return BmExpandPartitionDevicePath (FilePath);
} else {
return NULL;
}
} else if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) &&
(DevicePathSubType (FilePath) == MEDIA_FILEPATH_DP)) {
//
// Expand the File-path device path
//
return BmExpandFileDevicePath (FilePath, FullPath, FileSize);
return BmExpandFileDevicePath (FilePath, FullPath);
} else if ((DevicePathType (FilePath) == MESSAGING_DEVICE_PATH) &&
(DevicePathSubType (FilePath) == MSG_URI_DP)) {
//
// Expand the URI device path
//
return BmExpandUriDevicePath (FilePath, FullPath, FileSize);
return BmExpandUriDevicePath (FilePath, FullPath);
} else {
for (Node = FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) {
if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&
@ -1490,45 +1553,50 @@ EfiBootManagerGetLoadOptionBuffer (
}
}
//
// Expand the USB WWID/Class device path
//
if (!IsDevicePathEnd (Node)) {
//
// Expand the USB WWID/Class device path
//
FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node);
if ((FileBuffer == NULL) && (FilePath == Node)) {
if (FilePath == Node) {
//
// Boot Option device path starts with USB Class or USB WWID device path.
// For Boot Option device path which doesn't begin with the USB Class or
// USB WWID device path, it's not needed to connect again here.
//
BmConnectUsbShortFormDevicePath (FilePath);
FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node);
}
return FileBuffer;
return BmExpandUsbDevicePath (FilePath, FullPath, Node);
}
}
//
// Get file buffer from FV file path.
// For the below cases, FilePath only expands to one Full path.
// So just handle the case when FullPath == NULL.
//
if (BmIsFvFilePath (FilePath)) {
return BmGetFileBufferByFvFilePath (FilePath, FullPath, FileSize);
if (FullPath != NULL) {
return NULL;
}
//
// Get file buffer from simple file system.
// Load option resides in FV.
//
if (BmIsFvFilePath (FilePath)) {
return BmAdjustFvFilePath (FilePath);
}
//
// Load option resides in Simple File System.
//
Node = FilePath;
Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle);
if (!EFI_ERROR (Status)) {
FileBuffer = GetFileBufferByFilePath (TRUE, FilePath, FileSize, &AuthenticationStatus);
if (FileBuffer != NULL) {
*FullPath = DuplicateDevicePath (FilePath);
}
return FileBuffer;
return DuplicateDevicePath (FilePath);
}
return BmGetFileBufferFromLoadFiles (FilePath, FullPath, FileSize);
//
// Last chance to try: Load option may be loaded through LoadFile.
//
return BmExpandLoadFiles (FilePath);
}
/**
@ -1686,21 +1754,13 @@ EfiBootManagerBoot (
ImageHandle = NULL;
RamDiskDevicePath = NULL;
if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) {
Status = EFI_NOT_FOUND;
FileBuffer = EfiBootManagerGetLoadOptionBuffer (BootOption->FilePath, &FilePath, &FileSize);
Status = EFI_NOT_FOUND;
FilePath = NULL;
EfiBootManagerConnectDevicePath (BootOption->FilePath, NULL);
FileBuffer = BmGetNextLoadOptionBuffer (LoadOptionTypeBoot, BootOption->FilePath, &FilePath, &FileSize);
if (FileBuffer != NULL) {
RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath);
}
DEBUG_CODE (
if (FileBuffer != NULL && CompareMem (BootOption->FilePath, FilePath, GetDevicePathSize (FilePath)) != 0) {
DEBUG ((EFI_D_INFO, "[Bds] DevicePath expand: "));
BmPrintDp (BootOption->FilePath);
DEBUG ((EFI_D_INFO, " -> "));
BmPrintDp (FilePath);
DEBUG ((EFI_D_INFO, "\n"));
}
);
if (BmIsLoadOptionPeHeaderValid (BootOption->OptionType, FileBuffer, FileSize)) {
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));
Status = gBS->LoadImage (
TRUE,

View File

@ -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;

View File

@ -309,23 +309,6 @@ BmSetVariableAndReportStatusCodeOnError (
IN VOID *Data
);
/**
Return whether the PE header of the load option is valid or not.
@param[in] Type The load option type.
@param[in] FileBuffer The PE file buffer of the load option.
@param[in] FileSize The size of the load option file.
@retval TRUE The PE header of the load option is valid.
@retval FALSE The PE header of the load option is not valid.
**/
BOOLEAN
BmIsLoadOptionPeHeaderValid (
IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,
IN VOID *FileBuffer,
IN UINTN FileSize
);
/**
Function compares a device path data structure to that of all the nodes of a
second device path instance.
@ -425,16 +408,83 @@ BmMakeBootOptionDescriptionUnique (
@param LoadFileHandle The specified Load File instance.
@param FilePath The file path which will pass to LoadFile().
@param FullPath Return the full device path pointing to the load option.
@param FileSize Return the size of the load option.
@return The load option buffer or NULL if fails.
@return The full device path pointing to the load option buffer.
**/
EFI_DEVICE_PATH_PROTOCOL *
BmExpandLoadFile (
IN EFI_HANDLE LoadFileHandle,
IN EFI_DEVICE_PATH_PROTOCOL *FilePath
);
/**
Return the RAM Disk device path created by LoadFile.
@param FilePath The source file path.
@return Callee-to-free RAM Disk device path
**/
EFI_DEVICE_PATH_PROTOCOL *
BmGetRamDiskDevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *FilePath
);
/**
Destroy the RAM Disk.
The destroy operation includes to call RamDisk.Unregister to
unregister the RAM DISK from RAM DISK driver, free the memory
allocated for the RAM Disk.
@param RamDiskDevicePath RAM Disk device path.
**/
VOID
BmDestroyRamDisk (
IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath
);
/**
Get the next possible full path pointing to the load option.
@param FilePath The device path pointing to a load option.
It could be a short-form device path.
@param FullPath The full path returned by the routine in last call.
Set to NULL in first call.
@return The next possible full path pointing to the load option.
Caller is responsible to free the memory.
**/
EFI_DEVICE_PATH_PROTOCOL *
BmGetNextLoadOptionDevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
IN EFI_DEVICE_PATH_PROTOCOL *FullPath
);
/**
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 *
BmGetFileBufferFromLoadFile (
EFI_HANDLE LoadFileHandle,
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
OUT UINTN *FileSize
BmGetNextLoadOptionBuffer (
IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
OUT UINTN *FileSize
);
#endif // _INTERNAL_BM_H_