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 Status
= gBS
->AllocatePool (EfiBootServicesData
, TotalSize
, (VOID
**) &LogEntry
);
128 if (EFI_ERROR (Status
)) {
129 EfiReleaseLock (&Private
->DataLock
);
130 return EFI_OUT_OF_RESOURCES
;
133 ZeroMem (LogEntry
, TotalSize
);
135 Record
= (EFI_DATA_RECORD_HEADER
*) (LogEntry
+ 1);
136 Raw
= (VOID
*) (Record
+ 1);
139 // Build Standard Log Header
141 Record
->Version
= EFI_DATA_RECORD_HEADER_VERSION
;
142 Record
->HeaderSize
= sizeof (EFI_DATA_RECORD_HEADER
);
143 Record
->RecordSize
= RecordSize
;
144 CopyMem (&Record
->DataRecordGuid
, DataRecordGuid
, sizeof (EFI_GUID
));
145 CopyMem (&Record
->ProducerName
, ProducerName
, sizeof (EFI_GUID
));
146 Record
->DataRecordClass
= DataRecordClass
;
148 Record
->LogMonotonicCount
= Private
->GlobalMonotonicCount
++;
150 gRT
->GetTime (&Record
->LogTime
, NULL
);
153 // Insert log into the internal linked list.
155 LogEntry
->Signature
= EFI_DATA_ENTRY_SIGNATURE
;
156 LogEntry
->Record
= Record
;
157 LogEntry
->RecordSize
= sizeof (EFI_DATA_ENTRY
) + RawDataSize
;
158 InsertTailList (&Private
->DataListHead
, &LogEntry
->Link
);
160 CopyMem (Raw
, RawData
, RawDataSize
);
162 EfiReleaseLock (&Private
->DataLock
);
165 // Send Signal to all the filter drivers which are interested
166 // in the record's class and guid.
168 Head
= &Private
->FilterDriverListHead
;
169 for (Link
= Head
->ForwardLink
; Link
!= Head
; Link
= Link
->ForwardLink
) {
170 FilterEntry
= FILTER_ENTRY_FROM_LINK (Link
);
171 if (((FilterEntry
->ClassFilter
& DataRecordClass
) != 0) &&
172 (CompareGuid (&FilterEntry
->FilterDataRecordGuid
, &gZeroGuid
) ||
173 CompareGuid (&FilterEntry
->FilterDataRecordGuid
, DataRecordGuid
))) {
174 gBS
->SignalEvent (FilterEntry
->Event
);
184 DataHubGetNextRecord (
185 IN EFI_DATA_HUB_PROTOCOL
*This
,
186 IN OUT UINT64
*MonotonicCount
,
187 IN EFI_EVENT
*FilterDriverEvent
, OPTIONAL
188 OUT EFI_DATA_RECORD_HEADER
**Record
194 Get a previously logged data record and the MonotonicCount for the next
195 availible Record. This allows all records or all records later
196 than a give MonotonicCount to be returned. If an optional FilterDriverEvent
197 is passed in with a MonotonicCout of zero return the first record
198 not yet read by the filter driver. If FilterDriverEvent is NULL and
199 MonotonicCount is zero return the first data record.
203 This - The EFI_DATA_HUB_PROTOCOL instance.
204 MonotonicCount - Specifies the Record to return. On input, zero means
205 return the first record. On output, contains the next
206 record to availible. Zero indicates no more records.
207 FilterDriverEvent - If FilterDriverEvent is not passed in a MonotonicCount
208 of zero, it means to return the first data record.
209 If FilterDriverEvent is passed in, then a MonotonicCount
210 of zero means to return the first data not yet read by
212 Record - Returns a dynamically allocated memory buffer with a data
213 record that matches MonotonicCount.
217 EFI_SUCCESS - Data was returned in Record.
218 EFI_INVALID_PARAMETER - FilterDriverEvent was passed in but does not exist.
219 EFI_NOT_FOUND - MonotonicCount does not match any data record in the
220 system. If a MonotonicCount of zero was passed in, then
221 no data records exist in the system.
222 EFI_OUT_OF_RESOURCES - Record was not returned due to lack of system resources.
226 DATA_HUB_INSTANCE
*Private
;
227 DATA_HUB_FILTER_DRIVER
*FilterDriver
;
229 UINT64 FilterMonotonicCount
;
231 Private
= DATA_HUB_INSTANCE_FROM_THIS (This
);
234 FilterMonotonicCount
= 0;
235 ClassFilter
= EFI_DATA_RECORD_CLASS_DEBUG
|
236 EFI_DATA_RECORD_CLASS_ERROR
|
237 EFI_DATA_RECORD_CLASS_DATA
|
238 EFI_DATA_RECORD_CLASS_PROGRESS_CODE
;
240 if (FilterDriverEvent
!= NULL
) {
242 // For events the beginning is the last unread record. This info is
243 // stored in the instance structure, so we must look up the event
246 FilterDriver
= FindFilterDriverByEvent (
247 &Private
->FilterDriverListHead
,
250 if (FilterDriver
== NULL
) {
251 return EFI_INVALID_PARAMETER
;
254 // Use the Class filter the event was created with.
256 ClassFilter
= FilterDriver
->ClassFilter
;
258 if (*MonotonicCount
== 0) {
260 // Use the MTC from the Filter Driver.
262 FilterMonotonicCount
= FilterDriver
->GetNextMonotonicCount
;
263 if (FilterMonotonicCount
!= 0) {
265 // The GetNextMonotonicCount field remembers the last value from the previous time.
266 // But we already processed this vaule, so we need to find the next one. So if
267 // It is not the first time get the new record entry.
269 *Record
= GetNextDataRecord (&Private
->DataListHead
, ClassFilter
, &FilterMonotonicCount
);
270 *MonotonicCount
= FilterMonotonicCount
;
271 if (FilterMonotonicCount
== 0) {
273 // If there is no new record to get exit now.
275 return EFI_NOT_FOUND
;
283 *Record
= GetNextDataRecord (&Private
->DataListHead
, ClassFilter
, MonotonicCount
);
284 if (*Record
== NULL
) {
285 return EFI_NOT_FOUND
;
288 if (FilterDriver
!= NULL
) {
290 // If we have a filter driver update the records that have been read.
291 // If MonotonicCount is zero No more reacords left.
293 if (*MonotonicCount
== 0) {
294 if (FilterMonotonicCount
!= 0) {
296 // Return the result of our extra GetNextDataRecord.
298 FilterDriver
->GetNextMonotonicCount
= FilterMonotonicCount
;
302 // Point to next undread record
304 FilterDriver
->GetNextMonotonicCount
= *MonotonicCount
;
314 DataHubRegisterFilterDriver (
315 IN EFI_DATA_HUB_PROTOCOL
* This
,
316 IN EFI_EVENT FilterEvent
,
317 IN EFI_TPL FilterTpl
,
318 IN UINT64 FilterClass
,
319 IN EFI_GUID
* FilterDataRecordGuid OPTIONAL
325 This function registers the data hub filter driver that is represented
326 by FilterEvent. Only one instance of each FilterEvent can be registered.
327 After the FilterEvent is registered, it will be signaled so it can sync
328 with data records that have been recorded prior to the FilterEvent being
333 This - The EFI_DATA_HUB_PROTOCOL instance.
334 FilterEvent - The EFI_EVENT to signal whenever data that matches
335 FilterClass is logged in the system.
336 FilterTpl - The maximum EFI_TPL at which FilterEvent can be
337 signaled. It is strongly recommended that you use the
338 lowest EFI_TPL possible.
339 FilterClass - FilterEvent will be signaled whenever a bit in
340 EFI_DATA_RECORD_HEADER.DataRecordClass is also set in
341 FilterClass. If FilterClass is zero, no class-based
342 filtering will be performed.
343 FilterDataRecordGuid - FilterEvent will be signaled whenever FilterDataRecordGuid
344 matches EFI_DATA_RECORD_HEADER.DataRecordGuid. If
345 FilterDataRecordGuid is NULL, then no GUID-based filtering
349 EFI_SUCCESS - The filter driver event was registered.
350 EFI_ALREADY_STARTED - FilterEvent was previously registered and cannot be
352 EFI_OUT_OF_RESOURCES - The filter driver event was not registered due to lack of
357 DATA_HUB_INSTANCE
*Private
;
358 DATA_HUB_FILTER_DRIVER
*FilterDriver
;
360 Private
= DATA_HUB_INSTANCE_FROM_THIS (This
);
362 FilterDriver
= (DATA_HUB_FILTER_DRIVER
*) AllocateZeroPool (sizeof (DATA_HUB_FILTER_DRIVER
));
363 if (FilterDriver
== NULL
) {
364 return EFI_OUT_OF_RESOURCES
;
367 // Initialize filter driver info
369 FilterDriver
->Signature
= EFI_DATA_HUB_FILTER_DRIVER_SIGNATURE
;
370 FilterDriver
->Event
= FilterEvent
;
371 FilterDriver
->Tpl
= FilterTpl
;
372 FilterDriver
->GetNextMonotonicCount
= 0;
373 if (FilterClass
== 0) {
374 FilterDriver
->ClassFilter
= EFI_DATA_RECORD_CLASS_DEBUG
|
375 EFI_DATA_RECORD_CLASS_ERROR
|
376 EFI_DATA_RECORD_CLASS_DATA
|
377 EFI_DATA_RECORD_CLASS_PROGRESS_CODE
;
379 FilterDriver
->ClassFilter
= FilterClass
;
382 if (FilterDataRecordGuid
!= NULL
) {
383 CopyMem (&FilterDriver
->FilterDataRecordGuid
, FilterDataRecordGuid
, sizeof (EFI_GUID
));
386 // Search for duplicate entries
388 if (FindFilterDriverByEvent (&Private
->FilterDriverListHead
, FilterEvent
) != NULL
) {
389 gBS
->FreePool (FilterDriver
);
390 return EFI_ALREADY_STARTED
;
393 // Make insertion an atomic operation with the lock.
395 EfiAcquireLock (&Private
->DataLock
);
396 InsertTailList (&Private
->FilterDriverListHead
, &FilterDriver
->Link
);
397 EfiReleaseLock (&Private
->DataLock
);
400 // Signal the Filter driver we just loaded so they will recieve all the
401 // previous history. If we did not signal here we would have to wait until
402 // the next data was logged to get the history. In a case where no next
403 // data was logged we would never get synced up.
405 gBS
->SignalEvent (FilterEvent
);
413 DataHubUnregisterFilterDriver (
414 IN EFI_DATA_HUB_PROTOCOL
*This
,
415 IN EFI_EVENT FilterEvent
421 Remove a Filter Driver, so it no longer gets called when data
422 information is logged.
426 This - Protocol instance structure
428 FilterEvent - Event that represents a filter driver that is to be
433 EFI_SUCCESS - If FilterEvent was unregistered
435 EFI_NOT_FOUND - If FilterEvent does not exist
439 DATA_HUB_INSTANCE
*Private
;
440 DATA_HUB_FILTER_DRIVER
*FilterDriver
;
442 Private
= DATA_HUB_INSTANCE_FROM_THIS (This
);
445 // Search for duplicate entries
447 FilterDriver
= FindFilterDriverByEvent (
448 &Private
->FilterDriverListHead
,
451 if (FilterDriver
== NULL
) {
452 return EFI_NOT_FOUND
;
455 // Make removal an atomic operation with the lock
457 EfiAcquireLock (&Private
->DataLock
);
458 RemoveEntryList (&FilterDriver
->Link
);
459 EfiReleaseLock (&Private
->DataLock
);
464 // STATIC Worker fucntions follow
467 DATA_HUB_FILTER_DRIVER
*
468 FindFilterDriverByEvent (
475 Search the Head list for a EFI_DATA_HUB_FILTER_DRIVER member that
476 represents Event and return it.
480 Head - Head of dual linked list of EFI_DATA_HUB_FILTER_DRIVER
483 Event - Event to be search for in the Head list.
487 EFI_DATA_HUB_FILTER_DRIVER - Returned if Event stored in the
488 Head doubly linked list.
490 NULL - If Event is not in the list
494 DATA_HUB_FILTER_DRIVER
*FilterEntry
;
497 for (Link
= Head
->ForwardLink
; Link
!= Head
; Link
= Link
->ForwardLink
) {
498 FilterEntry
= FILTER_ENTRY_FROM_LINK (Link
);
499 if (FilterEntry
->Event
== Event
) {
508 EFI_DATA_RECORD_HEADER
*
511 IN UINT64 ClassFilter
,
512 IN OUT UINT64
*PtrCurrentMTC
517 Search the Head doubly linked list for the passed in MTC. Return the
518 matching element in Head and the MTC on the next entry.
522 Head - Head of Data Log linked list.
524 ClassFilter - Only match the MTC if it is in the same Class as the
527 PtrCurrentMTC - On IN contians MTC to search for. On OUT contians next
528 MTC in the data log list or zero if at end of the list.
532 EFI_DATA_LOG_ENTRY - Return pointer to data log data from Head list.
534 NULL - If no data record exists.
538 EFI_DATA_ENTRY
*LogEntry
;
540 BOOLEAN ReturnFirstEntry
;
541 EFI_DATA_RECORD_HEADER
*Record
;
542 EFI_DATA_ENTRY
*NextLogEntry
;
545 // If MonotonicCount == 0 just return the first one
547 ReturnFirstEntry
= (BOOLEAN
) (*PtrCurrentMTC
== 0);
550 for (Link
= Head
->ForwardLink
; Link
!= Head
; Link
= Link
->ForwardLink
) {
551 LogEntry
= DATA_ENTRY_FROM_LINK (Link
);
552 if ((LogEntry
->Record
->DataRecordClass
& ClassFilter
) == 0) {
554 // Skip any entry that does not have the correct ClassFilter
559 if ((LogEntry
->Record
->LogMonotonicCount
== *PtrCurrentMTC
) || ReturnFirstEntry
) {
561 // Return record to the user
563 Record
= LogEntry
->Record
;
566 // Calculate the next MTC value. If there is no next entry set
570 for (Link
= Link
->ForwardLink
; Link
!= Head
; Link
= Link
->ForwardLink
) {
571 NextLogEntry
= DATA_ENTRY_FROM_LINK (Link
);
572 if ((NextLogEntry
->Record
->DataRecordClass
& ClassFilter
) != 0) {
574 // Return the MTC of the next thing to search for if found
576 *PtrCurrentMTC
= NextLogEntry
->Record
->LogMonotonicCount
;
581 // Record found exit loop and return
591 // Since this driver will only ever produce one instance of the Logging Hub
592 // protocol you are not required to dynamically allocate the PrivateData.
594 DATA_HUB_INSTANCE mPrivateData
;
599 IN EFI_HANDLE ImageHandle
,
600 IN EFI_SYSTEM_TABLE
*SystemTable
605 Install Driver to produce Data Hub protocol.
608 (Standard EFI Image entry - EFI_IMAGE_ENTRY_POINT)
612 EFI_SUCCESS - Logging Hub protocol installed
614 Other - No protocol installed, unload driver.
619 UINT32 HighMontonicCount
;
621 mPrivateData
.Signature
= DATA_HUB_INSTANCE_SIGNATURE
;
622 mPrivateData
.DataHub
.LogData
= DataHubLogData
;
623 mPrivateData
.DataHub
.GetNextRecord
= DataHubGetNextRecord
;
624 mPrivateData
.DataHub
.RegisterFilterDriver
= DataHubRegisterFilterDriver
;
625 mPrivateData
.DataHub
.UnregisterFilterDriver
= DataHubUnregisterFilterDriver
;
628 // Initialize Private Data in CORE_LOGGING_HUB_INSTANCE that is
629 // required by this protocol
631 InitializeListHead (&mPrivateData
.DataListHead
);
632 InitializeListHead (&mPrivateData
.FilterDriverListHead
);
634 EfiInitializeLock (&mPrivateData
.DataLock
, EFI_TPL_NOTIFY
);
637 // Make sure we get a bigger MTC number on every boot!
639 Status
= gRT
->GetNextHighMonotonicCount (&HighMontonicCount
);
640 if (EFI_ERROR (Status
)) {
642 // if system service fails pick a sane value.
644 mPrivateData
.GlobalMonotonicCount
= 0;
646 mPrivateData
.GlobalMonotonicCount
= LShiftU64 ((UINT64
) HighMontonicCount
, 32);
649 // Make a new handle and install the protocol
651 mPrivateData
.Handle
= NULL
;
652 Status
= gBS
->InstallProtocolInterface (
653 &mPrivateData
.Handle
,
654 &gEfiDataHubProtocolGuid
,
655 EFI_NATIVE_INTERFACE
,
656 &mPrivateData
.DataHub