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