]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/SmmControl2Dxe/SmiFeatures.c
bd0653e6b13f43ff64d3a3f03cd1df45d2394473
[mirror_edk2.git] / OvmfPkg / SmmControl2Dxe / SmiFeatures.c
1 /**@file
2 Negotiate SMI features with QEMU, and configure UefiCpuPkg/PiSmmCpuDxeSmm
3 accordingly.
4
5 Copyright (C) 2016-2017, Red Hat, Inc.
6
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8 **/
9
10 #include <Library/BaseLib.h>
11 #include <Library/DebugLib.h>
12 #include <Library/MemEncryptSevLib.h>
13 #include <Library/MemoryAllocationLib.h>
14 #include <Library/PcdLib.h>
15 #include <Library/QemuFwCfgLib.h>
16 #include <Library/QemuFwCfgS3Lib.h>
17
18 #include "SmiFeatures.h"
19
20 //
21 // The following bit value stands for "broadcast SMI" in the
22 // "etc/smi/supported-features" and "etc/smi/requested-features" fw_cfg files.
23 //
24 #define ICH9_LPC_SMI_F_BROADCAST BIT0
25 //
26 // The following bit value stands for "enable CPU hotplug, and inject an SMI
27 // with control value ICH9_APM_CNT_CPU_HOTPLUG upon hotplug", in the
28 // "etc/smi/supported-features" and "etc/smi/requested-features" fw_cfg files.
29 //
30 #define ICH9_LPC_SMI_F_CPU_HOTPLUG BIT1
31 //
32 // The following bit value stands for "enable CPU hot-unplug, and inject an SMI
33 // with control value ICH9_APM_CNT_CPU_HOTPLUG upon hot-unplug", in the
34 // "etc/smi/supported-features" and "etc/smi/requested-features" fw_cfg files.
35 //
36 #define ICH9_LPC_SMI_F_CPU_HOT_UNPLUG BIT2
37
38 //
39 // Provides a scratch buffer (allocated in EfiReservedMemoryType type memory)
40 // for the S3 boot script fragment to write to and read from.
41 //
42 #pragma pack (1)
43 typedef union {
44 UINT64 Features;
45 UINT8 FeaturesOk;
46 } SCRATCH_BUFFER;
47 #pragma pack ()
48
49 //
50 // These carry the selector keys of the "etc/smi/requested-features" and
51 // "etc/smi/features-ok" fw_cfg files from NegotiateSmiFeatures() to
52 // AppendFwCfgBootScript().
53 //
54 STATIC FIRMWARE_CONFIG_ITEM mRequestedFeaturesItem;
55 STATIC FIRMWARE_CONFIG_ITEM mFeaturesOkItem;
56
57 //
58 // Carries the negotiated SMI features from NegotiateSmiFeatures() to
59 // AppendFwCfgBootScript().
60 //
61 STATIC UINT64 mSmiFeatures;
62
63 /**
64 Negotiate SMI features with QEMU.
65
66 @retval FALSE If SMI feature negotiation is not supported by QEMU. This is
67 not an error, it just means that SaveSmiFeatures() should not
68 be called.
69
70 @retval TRUE SMI feature negotiation is supported, and it has completed
71 successfully as well. (Failure to negotiate is a fatal error
72 and the function never returns in that case.)
73 **/
74 BOOLEAN
75 NegotiateSmiFeatures (
76 VOID
77 )
78 {
79 FIRMWARE_CONFIG_ITEM SupportedFeaturesItem;
80 UINTN SupportedFeaturesSize;
81 UINTN RequestedFeaturesSize;
82 UINTN FeaturesOkSize;
83 UINT64 RequestedFeaturesMask;
84
85 //
86 // Look up the fw_cfg files used for feature negotiation. The selector keys
87 // of "etc/smi/requested-features" and "etc/smi/features-ok" are saved
88 // statically. If the files are missing, then QEMU doesn't support SMI
89 // feature negotiation.
90 //
91 if (RETURN_ERROR (QemuFwCfgFindFile ("etc/smi/supported-features",
92 &SupportedFeaturesItem, &SupportedFeaturesSize)) ||
93 RETURN_ERROR (QemuFwCfgFindFile ("etc/smi/requested-features",
94 &mRequestedFeaturesItem, &RequestedFeaturesSize)) ||
95 RETURN_ERROR (QemuFwCfgFindFile ("etc/smi/features-ok",
96 &mFeaturesOkItem, &FeaturesOkSize))) {
97 DEBUG ((DEBUG_INFO, "%a: SMI feature negotiation unavailable\n",
98 __FUNCTION__));
99 return FALSE;
100 }
101
102 //
103 // If the files are present but their sizes disagree with us, that's a fatal
104 // error (we can't trust the behavior of SMIs either way).
105 //
106 if (SupportedFeaturesSize != sizeof mSmiFeatures ||
107 RequestedFeaturesSize != sizeof mSmiFeatures ||
108 FeaturesOkSize != sizeof (UINT8)) {
109 DEBUG ((DEBUG_ERROR, "%a: size mismatch in feature negotiation\n",
110 __FUNCTION__));
111 goto FatalError;
112 }
113
114 //
115 // Get the features supported by the host.
116 //
117 QemuFwCfgSelectItem (SupportedFeaturesItem);
118 QemuFwCfgReadBytes (sizeof mSmiFeatures, &mSmiFeatures);
119
120 //
121 // We want broadcast SMI, SMI on CPU hotplug, SMI on CPU hot-unplug
122 // and nothing else.
123 //
124 RequestedFeaturesMask = ICH9_LPC_SMI_F_BROADCAST;
125 if (!MemEncryptSevIsEnabled ()) {
126 //
127 // For now, we only support hotplug with SEV disabled.
128 //
129 RequestedFeaturesMask |= ICH9_LPC_SMI_F_CPU_HOTPLUG;
130 RequestedFeaturesMask |= ICH9_LPC_SMI_F_CPU_HOT_UNPLUG;
131 }
132 mSmiFeatures &= RequestedFeaturesMask;
133 QemuFwCfgSelectItem (mRequestedFeaturesItem);
134 QemuFwCfgWriteBytes (sizeof mSmiFeatures, &mSmiFeatures);
135
136 //
137 // Invoke feature validation in QEMU. If the selection is accepted, the
138 // features will be locked down. If the selection is rejected, feature
139 // negotiation remains open; however we don't know what to do in that case,
140 // so that's a fatal error.
141 //
142 QemuFwCfgSelectItem (mFeaturesOkItem);
143 if (QemuFwCfgRead8 () != 1) {
144 DEBUG ((DEBUG_ERROR, "%a: negotiation failed for feature bitmap 0x%Lx\n",
145 __FUNCTION__, mSmiFeatures));
146 goto FatalError;
147 }
148
149 if ((mSmiFeatures & ICH9_LPC_SMI_F_BROADCAST) == 0) {
150 //
151 // If we can't get broadcast SMIs from QEMU, that's acceptable too,
152 // although not optimal.
153 //
154 DEBUG ((DEBUG_INFO, "%a: SMI broadcast unavailable\n", __FUNCTION__));
155 } else {
156 //
157 // Configure the traditional AP sync / SMI delivery mode for
158 // PiSmmCpuDxeSmm. Effectively, restore the UefiCpuPkg defaults, from which
159 // the original QEMU behavior (i.e., unicast SMI) used to differ.
160 //
161 if (RETURN_ERROR (PcdSet64S (PcdCpuSmmApSyncTimeout, 1000000)) ||
162 RETURN_ERROR (PcdSet8S (PcdCpuSmmSyncMode, 0x00))) {
163 DEBUG ((DEBUG_ERROR, "%a: PiSmmCpuDxeSmm PCD configuration failed\n",
164 __FUNCTION__));
165 goto FatalError;
166 }
167 DEBUG ((DEBUG_INFO, "%a: using SMI broadcast\n", __FUNCTION__));
168 }
169
170 if ((mSmiFeatures & ICH9_LPC_SMI_F_CPU_HOTPLUG) == 0) {
171 DEBUG ((DEBUG_INFO, "%a: CPU hotplug not negotiated\n", __FUNCTION__));
172 } else {
173 DEBUG ((DEBUG_INFO, "%a: CPU hotplug with SMI negotiated\n",
174 __FUNCTION__));
175 }
176
177 if ((mSmiFeatures & ICH9_LPC_SMI_F_CPU_HOT_UNPLUG) == 0) {
178 DEBUG ((DEBUG_INFO, "%a: CPU hot-unplug not negotiated\n", __FUNCTION__));
179 } else {
180 DEBUG ((DEBUG_INFO, "%a: CPU hot-unplug with SMI negotiated\n",
181 __FUNCTION__));
182 }
183
184 //
185 // Negotiation successful (although we may not have gotten the optimal
186 // feature set).
187 //
188 return TRUE;
189
190 FatalError:
191 ASSERT (FALSE);
192 CpuDeadLoop ();
193 //
194 // Keep the compiler happy.
195 //
196 return FALSE;
197 }
198
199 /**
200 FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION provided to QemuFwCfgS3Lib.
201 **/
202 STATIC
203 VOID
204 EFIAPI
205 AppendFwCfgBootScript (
206 IN OUT VOID *Context, OPTIONAL
207 IN OUT VOID *ExternalScratchBuffer
208 )
209 {
210 SCRATCH_BUFFER *ScratchBuffer;
211 RETURN_STATUS Status;
212
213 ScratchBuffer = ExternalScratchBuffer;
214
215 //
216 // Write the negotiated feature bitmap into "etc/smi/requested-features".
217 //
218 ScratchBuffer->Features = mSmiFeatures;
219 Status = QemuFwCfgS3ScriptWriteBytes (mRequestedFeaturesItem,
220 sizeof ScratchBuffer->Features);
221 if (RETURN_ERROR (Status)) {
222 goto FatalError;
223 }
224
225 //
226 // Read back "etc/smi/features-ok". This invokes the feature validation &
227 // lockdown. (The validation succeeded at first boot.)
228 //
229 Status = QemuFwCfgS3ScriptReadBytes (mFeaturesOkItem,
230 sizeof ScratchBuffer->FeaturesOk);
231 if (RETURN_ERROR (Status)) {
232 goto FatalError;
233 }
234
235 //
236 // If "etc/smi/features-ok" read as 1, we're good. Otherwise, hang the S3
237 // resume process.
238 //
239 Status = QemuFwCfgS3ScriptCheckValue (&ScratchBuffer->FeaturesOk,
240 sizeof ScratchBuffer->FeaturesOk, MAX_UINT8, 1);
241 if (RETURN_ERROR (Status)) {
242 goto FatalError;
243 }
244
245 DEBUG ((DEBUG_VERBOSE, "%a: SMI feature negotiation boot script saved\n",
246 __FUNCTION__));
247 return;
248
249 FatalError:
250 ASSERT (FALSE);
251 CpuDeadLoop ();
252 }
253
254
255 /**
256 Append a boot script fragment that will re-select the previously negotiated
257 SMI features during S3 resume.
258 **/
259 VOID
260 SaveSmiFeatures (
261 VOID
262 )
263 {
264 RETURN_STATUS Status;
265
266 //
267 // We are already running at TPL_CALLBACK, on the stack of
268 // OnS3SaveStateInstalled(). But that's okay, we can easily queue more
269 // notification functions while executing a notification function.
270 //
271 Status = QemuFwCfgS3CallWhenBootScriptReady (AppendFwCfgBootScript, NULL,
272 sizeof (SCRATCH_BUFFER));
273 if (RETURN_ERROR (Status)) {
274 ASSERT (FALSE);
275 CpuDeadLoop ();
276 }
277 }