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