]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/SmmControl2Dxe/SmiFeatures.c
OvmfPkg/QemuFwCfg: introduce FW_CFG_IO_DMA_ADDRESS, adapt the package
[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 This program and the accompanying materials are licensed and made available
8 under the terms and conditions of the BSD License which accompanies this
9 distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
13 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14 **/
15
16 #include <Library/BaseLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/MemoryAllocationLib.h>
19 #include <Library/PcdLib.h>
20 #include <Library/QemuFwCfgLib.h>
21
22 #include "SmiFeatures.h"
23
24 //
25 // The following bit value stands for "broadcast SMI" in the
26 // "etc/smi/supported-features" and "etc/smi/requested-features" fw_cfg files.
27 //
28 #define ICH9_LPC_SMI_F_BROADCAST BIT0
29
30 //
31 // Provides a scratch buffer (allocated in EfiReservedMemoryType type memory)
32 // for the S3 boot script fragment to write to and read from. The buffer
33 // captures a combined fw_cfg item selection + write command using the DMA
34 // access method. Note that we don't trust the runtime OS to preserve the
35 // contents of the buffer, the boot script will first rewrite it.
36 //
37 #pragma pack (1)
38 typedef struct {
39 FW_CFG_DMA_ACCESS Access;
40 UINT64 Features;
41 } SCRATCH_BUFFER;
42 #pragma pack ()
43
44 //
45 // These carry the selector keys of the "etc/smi/requested-features" and
46 // "etc/smi/features-ok" fw_cfg files from NegotiateSmiFeatures() to
47 // SaveSmiFeatures().
48 //
49 STATIC FIRMWARE_CONFIG_ITEM mRequestedFeaturesItem;
50 STATIC FIRMWARE_CONFIG_ITEM mFeaturesOkItem;
51
52 //
53 // Carries the negotiated SMI features from NegotiateSmiFeatures() to
54 // SaveSmiFeatures().
55 //
56 STATIC UINT64 mSmiFeatures;
57
58 /**
59 Negotiate SMI features with QEMU.
60
61 @retval FALSE If SMI feature negotiation is not supported by QEMU. This is
62 not an error, it just means that SaveSmiFeatures() should not
63 be called.
64
65 @retval TRUE SMI feature negotiation is supported, and it has completed
66 successfully as well. (Failure to negotiate is a fatal error
67 and the function never returns in that case.)
68 **/
69 BOOLEAN
70 NegotiateSmiFeatures (
71 VOID
72 )
73 {
74 FIRMWARE_CONFIG_ITEM SupportedFeaturesItem;
75 UINTN SupportedFeaturesSize;
76 UINTN RequestedFeaturesSize;
77 UINTN FeaturesOkSize;
78
79 //
80 // Look up the fw_cfg files used for feature negotiation. The selector keys
81 // of "etc/smi/requested-features" and "etc/smi/features-ok" are saved
82 // statically. If the files are missing, then QEMU doesn't support SMI
83 // feature negotiation.
84 //
85 if (RETURN_ERROR (QemuFwCfgFindFile ("etc/smi/supported-features",
86 &SupportedFeaturesItem, &SupportedFeaturesSize)) ||
87 RETURN_ERROR (QemuFwCfgFindFile ("etc/smi/requested-features",
88 &mRequestedFeaturesItem, &RequestedFeaturesSize)) ||
89 RETURN_ERROR (QemuFwCfgFindFile ("etc/smi/features-ok",
90 &mFeaturesOkItem, &FeaturesOkSize))) {
91 DEBUG ((DEBUG_INFO, "%a: SMI feature negotiation unavailable\n",
92 __FUNCTION__));
93 return FALSE;
94 }
95
96 //
97 // If the files are present but their sizes disagree with us, that's a fatal
98 // error (we can't trust the behavior of SMIs either way).
99 //
100 if (SupportedFeaturesSize != sizeof mSmiFeatures ||
101 RequestedFeaturesSize != sizeof mSmiFeatures ||
102 FeaturesOkSize != sizeof (UINT8)) {
103 DEBUG ((DEBUG_ERROR, "%a: size mismatch in feature negotiation\n",
104 __FUNCTION__));
105 goto FatalError;
106 }
107
108 //
109 // Get the features supported by the host.
110 //
111 QemuFwCfgSelectItem (SupportedFeaturesItem);
112 QemuFwCfgReadBytes (sizeof mSmiFeatures, &mSmiFeatures);
113
114 //
115 // We want broadcast SMI and nothing else.
116 //
117 mSmiFeatures &= ICH9_LPC_SMI_F_BROADCAST;
118 QemuFwCfgSelectItem (mRequestedFeaturesItem);
119 QemuFwCfgWriteBytes (sizeof mSmiFeatures, &mSmiFeatures);
120
121 //
122 // Invoke feature validation in QEMU. If the selection is accepted, the
123 // features will be locked down. If the selection is rejected, feature
124 // negotiation remains open; however we don't know what to do in that case,
125 // so that's a fatal error.
126 //
127 QemuFwCfgSelectItem (mFeaturesOkItem);
128 if (QemuFwCfgRead8 () != 1) {
129 DEBUG ((DEBUG_ERROR, "%a: negotiation failed for feature bitmap 0x%Lx\n",
130 __FUNCTION__, mSmiFeatures));
131 goto FatalError;
132 }
133
134 if ((mSmiFeatures & ICH9_LPC_SMI_F_BROADCAST) == 0) {
135 //
136 // If we can't get broadcast SMIs from QEMU, that's acceptable too,
137 // although not optimal.
138 //
139 DEBUG ((DEBUG_INFO, "%a: SMI broadcast unavailable\n", __FUNCTION__));
140 } else {
141 //
142 // Configure the traditional AP sync / SMI delivery mode for
143 // PiSmmCpuDxeSmm. Effectively, restore the UefiCpuPkg defaults, from which
144 // the original QEMU behavior (i.e., unicast SMI) used to differ.
145 //
146 if (RETURN_ERROR (PcdSet64S (PcdCpuSmmApSyncTimeout, 1000000)) ||
147 RETURN_ERROR (PcdSet8S (PcdCpuSmmSyncMode, 0x00))) {
148 DEBUG ((DEBUG_ERROR, "%a: PiSmmCpuDxeSmm PCD configuration failed\n",
149 __FUNCTION__));
150 goto FatalError;
151 }
152 DEBUG ((DEBUG_INFO, "%a: using SMI broadcast\n", __FUNCTION__));
153 }
154
155 //
156 // Negotiation successful (although we may not have gotten the optimal
157 // feature set).
158 //
159 return TRUE;
160
161 FatalError:
162 ASSERT (FALSE);
163 CpuDeadLoop ();
164 //
165 // Keep the compiler happy.
166 //
167 return FALSE;
168 }
169
170 /**
171 Append a boot script fragment that will re-select the previously negotiated
172 SMI features during S3 resume.
173
174 @param[in] S3SaveState The EFI_S3_SAVE_STATE_PROTOCOL instance to append to
175 the S3 boot script with.
176 **/
177 VOID
178 SaveSmiFeatures (
179 IN EFI_S3_SAVE_STATE_PROTOCOL *S3SaveState
180 )
181 {
182 SCRATCH_BUFFER *ScratchBuffer;
183 EFI_STATUS Status;
184 UINT64 AccessAddress;
185 UINT32 ControlPollData;
186 UINT32 ControlPollMask;
187 UINT16 FeaturesOkItemAsUint16;
188 UINT8 FeaturesOkData;
189 UINT8 FeaturesOkMask;
190
191 ScratchBuffer = AllocateReservedPool (sizeof *ScratchBuffer);
192 if (ScratchBuffer == NULL) {
193 DEBUG ((DEBUG_ERROR, "%a: scratch buffer allocation failed\n",
194 __FUNCTION__));
195 goto FatalError;
196 }
197
198 //
199 // Populate the scratch buffer with a select + write fw_cfg DMA command that
200 // will write the negotiated feature bitmap into
201 // "etc/smi/requested-features".
202 //
203 ScratchBuffer->Access.Control = SwapBytes32 (
204 (UINT32)mRequestedFeaturesItem << 16 |
205 FW_CFG_DMA_CTL_SELECT |
206 FW_CFG_DMA_CTL_WRITE
207 );
208 ScratchBuffer->Access.Length = SwapBytes32 (
209 (UINT32)sizeof ScratchBuffer->Features);
210 ScratchBuffer->Access.Address = SwapBytes64 (
211 (UINTN)&ScratchBuffer->Features);
212 ScratchBuffer->Features = mSmiFeatures;
213
214 //
215 // Copy the scratch buffer into the boot script. When replayed, this
216 // EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE will restore the current contents of the
217 // scratch buffer, in-place.
218 //
219 Status = S3SaveState->Write (
220 S3SaveState, // This
221 EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE, // OpCode
222 EfiBootScriptWidthUint8, // Width
223 (UINT64)(UINTN)ScratchBuffer, // Address
224 sizeof *ScratchBuffer, // Count
225 (VOID*)ScratchBuffer // Buffer
226 );
227 if (EFI_ERROR (Status)) {
228 DEBUG ((DEBUG_ERROR, "%a:%d: EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE: %r\n",
229 __FUNCTION__, __LINE__, Status));
230 goto FatalError;
231 }
232
233 //
234 // Append an opcode that will write the address of the scratch buffer to the
235 // fw_cfg DMA address register, which consists of two 32-bit IO ports. The
236 // second (highest address, least significant) write will start the transfer.
237 //
238 AccessAddress = SwapBytes64 ((UINTN)&ScratchBuffer->Access);
239 Status = S3SaveState->Write (
240 S3SaveState, // This
241 EFI_BOOT_SCRIPT_IO_WRITE_OPCODE, // OpCode
242 EfiBootScriptWidthUint32, // Width
243 (UINT64)FW_CFG_IO_DMA_ADDRESS, // Address
244 (UINTN)2, // Count
245 &AccessAddress // Buffer
246 );
247 if (EFI_ERROR (Status)) {
248 DEBUG ((DEBUG_ERROR, "%a:%d: EFI_BOOT_SCRIPT_IO_WRITE_OPCODE: %r\n",
249 __FUNCTION__, __LINE__, Status));
250 goto FatalError;
251 }
252
253 //
254 // The EFI_BOOT_SCRIPT_MEM_POLL_OPCODE will wait until the Control word reads
255 // as zero (transfer complete). As timeout we use MAX_UINT64 * 100ns, which
256 // is approximately 58494 years.
257 //
258 ControlPollData = 0;
259 ControlPollMask = MAX_UINT32;
260 Status = S3SaveState->Write (
261 S3SaveState, // This
262 EFI_BOOT_SCRIPT_MEM_POLL_OPCODE, // OpCode
263 EfiBootScriptWidthUint32, // Width
264 (UINT64)(UINTN)&ScratchBuffer->Access.Control, // Address
265 &ControlPollData, // Data
266 &ControlPollMask, // DataMask
267 MAX_UINT64 // Delay
268 );
269 if (EFI_ERROR (Status)) {
270 DEBUG ((DEBUG_ERROR, "%a:%d: EFI_BOOT_SCRIPT_MEM_POLL_OPCODE: %r\n",
271 __FUNCTION__, __LINE__, Status));
272 goto FatalError;
273 }
274
275 //
276 // Select the "etc/smi/features-ok" fw_cfg file, which invokes the feature
277 // validation & lockdown. (The validation succeeded at first boot.)
278 //
279 FeaturesOkItemAsUint16 = (UINT16)mFeaturesOkItem;
280 Status = S3SaveState->Write (
281 S3SaveState, // This
282 EFI_BOOT_SCRIPT_IO_WRITE_OPCODE, // OpCode
283 EfiBootScriptWidthUint16, // Width
284 (UINT64)FW_CFG_IO_SELECTOR, // Address
285 (UINTN)1, // Count
286 &FeaturesOkItemAsUint16 // Buffer
287 );
288 if (EFI_ERROR (Status)) {
289 DEBUG ((DEBUG_ERROR, "%a:%d: EFI_BOOT_SCRIPT_IO_WRITE_OPCODE: %r\n",
290 __FUNCTION__, __LINE__, Status));
291 goto FatalError;
292 }
293
294 //
295 // Read the contents (one byte) of "etc/smi/features-ok". If the value is
296 // one, we're good. Otherwise, continue reading the data port: QEMU returns 0
297 // past the end of the fw_cfg item, so this will hang the resume process,
298 // which matches our intent.
299 //
300 FeaturesOkData = 1;
301 FeaturesOkMask = MAX_UINT8;
302 Status = S3SaveState->Write (
303 S3SaveState, // This
304 EFI_BOOT_SCRIPT_IO_POLL_OPCODE, // OpCode
305 EfiBootScriptWidthUint8, // Width
306 (UINT64)(UINTN)FW_CFG_IO_DATA, // Address
307 &FeaturesOkData, // Data
308 &FeaturesOkMask, // DataMask
309 MAX_UINT64 // Delay
310 );
311 if (EFI_ERROR (Status)) {
312 DEBUG ((DEBUG_ERROR, "%a:%d: EFI_BOOT_SCRIPT_IO_POLL_OPCODE: %r\n",
313 __FUNCTION__, __LINE__, Status));
314 goto FatalError;
315 }
316
317 DEBUG ((DEBUG_VERBOSE, "%a: ScratchBuffer@%p\n", __FUNCTION__,
318 (VOID *)ScratchBuffer));
319 return;
320
321 FatalError:
322 ASSERT (FALSE);
323 CpuDeadLoop ();
324 }