| /** @file | |
| Basic command line parser for EBL (Embedded Boot Loader) | |
| Copyright (c) 2007, Intel Corporation. All rights reserved.<BR> | |
| Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR> | |
| (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR> | |
| 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. | |
| Module Name: HwDebug.c | |
| Commands useful for debugging hardware. | |
| **/ | |
| #include "Ebl.h" | |
| /** | |
| Dump memory | |
| Argv[0] - "md"[.#] # is optional width 1, 2, 4, or 8. Default 1 | |
| Argv[1] - Hex Address to dump | |
| Argv[2] - Number of hex bytes to dump (0x20 is default) | |
| md.4 0x123445678 50 ; Dump 0x50 4 byte quantities starting at 0x123445678 | |
| md 0x123445678 40 ; Dump 0x40 1 byte quantities starting at 0x123445678 | |
| md 0x123445678 ; Dump 0x20 1 byte quantities starting at 0x123445678 | |
| @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 | |
| EFIAPI | |
| EblMdCmd ( | |
| IN UINTN Argc, | |
| IN CHAR8 **Argv | |
| ) | |
| { | |
| STATIC UINT8 *Address = NULL; | |
| STATIC UINTN Length = 0x20; | |
| STATIC UINTN Width; | |
| Width = WidthFromCommandName (Argv[0], 1); | |
| switch (Argc) { | |
| case 3: | |
| Length = AsciiStrHexToUintn(Argv[2]); | |
| case 2: | |
| Address = (UINT8 *)AsciiStrHexToUintn (Argv[1]); | |
| default: | |
| break; | |
| } | |
| OutputData (Address, Length, Width, (UINTN)Address); | |
| Address += Length; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Fill Memory with data | |
| Argv[0] - "mfill"[.#] # is optional width 1, 2, 4, or 8. Default 4 | |
| Argv[1] - Hex Address to fill | |
| Argv[2] - Data to write (0x00 is default) | |
| Argv[3] - Number of units to dump. | |
| mf.1 0x123445678 aa 100 ; Start at 0x123445678 and write aa (1 byte) to the next 100 bytes | |
| mf.4 0x123445678 aa 100 ; Start at 0x123445678 and write aa (4 byte) to the next 400 bytes | |
| mf 0x123445678 aa ; Start at 0x123445678 and write aa (4 byte) to the next 1 byte | |
| mf 0x123445678 ; Start at 0x123445678 and write 00 (4 byte) to the next 1 byte | |
| @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 | |
| EFIAPI | |
| EblMfillCmd ( | |
| IN UINTN Argc, | |
| IN CHAR8 **Argv | |
| ) | |
| { | |
| UINTN Address; | |
| UINTN EndAddress; | |
| UINT32 Data; | |
| UINTN Length; | |
| UINTN Width; | |
| if (Argc < 2) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Width = WidthFromCommandName (Argv[0], 4); | |
| Address = AsciiStrHexToUintn (Argv[1]); | |
| Data = (Argc > 2) ? (UINT32)AsciiStrHexToUintn (Argv[2]) : 0; | |
| Length = (Argc > 3) ? AsciiStrHexToUintn (Argv[3]) : 1; | |
| for (EndAddress = Address + (Length * Width); Address < EndAddress; Address += Width) { | |
| if (Width == 4) { | |
| MmioWrite32 (Address, Data); | |
| } else if (Width == 2) { | |
| MmioWrite16 (Address, (UINT16)Data); | |
| } else { | |
| MmioWrite8 (Address, (UINT8)Data); | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // Strings for PCI Class code [2] | |
| // | |
| CHAR8 *gPciDevClass[] = { | |
| "Old Device ", | |
| "Mass storage ", | |
| "Network ", | |
| "Display ", | |
| "Multimedia ", | |
| "Memory controller ", | |
| "Bridge device ", | |
| "simple communications ", | |
| "base system peripherals", | |
| "Input devices ", | |
| "Docking stations ", | |
| "Processors ", | |
| "serial bus ", | |
| }; | |
| CHAR8 *gPciSerialClassCodes[] = { | |
| "Mass storage ", | |
| "Firewire ", | |
| "ACCESS bus ", | |
| "SSA ", | |
| "USB " | |
| }; | |
| /** | |
| PCI Dump | |
| Argv[0] - "pci" | |
| Argv[1] - bus | |
| Argv[2] - dev | |
| Argv[3] - func | |
| @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 | |
| EFIAPI | |
| EblPciCmd ( | |
| IN UINTN Argc, | |
| IN CHAR8 **Argv | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_PCI_IO_PROTOCOL *Pci; | |
| UINTN HandleCount; | |
| EFI_HANDLE *HandleBuffer; | |
| UINTN Seg; | |
| UINTN Bus; | |
| UINTN Dev; | |
| UINTN Func; | |
| UINTN BusArg; | |
| UINTN DevArg; | |
| UINTN FuncArg; | |
| UINTN Index; | |
| UINTN Count; | |
| PCI_TYPE_GENERIC PciHeader; | |
| PCI_TYPE_GENERIC *Header; | |
| PCI_BRIDGE_CONTROL_REGISTER *Bridge; | |
| PCI_DEVICE_HEADER_TYPE_REGION *Device; | |
| PCI_DEVICE_INDEPENDENT_REGION *Hdr; | |
| CHAR8 *Str; | |
| UINTN ThisBus; | |
| BusArg = (Argc > 1) ? AsciiStrDecimalToUintn (Argv[1]) : 0; | |
| DevArg = (Argc > 2) ? AsciiStrDecimalToUintn (Argv[2]) : 0; | |
| FuncArg = (Argc > 3) ? AsciiStrDecimalToUintn (Argv[3]) : 0; | |
| Header = &PciHeader; | |
| Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiPciIoProtocolGuid, NULL, &HandleCount, &HandleBuffer); | |
| if (EFI_ERROR (Status)) { | |
| AsciiPrint ("No PCI devices found in the system\n"); | |
| return EFI_SUCCESS; | |
| } | |
| if (Argc == 1) { | |
| // Dump all PCI devices | |
| AsciiPrint ("BusDevFun VendorId DeviceId Device Class Sub-Class\n"); | |
| AsciiPrint ("_____________________________________________________________"); | |
| for (ThisBus = 0; ThisBus <= PCI_MAX_BUS; ThisBus++) { | |
| for (Index = 0; Index < HandleCount; Index++) { | |
| Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiPciIoProtocolGuid, (VOID **)&Pci); | |
| if (!EFI_ERROR (Status)) { | |
| Pci->GetLocation (Pci, &Seg, &Bus, &Dev, &Func); | |
| if (ThisBus != Bus) { | |
| continue; | |
| } | |
| AsciiPrint ("\n%03d.%02d.%02d", Bus, Dev, Func); | |
| Status = Pci->Pci.Read (Pci, EfiPciIoWidthUint32, 0, sizeof (PciHeader)/sizeof (UINT32), &PciHeader); | |
| if (!EFI_ERROR (Status)) { | |
| Hdr = &PciHeader.Bridge.Hdr; | |
| if (Hdr->ClassCode[2] < sizeof (gPciDevClass)/sizeof (VOID *)) { | |
| Str = gPciDevClass[Hdr->ClassCode[2]]; | |
| if (Hdr->ClassCode[2] == PCI_CLASS_SERIAL) { | |
| if (Hdr->ClassCode[1] < sizeof (gPciSerialClassCodes)/sizeof (VOID *)) { | |
| // print out Firewire or USB inplace of Serial Bus controllers | |
| Str = gPciSerialClassCodes[Hdr->ClassCode[1]]; | |
| } | |
| } | |
| } else { | |
| Str = "Unknown device "; | |
| } | |
| AsciiPrint (" 0x%04x 0x%04x %a 0x%02x", Hdr->VendorId, Hdr->DeviceId, Str, Hdr->ClassCode[1]); | |
| } | |
| if (Seg != 0) { | |
| // Only print Segment if it is non zero. If you only have one PCI segment it is | |
| // redundent to print it out | |
| AsciiPrint (" Seg:%d", Seg); | |
| } | |
| } | |
| } | |
| } | |
| AsciiPrint ("\n"); | |
| } else { | |
| // Dump specific PCI device | |
| for (Index = 0; Index < HandleCount; Index++) { | |
| Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiPciIoProtocolGuid, (VOID **)&Pci); | |
| if (!EFI_ERROR (Status)) { | |
| Pci->GetLocation (Pci, &Seg, &Bus, &Dev, &Func); | |
| if ((Bus == BusArg) && (Dev == DevArg) && (Func == FuncArg)) { | |
| // Only print Segment if it is non zero. If you only have one PCI segment it is | |
| // redundant to print it out | |
| if (Seg != 0) { | |
| AsciiPrint ("Seg:%d ", Seg); | |
| } | |
| AsciiPrint ("Bus:%d Dev:%d Func:%d ", Bus, Dev, Func); | |
| Status = Pci->Pci.Read (Pci, EfiPciIoWidthUint32, 0, sizeof (PciHeader)/sizeof (UINT32), Header); | |
| if (!EFI_ERROR (Status)) { | |
| Hdr = &PciHeader.Bridge.Hdr; | |
| if (IS_PCI_BRIDGE (&PciHeader.Bridge)) { | |
| Bridge = &PciHeader.Bridge.Bridge; | |
| AsciiPrint ( | |
| "PCI Bridge. Bus Primary %d Secondary %d Subordinate %d\n", | |
| Bridge->PrimaryBus, Bridge->SecondaryBus, Bridge->SubordinateBus | |
| ); | |
| AsciiPrint (" Bar 0: 0x%08x Bar 1: 0x%08x\n", Bridge->Bar[0], Bridge->Bar[1]); | |
| } else { | |
| Device = &PciHeader.Device.Device; | |
| AsciiPrint ( | |
| "VendorId: 0x%04x DeviceId: 0x%04x SubSusVendorId: 0x%04x SubSysDeviceId: 0x%04x\n", | |
| Hdr->VendorId, Hdr->DeviceId, Device->SubsystemVendorID, Device->SubsystemID | |
| ); | |
| AsciiPrint (" Class Code: 0x%02x 0x%02x 0x%02x\n", Hdr->ClassCode[2], Hdr->ClassCode[1], Hdr->ClassCode[0]); | |
| for (Count = 0; Count < 6; Count++) { | |
| AsciiPrint (" Bar %d: 0x%08x\n", Count, Device->Bar[Count]); | |
| } | |
| } | |
| } | |
| AsciiPrint ("\n"); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| FreePool (HandleBuffer); | |
| return EFI_SUCCESS; | |
| } | |
| GLOBAL_REMOVE_IF_UNREFERENCED const EBL_COMMAND_TABLE mCmdPciDebugTemplate[] = { | |
| { | |
| "pci", | |
| " [bus] [dev] [func]; Dump PCI", | |
| NULL, | |
| EblPciCmd | |
| } | |
| }; | |
| GLOBAL_REMOVE_IF_UNREFERENCED const EBL_COMMAND_TABLE mCmdHwDebugTemplate[] = | |
| { | |
| { | |
| "md", | |
| "[.{1|2|4}] [Addr] [Len] [1|2|4]; Memory Dump from Addr Len bytes", | |
| NULL, | |
| EblMdCmd | |
| }, | |
| { | |
| "mfill", | |
| "[.{1|2|4}] Addr Len [data]; Memory Fill Addr Len*(1|2|4) bytes of data(0)", | |
| NULL, | |
| EblMfillCmd | |
| }, | |
| }; | |
| /** | |
| Initialize the commands in this in this file | |
| **/ | |
| VOID | |
| EblInitializemdHwDebugCmds ( | |
| VOID | |
| ) | |
| { | |
| if (FeaturePcdGet (PcdEmbeddedHwDebugCmd)) { | |
| EblAddCommands (mCmdHwDebugTemplate, sizeof (mCmdHwDebugTemplate)/sizeof (EBL_COMMAND_TABLE)); | |
| } | |
| if (FeaturePcdGet (PcdEmbeddedPciDebugCmd)) { | |
| EblAddCommands (mCmdPciDebugTemplate, sizeof (mCmdPciDebugTemplate)/sizeof (EBL_COMMAND_TABLE)); | |
| } | |
| } | |