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