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-2015 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->ChildContext      = *RegisterContext;
355 
356   Qualified                 = QUALIFIED_PROTOCOL_FROM_GENERIC (This);
357 
358   Record->ProtocolType      = Qualified->Type;
359 
360   CopyMem (&Record->ContextFunctions, &mContextFunctions[Qualified->Type], sizeof (Record->ContextFunctions));
361   //
362   // Perform linked list housekeeping
363   //
364   Record->Signature = DATABASE_RECORD_SIGNATURE;
365 
366   switch (Qualified->Type) {
367   //
368   // By the end of this switch statement, we'll know the
369   // source description the child is registering for
370   //
371   case SxType:
372     //
373     // Check the validity of Context Type and Phase
374     //
375     if ((Record->ChildContext.Sx.Type < SxS0) ||
376         (Record->ChildContext.Sx.Type >= EfiMaximumSleepType) ||
377         (Record->ChildContext.Sx.Phase < SxEntry) ||
378         (Record->ChildContext.Sx.Phase >= EfiMaximumPhase)
379         ) {
380       goto Error;
381     }
382 
383     InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
384     CopyMem (&Record->SrcDesc, &SX_SOURCE_DESC, sizeof (Record->SrcDesc));
385     //
386     // use default clear source function
387     //
388     break;
389 
390   case SwType:
391     if (RegisterContext->Sw.SwSmiInputValue == (UINTN)-1) {
392       //
393       // If SwSmiInputValue is set to (UINTN) -1 then a unique value will be assigned and returned in the structure.
394       //
395       Status = EFI_NOT_FOUND;
396       for (Index = 1; Index < MAXIMUM_SWI_VALUE; Index++) {
397         Status = SmiInputValueDuplicateCheck (Index);
398         if (!EFI_ERROR (Status)) {
399           RegisterContext->Sw.SwSmiInputValue = Index;
400           break;
401         }
402       }
403       if (RegisterContext->Sw.SwSmiInputValue == (UINTN)-1) {
404         Status = gSmst->SmmFreePool (Record);
405         return EFI_OUT_OF_RESOURCES;
406       }
407       //
408       // Update ChildContext again as SwSmiInputValue has been changed
409       //
410       Record->ChildContext = *RegisterContext;
411     }
412 
413     //
414     // Check the validity of Context Value
415     //
416     if (Record->ChildContext.Sw.SwSmiInputValue > MAXIMUM_SWI_VALUE) {
417       goto Error;
418     }
419 
420     if (EFI_ERROR (SmiInputValueDuplicateCheck (Record->ChildContext.Sw.SwSmiInputValue))) {
421       goto Error;
422     }
423 
424     InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
425     CopyMem (&Record->SrcDesc, &SW_SOURCE_DESC, sizeof (Record->SrcDesc));
426     Record->BufferSize = sizeof (EFI_SMM_SW_REGISTER_CONTEXT);
427     //
428     // use default clear source function
429     //
430     break;
431 
432   case GpiType:
433 
434     InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
435     CopyMem (&Record->SrcDesc, &GPI_SOURCE_DESC, sizeof (Record->SrcDesc));
436     //
437     // use default clear source function
438     //
439     break;
440 
441   case QNCnType:
442     //
443     // Check the validity of Context Type
444     //
445     if ((Record->ChildContext.QNCn.Type < IchnMch) || (Record->ChildContext.QNCn.Type >= NUM_ICHN_TYPES)) {
446       goto Error;
447     }
448 
449     InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
450     CopyMem (&Record->SrcDesc, &QNCN_SOURCE_DESCS[Record->ChildContext.QNCn.Type], sizeof (Record->SrcDesc));
451     Record->ClearSource = QNCSmmQNCnClearSource;
452     break;
453 
454   case PeriodicTimerType:
455 
456     Status = MapPeriodicTimerToSrcDesc (RegisterContext, &(Record->SrcDesc));
457     if (EFI_ERROR (Status)) {
458       goto Error;
459     }
460 
461     InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
462     Record->BufferSize = sizeof (EFI_SMM_PERIODIC_TIMER_CONTEXT);
463     Record->ClearSource = QNCSmmPeriodicTimerClearSource;
464     break;
465 
466   default:
467     goto Error;
468     break;
469   };
470 
471   if (Record->ClearSource == NULL) {
472     //
473     // Clear the SMI associated w/ the source using the default function
474     //
475     QNCSmmClearSource (&Record->SrcDesc);
476   } else {
477     //
478     // This source requires special handling to clear
479     //
480     Record->ClearSource (&Record->SrcDesc);
481   }
482 
483   QNCSmmEnableSource (&Record->SrcDesc);
484 
485   //
486   // Child's handle will be the address linked list link in the record
487   //
488   *DispatchHandle = (EFI_HANDLE) (&Record->Link);
489 
490   return EFI_SUCCESS;
491 
492 Error:
493   FreePool (Record);
494   //
495   // DEBUG((EFI_D_ERROR,"Free pool status %d\n", Status ));
496   //
497   return EFI_INVALID_PARAMETER;
498 }
499 
500 EFI_STATUS
QNCSmmCoreUnRegister(IN QNC_SMM_GENERIC_PROTOCOL * This,IN EFI_HANDLE DispatchHandle)501 QNCSmmCoreUnRegister (
502   IN QNC_SMM_GENERIC_PROTOCOL                         *This,
503   IN EFI_HANDLE                                        DispatchHandle
504   )
505 /*++
506 
507 Routine Description:
508 
509 Arguments:
510 
511 Returns:
512 
513 --*/
514 // GC_TODO:    This - add argument and description to function comment
515 // GC_TODO:    DispatchHandle - add argument and description to function comment
516 // GC_TODO:    EFI_INVALID_PARAMETER - add return value to function comment
517 // GC_TODO:    EFI_INVALID_PARAMETER - add return value to function comment
518 // GC_TODO:    EFI_SUCCESS - add return value to function comment
519 {
520   BOOLEAN         SafeToDisable;
521   DATABASE_RECORD *RecordToDelete;
522   DATABASE_RECORD *RecordInDb;
523   LIST_ENTRY      *LinkInDb;
524 
525   if (DispatchHandle == NULL) {
526     return EFI_INVALID_PARAMETER;
527   }
528 
529   if (BASE_CR (DispatchHandle, DATABASE_RECORD, Link)->Signature != DATABASE_RECORD_SIGNATURE) {
530     return EFI_INVALID_PARAMETER;
531   }
532 
533   RecordToDelete = DATABASE_RECORD_FROM_LINK (DispatchHandle);
534 
535   RemoveEntryList (&RecordToDelete->Link);
536   RecordToDelete->Signature = 0;
537 
538   //
539   // See if we can disable the source, reserved for future use since this might
540   //  not be the only criteria to disable
541   //
542   SafeToDisable = TRUE;
543   LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);
544   while(!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {
545     RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);
546     if (CompareEnables (&RecordToDelete->SrcDesc, &RecordInDb->SrcDesc)) {
547       SafeToDisable = FALSE;
548       break;
549     }
550     LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);
551   }
552   if (SafeToDisable) {
553     QNCSmmDisableSource( &RecordToDelete->SrcDesc );
554 }
555 
556   FreePool (RecordToDelete);
557 
558   return EFI_SUCCESS;
559 }
560 
561 /**
562   This function is the main entry point for an SMM handler dispatch
563   or communicate-based callback.
564 
565   @param  DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().
566   @param  RegisterContext Points to an optional handler context which was specified when the handler was registered.
567   @param  CommBuffer      A pointer to a collection of data in memory that will
568                           be conveyed from a non-SMM environment into an SMM environment.
569   @param  CommBufferSize  The size of the CommBuffer.
570 
571   @return Status Code
572 
573 **/
574 EFI_STATUS
QNCSmmCoreDispatcher(IN EFI_HANDLE DispatchHandle,IN CONST VOID * RegisterContext,IN OUT VOID * CommBuffer,IN OUT UINTN * CommBufferSize)575 QNCSmmCoreDispatcher (
576   IN     EFI_HANDLE               DispatchHandle,
577   IN     CONST VOID               *RegisterContext,
578   IN OUT VOID                     *CommBuffer,
579   IN OUT UINTN                    *CommBufferSize
580   )
581 {
582   //
583   // Used to prevent infinite loops
584   //
585   UINTN               EscapeCount;
586 
587   BOOLEAN             ContextsMatch;
588   BOOLEAN             ResetListSearch;
589   BOOLEAN             EosSet;
590   BOOLEAN             SxChildWasDispatched;
591   BOOLEAN             ChildWasDispatched;
592 
593   DATABASE_RECORD     *RecordInDb;
594   LIST_ENTRY          *LinkInDb;
595   DATABASE_RECORD     *RecordToExhaust;
596   LIST_ENTRY          *LinkToExhaust;
597 
598   QNC_SMM_CONTEXT     Context;
599   VOID                *CommunicationBuffer;
600   UINTN               BufferSize;
601 
602   EFI_STATUS          Status;
603   UINT32              NewValue;
604 
605   QNC_SMM_SOURCE_DESC ActiveSource = NULL_SOURCE_DESC_INITIALIZER;
606 
607   EscapeCount           = 100;
608   ContextsMatch         = FALSE;
609   ResetListSearch       = FALSE;
610   EosSet                = FALSE;
611   SxChildWasDispatched  = FALSE;
612   Status                = EFI_WARN_INTERRUPT_SOURCE_PENDING;
613   ChildWasDispatched    = FALSE;
614 
615   //
616   // Preserve Index registers
617   //
618   SaveState ();
619 
620   if (!IsListEmpty (&mPrivateData.CallbackDataBase)) {
621     //
622     // We have children registered w/ us -- continue
623     //
624     while ((!EosSet) && (EscapeCount > 0)) {
625       EscapeCount--;
626 
627       //
628       // Reset this flag in order to be able to process multiple SMI Sources in one loop.
629       //
630       ResetListSearch = FALSE;
631 
632       LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);
633 
634       while ((!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) && (ResetListSearch == FALSE)) {
635         RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);
636 
637         //
638         // look for the first active source
639         //
640         if (!SourceIsActive (&RecordInDb->SrcDesc)) {
641           //
642           // Didn't find the source yet, keep looking
643           //
644           LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);
645 
646         } else {
647           //
648           // We found a source. If this is a sleep type, we have to go to
649           // appropriate sleep state anyway.No matter there is sleep child or not
650           //
651           if (RecordInDb->ProtocolType == SxType) {
652             SxChildWasDispatched = TRUE;
653           }
654           //
655           // "cache" the source description and don't query I/O anymore
656           //
657           CopyMem (&ActiveSource, &RecordInDb->SrcDesc, sizeof (ActiveSource));
658           LinkToExhaust = LinkInDb;
659 
660           //
661           // exhaust the rest of the queue looking for the same source
662           //
663           while (!IsNull (&mPrivateData.CallbackDataBase, LinkToExhaust)) {
664             RecordToExhaust = DATABASE_RECORD_FROM_LINK (LinkToExhaust);
665 
666             if (CompareSources (&RecordToExhaust->SrcDesc, &ActiveSource)) {
667               //
668               // These source descriptions are equal, so this callback should be
669               // dispatched.
670               //
671               if (RecordToExhaust->ContextFunctions.GetContext != NULL) {
672                 //
673                 // This child requires that we get a calling context from
674                 // hardware and compare that context to the one supplied
675                 // by the child.
676                 //
677                 ASSERT (RecordToExhaust->ContextFunctions.CmpContext != NULL);
678 
679                 //
680                 // Make sure contexts match before dispatching event to child
681                 //
682                 RecordToExhaust->ContextFunctions.GetContext (RecordToExhaust, &Context);
683                 ContextsMatch = RecordToExhaust->ContextFunctions.CmpContext (&Context, &RecordToExhaust->ChildContext);
684 
685               } else {
686                 //
687                 // This child doesn't require any more calling context beyond what
688                 // it supplied in registration.  Simply pass back what it gave us.
689                 //
690                 ASSERT (RecordToExhaust->Callback != NULL);
691                 Context       = RecordToExhaust->ChildContext;
692                 ContextsMatch = TRUE;
693               }
694 
695               if (ContextsMatch) {
696 
697                 if (RecordToExhaust->BufferSize != 0) {
698                   ASSERT (RecordToExhaust->ContextFunctions.GetBuffer != NULL);
699 
700                   RecordToExhaust->ContextFunctions.GetBuffer (RecordToExhaust);
701 
702                   CommunicationBuffer = &RecordToExhaust->CommBuffer;
703                   BufferSize = RecordToExhaust->BufferSize;
704                 } else {
705                   CommunicationBuffer = NULL;
706                   BufferSize = 0;
707                 }
708 
709                 ASSERT (RecordToExhaust->Callback != NULL);
710 
711                 RecordToExhaust->Callback (
712                                    (EFI_HANDLE) & RecordToExhaust->Link,
713                                    &Context,
714                                    CommunicationBuffer,
715                                    &BufferSize
716                                    );
717 
718                 ChildWasDispatched = TRUE;
719                 if (RecordToExhaust->ProtocolType == SxType) {
720                   SxChildWasDispatched = TRUE;
721                 }
722               }
723             }
724             //
725             // Get next record in DB
726             //
727             LinkToExhaust = GetNextNode (&mPrivateData.CallbackDataBase, &RecordToExhaust->Link);
728           }
729 
730           if (RecordInDb->ClearSource == NULL) {
731             //
732             // Clear the SMI associated w/ the source using the default function
733             //
734             QNCSmmClearSource (&ActiveSource);
735           } else {
736             //
737             // This source requires special handling to clear
738             //
739             RecordInDb->ClearSource (&ActiveSource);
740           }
741 
742           if (ChildWasDispatched) {
743             //
744             // The interrupt was handled and quiesced
745             //
746             Status = EFI_SUCCESS;
747           } else {
748             //
749             // The interrupt was not handled but quiesced
750             //
751             Status = EFI_WARN_INTERRUPT_SOURCE_QUIESCED;
752           }
753 
754           //
755           // Queue is empty, reset the search
756           //
757           ResetListSearch = TRUE;
758 
759         }
760       }
761       EosSet = QNCSmmSetAndCheckEos ();
762     }
763   }
764   //
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
770   // to this driver.
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
773   // and handle it.
774   // If not, we don't want to exit and have the foreground app. clear EOS without letting
775   // these other sources get serviced.
776   //
777   ASSERT (EscapeCount > 0);
778 
779   //
780   // Restore Index registers
781   //
782   RestoreState ();
783 
784   if (SxChildWasDispatched) {
785     //
786     // A child of the SmmSxDispatch protocol was dispatched during this call;
787     // put the system to sleep.
788     //
789     QNCSmmSxGoToSleep ();
790   }
791 
792   //
793   // Ensure that SMI signal pin indicator is clear at the end of SMM handling.
794   //
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);
798 
799   return Status;
800 }
801