]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Universal / CapsuleRuntimeDxe / CapsuleService.c
index 2814c9d774bdcff6339fc887761e9112fd72bbb8..aaf819c4c615e7f2903b77139120c829055bb8e3 100644 (file)
@@ -1,26 +1,60 @@
-/*++\r
+/** @file\r
+  Capsule Runtime Driver produces two UEFI capsule runtime services.\r
+  (UpdateCapsule, QueryCapsuleCapabilities)\r
+  It installs the Capsule Architectural Protocol defined in PI1.0a to signify\r
+  the capsule runtime services are ready.\r
 \r
-Copyright (c) 2006 - 2007, Intel Corporation\r
-All rights reserved. This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution.  The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
-\r
-Module Name:\r
-\r
-  CapsuleService.c\r
-\r
-Abstract:\r
-\r
-  Capsule Runtime Service.\r
-\r
---*/\r
+**/\r
 \r
 #include "CapsuleService.h"\r
 \r
+//\r
+// Handle for the installation of Capsule Architecture Protocol.\r
+//\r
+EFI_HANDLE  mNewHandle = NULL;\r
+\r
+//\r
+// The times of calling UpdateCapsule ()\r
+//\r
+UINTN       mTimes      = 0;\r
+\r
+UINT32      mMaxSizePopulateCapsule     = 0;\r
+UINT32      mMaxSizeNonPopulateCapsule  = 0;\r
+\r
+/**\r
+  Passes capsules to the firmware with both virtual and physical mapping. Depending on the intended\r
+  consumption, the firmware may process the capsule immediately. If the payload should persist\r
+  across a system reset, the reset value returned from EFI_QueryCapsuleCapabilities must\r
+  be passed into ResetSystem() and will cause the capsule to be processed by the firmware as\r
+  part of the reset process.\r
+\r
+  @param  CapsuleHeaderArray    Virtual pointer to an array of virtual pointers to the capsules\r
+                                being passed into update capsule.\r
+  @param  CapsuleCount          Number of pointers to EFI_CAPSULE_HEADER in\r
+                                CaspuleHeaderArray.\r
+  @param  ScatterGatherList     Physical pointer to a set of\r
+                                EFI_CAPSULE_BLOCK_DESCRIPTOR that describes the\r
+                                location in physical memory of a set of capsules.\r
+\r
+  @retval EFI_SUCCESS           Valid capsule was passed. If\r
+                                CAPSULE_FLAGS_PERSIT_ACROSS_RESET is not set, the\r
+                                capsule has been successfully processed by the firmware.\r
+  @retval EFI_DEVICE_ERROR      The capsule update was started, but failed due to a device error.\r
+  @retval EFI_INVALID_PARAMETER CapsuleSize is NULL, or an incompatible set of flags were\r
+                                set in the capsule header.\r
+  @retval EFI_INVALID_PARAMETER CapsuleCount is Zero.\r
+  @retval EFI_INVALID_PARAMETER For across reset capsule image, ScatterGatherList is NULL.\r
+  @retval EFI_UNSUPPORTED       CapsuleImage is not recognized by the firmware.\r
+  @retval EFI_OUT_OF_RESOURCES  When ExitBootServices() has been previously called this error indicates the capsule\r
+                                is compatible with this platform but is not capable of being submitted or processed\r
+                                in runtime. The caller may resubmit the capsule prior to ExitBootServices().\r
+  @retval EFI_OUT_OF_RESOURCES  When ExitBootServices() has not been previously called then this error indicates\r
+                                the capsule is compatible with this platform but there are insufficient resources to process.\r
+\r
+**/\r
 EFI_STATUS\r
 EFIAPI\r
 UpdateCapsule (\r
@@ -28,38 +62,26 @@ UpdateCapsule (
   IN UINTN                   CapsuleCount,\r
   IN EFI_PHYSICAL_ADDRESS    ScatterGatherList OPTIONAL\r
   )\r
-/*++\r
-\r
-Routine Description:\r
-\r
-  This code finds whether the capsules need reset to update, if not, update immediately.\r
-\r
-Arguments:\r
-\r
-  CapsuleHeaderArray             A array of pointers to capsule headers passed in\r
-  CapsuleCount                   The number of capsule\r
-  ScatterGatherList              Physical address of datablock list points to capsule\r
-\r
-Returns:\r
-\r
-  EFI STATUS\r
-  EFI_SUCCESS                    Valid capsule was passed.If CAPSULE_FLAG_PERSIT_ACROSS_RESET is\r
-                                 not set, the capsule has been successfully processed by the firmware.\r
-                                 If it set, the ScattlerGatherList is successfully to be set.\r
-  EFI_INVALID_PARAMETER          CapsuleCount is less than 1,CapsuleGuid is not supported.\r
-  EFI_DEVICE_ERROR               Failed to SetVariable or ProcessFirmwareVolume.\r
-\r
---*/\r
 {\r
   UINTN                     ArrayNumber;\r
   EFI_STATUS                Status;\r
   EFI_CAPSULE_HEADER        *CapsuleHeader;\r
+  BOOLEAN                   NeedReset;\r
+  BOOLEAN                   InitiateReset;\r
+  CHAR16                    CapsuleVarName[30];\r
+  CHAR16                    *TempVarName;\r
 \r
+  //\r
+  // Capsule Count can't be less than one.\r
+  //\r
   if (CapsuleCount < 1) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  CapsuleHeader   = NULL;\r
+  NeedReset         = FALSE;\r
+  InitiateReset     = FALSE;\r
+  CapsuleHeader     = NULL;\r
+  CapsuleVarName[0] = 0;\r
 \r
   for (ArrayNumber = 0; ArrayNumber < CapsuleCount; ArrayNumber++) {\r
     //\r
@@ -71,79 +93,148 @@ Returns:
       return EFI_INVALID_PARAMETER;\r
     }\r
     //\r
-    // Check Capsule image without populate flag by firmware support capsule function  \r
+    // A capsule which has the CAPSULE_FLAGS_INITIATE_RESET flag must have\r
+    // CAPSULE_FLAGS_PERSIST_ACROSS_RESET set in its header as well.\r
     //\r
-    if (((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) == 0) && \r
-        (SupportCapsuleImage (CapsuleHeader) != EFI_SUCCESS)) {\r
-      return EFI_UNSUPPORTED;\r
+    if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) {\r
+      return EFI_INVALID_PARAMETER;\r
     }\r
-  }\r
 \r
-  //\r
-  // Assume that capsules have the same flags on reseting or not.\r
-  //\r
-  CapsuleHeader = CapsuleHeaderArray[0];\r
+    //\r
+    // Check FMP capsule flag\r
+    //\r
+    if (CompareGuid(&CapsuleHeader->CapsuleGuid, &gEfiFmpCapsuleGuid)\r
+     && (CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0 ) {\r
+       return EFI_INVALID_PARAMETER;\r
+    }\r
 \r
-  if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {\r
     //\r
-    // Check if the platform supports update capsule across a system reset\r
+    // Check Capsule image without populate flag by firmware support capsule function\r
     //\r
-    if (!FeaturePcdGet(PcdSupportUpdateCapsuleRest)) {\r
-      return EFI_UNSUPPORTED;\r
+    if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) == 0) {\r
+      Status = SupportCapsuleImage (CapsuleHeader);\r
+      if (EFI_ERROR(Status)) {\r
+        return Status;\r
+      }\r
     }\r
+  }\r
+\r
+  //\r
+  // Walk through all capsules, record whether there is a capsule needs reset\r
+  // or initiate reset. And then process capsules which has no reset flag directly.\r
+  //\r
+  for (ArrayNumber = 0; ArrayNumber < CapsuleCount ; ArrayNumber++) {\r
+    CapsuleHeader = CapsuleHeaderArray[ArrayNumber];\r
     //\r
-    // ScatterGatherList is only referenced if the capsules are defined to persist across\r
-    // system reset. \r
+    // Here should be in the boot-time for non-reset capsule image\r
+    // Platform specific update for the non-reset capsule image.\r
     //\r
-    if (ScatterGatherList == (EFI_PHYSICAL_ADDRESS) NULL) {\r
-      return EFI_INVALID_PARAMETER;\r
-    } else {\r
-      //\r
-      // ScatterGatherList is only referenced if the capsules are defined to persist across\r
-      // system reset. Set its value into NV storage to let pre-boot driver to pick it up \r
-      // after coming through a system reset.\r
-      //\r
-      Status = EfiSetVariable (\r
-                 EFI_CAPSULE_VARIABLE_NAME,\r
-                 &gEfiCapsuleVendorGuid,\r
-                 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
-                 sizeof (UINTN),\r
-                 (VOID *) &ScatterGatherList\r
-                 );\r
-      if (Status != EFI_SUCCESS) {\r
+    if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) == 0) {\r
+      if (EfiAtRuntime ()) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+      } else {\r
+        Status = ProcessCapsuleImage(CapsuleHeader);\r
+      }\r
+      if (EFI_ERROR(Status)) {\r
         return Status;\r
       }\r
-      //\r
-      // Successfully set the capsule image address into variable.\r
-      //\r
-      return EFI_SUCCESS;\r
+    } else {\r
+      NeedReset = TRUE;\r
+      if ((CapsuleHeader->Flags & CAPSULE_FLAGS_INITIATE_RESET) != 0) {\r
+        InitiateReset = TRUE;\r
+      }\r
     }\r
   }\r
 \r
   //\r
-  // The rest occurs in the condition of non-reset mode\r
-  // Now Runtime mode doesn't support the non-reset capsule image.\r
+  // After launching all capsules who has no reset flag, if no more capsules claims\r
+  // for a system reset just return.\r
   //\r
-  if (EfiAtRuntime ()) {\r
+  if (!NeedReset) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // ScatterGatherList is only referenced if the capsules are defined to persist across\r
+  // system reset.\r
+  //\r
+  if (ScatterGatherList == (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
   //\r
-  // Here should be in the boot-time for non-reset capsule image\r
-  // Default process to Update Capsule image into Flash.\r
+  // Check if the platform supports update capsule across a system reset\r
   //\r
-  for (ArrayNumber = 0; ArrayNumber < CapsuleCount ; ArrayNumber++) {\r
-    Status = ProcessCapsuleImage (CapsuleHeaderArray[ArrayNumber]);\r
-    if (EFI_ERROR (Status)) {\r
-      return Status;\r
-    }\r
+  if (!IsPersistAcrossResetCapsuleSupported ()) {\r
+    return EFI_UNSUPPORTED;\r
   }\r
 \r
-  return EFI_SUCCESS;\r
-}\r
+  CapsuleCacheWriteBack (ScatterGatherList);\r
 \r
+  //\r
+  // Construct variable name CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...\r
+  // if user calls UpdateCapsule multiple times.\r
+  //\r
+  StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CHAR16), EFI_CAPSULE_VARIABLE_NAME);\r
+  TempVarName = CapsuleVarName + StrLen (CapsuleVarName);\r
+  if (mTimes > 0) {\r
+    UnicodeValueToStringS (\r
+      TempVarName,\r
+      sizeof (CapsuleVarName) - ((UINTN)TempVarName - (UINTN)CapsuleVarName),\r
+      0,\r
+      mTimes,\r
+      0\r
+      );\r
+  }\r
 \r
+  //\r
+  // ScatterGatherList is only referenced if the capsules are defined to persist across\r
+  // system reset. Set its value into NV storage to let pre-boot driver to pick it up\r
+  // after coming through a system reset.\r
+  //\r
+  Status = EfiSetVariable (\r
+             CapsuleVarName,\r
+             &gEfiCapsuleVendorGuid,\r
+             EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+             sizeof (UINTN),\r
+             (VOID *) &ScatterGatherList\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+     //\r
+     // Variable has been set successfully, increase variable index.\r
+     //\r
+     mTimes++;\r
+     if(InitiateReset) {\r
+       //\r
+       // Firmware that encounters a capsule which has the CAPSULE_FLAGS_INITIATE_RESET Flag set in its header\r
+       // will initiate a reset of the platform which is compatible with the passed-in capsule request and will\r
+       // not return back to the caller.\r
+       //\r
+       EfiResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);\r
+     }\r
+  }\r
+  return Status;\r
+}\r
 \r
+/**\r
+  Returns if the capsule can be supported via UpdateCapsule().\r
+\r
+  @param  CapsuleHeaderArray    Virtual pointer to an array of virtual pointers to the capsules\r
+                                being passed into update capsule.\r
+  @param  CapsuleCount          Number of pointers to EFI_CAPSULE_HEADER in\r
+                                CaspuleHeaderArray.\r
+  @param  MaxiumCapsuleSize     On output the maximum size that UpdateCapsule() can\r
+                                support as an argument to UpdateCapsule() via\r
+                                CapsuleHeaderArray and ScatterGatherList.\r
+  @param  ResetType             Returns the type of reset required for the capsule update.\r
+\r
+  @retval EFI_SUCCESS           Valid answer returned.\r
+  @retval EFI_UNSUPPORTED       The capsule image is not supported on this platform, and\r
+                                MaximumCapsuleSize and ResetType are undefined.\r
+  @retval EFI_INVALID_PARAMETER MaximumCapsuleSize is NULL, or ResetTyep is NULL,\r
+                                Or CapsuleCount is Zero, or CapsuleImage is not valid.\r
+\r
+**/\r
 EFI_STATUS\r
 EFIAPI\r
 QueryCapsuleCapabilities (\r
@@ -152,41 +243,28 @@ QueryCapsuleCapabilities (
   OUT UINT64               *MaxiumCapsuleSize,\r
   OUT EFI_RESET_TYPE       *ResetType\r
   )\r
-/*++\r
-\r
-Routine Description:\r
-\r
-  This code is to query about capsule capability.\r
-\r
-Arguments:\r
-\r
-  CapsuleHeaderArray              A array of pointers to capsule headers passed in\r
-  CapsuleCount                    The number of capsule\r
-  MaxiumCapsuleSize               Max capsule size is supported\r
-  ResetType                       Reset type the capsule indicates, if reset is not needed,return EfiResetCold.\r
-                                  If reset is needed, return EfiResetWarm.\r
-\r
-Returns:\r
-\r
-  EFI STATUS\r
-  EFI_SUCCESS                     Valid answer returned\r
-  EFI_INVALID_PARAMETER           MaxiumCapsuleSize is NULL,ResetType is NULL.CapsuleCount is less than 1,CapsuleGuid is not supported.\r
-  EFI_UNSUPPORTED                 The capsule type is not supported.\r
-\r
---*/\r
 {\r
+  EFI_STATUS                Status;\r
   UINTN                     ArrayNumber;\r
   EFI_CAPSULE_HEADER        *CapsuleHeader;\r
+  BOOLEAN                   NeedReset;\r
 \r
+  //\r
+  // Capsule Count can't be less than one.\r
+  //\r
   if (CapsuleCount < 1) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
+  //\r
+  // Check whether input parameter is valid\r
+  //\r
   if ((MaxiumCapsuleSize == NULL) ||(ResetType == NULL)) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
   CapsuleHeader = NULL;\r
+  NeedReset     = FALSE;\r
 \r
   for (ArrayNumber = 0; ArrayNumber < CapsuleCount; ArrayNumber++) {\r
     CapsuleHeader = CapsuleHeaderArray[ArrayNumber];\r
@@ -198,76 +276,112 @@ Returns:
       return EFI_INVALID_PARAMETER;\r
     }\r
     //\r
-    // Check Capsule image without populate flag by firmware support capsule function  \r
+    // A capsule which has the CAPSULE_FLAGS_INITIATE_RESET flag must have\r
+    // CAPSULE_FLAGS_PERSIST_ACROSS_RESET set in its header as well.\r
     //\r
-    if (((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) == 0) && \r
-        (SupportCapsuleImage (CapsuleHeader) != EFI_SUCCESS)) {\r
-      return EFI_UNSUPPORTED;\r
+    if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    //\r
+    // Check FMP capsule flag\r
+    //\r
+    if (CompareGuid(&CapsuleHeader->CapsuleGuid, &gEfiFmpCapsuleGuid)\r
+     && (CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0 ) {\r
+       return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    //\r
+    // Check Capsule image without populate flag is supported by firmware\r
+    //\r
+    if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) == 0) {\r
+      Status = SupportCapsuleImage (CapsuleHeader);\r
+      if (EFI_ERROR(Status)) {\r
+        return Status;\r
+      }\r
     }\r
   }\r
 \r
   //\r
-  //Assume that capsules have the same flags on reseting or not.\r
+  // Find out whether there is any capsule defined to persist across system reset.\r
   //\r
-  CapsuleHeader = CapsuleHeaderArray[0];\r
-  if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {\r
+  for (ArrayNumber = 0; ArrayNumber < CapsuleCount ; ArrayNumber++) {\r
+    CapsuleHeader = CapsuleHeaderArray[ArrayNumber];\r
+    if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {\r
+      NeedReset = TRUE;\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (NeedReset) {\r
     //\r
     //Check if the platform supports update capsule across a system reset\r
     //\r
-    if (!FeaturePcdGet(PcdSupportUpdateCapsuleRest)) {\r
+    if (!IsPersistAcrossResetCapsuleSupported ()) {\r
       return EFI_UNSUPPORTED;\r
     }\r
     *ResetType = EfiResetWarm;\r
-    *MaxiumCapsuleSize = FixedPcdGet32(PcdMaxSizePopulateCapsule);\r
+    *MaxiumCapsuleSize = (UINT64) mMaxSizePopulateCapsule;\r
   } else {\r
+    //\r
+    // For non-reset capsule image.\r
+    //\r
     *ResetType = EfiResetCold;\r
-    *MaxiumCapsuleSize = FixedPcdGet32(PcdMaxSizeNonPopulateCapsule);\r
+    *MaxiumCapsuleSize = (UINT64) mMaxSizeNonPopulateCapsule;\r
   }\r
+\r
   return EFI_SUCCESS;\r
 }\r
 \r
 \r
+/**\r
+\r
+  This code installs UEFI capsule runtime service.\r
+\r
+  @param  ImageHandle    The firmware allocated handle for the EFI image.\r
+  @param  SystemTable    A pointer to the EFI System Table.\r
+\r
+  @retval EFI_SUCCESS    UEFI Capsule Runtime Services are installed successfully.\r
+\r
+**/\r
 EFI_STATUS\r
 EFIAPI\r
 CapsuleServiceInitialize (\r
   IN EFI_HANDLE         ImageHandle,\r
   IN EFI_SYSTEM_TABLE   *SystemTable\r
   )\r
-/*++\r
-\r
-Routine Description:\r
-\r
-  This code is capsule runtime service initialization.\r
-\r
-Arguments:\r
-\r
-  ImageHandle          The image handle\r
-  SystemTable          The system table.\r
-\r
-Returns:\r
-\r
-  EFI STATUS\r
-\r
---*/\r
 {\r
   EFI_STATUS  Status;\r
-  EFI_HANDLE  NewHandle;\r
 \r
-  SystemTable->RuntimeServices->UpdateCapsule                    = UpdateCapsule;\r
-  SystemTable->RuntimeServices->QueryCapsuleCapabilities         = QueryCapsuleCapabilities;\r
+  mMaxSizePopulateCapsule = PcdGet32(PcdMaxSizePopulateCapsule);\r
+  mMaxSizeNonPopulateCapsule = PcdGet32(PcdMaxSizeNonPopulateCapsule);\r
 \r
   //\r
-  // Now install the Capsule Architectural Protocol on a new handle\r
+  // When PEI phase is IA32, DXE phase is X64, it is possible that capsule data are\r
+  // put above 4GB, so capsule PEI will transfer to long mode to get capsule data.\r
+  // The page table and stack is used to transfer processor mode from IA32 to long mode.\r
+  // Create the base address of page table and stack, and save them into variable.\r
+  // This is not needed when capsule with reset type is not supported.\r
   //\r
-  NewHandle = NULL;\r
+  SaveLongModeContext ();\r
 \r
+  //\r
+  // Install capsule runtime services into UEFI runtime service tables.\r
+  //\r
+  gRT->UpdateCapsule                    = UpdateCapsule;\r
+  gRT->QueryCapsuleCapabilities         = QueryCapsuleCapabilities;\r
+\r
+  //\r
+  // Install the Capsule Architectural Protocol on a new handle\r
+  // to signify the capsule runtime services are ready.\r
+  //\r
   Status = gBS->InstallMultipleProtocolInterfaces (\r
-                  &NewHandle,\r
+                  &mNewHandle,\r
                   &gEfiCapsuleArchProtocolGuid,\r
                   NULL,\r
                   NULL\r
                   );\r
   ASSERT_EFI_ERROR (Status);\r
 \r
-  return EFI_SUCCESS;\r
+  return Status;\r
 }\r