Home | History | Annotate | Download | only in CpuDxe
      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
    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
    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
    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
    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
    472 ResetApStackless (
    473   IN UINT32 ProcessorId
    474   )
    475 {
    476   SendInitSipiSipi (ProcessorId,
    477                     (UINT32)(UINTN)(VOID*) StartupCode);
    478 }
    479