diff --git a/base/include/base/snprintf.h b/base/include/base/snprintf.h index 95c92dec08..ec565c3a78 100644 --- a/base/include/base/snprintf.h +++ b/base/include/base/snprintf.h @@ -52,7 +52,7 @@ namespace Genode { void _out_char(char c) { /* ensure to leave space for null-termination */ - if (_w_offset > _dst_len - 2) + if (_w_offset + 2 > _dst_len) return; _dst[_w_offset++] = c; diff --git a/os/include/pci_device/client.h b/os/include/pci_device/client.h index 69b7214178..643c34a1db 100644 --- a/os/include/pci_device/client.h +++ b/os/include/pci_device/client.h @@ -17,6 +17,7 @@ #include #include #include +#include namespace Pci { diff --git a/os/include/pci_device/pci_device.h b/os/include/pci_device/pci_device.h index b1ab86b248..a86cd0c5f5 100644 --- a/os/include/pci_device/pci_device.h +++ b/os/include/pci_device/pci_device.h @@ -15,6 +15,7 @@ #define _INCLUDE__PCI_DEVICE__PCI_DEVICE_H_ #include +#include namespace Pci { diff --git a/os/include/pci_session/client.h b/os/include/pci_session/client.h index 4f5713f40b..302bb108a0 100644 --- a/os/include/pci_session/client.h +++ b/os/include/pci_session/client.h @@ -32,6 +32,9 @@ namespace Pci { void release_device(Device_capability device) { call(device); } + + Genode::Io_mem_dataspace_capability config_extended(Device_capability device_cap) { + return call(device_cap); } }; } diff --git a/os/include/pci_session/pci_session.h b/os/include/pci_session/pci_session.h index 5f799efc87..53cd2c25dc 100644 --- a/os/include/pci_session/pci_session.h +++ b/os/include/pci_session/pci_session.h @@ -49,16 +49,25 @@ namespace Pci { */ virtual void release_device(Device_capability device) = 0; + /** + * Provide mapping to device configuration space of 4k, known as + * "Enhanced Configuration Access Mechanism (ECAM) for PCI Express + */ + virtual Genode::Io_mem_dataspace_capability config_extended(Device_capability) = 0; /********************* ** RPC declaration ** *********************/ GENODE_RPC(Rpc_first_device, Device_capability, first_device); - GENODE_RPC(Rpc_next_device, Device_capability, next_device, Device_capability); + GENODE_RPC(Rpc_next_device, Device_capability, next_device, + Device_capability); GENODE_RPC(Rpc_release_device, void, release_device, Device_capability); + GENODE_RPC(Rpc_config_extended, Genode::Io_mem_dataspace_capability, + config_extended, Device_capability); - GENODE_RPC_INTERFACE(Rpc_first_device, Rpc_next_device, Rpc_release_device); + GENODE_RPC_INTERFACE(Rpc_first_device, Rpc_next_device, + Rpc_release_device, Rpc_config_extended); }; } diff --git a/os/src/drivers/pci/main.cc b/os/src/drivers/pci/main.cc index 4c8038f589..ef40fa389e 100644 --- a/os/src/drivers/pci/main.cc +++ b/os/src/drivers/pci/main.cc @@ -22,7 +22,6 @@ using namespace Genode; using namespace Pci; - int main(int argc, char **argv) { printf("PCI driver started\n"); @@ -30,7 +29,7 @@ int main(int argc, char **argv) /* * Initialize server entry point */ - enum { STACK_SIZE = sizeof(addr_t)*1024 }; + enum { STACK_SIZE = 2 * sizeof(addr_t)*1024 }; static Cap_connection cap; static Rpc_entrypoint ep(&cap, STACK_SIZE, "pci_ep"); @@ -46,6 +45,6 @@ int main(int argc, char **argv) static Pci::Root root(&ep, &sliced_heap); env()->parent()->announce(ep.manage(&root)); - Genode::sleep_forever(); + sleep_forever(); return 0; } diff --git a/os/src/drivers/pci/pci_device_component.h b/os/src/drivers/pci/pci_device_component.h index c939b30d81..010b1a5981 100644 --- a/os/src/drivers/pci/pci_device_component.h +++ b/os/src/drivers/pci/pci_device_component.h @@ -19,6 +19,8 @@ #include #include +#include + #include "pci_device_config.h" namespace Pci { @@ -28,18 +30,32 @@ namespace Pci { { private: - Device_config _device_config; + Device_config _device_config; + Genode::addr_t _config_space; + Genode::Io_mem_connection *_io_mem; public: /** * Constructor */ - Device_component(Device_config device_config): - _device_config(device_config) { } + Device_component(Device_config device_config, Genode::addr_t addr) + : + _device_config(device_config), _config_space(addr), + _io_mem(0) { } + + /**************************************** + ** Methods used solely by pci session ** + ****************************************/ Device_config config() { return _device_config; } + Genode::addr_t config_space() { return _config_space; } + + void set_config_space(Genode::Io_mem_connection * io_mem) { + _io_mem = io_mem; } + + Genode::Io_mem_connection * get_config_space() { return _io_mem; } /************************** ** PCI-device interface ** diff --git a/os/src/drivers/pci/pci_device_config.h b/os/src/drivers/pci/pci_device_config.h index d2fc795716..b8b884f1ec 100644 --- a/os/src/drivers/pci/pci_device_config.h +++ b/os/src/drivers/pci/pci_device_config.h @@ -184,6 +184,29 @@ namespace Pci { pci_config->write(_bus, _device, _function, address, value, size); } }; + + class Config_space : public Genode::List::Element + { + private: + + Genode::uint32_t _bdf_start; + Genode::uint32_t _func_count; + Genode::addr_t _base; + + public: + + Config_space(Genode::uint32_t bdf_start, + Genode::uint32_t func_count, Genode::addr_t base) + : + _bdf_start(bdf_start), _func_count(func_count), _base(base) {} + + Genode::addr_t lookup_config_space(Genode::uint32_t bdf) + { + if ((_bdf_start <= bdf) && (bdf <= _bdf_start + _func_count - 1)) + return _base + (bdf << 12); + return 0; + } + }; } #endif /* _DEVICE_CONFIG_H_ */ diff --git a/os/src/drivers/pci/pci_session_component.h b/os/src/drivers/pci/pci_session_component.h index 9e6c7c0ec6..e7398d84fe 100644 --- a/os/src/drivers/pci/pci_session_component.h +++ b/os/src/drivers/pci/pci_session_component.h @@ -18,6 +18,10 @@ #include #include +#include + +#include + #include "pci_device_component.h" #include "pci_config_access.h" @@ -77,7 +81,6 @@ namespace Pci { Genode::Allocator *_md_alloc; Genode::List _device_list; - /** * Scan PCI busses for a device * @@ -119,6 +122,34 @@ namespace Pci { return false; } + /** + * List containing extended PCI config space information + */ + static Genode::List &config_space_list() { + static Genode::List config_space; + return config_space; + } + + /** + * Find for a given PCI device described by the bus:dev:func triple + * the corresponding extended 4K PCI config space address. + * A io mem dataspace is created and returned. + */ + Genode::addr_t + lookup_config_space(Genode::uint8_t bus, Genode::uint8_t dev, + Genode::uint8_t func) + { + using namespace Genode; + + uint32_t bdf = (bus << 8) | ((dev & 0x1f) << 3) | (func & 0x7); + addr_t config_space = ~0UL; /* invalid */ + + Config_space *e = config_space_list().first(); + for (; e && (config_space == ~0UL); e = e->next()) + config_space = e->lookup_config_space(bdf); + + return config_space; + } public: @@ -139,6 +170,17 @@ namespace Pci { release_device(_device_list.first()->cap()); } + static void add_config_space(Genode::uint32_t bdf_start, + Genode::uint32_t func_count, + Genode::addr_t base) + { + using namespace Genode; + Config_space * space = + new (env()->heap()) Config_space(bdf_start, func_count, + base); + config_space_list().insert(space); + } + /*************************** ** PCI session interface ** @@ -181,13 +223,22 @@ namespace Pci { if (!_find_next(bus, device, function, &config, &config_access)) return Device_capability(); + /* get new bdf values */ + bus = config.bus_number(); + device = config.device_number(); + function = config.function_number(); + + /* lookup if we have a extended pci config space */ + Genode::addr_t config_space = lookup_config_space(bus, device, + function); /* * A device was found. Create a new device component for the * device and return its capability. * * FIXME: check and adjust session quota */ - Device_component *device_component = new (_md_alloc) Device_component(config); + Device_component *device_component = + new (_md_alloc) Device_component(config, config_space); if (!device_component) return Device_capability(); @@ -209,8 +260,37 @@ namespace Pci { _ep->dissolve(device); /* FIXME: adjust quota */ + Genode::Io_mem_connection * io_mem = device->get_config_space(); + if (io_mem) + destroy(_md_alloc, io_mem); destroy(_md_alloc, device); } + + Genode::Io_mem_dataspace_capability config_extended(Device_capability device_cap) + { + using namespace Genode; + + Object_pool::Guard + device(_ep->lookup_and_lock(device_cap)); + + if (!device || device->config_space() == ~0UL) + return Io_mem_dataspace_capability(); + + Io_mem_connection * io_mem = device->get_config_space(); + if (io_mem) + return io_mem->dataspace(); + + try { + io_mem = new (_md_alloc) Io_mem_connection(device->config_space(), + 0x1000); + } catch (Parent::Service_denied) { + return Io_mem_dataspace_capability(); + } + + device->set_config_space(io_mem); + + return io_mem->dataspace(); + } }; @@ -220,6 +300,35 @@ namespace Pci { Genode::Cap_session *_cap_session; + + void _parse_config() + { + using namespace Genode; + + try { + unsigned i; + + for (i = 0; i < config()->xml_node().num_sub_nodes(); i++) + { + Xml_node node = config()->xml_node().sub_node(i); + uint32_t bdf_start = 0; + uint32_t func_count = 0; + addr_t base = 0; + node.sub_node("start").value(&bdf_start); + node.sub_node("count").value(&func_count); + node.sub_node("base").value(&base); + + PINF("%2u BDF start %x, functions: 0x%x, physical base " + "0x%lx", i, bdf_start, func_count, base); + + Session_component::add_config_space(bdf_start, + func_count, base); + } + } catch (...) { + PERR("PCI config space data could not be parsed."); + } + } + protected: Session_component *_create_session(const char *args) @@ -245,6 +354,8 @@ namespace Pci { : Genode::Root_component(ep, md_alloc) { + _parse_config(); + /* enforce initial bus scan */ bus_valid(); }