]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/SmmControl2Dxe/SmiFeatures.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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 (
92 QemuFwCfgFindFile (
93 "etc/smi/supported-features",
94 &SupportedFeaturesItem,
95 &SupportedFeaturesSize
96 )
97 ) ||
98 RETURN_ERROR (
99 QemuFwCfgFindFile (
100 "etc/smi/requested-features",
101 &mRequestedFeaturesItem,
102 &RequestedFeaturesSize
103 )
104 ) ||
105 RETURN_ERROR (
106 QemuFwCfgFindFile (
107 "etc/smi/features-ok",
108 &mFeaturesOkItem,
109 &FeaturesOkSize
110 )
111 ))
112 {
113 DEBUG ((
114 DEBUG_INFO,
115 "%a: SMI feature negotiation unavailable\n",
116 __FUNCTION__
117 ));
118 return FALSE;
119 }
120
121 //
122 // If the files are present but their sizes disagree with us, that's a fatal
123 // error (we can't trust the behavior of SMIs either way).
124 //
125 if ((SupportedFeaturesSize != sizeof mSmiFeatures) ||
126 (RequestedFeaturesSize != sizeof mSmiFeatures) ||
127 (FeaturesOkSize != sizeof (UINT8)))
128 {
129 DEBUG ((
130 DEBUG_ERROR,
131 "%a: size mismatch in feature negotiation\n",
132 __FUNCTION__
133 ));
134 goto FatalError;
135 }
136
137 //
138 // Get the features supported by the host.
139 //
140 QemuFwCfgSelectItem (SupportedFeaturesItem);
141 QemuFwCfgReadBytes (sizeof mSmiFeatures, &mSmiFeatures);
142
143 //
144 // We want broadcast SMI, SMI on CPU hotplug, SMI on CPU hot-unplug
145 // and nothing else.
146 //
147 RequestedFeaturesMask = ICH9_LPC_SMI_F_BROADCAST;
148 if (!MemEncryptSevIsEnabled ()) {
149 //
150 // For now, we only support hotplug with SEV disabled.
151 //
152 RequestedFeaturesMask |= ICH9_LPC_SMI_F_CPU_HOTPLUG;
153 RequestedFeaturesMask |= ICH9_LPC_SMI_F_CPU_HOT_UNPLUG;
154 }
155
156 mSmiFeatures &= RequestedFeaturesMask;
157 QemuFwCfgSelectItem (mRequestedFeaturesItem);
158 QemuFwCfgWriteBytes (sizeof mSmiFeatures, &mSmiFeatures);
159
160 //
161 // Invoke feature validation in QEMU. If the selection is accepted, the
162 // features will be locked down. If the selection is rejected, feature
163 // negotiation remains open; however we don't know what to do in that case,
164 // so that's a fatal error.
165 //
166 QemuFwCfgSelectItem (mFeaturesOkItem);
167 if (QemuFwCfgRead8 () != 1) {
168 DEBUG ((
169 DEBUG_ERROR,
170 "%a: negotiation failed for feature bitmap 0x%Lx\n",
171 __FUNCTION__,
172 mSmiFeatures
173 ));
174 goto FatalError;
175 }
176
177 if ((mSmiFeatures & ICH9_LPC_SMI_F_BROADCAST) == 0) {
178 //
179 // If we can't get broadcast SMIs from QEMU, that's acceptable too,
180 // although not optimal.
181 //
182 DEBUG ((DEBUG_INFO, "%a: SMI broadcast unavailable\n", __FUNCTION__));
183 } else {
184 //
185 // Configure the traditional AP sync / SMI delivery mode for
186 // PiSmmCpuDxeSmm. Effectively, restore the UefiCpuPkg defaults, from which
187 // the original QEMU behavior (i.e., unicast SMI) used to differ.
188 //
189 if (RETURN_ERROR (PcdSet64S (PcdCpuSmmApSyncTimeout, 1000000)) ||
190 RETURN_ERROR (PcdSet8S (PcdCpuSmmSyncMode, 0x00)))
191 {
192 DEBUG ((
193 DEBUG_ERROR,
194 "%a: PiSmmCpuDxeSmm PCD configuration failed\n",
195 __FUNCTION__
196 ));
197 goto FatalError;
198 }
199
200 DEBUG ((DEBUG_INFO, "%a: using SMI broadcast\n", __FUNCTION__));
201 }
202
203 if ((mSmiFeatures & ICH9_LPC_SMI_F_CPU_HOTPLUG) == 0) {
204 DEBUG ((DEBUG_INFO, "%a: CPU hotplug not negotiated\n", __FUNCTION__));
205 } else {
206 DEBUG ((
207 DEBUG_INFO,
208 "%a: CPU hotplug with SMI negotiated\n",
209 __FUNCTION__
210 ));
211 }
212
213 if ((mSmiFeatures & ICH9_LPC_SMI_F_CPU_HOT_UNPLUG) == 0) {
214 DEBUG ((DEBUG_INFO, "%a: CPU hot-unplug not negotiated\n", __FUNCTION__));
215 } else {
216 DEBUG ((
217 DEBUG_INFO,
218 "%a: CPU hot-unplug with SMI negotiated\n",
219 __FUNCTION__
220 ));
221 }
222
223 //
224 // Negotiation successful (although we may not have gotten the optimal
225 // feature set).
226 //
227 return TRUE;
228
229 FatalError:
230 ASSERT (FALSE);
231 CpuDeadLoop ();
232 //
233 // Keep the compiler happy.
234 //
235 return FALSE;
236 }
237
238 /**
239 FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION provided to QemuFwCfgS3Lib.
240 **/
241 STATIC
242 VOID
243 EFIAPI
244 AppendFwCfgBootScript (
245 IN OUT VOID *Context OPTIONAL,
246 IN OUT VOID *ExternalScratchBuffer
247 )
248 {
249 SCRATCH_BUFFER *ScratchBuffer;
250 RETURN_STATUS Status;
251
252 ScratchBuffer = ExternalScratchBuffer;
253
254 //
255 // Write the negotiated feature bitmap into "etc/smi/requested-features".
256 //
257 ScratchBuffer->Features = mSmiFeatures;
258 Status = QemuFwCfgS3ScriptWriteBytes (
259 mRequestedFeaturesItem,
260 sizeof ScratchBuffer->Features
261 );
262 if (RETURN_ERROR (Status)) {
263 goto FatalError;
264 }
265
266 //
267 // Read back "etc/smi/features-ok". This invokes the feature validation &
268 // lockdown. (The validation succeeded at first boot.)
269 //
270 Status = QemuFwCfgS3ScriptReadBytes (
271 mFeaturesOkItem,
272 sizeof ScratchBuffer->FeaturesOk
273 );
274 if (RETURN_ERROR (Status)) {
275 goto FatalError;
276 }
277
278 //
279 // If "etc/smi/features-ok" read as 1, we're good. Otherwise, hang the S3
280 // resume process.
281 //
282 Status = QemuFwCfgS3ScriptCheckValue (
283 &ScratchBuffer->FeaturesOk,
284 sizeof ScratchBuffer->FeaturesOk,
285 MAX_UINT8,
286 1
287 );
288 if (RETURN_ERROR (Status)) {
289 goto FatalError;
290 }
291
292 DEBUG ((
293 DEBUG_VERBOSE,
294 "%a: SMI feature negotiation boot script saved\n",
295 __FUNCTION__
296 ));
297 return;
298
299 FatalError:
300 ASSERT (FALSE);
301 CpuDeadLoop ();
302 }
303
304 /**
305 Append a boot script fragment that will re-select the previously negotiated
306 SMI features during S3 resume.
307 **/
308 VOID
309 SaveSmiFeatures (
310 VOID
311 )
312 {
313 RETURN_STATUS Status;
314
315 //
316 // We are already running at TPL_CALLBACK, on the stack of
317 // OnS3SaveStateInstalled(). But that's okay, we can easily queue more
318 // notification functions while executing a notification function.
319 //
320 Status = QemuFwCfgS3CallWhenBootScriptReady (
321 AppendFwCfgBootScript,
322 NULL,
323 sizeof (SCRATCH_BUFFER)
324 );
325 if (RETURN_ERROR (Status)) {
326 ASSERT (FALSE);
327 CpuDeadLoop ();
328 }
329 }