[bus][pci] first stab at assigning bus numbers to unconfigured bridges

Had to rearrange the proble logic somewhat, but now the bus manager will
assign consequtive bus numbers as it finds bridges and recursively
drills down to discover the entire bus structure.

Does not assign resources yet, but need to do this in the first pass
to find all of the devices before can figure out how much space they
occupy.
This commit is contained in:
Travis Geiselbrecht
2022-01-28 02:54:46 -08:00
parent ba21aa9236
commit d5f394859d
8 changed files with 211 additions and 77 deletions

View File

@@ -22,6 +22,7 @@
#define LOCAL_TRACE 0
#include "bus_mgr.h"
#include "bridge.h"
#include "bus.h"
@@ -54,11 +55,18 @@ status_t bridge::probe(pci_location_t loc, bus *parent_bus, bridge **out_bridge)
return ERR_NOT_FOUND;
}
header_type &= PCI_HEADER_TYPE_MASK;
if (header_type != 1) {
LTRACEF("type %d header on bridge we don't understand, skipping\n", header_type);
return ERR_NOT_FOUND;
}
// we are a bridge to a new set of busses
bridge *br = new bridge(loc, parent_bus);
// we only grok type 1 headers here
err = pci_read_config(loc, &br->config_);
err = br->load_config();
if (err < 0) {
delete br;
return err;
@@ -72,29 +80,88 @@ status_t bridge::probe(pci_location_t loc, bus *parent_bus, bridge **out_bridge)
*out_bridge = br;
if (br->secondary_bus() > 0 && br->subordinate_bus() >= br->secondary_bus()) {
// start a scan of the secondary bus downstream of this.
// via bridge devices on this bus, should find all of the subordinate busses.
bus *new_bus;
pci_location_t bus_location = {};
bus_location.segment = loc.segment;
bus_location.bus = br->config_.type1.secondary_bus;
err = bus::probe(bus_location, br, &new_bus);
if (err < 0) {
return err;
if (br->secondary_bus() == 0) {
// allocate a new secondary bus
uint8_t new_secondary_bus = allocate_next_bus();
// we do not yet know the range of busses we will find downstream, lower level bridges will have to
// back patch us as they find new children.
uint8_t new_subordinate_bus = new_secondary_bus;
LTRACEF("assigning secondary bus %d, parent bus %d\n", new_secondary_bus, parent_bus->bus_num());
br->assign_bus_numbers(parent_bus->bus_num(), new_secondary_bus, new_subordinate_bus);
// tell the parent bridge that we have a new range
auto *parent_bridge = parent_bus->get_bridge();
if (parent_bridge) {
parent_bridge->extend_subordinate_range(new_secondary_bus);
}
// add the bus to our list of children
DEBUG_ASSERT(new_bus);
br->add_bus(new_bus);
// add the bus to the global bus list
new_bus->add_to_global_list();
}
// sanity check that we don't have overlapping busses
if (br->secondary_bus() < get_last_bus()) {
TRACEF("secondary bus %u of bridge we've already seen (last bus seen %u)\n", br->secondary_bus(), get_last_bus());
delete br;
return ERR_NO_RESOURCES;
}
// start a scan of the secondary bus downstream of this.
// via bridge devices on this bus, should find all of the subordinate busses.
pci_location_t bus_location = {};
bus_location.segment = loc.segment;
bus_location.bus = br->secondary_bus();
bus *new_bus;
err = bus::probe(bus_location, br, &new_bus);
if (err != NO_ERROR) {
// TODO: don't leak bridge and/or bus
return err;
}
// add the bus to our list of children
DEBUG_ASSERT(new_bus);
br->add_bus(new_bus);
// add the bus to the global bus list
new_bus->add_to_global_list();
return NO_ERROR;
}
void bridge::extend_subordinate_range(uint8_t new_secondary_bus) {
LTRACEF("new_secondary_bus %u existing primary bus %hhd secondary %hhd subordinate %hhd\n",
new_secondary_bus, primary_bus(), secondary_bus(), subordinate_bus());
if (new_secondary_bus > subordinate_bus()) {
assign_bus_numbers(primary_bus(), secondary_bus(), new_secondary_bus);
DEBUG_ASSERT(subordinate_bus() == new_secondary_bus);
// tell the parent bridge that we have a new range
auto *parent_bridge = bus_->get_bridge();
if (parent_bridge) {
parent_bridge->extend_subordinate_range(new_secondary_bus);
}
}
}
void bridge::assign_bus_numbers(uint8_t primary, uint8_t secondary, uint8_t subordinate) {
LTRACEF("primary %u secondary %u subordinate %u\n", primary, secondary, subordinate);
uint32_t temp;
pci_read_config_word(loc_, 0x18, &temp);
temp &= 0xff000000; // leave latency timer alone
temp |= subordinate << 16;
temp |= secondary << 8;
temp |= primary << 0;
pci_write_config_word(loc_, 0x18, temp);
// reread the config
load_config();
}
void bridge::dump(size_t indent) {
auto scoot = [&]() {
for (size_t i = 0; i < indent; i++) {
@@ -104,9 +171,9 @@ void bridge::dump(size_t indent) {
};
char str[14];
scoot();
printf("bridge %s %04hx:%04hx child busses [%d..%d]\n", pci_loc_string(loc_, str),
config_.vendor_id, config_.device_id,
config_.type1.secondary_bus, config_.type1.subordinate_bus);
printf("bridge %s %04hx:%04hx primary bus %d child busses [%d..%d]\n", pci_loc_string(loc_, str),
vendor_id(), device_id(), primary_bus(),
secondary_bus(), subordinate_bus());
auto mr = mem_range();
auto ir = io_range();

View File

@@ -25,7 +25,13 @@ public:
DISALLOW_COPY_ASSIGN_AND_MOVE(bridge);
static status_t probe(pci_location_t loc, bus *bus, bridge **out_bridge);
static status_t probe(pci_location_t loc, bus *parent_bus, bridge **out_bridge);
// called when a sub bridge is assigned a new secondary bus.
// if this extends our subordinate bus, recursively call the parent bridge.
void extend_subordinate_range(uint8_t new_secondary_bus);
void assign_bus_numbers(uint8_t primary, uint8_t secondary, uint8_t subordinate_bus);
void add_bus(bus *b) { secondary_bus_ = b; }

View File

@@ -35,34 +35,89 @@ void bus::add_device(device *d) {
}
// walk all devices on a bus recursively walking into any bridge and scanning those busses
status_t bus::probe(pci_location_t loc, bridge *bridge, bus **out_bus) {
status_t bus::probe(pci_location_t loc, bridge *br, bus **out_bus) {
char str[14];
LTRACEF("%s\n", pci_loc_string(loc, str));
status_t err;
// create a bus to hold any devices we find
bus *b = new bus(loc, bridge);
bus *b = new bus(loc, br);
// mark this as at least the last we've seen
set_last_bus(b->bus_num());
// probe all functions on all 32 devices on this bus.
// add any devices found to this bus
for (uint dev = 0; dev < 32; dev++) {
loc.dev = dev;
for (uint fn = 0; fn < 8; fn++) {
// loop all 8 functions, but only if after seeing the multifunction bit set
// in the header type on fn 0. Otherwise just try fn 0 and stop.
bool possibly_multifunction = false;
for (uint fn = 0; (fn == 0) || (possibly_multifunction && fn < 8); fn++) {
loc.fn = fn;
device *d;
bool multifunction;
err = device::probe(loc, b, &d, &multifunction);
if (err < 0) {
break;
// see if there's something at this slot
// read vendor id and see if this is a real device
uint16_t vendor_id;
err = pci_read_config_half(loc, PCI_CONFIG_VENDOR_ID, &vendor_id);
if (err != NO_ERROR || vendor_id == 0xffff) {
continue;
}
b->add_device(d);
LTRACEF("something at %s\n", pci_loc_string(loc, str));
// move on to the next device
if (fn == 0 && !multifunction) {
break;
// read base and sub class
uint8_t base_class;
err = pci_read_config_byte(loc, PCI_CONFIG_CLASS_CODE_BASE, &base_class);
if (err != NO_ERROR) {
continue;
}
uint8_t sub_class;
err = pci_read_config_byte(loc, PCI_CONFIG_CLASS_CODE_SUB, &sub_class);
if (err != NO_ERROR) {
continue;
}
// read header type (0 or 1)
uint8_t header_type;
err = pci_read_config_byte(loc, PCI_CONFIG_HEADER_TYPE, &header_type);
if (err != NO_ERROR) {
continue;
}
// is it multifunction?
if (loc.fn == 0 && header_type & PCI_HEADER_TYPE_MULTI_FN) {
possibly_multifunction = true;
LTRACEF_LEVEL(2, "possibly multifunction\n");
}
header_type &= PCI_HEADER_TYPE_MASK;
LTRACEF_LEVEL(2, "base:sub class %#hhx:%hhx\n", base_class, sub_class);
// if it's a bridge, probe that
// PCI-PCI bridge, normal decode
if (base_class == 0x6 && sub_class == 0x4) { // XXX replace with #define
LTRACEF("found bridge, recursing\n");
err = bridge::probe(loc, b, &br);
if (err != NO_ERROR) {
continue;
}
DEBUG_ASSERT(br);
b->add_device(br);
} else {
device *d;
err = device::probe(loc, b, &d);
if (err != NO_ERROR) {
continue;
}
DEBUG_ASSERT(d);
b->add_device(d);
}
}
}

View File

@@ -31,10 +31,10 @@ public:
uint bus_num() const { return loc().bus; }
void add_device(device *d);
void dump(size_t indent = 0);
list_node *list_node_ptr() { return &node; }
const bridge *get_bridge() const { return b_; }
bridge *get_bridge() { return b_; }
template <typename F>
status_t for_every_device(F func);
@@ -43,6 +43,7 @@ public:
// master list of busses for easy iteration
list_node node = LIST_INITIAL_CLEARED_VALUE;
list_node *list_node_ptr() { return &node; }
private:
pci_location_t loc_ = {};

View File

@@ -24,18 +24,35 @@
#include "bus.h"
#include "bridge.h"
#define LOCAL_TRACE 0
#define LOCAL_TRACE 1
// root of the pci bus
namespace pci {
bus *root = nullptr;
list_node bus_list = LIST_INITIAL_VALUE(bus_list);
uint8_t last_bus = 0;
// used by bus object to stuff itself into a global list
void add_to_bus_list(bus *b) {
list_add_tail(&bus_list, b->list_node_ptr());
}
void set_last_bus(uint8_t bus) {
DEBUG_ASSERT(bus >= last_bus);
last_bus = bus;
}
// allocate the next bus (used when assigning busses to bridges)
uint8_t allocate_next_bus() {
return ++last_bus;
}
uint8_t get_last_bus() {
return last_bus;
}
namespace {
// helper routines

View File

@@ -10,10 +10,19 @@
#include <sys/types.h>
#include <dev/bus/pci.h>
// global state
namespace pci {
// global state
class bus;
extern bus *root;
extern list_node bus_list;
void add_to_bus_list(bus *b);
// set the last bus seen
void set_last_bus(uint8_t bus);
uint8_t get_last_bus();
// allocate the next bus (used when assigning busses to bridges)
uint8_t allocate_next_bus();
}

View File

@@ -37,14 +37,13 @@ device::~device() {
}
}
// probe the device, return a new node and a bool if it's a multifunction device or not
status_t device::probe(pci_location_t loc, bus *parent_bus, device **out_device, bool *out_multifunction) {
// probe the device, return a new device
status_t device::probe(pci_location_t loc, bus *parent_bus, device **out_device) {
status_t err;
*out_device = nullptr;
*out_multifunction = false;
// read vendor id and see if this is a real device
// read vendor id and make sure this
uint16_t vendor_id;
err = pci_read_config_half(loc, PCI_CONFIG_VENDOR_ID, &vendor_id);
if (err != NO_ERROR) {
@@ -54,9 +53,6 @@ status_t device::probe(pci_location_t loc, bus *parent_bus, device **out_device,
return ERR_NOT_FOUND;
}
char str[14];
LTRACEF("something at %s\n", pci_loc_string(loc, str));
// read base and sub class
uint8_t base_class;
err = pci_read_config_byte(loc, PCI_CONFIG_CLASS_CODE_BASE, &base_class);
@@ -76,49 +72,29 @@ status_t device::probe(pci_location_t loc, bus *parent_bus, device **out_device,
return ERR_NOT_FOUND;
}
// is it multifunction?
bool possibly_multifunction = false;
if (loc.fn == 0 && header_type & PCI_HEADER_TYPE_MULTI_FN) {
possibly_multifunction = true;
LTRACEF_LEVEL(2, "possibly multifunction\n");
}
header_type &= PCI_HEADER_TYPE_MASK;
LTRACEF_LEVEL(2, "base:sub class %#hhx:%hhx\n", base_class, sub_class);
if (header_type != 0) {
LTRACEF("type %d header on device we don't understand, skipping\n", header_type);
return ERR_NOT_FOUND;
}
// if it's a bridge, probe that
// if it's a bridge, we should not have been called
if (base_class == 0x6) { // XXX replace with #define
// bridge
if (sub_class == 0x4) { // PCI-PCI bridge, normal decode
LTRACEF("found bridge, recursing\n");
bridge *out_bridge;
err = bridge::probe(loc, parent_bus, &out_bridge);
if (err != NO_ERROR) {
return err;
}
DEBUG_ASSERT(out_bridge);
*out_device = out_bridge;
out_bridge->load_bars();
return err;
LTRACEF("found bridge, error\n");
return ERR_NOT_SUPPORTED;
}
}
LTRACEF_LEVEL(2, "type %#hhx\n", header_type);
if (header_type != 0) {
LTRACEF("type %d header on bridge we don't understand, skipping\n", header_type);
return ERR_NOT_FOUND;
}
// create a new device and pass it up
device *d = new device(loc, parent_bus);
// try to read in the basic config space for this device
err = pci_read_config(loc, &d->config_);
err = d->load_config();
if (err < 0) {
delete d;
return err;
@@ -130,9 +106,6 @@ status_t device::probe(pci_location_t loc, bus *parent_bus, device **out_device,
// probe the device's capabilities
d->probe_capabilities();
// we know we're a device at this point, set multifunction or not
*out_multifunction = possibly_multifunction;
// return the newly constructed device
*out_device = d;
@@ -417,4 +390,9 @@ status_t device::read_bars(pci_bar_t bar[6]) {
return NO_ERROR;
}
status_t device::load_config() {
status_t err = pci_read_config(loc_, &config_);
return err;
}
} // namespace pci

View File

@@ -24,7 +24,7 @@ public:
DISALLOW_COPY_ASSIGN_AND_MOVE(device);
static status_t probe(pci_location_t loc, bus *bus, device **out_device, bool *out_multifunction);
static status_t probe(pci_location_t loc, bus *bus, device **out_device);
status_t probe_capabilities();
status_t init_msi_capability(capability *cap);
@@ -32,6 +32,7 @@ public:
status_t allocate_irq(uint *irq);
status_t allocate_msi(size_t num_requested, uint *msi_base);
status_t load_config();
status_t load_bars();
pci_location_t loc() const { return loc_; }