1 /// An MMIO register which can only be read from.
2 #[derive(Default)]
3 #[repr(transparent)]
4 pub struct ReadOnly<T: Copy>(pub(crate) T);
5 
6 impl<T: Copy> ReadOnly<T> {
7     /// Construct a new instance for testing.
new(value: T) -> Self8     pub const fn new(value: T) -> Self {
9         Self(value)
10     }
11 }
12 
13 /// An MMIO register which can only be written to.
14 #[derive(Default)]
15 #[repr(transparent)]
16 pub struct WriteOnly<T: Copy>(pub(crate) T);
17 
18 /// An MMIO register which may be both read and written.
19 #[derive(Default)]
20 #[repr(transparent)]
21 pub struct Volatile<T: Copy>(T);
22 
23 impl<T: Copy> Volatile<T> {
24     /// Construct a new instance for testing.
new(value: T) -> Self25     pub const fn new(value: T) -> Self {
26         Self(value)
27     }
28 }
29 
30 /// A trait implemented by MMIO registers which may be read from.
31 pub trait VolatileReadable<T> {
32     /// Performs a volatile read from the MMIO register.
vread(self) -> T33     unsafe fn vread(self) -> T;
34 }
35 
36 #[cfg(not(target_arch = "aarch64"))]
37 impl<T: Copy> VolatileReadable<T> for *const ReadOnly<T> {
vread(self) -> T38     unsafe fn vread(self) -> T {
39         self.read_volatile().0
40     }
41 }
42 
43 #[cfg(not(target_arch = "aarch64"))]
44 impl<T: Copy> VolatileReadable<T> for *const Volatile<T> {
vread(self) -> T45     unsafe fn vread(self) -> T {
46         self.read_volatile().0
47     }
48 }
49 
50 /// A trait implemented by MMIO registers which may be written to.
51 pub trait VolatileWritable<T> {
52     /// Performs a volatile write to the MMIO register.
vwrite(self, value: T)53     unsafe fn vwrite(self, value: T);
54 }
55 
56 #[cfg(not(target_arch = "aarch64"))]
57 impl<T: Copy> VolatileWritable<T> for *mut WriteOnly<T> {
vwrite(self, value: T)58     unsafe fn vwrite(self, value: T) {
59         (self as *mut T).write_volatile(value)
60     }
61 }
62 
63 #[cfg(not(target_arch = "aarch64"))]
64 impl<T: Copy> VolatileWritable<T> for *mut Volatile<T> {
vwrite(self, value: T)65     unsafe fn vwrite(self, value: T) {
66         (self as *mut T).write_volatile(value)
67     }
68 }
69 
70 #[cfg(target_arch = "aarch64")]
71 mod aarch64_mmio {
72     use super::{ReadOnly, Volatile, VolatileReadable, VolatileWritable, WriteOnly};
73     use crate::{device::net::Status, transport::DeviceStatus};
74     use core::arch::asm;
75 
76     macro_rules! asm_mmio_write {
77         ($t:ty, $assembly:literal) => {
78             impl VolatileWritable<$t> for *mut WriteOnly<$t> {
79                 unsafe fn vwrite(self, value: $t) {
80                     asm!(
81                         $assembly,
82                         value = in(reg) value,
83                         ptr = in(reg) (self as *mut $t),
84                     );
85                 }
86             }
87 
88             impl VolatileWritable<$t> for *mut Volatile<$t> {
89                 unsafe fn vwrite(self, value: $t) {
90                     asm!(
91                         $assembly,
92                         value = in(reg) value,
93                         ptr = in(reg) (self as *mut $t),
94                     );
95                 }
96             }
97         };
98     }
99 
100     macro_rules! asm_mmio_read {
101         ($t:ty, $assembly:literal) => {
102             impl VolatileReadable<$t> for *const ReadOnly<$t> {
103                 unsafe fn vread(self) -> $t {
104                     let value;
105                     asm!(
106                         $assembly,
107                         value = out(reg) value,
108                         ptr = in(reg) (self as *const $t),
109                     );
110                     value
111                 }
112             }
113 
114             impl VolatileReadable<$t> for *const Volatile<$t> {
115                 unsafe fn vread(self) -> $t {
116                     let value;
117                     asm!(
118                         $assembly,
119                         value = out(reg) value,
120                         ptr = in(reg) (self as *const $t),
121                     );
122                     value
123                 }
124             }
125         };
126     }
127 
128     asm_mmio_write!(u8, "strb {value:w}, [{ptr}]");
129     asm_mmio_write!(u16, "strh {value:w}, [{ptr}]");
130     asm_mmio_write!(u32, "str {value:w}, [{ptr}]");
131     asm_mmio_write!(u64, "str {value:x}, [{ptr}]");
132 
133     impl VolatileWritable<DeviceStatus> for *mut WriteOnly<DeviceStatus> {
vwrite(self, value: DeviceStatus)134         unsafe fn vwrite(self, value: DeviceStatus) {
135             let value: u32 = value.bits();
136             asm!(
137                 "str {value:w}, [{ptr}]",
138                 value = in(reg) value,
139                 ptr = in(reg) (self as *mut u32),
140             );
141         }
142     }
143 
144     impl VolatileWritable<DeviceStatus> for *mut Volatile<DeviceStatus> {
vwrite(self, value: DeviceStatus)145         unsafe fn vwrite(self, value: DeviceStatus) {
146             let value: u32 = value.bits();
147             asm!(
148                 "str {value:w}, [{ptr}]",
149                 value = in(reg) value,
150                 ptr = in(reg) (self as *mut u32),
151             );
152         }
153     }
154 
155     asm_mmio_read!(u8, "ldrb {value:w}, [{ptr}]");
156     asm_mmio_read!(u16, "ldrh {value:w}, [{ptr}]");
157     asm_mmio_read!(u32, "ldr {value:w}, [{ptr}]");
158     asm_mmio_read!(u64, "ldr {value:x}, [{ptr}]");
159 
160     impl VolatileReadable<DeviceStatus> for *const ReadOnly<DeviceStatus> {
vread(self) -> DeviceStatus161         unsafe fn vread(self) -> DeviceStatus {
162             let value: u32;
163             asm!(
164                 "ldr {value:w}, [{ptr}]",
165                 value = out(reg) value,
166                 ptr = in(reg) (self as *const u32),
167             );
168             DeviceStatus::from_bits_retain(value)
169         }
170     }
171 
172     impl VolatileReadable<DeviceStatus> for *const Volatile<DeviceStatus> {
vread(self) -> DeviceStatus173         unsafe fn vread(self) -> DeviceStatus {
174             let value: u32;
175             asm!(
176                 "ldr {value:w}, [{ptr}]",
177                 value = out(reg) value,
178                 ptr = in(reg) (self as *const u32),
179             );
180             DeviceStatus::from_bits_retain(value)
181         }
182     }
183 
184     impl VolatileReadable<Status> for *const ReadOnly<Status> {
vread(self) -> Status185         unsafe fn vread(self) -> Status {
186             let value: u16;
187             asm!(
188                 "ldrh {value:w}, [{ptr}]",
189                 value = out(reg) value,
190                 ptr = in(reg) (self as *const u16),
191             );
192             Status::from_bits_retain(value)
193         }
194     }
195 
196     impl VolatileReadable<Status> for *const Volatile<Status> {
vread(self) -> Status197         unsafe fn vread(self) -> Status {
198             let value: u16;
199             asm!(
200                 "ldrh {value:w}, [{ptr}]",
201                 value = out(reg) value,
202                 ptr = in(reg) (self as *const u16),
203             );
204             Status::from_bits_retain(value)
205         }
206     }
207 
208     impl<const SIZE: usize> VolatileReadable<[u8; SIZE]> for *const ReadOnly<[u8; SIZE]> {
vread(self) -> [u8; SIZE]209         unsafe fn vread(self) -> [u8; SIZE] {
210             let mut value = [0; SIZE];
211             for i in 0..SIZE {
212                 asm!(
213                     "ldrb {value:w}, [{ptr}]",
214                     value = out(reg) value[i],
215                     ptr = in(reg) (self as *const u8).add(i),
216                 );
217             }
218             value
219         }
220     }
221 }
222 
223 /// Performs a volatile read from the given field of pointer to a struct representing an MMIO region.
224 ///
225 /// # Usage
226 /// ```compile_fail
227 /// # use core::ptr::NonNull;
228 /// # use virtio_drivers::volatile::{ReadOnly, volread};
229 /// struct MmioDevice {
230 ///   field: ReadOnly<u32>,
231 /// }
232 ///
233 /// let device: NonNull<MmioDevice> = NonNull::new(0x1234 as *mut MmioDevice).unwrap();
234 /// let value = unsafe { volread!(device, field) };
235 /// ```
236 macro_rules! volread {
237     ($nonnull:expr, $field:ident) => {
238         $crate::volatile::VolatileReadable::vread(core::ptr::addr_of!((*$nonnull.as_ptr()).$field))
239     };
240 }
241 
242 /// Performs a volatile write to the given field of pointer to a struct representing an MMIO region.
243 ///
244 /// # Usage
245 /// ```compile_fail
246 /// # use core::ptr::NonNull;
247 /// # use virtio_drivers::volatile::{WriteOnly, volread};
248 /// struct MmioDevice {
249 ///   field: WriteOnly<u32>,
250 /// }
251 ///
252 /// let device: NonNull<MmioDevice> = NonNull::new(0x1234 as *mut MmioDevice).unwrap();
253 /// unsafe { volwrite!(device, field, 42); }
254 /// ```
255 macro_rules! volwrite {
256     ($nonnull:expr, $field:ident, $value:expr) => {
257         $crate::volatile::VolatileWritable::vwrite(
258             core::ptr::addr_of_mut!((*$nonnull.as_ptr()).$field),
259             $value,
260         )
261     };
262 }
263 
264 pub(crate) use volread;
265 pub(crate) use volwrite;
266