/** @file\r
Load option library functions which relate with creating and processing load options.\r
\r
-Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2011 - 2017, Intel Corporation. All rights reserved.<BR>\r
(C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP<BR>\r
This program and the accompanying materials\r
are licensed and made available under the terms and conditions of the BSD License\r
UINTN VariableNameLen;\r
UINTN Index;\r
UINTN Uint;\r
+ EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LocalOptionType;\r
+ UINT16 LocalOptionNumber;\r
\r
if (VariableName == NULL) {\r
return FALSE;\r
\r
VariableNameLen = StrLen (VariableName);\r
\r
+ //\r
+ // Return FALSE when the variable name length is too small.\r
+ //\r
if (VariableNameLen <= 4) {\r
return FALSE;\r
}\r
\r
- for (Index = 0; Index < sizeof (mBmLoadOptionName) / sizeof (mBmLoadOptionName[0]); Index++) {\r
- if ((VariableNameLen - 4 == StrLen (mBmLoadOptionName[Index])) &&\r
- (StrnCmp (VariableName, mBmLoadOptionName[Index], VariableNameLen - 4) == 0)\r
+ //\r
+ // Return FALSE when the variable name doesn't start with Driver/SysPrep/Boot/PlatformRecovery.\r
+ //\r
+ for (LocalOptionType = 0; LocalOptionType < ARRAY_SIZE (mBmLoadOptionName); LocalOptionType++) {\r
+ if ((VariableNameLen - 4 == StrLen (mBmLoadOptionName[LocalOptionType])) &&\r
+ (StrnCmp (VariableName, mBmLoadOptionName[LocalOptionType], VariableNameLen - 4) == 0)\r
) {\r
break;\r
}\r
}\r
+ if (LocalOptionType == ARRAY_SIZE (mBmLoadOptionName)) {\r
+ return FALSE;\r
+ }\r
\r
- if (Index == sizeof (mBmLoadOptionName) / sizeof (mBmLoadOptionName[0])) {\r
+ //\r
+ // Return FALSE when the last four characters are not hex digits.\r
+ //\r
+ LocalOptionNumber = 0;\r
+ for (Index = VariableNameLen - 4; Index < VariableNameLen; Index++) {\r
+ Uint = BmCharToUint (VariableName[Index]);\r
+ if (Uint == -1) {\r
+ break;\r
+ } else {\r
+ LocalOptionNumber = (UINT16) Uint + LocalOptionNumber * 0x10;\r
+ }\r
+ }\r
+ if (Index != VariableNameLen) {\r
return FALSE;\r
}\r
\r
if (OptionType != NULL) {\r
- *OptionType = (EFI_BOOT_MANAGER_LOAD_OPTION_TYPE) Index;\r
+ *OptionType = LocalOptionType;\r
}\r
\r
if (OptionNumber != NULL) {\r
- *OptionNumber = 0;\r
- for (Index = VariableNameLen - 4; Index < VariableNameLen; Index++) {\r
- Uint = BmCharToUint (VariableName[Index]);\r
- if (Uint == -1) {\r
- break;\r
- } else {\r
- *OptionNumber = (UINT16) Uint + *OptionNumber * 0x10;\r
- }\r
- }\r
+ *OptionNumber = LocalOptionNumber;\r
}\r
\r
- return (BOOLEAN) (Index == VariableNameLen);\r
+ return TRUE;\r
}\r
\r
/**\r
FilePath = (EFI_DEVICE_PATH_PROTOCOL *) VariablePtr;\r
VariablePtr += FilePathSize;\r
\r
- OptionalDataSize = (UINT32) (VariableSize - (UINTN) (VariablePtr - Variable));\r
+ OptionalDataSize = (UINT32) (VariableSize - ((UINTN) VariablePtr - (UINTN) Variable));\r
if (OptionalDataSize == 0) {\r
OptionalData = NULL;\r
} else {\r
Return whether the PE header of the load option is valid or not.\r
\r
@param[in] Type The load option type.\r
+ It's used to check whether the load option is valid.\r
+ When it's LoadOptionTypeMax, the routine only guarantees\r
+ the load option is a valid PE image but doesn't guarantee\r
+ the PE's subsystem type is valid.\r
@param[in] FileBuffer The PE file buffer of the load option.\r
@param[in] FileSize The size of the load option file.\r
\r
// SysPrep####, Boot####, OsRecovery####, PlatformRecovery#### must be of type Application\r
//\r
Subsystem = OptionalHeader->Subsystem;\r
- if ((Type == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) ||\r
+ if ((Type == LoadOptionTypeMax) ||\r
+ (Type == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) ||\r
(Type == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) ||\r
(Type == LoadOptionTypeSysPrep && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) ||\r
(Type == LoadOptionTypeBoot && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) ||\r
return FALSE;\r
}\r
\r
+/**\r
+ Return the next matched load option buffer.\r
+ The routine keeps calling BmGetNextLoadOptionDevicePath() until a valid\r
+ load option is read.\r
+\r
+ @param Type The load option type.\r
+ It's used to check whether the load option is valid.\r
+ When it's LoadOptionTypeMax, the routine only guarantees\r
+ the load option is a valid PE image but doesn't guarantee\r
+ the PE's subsystem type is valid.\r
+ @param FilePath The device path pointing to a load option.\r
+ It could be a short-form device path.\r
+ @param FullPath Return the next full device path of the load option after\r
+ short-form device path expanding.\r
+ Caller is responsible to free it.\r
+ NULL to return the first matched full device path.\r
+ @param FileSize Return the load option size.\r
+\r
+ @return The load option buffer. Caller is responsible to free the memory.\r
+**/\r
+VOID *\r
+BmGetNextLoadOptionBuffer (\r
+ IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
+ OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,\r
+ OUT UINTN *FileSize\r
+ )\r
+{\r
+ VOID *FileBuffer;\r
+ EFI_DEVICE_PATH_PROTOCOL *PreFullPath;\r
+ EFI_DEVICE_PATH_PROTOCOL *CurFullPath;\r
+ UINTN LocalFileSize;\r
+ UINT32 AuthenticationStatus;\r
+ EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;\r
+\r
+ LocalFileSize = 0;\r
+ FileBuffer = NULL;\r
+ CurFullPath = *FullPath;\r
+ do {\r
+ PreFullPath = CurFullPath;\r
+ CurFullPath = BmGetNextLoadOptionDevicePath (FilePath, CurFullPath);\r
+ //\r
+ // Only free the full path created *inside* this routine\r
+ //\r
+ if ((PreFullPath != NULL) && (PreFullPath != *FullPath)) {\r
+ FreePool (PreFullPath);\r
+ }\r
+ if (CurFullPath == NULL) {\r
+ break;\r
+ }\r
+ FileBuffer = GetFileBufferByFilePath (TRUE, CurFullPath, &LocalFileSize, &AuthenticationStatus);\r
+ if ((FileBuffer != NULL) && !BmIsLoadOptionPeHeaderValid (Type, FileBuffer, LocalFileSize)) {\r
+ //\r
+ // Free the RAM disk file system if the load option is invalid.\r
+ //\r
+ RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath);\r
+ if (RamDiskDevicePath != NULL) {\r
+ BmDestroyRamDisk (RamDiskDevicePath);\r
+ FreePool (RamDiskDevicePath);\r
+ }\r
+\r
+ //\r
+ // Free the invalid load option buffer.\r
+ //\r
+ FreePool (FileBuffer);\r
+ FileBuffer = NULL;\r
+ }\r
+ } while (FileBuffer == NULL);\r
+\r
+ if (FileBuffer == NULL) {\r
+ CurFullPath = NULL;\r
+ LocalFileSize = 0;\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "[Bds] Expand "));\r
+ BmPrintDp (FilePath);\r
+ DEBUG ((DEBUG_INFO, " -> "));\r
+ BmPrintDp (CurFullPath);\r
+ DEBUG ((DEBUG_INFO, "\n"));\r
+\r
+ *FullPath = CurFullPath;\r
+ *FileSize = LocalFileSize;\r
+ return FileBuffer;\r
+}\r
+\r
/**\r
Process (load and execute) the load option.\r
\r
)\r
{\r
EFI_STATUS Status;\r
- EFI_DEVICE_PATH_PROTOCOL *FilePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *PreFullPath;\r
+ EFI_DEVICE_PATH_PROTOCOL *CurFullPath;\r
EFI_HANDLE ImageHandle;\r
EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;\r
VOID *FileBuffer;\r
return EFI_SUCCESS;\r
}\r
\r
- Status = EFI_INVALID_PARAMETER;\r
-\r
//\r
// Load and start the load option.\r
//\r
DEBUG ((\r
- DEBUG_INFO | DEBUG_LOAD, "Process Load Option (%s%04x) ...\n",\r
- mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber\r
+ DEBUG_INFO | DEBUG_LOAD, "Process %s%04x (%s) ...\n",\r
+ mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber,\r
+ LoadOption->Description\r
));\r
ImageHandle = NULL;\r
- FileBuffer = EfiBootManagerGetLoadOptionBuffer (LoadOption->FilePath, &FilePath, &FileSize);\r
- DEBUG_CODE (\r
- if (FileBuffer != NULL && CompareMem (LoadOption->FilePath, FilePath, GetDevicePathSize (FilePath)) != 0) {\r
- DEBUG ((EFI_D_INFO, "[Bds] DevicePath expand: "));\r
- BmPrintDp (LoadOption->FilePath);\r
- DEBUG ((EFI_D_INFO, " -> "));\r
- BmPrintDp (FilePath);\r
- DEBUG ((EFI_D_INFO, "\n"));\r
+ CurFullPath = NULL;\r
+ EfiBootManagerConnectDevicePath (LoadOption->FilePath, NULL);\r
+\r
+ //\r
+ // while() loop is to keep starting next matched load option if the PlatformRecovery#### returns failure status.\r
+ //\r
+ while (TRUE) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ PreFullPath = CurFullPath;\r
+ FileBuffer = BmGetNextLoadOptionBuffer (LoadOption->OptionType, LoadOption->FilePath, &CurFullPath, &FileSize);\r
+ if (PreFullPath != NULL) {\r
+ FreePool (PreFullPath);\r
+ }\r
+ if (FileBuffer == NULL) {\r
+ break;\r
}\r
- );\r
- if (BmIsLoadOptionPeHeaderValid (LoadOption->OptionType, FileBuffer, FileSize)) {\r
Status = gBS->LoadImage (\r
FALSE,\r
gImageHandle,\r
- FilePath,\r
+ CurFullPath,\r
FileBuffer,\r
FileSize,\r
&ImageHandle\r
);\r
- }\r
- if (FilePath != NULL) {\r
- FreePool (FilePath);\r
- }\r
- if (FileBuffer != NULL) {\r
FreePool (FileBuffer);\r
- }\r
\r
- if (!EFI_ERROR (Status)) {\r
- Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);\r
- ASSERT_EFI_ERROR (Status);\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&ImageInfo);\r
+ ASSERT_EFI_ERROR (Status);\r
\r
- ImageInfo->LoadOptionsSize = LoadOption->OptionalDataSize;\r
- ImageInfo->LoadOptions = LoadOption->OptionalData;\r
- //\r
- // Before calling the image, enable the Watchdog Timer for the 5-minute period\r
- //\r
- gBS->SetWatchdogTimer (5 * 60, 0, 0, NULL);\r
+ ImageInfo->LoadOptionsSize = LoadOption->OptionalDataSize;\r
+ ImageInfo->LoadOptions = LoadOption->OptionalData;\r
+ //\r
+ // Before calling the image, enable the Watchdog Timer for the 5-minute period\r
+ //\r
+ gBS->SetWatchdogTimer (5 * 60, 0, 0, NULL);\r
\r
- LoadOption->Status = gBS->StartImage (ImageHandle, &LoadOption->ExitDataSize, &LoadOption->ExitData);\r
- DEBUG ((\r
- DEBUG_INFO | DEBUG_LOAD, "Load Option (%s%04x) Return Status = %r\n",\r
- mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber, LoadOption->Status\r
+ LoadOption->Status = gBS->StartImage (ImageHandle, &LoadOption->ExitDataSize, &LoadOption->ExitData);\r
+ DEBUG ((\r
+ DEBUG_INFO | DEBUG_LOAD, "%s%04x Return Status = %r\n",\r
+ mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber, LoadOption->Status\r
));\r
\r
- //\r
- // Clear the Watchdog Timer after the image returns\r
- //\r
- gBS->SetWatchdogTimer (0, 0, 0, NULL);\r
+ //\r
+ // Clear the Watchdog Timer after the image returns\r
+ //\r
+ gBS->SetWatchdogTimer (0, 0, 0, NULL);\r
+\r
+ if ((LoadOption->OptionType != LoadOptionTypePlatformRecovery) || (LoadOption->Status == EFI_SUCCESS)) {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (CurFullPath != NULL) {\r
+ FreePool (CurFullPath);\r
}\r
\r
return Status;\r