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 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 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 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 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 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 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 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 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