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