1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-05-23 19:00:13 +02:00

Start on GICv3 driver.

This commit is contained in:
Andrew Walbran 2023-03-31 14:42:41 +01:00
parent ce752db15a
commit 9aed038d8e
4 changed files with 576 additions and 1 deletions

View File

@ -37,6 +37,14 @@ directory for the following files.
{{#include rtc/src/exceptions.rs}}
```
`src/gicv3.rs` (you shouldn't need to change this):
<!-- File src/gicv3.rs -->
```rust,compile_fail
{{#include rtc/src/gicv3.rs}}
```
`src/logger.rs` (you shouldn't need to change this):
<!-- File src/logger.rs -->

View File

@ -31,7 +31,7 @@ rtc.bin: build
$(OBJCOPY) -O binary target/aarch64-unknown-none/debug/rtc $@
qemu: rtc.bin
qemu-system-aarch64 -machine virt -cpu max -serial mon:stdio -display none -kernel $< -s
qemu-system-aarch64 -machine virt,gic_version=3 -cpu max -serial mon:stdio -display none -kernel $< -s
clean:
cargo clean

View File

@ -0,0 +1,555 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use bitflags::bitflags;
use core::{
arch::asm,
hint::spin_loop,
mem::size_of,
ptr::{addr_of, addr_of_mut},
};
use log::trace;
/// Reads and returns the value of the given aarch64 system register.
macro_rules! read_sysreg {
($name:ident) => {
{
let mut value: u64;
::core::arch::asm!(
concat!("mrs {value:x}, ", ::core::stringify!($name)),
value = out(reg) value,
options(nomem, nostack),
);
value
}
}
}
/// Writes the given value to the given aarch64 system register.
macro_rules! write_sysreg {
($name:ident, $value:expr) => {
{
let v: u64 = $value;
::core::arch::asm!(
concat!("msr ", ::core::stringify!($name), ", {value:x}"),
value = in(reg) v,
options(nomem, nostack),
)
}
}
}
/// The offset in bytes from `RD_base` to `SGI_base`.
const SGI_OFFSET: usize = 0x10000;
/// The ID of the first Software Generated Interrupt.
pub const SGI_START: u32 = 0;
/// The ID of the first Private Peripheral Interrupt.
pub const PPI_START: u32 = 16;
/// The ID of the first Shared Peripheral Interrupt.
pub const SPI_START: u32 = 32;
bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct GicdCtlr: u32 {
const RWP = 1 << 31;
const nASSGIreq = 1 << 8;
const E1NWF = 1 << 7;
const DS = 1 << 6;
const ARE_NS = 1 << 5;
const ARE_S = 1 << 4;
const EnableGrp1S = 1 << 2;
const EnableGrp1NS = 1 << 1;
const EnableGrp0 = 1 << 0;
}
}
/// GIC Distributor registers.
#[repr(C, align(8))]
struct GICD {
/// Distributor control register.
ctlr: GicdCtlr,
/// Interrupt controller type register.
typer: u32,
/// Distributor implementer identification register.
iidr: u32,
/// Interrupt controller type register 2.
typer2: u32,
/// Error reporting status register.
statusr: u32,
_reserved0: [u32; 3],
/// Implementation defined registers.
implementation_defined: [u32; 8],
/// Set SPI register.
setspi_nsr: u32,
_reserved1: u32,
/// Clear SPI register.
clrspi_nsr: u32,
_reserved2: u32,
/// Set SPI secure register.
setspi_sr: u32,
_reserved3: u32,
/// Clear SPI secure register.
clrspi_sr: u32,
_reserved4: [u32; 9],
/// Interrupt group registers.
igroupr: [u32; 32],
/// Interrupt set-enable registers.
isenabler: [u32; 32],
/// Interrupt clear-enable registers.
icenabler: [u32; 32],
/// Interrupt set-pending registers.
ispendr: [u32; 32],
/// Interrupt clear-pending registers.
icpendr: [u32; 32],
/// Interrupt set-active registers.
isactiver: [u32; 32],
/// Interrupt clear-active registers.
icactiver: [u32; 32],
/// Interrupt priority registers.
ipriorityr: [u8; 1024],
/// Interrupt processor targets registers.
itargetsr: [u32; 256],
/// Interrupt configuration registers.
icfgr: [u32; 64],
/// Interrupt group modifier registers.
igrpmodr: [u32; 32],
_reserved5: [u32; 32],
/// Non-secure access control registers.
nsacr: [u32; 64],
/// Software generated interrupt register.
sigr: u32,
_reserved6: [u32; 3],
/// SGI clear-pending registers.
cpendsgir: [u32; 4],
/// SGI set-pending registers.
spendsgir: [u32; 4],
_reserved7: [u32; 20],
/// Non-maskable interrupt registers.
inmir: [u32; 32],
/// Interrupt group registers for extended SPI range.
igroupr_e: [u32; 32],
_reserved8: [u32; 96],
/// Interrupt set-enable registers for extended SPI range.
isenabler_e: [u32; 32],
_reserved9: [u32; 96],
/// Interrupt clear-enable registers for extended SPI range.
icenabler_e: [u32; 32],
_reserved10: [u32; 96],
/// Interrupt set-pending registers for extended SPI range.
ispendr_e: [u32; 32],
_reserved11: [u32; 96],
/// Interrupt clear-pending registers for extended SPI range.
icpendr_e: [u32; 32],
_reserved12: [u32; 96],
/// Interrupt set-active registers for extended SPI range.
isactive_e: [u32; 32],
_reserved13: [u32; 96],
/// Interrupt clear-active registers for extended SPI range.
icactive_e: [u32; 32],
_reserved14: [u32; 224],
/// Interrupt priority registers for extended SPI range.
ipriorityr_e: [u8; 1024],
_reserved15: [u32; 768],
/// Extended SPI configuration registers.
icfgr_e: [u32; 64],
_reserved16: [u32; 192],
/// Interrupt group modifier registers for extended SPI range.
igrpmodr_e: [u32; 32],
_reserved17: [u32; 96],
/// Non-secure access control registers for extended SPI range.
nsacr_e: [u32; 32],
_reserved18: [u32; 288],
/// Non-maskable interrupt registers for extended SPI range.
inmr_e: [u32; 32],
_reserved19: [u32; 2400],
/// Interrupt routing registers.
irouter: [u32; 1975],
_reserved20: [u32; 9],
/// Interrupt routing registers for extended SPI range.
irouter_e: [u32; 2048],
_reserved21: [u32; 2048],
/// Implementation defined registers.
implementation_defined2: [u32; 4084],
/// ID registers.
id_registers: [u32; 12],
}
bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct Waker: u32 {
const CHILDREN_ASLEEP = 1 << 2;
const PROCESSOR_SLEEP = 1 << 1;
}
}
/// GIC Redistributor registers.
#[repr(C, align(8))]
struct GICR {
/// Redistributor control register.
ctlr: u32,
/// Implementer identification register.
iidr: u32,
/// Redistributor type register.
typer: u64,
/// Error reporting status register.
statusr: u32,
/// Redistributor wake register.
waker: Waker,
/// Report maximum PARTID and PMG register.
mpamidr: u32,
/// Set PARTID and PMG register.
partidr: u32,
/// Implementation defined registers.
implementation_defined1: [u32; 8],
/// Set LPI pending register.
setlprir: u64,
/// Clear LPI pending register.
clrlpir: u64,
_reserved0: [u32; 8],
/// Redistributor properties base address register.
propbaser: u64,
/// Redistributor LPI pending table base address register.
pendbaser: u64,
_reserved1: [u32; 8],
/// Redistributor invalidate LPI register.
invlpir: u64,
_reserved2: u64,
/// Redistributor invalidate all register.
invallr: u64,
_reserved3: u64,
/// Redistributor synchronize register.
syncr: u32,
_reserved4: [u32; 15],
/// Implementation defined registers.
implementation_defined2: u64,
_reserved5: u64,
/// Implementation defined registers.
implementation_defined3: u64,
_reserved6: [u32; 12218],
/// Implementation defined registers.
implementation_defined4: [u32; 4084],
/// ID registers.
id_registers: [u32; 12],
}
/// GIC Redistributor SGI and PPI registers.
#[repr(C, align(8))]
struct SGI {
_reserved0: [u32; 32],
/// Interrupt group register 0.
igroupr0: u32,
/// Interrupt group registers for extended PPI range.
igroupr_e: [u32; 2],
_reserved1: [u32; 29],
/// Interrupt set-enable register 0.
isenabler0: u32,
/// Interrupt set-enable registers for extended PPI range.
isenabler_e: [u32; 2],
_reserved2: [u32; 29],
/// Interrupt clear-enable register 0.
icenabler0: u32,
/// Interrupt clear-enable registers for extended PPI range.
icenabler_e: [u32; 2],
_reserved3: [u32; 29],
/// Interrupt set-pending register 0.
ispendr0: u32,
/// Interrupt set-pending registers for extended PPI range.
ispendr_e: [u32; 2],
_reserved4: [u32; 29],
/// Interrupt clear-pending register 0.
icpendr0: u32,
/// Interrupt clear-pending registers for extended PPI range.
icpendr_e: [u32; 2],
_reserved5: [u32; 29],
/// Interrupt set-active register 0.
isactiver0: u32,
/// Interrupt set-active registers for extended PPI range.
isactive_e: [u32; 2],
_reserved6: [u32; 29],
/// Interrupt clear-active register 0.
icactiver0: u32,
/// Interrupt clear-active registers for extended PPI range.
icactive_e: [u32; 2],
_reserved7: [u32; 29],
/// Interrupt priority registers.
ipriorityr: [u8; 32],
/// Interrupt priority registers for extended PPI range.
ipriorityr_e: [u8; 64],
_reserved8: [u32; 488],
/// SGI configuration register, PPI configuration register and extended PPI
/// configuration registers.
icfgr: [u32; 6],
_reserved9: [u32; 58],
/// Interrupt group modifier register 0.
igrpmodr0: u32,
/// Interrupt group modifier registers for extended PPI range.
igrpmodr_e: [u32; 2],
_reserved10: [u32; 61],
/// Non-secure access control register.
nsacr: u32,
_reserved11: [u32; 95],
/// Non-maskable interrupt register for PPIs.
inmir0: u32,
/// Non-maskable interrupt register for extended PPIs.
inmir_e: [u32; 31],
_reserved12: [u32; 11264],
/// Implementation defined registers.
implementation_defined: [u32; 4084],
_reserved13: [u32; 12],
}
/// Driver for an Arm Generic Interrupt Controller version 3 (or 4).
#[derive(Debug)]
pub struct GicV3 {
gicd: *mut GICD,
gicr: *mut GICR,
sgi: *mut SGI,
}
impl GicV3 {
/// Constructs a new instance of the driver for a GIC with the given
/// distributor and redistributor base addresses.
///
/// # Safety
///
/// The given base addresses must point to the GIC distributor and
/// redistributor registers respectively. These regions must be mapped into
/// the address space of the process as device memory, and not have any
/// other aliases, either via another instance of this driver or otherwise.
pub unsafe fn new(gicd: *mut u64, gicr: *mut u64) -> Self {
Self {
gicd: gicd as _,
gicr: gicr as _,
sgi: gicr.wrapping_add(SGI_OFFSET / size_of::<u64>()) as _,
}
}
/// Initialises the GIC.
pub fn setup(&mut self) {
// Safe because writing to this system register doesn't access memory in
// any way.
unsafe {
// Enable system register access.
write_sysreg!(icc_sre_el1, 0x01);
}
// Safe because we know that `self.gicr` is a valid and unique pointer
// to the registers of a GIC redistributor interface.
unsafe {
// Mark this CPU core as awake, and wait until the GIC wakes up
// before continuing.
let mut waker = addr_of!((*self.gicr).waker).read_volatile();
trace!("WAKER: {:?}", waker);
waker -= Waker::PROCESSOR_SLEEP;
addr_of_mut!((*self.gicr).waker).write_volatile(waker);
while addr_of!((*self.gicr).waker)
.read_volatile()
.contains(Waker::CHILDREN_ASLEEP)
{
spin_loop();
}
}
// Safe because accessing this system register doesn't access memory in
// any way.
unsafe {
trace!("ICC_CTLR_EL1={:#x}", read_sysreg!(icc_ctlr_el1));
// Disable use of `ICC_PMR_EL1` as a hint for interrupt
// distribution, configure a write to an EOI register to also
// deactivate the interrupt, and configure preemption groups for
// group 0 and group 1 interrupts separately.
write_sysreg!(icc_ctlr_el1, 0);
trace!("ICC_CTLR_EL1={:#x}", read_sysreg!(icc_ctlr_el1));
}
// Safe because we know that `self.gicd` is a valid and unique pointer
// to the registers of a GIC distributor interface.
unsafe {
// Enable affinity routing and non-secure group 1 interrupts.
addr_of_mut!((*self.gicd).ctlr)
.write_volatile(GicdCtlr::ARE_S | GicdCtlr::EnableGrp1NS);
}
// Safe because we know that `self.gicd` is a valid and unique pointer
// to the registers of a GIC distributor interface, and `self.sgi` to
// the SGI and PPI registers of a GIC redistributor interface.
unsafe {
// Put all SGIs and PPIs into non-secure group 1.
addr_of_mut!((*self.sgi).igroupr0).write_volatile(0xffffffff);
// Put all SPIs into non-secure group 1.
for i in 0..32 {
addr_of_mut!((*self.gicd).igroupr[i]).write_volatile(0xffffffff);
}
}
// Safe because writing to this system register doesn't access memory in
// any way.
unsafe {
// Enable non-secure group 1.
write_sysreg!(icc_igrpen1_el1, 0x00000001);
}
}
/// Enables or disables the interrupt with the given ID.
pub fn enable_interrupt(&mut self, intid: u32, enable: bool) {
let index = (intid / 32) as usize;
let bit = 1 << (intid % 32);
// Safe because we know that `self.gicd` is a valid and unique pointer
// to the registers of a GIC distributor interface, and `self.sgi` to
// the SGI and PPI registers of a GIC redistributor interface.
unsafe {
if enable {
addr_of_mut!((*self.gicd).isenabler[index]).write_volatile(bit);
if intid < SPI_START {
addr_of_mut!((*self.sgi).isenabler0).write_volatile(bit);
}
} else {
addr_of_mut!((*self.gicd).icenabler[index]).write_volatile(bit);
if intid < SPI_START {
addr_of_mut!((*self.sgi).icenabler0).write_volatile(bit);
}
}
}
}
/// Enables all interrupts.
pub fn enable_all_interrupts(&mut self, enable: bool) {
for i in 0..32 {
// Safe because we know that `self.gicd` is a valid and unique
// pointer to the registers of a GIC distributor interface.
unsafe {
if enable {
addr_of_mut!((*self.gicd).isenabler[i]).write_volatile(0xffffffff);
} else {
addr_of_mut!((*self.gicd).icenabler[i]).write_volatile(0xffffffff);
}
}
}
// Safe because we know that `self.sgi` is a valid and unique pointer
// to the SGI and PPI registers of a GIC redistributor interface.
unsafe {
if enable {
addr_of_mut!((*self.sgi).isenabler0).write_volatile(0xffffffff);
} else {
addr_of_mut!((*self.sgi).icenabler0).write_volatile(0xffffffff);
}
}
}
/// Sets the priority mask for the current CPU core.
///
/// Only interrupts with a higher priority (numerically lower) will be
/// signalled.
pub fn set_priority_mask(min_priority: u8) {
// Safe because writing to this system register doesn't access memory in
// any way.
unsafe {
write_sysreg!(icc_pmr_el1, min_priority.into());
}
}
/// Sets the priority of the interrupt with the given ID.
///
/// Note that lower numbers correspond to higher priorities; i.e. 0 is the
/// highest priority, and 255 is the lowest.
pub fn set_interrupt_priority(&mut self, intid: u32, priority: u8) {
// Safe because we know that `self.gicd` is a valid and unique pointer
// to the registers of a GIC distributor interface, and `self.sgi` to
// the SGI and PPI registers of a GIC redistributor interface.
unsafe {
// Affinity routing is enabled, so use the GICR for SGIs and PPIs.
if intid < SPI_START {
addr_of_mut!((*self.sgi).ipriorityr[intid as usize])
.write_volatile(priority);
} else {
addr_of_mut!((*self.gicd).ipriorityr[intid as usize])
.write_volatile(priority);
}
}
}
/// Configures the trigger type for the interrupt with the given ID.
pub fn set_trigger(&mut self, intid: u32, trigger: Trigger) {
let index = (intid / 16) as usize;
let bit = 1 << (((intid % 16) * 2) + 1);
// Safe because we know that `self.gicd` is a valid and unique pointer
// to the registers of a GIC distributor interface, and `self.sgi` to
// the SGI and PPI registers of a GIC redistributor interface.
unsafe {
// Affinity routing is enabled, so use the GICR for SGIs and PPIs.
let register = if intid < SPI_START {
addr_of_mut!((*self.sgi).icfgr[index])
} else {
addr_of_mut!((*self.gicd).icfgr[index])
};
let v = register.read_volatile();
register.write_volatile(match trigger {
Trigger::Edge => v | bit,
Trigger::Level => v & !bit,
});
}
}
/// Gets the ID of the highest priority signalled interrupt, and
/// acknowledges it.
pub fn get_and_acknowledge_interrupt() -> u32 {
// Safe because reading this system register doesn't access memory in
// any way.
(unsafe { read_sysreg!(icc_iar1_el1) }) as u32
}
/// Informs the interrupt controller that the CPU has completed processing
/// the given interrupt. This drops the interrupt priority and deactivates
/// the interrupt.
pub fn end_interrupt(intid: u32) {
// Safe because writing to this system register doesn't access memory in
// any way.
unsafe { write_sysreg!(icc_eoir1_el1, intid.into()) }
}
}
/// 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,
}
/// Disables debug, SError, IRQ and FIQ exceptions.
pub fn irq_disable() {
// Safe because writing to this system register doesn't access memory in any
// way.
unsafe {
asm!("msr DAIFSet, #0xf", options(nomem, nostack));
}
}
/// Enables debug, SError, IRQ and FIQ exceptions.
pub fn irq_enable() {
// Safe because writing to this system register doesn't access memory in any
// way.
unsafe {
asm!("msr DAIFClr, #0xf", options(nomem, nostack));
}
}

View File

@ -17,11 +17,13 @@
#![no_std]
mod exceptions;
mod gicv3;
mod logger;
mod pl011;
// ANCHOR_END: top
mod pl031;
use crate::gicv3::GicV3;
use crate::pl031::Rtc;
use chrono::{TimeZone, Utc};
use core::hint::spin_loop;
@ -31,6 +33,10 @@ use core::panic::PanicInfo;
use log::{error, info, trace, LevelFilter};
use psci::system_off;
/// Base addresses of the GICv3.
const GICD_BASE_ADDRESS: *mut u64 = 0x800_0000 as _;
const GICR_BASE_ADDRESS: *mut u64 = 0x80A_0000 as _;
/// Base address of the primary PL011 UART.
const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
// ANCHOR_END: imports
@ -49,6 +55,12 @@ extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
info!("main({:#x}, {:#x}, {:#x}, {:#x})", x0, x1, x2, x3);
// ANCHOR_END: main
// Safe because `GICD_BASE_ADDRESS` and `GICR_BASE_ADDRESS` are the base
// addresses of a GICv3 distributor and redistributor respectively, and
// nothing else accesses those address ranges.
let mut gic = unsafe { GicV3::new(GICD_BASE_ADDRESS, GICR_BASE_ADDRESS) };
gic.setup();
// Safe because `PL031_BASE_ADDRESS` is the base address of a PL031 device,
// and nothing else accesses that address range.
let mut rtc = unsafe { Rtc::new(PL031_BASE_ADDRESS) };