]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c
UefiCpuPkg PiSmmCpuDxeSmm: SMM profile and static paging mutual exclusion
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / Ia32 / PageTbl.c
1 /** @file
2 Page table manipulation functions for IA-32 processors
3
4 Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
6
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 **/
16
17 #include "PiSmmCpuDxeSmm.h"
18
19 /**
20 Create PageTable for SMM use.
21
22 @return PageTable Address
23
24 **/
25 UINT32
26 SmmInitPageTable (
27 VOID
28 )
29 {
30 UINTN PageFaultHandlerHookAddress;
31 IA32_IDT_GATE_DESCRIPTOR *IdtEntry;
32 EFI_STATUS Status;
33
34 //
35 // Initialize spin lock
36 //
37 InitializeSpinLock (mPFLock);
38
39 mPhysicalAddressBits = 32;
40
41 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
42 //
43 // Set own Page Fault entry instead of the default one, because SMM Profile
44 // feature depends on IRET instruction to do Single Step
45 //
46 PageFaultHandlerHookAddress = (UINTN)PageFaultIdtHandlerSmmProfile;
47 IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) gcSmiIdtr.Base;
48 IdtEntry += EXCEPT_IA32_PAGE_FAULT;
49 IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress;
50 IdtEntry->Bits.Reserved_0 = 0;
51 IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;
52 IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16);
53 } else {
54 //
55 // Register SMM Page Fault Handler
56 //
57 Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);
58 ASSERT_EFI_ERROR (Status);
59 }
60
61 //
62 // Additional SMM IDT initialization for SMM stack guard
63 //
64 if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
65 InitializeIDTSmmStackGuard ();
66 }
67 return Gen4GPageTable (TRUE);
68 }
69
70 /**
71 Page Fault handler for SMM use.
72
73 **/
74 VOID
75 SmiDefaultPFHandler (
76 VOID
77 )
78 {
79 CpuDeadLoop ();
80 }
81
82 /**
83 ThePage Fault handler wrapper for SMM use.
84
85 @param InterruptType Defines the type of interrupt or exception that
86 occurred on the processor.This parameter is processor architecture specific.
87 @param SystemContext A pointer to the processor context when
88 the interrupt occurred on the processor.
89 **/
90 VOID
91 EFIAPI
92 SmiPFHandler (
93 IN EFI_EXCEPTION_TYPE InterruptType,
94 IN EFI_SYSTEM_CONTEXT SystemContext
95 )
96 {
97 UINTN PFAddress;
98 UINTN GuardPageAddress;
99 UINTN CpuIndex;
100
101 ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);
102
103 AcquireSpinLock (mPFLock);
104
105 PFAddress = AsmReadCr2 ();
106
107 //
108 // If a page fault occurs in SMRAM range, it might be in a SMM stack guard page,
109 // or SMM page protection violation.
110 //
111 if ((PFAddress >= mCpuHotPlugData.SmrrBase) &&
112 (PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) {
113 DumpCpuContext (InterruptType, SystemContext);
114 CpuIndex = GetCpuIndex ();
115 GuardPageAddress = (mSmmStackArrayBase + EFI_PAGE_SIZE + CpuIndex * mSmmStackSize);
116 if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&
117 (PFAddress >= GuardPageAddress) &&
118 (PFAddress < (GuardPageAddress + EFI_PAGE_SIZE))) {
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 CpuDeadLoop ();
134 }
135
136 //
137 // If a page fault occurs in non-SMRAM range.
138 //
139 if ((PFAddress < mCpuHotPlugData.SmrrBase) ||
140 (PFAddress >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) {
141 if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {
142 DumpCpuContext (InterruptType, SystemContext);
143 DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%x) out of SMM range after SMM is locked!\n", PFAddress));
144 DEBUG_CODE (
145 DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);
146 );
147 CpuDeadLoop ();
148 }
149 if (IsSmmCommBufferForbiddenAddress (PFAddress)) {
150 DumpCpuContext (InterruptType, SystemContext);
151 DEBUG ((DEBUG_ERROR, "Access SMM communication forbidden address (0x%x)!\n", PFAddress));
152 DEBUG_CODE (
153 DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);
154 );
155 CpuDeadLoop ();
156 }
157 }
158
159 //
160 // If NULL pointer was just accessed
161 //
162 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0 &&
163 (PFAddress < EFI_PAGE_SIZE)) {
164 DumpCpuContext (InterruptType, SystemContext);
165 DEBUG ((DEBUG_ERROR, "!!! NULL pointer access !!!\n"));
166 DEBUG_CODE (
167 DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);
168 );
169 CpuDeadLoop ();
170 }
171
172 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
173 SmmProfilePFHandler (
174 SystemContext.SystemContextIa32->Eip,
175 SystemContext.SystemContextIa32->ExceptionData
176 );
177 } else {
178 DumpCpuContext (InterruptType, SystemContext);
179 SmiDefaultPFHandler ();
180 }
181
182 ReleaseSpinLock (mPFLock);
183 }
184
185 /**
186 This function sets memory attribute for page table.
187 **/
188 VOID
189 SetPageTableAttributes (
190 VOID
191 )
192 {
193 UINTN Index2;
194 UINTN Index3;
195 UINT64 *L1PageTable;
196 UINT64 *L2PageTable;
197 UINT64 *L3PageTable;
198 BOOLEAN IsSplitted;
199 BOOLEAN PageTableSplitted;
200
201 //
202 // Don't mark page table to read-only if heap guard is enabled.
203 //
204 // BIT2: SMM page guard enabled
205 // BIT3: SMM pool guard enabled
206 //
207 if ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) {
208 DEBUG ((DEBUG_INFO, "Don't mark page table to read-only as heap guard is enabled\n"));
209 return ;
210 }
211
212 //
213 // Don't mark page table to read-only if SMM profile is enabled.
214 //
215 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
216 DEBUG ((DEBUG_INFO, "Don't mark page table to read-only as SMM profile is enabled\n"));
217 return ;
218 }
219
220 DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));
221
222 //
223 // Disable write protection, because we need mark page table to be write protected.
224 // We need *write* page table memory, to mark itself to be *read only*.
225 //
226 AsmWriteCr0 (AsmReadCr0() & ~CR0_WP);
227
228 do {
229 DEBUG ((DEBUG_INFO, "Start...\n"));
230 PageTableSplitted = FALSE;
231
232 L3PageTable = (UINT64 *)GetPageTableBase ();
233
234 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L3PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
235 PageTableSplitted = (PageTableSplitted || IsSplitted);
236
237 for (Index3 = 0; Index3 < 4; Index3++) {
238 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
239 if (L2PageTable == NULL) {
240 continue;
241 }
242
243 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L2PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
244 PageTableSplitted = (PageTableSplitted || IsSplitted);
245
246 for (Index2 = 0; Index2 < SIZE_4KB/sizeof(UINT64); Index2++) {
247 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
248 // 2M
249 continue;
250 }
251 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
252 if (L1PageTable == NULL) {
253 continue;
254 }
255 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L1PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
256 PageTableSplitted = (PageTableSplitted || IsSplitted);
257 }
258 }
259 } while (PageTableSplitted);
260
261 //
262 // Enable write protection, after page table updated.
263 //
264 AsmWriteCr0 (AsmReadCr0() | CR0_WP);
265
266 return ;
267 }