]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
MpInitLib: remove unneeded global ASM_PFX
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / X64 / MpFuncs.nasm
1 ;------------------------------------------------------------------------------ ;
2 ; Copyright (c) 2015 - 2022, Intel Corporation. All rights reserved.<BR>
3 ; SPDX-License-Identifier: BSD-2-Clause-Patent
4 ;
5 ; Module Name:
6 ;
7 ; MpFuncs.nasm
8 ;
9 ; Abstract:
10 ;
11 ; This is the assembly code for MP support
12 ;
13 ;-------------------------------------------------------------------------------
14
15 %include "MpEqu.inc"
16 extern ASM_PFX(InitializeFloatingPointUnits)
17
18 %macro OneTimeCall 1
19 jmp %1
20 %1 %+ OneTimerCallReturn:
21 %endmacro
22
23 %macro OneTimeCallRet 1
24 jmp %1 %+ OneTimerCallReturn
25 %endmacro
26
27 DEFAULT REL
28
29 SECTION .text
30
31 ;-------------------------------------------------------------------------------------
32 ;RendezvousFunnelProc procedure follows. All APs execute their procedure. This
33 ;procedure serializes all the AP processors through an Init sequence. It must be
34 ;noted that APs arrive here very raw...ie: real mode, no stack.
35 ;ALSO THIS PROCEDURE IS EXECUTED BY APs ONLY ON 16 BIT MODE. HENCE THIS PROC
36 ;IS IN MACHINE CODE.
37 ;-------------------------------------------------------------------------------------
38 RendezvousFunnelProcStart:
39 ; At this point CS = 0x(vv00) and ip= 0x0.
40 ; Save BIST information to ebp firstly
41
42 BITS 16
43 mov ebp, eax ; Save BIST information
44
45 mov ax, cs
46 mov ds, ax
47 mov es, ax
48 mov ss, ax
49 xor ax, ax
50 mov fs, ax
51 mov gs, ax
52
53 mov si, MP_CPU_EXCHANGE_INFO_FIELD (BufferStart)
54 mov ebx, [si]
55
56 mov si, MP_CPU_EXCHANGE_INFO_FIELD (DataSegment)
57 mov edx, [si]
58
59 ;
60 ; Get start address of 32-bit code in low memory (<1MB)
61 ;
62 mov edi, MP_CPU_EXCHANGE_INFO_FIELD (ModeTransitionMemory)
63
64 mov si, MP_CPU_EXCHANGE_INFO_FIELD (GdtrProfile)
65 o32 lgdt [cs:si]
66
67 mov si, MP_CPU_EXCHANGE_INFO_FIELD (IdtrProfile)
68 o32 lidt [cs:si]
69
70 ;
71 ; Switch to protected mode
72 ;
73 mov eax, cr0 ; Get control register 0
74 or eax, 000000003h ; Set PE bit (bit #0) & MP
75 mov cr0, eax
76
77 ; Switch to 32-bit code (>1MB)
78 o32 jmp far [cs:di]
79
80 ;
81 ; Following code must be copied to memory with type of EfiBootServicesCode.
82 ; This is required if NX is enabled for EfiBootServicesCode of memory.
83 ;
84 BITS 32
85 Flat32Start: ; protected mode entry point
86 mov ds, dx
87 mov es, dx
88 mov fs, dx
89 mov gs, dx
90 mov ss, dx
91
92 ;
93 ; Enable execute disable bit
94 ;
95 mov esi, MP_CPU_EXCHANGE_INFO_FIELD (EnableExecuteDisable)
96 cmp byte [ebx + esi], 0
97 jz SkipEnableExecuteDisableBit
98
99 mov ecx, 0c0000080h ; EFER MSR number
100 rdmsr ; Read EFER
101 bts eax, 11 ; Enable Execute Disable Bit
102 wrmsr ; Write EFER
103
104 SkipEnableExecuteDisableBit:
105 ;
106 ; Enable PAE
107 ;
108 mov eax, cr4
109 bts eax, 5
110
111 mov esi, MP_CPU_EXCHANGE_INFO_FIELD (Enable5LevelPaging)
112 cmp byte [ebx + esi], 0
113 jz SkipEnable5LevelPaging
114
115 ;
116 ; Enable 5 Level Paging
117 ;
118 bts eax, 12 ; Set LA57=1.
119
120 SkipEnable5LevelPaging:
121
122 mov cr4, eax
123
124 ;
125 ; Load page table
126 ;
127 mov esi, MP_CPU_EXCHANGE_INFO_FIELD (Cr3) ; Save CR3 in ecx
128 mov ecx, [ebx + esi]
129 mov cr3, ecx ; Load CR3
130
131 ;
132 ; Enable long mode
133 ;
134 mov ecx, 0c0000080h ; EFER MSR number
135 rdmsr ; Read EFER
136 bts eax, 8 ; Set LME=1
137 wrmsr ; Write EFER
138
139 ;
140 ; Enable paging
141 ;
142 mov eax, cr0 ; Read CR0
143 bts eax, 31 ; Set PG=1
144 mov cr0, eax ; Write CR0
145
146 ;
147 ; Far jump to 64-bit code
148 ;
149 mov edi, MP_CPU_EXCHANGE_INFO_FIELD (ModeHighMemory)
150 add edi, ebx
151 jmp far [edi]
152
153 BITS 64
154
155 ;
156 ; Required for the AMD SEV helper functions
157 ;
158 %include "AmdSev.nasm"
159
160 LongModeStart:
161 mov esi, ebx
162 lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (InitFlag)]
163 cmp qword [edi], 1 ; ApInitConfig
164 jnz GetApicId
165
166 ; Increment the number of APs executing here as early as possible
167 ; This is decremented in C code when AP is finished executing
168 mov edi, esi
169 add edi, MP_CPU_EXCHANGE_INFO_FIELD (NumApsExecuting)
170 lock inc dword [edi]
171
172 ; AP init
173 mov edi, esi
174 add edi, MP_CPU_EXCHANGE_INFO_FIELD (ApIndex)
175 mov ebx, 1
176 lock xadd dword [edi], ebx ; EBX = ApIndex++
177 inc ebx ; EBX is CpuNumber
178
179 ; program stack
180 mov edi, esi
181 add edi, MP_CPU_EXCHANGE_INFO_FIELD (StackSize)
182 mov eax, dword [edi]
183 mov ecx, ebx
184 inc ecx
185 mul ecx ; EAX = StackSize * (CpuNumber + 1)
186 mov edi, esi
187 add edi, MP_CPU_EXCHANGE_INFO_FIELD (StackStart)
188 add rax, qword [edi]
189 mov rsp, rax
190
191 ;
192 ; Setup the GHCB when AMD SEV-ES active.
193 ;
194 OneTimeCall SevEsSetupGhcb
195 jmp CProcedureInvoke
196
197 GetApicId:
198 ;
199 ; Use the GHCB protocol to get the ApicId when SEV-ES is active.
200 ;
201 OneTimeCall SevEsGetApicId
202
203 DoCpuid:
204 mov eax, 0
205 cpuid
206 cmp eax, 0bh
207 jb NoX2Apic ; CPUID level below CPUID_EXTENDED_TOPOLOGY
208
209 mov eax, 0bh
210 xor ecx, ecx
211 cpuid
212 test ebx, 0ffffh
213 jz NoX2Apic ; CPUID.0BH:EBX[15:0] is zero
214
215 ; Processor is x2APIC capable; 32-bit x2APIC ID is already in EDX
216 jmp GetProcessorNumber
217
218 NoX2Apic:
219 ; Processor is not x2APIC capable, so get 8-bit APIC ID
220 mov eax, 1
221 cpuid
222 shr ebx, 24
223 mov edx, ebx
224
225 GetProcessorNumber:
226 ;
227 ; Get processor number for this AP
228 ; Note that BSP may become an AP due to SwitchBsp()
229 ;
230 xor ebx, ebx
231 lea eax, [esi + MP_CPU_EXCHANGE_INFO_FIELD (CpuInfo)]
232 mov rdi, [eax]
233
234 GetNextProcNumber:
235 cmp dword [rdi + CPU_INFO_IN_HOB.InitialApicId], edx ; APIC ID match?
236 jz ProgramStack
237 add rdi, CPU_INFO_IN_HOB_size
238 inc ebx
239 jmp GetNextProcNumber
240
241 ProgramStack:
242 mov rsp, qword [rdi + CPU_INFO_IN_HOB.ApTopOfStack]
243
244 CProcedureInvoke:
245 push rbp ; Push BIST data at top of AP stack
246 xor rbp, rbp ; Clear ebp for call stack trace
247 push rbp
248 mov rbp, rsp
249
250 mov rax, qword [esi + MP_CPU_EXCHANGE_INFO_FIELD (InitializeFloatingPointUnits)]
251 sub rsp, 20h
252 call rax ; Call assembly function to initialize FPU per UEFI spec
253 add rsp, 20h
254
255 mov edx, ebx ; edx is ApIndex
256 mov ecx, esi
257 add ecx, MP_CPU_EXCHANGE_INFO_OFFSET ; rcx is address of exchange info data buffer
258
259 mov edi, esi
260 add edi, MP_CPU_EXCHANGE_INFO_FIELD (CFunction)
261 mov rax, qword [edi]
262
263 sub rsp, 20h
264 call rax ; Invoke C function
265 add rsp, 20h
266 jmp $ ; Should never reach here
267
268 RendezvousFunnelProcEnd:
269
270 ;-------------------------------------------------------------------------------------
271 ;SwitchToRealProc procedure follows.
272 ;ALSO THIS PROCEDURE IS EXECUTED BY APs TRANSITIONING TO 16 BIT MODE. HENCE THIS PROC
273 ;IS IN MACHINE CODE.
274 ; SwitchToRealProc (UINTN BufferStart, UINT16 Code16, UINT16 Code32, UINTN StackStart)
275 ; rcx - Buffer Start
276 ; rdx - Code16 Selector Offset
277 ; r8 - Code32 Selector Offset
278 ; r9 - Stack Start
279 ;-------------------------------------------------------------------------------------
280 SwitchToRealProcStart:
281 BITS 64
282 cli
283
284 ;
285 ; Get RDX reset value before changing stacks since the
286 ; new stack won't be able to accomodate a #VC exception.
287 ;
288 push rax
289 push rbx
290 push rcx
291 push rdx
292
293 mov rax, 1
294 cpuid
295 mov rsi, rax ; Save off the reset value for RDX
296
297 pop rdx
298 pop rcx
299 pop rbx
300 pop rax
301
302 ;
303 ; Establish stack below 1MB
304 ;
305 mov rsp, r9
306
307 ;
308 ; Push ultimate Reset Vector onto the stack
309 ;
310 mov rax, rcx
311 shr rax, 4
312 push word 0x0002 ; RFLAGS
313 push ax ; CS
314 push word 0x0000 ; RIP
315 push word 0x0000 ; For alignment, will be discarded
316
317 ;
318 ; Get address of "16-bit operand size" label
319 ;
320 lea rbx, [PM16Mode]
321
322 ;
323 ; Push addresses used to change to compatibility mode
324 ;
325 lea rax, [CompatMode]
326 push r8
327 push rax
328
329 ;
330 ; Clear R8 - R15, for reset, before going into 32-bit mode
331 ;
332 xor r8, r8
333 xor r9, r9
334 xor r10, r10
335 xor r11, r11
336 xor r12, r12
337 xor r13, r13
338 xor r14, r14
339 xor r15, r15
340
341 ;
342 ; Far return into 32-bit mode
343 ;
344 retfq
345
346 BITS 32
347 CompatMode:
348 ;
349 ; Set up stack to prepare for exiting protected mode
350 ;
351 push edx ; Code16 CS
352 push ebx ; PM16Mode label address
353
354 ;
355 ; Disable paging
356 ;
357 mov eax, cr0 ; Read CR0
358 btr eax, 31 ; Set PG=0
359 mov cr0, eax ; Write CR0
360
361 ;
362 ; Disable long mode
363 ;
364 mov ecx, 0c0000080h ; EFER MSR number
365 rdmsr ; Read EFER
366 btr eax, 8 ; Set LME=0
367 wrmsr ; Write EFER
368
369 ;
370 ; Disable PAE
371 ;
372 mov eax, cr4 ; Read CR4
373 btr eax, 5 ; Set PAE=0
374 mov cr4, eax ; Write CR4
375
376 mov edx, esi ; Restore RDX reset value
377
378 ;
379 ; Switch to 16-bit operand size
380 ;
381 retf
382
383 BITS 16
384 ;
385 ; At entry to this label
386 ; - RDX will have its reset value
387 ; - On the top of the stack
388 ; - Alignment data (two bytes) to be discarded
389 ; - IP for Real Mode (two bytes)
390 ; - CS for Real Mode (two bytes)
391 ;
392 ; This label is also used with AsmRelocateApLoop. During MP finalization,
393 ; the code from PM16Mode to SwitchToRealProcEnd is copied to the start of
394 ; the WakeupBuffer, allowing a parked AP to be booted by an OS.
395 ;
396 PM16Mode:
397 mov eax, cr0 ; Read CR0
398 btr eax, 0 ; Set PE=0
399 mov cr0, eax ; Write CR0
400
401 pop ax ; Discard alignment data
402
403 ;
404 ; Clear registers (except RDX and RSP) before going into 16-bit mode
405 ;
406 xor eax, eax
407 xor ebx, ebx
408 xor ecx, ecx
409 xor esi, esi
410 xor edi, edi
411 xor ebp, ebp
412
413 iret
414
415 SwitchToRealProcEnd:
416
417 ;-------------------------------------------------------------------------------------
418 ; AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish, Pm16CodeSegment, SevEsAPJumpTable, WakeupBuffer);
419 ;-------------------------------------------------------------------------------------
420 AsmRelocateApLoopStart:
421 BITS 64
422 cmp qword [rsp + 56], 0 ; SevEsAPJumpTable
423 je NoSevEs
424
425 ;
426 ; Perform some SEV-ES related setup before leaving 64-bit mode
427 ;
428 push rcx
429 push rdx
430
431 ;
432 ; Get the RDX reset value using CPUID
433 ;
434 mov rax, 1
435 cpuid
436 mov rsi, rax ; Save off the reset value for RDX
437
438 ;
439 ; Prepare the GHCB for the AP_HLT_LOOP VMGEXIT call
440 ; - Must be done while in 64-bit long mode so that writes to
441 ; the GHCB memory will be unencrypted.
442 ; - No NAE events can be generated once this is set otherwise
443 ; the AP_RESET_HOLD SW_EXITCODE will be overwritten.
444 ;
445 mov rcx, 0xc0010130
446 rdmsr ; Retrieve current GHCB address
447 shl rdx, 32
448 or rdx, rax
449
450 mov rdi, rdx
451 xor rax, rax
452 mov rcx, 0x800
453 shr rcx, 3
454 rep stosq ; Clear the GHCB
455
456 mov rax, 0x80000004 ; VMGEXIT AP_RESET_HOLD
457 mov [rdx + 0x390], rax
458 mov rax, 114 ; Set SwExitCode valid bit
459 bts [rdx + 0x3f0], rax
460 inc rax ; Set SwExitInfo1 valid bit
461 bts [rdx + 0x3f0], rax
462 inc rax ; Set SwExitInfo2 valid bit
463 bts [rdx + 0x3f0], rax
464
465 pop rdx
466 pop rcx
467
468 NoSevEs:
469 cli ; Disable interrupt before switching to 32-bit mode
470 mov rax, [rsp + 40] ; CountTofinish
471 lock dec dword [rax] ; (*CountTofinish)--
472
473 mov r10, [rsp + 48] ; Pm16CodeSegment
474 mov rax, [rsp + 56] ; SevEsAPJumpTable
475 mov rbx, [rsp + 64] ; WakeupBuffer
476 mov rsp, r9 ; TopOfApStack
477
478 push rax ; Save SevEsAPJumpTable
479 push rbx ; Save WakeupBuffer
480 push r10 ; Save Pm16CodeSegment
481 push rcx ; Save MwaitSupport
482 push rdx ; Save ApTargetCState
483
484 lea rax, [PmEntry] ; rax <- The start address of transition code
485
486 push r8
487 push rax
488
489 ;
490 ; Clear R8 - R15, for reset, before going into 32-bit mode
491 ;
492 xor r8, r8
493 xor r9, r9
494 xor r10, r10
495 xor r11, r11
496 xor r12, r12
497 xor r13, r13
498 xor r14, r14
499 xor r15, r15
500
501 ;
502 ; Far return into 32-bit mode
503 ;
504 retfq
505
506 BITS 32
507 PmEntry:
508 mov eax, cr0
509 btr eax, 31 ; Clear CR0.PG
510 mov cr0, eax ; Disable paging and caches
511
512 mov ecx, 0xc0000080
513 rdmsr
514 and ah, ~ 1 ; Clear LME
515 wrmsr
516 mov eax, cr4
517 and al, ~ (1 << 5) ; Clear PAE
518 mov cr4, eax
519
520 pop edx
521 add esp, 4
522 pop ecx,
523 add esp, 4
524
525 MwaitCheck:
526 cmp cl, 1 ; Check mwait-monitor support
527 jnz HltLoop
528 mov ebx, edx ; Save C-State to ebx
529 MwaitLoop:
530 cli
531 mov eax, esp ; Set Monitor Address
532 xor ecx, ecx ; ecx = 0
533 xor edx, edx ; edx = 0
534 monitor
535 mov eax, ebx ; Mwait Cx, Target C-State per eax[7:4]
536 shl eax, 4
537 mwait
538 jmp MwaitLoop
539
540 HltLoop:
541 pop edx ; PM16CodeSegment
542 add esp, 4
543 pop ebx ; WakeupBuffer
544 add esp, 4
545 pop eax ; SevEsAPJumpTable
546 add esp, 4
547 cmp eax, 0 ; Check for SEV-ES
548 je DoHlt
549
550 cli
551 ;
552 ; SEV-ES is enabled, use VMGEXIT (GHCB information already
553 ; set by caller)
554 ;
555 BITS 64
556 rep vmmcall
557 BITS 32
558
559 ;
560 ; Back from VMGEXIT AP_HLT_LOOP
561 ; Push the FLAGS/CS/IP values to use
562 ;
563 push word 0x0002 ; EFLAGS
564 xor ecx, ecx
565 mov cx, [eax + 2] ; CS
566 push cx
567 mov cx, [eax] ; IP
568 push cx
569 push word 0x0000 ; For alignment, will be discarded
570
571 push edx
572 push ebx
573
574 mov edx, esi ; Restore RDX reset value
575
576 retf
577
578 DoHlt:
579 cli
580 hlt
581 jmp DoHlt
582
583 BITS 64
584 AsmRelocateApLoopEnd:
585
586 ;-------------------------------------------------------------------------------------
587 ; AsmGetAddressMap (&AddressMap);
588 ;-------------------------------------------------------------------------------------
589 global ASM_PFX(AsmGetAddressMap)
590 ASM_PFX(AsmGetAddressMap):
591 lea rax, [RendezvousFunnelProcStart]
592 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RendezvousFunnelAddress], rax
593 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.ModeEntryOffset], LongModeStart - RendezvousFunnelProcStart
594 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RendezvousFunnelSize], RendezvousFunnelProcEnd - RendezvousFunnelProcStart
595 lea rax, [AsmRelocateApLoopStart]
596 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RelocateApLoopFuncAddress], rax
597 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RelocateApLoopFuncSize], AsmRelocateApLoopEnd - AsmRelocateApLoopStart
598 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.ModeTransitionOffset], Flat32Start - RendezvousFunnelProcStart
599 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealSize], SwitchToRealProcEnd - SwitchToRealProcStart
600 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealOffset], SwitchToRealProcStart - RendezvousFunnelProcStart
601 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealNoNxOffset], SwitchToRealProcStart - Flat32Start
602 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealPM16ModeOffset], PM16Mode - RendezvousFunnelProcStart
603 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealPM16ModeSize], SwitchToRealProcEnd - PM16Mode
604 ret
605
606 ;-------------------------------------------------------------------------------------
607 ;AsmExchangeRole procedure follows. This procedure executed by current BSP, that is
608 ;about to become an AP. It switches its stack with the current AP.
609 ;AsmExchangeRole (IN CPU_EXCHANGE_INFO *MyInfo, IN CPU_EXCHANGE_INFO *OthersInfo);
610 ;-------------------------------------------------------------------------------------
611 global ASM_PFX(AsmExchangeRole)
612 ASM_PFX(AsmExchangeRole):
613 ; DO NOT call other functions in this function, since 2 CPU may use 1 stack
614 ; at the same time. If 1 CPU try to call a function, stack will be corrupted.
615
616 push rax
617 push rbx
618 push rcx
619 push rdx
620 push rsi
621 push rdi
622 push rbp
623 push r8
624 push r9
625 push r10
626 push r11
627 push r12
628 push r13
629 push r14
630 push r15
631
632 mov rax, cr0
633 push rax
634
635 mov rax, cr4
636 push rax
637
638 ; rsi contains MyInfo pointer
639 mov rsi, rcx
640
641 ; rdi contains OthersInfo pointer
642 mov rdi, rdx
643
644 ;Store EFLAGS, GDTR and IDTR regiter to stack
645 pushfq
646 sgdt [rsi + CPU_EXCHANGE_ROLE_INFO.Gdtr]
647 sidt [rsi + CPU_EXCHANGE_ROLE_INFO.Idtr]
648
649 ; Store the its StackPointer
650 mov [rsi + CPU_EXCHANGE_ROLE_INFO.StackPointer], rsp
651
652 ; update its switch state to STORED
653 mov byte [rsi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_STORED
654
655 WaitForOtherStored:
656 ; wait until the other CPU finish storing its state
657 cmp byte [rdi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_STORED
658 jz OtherStored
659 pause
660 jmp WaitForOtherStored
661
662 OtherStored:
663 ; Since another CPU already stored its state, load them
664 ; load GDTR value
665 lgdt [rdi + CPU_EXCHANGE_ROLE_INFO.Gdtr]
666
667 ; load IDTR value
668 lidt [rdi + CPU_EXCHANGE_ROLE_INFO.Idtr]
669
670 ; load its future StackPointer
671 mov rsp, [rdi + CPU_EXCHANGE_ROLE_INFO.StackPointer]
672
673 ; update the other CPU's switch state to LOADED
674 mov byte [rdi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_LOADED
675
676 WaitForOtherLoaded:
677 ; wait until the other CPU finish loading new state,
678 ; otherwise the data in stack may corrupt
679 cmp byte [rsi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_LOADED
680 jz OtherLoaded
681 pause
682 jmp WaitForOtherLoaded
683
684 OtherLoaded:
685 ; since the other CPU already get the data it want, leave this procedure
686 popfq
687
688 pop rax
689 mov cr4, rax
690
691 pop rax
692 mov cr0, rax
693
694 pop r15
695 pop r14
696 pop r13
697 pop r12
698 pop r11
699 pop r10
700 pop r9
701 pop r8
702 pop rbp
703 pop rdi
704 pop rsi
705 pop rdx
706 pop rcx
707 pop rbx
708 pop rax
709
710 ret