From 3ba794a1accaeea97934d226a724af5f15a41699 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Thu, 19 Feb 2026 22:34:25 +0000 Subject: [PATCH 1/2] Dedicated EL2 examples. * Adds more Hyp register details. * A bunch of fixes to the docs * Makes sure CI actually runs these and the SMP examples --- .github/workflows/build.yml | 46 +++ .gitignore | 2 + .vscode/settings.json | 1 + Cargo.toml | 1 + aarch32-cpu/src/generic_timer/el2.rs | 4 +- aarch32-cpu/src/register/armv8r/hcptr.rs | 39 ++- aarch32-cpu/src/register/armv8r/hcr.rs | 121 +++++++- aarch32-cpu/src/register/armv8r/hsr.rs | 270 +++++++++++++++++- aarch32-rt/Cargo.toml | 2 + aarch32-rt/src/arch_v4/interrupt.rs | 2 +- aarch32-rt/src/arch_v7/hvc.rs | 2 +- aarch32-rt/src/arch_v7/interrupt.rs | 2 +- aarch32-rt/src/arch_v8_hyp/abort.rs | 77 +++++ aarch32-rt/src/arch_v8_hyp/hvc.rs | 46 +++ aarch32-rt/src/arch_v8_hyp/interrupt.rs | 38 +++ aarch32-rt/src/arch_v8_hyp/mod.rs | 7 + aarch32-rt/src/arch_v8_hyp/svc.rs | 52 ++++ aarch32-rt/src/arch_v8_hyp/undefined.rs | 41 +++ aarch32-rt/src/lib.rs | 76 ++++- examples/mps3-an536-el2/.cargo/config.toml | 10 + examples/mps3-an536-el2/Cargo.toml | 31 ++ examples/mps3-an536-el2/README.md | 90 ++++++ examples/mps3-an536-el2/build.rs | 26 ++ examples/mps3-an536-el2/commands.gdb | 13 + examples/mps3-an536-el2/memory.x | 44 +++ .../abt-exception-a32-armv8r-none-eabihf.out | 8 + ...abt-exception-a32-thumbv8r-none-eabihf.out | 8 + .../abt-exception-t32-armv8r-none-eabihf.out | 8 + ...abt-exception-t32-thumbv8r-none-eabihf.out | 8 + .../generic-timer-armv8r-none-eabihf.out | 40 +++ .../generic-timer-thumbv8r-none-eabihf.out | 40 +++ .../reference/hello-armv8r-none-eabihf.out | 19 ++ .../reference/hello-thumbv8r-none-eabihf.out | 19 ++ .../reference/hvc-a32-armv8r-none-eabihf.out | 5 + .../hvc-a32-thumbv8r-none-eabihf.out | 5 + .../reference/hvc-t32-armv8r-none-eabihf.out | 5 + .../hvc-t32-thumbv8r-none-eabihf.out | 5 + ...fetch-exception-a32-armv8r-none-eabihf.out | 8 + ...tch-exception-a32-thumbv8r-none-eabihf.out | 8 + ...fetch-exception-t32-armv8r-none-eabihf.out | 8 + ...tch-exception-t32-thumbv8r-none-eabihf.out | 8 + .../reference/svc-a32-armv8r-none-eabihf.out | 5 + .../svc-a32-thumbv8r-none-eabihf.out | 5 + .../reference/svc-t32-armv8r-none-eabihf.out | 5 + .../svc-t32-thumbv8r-none-eabihf.out | 5 + ...undef-exception-a32-armv8r-none-eabihf.out | 8 + ...def-exception-a32-thumbv8r-none-eabihf.out | 8 + ...undef-exception-t32-armv8r-none-eabihf.out | 8 + ...def-exception-t32-thumbv8r-none-eabihf.out | 8 + examples/mps3-an536-el2/rust-toolchain.toml | 6 + .../src/bin/abt-exception-a32.rs | 120 ++++++++ .../src/bin/abt-exception-t32.rs | 120 ++++++++ .../mps3-an536-el2/src/bin/generic-timer.rs | 115 ++++++++ examples/mps3-an536-el2/src/bin/hello.rs | 28 ++ examples/mps3-an536-el2/src/bin/hvc-a32.rs | 64 +++++ examples/mps3-an536-el2/src/bin/hvc-t32.rs | 64 +++++ .../src/bin/prefetch-exception-a32.rs | 93 ++++++ .../src/bin/prefetch-exception-t32.rs | 95 ++++++ examples/mps3-an536-el2/src/bin/svc-a32.rs | 64 +++++ examples/mps3-an536-el2/src/bin/svc-t32.rs | 64 +++++ .../src/bin/undef-exception-a32.rs | 94 ++++++ .../src/bin/undef-exception-t32.rs | 97 +++++++ examples/mps3-an536-el2/src/lib.rs | 259 +++++++++++++++++ examples/mps3-an536-smp/src/bin/smp-test.rs | 10 +- examples/mps3-an536-smp/src/lib.rs | 2 +- examples/mps3-an536/src/bin/el2_hello.rs | 98 ------- .../mps3-an536/src/bin/generic_timer_irq.rs | 3 - examples/mps3-an536/src/bin/gic-map.rs | 3 - .../src/bin/gic-priority-ceiling.rs | 3 - .../src/bin/gic-static-section-irq.rs | 3 - examples/mps3-an536/src/lib.rs | 8 +- justfile | 19 ++ 72 files changed, 2579 insertions(+), 150 deletions(-) create mode 100644 aarch32-rt/src/arch_v8_hyp/abort.rs create mode 100644 aarch32-rt/src/arch_v8_hyp/hvc.rs create mode 100644 aarch32-rt/src/arch_v8_hyp/interrupt.rs create mode 100644 aarch32-rt/src/arch_v8_hyp/mod.rs create mode 100644 aarch32-rt/src/arch_v8_hyp/svc.rs create mode 100644 aarch32-rt/src/arch_v8_hyp/undefined.rs create mode 100644 examples/mps3-an536-el2/.cargo/config.toml create mode 100644 examples/mps3-an536-el2/Cargo.toml create mode 100644 examples/mps3-an536-el2/README.md create mode 100644 examples/mps3-an536-el2/build.rs create mode 100644 examples/mps3-an536-el2/commands.gdb create mode 100644 examples/mps3-an536-el2/memory.x create mode 100644 examples/mps3-an536-el2/reference/abt-exception-a32-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/abt-exception-a32-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/abt-exception-t32-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/abt-exception-t32-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/generic-timer-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/generic-timer-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/hello-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/hello-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/hvc-a32-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/hvc-a32-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/hvc-t32-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/hvc-t32-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/prefetch-exception-a32-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/prefetch-exception-a32-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/prefetch-exception-t32-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/prefetch-exception-t32-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/svc-a32-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/svc-a32-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/svc-t32-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/svc-t32-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/undef-exception-a32-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/undef-exception-a32-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/undef-exception-t32-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/reference/undef-exception-t32-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536-el2/rust-toolchain.toml create mode 100644 examples/mps3-an536-el2/src/bin/abt-exception-a32.rs create mode 100644 examples/mps3-an536-el2/src/bin/abt-exception-t32.rs create mode 100644 examples/mps3-an536-el2/src/bin/generic-timer.rs create mode 100644 examples/mps3-an536-el2/src/bin/hello.rs create mode 100644 examples/mps3-an536-el2/src/bin/hvc-a32.rs create mode 100644 examples/mps3-an536-el2/src/bin/hvc-t32.rs create mode 100644 examples/mps3-an536-el2/src/bin/prefetch-exception-a32.rs create mode 100644 examples/mps3-an536-el2/src/bin/prefetch-exception-t32.rs create mode 100644 examples/mps3-an536-el2/src/bin/svc-a32.rs create mode 100644 examples/mps3-an536-el2/src/bin/svc-t32.rs create mode 100644 examples/mps3-an536-el2/src/bin/undef-exception-a32.rs create mode 100644 examples/mps3-an536-el2/src/bin/undef-exception-t32.rs create mode 100644 examples/mps3-an536-el2/src/lib.rs delete mode 100644 examples/mps3-an536/src/bin/el2_hello.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index afc8b3ff..7e0be5d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -406,6 +406,50 @@ jobs: export PATH=/opt/qemu/bin:$PATH just test-qemu-v8r + # Run some SMP programs in QEMU 9 for Armv8-R + # These tests build with nightly as pinned by the rust-toolchain.toml file, because they include Tier 3 targets + test-qemu-v8r-smp: + runs-on: ubuntu-24.04 + needs: [build-all] + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Just + uses: taiki-e/install-action@just + - name: Install Dependencies + run: | + sudo apt-get -y update + sudo apt-get -y install libpixman-1-0 libfdt1 libglib2.0-0t64 + - name: Install custom QEMU into /opt + run: | + curl -sSL https://github.com/jonathanpallant/qemu9-for-ubuntu-2404/releases/download/qemu-9.2.3%2Bbuild0/qemu-9.2.3-ubuntu-24.04.tar.gz | sudo tar xvzf - -C / + - name: Run tests in QEMU + run: | + export PATH=/opt/qemu/bin:$PATH + just test-qemu-v8r-smp + + # Run some EL2 programs in QEMU 9 for Armv8-R + # These tests build with nightly as pinned by the rust-toolchain.toml file, because they include Tier 3 targets + test-qemu-v8r-el2: + runs-on: ubuntu-24.04 + needs: [build-all] + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Just + uses: taiki-e/install-action@just + - name: Install Dependencies + run: | + sudo apt-get -y update + sudo apt-get -y install libpixman-1-0 libfdt1 libglib2.0-0t64 + - name: Install custom QEMU into /opt + run: | + curl -sSL https://github.com/jonathanpallant/qemu9-for-ubuntu-2404/releases/download/qemu-9.2.3%2Bbuild0/qemu-9.2.3-ubuntu-24.04.tar.gz | sudo tar xvzf - -C / + - name: Run tests in QEMU + run: | + export PATH=/opt/qemu/bin:$PATH + just test-qemu-v8r-el2 + # Gather all the above QEMU jobs together for the purposes of getting an overall pass-fail test-qemu-all: runs-on: ubuntu-24.04 @@ -417,6 +461,8 @@ jobs: test-qemu-v7a, test-qemu-v7r, test-qemu-v8r, + test-qemu-v8r-smp, + test-qemu-v8r-el2, ] steps: - run: /bin/true diff --git a/.gitignore b/.gitignore index 666f29a1..e58f52da 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ examples/mps3-an536/target examples/mps3-an536/target-d32 examples/mps3-an536-smp/target examples/mps3-an536-smp/target-d32 +examples/mps3-an536-el2/target +examples/mps3-an536-el2/target-d32 examples/versatileab/target examples/versatileab/target-d32 Cargo.lock diff --git a/.vscode/settings.json b/.vscode/settings.json index fbd3ad9b..1c0f63d0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,5 +11,6 @@ "examples/versatileab/Cargo.toml", "examples/mps3-an536/Cargo.toml", "examples/mps3-an536-smp/Cargo.toml", + "examples/mps3-an536-el2/Cargo.toml" ] } diff --git a/Cargo.toml b/Cargo.toml index 2209a19b..61edd8f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ exclude = [ "examples/versatileab", "examples/mps3-an536", "examples/mps3-an536-smp", + "examples/mps3-an536-el2", ] members = [ "aarch32-cpu", diff --git a/aarch32-cpu/src/generic_timer/el2.rs b/aarch32-cpu/src/generic_timer/el2.rs index 89a56e95..fd34c915 100644 --- a/aarch32-cpu/src/generic_timer/el2.rs +++ b/aarch32-cpu/src/generic_timer/el2.rs @@ -8,7 +8,7 @@ use super::{El1PhysicalTimer, El1VirtualTimer, GenericTimer}; pub struct El2PhysicalTimer(El1PhysicalTimer); impl El2PhysicalTimer { - /// Create an EL1 Generic Timer handle + /// Create an EL2 Physical Timer handle /// /// # Safety /// @@ -79,7 +79,7 @@ impl GenericTimer for El2PhysicalTimer { pub struct El2VirtualTimer(El1VirtualTimer); impl El2VirtualTimer { - /// Create an EL1 Generic Timer handle + /// Create an EL2 Generic Timer handle /// /// # Safety /// diff --git a/aarch32-cpu/src/register/armv8r/hcptr.rs b/aarch32-cpu/src/register/armv8r/hcptr.rs index b4057aac..864adea7 100644 --- a/aarch32-cpu/src/register/armv8r/hcptr.rs +++ b/aarch32-cpu/src/register/armv8r/hcptr.rs @@ -3,10 +3,22 @@ use crate::register::{SysReg, SysRegRead, SysRegWrite}; /// HCPTR (*Hyp Architectural Feature Trap Register*) -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[bitbybit::bitfield(u32, debug, defmt_fields(feature = "defmt"))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Hcptr(pub u32); +pub struct Hcptr { + /// TCPAC - Traps EL1 accesses to the CPACR to Hyp mode + #[bit(31, rw)] + tcpac: bool, + /// TTA - Traps System register accesses to all implemented trace registers to Hyp mode + #[bit(20, rw)] + tta: bool, + /// TASE - Traps execution of Advanced SIMD instructions to Hyp mode when the value of HCPTR.TCP10 is 0. + #[bit(15, rw)] + tase: bool, + /// TCP - Trap accesses to Advanced SIMD and floating-point functionality to Hyp mode + #[bit(10, rw)] + tcp: bool, +} impl SysReg for Hcptr { const CP: u32 = 15; @@ -22,7 +34,18 @@ impl Hcptr { #[inline] /// Reads HCPTR (*Hyp Architectural Feature Trap Register*) pub fn read() -> Hcptr { - unsafe { Self(::read_raw()) } + unsafe { Self::new_with_raw_value(::read_raw()) } + } + + /// Modify HCPTR (*Hyp Architectural Feature Trap Register*) + #[inline] + pub fn modify(f: F) + where + F: FnOnce(&mut Self), + { + let mut value = Self::read(); + f(&mut value); + Self::write(value); } } @@ -31,13 +54,9 @@ impl crate::register::SysRegWrite for Hcptr {} impl Hcptr { #[inline] /// Writes HCPTR (*Hyp Architectural Feature Trap Register*) - /// - /// # Safety - /// - /// Ensure that this value is appropriate for this register - pub unsafe fn write(value: Self) { + pub fn write(value: Self) { unsafe { - ::write_raw(value.0); + ::write_raw(value.raw_value()); } } } diff --git a/aarch32-cpu/src/register/armv8r/hcr.rs b/aarch32-cpu/src/register/armv8r/hcr.rs index 9da41175..9424f92b 100644 --- a/aarch32-cpu/src/register/armv8r/hcr.rs +++ b/aarch32-cpu/src/register/armv8r/hcr.rs @@ -3,10 +3,106 @@ use crate::register::{SysReg, SysRegRead, SysRegWrite}; /// HCR (*Hyp Configuration Register*) -#[derive(Debug, Copy, Clone)] +#[bitbybit::bitfield(u32, debug, defmt_fields(feature = "defmt"))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Hcr { + /// TCPAC - Traps EL1 accesses to the CPACR to Hyp mode + #[bit(31, rw)] + tcpac: bool, + /// TRVM - Trap Reads of Memory controls + #[bit(30, rw)] + trvm: bool, + /// HCD - HVC instruction disable + #[bit(29, rw)] + hcd: bool, + /// TGE - Trap General Exceptions from EL0 + #[bit(27, rw)] + tge: bool, + /// TVM - Trap Memory controls + #[bit(26, rw)] + tvm: bool, + /// TPU - Trap cache maintenance instructions that operate to the Point of Unification + #[bit(24, rw)] + tpu: bool, + /// TPC - Trap data or unified cache maintenance instructions that operate to the Point of Coherency + #[bit(23, rw)] + tpc: bool, + /// TSW - Trap data or unified cache maintenance instructions that operate by Set/Way + #[bit(22, rw)] + tsw: bool, + /// TAC - Trap Auxiliary Control Registers + #[bit(21, rw)] + tac: bool, + /// TIDCP - Trap IMPLEMENTATION DEFINED functionality + #[bit(20, rw)] + tidcp: bool, + /// TID3 - Trap ID group 3 + #[bit(18, rw)] + tid3: bool, + /// TID2 - Trap ID group 2 + #[bit(17, rw)] + tid2: bool, + /// TID1 - Trap ID group 1 + #[bit(16, rw)] + tid1: bool, + /// TID0 - Trap ID group 0 + #[bit(15, rw)] + tid0: bool, + /// TWE - Traps EL0 and EL1 execution of WFE instructions to Hyp mode + #[bit(14, rw)] + twe: bool, + /// TWI - Traps EL0 and EL1 execution of WFI instructions to Hyp mode + #[bit(13, rw)] + twi: bool, + /// DC - Default Cacheability + #[bit(12, rw)] + dc: bool, + /// BSU - Barrier Shareability upgrade. + #[bits(10..=11, rw)] + bsu: Bsu, + /// FB - Force broadcast + #[bit(9, rw)] + fb: bool, + /// VA - Virtual SError interrupt exception + #[bit(8, rw)] + va: bool, + /// VI - Virtual IRQ exception + #[bit(7, rw)] + vi: bool, + /// VF - Virtual FIQ exception + #[bit(6, rw)] + vf: bool, + /// AMO - SError interrupt Mask Override + #[bit(5, rw)] + amo: bool, + /// IMO - IRQ Mask Override + #[bit(4, rw)] + imo: bool, + /// FMO - FIQ Mask Override + #[bit(3, rw)] + fmo: bool, + /// SWIO - Set/Way Invalidation Override + #[bit(1, rw)] + swio: bool, + /// VM - Virtualization enable + #[bit(0, rw)] + vm: bool, +} + +/// Barrier Shareability upgrade +/// +/// This field determines the minimum Shareability domain that is applied to any +/// barrier instruction executed from EL1 or EL0 +#[bitbybit::bitenum(u2, exhaustive = true)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Hcr(pub u32); +#[derive(Debug, PartialEq, Eq)] +pub enum Bsu { + NoEffect = 0b00, + InnerShareable = 0b01, + OuterShareable = 0b10, + FullSystem = 0b11, +} impl SysReg for Hcr { const CP: u32 = 15; @@ -22,7 +118,7 @@ impl Hcr { #[inline] /// Reads HCR (*Hyp Configuration Register*) pub fn read() -> Hcr { - unsafe { Self(::read_raw()) } + unsafe { Self::new_with_raw_value(::read_raw()) } } } @@ -31,13 +127,20 @@ impl crate::register::SysRegWrite for Hcr {} impl Hcr { #[inline] /// Writes HCR (*Hyp Configuration Register*) - /// - /// # Safety - /// - /// Ensure that this value is appropriate for this register - pub unsafe fn write(value: Self) { + pub fn write(value: Self) { unsafe { - ::write_raw(value.0); + ::write_raw(value.raw_value()); } } + + #[inline] + /// Modify HCR (*Hyp Configuration Register*) + pub fn modify(f: F) + where + F: FnOnce(&mut Self), + { + let mut value = Self::read(); + f(&mut value); + Self::write(value); + } } diff --git a/aarch32-cpu/src/register/armv8r/hsr.rs b/aarch32-cpu/src/register/armv8r/hsr.rs index 40c2fe8f..b499db95 100644 --- a/aarch32-cpu/src/register/armv8r/hsr.rs +++ b/aarch32-cpu/src/register/armv8r/hsr.rs @@ -2,7 +2,7 @@ use crate::register::{SysReg, SysRegRead, SysRegWrite}; -use arbitrary_int::u25; +use arbitrary_int::{u2, u25, u3, u4, u6}; /// HSR (*Hyp Syndrome Register*) #[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))] @@ -27,6 +27,17 @@ pub struct Hsr { iss: u25, } +impl Hsr { + /// Get the ISS value from the HSR + pub fn get_iss(&self) -> Option { + if let Ok(ec) = self.ec() { + Some(ec.decode_iss(self.iss())) + } else { + None + } + } +} + #[bitbybit::bitenum(u6, exhaustive = false)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -52,6 +63,263 @@ pub enum ExceptionClass { DataAbortFromCurrent = 0b10_0101, } +/// A decoded ISS +/// +/// ISS is a 25 bit field whose meaning varies depending on the value of the EC field. +#[derive(Debug, Clone)] +pub enum Iss { + Unknown(IssUnknown), + TrappedWfiWfe(IssTrappedWfiWfe), + TrappedCp15McrMrc(IssTrappedMcrMrc), + TrappedCp15McrrMrrc(IssTrappedMcrrMrrc), + TrappedCp14McrMrc(IssTrappedMcrMrc), + TrappedLdcStc(IssTrappedLdcStc), + TrappedFpu(IssTrappedFpu), + TrappedVmrs(IssTrappedVmrs), + TrappedCp14McrrMrrc(IssTrappedMcrrMrrc), + IllegalAArch32Eret, + Svc(IssCall), + Hvc(IssCall), + Smc(IssSmc), + PrefetchAbortFromLower(IssPrefetchAbort), + PrefetchAbortFromCurrent(IssPrefetchAbort), + PcAlignment, + DataAbortFromLower(IssDataAbort), + DataAbortFromCurrent(IssDataAbort), +} + +impl ExceptionClass { + pub fn decode_iss(&self, iss: u25) -> Iss { + match self { + ExceptionClass::Unknown => Iss::Unknown(IssUnknown(iss.value())), + ExceptionClass::TrappedWfiWfe => { + Iss::TrappedWfiWfe(IssTrappedWfiWfe::new_with_raw_value(iss)) + } + ExceptionClass::TrappedCp15McrMrc => { + Iss::TrappedCp15McrMrc(IssTrappedMcrMrc::new_with_raw_value(iss)) + } + ExceptionClass::TrappedCp15McrrMrrc => { + Iss::TrappedCp15McrrMrrc(IssTrappedMcrrMrrc::new_with_raw_value(iss)) + } + ExceptionClass::TrappedCp14McrMrc => { + Iss::TrappedCp14McrMrc(IssTrappedMcrMrc::new_with_raw_value(iss)) + } + ExceptionClass::TrappedLdcStc => { + Iss::TrappedLdcStc(IssTrappedLdcStc::new_with_raw_value(iss)) + } + ExceptionClass::TrappedFpu => Iss::TrappedFpu(IssTrappedFpu::new_with_raw_value(iss)), + ExceptionClass::TrappedVmrs => Iss::TrappedVmrs(IssTrappedVmrs(iss.value())), + ExceptionClass::TrappedCp14McrrMrrc => { + Iss::TrappedCp14McrrMrrc(IssTrappedMcrrMrrc::new_with_raw_value(iss)) + } + ExceptionClass::IllegalAArch32Eret => Iss::IllegalAArch32Eret, + ExceptionClass::Svc => Iss::Svc(IssCall::new_with_raw_value(iss)), + ExceptionClass::Hvc => Iss::Hvc(IssCall::new_with_raw_value(iss)), + ExceptionClass::Smc => Iss::Smc(IssSmc(iss.value())), + ExceptionClass::PrefetchAbortFromLower => { + Iss::PrefetchAbortFromLower(IssPrefetchAbort::new_with_raw_value(iss)) + } + ExceptionClass::PrefetchAbortFromCurrent => { + Iss::PrefetchAbortFromCurrent(IssPrefetchAbort::new_with_raw_value(iss)) + } + ExceptionClass::PcAlignment => Iss::PcAlignment, + ExceptionClass::DataAbortFromLower => { + Iss::DataAbortFromLower(IssDataAbort::new_with_raw_value(iss)) + } + ExceptionClass::DataAbortFromCurrent => { + Iss::DataAbortFromCurrent(IssDataAbort::new_with_raw_value(iss)) + } + } + } +} + +/// The ISS field when EC = ExceptionClass::Unknown +/// +/// All bits are reserved +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct IssUnknown(pub u32); + +/// The ISS field when EC = ExceptionClass::TrappedWfiWfe +#[bitbybit::bitfield(u25, debug, defmt_bitfields(feature = "defmt"))] +pub struct IssTrappedWfiWfe { + /// Condition code valid + #[bit(24, r)] + cv: bool, + /// Condition code + #[bits(20..=23, r)] + cc: u4, + /// Trapped Instruction is WFE + #[bit(0, r)] + ti: bool, +} + +/// The ISS field when EC = ExceptionClass::TrappedCp15McrMrc or ExceptionClass::TrappedCp14McrMrc +#[bitbybit::bitfield(u25, debug, defmt_bitfields(feature = "defmt"))] +pub struct IssTrappedMcrMrc { + /// Condition code valid + #[bit(24, r)] + cv: bool, + /// Condition code + #[bits(20..=23, r)] + cc: u4, + /// OPC2 value from instruction + #[bits(17..=19, r)] + opc2: u3, + /// OPC1 value from instruction + #[bits(14..=16, r)] + opc1: u3, + /// CRn value from instruction + #[bits(10..=13, r)] + crn: u4, + /// Rt value from instruction + #[bits(5..=8, r)] + rt: u4, + /// CRm value from instruction + #[bits(1..=4, r)] + crm: u4, + /// Direction (true = read, false = write) + #[bit(0, r)] + is_read: bool, +} + +/// The ISS field when EC = ExceptionClass::TrappedCp15McrrMrrc or ExceptionClass::TrappedCp14McrrMrrc +#[bitbybit::bitfield(u25, debug, defmt_bitfields(feature = "defmt"))] +pub struct IssTrappedMcrrMrrc { + /// Condition code valid + #[bit(24, r)] + cv: bool, + /// Condition code + #[bits(20..=23, r)] + cc: u4, + /// OPC2 value from instruction + #[bits(16..=19, r)] + opc2: u4, + /// Rt2 value from instruction + #[bits(10..=13, r)] + rt2: u4, + /// Rt value from instruction + #[bits(5..=8, r)] + rt: u4, + /// CRm value from instruction + #[bits(1..=4, r)] + crm: u4, + /// Direction (true = read, false = write) + #[bit(0, r)] + is_read: bool, +} + +/// The ISS field when EC = ExceptionClass::TrappedLdcStc +#[bitbybit::bitfield(u25, debug, defmt_bitfields(feature = "defmt"))] +pub struct IssTrappedLdcStc { + /// Condition code valid + #[bit(24, r)] + cv: bool, + /// Condition code + #[bits(20..=23, r)] + cc: u4, + /// The immediate value from the instruction + #[bits(12..=19, r)] + imm8: u8, + /// Rn value from instruction + #[bits(5..=8, r)] + rn: u4, + /// Whether offset is added (true) or subtracted (false) + #[bit(4, r)] + offset: bool, + /// Addressing Mode + #[bits(1..=3, r)] + am: u3, + /// Direction (true = read, false = write) + #[bit(0, r)] + is_read: bool, +} + +/// The ISS field when EC = ExceptionClass::TrappedFpu +#[bitbybit::bitfield(u25, debug, defmt_bitfields(feature = "defmt"))] +pub struct IssTrappedFpu { + /// Condition code valid + #[bit(24, r)] + cv: bool, + /// Condition code + #[bits(20..=23, r)] + cc: u4, + /// Trapped Advanced SIMD + #[bit(5, r)] + ta: bool, + /// CoProc Bits + #[bits(0..=3, r)] + coproc: u4, +} + +/// The ISS field when EC = ExceptionClass::TrappedVmrs +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct IssTrappedVmrs(pub u32); + +/// The ISS field when EC = ExceptionClass::Svc +#[bitbybit::bitfield(u25, debug, defmt_bitfields(feature = "defmt"))] +pub struct IssCall { + /// Immediate value from instruction + #[bits(0..=15, r)] + imm16: u16, +} + +/// The ISS field when EC = ExceptionClass::Smc +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct IssSmc(pub u32); + +/// The ISS field when EC = ExceptionClass::PrefetchAbortFromLower or ExceptionClass::PrefetchAbortFromCurrent +#[bitbybit::bitfield(u25, debug, defmt_bitfields(feature = "defmt"))] +pub struct IssPrefetchAbort { + /// FAR not Valid, for a Synchronous External abort. + #[bit(10, r)] + fnv: bool, + /// External Abort Type. + /// + /// External = true, anything else = false + #[bit(9, r)] + ea: bool, + /// Instruction Fault Status Code + #[bits(0..=5, r)] + ifsc: u6, +} + +/// The ISS field when EC = ExceptionClass::DataAbortFromLower or ExceptionClass::DataAbortFromCurrent +#[bitbybit::bitfield(u25, debug, defmt_bitfields(feature = "defmt"))] +pub struct IssDataAbort { + /// Instruction Syndrome Valid + #[bit(24, r)] + isv: bool, + /// Syndrome Access Size + #[bits(22..=23, r)] + sas: u2, + /// Syndrome Sign Extend + #[bit(21, r)] + sae: bool, + /// Syndrome Register transfer + #[bits(16..=19, r)] + srt: u4, + /// Acquire/Release + #[bit(14, r)] + ar: bool, + /// FAR not Valid + #[bit(10, r)] + fnv: bool, + /// External Abort Type. + /// + /// External = true, anything else = false + #[bit(9, r)] + ea: bool, + /// Cache maintenance + #[bit(8, r)] + cm: bool, + /// Write not Read + #[bit(6, r)] + wnr: bool, + /// Data Fault Status Code + #[bits(0..=5, r)] + dfsc: u6, +} + #[bitbybit::bitenum(u1, exhaustive = true)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/aarch32-rt/Cargo.toml b/aarch32-rt/Cargo.toml index 8e9f25ac..f796ebe7 100644 --- a/aarch32-rt/Cargo.toml +++ b/aarch32-rt/Cargo.toml @@ -35,6 +35,8 @@ eabi-fpu = [] # if you have set the `+d32` target feature) then you need to enable this # option otherwise important FPU state may be lost when an exception occurs. fpu-d32 = [] +# Leave the CPU in HYP mode (EL2), and handle exceptions in HYP mode +el2-mode = [] [build-dependencies] arm-targets = { version = "0.4.0", path = "../arm-targets" } diff --git a/aarch32-rt/src/arch_v4/interrupt.rs b/aarch32-rt/src/arch_v4/interrupt.rs index a82296b3..db178f6f 100644 --- a/aarch32-rt/src/arch_v4/interrupt.rs +++ b/aarch32-rt/src/arch_v4/interrupt.rs @@ -20,7 +20,7 @@ core::arch::global_asm!( push {{ lr }} // save adjusted LR to IRQ stack mrs lr, spsr // The hardware has copied the interrupted task's CPSR to SPSR_irq - grab it and push {{ lr }} // save it to IRQ stack using LR - msr cpsr_c, {sys_mode} // switch to system mode so we can handle another interrupt (because if we interrupt irq mode we trash our own shadow registers) + msr cpsr_c, {sys_mode} // switch to system mode (because if we interrupt in IRQ mode we trash IRQ mode's LR) push {{ lr }} // Save LR of system mode before using it for stack alignment mov lr, sp // align SP down to eight byte boundary using LR and lr, lr, 7 // diff --git a/aarch32-rt/src/arch_v7/hvc.rs b/aarch32-rt/src/arch_v7/hvc.rs index 35474984..0840d698 100644 --- a/aarch32-rt/src/arch_v7/hvc.rs +++ b/aarch32-rt/src/arch_v7/hvc.rs @@ -15,7 +15,7 @@ core::arch::global_asm!( .global _asm_default_hvc_handler .type _asm_default_hvc_handler, %function _asm_default_hvc_handler: - push {{ r12, lr }} // push state to stack + push {{ r12, lr }} // give us R12 and LR to work with push {{ r0-r5 }} // push frame to stack mov r12, sp // r12 = pointer to Frame "#, diff --git a/aarch32-rt/src/arch_v7/interrupt.rs b/aarch32-rt/src/arch_v7/interrupt.rs index dab9b3c6..3a07c659 100644 --- a/aarch32-rt/src/arch_v7/interrupt.rs +++ b/aarch32-rt/src/arch_v7/interrupt.rs @@ -16,7 +16,7 @@ core::arch::global_asm!( _asm_default_irq_handler: sub lr, lr, 4 // make sure we jump back to the right place srsfd sp!, #{sys_mode} // store return state to SYS stack - cps #{sys_mode} // switch to system mode so we can handle another interrupt (because if we interrupt irq mode we trash our own shadow registers) + cps #{sys_mode} // switch to system mode (because if we interrupt in IRQ mode we trash IRQ mode's LR) push {{ lr }} // save adjusted LR to SYS stack mov lr, sp // align SP down to eight byte boundary using LR and lr, lr, 7 // diff --git a/aarch32-rt/src/arch_v8_hyp/abort.rs b/aarch32-rt/src/arch_v8_hyp/abort.rs new file mode 100644 index 00000000..8a281228 --- /dev/null +++ b/aarch32-rt/src/arch_v8_hyp/abort.rs @@ -0,0 +1,77 @@ +//! Data and Prefetch Abort handlers for Armv8-R at EL2 + +core::arch::global_asm!( + r#" + // Work around https://github.com/rust-lang/rust/issues/127269 + .fpu vfp3 + + .section .text._asm_default_data_abort_handler + + // Called from the vector table when we have an undefined exception. + // Saves state and calls a C-compatible handler like + // `extern "C" fn _data_abort_handler(addr: usize);` + .global _asm_default_data_abort_handler + .type _asm_default_data_abort_handler, %function + _asm_default_data_abort_handler: + push {{ r0-r3, r12, lr }} // preserve state that C function won't save + mrs r0, elr_hyp // grab ELR_hyp + mrs r1, spsr_hyp // grab SPSR_hyp + mov r12, sp // align SP down to eight byte boundary using R12 + and r12, r12, 7 // + sub sp, r12 // SP now aligned - only push 64-bit values from here + push {{ r0-r2, r12 }} // save ELR, SPSR, padding and alignment amount + "#, + crate::save_fpu_context!(), + r#" + mrs r0, elr_hyp // Pass the faulting instruction address to the handler. + bl _data_abort_handler // call C handler + msr elr_hyp, r0 // if we get back here, assume they returned a new LR in r0 + "#, + crate::restore_fpu_context!(), + r#" + pop {{ r0-r2, r12 }} // restore ELR, SPSR, padding and alignment amount + add sp, r12 // restore SP alignment + msr spsr_hyp, r1 // restore SPSR + pop {{ r0-r3, r12, lr }} // restore state that C function didn't save + eret // Return from the asm handler + .size _asm_default_data_abort_handler, . - _asm_default_data_abort_handler + "#, +); + +core::arch::global_asm!( + r#" + // Work around https://github.com/rust-lang/rust/issues/127269 + .fpu vfp3 + + .section .text._asm_default_prefetch_abort_handler + + // Called from the vector table when we have an undefined exception. + // Saves state and calls a C-compatible handler like + // `extern "C" fn _prefetch_abort_handler(addr: usize);` + .global _asm_default_prefetch_abort_handler + .type _asm_default_prefetch_abort_handler, %function + _asm_default_prefetch_abort_handler: + push {{ r0-r3, r12, lr }} // preserve state that C function won't save + mrs r0, elr_hyp // grab ELR_hyp + mrs r1, spsr_hyp // grab SPSR_hyp + mov r12, sp // align SP down to eight byte boundary using R12 + and r12, r12, 7 // + sub sp, r12 // SP now aligned - only push 64-bit values from here + push {{ r0-r2, r12 }} // save ELR, SPSR, padding and alignment amount + "#, + crate::save_fpu_context!(), + r#" + mrs r0, elr_hyp // Pass the faulting instruction address to the handler. + bl _prefetch_abort_handler // call C handler + msr elr_hyp, r0 // if we get back here, assume they returned a new LR in r0 + "#, + crate::restore_fpu_context!(), + r#" + pop {{ r0-r2, r12 }} // restore ELR, SPSR, padding and alignment amount + add sp, r12 // restore SP alignment + msr spsr_hyp, r1 // restore SPSR + pop {{ r0-r3, r12, lr }} // restore state that C function didn't save + eret // Return from the asm handler + .size _asm_default_prefetch_abort_handler, . - _asm_default_prefetch_abort_handler + "#, +); diff --git a/aarch32-rt/src/arch_v8_hyp/hvc.rs b/aarch32-rt/src/arch_v8_hyp/hvc.rs new file mode 100644 index 00000000..171f165f --- /dev/null +++ b/aarch32-rt/src/arch_v8_hyp/hvc.rs @@ -0,0 +1,46 @@ +//! HVC handler for Armv8-R at EL2 + +#[cfg(target_arch = "arm")] +core::arch::global_asm!( + r#" + // Work around https://github.com/rust-lang/rust/issues/127269 + .fpu vfp3 + + .section .text._asm_default_hvc_handler + + // Called from the vector table when we have an hypervisor call. + // Saves state and calls a C-compatible handler like + // `extern "C" fn _hvc_handler(hsr: u32, frame: &Frame) -> u32;` + .global _asm_default_hvc_handler + .type _asm_default_hvc_handler, %function + _asm_default_hvc_handler: + push {{ r12, lr }} // give us R12 and LR to work with + mrs lr, elr_hyp // grab elr + mrs r12, spsr_hyp // grab spsr + push {{ r12, lr }} // push them to stack + mov r12, sp // align SP down to eight byte boundary using R12 + and r12, r12, 7 // + sub sp, r12 // SP now aligned - only push 64-bit values from here + push {{ r0-r6, r12 }} // push frame and alignment amount to stack + mov r12, sp // r12 = pointer to Frame + "#, + crate::save_fpu_context!(), + r#" + mrc p15, 4, r0, c5, c2, 0 // r0 = HSR value + mov r1, r12 // r1 = frame pointer + bl _hvc_handler + mov lr, r0 // copy return value into LR, because we're about to use r0 in the FPU restore + "#, + crate::restore_fpu_context!(), + r#" + pop {{ r0-r6, r12 }} // restore frame and alignment + mov r0, lr // copy return value from lr back to r0, overwriting saved r0 + add sp, r12 // restore SP alignment using R12 + pop {{ r12, lr }} // pop elr and spsr from stack + msr elr_hyp, lr // restore elr + msr spsr_hyp, r12 // restore spsr + pop {{ r12, lr }} // pop R12 and LR from stack + eret // Return from the asm handler + .size _asm_default_hvc_handler, . - _asm_default_hvc_handler + "#, +); diff --git a/aarch32-rt/src/arch_v8_hyp/interrupt.rs b/aarch32-rt/src/arch_v8_hyp/interrupt.rs new file mode 100644 index 00000000..8295aaf1 --- /dev/null +++ b/aarch32-rt/src/arch_v8_hyp/interrupt.rs @@ -0,0 +1,38 @@ +//! IRQ handler for Armv8-R at EL2 + +#[cfg(target_arch = "arm")] +core::arch::global_asm!( + r#" + // Work around https://github.com/rust-lang/rust/issues/127269 + .fpu vfp3 + + .section .text._asm_default_irq_handler + + // Called from the vector table when we have an interrupt. + // Saves state and calls a C-compatible handler like + // `extern "C" fn _irq_handler();` + .global _asm_default_irq_handler + .type _asm_default_irq_handler, %function + _asm_default_irq_handler: + push {{ r0-r3, r12, lr }} // preserve state that C function won't save + mrs r0, elr_hyp // grab ELR_hyp + mrs r1, spsr_hyp // grab SPSR_hyp + mov r12, sp // align SP down to eight byte boundary using R12 + and r12, r12, 7 // + sub sp, r12 // SP now aligned - only push 64-bit values from here + push {{ r0-r2, r12 }} // save ELR, SPSR, padding and alignment amount + "#, + crate::save_fpu_context!(), + r#" + bl _irq_handler // call C handler (they may choose to re-enable interrupts) + "#, + crate::restore_fpu_context!(), + r#" + pop {{ r0-r2, r12 }} // restore ELR, SPSR, padding and alignment amount + add sp, r12 // restore SP alignment + msr spsr_hyp, r1 // restore SPSR + pop {{ r0-r3, r12, lr }} // restore state that C function didn't save + eret // Return from the asm handler + .size _asm_default_irq_handler, . - _asm_default_irq_handler + "#, +); diff --git a/aarch32-rt/src/arch_v8_hyp/mod.rs b/aarch32-rt/src/arch_v8_hyp/mod.rs new file mode 100644 index 00000000..144f7494 --- /dev/null +++ b/aarch32-rt/src/arch_v8_hyp/mod.rs @@ -0,0 +1,7 @@ +//! ASM routines for Armv8-R at EL2 + +mod abort; +mod hvc; +mod interrupt; +mod svc; +mod undefined; diff --git a/aarch32-rt/src/arch_v8_hyp/svc.rs b/aarch32-rt/src/arch_v8_hyp/svc.rs new file mode 100644 index 00000000..65e9a337 --- /dev/null +++ b/aarch32-rt/src/arch_v8_hyp/svc.rs @@ -0,0 +1,52 @@ +//! SVC handler for Armv8-R at EL2 + +#[cfg(target_arch = "arm")] +core::arch::global_asm!( + r#" + // Work around https://github.com/rust-lang/rust/issues/127269 + .fpu vfp3 + + // Called from the vector table when we have an hypervisor call from Hyp + // mode (which seems to end up in this SVC handler). + // + // Saves state and calls a C-compatible handler like `extern "C" fn + // _hvc_handler(hsr: u32, frame: &Frame) -> u32;` + // + // NOTE: We call '_hvc_handler' rather than '_svc_handler', because we are + // passing the Hypervisor Syndrome Register contents, rather trying to parse + // the HVC instruction. + .section .text._asm_default_svc_handler + .arm + .global _asm_default_svc_handler + .type _asm_default_svc_handler, %function + _asm_default_svc_handler: + push {{ r12, lr }} // give us R12 and LR to work with + mrs lr, elr_hyp // grab elr + mrs r12, spsr_hyp // grab spsr + push {{ r12, lr }} // push them to stack + mov r12, sp // align SP down to eight byte boundary using R12 + and r12, r12, 7 // + sub sp, r12 // SP now aligned - only push 64-bit values from here + push {{ r0-r6, r12 }} // push frame and alignment amount to stack + mov r12, sp // r12 = pointer to Frame + "#, + crate::save_fpu_context!(), + r#" + mrc p15, 4, r0, c5, c2, 0 // r0 = HSR value + mov r1, r12 // r1 = frame pointer + bl _hvc_handler + mov lr, r0 // copy return value into LR, because we're about to use r0 in the FPU restore + "#, + crate::restore_fpu_context!(), + r#" + pop {{ r0-r6, r12 }} // restore frame and alignment + mov r0, lr // copy return value from lr back to r0, overwriting saved r0 + add sp, r12 // restore SP alignment using R12 + pop {{ r12, lr }} // pop elr and spsr from stack + msr elr_hyp, lr // restore elr + msr spsr_hyp, r12 // restore spsr + pop {{ r12, lr }} // pop R12 and LR from stack + eret // Return from the asm handler + .size _asm_default_svc_handler, . - _asm_default_svc_handler + "#, +); diff --git a/aarch32-rt/src/arch_v8_hyp/undefined.rs b/aarch32-rt/src/arch_v8_hyp/undefined.rs new file mode 100644 index 00000000..8c4b0d5e --- /dev/null +++ b/aarch32-rt/src/arch_v8_hyp/undefined.rs @@ -0,0 +1,41 @@ +//! Undefined handler for Armv8-R at EL2 + +#[cfg(target_arch = "arm")] +core::arch::global_asm!( + r#" + // Work around https://github.com/rust-lang/rust/issues/127269 + .fpu vfp3 + + // Called from the vector table when we have an undefined exception. + // Saves state and calls a C-compatible handler like + // `extern "C" fn _undefined_handler(addr: usize) -> usize;` + // or + // `extern "C" fn _undefined_handler(addr: usize) -> !;` + .section .text._asm_default_undefined_handler + .global _asm_default_undefined_handler + .type _asm_default_undefined_handler, %function + _asm_default_undefined_handler: + push {{ r0-r3, r12, lr }} // preserve state that C function won't save + mrs r0, elr_hyp // grab ELR_hyp + mrs r1, spsr_hyp // grab SPSR_hyp + mov r12, sp // align SP down to eight byte boundary using R12 + and r12, r12, 7 // + sub sp, r12 // SP now aligned - only push 64-bit values from here + push {{ r0-r2, r12 }} // save ELR, SPSR, padding and alignment amount + "#, + crate::save_fpu_context!(), + r#" + mrs r0, elr_hyp // Pass the faulting instruction address to the handler. + bl _undefined_handler // call C handler + msr elr_hyp, r0 // if we get back here, assume they returned a new LR in r0 + "#, + crate::restore_fpu_context!(), + r#" + pop {{ r0-r2, r12 }} // restore ELR, SPSR, padding and alignment amount + add sp, r12 // restore SP alignment + msr spsr_hyp, r1 // restore SPSR + pop {{ r0-r3, r12, lr }} // restore state that C function didn't save + eret // Return from the asm handler + .size _asm_default_undefined_handler, . - _asm_default_undefined_handler + "#, +); diff --git a/aarch32-rt/src/lib.rs b/aarch32-rt/src/lib.rs index 03d49290..8cd6940b 100644 --- a/aarch32-rt/src/lib.rs +++ b/aarch32-rt/src/lib.rs @@ -517,11 +517,9 @@ //! not save a great deal of state on entry to an exception handler, unlike //! Armv7-M (and other M-Profile) processors. We must therefore save this state //! to the stack using assembly language, before transferring to an `extern "C"` -//! function. We do not change modes before entering that `extern "C"` function - -//! that's for the handler to deal with as it wishes. Because FIQ is often -//! performance-sensitive, we don't supply an FIQ trampoline; if you want to use -//! FIQ, you have to write your own assembly routine, allowing you to preserve -//! only whatever state is important to you. +//! function. Because FIQ is often performance-sensitive, we don't supply an FIQ +//! trampoline; if you want to use FIQ, you have to write your own assembly +//! routine, allowing you to preserve only whatever state is important to you. //! //! ## Examples //! @@ -533,18 +531,21 @@ #[cfg(target_arch = "arm")] use aarch32_cpu::register::{cpsr::ProcessorMode, Cpsr}; -#[cfg(arm_architecture = "v8-r")] +#[cfg(all(arm_architecture = "v8-r", not(feature = "el2-mode")))] use aarch32_cpu::register::Hactlr; pub use aarch32_rt_macros::{entry, exception, irq}; +#[cfg(all(target_arch = "arm", arm_architecture = "v8-r", feature = "el2-mode"))] +mod arch_v8_hyp; + #[cfg(all( target_arch = "arm", any( arm_architecture = "v7-a", arm_architecture = "v7-r", - arm_architecture = "v8-r" - ) + all(arm_architecture = "v8-r", not(feature = "el2-mode")) + ), ))] mod arch_v7; @@ -960,7 +961,7 @@ core::arch::global_asm!( // Set up stacks. mov r0, #0 bl _stack_setup_preallocated - "#, + "#, fpu_enable!(), r#" // Zero all registers before calling kmain @@ -985,13 +986,13 @@ core::arch::global_asm!( "# ); -// Start-up code for Armv8-R. +// Start-up code for Armv8-R to switch to EL1. // // There's only one Armv8-R CPU (the Cortex-R52) and the FPU is mandatory, so we // always enable it. // // We boot into EL2, set up a stack pointer, and run `kmain` in EL1. -#[cfg(arm_architecture = "v8-r")] +#[cfg(all(arm_architecture = "v8-r", not(feature = "el2-mode")))] core::arch::global_asm!( r#" // Work around https://github.com/rust-lang/rust/issues/127269 @@ -1080,3 +1081,56 @@ core::arch::global_asm!( .raw_value() } ); + +// Start-up code for Armv8-R to stay in EL2. +// +// There's only one Armv8-R CPU (the Cortex-R52) and the FPU is mandatory, so we +// always enable it. +// +// We boot into EL2, set up a HYP stack pointer, and run `kmain` in EL2. +#[cfg(all(arm_architecture = "v8-r", feature = "el2-mode"))] +core::arch::global_asm!( + r#" + // Work around https://github.com/rust-lang/rust/issues/127269 + .fpu vfp3 + + .section .text.default_start + .global _default_start + .type _default_start, %function + _default_start: + // Init .data and .bss + bl _init_segments + // Set stack pointer + ldr sp, =_hyp_stack_high_end + // Set the HVBAR (for EL2) to _vector_table + ldr r1, =_vector_table + mcr p15, 4, r1, c12, c0, 0 + // Mask IRQ and FIQ + mrs r0, CPSR + orr r0, {irq_fiq} + msr CPSR, r0 + "#, + fpu_enable!(), + r#" + // Zero all registers before calling kmain + mov r0, 0 + mov r1, 0 + mov r2, 0 + mov r3, 0 + mov r4, 0 + mov r5, 0 + mov r6, 0 + mov r7, 0 + mov r8, 0 + mov r9, 0 + mov r10, 0 + mov r11, 0 + mov r12, 0 + // Jump to application + bl kmain + // In case the application returns, loop forever + b . + .size _default_start, . - _default_start + "#, + irq_fiq = const aarch32_cpu::register::Cpsr::new_with_raw_value(0).with_i(true).with_f(true).raw_value() +); diff --git a/examples/mps3-an536-el2/.cargo/config.toml b/examples/mps3-an536-el2/.cargo/config.toml new file mode 100644 index 00000000..6960eb34 --- /dev/null +++ b/examples/mps3-an536-el2/.cargo/config.toml @@ -0,0 +1,10 @@ +[target.armv8r-none-eabihf] +# Note, this requires QEMU 9 or higher +runner = "qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -audio none -kernel" + +[target.thumbv8r-none-eabihf] +# Note, this requires QEMU 9 or higher +runner = "qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -audio none -kernel" + +[build] +target = "armv8r-none-eabihf" \ No newline at end of file diff --git a/examples/mps3-an536-el2/Cargo.toml b/examples/mps3-an536-el2/Cargo.toml new file mode 100644 index 00000000..5b30c467 --- /dev/null +++ b/examples/mps3-an536-el2/Cargo.toml @@ -0,0 +1,31 @@ +[package] +authors = [ + "Jonathan Pallant ", + "The Embedded Devices Working Group Arm Team " +] +default-run = "hello" +description = "Examples for MPS3-AN536 device (Arm Cortex-R52)" +edition = "2024" +homepage = "https://github.com/rust-embedded/aarch32" +license = "MIT OR Apache-2.0" +name = "mps3-an536-el2" +publish = false +readme = "README.md" +repository = "https://github.com/rust-embedded/aarch32.git" +version = "0.0.0" + +[dependencies] +aarch32-cpu = { path = "../../aarch32-cpu", features = ["critical-section-multi-core"] } +aarch32-rt = { path = "../../aarch32-rt", features = ["el2-mode"] } +arm-gic = { version = "0.7.1" } +critical-section = "1.2.0" +heapless = "0.9.1" +libm = "0.2.15" +semihosting = { version = "0.1.18", features = ["stdio"] } + +[build-dependencies] +arm-targets = {version = "0.4.0", path = "../../arm-targets"} + +[features] +eabi-fpu = ["aarch32-rt/eabi-fpu"] +fpu-d32 = ["aarch32-rt/fpu-d32"] diff --git a/examples/mps3-an536-el2/README.md b/examples/mps3-an536-el2/README.md new file mode 100644 index 00000000..1f71befe --- /dev/null +++ b/examples/mps3-an536-el2/README.md @@ -0,0 +1,90 @@ +# Examples for Arm MPS3-AN536 + +This package contains example binaries for the Arm MPS3-AN536 evaluation system, +featuring one or two Arm Cortex-R52 processor cores. This crate is tested on the +following targets: + +- `armv8r-none-eabihf` - ARMv8-R AArch32, hard-float, Arm mode +- `thumbv8r-none-eabihf` - ARMv8-R AArch32, hard-float, Thumb mode + +The repo-level [`.cargo/config.toml`] will ensure the code runs on the +appropriate QEMU configuration. + +As of Rust 1.92, `armv8r-none-eabihf` is a Tier 2 target and so any stable +release from 1.92 or newer should work for that target. However, +`thumbv8r-none-eabihf` is still a Tier 3 target, which means Nightly Rust is +required. This folder contains a [`rust-toolchain.toml`] which pins us to a +specific release of nightly that is known to work. + +We have only tested this crate on `qemu-system-arm` emulating the Arm +MPS3-AN536, not the real thing. + +[`.cargo/config.toml`]: ../../.cargo/config.toml +[`rust-toolchain.toml`]: ./rust-toolchain.toml + +## Running + +Run these examples as follows: + +```console +$ cargo run --bin hello + Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.03s + Running `qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -audio none -kernel target/armv8r-none-eabihf/debug/hello` +Hello, this is semihosting! x = 1.000, y = 2.000 +PANIC: PanicInfo { + message: I am an example panic, + location: Location { + file: "src/bin/hello.rs", + line: 20, + column: 5, + }, + can_unwind: true, + force_no_backtrace: false, +} +$ cargo run --bin hello --target thumbv8r-none-eabihf -Zbuild-std=core + Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.92s + Running `qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -audio none -kernel target/thumbv8r-none-eabihf/debug/hello` +Hello, this is semihosting! x = 1.000, y = 2.000 +PANIC: PanicInfo { + message: I am an example panic, + location: Location { + file: "src/bin/hello.rs", + line: 20, + column: 5, + }, + can_unwind: true, + force_no_backtrace: false, +} +``` + +## Debugging + +You can start a GDB server by adding `-- -s -S` to the end of the `cargo run` +command, and the connect with GDB as follows: + +```console +$ cargo run --bin hello -- -s -S +# QEMU runs and hangs waiting for a connection. In another terminal run: +$ arm-none-eabi-gdb -x commands.gdb target/armv8r-none-eabihf/debug/hello +# GDB will start and connect to QEMU's GDB server. The commands.gdb file sets up some useful defaults. +``` + +## Minimum Supported Rust Version (MSRV) + +These examples are guaranteed to compile on the version of Rust given in the +[`rust-toolchain.toml`] file. These examples are not version controlled and we +may change the MSRV at any time. + +## Licence + +- Copyright (c) Ferrous Systems +- Copyright (c) The Rust Embedded Devices Working Group developers + +Licensed under either [MIT](../LICENSE-MIT) or [Apache-2.0](../LICENSE-APACHE) at +your option. + +## Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you shall be licensed as above, without any +additional terms or conditions. diff --git a/examples/mps3-an536-el2/build.rs b/examples/mps3-an536-el2/build.rs new file mode 100644 index 00000000..b0b9f098 --- /dev/null +++ b/examples/mps3-an536-el2/build.rs @@ -0,0 +1,26 @@ +//! # Build script for the MPS3-AN536 Examples +//! +//! This script only executes when using `cargo` to build the project. +//! +//! Copyright (c) Ferrous Systems, 2025 + +use std::io::Write; + +fn main() { + arm_targets::process(); + write("memory.x", include_bytes!("memory.x")); + // Use the cortex-m-rt linker script + println!("cargo:rustc-link-arg=-Tlink.x"); +} + +fn write(file: &str, contents: &[u8]) { + // Put linker file in our output directory and ensure it's on the + // linker search path. + let out = &std::path::PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + std::fs::File::create(out.join(file)) + .unwrap() + .write_all(contents) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed={}", file); +} diff --git a/examples/mps3-an536-el2/commands.gdb b/examples/mps3-an536-el2/commands.gdb new file mode 100644 index 00000000..0634363b --- /dev/null +++ b/examples/mps3-an536-el2/commands.gdb @@ -0,0 +1,13 @@ +target extended-remote :1234 +break kmain +break _asm_undefined_handler +break _asm_svc_handler +break _asm_prefetch_abort_handler +break _asm_data_abort_handler +break _asm_irq_handler +break _asm_fiq_handler +layout asm +layout regs +set logging file ./target/debug.log +set logging enabled on +stepi diff --git a/examples/mps3-an536-el2/memory.x b/examples/mps3-an536-el2/memory.x new file mode 100644 index 00000000..00158077 --- /dev/null +++ b/examples/mps3-an536-el2/memory.x @@ -0,0 +1,44 @@ +/* +Memory configuration for the MPS3-AN536 machine. + +See https://github.com/qemu/qemu/blob/master/hw/arm/mps3r.c +*/ + +MEMORY { + QSPI : ORIGIN = 0x08000000, LENGTH = 8M + BRAM : ORIGIN = 0x10000000, LENGTH = 512K + DDR : ORIGIN = 0x20000000, LENGTH = 1536M +} + +REGION_ALIAS("VECTORS", QSPI); +REGION_ALIAS("CODE", QSPI); +REGION_ALIAS("DATA", BRAM); +REGION_ALIAS("STACKS", BRAM); + +SECTIONS { + /* ### Interrupt Handler Entries + * + * The IRQ handler walks this section to find registered + * interrupt handlers + */ + .irq_entries : ALIGN(4) + { + /* We put this in the header */ + __irq_entries_start = .; + /* Here are the entries */ + KEEP(*(.irq_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __irq_entries_end = .; + } > CODE +} INSERT AFTER .text; + + +PROVIDE(_hyp_stack_size = 16K); +PROVIDE(_und_stack_size = 16K); +PROVIDE(_svc_stack_size = 16K); +PROVIDE(_abt_stack_size = 16K); +PROVIDE(_irq_stack_size = 64); +PROVIDE(_fiq_stack_size = 64); +PROVIDE(_sys_stack_size = 16K); diff --git a/examples/mps3-an536-el2/reference/abt-exception-a32-armv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/abt-exception-a32-armv8r-none-eabihf.out new file mode 100644 index 00000000..1d3d93c4 --- /dev/null +++ b/examples/mps3-an536-el2/reference/abt-exception-a32-armv8r-none-eabihf.out @@ -0,0 +1,8 @@ +Hello, this is an data abort exception example +data abort occurred Hsr { ec: Ok(DataAbortFromCurrent), il: ThirtyTwoBit, iss: 33 } Some(DataAbortFromCurrent(IssDataAbort { isv: false, sas: 0, sae: false, srt: 0, ar: false, fnv: false, ea: false, cm: false, wnr: false, dfsc: 21 })) +caught unaligned_from_a32 +Doing it again +data abort occurred Hsr { ec: Ok(DataAbortFromCurrent), il: ThirtyTwoBit, iss: 33 } Some(DataAbortFromCurrent(IssDataAbort { isv: false, sas: 0, sae: false, srt: 0, ar: false, fnv: false, ea: false, cm: false, wnr: false, dfsc: 21 })) +caught unaligned_from_a32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/mps3-an536-el2/reference/abt-exception-a32-thumbv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/abt-exception-a32-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..1d3d93c4 --- /dev/null +++ b/examples/mps3-an536-el2/reference/abt-exception-a32-thumbv8r-none-eabihf.out @@ -0,0 +1,8 @@ +Hello, this is an data abort exception example +data abort occurred Hsr { ec: Ok(DataAbortFromCurrent), il: ThirtyTwoBit, iss: 33 } Some(DataAbortFromCurrent(IssDataAbort { isv: false, sas: 0, sae: false, srt: 0, ar: false, fnv: false, ea: false, cm: false, wnr: false, dfsc: 21 })) +caught unaligned_from_a32 +Doing it again +data abort occurred Hsr { ec: Ok(DataAbortFromCurrent), il: ThirtyTwoBit, iss: 33 } Some(DataAbortFromCurrent(IssDataAbort { isv: false, sas: 0, sae: false, srt: 0, ar: false, fnv: false, ea: false, cm: false, wnr: false, dfsc: 21 })) +caught unaligned_from_a32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/mps3-an536-el2/reference/abt-exception-t32-armv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/abt-exception-t32-armv8r-none-eabihf.out new file mode 100644 index 00000000..f53b10f6 --- /dev/null +++ b/examples/mps3-an536-el2/reference/abt-exception-t32-armv8r-none-eabihf.out @@ -0,0 +1,8 @@ +Hello, this is an data abort exception example +data abort occurred Hsr { ec: Ok(DataAbortFromCurrent), il: ThirtyTwoBit, iss: 33 } Some(DataAbortFromCurrent(IssDataAbort { isv: false, sas: 0, sae: false, srt: 0, ar: false, fnv: false, ea: false, cm: false, wnr: false, dfsc: 21 })) +caught unaligned_from_t32 +Doing it again +data abort occurred Hsr { ec: Ok(DataAbortFromCurrent), il: ThirtyTwoBit, iss: 33 } Some(DataAbortFromCurrent(IssDataAbort { isv: false, sas: 0, sae: false, srt: 0, ar: false, fnv: false, ea: false, cm: false, wnr: false, dfsc: 21 })) +caught unaligned_from_t32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/mps3-an536-el2/reference/abt-exception-t32-thumbv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/abt-exception-t32-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..f53b10f6 --- /dev/null +++ b/examples/mps3-an536-el2/reference/abt-exception-t32-thumbv8r-none-eabihf.out @@ -0,0 +1,8 @@ +Hello, this is an data abort exception example +data abort occurred Hsr { ec: Ok(DataAbortFromCurrent), il: ThirtyTwoBit, iss: 33 } Some(DataAbortFromCurrent(IssDataAbort { isv: false, sas: 0, sae: false, srt: 0, ar: false, fnv: false, ea: false, cm: false, wnr: false, dfsc: 21 })) +caught unaligned_from_t32 +Doing it again +data abort occurred Hsr { ec: Ok(DataAbortFromCurrent), il: ThirtyTwoBit, iss: 33 } Some(DataAbortFromCurrent(IssDataAbort { isv: false, sas: 0, sae: false, srt: 0, ar: false, fnv: false, ea: false, cm: false, wnr: false, dfsc: 21 })) +caught unaligned_from_t32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/mps3-an536-el2/reference/generic-timer-armv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/generic-timer-armv8r-none-eabihf.out new file mode 100644 index 00000000..9f58641c --- /dev/null +++ b/examples/mps3-an536-el2/reference/generic-timer-armv8r-none-eabihf.out @@ -0,0 +1,40 @@ +Found PERIPHBASE 0xf0000000 +Creating GIC driver @ 0xf0000000 / 0xf0100000 +Calling git.setup(0) +Hcr { tcpac: false, trvm: false, hcd: false, tge: false, tvm: false, tpu: false, tpc: false, tsw: false, tac: false, tidcp: false, tid3: false, tid2: false, tid1: false, tid0: false, twe: false, twi: false, dc: false, bsu: NoEffect, fb: false, va: false, vi: false, vf: false, amo: false, imo: false, fmo: false, swio: false, vm: false } +Configure Timer Interrupt... +Timer Hz = 62500000 +Enabling interrupts... +SCTLR { IE=0 TE=0 NMFI=0 EE=0 U=1 FI=0 DZ=1 BR=0 RR=0 V=0 I=0 Z=1 SW=0 C=0 A=0 M=0 } +SCTLR { IE=0 TE=0 NMFI=0 EE=0 U=1 FI=0 DZ=1 BR=0 RR=0 V=0 I=0 Z=1 SW=0 C=0 A=0 M=0 } +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +EL2 timer test completed OK diff --git a/examples/mps3-an536-el2/reference/generic-timer-thumbv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/generic-timer-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..9f58641c --- /dev/null +++ b/examples/mps3-an536-el2/reference/generic-timer-thumbv8r-none-eabihf.out @@ -0,0 +1,40 @@ +Found PERIPHBASE 0xf0000000 +Creating GIC driver @ 0xf0000000 / 0xf0100000 +Calling git.setup(0) +Hcr { tcpac: false, trvm: false, hcd: false, tge: false, tvm: false, tpu: false, tpc: false, tsw: false, tac: false, tidcp: false, tid3: false, tid2: false, tid1: false, tid0: false, twe: false, twi: false, dc: false, bsu: NoEffect, fb: false, va: false, vi: false, vf: false, amo: false, imo: false, fmo: false, swio: false, vm: false } +Configure Timer Interrupt... +Timer Hz = 62500000 +Enabling interrupts... +SCTLR { IE=0 TE=0 NMFI=0 EE=0 U=1 FI=0 DZ=1 BR=0 RR=0 V=0 I=0 Z=1 SW=0 C=0 A=0 M=0 } +SCTLR { IE=0 TE=0 NMFI=0 EE=0 U=1 FI=0 DZ=1 BR=0 RR=0 V=0 I=0 Z=1 SW=0 C=0 A=0 M=0 } +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +> IRQ +Hyp timer tick! +< IRQ +EL2 timer test completed OK diff --git a/examples/mps3-an536-el2/reference/hello-armv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/hello-armv8r-none-eabihf.out new file mode 100644 index 00000000..3238d13c --- /dev/null +++ b/examples/mps3-an536-el2/reference/hello-armv8r-none-eabihf.out @@ -0,0 +1,19 @@ +Hello, this is semihosting! x = 1.000, y = 2.000 +SCTLR { IE=0 TE=0 NMFI=0 EE=0 U=1 FI=0 DZ=1 BR=0 RR=0 V=0 I=0 Z=1 SW=0 C=0 A=0 M=0 } +CPSR { N=0 Z=1 C=1 V=0 Q=0 J=0 E=0 A=1 I=1 F=1 T=0 MODE=Ok(Hyp) } +Region 0: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 1: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 2: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 3: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 4: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 5: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 6: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 7: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 8: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 9: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 10: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 11: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 12: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 13: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 14: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 15: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } diff --git a/examples/mps3-an536-el2/reference/hello-thumbv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/hello-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..3238d13c --- /dev/null +++ b/examples/mps3-an536-el2/reference/hello-thumbv8r-none-eabihf.out @@ -0,0 +1,19 @@ +Hello, this is semihosting! x = 1.000, y = 2.000 +SCTLR { IE=0 TE=0 NMFI=0 EE=0 U=1 FI=0 DZ=1 BR=0 RR=0 V=0 I=0 Z=1 SW=0 C=0 A=0 M=0 } +CPSR { N=0 Z=1 C=1 V=0 Q=0 J=0 E=0 A=1 I=1 F=1 T=0 MODE=Ok(Hyp) } +Region 0: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 1: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 2: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 3: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 4: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 5: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 6: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 7: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 8: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 9: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 10: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 11: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 12: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 13: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 14: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } +Region 15: El2Region { range: 0x0..=0x3f, shareability: NonShareable, access: ReadWriteNoEL10, no_exec: false, mair: 0, enable: false } diff --git a/examples/mps3-an536-el2/reference/hvc-a32-armv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/hvc-a32-armv8r-none-eabihf.out new file mode 100644 index 00000000..0800ffcf --- /dev/null +++ b/examples/mps3-an536-el2/reference/hvc-a32-armv8r-none-eabihf.out @@ -0,0 +1,5 @@ +x = 1, y = 2, z = 3.000 +In hvc_handler, with Hsr { ec: Ok(Hvc), il: ThirtyTwoBit, iss: 0000abcd }, Some(Hvc(IssCall { imm16: abcd })), Frame { r0: 10000000, r1: 10000001, r2: 10000002, r3: 10000003, r4: 10000004, r5: 10000005 } +In hvc_handler, with Hsr { ec: Ok(Hvc), il: ThirtyTwoBit, iss: 00009876 }, Some(Hvc(IssCall { imm16: 9876 })), Frame { r0: 20000000, r1: 20000001, r2: 20000002, r3: 20000003, r4: 20000004, r5: 20000005 } +Got 12345678 +x = 1, y = 2, z = 3.000 diff --git a/examples/mps3-an536-el2/reference/hvc-a32-thumbv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/hvc-a32-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..0800ffcf --- /dev/null +++ b/examples/mps3-an536-el2/reference/hvc-a32-thumbv8r-none-eabihf.out @@ -0,0 +1,5 @@ +x = 1, y = 2, z = 3.000 +In hvc_handler, with Hsr { ec: Ok(Hvc), il: ThirtyTwoBit, iss: 0000abcd }, Some(Hvc(IssCall { imm16: abcd })), Frame { r0: 10000000, r1: 10000001, r2: 10000002, r3: 10000003, r4: 10000004, r5: 10000005 } +In hvc_handler, with Hsr { ec: Ok(Hvc), il: ThirtyTwoBit, iss: 00009876 }, Some(Hvc(IssCall { imm16: 9876 })), Frame { r0: 20000000, r1: 20000001, r2: 20000002, r3: 20000003, r4: 20000004, r5: 20000005 } +Got 12345678 +x = 1, y = 2, z = 3.000 diff --git a/examples/mps3-an536-el2/reference/hvc-t32-armv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/hvc-t32-armv8r-none-eabihf.out new file mode 100644 index 00000000..0800ffcf --- /dev/null +++ b/examples/mps3-an536-el2/reference/hvc-t32-armv8r-none-eabihf.out @@ -0,0 +1,5 @@ +x = 1, y = 2, z = 3.000 +In hvc_handler, with Hsr { ec: Ok(Hvc), il: ThirtyTwoBit, iss: 0000abcd }, Some(Hvc(IssCall { imm16: abcd })), Frame { r0: 10000000, r1: 10000001, r2: 10000002, r3: 10000003, r4: 10000004, r5: 10000005 } +In hvc_handler, with Hsr { ec: Ok(Hvc), il: ThirtyTwoBit, iss: 00009876 }, Some(Hvc(IssCall { imm16: 9876 })), Frame { r0: 20000000, r1: 20000001, r2: 20000002, r3: 20000003, r4: 20000004, r5: 20000005 } +Got 12345678 +x = 1, y = 2, z = 3.000 diff --git a/examples/mps3-an536-el2/reference/hvc-t32-thumbv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/hvc-t32-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..0800ffcf --- /dev/null +++ b/examples/mps3-an536-el2/reference/hvc-t32-thumbv8r-none-eabihf.out @@ -0,0 +1,5 @@ +x = 1, y = 2, z = 3.000 +In hvc_handler, with Hsr { ec: Ok(Hvc), il: ThirtyTwoBit, iss: 0000abcd }, Some(Hvc(IssCall { imm16: abcd })), Frame { r0: 10000000, r1: 10000001, r2: 10000002, r3: 10000003, r4: 10000004, r5: 10000005 } +In hvc_handler, with Hsr { ec: Ok(Hvc), il: ThirtyTwoBit, iss: 00009876 }, Some(Hvc(IssCall { imm16: 9876 })), Frame { r0: 20000000, r1: 20000001, r2: 20000002, r3: 20000003, r4: 20000004, r5: 20000005 } +Got 12345678 +x = 1, y = 2, z = 3.000 diff --git a/examples/mps3-an536-el2/reference/prefetch-exception-a32-armv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/prefetch-exception-a32-armv8r-none-eabihf.out new file mode 100644 index 00000000..50cd4e78 --- /dev/null +++ b/examples/mps3-an536-el2/reference/prefetch-exception-a32-armv8r-none-eabihf.out @@ -0,0 +1,8 @@ +Hello, this is a prefetch abort exception example +prefetch abort occurred Hsr { ec: Ok(PrefetchAbortFromCurrent), il: ThirtyTwoBit, iss: 00000022 }, Some(PrefetchAbortFromCurrent(IssPrefetchAbort { fnv: false, ea: false, ifsc: 22 })) +caught bkpt_from_a32 +Doing it again +prefetch abort occurred Hsr { ec: Ok(PrefetchAbortFromCurrent), il: ThirtyTwoBit, iss: 00000022 }, Some(PrefetchAbortFromCurrent(IssPrefetchAbort { fnv: false, ea: false, ifsc: 22 })) +caught bkpt_from_a32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/mps3-an536-el2/reference/prefetch-exception-a32-thumbv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/prefetch-exception-a32-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..50cd4e78 --- /dev/null +++ b/examples/mps3-an536-el2/reference/prefetch-exception-a32-thumbv8r-none-eabihf.out @@ -0,0 +1,8 @@ +Hello, this is a prefetch abort exception example +prefetch abort occurred Hsr { ec: Ok(PrefetchAbortFromCurrent), il: ThirtyTwoBit, iss: 00000022 }, Some(PrefetchAbortFromCurrent(IssPrefetchAbort { fnv: false, ea: false, ifsc: 22 })) +caught bkpt_from_a32 +Doing it again +prefetch abort occurred Hsr { ec: Ok(PrefetchAbortFromCurrent), il: ThirtyTwoBit, iss: 00000022 }, Some(PrefetchAbortFromCurrent(IssPrefetchAbort { fnv: false, ea: false, ifsc: 22 })) +caught bkpt_from_a32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/mps3-an536-el2/reference/prefetch-exception-t32-armv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/prefetch-exception-t32-armv8r-none-eabihf.out new file mode 100644 index 00000000..fa18811d --- /dev/null +++ b/examples/mps3-an536-el2/reference/prefetch-exception-t32-armv8r-none-eabihf.out @@ -0,0 +1,8 @@ +Hello, this is a prefetch abort exception example +prefetch abort occurred Hsr { ec: Ok(PrefetchAbortFromCurrent), il: ThirtyTwoBit, iss: 00000022 }, Some(PrefetchAbortFromCurrent(IssPrefetchAbort { fnv: false, ea: false, ifsc: 22 })) +caught bkpt_from_t32 +Doing it again +prefetch abort occurred Hsr { ec: Ok(PrefetchAbortFromCurrent), il: ThirtyTwoBit, iss: 00000022 }, Some(PrefetchAbortFromCurrent(IssPrefetchAbort { fnv: false, ea: false, ifsc: 22 })) +caught bkpt_from_t32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/mps3-an536-el2/reference/prefetch-exception-t32-thumbv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/prefetch-exception-t32-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..fa18811d --- /dev/null +++ b/examples/mps3-an536-el2/reference/prefetch-exception-t32-thumbv8r-none-eabihf.out @@ -0,0 +1,8 @@ +Hello, this is a prefetch abort exception example +prefetch abort occurred Hsr { ec: Ok(PrefetchAbortFromCurrent), il: ThirtyTwoBit, iss: 00000022 }, Some(PrefetchAbortFromCurrent(IssPrefetchAbort { fnv: false, ea: false, ifsc: 22 })) +caught bkpt_from_t32 +Doing it again +prefetch abort occurred Hsr { ec: Ok(PrefetchAbortFromCurrent), il: ThirtyTwoBit, iss: 00000022 }, Some(PrefetchAbortFromCurrent(IssPrefetchAbort { fnv: false, ea: false, ifsc: 22 })) +caught bkpt_from_t32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/mps3-an536-el2/reference/svc-a32-armv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/svc-a32-armv8r-none-eabihf.out new file mode 100644 index 00000000..f5d7e128 --- /dev/null +++ b/examples/mps3-an536-el2/reference/svc-a32-armv8r-none-eabihf.out @@ -0,0 +1,5 @@ +x = 1, y = 2, z = 3.000 +In hvc_handler, with Hsr { ec: Ok(Svc), il: ThirtyTwoBit, iss: 0000abcd }, Some(Svc(IssCall { imm16: abcd })), Frame { r0: 10000000, r1: 10000001, r2: 10000002, r3: 10000003, r4: 10000004, r5: 10000005 } +In hvc_handler, with Hsr { ec: Ok(Svc), il: ThirtyTwoBit, iss: 00009876 }, Some(Svc(IssCall { imm16: 9876 })), Frame { r0: 20000000, r1: 20000001, r2: 20000002, r3: 20000003, r4: 20000004, r5: 20000005 } +Got 12345678 +x = 1, y = 2, z = 3.000 diff --git a/examples/mps3-an536-el2/reference/svc-a32-thumbv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/svc-a32-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..f5d7e128 --- /dev/null +++ b/examples/mps3-an536-el2/reference/svc-a32-thumbv8r-none-eabihf.out @@ -0,0 +1,5 @@ +x = 1, y = 2, z = 3.000 +In hvc_handler, with Hsr { ec: Ok(Svc), il: ThirtyTwoBit, iss: 0000abcd }, Some(Svc(IssCall { imm16: abcd })), Frame { r0: 10000000, r1: 10000001, r2: 10000002, r3: 10000003, r4: 10000004, r5: 10000005 } +In hvc_handler, with Hsr { ec: Ok(Svc), il: ThirtyTwoBit, iss: 00009876 }, Some(Svc(IssCall { imm16: 9876 })), Frame { r0: 20000000, r1: 20000001, r2: 20000002, r3: 20000003, r4: 20000004, r5: 20000005 } +Got 12345678 +x = 1, y = 2, z = 3.000 diff --git a/examples/mps3-an536-el2/reference/svc-t32-armv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/svc-t32-armv8r-none-eabihf.out new file mode 100644 index 00000000..92fda701 --- /dev/null +++ b/examples/mps3-an536-el2/reference/svc-t32-armv8r-none-eabihf.out @@ -0,0 +1,5 @@ +x = 1, y = 2, z = 3.000 +In hvc_handler, with Hsr { ec: Ok(Svc), il: SixteenBit, iss: 00000012 }, Some(Svc(IssCall { imm16: 12 })), Frame { r0: 10000000, r1: 10000001, r2: 10000002, r3: 10000003, r4: 10000004, r5: 10000005 } +In hvc_handler, with Hsr { ec: Ok(Svc), il: SixteenBit, iss: 00000032 }, Some(Svc(IssCall { imm16: 32 })), Frame { r0: 20000000, r1: 20000001, r2: 20000002, r3: 20000003, r4: 20000004, r5: 20000005 } +Got 12345678 +x = 1, y = 2, z = 3.000 diff --git a/examples/mps3-an536-el2/reference/svc-t32-thumbv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/svc-t32-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..92fda701 --- /dev/null +++ b/examples/mps3-an536-el2/reference/svc-t32-thumbv8r-none-eabihf.out @@ -0,0 +1,5 @@ +x = 1, y = 2, z = 3.000 +In hvc_handler, with Hsr { ec: Ok(Svc), il: SixteenBit, iss: 00000012 }, Some(Svc(IssCall { imm16: 12 })), Frame { r0: 10000000, r1: 10000001, r2: 10000002, r3: 10000003, r4: 10000004, r5: 10000005 } +In hvc_handler, with Hsr { ec: Ok(Svc), il: SixteenBit, iss: 00000032 }, Some(Svc(IssCall { imm16: 32 })), Frame { r0: 20000000, r1: 20000001, r2: 20000002, r3: 20000003, r4: 20000004, r5: 20000005 } +Got 12345678 +x = 1, y = 2, z = 3.000 diff --git a/examples/mps3-an536-el2/reference/undef-exception-a32-armv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/undef-exception-a32-armv8r-none-eabihf.out new file mode 100644 index 00000000..7258e7b2 --- /dev/null +++ b/examples/mps3-an536-el2/reference/undef-exception-a32-armv8r-none-eabihf.out @@ -0,0 +1,8 @@ +Hello, this is a undef exception example +undefined abort occurred Hsr { ec: Ok(Unknown), il: ThirtyTwoBit, iss: 00000000 }, Some(Unknown(IssUnknown(0))) +caught udf_from_a32 +Doing it again +undefined abort occurred Hsr { ec: Ok(Unknown), il: ThirtyTwoBit, iss: 00000000 }, Some(Unknown(IssUnknown(0))) +caught udf_from_a32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/mps3-an536-el2/reference/undef-exception-a32-thumbv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/undef-exception-a32-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..7258e7b2 --- /dev/null +++ b/examples/mps3-an536-el2/reference/undef-exception-a32-thumbv8r-none-eabihf.out @@ -0,0 +1,8 @@ +Hello, this is a undef exception example +undefined abort occurred Hsr { ec: Ok(Unknown), il: ThirtyTwoBit, iss: 00000000 }, Some(Unknown(IssUnknown(0))) +caught udf_from_a32 +Doing it again +undefined abort occurred Hsr { ec: Ok(Unknown), il: ThirtyTwoBit, iss: 00000000 }, Some(Unknown(IssUnknown(0))) +caught udf_from_a32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/mps3-an536-el2/reference/undef-exception-t32-armv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/undef-exception-t32-armv8r-none-eabihf.out new file mode 100644 index 00000000..c0f85065 --- /dev/null +++ b/examples/mps3-an536-el2/reference/undef-exception-t32-armv8r-none-eabihf.out @@ -0,0 +1,8 @@ +Hello, this is a undef exception example +undefined abort occurred Hsr { ec: Ok(Unknown), il: ThirtyTwoBit, iss: 00000000 }, Some(Unknown(IssUnknown(0))) +caught udf_from_t32 +Doing it again +undefined abort occurred Hsr { ec: Ok(Unknown), il: ThirtyTwoBit, iss: 00000000 }, Some(Unknown(IssUnknown(0))) +caught udf_from_t32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/mps3-an536-el2/reference/undef-exception-t32-thumbv8r-none-eabihf.out b/examples/mps3-an536-el2/reference/undef-exception-t32-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..c0f85065 --- /dev/null +++ b/examples/mps3-an536-el2/reference/undef-exception-t32-thumbv8r-none-eabihf.out @@ -0,0 +1,8 @@ +Hello, this is a undef exception example +undefined abort occurred Hsr { ec: Ok(Unknown), il: ThirtyTwoBit, iss: 00000000 }, Some(Unknown(IssUnknown(0))) +caught udf_from_t32 +Doing it again +undefined abort occurred Hsr { ec: Ok(Unknown), il: ThirtyTwoBit, iss: 00000000 }, Some(Unknown(IssUnknown(0))) +caught udf_from_t32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/mps3-an536-el2/rust-toolchain.toml b/examples/mps3-an536-el2/rust-toolchain.toml new file mode 100644 index 00000000..7e4d9e6c --- /dev/null +++ b/examples/mps3-an536-el2/rust-toolchain.toml @@ -0,0 +1,6 @@ +[toolchain] +channel = "nightly-2026-01-26" +targets = [ + "armv8r-none-eabihf", +] +components = ["rust-src", "clippy", "rustfmt"] diff --git a/examples/mps3-an536-el2/src/bin/abt-exception-a32.rs b/examples/mps3-an536-el2/src/bin/abt-exception-a32.rs new file mode 100644 index 00000000..9bf95d4c --- /dev/null +++ b/examples/mps3-an536-el2/src/bin/abt-exception-a32.rs @@ -0,0 +1,120 @@ +//! Example triggering an data abort exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; + +use aarch32_cpu::register::Hsctlr; +use aarch32_rt::{entry, exception}; +use semihosting::println; + +#[unsafe(no_mangle)] +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[entry] +fn main() -> ! { + // Enable alignment check for Armv7-R. Was not required + // on Cortex-A for some reason, even though the bit was not set. + enable_alignment_check(); + + println!("Hello, this is an data abort exception example"); + unsafe { + // Unaligned read + unaligned_from_a32(); + } + + // turn it off before we do the stack dump on exit, because println! has been + // observed to do unaligned reads. + disable_alignment_check(); + + println!("Recovered from fault OK!"); + + mps3_an536_el2::exit(0); +} + +// These functions are written in assembly +unsafe extern "C" { + fn unaligned_from_a32(); +} + +core::arch::global_asm!( + r#" + // fn unaligned_from_a32(); + .arm + .global unaligned_from_a32 + .type unaligned_from_a32, %function + unaligned_from_a32: + ldr r0, =COUNTER + add r0, r0, 1 + ldr r0, [r0] + bx lr + .size unaligned_from_a32, . - unaligned_from_a32 +"# +); + +fn enable_alignment_check() { + let mut hsctrl = Hsctlr::read(); + hsctrl.set_a(true); + Hsctlr::write(hsctrl); +} + +fn disable_alignment_check() { + let mut hsctrl = Hsctlr::read(); + hsctrl.set_a(false); + Hsctlr::write(hsctrl); +} + +#[exception(Undefined)] +fn undefined_handler(_addr: usize) -> ! { + panic!("unexpected undefined exception"); +} + +#[exception(PrefetchAbort)] +fn prefetch_abort_handler(_addr: usize) -> ! { + panic!("unexpected prefetch abort"); +} + +#[exception(DataAbort)] +unsafe fn data_abort_handler(addr: usize) -> usize { + let hsr = aarch32_cpu::register::Hsr::read(); + disable_alignment_check(); + println!("data abort occurred {:?} {:x?}", hsr, hsr.get_iss()); + enable_alignment_check(); + + // note the fault isn't at the start of the function + let expect_fault_at = unaligned_from_a32 as unsafe extern "C" fn() as usize + 8; + + if addr == expect_fault_at { + println!("caught unaligned_from_a32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, expect_fault_at + ); + semihosting::process::abort(); + } + + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 4 + } + _ => { + // we've faulted thrice - time to quit + println!("We triple faulted"); + semihosting::process::abort(); + } + } +} diff --git a/examples/mps3-an536-el2/src/bin/abt-exception-t32.rs b/examples/mps3-an536-el2/src/bin/abt-exception-t32.rs new file mode 100644 index 00000000..9ac310f1 --- /dev/null +++ b/examples/mps3-an536-el2/src/bin/abt-exception-t32.rs @@ -0,0 +1,120 @@ +//! Example triggering an data abort exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; + +use aarch32_cpu::register::Hsctlr; +use aarch32_rt::{entry, exception}; +use semihosting::println; + +#[unsafe(no_mangle)] +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[entry] +fn main() -> ! { + // Enable alignment check for Armv7-R. Was not required + // on Cortex-A for some reason, even though the bit was not set. + enable_alignment_check(); + + println!("Hello, this is an data abort exception example"); + unsafe { + // Unaligned read + unaligned_from_t32(); + } + + // turn it off before we do the stack dump on exit, because println! has been + // observed to do unaligned reads. + disable_alignment_check(); + + println!("Recovered from fault OK!"); + + mps3_an536_el2::exit(0); +} + +// These functions are written in assembly +unsafe extern "C" { + fn unaligned_from_t32(); +} + +core::arch::global_asm!( + r#" + // fn unaligned_from_t32(); + .thumb + .global unaligned_from_t32 + .type unaligned_from_t32, %function + unaligned_from_t32: + ldr r0, =COUNTER + add r0, r0, 1 + ldr r0, [r0] + bx lr + .size unaligned_from_t32, . - unaligned_from_t32 +"# +); + +fn enable_alignment_check() { + let mut hsctrl = Hsctlr::read(); + hsctrl.set_a(true); + Hsctlr::write(hsctrl); +} + +fn disable_alignment_check() { + let mut hsctrl = Hsctlr::read(); + hsctrl.set_a(false); + Hsctlr::write(hsctrl); +} + +#[exception(Undefined)] +fn undefined_handler(_addr: usize) -> ! { + panic!("unexpected undefined exception"); +} + +#[exception(PrefetchAbort)] +fn prefetch_abort_handler(_addr: usize) -> ! { + panic!("unexpected prefetch abort"); +} + +#[exception(DataAbort)] +unsafe fn data_abort_handler(addr: usize) -> usize { + let hsr = aarch32_cpu::register::Hsr::read(); + disable_alignment_check(); + println!("data abort occurred {:?} {:x?}", hsr, hsr.get_iss()); + enable_alignment_check(); + + // note the fault isn't at the start of the function + let expect_fault_at = unaligned_from_t32 as unsafe extern "C" fn() as usize + 5; + + if addr == expect_fault_at { + println!("caught unaligned_from_t32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, expect_fault_at + ); + semihosting::process::abort(); + } + + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 2 + } + _ => { + // we've faulted thrice - time to quit + println!("We triple faulted"); + semihosting::process::abort(); + } + } +} diff --git a/examples/mps3-an536-el2/src/bin/generic-timer.rs b/examples/mps3-an536-el2/src/bin/generic-timer.rs new file mode 100644 index 00000000..102a3220 --- /dev/null +++ b/examples/mps3-an536-el2/src/bin/generic-timer.rs @@ -0,0 +1,115 @@ +//! Hyp Timer Test Arm Cortex-R52 running in EL2 (Hyp Mode) + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering::Relaxed}; + +use aarch32_cpu::generic_timer::GenericTimer; +use aarch32_rt::{entry, exception, irq}; +use arm_gic::gicv3::{GicCpuInterface, Group, InterruptGroup}; +use semihosting::println; + +use mps3_an536_el2::HYP_TIMER_PPI; + +static TICK_COUNT: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up code at the bottom of this file. +#[entry] +fn main() -> ! { + let mut board = mps3_an536_el2::Board::new().unwrap(); + + println!("{:?}", aarch32_cpu::register::Hcr::read()); + + println!("Configure Timer Interrupt..."); + board + .gic + .set_interrupt_priority(HYP_TIMER_PPI, Some(0), 0x31) + .unwrap(); + board + .gic + .set_group(HYP_TIMER_PPI, Some(0), Group::Group1NS) + .unwrap(); + board + .gic + .enable_interrupt(HYP_TIMER_PPI, Some(0), true) + .unwrap(); + + let mut hyp_timer = board.hyp_timer; + + println!("Timer Hz = {}", hyp_timer.frequency_hz()); + hyp_timer.interrupt_mask(false); + hyp_timer.countdown_set(hyp_timer.frequency_hz() / 5); + hyp_timer.enable(true); + // used in interrupt handler + drop(hyp_timer); + + println!("Enabling interrupts..."); + dump_sctlr(); + unsafe { + aarch32_cpu::interrupt::enable(); + } + dump_sctlr(); + + loop { + aarch32_cpu::asm::wfi(); + let tick_count = TICK_COUNT.load(Relaxed); + // println!("Main loop wake up {}", tick_count); + if tick_count >= 10 { + break; + } + } + + println!("EL2 timer test completed OK"); + + mps3_an536_el2::exit(0); +} + +fn dump_sctlr() { + let sctlr = aarch32_cpu::register::Sctlr::read(); + println!("{:?}", sctlr); +} + +#[irq] +fn irq_handler() { + println!("> IRQ"); + while let Some(int_id) = GicCpuInterface::get_and_acknowledge_interrupt(InterruptGroup::Group1) + { + match int_id { + HYP_TIMER_PPI => { + println!("Hyp timer tick!"); + handle_timer_irq(); + } + _ => { + println!("Interrupt {:?}?", int_id); + } + } + GicCpuInterface::end_interrupt(int_id, InterruptGroup::Group1); + } + println!("< IRQ"); +} + +/// Run when the timer IRQ fires +fn handle_timer_irq() { + // SAFETY: We drop en other time handle in main, this is the only active handle. + let mut el2_timer = unsafe { aarch32_cpu::generic_timer::El2HypPhysicalTimer::new() }; + // trigger a timer in 0.2 seconds + el2_timer.countdown_set(el2_timer.frequency_hz() / 5); + // tell the main loop the timer went tick + TICK_COUNT.fetch_add(1, Relaxed); +} + +/// This is our HVC exception handler +#[exception(HypervisorCall)] +fn hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 { + let hsr = aarch32_cpu::register::Hsr::new_with_raw_value(hsr); + println!( + "In hvc_handler, with {:08x?}, {:x?}, {:08x?}", + hsr, + hsr.get_iss(), + frame + ); + return frame.r0; +} diff --git a/examples/mps3-an536-el2/src/bin/hello.rs b/examples/mps3-an536-el2/src/bin/hello.rs new file mode 100644 index 00000000..7f1c01df --- /dev/null +++ b/examples/mps3-an536-el2/src/bin/hello.rs @@ -0,0 +1,28 @@ +//! Semihosting hello-world for Arm Cortex-R52 running in EL2 (Hyp Mode) + +#![no_std] +#![no_main] + +use aarch32_rt::entry; +use semihosting::println; + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up code at the bottom of this file. +#[entry] +fn main() -> ! { + let x = 1.0f64; + let y = x * 2.0; + println!("Hello, this is semihosting! x = {:0.3}, y = {:0.3}", x, y); + println!("{:?}", aarch32_cpu::register::Sctlr::read()); + println!("{:?}", aarch32_cpu::register::Cpsr::read()); + + let mut mpu = unsafe { aarch32_cpu::pmsav8::El2Mpu::new() }; + for idx in 0..mpu.num_regions() { + if let Some(region) = mpu.get_region(idx) { + println!("Region {}: {:?}", idx, region); + } + } + + mps3_an536_el2::exit(0); +} diff --git a/examples/mps3-an536-el2/src/bin/hvc-a32.rs b/examples/mps3-an536-el2/src/bin/hvc-a32.rs new file mode 100644 index 00000000..8d12237b --- /dev/null +++ b/examples/mps3-an536-el2/src/bin/hvc-a32.rs @@ -0,0 +1,64 @@ +//! HVC (hypervisor call) example + +#![no_std] +#![no_main] + +use aarch32_rt::{entry, exception}; +use semihosting::println; + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[entry] +fn main() -> ! { + let x = 1; + let y = x + 1; + let z = (y as f64) * 1.5; + println!("x = {}, y = {}, z = {:0.3}", x, y, z); + let value = do_hvc1(); + println!("Got {:08x}", value); + println!("x = {}, y = {}, z = {:0.3}", x, y, z); + mps3_an536_el2::exit(0); +} + +/// This is our HVC exception handler +#[exception(HypervisorCall)] +fn hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 { + let hsr = aarch32_cpu::register::Hsr::new_with_raw_value(hsr); + println!( + "In hvc_handler, with {:08x?}, {:x?}, {:08x?}", + hsr, + hsr.get_iss(), + frame + ); + if hsr.iss().value() == 0xABCD { + do_hvc2(); + } + return 0x12345678; +} + +#[instruction_set(arm::a32)] +fn do_hvc1() -> u32 { + aarch32_cpu::hvc6!( + 0xABCD, + 0x1000_0000, + 0x1000_0001, + 0x1000_0002, + 0x1000_0003, + 0x1000_0004, + 0x1000_0005 + ) +} + +#[instruction_set(arm::a32)] +fn do_hvc2() -> u32 { + aarch32_cpu::hvc6!( + 0x9876, + 0x2000_0000, + 0x2000_0001, + 0x2000_0002, + 0x2000_0003, + 0x2000_0004, + 0x2000_0005 + ) +} diff --git a/examples/mps3-an536-el2/src/bin/hvc-t32.rs b/examples/mps3-an536-el2/src/bin/hvc-t32.rs new file mode 100644 index 00000000..98ad5808 --- /dev/null +++ b/examples/mps3-an536-el2/src/bin/hvc-t32.rs @@ -0,0 +1,64 @@ +//! HVC (hypervisor call) example + +#![no_std] +#![no_main] + +use aarch32_rt::{entry, exception}; +use semihosting::println; + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[entry] +fn main() -> ! { + let x = 1; + let y = x + 1; + let z = (y as f64) * 1.5; + println!("x = {}, y = {}, z = {:0.3}", x, y, z); + let value = do_hvc1(); + println!("Got {:08x}", value); + println!("x = {}, y = {}, z = {:0.3}", x, y, z); + mps3_an536_el2::exit(0); +} + +/// This is our HVC exception handler +#[exception(HypervisorCall)] +fn hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 { + let hsr = aarch32_cpu::register::Hsr::new_with_raw_value(hsr); + println!( + "In hvc_handler, with {:08x?}, {:x?}, {:08x?}", + hsr, + hsr.get_iss(), + frame + ); + if hsr.iss().value() == 0xABCD { + do_hvc2(); + } + return 0x12345678; +} + +#[instruction_set(arm::t32)] +fn do_hvc1() -> u32 { + aarch32_cpu::hvc6!( + 0xABCD, + 0x1000_0000, + 0x1000_0001, + 0x1000_0002, + 0x1000_0003, + 0x1000_0004, + 0x1000_0005 + ) +} + +#[instruction_set(arm::t32)] +fn do_hvc2() -> u32 { + aarch32_cpu::hvc6!( + 0x9876, + 0x2000_0000, + 0x2000_0001, + 0x2000_0002, + 0x2000_0003, + 0x2000_0004, + 0x2000_0005 + ) +} diff --git a/examples/mps3-an536-el2/src/bin/prefetch-exception-a32.rs b/examples/mps3-an536-el2/src/bin/prefetch-exception-a32.rs new file mode 100644 index 00000000..f660baca --- /dev/null +++ b/examples/mps3-an536-el2/src/bin/prefetch-exception-a32.rs @@ -0,0 +1,93 @@ +//! Example triggering a prefetch abort exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; + +use aarch32_rt::{entry, exception}; +use semihosting::println; + +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[entry] +fn main() -> ! { + println!("Hello, this is a prefetch abort exception example"); + + // A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled. + // See p. 2038 of ARMv7-M Architecture Reference Manual + unsafe { + // trigger an prefetch abort exception, from A32 (Arm) mode + bkpt_from_a32(); + } + + println!("Recovered from fault OK!"); + + mps3_an536_el2::exit(0); +} + +// These functions are written in assembly +unsafe extern "C" { + fn bkpt_from_a32(); +} + +core::arch::global_asm!( + r#" + // fn bkpt_from_a32(); + .arm + .global bkpt_from_a32 + .type bkpt_from_a32, %function + bkpt_from_a32: + bkpt #0 + bx lr + .size bkpt_from_a32, . - bkpt_from_a32 +"# +); + +// Custom link sections are allowed as well. +#[exception(Undefined)] +fn undefined_handler(_addr: usize) -> ! { + panic!("unexpected undefined exception"); +} + +#[exception(PrefetchAbort)] +unsafe fn prefetch_abort_handler(addr: usize) -> usize { + let hsr = aarch32_cpu::register::Hsr::read(); + println!("prefetch abort occurred {:08x?}, {:x?}", hsr, hsr.get_iss()); + + if addr == bkpt_from_a32 as unsafe extern "C" fn() as usize { + println!("caught bkpt_from_a32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, bkpt_from_a32 as unsafe extern "C" fn() as usize + ); + } + + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 4 + } + _ => { + // we've faulted thrice - time to quit + panic!("prefetch_handler called too often"); + } + } +} + +#[exception(DataAbort)] +fn data_abort_handler(_addr: usize) -> ! { + panic!("unexpected data abort exception"); +} diff --git a/examples/mps3-an536-el2/src/bin/prefetch-exception-t32.rs b/examples/mps3-an536-el2/src/bin/prefetch-exception-t32.rs new file mode 100644 index 00000000..83270378 --- /dev/null +++ b/examples/mps3-an536-el2/src/bin/prefetch-exception-t32.rs @@ -0,0 +1,95 @@ +//! Example triggering a prefetch abort exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; + +use aarch32_rt::{entry, exception}; +use semihosting::println; + +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[entry] +fn main() -> ! { + println!("Hello, this is a prefetch abort exception example"); + + // A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled. + // See p. 2038 of ARMv7-M Architecture Reference Manual + unsafe { + // trigger an prefetch abort exception, from T32 (Thumb) mode + bkpt_from_t32(); + } + + println!("Recovered from fault OK!"); + + mps3_an536_el2::exit(0); +} + +// These functions are written in assembly +unsafe extern "C" { + fn bkpt_from_t32(); +} + +core::arch::global_asm!( + r#" + // fn bkpt_from_t32(); + .thumb + .global bkpt_from_t32 + .type bkpt_from_t32, %function + bkpt_from_t32: + bkpt #0 + bx lr + .size bkpt_from_t32, . - bkpt_from_t32 +"# +); + +#[exception(Undefined)] +fn undefined_handler(_addr: usize) -> ! { + panic!("unexpected undefined exception"); +} + +#[exception(PrefetchAbort)] +unsafe fn prefetch_abort_handler(addr: usize) -> usize { + let hsr = aarch32_cpu::register::Hsr::read(); + println!("prefetch abort occurred {:08x?}, {:x?}", hsr, hsr.get_iss()); + + if (addr + 1) == bkpt_from_t32 as unsafe extern "C" fn() as usize { + // note that thumb functions have their LSB set, despite always being a + // multiple of two - that's how the CPU knows they are written in T32 + // machine code. + println!("caught bkpt_from_t32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, bkpt_from_t32 as unsafe extern "C" fn() as usize + ); + } + + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 2 + } + _ => { + // we've faulted thrice - time to quit + panic!("prefetch_handler called too often"); + } + } +} + +#[exception(DataAbort)] +fn data_abort_handler(_addr: usize) -> ! { + panic!("unexpected data abort exception"); +} diff --git a/examples/mps3-an536-el2/src/bin/svc-a32.rs b/examples/mps3-an536-el2/src/bin/svc-a32.rs new file mode 100644 index 00000000..e131b768 --- /dev/null +++ b/examples/mps3-an536-el2/src/bin/svc-a32.rs @@ -0,0 +1,64 @@ +//! SVC (supervisor call) at EL2 example + +#![no_std] +#![no_main] + +use aarch32_rt::{entry, exception}; +use semihosting::println; + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[entry] +fn main() -> ! { + let x = 1; + let y = x + 1; + let z = (y as f64) * 1.5; + println!("x = {}, y = {}, z = {:0.3}", x, y, z); + let value = do_svc1(); + println!("Got {:08x}", value); + println!("x = {}, y = {}, z = {:0.3}", x, y, z); + mps3_an536_el2::exit(0); +} + +/// This is our HVC exception handler +#[exception(HypervisorCall)] +fn hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 { + let hsr = aarch32_cpu::register::Hsr::new_with_raw_value(hsr); + println!( + "In hvc_handler, with {:08x?}, {:x?}, {:08x?}", + hsr, + hsr.get_iss(), + frame + ); + if hsr.iss().value() == 0xABCD { + do_svc2(); + } + return 0x12345678; +} + +#[instruction_set(arm::a32)] +fn do_svc1() -> u32 { + aarch32_cpu::svc6!( + 0xABCD, + 0x1000_0000, + 0x1000_0001, + 0x1000_0002, + 0x1000_0003, + 0x1000_0004, + 0x1000_0005 + ) +} + +#[instruction_set(arm::a32)] +fn do_svc2() -> u32 { + aarch32_cpu::svc6!( + 0x9876, + 0x2000_0000, + 0x2000_0001, + 0x2000_0002, + 0x2000_0003, + 0x2000_0004, + 0x2000_0005 + ) +} diff --git a/examples/mps3-an536-el2/src/bin/svc-t32.rs b/examples/mps3-an536-el2/src/bin/svc-t32.rs new file mode 100644 index 00000000..6957aea0 --- /dev/null +++ b/examples/mps3-an536-el2/src/bin/svc-t32.rs @@ -0,0 +1,64 @@ +//! SVC (supervisor call) at EL2 example + +#![no_std] +#![no_main] + +use aarch32_rt::{entry, exception}; +use semihosting::println; + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[entry] +fn main() -> ! { + let x = 1; + let y = x + 1; + let z = (y as f64) * 1.5; + println!("x = {}, y = {}, z = {:0.3}", x, y, z); + let value = do_svc1(); + println!("Got {:08x}", value); + println!("x = {}, y = {}, z = {:0.3}", x, y, z); + mps3_an536_el2::exit(0); +} + +/// This is our HVC exception handler +#[exception(HypervisorCall)] +fn hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 { + let hsr = aarch32_cpu::register::Hsr::new_with_raw_value(hsr); + println!( + "In hvc_handler, with {:08x?}, {:x?}, {:08x?}", + hsr, + hsr.get_iss(), + frame + ); + if hsr.iss().value() == 0x12 { + do_svc2(); + } + return 0x12345678; +} + +#[instruction_set(arm::t32)] +fn do_svc1() -> u32 { + aarch32_cpu::svc6!( + 0x12, + 0x1000_0000, + 0x1000_0001, + 0x1000_0002, + 0x1000_0003, + 0x1000_0004, + 0x1000_0005 + ) +} + +#[instruction_set(arm::t32)] +fn do_svc2() -> u32 { + aarch32_cpu::svc6!( + 0x32, + 0x2000_0000, + 0x2000_0001, + 0x2000_0002, + 0x2000_0003, + 0x2000_0004, + 0x2000_0005 + ) +} diff --git a/examples/mps3-an536-el2/src/bin/undef-exception-a32.rs b/examples/mps3-an536-el2/src/bin/undef-exception-a32.rs new file mode 100644 index 00000000..bc1f34ed --- /dev/null +++ b/examples/mps3-an536-el2/src/bin/undef-exception-a32.rs @@ -0,0 +1,94 @@ +//! Example triggering a undef exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; + +use aarch32_rt::{entry, exception}; +use semihosting::println; + +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[entry] +fn main() -> ! { + println!("Hello, this is a undef exception example"); + + unsafe { + // trigger an Undefined exception, from A32 (Arm) mode + udf_from_a32(); + } + + println!("Recovered from fault OK!"); + + mps3_an536_el2::exit(0); +} + +// These functions are written in assembly +unsafe extern "C" { + fn udf_from_a32(); +} + +core::arch::global_asm!( + r#" + // fn udf_from_a32(); + .arm + .global udf_from_a32 + .type udf_from_a32, %function + udf_from_a32: + udf #0 + bx lr + .size udf_from_a32, . - udf_from_a32 +"# +); + +#[exception(PrefetchAbort)] +fn prefetch_abort_handler(_addr: usize) -> ! { + panic!("unexpected undefined exception"); +} + +#[exception(Undefined)] +unsafe fn undefined_handler(addr: usize) -> usize { + let hsr = aarch32_cpu::register::Hsr::read(); + println!( + "undefined abort occurred {:08x?}, {:x?}", + hsr, + hsr.get_iss() + ); + + if addr == udf_from_a32 as unsafe extern "C" fn() as usize { + println!("caught udf_from_a32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, udf_from_a32 as unsafe extern "C" fn() as usize + ); + } + + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 4 + } + _ => { + // we've faulted thrice - time to quit + panic!("_undefined_handler called too often"); + } + } +} + +#[exception(DataAbort)] +fn data_abort_handler(_addr: usize) -> ! { + panic!("unexpected data abort exception"); +} diff --git a/examples/mps3-an536-el2/src/bin/undef-exception-t32.rs b/examples/mps3-an536-el2/src/bin/undef-exception-t32.rs new file mode 100644 index 00000000..01b57ea2 --- /dev/null +++ b/examples/mps3-an536-el2/src/bin/undef-exception-t32.rs @@ -0,0 +1,97 @@ +//! Example triggering a undef exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; + +use aarch32_rt::{entry, exception}; +use semihosting::println; + +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[entry] +fn main() -> ! { + println!("Hello, this is a undef exception example"); + + unsafe { + // trigger an Undefined exception, from T32 (Thumb) mode + udf_from_t32(); + } + + println!("Recovered from fault OK!"); + + mps3_an536_el2::exit(0); +} + +// These functions are written in assembly +unsafe extern "C" { + fn udf_from_t32(); +} + +core::arch::global_asm!( + r#" + // fn udf_from_t32(); + .thumb + .global udf_from_t32 + .type udf_from_t32, %function + udf_from_t32: + udf #0 + bx lr + .size udf_from_t32, . - udf_from_t32 +"# +); + +#[exception(PrefetchAbort)] +fn prefetch_abort_handler(_addr: usize) -> ! { + panic!("unexpected undefined exception"); +} + +#[exception(Undefined)] +unsafe fn undefined_handler(addr: usize) -> usize { + let hsr = aarch32_cpu::register::Hsr::read(); + println!( + "undefined abort occurred {:08x?}, {:x?}", + hsr, + hsr.get_iss() + ); + + if (addr + 1) == udf_from_t32 as unsafe extern "C" fn() as usize { + // note that thumb functions have their LSB set, despite always being a + // multiple of two - that's how the CPU knows they are written in T32 + // machine code. + println!("caught udf_from_t32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, udf_from_t32 as unsafe extern "C" fn() as usize + ); + } + + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 2 + } + _ => { + // we've faulted thrice - time to quit + panic!("_undefined_handler called too often"); + } + } +} + +#[exception(DataAbort)] +fn data_abort_handler(_addr: usize) -> ! { + panic!("unexpected data abort exception"); +} diff --git a/examples/mps3-an536-el2/src/lib.rs b/examples/mps3-an536-el2/src/lib.rs new file mode 100644 index 00000000..0b1669f9 --- /dev/null +++ b/examples/mps3-an536-el2/src/lib.rs @@ -0,0 +1,259 @@ +//! Common code for all examples +//! +//! ## Interrupt Map +//! +//! | Interrupt ID | Description | +//! |--------------|------------------------------| +//! | `EXTPPI0[0]` | UART 0 Receive Interrupt | +//! | `EXTPPI0[1]` | UART 0 Transmit Interrupt | +//! | `EXTPPI0[2]` | UART 0 Combined Interrupt | +//! | `EXTPPI0[3]` | UART 0 Overflow | +//! | `EXTPPI1[0]` | UART 1 Receive Interrupt | +//! | `EXTPPI1[1]` | UART 1 Transmit Interrupt | +//! | `EXTPPI1[2]` | UART 1 Combined Interrupt | +//! | `EXTPPI1[3]` | UART 1 Overflow | +//! | `SP[0]` | WDG | +//! | `SP[1]` | DualTimer 1 | +//! | `SP[2]` | DualTimer 2 | +//! | `SP[3]` | DualTimer Combined | +//! | `SP[4]` | RTC | +//! | `SP[5]` | UART 2 Receive Interrupt | +//! | `SP[6]` | UART 2 Transmit Interrupt | +//! | `SP[7]` | UART 3 Receive Interrupt | +//! | `SP[8]` | UART 3 Transmit Interrupt | +//! | `SP[9]` | UART 4 Receive Interrupt | +//! | `SP[10]` | UART 4 Transmit Interrupt | +//! | `SP[11]` | UART 5 Receive Interrupt | +//! | `SP[12]` | UART 5 Transmit Interrupt | +//! | `SP[13]` | UART 2 Combined Interrupt | +//! | `SP[14]` | UART 3 Combined Interrupt | +//! | `SP[15]` | UART 4 Combined Interrupt | +//! | `SP[16]` | UART 5 Combined Interrupt | +//! | `SP[17]` | UART Overflow (2, 3, 4 & 5) | +//! | `SP[18]` | Ethernet | +//! | `SP[19]` | USB | +//! | `SP[20]` | FPGA Audio I2S | +//! | `SP[21]` | Touch Screen | +//! | `SP[22]` | SPI ADC | +//! | `SP[23]` | SPI Shield 0 | +//! | `SP[24]` | SPI Shield 1 | +//! | `SP[25]` | HDCLCD Interrupt | +//! | `SP[26]` | GPIO 0 Combined Interrupt | +//! | `SP[27]` | GPIO 1 Combined Interrupt | +//! | `SP[28]` | GPIO 2 Combined Interrupt | +//! | `SP[29]` | GPIO 3 Combined Interrupt | +//! | `SP[30..=45]`| GPIO 0.x Interrupt | +//! | `SP[46..=61]`| GPIO 1.x Interrupt | +//! | `SP[62..=77]`| GPIO 2.x Interrupt | +//! | `SP[78..=93]`| GPIO 3.x Interrupt | +//! +//! * Interrupt ID `SP[x]` are shared across cores +//! * Interrupt ID `EXTPPI0[x]` is only available on Core 0 +//! * Interrupt ID `EXTPPI1[x]` is only available on Core 1 + +#![no_std] + +use core::sync::atomic::{AtomicBool, Ordering}; + +/// The PPI for the virtual timer, according to the Cortex-R52 Technical Reference Manual, +/// Table 10-3: PPI assignments. +/// +/// This corresponds to Interrupt ID 27. +pub const VIRTUAL_TIMER_PPI: arm_gic::IntId = arm_gic::IntId::ppi(11); + +/// The PPI for the EL2 timer, according to the Cortex-R52 Technical Reference Manual, +/// Table 10-3: PPI assignments. +/// +/// This corresponds to Interrupt ID 26. +pub const HYP_TIMER_PPI: arm_gic::IntId = arm_gic::IntId::ppi(10); + +#[cfg(not(arm_architecture = "v8-r"))] +compile_error!("This example is only compatible to the ARMv8-R architecture"); + +static WANT_PANIC: AtomicBool = AtomicBool::new(false); + +/// Track if we're already in the exit routine. +/// +/// Stops us doing infinite recursion if we panic whilst doing the stack reporting. +static IN_EXIT: AtomicBool = AtomicBool::new(false); + +/// Called when the application raises an unrecoverable `panic!`. +/// +/// Prints the panic to the console and then exits QEMU using a semihosting +/// breakpoint. +#[panic_handler] +#[cfg(target_os = "none")] +fn panic(info: &core::panic::PanicInfo) -> ! { + semihosting::println!("PANIC: {:#?}", info); + if WANT_PANIC.load(Ordering::Relaxed) { + exit(0); + } else { + exit(1); + } +} + +/// Set the panic function as no longer returning a failure code via semihosting +pub fn want_panic() { + WANT_PANIC.store(true, Ordering::Relaxed); +} + +/// Exit from QEMU with code +pub fn exit(code: i32) -> ! { + if !IN_EXIT.swap(true, Ordering::Relaxed) { + stack_dump(); + } + semihosting::process::exit(code) +} + +/// Print stack using to semihosting output for each stack +/// +/// Produces output like: +/// +/// ```text +/// Stack usage report: +/// UND0 Stack = 0 used of 16384 bytes (000%) @ 0x1006bf80..0x1006ff80 +/// SVC0 Stack = 0 used of 16384 bytes (000%) @ 0x1006ff80..0x10073f80 +/// ABT0 Stack = 0 used of 16384 bytes (000%) @ 0x10073f80..0x10077f80 +/// HYP0 Stack = 0 used of 16384 bytes (000%) @ 0x10077f80..0x1007bf80 +/// IRQ0 Stack = 0 used of 64 bytes (000%) @ 0x1007bf80..0x1007bfc0 +/// FIQ0 Stack = 0 used of 64 bytes (000%) @ 0x1007bfc0..0x1007c000 +/// SYS0 Stack = 2416 used of 16384 bytes (014%) @ 0x1007c000..0x10080000 +/// ``` +fn stack_dump() { + use aarch32_cpu::stacks::stack_used_bytes; + use aarch32_rt::stacks::Stack; + + semihosting::eprintln!("Stack usage report:"); + + unsafe { + for stack in Stack::iter() { + for core in (0..Stack::num_cores()).rev() { + let core_range = stack.range(core).unwrap(); + let (total, used) = stack_used_bytes(core_range.clone()); + let percent = used * 100 / total; + // Send to stderr, so it doesn't mix with expected output on stdout + semihosting::eprintln!( + "{}{} Stack = {:6} used of {:6} bytes ({:03}%) @ {:08x?}", + stack, + core, + used, + total, + percent, + core_range + ); + } + } + } +} + +#[derive(Clone, Debug)] +/// Represents a handler for an interrupt +pub struct InterruptHandler { + int_id: arm_gic::IntId, + function: fn(arm_gic::IntId), +} + +impl InterruptHandler { + /// Create a new `InterruptHandler`, associating an `IntId` with a function to call + pub const fn new(int_id: arm_gic::IntId, function: fn(arm_gic::IntId)) -> InterruptHandler { + InterruptHandler { int_id, function } + } + + /// Get the [`arm_gic::IntId`] this handler is for + pub const fn int_id(&self) -> arm_gic::IntId { + self.int_id + } + + /// Is this handler for this [`arm_gic::IntId`]? + pub fn matches(&self, int_id: arm_gic::IntId) -> bool { + self.int_id == int_id + } + + /// Execute the handler + pub fn execute(&self) { + (self.function)(self.int_id); + } +} + +/// Represents all the hardware we support in our MPS3-AN536 system +pub struct Board { + /// The Arm Generic Interrupt Controller (v3) + pub gic: arm_gic::gicv3::GicV3<'static>, + /// The Arm Virtual Generic Timer + pub virtual_timer: aarch32_cpu::generic_timer::El2VirtualTimer, + /// The Arm Physical Generic Timer + pub physical_timer: aarch32_cpu::generic_timer::El2PhysicalTimer, + /// The Arm EL2-specific Physical Generic Timer + pub hyp_timer: aarch32_cpu::generic_timer::El2HypPhysicalTimer, +} + +impl Board { + /// Create a new board structure. + /// + /// Returns `Some(board)` the first time you call it, and None thereafter, + /// so you cannot have two copies of the [`Board`] structure. + pub fn new() -> Option { + static TAKEN: AtomicBool = AtomicBool::new(false); + if TAKEN + .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) + .is_ok() + { + Some(Board { + // SAFETY: This is the first and only call to `make_gic()` as guaranteed by + // the atomic flag check above, ensuring no aliasing of GIC register access. + gic: unsafe { make_gic() }, + // SAFETY: This is the first and only time we create the virtual timer instance + // as guaranteed by the atomic flag check above, ensuring exclusive access. + virtual_timer: unsafe { aarch32_cpu::generic_timer::El2VirtualTimer::new() }, + // SAFETY: This is the first and only time we create the physical timer instance + // as guaranteed by the atomic flag check above, ensuring exclusive access. + physical_timer: unsafe { aarch32_cpu::generic_timer::El2PhysicalTimer::new() }, + // SAFETY: This is the first and only time we create the physical timer instance + // as guaranteed by the atomic flag check above, ensuring exclusive access. + hyp_timer: unsafe { aarch32_cpu::generic_timer::El2HypPhysicalTimer::new() }, + }) + } else { + None + } + } +} + +/// Create the ARM GIC driver +/// +/// # Safety +/// +/// Only call this function once. +unsafe fn make_gic() -> arm_gic::gicv3::GicV3<'static> { + /// Offset from PERIPHBASE for GIC Distributor + const GICD_BASE_OFFSET: usize = 0x0000_0000usize; + + /// Offset from PERIPHBASE for the first GIC Redistributor + const GICR_BASE_OFFSET: usize = 0x0010_0000usize; + + // Get the GIC address by reading CBAR + let periphbase = aarch32_cpu::register::ImpCbar::read().periphbase(); + semihosting::println!("Found PERIPHBASE {:010p}", periphbase); + let gicd_base = periphbase.wrapping_byte_add(GICD_BASE_OFFSET); + let gicr_base = periphbase.wrapping_byte_add(GICR_BASE_OFFSET); + + // Initialise the GIC. + semihosting::println!( + "Creating GIC driver @ {:010p} / {:010p}", + gicd_base, + gicr_base + ); + // SAFETY: `gicd_base` points to the valid GICD MMIO region as obtained from the + // hardware CBAR register. This pointer is used exclusively by this GIC instance. + let gicd = unsafe { + arm_gic::UniqueMmioPointer::new(core::ptr::NonNull::new(gicd_base.cast()).unwrap()) + }; + let gicr_base = core::ptr::NonNull::new(gicr_base.cast()).unwrap(); + // SAFETY: The GICD and GICR base addresses point to valid GICv3 MMIO regions as + // obtained from the hardware CBAR register. This function is only called once + // (via Board::new()'s atomic guard), ensuring exclusive ownership of the GIC. + let mut gic = unsafe { arm_gic::gicv3::GicV3::new(gicd, gicr_base, 1, false) }; + semihosting::println!("Calling git.setup(0)"); + gic.setup(0); + arm_gic::gicv3::GicCpuInterface::set_priority_mask(0x80); + gic +} diff --git a/examples/mps3-an536-smp/src/bin/smp-test.rs b/examples/mps3-an536-smp/src/bin/smp-test.rs index 4364fbbe..9fb75435 100644 --- a/examples/mps3-an536-smp/src/bin/smp-test.rs +++ b/examples/mps3-an536-smp/src/bin/smp-test.rs @@ -32,7 +32,10 @@ const CS_MUTEX_LOOPS: u32 = 1000; /// It is called by the start-up code in `aarch32-rt`. #[entry] fn main() -> ! { - println!("I am core 0 - {:08x?}", aarch32_cpu::register::Mpidr::read()); + println!( + "I am core 0 - {:08x?}", + aarch32_cpu::register::Mpidr::read() + ); mps3_an536_smp::start_core1(); @@ -93,7 +96,10 @@ fn main() -> ! { /// It is called by the start-up code below, on Core 1. #[unsafe(no_mangle)] pub extern "C" fn kmain2() { - println!("I am core 1 - {:08x?}", aarch32_cpu::register::Mpidr::read()); + println!( + "I am core 1 - {:08x?}", + aarch32_cpu::register::Mpidr::read() + ); CORE1_BOOTED.store(true, Ordering::SeqCst); for _ in 0..CAS_LOOPS { diff --git a/examples/mps3-an536-smp/src/lib.rs b/examples/mps3-an536-smp/src/lib.rs index e3a798e2..a187a6ba 100644 --- a/examples/mps3-an536-smp/src/lib.rs +++ b/examples/mps3-an536-smp/src/lib.rs @@ -53,7 +53,7 @@ #![no_std] -use aarch32_cpu::register::{Hactlr, Cpsr, cpsr::ProcessorMode}; +use aarch32_cpu::register::{Cpsr, Hactlr, cpsr::ProcessorMode}; use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/examples/mps3-an536/src/bin/el2_hello.rs b/examples/mps3-an536/src/bin/el2_hello.rs deleted file mode 100644 index e10a979c..00000000 --- a/examples/mps3-an536/src/bin/el2_hello.rs +++ /dev/null @@ -1,98 +0,0 @@ -//! Semihosting hello-world for Arm Cortex-R52 running in EL2 (Hyp Mode) - -#![no_std] -#![no_main] - -use aarch32_cpu::register::Hactlr; -use aarch32_rt::entry; -use mps3_an536 as _; -use semihosting::println; - -/// The entry-point to the Rust application. -/// -/// It is called by the start-up code at the bottom of this file. -#[entry] -fn main() -> ! { - let x = 1.0f64; - let y = x * 2.0; - println!("Hello, this is semihosting! x = {:0.3}, y = {:0.3}", x, y); - - let mut mpu = unsafe { aarch32_cpu::pmsav8::El2Mpu::new() }; - for idx in 0..mpu.num_regions() { - if let Some(region) = mpu.get_region(idx) { - println!("Region {}: {:?}", idx, region); - } - } - - mps3_an536::want_panic(); - panic!("I am an example panic"); -} - -// Provide a custom `_start` function that sets us up in EL2 mode, with a -// stack. -// -// Unlike the default routine, it does not initialise any other stacks, or -// switch to EL1 mode. -core::arch::global_asm!( - r#" - // Work around https://github.com/rust-lang/rust/issues/127269 - .fpu vfp3-d16 - - .section .text.start - - .global _start - .type _start, %function - _start: - // Set stack pointer - ldr sp, =_hyp_stack_high_end - // Set the HVBAR (for EL2) to _vector_table - ldr r1, =_vector_table - mcr p15, 4, r1, c12, c0, 0 - // Configure HACTLR to let us enter EL1 - mrc p15, 4, r1, c1, c0, 1 - mov r2, {hactlr_bits} - orr r1, r1, r2 - mcr p15, 4, r1, c1, c0, 1 - // Init .data and .bss - bl _init_segments - // Allow VFP coprocessor access - mrc p15, 0, r0, c1, c0, 2 - orr r0, r0, #0xF00000 - mcr p15, 0, r0, c1, c0, 2 - // Enable VFP - mov r0, #0x40000000 - vmsr fpexc, r0 - // Zero all registers before calling kmain - mov r0, 0 - mov r1, 0 - mov r2, 0 - mov r3, 0 - mov r4, 0 - mov r5, 0 - mov r6, 0 - mov r7, 0 - mov r8, 0 - mov r9, 0 - mov r10, 0 - mov r11, 0 - mov r12, 0 - // Jump to application - bl kmain - // In case the application returns, loop forever - b . - .size _start, . - _start - "#, - hactlr_bits = const { - Hactlr::new_with_raw_value(0) - .with_cpuactlr(true) - .with_cdbgdci(true) - .with_flashifregionr(true) - .with_periphpregionr(true) - .with_qosr(true) - .with_bustimeoutr(true) - .with_intmonr(true) - .with_err(true) - .with_testr1(true) - .raw_value() - }, -); diff --git a/examples/mps3-an536/src/bin/generic_timer_irq.rs b/examples/mps3-an536/src/bin/generic_timer_irq.rs index f870ddea..2f2f4566 100644 --- a/examples/mps3-an536/src/bin/generic_timer_irq.rs +++ b/examples/mps3-an536/src/bin/generic_timer_irq.rs @@ -16,9 +16,6 @@ use semihosting::println; fn main() -> ! { let mut board = mps3_an536::Board::new().unwrap(); - // Only interrupts with a higher priority (numerically lower) will be signalled. - GicCpuInterface::set_priority_mask(0x80); - println!("Configure Timer Interrupt..."); board .gic diff --git a/examples/mps3-an536/src/bin/gic-map.rs b/examples/mps3-an536/src/bin/gic-map.rs index e45db841..f10edf92 100644 --- a/examples/mps3-an536/src/bin/gic-map.rs +++ b/examples/mps3-an536/src/bin/gic-map.rs @@ -29,9 +29,6 @@ static INTERRUPT_HANDLERS: critical_section::Mutex ! { let mut board = mps3_an536::Board::new().unwrap(); - // Only interrupts with a higher priority (numerically lower) will be signalled. - GicCpuInterface::set_priority_mask(0x80); - // Configure two Software Generated Interrupts for Core 0 println!("Configure low-prio SGI..."); board diff --git a/examples/mps3-an536/src/bin/gic-priority-ceiling.rs b/examples/mps3-an536/src/bin/gic-priority-ceiling.rs index 30e14cc3..c3064254 100644 --- a/examples/mps3-an536/src/bin/gic-priority-ceiling.rs +++ b/examples/mps3-an536/src/bin/gic-priority-ceiling.rs @@ -26,9 +26,6 @@ const HIGH_PRIORITY: u8 = 0x10; fn main() -> ! { let mut board = mps3_an536::Board::new().unwrap(); - // Only interrupts with a higher priority (numerically lower) will be signalled. - GicCpuInterface::set_priority_mask(0x80); - // Configure a Software Generated Interrupt for Core 0 println!("Configure low-prio SGI..."); board diff --git a/examples/mps3-an536/src/bin/gic-static-section-irq.rs b/examples/mps3-an536/src/bin/gic-static-section-irq.rs index 340bfc24..b3869885 100644 --- a/examples/mps3-an536/src/bin/gic-static-section-irq.rs +++ b/examples/mps3-an536/src/bin/gic-static-section-irq.rs @@ -23,9 +23,6 @@ const SGI_INTID_HI: IntId = IntId::sgi(4); fn main() -> ! { let mut board = mps3_an536::Board::new().unwrap(); - // Only interrupts with a higher priority (numerically lower) will be signalled. - GicCpuInterface::set_priority_mask(0x80); - // Configure two Software Generated Interrupts for Core 0 println!("Configure low-prio SGI..."); board diff --git a/examples/mps3-an536/src/lib.rs b/examples/mps3-an536/src/lib.rs index e3eb26b5..3f5ba82f 100644 --- a/examples/mps3-an536/src/lib.rs +++ b/examples/mps3-an536/src/lib.rs @@ -55,12 +55,18 @@ use core::sync::atomic::{AtomicBool, Ordering}; -/// The PPI for the virutal timer, according to the Cortex-R52 Technical Reference Manual, +/// The PPI for the virtual timer, according to the Cortex-R52 Technical Reference Manual, /// Table 10-3: PPI assignments. /// /// This corresponds to Interrupt ID 27. pub const VIRTUAL_TIMER_PPI: arm_gic::IntId = arm_gic::IntId::ppi(11); +/// The PPI for the EL2 timer, according to the Cortex-R52 Technical Reference Manual, +/// Table 10-3: PPI assignments. +/// +/// This corresponds to Interrupt ID 26. +pub const HYP_TIMER_PPI: arm_gic::IntId = arm_gic::IntId::ppi(10); + #[cfg(not(arm_architecture = "v8-r"))] compile_error!("This example is only compatible to the ARMv8-R architecture"); diff --git a/justfile b/justfile index 9990aab9..e556bd90 100644 --- a/justfile +++ b/justfile @@ -103,10 +103,14 @@ build-versatileab-tier2 target: # Builds the MPS3-AN536 examples, building core from source build-mps3-tier3 target: cd examples/mps3-an536 && cargo build --target={{target}} -Zbuild-std=core {{verbose}} + cd examples/mps3-an536-smp && cargo build --target={{target}} -Zbuild-std=core {{verbose}} + cd examples/mps3-an536-el2 && cargo build --target={{target}} -Zbuild-std=core {{verbose}} # Builds the MPS3-AN536 examples, assuming core has been prebuilt build-mps3-tier2 target: cd examples/mps3-an536 && cargo build --target={{target}} {{verbose}} + cd examples/mps3-an536-smp && cargo build --target={{target}} {{verbose}} + cd examples/mps3-an536-el2 && cargo build --target={{target}} {{verbose}} # Formats all the code fmt: @@ -117,6 +121,8 @@ fmt: # The cross-compiled examples cargo fmt cd examples/versatileab && cargo fmt {{verbose}} cd examples/mps3-an536 && cargo fmt {{verbose}} + cd examples/mps3-an536-smp && cargo fmt {{verbose}} + cd examples/mps3-an536-el2 && cargo fmt {{verbose}} # Checks all the code is formatted fmt-check: @@ -127,6 +133,8 @@ fmt-check: # The cross-compiled examples cargo fmt cd examples/versatileab && cargo fmt --check {{verbose}} cd examples/mps3-an536 && cargo fmt --check {{verbose}} + cd examples/mps3-an536-smp && cargo fmt --check {{verbose}} + cd examples/mps3-an536-el2 && cargo fmt --check {{verbose}} # Checks all the cross-compiled workspace passes the clippy lints clippy-targets: \ @@ -144,6 +152,8 @@ clippy-target target: clippy-examples: cd examples/versatileab && cargo clippy --target=armv7r-none-eabi {{verbose}} cd examples/mps3-an536 && cargo clippy --target=armv8r-none-eabihf {{verbose}} + cd examples/mps3-an536-smp && cargo clippy --target=armv8r-none-eabihf {{verbose}} + cd examples/mps3-an536-el2 && cargo clippy --target=armv8r-none-eabihf {{verbose}} # Checks the host code passes the clippy lints clippy-host: @@ -224,3 +234,12 @@ test-qemu-v8r-smp: RUSTFLAGS=-Ctarget-cpu=cortex-r52 ./tests.sh examples/mps3-an536-smp armv8r-none-eabihf --features=fpu-d32 --target-dir=target-d32 {{verbose}} --release || FAIL=1 RUSTFLAGS=-Ctarget-cpu=cortex-r52 ./tests.sh examples/mps3-an536-smp thumbv8r-none-eabihf -Zbuild-std=core --features=fpu-d32 --target-dir=target-d32 {{verbose}} --release || FAIL=1 if [ "${FAIL}" == "1" ]; then exit 1; fi + +test-qemu-v8r-el2: + #!/bin/bash + FAIL=0 + ./tests.sh examples/mps3-an536-el2 armv8r-none-eabihf {{verbose}} --release || FAIL=1 + ./tests.sh examples/mps3-an536-el2 thumbv8r-none-eabihf -Zbuild-std=core {{verbose}} --release || FAIL=1 + RUSTFLAGS=-Ctarget-cpu=cortex-r52 ./tests.sh examples/mps3-an536-el2 armv8r-none-eabihf --features=fpu-d32 --target-dir=target-d32 {{verbose}} --release || FAIL=1 + RUSTFLAGS=-Ctarget-cpu=cortex-r52 ./tests.sh examples/mps3-an536-el2 thumbv8r-none-eabihf -Zbuild-std=core --features=fpu-d32 --target-dir=target-d32 {{verbose}} --release || FAIL=1 + if [ "${FAIL}" == "1" ]; then exit 1; fi From f6b292da48dc99676e1e46cd3cc502820246c2e3 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sat, 28 Mar 2026 21:35:53 +0000 Subject: [PATCH 2/2] Comment updates. * Turned on deny(missing_docs), and added missing docs * Turned on deny(clippy::missing_safety_doc) and added missing safety docs * Turned on some more useful lints * Clippy fixes * Rationalised/renamed the various CachePolicy types --- aarch32-cpu/Cargo.toml | 24 +++- aarch32-cpu/src/asmv4.rs | 1 + aarch32-cpu/src/cache.rs | 2 + aarch32-cpu/src/generic_timer/el0.rs | 7 +- aarch32-cpu/src/lib.rs | 15 ++- aarch32-cpu/src/mmu.rs | 123 +++++++++++++----- aarch32-cpu/src/pmsav7.rs | 86 +++++++----- aarch32-cpu/src/pmsav8.rs | 27 ++-- aarch32-cpu/src/register/armv8r/cnthctl.rs | 40 ++---- aarch32-cpu/src/register/armv8r/cnthp_ctl.rs | 1 + aarch32-cpu/src/register/armv8r/cntkctl.rs | 1 + aarch32-cpu/src/register/armv8r/cntp_ctl.rs | 1 + aarch32-cpu/src/register/armv8r/cntv_ctl.rs | 1 + aarch32-cpu/src/register/armv8r/hcptr.rs | 1 + aarch32-cpu/src/register/armv8r/hcr.rs | 5 + aarch32-cpu/src/register/armv8r/hmpuir.rs | 1 + aarch32-cpu/src/register/armv8r/hprbar.rs | 1 + aarch32-cpu/src/register/armv8r/hprlar.rs | 1 + aarch32-cpu/src/register/armv8r/hsctlr.rs | 1 + aarch32-cpu/src/register/armv8r/hsr.rs | 42 ++++++ aarch32-cpu/src/register/armv8r/hvbar.rs | 2 +- aarch32-cpu/src/register/armv8r/prbar.rs | 1 + aarch32-cpu/src/register/armv8r/prlar.rs | 1 + aarch32-cpu/src/register/bpiall.rs | 1 + aarch32-cpu/src/register/ccsidr.rs | 8 ++ aarch32-cpu/src/register/csselr.rs | 5 + aarch32-cpu/src/register/dacr.rs | 2 + aarch32-cpu/src/register/dccimvac.rs | 6 +- aarch32-cpu/src/register/dccisw.rs | 5 +- aarch32-cpu/src/register/dccmvac.rs | 6 +- aarch32-cpu/src/register/dccmvau.rs | 6 +- aarch32-cpu/src/register/dccsw.rs | 5 +- aarch32-cpu/src/register/dcimvac.rs | 6 +- aarch32-cpu/src/register/dcisw.rs | 5 +- aarch32-cpu/src/register/dfsr.rs | 26 +++- aarch32-cpu/src/register/dracr.rs | 1 + aarch32-cpu/src/register/drsr.rs | 4 + aarch32-cpu/src/register/iciallu.rs | 5 +- aarch32-cpu/src/register/ifsr.rs | 58 +++++++++ aarch32-cpu/src/register/imp/imp_cdbgdcd.rs | 6 +- aarch32-cpu/src/register/imp/imp_cdbgdct.rs | 6 +- aarch32-cpu/src/register/imp/imp_cdbgdr0.rs | 6 +- aarch32-cpu/src/register/imp/imp_cdbgdr1.rs | 6 +- aarch32-cpu/src/register/imp/imp_cdbgdr2.rs | 6 +- aarch32-cpu/src/register/imp/imp_cdbgicd.rs | 6 +- aarch32-cpu/src/register/imp/imp_cdbgict.rs | 6 +- aarch32-cpu/src/register/iracr.rs | 1 + aarch32-cpu/src/register/irsr.rs | 1 + aarch32-cpu/src/register/mod.rs | 11 +- aarch32-cpu/src/register/mpuir.rs | 1 + aarch32-cpu/src/register/sctlr.rs | 1 + aarch32-cpu/src/register/tlbiall.rs | 1 + aarch32-cpu/src/register/ttbr0.rs | 5 +- aarch32-cpu/src/stacks.rs | 52 ++++---- .../mps3-an536-el2/src/bin/generic-timer.rs | 4 +- examples/mps3-an536-el2/src/bin/hvc-a32.rs | 2 +- examples/mps3-an536-el2/src/bin/hvc-t32.rs | 2 +- examples/mps3-an536-el2/src/bin/svc-a32.rs | 2 +- examples/mps3-an536-el2/src/bin/svc-t32.rs | 2 +- examples/mps3-an536/src/bin/hvc-a32.rs | 2 +- examples/mps3-an536/src/bin/hvc-t32.rs | 2 +- examples/mps3-an536/src/bin/registers.rs | 14 +- .../reference/registers-armv7r-none-eabi.out | 2 +- .../registers-armv7r-none-eabihf.out | 2 +- .../registers-thumbv7r-none-eabi.out | 2 +- .../registers-thumbv7r-none-eabihf.out | 2 +- examples/versatileab/src/bin/registers.rs | 6 +- examples/versatileab/src/mmu.rs | 8 +- 68 files changed, 494 insertions(+), 207 deletions(-) diff --git a/aarch32-cpu/Cargo.toml b/aarch32-cpu/Cargo.toml index 76903a97..b0a9d72c 100644 --- a/aarch32-cpu/Cargo.toml +++ b/aarch32-cpu/Cargo.toml @@ -51,11 +51,25 @@ serde = ["dep:serde", "arbitrary-int/serde"] check-asm = [] [package.metadata.docs.rs] -# This is a list of supported Tier 2 targets, as of latest stable targets = [ - "armv7r-none-eabihf", - "armv7r-none-eabi", - "armv7a-none-eabihf", + "armebv7r-none-eabi", + "armebv7r-none-eabihf", + "armv4t-none-eabi", + "armv5te-none-eabi", + "armv6-none-eabi", + "armv6-none-eabihf", "armv7a-none-eabi", - "armv8r-none-eabihf" + "armv7a-none-eabihf", + "armv7r-none-eabi", + "armv7r-none-eabihf", + "armv8r-none-eabihf", + "thumbv4t-none-eabi", + "thumbv5te-none-eabi", + "thumbv6-none-eabi", + "thumbv7a-none-eabi", + "thumbv7a-none-eabihf", + "thumbv7r-none-eabi", + "thumbv7r-none-eabihf", + "thumbv8r-none-eabihf", ] +cargo-args = ["-Z", "build-std"] diff --git a/aarch32-cpu/src/asmv4.rs b/aarch32-cpu/src/asmv4.rs index b6db42af..83424294 100644 --- a/aarch32-cpu/src/asmv4.rs +++ b/aarch32-cpu/src/asmv4.rs @@ -66,6 +66,7 @@ pub fn core_id() -> u32 { r & 0x00FF_FFFF } +/// LLVM intrinsic for memory barriers #[no_mangle] pub extern "C" fn __sync_synchronize() { // we don't have a barrier instruction - the linux kernel just uses an empty inline asm block diff --git a/aarch32-cpu/src/cache.rs b/aarch32-cpu/src/cache.rs index 0dfec354..b2f1d067 100644 --- a/aarch32-cpu/src/cache.rs +++ b/aarch32-cpu/src/cache.rs @@ -1,3 +1,5 @@ +//! Helper functions for dealing with CPU cache + use arbitrary_int::u3; use crate::register::{Dccimvac, Dccisw, Dccmvac, Dccsw, Dcimvac, Dcisw, SysRegWrite}; diff --git a/aarch32-cpu/src/generic_timer/el0.rs b/aarch32-cpu/src/generic_timer/el0.rs index 850babfb..90dd61f3 100644 --- a/aarch32-cpu/src/generic_timer/el0.rs +++ b/aarch32-cpu/src/generic_timer/el0.rs @@ -2,7 +2,7 @@ use crate::register; -/// Represents our Generic Timer when we are running at EL0. +/// Represents our Generic Physical Timer when we are running at EL0. /// /// Note that for most of these APIs to work, EL0 needs to have been granted /// access using methods like @@ -75,6 +75,11 @@ impl super::GenericTimer for El0PhysicalTimer { } } +/// Represents our Generic Virtual Timer when we are running at EL0. +/// +/// Note that for most of these APIs to work, EL0 needs to have been granted +/// access using methods like +/// [El1VirtualTimer::el0_access_virtual_counter](crate::generic_timer::El1VirtualTimer::el0_access_virtual_counter). pub struct El0VirtualTimer(); impl El0VirtualTimer { diff --git a/aarch32-cpu/src/lib.rs b/aarch32-cpu/src/lib.rs index 4e411d39..fd9952aa 100644 --- a/aarch32-cpu/src/lib.rs +++ b/aarch32-cpu/src/lib.rs @@ -1,5 +1,10 @@ //! CPU/peripheral support for Arm AArch32 #![no_std] +#![deny(missing_docs)] +#![deny(unsafe_op_in_unsafe_fn)] +#![deny(clippy::missing_safety_doc)] +#![deny(clippy::unnecessary_safety_comment)] +#![deny(clippy::unnecessary_safety_doc)] mod critical_section; @@ -23,18 +28,20 @@ pub mod asm; pub mod cache; pub mod interrupt; -pub mod mmu; pub mod register; #[cfg(target_arch = "arm")] pub mod stacks; -#[cfg(any(test, doc, arm_architecture = "v7-r"))] +#[cfg(any(test, arm_profile = "a", arm_profile = "legacy"))] +pub mod mmu; + +#[cfg(any(test, arm_architecture = "v7-r"))] pub mod pmsav7; -#[cfg(any(test, doc, arm_architecture = "v8-r"))] +#[cfg(any(test, arm_architecture = "v8-r"))] pub mod generic_timer; -#[cfg(any(test, doc, arm_architecture = "v8-r"))] +#[cfg(any(test, arm_architecture = "v8-r"))] pub mod pmsav8; /// Generate an SVC call with no parameters. diff --git a/aarch32-cpu/src/mmu.rs b/aarch32-cpu/src/mmu.rs index aa6a246d..43056296 100644 --- a/aarch32-cpu/src/mmu.rs +++ b/aarch32-cpu/src/mmu.rs @@ -9,7 +9,11 @@ pub const NUM_L1_PAGE_TABLE_ENTRIES: usize = 4096; /// /// You should create a static variable of this type, to represent your page table. #[repr(C, align(1048576))] +#[derive(Debug)] pub struct L1Table { + /// Our mutable list of MMU table entries + /// + /// This table is read by the hardware. pub entries: core::cell::UnsafeCell<[L1Section; NUM_L1_PAGE_TABLE_ENTRIES]>, } @@ -24,25 +28,39 @@ unsafe impl Sync for L1Table {} pub struct InvalidL1EntryType(pub L1EntryType); /// Access permissions for a region of memory -#[bitbybit::bitenum(u3, exhaustive = true)] +#[bitbybit::bitenum(u3)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, PartialEq, Eq)] pub enum AccessPermissions { + /// All accesses generate Permission faults PermissionFault = 0b000, + /// Privileged access only PrivilegedOnly = 0b001, + /// Writes in User mode generate Permission faults NoUserWrite = 0b010, + /// Full access FullAccess = 0b011, - _Reserved1 = 0b100, + /// Privileged read-only PrivilegedReadOnly = 0b101, - ReadOnly = 0b110, - _Reserved2 = 0b111, + /// Privileged and User read-only (deprecated in VMSAv7) + ReadOnlyv6 = 0b110, + /// Privileged and User read-only + ReadOnly = 0b111, } impl AccessPermissions { + /// Create a new [`AccessPermissions`] value from the APX bit at the AP bit + /// pair + /// + /// Will panic if you select an invalid value. #[inline] pub const fn new(apx: bool, ap: u2) -> Self { - Self::new_with_raw_value(u3::new(((apx as u8) << 2) | ap.value())) + let x = u3::new(((apx as u8) << 2) | ap.value()); + let Ok(ap) = Self::new_with_raw_value(x) else { + panic!("Invalid access permissions"); + }; + ap } /// AP bit for the given access permission. @@ -65,21 +83,25 @@ impl AccessPermissions { #[derive(Debug, PartialEq, Eq)] #[repr(u8)] pub enum L1EntryType { - /// Access generates an abort exception. Indicates an unmapped virtual address. + /// Access generates an abort exception. Indicates an unmapped virtual + /// address. Fault = 0b00, - /// Entry points to a L2 translation table, allowing 1 MB of memory to be further divided + /// Entry points to a L2 translation table, allowing 1 MB of memory to be + /// further divided PageTable = 0b01, /// Maps a 1 MB region to a physical address. Section = 0b10, - /// Special 1MB section entry which requires 16 entries in the translation table. + /// Special 1MB section entry which requires 16 entries in the translation + /// table. Supersection = 0b11, } -/// The ARM Cortex-A architecture reference manual p.1363 specifies these attributes in more detail. +/// The ARM Cortex-A architecture reference manual p.1363 specifies these +/// attributes in more detail. /// -/// The B (Bufferable), C (Cacheable), and TEX (Type extension) bit names are inherited from -/// earlier versions of the architecture. These names no longer adequately describe the function -/// of the B, C, and TEX bits. +/// The B (Bufferable), C (Cacheable), and TEX (Type extension) bit names are +/// inherited from earlier versions of the architecture. These names no longer +/// adequately describe the function of the B, C, and TEX bits. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -91,6 +113,7 @@ pub struct MemoryRegionAttributesRaw { } impl MemoryRegionAttributesRaw { + /// Create a new [`MemoryRegionAttributesRaw`] from constituent parts #[inline] pub const fn new(type_extensions: u3, c: bool, b: bool) -> Self { Self { @@ -105,11 +128,15 @@ impl MemoryRegionAttributesRaw { #[bitbybit::bitenum(u2, exhaustive = true)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug)] -pub enum CacheableMemoryAttribute { +#[derive(Debug, PartialEq, Eq)] +pub enum CachePolicy { + /// Non-cacheable NonCacheable = 0b00, + /// Write-Back Cacheable, Write-Allocate WriteBackWriteAlloc = 0b01, + /// Write-Through Cacheable WriteThroughNoWriteAlloc = 0b10, + /// Write-Back Cacheable, no Write-Allocate WriteBackNoWriteAlloc = 0b11, } @@ -118,20 +145,43 @@ pub enum CacheableMemoryAttribute { #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum MemoryRegionAttributes { + /// Strongly- ordered + /// + /// All memory accesses to Strongly-ordered memory occur in program order. + /// All Strongly-ordered regions are assumed to be Shareable. StronglyOrdered, + /// Device Shareable + /// + /// Intended to handle memory-mapped peripherals that are shared by several + /// processors. ShareableDevice, + /// Normal Memory, Write-Through Cacheable for both Inner and Outer Cache OuterAndInnerWriteThroughNoWriteAlloc, + /// Normal Memory, Write-Back no Write-Allocate Cacheable for both Inner and + /// Outer Cache OuterAndInnerWriteBackNoWriteAlloc, + /// Normal Memory, Non-cacheable for both Inner and Outer Cache OuterAndInnerNonCacheable, + /// Normal Memory, Write-Back Write-Allocate Cacheable for both Inner and + /// Outer Cache OuterAndInnerWriteBackWriteAlloc, + /// Device Non-Shareable + /// + /// Intended to handle memory-mapped peripherals that are used only by a + /// single processor. NonShareableDevice, + /// Normal Memory, where Inner and Outer cache have different settings CacheableMemory { - inner: CacheableMemoryAttribute, - outer: CacheableMemoryAttribute, + /// Settings for the Inner Cache + inner: CachePolicy, + /// Settings for the Outer Cache + outer: CachePolicy, }, } impl MemoryRegionAttributes { + /// Convert the Rust enum type [`MemoryRegionAttributes`] into a raw + /// [`MemoryRegionAttributesRaw`] value for the hardware pub const fn as_raw(&self) -> MemoryRegionAttributesRaw { match self { MemoryRegionAttributes::StronglyOrdered => { @@ -175,10 +225,13 @@ pub struct SectionAttributes { pub non_global: bool, /// Implementation defined bit. pub p_bit: bool, + /// Is memory shareable across multiple CPUs pub shareable: bool, - /// AP bits + /// Access permissions pub access: AccessPermissions, + /// Raw memory attributes pub memory_attrs: MemoryRegionAttributesRaw, + /// Domain value for this section pub domain: u4, /// xN bit. pub execute_never: bool, @@ -195,7 +248,8 @@ impl SectionAttributes { Ok(Self::from_raw_unchecked(raw)) } - /// Retrieves the corresponding L1 section part without the section base address being set. + /// Retrieves the corresponding L1 section part without the section base + /// address being set. const fn l1_section_part(&self) -> L1Section { L1Section::builder() .with_base_addr_upper_bits(u12::new(0)) @@ -231,8 +285,9 @@ impl SectionAttributes { /// 1 MB section translation entry, mapping a 1 MB region to a physical address. /// -/// The ARM Cortex-A architecture programmers manual chapter 9.4 (p.163) or the ARMv7-A and ArmV7-R -/// architecture reference manual p.1323 specify these attributes in more detail. +/// The ARM Cortex-A architecture programmers manual chapter 9.4 (p.163) or the +/// ARMv7-A and ArmV7-R architecture reference manual p.1323 specify these +/// attributes in more detail. #[bitbybit::bitfield(u32, default = 0, defmt_fields(feature = "defmt"))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(PartialEq, Eq)] @@ -246,23 +301,31 @@ pub struct L1Section { /// Shareable bit. #[bit(16, rw)] s: bool, + /// Part of the access permissions field #[bit(15, rw)] apx: bool, - /// Type extension bits. + /// Memory Region Attribute bit #[bits(12..=14, rw)] tex: u3, + /// Part of the access permissions field #[bits(10..=11, rw)] ap: u2, + /// Implementation defined bit #[bit(9, rw)] p_bit: bool, + /// Domain field #[bits(5..=8, rw)] domain: u4, + /// Execute-never bit #[bit(4, rw)] xn: bool, + /// Memory Region Attribute bit #[bit(3, rw)] c: bool, + /// Memory Region Attribute bit #[bit(2, rw)] b: bool, + /// Entry Type #[bits(0..=1, rw)] entry_type: L1EntryType, } @@ -287,11 +350,12 @@ impl core::fmt::Debug for L1Section { } impl L1Section { - /// Generates a new L1 section from a physical address and section attributes. + /// Generates a new L1 section from a physical address and section + /// attributes. /// - /// The uppermost 12 bits of the physical address define which 1 MB of virtual address space - /// are being accessed. They will be stored in the L1 section table. This address MUST be - /// aligned to 1 MB. + /// The uppermost 12 bits of the physical address define which 1 MB of + /// virtual address space are being accessed. They will be stored in the L1 + /// section table. This address MUST be aligned to 1 MB. /// /// # Panics /// @@ -316,7 +380,8 @@ impl L1Section { *self = Self::new_with_addr_upper_bits_and_attrs(self.base_addr_upper_bits(), section_attrs) } - /// Create a new L1 section with the given upper 12 bits of the address and section attributes. + /// Create a new L1 section with the given upper 12 bits of the address and + /// section attributes. #[inline] pub const fn new_with_addr_upper_bits_and_attrs( addr_upper_twelve_bits: u12, @@ -368,8 +433,8 @@ mod tests { access: AccessPermissions::FullAccess, // TEX 0b101, c false, b true memory_attrs: MemoryRegionAttributes::CacheableMemory { - inner: CacheableMemoryAttribute::WriteBackWriteAlloc, - outer: CacheableMemoryAttribute::WriteBackWriteAlloc, + inner: CachePolicy::WriteBackWriteAlloc, + outer: CachePolicy::WriteBackWriteAlloc, } .as_raw(), domain: u4::new(0b1010), @@ -431,7 +496,7 @@ mod tests { p_bit: true, shareable: false, // APX true, AP 0b10 - access: AccessPermissions::ReadOnly, + access: AccessPermissions::ReadOnlyv6, // TEX 0b000, c false, b false memory_attrs: MemoryRegionAttributes::StronglyOrdered.as_raw(), domain: u4::new(0b1001), diff --git a/aarch32-cpu/src/pmsav7.rs b/aarch32-cpu/src/pmsav7.rs index 2b55a457..9706d3e2 100644 --- a/aarch32-cpu/src/pmsav7.rs +++ b/aarch32-cpu/src/pmsav7.rs @@ -8,6 +8,7 @@ use crate::register; use arbitrary_int::{u2, u3}; + #[doc(inline)] pub use register::drsr::RegionSize; @@ -235,21 +236,42 @@ pub enum MemAttr { /// Strongly-ordered memory StronglyOrdered, /// Device (shareable or non-shareable) - Device { shareable: bool }, - /// Outer and Inner Write-Through, no Write-Allocate - WriteThroughNoWriteAllocate { shareable: bool }, - /// Outer and Inner Write-Back, no Write-Allocate - WriteBackNoWriteAllocate { shareable: bool }, - /// Outer and Inner Non-cacheable - NonCacheable { shareable: bool }, + Device { + /// Is device shareable (across multiple CPUs) + shareable: bool, + }, + /// Normal Memory, Outer and Inner Cache are Write-Through, no Write-Allocate + WriteThroughNoWriteAlloc { + /// Is memory shareable (across multiple CPUs) + shareable: bool, + }, + /// Normal Memory, Outer and Inner Cache are Write-Back, no Write-Allocate + WriteBackNoWriteAlloc { + /// Is memory shareable (across multiple CPUs) + shareable: bool, + }, + /// Normal Memory, Non-cacheable in Outer and Inner Caches + NonCacheable { + /// Is memory shareable (across multiple CPUs) + shareable: bool, + }, /// Implementation Defined - ImplementationDefined { shareable: bool }, + ImplementationDefined { + /// Is memory shareable (across multiple CPUs) + shareable: bool, + }, /// Outer and Inner Write-Back, Write-Allocate - WriteBackWriteAllocate { shareable: bool }, - /// Cacheable memory + WriteBackWriteAlloc { + /// Is memory shareable (across multiple CPUs) + shareable: bool, + }, + /// Normal Memory, where Inner and Outer cache have different settings Cacheable { - outer: CacheablePolicy, - inner: CacheablePolicy, + /// Settings for the Outer Cache + outer: CachePolicy, + /// Settings for the Inner Cache + inner: CachePolicy, + /// Is memory shareable (across multiple CPUs) shareable: bool, }, } @@ -276,13 +298,13 @@ impl MemAttr { b: false, s: false, }, - MemAttr::WriteThroughNoWriteAllocate { shareable } => MemAttrBits { + MemAttr::WriteThroughNoWriteAlloc { shareable } => MemAttrBits { tex: u3::from_u8(0b000), c: true, b: false, s: *shareable, }, - MemAttr::WriteBackNoWriteAllocate { shareable } => MemAttrBits { + MemAttr::WriteBackNoWriteAlloc { shareable } => MemAttrBits { tex: u3::from_u8(0b000), c: true, b: true, @@ -300,7 +322,7 @@ impl MemAttr { b: false, s: *shareable, }, - MemAttr::WriteBackWriteAllocate { shareable } => MemAttrBits { + MemAttr::WriteBackWriteAlloc { shareable } => MemAttrBits { tex: u3::from_u8(0b000), c: true, b: true, @@ -338,20 +360,18 @@ impl MemAttrBits { match (self.tex.value(), self.c, self.b) { (0b000, false, false) => Some(MemAttr::StronglyOrdered), (0b000, false, true) => Some(MemAttr::Device { shareable: true }), - (0b000, true, false) => { - Some(MemAttr::WriteThroughNoWriteAllocate { shareable: self.s }) - } - (0b000, true, true) => Some(MemAttr::WriteBackNoWriteAllocate { shareable: self.s }), + (0b000, true, false) => Some(MemAttr::WriteThroughNoWriteAlloc { shareable: self.s }), + (0b000, true, true) => Some(MemAttr::WriteBackNoWriteAlloc { shareable: self.s }), (0b001, false, false) => Some(MemAttr::NonCacheable { shareable: self.s }), (0b001, true, false) => Some(MemAttr::ImplementationDefined { shareable: self.s }), - (0b001, true, true) => Some(MemAttr::WriteBackWriteAllocate { shareable: self.s }), + (0b001, true, true) => Some(MemAttr::WriteBackWriteAlloc { shareable: self.s }), (0b010, false, false) => Some(MemAttr::Device { shareable: false }), (tex, c, b) if tex >= 0b100 => { let outer = tex & 0b11; let inner = ((c as u8) << 1) | (b as u8); Some(MemAttr::Cacheable { - outer: CacheablePolicy::new_with_raw_value(u2::from_u8(outer)), - inner: CacheablePolicy::new_with_raw_value(u2::from_u8(inner)), + outer: CachePolicy::new_with_raw_value(u2::from_u8(outer)), + inner: CachePolicy::new_with_raw_value(u2::from_u8(inner)), shareable: self.s, }) } @@ -363,14 +383,20 @@ impl MemAttrBits { } } -/// Describes the cache policy of a region -#[derive(Debug, PartialEq, Eq)] +/// Whether/how a region is cacheable #[bitbybit::bitenum(u2, exhaustive = true)] -pub enum CacheablePolicy { +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, PartialEq, Eq)] +pub enum CachePolicy { + /// Non-cacheable NonCacheable = 0b00, - WriteBackWriteAllocate = 0b01, - WriteThroughNoWriteAllocate = 0b10, - WriteBackNoWriteAllocate = 0b11, + /// Write-Back Cacheable, Write-Allocate + WriteBackWriteAlloc = 0b01, + /// Write-Through Cacheable + WriteThroughNoWriteAlloc = 0b10, + /// Write-Back Cacheable, no Write-Allocate + WriteBackNoWriteAlloc = 0b11, } #[cfg(test)] @@ -398,9 +424,9 @@ mod test { fn mem_attr_complex() { let mem_attr = MemAttr::Cacheable { // 0b01 - outer: CacheablePolicy::WriteBackWriteAllocate, + outer: CachePolicy::WriteBackWriteAlloc, // 0b10 - inner: CacheablePolicy::WriteThroughNoWriteAllocate, + inner: CachePolicy::WriteThroughNoWriteAlloc, shareable: true, }; let mem_attr_bits = mem_attr.to_bits(); diff --git a/aarch32-cpu/src/pmsav8.rs b/aarch32-cpu/src/pmsav8.rs index 0f82f65b..e690df2a 100644 --- a/aarch32-cpu/src/pmsav8.rs +++ b/aarch32-cpu/src/pmsav8.rs @@ -461,9 +461,9 @@ pub enum MemAttr { /// Normal memory NormalMemory { /// Controls outer access - outer: Cacheable, + outer: CachePolicy, /// Controls inner access - inner: Cacheable, + inner: CachePolicy, }, } @@ -484,23 +484,28 @@ impl MemAttr { /// Cacheability of a region #[derive(Debug, Clone, PartialEq, Eq)] -pub enum Cacheable { +pub enum CachePolicy { + /// Normal memory, Outer Write-Through Transient WriteThroughTransient(RwAllocPolicy), + /// Normal memory, Outer Write-Back Transient WriteBackTransient(RwAllocPolicy), + /// Normal memory, Outer Write-Through Non-transient WriteThroughNonTransient(RwAllocPolicy), + /// Normal memory, Outer Write-Back Non-transient WriteBackNonTransient(RwAllocPolicy), + /// Normal memory, Outer Non-cacheable NonCacheable, } -impl Cacheable { +impl CachePolicy { const fn to_bits(&self) -> u8 { #[allow(clippy::identity_op)] match self { - Cacheable::WriteThroughTransient(rw_alloc) => 0b0000 | (*rw_alloc as u8), - Cacheable::WriteBackTransient(rw_alloc) => 0b0100 | (*rw_alloc as u8), - Cacheable::WriteThroughNonTransient(rw_alloc) => 0b1000 | (*rw_alloc as u8), - Cacheable::WriteBackNonTransient(rw_alloc) => 0b1100 | (*rw_alloc as u8), - Cacheable::NonCacheable => 0b0100, + CachePolicy::WriteThroughTransient(rw_alloc) => 0b0000 | (*rw_alloc as u8), + CachePolicy::WriteBackTransient(rw_alloc) => 0b0100 | (*rw_alloc as u8), + CachePolicy::WriteThroughNonTransient(rw_alloc) => 0b1000 | (*rw_alloc as u8), + CachePolicy::WriteBackNonTransient(rw_alloc) => 0b1100 | (*rw_alloc as u8), + CachePolicy::NonCacheable => 0b0100, } } } @@ -536,8 +541,8 @@ mod test { #[test] fn mem_attr_normal() { let mem_attr = MemAttr::NormalMemory { - outer: Cacheable::NonCacheable, - inner: Cacheable::WriteBackNonTransient(RwAllocPolicy::W), + outer: CachePolicy::NonCacheable, + inner: CachePolicy::WriteBackNonTransient(RwAllocPolicy::W), }; assert_eq!( mem_attr.to_bits(), diff --git a/aarch32-cpu/src/register/armv8r/cnthctl.rs b/aarch32-cpu/src/register/armv8r/cnthctl.rs index 3bbf894c..e83992d5 100644 --- a/aarch32-cpu/src/register/armv8r/cnthctl.rs +++ b/aarch32-cpu/src/register/armv8r/cnthctl.rs @@ -8,40 +8,27 @@ use crate::register::{SysReg, SysRegRead, SysRegWrite}; #[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Cnthctl { - #[bits(19..=19, rw)] - cntpmask: bool, - #[bits(18..=18, rw)] - cntvmask: bool, - #[bits(17..=17, rw)] - evntis: bool, - #[bits(16..=16, rw)] - el1nvvct: bool, - #[bits(15..=15, rw)] - el1nvpct: bool, - #[bits(14..=14, rw)] - el1tvct: bool, - #[bits(13..=13, rw)] - el1tvt: bool, - #[bits(12..=12, rw)] - ecv: bool, - #[bits(11..=11, rw)] - el1pten: bool, - #[bits(10..=10, rw)] - el1pcten: bool, - #[bits(9..=9, rw)] - el0pten: bool, - #[bits(8..=8, rw)] - el0vten: bool, + /// Selects which bit of CNTPCT, as seen from EL2, is the trigger for the + /// event stream generated from that counter when that stream is enabled. #[bits(4..=7, rw)] evnti: u4, + /// Controls which transition of the CNTPCT trigger bit, as seen from EL2 + /// and defined by EVNTI, generates an event when the event stream is + /// enabled. #[bits(3..=3, rw)] evntdir: bool, + /// Enables the generation of an event stream from CNTPCT as seen from EL2. #[bits(2..=2, rw)] evnten: bool, + /// Traps Non-secure EL0 and EL1 MRC or MCR accesses, reported using EC + /// syndrome value 0x03, and MRRC or MCRR accesses, reported using EC + /// syndrome value 0x04, to the physical timer registers to Hyp mode. #[bits(1..=1, rw)] - el0vcten: bool, + pl1pcen: bool, + /// Traps Non-secure EL0 and EL1 MRRC or MCRR accesses, reported using EC + /// syndrome value 0x04, to the physical counter register to Hyp mode. #[bits(0..=0, rw)] - el0pcten: bool, + pl1pcten: bool, } impl SysReg for Cnthctl { @@ -58,6 +45,7 @@ impl Cnthctl { #[inline] /// Reads CNTHCTL (*Hyp Counter-timer Control Register*) pub fn read() -> Cnthctl { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/armv8r/cnthp_ctl.rs b/aarch32-cpu/src/register/armv8r/cnthp_ctl.rs index 9dd3aa9e..b0162b70 100644 --- a/aarch32-cpu/src/register/armv8r/cnthp_ctl.rs +++ b/aarch32-cpu/src/register/armv8r/cnthp_ctl.rs @@ -34,6 +34,7 @@ impl CnthpCtl { #[inline] /// Reads CNTHP_CTL (*Hyp Physical Counter-timer Control Register (EL2)*) pub fn read() -> CnthpCtl { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/armv8r/cntkctl.rs b/aarch32-cpu/src/register/armv8r/cntkctl.rs index 92db33f1..49fc9c70 100644 --- a/aarch32-cpu/src/register/armv8r/cntkctl.rs +++ b/aarch32-cpu/src/register/armv8r/cntkctl.rs @@ -68,6 +68,7 @@ impl Cntkctl { #[inline] /// Reads CNTKCTL (*Counter-timer Kernel Control Register*) pub fn read() -> Cntkctl { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/armv8r/cntp_ctl.rs b/aarch32-cpu/src/register/armv8r/cntp_ctl.rs index a61da3b9..0a78b081 100644 --- a/aarch32-cpu/src/register/armv8r/cntp_ctl.rs +++ b/aarch32-cpu/src/register/armv8r/cntp_ctl.rs @@ -44,6 +44,7 @@ impl CntpCtl { #[inline] /// Reads CNTP_CTL (*Physical Counter-timer Control Register*) pub fn read() -> CntpCtl { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/armv8r/cntv_ctl.rs b/aarch32-cpu/src/register/armv8r/cntv_ctl.rs index e7b4bc45..55bd739f 100644 --- a/aarch32-cpu/src/register/armv8r/cntv_ctl.rs +++ b/aarch32-cpu/src/register/armv8r/cntv_ctl.rs @@ -44,6 +44,7 @@ impl CntvCtl { #[inline] /// Reads CNTV_CTL (*Virtual Counter-timer Control Register*) pub fn read() -> CntvCtl { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/armv8r/hcptr.rs b/aarch32-cpu/src/register/armv8r/hcptr.rs index 864adea7..448b8b3c 100644 --- a/aarch32-cpu/src/register/armv8r/hcptr.rs +++ b/aarch32-cpu/src/register/armv8r/hcptr.rs @@ -34,6 +34,7 @@ impl Hcptr { #[inline] /// Reads HCPTR (*Hyp Architectural Feature Trap Register*) pub fn read() -> Hcptr { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } diff --git a/aarch32-cpu/src/register/armv8r/hcr.rs b/aarch32-cpu/src/register/armv8r/hcr.rs index 9424f92b..4b7b8022 100644 --- a/aarch32-cpu/src/register/armv8r/hcr.rs +++ b/aarch32-cpu/src/register/armv8r/hcr.rs @@ -98,9 +98,13 @@ pub struct Hcr { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, PartialEq, Eq)] pub enum Bsu { + /// No effect NoEffect = 0b00, + /// Inner Shareable InnerShareable = 0b01, + /// Outer Shareable OuterShareable = 0b10, + /// Full System FullSystem = 0b11, } @@ -118,6 +122,7 @@ impl Hcr { #[inline] /// Reads HCR (*Hyp Configuration Register*) pub fn read() -> Hcr { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/armv8r/hmpuir.rs b/aarch32-cpu/src/register/armv8r/hmpuir.rs index 090fc4b6..211bf932 100644 --- a/aarch32-cpu/src/register/armv8r/hmpuir.rs +++ b/aarch32-cpu/src/register/armv8r/hmpuir.rs @@ -25,6 +25,7 @@ impl Hmpuir { #[inline] /// Reads HMPUIR (*Hyp MPU Type Register*) pub fn read() -> Hmpuir { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/armv8r/hprbar.rs b/aarch32-cpu/src/register/armv8r/hprbar.rs index e313c1ef..1efae36e 100644 --- a/aarch32-cpu/src/register/armv8r/hprbar.rs +++ b/aarch32-cpu/src/register/armv8r/hprbar.rs @@ -67,6 +67,7 @@ impl Hprbar { #[inline] /// Reads HPRBAR (*Hyp Protection Region Base Address Register*) pub fn read() -> Hprbar { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/armv8r/hprlar.rs b/aarch32-cpu/src/register/armv8r/hprlar.rs index b4f4549c..67559591 100644 --- a/aarch32-cpu/src/register/armv8r/hprlar.rs +++ b/aarch32-cpu/src/register/armv8r/hprlar.rs @@ -32,6 +32,7 @@ impl Hprlar { #[inline] /// Reads HPRLAR (*Hyp Protection Region Limit Address Register*) pub fn read() -> Hprlar { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/armv8r/hsctlr.rs b/aarch32-cpu/src/register/armv8r/hsctlr.rs index b09e65e6..0e03ff4a 100644 --- a/aarch32-cpu/src/register/armv8r/hsctlr.rs +++ b/aarch32-cpu/src/register/armv8r/hsctlr.rs @@ -57,6 +57,7 @@ impl Hsctlr { #[inline] /// Reads HSCTLR (*Hyp System Control Register*) pub fn read() -> Hsctlr { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/armv8r/hsr.rs b/aarch32-cpu/src/register/armv8r/hsr.rs index b499db95..b4ff09ae 100644 --- a/aarch32-cpu/src/register/armv8r/hsr.rs +++ b/aarch32-cpu/src/register/armv8r/hsr.rs @@ -42,24 +42,43 @@ impl Hsr { #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, PartialEq, Eq)] +/// Exception Class value from the HSR (*Hyp Syndrome Register*) pub enum ExceptionClass { + /// Unknown reason Unknown = 0b00_0000, + /// Trapped WFI or WFE instruction execution TrappedWfiWfe = 0b00_0001, + /// Trapped MCR or MRC access with (coproc==`0b1111`) that is not reported using EC value `0b000000`` TrappedCp15McrMrc = 0b00_0011, + /// Trapped MCRR or MRRC access with (coproc==`0b1111`) that is not reported using EC value `0b000000`` TrappedCp15McrrMrrc = 0b00_0100, + /// Trapped MCR or MRC access with (coproc==`0b1110``) TrappedCp14McrMrc = 0b00_0101, + /// Trapped LDC or STC access TrappedLdcStc = 0b00_0110, + /// Access to Advanced SIMD or floating-point functionality trapped by a `HCPTR.{TASE, TCP10}` control TrappedFpu = 0b00_0111, + /// Trapped VMRS access, from ID group trap, that is not reported using EC value `0b000111` TrappedVmrs = 0b00_1000, + /// Trapped MRRC access with (coproc==`0b1110`) TrappedCp14McrrMrrc = 0b00_1100, + /// Illegal exception return to AArch32 state IllegalAArch32Eret = 0b00_1110, + /// Exception on SVC instruction execution in AArch32 state routed to EL2 Svc = 0b01_0001, + /// HVC instruction execution in AArch32 state, when HVC is not disabled Hvc = 0b01_0010, + /// Trapped execution of SMC instruction in AArch32 state Smc = 0b01_0011, + /// Prefetch Abort from a lower Exception level PrefetchAbortFromLower = 0b10_0000, + /// Prefetch Abort taken without a change in Exception level PrefetchAbortFromCurrent = 0b10_0001, + /// PC alignment fault exception PcAlignment = 0b10_0010, + /// Data Abort exception from a lower Exception level DataAbortFromLower = 0b10_0100, + /// Data Abort exception taken without a change in Exception level DataAbortFromCurrent = 0b10_0101, } @@ -68,27 +87,46 @@ pub enum ExceptionClass { /// ISS is a 25 bit field whose meaning varies depending on the value of the EC field. #[derive(Debug, Clone)] pub enum Iss { + /// ISS for [`ExceptionClass::Unknown`] Unknown(IssUnknown), + /// ISS for [`ExceptionClass::TrappedWfiWfe`] TrappedWfiWfe(IssTrappedWfiWfe), + /// ISS for [`ExceptionClass::TrappedCp15McrMrc`] TrappedCp15McrMrc(IssTrappedMcrMrc), + /// ISS for [`ExceptionClass::TrappedCp15McrrMrrc`] TrappedCp15McrrMrrc(IssTrappedMcrrMrrc), + /// ISS for [`ExceptionClass::TrappedCp14McrMrc`] TrappedCp14McrMrc(IssTrappedMcrMrc), + /// ISS for [`ExceptionClass::TrappedLdcStc`] TrappedLdcStc(IssTrappedLdcStc), + /// ISS for [`ExceptionClass::TrappedFpu`] TrappedFpu(IssTrappedFpu), + /// ISS for [`ExceptionClass::TrappedVmrs`] TrappedVmrs(IssTrappedVmrs), + /// ISS for [`ExceptionClass::TrappedCp14McrrMrrc`] TrappedCp14McrrMrrc(IssTrappedMcrrMrrc), + /// ISS for [`ExceptionClass::IllegalAArch32Eret`] IllegalAArch32Eret, + /// ISS for [`ExceptionClass::Svc`] Svc(IssCall), + /// ISS for [`ExceptionClass::Hvc`] Hvc(IssCall), + /// ISS for [`ExceptionClass::Smc`] Smc(IssSmc), + /// ISS for [`ExceptionClass::PrefetchAbortFromLower`] PrefetchAbortFromLower(IssPrefetchAbort), + /// ISS for [`ExceptionClass::PrefetchAbortFromCurrent`] PrefetchAbortFromCurrent(IssPrefetchAbort), + /// ISS for [`ExceptionClass::PcAlignment`] PcAlignment, + /// ISS for [`ExceptionClass::DataAbortFromLower`] DataAbortFromLower(IssDataAbort), + /// ISS for [`ExceptionClass::DataAbortFromCurrent`] DataAbortFromCurrent(IssDataAbort), } impl ExceptionClass { + /// Convert an ISS value based on the Exception Class pub fn decode_iss(&self, iss: u25) -> Iss { match self { ExceptionClass::Unknown => Iss::Unknown(IssUnknown(iss.value())), @@ -324,8 +362,11 @@ pub struct IssDataAbort { #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, PartialEq, Eq)] +/// The length of the instruction that trapped pub enum InstructionLength { + /// A 16-bit instruction SixteenBit = 0b0, + /// A 32-bit instruction ThirtyTwoBit = 0b1, } @@ -343,6 +384,7 @@ impl Hsr { #[inline] /// Reads HSR (*Hyp Syndrome Register*) pub fn read() -> Hsr { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/armv8r/hvbar.rs b/aarch32-cpu/src/register/armv8r/hvbar.rs index 13e238c5..bc076e93 100644 --- a/aarch32-cpu/src/register/armv8r/hvbar.rs +++ b/aarch32-cpu/src/register/armv8r/hvbar.rs @@ -1,4 +1,4 @@ -//! Code for HVBAR (*Hyp Vector Base Address Register*) +//! Code for managing HVBAR (*Hyp Vector Base Address Register*) use crate::register::{SysReg, SysRegRead, SysRegWrite}; diff --git a/aarch32-cpu/src/register/armv8r/prbar.rs b/aarch32-cpu/src/register/armv8r/prbar.rs index e67d7410..7d31a5af 100644 --- a/aarch32-cpu/src/register/armv8r/prbar.rs +++ b/aarch32-cpu/src/register/armv8r/prbar.rs @@ -67,6 +67,7 @@ impl Prbar { #[inline] /// Reads PRBAR (*Protection Region Base Address Register*) pub fn read() -> Prbar { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/armv8r/prlar.rs b/aarch32-cpu/src/register/armv8r/prlar.rs index f045ff84..bcf35ee7 100644 --- a/aarch32-cpu/src/register/armv8r/prlar.rs +++ b/aarch32-cpu/src/register/armv8r/prlar.rs @@ -32,6 +32,7 @@ impl Prlar { #[inline] /// Reads PRLAR (*Protection Region Limit Address Register*) pub fn read() -> Prlar { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/bpiall.rs b/aarch32-cpu/src/register/bpiall.rs index 7f0ce864..71d68659 100644 --- a/aarch32-cpu/src/register/bpiall.rs +++ b/aarch32-cpu/src/register/bpiall.rs @@ -17,6 +17,7 @@ impl crate::register::SysRegWrite for BpIAll {} impl BpIAll { #[inline] + /// Writes 0 to BPIALL (*Branch Predictor Invalidate All*) to trigger operation pub fn write() { unsafe { ::write_raw(0) } } diff --git a/aarch32-cpu/src/register/ccsidr.rs b/aarch32-cpu/src/register/ccsidr.rs index f9955761..9cfaa7a5 100644 --- a/aarch32-cpu/src/register/ccsidr.rs +++ b/aarch32-cpu/src/register/ccsidr.rs @@ -7,18 +7,25 @@ use arbitrary_int::{u10, u15, u3}; #[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Ccsidr { + /// Indicates whether the cache level supports Write-Through #[bit(31, rw)] write_through: bool, + /// Indicates whether the cache level supports Write-Back #[bit(30, rw)] write_back: bool, + /// Indicates whether the cache level supports Read-Allocation #[bit(29, rw)] read_alloc: bool, + /// Indicates whether the cache level supports Write-Allocation #[bit(28, rw)] write_alloc: bool, + /// Number of sets in cache, minus 1 #[bits(13..=27, rw)] num_sets: u15, + /// Associativity of cache, minus 1 #[bits(3..=12, rw)] associativity: u10, + /// log2(cache line size in words), minus 1 #[bits(0..=2, rw)] line_size: u3, } @@ -37,6 +44,7 @@ impl Ccsidr { #[inline] /// Reads CCSIDR (*Current Cache Size ID Register*) pub fn read() -> Ccsidr { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/csselr.rs b/aarch32-cpu/src/register/csselr.rs index eed48318..580f4f54 100644 --- a/aarch32-cpu/src/register/csselr.rs +++ b/aarch32-cpu/src/register/csselr.rs @@ -7,8 +7,11 @@ use crate::register::{SysReg, SysRegRead, SysRegWrite}; #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +/// Type of processor cache pub enum CacheType { + /// Data or Unified Cache DataOrUnified = 0, + /// Instruction Cache Instruction = 1, } @@ -20,6 +23,7 @@ pub struct Csselr { /// 0 for L1 cache, 1 for L2, etc. #[bits(1..=3, rw)] level: u3, + /// The type of cache #[bit(0, rw)] cache_type: CacheType, } @@ -38,6 +42,7 @@ impl Csselr { #[inline] /// Reads CSSELR (*Cache Size Selection Register*) pub fn read() -> Csselr { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/dacr.rs b/aarch32-cpu/src/register/dacr.rs index 1c52743e..a597d8ee 100644 --- a/aarch32-cpu/src/register/dacr.rs +++ b/aarch32-cpu/src/register/dacr.rs @@ -6,6 +6,7 @@ use crate::register::{SysReg, SysRegRead, SysRegWrite}; #[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Dacr { + /// An array of bits controlling access to each of the 16 domains #[bits(0..=1, rw)] d: [DomainAccess; 16], } @@ -40,6 +41,7 @@ impl Dacr { #[inline] /// Reads DACR (*Domain Access Control Register*) pub fn read() -> Dacr { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/dccimvac.rs b/aarch32-cpu/src/register/dccimvac.rs index 9cd5f18e..6df24fca 100644 --- a/aarch32-cpu/src/register/dccimvac.rs +++ b/aarch32-cpu/src/register/dccimvac.rs @@ -1,7 +1,8 @@ -//! Code for managing DCCIMVAC (*Clean And Invalidate Data Cache Or Unified Cache Line by MVA to Point of Coherence.*) +//! Code for managing DCCIMVAC (*Data Cache line Clean and Invalidate by VA to PoC Register*) use crate::register::{SysReg, SysRegWrite}; +/// DCCIMVAC (*Data Cache line Clean and Invalidate by VA to PoC Register*) #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -9,6 +10,7 @@ pub struct Dccimvac(pub u32); impl Dccimvac { #[inline] + /// Create a new DCCIMVAC containing the address to clean and invalidate pub const fn new(addr: u32) -> Self { Self(addr) } @@ -26,7 +28,7 @@ impl crate::register::SysRegWrite for Dccimvac {} impl Dccimvac { #[inline] - /// Writes DCCIMVAC (*Clean And Invalidate Data Cache Or Unified Cache Line by MVA to Point of Coherence.*) + /// Writes DCCIMVAC (*Data Cache line Clean and Invalidate by VA to PoC Register*) /// /// # Safety /// diff --git a/aarch32-cpu/src/register/dccisw.rs b/aarch32-cpu/src/register/dccisw.rs index e8152f24..987686a6 100644 --- a/aarch32-cpu/src/register/dccisw.rs +++ b/aarch32-cpu/src/register/dccisw.rs @@ -1,4 +1,4 @@ -//! Code for managing DCCISW (*Clean and Invalidate Data or Unified cache line by Set/Way.*) +//! Code for managing DCCISW (*Data Cache line Clean and Invalidate by Set/Way Register*) use arbitrary_int::u3; @@ -7,6 +7,7 @@ use crate::register::{SysReg, SysRegWrite}; #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +/// DCCISW (*Data Cache line Clean and Invalidate by Set/Way Register*) pub struct Dccisw(pub u32); impl Dccisw { @@ -52,7 +53,7 @@ impl crate::register::SysRegWrite for Dccisw {} impl Dccisw { #[inline] - /// Writes DCCISW (*Clean and Invalidate data or unified cache line by set/way.*) + /// Writes DCCISW (*Data Cache line Clean and Invalidate by Set/Way Register*) /// /// # Safety /// diff --git a/aarch32-cpu/src/register/dccmvac.rs b/aarch32-cpu/src/register/dccmvac.rs index abf7ad27..b24bb507 100644 --- a/aarch32-cpu/src/register/dccmvac.rs +++ b/aarch32-cpu/src/register/dccmvac.rs @@ -1,14 +1,16 @@ -//! Code for managing DCCMVAC (*Clean Data Cache Or Unified Cache Line by MVA to Point of Coherence.*) +//! Code for managing DCCMVAC (*Data Cache line Clean by VA to PoC Register*) use crate::register::{SysReg, SysRegWrite}; #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +/// DCCMVAC (*Data Cache line Clean by VA to PoC Register*) pub struct Dccmvac(pub u32); impl Dccmvac { #[inline] + /// Create a new DCCMVAC containing the Virtual Address to clean pub const fn new(addr: u32) -> Self { Self(addr) } @@ -26,7 +28,7 @@ impl crate::register::SysRegWrite for Dccmvac {} impl Dccmvac { #[inline] - /// Writes DCCMVAC (*Clean Data Cache Or Unified Cache Line by MVA to Point of Coherence.*) + /// Writes DCCMVAC (*Data Cache line Clean by VA to PoC Register*) /// /// # Safety /// diff --git a/aarch32-cpu/src/register/dccmvau.rs b/aarch32-cpu/src/register/dccmvau.rs index 98fdc70e..8c1f3ecb 100644 --- a/aarch32-cpu/src/register/dccmvau.rs +++ b/aarch32-cpu/src/register/dccmvau.rs @@ -1,14 +1,16 @@ -//! Code for managing DCCMVAU (*Clean Data Cache Or Unified Cache Line by MVA to Point of Unification.*) +//! Code for managing DCCMVAU (*Data Cache line Clean by VA to PoU Register*) use crate::register::{SysReg, SysRegWrite}; #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +/// DCCMVAU (*Data Cache line Clean by VA to PoU Register*) pub struct Dccmvau(pub u32); impl Dccmvau { #[inline] + /// Create a new DCCMVAU value containing the Virtual Address to clean pub const fn new(addr: u32) -> Self { Self(addr) } @@ -26,7 +28,7 @@ impl crate::register::SysRegWrite for Dccmvau {} impl Dccmvau { #[inline] - /// Writes DCCMVAU (*Clean Data Cache Or Unified Cache Line by MVA to Point of Unification.*) + /// Writes DCCMVAU (*Data Cache line Clean by VA to PoU Register*) /// /// # Safety /// diff --git a/aarch32-cpu/src/register/dccsw.rs b/aarch32-cpu/src/register/dccsw.rs index 77f58b9e..40817a1e 100644 --- a/aarch32-cpu/src/register/dccsw.rs +++ b/aarch32-cpu/src/register/dccsw.rs @@ -1,4 +1,4 @@ -//! Code for managing DCCSW (*Clean Data or Unified Cache line by Set/Way.*) +//! Code for managing DCCSW (*Data Cache line Clean by Set/Way Register*) use arbitrary_int::u3; @@ -7,6 +7,7 @@ use crate::register::{SysReg, SysRegWrite}; #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +/// DCCSW (*Data Cache line Clean by Set/Way Register*) pub struct Dccsw(pub u32); impl Dccsw { @@ -52,7 +53,7 @@ impl crate::register::SysRegWrite for Dccsw {} impl Dccsw { #[inline] - /// Writes DCCSW (*Clean Data or Unified Cache line by Set/Way.*) + /// Writes DCCSW (*Data Cache line Clean by Set/Way Register*) /// /// # Safety /// diff --git a/aarch32-cpu/src/register/dcimvac.rs b/aarch32-cpu/src/register/dcimvac.rs index 9848585a..a86f4080 100644 --- a/aarch32-cpu/src/register/dcimvac.rs +++ b/aarch32-cpu/src/register/dcimvac.rs @@ -1,14 +1,16 @@ -//! Code for managing DCIMVAC (*Invalidate Data Cache Or Unified Cache Line by MVA to Point of Coherence.*) +//! Code for managing DCIMVAC (*Data Cache line Invalidate by VA to PoC Register*) use crate::register::{SysReg, SysRegWrite}; #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +/// DCIMVAC (*Data Cache line Invalidate by VA to PoC Register*) pub struct Dcimvac(pub u32); impl Dcimvac { #[inline] + /// Create a new DCIMVAC value, given an address pub const fn new(addr: u32) -> Self { Self(addr) } @@ -26,7 +28,7 @@ impl crate::register::SysRegWrite for Dcimvac {} impl Dcimvac { #[inline] - /// Writes DCIMVAC (*Invalidate Data Cache Or Unified Cache Line by MVA to Point of Coherence.*) + /// Writes DCIMVAC (*Data Cache line Invalidate by VA to PoC Register*) /// /// # Safety /// diff --git a/aarch32-cpu/src/register/dcisw.rs b/aarch32-cpu/src/register/dcisw.rs index 444b79d4..1cfbd2f6 100644 --- a/aarch32-cpu/src/register/dcisw.rs +++ b/aarch32-cpu/src/register/dcisw.rs @@ -1,4 +1,4 @@ -//! Code for managing DCISW (*Invalidate Data or Unified Cache line by Set/Way.*) +//! Code for managing DCISW (*Data Cache line Invalidate by Set/Way Register*) use arbitrary_int::u3; @@ -7,6 +7,7 @@ use crate::register::{SysReg, SysRegWrite}; #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +/// DCISW (*Data Cache line Invalidate by Set/Way Register*) pub struct Dcisw(pub u32); impl Dcisw { @@ -52,7 +53,7 @@ impl crate::register::SysRegWrite for Dcisw {} impl Dcisw { #[inline] - /// Writes DCSW (*Invalidate Data or Unified Cache line by Set/Way.*) + /// Writes DCSW (*Data Cache line Invalidate by Set/Way Register*) /// /// # Safety /// diff --git a/aarch32-cpu/src/register/dfsr.rs b/aarch32-cpu/src/register/dfsr.rs index fe132db2..6742388c 100644 --- a/aarch32-cpu/src/register/dfsr.rs +++ b/aarch32-cpu/src/register/dfsr.rs @@ -92,18 +92,31 @@ pub struct Dfsr { #[derive(Debug, PartialEq, Eq)] #[repr(u8)] pub enum DfsrStatus { + /// Alignment fault AlignmentFault = 1, + /// Debug Exception Debug = 2, + /// Alternate value for Alignment fault AlignmentAlt = 3, + /// Translation fault, level 1 TranslationFaultFirstLevel = 5, + /// Translation fault, level 2 TranslationFaultSecondLevel = 7, + /// Synchronous External Abort SyncExtAbort = 8, + /// Domain fault, level 1 DomainFaultFirstLevel = 9, + /// Alternate value for Synchronous External Abort SyncExtAbortAlt = 10, + /// Domain fault, level 2 DomainFaultSecondLevel = 11, + /// Synchronous External abort, on translation table walk, level 1 SyncExtAbortOnTranslationTableWalkFirstLevel = 12, + /// Permission fault, level 1 PermissionFaultFirstLevel = 13, + /// Synchronous External abort, on translation table walk, level 2 SyncExtAbortOnTranslationTableWalkSecondLevel = 14, + /// Permission fault, level 2 PermissionFaultSecondLevel = 15, } @@ -185,11 +198,11 @@ pub enum DfsrStatus { AlignmentFault = 0b00001, /// Debug exception. Debug = 0b00010, - /// Access flag fault, level 1. + /// Access flag fault, level 1 AccessFlagFaultFirstLevel = 0b00011, /// Fault on instruction cache maintenance. CacheMaintenance = 0b00100, - /// Translation fault, level 1. + /// Translation fault, level 1 TranslationFaultFirstLevel = 0b00101, /// Access flag fault, level 2. AccessFlagFaultSecondLevel = 0b00110, @@ -197,13 +210,13 @@ pub enum DfsrStatus { TranslationFaultSecondLevel = 0b00111, /// Synchronous External abort, not on translation table walk. SyncExtAbort = 0b01000, - /// Domain fault, level 1. + /// Domain fault, level 1 DomainFaultFirstLevel = 0b01001, /// Domain fault, level 2. DomainFaultSecondLevel = 0b01011, - /// Synchronous External abort, on translation table walk, level 1. + /// Synchronous External abort, on translation table walk, level 1 SyncExtAbortOnTranslationTableWalkFirstLevel = 0b01100, - /// Permission fault, level 1. + /// Permission fault, level 1 PermissionFaultFirstLevel = 0b01101, /// Synchronous External abort, on translation table walk, level 2. SyncExtAbortOnTranslationTableWalkSecondLevel = 0b01110, @@ -217,7 +230,7 @@ pub enum DfsrStatus { SErrorParityEccError = 0b11000, /// Synchronous parity or ECC error on memory access, not on translation table walk. SyncParErrorOnMemAccess = 0b11001, - /// Synchronous parity or ECC error on translation table walk, level 1. + /// Synchronous parity or ECC error on translation table walk, level 1 SyncParErrorOnTranslationTableWalkFirstLevel = 0b11100, /// Synchronous parity or ECC error on translation table walk, level 2. SyncParErrorOnTranslationTableWalkSecondLevel = 0b11110, @@ -263,6 +276,7 @@ impl Dfsr { #[inline] /// Reads DFSR (*Data Fault Status Register*) pub fn read() -> Dfsr { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/dracr.rs b/aarch32-cpu/src/register/dracr.rs index 5edd99cc..94f01649 100644 --- a/aarch32-cpu/src/register/dracr.rs +++ b/aarch32-cpu/src/register/dracr.rs @@ -44,6 +44,7 @@ impl Dracr { /// /// Set RGNR to control which region this reads. pub fn read() -> Dracr { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/drsr.rs b/aarch32-cpu/src/register/drsr.rs index b0d893c8..b99cb605 100644 --- a/aarch32-cpu/src/register/drsr.rs +++ b/aarch32-cpu/src/register/drsr.rs @@ -77,6 +77,9 @@ pub enum RegionSize { } impl RegionSize { + /// Check address alignment + /// + /// Reports whether an address is aligned according to this region size pub fn is_aligned(&self, addr: *const u8) -> bool { let addr = addr as usize; if *self == RegionSize::_4G { @@ -127,6 +130,7 @@ impl Drsr { /// /// Set RGNR to control which region this reads. pub fn read() -> Drsr { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/iciallu.rs b/aarch32-cpu/src/register/iciallu.rs index 589bd949..a0c1d552 100644 --- a/aarch32-cpu/src/register/iciallu.rs +++ b/aarch32-cpu/src/register/iciallu.rs @@ -1,4 +1,4 @@ -//! Code for managing ICIALLU (*Invalidate all instruction caches to PoU.*) +//! Code for managing ICIALLU (*Invalidate entire instruction cache to PoU Register*) //! //! Starting with ARMv6, the type of cache can be determined from the System Coprocessor register 0, //! and controlled through registers 1, 7 and 9. In earlier architecture variants, it is @@ -8,6 +8,7 @@ use crate::register::{SysReg, SysRegWrite}; #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +/// ICIALLU (*Invalidate entire instruction cache to PoU Register*) pub struct Iciallu; impl SysReg for Iciallu { @@ -22,7 +23,7 @@ impl crate::register::SysRegWrite for Iciallu {} impl Iciallu { #[inline] - /// Writes ICIALLU (*Invalidate all instruction caches to PoU.*) + /// Writes 0 to ICIALLU (*Invalidate entire instruction cache to PoU Register*) to trigger operation pub fn write() { unsafe { ::write_raw(0); diff --git a/aarch32-cpu/src/register/ifsr.rs b/aarch32-cpu/src/register/ifsr.rs index 00efa8d7..4a0232f8 100644 --- a/aarch32-cpu/src/register/ifsr.rs +++ b/aarch32-cpu/src/register/ifsr.rs @@ -86,18 +86,31 @@ pub struct Ifsr { #[derive(Debug, PartialEq, Eq)] #[cfg(arm_architecture = "v5te")] pub enum IfsrStatus { + /// PC Alignment Fault Alignment = 1, + /// Debug Exception DebugEvent = 2, + /// Alternate value for PC Alignment Fault AlignmentAlt = 3, + /// Translation fault, level 1 TranslationFaultFirstLevel = 5, + /// Translation fault, level 2 TranslationFaultSecondLevel = 7, + /// Synchronous External abort SyncExtAbort = 8, + /// Domain fault, level 1 DomainFaultFirstLevel = 9, + /// Alternate value for Synchronous External abort SyncExtAbortAlt = 10, + /// Domain fault, level 2 DomainFaultSecondLevel = 11, + /// Synchronous External abort, on translation table walk, level 1 SyncExtAbortOnTranslationTableWalkFirstLevel = 12, + /// Permission fault, level 1 PermissionFaultFirstLevel = 13, + /// Synchronous External abort, on translation table walk, level 2 SyncExtAbortOnTranslationTableWalkSecondLevel = 14, + /// Permission fault, level 2 PermissionFaultSecondLevel = 15, } @@ -108,18 +121,31 @@ pub enum IfsrStatus { #[derive(Debug, PartialEq, Eq)] #[cfg(arm_architecture = "v6")] pub enum IfsrStatus { + /// PC Alignment Fault Alignment = 1, + /// Debug Exception DebugEvent = 2, + /// Access Flag fault, level 1 AccessFlagFaultFirstLevel = 3, + /// Translation fault, level 1 TranslationFaultFirstLevel = 5, + /// Access Flag fault, level 2 AccessFlagFaultSecondLevel = 6, + /// Translation fault, level 2 TranslationFaultSecondLevel = 7, + /// Synchronouse External Abort SyncExtAbort = 8, + /// Domain fault, level 1 DomainFaultFirstLevel = 9, + /// Domain fault, level 2 DomainFaultSecondLevel = 11, + /// Synchronous External abort, on translation table walk, level 1 SyncExtAbortOnTranslationTableWalkFirstLevel = 12, + /// Permission fault, level 1 PermissionFaultFirstLevel = 13, + /// Synchronous External abort, on translation table walk, level 2 SyncExtAbortOnTranslationTableWalkSecondLevel = 14, + /// Permission fault, level 2 PermissionFaultSecondLevel = 15, } @@ -130,12 +156,19 @@ pub enum IfsrStatus { #[derive(Debug, PartialEq, Eq)] #[cfg(arm_architecture = "v7-r")] pub enum IfsrStatus { + /// PC Alignment Fault Alignment = 1, + /// Debug Exception DebugEvent = 2, + /// Synchronous External abort SyncExtAbort = 8, + /// Permission fault, level 1 PermissionFaultFirstLevel = 13, + /// Asynchronous External abort AsyncExtAbort = 21, + /// Synchronous parity or ECC error SyncParityEccError = 25, + /// asynchronous parity or ECC error AsyncParityEccError = 24, } @@ -146,23 +179,41 @@ pub enum IfsrStatus { #[derive(Debug, PartialEq, Eq)] #[cfg(arm_architecture = "v7-a")] pub enum IfsrStatus { + /// Synchronous External abort, on translation table walk, level 1 SyncExtAbortOnTranslationTableWalkFirstLevel = 0b01100, + /// Synchronous External abort, on translation table walk, level 2 SyncExtAbortOnTranslationTableWalkSecondLevel = 0b01110, + /// Synchronous parity or ECC error on memory access, on translation table walk, level 1 SyncParErrorOnTranslationTableWalkFirstLevel = 0b11100, + /// Synchronous parity or ECC error on memory access, on translation table walk, level 2 SyncParErrorOnTranslationTableWalkSecondLevel = 0b11110, + /// Translation fault, level 1 TranslationFaultFirstLevel = 0b00101, + /// Translation fault, level 2 TranslationFaultSecondLevel = 0b00111, + /// Access flag fault, level 1 AccessFlagFaultFirstLevel = 0b00011, + /// Access flag fault, level 2 AccessFlagFaultSecondLevel = 0b00110, + /// Domain fault, level 1 DomainFaultFirstLevel = 0b01001, + /// Domain fault, level 2 DomainFaultSecondLevel = 0b01011, + /// Permission fault, level 1 PermissionFaultFirstLevel = 0b01101, + /// Permission fault, level 2 PermissionFaultSecondLevel = 0b01111, + /// Debug exception DebugEvent = 0b00010, + /// Synchronous External abort SyncExtAbort = 0b01000, + /// TLB conflict abort TlbConflictAbort = 0b10000, + /// IMPLEMENTATION DEFINED fault (Lockdown fault) Lockdown = 0b10100, + /// Co-Processor Abort CoprocessorAbort = 0b11010, + /// Synchronous parity or ECC error on memory access, not on translation table walk SyncParErrorOnMemAccess = 0b11001, } @@ -173,11 +224,17 @@ pub enum IfsrStatus { #[derive(Debug, PartialEq, Eq)] #[cfg(arm_architecture = "v8-r")] pub enum IfsrStatus { + /// Translation fault Translation = 4, + /// Permission fault Permission = 12, + /// Synchronous External abort SyncExtAbort = 16, + /// Synchronous parity or ECC error on memory access SyncParityEccError = 24, + /// PC alignment fault PcAlignment = 33, + /// Debug exception Debug = 34, } @@ -195,6 +252,7 @@ impl Ifsr { #[inline] /// Reads IFSR (*Instruction Fault Status Register*) pub fn read() -> Ifsr { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/imp/imp_cdbgdcd.rs b/aarch32-cpu/src/register/imp/imp_cdbgdcd.rs index 9abc129a..fbbb8e15 100644 --- a/aarch32-cpu/src/register/imp/imp_cdbgdcd.rs +++ b/aarch32-cpu/src/register/imp/imp_cdbgdcd.rs @@ -1,8 +1,8 @@ -//! Code for managing IMP_CDBGDCD (*Data Cache Data Read Operation.*) +//! Code for managing IMP_CDBGDCD (*Data Cache Data Read Operation*) use crate::register::{SysReg, SysRegWrite}; -/// IMP_CDBGDCD (*Data Cache Data Read Operation.*) +/// IMP_CDBGDCD (*Data Cache Data Read Operation*) #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -20,7 +20,7 @@ impl crate::register::SysRegWrite for ImpCdbgdcd {} impl ImpCdbgdcd { #[inline] - /// Writes IMP_CDBGDCD (*Data Cache Data Read Operation.*) + /// Writes IMP_CDBGDCD (*Data Cache Data Read Operation*) /// /// # Safety /// diff --git a/aarch32-cpu/src/register/imp/imp_cdbgdct.rs b/aarch32-cpu/src/register/imp/imp_cdbgdct.rs index 00194b37..5933a5ae 100644 --- a/aarch32-cpu/src/register/imp/imp_cdbgdct.rs +++ b/aarch32-cpu/src/register/imp/imp_cdbgdct.rs @@ -1,8 +1,8 @@ -//! Code for managing IMP_CDBGDCT (*Data Cache Tag Read Operation.*) +//! Code for managing IMP_CDBGDCT (*Data Cache Tag Read Operation*) use crate::register::{SysReg, SysRegWrite}; -/// IMP_CDBGDCT (*Data Cache Tag Read Operation.*) +/// IMP_CDBGDCT (*Data Cache Tag Read Operation*) #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -20,7 +20,7 @@ impl crate::register::SysRegWrite for ImpCdbgdct {} impl ImpCdbgdct { #[inline] - /// Writes IMP_CDBGDCT (*Data Cache Tag Read Operation.*) + /// Writes IMP_CDBGDCT (*Data Cache Tag Read Operation*) /// /// # Safety /// diff --git a/aarch32-cpu/src/register/imp/imp_cdbgdr0.rs b/aarch32-cpu/src/register/imp/imp_cdbgdr0.rs index 1d3e87b7..a1414030 100644 --- a/aarch32-cpu/src/register/imp/imp_cdbgdr0.rs +++ b/aarch32-cpu/src/register/imp/imp_cdbgdr0.rs @@ -1,8 +1,8 @@ -//! Code for managing IMP_CDBGDR0 (*Cache Debug Data Register 0.*) +//! Code for managing IMP_CDBGDR0 (*Cache Debug Data Register 0*) use crate::register::{SysReg, SysRegRead}; -/// IMP_CDBGDR0 (*Cache Debug Data Register 0.*) +/// IMP_CDBGDR0 (*Cache Debug Data Register 0*) #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -20,7 +20,7 @@ impl crate::register::SysRegRead for ImpCdbgdr0 {} impl ImpCdbgdr0 { #[inline] - /// Reads IMP_CDBGDR0 (*Cache Debug Data Register 0.*) + /// Reads IMP_CDBGDR0 (*Cache Debug Data Register 0*) pub fn read() -> ImpCdbgdr0 { unsafe { Self(::read_raw()) } } diff --git a/aarch32-cpu/src/register/imp/imp_cdbgdr1.rs b/aarch32-cpu/src/register/imp/imp_cdbgdr1.rs index 128919dd..3c6d9c2e 100644 --- a/aarch32-cpu/src/register/imp/imp_cdbgdr1.rs +++ b/aarch32-cpu/src/register/imp/imp_cdbgdr1.rs @@ -1,8 +1,8 @@ -//! Code for managing IMP_CDBGDR1 (*Cache Debug Data Register 1.*) +//! Code for managing IMP_CDBGDR1 (*Cache Debug Data Register 1*) use crate::register::{SysReg, SysRegRead}; -/// IMP_CDBGDR1 (*Cache Debug Data Register 1.*) +/// IMP_CDBGDR1 (*Cache Debug Data Register 1*) #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -20,7 +20,7 @@ impl crate::register::SysRegRead for ImpCdbgdr1 {} impl ImpCdbgdr1 { #[inline] - /// Reads IMP_CDBGDR1 (*Cache Debug Data Register 1.*) + /// Reads IMP_CDBGDR1 (*Cache Debug Data Register 1*) pub fn read() -> ImpCdbgdr1 { unsafe { Self(::read_raw()) } } diff --git a/aarch32-cpu/src/register/imp/imp_cdbgdr2.rs b/aarch32-cpu/src/register/imp/imp_cdbgdr2.rs index 4fc3e103..ef109e1f 100644 --- a/aarch32-cpu/src/register/imp/imp_cdbgdr2.rs +++ b/aarch32-cpu/src/register/imp/imp_cdbgdr2.rs @@ -1,8 +1,8 @@ -//! Code for managing IMP_CDBGDR2 (*Cache Debug Data Register 2.*) +//! Code for managing IMP_CDBGDR2 (*Cache Debug Data Register 2*) use crate::register::{SysReg, SysRegRead}; -/// IMP_CDBGDR2 (*Cache Debug Data Register 2.*) +/// IMP_CDBGDR2 (*Cache Debug Data Register 2*) #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -20,7 +20,7 @@ impl crate::register::SysRegRead for ImpCdbgdr2 {} impl ImpCdbgdr2 { #[inline] - /// Reads IMP_CDBGDR2 (*Cache Debug Data Register 2.*) + /// Reads IMP_CDBGDR2 (*Cache Debug Data Register 2*) pub fn read() -> ImpCdbgdr2 { unsafe { Self(::read_raw()) } } diff --git a/aarch32-cpu/src/register/imp/imp_cdbgicd.rs b/aarch32-cpu/src/register/imp/imp_cdbgicd.rs index 9847be2b..64d5705f 100644 --- a/aarch32-cpu/src/register/imp/imp_cdbgicd.rs +++ b/aarch32-cpu/src/register/imp/imp_cdbgicd.rs @@ -1,8 +1,8 @@ -//! Code for managing IMP_CDBGICD (*Instruction Cache Data Read Operation.*) +//! Code for managing IMP_CDBGICD (*Instruction Cache Data Read Operation*) use crate::register::{SysReg, SysRegWrite}; -/// IMP_CDBGICD (*Instruction Cache Data Read Operation.*) +/// IMP_CDBGICD (*Instruction Cache Data Read Operation*) #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -20,7 +20,7 @@ impl crate::register::SysRegWrite for ImpCdbgicd {} impl ImpCdbgicd { #[inline] - /// Writes IMP_CDBGICD (*Instruction Cache Data Read Operation.*) + /// Writes IMP_CDBGICD (*Instruction Cache Data Read Operation*) /// /// # Safety /// diff --git a/aarch32-cpu/src/register/imp/imp_cdbgict.rs b/aarch32-cpu/src/register/imp/imp_cdbgict.rs index cab5cec1..f6c847b4 100644 --- a/aarch32-cpu/src/register/imp/imp_cdbgict.rs +++ b/aarch32-cpu/src/register/imp/imp_cdbgict.rs @@ -1,8 +1,8 @@ -//! Code for managing IMP_CDBGICT (*Instruction Cache Tag Read Operation.*) +//! Code for managing IMP_CDBGICT (*Instruction Cache Tag Read Operation*) use crate::register::{SysReg, SysRegWrite}; -/// IMP_CDBGICT (*Instruction Cache Tag Read Operation.*) +/// IMP_CDBGICT (*Instruction Cache Tag Read Operation*) #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -20,7 +20,7 @@ impl crate::register::SysRegWrite for ImpCdbgict {} impl ImpCdbgict { #[inline] - /// Writes IMP_CDBGICT (*Instruction Cache Tag Read Operation.*) + /// Writes IMP_CDBGICT (*Instruction Cache Tag Read Operation*) /// /// # Safety /// diff --git a/aarch32-cpu/src/register/iracr.rs b/aarch32-cpu/src/register/iracr.rs index ae640f09..4d427c1d 100644 --- a/aarch32-cpu/src/register/iracr.rs +++ b/aarch32-cpu/src/register/iracr.rs @@ -44,6 +44,7 @@ impl Iracr { /// /// Set RGNR to control which region this reads. pub fn read() -> Iracr { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/irsr.rs b/aarch32-cpu/src/register/irsr.rs index 5e249923..1552c05f 100644 --- a/aarch32-cpu/src/register/irsr.rs +++ b/aarch32-cpu/src/register/irsr.rs @@ -42,6 +42,7 @@ impl Irsr { /// /// Set RGNR to control which region this reads. pub fn read() -> Irsr { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/mod.rs b/aarch32-cpu/src/register/mod.rs index 39abab06..c873066d 100644 --- a/aarch32-cpu/src/register/mod.rs +++ b/aarch32-cpu/src/register/mod.rs @@ -1,4 +1,6 @@ -//! Access registers in Armv7-R and Armv8-R +//! Defines various AArch32 system registers +//! +//! These are all ready using Co-Processor read/write instructions pub mod actlr; pub mod actlr2; @@ -204,9 +206,10 @@ pub use vmpidr::Vmpidr; pub use vpidr::Vpidr; pub use vsctlr::Vsctlr; -#[cfg(any(test, doc, arm_architecture = "v8-r"))] -pub mod armv8r; -#[cfg(any(test, doc, arm_architecture = "v8-r"))] +#[cfg(any(test, arm_architecture = "v8-r"))] +mod armv8r; +#[cfg(any(test, arm_architecture = "v8-r"))] +#[doc(inline)] pub use armv8r::*; pub use imp::*; diff --git a/aarch32-cpu/src/register/mpuir.rs b/aarch32-cpu/src/register/mpuir.rs index f28df795..25a652e2 100644 --- a/aarch32-cpu/src/register/mpuir.rs +++ b/aarch32-cpu/src/register/mpuir.rs @@ -33,6 +33,7 @@ impl Mpuir { #[inline] /// Reads MPUIR (*MPU Type Register*) pub fn read() -> Mpuir { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } } diff --git a/aarch32-cpu/src/register/sctlr.rs b/aarch32-cpu/src/register/sctlr.rs index 486857a1..f52d4750 100644 --- a/aarch32-cpu/src/register/sctlr.rs +++ b/aarch32-cpu/src/register/sctlr.rs @@ -72,6 +72,7 @@ impl Sctlr { /// Read SCTLR (*System Control Register*) #[inline] pub fn read() -> Self { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } diff --git a/aarch32-cpu/src/register/tlbiall.rs b/aarch32-cpu/src/register/tlbiall.rs index e6f664eb..c5298367 100644 --- a/aarch32-cpu/src/register/tlbiall.rs +++ b/aarch32-cpu/src/register/tlbiall.rs @@ -17,6 +17,7 @@ impl crate::register::SysRegWrite for TlbIAll {} impl TlbIAll { #[inline] + /// Writes 0 to TLBIALL (*TLB Invalidate All Register*) to trigger operation pub fn write() { unsafe { ::write_raw(0) } } diff --git a/aarch32-cpu/src/register/ttbr0.rs b/aarch32-cpu/src/register/ttbr0.rs index ea9c8dd2..4fb9a523 100644 --- a/aarch32-cpu/src/register/ttbr0.rs +++ b/aarch32-cpu/src/register/ttbr0.rs @@ -87,11 +87,11 @@ pub enum Region { /// Normal memory, Outer Non-cacheable NonCacheable = 0b00, /// Normal memory, Outer Write-Back Write-Allocate Cacheable - WriteBackWriteAllocateCacheable = 0b01, + WriteBackWriteAllocCacheable = 0b01, /// Normal memory, Outer Write-Through Cacheable WriteThroughCacheable = 0b10, /// Normal memory, Outer Write-Back no Write-Allocate Cacheable - WriteBackNoWriteAllocateCacheable = 0b11, + WriteBackNoWriteAllocCacheable = 0b11, } impl SysReg for Ttbr0 { @@ -110,6 +110,7 @@ impl Ttbr0 { #[inline] /// Reads TTBR0 (*Translation Table Base Register 0*) pub fn read() -> Ttbr0 { + // Safety: it's OK to set bits with no accessors specified unsafe { Self::new_with_raw_value(::read_raw()) } } diff --git a/aarch32-cpu/src/stacks.rs b/aarch32-cpu/src/stacks.rs index 161902f7..20ee7a2a 100644 --- a/aarch32-cpu/src/stacks.rs +++ b/aarch32-cpu/src/stacks.rs @@ -33,30 +33,32 @@ pub unsafe fn stack_used_bytes(stack: core::ops::Range<*const u32>) -> (usize, u /// of at least `size` words in length. unsafe fn stack_unused_bytes_asm(start: *const u32, size: usize) -> usize { let result: usize; - core::arch::asm!( - r#" - // skip out if size is zero - movs {result}, #0 - cmp {size}, #0 - beq 3f -2: // loop - ldr {scratch}, [{start}] - cmp {scratch}, #0 - // break out if value is non-zero - bne 3f - // otherwise increment counter - adds {result}, {result}, #1 - adds {start}, {start}, #4 - // loop if not finished yet - cmp {result}, {size} - bne 2b - // all finished -3: - "#, - size = in(reg) size, - start = inout(reg) start => _, - result = out(reg) result, - scratch = out(reg) _, - ); + unsafe { + core::arch::asm!( + r#" + // skip out if size is zero + movs {result}, #0 + cmp {size}, #0 + beq 3f + 2: // loop + ldr {scratch}, [{start}] + cmp {scratch}, #0 + // break out if value is non-zero + bne 3f + // otherwise increment counter + adds {result}, {result}, #1 + adds {start}, {start}, #4 + // loop if not finished yet + cmp {result}, {size} + bne 2b + // all finished + 3: + "#, + size = in(reg) size, + start = inout(reg) start => _, + result = out(reg) result, + scratch = out(reg) _, + ); + } result } diff --git a/examples/mps3-an536-el2/src/bin/generic-timer.rs b/examples/mps3-an536-el2/src/bin/generic-timer.rs index 102a3220..3a20c547 100644 --- a/examples/mps3-an536-el2/src/bin/generic-timer.rs +++ b/examples/mps3-an536-el2/src/bin/generic-timer.rs @@ -44,7 +44,7 @@ fn main() -> ! { hyp_timer.countdown_set(hyp_timer.frequency_hz() / 5); hyp_timer.enable(true); // used in interrupt handler - drop(hyp_timer); + let _ = hyp_timer; println!("Enabling interrupts..."); dump_sctlr(); @@ -111,5 +111,5 @@ fn hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 { hsr.get_iss(), frame ); - return frame.r0; + frame.r0 } diff --git a/examples/mps3-an536-el2/src/bin/hvc-a32.rs b/examples/mps3-an536-el2/src/bin/hvc-a32.rs index 8d12237b..5bb2aa89 100644 --- a/examples/mps3-an536-el2/src/bin/hvc-a32.rs +++ b/examples/mps3-an536-el2/src/bin/hvc-a32.rs @@ -34,7 +34,7 @@ fn hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 { if hsr.iss().value() == 0xABCD { do_hvc2(); } - return 0x12345678; + 0x12345678 } #[instruction_set(arm::a32)] diff --git a/examples/mps3-an536-el2/src/bin/hvc-t32.rs b/examples/mps3-an536-el2/src/bin/hvc-t32.rs index 98ad5808..9b63655c 100644 --- a/examples/mps3-an536-el2/src/bin/hvc-t32.rs +++ b/examples/mps3-an536-el2/src/bin/hvc-t32.rs @@ -34,7 +34,7 @@ fn hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 { if hsr.iss().value() == 0xABCD { do_hvc2(); } - return 0x12345678; + 0x12345678 } #[instruction_set(arm::t32)] diff --git a/examples/mps3-an536-el2/src/bin/svc-a32.rs b/examples/mps3-an536-el2/src/bin/svc-a32.rs index e131b768..02adc0a3 100644 --- a/examples/mps3-an536-el2/src/bin/svc-a32.rs +++ b/examples/mps3-an536-el2/src/bin/svc-a32.rs @@ -34,7 +34,7 @@ fn hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 { if hsr.iss().value() == 0xABCD { do_svc2(); } - return 0x12345678; + 0x12345678 } #[instruction_set(arm::a32)] diff --git a/examples/mps3-an536-el2/src/bin/svc-t32.rs b/examples/mps3-an536-el2/src/bin/svc-t32.rs index 6957aea0..e542a727 100644 --- a/examples/mps3-an536-el2/src/bin/svc-t32.rs +++ b/examples/mps3-an536-el2/src/bin/svc-t32.rs @@ -34,7 +34,7 @@ fn hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 { if hsr.iss().value() == 0x12 { do_svc2(); } - return 0x12345678; + 0x12345678 } #[instruction_set(arm::t32)] diff --git a/examples/mps3-an536/src/bin/hvc-a32.rs b/examples/mps3-an536/src/bin/hvc-a32.rs index 2e48231b..7dad76a4 100644 --- a/examples/mps3-an536/src/bin/hvc-a32.rs +++ b/examples/mps3-an536/src/bin/hvc-a32.rs @@ -27,7 +27,7 @@ fn main() -> ! { fn hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 { let hsr = aarch32_cpu::register::Hsr::new_with_raw_value(hsr); println!("In hvc_handler, with {:08x?}, {:08x?}", hsr, frame); - return 0x12345678; + 0x12345678 } #[instruction_set(arm::a32)] diff --git a/examples/mps3-an536/src/bin/hvc-t32.rs b/examples/mps3-an536/src/bin/hvc-t32.rs index 4d0ce587..631bde18 100644 --- a/examples/mps3-an536/src/bin/hvc-t32.rs +++ b/examples/mps3-an536/src/bin/hvc-t32.rs @@ -27,7 +27,7 @@ fn main() -> ! { fn hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 { let hsr = aarch32_cpu::register::Hsr::new_with_raw_value(hsr); println!("In hvc_handler, with {:08x?}, {:08x?}", hsr, frame); - return 0x12345678; + 0x12345678 } #[instruction_set(arm::t32)] diff --git a/examples/mps3-an536/src/bin/registers.rs b/examples/mps3-an536/src/bin/registers.rs index 2522b0bf..71d39da4 100644 --- a/examples/mps3-an536/src/bin/registers.rs +++ b/examples/mps3-an536/src/bin/registers.rs @@ -36,7 +36,7 @@ fn chip_info() { #[cfg(arm_architecture = "v7-r")] fn mpu_pmsa_v7() { use aarch32_cpu::{ - pmsav7::{CacheablePolicy, Config, MemAttr, Mpu, Region, RegionSize}, + pmsav7::{Cacheable, Config, MemAttr, Mpu, Region, RegionSize}, register::Mpuir, }; @@ -69,8 +69,8 @@ fn mpu_pmsa_v7() { enabled: true, no_exec: false, mem_attr: MemAttr::Cacheable { - inner: CacheablePolicy::WriteThroughNoWriteAllocate, - outer: CacheablePolicy::NonCacheable, + inner: Cacheable::WriteThroughNoWriteAlloc, + outer: Cacheable::NonCacheable, shareable: true, }, }], @@ -90,10 +90,10 @@ fn mpu_pmsa_v7() { fn mpu_pmsa_v8() { use aarch32_cpu::{ pmsav8::{ - Cacheable, El1AccessPerms, El1Config, El1Mpu, El1Region, El1Shareability, MemAttr, + CachePolicy, El1AccessPerms, El1Config, El1Mpu, El1Region, El1Shareability, MemAttr, RwAllocPolicy, }, - register::{Mpuir, armv8r::*}, + register::*, }; // How many regions? @@ -261,8 +261,8 @@ fn mpu_pmsa_v8() { }, ], memory_attributes: &[MemAttr::NormalMemory { - outer: Cacheable::WriteThroughNonTransient(RwAllocPolicy::RW), - inner: Cacheable::WriteThroughNonTransient(RwAllocPolicy::RW), + outer: CachePolicy::WriteThroughNonTransient(RwAllocPolicy::RW), + inner: CachePolicy::WriteThroughNonTransient(RwAllocPolicy::RW), }], }) .unwrap(); diff --git a/examples/versatileab/reference/registers-armv7r-none-eabi.out b/examples/versatileab/reference/registers-armv7r-none-eabi.out index de0f7f8e..a1d95060 100644 --- a/examples/versatileab/reference/registers-armv7r-none-eabi.out +++ b/examples/versatileab/reference/registers-armv7r-none-eabi.out @@ -20,7 +20,7 @@ DRegion 12: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false DRegion 13: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 14: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 15: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } -DRegion 0: Region { base: 0x20000000, size: _16M, subregion_mask: 0, enabled: true, no_exec: false, mem_attr: Cacheable { outer: NonCacheable, inner: WriteThroughNoWriteAllocate, shareable: true } } +DRegion 0: Region { base: 0x20000000, size: _16M, subregion_mask: 0, enabled: true, no_exec: false, mem_attr: Cacheable { outer: NonCacheable, inner: WriteThroughNoWriteAlloc, shareable: true } } DRegion 1: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 2: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 3: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } diff --git a/examples/versatileab/reference/registers-armv7r-none-eabihf.out b/examples/versatileab/reference/registers-armv7r-none-eabihf.out index de0f7f8e..a1d95060 100644 --- a/examples/versatileab/reference/registers-armv7r-none-eabihf.out +++ b/examples/versatileab/reference/registers-armv7r-none-eabihf.out @@ -20,7 +20,7 @@ DRegion 12: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false DRegion 13: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 14: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 15: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } -DRegion 0: Region { base: 0x20000000, size: _16M, subregion_mask: 0, enabled: true, no_exec: false, mem_attr: Cacheable { outer: NonCacheable, inner: WriteThroughNoWriteAllocate, shareable: true } } +DRegion 0: Region { base: 0x20000000, size: _16M, subregion_mask: 0, enabled: true, no_exec: false, mem_attr: Cacheable { outer: NonCacheable, inner: WriteThroughNoWriteAlloc, shareable: true } } DRegion 1: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 2: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 3: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } diff --git a/examples/versatileab/reference/registers-thumbv7r-none-eabi.out b/examples/versatileab/reference/registers-thumbv7r-none-eabi.out index de0f7f8e..a1d95060 100644 --- a/examples/versatileab/reference/registers-thumbv7r-none-eabi.out +++ b/examples/versatileab/reference/registers-thumbv7r-none-eabi.out @@ -20,7 +20,7 @@ DRegion 12: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false DRegion 13: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 14: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 15: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } -DRegion 0: Region { base: 0x20000000, size: _16M, subregion_mask: 0, enabled: true, no_exec: false, mem_attr: Cacheable { outer: NonCacheable, inner: WriteThroughNoWriteAllocate, shareable: true } } +DRegion 0: Region { base: 0x20000000, size: _16M, subregion_mask: 0, enabled: true, no_exec: false, mem_attr: Cacheable { outer: NonCacheable, inner: WriteThroughNoWriteAlloc, shareable: true } } DRegion 1: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 2: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 3: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } diff --git a/examples/versatileab/reference/registers-thumbv7r-none-eabihf.out b/examples/versatileab/reference/registers-thumbv7r-none-eabihf.out index de0f7f8e..a1d95060 100644 --- a/examples/versatileab/reference/registers-thumbv7r-none-eabihf.out +++ b/examples/versatileab/reference/registers-thumbv7r-none-eabihf.out @@ -20,7 +20,7 @@ DRegion 12: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false DRegion 13: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 14: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 15: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } -DRegion 0: Region { base: 0x20000000, size: _16M, subregion_mask: 0, enabled: true, no_exec: false, mem_attr: Cacheable { outer: NonCacheable, inner: WriteThroughNoWriteAllocate, shareable: true } } +DRegion 0: Region { base: 0x20000000, size: _16M, subregion_mask: 0, enabled: true, no_exec: false, mem_attr: Cacheable { outer: NonCacheable, inner: WriteThroughNoWriteAlloc, shareable: true } } DRegion 1: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 2: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } DRegion 3: Region { base: 0x0, size: Invalid, subregion_mask: 0, enabled: false, no_exec: false, mem_attr: StronglyOrdered } diff --git a/examples/versatileab/src/bin/registers.rs b/examples/versatileab/src/bin/registers.rs index 7951be3c..e882dbb9 100644 --- a/examples/versatileab/src/bin/registers.rs +++ b/examples/versatileab/src/bin/registers.rs @@ -29,7 +29,7 @@ fn chip_info() { #[cfg(arm_architecture = "v7-r")] fn mpu_pmsa_v7() { use aarch32_cpu::{ - pmsav7::{CacheablePolicy, Config, MemAttr, Mpu, Region, RegionSize}, + pmsav7::{CachePolicy, Config, MemAttr, Mpu, Region, RegionSize}, register::Mpuir, }; @@ -62,8 +62,8 @@ fn mpu_pmsa_v7() { enabled: true, no_exec: false, mem_attr: MemAttr::Cacheable { - inner: CacheablePolicy::WriteThroughNoWriteAllocate, - outer: CacheablePolicy::NonCacheable, + inner: CachePolicy::WriteThroughNoWriteAlloc, + outer: CachePolicy::NonCacheable, shareable: true, }, }], diff --git a/examples/versatileab/src/mmu.rs b/examples/versatileab/src/mmu.rs index 60da0210..afe918a0 100644 --- a/examples/versatileab/src/mmu.rs +++ b/examples/versatileab/src/mmu.rs @@ -18,7 +18,7 @@ //! all the hardware either on the real board or emulated by QEMU. use aarch32_cpu::mmu::{ - AccessPermissions, CacheableMemoryAttribute, L1Section, L1Table, MemoryRegionAttributes, + AccessPermissions, CachePolicy, L1Section, L1Table, MemoryRegionAttributes, NUM_L1_PAGE_TABLE_ENTRIES, SectionAttributes, }; use arbitrary_int::u4; @@ -32,8 +32,8 @@ const SDRAM_ATTRS: SectionAttributes = SectionAttributes { shareable: true, access: AccessPermissions::FullAccess, memory_attrs: MemoryRegionAttributes::CacheableMemory { - inner: CacheableMemoryAttribute::WriteBackWriteAlloc, - outer: CacheableMemoryAttribute::WriteBackWriteAlloc, + inner: CachePolicy::WriteBackWriteAlloc, + outer: CachePolicy::WriteBackWriteAlloc, } .as_raw(), domain: u4::new(0b0), @@ -84,7 +84,7 @@ pub fn set_mmu() { .with_address(core::ptr::addr_of!(MMU_L1_PAGE_TABLE) as usize) .with_irgn(false) .with_nos(false) - .with_rgn(aarch32_cpu::register::ttbr0::Region::WriteBackWriteAllocateCacheable) + .with_rgn(aarch32_cpu::register::ttbr0::Region::WriteBackWriteAllocCacheable) .with_s(true) .with_c(true); unsafe { aarch32_cpu::register::Ttbr0::write(ttbr0) }