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