• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Ia32-specific functionality for DxeLoad.
3 
4 Copyright (c) 2006 - 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 "DxeIpl.h"
16 #include "VirtualMemory.h"
17 
18 #define IDT_ENTRY_COUNT       32
19 
20 typedef struct _X64_IDT_TABLE {
21   //
22   // Reserved 4 bytes preceding PeiService and IdtTable,
23   // since IDT base address should be 8-byte alignment.
24   //
25   UINT32                   Reserved;
26   CONST EFI_PEI_SERVICES   **PeiService;
27   X64_IDT_GATE_DESCRIPTOR  IdtTable[IDT_ENTRY_COUNT];
28 } X64_IDT_TABLE;
29 
30 //
31 // Global Descriptor Table (GDT)
32 //
33 GLOBAL_REMOVE_IF_UNREFERENCED IA32_GDT gGdtEntries[] = {
34 /* selector { Global Segment Descriptor                              } */
35 /* 0x00 */  {{0,      0,  0,  0,    0,  0,  0,  0,    0,  0, 0,  0,  0}}, //null descriptor
36 /* 0x08 */  {{0xffff, 0,  0,  0x2,  1,  0,  1,  0xf,  0,  0, 1,  1,  0}}, //linear data segment descriptor
37 /* 0x10 */  {{0xffff, 0,  0,  0xf,  1,  0,  1,  0xf,  0,  0, 1,  1,  0}}, //linear code segment descriptor
38 /* 0x18 */  {{0xffff, 0,  0,  0x3,  1,  0,  1,  0xf,  0,  0, 1,  1,  0}}, //system data segment descriptor
39 /* 0x20 */  {{0xffff, 0,  0,  0xa,  1,  0,  1,  0xf,  0,  0, 1,  1,  0}}, //system code segment descriptor
40 /* 0x28 */  {{0,      0,  0,  0,    0,  0,  0,  0,    0,  0, 0,  0,  0}}, //spare segment descriptor
41 /* 0x30 */  {{0xffff, 0,  0,  0x2,  1,  0,  1,  0xf,  0,  0, 1,  1,  0}}, //system data segment descriptor
42 /* 0x38 */  {{0xffff, 0,  0,  0xa,  1,  0,  1,  0xf,  0,  1, 0,  1,  0}}, //system code segment descriptor
43 /* 0x40 */  {{0,      0,  0,  0,    0,  0,  0,  0,    0,  0, 0,  0,  0}}, //spare segment descriptor
44 };
45 
46 //
47 // IA32 Gdt register
48 //
49 GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR gGdt = {
50   sizeof (gGdtEntries) - 1,
51   (UINTN) gGdtEntries
52   };
53 
54 GLOBAL_REMOVE_IF_UNREFERENCED  IA32_DESCRIPTOR gLidtDescriptor = {
55   sizeof (X64_IDT_GATE_DESCRIPTOR) * IDT_ENTRY_COUNT - 1,
56   0
57 };
58 
59 /**
60   Allocates and fills in the Page Directory and Page Table Entries to
61   establish a 4G page table.
62 
63   @param[in] StackBase  Stack base address.
64   @param[in] StackSize  Stack size.
65 
66   @return The address of page table.
67 
68 **/
69 UINTN
Create4GPageTablesIa32Pae(IN EFI_PHYSICAL_ADDRESS StackBase,IN UINTN StackSize)70 Create4GPageTablesIa32Pae (
71   IN EFI_PHYSICAL_ADDRESS   StackBase,
72   IN UINTN                  StackSize
73   )
74 {
75   UINT8                                         PhysicalAddressBits;
76   EFI_PHYSICAL_ADDRESS                          PhysicalAddress;
77   UINTN                                         IndexOfPdpEntries;
78   UINTN                                         IndexOfPageDirectoryEntries;
79   UINT32                                        NumberOfPdpEntriesNeeded;
80   PAGE_MAP_AND_DIRECTORY_POINTER                *PageMap;
81   PAGE_MAP_AND_DIRECTORY_POINTER                *PageDirectoryPointerEntry;
82   PAGE_TABLE_ENTRY                              *PageDirectoryEntry;
83   UINTN                                         TotalPagesNum;
84   UINTN                                         PageAddress;
85 
86   PhysicalAddressBits = 32;
87 
88   //
89   // Calculate the table entries needed.
90   //
91   NumberOfPdpEntriesNeeded = (UINT32) LShiftU64 (1, (PhysicalAddressBits - 30));
92 
93   TotalPagesNum = NumberOfPdpEntriesNeeded + 1;
94   PageAddress = (UINTN) AllocatePages (TotalPagesNum);
95   ASSERT (PageAddress != 0);
96 
97   PageMap = (VOID *) PageAddress;
98   PageAddress += SIZE_4KB;
99 
100   PageDirectoryPointerEntry = PageMap;
101   PhysicalAddress = 0;
102 
103   for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
104     //
105     // Each Directory Pointer entries points to a page of Page Directory entires.
106     // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
107     //
108     PageDirectoryEntry = (VOID *) PageAddress;
109     PageAddress += SIZE_4KB;
110 
111     //
112     // Fill in a Page Directory Pointer Entries
113     //
114     PageDirectoryPointerEntry->Uint64 = (UINT64) (UINTN) PageDirectoryEntry;
115     PageDirectoryPointerEntry->Bits.Present = 1;
116 
117     for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PhysicalAddress += SIZE_2MB) {
118       if ((PhysicalAddress < StackBase + StackSize) && ((PhysicalAddress + SIZE_2MB) > StackBase)) {
119         //
120         // Need to split this 2M page that covers stack range.
121         //
122         Split2MPageTo4K (PhysicalAddress, (UINT64 *) PageDirectoryEntry, StackBase, StackSize);
123       } else {
124         //
125         // Fill in the Page Directory entries
126         //
127         PageDirectoryEntry->Uint64 = (UINT64) PhysicalAddress;
128         PageDirectoryEntry->Bits.ReadWrite = 1;
129         PageDirectoryEntry->Bits.Present = 1;
130         PageDirectoryEntry->Bits.MustBe1 = 1;
131       }
132     }
133   }
134 
135   for (; IndexOfPdpEntries < 512; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
136     ZeroMem (
137       PageDirectoryPointerEntry,
138       sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)
139       );
140   }
141 
142   return (UINTN) PageMap;
143 }
144 
145 /**
146   The function will check if IA32 PAE is supported.
147 
148   @retval TRUE      IA32 PAE is supported.
149   @retval FALSE     IA32 PAE is not supported.
150 
151 **/
152 BOOLEAN
IsIa32PaeSupport(VOID)153 IsIa32PaeSupport (
154   VOID
155   )
156 {
157   UINT32            RegEax;
158   UINT32            RegEdx;
159   BOOLEAN           Ia32PaeSupport;
160 
161   Ia32PaeSupport = FALSE;
162   AsmCpuid (0x0, &RegEax, NULL, NULL, NULL);
163   if (RegEax >= 0x1) {
164     AsmCpuid (0x1, NULL, NULL, NULL, &RegEdx);
165     if ((RegEdx & BIT6) != 0) {
166       Ia32PaeSupport = TRUE;
167     }
168   }
169 
170   return Ia32PaeSupport;
171 }
172 
173 /**
174   The function will check if Execute Disable Bit is available.
175 
176   @retval TRUE      Execute Disable Bit is available.
177   @retval FALSE     Execute Disable Bit is not available.
178 
179 **/
180 BOOLEAN
IsExecuteDisableBitAvailable(VOID)181 IsExecuteDisableBitAvailable (
182   VOID
183   )
184 {
185   UINT32            RegEax;
186   UINT32            RegEdx;
187   BOOLEAN           Available;
188 
189   Available = FALSE;
190   AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
191   if (RegEax >= 0x80000001) {
192     AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
193     if ((RegEdx & BIT20) != 0) {
194       //
195       // Bit 20: Execute Disable Bit available.
196       //
197       Available = TRUE;
198     }
199   }
200 
201   return Available;
202 }
203 
204 /**
205    Transfers control to DxeCore.
206 
207    This function performs a CPU architecture specific operations to execute
208    the entry point of DxeCore with the parameters of HobList.
209    It also installs EFI_END_OF_PEI_PPI to signal the end of PEI phase.
210 
211    @param DxeCoreEntryPoint         The entry point of DxeCore.
212    @param HobList                   The start of HobList passed to DxeCore.
213 
214 **/
215 VOID
HandOffToDxeCore(IN EFI_PHYSICAL_ADDRESS DxeCoreEntryPoint,IN EFI_PEI_HOB_POINTERS HobList)216 HandOffToDxeCore (
217   IN EFI_PHYSICAL_ADDRESS   DxeCoreEntryPoint,
218   IN EFI_PEI_HOB_POINTERS   HobList
219   )
220 {
221   EFI_STATUS                Status;
222   EFI_PHYSICAL_ADDRESS      BaseOfStack;
223   EFI_PHYSICAL_ADDRESS      TopOfStack;
224   UINTN                     PageTables;
225   X64_IDT_GATE_DESCRIPTOR   *IdtTable;
226   UINTN                     SizeOfTemplate;
227   VOID                      *TemplateBase;
228   EFI_PHYSICAL_ADDRESS      VectorAddress;
229   UINT32                    Index;
230   X64_IDT_TABLE             *IdtTableForX64;
231   EFI_VECTOR_HANDOFF_INFO   *VectorInfo;
232   EFI_PEI_VECTOR_HANDOFF_INFO_PPI *VectorHandoffInfoPpi;
233   BOOLEAN                   BuildPageTablesIa32Pae;
234 
235   Status = PeiServicesAllocatePages (EfiBootServicesData, EFI_SIZE_TO_PAGES (STACK_SIZE), &BaseOfStack);
236   ASSERT_EFI_ERROR (Status);
237 
238   if (FeaturePcdGet(PcdDxeIplSwitchToLongMode)) {
239     //
240     // Compute the top of the stack we were allocated, which is used to load X64 dxe core.
241     // Pre-allocate a 32 bytes which confroms to x64 calling convention.
242     //
243     // The first four parameters to a function are passed in rcx, rdx, r8 and r9.
244     // Any further parameters are pushed on the stack. Furthermore, space (4 * 8bytes) for the
245     // register parameters is reserved on the stack, in case the called function
246     // wants to spill them; this is important if the function is variadic.
247     //
248     TopOfStack = BaseOfStack + EFI_SIZE_TO_PAGES (STACK_SIZE) * EFI_PAGE_SIZE - 32;
249 
250     //
251     //  x64 Calling Conventions requires that the stack must be aligned to 16 bytes
252     //
253     TopOfStack = (EFI_PHYSICAL_ADDRESS) (UINTN) ALIGN_POINTER (TopOfStack, 16);
254 
255     //
256     // Load the GDT of Go64. Since the GDT of 32-bit Tiano locates in the BS_DATA
257     // memory, it may be corrupted when copying FV to high-end memory
258     //
259     AsmWriteGdtr (&gGdt);
260     //
261     // Create page table and save PageMapLevel4 to CR3
262     //
263     PageTables = CreateIdentityMappingPageTables (BaseOfStack, STACK_SIZE);
264 
265     //
266     // End of PEI phase signal
267     //
268     Status = PeiServicesInstallPpi (&gEndOfPeiSignalPpi);
269     ASSERT_EFI_ERROR (Status);
270 
271     AsmWriteCr3 (PageTables);
272 
273     //
274     // Update the contents of BSP stack HOB to reflect the real stack info passed to DxeCore.
275     //
276     UpdateStackHob (BaseOfStack, STACK_SIZE);
277 
278     SizeOfTemplate = AsmGetVectorTemplatInfo (&TemplateBase);
279 
280     Status = PeiServicesAllocatePages (
281                EfiBootServicesData,
282                EFI_SIZE_TO_PAGES(sizeof (X64_IDT_TABLE) + SizeOfTemplate * IDT_ENTRY_COUNT),
283                &VectorAddress
284                );
285     ASSERT_EFI_ERROR (Status);
286 
287     //
288     // Store EFI_PEI_SERVICES** in the 4 bytes immediately preceding IDT to avoid that
289     // it may not be gotten correctly after IDT register is re-written.
290     //
291     IdtTableForX64 = (X64_IDT_TABLE *) (UINTN) VectorAddress;
292     IdtTableForX64->PeiService = GetPeiServicesTablePointer ();
293 
294     VectorAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) (IdtTableForX64 + 1);
295     IdtTable      = IdtTableForX64->IdtTable;
296     for (Index = 0; Index < IDT_ENTRY_COUNT; Index++) {
297       IdtTable[Index].Ia32IdtEntry.Bits.GateType    =  0x8e;
298       IdtTable[Index].Ia32IdtEntry.Bits.Reserved_0  =  0;
299       IdtTable[Index].Ia32IdtEntry.Bits.Selector    =  SYS_CODE64_SEL;
300 
301       IdtTable[Index].Ia32IdtEntry.Bits.OffsetLow   = (UINT16) VectorAddress;
302       IdtTable[Index].Ia32IdtEntry.Bits.OffsetHigh  = (UINT16) (RShiftU64 (VectorAddress, 16));
303       IdtTable[Index].Offset32To63                  = (UINT32) (RShiftU64 (VectorAddress, 32));
304       IdtTable[Index].Reserved                      = 0;
305 
306       CopyMem ((VOID *) (UINTN) VectorAddress, TemplateBase, SizeOfTemplate);
307       AsmVectorFixup ((VOID *) (UINTN) VectorAddress, (UINT8) Index);
308 
309       VectorAddress += SizeOfTemplate;
310     }
311 
312     gLidtDescriptor.Base = (UINTN) IdtTable;
313 
314     //
315     // Disable interrupt of Debug timer, since new IDT table cannot handle it.
316     //
317     SaveAndSetDebugTimerInterrupt (FALSE);
318 
319     AsmWriteIdtr (&gLidtDescriptor);
320 
321     DEBUG ((
322       DEBUG_INFO,
323       "%a() Stack Base: 0x%lx, Stack Size: 0x%x\n",
324       __FUNCTION__,
325       BaseOfStack,
326       STACK_SIZE
327       ));
328 
329     //
330     // Go to Long Mode and transfer control to DxeCore.
331     // Interrupts will not get turned on until the CPU AP is loaded.
332     // Call x64 drivers passing in single argument, a pointer to the HOBs.
333     //
334     AsmEnablePaging64 (
335       SYS_CODE64_SEL,
336       DxeCoreEntryPoint,
337       (EFI_PHYSICAL_ADDRESS)(UINTN)(HobList.Raw),
338       0,
339       TopOfStack
340       );
341   } else {
342     //
343     // Get Vector Hand-off Info PPI and build Guided HOB
344     //
345     Status = PeiServicesLocatePpi (
346                &gEfiVectorHandoffInfoPpiGuid,
347                0,
348                NULL,
349                (VOID **)&VectorHandoffInfoPpi
350                );
351     if (Status == EFI_SUCCESS) {
352       DEBUG ((EFI_D_INFO, "Vector Hand-off Info PPI is gotten, GUIDed HOB is created!\n"));
353       VectorInfo = VectorHandoffInfoPpi->Info;
354       Index = 1;
355       while (VectorInfo->Attribute != EFI_VECTOR_HANDOFF_LAST_ENTRY) {
356         VectorInfo ++;
357         Index ++;
358       }
359       BuildGuidDataHob (
360         &gEfiVectorHandoffInfoPpiGuid,
361         VectorHandoffInfoPpi->Info,
362         sizeof (EFI_VECTOR_HANDOFF_INFO) * Index
363         );
364     }
365 
366     //
367     // Compute the top of the stack we were allocated. Pre-allocate a UINTN
368     // for safety.
369     //
370     TopOfStack = BaseOfStack + EFI_SIZE_TO_PAGES (STACK_SIZE) * EFI_PAGE_SIZE - CPU_STACK_ALIGNMENT;
371     TopOfStack = (EFI_PHYSICAL_ADDRESS) (UINTN) ALIGN_POINTER (TopOfStack, CPU_STACK_ALIGNMENT);
372 
373     PageTables = 0;
374     BuildPageTablesIa32Pae = (BOOLEAN) (PcdGetBool (PcdSetNxForStack) && IsIa32PaeSupport () && IsExecuteDisableBitAvailable ());
375     if (BuildPageTablesIa32Pae) {
376       PageTables = Create4GPageTablesIa32Pae (BaseOfStack, STACK_SIZE);
377       EnableExecuteDisableBit ();
378     }
379 
380     //
381     // End of PEI phase signal
382     //
383     Status = PeiServicesInstallPpi (&gEndOfPeiSignalPpi);
384     ASSERT_EFI_ERROR (Status);
385 
386     if (BuildPageTablesIa32Pae) {
387       AsmWriteCr3 (PageTables);
388       //
389       // Set Physical Address Extension (bit 5 of CR4).
390       //
391       AsmWriteCr4 (AsmReadCr4 () | BIT5);
392     }
393 
394     //
395     // Update the contents of BSP stack HOB to reflect the real stack info passed to DxeCore.
396     //
397     UpdateStackHob (BaseOfStack, STACK_SIZE);
398 
399     DEBUG ((
400       DEBUG_INFO,
401       "%a() Stack Base: 0x%lx, Stack Size: 0x%x\n",
402       __FUNCTION__,
403       BaseOfStack,
404       STACK_SIZE
405       ));
406 
407     //
408     // Transfer the control to the entry point of DxeCore.
409     //
410     if (BuildPageTablesIa32Pae) {
411       AsmEnablePaging32 (
412         (SWITCH_STACK_ENTRY_POINT)(UINTN)DxeCoreEntryPoint,
413         HobList.Raw,
414         NULL,
415         (VOID *) (UINTN) TopOfStack
416         );
417     } else {
418       SwitchStack (
419         (SWITCH_STACK_ENTRY_POINT)(UINTN)DxeCoreEntryPoint,
420         HobList.Raw,
421         NULL,
422         (VOID *) (UINTN) TopOfStack
423         );
424     }
425   }
426 }
427 
428