X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=UefiCpuPkg%2FLibrary%2FMpInitLib%2FX64%2FMpFuncs.nasm;h=d7e0e1fabd4b14579ab7609e310284afe742cc4b;hb=76323c31458c37c2f16288bac3e7699bdbc677d0;hp=bfc3ff1f5c7af7122a6a2546ff795169936720c1;hpb=845c5be1fd9bf7edfac4a103dfab70829686978f;p=mirror_edk2.git diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm index bfc3ff1f5c..d7e0e1fabd 100644 --- a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm @@ -1,12 +1,6 @@ ;------------------------------------------------------------------------------ ; -; Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
-; This program and the accompanying materials -; are licensed and made available under the terms and conditions of the BSD License -; which accompanies this distribution. The full text of the license may be found at -; http://opensource.org/licenses/bsd-license.php. -; -; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; Copyright (c) 2015 - 2022, Intel Corporation. All rights reserved.
+; SPDX-License-Identifier: BSD-2-Clause-Patent ; ; Module Name: ; @@ -21,6 +15,15 @@ %include "MpEqu.inc" extern ASM_PFX(InitializeFloatingPointUnits) +%macro OneTimeCall 1 + jmp %1 +%1 %+ OneTimerCallReturn: +%endmacro + +%macro OneTimeCallRet 1 + jmp %1 %+ OneTimerCallReturn +%endmacro + DEFAULT REL SECTION .text @@ -32,8 +35,6 @@ SECTION .text ;ALSO THIS PROCEDURE IS EXECUTED BY APs ONLY ON 16 BIT MODE. HENCE THIS PROC ;IS IN MACHINE CODE. ;------------------------------------------------------------------------------------- -global ASM_PFX(RendezvousFunnelProc) -ASM_PFX(RendezvousFunnelProc): RendezvousFunnelProcStart: ; At this point CS = 0x(vv00) and ip= 0x0. ; Save BIST information to ebp firstly @@ -49,150 +50,196 @@ BITS 16 mov fs, ax mov gs, ax - mov si, BufferStartLocation + mov si, MP_CPU_EXCHANGE_INFO_FIELD (BufferStart) mov ebx, [si] - mov di, ModeOffsetLocation - mov eax, [di] - mov di, CodeSegmentLocation - mov edx, [di] - mov di, ax - sub di, 02h - mov [di],dx ; Patch long mode CS - sub di, 04h - add eax, ebx - mov [di],eax ; Patch address - - mov si, GdtrLocation + mov si, MP_CPU_EXCHANGE_INFO_FIELD (DataSegment) + mov edx, [si] + + ; + ; Get start address of 32-bit code in low memory (<1MB) + ; + mov edi, MP_CPU_EXCHANGE_INFO_FIELD (ModeTransitionMemory) + + mov si, MP_CPU_EXCHANGE_INFO_FIELD (GdtrProfile) o32 lgdt [cs:si] - mov si, IdtrLocation + mov si, MP_CPU_EXCHANGE_INFO_FIELD (IdtrProfile) o32 lidt [cs:si] - mov si, EnableExecuteDisableLocation - cmp byte [si], 0 - jz SkipEnableExecuteDisableBit + ; + ; Switch to protected mode + ; + mov eax, cr0 ; Get control register 0 + or eax, 000000003h ; Set PE bit (bit #0) & MP + mov cr0, eax + + ; Switch to 32-bit code (>1MB) +o32 jmp far [cs:di] + +; +; Following code must be copied to memory with type of EfiBootServicesCode. +; This is required if NX is enabled for EfiBootServicesCode of memory. +; +BITS 32 +Flat32Start: ; protected mode entry point + mov ds, dx + mov es, dx + mov fs, dx + mov gs, dx + mov ss, dx ; ; Enable execute disable bit ; + mov esi, MP_CPU_EXCHANGE_INFO_FIELD (EnableExecuteDisable) + cmp byte [ebx + esi], 0 + jz SkipEnableExecuteDisableBit + mov ecx, 0c0000080h ; EFER MSR number rdmsr ; Read EFER bts eax, 11 ; Enable Execute Disable Bit wrmsr ; Write EFER SkipEnableExecuteDisableBit: + ; + ; Enable PAE + ; + mov eax, cr4 + bts eax, 5 - mov di, DataSegmentLocation - mov edi, [di] ; Save long mode DS in edi - - mov si, Cr3Location ; Save CR3 in ecx - mov ecx, [si] + mov esi, MP_CPU_EXCHANGE_INFO_FIELD (Enable5LevelPaging) + cmp byte [ebx + esi], 0 + jz SkipEnable5LevelPaging - xor ax, ax - mov ds, ax ; Clear data segment + ; + ; Enable 5 Level Paging + ; + bts eax, 12 ; Set LA57=1. - mov eax, cr0 ; Get control register 0 - or eax, 000000003h ; Set PE bit (bit #0) & MP - mov cr0, eax +SkipEnable5LevelPaging: - mov eax, cr4 - bts eax, 5 mov cr4, eax + ; + ; Load page table + ; + mov esi, MP_CPU_EXCHANGE_INFO_FIELD (Cr3) ; Save CR3 in ecx + mov ecx, [ebx + esi] mov cr3, ecx ; Load CR3 + ; + ; Enable long mode + ; mov ecx, 0c0000080h ; EFER MSR number rdmsr ; Read EFER bts eax, 8 ; Set LME=1 wrmsr ; Write EFER + ; + ; Enable paging + ; mov eax, cr0 ; Read CR0 bts eax, 31 ; Set PG=1 mov cr0, eax ; Write CR0 - jmp 0:strict dword 0 ; far jump to long mode + ; + ; Far jump to 64-bit code + ; + mov edi, MP_CPU_EXCHANGE_INFO_FIELD (ModeHighMemory) + add edi, ebx + jmp far [edi] + BITS 64 -LongModeStart: - mov eax, edi - mov ds, ax - mov es, ax - mov ss, ax +; +; Required for the AMD SEV helper functions +; +%include "AmdSev.nasm" + +LongModeStart: mov esi, ebx - lea edi, [esi + InitFlagLocation] + lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (InitFlag)] cmp qword [edi], 1 ; ApInitConfig jnz GetApicId - ; AP init - mov esi, ebx + ; Increment the number of APs executing here as early as possible + ; This is decremented in C code when AP is finished executing mov edi, esi - add edi, LockLocation - mov rax, NotVacantFlag - -TestLock: - xchg qword [edi], rax - cmp rax, NotVacantFlag - jz TestLock + add edi, MP_CPU_EXCHANGE_INFO_FIELD (NumApsExecuting) + lock inc dword [edi] - lea ecx, [esi + InitFlagLocation] - inc dword [ecx] - mov ebx, [ecx] + ; AP init + mov edi, esi + add edi, MP_CPU_EXCHANGE_INFO_FIELD (ApIndex) + mov ebx, 1 + lock xadd dword [edi], ebx ; EBX = ApIndex++ + inc ebx ; EBX is CpuNumber -Releaselock: - mov rax, VacantFlag - xchg qword [edi], rax ; program stack mov edi, esi - add edi, StackSizeLocation + add edi, MP_CPU_EXCHANGE_INFO_FIELD (StackSize) mov eax, dword [edi] mov ecx, ebx inc ecx mul ecx ; EAX = StackSize * (CpuNumber + 1) mov edi, esi - add edi, StackStartAddressLocation + add edi, MP_CPU_EXCHANGE_INFO_FIELD (StackStart) add rax, qword [edi] mov rsp, rax + + ; + ; Setup the GHCB when AMD SEV-ES active. + ; + OneTimeCall SevEsSetupGhcb jmp CProcedureInvoke GetApicId: + ; + ; Use the GHCB protocol to get the ApicId when SEV-ES is active. + ; + OneTimeCall SevEsGetApicId + +DoCpuid: mov eax, 0 cpuid cmp eax, 0bh - jnb X2Apic + jb NoX2Apic ; CPUID level below CPUID_EXTENDED_TOPOLOGY + + mov eax, 0bh + xor ecx, ecx + cpuid + test ebx, 0ffffh + jz NoX2Apic ; CPUID.0BH:EBX[15:0] is zero + + ; Processor is x2APIC capable; 32-bit x2APIC ID is already in EDX + jmp GetProcessorNumber + +NoX2Apic: ; Processor is not x2APIC capable, so get 8-bit APIC ID mov eax, 1 cpuid shr ebx, 24 mov edx, ebx - jmp GetProcessorNumber -X2Apic: - ; Processor is x2APIC capable, so get 32-bit x2APIC ID - mov eax, 0bh - xor ecx, ecx - cpuid - ; edx save x2APIC ID - GetProcessorNumber: ; ; Get processor number for this AP ; Note that BSP may become an AP due to SwitchBsp() ; xor ebx, ebx - lea eax, [esi + CpuInfoLocation] - mov edi, [eax] + lea eax, [esi + MP_CPU_EXCHANGE_INFO_FIELD (CpuInfo)] + mov rdi, [eax] GetNextProcNumber: - cmp dword [edi], edx ; APIC ID match? + cmp dword [rdi + CPU_INFO_IN_HOB.InitialApicId], edx ; APIC ID match? jz ProgramStack - add edi, 16 + add rdi, CPU_INFO_IN_HOB_size inc ebx - jmp GetNextProcNumber + jmp GetNextProcNumber ProgramStack: - xor rsp, rsp - mov esp, dword [edi + 12] + mov rsp, qword [rdi + CPU_INFO_IN_HOB.ApTopOfStack] CProcedureInvoke: push rbp ; Push BIST data at top of AP stack @@ -200,17 +247,17 @@ CProcedureInvoke: push rbp mov rbp, rsp - mov rax, ASM_PFX(InitializeFloatingPointUnits) + mov rax, qword [esi + MP_CPU_EXCHANGE_INFO_FIELD (InitializeFloatingPointUnits)] sub rsp, 20h call rax ; Call assembly function to initialize FPU per UEFI spec add rsp, 20h - mov edx, ebx ; edx is NumApsExecuting + mov edx, ebx ; edx is ApIndex mov ecx, esi - add ecx, LockLocation ; rcx is address of exchange info data buffer + add ecx, MP_CPU_EXCHANGE_INFO_OFFSET ; rcx is address of exchange info data buffer mov edi, esi - add edi, ApProcedureLocation + add edi, MP_CPU_EXCHANGE_INFO_FIELD (CFunction) mov rax, qword [edi] sub rsp, 20h @@ -221,27 +268,247 @@ CProcedureInvoke: RendezvousFunnelProcEnd: ;------------------------------------------------------------------------------------- -; AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment); +;SwitchToRealProc procedure follows. +;ALSO THIS PROCEDURE IS EXECUTED BY APs TRANSITIONING TO 16 BIT MODE. HENCE THIS PROC +;IS IN MACHINE CODE. +; SwitchToRealProc (UINTN BufferStart, UINT16 Code16, UINT16 Code32, UINTN StackStart) +; rcx - Buffer Start +; rdx - Code16 Selector Offset +; r8 - Code32 Selector Offset +; r9 - Stack Start ;------------------------------------------------------------------------------------- -global ASM_PFX(AsmRelocateApLoop) -ASM_PFX(AsmRelocateApLoop): -AsmRelocateApLoopStart: +SwitchToRealProcStart: +BITS 64 + cli + + ; + ; Get RDX reset value before changing stacks since the + ; new stack won't be able to accomodate a #VC exception. + ; + push rax + push rbx push rcx push rdx - lea rsi, [PmEntry] ; rsi <- The start address of transition code + mov rax, 1 + cpuid + mov rsi, rax ; Save off the reset value for RDX + + pop rdx + pop rcx + pop rbx + pop rax + + ; + ; Establish stack below 1MB + ; + mov rsp, r9 + + ; + ; Push ultimate Reset Vector onto the stack + ; + mov rax, rcx + shr rax, 4 + push word 0x0002 ; RFLAGS + push ax ; CS + push word 0x0000 ; RIP + push word 0x0000 ; For alignment, will be discarded + + ; + ; Get address of "16-bit operand size" label + ; + lea rbx, [PM16Mode] + ; + ; Push addresses used to change to compatibility mode + ; + lea rax, [CompatMode] push r8 - push rsi - DB 0x48 + push rax + + ; + ; Clear R8 - R15, for reset, before going into 32-bit mode + ; + xor r8, r8 + xor r9, r9 + xor r10, r10 + xor r11, r11 + xor r12, r12 + xor r13, r13 + xor r14, r14 + xor r15, r15 + + ; + ; Far return into 32-bit mode + ; + retfq + +BITS 32 +CompatMode: + ; + ; Set up stack to prepare for exiting protected mode + ; + push edx ; Code16 CS + push ebx ; PM16Mode label address + + ; + ; Disable paging + ; + mov eax, cr0 ; Read CR0 + btr eax, 31 ; Set PG=0 + mov cr0, eax ; Write CR0 + + ; + ; Disable long mode + ; + mov ecx, 0c0000080h ; EFER MSR number + rdmsr ; Read EFER + btr eax, 8 ; Set LME=0 + wrmsr ; Write EFER + + ; + ; Disable PAE + ; + mov eax, cr4 ; Read CR4 + btr eax, 5 ; Set PAE=0 + mov cr4, eax ; Write CR4 + + mov edx, esi ; Restore RDX reset value + + ; + ; Switch to 16-bit operand size + ; retf + +BITS 16 + ; + ; At entry to this label + ; - RDX will have its reset value + ; - On the top of the stack + ; - Alignment data (two bytes) to be discarded + ; - IP for Real Mode (two bytes) + ; - CS for Real Mode (two bytes) + ; + ; This label is also used with AsmRelocateApLoop. During MP finalization, + ; the code from PM16Mode to SwitchToRealProcEnd is copied to the start of + ; the WakeupBuffer, allowing a parked AP to be booted by an OS. + ; +PM16Mode: + mov eax, cr0 ; Read CR0 + btr eax, 0 ; Set PE=0 + mov cr0, eax ; Write CR0 + + pop ax ; Discard alignment data + + ; + ; Clear registers (except RDX and RSP) before going into 16-bit mode + ; + xor eax, eax + xor ebx, ebx + xor ecx, ecx + xor esi, esi + xor edi, edi + xor ebp, ebp + + iret + +SwitchToRealProcEnd: + +;------------------------------------------------------------------------------------- +; AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish, Pm16CodeSegment, SevEsAPJumpTable, WakeupBuffer); +;------------------------------------------------------------------------------------- +AsmRelocateApLoopStart: +BITS 64 + cmp qword [rsp + 56], 0 ; SevEsAPJumpTable + je NoSevEs + + ; + ; Perform some SEV-ES related setup before leaving 64-bit mode + ; + push rcx + push rdx + + ; + ; Get the RDX reset value using CPUID + ; + mov rax, 1 + cpuid + mov rsi, rax ; Save off the reset value for RDX + + ; + ; Prepare the GHCB for the AP_HLT_LOOP VMGEXIT call + ; - Must be done while in 64-bit long mode so that writes to + ; the GHCB memory will be unencrypted. + ; - No NAE events can be generated once this is set otherwise + ; the AP_RESET_HOLD SW_EXITCODE will be overwritten. + ; + mov rcx, 0xc0010130 + rdmsr ; Retrieve current GHCB address + shl rdx, 32 + or rdx, rax + + mov rdi, rdx + xor rax, rax + mov rcx, 0x800 + shr rcx, 3 + rep stosq ; Clear the GHCB + + mov rax, 0x80000004 ; VMGEXIT AP_RESET_HOLD + mov [rdx + 0x390], rax + mov rax, 114 ; Set SwExitCode valid bit + bts [rdx + 0x3f0], rax + inc rax ; Set SwExitInfo1 valid bit + bts [rdx + 0x3f0], rax + inc rax ; Set SwExitInfo2 valid bit + bts [rdx + 0x3f0], rax + + pop rdx + pop rcx + +NoSevEs: + cli ; Disable interrupt before switching to 32-bit mode + mov rax, [rsp + 40] ; CountTofinish + lock dec dword [rax] ; (*CountTofinish)-- + + mov r10, [rsp + 48] ; Pm16CodeSegment + mov rax, [rsp + 56] ; SevEsAPJumpTable + mov rbx, [rsp + 64] ; WakeupBuffer + mov rsp, r9 ; TopOfApStack + + push rax ; Save SevEsAPJumpTable + push rbx ; Save WakeupBuffer + push r10 ; Save Pm16CodeSegment + push rcx ; Save MwaitSupport + push rdx ; Save ApTargetCState + + lea rax, [PmEntry] ; rax <- The start address of transition code + + push r8 + push rax + + ; + ; Clear R8 - R15, for reset, before going into 32-bit mode + ; + xor r8, r8 + xor r9, r9 + xor r10, r10 + xor r11, r11 + xor r12, r12 + xor r13, r13 + xor r14, r14 + xor r15, r15 + + ; + ; Far return into 32-bit mode + ; + retfq + BITS 32 PmEntry: mov eax, cr0 btr eax, 31 ; Clear CR0.PG mov cr0, eax ; Disable paging and caches - mov ebx, edx ; Save EntryPoint to rbx, for rdmsr will overwrite rdx mov ecx, 0xc0000080 rdmsr and ah, ~ 1 ; Clear LME @@ -254,23 +521,65 @@ PmEntry: add esp, 4 pop ecx, add esp, 4 + +MwaitCheck: cmp cl, 1 ; Check mwait-monitor support jnz HltLoop mov ebx, edx ; Save C-State to ebx MwaitLoop: + cli mov eax, esp ; Set Monitor Address xor ecx, ecx ; ecx = 0 xor edx, edx ; edx = 0 monitor - shl ebx, 4 mov eax, ebx ; Mwait Cx, Target C-State per eax[7:4] + shl eax, 4 mwait jmp MwaitLoop + HltLoop: + pop edx ; PM16CodeSegment + add esp, 4 + pop ebx ; WakeupBuffer + add esp, 4 + pop eax ; SevEsAPJumpTable + add esp, 4 + cmp eax, 0 ; Check for SEV-ES + je DoHlt + + cli + ; + ; SEV-ES is enabled, use VMGEXIT (GHCB information already + ; set by caller) + ; +BITS 64 + rep vmmcall +BITS 32 + + ; + ; Back from VMGEXIT AP_HLT_LOOP + ; Push the FLAGS/CS/IP values to use + ; + push word 0x0002 ; EFLAGS + xor ecx, ecx + mov cx, [eax + 2] ; CS + push cx + mov cx, [eax] ; IP + push cx + push word 0x0000 ; For alignment, will be discarded + + push edx + push ebx + + mov edx, esi ; Restore RDX reset value + + retf + +DoHlt: cli hlt - jmp HltLoop - ret + jmp DoHlt + BITS 64 AsmRelocateApLoopEnd: @@ -279,13 +588,19 @@ AsmRelocateApLoopEnd: ;------------------------------------------------------------------------------------- global ASM_PFX(AsmGetAddressMap) ASM_PFX(AsmGetAddressMap): - mov rax, ASM_PFX(RendezvousFunnelProc) - mov qword [rcx], rax - mov qword [rcx + 8h], LongModeStart - RendezvousFunnelProcStart - mov qword [rcx + 10h], RendezvousFunnelProcEnd - RendezvousFunnelProcStart - mov rax, ASM_PFX(AsmRelocateApLoop) - mov qword [rcx + 18h], rax - mov qword [rcx + 20h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart + lea rax, [RendezvousFunnelProcStart] + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RendezvousFunnelAddress], rax + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.ModeEntryOffset], LongModeStart - RendezvousFunnelProcStart + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RendezvousFunnelSize], RendezvousFunnelProcEnd - RendezvousFunnelProcStart + lea rax, [AsmRelocateApLoopStart] + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RelocateApLoopFuncAddress], rax + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RelocateApLoopFuncSize], AsmRelocateApLoopEnd - AsmRelocateApLoopStart + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.ModeTransitionOffset], Flat32Start - RendezvousFunnelProcStart + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealSize], SwitchToRealProcEnd - SwitchToRealProcStart + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealOffset], SwitchToRealProcStart - RendezvousFunnelProcStart + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealNoNxOffset], SwitchToRealProcStart - Flat32Start + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealPM16ModeOffset], PM16Mode - RendezvousFunnelProcStart + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealPM16ModeSize], SwitchToRealProcEnd - PM16Mode ret ;------------------------------------------------------------------------------------- @@ -328,18 +643,18 @@ ASM_PFX(AsmExchangeRole): ;Store EFLAGS, GDTR and IDTR regiter to stack pushfq - sgdt [rsi + 16] - sidt [rsi + 26] + sgdt [rsi + CPU_EXCHANGE_ROLE_INFO.Gdtr] + sidt [rsi + CPU_EXCHANGE_ROLE_INFO.Idtr] ; Store the its StackPointer - mov [rsi + 8], rsp + mov [rsi + CPU_EXCHANGE_ROLE_INFO.StackPointer], rsp ; update its switch state to STORED - mov byte [rsi], CPU_SWITCH_STATE_STORED + mov byte [rsi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_STORED WaitForOtherStored: ; wait until the other CPU finish storing its state - cmp byte [rdi], CPU_SWITCH_STATE_STORED + cmp byte [rdi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_STORED jz OtherStored pause jmp WaitForOtherStored @@ -347,21 +662,21 @@ WaitForOtherStored: OtherStored: ; Since another CPU already stored its state, load them ; load GDTR value - lgdt [rdi + 16] + lgdt [rdi + CPU_EXCHANGE_ROLE_INFO.Gdtr] ; load IDTR value - lidt [rdi + 26] + lidt [rdi + CPU_EXCHANGE_ROLE_INFO.Idtr] ; load its future StackPointer - mov rsp, [rdi + 8] + mov rsp, [rdi + CPU_EXCHANGE_ROLE_INFO.StackPointer] ; update the other CPU's switch state to LOADED - mov byte [rdi], CPU_SWITCH_STATE_LOADED + mov byte [rdi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_LOADED WaitForOtherLoaded: ; wait until the other CPU finish loading new state, ; otherwise the data in stack may corrupt - cmp byte [rsi], CPU_SWITCH_STATE_LOADED + cmp byte [rsi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_LOADED jz OtherLoaded pause jmp WaitForOtherLoaded