mirror of
				https://github.com/acidanthera/audk.git
				synced 2025-11-02 20:44:39 +01:00 
			
		
		
		
	Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Jiewen Yao <jiewen.yao@intel.com>
		
			
				
	
	
		
			425 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			425 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  DXE capsule report related function.
 | 
						|
 | 
						|
  Copyright (c) 2016 - 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 <PiDxe.h>
 | 
						|
#include <Protocol/FirmwareManagement.h>
 | 
						|
#include <Protocol/VariableLock.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 <IndustryStandard/WindowsUxCapsule.h>
 | 
						|
 | 
						|
/**
 | 
						|
  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
 | 
						|
 | 
						|
  @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
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_CAPSULE_RESULT_VARIABLE_HEADER  *CapsuleResultVariableHeader;
 | 
						|
  EFI_CAPSULE_RESULT_VARIABLE_FMP     *CapsuleResultVariableFmp;
 | 
						|
  EFI_STATUS                          Status;
 | 
						|
  UINT8                               *CapsuleResultVariable;
 | 
						|
  UINTN                               CapsuleResultVariableSize;
 | 
						|
  CHAR16                              *DevicePathStr;
 | 
						|
  UINTN                               DevicePathStrSize;
 | 
						|
 | 
						|
  DevicePathStr = NULL;
 | 
						|
  if (FmpDevicePath != NULL) {
 | 
						|
    DevicePathStr = ConvertDevicePathToText (FmpDevicePath, FALSE, FALSE);
 | 
						|
  }
 | 
						|
  if (DevicePathStr != NULL) {
 | 
						|
    DevicePathStrSize = StrSize(DevicePathStr);
 | 
						|
  } else {
 | 
						|
    DevicePathStrSize = sizeof(CHAR16);
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Allocate zero CHAR16 for CapsuleFileName.
 | 
						|
  //
 | 
						|
  CapsuleResultVariableSize = sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER) + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + sizeof(CHAR16) + 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 (DevicePathStr != NULL) {
 | 
						|
    CopyMem ((UINT8 *)CapsuleResultVariableFmp + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + sizeof(CHAR16), 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 related variables.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
InitCapsuleVariable (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  InitCapsuleUpdateVariable();
 | 
						|
  InitCapsuleMaxVariable();
 | 
						|
  InitCapsuleLastVariable();
 | 
						|
  //
 | 
						|
  // No need to clear L"Capsule####", because OS/APP should refer L"CapsuleLast"
 | 
						|
  // to check status and delete them.
 | 
						|
  //
 | 
						|
}
 |