/** @file
|
|
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
**/
|
|
#include <Uefi.h>
|
#include <PiSmm.h>
|
#include <Library/TestPointCheckLib.h>
|
#include <Library/TestPointLib.h>
|
#include <Library/DebugLib.h>
|
#include <Library/SmmServicesTableLib.h>
|
#include <Library/BaseMemoryLib.h>
|
#include <Library/MemoryAllocationLib.h>
|
#include <Guid/MemoryAttributesTable.h>
|
#include <Register/SmramSaveStateMap.h>
|
#include <Register/StmApi.h>
|
#include <Register/Msr.h>
|
|
#define NEXT_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
|
((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) + (Size)))
|
|
///
|
/// Page Table Entry
|
///
|
#define IA32_PG_P BIT0
|
#define IA32_PG_RW BIT1
|
#define IA32_PG_U BIT2
|
#define IA32_PG_WT BIT3
|
#define IA32_PG_CD BIT4
|
#define IA32_PG_A BIT5
|
#define IA32_PG_D BIT6
|
#define IA32_PG_PS BIT7
|
#define IA32_PG_PAT_2M BIT12
|
#define IA32_PG_PAT_4K IA32_PG_PS
|
#define IA32_PG_PMNT BIT62
|
#define IA32_PG_NX BIT63
|
|
#define PAGING_4K_MASK 0xFFF
|
#define PAGING_2M_MASK 0x1FFFFF
|
#define PAGING_1G_MASK 0x3FFFFFFF
|
|
#define PAGING_PAE_INDEX_MASK 0x1FF
|
|
#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull
|
#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull
|
#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
|
|
typedef enum {
|
PageNone,
|
Page4K,
|
Page2M,
|
Page1G,
|
} PAGE_ATTRIBUTE;
|
|
typedef struct {
|
PAGE_ATTRIBUTE Attribute;
|
UINT64 Length;
|
UINT64 AddressMask;
|
} PAGE_ATTRIBUTE_TABLE;
|
|
PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
|
{Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64},
|
{Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64},
|
{Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},
|
};
|
|
EFI_STATUS
|
EFIAPI
|
SmmGetSystemConfigurationTable (
|
IN EFI_GUID *TableGuid,
|
OUT VOID **Table
|
);
|
|
/**
|
Return page table base.
|
|
@return page table base.
|
**/
|
UINTN
|
GetPageTableBase (
|
VOID
|
)
|
{
|
return (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);
|
}
|
|
/**
|
Return length according to page attributes.
|
|
@param[in] PageAttributes The page attribute of the page entry.
|
|
@return The length of page entry.
|
**/
|
UINTN
|
PageAttributeToLength (
|
IN PAGE_ATTRIBUTE PageAttribute
|
)
|
{
|
UINTN Index;
|
for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {
|
if (PageAttribute == mPageAttributeTable[Index].Attribute) {
|
return (UINTN)mPageAttributeTable[Index].Length;
|
}
|
}
|
return 0;
|
}
|
|
/**
|
Return page table entry to match the address.
|
|
@param[in] Address The address to be checked.
|
@param[out] PageAttributes The page attribute of the page entry.
|
|
@return The page entry.
|
**/
|
VOID *
|
GetPageTableEntry (
|
IN PHYSICAL_ADDRESS Address,
|
OUT PAGE_ATTRIBUTE *PageAttribute
|
)
|
{
|
UINTN Index1;
|
UINTN Index2;
|
UINTN Index3;
|
UINTN Index4;
|
UINT64 *L1PageTable;
|
UINT64 *L2PageTable;
|
UINT64 *L3PageTable;
|
UINT64 *L4PageTable;
|
|
Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK;
|
Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK;
|
Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK;
|
Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK;
|
|
if (sizeof(UINTN) == sizeof(UINT64)) {
|
L4PageTable = (UINT64 *)GetPageTableBase ();
|
if (L4PageTable[Index4] == 0) {
|
*PageAttribute = Page1G;
|
return NULL;
|
}
|
|
L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & PAGING_4K_ADDRESS_MASK_64);
|
} else {
|
L3PageTable = (UINT64 *)GetPageTableBase ();
|
}
|
if (L3PageTable[Index3] == 0) {
|
*PageAttribute = Page1G;
|
return NULL;
|
}
|
if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {
|
// 1G
|
*PageAttribute = Page1G;
|
return &L3PageTable[Index3];
|
}
|
|
L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & PAGING_4K_ADDRESS_MASK_64);
|
if (L2PageTable[Index2] == 0) {
|
*PageAttribute = Page2M;
|
return NULL;
|
}
|
if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
|
// 2M
|
*PageAttribute = Page2M;
|
return &L2PageTable[Index2];
|
}
|
|
// 4k
|
L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & PAGING_4K_ADDRESS_MASK_64);
|
if ((L1PageTable[Index1] == 0) && (Address != 0)) {
|
*PageAttribute = Page4K;
|
return NULL;
|
}
|
*PageAttribute = Page4K;
|
return &L1PageTable[Index1];
|
}
|
|
typedef struct {
|
volatile BOOLEAN Valid;
|
volatile UINT64 SmBase;
|
} SMBASE_SHARED_BUFFER;
|
|
VOID
|
EFIAPI
|
GetSmBaseOnCurrentProcessor (
|
IN OUT VOID *Buffer
|
)
|
{
|
SMBASE_SHARED_BUFFER *SmBaseBuffer;
|
|
SmBaseBuffer = Buffer;
|
SmBaseBuffer->SmBase = AsmReadMsr64 (MSR_IA32_SMBASE);
|
SmBaseBuffer->Valid = TRUE;
|
}
|
|
UINT64 *mSmBaseBuffer;
|
|
VOID
|
SetupSmBaseBuffer (
|
VOID
|
)
|
{
|
volatile SMBASE_SHARED_BUFFER SmBaseBuffer;
|
EFI_STATUS Status;
|
UINTN Index;
|
UINTN Base = 0;
|
UINTN Delta = 0;
|
|
if (mSmBaseBuffer != NULL) {
|
return ;
|
}
|
|
mSmBaseBuffer = AllocatePool (sizeof(UINT64) * gSmst->NumberOfCpus);
|
ASSERT(mSmBaseBuffer != NULL);
|
|
for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
|
ZeroMem ((VOID *)&SmBaseBuffer, sizeof(SmBaseBuffer));
|
if (Index == gSmst->CurrentlyExecutingCpu) {
|
GetSmBaseOnCurrentProcessor ((VOID *)&SmBaseBuffer);
|
DEBUG ((DEBUG_INFO, "SmbaseBsp(%d) - 0x%x\n", Index, SmBaseBuffer.SmBase));
|
} else {
|
Status = gSmst->SmmStartupThisAp (GetSmBaseOnCurrentProcessor, Index, (VOID *)&SmBaseBuffer);
|
if (!FeaturePcdGet (PcdCpuHotPlugSupport)) {
|
ASSERT_EFI_ERROR (Status);
|
}
|
if (EFI_ERROR(Status)) {
|
SmBaseBuffer.SmBase = Base + Delta * Index;
|
DEBUG ((DEBUG_INFO, "SmbaseAp(%d) - unknown, guess - 0x%x\n", Index, SmBaseBuffer.SmBase));
|
} else {
|
while (!SmBaseBuffer.Valid) {
|
CpuPause ();
|
}
|
DEBUG ((DEBUG_INFO, "SmbaseAp(%d) - 0x%x\n", Index, SmBaseBuffer.SmBase));
|
}
|
}
|
mSmBaseBuffer[Index] = SmBaseBuffer.SmBase;
|
if (Base == 0) {
|
Base = (UINTN)SmBaseBuffer.SmBase;
|
DEBUG ((DEBUG_INFO, "-- Base - 0x%x\n", Base));
|
} else if (Delta == 0) {
|
Delta = (UINTN)(SmBaseBuffer.SmBase - Base);
|
DEBUG ((DEBUG_INFO, "-- Delta - 0x%x\n", Delta));
|
}
|
}
|
}
|
|
BOOLEAN
|
IsSmmSaveState (
|
IN EFI_PHYSICAL_ADDRESS BaseAddress
|
)
|
{
|
UINTN Index;
|
UINTN TileCodeSize;
|
UINTN TileDataSize;
|
UINTN TileSize;
|
|
SetupSmBaseBuffer ();
|
|
TileCodeSize = SIZE_4KB; // BUGBUG: Assume 4KB
|
TileCodeSize = ALIGN_VALUE(TileCodeSize, SIZE_4KB);
|
TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - TXT_SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP);
|
TileDataSize = ALIGN_VALUE(TileDataSize, SIZE_4KB);
|
TileSize = TileDataSize + TileCodeSize - 1;
|
TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);
|
|
for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
|
if (Index == gSmst->NumberOfCpus - 1) {
|
TileSize = SIZE_32KB;
|
}
|
if ((BaseAddress >= mSmBaseBuffer[Index] + SMM_HANDLER_OFFSET + TileCodeSize) &&
|
(BaseAddress < mSmBaseBuffer[Index] + SMM_HANDLER_OFFSET + TileSize)) {
|
return TRUE;
|
}
|
}
|
|
return FALSE;
|
}
|
|
EFI_STATUS
|
TestPointCheckPageTable (
|
IN EFI_PHYSICAL_ADDRESS BaseAddress,
|
IN UINTN Length,
|
IN BOOLEAN IsCode,
|
IN BOOLEAN IsOutsideSmram
|
)
|
{
|
UINT64 *PageEntry;
|
PAGE_ATTRIBUTE PageAttribute;
|
UINTN PageEntryLength;
|
EFI_PHYSICAL_ADDRESS BaseAddressEnd;
|
|
ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);
|
ASSERT ((Length & (SIZE_4KB - 1)) == 0);
|
|
BaseAddressEnd = BaseAddress + Length;
|
|
//
|
// Below logic is to check 2M/4K page to make sure we donot waist memory.
|
//
|
while (BaseAddress < BaseAddressEnd) {
|
PageEntry = GetPageTableEntry (BaseAddress, &PageAttribute);
|
ASSERT (PageAttribute != PageNone);
|
|
if (IsOutsideSmram) {
|
if ((PageEntry != NULL) && ((*PageEntry & IA32_PG_P) != 0)) {
|
DEBUG ((DEBUG_ERROR, "OutsideSmram present - 0x%x\n", BaseAddress));
|
return EFI_INVALID_PARAMETER;
|
}
|
}
|
|
if (IsCode) {
|
if ((PageEntry == NULL) || ((*PageEntry & IA32_PG_P) == 0)) {
|
DEBUG ((DEBUG_ERROR, "Code Page not exist - 0x%x\n", BaseAddress));
|
return EFI_INVALID_PARAMETER;
|
} else if ((*PageEntry & IA32_PG_RW) != 0) {
|
//
|
// Check if it is SMM SaveState
|
//
|
if (!IsSmmSaveState (BaseAddress)) {
|
DEBUG ((DEBUG_ERROR, "Code Page read write - 0x%x\n", BaseAddress));
|
return EFI_INVALID_PARAMETER;
|
}
|
}
|
} else {
|
if ((PageEntry == NULL) || ((*PageEntry & IA32_PG_P) == 0)) {
|
// Pass
|
} else if ((*PageEntry & IA32_PG_NX) == 0) {
|
DEBUG ((DEBUG_ERROR, "Data Page executable - 0x%x\n", BaseAddress));
|
return EFI_INVALID_PARAMETER;
|
}
|
}
|
|
PageEntryLength = PageAttributeToLength (PageAttribute);
|
PageEntryLength = (UINTN)((BaseAddress & ~(PageEntryLength - 1)) + PageEntryLength - BaseAddress);
|
|
//
|
// move to next
|
//
|
BaseAddress += PageEntryLength;
|
Length -= PageEntryLength;
|
}
|
|
return RETURN_SUCCESS;
|
}
|
|
EFI_STATUS
|
TestPointCheckPagingWithMemoryAttributesTable (
|
IN EFI_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable
|
)
|
{
|
UINTN Index;
|
EFI_MEMORY_DESCRIPTOR *Entry;
|
EFI_STATUS Status;
|
EFI_STATUS ReturnStatus;
|
|
ReturnStatus = EFI_SUCCESS;
|
Entry = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1);
|
for (Index = 0; Index < MemoryAttributesTable->NumberOfEntries; Index++) {
|
DEBUG ((DEBUG_INFO, "SmmMemoryAttribute Checking 0x%lx - 0x%x\n", Entry->PhysicalStart, EFI_PAGES_TO_SIZE((UINTN)Entry->NumberOfPages)));
|
Status = TestPointCheckPageTable (
|
Entry->PhysicalStart,
|
EFI_PAGES_TO_SIZE((UINTN)Entry->NumberOfPages),
|
((Entry->Attribute & EFI_MEMORY_RO) == 0) ? FALSE : TRUE,
|
FALSE
|
);
|
if (EFI_ERROR(Status)) {
|
ReturnStatus = Status;
|
}
|
Entry = NEXT_MEMORY_DESCRIPTOR (Entry, MemoryAttributesTable->DescriptorSize);
|
}
|
|
return ReturnStatus;
|
}
|
|
EFI_STATUS
|
TestPointCheckSmmPaging (
|
VOID
|
)
|
{
|
EFI_STATUS Status;
|
VOID *MemoryAttributesTable;
|
|
DEBUG ((DEBUG_INFO, "==== TestPointCheckSmmPaging - Enter\n"));
|
|
Status = SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);
|
if (!EFI_ERROR (Status)) {
|
Status = TestPointCheckPagingWithMemoryAttributesTable(MemoryAttributesTable);
|
}
|
|
if (EFI_ERROR (Status)) {
|
TestPointLibAppendErrorString (
|
PLATFORM_TEST_POINT_ROLE_PLATFORM_IBV,
|
NULL,
|
TEST_POINT_BYTE6_SMM_READY_TO_BOOT_SMM_PAGE_LEVEL_PROTECTION_ERROR_CODE \
|
TEST_POINT_SMM_READY_TO_BOOT \
|
TEST_POINT_BYTE6_SMM_READY_TO_BOOT_SMM_PAGE_LEVEL_PROTECTION_ERROR_STRING
|
);
|
}
|
|
DEBUG ((DEBUG_INFO, "==== TestPointCheckSmmPaging - Exit\n"));
|
return EFI_SUCCESS;
|
}
|