Skip to content

Commit 682dd70

Browse files
committed
Add access types for allowing only unsafe reads and/or writes
1 parent 946b37b commit 682dd70

File tree

3 files changed

+147
-4
lines changed

3 files changed

+147
-4
lines changed

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ repository = "https://github.com/rust-osdev/volatile"
1414
# Enable unstable features; requires Rust nightly; might break on compiler updates
1515
unstable = []
1616

17+
[dev-dependencies]
18+
rand = "0.8.3"
19+
1720
[package.metadata.release]
1821
no-dev-version = true
1922
pre-release-replacements = [

src/access.rs

+30-2
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,56 @@
11
pub trait Access {}
22

33
/// Helper trait that is implemented by [`ReadWrite`] and [`ReadOnly`].
4-
pub trait Readable {}
4+
pub trait Readable: UnsafelyReadable {}
55

66
/// Helper trait that is implemented by [`ReadWrite`] and [`WriteOnly`].
7-
pub trait Writable {}
7+
pub trait Writable: UnsafelyWritable {}
8+
9+
pub trait UnsafelyReadable {}
10+
11+
pub trait UnsafelyWritable {}
812

913
/// Zero-sized marker type for allowing both read and write access.
1014
#[derive(Debug, Copy, Clone)]
1115
pub struct ReadWrite;
1216
impl Access for ReadWrite {}
1317
impl Readable for ReadWrite {}
18+
impl UnsafelyReadable for ReadWrite {}
1419
impl Writable for ReadWrite {}
20+
impl UnsafelyWritable for ReadWrite {}
1521

1622
/// Zero-sized marker type for allowing only read access.
1723
#[derive(Debug, Copy, Clone)]
1824
pub struct ReadOnly;
1925

2026
impl Access for ReadOnly {}
2127
impl Readable for ReadOnly {}
28+
impl UnsafelyReadable for ReadOnly {}
2229

2330
/// Zero-sized marker type for allowing only write access.
2431
#[derive(Debug, Copy, Clone)]
2532
pub struct WriteOnly;
2633

2734
impl Access for WriteOnly {}
2835
impl Writable for WriteOnly {}
36+
impl UnsafelyWritable for WriteOnly {}
37+
38+
#[derive(Clone, Copy, PartialEq, Eq)]
39+
pub struct Custom<R, W> {
40+
pub read: R,
41+
pub write: W,
42+
}
43+
44+
#[derive(Clone, Copy, PartialEq, Eq)]
45+
pub struct NoAccess;
46+
#[derive(Clone, Copy, PartialEq, Eq)]
47+
pub struct SafeAccess;
48+
#[derive(Clone, Copy, PartialEq, Eq)]
49+
pub struct UnsafeAccess;
50+
51+
impl<W> Readable for Custom<SafeAccess, W> {}
52+
impl<W> UnsafelyReadable for Custom<SafeAccess, W> {}
53+
impl<W> UnsafelyReadable for Custom<UnsafeAccess, W> {}
54+
impl<R> Writable for Custom<R, SafeAccess> {}
55+
impl<R> UnsafelyWritable for Custom<R, SafeAccess> {}
56+
impl<R> UnsafelyWritable for Custom<R, UnsafeAccess> {}

src/lib.rs

+114-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
#![warn(missing_docs)]
1717
#![deny(unsafe_op_in_unsafe_fn)]
1818

19-
use access::{ReadOnly, ReadWrite, Readable, Writable, WriteOnly};
19+
use access::{
20+
ReadOnly, ReadWrite, Readable, UnsafelyReadable, UnsafelyWritable, Writable, WriteOnly,
21+
};
2022
use core::{fmt, marker::PhantomData, ptr};
2123
#[cfg(feature = "unstable")]
2224
use core::{
@@ -178,6 +180,30 @@ where
178180
f(&mut value);
179181
self.write(value);
180182
}
183+
184+
pub unsafe fn read_unsafe(&self) -> T
185+
where
186+
A: UnsafelyReadable,
187+
{
188+
unsafe { ptr::read_volatile(self.pointer) }
189+
}
190+
191+
pub unsafe fn write_unsafe(&mut self, value: T)
192+
where
193+
A: UnsafelyWritable,
194+
{
195+
unsafe { ptr::write_volatile(self.pointer, value) };
196+
}
197+
198+
pub unsafe fn update_unsafe<F>(&mut self, f: F)
199+
where
200+
A: UnsafelyReadable + UnsafelyWritable,
201+
F: FnOnce(&mut T),
202+
{
203+
let mut value = unsafe { self.read_unsafe() };
204+
f(&mut value);
205+
unsafe { self.write_unsafe(value) };
206+
}
181207
}
182208

183209
/// Method for extracting the wrapped value.
@@ -765,7 +791,7 @@ macro_rules! map_field_mut {
765791

766792
#[cfg(test)]
767793
mod tests {
768-
use super::Volatile;
794+
use super::{access::*, Volatile};
769795
use core::cell::UnsafeCell;
770796

771797
#[test]
@@ -790,6 +816,92 @@ mod tests {
790816
assert_eq!(val, 43);
791817
}
792818

819+
#[test]
820+
fn test_access() {
821+
let mut val: i64 = 42;
822+
823+
// ReadWrite
824+
assert_eq!(unsafe { Volatile::new(&mut val, ReadWrite) }.read(), 42);
825+
unsafe { Volatile::new(&mut val, ReadWrite) }.write(50);
826+
assert_eq!(val, 50);
827+
unsafe { Volatile::new(&mut val, ReadWrite) }.update(|i| *i += 1);
828+
assert_eq!(val, 51);
829+
830+
// ReadOnly and WriteOnly
831+
assert_eq!(unsafe { Volatile::new(&mut val, ReadOnly) }.read(), 51);
832+
unsafe { Volatile::new(&mut val, WriteOnly) }.write(12);
833+
assert_eq!(val, 12);
834+
835+
// Custom: safe read + safe write
836+
{
837+
let access = Custom {
838+
read: SafeAccess,
839+
write: SafeAccess,
840+
};
841+
let mut volatile = unsafe { Volatile::new(&mut val, access) };
842+
let random: i32 = rand::random();
843+
volatile.write(i64::from(random));
844+
assert_eq!(volatile.read(), i64::from(random));
845+
let random2: i32 = rand::random();
846+
volatile.update(|i| *i += i64::from(random2));
847+
assert_eq!(volatile.read(), i64::from(random) + i64::from(random2));
848+
}
849+
850+
// Custom: safe read + unsafe write
851+
{
852+
let access = Custom {
853+
read: SafeAccess,
854+
write: UnsafeAccess,
855+
};
856+
let mut volatile = unsafe { Volatile::new(&mut val, access) };
857+
let random: i32 = rand::random();
858+
unsafe { volatile.write_unsafe(i64::from(random)) };
859+
assert_eq!(volatile.read(), i64::from(random));
860+
let random2: i32 = rand::random();
861+
unsafe { volatile.update_unsafe(|i| *i += i64::from(random2)) };
862+
assert_eq!(volatile.read(), i64::from(random) + i64::from(random2));
863+
}
864+
865+
// Custom: safe read + no write
866+
{
867+
let access = Custom {
868+
read: SafeAccess,
869+
write: NoAccess,
870+
};
871+
let random = rand::random();
872+
val = random;
873+
let mut volatile = unsafe { Volatile::new(&mut val, access) };
874+
assert_eq!(volatile.read(), i64::from(random));
875+
}
876+
877+
// Custom: unsafe read + safe write
878+
{
879+
let access = Custom {
880+
read: UnsafeAccess,
881+
write: SafeAccess,
882+
};
883+
let mut volatile = unsafe { Volatile::new(&mut val, access) };
884+
let random: i32 = rand::random();
885+
volatile.write(i64::from(random));
886+
assert_eq!(unsafe { volatile.read_unsafe() }, i64::from(random));
887+
let random2: i32 = rand::random();
888+
volatile.update_unsafe(|i| *i += i64::from(random2));
889+
assert_eq!(
890+
volatile.read_unsafe(),
891+
i64::from(random) + i64::from(random2)
892+
);
893+
}
894+
895+
// Todo: Custom: unsafe read + unsafe write
896+
// Todo: Custom: unsafe read + no write
897+
// Todo: Custom: no read + safe write
898+
// Todo: Custom: no read + unsafe write
899+
// Todo: Custom: no read + no write
900+
901+
// Todo: is there a way to check that a compile error occurs when trying to use
902+
// unavailable methods (e.g. `write` when write permission is `UnsafeAccess`)?
903+
}
904+
793905
#[test]
794906
fn test_struct() {
795907
#[derive(Debug, PartialEq)]

0 commit comments

Comments
 (0)