blob: 2521dc5a8592e200cd7399503f8a3d2e3c83fe70 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2016, Google, Inc. All rights reserved
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <ctype.h>
#include <debug.h>
#include <inttypes.h>
#include <lib/console.h>
#include <string.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <dev/pcie_bridge.h>
#include <dev/pcie_bus_driver.h>
#include <dev/pcie_device.h>
#include <fbl/algorithm.h>
#include <ktl/iterator.h>
#include "dev/pcie_irqs.h"
class PcieDebugConsole {
public:
static int CmdLsPci(int argc, const cmd_args* argv, uint32_t flags);
static int CmdPciUnplug(int argc, const cmd_args* argv, uint32_t flags);
static int CmdPciReset(int argc, const cmd_args* argv, uint32_t flags);
static int CmdPciRescan(int argc, const cmd_args* argv, uint32_t flags);
static int CmdPciRegionDump(int argc, const cmd_args* argv, uint32_t flags);
};
/* Class code/Subclass code definitions taken from
* http://wiki.osdev.org/Pci#Class_Codes */
typedef struct {
uint8_t class_code;
uint8_t subclass;
uint8_t prof_if_start;
uint8_t prof_if_end;
const char* desc;
} pci_dev_type_lut_entry_t;
typedef struct lspci_params {
bool verbose;
uint base_level;
uint indent_level;
uint bus_id;
uint dev_id;
uint func_id;
uint cfg_dump_amt;
bool cfg_dump_ints;
bool force_dump_cfg;
uint found;
} lspci_params_t;
#define WILDCARD_ID (0xFFFFFFFF)
#define LUT_ENTRY(_class, _subclass, _pif_start, _pif_end, _desc) \
{ \
.class_code = _class, .subclass = _subclass, .prof_if_start = _pif_start, \
.prof_if_end = _pif_end, .desc = _desc, \
}
#define LUT_ENTRY_ONE_PIF(_class, _subclass, _pif, _desc) \
LUT_ENTRY(_class, _subclass, _pif, _pif, _desc)
#define LUT_ENTRY_ALL_PIF(_class, _subclass, _desc) LUT_ENTRY(_class, _subclass, 0x00, 0xFF, _desc)
static const char* IRQ_MODE_LABELS[] = {"Disabled", "Legacy", "MSI", "MSI-X"};
static const pci_dev_type_lut_entry_t PCI_DEV_TYPE_LUT[] = {
LUT_ENTRY_ONE_PIF(0x00, 0x00, 0x00, "Any device except for VGA-Compatible devices"),
LUT_ENTRY_ONE_PIF(0x00, 0x01, 0x00, "VGA-Compatible Device"),
LUT_ENTRY_ONE_PIF(0x01, 0x00, 0x00, "SCSI Bus Controller"),
LUT_ENTRY_ALL_PIF(0x01, 0x01, "IDE Controller"),
LUT_ENTRY_ONE_PIF(0x01, 0x02, 0x00, "Floppy Disk Controller"),
LUT_ENTRY_ONE_PIF(0x01, 0x03, 0x00, "IPI Bus Controller"),
LUT_ENTRY_ONE_PIF(0x01, 0x04, 0x00, "RAID Controller"),
LUT_ENTRY_ONE_PIF(0x01, 0x05, 0x20, "ATA Controller (Single DMA)"),
LUT_ENTRY_ONE_PIF(0x01, 0x05, 0x30, "ATA Controller (Chained DMA)"),
LUT_ENTRY_ONE_PIF(0x01, 0x06, 0x00, "Serial ATA (Vendor Specific Interface)"),
LUT_ENTRY_ONE_PIF(0x01, 0x06, 0x01, "Serial ATA (AHCI 1.0)"),
LUT_ENTRY_ONE_PIF(0x01, 0x07, 0x00, "Serial Attached SCSI (SAS)"),
LUT_ENTRY_ONE_PIF(0x01, 0x08, 0x01, "Non-Volatile Memory Controller (NVMHCI)"),
LUT_ENTRY_ONE_PIF(0x01, 0x08, 0x02, "Non-Volatile Memory Controller (NVM Express)"),
LUT_ENTRY_ONE_PIF(0x01, 0x80, 0x00, "Other Mass Storage Controller"),
LUT_ENTRY_ONE_PIF(0x02, 0x00, 0x00, "Ethernet Controller"),
LUT_ENTRY_ONE_PIF(0x02, 0x01, 0x00, "Token Ring Controller"),
LUT_ENTRY_ONE_PIF(0x02, 0x02, 0x00, "FDDI Controller"),
LUT_ENTRY_ONE_PIF(0x02, 0x03, 0x00, "ATM Controller"),
LUT_ENTRY_ONE_PIF(0x02, 0x04, 0x00, "ISDN Controller"),
LUT_ENTRY_ONE_PIF(0x02, 0x05, 0x00, "WorldFip Controller"),
LUT_ENTRY_ALL_PIF(0x02, 0x06, "PICMG 2.14 Multi Computing"),
LUT_ENTRY_ONE_PIF(0x02, 0x80, 0x00, "Other Network Controller"),
LUT_ENTRY_ONE_PIF(0x03, 0x00, 0x00, "VGA-Compatible Controller"),
LUT_ENTRY_ONE_PIF(0x03, 0x00, 0x01, "8512-Compatible Controller"),
LUT_ENTRY_ONE_PIF(0x03, 0x01, 0x00, "XGA Controller"),
LUT_ENTRY_ONE_PIF(0x03, 0x02, 0x00, "3D Controller (Not VGA-Compatible)"),
LUT_ENTRY_ONE_PIF(0x03, 0x80, 0x00, "Other Display Controller"),
LUT_ENTRY_ONE_PIF(0x04, 0x00, 0x00, "Video Device"),
LUT_ENTRY_ONE_PIF(0x04, 0x01, 0x00, "Audio Device"),
LUT_ENTRY_ONE_PIF(0x04, 0x02, 0x00, "Computer Telephony Device"),
LUT_ENTRY_ONE_PIF(0x04, 0x80, 0x00, "Other Multimedia Device"),
LUT_ENTRY_ONE_PIF(0x05, 0x00, 0x00, "RAM Controller"),
LUT_ENTRY_ONE_PIF(0x05, 0x01, 0x00, "Flash Controller"),
LUT_ENTRY_ONE_PIF(0x05, 0x80, 0x00, "Other Memory Controller"),
LUT_ENTRY_ONE_PIF(0x06, 0x00, 0x00, "Host Bridge"),
LUT_ENTRY_ONE_PIF(0x06, 0x01, 0x00, "ISA Bridge"),
LUT_ENTRY_ONE_PIF(0x06, 0x02, 0x00, "EISA Bridge"),
LUT_ENTRY_ONE_PIF(0x06, 0x03, 0x00, "MCA Bridge"),
LUT_ENTRY_ONE_PIF(0x06, 0x04, 0x00, "PCI-to-PCI Bridge"),
LUT_ENTRY_ONE_PIF(0x06, 0x04, 0x01, "PCI-to-PCI Bridge (Subtractive Decode)"),
LUT_ENTRY_ONE_PIF(0x06, 0x05, 0x00, "PCMCIA Bridge"),
LUT_ENTRY_ONE_PIF(0x06, 0x06, 0x00, "NuBus Bridge"),
LUT_ENTRY_ONE_PIF(0x06, 0x07, 0x00, "CardBus Bridge"),
LUT_ENTRY_ALL_PIF(0x06, 0x08, "RACEway Bridge"),
LUT_ENTRY_ONE_PIF(0x06, 0x09, 0x40, "PCI-to-PCI Bridge (Semi-Transparent, Primary)"),
LUT_ENTRY_ONE_PIF(0x06, 0x09, 0x80, "PCI-to-PCI Bridge (Semi-Transparent, Secondary)"),
LUT_ENTRY_ONE_PIF(0x06, 0x0A, 0x00, "InfiniBrand-to-PCI Host Bridge"),
LUT_ENTRY_ONE_PIF(0x06, 0x80, 0x00, "Other Bridge Device"),
LUT_ENTRY_ONE_PIF(0x07, 0x00, 0x00, "Generic XT-Compatible Serial Controller"),
LUT_ENTRY_ONE_PIF(0x07, 0x00, 0x01, "16450-Compatible Serial Controller"),
LUT_ENTRY_ONE_PIF(0x07, 0x00, 0x02, "16550-Compatible Serial Controller"),
LUT_ENTRY_ONE_PIF(0x07, 0x00, 0x03, "16650-Compatible Serial Controller"),
LUT_ENTRY_ONE_PIF(0x07, 0x00, 0x04, "16750-Compatible Serial Controller"),
LUT_ENTRY_ONE_PIF(0x07, 0x00, 0x05, "16850-Compatible Serial Controller"),
LUT_ENTRY_ONE_PIF(0x07, 0x00, 0x06, "16950-Compatible Serial Controller"),
LUT_ENTRY_ONE_PIF(0x07, 0x01, 0x00, "Parallel Port"),
LUT_ENTRY_ONE_PIF(0x07, 0x01, 0x01, "Bi-Directional Parallel Port"),
LUT_ENTRY_ONE_PIF(0x07, 0x01, 0x02, "ECP 1.X Compliant Parallel Port"),
LUT_ENTRY_ONE_PIF(0x07, 0x01, 0x03, "IEEE 1284 Controller"),
LUT_ENTRY_ONE_PIF(0x07, 0x01, 0xFE, "IEEE 1284 Target Device"),
LUT_ENTRY_ONE_PIF(0x07, 0x02, 0x00, "Multiport Serial Controller"),
LUT_ENTRY_ONE_PIF(0x07, 0x03, 0x00, "Generic Modem"),
LUT_ENTRY_ONE_PIF(0x07, 0x03, 0x01, "Hayes Compatible Modem (16450-Compatible Interface)"),
LUT_ENTRY_ONE_PIF(0x07, 0x03, 0x02, "Hayes Compatible Modem (16550-Compatible Interface)"),
LUT_ENTRY_ONE_PIF(0x07, 0x03, 0x03, "Hayes Compatible Modem (16650-Compatible Interface)"),
LUT_ENTRY_ONE_PIF(0x07, 0x03, 0x04, "Hayes Compatible Modem (16750-Compatible Interface)"),
LUT_ENTRY_ONE_PIF(0x07, 0x04, 0x00, "IEEE 488.1/2 (GPIB) Controller"),
LUT_ENTRY_ONE_PIF(0x07, 0x05, 0x00, "Smart Card"),
LUT_ENTRY_ONE_PIF(0x07, 0x80, 0x00, "Other Communications Device"),
LUT_ENTRY_ONE_PIF(0x08, 0x00, 0x00, "Generic 8259 PIC"),
LUT_ENTRY_ONE_PIF(0x08, 0x00, 0x01, "ISA PIC"),
LUT_ENTRY_ONE_PIF(0x08, 0x00, 0x02, "EISA PIC"),
LUT_ENTRY_ONE_PIF(0x08, 0x00, 0x10, "I/O APIC Interrupt Controller"),
LUT_ENTRY_ONE_PIF(0x08, 0x00, 0x20, "I/O(x) APIC Interrupt Controller"),
LUT_ENTRY_ONE_PIF(0x08, 0x01, 0x00, "Generic 8237 DMA Controller"),
LUT_ENTRY_ONE_PIF(0x08, 0x01, 0x01, "ISA DMA Controller"),
LUT_ENTRY_ONE_PIF(0x08, 0x01, 0x02, "EISA DMA Controller"),
LUT_ENTRY_ONE_PIF(0x08, 0x02, 0x00, "Generic 8254 System Timer"),
LUT_ENTRY_ONE_PIF(0x08, 0x02, 0x01, "ISA System Timer"),
LUT_ENTRY_ONE_PIF(0x08, 0x02, 0x02, "EISA System Timer"),
LUT_ENTRY_ONE_PIF(0x08, 0x03, 0x00, "Generic RTC Controller"),
LUT_ENTRY_ONE_PIF(0x08, 0x03, 0x01, "ISA RTC Controller"),
LUT_ENTRY_ONE_PIF(0x08, 0x04, 0x00, "Generic PCI Hot-Plug Controller"),
LUT_ENTRY_ONE_PIF(0x08, 0x80, 0x00, "Other System Peripheral"),
LUT_ENTRY_ONE_PIF(0x09, 0x00, 0x00, "Keyboard Controller"),
LUT_ENTRY_ONE_PIF(0x09, 0x01, 0x00, "Digitizer"),
LUT_ENTRY_ONE_PIF(0x09, 0x02, 0x00, "Mouse Controller"),
LUT_ENTRY_ONE_PIF(0x09, 0x03, 0x00, "Scanner Controller"),
LUT_ENTRY_ONE_PIF(0x09, 0x04, 0x00, "Gameport Controller (Generic)"),
LUT_ENTRY_ONE_PIF(0x09, 0x04, 0x10, "Gameport Contrlller (Legacy)"),
LUT_ENTRY_ONE_PIF(0x09, 0x80, 0x00, "Other Input Controller"),
LUT_ENTRY_ONE_PIF(0x0a, 0x00, 0x00, "Generic Docking Station"),
LUT_ENTRY_ONE_PIF(0x0a, 0x80, 0x00, "Other Docking Station"),
LUT_ENTRY_ONE_PIF(0x0b, 0x00, 0x00, "386 Processor"),
LUT_ENTRY_ONE_PIF(0x0b, 0x01, 0x00, "486 Processor"),
LUT_ENTRY_ONE_PIF(0x0b, 0x02, 0x00, "Pentium Processor"),
LUT_ENTRY_ONE_PIF(0x0b, 0x10, 0x00, "Alpha Processor"),
LUT_ENTRY_ONE_PIF(0x0b, 0x20, 0x00, "PowerPC Processor"),
LUT_ENTRY_ONE_PIF(0x0b, 0x30, 0x00, "MIPS Processor"),
LUT_ENTRY_ONE_PIF(0x0b, 0x40, 0x00, "Co-Processor"),
LUT_ENTRY_ONE_PIF(0x0c, 0x00, 0x00, "IEEE 1394 Controller (FireWire)"),
LUT_ENTRY_ONE_PIF(0x0c, 0x00, 0x10, "IEEE 1394 Controller (1394 OpenHCI Spec)"),
LUT_ENTRY_ONE_PIF(0x0c, 0x01, 0x00, "ACCESS.bus"),
LUT_ENTRY_ONE_PIF(0x0c, 0x02, 0x00, "SSA"),
LUT_ENTRY_ONE_PIF(0x0c, 0x03, 0x00, "USB (Universal Host Controller Spec)"),
LUT_ENTRY_ONE_PIF(0x0c, 0x03, 0x10, "USB (Open Host Controller Spec)"),
LUT_ENTRY_ONE_PIF(0x0c, 0x03, 0x20, "USB2 Host Controller (Intel EHCI)"),
LUT_ENTRY_ONE_PIF(0x0c, 0x03, 0x30, "USB3 XHCI Controller"),
LUT_ENTRY_ONE_PIF(0x0c, 0x03, 0x80, "Unspecified USB Controller"),
LUT_ENTRY_ONE_PIF(0x0c, 0x03, 0xFE, "USB (Not Host Controller)"),
LUT_ENTRY_ONE_PIF(0x0c, 0x04, 0x00, "Fibre Channel"),
LUT_ENTRY_ONE_PIF(0x0c, 0x05, 0x00, "SMBus"),
LUT_ENTRY_ONE_PIF(0x0c, 0x06, 0x00, "InfiniBand"),
LUT_ENTRY_ONE_PIF(0x0c, 0x07, 0x00, "IPMI SMIC Interface"),
LUT_ENTRY_ONE_PIF(0x0c, 0x07, 0x01, "IPMI Kybd Controller Style Interface"),
LUT_ENTRY_ONE_PIF(0x0c, 0x07, 0x02, "IPMI Block Transfer Interface"),
LUT_ENTRY_ONE_PIF(0x0c, 0x08, 0x00, "SERCOS Interface Standard (IEC 61491)"),
LUT_ENTRY_ONE_PIF(0x0c, 0x09, 0x00, "CANbus"),
LUT_ENTRY_ONE_PIF(0x0d, 0x00, 0x00, "iRDA Compatible Controller"),
LUT_ENTRY_ONE_PIF(0x0d, 0x01, 0x00, "Consumer IR Controller"),
LUT_ENTRY_ONE_PIF(0x0d, 0x10, 0x00, "RF Controller"),
LUT_ENTRY_ONE_PIF(0x0d, 0x11, 0x00, "Bluetooth Controller"),
LUT_ENTRY_ONE_PIF(0x0d, 0x12, 0x00, "Broadband Controller"),
LUT_ENTRY_ONE_PIF(0x0d, 0x20, 0x00, "Ethernet Controller (802.11a)"),
LUT_ENTRY_ONE_PIF(0x0d, 0x21, 0x00, "Ethernet Controller (802.11b)"),
LUT_ENTRY_ONE_PIF(0x0d, 0x80, 0x00, "Other Wireless Controller"),
LUT_ENTRY(0x0e, 0x00, 0x01, 0xFF, "I20 Architecture"),
LUT_ENTRY_ONE_PIF(0x0e, 0x00, 0x00, "Message FIFO"),
LUT_ENTRY_ONE_PIF(0x0f, 0x01, 0x00, "TV Controller"),
LUT_ENTRY_ONE_PIF(0x0f, 0x02, 0x00, "Audio Controller"),
LUT_ENTRY_ONE_PIF(0x0f, 0x03, 0x00, "Voice Controller"),
LUT_ENTRY_ONE_PIF(0x0f, 0x04, 0x00, "Data Controller"),
LUT_ENTRY_ONE_PIF(0x10, 0x00, 0x00, "Network and Computing Encrpytion/Decryption"),
LUT_ENTRY_ONE_PIF(0x10, 0x10, 0x00, "Entertainment Encryption/Decryption"),
LUT_ENTRY_ONE_PIF(0x10, 0x80, 0x00, "Other Encryption/Decryption"),
LUT_ENTRY_ONE_PIF(0x11, 0x00, 0x00, "DPIO Modules"),
LUT_ENTRY_ONE_PIF(0x11, 0x01, 0x00, "Performance Counters"),
LUT_ENTRY_ONE_PIF(0x11, 0x10, 0x00, "Communications Syncrhonization"),
LUT_ENTRY_ONE_PIF(0x11, 0x20, 0x00, "Management Card"),
LUT_ENTRY_ONE_PIF(0x11, 0x80, 0x00, "Other Data Acquisition/Signal Processing Controller"),
};
#undef LUT_ENTRY
#undef LUT_ENTRY_ONE_PIF
#undef LUT_ENTRY_ALL_PIF
static const char* pci_class_code_to_string(uint8_t class_code) {
switch (class_code) {
case 0x00:
return "Pre-Class Code Device";
case 0x01:
return "Mass Storage Controller";
case 0x02:
return "Network Controller";
case 0x03:
return "Display Controller";
case 0x04:
return "Multimedia Controller";
case 0x05:
return "Memory Controller";
case 0x06:
return "Bridge Device";
case 0x07:
return "Simple Communication Controller";
case 0x08:
return "Base System Peripheral";
case 0x09:
return "Input Device";
case 0x0A:
return "Docking Station";
case 0x0B:
return "Processor";
case 0x0C:
return "Serial Bus Controller";
case 0x0D:
return "Wireless Controller";
case 0x0E:
return "Intelligent I/O Controller";
case 0x0F:
return "Satellite Communication Controller";
case 0x10:
return "Encryption/Decryption Controller";
case 0x11:
return "Data Acquisition or Signal Processing Controller";
case 0xFF:
return "Vendor";
default:
return "<Unknown>";
}
}
static const char* pci_device_type(const PcieDevice& dev) {
// TODO(johngro): It might be a good idea, some day, to make this something
// better than an O(n) search.
// If this is a PCIe style bridge with a specific device type spelled out in
// its PCI Express Capabilities structure, use that to provide the type
// string.
switch (dev.pcie_device_type()) {
case PCIE_DEVTYPE_RC_ROOT_PORT:
return "PCIe Root Port";
case PCIE_DEVTYPE_SWITCH_UPSTREAM_PORT:
return "PCIe Upstream Switch Port";
case PCIE_DEVTYPE_SWITCH_DOWNSTREAM_PORT:
return "PCIe Downstream Switch Port";
case PCIE_DEVTYPE_PCIE_TO_PCI_BRIDGE:
return "PCIe-to-PCI Bridge";
case PCIE_DEVTYPE_PCI_TO_PCIE_BRIDGE:
return "PCI-to-PCIe Bridge";
default:
break;
}
for (size_t i = 0; i < ktl::size(PCI_DEV_TYPE_LUT); ++i) {
const pci_dev_type_lut_entry_t* entry = PCI_DEV_TYPE_LUT + i;
if ((dev.class_id() == entry->class_code) && (dev.subclass() == entry->subclass) &&
(dev.prog_if() >= entry->prof_if_start) && (dev.prog_if() <= entry->prof_if_end))
return entry->desc;
}
return pci_class_code_to_string(dev.class_id());
}
static void do_lspci_indent(uint level) {
while (level--)
printf(" ");
}
#define LSPCI_PRINTF(_fmt, ...) \
do { \
do_lspci_indent(params->indent_level); \
printf(_fmt, ##__VA_ARGS__); \
} while (0)
static void dump_pcie_hdr(const PcieDevice& dev, lspci_params_t* params) {
DEBUG_ASSERT(params);
LSPCI_PRINTF("[%02x:%02x.%01x] - VID 0x%04x DID 0x%04x :: %s", dev.bus_id(), dev.dev_id(),
dev.func_id(), dev.vendor_id(), dev.device_id(), pci_device_type(dev));
if (dev.disabled())
printf(" [DISABLED]");
printf("\n");
}
static void dump_pcie_bars(const PcieDevice& dev, lspci_params_t* params) {
auto cfg = dev.config();
DEBUG_ASSERT(dev.bar_count() <= PCIE_MAX_BAR_REGS);
for (uint i = 0; i < dev.bar_count(); ++i) {
LSPCI_PRINTF("Base Addr[%u] : 0x%08x", i, cfg->Read(PciConfig::kBAR(i)));
const pcie_bar_info_t* info = dev.GetBarInfo(i);
if (info == nullptr) {
printf("\n");
continue;
}
printf(" :: paddr %#" PRIx64 " size %#" PRIx64 "%s%s %s%s\n", info->bus_addr, info->size,
info->is_prefetchable ? " prefetchable" : "",
info->is_mmio ? (info->is_64bit ? " 64-bit" : " 32-bit") : "",
info->is_mmio ? "MMIO" : "PIO", info->allocation == nullptr ? "" : " (allocated)");
}
}
static void dump_pcie_common(const PcieDevice& dev, lspci_params_t* params) {
auto cfg = dev.config();
uint8_t base_class = cfg->Read(PciConfig::kBaseClass);
LSPCI_PRINTF("Command : 0x%04x\n", cfg->Read(PciConfig::kCommand));
LSPCI_PRINTF("Status : 0x%04x\n", cfg->Read(PciConfig::kStatus));
LSPCI_PRINTF("Rev ID : 0x%02x\n", cfg->Read(PciConfig::kRevisionId));
LSPCI_PRINTF("Prog Iface : 0x%02x\n", cfg->Read(PciConfig::kProgramInterface));
LSPCI_PRINTF("Sub Class : 0x%02x\n", cfg->Read(PciConfig::kSubClass));
LSPCI_PRINTF("Base Class : 0x%02x %s\n", base_class, pci_class_code_to_string(base_class));
LSPCI_PRINTF("Cache Line Sz : 0x%02x\n", cfg->Read(PciConfig::kCacheLineSize));
LSPCI_PRINTF("Latency Timer : 0x%02x\n", cfg->Read(PciConfig::kLatencyTimer));
LSPCI_PRINTF("Header Type : 0x%02x\n", cfg->Read(PciConfig::kHeaderType));
LSPCI_PRINTF("BIST : 0x%02x\n", cfg->Read(PciConfig::kBist));
}
static void dump_pcie_standard(const PcieDevice& dev, lspci_params_t* params) {
auto cfg = dev.config();
LSPCI_PRINTF("Cardbus CIS : 0x%08x\n", cfg->Read(PciConfig::kCardbusCisPtr));
LSPCI_PRINTF("Subsystem VID : 0x%04x\n", cfg->Read(PciConfig::kSubsystemVendorId));
LSPCI_PRINTF("Subsystem ID : 0x%04x\n", cfg->Read(PciConfig::kSubsystemId));
LSPCI_PRINTF("Exp ROM addr : 0x%08x\n", cfg->Read(PciConfig::kExpansionRomAddress));
LSPCI_PRINTF("Cap Ptr : 0x%02x\n", cfg->Read(PciConfig::kCapabilitiesPtr));
LSPCI_PRINTF("IRQ line : 0x%02x\n", cfg->Read(PciConfig::kInterruptLine));
LSPCI_PRINTF("IRQ pin : 0x%02x\n", cfg->Read(PciConfig::kInterruptPin));
LSPCI_PRINTF("Min Grant : 0x%02x\n", cfg->Read(PciConfig::kMinGrant));
LSPCI_PRINTF("Max Latency : 0x%02x\n", cfg->Read(PciConfig::kMaxLatency));
}
static void dump_pcie_bridge(const PcieBridge& bridge, lspci_params_t* params) {
auto cfg = bridge.config();
LSPCI_PRINTF("P. Bus ID : 0x%02x\n", cfg->Read(PciConfig::kPrimaryBusId));
LSPCI_PRINTF("S. Bus Range : [0x%02x, 0x%02x]\n", cfg->Read(PciConfig::kSecondaryBusId),
cfg->Read(PciConfig::kSubordinateBusId));
LSPCI_PRINTF("S. Latency Timer : 0x%02x\n", cfg->Read(PciConfig::kSecondaryLatencyTimer));
LSPCI_PRINTF("IO Base : 0x%02x\n", cfg->Read(PciConfig::kIoBase));
LSPCI_PRINTF("IO Base Upper : 0x%04x\n", cfg->Read(PciConfig::kIoBaseUpper));
LSPCI_PRINTF("IO Limit : 0x%02x\n", cfg->Read(PciConfig::kIoLimit));
LSPCI_PRINTF("IO Limit Upper : 0x%04x", cfg->Read(PciConfig::kIoLimitUpper));
if (bridge.io_base() < bridge.io_limit()) {
printf(" :: [0x%08x, 0x%08x]\n", bridge.io_base(), bridge.io_limit());
} else {
printf("\n");
}
LSPCI_PRINTF("Secondary Status : 0x%04x\n", cfg->Read(PciConfig::kSecondaryStatus));
LSPCI_PRINTF("Memory Limit : 0x%04x\n", cfg->Read(PciConfig::kMemoryLimit));
LSPCI_PRINTF("Memory Base : 0x%04x", cfg->Read(PciConfig::kMemoryBase));
if (bridge.mem_base() < bridge.mem_limit()) {
printf(" :: [0x%08x, 0x%08x]\n", bridge.mem_base(), bridge.mem_limit());
} else {
printf("\n");
}
LSPCI_PRINTF("PFMem Base : 0x%04x\n", cfg->Read(PciConfig::kPrefetchableMemoryBase));
LSPCI_PRINTF("PFMem Base Upper : 0x%08x\n", cfg->Read(PciConfig::kPrefetchableMemoryBaseUpper));
LSPCI_PRINTF("PFMem Limit : 0x%04x\n", cfg->Read(PciConfig::kPrefetchableMemoryLimit));
LSPCI_PRINTF("PFMem Limit Upper : 0x%08x", cfg->Read(PciConfig::kPrefetchableMemoryLimitUpper));
if (bridge.pf_mem_base() < bridge.pf_mem_limit()) {
printf(" :: [0x%016" PRIx64 ", 0x%016" PRIx64 "]\n", bridge.pf_mem_base(),
bridge.pf_mem_limit());
} else {
printf("\n");
}
LSPCI_PRINTF("Capabilities Ptr : 0x%02x\n", cfg->Read(PciConfig::kCapabilitiesPtr));
LSPCI_PRINTF("Exp ROM Address : 0x%08x\n", cfg->Read(PciConfig::kExpansionRomAddress));
LSPCI_PRINTF("Interrupt Line : 0x%02x\n", cfg->Read(PciConfig::kInterruptLine));
LSPCI_PRINTF("Interrupt Pin : 0x%02x\n", cfg->Read(PciConfig::kInterruptPin));
LSPCI_PRINTF("Bridge Control : 0x%04x\n", cfg->Read(PciConfig::kBridgeControl));
}
static void dump_pcie_raw_config(uint amt, const PciConfig* cfg) {
DEBUG_ASSERT(amt == PCIE_BASE_CONFIG_SIZE || amt == PCIE_EXTENDED_CONFIG_SIZE);
cfg->DumpConfig(static_cast<uint16_t>(amt & 0xFFFF));
}
#define CAP_TBL_ENTRY(s) (s, #s)
static struct _cap_tbl {
uint8_t id;
const char* label;
} cap_tbl[] = {
{PCIE_CAP_ID_PCI_PWR_MGMT, "PCI_PWR_MGMT"},
{PCIE_CAP_ID_AGP, "AGP"},
{PCIE_CAP_ID_VPD, "VPD"},
{PCIE_CAP_ID_MSI, "MSI"},
{PCIE_CAP_ID_PCIX, "PCIX"},
{PCIE_CAP_ID_HYPERTRANSPORT, "HYPERTRANSPORT"},
{PCIE_CAP_ID_VENDOR, "VENDOR"},
{PCIE_CAP_ID_DEBUG_PORT, "DEBUG_PORT"},
{PCIE_CAP_ID_COMPACTPCI_CRC, "COMPACTPCI_CRC"},
{PCIE_CAP_ID_PCI_HOTPLUG, "PCI_HOTPLUG"},
{PCIE_CAP_ID_PCI_BRIDGE_SUBSYSTEM_VID, "PCI_BRIDGE_SUBSYSTEM_VID"},
{PCIE_CAP_ID_AGP_8X, "AGP_8X"},
{PCIE_CAP_ID_SECURE_DEVICE, "SECURE_DEVICE"},
{PCIE_CAP_ID_PCI_EXPRESS, "PCI_EXPRESS"},
{PCIE_CAP_ID_MSIX, "MSIX"},
{PCIE_CAP_ID_SATA_DATA_NDX_CFG, "SATA_DATA_NDX_CFG"},
{PCIE_CAP_ID_ADVANCED_FEATURES, "ADVANCED_FEATURES"},
{PCIE_CAP_ID_ENHANCED_ALLOCATION, "ENHANCED_ALLOCATION"},
};
#undef CAP_TABLE_ENTRY
static inline const char* get_cap_str(uint8_t id) {
for (const auto& cur : cap_tbl) {
if (cur.id == id) {
return cur.label;
}
}
return "<Unknown>";
}
static void dump_pcie_capabilities(fbl::RefPtr<PcieDevice> dev, void* ctx) {
bool is_first = true;
lspci_params_t* params = static_cast<lspci_params_t*>(ctx);
auto initial_indent = params->indent_level;
params->indent_level += 2;
if (!dev->capabilities().is_empty()) {
LSPCI_PRINTF("Std Capabilities :");
for (const auto& cap : dev->capabilities()) {
if (is_first) {
printf(" %s (%#02x)\n", get_cap_str(cap.id()), cap.id());
is_first = false;
params->indent_level += 10;
} else {
LSPCI_PRINTF("%s (%#02x)\n", get_cap_str(cap.id()), cap.id());
}
}
}
params->indent_level = initial_indent;
}
static bool dump_pcie_device(const fbl::RefPtr<PcieDevice>& dev, void* ctx, uint level) {
DEBUG_ASSERT(dev && ctx);
lspci_params_t* params = (lspci_params_t*)ctx;
bool match;
auto cfg = dev->config();
// Caching IRQ information up front because these methods are designed to be called from the
// protocol outside of the device lock.
struct irq_info_t {
zx_status_t status;
pcie_irq_mode_caps_t caps;
const char* label;
} irq_info[2] = {};
irq_info[0].label = IRQ_MODE_LABELS[PCIE_IRQ_MODE_LEGACY];
irq_info[0].status = dev->QueryIrqModeCapabilities(PCIE_IRQ_MODE_LEGACY, &irq_info[0].caps);
irq_info[1].label = IRQ_MODE_LABELS[PCIE_IRQ_MODE_MSI];
irq_info[1].status = dev->QueryIrqModeCapabilities(PCIE_IRQ_MODE_MSI, &irq_info[1].caps);
/* Grab the device's lock so it cannot be unplugged out from under us while
* we print details. */
Guard<Mutex> guard{dev->dev_lock()};
/* If the device has already been unplugged, just skip it */
if (!dev->plugged_in())
return true;
match = (((params->bus_id == WILDCARD_ID) || (params->bus_id == dev->bus_id())) &&
((params->dev_id == WILDCARD_ID) || (params->dev_id == dev->dev_id())) &&
((params->func_id == WILDCARD_ID) || (params->func_id == dev->func_id())));
if (!match)
return true;
if (!params->found && (params->bus_id != WILDCARD_ID)) {
params->base_level = level;
} else {
DEBUG_ASSERT(!params->base_level);
}
params->found++;
DEBUG_ASSERT(level >= params->base_level);
params->indent_level = params->verbose ? 0 : level - params->base_level;
/* Dump the header */
dump_pcie_hdr(*dev, params);
/* Only dump details if we are in verbose mode and this device matches our
* filter */
if (params->verbose) {
params->indent_level += 2;
dump_pcie_common(*dev, params);
dump_pcie_bars(*dev, params);
uint8_t header_type = cfg->Read(PciConfig::kHeaderType) & PCI_HEADER_TYPE_MASK;
switch (header_type) {
case PCI_HEADER_TYPE_STANDARD:
dump_pcie_standard(*dev, params);
break;
case PCI_HEADER_TYPE_PCI_BRIDGE: {
if (dev->is_bridge()) {
dump_pcie_bridge(*static_cast<PcieBridge*>(dev.get()), params);
} else {
printf("ERROR! Type 1 header detected for non-bridge device!\n");
}
} break;
case PCI_HEADER_TYPE_CARD_BUS:
printf("TODO : Implemnt CardBus Config header register dump\n");
break;
default:
printf("Unknown Header Type (0x%02x)\n", header_type);
break;
}
params->indent_level -= 2;
dump_pcie_capabilities(dev, params);
}
if (params->cfg_dump_ints) {
params->indent_level++;
LSPCI_PRINTF("IRQ Information:\n");
params->indent_level++;
LSPCI_PRINTF("Current mode: %s\n", IRQ_MODE_LABELS[dev->irq_mode()]);
LSPCI_PRINTF("Supported modes:\n");
params->indent_level++;
for (auto irq : irq_info) {
if (irq.status == ZX_OK) {
LSPCI_PRINTF("%s (max_irqs = %u, pvm = %s)\n", irq.label, irq.caps.max_irqs,
(irq.caps.per_vector_masking_supported) ? "true" : "false");
}
}
params->indent_level -= 3;
}
if (params->cfg_dump_amt) {
dump_pcie_raw_config(params->cfg_dump_amt, dev->config());
}
return true;
}
int PcieDebugConsole::CmdLsPci(int argc, const cmd_args* argv, uint32_t flags) {
lspci_params_t params;
uint filter_ndx = 0;
memset(&params, 0, sizeof(params));
params.bus_id = WILDCARD_ID;
params.dev_id = WILDCARD_ID;
params.func_id = WILDCARD_ID;
for (int i = 1; i < argc; ++i) {
bool confused = false;
if (argv[i].str[0] == '-') {
const char* c = argv[i].str + 1;
if (!(*c))
confused = true;
while (!confused && *c) {
switch (*c) {
case 'f':
if (params.cfg_dump_amt < PCIE_BASE_CONFIG_SIZE)
params.cfg_dump_amt = PCIE_BASE_CONFIG_SIZE;
params.force_dump_cfg = true;
break;
case 'i':
params.cfg_dump_ints = true;
break;
case 'e':
if (params.cfg_dump_amt < PCIE_EXTENDED_CONFIG_SIZE)
params.cfg_dump_amt = PCIE_EXTENDED_CONFIG_SIZE;
__FALLTHROUGH;
case 'c':
if (params.cfg_dump_amt < PCIE_BASE_CONFIG_SIZE)
params.cfg_dump_amt = PCIE_BASE_CONFIG_SIZE;
__FALLTHROUGH;
case 'l':
params.verbose = true;
break;
default:
confused = true;
break;
}
c++;
}
} else {
switch (filter_ndx) {
case 0:
params.bus_id = static_cast<uint>(argv[i].i);
if (params.bus_id >= PCIE_MAX_BUSSES)
confused = true;
break;
case 1:
params.dev_id = static_cast<uint>(argv[i].i);
if (params.dev_id >= PCIE_MAX_DEVICES_PER_BUS)
confused = true;
break;
case 2:
params.func_id = static_cast<uint>(argv[i].i);
if (params.func_id >= PCIE_MAX_FUNCTIONS_PER_DEVICE)
confused = true;
break;
default:
confused = true;
break;
}
filter_ndx++;
}
if (confused) {
printf("usage: %s [-t] [-l] [<bus_id>] [<dev_id>] [<func_id>]\n", argv[0].str);
printf(" -l : Be verbose when dumping info about discovered devices.\n");
printf(" -c : Dump raw standard config (implies -l)\n");
printf(" -e : Dump raw extended config (implies -l -c)\n");
printf(" -i : Dump interrupt information\n");
printf(
" -f : Force dump at least standard config, even if the device didn't "
"enumerate (requires a full BDF address)\n");
return ZX_OK;
}
}
auto bus_drv = PcieBusDriver::GetDriver();
if (bus_drv == nullptr) {
printf("PCIe driver is not initialized\n");
return ZX_ERR_BAD_STATE;
}
bus_drv->ForeachDevice(dump_pcie_device, &params);
if (!params.found && params.force_dump_cfg && (params.bus_id != WILDCARD_ID) &&
(params.dev_id != WILDCARD_ID) && (params.func_id != WILDCARD_ID)) {
const PciConfig* cfg;
cfg = bus_drv->GetConfig(params.bus_id, params.dev_id, params.func_id);
if (!cfg) {
printf("Config space for %02x:%02x.%01x not mapped by bus driver!\n", params.bus_id,
params.dev_id, params.func_id);
} else {
dump_pcie_raw_config(params.cfg_dump_amt, cfg);
}
} else {
printf("PCIe scan discovered %u device%s\n", params.found, (params.found == 1) ? "" : "s");
}
return ZX_OK;
}
int PcieDebugConsole::CmdPciUnplug(int argc, const cmd_args* argv, uint32_t flags) {
bool confused = false;
uint bus_id, dev_id, func_id;
if (argc == 4) {
bus_id = static_cast<uint>(argv[1].i);
dev_id = static_cast<uint>(argv[2].i);
func_id = static_cast<uint>(argv[3].i);
if ((bus_id >= PCIE_MAX_BUSSES) || (dev_id >= PCIE_MAX_DEVICES_PER_BUS) ||
(func_id >= PCIE_MAX_FUNCTIONS_PER_DEVICE))
confused = true;
} else {
confused = true;
}
if (confused) {
printf("usage: %s <bus_id> <dev_id> <func_id>\n", argv[0].str);
return ZX_OK;
}
auto bus_drv = PcieBusDriver::GetDriver();
if (bus_drv == nullptr) {
printf("PCIe driver is not initialized\n");
return ZX_ERR_BAD_STATE;
}
fbl::RefPtr<PcieDevice> dev = bus_drv->GetRefedDevice(bus_id, dev_id, func_id);
if (!dev) {
printf("Failed to find PCI device %02x:%02x.%01x\n", bus_id, dev_id, func_id);
} else {
printf("Unplugging PCI device %02x:%02x.%x...\n", bus_id, dev_id, func_id);
dev->Unplug();
dev = nullptr;
printf("done\n");
}
return ZX_OK;
}
int PcieDebugConsole::CmdPciReset(int argc, const cmd_args* argv, uint32_t flags) {
bool confused = false;
uint bus_id, dev_id, func_id;
if (argc == 4) {
bus_id = static_cast<uint>(argv[1].i);
dev_id = static_cast<uint>(argv[2].i);
func_id = static_cast<uint>(argv[3].i);
if ((bus_id >= PCIE_MAX_BUSSES) || (dev_id >= PCIE_MAX_DEVICES_PER_BUS) ||
(func_id >= PCIE_MAX_FUNCTIONS_PER_DEVICE))
confused = true;
} else {
confused = true;
}
if (confused) {
printf("usage: %s <bus_id> <dev_id> <func_id>\n", argv[0].str);
return ZX_OK;
}
auto bus_drv = PcieBusDriver::GetDriver();
if (bus_drv == nullptr) {
printf("PCIe driver is not initialized\n");
return ZX_ERR_BAD_STATE;
}
fbl::RefPtr<PcieDevice> dev = bus_drv->GetRefedDevice(bus_id, dev_id, func_id);
if (!dev) {
printf("Failed to find PCI device %02x:%02x.%01x\n", bus_id, dev_id, func_id);
} else {
printf("Attempting reset of device %02x:%02x.%01x...\n", bus_id, dev_id, func_id);
zx_status_t res = dev->DoFunctionLevelReset();
dev = nullptr;
if (res != ZX_OK)
printf("Reset attempt failed (res = %d).\n", res);
else
printf("Success, device %02x:%02x.%01x has been reset.\n", bus_id, dev_id, func_id);
}
return ZX_OK;
}
int PcieDebugConsole::CmdPciRescan(int argc, const cmd_args* argv, uint32_t flags) {
auto bus_drv = PcieBusDriver::GetDriver();
if (bus_drv == nullptr) {
printf("PCIe driver is not initialized\n");
return ZX_ERR_BAD_STATE;
}
return bus_drv->RescanDevices();
}
int PcieDebugConsole::CmdPciRegionDump(int argc, const cmd_args* argv, uint32_t flags) {
auto walk_cb = [](const ralloc_region_t* r) -> bool {
printf("\tregion { base = %" PRIxPTR ", size = %#lx }\n", r->base, r->size);
return true;
};
auto bus_drv = PcieBusDriver::GetDriver();
if (bus_drv == nullptr) {
printf("PCIe driver is not initialized\n");
return ZX_ERR_BAD_STATE;
}
printf("mmio_low:\n");
bus_drv->mmio_lo_regions_.WalkAllocatedRegions(walk_cb);
printf("mmio_high:\n");
bus_drv->mmio_hi_regions_.WalkAllocatedRegions(walk_cb);
printf("pio:\n");
bus_drv->pio_regions_.WalkAllocatedRegions(walk_cb);
return ZX_OK;
}
STATIC_COMMAND_START
STATIC_COMMAND("lspci", "Enumerate the devices detected in PCIe ECAM space",
&PcieDebugConsole::CmdLsPci)
STATIC_COMMAND("pciunplug", "Force \"unplug\" the specified PCIe device",
&PcieDebugConsole::CmdPciUnplug)
STATIC_COMMAND("pcireset", "Initiate a Function Level Reset of the specified device.",
&PcieDebugConsole::CmdPciReset)
STATIC_COMMAND("pcirescan",
"Force a rescan of the PCIe configuration space, matching drivers to unclaimed "
"devices as we go. Then attempt to start all newly claimed devices.",
&PcieDebugConsole::CmdPciRescan)
STATIC_COMMAND("pciregions", "Dump information on present PCI address region allocations",
&PcieDebugConsole::CmdPciRegionDump)
STATIC_COMMAND_END(pcie)