]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
MdeModulePkg BrotliLib: Rename function with the specific lib name
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmLoadOption.c
index 8201255484cf4a5c1e8403a121bdf74c61e64f9a..32918caf324ccdc98893149ccd9be4fc5320c7fe 100644 (file)
@@ -1,7 +1,7 @@
 /** @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
@@ -785,6 +785,8 @@ EfiBootManagerIsValidLoadOptionVariableName (
   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
@@ -792,39 +794,52 @@ EfiBootManagerIsValidLoadOptionVariableName (
 \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
@@ -912,7 +927,7 @@ EfiBootManagerVariableToLoadOptionEx (
   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
@@ -1167,6 +1182,10 @@ EfiBootManagerFreeLoadOptions (
   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
@@ -1217,7 +1236,8 @@ BmIsLoadOptionPeHeaderValid (
         //   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
@@ -1232,6 +1252,91 @@ BmIsLoadOptionPeHeaderValid (
   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
@@ -1249,7 +1354,8 @@ EfiBootManagerProcessLoadOption (
   )\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
@@ -1271,64 +1377,71 @@ EfiBootManagerProcessLoadOption (
     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 = BmGetLoadOptionBuffer (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