From f1431b81d0318b1c33d9ef01268efb773a93217f Mon Sep 17 00:00:00 2001 From: Travis Geiselbrecht Date: Sun, 30 Jan 2022 15:12:38 -0800 Subject: [PATCH] [bus][pci] Support for dynamically assigning BARs and bridges if needed In the case of platforms where a bios or firmware has not already assigned all the resources, do so. Requires the platform supply one or more ranges of physical address space and IO that can be mapped into BARs. Handles iterating through bridges, computing the sizes of all the peripherals downstream and rolling that up as well. --- dev/bus/pci/bus_mgr/bridge.cpp | 251 +++++++++++++++++- dev/bus/pci/bus_mgr/bridge.h | 11 +- dev/bus/pci/bus_mgr/bus.cpp | 141 +++++++++- dev/bus/pci/bus_mgr/bus.h | 10 +- dev/bus/pci/bus_mgr/bus_mgr.cpp | 155 ++++++++--- dev/bus/pci/bus_mgr/bus_mgr.h | 9 +- dev/bus/pci/bus_mgr/device.cpp | 185 ++++++++++++- dev/bus/pci/bus_mgr/device.h | 78 ++++++ dev/bus/pci/bus_mgr/resource.cpp | 98 +++++++ dev/bus/pci/bus_mgr/resource.h | 60 +++++ dev/bus/pci/include/dev/bus/pci.h | 9 +- dev/bus/pci/rules.mk | 1 + dev/interrupt/arm_gic/arm_gic.c | 5 +- platform/include/platform/interrupts.h | 7 +- platform/pc/interrupts.c | 18 +- .../include/platform/qemu-virt.h | 7 +- platform/qemu-virt-arm/platform.c | 74 +++++- platform/qemu-virt-riscv/platform.c | 12 +- platform/qemu-virt-riscv/plic.c | 7 +- 19 files changed, 1058 insertions(+), 80 deletions(-) create mode 100644 dev/bus/pci/bus_mgr/resource.cpp create mode 100644 dev/bus/pci/bus_mgr/resource.h diff --git a/dev/bus/pci/bus_mgr/bridge.cpp b/dev/bus/pci/bus_mgr/bridge.cpp index 1dc82751..0d199288 100644 --- a/dev/bus/pci/bus_mgr/bridge.cpp +++ b/dev/bus/pci/bus_mgr/bridge.cpp @@ -15,7 +15,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -25,6 +27,7 @@ #include "bus_mgr.h" #include "bridge.h" #include "bus.h" +#include "resource.h" namespace pci { @@ -179,7 +182,7 @@ void bridge::dump(size_t indent) { auto ir = io_range(); auto pr = prefetch_range(); scoot(); - printf("mem_range [%#x..%#x] io_range [%#x..%#x] pref_range [%#llx..%#llx] \n", + printf("mem_range [%#x..%#x] io_range [%#x..%#x] pref_range [%#" PRIx64 "..%#" PRIx64"] \n", mr.base, mr.limit, ir.base, ir.limit, pr.base, pr.limit); for (size_t b = 0; b < 2; b++) { @@ -187,7 +190,7 @@ void bridge::dump(size_t indent) { for (size_t i = 0; i < indent + 1; i++) { printf(" "); } - printf("BAR %zu: addr %#llx size %#zx io %d valid %d\n", b, bars_[b].addr, bars_[b].size, bars_[b].io, bars_[b].valid); + 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); } } @@ -236,4 +239,248 @@ bridge::range bridge::prefetch_range() { } } +status_t bridge::compute_bar_sizes_no_local_bar(bar_sizes *bridge_sizes) { + bar_sizes bus_sizes = {}; + + // drill into our bus and accumulate from there + auto perdev = [&](device *d) -> status_t { + device::bar_sizes sizes = {}; + + d->compute_bar_sizes(&sizes); + + if (LOCAL_TRACE) { + char str[14]; + 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", + pci_loc_string(d->loc(), str), sizes.io_size, sizes.io_align, sizes.mmio_size, sizes.mmio_align, + sizes.mmio64_size, sizes.mmio64_align, sizes.prefetchable_size, sizes.prefetchable_align, + sizes.prefetchable64_size, sizes.prefetchable64_align); + } + + // accumulate to the passed in size struct + bus_sizes += sizes; + + return 0; + }; + + LTRACEF("recursing into bus %u\n", secondary_bus_->bus_num()); + secondary_bus_->for_every_device(perdev); + + // we've accumulated the size of the bus and everything below it + // round up some of the accumulated sizes based on limitations in the bridge + + // bridges can only pass through io ports in blocks of 0x1000 + if (bus_sizes.io_size > 0 && bus_sizes.io_align < 12) { + bus_sizes.io_align = 12; + } + bus_sizes.io_size = ROUNDUP(bus_sizes.io_size, 0x1000); + + // mmio ranges can only pass through in units of 1MB (1<<20) + if (bus_sizes.mmio_size > 0 && bus_sizes.mmio_align < 20) { + bus_sizes.mmio_align = 20; + } + bus_sizes.mmio_size = ROUNDUP(bus_sizes.mmio_size, (1UL << 20)); + + // 64bit mmio ranges can't be passed through bridges, so merge with the mmio range + if (bus_sizes.mmio64_size > 0 && bus_sizes.mmio_align < bus_sizes.mmio64_align) { + bus_sizes.mmio_align = bus_sizes.mmio64_align; + } + bus_sizes.mmio_size += ROUNDUP(bus_sizes.mmio64_size, (1UL << 20)); + bus_sizes.mmio64_align = 0; + bus_sizes.mmio64_size = 0; + + // prefetchable ranges are sent through like anything else + if (bus_sizes.prefetchable_size > 0 && bus_sizes.prefetchable_align < 20) { + bus_sizes.prefetchable_align = 20; + } + bus_sizes.prefetchable_size = ROUNDUP(bus_sizes.prefetchable_size, (1UL << 20)); + + // 64bit prefetchable ranges are sent through like anything else + if (bus_sizes.prefetchable64_size > 0 && bus_sizes.prefetchable64_align < 20) { + bus_sizes.prefetchable64_align = 20; + } + bus_sizes.prefetchable64_size = ROUNDUP(bus_sizes.prefetchable64_size, (1UL << 20)); + + *bridge_sizes += bus_sizes; + + return NO_ERROR; +} + +status_t bridge::compute_bar_sizes(bar_sizes *bridge_sizes) { + char str[14]; + LTRACEF("bridge at %s\n", pci_loc_string(loc(), str)); + + // accumulate our BARs + device::compute_bar_sizes(bridge_sizes); + + return compute_bar_sizes_no_local_bar(bridge_sizes); +} + +status_t bridge::get_bar_alloc_requests(list_node *bar_alloc_requests) { + char str[14]; + LTRACEF("bridge at %s\n", pci_loc_string(loc(), str)); + + // add our local bars to the list of requests + device::get_bar_alloc_requests(bar_alloc_requests); + + // accumulate the size of our children + bar_sizes bus_sizes = {}; + compute_bar_sizes_no_local_bar(&bus_sizes); + + // TODO: test if bridge supports prefetchable and if so if it supports 64bit + + // construct a request out of the different ranges returned + auto make_request = [this, &bar_alloc_requests](pci_resource_type type, bool prefetchable, uint64_t size, uint8_t align) { + if (size > 0) { + auto request = new bar_alloc_request; + *request = {}; + request->dev = this; + request->bridge = true; + request->type = type; + request->prefetchable = prefetchable; + request->size = size; + request->align = align; + + list_add_tail(bar_alloc_requests, &request->node); + } + }; + + make_request(PCI_RESOURCE_IO_RANGE, false, bus_sizes.io_size, bus_sizes.io_align); + make_request(PCI_RESOURCE_MMIO_RANGE, false, bus_sizes.mmio_size, bus_sizes.mmio_align); + make_request(PCI_RESOURCE_MMIO64_RANGE, false, bus_sizes.mmio64_size, bus_sizes.mmio64_align); + + + // TODO: merge prefetchable 64 and 32bit? + // should only be able to deal with one or the other + DEBUG_ASSERT(!(bus_sizes.prefetchable_size && bus_sizes.prefetchable64_size)); + make_request(PCI_RESOURCE_MMIO_RANGE, true, bus_sizes.prefetchable_size, bus_sizes.prefetchable_align); + make_request(PCI_RESOURCE_MMIO64_RANGE, true, bus_sizes.prefetchable64_size, bus_sizes.prefetchable64_align); + + return NO_ERROR; +} + +status_t bridge::assign_resource(bar_alloc_request *request, uint64_t address) { + char str[14]; + LTRACEF("bridge at %s resource addr %#" PRIx64 " request:\n", pci_loc_string(loc(), str), address); + if (LOCAL_TRACE) { + request->dump(); + } + + if (!request->bridge) { + // this is a request for one of our bars, pass it to the device base class virtual + return device::assign_resource(request, address); + } + + DEBUG_ASSERT(IS_ALIGNED(address, (1UL << request->align))); + + // this is an allocation for one of the bridge resources + uint32_t temp; + switch (request->type) { + case PCI_RESOURCE_IO_RANGE: + // write to the io configuration bits + DEBUG_ASSERT(IS_ALIGNED(address, (1UL << 12))); + temp = ((address >> 8) & 0xf0); // io base + temp |= (((address + request->size - 1) >> 8) & 0xf0) << 8; + pci_write_config_word(loc(), 0x1c, temp); + break; + case PCI_RESOURCE_MMIO_RANGE: + DEBUG_ASSERT(IS_ALIGNED(address, (1UL << 20))); + DEBUG_ASSERT(IS_ALIGNED(request->size, (1UL << 20))); + if (request->prefetchable) { + temp = ((address >> 16) & 0xfff0); // mmio base + temp |= ((address + request->size - 1) >> 16 & 0xfff0) << 16; + pci_write_config_word(loc(), 0x24, temp); + } else { + // non prefetchable mmio range + temp = ((address >> 16) & 0xfff0); // mmio base + temp |= ((address + request->size - 1) >> 16 & 0xfff0) << 16; + pci_write_config_word(loc(), 0x20, temp); + } + break; + case PCI_RESOURCE_MMIO64_RANGE: + if (!request->prefetchable) { + // cannot handle non prefetchable 64bit due to the way the bus works + printf("PCI bridge at %s: invalid 64bit non prefetchable range\n", pci_loc_string(loc(), str)); + return ERR_NOT_SUPPORTED; + } + // assert that the device supports 64bit addresses + DEBUG_ASSERT((config_.type1.prefetchable_memory_base & 0xf) == 1); + + DEBUG_ASSERT(IS_ALIGNED(address, (1UL << 20))); + DEBUG_ASSERT(IS_ALIGNED(request->size, (1UL << 20))); + + // bottom part of prefetchable base and limit + temp = ((address >> 16) & 0xfff0); // mmio base + temp |= ((address + request->size - 1) >> 16 & 0xfff0) << 16; + pci_write_config_word(loc(), 0x24, temp); + + // high part of base and limit + temp = (address >> 32); + pci_write_config_word(loc(), 0x28, temp); + temp = (address + request->size - 1) >> 32; + pci_write_config_word(loc(), 0x2c, temp); + break; + default: + PANIC_UNIMPLEMENTED; + } + load_config(); + + // PROBLEM: do we recurse here and start the bus allocation for the children with a restricted resource allocator + // but we have only a subset of resources to allocate. Or do we wait until the bridge is completely configured, + // and then trigger a recursive assignment on each sub bus (this is probably the right strategy). + + + return NO_ERROR; +} + +status_t bridge::assign_child_resources() { + char str[14]; + LTRACEF("bridge at %s\n", pci_loc_string(loc(), str)); + + // construct a resource allocator that covers what we've been assigned + resource_allocator allocator; + + auto io = io_range(); + if (io.limit > io.base) { + resource_range r; + r.base = io.base; + r.size = io.limit + io.base + 1; + r.type = PCI_RESOURCE_IO_RANGE; + allocator.set_range(r); + } + + auto mmio = mem_range(); + if (mmio.limit > mmio.base) { + resource_range r; + r.base = mmio.base; + r.size = mmio.limit - mmio.base + 1; + r.type = PCI_RESOURCE_MMIO_RANGE; + allocator.set_range(r); + } + + auto pref = prefetch_range(); + if (pref.limit > pref.base) { + resource_range r; + r.base = pref.base; + r.size = pref.limit - pref.base + 1; + + // if the prefetch window is completely < 4GB, set it up as a 32bit mmio prefetchable + if (pref.base < (1ULL << 32) || (pref.base + pref.limit < (1ULL << 32))) { + r.type = PCI_RESOURCE_MMIO_RANGE; + } else { + r.type = PCI_RESOURCE_MMIO64_RANGE; + } + + allocator.set_range(r, true); + } + + // recurse into the secondary bus with the allocator we've set up for it + secondary_bus_->allocate_resources(allocator); + + // enable the bridge + enable(); + + return NO_ERROR; +} + + } // namespace pci diff --git a/dev/bus/pci/bus_mgr/bridge.h b/dev/bus/pci/bus_mgr/bridge.h index 658201e8..ac656146 100644 --- a/dev/bus/pci/bus_mgr/bridge.h +++ b/dev/bus/pci/bus_mgr/bridge.h @@ -19,8 +19,10 @@ class bus; // bridge device, holds a list of busses that it is responsible for class bridge : public device { -public: +protected: + // created via the probe() call bridge(pci_location_t loc, bus *bus); +public: ~bridge() override; DISALLOW_COPY_ASSIGN_AND_MOVE(bridge); @@ -35,6 +37,11 @@ public: void add_bus(bus *b) { secondary_bus_ = b; } + status_t compute_bar_sizes(bar_sizes *sizes) override; + status_t get_bar_alloc_requests(list_node *bar_alloc_requests) override; + status_t assign_resource(bar_alloc_request *request, uint64_t address) override; + status_t assign_child_resources() override; + void dump(size_t indent = 0) override; // config accessors @@ -53,6 +60,8 @@ public: range prefetch_range(); private: + status_t compute_bar_sizes_no_local_bar(bar_sizes *sizes); + bus *secondary_bus_ = nullptr; }; diff --git a/dev/bus/pci/bus_mgr/bus.cpp b/dev/bus/pci/bus_mgr/bus.cpp index a8f730b1..0ededd85 100644 --- a/dev/bus/pci/bus_mgr/bus.cpp +++ b/dev/bus/pci/bus_mgr/bus.cpp @@ -24,10 +24,11 @@ #include "device.h" #include "bus_mgr.h" +#include "resource.h" namespace pci { -bus::bus(pci_location_t loc, bridge *b) : loc_(loc), b_(b) {} +bus::bus(pci_location_t loc, bridge *b, bool root_bus) : loc_(loc), b_(b), root_bus_(root_bus) {} void bus::add_device(device *d) { // TODO: assert that no two devices have the same address @@ -35,14 +36,14 @@ 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 *br, bus **out_bus) { +status_t bus::probe(pci_location_t loc, bridge *br, bus **out_bus, bool root_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, br); + bus *b = new bus(loc, br, root_bus); // mark this as at least the last we've seen set_last_bus(b->bus_num()); @@ -127,6 +128,140 @@ status_t bus::probe(pci_location_t loc, bridge *br, bus **out_bus) { return NO_ERROR; } +status_t bus::allocate_resources(resource_allocator &allocator) { + LTRACEF("bus %u\n", bus_num()); + +#if 0 + { + auto perdev = [](device *d) -> status_t { + device::bar_sizes sizes = {}; + + d->compute_bar_sizes(&sizes); + + char str[14]; + printf("bar sizes for device %s : io %#x align %u mmio %#x align %u mmio64 %#llx align %u prefetch %#llx align %u\n", + pci_loc_string(d->loc(), str), sizes.io_size, sizes.io_align, sizes.mmio_size, sizes.mmio_align, + sizes.mmio64_size, sizes.mmio64_align, sizes.prefetchable_size, sizes.prefetchable_align); + + return 0; + }; + + for_every_device(perdev); + } +#endif + + // accumulate a list of allocation requests for all devices on this bus + list_node alloc_requests; + list_initialize(&alloc_requests); + auto perdev = [&](device *d) -> status_t { + d->get_bar_alloc_requests(&alloc_requests); + return 0; + }; + for_every_device(perdev); + + auto dump_list = [](list_node *list) { + device::bar_alloc_request *r; + list_for_every_entry(list, r, device::bar_alloc_request, node) { + r->dump(); + } + }; + + // split the list into different allocations and sort by size + auto split_and_sort_list = [](pci_resource_type type, list_node *main_list, list_node *out_list) { + device::bar_alloc_request *r, *temp; + list_for_every_entry_safe(main_list, r, temp, device::bar_alloc_request, node) { + if (r->type == type) { + list_delete(&r->node); + + // add it to the destination list, sorted by size + bool added = false; + device::bar_alloc_request *temp2; + list_for_every_entry(out_list, temp2, device::bar_alloc_request, node) { + if (r->size > temp2->size) { + list_add_before(&temp2->node, &r->node); + added = true; + break; + } + } + if (!added) { + list_add_tail(out_list, &r->node); + } + } + } + }; + + // create a sorted list of all io requests + list_node io_requests; + list_initialize(&io_requests); + split_and_sort_list(PCI_RESOURCE_IO_RANGE, &alloc_requests, &io_requests); + + if (LOCAL_TRACE) { + printf("IO requests for devices on bus %d:\n", bus_num()); + dump_list(&io_requests); + } + + // create a sorted list of all mmio requests, 32, 64 and prefetchable combined + list_node mmio_requests; + list_initialize(&mmio_requests); + + split_and_sort_list(PCI_RESOURCE_MMIO_RANGE, &alloc_requests, &mmio_requests); + split_and_sort_list(PCI_RESOURCE_MMIO64_RANGE, &alloc_requests, &mmio_requests); + + if (LOCAL_TRACE) { + printf("MMIO combined requests on bus %d:\n", bus_num()); + dump_list(&mmio_requests); + } + + // allocate and assign IO ranges + device::bar_alloc_request *r, *temp; + list_for_every_entry_safe(&io_requests, r, temp, device::bar_alloc_request, node) { + uint32_t addr = 0; + auto err = allocator.allocate_io(r->size, r->align, &addr); + if (err != NO_ERROR) { + TRACEF("ERR: error allocating resource\n"); + r->dump(); + continue; + } + + DEBUG_ASSERT(r->dev); + + r->dev->assign_resource(r, addr); + list_delete(&r->node); + delete r; + } + + // allocate and assign various kinds of mmio ranges + list_for_every_entry_safe(&mmio_requests, r, temp, device::bar_alloc_request, node) { + uint64_t addr = 0; + auto type = r->type; + const bool can_be_64bit = (type == PCI_RESOURCE_MMIO64_RANGE); + + // root busses dont need to worry about allocating from a prefetchable pool + // in our resource allocator, so ask for non prefetchable memory + const bool prefetchable = root_bus_ ? false : r->prefetchable; + + auto err = allocator.allocate_mmio(can_be_64bit, prefetchable, r->size, r->align, &addr); + if (err != NO_ERROR) { + // failed with a 32bit alloc + panic("failed to allocate resource\n"); + } + + DEBUG_ASSERT(r->dev); + + r->dev->assign_resource(r, addr); + list_delete(&r->node); + delete r; + } + + // instruct all the devices on the bus that may have children (bridges) to assign resources + for_every_device([](device *d) -> status_t { + d->assign_child_resources(); + return 0; + }); + + return NO_ERROR; +} + void bus::dump(size_t indent) { for (size_t i = 0; i < indent; i++) { printf(" "); diff --git a/dev/bus/pci/bus_mgr/bus.h b/dev/bus/pci/bus_mgr/bus.h index 2cd645ab..4aaa943e 100644 --- a/dev/bus/pci/bus_mgr/bus.h +++ b/dev/bus/pci/bus_mgr/bus.h @@ -17,15 +17,20 @@ namespace pci { +class resource_allocator; + // bus device holds a list of devices and a reference to its bridge device class bus { public: - bus(pci_location_t loc, bridge *b); + bus(pci_location_t loc, bridge *b, bool root_bus = false); ~bus() = default; DISALLOW_COPY_ASSIGN_AND_MOVE(bus); - static status_t probe(pci_location_t loc, bridge *bridge, bus **out_bus); + static status_t probe(pci_location_t loc, bridge *bridge, bus **out_bus, bool root_bus = false); + + // allocate resources for devices on this bus and recursively all of its children + status_t allocate_resources(resource_allocator &allocator); pci_location_t loc() const { return loc_; } uint bus_num() const { return loc().bus; } @@ -49,6 +54,7 @@ private: pci_location_t loc_ = {}; bridge *b_ = nullptr; list_node child_devices_ = LIST_INITIAL_VALUE(child_devices_); + const bool root_bus_ = false; // changes some of the allocation behavior }; // call the provided functor on every device in this bus diff --git a/dev/bus/pci/bus_mgr/bus_mgr.cpp b/dev/bus/pci/bus_mgr/bus_mgr.cpp index 3d70dbea..d64a1aa6 100644 --- a/dev/bus/pci/bus_mgr/bus_mgr.cpp +++ b/dev/bus/pci/bus_mgr/bus_mgr.cpp @@ -23,39 +23,24 @@ #include "device.h" #include "bus.h" #include "bridge.h" +#include "resource.h" -#define LOCAL_TRACE 1 +#define LOCAL_TRACE 0 + +// global state of the pci bus manager +namespace pci { // 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; -} +resource_allocator resources; namespace { -// helper routines +// local helper routines // iterate all devices on all busses with the functor template status_t for_every_device_on_every_bus(F func) { @@ -71,6 +56,20 @@ status_t for_every_device_on_every_bus(F func) { return err; } +template +status_t for_every_bus(F func) { + status_t err = NO_ERROR; + + bus *b; + list_for_every_entry(&bus_list, b, bus, node) { + err = func(b); + if (err != NO_ERROR) { + return err; + } + } + return err; +} + // return a pointer to the device that matches a particular location device *lookup_device_by_loc(pci_location_t loc) { device *ret = nullptr; @@ -89,6 +88,42 @@ device *lookup_device_by_loc(pci_location_t loc) { } } // namespace + +// 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; +} + +// find a bus by number +bus *lookup_bus(uint8_t bus_num) { + bus *found_bus = nullptr; + auto b_finder = [&](bus *b) -> status_t { + if (bus_num == b->bus_num()) { + found_bus = b; + return 1; + } + return 0; + }; + + for_every_bus(b_finder); + return found_bus; +} + } // namespace pci // C api, so outside of the namespace @@ -104,7 +139,7 @@ status_t pci_bus_mgr_init() { bus *b; // TODO: deal with root bus not having reference to bridge device - status_t err = bus::probe(loc, nullptr, &b); + status_t err = bus::probe(loc, nullptr, &b, true); if (err < 0) { return err; } @@ -112,25 +147,53 @@ status_t pci_bus_mgr_init() { // if we found anything there should be at least an empty bus device DEBUG_ASSERT(b); root = b; - list_add_tail(&bus_list, b->list_node_ptr()); + list_add_head(&bus_list, b->list_node_ptr()); // iterate over all the devices found - printf("PCI dump:\n"); - root->dump(2); + if (LK_DEBUGLEVEL >= SPEW) { + printf("PCI dump:\n"); + root->dump(2); + } -#if 0 - printf("visit all devices\n"); - pci_bus_mgr_visit_devices([](pci_location_t _loc) { - char str[14]; - printf("%s\n", pci_loc_string(_loc, str)); - }); -#endif + if (LOCAL_TRACE) { + printf("visit all devices\n"); + pci_bus_mgr_visit_devices([](pci_location_t _loc) { + char str[14]; + printf("%s\n", pci_loc_string(_loc, str)); + }); + } return NO_ERROR; } -status_t pci_bus_mgr_add_resource(enum pci_resource_type type, uint64_t mmio_base, uint64_t aux_base, uint64_t len) { - TRACEF("type %d: mmio base %#llx aux base %#llx len %#llx\n", type, mmio_base, aux_base, len); +status_t pci_bus_mgr_add_resource(enum pci_resource_type type, uint64_t mmio_base, uint64_t len) { + LTRACEF("type %d: mmio base %#llx len %#llx\n", type, mmio_base, len); + + resource_range r = {}; + r.type = type; + r.base = mmio_base; + r.size = len; + return resources.set_range(r); +} + +status_t pci_bus_mgr_assign_resources() { + LTRACE_ENTRY; + + if (!root) { + return NO_ERROR; + } + + status_t err = root->allocate_resources(resources); + if (err != NO_ERROR) { + printf("PCI: error assigning resources to devices\n"); + return err; + } + + // iterate over all the devices found + if (LK_DEBUGLEVEL >= SPEW) { + printf("PCI dump post assign:\n"); + root->dump(2); + } return NO_ERROR; } @@ -212,15 +275,7 @@ status_t pci_bus_mgr_enable_device(const pci_location_t loc) { return ERR_NOT_FOUND; } - uint16_t command; - status_t err = pci_read_config_half(loc, PCI_CONFIG_COMMAND, &command); - if (err != NO_ERROR) return err; - LTRACEF("command reg %#x\n", command); - command |= PCI_COMMAND_IO_EN | PCI_COMMAND_MEM_EN | PCI_COMMAND_BUS_MASTER_EN; - err = pci_write_config_half(loc, PCI_CONFIG_COMMAND, command); - if (err != NO_ERROR) return err; - - return NO_ERROR; + return d->enable(); } status_t pci_bus_mgr_read_bars(const pci_location_t loc, pci_bar_t bar[6]) { @@ -285,3 +340,15 @@ const char *pci_loc_string(pci_location_t loc, char out_str[14]) { return out_str; } +const char *pci_resource_type_to_str(enum pci_resource_type type) { + switch (type) { + case PCI_RESOURCE_IO_RANGE: + return "io"; + case PCI_RESOURCE_MMIO_RANGE: + return "mmio"; + case PCI_RESOURCE_MMIO64_RANGE: + return "mmio64"; + } + return "unknown"; +} + diff --git a/dev/bus/pci/bus_mgr/bus_mgr.h b/dev/bus/pci/bus_mgr/bus_mgr.h index 4062d1e4..c4282a38 100644 --- a/dev/bus/pci/bus_mgr/bus_mgr.h +++ b/dev/bus/pci/bus_mgr/bus_mgr.h @@ -7,6 +7,10 @@ */ #pragma once +#include +#include +#include +#include #include #include @@ -25,4 +29,7 @@ uint8_t get_last_bus(); // allocate the next bus (used when assigning busses to bridges) uint8_t allocate_next_bus(); -} +// get a pointer to a bus based on number +bus *lookup_bus(uint8_t bus_num); + +} // namespace pci diff --git a/dev/bus/pci/bus_mgr/device.cpp b/dev/bus/pci/bus_mgr/device.cpp index 9426472b..dea41f2d 100644 --- a/dev/bus/pci/bus_mgr/device.cpp +++ b/dev/bus/pci/bus_mgr/device.cpp @@ -14,14 +14,17 @@ #include #include #include +#include #include #include +#include #include #include #include #define LOCAL_TRACE 0 +#include "bus_mgr.h" #include "bridge.h" namespace pci { @@ -128,6 +131,24 @@ void device::dump(size_t indent) { } } +status_t device::enable() { + char str[14]; + LTRACEF("%s\n", pci_loc_string(loc(), str)); + + uint16_t command; + status_t err = pci_read_config_half(loc_, PCI_CONFIG_COMMAND, &command); + if (err != NO_ERROR) { + return err; + } + command |= PCI_COMMAND_IO_EN | PCI_COMMAND_MEM_EN | PCI_COMMAND_BUS_MASTER_EN; + err = pci_write_config_half(loc_, PCI_CONFIG_COMMAND, command); + if (err != NO_ERROR) { + return err; + } + + return NO_ERROR; +} + // walk the device's capability list, reading them in and creating sub objects per status_t device::probe_capabilities() { char str[14]; @@ -258,7 +279,7 @@ status_t device::allocate_msi(size_t num_requested, uint *msi_base) { // ask the platform for interrupts uint vector_base; - status_t err = platform_allocate_interrupts(num_requested, 1, &vector_base); + status_t err = platform_allocate_interrupts(num_requested, 0, true, &vector_base); if (err != NO_ERROR) { return err; } @@ -266,12 +287,11 @@ status_t device::allocate_msi(size_t num_requested, uint *msi_base) { // compute the MSI message to construct uint64_t msi_address = 0; uint16_t msi_data = 0; -#if ARCH_X86 - msi_data = (vector_base & 0xff) | (0<<15); // edge triggered - msi_address = 0xfee0'0000 | (0 << 12); // cpu 0 -#else - return ERR_NOT_SUPPORTED; -#endif + err = platform_compute_msi_values(vector_base, 0, true, &msi_address, &msi_data); + if (err != NO_ERROR) { + // TODO: return the allocated msi + return err; + } // program it into the capability const uint16_t cap_offset = msi_cap_->config_offset; @@ -402,4 +422,155 @@ status_t device::load_config() { return err; } +status_t device::compute_bar_sizes(bar_sizes *sizes) { + char str[14]; + LTRACEF("device at %s\n", pci_loc_string(loc(), str)); + + // iterate through the bars on this device and accumulate the size + // of all the bars of various types. also accumulate the maximum alignment + for (auto i = 0; i < 6; i++) { + const auto &bar = bars_[i]; + if (!bar.valid) { + continue; + } + + if (bar.io) { + // io case + sizes->io_size += ROUNDUP(bar.size, 16); + if (sizes->io_align < 4) { + sizes->io_align = 4; + } + } else if (bar.size_64 && bar.prefetchable) { + // 64bit mmio + auto size = ROUNDUP(bar.size, PAGE_SIZE); + auto align = __builtin_ctz(size); + sizes->prefetchable64_size += size; + if (sizes->prefetchable64_align < align) { + sizes->prefetchable64_align = align; + } + } else if (bar.size_64) { + // 64bit mmio + auto size = ROUNDUP(bar.size, PAGE_SIZE); + auto align = __builtin_ctz(size); + sizes->mmio64_size += size; + if (sizes->mmio64_align < align) { + sizes->mmio64_align = align; + } + } else if (bar.prefetchable) { + // 64bit prefetchable mmio + auto size = ROUNDUP(bar.size, PAGE_SIZE); + auto align = __builtin_ctz(size); + sizes->prefetchable_size += size; + if (sizes->prefetchable_align < align) { + sizes->prefetchable_align = align; + } + } else { + // 32bit mmio + auto size = ROUNDUP(bar.size, PAGE_SIZE); + auto align = __builtin_ctz(size); + sizes->mmio_size += size; + if (sizes->mmio_align < align) { + sizes->mmio_align = align; + } + } + } + + return NO_ERROR; +} + +status_t device::get_bar_alloc_requests(list_node *bar_alloc_requests) { + char str[14]; + LTRACEF("device at %s\n", pci_loc_string(loc(), str)); + + DEBUG_ASSERT(bar_alloc_requests); + + // iterate through the bars on this device and accumulate the size + // of all the bars of various types. also accumulate the maximum alignment + for (auto i = 0; i < 6; i++) { + const auto &bar = bars_[i]; + if (!bar.valid) { + continue; + } + + auto request = new bar_alloc_request; + *request = {}; + request->bridge = false; + request->dev = this; + request->bar_num = i; + + if (bar.io) { + // io case + request->size = ROUNDUP(bar.size, 16); + request->align = 4; + request->type = PCI_RESOURCE_IO_RANGE; + } else if (bar.size_64) { + // 64bit mmio + auto size = ROUNDUP(bar.size, PAGE_SIZE); + auto align = __builtin_ctz(size); + request->size = size; + request->align = align; + request->type = PCI_RESOURCE_MMIO64_RANGE; + request->prefetchable = bar.prefetchable; + } else { + // 32bit mmio + auto size = ROUNDUP(bar.size, PAGE_SIZE); + auto align = __builtin_ctz(size); + request->size = size; + request->align = align; + request->type = PCI_RESOURCE_MMIO_RANGE; + request->prefetchable = bar.prefetchable; + } + // add it to the list passed in + list_add_tail(bar_alloc_requests, &request->node); + } + + return NO_ERROR; +} + +status_t device::assign_resource(bar_alloc_request *request, uint64_t address) { + char str[14]; + LTRACEF("device at %s resource addr %#llx request:\n", pci_loc_string(loc(), str), address); + if (LOCAL_TRACE) { + request->dump(); + } + + DEBUG_ASSERT(IS_ALIGNED(address, (1UL << request->align))); + + uint32_t temp; + switch (request->type) { + case PCI_RESOURCE_IO_RANGE: + temp = (address & 0xfffc); // XXX do we need to write the bottom bits? + pci_write_config_word(loc(), PCI_CONFIG_BASE_ADDRESSES + request->bar_num * 4, temp); + break; + case PCI_RESOURCE_MMIO_RANGE: + temp = (address & 0xfffffff0); // XXX do we need to write the bottom bits? + pci_write_config_word(loc(), PCI_CONFIG_BASE_ADDRESSES + request->bar_num * 4, temp); + break; + case PCI_RESOURCE_MMIO64_RANGE: + temp = (address & 0xfffffff0); // XXX do we need to write the bottom bits? + pci_write_config_word(loc(), PCI_CONFIG_BASE_ADDRESSES + request->bar_num * 4, temp); + temp = address >> 32; + pci_write_config_word(loc(), PCI_CONFIG_BASE_ADDRESSES + request->bar_num * 4 + 4, temp); + break; + default: + panic("invalid request type %d\n", request->type); + } + + load_config(); + load_bars(); + + return NO_ERROR; +} + +void device::bar_alloc_request::dump() { + char str[14]; + if (bridge) { + printf("BAR alloc request %p: bridge %s type %u (%s) pref %d size %#llx align %u\n", + this, pci_loc_string(dev->loc(), str), type, pci_resource_type_to_str(type), prefetchable, size, align); + } else { + printf("BAR alloc request %p: device %s type %u (%s) pref %d size %#llx align %u bar %u\n", + this, pci_loc_string(dev->loc(), str), type, pci_resource_type_to_str(type), prefetchable, size, align, bar_num); + } +} + } // namespace pci diff --git a/dev/bus/pci/bus_mgr/device.h b/dev/bus/pci/bus_mgr/device.h index 1513e9df..ae2b9f28 100644 --- a/dev/bus/pci/bus_mgr/device.h +++ b/dev/bus/pci/bus_mgr/device.h @@ -10,6 +10,8 @@ #include #include #include +#include +#include namespace pci { @@ -35,6 +37,48 @@ public: status_t load_config(); status_t load_bars(); + status_t enable(); + + // ask the device to add up the sizes of all its bars and return the sum + // bridges will be expected to recurse into sub-bridges + struct bar_sizes { + uint32_t io_size; + uint32_t mmio_size; + uint64_t mmio64_size; + uint64_t prefetchable_size; + uint64_t prefetchable64_size; + + uint8_t io_align; + uint8_t mmio_align; + uint8_t mmio64_align; + uint8_t prefetchable_align; + uint8_t prefetchable64_align; + + bar_sizes &operator+=(const bar_sizes &a); + }; + virtual status_t compute_bar_sizes(bar_sizes *sizes); + + struct bar_alloc_request { + // linked list node + list_node node; + + // requst for allocation of this type + pci_resource_type type; + uint64_t size; + uint8_t align; // power of 2 + + bool bridge; // either a bridge request or a bar + bool prefetchable; // prefetchable request (only makes sense for mmio or mmio64) + + device *dev; + uint8_t bar_num; + + void dump(); + }; + virtual status_t get_bar_alloc_requests(list_node *bar_alloc_requests); + virtual status_t assign_resource(bar_alloc_request *request, uint64_t address); + virtual status_t assign_child_resources() { return NO_ERROR; } + pci_location_t loc() const { return loc_; } const bus *get_bus() const { return bus_; } @@ -79,4 +123,38 @@ struct capability { bool is_msix() const { return id == 0x11; } }; +inline device::bar_sizes operator+(const device::bar_sizes &a, const device::bar_sizes &b) { + device::bar_sizes result; + + result.io_size = a.io_size + b.io_size; + result.mmio_size = a.mmio_size + b.mmio_size; + result.mmio64_size = a.mmio64_size + b.mmio64_size; + result.prefetchable_size = a.prefetchable_size + b.prefetchable_size; + result.prefetchable64_size = a.prefetchable64_size + b.prefetchable64_size; + + result.io_align = (a.io_align > b.io_align) ? a.io_align : b.io_align; + result.mmio_align = (a.mmio_align > b.mmio_align) ? a.mmio_align : b.mmio_align; + result.mmio64_align = (a.mmio64_align > b.mmio64_align) ? a.mmio64_align : b.mmio64_align; + result.prefetchable_align = (a.prefetchable_align > b.prefetchable_align) ? a.prefetchable_align : b.prefetchable_align; + result.prefetchable64_align = (a.prefetchable64_align > b.prefetchable64_align) ? a.prefetchable64_align : b.prefetchable64_align; + + return result; +} + +inline device::bar_sizes &device::bar_sizes::operator+=(const device::bar_sizes &a) { + io_size += a.io_size; + mmio_size += a.mmio_size; + mmio64_size += a.mmio64_size; + prefetchable_size += a.prefetchable_size; + prefetchable64_size += a.prefetchable64_size; + + io_align = (io_align > a.io_align) ? io_align : a.io_align; + mmio_align = (mmio_align > a.mmio_align) ? mmio_align : a.mmio_align; + mmio64_align = (mmio64_align > a.mmio64_align) ? mmio64_align : a.mmio64_align; + prefetchable_align = (prefetchable_align > a.prefetchable_align) ? prefetchable_align : a.prefetchable_align; + prefetchable64_align = (prefetchable64_align > a.prefetchable64_align) ? prefetchable64_align : a.prefetchable64_align; + + return *this; +} + } // pci diff --git a/dev/bus/pci/bus_mgr/resource.cpp b/dev/bus/pci/bus_mgr/resource.cpp new file mode 100644 index 00000000..0e35d1d7 --- /dev/null +++ b/dev/bus/pci/bus_mgr/resource.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021 Travis Geiseblrecht + * + * Use of this source code is governed by a MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT + */ +#include "resource.h" + +#include +#include +#include +#include + +#define LOCAL_TRACE 0 + +namespace pci { + +resource_range &resource_allocator::type_to_range(pci_resource_type type, bool prefetchable) { + if (prefetchable) { + switch (type) { + case PCI_RESOURCE_MMIO_RANGE: + return ranges_.mmio_prefetchable; + case PCI_RESOURCE_MMIO64_RANGE: + return ranges_.mmio64_prefetchable; + default: + DEBUG_ASSERT_MSG(0, "uhandled prefetchable pci resource type %d\n", type); + } + } else { + switch (type) { + case PCI_RESOURCE_IO_RANGE: + return ranges_.io; + case PCI_RESOURCE_MMIO_RANGE: + return ranges_.mmio; + case PCI_RESOURCE_MMIO64_RANGE: + return ranges_.mmio64; + default: + DEBUG_ASSERT_MSG(0, "uhandled pci resource type %d\n", type); + } + } +} + +status_t resource_allocator::set_range(const resource_range &range, bool prefetchable) { + LTRACEF("range base %#llx size %#llx type %d prefetchable %d\n", range.base, range.size, range.type, prefetchable); + type_to_range(range.type, prefetchable) = range; + return NO_ERROR; +} + +status_t resource_allocator::allocate_mmio(bool can_be_64bit, bool prefetchable, uint64_t size, uint8_t align, uint64_t *out) { + pci_resource_type type; + + for (;;) { + if (can_be_64bit) { + type = PCI_RESOURCE_MMIO64_RANGE; + } else { + type = PCI_RESOURCE_MMIO_RANGE; + } + + auto &range = type_to_range(type, prefetchable); + + LTRACEF("range base %#llx size %#llx. request size %#llx align %u prefetchable %d can_be_64 %d\n", + range.base, range.size, size, align, prefetchable, can_be_64bit); + + // TODO: make sure align is honored or removed + if (range.base + size <= range.base + range.size) { + *out = range.base; + range.base += size; + range.size -= size; + return NO_ERROR; + } + + if (can_be_64bit) { + can_be_64bit = false; + continue; + } + break; + } + + return ERR_NO_RESOURCES; +} + +status_t resource_allocator::allocate_io(uint32_t size, uint8_t align, uint32_t *out) { + auto &range = type_to_range(PCI_RESOURCE_IO_RANGE, false); + + LTRACEF("range base %#llx size %#llx. request size %#x align %u\n", range.base, range.size, size, align); + + // TODO: make sure align is honored or removed + if (range.base + size <= range.base + range.size) { + *out = range.base; + range.base += size; + range.size -= size; + return NO_ERROR; + } + + return ERR_NO_RESOURCES; +} + +} // namespace pci diff --git a/dev/bus/pci/bus_mgr/resource.h b/dev/bus/pci/bus_mgr/resource.h new file mode 100644 index 00000000..07322164 --- /dev/null +++ b/dev/bus/pci/bus_mgr/resource.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 Travis Geiseblrecht + * + * Use of this source code is governed by a MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT + */ +#pragma once + +#include +#include +#include + +namespace pci { + +// TODO: move into separate file +struct resource_range { + pci_resource_type type; + uint64_t base; + uint64_t size; + + void dump() { + printf("resource type %d: base %#llx size %#llx\n", + type, base, size); + } +}; + +struct resource_range_set { + resource_range io; + resource_range mmio; + resource_range mmio64; + resource_range mmio_prefetchable; + resource_range mmio64_prefetchable; + + void dump() { + printf("resource range set:\n"); + io.dump(); + mmio.dump(); + mmio64.dump(); + mmio_prefetchable.dump(); + mmio64_prefetchable.dump(); + } +}; + +class resource_allocator { +public: + resource_allocator() = default; + ~resource_allocator() = default; + + status_t set_range(const resource_range &range, bool prefetchable = false); + status_t allocate_io(uint32_t size, uint8_t align, uint32_t *out); + status_t allocate_mmio(bool can_be_64bit, bool prefetchable, uint64_t size, uint8_t align, uint64_t *out); + +private: + resource_range &type_to_range(pci_resource_type type, bool prefetchable); + + resource_range_set ranges_ = {}; +}; + +} // namespace pci diff --git a/dev/bus/pci/include/dev/bus/pci.h b/dev/bus/pci/include/dev/bus/pci.h index b84a3df6..10e90d19 100644 --- a/dev/bus/pci/include/dev/bus/pci.h +++ b/dev/bus/pci/include/dev/bus/pci.h @@ -87,11 +87,13 @@ status_t pci_bus_mgr_visit_devices(pci_visit_routine routine, void *cookie); // must be called before pci_bus_mgr_init if available enum pci_resource_type { - PCI_RESOURCE_IO_RANGE, // aux base is base of io range, mmio base is if memory mapped + PCI_RESOURCE_IO_RANGE = 0, PCI_RESOURCE_MMIO_RANGE, PCI_RESOURCE_MMIO64_RANGE, }; -status_t pci_bus_mgr_add_resource(enum pci_resource_type, uint64_t mmio_base, uint64_t aux_base, uint64_t len); +status_t pci_bus_mgr_add_resource(enum pci_resource_type, uint64_t mmio_base, uint64_t len); + +status_t pci_bus_mgr_assign_resources(void); // must be called after pci_init_*(); status_t pci_bus_mgr_init(void); @@ -119,8 +121,11 @@ status_t pci_bus_mgr_allocate_irq(const pci_location_t loc, uint *irqbase); // return a pointer to a formatted string const char *pci_loc_string(pci_location_t loc, char out_str[14]); + +// debug printing routines void pci_dump_bar(const pci_bar_t *bar, int index); void pci_dump_bars(pci_bar_t bar[6], size_t count); +const char *pci_resource_type_to_str(enum pci_resource_type); __END_CDECLS diff --git a/dev/bus/pci/rules.mk b/dev/bus/pci/rules.mk index 5c45f1a2..844f6c02 100644 --- a/dev/bus/pci/rules.mk +++ b/dev/bus/pci/rules.mk @@ -9,6 +9,7 @@ MODULE_SRCS += $(LOCAL_DIR)/bus_mgr/bridge.cpp MODULE_SRCS += $(LOCAL_DIR)/bus_mgr/bus.cpp MODULE_SRCS += $(LOCAL_DIR)/bus_mgr/bus_mgr.cpp MODULE_SRCS += $(LOCAL_DIR)/bus_mgr/device.cpp +MODULE_SRCS += $(LOCAL_DIR)/bus_mgr/resource.cpp MODULE_SRCS += $(LOCAL_DIR)/backend/ecam.cpp MODULE_SRCS += $(LOCAL_DIR)/backend/bios32.cpp diff --git a/dev/interrupt/arm_gic/arm_gic.c b/dev/interrupt/arm_gic/arm_gic.c index 3b262bc1..de08bb83 100644 --- a/dev/interrupt/arm_gic/arm_gic.c +++ b/dev/interrupt/arm_gic/arm_gic.c @@ -105,7 +105,10 @@ void register_int_handler(unsigned int vector, int_handler handler, void *arg) { } void register_int_handler_msi(unsigned int vector, int_handler handler, void *arg, bool edge) { - PANIC_UNIMPLEMENTED; + // only can deal with edge triggered at the moment + DEBUG_ASSERT(edge); + + register_int_handler(vector, handler, arg); } #define GICREG(gic, reg) (*REG32(GICBASE(gic) + (reg))) diff --git a/platform/include/platform/interrupts.h b/platform/include/platform/interrupts.h index 2a96fc53..5bcd117d 100644 --- a/platform/include/platform/interrupts.h +++ b/platform/include/platform/interrupts.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include __BEGIN_CDECLS @@ -31,11 +32,15 @@ void register_int_handler_msi(unsigned int vector, int_handler handler, void *ar /* Allocate a run of interrupts with alignment log2 in a platform specific way. * Used for PCI MSI and possibly other use cases. */ -status_t platform_allocate_interrupts(size_t count, uint align_log2, unsigned int *vector); +status_t platform_allocate_interrupts(size_t count, uint align_log2, bool msi, unsigned int *vector); /* Map the incoming interrupt line number from the pci bus config to raw * vector number, usable in the above apis. */ status_t platform_pci_int_to_vector(unsigned int pci_int, unsigned int *vector); +/* Ask the platform to compute for us the value to stuff in the MSI address and data fields. */ +status_t platform_compute_msi_values(unsigned int vector, unsigned int cpu, bool edge, + uint64_t *msi_address_out, uint16_t *msi_data_out); + __END_CDECLS diff --git a/platform/pc/interrupts.c b/platform/pc/interrupts.c index 466fcf20..89dad926 100644 --- a/platform/pc/interrupts.c +++ b/platform/pc/interrupts.c @@ -170,9 +170,9 @@ status_t platform_pci_int_to_vector(unsigned int pci_int, unsigned int *vector) return NO_ERROR; } -status_t platform_allocate_interrupts(size_t count, uint align_log2, unsigned int *vector) { - LTRACEF("count %zu, align %u\n", count, align_log2); - if (align_log2 > 1) { +status_t platform_allocate_interrupts(size_t count, uint align_log2, bool msi, unsigned int *vector) { + LTRACEF("count %zu align %u msi %d\n", count, align_log2, msi); + if (align_log2 > 0) { PANIC_UNIMPLEMENTED; } @@ -196,3 +196,15 @@ status_t platform_allocate_interrupts(size_t count, uint align_log2, unsigned in return err; } +status_t platform_compute_msi_values(unsigned int vector, unsigned int cpu, bool edge, + uint64_t *msi_address_out, uint16_t *msi_data_out) { + + // only handle edge triggered at the moment + DEBUG_ASSERT(edge); + + *msi_data_out = (vector & 0xff) | (0<<15); // edge triggered + *msi_address_out = 0xfee00000 | (cpu << 12); + + return NO_ERROR; +} + diff --git a/platform/qemu-virt-arm/include/platform/qemu-virt.h b/platform/qemu-virt-arm/include/platform/qemu-virt.h index e8fd588e..83a9c0ff 100644 --- a/platform/qemu-virt-arm/include/platform/qemu-virt.h +++ b/platform/qemu-virt-arm/include/platform/qemu-virt.h @@ -39,6 +39,9 @@ static const MemMapEntry base_memmap[] = { [VIRT_SMMU] = { 0x09050000, 0x00020000 }, [VIRT_PCDIMM_ACPI] = { 0x09070000, MEMORY_HOTPLUG_IO_LEN }, [VIRT_ACPI_GED] = { 0x09080000, ACPI_GED_EVT_SEL_LEN }, + [VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN}, + [VIRT_PVTIME] = { 0x090a0000, 0x00010000 }, + [VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 }, [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, @@ -94,7 +97,9 @@ static const int a15irqmap[] = { #define ARM_GENERIC_TIMER_VIRTUAL_INT 27 #define ARM_GENERIC_TIMER_PHYSICAL_INT 30 #define UART0_INT (32 + 1) -#define VIRTIO0_INT (32 + 16) +#define PCIE_INT_BASE (32 + 3) +#define VIRTIO0_INT_BASE (32 + 16) +#define MSI_INT_BASE (32 + 48) #define MAX_INT 128 diff --git a/platform/qemu-virt-arm/platform.c b/platform/qemu-virt-arm/platform.c index 824687db..a31ca84f 100644 --- a/platform/qemu-virt-arm/platform.c +++ b/platform/qemu-virt-arm/platform.c @@ -189,24 +189,30 @@ void platform_init(void) { if (err == NO_ERROR) { // add some additional resources to the pci bus manager in case it needs to configure if (pcie_state.info.io_len > 0) { - pci_bus_mgr_add_resource(PCI_RESOURCE_IO_RANGE, pcie_state.info.io_base_mmio, pcie_state.info.io_base, pcie_state.info.io_len); + // we can only deal with a mapping of io base 0 to the mmio base + DEBUG_ASSERT(pcie_state.info.io_base == 0); + pci_bus_mgr_add_resource(PCI_RESOURCE_IO_RANGE, pcie_state.info.io_base, pcie_state.info.io_len); + + // TODO: set the mmio base somehow so pci knows what to do with it } if (pcie_state.info.mmio_len > 0) { - pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO_RANGE, pcie_state.info.mmio_base, 0, pcie_state.info.mmio_len); + pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO_RANGE, pcie_state.info.mmio_base, pcie_state.info.mmio_len); } if (pcie_state.info.mmio64_len > 0) { - pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO64_RANGE, pcie_state.info.mmio64_base, 0, pcie_state.info.mmio64_len); + pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO64_RANGE, pcie_state.info.mmio64_base, pcie_state.info.mmio64_len); } // start the bus manager pci_bus_mgr_init(); + + pci_bus_mgr_assign_resources(); } } /* detect any virtio devices */ uint virtio_irqs[NUM_VIRTIO_TRANSPORTS]; for (int i = 0; i < NUM_VIRTIO_TRANSPORTS; i++) { - virtio_irqs[i] = VIRTIO0_INT + i; + virtio_irqs[i] = VIRTIO0_INT_BASE + i; } virtio_mmio_detect((void *)VIRTIO_BASE, NUM_VIRTIO_TRANSPORTS, virtio_irqs, 0x200); @@ -235,11 +241,63 @@ void platform_init(void) { } status_t platform_pci_int_to_vector(unsigned int pci_int, unsigned int *vector) { - // at the moment there's no translation between PCI IRQs and native irqs - *vector = pci_int; + // only 4 legacy vectors supported, within PCIE_INT_BASE and PCIE_INT_BASE + 3 + if (pci_int >= 4) { + return ERR_OUT_OF_RANGE; + } + *vector = pci_int + PCIE_INT_BASE; return NO_ERROR; } -status_t platform_allocate_interrupts(size_t count, uint align_log2, unsigned int *vector) { - return ERR_NOT_SUPPORTED; +status_t platform_allocate_interrupts(size_t count, uint align_log2, bool msi, unsigned int *vector) { + TRACEF("count %zu align %u msi %d\n", count, align_log2, msi); + + // TODO: handle nonzero alignment, count > 0, and add locking + + // list of allocated msi interrupts + static uint64_t msi_bitmap = 0; + + // cannot handle allocating for anything but MSI interrupts + if (!msi) { + return ERR_NOT_SUPPORTED; + } + + // cannot deal with alignment yet + DEBUG_ASSERT(align_log2 == 0); + DEBUG_ASSERT(count == 1); + + // make a copy of the bitmap + int allocated = -1; + for (size_t i = 0; i < sizeof(msi_bitmap) * 8; i++) { + if ((msi_bitmap & (1UL << i)) == 0) { + msi_bitmap |= (1UL << i); + allocated = i; + break; + } + } + if (allocated < 0) { + return ERR_NOT_FOUND; + } + + allocated += MSI_INT_BASE; + + TRACEF("allocated msi at %u\n", allocated); + *vector = allocated; + return NO_ERROR; +} + +status_t platform_compute_msi_values(unsigned int vector, unsigned int cpu, bool edge, + uint64_t *msi_address_out, uint16_t *msi_data_out) { + + // only handle edge triggered at the moment + DEBUG_ASSERT(edge); + // only handle cpu 0 + DEBUG_ASSERT(cpu == 0); + + // TODO: call through to the appropriate gic driver to deal with GICv2 vs v3 + + *msi_data_out = vector; + *msi_address_out = 0x08020040; // address of the GICv2 MSI port + + return NO_ERROR; } diff --git a/platform/qemu-virt-riscv/platform.c b/platform/qemu-virt-riscv/platform.c index f3d71ec2..cd19273a 100644 --- a/platform/qemu-virt-riscv/platform.c +++ b/platform/qemu-virt-riscv/platform.c @@ -161,17 +161,23 @@ void platform_init(void) { if (err == NO_ERROR) { // add some additional resources to the pci bus manager in case it needs to configure if (pcie_state.info.io_len > 0) { - pci_bus_mgr_add_resource(PCI_RESOURCE_IO_RANGE, pcie_state.info.io_base_mmio, pcie_state.info.io_base, pcie_state.info.io_len); + // we can only deal with a mapping of io base 0 to the mmio base + DEBUG_ASSERT(pcie_state.info.io_base == 0); + pci_bus_mgr_add_resource(PCI_RESOURCE_IO_RANGE, pcie_state.info.io_base, pcie_state.info.io_len); + + // TODO: set the mmio base somehow so pci knows what to do with it } if (pcie_state.info.mmio_len > 0) { - pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO_RANGE, pcie_state.info.mmio_base, 0, pcie_state.info.mmio_len); + pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO_RANGE, pcie_state.info.mmio_base, pcie_state.info.mmio_len); } if (pcie_state.info.mmio64_len > 0) { - pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO64_RANGE, pcie_state.info.mmio64_base, 0, pcie_state.info.mmio64_len); + pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO64_RANGE, pcie_state.info.mmio64_base, pcie_state.info.mmio64_len); } // start the bus manager pci_bus_mgr_init(); + + pci_bus_mgr_assign_resources(); // add some additional resources to the pci bus manager in case it needs to configure }; } diff --git a/platform/qemu-virt-riscv/plic.c b/platform/qemu-virt-riscv/plic.c index dd9b79bf..7c5dfa4f 100644 --- a/platform/qemu-virt-riscv/plic.c +++ b/platform/qemu-virt-riscv/plic.c @@ -102,6 +102,11 @@ status_t platform_pci_int_to_vector(unsigned int pci_int, unsigned int *vector) return NO_ERROR; } -status_t platform_allocate_interrupts(size_t count, uint align_log2, unsigned int *vector) { +status_t platform_allocate_interrupts(size_t count, uint align_log2, bool msi, unsigned int *vector) { + return ERR_NOT_SUPPORTED; +} + +status_t platform_compute_msi_values(unsigned int vector, unsigned int cpu, bool edge, + uint64_t *msi_address_out, uint16_t *msi_data_out) { return ERR_NOT_SUPPORTED; }