base: remove shared irq from core
Cleanup commit after all relevant drivers got adapted to use the x86 platform driver (pci_drv). Issue #1471
This commit is contained in:
parent
3783db66e2
commit
e84284c0cd
|
@ -13,7 +13,6 @@
|
||||||
|
|
||||||
/* Genode includes */
|
/* Genode includes */
|
||||||
#include <base/printf.h>
|
#include <base/printf.h>
|
||||||
#include <base/sleep.h>
|
|
||||||
|
|
||||||
/* core includes */
|
/* core includes */
|
||||||
#include <irq_root.h>
|
#include <irq_root.h>
|
||||||
|
@ -22,128 +21,109 @@
|
||||||
#include <codezero/syscalls.h>
|
#include <codezero/syscalls.h>
|
||||||
|
|
||||||
|
|
||||||
namespace Genode {
|
|
||||||
typedef Irq_proxy<Thread<1024 * sizeof(addr_t)> > Irq_proxy_base;
|
|
||||||
class Irq_proxy_component;
|
|
||||||
}
|
|
||||||
|
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
|
|
||||||
/**
|
|
||||||
* Platform-specific proxy code
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Genode::Irq_proxy_component : public Irq_proxy_base
|
bool Irq_object::_associate() { return true; }
|
||||||
|
|
||||||
|
|
||||||
|
void Irq_object::_wait_for_irq()
|
||||||
{
|
{
|
||||||
private:
|
using namespace Codezero;
|
||||||
|
|
||||||
bool _irq_attached;
|
/* block for IRQ */
|
||||||
|
int ret = l4_irq_control(IRQ_CONTROL_WAIT, 0, _irq);
|
||||||
protected:
|
if (ret < 0)
|
||||||
|
PWRN("l4_irq_control(IRQ_CONTROL_WAIT) returned %d", ret);
|
||||||
bool _associate() { return true; }
|
}
|
||||||
|
|
||||||
void _wait_for_irq()
|
|
||||||
{
|
|
||||||
using namespace Codezero;
|
|
||||||
|
|
||||||
/* attach thread to IRQ when first called */
|
|
||||||
if (!_irq_attached) {
|
|
||||||
int ret = l4_irq_control(IRQ_CONTROL_REGISTER, 0, _irq_number);
|
|
||||||
if (ret < 0) {
|
|
||||||
PERR("l4_irq_control(IRQ_CONTROL_REGISTER) returned %d", ret);
|
|
||||||
sleep_forever();
|
|
||||||
}
|
|
||||||
_irq_attached = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* block for IRQ */
|
|
||||||
int ret = l4_irq_control(IRQ_CONTROL_WAIT, 0, _irq_number);
|
|
||||||
if (ret < 0)
|
|
||||||
PWRN("l4_irq_control(IRQ_CONTROL_WAIT) returned %d", ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _ack_irq() { }
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Irq_proxy_component(long irq_number)
|
|
||||||
:
|
|
||||||
Irq_proxy(irq_number),
|
|
||||||
_irq_attached(false)
|
|
||||||
{
|
|
||||||
_start();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/***************************
|
void Irq_object::start()
|
||||||
** IRQ session component **
|
|
||||||
***************************/
|
|
||||||
|
|
||||||
|
|
||||||
void Irq_session_component::ack_irq()
|
|
||||||
{
|
{
|
||||||
if (!_proxy) {
|
::Thread_base::start();
|
||||||
PERR("Expected to find IRQ proxy for IRQ %02x", _irq_number);
|
_sync_bootup.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Irq_object::entry()
|
||||||
|
{
|
||||||
|
if (!_associate()) {
|
||||||
|
PERR("Could not associate with IRQ 0x%x", _irq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_proxy->ack_irq();
|
/* thread is up and ready */
|
||||||
|
_sync_bootup.unlock();
|
||||||
|
|
||||||
|
/* wait for first ack_irq */
|
||||||
|
_sync_ack.lock();
|
||||||
|
|
||||||
|
using namespace Codezero;
|
||||||
|
|
||||||
|
/* attach thread to IRQ when first called */
|
||||||
|
int ret = l4_irq_control(IRQ_CONTROL_REGISTER, 0, _irq);
|
||||||
|
if (ret < 0) {
|
||||||
|
PERR("l4_irq_control(IRQ_CONTROL_REGISTER) returned %d", ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
_wait_for_irq();
|
||||||
|
|
||||||
|
if (!_sig_cap.valid())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Genode::Signal_transmitter(_sig_cap).submit(1);
|
||||||
|
|
||||||
|
_sync_ack.lock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Irq_object::Irq_object(unsigned irq)
|
||||||
|
:
|
||||||
|
Thread<4096>("irq"),
|
||||||
|
_sync_ack(Lock::LOCKED), _sync_bootup(Lock::LOCKED),
|
||||||
|
_irq(irq)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
Irq_session_component::Irq_session_component(Range_allocator *irq_alloc,
|
Irq_session_component::Irq_session_component(Range_allocator *irq_alloc,
|
||||||
const char *args)
|
const char *args)
|
||||||
:
|
:
|
||||||
_irq_alloc(irq_alloc)
|
_irq_number(Arg_string::find_arg(args, "irq_number").long_value(-1)),
|
||||||
|
_irq_alloc(irq_alloc),
|
||||||
|
_irq_object(_irq_number)
|
||||||
{
|
{
|
||||||
long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1);
|
|
||||||
if (irq_number == -1) {
|
|
||||||
PERR("invalid IRQ number requested");
|
|
||||||
throw Root::Unavailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
long msi = Arg_string::find_arg(args, "device_config_phys").long_value(0);
|
long msi = Arg_string::find_arg(args, "device_config_phys").long_value(0);
|
||||||
if (msi)
|
if (msi)
|
||||||
throw Root::Unavailable();
|
throw Root::Unavailable();
|
||||||
|
|
||||||
/* check if IRQ thread was started before */
|
if (!irq_alloc || irq_alloc->alloc_addr(1, _irq_number).is_error()) {
|
||||||
_proxy = Irq_proxy_component::get_irq_proxy<Irq_proxy_component>(irq_number, irq_alloc);
|
PERR("Unavailable IRQ 0x%x requested", _irq_number);
|
||||||
if (!_proxy) {
|
|
||||||
PERR("unavailable IRQ %lx requested", irq_number);
|
|
||||||
throw Root::Unavailable();
|
throw Root::Unavailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
_irq_number = irq_number;
|
_irq_object.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Irq_session_component::~Irq_session_component()
|
Irq_session_component::~Irq_session_component()
|
||||||
{
|
{
|
||||||
if (!_proxy) return;
|
PERR("Not yet implemented.");
|
||||||
|
|
||||||
if (_irq_sigh.valid())
|
|
||||||
_proxy->remove_sharer(&_irq_sigh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Irq_session_component::sigh(Genode::Signal_context_capability sigh)
|
void Irq_session_component::ack_irq()
|
||||||
{
|
{
|
||||||
if (!_proxy) {
|
_irq_object.ack_irq();
|
||||||
PERR("signal handler got not registered - irq thread unavailable");
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Genode::Signal_context_capability old = _irq_sigh;
|
|
||||||
|
|
||||||
if (old.valid() && !sigh.valid())
|
void Irq_session_component::sigh(Genode::Signal_context_capability cap)
|
||||||
_proxy->remove_sharer(&_irq_sigh);
|
{
|
||||||
|
_irq_object.sigh(cap);
|
||||||
_irq_sigh = sigh;
|
|
||||||
|
|
||||||
if (!old.valid() && sigh.valid())
|
|
||||||
_proxy->add_sharer(&_irq_sigh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,10 @@
|
||||||
* \brief Core implementation of IRQ sessions
|
* \brief Core implementation of IRQ sessions
|
||||||
* \author Christian Helmuth
|
* \author Christian Helmuth
|
||||||
* \date 2007-09-13
|
* \date 2007-09-13
|
||||||
*
|
|
||||||
* FIXME ram quota missing
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2007-2013 Genode Labs GmbH
|
* Copyright (C) 2007-2015 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
@ -18,7 +16,6 @@
|
||||||
#include <util/arg_string.h>
|
#include <util/arg_string.h>
|
||||||
|
|
||||||
/* core includes */
|
/* core includes */
|
||||||
#include <irq_proxy.h>
|
|
||||||
#include <irq_root.h>
|
#include <irq_root.h>
|
||||||
#include <util.h>
|
#include <util.h>
|
||||||
|
|
||||||
|
@ -29,155 +26,136 @@ namespace Fiasco {
|
||||||
#include <l4/sys/types.h>
|
#include <l4/sys/types.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Genode {
|
using namespace Genode;
|
||||||
typedef Irq_proxy<Thread<1024 * sizeof(addr_t)> > Irq_proxy_base;
|
|
||||||
class Irq_proxy_component;
|
bool Irq_object::_associate()
|
||||||
|
{
|
||||||
|
using namespace Fiasco;
|
||||||
|
|
||||||
|
int err;
|
||||||
|
l4_threadid_t irq_tid;
|
||||||
|
l4_umword_t dw0, dw1;
|
||||||
|
l4_msgdope_t result;
|
||||||
|
|
||||||
|
l4_make_taskid_from_irq(_irq, &irq_tid);
|
||||||
|
|
||||||
|
/* boost thread to IRQ priority */
|
||||||
|
enum { IRQ_PRIORITY = 0xC0 };
|
||||||
|
|
||||||
|
l4_sched_param_t param = {sp:{prio:IRQ_PRIORITY, small:0, state:0, time_exp:0, time_man:0}};
|
||||||
|
l4_threadid_t ext_preempter = L4_INVALID_ID;
|
||||||
|
l4_threadid_t partner = L4_INVALID_ID;
|
||||||
|
l4_sched_param_t old_param;
|
||||||
|
l4_thread_schedule(l4_myself(), param, &ext_preempter, &partner, &old_param);
|
||||||
|
|
||||||
|
err = l4_ipc_receive(irq_tid,
|
||||||
|
L4_IPC_SHORT_MSG, &dw0, &dw1,
|
||||||
|
L4_IPC_BOTH_TIMEOUT_0, &result);
|
||||||
|
|
||||||
|
if (err != L4_IPC_RETIMEOUT) PERR("IRQ association failed");
|
||||||
|
|
||||||
|
return (err == L4_IPC_RETIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
using namespace Genode;
|
void Irq_object::_wait_for_irq()
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Platform-specific proxy code
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Genode::Irq_proxy_component : public Irq_proxy_base
|
|
||||||
{
|
{
|
||||||
protected:
|
using namespace Fiasco;
|
||||||
|
|
||||||
bool _associate()
|
l4_threadid_t irq_tid;
|
||||||
{
|
l4_umword_t dw0=0, dw1=0;
|
||||||
using namespace Fiasco;
|
l4_msgdope_t result;
|
||||||
|
|
||||||
int err;
|
l4_make_taskid_from_irq(_irq, &irq_tid);
|
||||||
l4_threadid_t irq_tid;
|
|
||||||
l4_umword_t dw0, dw1;
|
|
||||||
l4_msgdope_t result;
|
|
||||||
|
|
||||||
l4_make_taskid_from_irq(_irq_number, &irq_tid);
|
do {
|
||||||
|
l4_ipc_call(irq_tid,
|
||||||
|
L4_IPC_SHORT_MSG, 0, 0,
|
||||||
|
L4_IPC_SHORT_MSG, &dw0, &dw1,
|
||||||
|
L4_IPC_NEVER, &result);
|
||||||
|
|
||||||
/* boost thread to IRQ priority */
|
if (L4_IPC_IS_ERROR(result)) PERR("Ipc error %lx", L4_IPC_ERROR(result));
|
||||||
enum { IRQ_PRIORITY = 0xC0 };
|
} while (L4_IPC_IS_ERROR(result));
|
||||||
|
}
|
||||||
l4_sched_param_t param = {sp:{prio:IRQ_PRIORITY, small:0, state:0,
|
|
||||||
time_exp:0, time_man:0}};
|
|
||||||
l4_threadid_t ext_preempter = L4_INVALID_ID;
|
|
||||||
l4_threadid_t partner = L4_INVALID_ID;
|
|
||||||
l4_sched_param_t old_param;
|
|
||||||
l4_thread_schedule(l4_myself(), param, &ext_preempter, &partner,
|
|
||||||
&old_param);
|
|
||||||
|
|
||||||
err = l4_ipc_receive(irq_tid,
|
|
||||||
L4_IPC_SHORT_MSG, &dw0, &dw1,
|
|
||||||
L4_IPC_BOTH_TIMEOUT_0, &result);
|
|
||||||
|
|
||||||
if (err != L4_IPC_RETIMEOUT) PERR("IRQ association failed");
|
|
||||||
|
|
||||||
return (err == L4_IPC_RETIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _wait_for_irq()
|
|
||||||
{
|
|
||||||
using namespace Fiasco;
|
|
||||||
|
|
||||||
l4_threadid_t irq_tid;
|
|
||||||
l4_umword_t dw0=0, dw1=0;
|
|
||||||
l4_msgdope_t result;
|
|
||||||
|
|
||||||
l4_make_taskid_from_irq(_irq_number, &irq_tid);
|
|
||||||
|
|
||||||
do {
|
|
||||||
l4_ipc_call(irq_tid,
|
|
||||||
L4_IPC_SHORT_MSG, 0, 0,
|
|
||||||
L4_IPC_SHORT_MSG, &dw0, &dw1,
|
|
||||||
L4_IPC_NEVER, &result);
|
|
||||||
|
|
||||||
if (L4_IPC_IS_ERROR(result))
|
|
||||||
PERR("Ipc error %lx", L4_IPC_ERROR(result));
|
|
||||||
} while (L4_IPC_IS_ERROR(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _ack_irq() { }
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Irq_proxy_component(long irq_number)
|
|
||||||
:
|
|
||||||
Irq_proxy(irq_number)
|
|
||||||
{
|
|
||||||
_start();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/***************************
|
void Irq_object::start()
|
||||||
** IRQ session component **
|
|
||||||
***************************/
|
|
||||||
|
|
||||||
|
|
||||||
void Irq_session_component::ack_irq()
|
|
||||||
{
|
{
|
||||||
if (!_proxy) {
|
::Thread_base::start();
|
||||||
PERR("Expected to find IRQ proxy for IRQ %02x", _irq_number);
|
_sync_bootup.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Irq_object::entry()
|
||||||
|
{
|
||||||
|
if (!_associate()) {
|
||||||
|
PERR("Could not associate with IRQ 0x%x", _irq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_proxy->ack_irq();
|
/* thread is up and ready */
|
||||||
|
_sync_bootup.unlock();
|
||||||
|
|
||||||
|
/* wait for first ack_irq */
|
||||||
|
_sync_ack.lock();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
_wait_for_irq();
|
||||||
|
|
||||||
|
if (!_sig_cap.valid())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Genode::Signal_transmitter(_sig_cap).submit(1);
|
||||||
|
|
||||||
|
_sync_ack.lock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Irq_object::Irq_object(unsigned irq)
|
||||||
|
:
|
||||||
|
Thread<4096>("irq"),
|
||||||
|
_sync_ack(Lock::LOCKED), _sync_bootup(Lock::LOCKED),
|
||||||
|
_irq(irq)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
Irq_session_component::Irq_session_component(Range_allocator *irq_alloc,
|
Irq_session_component::Irq_session_component(Range_allocator *irq_alloc,
|
||||||
const char *args)
|
const char *args)
|
||||||
:
|
:
|
||||||
_irq_alloc(irq_alloc)
|
_irq_number(Arg_string::find_arg(args, "irq_number").long_value(-1)),
|
||||||
|
_irq_alloc(irq_alloc),
|
||||||
|
_irq_object(_irq_number)
|
||||||
{
|
{
|
||||||
long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1);
|
|
||||||
if (irq_number == -1) {
|
|
||||||
PERR("invalid IRQ number requested");
|
|
||||||
throw Root::Unavailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
long msi = Arg_string::find_arg(args, "device_config_phys").long_value(0);
|
long msi = Arg_string::find_arg(args, "device_config_phys").long_value(0);
|
||||||
if (msi)
|
if (msi)
|
||||||
throw Root::Unavailable();
|
throw Root::Unavailable();
|
||||||
|
|
||||||
/* check if IRQ thread was started before */
|
if (!irq_alloc || irq_alloc->alloc_addr(1, _irq_number).is_error()) {
|
||||||
_proxy = Irq_proxy_component::get_irq_proxy<Irq_proxy_component>(irq_number, irq_alloc);
|
PERR("Unavailable IRQ 0x%x requested", _irq_number);
|
||||||
if (!_proxy) {
|
|
||||||
PERR("unavailable IRQ %lx requested", irq_number);
|
|
||||||
throw Root::Unavailable();
|
throw Root::Unavailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
_irq_number = irq_number;
|
_irq_object.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Irq_session_component::~Irq_session_component()
|
Irq_session_component::~Irq_session_component()
|
||||||
{
|
{
|
||||||
if (!_proxy) return;
|
PERR("Not yet implemented.");
|
||||||
|
|
||||||
if (_irq_sigh.valid())
|
|
||||||
_proxy->remove_sharer(&_irq_sigh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Irq_session_component::sigh(Genode::Signal_context_capability sigh)
|
void Irq_session_component::ack_irq()
|
||||||
{
|
{
|
||||||
if (!_proxy) {
|
_irq_object.ack_irq();
|
||||||
PERR("signal handler got not registered - irq thread unavailable");
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Genode::Signal_context_capability old = _irq_sigh;
|
|
||||||
|
|
||||||
if (old.valid() && !sigh.valid())
|
void Irq_session_component::sigh(Genode::Signal_context_capability cap)
|
||||||
_proxy->remove_sharer(&_irq_sigh);
|
{
|
||||||
|
_irq_object.sigh(cap);
|
||||||
_irq_sigh = sigh;
|
|
||||||
|
|
||||||
if (!old.valid() && sigh.valid())
|
|
||||||
_proxy->add_sharer(&_irq_sigh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
59
repos/base-foc/src/core/include/irq_object.h
Normal file
59
repos/base-foc/src/core/include/irq_object.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* \brief Fiasco.OC-specific core implementation of IRQ sessions
|
||||||
|
* \author Christian Helmuth
|
||||||
|
* \author Stefan Kalkowski
|
||||||
|
* \author Sebastian Sumpf
|
||||||
|
* \date 2007-09-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007-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
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <base/printf.h>
|
||||||
|
#include <irq_session/irq_session.h>
|
||||||
|
#include <cap_index.h>
|
||||||
|
|
||||||
|
namespace Genode { class Irq_object; }
|
||||||
|
|
||||||
|
|
||||||
|
class Genode::Irq_object
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Cap_index *_cap;
|
||||||
|
Irq_session::Trigger _trigger; /* interrupt trigger */
|
||||||
|
Irq_session::Polarity _polarity; /* interrupt polarity */
|
||||||
|
|
||||||
|
unsigned _irq;
|
||||||
|
Genode::addr_t _msi_addr;
|
||||||
|
Genode::addr_t _msi_data;
|
||||||
|
|
||||||
|
Signal_context_capability _sig_cap;
|
||||||
|
|
||||||
|
Native_thread _capability() const { return _cap->kcap(); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Irq_object();
|
||||||
|
~Irq_object();
|
||||||
|
|
||||||
|
Irq_session::Trigger trigger() const { return _trigger; }
|
||||||
|
Irq_session::Polarity polarity() const { return _polarity; }
|
||||||
|
|
||||||
|
Genode::addr_t msi_address() const { return _msi_addr; }
|
||||||
|
Genode::addr_t msi_value() const { return _msi_data; }
|
||||||
|
|
||||||
|
void sigh(Genode::Signal_context_capability cap) { _sig_cap = cap; }
|
||||||
|
void notify() { Genode::Signal_transmitter(_sig_cap).submit(1); }
|
||||||
|
|
||||||
|
bool associate(unsigned irq, bool msi, Irq_session::Trigger,
|
||||||
|
Irq_session::Polarity);
|
||||||
|
void ack_irq();
|
||||||
|
};
|
|
@ -4,12 +4,10 @@
|
||||||
* \author Stefan Kalkowski
|
* \author Stefan Kalkowski
|
||||||
* \author Sebastian Sumpf
|
* \author Sebastian Sumpf
|
||||||
* \date 2007-09-13
|
* \date 2007-09-13
|
||||||
*
|
|
||||||
* FIXME ram quota missing
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2007-2013 Genode Labs GmbH
|
* Copyright (C) 2007-2015 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
@ -18,6 +16,7 @@
|
||||||
/* Genode includes */
|
/* Genode includes */
|
||||||
#include <base/printf.h>
|
#include <base/printf.h>
|
||||||
#include <util/arg_string.h>
|
#include <util/arg_string.h>
|
||||||
|
#include <util/bit_array.h>
|
||||||
|
|
||||||
/* core includes */
|
/* core includes */
|
||||||
#include <irq_root.h>
|
#include <irq_root.h>
|
||||||
|
@ -33,14 +32,14 @@ namespace Fiasco {
|
||||||
#include <l4/sys/types.h>
|
#include <l4/sys/types.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace Genode;
|
|
||||||
|
|
||||||
namespace Genode {
|
namespace Genode {
|
||||||
class Interrupt_handler;
|
class Interrupt_handler;
|
||||||
class Irq_proxy_component;
|
|
||||||
typedef Irq_proxy<Thread<1024 * sizeof(addr_t)> > Irq_proxy_base;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatches interrupts from kernel
|
* Dispatches interrupts from kernel
|
||||||
*/
|
*/
|
||||||
|
@ -59,126 +58,121 @@ class Genode::Interrupt_handler : public Thread<4096>
|
||||||
static Interrupt_handler handler;
|
static Interrupt_handler handler;
|
||||||
return handler._thread_cap.dst();
|
return handler._thread_cap.dst();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
enum { MAX_MSIS = 256 };
|
||||||
* Irq_proxy interface implementation
|
static class Msi_allocator : public Genode::Bit_array<MAX_MSIS>
|
||||||
*/
|
|
||||||
class Genode::Irq_proxy_component : public Irq_proxy_base
|
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
|
|
||||||
Cap_index *_cap;
|
|
||||||
Semaphore _sem;
|
|
||||||
Irq_session::Trigger _trigger; /* interrupt trigger */
|
|
||||||
Irq_session::Polarity _polarity; /* interrupt polarity */
|
|
||||||
|
|
||||||
bool _running;
|
|
||||||
Genode::addr_t _msi_addr;
|
|
||||||
Genode::addr_t _msi_data;
|
|
||||||
|
|
||||||
Native_thread _capability() const { return _cap->kcap(); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
bool _associate()
|
|
||||||
{
|
|
||||||
using namespace Fiasco;
|
|
||||||
if (l4_error(l4_factory_create_irq(L4_BASE_FACTORY_CAP,
|
|
||||||
_capability()))) {
|
|
||||||
PERR("l4_factory_create_irq failed!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long gsi = _irq_number;
|
|
||||||
if (_msi_addr)
|
|
||||||
gsi |= L4_ICU_FLAG_MSI;
|
|
||||||
|
|
||||||
if (l4_error(l4_icu_bind(L4_BASE_ICU_CAP, gsi, _capability()))) {
|
|
||||||
PERR("Binding IRQ%ld to the ICU failed", _irq_number);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_msi_addr)
|
|
||||||
/* set interrupt mode */
|
|
||||||
Platform::setup_irq_mode(gsi, _trigger, _polarity);
|
|
||||||
|
|
||||||
if (l4_error(l4_irq_attach(_capability(), _irq_number,
|
|
||||||
Interrupt_handler::handler_cap()))) {
|
|
||||||
PERR("Error attaching to IRQ %ld", _irq_number);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_msi_addr && l4_error(l4_icu_msi_info(L4_BASE_ICU_CAP, gsi,
|
|
||||||
&_msi_data))) {
|
|
||||||
PERR("Error getting MSI info");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _wait_for_irq()
|
|
||||||
{
|
|
||||||
using namespace Fiasco;
|
|
||||||
|
|
||||||
int err;
|
|
||||||
l4_msgtag_t tag = l4_irq_unmask(_capability());
|
|
||||||
if ((err = l4_ipc_error(tag, l4_utcb())))
|
|
||||||
PERR("IRQ unmask: %d\n", err);
|
|
||||||
|
|
||||||
_sem.down();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _ack_irq() { }
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Irq_proxy_component(long irq_number)
|
Msi_allocator() {
|
||||||
:
|
using namespace Fiasco;
|
||||||
Irq_proxy_base(irq_number),
|
|
||||||
_cap(cap_map()->insert(platform_specific()->cap_id_alloc()->alloc())),
|
|
||||||
_sem(), _trigger(Irq_session::TRIGGER_UNCHANGED),
|
|
||||||
_polarity(Irq_session::POLARITY_UNCHANGED),
|
|
||||||
_running(false), _msi_addr(0), _msi_data(0)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
Semaphore *semaphore() { return &_sem; }
|
l4_icu_info_t info { .features = 0 };
|
||||||
|
l4_msgtag_t res = l4_icu_info(Fiasco::L4_BASE_ICU_CAP, &info);
|
||||||
|
|
||||||
Irq_session::Trigger trigger() const { return _trigger; }
|
if (l4_error(res) || !(info.features & L4_ICU_FLAG_MSI))
|
||||||
Irq_session::Polarity polarity() const { return _polarity; }
|
set(0, MAX_MSIS);
|
||||||
|
else
|
||||||
void setup_irq_mode(Irq_session::Trigger t, Irq_session::Polarity p)
|
if (info.nr_msis < MAX_MSIS)
|
||||||
{
|
set(info.nr_msis, MAX_MSIS - info.nr_msis);
|
||||||
_trigger = t;
|
|
||||||
_polarity = p;
|
|
||||||
|
|
||||||
/* set interrupt mode */
|
|
||||||
Platform::setup_irq_mode(_irq_number, _trigger, _polarity);
|
|
||||||
}
|
}
|
||||||
|
} msi_alloc;
|
||||||
|
|
||||||
Genode::addr_t msi_address() const { return _msi_addr; }
|
bool Genode::Irq_object::associate(unsigned irq, bool msi,
|
||||||
Genode::addr_t msi_value() const { return _msi_data; }
|
Irq_session::Trigger trigger,
|
||||||
|
Irq_session::Polarity polarity)
|
||||||
|
{
|
||||||
|
if (msi)
|
||||||
|
/*
|
||||||
|
* Local APIC address, See Intel x86 Spec - Section MSI 10.11.
|
||||||
|
*
|
||||||
|
* XXX local Apic ID encoding missing - address is constructed
|
||||||
|
* assuming that local APIC id of boot CPU is 0 XXX
|
||||||
|
*/
|
||||||
|
_msi_addr = 0xfee00000UL;
|
||||||
|
|
||||||
void enable(bool msi)
|
_irq = irq;
|
||||||
{
|
_trigger = trigger;
|
||||||
if (_running)
|
_polarity = polarity;
|
||||||
return;
|
|
||||||
|
|
||||||
if (msi)
|
using namespace Fiasco;
|
||||||
/*
|
if (l4_error(l4_factory_create_irq(L4_BASE_FACTORY_CAP,
|
||||||
* Local APIC address, See Intel x86 Spec - Section MSI 10.11.
|
_capability()))) {
|
||||||
*
|
PERR("l4_factory_create_irq failed!");
|
||||||
* XXX local Apic ID encoding missing - address is constructed
|
return false;
|
||||||
* assuming that local APIC id of boot CPU is 0 XXX
|
}
|
||||||
*/
|
|
||||||
_msi_addr = 0xfee00000UL;
|
|
||||||
|
|
||||||
_running = true;
|
unsigned long gsi = _irq;
|
||||||
_start();
|
if (_msi_addr)
|
||||||
}
|
gsi |= L4_ICU_FLAG_MSI;
|
||||||
};
|
|
||||||
|
if (l4_error(l4_icu_bind(L4_BASE_ICU_CAP, gsi, _capability()))) {
|
||||||
|
PERR("Binding IRQ %u to the ICU failed", _irq);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_msi_addr)
|
||||||
|
/* set interrupt mode */
|
||||||
|
Platform::setup_irq_mode(gsi, _trigger, _polarity);
|
||||||
|
|
||||||
|
if (l4_error(l4_irq_attach(_capability(), reinterpret_cast<l4_umword_t>(this),
|
||||||
|
Interrupt_handler::handler_cap()))) {
|
||||||
|
PERR("Error attaching to IRQ %u", _irq);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_msi_addr && l4_error(l4_icu_msi_info(L4_BASE_ICU_CAP, gsi,
|
||||||
|
&_msi_data))) {
|
||||||
|
PERR("Error getting MSI info");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Genode::Irq_object::ack_irq()
|
||||||
|
{
|
||||||
|
using namespace Fiasco;
|
||||||
|
|
||||||
|
int err;
|
||||||
|
l4_msgtag_t tag = l4_irq_unmask(_capability());
|
||||||
|
if ((err = l4_ipc_error(tag, l4_utcb())))
|
||||||
|
PERR("IRQ unmask: %d\n", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Genode::Irq_object::Irq_object()
|
||||||
|
:
|
||||||
|
_cap(cap_map()->insert(platform_specific()->cap_id_alloc()->alloc())),
|
||||||
|
_trigger(Irq_session::TRIGGER_UNCHANGED),
|
||||||
|
_polarity(Irq_session::POLARITY_UNCHANGED),
|
||||||
|
_irq(~0U), _msi_addr(0), _msi_data(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
Genode::Irq_object::~Irq_object()
|
||||||
|
{
|
||||||
|
if (_irq == ~0U)
|
||||||
|
return;
|
||||||
|
|
||||||
|
using namespace Fiasco;
|
||||||
|
|
||||||
|
unsigned long gsi = _irq;
|
||||||
|
if (_msi_addr)
|
||||||
|
gsi |= L4_ICU_FLAG_MSI;
|
||||||
|
|
||||||
|
if (l4_error(l4_irq_detach(_capability())))
|
||||||
|
PERR("Error detaching IRQ");
|
||||||
|
|
||||||
|
if (l4_error(l4_icu_unbind(L4_BASE_ICU_CAP, gsi, _capability())))
|
||||||
|
PERR("Error unbinding IRQ");
|
||||||
|
|
||||||
|
cap_map()->remove(_cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***************************
|
/***************************
|
||||||
|
@ -186,26 +180,28 @@ class Genode::Irq_proxy_component : public Irq_proxy_base
|
||||||
***************************/
|
***************************/
|
||||||
|
|
||||||
|
|
||||||
void Irq_session_component::ack_irq()
|
|
||||||
{
|
|
||||||
if (!_proxy) {
|
|
||||||
PERR("Expected to find IRQ proxy for IRQ %02x", _irq_number);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_proxy->ack_irq();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Irq_session_component::Irq_session_component(Range_allocator *irq_alloc,
|
Irq_session_component::Irq_session_component(Range_allocator *irq_alloc,
|
||||||
const char *args)
|
const char *args)
|
||||||
|
:
|
||||||
|
_irq_number(~0U), _irq_alloc(irq_alloc)
|
||||||
{
|
{
|
||||||
long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1);
|
long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1);
|
||||||
if (irq_number < 0) {
|
long msi = Arg_string::find_arg(args, "device_config_phys").long_value(0);
|
||||||
PERR("invalid IRQ number requested %ld", irq_number);
|
if (msi) {
|
||||||
throw Root::Unavailable();
|
if (msi_alloc.get(irq_number, 1)) {
|
||||||
|
PERR("Unavailable MSI %ld requested.", irq_number);
|
||||||
|
throw Root::Unavailable();
|
||||||
|
}
|
||||||
|
msi_alloc.set(irq_number, 1);
|
||||||
|
} else {
|
||||||
|
if (!irq_alloc || irq_alloc->alloc_addr(1, irq_number).is_error()) {
|
||||||
|
PERR("Unavailable IRQ %ld requested.", irq_number);
|
||||||
|
throw Root::Unavailable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_irq_number = irq_number;
|
||||||
|
|
||||||
long irq_t = Arg_string::find_arg(args, "irq_trigger").long_value(-1);
|
long irq_t = Arg_string::find_arg(args, "irq_trigger").long_value(-1);
|
||||||
long irq_p = Arg_string::find_arg(args, "irq_polarity").long_value(-1);
|
long irq_p = Arg_string::find_arg(args, "irq_polarity").long_value(-1);
|
||||||
|
|
||||||
|
@ -242,114 +238,49 @@ Irq_session_component::Irq_session_component(Range_allocator *irq_alloc,
|
||||||
throw Root::Unavailable();
|
throw Root::Unavailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
long msi = Arg_string::find_arg(args, "device_config_phys").long_value(0);
|
_irq_object.associate(_irq_number, msi, irq_trigger, irq_polarity);
|
||||||
if (msi) {
|
|
||||||
using namespace Fiasco;
|
|
||||||
|
|
||||||
l4_icu_info_t info { .features = 0 };
|
|
||||||
l4_msgtag_t res = l4_icu_info(Fiasco::L4_BASE_ICU_CAP, &info);
|
|
||||||
if (l4_error(res) || !(info.features & L4_ICU_FLAG_MSI))
|
|
||||||
throw Root::Unavailable();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* irq_alloc range [0, max)
|
|
||||||
*
|
|
||||||
* legacy irq [0, max_legacy)
|
|
||||||
* msi [max_legacy, info.nr_msis)
|
|
||||||
* unused [info.nr_msis, max)
|
|
||||||
*
|
|
||||||
* max - is currently set to 256 in base-foc platform.cc
|
|
||||||
* max_legacy - is info.nr_irqs, defined by hardware/kernel
|
|
||||||
*/
|
|
||||||
irq_number = info.nr_msis - 1 - irq_number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check if IRQ thread was started before */
|
|
||||||
_proxy = Irq_proxy_component::get_irq_proxy<Irq_proxy_component>(irq_number, irq_alloc);
|
|
||||||
if (!_proxy) {
|
|
||||||
PERR("unavailable IRQ %lx requested", irq_number);
|
|
||||||
throw Root::Unavailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setup = false;
|
|
||||||
bool fail = false;
|
|
||||||
/* sanity check */
|
|
||||||
if (irq_trigger != TRIGGER_UNCHANGED && _proxy->trigger() != irq_trigger) {
|
|
||||||
if (_proxy->trigger() == TRIGGER_UNCHANGED)
|
|
||||||
setup = true;
|
|
||||||
else
|
|
||||||
fail = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (irq_polarity != POLARITY_UNCHANGED && _proxy->polarity() != irq_polarity) {
|
|
||||||
if (_proxy->polarity() == POLARITY_UNCHANGED)
|
|
||||||
setup = true;
|
|
||||||
else
|
|
||||||
fail = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fail) {
|
|
||||||
PERR("Interrupt mode mismatch: IRQ %ld current mode: t: %d p: %d "
|
|
||||||
"request mode: trg: %d p: %d",
|
|
||||||
irq_number, _proxy->trigger(), _proxy->polarity(),
|
|
||||||
irq_trigger, irq_polarity);
|
|
||||||
throw Root::Unavailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setup)
|
|
||||||
/* set interrupt mode */
|
|
||||||
_proxy->setup_irq_mode(irq_trigger, irq_polarity);
|
|
||||||
|
|
||||||
_irq_number = irq_number;
|
|
||||||
|
|
||||||
_proxy->enable(msi);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Irq_session_component::~Irq_session_component()
|
Irq_session_component::~Irq_session_component()
|
||||||
{
|
{
|
||||||
if (!_proxy) return;
|
if (_irq_number == ~0U)
|
||||||
|
return;
|
||||||
|
|
||||||
if (_irq_sigh.valid())
|
if (_irq_object.msi_address()) {
|
||||||
_proxy->remove_sharer(&_irq_sigh);
|
msi_alloc.clear(_irq_number, 1);
|
||||||
|
} else {
|
||||||
|
Genode::addr_t free_irq = _irq_number;
|
||||||
|
_irq_alloc->free((void *)free_irq);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Irq_session_component::sigh(Genode::Signal_context_capability sigh)
|
void Irq_session_component::ack_irq() { _irq_object.ack_irq(); }
|
||||||
|
|
||||||
|
|
||||||
|
void Irq_session_component::sigh(Genode::Signal_context_capability cap)
|
||||||
{
|
{
|
||||||
if (!_proxy) {
|
_irq_object.sigh(cap);
|
||||||
PERR("signal handler got not registered - irq thread unavailable");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Genode::Signal_context_capability old = _irq_sigh;
|
|
||||||
|
|
||||||
if (old.valid() && !sigh.valid())
|
|
||||||
_proxy->remove_sharer(&_irq_sigh);
|
|
||||||
|
|
||||||
_irq_sigh = sigh;
|
|
||||||
|
|
||||||
if (!old.valid() && sigh.valid())
|
|
||||||
_proxy->add_sharer(&_irq_sigh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Genode::Irq_session::Info Irq_session_component::info()
|
Genode::Irq_session::Info Irq_session_component::info()
|
||||||
{
|
{
|
||||||
if (!_proxy || !_proxy->msi_address() || !_proxy->msi_value())
|
if (!_irq_object.msi_address() || !_irq_object.msi_value())
|
||||||
return { .type = Genode::Irq_session::Info::Type::INVALID };
|
return { .type = Genode::Irq_session::Info::Type::INVALID };
|
||||||
|
|
||||||
return {
|
return {
|
||||||
.type = Genode::Irq_session::Info::Type::MSI,
|
.type = Genode::Irq_session::Info::Type::MSI,
|
||||||
.address = _proxy->msi_address(),
|
.address = _irq_object.msi_address(),
|
||||||
.value = _proxy->msi_value()
|
.value = _irq_object.msi_value()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/***************************************
|
/**************************************
|
||||||
** Interrupt handler implemtentation **
|
** Interrupt handler implementation **
|
||||||
***************************************/
|
**************************************/
|
||||||
|
|
||||||
/* Build with frame pointer to make GDB backtraces work. See issue #1061. */
|
/* Build with frame pointer to make GDB backtraces work. See issue #1061. */
|
||||||
__attribute__((optimize("-fno-omit-frame-pointer")))
|
__attribute__((optimize("-fno-omit-frame-pointer")))
|
||||||
|
@ -367,11 +298,8 @@ void Interrupt_handler::entry()
|
||||||
if ((err = l4_ipc_error(tag, l4_utcb())))
|
if ((err = l4_ipc_error(tag, l4_utcb())))
|
||||||
PERR("IRQ receive: %d\n", err);
|
PERR("IRQ receive: %d\n", err);
|
||||||
else {
|
else {
|
||||||
Irq_proxy_component *proxy;
|
Irq_object * irq_object = reinterpret_cast<Irq_object *>(label);
|
||||||
proxy = Irq_proxy_component::get_irq_proxy<Irq_proxy_component>(label);
|
irq_object->notify();
|
||||||
|
|
||||||
if (proxy)
|
|
||||||
proxy->semaphore()->up();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
/* Fiasco includes */
|
/* Fiasco includes */
|
||||||
namespace Fiasco {
|
namespace Fiasco {
|
||||||
#include <l4/sigma0/sigma0.h>
|
#include <l4/sigma0/sigma0.h>
|
||||||
|
#include <l4/sys/icu.h>
|
||||||
#include <l4/sys/ipc.h>
|
#include <l4/sys/ipc.h>
|
||||||
#include <l4/sys/kip>
|
#include <l4/sys/kip>
|
||||||
#include <l4/sys/thread.h>
|
#include <l4/sys/thread.h>
|
||||||
|
@ -333,7 +334,17 @@ void Platform::_setup_mem_alloc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Platform::_setup_irq_alloc() { _irq_alloc.add_range(0, 0x100); }
|
void Platform::_setup_irq_alloc()
|
||||||
|
{
|
||||||
|
using namespace Fiasco;
|
||||||
|
|
||||||
|
l4_icu_info_t info { .features = 0 };
|
||||||
|
l4_msgtag_t res = l4_icu_info(Fiasco::L4_BASE_ICU_CAP, &info);
|
||||||
|
if (l4_error(res))
|
||||||
|
panic("could not determine number of IRQs");
|
||||||
|
|
||||||
|
_irq_alloc.add_range(0, info.nr_irqs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Platform::_setup_basics()
|
void Platform::_setup_basics()
|
||||||
|
|
|
@ -69,6 +69,10 @@ Irq_session_component::Irq_session_component(Range_allocator * const irq_alloc,
|
||||||
_irq_number(Platform::irq(_find_irq_number(args))),
|
_irq_number(Platform::irq(_find_irq_number(args))),
|
||||||
_irq_alloc(irq_alloc)
|
_irq_alloc(irq_alloc)
|
||||||
{
|
{
|
||||||
|
long const msi = Arg_string::find_arg(args, "device_config_phys").long_value(0);
|
||||||
|
if (msi)
|
||||||
|
throw Root::Unavailable();
|
||||||
|
|
||||||
/* allocate interrupt */
|
/* allocate interrupt */
|
||||||
if (_irq_alloc->alloc_addr(1, _irq_number).is_error()) {
|
if (_irq_alloc->alloc_addr(1, _irq_number).is_error()) {
|
||||||
PERR("unavailable interrupt requested");
|
PERR("unavailable interrupt requested");
|
||||||
|
|
54
repos/base-nova/src/core/include/irq_object.h
Normal file
54
repos/base-nova/src/core/include/irq_object.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* \brief Nova-specific instance of the IRQ object
|
||||||
|
* \author Alexander Boettcher
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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/thread.h>
|
||||||
|
|
||||||
|
namespace Genode { class Irq_object; }
|
||||||
|
|
||||||
|
class Genode::Irq_object : public Thread<4096> {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Signal_context_capability _sig_cap;
|
||||||
|
Lock _sync_ack;
|
||||||
|
Lock _sync_life;
|
||||||
|
|
||||||
|
Genode::addr_t _kernel_caps;
|
||||||
|
Genode::addr_t _msi_addr;
|
||||||
|
Genode::addr_t _msi_data;
|
||||||
|
enum { UNDEFINED, SHUTDOWN } volatile _state;
|
||||||
|
|
||||||
|
void entry() override;
|
||||||
|
|
||||||
|
enum { KERNEL_CAP_COUNT_LOG2 = 1 };
|
||||||
|
|
||||||
|
Genode::addr_t const irq_sel() { return _kernel_caps; }
|
||||||
|
Genode::addr_t const sc_sel() { return _kernel_caps + 1; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Irq_object();
|
||||||
|
~Irq_object();
|
||||||
|
|
||||||
|
Genode::addr_t msi_address() const { return _msi_addr; }
|
||||||
|
Genode::addr_t msi_value() const { return _msi_data; }
|
||||||
|
|
||||||
|
void sigh(Signal_context_capability cap) { _sig_cap = cap; }
|
||||||
|
void notify() { Genode::Signal_transmitter(_sig_cap).submit(1); }
|
||||||
|
|
||||||
|
void ack_irq() { _sync_ack.unlock(); }
|
||||||
|
|
||||||
|
void start() override;
|
||||||
|
void start(unsigned irq, Genode::addr_t);
|
||||||
|
};
|
|
@ -2,11 +2,12 @@
|
||||||
* \brief Implementation of IRQ session component
|
* \brief Implementation of IRQ session component
|
||||||
* \author Norman Feske
|
* \author Norman Feske
|
||||||
* \author Sebastian Sumpf
|
* \author Sebastian Sumpf
|
||||||
|
* \author Alexander Boettcher
|
||||||
* \date 2009-10-02
|
* \date 2009-10-02
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2013 Genode Labs GmbH
|
* Copyright (C) 2009-2015 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
@ -18,7 +19,6 @@
|
||||||
|
|
||||||
/* core includes */
|
/* core includes */
|
||||||
#include <irq_root.h>
|
#include <irq_root.h>
|
||||||
#include <irq_proxy.h>
|
|
||||||
#include <platform.h>
|
#include <platform.h>
|
||||||
#include <platform_pd.h>
|
#include <platform_pd.h>
|
||||||
|
|
||||||
|
@ -29,201 +29,214 @@
|
||||||
|
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
|
|
||||||
namespace Genode {
|
|
||||||
class Irq_proxy_component;
|
static void thread_start()
|
||||||
|
{
|
||||||
|
Thread_base::myself()->entry();
|
||||||
|
sleep_forever();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool associate(unsigned irq, Genode::addr_t irq_sel,
|
||||||
|
Genode::addr_t &msi_addr, Genode::addr_t &msi_data,
|
||||||
|
Genode::addr_t virt_addr = 0)
|
||||||
|
{
|
||||||
|
/* map IRQ SM cap from kernel to core at irq_sel selector */
|
||||||
|
using Nova::Obj_crd;
|
||||||
|
|
||||||
|
Obj_crd src(platform_specific()->gsi_base_sel() + irq, 0);
|
||||||
|
Obj_crd dst(irq_sel, 0);
|
||||||
|
enum { MAP_FROM_KERNEL_TO_CORE = true };
|
||||||
|
|
||||||
|
int ret = map_local((Nova::Utcb *)Thread_base::myself()->utcb(),
|
||||||
|
src, dst, MAP_FROM_KERNEL_TO_CORE);
|
||||||
|
if (ret) {
|
||||||
|
PERR("Could not map IRQ %u", irq);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* assign IRQ to CPU && request msi data to be used by driver */
|
||||||
|
uint8_t res = Nova::assign_gsi(irq_sel, virt_addr, boot_cpu(),
|
||||||
|
msi_addr, msi_data);
|
||||||
|
|
||||||
|
if (virt_addr && res != Nova::NOVA_OK) {
|
||||||
|
PERR("setting up MSI %u failed - error %u", irq, res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* nova syscall interface specifies msi addr/data to be 32bit */
|
||||||
|
msi_addr = msi_addr & ~0U;
|
||||||
|
msi_data = msi_data & ~0U;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool msi(unsigned irq, Genode::addr_t irq_sel, Genode::addr_t phys_mem,
|
||||||
|
Genode::addr_t &msi_addr, Genode::addr_t &msi_data)
|
||||||
|
{
|
||||||
|
void * virt = 0;
|
||||||
|
if (platform()->region_alloc()->alloc_aligned(4096, &virt, 12).is_error())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Genode::addr_t virt_addr = reinterpret_cast<Genode::addr_t>(virt);
|
||||||
|
if (!virt_addr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
using Nova::Rights;
|
||||||
|
using Nova::Utcb;
|
||||||
|
|
||||||
|
Nova::Mem_crd phys_crd(phys_mem >> 12, 0, Rights(true, false, false));
|
||||||
|
Nova::Mem_crd virt_crd(virt_addr >> 12, 0, Rights(true, false, false));
|
||||||
|
Utcb * utcb = reinterpret_cast<Utcb *>(Thread_base::myself()->utcb());
|
||||||
|
|
||||||
|
if (map_local_phys_to_virt(utcb, phys_crd, virt_crd)) {
|
||||||
|
platform()->region_alloc()->free(virt, 4096);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* try to assign MSI to device */
|
||||||
|
bool res = associate(irq, irq_sel, msi_addr, msi_data, virt_addr);
|
||||||
|
|
||||||
|
unmap_local(Nova::Mem_crd(virt_addr >> 12, 0, Rights(true, true, true)));
|
||||||
|
platform()->region_alloc()->free(virt, 4096);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Irq_object::start()
|
||||||
|
{
|
||||||
|
PERR("wrong start method called");
|
||||||
|
throw Root::Unavailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global worker (i.e. thread with SC)
|
* Create global EC, associate it to SC
|
||||||
*/
|
*/
|
||||||
class Irq_thread : public Thread_base
|
void Irq_object::start(unsigned irq, Genode::addr_t const device_phys)
|
||||||
{
|
{
|
||||||
private:
|
/* associate GSI or MSI to device belonging to device_phys */
|
||||||
|
bool ok = false;
|
||||||
|
if (device_phys)
|
||||||
|
ok = msi(irq, irq_sel(), device_phys, _msi_addr, _msi_data);
|
||||||
|
else
|
||||||
|
ok = associate(irq, irq_sel(), _msi_addr, _msi_data);
|
||||||
|
|
||||||
enum { STACK_SIZE = 1024 * sizeof(addr_t) };
|
if (!ok)
|
||||||
enum { WEIGHT = Cpu_session::DEFAULT_WEIGHT };
|
throw Root::Unavailable();
|
||||||
|
|
||||||
static void _thread_start()
|
/* start thread having a SC */
|
||||||
{
|
using namespace Nova;
|
||||||
Thread_base::myself()->entry();
|
addr_t pd_sel = Platform_pd::pd_core_sel();
|
||||||
sleep_forever();
|
addr_t utcb = reinterpret_cast<addr_t>(&_context->utcb);
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
/* put IP on stack, it will be read from core pager in platform.cc */
|
||||||
|
addr_t *sp = reinterpret_cast<addr_t *>(_context->stack_top() - sizeof(addr_t));
|
||||||
|
*sp = reinterpret_cast<addr_t>(thread_start);
|
||||||
|
|
||||||
Irq_thread(char const *name) : Thread_base(WEIGHT, name, STACK_SIZE)
|
/* create global EC */
|
||||||
{ }
|
enum { GLOBAL = true };
|
||||||
|
uint8_t res = create_ec(_tid.ec_sel, pd_sel, boot_cpu(),
|
||||||
|
utcb, (mword_t)sp, _tid.exc_pt_sel, GLOBAL);
|
||||||
|
if (res != NOVA_OK) {
|
||||||
|
PERR("%p - create_ec returned %d", this, res);
|
||||||
|
throw Root::Unavailable();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/* remap startup portal from main thread */
|
||||||
* Create global EC, associate it to SC
|
if (map_local((Utcb *)Thread_base::myself()->utcb(),
|
||||||
*/
|
Obj_crd(PT_SEL_STARTUP, 0),
|
||||||
void start()
|
Obj_crd(_tid.exc_pt_sel + PT_SEL_STARTUP, 0))) {
|
||||||
{
|
PERR("could not create startup portal");
|
||||||
using namespace Nova;
|
throw Root::Unavailable();
|
||||||
addr_t pd_sel = Platform_pd::pd_core_sel();
|
}
|
||||||
addr_t utcb = reinterpret_cast<addr_t>(&_context->utcb);
|
|
||||||
|
|
||||||
/*
|
/* remap debugging page fault portal for core threads */
|
||||||
* Put IP on stack, it will be read from core pager in platform.cc
|
if (map_local((Utcb *)Thread_base::myself()->utcb(),
|
||||||
*/
|
Obj_crd(PT_SEL_PAGE_FAULT, 0),
|
||||||
addr_t *sp = reinterpret_cast<addr_t *>(_context->stack_top() - sizeof(addr_t));
|
Obj_crd(_tid.exc_pt_sel + PT_SEL_PAGE_FAULT, 0))) {
|
||||||
*sp = reinterpret_cast<addr_t>(_thread_start);
|
PERR("could not create page fault portal");
|
||||||
|
throw Root::Unavailable();
|
||||||
|
}
|
||||||
|
|
||||||
/* create global EC */
|
/* default: we don't accept any mappings or translations */
|
||||||
enum { GLOBAL = true };
|
Utcb * utcb_obj = reinterpret_cast<Utcb *>(Thread_base::utcb());
|
||||||
uint8_t res = create_ec(_tid.ec_sel, pd_sel, boot_cpu(),
|
utcb_obj->crd_rcv = Obj_crd();
|
||||||
utcb, (mword_t)sp, _tid.exc_pt_sel, GLOBAL);
|
utcb_obj->crd_xlt = Obj_crd();
|
||||||
if (res != NOVA_OK) {
|
|
||||||
PERR("%p - create_ec returned %d", this, res);
|
|
||||||
throw Cpu_session::Thread_creation_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* remap startup portal from main thread */
|
/* create SC */
|
||||||
if (map_local((Utcb *)Thread_base::myself()->utcb(),
|
Qpd qpd(Qpd::DEFAULT_QUANTUM, Qpd::DEFAULT_PRIORITY + 1);
|
||||||
Obj_crd(PT_SEL_STARTUP, 0),
|
res = create_sc(sc_sel(), pd_sel, _tid.ec_sel, qpd);
|
||||||
Obj_crd(_tid.exc_pt_sel + PT_SEL_STARTUP, 0))) {
|
if (res != NOVA_OK) {
|
||||||
PERR("could not create startup portal");
|
PERR("%p - create_sc returned returned %d", this, res);
|
||||||
throw Cpu_session::Thread_creation_failed();
|
throw Root::Unavailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* remap debugging page fault portal for core threads */
|
_sync_life.lock();
|
||||||
if (map_local((Utcb *)Thread_base::myself()->utcb(),
|
}
|
||||||
Obj_crd(PT_SEL_PAGE_FAULT, 0),
|
|
||||||
Obj_crd(_tid.exc_pt_sel + PT_SEL_PAGE_FAULT, 0))) {
|
|
||||||
PERR("could not create page fault portal");
|
|
||||||
throw Cpu_session::Thread_creation_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* default: we don't accept any mappings or translations */
|
|
||||||
Utcb * utcb_obj = reinterpret_cast<Utcb *>(Thread_base::utcb());
|
|
||||||
utcb_obj->crd_rcv = Obj_crd();
|
|
||||||
utcb_obj->crd_xlt = Obj_crd();
|
|
||||||
|
|
||||||
/* create SC */
|
|
||||||
unsigned sc_sel = cap_map()->insert();
|
|
||||||
res = create_sc(sc_sel, pd_sel, _tid.ec_sel, Qpd(Qpd::DEFAULT_QUANTUM, Qpd::DEFAULT_PRIORITY + 1));
|
|
||||||
if (res != NOVA_OK) {
|
|
||||||
PERR("%p - create_sc returned returned %d", this, res);
|
|
||||||
throw Cpu_session::Thread_creation_failed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
void Irq_object::entry()
|
||||||
* Irq_proxy interface implementation
|
|
||||||
*/
|
|
||||||
class Genode::Irq_proxy_component : public Irq_proxy<Irq_thread>
|
|
||||||
{
|
{
|
||||||
private:
|
/* signal that thread is up and ready */
|
||||||
|
_sync_life.unlock();
|
||||||
|
|
||||||
Genode::addr_t _irq_sel; /* IRQ cap selector */
|
/* wait for first ack_irq */
|
||||||
Genode::addr_t _dev_mem; /* used when MSI or HPET is used */
|
_sync_ack.lock();
|
||||||
bool _ready; /* flag to signal that IRQ SM can be used */
|
|
||||||
|
|
||||||
Genode::addr_t _msi_addr;
|
while (true) {
|
||||||
Genode::addr_t _msi_data;
|
|
||||||
|
|
||||||
protected:
|
if (Nova::NOVA_OK != Nova::sm_ctrl(irq_sel(), Nova::SEMAPHORE_DOWN))
|
||||||
|
PERR("Error: blocking for irq_sel 0x%lx failed", irq_sel());
|
||||||
|
|
||||||
bool _associate()
|
if (_state == SHUTDOWN) {
|
||||||
{
|
/* signal end of life to entrypoint thread */
|
||||||
/* assign IRQ to CPU && request msi data to be used by driver */
|
_sync_life.unlock();
|
||||||
uint8_t res = Nova::assign_gsi(_irq_sel, _dev_mem, boot_cpu(),
|
while (1) nova_die();
|
||||||
_msi_addr, _msi_data);
|
|
||||||
|
|
||||||
_ready = res == Nova::NOVA_OK;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return ever success so that the IRQ proxy thread gets started.
|
|
||||||
* For MSIs or HPET a separate associate() call with a valid
|
|
||||||
* dev_mem address is required.
|
|
||||||
*/
|
|
||||||
if (_dev_mem && !_ready)
|
|
||||||
PERR("setting up MSI 0x%lx failed - error %u", _irq_number, res);
|
|
||||||
|
|
||||||
return _dev_mem ? _ready : true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _wait_for_irq()
|
if (!_sig_cap.valid())
|
||||||
{
|
continue;
|
||||||
if (!_ready)
|
|
||||||
PERR("Error: assign_gsi failed for IRQ %ld", _irq_number);
|
|
||||||
|
|
||||||
if (Nova::NOVA_OK != Nova::sm_ctrl(_irq_sel, Nova::SEMAPHORE_DOWN))
|
notify();
|
||||||
PERR("Error: blocking for irq %ld failed", _irq_number);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _ack_irq() { }
|
_sync_ack.lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Irq_proxy_component(long irq_number)
|
Irq_object::Irq_object()
|
||||||
:
|
:
|
||||||
/* since we run in APIC mode translate IRQ 0 (PIT) to 2 */
|
Thread<4096>("irq"),
|
||||||
Irq_proxy(irq_number ? irq_number : 2),
|
_sync_ack(Lock::LOCKED), _sync_life(Lock::LOCKED),
|
||||||
_irq_sel(cap_map()->insert()),
|
_kernel_caps(cap_map()->insert(KERNEL_CAP_COUNT_LOG2)),
|
||||||
_dev_mem(0),
|
_msi_addr(0UL), _msi_data(0UL), _state(UNDEFINED)
|
||||||
_ready(false),
|
{ }
|
||||||
_msi_addr(0),
|
|
||||||
_msi_data(0)
|
|
||||||
{
|
|
||||||
/* map IRQ SM cap from kernel to core at _irq_sel selector */
|
|
||||||
using Nova::Obj_crd;
|
|
||||||
|
|
||||||
Obj_crd src(platform_specific()->gsi_base_sel() + _irq_number, 0);
|
|
||||||
Obj_crd dst(_irq_sel, 0);
|
|
||||||
enum { MAP_FROM_KERNEL_TO_CORE = true };
|
|
||||||
|
|
||||||
int ret = map_local((Nova::Utcb *)Thread_base::myself()->utcb(),
|
Irq_object::~Irq_object()
|
||||||
src, dst, MAP_FROM_KERNEL_TO_CORE);
|
{
|
||||||
if (ret) {
|
/* tell interrupt thread to get in a defined dead state */
|
||||||
PERR("Could not map IRQ %ld", _irq_number);
|
_state = SHUTDOWN;
|
||||||
throw Root::Unavailable();
|
/* send ack - thread maybe got not the first ack */
|
||||||
}
|
_sync_ack.unlock();
|
||||||
|
/* unblock thread if it is waiting for interrupts */
|
||||||
|
Nova::sm_ctrl(irq_sel(), Nova::SEMAPHORE_UP);
|
||||||
|
/* wait until thread signals end of life */
|
||||||
|
_sync_life.lock();
|
||||||
|
|
||||||
/* let thread run */
|
/* revoke SC and SM of interrupt source */
|
||||||
_start();
|
Nova::revoke(Nova::Obj_crd(_kernel_caps, KERNEL_CAP_COUNT_LOG2));
|
||||||
}
|
enum { NO_REVOKE_REQUIRED = false };
|
||||||
|
cap_map()->remove(_kernel_caps, KERNEL_CAP_COUNT_LOG2, NO_REVOKE_REQUIRED);
|
||||||
|
}
|
||||||
|
|
||||||
void associate(Genode::addr_t phys_mem)
|
|
||||||
{
|
|
||||||
void * v = 0;
|
|
||||||
if (platform()->region_alloc()->alloc_aligned(4096,
|
|
||||||
&v, 12).is_error())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Genode::addr_t virt_addr = reinterpret_cast<Genode::addr_t>(v);
|
/***************************
|
||||||
|
** IRQ session component **
|
||||||
if (!virt_addr)
|
***************************/
|
||||||
return;
|
|
||||||
|
|
||||||
using Nova::Mem_crd;
|
|
||||||
using Nova::Rights;
|
|
||||||
|
|
||||||
if (map_local_phys_to_virt(reinterpret_cast<Nova::Utcb *>(Thread_base::myself()->utcb()),
|
|
||||||
Mem_crd(phys_mem >> 12, 0, Rights(true, false, false)),
|
|
||||||
Mem_crd(virt_addr >> 12, 0, Rights(true, false, false)))) {
|
|
||||||
platform()->region_alloc()->free(v, 4096);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* local attached pci config extended io mem of device */
|
|
||||||
_dev_mem = virt_addr;
|
|
||||||
/* try to assign MSI to device */
|
|
||||||
_associate();
|
|
||||||
|
|
||||||
/* revert local mapping */
|
|
||||||
_dev_mem = 0;
|
|
||||||
unmap_local(Mem_crd(virt_addr >> 12, 0, Rights(true, true, true)));
|
|
||||||
platform()->region_alloc()->free(v, 4096);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ready() const { return _ready; }
|
|
||||||
Genode::addr_t msi_address() const { return _msi_addr; }
|
|
||||||
Genode::addr_t msi_value() const { return _msi_data; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static Nova::Hip * kernel_hip()
|
static Nova::Hip * kernel_hip()
|
||||||
|
@ -237,35 +250,15 @@ static Nova::Hip * kernel_hip()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/***************************
|
|
||||||
** IRQ session component **
|
|
||||||
***************************/
|
|
||||||
|
|
||||||
|
|
||||||
void Irq_session_component::ack_irq()
|
|
||||||
{
|
|
||||||
if (!_proxy) {
|
|
||||||
PERR("Expected to find IRQ proxy for IRQ %02x", _irq_number);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_proxy->ack_irq();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Irq_session_component::Irq_session_component(Range_allocator *irq_alloc,
|
Irq_session_component::Irq_session_component(Range_allocator *irq_alloc,
|
||||||
const char *args)
|
const char *args)
|
||||||
|
:
|
||||||
|
_irq_number(~0U), _irq_alloc(irq_alloc)
|
||||||
{
|
{
|
||||||
typedef Irq_proxy<Irq_thread> Proxy;
|
|
||||||
|
|
||||||
long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1);
|
long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1);
|
||||||
if (irq_number < 0) {
|
|
||||||
PERR("invalid IRQ number requested");
|
|
||||||
throw Root::Unavailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
long device_phys = Arg_string::find_arg(args, "device_config_phys").long_value(0);
|
long device_phys = Arg_string::find_arg(args, "device_config_phys").long_value(0);
|
||||||
if (device_phys) {
|
if (device_phys) {
|
||||||
|
|
||||||
if (irq_number >= kernel_hip()->sel_gsi)
|
if (irq_number >= kernel_hip()->sel_gsi)
|
||||||
throw Root::Unavailable();
|
throw Root::Unavailable();
|
||||||
|
|
||||||
|
@ -275,58 +268,47 @@ Irq_session_component::Irq_session_component(Range_allocator *irq_alloc,
|
||||||
throw Root::Unavailable();
|
throw Root::Unavailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if IRQ thread was started before */
|
if (!irq_alloc || irq_alloc->alloc_addr(1, irq_number).is_error()) {
|
||||||
_proxy = Proxy::get_irq_proxy<Irq_proxy_component>(irq_number, irq_alloc);
|
PERR("Unavailable IRQ 0x%lx requested", irq_number);
|
||||||
if (!_proxy) {
|
|
||||||
PERR("unavailable IRQ %lx requested", irq_number);
|
|
||||||
throw Root::Unavailable();
|
throw Root::Unavailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device_phys)
|
|
||||||
_proxy->associate(device_phys);
|
|
||||||
|
|
||||||
_irq_number = irq_number;
|
_irq_number = irq_number;
|
||||||
|
|
||||||
|
_irq_object.start(_irq_number, device_phys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Irq_session_component::~Irq_session_component()
|
Irq_session_component::~Irq_session_component()
|
||||||
{
|
{
|
||||||
if (!_proxy)
|
if (_irq_number == ~0U)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_irq_sigh.valid())
|
Genode::addr_t free_irq = _irq_number;
|
||||||
_proxy->remove_sharer(&_irq_sigh);
|
_irq_alloc->free((void *)free_irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Irq_session_component::sigh(Genode::Signal_context_capability sigh)
|
void Irq_session_component::ack_irq()
|
||||||
{
|
{
|
||||||
if (!_proxy) {
|
_irq_object.ack_irq();
|
||||||
PERR("signal handler got not registered - irq thread unavailable");
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Genode::Signal_context_capability old = _irq_sigh;
|
|
||||||
|
|
||||||
if (old.valid() && !sigh.valid())
|
void Irq_session_component::sigh(Genode::Signal_context_capability cap)
|
||||||
_proxy->remove_sharer(&_irq_sigh);
|
{
|
||||||
|
_irq_object.sigh(cap);
|
||||||
_irq_sigh = sigh;
|
|
||||||
|
|
||||||
if (!old.valid() && sigh.valid())
|
|
||||||
_proxy->add_sharer(&_irq_sigh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Genode::Irq_session::Info Irq_session_component::info()
|
Genode::Irq_session::Info Irq_session_component::info()
|
||||||
{
|
{
|
||||||
if (!_proxy || !_proxy->ready() || !_proxy->msi_address() ||
|
if (!_irq_object.msi_address() || !_irq_object.msi_value())
|
||||||
!_proxy->msi_value())
|
|
||||||
return { .type = Genode::Irq_session::Info::Type::INVALID };
|
return { .type = Genode::Irq_session::Info::Type::INVALID };
|
||||||
|
|
||||||
return {
|
return {
|
||||||
.type = Genode::Irq_session::Info::Type::MSI,
|
.type = Genode::Irq_session::Info::Type::MSI,
|
||||||
.address = _proxy->msi_address(),
|
.address = _irq_object.msi_address(),
|
||||||
.value = _proxy->msi_value()
|
.value = _irq_object.msi_value()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <util/arg_string.h>
|
#include <util/arg_string.h>
|
||||||
|
|
||||||
/* core includes */
|
/* core includes */
|
||||||
|
#include <irq_root.h>
|
||||||
#include <irq_session_component.h>
|
#include <irq_session_component.h>
|
||||||
|
|
||||||
/* OKL4 includes */
|
/* OKL4 includes */
|
||||||
|
@ -32,6 +33,10 @@ using namespace Okl4;
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
|
|
||||||
|
|
||||||
|
/* bit to use for IRQ notifications */
|
||||||
|
enum { IRQ_NOTIFY_BIT = 13 };
|
||||||
|
|
||||||
|
|
||||||
/* XXX move this functionality to a central place instead of duplicating it */
|
/* XXX move this functionality to a central place instead of duplicating it */
|
||||||
static inline Okl4::L4_ThreadId_t thread_get_my_global_id()
|
static inline Okl4::L4_ThreadId_t thread_get_my_global_id()
|
||||||
{
|
{
|
||||||
|
@ -40,72 +45,87 @@ static inline Okl4::L4_ThreadId_t thread_get_my_global_id()
|
||||||
return myself;
|
return myself;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Genode {
|
|
||||||
typedef Irq_proxy<Thread<1024 * sizeof(addr_t)> > Irq_proxy_base;
|
bool Irq_object::_associate()
|
||||||
class Irq_proxy_component;
|
{
|
||||||
|
/* allow roottask (ourself) to handle the interrupt */
|
||||||
|
L4_LoadMR(0, _irq);
|
||||||
|
int ret = L4_AllowInterruptControl(L4_rootspace);
|
||||||
|
if (ret != 1) {
|
||||||
|
PERR("L4_AllowInterruptControl returned %d, error code=%ld\n",
|
||||||
|
ret, L4_ErrorCode());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: 'L4_Myself()' does not work for the thread argument of
|
||||||
|
* 'L4_RegisterInterrupt'. We have to specify our global ID.
|
||||||
|
*/
|
||||||
|
L4_LoadMR(0, _irq);
|
||||||
|
ret = L4_RegisterInterrupt(thread_get_my_global_id(), IRQ_NOTIFY_BIT, 0, 0);
|
||||||
|
if (ret != 1) {
|
||||||
|
PERR("L4_RegisterInterrupt returned %d, error code=%ld\n",
|
||||||
|
ret, L4_ErrorCode());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Platform-specific proxy code
|
void Irq_object::_wait_for_irq()
|
||||||
*/
|
|
||||||
class Genode::Irq_proxy_component : public Irq_proxy_base
|
|
||||||
{
|
{
|
||||||
protected:
|
/* prepare ourself to receive asynchronous IRQ notifications */
|
||||||
|
L4_Set_NotifyMask(1 << IRQ_NOTIFY_BIT);
|
||||||
|
L4_Accept(L4_NotifyMsgAcceptor);
|
||||||
|
|
||||||
bool _associate()
|
/* wait for asynchronous interrupt notification */
|
||||||
{
|
L4_ThreadId_t partner = L4_nilthread;
|
||||||
/* allow roottask (ourself) to handle the interrupt */
|
L4_ReplyWait(partner, &partner);
|
||||||
L4_LoadMR(0, _irq_number);
|
}
|
||||||
int ret = L4_AllowInterruptControl(L4_rootspace);
|
|
||||||
if (ret != 1) {
|
|
||||||
PERR("L4_AllowInterruptControl returned %d, error code=%ld\n",
|
|
||||||
ret, L4_ErrorCode());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* bit to use for IRQ notifications */
|
|
||||||
enum { IRQ_NOTIFY_BIT = 13 };
|
|
||||||
|
|
||||||
/*
|
void Irq_object::start()
|
||||||
* Note: 'L4_Myself()' does not work for the thread argument of
|
{
|
||||||
* 'L4_RegisterInterrupt'. We have to specify our global ID.
|
::Thread_base::start();
|
||||||
*/
|
_sync_bootup.lock();
|
||||||
L4_LoadMR(0, _irq_number);
|
}
|
||||||
ret = L4_RegisterInterrupt(thread_get_my_global_id(), IRQ_NOTIFY_BIT, 0, 0);
|
|
||||||
if (ret != 1) {
|
|
||||||
PERR("L4_RegisterInterrupt returned %d, error code=%ld\n",
|
|
||||||
ret, L4_ErrorCode());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* prepare ourself to receive asynchronous IRQ notifications */
|
|
||||||
L4_Set_NotifyMask(1 << IRQ_NOTIFY_BIT);
|
|
||||||
|
|
||||||
return true;
|
void Irq_object::entry()
|
||||||
}
|
{
|
||||||
|
if (!_associate())
|
||||||
|
PERR("Could not associate with IRQ 0x%x", _irq);
|
||||||
|
|
||||||
void _wait_for_irq()
|
/* thread is up and ready */
|
||||||
{
|
_sync_bootup.unlock();
|
||||||
L4_Accept(L4_NotifyMsgAcceptor);
|
|
||||||
|
|
||||||
/* wait for asynchronous interrupt notification */
|
/* wait for first ack_irq */
|
||||||
L4_ThreadId_t partner = L4_nilthread;
|
_sync_ack.lock();
|
||||||
L4_ReplyWait(partner, &partner);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _ack_irq()
|
while (true) {
|
||||||
{
|
|
||||||
L4_LoadMR(0, _irq_number);
|
|
||||||
L4_AcknowledgeInterrupt(0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
L4_LoadMR(0, _irq);
|
||||||
|
L4_AcknowledgeInterrupt(0, 0);
|
||||||
|
|
||||||
Irq_proxy_component(long irq_number) : Irq_proxy(irq_number)
|
_wait_for_irq();
|
||||||
{
|
|
||||||
_start();
|
if (!_sig_cap.valid())
|
||||||
}
|
continue;
|
||||||
};
|
|
||||||
|
Genode::Signal_transmitter(_sig_cap).submit(1);
|
||||||
|
|
||||||
|
_sync_ack.lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Irq_object::Irq_object(unsigned irq)
|
||||||
|
:
|
||||||
|
Thread<4096>("irq"),
|
||||||
|
_sync_ack(Lock::LOCKED), _sync_bootup(Lock::LOCKED),
|
||||||
|
_irq(irq)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
/***************************
|
/***************************
|
||||||
|
@ -113,74 +133,41 @@ class Genode::Irq_proxy_component : public Irq_proxy_base
|
||||||
***************************/
|
***************************/
|
||||||
|
|
||||||
|
|
||||||
void Irq_session_component::ack_irq()
|
|
||||||
{
|
|
||||||
/* block at interrupt proxy */
|
|
||||||
if (!_proxy) {
|
|
||||||
PERR("Expected to find IRQ proxy for IRQ %02x", _irq_number);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_proxy->ack_irq();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Irq_session_component::Irq_session_component(Range_allocator *irq_alloc,
|
Irq_session_component::Irq_session_component(Range_allocator *irq_alloc,
|
||||||
const char *args)
|
const char *args)
|
||||||
:
|
:
|
||||||
_irq_alloc(irq_alloc)
|
_irq_number(Arg_string::find_arg(args, "irq_number").long_value(-1)),
|
||||||
|
_irq_alloc(irq_alloc),
|
||||||
|
_irq_object(_irq_number)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* XXX Removed irq_shared argument as this is the default now. If we need
|
|
||||||
* exclusive later on, we should add this as new argument.
|
|
||||||
*/
|
|
||||||
|
|
||||||
long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1);
|
|
||||||
if (irq_number == -1) {
|
|
||||||
PERR("invalid IRQ number requested");
|
|
||||||
throw Root::Unavailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
long msi = Arg_string::find_arg(args, "device_config_phys").long_value(0);
|
long msi = Arg_string::find_arg(args, "device_config_phys").long_value(0);
|
||||||
if (msi)
|
if (msi)
|
||||||
throw Root::Unavailable();
|
throw Root::Unavailable();
|
||||||
|
|
||||||
/* check if IRQ thread was started before */
|
if (!irq_alloc || irq_alloc->alloc_addr(1, _irq_number).is_error()) {
|
||||||
_proxy = Irq_proxy_component::get_irq_proxy<Irq_proxy_component>(irq_number, irq_alloc);
|
PERR("Unavailable IRQ 0x%x requested", _irq_number);
|
||||||
if (!_proxy) {
|
|
||||||
PERR("unavailable IRQ %lx requested", irq_number);
|
|
||||||
throw Root::Unavailable();
|
throw Root::Unavailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
_irq_number = irq_number;
|
_irq_object.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Irq_session_component::~Irq_session_component()
|
Irq_session_component::~Irq_session_component()
|
||||||
{
|
{
|
||||||
if (!_proxy) return;
|
PDBG("Not yet implemented!");
|
||||||
|
|
||||||
if (_irq_sigh.valid())
|
|
||||||
_proxy->remove_sharer(&_irq_sigh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Irq_session_component::sigh(Genode::Signal_context_capability sigh)
|
void Irq_session_component::ack_irq()
|
||||||
{
|
{
|
||||||
if (!_proxy) {
|
_irq_object.ack_irq();
|
||||||
PERR("signal handler got not registered - irq thread unavailable");
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Genode::Signal_context_capability old = _irq_sigh;
|
|
||||||
|
|
||||||
if (old.valid() && !sigh.valid())
|
void Irq_session_component::sigh(Genode::Signal_context_capability cap)
|
||||||
_proxy->remove_sharer(&_irq_sigh);
|
{
|
||||||
|
_irq_object.sigh(cap);
|
||||||
_irq_sigh = sigh;
|
|
||||||
|
|
||||||
if (!old.valid() && sigh.valid())
|
|
||||||
_proxy->add_sharer(&_irq_sigh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,10 @@
|
||||||
* \brief Pistachio-specific implementation of IRQ sessions
|
* \brief Pistachio-specific implementation of IRQ sessions
|
||||||
* \author Julian Stecklina
|
* \author Julian Stecklina
|
||||||
* \date 2008-02-21
|
* \date 2008-02-21
|
||||||
*
|
|
||||||
* FIXME ram quota missing
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2013 Genode Labs GmbH
|
* Copyright (C) 2008-2015 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
@ -18,7 +16,6 @@
|
||||||
#include <util/arg_string.h>
|
#include <util/arg_string.h>
|
||||||
|
|
||||||
/* core includes */
|
/* core includes */
|
||||||
#include <irq_proxy.h>
|
|
||||||
#include <irq_root.h>
|
#include <irq_root.h>
|
||||||
#include <util.h>
|
#include <util.h>
|
||||||
|
|
||||||
|
@ -40,97 +37,88 @@ static inline L4_ThreadId_t irqno_to_threadid(unsigned int irqno)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace Genode {
|
bool Irq_object::_associate()
|
||||||
typedef Irq_proxy<Thread<1024 * sizeof(addr_t)> > Irq_proxy_base;
|
{
|
||||||
class Irq_proxy_component;
|
L4_ThreadId_t const irq_thread = irqno_to_threadid(_irq);
|
||||||
|
|
||||||
|
return L4_AssociateInterrupt(irq_thread, L4_Myself());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
void Irq_object::_wait_for_irq()
|
||||||
* Platform-specific proxy code
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Genode::Irq_proxy_component : public Irq_proxy_base
|
|
||||||
{
|
{
|
||||||
private:
|
L4_ThreadId_t const irq_thread = irqno_to_threadid(_irq);
|
||||||
|
|
||||||
/*
|
/* send unmask message and wait for new IRQ */
|
||||||
* On Pistachio, an IRQ is unmasked right after attaching.
|
L4_Set_MsgTag(L4_Niltag);
|
||||||
* Hence, the kernel may send an IRQ IPC when the IRQ hander is
|
L4_MsgTag_t res = L4_Call(irq_thread);
|
||||||
* not explicitly waiting for an IRQ but when it is waiting for
|
|
||||||
* a client's 'wait_for_irq' RPC call. To avoid this conflict, we
|
|
||||||
* lazily associate to the IRQ when calling the 'wait_for_irq'
|
|
||||||
* function for the first time. We use the '_irq_attached' flag
|
|
||||||
* for detecting the first call.
|
|
||||||
*/
|
|
||||||
bool _irq_attached; /* true if IRQ is already attached */
|
|
||||||
|
|
||||||
protected:
|
if (L4_IpcFailed(res))
|
||||||
|
PERR("ipc error while waiting for interrupt.");
|
||||||
|
}
|
||||||
|
|
||||||
bool _associate() { return true; }
|
|
||||||
|
|
||||||
void _wait_for_irq()
|
void Irq_object::start()
|
||||||
{
|
{
|
||||||
L4_ThreadId_t irq_thread = irqno_to_threadid(_irq_number);
|
::Thread_base::start();
|
||||||
|
_sync_bootup.lock();
|
||||||
|
}
|
||||||
|
|
||||||
/* attach to IRQ when called for the first time */
|
|
||||||
L4_MsgTag_t res;
|
|
||||||
if (!_irq_attached) {
|
|
||||||
|
|
||||||
if (L4_AssociateInterrupt(irq_thread, L4_Myself()) != true) {
|
void Irq_object::entry()
|
||||||
PERR("L4_AssociateInterrupt failed");
|
{
|
||||||
return;
|
if (!_associate()) {
|
||||||
}
|
PERR("Could not associate with IRQ 0x%x", _irq);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/* thread is up and ready */
|
||||||
* Right after associating with an interrupt, the interrupt is
|
_sync_bootup.unlock();
|
||||||
* unmasked. Hence, we do not need to send an unmask message
|
|
||||||
* to the IRQ thread but just wait for the IRQ.
|
|
||||||
*/
|
|
||||||
L4_Set_MsgTag(L4_Niltag);
|
|
||||||
res = L4_Receive(irq_thread);
|
|
||||||
|
|
||||||
/*
|
/* wait for first ack_irq */
|
||||||
* Now, the IRQ is masked. To receive the next IRQ we have to send
|
_sync_ack.lock();
|
||||||
* an unmask message to the IRQ thread first.
|
|
||||||
*/
|
|
||||||
_irq_attached = true;
|
|
||||||
|
|
||||||
/* receive subsequent interrupt */
|
/*
|
||||||
} else {
|
* Right after associating with an interrupt, the interrupt is
|
||||||
|
* unmasked. Hence, we do not need to send an unmask message
|
||||||
|
* to the IRQ thread but just wait for the IRQ.
|
||||||
|
*/
|
||||||
|
L4_ThreadId_t const irq_thread = irqno_to_threadid(_irq);
|
||||||
|
L4_Set_MsgTag(L4_Niltag);
|
||||||
|
L4_MsgTag_t res = L4_Receive(irq_thread);
|
||||||
|
|
||||||
/* send unmask message and wait for new IRQ */
|
if (L4_IpcFailed(res))
|
||||||
L4_Set_MsgTag(L4_Niltag);
|
PERR("ipc error while attaching to interrupt.");
|
||||||
res = L4_Call(irq_thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (L4_IpcFailed(res)) {
|
/*
|
||||||
PERR("ipc error while waiting for interrupt.");
|
* Now, the IRQ is masked. To receive the next IRQ we have to send
|
||||||
return;
|
* an unmask message to the IRQ thread first.
|
||||||
}
|
*/
|
||||||
}
|
if (_sig_cap.valid()) {
|
||||||
|
Genode::Signal_transmitter(_sig_cap).submit(1);
|
||||||
|
_sync_ack.lock();
|
||||||
|
}
|
||||||
|
|
||||||
void _ack_irq() { }
|
while (true) {
|
||||||
|
|
||||||
public:
|
_wait_for_irq();
|
||||||
|
|
||||||
Irq_proxy_component(long irq_number)
|
if (!_sig_cap.valid())
|
||||||
:
|
continue;
|
||||||
Irq_proxy(irq_number),
|
|
||||||
_irq_attached(false)
|
|
||||||
{
|
|
||||||
_start();
|
|
||||||
}
|
|
||||||
|
|
||||||
~Irq_proxy_component()
|
Genode::Signal_transmitter(_sig_cap).submit(1);
|
||||||
{
|
|
||||||
L4_ThreadId_t const thread_id = irqno_to_threadid(_irq_number);
|
|
||||||
L4_Word_t const res = L4_DeassociateInterrupt(thread_id);
|
|
||||||
|
|
||||||
if (res != 1)
|
_sync_ack.lock();
|
||||||
PERR("L4_DeassociateInterrupt failed");
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
Irq_object::Irq_object(unsigned irq)
|
||||||
|
:
|
||||||
|
Thread<4096>("irq"),
|
||||||
|
_sync_ack(Lock::LOCKED), _sync_bootup(Lock::LOCKED),
|
||||||
|
_irq(irq)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
/***************************
|
/***************************
|
||||||
|
@ -138,68 +126,44 @@ class Genode::Irq_proxy_component : public Irq_proxy_base
|
||||||
***************************/
|
***************************/
|
||||||
|
|
||||||
|
|
||||||
void Irq_session_component::ack_irq()
|
|
||||||
{
|
|
||||||
if (!_proxy) {
|
|
||||||
PERR("Expected to find IRQ proxy for IRQ %02x", _irq_number);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_proxy->ack_irq();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Irq_session_component::Irq_session_component(Range_allocator *irq_alloc,
|
Irq_session_component::Irq_session_component(Range_allocator *irq_alloc,
|
||||||
const char *args)
|
const char *args)
|
||||||
:
|
:
|
||||||
_irq_alloc(irq_alloc)
|
_irq_number(Arg_string::find_arg(args, "irq_number").long_value(-1)),
|
||||||
|
_irq_alloc(irq_alloc),
|
||||||
|
_irq_object(_irq_number)
|
||||||
{
|
{
|
||||||
long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1);
|
|
||||||
if (irq_number == -1) {
|
|
||||||
PERR("invalid IRQ number requested");
|
|
||||||
throw Root::Unavailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
long msi = Arg_string::find_arg(args, "device_config_phys").long_value(0);
|
long msi = Arg_string::find_arg(args, "device_config_phys").long_value(0);
|
||||||
if (msi)
|
if (msi)
|
||||||
throw Root::Unavailable();
|
throw Root::Unavailable();
|
||||||
|
|
||||||
/* check if IRQ thread was started before */
|
if (!irq_alloc || irq_alloc->alloc_addr(1, _irq_number).is_error()) {
|
||||||
_proxy = Irq_proxy_component::get_irq_proxy<Irq_proxy_component>(irq_number, irq_alloc);
|
PERR("Unavailable IRQ 0x%x requested", _irq_number);
|
||||||
if (!_proxy) {
|
|
||||||
PERR("unavailable IRQ %lx requested", irq_number);
|
|
||||||
throw Root::Unavailable();
|
throw Root::Unavailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
_irq_number = irq_number;
|
_irq_object.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Irq_session_component::~Irq_session_component()
|
Irq_session_component::~Irq_session_component()
|
||||||
{
|
{
|
||||||
if (!_proxy) return;
|
L4_Word_t res = L4_DeassociateInterrupt(irqno_to_threadid(_irq_number));
|
||||||
|
|
||||||
if (_irq_sigh.valid())
|
if (res != 1)
|
||||||
_proxy->remove_sharer(&_irq_sigh);
|
PERR("L4_DeassociateInterrupt failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Irq_session_component::sigh(Genode::Signal_context_capability sigh)
|
void Irq_session_component::ack_irq()
|
||||||
{
|
{
|
||||||
if (!_proxy) {
|
_irq_object.ack_irq();
|
||||||
PERR("signal handler got not registered - irq thread unavailable");
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Genode::Signal_context_capability old = _irq_sigh;
|
|
||||||
|
|
||||||
if (old.valid() && !sigh.valid())
|
void Irq_session_component::sigh(Genode::Signal_context_capability cap)
|
||||||
_proxy->remove_sharer(&_irq_sigh);
|
{
|
||||||
|
_irq_object.sigh(cap);
|
||||||
_irq_sigh = sigh;
|
|
||||||
|
|
||||||
if (!old.valid() && sigh.valid())
|
|
||||||
_proxy->add_sharer(&_irq_sigh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
42
repos/base/src/core/include/irq_object.h
Normal file
42
repos/base/src/core/include/irq_object.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* \brief Core-specific instance of the IRQ session interface
|
||||||
|
* \author Christian Helmuth
|
||||||
|
* \date 2007-09-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2007-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/thread.h>
|
||||||
|
|
||||||
|
namespace Genode { class Irq_object; }
|
||||||
|
|
||||||
|
class Genode::Irq_object : public Thread<4096> {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Signal_context_capability _sig_cap;
|
||||||
|
Lock _sync_ack;
|
||||||
|
Lock _sync_bootup;
|
||||||
|
unsigned _irq;
|
||||||
|
|
||||||
|
bool _associate();
|
||||||
|
void _wait_for_irq();
|
||||||
|
|
||||||
|
void entry() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Irq_object(unsigned irq);
|
||||||
|
|
||||||
|
void sigh(Signal_context_capability cap) { _sig_cap = cap; }
|
||||||
|
void ack_irq() { _sync_ack.unlock(); }
|
||||||
|
|
||||||
|
void start() override;
|
||||||
|
};
|
|
@ -1,240 +0,0 @@
|
||||||
/**
|
|
||||||
* \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 void remove_sharer(Irq_sigh *s)
|
|
||||||
{
|
|
||||||
Lock::Guard lock_guard(_mutex);
|
|
||||||
|
|
||||||
_sigh_list.remove(s);
|
|
||||||
--_num_sharers;
|
|
||||||
|
|
||||||
if (_woken_up)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_num_acknowledgers == _num_sharers) {
|
|
||||||
_sleep.up();
|
|
||||||
_woken_up = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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_ */
|
|
|
@ -5,37 +5,33 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2007-2013 Genode Labs GmbH
|
* Copyright (C) 2007-2015 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_
|
#pragma once
|
||||||
#define _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_
|
|
||||||
|
|
||||||
|
#include <base/lock.h>
|
||||||
#include <base/rpc_server.h>
|
#include <base/rpc_server.h>
|
||||||
#include <base/rpc_client.h>
|
#include <base/rpc_client.h>
|
||||||
#include <util/list.h>
|
#include <util/list.h>
|
||||||
#include <irq_session/irq_session.h>
|
#include <irq_session/irq_session.h>
|
||||||
#include <irq_session/capability.h>
|
#include <irq_session/capability.h>
|
||||||
#include <irq_proxy.h>
|
|
||||||
|
|
||||||
namespace Genode {
|
#include <irq_object.h>
|
||||||
class Irq_proxy_component;
|
|
||||||
class Irq_session_component;
|
namespace Genode { class Irq_session_component; }
|
||||||
}
|
|
||||||
|
|
||||||
class Genode::Irq_session_component : public Rpc_object<Irq_session>,
|
class Genode::Irq_session_component : public Rpc_object<Irq_session>,
|
||||||
public List<Irq_session_component>::Element
|
public List<Irq_session_component>::Element
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
unsigned _irq_number;
|
unsigned _irq_number;
|
||||||
Range_allocator *_irq_alloc;
|
Range_allocator *_irq_alloc;
|
||||||
Irq_proxy_component *_proxy;
|
Irq_object _irq_object;
|
||||||
|
|
||||||
Irq_sigh _irq_sigh;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -45,14 +41,14 @@ class Genode::Irq_session_component : public Rpc_object<Irq_session>,
|
||||||
* \param irq_alloc platform-dependent IRQ allocator
|
* \param irq_alloc platform-dependent IRQ allocator
|
||||||
* \param args session construction arguments
|
* \param args session construction arguments
|
||||||
*/
|
*/
|
||||||
Irq_session_component(Range_allocator *irq_alloc,
|
Irq_session_component(Range_allocator *irq_alloc, const char *args);
|
||||||
const char *args);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destructor
|
* Destructor
|
||||||
*/
|
*/
|
||||||
~Irq_session_component();
|
~Irq_session_component();
|
||||||
|
|
||||||
|
|
||||||
/***************************
|
/***************************
|
||||||
** Irq session interface **
|
** Irq session interface **
|
||||||
***************************/
|
***************************/
|
||||||
|
@ -61,5 +57,3 @@ class Genode::Irq_session_component : public Rpc_object<Irq_session>,
|
||||||
void sigh(Signal_context_capability) override;
|
void sigh(Signal_context_capability) override;
|
||||||
Info info() override;
|
Info info() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ */
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user