diff --git a/UefiCpuPkg/Library/MpInitLib/AmdSev.c b/UefiCpuPkg/Library/MpInitLib/AmdSev.c index 75429e3dae..5108873c3b 100644 --- a/UefiCpuPkg/Library/MpInitLib/AmdSev.c +++ b/UefiCpuPkg/Library/MpInitLib/AmdSev.c @@ -293,6 +293,8 @@ FillExchangeInfoDataSevEs ( ); ExchangeInfo->ExtTopoAvail = !!ExtTopoEbx.Bits.LogicalProcessors; } + + ExchangeInfo->SevSnpKnownInitApicId = FALSE; } /** diff --git a/UefiCpuPkg/Library/MpInitLib/MpEqu.inc b/UefiCpuPkg/Library/MpInitLib/MpEqu.inc index 317e627b58..d8ba9ea124 100644 --- a/UefiCpuPkg/Library/MpInitLib/MpEqu.inc +++ b/UefiCpuPkg/Library/MpInitLib/MpEqu.inc @@ -97,6 +97,7 @@ struc MP_CPU_EXCHANGE_INFO .SevSnpIsEnabled CTYPE_BOOLEAN 1 .GhcbBase: CTYPE_UINTN 1 .ExtTopoAvail: CTYPE_BOOLEAN 1 + .SevSnpKnownInitApicId: CTYPE_BOOLEAN 1 endstruc MP_CPU_EXCHANGE_INFO_OFFSET equ (Flat32Start - RendezvousFunnelProcStart) diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h b/UefiCpuPkg/Library/MpInitLib/MpLib.h index 145538b6ee..a63bb81bef 100644 --- a/UefiCpuPkg/Library/MpInitLib/MpLib.h +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h @@ -239,6 +239,7 @@ typedef struct { BOOLEAN SevSnpIsEnabled; UINTN GhcbBase; BOOLEAN ExtTopoAvail; + BOOLEAN SevSnpKnownInitApicId; } MP_CPU_EXCHANGE_INFO; #pragma pack() diff --git a/UefiCpuPkg/Library/MpInitLib/X64/AmdSev.c b/UefiCpuPkg/Library/MpInitLib/X64/AmdSev.c index a75e1e2018..56cd7a1138 100644 --- a/UefiCpuPkg/Library/MpInitLib/X64/AmdSev.c +++ b/UefiCpuPkg/Library/MpInitLib/X64/AmdSev.c @@ -271,18 +271,27 @@ SevSnpCreateAP ( IN INTN ProcessorNumber ) { - CPU_INFO_IN_HOB *CpuInfoInHob; - CPU_AP_DATA *CpuData; - UINTN Index; - UINTN MaxIndex; - UINT32 ApicId; - EFI_HOB_GUID_TYPE *GuidHob; - GHCB_APIC_IDS *GhcbApicIds; + CPU_INFO_IN_HOB *CpuInfoInHob; + CPU_AP_DATA *CpuData; + UINTN Index; + UINTN MaxIndex; + UINT32 ApicId; + EFI_HOB_GUID_TYPE *GuidHob; + GHCB_APIC_IDS *GhcbApicIds; + volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo; ASSERT (CpuMpData->MpCpuExchangeInfo->BufferStart < 0x100000); + ExchangeInfo = CpuMpData->MpCpuExchangeInfo; CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; + // + // Set to FALSE by default. This is only set to TRUE when the InitFlag + // is equal to ApInitConfig and the GHCB APIC ID List NAE event has + // been called. + // + ExchangeInfo->SevSnpKnownInitApicId = FALSE; + if (ProcessorNumber < 0) { if (CpuMpData->InitFlag == ApInitConfig) { // @@ -294,6 +303,14 @@ SevSnpCreateAP ( GuidHob = GetFirstGuidHob (&gGhcbApicIdsGuid); GhcbApicIds = (GHCB_APIC_IDS *)(*(UINTN *)GET_GUID_HOB_DATA (GuidHob)); MaxIndex = MIN (GhcbApicIds->NumEntries, PcdGet32 (PcdCpuMaxLogicalProcessorNumber)); + + // + // Set to TRUE so that ApicId and SEV-SNP SaveArea stay in sync. When + // the InitFlag is ApInitConfig, the random order of AP initialization + // can end up with an Index / ApicId mismatch (see X64/AmdSev.nasm). + // + ExchangeInfo->SevSnpKnownInitApicId = TRUE; + DEBUG ((DEBUG_INFO, "SEV-SNP: Using known initial APIC IDs\n")); } else { // // APs have been previously started. @@ -308,6 +325,13 @@ SevSnpCreateAP ( if (CpuMpData->InitFlag == ApInitConfig) { ApicId = GhcbApicIds->ApicIds[Index]; + // + // Set the ApicId values so that the proper AP data structure + // can be found during boot. + // + CpuInfoInHob[Index].InitialApicId = ApicId; + CpuInfoInHob[Index].ApicId = ApicId; + // // For the first boot, use the BSP register information. // diff --git a/UefiCpuPkg/Library/MpInitLib/X64/AmdSev.nasm b/UefiCpuPkg/Library/MpInitLib/X64/AmdSev.nasm index 2efa3cb104..66d63a2b90 100644 --- a/UefiCpuPkg/Library/MpInitLib/X64/AmdSev.nasm +++ b/UefiCpuPkg/Library/MpInitLib/X64/AmdSev.nasm @@ -15,6 +15,57 @@ %define SIZE_4KB 0x1000 +; +; This function will ensure that CpuNumber and ApicId are in sync when using +; the ApicIds retrieved via the GHCB APIC ID List NAE event to start the APs +; when the InitFlag is ApInitConfig. If this is not done, the CpuNumber to +; ApicId relationship may not hold, which would result in the ApicId to VSMA +; relationship getting out of sync after the first AP boot. +; +SevSnpGetInitCpuNumber: + ; + ; If not an SNP guest, leave EBX (CpuNumber) as is + ; + lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevSnpIsEnabled)] + cmp byte [edi], 1 ; SevSnpIsEnabled + jne SevSnpGetCpuNumberDone + + ; + ; If not starting the AP with a specific ApicId, leave EBX (CpuNumber) as is + ; + lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevSnpKnownInitApicId)] + cmp byte [edi], 1 ; SevSnpKnownInitApicId + jne SevSnpGetCpuNumberDone + + ; + ; Use existing code to retrieve the ApicId. SevEsGetApicId will return to + ; the SevSnpGetInitApicId label if SevSnpKnownInitApicId is set. + ; + jmp SevEsGetApicId + +SevSnpGetInitApicId: + ; + ; EDX holds the ApicId, get processor number for this AP + ; + xor ebx, ebx + lea eax, [esi + MP_CPU_EXCHANGE_INFO_FIELD (CpuInfo)] + mov rdi, [eax] + +SevSnpGetNextProcNumber: + cmp dword [rdi + CPU_INFO_IN_HOB.InitialApicId], edx ; APIC ID match? + jz SevSnpGetCpuNumberDone + add rdi, CPU_INFO_IN_HOB_size + inc ebx + jmp SevSnpGetNextProcNumber + +SevSnpGetCpuNumberDone: + ; + ; If SevSnpKnownInitApicId is set, EBX now holds the CpuNumber for this + ; ApicId, which matches how it was started in SevSnpCreateAP(). Otherwise, + ; EBX is unchanged and holds the CpuNumber based on the startup order. + ; + OneTimeCallRet SevSnpGetInitCpuNumber + RegisterGhcbGpa: ; ; Register GHCB GPA when SEV-SNP is enabled @@ -193,7 +244,14 @@ RestoreGhcb: mov rdx, rbx - ; x2APIC ID or APIC ID is in EDX + ; + ; x2APIC ID or APIC ID is in EDX. If SevSnpKnownInitApicId is set, then + ; return to SevSnpGetInitApicId, otherwise return to GetProcessorNumber. + ; + lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevSnpKnownInitApicId)] + cmp byte [edi], 1 ; SevSnpKnownInitApicId + je SevSnpGetInitApicId + jmp GetProcessorNumber SevEsGetApicIdExit: diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm index 40e80ffab4..95ee65e288 100644 --- a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm +++ b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm @@ -173,6 +173,10 @@ LongModeStart: lock xadd dword [edi], ebx ; EBX = ApIndex++ inc ebx ; EBX is CpuNumber + ; If running under AMD SEV-SNP and starting with a known ApicId, + ; adjust EBX to be the actual CpuNumber + OneTimeCall SevSnpGetInitCpuNumber + ; program stack mov edi, esi add edi, MP_CPU_EXCHANGE_INFO_FIELD (StackSize)