platform: add IRQ remapping support

genodelabs/genode#5066
This commit is contained in:
Johannes Schlatow 2024-03-20 18:17:38 +01:00 committed by Christian Helmuth
parent b136ed0dfc
commit 5006b009cb
9 changed files with 299 additions and 22 deletions

View File

@ -20,6 +20,7 @@
#include <root.h>
#include <device_owner.h>
#include <io_mmu.h>
#include <irq_controller.h>
#include <device_pd.h>
namespace Driver { class Common; };
@ -43,6 +44,9 @@ class Driver::Common : Device_reporter,
Io_mmu_devices _io_mmu_devices { };
Registry<Io_mmu_factory> _io_mmu_factories { };
Registry<Irq_controller> _irq_controller_registry { };
Registry<Irq_controller_factory> _irq_controller_factories { };
Driver::Root _root;
Constructible<Expanding_reporter> _cfg_reporter { };
@ -71,9 +75,13 @@ class Driver::Common : Device_reporter,
Xml_node platform_info() {
return _platform_info.xml(); }
Registry<Irq_controller_factory> & irq_controller_factories() {
return _irq_controller_factories; }
void announce_service();
void handle_config(Xml_node config);
void acquire_io_mmu_devices();
void acquire_irq_controller();
void report_resume();
@ -161,11 +169,30 @@ void Driver::Common::acquire_io_mmu_devices()
}
void Driver::Common::acquire_irq_controller()
{
_irq_controller_factories.for_each([&] (Irq_controller_factory & factory) {
_devices.for_each([&] (Device & dev) {
if (dev.owner().valid())
return;
if (factory.matches(dev)) {
dev.acquire(*this);
factory.create(_heap, _irq_controller_registry, dev);
}
});
});
}
void Driver::Common::_handle_devices()
{
_devices_rom.update();
_devices.update(_devices_rom.xml(), _root);
acquire_io_mmu_devices();
acquire_irq_controller();
update_report();
_root.update_policy();
}
@ -206,6 +233,10 @@ void Driver::Common::disable_device(Device const & device)
if (io_mmu.name() == device.name())
destroy(_heap, &io_mmu);
});
_irq_controller_registry.for_each([&] (Irq_controller & irq_controller) {
if (irq_controller.name() == device.name())
destroy(_heap, &irq_controller);
});
}
@ -243,7 +274,8 @@ Driver::Common::Common(Genode::Env & env,
_env(env),
_rom_name(config_rom.xml().attribute_value("devices_rom",
String<64>("devices"))),
_root(_env, _sliced_heap, config_rom, _devices, _io_mmu_devices, _iommu())
_root(_env, _sliced_heap, config_rom, _devices, _io_mmu_devices,
_irq_controller_registry, _iommu())
{
_devices_rom.sigh(_dev_handler);
_handle_devices();

View File

@ -26,17 +26,43 @@ void Driver::Device_component::_release_resources()
destroy(_session.heap(), &iomem); });
_irq_registry.for_each([&] (Irq & irq) {
destroy(_session.heap(), &irq); });
/* unmap IRQ from corresponding remapping table */
if (irq.type == Irq_session::TYPE_LEGACY) {
_session.irq_controller_registry().for_each([&] (Irq_controller & controller) {
if (!controller.handles_irq(irq.number)) return;
_session.with_io_mmu(controller.iommu(), [&] (Driver::Io_mmu & io_mmu_dev) {
io_mmu_dev.unmap_irq(controller.bdf(), irq.remapped_nbr);
});
});
} else {
_io_mmu_registry.for_each([&] (Io_mmu & io_mmu) {
if (_pci_config.constructed())
_session.with_io_mmu(io_mmu.name, [&] (Driver::Io_mmu & io_mmu_dev) {
io_mmu_dev.unmap_irq(_pci_config->bdf, irq.remapped_nbr); });
});
}
destroy(_session.heap(), &irq);
});
_io_port_range_registry.for_each([&] (Io_port_range & iop) {
destroy(_session.heap(), &iop); });
/* remove reserved memory ranges from IOMMU domains */
_session.domain_registry().for_each_domain(
[&] (Driver::Io_mmu::Domain & domain) {
_reserved_mem_registry.for_each([&] (Io_mem & iomem) {
domain.remove_range(iomem.range);
});
_io_mmu_registry.for_each([&] (Io_mmu & io_mmu) {
_session.domain_registry().with_domain(io_mmu.name,
[&] (Driver::Io_mmu::Domain & domain) {
/* remove reserved memory ranges from IOMMU domains */
_reserved_mem_registry.for_each([&] (Io_mem & iomem) {
domain.remove_range(iomem.range); });
},
[&] () {} /* no match */
);
destroy(_session.heap(), &io_mmu);
});
_reserved_mem_registry.for_each([&] (Io_mem & iomem) {
@ -93,27 +119,77 @@ Device_component::io_mem(unsigned idx, Range &range)
Genode::Irq_session_capability Device_component::irq(unsigned idx)
{
using Irq_config = Irq_controller::Irq_config;
Irq_session_capability cap;
auto remapped_irq = [&] (Device::Name const & iommu_name,
Pci::Bdf const & bdf,
Irq & irq,
Irq_session::Info const & info,
Irq_config const & config)
{
using Irq_info = Driver::Io_mmu::Irq_info;
Irq_info remapped_irq { Irq_info::DIRECT, info, irq.number };
auto map_fn = [&] (Device::Name const & name) {
_session.with_io_mmu(name, [&] (Driver::Io_mmu & io_mmu) {
remapped_irq = io_mmu.map_irq(bdf, remapped_irq, config);
});
};
/* for legacy IRQs, take IOMMU referenced by IOAPIC */
if (iommu_name != "")
map_fn(iommu_name);
else
_io_mmu_registry.for_each([&] (Io_mmu const & io_mmu) {
map_fn(io_mmu.name);
});
/* store remapped number at irq object */
irq.remapped_nbr = remapped_irq.irq_number;
return remapped_irq;
};
_irq_registry.for_each([&] (Irq & irq)
{
if (irq.idx != idx)
return;
if (!irq.shared && !irq.irq.constructed()) {
addr_t pci_cfg_addr = 0;
addr_t pci_cfg_addr = 0;
Pci::Bdf bdf { 0, 0, 0 };
if (irq.type != Irq_session::TYPE_LEGACY) {
if (_pci_config.constructed()) pci_cfg_addr = _pci_config->addr;
else
if (_pci_config.constructed()) {
pci_cfg_addr = _pci_config->addr;
bdf = _pci_config->bdf;
} else
error("MSI(-x) detected for device without pci-config!");
irq.irq.construct(_env, irq.number, pci_cfg_addr, irq.type);
} else
irq.irq.construct(_env, irq.number, irq.mode, irq.polarity);
/**
* Core/Kernel is and remains in control of the IRQ controller. When
* IRQ remapping is enabled, however, we need to modify the upper 32bit
* of the corresponding redirection table entry. This is save for
* base-hw as it never touches the upper 32bit after the initial setup.
*/
Irq_session::Info info = irq.irq->info();
if (pci_cfg_addr && info.type == Irq_session::Info::MSI)
pci_msi_enable(_env, *this, pci_cfg_addr, info, irq.type);
pci_msi_enable(_env, *this, pci_cfg_addr,
remapped_irq("", bdf, irq, info, Irq_config::Invalid()).session_info,
irq.type);
else
_session.irq_controller_registry().for_each([&] (Irq_controller & controller) {
if (!controller.handles_irq(irq.number)) return;
remapped_irq(controller.iommu(), controller.bdf(), irq, info,
controller.irq_config(irq.number));
controller.remap_irq(irq.number, irq.remapped_nbr);
});
}
if (irq.shared && !irq.sirq.constructed())
@ -121,6 +197,15 @@ Genode::Irq_session_capability Device_component::irq(unsigned idx)
[&] (Shared_interrupt & sirq) {
irq.sirq.construct(_env.ep().rpc_ep(), sirq,
irq.mode, irq.polarity);
_session.irq_controller_registry().for_each([&] (Irq_controller & controller) {
if (!controller.handles_irq(irq.number)) return;
remapped_irq(controller.iommu(), controller.bdf(), irq,
{ Irq_session::Info::INVALID, 0, 0 },
controller.irq_config(irq.number));
controller.remap_irq(irq.number, irq.remapped_nbr);
});
});
cap = irq.shared ? irq.sirq->cap() : irq.irq->cap();
@ -218,7 +303,8 @@ Device_component::Device_component(Registry<Device_component> & registry,
_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;
_pci_config.construct(cfg.addr);
Pci::Bdf bdf { cfg.bus_num, cfg.dev_num, cfg.func_num };
_pci_config.construct(cfg.addr, bdf);
});
device.for_each_reserved_memory([&] (unsigned idx, Range range)
@ -249,7 +335,10 @@ Device_component::Device_component(Registry<Device_component> & registry,
[&] (Driver::Device::Io_mmu const &io_mmu) {
session.domain_registry().with_domain(io_mmu.name,
add_range_fn,
default_domain_fn); },
[&] () { });
/* save IOMMU names for this device */
new (session.heap()) Io_mmu(_io_mmu_registry, io_mmu.name);
},
/* empty list fn */
default_domain_fn

View File

@ -40,6 +40,7 @@ class Driver::Device_component : public Rpc_object<Platform::Device_interface,
{
unsigned idx;
unsigned number;
unsigned remapped_nbr;
Irq_session::Type type;
Irq_session::Polarity polarity;
Irq_session::Trigger mode;
@ -56,7 +57,7 @@ class Driver::Device_component : public Rpc_object<Platform::Device_interface,
bool shared)
:
Registry<Irq>::Element(registry, *this),
idx(idx), number(number), type(type),
idx(idx), number(number), remapped_nbr(number), type(type),
polarity(polarity), mode(mode), shared(shared) {}
};
@ -96,11 +97,22 @@ class Driver::Device_component : public Rpc_object<Platform::Device_interface,
idx(idx), range(range) {}
};
struct Io_mmu : Registry<Io_mmu>::Element
{
Device::Name name;
Io_mmu(Registry<Io_mmu> & registry, Device::Name const & name)
:
Registry<Io_mmu>::Element(registry, *this),
name(name) {}
};
struct Pci_config
{
addr_t addr;
addr_t addr;
Pci::Bdf bdf;
Pci_config(addr_t addr) : addr(addr) {}
Pci_config(addr_t addr, Pci::Bdf bdf) : addr(addr), bdf(bdf) {}
};
Device_component(Registry<Device_component> & registry,
@ -136,6 +148,7 @@ class Driver::Device_component : public Rpc_object<Platform::Device_interface,
Registry<Io_mem> _io_mem_registry {};
Registry<Io_port_range> _io_port_range_registry {};
Registry<Io_mem> _reserved_mem_registry {};
Registry<Io_mmu> _io_mmu_registry {};
Constructible<Pci_config> _pci_config {};
void _release_resources();

View File

@ -22,6 +22,7 @@
/* local includes */
#include <device.h>
#include <dma_allocator.h>
#include <irq_controller.h>
namespace Driver
{
@ -38,7 +39,14 @@ class Driver::Io_mmu : private Io_mmu_devices::Element
{
public:
using Range = Platform::Device_interface::Range;
using Range = Platform::Device_interface::Range;
using Irq_config = Irq_controller::Irq_config;
struct Irq_info {
enum { DIRECT, REMAPPED } remapped;
Irq_session::Info session_info;
unsigned irq_number;
};
class Domain : private Registry<Domain>::Element
{
@ -147,6 +155,11 @@ class Driver::Io_mmu : private Io_mmu_devices::Element
/* interface for completing default mappings (enabled IOMMU) */
virtual void default_mappings_complete() { }
/* interface for mapping/unmapping interrupts */
virtual void unmap_irq(Pci::Bdf const &, unsigned) { }
virtual Irq_info map_irq(Pci::Bdf const &, Irq_info const & info, Irq_config const &) {
return info; }
Device::Name const & name() const { return _name; }
bool domain_owner(Domain const & domain) const {

View File

@ -0,0 +1,102 @@
/*
* \brief Platform driver - IRQ controller interface
* \author Johannes Schlatow
* \date 2024-03-20
*/
/*
* Copyright (C) 2024 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SRC__DRIVERS__PLATFORM__IRQ_CONTROLLER_H_
#define _SRC__DRIVERS__PLATFORM__IRQ_CONTROLLER_H_
/* Genode includes */
#include <base/registry.h>
#include <pci/types.h>
/* local includes */
#include <device.h>
namespace Driver
{
using namespace Genode;
class Irq_controller;
class Irq_controller_factory;
}
class Driver::Irq_controller : private Registry<Irq_controller>::Element
{
private:
Device::Name _name;
Device::Name _iommu_name;
Pci::Bdf _bdf;
public:
struct Irq_config
{
enum Mode { INVALID,
PHYSICAL,
LOGICAL } mode;
Irq_session::Trigger trigger;
unsigned vector;
unsigned destination;
static Irq_config Invalid() {
return { Mode::INVALID, Irq_session::TRIGGER_UNCHANGED, 0, 0 }; }
};
Device::Name const & name() const { return _name; }
Device::Name const & iommu() const { return _iommu_name; }
Pci::Bdf const & bdf() const { return _bdf;}
virtual void remap_irq(unsigned from, unsigned to) = 0;
virtual bool handles_irq(unsigned) = 0;
virtual Irq_config irq_config(unsigned) = 0;
Irq_controller(Registry<Irq_controller> & registry,
Device::Name const & name,
Device::Name const & iommu_name,
Pci::Bdf const & bdf)
: Registry<Irq_controller>::Element(registry, *this),
_name(name), _iommu_name(iommu_name), _bdf(bdf)
{ }
virtual ~Irq_controller() { }
};
class Driver::Irq_controller_factory : private Genode::Registry<Irq_controller_factory>::Element
{
protected:
Device::Type _type;
public:
Irq_controller_factory(Registry<Irq_controller_factory> & registry,
Device::Type const & type)
: Registry<Irq_controller_factory>::Element(registry, *this),
_type(type)
{ }
virtual ~Irq_controller_factory() { }
bool matches(Device const & dev) {
return dev.type() == _type; }
virtual void create(Allocator &,
Registry<Irq_controller> &,
Device const &) = 0;
};
#endif /* _SRC__DRIVERS__PLATFORM__IRQ_CONTROLLER_H_ */

View File

@ -43,6 +43,7 @@ Driver::Session_component * Driver::Root::_create_session(const char *args)
sc = new (md_alloc())
Session_component(_env, _config, _devices, _sessions, _io_mmu_devices,
_irq_controller_registry,
label,
session_resources_from_args(args),
session_diag_from_args(args),
@ -111,8 +112,11 @@ Driver::Root::Root(Env & env,
Attached_rom_dataspace const & config,
Device_model & devices,
Io_mmu_devices & io_mmu_devices,
Registry<Irq_controller> & irq_controller_registry,
bool const kernel_iommu)
: Root_component<Session_component>(env.ep(), sliced_heap),
_env(env), _config(config), _devices(devices),
_io_mmu_devices(io_mmu_devices), _kernel_iommu(kernel_iommu)
_io_mmu_devices(io_mmu_devices),
_irq_controller_registry(irq_controller_registry),
_kernel_iommu(kernel_iommu)
{ }

View File

@ -34,6 +34,7 @@ class Driver::Root : public Root_component<Session_component>,
Attached_rom_dataspace const & config,
Device_model & devices,
Io_mmu_devices & io_mmu_devices,
Registry<Irq_controller> & irq_controller_registry,
bool const kernel_iommu);
void update_policy();
@ -69,6 +70,7 @@ class Driver::Root : public Root_component<Session_component>,
Attached_rom_dataspace const & _config;
Device_model & _devices;
Io_mmu_devices & _io_mmu_devices;
Registry<Irq_controller> & _irq_controller_registry;
bool _io_mmu_present { false };
bool const _kernel_iommu;
Registry<Session_component> _sessions {};

View File

@ -218,6 +218,12 @@ Driver::Io_mmu_domain_registry & Session_component::domain_registry()
}
Genode::Registry<Driver::Irq_controller> & Session_component::irq_controller_registry()
{
return _irq_controller_registry;
}
void Session_component::update_devices_rom()
{
_rom_session.trigger_update();
@ -418,6 +424,7 @@ Session_component::Session_component(Env & env,
Device_model & devices,
Session_registry & registry,
Io_mmu_devices & io_mmu_devices,
Registry<Irq_controller> & irq_controller_registry,
Label const & label,
Resources const & resources,
Diag const & diag,
@ -430,7 +437,9 @@ Session_component::Session_component(Env & env,
Session_registry::Element(registry, *this),
Dynamic_rom_session::Xml_producer("devices"),
_env(env), _config(config), _devices(devices),
_io_mmu_devices(io_mmu_devices), _info(info), _version(version),
_io_mmu_devices(io_mmu_devices),
_irq_controller_registry(irq_controller_registry),
_info(info), _version(version),
_dma_allocator(_md_alloc, dma_remapping)
{
/*

View File

@ -28,6 +28,7 @@
#include <device_owner.h>
#include <io_mmu.h>
#include <io_mmu_domain_registry.h>
#include <irq_controller.h>
namespace Driver {
class Session_component;
@ -52,6 +53,7 @@ class Driver::Session_component
Device_model & devices,
Session_registry & registry,
Io_mmu_devices & io_mmu_devices,
Registry<Irq_controller> & irq_controller_registry,
Label const & label,
Resources const & resources,
Diag const & diag,
@ -62,8 +64,18 @@ class Driver::Session_component
~Session_component();
Heap & heap();
Io_mmu_domain_registry & domain_registry();
Heap & heap();
Io_mmu_domain_registry & domain_registry();
Registry<Irq_controller> & irq_controller_registry();
template <typename FN>
void with_io_mmu(Device::Name const & name, FN && fn)
{
_io_mmu_devices.for_each([&] (Io_mmu & io_mmu) {
if (io_mmu.name() == name)
fn(io_mmu);
});
}
void enable_dma_remapping() { _dma_allocator.enable_remapping(); }
@ -107,6 +119,7 @@ class Driver::Session_component
Device_model & _devices;
Io_mmu_devices & _io_mmu_devices;
Registry<Irq_controller> & _irq_controller_registry;
Device::Owner _owner_id { *this };
Constrained_ram_allocator _env_ram { _env.pd(),
_ram_quota_guard(),