From 9aed038d8e425714fe43aa1db7f28a296bf4caba Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Fri, 31 Mar 2023 14:42:41 +0100 Subject: [PATCH] Start on GICv3 driver. --- src/exercises/bare-metal/rtc.md | 8 + src/exercises/bare-metal/rtc/Makefile | 2 +- src/exercises/bare-metal/rtc/src/gicv3.rs | 555 ++++++++++++++++++++++ src/exercises/bare-metal/rtc/src/main.rs | 12 + 4 files changed, 576 insertions(+), 1 deletion(-) create mode 100644 src/exercises/bare-metal/rtc/src/gicv3.rs diff --git a/src/exercises/bare-metal/rtc.md b/src/exercises/bare-metal/rtc.md index 5d7f943c..9a12f42a 100644 --- a/src/exercises/bare-metal/rtc.md +++ b/src/exercises/bare-metal/rtc.md @@ -37,6 +37,14 @@ directory for the following files. {{#include rtc/src/exceptions.rs}} ``` +`src/gicv3.rs` (you shouldn't need to change this): + + + +```rust,compile_fail +{{#include rtc/src/gicv3.rs}} +``` + `src/logger.rs` (you shouldn't need to change this): diff --git a/src/exercises/bare-metal/rtc/Makefile b/src/exercises/bare-metal/rtc/Makefile index 2d0ac80a..3e274aeb 100644 --- a/src/exercises/bare-metal/rtc/Makefile +++ b/src/exercises/bare-metal/rtc/Makefile @@ -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 diff --git a/src/exercises/bare-metal/rtc/src/gicv3.rs b/src/exercises/bare-metal/rtc/src/gicv3.rs new file mode 100644 index 00000000..366d6320 --- /dev/null +++ b/src/exercises/bare-metal/rtc/src/gicv3.rs @@ -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::()) 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)); + } +} diff --git a/src/exercises/bare-metal/rtc/src/main.rs b/src/exercises/bare-metal/rtc/src/main.rs index a24a3f83..f410a645 100644 --- a/src/exercises/bare-metal/rtc/src/main.rs +++ b/src/exercises/bare-metal/rtc/src/main.rs @@ -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) };