コード例 #1
0
ファイル: QNCSmmCore.c プロジェクト: b-man/edk2
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
  )
/*++

Routine Description:

Arguments:

Returns:

--*/
// GC_TODO:    This - add argument and description to function comment
// GC_TODO:    DispatchFunction - add argument and description to function comment
// GC_TODO:    RegisterContext - add argument and description to function comment
// GC_TODO:    DispatchHandle - add argument and description to function comment
// GC_TODO:    EFI_OUT_OF_RESOURCES - add return value to function comment
// GC_TODO:    EFI_INVALID_PARAMETER - add return value to function comment
// GC_TODO:    EFI_SUCCESS - add return value to function comment
// GC_TODO:    EFI_INVALID_PARAMETER - add return value to function comment
{
  EFI_STATUS                  Status;
  DATABASE_RECORD             *Record;
  QNC_SMM_QUALIFIED_PROTOCOL  *Qualified;
  INTN                        Index;

  //
  // Check for invalid parameter
  //
  if (This == NULL || RegisterContext == NULL || DispatchHandle == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Create database record and add to database
  //
  Record = (DATABASE_RECORD *) AllocateZeroPool (sizeof (DATABASE_RECORD));
  if (Record == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Gather information about the registration request
  //
  Record->Callback          = DispatchFunction;
  Record->CallbackContext   = RegisterContext;
  CopyMem (&Record->ChildContext, RegisterContext, sizeof (QNC_SMM_CONTEXT));

  Qualified                 = QUALIFIED_PROTOCOL_FROM_GENERIC (This);

  Record->ProtocolType      = Qualified->Type;

  CopyMem (&Record->ContextFunctions, &mContextFunctions[Qualified->Type], sizeof (Record->ContextFunctions));
  //
  // Perform linked list housekeeping
  //
  Record->Signature = DATABASE_RECORD_SIGNATURE;

  switch (Qualified->Type) {
  //
  // By the end of this switch statement, we'll know the
  // source description the child is registering for
  //
  case SxType:
    //
    // Check the validity of Context Type and Phase
    //
    if ((Record->ChildContext.Sx.Type < SxS0) ||
        (Record->ChildContext.Sx.Type >= EfiMaximumSleepType) ||
        (Record->ChildContext.Sx.Phase < SxEntry) ||
        (Record->ChildContext.Sx.Phase >= EfiMaximumPhase)
        ) {
      goto Error;
    }

    InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
    CopyMem (&Record->SrcDesc, &SX_SOURCE_DESC, sizeof (Record->SrcDesc));
    //
    // use default clear source function
    //
    break;

  case SwType:
    if (RegisterContext->Sw.SwSmiInputValue == (UINTN)-1) {
      //
      // If SwSmiInputValue is set to (UINTN) -1 then a unique value will be assigned and returned in the structure.
      //
      Status = EFI_NOT_FOUND;
      for (Index = 1; Index < MAXIMUM_SWI_VALUE; Index++) {
        Status = SmiInputValueDuplicateCheck (Index);
        if (!EFI_ERROR (Status)) {
          RegisterContext->Sw.SwSmiInputValue = Index;
          break;
        }
      }
      if (RegisterContext->Sw.SwSmiInputValue == (UINTN)-1) {
        Status = gSmst->SmmFreePool (Record);
        return EFI_OUT_OF_RESOURCES;
      }
      //
      // Update ChildContext again as SwSmiInputValue has been changed
      //
      CopyMem (&Record->ChildContext, RegisterContext, sizeof (QNC_SMM_CONTEXT));
    }

    //
    // Check the validity of Context Value
    //
    if (Record->ChildContext.Sw.SwSmiInputValue > MAXIMUM_SWI_VALUE) {
      goto Error;
    }

    if (EFI_ERROR (SmiInputValueDuplicateCheck (Record->ChildContext.Sw.SwSmiInputValue))) {
      goto Error;
    }

    InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
    CopyMem (&Record->SrcDesc, &SW_SOURCE_DESC, sizeof (Record->SrcDesc));
    Record->BufferSize = sizeof (EFI_SMM_SW_REGISTER_CONTEXT);
    //
    // use default clear source function
    //
    break;

  case GpiType:

    InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
    CopyMem (&Record->SrcDesc, &GPI_SOURCE_DESC, sizeof (Record->SrcDesc));
    //
    // use default clear source function
    //
    break;

  case QNCnType:
    //
    // Check the validity of Context Type
    //
    if ((Record->ChildContext.QNCn.Type < IchnMch) || (Record->ChildContext.QNCn.Type >= NUM_ICHN_TYPES)) {
      goto Error;
    }

    InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
    CopyMem (&Record->SrcDesc, &QNCN_SOURCE_DESCS[Record->ChildContext.QNCn.Type], sizeof (Record->SrcDesc));
    Record->ClearSource = QNCSmmQNCnClearSource;
    break;

  case PeriodicTimerType:

    Status = MapPeriodicTimerToSrcDesc (RegisterContext, &(Record->SrcDesc));
    if (EFI_ERROR (Status)) {
      goto Error;
    }

    InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
    Record->BufferSize = sizeof (EFI_SMM_PERIODIC_TIMER_CONTEXT);
    Record->ClearSource = QNCSmmPeriodicTimerClearSource;
    break;

  default:
    goto Error;
    break;
  };

  if (Record->ClearSource == NULL) {
    //
    // Clear the SMI associated w/ the source using the default function
    //
    QNCSmmClearSource (&Record->SrcDesc);
  } else {
    //
    // This source requires special handling to clear
    //
    Record->ClearSource (&Record->SrcDesc);
  }

  QNCSmmEnableSource (&Record->SrcDesc);

  //
  // Child's handle will be the address linked list link in the record
  //
  *DispatchHandle = (EFI_HANDLE) (&Record->Link);

  return EFI_SUCCESS;

Error:
  FreePool (Record);
  //
  // DEBUG((EFI_D_ERROR,"Free pool status %d\n", Status ));
  //
  return EFI_INVALID_PARAMETER;
}
コード例 #2
0
ファイル: QNCSmmCore.c プロジェクト: b-man/edk2
/**
  This function is the main entry point for an SMM handler dispatch
  or communicate-based callback.

  @param  DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().
  @param  RegisterContext Points to an optional handler context which was specified when the handler was registered.
  @param  CommBuffer      A pointer to a collection of data in memory that will
                          be conveyed from a non-SMM environment into an SMM environment.
  @param  CommBufferSize  The size of the CommBuffer.

  @return Status Code

**/
EFI_STATUS
QNCSmmCoreDispatcher (
  IN     EFI_HANDLE               DispatchHandle,
  IN     CONST VOID               *RegisterContext,
  IN OUT VOID                     *CommBuffer,
  IN OUT UINTN                    *CommBufferSize
  )
{
  //
  // Used to prevent infinite loops
  //
  UINTN               EscapeCount;

  BOOLEAN             ContextsMatch;
  BOOLEAN             ResetListSearch;
  BOOLEAN             EosSet;
  BOOLEAN             SxChildWasDispatched;
  BOOLEAN             ChildWasDispatched;

  DATABASE_RECORD     *RecordInDb;
  DATABASE_RECORD     ActiveRecordInDb;
  LIST_ENTRY          *LinkInDb;
  DATABASE_RECORD     *RecordToExhaust;
  LIST_ENTRY          *LinkToExhaust;

  QNC_SMM_CONTEXT     Context;
  VOID                *CommunicationBuffer;
  UINTN               BufferSize;

  EFI_STATUS          Status;
  UINT32              NewValue;

  QNC_SMM_SOURCE_DESC ActiveSource = NULL_SOURCE_DESC_INITIALIZER;

  EscapeCount           = 100;
  ContextsMatch         = FALSE;
  ResetListSearch       = FALSE;
  EosSet                = FALSE;
  SxChildWasDispatched  = FALSE;
  Status                = EFI_WARN_INTERRUPT_SOURCE_PENDING;
  ChildWasDispatched    = FALSE;

  //
  // Mark all child handlers as not processed
  //
  LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);
  while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {
    RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);
    RecordInDb->Processed = FALSE;
    LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, LinkInDb);
  }

  //
  // Preserve Index registers
  //
  SaveState ();

  if (!IsListEmpty (&mPrivateData.CallbackDataBase)) {
    //
    // We have children registered w/ us -- continue
    //
    while ((!EosSet) && (EscapeCount > 0)) {
      EscapeCount--;

      //
      // Reset this flag in order to be able to process multiple SMI Sources in one loop.
      //
      ResetListSearch = FALSE;

      LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);

      while ((!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) && (ResetListSearch == FALSE)) {
        RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);
        //
        // Make a copy of the record that contains an active SMI source,
        // because un-register maybe invoked in callback function and
        // RecordInDb maybe released
        //
        CopyMem (&ActiveRecordInDb, RecordInDb, sizeof (ActiveRecordInDb));

        //
        // look for the first active source
        //
        if (!SourceIsActive (&RecordInDb->SrcDesc)) {
          //
          // Didn't find the source yet, keep looking
          //
          LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);

        } else {
          //
          // We found a source. If this is a sleep type, we have to go to
          // appropriate sleep state anyway.No matter there is sleep child or not
          //
          if (RecordInDb->ProtocolType == SxType) {
            SxChildWasDispatched = TRUE;
          }
          //
          // "cache" the source description and don't query I/O anymore
          //
          CopyMem (&ActiveSource, &RecordInDb->SrcDesc, sizeof (ActiveSource));
          LinkToExhaust = LinkInDb;

          //
          // exhaust the rest of the queue looking for the same source
          //
          while (!IsNull (&mPrivateData.CallbackDataBase, LinkToExhaust)) {
            RecordToExhaust = DATABASE_RECORD_FROM_LINK (LinkToExhaust);
            LinkToExhaust = GetNextNode (&mPrivateData.CallbackDataBase, LinkToExhaust);
            if (RecordToExhaust->Processed) {
              //
              // Record has already been processed.  Continue with next child handler.
              //
              continue;
            }

            if (CompareSources (&RecordToExhaust->SrcDesc, &ActiveSource)) {
              //
              // These source descriptions are equal, so this callback should be
              // dispatched.
              //
              if (RecordToExhaust->ContextFunctions.GetContext != NULL) {
                //
                // This child requires that we get a calling context from
                // hardware and compare that context to the one supplied
                // by the child.
                //
                ASSERT (RecordToExhaust->ContextFunctions.CmpContext != NULL);

                //
                // Make sure contexts match before dispatching event to child
                //
                RecordToExhaust->ContextFunctions.GetContext (RecordToExhaust, &Context);
                ContextsMatch = RecordToExhaust->ContextFunctions.CmpContext (&Context, &RecordToExhaust->ChildContext);

              } else {
                //
                // This child doesn't require any more calling context beyond what
                // it supplied in registration.  Simply pass back what it gave us.
                //
                ASSERT (RecordToExhaust->Callback != NULL);
                ContextsMatch = TRUE;
              }

              //
              // Mark this child handler so it will not be processed again
              //
              RecordToExhaust->Processed = TRUE;

              if (ContextsMatch) {

                if (RecordToExhaust->BufferSize != 0) {
                  ASSERT (RecordToExhaust->ContextFunctions.GetBuffer != NULL);

                  RecordToExhaust->ContextFunctions.GetBuffer (RecordToExhaust);

                  CommunicationBuffer = &RecordToExhaust->CommBuffer;
                  BufferSize = RecordToExhaust->BufferSize;
                } else {
                  CommunicationBuffer = NULL;
                  BufferSize = 0;
                }

                ASSERT (RecordToExhaust->Callback != NULL);

                RecordToExhaust->Callback (
                                   (EFI_HANDLE) & RecordToExhaust->Link,
                                   RecordToExhaust->CallbackContext,
                                   CommunicationBuffer,
                                   &BufferSize
                                   );

                ChildWasDispatched = TRUE;
                if (RecordToExhaust->ProtocolType == SxType) {
                  SxChildWasDispatched = TRUE;
                }
              }
              //
              // Can not use RecordInDb after this point because Callback may have unregistered RecordInDb
              // Restart processing of SMI handlers from the begining of the linked list because the
              // state of the linked listed may have been modified due to unregister actions in the Callback.
              //
              LinkToExhaust = GetFirstNode (&mPrivateData.CallbackDataBase);
            }
          }

          if (ActiveRecordInDb.ClearSource == NULL) {
            //
            // Clear the SMI associated w/ the source using the default function
            //
            QNCSmmClearSource (&ActiveSource);
          } else {
            //
            // This source requires special handling to clear
            //
            ActiveRecordInDb.ClearSource (&ActiveSource);
          }

          if (ChildWasDispatched) {
            //
            // The interrupt was handled and quiesced
            //
            Status = EFI_SUCCESS;
          } else {
            //
            // The interrupt was not handled but quiesced
            //
            Status = EFI_WARN_INTERRUPT_SOURCE_QUIESCED;
          }

          //
          // Queue is empty, reset the search
          //
          ResetListSearch = TRUE;

        }
      }
      EosSet = QNCSmmSetAndCheckEos ();
    }
  }
  //
  // If you arrive here, there are two possible reasons:
  // (1) you've got problems with clearing the SMI status bits in the
  // ACPI table.  If you don't properly clear the SMI bits, then you won't be able to set the
  // EOS bit.  If this happens too many times, the loop exits.
  // (2) there was a SMM communicate for callback messages that was received prior
  // to this driver.
  // If there is an asynchronous SMI that occurs while processing the Callback, let
  // all of the drivers (including this one) have an opportunity to scan for the SMI
  // and handle it.
  // If not, we don't want to exit and have the foreground app. clear EOS without letting
  // these other sources get serviced.
  //
  ASSERT (EscapeCount > 0);

  //
  // Restore Index registers
  //
  RestoreState ();

  if (SxChildWasDispatched) {
    //
    // A child of the SmmSxDispatch protocol was dispatched during this call;
    // put the system to sleep.
    //
    QNCSmmSxGoToSleep ();
  }

  //
  // Ensure that SMI signal pin indicator is clear at the end of SMM handling.
  //
  NewValue = QNCPortRead (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QUARK_NC_HOST_BRIDGE_HLEGACY_REG);
  NewValue &= ~(HLEGACY_SMI_PIN_VALUE);
  QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QUARK_NC_HOST_BRIDGE_HLEGACY_REG, NewValue);

  return Status;
}
コード例 #3
0
ファイル: QNCSmmPeriodicTimer.c プロジェクト: OznOg/edk2
VOID
QNCSmmPeriodicTimerProgramTimers (
  VOID
  )
{
  UINT32            GpePmcwValue;
  SUPPORTED_TIMER   Timer;
  DATABASE_RECORD   *RecordInDb;
  LIST_ENTRY        *LinkInDb;
  TIMER_INTERVAL    *TimerInterval;

  //
  // Find the minimum required interval for each timer
  //
  for (Timer = (SUPPORTED_TIMER)0; Timer < NUM_TIMERS; Timer++) {
    mTimers[Timer].MinReqInterval = ~(UINT64)0x0;
    mTimers[Timer].NumChildren = 0;
  }
  LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);
  while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {
    RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);
    if (RecordInDb->ProtocolType == PeriodicTimerType) {
      //
      // This child is registerd with the PeriodicTimer protocol
      //
      TimerInterval = ContextToTimerInterval (&RecordInDb->ChildContext);

      if(TimerInterval != NULL) {
        Timer = (SUPPORTED_TIMER)((TIMER_INTERVAL *) (TimerInterval))->AssociatedTimer;

        ASSERT (Timer >= 0 && Timer < NUM_TIMERS);

        if (mTimers[Timer].MinReqInterval > RecordInDb->ChildContext.PeriodicTimer.SmiTickInterval) {
          mTimers[Timer].MinReqInterval = RecordInDb->ChildContext.PeriodicTimer.SmiTickInterval;
        }
        mTimers[Timer].NumChildren++;
      }
    }
    LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);
  }

  //
  // Program the hardware
  //
  GpePmcwValue = 0;
  if (mTimers[PERIODIC_TIMER].NumChildren > 0) {
    switch (mTimers[PERIODIC_TIMER].MinReqInterval) {

    case TIME_64s:
      GpePmcwValue = INDEX_TIME_64s;
      mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_64s;
      break;

    case TIME_32s:
      GpePmcwValue = INDEX_TIME_32s;
      mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_32s;
      break;

    case TIME_16s:
      GpePmcwValue = INDEX_TIME_16s;
      mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_16s;
      break;

    case TIME_8s:
      GpePmcwValue = INDEX_TIME_8s;
      mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_8s;
      break;

    case TIME_64ms:
      GpePmcwValue = INDEX_TIME_64ms;
      mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_64ms;
      break;

    case TIME_32ms:
      GpePmcwValue = INDEX_TIME_32ms;
      mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_32ms;
      break;

    case TIME_16ms:
      GpePmcwValue = INDEX_TIME_16ms;
      mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_16ms;
      break;

    case TIME_1_5ms:
      GpePmcwValue = INDEX_TIME_1_5ms;
      mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_1_5ms;
      break;

    default:
      ASSERT (FALSE);
      break;
    };

    GpePmcwValue |= B_QNC_GPE0BLK_PMCW_PSE;

    IoOr32(((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + R_QNC_GPE0BLK_PMCW), GpePmcwValue);

    //
    // Restart the timer here, just need to clear the SMI
    //
    QNCSmmClearSource (&mTIMER_SOURCE_DESCS[PERIODIC_TIMER]);
  } else {
    QNCSmmDisableSource (&mTIMER_SOURCE_DESCS[PERIODIC_TIMER]);
  }
}