1 /** @file
2 This module produces the SMM COntrol2 Protocol for QNC
3 
4 Copyright (c) 2013-2015 Intel Corporation.
5 
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution.  The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10 
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include <PiDxe.h>
17 #include <Protocol/SmmControl2.h>
18 #include <IndustryStandard/Pci.h>
19 #include <Library/DebugLib.h>
20 #include <Library/UefiBootServicesTableLib.h>
21 #include <Library/UefiRuntimeServicesTableLib.h>
22 #include <Library/PcdLib.h>
23 #include <Library/IoLib.h>
24 #include <Library/PciLib.h>
25 #include <IntelQNCDxe.h>
26 #include <Library/QNCAccessLib.h>
27 #include <Uefi/UefiBaseType.h>
28 
29 #define EFI_INTERNAL_POINTER  0x00000004
30 
31 extern EFI_GUID gEfiEventVirtualAddressChangeGuid;
32 
33 /**
34   Generates an SMI using the parameters passed in.
35 
36   @param  This                A pointer to an instance of
37                               EFI_SMM_CONTROL2_PROTOCOL
38   @param  ArgumentBuffer      The argument buffer
39   @param  ArgumentBufferSize  The size of the argument buffer
40   @param  Periodic            TRUE to indicate a periodical SMI
41   @param  ActivationInterval  Interval of the periodical SMI
42 
43   @retval EFI_INVALID_PARAMETER Periodic is TRUE or ArgumentBufferSize > 1
44   @return Return value from SmmTrigger().
45 
46 **/
47 EFI_STATUS
48 EFIAPI
49 Activate (
50   IN CONST EFI_SMM_CONTROL2_PROTOCOL     *This,
51   IN OUT  UINT8                          *CommandPort       OPTIONAL,
52   IN OUT  UINT8                          *DataPort          OPTIONAL,
53   IN      BOOLEAN                        Periodic           OPTIONAL,
54   IN      EFI_SMM_PERIOD                 ActivationInterval OPTIONAL
55                   );
56 
57 /**
58   Clears an SMI.
59 
60   @param  This      Pointer to an instance of EFI_SMM_CONTROL2_PROTOCOL
61   @param  Periodic  TRUE to indicate a periodical SMI
62 
63   @return Return value from SmmClear()
64 
65 **/
66 EFI_STATUS
67 EFIAPI
68 Deactivate (
69   IN CONST     EFI_SMM_CONTROL2_PROTOCOL  *This,
70   IN      BOOLEAN                         Periodic OPTIONAL
71   );
72 
73 ///
74 /// Handle for the SMM Control2 Protocol
75 ///
76 EFI_HANDLE  mSmmControl2Handle = NULL;
77 
78 ///
79 /// SMM COntrol2 Protocol instance
80 ///
81 EFI_SMM_CONTROL2_PROTOCOL mSmmControl2 = {
82   Activate,
83   Deactivate,
84   0
85 };
86 
87 VOID
88 EFIAPI
SmmControlVirtualddressChangeEvent(IN EFI_EVENT Event,IN VOID * Context)89 SmmControlVirtualddressChangeEvent (
90   IN EFI_EVENT                  Event,
91   IN VOID                       *Context
92   )
93 /*++
94 
95 Routine Description:
96 
97   Fixup internal data pointers so that the services can be called in virtual mode.
98 
99 Arguments:
100 
101   Event                         The event registered.
102   Context                       Event context.
103 
104 Returns:
105 
106   None.
107 
108 --*/
109 {
110   gRT->ConvertPointer (EFI_INTERNAL_POINTER, (VOID *) &(mSmmControl2.Trigger));
111   gRT->ConvertPointer (EFI_INTERNAL_POINTER, (VOID *) &(mSmmControl2.Clear));
112 }
113 
114 /**
115   Clear SMI related chipset status and re-enable SMI by setting the EOS bit.
116 
117   @retval EFI_SUCCESS The requested operation has been carried out successfully
118   @retval EFI_DEVICE_ERROR  The EOS bit could not be set.
119 
120 **/
121 EFI_STATUS
SmmClear(VOID)122 SmmClear (
123   VOID
124   )
125 {
126   UINT16                       PM1BLK_Base;
127   UINT16                       GPE0BLK_Base;
128 
129   //
130   // Get PM1BLK_Base & GPE0BLK_Base
131   //
132   PM1BLK_Base  = PcdGet16 (PcdPm1blkIoBaseAddress);
133   GPE0BLK_Base = PcdGet16 (PcdGpe0blkIoBaseAddress);
134 
135   //
136   // Clear the Power Button Override Status Bit, it gates EOS from being set.
137   // In QuarkNcSocId - Bit is read only. Handled by external SMC, do nothing.
138   //
139 
140   //
141   // Clear the APM SMI Status Bit
142   //
143   IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_SMIS), B_QNC_GPE0BLK_SMIS_APM);
144 
145   //
146   // Set the EOS Bit
147   //
148   IoOr32 ((GPE0BLK_Base + R_QNC_GPE0BLK_SMIS), B_QNC_GPE0BLK_SMIS_EOS);
149 
150   return EFI_SUCCESS;
151 }
152 
153 /**
154   Generates an SMI using the parameters passed in.
155 
156   @param  This                A pointer to an instance of
157                               EFI_SMM_CONTROL_PROTOCOL
158   @param  ArgumentBuffer      The argument buffer
159   @param  ArgumentBufferSize  The size of the argument buffer
160   @param  Periodic            TRUE to indicate a periodical SMI
161   @param  ActivationInterval  Interval of the periodical SMI
162 
163   @retval EFI_INVALID_PARAMETER Periodic is TRUE or ArgumentBufferSize > 1
164   @retval EFI_SUCCESS            SMI generated
165 
166 **/
167 EFI_STATUS
168 EFIAPI
Activate(IN CONST EFI_SMM_CONTROL2_PROTOCOL * This,IN OUT UINT8 * CommandPort OPTIONAL,IN OUT UINT8 * DataPort OPTIONAL,IN BOOLEAN Periodic OPTIONAL,IN EFI_SMM_PERIOD ActivationInterval OPTIONAL)169 Activate (
170   IN CONST EFI_SMM_CONTROL2_PROTOCOL     *This,
171   IN OUT  UINT8                          *CommandPort       OPTIONAL,
172   IN OUT  UINT8                          *DataPort          OPTIONAL,
173   IN      BOOLEAN                        Periodic           OPTIONAL,
174   IN      EFI_SMM_PERIOD                 ActivationInterval OPTIONAL
175   )
176 {
177   UINT16        GPE0BLK_Base;
178   UINT32        NewValue;
179 
180   //
181   // Get GPE0BLK_Base
182   //
183   GPE0BLK_Base = PcdGet16 (PcdGpe0blkIoBaseAddress);
184 
185   if (Periodic) {
186     return EFI_INVALID_PARAMETER;
187   }
188 
189   //
190   // Clear any pending the APM SMI
191   //
192   if (EFI_ERROR (SmmClear())) {
193     return EFI_DEVICE_ERROR;
194     }
195 
196   //
197   // Enable the APMC SMI
198   //
199   IoOr32 (GPE0BLK_Base + R_QNC_GPE0BLK_SMIE, B_QNC_GPE0BLK_SMIE_APM);
200 
201   //
202   // Enable SMI globally
203   //
204   NewValue = QNCPortRead (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC);
205   NewValue |= SMI_EN;
206   QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC, NewValue);
207 
208 
209   //
210   // Set APMC_STS
211   //
212   if (DataPort == NULL) {
213     IoWrite8 (PcdGet16 (PcdSmmDataPort), 0xFF);
214   } else {
215     IoWrite8 (PcdGet16 (PcdSmmDataPort), *DataPort);
216   }
217 
218   //
219   // Generate the APMC SMI
220   //
221   if (CommandPort == NULL) {
222     IoWrite8 (PcdGet16 (PcdSmmActivationPort), 0xFF);
223   } else {
224     IoWrite8 (PcdGet16 (PcdSmmActivationPort), *CommandPort);
225   }
226 
227   return EFI_SUCCESS;
228 }
229 
230 /**
231   Clears an SMI.
232 
233   @param  This      Pointer to an instance of EFI_SMM_CONTROL_PROTOCOL
234   @param  Periodic  TRUE to indicate a periodical SMI
235 
236   @return Return value from SmmClear()
237 
238 **/
239 EFI_STATUS
240 EFIAPI
Deactivate(IN CONST EFI_SMM_CONTROL2_PROTOCOL * This,IN BOOLEAN Periodic)241 Deactivate (
242   IN CONST EFI_SMM_CONTROL2_PROTOCOL     *This,
243   IN      BOOLEAN                   Periodic
244   )
245 {
246   if (Periodic) {
247     return EFI_INVALID_PARAMETER;
248   }
249 
250   return SmmClear();
251 }
252 
253 /**
254   This is the constructor for the SMM Control protocol.
255 
256   This function installs EFI_SMM_CONTROL2_PROTOCOL.
257 
258   @param  ImageHandle Handle for the image of this driver
259   @param  SystemTable Pointer to the EFI System Table
260 
261   @retval EFI_UNSUPPORTED There's no Intel ICH on this platform
262   @return The status returned from InstallProtocolInterface().
263 
264 --*/
265 EFI_STATUS
SmmControl2Init(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)266 SmmControl2Init (
267   IN EFI_HANDLE        ImageHandle,
268   IN EFI_SYSTEM_TABLE  *SystemTable
269   )
270 {
271   EFI_STATUS  Status;
272   EFI_EVENT   Event;
273   UINT16      PM1BLK_Base;
274   UINT16      GPE0BLK_Base;
275   BOOLEAN     SciEn;
276   UINT32      NewValue;
277 
278   //
279   // Get PM1BLK_Base & GPE0BLK_Base
280   //
281   PM1BLK_Base  = PcdGet16 (PcdPm1blkIoBaseAddress);
282   GPE0BLK_Base = PcdGet16 (PcdGpe0blkIoBaseAddress);
283 
284   //
285   // Install our protocol interfaces on the device's handle
286   //
287   Status = gBS->InstallMultipleProtocolInterfaces (
288                   &mSmmControl2Handle,
289                   &gEfiSmmControl2ProtocolGuid,  &mSmmControl2,
290                   NULL
291                   );
292   ASSERT_EFI_ERROR (Status);
293 
294   //
295   // Determine whether an ACPI OS is present (via the SCI_EN bit)
296   //
297   SciEn = (BOOLEAN)((IoRead16 (PM1BLK_Base + R_QNC_PM1BLK_PM1C) & B_QNC_PM1BLK_PM1C_SCIEN) != 0);
298   if (!SciEn) {
299     //
300     // Clear any SMIs that double as SCIs (when SCI_EN==0)
301     //
302     IoWrite16 ((PM1BLK_Base + R_QNC_PM1BLK_PM1S), B_QNC_PM1BLK_PM1S_ALL);
303     IoWrite16 ((PM1BLK_Base + R_QNC_PM1BLK_PM1E), 0x00000000);
304     IoWrite32 ((PM1BLK_Base + R_QNC_PM1BLK_PM1C),  0x00000000);
305     IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_GPE0S), B_QNC_GPE0BLK_GPE0S_ALL);
306     IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_GPE0E), 0x00000000);
307   }
308 
309   //
310   // Clear and disable all SMIs that are unaffected by SCI_EN
311   // Set EOS
312   //
313   IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_SMIE), 0x00000000);
314   IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_SMIS), (B_QNC_GPE0BLK_SMIS_EOS + B_QNC_GPE0BLK_SMIS_ALL));
315 
316   //
317   // Enable SMI globally
318   //
319   NewValue = QNCPortRead (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC);
320   NewValue |= SMI_EN;
321   QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC, NewValue);
322 
323   //
324   // Make sure to write this register last -- EOS re-enables SMIs for the QNC
325   //
326   IoAndThenOr32 (
327     GPE0BLK_Base + R_QNC_GPE0BLK_SMIE,
328     (UINT32)(~B_QNC_GPE0BLK_SMIE_ALL),
329     B_QNC_GPE0BLK_SMIE_APM
330     );
331 
332   //
333   // Make sure EOS bit cleared
334   //
335   DEBUG_CODE_BEGIN ();
336   if (IoRead32 (GPE0BLK_Base + R_QNC_GPE0BLK_SMIS) & B_QNC_GPE0BLK_SMIS_EOS) {
337     DEBUG ((
338       EFI_D_ERROR,
339       "******************************************************************************\n"
340       "BIG ERROR: SmmControl constructor couldn't properly initialize the ACPI table.\n"
341       "           SmmControl->Clear will probably hang.                              \n"
342       "              NOTE: SCI_EN = %d                                               \n"
343       "******************************************************************************\n",
344       SciEn
345       ));
346 
347     //
348     // If we want the system to stop, then keep the ASSERT(FALSE).
349     // Otherwise, comment it out.
350     //
351     ASSERT (FALSE);
352   }
353   DEBUG_CODE_END ();
354 
355   Status = gBS->CreateEventEx (
356                 EVT_NOTIFY_SIGNAL,
357                 TPL_NOTIFY,
358                 SmmControlVirtualddressChangeEvent,
359                 NULL,
360                 &gEfiEventVirtualAddressChangeGuid,
361                 &Event
362                 );
363   ASSERT_EFI_ERROR (Status);
364 
365   return Status;
366 }
367