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