]> mj.ucw.cz Git - pciutils.git/blob - ls-tree.c
Fix stripping in cross-compiling mode
[pciutils.git] / ls-tree.c
1 /*
2  *      The PCI Utilities -- Show Bus Tree
3  *
4  *      Copyright (c) 1997--2021 Martin Mares <mj@ucw.cz>
5  *
6  *      Can be freely distributed and used under the terms of the GNU GPL.
7  */
8
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <string.h>
12
13 #include "lspci.h"
14
15 struct bridge host_bridge = { NULL, NULL, NULL, NULL, NULL, NULL, ~0, ~0, ~0, ~0, NULL };
16
17 static struct bus *
18 find_bus(struct bridge *b, unsigned int domain, unsigned int n)
19 {
20   struct bus *bus;
21
22   for (bus=b->first_bus; bus; bus=bus->sibling)
23     if (bus->domain == domain && bus->number == n)
24       break;
25   return bus;
26 }
27
28 static struct device *
29 find_device(struct pci_dev *dd)
30 {
31   struct device *d;
32
33   if (!dd)
34     return NULL;
35   for (d=first_dev; d; d=d->next)
36     if (d->dev == dd)
37       break;
38   return d;
39 }
40
41 static struct bus *
42 new_bus(struct bridge *b, unsigned int domain, unsigned int n)
43 {
44   struct bus *bus = xmalloc(sizeof(struct bus));
45   bus->domain = domain;
46   bus->number = n;
47   bus->sibling = NULL;
48   bus->first_dev = NULL;
49   bus->last_dev = &bus->first_dev;
50   bus->parent_bridge = b;
51   if (b->last_bus)
52     b->last_bus->sibling = bus;
53   b->last_bus = bus;
54   if (!b->first_bus)
55     b->first_bus = bus;
56   return bus;
57 }
58
59 static void
60 insert_dev(struct device *d, struct bridge *b)
61 {
62   struct pci_dev *p = d->dev;
63   struct device *parent = NULL;
64   struct bus *bus = NULL;
65
66   if (p->known_fields & PCI_FILL_PARENT)
67     parent = find_device(p->parent);
68
69   if (parent && parent->bridge)
70     {
71       bus = parent->bridge->first_bus;
72       if (!bus)
73         bus = new_bus(parent->bridge, p->domain, p->bus);
74     }
75
76   if (!bus && b == &host_bridge)
77     {
78       for (b=b->child; b; b=b->prev)
79         if (b->domain == (unsigned)p->domain)
80           break;
81       if (!b)
82         b = &host_bridge;
83     }
84
85   if (!bus && ! (bus = find_bus(b, p->domain, p->bus)))
86     {
87       struct bridge *c;
88       for (c=b->child; c; c=c->prev)
89         if (c->domain == (unsigned)p->domain && c->secondary <= p->bus && p->bus <= c->subordinate)
90           {
91             insert_dev(d, c);
92             return;
93           }
94       bus = new_bus(b, p->domain, p->bus);
95     }
96   /* Simple insertion at the end _does_ guarantee the correct order as the
97    * original device list was sorted by (domain, bus, devfn) lexicographically
98    * and all devices on the new list have the same bus number.
99    */
100   *bus->last_dev = d;
101   bus->last_dev = &d->bus_next;
102   d->bus_next = NULL;
103   d->parent_bus = bus;
104 }
105
106 void
107 grow_tree(void)
108 {
109   struct device *d;
110   struct bridge **last_br, *b;
111
112   last_br = &host_bridge.chain;
113
114   /* Build list of top level domain bridges */
115
116   for (d=first_dev; d; d=d->next)
117     {
118       for (b=host_bridge.chain; b; b=b->chain)
119         if (b->domain == (unsigned)d->dev->domain)
120           break;
121       if (b)
122         continue;
123       b = xmalloc(sizeof(struct bridge));
124       b->domain = d->dev->domain;
125       b->primary = ~0;
126       b->secondary = 0;
127       b->subordinate = ~0;
128       *last_br = b;
129       last_br = &b->chain;
130       b->prev = b->next = b->child = NULL;
131       b->first_bus = NULL;
132       b->last_bus = NULL;
133       b->br_dev = NULL;
134       b->chain = NULL;
135       pacc->debug("Tree: domain %04x\n", b->domain);
136     }
137
138   /* Build list of bridges */
139
140   for (d=first_dev; d; d=d->next)
141     {
142       struct pci_dev *dd = d->dev;
143       word class = dd->device_class;
144       byte ht = d->no_config_access ? -1 : (get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f);
145       if ((class >> 8) == PCI_BASE_CLASS_BRIDGE &&
146           (ht == PCI_HEADER_TYPE_BRIDGE || ht == PCI_HEADER_TYPE_CARDBUS))
147         {
148           b = xmalloc(sizeof(struct bridge));
149           b->domain = dd->domain;
150           b->primary = dd->bus;
151           if (ht == PCI_HEADER_TYPE_BRIDGE)
152             {
153               b->secondary = get_conf_byte(d, PCI_SECONDARY_BUS);
154               b->subordinate = get_conf_byte(d, PCI_SUBORDINATE_BUS);
155             }
156           else
157             {
158               b->secondary = get_conf_byte(d, PCI_CB_CARD_BUS);
159               b->subordinate = get_conf_byte(d, PCI_CB_SUBORDINATE_BUS);
160             }
161           *last_br = b;
162           last_br = &b->chain;
163           b->prev = b->next = b->child = NULL;
164           b->first_bus = NULL;
165           b->last_bus = NULL;
166           b->br_dev = d;
167           d->bridge = b;
168           pacc->debug("Tree: bridge %04x:%02x:%02x.%d: %02x -> %02x-%02x\n",
169             dd->domain, dd->bus, dd->dev, dd->func,
170             b->primary, b->secondary, b->subordinate);
171         }
172     }
173
174   /* Append additional bridges reported by libpci via d->parent */
175
176   for (d=first_dev; d; d=d->next)
177     {
178       struct device *parent = NULL;
179       if (d->dev->known_fields & PCI_FILL_PARENT)
180         parent = find_device(d->dev->parent);
181       if (!parent || parent->bridge)
182         continue;
183       b = xmalloc(sizeof(struct bridge));
184       b->domain = parent->dev->domain;
185       b->primary = parent->dev->bus;
186       b->secondary = d->dev->bus;
187       /* At this stage subordinate number is unknown, so set it to secondary bus number. */
188       b->subordinate = b->secondary;
189       *last_br = b;
190       last_br = &b->chain;
191       b->prev = b->next = b->child = NULL;
192       b->first_bus = NULL;
193       b->last_bus = NULL;
194       b->br_dev = parent;
195       parent->bridge = b;
196       pacc->debug("Tree: bridge %04x:%02x:%02x.%d\n", b->domain,
197         parent->dev->bus, parent->dev->dev, parent->dev->func);
198     }
199   *last_br = NULL;
200
201   /* Create a bridge tree */
202
203   for (b=host_bridge.chain; b; b=b->chain)
204     {
205       struct device *br_dev = b->br_dev;
206       struct bridge *c, *best = NULL;
207       struct device *parent = NULL;
208
209       if (br_dev && (br_dev->dev->known_fields & PCI_FILL_PARENT))
210         parent = find_device(br_dev->dev->parent);
211       if (parent)
212         best = parent->bridge;
213       if (!best)
214       for (c=&host_bridge; c; c=c->chain)
215         if (c != b && (c == &host_bridge || b->domain == c->domain) &&
216             b->primary >= c->secondary && b->primary <= c->subordinate &&
217             (!best || best == &host_bridge || best->subordinate - best->primary > c->subordinate - c->primary))
218           best = c;
219       if (best)
220         {
221           b->prev = best->child;
222           best->child = b;
223         }
224     }
225
226   /* Insert secondary bus for each bridge */
227
228   for (b=host_bridge.chain; b; b=b->chain)
229     if (b->br_dev && !find_bus(b, b->domain, b->secondary))
230       new_bus(b, b->domain, b->secondary);
231
232   /* Create bus structs and link devices */
233
234   for (d=first_dev; d; d=d->next)
235     insert_dev(d, &host_bridge);
236 }
237
238 #define LINE_BUF_SIZE 1024
239
240 static void
241 print_it(char *line, char *p)
242 {
243   *p = 0;
244   fputs(line, stdout);
245   if (p >= line + LINE_BUF_SIZE - 1)
246     fputs("...", stdout);
247   putchar('\n');
248   for (p=line; *p; p++)
249     if (*p == '+' || *p == '|')
250       *p = '|';
251     else
252       *p = ' ';
253 }
254
255 static void show_tree_bridge(struct pci_filter *filter, struct bridge *, char *, char *);
256
257 static char * FORMAT_CHECK(printf, 3, 4)
258 tree_printf(char *line, char *p, char *fmt, ...)
259 {
260   va_list args;
261   int space = line + LINE_BUF_SIZE - 1 - p;
262
263   if (space <= 0)
264     return p;
265
266   va_start(args, fmt);
267   int res = vsnprintf(p, space, fmt, args);
268   if (res < 0)
269     {
270       /* Ancient C libraries return -1 on overflow and they do not truncate the output properly. */
271       *p = 0;
272       p += space;
273     }
274   else if (res >= space)
275     {
276       /* Ancient C libraries do not truncate the output properly. */
277       *(p+space-1) = 0;
278       p += space;
279     }
280   else
281     p += res;
282
283   va_end(args);
284   return p;
285 }
286
287 static void
288 show_tree_dev(struct pci_filter *filter, struct device *d, char *line, char *p)
289 {
290   struct pci_dev *q = d->dev;
291   struct bridge *b;
292   char namebuf[256];
293
294   p = tree_printf(line, p, "%02x.%x", q->dev, q->func);
295   for (b=host_bridge.chain; b; b=b->chain)
296     if (b->br_dev == d)
297       {
298         if (b->secondary == 0)
299           p = tree_printf(line, p, "-");
300         else if (b->secondary == b->subordinate)
301           p = tree_printf(line, p, "-[%02x]-", b->secondary);
302         else
303           p = tree_printf(line, p, "-[%02x-%02x]-", b->secondary, b->subordinate);
304         show_tree_bridge(filter, b, line, p);
305         return;
306       }
307   if (verbose)
308     p = tree_printf(line, p, "  %s",
309                     pci_lookup_name(pacc, namebuf, sizeof(namebuf),
310                                     PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
311                                     q->vendor_id, q->device_id));
312   print_it(line, p);
313 }
314
315 static struct pci_filter *
316 get_filter_for_child(struct pci_filter *filter, struct device *d)
317 {
318   if (!filter)
319     return NULL;
320
321   if (pci_filter_match(filter, d->dev))
322     return NULL;
323
324   return filter;
325 }
326
327 static int
328 check_bus_filter(struct pci_filter *filter, struct bus *b);
329
330 static int
331 check_dev_filter(struct pci_filter *filter, struct device *d)
332 {
333   struct bridge *br;
334   struct bus *b;
335
336   if (!filter)
337     return 1;
338
339   if (pci_filter_match(filter, d->dev))
340     return 1;
341
342   for (br = host_bridge.chain; br; br = br->chain)
343     if (br->br_dev == d)
344       {
345         for (b = br->first_bus; b; b = b->sibling)
346           if (check_bus_filter(filter, b))
347             return 1;
348         break;
349       }
350
351   return 0;
352 }
353
354 static int
355 check_bus_filter(struct pci_filter *filter, struct bus *b)
356 {
357   struct device *d;
358
359   if (!filter)
360     return 1;
361
362   for (d = b->first_dev; d; d = d->bus_next)
363     if (check_dev_filter(filter, d))
364       return 1;
365
366   return 0;
367 }
368
369 static void
370 show_tree_bus(struct pci_filter *filter, struct bus *b, char *line, char *p)
371 {
372   if (!b->first_dev)
373     print_it(line, p);
374   else if (!b->first_dev->bus_next)
375     {
376       if (check_dev_filter(filter, b->first_dev))
377         {
378           p = tree_printf(line, p, "--");
379           show_tree_dev(get_filter_for_child(filter, b->first_dev), b->first_dev, line, p);
380         }
381       else
382         print_it(line, p);
383     }
384   else
385     {
386       int i, count = 0;
387       struct device *d = b->first_dev;
388
389       do
390         {
391           if (check_dev_filter(filter, d))
392             count++;
393           d = d->bus_next;
394         }
395       while (d);
396
397       for (i = 0, d = b->first_dev; d; d = d->bus_next)
398         {
399           if (!check_dev_filter(filter, d))
400             continue;
401           char *p2 = tree_printf(line, p, count == 1 ? "--" : count == i+1 ? "\\-" : "+-");
402           show_tree_dev(get_filter_for_child(filter, d), d, line, p2);
403           i++;
404         }
405
406       if (count == 0)
407         print_it(line, p);
408     }
409 }
410
411 static void
412 show_tree_bridge(struct pci_filter *filter, struct bridge *b, char *line, char *p)
413 {
414   *p++ = '-';
415   if (!b->first_bus->sibling)
416     {
417       if (check_bus_filter(filter, b->first_bus))
418         {
419           if (!b->br_dev)
420             p = tree_printf(line, p, "[%04x:%02x]-", b->first_bus->domain, b->first_bus->number);
421           show_tree_bus(filter, b->first_bus, line, p);
422         }
423       else
424         print_it(line, p);
425     }
426   else
427     {
428       int i, count = 0;
429       struct bus *u = b->first_bus;
430       char *k;
431
432       do
433         {
434           if (check_bus_filter(filter, u))
435             count++;
436           u = u->sibling;
437         }
438       while (u);
439
440       for (i = 0, u = b->first_bus; u; u = u->sibling)
441         {
442           if (!check_bus_filter(filter, u))
443             continue;
444           k = tree_printf(line, p, count == 1 ? "[%04x:%02x]-" : count == i+1 ? "\\-[%04x:%02x]-" : "+-[%04x:%02x]-", u->domain, u->number);
445           show_tree_bus(filter, u, line, k);
446           i++;
447         }
448
449       if (count == 0)
450         print_it(line, p);
451     }
452 }
453
454 void
455 show_forest(struct pci_filter *filter)
456 {
457   char line[LINE_BUF_SIZE];
458   struct bridge *b;
459   if (host_bridge.child)
460     {
461       for (b=host_bridge.child; b->prev; b=b->prev)
462         b->prev->next = b;
463       for (; b; b=b->next)
464         show_tree_bridge(filter, b, line, line);
465     }
466 }