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