mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-19 03:06:39 +00:00
Core: Shared IRQ support for Nova/FOC/OKL4
Implement shared IRQs using 'Irq_proxy' class. Nova: Added global worker 'Irq_thread' support in core and adapted Irq_session. FOC: Adapted IRQ session code, x86 has shared IRQ support, ARM uses the old model. Read and set 'mode' argument (from MADT) in 'Irq_session'. OKL4: Use generic 'Irq_proxy' Fixes issue #390
This commit is contained in:
parent
5549f6413b
commit
4a3d852b65
@ -15,4 +15,4 @@
|
||||
|
||||
void Genode::Platform::_setup_io_port_alloc() { }
|
||||
|
||||
void Genode::Platform::setup_irq_mode(unsigned irq_number) { }
|
||||
void Genode::Platform::setup_irq_mode(unsigned, unsigned, unsigned) { }
|
||||
|
29
base-foc/src/core/include/arm/irq_proxy_component.h
Normal file
29
base-foc/src/core/include/arm/irq_proxy_component.h
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* \brief Base class for shared interrupts on ARM
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2012-10-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012 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.
|
||||
*/
|
||||
|
||||
#ifndef _CORE__INCLUDE__ARM__IRQ_PROXY_COMPONENT_H_
|
||||
#define _CORE__INCLUDE__ARM__IRQ_PROXY_COMPONENT_H_
|
||||
|
||||
#include <irq_proxy.h>
|
||||
|
||||
namespace Genode {
|
||||
|
||||
/**
|
||||
* On ARM we disable shared interrupts
|
||||
*/
|
||||
typedef Irq_proxy_single Irq_proxy_base;
|
||||
|
||||
}
|
||||
|
||||
#endif /* _CORE__INCLUDE__ARM__IRQ_PROXY_COMPONENT_H_ */
|
||||
|
@ -23,48 +23,13 @@
|
||||
|
||||
namespace Genode {
|
||||
|
||||
class Irq_proxy_component;
|
||||
|
||||
class Irq_session_component : public Rpc_object<Irq_session>,
|
||||
public List<Irq_session_component>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
class Interrupt : public Avl_node<Interrupt>
|
||||
{
|
||||
private:
|
||||
|
||||
Cap_index* _cap;
|
||||
Semaphore _sem;
|
||||
|
||||
public:
|
||||
|
||||
unsigned number;
|
||||
|
||||
Interrupt();
|
||||
|
||||
bool higher(Interrupt *n);
|
||||
Interrupt* find_by_num(unsigned num);
|
||||
|
||||
Native_thread capability() { return _cap->kcap(); }
|
||||
Semaphore* semaphore() { return &_sem; }
|
||||
};
|
||||
|
||||
|
||||
class Interrupt_handler : public Thread<4096>
|
||||
{
|
||||
private:
|
||||
|
||||
Interrupt_handler() { start(); }
|
||||
|
||||
void entry();
|
||||
|
||||
public:
|
||||
|
||||
static Native_thread handler_cap();
|
||||
};
|
||||
|
||||
|
||||
Interrupt _irq;
|
||||
Range_allocator *_irq_alloc;
|
||||
|
||||
/*
|
||||
* Each IRQ session uses a dedicated server activation
|
||||
@ -73,8 +38,7 @@ namespace Genode {
|
||||
Rpc_entrypoint _ep;
|
||||
|
||||
Irq_session_capability _irq_cap;
|
||||
|
||||
static Avl_tree<Interrupt>* _irqs();
|
||||
Irq_proxy_component *_proxy;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -129,9 +129,10 @@ namespace Genode {
|
||||
Core_pager *core_pager();
|
||||
|
||||
/**
|
||||
* Set interrupt mode (e.g., level or edge)
|
||||
* Set interrupt trigger/polarity (e.g., level or edge, high or low)
|
||||
*/
|
||||
static void setup_irq_mode(unsigned irq_number);
|
||||
static void setup_irq_mode(unsigned irq_number, unsigned trigger,
|
||||
unsigned polarity);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
24
base-foc/src/core/include/x86/irq_proxy_component.h
Normal file
24
base-foc/src/core/include/x86/irq_proxy_component.h
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* \brief Base class for shared interrupts on x86
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2012-10-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012 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.
|
||||
*/
|
||||
|
||||
#ifndef _CORE__INCLUDE__X86__IRQ_PROXY_COMPONENT_H_
|
||||
#define _CORE__INCLUDE__X86__IRQ_PROXY_COMPONENT_H_
|
||||
|
||||
#include <irq_proxy.h>
|
||||
|
||||
namespace Genode {
|
||||
typedef Irq_proxy<Thread<1024 * sizeof(addr_t)> > Irq_proxy_base;
|
||||
}
|
||||
|
||||
#endif /* _CORE__INCLUDE__X86__IRQ_PROXY_COMPONENT_H_ */
|
||||
|
@ -2,6 +2,7 @@
|
||||
* \brief Fiasco.OC-specific core implementation of IRQ sessions
|
||||
* \author Christian Helmuth
|
||||
* \author Stefan Kalkowski
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2007-09-13
|
||||
*
|
||||
* FIXME ram quota missing
|
||||
@ -20,6 +21,7 @@
|
||||
|
||||
/* core includes */
|
||||
#include <irq_root.h>
|
||||
#include <irq_proxy_component.h>
|
||||
#include <irq_session_component.h>
|
||||
#include <platform.h>
|
||||
#include <util.h>
|
||||
@ -34,36 +36,197 @@ namespace Fiasco {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
namespace Genode {
|
||||
class Interrupt_handler;
|
||||
class Irq_proxy_component;
|
||||
}
|
||||
|
||||
Irq_session_component::Interrupt*
|
||||
Irq_session_component::Interrupt::find_by_num(unsigned num)
|
||||
/**
|
||||
* Dispatches interrupts from kernel
|
||||
*/
|
||||
class Genode::Interrupt_handler : public Thread<4096>
|
||||
{
|
||||
if (number == num) return this;
|
||||
private:
|
||||
|
||||
Interrupt *n = Avl_node<Interrupt>::child(num > number);
|
||||
return n ? n->find_by_num(num) : 0;
|
||||
Interrupt_handler() { start(); }
|
||||
|
||||
public:
|
||||
|
||||
void entry();
|
||||
|
||||
static Native_thread handler_cap()
|
||||
{
|
||||
static Interrupt_handler handler;
|
||||
return handler._thread_cap.dst();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Irq_proxy interface implementation
|
||||
*/
|
||||
class Genode::Irq_proxy_component : public Irq_proxy_base
|
||||
{
|
||||
private:
|
||||
|
||||
Cap_index *_cap;
|
||||
Semaphore _sem;
|
||||
long _trigger; /* interrupt trigger */
|
||||
long _polarity; /* interrupt polarity */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (l4_error(l4_icu_bind(L4_BASE_ICU_CAP, _irq_number, _capability()))) {
|
||||
PERR("Binding IRQ%ld to the ICU failed", _irq_number);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* set interrupt mode */
|
||||
Platform::setup_irq_mode(_irq_number, _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;
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
Irq_proxy_component(long irq_number)
|
||||
:
|
||||
Irq_proxy_base(irq_number),
|
||||
_cap(cap_map()->insert(platform_specific()->cap_id_alloc()->alloc())),
|
||||
_sem(), _trigger(-1), _polarity(-1) { }
|
||||
|
||||
Semaphore *semaphore() { return &_sem; }
|
||||
|
||||
void start(long trigger, long polarity)
|
||||
{
|
||||
_trigger = trigger;
|
||||
_polarity = polarity;
|
||||
_start();
|
||||
}
|
||||
|
||||
bool match_mode(long trigger, long polarity)
|
||||
{
|
||||
if (trigger == Irq_session::TRIGGER_UNCHANGED &&
|
||||
polarity == Irq_session::POLARITY_UNCHANGED)
|
||||
return true;
|
||||
|
||||
if (_trigger < 0 && _polarity < 0)
|
||||
return true;
|
||||
|
||||
return _trigger == trigger && _polarity == polarity;
|
||||
}
|
||||
|
||||
long trigger() const { return _trigger; }
|
||||
long polarity() const { return _polarity; }
|
||||
};
|
||||
|
||||
|
||||
/********************************
|
||||
** IRQ session implementation **
|
||||
********************************/
|
||||
|
||||
|
||||
Irq_session_component::Irq_session_component(Cap_session *cap_session,
|
||||
Range_allocator *irq_alloc,
|
||||
const char *args)
|
||||
:
|
||||
_ep(cap_session, STACK_SIZE, "irqctrl"),
|
||||
_proxy(0)
|
||||
{
|
||||
using namespace Fiasco;
|
||||
|
||||
long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1);
|
||||
if (irq_number == -1) {
|
||||
PERR("Unavailable IRQ %lx requested", irq_number);
|
||||
throw Root::Invalid_args();
|
||||
}
|
||||
|
||||
long irq_trigger = Arg_string::find_arg(args, "irq_trigger").long_value(-1);
|
||||
irq_trigger = irq_trigger == -1 ? 0 : irq_trigger;
|
||||
|
||||
long irq_polarity = Arg_string::find_arg(args, "irq_polarity").long_value(-1);
|
||||
irq_polarity = irq_polarity == -1 ? 0 : irq_polarity;
|
||||
|
||||
/*
|
||||
* temorary hack for fiasco.oc using the local-apic,
|
||||
* where old pic-line 0 maps to 2
|
||||
*/
|
||||
if (irq_number == 0)
|
||||
irq_number = 2;
|
||||
|
||||
if (!(_proxy = Irq_proxy_component::get_irq_proxy<Irq_proxy_component>(irq_number,
|
||||
irq_alloc))) {
|
||||
PERR("No proxy for IRQ %lu found", irq_number);
|
||||
throw Root::Unavailable();
|
||||
}
|
||||
|
||||
/* sanity check */
|
||||
if (!_proxy->match_mode(irq_trigger, irq_polarity)) {
|
||||
PERR("Interrupt mode mismatch: IRQ %ld current mode: t: %ld p: %ld"
|
||||
"request mode: trg: %ld p: %ld",
|
||||
irq_number, _proxy->trigger(), _proxy->polarity(),
|
||||
irq_trigger, irq_polarity);
|
||||
throw Root::Unavailable();
|
||||
}
|
||||
|
||||
/* set interrupt mode and start proxy */
|
||||
_proxy->start(irq_trigger, irq_polarity);
|
||||
|
||||
if (!_proxy->add_sharer())
|
||||
throw Root::Unavailable();
|
||||
|
||||
/* initialize capability */
|
||||
_irq_cap = _ep.manage(this);
|
||||
}
|
||||
|
||||
|
||||
bool Irq_session_component::Interrupt::higher(Irq_session_component::Interrupt *n)
|
||||
void Irq_session_component::wait_for_irq()
|
||||
{
|
||||
return n->number > number;
|
||||
_proxy->wait_for_irq();
|
||||
}
|
||||
|
||||
|
||||
Irq_session_component::Interrupt::Interrupt()
|
||||
: _cap(cap_map()->insert(platform_specific()->cap_id_alloc()->alloc())),
|
||||
_sem(), number(0) {}
|
||||
|
||||
|
||||
Native_thread Irq_session_component::Interrupt_handler::handler_cap()
|
||||
Irq_session_component::~Irq_session_component()
|
||||
{
|
||||
static Interrupt_handler handler;
|
||||
return handler._thread_cap.dst();
|
||||
PERR("Implement me, immediately!");
|
||||
}
|
||||
|
||||
|
||||
void Irq_session_component::Interrupt_handler::entry()
|
||||
/***************************************
|
||||
** Interrupt handler implemtentation **
|
||||
***************************************/
|
||||
|
||||
void Interrupt_handler::entry()
|
||||
{
|
||||
using namespace Fiasco;
|
||||
|
||||
@ -76,81 +239,11 @@ void Irq_session_component::Interrupt_handler::entry()
|
||||
if ((err = l4_ipc_error(tag, l4_utcb())))
|
||||
PERR("IRQ receive: %d\n", err);
|
||||
else {
|
||||
Interrupt *intr = _irqs()->first();
|
||||
if (intr) {
|
||||
intr = intr->find_by_num(label);
|
||||
if (intr)
|
||||
intr->semaphore()->up();
|
||||
}
|
||||
Irq_proxy_component *proxy;
|
||||
proxy = Irq_proxy_component::get_irq_proxy<Irq_proxy_component>(label);
|
||||
|
||||
if (proxy)
|
||||
proxy->semaphore()->up();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Avl_tree<Irq_session_component::Interrupt>* Irq_session_component::_irqs()
|
||||
{
|
||||
static Avl_tree<Interrupt> irqs;
|
||||
return &irqs;
|
||||
}
|
||||
|
||||
|
||||
Irq_session_component::Irq_session_component(Cap_session *cap_session,
|
||||
Range_allocator *irq_alloc,
|
||||
const char *args)
|
||||
:
|
||||
_irq_alloc(irq_alloc),
|
||||
_ep(cap_session, STACK_SIZE, "irqctrl")
|
||||
{
|
||||
using namespace Fiasco;
|
||||
|
||||
long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1);
|
||||
if ((irq_number == -1) || _irq_alloc->alloc_addr(1, irq_number)) {
|
||||
PERR("Unavailable IRQ %lx requested", irq_number);
|
||||
throw Root::Invalid_args();
|
||||
}
|
||||
|
||||
/*
|
||||
* temorary hack for fiasco.oc using the local-apic,
|
||||
* where old pic-line 0 maps to 2
|
||||
*/
|
||||
if (irq_number == 0)
|
||||
irq_number = 2;
|
||||
|
||||
_irq.number = irq_number;
|
||||
Irq_session_component::_irqs()->insert(&_irq);
|
||||
|
||||
if (l4_error(l4_factory_create_irq(L4_BASE_FACTORY_CAP, _irq.capability())))
|
||||
PERR("l4_factory_create_irq failed!");
|
||||
|
||||
if (l4_error(l4_icu_bind(L4_BASE_ICU_CAP, irq_number, _irq.capability())))
|
||||
PERR("Binding IRQ%ld to the ICU failed", irq_number);
|
||||
|
||||
/* set interrupt mode */
|
||||
Platform::setup_irq_mode(irq_number);
|
||||
|
||||
if (l4_error(l4_irq_attach(_irq.capability(), irq_number,
|
||||
Interrupt_handler::handler_cap())))
|
||||
PERR("Error attaching to IRQ %ld", irq_number);
|
||||
|
||||
/* initialize capability */
|
||||
_irq_cap = _ep.manage(this);
|
||||
}
|
||||
|
||||
|
||||
void Irq_session_component::wait_for_irq()
|
||||
{
|
||||
using namespace Fiasco;
|
||||
|
||||
int err;
|
||||
l4_msgtag_t tag = l4_irq_unmask(_irq.capability());
|
||||
if ((err = l4_ipc_error(tag, l4_utcb())))
|
||||
PERR("IRQ unmask: %d\n", err);
|
||||
|
||||
_irq.semaphore()->down();
|
||||
}
|
||||
|
||||
|
||||
Irq_session_component::~Irq_session_component()
|
||||
{
|
||||
PERR("Implement me, immediately!");
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ LD_TEXT_ADDR = 0x80140000
|
||||
|
||||
REQUIRES += arm foc_panda
|
||||
SRC_CC += arm/platform_arm.cc
|
||||
INC_DIR += $(REP_DIR)/src/core/include/arm
|
||||
|
||||
|
||||
vpath io_port_session_component.cc $(GEN_CORE_DIR)/arm
|
||||
|
||||
|
@ -2,6 +2,7 @@ include $(PRG_DIR)/../target.inc
|
||||
|
||||
REQUIRES += arm foc_pbxa9
|
||||
SRC_CC += arm/platform_arm.cc
|
||||
INC_DIR += $(REP_DIR)/src/core/include/arm
|
||||
|
||||
LD_TEXT_ADDR = 0x70490000
|
||||
|
||||
|
@ -2,6 +2,7 @@ include $(PRG_DIR)/../target.inc
|
||||
|
||||
REQUIRES += arm foc_vea9x4
|
||||
SRC_CC += arm/platform_arm.cc
|
||||
INC_DIR += $(REP_DIR)/src/core/include/arm
|
||||
|
||||
LD_TEXT_ADDR = 0x60490000
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <irq_session/irq_session.h>
|
||||
|
||||
#include "platform.h"
|
||||
#include "util.h"
|
||||
@ -45,12 +46,28 @@ void Genode::Platform::_setup_io_port_alloc()
|
||||
}
|
||||
|
||||
|
||||
void Genode::Platform::setup_irq_mode(unsigned irq_number)
|
||||
void Genode::Platform::setup_irq_mode(unsigned irq_number, unsigned trigger,
|
||||
unsigned polarity)
|
||||
{
|
||||
using namespace Fiasco;
|
||||
|
||||
/* set IRQ below 16 to edge/high and others to level/low */
|
||||
l4_umword_t mode = irq_number < 16 ? L4_IRQ_F_POS_EDGE : L4_IRQ_F_LEVEL_LOW;
|
||||
/*
|
||||
* Translate ACPI interrupt mode (trigger/polarity) to Fiasco APIC
|
||||
* values. Default is edge/high for IRQs < 16 and level low for IRQs > 16
|
||||
*/
|
||||
l4_umword_t mode;
|
||||
mode = (trigger == Irq_session::TRIGGER_LEVEL) ||
|
||||
(irq_number > 15 && trigger == Irq_session::TRIGGER_UNCHANGED)
|
||||
? L4_IRQ_F_LEVEL : L4_IRQ_F_EDGE;
|
||||
|
||||
mode |= (polarity == Irq_session::POLARITY_LOW) ||
|
||||
(irq_number > 15 && polarity == Irq_session::POLARITY_UNCHANGED)
|
||||
? L4_IRQ_F_NEG : L4_IRQ_F_POS;
|
||||
|
||||
|
||||
/*
|
||||
* Set mode
|
||||
*/
|
||||
if (l4_error(l4_icu_set_mode(L4_BASE_ICU_CAP, irq_number, mode)))
|
||||
PERR("Setting mode for IRQ%u failed", irq_number);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ include $(PRG_DIR)/../target.inc
|
||||
|
||||
REQUIRES += x86
|
||||
SRC_CC += x86/platform_x86.cc
|
||||
INC_DIR += $(REP_DIR)/src/core/include/x86
|
||||
|
||||
vpath io_port_session_component.cc $(GEN_CORE_DIR)/x86
|
||||
|
||||
|
@ -214,6 +214,9 @@ Pager_object::Pager_object(unsigned long badge)
|
||||
_state.singlestep = false;
|
||||
_state.sel_client_ec = Native_thread::INVALID_INDEX;
|
||||
|
||||
/* creates local EC */
|
||||
Thread_base::start();
|
||||
|
||||
/* Create portal for exception handlers 0x0 - 0xd */
|
||||
for (unsigned i = 0; i < PT_SEL_PAGE_FAULT; i++) {
|
||||
res = create_pt(exc_pt_sel() + i, pd_sel, _tid.ec_sel,
|
||||
|
@ -243,6 +243,11 @@ Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size,
|
||||
throw Cpu_session::Thread_creation_failed();
|
||||
_tid.ec_sel = ec_cap.local_name();
|
||||
}
|
||||
else
|
||||
/**
|
||||
* Required for core threads (creates local EC)
|
||||
*/
|
||||
Thread_base::start();
|
||||
|
||||
_rcv_buf.rcv_prepare_pt_sel_window((Nova::Utcb *)&_context->utcb);
|
||||
|
||||
|
@ -22,20 +22,20 @@
|
||||
|
||||
namespace Genode {
|
||||
|
||||
class Irq_proxy_component;
|
||||
|
||||
class Irq_session_component : public Rpc_object<Irq_session>,
|
||||
public List<Irq_session_component>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
unsigned _irq_number;
|
||||
Range_allocator *_irq_alloc;
|
||||
|
||||
/*
|
||||
* Each IRQ session uses a dedicated server activation
|
||||
*/
|
||||
enum { STACK_SIZE = 2048 };
|
||||
Rpc_entrypoint _ep;
|
||||
Irq_session_capability _irq_cap;
|
||||
Irq_proxy_component *_proxy;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -137,7 +137,7 @@ inline int map_local(Nova::Utcb *utcb,
|
||||
|
||||
if (verbose_local_map)
|
||||
Genode::printf("::map_local: order %zx %lx:%lx %lx:%lx\n",
|
||||
order, from_curr, from_end, to_curr, to_end);
|
||||
order, from_curr, from_end, to_curr, to_end);
|
||||
|
||||
int const res = map_local(utcb,
|
||||
Mem_crd((from_curr >> 12), order - get_page_size_log2(), rwx),
|
||||
|
@ -14,10 +14,13 @@
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <base/sleep.h>
|
||||
|
||||
/* core includes */
|
||||
#include <irq_root.h>
|
||||
#include <irq_proxy.h>
|
||||
#include <platform.h>
|
||||
#include <platform_pd.h>
|
||||
|
||||
/* NOVA includes */
|
||||
#include <nova/syscalls.h>
|
||||
@ -26,11 +29,129 @@
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
namespace Genode {
|
||||
class Irq_proxy_component;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Global worker (i.e. thread with SC)
|
||||
*/
|
||||
class Irq_thread : public Thread_base
|
||||
{
|
||||
private:
|
||||
|
||||
static void _thread_start()
|
||||
{
|
||||
Thread_base::myself()->entry();
|
||||
sleep_forever();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Irq_thread(char const *name) : Thread_base(name, 1024 * sizeof(addr_t)) { }
|
||||
|
||||
/**
|
||||
* Create global EC, associate it to SC
|
||||
*/
|
||||
void start()
|
||||
{
|
||||
using namespace Nova;
|
||||
addr_t pd_sel = Platform_pd::pd_core_sel();
|
||||
addr_t utcb = reinterpret_cast<addr_t>(&_context->utcb);
|
||||
|
||||
/*
|
||||
* Put IP on stack, it will be read from core pager in platform.cc
|
||||
*/
|
||||
addr_t *sp = reinterpret_cast<addr_t *>(_context->stack - sizeof(addr_t));
|
||||
*sp = reinterpret_cast<addr_t>(_thread_start);
|
||||
|
||||
/* create global EC */
|
||||
enum { CPU_NO = 0, GLOBAL = true };
|
||||
uint8_t res = create_ec(_tid.ec_sel, pd_sel, CPU_NO,
|
||||
utcb, (mword_t)sp, _tid.exc_pt_sel, GLOBAL);
|
||||
if (res != NOVA_OK) {
|
||||
PERR("%p - create_ec returned %d", this, res);
|
||||
throw Cpu_session::Thread_creation_failed();
|
||||
}
|
||||
|
||||
/* map startup portal from main thread */
|
||||
map_local((Utcb *)Thread_base::myself()->utcb(),
|
||||
Obj_crd(PT_SEL_STARTUP, 0),
|
||||
Obj_crd(_tid.exc_pt_sel + PT_SEL_STARTUP, 0));
|
||||
|
||||
/* create SC */
|
||||
unsigned sc_sel = cap_selector_allocator()->alloc();
|
||||
res = create_sc(sc_sel, pd_sel, _tid.ec_sel, Qpd());
|
||||
if (res != NOVA_OK) {
|
||||
PERR("%p - create_sc returned returned %d", this, res);
|
||||
throw Cpu_session::Thread_creation_failed();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Irq_proxy interface implementation
|
||||
*/
|
||||
class Genode::Irq_proxy_component : public Irq_proxy<Irq_thread>
|
||||
{
|
||||
private:
|
||||
|
||||
long _irq_sel; /* IRQ cap selector */
|
||||
|
||||
protected:
|
||||
|
||||
bool _associate()
|
||||
{
|
||||
/* alloc slector where IRQ will be mapped */
|
||||
_irq_sel = cap_selector_allocator()->alloc();
|
||||
|
||||
/* since we run in APIC mode translate IRQ 0 (PIT) to 2 */
|
||||
if (!_irq_number)
|
||||
_irq_number = 2;
|
||||
|
||||
/* map IRQ number to selector */
|
||||
int ret = map_local((Nova::Utcb *)Thread_base::myself()->utcb(),
|
||||
Nova::Obj_crd(platform_specific()->gsi_base_sel() + _irq_number, 0),
|
||||
Nova::Obj_crd(_irq_sel, 0),
|
||||
true);
|
||||
if (ret) {
|
||||
PERR("Could not map IRQ %ld", _irq_number);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* assign IRQ to CPU */
|
||||
enum { CPU = 0 };
|
||||
Nova::assign_gsi(_irq_sel, 0, CPU);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _wait_for_irq()
|
||||
{
|
||||
if (Nova::sm_ctrl(_irq_sel, Nova::SEMAPHORE_DOWN))
|
||||
nova_die();
|
||||
}
|
||||
|
||||
void _ack_irq() { }
|
||||
|
||||
public:
|
||||
|
||||
Irq_proxy_component(long irq_number) : Irq_proxy(irq_number)
|
||||
{
|
||||
_start();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef Irq_proxy<Irq_thread> Proxy;
|
||||
|
||||
|
||||
void Irq_session_component::wait_for_irq()
|
||||
{
|
||||
if (Nova::sm_ctrl(_irq_number, Nova::SEMAPHORE_DOWN))
|
||||
nova_die();
|
||||
_proxy->wait_for_irq();
|
||||
/* interrupt ocurred and proxy woke us up */
|
||||
}
|
||||
|
||||
|
||||
@ -38,36 +159,19 @@ Irq_session_component::Irq_session_component(Cap_session *cap_session,
|
||||
Range_allocator *irq_alloc,
|
||||
const char *args)
|
||||
:
|
||||
_irq_alloc(irq_alloc),
|
||||
_ep(cap_session, STACK_SIZE, "irq")
|
||||
{
|
||||
long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1);
|
||||
if (irq_number == -1 || !irq_alloc ||
|
||||
irq_alloc->alloc_addr(1, irq_number) != Range_allocator::ALLOC_OK) {
|
||||
|
||||
/* check if IRQ thread was started before */
|
||||
_proxy = Proxy::get_irq_proxy<Irq_proxy_component>(irq_number, irq_alloc);
|
||||
if (irq_number == -1 || !_proxy) {
|
||||
PERR("Unavailable IRQ %lx requested", irq_number);
|
||||
throw Root::Invalid_args();
|
||||
}
|
||||
|
||||
/* alloc slector where IRQ will be mapped */
|
||||
_irq_number = cap_selector_allocator()->alloc();
|
||||
|
||||
/* since we run in APIC mode translate IRQ 0 (PIT) to 2 */
|
||||
if (!irq_number)
|
||||
irq_number = 2;
|
||||
|
||||
/* map IRQ number to selector */
|
||||
int ret = map_local((Nova::Utcb *)Thread_base::myself()->utcb(),
|
||||
Nova::Obj_crd(platform_specific()->gsi_base_sel() + irq_number, 0),
|
||||
Nova::Obj_crd(_irq_number, 0),
|
||||
true);
|
||||
if (ret) {
|
||||
PERR("Could not map IRQ %d", _irq_number);
|
||||
throw Root::Unavailable();
|
||||
}
|
||||
|
||||
/* assign IRQ to CPU */
|
||||
enum { CPU = 0 };
|
||||
Nova::assign_gsi(_irq_number, 0, CPU);
|
||||
_proxy->add_sharer();
|
||||
|
||||
/* initialize capability */
|
||||
_irq_cap = _ep.manage(this);
|
||||
}
|
||||
|
@ -134,24 +134,44 @@ static void page_fault_handler()
|
||||
}
|
||||
|
||||
|
||||
static addr_t core_pager_stack_top()
|
||||
{
|
||||
enum { STACK_SIZE = 4*1024 };
|
||||
static char stack[STACK_SIZE];
|
||||
return (addr_t)&stack[STACK_SIZE - sizeof(addr_t)];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Startup handler for core threads
|
||||
*/
|
||||
static void startup_handler()
|
||||
{
|
||||
Utcb *utcb = (Utcb *)CORE_PAGER_UTCB_ADDR;
|
||||
|
||||
/* initial IP is on stack */
|
||||
utcb->ip = *reinterpret_cast<addr_t *>(utcb->sp);
|
||||
utcb->mtd = Mtd::EIP | Mtd::ESP;
|
||||
utcb->set_msg_word(0);
|
||||
|
||||
reply((void*)core_pager_stack_top());
|
||||
}
|
||||
|
||||
|
||||
static void init_core_page_fault_handler()
|
||||
{
|
||||
/* create echo EC */
|
||||
enum {
|
||||
STACK_SIZE = 4*1024,
|
||||
CPU_NO = 0,
|
||||
GLOBAL = false,
|
||||
EXC_BASE = 0
|
||||
};
|
||||
|
||||
static char stack[STACK_SIZE];
|
||||
|
||||
addr_t sp = (addr_t)&stack[STACK_SIZE - sizeof(addr_t)];
|
||||
addr_t ec_sel = cap_selector_allocator()->alloc();
|
||||
|
||||
uint8_t ret = create_ec(ec_sel, __core_pd_sel, CPU_NO,
|
||||
CORE_PAGER_UTCB_ADDR, (addr_t)sp, EXC_BASE,
|
||||
GLOBAL);
|
||||
CORE_PAGER_UTCB_ADDR, core_pager_stack_top(),
|
||||
EXC_BASE, GLOBAL);
|
||||
if (ret)
|
||||
PDBG("create_ec returned %u", ret);
|
||||
|
||||
@ -159,6 +179,11 @@ static void init_core_page_fault_handler()
|
||||
create_pt(PT_SEL_PAGE_FAULT, __core_pd_sel, ec_sel,
|
||||
Mtd(Mtd::QUAL | Mtd::ESP | Mtd::EIP),
|
||||
(addr_t)page_fault_handler);
|
||||
|
||||
/* startup portal for global core threads */
|
||||
create_pt(PT_SEL_STARTUP, __core_pd_sel, ec_sel,
|
||||
Mtd(Mtd::EIP | Mtd::ESP),
|
||||
(addr_t)startup_handler);
|
||||
}
|
||||
|
||||
|
||||
|
@ -30,13 +30,13 @@
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
/**
|
||||
* This function is called for constructing server activations and pager
|
||||
* objects. It allocates capability selectors for the thread's execution
|
||||
* context and a synchronization-helper semaphore needed for 'Lock'.
|
||||
*/
|
||||
void Thread_base::_init_platform_thread()
|
||||
{
|
||||
/*
|
||||
* This function is called for constructing server activations and pager
|
||||
* objects. It allocates capability selectors for the thread's execution
|
||||
* context and a synchronization-helper semaphore needed for 'Lock'.
|
||||
*/
|
||||
using namespace Nova;
|
||||
|
||||
_tid.ec_sel = cap_selector_allocator()->alloc();
|
||||
@ -50,18 +50,6 @@ void Thread_base::_init_platform_thread()
|
||||
PERR("create_sm returned %u", res);
|
||||
throw Cpu_session::Thread_creation_failed();
|
||||
}
|
||||
|
||||
addr_t sp = reinterpret_cast<addr_t>(&_context->stack[-4]);
|
||||
addr_t utcb = reinterpret_cast<addr_t>(&_context->utcb);
|
||||
|
||||
/* create local EC */
|
||||
enum { CPU_NO = 0, GLOBAL = false };
|
||||
res = create_ec(_tid.ec_sel, pd_sel, CPU_NO,
|
||||
utcb, sp, _tid.exc_pt_sel, GLOBAL);
|
||||
if (res != NOVA_OK) {
|
||||
PERR("%p - create_ec returned %d", this, res);
|
||||
throw Cpu_session::Thread_creation_failed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -84,10 +72,26 @@ void Thread_base::_deinit_platform_thread()
|
||||
void Thread_base::start()
|
||||
{
|
||||
/*
|
||||
* On NOVA, core never starts regular threads.
|
||||
* On NOVA, core almost nerver starts regular threads. This simply creates a
|
||||
* local EC
|
||||
*/
|
||||
using namespace Nova;
|
||||
|
||||
addr_t sp = reinterpret_cast<addr_t>(&_context->stack[-4]);
|
||||
addr_t utcb = reinterpret_cast<addr_t>(&_context->utcb);
|
||||
addr_t pd_sel = Platform_pd::pd_core_sel();
|
||||
|
||||
/* create local EC */
|
||||
enum { CPU_NO = 0, GLOBAL = false };
|
||||
uint8_t res = create_ec(_tid.ec_sel, pd_sel, CPU_NO,
|
||||
utcb, sp, _tid.exc_pt_sel, GLOBAL);
|
||||
if (res != NOVA_OK) {
|
||||
PERR("%p - create_ec returned %d", this, res);
|
||||
throw Cpu_session::Thread_creation_failed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Thread_base::cancel_blocking()
|
||||
{
|
||||
using namespace Nova;
|
||||
|
@ -12,14 +12,10 @@
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <base/env.h>
|
||||
#include <util/arg_string.h>
|
||||
#include <irq_proxy.h>
|
||||
|
||||
/* core includes */
|
||||
#include <irq_root.h>
|
||||
#include <util.h>
|
||||
|
||||
/* OKL4 includes */
|
||||
namespace Okl4 { extern "C" {
|
||||
@ -30,8 +26,14 @@ namespace Okl4 { extern "C" {
|
||||
#include <l4/ipc.h>
|
||||
} }
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Okl4;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
/**
|
||||
* Proxy class with generic thread
|
||||
*/
|
||||
typedef Irq_proxy<Thread<0x1000> > Proxy;
|
||||
|
||||
|
||||
/* XXX move this functionality to a central place instead of duplicating it */
|
||||
@ -43,55 +45,12 @@ static inline Okl4::L4_ThreadId_t thread_get_my_global_id()
|
||||
}
|
||||
|
||||
|
||||
/******************************
|
||||
** Shared-interrupt support **
|
||||
******************************/
|
||||
|
||||
class Irq_blocker : public List<Irq_blocker>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
Lock _wait_lock;
|
||||
|
||||
public:
|
||||
|
||||
Irq_blocker() : _wait_lock(Lock::LOCKED) { }
|
||||
|
||||
void block() { _wait_lock.lock(); }
|
||||
void unblock() { _wait_lock.unlock(); }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Proxy thread that associates to the interrupt and unblocks waiting irqctrl
|
||||
* threads. Maybe, we should utilize our signals for interrupt delivery...
|
||||
*
|
||||
* XXX resources are not accounted as the interrupt is shared
|
||||
/**
|
||||
* Platform-specific proxy code
|
||||
*/
|
||||
class Irq_proxy : public Thread<0x1000>,
|
||||
public List<Irq_proxy>::Element
|
||||
class Irq_proxy_component : public Proxy
|
||||
{
|
||||
private:
|
||||
|
||||
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_blocker> _blocker_list;
|
||||
int _num_blockers; /* number of currently blocked clients */
|
||||
bool _woken_up; /* client decided to wake me up -
|
||||
this prevents multiple wakeups
|
||||
to happen during initialization */
|
||||
|
||||
const char *_construct_name(long irq_number)
|
||||
{
|
||||
snprintf(_name, sizeof(_name), "irqproxy%02lx", irq_number);
|
||||
return _name;
|
||||
}
|
||||
protected:
|
||||
|
||||
bool _associate()
|
||||
{
|
||||
@ -126,126 +85,28 @@ class Irq_proxy : public Thread<0x1000>,
|
||||
return true;
|
||||
}
|
||||
|
||||
void _loop()
|
||||
void _wait_for_irq()
|
||||
{
|
||||
/* wait for first blocker */
|
||||
_sleep.down();
|
||||
/* wait for asynchronous interrupt notification */
|
||||
L4_ThreadId_t partner = L4_nilthread;
|
||||
L4_ReplyWait(partner, &partner);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
/* wait for asynchronous interrupt notification */
|
||||
L4_ThreadId_t partner = L4_nilthread;
|
||||
L4_ReplyWait(partner, &partner);
|
||||
|
||||
{
|
||||
Lock::Guard lock_guard(_mutex);
|
||||
|
||||
/* inform blocked clients */
|
||||
Irq_blocker *b;
|
||||
while ((b = _blocker_list.first())) {
|
||||
_blocker_list.remove(b);
|
||||
b->unblock();
|
||||
}
|
||||
|
||||
/* reset blocker state */
|
||||
_num_blockers = 0;
|
||||
_woken_up = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
L4_LoadMR(0, _irq_number);
|
||||
L4_AcknowledgeInterrupt(0, 0);
|
||||
}
|
||||
void _ack_irq()
|
||||
{
|
||||
L4_LoadMR(0, _irq_number);
|
||||
L4_AcknowledgeInterrupt(0, 0);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Irq_proxy(long irq_number)
|
||||
:
|
||||
Thread<0x1000>(_construct_name(irq_number)),
|
||||
_startup_lock(Lock::LOCKED), _irq_number(irq_number),
|
||||
_mutex(Lock::UNLOCKED), _num_sharers(0), _num_blockers(0), _woken_up(false)
|
||||
Irq_proxy_component(long irq_number) : Irq_proxy(irq_number)
|
||||
{
|
||||
start();
|
||||
_startup_lock.lock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread interface
|
||||
*/
|
||||
void entry()
|
||||
{
|
||||
if (_associate()) {
|
||||
_startup_lock.unlock();
|
||||
_loop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Block until interrupt occured
|
||||
*/
|
||||
void wait_for_irq()
|
||||
{
|
||||
Irq_blocker blocker;
|
||||
{
|
||||
Lock::Guard lock_guard(_mutex);
|
||||
|
||||
_blocker_list.insert(&blocker);
|
||||
_num_blockers++;
|
||||
|
||||
/*
|
||||
* The proxy thread is woken up if no client woke it up before
|
||||
* and this client is the last aspired blocker.
|
||||
*/
|
||||
if (!_woken_up && _num_blockers == _num_sharers) {
|
||||
_sleep.up();
|
||||
_woken_up = true;
|
||||
}
|
||||
}
|
||||
blocker.block();
|
||||
}
|
||||
|
||||
long irq_number() const { return _irq_number; }
|
||||
|
||||
void add_sharer()
|
||||
{
|
||||
Lock::Guard lock_guard(_mutex);
|
||||
++_num_sharers;
|
||||
_start();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static Irq_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 p;
|
||||
|
||||
/* try to create proxy */
|
||||
if (!irq_alloc || irq_alloc->alloc_addr(1, irq_number) != Range_allocator::ALLOC_OK)
|
||||
return 0;
|
||||
|
||||
Irq_proxy *new_proxy = new (env()->heap()) Irq_proxy(irq_number);
|
||||
proxies.insert(new_proxy);
|
||||
|
||||
return new_proxy;
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
** IRQ session component **
|
||||
***************************/
|
||||
@ -259,7 +120,7 @@ bool Irq_session_component::Irq_control_component::associate_to_irq(unsigned irq
|
||||
void Irq_session_component::wait_for_irq()
|
||||
{
|
||||
/* block at interrupt proxy */
|
||||
Irq_proxy *p = get_irq_proxy(_irq_number);
|
||||
Proxy *p = Proxy::get_irq_proxy<Irq_proxy_component>(_irq_number);
|
||||
if (!p) {
|
||||
PERR("Expected to find IRQ proxy for IRQ %02x", _irq_number);
|
||||
return;
|
||||
@ -293,7 +154,7 @@ Irq_session_component::Irq_session_component(Cap_session *cap_session,
|
||||
}
|
||||
|
||||
/* check if IRQ thread was started before */
|
||||
Irq_proxy *irq_proxy = get_irq_proxy(irq_number, irq_alloc);
|
||||
Proxy *irq_proxy = Proxy::get_irq_proxy<Irq_proxy_component>(irq_number, irq_alloc);
|
||||
if (!irq_proxy) {
|
||||
PERR("unavailable IRQ %lx requested", irq_number);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user