diff --git a/libutils/src/io.rs b/libutils/src/io.rs deleted file mode 100644 index 5040c0cba..000000000 --- a/libutils/src/io.rs +++ /dev/null @@ -1,187 +0,0 @@ -//! The IO interface -//! -//! Copied from [redox io](https://gitlab.redox-os.org/redox-os/syscall/blob/master/src/io/io.rs) - -use core::cmp::PartialEq; -use core::ops::{BitAnd, BitOr, Not}; - -/// The Io trait allows for accessing device IO in a generic way, abstracting -/// over different IO accesses (Port IO and Memory Mapped IO). -pub trait Io { - /// The width of the IO access. - /// Should be a primitive type like u8, u16, u32... - type Value: Copy + PartialEq + BitAnd + BitOr + Not; - - /// Reads from this Io. - fn read(&self) -> Self::Value; - - /// Writes `value` to this Io. - fn write(&mut self, value: Self::Value); - - /// Read from this Io, and mask the value with `flags`. - #[inline(always)] - fn readf(&self, flags: Self::Value) -> bool { - (self.read() & flags) as Self::Value == flags - } - - /// Mask `value` with `flags`, and write it to this device address. Note that - /// this causes a read! - #[inline(always)] - fn writef(&mut self, flags: Self::Value, value: bool) { - let tmp: Self::Value = if value { - self.read() | flags - }else { - self.read() & !flags - }; - self.write(tmp); - } -} - -/// A read-only wrapper around an IO device. -#[derive(Debug)] -#[allow(clippy::missing_docs_in_private_items)] -pub struct ReadOnly { - inner: I -} - -impl ReadOnly { - /// Create a read-only wrapper around the IO device address. - pub const fn new(inner: I) -> ReadOnly { - ReadOnly { - inner: inner - } - } -} - -impl ReadOnly { - /// Reads from this Io. - #[inline(always)] - pub fn read(&self) -> I::Value { - self.inner.read() - } - - /// Read from this Io, and mask the value with `flags`. - #[inline(always)] - pub fn readf(&self, flags: I::Value) -> bool { - self.inner.readf(flags) - } -} - -/// An Io that we can only write to. -#[derive(Debug)] -#[allow(clippy::missing_docs_in_private_items)] -pub struct WriteOnly { - inner: I -} - -impl WriteOnly { - /// Creates a WriteOnly Io. - pub const fn new(inner: I) -> WriteOnly { - WriteOnly { - inner: inner - } - } -} - -impl WriteOnly { - /// Writes `value` to this Io. - #[inline(always)] - pub fn write(&mut self, value: I::Value) { - self.inner.write(value) - } -} - -use core::marker::PhantomData; - -/// Port IO accessor. -#[cfg(target_arch = "x86")] -#[derive(Copy, Clone, Debug)] -pub struct Pio { - /// IO-space address this Pio reads from. - port: u16, - /// The word size of this pointer. Should be u8, u16 or u32. - value: PhantomData, -} - -#[cfg(target_arch = "x86")] -impl Pio { - /// Create a PIO from a given port - pub const fn new(port: u16) -> Self { - Pio:: { - port: port, - value: PhantomData, - } - } -} - -/// Read/Write for byte PIO -#[cfg(target_arch = "x86")] -impl Io for Pio { - type Value = u8; - - /// Read - #[inline(always)] - fn read(&self) -> u8 { - let value: u8; - unsafe { - asm!("in $0, $1" : "={al}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile"); - } - value - } - - /// Write - #[inline(always)] - fn write(&mut self, value: u8) { - unsafe { - asm!("out $1, $0" : : "{al}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile"); - } - } -} - -/// Read/Write for word PIO -#[cfg(target_arch = "x86")] -impl Io for Pio { - type Value = u16; - - /// Read - #[inline(always)] - fn read(&self) -> u16 { - let value: u16; - unsafe { - asm!("in $0, $1" : "={ax}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile"); - } - value - } - - /// Write - #[inline(always)] - fn write(&mut self, value: u16) { - unsafe { - asm!("out $1, $0" : : "{ax}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile"); - } - } -} - -/// Read/Write for doubleword PIO -#[cfg(target_arch = "x86")] -impl Io for Pio { - type Value = u32; - - /// Read - #[inline(always)] - fn read(&self) -> u32 { - let value: u32; - unsafe { - asm!("in $0, $1" : "={eax}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile"); - } - value - } - - /// Write - #[inline(always)] - fn write(&mut self, value: u32) { - unsafe { - asm!("out $1, $0" : : "{eax}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile"); - } - } -} diff --git a/libutils/src/io/mmio.rs b/libutils/src/io/mmio.rs new file mode 100644 index 000000000..7d538818f --- /dev/null +++ b/libutils/src/io/mmio.rs @@ -0,0 +1,93 @@ +//! 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::uninitialized; +use core::fmt::{Debug, Formatter, Error}; + +use super::Io; + +/// A value that can only be accessed volatilely. +/// +/// Generally used behind a pointer, as such: +/// +/// ``` +/// /// 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, +/// register_command: Mmio, +/// } +/// +/// let device_address = 0xabcdef00 as *mut DeviceFooRegisters; +/// +/// let device: &mut DeviceFooRegisters = unsafe { +/// // safety: make sure that mmio_address is valid and we're not violating +/// // rust's aliasing rules. +/// mmio_address.as_mut().unwrap() +/// }; +/// +/// let status = device.register_control.read(); +/// device.register_command.write(0xFOOD); +/// ``` +// todo: Mmio UnsafeCell +// body: Figure out if Mmio should implement UnsafeCell. +// body: Does this mean that, just like atomic, write can take self by const reference only ? +// body: But is a Mmio 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 { + /// The value. Can only be accessed through .read() + value: T, +} + +impl Mmio { + /// 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: unsafe { uninitialized() } + } + } +} + +impl Io for Mmio where T: Copy { + type Value = T; + + /// Performs a volatile read of the value. + fn read(&self) -> T { + unsafe { read_volatile(&self.value) } + } + + /// Performs a volatile write of the value. + fn write(&mut self, value: T) { + unsafe { write_volatile(&mut self.value, value) }; + } +} + +impl Debug for Mmio 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() + } +} diff --git a/libutils/src/io/mod.rs b/libutils/src/io/mod.rs new file mode 100644 index 000000000..f353f58bf --- /dev/null +++ b/libutils/src/io/mod.rs @@ -0,0 +1,116 @@ +//! The IO interface +//! +//! Stolen from [Redox Io](https://gitlab.redox-os.org/redox-os/syscall/blob/master/src/io/io.rs) + +mod pio; +mod mmio; +pub use self::pio::Pio; +pub use self::mmio::Mmio; + +use core::cmp::PartialEq; +use core::ops::{BitAnd, BitOr, Not}; +use core::fmt::{Debug, Formatter, Error}; + +/// The Io trait allows for accessing device IO in a generic way, abstracting +/// over different IO accesses (Port IO and Memory Mapped IO). +pub trait Io { + /// The width of the IO access. + /// Should be a primitive type like u8, u16, u32... + type Value: Copy; + + /// Reads from this Io. + fn read(&self) -> Self::Value; + + /// Writes `value` to this Io. + fn write(&mut self, value: Self::Value); + + /// Read from this Io, and mask the value with `flags`. + #[inline(always)] + fn readf(&self, flags: Self::Value) -> bool + where + Self::Value: PartialEq + BitAnd + { + (self.read() & flags) as Self::Value == flags + } + + /// Mask `value` with `flags`, and write it to this device address. Note that + /// this causes a read! + #[inline(always)] + fn writef(&mut self, flags: Self::Value, value: bool) + where + Self::Value: PartialEq + BitAnd + BitOr + Not + { + let tmp: Self::Value = if value { + self.read() | flags + } else { + self.read() & !flags + }; + self.write(tmp); + } +} + +/// A read-only wrapper around an IO device. +#[derive(Debug)] +#[allow(clippy::missing_docs_in_private_items)] +pub struct ReadOnly { + inner: I +} + +impl ReadOnly { + /// Create a read-only wrapper around the IO device address. + pub const fn new(inner: I) -> ReadOnly { + ReadOnly { + inner: inner + } + } +} + +impl ReadOnly { + /// Reads from this Io. + #[inline(always)] + pub fn read(&self) -> I::Value { + self.inner.read() + } + + /// Read from this Io, and mask the value with `flags`. + #[inline(always)] + pub fn readf(&self, flags: I::Value) -> bool + where + ::Value: PartialEq + BitAnd::Value> + { + self.inner.readf(flags) + } +} + +/// An Io that we can only write to. +#[allow(clippy::missing_docs_in_private_items)] +pub struct WriteOnly { + inner: I +} + +impl WriteOnly { + /// Creates a WriteOnly Io. + pub const fn new(inner: I) -> WriteOnly { + WriteOnly { + inner: inner + } + } +} + +impl WriteOnly { + /// Writes `value` to this Io. + #[inline(always)] + pub fn write(&mut self, value: I::Value) { + self.inner.write(value) + } + + // writef() not exposed as it requires a read. +} + +impl Debug for WriteOnly { + /// Debug does not access the **write only** value. + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + f.debug_struct("WriteOnly") + .finish() + } +} diff --git a/libutils/src/io/pio.rs b/libutils/src/io/pio.rs new file mode 100644 index 000000000..2aaef1599 --- /dev/null +++ b/libutils/src/io/pio.rs @@ -0,0 +1,96 @@ +//! Port Io +//! +//! All PIOs implement the [Io] trait, and can be abstracted that way. +//! +//! Stolen from [Redox OS](https://gitlab.redox-os.org/redox-os/syscall/blob/master/src/io/pio.rs). + +use core::marker::PhantomData; +use super::Io; + +/// Port IO accessor. +#[derive(Copy, Clone, Debug)] +pub struct Pio { + /// The io port address. + port: u16, + /// The width of the port. + value: PhantomData, +} + +impl Pio { + /// Create a PIO from a given port + pub const fn new(port: u16) -> Self { + Pio:: { + port: port, + value: PhantomData, + } + } +} + +/// Read/Write for byte PIO +impl Io for Pio { + type Value = u8; + + /// Read + #[inline(always)] + fn read(&self) -> u8 { + let value: u8; + unsafe { + asm!("in $0, $1" : "={al}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + value + } + + /// Write + #[inline(always)] + fn write(&mut self, value: u8) { + unsafe { + asm!("out $1, $0" : : "{al}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + } +} + +/// Read/Write for word PIO +impl Io for Pio { + type Value = u16; + + /// Read + #[inline(always)] + fn read(&self) -> u16 { + let value: u16; + unsafe { + asm!("in $0, $1" : "={ax}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + value + } + + /// Write + #[inline(always)] + fn write(&mut self, value: u16) { + unsafe { + asm!("out $1, $0" : : "{ax}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + } +} + +/// Read/Write for doubleword PIO +impl Io for Pio { + type Value = u32; + + /// Read + #[inline(always)] + fn read(&self) -> u32 { + let value: u32; + unsafe { + asm!("in $0, $1" : "={eax}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + value + } + + /// Write + #[inline(always)] + fn write(&mut self, value: u32) { + unsafe { + asm!("out $1, $0" : : "{eax}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + } +}