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