alder_lake_bios/Insyde/InsydeModulePkg/Universal/FirmwareVolume/FvbServicesRuntimeDxe/SmmFunctions.c

453 lines
15 KiB
C

/** @file
Provide support functions for FVB services which can be executed in
SMM mode.
;******************************************************************************
;* 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 <Library/FlashRegionLib.h>
#include "SmmFunctions.h"
#include "CommonFunctions.h"
SMM_FW_VOL_INSTANCE *mSmmFwVolInstance;
SMM_FD_FUNCTIONS mSmmFdFunctions = {FlashRead, FlashProgram, FlashErase};
EFI_GUID mSmmFdFunctionsProtocolGuid = SMM_FD_FUNCTIONS_PROTOCOL_GUID;
/**
Internal function to initialzie module mSmmFwVolInstance
@retval EFI_SUCCESS Initialzie mSmmFwVolInstance successful.
@retval other Allocate memory failed or locate gEfiSmmFwBlockServiceProtocolGuid or
gEfiSmmCpuProtocolGuid failed.
**/
STATIC
EFI_STATUS
InitializeSmmFwVolInstance (
VOID
)
{
EFI_STATUS Status;
EFI_HANDLE Handle;
mSmmFwVolInstance = FvbAllocateZeroBuffer (sizeof (SMM_FW_VOL_INSTANCE), TRUE);
ASSERT (mSmmFwVolInstance != NULL);
Status = mSmst->SmmLocateProtocol (
&gEfiSmmFwBlockServiceProtocolGuid,
NULL,
(VOID **)&mSmmFwVolInstance->SmmFwbService
);
if (EFI_ERROR (Status)) {
return Status;
}
Handle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
&Handle,
&mSmmFdFunctionsProtocolGuid,
&mSmmFdFunctions,
NULL
);
return Status;
}
/**
Check the input memory buffer is whether overlap the SMRAM ranges.
@param[in] Buffer The pointer to the buffer to be checked.
@param[in] BufferSize The size in bytes of the input buffer
@retval TURE The buffer overlaps SMRAM ranges.
@retval FALSE The buffer doesn't overlap SMRAM ranges.
**/
STATIC
BOOLEAN
EFIAPI
BufferOverlapSmram (
IN VOID *Buffer,
IN UINTN BufferSize
)
{
EFI_STATUS Status;
STATIC H2O_IHISI_PROTOCOL *Ihisi = NULL;
if (Ihisi == NULL) {
Status = mSmst->SmmLocateProtocol (
&gH2OIhisiProtocolGuid,
NULL,
(VOID **) &Ihisi
);
if (EFI_ERROR (Status)) {
return FALSE;
}
}
if (Ihisi->BufferOverlapSmram((VOID *) Buffer, BufferSize)) {
return TRUE;
}
return FALSE;
}
/**
Communication service SMI Handler entry.
This SMI handler provides services for the firmware volume access through SMI.
@param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
@param[in] RegisterContext Points to an optional handler context which was specified when the
handler was registered.
@param[in, out] CommBuffer A pointer to a collection of data in memory that will
be conveyed from a non-SMM environment into an SMM environment.
@param[in, out] CommBufferSize The size of the CommBuffer.
@retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
should still be called.
@retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
still be called.
@retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
be called.
@retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
**/
EFI_STATUS
EFIAPI
SmmFvbAccessHandler (
IN EFI_HANDLE DispatchHandle,
IN CONST VOID *Context OPTIONAL,
IN OUT VOID *CommBuffer OPTIONAL,
IN OUT UINTN *CommBufferSize OPTIONAL
)
{
SMM_FVB_BUFFER *SmmFvbBuffer;
UINT8 *DataBuffer;
EFI_STATUS Status;
EFI_SMM_FW_BLOCK_SERVICE_PROTOCOL *SmmFwbService;
if (CommBuffer == NULL || CommBufferSize == NULL ) {
return EFI_SUCCESS;
}
//
// Check buffer size, address, not overlap SMMRAM and the signature to make sure the communication buffer is correct.
//
if (*CommBufferSize != mSmmCommunicationBufferSize - sizeof (EFI_SMM_COMMUNICATE_HEADER) + sizeof (UINT8) ||
(UINTN)CommBuffer != (UINTN)mSmmPhyCommunicationBuffer + sizeof (EFI_SMM_COMMUNICATE_HEADER) - sizeof (UINT8) ||
BufferOverlapSmram (CommBuffer, *CommBufferSize) ||
((SMM_FVB_BUFFER *)CommBuffer)->Signature != SMM_FVB_ACCESS_SIGNATURE) {
return EFI_SUCCESS;
}
//
// Copy SMM_FVB_BUFFER to SMRAM to prevent from TOCTOU attack.
//
SmmFvbBuffer = AllocateCopyPool (sizeof (SMM_FVB_BUFFER), CommBuffer);
if (SmmFvbBuffer == NULL) {
return EFI_SUCCESS;
}
DataBuffer = (UINT8 *)((SMM_FVB_BUFFER *)CommBuffer + 1);
//
// Make sure DataSize isn't larger than buffer size.
//
if (SmmFvbBuffer->DataSize > *CommBufferSize - sizeof (SMM_FVB_BUFFER)) {
return EFI_SUCCESS;
}
Status = EFI_UNSUPPORTED;
SmmFwbService = mSmmFwVolInstance->SmmFwbService;
switch (SmmFvbBuffer->AccessType) {
case PfatReadFvb:
Status = SmmFwbService->Read (
SmmFwbService,
SmmFvbBuffer->AccessAddress,
0,
&SmmFvbBuffer->DataSize,
DataBuffer
);
break;
case PfatWriteFvb:
Status = SmmFwbService->Write (
SmmFwbService,
SmmFvbBuffer->AccessAddress,
&SmmFvbBuffer->DataSize,
DataBuffer
);
break;
case PfatEraseFvb:
Status = SmmFwbService->EraseBlocks (
SmmFwbService,
SmmFvbBuffer->AccessAddress,
&SmmFvbBuffer->DataSize
);
break;
default:
ASSERT (FALSE);
break;
}
SmmFvbBuffer->Status = Status;
CopyMem (CommBuffer, SmmFvbBuffer, sizeof (SMM_FVB_BUFFER));
FreePool (SmmFvbBuffer);
return EFI_SUCCESS;
}
/**
Register software SMI callback function to provide interface to access FVB through SMI.
@retval EFI_SUCCESS Register callback function successful.
@retval Other Locate gEfiSmmSwDispatch2ProtocolGuid or register callback function failed.
**/
STATIC
EFI_STATUS
RegisterSmmAccessCallback (
VOID
)
{
EFI_HANDLE Handle;
Handle = NULL;
return mSmst->SmiHandlerRegister (SmmFvbAccessHandler, &gFvbAccessThroughSmiGuid, &Handle);
}
/**
Initailize SMM FVB access service.
1.Initialize moudle SmmFwVolInstance.
2.Register SMM callback function to provide SMM FVB access interface.
@retval EFI_SUCCESS Initialize SMM FVB access service.
@retval Other Any error occurred while initializing SMM FVB service.
**/
EFI_STATUS
InitializeSmmFvbAccess (
VOID
)
{
EFI_STATUS Status;
Status = InitializeSmmFwVolInstance ();
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
return Status;
}
Status = RegisterSmmAccessCallback ();
ASSERT_EFI_ERROR (Status);
return Status;
}
/**
Send the data in communicate buffer to SMM.
@retval EFI_SUCCESS Success is returned from the functin in SMM.
@return Others Failure is returned from the function in SMM.
**/
EFI_STATUS
SendCommunicateBuffer (
VOID
)
{
EFI_STATUS Status;
UINTN CommSize;
EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
SMM_FVB_BUFFER *SmmFvbBuffer;
//
// Note!!! In runtime, the memory address is virtual address in protected mode but the memory address is
// physical address in SMM mode. Therefore, if we want to access FV through SMI in runtime, we must set data
// to virtual address in protected mode and then use physical address to call SMI.
//
CommSize = mSmmCommunicationBufferSize;
Status = mSmmCommunication->Communicate (mSmmCommunication, mSmmPhyCommunicationBuffer, &CommSize);
ASSERT_EFI_ERROR (Status);
SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *)mSmmCommunicationBuffer;
SmmFvbBuffer = (SMM_FVB_BUFFER *)SmmCommunicateHeader->Data;
return SmmFvbBuffer->Status;
}
/**
According to access to send SMI to do FVB access.
@param[in] AccessType Input access command type. system support three tyeps of command -
PfatReadFvb, PfatWriteFvb and PfatEraseFvb.
@param[in] AccessAddress target address to access.
@param[in,out] AccessLength In: input size in bytes to access.
Out: only update this update length if AccessType is PfatReadFvb and
read data successful.
@param[in,out] AccessBuffer In: only use this buffer as input buffer to write flash if AccessType
is PfatWriteFvb.
Out: only use this buffer as output buffer to read flash if AccessType
is PfatReadFvb.
@retval EFI_SUCCESS Data successfully read from flash device.
@retval EFI_UNSUPPORTED The flash device is not supported.
@retval EFI_DEVICE_ERROR Failed to read the blocks.
**/
STATIC
EFI_STATUS
SendSmiByAccessType (
IN UINTN AccessType,
IN UINTN AccessAddress,
IN OUT UINTN *AccessLength,
IN OUT VOID *AccessBuffer
)
{
UINT8 *WorkingBuf;
EFI_STATUS Status;
EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
SMM_FVB_BUFFER *SmmFvbBuffer;
ZeroMem (mSmmCommunicationBuffer, mSmmCommunicationBufferSize);
SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *)mSmmCommunicationBuffer;
CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gFvbAccessThroughSmiGuid);
SmmCommunicateHeader->MessageLength = mSmmCommunicationBufferSize;
SmmFvbBuffer = (SMM_FVB_BUFFER *)SmmCommunicateHeader->Data;
SmmFvbBuffer->Status = EFI_UNSUPPORTED;
SmmFvbBuffer->Signature = SMM_FVB_ACCESS_SIGNATURE;
SmmFvbBuffer->AccessAddress = AccessAddress;
SmmFvbBuffer->AccessType = AccessType;
SmmFvbBuffer->DataSize = *AccessLength;
//
// copy write data to write buffer before sending SMI
//
WorkingBuf = (UINT8 *)(SmmFvbBuffer + 1);
if (AccessType == PfatWriteFvb) {
CopyMem (WorkingBuf, AccessBuffer, *AccessLength);
}
Status = SendCommunicateBuffer ();
//
// Update access length after executing SMI function
//
*AccessLength = SmmFvbBuffer->DataSize;
if (!EFI_ERROR (Status) && AccessType == PfatReadFvb) {
CopyMem (AccessBuffer, WorkingBuf, *AccessLength);
}
return Status;
}
/**
Function to do flash read through SMI.
@param[in] ReadAddress Target address to be read.
@param[in, out] ReadLength In: Input buffer size in bytes.
Out: Total read data size in bytes.
@param[out] ReadBuffer Output buffer to contains read data.
@retval EFI_SUCCESS Data successfully read from flash device.
@retval EFI_UNSUPPORTED The flash device is not supported.
@retval EFI_DEVICE_ERROR Failed to read the blocks.
**/
EFI_STATUS
ReadFdThroughSmi (
IN UINTN ReadAddress,
IN OUT UINTN *ReadLength,
OUT VOID *ReadBuffer
)
{
UINTN RemaindingSize;
UINTN ReadSize;
UINTN TotalReadSize;
UINT8 *CurrentBuffer;
EFI_STATUS Status;
RemaindingSize = *ReadLength;
TotalReadSize = 0;
CurrentBuffer = (UINT8 *) ReadBuffer;
do {
if (RemaindingSize > (UINTN) FdmGetNAtSize (&gH2OFlashMapRegionFtwBackupGuid , 1)) {
ReadSize = (UINTN) FdmGetNAtSize (&gH2OFlashMapRegionFtwBackupGuid , 1);
} else {
ReadSize = RemaindingSize;
}
RemaindingSize -= ReadSize;
Status = SendSmiByAccessType (PfatReadFvb, ReadAddress + TotalReadSize, &ReadSize, (VOID *) CurrentBuffer);
CurrentBuffer += ReadSize;
TotalReadSize += ReadSize;;
} while (RemaindingSize != 0 && !EFI_ERROR (Status));
if (!EFI_ERROR (Status)) {
*ReadLength = TotalReadSize;
}
return Status;
}
/**
Function to do flash write through SMI.
@param[in] WriteAddress Target address to write.
@param[in, out] WriteLength In: Input buffer size in bytes.
Out: Total write data size in bytes.
@param[out] WriteBuffer input buffer to write.
@retval EFI_SUCCESS Data successfully write to flash device.
@retval EFI_UNSUPPORTED The flash device is not supported.
@retval EFI_DEVICE_ERROR Failed to write the blocks.
**/
EFI_STATUS
WriteFdThroughSmi (
IN UINTN WriteAddress,
IN OUT UINTN *WriteLength,
IN VOID *WriteBuffer
)
{
UINTN RemaindingSize;
UINTN WriteSize;
UINTN TotalWriteSize;
UINT8 *CurrentBuffer;
EFI_STATUS Status;
RemaindingSize = *WriteLength;
TotalWriteSize = 0;
CurrentBuffer = (UINT8 *) WriteBuffer;
do {
if (RemaindingSize > (UINTN) FdmGetNAtSize (&gH2OFlashMapRegionFtwBackupGuid , 1)) {
WriteSize = (UINTN) FdmGetNAtSize (&gH2OFlashMapRegionFtwBackupGuid , 1);
} else {
WriteSize = RemaindingSize;
}
RemaindingSize -= WriteSize;
Status = SendSmiByAccessType (PfatWriteFvb, WriteAddress + TotalWriteSize, &WriteSize, (VOID *) CurrentBuffer);
CurrentBuffer += WriteSize;
TotalWriteSize += WriteSize;;
} while (RemaindingSize != 0 && !EFI_ERROR (Status));
if (!EFI_ERROR (Status)) {
*WriteLength = TotalWriteSize;
}
return Status;
}
/**
Function to do flash erase through SMI.
@param[in] EraseAddress Target address to erase.
@param[in] EraseLength Erase size in bytes.
@retval EFI_SUCCESS Erase flash block successful.
@retval EFI_UNSUPPORTED The flash device is not supported.
@retval EFI_DEVICE_ERROR Failed to erase blocks.
**/
EFI_STATUS
EraseFdThroughSmi (
IN UINTN EraseAddress,
IN UINTN EraseLength
)
{
return SendSmiByAccessType (PfatEraseFvb, EraseAddress, &EraseLength, NULL);
}