diff --git a/repos/os/include/platform/irq_proxy.h b/repos/os/include/platform/irq_proxy.h
new file mode 100644
index 000000000..11d6c04c1
--- /dev/null
+++ b/repos/os/include/platform/irq_proxy.h
@@ -0,0 +1,242 @@
+/**
+ * \brief Shared-interrupt support
+ * \author Christian Helmuth
+ * \author Sebastian Sumpf
+ * \date 2009-12-15
+ */
+
+#ifndef _CORE__INCLUDE__IRQ_PROXY_H_
+#define _CORE__INCLUDE__IRQ_PROXY_H_
+
+#include
+
+
+namespace Genode {
+ class Irq_sigh;
+ template class Irq_proxy;
+}
+
+
+class Genode::Irq_sigh : public Genode::Signal_context_capability,
+ public Genode::List::Element
+{
+ public:
+
+ inline Irq_sigh * operator= (const Signal_context_capability &cap)
+ {
+ Signal_context_capability::operator=(cap);
+ return this;
+ }
+
+ Irq_sigh() { }
+
+ void notify() { Genode::Signal_transmitter(*this).submit(1); }
+};
+
+
+/*
+ * Proxy thread that associates to the interrupt and unblocks waiting irqctrl
+ * threads.
+ *
+ * XXX resources are not accounted as the interrupt is shared
+ */
+template
+class Genode::Irq_proxy : public THREAD,
+ public Genode::List >::Element
+{
+ protected:
+
+ char _name[32];
+ Lock _startup_lock;
+
+ long _irq_number;
+
+ Lock _mutex; /* protects this object */
+ int _num_sharers; /* number of clients sharing this IRQ */
+ Semaphore _sleep; /* wake me up if aspired blockers return */
+ List _sigh_list;
+ int _num_acknowledgers; /* number of currently blocked clients */
+ bool _woken_up; /* client decided to wake me up -
+ this prevents multiple wakeups
+ to happen during initialization */
+
+
+ /***************
+ ** Interface **
+ ***************/
+
+ /**
+ * Request interrupt
+ *
+ * \return true on success
+ */
+ virtual bool _associate() = 0;
+
+ /**
+ * Wait for associated interrupt
+ */
+ virtual void _wait_for_irq() = 0;
+
+ /**
+ * Acknowledge interrupt
+ */
+ virtual void _ack_irq() = 0;
+
+ /********************
+ ** Implementation **
+ ********************/
+
+ const char *_construct_name(long irq_number)
+ {
+ snprintf(_name, sizeof(_name), "irqproxy%02lx", irq_number);
+ return _name;
+ }
+
+ void _loop()
+ {
+ /* wait for first blocker */
+ _sleep.down();
+
+ while (1) {
+ _wait_for_irq();
+
+ /* notify all */
+ notify_about_irq(1);
+
+ /*
+ * We must wait for all clients to ack their interrupt,
+ * otherwise level-triggered interrupts will occur immediately
+ * after acknowledgement. That's an inherent security problem
+ * with shared IRQs and induces problems with dynamic driver
+ * load and unload.
+ */
+ _sleep.down();
+
+ /* acknowledge previous interrupt */
+ _ack_irq();
+ }
+ }
+
+ /**
+ * Start this thread, should be called externally from derived class
+ */
+ virtual void _start()
+ {
+ THREAD::start();
+ _startup_lock.lock();
+ }
+
+ public:
+
+ Irq_proxy(long irq_number)
+ :
+ THREAD(_construct_name(irq_number)),
+ _startup_lock(Lock::LOCKED), _irq_number(irq_number),
+ _mutex(Lock::UNLOCKED), _num_sharers(0), _num_acknowledgers(0), _woken_up(false)
+
+ { }
+
+ /**
+ * Thread interface
+ */
+ void entry()
+ {
+ bool const associate_suceeded = _associate();
+
+ _startup_lock.unlock();
+
+ if (associate_suceeded)
+ _loop();
+ }
+
+ /**
+ * Acknowledgements of clients
+ */
+ virtual bool ack_irq()
+ {
+ Lock::Guard lock_guard(_mutex);
+
+ _num_acknowledgers++;
+
+ /*
+ * The proxy thread has to be woken up if no client woke it up
+ * before and this client is the last aspired acknowledger.
+ */
+ if (!_woken_up && _num_acknowledgers == _num_sharers) {
+ _sleep.up();
+ _woken_up = true;
+ }
+
+ return _woken_up;
+ }
+
+ /**
+ * Notify all clients about irq
+ */
+ void notify_about_irq(unsigned)
+ {
+ Lock::Guard lock_guard(_mutex);
+
+ /* reset acknowledger state */
+ _num_acknowledgers = 0;
+ _woken_up = false;
+
+ /* inform blocked clients */
+ for (Irq_sigh * s = _sigh_list.first(); s ; s = s->next())
+ s->notify();
+ }
+
+ long irq_number() const { return _irq_number; }
+
+ virtual bool add_sharer(Irq_sigh *s)
+ {
+ Lock::Guard lock_guard(_mutex);
+
+ ++_num_sharers;
+ _sigh_list.insert(s);
+
+ return true;
+ }
+
+ virtual bool remove_sharer(Irq_sigh *s)
+ {
+ Lock::Guard lock_guard(_mutex);
+
+ _sigh_list.remove(s);
+ --_num_sharers;
+
+ if (_woken_up)
+ return _num_sharers == 0;
+
+ if (_num_acknowledgers == _num_sharers) {
+ _sleep.up();
+ _woken_up = true;
+ }
+
+ return _num_sharers == 0;
+ }
+
+ template
+ static PROXY *get_irq_proxy(long irq_number, Range_allocator *irq_alloc = 0)
+ {
+ static List proxies;
+ static Lock proxies_lock;
+
+ Lock::Guard lock_guard(proxies_lock);
+
+ /* lookup proxy in database */
+ for (Irq_proxy *p = proxies.first(); p; p = p->next())
+ if (p->irq_number() == irq_number)
+ return static_cast(p);
+
+ /* try to create proxy */
+ if (!irq_alloc || irq_alloc->alloc_addr(1, irq_number).is_error())
+ return 0;
+
+ PROXY *new_proxy = new (env()->heap()) PROXY(irq_number);
+ proxies.insert(new_proxy);
+ return new_proxy;
+ }
+};
+
+#endif /* _CORE__INCLUDE__IRQ_PROXY_H_ */
diff --git a/repos/os/src/drivers/pci/irq.cc b/repos/os/src/drivers/pci/irq.cc
new file mode 100644
index 000000000..3c4a4a5c0
--- /dev/null
+++ b/repos/os/src/drivers/pci/irq.cc
@@ -0,0 +1,241 @@
+/*
+ * \brief Implementation of shared IRQs in PCI driver
+ * \author Alexander Boettcher
+ * \date 2015-03-27
+ */
+
+/*
+ * Copyright (C) 2015 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU General Public License version 2.
+ */
+
+/* Genode includes */
+#include
+#include
+
+/* Genode OS includes */
+#include
+
+/* PCI driver include */
+#include "irq.h"
+
+
+namespace Pci {
+ class Irq_component;
+ class Irq_allocator;
+ class Irq_thread;
+}
+
+
+using namespace Genode;
+
+
+/**
+ * A simple range allocator implementation used by the Irq_proxy
+ */
+class Pci::Irq_allocator : public Range_allocator, Bit_allocator<256>
+{
+ Alloc_return alloc_addr(size_t size, addr_t addr) override
+ {
+ try {
+ _array.set(addr, size);
+ return Alloc_return::OK;
+ } catch (...) {
+ return Alloc_return::RANGE_CONFLICT;
+ }
+ }
+
+ /* unused methods */
+ int remove_range(addr_t, size_t) override { return 0; }
+ int add_range(addr_t, size_t) override { return 0; }
+ bool valid_addr(addr_t) const override { return false; }
+ size_t avail() const override { return 0; }
+ bool alloc(size_t, void **) override { return false; }
+ void free(void *) override { }
+ void free(void *, size_t) override { }
+ size_t overhead(size_t) const override { return 0; }
+ bool need_size_for_free() const override { return 0; }
+
+ Alloc_return alloc_aligned(size_t, void **, int, addr_t, addr_t) override {
+ return Alloc_return::RANGE_CONFLICT; }
+};
+
+
+/**
+ * Required by Irq_proxy if we would like to have a thread per IRQ,
+ * which we don't want to in the PCI driver - one thread is sufficient.
+ */
+class NoThread
+{
+ public:
+
+ NoThread(const char *) { }
+
+ void start(void) { }
+};
+
+
+/**
+ * Thread waiting for signals caused by IRQs
+ */
+class Pci::Irq_thread : public Genode::Thread<4096>
+{
+ private:
+
+ Signal_receiver _sig_rec;
+
+ public:
+
+ Irq_thread() : Thread<4096>("irq_sig_recv") { start(); }
+
+ Signal_receiver & sig_rec() { return _sig_rec; }
+
+ void entry() {
+
+ typedef Genode::Signal_dispatcher_base Sdb;
+
+ while (1) {
+ Genode::Signal sig = _sig_rec.wait_for_signal();
+
+ Sdb *dispatcher = dynamic_cast(sig.context());
+
+ if (!dispatcher) {
+ PERR("dispatcher missing for signal %p, %u",
+ sig.context(), sig.num());
+ continue;
+ }
+ dispatcher->dispatch(sig.num());
+ }
+ }
+};
+
+
+/**
+ * One allocator for managing in use IRQ numbers and one IRQ thread waiting
+ * for Genode signals of all hardware IRQs.
+ */
+static Pci::Irq_allocator irq_alloc;
+static Pci::Irq_thread irq_thread;
+
+
+/**
+ * Irq_proxy interface implementation
+ */
+typedef Genode::Irq_proxy Proxy;
+
+class Pci::Irq_component : public Proxy
+{
+ private:
+
+ Genode::Irq_connection _irq;
+ Genode::Signal_dispatcher _irq_dispatcher;
+
+ bool _associated;
+
+ public:
+
+ void _ack_irq() {
+ /*
+ * Associate handler only when required, because our partner may
+ * also implement shared irq and would expect to get ack_irq()
+ * form us even if we have no client ...
+ */
+ if (!_associated) {
+ _associated = true;
+ /* register signal handler on irq_session */
+ _irq.sigh(_irq_dispatcher);
+ }
+
+ _irq.ack_irq();
+ }
+
+ bool _associate() { return _associated; }
+ void _wait_for_irq() { }
+
+ virtual bool remove_sharer(Irq_sigh *s) override {
+ if (!Proxy::remove_sharer(s))
+ return false;
+
+ /* De-associate handler. */
+ _associated = false;
+ _irq.sigh(Genode::Signal_context_capability());
+ return true;
+ }
+
+ public:
+
+ Irq_component(unsigned gsi)
+ :
+ Proxy(gsi), _irq(gsi), _irq_dispatcher(irq_thread.sig_rec(), *this,
+ &::Proxy::notify_about_irq),
+ _associated(false)
+ { }
+};
+
+
+
+/*******************************
+ ** PCI IRQ session component **
+ *******************************/
+
+void Pci::Irq_session_component::ack_irq()
+{
+ Irq_component *irq_obj = Proxy::get_irq_proxy(_gsi);
+ if (!irq_obj) {
+ PERR("Expected to find IRQ proxy for IRQ %02x", _gsi);
+ return;
+ }
+
+ if (irq_obj->ack_irq())
+ irq_obj->_ack_irq();
+}
+
+
+Pci::Irq_session_component::Irq_session_component(unsigned irq) : _gsi(irq)
+{
+ bool valid = false;
+
+ /* invalid irq number for pci devices */
+ if (irq == 0xFF)
+ return;
+
+ try {
+ /* check if IRQ object was used before */
+ valid = Proxy::get_irq_proxy(_gsi, &irq_alloc);
+ } catch (Genode::Parent::Service_denied) { }
+
+ if (!valid)
+ PERR("unavailable IRQ object 0x%x requested", _gsi);
+}
+
+
+Pci::Irq_session_component::~Irq_session_component()
+{
+ Irq_component *irq_obj = Proxy::get_irq_proxy(_gsi);
+ if (!irq_obj) return;
+
+ if (_irq_sigh.valid())
+ irq_obj->remove_sharer(&_irq_sigh);
+}
+
+
+void Pci::Irq_session_component::sigh(Genode::Signal_context_capability sigh)
+{
+ Irq_component *irq_obj = Proxy::get_irq_proxy(_gsi);
+ if (!irq_obj) {
+ PERR("signal handler got not registered - irq object unavailable");
+ return;
+ }
+
+ Genode::Signal_context_capability old = _irq_sigh;
+
+ if (old.valid() && !sigh.valid())
+ irq_obj->remove_sharer(&_irq_sigh);
+
+ _irq_sigh = sigh;
+
+ if (!old.valid() && sigh.valid())
+ irq_obj->add_sharer(&_irq_sigh);
+}
diff --git a/repos/os/src/drivers/pci/irq.h b/repos/os/src/drivers/pci/irq.h
new file mode 100644
index 000000000..1f9c93b03
--- /dev/null
+++ b/repos/os/src/drivers/pci/irq.h
@@ -0,0 +1,49 @@
+/*
+ * \brief IRQ session interface
+ * \author Alexander Boettcher
+ * \date 2015-03-25
+ */
+
+/*
+ * Copyright (C) 2015 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU General Public License version 2.
+ */
+
+#pragma once
+
+#include
+
+#include
+
+#include
+#include
+
+/* PCI local includes */
+#include
+
+
+namespace Pci { class Irq_session_component; }
+
+
+class Pci::Irq_session_component : public Genode::Rpc_object,
+ public Genode::List::Element
+{
+ private:
+
+ unsigned _gsi;
+ Genode::Irq_sigh _irq_sigh;
+
+ public:
+
+ Irq_session_component(unsigned);
+ ~Irq_session_component();
+
+ /***************************
+ ** Irq session interface **
+ ***************************/
+
+ void ack_irq() override;
+ void sigh(Genode::Signal_context_capability) override;
+};
diff --git a/repos/os/src/drivers/pci/pci_device_component.h b/repos/os/src/drivers/pci/pci_device_component.h
index 878618405..23f15fdd9 100644
--- a/repos/os/src/drivers/pci/pci_device_component.h
+++ b/repos/os/src/drivers/pci/pci_device_component.h
@@ -20,106 +20,111 @@
#include
#include
-#include
#include "pci_device_config.h"
-namespace Pci {
+#include "irq.h"
- class Device_component : public Genode::Rpc_object,
- public Genode::List::Element
- {
- private:
+namespace Pci { class Device_component; }
- Device_config _device_config;
- Genode::addr_t _config_space;
- Genode::Io_mem_connection *_io_mem;
- Config_access _config_access;
- Genode::Irq_connection _irq;
+class Pci::Device_component : public Genode::Rpc_object,
+ public Genode::List::Element
+{
+ private:
- enum { PCI_IRQ = 0x3c };
+ Device_config _device_config;
+ Genode::addr_t _config_space;
+ Genode::Io_mem_connection *_io_mem;
+ Config_access _config_access;
+ Genode::Rpc_entrypoint *_ep;
+ Irq_session_component _irq_session;
- public:
+ enum { PCI_IRQ = 0x3c };
- /**
- * Constructor
+ public:
+
+ /**
+ * Constructor
+ */
+ Device_component(Device_config device_config, Genode::addr_t addr,
+ Genode::Rpc_entrypoint *ep)
+ :
+ _device_config(device_config), _config_space(addr),
+ _io_mem(0), _ep(ep),
+ _irq_session(_device_config.read(&_config_access, PCI_IRQ,
+ Pci::Device::ACCESS_8BIT))
+ {
+ _ep->manage(&_irq_session);
+ }
+
+ /**
+ * De-constructor
+ */
+ ~Device_component()
+ {
+ _ep->dissolve(&_irq_session);
+ }
+
+ /****************************************
+ ** Methods used solely by pci session **
+ ****************************************/
+
+ Device_config config() { return _device_config; }
+
+ Genode::addr_t config_space() { return _config_space; }
+
+ void set_config_space(Genode::Io_mem_connection * io_mem) {
+ _io_mem = io_mem; }
+
+ Genode::Io_mem_connection * get_config_space() { return _io_mem; }
+
+ /**************************
+ ** PCI-device interface **
+ **************************/
+
+ void bus_address(unsigned char *bus, unsigned char *dev,
+ unsigned char *fn)
+ {
+ *bus = _device_config.bus_number();
+ *dev = _device_config.device_number();
+ *fn = _device_config.function_number();
+ }
+
+ unsigned short vendor_id() { return _device_config.vendor_id(); }
+
+ unsigned short device_id() { return _device_config.device_id(); }
+
+ unsigned class_code() { return _device_config.class_code(); }
+
+ Resource resource(int resource_id)
+ {
+ /* return invalid resource if device is invalid */
+ if (!_device_config.valid())
+ Resource(0, 0);
+
+ return _device_config.resource(resource_id);
+ }
+
+ unsigned config_read(unsigned char address, Access_size size)
+ {
+ return _device_config.read(&_config_access, address, size);
+ }
+
+ void config_write(unsigned char address, unsigned value, Access_size size)
+ {
+ /*
+ * XXX implement policy to prevent write access to base-address registers
*/
- Device_component(Device_config device_config, Genode::addr_t addr)
- :
- _device_config(device_config), _config_space(addr),
- _io_mem(0),
- _irq(_device_config.read(&_config_access, PCI_IRQ,
- Pci::Device::ACCESS_8BIT))
- { }
- /****************************************
- ** Methods used solely by pci session **
- ****************************************/
+ _device_config.write(&_config_access, address, value, size);
+ }
- Device_config config() { return _device_config; }
-
- Genode::addr_t config_space() { return _config_space; }
-
- void set_config_space(Genode::Io_mem_connection * io_mem) {
- _io_mem = io_mem; }
-
- Genode::Io_mem_connection * get_config_space() { return _io_mem; }
-
- /**************************
- ** PCI-device interface **
- **************************/
-
- void bus_address(unsigned char *bus, unsigned char *dev,
- unsigned char *fn)
- {
- *bus = _device_config.bus_number();
- *dev = _device_config.device_number();
- *fn = _device_config.function_number();
- }
-
- unsigned short vendor_id() {
- return _device_config.vendor_id(); }
-
- unsigned short device_id() {
- return _device_config.device_id(); }
-
- unsigned class_code() {
- return _device_config.class_code(); }
-
- Resource resource(int resource_id)
- {
- /* return invalid resource if device is invalid */
- if (!_device_config.valid())
- Resource(0, 0);
-
- return _device_config.resource(resource_id);
- }
-
- unsigned config_read(unsigned char address, Access_size size)
- {
- Config_access config_access;
-
- return _device_config.read(&config_access, address, size);
- }
-
- void config_write(unsigned char address, unsigned value, Access_size size)
- {
- Config_access config_access;
-
- /*
- * XXX implement policy to prevent write access to base-address registers
- */
-
- _device_config.write(&config_access, address, value, size);
- }
-
- Genode::Irq_session_capability irq(Genode::uint8_t id) override
- {
- if (id != 0)
- return Genode::Irq_session_capability();
- return _irq_session.cap();
- }
- };
-}
+ Genode::Irq_session_capability irq(Genode::uint8_t id) override
+ {
+ if (id != 0)
+ return Genode::Irq_session_capability();
+ return _irq_session.cap();
+ }
+};
#endif /* _PCI_DEVICE_COMPONENT_H_ */
diff --git a/repos/os/src/drivers/pci/pci_session_component.h b/repos/os/src/drivers/pci/pci_session_component.h
index 87b11ae56..c32bea8ca 100644
--- a/repos/os/src/drivers/pci/pci_session_component.h
+++ b/repos/os/src/drivers/pci/pci_session_component.h
@@ -253,7 +253,7 @@ namespace Pci {
* FIXME: check and adjust session quota
*/
Device_component *device_component =
- new (_md_alloc) Device_component(config, config_space);
+ new (_md_alloc) Device_component(config, config_space, _ep);
if (!device_component)
return Device_capability();
diff --git a/repos/os/src/drivers/pci/x86/target.mk b/repos/os/src/drivers/pci/x86/target.mk
index 2e67d748f..b3c3c97e2 100644
--- a/repos/os/src/drivers/pci/x86/target.mk
+++ b/repos/os/src/drivers/pci/x86/target.mk
@@ -1,8 +1,8 @@
TARGET = pci_drv
REQUIRES = x86
-SRC_CC = main.cc
+SRC_CC = main.cc irq.cc
LIBS = base config
INC_DIR = $(PRG_DIR)/..
-vpath main.cc $(PRG_DIR)/..
+vpath %.cc $(PRG_DIR)/..