TianoCore EDK2 master
Loading...
Searching...
No Matches
QemuCpuhp.c
Go to the documentation of this file.
1
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()
19
20#include "QemuCpuhp.h"
21
22UINT32
23QemuCpuhpReadCommandData2 (
25 )
26{
27 UINT32 CommandData2;
28 EFI_STATUS Status;
29
30 CommandData2 = 0;
31 Status = MmCpuIo->Io.Read (
32 MmCpuIo,
33 MM_IO_UINT32,
34 ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_R_CMD_DATA2,
35 1,
36 &CommandData2
37 );
38 if (EFI_ERROR (Status)) {
39 DEBUG ((DEBUG_ERROR, "%a: %r\n", __func__, Status));
40 ASSERT (FALSE);
41 CpuDeadLoop ();
42 }
43
44 return CommandData2;
45}
46
47UINT8
48QemuCpuhpReadCpuStatus (
50 )
51{
52 UINT8 CpuStatus;
53 EFI_STATUS Status;
54
55 CpuStatus = 0;
56 Status = MmCpuIo->Io.Read (
57 MmCpuIo,
58 MM_IO_UINT8,
59 ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_R_CPU_STAT,
60 1,
61 &CpuStatus
62 );
63 if (EFI_ERROR (Status)) {
64 DEBUG ((DEBUG_ERROR, "%a: %r\n", __func__, Status));
65 ASSERT (FALSE);
66 CpuDeadLoop ();
67 }
68
69 return CpuStatus;
70}
71
72UINT32
73QemuCpuhpReadCommandData (
75 )
76{
77 UINT32 CommandData;
78 EFI_STATUS Status;
79
80 CommandData = 0;
81 Status = MmCpuIo->Io.Read (
82 MmCpuIo,
83 MM_IO_UINT32,
84 ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_RW_CMD_DATA,
85 1,
86 &CommandData
87 );
88 if (EFI_ERROR (Status)) {
89 DEBUG ((DEBUG_ERROR, "%a: %r\n", __func__, Status));
90 ASSERT (FALSE);
91 CpuDeadLoop ();
92 }
93
94 return CommandData;
95}
96
97VOID
98QemuCpuhpWriteCpuSelector (
100 IN UINT32 Selector
101 )
102{
103 EFI_STATUS Status;
104
105 Status = MmCpuIo->Io.Write (
106 MmCpuIo,
107 MM_IO_UINT32,
108 ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_W_CPU_SEL,
109 1,
110 &Selector
111 );
112 if (EFI_ERROR (Status)) {
113 DEBUG ((DEBUG_ERROR, "%a: %r\n", __func__, Status));
114 ASSERT (FALSE);
115 CpuDeadLoop ();
116 }
117}
118
119VOID
120QemuCpuhpWriteCpuStatus (
122 IN UINT8 CpuStatus
123 )
124{
125 EFI_STATUS Status;
126
127 Status = MmCpuIo->Io.Write (
128 MmCpuIo,
129 MM_IO_UINT8,
130 ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_R_CPU_STAT,
131 1,
132 &CpuStatus
133 );
134 if (EFI_ERROR (Status)) {
135 DEBUG ((DEBUG_ERROR, "%a: %r\n", __func__, Status));
136 ASSERT (FALSE);
137 CpuDeadLoop ();
138 }
139}
140
141VOID
142QemuCpuhpWriteCommand (
144 IN UINT8 Command
145 )
146{
147 EFI_STATUS Status;
148
149 Status = MmCpuIo->Io.Write (
150 MmCpuIo,
151 MM_IO_UINT8,
152 ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_W_CMD,
153 1,
154 &Command
155 );
156 if (EFI_ERROR (Status)) {
157 DEBUG ((DEBUG_ERROR, "%a: %r\n", __func__, Status));
158 ASSERT (FALSE);
159 CpuDeadLoop ();
160 }
161}
162
213 IN UINT32 PossibleCpuCount,
214 IN UINT32 ApicIdCount,
215 OUT APIC_ID *PluggedApicIds,
216 OUT UINT32 *PluggedCount,
217 OUT APIC_ID *ToUnplugApicIds,
218 OUT UINT32 *ToUnplugSelectors,
219 OUT UINT32 *ToUnplugCount
220 )
221{
222 UINT32 CurrentSelector;
223
224 if ((PossibleCpuCount == 0) || (ApicIdCount == 0)) {
225 return EFI_INVALID_PARAMETER;
226 }
227
228 *PluggedCount = 0;
229 *ToUnplugCount = 0;
230
231 CurrentSelector = 0;
232 do {
233 UINT32 PendingSelector;
234 UINT8 CpuStatus;
235 APIC_ID *ExtendIds;
236 UINT32 *ExtendSels;
237 UINT32 *ExtendCount;
238 APIC_ID NewApicId;
239
240 //
241 // Write CurrentSelector (which is valid) to the CPU selector register.
242 // Consequences:
243 //
244 // - Other register accesses will be permitted.
245 //
246 // - The QEMU_CPUHP_CMD_GET_PENDING command will start scanning for a CPU
247 // with pending events at CurrentSelector (inclusive).
248 //
249 QemuCpuhpWriteCpuSelector (MmCpuIo, CurrentSelector);
250 //
251 // Write the QEMU_CPUHP_CMD_GET_PENDING command. Consequences
252 // (independently of each other):
253 //
254 // - If there is a CPU with pending events, starting at CurrentSelector
255 // (inclusive), the CPU selector will be updated to that CPU. Note that
256 // the scanning in QEMU may wrap around, because we must never clear the
257 // event bits.
258 //
259 // - The QEMU_CPUHP_RW_CMD_DATA register will return the (possibly updated)
260 // CPU selector value.
261 //
262 QemuCpuhpWriteCommand (MmCpuIo, QEMU_CPUHP_CMD_GET_PENDING);
263 PendingSelector = QemuCpuhpReadCommandData (MmCpuIo);
264 if (PendingSelector < CurrentSelector) {
265 DEBUG ((
266 DEBUG_VERBOSE,
267 "%a: CurrentSelector=%u PendingSelector=%u: "
268 "wrap-around\n",
269 __func__,
270 CurrentSelector,
271 PendingSelector
272 ));
273 break;
274 }
275
276 CurrentSelector = PendingSelector;
277
278 //
279 // Check the known status / event bits for the currently selected CPU.
280 //
281 CpuStatus = QemuCpuhpReadCpuStatus (MmCpuIo);
282 if ((CpuStatus & QEMU_CPUHP_STAT_INSERT) != 0) {
283 //
284 // The "insert" event guarantees the "enabled" status; plus it excludes
285 // the "fw_remove" event.
286 //
287 if (((CpuStatus & QEMU_CPUHP_STAT_ENABLED) == 0) ||
288 ((CpuStatus & QEMU_CPUHP_STAT_FW_REMOVE) != 0))
289 {
290 DEBUG ((
291 DEBUG_ERROR,
292 "%a: CurrentSelector=%u CpuStatus=0x%x: "
293 "inconsistent CPU status\n",
294 __func__,
295 CurrentSelector,
296 CpuStatus
297 ));
298 return EFI_PROTOCOL_ERROR;
299 }
300
301 DEBUG ((
302 DEBUG_VERBOSE,
303 "%a: CurrentSelector=%u: insert\n",
304 __func__,
305 CurrentSelector
306 ));
307
308 ExtendIds = PluggedApicIds;
309 ExtendSels = NULL;
310 ExtendCount = PluggedCount;
311 } else if ((CpuStatus & QEMU_CPUHP_STAT_FW_REMOVE) != 0) {
312 //
313 // "fw_remove" event guarantees "enabled".
314 //
315 if ((CpuStatus & QEMU_CPUHP_STAT_ENABLED) == 0) {
316 DEBUG ((
317 DEBUG_ERROR,
318 "%a: CurrentSelector=%u CpuStatus=0x%x: "
319 "inconsistent CPU status\n",
320 __func__,
321 CurrentSelector,
322 CpuStatus
323 ));
324 return EFI_PROTOCOL_ERROR;
325 }
326
327 DEBUG ((
328 DEBUG_VERBOSE,
329 "%a: CurrentSelector=%u: fw_remove\n",
330 __func__,
331 CurrentSelector
332 ));
333
334 ExtendIds = ToUnplugApicIds;
335 ExtendSels = ToUnplugSelectors;
336 ExtendCount = ToUnplugCount;
337 } else if ((CpuStatus & QEMU_CPUHP_STAT_REMOVE) != 0) {
338 //
339 // Let the OSPM deal with the "remove" event.
340 //
341 DEBUG ((
342 DEBUG_VERBOSE,
343 "%a: CurrentSelector=%u: remove (ignored)\n",
344 __func__,
345 CurrentSelector
346 ));
347
348 ExtendIds = NULL;
349 ExtendSels = NULL;
350 ExtendCount = NULL;
351 } else {
352 DEBUG ((
353 DEBUG_VERBOSE,
354 "%a: CurrentSelector=%u: no event\n",
355 __func__,
356 CurrentSelector
357 ));
358 break;
359 }
360
361 ASSERT ((ExtendIds == NULL) == (ExtendCount == NULL));
362 ASSERT ((ExtendSels == NULL) || (ExtendIds != NULL));
363
364 if (ExtendIds != NULL) {
365 //
366 // Save the APIC ID of the CPU with the pending event, to the
367 // corresponding APIC ID array.
368 // For unplug events, also save the CurrentSelector.
369 //
370 if (*ExtendCount == ApicIdCount) {
371 DEBUG ((DEBUG_ERROR, "%a: APIC ID array too small\n", __func__));
372 return EFI_BUFFER_TOO_SMALL;
373 }
374
375 QemuCpuhpWriteCommand (MmCpuIo, QEMU_CPUHP_CMD_GET_ARCH_ID);
376 NewApicId = QemuCpuhpReadCommandData (MmCpuIo);
377 DEBUG ((
378 DEBUG_VERBOSE,
379 "%a: ApicId=" FMT_APIC_ID "\n",
380 __func__,
381 NewApicId
382 ));
383 if (ExtendSels != NULL) {
384 ExtendSels[(*ExtendCount)] = CurrentSelector;
385 }
386
387 ExtendIds[(*ExtendCount)++] = NewApicId;
388 }
389
390 //
391 // We've processed the CPU with (known) pending events, but we must never
392 // clear events. Therefore we need to advance past this CPU manually;
393 // otherwise, QEMU_CPUHP_CMD_GET_PENDING would stick to the currently
394 // selected CPU.
395 //
396 CurrentSelector++;
397 } while (CurrentSelector < PossibleCpuCount);
398
399 DEBUG ((
400 DEBUG_VERBOSE,
401 "%a: PluggedCount=%u ToUnplugCount=%u\n",
402 __func__,
403 *PluggedCount,
404 *ToUnplugCount
405 ));
406 return EFI_SUCCESS;
407}
VOID EFIAPI CpuDeadLoop(VOID)
Definition: CpuDeadLoop.c:25
#define NULL
Definition: Base.h:319
#define CONST
Definition: Base.h:259
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define DEBUG(Expression)
Definition: DebugLib.h:434
EFI_STATUS QemuCpuhpCollectApicIds(IN CONST EFI_MM_CPU_IO_PROTOCOL *MmCpuIo, IN UINT32 PossibleCpuCount, IN UINT32 ApicIdCount, OUT APIC_ID *PluggedApicIds, OUT UINT32 *PluggedCount, OUT APIC_ID *ToUnplugApicIds, OUT UINT32 *ToUnplugSelectors, OUT UINT32 *ToUnplugCount)
Definition: QemuCpuhp.c:211
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
#define EFI_SUCCESS
Definition: UefiBaseType.h:112