2 Page table manipulation functions for IA-32 processors
4 Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include "PiSmmCpuDxeSmm.h"
32 Get page table base address and the depth of the page table.
34 @param[out] Base Page table base address.
35 @param[out] FiveLevels TRUE means 5 level paging. FALSE means 4 level paging.
40 OUT BOOLEAN
*FiveLevels OPTIONAL
43 *Base
= ((mInternalCr3
== 0) ?
44 (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64
) :
46 if (FiveLevels
!= NULL
) {
52 Create PageTable for SMM use.
54 @return PageTable Address
62 UINTN PageFaultHandlerHookAddress
;
63 IA32_IDT_GATE_DESCRIPTOR
*IdtEntry
;
67 // Initialize spin lock
69 InitializeSpinLock (mPFLock
);
71 mPhysicalAddressBits
= 32;
73 if (FeaturePcdGet (PcdCpuSmmProfileEnable
) ||
74 HEAP_GUARD_NONSTOP_MODE
||
75 NULL_DETECTION_NONSTOP_MODE
)
78 // Set own Page Fault entry instead of the default one, because SMM Profile
79 // feature depends on IRET instruction to do Single Step
81 PageFaultHandlerHookAddress
= (UINTN
)PageFaultIdtHandlerSmmProfile
;
82 IdtEntry
= (IA32_IDT_GATE_DESCRIPTOR
*)gcSmiIdtr
.Base
;
83 IdtEntry
+= EXCEPT_IA32_PAGE_FAULT
;
84 IdtEntry
->Bits
.OffsetLow
= (UINT16
)PageFaultHandlerHookAddress
;
85 IdtEntry
->Bits
.Reserved_0
= 0;
86 IdtEntry
->Bits
.GateType
= IA32_IDT_GATE_TYPE_INTERRUPT_32
;
87 IdtEntry
->Bits
.OffsetHigh
= (UINT16
)(PageFaultHandlerHookAddress
>> 16);
90 // Register SMM Page Fault Handler
92 Status
= SmmRegisterExceptionHandler (&mSmmCpuService
, EXCEPT_IA32_PAGE_FAULT
, SmiPFHandler
);
93 ASSERT_EFI_ERROR (Status
);
97 // Additional SMM IDT initialization for SMM stack guard
99 if (FeaturePcdGet (PcdCpuSmmStackGuard
)) {
100 InitializeIDTSmmStackGuard ();
103 return Gen4GPageTable (TRUE
);
107 Page Fault handler for SMM use.
111 SmiDefaultPFHandler (
119 ThePage Fault handler wrapper for SMM use.
121 @param InterruptType Defines the type of interrupt or exception that
122 occurred on the processor.This parameter is processor architecture specific.
123 @param SystemContext A pointer to the processor context when
124 the interrupt occurred on the processor.
129 IN EFI_EXCEPTION_TYPE InterruptType
,
130 IN EFI_SYSTEM_CONTEXT SystemContext
134 UINTN GuardPageAddress
;
137 ASSERT (InterruptType
== EXCEPT_IA32_PAGE_FAULT
);
139 AcquireSpinLock (mPFLock
);
141 PFAddress
= AsmReadCr2 ();
144 // If a page fault occurs in SMRAM range, it might be in a SMM stack guard page,
145 // or SMM page protection violation.
147 if ((PFAddress
>= mCpuHotPlugData
.SmrrBase
) &&
148 (PFAddress
< (mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
)))
150 DumpCpuContext (InterruptType
, SystemContext
);
151 CpuIndex
= GetCpuIndex ();
152 GuardPageAddress
= (mSmmStackArrayBase
+ EFI_PAGE_SIZE
+ CpuIndex
* mSmmStackSize
);
153 if ((FeaturePcdGet (PcdCpuSmmStackGuard
)) &&
154 (PFAddress
>= GuardPageAddress
) &&
155 (PFAddress
< (GuardPageAddress
+ EFI_PAGE_SIZE
)))
157 DEBUG ((DEBUG_ERROR
, "SMM stack overflow!\n"));
159 if ((SystemContext
.SystemContextIa32
->ExceptionData
& IA32_PF_EC_ID
) != 0) {
160 DEBUG ((DEBUG_ERROR
, "SMM exception at execution (0x%x)\n", PFAddress
));
162 DumpModuleInfoByIp (*(UINTN
*)(UINTN
)SystemContext
.SystemContextIa32
->Esp
);
165 DEBUG ((DEBUG_ERROR
, "SMM exception at access (0x%x)\n", PFAddress
));
167 DumpModuleInfoByIp ((UINTN
)SystemContext
.SystemContextIa32
->Eip
);
171 if (HEAP_GUARD_NONSTOP_MODE
) {
172 GuardPagePFHandler (SystemContext
.SystemContextIa32
->ExceptionData
);
182 // If a page fault occurs in non-SMRAM range.
184 if ((PFAddress
< mCpuHotPlugData
.SmrrBase
) ||
185 (PFAddress
>= mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
))
187 if ((SystemContext
.SystemContextIa32
->ExceptionData
& IA32_PF_EC_ID
) != 0) {
188 DumpCpuContext (InterruptType
, SystemContext
);
189 DEBUG ((DEBUG_ERROR
, "Code executed on IP(0x%x) out of SMM range after SMM is locked!\n", PFAddress
));
191 DumpModuleInfoByIp (*(UINTN
*)(UINTN
)SystemContext
.SystemContextIa32
->Esp
);
198 // If NULL pointer was just accessed
200 if (((PcdGet8 (PcdNullPointerDetectionPropertyMask
) & BIT1
) != 0) &&
201 (PFAddress
< EFI_PAGE_SIZE
))
203 DumpCpuContext (InterruptType
, SystemContext
);
204 DEBUG ((DEBUG_ERROR
, "!!! NULL pointer access !!!\n"));
206 DumpModuleInfoByIp ((UINTN
)SystemContext
.SystemContextIa32
->Eip
);
209 if (NULL_DETECTION_NONSTOP_MODE
) {
210 GuardPagePFHandler (SystemContext
.SystemContextIa32
->ExceptionData
);
218 if (IsSmmCommBufferForbiddenAddress (PFAddress
)) {
219 DumpCpuContext (InterruptType
, SystemContext
);
220 DEBUG ((DEBUG_ERROR
, "Access SMM communication forbidden address (0x%x)!\n", PFAddress
));
222 DumpModuleInfoByIp ((UINTN
)SystemContext
.SystemContextIa32
->Eip
);
229 if (FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
230 SmmProfilePFHandler (
231 SystemContext
.SystemContextIa32
->Eip
,
232 SystemContext
.SystemContextIa32
->ExceptionData
235 DumpCpuContext (InterruptType
, SystemContext
);
236 SmiDefaultPFHandler ();
240 ReleaseSpinLock (mPFLock
);
244 This function sets memory attribute for page table.
247 SetPageTableAttributes (
258 BOOLEAN PageTableSplitted
;
262 // Don't mark page table to read-only if heap guard is enabled.
264 // BIT2: SMM page guard enabled
265 // BIT3: SMM pool guard enabled
267 if ((PcdGet8 (PcdHeapGuardPropertyMask
) & (BIT3
| BIT2
)) != 0) {
268 DEBUG ((DEBUG_INFO
, "Don't mark page table to read-only as heap guard is enabled\n"));
273 // Don't mark page table to read-only if SMM profile is enabled.
275 if (FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
276 DEBUG ((DEBUG_INFO
, "Don't mark page table to read-only as SMM profile is enabled\n"));
280 DEBUG ((DEBUG_INFO
, "SetPageTableAttributes\n"));
283 // Disable write protection, because we need mark page table to be write protected.
284 // We need *write* page table memory, to mark itself to be *read only*.
286 CetEnabled
= ((AsmReadCr4 () & CR4_CET_ENABLE
) != 0) ? TRUE
: FALSE
;
289 // CET must be disabled if WP is disabled.
294 AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP
);
297 DEBUG ((DEBUG_INFO
, "Start...\n"));
298 PageTableSplitted
= FALSE
;
300 GetPageTable (&PageTableBase
, NULL
);
301 L3PageTable
= (UINT64
*)PageTableBase
;
303 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)PageTableBase
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
304 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
306 for (Index3
= 0; Index3
< 4; Index3
++) {
307 L2PageTable
= (UINT64
*)(UINTN
)(L3PageTable
[Index3
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
308 if (L2PageTable
== NULL
) {
312 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)(UINTN
)L2PageTable
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
313 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
315 for (Index2
= 0; Index2
< SIZE_4KB
/sizeof (UINT64
); Index2
++) {
316 if ((L2PageTable
[Index2
] & IA32_PG_PS
) != 0) {
321 L1PageTable
= (UINT64
*)(UINTN
)(L2PageTable
[Index2
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
322 if (L1PageTable
== NULL
) {
326 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)(UINTN
)L1PageTable
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
327 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
330 } while (PageTableSplitted
);
333 // Enable write protection, after page table updated.
335 AsmWriteCr0 (AsmReadCr0 () | CR0_WP
);
347 This function returns with no action for 32 bit.
349 @param[out] *Cr2 Pointer to variable to hold CR2 register value.
360 This function returns with no action for 32 bit.
362 @param[in] Cr2 Value to write into CR2 register.
373 Return whether access to non-SMRAM is restricted.
375 @retval TRUE Access to non-SMRAM is restricted.
376 @retval FALSE Access to non-SMRAM is not restricted.
379 IsRestrictedMemoryAccess (