TianoCore EDK2 master
Loading...
Searching...
No Matches
SmmControl2Dxe.c
Go to the documentation of this file.
1
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>
31
32#include "SmiFeatures.h"
33
34//
35// Forward declaration.
36//
38VOID
39EFIAPI
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//
51STATIC 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//
58STATIC BOOLEAN mSmiFeatureNegotiation;
59
60//
61// Event signaled when an S3SaveState protocol interface is installed.
62//
63STATIC EFI_EVENT mS3SaveStateInstalled;
64
88EFIAPI
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
135STATIC
137EFIAPI
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
167 MAX_UINTN // MinimumTriggerPeriod -- we don't support periodic SMIs
168};
169
170//
171// Entry point of this driver.
172//
174EFIAPI
175SmmControl2DxeEntryPoint (
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 __func__
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 __func__
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,
259 NULL /* Context */,
260 &mS3SaveStateInstalled
261 );
262 if (EFI_ERROR (Status)) {
263 DEBUG ((DEBUG_ERROR, "%a: CreateEvent: %r\n", __func__, 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 __func__,
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", __func__, 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 __func__,
307 Status
308 ));
309 goto ReleaseEvent;
310 }
311
312 return EFI_SUCCESS;
313
314ReleaseEvent:
315 if (mS3SaveStateInstalled != NULL) {
316 gBS->CloseEvent (mS3SaveStateInstalled);
317 }
318
319FatalError:
320 //
321 // We really don't want to continue in this case.
322 //
323 ASSERT (FALSE);
324 CpuDeadLoop ();
325 return EFI_UNSUPPORTED;
326}
327
336STATIC
337VOID
338EFIAPI
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 __func__,
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 __func__,
404 Status
405 ));
406 ASSERT (FALSE);
407 CpuDeadLoop ();
408 }
409
410 DEBUG ((DEBUG_VERBOSE, "%a: chipset boot script saved\n", __func__));
411
412 //
413 // Append a boot script fragment that re-selects the negotiated SMI features.
414 //
415 if (mSmiFeatureNegotiation) {
417 }
418
419 gBS->CloseEvent (Event);
420 mS3SaveStateInstalled = NULL;
421}
UINT64 UINTN
VOID EFIAPI CpuDeadLoop(VOID)
Definition: CpuDeadLoop.c:25
UINT8 EFIAPI IoWrite8(IN UINTN Port, IN UINT8 Value)
Definition: IoLibArmVirt.c:200
UINT32 EFIAPI IoRead32(IN UINTN Port)
Definition: IoLibArmVirt.c:275
UINT32 EFIAPI IoWrite32(IN UINTN Port, IN UINT32 Value)
Definition: IoLibArmVirt.c:300
#define NULL
Definition: Base.h:319
#define CONST
Definition: Base.h:259
#define STATIC
Definition: Base.h:264
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define DEBUG(Expression)
Definition: DebugLib.h:434
UINT32 EFIAPI PciRead32(IN UINTN Address)
Definition: PciLib.c:739
UINT16 EFIAPI PciOr16(IN UINTN Address, IN UINT16 OrData)
Definition: PciLib.c:453
#define PcdGetBool(TokenName)
Definition: PcdLib.h:401
#define FeaturePcdGet(TokenName)
Definition: PcdLib.h:50
BOOLEAN NegotiateSmiFeatures(VOID)
Definition: SmiFeatures.c:76
VOID SaveSmiFeatures(VOID)
Definition: SmiFeatures.c:311
STATIC EFI_STATUS EFIAPI SmmControl2DxeClear(IN CONST EFI_SMM_CONTROL2_PROTOCOL *This, IN BOOLEAN Periodic OPTIONAL)
STATIC EFI_STATUS EFIAPI SmmControl2DxeTrigger(IN CONST EFI_SMM_CONTROL2_PROTOCOL *This, IN OUT UINT8 *CommandPort OPTIONAL, IN OUT UINT8 *DataPort OPTIONAL, IN BOOLEAN Periodic OPTIONAL, IN UINTN ActivationInterval OPTIONAL)
STATIC VOID EFIAPI OnS3SaveStateInstalled(IN EFI_EVENT Event, IN VOID *Context)
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
VOID * EFI_EVENT
Definition: UefiBaseType.h:37
VOID * EFI_HANDLE
Definition: UefiBaseType.h:33
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
EFI_BOOT_SERVICES * gBS