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