Skip to content

Commit dca5d26

Browse files
tlendackymergify[bot]
authored andcommitted
UefiCpuPkg/MpInitLib: Fix SNP AP creation when using known APIC IDs
A typical initial AP boot up will choose a CpuNumber based on the ApIndex value that it gets back after a locked increment of the ApIndex value. The ApIndex to APIC ID relationship is random, which is not an issue when a broadcast INIT-SIPI is performed. With SNP and a hypervisor that supports retrieval of the known APIC IDs, the broadcast INIT-SIPI method is replaced by waking each individual vCPU. In this situation, a specific VMSA is associated with a specific APIC ID. However, random assignment of an ApIndex can break this association. This isn't typically an issue, because the AP bring-up finishes with the AP issuing a HLT instruction, which is intercepted by the hypervisor and the AP won't run again until the next INIT-SIPI. However, when HLT isn't intercepted by the hypervisor (Qemu '-overcommit cpu-pm=on' parameter), then the HLT does not exit to the hypervisor. On the next INIT-SIPI, it can happen that a VMRUN is executed with a different VMSA address than was originally used, and if that VMSA is still in a VMRUN on another AP, then the executing VMRUN will fail, crashing the guest. To fix this issue, add a CPU exchange info field, SevSnpKnownInitApicId, that indicates the APs are starting with an already known initial APIC ID and set the initial APIC ID and APIC ID in the CPU_INFO_IN_HOB HOB. During AP boot, the SevSnpKnownInitApicId field will result in the CpuNumber being set to the index with a matching APIC ID (similar to AP booting when the InitFlag != ApInitConfig). Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
1 parent dd8c272 commit dca5d26

File tree

6 files changed

+98
-8
lines changed

6 files changed

+98
-8
lines changed

UefiCpuPkg/Library/MpInitLib/AmdSev.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@ FillExchangeInfoDataSevEs (
293293
);
294294
ExchangeInfo->ExtTopoAvail = !!ExtTopoEbx.Bits.LogicalProcessors;
295295
}
296+
297+
ExchangeInfo->SevSnpKnownInitApicId = FALSE;
296298
}
297299

298300
/**

UefiCpuPkg/Library/MpInitLib/MpEqu.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ struc MP_CPU_EXCHANGE_INFO
9797
.SevSnpIsEnabled CTYPE_BOOLEAN 1
9898
.GhcbBase: CTYPE_UINTN 1
9999
.ExtTopoAvail: CTYPE_BOOLEAN 1
100+
.SevSnpKnownInitApicId: CTYPE_BOOLEAN 1
100101
endstruc
101102

102103
MP_CPU_EXCHANGE_INFO_OFFSET equ (Flat32Start - RendezvousFunnelProcStart)

UefiCpuPkg/Library/MpInitLib/MpLib.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ typedef struct {
239239
BOOLEAN SevSnpIsEnabled;
240240
UINTN GhcbBase;
241241
BOOLEAN ExtTopoAvail;
242+
BOOLEAN SevSnpKnownInitApicId;
242243
} MP_CPU_EXCHANGE_INFO;
243244

244245
#pragma pack()

UefiCpuPkg/Library/MpInitLib/X64/AmdSev.c

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -271,18 +271,27 @@ SevSnpCreateAP (
271271
IN INTN ProcessorNumber
272272
)
273273
{
274-
CPU_INFO_IN_HOB *CpuInfoInHob;
275-
CPU_AP_DATA *CpuData;
276-
UINTN Index;
277-
UINTN MaxIndex;
278-
UINT32 ApicId;
279-
EFI_HOB_GUID_TYPE *GuidHob;
280-
GHCB_APIC_IDS *GhcbApicIds;
274+
CPU_INFO_IN_HOB *CpuInfoInHob;
275+
CPU_AP_DATA *CpuData;
276+
UINTN Index;
277+
UINTN MaxIndex;
278+
UINT32 ApicId;
279+
EFI_HOB_GUID_TYPE *GuidHob;
280+
GHCB_APIC_IDS *GhcbApicIds;
281+
volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo;
281282

282283
ASSERT (CpuMpData->MpCpuExchangeInfo->BufferStart < 0x100000);
283284

285+
ExchangeInfo = CpuMpData->MpCpuExchangeInfo;
284286
CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob;
285287

288+
//
289+
// Set to FALSE by default. This is only set to TRUE when the InitFlag
290+
// is equal to ApInitConfig and the GHCB APIC ID List NAE event has
291+
// been called.
292+
//
293+
ExchangeInfo->SevSnpKnownInitApicId = FALSE;
294+
286295
if (ProcessorNumber < 0) {
287296
if (CpuMpData->InitFlag == ApInitConfig) {
288297
//
@@ -294,6 +303,14 @@ SevSnpCreateAP (
294303
GuidHob = GetFirstGuidHob (&gGhcbApicIdsGuid);
295304
GhcbApicIds = (GHCB_APIC_IDS *)(*(UINTN *)GET_GUID_HOB_DATA (GuidHob));
296305
MaxIndex = MIN (GhcbApicIds->NumEntries, PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
306+
307+
//
308+
// Set to TRUE so that ApicId and SEV-SNP SaveArea stay in sync. When
309+
// the InitFlag is ApInitConfig, the random order of AP initialization
310+
// can end up with an Index / ApicId mismatch (see X64/AmdSev.nasm).
311+
//
312+
ExchangeInfo->SevSnpKnownInitApicId = TRUE;
313+
DEBUG ((DEBUG_INFO, "SEV-SNP: Using known initial APIC IDs\n"));
297314
} else {
298315
//
299316
// APs have been previously started.
@@ -308,6 +325,13 @@ SevSnpCreateAP (
308325
if (CpuMpData->InitFlag == ApInitConfig) {
309326
ApicId = GhcbApicIds->ApicIds[Index];
310327

328+
//
329+
// Set the ApicId values so that the proper AP data structure
330+
// can be found during boot.
331+
//
332+
CpuInfoInHob[Index].InitialApicId = ApicId;
333+
CpuInfoInHob[Index].ApicId = ApicId;
334+
311335
//
312336
// For the first boot, use the BSP register information.
313337
//

UefiCpuPkg/Library/MpInitLib/X64/AmdSev.nasm

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,57 @@
1515

1616
%define SIZE_4KB 0x1000
1717

18+
;
19+
; This function will ensure that CpuNumber and ApicId are in sync when using
20+
; the ApicIds retrieved via the GHCB APIC ID List NAE event to start the APs
21+
; when the InitFlag is ApInitConfig. If this is not done, the CpuNumber to
22+
; ApicId relationship may not hold, which would result in the ApicId to VSMA
23+
; relationship getting out of sync after the first AP boot.
24+
;
25+
SevSnpGetInitCpuNumber:
26+
;
27+
; If not an SNP guest, leave EBX (CpuNumber) as is
28+
;
29+
lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevSnpIsEnabled)]
30+
cmp byte [edi], 1 ; SevSnpIsEnabled
31+
jne SevSnpGetCpuNumberDone
32+
33+
;
34+
; If not starting the AP with a specific ApicId, leave EBX (CpuNumber) as is
35+
;
36+
lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevSnpKnownInitApicId)]
37+
cmp byte [edi], 1 ; SevSnpKnownInitApicId
38+
jne SevSnpGetCpuNumberDone
39+
40+
;
41+
; Use existing code to retrieve the ApicId. SevEsGetApicId will return to
42+
; the SevSnpGetInitApicId label if SevSnpKnownInitApicId is set.
43+
;
44+
jmp SevEsGetApicId
45+
46+
SevSnpGetInitApicId:
47+
;
48+
; EDX holds the ApicId, get processor number for this AP
49+
;
50+
xor ebx, ebx
51+
lea eax, [esi + MP_CPU_EXCHANGE_INFO_FIELD (CpuInfo)]
52+
mov rdi, [eax]
53+
54+
SevSnpGetNextProcNumber:
55+
cmp dword [rdi + CPU_INFO_IN_HOB.InitialApicId], edx ; APIC ID match?
56+
jz SevSnpGetCpuNumberDone
57+
add rdi, CPU_INFO_IN_HOB_size
58+
inc ebx
59+
jmp SevSnpGetNextProcNumber
60+
61+
SevSnpGetCpuNumberDone:
62+
;
63+
; If SevSnpKnownInitApicId is set, EBX now holds the CpuNumber for this
64+
; ApicId, which matches how it was started in SevSnpCreateAP(). Otherwise,
65+
; EBX is unchanged and holds the CpuNumber based on the startup order.
66+
;
67+
OneTimeCallRet SevSnpGetInitCpuNumber
68+
1869
RegisterGhcbGpa:
1970
;
2071
; Register GHCB GPA when SEV-SNP is enabled
@@ -193,7 +244,14 @@ RestoreGhcb:
193244

194245
mov rdx, rbx
195246

196-
; x2APIC ID or APIC ID is in EDX
247+
;
248+
; x2APIC ID or APIC ID is in EDX. If SevSnpKnownInitApicId is set, then
249+
; return to SevSnpGetInitApicId, otherwise return to GetProcessorNumber.
250+
;
251+
lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevSnpKnownInitApicId)]
252+
cmp byte [edi], 1 ; SevSnpKnownInitApicId
253+
je SevSnpGetInitApicId
254+
197255
jmp GetProcessorNumber
198256

199257
SevEsGetApicIdExit:

UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ LongModeStart:
173173
lock xadd dword [edi], ebx ; EBX = ApIndex++
174174
inc ebx ; EBX is CpuNumber
175175

176+
; If running under AMD SEV-SNP and starting with a known ApicId,
177+
; adjust EBX to be the actual CpuNumber
178+
OneTimeCall SevSnpGetInitCpuNumber
179+
176180
; program stack
177181
mov edi, esi
178182
add edi, MP_CPU_EXCHANGE_INFO_FIELD (StackSize)

0 commit comments

Comments
 (0)