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