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