]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c
UefiCpuPkg\CpuSmm: Save & restore CR2 on-demand paging in SMM
[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
7947da3c
MK
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
3eb69b08
JY
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
7947da3c
MK
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
5c88af79 50 EFI_STATUS Status;\r
7947da3c
MK
51\r
52 //\r
53 // Initialize spin lock\r
54 //\r
fe3a75bc 55 InitializeSpinLock (mPFLock);\r
7947da3c 56\r
714c2603
SZ
57 mPhysicalAddressBits = 32;\r
58\r
09afd9a4
JW
59 if (FeaturePcdGet (PcdCpuSmmProfileEnable) ||\r
60 HEAP_GUARD_NONSTOP_MODE ||\r
61 NULL_DETECTION_NONSTOP_MODE) {\r
7947da3c
MK
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
5c88af79
JF
77 Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);\r
78 ASSERT_EFI_ERROR (Status);\r
7947da3c
MK
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
717fb604 87 return Gen4GPageTable (TRUE);\r
7947da3c
MK
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
b8caae19
JF
113 IN EFI_EXCEPTION_TYPE InterruptType,\r
114 IN EFI_SYSTEM_CONTEXT SystemContext\r
7947da3c
MK
115 )\r
116{\r
117 UINTN PFAddress;\r
7fa1376c
JY
118 UINTN GuardPageAddress;\r
119 UINTN CpuIndex;\r
7947da3c
MK
120\r
121 ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);\r
122\r
fe3a75bc 123 AcquireSpinLock (mPFLock);\r
7947da3c
MK
124\r
125 PFAddress = AsmReadCr2 ();\r
126\r
7fa1376c
JY
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
7947da3c 132 (PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) {\r
b8caae19 133 DumpCpuContext (InterruptType, SystemContext);\r
7fa1376c
JY
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
7fa1376c
JY
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
09afd9a4
JW
152\r
153 if (HEAP_GUARD_NONSTOP_MODE) {\r
154 GuardPagePFHandler (SystemContext.SystemContextIa32->ExceptionData);\r
155 goto Exit;\r
156 }\r
7fa1376c 157 }\r
7947da3c 158 CpuDeadLoop ();\r
3eb69b08 159 goto Exit;\r
7947da3c
MK
160 }\r
161\r
162 //\r
8bf0380e 163 // If a page fault occurs in non-SMRAM range.\r
7947da3c
MK
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
8bf0380e 168 DumpCpuContext (InterruptType, SystemContext);\r
717fb604 169 DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%x) out of SMM range after SMM is locked!\n", PFAddress));\r
7947da3c
MK
170 DEBUG_CODE (\r
171 DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);\r
172 );\r
173 CpuDeadLoop ();\r
3eb69b08 174 goto Exit;\r
7947da3c 175 }\r
09afd9a4
JW
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
3eb69b08 194 goto Exit;\r
09afd9a4
JW
195 }\r
196\r
d2fc7711 197 if (IsSmmCommBufferForbiddenAddress (PFAddress)) {\r
8bf0380e 198 DumpCpuContext (InterruptType, SystemContext);\r
d2fc7711
JY
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
3eb69b08 204 goto Exit;\r
d2fc7711 205 }\r
7947da3c
MK
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
b8caae19 214 DumpCpuContext (InterruptType, SystemContext);\r
7947da3c
MK
215 SmiDefaultPFHandler ();\r
216 }\r
217\r
09afd9a4 218Exit:\r
fe3a75bc 219 ReleaseSpinLock (mPFLock);\r
7947da3c 220}\r
717fb604
JY
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
3eb69b08 237 BOOLEAN CetEnabled;\r
717fb604 238\r
827330cc 239 //\r
1015fb3c 240 // Don't mark page table to read-only if heap guard is enabled.\r
827330cc
JW
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
1015fb3c
SZ
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
827330cc
JW
255 return ;\r
256 }\r
257\r
717fb604
JY
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
3eb69b08
JY
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
717fb604
JY
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
241f9149 283 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
717fb604
JY
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
241f9149 296 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
717fb604
JY
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
3eb69b08
JY
310 if (CetEnabled) {\r
311 //\r
312 // re-enable CET.\r
313 //\r
314 EnableCet();\r
315 }\r
717fb604
JY
316\r
317 return ;\r
318}\r
37f9fea5
VN
319\r
320/**\r
321 This function returns with no action for 32 bit.\r
322\r
323 @param[out] *Cr2 Pointer to variable to hold CR2 register value.\r
324**/\r
325VOID\r
326SaveCr2 (\r
327 OUT UINTN *Cr2\r
328 )\r
329{\r
330 return ;\r
331}\r
332\r
333/**\r
334 This function returns with no action for 32 bit.\r
335\r
336 @param[in] Cr2 Value to write into CR2 register.\r
337**/\r
338VOID\r
339RestoreCr2 (\r
340 IN UINTN Cr2\r
341 )\r
342{\r
343 return ;\r
344}\r