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
56 IN EFI_DATA_HUB_PROTOCOL
*This
,
57 IN EFI_GUID
*DataRecordGuid
,
58 IN EFI_GUID
*ProducerName
,
59 IN UINT64 DataRecordClass
,
67 Log data record into the data logging hub
71 This - Protocol instance structure
73 DataRecordGuid - GUID that defines record contents
75 ProducerName - GUID that defines the name of the producer of the data
77 DataRecordClass - Class that defines generic record type
79 RawData - Data Log record as defined by DataRecordGuid
81 RawDataSize - Size of Data Log data in bytes
85 EFI_SUCCESS - If data was logged
87 EFI_OUT_OF_RESOURCES - If data was not logged due to lack of system
92 DATA_HUB_INSTANCE
*Private
;
93 EFI_DATA_ENTRY
*LogEntry
;
96 EFI_DATA_RECORD_HEADER
*Record
;
98 DATA_HUB_FILTER_DRIVER
*FilterEntry
;
102 Private
= DATA_HUB_INSTANCE_FROM_THIS (This
);
105 // Combine the storage for the internal structs and a copy of the log record.
106 // Record follows PrivateLogEntry. The consumer will be returned a pointer
107 // to Record so we don't what it to be the thing that was allocated from
108 // pool, so the consumer can't free an data record by mistake.
110 RecordSize
= sizeof (EFI_DATA_RECORD_HEADER
) + RawDataSize
;
111 TotalSize
= sizeof (EFI_DATA_ENTRY
) + RecordSize
;
114 // The Logging action is the critical section, so it is locked.
115 // The MTC asignment & update, time, and logging must be an
116 // atomic operation, so use the lock.
118 Status
= EfiAcquireLockOrFail (&Private
->DataLock
);
119 if (EFI_ERROR (Status
)) {
121 // Reentrancy detected so exit!
126 Status
= gBS
->AllocatePool (EfiBootServicesData
, TotalSize
, (VOID
**) &LogEntry
);
127 if (EFI_ERROR (Status
)) {
128 EfiReleaseLock (&Private
->DataLock
);
129 return EFI_OUT_OF_RESOURCES
;
132 ZeroMem (LogEntry
, TotalSize
);
134 Record
= (EFI_DATA_RECORD_HEADER
*) (LogEntry
+ 1);
135 Raw
= (VOID
*) (Record
+ 1);
138 // Build Standard Log Header
140 Record
->Version
= EFI_DATA_RECORD_HEADER_VERSION
;
141 Record
->HeaderSize
= sizeof (EFI_DATA_RECORD_HEADER
);
142 Record
->RecordSize
= RecordSize
;
143 CopyMem (&Record
->DataRecordGuid
, DataRecordGuid
, sizeof (EFI_GUID
));
144 CopyMem (&Record
->ProducerName
, ProducerName
, sizeof (EFI_GUID
));
145 Record
->DataRecordClass
= DataRecordClass
;
147 Record
->LogMonotonicCount
= Private
->GlobalMonotonicCount
++;
149 gRT
->GetTime (&Record
->LogTime
, NULL
);
152 // Insert log into the internal linked list.
154 LogEntry
->Signature
= EFI_DATA_ENTRY_SIGNATURE
;
155 LogEntry
->Record
= Record
;
156 LogEntry
->RecordSize
= sizeof (EFI_DATA_ENTRY
) + RawDataSize
;
157 InsertTailList (&Private
->DataListHead
, &LogEntry
->Link
);
159 CopyMem (Raw
, RawData
, RawDataSize
);
161 EfiReleaseLock (&Private
->DataLock
);
164 // Send Signal to all the filter drivers which are interested
165 // in the record's class and guid.
167 Head
= &Private
->FilterDriverListHead
;
168 for (Link
= Head
->ForwardLink
; Link
!= Head
; Link
= Link
->ForwardLink
) {
169 FilterEntry
= FILTER_ENTRY_FROM_LINK (Link
);
170 if (((FilterEntry
->ClassFilter
& DataRecordClass
) != 0) &&
171 (CompareGuid (&FilterEntry
->FilterDataRecordGuid
, &gZeroGuid
) ||
172 CompareGuid (&FilterEntry
->FilterDataRecordGuid
, DataRecordGuid
))) {
173 gBS
->SignalEvent (FilterEntry
->Event
);
182 DataHubGetNextRecord (
183 IN EFI_DATA_HUB_PROTOCOL
*This
,
184 IN OUT UINT64
*MonotonicCount
,
185 IN EFI_EVENT
*FilterDriverEvent
, OPTIONAL
186 OUT EFI_DATA_RECORD_HEADER
**Record
192 Get a previously logged data record and the MonotonicCount for the next
193 availible Record. This allows all records or all records later
194 than a give MonotonicCount to be returned. If an optional FilterDriverEvent
195 is passed in with a MonotonicCout of zero return the first record
196 not yet read by the filter driver. If FilterDriverEvent is NULL and
197 MonotonicCount is zero return the first data record.
201 This - The EFI_DATA_HUB_PROTOCOL instance.
202 MonotonicCount - Specifies the Record to return. On input, zero means
203 return the first record. On output, contains the next
204 record to availible. Zero indicates no more records.
205 FilterDriverEvent - If FilterDriverEvent is not passed in a MonotonicCount
206 of zero, it means to return the first data record.
207 If FilterDriverEvent is passed in, then a MonotonicCount
208 of zero means to return the first data not yet read by
210 Record - Returns a dynamically allocated memory buffer with a data
211 record that matches MonotonicCount.
215 EFI_SUCCESS - Data was returned in Record.
216 EFI_INVALID_PARAMETER - FilterDriverEvent was passed in but does not exist.
217 EFI_NOT_FOUND - MonotonicCount does not match any data record in the
218 system. If a MonotonicCount of zero was passed in, then
219 no data records exist in the system.
220 EFI_OUT_OF_RESOURCES - Record was not returned due to lack of system resources.
224 DATA_HUB_INSTANCE
*Private
;
225 DATA_HUB_FILTER_DRIVER
*FilterDriver
;
227 UINT64 FilterMonotonicCount
;
229 Private
= DATA_HUB_INSTANCE_FROM_THIS (This
);
232 FilterMonotonicCount
= 0;
233 ClassFilter
= EFI_DATA_RECORD_CLASS_DEBUG
|
234 EFI_DATA_RECORD_CLASS_ERROR
|
235 EFI_DATA_RECORD_CLASS_DATA
|
236 EFI_DATA_RECORD_CLASS_PROGRESS_CODE
;
238 if (FilterDriverEvent
!= NULL
) {
240 // For events the beginning is the last unread record. This info is
241 // stored in the instance structure, so we must look up the event
244 FilterDriver
= FindFilterDriverByEvent (
245 &Private
->FilterDriverListHead
,
248 if (FilterDriver
== NULL
) {
249 return EFI_INVALID_PARAMETER
;
252 // Use the Class filter the event was created with.
254 ClassFilter
= FilterDriver
->ClassFilter
;
256 if (*MonotonicCount
== 0) {
258 // Use the MTC from the Filter Driver.
260 FilterMonotonicCount
= FilterDriver
->GetNextMonotonicCount
;
261 if (FilterMonotonicCount
!= 0) {
263 // The GetNextMonotonicCount field remembers the last value from the previous time.
264 // But we already processed this vaule, so we need to find the next one. So if
265 // It is not the first time get the new record entry.
267 *Record
= GetNextDataRecord (&Private
->DataListHead
, ClassFilter
, &FilterMonotonicCount
);
268 *MonotonicCount
= FilterMonotonicCount
;
269 if (FilterMonotonicCount
== 0) {
271 // If there is no new record to get exit now.
273 return EFI_NOT_FOUND
;
281 *Record
= GetNextDataRecord (&Private
->DataListHead
, ClassFilter
, MonotonicCount
);
282 if (*Record
== NULL
) {
283 return EFI_NOT_FOUND
;
286 if (FilterDriver
!= NULL
) {
288 // If we have a filter driver update the records that have been read.
289 // If MonotonicCount is zero No more reacords left.
291 if (*MonotonicCount
== 0) {
292 if (FilterMonotonicCount
!= 0) {
294 // Return the result of our extra GetNextDataRecord.
296 FilterDriver
->GetNextMonotonicCount
= FilterMonotonicCount
;
300 // Point to next undread record
302 FilterDriver
->GetNextMonotonicCount
= *MonotonicCount
;
311 DataHubRegisterFilterDriver (
312 IN EFI_DATA_HUB_PROTOCOL
* This
,
313 IN EFI_EVENT FilterEvent
,
314 IN EFI_TPL FilterTpl
,
315 IN UINT64 FilterClass
,
316 IN EFI_GUID
* FilterDataRecordGuid OPTIONAL
322 This function registers the data hub filter driver that is represented
323 by FilterEvent. Only one instance of each FilterEvent can be registered.
324 After the FilterEvent is registered, it will be signaled so it can sync
325 with data records that have been recorded prior to the FilterEvent being
330 This - The EFI_DATA_HUB_PROTOCOL instance.
331 FilterEvent - The EFI_EVENT to signal whenever data that matches
332 FilterClass is logged in the system.
333 FilterTpl - The maximum EFI_TPL at which FilterEvent can be
334 signaled. It is strongly recommended that you use the
335 lowest EFI_TPL possible.
336 FilterClass - FilterEvent will be signaled whenever a bit in
337 EFI_DATA_RECORD_HEADER.DataRecordClass is also set in
338 FilterClass. If FilterClass is zero, no class-based
339 filtering will be performed.
340 FilterDataRecordGuid - FilterEvent will be signaled whenever FilterDataRecordGuid
341 matches EFI_DATA_RECORD_HEADER.DataRecordGuid. If
342 FilterDataRecordGuid is NULL, then no GUID-based filtering
346 EFI_SUCCESS - The filter driver event was registered.
347 EFI_ALREADY_STARTED - FilterEvent was previously registered and cannot be
349 EFI_OUT_OF_RESOURCES - The filter driver event was not registered due to lack of
354 DATA_HUB_INSTANCE
*Private
;
355 DATA_HUB_FILTER_DRIVER
*FilterDriver
;
357 Private
= DATA_HUB_INSTANCE_FROM_THIS (This
);
359 FilterDriver
= (DATA_HUB_FILTER_DRIVER
*) AllocateZeroPool (sizeof (DATA_HUB_FILTER_DRIVER
));
360 if (FilterDriver
== NULL
) {
361 return EFI_OUT_OF_RESOURCES
;
364 // Initialize filter driver info
366 FilterDriver
->Signature
= EFI_DATA_HUB_FILTER_DRIVER_SIGNATURE
;
367 FilterDriver
->Event
= FilterEvent
;
368 FilterDriver
->Tpl
= FilterTpl
;
369 FilterDriver
->GetNextMonotonicCount
= 0;
370 if (FilterClass
== 0) {
371 FilterDriver
->ClassFilter
= EFI_DATA_RECORD_CLASS_DEBUG
|
372 EFI_DATA_RECORD_CLASS_ERROR
|
373 EFI_DATA_RECORD_CLASS_DATA
|
374 EFI_DATA_RECORD_CLASS_PROGRESS_CODE
;
376 FilterDriver
->ClassFilter
= FilterClass
;
379 if (FilterDataRecordGuid
!= NULL
) {
380 CopyMem (&FilterDriver
->FilterDataRecordGuid
, FilterDataRecordGuid
, sizeof (EFI_GUID
));
383 // Search for duplicate entries
385 if (FindFilterDriverByEvent (&Private
->FilterDriverListHead
, FilterEvent
) != NULL
) {
386 gBS
->FreePool (FilterDriver
);
387 return EFI_ALREADY_STARTED
;
390 // Make insertion an atomic operation with the lock.
392 EfiAcquireLock (&Private
->DataLock
);
393 InsertTailList (&Private
->FilterDriverListHead
, &FilterDriver
->Link
);
394 EfiReleaseLock (&Private
->DataLock
);
397 // Signal the Filter driver we just loaded so they will recieve all the
398 // previous history. If we did not signal here we would have to wait until
399 // the next data was logged to get the history. In a case where no next
400 // data was logged we would never get synced up.
402 gBS
->SignalEvent (FilterEvent
);
409 DataHubUnregisterFilterDriver (
410 IN EFI_DATA_HUB_PROTOCOL
*This
,
411 IN EFI_EVENT FilterEvent
417 Remove a Filter Driver, so it no longer gets called when data
418 information is logged.
422 This - Protocol instance structure
424 FilterEvent - Event that represents a filter driver that is to be
429 EFI_SUCCESS - If FilterEvent was unregistered
431 EFI_NOT_FOUND - If FilterEvent does not exist
435 DATA_HUB_INSTANCE
*Private
;
436 DATA_HUB_FILTER_DRIVER
*FilterDriver
;
438 Private
= DATA_HUB_INSTANCE_FROM_THIS (This
);
441 // Search for duplicate entries
443 FilterDriver
= FindFilterDriverByEvent (
444 &Private
->FilterDriverListHead
,
447 if (FilterDriver
== NULL
) {
448 return EFI_NOT_FOUND
;
451 // Make removal an atomic operation with the lock
453 EfiAcquireLock (&Private
->DataLock
);
454 RemoveEntryList (&FilterDriver
->Link
);
455 EfiReleaseLock (&Private
->DataLock
);
460 // STATIC Worker fucntions follow
463 DATA_HUB_FILTER_DRIVER
*
464 FindFilterDriverByEvent (
471 Search the Head list for a EFI_DATA_HUB_FILTER_DRIVER member that
472 represents Event and return it.
476 Head - Head of dual linked list of EFI_DATA_HUB_FILTER_DRIVER
479 Event - Event to be search for in the Head list.
483 EFI_DATA_HUB_FILTER_DRIVER - Returned if Event stored in the
484 Head doubly linked list.
486 NULL - If Event is not in the list
490 DATA_HUB_FILTER_DRIVER
*FilterEntry
;
493 for (Link
= Head
->ForwardLink
; Link
!= Head
; Link
= Link
->ForwardLink
) {
494 FilterEntry
= FILTER_ENTRY_FROM_LINK (Link
);
495 if (FilterEntry
->Event
== Event
) {
504 EFI_DATA_RECORD_HEADER
*
507 IN UINT64 ClassFilter
,
508 IN OUT UINT64
*PtrCurrentMTC
513 Search the Head doubly linked list for the passed in MTC. Return the
514 matching element in Head and the MTC on the next entry.
518 Head - Head of Data Log linked list.
520 ClassFilter - Only match the MTC if it is in the same Class as the
523 PtrCurrentMTC - On IN contians MTC to search for. On OUT contians next
524 MTC in the data log list or zero if at end of the list.
528 EFI_DATA_LOG_ENTRY - Return pointer to data log data from Head list.
530 NULL - If no data record exists.
534 EFI_DATA_ENTRY
*LogEntry
;
536 BOOLEAN ReturnFirstEntry
;
537 EFI_DATA_RECORD_HEADER
*Record
;
538 EFI_DATA_ENTRY
*NextLogEntry
;
541 // If MonotonicCount == 0 just return the first one
543 ReturnFirstEntry
= (BOOLEAN
) (*PtrCurrentMTC
== 0);
546 for (Link
= Head
->ForwardLink
; Link
!= Head
; Link
= Link
->ForwardLink
) {
547 LogEntry
= DATA_ENTRY_FROM_LINK (Link
);
548 if ((LogEntry
->Record
->DataRecordClass
& ClassFilter
) == 0) {
550 // Skip any entry that does not have the correct ClassFilter
555 if ((LogEntry
->Record
->LogMonotonicCount
== *PtrCurrentMTC
) || ReturnFirstEntry
) {
557 // Return record to the user
559 Record
= LogEntry
->Record
;
562 // Calculate the next MTC value. If there is no next entry set
566 for (Link
= Link
->ForwardLink
; Link
!= Head
; Link
= Link
->ForwardLink
) {
567 NextLogEntry
= DATA_ENTRY_FROM_LINK (Link
);
568 if ((NextLogEntry
->Record
->DataRecordClass
& ClassFilter
) != 0) {
570 // Return the MTC of the next thing to search for if found
572 *PtrCurrentMTC
= NextLogEntry
->Record
->LogMonotonicCount
;
577 // Record found exit loop and return
587 // Since this driver will only ever produce one instance of the Logging Hub
588 // protocol you are not required to dynamically allocate the PrivateData.
590 DATA_HUB_INSTANCE mPrivateData
;
595 IN EFI_HANDLE ImageHandle
,
596 IN EFI_SYSTEM_TABLE
*SystemTable
601 Install Driver to produce Data Hub protocol.
604 (Standard EFI Image entry - EFI_IMAGE_ENTRY_POINT)
608 EFI_SUCCESS - Logging Hub protocol installed
610 Other - No protocol installed, unload driver.
615 UINT32 HighMontonicCount
;
617 mPrivateData
.Signature
= DATA_HUB_INSTANCE_SIGNATURE
;
618 mPrivateData
.DataHub
.LogData
= DataHubLogData
;
619 mPrivateData
.DataHub
.GetNextRecord
= DataHubGetNextRecord
;
620 mPrivateData
.DataHub
.RegisterFilterDriver
= DataHubRegisterFilterDriver
;
621 mPrivateData
.DataHub
.UnregisterFilterDriver
= DataHubUnregisterFilterDriver
;
624 // Initialize Private Data in CORE_LOGGING_HUB_INSTANCE that is
625 // required by this protocol
627 InitializeListHead (&mPrivateData
.DataListHead
);
628 InitializeListHead (&mPrivateData
.FilterDriverListHead
);
630 EfiInitializeLock (&mPrivateData
.DataLock
, EFI_TPL_NOTIFY
);
633 // Make sure we get a bigger MTC number on every boot!
635 Status
= gRT
->GetNextHighMonotonicCount (&HighMontonicCount
);
636 if (EFI_ERROR (Status
)) {
638 // if system service fails pick a sane value.
640 mPrivateData
.GlobalMonotonicCount
= 0;
642 mPrivateData
.GlobalMonotonicCount
= LShiftU64 ((UINT64
) HighMontonicCount
, 32);
645 // Make a new handle and install the protocol
647 mPrivateData
.Handle
= NULL
;
648 Status
= gBS
->InstallProtocolInterface (
649 &mPrivateData
.Handle
,
650 &gEfiDataHubProtocolGuid
,
651 EFI_NATIVE_INTERFACE
,
652 &mPrivateData
.DataHub