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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
//! This crate provides a type, [`Volatile`], for managing volatile memory or
//! data.
//!
//! The [`Volatile`] object does not contain a pointer or reference to the
//! volatile memory, but is a container of the volatile data itself. This means
//! that a pointer to a [`Volatile`] object is a pointer to the volatile memory.
//! As such, it would make little or no sense to create a local variable or
//! parameter of type [`Volatile`]. You would typically use some kind of pointer
//! or reference to the [`Volatile`] object instead.
//!
//! Besides [`Volatile`], the crate provides two additional volatile types. They
//! are [`VolatileReadOnly`], and [`VolatileWriteOnly`]. These are technically
//! just type definitions which alias read-only and write-only variants of
//! [`Volatile`], respectively.  However, those variants are only available
//! through these aliases. The default variant for [`Volatile`] allows both
//! reads and writes.
//!
//! [`Volatile`] is meant for reading from or writing to memory used for
//! communication with some process external to the program. A common use case
//! would be memory-mapped I/O.
//!
//! # Safety
//! Typically, [`Volatile`] would be created from a raw pointer, which carries
//! with it the typical [pointer safety concerns](core::ptr#safety). In
//! particular, the following must be guaranteed regarding the location of the
//! volatile memory.
//!
//! - The memory must be [valid](core::ptr#safety) for reads and/or writes.
//!
//! - The memory must be properly aligned.
//!
//! - The memory must point to a properly initialized for the data type, unless
//! the [`Volatile`] is [write-only](VolatileWriteOnly).
//!
//! Note that even if the data has size zero, the pointer must be non-NULL and
//! properly aligned.
//!
//! Do not forget that even creating a reference to uninitialized data (even if
//! that data is never used) is immediate undefined behavior. As such, do not at
//! any point create a reference directly to uninitialized data (as opposed to a
//! reference to [`VolatileWriteOnly`] or [`MaybeUninit`](core::mem::MaybeUninit),
//! each of which can safely handle uninitialized data).
//!
//! Just like in C, whether an operation is volatile has no bearing whatsoever
//! on questions involving concurrent access from multiple threads. Volatile
//! accesses behave exactly like non-atomic accesses in that regard. In
//! particular, a race between a write operation and any other operation
//! (reading or writing) to the same location is undefined behavior.
//!
//! # Disclaimer
//! The Rust documentation contains the following note regarding volatile reads
//! and writes:
//!
//! > Rust does not currently have a rigorously and formally defined memory
//! > model, so the precise semantics of what "volatile" means here is subject
//! > to change over time. That being said, the semantics will almost always end
//! > up pretty similar to [C11's definition of volatile][c11].
//! >
//! > The compiler shouldn't change the relative order or number of volatile
//! > memory operations. However, volatile memory operations on zero-sized types
//! > [...] are noops and may be ignored.
//!
//! [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
#![no_std]
#![allow(unused_unsafe)] // stable alternative to #![deny(unsafe_op_in_unsafe_fn)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(unused_extern_crates)]
#![warn(clippy::todo)]
#![warn(clippy::unimplemented)]
#![warn(clippy::unwrap_used)]
#![deny(safe_packed_borrows)]

mod volatile;
pub use volatile::{Volatile, VolatileReadOnly, VolatileWriteOnly};

/// A marker trait for volatile types.
///
/// This trait must be implemented in order to implement [`VolatileRead`] and
/// [`VolatileWrite`], which will read or write data of type `T`.
pub trait VolatileData<T> {}

/// Volatile data which can be read.
///
/// The data to be read is of type `T`.
pub trait VolatileRead<T>
where
    Self: VolatileData<T>,
    T: Copy,
{
    /// Performs a volatile read of the value in `self` without moving it. This
    /// leaves the memory in `self` unchanged.
    ///
    /// # Safety
    /// Just like in C, whether an operation is volatile has no bearing
    /// whatsoever on questions involving concurrent access from multiple
    /// threads. Volatile accesses behave exactly like non-atomic accesses in
    /// that regard. In particular, a race between a read operation any write
    /// operation to the same location is undefined behavior.
    fn read(&self) -> T;
}

/// Volatile data which can be written.
///
/// The data to be written is of type `T`.
pub trait VolatileWrite<T>
where
    Self: VolatileData<T>,
    T: Copy,
{
    /// Performs a volatile write of `self` with the given value without reading
    /// the old value.
    ///
    /// # Safety
    /// Just like in C, whether an operation is volatile has no bearing
    /// whatsoever on questions involving concurrent access from multiple
    /// threads. Volatile accesses behave exactly like non-atomic accesses in
    /// that regard. In particular, a race between a write operation any other
    /// operation (reading or writing) to the same location is undefined
    /// behavior.
    fn write(&mut self, val: T);
}

/// Data which is, or can be treated as, a readable slice of volatile elements.
///
/// The data to be read is of type [`[U]`](slice).
///
/// This trait has a blanket implementation for all types which meet the
/// criteria.
pub trait VolatileReadSlice<T, U>
where
    Self: AsRef<[T]>,
    T: VolatileRead<U>,
    U: Copy,
{
    /// Performs a volatile read of each element of `self` copying the data to
    /// `dst`. This leaves the memory in `self` unchanged.
    ///
    /// The length of `dst` must be the same as `self`.
    ///
    /// # Panics
    ///
    /// This function will panic if the two slices have different lengths.
    ///
    /// # Safety
    /// Just like in C, whether an operation is volatile has no bearing
    /// whatsoever on questions involving concurrent access from multiple
    /// threads. Volatile accesses behave exactly like non-atomic accesses in
    /// that regard. In particular, a race between a read operation any write
    /// operation to the same location is undefined behavior.
    fn read_slice_volatile(&self, dst: &mut [U]) {
        let this = self.as_ref();
        assert!(
            this.len() == dst.len(),
            "source slice length ({}) does not match destination slice length ({})",
            this.len(),
            dst.len()
        );

        for i in 0..this.len() {
            dst[i] = this[i].read();
        }
    }
}

impl<S, T, U> VolatileReadSlice<T, U> for S
where
    S: AsRef<[T]>,
    T: VolatileRead<U>,
    U: Copy,
{
}

/// Data which is, or can be treated as, a writable slice of volatile elements.
///
/// The data to be written is of type [`[U]`](slice).
///
/// This trait has a blanket implementation for all types which meet the
/// criteria.
pub trait VolatileWriteSlice<T, U>
where
    Self: AsMut<[T]>,
    T: VolatileWrite<U>,
    U: Copy,
{
    /// Performs a volatile write of each element of the slice with the given
    /// value without reading the old data from `self`.
    ///
    /// # Safety
    /// Just like in C, whether an operation is volatile has no bearing
    /// whatsoever on questions involving concurrent access from multiple
    /// threads. Volatile accesses behave exactly like non-atomic accesses in
    /// that regard. In particular, a race between a write operation any other
    /// operation (reading or writing) to the same location is undefined
    /// behavior.
    fn fill_volatile(&mut self, val: U) {
        let this = self.as_mut();
        for elem in this.iter_mut() {
            elem.write(val);
        }
    }

    /// Performs a volatile write of each element of `self`, copying the data
    /// from `src`, without reading the old data from `self`.
    ///
    /// The length of `src` must be the same as `self`.
    ///
    /// # Panics
    ///
    /// This function will panic if the two slices have different lengths.
    ///
    /// # Safety
    /// Just like in C, whether an operation is volatile has no bearing
    /// whatsoever on questions involving concurrent access from multiple
    /// threads. Volatile accesses behave exactly like non-atomic accesses in
    /// that regard. In particular, a race between a write operation any other
    /// operation (reading or writing) to the same location is undefined
    /// behavior.
    fn write_slice_volatile(&mut self, src: &[U]) {
        let this = self.as_mut();
        assert!(
            this.len() == src.len(),
            "source slice length ({}) does not match destination slice length ({})",
            src.len(),
            this.len()
        );

        for i in 0..this.len() {
            this[i].write(src[i]);
        }
    }
}

impl<S, T, U> VolatileWriteSlice<T, U> for S
where
    S: AsMut<[T]>,
    T: VolatileWrite<U>,
    U: Copy,
{
}