3 Copyright (c) 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
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.
18 This code produces the Data Hub protocol. It preloads the data hub
19 with status information copied in from PEI HOBs.
21 Only code that implements the Data Hub protocol should go in this file!
23 The Term MTC stands for MonoTonicCounter.
25 For more information please look at DataHub.doc
27 NOTE: For extra security of the log GetNextDataRecord () could return a copy
33 CONST EFI_GUID gZeroGuid
= { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
36 // Worker functions private to this file
39 DATA_HUB_FILTER_DRIVER
*
40 FindFilterDriverByEvent (
46 EFI_DATA_RECORD_HEADER
*
49 IN UINT64 ClassFilter
,
50 IN OUT UINT64
*PtrCurrentMTC
57 IN EFI_DATA_HUB_PROTOCOL
*This
,
58 IN EFI_GUID
*DataRecordGuid
,
59 IN EFI_GUID
*ProducerName
,
60 IN UINT64 DataRecordClass
,
68 Log data record into the data logging hub
72 This - Protocol instance structure
74 DataRecordGuid - GUID that defines record contents
76 ProducerName - GUID that defines the name of the producer of the data
78 DataRecordClass - Class that defines generic record type
80 RawData - Data Log record as defined by DataRecordGuid
82 RawDataSize - Size of Data Log data in bytes
86 EFI_SUCCESS - If data was logged
88 EFI_OUT_OF_RESOURCES - If data was not logged due to lack of system
93 DATA_HUB_INSTANCE
*Private
;
94 EFI_DATA_ENTRY
*LogEntry
;
97 EFI_DATA_RECORD_HEADER
*Record
;
99 DATA_HUB_FILTER_DRIVER
*FilterEntry
;
103 Private
= DATA_HUB_INSTANCE_FROM_THIS (This
);
106 // Combine the storage for the internal structs and a copy of the log record.
107 // Record follows PrivateLogEntry. The consumer will be returned a pointer
108 // to Record so we don't what it to be the thing that was allocated from
109 // pool, so the consumer can't free an data record by mistake.
111 RecordSize
= sizeof (EFI_DATA_RECORD_HEADER
) + RawDataSize
;
112 TotalSize
= sizeof (EFI_DATA_ENTRY
) + RecordSize
;
115 // The Logging action is the critical section, so it is locked.
116 // The MTC asignment & update, time, and logging must be an
117 // atomic operation, so use the lock.
119 Status
= EfiAcquireLockOrFail (&Private
->DataLock
);
120 if (EFI_ERROR (Status
)) {
122 // Reentrancy detected so exit!
127 LogEntry
= AllocatePool (TotalSize
);
129 if (LogEntry
== NULL
) {
130 EfiReleaseLock (&Private
->DataLock
);
131 return EFI_OUT_OF_RESOURCES
;
134 ZeroMem (LogEntry
, TotalSize
);
136 Record
= (EFI_DATA_RECORD_HEADER
*) (LogEntry
+ 1);
137 Raw
= (VOID
*) (Record
+ 1);
140 // Build Standard Log Header
142 Record
->Version
= EFI_DATA_RECORD_HEADER_VERSION
;
143 Record
->HeaderSize
= sizeof (EFI_DATA_RECORD_HEADER
);
144 Record
->RecordSize
= RecordSize
;
145 CopyMem (&Record
->DataRecordGuid
, DataRecordGuid
, sizeof (EFI_GUID
));
146 CopyMem (&Record
->ProducerName
, ProducerName
, sizeof (EFI_GUID
));
147 Record
->DataRecordClass
= DataRecordClass
;
149 Record
->LogMonotonicCount
= Private
->GlobalMonotonicCount
++;
151 gRT
->GetTime (&Record
->LogTime
, NULL
);
154 // Insert log into the internal linked list.
156 LogEntry
->Signature
= EFI_DATA_ENTRY_SIGNATURE
;
157 LogEntry
->Record
= Record
;
158 LogEntry
->RecordSize
= sizeof (EFI_DATA_ENTRY
) + RawDataSize
;
159 InsertTailList (&Private
->DataListHead
, &LogEntry
->Link
);
161 CopyMem (Raw
, RawData
, RawDataSize
);
163 EfiReleaseLock (&Private
->DataLock
);
166 // Send Signal to all the filter drivers which are interested
167 // in the record's class and guid.
169 Head
= &Private
->FilterDriverListHead
;
170 for (Link
= Head
->ForwardLink
; Link
!= Head
; Link
= Link
->ForwardLink
) {
171 FilterEntry
= FILTER_ENTRY_FROM_LINK (Link
);
172 if (((FilterEntry
->ClassFilter
& DataRecordClass
) != 0) &&
173 (CompareGuid (&FilterEntry
->FilterDataRecordGuid
, &gZeroGuid
) ||
174 CompareGuid (&FilterEntry
->FilterDataRecordGuid
, DataRecordGuid
))) {
175 gBS
->SignalEvent (FilterEntry
->Event
);
185 DataHubGetNextRecord (
186 IN EFI_DATA_HUB_PROTOCOL
*This
,
187 IN OUT UINT64
*MonotonicCount
,
188 IN EFI_EVENT
*FilterDriverEvent
, OPTIONAL
189 OUT EFI_DATA_RECORD_HEADER
**Record
195 Get a previously logged data record and the MonotonicCount for the next
196 availible Record. This allows all records or all records later
197 than a give MonotonicCount to be returned. If an optional FilterDriverEvent
198 is passed in with a MonotonicCout of zero return the first record
199 not yet read by the filter driver. If FilterDriverEvent is NULL and
200 MonotonicCount is zero return the first data record.
204 This - The EFI_DATA_HUB_PROTOCOL instance.
205 MonotonicCount - Specifies the Record to return. On input, zero means
206 return the first record. On output, contains the next
207 record to availible. Zero indicates no more records.
208 FilterDriverEvent - If FilterDriverEvent is not passed in a MonotonicCount
209 of zero, it means to return the first data record.
210 If FilterDriverEvent is passed in, then a MonotonicCount
211 of zero means to return the first data not yet read by
213 Record - Returns a dynamically allocated memory buffer with a data
214 record that matches MonotonicCount.
218 EFI_SUCCESS - Data was returned in Record.
219 EFI_INVALID_PARAMETER - FilterDriverEvent was passed in but does not exist.
220 EFI_NOT_FOUND - MonotonicCount does not match any data record in the
221 system. If a MonotonicCount of zero was passed in, then
222 no data records exist in the system.
223 EFI_OUT_OF_RESOURCES - Record was not returned due to lack of system resources.
227 DATA_HUB_INSTANCE
*Private
;
228 DATA_HUB_FILTER_DRIVER
*FilterDriver
;
230 UINT64 FilterMonotonicCount
;
232 Private
= DATA_HUB_INSTANCE_FROM_THIS (This
);
235 FilterMonotonicCount
= 0;
236 ClassFilter
= EFI_DATA_RECORD_CLASS_DEBUG
|
237 EFI_DATA_RECORD_CLASS_ERROR
|
238 EFI_DATA_RECORD_CLASS_DATA
|
239 EFI_DATA_RECORD_CLASS_PROGRESS_CODE
;
241 if (FilterDriverEvent
!= NULL
) {
243 // For events the beginning is the last unread record. This info is
244 // stored in the instance structure, so we must look up the event
247 FilterDriver
= FindFilterDriverByEvent (
248 &Private
->FilterDriverListHead
,
251 if (FilterDriver
== NULL
) {
252 return EFI_INVALID_PARAMETER
;
255 // Use the Class filter the event was created with.
257 ClassFilter
= FilterDriver
->ClassFilter
;
259 if (*MonotonicCount
== 0) {
261 // Use the MTC from the Filter Driver.
263 FilterMonotonicCount
= FilterDriver
->GetNextMonotonicCount
;
264 if (FilterMonotonicCount
!= 0) {
266 // The GetNextMonotonicCount field remembers the last value from the previous time.
267 // But we already processed this vaule, so we need to find the next one. So if
268 // It is not the first time get the new record entry.
270 *Record
= GetNextDataRecord (&Private
->DataListHead
, ClassFilter
, &FilterMonotonicCount
);
271 *MonotonicCount
= FilterMonotonicCount
;
272 if (FilterMonotonicCount
== 0) {
274 // If there is no new record to get exit now.
276 return EFI_NOT_FOUND
;
284 *Record
= GetNextDataRecord (&Private
->DataListHead
, ClassFilter
, MonotonicCount
);
285 if (*Record
== NULL
) {
286 return EFI_NOT_FOUND
;
289 if (FilterDriver
!= NULL
) {
291 // If we have a filter driver update the records that have been read.
292 // If MonotonicCount is zero No more reacords left.
294 if (*MonotonicCount
== 0) {
295 if (FilterMonotonicCount
!= 0) {
297 // Return the result of our extra GetNextDataRecord.
299 FilterDriver
->GetNextMonotonicCount
= FilterMonotonicCount
;
303 // Point to next undread record
305 FilterDriver
->GetNextMonotonicCount
= *MonotonicCount
;
315 DataHubRegisterFilterDriver (
316 IN EFI_DATA_HUB_PROTOCOL
* This
,
317 IN EFI_EVENT FilterEvent
,
318 IN EFI_TPL FilterTpl
,
319 IN UINT64 FilterClass
,
320 IN EFI_GUID
* FilterDataRecordGuid OPTIONAL
326 This function registers the data hub filter driver that is represented
327 by FilterEvent. Only one instance of each FilterEvent can be registered.
328 After the FilterEvent is registered, it will be signaled so it can sync
329 with data records that have been recorded prior to the FilterEvent being
334 This - The EFI_DATA_HUB_PROTOCOL instance.
335 FilterEvent - The EFI_EVENT to signal whenever data that matches
336 FilterClass is logged in the system.
337 FilterTpl - The maximum EFI_TPL at which FilterEvent can be
338 signaled. It is strongly recommended that you use the
339 lowest EFI_TPL possible.
340 FilterClass - FilterEvent will be signaled whenever a bit in
341 EFI_DATA_RECORD_HEADER.DataRecordClass is also set in
342 FilterClass. If FilterClass is zero, no class-based
343 filtering will be performed.
344 FilterDataRecordGuid - FilterEvent will be signaled whenever FilterDataRecordGuid
345 matches EFI_DATA_RECORD_HEADER.DataRecordGuid. If
346 FilterDataRecordGuid is NULL, then no GUID-based filtering
350 EFI_SUCCESS - The filter driver event was registered.
351 EFI_ALREADY_STARTED - FilterEvent was previously registered and cannot be
353 EFI_OUT_OF_RESOURCES - The filter driver event was not registered due to lack of
358 DATA_HUB_INSTANCE
*Private
;
359 DATA_HUB_FILTER_DRIVER
*FilterDriver
;
361 Private
= DATA_HUB_INSTANCE_FROM_THIS (This
);
363 FilterDriver
= (DATA_HUB_FILTER_DRIVER
*) AllocateZeroPool (sizeof (DATA_HUB_FILTER_DRIVER
));
364 if (FilterDriver
== NULL
) {
365 return EFI_OUT_OF_RESOURCES
;
368 // Initialize filter driver info
370 FilterDriver
->Signature
= EFI_DATA_HUB_FILTER_DRIVER_SIGNATURE
;
371 FilterDriver
->Event
= FilterEvent
;
372 FilterDriver
->Tpl
= FilterTpl
;
373 FilterDriver
->GetNextMonotonicCount
= 0;
374 if (FilterClass
== 0) {
375 FilterDriver
->ClassFilter
= EFI_DATA_RECORD_CLASS_DEBUG
|
376 EFI_DATA_RECORD_CLASS_ERROR
|
377 EFI_DATA_RECORD_CLASS_DATA
|
378 EFI_DATA_RECORD_CLASS_PROGRESS_CODE
;
380 FilterDriver
->ClassFilter
= FilterClass
;
383 if (FilterDataRecordGuid
!= NULL
) {
384 CopyMem (&FilterDriver
->FilterDataRecordGuid
, FilterDataRecordGuid
, sizeof (EFI_GUID
));
387 // Search for duplicate entries
389 if (FindFilterDriverByEvent (&Private
->FilterDriverListHead
, FilterEvent
) != NULL
) {
390 FreePool (FilterDriver
);
391 return EFI_ALREADY_STARTED
;
394 // Make insertion an atomic operation with the lock.
396 EfiAcquireLock (&Private
->DataLock
);
397 InsertTailList (&Private
->FilterDriverListHead
, &FilterDriver
->Link
);
398 EfiReleaseLock (&Private
->DataLock
);
401 // Signal the Filter driver we just loaded so they will recieve all the
402 // previous history. If we did not signal here we would have to wait until
403 // the next data was logged to get the history. In a case where no next
404 // data was logged we would never get synced up.
406 gBS
->SignalEvent (FilterEvent
);
414 DataHubUnregisterFilterDriver (
415 IN EFI_DATA_HUB_PROTOCOL
*This
,
416 IN EFI_EVENT FilterEvent
422 Remove a Filter Driver, so it no longer gets called when data
423 information is logged.
427 This - Protocol instance structure
429 FilterEvent - Event that represents a filter driver that is to be
434 EFI_SUCCESS - If FilterEvent was unregistered
436 EFI_NOT_FOUND - If FilterEvent does not exist
440 DATA_HUB_INSTANCE
*Private
;
441 DATA_HUB_FILTER_DRIVER
*FilterDriver
;
443 Private
= DATA_HUB_INSTANCE_FROM_THIS (This
);
446 // Search for duplicate entries
448 FilterDriver
= FindFilterDriverByEvent (
449 &Private
->FilterDriverListHead
,
452 if (FilterDriver
== NULL
) {
453 return EFI_NOT_FOUND
;
456 // Make removal an atomic operation with the lock
458 EfiAcquireLock (&Private
->DataLock
);
459 RemoveEntryList (&FilterDriver
->Link
);
460 EfiReleaseLock (&Private
->DataLock
);
465 // STATIC Worker fucntions follow
468 DATA_HUB_FILTER_DRIVER
*
469 FindFilterDriverByEvent (
476 Search the Head list for a EFI_DATA_HUB_FILTER_DRIVER member that
477 represents Event and return it.
481 Head - Head of dual linked list of EFI_DATA_HUB_FILTER_DRIVER
484 Event - Event to be search for in the Head list.
488 EFI_DATA_HUB_FILTER_DRIVER - Returned if Event stored in the
489 Head doubly linked list.
491 NULL - If Event is not in the list
495 DATA_HUB_FILTER_DRIVER
*FilterEntry
;
498 for (Link
= Head
->ForwardLink
; Link
!= Head
; Link
= Link
->ForwardLink
) {
499 FilterEntry
= FILTER_ENTRY_FROM_LINK (Link
);
500 if (FilterEntry
->Event
== Event
) {
509 EFI_DATA_RECORD_HEADER
*
512 IN UINT64 ClassFilter
,
513 IN OUT UINT64
*PtrCurrentMTC
518 Search the Head doubly linked list for the passed in MTC. Return the
519 matching element in Head and the MTC on the next entry.
523 Head - Head of Data Log linked list.
525 ClassFilter - Only match the MTC if it is in the same Class as the
528 PtrCurrentMTC - On IN contians MTC to search for. On OUT contians next
529 MTC in the data log list or zero if at end of the list.
533 EFI_DATA_LOG_ENTRY - Return pointer to data log data from Head list.
535 NULL - If no data record exists.
539 EFI_DATA_ENTRY
*LogEntry
;
541 BOOLEAN ReturnFirstEntry
;
542 EFI_DATA_RECORD_HEADER
*Record
;
543 EFI_DATA_ENTRY
*NextLogEntry
;
546 // If MonotonicCount == 0 just return the first one
548 ReturnFirstEntry
= (BOOLEAN
) (*PtrCurrentMTC
== 0);
551 for (Link
= Head
->ForwardLink
; Link
!= Head
; Link
= Link
->ForwardLink
) {
552 LogEntry
= DATA_ENTRY_FROM_LINK (Link
);
553 if ((LogEntry
->Record
->DataRecordClass
& ClassFilter
) == 0) {
555 // Skip any entry that does not have the correct ClassFilter
560 if ((LogEntry
->Record
->LogMonotonicCount
== *PtrCurrentMTC
) || ReturnFirstEntry
) {
562 // Return record to the user
564 Record
= LogEntry
->Record
;
567 // Calculate the next MTC value. If there is no next entry set
571 for (Link
= Link
->ForwardLink
; Link
!= Head
; Link
= Link
->ForwardLink
) {
572 NextLogEntry
= DATA_ENTRY_FROM_LINK (Link
);
573 if ((NextLogEntry
->Record
->DataRecordClass
& ClassFilter
) != 0) {
575 // Return the MTC of the next thing to search for if found
577 *PtrCurrentMTC
= NextLogEntry
->Record
->LogMonotonicCount
;
582 // Record found exit loop and return
592 // Since this driver will only ever produce one instance of the Logging Hub
593 // protocol you are not required to dynamically allocate the PrivateData.
595 DATA_HUB_INSTANCE mPrivateData
;
600 IN EFI_HANDLE ImageHandle
,
601 IN EFI_SYSTEM_TABLE
*SystemTable
606 Install Driver to produce Data Hub protocol.
609 (Standard EFI Image entry - EFI_IMAGE_ENTRY_POINT)
613 EFI_SUCCESS - Logging Hub protocol installed
615 Other - No protocol installed, unload driver.
620 UINT32 HighMontonicCount
;
622 mPrivateData
.Signature
= DATA_HUB_INSTANCE_SIGNATURE
;
623 mPrivateData
.DataHub
.LogData
= DataHubLogData
;
624 mPrivateData
.DataHub
.GetNextRecord
= DataHubGetNextRecord
;
625 mPrivateData
.DataHub
.RegisterFilterDriver
= DataHubRegisterFilterDriver
;
626 mPrivateData
.DataHub
.UnregisterFilterDriver
= DataHubUnregisterFilterDriver
;
629 // Initialize Private Data in CORE_LOGGING_HUB_INSTANCE that is
630 // required by this protocol
632 InitializeListHead (&mPrivateData
.DataListHead
);
633 InitializeListHead (&mPrivateData
.FilterDriverListHead
);
635 EfiInitializeLock (&mPrivateData
.DataLock
, TPL_NOTIFY
);
638 // Make sure we get a bigger MTC number on every boot!
640 Status
= gRT
->GetNextHighMonotonicCount (&HighMontonicCount
);
641 if (EFI_ERROR (Status
)) {
643 // if system service fails pick a sane value.
645 mPrivateData
.GlobalMonotonicCount
= 0;
647 mPrivateData
.GlobalMonotonicCount
= LShiftU64 ((UINT64
) HighMontonicCount
, 32);
650 // Make a new handle and install the protocol
652 mPrivateData
.Handle
= NULL
;
653 Status
= gBS
->InstallProtocolInterface (
654 &mPrivateData
.Handle
,
655 &gEfiDataHubProtocolGuid
,
656 EFI_NATIVE_INTERFACE
,
657 &mPrivateData
.DataHub