Home | History | Annotate | Download | only in RuntimeDxe
      1 /** @file
      2   Data Hub status code worker.
      3 
      4   Copyright (c) 2006 - 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 "StatusCodeRuntimeDxe.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 DataHubStatusCodeReportWorker (
    180   IN EFI_STATUS_CODE_TYPE     CodeType,
    181   IN EFI_STATUS_CODE_VALUE    Value,
    182   IN UINT32                   Instance,
    183   IN EFI_GUID                 *CallerId,
    184   IN EFI_STATUS_CODE_DATA     *Data OPTIONAL
    185   )
    186 {
    187   DATA_HUB_STATUS_CODE_DATA_RECORD  *Record;
    188   UINT32                            ErrorLevel;
    189   BASE_LIST                         Marker;
    190   CHAR8                             *Format;
    191   UINTN                             CharCount;
    192   EFI_STATUS                        Status;
    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   //
    203   // See whether in runtime phase or not.
    204   //
    205   if (EfiAtRuntime ()) {
    206     return EFI_DEVICE_ERROR;
    207   }
    208 
    209   if (mDataHubProtocol == NULL) {
    210     Status = DataHubStatusCodeInitializeWorker ();
    211     if (EFI_ERROR (Status)) {
    212       return Status;
    213     }
    214   }
    215 
    216   Record = AcquireRecordBuffer ();
    217   if (Record == NULL) {
    218     //
    219     // There are no empty record buffer in private buffers
    220     //
    221     return EFI_OUT_OF_RESOURCES;
    222   }
    223 
    224   //
    225   // Construct Data Hub Extended Data
    226   //
    227   Record->CodeType = CodeType;
    228   Record->Value    = Value;
    229   Record->Instance = Instance;
    230 
    231   if (CallerId != NULL) {
    232     CopyMem (&Record->CallerId, CallerId, sizeof (EFI_GUID));
    233   }
    234 
    235   if (Data != NULL) {
    236     if (ReportStatusCodeExtractDebugInfo (Data, &ErrorLevel, &Marker, &Format)) {
    237       CharCount = UnicodeBSPrintAsciiFormat (
    238                     (CHAR16 *) (Record + 1),
    239                     EFI_STATUS_CODE_DATA_MAX_SIZE,
    240                     Format,
    241                     Marker
    242                     );
    243       //
    244       // Change record data type to DebugType.
    245       //
    246       CopyGuid (&Record->Data.Type, &gEfiStatusCodeDataTypeDebugGuid);
    247       Record->Data.HeaderSize = Data->HeaderSize;
    248       Record->Data.Size = (UINT16) ((CharCount + 1) * sizeof (CHAR16));
    249     } else {
    250       //
    251       // Copy status code data header
    252       //
    253       CopyMem (&Record->Data, Data, sizeof (EFI_STATUS_CODE_DATA));
    254 
    255       if (Data->Size > EFI_STATUS_CODE_DATA_MAX_SIZE) {
    256         Record->Data.Size = EFI_STATUS_CODE_DATA_MAX_SIZE;
    257       }
    258       CopyMem ((VOID *) (Record + 1), Data + 1, Record->Data.Size);
    259     }
    260   }
    261 
    262   gBS->SignalEvent (mLogDataHubEvent);
    263 
    264   return EFI_SUCCESS;
    265 }
    266 
    267 
    268 /**
    269   The Event handler which will be notified to log data in Data Hub.
    270 
    271   @param  Event       Instance of the EFI_EVENT to signal whenever data is
    272                       available to be logged in the system.
    273   @param  Context     Context of the event.
    274 
    275 **/
    276 VOID
    277 EFIAPI
    278 LogDataHubEventCallBack (
    279   IN  EFI_EVENT     Event,
    280   IN  VOID          *Context
    281   )
    282 {
    283   DATA_HUB_STATUS_CODE_DATA_RECORD  *Record;
    284   UINT32                            Size;
    285   UINT64                            DataRecordClass;
    286 
    287   //
    288   // Use atom operation to avoid the reentant of report.
    289   // If current status is not zero, then the function is reentrancy.
    290   //
    291   if (InterlockedCompareExchange32 (&mLogDataHubStatus, 0, 1) == 1) {
    292     return;
    293   }
    294 
    295   //
    296   // Log DataRecord in Data Hub.
    297   // Journal records fifo to find all record entry.
    298   //
    299   while (TRUE) {
    300     //
    301     // Retrieve record from record FIFO until no more record can be retrieved.
    302     //
    303     Record = RetrieveRecord ();
    304     if (Record == NULL) {
    305       break;
    306     }
    307     //
    308     // Add in the size of the header we added.
    309     //
    310     Size = sizeof (DATA_HUB_STATUS_CODE_DATA_RECORD) + (UINT32) Record->Data.Size;
    311 
    312     if ((Record->CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_PROGRESS_CODE) {
    313       DataRecordClass = EFI_DATA_RECORD_CLASS_PROGRESS_CODE;
    314     } else if ((Record->CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_ERROR_CODE) {
    315       DataRecordClass = EFI_DATA_RECORD_CLASS_ERROR;
    316     } else if ((Record->CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_DEBUG_CODE) {
    317       DataRecordClass = EFI_DATA_RECORD_CLASS_DEBUG;
    318     } else {
    319       //
    320       // Should never get here.
    321       //
    322       DataRecordClass = EFI_DATA_RECORD_CLASS_DEBUG |
    323         EFI_DATA_RECORD_CLASS_ERROR |
    324         EFI_DATA_RECORD_CLASS_DATA |
    325         EFI_DATA_RECORD_CLASS_PROGRESS_CODE;
    326     }
    327 
    328     //
    329     // Log DataRecord in Data Hub
    330     //
    331     mDataHubProtocol->LogData (
    332                         mDataHubProtocol,
    333                         &gEfiDataHubStatusCodeRecordGuid,
    334                         &gEfiStatusCodeRuntimeProtocolGuid,
    335                         DataRecordClass,
    336                         Record,
    337                         Size
    338                         );
    339 
    340     ReleaseRecord (Record);
    341   }
    342 
    343   //
    344   // Restore the nest status of report
    345   //
    346   InterlockedCompareExchange32 (&mLogDataHubStatus, 1, 0);
    347 }
    348 
    349 
    350 /**
    351   Locate Data Hub Protocol and create event for logging data
    352   as initialization for data hub status code worker.
    353 
    354   @retval EFI_SUCCESS  Initialization is successful.
    355 
    356 **/
    357 EFI_STATUS
    358 DataHubStatusCodeInitializeWorker (
    359   VOID
    360   )
    361 {
    362   EFI_STATUS  Status;
    363 
    364   Status = gBS->LocateProtocol (
    365                   &gEfiDataHubProtocolGuid,
    366                   NULL,
    367                   (VOID **) &mDataHubProtocol
    368                   );
    369   if (EFI_ERROR (Status)) {
    370     mDataHubProtocol = NULL;
    371     return Status;
    372   }
    373 
    374   //
    375   // Create a Notify Event to log data in Data Hub
    376   //
    377   Status = gBS->CreateEvent (
    378                   EVT_NOTIFY_SIGNAL,
    379                   TPL_CALLBACK,
    380                   LogDataHubEventCallBack,
    381                   NULL,
    382                   &mLogDataHubEvent
    383                   );
    384 
    385   ASSERT_EFI_ERROR (Status);
    386 
    387   return EFI_SUCCESS;
    388 }
    389 
    390 
    391