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:
Sebastian Sumpf 2012-10-05 14:46:16 +02:00 committed by Norman Feske
parent 5549f6413b
commit 4a3d852b65
19 changed files with 488 additions and 353 deletions

View File

@ -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) { }

View 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_ */

View File

@ -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:

View File

@ -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

View 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_ */

View File

@ -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!");
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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,

View File

@ -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);

View File

@ -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:

View File

@ -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),

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);