abyss/dev/
mmio.rs

1// Copyright 2025 Computer Architecture and Systems Lab
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Mmio interface.
16
17use crate::addressing::{Kva, Pa};
18
19/// Type for accessing mmio register.
20#[repr(transparent)]
21#[derive(Clone, Copy, Debug)]
22pub struct MmioAccessor<T, const R: bool, const W: bool>(pub *mut T);
23
24unsafe impl<T, const R: bool, const W: bool> Send for MmioAccessor<T, R, W> {}
25
26impl<T, const W: bool> MmioAccessor<T, true, W> {
27    /// Read from the register.
28    ///
29    /// # Safety
30    /// Mmio region must be mapped to the valid virtual address.
31    #[inline(always)]
32    pub fn read(&self) -> T {
33        unsafe { core::ptr::read_volatile(self.0) }
34    }
35}
36
37impl<T, const R: bool> MmioAccessor<T, R, true> {
38    /// Write to the register.
39    ///
40    /// # Safety
41    /// Mmio region must be mapped to the valid virtual address.
42    #[inline(always)]
43    pub fn write(&self, v: T) {
44        unsafe { core::ptr::write_volatile::<T>(self.0, v) }
45    }
46}
47
48/// Type for accessing array of mmio registers.
49#[derive(Clone, Copy, Debug)]
50pub struct MmioArrayAccessor<T, const R: bool, const W: bool, const SZ: usize>(
51    pub *mut T,
52    pub usize,
53);
54
55impl<T, const W: bool, const SZ: usize> MmioArrayAccessor<T, true, W, SZ> {
56    /// Read from the register.
57    ///
58    /// # Safety
59    /// Mmio region must be mapped to the valid virtual address.
60    #[inline(always)]
61    pub fn read_at(&self, idx: usize) -> T {
62        unsafe {
63            core::assert!(idx < SZ);
64            core::ptr::read_volatile((self.0 as usize + idx * self.1) as *mut T)
65        }
66    }
67}
68
69impl<T, const R: bool, const SZ: usize> MmioArrayAccessor<T, R, true, SZ> {
70    /// Write to the register.
71    ///
72    /// # Safety
73    /// Mmio region must be mapped to the valid virtual address.
74    #[inline(always)]
75    pub fn write_at(&self, idx: usize, v: T) {
76        core::assert!(idx < SZ);
77        unsafe { core::ptr::write_volatile::<T>((self.0 as usize + idx * self.1) as *mut T, v) }
78    }
79}
80
81/// Representation of Mmio area.
82#[repr(transparent)]
83#[derive(Debug)]
84pub struct MmioArea(pub core::ops::Range<Pa>);
85
86impl MmioArea {
87    /// Create a new mmio area.
88    ///
89    /// # Safety
90    /// mmio range should be valid.
91    #[inline(always)]
92    pub const unsafe fn new(n: core::ops::Range<Pa>) -> Self {
93        Self(n)
94    }
95
96    /// Get size of this mmio area.
97    #[inline(always)]
98    pub const fn size(&self) -> usize {
99        self.0.end.into_usize() - self.0.start.into_usize()
100    }
101
102    /// Activate this mmio area.
103    #[inline(always)]
104    pub fn activate(self) -> ActiveMmioArea {
105        let core::ops::Range { start, end } = self.0;
106
107        ActiveMmioArea(start.into_kva()..end.into_kva())
108    }
109
110    /// Clone this mmio area.
111    ///
112    /// # Safety
113    /// The caller must synchronize the access of the duplicated mmio area.
114    pub unsafe fn clone(&self) -> Self {
115        Self(self.0.clone())
116    }
117
118    /// Divides one mmio area into two at an index.
119    ///
120    /// The first will contain all indices from `[0, mid)` (excluding
121    /// the index `mid` itself) and the second will contain all
122    /// indices from `[mid, len)` (excluding the index `len` itself).
123    ///
124    /// # Panics
125    ///
126    /// Panics if `mid > len`.
127    pub fn split_at(self, mid: usize) -> (Self, Self) {
128        assert!(mid <= self.size());
129        let core::ops::Range { start, end } = self.0;
130        let mid = start + mid;
131        (Self(start..mid), Self(mid..end))
132    }
133}
134
135/// Represent the activated mmio area.
136#[repr(transparent)]
137#[derive(Debug)]
138pub struct ActiveMmioArea(core::ops::Range<Kva>);
139
140impl ActiveMmioArea {
141    /// Return range of this mmio area as virtual.
142    #[inline]
143    pub fn start_end(&self) -> (usize, usize) {
144        (self.0.start.into_usize(), self.0.end.into_usize())
145    }
146
147    /// Write value at `of`.
148    pub fn write_at<T: Copy>(&self, of: usize, t: T) {
149        unsafe {
150            assert!(
151                self.0.start.into_usize() + of * core::mem::size_of::<T>()
152                    < self.0.end.into_usize()
153            );
154            core::ptr::write_volatile(
155                ((self.0.start.into_usize() + of * core::mem::size_of::<T>()) as *mut T)
156                    .as_mut()
157                    .unwrap(),
158                t,
159            );
160        }
161    }
162
163    /// Read value from `of`.
164    pub fn read_at<T: Copy>(&self, of: usize) -> T {
165        unsafe {
166            assert!(
167                self.0.start.into_usize() + of * core::mem::size_of::<T>()
168                    < self.0.end.into_usize()
169            );
170            core::ptr::read_volatile(
171                ((self.0.start.into_usize() + of * core::mem::size_of::<T>()) as *mut T)
172                    .as_ref()
173                    .unwrap(),
174            )
175        }
176    }
177}
178
179#[doc(hidden)]
180#[macro_export(local_inner_macros)]
181macro_rules! __mmio_mk_register {
182    ($e:ident, $(#[$attr:meta])* $N:ident @ $off:expr_2021 => R, $T:ty; $($t:tt)*) => {
183        __mmio_mk_register!(@MAKE, $e, $(#[$attr])*, $N, $T, $off, true, false);
184        __mmio_mk_register!($e, $($t)*);
185    };
186    ($e:ident, $(#[$attr:meta])* $N:ident @ $off:expr_2021 => W, $T:ty; $($t:tt)*) => {
187        __mmio_mk_register!(@MAKE, $e, $(#[$attr])*, $N, $T, $off, false, true);
188        __mmio_mk_register!($e, $($t)*);
189    };
190    ($e:ident, $(#[$attr:meta])* $N:ident @ $off:expr_2021 => RW, $T:ty; $($t:tt)*) => {
191        __mmio_mk_register!(@MAKE, $e, $(#[$attr])*, $N, $T, $off, true, true);
192        __mmio_mk_register!($e, $($t)*);
193    };
194
195    // Array
196    ($e:ident, $(#[$attr:meta])* $N:ident @ $off:expr_2021 => R, $T:ty, $sz:expr_2021; $($t:tt)*) => {
197        __mmio_mk_register!(@MAKE, $e, $(#[$attr])*, $N, $T, core::mem::size_of::<$T>(), $off, $sz, true, false);
198        __mmio_mk_register!($e, $($t)*);
199    };
200    ($e:ident, $(#[$attr:meta])* $N:ident @ $off:expr_2021 => W, $T:ty, $sz:expr_2021; $($t:tt)*) => {
201        __mmio_mk_register!(@MAKE, $e, $(#[$attr])*, $N, $T, core::mem::size_of::<$T>(), $off, $sz, false, true);
202        __mmio_mk_register!($e, $($t)*);
203    };
204    ($e:ident, $(#[$attr:meta])* $N:ident @ $off:expr_2021 => RW, $T:ty, $sz:expr_2021; $($t:tt)*) => {
205        __mmio_mk_register!(@MAKE, $e, $(#[$attr])*, $N, $T, core::mem::size_of::<$T>(), $off, $sz, true, true);
206       __mmio_mk_register!($e, $($t)*);
207    };
208
209    // Array with Stride
210    ($e:ident, $(#[$attr:meta])* $N:ident @ $off:expr_2021, $S:expr_2021 => R, $T:ty, $sz:expr_2021; $($t:tt)*) => {
211        __mmio_mk_register!(@MAKE, $e, $(#[$attr])*, $N, $T, $S, $off, $sz, true, false);
212        __mmio_mk_register!($e, $($t)*);
213    };
214    ($e:ident, $(#[$attr:meta])* $N:ident @ $off:expr_2021, $S:expr_2021 => W, $T:ty, $sz:expr_2021; $($t:tt)*) => {
215        __mmio_mk_register!(@MAKE, $e, $(#[$attr])*, $N, $T, $S, $off, $sz, false, true);
216        __mmio_mk_register!($e, $($t)*);
217    };
218    ($e:ident, $(#[$attr:meta])* $N:ident @ $off:expr_2021, $S:expr_2021 => RW, $T:ty, $sz:expr_2021; $($t:tt)*) => {
219        __mmio_mk_register!(@MAKE, $e, $(#[$attr])*, $N, $T, $S, $off, $sz, true, true);
220       __mmio_mk_register!($e, $($t)*);
221    };
222    (@MAKE, $e:ident, $(#[$attr:meta])*, $N:ident, $T: ty, $off:expr_2021, $r:expr_2021, $w:expr_2021) => {
223        impl $e {
224            $(#[$attr])*
225            #[inline(always)]
226            #[allow(non_snake_case)]
227            #[allow(dead_code)]
228            pub fn $N(&self) -> $crate::dev::mmio::MmioAccessor<$T, $r, $w> {
229                let core::ops::Range { start, end } = self.0;
230                core::assert!(
231                    (start + $off) + core::mem::size_of::<$T>() <= end,
232                    "{:x} {:x} {:x} {:x} {:?}",
233                    start,
234                    start + $off,
235                    (start + $off) + core::mem::size_of::<$T>(),
236                    end,
237                    (start + $off) + core::mem::size_of::<$T>() <= end,
238                );
239                $crate::dev::mmio::MmioAccessor((start + $off) as *mut $T)
240            }
241        }
242    };
243    (@MAKE, $e:ident, $(#[$attr:meta])*, $N:ident, $T:ty, $S:expr_2021, $off:expr_2021, $sz:expr_2021, $r:expr_2021, $w:expr_2021) => {
244        impl $e {
245            $(#[$attr])*
246            #[inline(always)]
247            #[allow(non_snake_case)]
248            pub fn $N(&self) -> $crate::dev::mmio::MmioArrayAccessor<$T, $r, $w, $sz> {
249                let core::ops::Range { start, end } = self.0;
250                core::assert!(start + $off + $S * $sz - $sz < end);
251
252                $crate::dev::mmio::MmioArrayAccessor((start + $off) as *mut $T, $S)
253            }
254        }
255    };
256    ($e:expr_2021,) => ();
257}
258
259#[doc(hidden)]
260#[macro_export(local_inner_macros)]
261macro_rules! __mmio_mk_method {
262    ($N:ident) => {
263        impl $N {
264            /// Create new mmio area.
265            pub fn new_from_mmio_area(area: $crate::dev::mmio::MmioArea) -> Self {
266                let core::ops::Range { start, end } = area.0;
267                Self(start.into_kva().into_usize()..end.into_kva().into_usize())
268            }
269
270            /// Get starting virtual address.
271            #[inline]
272            #[allow(dead_code)]
273            pub fn kva(&self) -> $crate::addressing::Kva {
274                $crate::addressing::Kva::new(self.0.start).unwrap()
275            }
276        }
277    };
278}
279
280/// Make mmio register groups.
281#[macro_export]
282macro_rules! mmio {
283    ($(#[$attr:meta])* $N:ident: $($t:tt)*) => {
284        $(#[$attr])*
285        struct $N(core::ops::Range<usize>);
286
287        $crate::__mmio_mk_method!($N);
288        $crate::__mmio_mk_register!($N, $($t)*);
289    };
290
291    ($(#[$attr:meta])* pub $N:ident: $($t:tt)*) => {
292        $(#[$attr])*
293        pub struct $N(core::ops::Range<usize>);
294
295        $crate::__mmio_mk_method!($N);
296        $crate::__mmio_mk_register!($N, $($t)*);
297    };
298
299    ($(#[$attr:meta])* pub ($($vis:tt)+) $N:ident: $($t:tt)*) => {
300        $(#[$attr])*
301        #[allow(non_snake_case, dead_code)]
302        pub ($($vis:tt)+) struct $N(core::ops::Range<usize>);
303
304        $crate::__mmio_mk_method!($N);
305        $crate::__mmio_mk_register!($N, $($t)*);
306    };
307}