#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
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
{\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
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