alder_lake_bios/Intel/AlderLake/ClientOneSiliconPkg/Cpu/Library/SmmCpuFeaturesLib/SmmSpa.c

456 lines
14 KiB
C

/** @file
SMM Policy Analyzer (SPA) support functions
@copyright
INTEL CONFIDENTIAL
Copyright 2020 Intel Corporation.
The source code contained or described herein and all documents related to the
source code ("Material") are owned by Intel Corporation or its suppliers or
licensors. Title to the Material remains with Intel Corporation or its suppliers
and licensors. The Material may contain trade secrets and proprietary and
confidential information of Intel Corporation and its suppliers and licensors,
and is protected by worldwide copyright and trade secret laws and treaty
provisions. No part of the Material may be used, copied, reproduced, modified,
published, uploaded, posted, transmitted, distributed, or disclosed in any way
without Intel's prior express written permission.
No license under any patent, copyright, trade secret or other intellectual
property right is granted to or conferred upon you by disclosure or delivery
of the Materials, either expressly, by implication, inducement, estoppel or
otherwise. Any license under such intellectual property rights must be
express and approved by Intel in writing.
Unless otherwise agreed by Intel in writing, you may not remove or alter
this notice or any other notice embedded in Materials by Intel or
Intel's suppliers or licensors in any way.
This file contains an 'Intel Peripheral Driver' and is uniquely identified as
"Intel Reference Module" and is licensed for Intel CPUs and chipsets under
the terms of your license agreement with Intel or your vendor. This file may
be modified by the user, subject to additional terms of the license agreement.
@par Specification Reference:
**/
#if FixedPcdGetBool (PcdSpaEnable) == 1
#include "SmmCpuFeatures.h"
#include <Protocol/SmmSpaLogOutputProtocol.h>
#include <Uefi/UefiBaseType.h>
#include "SmmSpa.h"
UINTN mSpaLogOutputProtocolBase [MAX_PROTOCOL_ENTRIES];
UINT8 mNoHandles;
SPA_CTXT *gSpaCtxt = NULL;
EFI_HANDLE gSpaSmmReadyToLockRegistration;
/**
SPA Log output callback
@param[in] LogString The log string output.
**/
VOID
EFIAPI
AsmSpaLogOutputCb (
IN CHAR16 *LogString
);
/**
SPA Log output callback
@param[in] LogString The log string output.
**/
VOID
EFIAPI
SpaLogOutputCb (
IN CHAR16 *LogString
)
{
EFI_STATUS Status;
EFI_SPA_LOG_OUTPUT_PROTOCOL *SpaLogOutputProtocol = NULL;
UINT8 Index;
if (LogString == NULL) {
DEBUG ((DEBUG_INFO, "SpaLogOutputCb Invalid parameter.\n"));
return;
}
// Invoking Protocol interface as per given entries.
for (Index = 0; Index < mNoHandles; Index++) {
SpaLogOutputProtocol = (EFI_SPA_LOG_OUTPUT_PROTOCOL *) mSpaLogOutputProtocolBase [Index];
if (SpaLogOutputProtocol != NULL) {
Status = SpaLogOutputProtocol->SpaLogOutput (
SpaLogOutputProtocol,
LogString
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "SpaLogOutput protocol entry failed with return Status %s.\n", Status));
}
}
}
return ;
}
/**
Get SPA Log output protocol entries which are installed.
@retval EFI_SUCCESS The function completed successfully
@retval EFI_NOT_FOUND Failed to locate Spa log output protocol
@retval EFI_OUT_OF_RESOURCES Insufficient resources to create buffer
**/
EFI_STATUS
SpaGetLogProtocolEntries (
VOID
)
{
EFI_STATUS Status;
UINTN HandleBufferSize;
EFI_HANDLE *HandleBuffer = NULL;
UINT8 Index;
EFI_SPA_LOG_OUTPUT_PROTOCOL *SpaLogOutputProtocol = NULL;
HandleBufferSize = 0;
mNoHandles = 0;
ZeroMem (mSpaLogOutputProtocolBase, sizeof (mSpaLogOutputProtocolBase));
Status = gSmst->SmmLocateHandle (
ByProtocol,
&gEfiSpaLogOutputProtocolGuid,
NULL,
&HandleBufferSize,
HandleBuffer
);
if (Status != EFI_BUFFER_TOO_SMALL) {
return EFI_NOT_FOUND;
}
HandleBuffer = AllocateZeroPool (HandleBufferSize);
if (HandleBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = gSmst->SmmLocateHandle (
ByProtocol,
&gEfiSpaLogOutputProtocolGuid,
NULL,
&HandleBufferSize,
HandleBuffer
);
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
mNoHandles = (UINT8) HandleBufferSize / sizeof (EFI_HANDLE);
if (mNoHandles > MAX_PROTOCOL_ENTRIES) {
mNoHandles = MAX_PROTOCOL_ENTRIES;
DEBUG ((DEBUG_INFO, "mNoHandles are higher than Max handles, hence limited to Max handles.\n"));
}
//
// Get Protocol Entries. One of protocol instance is installed in Intel platform package.
// OEM can install their owned protocol to get log string.
//
for (Index = 0; Index < mNoHandles; Index++) {
Status = gSmst->SmmHandleProtocol (
HandleBuffer [Index],
&gEfiSpaLogOutputProtocolGuid,
(VOID **)&SpaLogOutputProtocol
);
if (EFI_ERROR (Status)) {
continue;
}
mSpaLogOutputProtocolBase [Index] = (UINTN) SpaLogOutputProtocol;
}
return EFI_SUCCESS;
}
/**
Add Images into the Image structure database.
@param[in] ImageBase Address of real image Base.
@param[in] ImageSize Loaded image size.
@param[in] LoadedImageBase Address of loaded image base.
@param[in] EntryPoint Image entry point.
@param[in] NameString Image name string pointer.
@param[in] Guid Image Guid.
@param[in] ImageStruct Pointer to Image structure database.
**/
VOID
AddImageStruct (
IN UINTN ImageBase,
IN UINTN ImageSize,
IN UINTN LoadedImageBase,
IN UINTN EntryPoint,
IN CHAR16 *NameString,
IN EFI_GUID *Guid,
IN IMAGE_STRUCT *ImageStruct
)
{
ImageStruct->ImageBase = ImageBase;
DEBUG ((DEBUG_INFO, "ImageBase = %x\n", ImageBase));
ImageStruct->ImageSize = ImageSize;
DEBUG ((DEBUG_INFO, "ImageSize = %x\n", ImageSize));
ImageStruct->LoadedImageBase = LoadedImageBase;
DEBUG ((DEBUG_INFO, "LoadedImageBase = %x\n", LoadedImageBase));
ImageStruct->EntryPoint = EntryPoint;
DEBUG ((DEBUG_INFO, "EntryPoint = %x\n", EntryPoint));
if (NameString != NULL) {
DEBUG ((DEBUG_INFO, "NameString = %s\n", NameString));
StrnCpyS (ImageStruct->NameString, NAME_STRING_LENGTH + 1, NameString, NAME_STRING_LENGTH);
}
CopyGuid (&ImageStruct->FileGuid, Guid);
DEBUG ((DEBUG_INFO, "Guid = %x\n", Guid));
}
/**
Get loaded SMM driver Name details.
The following methods will be tried orderly:
1. Extracting from FFS UI section using Image Guid.
2. Get image name as File name.
3. Get image name as File path name.
@param[in] LoadedImage LoadedImage protocol.
@param[out] Guid Guid of the FFS.
@param[out] CHAR16 Name string pointer.
@retval EFI_SUCCESS Unicode name string is stored in the mNameString.
@retval EFI_NOT_FOUND Device path or file name not found.
@retval EFI_INVALID_PARAMETER Input parameters are not valid
**/
EFI_STATUS
GetDriverNameString (
IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage,
OUT EFI_GUID *Guid,
OUT CHAR16 *mNameString
)
{
EFI_STATUS Status;
CHAR16 *NameString;
UINTN StringSize;
EFI_GUID *FileName = NULL;
if ((LoadedImage == NULL) || (Guid == NULL) || (mNameString == NULL)) {
return EFI_INVALID_PARAMETER;
} else {
ZeroMem (Guid, sizeof (EFI_GUID));
ZeroMem (mNameString, NAME_STRING_LENGTH+1);
}
if (DevicePathType (LoadedImage->FilePath) != MEDIA_DEVICE_PATH) {
return EFI_NOT_FOUND;
}
if (DevicePathSubType (LoadedImage->FilePath) == MEDIA_PIWG_FW_FILE_DP) {
FileName = &((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)LoadedImage->FilePath)->FvFileName;
if (FileName == NULL) {
return EFI_NOT_FOUND;
}
CopyGuid (Guid, FileName);
//
// Try to get the image's FFS UI section by image GUID
//
NameString = NULL;
StringSize = 0;
Status = GetSectionFromAnyFv (
FileName,
EFI_SECTION_USER_INTERFACE,
0,
(VOID **) &NameString,
&StringSize
);
if (EFI_ERROR (Status)) {
UnicodeSPrint (mNameString, sizeof (mNameString), L"%g", FileName);
} else {
StrnCpyS (mNameString, NAME_STRING_LENGTH + 1, NameString, NAME_STRING_LENGTH);
mNameString [NAME_STRING_LENGTH] = 0;
FreePool (NameString);
}
} else if (DevicePathSubType (LoadedImage->FilePath) == MEDIA_FILEPATH_DP) {
StrnCpyS (
mNameString,
NAME_STRING_LENGTH + 1,
((FILEPATH_DEVICE_PATH *) LoadedImage->FilePath)->PathName,
NAME_STRING_LENGTH
);
}
return EFI_SUCCESS;
}
/**
Get all images loaded at SMM and update the image database structure.
@retval EFI_SUCCESS The function completed successfully
@retval EFI_NOT_FOUND Failed to locate loaded Image protocol handler
@retval EFI_OUT_OF_RESOURCES Insufficient resources to create buffer
**/
EFI_STATUS
SpaGetLoadedImageSmmLock (
VOID
)
{
EFI_STATUS Status;
UINTN NoImageHandles;
UINTN HandleBufferSize;
EFI_HANDLE *HandleBuffer;
UINTN Index;
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
CHAR16 NameString [NAME_STRING_LENGTH + 1];
UINTN EntryPoint;
UINTN RealImageBase;
EFI_GUID Guid;
IMAGE_HANDLE_DB *ImageHandleDb = NULL;
UINTN ImageStructCount;
HandleBufferSize = 0;
HandleBuffer = NULL;
Status = gSmst->SmmLocateHandle (
ByProtocol,
&gEfiLoadedImageProtocolGuid,
NULL,
&HandleBufferSize,
HandleBuffer
);
if (Status != EFI_BUFFER_TOO_SMALL) {
return EFI_NOT_FOUND;
}
HandleBuffer = AllocateZeroPool (HandleBufferSize);
if (HandleBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = gSmst->SmmLocateHandle (
ByProtocol,
&gEfiLoadedImageProtocolGuid,
NULL,
&HandleBufferSize,
HandleBuffer
);
if (EFI_ERROR(Status)) {
DEBUG ((DEBUG_INFO, "SpsGetLoaderImageSmmLock failed to locate loaded Image protocol handler.\n"));
return EFI_NOT_FOUND;
}
NoImageHandles = HandleBufferSize / sizeof (EFI_HANDLE);
//
// Allocate memory for Image Handler DB structure. Size should be Max number of Image handles of Image structure.
// Remove one Image structure size since its already considered in image_handle_db.
//
ImageHandleDb = AllocateZeroPool (sizeof (IMAGE_HANDLE_DB) + ((NoImageHandles-1) * sizeof (IMAGE_STRUCT)));
if (ImageHandleDb == NULL) {
gSmst->SmmFreePool (HandleBuffer); // Clear Handler buffer which was allocated earlier in this function.
return EFI_OUT_OF_RESOURCES;
}
ImageStructCount = 0;
ImageHandleDb->ImageStructCountMax = NoImageHandles;
DEBUG((DEBUG_INFO, "Max no of Image Handles = %d\n", NoImageHandles));
for (Index = 0; Index < NoImageHandles; Index++) {
Status = gSmst->SmmHandleProtocol (
HandleBuffer [Index],
&gEfiLoadedImageProtocolGuid,
(VOID **) &LoadedImage);
if (EFI_ERROR (Status)) {
continue;
}
Status = GetDriverNameString (LoadedImage, &Guid, NameString);
EntryPoint = 0;
RealImageBase = (UINTN) LoadedImage->ImageBase;
AddImageStruct (
RealImageBase,
(UINTN) LoadedImage->ImageSize,
(UINTN) LoadedImage->ImageBase,
EntryPoint,
NameString,
&Guid,
&ImageHandleDb->ImageHandles [ImageStructCount]
);
ImageStructCount++;
}
ImageHandleDb->ImageStructCount = ImageStructCount;
gSpaCtxt->ImageHandleDb = (UINTN) ImageHandleDb;
gSmst->SmmFreePool (HandleBuffer);
return EFI_SUCCESS;
}
/**
SMM Policy Analyzer (SPA) callback function at SMM Ready to Lock.
Initializes the SPA Context structure with reserved memory for SPA.
@param[in] Protocol Points to the protocol's unique identifier
@param[in] Interface Points to the interface instance
@param[in] Handle The handle on which the interface was installed
@retval EFI_SUCCESS Notification handler runs successfully.
**/
EFI_STATUS
EFIAPI
SpaSmmReadyToLockCallback (
IN CONST EFI_GUID *Protocol,
IN VOID *Interface,
IN EFI_HANDLE Handle
)
{
EFI_STATUS Status;
UINTN *PageTablePool = NULL;
UINTN *UsermodePage = NULL;
// Update for SPA log output protocol no of entries and Entries address
Status = SpaGetLogProtocolEntries ();
if (EFI_ERROR (Status)) {
DEBUG((DEBUG_ERROR, "Failed to get spa log output protocol entries.\n"));
return Status;
}
Status = SpaGetLoadedImageSmmLock ();
if (EFI_ERROR (Status)) {
// This is only SMM Driver information. There is no harm if continued.
// But driver details will not be available in SPA log.
DEBUG(( DEBUG_ERROR, "Failed to get SMM driver loaded information. Continue in case of Release build.\n"));
ASSERT_EFI_ERROR (FALSE);
}
//
// Allocate 1 MB of Page table pool for SPA Usage.
//
PageTablePool = AllocateZeroPool (SPA_PAGE_TABLE_POOL_SIZE);
if (PageTablePool == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Allocate 1 Page for user mode page. This is used for passing log data to user mode callback.
//
UsermodePage = AllocateZeroPool (EFI_PAGE_SIZE);
if (UsermodePage == NULL) {
return EFI_OUT_OF_RESOURCES;
}
gSpaCtxt->UsermodePage = (UINTN) UsermodePage;
gSpaCtxt->SpaPageTablePool = (UINTN) PageTablePool;
gSpaCtxt->SpaPageTablePoolSize = SPA_PAGE_TABLE_POOL_SIZE;
gSpaCtxt->SpaLogOutputCallback = (UINTN) AsmSpaLogOutputCb;
gSpaCtxt->Valid = 1;
return EFI_SUCCESS;
}
#endif