From: Waldemar Kozaczuk <
jwkoz...@gmail.com>
Committer: WALDEMAR KOZACZUK <
jwkoz...@gmail.com>
Branch: master
aarch64: refactor GICv2 code for introduction of GICv3
This patch refactors existing implementation of the GICv2
driver found under arch/aarch64/gic.** to allow fo upcoming
introduction of GICv3 support.
In essence, we create new base class gic_driver with mostly
virtual functions intended to provide an abstraction of the GIC driver.
We also extract common code into gic_dist class under gic-common.**.
Finally, we move and refactor a little the original GICv2 implementation
found under gic.** to giv-v2.**.
Signed-off-by: Waldemar Kozaczuk <
jwkoz...@gmail.com>
---
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -993,7 +993,8 @@ $(out)/arch/x64/string-ssse3.o: CXXFLAGS += -mssse3
ifeq ($(arch),aarch64)
objects += arch/$(arch)/psci.o
objects += arch/$(arch)/arm-clock.o
-objects += arch/$(arch)/gic.o
+objects += arch/$(arch)/gic-common.o
+objects += arch/$(arch)/gic-v2.o
objects += arch/$(arch)/arch-dtb.o
objects += arch/$(arch)/hypercall.o
objects += arch/$(arch)/memset.o
diff --git a/arch/aarch64/arch-cpu.hh b/arch/aarch64/arch-cpu.hh
--- a/arch/aarch64/arch-cpu.hh
+++ b/arch/aarch64/arch-cpu.hh
@@ -53,7 +53,7 @@ public:
inline void arch_cpu::init_on_cpu()
{
if (this->smp_idx != 0) {
- gic::gic->init_cpu(this->smp_idx);
+ gic::gic->init_on_secondary_cpu(this->smp_idx);
}
}
diff --git a/arch/aarch64/arch-interrupt.hh b/arch/aarch64/arch-interrupt.hh
--- a/arch/aarch64/arch-interrupt.hh
+++ b/arch/aarch64/arch-interrupt.hh
@@ -9,7 +9,7 @@
#define ARCH_INTERRUPT_HH
#include "exceptions.hh"
-#include "gic.hh"
+#include "gic-common.hh"
#include <functional>
diff --git a/arch/aarch64/arch-setup.cc b/arch/aarch64/arch-setup.cc
--- a/arch/aarch64/arch-setup.cc
+++ b/arch/aarch64/arch-setup.cc
@@ -23,6 +23,7 @@
#include "arch-mmu.hh"
#include "arch-dtb.hh"
+#include "gic-v2.hh"
#include "drivers/console.hh"
#include "drivers/pl011.hh"
@@ -122,17 +123,21 @@ void arch_setup_free_memory()
}
#endif
- /* linear_map [TTBR0 - GIC DIST and GIC CPU] */
- u64 dist, cpu;
- size_t dist_len, cpu_len;
- if (!dtb_get_gic_v2(&dist, &dist_len, &cpu, &cpu_len)) {
- abort("arch-setup: failed to get GICv2 information from dtb.\n");
+ //Locate GICv2 information in DTB and construct corresponding GIC driver
+ //and map relevant physical memory
+ u64 dist, cpuif;
+ size_t dist_len, cpuif_len;
+ if (dtb_get_gic_v2(&dist, &dist_len, &cpuif, &cpuif_len)) {
+ gic::gic = new gic::gic_v2_driver(dist, cpuif);
+ /* linear_map [TTBR0 - GIC CPUIF] */
+ mmu::linear_map((void *)cpuif, (mmu::phys)cpuif, cpuif_len, "gic_cpuif", mmu::page_size,
+ mmu::mattr::dev);
+ } else {
+ abort("arch-setup: failed to get GiCv2 information from dtb.\n");
}
- gic::gic = new gic::gic_driver(dist, cpu);
+ /* linear_map [TTBR0 - GIC DIST] */
mmu::linear_map((void *)dist, (mmu::phys)dist, dist_len, "gic_dist", mmu::page_size,
mmu::mattr::dev);
- mmu::linear_map((void *)cpu, (mmu::phys)cpu, cpu_len, "gic_cpu", mmu::page_size,
- mmu::mattr::dev);
#if CONF_drivers_pci
if (!opt_pci_disabled) {
diff --git a/arch/aarch64/exceptions.cc b/arch/aarch64/exceptions.cc
--- a/arch/aarch64/exceptions.cc
+++ b/arch/aarch64/exceptions.cc
@@ -37,10 +37,9 @@ interrupt_table::interrupt_table() {
debug_early_entry("interrupt_table::interrupt_table()");
#endif
- gic::gic->init_cpu(0);
- gic::gic->init_dist(0);
+ gic::gic->init_on_primary_cpu();
- this->nr_irqs = gic::gic->nr_irqs;
+ this->nr_irqs = gic::gic->nr_of_irqs();
#if CONF_logger_debug
debug_early("interrupt table: gic driver created.\n");
#endif
@@ -163,7 +162,7 @@ void interrupt(exception_frame* frame)
/* note that special values 1022 and 1023 are used for
group 1 and spurious interrupts respectively. */
- if (irq >= gic::gic->nr_irqs) {
+ if (irq >= gic::gic->nr_of_irqs()) {
debug_early_u64("special InterruptID detected irq=", irq);
} else {
diff --git a/arch/aarch64/exceptions.hh b/arch/aarch64/exceptions.hh
--- a/arch/aarch64/exceptions.hh
+++ b/arch/aarch64/exceptions.hh
@@ -18,7 +18,7 @@
#include <osv/interrupt.hh>
#include <vector>
-#include "gic.hh"
+#include "gic-common.hh"
struct exception_frame {
u64 regs[31];
diff --git a/arch/aarch64/gic-common.cc b/arch/aarch64/gic-common.cc
--- a/arch/aarch64/gic-common.cc
+++ b/arch/aarch64/gic-common.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 Huawei Technologies Duesseldorf GmbH
+ * Copyright (C) 2024 Waldemar Kozaczuk
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#include <osv/mmio.hh>
+
+#include "gic-common.hh"
+
+namespace gic {
+
+u32 gic_dist::read_reg(gicd_reg reg)
+{
+ return mmio_getl((mmioaddr_t)_base + (u32)reg);
+}
+
+void gic_dist::write_reg(gicd_reg reg, u32 value)
+{
+ mmio_setl((mmioaddr_t)_base + (u32)reg, value);
+}
+
+u32 gic_dist::read_reg_at_offset(u32 reg, u32 offset)
+{
+ return mmio_getl((mmioaddr_t)_base + reg + offset);
+}
+
+void gic_dist::write_reg_at_offset(u32 reg, u32 offset, u32 value)
+{
+ mmio_setl((mmioaddr_t)_base + reg + offset, value);
+}
+
+void gic_dist::write_reg64_at_offset(u32 reg, u32 offset, u64 value)
+{
+ mmio_setq((mmioaddr_t)_base + reg + offset, value);
+}
+
+unsigned int gic_dist::read_number_of_interrupts()
+{
+ return ((read_reg(gicd_reg::GICD_TYPER) & 0x1f) + 1) * 32;
+}
+
+class gic_driver *gic;
+}
diff --git a/arch/aarch64/gic-common.hh b/arch/aarch64/gic-common.hh
--- a/arch/aarch64/gic-common.hh
+++ b/arch/aarch64/gic-common.hh
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2014 Huawei Technologies Duesseldorf GmbH
+ * Copyright (C) 2024 Waldemar Kozaczuk
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#ifndef GIC_COMMON_HH
+#define GIC_COMMON_HH
+
+#include <osv/types.h>
+#include <osv/mmu-defs.hh>
+#include <osv/spinlock.h>
+
+#define GIC_MAX_IRQ 1019
+#define GIC_SPI_BASE 32
+#define GIC_PPI_BASE 16
+
+namespace gic {
+
+constexpr int max_nr_irqs = 1020;
+
+enum class gicd_reg : unsigned int {
+ GICD_CTLR = 0x0000, /* Distributor Control Reg */
+ GICD_TYPER = 0x0004, /* Interrupt Controller Type Reg */
+ GICD_IDDR = 0x0008, /* Distributor Implementer Identification Reg */
+ GICD_SGIR = 0x0f00, /* Software Generated Interrupt Register */
+ ICPIDR2 = 0x0fe8, /* Peripheral ID2 Register */
+};
+
+/* the following are groups of 32 x 32bit bitfield registers,
+ with 1 bit/flag assigned to each interrupt (32x32=1024) */
+enum class gicd_reg_irq1 : unsigned int {
+ GICD_IGROUPR = 0x0080, /* Interrupt Group Registers */
+ GICD_ISENABLER = 0x0100, /* Interrupt Set-Enable Regs */
+ GICD_ICENABLER = 0x0180, /* Interrupt Clear-Enable Regs */
+ GICD_ISPENDR = 0x0200, /* Interrupt Set-Pending Regs */
+ GICD_ICPENDR = 0x0280, /* Interrupt Clear-Pending Regs */
+ GICD_ISACTIVER = 0x0300, /* Interrupt Set-Active Regs */
+ GICD_ICACTIVER = 0x0380, /* Interrupt Clear-Active Regs */
+};
+
+/* the following are groups of 64 x 32bit bitfield registers,
+ with 2 bits assigned to each interrupt (64x32/2=1024) */
+enum class gicd_reg_irq2 : unsigned int {
+ GICD_ICFGR = 0x0c00, /* Interrupt Configuration Regs */
+ GICD_NSACR = 0x0e00, /* Non-secure Access Control Regs */
+};
+
+/* the following are groups of 256 x 32bit bitfield registers, byte access,
+ with 1 byte assigned to each interrupt (256x32/8=1024) */
+enum class gicd_reg_irq8 : unsigned int {
+ GICD_IPRIORITYR = 0x0400, /* Interrupt Priority Regs */
+ GICD_ITARGETSR = 0x0800, /* Interrupt Processor Target Regs */
+};
+
+enum class sgi_filter : unsigned int {
+ SGI_TARGET_LIST = 0,
+ SGI_TARGET_ALL_BUT_SELF = 1,
+ SGI_TARGET_SELF = 2,
+};
+
+enum class irq_type : unsigned int {
+ IRQ_TYPE_LEVEL = 0,
+ IRQ_TYPE_EDGE = 1,
+};
+
+// Interrupt Group Registers, GICD_IGROUPRn
+// These registers provide a status bit for each interrupt supported by
+// the GIC. Each bit controls whether the corresponding interrupt is in
+// Group 0 or Group 1
+#define GICD_I_PER_IGROUPRn 32
+#define GICD_DEF_IGROUPRn 0xffffffff
+
+#define GICD_ICFGR_DEF_TYPE 0
+#define GICD_I_PER_ICFGRn 16
+
+#define GICD_IPRIORITY_DEF 0xc0c0c0c0
+
+#define GICD_DEF_ICACTIVERn 0xffffffff
+#define GICD_DEF_ICENABLERn 0xffffffff
+
+#define GICD_I_PER_ICACTIVERn 32
+#define GICD_I_PER_IPRIORITYn 4
+
+#define GICD_I_PER_ICENABLERn 32
+#define GICD_I_PER_ISENABLERn 32
+
+/* GIC Distributor Interface */
+class gic_dist {
+protected:
+ gic_dist(mmu::phys b) : _base(b) {}
+
+public:
+ u32 read_reg(gicd_reg r);
+ void write_reg(gicd_reg r, u32 v);
+
+ u32 read_reg_at_offset(u32 reg, u32 offset);
+ void write_reg_at_offset(u32 reg, u32 offset, u32 value);
+
+ void write_reg64_at_offset(u32 reg, u32 offset, u64 v);
+
+ unsigned int read_number_of_interrupts();
+
+protected:
+ mmu::phys _base;
+};
+
+/* Base class with mostly virtual functions intended to provide
+ abstraction of the GIC driver. The implementation of specific
+ version (GICv2 or GICv3) should be provided by subclasses like
+ gic_v2_driver. */
+class gic_driver {
+public:
+ virtual ~gic_driver() {}
+
+ virtual void init_on_primary_cpu() = 0;
+ virtual void init_on_secondary_cpu(int smp_idx) = 0;
+
+ virtual void mask_irq(unsigned int id) = 0;
+ virtual void unmask_irq(unsigned int id) = 0;
+
+ virtual void set_irq_type(unsigned int id, irq_type type) = 0;
+
+ virtual void send_sgi(sgi_filter filter, int smp_idx, unsigned int vector) = 0;
+
+ virtual unsigned int ack_irq() = 0;
+ virtual void end_irq(unsigned int iar) = 0;
+
+ unsigned int nr_of_irqs() { return _nr_irqs; }
+protected:
+ unsigned int _nr_irqs;
+ spinlock_t gic_lock;
+};
+
+extern class gic_driver *gic;
+
+}
+
+#endif
diff --git a/arch/aarch64/gic-v2.cc b/arch/aarch64/gic-v2.cc
--- a/arch/aarch64/gic-v2.cc
+++ b/arch/aarch64/gic-v2.cc
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2014 Huawei Technologies Duesseldorf GmbH
+ * Copyright (C) 2024 Waldemar Kozaczuk
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#include <osv/mmio.hh>
+#include <osv/irqlock.hh>
+
+#include "processor.hh"
+#include "gic-v2.hh"
+#include "arm-clock.hh"
+
+namespace gic {
+
+void gic_v2_dist::disable()
+{
+ unsigned int gicd_ctlr = read_reg(gicd_reg::GICD_CTLR);
+ gicd_ctlr &= ~1;
+ write_reg(gicd_reg::GICD_CTLR, gicd_ctlr);
+}
+
+void gic_v2_dist::enable()
+{
+ unsigned int gicd_ctlr = read_reg(gicd_reg::GICD_CTLR);
+ gicd_ctlr |= 1;
+ write_reg(gicd_reg::GICD_CTLR, gicd_ctlr);
+}
+
+void gic_v2_dist::write_reg_grp(gicd_reg_irq1 reg, unsigned int irq, u8 value)
+{
+ assert(value <= 1 && irq < max_nr_irqs);
+
+ unsigned int offset = (u32)reg + (irq / 32) * 4;
+ unsigned int shift = irq % 32;
+ unsigned int old = mmio_getl((mmioaddr_t)_base + offset);
+
+ old &= ~(1 << shift);
+ old |= value << shift;
+
+ mmio_setl((mmioaddr_t)_base + offset, old);
+}
+
+void gic_v2_dist::write_reg_grp(gicd_reg_irq2 reg, unsigned int irq, u8 value)
+{
+ assert(value <= 3 && irq < max_nr_irqs);
+
+ unsigned int offset = (u32)reg + (irq / 16) * 4;
+ unsigned int shift = ((irq % 16) * 2);
+ unsigned int old = mmio_getl((mmioaddr_t)_base + offset);
+
+ old &= ~(0x3 << shift);
+ old |= value << shift;
+
+ mmio_setl((mmioaddr_t)_base + offset, old);
+}
+
+u32 gic_v2_cpu::read_reg(gicc_reg reg)
+{
+ return mmio_getl((mmioaddr_t)_base + (u32)reg);
+}
+
+void gic_v2_cpu::write_reg(gicc_reg reg, u32 value)
+{
+ mmio_setl((mmioaddr_t)_base + (u32)reg, value);
+}
+
+void gic_v2_cpu::enable()
+{
+ unsigned int gicc_ctlr = read_reg(gicc_reg::GICC_CTLR);
+ gicc_ctlr |= 1;
+ write_reg(gicc_reg::GICC_CTLR, gicc_ctlr);
+}
+
+/* to be called only from the boot CPU */
+void gic_v2_driver::init_dist()
+{
+ _gicd.disable();
+
+ _nr_irqs = _gicd.read_number_of_interrupts();
+ if (_nr_irqs > GIC_MAX_IRQ) {
+ _nr_irqs = GIC_MAX_IRQ + 1;
+ }
+
+ // Send all SPIs to the cpu 0
+ u32 cpu_0_mask = 1U;
+ cpu_0_mask |= cpu_0_mask << 8; /* duplicate pattern (x2) */
+ cpu_0_mask |= cpu_0_mask << 16; /* duplicate pattern (x4) */
+ for (unsigned int i = GIC_SPI_BASE; i < _nr_irqs; i += 4) {
+ _gicd.write_reg_at_offset((u32)gicd_reg_irq8::GICD_ITARGETSR, i, cpu_0_mask);
+ }
+
+ // Set all SPIs to level-sensitive at the start
+ for (unsigned int i = GIC_SPI_BASE; i < _nr_irqs; i += 16)
+ _gicd.write_reg_at_offset((u32)gicd_reg_irq2::GICD_ICFGR, i / 4, GICD_ICFGR_DEF_TYPE);
+
+ // Set priority
+ for (unsigned int i = GIC_SPI_BASE; i < _nr_irqs; i += 4) {
+ _gicd.write_reg_at_offset((u32)gicd_reg_irq8::GICD_IPRIORITYR, i, GICD_IPRIORITY_DEF);
+ }
+
+ // Deactivate and disable all SPIs
+ for (unsigned int i = GIC_SPI_BASE; i < _nr_irqs; i += 32) {
+ _gicd.write_reg_at_offset((u32)gicd_reg_irq1::GICD_ICACTIVER, i / 8, GICD_DEF_ICACTIVERn);
+ _gicd.write_reg_at_offset((u32)gicd_reg_irq1::GICD_ICENABLER, i / 8, GICD_DEF_ICENABLERn);
+ }
+
+ _gicd.enable();
+}
+
+void gic_v2_driver::init_cpuif(int smp_idx)
+{
+#if CONF_logger_debug
+ debug_early_entry("gic_driver::init_cpuif()");
+#endif
+
+ /* set priority mask register for CPU */
+ _gicc.write_reg(gicc_reg::GICC_PMR, 0xf0);
+
+ /* disable all PPI interrupts */
+ _gicd.write_reg_at_offset((u32)gicd_reg_irq1::GICD_ICENABLER, 0, 0xffff0000);
+
+ /* enable all SGI interrupts */
+ _gicd.write_reg_at_offset((u32)gicd_reg_irq1::GICD_ISENABLER, 0, 0x0000ffff);
+
+ /* set priority on SGI/PPI (at least bits [7:4] must be implemented) */
+ for (int i = 0; i < GIC_SPI_BASE; i += 4) {
+ _gicd.write_reg_at_offset((u32)gicd_reg_irq8::GICD_IPRIORITYR, i, GICD_IPRIORITY_DEF);
+ }
+
+ /* enable CPU interface */
+ _gicc.enable();
+
+ /* enable CPU clock timer PPI interrupt on non-primary CPUs */
+ if (smp_idx) {
+ _gicd.write_reg_grp(gicd_reg_irq1::GICD_ISENABLER, get_timer_irq_id(), 1);
+ }
+
+#if CONF_logger_debug
+ debug_early("CPU interface enabled.\n");
+#endif
+}
+
+void gic_v2_driver::mask_irq(unsigned int id)
+{
+ WITH_LOCK(gic_lock) {
+ _gicd.write_reg_grp(gicd_reg_irq1::GICD_ICENABLER, id, 1);
+ }
+}
+
+void gic_v2_driver::unmask_irq(unsigned int id)
+{
+ WITH_LOCK(gic_lock) {
+ _gicd.write_reg_grp(gicd_reg_irq1::GICD_ISENABLER, id, 1);
+ }
+}
+
+void gic_v2_driver::set_irq_type(unsigned int id, irq_type type)
+{
+ WITH_LOCK(gic_lock) {
+ _gicd.write_reg_grp(gicd_reg_irq2::GICD_ICFGR, id, (u32)type << 1);
+ }
+}
+
+/* send software-generated interrupt to other cpus; vector is [0..15]
+ * GICD_SGIR distributor register:
+ *
+ * 31 26 | 25 24 | 23 16 | 15 | 14 4 | 3 0
+ * reserved | filter | cpulist | NSATT | reserved | INTID
+ */
+void gic_v2_driver::send_sgi(sgi_filter filter, int smp_idx, unsigned int vector)
+{
+ u32 sgir = 0;
+ assert(vector <= 0x0f);
+ irq_save_lock_type irq_lock;
+
+ //We disable interrupts before taking a lock to prevent scenarios
+ //when interrupt arrives after gic_lock is taken and interrupt handler
+ //ends up calling send_sgi() (nested example) and stays spinning forever
+ //in attempt to take a lock again
+ WITH_LOCK(irq_lock) {
+ WITH_LOCK(gic_lock) {
+ switch (filter) {
+ case sgi_filter::SGI_TARGET_LIST:
+ sgir = 1U << (((u32)smp_idx) + 16u);
+ break;
+ case sgi_filter::SGI_TARGET_ALL_BUT_SELF:
+ sgir = 1 << 24u;
+ break;
+ case sgi_filter::SGI_TARGET_SELF:
+ sgir = 2 << 24u;
+ break;
+ }
+ asm volatile ("dmb ishst");
+ _gicd.write_reg(gicd_reg::GICD_SGIR, sgir | vector);
+ }
+ }
+}
+
+unsigned int gic_v2_driver::ack_irq(void)
+{
+ return _gicc.read_reg(gicc_reg::GICC_IAR);
+}
+
+void gic_v2_driver::end_irq(unsigned int iar)
+{
+ _gicc.write_reg(gicc_reg::GICC_EOIR, iar);
+}
+
+}
diff --git a/arch/aarch64/gic-v2.hh b/arch/aarch64/gic-v2.hh
--- a/arch/aarch64/gic-v2.hh
+++ b/arch/aarch64/gic-v2.hh
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 Huawei Technologies Duesseldorf GmbH
+ * Copyright (C) 2024 Waldemar Kozaczuk
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#ifndef GIC_V2_HH
+#define GIC_V2_HH
+
+#include "gic-common.hh"
+
+namespace gic {
+
+class gic_v2_dist : public gic_dist {
+public:
+ gic_v2_dist(mmu::phys b) : gic_dist(b) {}
+
+ void enable();
+ void disable();
+
+ void write_reg_grp(gicd_reg_irq1, unsigned int irq, u8 value);
+ void write_reg_grp(gicd_reg_irq2, unsigned int irq, u8 value);
+};
+
+enum class gicc_reg : unsigned int {
+ GICC_CTLR = 0x0000, /* CPU Interface Control Reg */
+ GICC_PMR = 0x0004, /* Interrupt Priority Mask Reg */
+ GICC_BPR = 0x0008, /* Binary Point Reg */
+ GICC_IAR = 0x000c, /* Interrupt Acklowledge Reg */
+ GICC_EOIR = 0x0010, /* End of Interrupt Reg */
+ GICC_RPR = 0x0014, /* Running Priority Reg */
+ GICC_HPPIR = 0x0018, /* Highest Priority Pending Interrupt Reg */
+ GICC_ABPR = 0x001c, /* Aliased Binary Point Reg */
+ GICC_AIAR = 0x0020, /* Aliased Interrupt Acknowledge Reg */
+ GICC_AEOIR = 0x0024, /* Aliased End of Interrupt Reg */
+ GICC_AHPPIR = 0x0028, /* Aliased Highest Prio Pending Interrupt Reg */
+ GICC_APR = 0x00d0, /* Active Priorities Registers */
+ GICC_NSAPR = 0x00e0, /* Non-secure APR */
+ GICC_IIDR = 0x00fc, /* CPU Interface Identification Reg */
+ GICC_DIR = 0x1000 /* Deactivate Interrupt Reg */
+ /* Note: we will not use GICC_DIR (the two-step mechanism) */
+};
+
+/* GIC CPU Interface */
+class gic_v2_cpu {
+public:
+ gic_v2_cpu(mmu::phys b) : _base(b) {}
+
+ u32 read_reg(gicc_reg r);
+ void write_reg(gicc_reg r, u32 value);
+
+ void enable();
+protected:
+ mmu::phys _base;
+};
+
+class gic_v2_driver : public gic_driver {
+public:
+ gic_v2_driver(mmu::phys d, mmu::phys c) : _gicd(d), _gicc(c) {}
+
+ virtual void init_on_primary_cpu()
+ {
+ init_dist();
+ init_cpuif(0);
+ }
+
+ virtual void init_on_secondary_cpu(int smp_idx) { init_cpuif(smp_idx); }
+
+ virtual void mask_irq(unsigned int id);
+ virtual void unmask_irq(unsigned int id);
+
+ virtual void set_irq_type(unsigned int id, irq_type type);
+
+ virtual void send_sgi(sgi_filter filter, int smp_idx, unsigned int vector);
+
+ virtual unsigned int ack_irq();
+ virtual void end_irq(unsigned int iar);
+private:
+ void init_dist();
+ void init_cpuif(int smp_idx);
+
+ gic_v2_dist _gicd;
+ gic_v2_cpu _gicc;
+};
+
+}
+#endif
diff --git a/arch/aarch64/gic.cc b/arch/aarch64/gic.cc
--- a/arch/aarch64/gic.cc
+++ b/arch/aarch64/gic.cc
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2014 Huawei Technologies Duesseldorf GmbH
- *
- * This work is open source software, licensed under the terms of the
- * BSD license as described in the LICENSE file in the top-level directory.
- */
-
-#include <osv/mutex.h>
-#include <osv/debug.hh>
-#include <osv/mmio.hh>
-#include <osv/irqlock.hh>
-
-#include "processor.hh"
-
-#include "gic.hh"
-#include "arm-clock.hh"
-
-namespace gic {
-
-class gic_driver *gic;
-
-void gic_driver::init_cpu(int smp_idx)
-{
- /* GICD_ITARGETSR[0..7] are "special" read-only registers
- which allow us to read our own target mask.
- Since PPIs are by definition targeted at just "self",
- and the registers are banked for each cpu target,
- we logically start looking from IRQ 16 to get the mask.
- */
-#if CONF_logger_debug
- debug_early_entry("gic_driver::init_cpu()");
-#endif
-
- assert(smp_idx < max_cpu_if);
- unsigned char my_mask = 0;
-
- for (int i = 16; i < 32; i++) { /* check PPIs target */
- my_mask = this->gicd.read_reg_grp(gicd_reg_irq8::GICD_ITARGETSR, i);
- if (my_mask) {
- this->cpu_targets[smp_idx] = my_mask;
- break;
- }
- }
-
- if (!my_mask) {
-#if CONF_logger_debug
- debug_early("gic: failed to read cpu mask, assuming uniprocessor\n");
-#endif
- this->cpu_targets[smp_idx] = 0;
- }
-
- /* set priority mask register for CPU */
- for (int i = 0; i < 32; i += 4) {
- this->gicc.write_reg(gicc_reg::GICC_PMR, 0xf0);
- }
-
- /* enable CPU interface */
- unsigned int gicc_ctlr = this->gicc.read_reg(gicc_reg::GICC_CTLR);
- gicc_ctlr |= 1;
- this->gicc.write_reg(gicc_reg::GICC_CTLR, gicc_ctlr);
-
- /* enable CPU clock timer PPI interrupt on non-primary CPUs */
- if (smp_idx) {
- this->gicd.write_reg_grp(gicd_reg_irq1::GICD_ISENABLER, get_timer_irq_id(), 1);
- }
-
-#if CONF_logger_debug
- debug_early("CPU interface enabled.\n");
-#endif
-}
-
-/* to be called only from the boot CPU */
-void gic_driver::init_dist(int smp_idx)
-{
-#if CONF_logger_debug
- debug_early_entry("gic_driver::init_dist()");
-#endif
- assert(smp_idx < max_cpu_if);
-
- /* disable first */
- unsigned int gicd_ctlr;
- gicd_ctlr = this->gicd.read_reg(gicd_reg::GICD_CTLR);
- gicd_ctlr &= ~1;
- this->gicd.write_reg(gicd_reg::GICD_CTLR, gicd_ctlr);
-
- /* note: number of CPU interfaces could be read from here as well */
- this->nr_irqs = ((this->gicd.read_reg(gicd_reg::GICD_TYPER) & 0x1f)
- + 1) * 32;
-#if CONF_logger_debug
- debug_early_u64("number of supported IRQs: ", (u64)this->nr_irqs);
-#endif
-
- /* set all SPIs to level-sensitive at the start */
- for (unsigned int i = 32; i < this->nr_irqs; i += 16)
- this->gicd.write_reg_raw((u32)gicd_reg_irq2::GICD_ICFGR, i / 4, 0);
-
- unsigned int mask = this->cpu_targets[smp_idx];
- if (mask) {
- mask |= mask << 8; /* duplicate pattern (x2) */
- mask |= mask << 16; /* duplicate pattern (x4) */
- }
-
- /* send all SPIs to this only, set priority */
- for (unsigned int i = 32; i < this->nr_irqs; i += 4) {
- if (mask) {
- this->gicd.write_reg_raw((u32)gicd_reg_irq8::GICD_ITARGETSR, i,
- mask);
- }
- this->gicd.write_reg_raw((u32)gicd_reg_irq8::GICD_IPRIORITYR, i,
- 0xc0c0c0c0);
- }
-
- /* disable all SPIs */
- for (unsigned int i = 32; i < this->nr_irqs; i += 32)
- this->gicd.write_reg_raw((u32)gicd_reg_irq1::GICD_ICENABLER, i / 8,
- 0xffffffff);
-
- /* disable all PPI interrupts */
- this->gicd.write_reg_raw((u32)gicd_reg_irq1::GICD_ICENABLER, 0,
- 0xffff0000);
- /* enable all SGI interrupts */
- this->gicd.write_reg_raw((u32)gicd_reg_irq1::GICD_ISENABLER, 0,
- 0x0000ffff);
- /* set priority on SGI/PPI (at least bits [7:4] must be implemented) */
- for (int i = 0; i < 32; i += 4) {
- this->gicd.write_reg_raw((u32)gicd_reg_irq8::GICD_IPRIORITYR, i,
- 0xc0c0c0c0);
- }
- /* enable distributor interface */
- gicd_ctlr |= 1;
- this->gicd.write_reg(gicd_reg::GICD_CTLR, gicd_ctlr);
-}
-
-void gic_driver::mask_irq(unsigned int id)
-{
- WITH_LOCK(gic_lock) {
- this->gicd.write_reg_grp(gicd_reg_irq1::GICD_ICENABLER, id, 1);
- }
-}
-
-void gic_driver::unmask_irq(unsigned int id)
-{
- WITH_LOCK(gic_lock) {
- this->gicd.write_reg_grp(gicd_reg_irq1::GICD_ISENABLER, id, 1);
- }
-}
-
-void gic_driver::set_irq_type(unsigned int id, irq_type type)
-{
- WITH_LOCK(gic_lock) {
- this->gicd.write_reg_grp(gicd_reg_irq2::GICD_ICFGR, id, (u32)type << 1);
- }
-}
-
-/* send software-generated interrupt to other cpus; vector is [0..15]
- * GICD_SGIR distributor register:
- *
- * 31 26 | 25 24 | 23 16 | 15 | 14 4 | 3 0
- * reserved | filter | cpulist | NSATT | reserved | INTID
- */
-void gic_driver::send_sgi(sgi_filter filter, int smp_idx, unsigned int vector)
-{
- u32 sgir = 0;
- assert(smp_idx < max_cpu_if);
- assert(vector <= 0x0f);
- irq_save_lock_type irq_lock;
-
- WITH_LOCK(irq_lock) {
- WITH_LOCK(gic_lock) {
- switch (filter) {
- case sgi_filter::SGI_TARGET_LIST:
- sgir = cpu_targets[smp_idx] << 16u;
- break;
- case sgi_filter::SGI_TARGET_ALL_BUT_SELF:
- sgir = 1 << 24u;
- break;
- case sgi_filter::SGI_TARGET_SELF:
- sgir = 2 << 24u;
- break;
- }
- asm volatile ("dmb ishst");
- this->gicd.write_reg(gicd_reg::GICD_SGIR, sgir | vector);
- }
- }
-}
-
-unsigned int gic_driver::ack_irq(void)
-{
- unsigned int iar;
- iar = this->gicc.read_reg(gicc_reg::GICC_IAR);
- return iar;
-}
-
-void gic_driver::end_irq(unsigned int iar)
-{
- this->gicc.write_reg(gicc_reg::GICC_EOIR, iar);
-}
-
-unsigned int gic_dist::read_reg(gicd_reg reg)
-{
- return mmio_getl((mmioaddr_t)this->base + (u32)reg);
-}
-
-void gic_dist::write_reg(gicd_reg reg, unsigned int value)
-{
- mmio_setl((mmioaddr_t)this->base + (u32)reg, value);
-}
-
-unsigned int gic_dist::read_reg_raw(unsigned int reg, unsigned int offset)
-{
- return mmio_getl((mmioaddr_t)this->base + (u32)reg + offset);
-}
-
-void gic_dist::write_reg_raw(unsigned int reg, unsigned int offset,
- unsigned int value)
-{
- mmio_setl((mmioaddr_t)this->base + (u32)reg + offset, value);
-}
-
-unsigned char gic_dist::read_reg_grp(gicd_reg_irq1 reg, unsigned int irq)
-{
- assert(irq < max_nr_irqs);
-
- unsigned int offset = (u32)reg + (irq / 32) * 4;
- unsigned int mask = 1 << (irq % 32);
-
- return (mmio_getl((mmioaddr_t)this->base + offset) & mask) ? 1 : 0;
-}
-
-void gic_dist::write_reg_grp(gicd_reg_irq1 reg, unsigned int irq,
- unsigned char value)
-{
- assert(value <= 1 && irq < max_nr_irqs);
-
- unsigned int offset = (u32)reg + (irq / 32) * 4;
- unsigned int shift = irq % 32;
- unsigned int old = mmio_getl((mmioaddr_t)this->base + offset);
-
- old &= ~(1 << shift);
- old |= value << shift;
-
- mmio_setl((mmioaddr_t)this->base + offset, old);
-}
-
-unsigned char gic_dist::read_reg_grp(gicd_reg_irq2 reg, unsigned int irq)
-{
- assert(irq < max_nr_irqs);
-
- unsigned int offset = (u32)reg + (irq / 16) * 4;
- unsigned int shift = ((irq % 16) * 2);
- unsigned int old = mmio_getl((mmioaddr_t)this->base + offset);
-
- return (old >> shift) & 0x3;
-}
-
-void gic_dist::write_reg_grp(gicd_reg_irq2 reg, unsigned int irq,
- unsigned char value)
-{
- assert(value <= 3 && irq < max_nr_irqs);
-
- unsigned int offset = (u32)reg + (irq / 16) * 4;
- unsigned int shift = ((irq % 16) * 2);
- unsigned int old = mmio_getl((mmioaddr_t)this->base + offset);
-
- old &= ~(0x3 << shift);
- old |= value << shift;
-
- mmio_setl((mmioaddr_t)this->base + offset, old);
-}
-
-/* spec explicitly mentions that this class of registers is byte-accessible */
-unsigned char gic_dist::read_reg_grp(gicd_reg_irq8 reg, unsigned int irq)
-{
- assert(irq < max_nr_irqs);
-
- unsigned int offset = (u32)reg + irq;
- return mmio_getb((mmioaddr_t)this->base + offset);
-}
-
-void gic_dist::write_reg_grp(gicd_reg_irq8 reg, unsigned int irq,
- unsigned char value)
-{
- assert(irq < max_nr_irqs);
-
- unsigned int offset = (u32)reg + irq;
- mmio_setb((mmioaddr_t)this->base + offset, value);
-}
-
-unsigned int gic_cpu::read_reg(gicc_reg r)
-{
- return mmio_getl((mmioaddr_t)this->base + (u32)r);
-}
-
-void gic_cpu::write_reg(gicc_reg r, unsigned int value)
-{
- mmio_setl((mmioaddr_t)this->base + (u32)r, value);
-}
-
-}
diff --git a/arch/aarch64/gic.hh b/arch/aarch64/gic.hh
--- a/arch/aarch64/gic.hh
+++ b/arch/aarch64/gic.hh
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2014 Huawei Technologies Duesseldorf GmbH
- *
- * This work is open source software, licensed under the terms of the
- * BSD license as described in the LICENSE file in the top-level directory.
- */
-
-#ifndef GIC_HH
-#define GIC_HH
-
-#include <osv/types.h>
-#include <osv/mmu-defs.hh>
-#include <osv/spinlock.h>
-
-/* This is GICv2. Revisit for v3 if/when needed. */
-namespace gic {
-
-constexpr int max_cpu_if = 8;
-constexpr int max_nr_irqs = 1020;
-
-enum class gicd_reg : unsigned int {
- GICD_CTLR = 0x000, /* Distributor Control Reg */
- GICD_TYPER = 0x004, /* Interrupt Controller Type Reg */
- GICD_IDDR = 0x008, /* Distributor Implementer Identification Reg */
- GICD_SGIR = 0xf00, /* Software Generated Interrupt Register */
- ICPIDR2 = 0xfe8, /* Peripheral ID2 Register */
-};
-
-/* the following are groups of 32 x 32bit bitfield registers,
- with 1 bit/flag assigned to each interrupt (32x32=1024) */
-enum class gicd_reg_irq1 : unsigned int {
- GICD_IGROUPR = 0x080, /* Interrupt Group Registers */
- GICD_ISENABLER = 0x100, /* Interrupt Set-Enable Regs */
- GICD_ICENABLER = 0x180, /* Interrupt Clear-Enable Regs */
- GICD_ISPENDR = 0x200, /* Interrupt Set-Pending Regs */
- GICD_ICPENDR = 0x280, /* Interrupt Clear-Pending Regs */
- GICD_ISACTIVER = 0x300, /* Interrupt Set-Active Regs */
- GICD_ICACTIVER = 0x380, /* Interrupt Clear-Active Regs */
-};
-
-/* the following are groups of 64 x 32bit bitfield registers,
- with 2 bits assigned to each interrupt (64x32/2=1024) */
-enum class gicd_reg_irq2 : unsigned int {
- GICD_ICFGR = 0xc00, /* Interrupt Configuration Regs */
- GICD_NSACR = 0xe00, /* Non-secure Access Control Regs */
-};
-
-/* the following are groups of 256 x 32bit bitfield registers, byte access,
- with 1 byte assigned to each interrupt (256x32/8=1024) */
-enum class gicd_reg_irq8 : unsigned int {
- GICD_IPRIORITYR = 0x400, /* Interrupt Priority Regs */
- GICD_ITARGETSR = 0x800, /* Interrupt Processor Target Regs */
-};
-
-/* the following are groups of 4 x 32bit bitfield registers, byte access,
- with 1 byte assigned to each of the 16 SGIs (4x32/8=16) */
-enum class gicd_reg_sgi8 : unsigned int {
- GICD_CPENDSGIR = 0xf10,
- GICD_SPENDSGIR = 0xf20,
-};
-
-/* GIC Distributor Interface */
-class gic_dist {
-public:
- gic_dist(mmu::phys b) : base(b) {}
- unsigned int read_reg(gicd_reg r);
- void write_reg(gicd_reg r, unsigned int);
- unsigned int read_reg_raw(unsigned int r, unsigned int);
- void write_reg_raw(unsigned int r, unsigned int, unsigned int);
-
- unsigned char read_reg_grp(gicd_reg_irq1, unsigned int);
- void write_reg_grp(gicd_reg_irq1, unsigned int, unsigned char);
-
- unsigned char read_reg_grp(gicd_reg_irq2, unsigned int);
- void write_reg_grp(gicd_reg_irq2, unsigned int, unsigned char);
-
- unsigned char read_reg_grp(gicd_reg_irq8, unsigned int);
- void write_reg_grp(gicd_reg_irq8, unsigned int, unsigned char);
-
- unsigned char read_reg_grp(gicd_reg_sgi8, unsigned int);
- void write_reg_grp(gicd_reg_sgi8, unsigned int, unsigned char);
-
-protected:
- mmu::phys base;
-};
-
-enum class gicc_reg : unsigned int {
- GICC_CTLR = 0x0000, /* CPU Interface Control Reg */
- GICC_PMR = 0x0004, /* Interrupt Priority Mask Reg */
- GICC_BPR = 0x0008, /* Binary Point Reg */
- GICC_IAR = 0x000c, /* Interrupt Acklowledge Reg */
- GICC_EOIR = 0x0010, /* End of Interrupt Reg */
- GICC_RPR = 0x0014, /* Running Priority Reg */
- GICC_HPPIR = 0x0018, /* Highest Priority Pending Interrupt Reg */
- GICC_ABPR = 0x001c, /* Aliased Binary Point Reg */
- GICC_AIAR = 0x0020, /* Aliased Interrupt Acknowledge Reg */
- GICC_AEOIR = 0x0024, /* Aliased End of Interrupt Reg */
- GICC_AHPPIR = 0x0028, /* Aliased Highest Prio Pending Interrupt Reg */
- GICC_APR = 0x00d0, /* Active Priorities Registers */
- GICC_NSAPR = 0x00e0, /* Non-secure APR */
- GICC_IIDR = 0x00fc, /* CPU Interface Identification Reg */
- GICC_DIR = 0x1000 /* Deactivate Interrupt Reg */
- /* Note: we will not use GICC_DIR (the two-step mechanism) */
-};
-
-/* GIC CPU Interface */
-class gic_cpu {
-public:
- gic_cpu(mmu::phys b) : base(b) {}
- unsigned int read_reg(gicc_reg r);
- void write_reg(gicc_reg r, unsigned int);
-protected:
- mmu::phys base;
-};
-
-enum class sgi_filter : unsigned int {
- SGI_TARGET_LIST = 0,
- SGI_TARGET_ALL_BUT_SELF = 1,
- SGI_TARGET_SELF = 2,
-};
-
-enum class irq_type : unsigned int {
- IRQ_TYPE_LEVEL = 0,
- IRQ_TYPE_EDGE = 1,
-};
-
-class gic_driver {
-public:
- gic_driver(mmu::phys d, mmu::phys c) : gicd(d), gicc(c) {}
-
- void init_cpu(int smp_idx); /* called on each cpu bringup */
- void init_dist(int smp_idx); /* only called by boot cpu once */
-
- void mask_irq(unsigned int id); /* disable a single InterruptID */
- void unmask_irq(unsigned int id); /* enable a single InterruptID */
-
- void set_irq_type(unsigned int id, irq_type type); /* set level or edge */
-
- /* send software-generated interrupt to self, to another cpu,
- * or to all cpus but self; vector must be [0..15]
- */
- void send_sgi(sgi_filter filter, int smp_idx, unsigned int vector);
-
- /* IAR: acknowledge irq, state pending=>active (or active/pending).
- * 31 .. 13 | 12 10 | 9 0 |
- * Reserved | CPUID | Interrupt ID |
- *
- * ack_irq returns the whole IAR register, further processing of result
- * depends on interrupt type.
- */
- unsigned int ack_irq(void);
-
- /* EOIR: inform interface about completion of interrupt processing */
- void end_irq(unsigned int);
-
-public:
- class gic_dist gicd;
- class gic_cpu gicc;
- unsigned int nr_irqs;
-protected:
- /* cpu_targets: our smp cpu index is not necessarily equal
- to the gic interface target. We query the gic to get
- the cpumask corresponding to each smp cpu (gic_init_on_cpu),
- and put the result here at the right smp index.
- Note that for uniprocessor we will have just a zero
- stored in cpu_targets[0] instead. */
- unsigned char cpu_targets[max_cpu_if];
- spinlock_t gic_lock;
-};
-
-/* the gic driver class is created by the interrupt table class */
-extern class gic_driver *gic;
-}
-
-#endif /* GIC_HH */