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