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