Skip to content

Commit

Permalink
[init] initial commit, basic smmuv3 functions
Browse files Browse the repository at this point in the history
  • Loading branch information
hky1999 committed Nov 25, 2024
0 parents commit 1f3fa92
Show file tree
Hide file tree
Showing 19 changed files with 1,435 additions and 0 deletions.
55 changes: 55 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: CI

on: [push, pull_request]

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

doc:
runs-on: ubuntu-latest
strategy:
fail-fast: false
permissions:
contents: write
env:
default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }}
RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- name: Build docs
continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }}
run: |
cargo doc --no-deps --all-features
printf '<meta http-equiv="refresh" content="0;url=%s/index.html">' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html
- name: Deploy to Github Pages
if: ${{ github.ref == env.default-branch }}
uses: JamesIves/github-pages-deploy-action@v4
with:
single-commit: true
branch: gh-pages
folder: target/doc
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/target
/.vscode
.DS_Store
Cargo.lock
12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
14 changes: 14 additions & 0 deletions src/hal.rs
Original file line number Diff line number Diff line change
@@ -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<PhysAddr>;
/// 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;
}
183 changes: 183 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<u32>),
(0x000C => IDR3: ReadOnly<u32>),
(0x0010 => IDR4: ReadOnly<u32>),
(0x0014 => IDR5: ReadOnly<u32>),
(0x0018 => IIDR: ReadOnly<u32>),
(0x001C => AIDR: AIDRReg),
(0x0020 => CR0: Cr0Reg),
(0x0024 => CR0ACK: Cr0AckReg),
(0x0028 => CR1: Cr1Reg),
(0x002c => CR2: ReadWrite<u32>),
(0x0030 => _reserved0),
(0x0050 => IRQ_CTRL: ReadWrite<u32>),
(0x0054 => IRQ_CTRLACK: ReadOnly<u32>),
(0x0058 => _reserved1),
(0x0060 => GERROR: ReadOnly<u32>),
(0x0064 => GERRORN: ReadWrite<u32>),
(0x0068 => GERROR_IRQ_CFG0: ReadWrite<u64>),
(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<u64>),
(0x00a8 => _reserved4),
(0x00b0 => EVENTQ_IRQ_CFG0: ReadWrite<u64>),
(0x00b8 => EVENTQ_IRQ_CFG1: ReadWrite<u32>),
(0x00bc => EVENTQ_IRQ_CFG2: ReadWrite<u32>),
(0x00c0 => _reserved5),
(0x100a8 => EVENTQ_PROD: ReadWrite<u32>),
(0x100ac => EVENTQ_CONS: ReadWrite<u32>),
(0x100b0 => _reserved6),
(0x20000 => @END),
}
}

pub struct SMMUv3<H: PagingHandler> {
base: NonNull<SMMUv3Page0Regs>,
stream_table: LinearStreamTable<H>,
cmd_queue: Queue<H>,
}

unsafe impl<H: PagingHandler> Send for SMMUv3<H> {}
unsafe impl<H: PagingHandler> Sync for SMMUv3<H> {}

impl<H: PagingHandler> SMMUv3<H> {
/// 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);
}
}
49 changes: 49 additions & 0 deletions src/queue.rs
Original file line number Diff line number Diff line change
@@ -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<H: PagingHandler> {
base: PhysAddr,
queue_size: u32,
prod: u32,
cons: u32,
_marker: core::marker::PhantomData<H>,
}

impl<H: PagingHandler> Queue<H> {
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
}
}
29 changes: 29 additions & 0 deletions src/regs/aidr.rs
Original file line number Diff line number Diff line change
@@ -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<u32, AIDR::Register>;
Loading

0 comments on commit 1f3fa92

Please sign in to comment.