]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.c
BaseTools/Capsule: Do not support -o with --dump-info
[mirror_edk2.git] / OvmfPkg / SmmControl2Dxe / SmmControl2Dxe.c
CommitLineData
c40e1a0c
LE
1/** @file\r
2\r
3 A DXE_RUNTIME_DRIVER providing synchronous SMI activations via the\r
4 EFI_SMM_CONTROL2_PROTOCOL.\r
5\r
6 We expect the PEI phase to have covered the following:\r
7 - ensure that the underlying QEMU machine type be Q35\r
8 (responsible: OvmfPkg/SmmAccess/SmmAccessPei.inf)\r
9 - ensure that the ACPI PM IO space be configured\r
10 (responsible: OvmfPkg/PlatformPei/PlatformPei.inf)\r
11\r
12 Our own entry point is responsible for confirming the SMI feature and for\r
13 configuring it.\r
14\r
15 Copyright (C) 2013, 2015, Red Hat, Inc.<BR>\r
16 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
17\r
18 This program and the accompanying materials are licensed and made available\r
19 under the terms and conditions of the BSD License which accompanies this\r
20 distribution. The full text of the license may be found at\r
21 http://opensource.org/licenses/bsd-license.php\r
22\r
23 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
24 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
25\r
26**/\r
27\r
28#include <IndustryStandard/Q35MchIch9.h>\r
29#include <Library/BaseLib.h>\r
30#include <Library/DebugLib.h>\r
31#include <Library/IoLib.h>\r
32#include <Library/PcdLib.h>\r
33#include <Library/PciLib.h>\r
34#include <Library/QemuFwCfgLib.h>\r
687f7521 35#include <Library/QemuFwCfgS3Lib.h>\r
c40e1a0c
LE
36#include <Library/UefiBootServicesTableLib.h>\r
37#include <Protocol/S3SaveState.h>\r
38#include <Protocol/SmmControl2.h>\r
39\r
a316d7ac
LE
40#include "SmiFeatures.h"\r
41\r
c40e1a0c
LE
42//\r
43// Forward declaration.\r
44//\r
45STATIC\r
46VOID\r
47EFIAPI\r
48OnS3SaveStateInstalled (\r
49 IN EFI_EVENT Event,\r
50 IN VOID *Context\r
51 );\r
52\r
53//\r
54// The absolute IO port address of the SMI Control and Enable Register. It is\r
55// only used to carry information from the entry point function to the\r
56// S3SaveState protocol installation callback, strictly before the runtime\r
57// phase.\r
58//\r
59STATIC UINTN mSmiEnable;\r
60\r
a316d7ac
LE
61//\r
62// Captures whether SMI feature negotiation is supported. The variable is only\r
63// used to carry this information from the entry point function to the\r
64// S3SaveState protocol installation callback.\r
65//\r
66STATIC BOOLEAN mSmiFeatureNegotiation;\r
67\r
c40e1a0c
LE
68//\r
69// Event signaled when an S3SaveState protocol interface is installed.\r
70//\r
71STATIC EFI_EVENT mS3SaveStateInstalled;\r
72\r
73/**\r
74 Invokes SMI activation from either the preboot or runtime environment.\r
75\r
76 This function generates an SMI.\r
77\r
78 @param[in] This The EFI_SMM_CONTROL2_PROTOCOL instance.\r
79 @param[in,out] CommandPort The value written to the command port.\r
80 @param[in,out] DataPort The value written to the data port.\r
81 @param[in] Periodic Optional mechanism to engender a periodic\r
82 stream.\r
83 @param[in] ActivationInterval Optional parameter to repeat at this\r
84 period one time or, if the Periodic\r
85 Boolean is set, periodically.\r
86\r
87 @retval EFI_SUCCESS The SMI/PMI has been engendered.\r
88 @retval EFI_DEVICE_ERROR The timing is unsupported.\r
89 @retval EFI_INVALID_PARAMETER The activation period is unsupported.\r
90 @retval EFI_INVALID_PARAMETER The last periodic activation has not been\r
91 cleared.\r
92 @retval EFI_NOT_STARTED The SMM base service has not been initialized.\r
93**/\r
94STATIC\r
95EFI_STATUS\r
96EFIAPI\r
97SmmControl2DxeTrigger (\r
98 IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,\r
99 IN OUT UINT8 *CommandPort OPTIONAL,\r
100 IN OUT UINT8 *DataPort OPTIONAL,\r
101 IN BOOLEAN Periodic OPTIONAL,\r
102 IN UINTN ActivationInterval OPTIONAL\r
103 )\r
104{\r
105 //\r
106 // No support for queued or periodic activation.\r
107 //\r
108 if (Periodic || ActivationInterval > 0) {\r
109 return EFI_DEVICE_ERROR;\r
110 }\r
111\r
112 //\r
113 // The so-called "Advanced Power Management Status Port Register" is in fact\r
114 // a generic data passing register, between the caller and the SMI\r
115 // dispatcher. The ICH9 spec calls it "scratchpad register" -- calling it\r
116 // "status" elsewhere seems quite the misnomer. Status registers usually\r
117 // report about hardware status, while this register is fully governed by\r
118 // software.\r
119 //\r
120 // Write to the status register first, as this won't trigger the SMI just\r
121 // yet. Then write to the control register.\r
122 //\r
123 IoWrite8 (ICH9_APM_STS, DataPort == NULL ? 0 : *DataPort);\r
124 IoWrite8 (ICH9_APM_CNT, CommandPort == NULL ? 0 : *CommandPort);\r
125 return EFI_SUCCESS;\r
126}\r
127\r
128/**\r
129 Clears any system state that was created in response to the Trigger() call.\r
130\r
131 This function acknowledges and causes the deassertion of the SMI activation\r
132 source.\r
133\r
134 @param[in] This The EFI_SMM_CONTROL2_PROTOCOL instance.\r
135 @param[in] Periodic Optional parameter to repeat at this period\r
136 one time\r
137\r
138 @retval EFI_SUCCESS The SMI/PMI has been engendered.\r
139 @retval EFI_DEVICE_ERROR The source could not be cleared.\r
140 @retval EFI_INVALID_PARAMETER The service did not support the Periodic input\r
141 argument.\r
142**/\r
143STATIC\r
144EFI_STATUS\r
145EFIAPI\r
146SmmControl2DxeClear (\r
147 IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,\r
148 IN BOOLEAN Periodic OPTIONAL\r
149 )\r
150{\r
151 if (Periodic) {\r
152 return EFI_INVALID_PARAMETER;\r
153 }\r
154\r
155 //\r
156 // The PI spec v1.4 explains that Clear() is only supposed to clear software\r
157 // status; it is not in fact responsible for deasserting the SMI. It gives\r
158 // two reasons for this: (a) many boards clear the SMI automatically when\r
159 // entering SMM, (b) if Clear() actually deasserted the SMI, then it could\r
160 // incorrectly suppress an SMI that was asynchronously asserted between the\r
161 // last return of the SMI handler and the call made to Clear().\r
162 //\r
163 // In fact QEMU automatically deasserts CPU_INTERRUPT_SMI in:\r
164 // - x86_cpu_exec_interrupt() [target-i386/seg_helper.c], and\r
165 // - kvm_arch_pre_run() [target-i386/kvm.c].\r
166 //\r
167 // So, nothing to do here.\r
168 //\r
169 return EFI_SUCCESS;\r
170}\r
171\r
172STATIC EFI_SMM_CONTROL2_PROTOCOL mControl2 = {\r
173 &SmmControl2DxeTrigger,\r
174 &SmmControl2DxeClear,\r
175 MAX_UINTN // MinimumTriggerPeriod -- we don't support periodic SMIs\r
176};\r
177\r
178//\r
179// Entry point of this driver.\r
180//\r
181EFI_STATUS\r
182EFIAPI\r
183SmmControl2DxeEntryPoint (\r
184 IN EFI_HANDLE ImageHandle,\r
185 IN EFI_SYSTEM_TABLE *SystemTable\r
186 )\r
187{\r
188 UINT32 PmBase;\r
189 UINT32 SmiEnableVal;\r
190 EFI_STATUS Status;\r
191\r
192 //\r
193 // This module should only be included if SMRAM support is required.\r
194 //\r
195 ASSERT (FeaturePcdGet (PcdSmmSmramRequire));\r
196\r
197 //\r
198 // Calculate the absolute IO port address of the SMI Control and Enable\r
199 // Register. (As noted at the top, the PEI phase has left us with a working\r
200 // ACPI PM IO space.)\r
201 //\r
202 PmBase = PciRead32 (POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE)) &\r
203 ICH9_PMBASE_MASK;\r
204 mSmiEnable = PmBase + ICH9_PMBASE_OFS_SMI_EN;\r
205\r
206 //\r
207 // If APMC_EN is pre-set in SMI_EN, that's QEMU's way to tell us that SMI\r
208 // support is not available. (For example due to KVM lacking it.) Otherwise,\r
209 // this bit is clear after each reset.\r
210 //\r
211 SmiEnableVal = IoRead32 (mSmiEnable);\r
212 if ((SmiEnableVal & ICH9_SMI_EN_APMC_EN) != 0) {\r
213 DEBUG ((EFI_D_ERROR, "%a: this Q35 implementation lacks SMI\n",\r
214 __FUNCTION__));\r
215 goto FatalError;\r
216 }\r
217\r
218 //\r
219 // Otherwise, configure the board to inject an SMI when ICH9_APM_CNT is\r
220 // written to. (See the Trigger() method above.)\r
221 //\r
222 SmiEnableVal |= ICH9_SMI_EN_APMC_EN | ICH9_SMI_EN_GBL_SMI_EN;\r
223 IoWrite32 (mSmiEnable, SmiEnableVal);\r
224\r
225 //\r
226 // Prevent software from undoing the above (until platform reset).\r
227 //\r
228 PciOr16 (POWER_MGMT_REGISTER_Q35 (ICH9_GEN_PMCON_1),\r
229 ICH9_GEN_PMCON_1_SMI_LOCK);\r
230\r
231 //\r
232 // If we can clear GBL_SMI_EN now, that means QEMU's SMI support is not\r
233 // appropriate.\r
234 //\r
235 IoWrite32 (mSmiEnable, SmiEnableVal & ~(UINT32)ICH9_SMI_EN_GBL_SMI_EN);\r
236 if (IoRead32 (mSmiEnable) != SmiEnableVal) {\r
237 DEBUG ((EFI_D_ERROR, "%a: failed to lock down GBL_SMI_EN\n",\r
238 __FUNCTION__));\r
239 goto FatalError;\r
240 }\r
241\r
a316d7ac
LE
242 //\r
243 // QEMU can inject SMIs in different ways, negotiate our preferences.\r
244 //\r
245 mSmiFeatureNegotiation = NegotiateSmiFeatures ();\r
246\r
c40e1a0c
LE
247 if (QemuFwCfgS3Enabled ()) {\r
248 VOID *Registration;\r
249\r
250 //\r
251 // On S3 resume the above register settings have to be repeated. Register a\r
252 // protocol notify callback that, when boot script saving becomes\r
253 // available, saves operations equivalent to the above to the boot script.\r
254 //\r
255 Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK,\r
256 OnS3SaveStateInstalled, NULL /* Context */,\r
257 &mS3SaveStateInstalled);\r
258 if (EFI_ERROR (Status)) {\r
259 DEBUG ((EFI_D_ERROR, "%a: CreateEvent: %r\n", __FUNCTION__, Status));\r
260 goto FatalError;\r
261 }\r
262\r
263 Status = gBS->RegisterProtocolNotify (&gEfiS3SaveStateProtocolGuid,\r
264 mS3SaveStateInstalled, &Registration);\r
265 if (EFI_ERROR (Status)) {\r
266 DEBUG ((EFI_D_ERROR, "%a: RegisterProtocolNotify: %r\n", __FUNCTION__,\r
267 Status));\r
268 goto ReleaseEvent;\r
269 }\r
270\r
271 //\r
272 // Kick the event right now -- maybe the boot script is already saveable.\r
273 //\r
274 Status = gBS->SignalEvent (mS3SaveStateInstalled);\r
275 if (EFI_ERROR (Status)) {\r
276 DEBUG ((EFI_D_ERROR, "%a: SignalEvent: %r\n", __FUNCTION__, Status));\r
277 goto ReleaseEvent;\r
278 }\r
279 }\r
280\r
281 //\r
282 // We have no pointers to convert to virtual addresses. The handle itself\r
283 // doesn't matter, as protocol services are not accessible at runtime.\r
284 //\r
285 Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle,\r
286 &gEfiSmmControl2ProtocolGuid, &mControl2,\r
287 NULL);\r
288 if (EFI_ERROR (Status)) {\r
289 DEBUG ((EFI_D_ERROR, "%a: InstallMultipleProtocolInterfaces: %r\n",\r
290 __FUNCTION__, Status));\r
291 goto ReleaseEvent;\r
292 }\r
293\r
294 return EFI_SUCCESS;\r
295\r
296ReleaseEvent:\r
297 if (mS3SaveStateInstalled != NULL) {\r
298 gBS->CloseEvent (mS3SaveStateInstalled);\r
299 }\r
300\r
301FatalError:\r
302 //\r
303 // We really don't want to continue in this case.\r
304 //\r
305 ASSERT (FALSE);\r
306 CpuDeadLoop ();\r
307 return EFI_UNSUPPORTED;\r
308}\r
309\r
310/**\r
311 Notification callback for S3SaveState installation.\r
312\r
313 @param[in] Event Event whose notification function is being invoked.\r
314\r
315 @param[in] Context The pointer to the notification function's context, which\r
316 is implementation-dependent.\r
317**/\r
318STATIC\r
319VOID\r
320EFIAPI\r
321OnS3SaveStateInstalled (\r
322 IN EFI_EVENT Event,\r
323 IN VOID *Context\r
324 )\r
325{\r
326 EFI_STATUS Status;\r
327 EFI_S3_SAVE_STATE_PROTOCOL *S3SaveState;\r
328 UINT32 SmiEnOrMask, SmiEnAndMask;\r
7ecfa0aa 329 UINT64 GenPmCon1Address;\r
c40e1a0c
LE
330 UINT16 GenPmCon1OrMask, GenPmCon1AndMask;\r
331\r
332 ASSERT (Event == mS3SaveStateInstalled);\r
333\r
334 Status = gBS->LocateProtocol (&gEfiS3SaveStateProtocolGuid,\r
335 NULL /* Registration */, (VOID **)&S3SaveState);\r
336 if (EFI_ERROR (Status)) {\r
337 return;\r
338 }\r
339\r
340 //\r
341 // These operations were originally done, verified and explained in the entry\r
342 // point function of the driver.\r
343 //\r
344 SmiEnOrMask = ICH9_SMI_EN_APMC_EN | ICH9_SMI_EN_GBL_SMI_EN;\r
345 SmiEnAndMask = MAX_UINT32;\r
346 Status = S3SaveState->Write (\r
347 S3SaveState,\r
348 EFI_BOOT_SCRIPT_IO_READ_WRITE_OPCODE,\r
349 EfiBootScriptWidthUint32,\r
350 (UINT64)mSmiEnable,\r
351 &SmiEnOrMask,\r
352 &SmiEnAndMask\r
353 );\r
354 if (EFI_ERROR (Status)) {\r
355 DEBUG ((EFI_D_ERROR, "%a: EFI_BOOT_SCRIPT_IO_READ_WRITE_OPCODE: %r\n",\r
356 __FUNCTION__, Status));\r
357 ASSERT (FALSE);\r
358 CpuDeadLoop ();\r
359 }\r
360\r
7ecfa0aa
LE
361 GenPmCon1Address = POWER_MGMT_REGISTER_Q35_EFI_PCI_ADDRESS (\r
362 ICH9_GEN_PMCON_1);\r
c40e1a0c
LE
363 GenPmCon1OrMask = ICH9_GEN_PMCON_1_SMI_LOCK;\r
364 GenPmCon1AndMask = MAX_UINT16;\r
365 Status = S3SaveState->Write (\r
366 S3SaveState,\r
367 EFI_BOOT_SCRIPT_PCI_CONFIG_READ_WRITE_OPCODE,\r
368 EfiBootScriptWidthUint16,\r
7ecfa0aa 369 GenPmCon1Address,\r
c40e1a0c
LE
370 &GenPmCon1OrMask,\r
371 &GenPmCon1AndMask\r
372 );\r
373 if (EFI_ERROR (Status)) {\r
374 DEBUG ((EFI_D_ERROR,\r
375 "%a: EFI_BOOT_SCRIPT_PCI_CONFIG_READ_WRITE_OPCODE: %r\n", __FUNCTION__,\r
376 Status));\r
377 ASSERT (FALSE);\r
378 CpuDeadLoop ();\r
379 }\r
380\r
36a6aa6c
LE
381 DEBUG ((DEBUG_VERBOSE, "%a: chipset boot script saved\n", __FUNCTION__));\r
382\r
a316d7ac
LE
383 //\r
384 // Append a boot script fragment that re-selects the negotiated SMI features.\r
385 //\r
386 if (mSmiFeatureNegotiation) {\r
36a6aa6c 387 SaveSmiFeatures ();\r
a316d7ac
LE
388 }\r
389\r
c40e1a0c
LE
390 gBS->CloseEvent (Event);\r
391 mS3SaveStateInstalled = NULL;\r
392}\r