]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Library/PlatformInitLib/Platform.c
OvmfPkg/PlatformInitLib: catch QEMU's CPU hotplug reg block regression
[mirror_edk2.git] / OvmfPkg / Library / PlatformInitLib / Platform.c
index d1be5c2d79705ee51f28aea052fdbdca1f106230..9fee6e481038647d0140a66eb331c568a1f63736 100644 (file)
@@ -36,6 +36,9 @@
 \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
@@ -437,6 +440,87 @@ PlatformCpuCountBugCheck (
 {\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
@@ -596,6 +680,9 @@ PlatformMaxCpuCountInitialization (
       } 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