diff --git a/repos/base-codezero/src/core/irq_session_component.cc b/repos/base-codezero/src/core/irq_session_component.cc index 08e40ad04..326076e2f 100644 --- a/repos/base-codezero/src/core/irq_session_component.cc +++ b/repos/base-codezero/src/core/irq_session_component.cc @@ -101,10 +101,13 @@ Irq_session_component::Irq_session_component(Range_allocator *irq_alloc, 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) { @@ -142,3 +145,10 @@ void Irq_session_component::sigh(Genode::Signal_context_capability sigh) if (!old.valid() && sigh.valid()) _proxy->add_sharer(&_irq_sigh); } + + +Genode::Irq_session::Info Irq_session_component::info() +{ + /* no MSI support */ + return { .type = Genode::Irq_session::Info::Type::INVALID }; +} diff --git a/repos/base-fiasco/src/core/irq_session_component.cc b/repos/base-fiasco/src/core/irq_session_component.cc index a2d649ca1..0578f6b00 100644 --- a/repos/base-fiasco/src/core/irq_session_component.cc +++ b/repos/base-fiasco/src/core/irq_session_component.cc @@ -135,10 +135,13 @@ Irq_session_component::Irq_session_component(Range_allocator *irq_alloc, 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) { @@ -176,3 +179,10 @@ void Irq_session_component::sigh(Genode::Signal_context_capability sigh) if (!old.valid() && sigh.valid()) _proxy->add_sharer(&_irq_sigh); } + + +Genode::Irq_session::Info Irq_session_component::info() +{ + /* no MSI support */ + return { .type = Genode::Irq_session::Info::Type::INVALID }; +} diff --git a/repos/base-foc/src/core/irq_session_component.cc b/repos/base-foc/src/core/irq_session_component.cc index 2125f218e..111954385 100644 --- a/repos/base-foc/src/core/irq_session_component.cc +++ b/repos/base-foc/src/core/irq_session_component.cc @@ -74,6 +74,10 @@ class Genode::Irq_proxy_component : public Irq_proxy_base 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: @@ -81,18 +85,24 @@ class Genode::Irq_proxy_component : public Irq_proxy_base bool _associate() { using namespace Fiasco; - if (l4_error(l4_factory_create_irq(L4_BASE_FACTORY_CAP, _capability()))) { + 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()))) { + 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; } - /* set interrupt mode */ - Platform::setup_irq_mode(_irq_number, _trigger, _polarity); + 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()))) { @@ -100,6 +110,12 @@ class Genode::Irq_proxy_component : public Irq_proxy_base 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; } @@ -124,10 +140,9 @@ class Genode::Irq_proxy_component : public Irq_proxy_base 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) - { - _start(); - } + _polarity(Irq_session::POLARITY_UNCHANGED), + _running(false), _msi_addr(0), _msi_data(0) + { } Semaphore *semaphore() { return &_sem; } @@ -142,6 +157,27 @@ class Genode::Irq_proxy_component : public Irq_proxy_base /* set interrupt mode */ Platform::setup_irq_mode(_irq_number, _trigger, _polarity); } + + Genode::addr_t msi_address() const { return _msi_addr; } + Genode::addr_t msi_value() const { return _msi_data; } + + void enable(bool msi) + { + if (_running) + return; + + 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; + + _running = true; + _start(); + } }; @@ -165,8 +201,8 @@ Irq_session_component::Irq_session_component(Range_allocator *irq_alloc, const char *args) { long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1); - if (irq_number == -1) { - PERR("invalid IRQ number requested"); + if (irq_number < 0) { + PERR("invalid IRQ number requested %ld", irq_number); throw Root::Unavailable(); } @@ -206,12 +242,27 @@ Irq_session_component::Irq_session_component(Range_allocator *irq_alloc, throw Root::Unavailable(); } - /* - * temporary hack for fiasco.oc using the local-apic, - * where old pic-line 0 maps to 2 - */ - if (irq_number == 0) - irq_number = 2; + 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); @@ -222,7 +273,6 @@ Irq_session_component::Irq_session_component(Range_allocator *irq_alloc, bool setup = false; bool fail = false; - /* sanity check */ if (irq_trigger != TRIGGER_UNCHANGED && _proxy->trigger() != irq_trigger) { if (_proxy->trigger() == TRIGGER_UNCHANGED) @@ -251,6 +301,8 @@ Irq_session_component::Irq_session_component(Range_allocator *irq_alloc, _proxy->setup_irq_mode(irq_trigger, irq_polarity); _irq_number = irq_number; + + _proxy->enable(msi); } @@ -282,6 +334,19 @@ void Irq_session_component::sigh(Genode::Signal_context_capability sigh) } +Genode::Irq_session::Info Irq_session_component::info() +{ + if (!_proxy || !_proxy->msi_address() || !_proxy->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() + }; +} + + /*************************************** ** Interrupt handler implemtentation ** ***************************************/ diff --git a/repos/base-hw/src/core/include/irq_session_component.h b/repos/base-hw/src/core/include/irq_session_component.h index ddd36bb2b..ce0ed8297 100644 --- a/repos/base-hw/src/core/include/irq_session_component.h +++ b/repos/base-hw/src/core/include/irq_session_component.h @@ -56,8 +56,10 @@ class Genode::Irq_session_component : public Rpc_object, ** Irq session interface ** ***************************/ - void ack_irq(); - void sigh(Signal_context_capability) override; + void ack_irq() override; + void sigh(Signal_context_capability) override; + Info info() override { + return { .type = Genode::Irq_session::Info::Type::INVALID }; } }; #endif /* _INCLUDE__IRQ_SESSION_COMPONENT_H_ */ diff --git a/repos/base-linux/src/core/include/irq_session_component.h b/repos/base-linux/src/core/include/irq_session_component.h index f45e54ec9..c7f5c00f1 100644 --- a/repos/base-linux/src/core/include/irq_session_component.h +++ b/repos/base-linux/src/core/include/irq_session_component.h @@ -45,8 +45,10 @@ class Genode::Irq_session_component : public Rpc_object, ** Irq session interface ** ***************************/ - void ack_irq() { } - void sigh(Signal_context_capability) override { } + void ack_irq() override { } + void sigh(Signal_context_capability) override { } + Info info() override { + return { .type = Genode::Irq_session::Info::Type::INVALID }; } }; #endif /* _CORE__INCLUDE__LINUX__IRQ_SESSION_COMPONENT_H_ */ diff --git a/repos/base-nova/src/core/irq_session_component.cc b/repos/base-nova/src/core/irq_session_component.cc index 908803c45..6bdc406c8 100644 --- a/repos/base-nova/src/core/irq_session_component.cc +++ b/repos/base-nova/src/core/irq_session_component.cc @@ -120,45 +120,39 @@ class Genode::Irq_proxy_component : public Irq_proxy 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 */ + + Genode::addr_t _msi_addr; + Genode::addr_t _msi_data; protected: bool _associate() { - /* alloc slector where IRQ will be mapped */ - _irq_sel = cap_map()->insert(); - - /* 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 */ - addr_t msi_addr = 0; - addr_t msi_data = 0; + /* 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); - if (res != Nova::NOVA_OK) - PERR("Error: assign_pci failed -irq:dev:msi_addr:msi_data " - "%lx:%lx:%lx:%lx", _irq_number, _dev_mem, msi_addr, - msi_data); + _msi_addr, _msi_data); - return res == Nova::NOVA_OK; + _ready = res == Nova::NOVA_OK; + + /* + * Return ever success so that the IRQ proxy thread gets started. + * For MSIs or HPET a separate associate() call with a valid + * dev_mem address is required. + */ + if (_dev_mem && !_ready) + PERR("setting up MSI 0x%lx failed - error %u", _irq_number, res); + + return _dev_mem ? _ready : true; } void _wait_for_irq() { - if (Nova::sm_ctrl(_irq_sel, Nova::SEMAPHORE_DOWN)) - nova_die(); + if (!_ready) + PERR("Error: assign_gsi failed for IRQ %ld", _irq_number); + + if (Nova::NOVA_OK != Nova::sm_ctrl(_irq_sel, Nova::SEMAPHORE_DOWN)) + PERR("Error: blocking for irq %ld failed", _irq_number); } void _ack_irq() { } @@ -167,13 +161,80 @@ class Genode::Irq_proxy_component : public Irq_proxy Irq_proxy_component(long irq_number) : - Irq_proxy(irq_number), _dev_mem(0) + /* 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; + + 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(); + } + + /* let thread run */ _start(); } + + 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; } }; +static Nova::Hip * kernel_hip() +{ + /** + * Initial value of esp register, saved by the crt0 startup code. + * This value contains the address of the hypervisor information page. + */ + extern addr_t __initial_sp; + return reinterpret_cast(__initial_sp); +} /*************************** @@ -195,20 +256,35 @@ void Irq_session_component::ack_irq() Irq_session_component::Irq_session_component(Range_allocator *irq_alloc, const char *args) { + typedef Irq_proxy Proxy; + long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1); - if (irq_number == -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(); + + irq_number = kernel_hip()->sel_gsi - 1 - irq_number; + /* XXX last GSI number unknown - assume 40 GSIs (depends on IO-APIC) */ + if (irq_number < 40) + throw Root::Unavailable(); + } + /* check if IRQ thread was started before */ - typedef Irq_proxy Proxy; _proxy = Proxy::get_irq_proxy(irq_number, irq_alloc); if (!_proxy) { PERR("unavailable IRQ %lx requested", irq_number); throw Root::Unavailable(); } + if (device_phys) + _proxy->associate(device_phys); + _irq_number = irq_number; } @@ -240,3 +316,17 @@ void Irq_session_component::sigh(Genode::Signal_context_capability sigh) if (!old.valid() && sigh.valid()) _proxy->add_sharer(&_irq_sigh); } + + +Genode::Irq_session::Info Irq_session_component::info() +{ + if (!_proxy || !_proxy->ready() || !_proxy->msi_address() || + !_proxy->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() + }; +} diff --git a/repos/base-nova/src/core/platform.cc b/repos/base-nova/src/core/platform.cc index 353b22716..d73d8fef7 100644 --- a/repos/base-nova/src/core/platform.cc +++ b/repos/base-nova/src/core/platform.cc @@ -550,7 +550,7 @@ Platform::Platform() : _io_port_alloc.add_range(0, 0x10000); /* IRQ allocator */ - _irq_alloc.add_range(0, hip->sel_gsi - 1); + _irq_alloc.add_range(0, hip->sel_gsi); _gsi_base_sel = (hip->mem_desc_offset - hip->cpu_desc_offset) / hip->cpu_desc_size; if (verbose_boot_info) { diff --git a/repos/base-okl4/src/core/irq_session_component.cc b/repos/base-okl4/src/core/irq_session_component.cc index 5ef06c72f..90fb96f3f 100644 --- a/repos/base-okl4/src/core/irq_session_component.cc +++ b/repos/base-okl4/src/core/irq_session_component.cc @@ -138,10 +138,13 @@ Irq_session_component::Irq_session_component(Range_allocator *irq_alloc, 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) { @@ -179,3 +182,10 @@ void Irq_session_component::sigh(Genode::Signal_context_capability sigh) if (!old.valid() && sigh.valid()) _proxy->add_sharer(&_irq_sigh); } + + +Genode::Irq_session::Info Irq_session_component::info() +{ + /* no MSI support */ + return { .type = Genode::Irq_session::Info::Type::INVALID }; +} diff --git a/repos/base-pistachio/src/core/irq_session_component.cc b/repos/base-pistachio/src/core/irq_session_component.cc index c985d862a..8cdc5bf2e 100644 --- a/repos/base-pistachio/src/core/irq_session_component.cc +++ b/repos/base-pistachio/src/core/irq_session_component.cc @@ -157,10 +157,13 @@ Irq_session_component::Irq_session_component(Range_allocator *irq_alloc, 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) { @@ -198,3 +201,10 @@ void Irq_session_component::sigh(Genode::Signal_context_capability sigh) if (!old.valid() && sigh.valid()) _proxy->add_sharer(&_irq_sigh); } + + +Genode::Irq_session::Info Irq_session_component::info() +{ + /* no MSI support */ + return { .type = Genode::Irq_session::Info::Type::INVALID }; +} diff --git a/repos/base/include/irq_session/client.h b/repos/base/include/irq_session/client.h index d0cecef3b..5112571a3 100644 --- a/repos/base/include/irq_session/client.h +++ b/repos/base/include/irq_session/client.h @@ -44,6 +44,8 @@ struct Genode::Irq_session_client : Rpc_client void ack_irq() override; void sigh(Signal_context_capability sigh) override { call(sigh); } + + Info info() override { return call(); } }; #endif /* _INCLUDE__IRQ_SESSION__CLIENT_H_ */ diff --git a/repos/base/include/irq_session/connection.h b/repos/base/include/irq_session/connection.h index 9f8ef004f..48fce7e4b 100644 --- a/repos/base/include/irq_session/connection.h +++ b/repos/base/include/irq_session/connection.h @@ -32,11 +32,13 @@ struct Genode::Irq_connection : Connection, Irq_session_client */ Irq_connection(unsigned irq, Irq_session::Trigger trigger = Irq_session::TRIGGER_UNCHANGED, - Irq_session::Polarity polarity = Irq_session::POLARITY_UNCHANGED) + Irq_session::Polarity polarity = Irq_session::POLARITY_UNCHANGED, + Genode::addr_t device_config_phys = 0) : Connection( - session("ram_quota=4K, irq_number=%u, irq_trigger=%u, irq_polarity=%u", - irq, trigger, polarity)), + session("ram_quota=4K, irq_number=%u, irq_trigger=%u, " + " irq_polarity=%u, device_config_phys=0x%lx", + irq, trigger, polarity, device_config_phys)), Irq_session_client(cap()) { } }; diff --git a/repos/base/include/irq_session/irq_session.h b/repos/base/include/irq_session/irq_session.h index a20470010..21c2cd113 100644 --- a/repos/base/include/irq_session/irq_session.h +++ b/repos/base/include/irq_session/irq_session.h @@ -32,6 +32,12 @@ namespace Genode { struct Genode::Irq_session : Session { + struct Info { + enum Type { INVALID, MSI } type; + unsigned long address; + unsigned long value; + }; + /** * Interrupt trigger */ @@ -57,6 +63,12 @@ struct Genode::Irq_session : Session */ virtual void sigh(Genode::Signal_context_capability sigh) = 0; + /** + * Request information about IRQ, e.g. on x86 request MSI address and + * MSI value to be programmed to device specific PCI registers. + */ + virtual Info info() = 0; + /************* ** Session ** *************/ @@ -70,7 +82,8 @@ struct Genode::Irq_session : Session GENODE_RPC(Rpc_ack_irq, void, ack_irq); GENODE_RPC(Rpc_sigh, void, sigh, Genode::Signal_context_capability); - GENODE_RPC_INTERFACE(Rpc_ack_irq, Rpc_sigh); + GENODE_RPC(Rpc_info, Info, info); + GENODE_RPC_INTERFACE(Rpc_ack_irq, Rpc_sigh, Rpc_info); }; #endif /* _INCLUDE__IRQ_SESSION__IRQ_SESSION_H_ */ diff --git a/repos/base/src/core/include/irq_session_component.h b/repos/base/src/core/include/irq_session_component.h index 49249b635..f65024832 100644 --- a/repos/base/src/core/include/irq_session_component.h +++ b/repos/base/src/core/include/irq_session_component.h @@ -57,8 +57,9 @@ class Genode::Irq_session_component : public Rpc_object, ** Irq session interface ** ***************************/ - void ack_irq(); - void sigh(Signal_context_capability) override; + void ack_irq() override; + void sigh(Signal_context_capability) override; + Info info() override; }; #endif /* _CORE__INCLUDE__IRQ_SESSION_COMPONENT_H_ */ diff --git a/repos/os/include/gpio/component.h b/repos/os/include/gpio/component.h index 2e58a63bd..419b330b3 100644 --- a/repos/os/include/gpio/component.h +++ b/repos/os/include/gpio/component.h @@ -46,6 +46,8 @@ class Gpio::Session_component : public Genode::Rpc_object void ack_irq() override { _driver.ack_irq(_pin); } void sigh(Genode::Signal_context_capability sigh) override { _driver.register_signal(_pin, sigh); } + Info info() override { + return { .type = Genode::Irq_session::Info::Type::INVALID }; } }; Genode::Rpc_entrypoint &_ep; diff --git a/repos/os/src/drivers/pci/irq.h b/repos/os/src/drivers/pci/irq.h index 1f9c93b03..2d05a7840 100644 --- a/repos/os/src/drivers/pci/irq.h +++ b/repos/os/src/drivers/pci/irq.h @@ -44,6 +44,8 @@ class Pci::Irq_session_component : public Genode::Rpc_object