]> git.proxmox.com Git - mirror_edk2.git/blob - EdkCompatibilityPkg/Sample/Platform/Generic/RuntimeDxe/StatusCode/Lib/BsDataHubStatusCode/BsDataHubStatusCode.c
Add in more library for ECP.
[mirror_edk2.git] / EdkCompatibilityPkg / Sample / Platform / Generic / RuntimeDxe / StatusCode / Lib / BsDataHubStatusCode / BsDataHubStatusCode.c
1 /*++
2
3 Copyright (c) 2004 - 2006, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13
14 BsDataHubStatusCode.c
15
16 Abstract:
17
18 This implements a status code listener that logs status codes into the data
19 hub. This is only active during non-runtime DXE.
20 The status codes are recorded in a extensiable buffer, and a event is signalled
21 to log them to the data hub. The recorder is the producer of the status code in
22 buffer and the event notify function the consummer.
23
24 --*/
25
26 #include "BsDataHubStatusCode.h"
27
28 //
29 // Globals only work at BootService Time. NOT at Runtime!
30 //
31 static EFI_DATA_HUB_PROTOCOL *mDataHub;
32 static EFI_LIST_ENTRY *mRecordHead;
33 static EFI_LIST_ENTRY *mRecordTail;
34 static INTN mRecordNum = 0;
35 static EFI_EVENT mLogDataHubEvent;
36 static EFI_LOCK mStatusCodeReportLock = EFI_INITIALIZE_LOCK_VARIABLE(EFI_TPL_HIGH_LEVEL);
37 static BOOLEAN mEventHandlerActive = FALSE;
38
39
40 STATUS_CODE_RECORD_LIST *
41 AllocateRecordBuffer (
42 VOID
43 )
44 /*++
45
46 Routine Description:
47
48 Allocate a new record list node and initialize it.
49 Inserting the node into the list isn't the task of this function.
50
51 Arguments:
52
53 None
54
55 Returns:
56
57 A pointer to the new allocated node or NULL if non available
58
59 --*/
60 {
61 STATUS_CODE_RECORD_LIST *DataBuffer;
62
63 DataBuffer = NULL;
64
65 gBS->AllocatePool (EfiBootServicesData, sizeof (STATUS_CODE_RECORD_LIST), &DataBuffer);
66 if (DataBuffer == NULL) {
67 return NULL;
68 }
69
70 EfiCommonLibZeroMem (DataBuffer, sizeof (STATUS_CODE_RECORD_LIST));
71 DataBuffer->Signature = BS_DATA_HUB_STATUS_CODE_SIGNATURE;
72
73 return DataBuffer;
74 }
75
76 DATA_HUB_STATUS_CODE_DATA_RECORD *
77 AquireEmptyRecordBuffer (
78 VOID
79 )
80 /*++
81
82 Routine Description:
83
84 Acquire an empty record buffer from the record list if there's free node,
85 or allocate one new node and insert it to the list if the list is full and
86 the function isn't run in EFI_TPL_HIGH_LEVEL.
87
88 Arguments:
89
90 None
91
92 Returns:
93
94 Pointer to new record buffer. NULL if none available.
95
96 --*/
97 {
98 EFI_TPL OldTpl;
99 STATUS_CODE_RECORD_LIST *DataBuffer;
100
101 DataBuffer = NULL;
102
103 //
104 // This function must be reentrant because an event with higher priority may interrupt it
105 // and also report status code.
106 //
107 EfiAcquireLock (&mStatusCodeReportLock);
108 if (mRecordTail != mRecordHead->ForwardLink) {
109 if (mRecordNum != 0) {
110 mRecordHead = mRecordHead->ForwardLink;
111 }
112 DataBuffer = CR (mRecordHead, STATUS_CODE_RECORD_LIST, Link, BS_DATA_HUB_STATUS_CODE_SIGNATURE);
113 mRecordNum++;
114 EfiReleaseLock (&mStatusCodeReportLock);
115
116 //
117 // Initalize the record buffer is the responsibility of the producer,
118 // because the consummer is in a lock so must keep it short.
119 //
120 EfiCommonLibZeroMem (&DataBuffer->RecordBuffer[0], BYTES_PER_BUFFER);
121 } else if (mRecordNum < MAX_RECORD_NUM) {
122 //
123 // The condition of "mRecordNum < MAX_RECORD_NUM" is not promised,
124 // because mRecodeNum may be increased out of this lock.
125 //
126 EfiReleaseLock (&mStatusCodeReportLock);
127
128 //
129 // Can't allocate additional buffer in EFI_TPL_HIGH_LEVEL.
130 // Reporting too many status code in EFI_TPL_HIGH_LEVEL may cause status code lost.
131 //
132 OldTpl = gBS->RaiseTPL (EFI_TPL_HIGH_LEVEL);
133 if (OldTpl == EFI_TPL_HIGH_LEVEL) {
134 return NULL;
135 }
136 gBS->RestoreTPL (OldTpl);
137 DataBuffer = AllocateRecordBuffer ();
138 if (DataBuffer == NULL) {
139 return NULL;
140 }
141 EfiAcquireLock (&mStatusCodeReportLock);
142 InsertHeadList (mRecordHead, &DataBuffer->Link);
143 mRecordHead = mRecordHead->ForwardLink;
144 mRecordNum++;
145 EfiReleaseLock (&mStatusCodeReportLock);
146 } else {
147 EfiReleaseLock (&mStatusCodeReportLock);
148 return NULL;
149 }
150
151 return (DATA_HUB_STATUS_CODE_DATA_RECORD *) DataBuffer->RecordBuffer;
152 }
153
154 EFI_STATUS
155 ReleaseRecordBuffer (
156 IN STATUS_CODE_RECORD_LIST *RecordBuffer
157 )
158 /*++
159
160 Routine Description:
161
162 Release a buffer in the list, remove some nodes to keep the list inital length.
163
164 Arguments:
165
166 RecordBuffer - Buffer to release
167
168 Returns:
169
170 EFI_SUCCESS - If DataRecord is valid
171 EFI_UNSUPPORTED - The record list has empty
172
173 --*/
174 {
175 ASSERT (RecordBuffer != NULL);
176
177 //
178 // The consummer needn't to be reentrient and the producer won't do any meaningful thing
179 // when consummer is logging records.
180 //
181 if (mRecordNum <= 0) {
182 return EFI_UNSUPPORTED;
183 } else if (mRecordNum > INITIAL_RECORD_NUM) {
184 mRecordTail = mRecordTail->ForwardLink;
185 RemoveEntryList (&RecordBuffer->Link);
186 mRecordNum--;
187 gBS->FreePool (RecordBuffer);
188 } else {
189 if (mRecordNum != 1) {
190 mRecordTail = mRecordTail->ForwardLink;
191 }
192 mRecordNum--;
193 }
194
195 return EFI_SUCCESS;
196 }
197
198 EFI_BOOTSERVICE
199 EFI_STATUS
200 EFIAPI
201 BsDataHubReportStatusCode (
202 IN EFI_STATUS_CODE_TYPE CodeType,
203 IN EFI_STATUS_CODE_VALUE Value,
204 IN UINT32 Instance,
205 IN EFI_GUID * CallerId,
206 IN EFI_STATUS_CODE_DATA * Data OPTIONAL
207 )
208 /*++
209
210 Routine Description:
211
212 Boot service report status code listener. This function logs the status code
213 into the data hub.
214
215 Arguments:
216
217 Same as gRT->ReportStatusCode (See Tiano Runtime Specification)
218
219 Returns:
220
221 None
222
223 --*/
224 {
225 DATA_HUB_STATUS_CODE_DATA_RECORD *DataHub;
226 UINT32 ErrorLevel;
227 VA_LIST Marker;
228 CHAR8 *Format;
229 UINTN Index;
230 CHAR16 FormatBuffer[BYTES_PER_RECORD];
231
232 DataHub = NULL;
233
234 if (EfiAtRuntime ()) {
235 //
236 // For now all we do is post code at runtime
237 //
238 return EFI_SUCCESS;
239 }
240 //
241 // If we had an error while in our event handler, then do nothing so
242 // that we don't get in an endless loop.
243 //
244 if (mEventHandlerActive) {
245 return EFI_SUCCESS;
246 }
247
248 DataHub = (DATA_HUB_STATUS_CODE_DATA_RECORD *) AquireEmptyRecordBuffer ();
249 if (DataHub == NULL) {
250 //
251 // There are no empty record buffer in private buffers
252 //
253 return EFI_OUT_OF_RESOURCES;
254 }
255 //
256 // Construct Data Hub Extended Data
257 //
258 DataHub->CodeType = CodeType;
259 DataHub->Value = Value;
260 DataHub->Instance = Instance;
261
262 if (CallerId != NULL) {
263 EfiCopyMem (&DataHub->CallerId, CallerId, sizeof (EFI_GUID));
264 } else {
265 EfiZeroMem (&DataHub->CallerId, sizeof (EFI_GUID));
266 }
267
268 if (Data == NULL) {
269 EfiZeroMem (&DataHub->Data, sizeof (EFI_STATUS_CODE_DATA));
270 } else {
271 //
272 // Copy generic Header
273 //
274 EfiCopyMem (&DataHub->Data, Data, sizeof (EFI_STATUS_CODE_DATA));
275
276 if (ReportStatusCodeExtractDebugInfo (Data, &ErrorLevel, &Marker, &Format)) {
277 //
278 // Convert Ascii Format string to Unicode.
279 //
280 for (Index = 0; Format[Index] != '\0' && Index < (BYTES_PER_RECORD - 1); Index += 1) {
281 FormatBuffer[Index] = (CHAR16) Format[Index];
282 }
283
284 FormatBuffer[Index] = L'\0';
285
286 //
287 // Put processed string into the buffer
288 //
289 Index = VSPrint (
290 (UINT16 *) (DataHub + 1),
291 BYTES_PER_RECORD - (sizeof (DATA_HUB_STATUS_CODE_DATA_RECORD)),
292 FormatBuffer,
293 Marker
294 );
295
296 //
297 // DATA_HUB_STATUS_CODE_DATA_RECORD followed by VSPrint String Buffer
298 //
299 DataHub->Data.Size = (UINT16) (Index * sizeof (CHAR16));
300
301 } else {
302 //
303 // Default behavior is to copy optional data
304 //
305 if (Data->Size > (BYTES_PER_RECORD - sizeof (DATA_HUB_STATUS_CODE_DATA_RECORD))) {
306 DataHub->Data.Size = (UINT16) (BYTES_PER_RECORD - sizeof (DATA_HUB_STATUS_CODE_DATA_RECORD));
307 }
308
309 EfiCopyMem (DataHub + 1, Data + 1, DataHub->Data.Size);
310 }
311 }
312
313 gBS->SignalEvent (mLogDataHubEvent);
314
315 return EFI_SUCCESS;
316 }
317
318 VOID
319 EFIAPI
320 LogDataHubEventHandler (
321 IN EFI_EVENT Event,
322 IN VOID *Context
323 )
324 /*++
325
326 Routine Description:
327
328 The Event handler which will be notified to log data in Data Hub.
329
330 Arguments:
331
332 Event - Instance of the EFI_EVENT to signal whenever data is
333 available to be logged in the system.
334 Context - Context of the event.
335
336 Returns:
337
338 None.
339
340 --*/
341 {
342 EFI_STATUS Status;
343 DATA_HUB_STATUS_CODE_DATA_RECORD *DataRecord;
344 UINTN Size;
345 UINT64 DataRecordClass;
346 EFI_LIST_ENTRY *Link;
347 STATUS_CODE_RECORD_LIST *BufferEntry;
348
349 //
350 // Set our global flag so we don't recurse if we get an error here.
351 //
352 mEventHandlerActive = TRUE;
353
354 //
355 // Log DataRecord in Data Hub.
356 // If there are multiple DataRecords, Log all of them.
357 //
358 Link = mRecordTail;
359
360 while (mRecordNum != 0) {
361 BufferEntry = CR (Link, STATUS_CODE_RECORD_LIST, Link, BS_DATA_HUB_STATUS_CODE_SIGNATURE);
362 DataRecord = (DATA_HUB_STATUS_CODE_DATA_RECORD *) (BufferEntry->RecordBuffer);
363 Link = Link->ForwardLink;
364
365 //
366 // Add in the size of the header we added.
367 //
368 Size = sizeof (DATA_HUB_STATUS_CODE_DATA_RECORD) + DataRecord->Data.Size;
369
370 if ((DataRecord->CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_PROGRESS_CODE) {
371 DataRecordClass = EFI_DATA_RECORD_CLASS_PROGRESS_CODE;
372 } else if ((DataRecord->CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_ERROR_CODE) {
373 DataRecordClass = EFI_DATA_RECORD_CLASS_ERROR;
374 } else if ((DataRecord->CodeType & EFI_STATUS_CODE_TYPE_MASK) == EFI_DEBUG_CODE) {
375 DataRecordClass = EFI_DATA_RECORD_CLASS_DEBUG;
376 } else {
377 //
378 // Should never get here.
379 //
380 DataRecordClass = EFI_DATA_RECORD_CLASS_DEBUG |
381 EFI_DATA_RECORD_CLASS_ERROR |
382 EFI_DATA_RECORD_CLASS_DATA |
383 EFI_DATA_RECORD_CLASS_PROGRESS_CODE;
384 }
385
386 if (((DataRecord->Instance & EFI_D_ERROR) != 0) &&
387 (((DataRecord->Instance & EFI_D_POOL) != 0) || ((DataRecord->Instance & EFI_D_PAGE) != 0))
388 ) {
389 //
390 // If memory error, do not call LogData ().
391 //
392 ErrorPrint (L"ERROR", "Memory Error\n");
393 Status = EFI_OUT_OF_RESOURCES;
394 } else {
395 //
396 // We don't log EFI_D_POOL and EFI_D_PAGE debug info to datahub
397 // to avoid recursive logging due to the memory allocation in datahub
398 //
399 if (DataRecordClass != EFI_DATA_RECORD_CLASS_DEBUG ||
400 ((DataRecord->Instance & EFI_D_POOL) == 0 && (DataRecord->Instance & EFI_D_PAGE) == 0)) {
401 //
402 // Log DataRecord in Data Hub
403 //
404 Status = mDataHub->LogData (
405 mDataHub,
406 &gEfiStatusCodeGuid,
407 &gEfiStatusCodeRuntimeProtocolGuid,
408 DataRecordClass,
409 DataRecord,
410 (UINT32) Size
411 );
412 }
413 }
414
415 ReleaseRecordBuffer (BufferEntry);
416 }
417
418 mEventHandlerActive = FALSE;
419
420 return ;
421 }
422
423 EFI_BOOTSERVICE
424 EFI_STATUS
425 BsDataHubInitializeStatusCode (
426 IN EFI_HANDLE ImageHandle,
427 IN EFI_SYSTEM_TABLE *SystemTable
428 )
429 /*++
430
431 Routine Description:
432
433 Install a data hub listener.
434
435 Arguments:
436
437 (Standard EFI Image entry - EFI_IMAGE_ENTRY_POINT)
438
439 Returns:
440
441 EFI_SUCCESS - Logging Hub protocol installed
442 Other - No protocol installed, unload driver.
443
444 --*/
445 {
446 EFI_STATUS Status;
447 STATUS_CODE_RECORD_LIST *DataBuffer;
448 UINTN Index1;
449
450 DataBuffer = NULL;
451
452 Status = gBS->LocateProtocol (&gEfiDataHubProtocolGuid, NULL, &mDataHub);
453 //
454 // Should never fail due to dependency grammer
455 //
456 ASSERT_EFI_ERROR (Status);
457
458 //
459 // Initialize a record list with length not greater than INITIAL_RECORD_NUM.
460 // If no buffer can be allocated, return EFI_OUT_OF_RESOURCES.
461 //
462 DataBuffer = AllocateRecordBuffer ();
463 if (DataBuffer == NULL) {
464 return EFI_OUT_OF_RESOURCES;
465 }
466 mRecordHead = &DataBuffer->Link;
467 mRecordTail = mRecordHead;
468 InitializeListHead (mRecordHead);
469
470 for (Index1 = 1; Index1 < INITIAL_RECORD_NUM; Index1++) {
471 DataBuffer = AllocateRecordBuffer ();
472 if (DataBuffer == NULL) {
473 break;
474 }
475 InsertHeadList (mRecordHead, &DataBuffer->Link);
476 }
477
478
479 //
480 // Create a Notify Event to log data in Data Hub
481 //
482 Status = gBS->CreateEvent (
483 EFI_EVENT_NOTIFY_SIGNAL,
484 EFI_TPL_CALLBACK,
485 LogDataHubEventHandler,
486 NULL,
487 &mLogDataHubEvent
488 );
489
490 return EFI_SUCCESS;
491 }