]> git.proxmox.com Git - mirror_edk2.git/blobdiff - IntelFrameworkModulePkg/Library/DxeCapsuleLib/DxeCapsuleLib.c
IntelFrameworkModulePkg/DxeCapsuleLib: Add ProcessCapsules().
[mirror_edk2.git] / IntelFrameworkModulePkg / Library / DxeCapsuleLib / DxeCapsuleLib.c
index 6f5ab5b0f55e5ea7c0c6f7b14d746b66bf2b3420..eaed06e0120f28aeaa1df89b620d4032902cadbc 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
-  Capsule Library instance to update capsule image to flash.\r
+  Capsule Library instance to process capsule images.\r
 \r
-  Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>\r
 \r
   This program and the accompanying materials\r
   are licensed and made available under the terms and conditions of the BSD License\r
 \r
 **/\r
 #include <PiDxe.h>\r
+\r
 #include <Guid/Capsule.h>\r
+#include <Guid/FmpCapsule.h>\r
+\r
 #include <Library/DebugLib.h>\r
 #include <Library/BaseMemoryLib.h>\r
 #include <Library/DxeServicesTableLib.h>\r
 #include <Library/MemoryAllocationLib.h>\r
 #include <Library/CapsuleLib.h>\r
+#include <Library/GenericBdsLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/DevicePathLib.h>\r
+\r
+#include <Protocol/FirmwareManagement.h>\r
+#include <Protocol/DevicePath.h>\r
+\r
+\r
+/**\r
+  Function indicate the current completion progress of the firmware\r
+  update. Platform may override with own specific progress function.\r
+\r
+  @param  Completion    A value between 1 and 100 indicating the current completion progress of the firmware update\r
+\r
+  @retval EFI_SUCESS    Input capsule is a correct FMP capsule.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Update_Image_Progress (\r
+   IN UINTN Completion\r
+)\r
+{\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Validate Fmp capsules layout.\r
+\r
+  @param  CapsuleHeader    Points to a capsule header.\r
+\r
+  @retval EFI_SUCESS                     Input capsule is a correct FMP capsule.\r
+  @retval EFI_INVALID_PARAMETER  Input capsule is not a correct FMP capsule.\r
+**/\r
+EFI_STATUS\r
+ValidateFmpCapsule (\r
+  IN EFI_CAPSULE_HEADER *CapsuleHeader\r
+  )\r
+{\r
+  EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER       *FmpCapsuleHeader;\r
+  UINT8                                        *EndOfCapsule;\r
+  EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;\r
+  UINT8                                        *EndOfPayload;\r
+  UINT64                                       *ItemOffsetList;\r
+  UINT32                                       ItemNum;\r
+  UINTN                                        Index;\r
+\r
+  FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize);\r
+  EndOfCapsule     = (UINT8 *) CapsuleHeader + CapsuleHeader->CapsuleImageSize;\r
+\r
+  if (FmpCapsuleHeader->Version > EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);\r
+\r
+  ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount;\r
+\r
+  if (ItemNum == FmpCapsuleHeader->EmbeddedDriverCount) {\r
+    //\r
+    // No payload element \r
+    //\r
+    if (((UINT8 *)FmpCapsuleHeader + ItemOffsetList[ItemNum - 1]) < EndOfCapsule) {\r
+      return EFI_SUCCESS;\r
+    } else {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  if (FmpCapsuleHeader->PayloadItemCount != 0) {\r
+    //\r
+    // Check if the last payload is within capsule image range\r
+    //\r
+    ImageHeader  = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[ItemNum - 1]);\r
+    EndOfPayload = (UINT8 *)(ImageHeader + 1) + ImageHeader->UpdateImageSize + ImageHeader->UpdateVendorCodeSize;\r
+  } else {\r
+    //\r
+    // No driver & payload element in FMP\r
+    //\r
+    EndOfPayload = (UINT8 *)(FmpCapsuleHeader + 1);\r
+  }\r
+\r
+  if (EndOfPayload != EndOfCapsule) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // All the address in ItemOffsetList must be stored in ascending order\r
+  //\r
+  if (ItemNum >= 2) {\r
+    for (Index = 0; Index < ItemNum - 1; Index++) {\r
+      if (ItemOffsetList[Index] >= ItemOffsetList[Index + 1]) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Process Firmware management protocol data capsule.  \r
+\r
+  @param  CapsuleHeader         Points to a capsule header.\r
+\r
+  @retval EFI_SUCESS            Process Capsule Image successfully.\r
+  @retval EFI_UNSUPPORTED       Capsule image is not supported by the firmware.\r
+  @retval EFI_VOLUME_CORRUPTED  FV volume in the capsule is corrupted.\r
+  @retval EFI_OUT_OF_RESOURCES  Not enough memory.\r
+**/\r
+EFI_STATUS\r
+ProcessFmpCapsuleImage (\r
+  IN EFI_CAPSULE_HEADER *CapsuleHeader\r
+  )\r
+{\r
+  EFI_STATUS                                    Status;\r
+  EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER        *FmpCapsuleHeader;\r
+  EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER  *ImageHeader;\r
+  EFI_HANDLE                                    ImageHandle;\r
+  UINT64                                        *ItemOffsetList;\r
+  UINT32                                        ItemNum;\r
+  UINTN                                         Index;\r
+  UINTN                                         ExitDataSize;\r
+  EFI_HANDLE                                    *HandleBuffer;\r
+  EFI_FIRMWARE_MANAGEMENT_PROTOCOL              *Fmp;\r
+  UINTN                                         NumberOfHandles;\r
+  UINTN                                         DescriptorSize;\r
+  UINT8                                         FmpImageInfoCount;\r
+  UINT32                                        FmpImageInfoDescriptorVer;\r
+  UINTN                                         ImageInfoSize;\r
+  UINT32                                        PackageVersion;\r
+  CHAR16                                        *PackageVersionName;\r
+  CHAR16                                        *AbortReason;\r
+  EFI_FIRMWARE_IMAGE_DESCRIPTOR                 *FmpImageInfoBuf;\r
+  EFI_FIRMWARE_IMAGE_DESCRIPTOR                 *TempFmpImageInfo;\r
+  UINTN                                         DriverLen;\r
+  UINTN                                         Index1;\r
+  UINTN                                         Index2;\r
+  MEMMAP_DEVICE_PATH                            MemMapNode;\r
+  EFI_DEVICE_PATH_PROTOCOL                      *DriverDevicePath;\r
+\r
+  Status           = EFI_SUCCESS;\r
+  HandleBuffer     = NULL;\r
+  ExitDataSize     = 0;\r
+  DriverDevicePath = NULL;\r
+\r
+  FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize);\r
+\r
+  if (FmpCapsuleHeader->Version > EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);\r
+\r
+  ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount;\r
+\r
+  //\r
+  // capsule in which driver count and payload count are both zero is not processed.\r
+  //\r
+  if (ItemNum == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // 1. ConnectAll to ensure \r
+  //    All the communication protocol required by driver in capsule installed \r
+  //    All FMP protocols are installed\r
+  //\r
+  BdsLibConnectAll();\r
+\r
+\r
+  //\r
+  // 2. Try to load & start all the drivers within capsule \r
+  //\r
+  SetDevicePathNodeLength (&MemMapNode.Header, sizeof (MemMapNode));\r
+  MemMapNode.Header.Type     = HARDWARE_DEVICE_PATH;\r
+  MemMapNode.Header.SubType  = HW_MEMMAP_DP;\r
+  MemMapNode.MemoryType      = EfiBootServicesCode;\r
+  MemMapNode.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CapsuleHeader;\r
+  MemMapNode.EndingAddress   = (EFI_PHYSICAL_ADDRESS)(UINTN)((UINT8 *)CapsuleHeader + CapsuleHeader->CapsuleImageSize - 1);\r
+\r
+  DriverDevicePath = AppendDevicePathNode (NULL, &MemMapNode.Header);\r
+  if (DriverDevicePath == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  for (Index = 0; Index < FmpCapsuleHeader->EmbeddedDriverCount; Index++) {\r
+    if (FmpCapsuleHeader->PayloadItemCount == 0 && Index == (UINTN)FmpCapsuleHeader->EmbeddedDriverCount - 1) {\r
+      //\r
+      // When driver is last element in the ItemOffsetList array, the driver size is calculated by reference CapsuleImageSize in EFI_CAPSULE_HEADER\r
+      //\r
+      DriverLen = CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize - (UINTN)ItemOffsetList[Index];\r
+    } else {\r
+      DriverLen = (UINTN)ItemOffsetList[Index + 1] - (UINTN)ItemOffsetList[Index];\r
+    }\r
+\r
+    Status = gBS->LoadImage(\r
+                    FALSE,\r
+                    gImageHandle,\r
+                    DriverDevicePath,\r
+                    (UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index],\r
+                    DriverLen,\r
+                    &ImageHandle\r
+                    );\r
+    if (EFI_ERROR(Status)) {\r
+      goto EXIT;\r
+    }\r
+\r
+    Status = gBS->StartImage(\r
+                    ImageHandle, \r
+                    &ExitDataSize, \r
+                    NULL\r
+                    );\r
+    if (EFI_ERROR(Status)) {\r
+      DEBUG ((DEBUG_ERROR, "Driver Return Status = %r\n", Status));\r
+      goto EXIT;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Connnect all again to connect drivers within capsule \r
+  //\r
+  if (FmpCapsuleHeader->EmbeddedDriverCount > 0) {\r
+    BdsLibConnectAll();\r
+  }\r
+\r
+  //\r
+  // 3. Route payload to right FMP instance\r
+  //\r
+  Status = gBS->LocateHandleBuffer (\r
+                  ByProtocol,\r
+                  &gEfiFirmwareManagementProtocolGuid,\r
+                  NULL,\r
+                  &NumberOfHandles,\r
+                  &HandleBuffer\r
+                  );\r
+\r
+  if (!EFI_ERROR(Status)) {\r
+    for(Index1 = 0; Index1 < NumberOfHandles; Index1++) {\r
+      Status = gBS->HandleProtocol(\r
+                      HandleBuffer[Index1],\r
+                      &gEfiFirmwareManagementProtocolGuid,\r
+                      (VOID **)&Fmp\r
+                      );\r
+      if (EFI_ERROR(Status)) {\r
+        continue;\r
+      }\r
+\r
+      ImageInfoSize = 0;\r
+      Status = Fmp->GetImageInfo (\r
+                      Fmp,\r
+                      &ImageInfoSize,\r
+                      NULL,\r
+                      NULL,\r
+                      NULL,\r
+                      NULL,\r
+                      NULL,\r
+                      NULL\r
+                      );\r
+      if (Status != EFI_BUFFER_TOO_SMALL) {\r
+        continue;\r
+      }\r
+\r
+      FmpImageInfoBuf = NULL;\r
+      FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize);\r
+      if (FmpImageInfoBuf == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto EXIT;\r
+      }\r
+\r
+      PackageVersionName = NULL;\r
+      Status = Fmp->GetImageInfo (\r
+                      Fmp,\r
+                      &ImageInfoSize,               // ImageInfoSize\r
+                      FmpImageInfoBuf,              // ImageInfo\r
+                      &FmpImageInfoDescriptorVer,   // DescriptorVersion\r
+                      &FmpImageInfoCount,           // DescriptorCount\r
+                      &DescriptorSize,              // DescriptorSize\r
+                      &PackageVersion,              // PackageVersion\r
+                      &PackageVersionName           // PackageVersionName\r
+                      );\r
+\r
+      //\r
+      // If FMP GetInformation interface failed, skip this resource\r
+      //\r
+      if (EFI_ERROR(Status)) {\r
+        FreePool(FmpImageInfoBuf);\r
+        continue;\r
+      }\r
+\r
+      if (PackageVersionName != NULL) {\r
+        FreePool(PackageVersionName);\r
+      }\r
+\r
+      TempFmpImageInfo = FmpImageInfoBuf;\r
+      for (Index2 = 0; Index2 < FmpImageInfoCount; Index2++) {\r
+        //\r
+        // Check all the payload entry in capsule payload list \r
+        //\r
+        for (Index = FmpCapsuleHeader->EmbeddedDriverCount; Index < ItemNum; Index++) {\r
+          ImageHeader  = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]);\r
+          if (CompareGuid(&ImageHeader->UpdateImageTypeId, &TempFmpImageInfo->ImageTypeId) &&\r
+              ImageHeader->UpdateImageIndex == TempFmpImageInfo->ImageIndex) {\r
+            AbortReason = NULL;\r
+            if (ImageHeader->UpdateVendorCodeSize == 0) {\r
+              Status = Fmp->SetImage(\r
+                              Fmp,\r
+                              TempFmpImageInfo->ImageIndex,           // ImageIndex\r
+                              (UINT8 *)(ImageHeader + 1),             // Image\r
+                              ImageHeader->UpdateImageSize,           // ImageSize\r
+                              NULL,                                   // VendorCode\r
+                              Update_Image_Progress,                  // Progress\r
+                              &AbortReason                            // AbortReason\r
+                              );\r
+            } else {\r
+              Status = Fmp->SetImage(\r
+                              Fmp,\r
+                              TempFmpImageInfo->ImageIndex,                                          // ImageIndex\r
+                              (UINT8 *)(ImageHeader + 1),                                            // Image\r
+                              ImageHeader->UpdateImageSize,                                          // ImageSize\r
+                              (UINT8 *)((UINT8 *) (ImageHeader + 1) + ImageHeader->UpdateImageSize), // VendorCode\r
+                              Update_Image_Progress,                                                 // Progress\r
+                              &AbortReason                                                           // AbortReason\r
+                              );\r
+            }\r
+            if (AbortReason != NULL) {\r
+              DEBUG ((EFI_D_ERROR, "%s\n", AbortReason));\r
+              FreePool(AbortReason);\r
+            }\r
+          }\r
+        }\r
+        //\r
+        // Use DescriptorSize to move ImageInfo Pointer to stay compatible with different ImageInfo version\r
+        //\r
+        TempFmpImageInfo = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)((UINT8 *)TempFmpImageInfo + DescriptorSize);\r
+      }\r
+      FreePool(FmpImageInfoBuf);\r
+    }\r
+  }\r
+\r
+EXIT:\r
+\r
+  if (HandleBuffer != NULL) {\r
+    FreePool(HandleBuffer);\r
+  }\r
+\r
+  if (DriverDevicePath != NULL) {\r
+    FreePool(DriverDevicePath);\r
+  }\r
+\r
+  return Status;\r
+}\r
 \r
 /**\r
   Those capsules supported by the firmwares.\r
 \r
   @retval EFI_SUCESS       Input capsule is supported by firmware.\r
   @retval EFI_UNSUPPORTED  Input capsule is not supported by the firmware.\r
+  @retval EFI_INVALID_PARAMETER Input capsule layout is not correct\r
 **/\r
 EFI_STATUS\r
 EFIAPI\r
@@ -38,6 +393,13 @@ SupportCapsuleImage (
     return EFI_SUCCESS;\r
   }\r
 \r
+  if (CompareGuid (&gEfiFmpCapsuleGuid, &CapsuleHeader->CapsuleGuid)) {\r
+    //\r
+    // Check layout of FMP capsule\r
+    //\r
+    return ValidateFmpCapsule(CapsuleHeader);\r
+  }\r
+\r
   return EFI_UNSUPPORTED;\r
 }\r
 \r
@@ -72,6 +434,21 @@ ProcessCapsuleImage (
     return EFI_UNSUPPORTED;\r
   }\r
 \r
+  //\r
+  // Check FMP capsule layout\r
+  //\r
+  if (CompareGuid (&gEfiFmpCapsuleGuid, &CapsuleHeader->CapsuleGuid)){\r
+    Status = ValidateFmpCapsule(CapsuleHeader);\r
+    if (EFI_ERROR(Status)) {\r
+      return Status;\r
+    }\r
+\r
+    //\r
+    // Press EFI FMP Capsule\r
+    //\r
+    return ProcessFmpCapsuleImage(CapsuleHeader);\r
+  }\r
+\r
   //\r
   // Skip the capsule header, move to the Firware Volume\r
   //\r
@@ -138,4 +515,42 @@ ProcessCapsuleImage (
   return Status;\r
 }\r
 \r
+/**\r
+\r
+  This routine is called to process capsules.\r
+\r
+  Caution: This function may receive untrusted input.\r
+\r
+  The capsules reported in EFI_HOB_UEFI_CAPSULE are processed.\r
+  If there is no EFI_HOB_UEFI_CAPSULE, this routine does nothing.\r
+\r
+  This routine should be called twice in BDS.\r
+  1) The first call must be before EndOfDxe. The system capsules is processed.\r
+     If device capsule FMP protocols are exposted at this time and device FMP\r
+     capsule has zero EmbeddedDriverCount, the device capsules are processed.\r
+     Each individual capsule result is recorded in capsule record variable.\r
+     System may reset in this function, if reset is required by capsule and\r
+     all capsules are processed.\r
+     If not all capsules are processed, reset will be defered to second call.\r
+\r
+  2) The second call must be after EndOfDxe and after ConnectAll, so that all\r
+     device capsule FMP protocols are exposed.\r
+     The system capsules are skipped. If the device capsules are NOT processed\r
+     in first call, they are processed here.\r
+     Each individual capsule result is recorded in capsule record variable.\r
+     System may reset in this function, if reset is required by capsule\r
+     processed in first call and second call.\r
+\r
+  @retval EFI_SUCCESS             There is no error when processing capsules.\r
+  @retval EFI_OUT_OF_RESOURCES    No enough resource to process capsules.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ProcessCapsules (\r
+  VOID\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
 \r