redox-driver-sys: add # Safety docs to all io.rs unsafe blocks
Document the safety contracts for: - acquire_iopl: kernel fd validity, IOPL privilege, no concurrent calls - inb/inw/inl: valid port, required privilege (IOPL or ring 0) - outb/outw/outl: valid writable port, no destructive side effects Closes the documentation gap for io.rs unsafe inline asm blocks. Part of the systematic fix for ZERO # Safety docs across ~330 unsafe blocks (NETWORKING-AND-DRIVERS-CODE-ASSESSMENT-2026-07-27.md §9.3).
This commit is contained in:
@@ -2,6 +2,19 @@
|
||||
|
||||
use syscall as redox_syscall;
|
||||
|
||||
/// Acquire I/O Privilege Level for the current thread so that subsequent
|
||||
/// `inb`/`inw`/`inl`/`outb`/`outw`/`outl` calls do not raise #GP.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure:
|
||||
/// - `acquire_iopl` is called from a thread that is permitted to hold IOPL
|
||||
/// (typically a driver thread under the `redox-driver-sys` umbrella).
|
||||
/// - No other code path is concurrently relying on the prior privilege level.
|
||||
/// - The kernel fd returned by `redox_cur_thrfd_v0()` is valid for the
|
||||
/// duration of this call (the function returns a duplicated handle which is
|
||||
/// closed before this function returns; the underlying fd must remain open
|
||||
/// at least until the close completes).
|
||||
#[cfg(all(target_arch = "x86_64", target_os = "redox"))]
|
||||
pub fn acquire_iopl() -> std::result::Result<(), crate::DriverError> {
|
||||
extern "C" {
|
||||
@@ -26,6 +39,16 @@ pub fn acquire_iopl() -> std::result::Result<(), crate::DriverError> {
|
||||
)))
|
||||
}
|
||||
|
||||
/// Read a byte from an x86 I/O port.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure:
|
||||
/// - `port` is a valid x86 I/O port number (0..=0xFFFF) that is mapped for the
|
||||
/// current privilege level (IOPL must have been acquired via `acquire_iopl`,
|
||||
/// or the caller must be ring 0).
|
||||
/// - No other thread is concurrently modifying port-mapped device state in a
|
||||
/// way that makes single-byte reads racy.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline]
|
||||
pub fn inb(port: u16) -> u8 {
|
||||
@@ -34,12 +57,28 @@ pub fn inb(port: u16) -> u8 {
|
||||
val
|
||||
}
|
||||
|
||||
/// Write a byte to an x86 I/O port.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure:
|
||||
/// - `port` is a valid x86 I/O port number (0..=0xFFFF) that is mapped and
|
||||
/// writable at the current privilege level (IOPL via `acquire_iopl`, or ring 0).
|
||||
/// - Writing `val` to `port` does not produce a side effect that puts the
|
||||
/// device in a state that violates the kernel's or another driver's
|
||||
/// invariants (e.g. disabling an interrupt source shared with the system).
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline]
|
||||
pub fn outb(port: u16, val: u8) {
|
||||
unsafe { core::arch::asm!("out dx, al", in("dx") port, in("al") val, options(nomem, nostack, preserves_flags)) };
|
||||
}
|
||||
|
||||
/// Read a 32-bit dword from an x86 I/O port.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Same requirements as `inb`, but reads 4 contiguous bytes (little-endian)
|
||||
/// from the port. The device must support 32-bit port I/O at `port..port+3`.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline]
|
||||
pub fn inl(port: u16) -> u32 {
|
||||
@@ -48,12 +87,24 @@ pub fn inl(port: u16) -> u32 {
|
||||
val
|
||||
}
|
||||
|
||||
/// Write a 32-bit dword to an x86 I/O port.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Same requirements as `outb`, but writes 4 contiguous bytes (little-endian)
|
||||
/// to `port..port+3`. The device must support 32-bit port I/O at this range.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline]
|
||||
pub fn outl(port: u16, val: u32) {
|
||||
unsafe { core::arch::asm!("out dx, eax", in("dx") port, in("eax") val, options(nomem, nostack, preserves_flags)) };
|
||||
}
|
||||
|
||||
/// Read a 16-bit word from an x86 I/O port.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Same requirements as `inb`, but reads 2 contiguous bytes (little-endian)
|
||||
/// from the port. The device must support 16-bit port I/O at `port..port+1`.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline]
|
||||
pub fn inw(port: u16) -> u16 {
|
||||
@@ -62,8 +113,14 @@ pub fn inw(port: u16) -> u16 {
|
||||
val
|
||||
}
|
||||
|
||||
/// Write a 16-bit word to an x86 I/O port.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Same requirements as `outb`, but writes 2 contiguous bytes (little-endian)
|
||||
/// to `port..port+1`. The device must support 16-bit port I/O at this range.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline]
|
||||
pub fn outw(port: u16, val: u16) {
|
||||
unsafe { core::arch::asm!("out dx, ax", in("dx") port, in("ax") val, options(nomem, nostack, preserves_flags)) };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user