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