From ab9a2107e42842843ffee155a1df2446c508b743 Mon Sep 17 00:00:00 2001 From: Christian Helmuth Date: Fri, 1 Jul 2022 17:04:22 +0200 Subject: [PATCH] nova: support interrupt mode config in assign_gsi Fixes #4553 --- .../base-nova/include/nova/syscall-generic.h | 24 ++++++ .../include/spec/32bit/nova/syscalls.h | 4 +- .../include/spec/64bit/nova/syscalls.h | 4 +- repos/base-nova/ports/nova.hash | 2 +- repos/base-nova/ports/nova.port | 2 +- repos/base-nova/src/core/include/irq_object.h | 14 ++-- .../src/core/irq_session_component.cc | 80 ++++++++++++------- 7 files changed, 88 insertions(+), 42 deletions(-) diff --git a/repos/base-nova/include/nova/syscall-generic.h b/repos/base-nova/include/nova/syscall-generic.h index 1692e5aa84..f37e4042c3 100644 --- a/repos/base-nova/include/nova/syscall-generic.h +++ b/repos/base-nova/include/nova/syscall-generic.h @@ -257,6 +257,30 @@ namespace Nova { */ enum Pd_op { TRANSFER_QUOTA = 0U, PD_DEBUG = 2U }; + class Gsi_flags + { + private: + + uint8_t _value { 0 }; + + public: + + enum Mode { HIGH, LOW, EDGE }; + + Gsi_flags() { } + + Gsi_flags(Mode m) + { + switch (m) { + case HIGH: _value = 0b110; break; /* level-high */ + case LOW: _value = 0b111; break; /* level-low */ + case EDGE: _value = 0b100; break; /* edge-triggered */ + } + } + + uint8_t value() const { return _value; } + }; + class Descriptor { diff --git a/repos/base-nova/include/spec/32bit/nova/syscalls.h b/repos/base-nova/include/spec/32bit/nova/syscalls.h index e52c61d334..6fb41d4fee 100644 --- a/repos/base-nova/include/spec/32bit/nova/syscalls.h +++ b/repos/base-nova/include/spec/32bit/nova/syscalls.h @@ -457,12 +457,12 @@ namespace Nova { ALWAYS_INLINE inline uint8_t assign_gsi(mword_t sm, mword_t dev, mword_t cpu, mword_t &msi_addr, mword_t &msi_data, - mword_t si = ~0UL) + mword_t si = ~0UL, Gsi_flags flags = Gsi_flags()) { msi_addr = dev; msi_data = cpu; - return syscall_5(NOVA_ASSIGN_GSI, 0, sm, msi_addr, msi_data, si); + return syscall_5(NOVA_ASSIGN_GSI, flags.value(), sm, msi_addr, msi_data, si); } diff --git a/repos/base-nova/include/spec/64bit/nova/syscalls.h b/repos/base-nova/include/spec/64bit/nova/syscalls.h index 3ab7e887ed..9e8fa765fc 100644 --- a/repos/base-nova/include/spec/64bit/nova/syscalls.h +++ b/repos/base-nova/include/spec/64bit/nova/syscalls.h @@ -402,11 +402,11 @@ namespace Nova { ALWAYS_INLINE inline uint8_t assign_gsi(mword_t sm, mword_t dev, mword_t cpu, mword_t &msi_addr, mword_t &msi_data, - mword_t si = ~0UL) + mword_t si = ~0UL, Gsi_flags flags = Gsi_flags()) { msi_addr = dev; msi_data = cpu; - return syscall_5(NOVA_ASSIGN_GSI, 0, sm, msi_addr, msi_data, si); + return syscall_5(NOVA_ASSIGN_GSI, flags.value(), sm, msi_addr, msi_data, si); } } #endif /* _INCLUDE__SPEC__64BIT__NOVA__SYSCALLS_H_ */ diff --git a/repos/base-nova/ports/nova.hash b/repos/base-nova/ports/nova.hash index bf8d5f7d78..8a005e973e 100644 --- a/repos/base-nova/ports/nova.hash +++ b/repos/base-nova/ports/nova.hash @@ -1 +1 @@ -33a2fa953ec52b0f63b921f4d33d68891c0aada0 +1b4d51180f6c3e64b2adb13a27257ef0544b859d diff --git a/repos/base-nova/ports/nova.port b/repos/base-nova/ports/nova.port index 90f33a67b9..0e21666094 100644 --- a/repos/base-nova/ports/nova.port +++ b/repos/base-nova/ports/nova.port @@ -4,7 +4,7 @@ DOWNLOADS := nova.git # r10 branch URL(nova) := https://github.com/alex-ab/NOVA.git -REV(nova) := 00dc49bc18e7f72a9c85487e8f94fd859511d89d +REV(nova) := 87db5b14163bc5e8b5dac6b191dbbe4833fb8cb3 DIR(nova) := src/kernel/nova PATCHES := $(sort $(wildcard $(REP_DIR)/patches/*.patch)) diff --git a/repos/base-nova/src/core/include/irq_object.h b/repos/base-nova/src/core/include/irq_object.h index e3a681bd2d..869eeed784 100644 --- a/repos/base-nova/src/core/include/irq_object.h +++ b/repos/base-nova/src/core/include/irq_object.h @@ -13,7 +13,9 @@ #ifndef _CORE__INCLUDE__IRQ_OBJECT_H_ #define _CORE__INCLUDE__IRQ_OBJECT_H_ -namespace Genode { class Irq_object; } +#include /* Gsi_flags */ + +namespace Genode { class Irq_object; class Irq_args; } class Genode::Irq_object { @@ -26,22 +28,24 @@ class Genode::Irq_object addr_t _msi_data; addr_t _device_phys = 0; /* PCI config extended address */ + Nova::Gsi_flags _gsi_flags { }; + enum { KERNEL_CAP_COUNT_LOG2 = 0 }; - Genode::addr_t irq_sel() const { return _kernel_caps; } + addr_t irq_sel() const { return _kernel_caps; } public: Irq_object(); ~Irq_object(); - Genode::addr_t msi_address() const { return _msi_addr; } - Genode::addr_t msi_value() const { return _msi_data; } + addr_t msi_address() const { return _msi_addr; } + addr_t msi_value() const { return _msi_data; } void sigh(Signal_context_capability cap); void ack_irq(); - void start(unsigned irq, Genode::addr_t); + void start(unsigned irq, addr_t, Irq_args const &); }; #endif /* _CORE__INCLUDE__IRQ_OBJECT_H_ */ diff --git a/repos/base-nova/src/core/irq_session_component.cc b/repos/base-nova/src/core/irq_session_component.cc index 472d885ce1..2d98d3f5bc 100644 --- a/repos/base-nova/src/core/irq_session_component.cc +++ b/repos/base-nova/src/core/irq_session_component.cc @@ -18,6 +18,7 @@ /* core includes */ #include +#include #include /* NOVA includes */ @@ -27,13 +28,12 @@ using namespace Genode; -static bool irq_ctrl(Genode::addr_t irq_sel, - Genode::addr_t &msi_addr, Genode::addr_t &msi_data, - Genode::addr_t sig_sel, Genode::addr_t virt_addr = 0) +static bool irq_ctrl(addr_t irq_sel, addr_t &msi_addr, addr_t &msi_data, + addr_t sig_sel, Nova::Gsi_flags flags, addr_t virt_addr) { /* 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, sig_sel); + msi_addr, msi_data, sig_sel, flags); if (res != Nova::NOVA_OK) error("setting up MSI failed - error ", res); @@ -46,30 +46,28 @@ static bool irq_ctrl(Genode::addr_t irq_sel, } -static bool associate(Genode::addr_t irq_sel, - Genode::addr_t &msi_addr, Genode::addr_t &msi_data, - Genode::Signal_context_capability sig_cap, - Genode::addr_t virt_addr = 0) -{ - return irq_ctrl(irq_sel, msi_addr, msi_data, sig_cap.local_name(), - virt_addr); -} - - -static void deassociate(Genode::addr_t irq_sel) +static bool associate_gsi(addr_t irq_sel, Signal_context_capability sig_cap, + Nova::Gsi_flags gsi_flags) { addr_t dummy1 = 0, dummy2 = 0; - if (!irq_ctrl(irq_sel, dummy1, dummy2, irq_sel)) + return irq_ctrl(irq_sel, dummy1, dummy2, sig_cap.local_name(), gsi_flags, 0); +} + + +static void deassociate(addr_t irq_sel) +{ + addr_t dummy1 = 0, dummy2 = 0; + + if (!irq_ctrl(irq_sel, dummy1, dummy2, irq_sel, Nova::Gsi_flags(), 0)) warning("Irq could not be de-associated"); } -static bool msi(Genode::addr_t irq_sel, Genode::addr_t phys_mem, - Genode::addr_t &msi_addr, Genode::addr_t &msi_data, - Genode::Signal_context_capability sig_cap) +static bool associate_msi(addr_t irq_sel, addr_t phys_mem, addr_t &msi_addr, + addr_t &msi_data, Signal_context_capability sig_cap) { - return platform().region_alloc().alloc_aligned(4096, 12).convert( + return platform().region_alloc().alloc_aligned(4096, 12).convert( [&] (void *virt_ptr) { @@ -89,7 +87,7 @@ static bool msi(Genode::addr_t irq_sel, Genode::addr_t phys_mem, } /* try to assign MSI to device */ - bool res = associate(irq_sel, msi_addr, msi_data, sig_cap, virt_addr); + bool res = irq_ctrl(irq_sel, msi_addr, msi_data, sig_cap.local_name(), Nova::Gsi_flags(), virt_addr); unmap_local(Nova::Mem_crd(virt_addr >> 12, 0, Rights(true, true, true))); platform().region_alloc().free(virt_ptr, 4096); @@ -118,11 +116,12 @@ void Irq_object::sigh(Signal_context_capability cap) return; } + /* associate GSI or MSI to device belonging to device_phys */ bool ok = false; if (_device_phys) - ok = msi(irq_sel(), _device_phys, _msi_addr, _msi_data, cap); + ok = associate_msi(irq_sel(), _device_phys, _msi_addr, _msi_data, cap); else - ok = associate(irq_sel(), _msi_addr, _msi_data, cap); + ok = associate_gsi(irq_sel(), cap, _gsi_flags); if (!ok) { deassociate(irq_sel()); @@ -141,7 +140,7 @@ void Irq_object::ack_irq() } -void Irq_object::start(unsigned irq, Genode::addr_t const device_phys) +void Irq_object::start(unsigned irq, addr_t const device_phys, Irq_args const &irq_args) { /* map IRQ SM cap from kernel to core at irq_sel selector */ using Nova::Obj_crd; @@ -158,12 +157,29 @@ void Irq_object::start(unsigned irq, Genode::addr_t const device_phys) throw Service_denied(); } + /* initialize GSI IRQ flags */ + auto gsi_flags = [] (Irq_args const &args) { + if (args.trigger() == Irq_session::TRIGGER_UNCHANGED + || args.polarity() == Irq_session::POLARITY_UNCHANGED) + return Nova::Gsi_flags(); + + if (args.trigger() == Irq_session::TRIGGER_EDGE) + return Nova::Gsi_flags(Nova::Gsi_flags::EDGE); + + if (args.polarity() == Irq_session::POLARITY_HIGH) + return Nova::Gsi_flags(Nova::Gsi_flags::HIGH); + else + return Nova::Gsi_flags(Nova::Gsi_flags::LOW); + }; + + _gsi_flags = gsi_flags(irq_args); + /* associate GSI or MSI to device belonging to device_phys */ bool ok = false; if (device_phys) - ok = msi(irq_sel(), device_phys, _msi_addr, _msi_data, _sigh_cap); + ok = associate_msi(irq_sel(), device_phys, _msi_addr, _msi_data, _sigh_cap); else - ok = associate(irq_sel(), _msi_addr, _msi_data, _sigh_cap); + ok = associate_gsi(irq_sel(), _sigh_cap, _gsi_flags); if (!ok) throw Service_denied(); @@ -212,7 +228,9 @@ Irq_session_component::Irq_session_component(Range_allocator &irq_alloc, : _irq_number(~0U), _irq_alloc(irq_alloc), _irq_object() { - long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1); + Irq_args const irq_args(args); + + long irq_number = irq_args.irq_number(); long device_phys = Arg_string::find_arg(args, "device_config_phys").long_value(0); if (device_phys) { @@ -232,7 +250,7 @@ Irq_session_component::Irq_session_component(Range_allocator &irq_alloc, _irq_number = (unsigned)irq_number; - _irq_object.start(_irq_number, device_phys); + _irq_object.start(_irq_number, device_phys, irq_args); } @@ -241,7 +259,7 @@ Irq_session_component::~Irq_session_component() if (_irq_number == ~0U) return; - Genode::addr_t free_irq = _irq_number; + addr_t free_irq = _irq_number; _irq_alloc.free((void *)free_irq); } @@ -252,13 +270,13 @@ void Irq_session_component::ack_irq() } -void Irq_session_component::sigh(Genode::Signal_context_capability cap) +void Irq_session_component::sigh(Signal_context_capability cap) { _irq_object.sigh(cap); } -Genode::Irq_session::Info Irq_session_component::info() +Irq_session::Info Irq_session_component::info() { if (!_irq_object.msi_address() || !_irq_object.msi_value()) return { .type = Info::Type::INVALID, .address = 0, .value = 0 };