diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c b/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c index bbc536a567..34bf6e1a25 100644 --- a/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c @@ -202,111 +202,6 @@ Exit: ReleaseSpinLock (mPFLock); } -/** - This function sets memory attribute for page table. -**/ -VOID -SetPageTableAttributes ( - VOID - ) -{ - UINTN Index2; - UINTN Index3; - UINT64 *L1PageTable; - UINT64 *L2PageTable; - UINT64 *L3PageTable; - UINTN PageTableBase; - BOOLEAN IsSplitted; - BOOLEAN PageTableSplitted; - BOOLEAN CetEnabled; - - // - // Don't mark page table to read-only if heap guard is enabled. - // - // BIT2: SMM page guard enabled - // BIT3: SMM pool guard enabled - // - if ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) { - DEBUG ((DEBUG_INFO, "Don't mark page table to read-only as heap guard is enabled\n")); - return; - } - - // - // Don't mark page table to read-only if SMM profile is enabled. - // - if (FeaturePcdGet (PcdCpuSmmProfileEnable)) { - DEBUG ((DEBUG_INFO, "Don't mark page table to read-only as SMM profile is enabled\n")); - return; - } - - 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); - - do { - DEBUG ((DEBUG_INFO, "Start...\n")); - PageTableSplitted = FALSE; - - PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64; - L3PageTable = (UINT64 *)PageTableBase; - - SmmSetMemoryAttributesEx (PageTableBase, FALSE, (EFI_PHYSICAL_ADDRESS)PageTableBase, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); - PageTableSplitted = (PageTableSplitted || IsSplitted); - - for (Index3 = 0; Index3 < 4; Index3++) { - L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); - if (L2PageTable == NULL) { - continue; - } - - SmmSetMemoryAttributesEx (PageTableBase, FALSE, (EFI_PHYSICAL_ADDRESS)(UINTN)L2PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); - PageTableSplitted = (PageTableSplitted || IsSplitted); - - for (Index2 = 0; Index2 < SIZE_4KB/sizeof (UINT64); Index2++) { - if ((L2PageTable[Index2] & IA32_PG_PS) != 0) { - // 2M - continue; - } - - L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); - if (L1PageTable == NULL) { - continue; - } - - SmmSetMemoryAttributesEx (PageTableBase, FALSE, (EFI_PHYSICAL_ADDRESS)(UINTN)L1PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); - PageTableSplitted = (PageTableSplitted || IsSplitted); - } - } - } while (PageTableSplitted); - - // - // Enable write protection, after page table updated. - // - AsmWriteCr0 (AsmReadCr0 () | CR0_WP); - if (CetEnabled) { - // - // re-enable CET. - // - EnableCet (); - } - - mIsReadOnlyPageTable = TRUE; - - return; -} - /** This function returns with no action for 32 bit. diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h b/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h index 3e69e043ca..5f0a38e400 100644 --- a/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h @@ -260,7 +260,6 @@ 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 diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c b/UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c index 11df7af016..4bb23f6920 100644 --- a/UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c @@ -1753,3 +1753,139 @@ EdkiiSmmGetMemoryAttributes ( return EFI_SUCCESS; } + +/** + 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. + // + ConvertMemoryPageAttributes (PageTableBase, m5LevelPagingNeeded, Address, PoolSize, EFI_MEMORY_RO, TRUE, NULL, NULL); + 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; + } + + 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 (); + } + + return; +} diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/PageTbl.c b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/PageTbl.c index 8d42d89801..3deb1ffd67 100644 --- a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/PageTbl.c +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/PageTbl.c @@ -1139,159 +1139,6 @@ Exit: ReleaseSpinLock (mPFLock); } -/** - This function sets memory attribute for page table. -**/ -VOID -SetPageTableAttributes ( - VOID - ) -{ - UINTN Index2; - UINTN Index3; - UINTN Index4; - UINTN Index5; - UINT64 *L1PageTable; - UINT64 *L2PageTable; - UINT64 *L3PageTable; - UINT64 *L4PageTable; - UINT64 *L5PageTable; - UINTN PageTableBase; - BOOLEAN IsSplitted; - BOOLEAN PageTableSplitted; - BOOLEAN CetEnabled; - BOOLEAN Enable5LevelPaging; - IA32_CR4 Cr4; - - // - // 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 (!mCpuSmmRestrictedMemoryAccess || - ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) || - FeaturePcdGet (PcdCpuSmmProfileEnable)) - { - // - // Restriction on access to non-SMRAM memory and heap guard could not be enabled at the same time. - // - ASSERT ( - !(mCpuSmmRestrictedMemoryAccess && - (PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) - ); - - // - // Restriction on access to non-SMRAM memory and SMM profile could not be enabled at the same time. - // - ASSERT (!(mCpuSmmRestrictedMemoryAccess && FeaturePcdGet (PcdCpuSmmProfileEnable))); - return; - } - - 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); - - do { - DEBUG ((DEBUG_INFO, "Start...\n")); - PageTableSplitted = FALSE; - L5PageTable = NULL; - - PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64; - Cr4.UintN = AsmReadCr4 (); - Enable5LevelPaging = (BOOLEAN)(Cr4.Bits.LA57 == 1); - - if (Enable5LevelPaging) { - L5PageTable = (UINT64 *)PageTableBase; - SmmSetMemoryAttributesEx (PageTableBase, Enable5LevelPaging, (EFI_PHYSICAL_ADDRESS)PageTableBase, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); - PageTableSplitted = (PageTableSplitted || IsSplitted); - } - - for (Index5 = 0; Index5 < (Enable5LevelPaging ? SIZE_4KB/sizeof (UINT64) : 1); Index5++) { - if (Enable5LevelPaging) { - L4PageTable = (UINT64 *)(UINTN)(L5PageTable[Index5] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); - if (L4PageTable == NULL) { - continue; - } - } else { - L4PageTable = (UINT64 *)PageTableBase; - } - - SmmSetMemoryAttributesEx (PageTableBase, Enable5LevelPaging, (EFI_PHYSICAL_ADDRESS)(UINTN)L4PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); - PageTableSplitted = (PageTableSplitted || IsSplitted); - - for (Index4 = 0; Index4 < SIZE_4KB/sizeof (UINT64); Index4++) { - L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); - if (L3PageTable == NULL) { - continue; - } - - SmmSetMemoryAttributesEx (PageTableBase, Enable5LevelPaging, (EFI_PHYSICAL_ADDRESS)(UINTN)L3PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); - PageTableSplitted = (PageTableSplitted || IsSplitted); - - for (Index3 = 0; Index3 < SIZE_4KB/sizeof (UINT64); Index3++) { - if ((L3PageTable[Index3] & IA32_PG_PS) != 0) { - // 1G - continue; - } - - L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); - if (L2PageTable == NULL) { - continue; - } - - SmmSetMemoryAttributesEx (PageTableBase, Enable5LevelPaging, (EFI_PHYSICAL_ADDRESS)(UINTN)L2PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); - PageTableSplitted = (PageTableSplitted || IsSplitted); - - for (Index2 = 0; Index2 < SIZE_4KB/sizeof (UINT64); Index2++) { - if ((L2PageTable[Index2] & IA32_PG_PS) != 0) { - // 2M - continue; - } - - L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); - if (L1PageTable == NULL) { - continue; - } - - SmmSetMemoryAttributesEx (PageTableBase, Enable5LevelPaging, (EFI_PHYSICAL_ADDRESS)(UINTN)L1PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); - PageTableSplitted = (PageTableSplitted || IsSplitted); - } - } - } - } - } while (PageTableSplitted); - - // - // Enable write protection, after page table updated. - // - AsmWriteCr0 (AsmReadCr0 () | CR0_WP); - if (CetEnabled) { - // - // re-enable CET. - // - EnableCet (); - } - - mIsReadOnlyPageTable = TRUE; - - return; -} - /** This function reads CR2 register when on-demand paging is enabled.