]> git.proxmox.com Git - mirror_edk2.git/blob - IntelFrameworkModulePkg/Universal/DataHubDxe/DataHub.c
Patch to remove STATIC modifier. This is on longer recommended by EFI Framework codin...
[mirror_edk2.git] / IntelFrameworkModulePkg / Universal / DataHubDxe / DataHub.c
1 /**@file
2 This code produces the Data Hub protocol. It preloads the data hub
3 with status information copied in from PEI HOBs.
4
5 Only code that implements the Data Hub protocol should go in this file!
6
7 The Term MTC stands for MonoTonicCounter.
8
9 For more information please look at DataHub.doc
10
11 NOTE: For extra security of the log GetNextDataRecord () could return a copy
12 of the data record.
13
14 Copyright (c) 2006, Intel Corporation
15 All rights reserved. This program and the accompanying materials
16 are licensed and made available under the terms and conditions of the BSD License
17 which accompanies this distribution. The full text of the license may be found at
18 http://opensource.org/licenses/bsd-license.php
19
20 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
21 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
22
23 **/
24
25 #include "DataHub.h"
26
27 CONST EFI_GUID gZeroGuid = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
28
29 //
30 // Worker functions private to this file
31 //
32 DATA_HUB_FILTER_DRIVER *
33 FindFilterDriverByEvent (
34 IN LIST_ENTRY *Head,
35 IN EFI_EVENT Event
36 );
37
38 EFI_DATA_RECORD_HEADER *
39 GetNextDataRecord (
40 IN LIST_ENTRY *Head,
41 IN UINT64 ClassFilter,
42 IN OUT UINT64 *PtrCurrentMTC
43 );
44
45 /**
46
47 Log data record into the data logging hub
48
49 @param This - Protocol instance structure
50 @param DataRecordGuid - GUID that defines record contents
51 @param ProducerName - GUID that defines the name of the producer of the data
52 @param DataRecordClass - Class that defines generic record type
53 @param RawData - Data Log record as defined by DataRecordGuid
54 @param RawDataSize - Size of Data Log data in bytes
55
56 @retval EFI_SUCCESS - If data was logged
57 @retval EFI_OUT_OF_RESOURCES - If data was not logged due to lack of system
58 resources.
59 **/
60 EFI_STATUS
61 EFIAPI
62 DataHubLogData (
63 IN EFI_DATA_HUB_PROTOCOL *This,
64 IN EFI_GUID *DataRecordGuid,
65 IN EFI_GUID *ProducerName,
66 IN UINT64 DataRecordClass,
67 IN VOID *RawData,
68 IN UINT32 RawDataSize
69 )
70 {
71 EFI_STATUS Status;
72 DATA_HUB_INSTANCE *Private;
73 EFI_DATA_ENTRY *LogEntry;
74 UINT32 TotalSize;
75 UINT32 RecordSize;
76 EFI_DATA_RECORD_HEADER *Record;
77 VOID *Raw;
78 DATA_HUB_FILTER_DRIVER *FilterEntry;
79 LIST_ENTRY *Link;
80 LIST_ENTRY *Head;
81
82 Private = DATA_HUB_INSTANCE_FROM_THIS (This);
83
84 //
85 // Combine the storage for the internal structs and a copy of the log record.
86 // Record follows PrivateLogEntry. The consumer will be returned a pointer
87 // to Record so we don't what it to be the thing that was allocated from
88 // pool, so the consumer can't free an data record by mistake.
89 //
90 RecordSize = sizeof (EFI_DATA_RECORD_HEADER) + RawDataSize;
91 TotalSize = sizeof (EFI_DATA_ENTRY) + RecordSize;
92
93 //
94 // The Logging action is the critical section, so it is locked.
95 // The MTC asignment & update, time, and logging must be an
96 // atomic operation, so use the lock.
97 //
98 Status = EfiAcquireLockOrFail (&Private->DataLock);
99 if (EFI_ERROR (Status)) {
100 //
101 // Reentrancy detected so exit!
102 //
103 return Status;
104 }
105
106 LogEntry = AllocatePool (TotalSize);
107
108 if (LogEntry == NULL) {
109 EfiReleaseLock (&Private->DataLock);
110 return EFI_OUT_OF_RESOURCES;
111 }
112
113 ZeroMem (LogEntry, TotalSize);
114
115 Record = (EFI_DATA_RECORD_HEADER *) (LogEntry + 1);
116 Raw = (VOID *) (Record + 1);
117
118 //
119 // Build Standard Log Header
120 //
121 Record->Version = EFI_DATA_RECORD_HEADER_VERSION;
122 Record->HeaderSize = sizeof (EFI_DATA_RECORD_HEADER);
123 Record->RecordSize = RecordSize;
124 CopyMem (&Record->DataRecordGuid, DataRecordGuid, sizeof (EFI_GUID));
125 CopyMem (&Record->ProducerName, ProducerName, sizeof (EFI_GUID));
126 Record->DataRecordClass = DataRecordClass;
127
128 //
129 // Ensure LogMonotonicCount is not zero
130 //
131 Record->LogMonotonicCount = ++Private->GlobalMonotonicCount;
132
133 gRT->GetTime (&Record->LogTime, NULL);
134
135 //
136 // Insert log into the internal linked list.
137 //
138 LogEntry->Signature = EFI_DATA_ENTRY_SIGNATURE;
139 LogEntry->Record = Record;
140 LogEntry->RecordSize = sizeof (EFI_DATA_ENTRY) + RawDataSize;
141 InsertTailList (&Private->DataListHead, &LogEntry->Link);
142
143 CopyMem (Raw, RawData, RawDataSize);
144
145 EfiReleaseLock (&Private->DataLock);
146
147 //
148 // Send Signal to all the filter drivers which are interested
149 // in the record's class and guid.
150 //
151 Head = &Private->FilterDriverListHead;
152 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
153 FilterEntry = FILTER_ENTRY_FROM_LINK (Link);
154 if (((FilterEntry->ClassFilter & DataRecordClass) != 0) &&
155 (CompareGuid (&FilterEntry->FilterDataRecordGuid, &gZeroGuid) ||
156 CompareGuid (&FilterEntry->FilterDataRecordGuid, DataRecordGuid))) {
157 gBS->SignalEvent (FilterEntry->Event);
158 }
159 }
160
161 return EFI_SUCCESS;
162 }
163
164 /**
165
166 Get a previously logged data record and the MonotonicCount for the next
167 availible Record. This allows all records or all records later
168 than a give MonotonicCount to be returned. If an optional FilterDriverEvent
169 is passed in with a MonotonicCout of zero return the first record
170 not yet read by the filter driver. If FilterDriverEvent is NULL and
171 MonotonicCount is zero return the first data record.
172
173 @param This The EFI_DATA_HUB_PROTOCOL instance.
174 @param MonotonicCount Specifies the Record to return. On input, zero means
175 return the first record. On output, contains the next
176 record to availible. Zero indicates no more records.
177 @param FilterDriverEvent If FilterDriverEvent is not passed in a MonotonicCount
178 of zero, it means to return the first data record.
179 If FilterDriverEvent is passed in, then a MonotonicCount
180 of zero means to return the first data not yet read by
181 FilterDriverEvent.
182 @param Record Returns a dynamically allocated memory buffer with a data
183 record that matches MonotonicCount.
184
185 @retval EFI_SUCCESS - Data was returned in Record.
186 @retval EFI_INVALID_PARAMETER - FilterDriverEvent was passed in but does not exist.
187 @retval EFI_NOT_FOUND - MonotonicCount does not match any data record in the
188 system. If a MonotonicCount of zero was passed in, then
189 no data records exist in the system.
190 @retval EFI_OUT_OF_RESOURCES - Record was not returned due to lack of system resources.
191
192 **/
193 EFI_STATUS
194 EFIAPI
195 DataHubGetNextRecord (
196 IN EFI_DATA_HUB_PROTOCOL *This,
197 IN OUT UINT64 *MonotonicCount,
198 IN EFI_EVENT *FilterDriverEvent, OPTIONAL
199 OUT EFI_DATA_RECORD_HEADER **Record
200 )
201 {
202 DATA_HUB_INSTANCE *Private;
203 DATA_HUB_FILTER_DRIVER *FilterDriver;
204 UINT64 ClassFilter;
205 UINT64 FilterMonotonicCount;
206
207 Private = DATA_HUB_INSTANCE_FROM_THIS (This);
208
209 FilterDriver = NULL;
210 FilterMonotonicCount = 0;
211 ClassFilter = EFI_DATA_RECORD_CLASS_DEBUG |
212 EFI_DATA_RECORD_CLASS_ERROR |
213 EFI_DATA_RECORD_CLASS_DATA |
214 EFI_DATA_RECORD_CLASS_PROGRESS_CODE;
215
216 if (FilterDriverEvent != NULL) {
217 //
218 // For events the beginning is the last unread record. This info is
219 // stored in the instance structure, so we must look up the event
220 // to get the data.
221 //
222 FilterDriver = FindFilterDriverByEvent (
223 &Private->FilterDriverListHead,
224 *FilterDriverEvent
225 );
226 if (FilterDriver == NULL) {
227 return EFI_INVALID_PARAMETER;
228 }
229 //
230 // Use the Class filter the event was created with.
231 //
232 ClassFilter = FilterDriver->ClassFilter;
233
234 if (*MonotonicCount == 0) {
235 //
236 // Use the MTC from the Filter Driver.
237 //
238 FilterMonotonicCount = FilterDriver->GetNextMonotonicCount;
239 if (FilterMonotonicCount != 0) {
240 //
241 // The GetNextMonotonicCount field remembers the last value from the previous time.
242 // But we already processed this vaule, so we need to find the next one.
243 //
244 *Record = GetNextDataRecord (&Private->DataListHead, ClassFilter, &FilterMonotonicCount);
245 *MonotonicCount = FilterMonotonicCount;
246 if (FilterMonotonicCount == 0) {
247 //
248 // If there is no new record to get exit now.
249 //
250 return EFI_NOT_FOUND;
251 }
252 }
253 }
254 }
255 //
256 // Return the record
257 //
258 *Record = GetNextDataRecord (&Private->DataListHead, ClassFilter, MonotonicCount);
259 if (*Record == NULL) {
260 return EFI_NOT_FOUND;
261 }
262
263 if (FilterDriver != NULL) {
264 //
265 // If we have a filter driver update the records that have been read.
266 // If MonotonicCount is zero No more reacords left.
267 //
268 if (*MonotonicCount == 0) {
269 //
270 // Save the current Record MonotonicCount.
271 //
272 FilterDriver->GetNextMonotonicCount = (*Record)->LogMonotonicCount;
273 } else {
274 //
275 // Point to next undread record
276 //
277 FilterDriver->GetNextMonotonicCount = *MonotonicCount;
278 }
279 }
280
281 return EFI_SUCCESS;
282 }
283
284 /**
285 This function registers the data hub filter driver that is represented
286 by FilterEvent. Only one instance of each FilterEvent can be registered.
287 After the FilterEvent is registered, it will be signaled so it can sync
288 with data records that have been recorded prior to the FilterEvent being
289 registered.
290
291 @param This - The EFI_DATA_HUB_PROTOCOL instance.
292 @param FilterEvent - The EFI_EVENT to signal whenever data that matches
293 FilterClass is logged in the system.
294 @param FilterTpl - The maximum EFI_TPL at which FilterEvent can be
295 signaled. It is strongly recommended that you use the
296 lowest EFI_TPL possible.
297 @param FilterClass - FilterEvent will be signaled whenever a bit in
298 EFI_DATA_RECORD_HEADER.DataRecordClass is also set in
299 FilterClass. If FilterClass is zero, no class-based
300 filtering will be performed.
301 @param FilterDataRecordGuid - FilterEvent will be signaled whenever FilterDataRecordGuid
302 matches EFI_DATA_RECORD_HEADER.DataRecordGuid. If
303 FilterDataRecordGuid is NULL, then no GUID-based filtering
304 will be performed.
305
306 @retval EFI_SUCCESS - The filter driver event was registered.
307 @retval EFI_ALREADY_STARTED - FilterEvent was previously registered and cannot be
308 registered again.
309 @retval EFI_OUT_OF_RESOURCES - The filter driver event was not registered due to lack of
310 system resources.
311
312 **/
313 EFI_STATUS
314 EFIAPI
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
321 )
322
323 {
324 DATA_HUB_INSTANCE *Private;
325 DATA_HUB_FILTER_DRIVER *FilterDriver;
326
327 Private = DATA_HUB_INSTANCE_FROM_THIS (This);
328
329 FilterDriver = (DATA_HUB_FILTER_DRIVER *) AllocateZeroPool (sizeof (DATA_HUB_FILTER_DRIVER));
330 if (FilterDriver == NULL) {
331 return EFI_OUT_OF_RESOURCES;
332 }
333 //
334 // Initialize filter driver info
335 //
336 FilterDriver->Signature = EFI_DATA_HUB_FILTER_DRIVER_SIGNATURE;
337 FilterDriver->Event = FilterEvent;
338 FilterDriver->Tpl = FilterTpl;
339 FilterDriver->GetNextMonotonicCount = 0;
340 if (FilterClass == 0) {
341 FilterDriver->ClassFilter = EFI_DATA_RECORD_CLASS_DEBUG |
342 EFI_DATA_RECORD_CLASS_ERROR |
343 EFI_DATA_RECORD_CLASS_DATA |
344 EFI_DATA_RECORD_CLASS_PROGRESS_CODE;
345 } else {
346 FilterDriver->ClassFilter = FilterClass;
347 }
348
349 if (FilterDataRecordGuid != NULL) {
350 CopyMem (&FilterDriver->FilterDataRecordGuid, FilterDataRecordGuid, sizeof (EFI_GUID));
351 }
352 //
353 // Search for duplicate entries
354 //
355 if (FindFilterDriverByEvent (&Private->FilterDriverListHead, FilterEvent) != NULL) {
356 FreePool (FilterDriver);
357 return EFI_ALREADY_STARTED;
358 }
359 //
360 // Make insertion an atomic operation with the lock.
361 //
362 EfiAcquireLock (&Private->DataLock);
363 InsertTailList (&Private->FilterDriverListHead, &FilterDriver->Link);
364 EfiReleaseLock (&Private->DataLock);
365
366 //
367 // Signal the Filter driver we just loaded so they will recieve all the
368 // previous history. If we did not signal here we would have to wait until
369 // the next data was logged to get the history. In a case where no next
370 // data was logged we would never get synced up.
371 //
372 gBS->SignalEvent (FilterEvent);
373
374 return EFI_SUCCESS;
375 }
376
377 /**
378 Remove a Filter Driver, so it no longer gets called when data
379 information is logged.
380
381 @param This - Protocol instance structure
382
383 @param FilterEvent - Event that represents a filter driver that is to be
384 Unregistered.
385
386 @retval EFI_SUCCESS - If FilterEvent was unregistered
387
388 @retval EFI_NOT_FOUND - If FilterEvent does not exist
389
390 **/
391 EFI_STATUS
392 EFIAPI
393 DataHubUnregisterFilterDriver (
394 IN EFI_DATA_HUB_PROTOCOL *This,
395 IN EFI_EVENT FilterEvent
396 )
397 {
398 DATA_HUB_INSTANCE *Private;
399 DATA_HUB_FILTER_DRIVER *FilterDriver;
400
401 Private = DATA_HUB_INSTANCE_FROM_THIS (This);
402
403 //
404 // Search for duplicate entries
405 //
406 FilterDriver = FindFilterDriverByEvent (
407 &Private->FilterDriverListHead,
408 FilterEvent
409 );
410 if (FilterDriver == NULL) {
411 return EFI_NOT_FOUND;
412 }
413 //
414 // Make removal an atomic operation with the lock
415 //
416 EfiAcquireLock (&Private->DataLock);
417 RemoveEntryList (&FilterDriver->Link);
418 EfiReleaseLock (&Private->DataLock);
419
420 return EFI_SUCCESS;
421 }
422
423 /**
424 Search the Head list for a EFI_DATA_HUB_FILTER_DRIVER member that
425 represents Event and return it.
426
427 @param Head - Head of dual linked list of EFI_DATA_HUB_FILTER_DRIVER
428 structures.
429
430 @param Event - Event to be search for in the Head list.
431
432 @retval EFI_DATA_HUB_FILTER_DRIVER - Returned if Event stored in the
433 Head doubly linked list.
434
435 @retval NULL - If Event is not in the list
436
437 **/
438 DATA_HUB_FILTER_DRIVER *
439 FindFilterDriverByEvent (
440 IN LIST_ENTRY *Head,
441 IN EFI_EVENT Event
442 )
443
444 {
445 DATA_HUB_FILTER_DRIVER *FilterEntry;
446 LIST_ENTRY *Link;
447
448 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
449 FilterEntry = FILTER_ENTRY_FROM_LINK (Link);
450 if (FilterEntry->Event == Event) {
451 return FilterEntry;
452 }
453 }
454
455 return NULL;
456 }
457
458 /**
459 Search the Head doubly linked list for the passed in MTC. Return the
460 matching element in Head and the MTC on the next entry.
461
462 @param Head - Head of Data Log linked list.
463
464 @param ClassFilter - Only match the MTC if it is in the same Class as the
465 ClassFilter.
466
467 @param PtrCurrentMTC - On IN contians MTC to search for. On OUT contians next
468 MTC in the data log list or zero if at end of the list.
469
470 @retval EFI_DATA_LOG_ENTRY - Return pointer to data log data from Head list.
471
472 @retval NULL - If no data record exists.
473
474 **/
475 EFI_DATA_RECORD_HEADER *
476 GetNextDataRecord (
477 IN LIST_ENTRY *Head,
478 IN UINT64 ClassFilter,
479 IN OUT UINT64 *PtrCurrentMTC
480 )
481
482 {
483 EFI_DATA_ENTRY *LogEntry;
484 LIST_ENTRY *Link;
485 BOOLEAN ReturnFirstEntry;
486 EFI_DATA_RECORD_HEADER *Record;
487 EFI_DATA_ENTRY *NextLogEntry;
488
489 //
490 // If MonotonicCount == 0 just return the first one
491 //
492 ReturnFirstEntry = (BOOLEAN) (*PtrCurrentMTC == 0);
493
494 Record = NULL;
495 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
496 LogEntry = DATA_ENTRY_FROM_LINK (Link);
497 if ((LogEntry->Record->DataRecordClass & ClassFilter) == 0) {
498 //
499 // Skip any entry that does not have the correct ClassFilter
500 //
501 continue;
502 }
503
504 if ((LogEntry->Record->LogMonotonicCount == *PtrCurrentMTC) || ReturnFirstEntry) {
505 //
506 // Return record to the user
507 //
508 Record = LogEntry->Record;
509
510 //
511 // Calculate the next MTC value. If there is no next entry set
512 // MTC to zero.
513 //
514 *PtrCurrentMTC = 0;
515 for (Link = Link->ForwardLink; Link != Head; Link = Link->ForwardLink) {
516 NextLogEntry = DATA_ENTRY_FROM_LINK (Link);
517 if ((NextLogEntry->Record->DataRecordClass & ClassFilter) != 0) {
518 //
519 // Return the MTC of the next thing to search for if found
520 //
521 *PtrCurrentMTC = NextLogEntry->Record->LogMonotonicCount;
522 break;
523 }
524 }
525 //
526 // Record found exit loop and return
527 //
528 break;
529 }
530 }
531
532 return Record;
533 }
534 //
535 // Module Global:
536 // Since this driver will only ever produce one instance of the Logging Hub
537 // protocol you are not required to dynamically allocate the PrivateData.
538 //
539 DATA_HUB_INSTANCE mPrivateData;
540
541 /**
542
543 Install Driver to produce Data Hub protocol.
544
545 @param ImageHandle Module's image handle
546 @param SystemTable Pointer of EFI_SYSTEM_TABLE
547
548
549 @retval EFI_SUCCESS - Logging Hub protocol installed
550
551 @retval Other - No protocol installed, unload driver.
552
553 **/
554 EFI_STATUS
555 EFIAPI
556 DataHubInstall (
557 IN EFI_HANDLE ImageHandle,
558 IN EFI_SYSTEM_TABLE *SystemTable
559 )
560 {
561 EFI_STATUS Status;
562 UINT32 HighMontonicCount;
563
564 mPrivateData.Signature = DATA_HUB_INSTANCE_SIGNATURE;
565 mPrivateData.DataHub.LogData = DataHubLogData;
566 mPrivateData.DataHub.GetNextRecord = DataHubGetNextRecord;
567 mPrivateData.DataHub.RegisterFilterDriver = DataHubRegisterFilterDriver;
568 mPrivateData.DataHub.UnregisterFilterDriver = DataHubUnregisterFilterDriver;
569
570 //
571 // Initialize Private Data in CORE_LOGGING_HUB_INSTANCE that is
572 // required by this protocol
573 //
574 InitializeListHead (&mPrivateData.DataListHead);
575 InitializeListHead (&mPrivateData.FilterDriverListHead);
576
577 EfiInitializeLock (&mPrivateData.DataLock, TPL_NOTIFY);
578
579 //
580 // Make sure we get a bigger MTC number on every boot!
581 //
582 Status = gRT->GetNextHighMonotonicCount (&HighMontonicCount);
583 if (EFI_ERROR (Status)) {
584 //
585 // if system service fails pick a sane value.
586 //
587 mPrivateData.GlobalMonotonicCount = 0;
588 } else {
589 mPrivateData.GlobalMonotonicCount = LShiftU64 ((UINT64) HighMontonicCount, 32);
590 }
591 //
592 // Make a new handle and install the protocol
593 //
594 mPrivateData.Handle = NULL;
595 Status = gBS->InstallProtocolInterface (
596 &mPrivateData.Handle,
597 &gEfiDataHubProtocolGuid,
598 EFI_NATIVE_INTERFACE,
599 &mPrivateData.DataHub
600 );
601 return Status;
602 }
603