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