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