1 /*
2  * Copyright (c) 2021 Travis Geiseblrecht
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 
9 #include "bridge.h"
10 
11 #include <sys/types.h>
12 #include <lk/cpp.h>
13 #include <lk/debug.h>
14 #include <lk/err.h>
15 #include <lk/list.h>
16 #include <lk/trace.h>
17 #include <dev/bus/pci.h>
18 #include <inttypes.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <platform/interrupts.h>
24 
25 #define LOCAL_TRACE 0
26 
27 #include "bus_mgr.h"
28 #include "bridge.h"
29 #include "bus.h"
30 #include "resource.h"
31 
32 namespace pci {
33 
bridge(pci_location_t loc,bus * bus)34 bridge::bridge(pci_location_t loc, bus *bus) : device(loc, bus) {}
35 bridge::~bridge() = default;
36 
37 // examine the bridge device, figuring out the bus range it controls and recurse
probe(pci_location_t loc,bus * parent_bus,bridge ** out_bridge)38 status_t bridge::probe(pci_location_t loc, bus *parent_bus, bridge **out_bridge) {
39     char str[14];
40     LTRACEF("%s\n", pci_loc_string(loc, str));
41 
42     *out_bridge = nullptr;
43 
44     // read vendor id and see if this is a real device
45     uint16_t vendor_id;
46     status_t err = pci_read_config_half(loc, PCI_CONFIG_VENDOR_ID, &vendor_id);
47     if (err != NO_ERROR) {
48         return ERR_NOT_FOUND;
49     }
50     if (vendor_id == 0xffff) {
51         return ERR_NOT_FOUND;
52     }
53 
54     // read header type (0 or 1)
55     uint8_t header_type;
56     err = pci_read_config_byte(loc, PCI_CONFIG_HEADER_TYPE, &header_type);
57     if (err != NO_ERROR) {
58         return ERR_NOT_FOUND;
59     }
60 
61     header_type &= PCI_HEADER_TYPE_MASK;
62 
63     if (header_type != 1) {
64         LTRACEF("type %d header on bridge we don't understand, skipping\n", header_type);
65         return ERR_NOT_FOUND;
66     }
67 
68     // we are a bridge to a new set of busses
69     bridge *br = new bridge(loc, parent_bus);
70 
71     // we only grok type 1 headers here
72     err = br->load_config();
73     if (err < 0) {
74         delete br;
75         return err;
76     }
77 
78     LTRACEF("primary bus %hhd secondary %hhd subordinate %hhd\n",
79             br->primary_bus(), br->secondary_bus(), br->subordinate_bus());
80 
81     // probe the bridge's capabilities
82     br->probe_capabilities();
83 
84     *out_bridge = br;
85 
86     if (br->secondary_bus() == 0) {
87         // allocate a new secondary bus
88         uint8_t new_secondary_bus = allocate_next_bus();
89 
90         // we do not yet know the range of busses we will find downstream, lower level bridges will have to
91         // back patch us as they find new children.
92         uint8_t new_subordinate_bus = new_secondary_bus;
93 
94         LTRACEF("assigning secondary bus %d, parent bus %d\n", new_secondary_bus, parent_bus->bus_num());
95 
96         br->assign_bus_numbers(parent_bus->bus_num(), new_secondary_bus, new_subordinate_bus);
97 
98         // tell the parent bridge that we have a new range
99         auto *parent_bridge = parent_bus->get_bridge();
100         if (parent_bridge) {
101             parent_bridge->extend_subordinate_range(new_secondary_bus);
102         }
103     }
104 
105     // sanity check that we don't have overlapping busses
106     if (br->secondary_bus() < get_last_bus()) {
107         TRACEF("secondary bus %u of bridge we've already seen (last bus seen %u)\n", br->secondary_bus(), get_last_bus());
108         delete br;
109         return ERR_NO_RESOURCES;
110     }
111 
112     // start a scan of the secondary bus downstream of this.
113     // via bridge devices on this bus, should find all of the subordinate busses.
114     pci_location_t bus_location = {};
115     bus_location.segment = loc.segment;
116     bus_location.bus = br->secondary_bus();
117 
118     bus *new_bus;
119     err = bus::probe(bus_location, br, &new_bus);
120     if (err != NO_ERROR) {
121         // TODO: don't leak bridge and/or bus
122         return err;
123     }
124 
125     // add the bus to our list of children
126     DEBUG_ASSERT(new_bus);
127     br->add_bus(new_bus);
128 
129     // add the bus to the global bus list
130     new_bus->add_to_global_list();
131 
132     return NO_ERROR;
133 }
134 
extend_subordinate_range(uint8_t new_secondary_bus)135 void bridge::extend_subordinate_range(uint8_t new_secondary_bus) {
136     LTRACEF("new_secondary_bus %u existing primary bus %hhd secondary %hhd subordinate %hhd\n",
137             new_secondary_bus, primary_bus(), secondary_bus(), subordinate_bus());
138 
139     if (new_secondary_bus > subordinate_bus()) {
140         assign_bus_numbers(primary_bus(), secondary_bus(), new_secondary_bus);
141 
142         DEBUG_ASSERT(subordinate_bus() == new_secondary_bus);
143 
144         // tell the parent bridge that we have a new range
145         auto *parent_bridge = bus_->get_bridge();
146         if (parent_bridge) {
147             parent_bridge->extend_subordinate_range(new_secondary_bus);
148         }
149     }
150 }
151 
assign_bus_numbers(uint8_t primary,uint8_t secondary,uint8_t subordinate)152 void bridge::assign_bus_numbers(uint8_t primary, uint8_t secondary, uint8_t subordinate) {
153     LTRACEF("primary %u secondary %u subordinate %u\n", primary, secondary, subordinate);
154 
155     uint32_t temp;
156 
157     pci_read_config_word(loc_, 0x18, &temp);
158     temp &= 0xff000000; // leave latency timer alone
159     temp |= subordinate << 16;
160     temp |= secondary << 8;
161     temp |= primary << 0;
162     pci_write_config_word(loc_, 0x18, temp);
163 
164     // reread the config
165     load_config();
166 }
167 
dump(size_t indent)168 void bridge::dump(size_t indent) {
169     auto scoot = [&]() {
170         for (size_t i = 0; i < indent; i++) {
171             printf(" ");
172         }
173 
174     };
175     char str[14];
176     scoot();
177     printf("bridge %s %04hx:%04hx primary bus %d child busses [%d..%d]\n", pci_loc_string(loc_, str),
178            vendor_id(), device_id(), primary_bus(),
179            secondary_bus(), subordinate_bus());
180 
181     auto mr = mem_range();
182     auto ir = io_range();
183     auto pr = prefetch_range();
184     scoot();
185     printf("mem_range [%#x..%#x] io_range [%#x..%#x] pref_range [%#" PRIx64 "..%#" PRIx64"] \n",
186            mr.base, mr.limit, ir.base, ir.limit, pr.base, pr.limit);
187 
188     for (size_t b = 0; b < 2; b++) {
189         if (bars_[b].valid) {
190             for (size_t i = 0; i < indent + 1; i++) {
191                 printf(" ");
192             }
193             printf("BAR %zu: addr %#" PRIx64 " size %#zx io %d valid %d\n", b, bars_[b].addr, bars_[b].size, bars_[b].io, bars_[b].valid);
194         }
195     }
196 
197     if (secondary_bus_) {
198         secondary_bus_->dump(indent + 1);
199     }
200 }
201 
202 // accessors to compute the io and memory range of the bridge
io_range()203 bridge::range<uint32_t> bridge::io_range() {
204     if (config_.type1.io_limit < config_.type1.io_base) {
205         return { 0, 0 };
206     } else {
207         // TODO: handle 32bit io (does this really exist?)
208         return { ((uint32_t)config_.type1.io_base >> 4) << 12,
209                  (((uint32_t)config_.type1.io_limit >> 4) << 12) | 0xfff };
210     }
211 }
212 
mem_range()213 bridge::range<uint32_t> bridge::mem_range() {
214     if (config_.type1.memory_limit < config_.type1.memory_base) {
215         return { 0, 0 };
216     } else {
217         return { ((uint32_t)config_.type1.memory_base >> 4) << 20,
218                  (((uint32_t)config_.type1.memory_limit >> 4) << 20) | 0xfffff };
219     }
220 }
221 
prefetch_range()222 bridge::range<uint64_t> bridge::prefetch_range() {
223     if (config_.type1.prefetchable_memory_limit < config_.type1.prefetchable_memory_base) {
224         return { 0, 0 };
225     } else {
226         bool is_64 = (config_.type1.prefetchable_memory_base & 0xf) == 1;
227 
228         uint64_t base, limit;
229 
230         base = (((uint64_t)config_.type1.prefetchable_memory_base >> 4) << 20);
231         if (is_64) {
232             base |= (uint64_t)config_.type1.prefetchable_base_upper << 32;
233         }
234         limit = (((uint64_t)config_.type1.prefetchable_memory_limit >> 4) << 20) | 0xfffff;
235         if (is_64) {
236             limit |= (uint64_t)config_.type1.prefetchable_limit_upper << 32;
237         }
238         return { base, limit };
239     }
240 }
241 
compute_bar_sizes_no_local_bar(bar_sizes * bridge_sizes)242 status_t bridge::compute_bar_sizes_no_local_bar(bar_sizes *bridge_sizes) {
243     bar_sizes bus_sizes = {};
244 
245     // drill into our bus and accumulate from there
246     auto perdev = [&](device *d) -> status_t {
247         device::bar_sizes sizes = {};
248 
249         d->compute_bar_sizes(&sizes);
250 
251         if (LOCAL_TRACE) {
252             char str[14];
253             printf("bar sizes for device %s: io %#x align %u mmio %#x align %u mmio64 %#" PRIx64 " align %u prefetch %#" PRIx64 " align %u prefetch64 %#" PRIx64 " align %u\n",
254                     pci_loc_string(d->loc(), str), sizes.io_size, sizes.io_align, sizes.mmio_size, sizes.mmio_align,
255                     sizes.mmio64_size, sizes.mmio64_align, sizes.prefetchable_size, sizes.prefetchable_align,
256                     sizes.prefetchable64_size, sizes.prefetchable64_align);
257         }
258 
259         // accumulate to the passed in size struct
260         bus_sizes += sizes;
261 
262         return 0;
263     };
264 
265     LTRACEF("recursing into bus %u\n", secondary_bus_->bus_num());
266     secondary_bus_->for_every_device(perdev);
267 
268     // we've accumulated the size of the bus and everything below it
269     // round up some of the accumulated sizes based on limitations in the bridge
270 
271     // bridges can only pass through io ports in blocks of 0x1000
272     if (bus_sizes.io_size > 0 && bus_sizes.io_align < 12) {
273         bus_sizes.io_align = 12;
274     }
275     bus_sizes.io_size = ROUNDUP(bus_sizes.io_size, 0x1000);
276 
277     // mmio ranges can only pass through in units of 1MB (1<<20)
278     if (bus_sizes.mmio_size > 0 && bus_sizes.mmio_align < 20) {
279         bus_sizes.mmio_align = 20;
280     }
281     bus_sizes.mmio_size = ROUNDUP(bus_sizes.mmio_size, (1UL << 20));
282 
283     // 64bit mmio ranges can't be passed through bridges, so merge with the mmio range
284     if (bus_sizes.mmio64_size > 0 && bus_sizes.mmio_align < bus_sizes.mmio64_align) {
285         bus_sizes.mmio_align = bus_sizes.mmio64_align;
286     }
287     bus_sizes.mmio_size += ROUNDUP(bus_sizes.mmio64_size, (1UL << 20));
288     bus_sizes.mmio64_align = 0;
289     bus_sizes.mmio64_size = 0;
290 
291     // prefetchable ranges are sent through like anything else
292     if (bus_sizes.prefetchable_size > 0 && bus_sizes.prefetchable_align < 20) {
293         bus_sizes.prefetchable_align = 20;
294     }
295     bus_sizes.prefetchable_size = ROUNDUP(bus_sizes.prefetchable_size, (1UL << 20));
296 
297     // 64bit prefetchable ranges are sent through like anything else
298     if (bus_sizes.prefetchable64_size > 0 && bus_sizes.prefetchable64_align < 20) {
299         bus_sizes.prefetchable64_align = 20;
300     }
301     bus_sizes.prefetchable64_size = ROUNDUP(bus_sizes.prefetchable64_size, (1UL << 20));
302 
303     *bridge_sizes += bus_sizes;
304 
305     return NO_ERROR;
306 }
307 
compute_bar_sizes(bar_sizes * bridge_sizes)308 status_t bridge::compute_bar_sizes(bar_sizes *bridge_sizes) {
309     char str[14];
310     LTRACEF("bridge at %s\n", pci_loc_string(loc(), str));
311 
312     // accumulate our BARs
313     device::compute_bar_sizes(bridge_sizes);
314 
315     return compute_bar_sizes_no_local_bar(bridge_sizes);
316 }
317 
get_bar_alloc_requests(list_node * bar_alloc_requests)318 status_t bridge::get_bar_alloc_requests(list_node *bar_alloc_requests) {
319     char str[14];
320     LTRACEF("bridge at %s\n", pci_loc_string(loc(), str));
321 
322     // add our local bars to the list of requests
323     device::get_bar_alloc_requests(bar_alloc_requests);
324 
325     // accumulate the size of our children
326     bar_sizes bus_sizes = {};
327     compute_bar_sizes_no_local_bar(&bus_sizes);
328 
329     // TODO: test if bridge supports prefetchable and if so if it supports 64bit
330 
331     // construct a request out of the different ranges returned
332     auto make_request = [this, &bar_alloc_requests](pci_resource_type type, bool prefetchable, uint64_t size, uint8_t align) {
333         if (size > 0) {
334             auto request = new bar_alloc_request;
335             *request = {};
336             request->dev = this;
337             request->bridge = true;
338             request->type = type;
339             request->prefetchable = prefetchable;
340             request->size = size;
341             request->align = align;
342 
343             list_add_tail(bar_alloc_requests, &request->node);
344         }
345     };
346 
347     make_request(PCI_RESOURCE_IO_RANGE, false, bus_sizes.io_size, bus_sizes.io_align);
348     make_request(PCI_RESOURCE_MMIO_RANGE, false, bus_sizes.mmio_size, bus_sizes.mmio_align);
349     make_request(PCI_RESOURCE_MMIO64_RANGE, false, bus_sizes.mmio64_size, bus_sizes.mmio64_align);
350 
351 
352     // TODO: merge prefetchable 64 and 32bit?
353     // should only be able to deal with one or the other
354     DEBUG_ASSERT(!(bus_sizes.prefetchable_size && bus_sizes.prefetchable64_size));
355     make_request(PCI_RESOURCE_MMIO_RANGE, true, bus_sizes.prefetchable_size, bus_sizes.prefetchable_align);
356     make_request(PCI_RESOURCE_MMIO64_RANGE, true, bus_sizes.prefetchable64_size, bus_sizes.prefetchable64_align);
357 
358     return NO_ERROR;
359 }
360 
assign_resource(bar_alloc_request * request,uint64_t address)361 status_t bridge::assign_resource(bar_alloc_request *request, uint64_t address) {
362     char str[14];
363     LTRACEF("bridge at %s resource addr %#" PRIx64 " request:\n", pci_loc_string(loc(), str), address);
364     if (LOCAL_TRACE) {
365         request->dump();
366     }
367 
368     if (!request->bridge) {
369         // this is a request for one of our bars, pass it to the device base class virtual
370         return device::assign_resource(request, address);
371     }
372 
373     DEBUG_ASSERT(IS_ALIGNED(address, (1UL << request->align)));
374 
375     // this is an allocation for one of the bridge resources
376     uint32_t temp;
377     switch (request->type) {
378         case PCI_RESOURCE_IO_RANGE:
379             // write to the io configuration bits
380             DEBUG_ASSERT(IS_ALIGNED(address, (1UL << 12)));
381             temp = ((address >> 8) & 0xf0); // io base
382             temp |= (((address + request->size - 1) >> 8) & 0xf0) << 8;
383             pci_write_config_word(loc(), 0x1c, temp);
384             break;
385         case PCI_RESOURCE_MMIO_RANGE:
386             DEBUG_ASSERT(IS_ALIGNED(address, (1UL << 20)));
387             DEBUG_ASSERT(IS_ALIGNED(request->size, (1UL << 20)));
388             if (request->prefetchable) {
389                 temp = ((address >> 16) & 0xfff0); // mmio base
390                 temp |= ((address + request->size - 1) >> 16 & 0xfff0) << 16;
391                 pci_write_config_word(loc(), 0x24, temp);
392             } else {
393                 // non prefetchable mmio range
394                 temp = ((address >> 16) & 0xfff0); // mmio base
395                 temp |= ((address + request->size - 1) >> 16 & 0xfff0) << 16;
396                 pci_write_config_word(loc(), 0x20, temp);
397             }
398             break;
399         case PCI_RESOURCE_MMIO64_RANGE:
400             if (!request->prefetchable) {
401                 // cannot handle non prefetchable 64bit due to the way the bus works
402                 printf("PCI bridge at %s: invalid 64bit non prefetchable range\n", pci_loc_string(loc(), str));
403                 return ERR_NOT_SUPPORTED;
404             }
405             // assert that the device supports 64bit addresses
406             DEBUG_ASSERT((config_.type1.prefetchable_memory_base & 0xf) == 1);
407 
408             DEBUG_ASSERT(IS_ALIGNED(address, (1UL << 20)));
409             DEBUG_ASSERT(IS_ALIGNED(request->size, (1UL << 20)));
410 
411             // bottom part of prefetchable base and limit
412             temp = ((address >> 16) & 0xfff0); // mmio base
413             temp |= ((address + request->size - 1) >> 16 & 0xfff0) << 16;
414             pci_write_config_word(loc(), 0x24, temp);
415 
416             // high part of base and limit
417             temp = (address >> 32);
418             pci_write_config_word(loc(), 0x28, temp);
419             temp = (address + request->size - 1) >> 32;
420             pci_write_config_word(loc(), 0x2c, temp);
421             break;
422         default:
423             PANIC_UNIMPLEMENTED;
424     }
425     load_config();
426 
427     // PROBLEM: do we recurse here and start the bus allocation for the children with a restricted resource allocator
428     // but we have only a subset of resources to allocate. Or do we wait until the bridge is completely configured,
429     // and then trigger a recursive assignment on each sub bus (this is probably the right strategy).
430 
431 
432     return NO_ERROR;
433 }
434 
assign_child_resources()435 status_t bridge::assign_child_resources() {
436     char str[14];
437     LTRACEF("bridge at %s\n", pci_loc_string(loc(), str));
438 
439     // construct a resource allocator that covers what we've been assigned
440     resource_allocator allocator;
441 
442     auto io = io_range();
443     if (io.limit > io.base) {
444         resource_range r;
445         r.base = io.base;
446         r.size = io.limit + io.base + 1;
447         r.type = PCI_RESOURCE_IO_RANGE;
448         allocator.set_range(r);
449     }
450 
451     auto mmio = mem_range();
452     if (mmio.limit > mmio.base) {
453         resource_range r;
454         r.base = mmio.base;
455         r.size = mmio.limit - mmio.base + 1;
456         r.type = PCI_RESOURCE_MMIO_RANGE;
457         allocator.set_range(r);
458     }
459 
460     auto pref = prefetch_range();
461     if (pref.limit > pref.base) {
462         resource_range r;
463         r.base = pref.base;
464         r.size = pref.limit - pref.base + 1;
465 
466         // if the prefetch window is completely < 4GB, set it up as a 32bit mmio prefetchable
467         if (pref.base < (1ULL << 32) || (pref.base + pref.limit < (1ULL << 32))) {
468             r.type = PCI_RESOURCE_MMIO_RANGE;
469         } else {
470             r.type = PCI_RESOURCE_MMIO64_RANGE;
471         }
472 
473         allocator.set_range(r, true);
474     }
475 
476     // recurse into the secondary bus with the allocator we've set up for it
477     secondary_bus_->allocate_resources(allocator);
478 
479     // enable the bridge
480     enable();
481 
482     return NO_ERROR;
483 }
484 
485 
486 } // namespace pci
487