]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c
MdeModulePkg/Universal/CapsulePei: Add support for PCD PcdPteMemoryEncryptionAddressO...
[mirror_edk2.git] / MdeModulePkg / Universal / CapsulePei / X64 / X64Entry.c
CommitLineData
ab7017fe 1/** @file\r
2 The X64 entrypoint is used to process capsule in long mode.\r
3\r
359cb1a3 4Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>\r
89a286ce
LD
5Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
6\r
ab7017fe 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
ab7017fe 17#include <Library/DebugLib.h>\r
1e172d6b 18#include <Library/BaseMemoryLib.h>\r
19#include <Library/CpuExceptionHandlerLib.h>\r
933d80a1 20#include <Library/DebugAgentLib.h>\r
ab7017fe 21#include "CommonHeader.h"\r
22\r
933d80a1 23#define EXCEPTION_VECTOR_NUMBER 0x22\r
1e172d6b 24\r
716087e2
SZ
25#define IA32_PG_P BIT0\r
26#define IA32_PG_RW BIT1\r
27#define IA32_PG_PS BIT7\r
28\r
29typedef struct _PAGE_FAULT_CONTEXT {\r
30 BOOLEAN Page1GSupport;\r
31 UINT64 PhyMask;\r
32 UINTN PageFaultBuffer;\r
33 UINTN PageFaultIndex;\r
89a286ce 34 UINT64 AddressEncMask;\r
716087e2
SZ
35 //\r
36 // Store the uplink information for each page being used.\r
37 //\r
38 UINT64 *PageFaultUplink[EXTRA_PAGE_TABLE_PAGES];\r
39 VOID *OriginalHandler;\r
40} PAGE_FAULT_CONTEXT;\r
41\r
42typedef struct _PAGE_FAULT_IDT_TABLE {\r
43 PAGE_FAULT_CONTEXT PageFaultContext;\r
44 IA32_IDT_GATE_DESCRIPTOR IdtEntryTable[EXCEPTION_VECTOR_NUMBER];\r
45} PAGE_FAULT_IDT_TABLE;\r
46\r
47/**\r
48 Page fault handler.\r
49\r
50**/\r
51VOID\r
52EFIAPI\r
53PageFaultHandlerHook (\r
54 VOID\r
55 );\r
56\r
57/**\r
58 Hook IDT with our page fault handler so that the on-demand paging works on page fault.\r
59\r
60 @param[in, out] IdtEntry Pointer to IDT entry.\r
61 @param[in, out] PageFaultContext Pointer to page fault context.\r
62\r
63**/\r
64VOID\r
65HookPageFaultHandler (\r
66 IN OUT IA32_IDT_GATE_DESCRIPTOR *IdtEntry,\r
67 IN OUT PAGE_FAULT_CONTEXT *PageFaultContext\r
68 )\r
69{\r
70 UINT32 RegEax;\r
71 UINT8 PhysicalAddressBits;\r
72 UINTN PageFaultHandlerHookAddress;\r
73\r
74 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
75 if (RegEax >= 0x80000008) {\r
76 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);\r
77 PhysicalAddressBits = (UINT8) RegEax;\r
78 } else {\r
79 PhysicalAddressBits = 36;\r
80 }\r
81 PageFaultContext->PhyMask = LShiftU64 (1, PhysicalAddressBits) - 1;\r
82 PageFaultContext->PhyMask &= (1ull << 48) - SIZE_4KB;\r
83\r
84 //\r
85 // Set Page Fault entry to catch >4G access\r
86 //\r
87 PageFaultHandlerHookAddress = (UINTN)PageFaultHandlerHook;\r
88 PageFaultContext->OriginalHandler = (VOID *)(UINTN)(LShiftU64 (IdtEntry->Bits.OffsetUpper, 32) + IdtEntry->Bits.OffsetLow + (IdtEntry->Bits.OffsetHigh << 16));\r
89 IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress;\r
90 IdtEntry->Bits.Selector = (UINT16)AsmReadCs ();\r
91 IdtEntry->Bits.Reserved_0 = 0;\r
92 IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;\r
93 IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16);\r
94 IdtEntry->Bits.OffsetUpper = (UINT32)(PageFaultHandlerHookAddress >> 32);\r
95 IdtEntry->Bits.Reserved_1 = 0;\r
96\r
97 if (PageFaultContext->Page1GSupport) {\r
98 PageFaultContext->PageFaultBuffer = (UINTN)(AsmReadCr3 () & PageFaultContext->PhyMask) + EFI_PAGES_TO_SIZE(2);\r
99 }else {\r
100 PageFaultContext->PageFaultBuffer = (UINTN)(AsmReadCr3 () & PageFaultContext->PhyMask) + EFI_PAGES_TO_SIZE(6);\r
101 }\r
102 PageFaultContext->PageFaultIndex = 0;\r
103 ZeroMem (PageFaultContext->PageFaultUplink, sizeof (PageFaultContext->PageFaultUplink));\r
104}\r
105\r
106/**\r
107 Acquire page for page fault.\r
108\r
109 @param[in, out] PageFaultContext Pointer to page fault context.\r
110 @param[in, out] Uplink Pointer to up page table entry.\r
111\r
112**/\r
113VOID\r
114AcquirePage (\r
115 IN OUT PAGE_FAULT_CONTEXT *PageFaultContext,\r
116 IN OUT UINT64 *Uplink\r
117 )\r
118{\r
119 UINTN Address;\r
89a286ce 120 UINT64 AddressEncMask;\r
716087e2
SZ
121\r
122 Address = PageFaultContext->PageFaultBuffer + EFI_PAGES_TO_SIZE (PageFaultContext->PageFaultIndex);\r
123 ZeroMem ((VOID *) Address, EFI_PAGES_TO_SIZE (1));\r
124\r
89a286ce
LD
125 AddressEncMask = PageFaultContext->AddressEncMask;\r
126\r
716087e2
SZ
127 //\r
128 // Cut the previous uplink if it exists and wasn't overwritten.\r
129 //\r
89a286ce
LD
130 if ((PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] != NULL) &&\r
131 ((*PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] & ~AddressEncMask & PageFaultContext->PhyMask) == Address)) {\r
716087e2
SZ
132 *PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] = 0;\r
133 }\r
134\r
135 //\r
136 // Link & Record the current uplink.\r
137 //\r
89a286ce 138 *Uplink = Address | AddressEncMask | IA32_PG_P | IA32_PG_RW;\r
716087e2
SZ
139 PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] = Uplink;\r
140\r
141 PageFaultContext->PageFaultIndex = (PageFaultContext->PageFaultIndex + 1) % EXTRA_PAGE_TABLE_PAGES;\r
142}\r
143\r
144/**\r
145 The page fault handler that on-demand read >4G memory/MMIO.\r
146\r
147 @retval NULL The page fault is correctly handled.\r
148 @retval OriginalHandler The page fault is not handled and is passed through to original handler.\r
149\r
150**/\r
151VOID *\r
152EFIAPI\r
153PageFaultHandler (\r
154 VOID\r
155 )\r
156{\r
157 IA32_DESCRIPTOR Idtr;\r
158 PAGE_FAULT_CONTEXT *PageFaultContext;\r
159 UINT64 PhyMask;\r
160 UINT64 *PageTable;\r
161 UINT64 PFAddress;\r
162 UINTN PTIndex;\r
89a286ce 163 UINT64 AddressEncMask;\r
716087e2
SZ
164\r
165 //\r
166 // Get the IDT Descriptor.\r
167 //\r
168 AsmReadIdtr ((IA32_DESCRIPTOR *) &Idtr); \r
169 //\r
170 // Then get page fault context by IDT Descriptor.\r
171 //\r
172 PageFaultContext = (PAGE_FAULT_CONTEXT *) (UINTN) (Idtr.Base - sizeof (PAGE_FAULT_CONTEXT));\r
173 PhyMask = PageFaultContext->PhyMask;\r
89a286ce 174 AddressEncMask = PageFaultContext->AddressEncMask;\r
716087e2
SZ
175\r
176 PFAddress = AsmReadCr2 ();\r
177 DEBUG ((EFI_D_ERROR, "CapsuleX64 - PageFaultHandler: Cr2 - %lx\n", PFAddress));\r
178\r
179 if (PFAddress >= PhyMask + SIZE_4KB) {\r
180 return PageFaultContext->OriginalHandler;\r
181 }\r
182 PFAddress &= PhyMask;\r
183\r
184 PageTable = (UINT64*)(UINTN)(AsmReadCr3 () & PhyMask);\r
185\r
186 PTIndex = BitFieldRead64 (PFAddress, 39, 47);\r
187 // PML4E\r
188 if ((PageTable[PTIndex] & IA32_PG_P) == 0) {\r
189 AcquirePage (PageFaultContext, &PageTable[PTIndex]);\r
190 }\r
89a286ce 191 PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~AddressEncMask & PhyMask);\r
716087e2
SZ
192 PTIndex = BitFieldRead64 (PFAddress, 30, 38);\r
193 // PDPTE\r
194 if (PageFaultContext->Page1GSupport) {\r
89a286ce 195 PageTable[PTIndex] = ((PFAddress | AddressEncMask) & ~((1ull << 30) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;\r
716087e2
SZ
196 } else {\r
197 if ((PageTable[PTIndex] & IA32_PG_P) == 0) {\r
198 AcquirePage (PageFaultContext, &PageTable[PTIndex]);\r
199 }\r
89a286ce 200 PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~AddressEncMask & PhyMask);\r
716087e2
SZ
201 PTIndex = BitFieldRead64 (PFAddress, 21, 29);\r
202 // PD\r
89a286ce 203 PageTable[PTIndex] = ((PFAddress | AddressEncMask) & ~((1ull << 21) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;\r
716087e2
SZ
204 }\r
205\r
206 return NULL;\r
207}\r
208\r
209\r
ab7017fe 210/**\r
211 The X64 entrypoint is used to process capsule in long mode then\r
212 return to 32-bit protected mode.\r
213\r
214 @param EntrypointContext Pointer to the context of long mode.\r
215 @param ReturnContext Pointer to the context of 32-bit protected mode.\r
216\r
217 @retval This function should never return actually.\r
218\r
219**/\r
220EFI_STATUS\r
221EFIAPI\r
222_ModuleEntryPoint (\r
223 SWITCH_32_TO_64_CONTEXT *EntrypointContext,\r
224 SWITCH_64_TO_32_CONTEXT *ReturnContext\r
225)\r
226{\r
1e172d6b 227 EFI_STATUS Status;\r
228 IA32_DESCRIPTOR Ia32Idtr;\r
229 IA32_DESCRIPTOR X64Idtr;\r
716087e2
SZ
230 PAGE_FAULT_IDT_TABLE PageFaultIdtTable;\r
231 IA32_IDT_GATE_DESCRIPTOR *IdtEntry;\r
1e172d6b 232\r
233 //\r
234 // Save the IA32 IDT Descriptor\r
235 //\r
236 AsmReadIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr); \r
237\r
238 //\r
239 // Setup X64 IDT table\r
240 //\r
716087e2
SZ
241 ZeroMem (PageFaultIdtTable.IdtEntryTable, sizeof (IA32_IDT_GATE_DESCRIPTOR) * EXCEPTION_VECTOR_NUMBER);\r
242 X64Idtr.Base = (UINTN) PageFaultIdtTable.IdtEntryTable;\r
1e172d6b 243 X64Idtr.Limit = (UINT16) (sizeof (IA32_IDT_GATE_DESCRIPTOR) * EXCEPTION_VECTOR_NUMBER - 1);\r
244 AsmWriteIdtr ((IA32_DESCRIPTOR *) &X64Idtr); \r
245\r
246 //\r
247 // Setup the default CPU exception handlers\r
248 //\r
57f360f2
JF
249 Status = InitializeCpuExceptionHandlers (NULL);\r
250 ASSERT_EFI_ERROR (Status);\r
716087e2
SZ
251\r
252 //\r
253 // Hook page fault handler to handle >4G request.\r
254 //\r
255 PageFaultIdtTable.PageFaultContext.Page1GSupport = EntrypointContext->Page1GSupport;\r
89a286ce 256 PageFaultIdtTable.PageFaultContext.AddressEncMask = EntrypointContext->AddressEncMask;\r
716087e2
SZ
257 IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) (X64Idtr.Base + (14 * sizeof (IA32_IDT_GATE_DESCRIPTOR)));\r
258 HookPageFaultHandler (IdtEntry, &(PageFaultIdtTable.PageFaultContext));\r
259\r
933d80a1 260 //\r
261 // Initialize Debug Agent to support source level debug\r
262 //\r
263 InitializeDebugAgent (DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64, (VOID *) &Ia32Idtr, NULL);\r
ab7017fe 264\r
265 //\r
266 // Call CapsuleDataCoalesce to process capsule.\r
267 //\r
268 Status = CapsuleDataCoalesce (\r
269 NULL,\r
270 (EFI_PHYSICAL_ADDRESS *) (UINTN) EntrypointContext->BlockListAddr,\r
359cb1a3 271 (MEMORY_RESOURCE_DESCRIPTOR *) (UINTN) EntrypointContext->MemoryResource,\r
ab7017fe 272 (VOID **) (UINTN) EntrypointContext->MemoryBase64Ptr,\r
273 (UINTN *) (UINTN) EntrypointContext->MemorySize64Ptr\r
274 );\r
275 \r
276 ReturnContext->ReturnStatus = Status;\r
277\r
6fb389d0
JF
278 DEBUG ((\r
279 DEBUG_INFO,\r
280 "%a() Stack Base: 0x%lx, Stack Size: 0x%lx\n",\r
281 __FUNCTION__,\r
282 EntrypointContext->StackBufferBase,\r
283 EntrypointContext->StackBufferLength\r
284 ));\r
285\r
933d80a1 286 //\r
287 // Disable interrupt of Debug timer, since the new IDT table cannot work in long mode\r
288 //\r
289 SaveAndSetDebugTimerInterrupt (FALSE);\r
1e172d6b 290 //\r
291 // Restore IA32 IDT table\r
292 //\r
293 AsmWriteIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr); \r
294 \r
ab7017fe 295 //\r
296 // Finish to coalesce capsule, and return to 32-bit mode.\r
297 //\r
298 AsmDisablePaging64 (\r
299 ReturnContext->ReturnCs,\r
300 (UINT32) ReturnContext->ReturnEntryPoint,\r
301 (UINT32) (UINTN) EntrypointContext,\r
302 (UINT32) (UINTN) ReturnContext,\r
303 (UINT32) (EntrypointContext->StackBufferBase + EntrypointContext->StackBufferLength)\r
304 ); \r
305 \r
306 //\r
307 // Should never be here.\r
308 //\r
309 ASSERT (FALSE);\r
310 return EFI_SUCCESS;\r
89a286ce 311}\r