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