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