platform_drv: implement MSI-x support

Ref genodelabs/genode#4578
This commit is contained in:
Stefan Kalkowski 2022-10-05 18:25:55 +02:00 committed by Christian Helmuth
parent 9c3867e173
commit 3841ee1d51
5 changed files with 86 additions and 11 deletions

View File

@ -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<Table::Bar_index>(); }
Genode::size_t table_offset() {
return read<Table::Offset>() << 3; }
unsigned slots() { return read<Control::Slots>(); }
void enable()
{
Control::access_t ctrl = read<Control>();
Control::Function_mask::set(ctrl, 0);
Control::Enable::set(ctrl, 1);
write<Control>(ctrl);
}
};

View File

@ -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<Device_component> & 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<Device_component> & 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(),

View File

@ -62,18 +62,22 @@ class Driver::Device_component : public Rpc_object<Platform::Device_interface,
struct Io_mem : Registry<Io_mem>::Element
{
using Pci_bar = Device::Pci_bar;
Pci_bar bar;
unsigned idx;
Range range;
bool prefetchable;
Constructible<Io_mem_connection> io_mem {};
Io_mem(Registry<Io_mem> & registry,
Pci_bar bar,
unsigned idx,
Range range,
bool pf)
:
Registry<Io_mem>::Element(registry, *this),
idx(idx), range(range), prefetchable(pf) {}
bar(bar), idx(idx), range(range), prefetchable(pf) {}
};
struct Io_port_range : Registry<Io_port_range>::Element
@ -108,6 +112,7 @@ class Driver::Device_component : public Rpc_object<Platform::Device_interface,
Driver::Device::Name device() const;
Session_component & session();
unsigned io_mem_index(Device::Pci_bar bar);
/************************************

View File

@ -17,6 +17,7 @@
#include <device.h>
#include <device_pd.h>
#include <device_component.h>
#include <pci.h>
#include <pci_uhci.h>
#include <pci_ehci.h>
@ -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<void>() };
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<void>()
+ 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<Entry::Address_64_lower>(lower);
e.write<Entry::Address_64_upper>(upper);
e.write<Entry::Data>((uint32_t)info.value);
e.write<Entry::Vector_control::Mask>(0);
} else
e.write<Entry::Vector_control::Mask>(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)!");
}

View File

@ -18,15 +18,18 @@
#include <irq_session/irq_session.h>
#include <os/session_policy.h>
#include <device.h>
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,