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