UefiCpuPkg/PiSmmCpuDxeSmm: Fix memory protection crash
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / Ia32 / PageTbl.c
CommitLineData
7947da3c
MK
1/** @file\r
2Page table manipulation functions for IA-32 processors\r
3\r
b8caae19 4Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>\r
241f9149
LD
5Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
6\r
7947da3c
MK
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
714c2603
SZ
19UINT8 mPhysicalAddressBits;\r
20\r
7947da3c
MK
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
5c88af79 34 EFI_STATUS Status;\r
7947da3c
MK
35\r
36 //\r
37 // Initialize spin lock\r
38 //\r
fe3a75bc 39 InitializeSpinLock (mPFLock);\r
7947da3c 40\r
714c2603
SZ
41 mPhysicalAddressBits = 32;\r
42\r
7947da3c
MK
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
5c88af79
JF
59 Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);\r
60 ASSERT_EFI_ERROR (Status);\r
7947da3c
MK
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
717fb604 69 return Gen4GPageTable (TRUE);\r
7947da3c
MK
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
b8caae19
JF
95 IN EFI_EXCEPTION_TYPE InterruptType,\r
96 IN EFI_SYSTEM_CONTEXT SystemContext\r
7947da3c
MK
97 )\r
98{\r
99 UINTN PFAddress;\r
7fa1376c
JY
100 UINTN GuardPageAddress;\r
101 UINTN CpuIndex;\r
7947da3c
MK
102\r
103 ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);\r
104\r
fe3a75bc 105 AcquireSpinLock (mPFLock);\r
7947da3c
MK
106\r
107 PFAddress = AsmReadCr2 ();\r
108\r
7fa1376c
JY
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
7947da3c 114 (PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) {\r
b8caae19 115 DumpCpuContext (InterruptType, SystemContext);\r
7fa1376c
JY
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
7fa1376c
JY
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
7947da3c
MK
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
b8caae19 143 DumpCpuContext (InterruptType, SystemContext);\r
7947da3c 144 if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {\r
717fb604 145 DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%x) out of SMM range after SMM is locked!\n", PFAddress));\r
7947da3c
MK
146 DEBUG_CODE (\r
147 DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);\r
148 );\r
149 CpuDeadLoop ();\r
150 }\r
d2fc7711
JY
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
7947da3c
MK
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
b8caae19 166 DumpCpuContext (InterruptType, SystemContext);\r
7947da3c
MK
167 SmiDefaultPFHandler ();\r
168 }\r
169\r
fe3a75bc 170 ReleaseSpinLock (mPFLock);\r
7947da3c 171}\r
717fb604
JY
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
241f9149 207 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
717fb604
JY
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
241f9149 220 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
717fb604
JY
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