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