diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..531ddd1
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,55 @@
+name: CI
+
+on: [push, pull_request]
+
+jobs:
+ ci:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ rust-toolchain: [nightly]
+ targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat]
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@nightly
+ with:
+ toolchain: ${{ matrix.rust-toolchain }}
+ components: rust-src, clippy, rustfmt
+ targets: ${{ matrix.targets }}
+ - name: Check rust version
+ run: rustc --version --verbose
+ - name: Check code format
+ run: cargo fmt --all -- --check
+ - name: Clippy
+ run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
+ - name: Build
+ run: cargo build --target ${{ matrix.targets }} --all-features
+ - name: Unit test
+ if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}
+ run: cargo test --target ${{ matrix.targets }} -- --nocapture
+
+ doc:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ permissions:
+ contents: write
+ env:
+ default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }}
+ RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@nightly
+ - name: Build docs
+ continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }}
+ run: |
+ cargo doc --no-deps --all-features
+ printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html
+ - name: Deploy to Github Pages
+ if: ${{ github.ref == env.default-branch }}
+ uses: JamesIves/github-pages-deploy-action@v4
+ with:
+ single-commit: true
+ branch: gh-pages
+ folder: target/doc
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ff78c42
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.vscode
+.DS_Store
+Cargo.lock
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..e4b76d6
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "smmuv3"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+log = "=0.4.21"
+tock-registers = "0.8"
+aarch64-cpu = "9.3"
+bitflags = "2.6"
+
+memory_addr = "0.3"
\ No newline at end of file
diff --git a/src/hal.rs b/src/hal.rs
new file mode 100644
index 0000000..2165e1f
--- /dev/null
+++ b/src/hal.rs
@@ -0,0 +1,14 @@
+use memory_addr::{PhysAddr, VirtAddr};
+
+/// The low-level **OS-dependent** helpers that must be provided for
+/// [`smmuv3`].
+pub trait PagingHandler: Sized {
+ /// Request to allocate contiguous 4K-sized pages.
+ fn alloc_pages(num_pages: usize) -> Option;
+ /// Request to free allocated physical pages.
+ fn dealloc_pages(paddr: PhysAddr, num_pages: usize);
+ /// Returns a virtual address that maps to the given physical address.
+ ///
+ /// Used to access the physical memory directly in page table implementation.
+ fn phys_to_virt(paddr: PhysAddr) -> VirtAddr;
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..b35cbfa
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,183 @@
+//! ARM System Memory Management Unit (SMMU) v3 driver written in Rust.
+
+#![no_std]
+#![feature(const_option)]
+#![feature(const_nonnull_new)]
+
+#[macro_use]
+extern crate log;
+
+use core::panic;
+use core::ptr::NonNull;
+
+use memory_addr::PhysAddr;
+use tock_registers::interfaces::{Readable, Writeable};
+use tock_registers::register_structs;
+use tock_registers::registers::{ReadOnly, ReadWrite};
+
+mod hal;
+mod queue;
+mod regs;
+mod stream_table;
+
+pub use hal::PagingHandler;
+pub use regs::*;
+
+use queue::Queue;
+use stream_table::LinearStreamTable;
+
+register_structs! {
+ #[allow(non_snake_case)]
+ pub SMMUv3Page0Regs {
+ (0x0000 => IDR0: IDR0Reg),
+ (0x0004 => IDR1: IDR1Reg),
+ (0x0008 => IDR2: ReadOnly),
+ (0x000C => IDR3: ReadOnly),
+ (0x0010 => IDR4: ReadOnly),
+ (0x0014 => IDR5: ReadOnly),
+ (0x0018 => IIDR: ReadOnly),
+ (0x001C => AIDR: AIDRReg),
+ (0x0020 => CR0: Cr0Reg),
+ (0x0024 => CR0ACK: Cr0AckReg),
+ (0x0028 => CR1: Cr1Reg),
+ (0x002c => CR2: ReadWrite),
+ (0x0030 => _reserved0),
+ (0x0050 => IRQ_CTRL: ReadWrite),
+ (0x0054 => IRQ_CTRLACK: ReadOnly),
+ (0x0058 => _reserved1),
+ (0x0060 => GERROR: ReadOnly),
+ (0x0064 => GERRORN: ReadWrite),
+ (0x0068 => GERROR_IRQ_CFG0: ReadWrite),
+ (0x0070 => _reserved2),
+ (0x0080 => STRTAB_BASE: StrtabBaseReg),
+ (0x0088 => STRTAB_BASE_CFG: StrtabBaseCfgReg),
+ (0x008c => _reserved3),
+ (0x0090 => CMDQ_BASE: CmdQBaseReg),
+ (0x0098 => CMDQ_PROD: CmdQProdReg),
+ (0x009c => CMDQ_CONS: CmdQConsReg),
+ (0x00a0 => EVENTQ_BASE: ReadWrite),
+ (0x00a8 => _reserved4),
+ (0x00b0 => EVENTQ_IRQ_CFG0: ReadWrite),
+ (0x00b8 => EVENTQ_IRQ_CFG1: ReadWrite),
+ (0x00bc => EVENTQ_IRQ_CFG2: ReadWrite),
+ (0x00c0 => _reserved5),
+ (0x100a8 => EVENTQ_PROD: ReadWrite),
+ (0x100ac => EVENTQ_CONS: ReadWrite),
+ (0x100b0 => _reserved6),
+ (0x20000 => @END),
+ }
+}
+
+pub struct SMMUv3 {
+ base: NonNull,
+ stream_table: LinearStreamTable,
+ cmd_queue: Queue,
+}
+
+unsafe impl Send for SMMUv3 {}
+unsafe impl Sync for SMMUv3 {}
+
+impl SMMUv3 {
+ /// Construct a new SMMUv3 instance from the base address.
+ pub const fn new(base: *mut u8) -> Self {
+ Self {
+ base: NonNull::new(base).unwrap().cast(),
+ stream_table: LinearStreamTable::uninit(),
+ cmd_queue: Queue::uninit(),
+ }
+ }
+
+ pub fn init(&mut self) {
+ let sid_max_bits = self.regs().IDR1.read(IDR1::SIDSIZE);
+ info!(
+ "Max SID bits: {}, max SIE count {}",
+ sid_max_bits,
+ 1 << sid_max_bits
+ );
+
+ if sid_max_bits >= 7
+ && self.regs().IDR0.read(IDR0::ST_LEVEL) == IDR0::ST_LEVEL::LinearStreamTable.into()
+ {
+ // SMMU supports one stream
+ panic!("Smmuv3 the system must support for 2-level table");
+ }
+
+ self.stream_table.init(sid_max_bits);
+
+ self.regs().STRTAB_BASE.write(
+ STRTAB_BASE::RA::Enable
+ + STRTAB_BASE::ADDR.val(self.stream_table.base_addr().as_usize() as u64 >> 6),
+ );
+
+ self.regs()
+ .STRTAB_BASE_CFG
+ .write(STRTAB_BASE_CFG::FMT::Linear + STRTAB_BASE_CFG::LOG2SIZE.val(sid_max_bits));
+
+ let cmdqs_log2 = self.regs().IDR1.read(IDR1::CMDQS);
+ self.cmd_queue.init(cmdqs_log2);
+ self.regs().CMDQ_BASE.write(
+ CMDQ_BASE::RA::ReadAllocate
+ + CMDQ_BASE::ADDR.val(self.cmd_queue.base_addr().as_usize() as u64 >> 5)
+ + CMDQ_BASE::LOG2SIZE.val(cmdqs_log2 as _),
+ );
+ self.regs()
+ .CMDQ_PROD
+ .write(CMDQ_PROD::WR.val(self.cmd_queue.prod()));
+ self.regs()
+ .CMDQ_CONS
+ .write(CMDQ_CONS::RD.val(self.cmd_queue.cons()));
+
+ self.enable();
+ }
+
+ fn enable(&mut self) {
+ self.regs().CR1.write(
+ CR1::TABLE_IC::WriteBackCacheable
+ + CR1::TABLE_OC::WriteBackCacheable
+ + CR1::TABLE_SH::InnerShareable
+ + CR1::QUEUE_IC::WriteBackCacheable
+ + CR1::QUEUE_OC::WriteBackCacheable
+ + CR1::QUEUE_SH::InnerShareable,
+ );
+
+ self.regs().CR0.write(CR0::SMMUEN::Enable);
+
+ const ARM_SMMU_SYNC_TIMEOUT: usize = 0x1000000;
+
+ for _timeout in 0..ARM_SMMU_SYNC_TIMEOUT {
+ if self.regs().CR0ACK.is_set(CR0ACK::SMMUEN) {
+ info!("SMMUv3 enabled");
+ return;
+ }
+ }
+ error!("CR0 write err!");
+ }
+
+ const fn regs(&self) -> &SMMUv3Page0Regs {
+ unsafe { self.base.as_ref() }
+ }
+
+ pub fn version(&self) -> &'static str {
+ match self.regs().AIDR.read_as_enum(AIDR::ArchMinorRev) {
+ Some(AIDR::ArchMinorRev::Value::SMMUv3_0) => "SMMUv3.0",
+ Some(AIDR::ArchMinorRev::Value::SMMUv3_1) => "SMMUv3.1",
+ Some(AIDR::ArchMinorRev::Value::SMMUv3_2) => "SMMUv3.2",
+ Some(AIDR::ArchMinorRev::Value::SMMUv3_3) => "SMMUv3.3",
+ Some(AIDR::ArchMinorRev::Value::SMMUv3_4) => "SMMUv3.4",
+ _ => "Unknown",
+ }
+ }
+
+ pub fn aidr(&self) -> &AIDRReg {
+ &self.regs().AIDR
+ }
+
+ pub fn check_features(&self) {
+ self.regs().IDR0.is_set(IDR0::S1P);
+ }
+
+ pub fn add_device(&mut self, sid: usize, vmid: usize, s2pt_base: PhysAddr) {
+ self.stream_table
+ .set_s2_translated_ste(sid, vmid, s2pt_base);
+ }
+}
diff --git a/src/queue.rs b/src/queue.rs
new file mode 100644
index 0000000..ce9f132
--- /dev/null
+++ b/src/queue.rs
@@ -0,0 +1,49 @@
+use memory_addr::{pa, PhysAddr};
+
+use crate::hal::PagingHandler;
+
+/// According to the SMMUv3 spec, Chapter 3. Operation 3.5. Command and Event queues.
+///
+/// Each circular buffer is 2^n-items in size, where 0 <= n <= 19.
+/// An implementation might support fewer than 19 bits of index.
+/// Each PROD and CONS register is 20 bits to accommodate the maximum 19-bit index plus the wrap bit.
+pub const MAX_CMDQS: u32 = 19;
+
+pub struct Queue {
+ base: PhysAddr,
+ queue_size: u32,
+ prod: u32,
+ cons: u32,
+ _marker: core::marker::PhantomData,
+}
+
+impl Queue {
+ pub const fn uninit() -> Self {
+ Self {
+ base: pa!(0xdead_beef),
+ queue_size: 0,
+ prod: 0,
+ cons: 0,
+ _marker: core::marker::PhantomData,
+ }
+ }
+
+ pub fn init(&mut self, cmdqs: u32) {
+ self.base = H::alloc_pages(1).expect("Failed to allocate queue");
+
+ let cmdqs = u32::max(cmdqs, MAX_CMDQS);
+ self.queue_size = 1 << cmdqs;
+ }
+
+ pub fn base_addr(&self) -> PhysAddr {
+ self.base
+ }
+
+ pub fn prod(&self) -> u32 {
+ self.prod
+ }
+
+ pub fn cons(&self) -> u32 {
+ self.cons
+ }
+}
diff --git a/src/regs/aidr.rs b/src/regs/aidr.rs
new file mode 100644
index 0000000..6ea5f74
--- /dev/null
+++ b/src/regs/aidr.rs
@@ -0,0 +1,29 @@
+use tock_registers::register_bitfields;
+use tock_registers::registers::ReadOnly;
+
+register_bitfields! {u32,
+ pub AIDR [
+ /// Major Architecture revision.
+ ///
+ /// - 0b0000 SMMUv3.x.
+ ArchMajorRev OFFSET(4) NUMBITS(4) [
+ SMMUv3_x = 0b0000
+ ],
+ /// Minor Architecture revision.
+ ///
+ /// - 0b0000 SMMUv3.0.
+ /// - 0b0001 SMMUv3.1.
+ /// - 0b0010 SMMUv3.2.
+ /// - 0b0011 SMMUv3.3.
+ /// - 0b0100 SMMUv3.4.
+ ArchMinorRev OFFSET(0) NUMBITS(4) [
+ SMMUv3_0 = 0b0000,
+ SMMUv3_1 = 0b0001,
+ SMMUv3_2 = 0b0010,
+ SMMUv3_3 = 0b0011,
+ SMMUv3_4 = 0b0100
+ ],
+ ]
+}
+
+pub type AIDRReg = ReadOnly;
diff --git a/src/regs/cmdq_base.rs b/src/regs/cmdq_base.rs
new file mode 100644
index 0000000..7413cc9
--- /dev/null
+++ b/src/regs/cmdq_base.rs
@@ -0,0 +1,68 @@
+//! Chapter 6. Memory map and registers
+//! 6.3. Register formats
+//! 6.3.26 SMMU_CMDQ_BASE
+//!
+//! The SMMU_CMDQ_BASE characteristics are:
+//!
+//! ## Purpose
+//! Configuration of the Command queue base address.
+//!
+//! ## Attributes
+//! SMMU_CMDQ_BASE is a 64-bit register.
+//!
+//! This register is part of the SMMUv3_PAGE_0 block.
+//!
+
+use tock_registers::register_bitfields;
+use tock_registers::registers::ReadWrite;
+
+register_bitfields! {u64,
+ pub CMDQ_BASE [
+ /// Bit [63] Reserved, RES0.
+ Reserved31 OFFSET(63) NUMBITS(1) [],
+ /// RA, bit [62] Read-Allocate hint.
+ ///
+ /// - 0b0 No Read-Allocate.
+ /// - 0b1 Read-Allocate.
+ ///
+ /// The reset behavior of this field is:
+ /// - When SMMU_IDR1.QUEUES_PRESET == 1, this field resets to an IMPLEMENTATION DEFINED value.
+ /// - Otherwise, this field resets to an UNKNOWN value.
+ RA OFFSET(62) NUMBITS(1) [
+ NoReadAllocate = 0,
+ ReadAllocate = 1
+ ],
+ /// Bits [61:56] Reserved, RES0.
+ Reserved56 OFFSET(56) NUMBITS(6) [],
+ /// ADDR, bits [55:5] PA of Command queue base, bits [55:5].
+ /// - Address bits above and below this field range are treated as zero.
+ /// - High-order bits of the ADDR field above the system physical address size, as reported by SMMU_IDR5.OAS, are RES0.
+ /// – Note: An implementation is not required to store these bits.
+ /// - The effective base address is aligned by the SMMU to the larger of the queue size in bytes or 32 bytes, ignoring the least-significant bits of ADDR as required. ADDR bits [4:0] are treated as zero.
+ /// – Note: For example, a queue with 28 entries is 4096 bytes in size so software must align an allocation, and therefore ADDR, to a 4KB boundary.
+ ///
+ /// The reset behavior of this field is:
+ /// - When SMMU_IDR1.QUEUES_PRESET == 1, this field resets to an IMPLEMENTATION DEFINED value.
+ /// - Otherwise, this field resets to an UNKNOWN value.
+ ADDR OFFSET(5) NUMBITS(51) [],
+ /// LOG2SIZE, bits [4:0] Queue size as log2(entries).
+ /// - LOG2SIZE must be less than or equal to SMMU_IDR1.CMDQS. Except for the purposes of readback of this register, any use of the value of this field is capped at the maximum, SMMU_IDR1.CMDQS.
+ /// - The minimum size is 0, for one entry, but this must be aligned to a 32-byte (2 entry) boundary as above.
+ ///
+ /// The reset behavior of this field is:
+ /// - When SMMU_IDR1.QUEUES_PRESET == 1, this field resets to an IMPLEMENTATION DEFINED value.
+ /// - Otherwise, this field resets to an UNKNOWN value.
+ LOG2SIZE OFFSET(0) NUMBITS(5) []
+ ]
+}
+
+/// Upon initialization, if SMMU_IDR1.QUEUES_PRESET == 0 then the SMMU_CMDQ_BASE.LOG2SIZE field might affect which bits of SMMU_CMDQ_CONS.RD and SMMU_CMDQ_PROD.WR can be written upon initialization. The registers must be initialized in this order:
+/// 1. Write SMMU_CMDQ_BASE to set the queue base and size.
+/// 2. Write initial values to SMMU_CMDQ_CONS and SMMU_CMDQ_PROD.
+/// 3. Enable the queue with an Update of the respective SMMU_CR0.CMDQEN to 1.
+///
+/// This also applies to the initialization of Event queue and PRI queue registers.
+/// Access attributes of the Command queue are set using the SMMU_CR1.QUEUE_* fields. A Read-Allocate hint is provided for Command queue accesses with the RA field.
+///
+/// SMMU_CMDQ_BASE is Guarded by SMMU_CR0.CMDQEN and must only be modified when SMMU_CR0.CMDQEN == 0
+pub type CmdQBaseReg = ReadWrite;
diff --git a/src/regs/cmdq_cons.rs b/src/regs/cmdq_cons.rs
new file mode 100644
index 0000000..965a3c3
--- /dev/null
+++ b/src/regs/cmdq_cons.rs
@@ -0,0 +1,50 @@
+//! Chapter 6. Memory map and registers
+//! 6.3. Register formats
+//! 6.3.28 SMMU_CMDQ_CONS
+//!
+//! The SMMU_CMDQ_CONS characteristics are:
+//!
+//! ## Purpose
+//! Command queue consumer read index.
+//!
+//! ## Attributes
+//! SMMU_CMDQ_CONS is a 32-bit register.
+//! This register is part of the SMMUv3_PAGE_0 block.
+
+use tock_registers::register_bitfields;
+use tock_registers::registers::ReadWrite;
+
+register_bitfields! {u32,
+ pub CMDQ_CONS [
+ /// Bit [31] Reserved, RES0.
+ Reserved31 OFFSET(31) NUMBITS(1) [],
+ /// ERR, bits [30:24] Error reason code.
+ /// - When a command execution error is detected, ERR is set to a reason code and then the SMMU_GERROR.CMDQ_ERR global error becomes active.
+ /// - The value in this field is UNKNOWN when the CMDQ_ERR global error is not active.
+ ///
+ /// The reset behavior of this field is: • This field resets to an UNKNOWN value.
+ ERR OFFSET(24) NUMBITS(7) [],
+ /// Bits [23:20] Reserved, RES0.
+ Reserved23 OFFSET(20) NUMBITS(4) [],
+ /// RD, bits [19:0] Command queue read index.
+ /// This field is treated as two sub-fields, depending on the configured queue size:
+ /// - **Bit [QS]: RD_WRAP** - Queue read index wrap flag.
+ /// - **Bits [QS-1:0]: RD** - Queue read index.
+ /// - Updated by the SMMU (consumer) to point at the queue entry after the entry it has just consumed.
+ ///
+ /// The reset behavior of this field is:
+ /// - This field resets to an UNKNOWN value.
+ RD OFFSET(0) NUMBITS(20) []
+ ]
+}
+
+/// QS == SMMU_CMDQ_BASE.LOG2SIZE and SMMU_CMDQ_BASE.LOG2SIZE <= SMMU_IDR1.CMDQS <= 19.
+///
+/// This gives a configurable-sized index pointer followed immediately by the wrap bit.
+///
+/// If QS < 19, bits [19:QS + 1] are RAZ. When incremented by the SMMU, the RD index is always wrapped to the current queue size given by SMMU_CMDQ_BASE.LOG2SIZE.
+///
+/// If QS == 0 the queue has one entry. Zero bits of RD index are present and RD_WRAP is bit zero.
+///
+/// When SMMU_CMDQ_BASE.LOG2SIZE is increased within its valid range, the value of the bits of this register that were previously above the old wrap flag position are UNKNOWN and when it is decreased, the value of the bits from the wrap flag downward are the effective truncation of the value in the old field.
+pub type CmdQConsReg = ReadWrite;
diff --git a/src/regs/cmdq_prod.rs b/src/regs/cmdq_prod.rs
new file mode 100644
index 0000000..1aa87a7
--- /dev/null
+++ b/src/regs/cmdq_prod.rs
@@ -0,0 +1,49 @@
+//! Chapter 6. Memory map and registers
+//! 6.3. Register formats
+//! 6.3.27 SMMU_CMDQ_PROD
+//!
+//! The SMMU_CMDQ_PROD characteristics are:
+//!
+//! ## Purpose
+//! Allows Command queue producer to update the write index.
+//!
+//! ## Attributes
+//! SMMU_CMDQ_PROD is a 32-bit register.
+//! This register is part of the SMMUv3_PAGE_0 block.
+//!
+use tock_registers::register_bitfields;
+use tock_registers::registers::ReadWrite;
+
+register_bitfields! {u32,
+ pub CMDQ_PROD [
+ /// Bits [31:20] Reserved, RES0.
+ Reserved31 OFFSET(20) NUMBITS(12) [],
+ /// WR, bits [19:0]
+ /// Command queue write index.
+ ///
+ /// This field is treated as two sub-fields, depending on the configured queue size:
+ /// - **Bit [QS]: WR_WRAP** - Command queue write index wrap flag.
+ /// - **Bits [QS-1:0]: WR** - Command queue write index.
+ /// - Updated by the host PE (producer) indicating the next empty space in the queue after new data.
+ ///
+ /// The reset behavior of this field is:
+ /// - This field resets to an UNKNOWN value.
+ WR OFFSET(0) NUMBITS(20) []
+ ]
+}
+
+/// Additional Information
+///
+/// QS == SMMU_CMDQ_BASE.LOG2SIZE, see SMMU_CMDQ_CONS.
+///
+/// If QS < 19, bits [19:QS + 1] are RES0. If software writes a non-zero value to these bits, the value might be stored but has no other effect. In addition, if SMMU_IDR1.CMDQS < 19, bits [19:CMDQS+1] are UNKNOWN on read.
+///
+/// If QS == 0 the queue has one entry. Zero bits of WR index are present and WR_WRAP is bit zero.
+///
+/// When software increments WR, if the index would pass off the end of the queue it must be correctly wrapped to the queue size given by QS and WR_WRAP toggled.
+///
+/// Note: In the degenerate case of a one-entry queue, an increment of WR consists solely of a toggle of WR_WRAP.
+/// There is space in the queue for additional commands if:
+///
+/// `SMMU_CMDQ_CONS.RD != SMMU_CMDQ_PROD.WR || SMMU_CMDQ_CONS.RD_WRAP == SMMU_CMDQ_PROD.WR_WRAP`
+pub type CmdQProdReg = ReadWrite;
diff --git a/src/regs/cr0.rs b/src/regs/cr0.rs
new file mode 100644
index 0000000..37b398c
--- /dev/null
+++ b/src/regs/cr0.rs
@@ -0,0 +1,121 @@
+//! Chapter 6. Memory map and registers
+//! 6.3. Register formats
+//! 6.3.9 SMMU_CR0
+//!
+//! The SMMU_CR0 characteristics are:
+//! ## Purpose
+//! Non-secure SMMU programming interface control and configuration register
+//! ## Attributes
+//! SMMU_CR0 is a 32-bit register.
+//! This register is part of the SMMUv3_PAGE_0 block.
+
+use tock_registers::register_bitfields;
+use tock_registers::registers::ReadWrite;
+
+register_bitfields! {u32,
+ pub CR0 [
+ /// Bits [31:11] Reserved, RES0.
+ Reserved11 OFFSET(11) NUMBITS(21) [],
+ /// DPT_WALK_EN, bit [10]
+ /// When SMMU_IDR3.DPT == 1:
+ /// Enable DPT walks for Non-secure state.
+ /// - 0b0 Non-secure DPT walks are disabled.
+ /// - 0b1 Non-secure DPT walks are enabled.
+ /// This field has similar Update behavior to other CR0 fields, in that: When it is writable and its value is changed by a write, the SMMU begins a transition which is then acknowledged by updating SMMU_CR0ACK.DPT_WALK_EN to the new value.
+ /// Otherwise: Reserved, RES0.
+ DPT_WALK_EN OFFSET(10) NUMBITS(1) [
+ Disable = 0,
+ Enable = 1
+ ],
+ /// Bit [9] Reserved, RES0.
+ Reserved9 OFFSET(9) NUMBITS(1) [],
+ /// VMW, bits [8:6]
+ /// When SMMU_IDR0.VMW == 1: VMID Wildcard.
+ /// - 0b000 TLB invalidations match VMID tags exactly.
+ /// - 0b001 TLB invalidations match VMID[N:1].
+ /// - 0b010 TLB invalidations match VMID[N:2].
+ /// - 0b011 TLB invalidations match VMID[N:3].
+ /// - 0b100 TLB invalidations match VMID[N:4].
+ ///
+ /// All other values are reserved, and behave as 0b000.
+ /// - N == upper bit of VMID as determined by SMMU_IDR0.VMID16.
+ ///
+ /// This field has no effect on VMID matching on translation lookup.
+ ///
+ /// The reset behavior of this field is:
+ /// - This field resets to 0b000.
+ ///
+ /// Otherwise: Reserved, RES0.
+ VMW OFFSET(6) NUMBITS(3) [
+ MatchVMIDExactly = 0b000,
+ MatchVMIDN1 = 0b001,
+ MatchVMIDN2 = 0b010,
+ MatchVMIDN3 = 0b011,
+ MatchVMIDN4 = 0b100
+ ],
+ /// Bit [5] Reserved, RES0.
+ Reserved5 OFFSET(5) NUMBITS(1) [],
+ /// ATSCHK, bit [4]
+ /// When SMMU_IDR0.ATS == 1:
+ /// ATS behavior.
+ /// - 0b0 Fast mode, all ATS Translated traffic passes through the SMMU without Stream table or TLB lookup.
+ /// - 0b1 Safe mode, all ATS Translated traffic is checked against the corresponding STE.EATS field to determine whether the StreamID is allowed to produce Translated transactions.
+ ///
+ /// The reset behavior of this field is:
+ /// - This field resets to 0b0.
+ ///
+ /// Otherwise: Reserved, RES0.
+ ATSCHK OFFSET(4) NUMBITS(1) [
+ FastMode = 0,
+ SafeMode = 1
+ ],
+ /// CMDQEN, bit [3]
+ /// Enable Command queue processing.
+ ///
+ /// - 0b0 Processing of commands from the Non-secure Command queue is disabled.
+ /// - 0b1 Processing of commands from the Non-secure Command queue is enabled.
+ ///
+ /// The reset behavior of this field is:
+ /// - This field resets to 0b0.
+ CMDQEN OFFSET(3) NUMBITS(1) [
+ Disable = 0,
+ Enable = 1
+ ],
+ /// EVENTQEN, bit [2] Enable Event queue writes.
+ ///
+ /// - 0b0 Writes to the Non-secure Event queue are disabled.
+ /// - 0b1 Writes to the Non-secure Event queue are enabled.
+ ///
+ /// The reset behavior of this field is:
+ /// - This field resets to 0b0.
+ EVENTQEN OFFSET(2) NUMBITS(1) [
+ Disable = 0,
+ Enable = 1
+ ],
+ /// PRIQEN, bit [1]
+ /// - When SMMU_IDR0.PRI == 1:
+ /// - Enable PRI queue writes.
+ /// - 0b0 Writes to the PRI queue are disabled.
+ /// - 0b1 Writes to the PRI queue are enabled.
+ /// - The reset behavior of this field is:
+ /// - This field resets to 0b0.
+ /// - Otherwise: Reserved, RES0.
+ PRIQEN OFFSET(1) NUMBITS(1) [
+ Disable = 0,
+ Enable = 1
+ ],
+ /// SMMUEN, bit [0] Non-secure SMMU enable
+ /// - 0b0 All Non-secure streams bypass SMMU, with attributes determined from SMMU_GBPA.
+ /// - 0b1 All Non-secure streams are checked against configuration structures, and might undergo translation.
+ ///
+ /// The reset behavior of this field is:
+ /// - This field resets to 0b0.
+ SMMUEN OFFSET(0) NUMBITS(1) [
+ Bypass = 0,
+ Enable = 1
+ ]
+ ]
+}
+
+/// Each field in this register has a corresponding field in SMMU_CR0ACK. An individual field is described as Updated after the value of the field observed in SMMU_CR0ACK matches the value that was written to the field in [`Cr0Reg`]. Reserved fields in [`Cr0Reg`] are not reflected in SMMU_CR0ACK. To ensure a field change has taken effect, software must poll the equivalent field in SMMU_CR0ACK after writing the field in this register.
+pub type Cr0Reg = ReadWrite;
\ No newline at end of file
diff --git a/src/regs/cr0ack.rs b/src/regs/cr0ack.rs
new file mode 100644
index 0000000..79532c1
--- /dev/null
+++ b/src/regs/cr0ack.rs
@@ -0,0 +1,125 @@
+//! Chapter 6. Memory map and registers
+//! 6.3. Register formats
+//! 6.3.10 SMMU_CR0ACK
+//! The SMMU_CR0ACK characteristics are:
+//! ## Purpose
+//! Provides acknowledgment of changes to configurations and controls in the Non-secure SMMU programming interface, SMMU_CR0.
+//! ## Attributes
+//! SMMU_CR0ACK is a 32-bit register.
+//!
+//! This register is part of the SMMUv3_PAGE_0 block.
+
+use tock_registers::register_bitfields;
+use tock_registers::registers::ReadOnly;
+
+register_bitfields! {u32,
+ pub CR0ACK [
+ /// Bits [31:11] Reserved, RES0.
+ Reserved11 OFFSET(11) NUMBITS(21) [],
+ /// DPT_WALK_EN, bit [10]
+ ///
+ /// - When SMMU_IDR3.DPT == 1:
+ /// - Enable DPT walks for Non-secure state.
+ /// - 0b0 Non-secure DPT walks are disabled.
+ /// - 0b1 Non-secure DPT walks are enabled.
+ /// - See [`crate::regs::cr0::CR0::DPT_WALK_EN`].
+ /// - The reset behavior of this field is:
+ /// - This field resets to 0b0.
+ ///
+ /// Otherwise: Reserved, RES0.
+ DPT_WALK_EN OFFSET(10) NUMBITS(1) [
+ Disable = 0,
+ Enable = 1
+ ],
+ /// Bit [9] Reserved, RES0.
+ Reserved9 OFFSET(9) NUMBITS(1) [],
+ /// VMW, bits [8:6]
+ /// When SMMU_IDR0.VMW == 1:
+ /// - VMID Wildcard.
+ /// - 0b000 TLB invalidations match VMID tags exactly.
+ /// - 0b001 TLB invalidations match VMID[N:1].
+ /// - 0b010 TLB invalidations match VMID[N:2].
+ /// - 0b011 TLB invalidations match VMID[N:3].
+ /// - 0b100 TLB invalidations match VMID[N:4].
+ ///
+ /// - All other values are reserved, and behave as 0b000.
+ /// - N == upper bit of VMID as determined by SMMU_IDR0.VMID16.
+ /// - This field has no effect on VMID matching on translation lookup.
+ ///
+ /// The reset behavior of this field is:
+ /// - This field resets to 0b000.
+ ///
+ /// Otherwise: Reserved, RES0.
+ VMW OFFSET(6) NUMBITS(3) [
+ MatchVMIDExactly = 0b000,
+ MatchVMIDN1 = 0b001,
+ MatchVMIDN2 = 0b010,
+ MatchVMIDN3 = 0b011,
+ MatchVMIDN4 = 0b100
+ ],
+ /// Bit [5] Reserved, RES0.
+ Reserved5 OFFSET(5) NUMBITS(1) [],
+ /// ATSCHK, bit [4]
+ /// When SMMU_IDR0.ATS == 1:
+ /// - ATS behavior.
+ /// - 0b0 Fast mode, all ATS Translated traffic passes through the SMMU without Stream table or TLB lookup.
+ /// - 0b1 Safe mode, all ATS Translated traffic is checked against the corresponding STE.EATS field to determine whether the StreamID is allowed to produce Translated transactions.
+ ///
+ /// - The reset behavior of this field is:
+ /// - This field resets to 0b0.
+ /// Otherwise: Reserved, RES0.
+ ATSCHK OFFSET(4) NUMBITS(1) [
+ FastMode = 0,
+ SafeMode = 1
+ ],
+ /// CMDQEN, bit [3]
+ /// Enable Command queue processing.
+ ///
+ /// - 0b0 Processing of commands from the Non-secure Command queue is disabled.
+ /// - 0b1 Processing of commands from the Non-secure Command queue is enabled.
+ ///
+ /// The reset behavior of this field is:
+ /// - This field resets to 0b0.
+ CMDQEN OFFSET(3) NUMBITS(1) [
+ Disable = 0,
+ Enable = 1
+ ],
+ /// EVENTQEN, bit [2] Enable Event queue writes.
+ ///
+ /// - 0b0 Writes to the Non-secure Event queue are disabled.
+ /// - 0b1 Writes to the Non-secure Event queue are enabled.
+ ///
+ /// The reset behavior of this field is:
+ /// - This field resets to 0b0.
+ EVENTQEN OFFSET(2) NUMBITS(1) [
+ Disable = 0,
+ Enable = 1
+ ],
+ /// PRIQEN, bit [1]
+ /// - When SMMU_IDR0.PRI == 1:
+ /// - Enable PRI queue writes.
+ /// - 0b0 Writes to the PRI queue are disabled.
+ /// - 0b1 Writes to the PRI queue are enabled.
+ /// - The reset behavior of this field is:
+ /// - This field resets to 0b0.
+ /// - Otherwise: Reserved, RES0.
+ PRIQEN OFFSET(1) NUMBITS(1) [
+ Disable = 0,
+ Enable = 1
+ ],
+ /// SMMUEN, bit [0] Non-secure SMMU enable
+ /// - 0b0 All Non-secure streams bypass SMMU, with attributes determined from SMMU_GBPA.
+ /// - 0b1 All Non-secure streams are checked against configuration structures, and might undergo translation.
+ ///
+ /// The reset behavior of this field is:
+ /// - This field resets to 0b0.
+ SMMUEN OFFSET(0) NUMBITS(1) [
+ Bypass = 0,
+ Enable = 1
+ ]
+ ]
+}
+
+/// Access on this interface is RO.
+///
+pub type Cr0AckReg = ReadOnly;
\ No newline at end of file
diff --git a/src/regs/cr1.rs b/src/regs/cr1.rs
new file mode 100644
index 0000000..49d8aeb
--- /dev/null
+++ b/src/regs/cr1.rs
@@ -0,0 +1,125 @@
+//! Chapter 6.
+//! Memory map and registers
+//! 6.3. Register formats
+//! 6.3.11 SMMU_CR1
+//! The SMMU_CR1 characteristics are:
+//! ## Purpose
+//! Non-secure SMMU programming interface control and configuration register.
+//! ## Attributes
+//! SMMU_CR1 is a 32-bit register.
+//! This register is part of the SMMUv3_PAGE_0 block.
+
+use tock_registers::register_bitfields;
+use tock_registers::registers::ReadWrite;
+
+register_bitfields! {u32,
+ pub CR1 [
+ /// Bits [31:12] Reserved, RES0.
+ Reserved12 OFFSET(12) NUMBITS(19) [],
+ /// TABLE_SH, bits [11:10]
+ /// Table access Shareability.
+ /// - 0b00 Non-shareable.
+ /// - 0b01 Reserved, treated as 0b00.
+ /// - 0b10 Outer Shareable.
+ /// - 0b11 Inner Shareable.
+ /// - Note: When SMMU_CR1.TABLE_OC == 0b00 and SMMU_CR1.TABLE_IC == 0b00, this field is IGNORED and behaves as Outer Shareable.
+ /// The reset behavior of this field is:
+ /// - When SMMU_IDR1.TABLES_PRESET == 1, this field resets to an IMPLEMENTATION DEFINED value.
+ /// - Otherwise, this field resets to an UNKNOWN value.
+ /// Accessing this field has the following behavior:
+ /// - Access to this field is RW if all of the following are true:
+ /// - SMMU_CR0.SMMUEN == 0
+ /// – SMMU_CR0ACK.SMMUEN == 0
+ /// - Otherwise, access to this field is RO.
+ TABLE_SH OFFSET(10) NUMBITS(2) [
+ NonShareable = 0b00,
+ /// treated as 0b00
+ Reserved = 0b01,
+ OuterShareable = 0b10,
+ InnerShareable = 0b11
+ ],
+ /// TABLE_OC, bits [9:8]
+ /// Table access Outer Cacheability.
+ /// - 0b00 Non-cacheable.
+ /// - 0b01 Write-Back Cacheable.
+ /// - 0b10 Write-Through Cacheable.
+ /// - 0b11 Reserved, treated as 0b00.
+ /// The reset behavior of this field is:
+ /// - When SMMU_IDR1.TABLES_PRESET == 1, this field resets to an IMPLEMENTATION DEFINED value.
+ /// - Otherwise, this field resets to an UNKNOWN value.
+ /// Accessing this field has the following behavior:
+ /// - Access to this field is RW if all of the following are true:
+ /// – SMMU_CR0.SMMUEN == 0
+ /// – SMMU_CR0ACK.SMMUEN == 0
+ /// - Otherwise, access to this field is RO.
+ TABLE_OC OFFSET(8) NUMBITS(2) [
+ NonCacheable = 0b00,
+ WriteBackCacheable = 0b01,
+ WriteThroughCacheable = 0b10,
+ /// treated as 0b00
+ Reserved = 0b11
+ ],
+ /// TABLE_IC, bits [7:6]
+ /// Table access Inner Cacheability.
+ /// - 0b00 Non-cacheable.
+ /// - 0b01 Write-Back Cacheable.
+ /// - 0b10 Write-Through Cacheable.
+ /// - 0b11 Reserved. Treated as 0b00.
+ /// The reset behavior of this field is:
+ /// - When SMMU_IDR1.TABLES_PRESET == 1, this field resets to an IMPLEMENTATION DEFINED value.
+ /// - Otherwise, this field resets to an UNKNOWN value.
+ /// Accessing this field has the following behavior:
+ /// - Access to this field is RW if all of the following are true:
+ /// - SMMU_CR0.SMMUEN == 0
+ /// – SMMU_CR0ACK.SMMUEN == 0
+ /// - Otherwise, access to this field is RO.
+ TABLE_IC OFFSET(6) NUMBITS(2) [
+ NonCacheable = 0b00,
+ WriteBackCacheable = 0b01,
+ WriteThroughCacheable = 0b10,
+ /// treated as 0b00
+ Reserved = 0b11
+ ],
+ /// QUEUE_SH, bits [5:4]
+ /// Queue access Shareability.
+ /// - 0b00 Non-shareable.
+ /// - 0b01 Reserved, treated as 0b00.
+ /// - 0b10 Outer Shareable.
+ /// - 0b11 Inner Shareable.
+ QUEUE_SH OFFSET(4) NUMBITS(2) [
+ NonShareable = 0b00,
+ /// treated as 0b00
+ Reserved = 0b01,
+ OuterShareable = 0b10,
+ InnerShareable = 0b11
+ ],
+ /// QUEUE_OC, bits [3:2]
+ /// Queue access Outer Cacheability.
+ /// - 0b00 Non-cacheable.
+ /// - 0b01 Write-Back Cacheable.
+ /// - 0b10 Write-Through Cacheable.
+ /// - 0b11 Reserved, treated as 0b00.
+ QUEUE_OC OFFSET(2) NUMBITS(2) [
+ NonCacheable = 0b00,
+ WriteBackCacheable = 0b01,
+ WriteThroughCacheable = 0b10,
+ /// treated as 0b00
+ Reserved = 0b11
+ ],
+ /// QUEUE_IC, bits [1:0]
+ /// Queue access Inner Cacheability.
+ /// - 0b00 Non-cacheable.
+ /// - 0b01 Write-Back Cacheable.
+ /// - 0b10 Write-Through Cacheable.
+ /// - 0b11 Reserved, treated as 0b00.
+ QUEUE_IC OFFSET(0) NUMBITS(2) [
+ NonCacheable = 0b00,
+ WriteBackCacheable = 0b01,
+ WriteThroughCacheable = 0b10,
+ /// treated as 0b00
+ Reserved = 0b11
+ ]
+ ]
+}
+
+pub type Cr1Reg = ReadWrite;
\ No newline at end of file
diff --git a/src/regs/idr0.rs b/src/regs/idr0.rs
new file mode 100644
index 0000000..12b082d
--- /dev/null
+++ b/src/regs/idr0.rs
@@ -0,0 +1,99 @@
+use tock_registers::register_bitfields;
+use tock_registers::registers::ReadOnly;
+
+register_bitfields! {u32,
+ pub IDR0 [
+ /// Multi-level Stream table support.
+ ///
+ /// - 0b00 Linear Stream table supported.
+ /// - 0b01 2-level Stream table supported in addition to Linear Stream table.
+ /// - 0b1x Reserved.
+ ST_LEVEL OFFSET(27) NUMBITS(2) [
+ LinearStreamTable = 0b00,
+ TwoLevelStreamTableInAdditionToLinearStreamTable = 0b01
+ ],
+ /// 16-bit VMID supported.
+ ///
+ /// - 0b0 16-bit VMID not supported.
+ /// - VMID[15:8] is RES0in command parameters and must be zero in STE.S2VMID.
+ /// - 0b1 16-bit VMID supported.
+ VMID16 OFFSET(18) NUMBITS(1) [
+ NotSupported = 0,
+ Supported = 1
+ ],
+ /// Address Translation Operations supported.
+ ///
+ /// - 0b0 Address Translation Operations not supported.
+ /// - MMU_IDR0.VATOS is RES0 and all SMMU_(S_)GATOS_* registers are Reserved.
+ /// - 0b1 Address Translation Operations supported
+ ATOS OFFSET(15) NUMBITS(1) [
+ NotSupported = 0,
+ Supported = 1
+ ],
+ /// H/W translation table Access flag and Dirty state of the page updates supported.
+ ///
+ /// - 0b00 No flag updates supported.
+ /// - 0b01 Access flag update supported.
+ /// - 0b10 Access flag and Dirty state of the page update supported.
+ /// - 0b11 Access flag and Dirty state, and Access flag for Table descriptors supported.
+ HTTU OFFSET(6) NUMBITS(2) [
+ NoFlags = 0b00,
+ AccessFlag = 0b01,
+ AccessFlagDirtyState = 0b10,
+ AccessFlagDirtyStateAccessFlagTableDescriptors = 0b11
+ ],
+ /// Broadcast TLB Maintenance. Indicates support for receiving broadcast TLBI operations issued by Arm PEs in the system.
+ ///
+ /// - 0b0 Broadcast TLB maintenance not supported.
+ /// - 0b1 Boradcast TLB maintenance supported.
+ ///
+ /// This bit reflects the ability of the system, and SMMU implementation, to support broadcast maintenance. If either the SMMU, or the system, or the interconnect cannot fully support broadcast TLB maintenance, this bit reads as 0.
+ BTM OFFSET(5) NUMBITS(1) [
+ NotSupported = 0,
+ Supported = 1
+ ],
+ /// Coherent access supported to translations, structures and queues.
+ ///
+ /// - 0b0 Coherent access for translations, structures and queues is not supported.
+ /// - 0b1 IO-coherent access is supported for: • Translation table walks.
+ /// - Fetches of L1STD, STE, L1CD and CD. • Command queue, Event queue and PRI queue access.
+ /// - GERROR, CMD_SYNC, Event queue and PRI queue MSIs, if supported.
+ /// - Whether a specific access is performed in a cacheable shareable manner is dependent on the access type configured for access to structures, queues and translation table walks.
+ CHOACC OFFSET(4) NUMBITS(1) [
+ NotSupported = 0,
+ Supported = 1
+ ],
+ /// Translation table formats supported at both stage 1 and stage 2.
+ ///
+ /// - 0b00 Reserved.
+ /// - 0b01 VMSAv8-32 LPAE.
+ /// - 0b10 VMSAv8-64.
+ /// - 0b11 VMSAv8-32 LPAE and VMSAv8-64.
+ ///
+ /// TTF[0] is 0 in implementations where either SMMU_IDR3.DPT is 1 or SMMU_R_IDR3.DPT is 1.
+ TTF OFFSET(2) NUMBITS(2) [
+ Reserved = 0b00,
+ VMSAV8_32_LPAE = 0b01,
+ VMSAV8_64 = 0b10,
+ VMSAV8_32_LPAE_AND_VMSAV8_64 = 0b11
+ ],
+ /// Stage1 translation supported.
+ ///
+ /// - 0b0 Stage 1 translation not supported.
+ /// - 0b1 Stage 1 translation supported.
+ S1P OFFSET(1) NUMBITS(1) [
+ Supported = 1,
+ NotSupported = 0
+ ],
+ /// Stage2 translation supported.
+ ///
+ /// - 0b0 Stage 2 translation not supported.
+ /// - 0b1 Stage 2 translation supported.
+ S2P OFFSET(0) NUMBITS(1) [
+ Supported = 1,
+ NotSupported = 0
+ ],
+ ]
+}
+
+pub type IDR0Reg = ReadOnly;
diff --git a/src/regs/idr1.rs b/src/regs/idr1.rs
new file mode 100644
index 0000000..4c96d99
--- /dev/null
+++ b/src/regs/idr1.rs
@@ -0,0 +1,87 @@
+use tock_registers::register_bitfields;
+use tock_registers::registers::ReadOnly;
+
+register_bitfields! {u32,
+ pub IDR1 [
+ /// Support for enhanced Command queue interface.
+ ///
+ /// - 0b0 Enhanced Command queue interface not supported. SMMU_IDR6 is RES0.
+ /// - 0b1 Enhanced Command queue interface details are advertised in SMMU_IDR6.
+ ECMDQS OFFSET(31) NUMBITS(1) [
+ NotSupported = 0,
+ Supported = 1
+ ],
+ /// Table base addresses fixed.
+ ///
+ /// - 0b0 The contents of the registers SMMU_(*_)STRTAB_BASE and SMMU_(*_)STRTAB_BASE_CFG are not fixed.
+ /// - 0b1 The contents of the registers SMMU_(*_)STRTAB_BASE and SMMU_(*_)STRTAB_BASE_CFG are fixed
+ TABLES_PRESET OFFSET(30) NUMBITS(1) [
+ NotFixed = 0,
+ Fixed = 1
+ ],
+ /// Queue base addresses fixed.
+ ///
+ /// - 0b0 The contents of the registers SMMU_(*_)CMDQ_BASE, SMMU_(*_)EVENTQ_BASE, and if present, SMMU_(R_)PRIQ_BASE are not fixed.
+ /// - 0b1 The contents of the registers SMMU_(*_)CMDQ_BASE, SMMU_(*_)EVENTQ_BASE, and if present, SMMU_(R_)PRIQ_BASE are fixed.
+ QUEUES_PRESET OFFSET(29) NUMBITS(1) [
+ NotFixed = 0,
+ Fixed = 1
+ ],
+ /// Relative base pointers.
+ ///
+ /// - 0b0 When the corresponding preset field is set, base address registers report an absolute address.
+ /// - 0b1 When the corresponding preset field is set, base address registers report an address offset.
+ /// - Relative addresses are calculated using an addition of the unsigned ADDR field onto the base address of Page 0.
+ REL OFFSET(28) NUMBITS(1) [
+ Absolute = 0,
+ Relative = 1
+ ],
+ /// Incoming MemType, Shareability, allocation and transient hints override.
+ ///
+ /// - 0b0 Incoming attributes cannot be overridden before translation or by global bypass.
+ /// - 0b1 Incoming attributes can be overridden.
+ ATTR_TYPES_OVR OFFSET(27) NUMBITS(1) [
+ NotSupported = 0,
+ Supported = 1
+ ],
+ /// Incoming Data or Instruction, User or Privileged, input NS attribute override.
+ ///
+ /// - 0b0 Incoming attributes cannot be overridden before translation or by global bypass.
+ /// - 0b1 Incoming attributes can be overridden.
+ ATTR_PERMS_OVR OFFSET(26) NUMBITS(1) [
+ NotSupported = 0,
+ Supported = 1
+ ],
+ /// Maximum number of Command queue entries.
+ ///
+ /// - Maximum number of entries as log2(entries).
+ /// - Maximum value 19.
+ /// - Note: The index register values include an extra bit for wrap. Therefore a queue with 2N entries has indices of N bits, but an index register containing (N+1) bits.
+ CMDQS OFFSET(21) NUMBITS(5) [],
+ /// Maximum number of Event queue entries.
+ ///
+ /// - Maximum number of entries as log2(entries).
+ /// - Maximum value 19.
+ EVENTQS OFFSET(16) NUMBITS(5) [],
+ /// Maximum number of PRI queue entries.
+ ///
+ /// - Maximum number of entries as log2(entries).
+ /// - Maximum value 19.
+ /// - If SMMU_IDR0.PRI == 0, this field has an IMPLEMENTATION SPECIFIC value.
+ PRIQS OFFSET(11) NUMBITS(5) [],
+ /// Max bits of SubstreamID.
+ ///
+ /// - Valid range 0 to 20 inclusive, 0 meaning no substreams are supported.
+ /// - Reflects physical SubstreamID representation size, that is the SMMU cannot represent or be presented with SubstreamIDs greater than SSIDSIZE.
+ SSIDSIZE OFFSET(6) NUMBITS(5) [],
+ /// Max bits of StreamID.
+ ///
+ /// - This value is between 0 and 32 inclusive.
+ /// - Note: 0 is a legal value. In this case the SMMU supports one stream.
+ /// - This must reflect the physical StreamID size, that is the SMMU cannot represent or be presented with StreamIDs greater than SIDSIZE.
+ /// - When SMMU_IDR1.SIDSIZE >= 7, SMMU_IDR0.ST_LEVEL != 0b00.
+ SIDSIZE OFFSET(0) NUMBITS(6) [],
+ ]
+}
+
+pub type IDR1Reg = ReadOnly;
diff --git a/src/regs/mod.rs b/src/regs/mod.rs
new file mode 100644
index 0000000..ce92c0d
--- /dev/null
+++ b/src/regs/mod.rs
@@ -0,0 +1,23 @@
+mod aidr;
+mod cmdq_base;
+mod cmdq_cons;
+mod cmdq_prod;
+mod cr0;
+mod cr0ack;
+mod cr1;
+mod idr0;
+mod idr1;
+mod strtab_base;
+mod strtab_base_cfg;
+
+pub use aidr::*;
+pub use cmdq_base::*;
+pub use cmdq_cons::*;
+pub use cmdq_prod::*;
+pub use cr0::*;
+pub use cr0ack::*;
+pub use cr1::*;
+pub use idr0::*;
+pub use idr1::*;
+pub use strtab_base::*;
+pub use strtab_base_cfg::*;
diff --git a/src/regs/strtab_base.rs b/src/regs/strtab_base.rs
new file mode 100644
index 0000000..2f7e441
--- /dev/null
+++ b/src/regs/strtab_base.rs
@@ -0,0 +1,53 @@
+//! Chapter 6. Memory map and registers
+//! 6.3. Register formats
+//! 6.3.24 SMMU_STRTAB_BASE
+//!
+//! The SMMU_STRTAB_BASE characteristics are:
+//!
+//! ## Purpose
+//! Configuration of Stream table base address.
+//!
+//! ## Attributes
+//! SMMU_STRTAB_BASE is a 64-bit register.
+//! This register is part of the SMMUv3_PAGE_0 block.
+
+use tock_registers::register_bitfields;
+use tock_registers::registers::ReadWrite;
+
+register_bitfields! {u64,
+ pub STRTAB_BASE [
+ /// Bit [63] Reserved, RES0.
+ Reserved63 OFFSET(63) NUMBITS(1) [],
+ /// Read-Allocate hint.
+ ///
+ /// - 0b0 No Read-Allocate.
+ /// - 0b1 Read-Allocate.
+ RA OFFSET(62) NUMBITS(1) [
+ Disable = 0,
+ Enable = 1
+ ],
+ /// Bits [61:56] Reserved, RES0.
+ Reserved56 OFFSET(56) NUMBITS(6) [],
+ /// Physical address of Stream table base, bits [55:6].
+ ///
+ /// - Address bits above and below this field range are implied as zero.
+ /// - High-order bits of the ADDR field above the system physical address size, as reported by SMMU_IDR5.OAS, are RES0.
+ /// - Note: An implementation is not required to store these bits.
+ /// • When a Linear Stream table is used, that is when SMMU_STRTAB_BASE_CFG.FMT == 0b00, the effective base address is aligned by the SMMU to the table size, ignoring the least-significant bits in the ADDR range as required to do so:
+ /// ADDR[LOG2SIZE + 5:0] = 0.
+ /// When a 2-level Stream table is used, that is when SMMU_STRTAB_BASE_CFG.FMT == 0b01, the effective base address is aligned by the SMMU to the larger of 64 bytes or the first-level table size:
+ /// ADDR[MAX(5, (LOG2SIZE - SPLIT - 1 + 3)):0] = 0.
+ ///
+ /// The alignment of ADDR is affected by the literal value of the respective SMMU_STRTAB_BASE_CFG.LOG2SIZE field and is not limited by SIDSIZE.
+ /// Note: This means that configuring a table that is larger than required by the incoming StreamID span results in some entries being unreachable, but the table is still aligned to the configured size.
+ ///
+ /// The reset behavior of this field is:
+ /// - When SMMU_IDR1.TABLES_PRESET == 1, this field resets to an IMPLEMENTATION DEFINED value.
+ /// - Otherwise, this field resets to an UNKNOWN value.
+ ADDR OFFSET(6) NUMBITS(50) [],
+ /// Bits [5:0] Reserved, RES0.
+ Reserved0 OFFSET(0) NUMBITS(6) []
+ ]
+}
+
+pub type StrtabBaseReg = ReadWrite;
diff --git a/src/regs/strtab_base_cfg.rs b/src/regs/strtab_base_cfg.rs
new file mode 100644
index 0000000..0d48bb0
--- /dev/null
+++ b/src/regs/strtab_base_cfg.rs
@@ -0,0 +1,78 @@
+//! Chapter 6. Memory map and registers
+//! 6.3. Register formats
+//! 6.3.25 SMMU_STRTAB_BASE_CFG
+//!
+//! The SMMU_STRTAB_BASE_CFG characteristics are:
+//!
+//! ## Purpose
+//! Configuration of Stream table.
+//!
+//! ## Attributes
+//! SMMU_STRTAB_BASE_CFG is a 32-bit register.
+//!
+//! This register is part of the SMMUv3_PAGE_0 block.
+
+use tock_registers::register_bitfields;
+use tock_registers::registers::ReadWrite;
+
+register_bitfields! {u32,
+ pub STRTAB_BASE_CFG [
+ /// Bits [31:18] Reserved, RES0.
+ Reserved31 OFFSET(18) NUMBITS(14) [],
+ /// When SMMU_IDR0.ST_LEVEL != 0b00:
+ /// Format of Stream table.
+ /// - 0b00 Linear - ADDR points to an array of STEs.
+ /// - 0b01 2-level - ADDR points to an array of Level 1 Stream Table Descriptors.
+ ///
+ /// Other values are reserved, behave as 0b00.
+ ///
+ /// The reset behavior of this field is:
+ /// - When SMMU_IDR1.TABLES_PRESET == 1, this field resets to an IMPLEMENTATION DEFINED value.
+ /// - Otherwise, this field resets to an UNKNOWN value.
+ ///
+ /// Otherwise: Reserved, RES0.
+ FMT OFFSET(16) NUMBITS(2) [
+ Linear = 0b00,
+ TwoLevel = 0b01
+ ],
+ /// Bits [15:11] Reserved, RES0.
+ Reserved15 OFFSET(11) NUMBITS(5) [],
+ /// When SMMU_IDR0.ST_LEVEL != 0b00:
+ /// StreamID split point for multi-level table.
+ /// - This field determines the split point of a 2-level Stream table, selected by the number of bits at the bottom level.
+ /// - This field is IGNORED if FMT == 0b00.
+ /// - 0b00110 6 bits - 4KB leaf tables.
+ /// - 0b01000 8 bits - 16KB leaf tables.
+ /// - 0b01010 10 bits - 64KB leaf tables.
+ /// - Other values are reserved, behave as 6 (0b0110).
+ ///
+ /// - The upper-level L1STD is located using StreamID[LOG2SIZE - 1:SPLIT] and this indicates the lowest-level table which is indexed by StreamID[SPLIT - 1:0].
+ /// - For example, selecting SPLIT == 6 (0b0110) causes StreamID[5:0] to be used to index the lowest level Stream table and StreamID[LOG2SIZE - 1:6] to index the upper level table.
+ /// - Note: If SPLIT >= LOG2SIZE, a single upper-level descriptor indicates one bottom-level Stream table with 2LOG2SIZE usable entries. The L1STD.Span value’s valid range is up to SPLIT + 1, but not all of this Span is accessible, as it is not possible to use a StreamID >= 2LOG2SIZE.
+ ///
+ /// Note: Arm recommends that a Linear table, FMT == 0b00, is used instead of programming SPLIT > LOG2SIZE.
+ ///
+ /// The reset behavior of this field is:
+ /// - When SMMU_IDR1.TABLES_PRESET == 1, this field resets to an IMPLEMENTATION DEFINED value.
+ /// - Otherwise, this field resets to an UNKNOWN value.
+ ///
+ /// Otherwise: Reserved, RES0.
+ SPLIT OFFSET(6) NUMBITS(5) [
+ Split6Bits = 0b00110,
+ Split8Bits = 0b01000,
+ Split10Bits = 0b01010
+ ],
+ /// Table size as log2(entries).
+ ///
+ /// - The maximum StreamID value that can be used to index into the Stream table is 2LOG2SIZE - 1. The StreamID range is equal to the number of STEs in a linear Stream table or the maximum sum of the STEs in all second-level tables. The number of L1STDs in the upper level of a 2-level table is MAX(1, 2LOG2SIZE-SPLIT). Except for readback of a written value, the effective LOG2SIZE is <= SMMU_IDR1.SIDSIZE for the purposes of input StreamID range checking and upper/lower/linear Stream table index address calculation.
+ ///
+ /// The reset behavior of this field is:
+ /// - When SMMU_IDR1.TABLES_PRESET == 1, this field resets to an IMPLEMENTATION DEFINED value.
+ /// - Otherwise, this field resets to an UNKNOWN value.
+ LOG2SIZE OFFSET(0) NUMBITS(6) []
+ ]
+}
+
+/// SMMU_STRTAB_BASE_CFG is Guarded by SMMU_CR0.SMMUEN and must only be written when SMMU_CR0.SMMUEN == 0.
+/// See SMMU_STRTAB_BASE for detailed behavior.
+pub type StrtabBaseCfgReg = ReadWrite;
diff --git a/src/stream_table.rs b/src/stream_table.rs
new file mode 100644
index 0000000..53c9a73
--- /dev/null
+++ b/src/stream_table.rs
@@ -0,0 +1,211 @@
+use core::marker::PhantomData;
+
+use aarch64_cpu::registers::VTCR_EL2;
+
+use memory_addr::{pa, PhysAddr, PAGE_SIZE_4K};
+
+use crate::hal::PagingHandler;
+
+const STRTAB_STE_DWORDS_BITS: usize = 3;
+const STRTAB_STE_DWORDS: usize = 1 << STRTAB_STE_DWORDS_BITS;
+const STRTAB_STE_SIZE: usize = STRTAB_STE_DWORDS << 3;
+
+/// V, bit [0]
+/// STE Valid.
+///
+/// - 0b0 Structure contents are invalid. Other STE fields are IGNORED.
+/// - 0b1 Structure contents are valid. Other STE fields behave as described.
+const STRTAB_STE_0_V: u64 = 0b1 << 0;
+/// Config, bits [3:1]
+/// Stream configuration.
+///
+/// | Value | Traffic can pass? | Stage 1 | Stage 2 | Notes |
+///
+/// * 0b000 No – – Report abort to device, no event recorded.
+/// * 0b0xx No – – Reserved (behaves as 0b000)
+/// * 0b100 Yes Bypass Bypass STE.EATS value effectively 0b00
+/// * 0b101 Yes Translate Bypass S1* valid
+/// * 0b110 Yes Bypass Translate S2* valid
+/// * 0b111 Yes Translate Translate S1* and S2* valid.
+const STRTAB_STE_0_CFG_S1_BYPASS_S2_BYPASS: u64 = 0b100 << 1;
+const STRTAB_STE_0_CFG_S1_BYPASS_S2_TRANS: u64 = 0b110 << 1;
+/// SHCFG, bits [109:108]
+/// Shareability configuration.
+///
+/// - 0b00 Non-shareable
+/// - 0b01 Use incoming Shareability attribute
+/// - 0b10 Outer shareable
+/// - 0b11 Inner shareable
+const STRTAB_STE_1_SHCFG_INCOMING: u64 = 0b01 << 44; // 44 = 108 - 64
+/// S2VMID, bits [143:128]
+/// Virtual Machine Identifier
+///
+/// Marks TLB entries inserted because of translations located through this STE, differentiating them from translations belonging to different virtual machines.
+///
+const STRTAB_STE_2_S2VMID_OFFSET: u64 = 0; // 0 = 128 - 128
+/// S2T0SZ, bits [165:160]
+///
+/// Size of IPA input region covered by stage 2 translation table.
+///
+/// This field is equivalent to VTCR_EL2.T0SZ in the A-profile architecture.
+const STRTAB_STE_2_S2T0SZ_OFFSET: u64 = 32; // 32 = 160 - 128
+
+/// S2T0SZ, bits [165:160]
+/// S2SL0, bits [167:166]
+/// S2IR0, bits [169:168]
+/// S2OR0, bits [171:170]
+/// S2SH0, bits [173:172]
+/// S2TG, bits [175:174]
+/// S2PS, bits [178:176]
+/// Overall, bits [178:160] refers to the lower 19 bits of [`aarch64_cpu::registers::VTCR_EL2`].
+const STRTAB_STE_2_S2VTCR_LEN: u64 = 19;
+
+const DEFAULT_S2VTCR: u64 = VTCR_EL2::PS::PA_40B_1TB.mask()
+ | VTCR_EL2::TG0::Granule4KB.mask()
+ | VTCR_EL2::SH0::Inner.mask()
+ | VTCR_EL2::ORGN0::NormalWBRAWA.mask()
+ | VTCR_EL2::IRGN0::NormalWBRAWA.mask()
+ | VTCR_EL2::SL0.val(0b01).mask()
+ | VTCR_EL2::T0SZ.val(64 - 39).mask();
+
+/// S2AA64, bit [179]
+///
+/// Stage 2 translation table format for S2TTB0, and S_S2TTB0 if appropriate.
+///
+/// - 0b0 Use VMSAv8-32 LPAE descriptor formats. SMMU_IDR0.TTF[0] == 1
+/// - 0b0 Use VMSAv9-128 descriptor formats. SMMU_IDR5.D128 == 1
+/// - 0b1 Use VMSAv8-64 descriptor formats.
+///
+/// If stage 2 is not implemented, that is when SMMU_IDR0.S2P == 0, this field is RES0.
+const STRTAB_STE_2_S2AA64: u64 = 1 << 51; // 51 = 179 - 128
+/// S2PTW, bit [182]
+/// Protected Table Walk.
+///
+/// For an STE configured for translation at both stages, a stage 1 translation table walk access or CD fetch access made to a stage 2 page with any Device type is terminated and recorded as a stage 2 Permission fault if this field is set.
+///
+/// Note: This might provide early indication of a programming error.
+///
+/// - 0b0 If SMMU_IDR3.PTWNNC == 0: CD fetch and stage 1 translation table walks allowed to any valid stage 2 address. If SMMU_IDR3.PTWNNC == 1: A translation table access or CD fetch mapped as any Device type occurs as if it is to Normal Non-cacheable memory.
+/// - 0b1 CD fetch or Stage 1 translation table walks to stage 2 addresses mapped as any Device are terminated. A stage 2 Permission fault is recorded.
+const STRTAB_STE_2_S2PTW: u64 = 1 << 54; // 54 = 182 - 128
+/// S2R, bit [186]
+///
+/// Stage 2 fault behavior - Record.
+/// See section 5.5 Fault configuration (A, R, S bits) for a description of fault configuration.
+/// When STE.Config == 0b10x (Stage 2 disabled), {S2R, S2S} are IGNORED.
+/// If stage 2 is not implemented, that is when SMMU_IDR0.S2P == 0, this field is RES0.
+const STRTAB_STE_2_S2R: u64 = 1 << 58; // 58 = 186 - 128
+/// S2TTB, bits [247:196]
+/// In SMMUv3.1 and later, if STE.S2AA64 selects VMSAv9-128, then bits[247:196] represent the address of Stage 2 Translation Table base, bits[55:4]. Otherwise:
+/// - In SMMUv3.1 and later:
+/// – Bits[243:196] represent the address of Stage 2 Translation Table base, bits[51:4].
+/// – Bits[247:244] are RES0.
+/// - In SMMUv3.0:
+/// – Bits[239:196] represent the address of Stage 2 Translation Table base, bits[47:4].
+/// – Bits[247:240] are RES0.
+///
+/// Address bits above and below the field range are treated as zero.
+///
+/// Bits [(x-1):0] are treated as if all the bits are zero, where x is defined by the required alignment of the translation table as given in the A-profile architecture[2].
+/// Note: The SMMU effectively aligns the value in this field before use.
+const STRTAB_STE_3_S2TTB_OFF: u64 = 4;
+const STRTAB_STE_3_S2TTB_LEN: u64 = 48;
+
+const fn extract_bits(value: u64, start: u64, length: u64) -> u64 {
+ let mask = (1 << length) - 1;
+ (value >> start) & mask
+}
+
+pub struct StreamTableEntry([u64; STRTAB_STE_DWORDS]);
+
+impl StreamTableEntry {
+ pub const fn bypass_entry() -> Self {
+ Self([
+ STRTAB_STE_0_V | STRTAB_STE_0_CFG_S1_BYPASS_S2_BYPASS,
+ STRTAB_STE_1_SHCFG_INCOMING,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ ])
+ }
+
+ pub const fn s2_translated_entry(vmid: u64, s2pt_base: PhysAddr) -> Self {
+ Self([
+ STRTAB_STE_0_V | STRTAB_STE_0_CFG_S1_BYPASS_S2_TRANS,
+ 0,
+ (vmid << STRTAB_STE_2_S2VMID_OFFSET)
+ | extract_bits(DEFAULT_S2VTCR, 0, STRTAB_STE_2_S2VTCR_LEN)
+ << STRTAB_STE_2_S2T0SZ_OFFSET
+ | STRTAB_STE_2_S2AA64
+ | STRTAB_STE_2_S2PTW
+ | STRTAB_STE_2_S2R,
+ extract_bits(
+ s2pt_base.as_usize() as u64,
+ STRTAB_STE_3_S2TTB_OFF,
+ STRTAB_STE_3_S2TTB_LEN,
+ ) << STRTAB_STE_3_S2TTB_OFF,
+ 0,
+ 0,
+ 0,
+ 0,
+ ])
+ }
+}
+
+pub struct LinearStreamTable {
+ base: PhysAddr,
+ entry_count: usize,
+ _phantom: PhantomData,
+}
+
+impl LinearStreamTable {
+ pub const fn uninit() -> Self {
+ Self {
+ base: pa!(0xdead_beef),
+ entry_count: 0,
+ _phantom: PhantomData,
+ }
+ }
+
+ pub fn init(&mut self, sid_max_bits: u32) {
+ self.entry_count = 1 << sid_max_bits;
+ let size = self.entry_count * STRTAB_STE_SIZE;
+ let base = H::alloc_pages(size / PAGE_SIZE_4K).expect("Failed to allocate stream table");
+ self.base = base;
+
+ // First we just mark all entries as bypass.
+ for sid in 0..self.entry_count {
+ self.set_bypass_ste(sid);
+ }
+ }
+
+ pub fn base_addr(&self) -> PhysAddr {
+ self.base
+ }
+
+ fn ste(&self, sid: usize) -> &mut StreamTableEntry {
+ let base = self.base + sid * STRTAB_STE_SIZE;
+ unsafe { &mut *(base.as_usize() as *mut StreamTableEntry) }
+ }
+
+ fn set_bypass_ste(&self, sid: usize) {
+ let tab = self.ste(sid);
+ *tab = StreamTableEntry::bypass_entry();
+ }
+
+ pub(crate) fn set_s2_translated_ste(&self, sid: usize, vmid: usize, s2pt_base: PhysAddr) {
+ info!(
+ "write ste, sid: 0x{:x}, vmid: 0x{:x}, ste_addr:0x{:x}, root_pt: {:?}",
+ sid,
+ vmid,
+ self.base + sid * STRTAB_STE_SIZE,
+ s2pt_base
+ );
+
+ let entry = self.ste(sid);
+ *entry = StreamTableEntry::s2_translated_entry(vmid as _, s2pt_base);
+ }
+}