diff --git a/repos/os/src/driver/platform/common.h b/repos/os/src/driver/platform/common.h index 0f0216faf7..f640937d44 100644 --- a/repos/os/src/driver/platform/common.h +++ b/repos/os/src/driver/platform/common.h @@ -20,6 +20,7 @@ #include #include #include +#include #include namespace Driver { class Common; }; @@ -43,6 +44,9 @@ class Driver::Common : Device_reporter, Io_mmu_devices _io_mmu_devices { }; Registry _io_mmu_factories { }; + Registry _irq_controller_registry { }; + Registry _irq_controller_factories { }; + Driver::Root _root; Constructible _cfg_reporter { }; @@ -71,9 +75,13 @@ class Driver::Common : Device_reporter, Xml_node platform_info() { return _platform_info.xml(); } + Registry & 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(); diff --git a/repos/os/src/driver/platform/device_component.cc b/repos/os/src/driver/platform/device_component.cc index 5f7bccb6e3..bd6d5a23f1 100644 --- a/repos/os/src/driver/platform/device_component.cc +++ b/repos/os/src/driver/platform/device_component.cc @@ -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 & 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 & 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 diff --git a/repos/os/src/driver/platform/device_component.h b/repos/os/src/driver/platform/device_component.h index 5c2f5894ba..c18a619f67 100644 --- a/repos/os/src/driver/platform/device_component.h +++ b/repos/os/src/driver/platform/device_component.h @@ -40,6 +40,7 @@ class Driver::Device_component : public Rpc_object::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::Element + { + Device::Name name; + + Io_mmu(Registry & registry, Device::Name const & name) + : + Registry::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 & registry, @@ -136,6 +148,7 @@ class Driver::Device_component : public Rpc_object _io_mem_registry {}; Registry _io_port_range_registry {}; Registry _reserved_mem_registry {}; + Registry _io_mmu_registry {}; Constructible _pci_config {}; void _release_resources(); diff --git a/repos/os/src/driver/platform/io_mmu.h b/repos/os/src/driver/platform/io_mmu.h index 33201e6584..ad2158c0df 100644 --- a/repos/os/src/driver/platform/io_mmu.h +++ b/repos/os/src/driver/platform/io_mmu.h @@ -22,6 +22,7 @@ /* local includes */ #include #include +#include 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::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 { diff --git a/repos/os/src/driver/platform/irq_controller.h b/repos/os/src/driver/platform/irq_controller.h new file mode 100644 index 0000000000..7e405ab43d --- /dev/null +++ b/repos/os/src/driver/platform/irq_controller.h @@ -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 +#include + +/* local includes */ +#include + +namespace Driver +{ + using namespace Genode; + + class Irq_controller; + class Irq_controller_factory; +} + + +class Driver::Irq_controller : private Registry::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 & registry, + Device::Name const & name, + Device::Name const & iommu_name, + Pci::Bdf const & bdf) + : Registry::Element(registry, *this), + _name(name), _iommu_name(iommu_name), _bdf(bdf) + { } + + virtual ~Irq_controller() { } +}; + + +class Driver::Irq_controller_factory : private Genode::Registry::Element +{ + protected: + + Device::Type _type; + + public: + + Irq_controller_factory(Registry & registry, + Device::Type const & type) + : Registry::Element(registry, *this), + _type(type) + { } + + virtual ~Irq_controller_factory() { } + + bool matches(Device const & dev) { + return dev.type() == _type; } + + virtual void create(Allocator &, + Registry &, + Device const &) = 0; +}; + + +#endif /* _SRC__DRIVERS__PLATFORM__IRQ_CONTROLLER_H_ */ diff --git a/repos/os/src/driver/platform/root.cc b/repos/os/src/driver/platform/root.cc index 092566f4d7..3edefd4a31 100644 --- a/repos/os/src/driver/platform/root.cc +++ b/repos/os/src/driver/platform/root.cc @@ -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_registry, bool const kernel_iommu) : Root_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) { } diff --git a/repos/os/src/driver/platform/root.h b/repos/os/src/driver/platform/root.h index d9f2bb6c3d..17f1855e92 100644 --- a/repos/os/src/driver/platform/root.h +++ b/repos/os/src/driver/platform/root.h @@ -34,6 +34,7 @@ class Driver::Root : public Root_component, Attached_rom_dataspace const & config, Device_model & devices, Io_mmu_devices & io_mmu_devices, + Registry & irq_controller_registry, bool const kernel_iommu); void update_policy(); @@ -69,6 +70,7 @@ class Driver::Root : public Root_component, Attached_rom_dataspace const & _config; Device_model & _devices; Io_mmu_devices & _io_mmu_devices; + Registry & _irq_controller_registry; bool _io_mmu_present { false }; bool const _kernel_iommu; Registry _sessions {}; diff --git a/repos/os/src/driver/platform/session_component.cc b/repos/os/src/driver/platform/session_component.cc index 2de8cb521a..21d45eeb09 100644 --- a/repos/os/src/driver/platform/session_component.cc +++ b/repos/os/src/driver/platform/session_component.cc @@ -218,6 +218,12 @@ Driver::Io_mmu_domain_registry & Session_component::domain_registry() } +Genode::Registry & 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_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) { /* diff --git a/repos/os/src/driver/platform/session_component.h b/repos/os/src/driver/platform/session_component.h index 8d1d96b245..79287b5cb2 100644 --- a/repos/os/src/driver/platform/session_component.h +++ b/repos/os/src/driver/platform/session_component.h @@ -28,6 +28,7 @@ #include #include #include +#include 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_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_registry(); + + template + 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_registry; Device::Owner _owner_id { *this }; Constrained_ram_allocator _env_ram { _env.pd(), _ram_quota_guard(),