]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c
UefiCpuPkg/PiSmmCpuDxeSmm: Fix memory protection crash
[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
19UINT8 mPhysicalAddressBits;\r
20\r
21/**\r
22 Create PageTable for SMM use.\r
23\r
24 @return PageTable Address\r
25\r
26**/\r
27UINT32\r
28SmmInitPageTable (\r
29 VOID\r
30 )\r
31{\r
32 UINTN PageFaultHandlerHookAddress;\r
33 IA32_IDT_GATE_DESCRIPTOR *IdtEntry;\r
34 EFI_STATUS Status;\r
35\r
36 //\r
37 // Initialize spin lock\r
38 //\r
39 InitializeSpinLock (mPFLock);\r
40\r
41 mPhysicalAddressBits = 32;\r
42\r
43 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {\r
44 //\r
45 // Set own Page Fault entry instead of the default one, because SMM Profile\r
46 // feature depends on IRET instruction to do Single Step\r
47 //\r
48 PageFaultHandlerHookAddress = (UINTN)PageFaultIdtHandlerSmmProfile;\r
49 IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) gcSmiIdtr.Base;\r
50 IdtEntry += EXCEPT_IA32_PAGE_FAULT;\r
51 IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress;\r
52 IdtEntry->Bits.Reserved_0 = 0;\r
53 IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;\r
54 IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16);\r
55 } else {\r
56 //\r
57 // Register SMM Page Fault Handler\r
58 //\r
59 Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);\r
60 ASSERT_EFI_ERROR (Status);\r
61 }\r
62\r
63 //\r
64 // Additional SMM IDT initialization for SMM stack guard\r
65 //\r
66 if (FeaturePcdGet (PcdCpuSmmStackGuard)) {\r
67 InitializeIDTSmmStackGuard ();\r
68 }\r
69 return Gen4GPageTable (TRUE);\r
70}\r
71\r
72/**\r
73 Page Fault handler for SMM use.\r
74\r
75**/\r
76VOID\r
77SmiDefaultPFHandler (\r
78 VOID\r
79 )\r
80{\r
81 CpuDeadLoop ();\r
82}\r
83\r
84/**\r
85 ThePage Fault handler wrapper for SMM use.\r
86\r
87 @param InterruptType Defines the type of interrupt or exception that\r
88 occurred on the processor.This parameter is processor architecture specific.\r
89 @param SystemContext A pointer to the processor context when\r
90 the interrupt occurred on the processor.\r
91**/\r
92VOID\r
93EFIAPI\r
94SmiPFHandler (\r
95 IN EFI_EXCEPTION_TYPE InterruptType,\r
96 IN EFI_SYSTEM_CONTEXT SystemContext\r
97 )\r
98{\r
99 UINTN PFAddress;\r
100 UINTN GuardPageAddress;\r
101 UINTN CpuIndex;\r
102\r
103 ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);\r
104\r
105 AcquireSpinLock (mPFLock);\r
106\r
107 PFAddress = AsmReadCr2 ();\r
108\r
109 //\r
110 // If a page fault occurs in SMRAM range, it might be in a SMM stack guard page,\r
111 // or SMM page protection violation.\r
112 //\r
113 if ((PFAddress >= mCpuHotPlugData.SmrrBase) &&\r
114 (PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) {\r
115 DumpCpuContext (InterruptType, SystemContext);\r
116 CpuIndex = GetCpuIndex ();\r
117 GuardPageAddress = (mSmmStackArrayBase + EFI_PAGE_SIZE + CpuIndex * mSmmStackSize);\r
118 if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&\r
119 (PFAddress >= GuardPageAddress) &&\r
120 (PFAddress < (GuardPageAddress + EFI_PAGE_SIZE))) {\r
121 DEBUG ((DEBUG_ERROR, "SMM stack overflow!\n"));\r
122 } else {\r
123 if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {\r
124 DEBUG ((DEBUG_ERROR, "SMM exception at execution (0x%x)\n", PFAddress));\r
125 DEBUG_CODE (\r
126 DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);\r
127 );\r
128 } else {\r
129 DEBUG ((DEBUG_ERROR, "SMM exception at access (0x%x)\n", PFAddress));\r
130 DEBUG_CODE (\r
131 DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);\r
132 );\r
133 }\r
134 }\r
135 CpuDeadLoop ();\r
136 }\r
137\r
138 //\r
139 // If a page fault occurs in SMM range\r
140 //\r
141 if ((PFAddress < mCpuHotPlugData.SmrrBase) ||\r
142 (PFAddress >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) {\r
143 DumpCpuContext (InterruptType, SystemContext);\r
144 if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {\r
145 DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%x) out of SMM range after SMM is locked!\n", PFAddress));\r
146 DEBUG_CODE (\r
147 DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);\r
148 );\r
149 CpuDeadLoop ();\r
150 }\r
151 if (IsSmmCommBufferForbiddenAddress (PFAddress)) {\r
152 DEBUG ((DEBUG_ERROR, "Access SMM communication forbidden address (0x%x)!\n", PFAddress));\r
153 DEBUG_CODE (\r
154 DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);\r
155 );\r
156 CpuDeadLoop ();\r
157 }\r
158 }\r
159\r
160 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {\r
161 SmmProfilePFHandler (\r
162 SystemContext.SystemContextIa32->Eip,\r
163 SystemContext.SystemContextIa32->ExceptionData\r
164 );\r
165 } else {\r
166 DumpCpuContext (InterruptType, SystemContext);\r
167 SmiDefaultPFHandler ();\r
168 }\r
169\r
170 ReleaseSpinLock (mPFLock);\r
171}\r
172\r
173/**\r
174 This function sets memory attribute for page table.\r
175**/\r
176VOID\r
177SetPageTableAttributes (\r
178 VOID\r
179 )\r
180{\r
181 UINTN Index2;\r
182 UINTN Index3;\r
183 UINT64 *L1PageTable;\r
184 UINT64 *L2PageTable;\r
185 UINT64 *L3PageTable;\r
186 BOOLEAN IsSplitted;\r
187 BOOLEAN PageTableSplitted;\r
188\r
189 DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));\r
190\r
191 //\r
192 // Disable write protection, because we need mark page table to be write protected.\r
193 // We need *write* page table memory, to mark itself to be *read only*.\r
194 //\r
195 AsmWriteCr0 (AsmReadCr0() & ~CR0_WP);\r
196\r
197 do {\r
198 DEBUG ((DEBUG_INFO, "Start...\n"));\r
199 PageTableSplitted = FALSE;\r
200\r
201 L3PageTable = (UINT64 *)GetPageTableBase ();\r
202\r
203 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L3PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
204 PageTableSplitted = (PageTableSplitted || IsSplitted);\r
205\r
206 for (Index3 = 0; Index3 < 4; Index3++) {\r
207 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
208 if (L2PageTable == NULL) {\r
209 continue;\r
210 }\r
211\r
212 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L2PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
213 PageTableSplitted = (PageTableSplitted || IsSplitted);\r
214\r
215 for (Index2 = 0; Index2 < SIZE_4KB/sizeof(UINT64); Index2++) {\r
216 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {\r
217 // 2M\r
218 continue;\r
219 }\r
220 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
221 if (L1PageTable == NULL) {\r
222 continue;\r
223 }\r
224 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L1PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
225 PageTableSplitted = (PageTableSplitted || IsSplitted);\r
226 }\r
227 }\r
228 } while (PageTableSplitted);\r
229\r
230 //\r
231 // Enable write protection, after page table updated.\r
232 //\r
233 AsmWriteCr0 (AsmReadCr0() | CR0_WP);\r
234\r
235 return ;\r
236}\r