From: Waldemar Kozaczuk <[email protected]>
Committer: WALDEMAR KOZACZUK <[email protected]>
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 <[email protected]>

---
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 */

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/osv-dev/00000000000007c670060eaf0f93%40google.com.

Reply via email to