]>
Commit | Line | Data |
---|---|---|
c7a34993 MM |
1 | /* |
2 | * The PCI Utilities -- Show Bus Tree | |
3 | * | |
d1b22bb0 | 4 | * Copyright (c) 1997--2021 Martin Mares <mj@ucw.cz> |
c7a34993 MM |
5 | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
76d47191 | 9 | #include <stdarg.h> |
c7a34993 MM |
10 | #include <stdio.h> |
11 | #include <string.h> | |
12 | ||
13 | #include "lspci.h" | |
14 | ||
b1cff3a4 | 15 | struct bridge host_bridge = { NULL, NULL, NULL, NULL, NULL, 0, ~0, 0, ~0, NULL }; |
c7a34993 MM |
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 | ||
67954c8b PR |
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 | ||
c7a34993 MM |
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; | |
b1cff3a4 | 47 | bus->sibling = NULL; |
c7a34993 MM |
48 | bus->first_dev = NULL; |
49 | bus->last_dev = &bus->first_dev; | |
6b056c8e | 50 | bus->parent_bridge = b; |
b1cff3a4 PR |
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; | |
c7a34993 MM |
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; | |
67954c8b PR |
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); | |
c7a34993 | 68 | |
67954c8b PR |
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 && ! (bus = find_bus(b, p->domain, p->bus))) | |
c7a34993 MM |
77 | { |
78 | struct bridge *c; | |
79 | for (c=b->child; c; c=c->next) | |
ab61451d | 80 | if (c->domain == (unsigned)p->domain && c->secondary <= p->bus && p->bus <= c->subordinate) |
c7a34993 MM |
81 | { |
82 | insert_dev(d, c); | |
83 | return; | |
84 | } | |
85 | bus = new_bus(b, p->domain, p->bus); | |
86 | } | |
87 | /* Simple insertion at the end _does_ guarantee the correct order as the | |
88 | * original device list was sorted by (domain, bus, devfn) lexicographically | |
89 | * and all devices on the new list have the same bus number. | |
90 | */ | |
91 | *bus->last_dev = d; | |
6b056c8e MM |
92 | bus->last_dev = &d->bus_next; |
93 | d->bus_next = NULL; | |
94 | d->parent_bus = bus; | |
c7a34993 MM |
95 | } |
96 | ||
6b056c8e | 97 | void |
c7a34993 MM |
98 | grow_tree(void) |
99 | { | |
6b056c8e | 100 | struct device *d; |
c7a34993 MM |
101 | struct bridge **last_br, *b; |
102 | ||
103 | /* Build list of bridges */ | |
104 | ||
105 | last_br = &host_bridge.chain; | |
106 | for (d=first_dev; d; d=d->next) | |
107 | { | |
f558905d MM |
108 | struct pci_dev *dd = d->dev; |
109 | word class = dd->device_class; | |
c7a34993 | 110 | byte ht = get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f; |
f558905d | 111 | if ((class >> 8) == PCI_BASE_CLASS_BRIDGE && |
c7a34993 MM |
112 | (ht == PCI_HEADER_TYPE_BRIDGE || ht == PCI_HEADER_TYPE_CARDBUS)) |
113 | { | |
114 | b = xmalloc(sizeof(struct bridge)); | |
f558905d | 115 | b->domain = dd->domain; |
c7a34993 MM |
116 | if (ht == PCI_HEADER_TYPE_BRIDGE) |
117 | { | |
118 | b->primary = get_conf_byte(d, PCI_PRIMARY_BUS); | |
119 | b->secondary = get_conf_byte(d, PCI_SECONDARY_BUS); | |
120 | b->subordinate = get_conf_byte(d, PCI_SUBORDINATE_BUS); | |
121 | } | |
122 | else | |
123 | { | |
124 | b->primary = get_conf_byte(d, PCI_CB_PRIMARY_BUS); | |
125 | b->secondary = get_conf_byte(d, PCI_CB_CARD_BUS); | |
126 | b->subordinate = get_conf_byte(d, PCI_CB_SUBORDINATE_BUS); | |
127 | } | |
128 | *last_br = b; | |
129 | last_br = &b->chain; | |
130 | b->next = b->child = NULL; | |
131 | b->first_bus = NULL; | |
b1cff3a4 | 132 | b->last_bus = NULL; |
c7a34993 | 133 | b->br_dev = d; |
6b056c8e | 134 | d->bridge = b; |
f558905d MM |
135 | pacc->debug("Tree: bridge %04x:%02x:%02x.%d: %02x -> %02x-%02x\n", |
136 | dd->domain, dd->bus, dd->dev, dd->func, | |
137 | b->primary, b->secondary, b->subordinate); | |
c7a34993 MM |
138 | } |
139 | } | |
67954c8b PR |
140 | |
141 | /* Append additional bridges reported by libpci via d->parent */ | |
142 | ||
143 | for (d=first_dev; d; d=d->next) | |
144 | { | |
145 | struct device *parent = NULL; | |
146 | if (d->dev->known_fields & PCI_FILL_PARENT) | |
147 | parent = find_device(d->dev->parent); | |
148 | if (!parent || parent->bridge) | |
149 | continue; | |
150 | b = xmalloc(sizeof(struct bridge)); | |
151 | b->domain = parent->dev->domain; | |
152 | b->primary = parent->dev->bus; | |
153 | b->secondary = d->dev->bus; | |
154 | /* At this stage subordinate number is unknown, so set it to secondary bus number. */ | |
155 | b->subordinate = b->secondary; | |
156 | *last_br = b; | |
157 | last_br = &b->chain; | |
158 | b->next = b->child = NULL; | |
159 | b->first_bus = NULL; | |
160 | b->last_bus = NULL; | |
161 | b->br_dev = parent; | |
162 | parent->bridge = b; | |
163 | pacc->debug("Tree: bridge %04x:%02x:%02x.%d\n", b->domain, | |
164 | parent->dev->bus, parent->dev->dev, parent->dev->func); | |
165 | } | |
c7a34993 MM |
166 | *last_br = NULL; |
167 | ||
168 | /* Create a bridge tree */ | |
169 | ||
170 | for (b=&host_bridge; b; b=b->chain) | |
171 | { | |
67954c8b PR |
172 | struct device *br_dev = b->br_dev; |
173 | struct bridge *c, *best = NULL; | |
174 | struct device *parent = NULL; | |
175 | ||
176 | if (br_dev && (br_dev->dev->known_fields & PCI_FILL_PARENT)) | |
177 | parent = find_device(br_dev->dev->parent); | |
178 | if (parent) | |
179 | best = parent->bridge; | |
180 | if (!best) | |
c7a34993 MM |
181 | for (c=&host_bridge; c; c=c->chain) |
182 | if (c != b && (c == &host_bridge || b->domain == c->domain) && | |
183 | b->primary >= c->secondary && b->primary <= c->subordinate && | |
184 | (!best || best->subordinate - best->primary > c->subordinate - c->primary)) | |
185 | best = c; | |
186 | if (best) | |
187 | { | |
188 | b->next = best->child; | |
189 | best->child = b; | |
190 | } | |
191 | } | |
192 | ||
193 | /* Insert secondary bus for each bridge */ | |
194 | ||
195 | for (b=&host_bridge; b; b=b->chain) | |
196 | if (!find_bus(b, b->domain, b->secondary)) | |
197 | new_bus(b, b->domain, b->secondary); | |
198 | ||
199 | /* Create bus structs and link devices */ | |
200 | ||
6b056c8e MM |
201 | for (d=first_dev; d; d=d->next) |
202 | insert_dev(d, &host_bridge); | |
c7a34993 MM |
203 | } |
204 | ||
d1b22bb0 MM |
205 | #define LINE_BUF_SIZE 1024 |
206 | ||
c7a34993 MM |
207 | static void |
208 | print_it(char *line, char *p) | |
209 | { | |
c7a34993 MM |
210 | *p = 0; |
211 | fputs(line, stdout); | |
d1b22bb0 MM |
212 | if (p >= line + LINE_BUF_SIZE - 1) |
213 | fputs("...", stdout); | |
214 | putchar('\n'); | |
c7a34993 MM |
215 | for (p=line; *p; p++) |
216 | if (*p == '+' || *p == '|') | |
217 | *p = '|'; | |
218 | else | |
219 | *p = ' '; | |
220 | } | |
221 | ||
222 | static void show_tree_bridge(struct bridge *, char *, char *); | |
223 | ||
76d47191 MM |
224 | static char * FORMAT_CHECK(printf, 3, 4) |
225 | tree_printf(char *line, char *p, char *fmt, ...) | |
226 | { | |
227 | va_list args; | |
d1b22bb0 | 228 | int space = line + LINE_BUF_SIZE - 1 - p; |
76d47191 | 229 | |
d1b22bb0 | 230 | if (space <= 0) |
76d47191 MM |
231 | return p; |
232 | ||
233 | va_start(args, fmt); | |
d1b22bb0 | 234 | int res = vsnprintf(p, space, fmt, args); |
76d47191 MM |
235 | if (res < 0) |
236 | { | |
d1b22bb0 MM |
237 | /* Ancient C libraries return -1 on overflow and they do not truncate the output properly. */ |
238 | *p = 0; | |
239 | p += space; | |
76d47191 | 240 | } |
d1b22bb0 MM |
241 | else if (res >= space) |
242 | p += space; | |
76d47191 MM |
243 | else |
244 | p += res; | |
245 | ||
246 | va_end(args); | |
247 | return p; | |
248 | } | |
249 | ||
c7a34993 MM |
250 | static void |
251 | show_tree_dev(struct device *d, char *line, char *p) | |
252 | { | |
253 | struct pci_dev *q = d->dev; | |
254 | struct bridge *b; | |
255 | char namebuf[256]; | |
256 | ||
76d47191 | 257 | p = tree_printf(line, p, "%02x.%x", q->dev, q->func); |
c7a34993 MM |
258 | for (b=&host_bridge; b; b=b->chain) |
259 | if (b->br_dev == d) | |
260 | { | |
fd9c6a29 PR |
261 | if (b->secondary == 0) |
262 | p = tree_printf(line, p, "-"); | |
263 | else if (b->secondary == b->subordinate) | |
76d47191 | 264 | p = tree_printf(line, p, "-[%02x]-", b->secondary); |
c7a34993 | 265 | else |
76d47191 | 266 | p = tree_printf(line, p, "-[%02x-%02x]-", b->secondary, b->subordinate); |
c7a34993 MM |
267 | show_tree_bridge(b, line, p); |
268 | return; | |
269 | } | |
270 | if (verbose) | |
76d47191 MM |
271 | p = tree_printf(line, p, " %s", |
272 | pci_lookup_name(pacc, namebuf, sizeof(namebuf), | |
273 | PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE, | |
274 | q->vendor_id, q->device_id)); | |
c7a34993 MM |
275 | print_it(line, p); |
276 | } | |
277 | ||
278 | static void | |
279 | show_tree_bus(struct bus *b, char *line, char *p) | |
280 | { | |
281 | if (!b->first_dev) | |
282 | print_it(line, p); | |
6b056c8e | 283 | else if (!b->first_dev->bus_next) |
c7a34993 | 284 | { |
76d47191 | 285 | p = tree_printf(line, p, "--"); |
c7a34993 MM |
286 | show_tree_dev(b->first_dev, line, p); |
287 | } | |
288 | else | |
289 | { | |
290 | struct device *d = b->first_dev; | |
6b056c8e | 291 | while (d->bus_next) |
c7a34993 | 292 | { |
76d47191 MM |
293 | char *p2 = tree_printf(line, p, "+-"); |
294 | show_tree_dev(d, line, p2); | |
6b056c8e | 295 | d = d->bus_next; |
c7a34993 | 296 | } |
76d47191 MM |
297 | p = tree_printf(line, p, "\\-"); |
298 | show_tree_dev(d, line, p); | |
c7a34993 MM |
299 | } |
300 | } | |
301 | ||
302 | static void | |
303 | show_tree_bridge(struct bridge *b, char *line, char *p) | |
304 | { | |
305 | *p++ = '-'; | |
306 | if (!b->first_bus->sibling) | |
307 | { | |
308 | if (b == &host_bridge) | |
76d47191 | 309 | p = tree_printf(line, p, "[%04x:%02x]-", b->domain, b->first_bus->number); |
c7a34993 MM |
310 | show_tree_bus(b->first_bus, line, p); |
311 | } | |
312 | else | |
313 | { | |
314 | struct bus *u = b->first_bus; | |
315 | char *k; | |
316 | ||
317 | while (u->sibling) | |
318 | { | |
76d47191 | 319 | k = tree_printf(line, p, "+-[%04x:%02x]-", u->domain, u->number); |
c7a34993 MM |
320 | show_tree_bus(u, line, k); |
321 | u = u->sibling; | |
322 | } | |
76d47191 | 323 | k = tree_printf(line, p, "\\-[%04x:%02x]-", u->domain, u->number); |
c7a34993 MM |
324 | show_tree_bus(u, line, k); |
325 | } | |
326 | } | |
327 | ||
328 | void | |
888ddf0e | 329 | show_forest(struct pci_filter *filter) |
c7a34993 | 330 | { |
76d47191 | 331 | char line[LINE_BUF_SIZE]; |
888ddf0e GK |
332 | if (filter == NULL) |
333 | show_tree_bridge(&host_bridge, line, line); | |
334 | else | |
335 | { | |
336 | struct bridge *b; | |
337 | for (b=&host_bridge; b; b=b->chain) | |
338 | { | |
339 | if (b->br_dev && pci_filter_match(filter, b->br_dev->dev)) | |
340 | { | |
341 | struct pci_dev *d = b->br_dev->dev; | |
342 | char *p = line; | |
76d47191 | 343 | p = tree_printf(line, p, "%04x:%02x:", d->domain_16, d->bus); |
888ddf0e GK |
344 | show_tree_dev(b->br_dev, line, p); |
345 | } | |
346 | } | |
347 | } | |
c7a34993 | 348 | } |