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