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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
//! ARM Generic Interrupt Controller (GIC) register definitions and basic
//! operations.
//! Driver for the Arm Generic Interrupt Controller version 2 or 3 or 4, on aarch64.
//!
//! This top level module contains functions that are not specific to any particular interrupt
//! controller, as support for other GIC versions may be added in future.
//!
//! Note:
//!  - Interrupt grouping(secure state) is not supported
//!  - Interrupt proiority(preempt) is not supported
//! Please contact the developer if you need this function

#![no_std]
#![feature(const_ptr_as_ref)]
#![feature(const_option)]
#![feature(const_nonnull_new)]

use core::fmt;
use core::fmt::{Debug, Formatter};

mod gic_v2;
mod gic_v3;
mod sysregs;

pub(crate) mod registers;

pub use crate::gic_v2::GicV2;
pub use crate::gic_v3::GicV3;

/// An interrupt ID.
#[derive(Copy, Clone, Eq, Ord, PartialOrd, PartialEq)]
pub struct IntId(usize);

impl IntId {
    /// Maximum number of interrupts supported by the GIC.
    pub const GIC_MAX_IRQ: usize = 1020;

    /// The ID of the first Software Generated Interrupt.
    const SGI_START: usize = 0;

    /// The ID of the first Private Peripheral Interrupt.
    const PPI_START: usize = 16;

    /// The ID of the first Shared Peripheral Interrupt.
    const SPI_START: usize = 32;

    /// The first special interrupt ID.
    const SPECIAL_START: usize = 1020;

    /// Returns the interrupt ID for the given Software Generated Interrupt.
    pub const fn sgi(sgi: usize) -> Self {
        assert!(sgi < Self::PPI_START);
        Self(Self::SGI_START + sgi)
    }

    /// Returns the interrupt ID for the given Private Peripheral Interrupt.
    pub const fn ppi(ppi: usize) -> Self {
        assert!(ppi < Self::SPI_START - Self::PPI_START);
        Self(Self::PPI_START + ppi)
    }

    /// Returns the interrupt ID for the given Shared Peripheral Interrupt.
    pub const fn spi(spi: usize) -> Self {
        assert!(spi < Self::SPECIAL_START);
        Self(Self::SPI_START + spi)
    }

    /// Returns whether this interrupt ID is for a Software Generated Interrupt.
    #[allow(dead_code)]
    fn is_sgi(self) -> bool {
        self.0 < Self::PPI_START
    }

    /// Returns whether this interrupt ID is private to a core, i.e. it is an SGI or PPI.
    #[allow(dead_code)]
    fn is_private(self) -> bool {
        self.0 < Self::SPI_START
    }
}

/// Different types of interrupt that the GIC handles.
pub enum InterruptType {
    /// Software-generated interrupt.
    ///
    /// SGIs are typically used for inter-processor communication and are
    /// generated by a write to an SGI register in the GIC.
    SGI,
    /// Private Peripheral Interrupt.
    ///
    /// Peripheral interrupts that are private to one core.
    PPI,
    /// Shared Peripheral Interrupt.
    ///
    /// Peripheral interrupts that can delivered to any connected core.
    SPI,
}

/// Translate an interrupt of a given type to a GIC INTID.
pub const fn translate_irq(id: usize, int_type: InterruptType) -> Option<usize> {
    match int_type {
        InterruptType::SGI => {
            if id < IntId::PPI_START {
                Some(id)
            } else {
                None
            }
        }
        InterruptType::PPI => {
            if id < IntId::SPI_START - IntId::PPI_START {
                Some(id + IntId::PPI_START)
            } else {
                None
            }
        }
        InterruptType::SPI => {
            if id < IntId::SPECIAL_START {
                Some(id + IntId::SPI_START)
            } else {
                None
            }
        }
    }
}

impl Debug for IntId {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        if self.0 < Self::PPI_START {
            write!(f, "SGI {}", self.0 - Self::SGI_START)
        } else if self.0 < Self::SPI_START {
            write!(f, "PPI {}", self.0 - Self::PPI_START)
        } else if self.0 < Self::SPECIAL_START {
            write!(f, "SPI {}", self.0 - Self::SPI_START)
        } else {
            write!(f, "Special IntId {}", self.0)
        }
    }
}

impl From<IntId> for u32 {
    fn from(intid: IntId) -> Self {
        intid.0 as u32
    }
}

impl From<IntId> for usize {
    fn from(intid: IntId) -> Self {
        intid.0
    }
}

impl From<usize> for IntId {
    fn from(id: usize) -> Self {
        Self(id)
    }
}

/// Interrupt trigger mode.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum TriggerMode {
    /// Edge-triggered.
    ///
    /// This is an interrupt that is asserted on detection of a rising edge of
    /// an interrupt signal and then, regardless of the state of the signal,
    /// remains asserted until it is cleared by the conditions defined by this
    /// specification.
    Edge = 0,
    /// Level-sensitive.
    ///
    /// This is an interrupt that is asserted whenever the interrupt signal
    /// level is active, and deasserted whenever the level is not active.
    Level = 1,
}

/// [`GenericArmGic`].
/// It is used to implement the interface abstraction that the interrupt chip
/// driver should provide to the outside world.
/// I hope that the versatility of the driver interface should support more chip architectures.
pub trait GenericArmGic: Debug + Clone + Copy + Sync + Send + Sized {
    /// Initialises the GIC.
    fn init_primary(&mut self);

    /// Initialises the GIC for the current CPU core.
    fn per_cpu_init(&mut self);

    /// Configures the trigger type for the interrupt with the given ID.
    fn set_trigger(&mut self, intid: IntId, trigger: TriggerMode);

    /// Enables the interrupt with the given ID.pub fn enable_interrupt(&mut self, intid: IntId);
    fn enable_interrupt(&mut self, intid: IntId);

    /// Disable the interrupt with the given ID.
    fn disable_interrupt(&mut self, intid: IntId);

    /// Gets the ID of the highest priority signalled interrupt, and acknowledges it.
    ///
    /// Returns `None` if there is no pending interrupt of sufficient priority.
    fn get_and_acknowledge_interrupt(&self) -> Option<IntId>;

    /// Informs the interrupt controller that the CPU has completed processing the given interrupt.
    /// This drops the interrupt priority and deactivates the interrupt.
    fn end_interrupt(&self, intid: IntId);
}