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.
