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