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