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