/** @file\r
- Simple wrapper functions that access QEMU's modern CPU hotplug register\r
- block.\r
+ Simple wrapper functions and utility functions that access QEMU's modern CPU\r
+ hotplug register block.\r
\r
- These functions thinly wrap some of the registers described in\r
+ These functions manipulate some of the registers described in\r
"docs/specs/acpi_cpu_hotplug.txt" in the QEMU source. IO Ports are accessed\r
via EFI_MM_CPU_IO_PROTOCOL. If a protocol call fails, these functions don't\r
return.\r
CpuDeadLoop ();\r
}\r
}\r
+\r
+/**\r
+ Collect the APIC IDs of\r
+ - the CPUs that have been hot-plugged,\r
+ - the CPUs that are about to be hot-unplugged.\r
+\r
+ This function only scans for events -- it does not modify them -- in the\r
+ hotplug registers.\r
+\r
+ On error, the contents of the output parameters are undefined.\r
+\r
+ @param[in] MmCpuIo The EFI_MM_CPU_IO_PROTOCOL instance for\r
+ accessing IO Ports.\r
+\r
+ @param[in] PossibleCpuCount The number of possible CPUs in the system. Must\r
+ be positive.\r
+\r
+ @param[in] ApicIdCount The number of elements each one of the\r
+ PluggedApicIds and ToUnplugApicIds arrays can\r
+ accommodate. Must be positive.\r
+\r
+ @param[out] PluggedApicIds The APIC IDs of the CPUs that have been\r
+ hot-plugged.\r
+\r
+ @param[out] PluggedCount The number of filled-in APIC IDs in\r
+ PluggedApicIds.\r
+\r
+ @param[out] ToUnplugApicIds The APIC IDs of the CPUs that are about to be\r
+ hot-unplugged.\r
+\r
+ @param[out] ToUnplugCount The number of filled-in APIC IDs in\r
+ ToUnplugApicIds.\r
+\r
+ @retval EFI_INVALID_PARAMETER PossibleCpuCount is zero, or ApicIdCount is\r
+ zero.\r
+\r
+ @retval EFI_PROTOCOL_ERROR Invalid bitmap detected in the\r
+ QEMU_CPUHP_R_CPU_STAT register.\r
+\r
+ @retval EFI_BUFFER_TOO_SMALL There was an attempt to place more than\r
+ ApicIdCount APIC IDs into one of the\r
+ PluggedApicIds and ToUnplugApicIds arrays.\r
+\r
+ @retval EFI_SUCCESS Output parameters have been set successfully.\r
+**/\r
+EFI_STATUS\r
+QemuCpuhpCollectApicIds (\r
+ IN CONST EFI_MM_CPU_IO_PROTOCOL *MmCpuIo,\r
+ IN UINT32 PossibleCpuCount,\r
+ IN UINT32 ApicIdCount,\r
+ OUT APIC_ID *PluggedApicIds,\r
+ OUT UINT32 *PluggedCount,\r
+ OUT APIC_ID *ToUnplugApicIds,\r
+ OUT UINT32 *ToUnplugCount\r
+ )\r
+{\r
+ UINT32 CurrentSelector;\r
+\r
+ if (PossibleCpuCount == 0 || ApicIdCount == 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ *PluggedCount = 0;\r
+ *ToUnplugCount = 0;\r
+\r
+ CurrentSelector = 0;\r
+ do {\r
+ UINT32 PendingSelector;\r
+ UINT8 CpuStatus;\r
+ APIC_ID *ExtendIds;\r
+ UINT32 *ExtendCount;\r
+ APIC_ID NewApicId;\r
+\r
+ //\r
+ // Write CurrentSelector (which is valid) to the CPU selector register.\r
+ // Consequences:\r
+ //\r
+ // - Other register accesses will be permitted.\r
+ //\r
+ // - The QEMU_CPUHP_CMD_GET_PENDING command will start scanning for a CPU\r
+ // with pending events at CurrentSelector (inclusive).\r
+ //\r
+ QemuCpuhpWriteCpuSelector (MmCpuIo, CurrentSelector);\r
+ //\r
+ // Write the QEMU_CPUHP_CMD_GET_PENDING command. Consequences\r
+ // (independently of each other):\r
+ //\r
+ // - If there is a CPU with pending events, starting at CurrentSelector\r
+ // (inclusive), the CPU selector will be updated to that CPU. Note that\r
+ // the scanning in QEMU may wrap around, because we must never clear the\r
+ // event bits.\r
+ //\r
+ // - The QEMU_CPUHP_RW_CMD_DATA register will return the (possibly updated)\r
+ // CPU selector value.\r
+ //\r
+ QemuCpuhpWriteCommand (MmCpuIo, QEMU_CPUHP_CMD_GET_PENDING);\r
+ PendingSelector = QemuCpuhpReadCommandData (MmCpuIo);\r
+ if (PendingSelector < CurrentSelector) {\r
+ DEBUG ((DEBUG_VERBOSE, "%a: CurrentSelector=%u PendingSelector=%u: "\r
+ "wrap-around\n", __FUNCTION__, CurrentSelector, PendingSelector));\r
+ break;\r
+ }\r
+ CurrentSelector = PendingSelector;\r
+\r
+ //\r
+ // Check the known status / event bits for the currently selected CPU.\r
+ //\r
+ CpuStatus = QemuCpuhpReadCpuStatus (MmCpuIo);\r
+ if ((CpuStatus & QEMU_CPUHP_STAT_INSERT) != 0) {\r
+ //\r
+ // The "insert" event guarantees the "enabled" status; plus it excludes\r
+ // the "remove" event.\r
+ //\r
+ if ((CpuStatus & QEMU_CPUHP_STAT_ENABLED) == 0 ||\r
+ (CpuStatus & QEMU_CPUHP_STAT_REMOVE) != 0) {\r
+ DEBUG ((DEBUG_ERROR, "%a: CurrentSelector=%u CpuStatus=0x%x: "\r
+ "inconsistent CPU status\n", __FUNCTION__, CurrentSelector,\r
+ CpuStatus));\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "%a: CurrentSelector=%u: insert\n", __FUNCTION__,\r
+ CurrentSelector));\r
+\r
+ ExtendIds = PluggedApicIds;\r
+ ExtendCount = PluggedCount;\r
+ } else if ((CpuStatus & QEMU_CPUHP_STAT_REMOVE) != 0) {\r
+ DEBUG ((DEBUG_VERBOSE, "%a: CurrentSelector=%u: remove\n", __FUNCTION__,\r
+ CurrentSelector));\r
+\r
+ ExtendIds = ToUnplugApicIds;\r
+ ExtendCount = ToUnplugCount;\r
+ } else {\r
+ DEBUG ((DEBUG_VERBOSE, "%a: CurrentSelector=%u: no event\n",\r
+ __FUNCTION__, CurrentSelector));\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Save the APIC ID of the CPU with the pending event, to the corresponding\r
+ // APIC ID array.\r
+ //\r
+ if (*ExtendCount == ApicIdCount) {\r
+ DEBUG ((DEBUG_ERROR, "%a: APIC ID array too small\n", __FUNCTION__));\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+ QemuCpuhpWriteCommand (MmCpuIo, QEMU_CPUHP_CMD_GET_ARCH_ID);\r
+ NewApicId = QemuCpuhpReadCommandData (MmCpuIo);\r
+ DEBUG ((DEBUG_VERBOSE, "%a: ApicId=" FMT_APIC_ID "\n", __FUNCTION__,\r
+ NewApicId));\r
+ ExtendIds[(*ExtendCount)++] = NewApicId;\r
+\r
+ //\r
+ // We've processed the CPU with (known) pending events, but we must never\r
+ // clear events. Therefore we need to advance past this CPU manually;\r
+ // otherwise, QEMU_CPUHP_CMD_GET_PENDING would stick to the currently\r
+ // selected CPU.\r
+ //\r
+ CurrentSelector++;\r
+ } while (CurrentSelector < PossibleCpuCount);\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "%a: PluggedCount=%u ToUnplugCount=%u\n",\r
+ __FUNCTION__, *PluggedCount, *ToUnplugCount));\r
+ return EFI_SUCCESS;\r
+}\r
/** @file\r
- Simple wrapper functions that access QEMU's modern CPU hotplug register\r
- block.\r
+ Simple wrapper functions and utility functions that access QEMU's modern CPU\r
+ hotplug register block.\r
\r
- These functions thinly wrap some of the registers described in\r
+ These functions manipulate some of the registers described in\r
"docs/specs/acpi_cpu_hotplug.txt" in the QEMU source. IO Ports are accessed\r
via EFI_MM_CPU_IO_PROTOCOL. If a protocol call fails, these functions don't\r
return.\r
#define QEMU_CPUHP_H_\r
\r
#include <Protocol/MmCpuIo.h> // EFI_MM_CPU_IO_PROTOCOL\r
+#include <Uefi/UefiBaseType.h> // EFI_STATUS\r
+\r
+#include "ApicId.h" // APIC_ID\r
\r
UINT32\r
QemuCpuhpReadCommandData2 (\r
IN UINT8 Command\r
);\r
\r
+EFI_STATUS\r
+QemuCpuhpCollectApicIds (\r
+ IN CONST EFI_MM_CPU_IO_PROTOCOL *MmCpuIo,\r
+ IN UINT32 PossibleCpuCount,\r
+ IN UINT32 ApicIdCount,\r
+ OUT APIC_ID *PluggedApicIds,\r
+ OUT UINT32 *PluggedCount,\r
+ OUT APIC_ID *ToUnplugApicIds,\r
+ OUT UINT32 *ToUnplugCount\r
+ );\r
+\r
#endif // QEMU_CPUHP_H_\r