alder_lake_bios/Insyde/InsydeModulePkg/Universal/SmmResourceCheckDxe/SmmResourceCheckDxe.c

392 lines
12 KiB
C

/** @file
Driver checks SMM resource in ready to boot callback function.
This is a COMBINED_SMM_DXE type driver and DXE part uses SMM communication
;******************************************************************************
;* Copyright (c) 2018 - 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 "SmmResourceCheckDxe.h"
EFI_GUID mSmmResourceCommunicationGuid = {0x4b52e4da, 0x60eb, 0x4ec2, 0xa8, 0xc, 0xf9, 0xfd, 0xa, 0xb8, 0x5e, 0x97};
EFI_SMM_COMMUNICATE_HEADER *mSmmMemoryInfo;
UINTN mSmramRangeCount;
EFI_SMRAM_DESCRIPTOR *mSmramRanges;
VOID *mSmmAddressBuffer[MAX_ALLOCATE_COUNT];
/**
Get SMM memory information. It includes total SMM memory size and remaining SMM
memory size.
@param[out] MemorySize Total SMM memory size in bytes.
@param[out] RemainingSize Remaiing SMM memory size in bytes.
@retval EFI_SUCCESS Get SMM memory information successfully.
@retval EFI_INVALID_PARAMETER MemorySize or RemainingSize is NULL.
@return Other Other error in this function.
*/
EFI_STATUS
EFIAPI
GetSmmMemoryInfo (
OUT UINTN *MemorySize,
OUT UINTN *RemainingSize
)
{
EFI_STATUS Status;
EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateBufHeader;
SMM_MEMORY_INFO *SmmMemoryInfo;
EFI_SMM_COMMUNICATION_PROTOCOL *SmmCommunication;
UINTN CommSize;
Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &SmmCommunication);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Initial Communication buffer
//
SmmCommunicateBufHeader = mSmmMemoryInfo;
CopyGuid (&SmmCommunicateBufHeader->HeaderGuid, &mSmmResourceCommunicationGuid);
SmmCommunicateBufHeader->MessageLength = (SMM_RESOURCE_COMM_BUFF_SIZE - OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data));
SmmMemoryInfo = (SMM_MEMORY_INFO *) SmmCommunicateBufHeader->Data;
SmmMemoryInfo->ResourceHeader.Function = GET_SMM_MEMORY_INFO;
SmmMemoryInfo->ResourceHeader.ReturnStatus = EFI_UNSUPPORTED;
CommSize = SMM_RESOURCE_COMM_BUFF_SIZE;
Status = SmmCommunication->Communicate (SmmCommunication, mSmmMemoryInfo, &CommSize);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (SmmMemoryInfo->ResourceHeader.ReturnStatus)) {
return SmmMemoryInfo->ResourceHeader.ReturnStatus;
}
*MemorySize = SmmMemoryInfo->MemoryInfo.TotalSmmMemorySize;
*RemainingSize = SmmMemoryInfo->MemoryInfo.RemainingSmmMemorySize;
return EFI_SUCCESS;
}
/**
Function to get remaining SMM memory size in bytes.
@return The remaining SMM memory size in bytes.
**/
STATIC
UINTN
GetRemainingSmmMemorySize (
VOID
)
{
UINTN RemainingSmmMemorySize;
UINTN AllocateSize;
UINTN AllocateCount;
UINTN Index;
//
// Try to find the largest free SMM memory by AllocatePool function first.
//
RemainingSmmMemorySize = 0;
AllocateSize = EFI_PAGE_SIZE;
while (TRUE) {
mSmmAddressBuffer[0] = AllocatePool (AllocateSize);
if (mSmmAddressBuffer[0] != NULL) {
FreePool (mSmmAddressBuffer[0]);
AllocateSize *= 2;
} else {
break;
}
}
if (AllocateSize == EFI_PAGE_SIZE) {
return RemainingSmmMemorySize;
}
AllocateSize /= 2;
mSmmAddressBuffer[0] = AllocatePool (AllocateSize);
if (mSmmAddressBuffer[0] == NULL) {
return RemainingSmmMemorySize;
}
//
// Using AllocatePool function to calculate free SMM memory size.
//
RemainingSmmMemorySize += AllocateSize;
AllocateSize /= 2;
for (Index = 1; Index < MAX_ALLOCATE_COUNT; Index++) {
mSmmAddressBuffer[Index] = AllocatePool (AllocateSize);
while (mSmmAddressBuffer[Index] == NULL) {
if (AllocateSize <= EFI_PAGE_SIZE) {
break;
}
AllocateSize /= 2;
mSmmAddressBuffer[Index] = AllocatePool (AllocateSize);
}
if (mSmmAddressBuffer[Index] != NULL) {
RemainingSmmMemorySize += AllocateSize;
} else {
break;
}
}
AllocateCount = Index;
//
// Free all of the allocated SMM memory in this function
//
for (Index = 0; Index < AllocateCount; Index++) {
FreePool (mSmmAddressBuffer[Index]);
}
return RemainingSmmMemorySize;
}
/**
Function to get total SMM memory size in bytes.
@return The total SMM memory size in bytes.
**/
STATIC
UINTN
GetTotalSmmMemorySize (
VOID
)
{
UINTN TotalSmmMemorySize;
UINTN Index;
TotalSmmMemorySize = 0;
for (Index = 0; Index < mSmramRangeCount; Index ++) {
TotalSmmMemorySize += (UINTN) mSmramRanges[Index].PhysicalSize;
}
return TotalSmmMemorySize;
}
/**
Function to update SMM memory information. it includes total SMM memory size and remaining SMM
memory size
@param[out] SmmMemoryInfo Pointer to SMM_MEMORY_INFO instance.
**/
STATIC
VOID
UpdateSmmMemoryInfo (
OUT SMM_MEMORY_INFO *SmmMemoryInfo
)
{
SmmMemoryInfo->MemoryInfo.TotalSmmMemorySize = GetTotalSmmMemorySize ();
SmmMemoryInfo->MemoryInfo.RemainingSmmMemorySize = GetRemainingSmmMemorySize ();
SmmMemoryInfo->ResourceHeader.ReturnStatus = EFI_SUCCESS;
}
/**
This function is SMM Communication call back for get SMM resource information.
@param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
@param[in] Context 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.
**/
EFI_STATUS
EFIAPI
SmmGetSmmResourceInfoHandler (
IN EFI_HANDLE DispatchHandle,
IN CONST VOID *RegisterContext,
IN OUT VOID *CommBuffer,
IN OUT UINTN *CommBufferSize
)
{
SMM_MEMORY_INFO *SmmMemoryInfo;
if (CommBuffer == NULL || CommBufferSize == NULL) {
return EFI_SUCCESS;
}
//
// Check buffer size, address, must be the memory which is allocated in DXE part entry point to make sure
// the memory will not overlap SMM RAM.
//
if (*CommBufferSize != SMM_RESOURCE_COMM_BUFF_SIZE - SMM_COMMUNICATE_HEADER_SIZE ||
(UINTN)CommBuffer != (UINTN)mSmmMemoryInfo + SMM_COMMUNICATE_HEADER_SIZE) {
return EFI_SUCCESS;
}
//
// Copy SMM_MEMORY_INFO to SMRAM to prevent from TOCTOU attack.
//
SmmMemoryInfo = AllocateCopyPool (sizeof (SMM_MEMORY_INFO), CommBuffer);
if (SmmMemoryInfo == NULL) {
return EFI_SUCCESS;
}
switch (SmmMemoryInfo->ResourceHeader.Function) {
case GET_SMM_MEMORY_INFO:
UpdateSmmMemoryInfo (SmmMemoryInfo);
break;
default:
break;
}
//
// Restore data from SMRAM to parameter communication buffer.
//
CopyMem (CommBuffer, SmmMemoryInfo, sizeof (SMM_MEMORY_INFO));
FreePool (SmmMemoryInfo);
return EFI_SUCCESS;
}
/**
Check SMM resource is wether under threshold.
@param[in] Event Event whose notification function is being invoked.
@param[in] Context Pointer to the notification function's context.
**/
VOID
EFIAPI
SmmResourceOnReadyToBoot (
IN EFI_EVENT Event,
IN VOID *Context
)
{
UINTN MemorySize;
UINTN RemainingSize;
EFI_STATUS Status;
UINTN MemoryThreshold;
H2O_DIALOG_PROTOCOL *H2ODialog;
CHAR16 *String;
EFI_INPUT_KEY Key;
gBS->CloseEvent (Event);
if (!PcdGetBool (PcdH2OSmmMemorySizeCheckSupported)) {
return;
}
RemainingSize = 0;
MemorySize = 0;
Status = GetSmmMemoryInfo (&MemorySize, &RemainingSize);
if (EFI_ERROR (Status)) {
return;
}
MemoryThreshold = PcdGet32 (PcdH2OSmmMemorySizeThreshold);
if (RemainingSize >= MemoryThreshold) {
return;
}
//
// Display warning message to indicate remaing SMM memory size is too small.
//
Status = gBS->LocateProtocol (
&gH2ODialogProtocolGuid,
NULL,
(VOID **) &H2ODialog
);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
return;
}
String = CatSPrint (NULL, L"Remaining SMM memory size is too small!!!\n Remaining Size: 0x%08x\n Threshold Size: 0x%08x\n Total Size: 0x%08x\n", RemainingSize, MemoryThreshold, MemorySize);
H2ODialog->ConfirmDialog (
DlgOk,
FALSE,
0,
NULL,
&Key,
String
);
FreePool (String);
}
/**
The entry of SmmResourceCheckDxe driver.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS FVB service successfully initialized.
**/
EFI_STATUS
EFIAPI
SmmResourceCheckEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_HANDLE DispatchHandle;
UINTN Size;
EFI_SMM_ACCESS2_PROTOCOL *SmmAccess;
EFI_EVENT Event;
EFI_HANDLE Handle;
if (InSmm ()) {
//
// Locate SMM Access2 Protocol
//
Status = gBS->LocateProtocol (
&gEfiSmmAccess2ProtocolGuid,
NULL,
(VOID **)&SmmAccess
);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Get SMRAM range information
//
Size = 0;
Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL);
ASSERT (Status == EFI_BUFFER_TOO_SMALL);
mSmramRanges = AllocatePool (Size);
ASSERT (mSmramRanges != NULL);
if (mSmramRanges == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmramRanges);
ASSERT_EFI_ERROR (Status);
mSmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
Status = gBS->LocateProtocol (
&gEfiCallerIdGuid,
NULL,
(VOID **) &mSmmMemoryInfo
);
Status = gSmst->SmiHandlerRegister (
SmmGetSmmResourceInfoHandler,
&mSmmResourceCommunicationGuid,
&DispatchHandle
);
} else {
Status = EFI_SUCCESS;
mSmmMemoryInfo = AllocateRuntimePool (SMM_RESOURCE_COMM_BUFF_SIZE);
if (mSmmMemoryInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Handle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
&Handle,
&gEfiCallerIdGuid,
mSmmMemoryInfo,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
EfiCreateEventReadyToBootEx (
TPL_CALLBACK,
SmmResourceOnReadyToBoot,
NULL,
&Event
);
}
return Status;
}