[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:
@@ -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();
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_ = {};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_; }
|
||||
|
||||
Reference in New Issue
Block a user