UefiCpuPkg/PiSmmCpuDxeSmm: Refine DxeSmm PageTable update logic

This patch is to refine the updatePageTable logic for DxeSmm.

For DxeSmm, PageTable will be updated in the first SMI when SMM
ready to lock happen:

IF SMM Profile is TRUE:
1. Mark mProtectionMemRange attribute: SmrrBase:Present, SMM
   profile base:Present&Nx, MMRAM ranges:Present, MMIO ranges:
   Present&Nx.
2. Mark the ranges not in mProtectionMemRange as RP (non-present).

IF SMM Profile is FALSE:
1. Mark Non-MMRAM ranges as NX.
2. IF RestrictedMemoryAccess is TRUE:
   Forbidden Address mark as RP (IsUefiPageNotPresent is TRUE).

Signed-off-by: Jiaxin Wu <jiaxin.wu@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Star Zeng <star.zeng@intel.com>
Cc: Dun Tan <dun.tan@intel.com>
Cc: Hongbin1 Zhang <hongbin1.zhang@intel.com>
Cc: Wei6 Xu <wei6.xu@intel.com>
Cc: Yuanhao Xie <yuanhao.xie@intel.com>
This commit is contained in:
Jiaxin Wu 2024-07-08 09:59:03 +08:00 committed by mergify[bot]
parent 5bcf6049f2
commit 1816c78f43
5 changed files with 187 additions and 157 deletions

View File

@ -311,116 +311,170 @@ GetUefiMemoryMap (
}
/**
This function sets UEFI memory attribute according to UEFI memory map.
This function updates UEFI memory attribute according to UEFI memory map.
The normal memory region is marked as not present, such as
EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
EfiUnusableMemory, EfiACPIReclaimMemory.
**/
VOID
SetUefiMemMapAttributes (
UpdateUefiMemMapAttributes (
VOID
)
{
EFI_STATUS Status;
EFI_MEMORY_DESCRIPTOR *MemoryMap;
UINTN MemoryMapEntryCount;
UINTN Index;
EFI_MEMORY_DESCRIPTOR *Entry;
BOOLEAN WriteProtect;
BOOLEAN CetEnabled;
EFI_STATUS Status;
UINTN Index;
UINT64 Limit;
UINT64 PreviousAddress;
UINTN PageTable;
UINT64 Base;
EFI_MEMORY_DESCRIPTOR *MemoryMap;
UINTN MemoryMapEntryCount;
EFI_MEMORY_DESCRIPTOR *Entry;
PERF_FUNCTION_BEGIN ();
DEBUG ((DEBUG_INFO, "SetUefiMemMapAttributes\n"));
DEBUG ((DEBUG_INFO, "UpdateUefiMemMapAttributes Start...\n"));
WRITE_UNPROTECT_RO_PAGES (WriteProtect, CetEnabled);
if (mUefiMemoryMap != NULL) {
MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;
MemoryMap = mUefiMemoryMap;
for (Index = 0; Index < MemoryMapEntryCount; Index++) {
if (IsUefiPageNotPresent (MemoryMap)) {
Status = SmmSetMemoryAttributes (
MemoryMap->PhysicalStart,
EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
EFI_MEMORY_RP
);
DEBUG ((
DEBUG_INFO,
"UefiMemory protection: 0x%lx - 0x%lx %r\n",
MemoryMap->PhysicalStart,
MemoryMap->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
Status
));
}
PageTable = AsmReadCr3 ();
Limit = LShiftU64 (1, mPhysicalAddressBits);
MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, mUefiDescriptorSize);
//
// [0, 4k] may be non-present.
//
PreviousAddress = ((FixedPcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0) ? BASE_4KB : 0;
//
// NonMmram shall be non-executable after the SmmReadyToLock event occurs, regardless of whether
// RestrictedMemoryAccess is enabled, since all MM drivers located in NonMmram have already been dispatched and executed.
//
for (Index = 0; Index < mSmmCpuSmramRangeCount; Index++) {
Base = mSmmCpuSmramRanges[Index].CpuStart;
if (Base > PreviousAddress) {
Status = ConvertMemoryPageAttributes (PageTable, mPagingMode, PreviousAddress, Base - PreviousAddress, EFI_MEMORY_XP, TRUE, NULL);
ASSERT_RETURN_ERROR (Status);
}
PreviousAddress = mSmmCpuSmramRanges[Index].CpuStart + mSmmCpuSmramRanges[Index].PhysicalSize;
}
if (PreviousAddress < Limit) {
Status = ConvertMemoryPageAttributes (PageTable, mPagingMode, PreviousAddress, Limit - PreviousAddress, EFI_MEMORY_XP, TRUE, NULL);
ASSERT_RETURN_ERROR (Status);
}
//
// Do not free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress().
// Set NonMmram to not-present by excluding "RT, Reserved and NVS" memory type when RestrictedMemoryAccess is enabled.
//
//
// Set untested memory as not present.
//
if (mGcdMemSpace != NULL) {
for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) {
Status = SmmSetMemoryAttributes (
mGcdMemSpace[Index].BaseAddress,
mGcdMemSpace[Index].Length,
EFI_MEMORY_RP
);
DEBUG ((
DEBUG_INFO,
"GcdMemory protection: 0x%lx - 0x%lx %r\n",
mGcdMemSpace[Index].BaseAddress,
mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length,
Status
));
}
}
//
// Do not free mGcdMemSpace, it will be checked in IsSmmCommBufferForbiddenAddress().
//
//
// Set UEFI runtime memory with EFI_MEMORY_RO as not present.
//
if (mUefiMemoryAttributesTable != NULL) {
Entry = (EFI_MEMORY_DESCRIPTOR *)(mUefiMemoryAttributesTable + 1);
for (Index = 0; Index < mUefiMemoryAttributesTable->NumberOfEntries; Index++) {
if ((Entry->Type == EfiRuntimeServicesCode) || (Entry->Type == EfiRuntimeServicesData)) {
if ((Entry->Attribute & EFI_MEMORY_RO) != 0) {
Status = SmmSetMemoryAttributes (
Entry->PhysicalStart,
EFI_PAGES_TO_SIZE ((UINTN)Entry->NumberOfPages),
EFI_MEMORY_RP
if (IsRestrictedMemoryAccess ()) {
if (mUefiMemoryMap != NULL) {
MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;
MemoryMap = mUefiMemoryMap;
for (Index = 0; Index < MemoryMapEntryCount; Index++) {
if (IsUefiPageNotPresent (MemoryMap)) {
Status = ConvertMemoryPageAttributes (
PageTable,
mPagingMode,
MemoryMap->PhysicalStart,
EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
EFI_MEMORY_RP,
TRUE,
NULL
);
DEBUG ((
DEBUG_INFO,
"UefiMemoryAttribute protection: 0x%lx - 0x%lx %r\n",
Entry->PhysicalStart,
Entry->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE ((UINTN)Entry->NumberOfPages),
"UefiMemory protection: 0x%lx - 0x%lx %r\n",
MemoryMap->PhysicalStart,
MemoryMap->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
Status
));
}
}
Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);
MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, mUefiDescriptorSize);
}
}
//
// Do not free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress().
//
//
// Set untested NonMmram memory as not present.
//
if (mGcdMemSpace != NULL) {
for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) {
Status = ConvertMemoryPageAttributes (
PageTable,
mPagingMode,
mGcdMemSpace[Index].BaseAddress,
mGcdMemSpace[Index].Length,
EFI_MEMORY_RP,
TRUE,
NULL
);
DEBUG ((
DEBUG_INFO,
"GcdMemory protection: 0x%lx - 0x%lx %r\n",
mGcdMemSpace[Index].BaseAddress,
mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length,
Status
));
}
}
//
// Do not free mGcdMemSpace, it will be checked in IsSmmCommBufferForbiddenAddress().
//
//
// Above logic sets the whole RT memory as present.
// Below logic is to set the RT code as not present.
//
if (mUefiMemoryAttributesTable != NULL) {
Entry = (EFI_MEMORY_DESCRIPTOR *)(mUefiMemoryAttributesTable + 1);
for (Index = 0; Index < mUefiMemoryAttributesTable->NumberOfEntries; Index++) {
if ((Entry->Type == EfiRuntimeServicesCode) || (Entry->Type == EfiRuntimeServicesData)) {
if ((Entry->Attribute & EFI_MEMORY_RO) != 0) {
Status = ConvertMemoryPageAttributes (
PageTable,
mPagingMode,
Entry->PhysicalStart,
EFI_PAGES_TO_SIZE ((UINTN)Entry->NumberOfPages),
EFI_MEMORY_RP,
TRUE,
NULL
);
DEBUG ((
DEBUG_INFO,
"UefiMemoryAttribute protection: 0x%lx - 0x%lx %r\n",
Entry->PhysicalStart,
Entry->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE ((UINTN)Entry->NumberOfPages),
Status
));
}
}
Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);
}
}
//
// Do not free mUefiMemoryAttributesTable, it will be checked in IsSmmCommBufferForbiddenAddress().
//
}
//
// Flush TLB
//
CpuFlushTlb ();
//
// Set execute-disable flag
//
mXdEnabled = TRUE;
WRITE_PROTECT_RO_PAGES (WriteProtect, CetEnabled);
//
// Do not free mUefiMemoryAttributesTable, it will be checked in IsSmmCommBufferForbiddenAddress().
//
PERF_FUNCTION_END ();
DEBUG ((DEBUG_INFO, "UpdateUefiMemMapAttributes Done.\n"));
}
/**

View File

@ -958,14 +958,6 @@ SetMemMapAttributes (
EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable
);
/**
This function sets UEFI memory attribute according to UEFI memory map.
**/
VOID
SetUefiMemMapAttributes (
VOID
);
/**
Get SmmProfileData.
@ -1037,6 +1029,15 @@ CreateNonMmramMemMap (
OUT UINTN *MemoryRegionCount
);
/**
This function updates UEFI memory attribute according to UEFI memory map.
**/
VOID
UpdateUefiMemMapAttributes (
VOID
);
/**
This function caches the UEFI memory map information.
**/

View File

@ -58,9 +58,13 @@ PerformRemainingTasks (
}
//
// Create a mix of 2MB and 4KB page table. Update some memory ranges absent and execute-disable.
// Update Page Table for outside SMRAM.
//
InitPaging ();
if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
SmmProfileUpdateMemoryAttributes ();
} else {
UpdateUefiMemMapAttributes ();
}
//
// gEdkiiPiSmmMemoryAttributesTableGuid should have been published at EndOfDxe by SmmCore
@ -77,11 +81,6 @@ PerformRemainingTasks (
}
if (IsRestrictedMemoryAccess ()) {
//
// For outside SMRAM, we only map SMM communication buffer or MMIO.
//
SetUefiMemMapAttributes ();
//
// Set page table itself to be read-only
//

View File

@ -573,11 +573,11 @@ InitProtectedMemRange (
}
/**
Update page table according to protected memory ranges and the 4KB-page mapped memory ranges.
This function updates memory attribute according to mProtectionMemRangeCount.
**/
VOID
InitPaging (
SmmProfileUpdateMemoryAttributes (
VOID
)
{
@ -592,91 +592,67 @@ InitPaging (
BOOLEAN WriteProtect;
BOOLEAN CetEnabled;
PERF_FUNCTION_BEGIN ();
DEBUG ((DEBUG_INFO, "SmmProfileUpdateMemoryAttributes Start...\n"));
WRITE_UNPROTECT_RO_PAGES (WriteProtect, CetEnabled);
PageTable = AsmReadCr3 ();
Limit = LShiftU64 (1, mPhysicalAddressBits);
WRITE_UNPROTECT_RO_PAGES (WriteProtect, CetEnabled);
//
// [0, 4k] may be non-present.
//
PreviousAddress = ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0) ? BASE_4KB : 0;
DEBUG ((DEBUG_INFO, "Patch page table start ...\n"));
if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
for (Index = 0; Index < mProtectionMemRangeCount; Index++) {
MemoryAttrMask = 0;
if (mProtectionMemRange[Index].Nx == TRUE) {
MemoryAttrMask |= EFI_MEMORY_XP;
}
if (mProtectionMemRange[Index].Present == FALSE) {
MemoryAttrMask = EFI_MEMORY_RP;
}
Base = mProtectionMemRange[Index].Range.Base;
Length = mProtectionMemRange[Index].Range.Top - Base;
if (MemoryAttrMask != 0) {
Status = ConvertMemoryPageAttributes (PageTable, mPagingMode, Base, Length, MemoryAttrMask, TRUE, NULL);
ASSERT_RETURN_ERROR (Status);
}
if (Base > PreviousAddress) {
//
// Mark the ranges not in mProtectionMemRange as non-present.
//
MemoryAttrMask = EFI_MEMORY_RP;
Status = ConvertMemoryPageAttributes (PageTable, mPagingMode, PreviousAddress, Base - PreviousAddress, MemoryAttrMask, TRUE, NULL);
ASSERT_RETURN_ERROR (Status);
}
PreviousAddress = Base + Length;
for (Index = 0; Index < mProtectionMemRangeCount; Index++) {
MemoryAttrMask = 0;
if (mProtectionMemRange[Index].Nx == TRUE) {
MemoryAttrMask = EFI_MEMORY_XP;
}
//
// This assignment is for setting the last remaining range
//
MemoryAttrMask = EFI_MEMORY_RP;
} else {
MemoryAttrMask = EFI_MEMORY_XP;
for (Index = 0; Index < mSmmCpuSmramRangeCount; Index++) {
Base = mSmmCpuSmramRanges[Index].CpuStart;
if (Base > PreviousAddress) {
//
// Mark the ranges not in mSmmCpuSmramRanges as NX.
//
Status = ConvertMemoryPageAttributes (PageTable, mPagingMode, PreviousAddress, Base - PreviousAddress, MemoryAttrMask, TRUE, NULL);
ASSERT_RETURN_ERROR (Status);
}
PreviousAddress = mSmmCpuSmramRanges[Index].CpuStart + mSmmCpuSmramRanges[Index].PhysicalSize;
if (mProtectionMemRange[Index].Present == FALSE) {
MemoryAttrMask = EFI_MEMORY_RP;
}
Base = mProtectionMemRange[Index].Range.Base;
Length = mProtectionMemRange[Index].Range.Top - Base;
if (MemoryAttrMask != 0) {
Status = ConvertMemoryPageAttributes (PageTable, mPagingMode, Base, Length, MemoryAttrMask, TRUE, NULL);
ASSERT_RETURN_ERROR (Status);
}
if (Base > PreviousAddress) {
//
// Mark the ranges not in mProtectionMemRange as non-present.
//
Status = ConvertMemoryPageAttributes (PageTable, mPagingMode, PreviousAddress, Base - PreviousAddress, EFI_MEMORY_RP, TRUE, NULL);
ASSERT_RETURN_ERROR (Status);
}
PreviousAddress = Base + Length;
}
//
// Set the last remaining range
//
if (PreviousAddress < Limit) {
//
// Set the last remaining range to EFI_MEMORY_RP/EFI_MEMORY_XP.
// This path applies to both SmmProfile enable/disable case.
//
Status = ConvertMemoryPageAttributes (PageTable, mPagingMode, PreviousAddress, Limit - PreviousAddress, MemoryAttrMask, TRUE, NULL);
Status = ConvertMemoryPageAttributes (PageTable, mPagingMode, PreviousAddress, Limit - PreviousAddress, EFI_MEMORY_RP, TRUE, NULL);
ASSERT_RETURN_ERROR (Status);
}
WRITE_PROTECT_RO_PAGES (WriteProtect, CetEnabled);
//
// Flush TLB
//
CpuFlushTlb ();
DEBUG ((DEBUG_INFO, "Patch page table done!\n"));
//
// Set execute-disable flag
//
mXdEnabled = TRUE;
PERF_FUNCTION_END ();
WRITE_PROTECT_RO_PAGES (WriteProtect, CetEnabled);
DEBUG ((DEBUG_INFO, "SmmProfileUpdateMemoryAttributes Done.\n"));
}
/**

View File

@ -100,11 +100,11 @@ InitProtectedMemRange (
);
/**
Update page table according to protected memory ranges and the 4KB-page mapped memory ranges.
This function updates memory attribute according to mProtectionMemRangeCount.
**/
VOID
InitPaging (
SmmProfileUpdateMemoryAttributes (
VOID
);