mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-21 02:01:38 +00:00
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:
parent
0836726df2
commit
8e2b4d6f45
@ -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
|
||||
*/
|
||||
|
@ -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_ */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 **
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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 **
|
||||
***********************/
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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");
|
||||
|
Loading…
x
Reference in New Issue
Block a user