/* * \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. */ /* Genode includes */ #include #include #include /* core includes */ #include #include #include #include /* Fiasco includes */ namespace Fiasco { #include #include #include #include } namespace Genode { class Interrupt_handler; } using namespace Genode; /** * Dispatches interrupts from kernel */ class Genode::Interrupt_handler : public Thread<2048*sizeof(long)> { private: Interrupt_handler() : Thread("irq_handler") { start(); } public: void entry(); static Fiasco::l4_cap_idx_t handler_cap() { static Interrupt_handler handler; return handler._thread_cap.dst(); } }; enum { MAX_MSIS = 256 }; static class Msi_allocator : public Genode::Bit_array { public: Msi_allocator() { 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)) set(0, MAX_MSIS); else if (info.nr_msis < MAX_MSIS) set(info.nr_msis, MAX_MSIS - info.nr_msis); } } msi_alloc; 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; _irq = irq; _trigger = trigger; _polarity = polarity; 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; 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); } /*************************** ** IRQ session component ** ***************************/ 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); 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); Irq_session::Trigger irq_trigger; Irq_session::Polarity irq_polarity; switch(irq_t) { case -1: case Irq_session::TRIGGER_UNCHANGED: irq_trigger = Irq_session::TRIGGER_UNCHANGED; break; case Irq_session::TRIGGER_EDGE: irq_trigger = Irq_session::TRIGGER_EDGE; break; case Irq_session::TRIGGER_LEVEL: irq_trigger = Irq_session::TRIGGER_LEVEL; break; default: throw Root::Unavailable(); } switch(irq_p) { case -1: case POLARITY_UNCHANGED: irq_polarity = POLARITY_UNCHANGED; break; case POLARITY_HIGH: irq_polarity = POLARITY_HIGH; break; case POLARITY_LOW: irq_polarity = POLARITY_LOW; break; default: throw Root::Unavailable(); } _irq_object.associate(_irq_number, msi, irq_trigger, irq_polarity); } Irq_session_component::~Irq_session_component() { if (_irq_number == ~0U) return; 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::ack_irq() { _irq_object.ack_irq(); } void Irq_session_component::sigh(Genode::Signal_context_capability cap) { _irq_object.sigh(cap); } Genode::Irq_session::Info Irq_session_component::info() { 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 = _irq_object.msi_address(), .value = _irq_object.msi_value() }; } /************************************** ** Interrupt handler implementation ** **************************************/ /* Build with frame pointer to make GDB backtraces work. See issue #1061. */ __attribute__((optimize("-fno-omit-frame-pointer"))) __attribute__((noinline)) void Interrupt_handler::entry() { using namespace Fiasco; int err; l4_msgtag_t tag; l4_umword_t label; while (true) { tag = l4_ipc_wait(l4_utcb(), &label, L4_IPC_NEVER); if ((err = l4_ipc_error(tag, l4_utcb()))) PERR("IRQ receive: %d\n", err); else { Irq_object * irq_object = reinterpret_cast(label); irq_object->notify(); } } }