From: "Denis V. Lunev" <den@openvz.org>
To: svt-core@virtuozzo.com
Cc: andrey.drobyshev@virtuozzo.com, "Denis V. Lunev" <den@openvz.org>
Subject: [PATCH 2/2] UefiCpuPkg/MpInitLib: resend the Start-up IPI when an AP misses it #VSTOR-146639
Date: Mon, 21 Sep 2026 17:44:06 +0200 [thread overview]
Message-ID: <20260921154406.1628753-3-den@openvz.org> (raw)
In-Reply-To: <20260921154406.1628753-1-den@openvz.org>
From: Denis V. Lunev <den@openvz.org>
WaitApWakeup spins until the AP clears its wakeup signal, with no
timeout and no way out. If the AP never consumes the Start-up IPI, the
BSP spins forever and the boot is dead.
A hypervisor can lose that IPI. Under KVM, INIT is latched while a
vCPU is in SMM but the Start-up IPI is discarded outright, so an AP
that takes an SMI just before the BSP broadcasts INIT-SIPI-SIPI is
left in wait-for-SIPI with nothing left to start it. The window is the
few instructions between an AP releasing the BSP at the end of the SMM
rendezvous and its own RSM, which a scheduled vCPU can be preempted
inside of. KVM holds that delivering the SIPI is software's job, so
the firmware has to cope.
Resend rather than spin. Address the AP individually so that APs
already running the procedure are not disturbed, and pass the reset
vector only on the paths that sent a Start-up IPI in the first place,
so the SEV-SNP and hand-off paths keep waiting as before. A Start-up
IPI is ignored unless the target is in wait-for-SIPI, so resending
costs nothing when the AP is merely slow.
The failure this fixes looks like a guest that never boots: firmware
starts the boot manager, paints nothing further, and one CPU spins
while the other APs sit idle.
Signed-off-by: Denis V. Lunev <den@openvz.org>
Cc: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
UefiCpuPkg/Library/MpInitLib/MpLib.c | 85 +++++++++++++++++++++++++---
UefiCpuPkg/Library/MpInitLib/MpLib.h | 2 +
2 files changed, 80 insertions(+), 7 deletions(-)
diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c
index 426d998b9a..87e8f824b6 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.c
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c
@@ -942,16 +942,63 @@ DxeApEntryPoint (
ApWakeupFunction (CpuMpData, ProcessorNumber);
}
+/**
+ Calculate timeout value and return the current performance counter value.
+
+ @param[in] TimeoutInMicroseconds The timeout value in microseconds.
+ @param[out] CurrentTime Returns the current value of the performance counter.
+
+ @return Expected timestamp counter for timeout, or 0 if TimeoutInMicroseconds is 0.
+**/
+UINT64
+CalculateTimeout (
+ IN UINTN TimeoutInMicroseconds,
+ OUT UINT64 *CurrentTime
+ );
+
+/**
+ Checks whether timeout expires.
+
+ @param[in, out] PreviousTime On input, the requested time. On output, the elapsed time.
+ @param[in] TotalTime The total amount of elapsed time.
+ @param[in] Timeout The timeout ticks, 0 means infinity.
+
+ @retval TRUE A timeout occurred.
+ @retval FALSE No timeout occurred.
+**/
+BOOLEAN
+CheckTimeout (
+ IN OUT UINT64 *PreviousTime,
+ IN UINT64 *TotalTime,
+ IN UINT64 Timeout
+ );
+
/**
Wait for AP wakeup and write AP start-up signal till AP is waken up.
@param[in] ApStartupSignalBuffer Pointer to AP wakeup signal
+ @param[in] ApicId Local APIC ID of the AP being woken
+ @param[in] StartupRoutine AP reset vector, or 0 if the AP was not
+ woken by INIT-SIPI-SIPI and no Start-up
+ IPI may be resent
**/
VOID
WaitApWakeup (
- IN volatile UINT32 *ApStartupSignalBuffer
+ IN volatile UINT32 *ApStartupSignalBuffer,
+ IN UINT32 ApicId,
+ IN UINT32 StartupRoutine
)
{
+ UINT64 CurrentTime;
+ UINT64 TotalTime;
+ UINT64 Timeout;
+
+ Timeout = CalculateTimeout (
+ (StartupRoutine != 0) ? AP_SIPI_RESEND_TIMEOUT_US : 0,
+ &CurrentTime
+ );
+ TotalTime = 0;
+
//
// If AP is waken up, StartupApSignal should be cleared.
// Otherwise, write StartupApSignal again till AP waken up.
@@ -962,6 +1009,12 @@ WaitApWakeup (
WAKEUP_AP_SIGNAL
) != 0)
{
+ if (CheckTimeout (&CurrentTime, &TotalTime, Timeout)) {
+ SendStartupIpi (ApicId, StartupRoutine);
+ Timeout = CalculateTimeout (AP_SIPI_RESEND_TIMEOUT_US, &CurrentTime);
+ TotalTime = 0;
+ }
+
CpuPause ();
}
}
@@ -1256,8 +1309,10 @@ WakeUpAP (
CPU_AP_DATA *CpuData;
BOOLEAN ResetVectorRequired;
CPU_INFO_IN_HOB *CpuInfoInHob;
+ UINT32 SipiVector;
CpuMpData->FinishedCount = 0;
+ SipiVector = 0;
ResetVectorRequired = FALSE;
if (CpuMpData->WakeUpByInitSipiSipi ||
@@ -1320,13 +1375,14 @@ WakeUpAP (
if (CanUseSevSnpCreateAP (CpuMpData)) {
SevSnpCreateAP (CpuMpData, -1);
} else {
+ SipiVector = (UINT32)ExchangeInfo->BufferStart;
if ((CpuMpData->InitFlag == ApInitConfig) && FixedPcdGetBool (PcdFirstTimeWakeUpAPsBySipi)) {
//
// SIPI can be used for the first time wake up after reset to reduce boot time.
//
- SendStartupIpiAllExcludingSelf ((UINT32)ExchangeInfo->BufferStart);
+ SendStartupIpiAllExcludingSelf (SipiVector);
} else {
- SendInitSipiSipiAllExcludingSelf ((UINT32)ExchangeInfo->BufferStart);
+ SendInitSipiSipiAllExcludingSelf (SipiVector);
}
}
}
@@ -1398,10 +1454,15 @@ WakeUpAP (
//
// Wait all APs waken up if this is not the 1st broadcast of SIPI
//
+ CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob;
for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
CpuData = &CpuMpData->CpuData[Index];
if (Index != CpuMpData->BspNumber) {
- WaitApWakeup (CpuData->StartupApSignal);
+ WaitApWakeup (
+ CpuData->StartupApSignal,
+ CpuInfoInHob[Index].ApicId,
+ SipiVector
+ );
}
}
}
@@ -1430,9 +1491,10 @@ WakeUpAP (
if (CanUseSevSnpCreateAP (CpuMpData)) {
SevSnpCreateAP (CpuMpData, (INTN)ProcessorNumber);
} else {
+ SipiVector = (UINT32)ExchangeInfo->BufferStart;
SendInitSipiSipi (
CpuInfoInHob[ProcessorNumber].ApicId,
- (UINT32)ExchangeInfo->BufferStart
+ SipiVector
);
}
}
@@ -1440,7 +1502,12 @@ WakeUpAP (
//
// Wait specified AP waken up
//
- WaitApWakeup (CpuData->StartupApSignal);
+ CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob;
+ WaitApWakeup (
+ CpuData->StartupApSignal,
+ CpuInfoInHob[ProcessorNumber].ApicId,
+ SipiVector
+ );
}
if (ResetVectorRequired) {
@@ -2017,7 +2084,11 @@ SwitchApContext (
{
for (Index = 0; Index < MpHandOff->CpuCount; Index++) {
if (MpHandOff->ProcessorIndex + Index != BspNumber) {
- WaitApWakeup ((UINT32 *)(UINTN)(MpHandOff->Info[Index].StartupSignalAddress));
+ WaitApWakeup (
+ (UINT32 *)(UINTN)(MpHandOff->Info[Index].StartupSignalAddress),
+ 0,
+ 0
+ );
}
}
}
diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h b/UefiCpuPkg/Library/MpInitLib/MpLib.h
index 5d10516cd8..94bda7f1ea 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.h
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h
@@ -44,6 +44,8 @@
#include "MpHandOff.h"
#define WAKEUP_AP_SIGNAL SIGNATURE_32 ('S', 'T', 'A', 'P')
+
+#define AP_SIPI_RESEND_TIMEOUT_US 10000
//
// To trigger the start-up signal, BSP writes the specified
// StartupSignalValue to the StartupSignalAddress of each processor.
--
2.53.0
next prev parent reply other threads:[~2026-09-21 15:44 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-21 15:44 [PATCH 0/2] UefiCpuPkg/MpInitLib: recover from a lost Start-up IPI #VSTOR-146639 Denis V. Lunev
2026-09-21 15:44 ` [PATCH 1/2] UefiCpuPkg: add SendStartupIpi to LocalApicLib #VSTOR-146639 Denis V. Lunev
2026-09-23 13:30 ` Andrey Drobyshev
2026-09-21 15:44 ` Denis V. Lunev [this message]
2026-09-23 13:30 ` [PATCH 2/2] UefiCpuPkg/MpInitLib: resend the Start-up IPI when an AP misses it #VSTOR-146639 Andrey Drobyshev
2026-09-23 13:30 ` [PATCH 0/2] UefiCpuPkg/MpInitLib: recover from a lost Start-up IPI #VSTOR-146639 Andrey Drobyshev
2026-09-23 13:30 ` Denis V. Lunev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260921154406.1628753-3-den@openvz.org \
--to=den@openvz.org \
--cc=andrey.drobyshev@virtuozzo.com \
--cc=svt-core@virtuozzo.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox