Blame ls-tree.c

Packit 9fb349
/*
Packit 9fb349
 *	The PCI Utilities -- Show Bus Tree
Packit 9fb349
 *
Packit 9fb349
 *	Copyright (c) 1997--2020 Martin Mares <mj@ucw.cz>
Packit 9fb349
 *
Packit 9fb349
 *	Can be freely distributed and used under the terms of the GNU GPL.
Packit 9fb349
 */
Packit 9fb349
Packit 9fb349
#include <stdarg.h>
Packit 9fb349
#include <stdio.h>
Packit 9fb349
#include <string.h>
Packit 9fb349
Packit 9fb349
#include "lspci.h"
Packit 9fb349
Packit 9fb349
struct bridge host_bridge = { NULL, NULL, NULL, NULL, 0, ~0, 0, ~0, NULL };
Packit 9fb349
Packit 9fb349
static struct bus *
Packit 9fb349
find_bus(struct bridge *b, unsigned int domain, unsigned int n)
Packit 9fb349
{
Packit 9fb349
  struct bus *bus;
Packit 9fb349
Packit 9fb349
  for (bus=b->first_bus; bus; bus=bus->sibling)
Packit 9fb349
    if (bus->domain == domain && bus->number == n)
Packit 9fb349
      break;
Packit 9fb349
  return bus;
Packit 9fb349
}
Packit 9fb349
Packit 9fb349
static struct bus *
Packit 9fb349
new_bus(struct bridge *b, unsigned int domain, unsigned int n)
Packit 9fb349
{
Packit 9fb349
  struct bus *bus = xmalloc(sizeof(struct bus));
Packit 9fb349
  bus->domain = domain;
Packit 9fb349
  bus->number = n;
Packit 9fb349
  bus->sibling = b->first_bus;
Packit 9fb349
  bus->first_dev = NULL;
Packit 9fb349
  bus->last_dev = &bus->first_dev;
Packit 9fb349
  bus->parent_bridge = b;
Packit 9fb349
  b->first_bus = bus;
Packit 9fb349
  return bus;
Packit 9fb349
}
Packit 9fb349
Packit 9fb349
static void
Packit 9fb349
insert_dev(struct device *d, struct bridge *b)
Packit 9fb349
{
Packit 9fb349
  struct pci_dev *p = d->dev;
Packit 9fb349
  struct bus *bus;
Packit 9fb349
Packit 9fb349
  if (! (bus = find_bus(b, p->domain, p->bus)))
Packit 9fb349
    {
Packit 9fb349
      struct bridge *c;
Packit 9fb349
      for (c=b->child; c; c=c->next)
Packit 9fb349
	if (c->domain == (unsigned)p->domain && c->secondary <= p->bus && p->bus <= c->subordinate)
Packit 9fb349
          {
Packit 9fb349
            insert_dev(d, c);
Packit 9fb349
            return;
Packit 9fb349
          }
Packit 9fb349
      bus = new_bus(b, p->domain, p->bus);
Packit 9fb349
    }
Packit 9fb349
  /* Simple insertion at the end _does_ guarantee the correct order as the
Packit 9fb349
   * original device list was sorted by (domain, bus, devfn) lexicographically
Packit 9fb349
   * and all devices on the new list have the same bus number.
Packit 9fb349
   */
Packit 9fb349
  *bus->last_dev = d;
Packit 9fb349
  bus->last_dev = &d->bus_next;
Packit 9fb349
  d->bus_next = NULL;
Packit 9fb349
  d->parent_bus = bus;
Packit 9fb349
}
Packit 9fb349
Packit 9fb349
void
Packit 9fb349
grow_tree(void)
Packit 9fb349
{
Packit 9fb349
  struct device *d;
Packit 9fb349
  struct bridge **last_br, *b;
Packit 9fb349
Packit 9fb349
  /* Build list of bridges */
Packit 9fb349
Packit 9fb349
  last_br = &host_bridge.chain;
Packit 9fb349
  for (d=first_dev; d; d=d->next)
Packit 9fb349
    {
Packit 9fb349
      struct pci_dev *dd = d->dev;
Packit 9fb349
      word class = dd->device_class;
Packit 9fb349
      byte ht = get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f;
Packit 9fb349
      if ((class >> 8) == PCI_BASE_CLASS_BRIDGE &&
Packit 9fb349
	  (ht == PCI_HEADER_TYPE_BRIDGE || ht == PCI_HEADER_TYPE_CARDBUS))
Packit 9fb349
	{
Packit 9fb349
	  b = xmalloc(sizeof(struct bridge));
Packit 9fb349
	  b->domain = dd->domain;
Packit 9fb349
	  if (ht == PCI_HEADER_TYPE_BRIDGE)
Packit 9fb349
	    {
Packit 9fb349
	      b->primary = get_conf_byte(d, PCI_PRIMARY_BUS);
Packit 9fb349
	      b->secondary = get_conf_byte(d, PCI_SECONDARY_BUS);
Packit 9fb349
	      b->subordinate = get_conf_byte(d, PCI_SUBORDINATE_BUS);
Packit 9fb349
	    }
Packit 9fb349
	  else
Packit 9fb349
	    {
Packit 9fb349
	      b->primary = get_conf_byte(d, PCI_CB_PRIMARY_BUS);
Packit 9fb349
	      b->secondary = get_conf_byte(d, PCI_CB_CARD_BUS);
Packit 9fb349
	      b->subordinate = get_conf_byte(d, PCI_CB_SUBORDINATE_BUS);
Packit 9fb349
	    }
Packit 9fb349
	  *last_br = b;
Packit 9fb349
	  last_br = &b->chain;
Packit 9fb349
	  b->next = b->child = NULL;
Packit 9fb349
	  b->first_bus = NULL;
Packit 9fb349
	  b->br_dev = d;
Packit 9fb349
	  d->bridge = b;
Packit 9fb349
	  pacc->debug("Tree: bridge %04x:%02x:%02x.%d: %02x -> %02x-%02x\n",
Packit 9fb349
	    dd->domain, dd->bus, dd->dev, dd->func,
Packit 9fb349
	    b->primary, b->secondary, b->subordinate);
Packit 9fb349
	}
Packit 9fb349
    }
Packit 9fb349
  *last_br = NULL;
Packit 9fb349
Packit 9fb349
  /* Create a bridge tree */
Packit 9fb349
Packit 9fb349
  for (b=&host_bridge; b; b=b->chain)
Packit 9fb349
    {
Packit 9fb349
      struct bridge *c, *best;
Packit 9fb349
      best = NULL;
Packit 9fb349
      for (c=&host_bridge; c; c=c->chain)
Packit 9fb349
	if (c != b && (c == &host_bridge || b->domain == c->domain) &&
Packit 9fb349
	    b->primary >= c->secondary && b->primary <= c->subordinate &&
Packit 9fb349
	    (!best || best->subordinate - best->primary > c->subordinate - c->primary))
Packit 9fb349
	  best = c;
Packit 9fb349
      if (best)
Packit 9fb349
	{
Packit 9fb349
	  b->next = best->child;
Packit 9fb349
	  best->child = b;
Packit 9fb349
	}
Packit 9fb349
    }
Packit 9fb349
Packit 9fb349
  /* Insert secondary bus for each bridge */
Packit 9fb349
Packit 9fb349
  for (b=&host_bridge; b; b=b->chain)
Packit 9fb349
    if (!find_bus(b, b->domain, b->secondary))
Packit 9fb349
      new_bus(b, b->domain, b->secondary);
Packit 9fb349
Packit 9fb349
  /* Create bus structs and link devices */
Packit 9fb349
Packit 9fb349
  for (d=first_dev; d; d=d->next)
Packit 9fb349
    insert_dev(d, &host_bridge);
Packit 9fb349
}
Packit 9fb349
Packit 9fb349
static void
Packit 9fb349
print_it(char *line, char *p)
Packit 9fb349
{
Packit 9fb349
  *p++ = '\n';
Packit 9fb349
  *p = 0;
Packit 9fb349
  fputs(line, stdout);
Packit 9fb349
  for (p=line; *p; p++)
Packit 9fb349
    if (*p == '+' || *p == '|')
Packit 9fb349
      *p = '|';
Packit 9fb349
    else
Packit 9fb349
      *p = ' ';
Packit 9fb349
}
Packit 9fb349
Packit 9fb349
static void show_tree_bridge(struct bridge *, char *, char *);
Packit 9fb349
Packit 9fb349
#define LINE_BUF_SIZE 1024
Packit 9fb349
Packit 9fb349
static char * FORMAT_CHECK(printf, 3, 4)
Packit 9fb349
tree_printf(char *line, char *p, char *fmt, ...)
Packit 9fb349
{
Packit 9fb349
  va_list args;
Packit 9fb349
  char *end = line + LINE_BUF_SIZE - 2;
Packit 9fb349
Packit 9fb349
  if (p >= end)
Packit 9fb349
    return p;
Packit 9fb349
Packit 9fb349
  va_start(args, fmt);
Packit 9fb349
  int res = vsnprintf(p, end - p, fmt, args);
Packit 9fb349
  if (res < 0)
Packit 9fb349
    {
Packit 9fb349
      /* Ancient C libraries return -1 on overflow */
Packit 9fb349
      p += strlen(p);
Packit 9fb349
    }
Packit 9fb349
  else
Packit 9fb349
    p += res;
Packit 9fb349
Packit 9fb349
  va_end(args);
Packit 9fb349
  return p;
Packit 9fb349
}
Packit 9fb349
Packit 9fb349
static void
Packit 9fb349
show_tree_dev(struct device *d, char *line, char *p)
Packit 9fb349
{
Packit 9fb349
  struct pci_dev *q = d->dev;
Packit 9fb349
  struct bridge *b;
Packit 9fb349
  char namebuf[256];
Packit 9fb349
Packit 9fb349
  p = tree_printf(line, p, "%02x.%x", q->dev, q->func);
Packit 9fb349
  for (b=&host_bridge; b; b=b->chain)
Packit 9fb349
    if (b->br_dev == d)
Packit 9fb349
      {
Packit 9fb349
	if (b->secondary == b->subordinate)
Packit 9fb349
	  p = tree_printf(line, p, "-[%02x]-", b->secondary);
Packit 9fb349
	else
Packit 9fb349
	  p = tree_printf(line, p, "-[%02x-%02x]-", b->secondary, b->subordinate);
Packit 9fb349
        show_tree_bridge(b, line, p);
Packit 9fb349
        return;
Packit 9fb349
      }
Packit 9fb349
  if (verbose)
Packit 9fb349
    p = tree_printf(line, p, "  %s",
Packit 9fb349
		    pci_lookup_name(pacc, namebuf, sizeof(namebuf),
Packit 9fb349
				    PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
Packit 9fb349
				    q->vendor_id, q->device_id));
Packit 9fb349
  print_it(line, p);
Packit 9fb349
}
Packit 9fb349
Packit 9fb349
static void
Packit 9fb349
show_tree_bus(struct bus *b, char *line, char *p)
Packit 9fb349
{
Packit 9fb349
  if (!b->first_dev)
Packit 9fb349
    print_it(line, p);
Packit 9fb349
  else if (!b->first_dev->bus_next)
Packit 9fb349
    {
Packit 9fb349
      p = tree_printf(line, p, "--");
Packit 9fb349
      show_tree_dev(b->first_dev, line, p);
Packit 9fb349
    }
Packit 9fb349
  else
Packit 9fb349
    {
Packit 9fb349
      struct device *d = b->first_dev;
Packit 9fb349
      while (d->bus_next)
Packit 9fb349
	{
Packit 9fb349
	  char *p2 = tree_printf(line, p, "+-");
Packit 9fb349
	  show_tree_dev(d, line, p2);
Packit 9fb349
	  d = d->bus_next;
Packit 9fb349
	}
Packit 9fb349
      p = tree_printf(line, p, "\\-");
Packit 9fb349
      show_tree_dev(d, line, p);
Packit 9fb349
    }
Packit 9fb349
}
Packit 9fb349
Packit 9fb349
static void
Packit 9fb349
show_tree_bridge(struct bridge *b, char *line, char *p)
Packit 9fb349
{
Packit 9fb349
  *p++ = '-';
Packit 9fb349
  if (!b->first_bus->sibling)
Packit 9fb349
    {
Packit 9fb349
      if (b == &host_bridge)
Packit 9fb349
        p = tree_printf(line, p, "[%04x:%02x]-", b->domain, b->first_bus->number);
Packit 9fb349
      show_tree_bus(b->first_bus, line, p);
Packit 9fb349
    }
Packit 9fb349
  else
Packit 9fb349
    {
Packit 9fb349
      struct bus *u = b->first_bus;
Packit 9fb349
      char *k;
Packit 9fb349
Packit 9fb349
      while (u->sibling)
Packit 9fb349
        {
Packit 9fb349
          k = tree_printf(line, p, "+-[%04x:%02x]-", u->domain, u->number);
Packit 9fb349
          show_tree_bus(u, line, k);
Packit 9fb349
          u = u->sibling;
Packit 9fb349
        }
Packit 9fb349
      k = tree_printf(line, p, "\\-[%04x:%02x]-", u->domain, u->number);
Packit 9fb349
      show_tree_bus(u, line, k);
Packit 9fb349
    }
Packit 9fb349
}
Packit 9fb349
Packit 9fb349
void
Packit 9fb349
show_forest(struct pci_filter *filter)
Packit 9fb349
{
Packit 9fb349
  char line[LINE_BUF_SIZE];
Packit 9fb349
  if (filter == NULL)
Packit 9fb349
    show_tree_bridge(&host_bridge, line, line);
Packit 9fb349
  else
Packit 9fb349
    {
Packit 9fb349
      struct bridge *b;
Packit 9fb349
      for (b=&host_bridge; b; b=b->chain)
Packit 9fb349
        {
Packit 9fb349
          if (b->br_dev && pci_filter_match(filter, b->br_dev->dev))
Packit 9fb349
            {
Packit 9fb349
                struct pci_dev *d = b->br_dev->dev;
Packit 9fb349
                char *p = line;
Packit 9fb349
                p = tree_printf(line, p, "%04x:%02x:", d->domain_16, d->bus);
Packit 9fb349
                show_tree_dev(b->br_dev, line, p);
Packit 9fb349
            }
Packit 9fb349
        }
Packit 9fb349
    }
Packit 9fb349
}