1 /** @file
2 This driver is responsible for the registration of child drivers
3 and the abstraction of the QNC SMI sources.
4 
5 Copyright (c) 2013-2016 Intel Corporation.
6 
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
11 
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.
14 
15 
16 **/
17 
18 //
19 // Include common header file for this module.
20 //
21 #include "CommonHeader.h"
22 
23 #include "QNCSmm.h"
24 #include "QNCSmmHelpers.h"
25 
26 //
27 // /////////////////////////////////////////////////////////////////////////////
28 // MODULE / GLOBAL DATA
29 //
30 // Module variables used by the both the main dispatcher and the source dispatchers
31 // Declared in QNCSmmSources.h
32 //
33 UINT32                    mPciData;
34 UINT32                    mPciAddress;
35 
36 PRIVATE_DATA              mPrivateData = {  // for the structure
37   {
38     NULL
39   },                                        // CallbackDataBase linked list head
40   NULL,                                     // Handler returned whan calling SmiHandlerRegister
41   NULL,                                     // EFI handle returned when calling InstallMultipleProtocolInterfaces
42   {                                         // protocol arrays
43     // elements within the array
44     //
45     {
46       PROTOCOL_SIGNATURE,
47       SxType,
48       &gEfiSmmSxDispatch2ProtocolGuid,
49       {
50         {
51           (QNC_SMM_GENERIC_REGISTER) QNCSmmCoreRegister,
52           (QNC_SMM_GENERIC_UNREGISTER) QNCSmmCoreUnRegister
53         }
54       }
55     },
56     {
57       PROTOCOL_SIGNATURE,
58       SwType,
59       &gEfiSmmSwDispatch2ProtocolGuid,
60       {
61         {
62           (QNC_SMM_GENERIC_REGISTER) QNCSmmCoreRegister,
63           (QNC_SMM_GENERIC_UNREGISTER) QNCSmmCoreUnRegister,
64           (UINTN) MAXIMUM_SWI_VALUE
65         }
66       }
67     },
68     {
69       PROTOCOL_SIGNATURE,
70       GpiType,
71       &gEfiSmmGpiDispatch2ProtocolGuid,
72       {
73         {
74           (QNC_SMM_GENERIC_REGISTER) QNCSmmCoreRegister,
75           (QNC_SMM_GENERIC_UNREGISTER) QNCSmmCoreUnRegister,
76           (UINTN) 1
77         }
78       }
79     },
80     {
81       PROTOCOL_SIGNATURE,
82       QNCnType,
83       &gEfiSmmIchnDispatch2ProtocolGuid,
84       {
85         {
86           (QNC_SMM_GENERIC_REGISTER) QNCSmmCoreRegister,
87           (QNC_SMM_GENERIC_UNREGISTER) QNCSmmCoreUnRegister
88         }
89       }
90     },
91     {
92       PROTOCOL_SIGNATURE,
93       PowerButtonType,
94       &gEfiSmmPowerButtonDispatch2ProtocolGuid,
95       {
96         {
97           (QNC_SMM_GENERIC_REGISTER) QNCSmmCoreRegister,
98           (QNC_SMM_GENERIC_UNREGISTER) QNCSmmCoreUnRegister
99         }
100       }
101     },
102     {
103       PROTOCOL_SIGNATURE,
104       PeriodicTimerType,
105       &gEfiSmmPeriodicTimerDispatch2ProtocolGuid,
106       {
107         {
108           (QNC_SMM_GENERIC_REGISTER) QNCSmmCoreRegister,
109           (QNC_SMM_GENERIC_UNREGISTER) QNCSmmCoreUnRegister,
110           (UINTN) QNCSmmPeriodicTimerDispatchGetNextShorterInterval
111         }
112       }
113     },
114   }
115 };
116 
117 CONTEXT_FUNCTIONS         mContextFunctions[NUM_PROTOCOLS] = {
118   {
119     SxGetContext,
120     SxCmpContext,
121     NULL
122   },
123   {
124     SwGetContext,
125     SwCmpContext,
126     SwGetBuffer
127   },
128   {
129     NULL,
130     NULL,
131     NULL
132   },
133   {
134     NULL,
135     NULL,
136     NULL
137   },
138   {
139     NULL,
140     NULL,
141     NULL
142   },
143   {
144     PeriodicTimerGetContext,
145     PeriodicTimerCmpContext,
146     PeriodicTimerGetBuffer,
147   },
148 };
149 
150 //
151 // /////////////////////////////////////////////////////////////////////////////
152 // PROTOTYPES
153 //
154 // Functions use only in this file
155 //
156 EFI_STATUS
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
162   );
163 
164 
165 UINTN
166 DevicePathSize (
167   IN EFI_DEVICE_PATH_PROTOCOL  *DevicePath
168   );
169 
170 //
171 // /////////////////////////////////////////////////////////////////////////////
172 // FUNCTIONS
173 //
174 // Driver entry point
175 //
176 EFI_STATUS
177 EFIAPI
InitializeQNCSmmDispatcher(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)178 InitializeQNCSmmDispatcher (
179   IN EFI_HANDLE        ImageHandle,
180   IN EFI_SYSTEM_TABLE  *SystemTable
181   )
182 /*++
183 
184 Routine Description:
185 
186   Initializes the QNC SMM Dispatcher
187 
188 Arguments:
189 
190   ImageHandle   - Pointer to the loaded image protocol for this driver
191   SystemTable   - Pointer to the EFI System Table
192 
193 Returns:
194   Status        - EFI_SUCCESS
195 
196 --*/
197 {
198   EFI_STATUS                Status;
199 
200   QNCSmmPublishDispatchProtocols ();
201 
202   //
203   // Register a callback function to handle subsequent SMIs.  This callback
204   // will be called by SmmCoreDispatcher.
205   //
206   Status = gSmst->SmiHandlerRegister (QNCSmmCoreDispatcher, NULL, &mPrivateData.SmiHandle);
207   ASSERT_EFI_ERROR (Status);
208 
209   //
210   // Initialize Callback DataBase
211   //
212   InitializeListHead (&mPrivateData.CallbackDataBase);
213 
214   //
215   // Enable SMIs on the QNC now that we have a callback
216   //
217   QNCSmmInitHardware ();
218 
219   return EFI_SUCCESS;
220 }
221 
222 EFI_STATUS
SaveState(VOID)223 SaveState (
224   VOID
225   )
226 /*++
227 
228 Routine Description:
229 
230   Save Index registers to avoid corrupting the foreground environment
231 
232 Arguments:
233   None
234 
235 Returns:
236   Status - EFI_SUCCESS
237 
238 --*/
239 {
240   mPciAddress = IoRead32 (EFI_PCI_ADDRESS_PORT);
241   return EFI_SUCCESS;
242 }
243 
244 EFI_STATUS
RestoreState(VOID)245 RestoreState (
246   VOID
247   )
248 /*++
249 
250 Routine Description:
251 
252   Restore Index registers to avoid corrupting the foreground environment
253 
254 Arguments:
255   None
256 
257 Returns:
258   Status - EFI_SUCCESS
259 
260 --*/
261 {
262   IoWrite32 (EFI_PCI_ADDRESS_PORT, mPciAddress);
263   return EFI_SUCCESS;
264 }
265 
266 EFI_STATUS
SmiInputValueDuplicateCheck(UINTN FedSwSmiInputValue)267 SmiInputValueDuplicateCheck (
268   UINTN           FedSwSmiInputValue
269   )
270 /*++
271 
272 Routine Description:
273 
274   Check the Fed SwSmiInputValue to see if there is a duplicated one in the database
275 
276 Arguments:
277   None
278 
279 Returns:
280   Status - EFI_SUCCESS, EFI_INVALID_PARAMETER
281 
282 --*/
283 // GC_TODO:    FedSwSmiInputValue - add argument and description to function comment
284 {
285 
286   DATABASE_RECORD *RecordInDb;
287   LIST_ENTRY      *LinkInDb;
288 
289   LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);
290   while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {
291     RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);
292 
293     if (RecordInDb->ProtocolType == SwType) {
294       if (RecordInDb->ChildContext.Sw.SwSmiInputValue == FedSwSmiInputValue) {
295         return EFI_INVALID_PARAMETER;
296       }
297     }
298 
299     LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);
300   }
301 
302   return EFI_SUCCESS;
303 }
304 
305 EFI_STATUS
QNCSmmCoreRegister(IN QNC_SMM_GENERIC_PROTOCOL * This,IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction,IN QNC_SMM_CONTEXT * RegisterContext,OUT EFI_HANDLE * DispatchHandle)306 QNCSmmCoreRegister (
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
311   )
312 /*++
313 
314 Routine Description:
315 
316 Arguments:
317 
318 Returns:
319 
320 --*/
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
329 {
330   EFI_STATUS                  Status;
331   DATABASE_RECORD             *Record;
332   QNC_SMM_QUALIFIED_PROTOCOL  *Qualified;
333   INTN                        Index;
334 
335   //
336   // Check for invalid parameter
337   //
338   if (This == NULL || RegisterContext == NULL || DispatchHandle == NULL) {
339     return EFI_INVALID_PARAMETER;
340   }
341 
342   //
343   // Create database record and add to database
344   //
345   Record = (DATABASE_RECORD *) AllocateZeroPool (sizeof (DATABASE_RECORD));
346   if (Record == NULL) {
347     return EFI_OUT_OF_RESOURCES;
348   }
349 
350   //
351   // Gather information about the registration request
352   //
353   Record->Callback          = DispatchFunction;
354   Record->CallbackContext   = RegisterContext;
355   CopyMem (&Record->ChildContext, RegisterContext, sizeof (QNC_SMM_CONTEXT));
356 
357   Qualified                 = QUALIFIED_PROTOCOL_FROM_GENERIC (This);
358 
359   Record->ProtocolType      = Qualified->Type;
360 
361   CopyMem (&Record->ContextFunctions, &mContextFunctions[Qualified->Type], sizeof (Record->ContextFunctions));
362   //
363   // Perform linked list housekeeping
364   //
365   Record->Signature = DATABASE_RECORD_SIGNATURE;
366 
367   switch (Qualified->Type) {
368   //
369   // By the end of this switch statement, we'll know the
370   // source description the child is registering for
371   //
372   case SxType:
373     //
374     // Check the validity of Context Type and Phase
375     //
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)
380         ) {
381       goto Error;
382     }
383 
384     InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
385     CopyMem (&Record->SrcDesc, &SX_SOURCE_DESC, sizeof (Record->SrcDesc));
386     //
387     // use default clear source function
388     //
389     break;
390 
391   case SwType:
392     if (RegisterContext->Sw.SwSmiInputValue == (UINTN)-1) {
393       //
394       // If SwSmiInputValue is set to (UINTN) -1 then a unique value will be assigned and returned in the structure.
395       //
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;
401           break;
402         }
403       }
404       if (RegisterContext->Sw.SwSmiInputValue == (UINTN)-1) {
405         Status = gSmst->SmmFreePool (Record);
406         return EFI_OUT_OF_RESOURCES;
407       }
408       //
409       // Update ChildContext again as SwSmiInputValue has been changed
410       //
411       CopyMem (&Record->ChildContext, RegisterContext, sizeof (QNC_SMM_CONTEXT));
412     }
413 
414     //
415     // Check the validity of Context Value
416     //
417     if (Record->ChildContext.Sw.SwSmiInputValue > MAXIMUM_SWI_VALUE) {
418       goto Error;
419     }
420 
421     if (EFI_ERROR (SmiInputValueDuplicateCheck (Record->ChildContext.Sw.SwSmiInputValue))) {
422       goto Error;
423     }
424 
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);
428     //
429     // use default clear source function
430     //
431     break;
432 
433   case GpiType:
434 
435     InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
436     CopyMem (&Record->SrcDesc, &GPI_SOURCE_DESC, sizeof (Record->SrcDesc));
437     //
438     // use default clear source function
439     //
440     break;
441 
442   case QNCnType:
443     //
444     // Check the validity of Context Type
445     //
446     if ((Record->ChildContext.QNCn.Type < IchnMch) || (Record->ChildContext.QNCn.Type >= NUM_ICHN_TYPES)) {
447       goto Error;
448     }
449 
450     InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
451     CopyMem (&Record->SrcDesc, &QNCN_SOURCE_DESCS[Record->ChildContext.QNCn.Type], sizeof (Record->SrcDesc));
452     Record->ClearSource = QNCSmmQNCnClearSource;
453     break;
454 
455   case PeriodicTimerType:
456 
457     Status = MapPeriodicTimerToSrcDesc (RegisterContext, &(Record->SrcDesc));
458     if (EFI_ERROR (Status)) {
459       goto Error;
460     }
461 
462     InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
463     Record->BufferSize = sizeof (EFI_SMM_PERIODIC_TIMER_CONTEXT);
464     Record->ClearSource = QNCSmmPeriodicTimerClearSource;
465     break;
466 
467   default:
468     goto Error;
469     break;
470   };
471 
472   if (Record->ClearSource == NULL) {
473     //
474     // Clear the SMI associated w/ the source using the default function
475     //
476     QNCSmmClearSource (&Record->SrcDesc);
477   } else {
478     //
479     // This source requires special handling to clear
480     //
481     Record->ClearSource (&Record->SrcDesc);
482   }
483 
484   QNCSmmEnableSource (&Record->SrcDesc);
485 
486   //
487   // Child's handle will be the address linked list link in the record
488   //
489   *DispatchHandle = (EFI_HANDLE) (&Record->Link);
490 
491   return EFI_SUCCESS;
492 
493 Error:
494   FreePool (Record);
495   //
496   // DEBUG((EFI_D_ERROR,"Free pool status %d\n", Status ));
497   //
498   return EFI_INVALID_PARAMETER;
499 }
500 
501 EFI_STATUS
QNCSmmCoreUnRegister(IN QNC_SMM_GENERIC_PROTOCOL * This,IN EFI_HANDLE DispatchHandle)502 QNCSmmCoreUnRegister (
503   IN QNC_SMM_GENERIC_PROTOCOL                         *This,
504   IN EFI_HANDLE                                        DispatchHandle
505   )
506 /*++
507 
508 Routine Description:
509 
510 Arguments:
511 
512 Returns:
513 
514 --*/
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
520 {
521   BOOLEAN         SafeToDisable;
522   DATABASE_RECORD *RecordToDelete;
523   DATABASE_RECORD *RecordInDb;
524   LIST_ENTRY      *LinkInDb;
525 
526   if (DispatchHandle == NULL) {
527     return EFI_INVALID_PARAMETER;
528   }
529 
530   if (BASE_CR (DispatchHandle, DATABASE_RECORD, Link)->Signature != DATABASE_RECORD_SIGNATURE) {
531     return EFI_INVALID_PARAMETER;
532   }
533 
534   RecordToDelete = DATABASE_RECORD_FROM_LINK (DispatchHandle);
535 
536   RemoveEntryList (&RecordToDelete->Link);
537   RecordToDelete->Signature = 0;
538 
539   //
540   // See if we can disable the source, reserved for future use since this might
541   //  not be the only criteria to disable
542   //
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;
549       break;
550     }
551     LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);
552   }
553   if (SafeToDisable) {
554     QNCSmmDisableSource( &RecordToDelete->SrcDesc );
555 }
556 
557   FreePool (RecordToDelete);
558 
559   return EFI_SUCCESS;
560 }
561 
562 /**
563   This function is the main entry point for an SMM handler dispatch
564   or communicate-based callback.
565 
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.
571 
572   @return Status Code
573 
574 **/
575 EFI_STATUS
QNCSmmCoreDispatcher(IN EFI_HANDLE DispatchHandle,IN CONST VOID * RegisterContext,IN OUT VOID * CommBuffer,IN OUT UINTN * CommBufferSize)576 QNCSmmCoreDispatcher (
577   IN     EFI_HANDLE               DispatchHandle,
578   IN     CONST VOID               *RegisterContext,
579   IN OUT VOID                     *CommBuffer,
580   IN OUT UINTN                    *CommBufferSize
581   )
582 {
583   //
584   // Used to prevent infinite loops
585   //
586   UINTN               EscapeCount;
587 
588   BOOLEAN             ContextsMatch;
589   BOOLEAN             ResetListSearch;
590   BOOLEAN             EosSet;
591   BOOLEAN             SxChildWasDispatched;
592   BOOLEAN             ChildWasDispatched;
593 
594   DATABASE_RECORD     *RecordInDb;
595   DATABASE_RECORD     ActiveRecordInDb;
596   LIST_ENTRY          *LinkInDb;
597   DATABASE_RECORD     *RecordToExhaust;
598   LIST_ENTRY          *LinkToExhaust;
599 
600   QNC_SMM_CONTEXT     Context;
601   VOID                *CommunicationBuffer;
602   UINTN               BufferSize;
603 
604   EFI_STATUS          Status;
605   UINT32              NewValue;
606 
607   QNC_SMM_SOURCE_DESC ActiveSource = NULL_SOURCE_DESC_INITIALIZER;
608 
609   EscapeCount           = 100;
610   ContextsMatch         = FALSE;
611   ResetListSearch       = FALSE;
612   EosSet                = FALSE;
613   SxChildWasDispatched  = FALSE;
614   Status                = EFI_WARN_INTERRUPT_SOURCE_PENDING;
615   ChildWasDispatched    = FALSE;
616 
617   //
618   // Mark all child handlers as not processed
619   //
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);
625   }
626 
627   //
628   // Preserve Index registers
629   //
630   SaveState ();
631 
632   if (!IsListEmpty (&mPrivateData.CallbackDataBase)) {
633     //
634     // We have children registered w/ us -- continue
635     //
636     while ((!EosSet) && (EscapeCount > 0)) {
637       EscapeCount--;
638 
639       //
640       // Reset this flag in order to be able to process multiple SMI Sources in one loop.
641       //
642       ResetListSearch = FALSE;
643 
644       LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);
645 
646       while ((!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) && (ResetListSearch == FALSE)) {
647         RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);
648         //
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
652         //
653         CopyMem (&ActiveRecordInDb, RecordInDb, sizeof (ActiveRecordInDb));
654 
655         //
656         // look for the first active source
657         //
658         if (!SourceIsActive (&RecordInDb->SrcDesc)) {
659           //
660           // Didn't find the source yet, keep looking
661           //
662           LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);
663 
664         } else {
665           //
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
668           //
669           if (RecordInDb->ProtocolType == SxType) {
670             SxChildWasDispatched = TRUE;
671           }
672           //
673           // "cache" the source description and don't query I/O anymore
674           //
675           CopyMem (&ActiveSource, &RecordInDb->SrcDesc, sizeof (ActiveSource));
676           LinkToExhaust = LinkInDb;
677 
678           //
679           // exhaust the rest of the queue looking for the same source
680           //
681           while (!IsNull (&mPrivateData.CallbackDataBase, LinkToExhaust)) {
682             RecordToExhaust = DATABASE_RECORD_FROM_LINK (LinkToExhaust);
683             LinkToExhaust = GetNextNode (&mPrivateData.CallbackDataBase, LinkToExhaust);
684             if (RecordToExhaust->Processed) {
685               //
686               // Record has already been processed.  Continue with next child handler.
687               //
688               continue;
689             }
690 
691             if (CompareSources (&RecordToExhaust->SrcDesc, &ActiveSource)) {
692               //
693               // These source descriptions are equal, so this callback should be
694               // dispatched.
695               //
696               if (RecordToExhaust->ContextFunctions.GetContext != NULL) {
697                 //
698                 // This child requires that we get a calling context from
699                 // hardware and compare that context to the one supplied
700                 // by the child.
701                 //
702                 ASSERT (RecordToExhaust->ContextFunctions.CmpContext != NULL);
703 
704                 //
705                 // Make sure contexts match before dispatching event to child
706                 //
707                 RecordToExhaust->ContextFunctions.GetContext (RecordToExhaust, &Context);
708                 ContextsMatch = RecordToExhaust->ContextFunctions.CmpContext (&Context, &RecordToExhaust->ChildContext);
709 
710               } else {
711                 //
712                 // This child doesn't require any more calling context beyond what
713                 // it supplied in registration.  Simply pass back what it gave us.
714                 //
715                 ASSERT (RecordToExhaust->Callback != NULL);
716                 ContextsMatch = TRUE;
717               }
718 
719               //
720               // Mark this child handler so it will not be processed again
721               //
722               RecordToExhaust->Processed = TRUE;
723 
724               if (ContextsMatch) {
725 
726                 if (RecordToExhaust->BufferSize != 0) {
727                   ASSERT (RecordToExhaust->ContextFunctions.GetBuffer != NULL);
728 
729                   RecordToExhaust->ContextFunctions.GetBuffer (RecordToExhaust);
730 
731                   CommunicationBuffer = &RecordToExhaust->CommBuffer;
732                   BufferSize = RecordToExhaust->BufferSize;
733                 } else {
734                   CommunicationBuffer = NULL;
735                   BufferSize = 0;
736                 }
737 
738                 ASSERT (RecordToExhaust->Callback != NULL);
739 
740                 RecordToExhaust->Callback (
741                                    (EFI_HANDLE) & RecordToExhaust->Link,
742                                    RecordToExhaust->CallbackContext,
743                                    CommunicationBuffer,
744                                    &BufferSize
745                                    );
746 
747                 ChildWasDispatched = TRUE;
748                 if (RecordToExhaust->ProtocolType == SxType) {
749                   SxChildWasDispatched = TRUE;
750                 }
751               }
752               //
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.
756               //
757               LinkToExhaust = GetFirstNode (&mPrivateData.CallbackDataBase);
758             }
759           }
760 
761           if (RecordInDb->ClearSource == NULL) {
762             //
763             // Clear the SMI associated w/ the source using the default function
764             //
765             QNCSmmClearSource (&ActiveSource);
766           } else {
767             //
768             // This source requires special handling to clear
769             //
770             ActiveRecordInDb.ClearSource (&ActiveSource);
771           }
772 
773           if (ChildWasDispatched) {
774             //
775             // The interrupt was handled and quiesced
776             //
777             Status = EFI_SUCCESS;
778           } else {
779             //
780             // The interrupt was not handled but quiesced
781             //
782             Status = EFI_WARN_INTERRUPT_SOURCE_QUIESCED;
783           }
784 
785           //
786           // Queue is empty, reset the search
787           //
788           ResetListSearch = TRUE;
789 
790         }
791       }
792       EosSet = QNCSmmSetAndCheckEos ();
793     }
794   }
795   //
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
801   // to this driver.
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
804   // and handle it.
805   // If not, we don't want to exit and have the foreground app. clear EOS without letting
806   // these other sources get serviced.
807   //
808   ASSERT (EscapeCount > 0);
809 
810   //
811   // Restore Index registers
812   //
813   RestoreState ();
814 
815   if (SxChildWasDispatched) {
816     //
817     // A child of the SmmSxDispatch protocol was dispatched during this call;
818     // put the system to sleep.
819     //
820     QNCSmmSxGoToSleep ();
821   }
822 
823   //
824   // Ensure that SMI signal pin indicator is clear at the end of SMM handling.
825   //
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);
829 
830   return Status;
831 }
832