1 /** @file
2   CPU DXE AP Startup
3 
4   Copyright (c) 2008 - 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 "CpuDxe.h"
16 #include "CpuGdt.h"
17 #include "CpuMp.h"
18 
19 #pragma pack(1)
20 
21 typedef struct {
22   UINT8  MoveIa32EferMsrToEcx[5];
23   UINT8  ReadIa32EferMsr[2];
24   UINT8  SetExecuteDisableBitEnableBit[4];
25   UINT8  WriteIa32EferMsr[2];
26 
27 #if defined (MDE_CPU_IA32)
28   UINT8  MovEaxCr3;
29   UINT32 Cr3Value;
30   UINT8  MovCr3Eax[3];
31 
32   UINT8  MoveCr4ToEax[3];
33   UINT8  SetCr4Bit5[4];
34   UINT8  MoveEaxToCr4[3];
35 
36   UINT8  MoveCr0ToEax[3];
37   UINT8  SetCr0PagingBit[4];
38   UINT8  MoveEaxToCr0[3];
39 #endif
40 } ENABLE_EXECUTE_DISABLE_CODE;
41 
42 ENABLE_EXECUTE_DISABLE_CODE mEnableExecuteDisableCodeTemplate = {
43   { 0xB9, 0x80, 0x00, 0x00, 0xC0 },   // mov ecx, 0xc0000080
44   { 0x0F, 0x32 },                     // rdmsr
45   { 0x0F, 0xBA, 0xE8, 0x0B },         // bts eax, 11
46   { 0x0F, 0x30 },                     // wrmsr
47 
48 #if defined (MDE_CPU_IA32)
49   0xB8, 0x00000000,                   // mov eax, cr3 value
50   { 0x0F, 0x22, 0xd8 },               // mov cr3, eax
51 
52   { 0x0F, 0x20, 0xE0 },               // mov eax, cr4
53   { 0x0F, 0xBA, 0xE8, 0x05 },         // bts eax, 5
54   { 0x0F, 0x22, 0xE0 },               // mov cr4, eax
55 
56   { 0x0F, 0x20, 0xC0 },               // mov eax, cr0
57   { 0x0F, 0xBA, 0xE8, 0x1F },         // bts eax, 31
58   { 0x0F, 0x22, 0xC0 },               // mov cr0, eax
59 #endif
60 };
61 
62 typedef struct {
63   UINT8  JmpToCli[2];
64 
65   UINT16 GdtLimit;
66   UINT32 GdtBase;
67 
68   UINT8  Cli;
69 
70   UINT8  MovAxRealSegment; UINT16 RealSegment;
71   UINT8  MovDsAx[2];
72 
73   UINT8  MovBxGdtr[3];
74   UINT8  LoadGdt[5];
75 
76   UINT8  MovEaxCr0[2];
77   UINT32 MovEaxCr0Value;
78   UINT8  MovCr0Eax[3];
79 
80   UINT8  FarJmp32Flat[2]; UINT32 FlatJmpOffset; UINT16 FlatJmpSelector;
81 
82   //
83   // Now in IA32
84   //
85   UINT8  MovEaxCr4;
86   UINT32 MovEaxCr4Value;
87   UINT8  MovCr4Eax[3];
88 
89   UINT8  MoveDataSelectorIntoAx[2]; UINT16 FlatDataSelector;
90   UINT8  MoveFlatDataSelectorFromAxToDs[2];
91   UINT8  MoveFlatDataSelectorFromAxToEs[2];
92   UINT8  MoveFlatDataSelectorFromAxToFs[2];
93   UINT8  MoveFlatDataSelectorFromAxToGs[2];
94   UINT8  MoveFlatDataSelectorFromAxToSs[2];
95 
96   //
97   // Code placeholder to enable PAE Execute Disable for IA32
98   // and enable Execute Disable Bit for X64
99   //
100   ENABLE_EXECUTE_DISABLE_CODE EnableExecuteDisable;
101 
102 #if defined (MDE_CPU_X64)
103   //
104   // Transition to X64
105   //
106   UINT8  MovEaxCr3;
107   UINT32 Cr3Value;
108   UINT8  MovCr3Eax[3];
109 
110   UINT8  MoveCr4ToEax[3];
111   UINT8  SetCr4Bit5[4];
112   UINT8  MoveEaxToCr4[3];
113 
114   UINT8  MoveLongModeEnableMsrToEcx[5];
115   UINT8  ReadLmeMsr[2];
116   UINT8  SetLongModeEnableBit[4];
117   UINT8  WriteLmeMsr[2];
118 
119   UINT8  MoveCr0ToEax[3];
120   UINT8  SetCr0PagingBit[4];
121   UINT8  MoveEaxToCr0[3];
122   //UINT8  DeadLoop[2];
123 
124   UINT8  FarJmp32LongMode; UINT32 LongJmpOffset; UINT16 LongJmpSelector;
125 #endif // defined (MDE_CPU_X64)
126 
127 #if defined (MDE_CPU_X64)
128   UINT8  MovEaxOrRaxCpuDxeEntry[2]; UINTN CpuDxeEntryValue;
129 #else
130   UINT8  MovEaxOrRaxCpuDxeEntry; UINTN CpuDxeEntryValue;
131 #endif
132   UINT8  JmpToCpuDxeEntry[2];
133 
134 } STARTUP_CODE;
135 
136 #pragma pack()
137 
138 /**
139   This .asm code used for translating processor from 16 bit real mode into
140   64 bit long mode. which help to create the mStartupCodeTemplate value.
141 
142   To assemble:
143     * nasm -o ApStartup ApStartup.asm
144     Then disassemble:
145     * ndisasm -b 16 ApStartup
146     * ndisasm -b 16 -e 6 ApStartup
147     * ndisasm -b 32 -e 32 ApStartup (This -e offset may need adjustment)
148     * ndisasm -b 64 -e 0x83 ApStartup (This -e offset may need adjustment)
149 
150   %define DEFAULT_CR0  0x00000023
151   %define DEFAULT_CR4  0x640
152 
153   BITS    16
154 
155       jmp     short TransitionFromReal16To32BitFlat
156 
157   ALIGN   2
158 
159   Gdtr:
160       dw      0x5a5a
161       dd      0x5a5a5a5a
162 
163   ;
164   ; Modified:  EAX, EBX
165   ;
166   TransitionFromReal16To32BitFlat:
167 
168       cli
169       mov     ax, 0x5a5a
170       mov     ds, ax
171 
172       mov     bx, Gdtr
173   o32 lgdt    [ds:bx]
174 
175       mov     eax, cr4
176       btc     eax, 5
177       mov     cr4, eax
178 
179       mov     eax, DEFAULT_CR0
180       mov     cr0, eax
181 
182       jmp     0x5a5a:dword jumpTo32BitAndLandHere
183   BITS    32
184   jumpTo32BitAndLandHere:
185 
186       mov     eax, DEFAULT_CR4
187       mov     cr4, eax
188 
189       mov     ax, 0x5a5a
190       mov     ds, ax
191       mov     es, ax
192       mov     fs, ax
193       mov     gs, ax
194       mov     ss, ax
195 
196   ;
197   ; Jump to CpuDxe for IA32
198   ;
199       mov     eax, 0x5a5a5a5a
200       or      eax, eax
201       jz      Transition32FlatTo64Flat
202       jmp     eax
203 
204   ;
205   ; Transition to X64
206   ;
207   Transition32FlatTo64Flat:
208       mov     eax, 0x5a5a5a5a
209       mov     cr3, eax
210 
211       mov     eax, cr4
212       bts     eax, 5                      ; enable PAE
213       mov     cr4, eax
214 
215       mov     ecx, 0xc0000080
216       rdmsr
217       bts     eax, 8                      ; set LME
218       wrmsr
219 
220       mov     eax, cr0
221       bts     eax, 31                     ; set PG
222       mov     cr0, eax                    ; enable paging
223 
224   ;
225   ; Jump to CpuDxe for X64
226   ;
227       jmp     0x5a5a:jumpTo64BitAndLandHere
228   BITS    64
229   jumpTo64BitAndLandHere:
230       mov     rax, 0xcdcdcdcdcdcdcdcd
231       jmp     rax
232 **/
233 STARTUP_CODE mStartupCodeTemplate = {
234   { 0xeb, 0x06 },                     // Jump to cli
235   0,                                  // GDT Limit
236   0,                                  // GDT Base
237   0xfa,                               // cli (Clear Interrupts)
238   0xb8, 0x0000,                       // mov ax, RealSegment
239   { 0x8e, 0xd8 },                     // mov ds, ax
240   { 0xBB, 0x02, 0x00 },               // mov bx, Gdtr
241   { 0x3e, 0x66, 0x0f, 0x01, 0x17 },   // lgdt [ds:bx]
242   { 0x66, 0xB8 }, 0x00000023,         // mov eax, cr0 value
243   { 0x0F, 0x22, 0xC0 },               // mov cr0, eax
244   { 0x66, 0xEA },                     // far jmp to 32-bit flat
245         OFFSET_OF(STARTUP_CODE, MovEaxCr4),
246         LINEAR_CODE_SEL,
247   0xB8, 0x00000640,                   // mov eax, cr4 value
248   { 0x0F, 0x22, 0xe0 },               // mov cr4, eax
249   { 0x66, 0xb8 }, CPU_DATA_SEL,       // mov ax, FlatDataSelector
250   { 0x8e, 0xd8 },                     // mov ds, ax
251   { 0x8e, 0xc0 },                     // mov es, ax
252   { 0x8e, 0xe0 },                     // mov fs, ax
253   { 0x8e, 0xe8 },                     // mov gs, ax
254   { 0x8e, 0xd0 },                     // mov ss, ax
255 
256 #if defined (MDE_CPU_X64)
257   //
258   // Code placeholder to enable Execute Disable Bit for X64
259   // Default is all NOP - No Operation
260   //
261   {
262     { 0x90, 0x90, 0x90, 0x90, 0x90 },
263     { 0x90, 0x90 },
264     { 0x90, 0x90, 0x90, 0x90 },
265     { 0x90, 0x90 },
266   },
267 
268   0xB8, 0x00000000,                   // mov eax, cr3 value
269   { 0x0F, 0x22, 0xd8 },               // mov cr3, eax
270 
271   { 0x0F, 0x20, 0xE0 },               // mov eax, cr4
272   { 0x0F, 0xBA, 0xE8, 0x05 },         // bts eax, 5
273   { 0x0F, 0x22, 0xE0 },               // mov cr4, eax
274 
275   { 0xB9, 0x80, 0x00, 0x00, 0xC0 },   // mov ecx, 0xc0000080
276   { 0x0F, 0x32 },                     // rdmsr
277   { 0x0F, 0xBA, 0xE8, 0x08 },         // bts eax, 8
278   { 0x0F, 0x30 },                     // wrmsr
279 
280   { 0x0F, 0x20, 0xC0 },               // mov eax, cr0
281   { 0x0F, 0xBA, 0xE8, 0x1F },         // bts eax, 31
282   { 0x0F, 0x22, 0xC0 },               // mov cr0, eax
283 
284   0xEA,                               // FarJmp32LongMode
285         OFFSET_OF(STARTUP_CODE, MovEaxOrRaxCpuDxeEntry),
286         LINEAR_CODE64_SEL,
287 #else
288   //
289   // Code placeholder to enable PAE Execute Disable for IA32
290   // Default is all NOP - No Operation
291   //
292   {
293     { 0x90, 0x90, 0x90, 0x90, 0x90 },
294     { 0x90, 0x90 },
295     { 0x90, 0x90, 0x90, 0x90 },
296     { 0x90, 0x90 },
297 
298     0x90, 0x90909090,
299     { 0x90, 0x90, 0x90 },
300 
301     { 0x90, 0x90, 0x90 },
302     { 0x90, 0x90, 0x90, 0x90 },
303     { 0x90, 0x90, 0x90 },
304 
305     { 0x90, 0x90, 0x90 },
306     { 0x90, 0x90, 0x90, 0x90 },
307     { 0x90, 0x90, 0x90 },
308   },
309 #endif
310 
311   //0xeb, 0xfe,       // jmp $
312 #if defined (MDE_CPU_X64)
313   { 0x48, 0xb8 }, 0x0,                // mov rax, X64 CpuDxe MP Entry Point
314 #else
315   0xB8, 0x0,                          // mov eax, IA32 CpuDxe MP Entry Point
316 #endif
317   { 0xff, 0xe0 },                     // jmp to eax/rax (CpuDxe MP Entry Point)
318 
319 };
320 
321 volatile STARTUP_CODE *StartupCode = NULL;
322 
323 /**
324   The function will check if BSP Execute Disable is enabled.
325   DxeIpl may have enabled Execute Disable for BSP,
326   APs need to get the status and sync up the settings.
327 
328   @retval TRUE      BSP Execute Disable is enabled.
329   @retval FALSE     BSP Execute Disable is not enabled.
330 
331 **/
332 BOOLEAN
IsBspExecuteDisableEnabled(VOID)333 IsBspExecuteDisableEnabled (
334   VOID
335   )
336 {
337   UINT32            RegEax;
338   UINT32            RegEdx;
339   UINT64            MsrRegisters;
340   BOOLEAN           Enabled;
341 
342   Enabled = FALSE;
343   AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
344   if (RegEax >= 0x80000001) {
345     AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
346     //
347     // Cpuid 0x80000001
348     // Bit 20: Execute Disable Bit available.
349     //
350     if ((RegEdx & BIT20) != 0) {
351       MsrRegisters = AsmReadMsr64 (0xC0000080);
352       //
353       // Msr 0xC0000080
354       // Bit 11: Execute Disable Bit enable.
355       //
356       if ((MsrRegisters & BIT11) != 0) {
357         Enabled = TRUE;
358       }
359     }
360   }
361 
362   return Enabled;
363 }
364 
365 /**
366   Prepares Startup Code for APs.
367   This function prepares Startup Code for APs.
368 
369   @retval EFI_SUCCESS           The APs were started
370   @retval EFI_OUT_OF_RESOURCES  Cannot allocate memory to start APs
371 
372 **/
373 EFI_STATUS
PrepareAPStartupCode(VOID)374 PrepareAPStartupCode (
375   VOID
376   )
377 {
378   EFI_STATUS            Status;
379   IA32_DESCRIPTOR       Gdtr;
380   EFI_PHYSICAL_ADDRESS  StartAddress;
381 
382   StartAddress = BASE_1MB;
383   Status = gBS->AllocatePages (
384                   AllocateMaxAddress,
385                   EfiACPIMemoryNVS,
386                   EFI_SIZE_TO_PAGES (sizeof (*StartupCode)),
387                   &StartAddress
388                   );
389   if (EFI_ERROR (Status)) {
390     return Status;
391   }
392 
393   StartupCode = (STARTUP_CODE*)(VOID*)(UINTN) StartAddress;
394   CopyMem ((VOID*) StartupCode, &mStartupCodeTemplate, sizeof (*StartupCode));
395   StartupCode->RealSegment = (UINT16) (((UINTN) StartAddress) >> 4);
396 
397   AsmReadGdtr (&Gdtr);
398   StartupCode->GdtLimit = Gdtr.Limit;
399   StartupCode->GdtBase = (UINT32) Gdtr.Base;
400 
401   StartupCode->CpuDxeEntryValue = (UINTN) AsmApEntryPoint;
402 
403   StartupCode->FlatJmpOffset += (UINT32) StartAddress;
404 
405   if (IsBspExecuteDisableEnabled ()) {
406     CopyMem (
407       (VOID*) &StartupCode->EnableExecuteDisable,
408       &mEnableExecuteDisableCodeTemplate,
409       sizeof (ENABLE_EXECUTE_DISABLE_CODE)
410       );
411   }
412 #if defined (MDE_CPU_X64)
413   StartupCode->Cr3Value = (UINT32) AsmReadCr3 ();
414   StartupCode->LongJmpOffset += (UINT32) StartAddress;
415 #else
416   StartupCode->EnableExecuteDisable.Cr3Value = (UINT32) AsmReadCr3 ();
417 #endif
418 
419   return EFI_SUCCESS;
420 }
421 
422 /**
423   Free the code buffer of startup AP.
424 
425 **/
426 VOID
FreeApStartupCode(VOID)427 FreeApStartupCode (
428   VOID
429   )
430 {
431   if (StartupCode != NULL) {
432     gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)(VOID*) StartupCode,
433                     EFI_SIZE_TO_PAGES (sizeof (*StartupCode)));
434   }
435 }
436 
437 
438 /**
439   Starts the Application Processors and directs them to jump to the
440   specified routine.
441 
442   The processor jumps to this code in flat mode, but the processor's
443   stack is not initialized.
444 
445   @retval EFI_SUCCESS           The APs were started
446 
447 **/
448 EFI_STATUS
StartApsStackless(VOID)449 StartApsStackless (
450   VOID
451   )
452 {
453   SendInitSipiSipiAllExcludingSelf ((UINT32)(UINTN)(VOID*) StartupCode);
454   //
455   // Wait for APs to arrive at the ApEntryPoint routine
456   //
457   MicroSecondDelay (PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds));
458 
459   return EFI_SUCCESS;
460 }
461 
462 /**
463   Resets the Application Processor and directs it to jump to the
464   specified routine.
465 
466   The processor jumps to this code in flat mode, but the processor's
467   stack is not initialized.
468 
469   @param ProcessorId           the AP of ProcessorId was reset
470 **/
471 VOID
ResetApStackless(IN UINT32 ProcessorId)472 ResetApStackless (
473   IN UINT32 ProcessorId
474   )
475 {
476   SendInitSipiSipi (ProcessorId,
477                     (UINT32)(UINTN)(VOID*) StartupCode);
478 }
479