mirror of https://github.com/acidanthera/audk.git
UefiCpuPkg/PiSmmCpuDxeSmm: Introduce page table pool mechanism
Introduce page table pool mechanism for smm page table to simplify page table memory management and protection. This mechanism has been used in DxeIpl. The basic idea is to allocate a bunch of continuous pages of memory in advance, and all future page tables consumption will happen in those pool instead of system memory. Since we have centralized page tables, we only need to mark all page table pools as RO, instead of searching page table memory layer by layer in smm page table. Once current page table pool has been used up, another memory pool will be allocated and the new pool will also be set as RO if current page table memory has been marked as RO. Signed-off-by: Dun Tan <dun.tan@intel.com> Cc: Eric Dong <eric.dong@intel.com> Reviewed-by: Ray Ni <ray.ni@intel.com> Cc: Rahul Kumar <rahul1.kumar@intel.com>
This commit is contained in:
parent
0b633b1494
commit
b822be1a20
|
@ -10,24 +10,6 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
|
|||
|
||||
#include "PiSmmCpuDxeSmm.h"
|
||||
|
||||
/**
|
||||
Disable CET.
|
||||
**/
|
||||
VOID
|
||||
EFIAPI
|
||||
DisableCet (
|
||||
VOID
|
||||
);
|
||||
|
||||
/**
|
||||
Enable CET.
|
||||
**/
|
||||
VOID
|
||||
EFIAPI
|
||||
EnableCet (
|
||||
VOID
|
||||
);
|
||||
|
||||
/**
|
||||
Create PageTable for SMM use.
|
||||
|
||||
|
@ -320,6 +302,8 @@ SetPageTableAttributes (
|
|||
EnableCet ();
|
||||
}
|
||||
|
||||
mIsReadOnlyPageTable = TRUE;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1322,36 +1322,6 @@ ConfigSmmCodeAccessCheck (
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
This API provides a way to allocate memory for page table.
|
||||
|
||||
This API can be called more once to allocate memory for page tables.
|
||||
|
||||
Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the
|
||||
allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL
|
||||
is returned. If there is not enough memory remaining to satisfy the request, then NULL is
|
||||
returned.
|
||||
|
||||
@param Pages The number of 4 KB pages to allocate.
|
||||
|
||||
@return A pointer to the allocated buffer or NULL if allocation fails.
|
||||
|
||||
**/
|
||||
VOID *
|
||||
AllocatePageTableMemory (
|
||||
IN UINTN Pages
|
||||
)
|
||||
{
|
||||
VOID *Buffer;
|
||||
|
||||
Buffer = SmmCpuFeaturesAllocatePageTableMemory (Pages);
|
||||
if (Buffer != NULL) {
|
||||
return Buffer;
|
||||
}
|
||||
|
||||
return AllocatePages (Pages);
|
||||
}
|
||||
|
||||
/**
|
||||
Allocate pages for code.
|
||||
|
||||
|
|
|
@ -260,12 +260,43 @@ extern UINTN mNumberOfCpus;
|
|||
extern EFI_SMM_CPU_PROTOCOL mSmmCpu;
|
||||
extern EFI_MM_MP_PROTOCOL mSmmMp;
|
||||
extern BOOLEAN m5LevelPagingNeeded;
|
||||
extern BOOLEAN mIsReadOnlyPageTable;
|
||||
|
||||
///
|
||||
/// The mode of the CPU at the time an SMI occurs
|
||||
///
|
||||
extern UINT8 mSmmSaveStateRegisterLma;
|
||||
|
||||
#define PAGE_TABLE_POOL_ALIGNMENT BASE_128KB
|
||||
#define PAGE_TABLE_POOL_UNIT_SIZE BASE_128KB
|
||||
#define PAGE_TABLE_POOL_UNIT_PAGES EFI_SIZE_TO_PAGES (PAGE_TABLE_POOL_UNIT_SIZE)
|
||||
#define PAGE_TABLE_POOL_ALIGN_MASK \
|
||||
(~(EFI_PHYSICAL_ADDRESS)(PAGE_TABLE_POOL_ALIGNMENT - 1))
|
||||
|
||||
typedef struct {
|
||||
VOID *NextPool;
|
||||
UINTN Offset;
|
||||
UINTN FreePages;
|
||||
} PAGE_TABLE_POOL;
|
||||
|
||||
/**
|
||||
Disable CET.
|
||||
**/
|
||||
VOID
|
||||
EFIAPI
|
||||
DisableCet (
|
||||
VOID
|
||||
);
|
||||
|
||||
/**
|
||||
Enable CET.
|
||||
**/
|
||||
VOID
|
||||
EFIAPI
|
||||
EnableCet (
|
||||
VOID
|
||||
);
|
||||
|
||||
//
|
||||
// SMM CPU Protocol function prototypes.
|
||||
//
|
||||
|
|
|
@ -35,6 +35,143 @@ PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
|
|||
BOOLEAN mIsShadowStack = FALSE;
|
||||
BOOLEAN m5LevelPagingNeeded = FALSE;
|
||||
|
||||
//
|
||||
// Global variable to keep track current available memory used as page table.
|
||||
//
|
||||
PAGE_TABLE_POOL *mPageTablePool = NULL;
|
||||
|
||||
//
|
||||
// If memory used by SMM page table has been mareked as ReadOnly.
|
||||
//
|
||||
BOOLEAN mIsReadOnlyPageTable = FALSE;
|
||||
|
||||
/**
|
||||
Initialize a buffer pool for page table use only.
|
||||
|
||||
To reduce the potential split operation on page table, the pages reserved for
|
||||
page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and
|
||||
at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always
|
||||
initialized with number of pages greater than or equal to the given PoolPages.
|
||||
|
||||
Once the pages in the pool are used up, this method should be called again to
|
||||
reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. But usually this won't
|
||||
happen in practice.
|
||||
|
||||
@param PoolPages The least page number of the pool to be created.
|
||||
|
||||
@retval TRUE The pool is initialized successfully.
|
||||
@retval FALSE The memory is out of resource.
|
||||
**/
|
||||
BOOLEAN
|
||||
InitializePageTablePool (
|
||||
IN UINTN PoolPages
|
||||
)
|
||||
{
|
||||
VOID *Buffer;
|
||||
BOOLEAN CetEnabled;
|
||||
|
||||
//
|
||||
// Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for
|
||||
// header.
|
||||
//
|
||||
PoolPages += 1; // Add one page for header.
|
||||
PoolPages = ((PoolPages - 1) / PAGE_TABLE_POOL_UNIT_PAGES + 1) *
|
||||
PAGE_TABLE_POOL_UNIT_PAGES;
|
||||
Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);
|
||||
if (Buffer == NULL) {
|
||||
DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//
|
||||
// Link all pools into a list for easier track later.
|
||||
//
|
||||
if (mPageTablePool == NULL) {
|
||||
mPageTablePool = Buffer;
|
||||
mPageTablePool->NextPool = mPageTablePool;
|
||||
} else {
|
||||
((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool;
|
||||
mPageTablePool->NextPool = Buffer;
|
||||
mPageTablePool = Buffer;
|
||||
}
|
||||
|
||||
//
|
||||
// Reserve one page for pool header.
|
||||
//
|
||||
mPageTablePool->FreePages = PoolPages - 1;
|
||||
mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1);
|
||||
|
||||
//
|
||||
// If page table memory has been marked as RO, mark the new pool pages as read-only.
|
||||
//
|
||||
if (mIsReadOnlyPageTable) {
|
||||
CetEnabled = ((AsmReadCr4 () & CR4_CET_ENABLE) != 0) ? TRUE : FALSE;
|
||||
if (CetEnabled) {
|
||||
//
|
||||
// CET must be disabled if WP is disabled.
|
||||
//
|
||||
DisableCet ();
|
||||
}
|
||||
|
||||
AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP);
|
||||
SmmSetMemoryAttributes ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, EFI_PAGES_TO_SIZE (PoolPages), EFI_MEMORY_RO);
|
||||
AsmWriteCr0 (AsmReadCr0 () | CR0_WP);
|
||||
if (CetEnabled) {
|
||||
//
|
||||
// re-enable CET.
|
||||
//
|
||||
EnableCet ();
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
This API provides a way to allocate memory for page table.
|
||||
|
||||
This API can be called more once to allocate memory for page tables.
|
||||
|
||||
Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the
|
||||
allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL
|
||||
is returned. If there is not enough memory remaining to satisfy the request, then NULL is
|
||||
returned.
|
||||
|
||||
@param Pages The number of 4 KB pages to allocate.
|
||||
|
||||
@return A pointer to the allocated buffer or NULL if allocation fails.
|
||||
|
||||
**/
|
||||
VOID *
|
||||
AllocatePageTableMemory (
|
||||
IN UINTN Pages
|
||||
)
|
||||
{
|
||||
VOID *Buffer;
|
||||
|
||||
if (Pages == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// Renew the pool if necessary.
|
||||
//
|
||||
if ((mPageTablePool == NULL) ||
|
||||
(Pages > mPageTablePool->FreePages))
|
||||
{
|
||||
if (!InitializePageTablePool (Pages)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset;
|
||||
|
||||
mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages);
|
||||
mPageTablePool->FreePages -= Pages;
|
||||
|
||||
return Buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
Return length according to page attributes.
|
||||
|
||||
|
|
|
@ -20,24 +20,6 @@ BOOLEAN m1GPageTableSupport = FALSE;
|
|||
BOOLEAN mCpuSmmRestrictedMemoryAccess;
|
||||
X86_ASSEMBLY_PATCH_LABEL gPatch5LevelPagingNeeded;
|
||||
|
||||
/**
|
||||
Disable CET.
|
||||
**/
|
||||
VOID
|
||||
EFIAPI
|
||||
DisableCet (
|
||||
VOID
|
||||
);
|
||||
|
||||
/**
|
||||
Enable CET.
|
||||
**/
|
||||
VOID
|
||||
EFIAPI
|
||||
EnableCet (
|
||||
VOID
|
||||
);
|
||||
|
||||
/**
|
||||
Check if 1-GByte pages is supported by processor or not.
|
||||
|
||||
|
@ -1305,6 +1287,8 @@ SetPageTableAttributes (
|
|||
EnableCet ();
|
||||
}
|
||||
|
||||
mIsReadOnlyPageTable = TRUE;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue