Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add aarch32 support #33

Merged
merged 8 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- Added methods to read type register and its fields.
- Added `set_group`, `redistributor_mark_core_awake` and other methods to `GicV3`.
- Added support for GICv2 in a separate `GicV2` driver.
- Added support for aarch32.

## 0.1.2

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
description = "A driver for the Arm Generic Interrupt Controller version 2, 3 or 4."
authors = ["Andrew Walbran <qwandor@google.com>", "Fritz Stracke <fritz.stracke@rwth-aachen.de>"]
repository = "https://github.com/google/arm-gic"
keywords = ["arm", "aarch64", "driver", "gic", "interrupt-controller"]
keywords = ["arm", "aarch32", "aarch64", "driver", "gic", "interrupt-controller"]
categories = ["embedded", "no-std", "hardware-support"]

[dependencies]
Expand Down
15 changes: 6 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@
[![crates.io page](https://img.shields.io/crates/v/arm-gic.svg)](https://crates.io/crates/arm-gic)
[![docs.rs page](https://docs.rs/arm-gic/badge.svg)](https://docs.rs/arm-gic)

This crate provides a Rust driver for the Arm Generic Interrupt Controller version 3 or 4 (GICv3 and
GICv4) as well as verison 2.
This crate provides Rust drivers for the Arm Generic Interrupt Controller version 2, 3 or 4 (GICv2,
GICv3 and GICv4) on aarch32 and aarch64.

Because of large technical differences between the version 2 and version 3/4 Generic Interrupt Controllers
they have been separated in different modules. Use the one appropriate for your hardware. The interfaces are
largely compatible. Only differences when dispatching software-generated interrupts should be considered.
Look at the ARM-Manuals for further details.

Currently it only supports AArch64. Patches are welcome to add support for AArch32 and other GIC
versions.
Because of large technical differences between the version 2 and version 3/4 Generic Interrupt
Controllers, they have been separated in different modules. Use the one appropriate for your
hardware. The interfaces are largely compatible. Only differences when dispatching
software-generated interrupts should be considered. Look at the ARM manuals for further details.

This is not an officially supported Google product.

Expand Down
2 changes: 1 addition & 1 deletion src/gicv2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ impl GicV2 {
unsafe {
intid = (&raw mut (*self.gicc).aiar).read_volatile() as u32;
}
if intid == IntId::SPECIAL_START {
if IntId(intid) == IntId::SPECIAL_NONE {
None
} else {
Some(IntId(intid))
Expand Down
2 changes: 1 addition & 1 deletion src/gicv3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ impl<const CPU_COUNT: usize> GicV3<CPU_COUNT> {
/// Returns `None` if there is no pending interrupt of sufficient priority.
pub fn get_and_acknowledge_interrupt() -> Option<IntId> {
let intid = read_icc_iar1_el1() as u32;
if intid == IntId::SPECIAL_START {
if IntId(intid) == IntId::SPECIAL_NONE {
None
} else {
Some(IntId(intid))
Expand Down
21 changes: 13 additions & 8 deletions src/sysreg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@
#[cfg(test)]
#[macro_use]
pub mod fake;

#[cfg(all(not(test), target_arch = "aarch64"))]
#[macro_use]
mod aarch64;

read_sysreg!(icc_iar1_el1, read_icc_iar1_el1);
#[cfg(all(not(test), target_arch = "arm"))]
#[macro_use]
mod aarch32;

read_sysreg32!(icc_iar1_el1, 0, c12, c12, 0, read_icc_iar1_el1);

write_sysreg!(icc_ctlr_el1, write_icc_ctlr_el1);
write_sysreg!(icc_eoir1_el1, write_icc_eoir1_el1);
write_sysreg!(icc_igrpen0_el1, write_icc_igrpen0_el1);
write_sysreg!(icc_igrpen1_el1, write_icc_igrpen1_el1);
write_sysreg!(icc_pmr_el1, write_icc_pmr_el1);
write_sysreg!(icc_sgi1r_el1, write_icc_sgi1r_el1);
write_sysreg!(icc_sre_el1, write_icc_sre_el1);
write_sysreg32!(icc_ctlr_el1, 0, c12, c12, 4, write_icc_ctlr_el1);
write_sysreg32!(icc_eoir1_el1, 0, c12, c12, 1, write_icc_eoir1_el1);
write_sysreg32!(icc_igrpen0_el1, 0, c12, c12, 6, write_icc_igrpen0_el1);
write_sysreg32!(icc_igrpen1_el1, 0, c12, c12, 7, write_icc_igrpen1_el1);
write_sysreg32!(icc_pmr_el1, 0, c4, c6, 0, write_icc_pmr_el1);
write_sysreg64!(icc_sgi1r_el1, 0, c12, write_icc_sgi1r_el1);
write_sysreg32!(icc_sre_el1, 0, c12, c12, 5, write_icc_sre_el1);
98 changes: 98 additions & 0 deletions src/sysreg/aarch32.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2025 The arm-gic Authors.
// This project is dual-licensed under Apache 2.0 and MIT terms.
// See LICENSE-APACHE and LICENSE-MIT for details.

/// Generates a safe public function named `$function_name` to read the 32-bit system register `$sysreg`.
///
/// This should only be used for system registers which are indeed safe to read.
macro_rules! read_sysreg32 {
($sysreg:ident, $opc1:literal, $crm:ident, $crn:ident, $opc2: literal, $function_name:ident) => {
#[inline]
#[doc = "Autogenerated function to read the 32-bit "]
#[doc = stringify!($sysreg)]
#[doc = " system register"]
pub fn $function_name() -> u32 {
let value: u32;
// SAFETY: The caller of the macro guarantees that this system register is safe to read.
unsafe {
core::arch::asm!(
concat!(
"mrc p15, ",
stringify!($opc1), ",",
"{value}, ",
stringify!($crm), ",",
stringify!($crn), ",",
stringify!($opc2)
),
options(nostack),
value = out(reg) value,
);
}
value
}
};
}

/// Generates a safe public function named `$function_name` to write to the 32-bit system register
/// `$sysreg`.
///
/// This should only be used for system registers which are indeed safe to write.
macro_rules! write_sysreg32 {
($sysreg:ident, $opc1:literal, $crm:ident, $crn:ident, $opc2: literal, $function_name:ident) => {
#[inline]
#[doc = "Autogenerated function to write the 32-bit "]
#[doc = stringify!($sysreg)]
#[doc = " system register"]
pub fn $function_name(value: u32) {
// SAFETY: The caller of the macro guarantees that this system register is safe to
// write.
unsafe {
core::arch::asm!(
concat!(
"mcr p15, ",
stringify!($opc1), ",",
"{value}, ",
stringify!($crm), ",",
stringify!($crn), ",",
stringify!($opc2)
),
options(nostack),
value = in(reg) value,
);
}
}
};
}

/// Generates a safe public function named `$function_name` to write to the 64-bit system register
/// `$sysreg`.
///
/// This should only be used for system registers which are indeed safe to write.
macro_rules! write_sysreg64 {
($sysreg:ident, $opc1:literal, $crm:ident, $function_name:ident) => {
#[inline]
#[doc = "Autogenerated function to write the 64-bit "]
#[doc = stringify!($sysreg)]
#[doc = " system register"]
pub fn $function_name(value: u64) {
// SAFETY: The caller of the macro guarantees that this system register is safe to
// write.
let value_lo = value as u32;
let value_hi = (value >> 32) as u32;
unsafe {
core::arch::asm!(
concat!(
"mcrr p15, ",
stringify!($opc1), ",",
"{value_lo}, ",
"{value_hi}, ",
stringify!($crm)
),
options(nostack),
value_lo = in(reg) value_lo,
value_hi = in(reg) value_hi,
);
}
}
};
}
50 changes: 41 additions & 9 deletions src/sysreg/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@
// This project is dual-licensed under Apache 2.0 and MIT terms.
// See LICENSE-APACHE and LICENSE-MIT for details.

/// Generates a safe public function named `$function_name` to read the system register `$sysreg`.
/// Generates a safe public function named `$function_name` to read the 32-bit system register `$sysreg`.
///
/// This should only be used for system registers which are indeed safe to read.
macro_rules! read_sysreg {
($sysreg:ident, $function_name:ident) => {
pub fn $function_name() -> u64 {
let value;
macro_rules! read_sysreg32 {
($sysreg:ident, $opc1:literal, $crm:ident, $crn:ident, $opc2: literal, $function_name:ident) => {
#[inline]
#[doc = "Autogenerated function to read the 32-bit "]
#[doc = stringify!($sysreg)]
#[doc = " system register"]
pub fn $function_name() -> u32 {
let value: u64;
// SAFETY: The caller of the macro guarantees that this system register is safe to read.
unsafe {
core::arch::asm!(
Expand All @@ -17,17 +21,45 @@ macro_rules! read_sysreg {
value = out(reg) value,
);
}
value
value as u32
}
};
}

/// Generates a safe public function named `$function_name` to write to the system register
/// Generates a safe public function named `$function_name` to write to the 32-bit system register
/// `$sysreg`.
///
/// This should only be used for system registers which are indeed safe to write.
macro_rules! write_sysreg {
($sysreg:ident, $function_name:ident) => {
macro_rules! write_sysreg32 {
($sysreg:ident, $opc1:literal, $crm:ident, $crn:ident, $opc2: literal, $function_name:ident) => {
#[inline]
#[doc = "Autogenerated function to write the 32-bit "]
#[doc = stringify!($sysreg)]
#[doc = " system register"]
pub fn $function_name(value: u32) {
// SAFETY: The caller of the macro guarantees that this system register is safe to
// write.
unsafe {
core::arch::asm!(
concat!("msr ", stringify!($sysreg), ", {value}"),
options(nostack),
value = in(reg) value as u64,
);
}
}
};
}

/// Generates a safe public function named `$function_name` to write to the 64-bit system register
/// `$sysreg`.
///
/// This should only be used for system registers which are indeed safe to write.
macro_rules! write_sysreg64 {
($sysreg:ident, $opc1:literal, $crm:ident, $function_name:ident) => {
#[inline]
#[doc = "Autogenerated function to write the 64-bit "]
#[doc = stringify!($sysreg)]
#[doc = " system register"]
pub fn $function_name(value: u64) {
// SAFETY: The caller of the macro guarantees that this system register is safe to
// write.
Expand Down
34 changes: 22 additions & 12 deletions src/sysreg/fake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ pub static SYSREGS: Mutex<SystemRegisters> = Mutex::new(SystemRegisters::new());
/// A set of fake system registers.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SystemRegisters {
pub icc_iar1_el1: u64,
pub icc_ctlr_el1: u64,
pub icc_eoir1_el1: u64,
pub icc_igrpen0_el1: u64,
pub icc_igrpen1_el1: u64,
pub icc_pmr_el1: u64,
pub icc_iar1_el1: u32,
pub icc_ctlr_el1: u32,
pub icc_eoir1_el1: u32,
pub icc_igrpen0_el1: u32,
pub icc_igrpen1_el1: u32,
pub icc_pmr_el1: u32,
pub icc_sgi1r_el1: u64,
pub icc_sre_el1: u64,
pub icc_sre_el1: u32,
}

impl SystemRegisters {
Expand All @@ -38,18 +38,28 @@ impl SystemRegisters {
}

/// Generates a public function named `$function_name` to read the fake system register `$sysreg`.
macro_rules! read_sysreg {
($sysreg:ident, $function_name:ident) => {
pub fn $function_name() -> u64 {
macro_rules! read_sysreg32 {
($sysreg:ident, $opc1:literal, $crm:ident, $crn:ident, $opc2: literal, $function_name:ident) => {
pub fn $function_name() -> u32 {
crate::sysreg::fake::SYSREGS.lock().unwrap().$sysreg
}
};
}

/// Generates a public function named `$function_name` to write to the fake system register
/// `$sysreg`.
macro_rules! write_sysreg {
($sysreg:ident, $function_name:ident) => {
macro_rules! write_sysreg32 {
($sysreg:ident, $opc1:literal, $crm:ident, $crn:ident, $opc2: literal, $function_name:ident) => {
pub fn $function_name(value: u32) {
crate::sysreg::fake::SYSREGS.lock().unwrap().$sysreg = value;
}
};
}

/// Generates a public function named `$function_name` to write to the fake system register
/// `$sysreg`.
macro_rules! write_sysreg64 {
($sysreg:ident, $opc1:literal, $crm:ident, $function_name:ident) => {
pub fn $function_name(value: u64) {
crate::sysreg::fake::SYSREGS.lock().unwrap().$sysreg = value;
}
Expand Down
Loading