]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/CpuHotplugSmm/CpuHotplug.c
OvmfPkg/CpuHotplugSmm: add function for collecting CPUs with events
[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
9#include <IndustryStandard/Q35MchIch9.h> // ICH9_APM_CNT\r
f668e788 10#include <IndustryStandard/QemuCpuHotplug.h> // QEMU_CPUHP_CMD_GET_PENDING\r
17efae27
LE
11#include <Library/BaseLib.h> // CpuDeadLoop()\r
12#include <Library/DebugLib.h> // ASSERT()\r
13#include <Library/MmServicesTableLib.h> // gMmst\r
14#include <Library/PcdLib.h> // PcdGetBool()\r
15#include <Protocol/MmCpuIo.h> // EFI_MM_CPU_IO_PROTOCOL\r
16#include <Uefi/UefiBaseType.h> // EFI_STATUS\r
17\r
f668e788
LE
18#include "QemuCpuhp.h" // QemuCpuhpWriteCpuSelector()\r
19\r
17efae27
LE
20//\r
21// We use this protocol for accessing IO Ports.\r
22//\r
23STATIC EFI_MM_CPU_IO_PROTOCOL *mMmCpuIo;\r
24//\r
25// Represents the registration of the CPU Hotplug MMI handler.\r
26//\r
27STATIC EFI_HANDLE mDispatchHandle;\r
28\r
29\r
30/**\r
31 CPU Hotplug MMI handler function.\r
32\r
33 This is a root MMI handler.\r
34\r
35 @param[in] DispatchHandle The unique handle assigned to this handler by\r
36 EFI_MM_SYSTEM_TABLE.MmiHandlerRegister().\r
37\r
38 @param[in] Context Context passed in by\r
39 EFI_MM_SYSTEM_TABLE.MmiManage(). Due to\r
40 CpuHotplugMmi() being a root MMI handler,\r
41 Context is ASSERT()ed to be NULL.\r
42\r
43 @param[in,out] CommBuffer Ignored, due to CpuHotplugMmi() being a root\r
44 MMI handler.\r
45\r
46 @param[in,out] CommBufferSize Ignored, due to CpuHotplugMmi() being a root\r
47 MMI handler.\r
48\r
49 @retval EFI_SUCCESS The MMI was handled and the MMI\r
50 source was quiesced. When returned\r
51 by a non-root MMI handler,\r
52 EFI_SUCCESS terminates the\r
53 processing of MMI handlers in\r
54 EFI_MM_SYSTEM_TABLE.MmiManage().\r
55 For a root MMI handler (i.e., for\r
56 the present function too),\r
57 EFI_SUCCESS behaves identically to\r
58 EFI_WARN_INTERRUPT_SOURCE_QUIESCED,\r
59 as further root MMI handlers are\r
60 going to be called by\r
61 EFI_MM_SYSTEM_TABLE.MmiManage()\r
62 anyway.\r
63\r
64 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The MMI source has been quiesced,\r
65 but other handlers should still\r
66 be called.\r
67\r
68 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The MMI source is still pending,\r
69 and other handlers should still\r
70 be called.\r
71\r
72 @retval EFI_INTERRUPT_PENDING The MMI source could not be\r
73 quiesced.\r
74**/\r
75STATIC\r
76EFI_STATUS\r
77EFIAPI\r
78CpuHotplugMmi (\r
79 IN EFI_HANDLE DispatchHandle,\r
80 IN CONST VOID *Context OPTIONAL,\r
81 IN OUT VOID *CommBuffer OPTIONAL,\r
82 IN OUT UINTN *CommBufferSize OPTIONAL\r
83 )\r
84{\r
85 EFI_STATUS Status;\r
86 UINT8 ApmControl;\r
87\r
88 //\r
89 // Assert that we are entering this function due to our root MMI handler\r
90 // registration.\r
91 //\r
92 ASSERT (DispatchHandle == mDispatchHandle);\r
93 //\r
94 // When MmiManage() is invoked to process root MMI handlers, the caller (the\r
95 // MM Core) is expected to pass in a NULL Context. MmiManage() then passes\r
96 // the same NULL Context to individual handlers.\r
97 //\r
98 ASSERT (Context == NULL);\r
99 //\r
100 // Read the MMI command value from the APM Control Port, to see if this is an\r
101 // MMI we should care about.\r
102 //\r
103 Status = mMmCpuIo->Io.Read (mMmCpuIo, MM_IO_UINT8, ICH9_APM_CNT, 1,\r
104 &ApmControl);\r
105 if (EFI_ERROR (Status)) {\r
106 DEBUG ((DEBUG_ERROR, "%a: failed to read ICH9_APM_CNT: %r\n", __FUNCTION__,\r
107 Status));\r
108 //\r
109 // We couldn't even determine if the MMI was for us or not.\r
110 //\r
111 goto Fatal;\r
112 }\r
113\r
114 if (ApmControl != ICH9_APM_CNT_CPU_HOTPLUG) {\r
115 //\r
116 // The MMI is not for us.\r
117 //\r
118 return EFI_WARN_INTERRUPT_SOURCE_QUIESCED;\r
119 }\r
120\r
121 //\r
122 // We've handled this MMI.\r
123 //\r
124 return EFI_SUCCESS;\r
125\r
126Fatal:\r
127 ASSERT (FALSE);\r
128 CpuDeadLoop ();\r
129 //\r
130 // We couldn't handle this MMI.\r
131 //\r
132 return EFI_INTERRUPT_PENDING;\r
133}\r
134\r
135\r
136//\r
137// Entry point function of this driver.\r
138//\r
139EFI_STATUS\r
140EFIAPI\r
141CpuHotplugEntry (\r
142 IN EFI_HANDLE ImageHandle,\r
143 IN EFI_SYSTEM_TABLE *SystemTable\r
144 )\r
145{\r
146 EFI_STATUS Status;\r
147\r
148 //\r
149 // This module should only be included when SMM support is required.\r
150 //\r
151 ASSERT (FeaturePcdGet (PcdSmmSmramRequire));\r
152 //\r
153 // This driver depends on the dynamically detected "SMRAM at default SMBASE"\r
154 // feature.\r
155 //\r
156 if (!PcdGetBool (PcdQ35SmramAtDefaultSmbase)) {\r
157 return EFI_UNSUPPORTED;\r
158 }\r
159\r
160 //\r
161 // Errors from here on are fatal; we cannot allow the boot to proceed if we\r
162 // can't set up this driver to handle CPU hotplug.\r
163 //\r
164 // First, collect the protocols needed later. All of these protocols are\r
165 // listed in our module DEPEX.\r
166 //\r
167 Status = gMmst->MmLocateProtocol (&gEfiMmCpuIoProtocolGuid,\r
168 NULL /* Registration */, (VOID **)&mMmCpuIo);\r
169 if (EFI_ERROR (Status)) {\r
170 DEBUG ((DEBUG_ERROR, "%a: locate MmCpuIo: %r\n", __FUNCTION__, Status));\r
171 goto Fatal;\r
172 }\r
173\r
f668e788
LE
174 //\r
175 // Sanity-check the CPU hotplug interface.\r
176 //\r
177 // Both of the following features are part of QEMU 5.0, introduced primarily\r
178 // in commit range 3e08b2b9cb64..3a61c8db9d25:\r
179 //\r
180 // (a) the QEMU_CPUHP_CMD_GET_ARCH_ID command of the modern CPU hotplug\r
181 // interface,\r
182 //\r
183 // (b) the "SMRAM at default SMBASE" feature.\r
184 //\r
185 // From these, (b) is restricted to 5.0+ machine type versions, while (a)\r
186 // does not depend on machine type version. Because we ensured the stricter\r
187 // condition (b) through PcdQ35SmramAtDefaultSmbase above, the (a)\r
188 // QEMU_CPUHP_CMD_GET_ARCH_ID command must now be available too. While we\r
189 // can't verify the presence of precisely that command, we can still verify\r
190 // (sanity-check) that the modern interface is active, at least.\r
191 //\r
192 // Consult the "Typical usecases | Detecting and enabling modern CPU hotplug\r
193 // interface" section in QEMU's "docs/specs/acpi_cpu_hotplug.txt", on the\r
194 // following.\r
195 //\r
196 QemuCpuhpWriteCpuSelector (mMmCpuIo, 0);\r
197 QemuCpuhpWriteCpuSelector (mMmCpuIo, 0);\r
198 QemuCpuhpWriteCommand (mMmCpuIo, QEMU_CPUHP_CMD_GET_PENDING);\r
199 if (QemuCpuhpReadCommandData2 (mMmCpuIo) != 0) {\r
200 Status = EFI_NOT_FOUND;\r
201 DEBUG ((DEBUG_ERROR, "%a: modern CPU hotplug interface: %r\n",\r
202 __FUNCTION__, Status));\r
203 goto Fatal;\r
204 }\r
205\r
17efae27
LE
206 //\r
207 // Register the handler for the CPU Hotplug MMI.\r
208 //\r
209 Status = gMmst->MmiHandlerRegister (\r
210 CpuHotplugMmi,\r
211 NULL, // HandlerType: root MMI handler\r
212 &mDispatchHandle\r
213 );\r
214 if (EFI_ERROR (Status)) {\r
215 DEBUG ((DEBUG_ERROR, "%a: MmiHandlerRegister(): %r\n", __FUNCTION__,\r
216 Status));\r
217 goto Fatal;\r
218 }\r
219\r
220 return EFI_SUCCESS;\r
221\r
222Fatal:\r
223 ASSERT (FALSE);\r
224 CpuDeadLoop ();\r
225 return Status;\r
226}\r