Skip to main content

mmio_api/
lib.rs

1#![cfg_attr(target_os = "none", no_std)]
2
3use core::{fmt::Display, ops::Deref, ptr::NonNull, sync::atomic::Ordering};
4
5#[cfg(all(axtest, feature = "axtest"))]
6extern crate alloc;
7
8#[cfg(all(axtest, feature = "axtest"))]
9pub mod axtest;
10
11#[derive(thiserror::Error, Debug)]
12pub enum MapError {
13    #[error("Invalid MMIO address or size")]
14    Invalid,
15    #[error("Failed to allocate memory for MMIO mapping")]
16    NoMemory,
17    #[error("MMIO address is already in use")]
18    Busy,
19}
20
21pub trait MmioOp: Sync + Send + 'static {
22    fn ioremap(&self, addr: MmioAddr, size: usize) -> Result<MmioRaw, MapError>;
23    fn iounmap(&self, mmio: &MmioRaw);
24}
25
26static mut MMIO_OP: Option<&'static dyn MmioOp> = None;
27static INIT: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false);
28
29pub fn init(mmio_op: &'static dyn MmioOp) {
30    if INIT
31        .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
32        .is_err()
33    {
34        return;
35    }
36
37    unsafe {
38        MMIO_OP = Some(mmio_op);
39    }
40}
41
42/// # Safety
43///
44/// Caller should manually unmap the returned `Mmio` by calling `iounmap` when it is no longer needed.
45pub unsafe fn ioremap_raw(addr: MmioAddr, size: usize) -> Result<MmioRaw, MapError> {
46    let mmio_op = unsafe { MMIO_OP.expect("MmioOp is not initialized") };
47    mmio_op.ioremap(addr, size)
48}
49
50/// # Safety
51///
52/// Caller must ensure that `mmio` was previously mapped by `ioremap`.
53pub unsafe fn iounmap(mmio: &MmioRaw) {
54    let mmio_op = unsafe { MMIO_OP.expect("MmioOp is not initialized") };
55    mmio_op.iounmap(mmio);
56}
57
58pub fn ioremap(addr: MmioAddr, size: usize) -> Result<Mmio, MapError> {
59    let mmio = unsafe { ioremap_raw(addr, size)? };
60    Ok(Mmio(mmio))
61}
62
63/// Physical MMIO Address
64#[derive(
65    Default,
66    derive_more::From,
67    derive_more::Into,
68    Clone,
69    Copy,
70    derive_more::Debug,
71    derive_more::Display,
72    PartialEq,
73    Eq,
74    PartialOrd,
75    Ord,
76    Hash,
77)]
78#[repr(transparent)]
79#[debug("PhysAddr({_0:#x})")]
80#[display("{_0:#x}")]
81pub struct MmioAddr(usize);
82
83impl MmioAddr {
84    pub fn as_usize(&self) -> usize {
85        self.0
86    }
87}
88
89impl From<u64> for MmioAddr {
90    fn from(value: u64) -> Self {
91        MmioAddr(value as usize)
92    }
93}
94
95#[derive(Debug, Clone)]
96pub struct MmioRaw {
97    phys: MmioAddr,
98    virt: NonNull<u8>,
99    size: usize,
100}
101
102impl MmioRaw {
103    /// # Safety
104    ///
105    /// Caller must ensure that `virt` is a valid mapping for the given `phys` and `size`.
106    pub unsafe fn new(phys: MmioAddr, virt: NonNull<u8>, size: usize) -> Self {
107        MmioRaw { phys, virt, size }
108    }
109
110    pub fn phys_addr(&self) -> MmioAddr {
111        self.phys
112    }
113
114    pub fn as_slice(&self) -> &[u8] {
115        unsafe { core::slice::from_raw_parts(self.virt.as_ptr(), self.size) }
116    }
117
118    pub fn as_ptr(&self) -> *mut u8 {
119        self.virt.as_ptr()
120    }
121
122    pub fn as_nonnull_ptr(&self) -> NonNull<u8> {
123        self.virt
124    }
125
126    pub fn size(&self) -> usize {
127        self.size
128    }
129
130    pub fn read<T>(&self, offset: usize) -> T {
131        assert!(offset < self.size);
132        unsafe { self.virt.add(offset).cast::<T>().read_volatile() }
133    }
134
135    pub fn write<T>(&self, offset: usize, value: T) {
136        assert!(offset < self.size);
137        unsafe { self.virt.add(offset).cast::<T>().write_volatile(value) }
138    }
139}
140
141pub struct Mmio(MmioRaw);
142
143impl Deref for Mmio {
144    type Target = MmioRaw;
145
146    fn deref(&self) -> &Self::Target {
147        &self.0
148    }
149}
150
151impl Drop for Mmio {
152    fn drop(&mut self) {
153        let mmio_op = unsafe { MMIO_OP.expect("MmioOp is not initialized") };
154        mmio_op.iounmap(self);
155    }
156}
157
158unsafe impl Send for MmioRaw {}
159unsafe impl Sync for MmioRaw {}
160
161impl Display for MmioRaw {
162    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
163        write!(
164            f,
165            "Mmio [{}, {:#x}) -> virt: {:#p}",
166            self.phys,
167            self.phys.0 + self.size,
168            self.virt
169        )
170    }
171}
172
173#[cfg(all(test, not(target_os = "none")))]
174mod tests {
175    use super::MmioRaw;
176
177    struct DummyMmioOp;
178    impl super::MmioOp for DummyMmioOp {
179        fn ioremap(&self, addr: super::MmioAddr, size: usize) -> Result<MmioRaw, super::MapError> {
180            Ok(MmioRaw {
181                phys: addr,
182                virt: core::ptr::NonNull::dangling(),
183                size,
184            })
185        }
186
187        fn iounmap(&self, _mmio: &MmioRaw) {}
188    }
189
190    #[test]
191    fn test_mmio_new() {
192        super::init(&DummyMmioOp);
193
194        let addr = MmioRaw {
195            phys: super::MmioAddr(0x1000),
196            virt: core::ptr::NonNull::dangling(),
197            size: 0x100,
198        };
199        println!("Mmio address: {:?}", addr);
200        println!("Mmio address display: {}", addr);
201    }
202}