RustForge

rust-kernel

Rustbelt: a hobby x86_64 kernel written in Rust

rust-kernel / src / arch / aarch64 / gic.rs
2392 bytes ยท raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//! The Arm GICv2 interrupt controller (QEMU virt's default). Two parts:
//!
//! - the **distributor** (GICD) decides which interrupts are on and where
//!   they go;
//! - the **CPU interface** (GICC) is per CPU: `IAR` says which interrupt
//!   fired (and marks it "being handled"), `EOIR` says we're done.
//!
//! Interrupt numbers ("INTIDs"): 0-15 are software interrupts between
//! CPUs, 16-31 are per-CPU ("PPIs", like each CPU's timer), 32 and up are
//! shared devices ("SPIs"). 1020-1023 mean "nothing" (spurious).

use super::board::IrqController;
use super::mmio;
use core::sync::atomic::{AtomicUsize, Ordering};

const GICD_CTLR: usize = 0x000;
const GICD_ISENABLER: usize = 0x100;
const GICD_ICENABLER: usize = 0x180;
const GICD_IPRIORITYR: usize = 0x400;
const GICC_CTLR: usize = 0x000;
const GICC_PMR: usize = 0x004;
const GICC_IAR: usize = 0x00c;
const GICC_EOIR: usize = 0x010;

pub struct GicV2 {
    dist: AtomicUsize,
    cpu: AtomicUsize,
}

impl GicV2 {
    pub const fn new(dist: usize, cpu: usize) -> Self {
        GicV2 { dist: AtomicUsize::new(dist), cpu: AtomicUsize::new(cpu) }
    }
    pub fn set_bases(&self, dist: usize, cpu: usize) {
        self.dist.store(dist, Ordering::Relaxed);
        self.cpu.store(cpu, Ordering::Relaxed);
    }
    pub fn bases(&self) -> (usize, usize) {
        (self.dist.load(Ordering::Relaxed), self.cpu.load(Ordering::Relaxed))
    }
}

impl IrqController for GicV2 {
    fn init(&self) {
        let (d, c) = self.bases();
        mmio::write32(d + GICD_CTLR, 0);
        for i in 0..32 {
            mmio::write32(d + GICD_ICENABLER + 4 * i, 0xffff_ffff); // everything off
        }
        mmio::write32(d + GICD_CTLR, 1); // distributor on
        mmio::write32(c + GICC_PMR, 0xf0); // let through every priority below 0xf0
        mmio::write32(c + GICC_CTLR, 1); // CPU interface on
    }
    fn enable(&self, irq: u32) {
        let d = self.dist.load(Ordering::Relaxed);
        mmio::write8(d + GICD_IPRIORITYR + irq as usize, 0xa0);
        mmio::write32(d + GICD_ISENABLER + 4 * (irq as usize / 32), 1 << (irq % 32));
    }
    fn ack(&self) -> Option<u32> {
        let irq = mmio::read32(self.cpu.load(Ordering::Relaxed) + GICC_IAR) & 0x3ff;
        if irq >= 1020 { None } else { Some(irq) }
    }
    fn eoi(&self, irq: u32) {
        mmio::write32(self.cpu.load(Ordering::Relaxed) + GICC_EOIR, irq);
    }
}