diff --git a/repos/os/src/drivers/platform/device.cc b/repos/os/src/drivers/platform/device.cc index fa584fc357..082d0d8a4b 100644 --- a/repos/os/src/drivers/platform/device.cc +++ b/repos/os/src/drivers/platform/device.cc @@ -11,6 +11,8 @@ * under the terms of the GNU Affero General Public License version 3. */ +#include + #include #include #include @@ -137,6 +139,7 @@ void Driver::Device::generate(Xml_generator & xml, bool info) const if (!info) return; xml.attribute("number", irq.number); + if (irq.shared) xml.attribute("shared", true); }); }); _io_port_range_list.for_each([&] (Io_port_range const & iop) { @@ -206,6 +209,52 @@ void Driver::Device_model::update(Xml_node const & node) { _model.update_from_xml(*this, node); + /* + * Detect all shared interrupts + */ + enum { MAX_IRQ = 1024 }; + Bit_array detected_irqs, shared_irqs; + for_each([&] (Device const & device) { + device._irq_list.for_each([&] (Device::Irq const & irq) { + + if (irq.type != Device::Irq::LEGACY) + return; + + if (detected_irqs.get(irq.number, 1)) { + if (!shared_irqs.get(irq.number, 1)) + shared_irqs.set(irq.number, 1); + } else + detected_irqs.set(irq.number, 1); + }); + }); + + /* + * Mark all shared interrupts in the devices + */ + for_each([&] (Device & device) { + device._irq_list.for_each([&] (Device::Irq & irq) { + + if (irq.type != Device::Irq::LEGACY) + return; + + if (shared_irqs.get(irq.number, 1)) + irq.shared = true; + }); + }); + + /* + * Create shared interrupt objects + */ + for (unsigned i = 0; i < MAX_IRQ; i++) { + if (!shared_irqs.get(i, 1)) + continue; + bool found = false; + _shared_irqs.for_each([&] (Shared_interrupt & sirq) { + if (sirq.number() == i) found = true; }); + if (!found) + new (_heap) Shared_interrupt(_shared_irqs, _env, i); + } + /* * Iterate over all devices and apply PCI quirks if necessary */ diff --git a/repos/os/src/drivers/platform/device.h b/repos/os/src/drivers/platform/device.h index 7b527917ee..9b6d415bcc 100644 --- a/repos/os/src/drivers/platform/device.h +++ b/repos/os/src/drivers/platform/device.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -98,6 +99,7 @@ class Driver::Device : private List_model::Element Type type { LEGACY }; Irq_session::Polarity polarity { Irq_session::POLARITY_UNCHANGED }; Irq_session::Trigger mode { Irq_session::TRIGGER_UNCHANGED }; + bool shared { false }; Irq(unsigned number) : number(number) {} }; @@ -222,7 +224,8 @@ class Driver::Device : private List_model::Element { unsigned idx = 0; _irq_list.for_each([&] (Irq const & irq) { - fn(idx++, irq.number, irq.type, irq.polarity, irq.mode); }); + fn(idx++, irq.number, irq.type, irq.polarity, + irq.mode, irq.shared); }); } template void for_each_io_mem(FN const & fn) const @@ -308,13 +311,14 @@ class Driver::Device_model : { private: - Env & _env; - Heap & _heap; - Device_reporter & _reporter; - List_model _model { }; - Clocks _clocks { }; - Resets _resets { }; - Powers _powers { }; + Env & _env; + Heap & _heap; + Device_reporter & _reporter; + List_model _model { }; + Registry _shared_irqs { }; + Clocks _clocks { }; + Resets _resets { }; + Powers _powers { }; public: @@ -336,6 +340,13 @@ class Driver::Device_model : template void for_each(FN const & fn) const { _model.for_each(fn); } + template + void with_shared_irq(unsigned number, FN const & fn) + { + _shared_irqs.for_each([&] (Shared_interrupt & sirq) { + if (sirq.number() == number) fn(sirq); }); + } + /*********************** ** Update_policy API ** diff --git a/repos/os/src/drivers/platform/device_component.cc b/repos/os/src/drivers/platform/device_component.cc index ff0aa9d4dc..3a7b6780bf 100644 --- a/repos/os/src/drivers/platform/device_component.cc +++ b/repos/os/src/drivers/platform/device_component.cc @@ -15,6 +15,7 @@ #include #include #include +#include using Driver::Device_component; @@ -80,7 +81,7 @@ Genode::Irq_session_capability Device_component::irq(unsigned idx) if (irq.idx != idx) return; - if (!irq.irq.constructed()) { + if (!irq.shared && !irq.irq.constructed()) { addr_t pci_cfg_addr = 0; if (irq.type != Device::Irq::LEGACY) { if (_pci_config.constructed()) pci_cfg_addr = _pci_config->addr; @@ -94,7 +95,14 @@ Genode::Irq_session_capability Device_component::irq(unsigned idx) pci_msi_enable(_env, pci_cfg_addr, info); } - cap = irq.irq->cap(); + if (irq.shared && !irq.sirq.constructed()) + _device_model.with_shared_irq(irq.number, + [&] (Shared_interrupt & sirq) { + irq.sirq.construct(sirq, irq.mode, irq.polarity); + _env.ep().rpc_ep().manage(&*irq.sirq); + }); + + cap = irq.shared ? irq.sirq->cap() : irq.irq->cap(); }); return cap; @@ -124,10 +132,12 @@ Genode::Io_port_session_capability Device_component::io_port_range(unsigned idx) Device_component::Device_component(Registry & registry, Env & env, Driver::Session_component & session, + Driver::Device_model & model, Driver::Device & device) : _env(env), _session(session), + _device_model(model), _device(device.name()), _reg_elem(registry, *this) { @@ -150,13 +160,15 @@ Device_component::Device_component(Registry & registry, unsigned nr, Device::Irq::Type type, Irq_session::Polarity polarity, - Irq_session::Trigger mode) + Irq_session::Trigger mode, + bool shared) { session.ram_quota_guard().withdraw(Ram_quota{Irq_session::RAM_QUOTA}); _ram_quota += Irq_session::RAM_QUOTA; session.cap_quota_guard().withdraw(Cap_quota{Irq_session::CAP_QUOTA}); _cap_quota += Irq_session::CAP_QUOTA; - new (session.heap()) Irq(_irq_registry, idx, nr, type, polarity, mode); + new (session.heap()) Irq(_irq_registry, idx, nr, type, polarity, + mode, shared); }); device.for_each_io_mem([&] (unsigned idx, Range range, diff --git a/repos/os/src/drivers/platform/device_component.h b/repos/os/src/drivers/platform/device_component.h index 8ba561ced0..2f0de6bb0f 100644 --- a/repos/os/src/drivers/platform/device_component.h +++ b/repos/os/src/drivers/platform/device_component.h @@ -38,23 +38,26 @@ class Driver::Device_component : public Rpc_object::Element { - unsigned idx; - unsigned number; - Device::Irq::Type type; - Irq_session::Polarity polarity; - Irq_session::Trigger mode; - Constructible irq {}; + unsigned idx; + unsigned number; + Device::Irq::Type type; + Irq_session::Polarity polarity; + Irq_session::Trigger mode; + bool shared; + Constructible irq {}; + Constructible sirq {}; Irq(Registry & registry, unsigned idx, unsigned number, Device::Irq::Type type, Irq_session::Polarity polarity, - Irq_session::Trigger mode) + Irq_session::Trigger mode, + bool shared) : Registry::Element(registry, *this), idx(idx), number(number), type(type), - polarity(polarity), mode(mode) {} + polarity(polarity), mode(mode), shared(shared) {} }; struct Io_mem : Registry::Element @@ -99,6 +102,7 @@ class Driver::Device_component : public Rpc_object & registry, Env & env, Session_component & session, + Device_model & model, Driver::Device & device); ~Device_component(); @@ -118,6 +122,7 @@ class Driver::Device_component : public Rpc_object Session_component::_acquire(Device & device) { Device_component * dc = new (heap()) - Device_component(_device_registry, _env, *this, device); + Device_component(_device_registry, _env, *this, _devices, device); device.acquire(*this); return _env.ep().rpc_ep().manage(dc); }; diff --git a/repos/os/src/drivers/platform/shared_irq.cc b/repos/os/src/drivers/platform/shared_irq.cc new file mode 100644 index 0000000000..a87587937d --- /dev/null +++ b/repos/os/src/drivers/platform/shared_irq.cc @@ -0,0 +1,65 @@ +/* + * \brief Platform driver - shared interrupts + * \author Stefan Kalkowski + * \date 2022-09-27 + */ + +/* + * Copyright (C) 2022 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. + */ + +#include + +using Driver::Shared_interrupt; +using Driver::Shared_interrupt_session; + + +void Shared_interrupt::_handle() +{ + _sessions.for_each([&] (Shared_interrupt_session & session) { + session.signal(); }); +} + + +void Shared_interrupt::enable(Irq_session::Trigger mode, + Irq_session::Polarity polarity) +{ + if (!_irq.constructed()) { + _irq.construct(_env, _number, mode, polarity); + _irq->sigh(_handler); + } +} + + +void Shared_interrupt::disable() +{ + unsigned session_count = 0; + _sessions.for_each([&] (Shared_interrupt_session &) { + session_count++; }); + + /* if it is the last session, close the upstream connection */ + if (session_count <= 1) + _irq.destruct(); +} + + +void Shared_interrupt::ack() +{ + unsigned outstanding = 0; + _sessions.for_each([&] (Shared_interrupt_session & session) { + if (session.outstanding()) outstanding++; }); + if (!outstanding) _irq->ack_irq(); +} + + +void Shared_interrupt_session::signal() +{ + if (!_cap.valid()) + return; + + _outstanding = true; + Signal_transmitter(_cap).submit(1); +} diff --git a/repos/os/src/drivers/platform/shared_irq.h b/repos/os/src/drivers/platform/shared_irq.h new file mode 100644 index 0000000000..b7198a01d7 --- /dev/null +++ b/repos/os/src/drivers/platform/shared_irq.h @@ -0,0 +1,110 @@ +/* + * \brief Platform driver - shared interrupts + * \author Stefan Kalkowski + * \date 2022-09-27 + */ + +/* + * Copyright (C) 2022 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__SHARED_IRQ_H_ +#define _SRC__DRIVERS__PLATFORM__SHARED_IRQ_H_ + +#include +#include +#include + +namespace Driver { + using namespace Genode; + + class Shared_interrupt; + class Shared_interrupt_session; +} + + +class Driver::Shared_interrupt : + private Registry::Element +{ + private: + + Env & _env; + unsigned _number; + Io_signal_handler _handler; + Constructible _irq {}; + Registry _sessions {}; + + void _handle(); + + friend class Shared_interrupt_session; + + public: + + Shared_interrupt(Registry & registry, + Env & env, + unsigned number) + : + Registry::Element(registry, *this), + _env(env), + _number(number), + _handler(env.ep(), *this, &Shared_interrupt::_handle) {} + + unsigned number() const { return _number; } + + void enable(Irq_session::Trigger mode, + Irq_session::Polarity polarity); + void disable(); + void ack(); +}; + + +class Driver::Shared_interrupt_session : + public Rpc_object, + private Registry::Element +{ + private: + + Shared_interrupt & _sirq; + Signal_context_capability _cap {}; + bool _outstanding { true }; + + public: + + Shared_interrupt_session(Shared_interrupt & sirq, + Irq_session::Trigger mode, + Irq_session::Polarity polarity) + : + Registry::Element(sirq._sessions, *this), + _sirq(sirq) + { + sirq.enable(mode, polarity); + } + + ~Shared_interrupt_session() { _sirq.disable(); } + + bool outstanding() { return _outstanding; } + void signal(); + + + /*************************** + ** Irq session interface ** + ***************************/ + + void ack_irq() override + { + _outstanding = false; + _sirq.ack(); + } + + void sigh(Signal_context_capability cap) override { _cap = cap; } + + Info info() override + { + return { .type = Info::Type::INVALID, .address = 0, .value = 0 }; + } +}; + +#endif /* _SRC__DRIVERS__PLATFORM__SHARED_IRQ_H_ */ diff --git a/repos/os/src/drivers/platform/target.inc b/repos/os/src/drivers/platform/target.inc index 15fa2656b4..f6ea60c1aa 100644 --- a/repos/os/src/drivers/platform/target.inc +++ b/repos/os/src/drivers/platform/target.inc @@ -6,6 +6,7 @@ SRC_CC += main.cc SRC_CC += pci.cc SRC_CC += root.cc SRC_CC += session_component.cc +SRC_CC += shared_irq.cc GENERIC_DIR := $(dir $(call select_from_repositories,src/drivers/platform/target.inc))