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 | }
|
---|