1 /** @file
2   Utility functions used by the Dp application.
3 
4   Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.
5   (C) Copyright 2015-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 #include <Library/HandleParsingLib.h>
28 
29 #include <Pi/PiFirmwareFile.h>
30 #include <Library/DxeServicesLib.h>
31 
32 #include <Protocol/LoadedImage.h>
33 #include <Protocol/DriverBinding.h>
34 #include <Protocol/ComponentName2.h>
35 #include <Protocol/DevicePath.h>
36 
37 #include <Guid/Performance.h>
38 
39 #include "Dp.h"
40 #include "Literals.h"
41 #include "DpInternal.h"
42 
43 /**
44   Calculate an event's duration in timer ticks.
45 
46   Given the count direction and the event's start and end timer values,
47   calculate the duration of the event in timer ticks.  Information for
48   the current measurement is pointed to by the parameter.
49 
50   If the measurement's start time is 1, it indicates that the developer
51   is indicating that the measurement began at the release of reset.
52   The start time is adjusted to the timer's starting count before performing
53   the elapsed time calculation.
54 
55   The calculated duration, in ticks, is the absolute difference between
56   the measurement's ending and starting counts.
57 
58   @param Measurement   Pointer to a MEASUREMENT_RECORD structure containing
59                        data for the current measurement.
60 
61   @return              The 64-bit duration of the event.
62 **/
63 UINT64
GetDuration(IN OUT MEASUREMENT_RECORD * Measurement)64 GetDuration (
65   IN OUT MEASUREMENT_RECORD   *Measurement
66   )
67 {
68   UINT64    Duration;
69   BOOLEAN   Error;
70 
71   if (Measurement->EndTimeStamp == 0) {
72     return 0;
73   }
74 
75   // PERF_START macros are called with a value of 1 to indicate
76   // the beginning of time.  So, adjust the start ticker value
77   // to the real beginning of time.
78   // Assumes no wraparound.  Even then, there is a very low probability
79   // of having a valid StartTicker value of 1.
80   if (Measurement->StartTimeStamp == 1) {
81     Measurement->StartTimeStamp = TimerInfo.StartCount;
82   }
83   if (TimerInfo.CountUp) {
84     Duration = Measurement->EndTimeStamp - Measurement->StartTimeStamp;
85     Error = (BOOLEAN)(Duration > Measurement->EndTimeStamp);
86   }
87   else {
88     Duration = Measurement->StartTimeStamp - Measurement->EndTimeStamp;
89     Error = (BOOLEAN)(Duration > Measurement->StartTimeStamp);
90   }
91 
92   if (Error) {
93     DEBUG ((EFI_D_ERROR, ALit_TimerLibError));
94     Duration = 0;
95   }
96   return Duration;
97 }
98 
99 /**
100   Determine whether the Measurement record is for an EFI Phase.
101 
102   The Token and Module members of the measurement record are checked.
103   Module must be empty and Token must be one of SEC, PEI, DXE, BDS, or SHELL.
104 
105   @param[in]  Measurement A pointer to the Measurement record to test.
106 
107   @retval     TRUE        The measurement record is for an EFI Phase.
108   @retval     FALSE       The measurement record is NOT for an EFI Phase.
109 **/
110 BOOLEAN
IsPhase(IN MEASUREMENT_RECORD * Measurement)111 IsPhase(
112   IN MEASUREMENT_RECORD        *Measurement
113   )
114 {
115   BOOLEAN   RetVal;
116 
117   RetVal = (BOOLEAN)( ( *Measurement->Module == '\0')                               &&
118             ((AsciiStrnCmp (Measurement->Token, ALit_SEC, PERF_TOKEN_LENGTH) == 0)    ||
119              (AsciiStrnCmp (Measurement->Token, ALit_PEI, PERF_TOKEN_LENGTH) == 0)    ||
120              (AsciiStrnCmp (Measurement->Token, ALit_DXE, PERF_TOKEN_LENGTH) == 0)    ||
121              (AsciiStrnCmp (Measurement->Token, ALit_BDS, PERF_TOKEN_LENGTH) == 0))
122             );
123   return RetVal;
124 }
125 
126 /**
127   Get the file name portion of the Pdb File Name.
128 
129   The portion of the Pdb File Name between the last backslash and
130   either a following period or the end of the string is converted
131   to Unicode and copied into UnicodeBuffer.  The name is truncated,
132   if necessary, to ensure that UnicodeBuffer is not overrun.
133 
134   @param[in]  PdbFileName     Pdb file name.
135   @param[out] UnicodeBuffer   The resultant Unicode File Name.
136 
137 **/
138 VOID
DpGetShortPdbFileName(IN CHAR8 * PdbFileName,OUT CHAR16 * UnicodeBuffer)139 DpGetShortPdbFileName (
140   IN  CHAR8     *PdbFileName,
141   OUT CHAR16    *UnicodeBuffer
142   )
143 {
144   UINTN IndexA;     // Current work location within an ASCII string.
145   UINTN IndexU;     // Current work location within a Unicode string.
146   UINTN StartIndex;
147   UINTN EndIndex;
148 
149   ZeroMem (UnicodeBuffer, (DP_GAUGE_STRING_LENGTH + 1) * sizeof (CHAR16));
150 
151   if (PdbFileName == NULL) {
152     StrnCpyS (UnicodeBuffer, DP_GAUGE_STRING_LENGTH + 1, L" ", 1);
153   } else {
154     StartIndex = 0;
155     for (EndIndex = 0; PdbFileName[EndIndex] != 0; EndIndex++)
156       ;
157     for (IndexA = 0; PdbFileName[IndexA] != 0; IndexA++) {
158       if (PdbFileName[IndexA] == '\\') {
159         StartIndex = IndexA + 1;
160       }
161 
162       if (PdbFileName[IndexA] == '.') {
163         EndIndex = IndexA;
164       }
165     }
166 
167     IndexU = 0;
168     for (IndexA = StartIndex; IndexA < EndIndex; IndexA++) {
169       UnicodeBuffer[IndexU] = (CHAR16) PdbFileName[IndexA];
170       IndexU++;
171       if (IndexU >= DP_GAUGE_STRING_LENGTH) {
172         UnicodeBuffer[DP_GAUGE_STRING_LENGTH] = 0;
173         break;
174       }
175     }
176   }
177 }
178 
179 /**
180   Get a human readable name for an image handle.
181   The following methods will be tried orderly:
182     1. Image PDB
183     2. ComponentName2 protocol
184     3. FFS UI section
185     4. Image GUID
186     5. Image DevicePath
187     6. Unknown Driver Name
188 
189   @param[in]    Handle
190 
191   @post   The resulting Unicode name string is stored in the
192           mGaugeString global array.
193 
194 **/
195 VOID
DpGetNameFromHandle(IN EFI_HANDLE Handle)196 DpGetNameFromHandle (
197   IN EFI_HANDLE   Handle
198   )
199 {
200   EFI_STATUS                  Status;
201   EFI_LOADED_IMAGE_PROTOCOL   *Image;
202   CHAR8                       *PdbFileName;
203   EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;
204   EFI_STRING                  StringPtr;
205   EFI_DEVICE_PATH_PROTOCOL    *LoadedImageDevicePath;
206   EFI_DEVICE_PATH_PROTOCOL    *DevicePath;
207   EFI_GUID                    *NameGuid;
208   CHAR16                      *NameString;
209   UINTN                       StringSize;
210   CHAR8                       *PlatformLanguage;
211   EFI_COMPONENT_NAME2_PROTOCOL      *ComponentName2;
212 
213   Image = NULL;
214   LoadedImageDevicePath = NULL;
215   DevicePath = NULL;
216 
217   //
218   // Method 1: Get the name string from image PDB
219   //
220   Status = gBS->HandleProtocol (
221                   Handle,
222                   &gEfiLoadedImageProtocolGuid,
223                   (VOID **) &Image
224                   );
225 
226   if (EFI_ERROR (Status)) {
227     Status = gBS->OpenProtocol (
228                     Handle,
229                     &gEfiDriverBindingProtocolGuid,
230                     (VOID **) &DriverBinding,
231                     NULL,
232                     NULL,
233                     EFI_OPEN_PROTOCOL_GET_PROTOCOL
234                     );
235     if (!EFI_ERROR (Status)) {
236       Status = gBS->HandleProtocol (
237                       DriverBinding->ImageHandle,
238                       &gEfiLoadedImageProtocolGuid,
239                       (VOID **) &Image
240                       );
241     }
242   }
243 
244   if (!EFI_ERROR (Status)) {
245     PdbFileName = PeCoffLoaderGetPdbPointer (Image->ImageBase);
246 
247     if (PdbFileName != NULL) {
248       DpGetShortPdbFileName (PdbFileName, mGaugeString);
249       return;
250     }
251   }
252 
253   //
254   // Method 2: Get the name string from ComponentName2 protocol
255   //
256   Status = gBS->HandleProtocol (
257                   Handle,
258                   &gEfiComponentName2ProtocolGuid,
259                   (VOID **) &ComponentName2
260                   );
261   if (!EFI_ERROR (Status)) {
262     //
263     // Get the current platform language setting
264     //
265     PlatformLanguage = GetBestLanguageForDriver(ComponentName2->SupportedLanguages, NULL, FALSE);
266     Status = ComponentName2->GetDriverName (
267                                ComponentName2,
268                                PlatformLanguage != NULL ? PlatformLanguage : "en-US",
269                                &StringPtr
270                                );
271     if (!EFI_ERROR (Status)) {
272       SHELL_FREE_NON_NULL (PlatformLanguage);
273       StrnCpyS (mGaugeString, DP_GAUGE_STRING_LENGTH + 1, StringPtr, DP_GAUGE_STRING_LENGTH);
274       mGaugeString[DP_GAUGE_STRING_LENGTH] = 0;
275       return;
276     }
277   }
278 
279   Status = gBS->HandleProtocol (
280                   Handle,
281                   &gEfiLoadedImageDevicePathProtocolGuid,
282                   (VOID **) &LoadedImageDevicePath
283                   );
284   if (!EFI_ERROR (Status) && (LoadedImageDevicePath != NULL)) {
285     DevicePath = LoadedImageDevicePath;
286   } else if (Image != NULL) {
287     DevicePath = Image->FilePath;
288   }
289 
290   if (DevicePath != NULL) {
291     //
292     // Try to get image GUID from image DevicePath
293     //
294     NameGuid = NULL;
295     while (!IsDevicePathEndType (DevicePath)) {
296       NameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) DevicePath);
297       if (NameGuid != NULL) {
298         break;
299       }
300       DevicePath = NextDevicePathNode (DevicePath);
301     }
302 
303     if (NameGuid != NULL) {
304       //
305       // Try to get the image's FFS UI section by image GUID
306       //
307       NameString = NULL;
308       StringSize = 0;
309       Status = GetSectionFromAnyFv (
310                 NameGuid,
311                 EFI_SECTION_USER_INTERFACE,
312                 0,
313                 (VOID **) &NameString,
314                 &StringSize
315                 );
316 
317       if (!EFI_ERROR (Status)) {
318         //
319         // Method 3. Get the name string from FFS UI section
320         //
321         StrnCpyS (mGaugeString, DP_GAUGE_STRING_LENGTH + 1, NameString, DP_GAUGE_STRING_LENGTH);
322         mGaugeString[DP_GAUGE_STRING_LENGTH] = 0;
323         FreePool (NameString);
324       } else {
325         //
326         // Method 4: Get the name string from image GUID
327         //
328         UnicodeSPrint (mGaugeString, sizeof (mGaugeString), L"%g", NameGuid);
329       }
330       return;
331     } else {
332       //
333       // Method 5: Get the name string from image DevicePath
334       //
335       NameString = ConvertDevicePathToText (DevicePath, TRUE, FALSE);
336       if (NameString != NULL) {
337         StrnCpyS (mGaugeString, DP_GAUGE_STRING_LENGTH + 1, NameString, DP_GAUGE_STRING_LENGTH);
338         mGaugeString[DP_GAUGE_STRING_LENGTH] = 0;
339         FreePool (NameString);
340         return;
341       }
342     }
343   }
344 
345   //
346   // Method 6: Unknown Driver Name
347   //
348   StringPtr = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_DP_ERROR_NAME), NULL);
349   ASSERT (StringPtr != NULL);
350   StrnCpyS (mGaugeString, DP_GAUGE_STRING_LENGTH + 1, StringPtr, DP_GAUGE_STRING_LENGTH);
351   FreePool (StringPtr);
352 }
353 
354 /**
355   Calculate the Duration in microseconds.
356 
357   Duration is multiplied by 1000, instead of Frequency being divided by 1000 or
358   multiplying the result by 1000, in order to maintain precision.  Since Duration is
359   a 64-bit value, multiplying it by 1000 is unlikely to produce an overflow.
360 
361   The time is calculated as (Duration * 1000) / Timer_Frequency.
362 
363   @param[in]  Duration   The event duration in timer ticks.
364 
365   @return     A 64-bit value which is the Elapsed time in microseconds.
366 **/
367 UINT64
DurationInMicroSeconds(IN UINT64 Duration)368 DurationInMicroSeconds (
369   IN UINT64 Duration
370   )
371 {
372   UINT64 Temp;
373 
374   Temp = MultU64x32 (Duration, 1000);
375   return DivU64x32 (Temp, TimerInfo.Frequency);
376 }
377 
378 /**
379   Get index of Measurement Record's match in the CumData array.
380 
381   If the Measurement's Token value matches a Token in one of the CumData
382   records, the index of the matching record is returned.  The returned
383   index is a signed value so that negative values can indicate that
384   the Measurement didn't match any entry in the CumData array.
385 
386   @param[in]  Measurement A pointer to a Measurement Record to match against the CumData array.
387 
388   @retval     <0    Token is not in the CumData array.
389   @retval     >=0   Return value is the index into CumData where Token is found.
390 **/
391 INTN
GetCumulativeItem(IN MEASUREMENT_RECORD * Measurement)392 GetCumulativeItem(
393   IN MEASUREMENT_RECORD   *Measurement
394   )
395 {
396   INTN    Index;
397 
398   for( Index = 0; Index < (INTN)NumCum; ++Index) {
399     if (AsciiStrnCmp (Measurement->Token, CumData[Index].Name, PERF_TOKEN_LENGTH) == 0) {
400       return Index;  // Exit, we found a match
401     }
402   }
403   // If the for loop exits, Token was not found.
404   return -1;   // Indicate failure
405 }
406