1 /** @file
2 This is the driver that publishes the SMM Control Protocol.
3
4 Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 **/
7 #include <Library/IoLib.h>
8 #include <Library/DebugLib.h>
9 #include <Library/UefiDriverEntryPoint.h>
10 #include <Library/UefiBootServicesTableLib.h>
11 #include <Library/UefiRuntimeServicesTableLib.h>
12 #include <Guid/EventGroup.h>
13 #include <Library/PmcLib.h>
14 #include <Library/GpioLib.h>
15 #include <IndustryStandard/Pci30.h>
16 #include <Register/PchRegsLpc.h>
17 #include <Register/SpiRegs.h>
18 #include <Register/PmcRegs.h>
19 #include "SmmControlDriver.h"
20
21 STATIC SMM_CONTROL_PRIVATE_DATA mSmmControl;
22 GLOBAL_REMOVE_IF_UNREFERENCED UINT16 mABase;
23
24 VOID
25 EFIAPI
26 DisablePendingSmis (
27 VOID
28 );
29
30 /**
31 Fixup internal data pointers so that the services can be called in virtual mode.
32
33 @param[in] Event The event registered.
34 @param[in] Context Event context.
35
36 **/
37 VOID
38 EFIAPI
SmmControlVirtualAddressChangeEvent(IN EFI_EVENT Event,IN VOID * Context)39 SmmControlVirtualAddressChangeEvent (
40 IN EFI_EVENT Event,
41 IN VOID *Context
42 )
43 {
44 gRT->ConvertPointer (0, (VOID *) &(mSmmControl.SmmControl.Trigger));
45 gRT->ConvertPointer (0, (VOID *) &(mSmmControl.SmmControl.Clear));
46 }
47
48 /**
49 <b>SmmControl DXE RUNTIME Module Entry Point</b>\n
50 - <b>Introduction</b>\n
51 The SmmControl module is a DXE RUNTIME driver that provides a standard way
52 for other drivers to trigger software SMIs.
53
54 - @pre
55 - PCH Power Management I/O space base address has already been programmed.
56 If SmmControl Runtime DXE driver is run before Status Code Runtime Protocol
57 is installed and there is the need to use Status code in the driver, it will
58 be necessary to add EFI_STATUS_CODE_RUNTIME_PROTOCOL_GUID to the dependency file.
59 - EFI_SMM_BASE2_PROTOCOL
60 - Documented in the System Management Mode Core Interface Specification.
61
62 - @result
63 The SmmControl driver produces the EFI_SMM_CONTROL_PROTOCOL documented in
64 System Management Mode Core Interface Specification.
65
66 @param[in] ImageHandle Handle for the image of this driver
67 @param[in] SystemTable Pointer to the EFI System Table
68
69 @retval EFI_STATUS Results of the installation of the SMM Control Protocol
70 **/
71 EFI_STATUS
72 EFIAPI
SmmControlDriverEntryInit(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)73 SmmControlDriverEntryInit (
74 IN EFI_HANDLE ImageHandle,
75 IN EFI_SYSTEM_TABLE *SystemTable
76 )
77 {
78 EFI_STATUS Status;
79 EFI_EVENT Event;
80
81 DEBUG ((DEBUG_INFO, "SmmControlDriverEntryInit() Start\n"));
82
83 //
84 // Get the Power Management I/O space base address. We assume that
85 // this base address has already been programmed if this driver is
86 // being run.
87 //
88 mABase = PmcGetAcpiBase ();
89
90 Status = EFI_SUCCESS;
91 if (mABase != 0) {
92 //
93 // Install the instance of the protocol
94 //
95 mSmmControl.Signature = SMM_CONTROL_PRIVATE_DATA_SIGNATURE;
96 mSmmControl.Handle = ImageHandle;
97
98 mSmmControl.SmmControl.Trigger = Activate;
99 mSmmControl.SmmControl.Clear = Deactivate;
100 mSmmControl.SmmControl.MinimumTriggerPeriod = 0;
101
102 //
103 // Install our protocol interfaces on the device's handle
104 //
105 Status = gBS->InstallMultipleProtocolInterfaces (
106 &mSmmControl.Handle,
107 &gEfiSmmControl2ProtocolGuid,
108 &mSmmControl.SmmControl,
109 NULL
110 );
111 } else {
112 Status = EFI_DEVICE_ERROR;
113 return Status;
114 }
115
116 Status = gBS->CreateEventEx (
117 EVT_NOTIFY_SIGNAL,
118 TPL_NOTIFY,
119 SmmControlVirtualAddressChangeEvent,
120 NULL,
121 &gEfiEventVirtualAddressChangeGuid,
122 &Event
123 );
124 //
125 // Disable any PCH SMIs that, for whatever reason, are asserted after the boot.
126 //
127 DisablePendingSmis ();
128
129 DEBUG ((DEBUG_INFO, "SmmControlDriverEntryInit() End\n"));
130
131 return Status;
132 }
133
134 /**
135 Trigger the software SMI
136
137 @param[in] Data The value to be set on the software SMI data port
138
139 @retval EFI_SUCCESS Function completes successfully
140 **/
141 EFI_STATUS
142 EFIAPI
SmmTrigger(IN UINT8 Data)143 SmmTrigger (
144 IN UINT8 Data
145 )
146 {
147 UINT32 OutputData;
148 UINT32 OutputPort;
149
150 //
151 // Enable the APMC SMI
152 //
153 OutputPort = mABase + R_ACPI_IO_SMI_EN;
154 OutputData = IoRead32 ((UINTN) OutputPort);
155 OutputData |= (B_ACPI_IO_SMI_EN_APMC | B_ACPI_IO_SMI_EN_GBL_SMI);
156 DEBUG (
157 (DEBUG_VERBOSE,
158 "The SMI Control Port at address %x will be written to %x.\n",
159 OutputPort,
160 OutputData)
161 );
162 IoWrite32 (
163 (UINTN) OutputPort,
164 (UINT32) (OutputData)
165 );
166
167 OutputPort = R_PCH_IO_APM_CNT;
168 OutputData = Data;
169
170 //
171 // Generate the APMC SMI
172 //
173 IoWrite8 (
174 (UINTN) OutputPort,
175 (UINT8) (OutputData)
176 );
177
178 return EFI_SUCCESS;
179 }
180
181 /**
182 Clear the SMI status
183
184
185 @retval EFI_SUCCESS The function completes successfully
186 @retval EFI_DEVICE_ERROR Something error occurred
187 **/
188 EFI_STATUS
189 EFIAPI
SmmClear(VOID)190 SmmClear (
191 VOID
192 )
193 {
194 EFI_STATUS Status;
195 UINT32 OutputData;
196 UINT32 OutputPort;
197
198 Status = EFI_SUCCESS;
199
200 //
201 // Clear the Power Button Override Status Bit, it gates EOS from being set.
202 //
203 OutputPort = mABase + R_ACPI_IO_PM1_STS;
204 OutputData = B_ACPI_IO_PM1_STS_PRBTNOR;
205 DEBUG (
206 (DEBUG_VERBOSE,
207 "The PM1 Status Port at address %x will be written to %x.\n",
208 OutputPort,
209 OutputData)
210 );
211 IoWrite16 (
212 (UINTN) OutputPort,
213 (UINT16) (OutputData)
214 );
215
216 //
217 // Clear the APM SMI Status Bit
218 //
219 OutputPort = mABase + R_ACPI_IO_SMI_STS;
220 OutputData = B_ACPI_IO_SMI_STS_APM;
221 DEBUG (
222 (DEBUG_VERBOSE,
223 "The SMI Status Port at address %x will be written to %x.\n",
224 OutputPort,
225 OutputData)
226 );
227 IoWrite32 (
228 (UINTN) OutputPort,
229 (UINT32) (OutputData)
230 );
231
232 //
233 // Set the EOS Bit
234 //
235 OutputPort = mABase + R_ACPI_IO_SMI_EN;
236 OutputData = IoRead32 ((UINTN) OutputPort);
237 OutputData |= B_ACPI_IO_SMI_EN_EOS;
238 DEBUG (
239 (DEBUG_VERBOSE,
240 "The SMI Control Port at address %x will be written to %x.\n",
241 OutputPort,
242 OutputData)
243 );
244 IoWrite32 (
245 (UINTN) OutputPort,
246 (UINT32) (OutputData)
247 );
248
249 //
250 // There is no need to read EOS back and check if it is set.
251 // This can lead to a reading of zero if an SMI occurs right after the SMI_EN port read
252 // but before the data is returned to the CPU.
253 // SMM Dispatcher should make sure that EOS is set after all SMI sources are processed.
254 //
255 return Status;
256 }
257
258 /**
259 This routine generates an SMI
260
261 @param[in] This The EFI SMM Control protocol instance
262 @param[in, out] CommandPort The buffer contains data to the command port
263 @param[in, out] DataPort The buffer contains data to the data port
264 @param[in] Periodic Periodic or not
265 @param[in] ActivationInterval Interval of periodic SMI
266
267 @retval EFI Status Describing the result of the operation
268 @retval EFI_INVALID_PARAMETER Some parameter value passed is not supported
269 **/
270 EFI_STATUS
271 EFIAPI
Activate(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)272 Activate (
273 IN CONST EFI_SMM_CONTROL2_PROTOCOL * This,
274 IN OUT UINT8 *CommandPort OPTIONAL,
275 IN OUT UINT8 *DataPort OPTIONAL,
276 IN BOOLEAN Periodic OPTIONAL,
277 IN UINTN ActivationInterval OPTIONAL
278 )
279 {
280 EFI_STATUS Status;
281 UINT8 Data;
282
283 if (Periodic) {
284 DEBUG ((DEBUG_WARN, "Invalid parameter\n"));
285 return EFI_INVALID_PARAMETER;
286 }
287
288 if (CommandPort == NULL) {
289 Data = 0xFF;
290 } else {
291 Data = *CommandPort;
292 }
293 //
294 // Clear any pending the APM SMI
295 //
296 Status = SmmClear ();
297 if (EFI_ERROR (Status)) {
298 return Status;
299 }
300
301 return SmmTrigger (Data);
302 }
303
304 /**
305 This routine clears an SMI
306
307 @param[in] This The EFI SMM Control protocol instance
308 @param[in] Periodic Periodic or not
309
310 @retval EFI Status Describing the result of the operation
311 @retval EFI_INVALID_PARAMETER Some parameter value passed is not supported
312 **/
313 EFI_STATUS
314 EFIAPI
Deactivate(IN CONST EFI_SMM_CONTROL2_PROTOCOL * This,IN BOOLEAN Periodic OPTIONAL)315 Deactivate (
316 IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,
317 IN BOOLEAN Periodic OPTIONAL
318 )
319 {
320 if (Periodic) {
321 return EFI_INVALID_PARAMETER;
322 }
323
324 return SmmClear ();
325 }
326 /**
327 Disable all pending SMIs
328
329 **/
330 VOID
331 EFIAPI
DisablePendingSmis(VOID)332 DisablePendingSmis (
333 VOID
334 )
335 {
336 UINT32 Data;
337 BOOLEAN SciEn;
338
339 //
340 // Determine whether an ACPI OS is present (via the SCI_EN bit)
341 //
342 Data = IoRead16 ((UINTN) mABase + R_ACPI_IO_PM1_CNT);
343 SciEn = (BOOLEAN) ((Data & B_ACPI_IO_PM1_CNT_SCI_EN) == B_ACPI_IO_PM1_CNT_SCI_EN);
344
345 if (!SciEn) {
346 //
347 // Clear any SMIs that double as SCIs (when SCI_EN==0)
348 //
349 IoWrite16 ((UINTN) mABase + R_ACPI_IO_PM1_STS, 0xFFFF);
350 IoWrite16 ((UINTN) mABase + R_ACPI_IO_PM1_EN, 0);
351 IoWrite16 ((UINTN) mABase + R_ACPI_IO_PM1_CNT, 0);
352 IoWrite32 (
353 (UINTN) mABase + R_ACPI_IO_GPE0_STS_127_96,
354 (UINT32)( B_ACPI_IO_GPE0_STS_127_96_WADT |
355 B_ACPI_IO_GPE0_STS_127_96_USB_CON_DSX_STS |
356 B_ACPI_IO_GPE0_STS_127_96_LAN_WAKE |
357 B_ACPI_IO_GPE0_STS_127_96_PME_B0 |
358 B_ACPI_IO_GPE0_STS_127_96_PME |
359 B_ACPI_IO_GPE0_STS_127_96_BATLOW |
360 B_ACPI_IO_GPE0_STS_127_96_RI |
361 B_ACPI_IO_GPE0_STS_127_96_SWGPE)
362 );
363 IoWrite32 ((UINTN) mABase + R_ACPI_IO_GPE0_EN_127_96, (UINT32) B_ACPI_IO_GPE0_EN_127_96_WADT);
364 }
365 //
366 // Clear and disable all SMIs that are unaffected by SCI_EN
367 //
368 GpioDisableAllGpiSmi ();
369
370 GpioClearAllGpiSmiSts ();
371
372 IoWrite32 ((UINTN) mABase + R_ACPI_IO_DEVACT_STS, 0x0000FFFF);
373
374 IoWrite32 ((UINTN) mABase + R_ACPI_IO_SMI_STS, ~0u);
375
376 //
377 // (Make sure to write this register last -- EOS re-enables SMIs for the PCH)
378 //
379 Data = IoRead32 ((UINTN) mABase + R_ACPI_IO_SMI_EN);
380 //
381 // clear all bits except those tied to SCI_EN
382 //
383 Data &= B_ACPI_IO_SMI_EN_BIOS_RLS;
384 //
385 // enable SMIs and specifically enable writes to APM_CNT.
386 //
387 Data |= B_ACPI_IO_SMI_EN_GBL_SMI | B_ACPI_IO_SMI_EN_APMC;
388 //
389 // NOTE: Default value of EOS is set in PCH, it will be automatically cleared Once the PCH asserts SMI# low,
390 // we don't need to do anything to clear it
391 //
392 IoWrite32 ((UINTN) mABase + R_ACPI_IO_SMI_EN, Data);
393 }
394
395