From dd505edd19a229dec574b956cb4f935293e2dd53 Mon Sep 17 00:00:00 2001 From: Sebastian Sumpf Date: Mon, 24 Jun 2019 18:39:48 +0200 Subject: [PATCH] hw: GICv3 implementation * modern GICv3 implementation * distributor * redistributor * MMIO cpu interface Ref #3426 --- repos/base-hw/src/bootstrap/spec/arm/gicv3.cc | 62 +++++ repos/base-hw/src/core/spec/arm/gicv3.cc | 29 ++ repos/base-hw/src/include/hw/spec/arm/gicv3.h | 262 ++++++++++++++++++ 3 files changed, 353 insertions(+) create mode 100644 repos/base-hw/src/bootstrap/spec/arm/gicv3.cc create mode 100644 repos/base-hw/src/core/spec/arm/gicv3.cc create mode 100644 repos/base-hw/src/include/hw/spec/arm/gicv3.h diff --git a/repos/base-hw/src/bootstrap/spec/arm/gicv3.cc b/repos/base-hw/src/bootstrap/spec/arm/gicv3.cc new file mode 100644 index 000000000..afe8f0e87 --- /dev/null +++ b/repos/base-hw/src/bootstrap/spec/arm/gicv3.cc @@ -0,0 +1,62 @@ +/* + * \brief GICv3 interrupt controller for core + * \author Sebastian Sumpf + * \date 2019-07-08 + */ + +/* + * 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 + +Hw::Pic::Pic() +: _distr(Board::Cpu_mmio::IRQ_CONTROLLER_DISTR_BASE), + _redistr(Board::Cpu_mmio::IRQ_CONTROLLER_REDIST_BASE), + _redistr_sgi(Board::Cpu_mmio::IRQ_CONTROLLER_REDIST_BASE + + Board::Cpu_mmio::IRQ_CONTROLLER_REDIST_SIZE / 2), + _max_irq(_distr.max_irq()) +{ + /* disable device */ + _distr.write(0); + _distr.wait_for_rwp(); + + /* XXX: remove */ + struct Affinity : Genode::Register<64> + { + struct Aff0 : Bitfield<0, 8> { }; + struct Aff1 : Bitfield<8, 8> { }; + struct Aff2 : Bitfield<16, 8> { }; + struct Aff3 : Bitfield<32, 8> { }; + }; + Genode::uint64_t mpidr = 0; + asm volatile ("mrs %0, mpidr_el1" : "=r"(mpidr) : : "memory"); + Affinity::access_t affinity = 0; + Affinity::Aff0::set(affinity, mpidr); + Affinity::Aff1::set(affinity, (mpidr >> 8) & 0xff); + Affinity::Aff2::set(affinity, (mpidr >> 16) & 0xff); + Affinity::Aff3::set(affinity, (mpidr >> 32) & 0xff); + + /* configure every shared peripheral interrupt */ + for (unsigned i = min_spi; i <= _max_irq; i++) { + _distr.write(0, i); + _distr.write(0xa0, i); + _distr.write(1, i); + _distr.write(1, i); + _distr.write(1, i); + + /* XXX remove: route all SPIs to this PE */ + _distr.write(affinity, i); + } + + /* enable device GRP1_NS with affinity */ + Distributor::Ctlr::access_t ctlr = 0; + Distributor::Ctlr::Enable_grp1_a::set(ctlr, 1); + Distributor::Ctlr::Are_ns::set(ctlr, 1); + + _distr.write(ctlr); + _distr.wait_for_rwp(); +} diff --git a/repos/base-hw/src/core/spec/arm/gicv3.cc b/repos/base-hw/src/core/spec/arm/gicv3.cc new file mode 100644 index 000000000..1e0a02c7b --- /dev/null +++ b/repos/base-hw/src/core/spec/arm/gicv3.cc @@ -0,0 +1,29 @@ +/* + * \brief Generic Interrupt Controller version 3 + * \author Sebastian Sumpf + * \date 2019-06-27 + */ + +/* + * 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. + */ + +/* core includes */ +#include +#include + +using namespace Genode; + +Hw::Pic::Pic() +: _distr(Platform::mmio_to_virt(Board::Cpu_mmio::IRQ_CONTROLLER_DISTR_BASE)), + _redistr(Platform::mmio_to_virt(Board::Cpu_mmio::IRQ_CONTROLLER_REDIST_BASE)), + _redistr_sgi(Platform::mmio_to_virt(Board::Cpu_mmio::IRQ_CONTROLLER_REDIST_BASE) + + Board::Cpu_mmio::IRQ_CONTROLLER_REDIST_SIZE / 2), + _max_irq(_distr.max_irq()) +{ + _redistributor_init(); + _cpui.init(); +} diff --git a/repos/base-hw/src/include/hw/spec/arm/gicv3.h b/repos/base-hw/src/include/hw/spec/arm/gicv3.h new file mode 100644 index 000000000..d5e113b08 --- /dev/null +++ b/repos/base-hw/src/include/hw/spec/arm/gicv3.h @@ -0,0 +1,262 @@ +/* + * \brief GICv3 interrupt controller for core + * \author Sebastian Sumpf + * \date 2019-07-08 + */ + +/* + * 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__INCLUDE__HW__SPEC__ARM__GIC_V3_H_ +#define _SRC__INCLUDE__HW__SPEC__ARM__GIC_V3_H_ + +#include + +namespace Hw { class Pic; } + +#define SYSTEM_REGISTER(sz, name, reg, ...) \ + struct name : Genode::Register \ + { \ + static access_t read() \ + { \ + access_t v; \ + asm volatile ("mrs %0, " reg : "=r" (v)); \ + return v; \ + } \ + \ + static void write(access_t const v) { \ + asm volatile ("msr " reg ", %0" :: "r" (v)); } \ + \ + __VA_ARGS__; \ + }; + +class Hw::Pic +{ + protected: + + static constexpr unsigned min_spi = 32; + static constexpr unsigned spurious_id = 1023; + + struct Distributor : Genode::Mmio + { + static constexpr unsigned nr_of_irq = 1024; + + /** + * Control register (secure access) + */ + /* XXX: CAUTION this is different in EL3/EL2/EL1! */ + struct Ctlr : Register<0x0, 32> + { + struct Enable_grp0 : Bitfield<0, 1> { }; + struct Enable_grp1_a : Bitfield<1, 1> { }; + struct Are_ns : Bitfield<5, 1> { }; + struct Rwp : Bitfield<31, 1> { }; + }; + + struct Typer : Register<0x004, 32> { + struct It_lines_number : Bitfield<0,5> { }; }; + + struct Igroup0r : Register_array<0x80, 32, 32*32, 1> { + struct Group1 : Bitfield<0, 1> { }; }; + + /** + * Interrupt Set-Enable register + */ + struct Isenabler : Register_array<0x100, 32, 32*32, 1, true> { + struct Set_enable : Bitfield<0, 1> { }; }; + + /** + * Interrupt clear enable registers + */ + struct Icenabler : Register_array<0x180, 32, 32*32, 1, true> { + struct Clear_enable : Bitfield<0, 1> { }; }; + + /** + * Interrupt clear pending registers + */ + struct Icpendr : Register_array<0x280, 32, 32*32, 1, true> { + struct Clear_pending : Bitfield<0, 1> { }; }; + + /** + * Interrupt priority level registers + */ + struct Ipriorityr : Register_array<0x400, 32, 255*4, 8> { + struct Priority : Bitfield<0, 8> { }; }; + + + struct Icfgr : Register_array<0xc00, 32, 64*16, 2> { + struct Edge_triggered : Bitfield<1, 1> { }; }; + + struct Irouter : Register_array<0x6000, 64, 1020, 64, true> { }; + + void wait_for_rwp() + { + for (unsigned i = 0; i < 1000; i++) + if (read() == 0) + return; + } + + unsigned max_irq() { return 32 * (read() + 1) - 1; } + + + + Distributor(Genode::addr_t const base) : Genode::Mmio(base) + { } + }; + + struct Redistributor : Genode::Mmio + { + + struct Ctlr : Register<0x0, 32> + { + struct Uwp : Bitfield<31, 1> { }; + }; + + Redistributor(Genode::addr_t const base) : Genode::Mmio(base) + { } + + /* wait for upstream writes */ + void wait_for_uwp() + { + for (unsigned i = 0; i < 1000; i++) + if (read() == 0) + return; + } + }; + + struct Redistributor_sgi_ppi : Genode::Mmio + { + struct Igroupr0 : Register<0x80, 32> { }; + + struct Isenabler0 : Register_array<0x100, 32, 32, 1> + { }; + + struct Icenabler0 : Register_array<0x180, 32, 32, 1> + { }; + + struct Icactiver0 : Register<0x380, 32> { }; + + struct Ipriorityr : Register_array<0x400, 32, min_spi, 8> { + struct Priority : Bitfield<0, 8> { }; }; + + struct Icfgr1 : Register<0xc04, 32> { }; + + Redistributor_sgi_ppi(Genode::addr_t const base) : Genode::Mmio(base) + { + } + }; + + struct Cpu_interface + { + SYSTEM_REGISTER(32, Icc_sre_el1, "S3_0_C12_C12_5", + struct Sre : Bitfield<0, 1> { }; + ); + + SYSTEM_REGISTER(32, Icc_iar1_el1, "S3_0_C12_C12_0"); + SYSTEM_REGISTER(32, Icc_br1_el1, "S3_0_C12_C12_3"); + SYSTEM_REGISTER(32, Icc_pmr_el1, "S3_0_C4_C6_0"); + SYSTEM_REGISTER(32, Icc_igrpen1_el1, "S3_0_C12_C12_7"); + SYSTEM_REGISTER(32, Icc_eoir1_el1, "S3_0_C12_C12_1"); + + void init() + { + /* enable register access */ + Icc_sre_el1::access_t sre = Icc_sre_el1::read(); + Icc_sre_el1::Sre::set(sre, 1); + Icc_sre_el1::write(sre); + + /* XXX: check if needed or move somewhere else */ + asm volatile("isb sy" ::: "memory"); + + /* no priority grouping */ + Icc_br1_el1::write(0); + + /* allow all priorities */ + Icc_pmr_el1::write(0xff); + + /* enable GRP1 interrupts */ + Icc_igrpen1_el1::write(1); + + /* XXX: check if needed or move somewhere else */ + asm volatile("isb sy" ::: "memory"); + } + }; + + void _redistributor_init() + { + /* diactivate SGI/PPI */ + _redistr_sgi.write(~0u); + + for (unsigned i = 0; i < min_spi; i++) { + _redistr_sgi.write(0xa0, i); } + + /* set group 1 for all PPI/SGIs */ + _redistr_sgi.write(~0); + + /* disable SGI/PPI */ + _redistr_sgi.write(~0); + + /* set PPIs to level triggered */ + _redistr_sgi.write(0); + + _redistr.wait_for_uwp(); + } + + Distributor _distr; + Redistributor _redistr; + Redistributor_sgi_ppi _redistr_sgi; + Cpu_interface _cpui { }; + + unsigned const _max_irq; + + Cpu_interface::Icc_iar1_el1::access_t _last_iar { spurious_id }; + + bool _valid(unsigned const irq_id) const { return irq_id <= _max_irq; } + + public: + + Pic(); + + enum { IPI = 0 }; + enum { NR_OF_IRQ = Distributor::nr_of_irq }; + + bool take_request(unsigned &irq) + { + _last_iar = Cpu_interface::Icc_iar1_el1::read(); + irq = _last_iar; + + return _valid(irq); + } + + void finish_request() + { + Cpu_interface::Icc_eoir1_el1::write(_last_iar); + _last_iar = spurious_id; + } + + void unmask(unsigned const irq_id, unsigned const /* cpu_id */) + { + if (irq_id < min_spi) { + _redistr_sgi.write(1, irq_id); + } else { + _distr.write(1, irq_id); + } + } + + void mask(unsigned const irq_id) + { + if (irq_id < min_spi) { + _redistr_sgi.write(1, irq_id); + } else { + _distr.write(1, irq_id); + } + } +}; + +#undef SYSTEM_REGISTER + +#endif /* _SRC__INCLUDE__HW__SPEC__ARM__GIC_V3_H_ */