arm-gic 0.8.0

A driver for the Arm Generic Interrupt Controller version 2, 3 or 4.
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
// Copyright 2023 The arm-gic Authors.
// SPDX-License-Identifier: MIT OR Apache-2.0

//! Driver for the Arm Generic Interrupt Controller version 2, 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.
//!
//! # Example
//!
//! Using a GICv3 on a single-core aarch64 system:
//!
//! ```no_run
//! use arm_gic::{
//!     IntId,
//!     UniqueMmioPointer,
//!     gicv3::{
//!         GicCpuInterface, GicV3, SgiTarget, SgiTargetGroup,
//!         registers::{Gicd, GicrSgi},
//!     },
//!     irq_enable,
//! };
//! use core::ptr::NonNull;
//!
//! // Base addresses of the GICv3 distributor and redistributor.
//! const GICD_BASE_ADDRESS: *mut Gicd = 0x800_0000 as _;
//! const GICR_BASE_ADDRESS: *mut GicrSgi = 0x80A_0000 as _;
//!
//! let gicd = unsafe { UniqueMmioPointer::new(NonNull::new(GICD_BASE_ADDRESS).unwrap()) };
//! let gicr = unsafe { NonNull::new(GICR_BASE_ADDRESS).unwrap() };
//!
//! // Initialise the GIC.
//! let mut gic = unsafe { GicV3::new(gicd, gicr, 1, false) };
//! gic.setup(0);
//!
//! // Configure an SGI and then send it to ourself.
//! let sgi_intid = IntId::sgi(3);
//! GicCpuInterface::set_priority_mask(0xff);
//! gic.set_interrupt_priority(sgi_intid, Some(0), 0x80);
//! gic.enable_interrupt(sgi_intid, Some(0), true);
//! irq_enable();
//! GicCpuInterface::send_sgi(
//!     sgi_intid,
//!     SgiTarget::List {
//!         affinity3: 0,
//!         affinity2: 0,
//!         affinity1: 0,
//!         target_list: 0b1,
//!     },
//!     SgiTargetGroup::CurrentGroup1,
//! );
//! ```

#![cfg_attr(not(any(test, feature = "fakes")), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(clippy::undocumented_unsafe_blocks)]
#![deny(unsafe_op_in_unsafe_fn)]

pub mod gicv2;
pub mod gicv3;
#[cfg(any(test, feature = "fakes", target_arch = "aarch64", target_arch = "arm"))]
mod sysreg;

pub use safe_mmio::UniqueMmioPointer;
use safe_mmio::fields::ReadPureWrite;
use thiserror::Error;
use zerocopy::{FromZeros, Immutable, IntoBytes, KnownLayout};

#[cfg(all(target_arch = "aarch64", not(feature = "fakes")))]
use core::arch::asm;
use core::fmt::{self, Debug, Formatter};

/// The trigger configuration for an interrupt.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Trigger {
    /// The interrupt is edge triggered.
    Edge,
    /// The interrupt is level triggered.
    Level,
}

/// An interrupt group, without distinguishing between secure and non-secure.
///
/// This is used to select which group of interrupts to get, acknowledge and end.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum InterruptGroup {
    /// Interrupt group 0.
    Group0,
    /// Interrupt group 1.
    Group1,
}

/// An interrupt ID.
#[derive(
    Copy, Clone, Eq, FromZeros, Immutable, IntoBytes, KnownLayout, Ord, PartialOrd, PartialEq,
)]
#[repr(transparent)]
pub struct IntId(u32);

impl IntId {
    /// Special interrupt ID returned when running at EL3 and the interrupt should be handled at
    /// S-EL2 or S-EL1.
    pub const SPECIAL_SECURE: Self = Self(1020);

    /// Special interrupt ID returned when running at EL3 and the interrupt should be handled at
    /// (non-secure) EL2 or EL1.
    pub const SPECIAL_NONSECURE: Self = Self(1021);

    /// Special interrupt ID returned when the interrupt is a non-maskable interrupt.
    pub const SPECIAL_NMI: Self = Self(1022);

    /// Special interrupt ID returned when there is no pending interrupt of sufficient priority for
    /// the current security state and interrupt group.
    pub const SPECIAL_NONE: Self = Self(1023);

    /// The maximum number of SPIs which may be supported.
    pub const MAX_SPI_COUNT: u32 = Self::SPECIAL_START - Self::SPI_START;

    /// The number of Software Generated Interrupts.
    pub const SGI_COUNT: u32 = Self::PPI_START - Self::SGI_START;

    /// The number of (non-extended) Private Peripheral Interrupts.
    pub const PPI_COUNT: u32 = Self::SPI_START - Self::PPI_START;

    /// The maximum number of extended Private Peripheral Interrupts which may be supported.
    pub const MAX_EPPI_COUNT: u32 = Self::EPPI_END - Self::EPPI_START;

    /// The maximum number of extended Shared Peripheral Interrupts which may be supported.
    pub const MAX_ESPI_COUNT: u32 = Self::ESPI_END - Self::ESPI_START;

    /// The maximum number of Locality-specific Peripheral Interrupts which may be supported.
    pub const MAX_LPI_COUNT: u32 = Self::LPI_END - Self::LPI_START;

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

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

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

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

    /// One more than the last special interrupt ID.
    const SPECIAL_END: u32 = 1024;

    /// The first extended Private Peripheral Interrupt.
    const EPPI_START: u32 = 1056;

    /// One more than the last extended Private Peripheral Interrupt.
    const EPPI_END: u32 = 1120;

    /// The first extended Shared Peripheral Interrupt.
    const ESPI_START: u32 = 4096;

    /// One more than the last extended Shared Peripheral Interrupt.
    const ESPI_END: u32 = 5120;

    /// The first Locality-specific Peripheral Interrupt.
    const LPI_START: u32 = 8192;

    /// One more than the last possible Locality-specific Peripheral Interrupt.
    const LPI_END: u32 = 1 << 24;

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

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

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

    /// Returns the interrupt ID for the given extended Private Peripheral Interrupt.
    pub const fn eppi(eppi: u32) -> Self {
        assert!(eppi < Self::MAX_EPPI_COUNT);
        Self(Self::EPPI_START + eppi)
    }

    /// Returns the interrupt ID for the given extended Shared Peripheral Interrupt.
    pub const fn espi(espi: u32) -> Self {
        assert!(espi < Self::MAX_ESPI_COUNT);
        Self(Self::ESPI_START + espi)
    }

    /// Returns the interrupt ID for the given Locality-specific Peripheral Interrupt.
    pub const fn lpi(lpi: u32) -> Self {
        assert!(lpi < Self::MAX_LPI_COUNT);
        Self(Self::LPI_START + lpi)
    }

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

    /// Returns whether this interrupt ID is for a Private Peripheral Interrupt.
    pub const fn is_ppi(self) -> bool {
        Self::PPI_START <= self.0 && self.0 < Self::SPI_START
    }

    /// Returns whether this interrupt ID is for an Extended Private Peripheral Interrupt.
    pub const fn is_eppi(self) -> bool {
        Self::EPPI_START <= self.0 && self.0 < Self::EPPI_END
    }

    /// Returns whether this interrupt ID is for a Shared Peripheral Interrupt.
    pub const fn is_spi(self) -> bool {
        Self::SPI_START <= self.0 && self.0 < Self::SPECIAL_START
    }

    /// Returns whether this interrupt ID is for an Extended Shared Peripheral Interrupt.
    pub const fn is_espi(self) -> bool {
        Self::ESPI_START <= self.0 && self.0 < Self::ESPI_END
    }

    /// Returns whether this interrupt ID is private to a core, i.e. it is an SGI, PPI or EPPI.
    pub const fn is_private(self) -> bool {
        self.is_sgi() || self.is_ppi() || self.is_eppi()
    }

    /// Returns SGI index or `None` if it is not an SGI interrupt ID.
    pub const fn sgi_index(self) -> Option<u32> {
        if self.is_sgi() {
            Some(self.0 - Self::SGI_START)
        } else {
            None
        }
    }

    /// Maps SGI, PPI and EPPI interrupt IDs into a continuous index range starting from 0,
    /// making it ideal for indexing redistributor registers.
    pub const fn private_index(self) -> Option<usize> {
        if self.is_sgi() || self.is_ppi() {
            Some(self.0 as usize)
        } else if self.is_eppi() {
            // EPPI
            Some((self.0 - IntId::EPPI_START + IntId::SGI_COUNT + IntId::PPI_COUNT) as usize)
        } else {
            None
        }
    }

    /// Returns SPI index or `None` if it is not an SPI interrupt ID.
    pub const fn spi_index(self) -> Option<usize> {
        if self.is_spi() {
            Some((self.0 - Self::SPI_START) as usize)
        } else {
            None
        }
    }

    /// Returns ESPI index or `None` if it is not an ESPI interrupt ID.
    pub const fn espi_index(self) -> Option<usize> {
        if self.is_espi() {
            Some((self.0 - Self::ESPI_START) as usize)
        } else {
            None
        }
    }

    /// Returns the raw interrupt ID value.
    ///
    /// This is the same as using the `From<IntId>` implementation, but const.
    pub const fn raw_value(self) -> u32 {
        self.0
    }

    // TODO: Change this to return a Range<IntId> once core::iter::Step is stabilised.
    /// Returns an array of all interrupt Ids that are private to a core, i.e. SGIs and PPIs.
    pub fn private() -> impl Iterator<Item = IntId> {
        let sgis = (0..Self::SGI_COUNT).map(Self::sgi);
        let ppis = (0..Self::PPI_COUNT).map(Self::ppi);

        sgis.chain(ppis)
    }

    // TODO: Change this to return a Range<IntId> once core::iter::Step is stabilised.
    /// Returns an array of all SPI Ids.
    pub fn spis() -> impl Iterator<Item = IntId> {
        (0..Self::MAX_SPI_COUNT).map(Self::spi)
    }
}

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 if self.0 < Self::SPECIAL_END {
            write!(f, "Special IntId {}", self.0)
        } else if self.0 < Self::EPPI_START {
            write!(f, "Reserved IntId {}", self.0)
        } else if self.0 < Self::EPPI_END {
            write!(f, "EPPI {}", self.0 - Self::EPPI_START)
        } else if self.0 < Self::ESPI_START {
            write!(f, "Reserved IntId {}", self.0)
        } else if self.0 < Self::ESPI_END {
            write!(f, "ESPI {}", self.0 - Self::ESPI_START)
        } else if self.0 < Self::LPI_START {
            write!(f, "Reserved IntId {}", self.0)
        } else {
            write!(f, "LPI {}", self.0 - Self::LPI_START)
        }
    }
}

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

#[derive(Clone, Copy, Debug, Eq, PartialEq, Error)]
#[error("Invalid (reserved) interrupt ID {0}")]
pub struct InvalidIntId(u32);

impl TryFrom<u32> for IntId {
    type Error = InvalidIntId;

    fn try_from(value: u32) -> Result<Self, Self::Error> {
        if (Self::SPECIAL_END..Self::EPPI_START).contains(&value)
            || (Self::EPPI_END..Self::ESPI_START).contains(&value)
            || (Self::ESPI_END..Self::LPI_START).contains(&value)
            || value >= Self::LPI_END
        {
            Err(InvalidIntId(value))
        } else {
            Ok(Self(value))
        }
    }
}

/// Disables debug, SError, IRQ and FIQ exceptions.
#[cfg(all(target_arch = "aarch64", not(feature = "fakes")))]
pub fn irq_disable() {
    // SAFETY: Writing to this system register doesn't access memory in any way.
    unsafe {
        asm!("msr DAIFSet, #0xf", options(nomem, nostack));
    }
}

/// Disables debug, SError, IRQ and FIQ exceptions.
#[cfg(any(test, feature = "fakes"))]
pub fn irq_disable() {}

/// Enables debug, SError, IRQ and FIQ exceptions.
#[cfg(all(target_arch = "aarch64", not(feature = "fakes")))]
pub fn irq_enable() {
    // SAFETY: Writing to this system register doesn't access memory in any way.
    unsafe {
        asm!("msr DAIFClr, #0xf", options(nomem, nostack));
    }
}

/// Enables debug, SError, IRQ and FIQ exceptions.
#[cfg(any(test, feature = "fakes"))]
pub fn irq_enable() {}

/// Waits for an interrupt.
#[cfg(all(target_arch = "aarch64", not(feature = "fakes")))]
pub fn wfi() {
    // SAFETY: This doesn't access memory in any way.
    unsafe {
        asm!("wfi", options(nomem, nostack));
    }
}

/// Waits for an interrupt.
#[cfg(any(test, feature = "fakes"))]
pub fn wfi() {
    // No-op, just return immediately.
}

/// Modifies `nth` bit of memory pointed by `registers`.
fn modify_bit<const N: usize>(
    mut registers: UniqueMmioPointer<[ReadPureWrite<u32>; N]>,
    nth: usize,
    set_bit: bool,
) {
    let reg_num: usize = nth / 32;

    let bit_num: usize = nth % 32;
    let bit_mask: u32 = 1 << bit_num;

    let mut reg_ptr = registers.get(reg_num).unwrap();

    reg_ptr.modify(|old_value| {
        if set_bit {
            old_value | bit_mask
        } else {
            old_value & !bit_mask
        }
    });
}

/// Sets `nth` bit of memory pointed by `registers`.
fn set_bit<const N: usize>(registers: UniqueMmioPointer<[ReadPureWrite<u32>; N]>, nth: usize) {
    modify_bit(registers, nth, true);
}

/// Clears `nth` bit of memory pointed by `registers`.
fn clear_bit<const N: usize>(registers: UniqueMmioPointer<[ReadPureWrite<u32>; N]>, nth: usize) {
    modify_bit(registers, nth, false);
}