From ec8af52952d2277daab1ddf58dc4ebf3a717f1ab Mon Sep 17 00:00:00 2001 From: vasilito Date: Mon, 27 Jul 2026 14:34:48 +0900 Subject: [PATCH] redox-driver-sys: add # Safety docs to all io.rs unsafe blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- .../drivers/redox-driver-sys/source/src/io.rs | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/local/recipes/drivers/redox-driver-sys/source/src/io.rs b/local/recipes/drivers/redox-driver-sys/source/src/io.rs index e5d3c32d51..1e76186eaf 100644 --- a/local/recipes/drivers/redox-driver-sys/source/src/io.rs +++ b/local/recipes/drivers/redox-driver-sys/source/src/io.rs @@ -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)) }; -} +} \ No newline at end of file