/** @file DXE capsule report related function. Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** 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; } /** 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. **/ VOID InitCapsuleMaxVariable ( VOID ) { EFI_STATUS Status; UINTN Size; CHAR16 CapsuleMaxStr[sizeof("Capsule####")]; EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock; 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. Status = gBS->LocateProtocol(&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock); if (!EFI_ERROR(Status)) { Status = VariableLock->RequestToLock(VariableLock, L"CapsuleMax", &gEfiCapsuleReportGuid); ASSERT_EFI_ERROR(Status); } } } /** Initialize CapsuleLast variables. **/ VOID InitCapsuleLastVariable ( VOID ) { EFI_STATUS Status; EFI_BOOT_MODE BootMode; EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock; 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. Status = gBS->LocateProtocol(&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock); if (!EFI_ERROR(Status)) { Status = VariableLock->RequestToLock(VariableLock, L"CapsuleLast", &gEfiCapsuleReportGuid); ASSERT_EFI_ERROR(Status); } } } /** 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. **/ VOID InitCapsuleRelocationInfo ( VOID ) { EFI_STATUS Status; EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock; CoDClearCapsuleRelocationInfo(); // // Unlock Capsule On Disk relocation Info variable only when Capsule On Disk flag is enabled // if (!CoDCheckCapsuleOnDiskFlag()) { Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock); if (!EFI_ERROR(Status)) { Status = VariableLock->RequestToLock (VariableLock, COD_RELOCATION_INFO_VAR_NAME, &gEfiCapsuleVendorGuid); ASSERT_EFI_ERROR(Status); } } } /** Initialize capsule related variables. **/ VOID InitCapsuleVariable ( VOID ) { InitCapsuleUpdateVariable(); InitCapsuleMaxVariable(); InitCapsuleLastVariable(); InitCapsuleRelocationInfo(); // // No need to clear L"Capsule####", because OS/APP should refer L"CapsuleLast" // to check status and delete them. // }