]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c
UefiCpuPkg/PiSmmCpuDxeSmm: Reflect page table depth with page table address
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / Ia32 / PageTbl.c
CommitLineData
7947da3c
MK
1/** @file\r
2Page table manipulation functions for IA-32 processors\r
3\r
3eb69b08 4Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>\r
241f9149
LD
5Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
6\r
0acd8697 7SPDX-License-Identifier: BSD-2-Clause-Patent\r
7947da3c
MK
8\r
9**/\r
10\r
11#include "PiSmmCpuDxeSmm.h"\r
12\r
3eb69b08
JY
13/**\r
14 Disable CET.\r
15**/\r
16VOID\r
17EFIAPI\r
18DisableCet (\r
19 VOID\r
20 );\r
21\r
22/**\r
23 Enable CET.\r
24**/\r
25VOID\r
26EFIAPI\r
27EnableCet (\r
28 VOID\r
29 );\r
30\r
404250c8
SW
31/**\r
32 Get page table base address and the depth of the page table.\r
33\r
34 @param[out] Base Page table base address.\r
35 @param[out] FiveLevels TRUE means 5 level paging. FALSE means 4 level paging.\r
36**/\r
37VOID\r
38GetPageTable (\r
39 OUT UINTN *Base,\r
40 OUT BOOLEAN *FiveLevels OPTIONAL\r
41 )\r
42{\r
43 *Base = ((mInternalCr3 == 0) ?\r
44 (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64) :\r
45 mInternalCr3);\r
46 if (FiveLevels != NULL) {\r
47 *FiveLevels = FALSE;\r
48 }\r
49}\r
50\r
7947da3c
MK
51/**\r
52 Create PageTable for SMM use.\r
53\r
54 @return PageTable Address\r
55\r
56**/\r
57UINT32\r
58SmmInitPageTable (\r
59 VOID\r
60 )\r
61{\r
62 UINTN PageFaultHandlerHookAddress;\r
63 IA32_IDT_GATE_DESCRIPTOR *IdtEntry;\r
5c88af79 64 EFI_STATUS Status;\r
7947da3c
MK
65\r
66 //\r
67 // Initialize spin lock\r
68 //\r
fe3a75bc 69 InitializeSpinLock (mPFLock);\r
7947da3c 70\r
714c2603
SZ
71 mPhysicalAddressBits = 32;\r
72\r
09afd9a4
JW
73 if (FeaturePcdGet (PcdCpuSmmProfileEnable) ||\r
74 HEAP_GUARD_NONSTOP_MODE ||\r
75 NULL_DETECTION_NONSTOP_MODE) {\r
7947da3c
MK
76 //\r
77 // Set own Page Fault entry instead of the default one, because SMM Profile\r
78 // feature depends on IRET instruction to do Single Step\r
79 //\r
80 PageFaultHandlerHookAddress = (UINTN)PageFaultIdtHandlerSmmProfile;\r
81 IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) gcSmiIdtr.Base;\r
82 IdtEntry += EXCEPT_IA32_PAGE_FAULT;\r
83 IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress;\r
84 IdtEntry->Bits.Reserved_0 = 0;\r
85 IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;\r
86 IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16);\r
87 } else {\r
88 //\r
89 // Register SMM Page Fault Handler\r
90 //\r
5c88af79
JF
91 Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);\r
92 ASSERT_EFI_ERROR (Status);\r
7947da3c
MK
93 }\r
94\r
95 //\r
96 // Additional SMM IDT initialization for SMM stack guard\r
97 //\r
98 if (FeaturePcdGet (PcdCpuSmmStackGuard)) {\r
99 InitializeIDTSmmStackGuard ();\r
100 }\r
717fb604 101 return Gen4GPageTable (TRUE);\r
7947da3c
MK
102}\r
103\r
104/**\r
105 Page Fault handler for SMM use.\r
106\r
107**/\r
108VOID\r
109SmiDefaultPFHandler (\r
110 VOID\r
111 )\r
112{\r
113 CpuDeadLoop ();\r
114}\r
115\r
116/**\r
117 ThePage Fault handler wrapper for SMM use.\r
118\r
119 @param InterruptType Defines the type of interrupt or exception that\r
120 occurred on the processor.This parameter is processor architecture specific.\r
121 @param SystemContext A pointer to the processor context when\r
122 the interrupt occurred on the processor.\r
123**/\r
124VOID\r
125EFIAPI\r
126SmiPFHandler (\r
b8caae19
JF
127 IN EFI_EXCEPTION_TYPE InterruptType,\r
128 IN EFI_SYSTEM_CONTEXT SystemContext\r
7947da3c
MK
129 )\r
130{\r
131 UINTN PFAddress;\r
7fa1376c
JY
132 UINTN GuardPageAddress;\r
133 UINTN CpuIndex;\r
7947da3c
MK
134\r
135 ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);\r
136\r
fe3a75bc 137 AcquireSpinLock (mPFLock);\r
7947da3c
MK
138\r
139 PFAddress = AsmReadCr2 ();\r
140\r
7fa1376c
JY
141 //\r
142 // If a page fault occurs in SMRAM range, it might be in a SMM stack guard page,\r
143 // or SMM page protection violation.\r
144 //\r
145 if ((PFAddress >= mCpuHotPlugData.SmrrBase) &&\r
7947da3c 146 (PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) {\r
b8caae19 147 DumpCpuContext (InterruptType, SystemContext);\r
7fa1376c
JY
148 CpuIndex = GetCpuIndex ();\r
149 GuardPageAddress = (mSmmStackArrayBase + EFI_PAGE_SIZE + CpuIndex * mSmmStackSize);\r
150 if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&\r
151 (PFAddress >= GuardPageAddress) &&\r
152 (PFAddress < (GuardPageAddress + EFI_PAGE_SIZE))) {\r
153 DEBUG ((DEBUG_ERROR, "SMM stack overflow!\n"));\r
154 } else {\r
7fa1376c
JY
155 if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {\r
156 DEBUG ((DEBUG_ERROR, "SMM exception at execution (0x%x)\n", PFAddress));\r
157 DEBUG_CODE (\r
158 DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);\r
159 );\r
160 } else {\r
161 DEBUG ((DEBUG_ERROR, "SMM exception at access (0x%x)\n", PFAddress));\r
162 DEBUG_CODE (\r
163 DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);\r
164 );\r
165 }\r
09afd9a4
JW
166\r
167 if (HEAP_GUARD_NONSTOP_MODE) {\r
168 GuardPagePFHandler (SystemContext.SystemContextIa32->ExceptionData);\r
169 goto Exit;\r
170 }\r
7fa1376c 171 }\r
7947da3c 172 CpuDeadLoop ();\r
3eb69b08 173 goto Exit;\r
7947da3c
MK
174 }\r
175\r
176 //\r
8bf0380e 177 // If a page fault occurs in non-SMRAM range.\r
7947da3c
MK
178 //\r
179 if ((PFAddress < mCpuHotPlugData.SmrrBase) ||\r
180 (PFAddress >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) {\r
181 if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {\r
8bf0380e 182 DumpCpuContext (InterruptType, SystemContext);\r
717fb604 183 DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%x) out of SMM range after SMM is locked!\n", PFAddress));\r
7947da3c
MK
184 DEBUG_CODE (\r
185 DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);\r
186 );\r
187 CpuDeadLoop ();\r
3eb69b08 188 goto Exit;\r
7947da3c 189 }\r
09afd9a4
JW
190\r
191 //\r
192 // If NULL pointer was just accessed\r
193 //\r
194 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0 &&\r
195 (PFAddress < EFI_PAGE_SIZE)) {\r
196 DumpCpuContext (InterruptType, SystemContext);\r
197 DEBUG ((DEBUG_ERROR, "!!! NULL pointer access !!!\n"));\r
198 DEBUG_CODE (\r
199 DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);\r
200 );\r
201\r
202 if (NULL_DETECTION_NONSTOP_MODE) {\r
203 GuardPagePFHandler (SystemContext.SystemContextIa32->ExceptionData);\r
204 goto Exit;\r
205 }\r
206\r
207 CpuDeadLoop ();\r
3eb69b08 208 goto Exit;\r
09afd9a4
JW
209 }\r
210\r
d2fc7711 211 if (IsSmmCommBufferForbiddenAddress (PFAddress)) {\r
8bf0380e 212 DumpCpuContext (InterruptType, SystemContext);\r
d2fc7711
JY
213 DEBUG ((DEBUG_ERROR, "Access SMM communication forbidden address (0x%x)!\n", PFAddress));\r
214 DEBUG_CODE (\r
215 DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);\r
216 );\r
217 CpuDeadLoop ();\r
3eb69b08 218 goto Exit;\r
d2fc7711 219 }\r
7947da3c
MK
220 }\r
221\r
222 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {\r
223 SmmProfilePFHandler (\r
224 SystemContext.SystemContextIa32->Eip,\r
225 SystemContext.SystemContextIa32->ExceptionData\r
226 );\r
227 } else {\r
b8caae19 228 DumpCpuContext (InterruptType, SystemContext);\r
7947da3c
MK
229 SmiDefaultPFHandler ();\r
230 }\r
231\r
09afd9a4 232Exit:\r
fe3a75bc 233 ReleaseSpinLock (mPFLock);\r
7947da3c 234}\r
717fb604
JY
235\r
236/**\r
237 This function sets memory attribute for page table.\r
238**/\r
239VOID\r
240SetPageTableAttributes (\r
241 VOID\r
242 )\r
243{\r
244 UINTN Index2;\r
245 UINTN Index3;\r
246 UINT64 *L1PageTable;\r
247 UINT64 *L2PageTable;\r
248 UINT64 *L3PageTable;\r
404250c8 249 UINTN PageTableBase;\r
717fb604
JY
250 BOOLEAN IsSplitted;\r
251 BOOLEAN PageTableSplitted;\r
3eb69b08 252 BOOLEAN CetEnabled;\r
717fb604 253\r
827330cc 254 //\r
1015fb3c 255 // Don't mark page table to read-only if heap guard is enabled.\r
827330cc
JW
256 //\r
257 // BIT2: SMM page guard enabled\r
258 // BIT3: SMM pool guard enabled\r
259 //\r
260 if ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) {\r
1015fb3c
SZ
261 DEBUG ((DEBUG_INFO, "Don't mark page table to read-only as heap guard is enabled\n"));\r
262 return ;\r
263 }\r
264\r
265 //\r
266 // Don't mark page table to read-only if SMM profile is enabled.\r
267 //\r
268 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {\r
269 DEBUG ((DEBUG_INFO, "Don't mark page table to read-only as SMM profile is enabled\n"));\r
827330cc
JW
270 return ;\r
271 }\r
272\r
717fb604
JY
273 DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));\r
274\r
275 //\r
276 // Disable write protection, because we need mark page table to be write protected.\r
277 // We need *write* page table memory, to mark itself to be *read only*.\r
278 //\r
3eb69b08
JY
279 CetEnabled = ((AsmReadCr4() & CR4_CET_ENABLE) != 0) ? TRUE : FALSE;\r
280 if (CetEnabled) {\r
281 //\r
282 // CET must be disabled if WP is disabled.\r
283 //\r
284 DisableCet();\r
285 }\r
717fb604
JY
286 AsmWriteCr0 (AsmReadCr0() & ~CR0_WP);\r
287\r
288 do {\r
289 DEBUG ((DEBUG_INFO, "Start...\n"));\r
290 PageTableSplitted = FALSE;\r
291\r
404250c8
SW
292 GetPageTable (&PageTableBase, NULL);\r
293 L3PageTable = (UINT64 *)PageTableBase;\r
717fb604 294\r
404250c8 295 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)PageTableBase, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
717fb604
JY
296 PageTableSplitted = (PageTableSplitted || IsSplitted);\r
297\r
298 for (Index3 = 0; Index3 < 4; Index3++) {\r
241f9149 299 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
717fb604
JY
300 if (L2PageTable == NULL) {\r
301 continue;\r
302 }\r
303\r
304 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L2PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
305 PageTableSplitted = (PageTableSplitted || IsSplitted);\r
306\r
307 for (Index2 = 0; Index2 < SIZE_4KB/sizeof(UINT64); Index2++) {\r
308 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {\r
309 // 2M\r
310 continue;\r
311 }\r
241f9149 312 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
717fb604
JY
313 if (L1PageTable == NULL) {\r
314 continue;\r
315 }\r
316 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L1PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
317 PageTableSplitted = (PageTableSplitted || IsSplitted);\r
318 }\r
319 }\r
320 } while (PageTableSplitted);\r
321\r
322 //\r
323 // Enable write protection, after page table updated.\r
324 //\r
325 AsmWriteCr0 (AsmReadCr0() | CR0_WP);\r
3eb69b08
JY
326 if (CetEnabled) {\r
327 //\r
328 // re-enable CET.\r
329 //\r
330 EnableCet();\r
331 }\r
717fb604
JY
332\r
333 return ;\r
334}\r
37f9fea5
VN
335\r
336/**\r
337 This function returns with no action for 32 bit.\r
338\r
339 @param[out] *Cr2 Pointer to variable to hold CR2 register value.\r
340**/\r
341VOID\r
342SaveCr2 (\r
343 OUT UINTN *Cr2\r
344 )\r
345{\r
346 return ;\r
347}\r
348\r
349/**\r
350 This function returns with no action for 32 bit.\r
351\r
352 @param[in] Cr2 Value to write into CR2 register.\r
353**/\r
354VOID\r
355RestoreCr2 (\r
356 IN UINTN Cr2\r
357 )\r
358{\r
359 return ;\r
360}\r
79186ddc
RN
361\r
362/**\r
363 Return whether access to non-SMRAM is restricted.\r
364\r
365 @retval TRUE Access to non-SMRAM is restricted.\r
366 @retval FALSE Access to non-SMRAM is not restricted.\r
9c33f16f 367**/\r
79186ddc
RN
368BOOLEAN\r
369IsRestrictedMemoryAccess (\r
370 VOID\r
371 )\r
372{\r
373 return TRUE;\r
374}\r