]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/CpuHotplugSmm/CpuHotplug.c
OvmfPkg/SmmCpuFeaturesLib: call CPU hot-eject handler
[mirror_edk2.git] / OvmfPkg / CpuHotplugSmm / CpuHotplug.c
CommitLineData
17efae27
LE
1/** @file\r
2 Root SMI handler for VCPU hotplug SMIs.\r
3\r
4 Copyright (c) 2020, Red Hat, Inc.\r
5\r
6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
7**/\r
8\r
17cb8ddb 9#include <CpuHotPlugData.h> // CPU_HOT_PLUG_DATA\r
17efae27 10#include <IndustryStandard/Q35MchIch9.h> // ICH9_APM_CNT\r
f668e788 11#include <IndustryStandard/QemuCpuHotplug.h> // QEMU_CPUHP_CMD_GET_PENDING\r
17efae27
LE
12#include <Library/BaseLib.h> // CpuDeadLoop()\r
13#include <Library/DebugLib.h> // ASSERT()\r
14#include <Library/MmServicesTableLib.h> // gMmst\r
15#include <Library/PcdLib.h> // PcdGetBool()\r
17cb8ddb 16#include <Library/SafeIntLib.h> // SafeUintnSub()\r
17efae27 17#include <Protocol/MmCpuIo.h> // EFI_MM_CPU_IO_PROTOCOL\r
17cb8ddb 18#include <Protocol/SmmCpuService.h> // EFI_SMM_CPU_SERVICE_PROTOCOL\r
17efae27
LE
19#include <Uefi/UefiBaseType.h> // EFI_STATUS\r
20\r
17cb8ddb 21#include "ApicId.h" // APIC_ID\r
f668e788 22#include "QemuCpuhp.h" // QemuCpuhpWriteCpuSelector()\r
bc498ac4 23#include "Smbase.h" // SmbaseAllocatePostSmmPen()\r
f668e788 24\r
17efae27
LE
25//\r
26// We use this protocol for accessing IO Ports.\r
27//\r
28STATIC EFI_MM_CPU_IO_PROTOCOL *mMmCpuIo;\r
29//\r
17cb8ddb
LE
30// The following protocol is used to report the addition or removal of a CPU to\r
31// the SMM CPU driver (PiSmmCpuDxeSmm).\r
32//\r
33STATIC EFI_SMM_CPU_SERVICE_PROTOCOL *mMmCpuService;\r
34//\r
35// This structure is a communication side-channel between the\r
36// EFI_SMM_CPU_SERVICE_PROTOCOL consumer (i.e., this driver) and provider\r
37// (i.e., PiSmmCpuDxeSmm).\r
38//\r
39STATIC CPU_HOT_PLUG_DATA *mCpuHotPlugData;\r
40//\r
41// SMRAM arrays for fetching the APIC IDs of processors with pending events (of\r
42// known event types), for the time of just one MMI.\r
43//\r
44// The lifetimes of these arrays match that of this driver only because we\r
45// don't want to allocate SMRAM at OS runtime, and potentially fail (or\r
46// fragment the SMRAM map).\r
47//\r
a752dd07
AA
48// The first array stores APIC IDs for hot-plug events, the second and the\r
49// third store APIC IDs and QEMU CPU Selectors (both indexed similarly) for\r
50// hot-unplug events. All of these provide room for "possible CPU count" minus\r
51// one elements as we don't expect every possible CPU to appear, or disappear,\r
52// in a single MMI. The numbers of used (populated) elements in the arrays are\r
17cb8ddb
LE
53// determined on every MMI separately.\r
54//\r
55STATIC APIC_ID *mPluggedApicIds;\r
56STATIC APIC_ID *mToUnplugApicIds;\r
a752dd07 57STATIC UINT32 *mToUnplugSelectors;\r
17cb8ddb 58//\r
bc498ac4
LE
59// Address of the non-SMRAM reserved memory page that contains the Post-SMM Pen\r
60// for hot-added CPUs.\r
61//\r
62STATIC UINT32 mPostSmmPenAddress;\r
63//\r
17efae27
LE
64// Represents the registration of the CPU Hotplug MMI handler.\r
65//\r
66STATIC EFI_HANDLE mDispatchHandle;\r
67\r
0cb242e3
AA
68/**\r
69 Process CPUs that have been hot-added, per QemuCpuhpCollectApicIds().\r
70\r
71 For each such CPU, relocate the SMBASE, and report the CPU to PiSmmCpuDxeSmm\r
72 via EFI_SMM_CPU_SERVICE_PROTOCOL. If the supposedly hot-added CPU is already\r
73 known, skip it silently.\r
74\r
75 @param[in] PluggedApicIds The APIC IDs of the CPUs that have been\r
76 hot-plugged.\r
77\r
78 @param[in] PluggedCount The number of filled-in APIC IDs in\r
79 PluggedApicIds.\r
80\r
81 @retval EFI_SUCCESS CPUs corresponding to all the APIC IDs are\r
82 populated.\r
83\r
84 @retval EFI_OUT_OF_RESOURCES Out of APIC ID space in "mCpuHotPlugData".\r
85\r
86 @return Error codes propagated from SmbaseRelocate()\r
87 and mMmCpuService->AddProcessor().\r
88**/\r
89STATIC\r
90EFI_STATUS\r
91ProcessHotAddedCpus (\r
92 IN APIC_ID *PluggedApicIds,\r
93 IN UINT32 PluggedCount\r
94 )\r
95{\r
96 EFI_STATUS Status;\r
97 UINT32 PluggedIdx;\r
98 UINT32 NewSlot;\r
99\r
100 //\r
101 // The Post-SMM Pen need not be reinstalled multiple times within a single\r
102 // root MMI handling. Even reinstalling once per root MMI is only prudence;\r
103 // in theory installing the pen in the driver's entry point function should\r
104 // suffice.\r
105 //\r
106 SmbaseReinstallPostSmmPen (mPostSmmPenAddress);\r
107\r
108 PluggedIdx = 0;\r
109 NewSlot = 0;\r
110 while (PluggedIdx < PluggedCount) {\r
111 APIC_ID NewApicId;\r
112 UINT32 CheckSlot;\r
113 UINTN NewProcessorNumberByProtocol;\r
114\r
115 NewApicId = PluggedApicIds[PluggedIdx];\r
116\r
117 //\r
118 // Check if the supposedly hot-added CPU is already known to us.\r
119 //\r
120 for (CheckSlot = 0;\r
121 CheckSlot < mCpuHotPlugData->ArrayLength;\r
122 CheckSlot++) {\r
123 if (mCpuHotPlugData->ApicId[CheckSlot] == NewApicId) {\r
124 break;\r
125 }\r
126 }\r
127 if (CheckSlot < mCpuHotPlugData->ArrayLength) {\r
128 DEBUG ((DEBUG_VERBOSE, "%a: APIC ID " FMT_APIC_ID " was hot-plugged "\r
129 "before; ignoring it\n", __FUNCTION__, NewApicId));\r
130 PluggedIdx++;\r
131 continue;\r
132 }\r
133\r
134 //\r
135 // Find the first empty slot in CPU_HOT_PLUG_DATA.\r
136 //\r
137 while (NewSlot < mCpuHotPlugData->ArrayLength &&\r
138 mCpuHotPlugData->ApicId[NewSlot] != MAX_UINT64) {\r
139 NewSlot++;\r
140 }\r
141 if (NewSlot == mCpuHotPlugData->ArrayLength) {\r
142 DEBUG ((DEBUG_ERROR, "%a: no room for APIC ID " FMT_APIC_ID "\n",\r
143 __FUNCTION__, NewApicId));\r
144 return EFI_OUT_OF_RESOURCES;\r
145 }\r
146\r
147 //\r
148 // Store the APIC ID of the new processor to the slot.\r
149 //\r
150 mCpuHotPlugData->ApicId[NewSlot] = NewApicId;\r
151\r
152 //\r
153 // Relocate the SMBASE of the new CPU.\r
154 //\r
155 Status = SmbaseRelocate (NewApicId, mCpuHotPlugData->SmBase[NewSlot],\r
156 mPostSmmPenAddress);\r
157 if (EFI_ERROR (Status)) {\r
158 goto RevokeNewSlot;\r
159 }\r
160\r
161 //\r
162 // Add the new CPU with EFI_SMM_CPU_SERVICE_PROTOCOL.\r
163 //\r
164 Status = mMmCpuService->AddProcessor (mMmCpuService, NewApicId,\r
165 &NewProcessorNumberByProtocol);\r
166 if (EFI_ERROR (Status)) {\r
167 DEBUG ((DEBUG_ERROR, "%a: AddProcessor(" FMT_APIC_ID "): %r\n",\r
168 __FUNCTION__, NewApicId, Status));\r
169 goto RevokeNewSlot;\r
170 }\r
171\r
172 DEBUG ((DEBUG_INFO, "%a: hot-added APIC ID " FMT_APIC_ID ", SMBASE 0x%Lx, "\r
173 "EFI_SMM_CPU_SERVICE_PROTOCOL assigned number %Lu\n", __FUNCTION__,\r
174 NewApicId, (UINT64)mCpuHotPlugData->SmBase[NewSlot],\r
175 (UINT64)NewProcessorNumberByProtocol));\r
176\r
177 NewSlot++;\r
178 PluggedIdx++;\r
179 }\r
180\r
181 //\r
182 // We've processed this batch of hot-added CPUs.\r
183 //\r
184 return EFI_SUCCESS;\r
185\r
186RevokeNewSlot:\r
187 mCpuHotPlugData->ApicId[NewSlot] = MAX_UINT64;\r
188\r
189 return Status;\r
190}\r
17efae27 191\r
15e6ae8e
AA
192/**\r
193 Process to be hot-unplugged CPUs, per QemuCpuhpCollectApicIds().\r
194\r
195 For each such CPU, report the CPU to PiSmmCpuDxeSmm via\r
196 EFI_SMM_CPU_SERVICE_PROTOCOL. If the to be hot-unplugged CPU is\r
197 unknown, skip it silently.\r
198\r
199 @param[in] ToUnplugApicIds The APIC IDs of the CPUs that are about to be\r
200 hot-unplugged.\r
201\r
202 @param[in] ToUnplugCount The number of filled-in APIC IDs in\r
203 ToUnplugApicIds.\r
204\r
205 @retval EFI_SUCCESS Known APIC IDs have been removed from SMM data\r
206 structures.\r
207\r
208 @return Error codes propagated from\r
209 mMmCpuService->RemoveProcessor().\r
210**/\r
211STATIC\r
212EFI_STATUS\r
213UnplugCpus (\r
214 IN APIC_ID *ToUnplugApicIds,\r
215 IN UINT32 ToUnplugCount\r
216 )\r
217{\r
218 EFI_STATUS Status;\r
219 UINT32 ToUnplugIdx;\r
220 UINTN ProcessorNum;\r
221\r
222 ToUnplugIdx = 0;\r
223 while (ToUnplugIdx < ToUnplugCount) {\r
224 APIC_ID RemoveApicId;\r
225\r
226 RemoveApicId = ToUnplugApicIds[ToUnplugIdx];\r
227\r
228 //\r
229 // mCpuHotPlugData->ApicId maps ProcessorNum -> ApicId. Use it to find\r
230 // the ProcessorNum for the APIC ID to be removed.\r
231 //\r
232 for (ProcessorNum = 0;\r
233 ProcessorNum < mCpuHotPlugData->ArrayLength;\r
234 ProcessorNum++) {\r
235 if (mCpuHotPlugData->ApicId[ProcessorNum] == RemoveApicId) {\r
236 break;\r
237 }\r
238 }\r
239\r
240 //\r
241 // Ignore the unplug if APIC ID not found\r
242 //\r
243 if (ProcessorNum == mCpuHotPlugData->ArrayLength) {\r
244 DEBUG ((DEBUG_VERBOSE, "%a: did not find APIC ID " FMT_APIC_ID\r
245 " to unplug\n", __FUNCTION__, RemoveApicId));\r
246 ToUnplugIdx++;\r
247 continue;\r
248 }\r
249\r
250 //\r
251 // Mark ProcessorNum for removal from SMM data structures\r
252 //\r
253 Status = mMmCpuService->RemoveProcessor (mMmCpuService, ProcessorNum);\r
254 if (EFI_ERROR (Status)) {\r
255 DEBUG ((DEBUG_ERROR, "%a: RemoveProcessor(" FMT_APIC_ID "): %r\n",\r
256 __FUNCTION__, RemoveApicId, Status));\r
257 return Status;\r
258 }\r
259\r
260 ToUnplugIdx++;\r
261 }\r
262\r
263 //\r
264 // We've removed this set of APIC IDs from SMM data structures.\r
265 //\r
266 return EFI_SUCCESS;\r
267}\r
268\r
17efae27
LE
269/**\r
270 CPU Hotplug MMI handler function.\r
271\r
272 This is a root MMI handler.\r
273\r
274 @param[in] DispatchHandle The unique handle assigned to this handler by\r
275 EFI_MM_SYSTEM_TABLE.MmiHandlerRegister().\r
276\r
277 @param[in] Context Context passed in by\r
278 EFI_MM_SYSTEM_TABLE.MmiManage(). Due to\r
279 CpuHotplugMmi() being a root MMI handler,\r
280 Context is ASSERT()ed to be NULL.\r
281\r
282 @param[in,out] CommBuffer Ignored, due to CpuHotplugMmi() being a root\r
283 MMI handler.\r
284\r
285 @param[in,out] CommBufferSize Ignored, due to CpuHotplugMmi() being a root\r
286 MMI handler.\r
287\r
288 @retval EFI_SUCCESS The MMI was handled and the MMI\r
289 source was quiesced. When returned\r
290 by a non-root MMI handler,\r
291 EFI_SUCCESS terminates the\r
292 processing of MMI handlers in\r
293 EFI_MM_SYSTEM_TABLE.MmiManage().\r
294 For a root MMI handler (i.e., for\r
295 the present function too),\r
296 EFI_SUCCESS behaves identically to\r
297 EFI_WARN_INTERRUPT_SOURCE_QUIESCED,\r
298 as further root MMI handlers are\r
299 going to be called by\r
300 EFI_MM_SYSTEM_TABLE.MmiManage()\r
301 anyway.\r
302\r
303 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The MMI source has been quiesced,\r
304 but other handlers should still\r
305 be called.\r
306\r
307 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The MMI source is still pending,\r
308 and other handlers should still\r
309 be called.\r
310\r
311 @retval EFI_INTERRUPT_PENDING The MMI source could not be\r
312 quiesced.\r
313**/\r
314STATIC\r
315EFI_STATUS\r
316EFIAPI\r
317CpuHotplugMmi (\r
318 IN EFI_HANDLE DispatchHandle,\r
319 IN CONST VOID *Context OPTIONAL,\r
320 IN OUT VOID *CommBuffer OPTIONAL,\r
321 IN OUT UINTN *CommBufferSize OPTIONAL\r
322 )\r
323{\r
324 EFI_STATUS Status;\r
325 UINT8 ApmControl;\r
17cb8ddb
LE
326 UINT32 PluggedCount;\r
327 UINT32 ToUnplugCount;\r
17efae27
LE
328\r
329 //\r
330 // Assert that we are entering this function due to our root MMI handler\r
331 // registration.\r
332 //\r
333 ASSERT (DispatchHandle == mDispatchHandle);\r
334 //\r
335 // When MmiManage() is invoked to process root MMI handlers, the caller (the\r
336 // MM Core) is expected to pass in a NULL Context. MmiManage() then passes\r
337 // the same NULL Context to individual handlers.\r
338 //\r
339 ASSERT (Context == NULL);\r
340 //\r
341 // Read the MMI command value from the APM Control Port, to see if this is an\r
342 // MMI we should care about.\r
343 //\r
344 Status = mMmCpuIo->Io.Read (mMmCpuIo, MM_IO_UINT8, ICH9_APM_CNT, 1,\r
345 &ApmControl);\r
346 if (EFI_ERROR (Status)) {\r
347 DEBUG ((DEBUG_ERROR, "%a: failed to read ICH9_APM_CNT: %r\n", __FUNCTION__,\r
348 Status));\r
349 //\r
350 // We couldn't even determine if the MMI was for us or not.\r
351 //\r
352 goto Fatal;\r
353 }\r
354\r
355 if (ApmControl != ICH9_APM_CNT_CPU_HOTPLUG) {\r
356 //\r
357 // The MMI is not for us.\r
358 //\r
359 return EFI_WARN_INTERRUPT_SOURCE_QUIESCED;\r
360 }\r
361\r
17cb8ddb
LE
362 //\r
363 // Collect the CPUs with pending events.\r
364 //\r
365 Status = QemuCpuhpCollectApicIds (\r
366 mMmCpuIo,\r
367 mCpuHotPlugData->ArrayLength, // PossibleCpuCount\r
368 mCpuHotPlugData->ArrayLength - 1, // ApicIdCount\r
369 mPluggedApicIds,\r
370 &PluggedCount,\r
371 mToUnplugApicIds,\r
a752dd07 372 mToUnplugSelectors,\r
17cb8ddb
LE
373 &ToUnplugCount\r
374 );\r
375 if (EFI_ERROR (Status)) {\r
376 goto Fatal;\r
377 }\r
378 if (ToUnplugCount > 0) {\r
379 DEBUG ((DEBUG_ERROR, "%a: hot-unplug is not supported yet\n",\r
380 __FUNCTION__));\r
381 goto Fatal;\r
382 }\r
383\r
0cb242e3
AA
384 if (PluggedCount > 0) {\r
385 Status = ProcessHotAddedCpus (mPluggedApicIds, PluggedCount);\r
bc498ac4 386 if (EFI_ERROR (Status)) {\r
0cb242e3 387 goto Fatal;\r
bc498ac4 388 }\r
bc498ac4
LE
389 }\r
390\r
15e6ae8e
AA
391 if (ToUnplugCount > 0) {\r
392 Status = UnplugCpus (mToUnplugApicIds, ToUnplugCount);\r
393 if (EFI_ERROR (Status)) {\r
394 goto Fatal;\r
395 }\r
396 }\r
397\r
17efae27
LE
398 //\r
399 // We've handled this MMI.\r
400 //\r
401 return EFI_SUCCESS;\r
402\r
403Fatal:\r
404 ASSERT (FALSE);\r
405 CpuDeadLoop ();\r
406 //\r
407 // We couldn't handle this MMI.\r
408 //\r
409 return EFI_INTERRUPT_PENDING;\r
410}\r
411\r
412\r
413//\r
414// Entry point function of this driver.\r
415//\r
416EFI_STATUS\r
417EFIAPI\r
418CpuHotplugEntry (\r
419 IN EFI_HANDLE ImageHandle,\r
420 IN EFI_SYSTEM_TABLE *SystemTable\r
421 )\r
422{\r
423 EFI_STATUS Status;\r
a752dd07 424 UINTN Len;\r
17cb8ddb 425 UINTN Size;\r
a752dd07 426 UINTN SizeSel;\r
17efae27
LE
427\r
428 //\r
429 // This module should only be included when SMM support is required.\r
430 //\r
431 ASSERT (FeaturePcdGet (PcdSmmSmramRequire));\r
432 //\r
433 // This driver depends on the dynamically detected "SMRAM at default SMBASE"\r
434 // feature.\r
435 //\r
436 if (!PcdGetBool (PcdQ35SmramAtDefaultSmbase)) {\r
437 return EFI_UNSUPPORTED;\r
438 }\r
439\r
440 //\r
441 // Errors from here on are fatal; we cannot allow the boot to proceed if we\r
442 // can't set up this driver to handle CPU hotplug.\r
443 //\r
444 // First, collect the protocols needed later. All of these protocols are\r
445 // listed in our module DEPEX.\r
446 //\r
447 Status = gMmst->MmLocateProtocol (&gEfiMmCpuIoProtocolGuid,\r
448 NULL /* Registration */, (VOID **)&mMmCpuIo);\r
449 if (EFI_ERROR (Status)) {\r
450 DEBUG ((DEBUG_ERROR, "%a: locate MmCpuIo: %r\n", __FUNCTION__, Status));\r
451 goto Fatal;\r
452 }\r
17cb8ddb
LE
453 Status = gMmst->MmLocateProtocol (&gEfiSmmCpuServiceProtocolGuid,\r
454 NULL /* Registration */, (VOID **)&mMmCpuService);\r
455 if (EFI_ERROR (Status)) {\r
456 DEBUG ((DEBUG_ERROR, "%a: locate MmCpuService: %r\n", __FUNCTION__,\r
457 Status));\r
458 goto Fatal;\r
459 }\r
460\r
461 //\r
462 // Our DEPEX on EFI_SMM_CPU_SERVICE_PROTOCOL guarantees that PiSmmCpuDxeSmm\r
463 // has pointed PcdCpuHotPlugDataAddress to CPU_HOT_PLUG_DATA in SMRAM.\r
464 //\r
465 mCpuHotPlugData = (VOID *)(UINTN)PcdGet64 (PcdCpuHotPlugDataAddress);\r
466 if (mCpuHotPlugData == NULL) {\r
467 Status = EFI_NOT_FOUND;\r
468 DEBUG ((DEBUG_ERROR, "%a: CPU_HOT_PLUG_DATA: %r\n", __FUNCTION__, Status));\r
469 goto Fatal;\r
470 }\r
471 //\r
472 // If the possible CPU count is 1, there's nothing for this driver to do.\r
473 //\r
474 if (mCpuHotPlugData->ArrayLength == 1) {\r
475 return EFI_UNSUPPORTED;\r
476 }\r
477 //\r
478 // Allocate the data structures that depend on the possible CPU count.\r
479 //\r
a752dd07
AA
480 if (RETURN_ERROR (SafeUintnSub (mCpuHotPlugData->ArrayLength, 1, &Len)) ||\r
481 RETURN_ERROR (SafeUintnMult (sizeof (APIC_ID), Len, &Size)) ||\r
482 RETURN_ERROR (SafeUintnMult (sizeof (UINT32), Len, &SizeSel))) {\r
17cb8ddb
LE
483 Status = EFI_ABORTED;\r
484 DEBUG ((DEBUG_ERROR, "%a: invalid CPU_HOT_PLUG_DATA\n", __FUNCTION__));\r
485 goto Fatal;\r
486 }\r
487 Status = gMmst->MmAllocatePool (EfiRuntimeServicesData, Size,\r
488 (VOID **)&mPluggedApicIds);\r
489 if (EFI_ERROR (Status)) {\r
490 DEBUG ((DEBUG_ERROR, "%a: MmAllocatePool(): %r\n", __FUNCTION__, Status));\r
491 goto Fatal;\r
492 }\r
493 Status = gMmst->MmAllocatePool (EfiRuntimeServicesData, Size,\r
494 (VOID **)&mToUnplugApicIds);\r
495 if (EFI_ERROR (Status)) {\r
496 DEBUG ((DEBUG_ERROR, "%a: MmAllocatePool(): %r\n", __FUNCTION__, Status));\r
497 goto ReleasePluggedApicIds;\r
498 }\r
a752dd07
AA
499 Status = gMmst->MmAllocatePool (EfiRuntimeServicesData, SizeSel,\r
500 (VOID **)&mToUnplugSelectors);\r
501 if (EFI_ERROR (Status)) {\r
502 DEBUG ((DEBUG_ERROR, "%a: MmAllocatePool(): %r\n", __FUNCTION__, Status));\r
503 goto ReleaseToUnplugApicIds;\r
504 }\r
17efae27 505\r
bc498ac4
LE
506 //\r
507 // Allocate the Post-SMM Pen for hot-added CPUs.\r
508 //\r
509 Status = SmbaseAllocatePostSmmPen (&mPostSmmPenAddress,\r
510 SystemTable->BootServices);\r
511 if (EFI_ERROR (Status)) {\r
a752dd07 512 goto ReleaseToUnplugSelectors;\r
bc498ac4
LE
513 }\r
514\r
f668e788
LE
515 //\r
516 // Sanity-check the CPU hotplug interface.\r
517 //\r
518 // Both of the following features are part of QEMU 5.0, introduced primarily\r
519 // in commit range 3e08b2b9cb64..3a61c8db9d25:\r
520 //\r
521 // (a) the QEMU_CPUHP_CMD_GET_ARCH_ID command of the modern CPU hotplug\r
522 // interface,\r
523 //\r
524 // (b) the "SMRAM at default SMBASE" feature.\r
525 //\r
526 // From these, (b) is restricted to 5.0+ machine type versions, while (a)\r
527 // does not depend on machine type version. Because we ensured the stricter\r
528 // condition (b) through PcdQ35SmramAtDefaultSmbase above, the (a)\r
529 // QEMU_CPUHP_CMD_GET_ARCH_ID command must now be available too. While we\r
530 // can't verify the presence of precisely that command, we can still verify\r
531 // (sanity-check) that the modern interface is active, at least.\r
532 //\r
533 // Consult the "Typical usecases | Detecting and enabling modern CPU hotplug\r
534 // interface" section in QEMU's "docs/specs/acpi_cpu_hotplug.txt", on the\r
535 // following.\r
536 //\r
537 QemuCpuhpWriteCpuSelector (mMmCpuIo, 0);\r
538 QemuCpuhpWriteCpuSelector (mMmCpuIo, 0);\r
539 QemuCpuhpWriteCommand (mMmCpuIo, QEMU_CPUHP_CMD_GET_PENDING);\r
540 if (QemuCpuhpReadCommandData2 (mMmCpuIo) != 0) {\r
541 Status = EFI_NOT_FOUND;\r
542 DEBUG ((DEBUG_ERROR, "%a: modern CPU hotplug interface: %r\n",\r
543 __FUNCTION__, Status));\r
bc498ac4 544 goto ReleasePostSmmPen;\r
f668e788
LE
545 }\r
546\r
17efae27
LE
547 //\r
548 // Register the handler for the CPU Hotplug MMI.\r
549 //\r
550 Status = gMmst->MmiHandlerRegister (\r
551 CpuHotplugMmi,\r
552 NULL, // HandlerType: root MMI handler\r
553 &mDispatchHandle\r
554 );\r
555 if (EFI_ERROR (Status)) {\r
556 DEBUG ((DEBUG_ERROR, "%a: MmiHandlerRegister(): %r\n", __FUNCTION__,\r
557 Status));\r
bc498ac4 558 goto ReleasePostSmmPen;\r
17efae27
LE
559 }\r
560\r
bc498ac4
LE
561 //\r
562 // Install the handler for the hot-added CPUs' first SMI.\r
563 //\r
564 SmbaseInstallFirstSmiHandler ();\r
565\r
17efae27
LE
566 return EFI_SUCCESS;\r
567\r
bc498ac4
LE
568ReleasePostSmmPen:\r
569 SmbaseReleasePostSmmPen (mPostSmmPenAddress, SystemTable->BootServices);\r
570 mPostSmmPenAddress = 0;\r
571\r
a752dd07
AA
572ReleaseToUnplugSelectors:\r
573 gMmst->MmFreePool (mToUnplugSelectors);\r
574 mToUnplugSelectors = NULL;\r
575\r
17cb8ddb
LE
576ReleaseToUnplugApicIds:\r
577 gMmst->MmFreePool (mToUnplugApicIds);\r
578 mToUnplugApicIds = NULL;\r
579\r
580ReleasePluggedApicIds:\r
581 gMmst->MmFreePool (mPluggedApicIds);\r
582 mPluggedApicIds = NULL;\r
583\r
17efae27
LE
584Fatal:\r
585 ASSERT (FALSE);\r
586 CpuDeadLoop ();\r
587 return Status;\r
588}\r