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