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