1/** @file
2This driver is responsible for the registration of child drivers
3and the abstraction of the QNC SMI sources.
4
5Copyright (c) 2013-2015 Intel Corporation.
6
7This program and the accompanying materials
8are licensed and made available under the terms and conditions of the BSD License
9which accompanies this distribution.  The full text of the license may be found at
10http://opensource.org/licenses/bsd-license.php
11
12THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13WITHOUT 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//
33UINT32                    mPciData;
34UINT32                    mPciAddress;
35
36PRIVATE_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
117CONTEXT_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//
156EFI_STATUS
157QNCSmmCoreDispatcher (
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
165UINTN
166DevicePathSize (
167  IN EFI_DEVICE_PATH_PROTOCOL  *DevicePath
168  );
169
170//
171// /////////////////////////////////////////////////////////////////////////////
172// FUNCTIONS
173//
174// Driver entry point
175//
176EFI_STATUS
177EFIAPI
178InitializeQNCSmmDispatcher (
179  IN EFI_HANDLE        ImageHandle,
180  IN EFI_SYSTEM_TABLE  *SystemTable
181  )
182/*++
183
184Routine Description:
185
186  Initializes the QNC SMM Dispatcher
187
188Arguments:
189
190  ImageHandle   - Pointer to the loaded image protocol for this driver
191  SystemTable   - Pointer to the EFI System Table
192
193Returns:
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
222EFI_STATUS
223SaveState (
224  VOID
225  )
226/*++
227
228Routine Description:
229
230  Save Index registers to avoid corrupting the foreground environment
231
232Arguments:
233  None
234
235Returns:
236  Status - EFI_SUCCESS
237
238--*/
239{
240  mPciAddress = IoRead32 (EFI_PCI_ADDRESS_PORT);
241  return EFI_SUCCESS;
242}
243
244EFI_STATUS
245RestoreState (
246  VOID
247  )
248/*++
249
250Routine Description:
251
252  Restore Index registers to avoid corrupting the foreground environment
253
254Arguments:
255  None
256
257Returns:
258  Status - EFI_SUCCESS
259
260--*/
261{
262  IoWrite32 (EFI_PCI_ADDRESS_PORT, mPciAddress);
263  return EFI_SUCCESS;
264}
265
266EFI_STATUS
267SmiInputValueDuplicateCheck (
268  UINTN           FedSwSmiInputValue
269  )
270/*++
271
272Routine Description:
273
274  Check the Fed SwSmiInputValue to see if there is a duplicated one in the database
275
276Arguments:
277  None
278
279Returns:
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
305EFI_STATUS
306QNCSmmCoreRegister (
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
314Routine Description:
315
316Arguments:
317
318Returns:
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
492Error:
493  FreePool (Record);
494  //
495  // DEBUG((EFI_D_ERROR,"Free pool status %d\n", Status ));
496  //
497  return EFI_INVALID_PARAMETER;
498}
499
500EFI_STATUS
501QNCSmmCoreUnRegister (
502  IN QNC_SMM_GENERIC_PROTOCOL                         *This,
503  IN EFI_HANDLE                                        DispatchHandle
504  )
505/*++
506
507Routine Description:
508
509Arguments:
510
511Returns:
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**/
574EFI_STATUS
575QNCSmmCoreDispatcher (
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