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