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