hw: extend kernel interrupt class

The generalization of interrupt objects in the kernel and the use of
C++ polymorphism instead of explicitely checking for special interrupts
within generic code (Cpu_job::_interrupt) enables the registration of
additional interrupts used by the kernel, which are needed for specific
aspects added to the kernel, like ARM hardware virtualization interrupts.

* Introduce generic base class for interrupt objects handled by the kernel
* Derive an interrupt class for those handled by the user-land
* Implement IPI-specific interrupt class
* Implement timer interrupts using the new generic base class

Ref #1405
This commit is contained in:
Stefan Kalkowski 2015-02-18 14:23:54 +01:00 committed by Christian Helmuth
parent 0836726df2
commit 8e2b4d6f45
9 changed files with 218 additions and 153 deletions

View File

@ -20,6 +20,7 @@
#include <timer.h>
#include <cpu.h>
#include <kernel/cpu_scheduler.h>
#include <kernel/irq.h>
/* base includes */
#include <unmanaged_singleton.h>
@ -232,22 +233,51 @@ class Kernel::Cpu_idle : public Genode::Cpu::User_context, public Cpu_job
Cpu_job * helping_sink() { return this; }
};
class Kernel::Cpu : public Genode::Cpu
class Kernel::Cpu : public Genode::Cpu,
public Irq::Pool
{
private:
typedef Cpu_job Job;
/**
* Inter-processor-interrupt object of the cpu
*/
struct Ipi : Irq
{
bool pending = false;
/*********************
** Irq interface **
*********************/
void occurred();
/**
* Constructor
*
* \param p interrupt pool this irq shall reside in
*/
Ipi(Irq::Pool &p);
/**
* Trigger the ipi
*
* \param cpu_id id of the cpu this ipi object is related to
*/
void trigger(unsigned const cpu_id);
};
unsigned const _id;
Cpu_idle _idle;
Timer * const _timer;
Cpu_scheduler _scheduler;
bool _ip_interrupt_pending;
Ipi _ipi_irq;
Irq _timer_irq; /* timer irq implemented as empty event */
unsigned _quota() const { return _timer->ms_to_tics(cpu_quota_ms); }
unsigned _fill() const { return _timer->ms_to_tics(cpu_fill_ms); }
Job * _scheduled_job() const {
return static_cast<Job *>(_scheduler.head())->helping_sink(); }
unsigned _fill() const { return _timer->ms_to_tics(cpu_fill_ms); }
public:
@ -255,25 +285,29 @@ class Kernel::Cpu : public Genode::Cpu
* Construct object for CPU 'id' with scheduling timer 'timer'
*/
Cpu(unsigned const id, Timer * const timer)
:
_id(id), _idle(this), _timer(timer),
_scheduler(&_idle, _quota(), _fill()),
_ip_interrupt_pending(false) { }
/**
* Check if IRQ 'i' was due to a scheduling timeout
*/
bool timer_irq(unsigned const i) { return _timer->interrupt_id(_id) == i; }
/**
* Notice that the IPI of the CPU isn't pending anymore
*/
void ip_interrupt_handled() { _ip_interrupt_pending = false; }
: _id(id), _idle(this), _timer(timer),
_scheduler(&_idle, _quota(), _fill()),
_ipi_irq(*this),
_timer_irq(_timer->interrupt_id(_id), *this) { }
/**
* Raise the IPI of the CPU
*/
void trigger_ip_interrupt();
void trigger_ip_interrupt() { _ipi_irq.trigger(_id); }
/**
* Deliver interrupt to the CPU
*
* \param irq_id id of the interrupt that occured
* \returns true if the interrupt belongs to this CPU, otherwise false
*/
bool interrupt(unsigned const irq_id)
{
Irq * const irq = object(irq_id);
if (!irq) return false;
irq->occurred();
return true;
}
/**
* Schedule 'job' at this CPU
@ -286,7 +320,7 @@ class Kernel::Cpu : public Genode::Cpu
void exception()
{
/* update old job */
Job * const old_job = _scheduled_job();
Job * const old_job = scheduled_job();
old_job->exception(_id);
/* update scheduler */
@ -296,7 +330,7 @@ class Kernel::Cpu : public Genode::Cpu
_scheduler.update(quota);
/* get new job */
Job * const new_job = _scheduled_job();
Job * const new_job = scheduled_job();
quota = _scheduler.head_quota();
assert(quota);
_timer->start_one_shot(quota, _id);
@ -314,6 +348,12 @@ class Kernel::Cpu : public Genode::Cpu
** Accessors **
***************/
/**
* Returns the currently active job
*/
Job * scheduled_job() const {
return static_cast<Job *>(_scheduler.head())->helping_sink(); }
unsigned id() const { return _id; }
Cpu_scheduler * scheduler() { return &_scheduler; }
};
@ -351,6 +391,11 @@ class Kernel::Cpu_pool
*/
Cpu * primary_cpu() const { return cpu(Cpu::primary_id()); }
/**
* Return object of current CPU
*/
Cpu * executing_cpu() const { return cpu(Cpu::executing_id()); }
/*
* Accessors
*/

View File

@ -1,6 +1,7 @@
/*
* \brief Kernel back-end and core front-end for user interrupts
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2013-10-28
*/
@ -25,9 +26,14 @@
namespace Kernel
{
/**
* Kernel back-end of a user interrupt
* Kernel back-end interface of an interrupt
*/
class Irq;
/**
* Kernel back-end of a user interrupt
*/
class User_irq;
}
namespace Genode
@ -38,27 +44,10 @@ namespace Genode
class Irq;
}
class Kernel::Irq
:
public Object_pool<Irq>::Item,
public Signal_receiver,
public Signal_context,
public Signal_ack_handler
class Kernel::Irq : public Object_pool<Irq>::Item
{
friend class Genode::Irq;
private:
typedef Object_pool<Irq> Pool;
/**
* Get map that provides all user interrupts by their kernel names
*/
static Pool * _pool()
{
static Pool p;
return &p;
}
protected:
/**
* Prevent interrupt from occurring
@ -75,6 +64,55 @@ class Kernel::Irq
*/
unsigned _id() const { return Pool::Item::id(); };
public:
using Pool = Object_pool<Irq>;
/**
* Constructor
*
* \param irq_id kernel name of the interrupt
*/
Irq(unsigned const irq_id)
: Pool::Item(irq_id) { }
/**
* Constructor
*
* \param irq_id kernel name of the interrupt
* \param pool pool this interrupt shall belong to
*/
Irq(unsigned const irq_id, Pool &pool)
: Irq(irq_id) { pool.insert(this); }
/**
* Destructor
*
* By now, there is no use case to destruct interrupts
*/
virtual ~Irq() { PERR("destruction of interrupts not implemented"); }
/**
* Handle occurence of the interrupt
*/
virtual void occurred() { }
};
class Kernel::User_irq
:
public Kernel::Irq,
public Signal_receiver,
public Signal_context,
public Signal_ack_handler
{
private:
/**
* Get map that provides all user interrupts by their kernel names
*/
static Irq::Pool * _pool();
/**
* Get kernel name of the interrupt-signal receiver
*/
@ -85,43 +123,6 @@ class Kernel::Irq
*/
unsigned _context_id() const { return Signal_context::Object::id(); }
/**
* Handle occurence of the interrupt
*/
void _occurred()
{
Signal_context::submit(1);
_disable();
}
/**
* Get information that enables a user to handle an interrupt
*
* \param irq_id kernel name of targeted interrupt
*/
static Genode::Irq_signal _genode_signal(unsigned const irq_id)
{
typedef Genode::Irq_signal Irq_signal;
static Irq_signal const invalid = { 0, 0 };
Irq * const irq = _pool()->object(irq_id);
if (irq) {
Irq_signal s = { irq->_receiver_id(), irq->_context_id() };
return s;
}
return invalid;
}
/**
* Destructor
*
* By now, there is no use case to destruct user interrupts
*/
~Irq()
{
PERR("destruction of interrupts not implemented");
while (1) { }
}
/************************
** Signal_ack_handler **
@ -136,14 +137,39 @@ class Kernel::Irq
*
* \param irq_id kernel name of the interrupt
*/
Irq(unsigned const irq_id)
:
Pool::Item(irq_id),
Signal_context(this, 0)
User_irq(unsigned const irq_id)
: Irq(irq_id), Signal_context(this, 0)
{
Signal_context::ack_handler(this);
_pool()->insert(this);
_disable();
Signal_context::ack_handler(this);
}
/**
* Handle occurence of the interrupt
*/
void occurred()
{
Signal_context::submit(1);
_disable();
}
/**
* Get information that enables a user to handle an interrupt
*
* \param irq_id kernel name of targeted interrupt
*/
static Genode::Irq_signal signal(unsigned const irq_id)
{
typedef Genode::Irq_signal Irq_signal;
static Irq_signal const invalid = { 0, 0 };
User_irq * const irq =
dynamic_cast<User_irq*>(_pool()->object(irq_id));
if (irq) {
Irq_signal s = { irq->_receiver_id(), irq->_context_id() };
return s;
}
return invalid;
}
/**
@ -151,30 +177,8 @@ class Kernel::Irq
*
* \param irq_id kernel name of targeted interrupt
*/
static void occurred(unsigned const irq_id)
{
Irq * const irq = _pool()->object(irq_id);
if (!irq) {
PWRN("unknown interrupt occurred");
return;
}
irq->_occurred();
}
};
class Genode::Irq
{
public:
/**
* Get information that enables a user to handle an interrupt
*
* \param irq_id kernel name of targeted interrupt
*/
static Irq_signal signal(unsigned const irq_id)
{
return Kernel::Irq::_genode_signal(irq_id);
}
static User_irq * object(unsigned const irq_id) {
return dynamic_cast<User_irq*>(_pool()->object(irq_id)); }
};
#endif /* _KERNEL__IRQ_H_ */

View File

@ -177,12 +177,15 @@ class Genode::Arm_gic_cpu_interface : public Mmio
class Genode::Pic
{
public:
enum { IPI = 1 };
protected:
typedef Arm_gic_cpu_interface Cpui;
typedef Arm_gic_distributor Distr;
static constexpr unsigned ipi = 1;
static constexpr unsigned min_spi = 32;
static constexpr unsigned spurious_id = 1023;
@ -260,14 +263,6 @@ class Genode::Pic
void mask(unsigned const irq_id) {
_distr.write<Distr::Icenabler::Clear_enable>(1, irq_id); }
/**
* Return wether an IRQ is inter-processor IRQ of a CPU
*
* \param irq_id kernel name of the IRQ
*/
bool is_ip_interrupt(unsigned const irq_id) {
return irq_id == ipi; }
/**
* Raise inter-processor IRQ of the CPU with kernel name 'cpu_id'
*/
@ -275,7 +270,7 @@ class Genode::Pic
{
typedef Distr::Sgir Sgir;
Sgir::access_t sgir = 0;
Sgir::Sgi_int_id::set(sgir, ipi);
Sgir::Sgi_int_id::set(sgir, IPI);
Sgir::Cpu_target_list::set(sgir, 1 << cpu_id);
_distr.write<Sgir>(sgir);
}

View File

@ -32,7 +32,14 @@ class Genode::Pic : public Mmio
{
public:
enum { NR_OF_IRQ = 109 };
enum {
/*
* FIXME: dummy ipi value on non-SMP platform, should be removed
* when SMP is an aspect of CPUs only compiled where necessary
*/
IPI = 0,
NR_OF_IRQ = 109,
};
protected:
@ -146,10 +153,6 @@ class Genode::Pic : public Mmio
void mask(unsigned const i) {
if (valid(i)) { write<Enclear::Clear_enable>(1, i); } }
/**
* Wether an interrupt is inter-processor interrupt of a CPU
*/
bool is_ip_interrupt(unsigned) { return false; }
/*************
** Dummies **

View File

@ -135,7 +135,14 @@ class Genode::Pic : Mmio
{
public:
enum { NR_OF_IRQ = 64 };
enum {
/*
* FIXME: dummy ipi value on non-SMP platform, should be removed
* when SMP is an aspect of CPUs only compiled where necessary
*/
IPI = 63,
NR_OF_IRQ = 64,
};
private:

View File

@ -63,6 +63,6 @@ Irq_session_component::Irq_session_component(Cap_session * const cap_session
throw Root::Invalid_args();
}
/* make interrupt accessible */
_signal = Irq::signal(irq_number);
_signal = Kernel::User_irq::signal(irq_number);
_cap = Irq_session_capability(irq_session_ep()->manage(this));
}

View File

@ -92,24 +92,19 @@ void Cpu_job::_interrupt(unsigned const cpu_id)
{
/* determine handling for specific interrupt */
unsigned irq_id;
Pic * const ic = pic();
if (ic->take_request(irq_id)) {
if (pic()->take_request(irq_id))
/* check wether the interrupt is a CPU-scheduling timeout */
if (!_cpu->timer_irq(irq_id)) {
/* is the interrupt a cpu-local one */
if (!_cpu->interrupt(irq_id)) {
/* check wether the interrupt is our IPI */
if (ic->is_ip_interrupt(irq_id)) {
cpu_domain_update_list()->do_each();
_cpu->ip_interrupt_handled();
/* try to inform the user interrupt-handler */
} else { Irq::occurred(irq_id); }
/* it needs to be a user interrupt */
User_irq * irq = User_irq::object(irq_id);
if (irq) irq->occurred();
else PWRN("Unknown interrupt %u", irq_id);
}
}
/* end interrupt request at controller */
ic->finish_request();
pic()->finish_request();
}
@ -138,15 +133,25 @@ void Cpu::schedule(Job * const job)
}
void Cpu::trigger_ip_interrupt()
void Cpu::Ipi::occurred()
{
if (!_ip_interrupt_pending) {
pic()->trigger_ip_interrupt(_id);
_ip_interrupt_pending = true;
}
cpu_domain_update_list()->do_each();
pending = false;
}
void Cpu::Ipi::trigger(unsigned const cpu_id)
{
if (pending) return;
pic()->trigger_ip_interrupt(cpu_id);
pending = true;
}
Cpu::Ipi::Ipi(Irq::Pool &p) : Irq(Pic::IPI, p) { }
/***********************
** Cpu_domain_update **
***********************/

View File

@ -22,4 +22,10 @@ namespace Kernel { Pic * pic(); }
void Irq::_disable() const { pic()->mask(_id()); }
void Irq::_enable() const { pic()->unmask(_id(), Cpu::executing_id()); }
void Irq::_enable() const { pic()->unmask(_id(), Cpu::executing_id()); }
Irq::Pool * User_irq::_pool()
{
static Irq::Pool p;
return &p;
}

View File

@ -161,10 +161,10 @@ namespace Kernel
*/
bool private_interrupt(unsigned const irq)
{
for (unsigned i = 0; i < NR_OF_CPUS; i++) {
if (irq == Timer::interrupt_id(i)) { return 1; }
}
return 0;
for (unsigned i = 0; i < NR_OF_CPUS; i++)
if (irq == Timer::interrupt_id(i)) return true;
if (irq == Pic::IPI) return true;
return false;
}
}
@ -261,11 +261,11 @@ void init_kernel_mp_primary()
t.sp = (addr_t)s + STACK_SIZE;
t.init(cpu_pool()->primary_cpu(), core_pd(), &utcb, 1);
/* initialize interrupt objects */
static Genode::uint8_t _irqs[Pic::NR_OF_IRQ * sizeof(Irq)];
/* initialize user interrupt objects */
static Genode::uint8_t _irqs[Pic::NR_OF_IRQ * sizeof(User_irq)];
for (unsigned i = 0; i < Pic::NR_OF_IRQ; i++) {
if (private_interrupt(i)) { continue; }
new (&_irqs[i * sizeof(Irq)]) Irq(i);
new (&_irqs[i * sizeof(User_irq)]) User_irq(i);
}
/* kernel initialization finished */
Genode::printf("kernel initialized\n");