]> git.proxmox.com Git - mirror_edk2.git/blob - UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.c
MdePkg/Acpi62: Add type 7 NFIT Platform Capabilities Structure support
[mirror_edk2.git] / UefiPayloadPkg / PchSmiDispatchSmm / PchSmiDispatchSmm.c
1 /** @file
2 SMM SwDispatch2 Protocol.
3
4 Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7
8
9 **/
10
11 #include "PchSmiDispatchSmm.h"
12
13 typedef struct {
14 UINT8 EosBitOffset;
15 UINT8 ApmBitOffset;
16 UINT32 SmiEosAddr;
17 UINT32 SmiApmStsAddr;
18 } SMM_PCH_REGISTER;
19
20 SMM_PCH_REGISTER mSmiPchReg;
21
22 EFI_SMM_CPU_PROTOCOL *mSmmCpuProtocol;
23 LIST_ENTRY mSmmSwDispatch2Queue = INITIALIZE_LIST_HEAD_VARIABLE (mSmmSwDispatch2Queue);
24
25 /**
26 Find SmmSwDispatch2Context by SwSmiInputValue.
27
28 @param[in] SwSmiInputValue The value to indentify the SmmSwDispatch2 context
29
30 @return Pointer to EFI_SMM_SW_DISPATCH2_CONTEXT context
31 **/
32 EFI_SMM_SW_DISPATCH2_CONTEXT *
33 FindContextBySwSmiInputValue (
34 IN UINTN SwSmiInputValue
35 )
36 {
37 LIST_ENTRY *Node;
38 EFI_SMM_SW_DISPATCH2_CONTEXT *Dispatch2Context;
39
40 Node = mSmmSwDispatch2Queue.ForwardLink;
41 for ( ; Node != &mSmmSwDispatch2Queue; Node = Node->ForwardLink) {
42 Dispatch2Context = BASE_CR (Node, EFI_SMM_SW_DISPATCH2_CONTEXT, Link);
43 if (Dispatch2Context->SwSmiInputValue == SwSmiInputValue) {
44 return Dispatch2Context;
45 }
46 }
47
48 return NULL;
49 }
50
51 /**
52 Find SmmSwDispatch2Context by DispatchHandle.
53
54 @param DispatchHandle The handle to indentify the SmmSwDispatch2 context
55
56 @return Pointer to EFI_SMM_SW_DISPATCH2_CONTEXT context
57 **/
58 EFI_SMM_SW_DISPATCH2_CONTEXT *
59 FindContextByDispatchHandle (
60 IN EFI_HANDLE DispatchHandle
61 )
62 {
63 LIST_ENTRY *Node;
64 EFI_SMM_SW_DISPATCH2_CONTEXT *Dispatch2Context;
65
66 Node = mSmmSwDispatch2Queue.ForwardLink;
67 for ( ; Node != &mSmmSwDispatch2Queue; Node = Node->ForwardLink) {
68 Dispatch2Context = BASE_CR (Node, EFI_SMM_SW_DISPATCH2_CONTEXT, Link);
69 if (Dispatch2Context->DispatchHandle == DispatchHandle) {
70 return Dispatch2Context;
71 }
72 }
73
74 return NULL;
75 }
76
77 /**
78 Dispatch registered SMM handlers
79
80 @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
81 @param RegisterContext Points to an optional handler context which was specified when the handler was registered.
82 @param CommBuffer A pointer to a collection of data in memory that will
83 be conveyed from a non-SMM environment into an SMM environment.
84 @param CommBufferSize The size of the CommBuffer.
85
86 @return Status Code
87
88 **/
89 EFI_STATUS
90 SmmSwDispatcher (
91 IN EFI_HANDLE DispatchHandle,
92 IN CONST VOID *RegisterContext,
93 IN OUT VOID *CommBuffer,
94 IN OUT UINTN *CommBufferSize
95 )
96 {
97 EFI_STATUS Status;
98 EFI_SMM_SW_CONTEXT SwContext;
99 UINTN Index;
100 EFI_SMM_SW_DISPATCH2_CONTEXT *Context;
101 EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction;
102 EFI_SMM_SW_REGISTER_CONTEXT DispatchContext;
103 UINTN Size;
104 EFI_SMM_SAVE_STATE_IO_INFO IoInfo;
105
106 //
107 // Construct new context
108 //
109 SwContext.SwSmiCpuIndex = 0;
110 SwContext.CommandPort = IoRead8 (SMM_CONTROL_PORT);
111 SwContext.DataPort = IoRead8 (SMM_DATA_PORT);
112
113 //
114 // Try to find which CPU trigger SWSMI
115 //
116 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
117 Status = mSmmCpuProtocol->ReadSaveState (
118 mSmmCpuProtocol,
119 sizeof (IoInfo),
120 EFI_SMM_SAVE_STATE_REGISTER_IO,
121 Index,
122 &IoInfo
123 );
124 if (EFI_ERROR (Status)) {
125 continue;
126 }
127
128 if (IoInfo.IoPort == SMM_CONTROL_PORT) {
129 //
130 // Great! Find it.
131 //
132 SwContext.SwSmiCpuIndex = Index;
133 DEBUG ((DEBUG_VERBOSE, "CPU index = 0x%x/0x%x\n", Index, gSmst->NumberOfCpus));
134 break;
135 }
136 }
137
138 if (SwContext.CommandPort == 0) {
139 DEBUG ((DEBUG_VERBOSE, "NOT SW SMI\n"));
140 Status = EFI_SUCCESS;
141 goto End;
142 }
143
144 //
145 // Search context
146 //
147 Context = FindContextBySwSmiInputValue (SwContext.CommandPort);
148 if (Context == NULL) {
149 DEBUG ((DEBUG_INFO, "No handler for SMI value 0x%x\n", SwContext.CommandPort));
150 Status = EFI_SUCCESS;
151 goto End;
152 }
153
154 DEBUG ((DEBUG_VERBOSE, "Prepare to call handler for 0x%x\n", SwContext.CommandPort));
155
156 //
157 // Dispatch
158 //
159 DispatchContext.SwSmiInputValue = SwContext.CommandPort;
160 Size = sizeof (SwContext);
161 DispatchFunction = (EFI_SMM_HANDLER_ENTRY_POINT2)Context->DispatchFunction;
162 Status = DispatchFunction (DispatchHandle, &DispatchContext, &SwContext, &Size);
163
164 End:
165 //
166 // Clear SMI APM status
167 //
168 IoOr32 (mSmiPchReg.SmiApmStsAddr, 1 << mSmiPchReg.ApmBitOffset);
169
170 //
171 // Set EOS bit
172 //
173 IoOr32 (mSmiPchReg.SmiEosAddr, 1 << mSmiPchReg.EosBitOffset);
174
175 return Status;
176 }
177
178 /**
179 Check the SwSmiInputValue is already used
180
181 @param[in] SwSmiInputValue To indentify the SmmSwDispatch2 context
182
183 @retval EFI_SUCCESS SwSmiInputValue could be used.
184 @retval EFI_INVALID_PARAMETER SwSmiInputValue is already be used.
185
186 **/
187 EFI_STATUS
188 SmiInputValueCheck (
189 IN UINTN SwSmiInputValue
190 )
191 {
192 LIST_ENTRY *Node;
193 EFI_SMM_SW_DISPATCH2_CONTEXT *Dispatch2Context;
194
195 Node = mSmmSwDispatch2Queue.ForwardLink;
196 for ( ; Node != &mSmmSwDispatch2Queue; Node = Node->ForwardLink) {
197 Dispatch2Context = BASE_CR (Node, EFI_SMM_SW_DISPATCH2_CONTEXT, Link);
198 if (Dispatch2Context->SwSmiInputValue == SwSmiInputValue) {
199 return EFI_INVALID_PARAMETER;
200 }
201 }
202
203 return EFI_SUCCESS;
204 }
205
206 /**
207 Register a child SMI source dispatch function for the specified software SMI.
208
209 This service registers a function (DispatchFunction) which will be called when the software
210 SMI source specified by RegContext->SwSmiCpuIndex is detected. On return, DispatchHandle
211 contains a unique handle which may be used later to unregister the function using UnRegister().
212
213 @param[in] This Pointer to the EFI_SMM_SW_DISPATCH2_PROTOCOL instance.
214 @param[in] DispatchFunction Function to register for handler when the specified software
215 SMI is generated.
216 @param[in, out] RegContext Pointer to the dispatch function's context.
217 The caller fills this context in before calling
218 the register function to indicate to the register
219 function which Software SMI input value the
220 dispatch function should be invoked for.
221 @param[out] DispatchHandle Handle generated by the dispatcher to track the
222 function instance.
223
224 @retval EFI_SUCCESS The dispatch function has been successfully
225 registered and the SMI source has been enabled.
226 @retval EFI_DEVICE_ERROR The SW driver was unable to enable the SMI source.
227 @retval EFI_INVALID_PARAMETER RegisterContext is invalid. The SW SMI input value
228 is not within valid range.
229 @retval EFI_OUT_OF_RESOURCES There is not enough memory (system or SMM) to manage this
230 child.
231 @retval EFI_OUT_OF_RESOURCES A unique software SMI value could not be assigned
232 for this dispatch.
233 **/
234 EFI_STATUS
235 EFIAPI
236 SmmSwDispatch2Register (
237 IN CONST EFI_SMM_SW_DISPATCH2_PROTOCOL *This,
238 IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction,
239 IN OUT EFI_SMM_SW_REGISTER_CONTEXT *RegContext,
240 OUT EFI_HANDLE *DispatchHandle
241 )
242 {
243 EFI_STATUS Status;
244 UINTN Index;
245 EFI_SMM_SW_DISPATCH2_CONTEXT *Context;
246
247 if (RegContext->SwSmiInputValue == (UINTN)-1) {
248 //
249 // If SwSmiInputValue is set to (UINTN) -1 then a unique value
250 // will be assigned and returned in the structure.
251 //
252 Status = EFI_NOT_FOUND;
253 for (Index = 1; Index < MAXIMUM_SWI_VALUE; Index++) {
254 Status = SmiInputValueCheck (Index);
255 if (!EFI_ERROR (Status)) {
256 RegContext->SwSmiInputValue = Index;
257 break;
258 }
259 }
260
261 if (RegContext->SwSmiInputValue == (UINTN)-1) {
262 return EFI_OUT_OF_RESOURCES;
263 }
264 }
265
266 if ((RegContext->SwSmiInputValue > MAXIMUM_SWI_VALUE) || (RegContext->SwSmiInputValue == 0)) {
267 DEBUG ((DEBUG_ERROR, "ERROR: SMI value range (1 ~ 0x%x)\n", MAXIMUM_SWI_VALUE));
268 return EFI_INVALID_PARAMETER;
269 }
270
271 //
272 // Register
273 //
274 Status = gSmst->SmmAllocatePool (EfiRuntimeServicesData, sizeof (*Context), (VOID **)&Context);
275 ASSERT_EFI_ERROR (Status);
276 if (EFI_ERROR (Status)) {
277 return EFI_OUT_OF_RESOURCES;
278 }
279
280 *DispatchHandle = (EFI_HANDLE)Context;
281 Context->Signature = SMI_SW_HANDLER_SIGNATURE;
282 Context->SwSmiInputValue = RegContext->SwSmiInputValue;
283 Context->DispatchFunction = (UINTN)DispatchFunction;
284 Context->DispatchHandle = *DispatchHandle;
285 InsertTailList (&mSmmSwDispatch2Queue, &Context->Link);
286
287 return Status;
288 }
289
290 /**
291 Unregister a child SMI source dispatch function for the specified software SMI.
292
293 This service removes the handler associated with DispatchHandle so that it will no longer be
294 called in response to a software SMI.
295
296 @param[in] This Pointer to the EFI_SMM_SW_DISPATCH2_PROTOCOL instance.
297 @param[in] DispatchHandle Handle of dispatch function to deregister.
298
299 @retval EFI_SUCCESS The dispatch function has been successfully unregistered.
300 @retval EFI_INVALID_PARAMETER The DispatchHandle was not valid.
301 **/
302 EFI_STATUS
303 EFIAPI
304 SmmSwDispatch2UnRegister (
305 IN CONST EFI_SMM_SW_DISPATCH2_PROTOCOL *This,
306 IN EFI_HANDLE DispatchHandle
307 )
308 {
309 EFI_SMM_SW_DISPATCH2_CONTEXT *Context;
310
311 //
312 // Unregister
313 //
314 Context = FindContextByDispatchHandle (DispatchHandle);
315 ASSERT (Context != NULL);
316 if (Context != NULL) {
317 RemoveEntryList (&Context->Link);
318 gSmst->SmmFreePool (Context);
319 }
320
321 return EFI_SUCCESS;
322 }
323
324 EFI_SMM_SW_DISPATCH2_PROTOCOL gSmmSwDispatch2 = {
325 SmmSwDispatch2Register,
326 SmmSwDispatch2UnRegister,
327 MAXIMUM_SWI_VALUE
328 };
329
330 /**
331 Get specified SMI register based on given register ID
332
333 @param[in] SmmRegister SMI related register array from bootloader
334 @param[in] Id The register ID to get.
335
336 @retval NULL The register is not found or the format is not expected.
337 @return smi register
338
339 **/
340 PLD_GENERIC_REGISTER *
341 GetSmmCtrlRegById (
342 IN PLD_SMM_REGISTERS *SmmRegister,
343 IN UINT32 Id
344 )
345 {
346 UINT32 Index;
347 PLD_GENERIC_REGISTER *PldReg;
348
349 PldReg = NULL;
350 for (Index = 0; Index < SmmRegister->Count; Index++) {
351 if (SmmRegister->Registers[Index].Id == Id) {
352 PldReg = &SmmRegister->Registers[Index];
353 break;
354 }
355 }
356
357 if (PldReg == NULL) {
358 DEBUG ((DEBUG_INFO, "Register %d not found.\n", Id));
359 return NULL;
360 }
361
362 //
363 // Checking the register if it is expected.
364 //
365 if ((PldReg->Address.AccessSize != EFI_ACPI_3_0_DWORD) ||
366 (PldReg->Address.Address == 0) ||
367 (PldReg->Address.RegisterBitWidth != 1) ||
368 (PldReg->Address.AddressSpaceId != EFI_ACPI_3_0_SYSTEM_IO) ||
369 (PldReg->Value != 1))
370 {
371 DEBUG ((DEBUG_INFO, "Unexpected SMM register.\n"));
372 DEBUG ((DEBUG_INFO, "AddressSpaceId= 0x%x\n", PldReg->Address.AddressSpaceId));
373 DEBUG ((DEBUG_INFO, "RegBitWidth = 0x%x\n", PldReg->Address.RegisterBitWidth));
374 DEBUG ((DEBUG_INFO, "RegBitOffset = 0x%x\n", PldReg->Address.RegisterBitOffset));
375 DEBUG ((DEBUG_INFO, "AccessSize = 0x%x\n", PldReg->Address.AccessSize));
376 DEBUG ((DEBUG_INFO, "Address = 0x%lx\n", PldReg->Address.Address));
377 return NULL;
378 }
379
380 return PldReg;
381 }
382
383 /**
384 Entry Point for this driver.
385
386 @param[in] ImageHandle Image handle of this driver.
387 @param[in] SystemTable A Pointer to the EFI System Table.
388
389 @retval EFI_SUCCESS The entry point is executed successfully.
390 @retval other Some error occurred when executing this entry point.
391 **/
392 EFI_STATUS
393 EFIAPI
394 PchSmiDispatchEntryPoint (
395 IN EFI_HANDLE ImageHandle,
396 IN EFI_SYSTEM_TABLE *SystemTable
397 )
398 {
399 EFI_STATUS Status;
400 EFI_HANDLE DispatchHandle;
401 EFI_HOB_GUID_TYPE *GuidHob;
402 PLD_SMM_REGISTERS *SmmRegister;
403 PLD_GENERIC_REGISTER *SmiEosReg;
404 PLD_GENERIC_REGISTER *SmiApmStsReg;
405
406 GuidHob = GetFirstGuidHob (&gSmmRegisterInfoGuid);
407 if (GuidHob == NULL) {
408 return EFI_UNSUPPORTED;
409 }
410
411 SmmRegister = (PLD_SMM_REGISTERS *)GET_GUID_HOB_DATA (GuidHob);
412 SmiEosReg = GetSmmCtrlRegById (SmmRegister, REGISTER_ID_SMI_EOS);
413 if (SmiEosReg == NULL) {
414 DEBUG ((DEBUG_ERROR, "SMI EOS reg not found.\n"));
415 return EFI_NOT_FOUND;
416 }
417
418 mSmiPchReg.SmiEosAddr = (UINT32)SmiEosReg->Address.Address;
419 mSmiPchReg.EosBitOffset = SmiEosReg->Address.RegisterBitOffset;
420
421 SmiApmStsReg = GetSmmCtrlRegById (SmmRegister, REGISTER_ID_SMI_APM_STS);
422 if (SmiApmStsReg == NULL) {
423 DEBUG ((DEBUG_ERROR, "SMI APM status reg not found.\n"));
424 return EFI_NOT_FOUND;
425 }
426
427 mSmiPchReg.SmiApmStsAddr = (UINT32)SmiApmStsReg->Address.Address;
428 mSmiPchReg.ApmBitOffset = SmiApmStsReg->Address.RegisterBitOffset;
429
430 //
431 // Locate PI SMM CPU protocol
432 //
433 Status = gSmst->SmmLocateProtocol (&gEfiSmmCpuProtocolGuid, NULL, (VOID **)&mSmmCpuProtocol);
434 ASSERT_EFI_ERROR (Status);
435
436 //
437 // Register a SMM handler to handle subsequent SW SMIs.
438 //
439 Status = gSmst->SmiHandlerRegister ((EFI_MM_HANDLER_ENTRY_POINT)SmmSwDispatcher, NULL, &DispatchHandle);
440 ASSERT_EFI_ERROR (Status);
441
442 //
443 // Publish PI SMM SwDispatch2 Protocol
444 //
445 ImageHandle = NULL;
446 Status = gSmst->SmmInstallProtocolInterface (
447 &ImageHandle,
448 &gEfiSmmSwDispatch2ProtocolGuid,
449 EFI_NATIVE_INTERFACE,
450 &gSmmSwDispatch2
451 );
452 ASSERT_EFI_ERROR (Status);
453
454 return Status;
455 }