Skip to main content

regio/x86/
pio.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use core::marker::PhantomData;
6
7use crate::{Accessible, IoHandle, LayoutOver, Register};
8
9/// A specialization of [`Register`] representing an x86 I/O port.
10///
11/// Example usage:
12/// ```rust
13/// use regio::RwSafe;
14/// use regio::x86::Port;
15///
16/// const IO_PORT: Port<u8, u8, RwSafe> = Port::new(0x3f8);
17/// ```
18pub type Port<Layout, Base, Access> = Register<Layout, Access, PortIo<Base, Access>>;
19
20impl<Layout, Base, Access> Port<Layout, Base, Access>
21where
22    Layout: LayoutOver<Base>,
23    Base: Copy,
24    Access: Accessible,
25{
26    /// Constructs a new port register directly from a port address.
27    pub const fn new(port: u16) -> Self {
28        // Safety: There is nothing unsound about PortIo construction.
29        unsafe { Register::from_io(PortIo::new(port)) }
30    }
31}
32
33/// An I/O backend for reading from and writing to x86 I/O ports.
34#[derive(Debug)]
35pub struct PortIo<Base, Access: Accessible> {
36    port: u16,
37    _marker: PhantomData<(Base, Access)>,
38}
39
40impl<Base, Access: Accessible> PortIo<Base, Access> {
41    /// Constructs a new port I/O handle.
42    pub const fn new(port: u16) -> Self {
43        Self { port, _marker: PhantomData }
44    }
45
46    /// Returns the port address.
47    pub const fn port(&self) -> u16 {
48        self.port
49    }
50}
51
52impl<Base, Access: Accessible> Clone for PortIo<Base, Access> {
53    fn clone(&self) -> Self {
54        Self { port: self.port, _marker: PhantomData }
55    }
56}
57
58impl<Base, Access: Accessible> Copy for PortIo<Base, Access> {}
59
60impl<Base: Copy, Access: Accessible> IoHandle for PortIo<Base, Access> {
61    type Base = Base;
62}
63
64#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
65mod x86_only {
66    use core::arch::asm;
67
68    use super::*;
69    use crate::{ReadHandle, Readable, Writable, WriteHandle};
70
71    macro_rules! impl_port_io_handle_for_small_base {
72        ($ty:ty, $reg:tt) => {
73            impl<Access: Readable> ReadHandle for PortIo<$ty, Access> {
74                #[inline]
75                unsafe fn read_raw(&self) -> $ty {
76                    let value: $ty;
77                    unsafe {
78                        asm!(
79                            concat!("in ", $reg, ", dx"),
80                            in("dx") self.port,
81                            out($reg) value,
82                            options(nostack, preserves_flags),
83                        );
84                    }
85                    value
86                }
87            }
88
89            impl<Access: Writable> WriteHandle for PortIo<$ty, Access> {
90                #[inline]
91                unsafe fn write_raw(&self, value: $ty) {
92                    unsafe {
93                        asm!(
94                            concat!("out dx, ", $reg),
95                            in("dx") self.port,
96                            in($reg) value,
97                            options(nostack, preserves_flags),
98                        );
99                    }
100                }
101            }
102        };
103    }
104
105    impl_port_io_handle_for_small_base!(u8, "al");
106    impl_port_io_handle_for_small_base!(u16, "ax");
107    impl_port_io_handle_for_small_base!(u32, "eax");
108
109    impl<Access: Readable> ReadHandle for PortIo<u64, Access> {
110        #[inline]
111        unsafe fn read_raw(&self) -> u64 {
112            let low = unsafe { PortIo::<u32, Access>::new(self.port).read_raw() };
113            let high = unsafe { PortIo::<u32, Access>::new(self.port + 1).read_raw() };
114            u64::from(high) << 32 | u64::from(low)
115        }
116    }
117
118    impl<Access: Writable> WriteHandle for PortIo<u64, Access> {
119        #[inline]
120        unsafe fn write_raw(&self, value: u64) {
121            let low = value as u32;
122            let high = (value >> 32) as u32;
123            unsafe {
124                PortIo::<u32, Access>::new(self.port).write_raw(low);
125                PortIo::<u32, Access>::new(self.port + 1).write_raw(high);
126            }
127        }
128    }
129}