2 This driver is responsible for the registration of child drivers
3 and the abstraction of the QNC SMI sources.
5 Copyright (c) 2013-2017 Intel Corporation.
7 SPDX-License-Identifier: BSD-2-Clause-Patent
13 // Include common header file for this module.
15 #include "CommonHeader.h"
18 #include "QNCSmmHelpers.h"
21 // /////////////////////////////////////////////////////////////////////////////
22 // MODULE / GLOBAL DATA
24 // Module variables used by the both the main dispatcher and the source dispatchers
25 // Declared in QNCSmmSources.h
30 PRIVATE_DATA mPrivateData
= { // for the structure
33 }, // CallbackDataBase linked list head
34 NULL
, // Handler returned whan calling SmiHandlerRegister
35 NULL
, // EFI handle returned when calling InstallMultipleProtocolInterfaces
37 // elements within the array
42 &gEfiSmmSxDispatch2ProtocolGuid
,
45 (QNC_SMM_GENERIC_REGISTER
) QNCSmmCoreRegister
,
46 (QNC_SMM_GENERIC_UNREGISTER
) QNCSmmCoreUnRegister
53 &gEfiSmmSwDispatch2ProtocolGuid
,
56 (QNC_SMM_GENERIC_REGISTER
) QNCSmmCoreRegister
,
57 (QNC_SMM_GENERIC_UNREGISTER
) QNCSmmCoreUnRegister
,
58 (UINTN
) MAXIMUM_SWI_VALUE
65 &gEfiSmmGpiDispatch2ProtocolGuid
,
68 (QNC_SMM_GENERIC_REGISTER
) QNCSmmCoreRegister
,
69 (QNC_SMM_GENERIC_UNREGISTER
) QNCSmmCoreUnRegister
,
77 &gEfiSmmIchnDispatch2ProtocolGuid
,
80 (QNC_SMM_GENERIC_REGISTER
) QNCSmmCoreRegister
,
81 (QNC_SMM_GENERIC_UNREGISTER
) QNCSmmCoreUnRegister
88 &gEfiSmmPowerButtonDispatch2ProtocolGuid
,
91 (QNC_SMM_GENERIC_REGISTER
) QNCSmmCoreRegister
,
92 (QNC_SMM_GENERIC_UNREGISTER
) QNCSmmCoreUnRegister
99 &gEfiSmmPeriodicTimerDispatch2ProtocolGuid
,
102 (QNC_SMM_GENERIC_REGISTER
) QNCSmmCoreRegister
,
103 (QNC_SMM_GENERIC_UNREGISTER
) QNCSmmCoreUnRegister
,
104 (UINTN
) QNCSmmPeriodicTimerDispatchGetNextShorterInterval
111 CONTEXT_FUNCTIONS mContextFunctions
[NUM_PROTOCOLS
] = {
138 PeriodicTimerGetContext
,
139 PeriodicTimerCmpContext
,
140 PeriodicTimerGetBuffer
,
145 // /////////////////////////////////////////////////////////////////////////////
148 // Functions use only in this file
151 QNCSmmCoreDispatcher (
152 IN EFI_HANDLE DispatchHandle
,
153 IN CONST VOID
*Context
, OPTIONAL
154 IN OUT VOID
*CommBuffer
, OPTIONAL
155 IN OUT UINTN
*CommBufferSize OPTIONAL
161 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
165 // /////////////////////////////////////////////////////////////////////////////
168 // Driver entry point
172 InitializeQNCSmmDispatcher (
173 IN EFI_HANDLE ImageHandle
,
174 IN EFI_SYSTEM_TABLE
*SystemTable
180 Initializes the QNC SMM Dispatcher
184 ImageHandle - Pointer to the loaded image protocol for this driver
185 SystemTable - Pointer to the EFI System Table
194 QNCSmmPublishDispatchProtocols ();
197 // Register a callback function to handle subsequent SMIs. This callback
198 // will be called by SmmCoreDispatcher.
200 Status
= gSmst
->SmiHandlerRegister (QNCSmmCoreDispatcher
, NULL
, &mPrivateData
.SmiHandle
);
201 ASSERT_EFI_ERROR (Status
);
204 // Initialize Callback DataBase
206 InitializeListHead (&mPrivateData
.CallbackDataBase
);
209 // Enable SMIs on the QNC now that we have a callback
211 QNCSmmInitHardware ();
224 Save Index registers to avoid corrupting the foreground environment
234 mPciAddress
= IoRead32 (EFI_PCI_ADDRESS_PORT
);
246 Restore Index registers to avoid corrupting the foreground environment
256 IoWrite32 (EFI_PCI_ADDRESS_PORT
, mPciAddress
);
261 SmiInputValueDuplicateCheck (
262 UINTN FedSwSmiInputValue
268 Check the Fed SwSmiInputValue to see if there is a duplicated one in the database
274 Status - EFI_SUCCESS, EFI_INVALID_PARAMETER
277 // GC_TODO: FedSwSmiInputValue - add argument and description to function comment
280 DATABASE_RECORD
*RecordInDb
;
281 LIST_ENTRY
*LinkInDb
;
283 LinkInDb
= GetFirstNode (&mPrivateData
.CallbackDataBase
);
284 while (!IsNull (&mPrivateData
.CallbackDataBase
, LinkInDb
)) {
285 RecordInDb
= DATABASE_RECORD_FROM_LINK (LinkInDb
);
287 if (RecordInDb
->ProtocolType
== SwType
) {
288 if (RecordInDb
->ChildContext
.Sw
.SwSmiInputValue
== FedSwSmiInputValue
) {
289 return EFI_INVALID_PARAMETER
;
293 LinkInDb
= GetNextNode (&mPrivateData
.CallbackDataBase
, &RecordInDb
->Link
);
301 IN QNC_SMM_GENERIC_PROTOCOL
*This
,
302 IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction
,
303 IN QNC_SMM_CONTEXT
*RegisterContext
,
304 OUT EFI_HANDLE
*DispatchHandle
315 // GC_TODO: This - add argument and description to function comment
316 // GC_TODO: DispatchFunction - add argument and description to function comment
317 // GC_TODO: RegisterContext - add argument and description to function comment
318 // GC_TODO: DispatchHandle - add argument and description to function comment
319 // GC_TODO: EFI_OUT_OF_RESOURCES - add return value to function comment
320 // GC_TODO: EFI_INVALID_PARAMETER - add return value to function comment
321 // GC_TODO: EFI_SUCCESS - add return value to function comment
322 // GC_TODO: EFI_INVALID_PARAMETER - add return value to function comment
325 DATABASE_RECORD
*Record
;
326 QNC_SMM_QUALIFIED_PROTOCOL
*Qualified
;
330 // Check for invalid parameter
332 if (This
== NULL
|| RegisterContext
== NULL
|| DispatchHandle
== NULL
) {
333 return EFI_INVALID_PARAMETER
;
337 // Create database record and add to database
339 Record
= (DATABASE_RECORD
*) AllocateZeroPool (sizeof (DATABASE_RECORD
));
340 if (Record
== NULL
) {
341 return EFI_OUT_OF_RESOURCES
;
345 // Gather information about the registration request
347 Record
->Callback
= DispatchFunction
;
348 Record
->CallbackContext
= RegisterContext
;
349 CopyMem (&Record
->ChildContext
, RegisterContext
, sizeof (QNC_SMM_CONTEXT
));
351 Qualified
= QUALIFIED_PROTOCOL_FROM_GENERIC (This
);
353 Record
->ProtocolType
= Qualified
->Type
;
355 CopyMem (&Record
->ContextFunctions
, &mContextFunctions
[Qualified
->Type
], sizeof (Record
->ContextFunctions
));
357 // Perform linked list housekeeping
359 Record
->Signature
= DATABASE_RECORD_SIGNATURE
;
361 switch (Qualified
->Type
) {
363 // By the end of this switch statement, we'll know the
364 // source description the child is registering for
368 // Check the validity of Context Type and Phase
370 if ((Record
->ChildContext
.Sx
.Type
< SxS0
) ||
371 (Record
->ChildContext
.Sx
.Type
>= EfiMaximumSleepType
) ||
372 (Record
->ChildContext
.Sx
.Phase
< SxEntry
) ||
373 (Record
->ChildContext
.Sx
.Phase
>= EfiMaximumPhase
)
378 InsertTailList (&mPrivateData
.CallbackDataBase
, &Record
->Link
);
379 CopyMem (&Record
->SrcDesc
, &SX_SOURCE_DESC
, sizeof (Record
->SrcDesc
));
381 // use default clear source function
386 if (RegisterContext
->Sw
.SwSmiInputValue
== (UINTN
)-1) {
388 // If SwSmiInputValue is set to (UINTN) -1 then a unique value will be assigned and returned in the structure.
390 Status
= EFI_NOT_FOUND
;
391 for (Index
= 1; Index
< MAXIMUM_SWI_VALUE
; Index
++) {
392 Status
= SmiInputValueDuplicateCheck (Index
);
393 if (!EFI_ERROR (Status
)) {
394 RegisterContext
->Sw
.SwSmiInputValue
= Index
;
398 if (RegisterContext
->Sw
.SwSmiInputValue
== (UINTN
)-1) {
399 Status
= gSmst
->SmmFreePool (Record
);
400 return EFI_OUT_OF_RESOURCES
;
403 // Update ChildContext again as SwSmiInputValue has been changed
405 CopyMem (&Record
->ChildContext
, RegisterContext
, sizeof (QNC_SMM_CONTEXT
));
409 // Check the validity of Context Value
411 if (Record
->ChildContext
.Sw
.SwSmiInputValue
> MAXIMUM_SWI_VALUE
) {
415 if (EFI_ERROR (SmiInputValueDuplicateCheck (Record
->ChildContext
.Sw
.SwSmiInputValue
))) {
419 InsertTailList (&mPrivateData
.CallbackDataBase
, &Record
->Link
);
420 CopyMem (&Record
->SrcDesc
, &SW_SOURCE_DESC
, sizeof (Record
->SrcDesc
));
421 Record
->BufferSize
= sizeof (EFI_SMM_SW_REGISTER_CONTEXT
);
423 // use default clear source function
429 InsertTailList (&mPrivateData
.CallbackDataBase
, &Record
->Link
);
430 CopyMem (&Record
->SrcDesc
, &GPI_SOURCE_DESC
, sizeof (Record
->SrcDesc
));
432 // use default clear source function
438 // Check the validity of Context Type
440 if ((Record
->ChildContext
.QNCn
.Type
< IchnMch
) || (Record
->ChildContext
.QNCn
.Type
>= NUM_ICHN_TYPES
)) {
444 InsertTailList (&mPrivateData
.CallbackDataBase
, &Record
->Link
);
445 CopyMem (&Record
->SrcDesc
, &QNCN_SOURCE_DESCS
[Record
->ChildContext
.QNCn
.Type
], sizeof (Record
->SrcDesc
));
446 Record
->ClearSource
= QNCSmmQNCnClearSource
;
449 case PeriodicTimerType
:
451 Status
= MapPeriodicTimerToSrcDesc (RegisterContext
, &(Record
->SrcDesc
));
452 if (EFI_ERROR (Status
)) {
456 InsertTailList (&mPrivateData
.CallbackDataBase
, &Record
->Link
);
457 Record
->BufferSize
= sizeof (EFI_SMM_PERIODIC_TIMER_CONTEXT
);
458 Record
->ClearSource
= QNCSmmPeriodicTimerClearSource
;
466 if (Record
->ClearSource
== NULL
) {
468 // Clear the SMI associated w/ the source using the default function
470 QNCSmmClearSource (&Record
->SrcDesc
);
473 // This source requires special handling to clear
475 Record
->ClearSource (&Record
->SrcDesc
);
478 QNCSmmEnableSource (&Record
->SrcDesc
);
481 // Child's handle will be the address linked list link in the record
483 *DispatchHandle
= (EFI_HANDLE
) (&Record
->Link
);
490 // DEBUG((EFI_D_ERROR,"Free pool status %d\n", Status ));
492 return EFI_INVALID_PARAMETER
;
496 QNCSmmCoreUnRegister (
497 IN QNC_SMM_GENERIC_PROTOCOL
*This
,
498 IN EFI_HANDLE DispatchHandle
509 // GC_TODO: This - add argument and description to function comment
510 // GC_TODO: DispatchHandle - add argument and description to function comment
511 // GC_TODO: EFI_INVALID_PARAMETER - add return value to function comment
512 // GC_TODO: EFI_INVALID_PARAMETER - add return value to function comment
513 // GC_TODO: EFI_SUCCESS - add return value to function comment
515 BOOLEAN SafeToDisable
;
516 DATABASE_RECORD
*RecordToDelete
;
517 DATABASE_RECORD
*RecordInDb
;
518 LIST_ENTRY
*LinkInDb
;
520 if (DispatchHandle
== NULL
) {
521 return EFI_INVALID_PARAMETER
;
524 if (BASE_CR (DispatchHandle
, DATABASE_RECORD
, Link
)->Signature
!= DATABASE_RECORD_SIGNATURE
) {
525 return EFI_INVALID_PARAMETER
;
528 RecordToDelete
= DATABASE_RECORD_FROM_LINK (DispatchHandle
);
530 RemoveEntryList (&RecordToDelete
->Link
);
531 RecordToDelete
->Signature
= 0;
534 // See if we can disable the source, reserved for future use since this might
535 // not be the only criteria to disable
537 SafeToDisable
= TRUE
;
538 LinkInDb
= GetFirstNode (&mPrivateData
.CallbackDataBase
);
539 while(!IsNull (&mPrivateData
.CallbackDataBase
, LinkInDb
)) {
540 RecordInDb
= DATABASE_RECORD_FROM_LINK (LinkInDb
);
541 if (CompareEnables (&RecordToDelete
->SrcDesc
, &RecordInDb
->SrcDesc
)) {
542 SafeToDisable
= FALSE
;
545 LinkInDb
= GetNextNode (&mPrivateData
.CallbackDataBase
, &RecordInDb
->Link
);
548 QNCSmmDisableSource( &RecordToDelete
->SrcDesc
);
551 FreePool (RecordToDelete
);
557 This function is the main entry point for an SMM handler dispatch
558 or communicate-based callback.
560 @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
561 @param RegisterContext Points to an optional handler context which was specified when the handler was registered.
562 @param CommBuffer A pointer to a collection of data in memory that will
563 be conveyed from a non-SMM environment into an SMM environment.
564 @param CommBufferSize The size of the CommBuffer.
570 QNCSmmCoreDispatcher (
571 IN EFI_HANDLE DispatchHandle
,
572 IN CONST VOID
*RegisterContext
,
573 IN OUT VOID
*CommBuffer
,
574 IN OUT UINTN
*CommBufferSize
578 // Used to prevent infinite loops
582 BOOLEAN ContextsMatch
;
583 BOOLEAN ResetListSearch
;
585 BOOLEAN SxChildWasDispatched
;
586 BOOLEAN ChildWasDispatched
;
588 DATABASE_RECORD
*RecordInDb
;
589 DATABASE_RECORD ActiveRecordInDb
;
590 LIST_ENTRY
*LinkInDb
;
591 DATABASE_RECORD
*RecordToExhaust
;
592 LIST_ENTRY
*LinkToExhaust
;
594 QNC_SMM_CONTEXT Context
;
595 VOID
*CommunicationBuffer
;
601 QNC_SMM_SOURCE_DESC ActiveSource
= NULL_SOURCE_DESC_INITIALIZER
;
604 ContextsMatch
= FALSE
;
605 ResetListSearch
= FALSE
;
607 SxChildWasDispatched
= FALSE
;
608 Status
= EFI_WARN_INTERRUPT_SOURCE_PENDING
;
609 ChildWasDispatched
= FALSE
;
612 // Mark all child handlers as not processed
614 LinkInDb
= GetFirstNode (&mPrivateData
.CallbackDataBase
);
615 while (!IsNull (&mPrivateData
.CallbackDataBase
, LinkInDb
)) {
616 RecordInDb
= DATABASE_RECORD_FROM_LINK (LinkInDb
);
617 RecordInDb
->Processed
= FALSE
;
618 LinkInDb
= GetNextNode (&mPrivateData
.CallbackDataBase
, LinkInDb
);
622 // Preserve Index registers
626 if (!IsListEmpty (&mPrivateData
.CallbackDataBase
)) {
628 // We have children registered w/ us -- continue
630 while ((!EosSet
) && (EscapeCount
> 0)) {
634 // Reset this flag in order to be able to process multiple SMI Sources in one loop.
636 ResetListSearch
= FALSE
;
638 LinkInDb
= GetFirstNode (&mPrivateData
.CallbackDataBase
);
640 while ((!IsNull (&mPrivateData
.CallbackDataBase
, LinkInDb
)) && (ResetListSearch
== FALSE
)) {
641 RecordInDb
= DATABASE_RECORD_FROM_LINK (LinkInDb
);
643 // Make a copy of the record that contains an active SMI source,
644 // because un-register maybe invoked in callback function and
645 // RecordInDb maybe released
647 CopyMem (&ActiveRecordInDb
, RecordInDb
, sizeof (ActiveRecordInDb
));
650 // look for the first active source
652 if (!SourceIsActive (&RecordInDb
->SrcDesc
)) {
654 // Didn't find the source yet, keep looking
656 LinkInDb
= GetNextNode (&mPrivateData
.CallbackDataBase
, &RecordInDb
->Link
);
660 // We found a source. If this is a sleep type, we have to go to
661 // appropriate sleep state anyway.No matter there is sleep child or not
663 if (RecordInDb
->ProtocolType
== SxType
) {
664 SxChildWasDispatched
= TRUE
;
667 // "cache" the source description and don't query I/O anymore
669 CopyMem (&ActiveSource
, &RecordInDb
->SrcDesc
, sizeof (ActiveSource
));
670 LinkToExhaust
= LinkInDb
;
673 // exhaust the rest of the queue looking for the same source
675 while (!IsNull (&mPrivateData
.CallbackDataBase
, LinkToExhaust
)) {
676 RecordToExhaust
= DATABASE_RECORD_FROM_LINK (LinkToExhaust
);
677 LinkToExhaust
= GetNextNode (&mPrivateData
.CallbackDataBase
, LinkToExhaust
);
678 if (RecordToExhaust
->Processed
) {
680 // Record has already been processed. Continue with next child handler.
685 if (CompareSources (&RecordToExhaust
->SrcDesc
, &ActiveSource
)) {
687 // These source descriptions are equal, so this callback should be
690 if (RecordToExhaust
->ContextFunctions
.GetContext
!= NULL
) {
692 // This child requires that we get a calling context from
693 // hardware and compare that context to the one supplied
696 ASSERT (RecordToExhaust
->ContextFunctions
.CmpContext
!= NULL
);
699 // Make sure contexts match before dispatching event to child
701 RecordToExhaust
->ContextFunctions
.GetContext (RecordToExhaust
, &Context
);
702 ContextsMatch
= RecordToExhaust
->ContextFunctions
.CmpContext (&Context
, &RecordToExhaust
->ChildContext
);
706 // This child doesn't require any more calling context beyond what
707 // it supplied in registration. Simply pass back what it gave us.
709 ASSERT (RecordToExhaust
->Callback
!= NULL
);
710 ContextsMatch
= TRUE
;
714 // Mark this child handler so it will not be processed again
716 RecordToExhaust
->Processed
= TRUE
;
720 if (RecordToExhaust
->BufferSize
!= 0) {
721 ASSERT (RecordToExhaust
->ContextFunctions
.GetBuffer
!= NULL
);
723 RecordToExhaust
->ContextFunctions
.GetBuffer (RecordToExhaust
);
725 CommunicationBuffer
= &RecordToExhaust
->CommBuffer
;
726 BufferSize
= RecordToExhaust
->BufferSize
;
728 CommunicationBuffer
= NULL
;
732 ASSERT (RecordToExhaust
->Callback
!= NULL
);
734 RecordToExhaust
->Callback (
735 (EFI_HANDLE
) & RecordToExhaust
->Link
,
736 RecordToExhaust
->CallbackContext
,
741 ChildWasDispatched
= TRUE
;
742 if (RecordToExhaust
->ProtocolType
== SxType
) {
743 SxChildWasDispatched
= TRUE
;
747 // Can not use RecordInDb after this point because Callback may have unregistered RecordInDb
748 // Restart processing of SMI handlers from the begining of the linked list because the
749 // state of the linked listed may have been modified due to unregister actions in the Callback.
751 LinkToExhaust
= GetFirstNode (&mPrivateData
.CallbackDataBase
);
755 if (ActiveRecordInDb
.ClearSource
== NULL
) {
757 // Clear the SMI associated w/ the source using the default function
759 QNCSmmClearSource (&ActiveSource
);
762 // This source requires special handling to clear
764 ActiveRecordInDb
.ClearSource (&ActiveSource
);
767 if (ChildWasDispatched
) {
769 // The interrupt was handled and quiesced
771 Status
= EFI_SUCCESS
;
774 // The interrupt was not handled but quiesced
776 Status
= EFI_WARN_INTERRUPT_SOURCE_QUIESCED
;
780 // Queue is empty, reset the search
782 ResetListSearch
= TRUE
;
786 EosSet
= QNCSmmSetAndCheckEos ();
790 // If you arrive here, there are two possible reasons:
791 // (1) you've got problems with clearing the SMI status bits in the
792 // ACPI table. If you don't properly clear the SMI bits, then you won't be able to set the
793 // EOS bit. If this happens too many times, the loop exits.
794 // (2) there was a SMM communicate for callback messages that was received prior
796 // If there is an asynchronous SMI that occurs while processing the Callback, let
797 // all of the drivers (including this one) have an opportunity to scan for the SMI
799 // If not, we don't want to exit and have the foreground app. clear EOS without letting
800 // these other sources get serviced.
802 ASSERT (EscapeCount
> 0);
805 // Restore Index registers
809 if (SxChildWasDispatched
) {
811 // A child of the SmmSxDispatch protocol was dispatched during this call;
812 // put the system to sleep.
814 QNCSmmSxGoToSleep ();
818 // Ensure that SMI signal pin indicator is clear at the end of SMM handling.
820 NewValue
= QNCPortRead (QUARK_NC_HOST_BRIDGE_SB_PORT_ID
, QUARK_NC_HOST_BRIDGE_HLEGACY_REG
);
821 NewValue
&= ~(HLEGACY_SMI_PIN_VALUE
);
822 QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID
, QUARK_NC_HOST_BRIDGE_HLEGACY_REG
, NewValue
);