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