Skip to content

Commit 9b6b48d

Browse files
authored
Merge pull request #248 from dbeckwith/port-rw
Use marker types for Port read/write access
2 parents dd1a4c1 + f00c35c commit 9b6b48d

File tree

4 files changed

+85
-91
lines changed

4 files changed

+85
-91
lines changed

Changelog.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Unreleased
22

3-
- The following methods no longer require the `nightly` feature to be `const fn`s`:
3+
- Make the following types aliases of the new `PortGeneric` type ([#248](https://github.com/rust-osdev/x86_64/pull/248)):
4+
- `Port<T> = PortGeneric<T, ReadWriteAccess>`
5+
- `PortReadOnly<T> = PortGeneric<T, ReadOnlyAccess>`
6+
- `PortWriteOnly<T> = PortGeneric<T, WriteOnlyAccess>`
7+
- The following methods no longer require the `nightly` feature to be `const fn`s` ([#255](https://github.com/rust-osdev/x86_64/pull/255)):
48
- `PageTable::new`
59
- `GlobalDescriptorTable::from_raw_slice`
610
- `MappedFrame::{start_address, size}`

src/instructions/port.rs

Lines changed: 76 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -80,84 +80,80 @@ impl PortWrite for u32 {
8080
}
8181
}
8282

83-
/// A read only I/O port.
84-
pub struct PortReadOnly<T> {
85-
port: u16,
86-
phantom: PhantomData<T>,
83+
mod sealed {
84+
pub trait Access {
85+
const DEBUG_NAME: &'static str;
86+
}
8787
}
8888

89-
/// A write only I/O port.
90-
pub struct PortWriteOnly<T> {
91-
port: u16,
92-
phantom: PhantomData<T>,
93-
}
89+
/// A marker trait for access types which allow reading port values.
90+
pub trait PortReadAccess: sealed::Access {}
9491

95-
/// An I/O port.
96-
pub struct Port<T> {
97-
port: u16,
98-
phantom: PhantomData<T>,
99-
}
92+
/// A marker trait for access types which allow writing port values.
93+
pub trait PortWriteAccess: sealed::Access {}
10094

101-
impl<T> PortReadOnly<T> {
102-
/// Creates a read only I/O port with the given port number.
103-
#[inline]
104-
pub const fn new(port: u16) -> PortReadOnly<T> {
105-
PortReadOnly {
106-
port,
107-
phantom: PhantomData,
108-
}
109-
}
95+
/// An access marker type indicating that a port is only allowed to read values.
96+
#[derive(Debug)]
97+
pub struct ReadOnlyAccess(());
98+
99+
impl sealed::Access for ReadOnlyAccess {
100+
const DEBUG_NAME: &'static str = "ReadOnly";
110101
}
102+
impl PortReadAccess for ReadOnlyAccess {}
111103

112-
impl<T: PortRead> PortReadOnly<T> {
113-
/// Reads from the port.
114-
///
115-
/// ## Safety
116-
///
117-
/// This function is unsafe because the I/O port could have side effects that violate memory
118-
/// safety.
119-
#[inline]
120-
pub unsafe fn read(&mut self) -> T {
121-
T::read_from_port(self.port)
122-
}
104+
/// An access marker type indicating that a port is only allowed to write values.
105+
#[derive(Debug)]
106+
pub struct WriteOnlyAccess(());
107+
108+
impl sealed::Access for WriteOnlyAccess {
109+
const DEBUG_NAME: &'static str = "WriteOnly";
123110
}
111+
impl PortWriteAccess for WriteOnlyAccess {}
124112

125-
impl<T> PortWriteOnly<T> {
126-
/// Creates a write only I/O port with the given port number.
127-
#[inline]
128-
pub const fn new(port: u16) -> PortWriteOnly<T> {
129-
PortWriteOnly {
130-
port,
131-
phantom: PhantomData,
132-
}
133-
}
113+
/// An access marker type indicating that a port is allowed to read or write values.
114+
#[derive(Debug)]
115+
pub struct ReadWriteAccess(());
116+
117+
impl sealed::Access for ReadWriteAccess {
118+
const DEBUG_NAME: &'static str = "ReadWrite";
134119
}
120+
impl PortReadAccess for ReadWriteAccess {}
121+
impl PortWriteAccess for ReadWriteAccess {}
135122

136-
impl<T: PortWrite> PortWriteOnly<T> {
137-
/// Writes to the port.
138-
///
139-
/// ## Safety
140-
///
141-
/// This function is unsafe because the I/O port could have side effects that violate memory
142-
/// safety.
143-
#[inline]
144-
pub unsafe fn write(&mut self, value: T) {
145-
T::write_to_port(self.port, value)
146-
}
123+
/// An I/O port.
124+
///
125+
/// The port reads or writes values of type `T` and has read/write access specified by `A`.
126+
///
127+
/// Use the provided marker types or aliases to get a port type with the access you need:
128+
/// * `PortGeneric<T, ReadWriteAccess>` -> `Port<T>`
129+
/// * `PortGeneric<T, ReadOnlyAccess>` -> `PortReadOnly<T>`
130+
/// * `PortGeneric<T, WriteOnlyAccess>` -> `PortWriteOnly<T>`
131+
pub struct PortGeneric<T, A> {
132+
port: u16,
133+
phantom: PhantomData<(T, A)>,
147134
}
148135

149-
impl<T> Port<T> {
136+
/// A read-write I/O port.
137+
pub type Port<T> = PortGeneric<T, ReadWriteAccess>;
138+
139+
/// A read-only I/O port.
140+
pub type PortReadOnly<T> = PortGeneric<T, ReadOnlyAccess>;
141+
142+
/// A write-only I/O port.
143+
pub type PortWriteOnly<T> = PortGeneric<T, WriteOnlyAccess>;
144+
145+
impl<T, A> PortGeneric<T, A> {
150146
/// Creates an I/O port with the given port number.
151147
#[inline]
152-
pub const fn new(port: u16) -> Port<T> {
153-
Port {
148+
pub const fn new(port: u16) -> PortGeneric<T, A> {
149+
PortGeneric {
154150
port,
155151
phantom: PhantomData,
156152
}
157153
}
158154
}
159155

160-
impl<T: PortRead> Port<T> {
156+
impl<T: PortRead, A: PortReadAccess> PortGeneric<T, A> {
161157
/// Reads from the port.
162158
///
163159
/// ## Safety
@@ -170,7 +166,7 @@ impl<T: PortRead> Port<T> {
170166
}
171167
}
172168

173-
impl<T: PortWrite> Port<T> {
169+
impl<T: PortWrite, A: PortWriteAccess> PortGeneric<T, A> {
174170
/// Writes to the port.
175171
///
176172
/// ## Safety
@@ -183,35 +179,29 @@ impl<T: PortWrite> Port<T> {
183179
}
184180
}
185181

186-
macro_rules! impl_port_util_traits {
187-
($struct_name:ident) => {
188-
impl<T> fmt::Debug for $struct_name<T> {
189-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190-
f.debug_struct(stringify!($struct_name))
191-
.field("port", &self.port)
192-
.finish()
193-
}
194-
}
195-
196-
impl<T> Clone for $struct_name<T> {
197-
fn clone(&self) -> Self {
198-
Self {
199-
port: self.port,
200-
phantom: PhantomData,
201-
}
202-
}
203-
}
182+
impl<T, A: sealed::Access> fmt::Debug for PortGeneric<T, A> {
183+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184+
f.debug_struct("PortGeneric")
185+
.field("port", &self.port)
186+
.field("size", &core::mem::size_of::<T>())
187+
.field("access", &format_args!("{}", A::DEBUG_NAME))
188+
.finish()
189+
}
190+
}
204191

205-
impl<T> PartialEq for $struct_name<T> {
206-
fn eq(&self, other: &Self) -> bool {
207-
self.port == other.port
208-
}
192+
impl<T, A> Clone for PortGeneric<T, A> {
193+
fn clone(&self) -> Self {
194+
Self {
195+
port: self.port,
196+
phantom: PhantomData,
209197
}
198+
}
199+
}
210200

211-
impl<T> Eq for $struct_name<T> {}
212-
};
201+
impl<T, A> PartialEq for PortGeneric<T, A> {
202+
fn eq(&self, other: &Self) -> bool {
203+
self.port == other.port
204+
}
213205
}
214206

215-
impl_port_util_traits!(Port);
216-
impl_port_util_traits!(PortReadOnly);
217-
impl_port_util_traits!(PortWriteOnly);
207+
impl<T, A> Eq for PortGeneric<T, A> {}

testing/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ pub enum QemuExitCode {
1919
}
2020

2121
pub fn exit_qemu(exit_code: QemuExitCode) {
22-
use x86_64::instructions::port::Port;
22+
use x86_64::instructions::port::PortWriteOnly;
2323

2424
unsafe {
25-
let mut port = Port::new(0xf4);
25+
let mut port = PortWriteOnly::new(0xf4);
2626
port.write(exit_code as u32);
2727
}
2828
}

testing/tests/port_read_write.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ pub extern "C" fn _start() -> ! {
3939
// Read the test value using PortReadOnly
4040
let read_only_test_value = crt_data_read_only_port.read() & 0xFF;
4141

42-
// Read the test value using PortReadWrite
42+
// Read the test value using Port
4343
let read_write_test_value = crt_read_write_data_port.read() & 0xFF;
4444

4545
if read_only_test_value != TEST_VALUE {
@@ -51,7 +51,7 @@ pub extern "C" fn _start() -> ! {
5151

5252
if read_write_test_value != TEST_VALUE {
5353
panic!(
54-
"PortReadWrite: {} does not match expected value {}",
54+
"Port: {} does not match expected value {}",
5555
read_write_test_value, TEST_VALUE
5656
);
5757
}

0 commit comments

Comments
 (0)