]> git.proxmox.com Git - mirror_edk2.git/blob - EdkModulePkg/Universal/DataHub/DataHub/Dxe/DataHub.c
01e7a6ed69b7b7ebaf46f0fae5c8763e2fa6e9f0
[mirror_edk2.git] / EdkModulePkg / Universal / DataHub / DataHub / Dxe / DataHub.c
1 /*++
2
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
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 DataHub.c
15
16 Abstract:
17
18 This code produces the Data Hub protocol. It preloads the data hub
19 with status information copied in from PEI HOBs.
20
21 Only code that implements the Data Hub protocol should go in this file!
22
23 The Term MTC stands for MonoTonicCounter.
24
25 For more information please look at DataHub.doc
26
27 NOTE: For extra security of the log GetNextDataRecord () could return a copy
28 of the data record.
29 --*/
30
31 #include "DataHub.h"
32
33 CONST EFI_GUID gZeroGuid = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
34
35 //
36 // Worker functions private to this file
37 //
38 STATIC
39 DATA_HUB_FILTER_DRIVER *
40 FindFilterDriverByEvent (
41 IN LIST_ENTRY *Head,
42 IN EFI_EVENT Event
43 );
44
45 STATIC
46 EFI_DATA_RECORD_HEADER *
47 GetNextDataRecord (
48 IN LIST_ENTRY *Head,
49 IN UINT64 ClassFilter,
50 IN OUT UINT64 *PtrCurrentMTC
51 );
52
53 EFI_STATUS
54 EFIAPI
55 DataHubLogData (
56 IN EFI_DATA_HUB_PROTOCOL *This,
57 IN EFI_GUID *DataRecordGuid,
58 IN EFI_GUID *ProducerName,
59 IN UINT64 DataRecordClass,
60 IN VOID *RawData,
61 IN UINT32 RawDataSize
62 )
63 /*++
64
65 Routine Description:
66
67 Log data record into the data logging hub
68
69 Arguments:
70
71 This - Protocol instance structure
72
73 DataRecordGuid - GUID that defines record contents
74
75 ProducerName - GUID that defines the name of the producer of the data
76
77 DataRecordClass - Class that defines generic record type
78
79 RawData - Data Log record as defined by DataRecordGuid
80
81 RawDataSize - Size of Data Log data in bytes
82
83 Returns:
84
85 EFI_SUCCESS - If data was logged
86
87 EFI_OUT_OF_RESOURCES - If data was not logged due to lack of system
88 resources.
89 --*/
90 {
91 EFI_STATUS Status;
92 DATA_HUB_INSTANCE *Private;
93 EFI_DATA_ENTRY *LogEntry;
94 UINT32 TotalSize;
95 UINT32 RecordSize;
96 EFI_DATA_RECORD_HEADER *Record;
97 VOID *Raw;
98 DATA_HUB_FILTER_DRIVER *FilterEntry;
99 LIST_ENTRY *Link;
100 LIST_ENTRY *Head;
101
102 Private = DATA_HUB_INSTANCE_FROM_THIS (This);
103
104 //
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.
109 //
110 RecordSize = sizeof (EFI_DATA_RECORD_HEADER) + RawDataSize;
111 TotalSize = sizeof (EFI_DATA_ENTRY) + RecordSize;
112
113 //
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.
117 //
118 Status = EfiAcquireLockOrFail (&Private->DataLock);
119 if (EFI_ERROR (Status)) {
120 //
121 // Reentrancy detected so exit!
122 //
123 return Status;
124 }
125
126 Status = gBS->AllocatePool (EfiBootServicesData, TotalSize, (VOID **) &LogEntry);
127 if (EFI_ERROR (Status)) {
128 EfiReleaseLock (&Private->DataLock);
129 return EFI_OUT_OF_RESOURCES;
130 }
131
132 ZeroMem (LogEntry, TotalSize);
133
134 Record = (EFI_DATA_RECORD_HEADER *) (LogEntry + 1);
135 Raw = (VOID *) (Record + 1);
136
137 //
138 // Build Standard Log Header
139 //
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;
146
147 Record->LogMonotonicCount = Private->GlobalMonotonicCount++;
148
149 gRT->GetTime (&Record->LogTime, NULL);
150
151 //
152 // Insert log into the internal linked list.
153 //
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);
158
159 CopyMem (Raw, RawData, RawDataSize);
160
161 EfiReleaseLock (&Private->DataLock);
162
163 //
164 // Send Signal to all the filter drivers which are interested
165 // in the record's class and guid.
166 //
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);
174 }
175 }
176
177 return EFI_SUCCESS;
178 }
179
180 EFI_STATUS
181 EFIAPI
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
187 )
188 /*++
189
190 Routine Description:
191
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.
198
199 Arguments:
200
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
209 FilterDriverEvent.
210 Record - Returns a dynamically allocated memory buffer with a data
211 record that matches MonotonicCount.
212
213 Returns:
214
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.
221
222 --*/
223 {
224 DATA_HUB_INSTANCE *Private;
225 DATA_HUB_FILTER_DRIVER *FilterDriver;
226 UINT64 ClassFilter;
227 UINT64 FilterMonotonicCount;
228
229 Private = DATA_HUB_INSTANCE_FROM_THIS (This);
230
231 FilterDriver = NULL;
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;
237
238 if (FilterDriverEvent != NULL) {
239 //
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
242 // to get the data.
243 //
244 FilterDriver = FindFilterDriverByEvent (
245 &Private->FilterDriverListHead,
246 *FilterDriverEvent
247 );
248 if (FilterDriver == NULL) {
249 return EFI_INVALID_PARAMETER;
250 }
251 //
252 // Use the Class filter the event was created with.
253 //
254 ClassFilter = FilterDriver->ClassFilter;
255
256 if (*MonotonicCount == 0) {
257 //
258 // Use the MTC from the Filter Driver.
259 //
260 FilterMonotonicCount = FilterDriver->GetNextMonotonicCount;
261 if (FilterMonotonicCount != 0) {
262 //
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.
266 //
267 *Record = GetNextDataRecord (&Private->DataListHead, ClassFilter, &FilterMonotonicCount);
268 *MonotonicCount = FilterMonotonicCount;
269 if (FilterMonotonicCount == 0) {
270 //
271 // If there is no new record to get exit now.
272 //
273 return EFI_NOT_FOUND;
274 }
275 }
276 }
277 }
278 //
279 // Return the record
280 //
281 *Record = GetNextDataRecord (&Private->DataListHead, ClassFilter, MonotonicCount);
282 if (*Record == NULL) {
283 return EFI_NOT_FOUND;
284 }
285
286 if (FilterDriver != NULL) {
287 //
288 // If we have a filter driver update the records that have been read.
289 // If MonotonicCount is zero No more reacords left.
290 //
291 if (*MonotonicCount == 0) {
292 if (FilterMonotonicCount != 0) {
293 //
294 // Return the result of our extra GetNextDataRecord.
295 //
296 FilterDriver->GetNextMonotonicCount = FilterMonotonicCount;
297 }
298 } else {
299 //
300 // Point to next undread record
301 //
302 FilterDriver->GetNextMonotonicCount = *MonotonicCount;
303 }
304 }
305
306 return EFI_SUCCESS;
307 }
308
309 EFI_STATUS
310 EFIAPI
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
317 )
318 /*++
319
320 Routine Description:
321
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
326 registered.
327
328 Arguments:
329
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
343 will be performed.
344 Returns:
345
346 EFI_SUCCESS - The filter driver event was registered.
347 EFI_ALREADY_STARTED - FilterEvent was previously registered and cannot be
348 registered again.
349 EFI_OUT_OF_RESOURCES - The filter driver event was not registered due to lack of
350 system resources.
351
352 --*/
353 {
354 DATA_HUB_INSTANCE *Private;
355 DATA_HUB_FILTER_DRIVER *FilterDriver;
356
357 Private = DATA_HUB_INSTANCE_FROM_THIS (This);
358
359 FilterDriver = (DATA_HUB_FILTER_DRIVER *) AllocateZeroPool (sizeof (DATA_HUB_FILTER_DRIVER));
360 if (FilterDriver == NULL) {
361 return EFI_OUT_OF_RESOURCES;
362 }
363 //
364 // Initialize filter driver info
365 //
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;
375 } else {
376 FilterDriver->ClassFilter = FilterClass;
377 }
378
379 if (FilterDataRecordGuid != NULL) {
380 CopyMem (&FilterDriver->FilterDataRecordGuid, FilterDataRecordGuid, sizeof (EFI_GUID));
381 }
382 //
383 // Search for duplicate entries
384 //
385 if (FindFilterDriverByEvent (&Private->FilterDriverListHead, FilterEvent) != NULL) {
386 gBS->FreePool (FilterDriver);
387 return EFI_ALREADY_STARTED;
388 }
389 //
390 // Make insertion an atomic operation with the lock.
391 //
392 EfiAcquireLock (&Private->DataLock);
393 InsertTailList (&Private->FilterDriverListHead, &FilterDriver->Link);
394 EfiReleaseLock (&Private->DataLock);
395
396 //
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.
401 //
402 gBS->SignalEvent (FilterEvent);
403
404 return EFI_SUCCESS;
405 }
406
407 EFI_STATUS
408 EFIAPI
409 DataHubUnregisterFilterDriver (
410 IN EFI_DATA_HUB_PROTOCOL *This,
411 IN EFI_EVENT FilterEvent
412 )
413 /*++
414
415 Routine Description:
416
417 Remove a Filter Driver, so it no longer gets called when data
418 information is logged.
419
420 Arguments:
421
422 This - Protocol instance structure
423
424 FilterEvent - Event that represents a filter driver that is to be
425 Unregistered.
426
427 Returns:
428
429 EFI_SUCCESS - If FilterEvent was unregistered
430
431 EFI_NOT_FOUND - If FilterEvent does not exist
432
433 --*/
434 {
435 DATA_HUB_INSTANCE *Private;
436 DATA_HUB_FILTER_DRIVER *FilterDriver;
437
438 Private = DATA_HUB_INSTANCE_FROM_THIS (This);
439
440 //
441 // Search for duplicate entries
442 //
443 FilterDriver = FindFilterDriverByEvent (
444 &Private->FilterDriverListHead,
445 FilterEvent
446 );
447 if (FilterDriver == NULL) {
448 return EFI_NOT_FOUND;
449 }
450 //
451 // Make removal an atomic operation with the lock
452 //
453 EfiAcquireLock (&Private->DataLock);
454 RemoveEntryList (&FilterDriver->Link);
455 EfiReleaseLock (&Private->DataLock);
456
457 return EFI_SUCCESS;
458 }
459 //
460 // STATIC Worker fucntions follow
461 //
462 STATIC
463 DATA_HUB_FILTER_DRIVER *
464 FindFilterDriverByEvent (
465 IN LIST_ENTRY *Head,
466 IN EFI_EVENT Event
467 )
468 /*++
469
470 Routine Description:
471 Search the Head list for a EFI_DATA_HUB_FILTER_DRIVER member that
472 represents Event and return it.
473
474 Arguments:
475
476 Head - Head of dual linked list of EFI_DATA_HUB_FILTER_DRIVER
477 structures.
478
479 Event - Event to be search for in the Head list.
480
481 Returns:
482
483 EFI_DATA_HUB_FILTER_DRIVER - Returned if Event stored in the
484 Head doubly linked list.
485
486 NULL - If Event is not in the list
487
488 --*/
489 {
490 DATA_HUB_FILTER_DRIVER *FilterEntry;
491 LIST_ENTRY *Link;
492
493 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
494 FilterEntry = FILTER_ENTRY_FROM_LINK (Link);
495 if (FilterEntry->Event == Event) {
496 return FilterEntry;
497 }
498 }
499
500 return NULL;
501 }
502
503 STATIC
504 EFI_DATA_RECORD_HEADER *
505 GetNextDataRecord (
506 IN LIST_ENTRY *Head,
507 IN UINT64 ClassFilter,
508 IN OUT UINT64 *PtrCurrentMTC
509 )
510 /*++
511
512 Routine Description:
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.
515
516 Arguments:
517
518 Head - Head of Data Log linked list.
519
520 ClassFilter - Only match the MTC if it is in the same Class as the
521 ClassFilter.
522
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.
525
526 Returns:
527
528 EFI_DATA_LOG_ENTRY - Return pointer to data log data from Head list.
529
530 NULL - If no data record exists.
531
532 --*/
533 {
534 EFI_DATA_ENTRY *LogEntry;
535 LIST_ENTRY *Link;
536 BOOLEAN ReturnFirstEntry;
537 EFI_DATA_RECORD_HEADER *Record;
538 EFI_DATA_ENTRY *NextLogEntry;
539
540 //
541 // If MonotonicCount == 0 just return the first one
542 //
543 ReturnFirstEntry = (BOOLEAN) (*PtrCurrentMTC == 0);
544
545 Record = NULL;
546 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
547 LogEntry = DATA_ENTRY_FROM_LINK (Link);
548 if ((LogEntry->Record->DataRecordClass & ClassFilter) == 0) {
549 //
550 // Skip any entry that does not have the correct ClassFilter
551 //
552 continue;
553 }
554
555 if ((LogEntry->Record->LogMonotonicCount == *PtrCurrentMTC) || ReturnFirstEntry) {
556 //
557 // Return record to the user
558 //
559 Record = LogEntry->Record;
560
561 //
562 // Calculate the next MTC value. If there is no next entry set
563 // MTC to zero.
564 //
565 *PtrCurrentMTC = 0;
566 for (Link = Link->ForwardLink; Link != Head; Link = Link->ForwardLink) {
567 NextLogEntry = DATA_ENTRY_FROM_LINK (Link);
568 if ((NextLogEntry->Record->DataRecordClass & ClassFilter) != 0) {
569 //
570 // Return the MTC of the next thing to search for if found
571 //
572 *PtrCurrentMTC = NextLogEntry->Record->LogMonotonicCount;
573 break;
574 }
575 }
576 //
577 // Record found exit loop and return
578 //
579 break;
580 }
581 }
582
583 return Record;
584 }
585 //
586 // Module Global:
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.
589 //
590 DATA_HUB_INSTANCE mPrivateData;
591
592 EFI_STATUS
593 EFIAPI
594 DataHubInstall (
595 IN EFI_HANDLE ImageHandle,
596 IN EFI_SYSTEM_TABLE *SystemTable
597 )
598 /*++
599
600 Routine Description:
601 Install Driver to produce Data Hub protocol.
602
603 Arguments:
604 (Standard EFI Image entry - EFI_IMAGE_ENTRY_POINT)
605
606 Returns:
607
608 EFI_SUCCESS - Logging Hub protocol installed
609
610 Other - No protocol installed, unload driver.
611
612 --*/
613 {
614 EFI_STATUS Status;
615 UINT32 HighMontonicCount;
616
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;
622
623 //
624 // Initialize Private Data in CORE_LOGGING_HUB_INSTANCE that is
625 // required by this protocol
626 //
627 InitializeListHead (&mPrivateData.DataListHead);
628 InitializeListHead (&mPrivateData.FilterDriverListHead);
629
630 EfiInitializeLock (&mPrivateData.DataLock, EFI_TPL_NOTIFY);
631
632 //
633 // Make sure we get a bigger MTC number on every boot!
634 //
635 Status = gRT->GetNextHighMonotonicCount (&HighMontonicCount);
636 if (EFI_ERROR (Status)) {
637 //
638 // if system service fails pick a sane value.
639 //
640 mPrivateData.GlobalMonotonicCount = 0;
641 } else {
642 mPrivateData.GlobalMonotonicCount = LShiftU64 ((UINT64) HighMontonicCount, 32);
643 }
644 //
645 // Make a new handle and install the protocol
646 //
647 mPrivateData.Handle = NULL;
648 Status = gBS->InstallProtocolInterface (
649 &mPrivateData.Handle,
650 &gEfiDataHubProtocolGuid,
651 EFI_NATIVE_INTERFACE,
652 &mPrivateData.DataHub
653 );
654 return Status;
655 }