/** @file UEFI image capsule processor on firmware management protocol implementation ;****************************************************************************** ;* Copyright (c) 2012 - 2020, Insyde Software Corp. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ #include "CapsuleProcessorDxe.h" #define ESRT_LAST_ATTEMPT_VERSION L"EsrtLastAttemptVersion" #define ESRT_LAST_ATTEMPT_STATUS L"EsrtLastAttemptStatus" #define CAPSULE_RESULT_VARIABLE L"Capsule0000" #define CAPSULE_RESULT_MAX_VARIABLE L"CapsuleMax" #define CAPSULE_RESULT_LAST_VARIABLE L"CapsuleLast" #define EFI_CAPSULE_REPORT_GUID { 0x39b68c46, 0xf7fb, 0x441b, { 0xb6, 0xec, 0x16, 0xb0, 0xf6, 0x98, 0x21, 0xf3 }} #define VERSION_STRING_NOT_AVAILABLE L"VERSION STRING NOT AVAILABLE" STATIC CAPSULE_FMP_ENTRY mCapsuleFmpEntry; EFI_RESET_SYSTEM mOriginalResetSystemPtr; typedef struct { EFI_SIGNATURE_LIST SignatureListHeader; EFI_SIGNATURE_DATA SignatureData; } CERTIFICATE_DATA; typedef struct { UINT32 VariableTotalSize; UINT32 Reserved; //for alignment EFI_GUID CapsuleGuid; EFI_TIME CapsuleProcessed; EFI_STATUS CapsuleStatus; } EFI_CAPSULE_RESULT_VARIABLE_HEADER; VOID EFIAPI DummyResetSystem ( IN EFI_RESET_TYPE ResetType, IN EFI_STATUS ResetStatus, IN UINTN DataSize, IN VOID *ResetData OPTIONAL ) { return; } /** Hook gRT->ResetSystem to avoid system reset cause firmware update fail @param TRUE Hook gRT->ResetSystem FALSE Restore gRT->ResetSystem @retval None **/ VOID HookSystemResetRoutine ( IN BOOLEAN ToHook ) { if (ToHook) { mOriginalResetSystemPtr = gRT->ResetSystem; gRT->ResetSystem = DummyResetSystem; } else { gRT->ResetSystem = mOriginalResetSystemPtr; } } /** Convert ESRT status to FMP CheckImage result @param[in] Status The value of ESRT status @retval IMAGE_UPDATABLE_VALID Indicates SetImage() will accept the new image and update the device with the new image. @retval IMAGE_UPDATABLE_INVALID Indicates SetImage() will reject the new image. No additional information is provided for the rejection @retval IMAGE_UPDATABLE_INVALID_TYPE Indicates SetImage() will reject the new image. The rejection is due to the new image is not a firmware image recognized for this device @retval IMAGE_UPDATABLE_INVALID_OLD indicates SetImage() will reject the new image. The rejection is due to the new image version is older than the current firmware image version in the device **/ STATIC UINT32 ConvertEsrtStatusToCheckImageResult ( IN ESRT_STATUS EsrtStatus ) { UINT32 CheckImageResult; switch (EsrtStatus) { case ESRT_SUCCESS: CheckImageResult = IMAGE_UPDATABLE_VALID; break; case ESRT_ERROR_INVALID_IMAGE_FORMAT: CheckImageResult = IMAGE_UPDATABLE_INVALID_TYPE; break; case ESRT_ERROR_INCORRECT_VERSION: CheckImageResult = IMAGE_UPDATABLE_INVALID_OLD; break; default: CheckImageResult = IMAGE_UPDATABLE_INVALID; break; } return CheckImageResult; } /** Convert EFI Status to ESRT status @param[in] Status The value of EFI status @retval ESRT_SUCCESS @retval ESRT_ERROR_UNSUCCESSFUL @retval ESRT_ERROR_INSUFFICIENT_RESOURCES @retval ESRT_ERROR_INCORRECT_VERSION @retval ESRT_ERROR_INVALID_IMAGE_FORMAT @retval ESRT_ERROR_AUTHENTICATION @retval ESRT_ERROR_AC_NOT_CONNECTED @retval ESRT_ERROR_INSUFFICIENT_BATTERY **/ STATIC ESRT_STATUS ConvertEfiStatusToEsrtStatus ( IN EFI_STATUS Status ) { ESRT_STATUS EsrtStatus; switch (Status) { case EFI_SUCCESS: EsrtStatus = ESRT_SUCCESS; break; case EFI_INCOMPATIBLE_VERSION: EsrtStatus = ESRT_ERROR_INCORRECT_VERSION; break; case EFI_OUT_OF_RESOURCES: case EFI_VOLUME_FULL: EsrtStatus = ESRT_ERROR_INSUFFICIENT_RESOURCES; break; case EFI_UNSUPPORTED: case EFI_LOAD_ERROR: EsrtStatus = ESRT_ERROR_INVALID_IMAGE_FORMAT; break; case EFI_SECURITY_VIOLATION: EsrtStatus = ESRT_ERROR_AUTHENTICATION; break; default: EsrtStatus = ESRT_ERROR_UNSUCCESSFUL; break; } return EsrtStatus; } /** Convert EFI Status to ESRT status @param[in] Status The value of EFI status @retval ESRT_SUCCESS @retval ESRT_ERROR_UNSUCCESSFUL @retval ESRT_ERROR_INSUFFICIENT_RESOURCES @retval ESRT_ERROR_INCORRECT_VERSION @retval ESRT_ERROR_INVALID_IMAGE_FORMAT @retval ESRT_ERROR_AUTHENTICATION @retval ESRT_ERROR_AC_NOT_CONNECTED @retval ESRT_ERROR_INSUFFICIENT_BATTERY **/ STATIC EFI_STATUS ConvertEsrtStatusToEfiStatus ( IN ESRT_STATUS EsrtStatus ) { EFI_STATUS Status; switch (EsrtStatus) { case ESRT_SUCCESS: Status = EFI_SUCCESS; break; case ESRT_ERROR_INSUFFICIENT_RESOURCES: Status = EFI_OUT_OF_RESOURCES; break; case ESRT_ERROR_INCORRECT_VERSION: case ESRT_ERROR_INVALID_IMAGE_FORMAT: Status = EFI_INVALID_PARAMETER; break; case ESRT_ERROR_AUTHENTICATION: Status = EFI_ACCESS_DENIED; break; default: Status = EFI_ABORTED; break; } return Status; } /** Update capsule status onto corresponding variables, including ESRT status and Capsule#### and CapsuleLast variables according to UEFI 2.4B 7.5.6 @param[in] CapsuleGuid The pointer of capsule GUID @param[in] AttemptVersion The value of last attempt version @param[in] AttemptStatus The value of last attempt status **/ STATIC VOID UpdateCapsuleStatus ( IN EFI_GUID *CapsuleGuid, IN UINT32 AttemptVersion, IN ESRT_STATUS AttemptStatus, IN EFI_STATUS CapsuleStatus ) { EFI_STATUS Status; UINTN Size; EFI_CAPSULE_RESULT_VARIABLE_HEADER CapsuleResult; UINTN CapsuleResultCounter; UINTN CapsuleResultMaxCounter; CHAR16 CapsuleResultVariableName[16]; EFI_GUID CapsuleResultVariableGuid = EFI_CAPSULE_REPORT_GUID; // // Update ESRT version and status // CommonSetVariable ( ESRT_LAST_ATTEMPT_VERSION, CapsuleGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, sizeof(UINT32), &AttemptVersion ); CommonSetVariable ( ESRT_LAST_ATTEMPT_STATUS, CapsuleGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, sizeof(ESRT_STATUS), &AttemptStatus ); // // Calculate the variable name for UEFI 2.4B 7.5.6 // if ((PcdGet64 (PcdOsIndicationsSupported) & EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED) == 0) return; CapsuleResultMaxCounter = (UINTN)PcdGet16 (PcdCapsuleMaxResult); Size = sizeof (CHAR16) * 11; Status = CommonGetVariable ( CAPSULE_RESULT_MAX_VARIABLE, &CapsuleResultVariableGuid, &Size, &CapsuleResultVariableName ); if (Status == EFI_NOT_FOUND) { UnicodeSPrint (CapsuleResultVariableName, sizeof(CapsuleResultVariableName), L"Capsule%04x", CapsuleResultMaxCounter); CommonSetVariable ( CAPSULE_RESULT_MAX_VARIABLE, &CapsuleResultVariableGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, Size, &CapsuleResultVariableName ); } StrCpyS (CapsuleResultVariableName, sizeof(CapsuleResultVariableName) / sizeof(CHAR16), CAPSULE_RESULT_VARIABLE); Size = sizeof (CHAR16) * 11; Status = CommonGetVariable ( CAPSULE_RESULT_LAST_VARIABLE, &CapsuleResultVariableGuid, &Size, &CapsuleResultVariableName ); if (!EFI_ERROR (Status)) { CapsuleResultCounter = StrHexToUintn (CapsuleResultVariableName + 7); if (CapsuleResultCounter == CapsuleResultMaxCounter) { CapsuleResultCounter = 0; } else { CapsuleResultCounter ++; } UnicodeSPrint (CapsuleResultVariableName, sizeof(CapsuleResultVariableName), L"Capsule%04x", CapsuleResultCounter); } // // Update UEFI capsule result status // CapsuleResult.VariableTotalSize = sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER); CapsuleResult.Reserved = 0; CopyGuid (&CapsuleResult.CapsuleGuid, CapsuleGuid); gRT->GetTime (&CapsuleResult.CapsuleProcessed, NULL); CapsuleResult.CapsuleStatus = CapsuleStatus; CommonSetVariable ( CapsuleResultVariableName, &CapsuleResultVariableGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, CapsuleResult.VariableTotalSize, &CapsuleResult ); CommonSetVariable ( CAPSULE_RESULT_LAST_VARIABLE, &CapsuleResultVariableGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, sizeof (CHAR16) * 11, &CapsuleResultVariableName ); } /** Get the certificate from firmware volume @param NameGuid Pointer to the file GUID of the certificate @param Buffer Returned the address of the certificate @param Size Pointer to the size of the certificate @retval EFI_SUCCESS The certificate was successfully retrieved @retval EFI_NOT_FOUND Failed to find the certificate @retval EFI_LOAD_ERROR Firmware Volume Protocol error **/ STATIC EFI_STATUS GetCertificateData ( IN EFI_GUID *NameGuid, IN OUT VOID **Buffer, IN OUT UINTN *Size ) { EFI_STATUS Status; EFI_HANDLE *HandleBuffer; UINTN HandleCount; UINTN Index; EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; UINT32 AuthenticationStatus; Fv = NULL; AuthenticationStatus = 0; Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiFirmwareVolume2ProtocolGuid, NULL, &HandleCount, &HandleBuffer ); if (EFI_ERROR (Status) || (HandleCount == 0)) { return EFI_NOT_FOUND; } // // Find desired image in all Fvs // for (Index = 0; Index < HandleCount; Index++) { Status = gBS->HandleProtocol ( HandleBuffer[Index], &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Fv ); if (EFI_ERROR (Status)) { return EFI_LOAD_ERROR; } *Buffer = NULL; *Size = 0; Status = Fv->ReadSection ( Fv, NameGuid, EFI_SECTION_RAW, 0, Buffer, Size, &AuthenticationStatus ); if (!EFI_ERROR (Status)) { break; } } if (Index >= HandleCount) { return EFI_NOT_FOUND; } return EFI_SUCCESS; } /** Load the certificate data to "SecureFlashCertData" variable The certificate is used when the Capsule image is loaded via gBS->LoadImage() @param None @retval EFI_SUCCESS Certificate variable was successfully set @retval EFI_NOT_FOUND Certificate data was not found **/ STATIC EFI_STATUS LoadCertToVariable ( VOID ) { EFI_STATUS Status; CERTIFICATE_DATA *CertData; UINTN CertSize; Status = GetCertificateData (PcdGetPtr (PcdSecureFlashCertificateFile), (VOID **)&CertData, &CertSize); if (EFI_ERROR (Status)) { return EFI_NOT_FOUND; } Status = CommonSetVariable ( L"SecureFlashCertData", &gSecureFlashInfoGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, CertSize, CertData ); FreePool (CertData); return Status; } /** Enable/disable security check of Capsule images @param[in] Enabled The switch of security check of Capsule images @retval EFI_SUCCESS Security check of Capsule images is disabled @return others Failed to disable Capsule security check **/ STATIC EFI_STATUS CapsuleSecurityCheck ( IN BOOLEAN Enabled ) { EFI_STATUS Status; UINT8 SetupMode; if (Enabled) { Status = LoadCertToVariable(); if (EFI_ERROR (Status)) { return Status; } // // Set SecureFlashSetupMode variable to trigger image verification process. // SetupMode = USER_MODE; Status = CommonSetVariable ( SECURE_FLASH_SETUP_MODE_NAME, &gSecureFlashInfoGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, sizeof(SetupMode), &SetupMode ); } else { // // Clear ceritificate data variable // Status = CommonSetVariable ( L"SecureFlashCertData", &gSecureFlashInfoGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 0, NULL ); // // Clear SecureFlashSetupMode variable // Status = CommonSetVariable ( SECURE_FLASH_SETUP_MODE_NAME, &gSecureFlashInfoGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 0, NULL ); } return Status; } /** Update the status when is it EFI_SECURITY_VIOLATION @param[in] Status Status for input @return The updated status if input status is EFI_SECURITY_VIOLATION **/ STATIC EFI_STATUS SecurityViolationStatusUpdate ( IN EFI_STATUS Status ) { EFI_IMAGE_EXECUTION_INFO_TABLE *ImageExeInfoTable; EFI_IMAGE_EXECUTION_INFO *ImageExeInfo; UINTN Index; UINTN NumberOfImages; if (Status != EFI_SECURITY_VIOLATION) { return Status; } ImageExeInfoTable = NULL; EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID**)&ImageExeInfoTable); if (ImageExeInfoTable == NULL) { return Status; } NumberOfImages = ImageExeInfoTable->NumberOfImages; ImageExeInfo = (EFI_IMAGE_EXECUTION_INFO*)(ImageExeInfoTable + 1); for (Index = 0; Index < NumberOfImages - 1; Index++) { ImageExeInfo = (EFI_IMAGE_EXECUTION_INFO *) (((UINT8 *) ImageExeInfo) + ImageExeInfo->InfoSize); } if (ImageExeInfo->Action == EFI_IMAGE_EXECUTION_AUTH_UNTESTED) { // // Change the status for the image without signature // Status = EFI_INVALID_PARAMETER; } return Status; } /** Internal function to initialize H2O_BDS_CP_CAPSULE_CHECK_UPDATE_CAPABILITY_DATA data and trigger gH2OBdsCpCapsuleCheckUpdateCapabilityGuid checkpoint. @param[in] Capsule Points to the capsule. @param[in] CapsuleSize Size of the capsule in bytes. @param[in, out] AttemptStatus Points to the capsule update check status. @retval EFI_SUCCESS Trigger gH2OBdsCpCapsuleCheckUpdateCapabilityGuid checkpoint successfully. @return Other Other error occurred while triggering gH2OBdsCpCapsuleCheckUpdateCapabilityGuid checkpoint. **/ STATIC EFI_STATUS TriggerCpCapsuleCheckUpdateCapability ( IN VOID *Capsule, IN UINTN CapsuleSize, IN OUT UINT32 *AttemptStatus ) { EFI_STATUS Status; H2O_BDS_CP_CAPSULE_CHECK_UPDATE_CAPABILITY_DATA CpData; UINT32 AttemptErrorBits; AttemptErrorBits = (*AttemptStatus == ESRT_SUCCESS) ? 0 : CheckCapsuleUpdateCapability ((EFI_CAPSULE_HEADER *) Capsule); CpData.Size = sizeof (H2O_BDS_CP_CAPSULE_CHECK_UPDATE_CAPABILITY_DATA); CpData.Status = H2O_CP_TASK_NORMAL; CpData.Capsule = Capsule; CpData.CapsuleSize = CapsuleSize; CpData.ErrorBits = AttemptErrorBits; DEBUG_CP ((DEBUG_INFO, "Checkpoint Trigger: %g\n", &gH2OBdsCpCapsuleCheckUpdateCapabilityGuid)); Status = H2OCpTrigger (&gH2OBdsCpCapsuleCheckUpdateCapabilityGuid, &CpData); DEBUG_CP ((DEBUG_INFO, "Checkpoint Result: %x\n", CpData.Status)); if (CpData.Status == H2O_CP_TASK_UPDATE) { *AttemptStatus = ConvertCapsuleErrorBitsToStatus (CpData.ErrorBits); } return Status; } /** Populates the EFI_FIRMWARE_IMAGE_DESCRIPTOR structure in the private context structure. @param[in,out] Private Pointer to the private context structure for the Firmware Management Protocol instance. **/ STATIC VOID PopulateDescriptor ( IN OUT CAPSULE_FMP_ENTRY *CapsuleFmpEntry ) { EFI_STATUS Status; EFI_GUID FirmwareClass; UINT32 FirmwareVersion; EFI_FIRMWARE_IMAGE_DESCRIPTOR *Descriptor; UINT32 LastAttemptVersion; UINT32 LastAttemptStatus; UINTN Size; if (CapsuleFmpEntry->DescriptorPopulated) { return; } Descriptor = &CapsuleFmpEntry->Descriptor; CopyGuid (&Descriptor->ImageTypeId, &mCapsuleFmpEntry.ImageTypeGuid); Descriptor->ImageIndex = CapsuleFmpEntry->ImageIndex; Descriptor->ImageId = Descriptor->ImageIndex; Descriptor->ImageIdName = NULL; Descriptor->HardwareInstance = 0; Descriptor->LowestSupportedImageVersion = PcdGet32(PcdH2OEsrtSystemFirmwareLowestSupportedVersion); Descriptor->AttributesSupported = (IMAGE_ATTRIBUTE_IMAGE_UPDATABLE | IMAGE_ATTRIBUTE_RESET_REQUIRED | IMAGE_ATTRIBUTE_IN_USE); Descriptor->AttributesSetting = (IMAGE_ATTRIBUTE_IMAGE_UPDATABLE | IMAGE_ATTRIBUTE_RESET_REQUIRED | IMAGE_ATTRIBUTE_IN_USE); Descriptor->Compatibilities = 0; Descriptor->Size = 0; // // Get the version. // Status = GetEsrtFirmwareInfo (&FirmwareClass, &FirmwareVersion); if (!EFI_ERROR (Status)) { Descriptor->Version = FirmwareVersion; } // // Free the current version name. Shouldn't really happen but this populate // function could be called multiple times (to refresh). // if (Descriptor->VersionName != NULL) { FreePool (Descriptor->VersionName); Descriptor->VersionName = NULL; } Size = BVDT_MAX_STR_SIZE; Descriptor->VersionName = AllocatePool (Size); if (Descriptor->VersionName != NULL) { // // Attempt to get the version string from BvdtLib. // Status = GetBvdtInfo ((BVDT_TYPE) BvdtCcbVer, &Size, Descriptor->VersionName); if (EFI_ERROR (Status)) { CopyMem (Descriptor->VersionName, VERSION_STRING_NOT_AVAILABLE, sizeof (VERSION_STRING_NOT_AVAILABLE)); } } // // Get last attemp version & status. // Size = sizeof(UINT32); Status = CommonGetVariable ( ESRT_LAST_ATTEMPT_VERSION, &Descriptor->ImageTypeId, &Size, &LastAttemptVersion ); if (Status == EFI_SUCCESS) { Descriptor->LastAttemptVersion = LastAttemptVersion; } Size = sizeof(UINT32); Status = CommonGetVariable ( ESRT_LAST_ATTEMPT_STATUS, &Descriptor->ImageTypeId, &Size, &LastAttemptStatus ); if (Status == EFI_SUCCESS) { Descriptor->LastAttemptStatus = LastAttemptStatus; } CapsuleFmpEntry->DescriptorPopulated = TRUE; } /** Returns information about the current firmware image(s) of the device. This function allows a copy of the current firmware image to be created and saved. The saved copy could later been used, for example, in firmware image recovery or rollback. @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. @param[in, out] ImageInfoSize A pointer to the size, in bytes, of the ImageInfo buffer. On input, this is the size of the buffer allocated by the caller. On output, it is the size of the buffer returned by the firmware if the buffer was large enough, or the size of the buffer needed to contain the image(s) information if the buffer was too small. @param[in, out] ImageInfo A pointer to the buffer in which firmware places the current image(s) information. The information is an array of EFI_FIRMWARE_IMAGE_DESCRIPTORs. @param[out] DescriptorVersion A pointer to the location in which firmware returns the version number associated with the EFI_FIRMWARE_IMAGE_DESCRIPTOR. @param[out] DescriptorCount A pointer to the location in which firmware returns the number of descriptors or firmware images within this device. @param[out] DescriptorSize A pointer to the location in which firmware returns the size, in bytes, of an individual EFI_FIRMWARE_IMAGE_DESCRIPTOR. @param[out] PackageVersion A version number that represents all the firmware images in the device. The format is vendor specific and new version must have a greater value than the old version. If PackageVersion is not supported, the value is 0xFFFFFFFF. A value of 0xFFFFFFFE indicates that package version comparison is to be performed using PackageVersionName. A value of 0xFFFFFFFD indicates that package version update is in progress. @param[out] PackageVersionName A pointer to a pointer to a null-terminated string representing the package version name. The buffer is allocated by this function with AllocatePool(), and it is the caller's responsibility to free it with a call to FreePool(). @retval EFI_SUCCESS The device was successfully updated with the new image. @retval EFI_BUFFER_TOO_SMALL The ImageInfo buffer was too small. The current buffer size needed to hold the image(s) information is returned in ImageInfoSize. @retval EFI_INVALID_PARAMETER ImageInfoSize is NULL. @retval EFI_DEVICE_ERROR Valid information could not be returned. Possible corrupted image. **/ EFI_STATUS EFIAPI UefiImageGetImageInfo ( IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, IN OUT UINTN *ImageInfoSize, IN OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageInfo, OUT UINT32 *DescriptorVersion, OUT UINT8 *DescriptorCount, OUT UINTN *DescriptorSize, OUT UINT32 *PackageVersion, OUT CHAR16 **PackageVersionName ) { // // Check for valid pointer // if (ImageInfoSize == NULL) { return EFI_INVALID_PARAMETER; } // // Check the buffer size // NOTE: Check this first so caller can get the necessary memory size it must allocate. // if (*ImageInfoSize < (sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR))) { *ImageInfoSize = sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR); return EFI_BUFFER_TOO_SMALL; } // // Confirm that buffer isn't null // if ( (ImageInfo == NULL) || (DescriptorVersion == NULL) || (DescriptorCount == NULL) || (DescriptorSize == NULL) || (PackageVersion == NULL)) { return EFI_INVALID_PARAMETER; } // // Set the size to whatever we need // *ImageInfoSize = sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR); // // Make sure the descriptor has already been loaded or refreshed // PopulateDescriptor (&mCapsuleFmpEntry); // // Copy the image descriptor // CopyMem (ImageInfo, &mCapsuleFmpEntry.Descriptor, sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR)); *DescriptorVersion = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION; *DescriptorCount = 1; *DescriptorSize = sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR); // // means unsupported // *PackageVersion = 0xFFFFFFFF; // // Do not update PackageVersionName since it is not supported in this instance. // return EFI_SUCCESS; } /** Checks if the firmware image is valid for the device. This function allows firmware update application to validate the firmware image without invoking the SetImage() first. @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. @param[in] ImageIndex A unique number identifying the firmware image(s) within the device. The number is between 1 and DescriptorCount. @param[in] Image Points to the new image. @param[in] ImageSize Size of the new image in bytes. @param[out] ImageUpdatable Indicates if the new image is valid for update. It also provides, if available, additional information if the image is invalid. @retval EFI_SUCCESS The image was successfully checked. @retval EFI_INVALID_PARAMETER The Image was NULL. @retval EFI_UNSUPPORTED The operation is not supported. @retval EFI_SECURITY_VIOLATION The operation could not be performed due to an authentication failure. **/ EFI_STATUS EFIAPI UefiImageCheckImage ( IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, IN UINT8 ImageIndex, IN CONST VOID *Capsule, IN UINTN CapsuleSize, OUT UINT32 *ImageUpdatable ) { EFI_STATUS Status; UINT32 CheckImageResult; EFI_HANDLE CapsuleHandle; UINT32 AttemptVersion; ESRT_STATUS AttemptStatus; // // Make sure the descriptor has already been loaded or refreshed // PopulateDescriptor (&mCapsuleFmpEntry); AttemptVersion = GetCapsuleSystemFirmwareVersion ((EFI_CAPSULE_HEADER *)Capsule); if (AttemptVersion < mCapsuleFmpEntry.Descriptor.LowestSupportedImageVersion) { // // Record the ESRT status for out of resources // CheckImageResult = IMAGE_UPDATABLE_INVALID_OLD; // // Update capsule status onto corresponding variables // AttemptStatus = ESRT_ERROR_INCORRECT_VERSION; UpdateCapsuleStatus (&((EFI_CAPSULE_HEADER *)Capsule)->CapsuleGuid, AttemptVersion, AttemptStatus, EFI_UNSUPPORTED); goto Exit; } if (CapsuleSize > 0x10000000) { // // Record the ESRT status for out of resources // CheckImageResult = IMAGE_UPDATABLE_INVALID; // // Update capsule status onto corresponding variables // AttemptStatus = ESRT_ERROR_INSUFFICIENT_RESOURCES; UpdateCapsuleStatus (&((EFI_CAPSULE_HEADER*)Capsule)->CapsuleGuid, AttemptVersion, AttemptStatus, EFI_OUT_OF_RESOURCES); goto Exit; } // // Try to load image to verify the image format // CapsuleHandle = NULL; CapsuleSecurityCheck (TRUE); Status = gBS->LoadImage ( FALSE, gImageHandle, NULL, (UINT8*)Capsule + ((EFI_CAPSULE_HEADER*)Capsule)->HeaderSize, CapsuleSize - ((EFI_CAPSULE_HEADER*)Capsule)->HeaderSize, (VOID **)&CapsuleHandle ); CapsuleSecurityCheck (FALSE); if (EFI_ERROR (Status)) { // // Record the ESRT status for out of resources // AttemptStatus = ConvertEfiStatusToEsrtStatus (Status); CheckImageResult = IMAGE_UPDATABLE_INVALID; // // Update capsule status onto corresponding variables // UpdateCapsuleStatus (&((EFI_CAPSULE_HEADER*)Capsule)->CapsuleGuid, AttemptVersion, AttemptStatus, SecurityViolationStatusUpdate (Status)); goto Exit; } if (CapsuleHandle) { gBS->UnloadImage (CapsuleHandle); } AttemptStatus = PreInstallationCheck ((EFI_CAPSULE_HEADER*)Capsule); if (FeaturePcdGet (PcdH2OBdsCpCapsuleCheckUpdateCapabilitySupported)) { TriggerCpCapsuleCheckUpdateCapability ((VOID *) Capsule, CapsuleSize, &AttemptStatus); } CheckImageResult = ConvertEsrtStatusToCheckImageResult (AttemptStatus); if (AttemptStatus != ESRT_SUCCESS) { // // Update capsule status onto corresponding variables // UpdateCapsuleStatus (&((EFI_CAPSULE_HEADER*)Capsule)->CapsuleGuid, AttemptVersion, AttemptStatus, ConvertEsrtStatusToEfiStatus (AttemptStatus)); } Exit: if (ImageUpdatable) { *ImageUpdatable = CheckImageResult; } return EFI_SUCCESS; } /** Process UEFI image in capsule image This function updates the hardware with the new firmware image. This function returns EFI_UNSUPPORTED if the firmware image is not updatable. If the firmware image is updatable, the function should perform the following minimal validations before proceeding to do the firmware image update. - Validate the image authentication if image has attribute IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED. The function returns EFI_SECURITY_VIOLATION if the validation fails. - Validate the image is a supported image for this device. The function returns EFI_ABORTED if the image is unsupported. The function can optionally provide more detailed information on why the image is not a supported image. - Validate the data from VendorCode if not null. Image validation must be performed before VendorCode data validation. VendorCode data is ignored or considered invalid if image validation failed. The function returns EFI_ABORTED if the data is invalid. VendorCode enables vendor to implement vendor-specific firmware image update policy. Null if the caller did not specify the policy or use the default policy. As an example, vendor can implement a policy to allow an option to force a firmware image update when the abort reason is due to the new firmware image version is older than the current firmware image version or bad image checksum. Sensitive operations such as those wiping the entire firmware image and render the device to be non-functional should be encoded in the image itself rather than passed with the VendorCode. AbortReason enables vendor to have the option to provide a more detailed description of the abort reason to the caller. @param[in] This A pointer to the EFI_FIRMWARE_MANAGEMENT_PROTOCOL instance. @param[in] ImageIndex A unique number identifying the firmware image(s) within the device. The number is between 1 and DescriptorCount. @param[in] Image Points to the new image. @param[in] ImageSize Size of the new image in bytes. @param[in] VendorCode This enables vendor to implement vendor-specific firmware image update policy. Null indicates the caller did not specify the policy or use the default policy. @param[in] Progress A function used by the driver to report the progress of the firmware update. @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more details for the aborted operation. The buffer is allocated by this function with AllocatePool(), and it is the caller's responsibility to free it with a call to FreePool(). @retval EFI_SUCCESS The device was successfully updated with the new image. @retval EFI_ABORTED The operation is aborted. @retval EFI_INVALID_PARAMETER The Image was NULL. @retval EFI_UNSUPPORTED The operation is not supported. @retval EFI_SECURITY_VIOLATION The operation could not be performed due to an authentication failure. **/ EFI_STATUS EFIAPI UefiImageSetImage ( IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *This, IN UINT8 ImageIndex, IN CONST VOID *Capsule, IN UINTN CapsuleSize, IN CONST VOID *VendorCode, IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress, OUT CHAR16 **AbortReason ) { EFI_STATUS Status; UINT32 CheckImageResult; UINT32 AttemptVersion; ESRT_STATUS AttemptStatus; EFI_HANDLE CapsuleHandle; // // Check Image before actual action to ensure the capsule is qualified // Status = UefiImageCheckImage ( This, ImageIndex, Capsule, CapsuleSize, &CheckImageResult ); if (CheckImageResult != IMAGE_UPDATABLE_VALID) { return EFI_UNSUPPORTED; } CapsuleHandle = NULL; Status = CapsuleSecurityCheck (TRUE); if (!EFI_ERROR (Status)) { Status = gBS->LoadImage ( FALSE, gImageHandle, NULL, (UINT8*)Capsule + ((EFI_CAPSULE_HEADER*)Capsule)->HeaderSize, CapsuleSize - ((EFI_CAPSULE_HEADER*)Capsule)->HeaderSize, (VOID **)&CapsuleHandle ); if (!EFI_ERROR (Status)) { // // Set firmware update flag by flashing the signature onto flash part for seamless recovery // HookSystemResetRoutine(TRUE); SetFirmwareUpdatingFlag (TRUE); // // Start flash process // Status = gBS->StartImage (CapsuleHandle, NULL, NULL); // // Clear firmware update flag by erase the signature in flash part for seamless recovery // SetFirmwareUpdatingFlag (FALSE); HookSystemResetRoutine(FALSE); } } CapsuleSecurityCheck (FALSE); AttemptVersion = GetCapsuleSystemFirmwareVersion ((EFI_CAPSULE_HEADER*)Capsule); AttemptStatus = ConvertEfiStatusToEsrtStatus (Status); // // Update capsule status onto corresponding variables // UpdateCapsuleStatus (&((EFI_CAPSULE_HEADER*)Capsule)->CapsuleGuid, AttemptVersion, AttemptStatus, SecurityViolationStatusUpdate (Status)); // // Reset system after BIOS flash completed if needed // if (CapsuleHandle) { gBS->UnloadImage (CapsuleHandle); } // // Need repopulate after SetImage is called to // update LastAttemptVersion and LastAttemptStatus. // mCapsuleFmpEntry.DescriptorPopulated = FALSE; return Status; } /** Installation of capsule in WindowsUx processor **/ EFI_STATUS InstallCapsuleInUefiImage ( VOID ) { EFI_STATUS Status; EFI_GUID FirmwareClass; UINT32 FirmwareVersion; Status = GetEsrtFirmwareInfo (&FirmwareClass, &FirmwareVersion); ASSERT_EFI_ERROR (Status); // // Capsule processor instance installation // mCapsuleFmp.CapsuleProcessorCount ++; mCapsuleFmpEntry.ImageIndex = (UINT8)mCapsuleFmp.CapsuleProcessorCount; CopyGuid (&mCapsuleFmpEntry.ImageTypeGuid, &FirmwareClass); mCapsuleFmpEntry.SetImage = UefiImageSetImage; mCapsuleFmpEntry.CheckImage = UefiImageCheckImage; mCapsuleFmpEntry.GetImageInfo = UefiImageGetImageInfo; InsertTailList (&mCapsuleFmp.CapsuleProcessorListHead, (LIST_ENTRY*)&mCapsuleFmpEntry); return EFI_SUCCESS; }