It is the follow up of commits 51f0ceb..9e32e97 to replace AsciiValueToString/UnicodeValueToString with AsciiValueToStringS/UnicodeValueToStringS. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Star Zeng <star.zeng@intel.com>
		
			
				
	
	
		
			415 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			415 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Capsule Runtime Driver produces two UEFI capsule runtime services.
 | |
|   (UpdateCapsule, QueryCapsuleCapabilities)
 | |
|   It installs the Capsule Architectural Protocol defined in PI1.0a to signify 
 | |
|   the capsule runtime services are ready.
 | |
| 
 | |
| Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>
 | |
| This program and the accompanying materials
 | |
| are licensed and made available under the terms and conditions of the BSD License
 | |
| which accompanies this distribution.  The full text of the license may be found at
 | |
| http://opensource.org/licenses/bsd-license.php
 | |
| 
 | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include <Uefi.h>
 | |
| 
 | |
| #include <Protocol/Capsule.h>
 | |
| #include <Guid/CapsuleVendor.h>
 | |
| #include <Guid/FmpCapsule.h>
 | |
| 
 | |
| #include <Library/DebugLib.h>
 | |
| #include <Library/PcdLib.h>
 | |
| #include <Library/CapsuleLib.h>
 | |
| #include <Library/UefiDriverEntryPoint.h>
 | |
| #include <Library/UefiBootServicesTableLib.h>
 | |
| #include <Library/UefiRuntimeServicesTableLib.h>
 | |
| #include <Library/UefiRuntimeLib.h>
 | |
| #include <Library/BaseLib.h>
 | |
| #include <Library/PrintLib.h>
 | |
| #include <Library/BaseMemoryLib.h>
 | |
| //
 | |
| // Handle for the installation of Capsule Architecture Protocol.
 | |
| //
 | |
| EFI_HANDLE  mNewHandle = NULL;
 | |
| 
 | |
| //
 | |
| // The times of calling UpdateCapsule ()
 | |
| //
 | |
| UINTN       mTimes      = 0;
 | |
| 
 | |
| UINT32      mMaxSizePopulateCapsule     = 0;
 | |
| UINT32      mMaxSizeNonPopulateCapsule  = 0;
 | |
| 
 | |
| /**
 | |
|   Create the variable to save the base address of page table and stack
 | |
|   for transferring into long mode in IA32 PEI.
 | |
| **/
 | |
| VOID
 | |
| SaveLongModeContext (
 | |
|   VOID
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   Passes capsules to the firmware with both virtual and physical mapping. Depending on the intended
 | |
|   consumption, the firmware may process the capsule immediately. If the payload should persist
 | |
|   across a system reset, the reset value returned from EFI_QueryCapsuleCapabilities must
 | |
|   be passed into ResetSystem() and will cause the capsule to be processed by the firmware as
 | |
|   part of the reset process.
 | |
| 
 | |
|   @param  CapsuleHeaderArray    Virtual pointer to an array of virtual pointers to the capsules
 | |
|                                 being passed into update capsule.
 | |
|   @param  CapsuleCount          Number of pointers to EFI_CAPSULE_HEADER in
 | |
|                                 CaspuleHeaderArray.
 | |
|   @param  ScatterGatherList     Physical pointer to a set of
 | |
|                                 EFI_CAPSULE_BLOCK_DESCRIPTOR that describes the
 | |
|                                 location in physical memory of a set of capsules.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Valid capsule was passed. If
 | |
|                                 CAPSULE_FLAGS_PERSIT_ACROSS_RESET is not set, the
 | |
|                                 capsule has been successfully processed by the firmware.
 | |
|   @retval EFI_DEVICE_ERROR      The capsule update was started, but failed due to a device error.
 | |
|   @retval EFI_INVALID_PARAMETER CapsuleSize is NULL, or an incompatible set of flags were
 | |
|                                 set in the capsule header.
 | |
|   @retval EFI_INVALID_PARAMETER CapsuleCount is Zero.
 | |
|   @retval EFI_INVALID_PARAMETER For across reset capsule image, ScatterGatherList is NULL.
 | |
|   @retval EFI_UNSUPPORTED       CapsuleImage is not recognized by the firmware.
 | |
|   @retval EFI_OUT_OF_RESOURCES  When ExitBootServices() has been previously called this error indicates the capsule 
 | |
|                                 is compatible with this platform but is not capable of being submitted or processed 
 | |
|                                 in runtime. The caller may resubmit the capsule prior to ExitBootServices().
 | |
|   @retval EFI_OUT_OF_RESOURCES  When ExitBootServices() has not been previously called then this error indicates 
 | |
|                                 the capsule is compatible with this platform but there are insufficient resources to process.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| UpdateCapsule (
 | |
|   IN EFI_CAPSULE_HEADER      **CapsuleHeaderArray,
 | |
|   IN UINTN                   CapsuleCount,
 | |
|   IN EFI_PHYSICAL_ADDRESS    ScatterGatherList OPTIONAL
 | |
|   )
 | |
| {
 | |
|   UINTN                     ArrayNumber;
 | |
|   EFI_STATUS                Status;
 | |
|   EFI_CAPSULE_HEADER        *CapsuleHeader;
 | |
|   BOOLEAN                   NeedReset;
 | |
|   BOOLEAN                   InitiateReset;
 | |
|   CHAR16                    CapsuleVarName[30];
 | |
|   CHAR16                    *TempVarName;  
 | |
|   
 | |
|   //
 | |
|   // Capsule Count can't be less than one.
 | |
|   //
 | |
|   if (CapsuleCount < 1) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   NeedReset         = FALSE;
 | |
|   InitiateReset     = FALSE;
 | |
|   CapsuleHeader     = NULL;
 | |
|   CapsuleVarName[0] = 0;
 | |
| 
 | |
|   for (ArrayNumber = 0; ArrayNumber < CapsuleCount; ArrayNumber++) {
 | |
|     //
 | |
|     // A capsule which has the CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE flag must have
 | |
|     // CAPSULE_FLAGS_PERSIST_ACROSS_RESET set in its header as well.
 | |
|     //
 | |
|     CapsuleHeader = CapsuleHeaderArray[ArrayNumber];
 | |
|     if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE)) == CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
|     //
 | |
|     // A capsule which has the CAPSULE_FLAGS_INITIATE_RESET flag must have
 | |
|     // CAPSULE_FLAGS_PERSIST_ACROSS_RESET set in its header as well.
 | |
|     //
 | |
|     if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Check FMP capsule flag 
 | |
|     //
 | |
|     if (CompareGuid(&CapsuleHeader->CapsuleGuid, &gEfiFmpCapsuleGuid)
 | |
|      && (CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0 ) {
 | |
|        return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Check Capsule image without populate flag by firmware support capsule function  
 | |
|     //
 | |
|     if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) == 0) {
 | |
|       Status = SupportCapsuleImage (CapsuleHeader);
 | |
|       if (EFI_ERROR(Status)) {
 | |
|         return Status;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Walk through all capsules, record whether there is a capsule needs reset
 | |
|   // or initiate reset. And then process capsules which has no reset flag directly.
 | |
|   //
 | |
|   for (ArrayNumber = 0; ArrayNumber < CapsuleCount ; ArrayNumber++) {
 | |
|     CapsuleHeader = CapsuleHeaderArray[ArrayNumber];
 | |
|     //
 | |
|     // Here should be in the boot-time for non-reset capsule image
 | |
|     // Platform specific update for the non-reset capsule image.
 | |
|     //
 | |
|     if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) == 0) {
 | |
|       if (EfiAtRuntime ()) { 
 | |
|         Status = EFI_OUT_OF_RESOURCES;
 | |
|       } else {
 | |
|         Status = ProcessCapsuleImage(CapsuleHeader);
 | |
|       }
 | |
|       if (EFI_ERROR(Status)) {
 | |
|         return Status;
 | |
|       }
 | |
|     } else {
 | |
|       NeedReset = TRUE;
 | |
|       if ((CapsuleHeader->Flags & CAPSULE_FLAGS_INITIATE_RESET) != 0) {
 | |
|         InitiateReset = TRUE;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // After launching all capsules who has no reset flag, if no more capsules claims
 | |
|   // for a system reset just return.
 | |
|   //
 | |
|   if (!NeedReset) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // ScatterGatherList is only referenced if the capsules are defined to persist across
 | |
|   // system reset. 
 | |
|   //
 | |
|   if (ScatterGatherList == (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check if the platform supports update capsule across a system reset
 | |
|   //
 | |
|   if (!FeaturePcdGet(PcdSupportUpdateCapsuleReset)) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Construct variable name CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
 | |
|   // if user calls UpdateCapsule multiple times.
 | |
|   //
 | |
|   StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CHAR16), EFI_CAPSULE_VARIABLE_NAME);
 | |
|   TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
 | |
|   if (mTimes > 0) {
 | |
|     UnicodeValueToStringS (
 | |
|       TempVarName,
 | |
|       sizeof (CapsuleVarName) - ((UINTN)TempVarName - (UINTN)CapsuleVarName),
 | |
|       0,
 | |
|       mTimes,
 | |
|       0
 | |
|       );
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // ScatterGatherList is only referenced if the capsules are defined to persist across
 | |
|   // system reset. Set its value into NV storage to let pre-boot driver to pick it up 
 | |
|   // after coming through a system reset.
 | |
|   //
 | |
|   Status = EfiSetVariable (
 | |
|              CapsuleVarName,
 | |
|              &gEfiCapsuleVendorGuid,
 | |
|              EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,
 | |
|              sizeof (UINTN),
 | |
|              (VOID *) &ScatterGatherList
 | |
|              );
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|      //
 | |
|      // Variable has been set successfully, increase variable index.
 | |
|      //
 | |
|      mTimes++;
 | |
|      if(InitiateReset) {
 | |
|        //
 | |
|        // Firmware that encounters a capsule which has the CAPSULE_FLAGS_INITIATE_RESET Flag set in its header
 | |
|        // will initiate a reset of the platform which is compatible with the passed-in capsule request and will 
 | |
|        // not return back to the caller.
 | |
|        //
 | |
|        EfiResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
 | |
|      }
 | |
|   }
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Returns if the capsule can be supported via UpdateCapsule().
 | |
| 
 | |
|   @param  CapsuleHeaderArray    Virtual pointer to an array of virtual pointers to the capsules
 | |
|                                 being passed into update capsule.
 | |
|   @param  CapsuleCount          Number of pointers to EFI_CAPSULE_HEADER in
 | |
|                                 CaspuleHeaderArray.
 | |
|   @param  MaxiumCapsuleSize     On output the maximum size that UpdateCapsule() can
 | |
|                                 support as an argument to UpdateCapsule() via
 | |
|                                 CapsuleHeaderArray and ScatterGatherList.
 | |
|   @param  ResetType             Returns the type of reset required for the capsule update.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Valid answer returned.
 | |
|   @retval EFI_UNSUPPORTED       The capsule image is not supported on this platform, and
 | |
|                                 MaximumCapsuleSize and ResetType are undefined.
 | |
|   @retval EFI_INVALID_PARAMETER MaximumCapsuleSize is NULL, or ResetTyep is NULL,
 | |
|                                 Or CapsuleCount is Zero, or CapsuleImage is not valid.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| QueryCapsuleCapabilities (
 | |
|   IN  EFI_CAPSULE_HEADER   **CapsuleHeaderArray,
 | |
|   IN  UINTN                CapsuleCount,
 | |
|   OUT UINT64               *MaxiumCapsuleSize,
 | |
|   OUT EFI_RESET_TYPE       *ResetType
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                Status;
 | |
|   UINTN                     ArrayNumber;
 | |
|   EFI_CAPSULE_HEADER        *CapsuleHeader;
 | |
|   BOOLEAN                   NeedReset;
 | |
| 
 | |
|   //
 | |
|   // Capsule Count can't be less than one.
 | |
|   //
 | |
|   if (CapsuleCount < 1) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // Check whether input parameter is valid
 | |
|   //
 | |
|   if ((MaxiumCapsuleSize == NULL) ||(ResetType == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   CapsuleHeader = NULL;
 | |
|   NeedReset     = FALSE;
 | |
| 
 | |
|   for (ArrayNumber = 0; ArrayNumber < CapsuleCount; ArrayNumber++) {
 | |
|     CapsuleHeader = CapsuleHeaderArray[ArrayNumber];
 | |
|     //
 | |
|     // A capsule which has the CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE flag must have
 | |
|     // CAPSULE_FLAGS_PERSIST_ACROSS_RESET set in its header as well.
 | |
|     //
 | |
|     if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE)) == CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
|     //
 | |
|     // A capsule which has the CAPSULE_FLAGS_INITIATE_RESET flag must have
 | |
|     // CAPSULE_FLAGS_PERSIST_ACROSS_RESET set in its header as well.
 | |
|     //
 | |
|     if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Check FMP capsule flag 
 | |
|     //
 | |
|     if (CompareGuid(&CapsuleHeader->CapsuleGuid, &gEfiFmpCapsuleGuid)
 | |
|      && (CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0 ) {
 | |
|        return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Check Capsule image without populate flag is supported by firmware
 | |
|     //
 | |
|     if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) == 0) {
 | |
|       Status = SupportCapsuleImage (CapsuleHeader);
 | |
|       if (EFI_ERROR(Status)) {
 | |
|         return Status;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Find out whether there is any capsule defined to persist across system reset. 
 | |
|   //
 | |
|   for (ArrayNumber = 0; ArrayNumber < CapsuleCount ; ArrayNumber++) {
 | |
|     CapsuleHeader = CapsuleHeaderArray[ArrayNumber];
 | |
|     if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {
 | |
|       NeedReset = TRUE;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (NeedReset) {
 | |
|     //
 | |
|     //Check if the platform supports update capsule across a system reset
 | |
|     //
 | |
|     if (!FeaturePcdGet(PcdSupportUpdateCapsuleReset)) {
 | |
|       return EFI_UNSUPPORTED;
 | |
|     }
 | |
|     *ResetType = EfiResetWarm;
 | |
|     *MaxiumCapsuleSize = (UINT64) mMaxSizePopulateCapsule;
 | |
|   } else {
 | |
|     //
 | |
|     // For non-reset capsule image.
 | |
|     //
 | |
|     *ResetType = EfiResetCold;
 | |
|     *MaxiumCapsuleSize = (UINT64) mMaxSizeNonPopulateCapsule;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
| 
 | |
|   This code installs UEFI capsule runtime service.
 | |
| 
 | |
|   @param  ImageHandle    The firmware allocated handle for the EFI image.  
 | |
|   @param  SystemTable    A pointer to the EFI System Table.
 | |
| 
 | |
|   @retval EFI_SUCCESS    UEFI Capsule Runtime Services are installed successfully. 
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| CapsuleServiceInitialize (
 | |
|   IN EFI_HANDLE         ImageHandle,
 | |
|   IN EFI_SYSTEM_TABLE   *SystemTable
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   mMaxSizePopulateCapsule = PcdGet32(PcdMaxSizePopulateCapsule);
 | |
|   mMaxSizeNonPopulateCapsule = PcdGet32(PcdMaxSizeNonPopulateCapsule);
 | |
| 
 | |
|   //
 | |
|   // When PEI phase is IA32, DXE phase is X64, it is possible that capsule data are 
 | |
|   // put above 4GB, so capsule PEI will transfer to long mode to get capsule data.
 | |
|   // The page table and stack is used to transfer processor mode from IA32 to long mode.
 | |
|   // Create the base address of page table and stack, and save them into variable.
 | |
|   // This is not needed when capsule with reset type is not supported.
 | |
|   //
 | |
|   SaveLongModeContext ();
 | |
|     
 | |
|   //
 | |
|   // Install capsule runtime services into UEFI runtime service tables.
 | |
|   //
 | |
|   gRT->UpdateCapsule                    = UpdateCapsule;
 | |
|   gRT->QueryCapsuleCapabilities         = QueryCapsuleCapabilities;
 | |
| 
 | |
|   //
 | |
|   // Install the Capsule Architectural Protocol on a new handle
 | |
|   // to signify the capsule runtime services are ready.
 | |
|   //
 | |
|   Status = gBS->InstallMultipleProtocolInterfaces (
 | |
|                   &mNewHandle,
 | |
|                   &gEfiCapsuleArchProtocolGuid,
 | |
|                   NULL,
 | |
|                   NULL
 | |
|                   );
 | |
|   ASSERT_EFI_ERROR (Status);
 | |
| 
 | |
|   return Status;
 | |
| }
 |