1 /** @file
2   File to contain all the hardware specific stuff for the Smm Gpi dispatch protocol.
3 
4   Copyright (c) 2019 Intel Corporation. All rights reserved. <BR>
5 
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 **/
8 
9 #include "PchSmm.h"
10 #include "PchSmmHelpers.h"
11 #include <Library/SmiHandlerProfileLib.h>
12 #include <Register/PchRegsGpio.h>
13 #include <Register/PchRegsPmc.h>
14 
15 //
16 // Structure for GPI SMI is a template which needs to have
17 // GPI Smi bit offset and Smi Status & Enable registers updated (accordingly
18 // to choosen group and pad number) after adding it to SMM Callback database
19 //
20 
21 GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mPchGpiSourceDescTemplate = {
22   PCH_SMM_NO_FLAGS,
23   {
24     NULL_BIT_DESC_INITIALIZER,
25     NULL_BIT_DESC_INITIALIZER
26   },
27   {
28     {
29       {
30         GPIO_ADDR_TYPE, {0x0}
31       },
32       S_GPIO_PCR_GP_SMI_STS, 0x0,
33     }
34   },
35   {
36     {
37       ACPI_ADDR_TYPE,
38       {R_ACPI_IO_SMI_STS}
39     },
40     S_ACPI_IO_SMI_STS,
41     N_ACPI_IO_SMI_STS_GPIO_SMI
42   }
43 };
44 
45 /**
46   The register function used to register SMI handler of GPI SMI event.
47 
48   @param[in]  This               Pointer to the EFI_SMM_GPI_DISPATCH2_PROTOCOL instance.
49   @param[in]  DispatchFunction   Function to register for handler when the specified GPI causes an SMI.
50   @param[in]  RegisterContext    Pointer to the dispatch function's context.
51                                  The caller fills this context in before calling
52                                  the register function to indicate to the register
53                                  function the GPI(s) for which the dispatch function
54                                  should be invoked.
55   @param[out] DispatchHandle     Handle generated by the dispatcher to track the
56                                  function instance.
57 
58   @retval EFI_SUCCESS            The dispatch function has been successfully
59                                  registered and the SMI source has been enabled.
60   @retval EFI_ACCESS_DENIED      Register is not allowed
61   @retval EFI_INVALID_PARAMETER  RegisterContext is invalid. The GPI input value
62                                  is not within valid range.
63   @retval EFI_OUT_OF_RESOURCES   There is not enough memory (system or SMM) to manage this child.
64 **/
65 EFI_STATUS
66 EFIAPI
PchGpiSmiRegister(IN CONST EFI_SMM_GPI_DISPATCH2_PROTOCOL * This,IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction,IN CONST EFI_SMM_GPI_REGISTER_CONTEXT * RegisterContext,OUT EFI_HANDLE * DispatchHandle)67 PchGpiSmiRegister (
68   IN CONST EFI_SMM_GPI_DISPATCH2_PROTOCOL  *This,
69   IN       EFI_SMM_HANDLER_ENTRY_POINT2    DispatchFunction,
70   IN CONST EFI_SMM_GPI_REGISTER_CONTEXT    *RegisterContext,
71   OUT      EFI_HANDLE                      *DispatchHandle
72   )
73 {
74   EFI_STATUS                  Status;
75   DATABASE_RECORD             Record;
76   GPIO_PAD                    GpioPad;
77   UINT8                       GpiSmiBitOffset;
78   UINT32                      GpiHostSwOwnRegAddress;
79   UINT32                      GpiSmiStsRegAddress;
80   UINT32                      Data32Or;
81   UINT32                      Data32And;
82 
83   //
84   // Return access denied if the SmmReadyToLock event has been triggered
85   //
86   if (mReadyToLock == TRUE) {
87     DEBUG ((DEBUG_ERROR, "Register is not allowed if the EndOfDxe event has been triggered! \n"));
88     return EFI_ACCESS_DENIED;
89   }
90 
91   Status = GpioGetPadAndSmiRegs (
92              (UINT32) RegisterContext->GpiNum,
93              &GpioPad,
94              &GpiSmiBitOffset,
95              &GpiHostSwOwnRegAddress,
96              &GpiSmiStsRegAddress
97              );
98 
99   if (EFI_ERROR (Status)) {
100     return Status;
101   }
102 
103   ZeroMem (&Record, sizeof (DATABASE_RECORD));
104 
105   //
106   // Gather information about the registration request
107   //
108   Record.Callback          = DispatchFunction;
109   Record.ChildContext.Gpi  = *RegisterContext;
110   Record.ProtocolType      = GpiType;
111   Record.Signature         = DATABASE_RECORD_SIGNATURE;
112 
113   CopyMem (&Record.SrcDesc, &mPchGpiSourceDescTemplate, sizeof (PCH_SMM_SOURCE_DESC) );
114 
115   Record.SrcDesc.Sts[0].Reg.Data.raw = GpiSmiStsRegAddress;  // GPI SMI Status register
116   Record.SrcDesc.Sts[0].Bit = GpiSmiBitOffset;               // Bit position for selected pad
117 
118   //
119   // Insert GpiSmi handler to PchSmmCore database
120   //
121   *DispatchHandle = NULL;
122 
123   Status = SmmCoreInsertRecord (
124              &Record,
125              DispatchHandle
126              );
127   ASSERT_EFI_ERROR (Status);
128 
129   SmiHandlerProfileRegisterHandler (&gEfiSmmGpiDispatch2ProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2) DispatchFunction, (UINTN)RETURN_ADDRESS (0), (void *)RegisterContext, sizeof(*RegisterContext));
130 
131   //
132   // Enable GPI SMI
133   // HOSTSW_OWN with respect to generating GPI SMI has negative logic:
134   //  - 0 (ACPI mode) - GPIO pad will be capable of generating SMI/NMI/SCI
135   //  - 1 (GPIO mode) - GPIO pad will not generate SMI/NMI/SCI
136   //
137   Data32And  = (UINT32)~(1u << GpiSmiBitOffset);
138   MmioAnd32 (GpiHostSwOwnRegAddress, Data32And);
139 
140   //
141   // Add HOSTSW_OWN programming into S3 boot script
142   //
143   Data32Or = 0;
144   S3BootScriptSaveMemReadWrite (S3BootScriptWidthUint32, GpiHostSwOwnRegAddress, &Data32Or, &Data32And);
145 
146   return EFI_SUCCESS;
147 }
148 
149 /**
150   Unregister a GPI SMI source dispatch function with a parent SMM driver
151 
152   @param[in] This                 Pointer to the EFI_SMM_GPI_DISPATCH2_PROTOCOL instance.
153   @param[in] DispatchHandle       Handle of dispatch function to deregister.
154 
155   @retval EFI_SUCCESS             The dispatch function has been successfully
156                                   unregistered and the SMI source has been disabled
157                                   if there are no other registered child dispatch
158                                   functions for this SMI source.
159   @retval EFI_INVALID_PARAMETER   Handle is invalid.
160 **/
161 EFI_STATUS
162 EFIAPI
PchGpiSmiUnRegister(IN CONST EFI_SMM_GPI_DISPATCH2_PROTOCOL * This,IN EFI_HANDLE DispatchHandle)163 PchGpiSmiUnRegister (
164   IN CONST EFI_SMM_GPI_DISPATCH2_PROTOCOL  *This,
165   IN       EFI_HANDLE                      DispatchHandle
166   )
167 {
168   EFI_STATUS      Status;
169   DATABASE_RECORD *RecordToDelete;
170   DATABASE_RECORD *RecordInDb;
171   LIST_ENTRY      *LinkInDb;
172   GPIO_PAD        GpioPad;
173   UINT8           GpiSmiBitOffset;
174   UINT32          GpiHostSwOwnRegAddress;
175   UINT32          GpiSmiStsRegAddress;
176   UINT32          Data32Or;
177   UINT32          Data32And;
178   BOOLEAN         DisableGpiSmiSource;
179 
180 
181   if (DispatchHandle == NULL) {
182     return EFI_INVALID_PARAMETER;
183   }
184 
185   RecordToDelete = DATABASE_RECORD_FROM_LINK (DispatchHandle);
186   if ((RecordToDelete->Signature != DATABASE_RECORD_SIGNATURE) ||
187       (RecordToDelete->ProtocolType != GpiType)) {
188     return EFI_INVALID_PARAMETER;
189   }
190 
191   //
192   // Return access denied if the SmmReadyToLock event has been triggered
193   //
194   if (mReadyToLock == TRUE) {
195     DEBUG ((DEBUG_ERROR, "UnRegister is not allowed if the SmmReadyToLock event has been triggered! \n"));
196     return EFI_ACCESS_DENIED;
197   }
198 
199   DisableGpiSmiSource = TRUE;
200   //
201   // Loop through all sources in record linked list to see if any other GPI SMI
202   // is installed on the same pin. If no then disable GPI SMI capability on this pad
203   //
204   LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);
205   while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {
206     RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);
207     LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);
208     //
209     // If this is the record to delete skip it
210     //
211     if (RecordInDb == RecordToDelete) {
212       continue;
213     }
214     //
215     // Check if record is GPI SMI type
216     //
217     if (RecordInDb->ProtocolType == GpiType) {
218       //
219       // Check if same GPIO pad is the source of this SMI
220       //
221       if (RecordInDb->ChildContext.Gpi.GpiNum == RecordToDelete->ChildContext.Gpi.GpiNum) {
222         DisableGpiSmiSource = FALSE;
223         break;
224       }
225     }
226   }
227 
228   if (DisableGpiSmiSource) {
229     GpioGetPadAndSmiRegs (
230       (UINT32) RecordToDelete->ChildContext.Gpi.GpiNum,
231       &GpioPad,
232       &GpiSmiBitOffset,
233       &GpiHostSwOwnRegAddress,
234       &GpiSmiStsRegAddress
235       );
236 
237     Data32Or = 1u << GpiSmiBitOffset;
238     Data32And = 0xFFFFFFFF;
239     MmioOr32 (GpiHostSwOwnRegAddress, Data32Or);
240     S3BootScriptSaveMemReadWrite (S3BootScriptWidthUint32, GpiHostSwOwnRegAddress, &Data32Or, &Data32And);
241   }
242 
243 
244   RemoveEntryList (&RecordToDelete->Link);
245   ZeroMem (RecordToDelete, sizeof (DATABASE_RECORD));
246   Status = gSmst->SmmFreePool (RecordToDelete);
247 
248   if (EFI_ERROR (Status)) {
249     ASSERT (FALSE);
250     return Status;
251   }
252   SmiHandlerProfileUnregisterHandler (&gEfiSmmGpiDispatch2ProtocolGuid, RecordToDelete->Callback, NULL, 0);
253   return EFI_SUCCESS;
254 }
255