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