nova: support interrupt mode config in assign_gsi

Fixes #4553
This commit is contained in:
Christian Helmuth 2022-07-01 17:04:22 +02:00
parent 47c924d1f5
commit ab9a2107e4
7 changed files with 88 additions and 42 deletions

View File

@ -257,6 +257,30 @@ namespace Nova {
*/ */
enum Pd_op { TRANSFER_QUOTA = 0U, PD_DEBUG = 2U }; 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 class Descriptor
{ {

View File

@ -457,12 +457,12 @@ namespace Nova {
ALWAYS_INLINE ALWAYS_INLINE
inline uint8_t assign_gsi(mword_t sm, mword_t dev, mword_t cpu, inline uint8_t assign_gsi(mword_t sm, mword_t dev, mword_t cpu,
mword_t &msi_addr, mword_t &msi_data, 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_addr = dev;
msi_data = cpu; 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);
} }

View File

@ -402,11 +402,11 @@ namespace Nova {
ALWAYS_INLINE ALWAYS_INLINE
inline uint8_t assign_gsi(mword_t sm, mword_t dev, mword_t cpu, inline uint8_t assign_gsi(mword_t sm, mword_t dev, mword_t cpu,
mword_t &msi_addr, mword_t &msi_data, 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_addr = dev;
msi_data = cpu; 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_ */ #endif /* _INCLUDE__SPEC__64BIT__NOVA__SYSCALLS_H_ */

View File

@ -1 +1 @@
33a2fa953ec52b0f63b921f4d33d68891c0aada0 1b4d51180f6c3e64b2adb13a27257ef0544b859d

View File

@ -4,7 +4,7 @@ DOWNLOADS := nova.git
# r10 branch # r10 branch
URL(nova) := https://github.com/alex-ab/NOVA.git URL(nova) := https://github.com/alex-ab/NOVA.git
REV(nova) := 00dc49bc18e7f72a9c85487e8f94fd859511d89d REV(nova) := 87db5b14163bc5e8b5dac6b191dbbe4833fb8cb3
DIR(nova) := src/kernel/nova DIR(nova) := src/kernel/nova
PATCHES := $(sort $(wildcard $(REP_DIR)/patches/*.patch)) PATCHES := $(sort $(wildcard $(REP_DIR)/patches/*.patch))

View File

@ -13,7 +13,9 @@
#ifndef _CORE__INCLUDE__IRQ_OBJECT_H_ #ifndef _CORE__INCLUDE__IRQ_OBJECT_H_
#define _CORE__INCLUDE__IRQ_OBJECT_H_ #define _CORE__INCLUDE__IRQ_OBJECT_H_
namespace Genode { class Irq_object; } #include <nova/syscall-generic.h> /* Gsi_flags */
namespace Genode { class Irq_object; class Irq_args; }
class Genode::Irq_object class Genode::Irq_object
{ {
@ -26,22 +28,24 @@ class Genode::Irq_object
addr_t _msi_data; addr_t _msi_data;
addr_t _device_phys = 0; /* PCI config extended address */ addr_t _device_phys = 0; /* PCI config extended address */
Nova::Gsi_flags _gsi_flags { };
enum { KERNEL_CAP_COUNT_LOG2 = 0 }; enum { KERNEL_CAP_COUNT_LOG2 = 0 };
Genode::addr_t irq_sel() const { return _kernel_caps; } addr_t irq_sel() const { return _kernel_caps; }
public: public:
Irq_object(); Irq_object();
~Irq_object(); ~Irq_object();
Genode::addr_t msi_address() const { return _msi_addr; } addr_t msi_address() const { return _msi_addr; }
Genode::addr_t msi_value() const { return _msi_data; } addr_t msi_value() const { return _msi_data; }
void sigh(Signal_context_capability cap); void sigh(Signal_context_capability cap);
void ack_irq(); 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_ */ #endif /* _CORE__INCLUDE__IRQ_OBJECT_H_ */

View File

@ -18,6 +18,7 @@
/* core includes */ /* core includes */
#include <irq_root.h> #include <irq_root.h>
#include <irq_args.h>
#include <platform.h> #include <platform.h>
/* NOVA includes */ /* NOVA includes */
@ -27,13 +28,12 @@
using namespace Genode; using namespace Genode;
static bool irq_ctrl(Genode::addr_t irq_sel, static bool irq_ctrl(addr_t irq_sel, addr_t &msi_addr, addr_t &msi_data,
Genode::addr_t &msi_addr, Genode::addr_t &msi_data, addr_t sig_sel, Nova::Gsi_flags flags, addr_t virt_addr)
Genode::addr_t sig_sel, Genode::addr_t virt_addr = 0)
{ {
/* assign IRQ to CPU && request msi data to be used by driver */ /* assign IRQ to CPU && request msi data to be used by driver */
uint8_t res = Nova::assign_gsi(irq_sel, virt_addr, boot_cpu(), 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) if (res != Nova::NOVA_OK)
error("setting up MSI failed - error ", res); 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, static bool associate_gsi(addr_t irq_sel, Signal_context_capability sig_cap,
Genode::addr_t &msi_addr, Genode::addr_t &msi_data, Nova::Gsi_flags gsi_flags)
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)
{ {
addr_t dummy1 = 0, dummy2 = 0; 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"); warning("Irq could not be de-associated");
} }
static bool msi(Genode::addr_t irq_sel, Genode::addr_t phys_mem, static bool associate_msi(addr_t irq_sel, addr_t phys_mem, addr_t &msi_addr,
Genode::addr_t &msi_addr, Genode::addr_t &msi_data, addr_t &msi_data, Signal_context_capability sig_cap)
Genode::Signal_context_capability sig_cap)
{ {
return platform().region_alloc().alloc_aligned(4096, 12).convert<bool>( return platform().region_alloc().alloc_aligned(4096, 12).convert<bool>(
[&] (void *virt_ptr) { [&] (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 */ /* 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))); unmap_local(Nova::Mem_crd(virt_addr >> 12, 0, Rights(true, true, true)));
platform().region_alloc().free(virt_ptr, 4096); platform().region_alloc().free(virt_ptr, 4096);
@ -118,11 +116,12 @@ void Irq_object::sigh(Signal_context_capability cap)
return; return;
} }
/* associate GSI or MSI to device belonging to device_phys */
bool ok = false; bool ok = false;
if (_device_phys) 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 else
ok = associate(irq_sel(), _msi_addr, _msi_data, cap); ok = associate_gsi(irq_sel(), cap, _gsi_flags);
if (!ok) { if (!ok) {
deassociate(irq_sel()); 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 */ /* map IRQ SM cap from kernel to core at irq_sel selector */
using Nova::Obj_crd; using Nova::Obj_crd;
@ -158,12 +157,29 @@ void Irq_object::start(unsigned irq, Genode::addr_t const device_phys)
throw Service_denied(); 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 */ /* associate GSI or MSI to device belonging to device_phys */
bool ok = false; bool ok = false;
if (device_phys) 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 else
ok = associate(irq_sel(), _msi_addr, _msi_data, _sigh_cap); ok = associate_gsi(irq_sel(), _sigh_cap, _gsi_flags);
if (!ok) if (!ok)
throw Service_denied(); 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() _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); long device_phys = Arg_string::find_arg(args, "device_config_phys").long_value(0);
if (device_phys) { if (device_phys) {
@ -232,7 +250,7 @@ Irq_session_component::Irq_session_component(Range_allocator &irq_alloc,
_irq_number = (unsigned)irq_number; _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) if (_irq_number == ~0U)
return; return;
Genode::addr_t free_irq = _irq_number; addr_t free_irq = _irq_number;
_irq_alloc.free((void *)free_irq); _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); _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()) if (!_irq_object.msi_address() || !_irq_object.msi_value())
return { .type = Info::Type::INVALID, .address = 0, .value = 0 }; return { .type = Info::Type::INVALID, .address = 0, .value = 0 };