| /** @file | |
| DXE capsule report related function. | |
| Copyright (c) 2016 - 2021, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <PiDxe.h> | |
| #include <Protocol/FirmwareManagement.h> | |
| #include <Guid/CapsuleReport.h> | |
| #include <Guid/FmpCapsule.h> | |
| #include <Guid/CapsuleVendor.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Library/UefiRuntimeServicesTableLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/UefiLib.h> | |
| #include <Library/PcdLib.h> | |
| #include <Library/HobLib.h> | |
| #include <Library/PrintLib.h> | |
| #include <Library/ReportStatusCodeLib.h> | |
| #include <Library/DevicePathLib.h> | |
| #include <Library/CapsuleLib.h> | |
| #include <Library/VariablePolicyHelperLib.h> | |
| #include <IndustryStandard/WindowsUxCapsule.h> | |
| /** | |
| This routine is called to clear CapsuleOnDisk Relocation Info variable. | |
| Total Capsule On Disk length is recorded in this variable | |
| @retval EFI_SUCCESS Capsule On Disk flags are cleared | |
| **/ | |
| EFI_STATUS | |
| CoDClearCapsuleRelocationInfo ( | |
| VOID | |
| ); | |
| /** | |
| Get current capsule last variable index. | |
| @return Current capsule last variable index. | |
| @retval -1 No current capsule last variable. | |
| **/ | |
| INTN | |
| GetCurrentCapsuleLastIndex ( | |
| VOID | |
| ) | |
| { | |
| UINTN Size; | |
| CHAR16 CapsuleLastStr[sizeof ("Capsule####")]; | |
| EFI_STATUS Status; | |
| UINT16 CurrentIndex; | |
| Size = sizeof (L"Capsule####") - sizeof (CHAR16); // no zero terminator | |
| Status = gRT->GetVariable ( | |
| L"CapsuleLast", | |
| &gEfiCapsuleReportGuid, | |
| NULL, | |
| &Size, | |
| CapsuleLastStr | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return -1; | |
| } | |
| CurrentIndex = (UINT16)StrHexToUintn (&CapsuleLastStr[sizeof ("Capsule") - 1]); | |
| return CurrentIndex; | |
| } | |
| /** | |
| Get a new capsule status variable index. | |
| @return A new capsule status variable index. | |
| @retval 0 No new capsule status variable index. Rolling over. | |
| **/ | |
| INTN | |
| GetNewCapsuleResultIndex ( | |
| VOID | |
| ) | |
| { | |
| INTN CurrentIndex; | |
| CurrentIndex = GetCurrentCapsuleLastIndex (); | |
| if (CurrentIndex >= PcdGet16 (PcdCapsuleMax)) { | |
| DEBUG ((DEBUG_INFO, " CapsuleResult variable Rolling Over!\n")); | |
| return 0; | |
| } | |
| return CurrentIndex + 1; | |
| } | |
| /** | |
| Lock Variable by variable policy. | |
| @param[in] VariableGuid The Guid of the variable to be locked | |
| @param[in] VariableName The name of the variable to be locked | |
| @param[in] VariablePolicy The pointer of variable lock policy | |
| **/ | |
| VOID | |
| LockVariable ( | |
| IN CONST EFI_GUID VariableGuid, | |
| IN CHAR16 *VariableName, | |
| IN EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // Set the policies to protect the target variables | |
| Status = RegisterBasicVariablePolicy ( | |
| VariablePolicy, | |
| &VariableGuid, | |
| VariableName, | |
| VARIABLE_POLICY_NO_MIN_SIZE, | |
| VARIABLE_POLICY_NO_MAX_SIZE, | |
| VARIABLE_POLICY_NO_MUST_ATTR, | |
| VARIABLE_POLICY_NO_CANT_ATTR, | |
| VARIABLE_POLICY_TYPE_LOCK_NOW | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "DxeCapsuleLibFmp: Failed to lock variable %g %s. Status = %r\n", | |
| &VariableGuid, | |
| VariableName, | |
| Status | |
| )); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| } | |
| /** | |
| Write a new capsule status variable. | |
| @param[in] CapsuleResult The capsule status variable | |
| @param[in] CapsuleResultSize The size of the capsule stauts variable in bytes | |
| @retval EFI_SUCCESS The capsule status variable is recorded. | |
| @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable. | |
| **/ | |
| EFI_STATUS | |
| WriteNewCapsuleResultVariable ( | |
| IN VOID *CapsuleResult, | |
| IN UINTN CapsuleResultSize | |
| ) | |
| { | |
| INTN CapsuleResultIndex; | |
| CHAR16 CapsuleResultStr[sizeof ("Capsule####")]; | |
| UINTN Size; | |
| EFI_STATUS Status; | |
| CapsuleResultIndex = GetNewCapsuleResultIndex (); | |
| DEBUG ((DEBUG_INFO, "New CapsuleResultIndex - 0x%x\n", CapsuleResultIndex)); | |
| UnicodeSPrint ( | |
| CapsuleResultStr, | |
| sizeof (CapsuleResultStr), | |
| L"Capsule%04x", | |
| CapsuleResultIndex | |
| ); | |
| Status = gRT->SetVariable ( | |
| CapsuleResultStr, | |
| &gEfiCapsuleReportGuid, | |
| EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, | |
| CapsuleResultSize, | |
| CapsuleResult | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| Size = sizeof (L"Capsule####") - sizeof (CHAR16); // no zero terminator | |
| DEBUG ((DEBUG_INFO, "Set CapsuleLast - %s\n", CapsuleResultStr)); | |
| Status = gRT->SetVariable ( | |
| L"CapsuleLast", | |
| &gEfiCapsuleReportGuid, | |
| EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, | |
| Size, | |
| CapsuleResultStr | |
| ); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Record capsule status variable and to local cache. | |
| @param[in] CapsuleHeader The capsule image header | |
| @param[in] CapsuleStatus The capsule process stauts | |
| @retval EFI_SUCCESS The capsule status variable is recorded. | |
| @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable. | |
| **/ | |
| EFI_STATUS | |
| RecordCapsuleStatusVariable ( | |
| IN EFI_CAPSULE_HEADER *CapsuleHeader, | |
| IN EFI_STATUS CapsuleStatus | |
| ) | |
| { | |
| EFI_CAPSULE_RESULT_VARIABLE_HEADER CapsuleResultVariable; | |
| EFI_STATUS Status; | |
| CapsuleResultVariable.VariableTotalSize = sizeof (CapsuleResultVariable); | |
| CapsuleResultVariable.Reserved = 0; | |
| CopyGuid (&CapsuleResultVariable.CapsuleGuid, &CapsuleHeader->CapsuleGuid); | |
| ZeroMem (&CapsuleResultVariable.CapsuleProcessed, sizeof (CapsuleResultVariable.CapsuleProcessed)); | |
| gRT->GetTime (&CapsuleResultVariable.CapsuleProcessed, NULL); | |
| CapsuleResultVariable.CapsuleStatus = CapsuleStatus; | |
| Status = EFI_SUCCESS; | |
| if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) { | |
| Status = WriteNewCapsuleResultVariable (&CapsuleResultVariable, sizeof (CapsuleResultVariable)); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Record FMP capsule status variable and to local cache. | |
| @param[in] CapsuleHeader The capsule image header | |
| @param[in] CapsuleStatus The capsule process stauts | |
| @param[in] PayloadIndex FMP payload index | |
| @param[in] ImageHeader FMP image header | |
| @param[in] FmpDevicePath DevicePath associated with the FMP producer | |
| @param[in] CapFileName Capsule file name | |
| @retval EFI_SUCCESS The capsule status variable is recorded. | |
| @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable. | |
| **/ | |
| EFI_STATUS | |
| RecordFmpCapsuleStatusVariable ( | |
| IN EFI_CAPSULE_HEADER *CapsuleHeader, | |
| IN EFI_STATUS CapsuleStatus, | |
| IN UINTN PayloadIndex, | |
| IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader, | |
| IN EFI_DEVICE_PATH_PROTOCOL *FmpDevicePath OPTIONAL, | |
| IN CHAR16 *CapFileName OPTIONAL | |
| ) | |
| { | |
| EFI_CAPSULE_RESULT_VARIABLE_HEADER *CapsuleResultVariableHeader; | |
| EFI_CAPSULE_RESULT_VARIABLE_FMP *CapsuleResultVariableFmp; | |
| EFI_STATUS Status; | |
| UINT8 *CapsuleResultVariable; | |
| UINTN CapsuleResultVariableSize; | |
| CHAR16 *DevicePathStr; | |
| UINTN DevicePathStrSize; | |
| UINTN CapFileNameSize; | |
| DevicePathStr = NULL; | |
| CapFileNameSize = sizeof (CHAR16); | |
| if (FmpDevicePath != NULL) { | |
| DevicePathStr = ConvertDevicePathToText (FmpDevicePath, FALSE, FALSE); | |
| } | |
| if (DevicePathStr != NULL) { | |
| DevicePathStrSize = StrSize (DevicePathStr); | |
| } else { | |
| DevicePathStrSize = sizeof (CHAR16); | |
| } | |
| if (CapFileName != NULL) { | |
| CapFileNameSize = StrSize (CapFileName); | |
| } | |
| // | |
| // Allocate room for CapsuleFileName. | |
| // | |
| CapsuleResultVariableSize = sizeof (EFI_CAPSULE_RESULT_VARIABLE_HEADER) + sizeof (EFI_CAPSULE_RESULT_VARIABLE_FMP) + CapFileNameSize + DevicePathStrSize; | |
| CapsuleResultVariable = AllocateZeroPool (CapsuleResultVariableSize); | |
| if (CapsuleResultVariable == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| CapsuleResultVariableHeader = (VOID *)CapsuleResultVariable; | |
| CapsuleResultVariableHeader->VariableTotalSize = (UINT32)CapsuleResultVariableSize; | |
| CapsuleResultVariableHeader->Reserved = 0; | |
| CopyGuid (&CapsuleResultVariableHeader->CapsuleGuid, &CapsuleHeader->CapsuleGuid); | |
| ZeroMem (&CapsuleResultVariableHeader->CapsuleProcessed, sizeof (CapsuleResultVariableHeader->CapsuleProcessed)); | |
| gRT->GetTime (&CapsuleResultVariableHeader->CapsuleProcessed, NULL); | |
| CapsuleResultVariableHeader->CapsuleStatus = CapsuleStatus; | |
| CapsuleResultVariableFmp = (VOID *)(CapsuleResultVariable + sizeof (EFI_CAPSULE_RESULT_VARIABLE_HEADER)); | |
| CapsuleResultVariableFmp->Version = 0x1; | |
| CapsuleResultVariableFmp->PayloadIndex = (UINT8)PayloadIndex; | |
| CapsuleResultVariableFmp->UpdateImageIndex = ImageHeader->UpdateImageIndex; | |
| CopyGuid (&CapsuleResultVariableFmp->UpdateImageTypeId, &ImageHeader->UpdateImageTypeId); | |
| if (CapFileName != NULL) { | |
| CopyMem ((UINT8 *)CapsuleResultVariableFmp + sizeof (EFI_CAPSULE_RESULT_VARIABLE_FMP), CapFileName, CapFileNameSize); | |
| } | |
| if (DevicePathStr != NULL) { | |
| CopyMem ((UINT8 *)CapsuleResultVariableFmp + sizeof (EFI_CAPSULE_RESULT_VARIABLE_FMP) + CapFileNameSize, DevicePathStr, DevicePathStrSize); | |
| FreePool (DevicePathStr); | |
| DevicePathStr = NULL; | |
| } | |
| Status = EFI_SUCCESS; | |
| if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) { | |
| Status = WriteNewCapsuleResultVariable (CapsuleResultVariable, CapsuleResultVariableSize); | |
| } | |
| FreePool (CapsuleResultVariable); | |
| return Status; | |
| } | |
| /** | |
| Initialize CapsuleMax variables. | |
| @param[in] VariablePolicy The pointer of variable lock policy | |
| **/ | |
| VOID | |
| InitCapsuleMaxVariable ( | |
| EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN Size; | |
| CHAR16 CapsuleMaxStr[sizeof ("Capsule####")]; | |
| UnicodeSPrint ( | |
| CapsuleMaxStr, | |
| sizeof (CapsuleMaxStr), | |
| L"Capsule%04x", | |
| PcdGet16 (PcdCapsuleMax) | |
| ); | |
| Size = sizeof (L"Capsule####") - sizeof (CHAR16); // no zero terminator | |
| Status = gRT->SetVariable ( | |
| L"CapsuleMax", | |
| &gEfiCapsuleReportGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, | |
| Size, | |
| CapsuleMaxStr | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| // Lock it per UEFI spec. | |
| LockVariable (gEfiCapsuleReportGuid, L"CapsuleMax", VariablePolicy); | |
| } | |
| } | |
| /** | |
| Initialize CapsuleLast variables. | |
| @param[in] VariablePolicy The pointer of variable lock policy | |
| **/ | |
| VOID | |
| InitCapsuleLastVariable ( | |
| EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_BOOT_MODE BootMode; | |
| VOID *CapsuleResult; | |
| UINTN Size; | |
| CHAR16 CapsuleLastStr[sizeof ("Capsule####")]; | |
| BootMode = GetBootModeHob (); | |
| if (BootMode == BOOT_ON_FLASH_UPDATE) { | |
| Status = gRT->SetVariable ( | |
| L"CapsuleLast", | |
| &gEfiCapsuleReportGuid, | |
| EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, | |
| 0, | |
| NULL | |
| ); | |
| // Do not lock it because it will be updated later. | |
| } else { | |
| // | |
| // Check if OS/APP cleared L"Capsule####" | |
| // | |
| ZeroMem (CapsuleLastStr, sizeof (CapsuleLastStr)); | |
| Size = sizeof (L"Capsule####") - sizeof (CHAR16); // no zero terminator | |
| Status = gRT->GetVariable ( | |
| L"CapsuleLast", | |
| &gEfiCapsuleReportGuid, | |
| NULL, | |
| &Size, | |
| CapsuleLastStr | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // L"CapsuleLast" is got, check if data is there. | |
| // | |
| Status = GetVariable2 ( | |
| CapsuleLastStr, | |
| &gEfiCapsuleReportGuid, | |
| (VOID **)&CapsuleResult, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // If no data, delete L"CapsuleLast" | |
| // | |
| Status = gRT->SetVariable ( | |
| L"CapsuleLast", | |
| &gEfiCapsuleReportGuid, | |
| EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, | |
| 0, | |
| NULL | |
| ); | |
| } else { | |
| if (CapsuleResult != NULL) { | |
| FreePool (CapsuleResult); | |
| } | |
| } | |
| } | |
| // Lock it in normal boot path per UEFI spec. | |
| LockVariable (gEfiCapsuleReportGuid, L"CapsuleLast", VariablePolicy); | |
| } | |
| } | |
| /** | |
| Initialize capsule update variables. | |
| **/ | |
| VOID | |
| InitCapsuleUpdateVariable ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN Index; | |
| CHAR16 CapsuleVarName[30]; | |
| CHAR16 *TempVarName; | |
| // | |
| // Clear all the capsule variables CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2... | |
| // as early as possible which will avoid the next time boot after the capsule update | |
| // will still into the capsule loop | |
| // | |
| StrCpyS (CapsuleVarName, sizeof (CapsuleVarName)/sizeof (CapsuleVarName[0]), EFI_CAPSULE_VARIABLE_NAME); | |
| TempVarName = CapsuleVarName + StrLen (CapsuleVarName); | |
| Index = 0; | |
| while (TRUE) { | |
| if (Index > 0) { | |
| UnicodeValueToStringS ( | |
| TempVarName, | |
| sizeof (CapsuleVarName) - ((UINTN)TempVarName - (UINTN)CapsuleVarName), | |
| 0, | |
| Index, | |
| 0 | |
| ); | |
| } | |
| Status = gRT->SetVariable ( | |
| CapsuleVarName, | |
| &gEfiCapsuleVendorGuid, | |
| EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, | |
| 0, | |
| (VOID *)NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // There is no capsule variables, quit | |
| // | |
| break; | |
| } | |
| Index++; | |
| } | |
| } | |
| /** | |
| Initialize capsule relocation info variable. | |
| @param[in] VariablePolicy The pointer of variable lock policy | |
| **/ | |
| VOID | |
| InitCapsuleRelocationInfo ( | |
| EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy | |
| ) | |
| { | |
| CoDClearCapsuleRelocationInfo (); | |
| // | |
| // Unlock Capsule On Disk relocation Info variable only when Capsule On Disk flag is enabled | |
| // | |
| if (!CoDCheckCapsuleOnDiskFlag ()) { | |
| LockVariable (gEfiCapsuleVendorGuid, COD_RELOCATION_INFO_VAR_NAME, VariablePolicy); | |
| } | |
| } | |
| /** | |
| Initialize capsule related variables. | |
| **/ | |
| VOID | |
| InitCapsuleVariable ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy; | |
| // Locate the VariablePolicy protocol | |
| Status = gBS->LocateProtocol (&gEdkiiVariablePolicyProtocolGuid, NULL, (VOID **)&VariablePolicy); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "DxeCapsuleReportLib %a - Could not locate VariablePolicy protocol! %r\n", __func__, Status)); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| InitCapsuleUpdateVariable (); | |
| InitCapsuleMaxVariable (VariablePolicy); | |
| InitCapsuleLastVariable (VariablePolicy); | |
| InitCapsuleRelocationInfo (VariablePolicy); | |
| // | |
| // No need to clear L"Capsule####", because OS/APP should refer L"CapsuleLast" | |
| // to check status and delete them. | |
| // | |
| } |