]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c
UefiCpuPkg/MpInitLib: Fix MemTest86 failure.
[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
7This program and the accompanying materials\r
8are licensed and made available under the terms and conditions of the BSD License\r
9which accompanies this distribution. The full text of the license may be found at\r
10http://opensource.org/licenses/bsd-license.php\r
11\r
12THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
13WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14\r
15**/\r
16\r
17#include "PiSmmCpuDxeSmm.h"\r
18\r
19/**\r
20 Disable CET.\r
21**/\r
22VOID\r
23EFIAPI\r
24DisableCet (\r
25 VOID\r
26 );\r
27\r
28/**\r
29 Enable CET.\r
30**/\r
31VOID\r
32EFIAPI\r
33EnableCet (\r
34 VOID\r
35 );\r
36\r
37/**\r
38 Create PageTable for SMM use.\r
39\r
40 @return PageTable Address\r
41\r
42**/\r
43UINT32\r
44SmmInitPageTable (\r
45 VOID\r
46 )\r
47{\r
48 UINTN PageFaultHandlerHookAddress;\r
49 IA32_IDT_GATE_DESCRIPTOR *IdtEntry;\r
50 EFI_STATUS Status;\r
51\r
52 //\r
53 // Initialize spin lock\r
54 //\r
55 InitializeSpinLock (mPFLock);\r
56\r
57 mPhysicalAddressBits = 32;\r
58\r
59 if (FeaturePcdGet (PcdCpuSmmProfileEnable) ||\r
60 HEAP_GUARD_NONSTOP_MODE ||\r
61 NULL_DETECTION_NONSTOP_MODE) {\r
62 //\r
63 // Set own Page Fault entry instead of the default one, because SMM Profile\r
64 // feature depends on IRET instruction to do Single Step\r
65 //\r
66 PageFaultHandlerHookAddress = (UINTN)PageFaultIdtHandlerSmmProfile;\r
67 IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) gcSmiIdtr.Base;\r
68 IdtEntry += EXCEPT_IA32_PAGE_FAULT;\r
69 IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress;\r
70 IdtEntry->Bits.Reserved_0 = 0;\r
71 IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;\r
72 IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16);\r
73 } else {\r
74 //\r
75 // Register SMM Page Fault Handler\r
76 //\r
77 Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);\r
78 ASSERT_EFI_ERROR (Status);\r
79 }\r
80\r
81 //\r
82 // Additional SMM IDT initialization for SMM stack guard\r
83 //\r
84 if (FeaturePcdGet (PcdCpuSmmStackGuard)) {\r
85 InitializeIDTSmmStackGuard ();\r
86 }\r
87 return Gen4GPageTable (TRUE);\r
88}\r
89\r
90/**\r
91 Page Fault handler for SMM use.\r
92\r
93**/\r
94VOID\r
95SmiDefaultPFHandler (\r
96 VOID\r
97 )\r
98{\r
99 CpuDeadLoop ();\r
100}\r
101\r
102/**\r
103 ThePage Fault handler wrapper for SMM use.\r
104\r
105 @param InterruptType Defines the type of interrupt or exception that\r
106 occurred on the processor.This parameter is processor architecture specific.\r
107 @param SystemContext A pointer to the processor context when\r
108 the interrupt occurred on the processor.\r
109**/\r
110VOID\r
111EFIAPI\r
112SmiPFHandler (\r
113 IN EFI_EXCEPTION_TYPE InterruptType,\r
114 IN EFI_SYSTEM_CONTEXT SystemContext\r
115 )\r
116{\r
117 UINTN PFAddress;\r
118 UINTN GuardPageAddress;\r
119 UINTN CpuIndex;\r
120\r
121 ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);\r
122\r
123 AcquireSpinLock (mPFLock);\r
124\r
125 PFAddress = AsmReadCr2 ();\r
126\r
127 //\r
128 // If a page fault occurs in SMRAM range, it might be in a SMM stack guard page,\r
129 // or SMM page protection violation.\r
130 //\r
131 if ((PFAddress >= mCpuHotPlugData.SmrrBase) &&\r
132 (PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) {\r
133 DumpCpuContext (InterruptType, SystemContext);\r
134 CpuIndex = GetCpuIndex ();\r
135 GuardPageAddress = (mSmmStackArrayBase + EFI_PAGE_SIZE + CpuIndex * mSmmStackSize);\r
136 if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&\r
137 (PFAddress >= GuardPageAddress) &&\r
138 (PFAddress < (GuardPageAddress + EFI_PAGE_SIZE))) {\r
139 DEBUG ((DEBUG_ERROR, "SMM stack overflow!\n"));\r
140 } else {\r
141 if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {\r
142 DEBUG ((DEBUG_ERROR, "SMM exception at execution (0x%x)\n", PFAddress));\r
143 DEBUG_CODE (\r
144 DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);\r
145 );\r
146 } else {\r
147 DEBUG ((DEBUG_ERROR, "SMM exception at access (0x%x)\n", PFAddress));\r
148 DEBUG_CODE (\r
149 DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);\r
150 );\r
151 }\r
152\r
153 if (HEAP_GUARD_NONSTOP_MODE) {\r
154 GuardPagePFHandler (SystemContext.SystemContextIa32->ExceptionData);\r
155 goto Exit;\r
156 }\r
157 }\r
158 CpuDeadLoop ();\r
159 goto Exit;\r
160 }\r
161\r
162 //\r
163 // If a page fault occurs in non-SMRAM range.\r
164 //\r
165 if ((PFAddress < mCpuHotPlugData.SmrrBase) ||\r
166 (PFAddress >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) {\r
167 if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {\r
168 DumpCpuContext (InterruptType, SystemContext);\r
169 DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%x) out of SMM range after SMM is locked!\n", PFAddress));\r
170 DEBUG_CODE (\r
171 DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);\r
172 );\r
173 CpuDeadLoop ();\r
174 goto Exit;\r
175 }\r
176\r
177 //\r
178 // If NULL pointer was just accessed\r
179 //\r
180 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0 &&\r
181 (PFAddress < EFI_PAGE_SIZE)) {\r
182 DumpCpuContext (InterruptType, SystemContext);\r
183 DEBUG ((DEBUG_ERROR, "!!! NULL pointer access !!!\n"));\r
184 DEBUG_CODE (\r
185 DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);\r
186 );\r
187\r
188 if (NULL_DETECTION_NONSTOP_MODE) {\r
189 GuardPagePFHandler (SystemContext.SystemContextIa32->ExceptionData);\r
190 goto Exit;\r
191 }\r
192\r
193 CpuDeadLoop ();\r
194 goto Exit;\r
195 }\r
196\r
197 if (IsSmmCommBufferForbiddenAddress (PFAddress)) {\r
198 DumpCpuContext (InterruptType, SystemContext);\r
199 DEBUG ((DEBUG_ERROR, "Access SMM communication forbidden address (0x%x)!\n", PFAddress));\r
200 DEBUG_CODE (\r
201 DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);\r
202 );\r
203 CpuDeadLoop ();\r
204 goto Exit;\r
205 }\r
206 }\r
207\r
208 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {\r
209 SmmProfilePFHandler (\r
210 SystemContext.SystemContextIa32->Eip,\r
211 SystemContext.SystemContextIa32->ExceptionData\r
212 );\r
213 } else {\r
214 DumpCpuContext (InterruptType, SystemContext);\r
215 SmiDefaultPFHandler ();\r
216 }\r
217\r
218Exit:\r
219 ReleaseSpinLock (mPFLock);\r
220}\r
221\r
222/**\r
223 This function sets memory attribute for page table.\r
224**/\r
225VOID\r
226SetPageTableAttributes (\r
227 VOID\r
228 )\r
229{\r
230 UINTN Index2;\r
231 UINTN Index3;\r
232 UINT64 *L1PageTable;\r
233 UINT64 *L2PageTable;\r
234 UINT64 *L3PageTable;\r
235 BOOLEAN IsSplitted;\r
236 BOOLEAN PageTableSplitted;\r
237 BOOLEAN CetEnabled;\r
238\r
239 //\r
240 // Don't mark page table to read-only if heap guard is enabled.\r
241 //\r
242 // BIT2: SMM page guard enabled\r
243 // BIT3: SMM pool guard enabled\r
244 //\r
245 if ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) {\r
246 DEBUG ((DEBUG_INFO, "Don't mark page table to read-only as heap guard is enabled\n"));\r
247 return ;\r
248 }\r
249\r
250 //\r
251 // Don't mark page table to read-only if SMM profile is enabled.\r
252 //\r
253 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {\r
254 DEBUG ((DEBUG_INFO, "Don't mark page table to read-only as SMM profile is enabled\n"));\r
255 return ;\r
256 }\r
257\r
258 DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));\r
259\r
260 //\r
261 // Disable write protection, because we need mark page table to be write protected.\r
262 // We need *write* page table memory, to mark itself to be *read only*.\r
263 //\r
264 CetEnabled = ((AsmReadCr4() & CR4_CET_ENABLE) != 0) ? TRUE : FALSE;\r
265 if (CetEnabled) {\r
266 //\r
267 // CET must be disabled if WP is disabled.\r
268 //\r
269 DisableCet();\r
270 }\r
271 AsmWriteCr0 (AsmReadCr0() & ~CR0_WP);\r
272\r
273 do {\r
274 DEBUG ((DEBUG_INFO, "Start...\n"));\r
275 PageTableSplitted = FALSE;\r
276\r
277 L3PageTable = (UINT64 *)GetPageTableBase ();\r
278\r
279 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L3PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
280 PageTableSplitted = (PageTableSplitted || IsSplitted);\r
281\r
282 for (Index3 = 0; Index3 < 4; Index3++) {\r
283 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
284 if (L2PageTable == NULL) {\r
285 continue;\r
286 }\r
287\r
288 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L2PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
289 PageTableSplitted = (PageTableSplitted || IsSplitted);\r
290\r
291 for (Index2 = 0; Index2 < SIZE_4KB/sizeof(UINT64); Index2++) {\r
292 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {\r
293 // 2M\r
294 continue;\r
295 }\r
296 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
297 if (L1PageTable == NULL) {\r
298 continue;\r
299 }\r
300 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L1PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
301 PageTableSplitted = (PageTableSplitted || IsSplitted);\r
302 }\r
303 }\r
304 } while (PageTableSplitted);\r
305\r
306 //\r
307 // Enable write protection, after page table updated.\r
308 //\r
309 AsmWriteCr0 (AsmReadCr0() | CR0_WP);\r
310 if (CetEnabled) {\r
311 //\r
312 // re-enable CET.\r
313 //\r
314 EnableCet();\r
315 }\r
316\r
317 return ;\r
318}\r