mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 13:47:56 +00:00
pci: support shared irqs (x86)
Step to move shared irq handling out of core in the long run. So, use irq_proxy implementation from base in os and implement shared irq handling in platform driver of x86 (pci_drv). Fixes #1471
This commit is contained in:
parent
31faee3b5c
commit
6dd9d349fc
242
repos/os/include/platform/irq_proxy.h
Normal file
242
repos/os/include/platform/irq_proxy.h
Normal file
@ -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 <base/env.h>
|
||||
|
||||
|
||||
namespace Genode {
|
||||
class Irq_sigh;
|
||||
template <typename THREAD> class Irq_proxy;
|
||||
}
|
||||
|
||||
|
||||
class Genode::Irq_sigh : public Genode::Signal_context_capability,
|
||||
public Genode::List<Genode::Irq_sigh>::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 <typename THREAD>
|
||||
class Genode::Irq_proxy : public THREAD,
|
||||
public Genode::List<Genode::Irq_proxy<THREAD> >::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<Irq_sigh> _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 <typename PROXY>
|
||||
static PROXY *get_irq_proxy(long irq_number, Range_allocator *irq_alloc = 0)
|
||||
{
|
||||
static List<Irq_proxy> 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<PROXY *>(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_ */
|
241
repos/os/src/drivers/pci/irq.cc
Normal file
241
repos/os/src/drivers/pci/irq.cc
Normal file
@ -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 <base/printf.h>
|
||||
#include <irq_session/connection.h>
|
||||
|
||||
/* Genode OS includes */
|
||||
#include <platform/irq_proxy.h>
|
||||
|
||||
/* 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<Sdb *>(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<NoThread> Proxy;
|
||||
|
||||
class Pci::Irq_component : public Proxy
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Irq_connection _irq;
|
||||
Genode::Signal_dispatcher<Pci::Irq_component> _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<Irq_component>(_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<Irq_component>(_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<Irq_component>(_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<Irq_component>(_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);
|
||||
}
|
49
repos/os/src/drivers/pci/irq.h
Normal file
49
repos/os/src/drivers/pci/irq.h
Normal file
@ -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 <base/rpc_server.h>
|
||||
|
||||
#include <util/list.h>
|
||||
|
||||
#include <irq_session/irq_session.h>
|
||||
#include <irq_session/capability.h>
|
||||
|
||||
/* PCI local includes */
|
||||
#include <platform/irq_proxy.h>
|
||||
|
||||
|
||||
namespace Pci { class Irq_session_component; }
|
||||
|
||||
|
||||
class Pci::Irq_session_component : public Genode::Rpc_object<Genode::Irq_session>,
|
||||
public Genode::List<Irq_session_component>::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;
|
||||
};
|
@ -20,13 +20,14 @@
|
||||
#include <base/printf.h>
|
||||
|
||||
#include <io_mem_session/io_mem_session.h>
|
||||
#include <irq_session/connection.h>
|
||||
|
||||
#include "pci_device_config.h"
|
||||
|
||||
namespace Pci {
|
||||
#include "irq.h"
|
||||
|
||||
class Device_component : public Genode::Rpc_object<Device>,
|
||||
namespace Pci { class Device_component; }
|
||||
|
||||
class Pci::Device_component : public Genode::Rpc_object<Pci::Device>,
|
||||
public Genode::List<Device_component>::Element
|
||||
{
|
||||
private:
|
||||
@ -35,7 +36,8 @@ namespace Pci {
|
||||
Genode::addr_t _config_space;
|
||||
Genode::Io_mem_connection *_io_mem;
|
||||
Config_access _config_access;
|
||||
Genode::Irq_connection _irq;
|
||||
Genode::Rpc_entrypoint *_ep;
|
||||
Irq_session_component _irq_session;
|
||||
|
||||
enum { PCI_IRQ = 0x3c };
|
||||
|
||||
@ -44,13 +46,24 @@ namespace Pci {
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Device_component(Device_config device_config, Genode::addr_t addr)
|
||||
Device_component(Device_config device_config, Genode::addr_t addr,
|
||||
Genode::Rpc_entrypoint *ep)
|
||||
:
|
||||
_device_config(device_config), _config_space(addr),
|
||||
_io_mem(0),
|
||||
_irq(_device_config.read(&_config_access, PCI_IRQ,
|
||||
_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 **
|
||||
@ -77,14 +90,11 @@ namespace Pci {
|
||||
*fn = _device_config.function_number();
|
||||
}
|
||||
|
||||
unsigned short vendor_id() {
|
||||
return _device_config.vendor_id(); }
|
||||
unsigned short vendor_id() { return _device_config.vendor_id(); }
|
||||
|
||||
unsigned short device_id() {
|
||||
return _device_config.device_id(); }
|
||||
unsigned short device_id() { return _device_config.device_id(); }
|
||||
|
||||
unsigned class_code() {
|
||||
return _device_config.class_code(); }
|
||||
unsigned class_code() { return _device_config.class_code(); }
|
||||
|
||||
Resource resource(int resource_id)
|
||||
{
|
||||
@ -97,20 +107,16 @@ namespace Pci {
|
||||
|
||||
unsigned config_read(unsigned char address, Access_size size)
|
||||
{
|
||||
Config_access config_access;
|
||||
|
||||
return _device_config.read(&config_access, address, size);
|
||||
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);
|
||||
_device_config.write(&_config_access, address, value, size);
|
||||
}
|
||||
|
||||
Genode::Irq_session_capability irq(Genode::uint8_t id) override
|
||||
@ -120,6 +126,5 @@ namespace Pci {
|
||||
return _irq_session.cap();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _PCI_DEVICE_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();
|
||||
|
@ -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)/..
|
||||
|
Loading…
Reference in New Issue
Block a user