1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
//! Wrapper around a mmio value //! //! Defines a pointer that should always be accessed by volatile reads/writes. //! //! Stolen from [Redox OS](https://gitlab.redox-os.org/redox-os/syscall/blob/master/src/io/mmio.rs). use core::ptr::{read_volatile, write_volatile}; use core::mem::MaybeUninit; use core::fmt::{Debug, Formatter, Error}; use super::Io; /// A value that can only be accessed volatilely. /// /// Generally used behind a pointer, as such: /// /// ``` /// use sunrise_libutils::io::{Io, Mmio}; /// /// /// Layout of Mmio registers of a random device. /// /// /// /// This struct is repr packed so its field are not re-ordered, /// /// and no undesired padding is added. /// /// /// /// Be careful though, in rust reading an unaligned field is undefined behaviour, /// /// so you must make sure it is correctly aligned. /// #[repr(packed)] /// struct DeviceFooRegisters { /// register_control: Mmio<u16>, /// register_command: Mmio<u16>, /// } /// /// # let mut device_foo_registers: DeviceFooRegisters = DeviceFooRegisters { /// # register_control: Mmio::new(), /// # register_command: Mmio::new(), /// # }; /// /// let device_address = 0xabcdef00 as *mut DeviceFooRegisters; /// # let device_address = &mut device_foo_registers as *mut DeviceFooRegisters; /// /// let device: &mut DeviceFooRegisters = unsafe { /// // safety: make sure that device_address is valid and we're not violating /// // rust's aliasing rules. /// device_address.as_mut().unwrap() /// }; /// /// let status = device.register_control.read(); /// device.register_command.write(0xF00D); /// ``` // todo: Mmio<T> UnsafeCell // body: Figure out if Mmio<T> should implement UnsafeCell. // body: Does this mean that, just like atomic, write can take self by const reference only ? // body: But is a Mmio<T> actually atomic ? // body: // body: Forward all these questions to @roblabla. // body: // body: Note: // body: // body: see [volatile cell](https://docs.rs/vcell/0.1.0/src/vcell/lib.rs.html#18-20) // body: and [this discussion](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/33) #[repr(packed)] pub struct Mmio<T> { /// The value. Can only be accessed through .read() value: MaybeUninit<T>, } impl<T> Mmio<T> { /// Create a new Mmio without initializing. /// /// Mostly unused, you would almost always get a Mmio /// by casting a raw pointer to a &mut Mmio. #[allow(clippy::new_without_default)] // because of Redox. pub fn new() -> Self { Mmio { value: MaybeUninit::uninit(), } } } impl<T> Io for Mmio<T> where T: Copy { type Value = T; /// Performs a volatile read of the value. fn read(&self) -> T { unsafe { read_volatile(self.value.as_ptr()) } } /// Performs a volatile write of the value. fn write(&mut self, value: T) { unsafe { write_volatile(self.value.as_mut_ptr(), value) }; } } impl<T> Debug for Mmio<T> where T: Copy + Debug { /// Debug volatilely reads `value`. fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { fmt.debug_struct("Mmio") .field("value", &self.read()) .finish() } }