• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Utility functions used by the Dp application.
3 
4   Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
5   (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
6   This program and the accompanying materials
7   are licensed and made available under the terms and conditions of the BSD License
8   which accompanies this distribution.  The full text of the license may be found at
9   http://opensource.org/licenses/bsd-license.php
10 
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 **/
14 
15 #include <Library/BaseLib.h>
16 #include <Library/BaseMemoryLib.h>
17 #include <Library/MemoryAllocationLib.h>
18 #include <Library/DebugLib.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 #include <Library/TimerLib.h>
21 #include <Library/PeCoffGetEntryPointLib.h>
22 #include <Library/PrintLib.h>
23 #include <Library/HiiLib.h>
24 #include <Library/PcdLib.h>
25 #include <Library/UefiLib.h>
26 #include <Library/DevicePathLib.h>
27 
28 #include <Pi/PiFirmwareFile.h>
29 #include <Library/DxeServicesLib.h>
30 
31 #include <Protocol/LoadedImage.h>
32 #include <Protocol/DriverBinding.h>
33 #include <Protocol/ComponentName2.h>
34 #include <Protocol/DevicePath.h>
35 
36 #include <Guid/Performance.h>
37 
38 #include "Dp.h"
39 #include "Literals.h"
40 #include "DpInternal.h"
41 
42 /**
43   Wrap original FreePool to check NULL pointer first.
44 
45   @param[in]    Buffer      The pointer to the buffer to free.
46 
47 **/
48 VOID
SafeFreePool(IN VOID * Buffer)49 SafeFreePool (
50   IN VOID   *Buffer
51   )
52 {
53   if (Buffer != NULL) {
54     FreePool (Buffer);
55   }
56 }
57 
58 /**
59   Calculate an event's duration in timer ticks.
60 
61   Given the count direction and the event's start and end timer values,
62   calculate the duration of the event in timer ticks.  Information for
63   the current measurement is pointed to by the parameter.
64 
65   If the measurement's start time is 1, it indicates that the developer
66   is indicating that the measurement began at the release of reset.
67   The start time is adjusted to the timer's starting count before performing
68   the elapsed time calculation.
69 
70   The calculated duration, in ticks, is the absolute difference between
71   the measurement's ending and starting counts.
72 
73   @param Measurement   Pointer to a MEASUREMENT_RECORD structure containing
74                        data for the current measurement.
75 
76   @return              The 64-bit duration of the event.
77 **/
78 UINT64
GetDuration(IN OUT MEASUREMENT_RECORD * Measurement)79 GetDuration (
80   IN OUT MEASUREMENT_RECORD   *Measurement
81   )
82 {
83   UINT64    Duration;
84   BOOLEAN   Error;
85 
86   if (Measurement->EndTimeStamp == 0) {
87     return 0;
88   }
89 
90   // PERF_START macros are called with a value of 1 to indicate
91   // the beginning of time.  So, adjust the start ticker value
92   // to the real beginning of time.
93   // Assumes no wraparound.  Even then, there is a very low probability
94   // of having a valid StartTicker value of 1.
95   if (Measurement->StartTimeStamp == 1) {
96     Measurement->StartTimeStamp = TimerInfo.StartCount;
97   }
98   if (TimerInfo.CountUp) {
99     Duration = Measurement->EndTimeStamp - Measurement->StartTimeStamp;
100     Error = (BOOLEAN)(Duration > Measurement->EndTimeStamp);
101   }
102   else {
103     Duration = Measurement->StartTimeStamp - Measurement->EndTimeStamp;
104     Error = (BOOLEAN)(Duration > Measurement->StartTimeStamp);
105   }
106 
107   if (Error) {
108     DEBUG ((EFI_D_ERROR, ALit_TimerLibError));
109     Duration = 0;
110   }
111   return Duration;
112 }
113 
114 /**
115   Determine whether the Measurement record is for an EFI Phase.
116 
117   The Token and Module members of the measurement record are checked.
118   Module must be empty and Token must be one of SEC, PEI, DXE, BDS, or SHELL.
119 
120   @param[in]  Measurement A pointer to the Measurement record to test.
121 
122   @retval     TRUE        The measurement record is for an EFI Phase.
123   @retval     FALSE       The measurement record is NOT for an EFI Phase.
124 **/
125 BOOLEAN
IsPhase(IN MEASUREMENT_RECORD * Measurement)126 IsPhase(
127   IN MEASUREMENT_RECORD        *Measurement
128   )
129 {
130   BOOLEAN   RetVal;
131 
132   RetVal = (BOOLEAN)( ( *Measurement->Module == '\0')                               &&
133             ((AsciiStrnCmp (Measurement->Token, ALit_SEC, PERF_TOKEN_LENGTH) == 0)    ||
134              (AsciiStrnCmp (Measurement->Token, ALit_PEI, PERF_TOKEN_LENGTH) == 0)    ||
135              (AsciiStrnCmp (Measurement->Token, ALit_DXE, PERF_TOKEN_LENGTH) == 0)    ||
136              (AsciiStrnCmp (Measurement->Token, ALit_BDS, PERF_TOKEN_LENGTH) == 0))
137             );
138   return RetVal;
139 }
140 
141 /**
142   Get the file name portion of the Pdb File Name.
143 
144   The portion of the Pdb File Name between the last backslash and
145   either a following period or the end of the string is converted
146   to Unicode and copied into UnicodeBuffer.  The name is truncated,
147   if necessary, to ensure that UnicodeBuffer is not overrun.
148 
149   @param[in]  PdbFileName     Pdb file name.
150   @param[out] UnicodeBuffer   The resultant Unicode File Name.
151 
152 **/
153 VOID
GetShortPdbFileName(IN CHAR8 * PdbFileName,OUT CHAR16 * UnicodeBuffer)154 GetShortPdbFileName (
155   IN  CHAR8     *PdbFileName,
156   OUT CHAR16    *UnicodeBuffer
157   )
158 {
159   UINTN IndexA;     // Current work location within an ASCII string.
160   UINTN IndexU;     // Current work location within a Unicode string.
161   UINTN StartIndex;
162   UINTN EndIndex;
163 
164   ZeroMem (UnicodeBuffer, (DP_GAUGE_STRING_LENGTH + 1) * sizeof (CHAR16));
165 
166   if (PdbFileName == NULL) {
167     StrCpyS (UnicodeBuffer, DP_GAUGE_STRING_LENGTH + 1, L" ");
168   } else {
169     StartIndex = 0;
170     for (EndIndex = 0; PdbFileName[EndIndex] != 0; EndIndex++)
171       ;
172     for (IndexA = 0; PdbFileName[IndexA] != 0; IndexA++) {
173       if (PdbFileName[IndexA] == '\\') {
174         StartIndex = IndexA + 1;
175       }
176 
177       if (PdbFileName[IndexA] == '.') {
178         EndIndex = IndexA;
179       }
180     }
181 
182     IndexU = 0;
183     for (IndexA = StartIndex; IndexA < EndIndex; IndexA++) {
184       UnicodeBuffer[IndexU] = (CHAR16) PdbFileName[IndexA];
185       IndexU++;
186       if (IndexU >= DP_GAUGE_STRING_LENGTH) {
187         UnicodeBuffer[DP_GAUGE_STRING_LENGTH] = 0;
188         break;
189       }
190     }
191   }
192 }
193 
194 /**
195   Get a human readable name for an image handle.
196   The following methods will be tried orderly:
197     1. Image PDB
198     2. ComponentName2 protocol
199     3. FFS UI section
200     4. Image GUID
201     5. Image DevicePath
202     6. Unknown Driver Name
203 
204   @param[in]    Handle
205 
206   @post   The resulting Unicode name string is stored in the
207           mGaugeString global array.
208 
209 **/
210 VOID
GetNameFromHandle(IN EFI_HANDLE Handle)211 GetNameFromHandle (
212   IN EFI_HANDLE   Handle
213   )
214 {
215   EFI_STATUS                  Status;
216   EFI_LOADED_IMAGE_PROTOCOL   *Image;
217   CHAR8                       *PdbFileName;
218   EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;
219   EFI_STRING                  StringPtr;
220   EFI_DEVICE_PATH_PROTOCOL    *LoadedImageDevicePath;
221   EFI_DEVICE_PATH_PROTOCOL    *DevicePath;
222   EFI_GUID                    *NameGuid;
223   CHAR16                      *NameString;
224   UINTN                       StringSize;
225   CHAR8                       *PlatformLanguage;
226   CHAR8                       *BestLanguage;
227   EFI_COMPONENT_NAME2_PROTOCOL      *ComponentName2;
228 
229   Image = NULL;
230   LoadedImageDevicePath = NULL;
231   DevicePath = NULL;
232   BestLanguage     = NULL;
233   PlatformLanguage = NULL;
234 
235   //
236   // Method 1: Get the name string from image PDB
237   //
238   Status = gBS->HandleProtocol (
239                   Handle,
240                   &gEfiLoadedImageProtocolGuid,
241                   (VOID **) &Image
242                   );
243 
244   if (EFI_ERROR (Status)) {
245     Status = gBS->OpenProtocol (
246                     Handle,
247                     &gEfiDriverBindingProtocolGuid,
248                     (VOID **) &DriverBinding,
249                     NULL,
250                     NULL,
251                     EFI_OPEN_PROTOCOL_GET_PROTOCOL
252                     );
253     if (!EFI_ERROR (Status)) {
254       Status = gBS->HandleProtocol (
255                       DriverBinding->ImageHandle,
256                       &gEfiLoadedImageProtocolGuid,
257                       (VOID **) &Image
258                       );
259     }
260   }
261 
262   if (!EFI_ERROR (Status)) {
263     PdbFileName = PeCoffLoaderGetPdbPointer (Image->ImageBase);
264 
265     if (PdbFileName != NULL) {
266       GetShortPdbFileName (PdbFileName, mGaugeString);
267       return;
268     }
269   }
270 
271   //
272   // Method 2: Get the name string from ComponentName2 protocol
273   //
274   Status = gBS->HandleProtocol (
275                   Handle,
276                   &gEfiComponentName2ProtocolGuid,
277                   (VOID **) &ComponentName2
278                   );
279   if (!EFI_ERROR (Status)) {
280     //
281     // Get the current platform language setting
282     //
283     GetEfiGlobalVariable2 (L"PlatformLang", (VOID**)&PlatformLanguage, NULL);
284 
285     BestLanguage = GetBestLanguage(
286                      ComponentName2->SupportedLanguages,
287                      FALSE,
288                      PlatformLanguage,
289                      ComponentName2->SupportedLanguages,
290                      NULL
291                      );
292 
293     SafeFreePool (PlatformLanguage);
294     Status = ComponentName2->GetDriverName (
295                                ComponentName2,
296                                BestLanguage,
297                                &StringPtr
298                                );
299     SafeFreePool (BestLanguage);
300     if (!EFI_ERROR (Status)) {
301       StrnCpyS (
302         mGaugeString,
303         DP_GAUGE_STRING_LENGTH + 1,
304         StringPtr,
305         DP_GAUGE_STRING_LENGTH
306         );
307       return;
308     }
309   }
310 
311   Status = gBS->HandleProtocol (
312                   Handle,
313                   &gEfiLoadedImageDevicePathProtocolGuid,
314                   (VOID **) &LoadedImageDevicePath
315                   );
316   if (!EFI_ERROR (Status) && (LoadedImageDevicePath != NULL)) {
317     DevicePath = LoadedImageDevicePath;
318   } else if (Image != NULL) {
319     DevicePath = Image->FilePath;
320   }
321 
322   if (DevicePath != NULL) {
323     //
324     // Try to get image GUID from image DevicePath
325     //
326     NameGuid = NULL;
327     while (!IsDevicePathEndType (DevicePath)) {
328       NameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) DevicePath);
329       if (NameGuid != NULL) {
330         break;
331       }
332       DevicePath = NextDevicePathNode (DevicePath);
333     }
334 
335     if (NameGuid != NULL) {
336       //
337       // Try to get the image's FFS UI section by image GUID
338       //
339       NameString = NULL;
340       StringSize = 0;
341       Status = GetSectionFromAnyFv (
342                 NameGuid,
343                 EFI_SECTION_USER_INTERFACE,
344                 0,
345                 (VOID **) &NameString,
346                 &StringSize
347                 );
348 
349       if (!EFI_ERROR (Status)) {
350         //
351         // Method 3. Get the name string from FFS UI section
352         //
353         StrnCpyS (
354           mGaugeString,
355           DP_GAUGE_STRING_LENGTH + 1,
356           NameString,
357           DP_GAUGE_STRING_LENGTH
358           );
359         FreePool (NameString);
360       } else {
361         //
362         // Method 4: Get the name string from image GUID
363         //
364         UnicodeSPrint (mGaugeString, sizeof (mGaugeString), L"%g", NameGuid);
365       }
366       return;
367     } else {
368       //
369       // Method 5: Get the name string from image DevicePath
370       //
371       NameString = ConvertDevicePathToText (DevicePath, TRUE, FALSE);
372       if (NameString != NULL) {
373         StrnCpyS (
374           mGaugeString,
375           DP_GAUGE_STRING_LENGTH + 1,
376           NameString,
377           DP_GAUGE_STRING_LENGTH
378           );
379         FreePool (NameString);
380         return;
381       }
382     }
383   }
384 
385   //
386   // Method 6: Unknown Driver Name
387   //
388   StringPtr = HiiGetString (gHiiHandle, STRING_TOKEN (STR_DP_ERROR_NAME), NULL);
389   ASSERT (StringPtr != NULL);
390   StrCpyS (mGaugeString, DP_GAUGE_STRING_LENGTH + 1, StringPtr);
391   FreePool (StringPtr);
392   return;
393 }
394 
395 /**
396   Calculate the Duration in microseconds.
397 
398   Duration is multiplied by 1000, instead of Frequency being divided by 1000 or
399   multiplying the result by 1000, in order to maintain precision.  Since Duration is
400   a 64-bit value, multiplying it by 1000 is unlikely to produce an overflow.
401 
402   The time is calculated as (Duration * 1000) / Timer_Frequency.
403 
404   @param[in]  Duration   The event duration in timer ticks.
405 
406   @return     A 64-bit value which is the Elapsed time in microseconds.
407 **/
408 UINT64
DurationInMicroSeconds(IN UINT64 Duration)409 DurationInMicroSeconds (
410   IN UINT64 Duration
411   )
412 {
413   UINT64 Temp;
414 
415   Temp = MultU64x32 (Duration, 1000);
416   return DivU64x32 (Temp, TimerInfo.Frequency);
417 }
418 
419 /**
420   Formatted Print using a Hii Token to reference the localized format string.
421 
422   @param[in]  Token   A HII token associated with a localized Unicode string.
423   @param[in]  ...     The variable argument list.
424 
425   @return             The number of characters converted by UnicodeVSPrint().
426 
427 **/
428 UINTN
429 EFIAPI
PrintToken(IN UINT16 Token,...)430 PrintToken (
431   IN UINT16           Token,
432   ...
433   )
434 {
435   VA_LIST           Marker;
436   EFI_STRING        StringPtr;
437   UINTN             Return;
438   UINTN             BufferSize;
439 
440   StringPtr = HiiGetString (gHiiHandle, Token, NULL);
441   ASSERT (StringPtr != NULL);
442 
443   VA_START (Marker, Token);
444 
445   BufferSize = (PcdGet32 (PcdUefiLibMaxPrintBufferSize) + 1) * sizeof (CHAR16);
446 
447   if (mPrintTokenBuffer == NULL) {
448     mPrintTokenBuffer = AllocatePool (BufferSize);
449     ASSERT (mPrintTokenBuffer != NULL);
450   }
451   SetMem( mPrintTokenBuffer, BufferSize, 0);
452 
453   Return = UnicodeVSPrint (mPrintTokenBuffer, BufferSize, StringPtr, Marker);
454   VA_END (Marker);
455 
456   if (Return > 0 && gST->ConOut != NULL) {
457     gST->ConOut->OutputString (gST->ConOut, mPrintTokenBuffer);
458   }
459   FreePool (StringPtr);
460   return Return;
461 }
462 
463 /**
464   Get index of Measurement Record's match in the CumData array.
465 
466   If the Measurement's Token value matches a Token in one of the CumData
467   records, the index of the matching record is returned.  The returned
468   index is a signed value so that negative values can indicate that
469   the Measurement didn't match any entry in the CumData array.
470 
471   @param[in]  Measurement A pointer to a Measurement Record to match against the CumData array.
472 
473   @retval     <0    Token is not in the CumData array.
474   @retval     >=0   Return value is the index into CumData where Token is found.
475 **/
476 INTN
GetCumulativeItem(IN MEASUREMENT_RECORD * Measurement)477 GetCumulativeItem(
478   IN MEASUREMENT_RECORD   *Measurement
479   )
480 {
481   INTN    Index;
482 
483   for( Index = 0; Index < (INTN)NumCum; ++Index) {
484     if (AsciiStrnCmp (Measurement->Token, CumData[Index].Name, PERF_TOKEN_LENGTH) == 0) {
485       return Index;  // Exit, we found a match
486     }
487   }
488   // If the for loop exits, Token was not found.
489   return -1;   // Indicate failure
490 }
491