genode/repos/os/src/server/vmm/spec/arm_v8/gicv2.cc

252 lines
5.0 KiB
C++

/*
* \brief VMM ARM Generic Interrupt Controller v2 device model
* \author Stefan Kalkowski
* \date 2019-08-05
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <cpu.h>
#include <gic.h>
#include <vm.h>
using Vmm::Gic;
using Register = Vmm::Mmio_register::Register;
bool Gic::Irq::enabled() const { return _enabled; }
bool Gic::Irq::active() const {
return _state == ACTIVE || _state == ACTIVE_PENDING; }
bool Gic::Irq::pending() const {
return _state == PENDING || _state == ACTIVE_PENDING; }
bool Gic::Irq::level() const { return _config == LEVEL; }
unsigned Gic::Irq::number() const { return _num; }
Genode::uint8_t Gic::Irq::priority() const { return _prio; }
Genode::uint8_t Gic::Irq::target() const { return _target; }
void Gic::Irq::enable()
{
_enabled = true;
if (_handler) _handler->enabled();
}
void Gic::Irq::disable()
{
_enabled = false;
if (_handler) _handler->disabled();
}
void Gic::Irq::activate()
{
switch (_state) {
case INACTIVE: return;
case PENDING: return;
case ACTIVE_PENDING: _state = PENDING; return;
case ACTIVE: _state = INACTIVE; return;
};
}
void Gic::Irq::deactivate()
{
switch (_state) {
case INACTIVE: return;
case PENDING: return;
case ACTIVE_PENDING: _state = PENDING; return;
case ACTIVE: _state = INACTIVE; return;
};
}
void Gic::Irq::assert()
{
if (pending()) return;
_state = PENDING;
_pending_list.insert(*this);
}
void Gic::Irq::deassert()
{
if (_state == INACTIVE) return;
_state = INACTIVE;
_pending_list.remove(this);
if (_handler) _handler->eoi();
}
void Gic::Irq::target(Genode::uint8_t t)
{
if (_target == t) return;
_target = t;
}
void Gic::Irq::level(bool l)
{
if (level() == l) return;
_config = l ? LEVEL : EDGE;
}
void Gic::Irq::priority(Genode::uint8_t p)
{
if (_prio == p) return;
_prio = p;
}
void Gic::Irq::handler(Gic::Irq::Irq_handler & handler) { _handler = &handler; }
Gic::Irq::Irq(unsigned num, Type t, Irq::List & l)
: _type(t), _num(num), _pending_list(l) {}
void Gic::Irq::List::insert(Irq & irq)
{
Irq * i = first();
while (i && i->priority() < irq.priority() && i->next()) i = i->next();
Genode::List<Irq>::insert(&irq, i);
}
Gic::Irq * Gic::Irq::List::highest_enabled()
{
Irq * i = first();
while(i) {
if (i->enabled()) return i;
i = i->next();
}
return nullptr;
}
Gic::Irq & Gic::Gicd_banked::irq(unsigned i)
{
if (i < MAX_SGI)
return *_sgi[i];
if (i < MAX_SGI+MAX_PPI)
return *_ppi[i-MAX_SGI];
return *_gic._spi[i-MAX_SGI-MAX_PPI];
}
void Gic::Gicd_banked::handle_irq()
{
unsigned i = _cpu.state().irqs.virtual_irq;
if (i > MAX_IRQ) return;
irq(i).deassert();
_cpu.state().irqs.virtual_irq = 1023;
}
bool Gic::Gicd_banked::pending_irq()
{
if (_cpu.state().irqs.virtual_irq != 1023) return true;
Irq * i = _gic._pending_list.highest_enabled();
Irq * j = _pending_list.highest_enabled();
Irq * n = j;
if (i && ((j && j->priority() > i->priority()) || !j)) n = i;
if (!n) return false;
_cpu.state().irqs.virtual_irq = n->number();
n->activate();
return true;
}
Gic::Gicd_banked::Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus)
: _cpu(cpu), _gic(gic)
{
for (unsigned i = 0; i < MAX_SGI; i++)
_sgi[i].construct(i, Irq::SGI, _pending_list);
for (unsigned i = 0; i < MAX_PPI; i++)
_ppi[i].construct(i+MAX_SGI, Irq::PPI, _pending_list);
_cpu.state().irqs.last_irq = 1023;
_cpu.state().irqs.virtual_irq = 1023;
_rdist.construct(0x80a0000 + (cpu.cpu_id()*0x20000), 0x20000, cpu.cpu_id(), Vm::last_cpu() == cpu.cpu_id());
bus.add(*_rdist);
}
Register Gic::Irq_reg::read(Address_range & access, Cpu & cpu)
{
Register ret = 0;
Register bits_per_irq = size * 8 / irq_count;
for (unsigned i = (access.start * 8) / bits_per_irq;
i < ((access.start+access.size) * 8) / bits_per_irq; i++)
ret |= read(cpu.gic().irq(i)) << (i % 32/bits_per_irq);
return ret;
}
void Gic::Irq_reg::write(Address_range & access, Cpu & cpu, Register value)
{
Register bits_per_irq = size * 8 / irq_count;
Register irq_value_mask = (1<<bits_per_irq) - 1;
for (unsigned i = (access.start * 8) / bits_per_irq;
i < ((access.start+access.size) * 8) / bits_per_irq; i++)
write(cpu.gic().irq(i), (value >> (i % 32/bits_per_irq))
& irq_value_mask);
}
Gic::Gic(const char * const name,
const Genode::uint64_t addr,
const Genode::uint64_t size,
Mmio_bus & bus,
Genode::Env & env)
: Mmio_device(name, addr, size)
{
add(_ctrl);
add(_typer);
add(_iidr);
add(_igroupr);
add(_isenabler);
add(_csenabler);
add(_ispendr);
add(_icpendr);
add(_isactiver);
add(_icactiver);
add(_ipriorityr);
add(_itargetr);
add(_icfgr);
add(_irouter);
add(_sgir);
for (unsigned i = 0; i < (sizeof(Dummy::regs) / sizeof(Mmio_register)); i++)
add(_reg_container.regs[i]);
for (unsigned i = 0; i < MAX_SPI; i++)
_spi[i].construct(i+MAX_SGI+MAX_PPI, Irq::SPI, _pending_list);
bus.add(*this);
}