2 This driver is responsible for the registration of child drivers
3 and the abstraction of the QNC SMI sources.
5 Copyright (c) 2013-2016 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 LIST_ENTRY
*LinkInDb
;
596 DATABASE_RECORD
*RecordToExhaust
;
597 LIST_ENTRY
*LinkToExhaust
;
599 QNC_SMM_CONTEXT Context
;
600 VOID
*CommunicationBuffer
;
606 QNC_SMM_SOURCE_DESC ActiveSource
= NULL_SOURCE_DESC_INITIALIZER
;
609 ContextsMatch
= FALSE
;
610 ResetListSearch
= FALSE
;
612 SxChildWasDispatched
= FALSE
;
613 Status
= EFI_WARN_INTERRUPT_SOURCE_PENDING
;
614 ChildWasDispatched
= FALSE
;
617 // Preserve Index registers
621 if (!IsListEmpty (&mPrivateData
.CallbackDataBase
)) {
623 // We have children registered w/ us -- continue
625 while ((!EosSet
) && (EscapeCount
> 0)) {
629 // Reset this flag in order to be able to process multiple SMI Sources in one loop.
631 ResetListSearch
= FALSE
;
633 LinkInDb
= GetFirstNode (&mPrivateData
.CallbackDataBase
);
635 while ((!IsNull (&mPrivateData
.CallbackDataBase
, LinkInDb
)) && (ResetListSearch
== FALSE
)) {
636 RecordInDb
= DATABASE_RECORD_FROM_LINK (LinkInDb
);
639 // look for the first active source
641 if (!SourceIsActive (&RecordInDb
->SrcDesc
)) {
643 // Didn't find the source yet, keep looking
645 LinkInDb
= GetNextNode (&mPrivateData
.CallbackDataBase
, &RecordInDb
->Link
);
649 // We found a source. If this is a sleep type, we have to go to
650 // appropriate sleep state anyway.No matter there is sleep child or not
652 if (RecordInDb
->ProtocolType
== SxType
) {
653 SxChildWasDispatched
= TRUE
;
656 // "cache" the source description and don't query I/O anymore
658 CopyMem (&ActiveSource
, &RecordInDb
->SrcDesc
, sizeof (ActiveSource
));
659 LinkToExhaust
= LinkInDb
;
662 // exhaust the rest of the queue looking for the same source
664 while (!IsNull (&mPrivateData
.CallbackDataBase
, LinkToExhaust
)) {
665 RecordToExhaust
= DATABASE_RECORD_FROM_LINK (LinkToExhaust
);
667 if (CompareSources (&RecordToExhaust
->SrcDesc
, &ActiveSource
)) {
669 // These source descriptions are equal, so this callback should be
672 if (RecordToExhaust
->ContextFunctions
.GetContext
!= NULL
) {
674 // This child requires that we get a calling context from
675 // hardware and compare that context to the one supplied
678 ASSERT (RecordToExhaust
->ContextFunctions
.CmpContext
!= NULL
);
681 // Make sure contexts match before dispatching event to child
683 RecordToExhaust
->ContextFunctions
.GetContext (RecordToExhaust
, &Context
);
684 ContextsMatch
= RecordToExhaust
->ContextFunctions
.CmpContext (&Context
, &RecordToExhaust
->ChildContext
);
688 // This child doesn't require any more calling context beyond what
689 // it supplied in registration. Simply pass back what it gave us.
691 ASSERT (RecordToExhaust
->Callback
!= NULL
);
692 ContextsMatch
= TRUE
;
697 if (RecordToExhaust
->BufferSize
!= 0) {
698 ASSERT (RecordToExhaust
->ContextFunctions
.GetBuffer
!= NULL
);
700 RecordToExhaust
->ContextFunctions
.GetBuffer (RecordToExhaust
);
702 CommunicationBuffer
= &RecordToExhaust
->CommBuffer
;
703 BufferSize
= RecordToExhaust
->BufferSize
;
705 CommunicationBuffer
= NULL
;
709 ASSERT (RecordToExhaust
->Callback
!= NULL
);
711 RecordToExhaust
->Callback (
712 (EFI_HANDLE
) & RecordToExhaust
->Link
,
713 RecordToExhaust
->CallbackContext
,
718 ChildWasDispatched
= TRUE
;
719 if (RecordToExhaust
->ProtocolType
== SxType
) {
720 SxChildWasDispatched
= TRUE
;
725 // Get next record in DB
727 LinkToExhaust
= GetNextNode (&mPrivateData
.CallbackDataBase
, &RecordToExhaust
->Link
);
730 if (RecordInDb
->ClearSource
== NULL
) {
732 // Clear the SMI associated w/ the source using the default function
734 QNCSmmClearSource (&ActiveSource
);
737 // This source requires special handling to clear
739 RecordInDb
->ClearSource (&ActiveSource
);
742 if (ChildWasDispatched
) {
744 // The interrupt was handled and quiesced
746 Status
= EFI_SUCCESS
;
749 // The interrupt was not handled but quiesced
751 Status
= EFI_WARN_INTERRUPT_SOURCE_QUIESCED
;
755 // Queue is empty, reset the search
757 ResetListSearch
= TRUE
;
761 EosSet
= QNCSmmSetAndCheckEos ();
765 // If you arrive here, there are two possible reasons:
766 // (1) you've got problems with clearing the SMI status bits in the
767 // ACPI table. If you don't properly clear the SMI bits, then you won't be able to set the
768 // EOS bit. If this happens too many times, the loop exits.
769 // (2) there was a SMM communicate for callback messages that was received prior
771 // If there is an asynchronous SMI that occurs while processing the Callback, let
772 // all of the drivers (including this one) have an opportunity to scan for the SMI
774 // If not, we don't want to exit and have the foreground app. clear EOS without letting
775 // these other sources get serviced.
777 ASSERT (EscapeCount
> 0);
780 // Restore Index registers
784 if (SxChildWasDispatched
) {
786 // A child of the SmmSxDispatch protocol was dispatched during this call;
787 // put the system to sleep.
789 QNCSmmSxGoToSleep ();
793 // Ensure that SMI signal pin indicator is clear at the end of SMM handling.
795 NewValue
= QNCPortRead (QUARK_NC_HOST_BRIDGE_SB_PORT_ID
, QUARK_NC_HOST_BRIDGE_HLEGACY_REG
);
796 NewValue
&= ~(HLEGACY_SMI_PIN_VALUE
);
797 QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID
, QUARK_NC_HOST_BRIDGE_HLEGACY_REG
, NewValue
);