From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by mx.groups.io with SMTP id smtpd.web11.70095.1674204624467921087 for ; Fri, 20 Jan 2023 00:50:25 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=LVXztZQD; spf=pass (domain: redhat.com, ip: 170.10.129.124, mailfrom: lersek@redhat.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1674204623; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=BszWMW6HP+R32bxKcCI2HQaJwq4pXB1hEjTnN1fHm4U=; b=LVXztZQD3USMynrxlTbKQKYNBg0tRomqQ8/RpvUjc7uMPdL+2QEyZ30yCZz7MN+KuJQb0W qDHW8kLeCr92ROtLEU8jNZLsQ/oQbDgllOOSFPDZ30f0XHKppeq59RcVWgRv3FUtqsBfvG emCHELe/1pCh8TrdaUdiynuLJvKzVsc= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-455-UvvsNYhHNb6ZYAoized6Tg-1; Fri, 20 Jan 2023 03:50:12 -0500 X-MC-Unique: UvvsNYhHNb6ZYAoized6Tg-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 31368823DFA; Fri, 20 Jan 2023 08:50:09 +0000 (UTC) Received: from [10.39.192.173] (unknown [10.39.192.173]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 948602166B2B; Fri, 20 Jan 2023 08:50:06 +0000 (UTC) Message-ID: Date: Fri, 20 Jan 2023 09:50:05 +0100 MIME-Version: 1.0 Subject: Re: [PATCH v3 2/2] OvmfPkg/PlatformInitLib: catch QEMU's CPU hotplug reg block regression To: Ard Biesheuvel , Oliver Steffen Cc: devel@edk2.groups.io, Ard Biesheuvel , Brijesh Singh , Erdem Aktas , Gerd Hoffmann , James Bottomley , Jiewen Yao , Jordan Justen , Michael Brown , Min Xu , Sebastien Boeuf , Tom Lendacky References: <20230119110131.91923-1-lersek@redhat.com> <20230119110131.91923-3-lersek@redhat.com> From: "Laszlo Ersek" In-Reply-To: X-Scanned-By: MIMEDefang 3.1 on 10.11.54.6 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit a couple of requests to Oliver below: On 1/19/23 12:27, Ard Biesheuvel wrote: > On Thu, 19 Jan 2023 at 12:01, Laszlo Ersek wrote: >> >> In QEMU v5.1.0, the CPU hotplug register block misbehaves: the negotiation >> protocol is (effectively) broken such that it suggests that switching from >> the legacy interface to the modern interface works, but in reality the >> switch never happens. The symptom has been witnessed when using TCG >> acceleration; KVM seems to mask the issue. The issue persists with the >> following (latest) stable QEMU releases: v5.2.0, v6.2.0, v7.2.0. Currently >> there is no stable release that addresses the problem. >> >> The QEMU bug confuses the Present and Possible counting in function >> PlatformMaxCpuCountInitialization(), in >> "OvmfPkg/Library/PlatformInitLib/Platform.c". OVMF ends up with Present=0 >> Possible=1. This in turn further confuses MpInitLib in UefiCpuPkg (hence >> firmware-time multiprocessing will be broken). Worse, CPU hot(un)plug with >> SMI will be summarily broken in OvmfPkg/CpuHotplugSmm, which (considering >> the privilege level of SMM) is not that great. >> >> Detect the issue in PlatformCpuCountBugCheck(), and print an error message >> and *hang* if the issue is present. >> >> Users willing to take risks can override the hang with the experimental >> QEMU command line option >> >> -fw_cfg name=opt/org.tianocore/X-Cpuhp-Bugcheck-Override,string=yes >> >> (The "-fw_cfg" QEMU option itself is not experimental; its above argument, >> as far it concerns the firmware, is experimental.) >> >> The problem was originally reported by Ard [0]. We analyzed it at [1] and >> [2]. A QEMU patch was sent at [3]; now merged as commit dab30fbef389 >> ("acpi: cpuhp: fix guest-visible maximum access size to the legacy reg >> block", 2023-01-08), to be included in QEMU v8.0.0. >> >> [0] https://bugzilla.tianocore.org/show_bug.cgi?id=4234#c2 >> >> [1] https://bugzilla.tianocore.org/show_bug.cgi?id=4234#c3 >> >> [2] IO port write width clamping differs between TCG and KVM >> http://mid.mail-archive.com/aaedee84-d3ed-a4f9-21e7-d221a28d1683@redhat.com >> https://lists.gnu.org/archive/html/qemu-devel/2023-01/msg00199.html >> >> [3] acpi: cpuhp: fix guest-visible maximum access size to the legacy reg block >> http://mid.mail-archive.com/20230104090138.214862-1-lersek@redhat.com >> https://lists.gnu.org/archive/html/qemu-devel/2023-01/msg00278.html >> >> NOTE: PlatformInitLib is used in the following platform DSCs: >> >> OvmfPkg/AmdSev/AmdSevX64.dsc >> OvmfPkg/CloudHv/CloudHvX64.dsc >> OvmfPkg/IntelTdx/IntelTdxX64.dsc >> OvmfPkg/Microvm/MicrovmX64.dsc >> OvmfPkg/OvmfPkgIa32.dsc >> OvmfPkg/OvmfPkgIa32X64.dsc >> OvmfPkg/OvmfPkgX64.dsc >> >> but I can only test this change with the last three platforms, running on >> QEMU. >> >> Test results: >> >> TCG QEMU OVMF override result >> patched patched >> --- ------- ------- -------- -------------------------------------- >> 0 0 0 0 CPU counts OK (KVM masks the QEMU bug) >> 0 0 1 0 CPU counts OK (KVM masks the QEMU bug) >> 0 1 0 0 CPU counts OK (QEMU fix, but KVM masks >> the QEMU bug anyway) >> 0 1 1 0 CPU counts OK (QEMU fix, but KVM masks >> the QEMU bug anyway) >> 1 0 0 0 boot with broken CPU counts (original >> QEMU bug) >> 1 0 1 0 broken CPU count caught (boot hangs) >> 1 0 1 1 broken CPU count caught, bug check >> overridden, boot continues >> 1 1 0 0 CPU counts OK (QEMU fix) >> 1 1 1 0 CPU counts OK (QEMU fix) >> >> Cc: Ard Biesheuvel >> Cc: Brijesh Singh >> Cc: Erdem Aktas >> Cc: Gerd Hoffmann >> Cc: James Bottomley >> Cc: Jiewen Yao >> Cc: Jordan Justen >> Cc: Michael Brown >> Cc: Min Xu >> Cc: Oliver Steffen >> Cc: Sebastien Boeuf >> Cc: Tom Lendacky >> Bugzilla: https://bugzilla.tianocore.org/show_bug.cgi?id=4250 >> Signed-off-by: Laszlo Ersek > > Thanks a lot for taking the time and investing the effort. I'm quite > happy that we have this 'escape hatch' now, which we could arguably > use temporarily in the VS2019 platform CI until its QEMU binary gets > updated, right? Yes, I have to agree there. Right now, because those QEMU binaries are affected by the regression, and because they use TCG, OVMF already sees Present=0 Possible=1. Due to the interference of Present=0 with the QEMU v2.7 reset bug workaround, we also get BootCpuCount=0. Furthermore, MaxCpuCount gets set to 1, from Possible. Thus, we exit PlatformMaxCpuCountInitialization() with PcdCpuBootLogicalProcessorNumber=0 (from BootCpuCount) and PcdCpuMaxLogicalProcessorNumber=1 (from MaxCpuCount). Then, in the "predictable subset" of consequences of the QEMU regression, we can say that MpInitLib interprets the above PCD values as "uniprocessor system with the boot CPU count not exposed by the platform". This (i.e., *just this*) does not fall outside of MpInitLib's domain (again, note my qualification "predictable subset"). Now, if we apply the patch and also add the -fw_cfg switch to the Windows CI, *and* we also don't add any -smp flags (as far as I can tell, no -smp flag is used now), then the new PCD state will be PcdCpuBootLogicalProcessorNumber=1 (changed from zero) PcdCpuMaxLogicalProcessorNumber=1 (stays the same) As far as I can tell, *right now* this change should have no effect *in MpInitLib*, IOW nothing gets worse or better there. Namely, PcdCpuBootLogicalProcessorNumber is only consumed in WakeUpAP(), and only when InitFlag == ApInitConfig. InitFlag is set like that only in CollectProcessorCount(). However, CollectProcessorCount() is only called if PcdCpuMaxLogicalProcessorNumber is >1 (see MaxLogicalProcessorNumber in MpInitLibInitialize()). Meaning in effect that PcdCpuMaxLogicalProcessorNumber=1 makes PcdCpuBootLogicalProcessorNumber irrelevant, so its change from 0 to 1 is invisible *to MpInitLib*. Oliver: (1) can you please post a patch for the Windows CI so that the following option be passed to QEMU: -fw_cfg name=opt/org.tianocore/X-Cpuhp-Bugcheck-Override,string=yes (This option is harmless when the firmware does not determine the QEMU bug, so it can be passed in advance; it will have no consequence at all.) In the patch, please reference https://bugzilla.tianocore.org/show_bug.cgi?id=4250 (2) Please file a separate TianoCore BZ for *backing out* the change (= for removing the -fw_cfg switch), and assign it to yourself :) Once the Windows CI advances to a fixed QEMU binary, the "escape hatch" should be shut welded down. (3) Please give me a hint when the CI patch (1) has been merged; then I can go ahead and merge this v3 series as well. Thanks! Laszlo > > For the series, > > Reviewed-by: Ard Biesheuvel > > > >> --- >> >> Notes: >> v3: >> >> - reimplement the bug check in the factored out function >> PlatformCpuCountBugCheck() >> >> - add override [Ard, Gerd, Michael] >> >> - assert "0 < Present == BootCpuCount <= Possible" whenever >> PlatformCpuCountBugCheck() returns >> >> - update commit message >> >> - update test matrix in commit message >> >> - (re)test all "OVMF patched = 1" cases (sigh) >> >> v2: >> >> - V1 was at >> . >> >> - Repo: , branch: >> cpuhp-reg-catch-4250-v2 >> >> - Remove KVM as a proposed workaround from the error message, because in >> the QEMU discussion, we had found that the KVM accelerator's behavior >> in QEMU (masking the problem) was not right, and that a fix for that >> had been in progress for quite some time. >> >> - Add the QEMU commit hash to the commit message, the code comment, and >> the error message. >> >> - Pick up Gerd's R-b; add Oliver to the Cc list. >> >> OvmfPkg/Library/PlatformInitLib/Platform.c | 87 ++++++++++++++++++++ >> 1 file changed, 87 insertions(+) >> >> diff --git a/OvmfPkg/Library/PlatformInitLib/Platform.c b/OvmfPkg/Library/PlatformInitLib/Platform.c >> index d1be5c2d7970..9fee6e481038 100644 >> --- a/OvmfPkg/Library/PlatformInitLib/Platform.c >> +++ b/OvmfPkg/Library/PlatformInitLib/Platform.c >> @@ -36,6 +36,9 @@ >> >> #include >> >> +#define CPUHP_BUGCHECK_OVERRIDE_FWCFG_FILE \ >> + "opt/org.tianocore/X-Cpuhp-Bugcheck-Override" >> + >> VOID >> EFIAPI >> PlatformAddIoMemoryBaseSizeHob ( >> @@ -437,6 +440,87 @@ PlatformCpuCountBugCheck ( >> { >> ASSERT (*BootCpuCount > 0); >> >> + // >> + // Sanity check: we need at least 1 present CPU (CPU#0 is always present). >> + // >> + // The legacy-to-modern switching of the CPU hotplug register block got broken >> + // (for TCG) in QEMU v5.1.0. Refer to "IO port write width clamping differs >> + // between TCG and KVM" at >> + // >> + // or at >> + // . >> + // >> + // QEMU received the fix in commit dab30fbef389 ("acpi: cpuhp: fix >> + // guest-visible maximum access size to the legacy reg block", 2023-01-08), to >> + // be included in QEMU v8.0.0. >> + // >> + // If we're affected by this QEMU bug, then we must not continue: it confuses >> + // the multiprocessing in UefiCpuPkg/Library/MpInitLib, and breaks CPU >> + // hot(un)plug with SMI in OvmfPkg/CpuHotplugSmm. >> + // >> + if (*Present == 0) { >> + UINTN Idx; >> + STATIC CONST CHAR8 *CONST Message[] = { >> + "Broken CPU hotplug register block found. Update QEMU to version 8+, or", >> + "to a stable release with commit dab30fbef389 backported. Refer to", >> + ".", >> + "Consequences of the QEMU bug may include, but are not limited to:", >> + "- all firmware logic, dependent on the CPU hotplug register block,", >> + " being confused, for example, multiprocessing-related logic;", >> + "- guest OS data loss, including filesystem corruption, due to crash or", >> + " hang during ACPI S3 resume;", >> + "- SMM privilege escalation, by a malicious guest OS or 3rd partty UEFI", >> + " agent, against the platform firmware.", >> + "These symptoms need not necessarily be limited to the QEMU user", >> + "attempting to hot(un)plug a CPU.", >> + "The firmware will now stop (hang) deliberately, in order to prevent the", >> + "above symptoms.", >> + "You can forcibly override the hang, *at your own risk*, with the", >> + "following *experimental* QEMU command line option:", >> + " -fw_cfg name=" CPUHP_BUGCHECK_OVERRIDE_FWCFG_FILE ",string=yes", >> + "Please only report such bugs that you can reproduce *without* the", >> + "override.", >> + }; >> + RETURN_STATUS ParseStatus; >> + BOOLEAN Override; >> + >> + DEBUG (( >> + DEBUG_ERROR, >> + "%a: Present=%u Possible=%u\n", >> + __FUNCTION__, >> + *Present, >> + *Possible >> + )); >> + for (Idx = 0; Idx < ARRAY_SIZE (Message); ++Idx) { >> + DEBUG ((DEBUG_ERROR, "%a: %a\n", __FUNCTION__, Message[Idx])); >> + } >> + >> + ParseStatus = QemuFwCfgParseBool ( >> + CPUHP_BUGCHECK_OVERRIDE_FWCFG_FILE, >> + &Override >> + ); >> + if (!RETURN_ERROR (ParseStatus) && Override) { >> + DEBUG (( >> + DEBUG_WARN, >> + "%a: \"%a\" active. You've been warned.\n", >> + __FUNCTION__, >> + CPUHP_BUGCHECK_OVERRIDE_FWCFG_FILE >> + )); >> + // >> + // The bug is in QEMU v5.1.0+, where we're not affected by the QEMU v2.7 >> + // reset bug, so BootCpuCount from fw_cfg is reliable. Assume a fully >> + // populated topology, like when the modern CPU hotplug interface is >> + // unavailable. >> + // >> + *Present = *BootCpuCount; >> + *Possible = *BootCpuCount; >> + return; >> + } >> + >> + ASSERT (FALSE); >> + CpuDeadLoop (); >> + } >> + >> // >> // Sanity check: fw_cfg and the modern CPU hotplug interface should expose the >> // same boot CPU count. >> @@ -596,6 +680,9 @@ PlatformMaxCpuCountInitialization ( >> } while (Selected > 0); >> >> PlatformCpuCountBugCheck (&BootCpuCount, &Present, &Possible); >> + ASSERT (Present > 0); >> + ASSERT (Present <= Possible); >> + ASSERT (BootCpuCount == Present); >> >> MaxCpuCount = Possible; >> } >