Vishal Bhoj | 82c8071 | 2015-12-15 21:13:33 +0530 | [diff] [blame^] | 1 | /** @file
|
| 2 | CPU DXE AP Startup
|
| 3 |
|
| 4 | Copyright (c) 2008 - 2012, 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 JmpToCli[2];
|
| 23 |
|
| 24 | UINT16 GdtLimit;
|
| 25 | UINT32 GdtBase;
|
| 26 |
|
| 27 | UINT8 Cli;
|
| 28 |
|
| 29 | UINT8 MovAxRealSegment; UINT16 RealSegment;
|
| 30 | UINT8 MovDsAx[2];
|
| 31 |
|
| 32 | UINT8 MovBxGdtr[3];
|
| 33 | UINT8 LoadGdt[5];
|
| 34 |
|
| 35 | UINT8 MovEaxCr0[2];
|
| 36 | UINT32 MovEaxCr0Value;
|
| 37 | UINT8 MovCr0Eax[3];
|
| 38 |
|
| 39 | UINT8 FarJmp32Flat[2]; UINT32 FlatJmpOffset; UINT16 FlatJmpSelector;
|
| 40 |
|
| 41 | //
|
| 42 | // Now in IA32
|
| 43 | //
|
| 44 | UINT8 MovEaxCr4;
|
| 45 | UINT32 MovEaxCr4Value;
|
| 46 | UINT8 MovCr4Eax[3];
|
| 47 |
|
| 48 | UINT8 MoveDataSelectorIntoAx[2]; UINT16 FlatDataSelector;
|
| 49 | UINT8 MoveFlatDataSelectorFromAxToDs[2];
|
| 50 | UINT8 MoveFlatDataSelectorFromAxToEs[2];
|
| 51 | UINT8 MoveFlatDataSelectorFromAxToFs[2];
|
| 52 | UINT8 MoveFlatDataSelectorFromAxToGs[2];
|
| 53 | UINT8 MoveFlatDataSelectorFromAxToSs[2];
|
| 54 |
|
| 55 | #if defined (MDE_CPU_X64)
|
| 56 | //
|
| 57 | // Transition to X64
|
| 58 | //
|
| 59 | UINT8 MovEaxCr3;
|
| 60 | UINT32 Cr3Value;
|
| 61 | UINT8 MovCr3Eax[3];
|
| 62 |
|
| 63 | UINT8 MoveCr4ToEax[3];
|
| 64 | UINT8 SetCr4Bit5[4];
|
| 65 | UINT8 MoveEaxToCr4[3];
|
| 66 |
|
| 67 | UINT8 MoveLongModeEnableMsrToEcx[5];
|
| 68 | UINT8 ReadLmeMsr[2];
|
| 69 | UINT8 SetLongModeEnableBit[4];
|
| 70 | UINT8 WriteLmeMsr[2];
|
| 71 |
|
| 72 | UINT8 MoveCr0ToEax[3];
|
| 73 | UINT8 SetCr0PagingBit[4];
|
| 74 | UINT8 MoveEaxToCr0[3];
|
| 75 | //UINT8 DeadLoop[2];
|
| 76 |
|
| 77 | UINT8 FarJmp32LongMode; UINT32 LongJmpOffset; UINT16 LongJmpSelector;
|
| 78 | #endif // defined (MDE_CPU_X64)
|
| 79 |
|
| 80 | #if defined (MDE_CPU_X64)
|
| 81 | UINT8 MovEaxOrRaxCpuDxeEntry[2]; UINTN CpuDxeEntryValue;
|
| 82 | #else
|
| 83 | UINT8 MovEaxOrRaxCpuDxeEntry; UINTN CpuDxeEntryValue;
|
| 84 | #endif
|
| 85 | UINT8 JmpToCpuDxeEntry[2];
|
| 86 |
|
| 87 | } STARTUP_CODE;
|
| 88 |
|
| 89 | #pragma pack()
|
| 90 |
|
| 91 | /**
|
| 92 | This .asm code used for translating processor from 16 bit real mode into
|
| 93 | 64 bit long mode. which help to create the mStartupCodeTemplate value.
|
| 94 |
|
| 95 | To assemble:
|
| 96 | * nasm -o ApStartup ApStartup.asm
|
| 97 | Then disassemble:
|
| 98 | * ndisasm -b 16 ApStartup
|
| 99 | * ndisasm -b 16 -e 6 ApStartup
|
| 100 | * ndisasm -b 32 -e 32 ApStartup (This -e offset may need adjustment)
|
| 101 | * ndisasm -b 64 -e 0x83 ApStartup (This -e offset may need adjustment)
|
| 102 |
|
| 103 | %define DEFAULT_CR0 0x00000023
|
| 104 | %define DEFAULT_CR4 0x640
|
| 105 |
|
| 106 | BITS 16
|
| 107 |
|
| 108 | jmp short TransitionFromReal16To32BitFlat
|
| 109 |
|
| 110 | ALIGN 2
|
| 111 |
|
| 112 | Gdtr:
|
| 113 | dw 0x5a5a
|
| 114 | dd 0x5a5a5a5a
|
| 115 |
|
| 116 | ;
|
| 117 | ; Modified: EAX, EBX
|
| 118 | ;
|
| 119 | TransitionFromReal16To32BitFlat:
|
| 120 |
|
| 121 | cli
|
| 122 | mov ax, 0x5a5a
|
| 123 | mov ds, ax
|
| 124 |
|
| 125 | mov bx, Gdtr
|
| 126 | o32 lgdt [ds:bx]
|
| 127 |
|
| 128 | mov eax, cr4
|
| 129 | btc eax, 5
|
| 130 | mov cr4, eax
|
| 131 |
|
| 132 | mov eax, DEFAULT_CR0
|
| 133 | mov cr0, eax
|
| 134 |
|
| 135 | jmp 0x5a5a:dword jumpTo32BitAndLandHere
|
| 136 | BITS 32
|
| 137 | jumpTo32BitAndLandHere:
|
| 138 |
|
| 139 | mov eax, DEFAULT_CR4
|
| 140 | mov cr4, eax
|
| 141 |
|
| 142 | mov ax, 0x5a5a
|
| 143 | mov ds, ax
|
| 144 | mov es, ax
|
| 145 | mov fs, ax
|
| 146 | mov gs, ax
|
| 147 | mov ss, ax
|
| 148 |
|
| 149 | ;
|
| 150 | ; Jump to CpuDxe for IA32
|
| 151 | ;
|
| 152 | mov eax, 0x5a5a5a5a
|
| 153 | or eax, eax
|
| 154 | jz Transition32FlatTo64Flat
|
| 155 | jmp eax
|
| 156 |
|
| 157 | ;
|
| 158 | ; Transition to X64
|
| 159 | ;
|
| 160 | Transition32FlatTo64Flat:
|
| 161 | mov eax, 0x5a5a5a5a
|
| 162 | mov cr3, eax
|
| 163 |
|
| 164 | mov eax, cr4
|
| 165 | bts eax, 5 ; enable PAE
|
| 166 | mov cr4, eax
|
| 167 |
|
| 168 | mov ecx, 0xc0000080
|
| 169 | rdmsr
|
| 170 | bts eax, 8 ; set LME
|
| 171 | wrmsr
|
| 172 |
|
| 173 | mov eax, cr0
|
| 174 | bts eax, 31 ; set PG
|
| 175 | mov cr0, eax ; enable paging
|
| 176 |
|
| 177 | ;
|
| 178 | ; Jump to CpuDxe for X64
|
| 179 | ;
|
| 180 | jmp 0x5a5a:jumpTo64BitAndLandHere
|
| 181 | BITS 64
|
| 182 | jumpTo64BitAndLandHere:
|
| 183 | mov rax, 0xcdcdcdcdcdcdcdcd
|
| 184 | jmp rax
|
| 185 | **/
|
| 186 | STARTUP_CODE mStartupCodeTemplate = {
|
| 187 | { 0xeb, 0x06 }, // Jump to cli
|
| 188 | 0, // GDT Limit
|
| 189 | 0, // GDT Base
|
| 190 | 0xfa, // cli (Clear Interrupts)
|
| 191 | 0xb8, 0x0000, // mov ax, RealSegment
|
| 192 | { 0x8e, 0xd8 }, // mov ds, ax
|
| 193 | { 0xBB, 0x02, 0x00 }, // mov bx, Gdtr
|
| 194 | { 0x3e, 0x66, 0x0f, 0x01, 0x17 }, // lgdt [ds:bx]
|
| 195 | { 0x66, 0xB8 }, 0x00000023, // mov eax, cr0 value
|
| 196 | { 0x0F, 0x22, 0xC0 }, // mov cr0, eax
|
| 197 | { 0x66, 0xEA }, // far jmp to 32-bit flat
|
| 198 | OFFSET_OF(STARTUP_CODE, MovEaxCr4),
|
| 199 | LINEAR_CODE_SEL,
|
| 200 | 0xB8, 0x00000640, // mov eax, cr4 value
|
| 201 | { 0x0F, 0x22, 0xe0 }, // mov cr4, eax
|
| 202 | { 0x66, 0xb8 }, CPU_DATA_SEL, // mov ax, FlatDataSelector
|
| 203 | { 0x8e, 0xd8 }, // mov ds, ax
|
| 204 | { 0x8e, 0xc0 }, // mov es, ax
|
| 205 | { 0x8e, 0xe0 }, // mov fs, ax
|
| 206 | { 0x8e, 0xe8 }, // mov gs, ax
|
| 207 | { 0x8e, 0xd0 }, // mov ss, ax
|
| 208 |
|
| 209 | #if defined (MDE_CPU_X64)
|
| 210 | 0xB8, 0x00000000, // mov eax, cr3 value
|
| 211 | { 0x0F, 0x22, 0xd8 }, // mov cr3, eax
|
| 212 |
|
| 213 | { 0x0F, 0x20, 0xE0 }, // mov eax, cr4
|
| 214 | { 0x0F, 0xBA, 0xE8, 0x05 }, // bts eax, 5
|
| 215 | { 0x0F, 0x22, 0xE0 }, // mov cr4, eax
|
| 216 |
|
| 217 | { 0xB9, 0x80, 0x00, 0x00, 0xC0 }, // mov ecx, 0xc0000080
|
| 218 | { 0x0F, 0x32 }, // rdmsr
|
| 219 | { 0x0F, 0xBA, 0xE8, 0x08 }, // bts eax, 8
|
| 220 | { 0x0F, 0x30 }, // wrmsr
|
| 221 |
|
| 222 | { 0x0F, 0x20, 0xC0 }, // mov eax, cr0
|
| 223 | { 0x0F, 0xBA, 0xE8, 0x1F }, // bts eax, 31
|
| 224 | { 0x0F, 0x22, 0xC0 }, // mov cr0, eax
|
| 225 |
|
| 226 | 0xEA, // FarJmp32LongMode
|
| 227 | OFFSET_OF(STARTUP_CODE, MovEaxOrRaxCpuDxeEntry),
|
| 228 | LINEAR_CODE64_SEL,
|
| 229 | #endif // defined (MDE_CPU_X64)
|
| 230 |
|
| 231 | //0xeb, 0xfe, // jmp $
|
| 232 | #if defined (MDE_CPU_X64)
|
| 233 | { 0x48, 0xb8 }, 0x0, // mov rax, X64 CpuDxe MP Entry Point
|
| 234 | #else
|
| 235 | 0xB8, 0x0, // mov eax, IA32 CpuDxe MP Entry Point
|
| 236 | #endif
|
| 237 | { 0xff, 0xe0 }, // jmp to eax/rax (CpuDxe MP Entry Point)
|
| 238 |
|
| 239 | };
|
| 240 |
|
| 241 | volatile STARTUP_CODE *StartupCode = NULL;
|
| 242 |
|
| 243 | /**
|
| 244 | Prepares Startup Code for APs.
|
| 245 | This function prepares Startup Code for APs.
|
| 246 |
|
| 247 | @retval EFI_SUCCESS The APs were started
|
| 248 | @retval EFI_OUT_OF_RESOURCES Cannot allocate memory to start APs
|
| 249 |
|
| 250 | **/
|
| 251 | EFI_STATUS
|
| 252 | PrepareAPStartupCode (
|
| 253 | VOID
|
| 254 | )
|
| 255 | {
|
| 256 | EFI_STATUS Status;
|
| 257 | IA32_DESCRIPTOR Gdtr;
|
| 258 | EFI_PHYSICAL_ADDRESS StartAddress;
|
| 259 |
|
| 260 | StartAddress = BASE_1MB;
|
| 261 | Status = gBS->AllocatePages (
|
| 262 | AllocateMaxAddress,
|
| 263 | EfiACPIMemoryNVS,
|
| 264 | EFI_SIZE_TO_PAGES (sizeof (*StartupCode)),
|
| 265 | &StartAddress
|
| 266 | );
|
| 267 | if (EFI_ERROR (Status)) {
|
| 268 | return Status;
|
| 269 | }
|
| 270 |
|
| 271 | StartupCode = (STARTUP_CODE*)(VOID*)(UINTN) StartAddress;
|
| 272 | CopyMem ((VOID*) StartupCode, &mStartupCodeTemplate, sizeof (*StartupCode));
|
| 273 | StartupCode->RealSegment = (UINT16) (((UINTN) StartAddress) >> 4);
|
| 274 |
|
| 275 | AsmReadGdtr (&Gdtr);
|
| 276 | StartupCode->GdtLimit = Gdtr.Limit;
|
| 277 | StartupCode->GdtBase = (UINT32) Gdtr.Base;
|
| 278 |
|
| 279 | StartupCode->CpuDxeEntryValue = (UINTN) AsmApEntryPoint;
|
| 280 |
|
| 281 | StartupCode->FlatJmpOffset += (UINT32) StartAddress;
|
| 282 |
|
| 283 | #if defined (MDE_CPU_X64)
|
| 284 | StartupCode->Cr3Value = (UINT32) AsmReadCr3 ();
|
| 285 | StartupCode->LongJmpOffset += (UINT32) StartAddress;
|
| 286 | #endif
|
| 287 |
|
| 288 | return EFI_SUCCESS;
|
| 289 | }
|
| 290 |
|
| 291 | /**
|
| 292 | Free the code buffer of startup AP.
|
| 293 |
|
| 294 | **/
|
| 295 | VOID
|
| 296 | FreeApStartupCode (
|
| 297 | VOID
|
| 298 | )
|
| 299 | {
|
| 300 | if (StartupCode != NULL) {
|
| 301 | gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)(VOID*) StartupCode,
|
| 302 | EFI_SIZE_TO_PAGES (sizeof (*StartupCode)));
|
| 303 | }
|
| 304 | }
|
| 305 |
|
| 306 |
|
| 307 | /**
|
| 308 | Starts the Application Processors and directs them to jump to the
|
| 309 | specified routine.
|
| 310 |
|
| 311 | The processor jumps to this code in flat mode, but the processor's
|
| 312 | stack is not initialized.
|
| 313 |
|
| 314 | @retval EFI_SUCCESS The APs were started
|
| 315 |
|
| 316 | **/
|
| 317 | EFI_STATUS
|
| 318 | StartApsStackless (
|
| 319 | VOID
|
| 320 | )
|
| 321 | {
|
| 322 | SendInitSipiSipiAllExcludingSelf ((UINT32)(UINTN)(VOID*) StartupCode);
|
| 323 | //
|
| 324 | // Wait 100 milliseconds for APs to arrive at the ApEntryPoint routine
|
| 325 | //
|
| 326 | MicroSecondDelay (100 * 1000);
|
| 327 |
|
| 328 | return EFI_SUCCESS;
|
| 329 | }
|
| 330 |
|
| 331 | /**
|
| 332 | Resets the Application Processor and directs it to jump to the
|
| 333 | specified routine.
|
| 334 |
|
| 335 | The processor jumps to this code in flat mode, but the processor's
|
| 336 | stack is not initialized.
|
| 337 |
|
| 338 | @param ProcessorId the AP of ProcessorId was reset
|
| 339 | **/
|
| 340 | VOID
|
| 341 | ResetApStackless (
|
| 342 | IN UINT32 ProcessorId
|
| 343 | )
|
| 344 | {
|
| 345 | SendInitSipiSipi (ProcessorId,
|
| 346 | (UINT32)(UINTN)(VOID*) StartupCode);
|
| 347 | }
|