dev_mmio/
lib.rs

1//! Memory mapped IO
2
3#![no_std]
4#![warn(missing_docs)]
5#![cfg_attr(feature = "cargo-clippy", allow(clippy::style))]
6
7use core::{fmt, ptr, marker};
8
9///Memory mapped raw pointer
10pub struct RawPtr<'a, T> {
11    ///Pointer
12    pub ptr: *mut T,
13    _lifetime: marker::PhantomData<&'a mut T>,
14}
15
16#[repr(transparent)]
17///Memory mapped IO
18pub struct MemoryMap<T> {
19    ptr: *mut T
20}
21
22impl<T> MemoryMap<T> {
23    #[inline]
24    ///Reads data
25    pub fn read(&self) -> T {
26        unsafe {
27            ptr::read_volatile(self.ptr)
28        }
29    }
30
31    #[inline]
32    ///Writes data
33    pub fn write(&mut self, val: T) {
34        unsafe {
35            ptr::write_volatile(self.ptr, val)
36        }
37    }
38
39    #[inline]
40    ///Gives callback to accept value to return modified value to write.
41    pub fn read_and_write<F: FnOnce(T) -> T>(&mut self, cb: F) {
42        let new = cb(self.read());
43        self.write(new);
44    }
45
46    #[inline]
47    #[allow(clippy::needless_lifetimes)]
48    ///Access raw pointer
49    ///
50    ///Note that, ownership is not transferred
51    pub fn as_ref<'a>(&'a mut self) -> RawPtr<'a, T> {
52        RawPtr {
53            ptr: self.ptr,
54            _lifetime: marker::PhantomData
55        }
56    }
57
58    #[allow(unused)]
59    #[inline]
60    ///Opens memory map.
61    ///
62    ///## Arguments
63    ///
64    ///- `offset` - Offset within memory to start.
65    ///- `fd` - File description. -1 for anonymous.
66    ///- `prot` - Memory protection. Specifies operations to be expected. At the very least must be `PROT_READ | PROT_WRITE`
67    ///- `flags` - Specifies whether changes to the mapping are visible across forks. Must be `MAP_ANON` for anonymous.
68    pub unsafe fn open_file_raw(offset: libc::off_t, fd: libc::c_int, prot: libc::c_int, flags: libc::c_int) -> Option<Self> {
69        #[cfg(unix)]
70        {
71            use core::mem;
72
73            let page_size = libc::sysconf(libc::_SC_PAGESIZE) as libc::off_t;
74            let offset_mask = (page_size - 1);
75            let page_mask = !0u32 as libc::off_t ^ offset_mask;
76
77            let ptr = libc::mmap(ptr::null_mut(), mem::size_of::<T>(), prot, flags, fd, offset & page_mask);
78
79            if ptr == libc::MAP_FAILED {
80                return None;
81            }
82
83            Some(Self {
84                ptr: unsafe {
85                    (ptr as *mut u8).add(offset as usize & offset_mask as usize) as *mut _
86                }
87            })
88        }
89
90        #[cfg(not(unix))]
91        None
92    }
93
94    ///Creates anonymous memory mapping
95    pub fn anonymous() -> Option<Self> {
96        #[cfg(unix)]
97        unsafe {
98            Self::open_file_raw(0, -1, libc::PROT_READ | libc::PROT_WRITE, libc::MAP_ANON | libc::MAP_SHARED)
99        }
100
101        #[cfg(not(unix))]
102        None
103    }
104
105    #[allow(unused)]
106    ///Creates memory mapping on `/dev/mem` which accesses physical memory
107    ///
108    ///## Arguments
109    ///
110    ///- `offset` - Offset within memory to start.
111    ///
112    ///Returns `None` on error, further details can be examined by checking last IO error.
113    pub unsafe fn dev_mem(offset: libc::off_t) -> Option<Self> {
114        #[cfg(unix)]
115        {
116            const DEV_MEM: [u8; 9] = *b"/dev/mem\0";
117            let fd = libc::open(DEV_MEM.as_ptr() as _, libc::O_RDWR | libc::O_SYNC);
118            if fd == -1 {
119                return None;
120            }
121
122            let result = Self::open_file_raw(offset, fd, libc::PROT_READ | libc::PROT_WRITE, libc::MAP_SHARED);
123            libc::close(fd);
124            result
125        }
126
127        #[cfg(not(unix))]
128        None
129    }
130}
131
132impl<T> Drop for MemoryMap<T> {
133    #[inline]
134    fn drop(&mut self) {
135        if self.ptr.is_null() {
136            return;
137        }
138
139        #[cfg(unix)]
140        {
141            use core::mem;
142
143            let page_size = unsafe {
144                libc::sysconf(libc::_SC_PAGESIZE) as u32
145            };
146            let offset_mask = page_size - 1;
147            let page_mask: u32 = !0u32 ^ offset_mask;
148            let base_addr = (self.ptr as usize) & page_mask as usize;
149            unsafe {
150                libc::munmap(mem::transmute(base_addr), mem::size_of::<T>());
151            }
152        }
153    }
154}
155
156impl<T> fmt::Pointer for MemoryMap<T> {
157    #[inline(always)]
158    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
159        fmt::Pointer::fmt(&self.ptr, fmt)
160    }
161}
162
163impl<T> fmt::Debug for MemoryMap<T> {
164    #[inline(always)]
165    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
166        fmt::Debug::fmt(&self.ptr, fmt)
167    }
168}
169
170unsafe impl<T> Send for MemoryMap<T> {
171}
172
173unsafe impl<T> Sync for MemoryMap<T> {
174}