• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   The X64 entrypoint is used to process capsule in long mode.
3 
4 Copyright (c) 2011 - 2015, 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
HookPageFaultHandler(IN OUT IA32_IDT_GATE_DESCRIPTOR * IdtEntry,IN OUT PAGE_FAULT_CONTEXT * PageFaultContext)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
AcquirePage(IN OUT PAGE_FAULT_CONTEXT * PageFaultContext,IN OUT UINT64 * Uplink)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
PageFaultHandler(VOID)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
_ModuleEntryPoint(SWITCH_32_TO_64_CONTEXT * EntrypointContext,SWITCH_64_TO_32_CONTEXT * ReturnContext)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              (VOID **) (UINTN) EntrypointContext->MemoryBase64Ptr,
262              (UINTN *) (UINTN) EntrypointContext->MemorySize64Ptr
263              );
264 
265   ReturnContext->ReturnStatus = Status;
266 
267   //
268   // Disable interrupt of Debug timer, since the new IDT table cannot work in long mode
269   //
270   SaveAndSetDebugTimerInterrupt (FALSE);
271   //
272   // Restore IA32 IDT table
273   //
274   AsmWriteIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr);
275 
276   //
277   // Finish to coalesce capsule, and return to 32-bit mode.
278   //
279   AsmDisablePaging64 (
280     ReturnContext->ReturnCs,
281     (UINT32) ReturnContext->ReturnEntryPoint,
282     (UINT32) (UINTN) EntrypointContext,
283     (UINT32) (UINTN) ReturnContext,
284     (UINT32) (EntrypointContext->StackBufferBase + EntrypointContext->StackBufferLength)
285     );
286 
287   //
288   // Should never be here.
289   //
290   ASSERT (FALSE);
291   return EFI_SUCCESS;
292 }