Skip to content

Commit c0c377d

Browse files
committed
libutils: Mmio + refactoring
Splitting up the io module in libutils in three modules: * mod.rs: Contains the Io trait, and ReadOnly/WriteOnly wrappers, * pio.rs: Contains the Port IO implementation for x86, * mmio.rs: New module, contains interface to a Memory Mapped IO.
1 parent f71e96f commit c0c377d

File tree

4 files changed

+305
-187
lines changed

4 files changed

+305
-187
lines changed

libutils/src/io.rs

Lines changed: 0 additions & 187 deletions
This file was deleted.

libutils/src/io/mmio.rs

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

libutils/src/io/mod.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//! The IO interface
2+
//!
3+
//! Stolen from [Redox Io](https://gitlab.redox-os.org/redox-os/syscall/blob/master/src/io/io.rs)
4+
5+
mod pio;
6+
mod mmio;
7+
pub use self::pio::Pio;
8+
pub use self::mmio::Mmio;
9+
10+
use core::cmp::PartialEq;
11+
use core::ops::{BitAnd, BitOr, Not};
12+
use core::fmt::{Debug, Formatter, Error};
13+
14+
/// The Io trait allows for accessing device IO in a generic way, abstracting
15+
/// over different IO accesses (Port IO and Memory Mapped IO).
16+
pub trait Io {
17+
/// The width of the IO access.
18+
/// Should be a primitive type like u8, u16, u32...
19+
type Value: Copy;
20+
21+
/// Reads from this Io.
22+
fn read(&self) -> Self::Value;
23+
24+
/// Writes `value` to this Io.
25+
fn write(&mut self, value: Self::Value);
26+
27+
/// Read from this Io, and mask the value with `flags`.
28+
#[inline(always)]
29+
fn readf(&self, flags: Self::Value) -> bool
30+
where
31+
Self::Value: PartialEq + BitAnd<Output = Self::Value>
32+
{
33+
(self.read() & flags) as Self::Value == flags
34+
}
35+
36+
/// Mask `value` with `flags`, and write it to this device address. Note that
37+
/// this causes a read!
38+
#[inline(always)]
39+
fn writef(&mut self, flags: Self::Value, value: bool)
40+
where
41+
Self::Value: PartialEq + BitAnd<Output = Self::Value> + BitOr<Output = Self::Value> + Not<Output = Self::Value>
42+
{
43+
let tmp: Self::Value = if value {
44+
self.read() | flags
45+
} else {
46+
self.read() & !flags
47+
};
48+
self.write(tmp);
49+
}
50+
}
51+
52+
/// A read-only wrapper around an IO device.
53+
#[derive(Debug)]
54+
#[allow(clippy::missing_docs_in_private_items)]
55+
pub struct ReadOnly<I> {
56+
inner: I
57+
}
58+
59+
impl<I> ReadOnly<I> {
60+
/// Create a read-only wrapper around the IO device address.
61+
pub const fn new(inner: I) -> ReadOnly<I> {
62+
ReadOnly {
63+
inner: inner
64+
}
65+
}
66+
}
67+
68+
impl<I: Io> ReadOnly<I> {
69+
/// Reads from this Io.
70+
#[inline(always)]
71+
pub fn read(&self) -> I::Value {
72+
self.inner.read()
73+
}
74+
75+
/// Read from this Io, and mask the value with `flags`.
76+
#[inline(always)]
77+
pub fn readf(&self, flags: I::Value) -> bool
78+
where
79+
<I as Io>::Value: PartialEq + BitAnd<Output = <I as Io>::Value>
80+
{
81+
self.inner.readf(flags)
82+
}
83+
}
84+
85+
/// An Io that we can only write to.
86+
#[allow(clippy::missing_docs_in_private_items)]
87+
pub struct WriteOnly<I> {
88+
inner: I
89+
}
90+
91+
impl<I> WriteOnly<I> {
92+
/// Creates a WriteOnly Io.
93+
pub const fn new(inner: I) -> WriteOnly<I> {
94+
WriteOnly {
95+
inner: inner
96+
}
97+
}
98+
}
99+
100+
impl<I: Io> WriteOnly<I> {
101+
/// Writes `value` to this Io.
102+
#[inline(always)]
103+
pub fn write(&mut self, value: I::Value) {
104+
self.inner.write(value)
105+
}
106+
107+
// writef() not exposed as it requires a read.
108+
}
109+
110+
impl<I> Debug for WriteOnly<I> {
111+
/// Debug does not access the **write only** value.
112+
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
113+
f.debug_struct("WriteOnly")
114+
.finish()
115+
}
116+
}

0 commit comments

Comments
 (0)