@@ -69,7 +69,7 @@ force:
lib/config.h lib/config.mk:
cd lib && ./configure
-lspci: lspci.o ls-vpd.o ls-caps.o ls-caps-vendor.o ls-ecaps.o ls-kernel.o ls-tree.o ls-map.o common.o lib/$(PCILIB)
+lspci: lspci.o ls-vpd.o ls-caps.o ls-caps-vendor.o ls-ecaps.o ls-kernel.o ls-tree.o ls-map.o ls-info.o common.o lib/$(PCILIB)
setpci: setpci.o common.o lib/$(PCILIB)
LSPCIINC=lspci.h pciutils.h $(PCIINC)
@@ -80,6 +80,7 @@ ls-ecaps.o: ls-ecaps.c $(LSPCIINC)
ls-kernel.o: ls-kernel.c $(LSPCIINC)
ls-tree.o: ls-tree.c $(LSPCIINC)
ls-map.o: ls-map.c $(LSPCIINC)
+ls-info.o: ls-info.c $(LSPCIINC)
setpci.o: setpci.c pciutils.h $(PCIINC)
common.o: common.c pciutils.h $(PCIINC)
@@ -302,6 +302,25 @@ show_kernel_machine(struct device *d)
printf("Module:\t%s\n", module);
}
+void
+fill_info_kernel(struct info_obj *dev_obj, struct device *d)
+{
+ char buf[DRIVER_BUF_SIZE];
+ const char *driver, *module;
+
+ if (driver = find_driver(d, buf))
+ info_obj_add_str(dev_obj, "Driver", driver);
+
+ if (!show_kernel_init())
+ return;
+
+ struct info_list *mod_list = info_list_create(INFO_VAL_STRING);
+ while (module = next_module_filtered(d))
+ info_list_add_str(mod_list, module);
+
+ info_obj_add_list(dev_obj, "Modules", mod_list);
+}
+
#else
void
@@ -319,5 +338,10 @@ show_kernel_cleanup(void)
{
}
+void
+fill_info_kernel(struct info_obj *dev_obj UNUSED, struct device *d UNUSED)
+{
+}
+
#endif
@@ -25,11 +25,12 @@ static int opt_domains; /* Show domain numbers (0=disabled, 1=auto-detected, 2
static int opt_kernel; /* Show kernel drivers */
static int opt_query_dns; /* Query the DNS (0=disabled, 1=enabled, 2=refresh cache) */
static int opt_query_all; /* Query the DNS for all entries */
+static int opt_json;
char *opt_pcimap; /* Override path to Linux modules.pcimap */
const char program_name[] = "lspci";
-static char options[] = "nvbxs:d:ti:mgp:qkMDQ" GENERIC_OPTIONS ;
+static char options[] = "nvbxs:d:ti:mgp:qkJMDQ" GENERIC_OPTIONS ;
static char help_msg[] =
"Usage: lspci [<switches>]\n"
@@ -48,6 +49,7 @@ static char help_msg[] =
"-xxxx\t\tShow hex-dump of the 4096-byte extended config space (root only)\n"
"-b\t\tBus-centric view (addresses and IRQ's as seen by the bus)\n"
"-D\t\tAlways show domain numbers\n"
+"-J\t\tUse JSON output format\n"
"\n"
"Resolving of device ID's to names:\n"
"-n\t\tShow numeric ID's\n"
@@ -257,6 +259,16 @@ show_slot_name(struct device *d)
printf("%02x:%02x.%d", p->bus, p->dev, p->func);
}
+static void
+fill_slot_name(struct device *d, char *buf, size_t size)
+{
+ struct pci_dev *p = d->dev;
+
+ if (!opt_machine ? opt_domains : (p->domain || opt_domains >= 2))
+ snprintf(buf, size, "%04x:", p->domain);
+ snprintf(buf, size, "%02x:%02x.%d", p->bus, p->dev, p->func);
+}
+
void
get_subid(struct device *d, word *subvp, word *subdp)
{
@@ -343,6 +355,24 @@ show_size(u64 x)
printf(" [size=%u%s]", (unsigned)x, suffix[i]);
}
+static void
+fill_size(char *buf, size_t size, u64 x)
+{
+ static const char suffix[][2] = { "", "K", "M", "G", "T" };
+ unsigned i;
+ if (!x)
+ {
+ snprintf(buf, size, "0");
+ return;
+ }
+ for (i = 0; i < (sizeof(suffix) / sizeof(*suffix) - 1); i++) {
+ if (x % 1024)
+ break;
+ x /= 1024;
+ }
+ snprintf(buf, size, "%u%s", (unsigned)x, suffix[i]);
+}
+
static void
show_range(char *prefix, u64 base, u64 limit, int is_64bit)
{
@@ -369,6 +399,21 @@ show_range(char *prefix, u64 base, u64 limit, int is_64bit)
putchar('\n');
}
+static void
+fill_range(char *buf, size_t size, u64 base, u64 limit, int is_64bit)
+{
+ if (base > limit)
+ {
+ snprintf(buf, size, "None");
+ return;
+ }
+
+ if (is_64bit)
+ snprintf(buf, size, "%016" PCI_U64_FMT_X "-%016" PCI_U64_FMT_X, base, limit);
+ else
+ snprintf(buf, size, "%08x-%08x", (unsigned) base, (unsigned) limit);
+}
+
static void
show_bases(struct device *d, int cnt)
{
@@ -840,6 +885,30 @@ show_hex_dump(struct device *d)
}
}
+static void
+fill_info_hex_dump(struct info_obj *dev_obj, struct device *d)
+{
+ unsigned int i, cnt;
+ char buf[3] = {0};
+ struct info_list *hex_dump_list = info_list_create(INFO_VAL_STRING);
+
+ cnt = d->config_cached;
+ if (opt_hex >= 3 && config_fetch(d, cnt, 256-cnt))
+ {
+ cnt = 256;
+ if (opt_hex >= 4 && config_fetch(d, 256, 4096-256))
+ cnt = 4096;
+ }
+
+ for (i=0; i<cnt; i++)
+ {
+ snprintf(buf, sizeof(buf), "%02x", get_conf_byte(d, i));
+ info_list_add_str(hex_dump_list, buf);
+ }
+
+ info_obj_add_list(dev_obj, "hexdump", hex_dump_list);
+}
+
static void
print_shell_escaped(char *c)
{
@@ -945,6 +1014,631 @@ show(void)
show_device(d);
}
+static void
+fill_info_machine(struct info_obj *dev_obj, struct device *d)
+{
+ struct pci_dev *p = d->dev;
+ int c;
+ word sv_id, sd_id;
+ char buf[128];
+
+ get_subid(d, &sv_id, &sd_id);
+
+ fill_slot_name(d, buf, sizeof(buf));
+ info_obj_add_str(dev_obj, "Slot", buf);
+
+ pci_lookup_name(pacc, buf, sizeof(buf), PCI_LOOKUP_CLASS, p->device_class);
+ info_obj_add_str(dev_obj, "Class", buf);
+ pci_lookup_name(pacc, buf, sizeof(buf), PCI_LOOKUP_VENDOR, p->vendor_id, p->device_id);
+ info_obj_add_str(dev_obj, "Vendor", buf);
+ pci_lookup_name(pacc, buf, sizeof(buf), PCI_LOOKUP_DEVICE, p->vendor_id, p->device_id);
+ info_obj_add_str(dev_obj, "Device", buf);
+
+ if (sv_id && sv_id != 0xffff)
+ {
+ pci_lookup_name(pacc, buf, sizeof(buf), PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR, sv_id);
+ info_obj_add_str(dev_obj, "SVendor", buf);
+ pci_lookup_name(pacc, buf, sizeof(buf), PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_DEVICE, p->vendor_id, p->device_id, sv_id, sd_id);
+ info_obj_add_str(dev_obj, "SDevice", buf);
+ }
+ else if (!verbose)
+ {
+ info_obj_add_str(dev_obj, "SVendor", "");
+ info_obj_add_str(dev_obj, "SDevice", "");
+ }
+
+ if (c = get_conf_byte(d, PCI_REVISION_ID))
+ {
+ snprintf(buf, sizeof(buf), "%02x", c);
+ info_obj_add_str(dev_obj, "Rev", buf);
+ }
+ if (c = get_conf_byte(d, PCI_CLASS_PROG))
+ {
+ snprintf(buf, sizeof(buf), "%02x", c);
+ info_obj_add_str(dev_obj, "ProgIf", buf);
+ }
+
+ if (opt_kernel)
+ fill_info_kernel(dev_obj, d);
+
+ if (verbose)
+ {
+ pci_fill_info(p, PCI_FILL_PHYS_SLOT | PCI_FILL_NUMA_NODE);
+ if (p->phy_slot)
+ info_obj_add_str(dev_obj, "PhySlot", p->phy_slot);
+ if (p->numa_node != -1)
+ {
+ snprintf(buf, sizeof(buf), "%d", p->numa_node);
+ info_obj_add_str(dev_obj, "NUMAnode", buf);
+ }
+ }
+}
+
+static void
+fill_info_terse(struct info_obj *dev_obj, struct device *d)
+{
+ struct pci_dev *p = d->dev;
+
+ fill_info_machine(dev_obj, d);
+
+ if (verbose || opt_kernel)
+ {
+ word subsys_v, subsys_d;
+ char ssnamebuf[256];
+
+ if (p->label)
+ info_obj_add_str(dev_obj, "DeviceName", p->label);
+ info_obj_delete_pair(dev_obj, "SDevice");
+ info_obj_delete_pair(dev_obj, "SVendor");
+ get_subid(d, &subsys_v, &subsys_d);
+ if (subsys_v && subsys_v != 0xffff)
+ info_obj_add_str(dev_obj, "Subsystem",
+ pci_lookup_name(pacc, ssnamebuf, sizeof(ssnamebuf),
+ PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
+ p->vendor_id, p->device_id, subsys_v, subsys_d));
+ }
+}
+
+static void
+fill_info_rom(struct info_obj *dev_obj, struct device *d, int reg)
+{
+ struct pci_dev *p = d->dev;
+ pciaddr_t rom = p->rom_base_addr;
+ pciaddr_t len = (p->known_fields & PCI_FILL_SIZES) ? p->rom_size : 0;
+ pciaddr_t ioflg = (p->known_fields & PCI_FILL_IO_FLAGS) ? p->rom_flags : 0;
+ u32 flg = get_conf_long(d, reg);
+ word cmd = get_conf_word(d, PCI_COMMAND);
+ int virtual = 0;
+ char buf[64];
+ struct info_obj *rom_obj;
+ struct info_list *attrs_list;
+
+ if (!rom && !flg && !len)
+ return;
+
+ rom_obj = info_obj_create_in_obj(dev_obj, "ROM");
+ attrs_list = info_list_create(INFO_VAL_STRING);
+ info_obj_add_list(rom_obj, "attrs", attrs_list);
+
+ if (ioflg & PCI_IORESOURCE_PCI_EA_BEI)
+ info_list_add_str(attrs_list, "[enhanced]");
+ else if ((rom & PCI_ROM_ADDRESS_MASK) && !(flg & PCI_ROM_ADDRESS_MASK))
+ {
+ info_list_add_str(attrs_list, "[virtual]");
+ flg = rom;
+ virtual = 1;
+ }
+
+ if (rom & PCI_ROM_ADDRESS_MASK)
+ info_obj_add_fmt_buf_str(rom_obj, "at", buf, sizeof(buf), PCIADDR_T_FMT, rom & PCI_ROM_ADDRESS_MASK);
+ else if (flg & PCI_ROM_ADDRESS_MASK)
+ info_list_add_str(attrs_list, "<ignored>");
+ else
+ info_list_add_str(attrs_list, "<unassigned>");
+
+ if (!(flg & PCI_ROM_ADDRESS_ENABLE))
+ info_list_add_str(attrs_list, "[disabled]");
+ else if (!virtual && !(cmd & PCI_COMMAND_MEMORY))
+ info_list_add_str(attrs_list, "[disabled by cmd]");
+
+ fill_size(buf, sizeof(buf), len);
+ info_obj_add_str(rom_obj, "size", buf);
+}
+
+static void
+fill_info_bases(struct info_obj *dev_obj, struct device *d, int cnt)
+{
+ struct pci_dev *p = d->dev;
+ word cmd = get_conf_word(d, PCI_COMMAND);
+ int i;
+ int virtual = 0;
+ char buf[64];
+ struct info_obj *bases_obj = info_obj_create_in_obj(dev_obj, "bases");
+ struct info_list *regions_list = info_list_create(INFO_VAL_OBJECT);
+
+ info_obj_add_list(bases_obj, "regions", regions_list);
+
+ for (i=0; i<cnt; i++)
+ {
+ struct info_obj *region_obj;
+ struct info_list *attrs_list;
+
+ pciaddr_t pos = p->base_addr[i];
+ pciaddr_t len = (p->known_fields & PCI_FILL_SIZES) ? p->size[i] : 0;
+ pciaddr_t ioflg = (p->known_fields & PCI_FILL_IO_FLAGS) ? p->flags[i] : 0;
+ u32 flg = get_conf_long(d, PCI_BASE_ADDRESS_0 + 4*i);
+ if (flg == 0xffffffff)
+ flg = 0;
+ if (!pos && !flg && !len)
+ continue;
+
+ region_obj = info_obj_create();
+ info_list_add_obj(regions_list, region_obj);
+ attrs_list = info_list_create(INFO_VAL_STRING);
+ info_obj_add_list(region_obj, "attrs", attrs_list);
+
+ if (ioflg & PCI_IORESOURCE_PCI_EA_BEI)
+ info_list_add_str(attrs_list, "[enhanced]");
+ else if (pos && !flg) /* Reported by the OS, but not by the device */
+ {
+ info_list_add_str(attrs_list, "[virtual]");
+ flg = pos;
+ virtual = 1;
+ }
+ if (flg & PCI_BASE_ADDRESS_SPACE_IO)
+ {
+ pciaddr_t a = pos & PCI_BASE_ADDRESS_IO_MASK;
+ if (a || (cmd & PCI_COMMAND_IO))
+ info_obj_add_fmt_buf_str(region_obj, "io-ports-at", buf, sizeof(buf), PCIADDR_PORT_FMT, a);
+ else if (flg & PCI_BASE_ADDRESS_IO_MASK)
+ info_list_add_str(attrs_list, "<ignored>");
+ else
+ info_list_add_str(attrs_list, "<unassigned>");
+ if (!virtual && !(cmd & PCI_COMMAND_IO))
+ info_list_add_str(attrs_list, "[disabled]");
+ }
+ else
+ {
+ int t = flg & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
+ pciaddr_t a = pos & PCI_ADDR_MEM_MASK;
+ int done = 0;
+ u32 z = 0;
+
+ if (t == PCI_BASE_ADDRESS_MEM_TYPE_64)
+ {
+ if (i >= cnt - 1)
+ {
+ info_list_add_str(attrs_list, "<invalid-64bit-slot>");
+ done = 1;
+ }
+ else
+ {
+ i++;
+ z = get_conf_long(d, PCI_BASE_ADDRESS_0 + 4*i);
+ }
+ }
+ if (!done)
+ {
+ if (a)
+ info_obj_add_fmt_buf_str(region_obj, "memory-at", buf, sizeof(buf), PCIADDR_T_FMT, a);
+ else
+ {
+ if ((flg & PCI_BASE_ADDRESS_MEM_MASK) || z)
+ info_list_add_str(attrs_list, "<ignored>");
+ else
+ info_list_add_str(attrs_list, "<unassigned>");
+ }
+ }
+ info_list_add_str(attrs_list,
+ (t == PCI_BASE_ADDRESS_MEM_TYPE_32) ? "32-bit" :
+ (t == PCI_BASE_ADDRESS_MEM_TYPE_64) ? "64-bit" :
+ (t == PCI_BASE_ADDRESS_MEM_TYPE_1M) ? "low-1M" : "type 3");
+ info_list_add_str(attrs_list,
+ (flg & PCI_BASE_ADDRESS_MEM_PREFETCH) ? "prefetchable" : "non-prefetchable");
+ if (!virtual && !(cmd & PCI_COMMAND_MEMORY))
+ info_list_add_str(attrs_list, "[disabled]");
+ }
+ fill_size(buf, sizeof(buf), len);
+ info_obj_add_str(region_obj, "size", buf);
+ }
+}
+
+static void
+fill_info_htype0(struct info_obj *dev_obj, struct device *d)
+{
+ struct info_obj *htype0_obj = info_obj_create_in_obj(dev_obj, "htype0");
+
+ fill_info_bases(htype0_obj, d, 6);
+ fill_info_rom(htype0_obj, d, PCI_ROM_ADDRESS);
+}
+
+static void
+fill_info_htype1(struct info_obj *dev_obj, struct device *d)
+{
+ u32 io_base = get_conf_byte(d, PCI_IO_BASE);
+ u32 io_limit = get_conf_byte(d, PCI_IO_LIMIT);
+ u32 io_type = io_base & PCI_IO_RANGE_TYPE_MASK;
+ u32 mem_base = get_conf_word(d, PCI_MEMORY_BASE);
+ u32 mem_limit = get_conf_word(d, PCI_MEMORY_LIMIT);
+ u32 mem_type = mem_base & PCI_MEMORY_RANGE_TYPE_MASK;
+ u32 pref_base = get_conf_word(d, PCI_PREF_MEMORY_BASE);
+ u32 pref_limit = get_conf_word(d, PCI_PREF_MEMORY_LIMIT);
+ u32 pref_type = pref_base & PCI_PREF_RANGE_TYPE_MASK;
+ word sec_stat = get_conf_word(d, PCI_SEC_STATUS);
+ word brc = get_conf_word(d, PCI_BRIDGE_CONTROL);
+ struct info_obj *htype1_obj = info_obj_create_in_obj(dev_obj, "htype1");
+ struct info_obj *bus_obj;
+ char buf[64];
+
+ fill_info_bases(htype1_obj, d, 2);
+
+ bus_obj = info_obj_create_in_obj(htype1_obj, "Bus");
+ info_obj_add_fmt_buf_str(bus_obj, "primary", buf, sizeof(buf), "%02x", get_conf_byte(d, PCI_PRIMARY_BUS));
+ info_obj_add_fmt_buf_str(bus_obj, "secondary", buf, sizeof(buf), "%02x", get_conf_byte(d, PCI_SECONDARY_BUS));
+ info_obj_add_fmt_buf_str(bus_obj, "subordinate", buf, sizeof(buf), "%02x", get_conf_byte(d, PCI_SUBORDINATE_BUS));
+ info_obj_add_fmt_buf_str(bus_obj, "sec-latency", buf, sizeof(buf), "%d", get_conf_byte(d, PCI_SEC_LATENCY_TIMER));
+
+ if (io_type != (io_limit & PCI_IO_RANGE_TYPE_MASK) ||
+ (io_type != PCI_IO_RANGE_TYPE_16 && io_type != PCI_IO_RANGE_TYPE_32))
+ fprintf(stderr, "\t!!! Unknown I/O range types %x/%x\n", io_base, io_limit);
+ else
+ {
+ io_base = (io_base & PCI_IO_RANGE_MASK) << 8;
+ io_limit = (io_limit & PCI_IO_RANGE_MASK) << 8;
+ if (io_type == PCI_IO_RANGE_TYPE_32)
+ {
+ io_base |= (get_conf_word(d, PCI_IO_BASE_UPPER16) << 16);
+ io_limit |= (get_conf_word(d, PCI_IO_LIMIT_UPPER16) << 16);
+ }
+ fill_range(buf, sizeof(buf), io_base, io_limit + 0xfff, 0);
+ info_obj_add_str(htype1_obj, "io-behind-bridge", buf);
+ }
+
+ if (mem_type != (mem_limit & PCI_MEMORY_RANGE_TYPE_MASK) ||
+ mem_type)
+ fprintf(stderr, "\t!!! Unknown memory range types %x/%x\n", mem_base, mem_limit);
+ else
+ {
+ mem_base = (mem_base & PCI_MEMORY_RANGE_MASK) << 16;
+ mem_limit = (mem_limit & PCI_MEMORY_RANGE_MASK) << 16;
+ fill_range(buf, sizeof(buf), mem_base, mem_limit + 0xfffff, 0);
+ info_obj_add_str(htype1_obj, "memory-behind-bridge", buf);
+ }
+
+ if (pref_type != (pref_limit & PCI_PREF_RANGE_TYPE_MASK) ||
+ (pref_type != PCI_PREF_RANGE_TYPE_32 && pref_type != PCI_PREF_RANGE_TYPE_64))
+ fprintf(stderr, "\t!!! Unknown prefetchable memory range types %x/%x\n", pref_base, pref_limit);
+ else
+ {
+ u64 pref_base_64 = (pref_base & PCI_PREF_RANGE_MASK) << 16;
+ u64 pref_limit_64 = (pref_limit & PCI_PREF_RANGE_MASK) << 16;
+ if (pref_type == PCI_PREF_RANGE_TYPE_64)
+ {
+ pref_base_64 |= (u64) get_conf_long(d, PCI_PREF_BASE_UPPER32) << 32;
+ pref_limit_64 |= (u64) get_conf_long(d, PCI_PREF_LIMIT_UPPER32) << 32;
+ }
+ fill_range(buf, sizeof(buf), pref_base_64, pref_limit_64 + 0xfffff, (pref_type == PCI_PREF_RANGE_TYPE_64));
+ info_obj_add_str(htype1_obj, "prefetchable-memory-behind-bridge", buf);
+ }
+
+ if (verbose > 1)
+ {
+ struct info_obj *sec_status_obj = info_obj_create();
+
+ sec_status_obj = info_obj_create_in_obj(htype1_obj, "SecondaryStatus");
+ info_obj_add_flag(sec_status_obj, "66MHz", FLAG(sec_stat, PCI_STATUS_66MHZ));
+ info_obj_add_flag(sec_status_obj, "FastB2B", FLAG(sec_stat, PCI_STATUS_FAST_BACK));
+ info_obj_add_flag(sec_status_obj, "ParErr", FLAG(sec_stat, PCI_STATUS_PARITY));
+ info_obj_add_str(sec_status_obj, "DEVSEL=", ((sec_stat & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_SLOW) ? "slow" :
+ ((sec_stat & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_MEDIUM) ? "medium" :
+ ((sec_stat & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_FAST) ? "fast" : "??");
+ info_obj_add_flag(sec_status_obj, ">TAbort", FLAG(sec_stat, PCI_STATUS_SIG_TARGET_ABORT));
+ info_obj_add_flag(sec_status_obj, "<TAbort", FLAG(sec_stat, PCI_STATUS_REC_TARGET_ABORT));
+ info_obj_add_flag(sec_status_obj, "<MAbort", FLAG(sec_stat, PCI_STATUS_REC_MASTER_ABORT));
+ info_obj_add_flag(sec_status_obj, "<SERR", FLAG(sec_stat, PCI_STATUS_SIG_SYSTEM_ERROR));
+ info_obj_add_flag(sec_status_obj, "<PERR", FLAG(sec_stat, PCI_STATUS_DETECTED_PARITY));
+ }
+
+ fill_info_rom(htype1_obj, d, PCI_ROM_ADDRESS1);
+
+ if (verbose > 1)
+ {
+ struct info_obj *bridgectl_obj;
+
+ bridgectl_obj = info_obj_create_in_obj(htype1_obj, "BridgeCtl");
+ info_obj_add_flag(bridgectl_obj, "Parity", FLAG(brc, PCI_BRIDGE_CTL_PARITY));
+ info_obj_add_flag(bridgectl_obj, "SERR", FLAG(brc, PCI_BRIDGE_CTL_SERR));
+ info_obj_add_flag(bridgectl_obj, "NoISA", FLAG(brc, PCI_BRIDGE_CTL_NO_ISA));
+ info_obj_add_flag(bridgectl_obj, "VGA", FLAG(brc, PCI_BRIDGE_CTL_VGA));
+ info_obj_add_flag(bridgectl_obj, "VGA16", FLAG(brc, PCI_BRIDGE_CTL_VGA_16BIT));
+ info_obj_add_flag(bridgectl_obj, "MAbort", FLAG(brc, PCI_BRIDGE_CTL_MASTER_ABORT));
+ info_obj_add_flag(bridgectl_obj, ">Reset", FLAG(brc, PCI_BRIDGE_CTL_BUS_RESET));
+ info_obj_add_flag(bridgectl_obj, "FastB2B", FLAG(brc, PCI_BRIDGE_CTL_FAST_BACK));
+ info_obj_add_flag(bridgectl_obj, "PriDiscTmr", FLAG(brc, PCI_BRIDGE_CTL_PRI_DISCARD_TIMER));
+ info_obj_add_flag(bridgectl_obj, "SecDiscTmr", FLAG(brc, PCI_BRIDGE_CTL_SEC_DISCARD_TIMER));
+ info_obj_add_flag(bridgectl_obj, "DiscTmrStat", FLAG(brc, PCI_BRIDGE_CTL_DISCARD_TIMER_STATUS));
+ info_obj_add_flag(bridgectl_obj, "DiscTmrSERREn", FLAG(brc, PCI_BRIDGE_CTL_DISCARD_TIMER_SERR_EN));
+ }
+}
+
+static void
+fill_info_htype2(struct info_obj *dev_obj, struct device *d)
+{
+ int i;
+ word cmd = get_conf_word(d, PCI_COMMAND);
+ word brc = get_conf_word(d, PCI_CB_BRIDGE_CONTROL);
+ word exca;
+ int verb = verbose > 2;
+ struct info_obj *htype2_obj = info_obj_create_in_obj(dev_obj, "htype2");
+ struct info_obj *bus_obj;
+ char buf[64];
+
+ fill_info_bases(htype2_obj, d, 1);
+
+ bus_obj = info_obj_create_in_obj(htype2_obj, "Bus");
+ info_obj_add_fmt_buf_str(bus_obj, "primary", buf, sizeof(buf), "%02x", get_conf_byte(d, PCI_CB_PRIMARY_BUS));
+ info_obj_add_fmt_buf_str(bus_obj, "secondary", buf, sizeof(buf), "%02x", get_conf_byte(d, PCI_CB_CARD_BUS));
+ info_obj_add_fmt_buf_str(bus_obj, "subordinate", buf, sizeof(buf), "%02x", get_conf_byte(d, PCI_CB_SUBORDINATE_BUS));
+ info_obj_add_fmt_buf_str(bus_obj, "sec-latency", buf, sizeof(buf), "%d", get_conf_byte(d, PCI_CB_LATENCY_TIMER));
+
+ for (i=0; i<2; i++)
+ {
+ int p = 8*i;
+ struct info_obj *mem_win_obj;
+
+ u32 base = get_conf_long(d, PCI_CB_MEMORY_BASE_0 + p);
+ u32 limit = get_conf_long(d, PCI_CB_MEMORY_LIMIT_0 + p);
+ limit = limit + 0xfff;
+ if (base <= limit || verb)
+ {
+ snprintf(buf, sizeof(buf), "memory-window-%d", i);
+ mem_win_obj = info_obj_create_in_obj(htype2_obj, buf);
+ info_obj_add_fmt_buf_str(mem_win_obj, "base", buf, sizeof(buf), "%08x", base);
+ info_obj_add_fmt_buf_str(mem_win_obj, "limit", buf, sizeof(buf), "%08x", limit);
+ info_obj_add_flag(mem_win_obj, "disabled", (cmd & PCI_COMMAND_MEMORY) ? '-' : '+');
+ info_obj_add_flag(mem_win_obj, "prefetchable", (brc & (PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 << i)) ? '-' : '+');
+ }
+ }
+ for (i=0; i<2; i++)
+ {
+ int p = 8*i;
+ struct info_obj *io_win_obj;
+
+ u32 base = get_conf_long(d, PCI_CB_IO_BASE_0 + p);
+ u32 limit = get_conf_long(d, PCI_CB_IO_LIMIT_0 + p);
+ if (!(base & PCI_IO_RANGE_TYPE_32))
+ {
+ base &= 0xffff;
+ limit &= 0xffff;
+ }
+ base &= PCI_CB_IO_RANGE_MASK;
+ limit = (limit & PCI_CB_IO_RANGE_MASK) + 3;
+ if (base <= limit || verb)
+ {
+ snprintf(buf, sizeof(buf), "io-window-%d", i);
+ io_win_obj = info_obj_create_in_obj(htype2_obj, buf);
+ info_obj_add_fmt_buf_str(io_win_obj, "base", buf, sizeof(buf), "%08x", base);
+ info_obj_add_fmt_buf_str(io_win_obj, "limit", buf, sizeof(buf), "%08x", limit);
+ info_obj_add_flag(io_win_obj, "disabled", (cmd & PCI_COMMAND_IO) ? '-' : '+');
+ }
+ }
+
+ if (get_conf_word(d, PCI_CB_SEC_STATUS) & PCI_STATUS_SIG_SYSTEM_ERROR)
+ info_obj_add_str(htype2_obj, "SecondaryStatus", "SERR");
+ if (verbose > 1)
+ {
+ struct info_obj *bridgectl_obj;
+
+ bridgectl_obj = info_obj_create_in_obj(htype2_obj, "BridgeCtl");
+ info_obj_add_flag(bridgectl_obj, "Parity", FLAG(brc, PCI_CB_BRIDGE_CTL_PARITY));
+ info_obj_add_flag(bridgectl_obj, "SERR", FLAG(brc, PCI_CB_BRIDGE_CTL_SERR));
+ info_obj_add_flag(bridgectl_obj, "ISA", FLAG(brc, PCI_CB_BRIDGE_CTL_ISA));
+ info_obj_add_flag(bridgectl_obj, "VGA", FLAG(brc, PCI_CB_BRIDGE_CTL_VGA));
+ info_obj_add_flag(bridgectl_obj, "MAbort", FLAG(brc, PCI_CB_BRIDGE_CTL_MASTER_ABORT));
+ info_obj_add_flag(bridgectl_obj, ">Reset", FLAG(brc, PCI_CB_BRIDGE_CTL_CB_RESET));
+ info_obj_add_flag(bridgectl_obj, "16bInt", FLAG(brc, PCI_CB_BRIDGE_CTL_16BIT_INT));
+ info_obj_add_flag(bridgectl_obj, "PostWrite", FLAG(brc, PCI_CB_BRIDGE_CTL_POST_WRITES));
+ }
+
+ if (d->config_cached < 128)
+ return;
+
+ exca = get_conf_word(d, PCI_CB_LEGACY_MODE_BASE);
+ if (exca)
+ info_obj_add_fmt_buf_str(htype2_obj, "exca", buf, sizeof(buf), "%04x", exca);
+}
+
+static void
+fill_info_verbose(struct info_obj *dev_obj, struct device *d)
+{
+ struct pci_dev *p = d->dev;
+ word status = get_conf_word(d, PCI_STATUS);
+ word cmd = get_conf_word(d, PCI_COMMAND);
+ word class = p->device_class;
+ byte bist = get_conf_byte(d, PCI_BIST);
+ byte htype = get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f;
+ byte latency = get_conf_byte(d, PCI_LATENCY_TIMER);
+ byte cache_line = get_conf_byte(d, PCI_CACHE_LINE_SIZE);
+ byte max_lat, min_gnt;
+ byte int_pin = get_conf_byte(d, PCI_INTERRUPT_PIN);
+ unsigned int irq;
+
+ fill_info_terse(dev_obj, d);
+ pci_fill_info(p, PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES |
+ PCI_FILL_PHYS_SLOT | PCI_FILL_LABEL | PCI_FILL_NUMA_NODE);
+ irq = p->irq;
+
+ switch (htype)
+ {
+ case PCI_HEADER_TYPE_NORMAL:
+ if (class == PCI_CLASS_BRIDGE_PCI)
+ fprintf(stderr, "\t!!! Invalid class %04x for header type %02x\n", class, htype);
+ max_lat = get_conf_byte(d, PCI_MAX_LAT);
+ min_gnt = get_conf_byte(d, PCI_MIN_GNT);
+ break;
+ case PCI_HEADER_TYPE_BRIDGE:
+ if ((class >> 8) != PCI_BASE_CLASS_BRIDGE)
+ fprintf(stderr, "\t!!! Invalid class %04x for header type %02x\n", class, htype);
+ min_gnt = max_lat = 0;
+ break;
+ case PCI_HEADER_TYPE_CARDBUS:
+ if ((class >> 8) != PCI_BASE_CLASS_BRIDGE)
+ fprintf(stderr, "\t!!! Invalid class %04x for header type %02x\n", class, htype);
+ min_gnt = max_lat = 0;
+ break;
+ default:
+ fprintf(stderr, "\t!!! Unknown header type %02x\n", htype);
+ return;
+ }
+
+ if (p->phy_slot)
+ info_obj_add_str(dev_obj, "PhySlot", p->phy_slot);
+
+ if (verbose > 1)
+ {
+ struct info_obj *control_obj;
+ struct info_obj *status_obj;
+
+ control_obj = info_obj_create_in_obj(dev_obj, "Control");
+ info_obj_add_flag(control_obj, "I/O", FLAG(cmd, PCI_COMMAND_IO));
+ info_obj_add_flag(control_obj, "Mem", FLAG(cmd, PCI_COMMAND_MEMORY));
+ info_obj_add_flag(control_obj, "BusMaster", FLAG(cmd, PCI_COMMAND_MASTER));
+ info_obj_add_flag(control_obj, "SpecCycle", FLAG(cmd, PCI_COMMAND_SPECIAL));
+ info_obj_add_flag(control_obj, "MemWINV", FLAG(cmd, PCI_COMMAND_INVALIDATE));
+ info_obj_add_flag(control_obj, "VGASnoop", FLAG(cmd, PCI_COMMAND_VGA_PALETTE));
+ info_obj_add_flag(control_obj, "ParErr", FLAG(cmd, PCI_COMMAND_PARITY));
+ info_obj_add_flag(control_obj, "Stepping", FLAG(cmd, PCI_COMMAND_WAIT));
+ info_obj_add_flag(control_obj, "SERR", FLAG(cmd, PCI_COMMAND_SERR));
+ info_obj_add_flag(control_obj, "FastB2B", FLAG(cmd, PCI_COMMAND_FAST_BACK));
+ info_obj_add_flag(control_obj, "DisINTx", FLAG(cmd, PCI_COMMAND_DISABLE_INTx));
+
+ status_obj = info_obj_create_in_obj(dev_obj, "Status");
+ info_obj_add_flag(status_obj, "Cap", FLAG(status, PCI_STATUS_CAP_LIST));
+ info_obj_add_flag(status_obj, "66MHz", FLAG(status, PCI_STATUS_66MHZ));
+ info_obj_add_flag(status_obj, "UDF", FLAG(status, PCI_STATUS_UDF));
+ info_obj_add_flag(status_obj, "FastB2B", FLAG(status, PCI_STATUS_FAST_BACK));
+ info_obj_add_flag(status_obj, "ParErr", FLAG(status, PCI_STATUS_PARITY));
+ info_obj_add_str(status_obj, "DEVSEL=",
+ ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_SLOW) ? "slow" :
+ ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_MEDIUM) ? "medium" :
+ ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_FAST) ? "fast" : "??");
+ info_obj_add_flag(status_obj, ">TAbort", FLAG(status, PCI_STATUS_SIG_TARGET_ABORT));
+ info_obj_add_flag(status_obj, "<TAbort", FLAG(status, PCI_STATUS_REC_TARGET_ABORT));
+ info_obj_add_flag(status_obj, "<MAbort", FLAG(status, PCI_STATUS_REC_MASTER_ABORT));
+ info_obj_add_flag(status_obj, ">SERR", FLAG(status, PCI_STATUS_SIG_SYSTEM_ERROR));
+ info_obj_add_flag(status_obj, "<PERR", FLAG(status, PCI_STATUS_DETECTED_PARITY));
+ info_obj_add_flag(status_obj, "INTx", FLAG(status, PCI_STATUS_INTx));
+
+ if (cmd & PCI_COMMAND_MASTER)
+ {
+ info_obj_add_fmt_str(dev_obj, "Latency", 16, "%d", latency);
+ if (min_gnt)
+ info_obj_add_fmt_str(dev_obj, "min-gnt", 16, "%d", min_gnt*250);
+ if (max_lat)
+ info_obj_add_fmt_str(dev_obj, "max-lat", 16, "%d", max_lat*250);
+ if (cache_line)
+ info_obj_add_fmt_str(dev_obj, "CacheLineSize", 16, "%d", cache_line * 4);
+ }
+ if (int_pin || irq)
+ {
+ info_obj_add_fmt_str(dev_obj, "int-pin", 2, "%c", (int_pin ? 'A' + int_pin - 1 : '?'));
+ info_obj_add_fmt_str(dev_obj, "IRQ", 16, PCIIRQ_FMT, irq);
+ }
+ }
+ else
+ {
+ struct info_list *flags_list = info_list_create(INFO_VAL_STRING);
+
+ info_obj_add_list(dev_obj, "Flags", flags_list);
+ if (cmd & PCI_COMMAND_MASTER)
+ info_list_add_str(flags_list, "bus master");
+ if (cmd & PCI_COMMAND_VGA_PALETTE)
+ info_list_add_str(flags_list, "VGA palette snoop");
+ if (cmd & PCI_COMMAND_WAIT)
+ info_list_add_str(flags_list, "stepping");
+ if (cmd & PCI_COMMAND_FAST_BACK)
+ info_list_add_str(flags_list, "fast Back2Back");
+ if (status & PCI_STATUS_66MHZ)
+ info_list_add_str(flags_list, "66MHz");
+ if (status & PCI_STATUS_UDF)
+ info_list_add_str(flags_list, "user-definable features");
+
+ info_obj_add_str(dev_obj, "devsel",
+ ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_SLOW) ? "slow" :
+ ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_MEDIUM) ? "medium" :
+ ((status & PCI_STATUS_DEVSEL_MASK) == PCI_STATUS_DEVSEL_FAST) ? "fast" : "??");
+ if (cmd & PCI_COMMAND_MASTER)
+ info_obj_add_fmt_str(dev_obj, "Latency", 16, "%d", latency);
+ if (irq)
+ info_obj_add_fmt_str(dev_obj, "IRQ", 16, PCIIRQ_FMT, irq);
+ }
+
+ if (bist & PCI_BIST_CAPABLE)
+ {
+ if (bist & PCI_BIST_START)
+ info_obj_add_str(dev_obj, "BIST", "running");
+ else
+ info_obj_add_fmt_str(dev_obj, "BIST", 3, "%02x", bist & PCI_BIST_CODE_MASK);
+ }
+
+ switch (htype)
+ {
+ case PCI_HEADER_TYPE_NORMAL:
+ fill_info_htype0(dev_obj, d);
+ break;
+ case PCI_HEADER_TYPE_BRIDGE:
+ fill_info_htype1(dev_obj, d);
+ break;
+ case PCI_HEADER_TYPE_CARDBUS:
+ fill_info_htype2(dev_obj, d);
+ break;
+ }
+}
+
+static void
+fill_info_device(struct info_obj *dev_obj, struct device *d)
+{
+ if (opt_machine)
+ fill_info_machine(dev_obj, d);
+ else
+ {
+ if (verbose)
+ fill_info_verbose(dev_obj, d);
+ else
+ fill_info_terse(dev_obj, d);
+ if (!opt_kernel && verbose)
+ fill_info_kernel(dev_obj, d);
+ }
+ if (opt_hex)
+ fill_info_hex_dump(dev_obj, d);
+}
+
+static void
+fill_info(struct info_obj *root)
+{
+ struct device *d;
+ struct info_list *dev_list = info_list_create(INFO_VAL_OBJECT);
+
+ for (d=first_dev; d; d=d->next)
+ {
+ struct info_obj *dev_obj = info_obj_create();
+ fill_info_device(dev_obj, d);
+ info_list_add_obj(dev_list, dev_obj);
+ }
+
+ info_obj_add_list(root, "pcidevices", dev_list);
+}
+
+static void
+show_json(void)
+{
+ struct info_obj *root = info_obj_create();
+
+ fill_info(root);
+ info_obj_print_json(root);
+ info_obj_delete(root);
+}
+
/* Main */
int
@@ -1009,6 +1703,9 @@ main(int argc, char **argv)
case 'D':
opt_domains = 2;
break;
+ case 'J':
+ opt_json = 1;
+ break;
#ifdef PCI_USE_DNS
case 'q':
opt_query_dns++;
@@ -1049,6 +1746,8 @@ main(int argc, char **argv)
sort_them();
if (opt_tree)
show_forest();
+ else if (opt_json)
+ show_json();
else
show();
}
@@ -133,6 +133,7 @@ void info_list_add_obj(struct info_list *list, struct info_obj *obj);
void show_kernel_machine(struct device *d UNUSED);
void show_kernel(struct device *d UNUSED);
void show_kernel_cleanup(void);
+void fill_info_kernel(struct info_obj *dev_obj UNUSED, struct device *d UNUSED);
/* ls-tree.c */
@@ -51,6 +51,9 @@ See below for details.
.B -t
Show a tree-like diagram containing all buses, bridges, devices and connections
between them.
+.TP
+.B -J
+Use JSON output format.
.SS Display options
.TP
This patch adds '-J' option for output in JSON format. When this option is enabled, the output contains the same data as without it, except for capabilities. Signed-off-by: Viktor Prutyanov <viktor.prutyanov@virtuozzo.com> --- Makefile | 3 +- ls-kernel.c | 24 +++ lspci.c | 701 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- lspci.h | 1 + lspci.man | 3 + 5 files changed, 730 insertions(+), 2 deletions(-)