From 3841ee1d51fa17d3b8702d43770e90a886e7689d Mon Sep 17 00:00:00 2001 From: Stefan Kalkowski Date: Wed, 5 Oct 2022 18:25:55 +0200 Subject: [PATCH] platform_drv: implement MSI-x support Ref genodelabs/genode#4578 --- repos/os/include/pci/config.h | 19 ++++++++ .../src/drivers/platform/device_component.cc | 17 +++++-- .../src/drivers/platform/device_component.h | 7 ++- repos/os/src/drivers/platform/pci.cc | 45 +++++++++++++++++-- repos/os/src/drivers/platform/pci.h | 9 ++-- 5 files changed, 86 insertions(+), 11 deletions(-) diff --git a/repos/os/include/pci/config.h b/repos/os/include/pci/config.h index 9224ade20a..2c5115ac72 100644 --- a/repos/os/include/pci/config.h +++ b/repos/os/include/pci/config.h @@ -246,6 +246,7 @@ struct Pci::Config : Genode::Mmio { struct Control : Register<0x2, 16> { + struct Slots : Bitfield<0, 10> {}; struct Size : Bitfield<0, 11> {}; struct Function_mask : Bitfield<14, 1> {}; struct Enable : Bitfield<15, 1> {}; @@ -265,6 +266,8 @@ struct Pci::Config : Genode::Mmio struct Table_entry : Genode::Mmio { + enum { SIZE = 16 }; + struct Address_64_lower : Register<0x0, 32> { }; struct Address_64_upper : Register<0x4, 32> { }; struct Data : Register<0x8, 32> { }; @@ -277,6 +280,22 @@ struct Pci::Config : Genode::Mmio }; using Pci_capability::Pci_capability; + + Genode::uint8_t bar() { + return (Genode::uint8_t) read(); } + + Genode::size_t table_offset() { + return read() << 3; } + + unsigned slots() { return read(); } + + void enable() + { + Control::access_t ctrl = read(); + Control::Function_mask::set(ctrl, 0); + Control::Enable::set(ctrl, 1); + write(ctrl); + } }; diff --git a/repos/os/src/drivers/platform/device_component.cc b/repos/os/src/drivers/platform/device_component.cc index 3a7b6780bf..c6771a9442 100644 --- a/repos/os/src/drivers/platform/device_component.cc +++ b/repos/os/src/drivers/platform/device_component.cc @@ -47,6 +47,15 @@ Driver::Device::Name Device_component::device() const { return _device; } Driver::Session_component & Device_component::session() { return _session; } +unsigned Device_component::io_mem_index(Device::Pci_bar bar) +{ + unsigned ret = ~0U; + _io_mem_registry.for_each([&] (Io_mem & iomem) { + if (iomem.bar.number == bar.number) ret = iomem.idx; }); + return ret; +} + + Genode::Io_mem_session_capability Device_component::io_mem(unsigned idx, Range &range) { @@ -92,7 +101,7 @@ Genode::Irq_session_capability Device_component::irq(unsigned idx) pci_cfg_addr); Irq_session::Info info = irq.irq->info(); if (info.type == Irq_session::Info::MSI) - pci_msi_enable(_env, pci_cfg_addr, info); + pci_msi_enable(_env, *this, pci_cfg_addr, info, irq.type); } if (irq.shared && !irq.sirq.constructed()) @@ -172,13 +181,13 @@ Device_component::Device_component(Registry & registry, }); device.for_each_io_mem([&] (unsigned idx, Range range, - Device::Pci_bar, bool pf) + Device::Pci_bar bar, bool pf) { session.ram_quota_guard().withdraw(Ram_quota{Io_mem_session::RAM_QUOTA}); _ram_quota += Io_mem_session::RAM_QUOTA; session.cap_quota_guard().withdraw(Cap_quota{Io_mem_session::CAP_QUOTA}); _cap_quota += Io_mem_session::CAP_QUOTA; - new (session.heap()) Io_mem(_io_mem_registry, idx, range, pf); + new (session.heap()) Io_mem(_io_mem_registry, bar, idx, range, pf); }); device.for_each_io_port_range([&] (unsigned idx, Io_port_range::Range range) @@ -206,7 +215,7 @@ Device_component::Device_component(Registry & registry, session.cap_quota_guard().withdraw(Cap_quota{Io_mem_session::CAP_QUOTA}); _cap_quota += Io_mem_session::CAP_QUOTA; Io_mem & iomem = *(new (session.heap()) - Io_mem(_reserved_mem_registry, idx, range, false)); + Io_mem(_reserved_mem_registry, {0}, idx, range, false)); iomem.io_mem.construct(_env, iomem.range.start, iomem.range.size, false); session.device_pd().attach_dma_mem(iomem.io_mem->dataspace(), diff --git a/repos/os/src/drivers/platform/device_component.h b/repos/os/src/drivers/platform/device_component.h index 2f0de6bb0f..a736437f49 100644 --- a/repos/os/src/drivers/platform/device_component.h +++ b/repos/os/src/drivers/platform/device_component.h @@ -62,18 +62,22 @@ class Driver::Device_component : public Rpc_object::Element { + using Pci_bar = Device::Pci_bar; + + Pci_bar bar; unsigned idx; Range range; bool prefetchable; Constructible io_mem {}; Io_mem(Registry & registry, + Pci_bar bar, unsigned idx, Range range, bool pf) : Registry::Element(registry, *this), - idx(idx), range(range), prefetchable(pf) {} + bar(bar), idx(idx), range(range), prefetchable(pf) {} }; struct Io_port_range : Registry::Element @@ -108,6 +112,7 @@ class Driver::Device_component : public Rpc_object #include +#include #include #include #include @@ -127,15 +128,53 @@ void Driver::pci_apply_quirks(Env & env, Device const & dev) void Driver::pci_msi_enable(Env & env, + Device_component & dc, addr_t cfg_space, - Irq_session::Info const info) + Irq_session::Info const info, + Device::Irq::Type type) { Attached_io_mem_dataspace io_mem { env, cfg_space, 0x1000 }; Config config { (addr_t)io_mem.local_addr() }; config.scan(); - if (config.msi_cap.constructed()) + + if (type == Device::Irq::Type::MSIX && config.msi_x_cap.constructed()) { + try { + /* find the MSI-x table from the device's memory bars */ + Platform::Device_interface::Range range; + unsigned idx = dc.io_mem_index({config.msi_x_cap->bar()}); + Io_mem_session_client dsc(dc.io_mem(idx, range)); + Attached_dataspace msix_table_ds(env.rm(), dsc.dataspace()); + addr_t msix_table_start = (addr_t)msix_table_ds.local_addr() + + config.msi_x_cap->table_offset(); + + /* disable all msi-x table entries beside the first one */ + unsigned slots = config.msi_x_cap->slots(); + for (unsigned i = 0; i < slots; i++) { + using Entry = Config::Msi_x_capability::Table_entry; + Entry e (msix_table_start + Entry::SIZE * i); + if (!i) { + uint32_t lower = info.address & 0xfffffffc; + uint32_t upper = sizeof(info.address) > 4 ? + (uint32_t)(info.address >> 32) : 0; + e.write(lower); + e.write(upper); + e.write((uint32_t)info.value); + e.write(0); + } else + e.write(1); + } + + config.msi_x_cap->enable(); + } catch(...) { error("Failed to setup MSI-x!"); } + return; + } + + if (type == Device::Irq::Type::MSI && config.msi_cap.constructed()) { config.msi_cap->enable(info.address, (uint16_t)info.value); - else error("Device does not support MSI(-x)!"); + return; + } + + error("Device does not support MSI(-x)!"); } diff --git a/repos/os/src/drivers/platform/pci.h b/repos/os/src/drivers/platform/pci.h index 08126f5f33..a33869da24 100644 --- a/repos/os/src/drivers/platform/pci.h +++ b/repos/os/src/drivers/platform/pci.h @@ -18,15 +18,18 @@ #include #include +#include + namespace Driver { - class Device; + class Device_component; class Device_pd; void pci_enable(Genode::Env & env, Device_pd & pd, Device const & dev); void pci_disable(Genode::Env & env, Device const & dev); void pci_apply_quirks(Genode::Env & env, Device const & dev); - void pci_msi_enable(Genode::Env & env, addr_t cfg_space, - Genode::Irq_session::Info const info); + void pci_msi_enable(Genode::Env & env, Device_component & dc, + addr_t cfg_space, Genode::Irq_session::Info const info, + Device::Irq::Type type); bool pci_device_matches(Genode::Session_policy const & policy, Device const & dev); void pci_device_specific_info(Device const & dev,