Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
equation314 committed Jul 10, 2024
0 parents commit 7f62d1b
Show file tree
Hide file tree
Showing 5 changed files with 438 additions and 0 deletions.
55 changes: 55 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: CI

on: [push, pull_request]

jobs:
ci:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
rust-toolchain: [nightly]
targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
with:
toolchain: ${{ matrix.rust-toolchain }}
components: rust-src, clippy, rustfmt
targets: ${{ matrix.targets }}
- name: Check rust version
run: rustc --version --verbose
- name: Check code format
run: cargo fmt --all -- --check
- name: Clippy
run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
- name: Build
run: cargo build --target ${{ matrix.targets }} --all-features
- name: Unit test
if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}
run: cargo test --target ${{ matrix.targets }} -- --nocapture

doc:
runs-on: ubuntu-latest
strategy:
fail-fast: false
permissions:
contents: write
env:
default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }}
RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- name: Build docs
continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }}
run: |
cargo doc --no-deps --all-features
printf '<meta http-equiv="refresh" content="0;url=%s/index.html">' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html
- name: Deploy to Github Pages
if: ${{ github.ref == env.default-branch }}
uses: JamesIves/github-pages-deploy-action@v4
with:
single-commit: true
branch: gh-pages
folder: target/doc
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/target
/.vscode
.DS_Store
Cargo.lock
13 changes: 13 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "arm_gic"
version = "0.1.0"
edition = "2021"
authors = ["Yuekai Jia <[email protected]>"]
description = "ARM Generic Interrupt Controller (GIC) register definitions and basic operations"
license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0"
homepage = "https://github.com/arceos-org/arceos"
repository = "https://github.com/arceos-org/arm_gic"
documentation = "https://arceos-org.github.io/arm_gic"

[dependencies]
tock-registers = "0.8"
275 changes: 275 additions & 0 deletions src/gic_v2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
//! Types and definitions for GICv2.
//!
//! The official documentation: <https://developer.arm.com/documentation/ihi0048/latest/>

use core::ptr::NonNull;

use crate::{TriggerMode, GIC_MAX_IRQ, SPI_RANGE};
use tock_registers::interfaces::{Readable, Writeable};
use tock_registers::register_structs;
use tock_registers::registers::{ReadOnly, ReadWrite, WriteOnly};

register_structs! {
/// GIC Distributor registers.
#[allow(non_snake_case)]
GicDistributorRegs {
/// Distributor Control Register.
(0x0000 => CTLR: ReadWrite<u32>),
/// Interrupt Controller Type Register.
(0x0004 => TYPER: ReadOnly<u32>),
/// Distributor Implementer Identification Register.
(0x0008 => IIDR: ReadOnly<u32>),
(0x000c => _reserved_0),
/// Interrupt Group Registers.
(0x0080 => IGROUPR: [ReadWrite<u32>; 0x20]),
/// Interrupt Set-Enable Registers.
(0x0100 => ISENABLER: [ReadWrite<u32>; 0x20]),
/// Interrupt Clear-Enable Registers.
(0x0180 => ICENABLER: [ReadWrite<u32>; 0x20]),
/// Interrupt Set-Pending Registers.
(0x0200 => ISPENDR: [ReadWrite<u32>; 0x20]),
/// Interrupt Clear-Pending Registers.
(0x0280 => ICPENDR: [ReadWrite<u32>; 0x20]),
/// Interrupt Set-Active Registers.
(0x0300 => ISACTIVER: [ReadWrite<u32>; 0x20]),
/// Interrupt Clear-Active Registers.
(0x0380 => ICACTIVER: [ReadWrite<u32>; 0x20]),
/// Interrupt Priority Registers.
(0x0400 => IPRIORITYR: [ReadWrite<u32>; 0x100]),
/// Interrupt Processor Targets Registers.
(0x0800 => ITARGETSR: [ReadWrite<u32>; 0x100]),
/// Interrupt Configuration Registers.
(0x0c00 => ICFGR: [ReadWrite<u32>; 0x40]),
(0x0d00 => _reserved_1),
/// Software Generated Interrupt Register.
(0x0f00 => SGIR: WriteOnly<u32>),
(0x0f04 => @END),
}
}

register_structs! {
/// GIC CPU Interface registers.
#[allow(non_snake_case)]
GicCpuInterfaceRegs {
/// CPU Interface Control Register.
(0x0000 => CTLR: ReadWrite<u32>),
/// Interrupt Priority Mask Register.
(0x0004 => PMR: ReadWrite<u32>),
/// Binary Point Register.
(0x0008 => BPR: ReadWrite<u32>),
/// Interrupt Acknowledge Register.
(0x000c => IAR: ReadOnly<u32>),
/// End of Interrupt Register.
(0x0010 => EOIR: WriteOnly<u32>),
/// Running Priority Register.
(0x0014 => RPR: ReadOnly<u32>),
/// Highest Priority Pending Interrupt Register.
(0x0018 => HPPIR: ReadOnly<u32>),
(0x001c => _reserved_1),
/// CPU Interface Identification Register.
(0x00fc => IIDR: ReadOnly<u32>),
(0x0100 => _reserved_2),
/// Deactivate Interrupt Register.
(0x1000 => DIR: WriteOnly<u32>),
(0x1004 => @END),
}
}

/// The GIC distributor.
///
/// The Distributor block performs interrupt prioritization and distribution
/// to the CPU interface blocks that connect to the processors in the system.
///
/// The Distributor provides a programming interface for:
/// - Globally enabling the forwarding of interrupts to the CPU interfaces.
/// - Enabling or disabling each interrupt.
/// - Setting the priority level of each interrupt.
/// - Setting the target processor list of each interrupt.
/// - Setting each peripheral interrupt to be level-sensitive or edge-triggered.
/// - Setting each interrupt as either Group 0 or Group 1.
/// - Forwarding an SGI to one or more target processors.
///
/// In addition, the Distributor provides:
/// - visibility of the state of each interrupt
/// - a mechanism for software to set or clear the pending state of a peripheral
/// interrupt.
pub struct GicDistributor {
base: NonNull<GicDistributorRegs>,
max_irqs: usize,
}

/// The GIC CPU interface.
///
/// Each CPU interface block performs priority masking and preemption
/// handling for a connected processor in the system.
///
/// Each CPU interface provides a programming interface for:
///
/// - enabling the signaling of interrupt requests to the processor
/// - acknowledging an interrupt
/// - indicating completion of the processing of an interrupt
/// - setting an interrupt priority mask for the processor
/// - defining the preemption policy for the processor
/// - determining the highest priority pending interrupt for the processor.
pub struct GicCpuInterface {
base: NonNull<GicCpuInterfaceRegs>,
}

unsafe impl Send for GicDistributor {}
unsafe impl Sync for GicDistributor {}

unsafe impl Send for GicCpuInterface {}
unsafe impl Sync for GicCpuInterface {}

impl GicDistributor {
/// Construct a new GIC distributor instance from the base address.
pub const fn new(base: *mut u8) -> Self {
Self {
base: NonNull::new(base).unwrap().cast(),
max_irqs: GIC_MAX_IRQ,
}
}

const fn regs(&self) -> &GicDistributorRegs {
unsafe { self.base.as_ref() }
}

/// The number of implemented CPU interfaces.
pub fn cpu_num(&self) -> usize {
((self.regs().TYPER.get() as usize >> 5) & 0b111) + 1
}

/// The maximum number of interrupts that the GIC supports
pub fn max_irqs(&self) -> usize {
((self.regs().TYPER.get() as usize & 0b11111) + 1) * 32
}

/// Configures the trigger mode for the given interrupt.
pub fn configure_interrupt(&mut self, vector: usize, tm: TriggerMode) {
// Only configurable for SPI interrupts
if vector >= self.max_irqs || vector < SPI_RANGE.start {
return;
}

// type is encoded with two bits, MSB of the two determine type
// 16 irqs encoded per ICFGR register
let reg_idx = vector >> 4;
let bit_shift = ((vector & 0xf) << 1) + 1;
let mut reg_val = self.regs().ICFGR[reg_idx].get();
match tm {
TriggerMode::Edge => reg_val |= 1 << bit_shift,
TriggerMode::Level => reg_val &= !(1 << bit_shift),
}
self.regs().ICFGR[reg_idx].set(reg_val);
}

/// Enables or disables the given interrupt.
pub fn set_enable(&mut self, vector: usize, enable: bool) {
if vector >= self.max_irqs {
return;
}
let reg = vector / 32;
let mask = 1 << (vector % 32);
if enable {
self.regs().ISENABLER[reg].set(mask);
} else {
self.regs().ICENABLER[reg].set(mask);
}
}

/// Initializes the GIC distributor.
///
/// It disables all interrupts, sets the target of all SPIs to CPU 0,
/// configures all SPIs to be edge-triggered, and finally enables the GICD.
///
/// This function should be called only once.
pub fn init(&mut self) {
let max_irqs = self.max_irqs();
assert!(max_irqs <= GIC_MAX_IRQ);
self.max_irqs = max_irqs;

// Disable all interrupts
for i in (0..max_irqs).step_by(32) {
self.regs().ICENABLER[i / 32].set(u32::MAX);
self.regs().ICPENDR[i / 32].set(u32::MAX);
}
if self.cpu_num() > 1 {
for i in (SPI_RANGE.start..max_irqs).step_by(4) {
// Set external interrupts to target cpu 0
self.regs().ITARGETSR[i / 4].set(0x01_01_01_01);
}
}
// Initialize all the SPIs to edge triggered
for i in SPI_RANGE.start..max_irqs {
self.configure_interrupt(i, TriggerMode::Edge);
}

// enable GIC0
self.regs().CTLR.set(1);
}
}

impl GicCpuInterface {
/// Construct a new GIC CPU interface instance from the base address.
pub const fn new(base: *mut u8) -> Self {
Self {
base: NonNull::new(base).unwrap().cast(),
}
}

const fn regs(&self) -> &GicCpuInterfaceRegs {
unsafe { self.base.as_ref() }
}

/// Returns the interrupt ID of the highest priority pending interrupt for
/// the CPU interface. (read GICC_IAR)
///
/// The read returns a spurious interrupt ID of `1023` if the distributor
/// or the CPU interface are disabled, or there is no pending interrupt on
/// the CPU interface.
pub fn iar(&self) -> u32 {
self.regs().IAR.get()
}

/// Informs the CPU interface that it has completed the processing of the
/// specified interrupt. (write GICC_EOIR)
///
/// The value written must be the value returns from [`Self::iar`].
pub fn eoi(&self, iar: u32) {
self.regs().EOIR.set(iar);
}

/// handles the signaled interrupt.
///
/// It first reads GICC_IAR to obtain the pending interrupt ID and then
/// calls the given handler. After the handler returns, it writes GICC_EOIR
/// to acknowledge the interrupt.
///
/// If read GICC_IAR returns a spurious interrupt ID of `1023`, it does
/// nothing.
pub fn handle_irq<F>(&self, handler: F)
where
F: FnOnce(u32),
{
let iar = self.iar();
let vector = iar & 0x3ff;
if vector < 1020 {
handler(vector);
self.eoi(iar);
} else {
// spurious
}
}

/// Initializes the GIC CPU interface.
///
/// It unmask interrupts at all priority levels and enables the GICC.
///
/// This function should be called only once.
pub fn init(&self) {
// enable GIC0
self.regs().CTLR.set(1);
// unmask interrupts at all priority levels
self.regs().PMR.set(0xff);
}
}
Loading

0 comments on commit 7f62d1b

Please sign in to comment.