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
42pub 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
50pub 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#[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 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}