\r
#include <Library/PlatformInitLib.h>\r
\r
+#define CPUHP_BUGCHECK_OVERRIDE_FWCFG_FILE \\r
+ "opt/org.tianocore/X-Cpuhp-Bugcheck-Override"\r
+\r
VOID\r
EFIAPI\r
PlatformAddIoMemoryBaseSizeHob (\r
{\r
ASSERT (*BootCpuCount > 0);\r
\r
+ //\r
+ // Sanity check: we need at least 1 present CPU (CPU#0 is always present).\r
+ //\r
+ // The legacy-to-modern switching of the CPU hotplug register block got broken\r
+ // (for TCG) in QEMU v5.1.0. Refer to "IO port write width clamping differs\r
+ // between TCG and KVM" at\r
+ // <http://mid.mail-archive.com/aaedee84-d3ed-a4f9-21e7-d221a28d1683@redhat.com>\r
+ // or at\r
+ // <https://lists.gnu.org/archive/html/qemu-devel/2023-01/msg00199.html>.\r
+ //\r
+ // QEMU received the fix in commit dab30fbef389 ("acpi: cpuhp: fix\r
+ // guest-visible maximum access size to the legacy reg block", 2023-01-08), to\r
+ // be included in QEMU v8.0.0.\r
+ //\r
+ // If we're affected by this QEMU bug, then we must not continue: it confuses\r
+ // the multiprocessing in UefiCpuPkg/Library/MpInitLib, and breaks CPU\r
+ // hot(un)plug with SMI in OvmfPkg/CpuHotplugSmm.\r
+ //\r
+ if (*Present == 0) {\r
+ UINTN Idx;\r
+ STATIC CONST CHAR8 *CONST Message[] = {\r
+ "Broken CPU hotplug register block found. Update QEMU to version 8+, or",\r
+ "to a stable release with commit dab30fbef389 backported. Refer to",\r
+ "<https://bugzilla.tianocore.org/show_bug.cgi?id=4250>.",\r
+ "Consequences of the QEMU bug may include, but are not limited to:",\r
+ "- all firmware logic, dependent on the CPU hotplug register block,",\r
+ " being confused, for example, multiprocessing-related logic;",\r
+ "- guest OS data loss, including filesystem corruption, due to crash or",\r
+ " hang during ACPI S3 resume;",\r
+ "- SMM privilege escalation, by a malicious guest OS or 3rd partty UEFI",\r
+ " agent, against the platform firmware.",\r
+ "These symptoms need not necessarily be limited to the QEMU user",\r
+ "attempting to hot(un)plug a CPU.",\r
+ "The firmware will now stop (hang) deliberately, in order to prevent the",\r
+ "above symptoms.",\r
+ "You can forcibly override the hang, *at your own risk*, with the",\r
+ "following *experimental* QEMU command line option:",\r
+ " -fw_cfg name=" CPUHP_BUGCHECK_OVERRIDE_FWCFG_FILE ",string=yes",\r
+ "Please only report such bugs that you can reproduce *without* the",\r
+ "override.",\r
+ };\r
+ RETURN_STATUS ParseStatus;\r
+ BOOLEAN Override;\r
+\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "%a: Present=%u Possible=%u\n",\r
+ __FUNCTION__,\r
+ *Present,\r
+ *Possible\r
+ ));\r
+ for (Idx = 0; Idx < ARRAY_SIZE (Message); ++Idx) {\r
+ DEBUG ((DEBUG_ERROR, "%a: %a\n", __FUNCTION__, Message[Idx]));\r
+ }\r
+\r
+ ParseStatus = QemuFwCfgParseBool (\r
+ CPUHP_BUGCHECK_OVERRIDE_FWCFG_FILE,\r
+ &Override\r
+ );\r
+ if (!RETURN_ERROR (ParseStatus) && Override) {\r
+ DEBUG ((\r
+ DEBUG_WARN,\r
+ "%a: \"%a\" active. You've been warned.\n",\r
+ __FUNCTION__,\r
+ CPUHP_BUGCHECK_OVERRIDE_FWCFG_FILE\r
+ ));\r
+ //\r
+ // The bug is in QEMU v5.1.0+, where we're not affected by the QEMU v2.7\r
+ // reset bug, so BootCpuCount from fw_cfg is reliable. Assume a fully\r
+ // populated topology, like when the modern CPU hotplug interface is\r
+ // unavailable.\r
+ //\r
+ *Present = *BootCpuCount;\r
+ *Possible = *BootCpuCount;\r
+ return;\r
+ }\r
+\r
+ ASSERT (FALSE);\r
+ CpuDeadLoop ();\r
+ }\r
+\r
//\r
// Sanity check: fw_cfg and the modern CPU hotplug interface should expose the\r
// same boot CPU count.\r
} while (Selected > 0);\r
\r
PlatformCpuCountBugCheck (&BootCpuCount, &Present, &Possible);\r
+ ASSERT (Present > 0);\r
+ ASSERT (Present <= Possible);\r
+ ASSERT (BootCpuCount == Present);\r
\r
MaxCpuCount = Possible;\r
}\r