]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/CpuHotplugSmm/QemuCpuhp.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / OvmfPkg / CpuHotplugSmm / QemuCpuhp.c
CommitLineData
590f5f09 1/** @file\r
763840c9
LE
2 Simple wrapper functions and utility functions that access QEMU's modern CPU\r
3 hotplug register block.\r
590f5f09 4\r
763840c9 5 These functions manipulate some of the registers described in\r
590f5f09
LE
6 "docs/specs/acpi_cpu_hotplug.txt" in the QEMU source. IO Ports are accessed\r
7 via EFI_MM_CPU_IO_PROTOCOL. If a protocol call fails, these functions don't\r
8 return.\r
9\r
10 Copyright (c) 2020, Red Hat, Inc.\r
11\r
12 SPDX-License-Identifier: BSD-2-Clause-Patent\r
13**/\r
14\r
15#include <IndustryStandard/Q35MchIch9.h> // ICH9_CPU_HOTPLUG_BASE\r
16#include <IndustryStandard/QemuCpuHotplug.h> // QEMU_CPUHP_R_CMD_DATA2\r
17#include <Library/BaseLib.h> // CpuDeadLoop()\r
18#include <Library/DebugLib.h> // DEBUG()\r
19\r
20#include "QemuCpuhp.h"\r
21\r
22UINT32\r
23QemuCpuhpReadCommandData2 (\r
ac0a286f 24 IN CONST EFI_MM_CPU_IO_PROTOCOL *MmCpuIo\r
590f5f09
LE
25 )\r
26{\r
ac0a286f
MK
27 UINT32 CommandData2;\r
28 EFI_STATUS Status;\r
590f5f09
LE
29\r
30 CommandData2 = 0;\r
ac0a286f
MK
31 Status = MmCpuIo->Io.Read (\r
32 MmCpuIo,\r
33 MM_IO_UINT32,\r
34 ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_R_CMD_DATA2,\r
35 1,\r
36 &CommandData2\r
37 );\r
590f5f09
LE
38 if (EFI_ERROR (Status)) {\r
39 DEBUG ((DEBUG_ERROR, "%a: %r\n", __FUNCTION__, Status));\r
40 ASSERT (FALSE);\r
41 CpuDeadLoop ();\r
42 }\r
ac0a286f 43\r
590f5f09
LE
44 return CommandData2;\r
45}\r
46\r
47UINT8\r
48QemuCpuhpReadCpuStatus (\r
ac0a286f 49 IN CONST EFI_MM_CPU_IO_PROTOCOL *MmCpuIo\r
590f5f09
LE
50 )\r
51{\r
ac0a286f
MK
52 UINT8 CpuStatus;\r
53 EFI_STATUS Status;\r
590f5f09
LE
54\r
55 CpuStatus = 0;\r
ac0a286f
MK
56 Status = MmCpuIo->Io.Read (\r
57 MmCpuIo,\r
58 MM_IO_UINT8,\r
59 ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_R_CPU_STAT,\r
60 1,\r
61 &CpuStatus\r
62 );\r
590f5f09
LE
63 if (EFI_ERROR (Status)) {\r
64 DEBUG ((DEBUG_ERROR, "%a: %r\n", __FUNCTION__, Status));\r
65 ASSERT (FALSE);\r
66 CpuDeadLoop ();\r
67 }\r
ac0a286f 68\r
590f5f09
LE
69 return CpuStatus;\r
70}\r
71\r
72UINT32\r
73QemuCpuhpReadCommandData (\r
ac0a286f 74 IN CONST EFI_MM_CPU_IO_PROTOCOL *MmCpuIo\r
590f5f09
LE
75 )\r
76{\r
ac0a286f
MK
77 UINT32 CommandData;\r
78 EFI_STATUS Status;\r
590f5f09
LE
79\r
80 CommandData = 0;\r
ac0a286f
MK
81 Status = MmCpuIo->Io.Read (\r
82 MmCpuIo,\r
83 MM_IO_UINT32,\r
84 ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_RW_CMD_DATA,\r
85 1,\r
86 &CommandData\r
87 );\r
590f5f09
LE
88 if (EFI_ERROR (Status)) {\r
89 DEBUG ((DEBUG_ERROR, "%a: %r\n", __FUNCTION__, Status));\r
90 ASSERT (FALSE);\r
91 CpuDeadLoop ();\r
92 }\r
ac0a286f 93\r
590f5f09
LE
94 return CommandData;\r
95}\r
96\r
97VOID\r
98QemuCpuhpWriteCpuSelector (\r
ac0a286f
MK
99 IN CONST EFI_MM_CPU_IO_PROTOCOL *MmCpuIo,\r
100 IN UINT32 Selector\r
590f5f09
LE
101 )\r
102{\r
ac0a286f 103 EFI_STATUS Status;\r
590f5f09
LE
104\r
105 Status = MmCpuIo->Io.Write (\r
106 MmCpuIo,\r
107 MM_IO_UINT32,\r
108 ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_W_CPU_SEL,\r
109 1,\r
110 &Selector\r
111 );\r
112 if (EFI_ERROR (Status)) {\r
113 DEBUG ((DEBUG_ERROR, "%a: %r\n", __FUNCTION__, Status));\r
114 ASSERT (FALSE);\r
115 CpuDeadLoop ();\r
116 }\r
117}\r
118\r
2d92e052
AA
119VOID\r
120QemuCpuhpWriteCpuStatus (\r
ac0a286f
MK
121 IN CONST EFI_MM_CPU_IO_PROTOCOL *MmCpuIo,\r
122 IN UINT8 CpuStatus\r
2d92e052
AA
123 )\r
124{\r
ac0a286f 125 EFI_STATUS Status;\r
2d92e052
AA
126\r
127 Status = MmCpuIo->Io.Write (\r
128 MmCpuIo,\r
129 MM_IO_UINT8,\r
130 ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_R_CPU_STAT,\r
131 1,\r
132 &CpuStatus\r
133 );\r
134 if (EFI_ERROR (Status)) {\r
135 DEBUG ((DEBUG_ERROR, "%a: %r\n", __FUNCTION__, Status));\r
136 ASSERT (FALSE);\r
137 CpuDeadLoop ();\r
138 }\r
139}\r
140\r
590f5f09
LE
141VOID\r
142QemuCpuhpWriteCommand (\r
ac0a286f
MK
143 IN CONST EFI_MM_CPU_IO_PROTOCOL *MmCpuIo,\r
144 IN UINT8 Command\r
590f5f09
LE
145 )\r
146{\r
ac0a286f 147 EFI_STATUS Status;\r
590f5f09
LE
148\r
149 Status = MmCpuIo->Io.Write (\r
150 MmCpuIo,\r
151 MM_IO_UINT8,\r
152 ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_W_CMD,\r
153 1,\r
154 &Command\r
155 );\r
156 if (EFI_ERROR (Status)) {\r
157 DEBUG ((DEBUG_ERROR, "%a: %r\n", __FUNCTION__, Status));\r
158 ASSERT (FALSE);\r
159 CpuDeadLoop ();\r
160 }\r
161}\r
763840c9
LE
162\r
163/**\r
164 Collect the APIC IDs of\r
165 - the CPUs that have been hot-plugged,\r
166 - the CPUs that are about to be hot-unplugged.\r
167\r
168 This function only scans for events -- it does not modify them -- in the\r
169 hotplug registers.\r
170\r
171 On error, the contents of the output parameters are undefined.\r
172\r
a752dd07
AA
173 @param[in] MmCpuIo The EFI_MM_CPU_IO_PROTOCOL instance for\r
174 accessing IO Ports.\r
763840c9 175\r
a752dd07
AA
176 @param[in] PossibleCpuCount The number of possible CPUs in the system. Must\r
177 be positive.\r
763840c9 178\r
a752dd07
AA
179 @param[in] ApicIdCount The number of elements each one of the\r
180 PluggedApicIds and ToUnplugApicIds arrays can\r
181 accommodate. Must be positive.\r
763840c9 182\r
a752dd07
AA
183 @param[out] PluggedApicIds The APIC IDs of the CPUs that have been\r
184 hot-plugged.\r
763840c9 185\r
a752dd07
AA
186 @param[out] PluggedCount The number of filled-in APIC IDs in\r
187 PluggedApicIds.\r
763840c9 188\r
a752dd07
AA
189 @param[out] ToUnplugApicIds The APIC IDs of the CPUs that are about to be\r
190 hot-unplugged.\r
763840c9 191\r
a752dd07
AA
192 @param[out] ToUnplugSelectors The QEMU Selectors of the CPUs that are about\r
193 to be hot-unplugged.\r
194\r
195 @param[out] ToUnplugCount The number of filled-in APIC IDs in\r
196 ToUnplugApicIds.\r
763840c9
LE
197\r
198 @retval EFI_INVALID_PARAMETER PossibleCpuCount is zero, or ApicIdCount is\r
199 zero.\r
200\r
201 @retval EFI_PROTOCOL_ERROR Invalid bitmap detected in the\r
202 QEMU_CPUHP_R_CPU_STAT register.\r
203\r
204 @retval EFI_BUFFER_TOO_SMALL There was an attempt to place more than\r
205 ApicIdCount APIC IDs into one of the\r
206 PluggedApicIds and ToUnplugApicIds arrays.\r
207\r
208 @retval EFI_SUCCESS Output parameters have been set successfully.\r
209**/\r
210EFI_STATUS\r
211QemuCpuhpCollectApicIds (\r
ac0a286f
MK
212 IN CONST EFI_MM_CPU_IO_PROTOCOL *MmCpuIo,\r
213 IN UINT32 PossibleCpuCount,\r
214 IN UINT32 ApicIdCount,\r
215 OUT APIC_ID *PluggedApicIds,\r
216 OUT UINT32 *PluggedCount,\r
217 OUT APIC_ID *ToUnplugApicIds,\r
218 OUT UINT32 *ToUnplugSelectors,\r
219 OUT UINT32 *ToUnplugCount\r
763840c9
LE
220 )\r
221{\r
ac0a286f 222 UINT32 CurrentSelector;\r
763840c9 223\r
ac0a286f 224 if ((PossibleCpuCount == 0) || (ApicIdCount == 0)) {\r
763840c9
LE
225 return EFI_INVALID_PARAMETER;\r
226 }\r
227\r
ac0a286f 228 *PluggedCount = 0;\r
763840c9
LE
229 *ToUnplugCount = 0;\r
230\r
231 CurrentSelector = 0;\r
232 do {\r
ac0a286f
MK
233 UINT32 PendingSelector;\r
234 UINT8 CpuStatus;\r
235 APIC_ID *ExtendIds;\r
236 UINT32 *ExtendSels;\r
237 UINT32 *ExtendCount;\r
238 APIC_ID NewApicId;\r
763840c9
LE
239\r
240 //\r
241 // Write CurrentSelector (which is valid) to the CPU selector register.\r
242 // Consequences:\r
243 //\r
244 // - Other register accesses will be permitted.\r
245 //\r
246 // - The QEMU_CPUHP_CMD_GET_PENDING command will start scanning for a CPU\r
247 // with pending events at CurrentSelector (inclusive).\r
248 //\r
249 QemuCpuhpWriteCpuSelector (MmCpuIo, CurrentSelector);\r
250 //\r
251 // Write the QEMU_CPUHP_CMD_GET_PENDING command. Consequences\r
252 // (independently of each other):\r
253 //\r
254 // - If there is a CPU with pending events, starting at CurrentSelector\r
255 // (inclusive), the CPU selector will be updated to that CPU. Note that\r
256 // the scanning in QEMU may wrap around, because we must never clear the\r
257 // event bits.\r
258 //\r
259 // - The QEMU_CPUHP_RW_CMD_DATA register will return the (possibly updated)\r
260 // CPU selector value.\r
261 //\r
262 QemuCpuhpWriteCommand (MmCpuIo, QEMU_CPUHP_CMD_GET_PENDING);\r
263 PendingSelector = QemuCpuhpReadCommandData (MmCpuIo);\r
264 if (PendingSelector < CurrentSelector) {\r
ac0a286f
MK
265 DEBUG ((\r
266 DEBUG_VERBOSE,\r
267 "%a: CurrentSelector=%u PendingSelector=%u: "\r
268 "wrap-around\n",\r
269 __FUNCTION__,\r
270 CurrentSelector,\r
271 PendingSelector\r
272 ));\r
763840c9
LE
273 break;\r
274 }\r
ac0a286f 275\r
763840c9
LE
276 CurrentSelector = PendingSelector;\r
277\r
278 //\r
279 // Check the known status / event bits for the currently selected CPU.\r
280 //\r
281 CpuStatus = QemuCpuhpReadCpuStatus (MmCpuIo);\r
282 if ((CpuStatus & QEMU_CPUHP_STAT_INSERT) != 0) {\r
283 //\r
284 // The "insert" event guarantees the "enabled" status; plus it excludes\r
a752dd07 285 // the "fw_remove" event.\r
763840c9 286 //\r
ac0a286f
MK
287 if (((CpuStatus & QEMU_CPUHP_STAT_ENABLED) == 0) ||\r
288 ((CpuStatus & QEMU_CPUHP_STAT_FW_REMOVE) != 0))\r
289 {\r
290 DEBUG ((\r
291 DEBUG_ERROR,\r
292 "%a: CurrentSelector=%u CpuStatus=0x%x: "\r
293 "inconsistent CPU status\n",\r
294 __FUNCTION__,\r
295 CurrentSelector,\r
296 CpuStatus\r
297 ));\r
763840c9
LE
298 return EFI_PROTOCOL_ERROR;\r
299 }\r
300\r
ac0a286f
MK
301 DEBUG ((\r
302 DEBUG_VERBOSE,\r
303 "%a: CurrentSelector=%u: insert\n",\r
304 __FUNCTION__,\r
305 CurrentSelector\r
306 ));\r
763840c9
LE
307\r
308 ExtendIds = PluggedApicIds;\r
a752dd07 309 ExtendSels = NULL;\r
763840c9 310 ExtendCount = PluggedCount;\r
a752dd07
AA
311 } else if ((CpuStatus & QEMU_CPUHP_STAT_FW_REMOVE) != 0) {\r
312 //\r
313 // "fw_remove" event guarantees "enabled".\r
314 //\r
315 if ((CpuStatus & QEMU_CPUHP_STAT_ENABLED) == 0) {\r
ac0a286f
MK
316 DEBUG ((\r
317 DEBUG_ERROR,\r
318 "%a: CurrentSelector=%u CpuStatus=0x%x: "\r
319 "inconsistent CPU status\n",\r
320 __FUNCTION__,\r
321 CurrentSelector,\r
322 CpuStatus\r
323 ));\r
a752dd07
AA
324 return EFI_PROTOCOL_ERROR;\r
325 }\r
326\r
ac0a286f
MK
327 DEBUG ((\r
328 DEBUG_VERBOSE,\r
329 "%a: CurrentSelector=%u: fw_remove\n",\r
330 __FUNCTION__,\r
331 CurrentSelector\r
332 ));\r
763840c9
LE
333\r
334 ExtendIds = ToUnplugApicIds;\r
a752dd07 335 ExtendSels = ToUnplugSelectors;\r
763840c9 336 ExtendCount = ToUnplugCount;\r
a752dd07
AA
337 } else if ((CpuStatus & QEMU_CPUHP_STAT_REMOVE) != 0) {\r
338 //\r
339 // Let the OSPM deal with the "remove" event.\r
340 //\r
ac0a286f
MK
341 DEBUG ((\r
342 DEBUG_VERBOSE,\r
343 "%a: CurrentSelector=%u: remove (ignored)\n",\r
344 __FUNCTION__,\r
345 CurrentSelector\r
346 ));\r
a752dd07
AA
347\r
348 ExtendIds = NULL;\r
349 ExtendSels = NULL;\r
350 ExtendCount = NULL;\r
763840c9 351 } else {\r
ac0a286f
MK
352 DEBUG ((\r
353 DEBUG_VERBOSE,\r
354 "%a: CurrentSelector=%u: no event\n",\r
355 __FUNCTION__,\r
356 CurrentSelector\r
357 ));\r
763840c9
LE
358 break;\r
359 }\r
360\r
a752dd07
AA
361 ASSERT ((ExtendIds == NULL) == (ExtendCount == NULL));\r
362 ASSERT ((ExtendSels == NULL) || (ExtendIds != NULL));\r
763840c9 363\r
a752dd07
AA
364 if (ExtendIds != NULL) {\r
365 //\r
366 // Save the APIC ID of the CPU with the pending event, to the\r
367 // corresponding APIC ID array.\r
368 // For unplug events, also save the CurrentSelector.\r
369 //\r
370 if (*ExtendCount == ApicIdCount) {\r
371 DEBUG ((DEBUG_ERROR, "%a: APIC ID array too small\n", __FUNCTION__));\r
372 return EFI_BUFFER_TOO_SMALL;\r
373 }\r
ac0a286f 374\r
a752dd07
AA
375 QemuCpuhpWriteCommand (MmCpuIo, QEMU_CPUHP_CMD_GET_ARCH_ID);\r
376 NewApicId = QemuCpuhpReadCommandData (MmCpuIo);\r
ac0a286f
MK
377 DEBUG ((\r
378 DEBUG_VERBOSE,\r
379 "%a: ApicId=" FMT_APIC_ID "\n",\r
380 __FUNCTION__,\r
381 NewApicId\r
382 ));\r
a752dd07
AA
383 if (ExtendSels != NULL) {\r
384 ExtendSels[(*ExtendCount)] = CurrentSelector;\r
385 }\r
ac0a286f 386\r
a752dd07
AA
387 ExtendIds[(*ExtendCount)++] = NewApicId;\r
388 }\r
ac0a286f 389\r
763840c9
LE
390 //\r
391 // We've processed the CPU with (known) pending events, but we must never\r
392 // clear events. Therefore we need to advance past this CPU manually;\r
393 // otherwise, QEMU_CPUHP_CMD_GET_PENDING would stick to the currently\r
394 // selected CPU.\r
395 //\r
396 CurrentSelector++;\r
397 } while (CurrentSelector < PossibleCpuCount);\r
398\r
ac0a286f
MK
399 DEBUG ((\r
400 DEBUG_VERBOSE,\r
401 "%a: PluggedCount=%u ToUnplugCount=%u\n",\r
402 __FUNCTION__,\r
403 *PluggedCount,\r
404 *ToUnplugCount\r
405 ));\r
763840c9
LE
406 return EFI_SUCCESS;\r
407}\r