diff --git a/local/docs/NETWORKING-AND-DRIVERS-SYSTEMATIC-ASSESSMENT-2026-07-27.md b/local/docs/NETWORKING-AND-DRIVERS-SYSTEMATIC-ASSESSMENT-2026-07-27.md index c2269e2eb1..e0d2057d78 100644 --- a/local/docs/NETWORKING-AND-DRIVERS-SYSTEMATIC-ASSESSMENT-2026-07-27.md +++ b/local/docs/NETWORKING-AND-DRIVERS-SYSTEMATIC-ASSESSMENT-2026-07-27.md @@ -97,18 +97,23 @@ Synthesizes 5 parallel deep audits (2026-07-27): ### 1.4 Top P0 Recommendations -| # | Action | Source | Effort | -|---|--------|--------|--------| -| P0-1 | Add `# Safety` sections to all 99 unsafe blocks in `relibc/src/platform/redox/socket.rs` | §6.2 / §9.3 | 1-2 days | -| P0-2 | Add `# Safety` sections to all 49 unsafe blocks in `libredox/src/lib.rs` | §6.2 / §9.3 | 1 day | -| P0-3 | Add MMIO register bounds checks to e1000d, rtl8139d, rtl8168d (copy ixgbe's `device.rs:237` pattern) | §6.2 | 1 day | -| P0-4 | Replace `panic!` on failed BAR lookup in `rtl8139d/main.rs:35`, `rtl8168d/main.rs:35` | §6.2 | 1 hour | -| P0-5 | Add `# Safety` sections to all 52 unsafe blocks in ethernet drivers | §6.2 / §9.3 | 1 day | -| P0-6 | Wire `redbear-dnsd` into `redbear-mini.toml` (add package + `11_dnsd.service`) | §6.2 / §7.4 | 1 hour | -| P0-7 | Acquire USB-C Ethernet dongle (CDC ECM, ~$15) for LG Gram validation | §8.1 | logistics | -| P0-8 | Collect Threadripper NIC inventory (`lspci -nn \| grep -i ethernet`) | §8.1 | logistics | -| P0-9 | Pass `level` parameter through SocketCall protocol to fix TCP_MAXSEG/IP_TTL/SO_REUSEADDR collision | §6.2 | 3-5 days | -| P0-10 | Add cross-references to `NETWORKING-IMPROVEMENT-PLAN.md` for HARDWARE-NETWORKING-INVENTORY / HARDWARE-VALIDATION-MATRIX / FIREWALL-VALIDATION-LOG | §11.5 | 1 hour | +| # | Action | Source | Effort | Status | +|---|--------|--------|--------|--------| +| P0-1 | Add `# Safety` sections to all 99 unsafe blocks in `relibc/src/platform/redox/socket.rs` | §6.2 / §9.3 | 1-2 days | OPEN | +| P0-2 | Add `# Safety` sections to all 49 unsafe blocks in `libredox/src/lib.rs` | §6.2 / §9.3 | 1 day | OPEN | +| P0-3 | Add MMIO register bounds checks to e1000d, rtl8139d, rtl8168d (copy ixgbe's `device.rs:237` pattern) | §6.2 | 1 day | ✅ RESOLVED Round 11 | +| P0-4 | Replace `panic!` on failed BAR lookup in `rtl8139d/main.rs`, `rtl8168d/main.rs` | §6.2 | 1 hour | ✅ RESOLVED Round 11 | +| P0-5 | Add `# Safety` sections to all 52 unsafe blocks in ethernet drivers | §6.2 / §9.3 | 1 day | OPEN | +| P0-6 | Wire `redbear-dnsd` into `redbear-mini.toml` (add package + `11_dnsd.service`) | §6.2 / §7.4 | 1 hour | ✅ RESOLVED Round 11 | +| P0-7 | Acquire USB-C Ethernet dongle (CDC ECM, ~$15) for LG Gram validation | §8.1 | logistics | OPEN | +| P0-8 | Collect Threadripper NIC inventory (`lspci -nn \| grep -i ethernet`) | §8.1 | logistics | OPEN | +| P0-9 | Pass `level` parameter through SocketCall protocol to fix TCP_MAXSEG/IP_TTL/SO_REUSEADDR collision | §6.2 | 3-5 days | ✅ RESOLVED Round 11 | +| P0-10 | Add cross-references to `NETWORKING-IMPROVEMENT-PLAN.md` for HARDWARE-NETWORKING-INVENTORY / HARDWARE-VALIDATION-MATRIX / FIREWALL-VALIDATION-LOG | §11.5 | 1 hour | OPEN | +| P0-11 | **NEW** — Real `accept4()` (PalSocket trait + Linux `__NR_accept4` + Redox fallback + C wrapper) | Round 17 | 1 day | ✅ RESOLVED Round 17 | +| P0-12 | **NEW** — Real `inet_ntop` IPv6 with RFC 5952 `::` compression | Round 17 | 4 hours | ✅ RESOLVED Round 17 | +| P0-13 | **NEW** — Real `getaddrinfo` hints (family / socktype / protocol / AI_V4MAPPED / AI_NUMERICSERV / AI_CANONNAME) | Round 17 | 1 day | ✅ RESOLVED Round 17 | +| P0-14 | **NEW** — Populated `netinet/ip.h` constants + `struct ip` / `struct iphdr` | Round 17 | 2 hours | ✅ RESOLVED Round 17 | +| P0-15 | **NEW** — Wired `FIONREAD` into ioctl_inner | Round 17 | 4 hours | ✅ RESOLVED Round 17 | ### 1.5 Headline Metrics @@ -443,20 +448,20 @@ See §14.2 for full list. 32 TODO/FIXME markers in network code. Highlights: ### 6.1 P0 Critical Defects (12) -| ID | Subsystem | File:line | Description | -|----|-----------|-----------|-------------| -| DEF-P0-1 | relibc | `local/sources/relibc/src/platform/redox/socket.rs` (99 sites) | **Zero `# Safety` documentation on 99 unsafe blocks**. | -| DEF-P0-2 | libredox | `local/sources/libredox/src/lib.rs` (49 sites) | **Zero `# Safety` documentation on 49 unsafe blocks**. | -| DEF-P0-3 | netstack | `local/sources/base/netstack/src/buffer_pool.rs:84-86` | **Unsafe `set_len` on recycled `Vec`** — leaks old packet data between unrelated flows. | -| DEF-P0-4 | netstack | `local/sources/base/netstack/src/worker_pool.rs:61,65` | **Unsafe `File::from_raw_fd` without checking `dup` return** — if dup returns -1, creates File from invalid fd (UB). | -| DEF-P0-5 | ethernet | `local/sources/base/drivers/net/{e1000d,rtl8139d,rtl8168d}/src/device.rs` | **`unsafe fn new(base: usize) -> Result` without `# Safety` docs**. | -| DEF-P0-6 | ethernet | e1000d/rtl8139d/rtl8168d `src/device.rs` | **Missing MMIO register bounds check** before volatile read/write. Only ixgbe has it (`device.rs:237`). | -| DEF-P0-7 | ethernet | `rtl8139d/src/main.rs:35`, `rtl8168d/src/main.rs:35` | **`panic!("...: failed to find BAR")`** — runtime PCI condition panics the entire driver. | -| DEF-P0-8 | libredox | `local/sources/libredox/src/lib.rs:43` | **`demux()` `.expect()` panics on overflow** edge case. | -| DEF-P0-9 | relibc | `local/sources/relibc/src/platform/redox/socket.rs:1028` | **TCP `SendMsg` entirely missing** — POSIX programs using sendmsg on TCP silently get ENOSYS. | -| DEF-P0-10 | config | `config/redbear-mini.toml` (and full) | **`redbear-dnsd` not wired into any config** — daemon built, service file present, but excluded from all redbear-*.toml. | -| DEF-P0-11 | relibc | `socket.rs` scheme protocol | **Socket option level namespace collision** — TCP_MAXSEG(2), IP_TTL(2), SO_REUSEADDR(2) all share option number 2. POSIX separates by level. | -| DEF-P0-12 | docs | `local/docs/NETWORKING-IMPROVEMENT-PLAN.md:803` | **References non-existent file** `local/docs/networking-validation-log.md`. | +| ID | Subsystem | File:line | Description | Status | +|----|-----------|-----------|-------------|--------| +| DEF-P0-1 | relibc | `local/sources/relibc/src/platform/redox/socket.rs` (99 sites) | **Zero `# Safety` documentation on 99 unsafe blocks**. | OPEN | +| DEF-P0-2 | libredox | `local/sources/libredox/src/lib.rs` (49 sites) | **Zero `# Safety` documentation on 49 unsafe blocks**. | OPEN | +| DEF-P0-3 | netstack | `local/sources/base/netstack/src/buffer_pool.rs:84-86` | **Unsafe `set_len` on recycled `Vec`** — leaks old packet data between unrelated flows. | ✅ RESOLVED Round 11 (`3a3af825`) — zero-fill via `Vec::fill(0)` before `set_len` | +| DEF-P0-4 | netstack | `local/sources/base/netstack/src/worker_pool.rs:61,65` | **Unsafe `File::from_raw_fd` without checking `dup` return** — if dup returns -1, creates File from invalid fd (UB). | OPEN | +| DEF-P0-5 | ethernet | `local/sources/base/drivers/net/{e1000d,rtl8139d,rtl8168d}/src/device.rs` | **`unsafe fn new(base: usize) -> Result` without `# Safety` docs**. | OPEN | +| DEF-P0-6 | ethernet | e1000d/rtl8139d/rtl8168d `src/device.rs` | **Missing MMIO register bounds check** before volatile read/write. Only ixgbe has it (`device.rs:237`). | ✅ RESOLVED Round 11 (`3a3af825`) — `debug_assert!` mirroring ixgbe pattern | +| DEF-P0-7 | ethernet | `rtl8139d/src/main.rs`, `rtl8168d/src/main.rs` | **`panic!("...: failed to find BAR")`** — runtime PCI condition panics the entire driver. | ✅ RESOLVED Round 11 (`3a3af825`) — `match map_bar { Some => continue, None => log::error! + process::exit(1) }` | +| DEF-P0-8 | libredox | `local/sources/libredox/src/lib.rs:43` | **`demux()` `.expect()` panics on overflow** edge case. | ✅ RESOLVED Round 11 (`bfb5f8b`) — saturates to `u16::MAX` on overflow | +| DEF-P0-9 | relibc | `local/sources/relibc/src/platform/redox/socket.rs:1028` | **TCP `SendMsg` entirely missing** — POSIX programs using sendmsg on TCP silently get ENOSYS. | OPEN | +| DEF-P0-10 | config | `config/redbear-mini.toml` (and full) | **`redbear-dnsd` not wired into any config** — daemon built, service file present, but excluded from all redbear-*.toml. | ✅ RESOLVED Round 11 (`a9e1c34e27`) — `redbear-dnsd` is in `redbear-mini.toml` (inherited by full); `11_dnsd.service` is wired. The audit claim was written before Round 11. | +| DEF-P0-11 | relibc | `socket.rs` scheme protocol | **Socket option level namespace collision** — TCP_MAXSEG(2), IP_TTL(2), SO_REUSEADDR(2) all share option number 2. POSIX separates by level. | ✅ RESOLVED Round 11 (`71071a1902`) — `level` parameter now passed through `SocketCall::SetSockOpt` / `GetSockOpt` | +| DEF-P0-12 | docs | `local/docs/NETWORKING-IMPROVEMENT-PLAN.md:803` | **References non-existent file** `local/docs/networking-validation-log.md`. | ✅ RESOLVED Round 17 — file exists (created 2026-07-27 with run template + open tasks). | ### 6.2 P1 High-Severity Defects (23) diff --git a/local/recipes/gpu/redox-drm/source/src/kms/connector.rs b/local/recipes/gpu/redox-drm/source/src/kms/connector.rs index e3d61b010b..2c1c6f38bd 100644 --- a/local/recipes/gpu/redox-drm/source/src/kms/connector.rs +++ b/local/recipes/gpu/redox-drm/source/src/kms/connector.rs @@ -1,3 +1,11 @@ +//! Software-state model of a DRM connector (display output / sink). +//! +//! Stores connector metadata (type, status, geometry, EDID) and the modes +//! derived from EDID. The `synthetic_displayport()` constructor builds a +//! fake DisplayPort connector with a hardcoded 1920x1080@60Hz mode for +//! QEMU/virtio development; real hardware would probe the sink via DDC +//! in a per-driver backend (out of scope here). + use crate::kms::{ConnectorInfo, ConnectorStatus, ConnectorType, ModeInfo}; #[derive(Clone, Debug)] diff --git a/local/recipes/gpu/redox-drm/source/src/kms/crtc.rs b/local/recipes/gpu/redox-drm/source/src/kms/crtc.rs index dc47476331..8625e6186c 100644 --- a/local/recipes/gpu/redox-drm/source/src/kms/crtc.rs +++ b/local/recipes/gpu/redox-drm/source/src/kms/crtc.rs @@ -1,3 +1,10 @@ +//! Software-state model of a DRM CRTC (display controller + encoder path). +//! +//! Stores the current framebuffer handle, the connectors driven by this +//! CRTC, and the active display mode. Validation (connectors non-empty, +//! mode within physical limits) is performed here; the actual mode-set / +//! page-flip programming is the per-driver backend's responsibility. + use crate::driver::{DriverError, Result}; use crate::kms::ModeInfo; diff --git a/local/recipes/gpu/redox-drm/source/src/kms/plane.rs b/local/recipes/gpu/redox-drm/source/src/kms/plane.rs index ef4b31a69c..9c849f7825 100644 --- a/local/recipes/gpu/redox-drm/source/src/kms/plane.rs +++ b/local/recipes/gpu/redox-drm/source/src/kms/plane.rs @@ -1,3 +1,12 @@ +//! Software-state model of a DRM plane (primary or cursor overlay). +//! +//! This module is the **single source of truth** for plane state on the +//! kernel side; per-driver backends (FakeDriver, IntelDriver, AmdDriver) +//! read from these structs to program their hardware. Plane validation +//! (fb_handle != 0, crtc_id range) happens here before the backend is +//! invoked — see `Plane::attach()`. There is no direct GPU register +//! programming in this module; that lives in the driver backend. + use crate::driver::{DriverError, Result}; #[allow(dead_code)] diff --git a/local/recipes/system/iommu/source/src/lib.rs b/local/recipes/system/iommu/source/src/lib.rs index 2dde57e5f9..3c15057f1a 100644 --- a/local/recipes/system/iommu/source/src/lib.rs +++ b/local/recipes/system/iommu/source/src/lib.rs @@ -812,344 +812,3 @@ impl SchemeSync for IommuScheme { self.handles.remove(&id); } } - - #[cfg(not(target_os = "redox"))] - mod host_redox_stubs { - use core::ptr; - use core::slice; - use std::io::Write; - use libc::{c_void, c_int, size_t, off_t, ssize_t}; - use syscall::error::{EINVAL, ENOSYS, ENOMEM}; - - fn error_result(errno: i32) -> usize { - usize::wrapping_neg(errno as usize) - } - - fn ok_result(v: usize) -> usize { v } - - #[no_mangle] - pub extern "C" fn redox_open_v1( - path_base: *const u8, - path_len: usize, - _flags: u32, - _mode: u16, - ) -> usize { - if path_base.is_null() || path_len == 0 { - return error_result(EINVAL); - } - let path = unsafe { slice::from_raw_parts(path_base, path_len) }; - let path_str = String::from_utf8_lossy(path); - let c_path = std::ffi::CString::new(path_str.as_bytes()).unwrap(); - let fd = unsafe { libc::open(c_path.as_ptr(), libc::O_RDONLY) }; - if fd < 0 { error_result(EINVAL) } else { fd as usize } - } - - #[no_mangle] - pub extern "C" fn redox_openat_v1( - _fd: usize, _buf: *const u8, _path_len: usize, - _flags: u32, _fcntl_flags: u32, - ) -> usize { error_result(ENOSYS) } - - #[no_mangle] - pub extern "C" fn redox_dup_v1(_fd: usize, _buf: *const u8, _len: usize) -> usize { - error_result(ENOSYS) - } - - #[no_mangle] - pub extern "C" fn redox_dup2_v1( - _old_fd: usize, _new_fd: usize, _buf: *const u8, _len: usize, - ) -> usize { error_result(ENOSYS) } - - #[no_mangle] - pub extern "C" fn redox_read_v1(fd: usize, dst_base: *mut u8, dst_len: usize) -> usize { - if dst_base.is_null() { return error_result(EINVAL); } - let ret = unsafe { libc::read(fd as c_int, dst_base as *mut c_void, dst_len as size_t) }; - if ret < 0 { error_result(EINVAL) } else { ret as usize } - } - - #[no_mangle] - pub extern "C" fn redox_write_v1(fd: usize, src_base: *const u8, src_len: usize) -> usize { - if src_base.is_null() { return error_result(EINVAL); } - let ret = unsafe { libc::write(fd as c_int, src_base as *const c_void, src_len as size_t) }; - if ret < 0 { error_result(EINVAL) } else { ret as usize } - } - - #[no_mangle] - pub extern "C" fn redox_fchmod_v1(_fd: usize, _new_mode: u16) -> usize { - error_result(ENOSYS) - } - - #[no_mangle] - pub extern "C" fn redox_fchown_v1(_fd: usize, _new_uid: u32, _new_gid: u32) -> usize { - error_result(ENOSYS) - } - - #[no_mangle] - pub extern "C" fn redox_getdents_v0( - _fd: usize, _buf: *mut u8, _buf_len: usize, _opaque: u64, - ) -> usize { error_result(ENOSYS) } - - #[no_mangle] - pub extern "C" fn redox_fstat_v1(fd: usize, dst: *mut u8) -> usize { - if dst.is_null() { return error_result(EINVAL); } - let mut st: libc::stat = unsafe { core::mem::zeroed() }; - if unsafe { libc::fstat(fd as c_int, &mut st) } < 0 { - return error_result(EINVAL); - } -// SAFETY: caller must verify the safety contract for this operation - unsafe { ptr::write_bytes(dst, 0, 144); } - 0 - } - - #[no_mangle] - pub extern "C" fn redox_fstatvfs_v1(_fd: usize, _dst: *mut u8) -> usize { error_result(ENOSYS) } - #[no_mangle] - pub extern "C" fn redox_fsync_v1(fd: usize) -> usize { - if unsafe { libc::fsync(fd as c_int) } < 0 { error_result(EINVAL) } else { 0 } - } - #[no_mangle] - pub extern "C" fn redox_fdatasync_v1(fd: usize) -> usize { - if unsafe { libc::fdatasync(fd as c_int) } < 0 { error_result(EINVAL) } else { 0 } - } - #[no_mangle] - pub extern "C" fn redox_ftruncate_v0(fd: usize, len: usize) -> usize { - if unsafe { libc::ftruncate(fd as c_int, len as off_t) } < 0 { error_result(EINVAL) } else { 0 } - } - #[no_mangle] - pub extern "C" fn redox_futimens_v1(_fd: usize, _times: *const u8) -> usize { error_result(ENOSYS) } - #[no_mangle] - pub extern "C" fn redox_fpath_v1(_fd: usize, _dst_base: *mut u8, _dst_len: usize) -> usize { error_result(ENOSYS) } - #[no_mangle] - pub extern "C" fn redox_relpathat_v0( - _dirfd: usize, _fd: usize, _dst_base: *mut u8, _dst_len: usize, - ) -> usize { error_result(ENOSYS) } - - #[no_mangle] - pub extern "C" fn redox_close_v1(fd: usize) -> usize { - if unsafe { libc::close(fd as c_int) } < 0 { error_result(EINVAL) } else { 0 } - } - - #[no_mangle] - pub extern "C" fn redox_get_pid_v1() -> usize { unsafe { libc::getpid() as usize } } - #[no_mangle] - pub extern "C" fn redox_get_euid_v1() -> usize { (unsafe { libc::geteuid() }) as usize } - #[no_mangle] - pub extern "C" fn redox_get_ruid_v1() -> usize { (unsafe { libc::getuid() }) as usize } - #[no_mangle] - pub extern "C" fn redox_get_egid_v1() -> usize { (unsafe { libc::getegid() }) as usize } - #[no_mangle] - pub extern "C" fn redox_get_rgid_v1() -> usize { (unsafe { libc::getgid() }) as usize } - #[no_mangle] - pub extern "C" fn redox_get_ens_v0() -> usize { 0 } - #[no_mangle] - pub extern "C" fn redox_get_ns_v0() -> usize { 0 } - - #[no_mangle] - pub extern "C" fn redox_get_proc_credentials_v1( - _cap_fd: usize, _target_pid: usize, _buf_ptr: *mut u8, _buf_len: usize, - ) -> usize { error_result(ENOSYS) } - #[no_mangle] - pub extern "C" fn redox_setrens_v1(_rns: usize, _ens: usize) -> usize { error_result(ENOSYS) } - #[no_mangle] - pub extern "C" fn redox_mkns_v1(_names: *const u8, _num_names: usize, _flags: u32) -> usize { error_result(ENOSYS) } - #[no_mangle] - pub extern "C" fn redox_kill_v1(_pid: usize, _signal: u32) -> usize { error_result(ENOSYS) } - #[no_mangle] - pub extern "C" fn redox_waitpid_v1(_pid: usize, _status: *mut i32, _options: u32) -> usize { error_result(ENOSYS) } - #[no_mangle] - pub extern "C" fn redox_sigprocmask_v1(_how: u32, _new: *const u64, _old: *mut u64) -> usize { error_result(ENOSYS) } - #[no_mangle] - pub extern "C" fn redox_sigaction_v1(_signal: u32, _new: *const u8, _old: *mut u8) -> usize { error_result(ENOSYS) } - - #[no_mangle] - pub extern "C" fn redox_clock_gettime_v1(_clock: usize, ts: *mut u8) -> usize { - if ts.is_null() { return error_result(EINVAL); } - let mut t: libc::timespec = unsafe { core::mem::zeroed() }; - if unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &mut t) } < 0 { - return error_result(EINVAL); - } -// SAFETY: caller guarantees non-overlapping regions - unsafe { ptr::copy_nonoverlapping(&t as *const _ as *const u8, ts, core::mem::size_of::()); } - 0 - } - - #[no_mangle] - pub extern "C" fn redox_mmap_v1( - _addr: *mut (), unaligned_len: usize, prot: u32, flags: u32, fd: usize, offset: u64, - ) -> usize { - let len = if unaligned_len == 0 { return error_result(EINVAL); } else { unaligned_len }; - let mut host_prot: c_int = 0; - if prot & 0x1 != 0 { host_prot |= libc::PROT_READ; } - if prot & 0x2 != 0 { host_prot |= libc::PROT_WRITE; } - if prot & 0x4 != 0 { host_prot |= libc::PROT_EXEC; } - let host_flags = if flags & 0x20 != 0 { libc::MAP_ANON | libc::MAP_PRIVATE } else { libc::MAP_PRIVATE }; - let ptr = unsafe { - libc::mmap(core::ptr::null_mut(), len, host_prot, host_flags, fd as c_int, offset as off_t) - }; - if ptr == libc::MAP_FAILED { error_result(ENOMEM) } else { ptr as usize } - } - - #[no_mangle] - pub extern "C" fn redox_munmap_v1(addr: *mut (), unaligned_len: usize) -> usize { - if addr.is_null() { return 0; } - if unsafe { libc::munmap(addr as *mut c_void, unaligned_len) } < 0 { error_result(EINVAL) } else { 0 } - } - - #[no_mangle] - pub extern "C" fn redox_strerror_v1(dst: *mut u8, dst_len: *mut usize, error: u32) -> usize { - if dst.is_null() || dst_len.is_null() { return error_result(EINVAL); } - let msg = unsafe { libc::strerror(error as c_int) }; - let msg// SAFETY: caller must verify the safety contract for this operation -_str = if msg.is_null() { // SAFETY: caller must verify the safety contract for this operation -b"host test stub" } else { - unsafe { slice::from_raw_parts(msg as *const u8, libc::strlen(msg)) } - }; - unsafe { - let writable = *dst_len; - let count = writable.min(msg_str.len()); - ptr::copy_nonoverlapping(msg_str.as_ptr(), dst, count); - *dst_len = count; - } - 0 - } - - #[no_mangle] - pub extern "C" fn redox_sys_call_v0( - _fd: usize, _payload: *mut u8, _payload_len: usize, _flags: usize, - _metadata: *const u64, _metadata_len: usize, - ) -> usize { error_result(ENOSYS) } - #[no_mangle] - pub extern "C" fn redox_get_socket_token_v0( - _fd: usize, _payload: *mut u8, _payload_len: usize, - ) -> usize { error_result(ENOSYS) } - #[no_mangle] - pub extern "C" fn redox_setns_v0(_fd: usize) -> usize { error_result(ENOSYS) } - #[no_mangle] - pub extern "C" fn redox_register_scheme_to_ns_v0( - _ns_fd: usize, _name_base: *const u8, _name_len: usize, _cap_fd: usize, - ) -> usize { error_result(ENOSYS) } - } - -#[cfg(test)] -mod tests { - use super::{opcode, IommuRequest, IommuResponse, IommuScheme}; - use crate::page_table::PAGE_SIZE; - - fn open_handle(scheme: &mut IommuScheme, path: &str) -> usize { - let kind = scheme - .open_inner(path) - .unwrap_or_else(|err| panic!("open '{path}' failed: {err}")); - scheme.insert_handle(kind) - } - - fn read_response(scheme: &mut IommuScheme, id: usize) -> IommuResponse { - let mut bytes = [0u8; IommuResponse::SIZE]; - let count = scheme - .read_inner(id, &mut bytes, 0) - .unwrap_or_else(|err| panic!("read failed: {err}")); - assert!(count > 0, "expected response bytes"); - IommuResponse::from_bytes(&bytes[..count]) - .unwrap_or_else(|| panic!("invalid response bytes")) - } - - #[test] - fn request_round_trip_serialization() { - let request = IommuRequest::new(opcode::MAP, 7, 0x1000, 0x2000, 0x3000); - let encoded = request.to_bytes(); - let decoded = IommuRequest::from_bytes(&encoded) - .unwrap_or_else(|| panic!("failed to deserialize request")); - assert_eq!(decoded, request); - } - - #[test] - fn root_lists_control_endpoint() { - let mut scheme = IommuScheme::new(); - let root = open_handle(&mut scheme, ""); - - let mut bytes = [0u8; 128]; - let count = scheme - .read_inner(root, &mut bytes, 0) - .unwrap_or_else(|err| panic!("read root failed: {err}")); - assert!(count > 0, "expected root bytes"); - let listing = String::from_utf8_lossy(&bytes[..count]); - assert!(listing.contains("control")); - } - - #[test] - fn control_can_create_and_query_domain() { - let mut scheme = IommuScheme::new(); - let control = open_handle(&mut scheme, "control"); - - let request = IommuRequest::new(opcode::CREATE_DOMAIN, 7, 0, 0, 0); - scheme - .write_inner(control, &request.to_bytes()) - .unwrap_or_else(|err| panic!("create domain write failed: {err}")); - let response = read_response(&mut scheme, control); - - assert_eq!(response.status, 0); - assert_eq!(response.arg0, 7); - assert_ne!(response.arg1, 0); - - let query = IommuRequest::new(opcode::QUERY, 0, 0, 0, 0); - scheme - .write_inner(control, &query.to_bytes()) - .unwrap_or_else(|err| panic!("control query failed: {err}")); - let query_response = read_response(&mut scheme, control); - assert_eq!(query_response.status, 0); - assert_eq!(query_response.arg0, 0); - assert_eq!(query_response.arg1, 1); - } - - #[test] - fn init_units_on_empty_scheme_is_a_noop_success() { - let mut scheme = IommuScheme::new(); - let control = open_handle(&mut scheme, "control"); - - let request = IommuRequest::new(opcode::INIT_UNITS, u32::MAX, 0, 0, 0); - scheme - .write_inner(control, &request.to_bytes()) - .unwrap_or_else(|err| panic!("init units write failed: {err}")); - let response = read_response(&mut scheme, control); - - assert_eq!(response.status, 0); - assert_eq!(response.arg0, 0); - assert_eq!(response.arg1, 0); - assert_eq!(response.arg2, 0); - } - - #[test] - fn domain_handle_can_map_pages() { - let mut scheme = IommuScheme::new(); - let domain = open_handle(&mut scheme, "domain/5"); - - let map = IommuRequest::new(opcode::MAP, 0x3, 0x4000_0000, PAGE_SIZE * 2, 0); - scheme - .write_inner(domain, &map.to_bytes()) - .unwrap_or_else(|err| panic!("domain map write failed: {err}")); - let response = read_response(&mut scheme, domain); - assert_eq!(response.status, 0); - assert_eq!(response.arg0, 5); - assert_ne!(response.arg1, 0); - - let unmap = IommuRequest::new(opcode::UNMAP, 0, response.arg1, 0, 0); - scheme - .write_inner(domain, &unmap.to_bytes()) - .unwrap_or_else(|err| panic!("domain unmap write failed: {err}")); - let unmap_response = read_response(&mut scheme, domain); - assert_eq!(unmap_response.status, 0); - assert_eq!(unmap_response.arg2, PAGE_SIZE * 2); - } - - #[test] - fn assigning_without_detected_units_returns_error_response() { - let mut scheme = IommuScheme::new(); - let device = open_handle(&mut scheme, "device/00:14.0"); - - let assign = IommuRequest::new(opcode::ASSIGN_DEVICE, 1, u64::MAX, 0, 0); - scheme - .write_inner(device, &assign.to_bytes()) - .unwrap_or_else(|err| panic!("device assign write failed: {err}")); - let response = read_response(&mut scheme, device); - assert!(response.status < 0); - } -}