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