From d657b61f1b8be8f07f8a097076e21a3a134572f4 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Thu, 6 Feb 2020 14:54:16 +0100 Subject: [PATCH] x86: support msi-x by platform driver Fixes #3699 --- .../drivers/platform/spec/x86/pci_device.cc | 160 +++++++++++++----- .../platform/spec/x86/pci_device_component.h | 64 +++++-- .../platform/spec/x86/pci_session_component.h | 17 ++ 3 files changed, 188 insertions(+), 53 deletions(-) diff --git a/repos/os/src/drivers/platform/spec/x86/pci_device.cc b/repos/os/src/drivers/platform/spec/x86/pci_device.cc index cb2e1a4b7..cf2c20c7c 100644 --- a/repos/os/src/drivers/platform/spec/x86/pci_device.cc +++ b/repos/os/src/drivers/platform/spec/x86/pci_device.cc @@ -174,30 +174,59 @@ Genode::Irq_session_capability Platform::Device_component::irq(Genode::uint8_t i return _irq_session->cap(); } + Genode::uint16_t const msi_cap = _msi_cap(); + Genode::uint16_t const msix_cap = _msix_cap(); + _irq_session = construct_at(_mem_irq_component, - _configure_irq(_irq_line), - (!_session.msi_usage() || !_msi_cap()) ? ~0UL : _config_space, + _configure_irq(_irq_line, msi_cap, msix_cap), + (!_session.msi_usage() || (!msi_cap && !msix_cap)) ? ~0UL : _config_space, _env, _global_heap); _env.ep().rpc_ep().manage(_irq_session); - Genode::uint16_t msi_cap = _msi_cap(); + bool msix_used = false; + bool msi_used = false; if (_irq_session->msi()) { + if (msix_cap) + msix_used = _setup_msix(msix_cap); + if (!msix_used && msi_cap) + msi_used = _setup_msi(msi_cap); + } - Genode::addr_t msi_address = _irq_session->msi_address(); - Genode::uint32_t msi_value = _irq_session->msi_data(); + if (_irq_session->msi()) + Genode::log(_device_config, " uses ", + msix_used ? "MSI-X " : "", + msi_used ? "MSI ": "", + (!msi_used && !msix_used) ? "no MSI/-X/IRQ " : "", + "vector ", Genode::Hex(_irq_session->msi_data()), ", " + "address ", Genode::Hex(_irq_session->msi_address())); + else + Genode::log(_device_config, " uses IRQ, vector ", + Genode::Hex(_irq_line), + ", supports: ", + msi_cap ? " MSI" : "", + msix_cap ? " MSI-X" : ""); - Genode::uint16_t msi = _device_config.read(_config_access, - msi_cap + 2, - Platform::Device::ACCESS_16BIT); + return _irq_session->cap(); +} + +bool Platform::Device_component::_setup_msi(Genode::uint16_t const msi_cap) +{ + try { + addr_t const msi_address = _irq_session->msi_address(); + uint32_t const msi_value = _irq_session->msi_data(); + + uint16_t msi = _device_config.read(_config_access, + msi_cap + 2, + Platform::Device::ACCESS_16BIT); _device_config.write(_config_access, msi_cap + 0x4, msi_address, Platform::Device::ACCESS_32BIT); if (msi & CAP_MSI_64) { - Genode::uint32_t upper_address = sizeof(msi_address) > 4 - ? (Genode::uint64_t)msi_address >> 32 - : 0UL; + uint32_t upper_address = sizeof(msi_address) > 4 + ? uint64_t(msi_address) >> 32 + : 0UL; _device_config.write(_config_access, msi_cap + 0x8, upper_address, @@ -205,8 +234,7 @@ Genode::Irq_session_capability Platform::Device_component::irq(Genode::uint8_t i _device_config.write(_config_access, msi_cap + 0xc, msi_value, Platform::Device::ACCESS_16BIT); - } - else + } else _device_config.write(_config_access, msi_cap + 0x8, msi_value, Platform::Device::ACCESS_16BIT); @@ -214,31 +242,87 @@ Genode::Irq_session_capability Platform::Device_component::irq(Genode::uint8_t i _device_config.write(_config_access, msi_cap + 2, msi ^ MSI_ENABLED, Platform::Device::ACCESS_8BIT); - } - bool msi_64 = false; - bool msi_mask = false; - if (msi_cap) { - Genode::uint16_t msi = _device_config.read(_config_access, - msi_cap + 2, - Platform::Device::ACCESS_16BIT); - msi_64 = msi & CAP_MSI_64; - msi_mask = msi & CAP_MASK; - } + msi = _device_config.read(_config_access, + msi_cap + 2, + Platform::Device::ACCESS_16BIT); + return msi & MSI_ENABLED; + } catch (...) { } - if (_irq_session->msi()) - Genode::log(_device_config, " uses ", - "MSI ", (msi_64 ? "64bit" : "32bit"), ", " - "vector ", Genode::Hex(_irq_session->msi_data()), ", " - "address ", Genode::Hex(_irq_session->msi_address()), ", ", - (msi_mask ? "maskable" : "non-maskable")); - else - Genode::log(_device_config, " uses IRQ, vector ", - Genode::Hex(_irq_line), - (msi_cap ? (msi_64 ? ", MSI 64bit capable" - : ", MSI 32bit capable") - : ""), - (msi_mask ? ", maskable" : ", non-maskable")); - - return _irq_session->cap(); + return false; +} + +bool Platform::Device_component::_setup_msix(Genode::uint16_t const msix_cap) +{ + try { + struct Table_pba : Register<32> + { + struct Bir : Bitfield<0, 3> { }; + struct Offset : Bitfield<3, 29> { }; + }; + + addr_t const msi_address = _irq_session->msi_address(); + uint32_t const msi_value = _irq_session->msi_data(); + + uint16_t ctrl = _device_config.read(_config_access, + msix_cap + 2, + Platform::Device::ACCESS_16BIT); + + uint32_t const table = _device_config.read(_config_access, + msix_cap + 4, + Platform::Device::ACCESS_32BIT); + + uint32_t const slots = Msix_ctrl::Slots::get(ctrl) + 1; + + uint8_t const table_bir = Table_pba::Bir::masked(table); + uint32_t const table_off = Table_pba::Offset::masked(table); + + enum { SIZEOF_MSI_TABLE_ENTRY = 16, SIZE_IOMEM = 0x1000 }; + + Pci::Resource res = _device_config.resource(table_bir); + if (!slots || !res.valid() || res.size() < SIZE_IOMEM || + table_off > res.size() - SIZE_IOMEM) + return false; + + Genode::uint64_t const msix_base = res.base() + table_off; + + Io_mem io_mem(_env, msix_base, SIZE_IOMEM, false); + Attached_dataspace x(_env.rm(), io_mem.dataspace()); + + if (slots * SIZEOF_MSI_TABLE_ENTRY > SIZE_IOMEM) + return false; + + struct Msi_entry : public Mmio { + Msi_entry(addr_t const base) : Mmio(base) { } + + struct Address : Register<0x0, 64> { }; + struct Value : Register<0x8, 32> { }; + struct Vector : Register<0xc, 32> { + struct Mask : Bitfield <0, 1> { }; + }; + } msi_entry_0 (reinterpret_cast(x.local_addr())); + + /* setup first msi-x table entry */ + msi_entry_0.write(msi_address & ~(0x3UL)); + msi_entry_0.write(msi_value); + msi_entry_0.write(0); + + /* disable all msi-x table entries beside the first one */ + for (unsigned i = 1; i < slots; i++) { + struct Msi_entry unused (reinterpret_cast(x.local_addr()) + SIZEOF_MSI_TABLE_ENTRY * i); + unused.write(1); + } + + /* enable MSI-X */ + Msix_ctrl::Fmask::set(ctrl, 0); + Msix_ctrl::Enable::set(ctrl, 1); + _device_config.write(_config_access, msix_cap + 2, ctrl, + Platform::Device::ACCESS_16BIT); + + ctrl = _device_config.read(_config_access, msix_cap + 2, + Platform::Device::ACCESS_16BIT); + return Msix_ctrl::Enable::get(ctrl); + } catch (...) { } + + return false; } diff --git a/repos/os/src/drivers/platform/spec/x86/pci_device_component.h b/repos/os/src/drivers/platform/spec/x86/pci_device_component.h index d7645891a..460b7a7d4 100644 --- a/repos/os/src/drivers/platform/spec/x86/pci_device_component.h +++ b/repos/os/src/drivers/platform/spec/x86/pci_device_component.h @@ -77,10 +77,16 @@ class Platform::Device_component : public Genode::Rpc_object, PCI_IRQ_PIN = 0x3d, CAP_MSI_64 = 0x80, - CAP_MASK = 0x100, MSI_ENABLED = 0x1 }; + struct Msix_ctrl : Register<16> + { + struct Slots : Bitfield< 0, 10> { }; + struct Fmask : Bitfield<14, 1> { }; + struct Enable : Bitfield<15, 1> { }; + }; + Genode::Tslab _slab_ioport; char _slab_ioport_block_data[IO_BLOCK_SIZE]; @@ -106,7 +112,19 @@ class Platform::Device_component : public Genode::Rpc_object, */ Genode::uint16_t _msi_cap() { - enum { PCI_STATUS = 0x6, PCI_CAP_OFFSET = 0x34, CAP_MSI = 0x5 }; + enum { CAP_MSI = 0x5 }; + return _lookup_cap(CAP_MSI); + } + + Genode::uint16_t _msix_cap() + { + enum { CAP_MSI_X = 0x11 }; + return _lookup_cap(CAP_MSI_X); + } + + Genode::uint16_t _lookup_cap(Genode::uint16_t const target_cap) + { + enum { PCI_STATUS = 0x6, PCI_CAP_OFFSET = 0x34 }; Status::access_t status = Status::read(_device_config.read(_config_access, PCI_STATUS, @@ -121,7 +139,7 @@ class Platform::Device_component : public Genode::Rpc_object, for (Genode::uint16_t val = 0; cap; cap = val >> 8) { val = _device_config.read(_config_access, cap, Platform::Device::ACCESS_16BIT); - if ((val & 0xff) != CAP_MSI) + if ((val & 0xff) != target_cap) continue; return cap; @@ -132,9 +150,10 @@ class Platform::Device_component : public Genode::Rpc_object, /** - * Disable MSI if already enabled. + * Disable MSI/MSI-X if already enabled. */ - unsigned _configure_irq(unsigned irq) + unsigned _configure_irq(unsigned irq, uint16_t const msi_cap, + uint16_t const msix_cap) { using Genode::uint16_t; using Genode::uint8_t; @@ -156,18 +175,30 @@ class Platform::Device_component : public Genode::Rpc_object, _irq_line = irq = irq_r; } - uint16_t cap = _msi_cap(); - if (!cap) - return irq; + if (msi_cap) { + uint16_t msi = _device_config.read(_config_access, msi_cap + 2, + Platform::Device::ACCESS_16BIT); - uint16_t msi = _device_config.read(_config_access, cap + 2, - Platform::Device::ACCESS_16BIT); + if (msi & MSI_ENABLED) + /* disable MSI */ + _device_config.write(_config_access, msi_cap + 2, + msi ^ MSI_ENABLED, + Platform::Device::ACCESS_8BIT); + } - if (msi & MSI_ENABLED) - /* disable MSI */ - _device_config.write(_config_access, cap + 2, - msi ^ MSI_ENABLED, - Platform::Device::ACCESS_8BIT); + if (msix_cap) { + uint16_t msix = _device_config.read(_config_access, + msix_cap + 2, + Platform::Device::ACCESS_16BIT); + + if (Msix_ctrl::Enable::get(msix)) { + Msix_ctrl::Enable::set(msix, 0); + + _device_config.write(_config_access, msix_cap + 2, + msix, + Platform::Device::ACCESS_16BIT); + } + } return irq; } @@ -188,6 +219,9 @@ class Platform::Device_component : public Genode::Rpc_object, _device_config.disable_bus_master_dma(_config_access); } + bool _setup_msi(Genode::uint16_t); + bool _setup_msix(Genode::uint16_t); + public: /** diff --git a/repos/os/src/drivers/platform/spec/x86/pci_session_component.h b/repos/os/src/drivers/platform/spec/x86/pci_session_component.h index 6c4376b9e..3004dcb63 100644 --- a/repos/os/src/drivers/platform/spec/x86/pci_session_component.h +++ b/repos/os/src/drivers/platform/spec/x86/pci_session_component.h @@ -1102,6 +1102,23 @@ class Platform::Root : public Genode::Root_component xml.attribute("device_id" , String<8>(Hex(config.device_id()))); xml.attribute("class_code", String<12>(Hex(config.class_code()))); xml.attribute("bridge" , config.pci_bridge() ? "yes" : "no"); + + enum { PCI_STATUS = 0x6, PCI_CAP_OFFSET = 0x34 }; + + try { + config.read(config_access, PCI_STATUS, Platform::Device::ACCESS_16BIT); + + Genode::uint8_t cap = config.read(config_access, + PCI_CAP_OFFSET, + Platform::Device::ACCESS_8BIT); + + for (Genode::uint16_t val = 0; cap; cap = val >> 8) { + val = config.read(config_access, cap, Platform::Device::ACCESS_16BIT); + xml.attribute("cap", String<8>(Hex(val & 0xff))); + } + } catch (...) { + xml.attribute("cap", "failed to read"); + } }); } });