2 SMM SwDispatch2 Protocol.
4 Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include "PchSmiDispatchSmm.h"
20 SMM_PCH_REGISTER mSmiPchReg
;
22 EFI_SMM_CPU_PROTOCOL
*mSmmCpuProtocol
;
23 LIST_ENTRY mSmmSwDispatch2Queue
= INITIALIZE_LIST_HEAD_VARIABLE (mSmmSwDispatch2Queue
);
26 Find SmmSwDispatch2Context by SwSmiInputValue.
28 @param[in] SwSmiInputValue The value to indentify the SmmSwDispatch2 context
30 @return Pointer to EFI_SMM_SW_DISPATCH2_CONTEXT context
32 EFI_SMM_SW_DISPATCH2_CONTEXT
*
33 FindContextBySwSmiInputValue (
34 IN UINTN SwSmiInputValue
38 EFI_SMM_SW_DISPATCH2_CONTEXT
*Dispatch2Context
;
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
;
52 Find SmmSwDispatch2Context by DispatchHandle.
54 @param DispatchHandle The handle to indentify the SmmSwDispatch2 context
56 @return Pointer to EFI_SMM_SW_DISPATCH2_CONTEXT context
58 EFI_SMM_SW_DISPATCH2_CONTEXT
*
59 FindContextByDispatchHandle (
60 IN EFI_HANDLE DispatchHandle
64 EFI_SMM_SW_DISPATCH2_CONTEXT
*Dispatch2Context
;
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
;
78 Dispatch registered SMM handlers
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.
91 IN EFI_HANDLE DispatchHandle
,
92 IN CONST VOID
*RegisterContext
,
93 IN OUT VOID
*CommBuffer
,
94 IN OUT UINTN
*CommBufferSize
98 EFI_SMM_SW_CONTEXT SwContext
;
100 EFI_SMM_SW_DISPATCH2_CONTEXT
*Context
;
101 EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction
;
102 EFI_SMM_SW_REGISTER_CONTEXT DispatchContext
;
104 EFI_SMM_SAVE_STATE_IO_INFO IoInfo
;
107 // Construct new context
109 SwContext
.SwSmiCpuIndex
= 0;
110 SwContext
.CommandPort
= IoRead8 (SMM_CONTROL_PORT
);
111 SwContext
.DataPort
= IoRead8 (SMM_DATA_PORT
);
114 // Try to find which CPU trigger SWSMI
116 for (Index
= 0; Index
< gSmst
->NumberOfCpus
; Index
++) {
117 Status
= mSmmCpuProtocol
->ReadSaveState (
120 EFI_SMM_SAVE_STATE_REGISTER_IO
,
124 if (EFI_ERROR (Status
)) {
128 if (IoInfo
.IoPort
== SMM_CONTROL_PORT
) {
132 SwContext
.SwSmiCpuIndex
= Index
;
133 DEBUG ((DEBUG_VERBOSE
, "CPU index = 0x%x/0x%x\n", Index
, gSmst
->NumberOfCpus
));
138 if (SwContext
.CommandPort
== 0) {
139 DEBUG ((DEBUG_VERBOSE
, "NOT SW SMI\n"));
140 Status
= EFI_SUCCESS
;
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
;
154 DEBUG ((DEBUG_VERBOSE
, "Prepare to call handler for 0x%x\n", SwContext
.CommandPort
));
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
);
166 // Clear SMI APM status
168 IoOr32 (mSmiPchReg
.SmiApmStsAddr
, 1 << mSmiPchReg
.ApmBitOffset
);
173 IoOr32 (mSmiPchReg
.SmiEosAddr
, 1 << mSmiPchReg
.EosBitOffset
);
179 Check the SwSmiInputValue is already used
181 @param[in] SwSmiInputValue To indentify the SmmSwDispatch2 context
183 @retval EFI_SUCCESS SwSmiInputValue could be used.
184 @retval EFI_INVALID_PARAMETER SwSmiInputValue is already be used.
189 IN UINTN SwSmiInputValue
193 EFI_SMM_SW_DISPATCH2_CONTEXT
*Dispatch2Context
;
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
;
207 Register a child SMI source dispatch function for the specified software SMI.
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().
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
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
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
231 @retval EFI_OUT_OF_RESOURCES A unique software SMI value could not be assigned
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
245 EFI_SMM_SW_DISPATCH2_CONTEXT
*Context
;
247 if (RegContext
->SwSmiInputValue
== (UINTN
)-1) {
249 // If SwSmiInputValue is set to (UINTN) -1 then a unique value
250 // will be assigned and returned in the structure.
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
;
261 if (RegContext
->SwSmiInputValue
== (UINTN
)-1) {
262 return EFI_OUT_OF_RESOURCES
;
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
;
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
;
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
);
291 Unregister a child SMI source dispatch function for the specified software SMI.
293 This service removes the handler associated with DispatchHandle so that it will no longer be
294 called in response to a software SMI.
296 @param[in] This Pointer to the EFI_SMM_SW_DISPATCH2_PROTOCOL instance.
297 @param[in] DispatchHandle Handle of dispatch function to deregister.
299 @retval EFI_SUCCESS The dispatch function has been successfully unregistered.
300 @retval EFI_INVALID_PARAMETER The DispatchHandle was not valid.
304 SmmSwDispatch2UnRegister (
305 IN CONST EFI_SMM_SW_DISPATCH2_PROTOCOL
*This
,
306 IN EFI_HANDLE DispatchHandle
309 EFI_SMM_SW_DISPATCH2_CONTEXT
*Context
;
314 Context
= FindContextByDispatchHandle (DispatchHandle
);
315 ASSERT (Context
!= NULL
);
316 if (Context
!= NULL
) {
317 RemoveEntryList (&Context
->Link
);
318 gSmst
->SmmFreePool (Context
);
324 EFI_SMM_SW_DISPATCH2_PROTOCOL gSmmSwDispatch2
= {
325 SmmSwDispatch2Register
,
326 SmmSwDispatch2UnRegister
,
331 Get specified SMI register based on given register ID
333 @param[in] SmmRegister SMI related register array from bootloader
334 @param[in] Id The register ID to get.
336 @retval NULL The register is not found or the format is not expected.
340 PLD_GENERIC_REGISTER
*
342 IN PLD_SMM_REGISTERS
*SmmRegister
,
347 PLD_GENERIC_REGISTER
*PldReg
;
350 for (Index
= 0; Index
< SmmRegister
->Count
; Index
++) {
351 if (SmmRegister
->Registers
[Index
].Id
== Id
) {
352 PldReg
= &SmmRegister
->Registers
[Index
];
357 if (PldReg
== NULL
) {
358 DEBUG ((DEBUG_INFO
, "Register %d not found.\n", Id
));
363 // Checking the register if it is expected.
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))
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
));
384 Entry Point for this driver.
386 @param[in] ImageHandle Image handle of this driver.
387 @param[in] SystemTable A Pointer to the EFI System Table.
389 @retval EFI_SUCCESS The entry point is executed successfully.
390 @retval other Some error occurred when executing this entry point.
394 PchSmiDispatchEntryPoint (
395 IN EFI_HANDLE ImageHandle
,
396 IN EFI_SYSTEM_TABLE
*SystemTable
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
;
406 GuidHob
= GetFirstGuidHob (&gSmmRegisterInfoGuid
);
407 if (GuidHob
== NULL
) {
408 return EFI_UNSUPPORTED
;
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
;
418 mSmiPchReg
.SmiEosAddr
= (UINT32
)SmiEosReg
->Address
.Address
;
419 mSmiPchReg
.EosBitOffset
= SmiEosReg
->Address
.RegisterBitOffset
;
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
;
427 mSmiPchReg
.SmiApmStsAddr
= (UINT32
)SmiApmStsReg
->Address
.Address
;
428 mSmiPchReg
.ApmBitOffset
= SmiApmStsReg
->Address
.RegisterBitOffset
;
431 // Locate PI SMM CPU protocol
433 Status
= gSmst
->SmmLocateProtocol (&gEfiSmmCpuProtocolGuid
, NULL
, (VOID
**)&mSmmCpuProtocol
);
434 ASSERT_EFI_ERROR (Status
);
437 // Register a SMM handler to handle subsequent SW SMIs.
439 Status
= gSmst
->SmiHandlerRegister ((EFI_MM_HANDLER_ENTRY_POINT
)SmmSwDispatcher
, NULL
, &DispatchHandle
);
440 ASSERT_EFI_ERROR (Status
);
443 // Publish PI SMM SwDispatch2 Protocol
446 Status
= gSmst
->SmmInstallProtocolInterface (
448 &gEfiSmmSwDispatch2ProtocolGuid
,
449 EFI_NATIVE_INTERFACE
,
452 ASSERT_EFI_ERROR (Status
);