nova: use signals with irqs in core

Avoids the need to have per IRQ a thread that blocks synchronously for next
interrupt. Now a thread may wait for multiple IRQs as other signals
simultaneously.

In core no threads are required anymore for IRQs/MSI - the clients (either
the pci_drv or in case of MSI the driver) gets the IRQ delivered directly as
a ordinary Genode signal.

Useful since #1216 and #1487 is now available.
Commit applies feature of #1446 also to IRQ/MSIs.
This commit is contained in:
Alexander Boettcher 2015-05-12 14:52:29 +02:00 committed by Christian Helmuth
parent d554200e5a
commit 3e90542559
6 changed files with 111 additions and 164 deletions

View File

@ -157,17 +157,23 @@ namespace Nova {
ALWAYS_INLINE
inline uint8_t syscall_5(Syscall s, uint8_t flags, mword_t sel,
mword_t &p1, mword_t &p2)
mword_t &p1, mword_t &p2, mword_t p3 = ~0UL)
{
mword_t status = eax(s, flags, sel);
asm volatile (" movl %%esp, %%ecx;"
asm volatile (" push %%ebx;"
" mov %%ecx, %%ebx;"
" movl %%esp, %%ecx;"
" movl $1f, %%edx;"
"sysenter;"
"1:"
: "+a" (status), "+D" (p1), "+S" (p2)
:
: "ecx", "edx", "memory");
" pop %%ebx;"
: "+a" (status), "+D" (p1), "+S" (p2), "+c" (p3)
:
: "edx", "memory");
return status;
}
@ -310,12 +316,14 @@ 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)
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)
{
msi_addr = dev;
msi_data = cpu;
return syscall_5(NOVA_ASSIGN_GSI, 0, sm, msi_addr, msi_data);
return syscall_5(NOVA_ASSIGN_GSI, 0, sm, msi_addr, msi_data, si);
}
}
#endif /* _PLATFORM__NOVA_SYSCALLS_H_ */

View File

@ -121,13 +121,13 @@ namespace Nova {
ALWAYS_INLINE
inline uint8_t syscall_5(Syscall s, uint8_t flags, mword_t sel,
mword_t &p1, mword_t &p2)
mword_t &p1, mword_t &p2, mword_t p3 = ~0UL)
{
mword_t status = rdi(s, flags, sel);
asm volatile ("syscall"
: "+D" (status), "+S"(p1), "+d"(p2)
:
: "a" (p3)
: "rcx", "r11", "memory");
return status;
}
@ -280,11 +280,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 &msi_addr, mword_t &msi_data,
mword_t si = ~0UL)
{
msi_addr = dev;
msi_data = cpu;
return syscall_5(NOVA_ASSIGN_GSI, 0, sm, msi_addr, msi_data);
return syscall_5(NOVA_ASSIGN_GSI, 0, sm, msi_addr, msi_data, si);
}
}
#endif /* _PLATFORM__NOVA_SYSCALLS_H_ */

View File

@ -1 +1 @@
64d63799ff123098285ae3f74e2c17ce00e580ad
70a0463d4ae4f5cfc6146b43d7bb2e904d208d55

View File

@ -4,7 +4,7 @@ DOWNLOADS := nova.git
URL(nova) := https://github.com/alex-ab/NOVA.git
# r8 branch
REV(nova) := 123f1e73080b7129a707c7e9547e14052ea6fe32
REV(nova) := 9916e624f59462b8bb157dad3b38592e4ffbc561
DIR(nova) := src/kernel/nova
PATCHES := $(wildcard $(REP_DIR)/patches/*.patch)

View File

@ -12,29 +12,22 @@
#pragma once
#include <base/thread.h>
namespace Genode { class Irq_object; }
class Genode::Irq_object : public Thread<4096> {
class Genode::Irq_object
{
private:
Signal_context_capability _sig_cap;
Lock _sync_ack;
Lock _sync_life;
Signal_context_capability _sigh_cap;
Genode::addr_t _kernel_caps;
Genode::addr_t _msi_addr;
Genode::addr_t _msi_data;
enum { UNDEFINED, SHUTDOWN } volatile _state;
Genode::addr_t _device_phys; /* PCI config extended address */
void entry() override;
enum { KERNEL_CAP_COUNT_LOG2 = 1 };
enum { KERNEL_CAP_COUNT_LOG2 = 0 };
Genode::addr_t const irq_sel() { return _kernel_caps; }
Genode::addr_t const sc_sel() { return _kernel_caps + 1; }
public:
@ -44,11 +37,8 @@ class Genode::Irq_object : public Thread<4096> {
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 sigh(Signal_context_capability cap);
void ack_irq();
void ack_irq() { _sync_ack.unlock(); }
void start() override;
void start(unsigned irq, Genode::addr_t);
};

View File

@ -15,65 +15,59 @@
/* Genode includes */
#include <base/printf.h>
#include <base/sleep.h>
/* core includes */
#include <irq_root.h>
#include <platform.h>
#include <platform_pd.h>
/* NOVA includes */
#include <nova/syscalls.h>
#include <nova/util.h>
#include <nova_util.h>
using namespace Genode;
static void thread_start()
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)
{
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);
msi_addr, msi_data, sig_sel);
if (virt_addr && res != Nova::NOVA_OK) {
PERR("setting up MSI %u failed - error %u", irq, res);
return false;
}
if (res != Nova::NOVA_OK)
PERR("setting up MSI failed - error %u", res);
/* nova syscall interface specifies msi addr/data to be 32bit */
msi_addr = msi_addr & ~0U;
msi_data = msi_data & ~0U;
return true;
return res == Nova::NOVA_OK;
}
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)
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)
{
addr_t dummy1 = 0, dummy2 = 0;
if (!irq_ctrl(irq_sel, dummy1, dummy2, irq_sel))
PWRN("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)
{
void * virt = 0;
if (platform()->region_alloc()->alloc_aligned(4096, &virt, 12).is_error())
@ -96,7 +90,7 @@ static bool msi(unsigned irq, Genode::addr_t irq_sel, Genode::addr_t phys_mem,
}
/* try to assign MSI to device */
bool res = associate(irq, irq_sel, msi_addr, msi_data, virt_addr);
bool res = associate(irq_sel, msi_addr, msi_data, sig_cap, virt_addr);
unmap_local(Nova::Mem_crd(virt_addr >> 12, 0, Rights(true, true, true)));
platform()->region_alloc()->free(virt, 4096);
@ -105,129 +99,83 @@ static bool msi(unsigned irq, Genode::addr_t irq_sel, Genode::addr_t phys_mem,
}
void Irq_object::start()
void Irq_object::sigh(Signal_context_capability cap)
{
PERR("wrong start method called");
throw Root::Unavailable();
if (!_sigh_cap.valid() && !cap.valid())
return;
if ((_sigh_cap.valid() && !cap.valid())) {
deassociate(irq_sel());
_sigh_cap = Signal_context_capability();
return;
}
bool ok = false;
if (_device_phys)
ok = msi(irq_sel(), _device_phys, _msi_addr, _msi_data, cap);
else
ok = associate(irq_sel(), _msi_addr, _msi_data, cap);
if (!ok) {
deassociate(irq_sel());
_sigh_cap = Signal_context_capability();
return;
}
_sigh_cap = cap;
}
void Irq_object::ack_irq()
{
if (Nova::NOVA_OK != Nova::sm_ctrl(irq_sel(), Nova::SEMAPHORE_DOWN))
PERR("Unmasking irq of selector 0x%lx failed", irq_sel());
}
/**
* Create global EC, associate it to SC
*/
void Irq_object::start(unsigned irq, Genode::addr_t const device_phys)
{
/* 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("Getting IRQ from kernel failed - %u", irq);
throw Root::Unavailable();
}
/* 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);
ok = msi(irq_sel(), device_phys, _msi_addr, _msi_data, _sigh_cap);
else
ok = associate(irq, irq_sel(), _msi_addr, _msi_data);
ok = associate(irq_sel(), _msi_addr, _msi_data, _sigh_cap);
if (!ok)
throw Root::Unavailable();
/* start thread having a SC */
using namespace Nova;
addr_t pd_sel = Platform_pd::pd_core_sel();
addr_t utcb = reinterpret_cast<addr_t>(&_context->utcb);
/* put IP on stack, it will be read from core pager in platform.cc */
addr_t *sp = reinterpret_cast<addr_t *>(_context->stack_top() - sizeof(addr_t));
*sp = reinterpret_cast<addr_t>(thread_start);
/* 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();
}
/* 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();
}
/* 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();
}
/* default: we don't accept any mappings or translations */
Utcb * utcb_obj = reinterpret_cast<Utcb *>(Thread_base::utcb());
utcb_obj->crd_rcv = Obj_crd();
utcb_obj->crd_xlt = Obj_crd();
/* 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();
}
_sync_life.lock();
}
void Irq_object::entry()
{
/* signal that thread is up and ready */
_sync_life.unlock();
/* wait for first ack_irq */
_sync_ack.lock();
while (true) {
if (Nova::NOVA_OK != Nova::sm_ctrl(irq_sel(), Nova::SEMAPHORE_DOWN))
PERR("Error: blocking for irq_sel 0x%lx failed", irq_sel());
if (_state == SHUTDOWN) {
/* signal end of life to entrypoint thread */
_sync_life.unlock();
while (1) nova_die();
}
if (!_sig_cap.valid())
continue;
notify();
_sync_ack.lock();
}
_device_phys = device_phys;
}
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)
_msi_addr(0UL), _msi_data(0UL)
{ }
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();
if (_sigh_cap.valid())
deassociate(irq_sel());
/* revoke SC and SM of interrupt source */
/* revoke IRQ SM */
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);
@ -259,7 +207,7 @@ Irq_session_component::Irq_session_component(Range_allocator *irq_alloc,
long device_phys = Arg_string::find_arg(args, "device_config_phys").long_value(0);
if (device_phys) {
if (irq_number >= kernel_hip()->sel_gsi)
if ((unsigned long)irq_number >= kernel_hip()->sel_gsi)
throw Root::Unavailable();
irq_number = kernel_hip()->sel_gsi - 1 - irq_number;