2016-10-23 17:19:52 +02:00
/** @file
2023-05-26 14:57:29 +02:00
Copyright ( c ) 2016 - 2023 , Intel Corporation . All rights reserved . < BR >
2019-04-04 01:07:22 +02:00
SPDX - License - Identifier : BSD - 2 - Clause - Patent
2016-10-23 17:19:52 +02:00
* */
# include "PiSmmCpuDxeSmm.h"
2017-11-02 12:00:02 +01:00
//
// attributes for reserved memory before it is promoted to system memory
//
# define EFI_MEMORY_PRESENT 0x0100000000000000ULL
# define EFI_MEMORY_INITIALIZED 0x0200000000000000ULL
# define EFI_MEMORY_TESTED 0x0400000000000000ULL
2016-11-24 06:36:56 +01:00
# define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
( ( EFI_MEMORY_DESCRIPTOR * ) ( ( UINT8 * ) ( MemoryDescriptor ) - ( Size ) ) )
EFI_MEMORY_DESCRIPTOR * mUefiMemoryMap ;
UINTN mUefiMemoryMapSize ;
UINTN mUefiDescriptorSize ;
2017-11-02 12:00:02 +01:00
EFI_GCD_MEMORY_SPACE_DESCRIPTOR * mGcdMemSpace = NULL ;
UINTN mGcdMemNumberOfDesc = 0 ;
2017-11-22 16:11:54 +01:00
EFI_MEMORY_ATTRIBUTES_TABLE * mUefiMemoryAttributesTable = NULL ;
2023-06-07 09:46:58 +02:00
BOOLEAN mIsShadowStack = FALSE ;
BOOLEAN m5LevelPagingNeeded = FALSE ;
PAGING_MODE mPagingMode = PagingModeMax ;
UefiCpuPkg/PiSmmCpu: Add Shadow Stack Support for X86 SMM.
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1521
We scan the SMM code with ROPgadget.
http://shell-storm.org/project/ROPgadget/
https://github.com/JonathanSalwan/ROPgadget/tree/master
This tool reports the gadget in SMM driver.
This patch enabled CET ShadowStack for X86 SMM.
If CET is supported, SMM will enable CET ShadowStack.
SMM CET will save the OS CET context at SmmEntry and
restore OS CET context at SmmExit.
Test:
1) test Intel internal platform (x64 only, CET enabled/disabled)
Boot test:
CET supported or not supported CPU
on CET supported platform
CET enabled/disabled
PcdCpuSmmCetEnable enabled/disabled
Single core/Multiple core
PcdCpuSmmStackGuard enabled/disabled
PcdCpuSmmProfileEnable enabled/disabled
PcdCpuSmmStaticPageTable enabled/disabled
CET exception test:
#CF generated with PcdCpuSmmStackGuard enabled/disabled.
Other exception test:
#PF for normal stack overflow
#PF for NX protection
#PF for RO protection
CET env test:
Launch SMM in CET enabled/disabled environment (DXE) - no impact to DXE
The test case can be found at
https://github.com/jyao1/SecurityEx/tree/master/ControlFlowPkg
2) test ovmf (both IA32 and X64 SMM, CET disabled only)
test OvmfIa32/Ovmf3264, with -D SMM_REQUIRE.
qemu-system-x86_64.exe -machine q35,smm=on -smp 4
-serial file:serial.log
-drive if=pflash,format=raw,unit=0,file=OVMF_CODE.fd,readonly=on
-drive if=pflash,format=raw,unit=1,file=OVMF_VARS.fd
QEMU emulator version 3.1.0 (v3.1.0-11736-g7a30e7adb0-dirty)
3) not tested
IA32 CET enabled platform
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Yao Jiewen <jiewen.yao@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
2019-02-22 14:30:36 +01:00
2022-12-21 05:21:54 +01:00
//
// 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
)
{
2023-01-03 06:56:17 +01:00
VOID * Buffer ;
BOOLEAN CetEnabled ;
BOOLEAN WpEnabled ;
IA32_CR0 Cr0 ;
2022-12-21 05:21:54 +01:00
//
// 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 ;
2023-01-03 06:56:17 +01:00
Cr0 . UintN = AsmReadCr0 ( ) ;
WpEnabled = ( Cr0 . Bits . WP ! = 0 ) ? TRUE : FALSE ;
if ( WpEnabled ) {
if ( CetEnabled ) {
//
// CET must be disabled if WP is disabled. Disable CET before clearing CR0.WP.
//
DisableCet ( ) ;
}
Cr0 . Bits . WP = 0 ;
AsmWriteCr0 ( Cr0 . UintN ) ;
2022-12-21 05:21:54 +01:00
}
SmmSetMemoryAttributes ( ( EFI_PHYSICAL_ADDRESS ) ( UINTN ) Buffer , EFI_PAGES_TO_SIZE ( PoolPages ) , EFI_MEMORY_RO ) ;
2023-01-03 06:56:17 +01:00
if ( WpEnabled ) {
Cr0 . UintN = AsmReadCr0 ( ) ;
Cr0 . Bits . WP = 1 ;
AsmWriteCr0 ( Cr0 . UintN ) ;
if ( CetEnabled ) {
//
// re-enable CET.
//
EnableCet ( ) ;
}
2022-12-21 05:21:54 +01:00
}
}
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 4 KB pages of type EfiRuntimeServicesData and returns a pointer to the
allocated buffer . The buffer returned is aligned on a 4 KB 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 ;
}
2016-10-23 17:19:52 +02:00
/**
Return page table entry to match the address .
2022-08-09 09:58:28 +02:00
@ param [ in ] PageTableBase The page table base .
@ param [ in ] Enable5LevelPaging If PML5 paging is enabled .
@ param [ in ] Address The address to be checked .
@ param [ out ] PageAttributes The page attribute of the page entry .
2016-10-23 17:19:52 +02:00
@ return The page entry .
* */
VOID *
GetPageTableEntry (
2022-08-09 09:58:28 +02:00
IN UINTN PageTableBase ,
IN BOOLEAN Enable5LevelPaging ,
2016-10-23 17:19:52 +02:00
IN PHYSICAL_ADDRESS Address ,
OUT PAGE_ATTRIBUTE * PageAttribute
)
{
2022-08-09 09:58:28 +02:00
UINTN Index1 ;
UINTN Index2 ;
UINTN Index3 ;
UINTN Index4 ;
UINTN Index5 ;
UINT64 * L1PageTable ;
UINT64 * L2PageTable ;
UINT64 * L3PageTable ;
UINT64 * L4PageTable ;
UINT64 * L5PageTable ;
2020-11-09 06:16:57 +01:00
2019-06-12 11:26:45 +02:00
Index5 = ( ( UINTN ) RShiftU64 ( Address , 48 ) ) & PAGING_PAE_INDEX_MASK ;
2016-10-23 17:19:52 +02:00
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 ) ) {
2019-06-12 11:26:45 +02:00
if ( Enable5LevelPaging ) {
2020-11-09 06:16:57 +01:00
L5PageTable = ( UINT64 * ) PageTableBase ;
2019-06-12 11:26:45 +02:00
if ( L5PageTable [ Index5 ] = = 0 ) {
* PageAttribute = PageNone ;
return NULL ;
}
L4PageTable = ( UINT64 * ) ( UINTN ) ( L5PageTable [ Index5 ] & ~ mAddressEncMask & PAGING_4K_ADDRESS_MASK_64 ) ;
} else {
2020-11-09 06:16:57 +01:00
L4PageTable = ( UINT64 * ) PageTableBase ;
2019-06-12 11:26:45 +02:00
}
2021-12-05 23:54:17 +01:00
2016-10-23 17:19:52 +02:00
if ( L4PageTable [ Index4 ] = = 0 ) {
* PageAttribute = PageNone ;
return NULL ;
}
2017-02-26 18:43:07 +01:00
L3PageTable = ( UINT64 * ) ( UINTN ) ( L4PageTable [ Index4 ] & ~ mAddressEncMask & PAGING_4K_ADDRESS_MASK_64 ) ;
2016-10-23 17:19:52 +02:00
} else {
2020-11-09 06:16:57 +01:00
L3PageTable = ( UINT64 * ) PageTableBase ;
2016-10-23 17:19:52 +02:00
}
2021-12-05 23:54:17 +01:00
2016-10-23 17:19:52 +02:00
if ( L3PageTable [ Index3 ] = = 0 ) {
* PageAttribute = PageNone ;
return NULL ;
}
2021-12-05 23:54:17 +01:00
2016-10-23 17:19:52 +02:00
if ( ( L3PageTable [ Index3 ] & IA32_PG_PS ) ! = 0 ) {
// 1G
* PageAttribute = Page1G ;
return & L3PageTable [ Index3 ] ;
}
2017-02-26 18:43:07 +01:00
L2PageTable = ( UINT64 * ) ( UINTN ) ( L3PageTable [ Index3 ] & ~ mAddressEncMask & PAGING_4K_ADDRESS_MASK_64 ) ;
2016-10-23 17:19:52 +02:00
if ( L2PageTable [ Index2 ] = = 0 ) {
* PageAttribute = PageNone ;
return NULL ;
}
2021-12-05 23:54:17 +01:00
2016-10-23 17:19:52 +02:00
if ( ( L2PageTable [ Index2 ] & IA32_PG_PS ) ! = 0 ) {
// 2M
* PageAttribute = Page2M ;
return & L2PageTable [ Index2 ] ;
}
// 4k
2017-02-26 18:43:07 +01:00
L1PageTable = ( UINT64 * ) ( UINTN ) ( L2PageTable [ Index2 ] & ~ mAddressEncMask & PAGING_4K_ADDRESS_MASK_64 ) ;
2016-10-23 17:19:52 +02:00
if ( ( L1PageTable [ Index1 ] = = 0 ) & & ( Address ! = 0 ) ) {
* PageAttribute = PageNone ;
return NULL ;
}
2021-12-05 23:54:17 +01:00
2016-10-23 17:19:52 +02:00
* PageAttribute = Page4K ;
return & L1PageTable [ Index1 ] ;
}
/**
Return memory attributes of page entry .
@ param [ in ] PageEntry The page entry .
@ return Memory attributes of page entry .
* */
UINT64
GetAttributesFromPageEntry (
IN UINT64 * PageEntry
)
{
UINT64 Attributes ;
2021-12-05 23:54:17 +01:00
2016-10-23 17:19:52 +02:00
Attributes = 0 ;
if ( ( * PageEntry & IA32_PG_P ) = = 0 ) {
Attributes | = EFI_MEMORY_RP ;
}
2021-12-05 23:54:17 +01:00
2016-10-23 17:19:52 +02:00
if ( ( * PageEntry & IA32_PG_RW ) = = 0 ) {
Attributes | = EFI_MEMORY_RO ;
}
2021-12-05 23:54:17 +01:00
2016-10-23 17:19:52 +02:00
if ( ( * PageEntry & IA32_PG_NX ) ! = 0 ) {
Attributes | = EFI_MEMORY_XP ;
}
2021-12-05 23:54:17 +01:00
2016-10-23 17:19:52 +02:00
return Attributes ;
}
/**
This function modifies the page attributes for the memory region specified by BaseAddress and
Length from their current attributes to the attributes specified by Attributes .
Caller should make sure BaseAddress and Length is at page boundary .
2022-08-09 09:58:28 +02:00
@ param [ in ] PageTableBase The page table base .
2023-06-07 09:46:58 +02:00
@ param [ in ] PagingMode The paging mode .
2016-10-23 17:19:52 +02:00
@ param [ in ] BaseAddress The physical address that is the start address of a memory region .
@ param [ in ] Length The size in bytes of the memory region .
@ param [ in ] Attributes The bit mask of attributes to modify for the memory region .
@ param [ in ] IsSet TRUE means to set attributes . FALSE means to clear attributes .
@ param [ out ] IsModified TRUE means page table modified . FALSE means page table not modified .
@ retval RETURN_SUCCESS The attributes were modified for the memory region .
@ retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
BaseAddress and Length cannot be modified .
@ retval RETURN_INVALID_PARAMETER Length is zero .
Attributes specified an illegal combination of attributes that
cannot be set together .
@ retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
the memory resource range .
@ retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
resource range specified by BaseAddress and Length .
The bit mask of attributes is not support for the memory resource
range specified by BaseAddress and Length .
* */
RETURN_STATUS
ConvertMemoryPageAttributes (
2022-08-09 09:58:28 +02:00
IN UINTN PageTableBase ,
2023-06-07 09:46:58 +02:00
IN PAGING_MODE PagingMode ,
2016-10-23 17:19:52 +02:00
IN PHYSICAL_ADDRESS BaseAddress ,
IN UINT64 Length ,
IN UINT64 Attributes ,
IN BOOLEAN IsSet ,
OUT BOOLEAN * IsModified OPTIONAL
)
{
RETURN_STATUS Status ;
2023-06-07 09:46:58 +02:00
IA32_MAP_ATTRIBUTE PagingAttribute ;
IA32_MAP_ATTRIBUTE PagingAttrMask ;
UINTN PageTableBufferSize ;
VOID * PageTableBuffer ;
2017-08-24 04:59:14 +02:00
EFI_PHYSICAL_ADDRESS MaximumSupportMemAddress ;
2023-06-07 09:46:58 +02:00
IA32_MAP_ENTRY * Map ;
UINTN Count ;
UINTN Index ;
2016-10-23 17:19:52 +02:00
ASSERT ( Attributes ! = 0 ) ;
2020-07-02 22:50:39 +02:00
ASSERT ( ( Attributes & ~ EFI_MEMORY_ATTRIBUTE_MASK ) = = 0 ) ;
2016-10-23 17:19:52 +02:00
ASSERT ( ( BaseAddress & ( SIZE_4KB - 1 ) ) = = 0 ) ;
ASSERT ( ( Length & ( SIZE_4KB - 1 ) ) = = 0 ) ;
2023-06-07 09:46:58 +02:00
ASSERT ( PageTableBase ! = 0 ) ;
2016-10-23 17:19:52 +02:00
if ( Length = = 0 ) {
return RETURN_INVALID_PARAMETER ;
}
2017-08-24 04:59:14 +02:00
MaximumSupportMemAddress = ( EFI_PHYSICAL_ADDRESS ) ( UINTN ) ( LShiftU64 ( 1 , mPhysicalAddressBits ) - 1 ) ;
if ( BaseAddress > MaximumSupportMemAddress ) {
return RETURN_UNSUPPORTED ;
}
2021-12-05 23:54:17 +01:00
2017-08-24 04:59:14 +02:00
if ( Length > MaximumSupportMemAddress ) {
return RETURN_UNSUPPORTED ;
}
2021-12-05 23:54:17 +01:00
2017-08-24 04:59:14 +02:00
if ( ( Length ! = 0 ) & & ( BaseAddress > MaximumSupportMemAddress - ( Length - 1 ) ) ) {
return RETURN_UNSUPPORTED ;
}
2016-10-23 17:19:52 +02:00
if ( IsModified ! = NULL ) {
* IsModified = FALSE ;
}
2023-06-07 09:46:58 +02:00
PagingAttribute . Uint64 = 0 ;
PagingAttribute . Uint64 = mAddressEncMask | BaseAddress ;
PagingAttrMask . Uint64 = 0 ;
2021-12-05 23:54:17 +01:00
2023-06-07 09:46:58 +02:00
if ( ( Attributes & EFI_MEMORY_RO ) ! = 0 ) {
PagingAttrMask . Bits . ReadWrite = 1 ;
if ( IsSet ) {
PagingAttribute . Bits . ReadWrite = 0 ;
PagingAttrMask . Bits . Dirty = 1 ;
if ( mIsShadowStack ) {
// Environment setup
// ReadOnly page need set Dirty bit for shadow stack
PagingAttribute . Bits . Dirty = 1 ;
// Clear user bit for supervisor shadow stack
PagingAttribute . Bits . UserSupervisor = 0 ;
PagingAttrMask . Bits . UserSupervisor = 1 ;
} else {
// Runtime update
// Clear dirty bit for non shadow stack, to protect RO page.
PagingAttribute . Bits . Dirty = 0 ;
2016-10-23 17:19:52 +02:00
}
2023-06-07 09:46:58 +02:00
} else {
PagingAttribute . Bits . ReadWrite = 1 ;
}
}
2021-12-05 23:54:17 +01:00
2023-06-07 09:46:58 +02:00
if ( ( Attributes & EFI_MEMORY_XP ) ! = 0 ) {
if ( mXdSupported ) {
PagingAttribute . Bits . Nx = IsSet ? 1 : 0 ;
PagingAttrMask . Bits . Nx = 1 ;
}
}
if ( ( Attributes & EFI_MEMORY_RP ) ! = 0 ) {
if ( IsSet ) {
PagingAttribute . Bits . Present = 0 ;
2016-10-23 17:19:52 +02:00
//
2023-06-07 09:46:58 +02:00
// When map a range to non-present, all attributes except Present should not be provided.
2016-10-23 17:19:52 +02:00
//
2023-06-07 09:46:58 +02:00
PagingAttrMask . Uint64 = 0 ;
PagingAttrMask . Bits . Present = 1 ;
2016-10-23 17:19:52 +02:00
} else {
2023-06-07 09:46:58 +02:00
//
// When map range to present range, provide all attributes.
//
PagingAttribute . Bits . Present = 1 ;
PagingAttrMask . Uint64 = MAX_UINT64 ;
2021-12-05 23:54:17 +01:00
2016-10-23 17:19:52 +02:00
//
2023-06-07 09:46:58 +02:00
// By default memory is Ring 3 accessble.
2016-10-23 17:19:52 +02:00
//
2023-06-07 09:46:58 +02:00
PagingAttribute . Bits . UserSupervisor = 1 ;
2016-10-23 17:19:52 +02:00
}
}
2023-06-07 09:46:58 +02:00
if ( PagingAttrMask . Uint64 = = 0 ) {
return RETURN_SUCCESS ;
}
PageTableBufferSize = 0 ;
Status = PageTableMap ( & PageTableBase , PagingMode , NULL , & PageTableBufferSize , BaseAddress , Length , & PagingAttribute , & PagingAttrMask , IsModified ) ;
if ( Status = = RETURN_BUFFER_TOO_SMALL ) {
PageTableBuffer = AllocatePageTableMemory ( EFI_SIZE_TO_PAGES ( PageTableBufferSize ) ) ;
ASSERT ( PageTableBuffer ! = NULL ) ;
Status = PageTableMap ( & PageTableBase , PagingMode , PageTableBuffer , & PageTableBufferSize , BaseAddress , Length , & PagingAttribute , & PagingAttrMask , IsModified ) ;
}
if ( Status = = RETURN_INVALID_PARAMETER ) {
//
// The only reason that PageTableMap returns RETURN_INVALID_PARAMETER here is to modify other attributes
// of a non-present range but remains the non-present range still as non-present.
//
DEBUG ( ( DEBUG_ERROR , " SMM ConvertMemoryPageAttributes: Only change EFI_MEMORY_XP/EFI_MEMORY_RO for non-present range in [0x%lx, 0x%lx] is not permitted \n " , BaseAddress , BaseAddress + Length ) ) ;
}
ASSERT_RETURN_ERROR ( Status ) ;
ASSERT ( PageTableBufferSize = = 0 ) ;
2016-10-23 17:19:52 +02:00
return RETURN_SUCCESS ;
}
/**
FlushTlb on current processor .
@ param [ in , out ] Buffer Pointer to private data buffer .
* */
VOID
EFIAPI
FlushTlbOnCurrentProcessor (
IN OUT VOID * Buffer
)
{
CpuFlushTlb ( ) ;
}
/**
FlushTlb for all processors .
* */
VOID
FlushTlbForAll (
VOID
)
{
UINTN Index ;
FlushTlbOnCurrentProcessor ( NULL ) ;
for ( Index = 0 ; Index < gSmst - > NumberOfCpus ; Index + + ) {
if ( Index ! = gSmst - > CurrentlyExecutingCpu ) {
// Force to start up AP in blocking mode,
SmmBlockingStartupThisAp ( FlushTlbOnCurrentProcessor , Index , NULL ) ;
// Do not check return status, because AP might not be present in some corner cases.
}
}
}
/**
This function sets the attributes for the memory region specified by BaseAddress and
Length from their current attributes to the attributes specified by Attributes .
2022-08-09 09:58:28 +02:00
@ param [ in ] PageTableBase The page table base .
2023-06-07 09:46:58 +02:00
@ param [ in ] PagingMode The paging mode .
2016-10-23 17:19:52 +02:00
@ param [ in ] BaseAddress The physical address that is the start address of a memory region .
@ param [ in ] Length The size in bytes of the memory region .
@ param [ in ] Attributes The bit mask of attributes to set for the memory region .
@ retval EFI_SUCCESS The attributes were set for the memory region .
@ retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
BaseAddress and Length cannot be modified .
@ retval EFI_INVALID_PARAMETER Length is zero .
Attributes specified an illegal combination of attributes that
cannot be set together .
@ retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
the memory resource range .
@ retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
resource range specified by BaseAddress and Length .
The bit mask of attributes is not support for the memory resource
range specified by BaseAddress and Length .
* */
EFI_STATUS
SmmSetMemoryAttributesEx (
2022-08-09 09:58:28 +02:00
IN UINTN PageTableBase ,
2023-06-07 09:46:58 +02:00
IN PAGING_MODE PagingMode ,
2016-10-23 17:19:52 +02:00
IN EFI_PHYSICAL_ADDRESS BaseAddress ,
IN UINT64 Length ,
2023-06-07 09:46:58 +02:00
IN UINT64 Attributes
2016-10-23 17:19:52 +02:00
)
{
EFI_STATUS Status ;
BOOLEAN IsModified ;
2023-06-07 09:46:58 +02:00
Status = ConvertMemoryPageAttributes ( PageTableBase , PagingMode , BaseAddress , Length , Attributes , TRUE , & IsModified ) ;
2016-10-23 17:19:52 +02:00
if ( ! EFI_ERROR ( Status ) ) {
if ( IsModified ) {
//
// Flush TLB as last step
//
FlushTlbForAll ( ) ;
}
}
return Status ;
}
/**
This function clears the attributes for the memory region specified by BaseAddress and
Length from their current attributes to the attributes specified by Attributes .
2022-08-09 09:58:28 +02:00
@ param [ in ] PageTableBase The page table base .
2023-06-07 09:46:58 +02:00
@ param [ in ] PagingMode The paging mode .
2016-10-23 17:19:52 +02:00
@ param [ in ] BaseAddress The physical address that is the start address of a memory region .
@ param [ in ] Length The size in bytes of the memory region .
@ param [ in ] Attributes The bit mask of attributes to clear for the memory region .
@ retval EFI_SUCCESS The attributes were cleared for the memory region .
@ retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
BaseAddress and Length cannot be modified .
@ retval EFI_INVALID_PARAMETER Length is zero .
Attributes specified an illegal combination of attributes that
2018-02-28 06:21:59 +01:00
cannot be cleared together .
2016-10-23 17:19:52 +02:00
@ retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
the memory resource range .
@ retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
resource range specified by BaseAddress and Length .
2018-02-28 06:21:59 +01:00
The bit mask of attributes is not supported for the memory resource
2016-10-23 17:19:52 +02:00
range specified by BaseAddress and Length .
* */
EFI_STATUS
SmmClearMemoryAttributesEx (
2022-08-09 09:58:28 +02:00
IN UINTN PageTableBase ,
2023-06-07 09:46:58 +02:00
IN PAGING_MODE PagingMode ,
2016-10-23 17:19:52 +02:00
IN EFI_PHYSICAL_ADDRESS BaseAddress ,
IN UINT64 Length ,
2023-06-07 09:46:58 +02:00
IN UINT64 Attributes
2016-10-23 17:19:52 +02:00
)
{
EFI_STATUS Status ;
BOOLEAN IsModified ;
2023-06-07 09:46:58 +02:00
Status = ConvertMemoryPageAttributes ( PageTableBase , PagingMode , BaseAddress , Length , Attributes , FALSE , & IsModified ) ;
2016-10-23 17:19:52 +02:00
if ( ! EFI_ERROR ( Status ) ) {
if ( IsModified ) {
//
// Flush TLB as last step
//
FlushTlbForAll ( ) ;
}
}
return Status ;
}
/**
This function sets the attributes for the memory region specified by BaseAddress and
Length from their current attributes to the attributes specified by Attributes .
@ param [ in ] BaseAddress The physical address that is the start address of a memory region .
@ param [ in ] Length The size in bytes of the memory region .
@ param [ in ] Attributes The bit mask of attributes to set for the memory region .
@ retval EFI_SUCCESS The attributes were set for the memory region .
@ retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
BaseAddress and Length cannot be modified .
@ retval EFI_INVALID_PARAMETER Length is zero .
Attributes specified an illegal combination of attributes that
cannot be set together .
@ retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
the memory resource range .
@ retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
resource range specified by BaseAddress and Length .
2018-02-28 06:21:59 +01:00
The bit mask of attributes is not supported for the memory resource
2016-10-23 17:19:52 +02:00
range specified by BaseAddress and Length .
* */
EFI_STATUS
SmmSetMemoryAttributes (
IN EFI_PHYSICAL_ADDRESS BaseAddress ,
IN UINT64 Length ,
IN UINT64 Attributes
)
{
2023-06-07 09:46:58 +02:00
UINTN PageTableBase ;
PageTableBase = AsmReadCr3 ( ) & PAGING_4K_ADDRESS_MASK_64 ;
return SmmSetMemoryAttributesEx ( PageTableBase , mPagingMode , BaseAddress , Length , Attributes ) ;
2016-10-23 17:19:52 +02:00
}
/**
This function clears the attributes for the memory region specified by BaseAddress and
Length from their current attributes to the attributes specified by Attributes .
@ param [ in ] BaseAddress The physical address that is the start address of a memory region .
@ param [ in ] Length The size in bytes of the memory region .
@ param [ in ] Attributes The bit mask of attributes to clear for the memory region .
@ retval EFI_SUCCESS The attributes were cleared for the memory region .
@ retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
BaseAddress and Length cannot be modified .
@ retval EFI_INVALID_PARAMETER Length is zero .
Attributes specified an illegal combination of attributes that
2018-02-28 06:21:59 +01:00
cannot be cleared together .
2016-10-23 17:19:52 +02:00
@ retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
the memory resource range .
@ retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
resource range specified by BaseAddress and Length .
2018-02-28 06:21:59 +01:00
The bit mask of attributes is not supported for the memory resource
2016-10-23 17:19:52 +02:00
range specified by BaseAddress and Length .
* */
EFI_STATUS
SmmClearMemoryAttributes (
IN EFI_PHYSICAL_ADDRESS BaseAddress ,
IN UINT64 Length ,
IN UINT64 Attributes
)
{
2023-06-07 09:46:58 +02:00
UINTN PageTableBase ;
PageTableBase = AsmReadCr3 ( ) & PAGING_4K_ADDRESS_MASK_64 ;
return SmmClearMemoryAttributesEx ( PageTableBase , mPagingMode , BaseAddress , Length , Attributes ) ;
2016-10-23 17:19:52 +02:00
}
UefiCpuPkg/PiSmmCpu: Add Shadow Stack Support for X86 SMM.
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1521
We scan the SMM code with ROPgadget.
http://shell-storm.org/project/ROPgadget/
https://github.com/JonathanSalwan/ROPgadget/tree/master
This tool reports the gadget in SMM driver.
This patch enabled CET ShadowStack for X86 SMM.
If CET is supported, SMM will enable CET ShadowStack.
SMM CET will save the OS CET context at SmmEntry and
restore OS CET context at SmmExit.
Test:
1) test Intel internal platform (x64 only, CET enabled/disabled)
Boot test:
CET supported or not supported CPU
on CET supported platform
CET enabled/disabled
PcdCpuSmmCetEnable enabled/disabled
Single core/Multiple core
PcdCpuSmmStackGuard enabled/disabled
PcdCpuSmmProfileEnable enabled/disabled
PcdCpuSmmStaticPageTable enabled/disabled
CET exception test:
#CF generated with PcdCpuSmmStackGuard enabled/disabled.
Other exception test:
#PF for normal stack overflow
#PF for NX protection
#PF for RO protection
CET env test:
Launch SMM in CET enabled/disabled environment (DXE) - no impact to DXE
The test case can be found at
https://github.com/jyao1/SecurityEx/tree/master/ControlFlowPkg
2) test ovmf (both IA32 and X64 SMM, CET disabled only)
test OvmfIa32/Ovmf3264, with -D SMM_REQUIRE.
qemu-system-x86_64.exe -machine q35,smm=on -smp 4
-serial file:serial.log
-drive if=pflash,format=raw,unit=0,file=OVMF_CODE.fd,readonly=on
-drive if=pflash,format=raw,unit=1,file=OVMF_VARS.fd
QEMU emulator version 3.1.0 (v3.1.0-11736-g7a30e7adb0-dirty)
3) not tested
IA32 CET enabled platform
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Yao Jiewen <jiewen.yao@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
2019-02-22 14:30:36 +01:00
/**
Set ShadowStack memory .
@ param [ in ] Cr3 The page table base address .
@ param [ in ] BaseAddress The physical address that is the start address of a memory region .
@ param [ in ] Length The size in bytes of the memory region .
@ retval EFI_SUCCESS The shadow stack memory is set .
* */
EFI_STATUS
SetShadowStack (
IN UINTN Cr3 ,
IN EFI_PHYSICAL_ADDRESS BaseAddress ,
IN UINT64 Length
)
{
EFI_STATUS Status ;
2022-08-09 09:22:07 +02:00
mIsShadowStack = TRUE ;
2023-06-07 09:46:58 +02:00
Status = SmmSetMemoryAttributesEx ( Cr3 , mPagingMode , BaseAddress , Length , EFI_MEMORY_RO ) ;
2022-08-09 09:22:07 +02:00
mIsShadowStack = FALSE ;
UefiCpuPkg/PiSmmCpu: Add Shadow Stack Support for X86 SMM.
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1521
We scan the SMM code with ROPgadget.
http://shell-storm.org/project/ROPgadget/
https://github.com/JonathanSalwan/ROPgadget/tree/master
This tool reports the gadget in SMM driver.
This patch enabled CET ShadowStack for X86 SMM.
If CET is supported, SMM will enable CET ShadowStack.
SMM CET will save the OS CET context at SmmEntry and
restore OS CET context at SmmExit.
Test:
1) test Intel internal platform (x64 only, CET enabled/disabled)
Boot test:
CET supported or not supported CPU
on CET supported platform
CET enabled/disabled
PcdCpuSmmCetEnable enabled/disabled
Single core/Multiple core
PcdCpuSmmStackGuard enabled/disabled
PcdCpuSmmProfileEnable enabled/disabled
PcdCpuSmmStaticPageTable enabled/disabled
CET exception test:
#CF generated with PcdCpuSmmStackGuard enabled/disabled.
Other exception test:
#PF for normal stack overflow
#PF for NX protection
#PF for RO protection
CET env test:
Launch SMM in CET enabled/disabled environment (DXE) - no impact to DXE
The test case can be found at
https://github.com/jyao1/SecurityEx/tree/master/ControlFlowPkg
2) test ovmf (both IA32 and X64 SMM, CET disabled only)
test OvmfIa32/Ovmf3264, with -D SMM_REQUIRE.
qemu-system-x86_64.exe -machine q35,smm=on -smp 4
-serial file:serial.log
-drive if=pflash,format=raw,unit=0,file=OVMF_CODE.fd,readonly=on
-drive if=pflash,format=raw,unit=1,file=OVMF_VARS.fd
QEMU emulator version 3.1.0 (v3.1.0-11736-g7a30e7adb0-dirty)
3) not tested
IA32 CET enabled platform
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Yao Jiewen <jiewen.yao@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
2019-02-22 14:30:36 +01:00
return Status ;
}
/**
Set not present memory .
@ param [ in ] Cr3 The page table base address .
@ param [ in ] BaseAddress The physical address that is the start address of a memory region .
@ param [ in ] Length The size in bytes of the memory region .
2016-10-23 17:19:52 +02:00
UefiCpuPkg/PiSmmCpu: Add Shadow Stack Support for X86 SMM.
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1521
We scan the SMM code with ROPgadget.
http://shell-storm.org/project/ROPgadget/
https://github.com/JonathanSalwan/ROPgadget/tree/master
This tool reports the gadget in SMM driver.
This patch enabled CET ShadowStack for X86 SMM.
If CET is supported, SMM will enable CET ShadowStack.
SMM CET will save the OS CET context at SmmEntry and
restore OS CET context at SmmExit.
Test:
1) test Intel internal platform (x64 only, CET enabled/disabled)
Boot test:
CET supported or not supported CPU
on CET supported platform
CET enabled/disabled
PcdCpuSmmCetEnable enabled/disabled
Single core/Multiple core
PcdCpuSmmStackGuard enabled/disabled
PcdCpuSmmProfileEnable enabled/disabled
PcdCpuSmmStaticPageTable enabled/disabled
CET exception test:
#CF generated with PcdCpuSmmStackGuard enabled/disabled.
Other exception test:
#PF for normal stack overflow
#PF for NX protection
#PF for RO protection
CET env test:
Launch SMM in CET enabled/disabled environment (DXE) - no impact to DXE
The test case can be found at
https://github.com/jyao1/SecurityEx/tree/master/ControlFlowPkg
2) test ovmf (both IA32 and X64 SMM, CET disabled only)
test OvmfIa32/Ovmf3264, with -D SMM_REQUIRE.
qemu-system-x86_64.exe -machine q35,smm=on -smp 4
-serial file:serial.log
-drive if=pflash,format=raw,unit=0,file=OVMF_CODE.fd,readonly=on
-drive if=pflash,format=raw,unit=1,file=OVMF_VARS.fd
QEMU emulator version 3.1.0 (v3.1.0-11736-g7a30e7adb0-dirty)
3) not tested
IA32 CET enabled platform
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Yao Jiewen <jiewen.yao@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
2019-02-22 14:30:36 +01:00
@ retval EFI_SUCCESS The not present memory is set .
* */
EFI_STATUS
SetNotPresentPage (
IN UINTN Cr3 ,
IN EFI_PHYSICAL_ADDRESS BaseAddress ,
IN UINT64 Length
)
{
EFI_STATUS Status ;
2023-06-07 09:46:58 +02:00
Status = SmmSetMemoryAttributesEx ( Cr3 , mPagingMode , BaseAddress , Length , EFI_MEMORY_RP ) ;
UefiCpuPkg/PiSmmCpu: Add Shadow Stack Support for X86 SMM.
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1521
We scan the SMM code with ROPgadget.
http://shell-storm.org/project/ROPgadget/
https://github.com/JonathanSalwan/ROPgadget/tree/master
This tool reports the gadget in SMM driver.
This patch enabled CET ShadowStack for X86 SMM.
If CET is supported, SMM will enable CET ShadowStack.
SMM CET will save the OS CET context at SmmEntry and
restore OS CET context at SmmExit.
Test:
1) test Intel internal platform (x64 only, CET enabled/disabled)
Boot test:
CET supported or not supported CPU
on CET supported platform
CET enabled/disabled
PcdCpuSmmCetEnable enabled/disabled
Single core/Multiple core
PcdCpuSmmStackGuard enabled/disabled
PcdCpuSmmProfileEnable enabled/disabled
PcdCpuSmmStaticPageTable enabled/disabled
CET exception test:
#CF generated with PcdCpuSmmStackGuard enabled/disabled.
Other exception test:
#PF for normal stack overflow
#PF for NX protection
#PF for RO protection
CET env test:
Launch SMM in CET enabled/disabled environment (DXE) - no impact to DXE
The test case can be found at
https://github.com/jyao1/SecurityEx/tree/master/ControlFlowPkg
2) test ovmf (both IA32 and X64 SMM, CET disabled only)
test OvmfIa32/Ovmf3264, with -D SMM_REQUIRE.
qemu-system-x86_64.exe -machine q35,smm=on -smp 4
-serial file:serial.log
-drive if=pflash,format=raw,unit=0,file=OVMF_CODE.fd,readonly=on
-drive if=pflash,format=raw,unit=1,file=OVMF_VARS.fd
QEMU emulator version 3.1.0 (v3.1.0-11736-g7a30e7adb0-dirty)
3) not tested
IA32 CET enabled platform
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Yao Jiewen <jiewen.yao@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
2019-02-22 14:30:36 +01:00
return Status ;
}
2016-10-23 17:19:52 +02:00
/**
Retrieves a pointer to the system configuration table from the SMM System Table
based on a specified GUID .
@ param [ in ] TableGuid The pointer to table ' s GUID type .
@ param [ out ] Table The pointer to the table associated with TableGuid in the EFI System Table .
@ retval EFI_SUCCESS A configuration table matching TableGuid was found .
@ retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found .
* */
EFI_STATUS
EFIAPI
SmmGetSystemConfigurationTable (
IN EFI_GUID * TableGuid ,
OUT VOID * * Table
)
{
UINTN Index ;
ASSERT ( TableGuid ! = NULL ) ;
ASSERT ( Table ! = NULL ) ;
* Table = NULL ;
for ( Index = 0 ; Index < gSmst - > NumberOfTableEntries ; Index + + ) {
if ( CompareGuid ( TableGuid , & ( gSmst - > SmmConfigurationTable [ Index ] . VendorGuid ) ) ) {
* Table = gSmst - > SmmConfigurationTable [ Index ] . VendorTable ;
return EFI_SUCCESS ;
}
}
return EFI_NOT_FOUND ;
}
/**
This function sets SMM save state buffer to be RW and XP .
* */
VOID
PatchSmmSaveStateMap (
VOID
)
{
UINTN Index ;
UINTN TileCodeSize ;
UINTN TileDataSize ;
UINTN TileSize ;
TileCodeSize = GetSmiHandlerSize ( ) ;
TileCodeSize = ALIGN_VALUE ( TileCodeSize , SIZE_4KB ) ;
2016-11-28 23:13:24 +01:00
TileDataSize = ( SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET ) + sizeof ( SMRAM_SAVE_STATE_MAP ) ;
2016-10-23 17:19:52 +02:00
TileDataSize = ALIGN_VALUE ( TileDataSize , SIZE_4KB ) ;
TileSize = TileDataSize + TileCodeSize - 1 ;
TileSize = 2 * GetPowerOfTwo32 ( ( UINT32 ) TileSize ) ;
DEBUG ( ( DEBUG_INFO , " PatchSmmSaveStateMap: \n " ) ) ;
for ( Index = 0 ; Index < mMaxNumberOfCpus - 1 ; Index + + ) {
//
// Code
//
SmmSetMemoryAttributes (
mCpuHotPlugData . SmBase [ Index ] + SMM_HANDLER_OFFSET ,
TileCodeSize ,
EFI_MEMORY_RO
) ;
SmmClearMemoryAttributes (
mCpuHotPlugData . SmBase [ Index ] + SMM_HANDLER_OFFSET ,
TileCodeSize ,
EFI_MEMORY_XP
) ;
//
// Data
//
SmmClearMemoryAttributes (
mCpuHotPlugData . SmBase [ Index ] + SMM_HANDLER_OFFSET + TileCodeSize ,
TileSize - TileCodeSize ,
EFI_MEMORY_RO
) ;
SmmSetMemoryAttributes (
mCpuHotPlugData . SmBase [ Index ] + SMM_HANDLER_OFFSET + TileCodeSize ,
TileSize - TileCodeSize ,
EFI_MEMORY_XP
) ;
}
//
// Code
//
SmmSetMemoryAttributes (
mCpuHotPlugData . SmBase [ mMaxNumberOfCpus - 1 ] + SMM_HANDLER_OFFSET ,
TileCodeSize ,
EFI_MEMORY_RO
) ;
SmmClearMemoryAttributes (
mCpuHotPlugData . SmBase [ mMaxNumberOfCpus - 1 ] + SMM_HANDLER_OFFSET ,
TileCodeSize ,
EFI_MEMORY_XP
) ;
//
// Data
//
SmmClearMemoryAttributes (
mCpuHotPlugData . SmBase [ mMaxNumberOfCpus - 1 ] + SMM_HANDLER_OFFSET + TileCodeSize ,
SIZE_32KB - TileCodeSize ,
EFI_MEMORY_RO
) ;
SmmSetMemoryAttributes (
mCpuHotPlugData . SmBase [ mMaxNumberOfCpus - 1 ] + SMM_HANDLER_OFFSET + TileCodeSize ,
SIZE_32KB - TileCodeSize ,
EFI_MEMORY_XP
) ;
}
2018-01-11 11:27:12 +01:00
/**
This function sets GDT / IDT buffer to be RO and XP .
* */
VOID
PatchGdtIdtMap (
VOID
)
{
EFI_PHYSICAL_ADDRESS BaseAddress ;
UINTN Size ;
//
// GDT
//
DEBUG ( ( DEBUG_INFO , " PatchGdtIdtMap - GDT: \n " ) ) ;
BaseAddress = mGdtBuffer ;
Size = ALIGN_VALUE ( mGdtBufferSize , SIZE_4KB ) ;
//
// The range should have been set to RO
// if it is allocated with EfiRuntimeServicesCode.
//
SmmSetMemoryAttributes (
BaseAddress ,
Size ,
EFI_MEMORY_XP
) ;
//
// IDT
//
DEBUG ( ( DEBUG_INFO , " PatchGdtIdtMap - IDT: \n " ) ) ;
BaseAddress = gcSmiIdtr . Base ;
Size = ALIGN_VALUE ( gcSmiIdtr . Limit + 1 , SIZE_4KB ) ;
2018-01-12 05:59:03 +01:00
//
// The range should have been set to RO
// if it is allocated with EfiRuntimeServicesCode.
//
2018-01-11 11:27:12 +01:00
SmmSetMemoryAttributes (
BaseAddress ,
Size ,
EFI_MEMORY_XP
) ;
}
2016-10-23 17:19:52 +02:00
/**
This function sets memory attribute according to MemoryAttributesTable .
* */
VOID
SetMemMapAttributes (
VOID
)
{
EFI_MEMORY_DESCRIPTOR * MemoryMap ;
EFI_MEMORY_DESCRIPTOR * MemoryMapStart ;
UINTN MemoryMapEntryCount ;
UINTN DescriptorSize ;
UINTN Index ;
EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE * MemoryAttributesTable ;
SmmGetSystemConfigurationTable ( & gEdkiiPiSmmMemoryAttributesTableGuid , ( VOID * * ) & MemoryAttributesTable ) ;
if ( MemoryAttributesTable = = NULL ) {
DEBUG ( ( DEBUG_INFO , " MemoryAttributesTable - NULL \n " ) ) ;
return ;
}
2023-05-26 14:57:29 +02:00
PERF_FUNCTION_BEGIN ( ) ;
2016-10-23 17:19:52 +02:00
DEBUG ( ( DEBUG_INFO , " MemoryAttributesTable: \n " ) ) ;
DEBUG ( ( DEBUG_INFO , " Version - 0x%08x \n " , MemoryAttributesTable - > Version ) ) ;
DEBUG ( ( DEBUG_INFO , " NumberOfEntries - 0x%08x \n " , MemoryAttributesTable - > NumberOfEntries ) ) ;
DEBUG ( ( DEBUG_INFO , " DescriptorSize - 0x%08x \n " , MemoryAttributesTable - > DescriptorSize ) ) ;
MemoryMapEntryCount = MemoryAttributesTable - > NumberOfEntries ;
DescriptorSize = MemoryAttributesTable - > DescriptorSize ;
MemoryMapStart = ( EFI_MEMORY_DESCRIPTOR * ) ( MemoryAttributesTable + 1 ) ;
MemoryMap = MemoryMapStart ;
for ( Index = 0 ; Index < MemoryMapEntryCount ; Index + + ) {
DEBUG ( ( DEBUG_INFO , " Entry (0x%x) \n " , MemoryMap ) ) ;
DEBUG ( ( DEBUG_INFO , " Type - 0x%x \n " , MemoryMap - > Type ) ) ;
DEBUG ( ( DEBUG_INFO , " PhysicalStart - 0x%016lx \n " , MemoryMap - > PhysicalStart ) ) ;
DEBUG ( ( DEBUG_INFO , " VirtualStart - 0x%016lx \n " , MemoryMap - > VirtualStart ) ) ;
DEBUG ( ( DEBUG_INFO , " NumberOfPages - 0x%016lx \n " , MemoryMap - > NumberOfPages ) ) ;
DEBUG ( ( DEBUG_INFO , " Attribute - 0x%016lx \n " , MemoryMap - > Attribute ) ) ;
MemoryMap = NEXT_MEMORY_DESCRIPTOR ( MemoryMap , DescriptorSize ) ;
}
MemoryMap = MemoryMapStart ;
for ( Index = 0 ; Index < MemoryMapEntryCount ; Index + + ) {
DEBUG ( ( DEBUG_VERBOSE , " SetAttribute: Memory Entry - 0x%lx, 0x%x \n " , MemoryMap - > PhysicalStart , MemoryMap - > NumberOfPages ) ) ;
switch ( MemoryMap - > Type ) {
case EfiRuntimeServicesCode :
SmmSetMemoryAttributes (
MemoryMap - > PhysicalStart ,
EFI_PAGES_TO_SIZE ( ( UINTN ) MemoryMap - > NumberOfPages ) ,
EFI_MEMORY_RO
) ;
break ;
case EfiRuntimeServicesData :
SmmSetMemoryAttributes (
MemoryMap - > PhysicalStart ,
EFI_PAGES_TO_SIZE ( ( UINTN ) MemoryMap - > NumberOfPages ) ,
EFI_MEMORY_XP
) ;
break ;
default :
SmmSetMemoryAttributes (
MemoryMap - > PhysicalStart ,
EFI_PAGES_TO_SIZE ( ( UINTN ) MemoryMap - > NumberOfPages ) ,
EFI_MEMORY_XP
) ;
break ;
}
2021-12-05 23:54:17 +01:00
2016-10-23 17:19:52 +02:00
MemoryMap = NEXT_MEMORY_DESCRIPTOR ( MemoryMap , DescriptorSize ) ;
}
PatchSmmSaveStateMap ( ) ;
PatchGdtIdtMap ( ) ;
2023-05-26 14:57:29 +02:00
PERF_FUNCTION_END ( ) ;
2016-10-23 17:19:52 +02:00
}
2016-11-24 06:36:56 +01:00
/**
Sort memory map entries based upon PhysicalStart , from low to high .
@ param MemoryMap A pointer to the buffer in which firmware places
the current memory map .
@ param MemoryMapSize Size , in bytes , of the MemoryMap buffer .
@ param DescriptorSize Size , in bytes , of an individual EFI_MEMORY_DESCRIPTOR .
* */
STATIC
VOID
SortMemoryMap (
IN OUT EFI_MEMORY_DESCRIPTOR * MemoryMap ,
IN UINTN MemoryMapSize ,
IN UINTN DescriptorSize
)
{
EFI_MEMORY_DESCRIPTOR * MemoryMapEntry ;
EFI_MEMORY_DESCRIPTOR * NextMemoryMapEntry ;
EFI_MEMORY_DESCRIPTOR * MemoryMapEnd ;
EFI_MEMORY_DESCRIPTOR TempMemoryMap ;
MemoryMapEntry = MemoryMap ;
NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR ( MemoryMapEntry , DescriptorSize ) ;
MemoryMapEnd = ( EFI_MEMORY_DESCRIPTOR * ) ( ( UINT8 * ) MemoryMap + MemoryMapSize ) ;
while ( MemoryMapEntry < MemoryMapEnd ) {
while ( NextMemoryMapEntry < MemoryMapEnd ) {
if ( MemoryMapEntry - > PhysicalStart > NextMemoryMapEntry - > PhysicalStart ) {
CopyMem ( & TempMemoryMap , MemoryMapEntry , sizeof ( EFI_MEMORY_DESCRIPTOR ) ) ;
CopyMem ( MemoryMapEntry , NextMemoryMapEntry , sizeof ( EFI_MEMORY_DESCRIPTOR ) ) ;
CopyMem ( NextMemoryMapEntry , & TempMemoryMap , sizeof ( EFI_MEMORY_DESCRIPTOR ) ) ;
}
NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR ( NextMemoryMapEntry , DescriptorSize ) ;
}
MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR ( MemoryMapEntry , DescriptorSize ) ;
NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR ( MemoryMapEntry , DescriptorSize ) ;
}
}
/**
Return if a UEFI memory page should be marked as not present in SMM page table .
If the memory map entries type is
EfiLoaderCode / Data , EfiBootServicesCode / Data , EfiConventionalMemory ,
EfiUnusableMemory , EfiACPIReclaimMemory , return TRUE .
Or return FALSE .
@ param [ in ] MemoryMap A pointer to the memory descriptor .
@ return TRUE The memory described will be marked as not present in SMM page table .
@ return FALSE The memory described will not be marked as not present in SMM page table .
* */
BOOLEAN
IsUefiPageNotPresent (
IN EFI_MEMORY_DESCRIPTOR * MemoryMap
)
{
switch ( MemoryMap - > Type ) {
case EfiLoaderCode :
case EfiLoaderData :
case EfiBootServicesCode :
case EfiBootServicesData :
case EfiConventionalMemory :
case EfiUnusableMemory :
case EfiACPIReclaimMemory :
return TRUE ;
default :
return FALSE ;
}
}
/**
2020-02-07 02:08:30 +01:00
Merge continuous memory map entries whose type is
2016-11-24 06:36:56 +01:00
EfiLoaderCode / Data , EfiBootServicesCode / Data , EfiConventionalMemory ,
EfiUnusableMemory , EfiACPIReclaimMemory , because the memory described by
these entries will be set as NOT present in SMM page table .
@ param [ in , out ] MemoryMap A pointer to the buffer in which firmware places
the current memory map .
@ param [ in , out ] MemoryMapSize A pointer to the size , in bytes , of the
MemoryMap buffer . On input , this is the size of
the current memory map . On output ,
it is the size of new memory map after merge .
@ param [ in ] DescriptorSize Size , in bytes , of an individual EFI_MEMORY_DESCRIPTOR .
* */
STATIC
VOID
MergeMemoryMapForNotPresentEntry (
IN OUT EFI_MEMORY_DESCRIPTOR * MemoryMap ,
IN OUT UINTN * MemoryMapSize ,
IN UINTN DescriptorSize
)
{
EFI_MEMORY_DESCRIPTOR * MemoryMapEntry ;
EFI_MEMORY_DESCRIPTOR * MemoryMapEnd ;
UINT64 MemoryBlockLength ;
EFI_MEMORY_DESCRIPTOR * NewMemoryMapEntry ;
EFI_MEMORY_DESCRIPTOR * NextMemoryMapEntry ;
MemoryMapEntry = MemoryMap ;
NewMemoryMapEntry = MemoryMap ;
MemoryMapEnd = ( EFI_MEMORY_DESCRIPTOR * ) ( ( UINT8 * ) MemoryMap + * MemoryMapSize ) ;
while ( ( UINTN ) MemoryMapEntry < ( UINTN ) MemoryMapEnd ) {
CopyMem ( NewMemoryMapEntry , MemoryMapEntry , sizeof ( EFI_MEMORY_DESCRIPTOR ) ) ;
NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR ( MemoryMapEntry , DescriptorSize ) ;
do {
MemoryBlockLength = ( UINT64 ) ( EFI_PAGES_TO_SIZE ( ( UINTN ) MemoryMapEntry - > NumberOfPages ) ) ;
if ( ( ( UINTN ) NextMemoryMapEntry < ( UINTN ) MemoryMapEnd ) & &
IsUefiPageNotPresent ( MemoryMapEntry ) & & IsUefiPageNotPresent ( NextMemoryMapEntry ) & &
( ( MemoryMapEntry - > PhysicalStart + MemoryBlockLength ) = = NextMemoryMapEntry - > PhysicalStart ) )
{
MemoryMapEntry - > NumberOfPages + = NextMemoryMapEntry - > NumberOfPages ;
if ( NewMemoryMapEntry ! = MemoryMapEntry ) {
NewMemoryMapEntry - > NumberOfPages + = NextMemoryMapEntry - > NumberOfPages ;
}
NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR ( NextMemoryMapEntry , DescriptorSize ) ;
continue ;
} else {
MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR ( NextMemoryMapEntry , DescriptorSize ) ;
break ;
}
} while ( TRUE ) ;
MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR ( MemoryMapEntry , DescriptorSize ) ;
NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR ( NewMemoryMapEntry , DescriptorSize ) ;
}
* MemoryMapSize = ( UINTN ) NewMemoryMapEntry - ( UINTN ) MemoryMap ;
return ;
}
2017-11-02 12:00:02 +01:00
/**
This function caches the GCD memory map information .
* */
VOID
GetGcdMemoryMap (
VOID
)
{
UINTN NumberOfDescriptors ;
EFI_GCD_MEMORY_SPACE_DESCRIPTOR * MemSpaceMap ;
EFI_STATUS Status ;
UINTN Index ;
Status = gDS - > GetMemorySpaceMap ( & NumberOfDescriptors , & MemSpaceMap ) ;
if ( EFI_ERROR ( Status ) ) {
return ;
}
mGcdMemNumberOfDesc = 0 ;
for ( Index = 0 ; Index < NumberOfDescriptors ; Index + + ) {
if ( ( MemSpaceMap [ Index ] . GcdMemoryType = = EfiGcdMemoryTypeReserved ) & &
( ( MemSpaceMap [ Index ] . Capabilities & ( EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED ) ) = =
( EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED ) )
)
{
mGcdMemNumberOfDesc + + ;
}
}
mGcdMemSpace = AllocateZeroPool ( mGcdMemNumberOfDesc * sizeof ( EFI_GCD_MEMORY_SPACE_DESCRIPTOR ) ) ;
ASSERT ( mGcdMemSpace ! = NULL ) ;
if ( mGcdMemSpace = = NULL ) {
mGcdMemNumberOfDesc = 0 ;
gBS - > FreePool ( MemSpaceMap ) ;
return ;
}
mGcdMemNumberOfDesc = 0 ;
for ( Index = 0 ; Index < NumberOfDescriptors ; Index + + ) {
if ( ( MemSpaceMap [ Index ] . GcdMemoryType = = EfiGcdMemoryTypeReserved ) & &
( ( MemSpaceMap [ Index ] . Capabilities & ( EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED ) ) = =
( EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED ) )
)
{
CopyMem (
& mGcdMemSpace [ mGcdMemNumberOfDesc ] ,
& MemSpaceMap [ Index ] ,
sizeof ( EFI_GCD_MEMORY_SPACE_DESCRIPTOR )
) ;
mGcdMemNumberOfDesc + + ;
}
}
gBS - > FreePool ( MemSpaceMap ) ;
}
2017-11-22 16:11:54 +01:00
/**
Get UEFI MemoryAttributesTable .
* */
VOID
GetUefiMemoryAttributesTable (
VOID
)
{
EFI_STATUS Status ;
EFI_MEMORY_ATTRIBUTES_TABLE * MemoryAttributesTable ;
UINTN MemoryAttributesTableSize ;
Status = EfiGetSystemConfigurationTable ( & gEfiMemoryAttributesTableGuid , ( VOID * * ) & MemoryAttributesTable ) ;
2018-07-30 07:20:13 +02:00
if ( ! EFI_ERROR ( Status ) & & ( MemoryAttributesTable ! = NULL ) ) {
2017-11-22 16:11:54 +01:00
MemoryAttributesTableSize = sizeof ( EFI_MEMORY_ATTRIBUTES_TABLE ) + MemoryAttributesTable - > DescriptorSize * MemoryAttributesTable - > NumberOfEntries ;
mUefiMemoryAttributesTable = AllocateCopyPool ( MemoryAttributesTableSize , MemoryAttributesTable ) ;
ASSERT ( mUefiMemoryAttributesTable ! = NULL ) ;
}
}
2016-11-24 06:36:56 +01:00
/**
This function caches the UEFI memory map information .
* */
VOID
GetUefiMemoryMap (
VOID
)
{
EFI_STATUS Status ;
UINTN MapKey ;
UINT32 DescriptorVersion ;
EFI_MEMORY_DESCRIPTOR * MemoryMap ;
UINTN UefiMemoryMapSize ;
DEBUG ( ( DEBUG_INFO , " GetUefiMemoryMap \n " ) ) ;
UefiMemoryMapSize = 0 ;
MemoryMap = NULL ;
Status = gBS - > GetMemoryMap (
& UefiMemoryMapSize ,
MemoryMap ,
& MapKey ,
& mUefiDescriptorSize ,
& DescriptorVersion
) ;
ASSERT ( Status = = EFI_BUFFER_TOO_SMALL ) ;
do {
Status = gBS - > AllocatePool ( EfiBootServicesData , UefiMemoryMapSize , ( VOID * * ) & MemoryMap ) ;
ASSERT ( MemoryMap ! = NULL ) ;
if ( MemoryMap = = NULL ) {
return ;
}
Status = gBS - > GetMemoryMap (
& UefiMemoryMapSize ,
MemoryMap ,
& MapKey ,
& mUefiDescriptorSize ,
& DescriptorVersion
) ;
if ( EFI_ERROR ( Status ) ) {
gBS - > FreePool ( MemoryMap ) ;
MemoryMap = NULL ;
}
} while ( Status = = EFI_BUFFER_TOO_SMALL ) ;
2016-12-20 02:13:23 +01:00
if ( MemoryMap = = NULL ) {
return ;
}
2016-11-24 06:36:56 +01:00
SortMemoryMap ( MemoryMap , UefiMemoryMapSize , mUefiDescriptorSize ) ;
MergeMemoryMapForNotPresentEntry ( MemoryMap , & UefiMemoryMapSize , mUefiDescriptorSize ) ;
mUefiMemoryMapSize = UefiMemoryMapSize ;
mUefiMemoryMap = AllocateCopyPool ( UefiMemoryMapSize , MemoryMap ) ;
ASSERT ( mUefiMemoryMap ! = NULL ) ;
gBS - > FreePool ( MemoryMap ) ;
2017-11-02 12:00:02 +01:00
//
// Get additional information from GCD memory map.
//
GetGcdMemoryMap ( ) ;
2017-11-22 16:11:54 +01:00
//
// Get UEFI memory attributes table.
//
GetUefiMemoryAttributesTable ( ) ;
2016-11-24 06:36:56 +01:00
}
/**
This function sets 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 (
VOID
)
{
2017-08-24 04:59:14 +02:00
EFI_STATUS Status ;
2016-11-24 06:36:56 +01:00
EFI_MEMORY_DESCRIPTOR * MemoryMap ;
UINTN MemoryMapEntryCount ;
UINTN Index ;
2017-11-22 16:11:54 +01:00
EFI_MEMORY_DESCRIPTOR * Entry ;
2016-11-24 06:36:56 +01:00
2023-05-26 14:57:29 +02:00
PERF_FUNCTION_BEGIN ( ) ;
2016-11-24 06:36:56 +01:00
DEBUG ( ( DEBUG_INFO , " SetUefiMemMapAttributes \n " ) ) ;
2017-11-02 12:00:02 +01:00
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
) ) ;
}
2021-12-05 23:54:17 +01:00
2017-11-02 12:00:02 +01:00
MemoryMap = NEXT_MEMORY_DESCRIPTOR ( MemoryMap , mUefiDescriptorSize ) ;
}
2016-11-24 06:36:56 +01:00
}
2021-12-05 23:54:17 +01:00
2017-11-02 12:00:02 +01:00
//
// Do not free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress().
//
2016-11-24 06:36:56 +01:00
2017-11-02 12:00:02 +01:00
//
// Set untested memory as not present.
//
if ( mGcdMemSpace ! = NULL ) {
for ( Index = 0 ; Index < mGcdMemNumberOfDesc ; Index + + ) {
2017-08-24 04:59:14 +02:00
Status = SmmSetMemoryAttributes (
2017-11-02 12:00:02 +01:00
mGcdMemSpace [ Index ] . BaseAddress ,
mGcdMemSpace [ Index ] . Length ,
2017-08-24 04:59:14 +02:00
EFI_MEMORY_RP
) ;
DEBUG ( (
DEBUG_INFO ,
2017-11-02 12:00:02 +01:00
" GcdMemory protection: 0x%lx - 0x%lx %r \n " ,
mGcdMemSpace [ Index ] . BaseAddress ,
mGcdMemSpace [ Index ] . BaseAddress + mGcdMemSpace [ Index ] . Length ,
2017-08-24 04:59:14 +02:00
Status
) ) ;
2016-11-24 06:36:56 +01:00
}
}
2021-12-05 23:54:17 +01:00
2016-11-24 06:36:56 +01:00
//
2017-11-02 12:00:02 +01:00
// Do not free mGcdMemSpace, it will be checked in IsSmmCommBufferForbiddenAddress().
2016-11-24 06:36:56 +01:00
//
2017-11-22 16:11:54 +01:00
//
// 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
) ;
DEBUG ( (
DEBUG_INFO ,
" UefiMemoryAttribute protection: 0x%lx - 0x%lx %r \n " ,
Entry - > PhysicalStart ,
Entry - > PhysicalStart + ( UINT64 ) EFI_PAGES_TO_SIZE ( ( UINTN ) Entry - > NumberOfPages ) ,
Status
) ) ;
}
}
2021-12-05 23:54:17 +01:00
2017-11-22 16:11:54 +01:00
Entry = NEXT_MEMORY_DESCRIPTOR ( Entry , mUefiMemoryAttributesTable - > DescriptorSize ) ;
}
}
2021-12-05 23:54:17 +01:00
2017-11-22 16:11:54 +01:00
//
// Do not free mUefiMemoryAttributesTable, it will be checked in IsSmmCommBufferForbiddenAddress().
//
2023-05-26 14:57:29 +02:00
PERF_FUNCTION_END ( ) ;
2016-11-24 06:36:56 +01:00
}
/**
Return if the Address is forbidden as SMM communication buffer .
@ param [ in ] Address the address to be checked
@ return TRUE The address is forbidden as SMM communication buffer .
@ return FALSE The address is allowed as SMM communication buffer .
* */
BOOLEAN
IsSmmCommBufferForbiddenAddress (
IN UINT64 Address
)
{
EFI_MEMORY_DESCRIPTOR * MemoryMap ;
UINTN MemoryMapEntryCount ;
UINTN Index ;
2017-11-22 16:11:54 +01:00
EFI_MEMORY_DESCRIPTOR * Entry ;
2016-11-24 06:36:56 +01:00
2017-11-02 12:00:02 +01:00
if ( mUefiMemoryMap ! = NULL ) {
MemoryMap = mUefiMemoryMap ;
MemoryMapEntryCount = mUefiMemoryMapSize / mUefiDescriptorSize ;
for ( Index = 0 ; Index < MemoryMapEntryCount ; Index + + ) {
if ( IsUefiPageNotPresent ( MemoryMap ) ) {
if ( ( Address > = MemoryMap - > PhysicalStart ) & &
( Address < MemoryMap - > PhysicalStart + EFI_PAGES_TO_SIZE ( ( UINTN ) MemoryMap - > NumberOfPages ) ) )
{
return TRUE ;
}
}
2021-12-05 23:54:17 +01:00
2017-11-02 12:00:02 +01:00
MemoryMap = NEXT_MEMORY_DESCRIPTOR ( MemoryMap , mUefiDescriptorSize ) ;
}
2016-12-20 02:13:23 +01:00
}
2017-11-02 12:00:02 +01:00
if ( mGcdMemSpace ! = NULL ) {
for ( Index = 0 ; Index < mGcdMemNumberOfDesc ; Index + + ) {
if ( ( Address > = mGcdMemSpace [ Index ] . BaseAddress ) & &
( Address < mGcdMemSpace [ Index ] . BaseAddress + mGcdMemSpace [ Index ] . Length ) )
{
2016-11-24 06:36:56 +01:00
return TRUE ;
}
}
}
2017-11-02 12:00:02 +01:00
2017-11-22 16:11:54 +01:00
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 ) {
if ( ( Address > = Entry - > PhysicalStart ) & &
( Address < Entry - > PhysicalStart + LShiftU64 ( Entry - > NumberOfPages , EFI_PAGE_SHIFT ) ) )
{
return TRUE ;
}
2021-12-05 23:54:17 +01:00
2017-11-22 16:11:54 +01:00
Entry = NEXT_MEMORY_DESCRIPTOR ( Entry , mUefiMemoryAttributesTable - > DescriptorSize ) ;
}
}
}
}
2021-12-05 23:54:17 +01:00
2016-11-24 06:36:56 +01:00
return FALSE ;
}
2017-11-20 09:14:10 +01:00
/**
This function set given attributes of the memory region specified by
BaseAddress and Length .
@ param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance .
@ param BaseAddress The physical address that is the start address of
a memory region .
@ param Length The size in bytes of the memory region .
@ param Attributes The bit mask of attributes to set for the memory
region .
@ retval EFI_SUCCESS The attributes were set for the memory region .
@ retval EFI_INVALID_PARAMETER Length is zero .
Attributes specified an illegal combination of
attributes that cannot be set together .
@ retval EFI_UNSUPPORTED The processor does not support one or more
bytes of the memory resource range specified
by BaseAddress and Length .
2018-02-28 06:21:59 +01:00
The bit mask of attributes is not supported for
2017-11-20 09:14:10 +01:00
the memory resource range specified by
BaseAddress and Length .
* */
EFI_STATUS
EFIAPI
EdkiiSmmSetMemoryAttributes (
IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL * This ,
IN EFI_PHYSICAL_ADDRESS BaseAddress ,
IN UINT64 Length ,
IN UINT64 Attributes
)
{
return SmmSetMemoryAttributes ( BaseAddress , Length , Attributes ) ;
}
/**
This function clears given attributes of the memory region specified by
BaseAddress and Length .
@ param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance .
@ param BaseAddress The physical address that is the start address of
a memory region .
@ param Length The size in bytes of the memory region .
2018-02-28 06:21:59 +01:00
@ param Attributes The bit mask of attributes to clear for the memory
2017-11-20 09:14:10 +01:00
region .
2018-02-28 06:21:59 +01:00
@ retval EFI_SUCCESS The attributes were cleared for the memory region .
2017-11-20 09:14:10 +01:00
@ retval EFI_INVALID_PARAMETER Length is zero .
Attributes specified an illegal combination of
2018-02-28 06:21:59 +01:00
attributes that cannot be cleared together .
2017-11-20 09:14:10 +01:00
@ retval EFI_UNSUPPORTED The processor does not support one or more
bytes of the memory resource range specified
by BaseAddress and Length .
2018-02-28 06:21:59 +01:00
The bit mask of attributes is not supported for
2017-11-20 09:14:10 +01:00
the memory resource range specified by
BaseAddress and Length .
* */
EFI_STATUS
EFIAPI
EdkiiSmmClearMemoryAttributes (
IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL * This ,
IN EFI_PHYSICAL_ADDRESS BaseAddress ,
IN UINT64 Length ,
IN UINT64 Attributes
)
{
return SmmClearMemoryAttributes ( BaseAddress , Length , Attributes ) ;
}
/**
2018-02-28 06:21:59 +01:00
This function retrieves the attributes of the memory region specified by
2017-11-20 09:14:10 +01:00
BaseAddress and Length . If different attributes are got from different part
of the memory region , EFI_NO_MAPPING will be returned .
@ param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance .
@ param BaseAddress The physical address that is the start address of
a memory region .
@ param Length The size in bytes of the memory region .
@ param Attributes Pointer to attributes returned .
@ retval EFI_SUCCESS The attributes got for the memory region .
@ retval EFI_INVALID_PARAMETER Length is zero .
Attributes is NULL .
@ retval EFI_NO_MAPPING Attributes are not consistent cross the memory
region .
@ retval EFI_UNSUPPORTED The processor does not support one or more
bytes of the memory resource range specified
by BaseAddress and Length .
* */
EFI_STATUS
EFIAPI
EdkiiSmmGetMemoryAttributes (
IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL * This ,
IN EFI_PHYSICAL_ADDRESS BaseAddress ,
IN UINT64 Length ,
OUT UINT64 * Attributes
)
{
EFI_PHYSICAL_ADDRESS Address ;
UINT64 * PageEntry ;
UINT64 MemAttr ;
PAGE_ATTRIBUTE PageAttr ;
INT64 Size ;
2022-08-09 09:58:28 +02:00
UINTN PageTableBase ;
BOOLEAN EnablePML5Paging ;
IA32_CR4 Cr4 ;
2017-11-20 09:14:10 +01:00
if ( ( Length < SIZE_4KB ) | | ( Attributes = = NULL ) ) {
return EFI_INVALID_PARAMETER ;
}
Size = ( INT64 ) Length ;
MemAttr = ( UINT64 ) - 1 ;
2022-08-09 09:58:28 +02:00
PageTableBase = AsmReadCr3 ( ) & PAGING_4K_ADDRESS_MASK_64 ;
Cr4 . UintN = AsmReadCr4 ( ) ;
EnablePML5Paging = ( BOOLEAN ) ( Cr4 . Bits . LA57 = = 1 ) ;
2017-11-20 09:14:10 +01:00
do {
2022-08-09 09:58:28 +02:00
PageEntry = GetPageTableEntry ( PageTableBase , EnablePML5Paging , BaseAddress , & PageAttr ) ;
2017-11-20 09:14:10 +01:00
if ( ( PageEntry = = NULL ) | | ( PageAttr = = PageNone ) ) {
return EFI_UNSUPPORTED ;
}
//
// If the memory range is cross page table boundary, make sure they
// share the same attribute. Return EFI_NO_MAPPING if not.
//
* Attributes = GetAttributesFromPageEntry ( PageEntry ) ;
if ( ( MemAttr ! = ( UINT64 ) - 1 ) & & ( * Attributes ! = MemAttr ) ) {
return EFI_NO_MAPPING ;
}
switch ( PageAttr ) {
case Page4K :
Address = * PageEntry & ~ mAddressEncMask & PAGING_4K_ADDRESS_MASK_64 ;
Size - = ( SIZE_4KB - ( BaseAddress - Address ) ) ;
BaseAddress + = ( SIZE_4KB - ( BaseAddress - Address ) ) ;
break ;
case Page2M :
Address = * PageEntry & ~ mAddressEncMask & PAGING_2M_ADDRESS_MASK_64 ;
Size - = SIZE_2MB - ( BaseAddress - Address ) ;
BaseAddress + = SIZE_2MB - ( BaseAddress - Address ) ;
2021-12-05 23:54:17 +01:00
break ;
2017-11-20 09:14:10 +01:00
case Page1G :
Address = * PageEntry & ~ mAddressEncMask & PAGING_1G_ADDRESS_MASK_64 ;
Size - = SIZE_1GB - ( BaseAddress - Address ) ;
BaseAddress + = SIZE_1GB - ( BaseAddress - Address ) ;
2021-12-05 23:54:17 +01:00
break ;
2017-11-20 09:14:10 +01:00
default :
return EFI_UNSUPPORTED ;
}
MemAttr = * Attributes ;
} while ( Size > 0 ) ;
return EFI_SUCCESS ;
}
2022-12-21 05:21:56 +01:00
/**
Prevent the memory pages used for SMM page table from been overwritten .
* */
VOID
EnablePageTableProtection (
VOID
)
{
PAGE_TABLE_POOL * HeadPool ;
PAGE_TABLE_POOL * Pool ;
UINT64 PoolSize ;
EFI_PHYSICAL_ADDRESS Address ;
UINTN PageTableBase ;
if ( mPageTablePool = = NULL ) {
return ;
}
PageTableBase = AsmReadCr3 ( ) & PAGING_4K_ADDRESS_MASK_64 ;
//
// ConvertMemoryPageAttributes might update mPageTablePool. It's safer to
// remember original one in advance.
//
HeadPool = mPageTablePool ;
Pool = HeadPool ;
do {
Address = ( EFI_PHYSICAL_ADDRESS ) ( UINTN ) Pool ;
PoolSize = Pool - > Offset + EFI_PAGES_TO_SIZE ( Pool - > FreePages ) ;
//
// Set entire pool including header, used-memory and left free-memory as ReadOnly in SMM page table.
//
2023-06-07 09:46:58 +02:00
ConvertMemoryPageAttributes ( PageTableBase , mPagingMode , Address , PoolSize , EFI_MEMORY_RO , TRUE , NULL ) ;
2022-12-21 05:21:56 +01:00
Pool = Pool - > NextPool ;
} while ( Pool ! = HeadPool ) ;
}
/**
Return whether memory used by SMM page table need to be set as Read Only .
@ retval TRUE Need to set SMM page table as Read Only .
@ retval FALSE Do not set SMM page table as Read Only .
* */
BOOLEAN
IfReadOnlyPageTableNeeded (
VOID
)
{
//
// Don't mark page table memory as read-only if
// - no restriction on access to non-SMRAM memory; or
// - SMM heap guard feature enabled; or
// BIT2: SMM page guard enabled
// BIT3: SMM pool guard enabled
// - SMM profile feature enabled
//
if ( ! IsRestrictedMemoryAccess ( ) | |
( ( PcdGet8 ( PcdHeapGuardPropertyMask ) & ( BIT3 | BIT2 ) ) ! = 0 ) | |
FeaturePcdGet ( PcdCpuSmmProfileEnable ) )
{
if ( sizeof ( UINTN ) = = sizeof ( UINT64 ) ) {
//
// Restriction on access to non-SMRAM memory and heap guard could not be enabled at the same time.
//
ASSERT (
! ( IsRestrictedMemoryAccess ( ) & &
( PcdGet8 ( PcdHeapGuardPropertyMask ) & ( BIT3 | BIT2 ) ) ! = 0 )
) ;
//
// Restriction on access to non-SMRAM memory and SMM profile could not be enabled at the same time.
//
ASSERT ( ! ( IsRestrictedMemoryAccess ( ) & & FeaturePcdGet ( PcdCpuSmmProfileEnable ) ) ) ;
}
return FALSE ;
}
return TRUE ;
}
/**
This function sets memory attribute for page table .
* */
VOID
SetPageTableAttributes (
VOID
)
{
BOOLEAN CetEnabled ;
if ( ! IfReadOnlyPageTableNeeded ( ) ) {
return ;
}
2023-05-26 14:57:29 +02:00
PERF_FUNCTION_BEGIN ( ) ;
2022-12-21 05:21:56 +01:00
DEBUG ( ( DEBUG_INFO , " SetPageTableAttributes \n " ) ) ;
//
// Disable write protection, because we need mark page table to be write protected.
// We need *write* page table memory, to mark itself to be *read only*.
//
CetEnabled = ( ( AsmReadCr4 ( ) & CR4_CET_ENABLE ) ! = 0 ) ? TRUE : FALSE ;
if ( CetEnabled ) {
//
// CET must be disabled if WP is disabled.
//
DisableCet ( ) ;
}
AsmWriteCr0 ( AsmReadCr0 ( ) & ~ CR0_WP ) ;
// Set memory used by page table as Read Only.
DEBUG ( ( DEBUG_INFO , " Start... \n " ) ) ;
EnablePageTableProtection ( ) ;
//
// Enable write protection, after page table attribute updated.
//
AsmWriteCr0 ( AsmReadCr0 ( ) | CR0_WP ) ;
mIsReadOnlyPageTable = TRUE ;
//
// Flush TLB after mark all page table pool as read only.
//
FlushTlbForAll ( ) ;
if ( CetEnabled ) {
//
// re-enable CET.
//
EnableCet ( ) ;
}
2023-05-26 14:57:29 +02:00
PERF_FUNCTION_END ( ) ;
2022-12-21 05:21:56 +01:00
}