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