]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/CpuHotplugSmm/CpuHotplug.c
OvmfPkg/CpuHotplugSmm: introduce skeleton for CPU Hotplug SMM driver
[mirror_edk2.git] / OvmfPkg / CpuHotplugSmm / CpuHotplug.c
1 /** @file
2 Root SMI handler for VCPU hotplug SMIs.
3
4 Copyright (c) 2020, Red Hat, Inc.
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7 **/
8
9 #include <IndustryStandard/Q35MchIch9.h> // ICH9_APM_CNT
10 #include <Library/BaseLib.h> // CpuDeadLoop()
11 #include <Library/DebugLib.h> // ASSERT()
12 #include <Library/MmServicesTableLib.h> // gMmst
13 #include <Library/PcdLib.h> // PcdGetBool()
14 #include <Protocol/MmCpuIo.h> // EFI_MM_CPU_IO_PROTOCOL
15 #include <Uefi/UefiBaseType.h> // EFI_STATUS
16
17 //
18 // We use this protocol for accessing IO Ports.
19 //
20 STATIC EFI_MM_CPU_IO_PROTOCOL *mMmCpuIo;
21 //
22 // Represents the registration of the CPU Hotplug MMI handler.
23 //
24 STATIC EFI_HANDLE mDispatchHandle;
25
26
27 /**
28 CPU Hotplug MMI handler function.
29
30 This is a root MMI handler.
31
32 @param[in] DispatchHandle The unique handle assigned to this handler by
33 EFI_MM_SYSTEM_TABLE.MmiHandlerRegister().
34
35 @param[in] Context Context passed in by
36 EFI_MM_SYSTEM_TABLE.MmiManage(). Due to
37 CpuHotplugMmi() being a root MMI handler,
38 Context is ASSERT()ed to be NULL.
39
40 @param[in,out] CommBuffer Ignored, due to CpuHotplugMmi() being a root
41 MMI handler.
42
43 @param[in,out] CommBufferSize Ignored, due to CpuHotplugMmi() being a root
44 MMI handler.
45
46 @retval EFI_SUCCESS The MMI was handled and the MMI
47 source was quiesced. When returned
48 by a non-root MMI handler,
49 EFI_SUCCESS terminates the
50 processing of MMI handlers in
51 EFI_MM_SYSTEM_TABLE.MmiManage().
52 For a root MMI handler (i.e., for
53 the present function too),
54 EFI_SUCCESS behaves identically to
55 EFI_WARN_INTERRUPT_SOURCE_QUIESCED,
56 as further root MMI handlers are
57 going to be called by
58 EFI_MM_SYSTEM_TABLE.MmiManage()
59 anyway.
60
61 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The MMI source has been quiesced,
62 but other handlers should still
63 be called.
64
65 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The MMI source is still pending,
66 and other handlers should still
67 be called.
68
69 @retval EFI_INTERRUPT_PENDING The MMI source could not be
70 quiesced.
71 **/
72 STATIC
73 EFI_STATUS
74 EFIAPI
75 CpuHotplugMmi (
76 IN EFI_HANDLE DispatchHandle,
77 IN CONST VOID *Context OPTIONAL,
78 IN OUT VOID *CommBuffer OPTIONAL,
79 IN OUT UINTN *CommBufferSize OPTIONAL
80 )
81 {
82 EFI_STATUS Status;
83 UINT8 ApmControl;
84
85 //
86 // Assert that we are entering this function due to our root MMI handler
87 // registration.
88 //
89 ASSERT (DispatchHandle == mDispatchHandle);
90 //
91 // When MmiManage() is invoked to process root MMI handlers, the caller (the
92 // MM Core) is expected to pass in a NULL Context. MmiManage() then passes
93 // the same NULL Context to individual handlers.
94 //
95 ASSERT (Context == NULL);
96 //
97 // Read the MMI command value from the APM Control Port, to see if this is an
98 // MMI we should care about.
99 //
100 Status = mMmCpuIo->Io.Read (mMmCpuIo, MM_IO_UINT8, ICH9_APM_CNT, 1,
101 &ApmControl);
102 if (EFI_ERROR (Status)) {
103 DEBUG ((DEBUG_ERROR, "%a: failed to read ICH9_APM_CNT: %r\n", __FUNCTION__,
104 Status));
105 //
106 // We couldn't even determine if the MMI was for us or not.
107 //
108 goto Fatal;
109 }
110
111 if (ApmControl != ICH9_APM_CNT_CPU_HOTPLUG) {
112 //
113 // The MMI is not for us.
114 //
115 return EFI_WARN_INTERRUPT_SOURCE_QUIESCED;
116 }
117
118 //
119 // We've handled this MMI.
120 //
121 return EFI_SUCCESS;
122
123 Fatal:
124 ASSERT (FALSE);
125 CpuDeadLoop ();
126 //
127 // We couldn't handle this MMI.
128 //
129 return EFI_INTERRUPT_PENDING;
130 }
131
132
133 //
134 // Entry point function of this driver.
135 //
136 EFI_STATUS
137 EFIAPI
138 CpuHotplugEntry (
139 IN EFI_HANDLE ImageHandle,
140 IN EFI_SYSTEM_TABLE *SystemTable
141 )
142 {
143 EFI_STATUS Status;
144
145 //
146 // This module should only be included when SMM support is required.
147 //
148 ASSERT (FeaturePcdGet (PcdSmmSmramRequire));
149 //
150 // This driver depends on the dynamically detected "SMRAM at default SMBASE"
151 // feature.
152 //
153 if (!PcdGetBool (PcdQ35SmramAtDefaultSmbase)) {
154 return EFI_UNSUPPORTED;
155 }
156
157 //
158 // Errors from here on are fatal; we cannot allow the boot to proceed if we
159 // can't set up this driver to handle CPU hotplug.
160 //
161 // First, collect the protocols needed later. All of these protocols are
162 // listed in our module DEPEX.
163 //
164 Status = gMmst->MmLocateProtocol (&gEfiMmCpuIoProtocolGuid,
165 NULL /* Registration */, (VOID **)&mMmCpuIo);
166 if (EFI_ERROR (Status)) {
167 DEBUG ((DEBUG_ERROR, "%a: locate MmCpuIo: %r\n", __FUNCTION__, Status));
168 goto Fatal;
169 }
170
171 //
172 // Register the handler for the CPU Hotplug MMI.
173 //
174 Status = gMmst->MmiHandlerRegister (
175 CpuHotplugMmi,
176 NULL, // HandlerType: root MMI handler
177 &mDispatchHandle
178 );
179 if (EFI_ERROR (Status)) {
180 DEBUG ((DEBUG_ERROR, "%a: MmiHandlerRegister(): %r\n", __FUNCTION__,
181 Status));
182 goto Fatal;
183 }
184
185 return EFI_SUCCESS;
186
187 Fatal:
188 ASSERT (FALSE);
189 CpuDeadLoop ();
190 return Status;
191 }