aboutsummaryrefslogtreecommitdiffstats
path: root/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c')
-rw-r--r--roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c397
1 files changed, 397 insertions, 0 deletions
diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c
new file mode 100644
index 000000000..2fba22dec
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c
@@ -0,0 +1,397 @@
+/** @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 - 2020, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "CapsuleService.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;
+
+/**
+ 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;
+
+ //
+ // Check if platform support Capsule In RAM or not.
+ // Platform could choose to drop CapsulePei/CapsuleX64 and do not support Capsule In RAM.
+ //
+ if (!PcdGetBool(PcdCapsuleInRamSupport)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // 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 () && !FeaturePcdGet (PcdSupportProcessCapsuleAtRuntime)) {
+ 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 (!IsPersistAcrossResetCapsuleSupported ()) {
+ return EFI_UNSUPPORTED;
+ }
+
+ CapsuleCacheWriteBack (ScatterGatherList);
+
+ //
+ // 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().
+ Notice: When PcdCapsuleInRamSupport is unsupported, even this routine returns a valid answer,
+ the capsule still is unsupported 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 (!IsPersistAcrossResetCapsuleSupported ()) {
+ 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;
+}