/** @file GenericBdsLib ;****************************************************************************** ;* Copyright (c) 2012 - 2018, Insyde Software Corp. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ /** This file include the file which can help to get the system performance, all the function will only include if the performance switch is set. Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include "InternalBdsLib.h" // // The data structure for performance data in ACPI memory. // #define PERFORMANCE_SIGNATURE SIGNATURE_32 ('P', 'e', 'r', 'f') #define PERF_TOKEN_SIZE 28 #define PERF_TOKEN_LENGTH (PERF_TOKEN_SIZE - 1) #define PERF_PEI_ENTRY_MAX_NUM 50 #define PERF_DATA_MAX_LENGTH 0x4000 typedef struct { CHAR8 Token[PERF_TOKEN_SIZE]; UINT32 Duration; } PERF_DATA; typedef struct { UINT64 BootToOs; UINT64 S3Resume; UINT32 S3EntryNum; PERF_DATA S3Entry[PERF_PEI_ENTRY_MAX_NUM]; UINT64 CpuFreq; UINT64 BDSRaw; UINT32 Count; UINT32 Signiture; } PERF_HEADER; STATIC PERF_HEADER mPerfHeader; STATIC PERF_DATA mPerfData; STATIC EFI_PHYSICAL_ADDRESS mAcpiLowMemoryBase = 0x0FFFFFFFFULL; STATIC CHAR8 *mSkipToken[] = {"LoadImage:", "StartImage:", "DB:Support:", "DB:Start:", "\0"}; /** Concatenates a formatted unicode string to allocated pool. The caller must free the resulting buffer. @param Str Tracks the allocated pool, size in use, and amount of pool allocated. @param Fmt The format string @param ... The data will be printed. @return Allocated buffer with the formatted string printed in it. The caller must free the allocated buffer. The buffer allocation is not packed. **/ CHAR16 * EFIAPI CatPrint ( IN OUT POOL_PRINT *Str, IN CHAR16 *Fmt, ... ) ; /** Convert Device Path to a Unicode string for printing. @param Str The buffer holding the output string. This buffer contains the length of the string and the maixmum length reserved for the string buffer. @param DevPath The device path. **/ CHAR16 * FvFilePath ( IN OUT POOL_PRINT *Str, IN VOID *DevPath ) { MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFilePath; FvFilePath = DevPath; return CatPrint (Str, L"%g", &FvFilePath->FvFileName); } /** Convert Unicode characters to Ascii characters. @param Dest Destination string @param Src Source string @return None **/ STATIC VOID ConvertChar16ToChar8 ( IN CHAR8 *Dest, IN CHAR16 *Src ) { UINT8 i = 0; while (*Src) { *Dest++ = (UINT8) (*Src++); i++; if(i > (PERF_TOKEN_SIZE - 2)) break; } *Dest = 0; } /** Write PEI file guid name. @param Buffer Input buffer. @param Guid File guid name. @return None **/ VOID WritePeiDriverGuid ( IN CHAR8 *Buffer, IN EFI_GUID *Guid ) { UINTN Index; ConvertChar16ToChar8 (Buffer, L"PEIM"); for (Index = 0; Index < 16; ++Index) { *(((UINT8*) Buffer) + (Index + 5)) = *(((UINT8*) Guid) + Index); } } /** Write DXE Driver token. @param Dest Input buffer. @param Token Driver token. @param Handle Driver handle. @return None **/ VOID WriteDxeDriverToken ( IN CHAR8 *Dest, IN CHAR8 *Token, IN UINT16 *Handle ) { UINTN Index; CHAR8 *Ptr; // // Reserved 8 bytes for driver description // if (AsciiStrLen (Dest) > 20) { Ptr = Dest + 20; ZeroMem (Ptr, 8); Ptr++; } else { Ptr = Dest + AsciiStrLen (Dest) + 1; } // // Write driver handle // for (Index = 0; Index < sizeof (UINT16); ++Index) { *Ptr++ = *(((UINT8*) Handle) + Index); } // // Write driver description: // DriverBinding:Start - dbst // DriverBinding:Support - dbsu // StartImage - simg // LoadImage - limg // if (AsciiStrCmp (Token, "DB:Start:") == 0) { ConvertChar16ToChar8 (Ptr + 1, L"dbst"); } else if (AsciiStrCmp (Token, "DB:Support:") == 0) { ConvertChar16ToChar8 (Ptr + 1, L"dbsu"); } else if (AsciiStrCmp (Token, "StartImage:") == 0) { ConvertChar16ToChar8 (Ptr + 1, L"simg"); } else if (AsciiStrCmp (Token, "LoadImage:") == 0) { ConvertChar16ToChar8 (Ptr + 1, L"limg"); } } /** Get the short verion of PDB file name to be used in performance data logging. @param PdbFileName The long PDB file name. @param GaugeString The output string to be logged by performance logger. **/ VOID GetShortPdbFileName ( IN CONST CHAR8 *PdbFileName, OUT CHAR8 *GaugeString ) { UINTN Index; UINTN Index1; UINTN StartIndex; UINTN EndIndex; if (PdbFileName == NULL) { AsciiStrCpyS (GaugeString, PERF_TOKEN_LENGTH, " "); } else { StartIndex = 0; for (EndIndex = 0; PdbFileName[EndIndex] != 0; EndIndex++) ; for (Index = 0; PdbFileName[Index] != 0; Index++) { if (PdbFileName[Index] == '\\') { StartIndex = Index + 1; } if (PdbFileName[Index] == '.') { EndIndex = Index; } } Index1 = 0; for (Index = StartIndex; Index < EndIndex; Index++) { GaugeString[Index1] = PdbFileName[Index]; Index1++; if (Index1 == PERF_TOKEN_LENGTH - 1) { break; } } GaugeString[Index1] = 0; } return ; } /** Get the name from the Driver handle, which can be a handle with EFI_LOADED_IMAGE_PROTOCOL or EFI_DRIVER_BINDING_PROTOCOL installed. This name can be used in performance data logging. @param Handle Driver handle. @param GaugeString The output string to be logged by performance logger. **/ VOID GetNameFromHandle ( IN EFI_HANDLE Handle, OUT CHAR8 *GaugeString ) { EFI_STATUS Status; EFI_LOADED_IMAGE_PROTOCOL *Image; CHAR8 *PdbFileName; EFI_DRIVER_BINDING_PROTOCOL *DriverBinding; CHAR16 *Tok; POOL_PRINT Str; AsciiStrCpyS (GaugeString, PERF_TOKEN_LENGTH, " "); ZeroMem (&Str, sizeof (Str)); Tok = NULL; // // Get handle name from image protocol // Status = gBS->HandleProtocol ( Handle, &gEfiLoadedImageProtocolGuid, (VOID **) &Image ); if (EFI_ERROR (Status)) { Status = gBS->OpenProtocol ( Handle, &gEfiDriverBindingProtocolGuid, (VOID **) &DriverBinding, NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { return ; } // // Get handle name from image protocol // Status = gBS->HandleProtocol ( DriverBinding->ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &Image ); } PdbFileName = PeCoffLoaderGetPdbPointer (Image->ImageBase); if (PdbFileName != NULL) { GetShortPdbFileName (PdbFileName, GaugeString); } else { Tok = FvFilePath (&Str,Image->FilePath); if (Tok != NULL) { ConvertChar16ToChar8 (GaugeString, Tok); FreePool (Tok); } } return ; } /** Allocates a block of memory and writes performance data of booting into it. OS can processing these record. **/ VOID WriteBootToOsPerformanceData ( VOID ) { EFI_STATUS Status; UINT32 LimitCount; EFI_HANDLE *Handles; UINTN NoHandles; CHAR8 GaugeString[PERF_TOKEN_LENGTH]; UINT8 *Ptr; UINT32 Index; UINT64 Ticker; UINT64 Freq; UINT32 Duration; UINTN LogEntryKey; CONST VOID *Handle; CONST CHAR8 *Token; CONST CHAR8 *Module; CONST CHAR8 *DriverToken; UINT64 StartTicker; UINT64 EndTicker; UINT64 StartValue; UINT64 EndValue; BOOLEAN CountUp; UINTN EntryIndex; UINTN NumPerfEntries; // // List of flags indicating PerfEntry contains DXE handle // BOOLEAN *PerfEntriesAsDxeHandle; UINTN VarSize; // // Retrieve time stamp count as early as possible // Ticker = GetPerformanceCounter (); Freq = GetPerformanceCounterProperties (&StartValue, &EndValue); Freq = DivU64x32 (Freq, 1000); ZeroMem (&mPerfHeader, sizeof (PERF_HEADER)); mPerfHeader.CpuFreq = Freq; // // Record BDS raw performance data // if (EndValue >= StartValue) { mPerfHeader.BDSRaw = Ticker - StartValue; CountUp = TRUE; } else { mPerfHeader.BDSRaw = StartValue - Ticker; CountUp = FALSE; } // // Put Detailed performance data into memory // Handles = NULL; Status = gBS->LocateHandleBuffer ( AllHandles, NULL, NULL, &NoHandles, &Handles ); if (EFI_ERROR (Status)) { return ; } if (mAcpiLowMemoryBase == 0x0FFFFFFFF) { VarSize = sizeof (EFI_PHYSICAL_ADDRESS); Status = gRT->GetVariable ( L"PerfDataMemAddr", &gEfiGenericVariableGuid, NULL, &VarSize, &mAcpiLowMemoryBase ); if (EFI_ERROR (Status)) { // // Fail to get the variable, return. // FreePool (Handles); return; } } Ptr = (UINT8 *) ((UINT32) mAcpiLowMemoryBase + sizeof (PERF_HEADER)); LimitCount = (UINT32) (PERF_DATA_MAX_LENGTH - sizeof (PERF_HEADER)) / sizeof (PERF_DATA); NumPerfEntries = 0; LogEntryKey = 0; while ((LogEntryKey = GetPerformanceMeasurement ( LogEntryKey, &Handle, &Token, &Module, &StartTicker, &EndTicker)) != 0) { NumPerfEntries++; } PerfEntriesAsDxeHandle = AllocateZeroPool (NumPerfEntries * sizeof (BOOLEAN)); ASSERT (PerfEntriesAsDxeHandle != NULL); if (PerfEntriesAsDxeHandle == NULL) { return; } // // Get DXE drivers performance // for (Index = 0; Index < NoHandles; Index++) { LogEntryKey = 0; EntryIndex = 0; Token = NULL; DriverToken = NULL; while ((LogEntryKey = GetPerformanceMeasurement ( LogEntryKey, &Handle, &Token, &Module, &StartTicker, &EndTicker)) != 0) { Ticker = 0; if (Handle == Handles[Index] && !PerfEntriesAsDxeHandle[EntryIndex]) { PerfEntriesAsDxeHandle[EntryIndex] = TRUE; DriverToken = Token; } EntryIndex++; if ((Handle == Handles[Index]) && (EndTicker != 0) && DriverToken != NULL) { if (StartTicker == 1) { StartTicker = StartValue; } if (EndTicker == 1) { EndTicker = StartValue; } Ticker += CountUp ? (EndTicker - StartTicker) : (StartTicker - EndTicker); Duration = (UINT32) DivU64x32 (Ticker, (UINT32) Freq); if (Duration > 0) { ZeroMem (&mPerfData, sizeof (PERF_DATA)); ZeroMem (GaugeString, PERF_TOKEN_LENGTH); GetNameFromHandle (Handles[Index], GaugeString); AsciiStrCpyS (mPerfData.Token, sizeof(mPerfData.Token), GaugeString); WriteDxeDriverToken (mPerfData.Token, (CHAR8 *) DriverToken, (UINT16 *)&Index); mPerfData.Duration = Duration; CopyMem (Ptr, &mPerfData, sizeof (PERF_DATA)); Ptr += sizeof (PERF_DATA); mPerfHeader.Count++; if (mPerfHeader.Count == LimitCount) { goto Done; } } } } } // // Get inserted performance data // LogEntryKey = 0; EntryIndex = 0; while ((LogEntryKey = GetPerformanceMeasurement ( LogEntryKey, &Handle, &Token, &Module, &StartTicker, &EndTicker)) != 0) { Index = 0; while (AsciiStrCmp (mSkipToken[Index], "\0") != 0) { if (AsciiStrCmp (Token, mSkipToken[Index]) == 0) { EndTicker = 0; break; } Index++; } if (!PerfEntriesAsDxeHandle[EntryIndex] && EndTicker != 0) { ZeroMem (&mPerfData, sizeof (PERF_DATA)); if (Handle != NULL){ WritePeiDriverGuid (mPerfData.Token, (VOID*)Handle); } else { AsciiStrnCpyS (mPerfData.Token, sizeof (mPerfData.Token), Token, PERF_TOKEN_LENGTH); } if (StartTicker == 1) { StartTicker = StartValue; } if (EndTicker == 1) { EndTicker = StartValue; } Ticker = CountUp ? (EndTicker - StartTicker) : (StartTicker - EndTicker); mPerfData.Duration = (UINT32) DivU64x32 (Ticker, (UINT32) Freq); CopyMem (Ptr, &mPerfData, sizeof (PERF_DATA)); Ptr += sizeof (PERF_DATA); mPerfHeader.Count++; if (mPerfHeader.Count == LimitCount) { goto Done; } } EntryIndex++; } Done: FreePool (Handles); FreePool (PerfEntriesAsDxeHandle); mPerfHeader.Signiture = PERFORMANCE_SIGNATURE; // // Put performance data to Reserved memory // CopyMem ( (UINTN *) (UINTN) mAcpiLowMemoryBase, &mPerfHeader, sizeof (PERF_HEADER) ); return ; }