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 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 // Include common header file for this module.
21 #include "CommonHeader.h"
24 #include "QNCSmmHelpers.h"
27 // /////////////////////////////////////////////////////////////////////////////
28 // MODULE / GLOBAL DATA
30 // Module variables used by the both the main dispatcher and the source dispatchers
31 // Declared in QNCSmmSources.h
36 PRIVATE_DATA mPrivateData
= { // for the structure
39 }, // CallbackDataBase linked list head
40 NULL
, // Handler returned whan calling SmiHandlerRegister
41 NULL
, // EFI handle returned when calling InstallMultipleProtocolInterfaces
43 // elements within the array
48 &gEfiSmmSxDispatch2ProtocolGuid
,
51 (QNC_SMM_GENERIC_REGISTER
) QNCSmmCoreRegister
,
52 (QNC_SMM_GENERIC_UNREGISTER
) QNCSmmCoreUnRegister
59 &gEfiSmmSwDispatch2ProtocolGuid
,
62 (QNC_SMM_GENERIC_REGISTER
) QNCSmmCoreRegister
,
63 (QNC_SMM_GENERIC_UNREGISTER
) QNCSmmCoreUnRegister
,
64 (UINTN
) MAXIMUM_SWI_VALUE
71 &gEfiSmmGpiDispatch2ProtocolGuid
,
74 (QNC_SMM_GENERIC_REGISTER
) QNCSmmCoreRegister
,
75 (QNC_SMM_GENERIC_UNREGISTER
) QNCSmmCoreUnRegister
,
83 &gEfiSmmIchnDispatch2ProtocolGuid
,
86 (QNC_SMM_GENERIC_REGISTER
) QNCSmmCoreRegister
,
87 (QNC_SMM_GENERIC_UNREGISTER
) QNCSmmCoreUnRegister
94 &gEfiSmmPowerButtonDispatch2ProtocolGuid
,
97 (QNC_SMM_GENERIC_REGISTER
) QNCSmmCoreRegister
,
98 (QNC_SMM_GENERIC_UNREGISTER
) QNCSmmCoreUnRegister
105 &gEfiSmmPeriodicTimerDispatch2ProtocolGuid
,
108 (QNC_SMM_GENERIC_REGISTER
) QNCSmmCoreRegister
,
109 (QNC_SMM_GENERIC_UNREGISTER
) QNCSmmCoreUnRegister
,
110 (UINTN
) QNCSmmPeriodicTimerDispatchGetNextShorterInterval
117 CONTEXT_FUNCTIONS mContextFunctions
[NUM_PROTOCOLS
] = {
144 PeriodicTimerGetContext
,
145 PeriodicTimerCmpContext
,
146 PeriodicTimerGetBuffer
,
151 // /////////////////////////////////////////////////////////////////////////////
154 // Functions use only in this file
157 QNCSmmCoreDispatcher (
158 IN EFI_HANDLE DispatchHandle
,
159 IN CONST VOID
*Context
, OPTIONAL
160 IN OUT VOID
*CommBuffer
, OPTIONAL
161 IN OUT UINTN
*CommBufferSize OPTIONAL
167 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
171 // /////////////////////////////////////////////////////////////////////////////
174 // Driver entry point
178 InitializeQNCSmmDispatcher (
179 IN EFI_HANDLE ImageHandle
,
180 IN EFI_SYSTEM_TABLE
*SystemTable
186 Initializes the QNC SMM Dispatcher
190 ImageHandle - Pointer to the loaded image protocol for this driver
191 SystemTable - Pointer to the EFI System Table
200 QNCSmmPublishDispatchProtocols ();
203 // Register a callback function to handle subsequent SMIs. This callback
204 // will be called by SmmCoreDispatcher.
206 Status
= gSmst
->SmiHandlerRegister (QNCSmmCoreDispatcher
, NULL
, &mPrivateData
.SmiHandle
);
207 ASSERT_EFI_ERROR (Status
);
210 // Initialize Callback DataBase
212 InitializeListHead (&mPrivateData
.CallbackDataBase
);
215 // Enable SMIs on the QNC now that we have a callback
217 QNCSmmInitHardware ();
230 Save Index registers to avoid corrupting the foreground environment
240 mPciAddress
= IoRead32 (EFI_PCI_ADDRESS_PORT
);
252 Restore Index registers to avoid corrupting the foreground environment
262 IoWrite32 (EFI_PCI_ADDRESS_PORT
, mPciAddress
);
267 SmiInputValueDuplicateCheck (
268 UINTN FedSwSmiInputValue
274 Check the Fed SwSmiInputValue to see if there is a duplicated one in the database
280 Status - EFI_SUCCESS, EFI_INVALID_PARAMETER
283 // GC_TODO: FedSwSmiInputValue - add argument and description to function comment
286 DATABASE_RECORD
*RecordInDb
;
287 LIST_ENTRY
*LinkInDb
;
289 LinkInDb
= GetFirstNode (&mPrivateData
.CallbackDataBase
);
290 while (!IsNull (&mPrivateData
.CallbackDataBase
, LinkInDb
)) {
291 RecordInDb
= DATABASE_RECORD_FROM_LINK (LinkInDb
);
293 if (RecordInDb
->ProtocolType
== SwType
) {
294 if (RecordInDb
->ChildContext
.Sw
.SwSmiInputValue
== FedSwSmiInputValue
) {
295 return EFI_INVALID_PARAMETER
;
299 LinkInDb
= GetNextNode (&mPrivateData
.CallbackDataBase
, &RecordInDb
->Link
);
307 IN QNC_SMM_GENERIC_PROTOCOL
*This
,
308 IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction
,
309 IN QNC_SMM_CONTEXT
*RegisterContext
,
310 OUT EFI_HANDLE
*DispatchHandle
321 // GC_TODO: This - add argument and description to function comment
322 // GC_TODO: DispatchFunction - add argument and description to function comment
323 // GC_TODO: RegisterContext - add argument and description to function comment
324 // GC_TODO: DispatchHandle - add argument and description to function comment
325 // GC_TODO: EFI_OUT_OF_RESOURCES - add return value to function comment
326 // GC_TODO: EFI_INVALID_PARAMETER - add return value to function comment
327 // GC_TODO: EFI_SUCCESS - add return value to function comment
328 // GC_TODO: EFI_INVALID_PARAMETER - add return value to function comment
331 DATABASE_RECORD
*Record
;
332 QNC_SMM_QUALIFIED_PROTOCOL
*Qualified
;
336 // Check for invalid parameter
338 if (This
== NULL
|| RegisterContext
== NULL
|| DispatchHandle
== NULL
) {
339 return EFI_INVALID_PARAMETER
;
343 // Create database record and add to database
345 Record
= (DATABASE_RECORD
*) AllocateZeroPool (sizeof (DATABASE_RECORD
));
346 if (Record
== NULL
) {
347 return EFI_OUT_OF_RESOURCES
;
351 // Gather information about the registration request
353 Record
->Callback
= DispatchFunction
;
354 Record
->CallbackContext
= RegisterContext
;
355 CopyMem (&Record
->ChildContext
, RegisterContext
, sizeof (QNC_SMM_CONTEXT
));
357 Qualified
= QUALIFIED_PROTOCOL_FROM_GENERIC (This
);
359 Record
->ProtocolType
= Qualified
->Type
;
361 CopyMem (&Record
->ContextFunctions
, &mContextFunctions
[Qualified
->Type
], sizeof (Record
->ContextFunctions
));
363 // Perform linked list housekeeping
365 Record
->Signature
= DATABASE_RECORD_SIGNATURE
;
367 switch (Qualified
->Type
) {
369 // By the end of this switch statement, we'll know the
370 // source description the child is registering for
374 // Check the validity of Context Type and Phase
376 if ((Record
->ChildContext
.Sx
.Type
< SxS0
) ||
377 (Record
->ChildContext
.Sx
.Type
>= EfiMaximumSleepType
) ||
378 (Record
->ChildContext
.Sx
.Phase
< SxEntry
) ||
379 (Record
->ChildContext
.Sx
.Phase
>= EfiMaximumPhase
)
384 InsertTailList (&mPrivateData
.CallbackDataBase
, &Record
->Link
);
385 CopyMem (&Record
->SrcDesc
, &SX_SOURCE_DESC
, sizeof (Record
->SrcDesc
));
387 // use default clear source function
392 if (RegisterContext
->Sw
.SwSmiInputValue
== (UINTN
)-1) {
394 // If SwSmiInputValue is set to (UINTN) -1 then a unique value will be assigned and returned in the structure.
396 Status
= EFI_NOT_FOUND
;
397 for (Index
= 1; Index
< MAXIMUM_SWI_VALUE
; Index
++) {
398 Status
= SmiInputValueDuplicateCheck (Index
);
399 if (!EFI_ERROR (Status
)) {
400 RegisterContext
->Sw
.SwSmiInputValue
= Index
;
404 if (RegisterContext
->Sw
.SwSmiInputValue
== (UINTN
)-1) {
405 Status
= gSmst
->SmmFreePool (Record
);
406 return EFI_OUT_OF_RESOURCES
;
409 // Update ChildContext again as SwSmiInputValue has been changed
411 CopyMem (&Record
->ChildContext
, RegisterContext
, sizeof (QNC_SMM_CONTEXT
));
415 // Check the validity of Context Value
417 if (Record
->ChildContext
.Sw
.SwSmiInputValue
> MAXIMUM_SWI_VALUE
) {
421 if (EFI_ERROR (SmiInputValueDuplicateCheck (Record
->ChildContext
.Sw
.SwSmiInputValue
))) {
425 InsertTailList (&mPrivateData
.CallbackDataBase
, &Record
->Link
);
426 CopyMem (&Record
->SrcDesc
, &SW_SOURCE_DESC
, sizeof (Record
->SrcDesc
));
427 Record
->BufferSize
= sizeof (EFI_SMM_SW_REGISTER_CONTEXT
);
429 // use default clear source function
435 InsertTailList (&mPrivateData
.CallbackDataBase
, &Record
->Link
);
436 CopyMem (&Record
->SrcDesc
, &GPI_SOURCE_DESC
, sizeof (Record
->SrcDesc
));
438 // use default clear source function
444 // Check the validity of Context Type
446 if ((Record
->ChildContext
.QNCn
.Type
< IchnMch
) || (Record
->ChildContext
.QNCn
.Type
>= NUM_ICHN_TYPES
)) {
450 InsertTailList (&mPrivateData
.CallbackDataBase
, &Record
->Link
);
451 CopyMem (&Record
->SrcDesc
, &QNCN_SOURCE_DESCS
[Record
->ChildContext
.QNCn
.Type
], sizeof (Record
->SrcDesc
));
452 Record
->ClearSource
= QNCSmmQNCnClearSource
;
455 case PeriodicTimerType
:
457 Status
= MapPeriodicTimerToSrcDesc (RegisterContext
, &(Record
->SrcDesc
));
458 if (EFI_ERROR (Status
)) {
462 InsertTailList (&mPrivateData
.CallbackDataBase
, &Record
->Link
);
463 Record
->BufferSize
= sizeof (EFI_SMM_PERIODIC_TIMER_CONTEXT
);
464 Record
->ClearSource
= QNCSmmPeriodicTimerClearSource
;
472 if (Record
->ClearSource
== NULL
) {
474 // Clear the SMI associated w/ the source using the default function
476 QNCSmmClearSource (&Record
->SrcDesc
);
479 // This source requires special handling to clear
481 Record
->ClearSource (&Record
->SrcDesc
);
484 QNCSmmEnableSource (&Record
->SrcDesc
);
487 // Child's handle will be the address linked list link in the record
489 *DispatchHandle
= (EFI_HANDLE
) (&Record
->Link
);
496 // DEBUG((EFI_D_ERROR,"Free pool status %d\n", Status ));
498 return EFI_INVALID_PARAMETER
;
502 QNCSmmCoreUnRegister (
503 IN QNC_SMM_GENERIC_PROTOCOL
*This
,
504 IN EFI_HANDLE DispatchHandle
515 // GC_TODO: This - add argument and description to function comment
516 // GC_TODO: DispatchHandle - add argument and description to function comment
517 // GC_TODO: EFI_INVALID_PARAMETER - add return value to function comment
518 // GC_TODO: EFI_INVALID_PARAMETER - add return value to function comment
519 // GC_TODO: EFI_SUCCESS - add return value to function comment
521 BOOLEAN SafeToDisable
;
522 DATABASE_RECORD
*RecordToDelete
;
523 DATABASE_RECORD
*RecordInDb
;
524 LIST_ENTRY
*LinkInDb
;
526 if (DispatchHandle
== NULL
) {
527 return EFI_INVALID_PARAMETER
;
530 if (BASE_CR (DispatchHandle
, DATABASE_RECORD
, Link
)->Signature
!= DATABASE_RECORD_SIGNATURE
) {
531 return EFI_INVALID_PARAMETER
;
534 RecordToDelete
= DATABASE_RECORD_FROM_LINK (DispatchHandle
);
536 RemoveEntryList (&RecordToDelete
->Link
);
537 RecordToDelete
->Signature
= 0;
540 // See if we can disable the source, reserved for future use since this might
541 // not be the only criteria to disable
543 SafeToDisable
= TRUE
;
544 LinkInDb
= GetFirstNode (&mPrivateData
.CallbackDataBase
);
545 while(!IsNull (&mPrivateData
.CallbackDataBase
, LinkInDb
)) {
546 RecordInDb
= DATABASE_RECORD_FROM_LINK (LinkInDb
);
547 if (CompareEnables (&RecordToDelete
->SrcDesc
, &RecordInDb
->SrcDesc
)) {
548 SafeToDisable
= FALSE
;
551 LinkInDb
= GetNextNode (&mPrivateData
.CallbackDataBase
, &RecordInDb
->Link
);
554 QNCSmmDisableSource( &RecordToDelete
->SrcDesc
);
557 FreePool (RecordToDelete
);
563 This function is the main entry point for an SMM handler dispatch
564 or communicate-based callback.
566 @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
567 @param RegisterContext Points to an optional handler context which was specified when the handler was registered.
568 @param CommBuffer A pointer to a collection of data in memory that will
569 be conveyed from a non-SMM environment into an SMM environment.
570 @param CommBufferSize The size of the CommBuffer.
576 QNCSmmCoreDispatcher (
577 IN EFI_HANDLE DispatchHandle
,
578 IN CONST VOID
*RegisterContext
,
579 IN OUT VOID
*CommBuffer
,
580 IN OUT UINTN
*CommBufferSize
584 // Used to prevent infinite loops
588 BOOLEAN ContextsMatch
;
589 BOOLEAN ResetListSearch
;
591 BOOLEAN SxChildWasDispatched
;
592 BOOLEAN ChildWasDispatched
;
594 DATABASE_RECORD
*RecordInDb
;
595 DATABASE_RECORD ActiveRecordInDb
;
596 LIST_ENTRY
*LinkInDb
;
597 DATABASE_RECORD
*RecordToExhaust
;
598 LIST_ENTRY
*LinkToExhaust
;
600 QNC_SMM_CONTEXT Context
;
601 VOID
*CommunicationBuffer
;
607 QNC_SMM_SOURCE_DESC ActiveSource
= NULL_SOURCE_DESC_INITIALIZER
;
610 ContextsMatch
= FALSE
;
611 ResetListSearch
= FALSE
;
613 SxChildWasDispatched
= FALSE
;
614 Status
= EFI_WARN_INTERRUPT_SOURCE_PENDING
;
615 ChildWasDispatched
= FALSE
;
618 // Mark all child handlers as not processed
620 LinkInDb
= GetFirstNode (&mPrivateData
.CallbackDataBase
);
621 while (!IsNull (&mPrivateData
.CallbackDataBase
, LinkInDb
)) {
622 RecordInDb
= DATABASE_RECORD_FROM_LINK (LinkInDb
);
623 RecordInDb
->Processed
= FALSE
;
624 LinkInDb
= GetNextNode (&mPrivateData
.CallbackDataBase
, LinkInDb
);
628 // Preserve Index registers
632 if (!IsListEmpty (&mPrivateData
.CallbackDataBase
)) {
634 // We have children registered w/ us -- continue
636 while ((!EosSet
) && (EscapeCount
> 0)) {
640 // Reset this flag in order to be able to process multiple SMI Sources in one loop.
642 ResetListSearch
= FALSE
;
644 LinkInDb
= GetFirstNode (&mPrivateData
.CallbackDataBase
);
646 while ((!IsNull (&mPrivateData
.CallbackDataBase
, LinkInDb
)) && (ResetListSearch
== FALSE
)) {
647 RecordInDb
= DATABASE_RECORD_FROM_LINK (LinkInDb
);
649 // Make a copy of the record that contains an active SMI source,
650 // because un-register maybe invoked in callback function and
651 // RecordInDb maybe released
653 CopyMem (&ActiveRecordInDb
, RecordInDb
, sizeof (ActiveRecordInDb
));
656 // look for the first active source
658 if (!SourceIsActive (&RecordInDb
->SrcDesc
)) {
660 // Didn't find the source yet, keep looking
662 LinkInDb
= GetNextNode (&mPrivateData
.CallbackDataBase
, &RecordInDb
->Link
);
666 // We found a source. If this is a sleep type, we have to go to
667 // appropriate sleep state anyway.No matter there is sleep child or not
669 if (RecordInDb
->ProtocolType
== SxType
) {
670 SxChildWasDispatched
= TRUE
;
673 // "cache" the source description and don't query I/O anymore
675 CopyMem (&ActiveSource
, &RecordInDb
->SrcDesc
, sizeof (ActiveSource
));
676 LinkToExhaust
= LinkInDb
;
679 // exhaust the rest of the queue looking for the same source
681 while (!IsNull (&mPrivateData
.CallbackDataBase
, LinkToExhaust
)) {
682 RecordToExhaust
= DATABASE_RECORD_FROM_LINK (LinkToExhaust
);
683 LinkToExhaust
= GetNextNode (&mPrivateData
.CallbackDataBase
, LinkToExhaust
);
684 if (RecordToExhaust
->Processed
) {
686 // Record has already been processed. Continue with next child handler.
691 if (CompareSources (&RecordToExhaust
->SrcDesc
, &ActiveSource
)) {
693 // These source descriptions are equal, so this callback should be
696 if (RecordToExhaust
->ContextFunctions
.GetContext
!= NULL
) {
698 // This child requires that we get a calling context from
699 // hardware and compare that context to the one supplied
702 ASSERT (RecordToExhaust
->ContextFunctions
.CmpContext
!= NULL
);
705 // Make sure contexts match before dispatching event to child
707 RecordToExhaust
->ContextFunctions
.GetContext (RecordToExhaust
, &Context
);
708 ContextsMatch
= RecordToExhaust
->ContextFunctions
.CmpContext (&Context
, &RecordToExhaust
->ChildContext
);
712 // This child doesn't require any more calling context beyond what
713 // it supplied in registration. Simply pass back what it gave us.
715 ASSERT (RecordToExhaust
->Callback
!= NULL
);
716 ContextsMatch
= TRUE
;
720 // Mark this child handler so it will not be processed again
722 RecordToExhaust
->Processed
= TRUE
;
726 if (RecordToExhaust
->BufferSize
!= 0) {
727 ASSERT (RecordToExhaust
->ContextFunctions
.GetBuffer
!= NULL
);
729 RecordToExhaust
->ContextFunctions
.GetBuffer (RecordToExhaust
);
731 CommunicationBuffer
= &RecordToExhaust
->CommBuffer
;
732 BufferSize
= RecordToExhaust
->BufferSize
;
734 CommunicationBuffer
= NULL
;
738 ASSERT (RecordToExhaust
->Callback
!= NULL
);
740 RecordToExhaust
->Callback (
741 (EFI_HANDLE
) & RecordToExhaust
->Link
,
742 RecordToExhaust
->CallbackContext
,
747 ChildWasDispatched
= TRUE
;
748 if (RecordToExhaust
->ProtocolType
== SxType
) {
749 SxChildWasDispatched
= TRUE
;
753 // Can not use RecordInDb after this point because Callback may have unregistered RecordInDb
754 // Restart processing of SMI handlers from the begining of the linked list because the
755 // state of the linked listed may have been modified due to unregister actions in the Callback.
757 LinkToExhaust
= GetFirstNode (&mPrivateData
.CallbackDataBase
);
761 if (ActiveRecordInDb
.ClearSource
== NULL
) {
763 // Clear the SMI associated w/ the source using the default function
765 QNCSmmClearSource (&ActiveSource
);
768 // This source requires special handling to clear
770 ActiveRecordInDb
.ClearSource (&ActiveSource
);
773 if (ChildWasDispatched
) {
775 // The interrupt was handled and quiesced
777 Status
= EFI_SUCCESS
;
780 // The interrupt was not handled but quiesced
782 Status
= EFI_WARN_INTERRUPT_SOURCE_QUIESCED
;
786 // Queue is empty, reset the search
788 ResetListSearch
= TRUE
;
792 EosSet
= QNCSmmSetAndCheckEos ();
796 // If you arrive here, there are two possible reasons:
797 // (1) you've got problems with clearing the SMI status bits in the
798 // ACPI table. If you don't properly clear the SMI bits, then you won't be able to set the
799 // EOS bit. If this happens too many times, the loop exits.
800 // (2) there was a SMM communicate for callback messages that was received prior
802 // If there is an asynchronous SMI that occurs while processing the Callback, let
803 // all of the drivers (including this one) have an opportunity to scan for the SMI
805 // If not, we don't want to exit and have the foreground app. clear EOS without letting
806 // these other sources get serviced.
808 ASSERT (EscapeCount
> 0);
811 // Restore Index registers
815 if (SxChildWasDispatched
) {
817 // A child of the SmmSxDispatch protocol was dispatched during this call;
818 // put the system to sleep.
820 QNCSmmSxGoToSleep ();
824 // Ensure that SMI signal pin indicator is clear at the end of SMM handling.
826 NewValue
= QNCPortRead (QUARK_NC_HOST_BRIDGE_SB_PORT_ID
, QUARK_NC_HOST_BRIDGE_HLEGACY_REG
);
827 NewValue
&= ~(HLEGACY_SMI_PIN_VALUE
);
828 QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID
, QUARK_NC_HOST_BRIDGE_HLEGACY_REG
, NewValue
);