alder_lake_bios/Insyde/InsydeModulePkg/Universal/IhisiServicesSmm/IhisiSecureFlash.c

493 lines
18 KiB
C

/** @file
SecureFlashLib Library Instance implementation
;******************************************************************************
;* Copyright (c) 2014 - 2021, 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 "SecureFlash.h"
#include "IhisiRegistration.h"
#include <IndustryStandard/PeImage.h>
#include <Library/VariableLib.h>
#include <Protocol/TianoCompress.h>
#include <Library/CacheMaintenanceLib.h>
#include <Library/KernelConfigLib.h>
BOOLEAN mInPOST = TRUE;
BOOLEAN mEspSupportCapability = TRUE;
typedef struct _FLASH_IMAGE_INFO {
UINT32 TotalImageSize;
UINT32 RemainingImageSize;
BOOLEAN Compressed;
COMPRESS_TABLE_HEADER CompressTableHeader;
} FLASH_IMAGE_INFO;
FLASH_IMAGE_INFO mFlashImageInfo = {0};
/**
Verify if the image is signed.
@param[in] ImageBase The address of the image.
@retval EFI_SUCCESS if the image is not signed
@retval EFI_ACCESS_DENIED if the image is signed
**/
EFI_STATUS
VerifySignedImage (
IN UINT8 *ImageBase
)
{
EFI_IMAGE_DOS_HEADER *DosHeader;
UINT32 PeCoffHeaderOffset;
UINT16 Magic;
EFI_IMAGE_DATA_DIRECTORY *SectionDataDir;
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION PeHeader;
DosHeader = (EFI_IMAGE_DOS_HEADER *)(UINTN)(ImageBase);
if (DosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
//
// DOS image header is present,
// so read the PE header after the DOS image header.
//
PeCoffHeaderOffset = DosHeader->e_lfanew;
} else {
PeCoffHeaderOffset = 0;
}
PeHeader.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) (ImageBase + PeCoffHeaderOffset);
if (PeHeader.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
//
// It is not a valid Pe/Coff file.
//
return EFI_ACCESS_DENIED;
}
Magic = PeHeader.Pe32->OptionalHeader.Magic;
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset.
//
SectionDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &PeHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
} else {
//
// Use PE32+ offset.
//
SectionDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &PeHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
}
if (SectionDataDir->Size == 0) {
//
// This image is not signed.
//
return EFI_ACCESS_DENIED;
}
return EFI_SUCCESS;
}
/**
Gather all block images and merge them into TargetImageAddress.
@param[in] TargetImageAddress The address of the complete image.
@retval EFI_SUCCESS Function executes successful.
@retval EFI_INVALID_PARAMETER The image size is incorrect.
**/
EFI_STATUS
MergeImageBlockWithoutCompress (
IN EFI_PHYSICAL_ADDRESS TargetImageAddress
)
{
UINTN Index;
UINT64 TotalImageSize;
UINT32 NumberOfImageBlk;
UINT8 *Destination;
IMAGE_BLOCK_DATA_ITEM_STRUCTURE *ImageBlock;
FBTS_SECURE_FLASH_IMAGE_BLOCK_STRUCTURE *ImageBlkPtr;
TotalImageSize = mFlashImageInfo.TotalImageSize - mFlashImageInfo.RemainingImageSize;
ImageBlkPtr = (FBTS_SECURE_FLASH_IMAGE_BLOCK_STRUCTURE*)(UINTN) IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RSI);
//
// The first DWORD is number of blocks, the second DWORD is reserved.
//
NumberOfImageBlk = ImageBlkPtr->BlockNum;
ImageBlock = ImageBlkPtr->BlockDataItem;
if (!IhisiProtBufferInCmdBuffer ((VOID *) ImageBlkPtr, NumberOfImageBlk)) {
return IHISI_BUFFER_RANGE_ERROR;
}
Destination = (UINT8 *) (UINTN) (TargetImageAddress + TotalImageSize);
for (Index = 0; Index < NumberOfImageBlk; Index++) {
if (!FeaturePcdGet(PcdH2OIhisiCmdBufferSupported) && ImageBlock->ImageBlockSize > UTILITY_ALLOCATE_BLOCK_SIZE) {
//
// The max block size need co-operate with utility
//
return EFI_INVALID_PARAMETER;
}
CopyMem ((VOID *) Destination, (UINT8 *)(UINTN) ImageBlock->ImageBlockAddress, (UINTN) ImageBlock->ImageBlockSize);
mFlashImageInfo.RemainingImageSize -= (UINT32) ImageBlock->ImageBlockSize;
TotalImageSize += ImageBlock->ImageBlockSize;
Destination += ImageBlock->ImageBlockSize;
ImageBlock++;
}
if (TotalImageSize <= (UINTN) IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RCX)) {
return EFI_SUCCESS;
} else {
return EFI_INVALID_PARAMETER;
}
}
/**
Gather all block images and merge them into reserved memory.
Compress the image data Before copying memory.
@param[in,out] ImageInfo The information of the image.
@retval EFI_SUCCESS Function executes successful.
@retval EFI_BUFFER_TOO_SMALL The reserved buffer is not enough.
@retval EFI_INVALID_PARAMETER The image size is incorrect.
@retval EFI_PROTOCOL_ERROR Cannot locate TianoCompress Protocol.
**/
EFI_STATUS
MergeImageBlock (
IN OUT IMAGE_INFO *ImageInfo
)
{
UINTN Index;
UINTN Index2;
EFI_STATUS Status;
UINT32 RemainSize;
UINT32 NumberOfImageBlk;
UINT32 MaxNumOfSplitBlock;
COMPRESS_DATA_HEADER CompressDataHeader;
COMPRESS_TABLE_HEADER *CompressTableHeader;
UINT8 *Source;
IMAGE_BLOCK_DATA_ITEM_STRUCTURE *ImageBlock;
FBTS_SECURE_FLASH_IMAGE_BLOCK_STRUCTURE *ImageBlkPtr;
TIANO_COMPRESS_PROTOCOL *TianoCompress;
UINT8 *TargetImagePtr;
TianoCompress = NULL;
Status = gSmst->SmmLocateProtocol (
&gTianoCompressProtocolGuid,
NULL,
(VOID **)&TianoCompress
);
if (EFI_ERROR (Status)) {
return EFI_PROTOCOL_ERROR;
}
Index2 = 0;
Source = NULL;
MaxNumOfSplitBlock = 0;
CompressTableHeader = &mFlashImageInfo.CompressTableHeader;
if (mFlashImageInfo.RemainingImageSize == mFlashImageInfo.TotalImageSize) {
//
// Initialize the information of table header.
//
CompressTableHeader->NumOfBlock = 0;
CompressTableHeader->TotalImageSize = 0;
CompressTableHeader->TotalCompressedDataSize = sizeof (COMPRESS_TABLE_HEADER);
}
TargetImagePtr = (UINT8*)(UINTN) ImageInfo->ImageAddress + CompressTableHeader->TotalCompressedDataSize;
//
// The first DWORD is number of blocks, the second DWORD is reserved.
//
ImageBlkPtr = (FBTS_SECURE_FLASH_IMAGE_BLOCK_STRUCTURE*)(UINTN) IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RSI);
NumberOfImageBlk = ImageBlkPtr->BlockNum;
ImageBlock = ImageBlkPtr->BlockDataItem;
if (!IhisiProtBufferInCmdBuffer ((VOID *) ImageBlkPtr, NumberOfImageBlk)) {
return IHISI_BUFFER_RANGE_ERROR;
}
for (Index = 0; Index < NumberOfImageBlk; Index++) {
Source = (UINT8*)(UINTN) ImageBlock->ImageBlockAddress;
if (!IhisiProtBufferInCmdBuffer ((VOID *) Source, (UINTN) ImageBlock->ImageBlockSize)) {
return IHISI_BUFFER_RANGE_ERROR;
}
RemainSize = (UINT32)(UINTN) ImageBlock->ImageBlockSize;
mFlashImageInfo.RemainingImageSize -= RemainSize;
//
// Calculate the max block number of size of IMAGE_BLOCK_BUFFERSIZE_FOR_COMPRESS.(at least one)
//
if ((RemainSize % IMAGE_BLOCK_BUFFERSIZE_FOR_COMPRESS) == 0) {
MaxNumOfSplitBlock = RemainSize / IMAGE_BLOCK_BUFFERSIZE_FOR_COMPRESS;
} else {
MaxNumOfSplitBlock = (RemainSize / IMAGE_BLOCK_BUFFERSIZE_FOR_COMPRESS) + 1;
}
for (Index2 = 0; Index2 < MaxNumOfSplitBlock; Index2++) {
if (((RemainSize % IMAGE_BLOCK_BUFFERSIZE_FOR_COMPRESS) == 0) || (RemainSize >= IMAGE_BLOCK_BUFFERSIZE_FOR_COMPRESS)) {
CompressDataHeader.SourceSize = IMAGE_BLOCK_BUFFERSIZE_FOR_COMPRESS;
} else {
CompressDataHeader.SourceSize = RemainSize;
}
CompressDataHeader.CompressedSize = CompressDataHeader.SourceSize * 2;
//
// We should not use the memory over reserved image size.
//
if (CompressTableHeader->TotalCompressedDataSize + CompressDataHeader.CompressedSize > ImageInfo->ImageSize) {
return EFI_BUFFER_TOO_SMALL;
}
Status = TianoCompress->Compress (
Source,
CompressDataHeader.SourceSize,
TargetImagePtr + sizeof (COMPRESS_DATA_HEADER),
&CompressDataHeader.CompressedSize
);
if (Status == EFI_BUFFER_TOO_SMALL) {
//
// We should not use the memory over reserved image size.
//
if (CompressTableHeader->TotalCompressedDataSize + CompressDataHeader.CompressedSize > ImageInfo->ImageSize) {
return EFI_BUFFER_TOO_SMALL;
}
Status = TianoCompress->Compress (
Source,
CompressDataHeader.SourceSize,
TargetImagePtr + sizeof (COMPRESS_DATA_HEADER),
&CompressDataHeader.CompressedSize
);
}
CopyMem (TargetImagePtr, &CompressDataHeader, sizeof (COMPRESS_DATA_HEADER));
CompressTableHeader->TotalImageSize += CompressDataHeader.SourceSize;
CompressTableHeader->TotalCompressedDataSize += CompressDataHeader.CompressedSize + sizeof (COMPRESS_DATA_HEADER);
TargetImagePtr += CompressDataHeader.CompressedSize + sizeof (COMPRESS_DATA_HEADER);
Source += CompressDataHeader.SourceSize;
RemainSize -= CompressDataHeader.SourceSize;
}
ImageBlock++;
CompressTableHeader->NumOfBlock += MaxNumOfSplitBlock;
}
if (mFlashImageInfo.RemainingImageSize == 0) {
ImageInfo->ImageSize = CompressTableHeader->TotalCompressedDataSize;
//
// Write information to the table header.
//
TargetImagePtr = (UINT8*)(UINTN) ImageInfo->ImageAddress;
CopyMem (TargetImagePtr, CompressTableHeader, sizeof (COMPRESS_TABLE_HEADER));
}
if (CompressTableHeader->TotalImageSize <= (UINTN) IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RCX)) {
return EFI_SUCCESS;
} else {
return EFI_INVALID_PARAMETER;
}
}
/**
OEM capsule secure flash.(AH=48h)
@retval EFI_SUCCESS Function succeeded.
@return Others Error occurred in this function.
**/
EFI_STATUS
SecureFlashFunction (
VOID
)
{
UINTN Size;
EFI_STATUS Status;
IMAGE_INFO ImageInfo;
UINT32 TotalImageSize;
UINT8 *ImageBase;
IMAGE_BLOCK_DATA_ITEM_STRUCTURE *ImageBlock;
FBTS_SECURE_FLASH_IMAGE_BLOCK_STRUCTURE *ImageBlkPtr;
FBTS_EXTEND_FLAG_STRUCTURE *ExtendFlagPtr;
if (!PcdGetBool (PcdSecureFlashSupported)) {
return IHISI_UNSUPPORTED_FUNCTION;
}
if (IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RCX) == SECURE_FLASH_LEGACY_SIGN) {
mEspSupportCapability = FALSE;
return EFI_SUCCESS;
}
//
// mInPOST is a flag used for disabling IHISI write function at Runtime.
// There is a SMI triggerd by SecureFlashDxe to modify this flag.
//
if (IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RCX) == SECURE_FLASH_SIGNATURE) {
mInPOST = FALSE;
return EFI_SUCCESS;
}
//
// The image size cannot be zero.
//
TotalImageSize = IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RCX);
//
// EDI offset8 Bit0: 0 = Update secure capsule via IHISI. (Default)
// 1 = Update secure capsule via ESP (EFI System Partition)
//
ExtendFlagPtr = (FBTS_EXTEND_FLAG_STRUCTURE *)(UINTN)IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RDI);
if (TotalImageSize == 0) {
if (IhisiProtBufferInCmdBuffer (ExtendFlagPtr, sizeof (FBTS_EXTEND_FLAG_STRUCTURE)) &&
(ExtendFlagPtr->Signature == EXTEND_FLAG_INPUT_SIGNATURE) &&
(ExtendFlagPtr->Flag.Bits.SecureCapsuleOrigin == FROME_ESP) &&
(mEspSupportCapability == TRUE)) {
//
// Report ESP support capability to IHISI tool.
//
ExtendFlagPtr->Signature = EXTEND_FLAG_OUTPUT_SIGNATURE;
ExtendFlagPtr->StructureSize = sizeof(FBTS_EXTEND_FLAG_STRUCTURE);
IhisiProtWriteCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RDI, (UINT32)(UINTN)ExtendFlagPtr);
return IHISI_SUCCESS;
}
return IHISI_ACCESS_PROHIBITED;
}
//
// Get reserved memory address to save the image we need.
//
Size = sizeof (IMAGE_INFO);
Status = CommonGetVariable (
L"SecureFlashInfo",
&gSecureFlashInfoGuid,
&Size,
&ImageInfo
);
if (EFI_ERROR (Status)) {
return IHISI_UNSUPPORTED_FUNCTION;
}
if (IhisiProtBufferInCmdBuffer ((VOID *) ExtendFlagPtr, sizeof (FBTS_EXTEND_FLAG_STRUCTURE)) &&
(ExtendFlagPtr->Signature == EXTEND_FLAG_INPUT_SIGNATURE) &&
(ExtendFlagPtr->Flag.Bits.SecureCapsuleOrigin == FROME_ESP)) {
//
// The image is saved in EFI Partition.
// Only save ImageSize.
//
Status = EFI_SUCCESS;
ImageInfo.ImageSize = TotalImageSize;
ImageInfo.Compressed = FALSE;
mFlashImageInfo.RemainingImageSize = 0;
} else {
//
// Get the address of the signed image(from ESI) and verify if it is a signed image.
// The first DWORD is number of blocks, the second DWORD is reserved, skip them.
// We only need the first block to verify if it is a signed image.
//
ImageBlkPtr = (FBTS_SECURE_FLASH_IMAGE_BLOCK_STRUCTURE*)(UINTN) IhisiProtReadCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RSI);
ImageBlock = ImageBlkPtr->BlockDataItem;
ImageBase = (UINT8*)(UINTN)(ImageBlock->ImageBlockAddress);
if (!IhisiProtBufferInCmdBuffer ((VOID *) ImageBase, (UINTN)(ImageBlock->ImageBlockSize))) {
mFlashImageInfo.RemainingImageSize = 0;
return IHISI_BUFFER_RANGE_ERROR;
}
Status = EFI_SUCCESS;
if (mFlashImageInfo.RemainingImageSize == 0) {
mFlashImageInfo.TotalImageSize = TotalImageSize;
mFlashImageInfo.RemainingImageSize = TotalImageSize;
Status = VerifySignedImage (ImageBase);
}
if (EFI_ERROR (Status)){
ZeroMem (&mFlashImageInfo, sizeof (mFlashImageInfo));
return IHISI_UNSUPPORTED_FUNCTION;
}
//
// The image is saved in reserved memroy.
// If the image size is smaller than the size of reserved memroy, no need to be compressed.
//
if (TotalImageSize <= ImageInfo.ImageSize) {
Status = MergeImageBlockWithoutCompress (ImageInfo.ImageAddress);
if (mFlashImageInfo.RemainingImageSize == 0) {
ImageInfo.ImageSize = TotalImageSize;
ImageInfo.Compressed = FALSE;
}
} else {
mFlashImageInfo.Compressed = TRUE;
Status = MergeImageBlock (&ImageInfo);
if (mFlashImageInfo.RemainingImageSize == 0) {
ImageInfo.Compressed = TRUE;
}
}
WriteBackInvalidateDataCache ();
if (EFI_ERROR (Status)) {
ZeroMem (&mFlashImageInfo, sizeof (mFlashImageInfo));
return IHISI_OB_LEN_TOO_SMALL;
}
}
if (!EFI_ERROR (Status) && mFlashImageInfo.RemainingImageSize == 0) {
//
// Set this variable to inform BIOS of doing secure flash.
//
ImageInfo.FlashMode = TRUE;
Status = CommonSetVariable (
L"SecureFlashInfo",
&gSecureFlashInfoGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
sizeof (IMAGE_INFO),
&ImageInfo
);
//
// return BIOS support secure flash type
// 0: winflash should do S3
// 1: winflash should do reboot
// 2: winflash should do shutdown
// 3: winflash should do nothing
//
IhisiProtWriteCpuReg32 (EFI_SMM_SAVE_STATE_REGISTER_RCX, WINFLASH_DO_REBOOT);
ZeroMem (&mFlashImageInfo, sizeof (mFlashImageInfo));
}
return EFI_SUCCESS;
}
STATIC
IHISI_REGISTER_TABLE
SECURE_FLASH_REGISTER_TABLE[] = {
//
// AH=48h
//
{ FBTSOEMCapsuleSecureFlash, "S48Kn_CpSecureFlash", SecureFlashFunction}
};
EFI_STATUS
InstallSecureFlashServices (
VOID
)
{
EFI_STATUS Status;
IHISI_REGISTER_TABLE *SubFuncTable;
UINT16 TableCount;
SubFuncTable = SECURE_FLASH_REGISTER_TABLE;
TableCount = sizeof(SECURE_FLASH_REGISTER_TABLE)/sizeof(SECURE_FLASH_REGISTER_TABLE[0]);
Status = RegisterIhisiSubFunction (SubFuncTable, TableCount);
if (EFI_ERROR(Status)) {
ASSERT_EFI_ERROR (Status);
}
return EFI_SUCCESS;
}