genode/repos/os/src/server/vmm/spec/arm_v8/gic.h

441 lines
13 KiB
C++

/*
* \brief VMM ARM Generic Interrupt Controller 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.
*/
#ifndef _SRC__SERVER__VMM__GIC_H_
#define _SRC__SERVER__VMM__GIC_H_
#include <mmio.h>
#include <base/env.h>
#include <drivers/defs/arm_v7.h>
#include <util/list.h>
#include <util/register.h>
#include <util/reconstructible.h>
namespace Vmm { class Gic; }
class Vmm::Gic : public Vmm::Mmio_device
{
public:
enum Irqs {
MAX_SGI = 16,
MAX_PPI = 16,
MAX_SPI = 992,
MAX_IRQ = 1020,
SPURIOUS = 1023,
};
class Irq : public Genode::List<Irq>::Element
{
public:
struct List : Genode::List<Irq>
{
void insert(Irq & irq);
Irq * highest_enabled();
};
struct Irq_handler {
virtual void eoi() {};
virtual void enabled() {};
virtual void disabled() {};
};
enum Type { SGI, PPI, SPI };
enum State { INACTIVE, ACTIVE, PENDING, ACTIVE_PENDING };
enum Config { LEVEL, EDGE };
Irq(unsigned num, Type t, List &pending_list);
bool enabled() const;
bool active() const;
bool pending() const;
bool level() const;
unsigned number() const;
Genode::uint8_t priority() const;
Genode::uint8_t target() const;
void enable();
void disable();
void activate();
void deactivate();
void assert();
void deassert();
void level(bool l);
void priority(Genode::uint8_t p);
void target(Genode::uint8_t t);
void handler(Irq_handler & handler);
private:
Irq(Irq const &);
Irq &operator = (Irq const &);
bool _enabled { false };
Type _type { SPI };
State _state { INACTIVE };
Config _config { LEVEL };
unsigned _num { 0 };
Genode::uint8_t _prio { 0 };
Genode::uint8_t _target { 1 };
List & _pending_list;
Irq_handler * _handler { nullptr };
};
struct Irq_reg: Mmio_register
{
unsigned const irq_count;
virtual Register read(Irq & irq) { return 0; }
virtual void write(Irq & irq, Register reg) { }
Register read(Address_range & access, Cpu&) override;
void write(Address_range & access, Cpu&, Register value) override;
Irq_reg(Mmio_register::Name name,
Mmio_register::Type type,
Genode::uint64_t start,
unsigned bits_per_irq,
unsigned irq_count)
: Mmio_register(name, type, start, bits_per_irq*irq_count/8),
irq_count(irq_count) {}
};
class Gicd_banked
{
public:
Irq & irq(unsigned num);
void handle_irq();
bool pending_irq();
Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus);
private:
Cpu & _cpu;
Gic & _gic;
Genode::Constructible<Irq> _sgi[MAX_SGI];
Genode::Constructible<Irq> _ppi[MAX_PPI];
Irq::List _pending_list;
struct Redistributor : Mmio_device
{
unsigned cpu_id;
bool last;
Mmio_register gicr_ctlr { "GICR_CTLR", Mmio_register::RO,
0x0, 4, 0b10010 };
Mmio_register gicr_typer { "GICR_TYPER", Mmio_register::RO,
0x8, 8, (Genode::uint64_t)cpu_id<<32 | cpu_id<<8 | (last ? 1<<4 : 0) };
Mmio_register gicr_waker { "GICR_WAKER", Mmio_register::RO,
0x14, 4, 0 };
Mmio_register gicr_pidr2 { "GICR_PIDR2", Mmio_register::RO,
0xffe8, 4, (3<<4) };
Mmio_register gicr_igroupr0 { "GICR_IGROUPR0", Mmio_register::RO,
0x10080, 4, 0 };
struct Gicr_isenabler0 : Irq_reg
{
Register read(Irq & irq) { return irq.enabled(); }
void write(Irq & irq, Register v) { if (v) irq.enable(); }
Gicr_isenabler0()
: Irq_reg("GICR_ISENABLER0", Mmio_register::RW, 0x10100, 1, 32) {}
} gicr_isenabler0;
struct Gicr_icenabler0 : Irq_reg
{
Register read(Irq & irq) { return irq.enabled(); }
void write(Irq & irq, Register v) { if (v) irq.disable(); }
Gicr_icenabler0()
: Irq_reg("GICR_ICENABLER0", Mmio_register::RW, 0x10180, 1, 32) {}
} gicr_icenabler0;
struct Gicr_ispendr0 : Irq_reg
{
Register read(Irq & irq) { return irq.pending(); }
void write(Irq & irq, Register v) { if (v) irq.assert(); }
Gicr_ispendr0()
: Irq_reg("GICR_ISPENDR0", Mmio_register::RW, 0x10200, 1, 32) {}
} gicr_ispendr0;
struct Gicr_icpendr0 : Irq_reg
{
Register read(Irq & irq) { return irq.pending(); }
void write(Irq & irq, Register v) { if (v) irq.deassert(); }
Gicr_icpendr0()
: Irq_reg("GICR_ICPENDR0", Mmio_register::RW, 0x10280, 1, 32) {}
} gicr_icpendr0;
struct Gicr_isactiver0 : Irq_reg
{
Register read(Irq & irq) { return irq.active(); }
void write(Irq & irq, Register v) { if (v) irq.activate(); }
Gicr_isactiver0()
: Irq_reg("GICR_ISACTIVER0", Mmio_register::RW, 0x10300, 1, 32) {}
} gicr_isactiver0;
struct Gicr_icactiver0 : Irq_reg
{
Register read(Irq & irq) { return irq.active(); }
void write(Irq & irq, Register v) { if (v) irq.deactivate(); }
Gicr_icactiver0()
: Irq_reg("GICR_ICACTIVER0", Mmio_register::RW, 0x10380, 1, 32) {}
} gicr_icactiver0;
struct Gicr_ipriorityr : Irq_reg
{
Register read(Irq & irq) { return irq.priority(); }
void write(Irq & irq, Register v) { irq.priority(v); }
Gicr_ipriorityr()
: Irq_reg("GICR_IPRIORITYR", Mmio_register::RW, 0x10400, 8, 32) {}
} gicr_ipriorityr;
struct Gicr_icfgr : Irq_reg
{
Register read(Irq & irq) { return irq.level() ? 0 : 1; }
void write(Irq & irq, Register v) { irq.level(!v); }
Gicr_icfgr()
: Irq_reg("GICR_ICFGR", Mmio_register::RW, 0x10c00, 8, 32) {}
} gicr_icfgr;
Redistributor(const Genode::uint64_t addr,
const Genode::uint64_t size,
unsigned cpu_id,
bool last)
: Mmio_device("GICR", addr, size), cpu_id(cpu_id), last(last)
{
add(gicr_ctlr);
add(gicr_typer);
add(gicr_waker);
add(gicr_pidr2);
add(gicr_igroupr0);
add(gicr_isenabler0);
add(gicr_icenabler0);
add(gicr_ispendr0);
add(gicr_icpendr0);
add(gicr_isactiver0);
add(gicr_icactiver0);
add(gicr_ipriorityr);
add(gicr_icfgr);
}
};
Genode::Constructible<Redistributor> _rdist;
};
Gic(const char * const name,
const Genode::uint64_t addr,
const Genode::uint64_t size,
Mmio_bus & bus,
Genode::Env & env);
private:
friend struct Gicd_banked;
Genode::Constructible<Irq> _spi[MAX_SPI];
Irq::List _pending_list;
unsigned _cpu_cnt { 2 }; /* FIXME: smp support */
unsigned _version { 3 }; /* FIXME: version support */
struct Gicd_ctlr : Genode::Register<32>, Mmio_register
{
struct Enable : Bitfield<0, 1> {};
struct Disable : Bitfield<6, 1> {};
void write(Address_range & access, Cpu & cpu,
Mmio_register::Register value) override
{
access_t v = value;
Disable::set(v, 0);
Mmio_register::write(access, cpu, v);
}
Gicd_ctlr()
: Mmio_register("GICD_CTLR", Mmio_register::RW, 0, 4, 0) {}
} _ctrl;
struct Gicd_typer : Genode::Register<32>, Mmio_register
{
struct It_lines_number : Bitfield<0, 5> {};
struct Cpu_number : Bitfield<5, 3> {};
struct Id_bits : Bitfield<19, 5> {};
Gicd_typer(unsigned cpus)
: Mmio_register("GICD_TYPER", Mmio_register::RO, 0x4, 4,
It_lines_number::bits(31) | Cpu_number::bits(cpus-1) | Id_bits::bits(9)) {}
} _typer { _cpu_cnt };
struct Gicd_iidr : Genode::Register<32>, Mmio_register
{
struct Implementer : Bitfield<0, 12> {};
struct Revision : Bitfield<12, 4> {};
struct Variant : Bitfield<16, 4> {};
struct Product_id : Bitfield<24, 8> {};
Gicd_iidr()
: Mmio_register("GICD_IIDR", Mmio_register::RO, 0x8, 4, 0x123) {}
} _iidr;
struct Gicd_igroupr : Irq_reg
{
Gicd_igroupr()
: Irq_reg("GICD_IGROUPR", Mmio_register::RW, 0x80, 1, 1024) {}
} _igroupr;
struct Gicd_isenabler : Irq_reg
{
Register read(Irq & irq) { return irq.enabled(); }
void write(Irq & irq, Register v) { if (v) irq.enable(); }
Gicd_isenabler()
: Irq_reg("GICD_ISENABLER", Mmio_register::RW, 0x100, 1, 1024) {}
} _isenabler;
struct Gicd_icenabler : Irq_reg
{
Register read(Irq & irq) { return irq.enabled(); }
void write(Irq & irq, Register v) { if (v) irq.disable(); }
Gicd_icenabler()
: Irq_reg("GICD_ICENABLER", Mmio_register::RW, 0x180, 1, 1024) {}
} _csenabler;
struct Gicd_ispendr : Irq_reg
{
Register read(Irq & irq) { return irq.pending(); }
void write(Irq & irq, Register v) { if (v) irq.assert(); }
Gicd_ispendr()
: Irq_reg("GICD_ISPENDR", Mmio_register::RW, 0x200, 1, 1024) {}
} _ispendr;
struct Gicd_icpendr : Irq_reg
{
Register read(Irq & irq) { return irq.pending(); }
void write(Irq & irq, Register v) { if (v) irq.deassert(); }
Gicd_icpendr()
: Irq_reg("GICD_ICPENDR", Mmio_register::RW, 0x280, 1, 1024) {}
} _icpendr;
struct Gicd_isactiver : Irq_reg
{
Register read(Irq & irq) { return irq.active(); }
void write(Irq & irq, Register v) { if (v) irq.activate(); }
Gicd_isactiver()
: Irq_reg("GICD_ISACTIVER", Mmio_register::RW, 0x300, 1, 1024) {}
} _isactiver;
struct Gicd_icactiver : Irq_reg
{
Register read(Irq & irq) { return irq.active(); }
void write(Irq & irq, Register v) { if (v) irq.deactivate(); }
Gicd_icactiver()
: Irq_reg("GICD_ICACTIVER", Mmio_register::RW, 0x380, 1, 1024) {}
} _icactiver;
struct Gicd_ipriorityr : Irq_reg
{
Register read(Irq & irq) { return irq.priority(); }
void write(Irq & irq, Register v) { irq.priority(v); }
Gicd_ipriorityr()
: Irq_reg("GICD_IPRIORITYR", Mmio_register::RW, 0x400, 8, 1024) {}
} _ipriorityr;
struct Gicd_itargetr : Irq_reg
{
Register read(Irq & irq) { return irq.target(); }
void write(Irq & irq, Register v) { irq.target(v); }
Gicd_itargetr()
: Irq_reg("GICD_ITARGETSR", Mmio_register::RW, 0x800, 8, 1024) {}
} _itargetr;
struct Gicd_icfgr : Irq_reg
{
Register read(Irq & irq) { return irq.level() ? 0 : 1; }
void write(Irq & irq, Register v) { irq.level(!v); }
Gicd_icfgr()
: Irq_reg("GICD_ICFGR", Mmio_register::RW, 0xc00, 8, 1024) {}
} _icfgr;
struct Gicd_sgir : Genode::Register<32>, Mmio_register
{
struct Enable : Bitfield<0, 1> {};
struct Disable : Bitfield<6, 1> {};
void write(Address_range & access, Cpu & cpu,
Mmio_register::Register value) override
{
Genode::error("SGIR WRITE ", value);
}
Gicd_sgir()
: Mmio_register("GICD_SGIR", Mmio_register::WO, 0xf00, 4, 0) {}
} _sgir;
struct Gicd_irouter : Irq_reg
{
Register read(Irq &) { return 0x0; } // FIXME smp
void write(Irq &, Register) { }
Gicd_irouter()
: Irq_reg("GICD_IROUTER", Mmio_register::RW, 0x6100, 64, 1024) {}
} _irouter;
/**
* FIXME: missing registers:
* GICD_IGROUP = 0x80,
* GICD_NSACR = 0xe00,
* GICD_SGIR = 0xf00,
* GICD_CPENDSGIR0 = 0xf10,
* GICD_SPENDSGIR0 = 0xf20,
* GICD identification registers 0xfd0...
*/
/**
* Dummy container for holding array of noncopyable objects
* Workaround for gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70395
*/
struct Dummy {
Mmio_register regs[8];
} _reg_container { .regs = {
{ "GICD_PIDR4", Mmio_register::RO, 0xffd0, 4, 0x0 },
{ "GICD_PIDR5", Mmio_register::RO, 0xffd4, 4, 0x0 },
{ "GICD_PIDR6", Mmio_register::RO, 0xffd8, 4, 0x0 },
{ "GICD_PIDR7", Mmio_register::RO, 0xffdc, 4, 0x0 },
{ "GICD_PIDR0", Mmio_register::RO, 0xffe0, 4, 0x492 },
{ "GICD_PIDR1", Mmio_register::RO, 0xffe4, 4, 0xb0 },
{ "GICD_PIDR2", Mmio_register::RO, 0xffe8, 4, (_version<<4) | 0xb },
{ "GICD_PIDR3", Mmio_register::RO, 0xffec, 4, 0x44 }
}};
};
#endif /* _SRC__SERVER__VMM__GIC_H_ */