-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[init] initial commit, basic smmuv3 functions
- Loading branch information
0 parents
commit 1f3fa92
Showing
19 changed files
with
1,435 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/target | ||
/.vscode | ||
.DS_Store | ||
Cargo.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>; |
Oops, something went wrong.