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