MdeModulePkg: Fix potential integer overflow issue
[mirror_edk2.git] / MdeModulePkg / Library / SmmCorePerformanceLib / SmmCorePerformanceLib.c
1 /** @file
2 Performance library instance used by SMM Core.
3
4 This library provides the performance measurement interfaces and initializes performance
5 logging for the SMM phase.
6 It initializes SMM phase performance logging by publishing the SMM Performance and PerformanceEx Protocol,
7 which is consumed by SmmPerformanceLib to logging performance data in SMM phase.
8
9 This library is mainly used by SMM Core to start performance logging to ensure that
10 SMM Performance and PerformanceEx Protocol are installed at the very beginning of SMM phase.
11
12 Caution: This module requires additional review when modified.
13 This driver will have external input - performance data and communicate buffer in SMM mode.
14 This external input must be validated carefully to avoid security issue like
15 buffer overflow, integer overflow.
16
17 SmmPerformanceHandlerEx(), SmmPerformanceHandler() will receive untrusted input and do basic validation.
18
19 Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
20 This program and the accompanying materials
21 are licensed and made available under the terms and conditions of the BSD License
22 which accompanies this distribution. The full text of the license may be found at
23 http://opensource.org/licenses/bsd-license.php
24
25 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
26 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
27
28 **/
29
30
31 #include "SmmCorePerformanceLibInternal.h"
32
33 //
34 // The data structure to hold global performance data.
35 //
36 GAUGE_DATA_HEADER *mGaugeData;
37
38 //
39 // The current maximum number of logging entries. If current number of
40 // entries exceeds this value, it will re-allocate a larger array and
41 // migration the old data to the larger array.
42 //
43 UINT32 mMaxGaugeRecords;
44
45 //
46 // The handle to install Performance Protocol instance.
47 //
48 EFI_HANDLE mHandle = NULL;
49
50 BOOLEAN mPerformanceMeasurementEnabled;
51
52 SPIN_LOCK mSmmPerfLock;
53
54 //
55 // Interfaces for SMM Performance Protocol.
56 //
57 PERFORMANCE_PROTOCOL mPerformanceInterface = {
58 StartGauge,
59 EndGauge,
60 GetGauge
61 };
62
63 //
64 // Interfaces for SMM PerformanceEx Protocol.
65 //
66 PERFORMANCE_EX_PROTOCOL mPerformanceExInterface = {
67 StartGaugeEx,
68 EndGaugeEx,
69 GetGaugeEx
70 };
71
72 /**
73 Searches in the gauge array with keyword Handle, Token, Module and Identfier.
74
75 This internal function searches for the gauge entry in the gauge array.
76 If there is an entry that exactly matches the given keywords
77 and its end time stamp is zero, then the index of that gauge entry is returned;
78 otherwise, the the number of gauge entries in the array is returned.
79
80 @param Handle Pointer to environment specific context used
81 to identify the component being measured.
82 @param Token Pointer to a Null-terminated ASCII string
83 that identifies the component being measured.
84 @param Module Pointer to a Null-terminated ASCII string
85 that identifies the module being measured.
86 @param Identifier 32-bit identifier.
87
88 @retval The index of gauge entry in the array.
89
90 **/
91 UINT32
92 SmmSearchForGaugeEntry (
93 IN CONST VOID *Handle, OPTIONAL
94 IN CONST CHAR8 *Token, OPTIONAL
95 IN CONST CHAR8 *Module, OPTIONAL
96 IN CONST UINT32 Identifier
97 )
98 {
99 UINT32 Index;
100 UINT32 Index2;
101 UINT32 NumberOfEntries;
102 GAUGE_DATA_ENTRY_EX *GaugeEntryExArray;
103
104 if (Token == NULL) {
105 Token = "";
106 }
107 if (Module == NULL) {
108 Module = "";
109 }
110
111 NumberOfEntries = mGaugeData->NumberOfEntries;
112 GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1);
113
114 Index2 = 0;
115
116 for (Index = 0; Index < NumberOfEntries; Index++) {
117 Index2 = NumberOfEntries - 1 - Index;
118 if (GaugeEntryExArray[Index2].EndTimeStamp == 0 &&
119 (GaugeEntryExArray[Index2].Handle == (EFI_PHYSICAL_ADDRESS) (UINTN) Handle) &&
120 AsciiStrnCmp (GaugeEntryExArray[Index2].Token, Token, SMM_PERFORMANCE_STRING_LENGTH) == 0 &&
121 AsciiStrnCmp (GaugeEntryExArray[Index2].Module, Module, SMM_PERFORMANCE_STRING_LENGTH) == 0 &&
122 (GaugeEntryExArray[Index2].Identifier == Identifier)) {
123 Index = Index2;
124 break;
125 }
126 }
127
128 return Index;
129 }
130
131 /**
132 Adds a record at the end of the performance measurement log
133 that records the start time of a performance measurement.
134
135 Adds a record to the end of the performance measurement log
136 that contains the Handle, Token, Module and Identifier.
137 The end time of the new record must be set to zero.
138 If TimeStamp is not zero, then TimeStamp is used to fill in the start time in the record.
139 If TimeStamp is zero, the start time in the record is filled in with the value
140 read from the current time stamp.
141
142 @param Handle Pointer to environment specific context used
143 to identify the component being measured.
144 @param Token Pointer to a Null-terminated ASCII string
145 that identifies the component being measured.
146 @param Module Pointer to a Null-terminated ASCII string
147 that identifies the module being measured.
148 @param TimeStamp 64-bit time stamp.
149 @param Identifier 32-bit identifier. If the value is 0, the created record
150 is same as the one created by StartGauge of PERFORMANCE_PROTOCOL.
151
152 @retval EFI_SUCCESS The data was read correctly from the device.
153 @retval EFI_OUT_OF_RESOURCES There are not enough resources to record the measurement.
154
155 **/
156 EFI_STATUS
157 EFIAPI
158 StartGaugeEx (
159 IN CONST VOID *Handle, OPTIONAL
160 IN CONST CHAR8 *Token, OPTIONAL
161 IN CONST CHAR8 *Module, OPTIONAL
162 IN UINT64 TimeStamp,
163 IN UINT32 Identifier
164 )
165 {
166 GAUGE_DATA_ENTRY_EX *GaugeEntryExArray;
167 UINTN GaugeDataSize;
168 GAUGE_DATA_HEADER *NewGaugeData;
169 UINTN OldGaugeDataSize;
170 GAUGE_DATA_HEADER *OldGaugeData;
171 UINT32 Index;
172
173 AcquireSpinLock (&mSmmPerfLock);
174
175 Index = mGaugeData->NumberOfEntries;
176 if (Index >= mMaxGaugeRecords) {
177 //
178 // Try to enlarge the scale of gauge array.
179 //
180 OldGaugeData = mGaugeData;
181 OldGaugeDataSize = sizeof (GAUGE_DATA_HEADER) + sizeof (GAUGE_DATA_ENTRY_EX) * mMaxGaugeRecords;
182
183 GaugeDataSize = sizeof (GAUGE_DATA_HEADER) + sizeof (GAUGE_DATA_ENTRY_EX) * mMaxGaugeRecords * 2;
184
185 NewGaugeData = AllocateZeroPool (GaugeDataSize);
186 if (NewGaugeData == NULL) {
187 ReleaseSpinLock (&mSmmPerfLock);
188 return EFI_OUT_OF_RESOURCES;
189 }
190
191 mGaugeData = NewGaugeData;
192 mMaxGaugeRecords *= 2;
193
194 //
195 // Initialize new data array and migrate old data one.
196 //
197 mGaugeData = CopyMem (mGaugeData, OldGaugeData, OldGaugeDataSize);
198
199 FreePool (OldGaugeData);
200 }
201
202 GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1);
203 GaugeEntryExArray[Index].Handle = (EFI_PHYSICAL_ADDRESS) (UINTN) Handle;
204
205 if (Token != NULL) {
206 AsciiStrnCpyS (GaugeEntryExArray[Index].Token, SMM_PERFORMANCE_STRING_SIZE, Token, SMM_PERFORMANCE_STRING_LENGTH);
207 }
208 if (Module != NULL) {
209 AsciiStrnCpyS (GaugeEntryExArray[Index].Module, SMM_PERFORMANCE_STRING_SIZE, Module, SMM_PERFORMANCE_STRING_LENGTH);
210 }
211
212 GaugeEntryExArray[Index].EndTimeStamp = 0;
213 GaugeEntryExArray[Index].Identifier = Identifier;
214
215 if (TimeStamp == 0) {
216 TimeStamp = GetPerformanceCounter ();
217 }
218 GaugeEntryExArray[Index].StartTimeStamp = TimeStamp;
219
220 mGaugeData->NumberOfEntries++;
221
222 ReleaseSpinLock (&mSmmPerfLock);
223
224 return EFI_SUCCESS;
225 }
226
227 /**
228 Searches the performance measurement log from the beginning of the log
229 for the first matching record that contains a zero end time and fills in a valid end time.
230
231 Searches the performance measurement log from the beginning of the log
232 for the first record that matches Handle, Token, Module and Identifier and has an end time value of zero.
233 If the record can not be found then return EFI_NOT_FOUND.
234 If the record is found and TimeStamp is not zero,
235 then the end time in the record is filled in with the value specified by TimeStamp.
236 If the record is found and TimeStamp is zero, then the end time in the matching record
237 is filled in with the current time stamp value.
238
239 @param Handle Pointer to environment specific context used
240 to identify the component being measured.
241 @param Token Pointer to a Null-terminated ASCII string
242 that identifies the component being measured.
243 @param Module Pointer to a Null-terminated ASCII string
244 that identifies the module being measured.
245 @param TimeStamp 64-bit time stamp.
246 @param Identifier 32-bit identifier. If the value is 0, the found record
247 is same as the one found by EndGauge of PERFORMANCE_PROTOCOL.
248
249 @retval EFI_SUCCESS The end of the measurement was recorded.
250 @retval EFI_NOT_FOUND The specified measurement record could not be found.
251
252 **/
253 EFI_STATUS
254 EFIAPI
255 EndGaugeEx (
256 IN CONST VOID *Handle, OPTIONAL
257 IN CONST CHAR8 *Token, OPTIONAL
258 IN CONST CHAR8 *Module, OPTIONAL
259 IN UINT64 TimeStamp,
260 IN UINT32 Identifier
261 )
262 {
263 GAUGE_DATA_ENTRY_EX *GaugeEntryExArray;
264 UINT32 Index;
265
266 AcquireSpinLock (&mSmmPerfLock);
267
268 if (TimeStamp == 0) {
269 TimeStamp = GetPerformanceCounter ();
270 }
271
272 Index = SmmSearchForGaugeEntry (Handle, Token, Module, Identifier);
273 if (Index >= mGaugeData->NumberOfEntries) {
274 ReleaseSpinLock (&mSmmPerfLock);
275 return EFI_NOT_FOUND;
276 }
277 GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1);
278 GaugeEntryExArray[Index].EndTimeStamp = TimeStamp;
279
280 ReleaseSpinLock (&mSmmPerfLock);
281
282 return EFI_SUCCESS;
283 }
284
285 /**
286 Retrieves a previously logged performance measurement.
287 It can also retrieve the log created by StartGauge and EndGauge of PERFORMANCE_PROTOCOL,
288 and then assign the Identifier with 0.
289
290 Retrieves the performance log entry from the performance log specified by LogEntryKey.
291 If it stands for a valid entry, then EFI_SUCCESS is returned and
292 GaugeDataEntryEx stores the pointer to that entry.
293
294 @param LogEntryKey The key for the previous performance measurement log entry.
295 If 0, then the first performance measurement log entry is retrieved.
296 @param GaugeDataEntryEx The indirect pointer to the extended gauge data entry specified by LogEntryKey
297 if the retrieval is successful.
298
299 @retval EFI_SUCCESS The GuageDataEntryEx is successfully found based on LogEntryKey.
300 @retval EFI_NOT_FOUND The LogEntryKey is the last entry (equals to the total entry number).
301 @retval EFI_INVALIDE_PARAMETER The LogEntryKey is not a valid entry (greater than the total entry number).
302 @retval EFI_INVALIDE_PARAMETER GaugeDataEntryEx is NULL.
303
304 **/
305 EFI_STATUS
306 EFIAPI
307 GetGaugeEx (
308 IN UINTN LogEntryKey,
309 OUT GAUGE_DATA_ENTRY_EX **GaugeDataEntryEx
310 )
311 {
312 UINTN NumberOfEntries;
313 GAUGE_DATA_ENTRY_EX *GaugeEntryExArray;
314
315 NumberOfEntries = (UINTN) (mGaugeData->NumberOfEntries);
316 if (LogEntryKey > NumberOfEntries) {
317 return EFI_INVALID_PARAMETER;
318 }
319 if (LogEntryKey == NumberOfEntries) {
320 return EFI_NOT_FOUND;
321 }
322
323 GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1);
324
325 if (GaugeDataEntryEx == NULL) {
326 return EFI_INVALID_PARAMETER;
327 }
328 *GaugeDataEntryEx = &GaugeEntryExArray[LogEntryKey];
329
330 return EFI_SUCCESS;
331 }
332
333 /**
334 Adds a record at the end of the performance measurement log
335 that records the start time of a performance measurement.
336
337 Adds a record to the end of the performance measurement log
338 that contains the Handle, Token, and Module.
339 The end time of the new record must be set to zero.
340 If TimeStamp is not zero, then TimeStamp is used to fill in the start time in the record.
341 If TimeStamp is zero, the start time in the record is filled in with the value
342 read from the current time stamp.
343
344 @param Handle Pointer to environment specific context used
345 to identify the component being measured.
346 @param Token Pointer to a Null-terminated ASCII string
347 that identifies the component being measured.
348 @param Module Pointer to a Null-terminated ASCII string
349 that identifies the module being measured.
350 @param TimeStamp 64-bit time stamp.
351
352 @retval EFI_SUCCESS The data was read correctly from the device.
353 @retval EFI_OUT_OF_RESOURCES There are not enough resources to record the measurement.
354
355 **/
356 EFI_STATUS
357 EFIAPI
358 StartGauge (
359 IN CONST VOID *Handle, OPTIONAL
360 IN CONST CHAR8 *Token, OPTIONAL
361 IN CONST CHAR8 *Module, OPTIONAL
362 IN UINT64 TimeStamp
363 )
364 {
365 return StartGaugeEx (Handle, Token, Module, TimeStamp, 0);
366 }
367
368 /**
369 Searches the performance measurement log from the beginning of the log
370 for the first matching record that contains a zero end time and fills in a valid end time.
371
372 Searches the performance measurement log from the beginning of the log
373 for the first record that matches Handle, Token, and Module and has an end time value of zero.
374 If the record can not be found then return EFI_NOT_FOUND.
375 If the record is found and TimeStamp is not zero,
376 then the end time in the record is filled in with the value specified by TimeStamp.
377 If the record is found and TimeStamp is zero, then the end time in the matching record
378 is filled in with the current time stamp value.
379
380 @param Handle Pointer to environment specific context used
381 to identify the component being measured.
382 @param Token Pointer to a Null-terminated ASCII string
383 that identifies the component being measured.
384 @param Module Pointer to a Null-terminated ASCII string
385 that identifies the module being measured.
386 @param TimeStamp 64-bit time stamp.
387
388 @retval EFI_SUCCESS The end of the measurement was recorded.
389 @retval EFI_NOT_FOUND The specified measurement record could not be found.
390
391 **/
392 EFI_STATUS
393 EFIAPI
394 EndGauge (
395 IN CONST VOID *Handle, OPTIONAL
396 IN CONST CHAR8 *Token, OPTIONAL
397 IN CONST CHAR8 *Module, OPTIONAL
398 IN UINT64 TimeStamp
399 )
400 {
401 return EndGaugeEx (Handle, Token, Module, TimeStamp, 0);
402 }
403
404 /**
405 Retrieves a previously logged performance measurement.
406 It can also retrieve the log created by StartGaugeEx and EndGaugeEx of PERFORMANCE_EX_PROTOCOL,
407 and then eliminate the Identifier.
408
409 Retrieves the performance log entry from the performance log specified by LogEntryKey.
410 If it stands for a valid entry, then EFI_SUCCESS is returned and
411 GaugeDataEntry stores the pointer to that entry.
412
413 @param LogEntryKey The key for the previous performance measurement log entry.
414 If 0, then the first performance measurement log entry is retrieved.
415 @param GaugeDataEntry The indirect pointer to the gauge data entry specified by LogEntryKey
416 if the retrieval is successful.
417
418 @retval EFI_SUCCESS The GuageDataEntry is successfully found based on LogEntryKey.
419 @retval EFI_NOT_FOUND The LogEntryKey is the last entry (equals to the total entry number).
420 @retval EFI_INVALIDE_PARAMETER The LogEntryKey is not a valid entry (greater than the total entry number).
421 @retval EFI_INVALIDE_PARAMETER GaugeDataEntry is NULL.
422
423 **/
424 EFI_STATUS
425 EFIAPI
426 GetGauge (
427 IN UINTN LogEntryKey,
428 OUT GAUGE_DATA_ENTRY **GaugeDataEntry
429 )
430 {
431 EFI_STATUS Status;
432 GAUGE_DATA_ENTRY_EX *GaugeEntryEx;
433
434 GaugeEntryEx = NULL;
435
436 Status = GetGaugeEx (LogEntryKey, &GaugeEntryEx);
437 if (EFI_ERROR (Status)) {
438 return Status;
439 }
440
441 if (GaugeDataEntry == NULL) {
442 return EFI_INVALID_PARAMETER;
443 }
444
445 *GaugeDataEntry = (GAUGE_DATA_ENTRY *) GaugeEntryEx;
446
447 return EFI_SUCCESS;
448 }
449
450 /**
451 Communication service SMI Handler entry.
452
453 This SMI handler provides services for the performance wrapper driver.
454
455 Caution: This function may receive untrusted input.
456 Communicate buffer and buffer size are external input, so this function will do basic validation.
457
458 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
459 @param[in] RegisterContext Points to an optional handler context which was specified when the
460 handler was registered.
461 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
462 be conveyed from a non-SMM environment into an SMM environment.
463 @param[in, out] CommBufferSize The size of the CommBuffer.
464
465 @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
466 should still be called.
467 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
468 still be called.
469 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
470 be called.
471 @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
472 **/
473 EFI_STATUS
474 EFIAPI
475 SmmPerformanceHandlerEx (
476 IN EFI_HANDLE DispatchHandle,
477 IN CONST VOID *RegisterContext,
478 IN OUT VOID *CommBuffer,
479 IN OUT UINTN *CommBufferSize
480 )
481 {
482 EFI_STATUS Status;
483 SMM_PERF_COMMUNICATE_EX *SmmPerfCommData;
484 GAUGE_DATA_ENTRY_EX *GaugeEntryExArray;
485 UINT64 DataSize;
486 UINTN Index;
487 GAUGE_DATA_ENTRY_EX *GaugeDataEx;
488 UINTN NumberOfEntries;
489 UINTN LogEntryKey;
490 UINTN TempCommBufferSize;
491
492 GaugeEntryExArray = NULL;
493
494 //
495 // If input is invalid, stop processing this SMI
496 //
497 if (CommBuffer == NULL || CommBufferSize == NULL) {
498 return EFI_SUCCESS;
499 }
500
501 TempCommBufferSize = *CommBufferSize;
502
503 if(TempCommBufferSize < sizeof (SMM_PERF_COMMUNICATE_EX)) {
504 return EFI_SUCCESS;
505 }
506
507 if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) {
508 DEBUG ((EFI_D_ERROR, "SmmPerformanceHandlerEx: SMM communcation data buffer in SMRAM or overflow!\n"));
509 return EFI_SUCCESS;
510 }
511
512 SmmPerfCommData = (SMM_PERF_COMMUNICATE_EX *)CommBuffer;
513
514 switch (SmmPerfCommData->Function) {
515 case SMM_PERF_FUNCTION_GET_GAUGE_ENTRY_NUMBER :
516 SmmPerfCommData->NumberOfEntries = mGaugeData->NumberOfEntries;
517 Status = EFI_SUCCESS;
518 break;
519
520 case SMM_PERF_FUNCTION_GET_GAUGE_DATA :
521 GaugeDataEx = SmmPerfCommData->GaugeDataEx;
522 NumberOfEntries = SmmPerfCommData->NumberOfEntries;
523 LogEntryKey = SmmPerfCommData->LogEntryKey;
524 if (GaugeDataEx == NULL || NumberOfEntries == 0 || LogEntryKey > mGaugeData->NumberOfEntries ||
525 NumberOfEntries > mGaugeData->NumberOfEntries || LogEntryKey > (mGaugeData->NumberOfEntries - NumberOfEntries)) {
526 Status = EFI_INVALID_PARAMETER;
527 break;
528 }
529
530 //
531 // Sanity check
532 //
533 DataSize = MultU64x32 (NumberOfEntries, sizeof(GAUGE_DATA_ENTRY_EX));
534 if (!SmmIsBufferOutsideSmmValid ((UINTN) GaugeDataEx, DataSize)) {
535 DEBUG ((EFI_D_ERROR, "SmmPerformanceHandlerEx: SMM Performance Data buffer in SMRAM or overflow!\n"));
536 Status = EFI_ACCESS_DENIED;
537 break;
538 }
539
540 GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1);
541
542 for (Index = 0; Index < NumberOfEntries; Index++) {
543 CopyMem (
544 (UINT8 *) &GaugeDataEx[Index],
545 (UINT8 *) &GaugeEntryExArray[LogEntryKey++],
546 sizeof (GAUGE_DATA_ENTRY_EX)
547 );
548 }
549 Status = EFI_SUCCESS;
550 break;
551
552 default:
553 Status = EFI_UNSUPPORTED;
554 }
555
556
557 SmmPerfCommData->ReturnStatus = Status;
558
559 return EFI_SUCCESS;
560 }
561
562 /**
563 Communication service SMI Handler entry.
564
565 This SMI handler provides services for the performance wrapper driver.
566
567 Caution: This function may receive untrusted input.
568 Communicate buffer and buffer size are external input, so this function will do basic validation.
569
570 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
571 @param[in] RegisterContext Points to an optional handler context which was specified when the
572 handler was registered.
573 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
574 be conveyed from a non-SMM environment into an SMM environment.
575 @param[in, out] CommBufferSize The size of the CommBuffer.
576
577 @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
578 should still be called.
579 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
580 still be called.
581 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
582 be called.
583 @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
584 **/
585 EFI_STATUS
586 EFIAPI
587 SmmPerformanceHandler (
588 IN EFI_HANDLE DispatchHandle,
589 IN CONST VOID *RegisterContext,
590 IN OUT VOID *CommBuffer,
591 IN OUT UINTN *CommBufferSize
592 )
593 {
594 EFI_STATUS Status;
595 SMM_PERF_COMMUNICATE *SmmPerfCommData;
596 GAUGE_DATA_ENTRY_EX *GaugeEntryExArray;
597 UINT64 DataSize;
598 UINTN Index;
599 GAUGE_DATA_ENTRY *GaugeData;
600 UINTN NumberOfEntries;
601 UINTN LogEntryKey;
602 UINTN TempCommBufferSize;
603
604 GaugeEntryExArray = NULL;
605
606 //
607 // If input is invalid, stop processing this SMI
608 //
609 if (CommBuffer == NULL || CommBufferSize == NULL) {
610 return EFI_SUCCESS;
611 }
612
613 TempCommBufferSize = *CommBufferSize;
614
615 if(TempCommBufferSize < sizeof (SMM_PERF_COMMUNICATE)) {
616 return EFI_SUCCESS;
617 }
618
619 if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) {
620 DEBUG ((EFI_D_ERROR, "SmmPerformanceHandler: SMM communcation data buffer in SMRAM or overflow!\n"));
621 return EFI_SUCCESS;
622 }
623
624 SmmPerfCommData = (SMM_PERF_COMMUNICATE *)CommBuffer;
625
626 switch (SmmPerfCommData->Function) {
627 case SMM_PERF_FUNCTION_GET_GAUGE_ENTRY_NUMBER :
628 SmmPerfCommData->NumberOfEntries = mGaugeData->NumberOfEntries;
629 Status = EFI_SUCCESS;
630 break;
631
632 case SMM_PERF_FUNCTION_GET_GAUGE_DATA :
633 GaugeData = SmmPerfCommData->GaugeData;
634 NumberOfEntries = SmmPerfCommData->NumberOfEntries;
635 LogEntryKey = SmmPerfCommData->LogEntryKey;
636 if (GaugeData == NULL || NumberOfEntries == 0 || LogEntryKey > mGaugeData->NumberOfEntries ||
637 NumberOfEntries > mGaugeData->NumberOfEntries || LogEntryKey > (mGaugeData->NumberOfEntries - NumberOfEntries)) {
638 Status = EFI_INVALID_PARAMETER;
639 break;
640 }
641
642 //
643 // Sanity check
644 //
645 DataSize = MultU64x32 (NumberOfEntries, sizeof(GAUGE_DATA_ENTRY));
646 if (!SmmIsBufferOutsideSmmValid ((UINTN) GaugeData, DataSize)) {
647 DEBUG ((EFI_D_ERROR, "SmmPerformanceHandler: SMM Performance Data buffer in SMRAM or overflow!\n"));
648 Status = EFI_ACCESS_DENIED;
649 break;
650 }
651
652 GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1);
653
654 for (Index = 0; Index < NumberOfEntries; Index++) {
655 CopyMem (
656 (UINT8 *) &GaugeData[Index],
657 (UINT8 *) &GaugeEntryExArray[LogEntryKey++],
658 sizeof (GAUGE_DATA_ENTRY)
659 );
660 }
661 Status = EFI_SUCCESS;
662 break;
663
664 default:
665 Status = EFI_UNSUPPORTED;
666 }
667
668
669 SmmPerfCommData->ReturnStatus = Status;
670
671 return EFI_SUCCESS;
672 }
673
674 /**
675 SmmBase2 protocol notify callback function, when SMST and SMM memory service get initialized
676 this function is callbacked to initialize the Smm Performance Lib
677
678 @param Event The event of notify protocol.
679 @param Context Notify event context.
680
681 **/
682 VOID
683 EFIAPI
684 InitializeSmmCorePerformanceLib (
685 IN EFI_EVENT Event,
686 IN VOID *Context
687 )
688 {
689 EFI_STATUS Status;
690 EFI_HANDLE Handle;
691
692 //
693 // Initialize spin lock
694 //
695 InitializeSpinLock (&mSmmPerfLock);
696
697 mMaxGaugeRecords = INIT_SMM_GAUGE_DATA_ENTRIES;
698
699 mGaugeData = AllocateZeroPool (sizeof (GAUGE_DATA_HEADER) + (sizeof (GAUGE_DATA_ENTRY_EX) * mMaxGaugeRecords));
700 ASSERT (mGaugeData != NULL);
701
702 //
703 // Install the protocol interfaces.
704 //
705 Status = gSmst->SmmInstallProtocolInterface (
706 &mHandle,
707 &gSmmPerformanceProtocolGuid,
708 EFI_NATIVE_INTERFACE,
709 &mPerformanceInterface
710 );
711 ASSERT_EFI_ERROR (Status);
712
713 Status = gSmst->SmmInstallProtocolInterface (
714 &mHandle,
715 &gSmmPerformanceExProtocolGuid,
716 EFI_NATIVE_INTERFACE,
717 &mPerformanceExInterface
718 );
719 ASSERT_EFI_ERROR (Status);
720
721 ///
722 /// Register SMM Performance SMI handler
723 ///
724 Handle = NULL;
725 Status = gSmst->SmiHandlerRegister (SmmPerformanceHandler, &gSmmPerformanceProtocolGuid, &Handle);
726 ASSERT_EFI_ERROR (Status);
727 Status = gSmst->SmiHandlerRegister (SmmPerformanceHandlerEx, &gSmmPerformanceExProtocolGuid, &Handle);
728 ASSERT_EFI_ERROR (Status);
729 }
730
731 /**
732 The constructor function initializes the Performance Measurement Enable flag and
733 registers SmmBase2 protocol notify callback.
734 It will ASSERT() if one of these operations fails and it will always return EFI_SUCCESS.
735
736 @param ImageHandle The firmware allocated handle for the EFI image.
737 @param SystemTable A pointer to the EFI System Table.
738
739 @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.
740
741 **/
742 EFI_STATUS
743 EFIAPI
744 SmmCorePerformanceLibConstructor (
745 IN EFI_HANDLE ImageHandle,
746 IN EFI_SYSTEM_TABLE *SystemTable
747 )
748 {
749 EFI_STATUS Status;
750 EFI_EVENT Event;
751 VOID *Registration;
752
753 mPerformanceMeasurementEnabled = (BOOLEAN) ((PcdGet8(PcdPerformanceLibraryPropertyMask) & PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED) != 0);
754 if (!mPerformanceMeasurementEnabled) {
755 //
756 // Do not initialize performance infrastructure if not required.
757 //
758 return EFI_SUCCESS;
759 }
760
761 //
762 // Create the events to do the library init.
763 //
764 Status = gBS->CreateEvent (
765 EVT_NOTIFY_SIGNAL,
766 TPL_CALLBACK,
767 InitializeSmmCorePerformanceLib,
768 NULL,
769 &Event
770 );
771 ASSERT_EFI_ERROR (Status);
772
773 //
774 // Register for protocol notifications on this event
775 //
776 Status = gBS->RegisterProtocolNotify (
777 &gEfiSmmBase2ProtocolGuid,
778 Event,
779 &Registration
780 );
781
782 ASSERT_EFI_ERROR (Status);
783
784 return EFI_SUCCESS;
785 }
786
787 /**
788 Adds a record at the end of the performance measurement log
789 that records the start time of a performance measurement.
790
791 Adds a record to the end of the performance measurement log
792 that contains the Handle, Token, Module and Identifier.
793 The end time of the new record must be set to zero.
794 If TimeStamp is not zero, then TimeStamp is used to fill in the start time in the record.
795 If TimeStamp is zero, the start time in the record is filled in with the value
796 read from the current time stamp.
797
798 @param Handle Pointer to environment specific context used
799 to identify the component being measured.
800 @param Token Pointer to a Null-terminated ASCII string
801 that identifies the component being measured.
802 @param Module Pointer to a Null-terminated ASCII string
803 that identifies the module being measured.
804 @param TimeStamp 64-bit time stamp.
805 @param Identifier 32-bit identifier. If the value is 0, the created record
806 is same as the one created by StartPerformanceMeasurement.
807
808 @retval RETURN_SUCCESS The start of the measurement was recorded.
809 @retval RETURN_OUT_OF_RESOURCES There are not enough resources to record the measurement.
810
811 **/
812 RETURN_STATUS
813 EFIAPI
814 StartPerformanceMeasurementEx (
815 IN CONST VOID *Handle, OPTIONAL
816 IN CONST CHAR8 *Token, OPTIONAL
817 IN CONST CHAR8 *Module, OPTIONAL
818 IN UINT64 TimeStamp,
819 IN UINT32 Identifier
820 )
821 {
822 return (RETURN_STATUS) StartGaugeEx (Handle, Token, Module, TimeStamp, Identifier);
823 }
824
825 /**
826 Searches the performance measurement log from the beginning of the log
827 for the first matching record that contains a zero end time and fills in a valid end time.
828
829 Searches the performance measurement log from the beginning of the log
830 for the first record that matches Handle, Token, Module and Identifier and has an end time value of zero.
831 If the record can not be found then return RETURN_NOT_FOUND.
832 If the record is found and TimeStamp is not zero,
833 then the end time in the record is filled in with the value specified by TimeStamp.
834 If the record is found and TimeStamp is zero, then the end time in the matching record
835 is filled in with the current time stamp value.
836
837 @param Handle Pointer to environment specific context used
838 to identify the component being measured.
839 @param Token Pointer to a Null-terminated ASCII string
840 that identifies the component being measured.
841 @param Module Pointer to a Null-terminated ASCII string
842 that identifies the module being measured.
843 @param TimeStamp 64-bit time stamp.
844 @param Identifier 32-bit identifier. If the value is 0, the found record
845 is same as the one found by EndPerformanceMeasurement.
846
847 @retval RETURN_SUCCESS The end of the measurement was recorded.
848 @retval RETURN_NOT_FOUND The specified measurement record could not be found.
849
850 **/
851 RETURN_STATUS
852 EFIAPI
853 EndPerformanceMeasurementEx (
854 IN CONST VOID *Handle, OPTIONAL
855 IN CONST CHAR8 *Token, OPTIONAL
856 IN CONST CHAR8 *Module, OPTIONAL
857 IN UINT64 TimeStamp,
858 IN UINT32 Identifier
859 )
860 {
861 return (RETURN_STATUS) EndGaugeEx (Handle, Token, Module, TimeStamp, Identifier);
862 }
863
864 /**
865 Attempts to retrieve a performance measurement log entry from the performance measurement log.
866 It can also retrieve the log created by StartPerformanceMeasurement and EndPerformanceMeasurement,
867 and then assign the Identifier with 0.
868
869 Attempts to retrieve the performance log entry specified by LogEntryKey. If LogEntryKey is
870 zero on entry, then an attempt is made to retrieve the first entry from the performance log,
871 and the key for the second entry in the log is returned. If the performance log is empty,
872 then no entry is retrieved and zero is returned. If LogEntryKey is not zero, then the performance
873 log entry associated with LogEntryKey is retrieved, and the key for the next entry in the log is
874 returned. If LogEntryKey is the key for the last entry in the log, then the last log entry is
875 retrieved and an implementation specific non-zero key value that specifies the end of the performance
876 log is returned. If LogEntryKey is equal this implementation specific non-zero key value, then no entry
877 is retrieved and zero is returned. In the cases where a performance log entry can be returned,
878 the log entry is returned in Handle, Token, Module, StartTimeStamp, EndTimeStamp and Identifier.
879 If LogEntryKey is not a valid log entry key for the performance measurement log, then ASSERT().
880 If Handle is NULL, then ASSERT().
881 If Token is NULL, then ASSERT().
882 If Module is NULL, then ASSERT().
883 If StartTimeStamp is NULL, then ASSERT().
884 If EndTimeStamp is NULL, then ASSERT().
885 If Identifier is NULL, then ASSERT().
886
887 @param LogEntryKey On entry, the key of the performance measurement log entry to retrieve.
888 0, then the first performance measurement log entry is retrieved.
889 On exit, the key of the next performance log entry.
890 @param Handle Pointer to environment specific context used to identify the component
891 being measured.
892 @param Token Pointer to a Null-terminated ASCII string that identifies the component
893 being measured.
894 @param Module Pointer to a Null-terminated ASCII string that identifies the module
895 being measured.
896 @param StartTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement
897 was started.
898 @param EndTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement
899 was ended.
900 @param Identifier Pointer to the 32-bit identifier that was recorded.
901
902 @return The key for the next performance log entry (in general case).
903
904 **/
905 UINTN
906 EFIAPI
907 GetPerformanceMeasurementEx (
908 IN UINTN LogEntryKey,
909 OUT CONST VOID **Handle,
910 OUT CONST CHAR8 **Token,
911 OUT CONST CHAR8 **Module,
912 OUT UINT64 *StartTimeStamp,
913 OUT UINT64 *EndTimeStamp,
914 OUT UINT32 *Identifier
915 )
916 {
917 EFI_STATUS Status;
918 GAUGE_DATA_ENTRY_EX *GaugeData;
919
920 GaugeData = NULL;
921
922 ASSERT (Handle != NULL);
923 ASSERT (Token != NULL);
924 ASSERT (Module != NULL);
925 ASSERT (StartTimeStamp != NULL);
926 ASSERT (EndTimeStamp != NULL);
927 ASSERT (Identifier != NULL);
928
929 Status = GetGaugeEx (LogEntryKey++, &GaugeData);
930
931 //
932 // Make sure that LogEntryKey is a valid log entry key,
933 //
934 ASSERT (Status != EFI_INVALID_PARAMETER);
935
936 if (EFI_ERROR (Status)) {
937 //
938 // The LogEntryKey is the last entry (equals to the total entry number).
939 //
940 return 0;
941 }
942
943 ASSERT (GaugeData != NULL);
944
945 *Handle = (VOID *) (UINTN) GaugeData->Handle;
946 *Token = GaugeData->Token;
947 *Module = GaugeData->Module;
948 *StartTimeStamp = GaugeData->StartTimeStamp;
949 *EndTimeStamp = GaugeData->EndTimeStamp;
950 *Identifier = GaugeData->Identifier;
951
952 return LogEntryKey;
953 }
954
955 /**
956 Adds a record at the end of the performance measurement log
957 that records the start time of a performance measurement.
958
959 Adds a record to the end of the performance measurement log
960 that contains the Handle, Token, and Module.
961 The end time of the new record must be set to zero.
962 If TimeStamp is not zero, then TimeStamp is used to fill in the start time in the record.
963 If TimeStamp is zero, the start time in the record is filled in with the value
964 read from the current time stamp.
965
966 @param Handle Pointer to environment specific context used
967 to identify the component being measured.
968 @param Token Pointer to a Null-terminated ASCII string
969 that identifies the component being measured.
970 @param Module Pointer to a Null-terminated ASCII string
971 that identifies the module being measured.
972 @param TimeStamp 64-bit time stamp.
973
974 @retval RETURN_SUCCESS The start of the measurement was recorded.
975 @retval RETURN_OUT_OF_RESOURCES There are not enough resources to record the measurement.
976
977 **/
978 RETURN_STATUS
979 EFIAPI
980 StartPerformanceMeasurement (
981 IN CONST VOID *Handle, OPTIONAL
982 IN CONST CHAR8 *Token, OPTIONAL
983 IN CONST CHAR8 *Module, OPTIONAL
984 IN UINT64 TimeStamp
985 )
986 {
987 return StartPerformanceMeasurementEx (Handle, Token, Module, TimeStamp, 0);
988 }
989
990 /**
991 Searches the performance measurement log from the beginning of the log
992 for the first matching record that contains a zero end time and fills in a valid end time.
993
994 Searches the performance measurement log from the beginning of the log
995 for the first record that matches Handle, Token, and Module and has an end time value of zero.
996 If the record can not be found then return RETURN_NOT_FOUND.
997 If the record is found and TimeStamp is not zero,
998 then the end time in the record is filled in with the value specified by TimeStamp.
999 If the record is found and TimeStamp is zero, then the end time in the matching record
1000 is filled in with the current time stamp value.
1001
1002 @param Handle Pointer to environment specific context used
1003 to identify the component being measured.
1004 @param Token Pointer to a Null-terminated ASCII string
1005 that identifies the component being measured.
1006 @param Module Pointer to a Null-terminated ASCII string
1007 that identifies the module being measured.
1008 @param TimeStamp 64-bit time stamp.
1009
1010 @retval RETURN_SUCCESS The end of the measurement was recorded.
1011 @retval RETURN_NOT_FOUND The specified measurement record could not be found.
1012
1013 **/
1014 RETURN_STATUS
1015 EFIAPI
1016 EndPerformanceMeasurement (
1017 IN CONST VOID *Handle, OPTIONAL
1018 IN CONST CHAR8 *Token, OPTIONAL
1019 IN CONST CHAR8 *Module, OPTIONAL
1020 IN UINT64 TimeStamp
1021 )
1022 {
1023 return EndPerformanceMeasurementEx (Handle, Token, Module, TimeStamp, 0);
1024 }
1025
1026 /**
1027 Attempts to retrieve a performance measurement log entry from the performance measurement log.
1028 It can also retrieve the log created by StartPerformanceMeasurementEx and EndPerformanceMeasurementEx,
1029 and then eliminate the Identifier.
1030
1031 Attempts to retrieve the performance log entry specified by LogEntryKey. If LogEntryKey is
1032 zero on entry, then an attempt is made to retrieve the first entry from the performance log,
1033 and the key for the second entry in the log is returned. If the performance log is empty,
1034 then no entry is retrieved and zero is returned. If LogEntryKey is not zero, then the performance
1035 log entry associated with LogEntryKey is retrieved, and the key for the next entry in the log is
1036 returned. If LogEntryKey is the key for the last entry in the log, then the last log entry is
1037 retrieved and an implementation specific non-zero key value that specifies the end of the performance
1038 log is returned. If LogEntryKey is equal this implementation specific non-zero key value, then no entry
1039 is retrieved and zero is returned. In the cases where a performance log entry can be returned,
1040 the log entry is returned in Handle, Token, Module, StartTimeStamp, and EndTimeStamp.
1041 If LogEntryKey is not a valid log entry key for the performance measurement log, then ASSERT().
1042 If Handle is NULL, then ASSERT().
1043 If Token is NULL, then ASSERT().
1044 If Module is NULL, then ASSERT().
1045 If StartTimeStamp is NULL, then ASSERT().
1046 If EndTimeStamp is NULL, then ASSERT().
1047
1048 @param LogEntryKey On entry, the key of the performance measurement log entry to retrieve.
1049 0, then the first performance measurement log entry is retrieved.
1050 On exit, the key of the next performance log entry.
1051 @param Handle Pointer to environment specific context used to identify the component
1052 being measured.
1053 @param Token Pointer to a Null-terminated ASCII string that identifies the component
1054 being measured.
1055 @param Module Pointer to a Null-terminated ASCII string that identifies the module
1056 being measured.
1057 @param StartTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement
1058 was started.
1059 @param EndTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement
1060 was ended.
1061
1062 @return The key for the next performance log entry (in general case).
1063
1064 **/
1065 UINTN
1066 EFIAPI
1067 GetPerformanceMeasurement (
1068 IN UINTN LogEntryKey,
1069 OUT CONST VOID **Handle,
1070 OUT CONST CHAR8 **Token,
1071 OUT CONST CHAR8 **Module,
1072 OUT UINT64 *StartTimeStamp,
1073 OUT UINT64 *EndTimeStamp
1074 )
1075 {
1076 UINT32 Identifier;
1077 return GetPerformanceMeasurementEx (LogEntryKey, Handle, Token, Module, StartTimeStamp, EndTimeStamp, &Identifier);
1078 }
1079
1080 /**
1081 Returns TRUE if the performance measurement macros are enabled.
1082
1083 This function returns TRUE if the PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED bit of
1084 PcdPerformanceLibraryPropertyMask is set. Otherwise FALSE is returned.
1085
1086 @retval TRUE The PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED bit of
1087 PcdPerformanceLibraryPropertyMask is set.
1088 @retval FALSE The PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED bit of
1089 PcdPerformanceLibraryPropertyMask is clear.
1090
1091 **/
1092 BOOLEAN
1093 EFIAPI
1094 PerformanceMeasurementEnabled (
1095 VOID
1096 )
1097 {
1098 return mPerformanceMeasurementEnabled;
1099 }