2 Simple wrapper functions and utility functions that access QEMU's modern CPU
3 hotplug register block.
5 These functions manipulate some of the registers described in
6 "docs/specs/acpi_cpu_hotplug.txt" in the QEMU source. IO Ports are accessed
7 via EFI_MM_CPU_IO_PROTOCOL. If a protocol call fails, these functions don't
10 Copyright (c) 2020, Red Hat, Inc.
12 SPDX-License-Identifier: BSD-2-Clause-Patent
15 #include <IndustryStandard/Q35MchIch9.h> // ICH9_CPU_HOTPLUG_BASE
16 #include <IndustryStandard/QemuCpuHotplug.h> // QEMU_CPUHP_R_CMD_DATA2
17 #include <Library/BaseLib.h> // CpuDeadLoop()
18 #include <Library/DebugLib.h> // DEBUG()
20 #include "QemuCpuhp.h"
23 QemuCpuhpReadCommandData2 (
24 IN CONST EFI_MM_CPU_IO_PROTOCOL
*MmCpuIo
31 Status
= MmCpuIo
->Io
.Read (
34 ICH9_CPU_HOTPLUG_BASE
+ QEMU_CPUHP_R_CMD_DATA2
,
38 if (EFI_ERROR (Status
)) {
39 DEBUG ((DEBUG_ERROR
, "%a: %r\n", __FUNCTION__
, Status
));
47 QemuCpuhpReadCpuStatus (
48 IN CONST EFI_MM_CPU_IO_PROTOCOL
*MmCpuIo
55 Status
= MmCpuIo
->Io
.Read (
58 ICH9_CPU_HOTPLUG_BASE
+ QEMU_CPUHP_R_CPU_STAT
,
62 if (EFI_ERROR (Status
)) {
63 DEBUG ((DEBUG_ERROR
, "%a: %r\n", __FUNCTION__
, Status
));
71 QemuCpuhpReadCommandData (
72 IN CONST EFI_MM_CPU_IO_PROTOCOL
*MmCpuIo
79 Status
= MmCpuIo
->Io
.Read (
82 ICH9_CPU_HOTPLUG_BASE
+ QEMU_CPUHP_RW_CMD_DATA
,
86 if (EFI_ERROR (Status
)) {
87 DEBUG ((DEBUG_ERROR
, "%a: %r\n", __FUNCTION__
, Status
));
95 QemuCpuhpWriteCpuSelector (
96 IN CONST EFI_MM_CPU_IO_PROTOCOL
*MmCpuIo
,
102 Status
= MmCpuIo
->Io
.Write (
105 ICH9_CPU_HOTPLUG_BASE
+ QEMU_CPUHP_W_CPU_SEL
,
109 if (EFI_ERROR (Status
)) {
110 DEBUG ((DEBUG_ERROR
, "%a: %r\n", __FUNCTION__
, Status
));
117 QemuCpuhpWriteCommand (
118 IN CONST EFI_MM_CPU_IO_PROTOCOL
*MmCpuIo
,
124 Status
= MmCpuIo
->Io
.Write (
127 ICH9_CPU_HOTPLUG_BASE
+ QEMU_CPUHP_W_CMD
,
131 if (EFI_ERROR (Status
)) {
132 DEBUG ((DEBUG_ERROR
, "%a: %r\n", __FUNCTION__
, Status
));
139 Collect the APIC IDs of
140 - the CPUs that have been hot-plugged,
141 - the CPUs that are about to be hot-unplugged.
143 This function only scans for events -- it does not modify them -- in the
146 On error, the contents of the output parameters are undefined.
148 @param[in] MmCpuIo The EFI_MM_CPU_IO_PROTOCOL instance for
151 @param[in] PossibleCpuCount The number of possible CPUs in the system. Must
154 @param[in] ApicIdCount The number of elements each one of the
155 PluggedApicIds and ToUnplugApicIds arrays can
156 accommodate. Must be positive.
158 @param[out] PluggedApicIds The APIC IDs of the CPUs that have been
161 @param[out] PluggedCount The number of filled-in APIC IDs in
164 @param[out] ToUnplugApicIds The APIC IDs of the CPUs that are about to be
167 @param[out] ToUnplugCount The number of filled-in APIC IDs in
170 @retval EFI_INVALID_PARAMETER PossibleCpuCount is zero, or ApicIdCount is
173 @retval EFI_PROTOCOL_ERROR Invalid bitmap detected in the
174 QEMU_CPUHP_R_CPU_STAT register.
176 @retval EFI_BUFFER_TOO_SMALL There was an attempt to place more than
177 ApicIdCount APIC IDs into one of the
178 PluggedApicIds and ToUnplugApicIds arrays.
180 @retval EFI_SUCCESS Output parameters have been set successfully.
183 QemuCpuhpCollectApicIds (
184 IN CONST EFI_MM_CPU_IO_PROTOCOL
*MmCpuIo
,
185 IN UINT32 PossibleCpuCount
,
186 IN UINT32 ApicIdCount
,
187 OUT APIC_ID
*PluggedApicIds
,
188 OUT UINT32
*PluggedCount
,
189 OUT APIC_ID
*ToUnplugApicIds
,
190 OUT UINT32
*ToUnplugCount
193 UINT32 CurrentSelector
;
195 if (PossibleCpuCount
== 0 || ApicIdCount
== 0) {
196 return EFI_INVALID_PARAMETER
;
204 UINT32 PendingSelector
;
211 // Write CurrentSelector (which is valid) to the CPU selector register.
214 // - Other register accesses will be permitted.
216 // - The QEMU_CPUHP_CMD_GET_PENDING command will start scanning for a CPU
217 // with pending events at CurrentSelector (inclusive).
219 QemuCpuhpWriteCpuSelector (MmCpuIo
, CurrentSelector
);
221 // Write the QEMU_CPUHP_CMD_GET_PENDING command. Consequences
222 // (independently of each other):
224 // - If there is a CPU with pending events, starting at CurrentSelector
225 // (inclusive), the CPU selector will be updated to that CPU. Note that
226 // the scanning in QEMU may wrap around, because we must never clear the
229 // - The QEMU_CPUHP_RW_CMD_DATA register will return the (possibly updated)
230 // CPU selector value.
232 QemuCpuhpWriteCommand (MmCpuIo
, QEMU_CPUHP_CMD_GET_PENDING
);
233 PendingSelector
= QemuCpuhpReadCommandData (MmCpuIo
);
234 if (PendingSelector
< CurrentSelector
) {
235 DEBUG ((DEBUG_VERBOSE
, "%a: CurrentSelector=%u PendingSelector=%u: "
236 "wrap-around\n", __FUNCTION__
, CurrentSelector
, PendingSelector
));
239 CurrentSelector
= PendingSelector
;
242 // Check the known status / event bits for the currently selected CPU.
244 CpuStatus
= QemuCpuhpReadCpuStatus (MmCpuIo
);
245 if ((CpuStatus
& QEMU_CPUHP_STAT_INSERT
) != 0) {
247 // The "insert" event guarantees the "enabled" status; plus it excludes
248 // the "remove" event.
250 if ((CpuStatus
& QEMU_CPUHP_STAT_ENABLED
) == 0 ||
251 (CpuStatus
& QEMU_CPUHP_STAT_REMOVE
) != 0) {
252 DEBUG ((DEBUG_ERROR
, "%a: CurrentSelector=%u CpuStatus=0x%x: "
253 "inconsistent CPU status\n", __FUNCTION__
, CurrentSelector
,
255 return EFI_PROTOCOL_ERROR
;
258 DEBUG ((DEBUG_VERBOSE
, "%a: CurrentSelector=%u: insert\n", __FUNCTION__
,
261 ExtendIds
= PluggedApicIds
;
262 ExtendCount
= PluggedCount
;
263 } else if ((CpuStatus
& QEMU_CPUHP_STAT_REMOVE
) != 0) {
264 DEBUG ((DEBUG_VERBOSE
, "%a: CurrentSelector=%u: remove\n", __FUNCTION__
,
267 ExtendIds
= ToUnplugApicIds
;
268 ExtendCount
= ToUnplugCount
;
270 DEBUG ((DEBUG_VERBOSE
, "%a: CurrentSelector=%u: no event\n",
271 __FUNCTION__
, CurrentSelector
));
276 // Save the APIC ID of the CPU with the pending event, to the corresponding
279 if (*ExtendCount
== ApicIdCount
) {
280 DEBUG ((DEBUG_ERROR
, "%a: APIC ID array too small\n", __FUNCTION__
));
281 return EFI_BUFFER_TOO_SMALL
;
283 QemuCpuhpWriteCommand (MmCpuIo
, QEMU_CPUHP_CMD_GET_ARCH_ID
);
284 NewApicId
= QemuCpuhpReadCommandData (MmCpuIo
);
285 DEBUG ((DEBUG_VERBOSE
, "%a: ApicId=" FMT_APIC_ID
"\n", __FUNCTION__
,
287 ExtendIds
[(*ExtendCount
)++] = NewApicId
;
290 // We've processed the CPU with (known) pending events, but we must never
291 // clear events. Therefore we need to advance past this CPU manually;
292 // otherwise, QEMU_CPUHP_CMD_GET_PENDING would stick to the currently
296 } while (CurrentSelector
< PossibleCpuCount
);
298 DEBUG ((DEBUG_VERBOSE
, "%a: PluggedCount=%u ToUnplugCount=%u\n",
299 __FUNCTION__
, *PluggedCount
, *ToUnplugCount
));