1 ;------------------------------------------------------------------------------ ;
2 ; Copyright (c) 2015 - 2022, Intel Corporation. All rights reserved.<BR>
3 ; SPDX-License-Identifier: BSD-2-Clause-Patent
11 ; This is the assembly code for MP support
13 ;-------------------------------------------------------------------------------
16 extern ASM_PFX(InitializeFloatingPointUnits)
20 %1 %+ OneTimerCallReturn:
23 %macro OneTimeCallRet 1
24 jmp %1 %+ OneTimerCallReturn
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
37 ;-------------------------------------------------------------------------------------
38 RendezvousFunnelProcStart:
39 ; At this point CS = 0x(vv00) and ip= 0x0.
40 ; Save BIST information to ebp firstly
43 mov ebp, eax ; Save BIST information
53 mov si, MP_CPU_EXCHANGE_INFO_FIELD (BufferStart)
56 mov si, MP_CPU_EXCHANGE_INFO_FIELD (DataSegment)
60 ; Get start address of 32-bit code in low memory (<1MB)
62 mov edi, MP_CPU_EXCHANGE_INFO_FIELD (ModeTransitionMemory)
64 mov si, MP_CPU_EXCHANGE_INFO_FIELD (GdtrProfile)
68 ; Switch to protected mode
70 mov eax, cr0 ; Get control register 0
71 or eax, 000000003h ; Set PE bit (bit #0) & MP
74 ; Switch to 32-bit code (>1MB)
78 ; Following code must be copied to memory with type of EfiBootServicesCode.
79 ; This is required if NX is enabled for EfiBootServicesCode of memory.
82 Flat32Start: ; protected mode entry point
90 ; Enable execute disable bit
92 mov esi, MP_CPU_EXCHANGE_INFO_FIELD (EnableExecuteDisable)
93 cmp byte [ebx + esi], 0
94 jz SkipEnableExecuteDisableBit
96 mov ecx, 0c0000080h ; EFER MSR number
98 bts eax, 11 ; Enable Execute Disable Bit
101 SkipEnableExecuteDisableBit:
108 mov esi, MP_CPU_EXCHANGE_INFO_FIELD (Enable5LevelPaging)
109 cmp byte [ebx + esi], 0
110 jz SkipEnable5LevelPaging
113 ; Enable 5 Level Paging
115 bts eax, 12 ; Set LA57=1.
117 SkipEnable5LevelPaging:
124 mov esi, MP_CPU_EXCHANGE_INFO_FIELD (Cr3) ; Save CR3 in ecx
126 mov cr3, ecx ; Load CR3
131 mov ecx, 0c0000080h ; EFER MSR number
133 bts eax, 8 ; Set LME=1
139 mov eax, cr0 ; Read CR0
140 bts eax, 31 ; Set PG=1
141 mov cr0, eax ; Write CR0
144 ; Far jump to 64-bit code
146 mov edi, MP_CPU_EXCHANGE_INFO_FIELD (ModeHighMemory)
155 ; Set IDT table at the start of 64 bit code
156 lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (IdtrProfile)]
159 lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (InitFlag)]
160 cmp qword [edi], 1 ; ApInitConfig
163 ; Increment the number of APs executing here as early as possible
164 ; This is decremented in C code when AP is finished executing
166 add edi, MP_CPU_EXCHANGE_INFO_FIELD (NumApsExecuting)
171 add edi, MP_CPU_EXCHANGE_INFO_FIELD (ApIndex)
173 lock xadd dword [edi], ebx ; EBX = ApIndex++
174 inc ebx ; EBX is CpuNumber
178 add edi, MP_CPU_EXCHANGE_INFO_FIELD (StackSize)
182 mul ecx ; EAX = StackSize * (CpuNumber + 1)
184 add edi, MP_CPU_EXCHANGE_INFO_FIELD (StackStart)
189 ; Setup the GHCB when AMD SEV-ES active.
191 OneTimeCall SevEsSetupGhcb
196 ; Use the GHCB protocol to get the ApicId when SEV-ES is active.
198 OneTimeCall SevEsGetApicId
204 jb NoX2Apic ; CPUID level below CPUID_EXTENDED_TOPOLOGY
210 jz NoX2Apic ; CPUID.0BH:EBX[15:0] is zero
212 ; Processor is x2APIC capable; 32-bit x2APIC ID is already in EDX
213 jmp GetProcessorNumber
216 ; Processor is not x2APIC capable, so get 8-bit APIC ID
224 ; Get processor number for this AP
225 ; Note that BSP may become an AP due to SwitchBsp()
228 lea eax, [esi + MP_CPU_EXCHANGE_INFO_FIELD (CpuInfo)]
232 cmp dword [rdi + CPU_INFO_IN_HOB.InitialApicId], edx ; APIC ID match?
234 add rdi, CPU_INFO_IN_HOB_size
236 jmp GetNextProcNumber
239 mov rsp, qword [rdi + CPU_INFO_IN_HOB.ApTopOfStack]
242 push rbp ; Push BIST data at top of AP stack
243 xor rbp, rbp ; Clear ebp for call stack trace
247 mov rax, qword [esi + MP_CPU_EXCHANGE_INFO_FIELD (InitializeFloatingPointUnits)]
249 call rax ; Call assembly function to initialize FPU per UEFI spec
252 mov edx, ebx ; edx is ApIndex
254 add ecx, MP_CPU_EXCHANGE_INFO_OFFSET ; rcx is address of exchange info data buffer
257 add edi, MP_CPU_EXCHANGE_INFO_FIELD (CFunction)
261 call rax ; Invoke C function
263 jmp $ ; Should never reach here
266 ; Required for the AMD SEV helper functions
268 %include "AmdSev.nasm"
270 RendezvousFunnelProcEnd:
272 ;-------------------------------------------------------------------------------------
273 ; AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish, Pm16CodeSegment, SevEsAPJumpTable, WakeupBuffer);
274 ;-------------------------------------------------------------------------------------
275 AsmRelocateApLoopStart:
277 cmp qword [rsp + 56], 0 ; SevEsAPJumpTable
281 ; Perform some SEV-ES related setup before leaving 64-bit mode
287 ; Get the RDX reset value using CPUID
291 mov rsi, rax ; Save off the reset value for RDX
294 ; Prepare the GHCB for the AP_HLT_LOOP VMGEXIT call
295 ; - Must be done while in 64-bit long mode so that writes to
296 ; the GHCB memory will be unencrypted.
297 ; - No NAE events can be generated once this is set otherwise
298 ; the AP_RESET_HOLD SW_EXITCODE will be overwritten.
301 rdmsr ; Retrieve current GHCB address
309 rep stosq ; Clear the GHCB
311 mov rax, 0x80000004 ; VMGEXIT AP_RESET_HOLD
312 mov [rdx + 0x390], rax
313 mov rax, 114 ; Set SwExitCode valid bit
314 bts [rdx + 0x3f0], rax
315 inc rax ; Set SwExitInfo1 valid bit
316 bts [rdx + 0x3f0], rax
317 inc rax ; Set SwExitInfo2 valid bit
318 bts [rdx + 0x3f0], rax
324 cli ; Disable interrupt before switching to 32-bit mode
325 mov rax, [rsp + 40] ; CountTofinish
326 lock dec dword [rax] ; (*CountTofinish)--
328 mov r10, [rsp + 48] ; Pm16CodeSegment
329 mov rax, [rsp + 56] ; SevEsAPJumpTable
330 mov rbx, [rsp + 64] ; WakeupBuffer
331 mov rsp, r9 ; TopOfApStack
333 push rax ; Save SevEsAPJumpTable
334 push rbx ; Save WakeupBuffer
335 push r10 ; Save Pm16CodeSegment
336 push rcx ; Save MwaitSupport
337 push rdx ; Save ApTargetCState
339 lea rax, [PmEntry] ; rax <- The start address of transition code
345 ; Clear R8 - R15, for reset, before going into 32-bit mode
357 ; Far return into 32-bit mode
364 btr eax, 31 ; Clear CR0.PG
365 mov cr0, eax ; Disable paging and caches
369 and ah, ~ 1 ; Clear LME
372 and al, ~ (1 << 5) ; Clear PAE
381 cmp cl, 1 ; Check mwait-monitor support
383 mov ebx, edx ; Save C-State to ebx
386 mov eax, esp ; Set Monitor Address
387 xor ecx, ecx ; ecx = 0
388 xor edx, edx ; edx = 0
390 mov eax, ebx ; Mwait Cx, Target C-State per eax[7:4]
396 pop edx ; PM16CodeSegment
398 pop ebx ; WakeupBuffer
400 pop eax ; SevEsAPJumpTable
402 cmp eax, 0 ; Check for SEV-ES
407 ; SEV-ES is enabled, use VMGEXIT (GHCB information already
415 ; Back from VMGEXIT AP_HLT_LOOP
416 ; Push the FLAGS/CS/IP values to use
418 push word 0x0002 ; EFLAGS
420 mov cx, [eax + 2] ; CS
424 push word 0x0000 ; For alignment, will be discarded
429 mov edx, esi ; Restore RDX reset value
439 AsmRelocateApLoopEnd:
441 ;-------------------------------------------------------------------------------------
442 ; AsmGetAddressMap (&AddressMap);
443 ;-------------------------------------------------------------------------------------
444 global ASM_PFX(AsmGetAddressMap)
445 ASM_PFX(AsmGetAddressMap):
446 lea rax, [RendezvousFunnelProcStart]
447 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RendezvousFunnelAddress], rax
448 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.ModeEntryOffset], LongModeStart - RendezvousFunnelProcStart
449 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RendezvousFunnelSize], RendezvousFunnelProcEnd - RendezvousFunnelProcStart
450 lea rax, [AsmRelocateApLoopStart]
451 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RelocateApLoopFuncAddress], rax
452 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RelocateApLoopFuncSize], AsmRelocateApLoopEnd - AsmRelocateApLoopStart
453 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.ModeTransitionOffset], Flat32Start - RendezvousFunnelProcStart
454 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealNoNxOffset], SwitchToRealProcStart - Flat32Start
455 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealPM16ModeOffset], PM16Mode - RendezvousFunnelProcStart
456 mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealPM16ModeSize], SwitchToRealProcEnd - PM16Mode
459 ;-------------------------------------------------------------------------------------
460 ;AsmExchangeRole procedure follows. This procedure executed by current BSP, that is
461 ;about to become an AP. It switches its stack with the current AP.
462 ;AsmExchangeRole (IN CPU_EXCHANGE_INFO *MyInfo, IN CPU_EXCHANGE_INFO *OthersInfo);
463 ;-------------------------------------------------------------------------------------
464 global ASM_PFX(AsmExchangeRole)
465 ASM_PFX(AsmExchangeRole):
466 ; DO NOT call other functions in this function, since 2 CPU may use 1 stack
467 ; at the same time. If 1 CPU try to call a function, stack will be corrupted.
485 ; rsi contains MyInfo pointer
488 ; rdi contains OthersInfo pointer
493 ; Store the its StackPointer
494 mov [rsi + CPU_EXCHANGE_ROLE_INFO.StackPointer], rsp
496 ; update its switch state to STORED
497 mov byte [rsi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_STORED
500 ; wait until the other CPU finish storing its state
501 cmp byte [rdi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_STORED
504 jmp WaitForOtherStored
508 ; load its future StackPointer
509 mov rsp, [rdi + CPU_EXCHANGE_ROLE_INFO.StackPointer]
511 ; update the other CPU's switch state to LOADED
512 mov byte [rdi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_LOADED
515 ; wait until the other CPU finish loading new state,
516 ; otherwise the data in stack may corrupt
517 cmp byte [rsi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_LOADED
520 jmp WaitForOtherLoaded
523 ; since the other CPU already get the data it want, leave this procedure