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