]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/CpuHotplugSmm/CpuHotplug.c
OvmfPkg/CpuHotplugSmm: do actual CPU hot-eject
[mirror_edk2.git] / OvmfPkg / CpuHotplugSmm / CpuHotplug.c
index 2eeb4567a26258e8e84d82b9abeca1c56c383c28..2c768f89f1ee0121a53125fc7557d1ea8b71eec5 100644 (file)
@@ -18,6 +18,7 @@
 #include <Pcd/CpuHotEjectData.h>             // CPU_HOT_EJECT_DATA\r
 #include <Protocol/MmCpuIo.h>                // EFI_MM_CPU_IO_PROTOCOL\r
 #include <Protocol/SmmCpuService.h>          // EFI_SMM_CPU_SERVICE_PROTOCOL\r
+#include <Register/Intel/ArchitecturalMsr.h> // MSR_IA32_APIC_BASE_REGISTER\r
 #include <Uefi/UefiBaseType.h>               // EFI_STATUS\r
 \r
 #include "ApicId.h"                          // APIC_ID\r
@@ -192,13 +193,41 @@ RevokeNewSlot:
   return Status;\r
 }\r
 \r
+/**\r
+  EjectCpu needs to know the BSP at SMI exit at a point when\r
+  some of the EFI_SMM_CPU_SERVICE_PROTOCOL state has been torn\r
+  down.\r
+  Reuse the logic from OvmfPkg::PlatformSmmBspElection() to\r
+  do that.\r
+\r
+  @retval TRUE   If the CPU executing this function is the BSP.\r
+\r
+  @retval FALSE  If the CPU executing this function is an AP.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+CheckIfBsp (\r
+  VOID\r
+  )\r
+{\r
+  MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr;\r
+  BOOLEAN                     IsBsp;\r
+\r
+  ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);\r
+  IsBsp = (BOOLEAN)(ApicBaseMsr.Bits.BSP == 1);\r
+  return IsBsp;\r
+}\r
+\r
 /**\r
   CPU Hot-eject handler, called from SmmCpuFeaturesRendezvousExit()\r
   on each CPU at exit from SMM.\r
 \r
-  If, the executing CPU is not being ejected, nothing to be done.\r
+  If, the executing CPU is neither the BSP, nor being ejected, nothing\r
+  to be done.\r
   If, the executing CPU is being ejected, wait in a halted loop\r
   until ejected.\r
+  If, the executing CPU is the BSP, set QEMU CPU status to eject\r
+  for CPUs being ejected.\r
 \r
   @param[in] ProcessorNum      ProcessorNum denotes the CPU exiting SMM,\r
                                and will be used as an index into\r
@@ -214,6 +243,81 @@ EjectCpu (
 {\r
   UINT64 QemuSelector;\r
 \r
+  if (CheckIfBsp ()) {\r
+    UINT32 Idx;\r
+\r
+    for (Idx = 0; Idx < mCpuHotEjectData->ArrayLength; Idx++) {\r
+      QemuSelector = mCpuHotEjectData->QemuSelectorMap[Idx];\r
+\r
+      if (QemuSelector != CPU_EJECT_QEMU_SELECTOR_INVALID) {\r
+        //\r
+        // This to-be-ejected-CPU has already received the BSP's SMI exit\r
+        // signal and will execute SmmCpuFeaturesRendezvousExit()\r
+        // followed by this callback or is already penned in the\r
+        // CpuSleep() loop below.\r
+        //\r
+        // Tell QEMU to context-switch it out.\r
+        //\r
+        QemuCpuhpWriteCpuSelector (mMmCpuIo, (UINT32) QemuSelector);\r
+        QemuCpuhpWriteCpuStatus (mMmCpuIo, QEMU_CPUHP_STAT_EJECT);\r
+\r
+        //\r
+        // Now that we've ejected the CPU corresponding to QemuSelectorMap[Idx],\r
+        // clear its eject status to ensure that an invalid future SMI does\r
+        // not end up trying a spurious eject or a newly hotplugged CPU does\r
+        // not get penned in the CpuSleep() loop.\r
+        //\r
+        // Note that the QemuCpuhpWriteCpuStatus() command above is a write to\r
+        // a different address space and uses the EFI_MM_CPU_IO_PROTOCOL.\r
+        //\r
+        // This means that we are guaranteed that the following assignment\r
+        // will not be reordered before the eject. And, so we can safely\r
+        // do this write here.\r
+        //\r
+        mCpuHotEjectData->QemuSelectorMap[Idx] =\r
+          CPU_EJECT_QEMU_SELECTOR_INVALID;\r
+\r
+        DEBUG ((DEBUG_INFO, "%a: Unplugged ProcessorNum %u, "\r
+          "QemuSelector %Lu\n", __FUNCTION__, Idx, QemuSelector));\r
+      }\r
+    }\r
+\r
+    //\r
+    // We are done until the next hot-unplug; clear the handler.\r
+    //\r
+    // mCpuHotEjectData->Handler is a NOP for any CPU not under ejection.\r
+    // So, once we are done with all the ejections, we can safely reset it\r
+    // here since any CPU dereferencing it would only see either the old\r
+    // or the new value (since it is aligned at a natural boundary.)\r
+    //\r
+    mCpuHotEjectData->Handler = NULL;\r
+    return;\r
+  }\r
+\r
+  //\r
+  // Reached only on APs\r
+  //\r
+\r
+  //\r
+  // mCpuHotEjectData->QemuSelectorMap[ProcessorNum] is updated\r
+  // on the BSP in the ongoing SMI at two places:\r
+  //\r
+  // - UnplugCpus() where the BSP determines if a CPU is under ejection\r
+  //   or not. As a comment in UnplugCpus() at set-up, and in\r
+  //   SmmCpuFeaturesRendezvousExit() where it is dereferenced describe,\r
+  //   any such updates are guaranteed to be ordered-before the\r
+  //   dereference below.\r
+  //\r
+  // - EjectCpu() on the BSP (above) updates QemuSelectorMap[ProcessorNum]\r
+  //   for a CPU once it's ejected.\r
+  //\r
+  //   The CPU under ejection: might be executing anywhere between the\r
+  //   AllCpusInSync loop in SmiRendezvous(), to about to dereference\r
+  //   QemuSelectorMap[ProcessorNum].\r
+  //   As described in the comment above where we do the reset, this\r
+  //   is not a problem since the ejected CPU never sees the after value.\r
+  //   CPUs not-under ejection: never see any changes so they are fine.\r
+  //\r
   QemuSelector = mCpuHotEjectData->QemuSelectorMap[ProcessorNum];\r
   if (QemuSelector == CPU_EJECT_QEMU_SELECTOR_INVALID) {\r
     return;\r
@@ -495,11 +599,6 @@ CpuHotplugMmi (
   if (EFI_ERROR (Status)) {\r
     goto Fatal;\r
   }\r
-  if (ToUnplugCount > 0) {\r
-    DEBUG ((DEBUG_ERROR, "%a: hot-unplug is not supported yet\n",\r
-      __FUNCTION__));\r
-    goto Fatal;\r
-  }\r
 \r
   if (PluggedCount > 0) {\r
     Status = ProcessHotAddedCpus (mPluggedApicIds, PluggedCount);\r