ostd/io/io_port/
mod.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! I/O port and its allocator that allocates port I/O (PIO) to device drivers.
4
5use crate::arch::device::io_port::{IoPortReadAccess, IoPortWriteAccess, PortRead, PortWrite};
6mod allocator;
7
8use core::marker::PhantomData;
9
10pub(super) use self::allocator::init;
11use crate::{Error, prelude::*};
12
13/// An I/O port, representing a specific address in the I/O address of x86.
14///
15/// The following code shows and example to read and write u32 value to an I/O port:
16///
17/// ```rust
18/// static PORT: IoPort<u32, ReadWriteAccess> = unsafe { IoPort::new(0x12) };
19///
20/// fn port_value_increase(){
21///     PORT.write(PORT.read() + 1)
22/// }
23/// ```
24///
25pub struct IoPort<T, A> {
26    port: u16,
27    is_overlapping: bool,
28    value_marker: PhantomData<T>,
29    access_marker: PhantomData<A>,
30}
31
32impl<T, A> IoPort<T, A> {
33    /// Acquires an `IoPort` instance for the given range.
34    ///
35    /// This method will mark all ports in the PIO range as occupied.
36    pub fn acquire(port: u16) -> Result<IoPort<T, A>> {
37        allocator::IO_PORT_ALLOCATOR
38            .get()
39            .unwrap()
40            .acquire(port, false)
41            .ok_or(Error::AccessDenied)
42    }
43
44    /// Acquires an `IoPort` instance that may overlap with other `IoPort`s.
45    ///
46    /// This method will only mark the first port in the PIO range as occupied.
47    pub fn acquire_overlapping(port: u16) -> Result<IoPort<T, A>> {
48        allocator::IO_PORT_ALLOCATOR
49            .get()
50            .unwrap()
51            .acquire(port, true)
52            .ok_or(Error::AccessDenied)
53    }
54
55    /// Returns the port number.
56    pub const fn port(&self) -> u16 {
57        self.port
58    }
59
60    /// Returns the size of the I/O port.
61    pub const fn size(&self) -> u16 {
62        size_of::<T>() as u16
63    }
64
65    /// Creates an I/O port.
66    ///
67    /// # Safety
68    ///
69    /// Reading from or writing to the I/O port may have side effects. Those side effects must not
70    /// cause soundness problems (e.g., they must not corrupt the kernel memory).
71    pub(crate) const unsafe fn new(port: u16) -> Self {
72        // SAFETY: The safety is upheld by the caller.
73        unsafe { Self::new_overlapping(port, false) }
74    }
75
76    /// Creates an I/O port.
77    ///
78    /// See [`IoPortAllocator::acquire`] for an explanation of the `is_overlapping` argument.
79    ///
80    /// [`IoPortAllocator::acquire`]: allocator::IoPortAllocator::acquire
81    ///
82    /// # Safety
83    ///
84    /// Reading from or writing to the I/O port may have side effects. Those side effects must not
85    /// cause soundness problems (e.g., they must not corrupt the kernel memory).
86    const unsafe fn new_overlapping(port: u16, is_overlapping: bool) -> Self {
87        Self {
88            port,
89            is_overlapping,
90            value_marker: PhantomData,
91            access_marker: PhantomData,
92        }
93    }
94}
95
96impl<T: PortRead, A: IoPortReadAccess> IoPort<T, A> {
97    /// Reads from the I/O port
98    pub fn read(&self) -> T {
99        unsafe { PortRead::read_from_port(self.port) }
100    }
101}
102
103impl<T: PortWrite, A: IoPortWriteAccess> IoPort<T, A> {
104    /// Writes to the I/O port
105    pub fn write(&self, value: T) {
106        unsafe { PortWrite::write_to_port(self.port, value) }
107    }
108}
109
110impl<T, A> Drop for IoPort<T, A> {
111    fn drop(&mut self) {
112        let range = if !self.is_overlapping {
113            self.port..(self.port + size_of::<T>() as u16)
114        } else {
115            self.port..(self.port + 1)
116        };
117
118        // SAFETY: We have ownership of the PIO region.
119        unsafe { allocator::IO_PORT_ALLOCATOR.get().unwrap().recycle(range) };
120    }
121}
122
123/// Reserves an I/O port range which may refer to the port I/O range used by the
124/// system device driver.
125///
126/// # Example
127/// ```
128/// reserve_io_port_range!(0x60..0x64);
129/// ```
130macro_rules! reserve_io_port_range {
131    ($range:expr) => {
132        crate::const_assert!(
133            $range.start < $range.end,
134            "I/O port range must be valid (start < end)"
135        );
136
137        const _: () = {
138            #[used]
139            // SAFETY: This is properly handled in the linker script.
140            #[unsafe(link_section = ".sensitive_io_ports")]
141            static _RANGE: crate::io::RawIoPortRange = crate::io::RawIoPortRange {
142                begin: $range.start,
143                end: $range.end,
144            };
145        };
146    };
147}
148
149/// Declares one or multiple sensitive I/O ports.
150///
151/// # Safety
152///
153/// User must ensures that:
154/// - The I/O port is valid and doesn't overlap with other sensitive I/O ports.
155/// - The I/O port is used by the target system device driver.
156///
157/// # Example
158/// ```no_run
159/// sensitive_io_port! {
160///     unsafe {
161///         /// Master PIC command port
162///         static MASTER_CMD: IoPort<u8, WriteOnlyAccess> = IoPort::new(0x20);
163///         /// Master PIC data port
164///         static MASTER_DATA: IoPort<u8, WriteOnlyAccess> = IoPort::new(0x21);
165///     }
166/// }
167/// ```
168macro_rules! sensitive_io_port {
169    (unsafe { $(
170        $(#[$meta:meta])*
171        $vis:vis static $name:ident: IoPort<$size:ty, $access:ty> = IoPort::new($port:expr);
172    )* }) => {
173        $(
174            $(#[$meta])*
175            $vis static $name: IoPort<$size, $access> = {
176                #[used]
177                // SAFETY: This is properly handled in the linker script.
178                #[unsafe(link_section = ".sensitive_io_ports")]
179                static _RESERVED_IO_PORT_RANGE: crate::io::RawIoPortRange = crate::io::RawIoPortRange {
180                    begin: $name.port(),
181                    end: $name.port() + $name.size(),
182                };
183
184            	unsafe {
185                     IoPort::new($port)
186            	}
187            };
188        )*
189    };
190}
191
192pub(crate) use reserve_io_port_range;
193pub(crate) use sensitive_io_port;
194
195#[doc(hidden)]
196#[derive(Debug, Clone, Copy)]
197#[repr(C)]
198pub(crate) struct RawIoPortRange {
199    pub(crate) begin: u16,
200    pub(crate) end: u16,
201}