diff --git a/repos/base-codezero/src/core/irq_session_component.cc b/repos/base-codezero/src/core/irq_session_component.cc index 326076e2f..30b892ff3 100644 --- a/repos/base-codezero/src/core/irq_session_component.cc +++ b/repos/base-codezero/src/core/irq_session_component.cc @@ -13,7 +13,6 @@ /* Genode includes */ #include -#include /* core includes */ #include @@ -22,128 +21,109 @@ #include -namespace Genode { - typedef Irq_proxy > Irq_proxy_base; - class Irq_proxy_component; -} - 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; - - protected: - - 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(); - } -}; + /* block for IRQ */ + int ret = l4_irq_control(IRQ_CONTROL_WAIT, 0, _irq); + if (ret < 0) + PWRN("l4_irq_control(IRQ_CONTROL_WAIT) returned %d", ret); +} -/*************************** - ** IRQ session component ** - ***************************/ - - -void Irq_session_component::ack_irq() +void Irq_object::start() { - if (!_proxy) { - PERR("Expected to find IRQ proxy for IRQ %02x", _irq_number); + ::Thread_base::start(); + _sync_bootup.lock(); +} + + +void Irq_object::entry() +{ + if (!_associate()) { + PERR("Could not associate with IRQ 0x%x", _irq); 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, 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); if (msi) throw Root::Unavailable(); - /* check if IRQ thread was started before */ - _proxy = Irq_proxy_component::get_irq_proxy(irq_number, irq_alloc); - if (!_proxy) { - PERR("unavailable IRQ %lx requested", irq_number); + if (!irq_alloc || irq_alloc->alloc_addr(1, _irq_number).is_error()) { + PERR("Unavailable IRQ 0x%x requested", _irq_number); throw Root::Unavailable(); } - _irq_number = irq_number; + _irq_object.start(); } Irq_session_component::~Irq_session_component() { - if (!_proxy) return; - - if (_irq_sigh.valid()) - _proxy->remove_sharer(&_irq_sigh); + PERR("Not yet implemented."); } -void Irq_session_component::sigh(Genode::Signal_context_capability sigh) +void Irq_session_component::ack_irq() { - if (!_proxy) { - PERR("signal handler got not registered - irq thread unavailable"); - return; - } + _irq_object.ack_irq(); +} - 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); +void Irq_session_component::sigh(Genode::Signal_context_capability cap) +{ + _irq_object.sigh(cap); } diff --git a/repos/base-fiasco/src/core/irq_session_component.cc b/repos/base-fiasco/src/core/irq_session_component.cc index 0578f6b00..cce0929ad 100644 --- a/repos/base-fiasco/src/core/irq_session_component.cc +++ b/repos/base-fiasco/src/core/irq_session_component.cc @@ -2,12 +2,10 @@ * \brief Core implementation of IRQ sessions * \author Christian Helmuth * \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 * under the terms of the GNU General Public License version 2. @@ -18,7 +16,6 @@ #include /* core includes */ -#include #include #include @@ -29,155 +26,136 @@ namespace Fiasco { #include } -namespace Genode { - typedef Irq_proxy > Irq_proxy_base; - class Irq_proxy_component; +using namespace Genode; + +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; - - -/** - * Platform-specific proxy code - */ - -class Genode::Irq_proxy_component : public Irq_proxy_base +void Irq_object::_wait_for_irq() { - protected: + using namespace Fiasco; - bool _associate() - { - using namespace Fiasco; + l4_threadid_t irq_tid; + l4_umword_t dw0=0, dw1=0; + l4_msgdope_t result; - int err; - l4_threadid_t irq_tid; - l4_umword_t dw0, dw1; - l4_msgdope_t result; + l4_make_taskid_from_irq(_irq, &irq_tid); - 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 */ - 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); - } - - 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(); - } -}; + if (L4_IPC_IS_ERROR(result)) PERR("Ipc error %lx", L4_IPC_ERROR(result)); + } while (L4_IPC_IS_ERROR(result)); +} -/*************************** - ** IRQ session component ** - ***************************/ - - -void Irq_session_component::ack_irq() +void Irq_object::start() { - if (!_proxy) { - PERR("Expected to find IRQ proxy for IRQ %02x", _irq_number); + ::Thread_base::start(); + _sync_bootup.lock(); +} + + +void Irq_object::entry() +{ + if (!_associate()) { + PERR("Could not associate with IRQ 0x%x", _irq); 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, 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); if (msi) throw Root::Unavailable(); - /* check if IRQ thread was started before */ - _proxy = Irq_proxy_component::get_irq_proxy(irq_number, irq_alloc); - if (!_proxy) { - PERR("unavailable IRQ %lx requested", irq_number); + if (!irq_alloc || irq_alloc->alloc_addr(1, _irq_number).is_error()) { + PERR("Unavailable IRQ 0x%x requested", _irq_number); throw Root::Unavailable(); } - _irq_number = irq_number; + _irq_object.start(); } Irq_session_component::~Irq_session_component() { - if (!_proxy) return; - - if (_irq_sigh.valid()) - _proxy->remove_sharer(&_irq_sigh); + PERR("Not yet implemented."); } -void Irq_session_component::sigh(Genode::Signal_context_capability sigh) +void Irq_session_component::ack_irq() { - if (!_proxy) { - PERR("signal handler got not registered - irq thread unavailable"); - return; - } + _irq_object.ack_irq(); +} - 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); +void Irq_session_component::sigh(Genode::Signal_context_capability cap) +{ + _irq_object.sigh(cap); } diff --git a/repos/base-foc/src/core/include/irq_object.h b/repos/base-foc/src/core/include/irq_object.h new file mode 100644 index 000000000..774539159 --- /dev/null +++ b/repos/base-foc/src/core/include/irq_object.h @@ -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 +#include +#include + +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(); +}; diff --git a/repos/base-foc/src/core/irq_session_component.cc b/repos/base-foc/src/core/irq_session_component.cc index 111954385..bd016bd38 100644 --- a/repos/base-foc/src/core/irq_session_component.cc +++ b/repos/base-foc/src/core/irq_session_component.cc @@ -4,12 +4,10 @@ * \author Stefan Kalkowski * \author Sebastian Sumpf * \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 * under the terms of the GNU General Public License version 2. @@ -18,6 +16,7 @@ /* Genode includes */ #include #include +#include /* core includes */ #include @@ -33,14 +32,14 @@ namespace Fiasco { #include } -using namespace Genode; - namespace Genode { class Interrupt_handler; - class Irq_proxy_component; - typedef Irq_proxy > Irq_proxy_base; } + +using namespace Genode; + + /** * Dispatches interrupts from kernel */ @@ -59,126 +58,121 @@ class Genode::Interrupt_handler : public Thread<4096> static Interrupt_handler handler; return handler._thread_cap.dst(); } + }; -/** - * Irq_proxy interface implementation - */ -class Genode::Irq_proxy_component : public Irq_proxy_base +enum { MAX_MSIS = 256 }; +static class Msi_allocator : public Genode::Bit_array { - 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: - Irq_proxy_component(long irq_number) - : - 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) - { } + Msi_allocator() { + using namespace Fiasco; - 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; } - Irq_session::Polarity polarity() const { return _polarity; } - - void setup_irq_mode(Irq_session::Trigger t, Irq_session::Polarity p) - { - _trigger = t; - _polarity = p; - - /* set interrupt mode */ - Platform::setup_irq_mode(_irq_number, _trigger, _polarity); + if (l4_error(res) || !(info.features & L4_ICU_FLAG_MSI)) + set(0, MAX_MSIS); + else + if (info.nr_msis < MAX_MSIS) + set(info.nr_msis, MAX_MSIS - info.nr_msis); } +} msi_alloc; - Genode::addr_t msi_address() const { return _msi_addr; } - Genode::addr_t msi_value() const { return _msi_data; } +bool Genode::Irq_object::associate(unsigned irq, bool msi, + 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) - { - if (_running) - return; + _irq = irq; + _trigger = trigger; + _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; + using namespace Fiasco; + if (l4_error(l4_factory_create_irq(L4_BASE_FACTORY_CAP, + _capability()))) { + PERR("l4_factory_create_irq failed!"); + return false; + } - _running = true; - _start(); - } -}; + unsigned long gsi = _irq; + 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(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, const char *args) +: + _irq_number(~0U), _irq_alloc(irq_alloc) { long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1); - if (irq_number < 0) { - PERR("invalid IRQ number requested %ld", irq_number); - throw Root::Unavailable(); + long msi = Arg_string::find_arg(args, "device_config_phys").long_value(0); + if (msi) { + 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_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(); } - long msi = Arg_string::find_arg(args, "device_config_phys").long_value(0); - 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_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_object.associate(_irq_number, msi, irq_trigger, irq_polarity); } Irq_session_component::~Irq_session_component() { - if (!_proxy) return; + if (_irq_number == ~0U) + return; - if (_irq_sigh.valid()) - _proxy->remove_sharer(&_irq_sigh); + if (_irq_object.msi_address()) { + 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) { - 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); + _irq_object.sigh(cap); } 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::MSI, - .address = _proxy->msi_address(), - .value = _proxy->msi_value() + .address = _irq_object.msi_address(), + .value = _irq_object.msi_value() }; } -/*************************************** - ** Interrupt handler implemtentation ** - ***************************************/ +/************************************** + ** Interrupt handler implementation ** + **************************************/ /* Build with frame pointer to make GDB backtraces work. See issue #1061. */ __attribute__((optimize("-fno-omit-frame-pointer"))) @@ -367,11 +298,8 @@ void Interrupt_handler::entry() if ((err = l4_ipc_error(tag, l4_utcb()))) PERR("IRQ receive: %d\n", err); else { - Irq_proxy_component *proxy; - proxy = Irq_proxy_component::get_irq_proxy(label); - - if (proxy) - proxy->semaphore()->up(); + Irq_object * irq_object = reinterpret_cast(label); + irq_object->notify(); } } } diff --git a/repos/base-foc/src/core/platform.cc b/repos/base-foc/src/core/platform.cc index c091816cc..4a562e283 100644 --- a/repos/base-foc/src/core/platform.cc +++ b/repos/base-foc/src/core/platform.cc @@ -30,6 +30,7 @@ /* Fiasco includes */ namespace Fiasco { #include +#include #include #include #include @@ -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() diff --git a/repos/base-hw/src/core/irq_session_component.cc b/repos/base-hw/src/core/irq_session_component.cc index f50105d43..e99297216 100644 --- a/repos/base-hw/src/core/irq_session_component.cc +++ b/repos/base-hw/src/core/irq_session_component.cc @@ -69,6 +69,10 @@ Irq_session_component::Irq_session_component(Range_allocator * const irq_alloc, _irq_number(Platform::irq(_find_irq_number(args))), _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 */ if (_irq_alloc->alloc_addr(1, _irq_number).is_error()) { PERR("unavailable interrupt requested"); diff --git a/repos/base-nova/src/core/include/irq_object.h b/repos/base-nova/src/core/include/irq_object.h new file mode 100644 index 000000000..057696cb1 --- /dev/null +++ b/repos/base-nova/src/core/include/irq_object.h @@ -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 + +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); +}; diff --git a/repos/base-nova/src/core/irq_session_component.cc b/repos/base-nova/src/core/irq_session_component.cc index 6bdc406c8..bb3ee2d8c 100644 --- a/repos/base-nova/src/core/irq_session_component.cc +++ b/repos/base-nova/src/core/irq_session_component.cc @@ -2,11 +2,12 @@ * \brief Implementation of IRQ session component * \author Norman Feske * \author Sebastian Sumpf + * \author Alexander Boettcher * \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 * under the terms of the GNU General Public License version 2. @@ -18,7 +19,6 @@ /* core includes */ #include -#include #include #include @@ -29,201 +29,214 @@ 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(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(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) }; - enum { WEIGHT = Cpu_session::DEFAULT_WEIGHT }; + if (!ok) + throw Root::Unavailable(); - static void _thread_start() - { - Thread_base::myself()->entry(); - sleep_forever(); - } + /* start thread having a SC */ + using namespace Nova; + addr_t pd_sel = Platform_pd::pd_core_sel(); + addr_t utcb = reinterpret_cast(&_context->utcb); - public: + /* put IP on stack, it will be read from core pager in platform.cc */ + addr_t *sp = reinterpret_cast(_context->stack_top() - sizeof(addr_t)); + *sp = reinterpret_cast(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(); + } - /** - * 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(&_context->utcb); + /* remap startup portal from main thread */ + if (map_local((Utcb *)Thread_base::myself()->utcb(), + Obj_crd(PT_SEL_STARTUP, 0), + Obj_crd(_tid.exc_pt_sel + PT_SEL_STARTUP, 0))) { + PERR("could not create startup portal"); + throw Root::Unavailable(); + } - /* - * Put IP on stack, it will be read from core pager in platform.cc - */ - addr_t *sp = reinterpret_cast(_context->stack_top() - sizeof(addr_t)); - *sp = reinterpret_cast(_thread_start); + /* remap debugging page fault portal for core threads */ + 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 Root::Unavailable(); + } - /* 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 Cpu_session::Thread_creation_failed(); - } + /* default: we don't accept any mappings or translations */ + Utcb * utcb_obj = reinterpret_cast(Thread_base::utcb()); + utcb_obj->crd_rcv = Obj_crd(); + utcb_obj->crd_xlt = Obj_crd(); - /* remap startup portal from main thread */ - if (map_local((Utcb *)Thread_base::myself()->utcb(), - Obj_crd(PT_SEL_STARTUP, 0), - Obj_crd(_tid.exc_pt_sel + PT_SEL_STARTUP, 0))) { - PERR("could not create startup portal"); - throw Cpu_session::Thread_creation_failed(); - } + /* create SC */ + Qpd qpd(Qpd::DEFAULT_QUANTUM, Qpd::DEFAULT_PRIORITY + 1); + 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 Root::Unavailable(); + } - /* remap debugging page fault portal for core threads */ - 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(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(); - } - } -}; + _sync_life.lock(); +} -/** - * Irq_proxy interface implementation - */ -class Genode::Irq_proxy_component : public Irq_proxy +void Irq_object::entry() { - private: + /* signal that thread is up and ready */ + _sync_life.unlock(); - Genode::addr_t _irq_sel; /* IRQ cap selector */ - Genode::addr_t _dev_mem; /* used when MSI or HPET is used */ - bool _ready; /* flag to signal that IRQ SM can be used */ + /* wait for first ack_irq */ + _sync_ack.lock(); - Genode::addr_t _msi_addr; - Genode::addr_t _msi_data; + while (true) { - 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() - { - /* assign IRQ to CPU && request msi data to be used by driver */ - uint8_t res = Nova::assign_gsi(_irq_sel, _dev_mem, boot_cpu(), - _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; + if (_state == SHUTDOWN) { + /* signal end of life to entrypoint thread */ + _sync_life.unlock(); + while (1) nova_die(); } - void _wait_for_irq() - { - if (!_ready) - PERR("Error: assign_gsi failed for IRQ %ld", _irq_number); + if (!_sig_cap.valid()) + continue; - if (Nova::NOVA_OK != Nova::sm_ctrl(_irq_sel, Nova::SEMAPHORE_DOWN)) - PERR("Error: blocking for irq %ld failed", _irq_number); - } + notify(); - void _ack_irq() { } + _sync_ack.lock(); + } +} - public: - Irq_proxy_component(long irq_number) - : - /* since we run in APIC mode translate IRQ 0 (PIT) to 2 */ - Irq_proxy(irq_number ? irq_number : 2), - _irq_sel(cap_map()->insert()), - _dev_mem(0), - _ready(false), - _msi_addr(0), - _msi_data(0) - { - /* map IRQ SM cap from kernel to core at _irq_sel selector */ - using Nova::Obj_crd; +Irq_object::Irq_object() +: + Thread<4096>("irq"), + _sync_ack(Lock::LOCKED), _sync_life(Lock::LOCKED), + _kernel_caps(cap_map()->insert(KERNEL_CAP_COUNT_LOG2)), + _msi_addr(0UL), _msi_data(0UL), _state(UNDEFINED) +{ } - 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(), - src, dst, MAP_FROM_KERNEL_TO_CORE); - if (ret) { - PERR("Could not map IRQ %ld", _irq_number); - throw Root::Unavailable(); - } +Irq_object::~Irq_object() +{ + /* tell interrupt thread to get in a defined dead state */ + _state = SHUTDOWN; + /* 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 */ - _start(); - } + /* revoke SC and SM of interrupt source */ + 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(v); - - if (!virt_addr) - return; - - using Nova::Mem_crd; - using Nova::Rights; - - if (map_local_phys_to_virt(reinterpret_cast(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; } -}; +/*************************** + ** IRQ session component ** + ***************************/ 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, const char *args) +: + _irq_number(~0U), _irq_alloc(irq_alloc) { - typedef Irq_proxy Proxy; - 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); if (device_phys) { + if (irq_number >= kernel_hip()->sel_gsi) throw Root::Unavailable(); @@ -275,58 +268,47 @@ Irq_session_component::Irq_session_component(Range_allocator *irq_alloc, throw Root::Unavailable(); } - /* check if IRQ thread was started before */ - _proxy = Proxy::get_irq_proxy(irq_number, irq_alloc); - if (!_proxy) { - PERR("unavailable IRQ %lx requested", irq_number); + if (!irq_alloc || irq_alloc->alloc_addr(1, irq_number).is_error()) { + PERR("Unavailable IRQ 0x%lx requested", irq_number); throw Root::Unavailable(); } - if (device_phys) - _proxy->associate(device_phys); - _irq_number = irq_number; + + _irq_object.start(_irq_number, device_phys); } Irq_session_component::~Irq_session_component() { - if (!_proxy) + if (_irq_number == ~0U) return; - if (_irq_sigh.valid()) - _proxy->remove_sharer(&_irq_sigh); + 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() { - if (!_proxy) { - PERR("signal handler got not registered - irq thread unavailable"); - return; - } + _irq_object.ack_irq(); +} - 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); +void Irq_session_component::sigh(Genode::Signal_context_capability cap) +{ + _irq_object.sigh(cap); } Genode::Irq_session::Info Irq_session_component::info() { - if (!_proxy || !_proxy->ready() || !_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::MSI, - .address = _proxy->msi_address(), - .value = _proxy->msi_value() + .address = _irq_object.msi_address(), + .value = _irq_object.msi_value() }; } diff --git a/repos/base-okl4/src/core/irq_session_component.cc b/repos/base-okl4/src/core/irq_session_component.cc index 90fb96f3f..840901a42 100644 --- a/repos/base-okl4/src/core/irq_session_component.cc +++ b/repos/base-okl4/src/core/irq_session_component.cc @@ -17,6 +17,7 @@ #include /* core includes */ +#include #include /* OKL4 includes */ @@ -32,6 +33,10 @@ using namespace Okl4; 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 */ 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; } -namespace Genode { - typedef Irq_proxy > Irq_proxy_base; - class Irq_proxy_component; + +bool Irq_object::_associate() +{ + /* 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 - */ -class Genode::Irq_proxy_component : public Irq_proxy_base + +void Irq_object::_wait_for_irq() { - protected: + /* prepare ourself to receive asynchronous IRQ notifications */ + L4_Set_NotifyMask(1 << IRQ_NOTIFY_BIT); + L4_Accept(L4_NotifyMsgAcceptor); - bool _associate() - { - /* allow roottask (ourself) to handle the interrupt */ - 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; - } + /* wait for asynchronous interrupt notification */ + L4_ThreadId_t partner = L4_nilthread; + L4_ReplyWait(partner, &partner); +} - /* bit to use for IRQ notifications */ - enum { IRQ_NOTIFY_BIT = 13 }; - /* - * Note: 'L4_Myself()' does not work for the thread argument of - * 'L4_RegisterInterrupt'. We have to specify our global ID. - */ - 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; - } +void Irq_object::start() +{ + ::Thread_base::start(); + _sync_bootup.lock(); +} - /* 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() - { - L4_Accept(L4_NotifyMsgAcceptor); + /* thread is up and ready */ + _sync_bootup.unlock(); - /* wait for asynchronous interrupt notification */ - L4_ThreadId_t partner = L4_nilthread; - L4_ReplyWait(partner, &partner); - } + /* wait for first ack_irq */ + _sync_ack.lock(); - void _ack_irq() - { - L4_LoadMR(0, _irq_number); - L4_AcknowledgeInterrupt(0, 0); - } + while (true) { - public: + L4_LoadMR(0, _irq); + L4_AcknowledgeInterrupt(0, 0); - Irq_proxy_component(long irq_number) : Irq_proxy(irq_number) - { - _start(); - } -}; + _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) +{ } /*************************** @@ -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, 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); if (msi) throw Root::Unavailable(); - /* check if IRQ thread was started before */ - _proxy = Irq_proxy_component::get_irq_proxy(irq_number, irq_alloc); - if (!_proxy) { - PERR("unavailable IRQ %lx requested", irq_number); + if (!irq_alloc || irq_alloc->alloc_addr(1, _irq_number).is_error()) { + PERR("Unavailable IRQ 0x%x requested", _irq_number); throw Root::Unavailable(); } - _irq_number = irq_number; + _irq_object.start(); } Irq_session_component::~Irq_session_component() { - if (!_proxy) return; - - if (_irq_sigh.valid()) - _proxy->remove_sharer(&_irq_sigh); + PDBG("Not yet implemented!"); } -void Irq_session_component::sigh(Genode::Signal_context_capability sigh) +void Irq_session_component::ack_irq() { - if (!_proxy) { - PERR("signal handler got not registered - irq thread unavailable"); - return; - } + _irq_object.ack_irq(); +} - 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); +void Irq_session_component::sigh(Genode::Signal_context_capability cap) +{ + _irq_object.sigh(cap); } diff --git a/repos/base-pistachio/src/core/irq_session_component.cc b/repos/base-pistachio/src/core/irq_session_component.cc index 8cdc5bf2e..e4e40e200 100644 --- a/repos/base-pistachio/src/core/irq_session_component.cc +++ b/repos/base-pistachio/src/core/irq_session_component.cc @@ -2,12 +2,10 @@ * \brief Pistachio-specific implementation of IRQ sessions * \author Julian Stecklina * \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 * under the terms of the GNU General Public License version 2. @@ -18,7 +16,6 @@ #include /* core includes */ -#include #include #include @@ -40,97 +37,88 @@ static inline L4_ThreadId_t irqno_to_threadid(unsigned int irqno) } -namespace Genode { - typedef Irq_proxy > Irq_proxy_base; - class Irq_proxy_component; +bool Irq_object::_associate() +{ + L4_ThreadId_t const irq_thread = irqno_to_threadid(_irq); + + return L4_AssociateInterrupt(irq_thread, L4_Myself()); } -/** - * Platform-specific proxy code - */ - -class Genode::Irq_proxy_component : public Irq_proxy_base +void Irq_object::_wait_for_irq() { - private: + L4_ThreadId_t const irq_thread = irqno_to_threadid(_irq); - /* - * On Pistachio, an IRQ is unmasked right after attaching. - * Hence, the kernel may send an IRQ IPC when the IRQ hander is - * 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 */ + /* send unmask message and wait for new IRQ */ + L4_Set_MsgTag(L4_Niltag); + L4_MsgTag_t res = L4_Call(irq_thread); - protected: + if (L4_IpcFailed(res)) + PERR("ipc error while waiting for interrupt."); +} - bool _associate() { return true; } - void _wait_for_irq() - { - L4_ThreadId_t irq_thread = irqno_to_threadid(_irq_number); +void Irq_object::start() +{ + ::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) { - PERR("L4_AssociateInterrupt failed"); - return; - } +void Irq_object::entry() +{ + if (!_associate()) { + PERR("Could not associate with IRQ 0x%x", _irq); + return; + } - /* - * 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_Set_MsgTag(L4_Niltag); - res = L4_Receive(irq_thread); + /* thread is up and ready */ + _sync_bootup.unlock(); - /* - * Now, the IRQ is masked. To receive the next IRQ we have to send - * an unmask message to the IRQ thread first. - */ - _irq_attached = true; + /* wait for first ack_irq */ + _sync_ack.lock(); - /* 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 */ - L4_Set_MsgTag(L4_Niltag); - res = L4_Call(irq_thread); - } + if (L4_IpcFailed(res)) + PERR("ipc error while attaching to interrupt."); - if (L4_IpcFailed(res)) { - PERR("ipc error while waiting for interrupt."); - return; - } - } + /* + * Now, the IRQ is masked. To receive the next IRQ we have to send + * 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) - : - Irq_proxy(irq_number), - _irq_attached(false) - { - _start(); - } + if (!_sig_cap.valid()) + continue; - ~Irq_proxy_component() - { - L4_ThreadId_t const thread_id = irqno_to_threadid(_irq_number); - L4_Word_t const res = L4_DeassociateInterrupt(thread_id); + Genode::Signal_transmitter(_sig_cap).submit(1); - if (res != 1) - PERR("L4_DeassociateInterrupt failed"); - } -}; + _sync_ack.lock(); + } +} + + +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, 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); if (msi) throw Root::Unavailable(); - /* check if IRQ thread was started before */ - _proxy = Irq_proxy_component::get_irq_proxy(irq_number, irq_alloc); - if (!_proxy) { - PERR("unavailable IRQ %lx requested", irq_number); + if (!irq_alloc || irq_alloc->alloc_addr(1, _irq_number).is_error()) { + PERR("Unavailable IRQ 0x%x requested", _irq_number); throw Root::Unavailable(); } - _irq_number = irq_number; + _irq_object.start(); } Irq_session_component::~Irq_session_component() { - if (!_proxy) return; + L4_Word_t res = L4_DeassociateInterrupt(irqno_to_threadid(_irq_number)); - if (_irq_sigh.valid()) - _proxy->remove_sharer(&_irq_sigh); + if (res != 1) + PERR("L4_DeassociateInterrupt failed"); } -void Irq_session_component::sigh(Genode::Signal_context_capability sigh) +void Irq_session_component::ack_irq() { - if (!_proxy) { - PERR("signal handler got not registered - irq thread unavailable"); - return; - } + _irq_object.ack_irq(); +} - 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); +void Irq_session_component::sigh(Genode::Signal_context_capability cap) +{ + _irq_object.sigh(cap); } diff --git a/repos/base/src/core/include/irq_object.h b/repos/base/src/core/include/irq_object.h new file mode 100644 index 000000000..02733c126 --- /dev/null +++ b/repos/base/src/core/include/irq_object.h @@ -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 + +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; +}; diff --git a/repos/base/src/core/include/irq_proxy.h b/repos/base/src/core/include/irq_proxy.h deleted file mode 100644 index 463957948..000000000 --- a/repos/base/src/core/include/irq_proxy.h +++ /dev/null @@ -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 - - -namespace Genode { - class Irq_sigh; - template class Irq_proxy; -} - - -class Genode::Irq_sigh : public Genode::Signal_context_capability, - public Genode::List::Element -{ - public: - - inline Irq_sigh * operator= (const Signal_context_capability &cap) - { - Signal_context_capability::operator=(cap); - return this; - } - - Irq_sigh() { } - - void notify() { Genode::Signal_transmitter(*this).submit(1); } -}; - - -/* - * Proxy thread that associates to the interrupt and unblocks waiting irqctrl - * threads. - * - * XXX resources are not accounted as the interrupt is shared - */ -template -class Genode::Irq_proxy : public THREAD, - public Genode::List >::Element -{ - protected: - - char _name[32]; - Lock _startup_lock; - - long _irq_number; - - Lock _mutex; /* protects this object */ - int _num_sharers; /* number of clients sharing this IRQ */ - Semaphore _sleep; /* wake me up if aspired blockers return */ - List _sigh_list; - int _num_acknowledgers; /* number of currently blocked clients */ - bool _woken_up; /* client decided to wake me up - - this prevents multiple wakeups - to happen during initialization */ - - - /*************** - ** Interface ** - ***************/ - - /** - * Request interrupt - * - * \return true on success - */ - virtual bool _associate() = 0; - - /** - * Wait for associated interrupt - */ - virtual void _wait_for_irq() = 0; - - /** - * Acknowledge interrupt - */ - virtual void _ack_irq() = 0; - - /******************** - ** Implementation ** - ********************/ - - const char *_construct_name(long irq_number) - { - snprintf(_name, sizeof(_name), "irqproxy%02lx", irq_number); - return _name; - } - - void _loop() - { - /* wait for first blocker */ - _sleep.down(); - - while (1) { - _wait_for_irq(); - - /* notify all */ - notify_about_irq(1); - - /* - * We must wait for all clients to ack their interrupt, - * otherwise level-triggered interrupts will occur immediately - * after acknowledgement. That's an inherent security problem - * with shared IRQs and induces problems with dynamic driver - * load and unload. - */ - _sleep.down(); - - /* acknowledge previous interrupt */ - _ack_irq(); - } - } - - /** - * Start this thread, should be called externally from derived class - */ - virtual void _start() - { - THREAD::start(); - _startup_lock.lock(); - } - - public: - - Irq_proxy(long irq_number) - : - THREAD(_construct_name(irq_number)), - _startup_lock(Lock::LOCKED), _irq_number(irq_number), - _mutex(Lock::UNLOCKED), _num_sharers(0), _num_acknowledgers(0), _woken_up(false) - - { } - - /** - * Thread interface - */ - void entry() - { - bool const associate_suceeded = _associate(); - - _startup_lock.unlock(); - - if (associate_suceeded) - _loop(); - } - - /** - * Acknowledgements of clients - */ - virtual bool ack_irq() - { - Lock::Guard lock_guard(_mutex); - - _num_acknowledgers++; - - /* - * The proxy thread has to be woken up if no client woke it up - * before and this client is the last aspired acknowledger. - */ - if (!_woken_up && _num_acknowledgers == _num_sharers) { - _sleep.up(); - _woken_up = true; - } - - return _woken_up; - } - - /** - * Notify all clients about irq - */ - void notify_about_irq(unsigned) - { - Lock::Guard lock_guard(_mutex); - - /* reset acknowledger state */ - _num_acknowledgers = 0; - _woken_up = false; - - /* inform blocked clients */ - for (Irq_sigh * s = _sigh_list.first(); s ; s = s->next()) - s->notify(); - } - - long irq_number() const { return _irq_number; } - - virtual bool add_sharer(Irq_sigh *s) - { - Lock::Guard lock_guard(_mutex); - - ++_num_sharers; - _sigh_list.insert(s); - - return true; - } - - virtual 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 - static PROXY *get_irq_proxy(long irq_number, Range_allocator *irq_alloc = 0) - { - static List proxies; - static Lock proxies_lock; - - Lock::Guard lock_guard(proxies_lock); - - /* lookup proxy in database */ - for (Irq_proxy *p = proxies.first(); p; p = p->next()) - if (p->irq_number() == irq_number) - return static_cast(p); - - /* try to create proxy */ - if (!irq_alloc || irq_alloc->alloc_addr(1, irq_number).is_error()) - return 0; - - PROXY *new_proxy = new (env()->heap()) PROXY(irq_number); - proxies.insert(new_proxy); - return new_proxy; - } -}; - -#endif /* _CORE__INCLUDE__IRQ_PROXY_H_ */ diff --git a/repos/base/src/core/include/irq_session_component.h b/repos/base/src/core/include/irq_session_component.h index f65024832..a69d2e0a7 100644 --- a/repos/base/src/core/include/irq_session_component.h +++ b/repos/base/src/core/include/irq_session_component.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 * under the terms of the GNU General Public License version 2. */ -#ifndef _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ -#define _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ +#pragma once +#include #include #include #include #include #include -#include -namespace Genode { - class Irq_proxy_component; - class Irq_session_component; -} +#include + +namespace Genode { class Irq_session_component; } class Genode::Irq_session_component : public Rpc_object, public List::Element { private: - unsigned _irq_number; - Range_allocator *_irq_alloc; - Irq_proxy_component *_proxy; - - Irq_sigh _irq_sigh; + unsigned _irq_number; + Range_allocator *_irq_alloc; + Irq_object _irq_object; public: @@ -45,14 +41,14 @@ class Genode::Irq_session_component : public Rpc_object, * \param irq_alloc platform-dependent IRQ allocator * \param args session construction arguments */ - Irq_session_component(Range_allocator *irq_alloc, - const char *args); + Irq_session_component(Range_allocator *irq_alloc, const char *args); /** * Destructor */ ~Irq_session_component(); + /*************************** ** Irq session interface ** ***************************/ @@ -61,5 +57,3 @@ class Genode::Irq_session_component : public Rpc_object, void sigh(Signal_context_capability) override; Info info() override; }; - -#endif /* _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ */