Home | History | Annotate | Download | only in DatahubStatusCodeHandlerDxe
      1 /** @file
      2   Data Hub status code worker.
      3 
      4   Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
      5   This program and the accompanying materials
      6   are licensed and made available under the terms and conditions of the BSD License
      7   which accompanies this distribution.  The full text of the license may be found at
      8   http://opensource.org/licenses/bsd-license.php
      9 
     10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "DatahubStatusCodeHandlerDxe.h"
     16 
     17 //
     18 // Initialize FIFO to cache records.
     19 //
     20 LIST_ENTRY                mRecordsFifo          = INITIALIZE_LIST_HEAD_VARIABLE (mRecordsFifo);
     21 LIST_ENTRY                mRecordsBuffer        = INITIALIZE_LIST_HEAD_VARIABLE (mRecordsBuffer);
     22 UINT32                    mLogDataHubStatus     = 0;
     23 EFI_EVENT                 mLogDataHubEvent;
     24 //
     25 // Cache data hub protocol.
     26 //
     27 EFI_DATA_HUB_PROTOCOL     *mDataHubProtocol = NULL;
     28 
     29 
     30 /**
     31   Retrieve one record of from free record buffer. This record is removed from
     32   free record buffer.
     33 
     34   This function retrieves one record from free record buffer.
     35   If the pool has been exhausted, then new memory would be allocated for it.
     36 
     37   @return  Pointer to the free record.
     38            NULL means failure to allocate new memeory for free record buffer.
     39 
     40 **/
     41 DATA_HUB_STATUS_CODE_DATA_RECORD *
     42 AcquireRecordBuffer (
     43   VOID
     44   )
     45 {
     46   DATAHUB_STATUSCODE_RECORD *Record;
     47   EFI_TPL                   CurrentTpl;
     48   LIST_ENTRY                *Node;
     49   UINT32                    Index;
     50 
     51   CurrentTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
     52 
     53   if (!IsListEmpty (&mRecordsBuffer)) {
     54     //
     55     // Strip one entry from free record buffer.
     56     //
     57     Node = GetFirstNode (&mRecordsBuffer);
     58     RemoveEntryList (Node);
     59 
     60     Record = BASE_CR (Node, DATAHUB_STATUSCODE_RECORD, Node);
     61   } else {
     62     if (CurrentTpl > TPL_NOTIFY) {
     63       //
     64       // Memory management should work at <=TPL_NOTIFY
     65       //
     66       gBS->RestoreTPL (CurrentTpl);
     67       return NULL;
     68     }
     69 
     70     //
     71     // If free record buffer is exhausted, then allocate 16 new records for it.
     72     //
     73     gBS->RestoreTPL (CurrentTpl);
     74     Record   = (DATAHUB_STATUSCODE_RECORD *) AllocateZeroPool (sizeof (DATAHUB_STATUSCODE_RECORD) * 16);
     75     if (Record == NULL) {
     76       return NULL;
     77     }
     78 
     79     CurrentTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
     80     //
     81     // Here we only insert 15 new records to the free record buffer, for the first record
     82     // will be returned immediately.
     83     //
     84     for (Index = 1; Index < 16; Index++) {
     85       InsertTailList (&mRecordsBuffer, &Record[Index].Node);
     86     }
     87   }
     88 
     89   Record->Signature = DATAHUB_STATUS_CODE_SIGNATURE;
     90   InsertTailList (&mRecordsFifo, &Record->Node);
     91 
     92   gBS->RestoreTPL (CurrentTpl);
     93 
     94   return (DATA_HUB_STATUS_CODE_DATA_RECORD *) (Record->Data);
     95 }
     96 
     97 
     98 /**
     99   Retrieve one record from Records FIFO. The record would be removed from FIFO.
    100 
    101   @return   Point to record, which is ready to be logged.
    102             NULL means the FIFO of record is empty.
    103 
    104 **/
    105 DATA_HUB_STATUS_CODE_DATA_RECORD *
    106 RetrieveRecord (
    107   VOID
    108   )
    109 {
    110   DATA_HUB_STATUS_CODE_DATA_RECORD  *RecordData;
    111   DATAHUB_STATUSCODE_RECORD         *Record;
    112   LIST_ENTRY                        *Node;
    113   EFI_TPL                           CurrentTpl;
    114 
    115   RecordData = NULL;
    116 
    117   CurrentTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
    118 
    119   if (!IsListEmpty (&mRecordsFifo)) {
    120     Node = GetFirstNode (&mRecordsFifo);
    121     Record = CR (Node, DATAHUB_STATUSCODE_RECORD, Node, DATAHUB_STATUS_CODE_SIGNATURE);
    122     ASSERT (Record != NULL);
    123 
    124     RemoveEntryList (&Record->Node);
    125     RecordData = (DATA_HUB_STATUS_CODE_DATA_RECORD *) Record->Data;
    126   }
    127 
    128   gBS->RestoreTPL (CurrentTpl);
    129 
    130   return RecordData;
    131 }
    132 
    133 /**
    134   Release given record and return it to free record buffer.
    135 
    136   @param RecordData  Pointer to the record to release.
    137 
    138 **/
    139 VOID
    140 ReleaseRecord (
    141   DATA_HUB_STATUS_CODE_DATA_RECORD  *RecordData
    142   )
    143 {
    144   DATAHUB_STATUSCODE_RECORD         *Record;
    145   EFI_TPL                           CurrentTpl;
    146 
    147   Record = CR (RecordData, DATAHUB_STATUSCODE_RECORD, Data[0], DATAHUB_STATUS_CODE_SIGNATURE);
    148   ASSERT (Record != NULL);
    149 
    150   CurrentTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
    151 
    152   InsertTailList (&mRecordsBuffer, &Record->Node);
    153   Record->Signature = 0;
    154 
    155   gBS->RestoreTPL (CurrentTpl);
    156 }
    157 
    158 /**
    159   Report status code into DataHub.
    160 
    161   @param  CodeType             Indicates the type of status code being reported.
    162   @param  Value                Describes the current status of a hardware or software entity.
    163                                This included information about the class and subclass that is used to
    164                                classify the entity as well as an operation.
    165   @param  Instance             The enumeration of a hardware or software entity within
    166                                the system. Valid instance numbers start with 1.
    167   @param  CallerId             This optional parameter may be used to identify the caller.
    168                                This parameter allows the status code driver to apply different rules to
    169                                different callers.
    170   @param  Data                 This optional parameter may be used to pass additional data.
    171 
    172   @retval EFI_SUCCESS          The function completed successfully.
    173   @retval EFI_DEVICE_ERROR     Function is reentered.
    174   @retval EFI_DEVICE_ERROR     Function is called at runtime.
    175   @retval EFI_OUT_OF_RESOURCES Fail to allocate memory for free record buffer.
    176 
    177 **/
    178 EFI_STATUS
    179 EFIAPI
    180 DataHubStatusCodeReportWorker (
    181   IN EFI_STATUS_CODE_TYPE     CodeType,
    182   IN EFI_STATUS_CODE_VALUE    Value,
    183   IN UINT32                   Instance,
    184   IN EFI_GUID                 *CallerId,
    185   IN EFI_STATUS_CODE_DATA     *Data OPTIONAL
    186   )
    187 {
    188   DATA_HUB_STATUS_CODE_DATA_RECORD  *Record;
    189   UINT32                            ErrorLevel;
    190   BASE_LIST                         Marker;
    191   CHAR8                             *Format;
    192   UINTN                             CharCount;
    193 
    194   //
    195   // Use atom operation to avoid the reentant of report.
    196   // If current status is not zero, then the function is reentrancy.
    197   //
    198   if (InterlockedCompareExchange32 (&mLogDataHubStatus, 0, 0) == 1) {
    199     return EFI_DEVICE_ERROR;
    200   }
    201 
    202   Record = AcquireRecordBuffer ();
    203   if (Record == NULL) {
    204     //
    205     // There are no empty record buffer in private buffers
    206     //
    207     return EFI_OUT_OF_RESOURCES;
    208   }
    209 
    210   //
    211   // Construct Data Hub Extended Data
    212   //
    213   Record->CodeType = CodeType;
    214   Record->Value    = Value;
    215   Record->Instance = Instance;
    216 
    217   if (CallerId != NULL) {
    218     CopyMem (&Record->CallerId, CallerId, sizeof (EFI_GUID));
    219   }
    220 
    221   if (Data != NULL) {
    222     if (ReportStatusCodeExtractDebugInfo (Data, &ErrorLevel, &Marker, &Format)) {
    223       CharCount = UnicodeBSPrintAsciiFormat (
    224                     (CHAR16 *) (Record + 1),
    225                     EFI_STATUS_CODE_DATA_MAX_SIZE,
    226                     Format,
    227                     Marker
    228                     );
    229       //
    230       // Change record data type to DebugType.
    231       //
    232       CopyGuid (&Record->Data.Type, &gEfiStatusCodeDataTypeDebugGuid);
    233       Record->Data.HeaderSize = Data->HeaderSize;
    234       Record->Data.Size = (UINT16) ((CharCount + 1) * sizeof (CHAR16));
    235     } else {
    236       //
    237       // Copy status code data header
    238       //
    239       CopyMem (&Record->Data, Data, sizeof (EFI_STATUS_CODE_DATA));
    240 
    241       if (Data->Size > EFI_STATUS_CODE_DATA_MAX_SIZE) {
    242         Record->Data.Size = EFI_STATUS_CODE_DATA_MAX_SIZE;
    243       }
    244       CopyMem ((VOID *) (Record + 1), Data + 1, Record->Data.Size);
    245     }
    246   }
    247 
    248   gBS->SignalEvent (mLogDataHubEvent);
    249 
    250   return EFI_SUCCESS;
    251 }
    252 
    253 
    254 /**
    255   The Event handler which will be notified to log data in Data Hub.
    256 
    257   @param  Event       Instance of the EFI_EVENT to signal whenever data is
    258                       available to be logged in the system.
    259   @param  Context     Context of the event.
    260 
    261 **/
    262 VOID
    263 EFIAPI
    264 LogDataHubEventCallBack (
    265   IN  EFI_EVENT     Event,
    266   IN  VOID          *Context
    267   )
    268 {
    269   DATA_HUB_STATUS_CODE_DATA_RECORD  *Record;
    270   UINT32                            Size;
    271   UINT64                            DataRecordClass;
    272 
    273   //
    274   // Use atom operation to avoid the reentant of report.
    275   // If current status is not zero, then the function is reentrancy.
    276   //
    277   if (InterlockedCompareExchange32 (&mLogDataHubStatus, 0, 1) == 1) {
    278     return;
    279   }
    280 
    281   //
    282   // Log DataRecord in Data Hub.
    283   // Journal records fifo to find all record entry.
    284   //
    285   while (TRUE) {
    286     //
    287     // Retrieve record from record FIFO until no more record can be retrieved.
    288     //
    289     Record = RetrieveRecord ();
    290     if (Record == NULL) {
    291       break;
    292     }
    293     //
    294     // Add in the size of the header we added.
    295     //
    296     Size = sizeof (DATA_HUB_STATUS_CODE_DATA_RECORD) + (UINT32) Record->Data.Size;
    297 
    298     if ((Record->CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_PROGRESS_CODE) {
    299       DataRecordClass = EFI_DATA_RECORD_CLASS_PROGRESS_CODE;
    300     } else if ((Record->CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_ERROR_CODE) {
    301       DataRecordClass = EFI_DATA_RECORD_CLASS_ERROR;
    302     } else if ((Record->CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_DEBUG_CODE) {
    303       DataRecordClass = EFI_DATA_RECORD_CLASS_DEBUG;
    304     } else {
    305       //
    306       // Should never get here.
    307       //
    308       DataRecordClass = EFI_DATA_RECORD_CLASS_DEBUG |
    309         EFI_DATA_RECORD_CLASS_ERROR |
    310         EFI_DATA_RECORD_CLASS_DATA |
    311         EFI_DATA_RECORD_CLASS_PROGRESS_CODE;
    312     }
    313 
    314     //
    315     // Log DataRecord in Data Hub
    316     //
    317     mDataHubProtocol->LogData (
    318                         mDataHubProtocol,
    319                         &gEfiDataHubStatusCodeRecordGuid,
    320                         &gEfiStatusCodeRuntimeProtocolGuid,
    321                         DataRecordClass,
    322                         Record,
    323                         Size
    324                         );
    325 
    326     ReleaseRecord (Record);
    327   }
    328 
    329   //
    330   // Restore the nest status of report
    331   //
    332   InterlockedCompareExchange32 (&mLogDataHubStatus, 1, 0);
    333 }
    334 
    335 
    336 /**
    337   Locate Data Hub Protocol and create event for logging data
    338   as initialization for data hub status code worker.
    339 
    340   @retval EFI_SUCCESS  Initialization is successful.
    341 
    342 **/
    343 EFI_STATUS
    344 DataHubStatusCodeInitializeWorker (
    345   VOID
    346   )
    347 {
    348   EFI_STATUS  Status;
    349 
    350   Status = gBS->LocateProtocol (
    351                   &gEfiDataHubProtocolGuid,
    352                   NULL,
    353                   (VOID **) &mDataHubProtocol
    354                   );
    355   if (EFI_ERROR (Status)) {
    356     mDataHubProtocol = NULL;
    357     return Status;
    358   }
    359 
    360   //
    361   // Create a Notify Event to log data in Data Hub
    362   //
    363   Status = gBS->CreateEvent (
    364                   EVT_NOTIFY_SIGNAL,
    365                   TPL_CALLBACK,
    366                   LogDataHubEventCallBack,
    367                   NULL,
    368                   &mLogDataHubEvent
    369                   );
    370 
    371   ASSERT_EFI_ERROR (Status);
    372 
    373   return EFI_SUCCESS;
    374 }
    375 
    376 
    377