]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c
MdePkg/ProcessorBind.h AARCH64: limit MAX_ADDRESS to 48 bits
[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 HEAP_GUARD_NONSTOP_MODE ||
43 NULL_DETECTION_NONSTOP_MODE) {
44 //
45 // Set own Page Fault entry instead of the default one, because SMM Profile
46 // feature depends on IRET instruction to do Single Step
47 //
48 PageFaultHandlerHookAddress = (UINTN)PageFaultIdtHandlerSmmProfile;
49 IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) gcSmiIdtr.Base;
50 IdtEntry += EXCEPT_IA32_PAGE_FAULT;
51 IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress;
52 IdtEntry->Bits.Reserved_0 = 0;
53 IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;
54 IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16);
55 } else {
56 //
57 // Register SMM Page Fault Handler
58 //
59 Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);
60 ASSERT_EFI_ERROR (Status);
61 }
62
63 //
64 // Additional SMM IDT initialization for SMM stack guard
65 //
66 if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
67 InitializeIDTSmmStackGuard ();
68 }
69 return Gen4GPageTable (TRUE);
70 }
71
72 /**
73 Page Fault handler for SMM use.
74
75 **/
76 VOID
77 SmiDefaultPFHandler (
78 VOID
79 )
80 {
81 CpuDeadLoop ();
82 }
83
84 /**
85 ThePage Fault handler wrapper for SMM use.
86
87 @param InterruptType Defines the type of interrupt or exception that
88 occurred on the processor.This parameter is processor architecture specific.
89 @param SystemContext A pointer to the processor context when
90 the interrupt occurred on the processor.
91 **/
92 VOID
93 EFIAPI
94 SmiPFHandler (
95 IN EFI_EXCEPTION_TYPE InterruptType,
96 IN EFI_SYSTEM_CONTEXT SystemContext
97 )
98 {
99 UINTN PFAddress;
100 UINTN GuardPageAddress;
101 UINTN CpuIndex;
102
103 ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);
104
105 AcquireSpinLock (mPFLock);
106
107 PFAddress = AsmReadCr2 ();
108
109 //
110 // If a page fault occurs in SMRAM range, it might be in a SMM stack guard page,
111 // or SMM page protection violation.
112 //
113 if ((PFAddress >= mCpuHotPlugData.SmrrBase) &&
114 (PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) {
115 DumpCpuContext (InterruptType, SystemContext);
116 CpuIndex = GetCpuIndex ();
117 GuardPageAddress = (mSmmStackArrayBase + EFI_PAGE_SIZE + CpuIndex * mSmmStackSize);
118 if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&
119 (PFAddress >= GuardPageAddress) &&
120 (PFAddress < (GuardPageAddress + EFI_PAGE_SIZE))) {
121 DEBUG ((DEBUG_ERROR, "SMM stack overflow!\n"));
122 } else {
123 if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {
124 DEBUG ((DEBUG_ERROR, "SMM exception at execution (0x%x)\n", PFAddress));
125 DEBUG_CODE (
126 DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);
127 );
128 } else {
129 DEBUG ((DEBUG_ERROR, "SMM exception at access (0x%x)\n", PFAddress));
130 DEBUG_CODE (
131 DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);
132 );
133 }
134
135 if (HEAP_GUARD_NONSTOP_MODE) {
136 GuardPagePFHandler (SystemContext.SystemContextIa32->ExceptionData);
137 goto Exit;
138 }
139 }
140 CpuDeadLoop ();
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 if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {
149 DumpCpuContext (InterruptType, SystemContext);
150 DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%x) out of SMM range after SMM is locked!\n", PFAddress));
151 DEBUG_CODE (
152 DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);
153 );
154 CpuDeadLoop ();
155 }
156
157 //
158 // If NULL pointer was just accessed
159 //
160 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0 &&
161 (PFAddress < EFI_PAGE_SIZE)) {
162 DumpCpuContext (InterruptType, SystemContext);
163 DEBUG ((DEBUG_ERROR, "!!! NULL pointer access !!!\n"));
164 DEBUG_CODE (
165 DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);
166 );
167
168 if (NULL_DETECTION_NONSTOP_MODE) {
169 GuardPagePFHandler (SystemContext.SystemContextIa32->ExceptionData);
170 goto Exit;
171 }
172
173 CpuDeadLoop ();
174 }
175
176 if (IsSmmCommBufferForbiddenAddress (PFAddress)) {
177 DumpCpuContext (InterruptType, SystemContext);
178 DEBUG ((DEBUG_ERROR, "Access SMM communication forbidden address (0x%x)!\n", PFAddress));
179 DEBUG_CODE (
180 DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);
181 );
182 CpuDeadLoop ();
183 }
184 }
185
186 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
187 SmmProfilePFHandler (
188 SystemContext.SystemContextIa32->Eip,
189 SystemContext.SystemContextIa32->ExceptionData
190 );
191 } else {
192 DumpCpuContext (InterruptType, SystemContext);
193 SmiDefaultPFHandler ();
194 }
195
196 Exit:
197 ReleaseSpinLock (mPFLock);
198 }
199
200 /**
201 This function sets memory attribute for page table.
202 **/
203 VOID
204 SetPageTableAttributes (
205 VOID
206 )
207 {
208 UINTN Index2;
209 UINTN Index3;
210 UINT64 *L1PageTable;
211 UINT64 *L2PageTable;
212 UINT64 *L3PageTable;
213 BOOLEAN IsSplitted;
214 BOOLEAN PageTableSplitted;
215
216 //
217 // Don't mark page table to read-only if heap guard is enabled.
218 //
219 // BIT2: SMM page guard enabled
220 // BIT3: SMM pool guard enabled
221 //
222 if ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) {
223 DEBUG ((DEBUG_INFO, "Don't mark page table to read-only as heap guard is enabled\n"));
224 return ;
225 }
226
227 //
228 // Don't mark page table to read-only if SMM profile is enabled.
229 //
230 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
231 DEBUG ((DEBUG_INFO, "Don't mark page table to read-only as SMM profile is enabled\n"));
232 return ;
233 }
234
235 DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));
236
237 //
238 // Disable write protection, because we need mark page table to be write protected.
239 // We need *write* page table memory, to mark itself to be *read only*.
240 //
241 AsmWriteCr0 (AsmReadCr0() & ~CR0_WP);
242
243 do {
244 DEBUG ((DEBUG_INFO, "Start...\n"));
245 PageTableSplitted = FALSE;
246
247 L3PageTable = (UINT64 *)GetPageTableBase ();
248
249 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L3PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
250 PageTableSplitted = (PageTableSplitted || IsSplitted);
251
252 for (Index3 = 0; Index3 < 4; Index3++) {
253 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
254 if (L2PageTable == NULL) {
255 continue;
256 }
257
258 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L2PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
259 PageTableSplitted = (PageTableSplitted || IsSplitted);
260
261 for (Index2 = 0; Index2 < SIZE_4KB/sizeof(UINT64); Index2++) {
262 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
263 // 2M
264 continue;
265 }
266 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
267 if (L1PageTable == NULL) {
268 continue;
269 }
270 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L1PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
271 PageTableSplitted = (PageTableSplitted || IsSplitted);
272 }
273 }
274 } while (PageTableSplitted);
275
276 //
277 // Enable write protection, after page table updated.
278 //
279 AsmWriteCr0 (AsmReadCr0() | CR0_WP);
280
281 return ;
282 }