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