/** @file * * Copyright (c) 2011-2013, ARM Limited. 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 <PiDxe.h> #include <Library/ArmLib.h> #include <Library/CacheMaintenanceLib.h> #include <Library/EblCmdLib.h> #include <Library/BaseLib.h> #include <Library/DxeServicesTableLib.h> #include <Library/DebugLib.h> #include <Library/UefiBootServicesTableLib.h> #include <Library/UefiRuntimeServicesTableLib.h> #include <Library/MemoryAllocationLib.h> #include <Library/UefiLib.h> #include <Library/PcdLib.h> #include <Library/EfiFileLib.h> #include <Library/ArmDisassemblerLib.h> #include <Library/PeCoffGetEntryPointLib.h> #include <Library/PerformanceLib.h> #include <Library/TimerLib.h> #include <Library/BdsLib.h> #include <Guid/DebugImageInfoTable.h> #include <Protocol/DebugSupport.h> #include <Protocol/LoadedImage.h> #include <Protocol/DevicePathToText.h> EFI_STATUS EblDumpMmu ( IN UINTN Argc, IN CHAR8 **Argv ); EFI_STATUS EblDumpFdt ( IN UINTN Argc, IN CHAR8 **Argv ); /** Simple arm disassembler via a library Argv[0] - symboltable Argv[1] - Optional qoted format string Argv[2] - Optional flag @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS **/ EFI_STATUS EblSymbolTable ( IN UINTN Argc, IN CHAR8 **Argv ) { EFI_STATUS Status; EFI_DEBUG_IMAGE_INFO_TABLE_HEADER *DebugImageTableHeader = NULL; EFI_DEBUG_IMAGE_INFO *DebugTable; UINTN Entry; CHAR8 *Format; CHAR8 *Pdb; UINT32 PeCoffSizeOfHeaders; UINT32 ImageBase; BOOLEAN Elf; // Need to add lots of error checking on the passed in string // Default string is for RealView debugger #if (__ARMCC_VERSION < 500000) Format = (Argc > 1) ? Argv[1] : "load /a /ni /np %a &0x%x"; #else Format = (Argc > 1) ? Argv[1] : "add-symbol-file %a 0x%x"; #endif Elf = (Argc > 2) ? FALSE : TRUE; Status = EfiGetSystemConfigurationTable (&gEfiDebugImageInfoTableGuid, (VOID **)&DebugImageTableHeader); if (EFI_ERROR (Status)) { return Status; } DebugTable = DebugImageTableHeader->EfiDebugImageInfoTable; if (DebugTable == NULL) { return EFI_SUCCESS; } for (Entry = 0; Entry < DebugImageTableHeader->TableSize; Entry++, DebugTable++) { if (DebugTable->NormalImage != NULL) { if ((DebugTable->NormalImage->ImageInfoType == EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL) && (DebugTable->NormalImage->LoadedImageProtocolInstance != NULL)) { ImageBase = (UINTN)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase; PeCoffSizeOfHeaders = PeCoffGetSizeOfHeaders ((VOID *)(UINTN)ImageBase); Pdb = PeCoffLoaderGetPdbPointer (DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase); if (Pdb != NULL) { if (Elf) { // ELF and Mach-O images don't include the header so the linked address does not include header ImageBase += PeCoffSizeOfHeaders; } AsciiPrint (Format, Pdb, ImageBase); AsciiPrint ("\n"); } else { } } } } return EFI_SUCCESS; } /** Simple arm disassembler via a library Argv[0] - disasm Argv[1] - Address to start disassembling from ARgv[2] - Number of instructions to disassembly (optional) @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS **/ EFI_STATUS EblDisassembler ( IN UINTN Argc, IN CHAR8 **Argv ) { UINT8 *Ptr, *CurrentAddress; UINT32 Address; UINT32 Count; CHAR8 Buffer[80]; UINT32 ItBlock; if (Argc < 2) { return EFI_INVALID_PARAMETER; } Address = AsciiStrHexToUintn (Argv[1]); Count = (Argc > 2) ? (UINT32)AsciiStrHexToUintn (Argv[2]) : 20; Ptr = (UINT8 *)(UINTN)Address; ItBlock = 0; do { CurrentAddress = Ptr; DisassembleInstruction (&Ptr, TRUE, TRUE, &ItBlock, Buffer, sizeof (Buffer)); AsciiPrint ("0x%08x: %a\n", CurrentAddress, Buffer); } while (Count-- > 0); return EFI_SUCCESS; } CHAR8 * ImageHandleToPdbFileName ( IN EFI_HANDLE Handle ) { EFI_STATUS Status; EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; CHAR8 *Pdb; CHAR8 *StripLeading; Status = gBS->HandleProtocol (Handle, &gEfiLoadedImageProtocolGuid, (VOID **)&LoadedImage); if (EFI_ERROR (Status)) { return ""; } Pdb = PeCoffLoaderGetPdbPointer (LoadedImage->ImageBase); StripLeading = AsciiStrStr (Pdb, "\\ARM\\"); if (StripLeading == NULL) { StripLeading = AsciiStrStr (Pdb, "/ARM/"); if (StripLeading == NULL) { return Pdb; } } // Hopefully we hacked off the unneeded part return (StripLeading + 5); } STATIC CHAR8 *mTokenList[] = { /*"SEC",*/ "PEI", "DXE", /*"BDS",*/ NULL }; /** Simple arm disassembler via a library Argv[0] - disasm Argv[1] - Address to start disassembling from ARgv[2] - Number of instructions to disassembly (optional) @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS **/ EFI_STATUS EblPerformance ( IN UINTN Argc, IN CHAR8 **Argv ) { UINTN Key; CONST VOID *Handle; CONST CHAR8 *Token, *Module; UINT64 Start, Stop, TimeStamp; UINT64 Delta, TicksPerSecond, Milliseconds, Microseconds; UINTN Index; BOOLEAN CountUp; TicksPerSecond = GetPerformanceCounterProperties (&Start, &Stop); if (Start < Stop) { CountUp = TRUE; } else { CountUp = FALSE; } Key = 0; do { Key = GetPerformanceMeasurement (Key, (CONST VOID **)&Handle, &Token, &Module, &Start, &Stop); if (Key != 0) { if (AsciiStriCmp ("StartImage:", Token) == 0) { if (Stop == 0) { // The entry for EBL is still running so the stop time will be zero. Skip it AsciiPrint (" running %a\n", ImageHandleToPdbFileName ((EFI_HANDLE)Handle)); } else { Delta = CountUp?(Stop - Start):(Start - Stop); Microseconds = DivU64x64Remainder (MultU64x32 (Delta, 1000000), TicksPerSecond, NULL); AsciiPrint ("%10ld us %a\n", Microseconds, ImageHandleToPdbFileName ((EFI_HANDLE)Handle)); } } } } while (Key != 0); AsciiPrint ("\n"); TimeStamp = 0; Key = 0; do { Key = GetPerformanceMeasurement (Key, (CONST VOID **)&Handle, &Token, &Module, &Start, &Stop); if (Key != 0) { for (Index = 0; mTokenList[Index] != NULL; Index++) { if (AsciiStriCmp (mTokenList[Index], Token) == 0) { Delta = CountUp?(Stop - Start):(Start - Stop); TimeStamp += Delta; Milliseconds = DivU64x64Remainder (MultU64x32 (Delta, 1000), TicksPerSecond, NULL); AsciiPrint ("%6a %6ld ms\n", Token, Milliseconds); break; } } } } while (Key != 0); AsciiPrint ("Total Time = %ld ms\n\n", DivU64x64Remainder (MultU64x32 (TimeStamp, 1000), TicksPerSecond, NULL)); return EFI_SUCCESS; } #define EFI_MEMORY_PORT_IO 0x4000000000000000ULL EFI_STATUS EblDumpGcd ( IN UINTN Argc, IN CHAR8 **Argv ) { EFI_STATUS Status; UINTN NumberOfDescriptors; UINTN i; EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; EFI_GCD_IO_SPACE_DESCRIPTOR *IoSpaceMap; Status = gDS->GetMemorySpaceMap(&NumberOfDescriptors,&MemorySpaceMap); if (EFI_ERROR (Status)) { return Status; } AsciiPrint (" Address Range Image Device Attributes\n"); AsciiPrint ("__________________________________________________________\n"); for (i=0; i < NumberOfDescriptors; i++) { AsciiPrint ("MEM %016lx - %016lx",(UINT64)MemorySpaceMap[i].BaseAddress,MemorySpaceMap[i].BaseAddress+MemorySpaceMap[i].Length-1); AsciiPrint (" %08x %08x",MemorySpaceMap[i].ImageHandle,MemorySpaceMap[i].DeviceHandle); if (MemorySpaceMap[i].Attributes & EFI_MEMORY_RUNTIME) AsciiPrint (" RUNTIME"); if (MemorySpaceMap[i].Attributes & EFI_MEMORY_PORT_IO) AsciiPrint (" PORT_IO"); if (MemorySpaceMap[i].Attributes & EFI_MEMORY_UC) AsciiPrint (" MEM_UC"); if (MemorySpaceMap[i].Attributes & EFI_MEMORY_WC) AsciiPrint (" MEM_WC"); if (MemorySpaceMap[i].Attributes & EFI_MEMORY_WT) AsciiPrint (" MEM_WT"); if (MemorySpaceMap[i].Attributes & EFI_MEMORY_WB) AsciiPrint (" MEM_WB"); if (MemorySpaceMap[i].Attributes & EFI_MEMORY_UCE) AsciiPrint (" MEM_UCE"); if (MemorySpaceMap[i].Attributes & EFI_MEMORY_WP) AsciiPrint (" MEM_WP"); if (MemorySpaceMap[i].Attributes & EFI_MEMORY_RP) AsciiPrint (" MEM_RP"); if (MemorySpaceMap[i].Attributes & EFI_MEMORY_XP) AsciiPrint (" MEM_XP"); switch (MemorySpaceMap[i].GcdMemoryType) { case EfiGcdMemoryTypeNonExistent: AsciiPrint (" TYPE_NONEXISTENT"); break; case EfiGcdMemoryTypeReserved: AsciiPrint (" TYPE_RESERVED"); break; case EfiGcdMemoryTypeSystemMemory: AsciiPrint (" TYPE_SYSMEM"); break; case EfiGcdMemoryTypeMemoryMappedIo: AsciiPrint (" TYPE_MEMMAP"); break; default: AsciiPrint (" TYPE_UNKNOWN"); break; } AsciiPrint ("\n"); } FreePool (MemorySpaceMap); Status = gDS->GetIoSpaceMap(&NumberOfDescriptors,&IoSpaceMap); if (EFI_ERROR (Status)) { return Status; } for (i=0; i < NumberOfDescriptors; i++) { AsciiPrint ("IO %08lx - %08lx",IoSpaceMap[i].BaseAddress,IoSpaceMap[i].BaseAddress+IoSpaceMap[i].Length); AsciiPrint ("\t%08x %08x",IoSpaceMap[i].ImageHandle,IoSpaceMap[i].DeviceHandle); switch (IoSpaceMap[i].GcdIoType) { case EfiGcdIoTypeNonExistent: AsciiPrint (" TYPE_NONEXISTENT"); break; case EfiGcdIoTypeReserved: AsciiPrint (" TYPE_RESERVED"); break; case EfiGcdIoTypeIo: AsciiPrint (" TYPE_IO"); break; default: AsciiPrint (" TYPE_UNKNOWN"); break; } AsciiPrint ("\n"); } FreePool (IoSpaceMap); return EFI_SUCCESS; } EFI_STATUS EblDevicePaths ( IN UINTN Argc, IN CHAR8 **Argv ) { EFI_STATUS Status; UINTN HandleCount; EFI_HANDLE *HandleBuffer; UINTN Index; CHAR16* String; EFI_DEVICE_PATH_PROTOCOL* DevicePathProtocol; EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol; BdsConnectAllDrivers(); Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol); if (EFI_ERROR (Status)) { AsciiPrint ("Did not find the DevicePathToTextProtocol.\n"); return EFI_SUCCESS; } Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiDevicePathProtocolGuid, NULL, &HandleCount, &HandleBuffer); if (EFI_ERROR (Status)) { AsciiPrint ("No device path found\n"); return EFI_SUCCESS; } for (Index = 0; Index < HandleCount; Index++) { Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol); String = DevicePathToTextProtocol->ConvertDevicePathToText(DevicePathProtocol,TRUE,TRUE); Print (L"[0x%X] %s\n",(UINTN)HandleBuffer[Index], String); } return EFI_SUCCESS; } GLOBAL_REMOVE_IF_UNREFERENCED const EBL_COMMAND_TABLE mLibCmdTemplate[] = { { "disasm address [count]", " disassemble count instructions", NULL, EblDisassembler }, { "performance", " Display boot performance info", NULL, EblPerformance }, { "symboltable [\"format string\"] [PECOFF]", " show symbol table commands for debugger", NULL, EblSymbolTable }, { "dumpgcd", " dump Global Coherency Domain", NULL, EblDumpGcd }, { "dumpmmu", " dump MMU Table", NULL, EblDumpMmu }, { "devicepaths", " list all the Device Paths", NULL, EblDevicePaths }, { "dumpfdt", " dump the current fdt or the one defined in the arguments", NULL, EblDumpFdt } }; VOID EblInitializeExternalCmd ( VOID ) { EblAddCommands (mLibCmdTemplate, sizeof (mLibCmdTemplate)/sizeof (EBL_COMMAND_TABLE)); return; }