diff --git a/local/recipes/drivers/redox-driver-core/Cargo.toml b/local/recipes/drivers/redox-driver-core/Cargo.toml deleted file mode 100644 index cfcfab8aba..0000000000 --- a/local/recipes/drivers/redox-driver-core/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "redox-driver-core" -version = "0.1.0" -edition = "2024" -description = "Core device-model traits and orchestration for Red Bear drivers" - -[features] -default = ["std"] -alloc = [] -std = ["alloc"] -hotplug = [] - -[dependencies] -hashbrown = { version = "0.15", default-features = false, features = ["default-hasher"] } diff --git a/local/recipes/system/driver-manager/Cargo.toml b/local/recipes/system/driver-manager/Cargo.toml deleted file mode 100644 index 415b8b28a0..0000000000 --- a/local/recipes/system/driver-manager/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "driver-manager" -version = "0.1.0" -edition = "2024" -description = "Device driver manager with deferred and async probing" - -[[bin]] -name = "driver-manager" -path = "src/main.rs" - -[dependencies] -redox-driver-core = { path = "../../drivers/redox-driver-core" } -redox-driver-pci = { path = "../../drivers/redox-driver-pci" } -pcid_interface = { path = "../../../../local/sources/base/drivers/pcid", package = "pcid" } -redox_syscall = { path = "../../../../local/sources/syscall" } -log = "0.4" -toml = "0.8" -serde = { version = "1", features = ["derive"] } diff --git a/local/recipes/system/driver-manager/src/config.rs b/local/recipes/system/driver-manager/src/config.rs deleted file mode 100644 index 20c54940d4..0000000000 --- a/local/recipes/system/driver-manager/src/config.rs +++ /dev/null @@ -1,334 +0,0 @@ -use std::collections::HashMap; -use std::fs::{self, File, OpenOptions}; -use std::os::fd::{AsRawFd, FromRawFd, OwnedFd}; -use std::path::Path; -use std::process::Command; -use std::string::String; -use std::sync::Mutex; -use std::vec::Vec; - -use pcid_interface::PciFunctionHandle; -use redox_driver_core::device::DeviceInfo; -use redox_driver_core::driver::{Driver, DriverError, ProbeResult}; -use redox_driver_core::params::{DriverParams, ParamValue}; -use redox_driver_core::r#match::DriverMatch; - -use serde::Deserialize; - -#[derive(Debug)] -struct SpawnedDriver { - pid: u32, - bind_handle: File, -} - -#[derive(Debug)] -pub struct DriverConfig { - pub name: String, - pub description: String, - pub priority: i32, - pub command: Vec, - pub matches: Vec, - spawned: Mutex>, -} - -impl Clone for DriverConfig { - fn clone(&self) -> Self { - DriverConfig { - name: self.name.clone(), - description: self.description.clone(), - priority: self.priority, - command: self.command.clone(), - matches: self.matches.clone(), - spawned: Mutex::new(HashMap::new()), - } - } -} - -#[derive(Deserialize)] -struct RawDriverMatch { - vendor: Option, - device: Option, - class: Option, - subclass: Option, - prog_if: Option, - subsystem_vendor: Option, - subsystem_device: Option, -} - -impl From for DriverMatch { - fn from(r: RawDriverMatch) -> Self { - DriverMatch { - vendor: r.vendor, - device: r.device, - class: r.class, - subclass: r.subclass, - prog_if: r.prog_if, - subsystem_vendor: r.subsystem_vendor, - subsystem_device: r.subsystem_device, - } - } -} - -impl DriverConfig { - pub fn load_all(dir: &str) -> Result, String> { - let entries = fs::read_dir(dir).map_err(|e| format!("read_dir failed: {}", e))?; - - let mut configs = Vec::new(); - - for entry in entries { - let entry = entry.map_err(|e| format!("entry error: {}", e))?; - let path = entry.path(); - - if !path.is_file() { - continue; - } - - let data = fs::read_to_string(&path) - .map_err(|e| format!("read {} failed: {}", path.display(), e))?; - - let parsed: RawDriverToml = toml::from_str(&data) - .map_err(|e| format!("parse {} failed: {}", path.display(), e))?; - - for driver in parsed.driver { - let matches: Vec = driver.r#match.into_iter().map(DriverMatch::from).collect(); - - configs.push(DriverConfig { - name: driver.name, - description: driver.description, - priority: driver.priority, - command: driver.command, - matches, - spawned: Mutex::new(HashMap::new()), - }); - } - } - - configs.sort_by(|a, b| b.priority.cmp(&a.priority)); - Ok(configs) - } -} - -fn pci_device_path(info: &DeviceInfo) -> String { - if info.raw_path.starts_with("/scheme/pci/") { - info.raw_path.clone() - } else { - format!("/scheme/pci/{}", info.id.path) - } -} - -fn claim_pci_device(info: &DeviceInfo) -> Result<(String, File), ProbeResult> { - let device_path = pci_device_path(info); - let bind_path = format!("{}/bind", device_path); - - match OpenOptions::new().read(true).write(true).open(&bind_path) { - Ok(bind_handle) => Ok((device_path, bind_handle)), - Err(err) => match err.raw_os_error() { - Some(code) if code == syscall::EALREADY as i32 || code == 114 => { - log::debug!("device {} already claimed via {}", info.id.path, bind_path); - Err(ProbeResult::NotSupported) - } - _ => Err(ProbeResult::Deferred { - reason: format!("bind {} failed: {}", bind_path, err), - }), - }, - } -} - -fn open_pcid_channel(device_path: &str) -> Result { - let mut handle = match PciFunctionHandle::connect_by_path(Path::new(device_path)) { - Ok(handle) => handle, - Err(err) => { - return Err(ProbeResult::Deferred { - reason: format!("open channel for {} failed: {}", device_path, err), - }); - } - }; - - handle.enable_device(); - - let channel_fd = handle.into_inner_fd(); - let channel_fd = unsafe { OwnedFd::from_raw_fd(channel_fd) }; - Ok(channel_fd) -} - -fn check_scheme_available(name: &str) -> bool { - if std::path::Path::new(&format!("/scheme/{}", name)).exists() { - return true; - } - false -} - -impl Driver for DriverConfig { - fn name(&self) -> &str { - &self.name - } - - fn description(&self) -> &str { - &self.description - } - - fn priority(&self) -> i32 { - self.priority - } - - fn match_table(&self) -> &[DriverMatch] { - &self.matches - } - - fn probe(&self, info: &DeviceInfo) -> ProbeResult { - let device_key = info.id.path.clone(); - - { - let spawned = self.spawned.lock().unwrap(); - if spawned.contains_key(&device_key) { - log::debug!("driver {} already bound to {}", self.name, device_key); - return ProbeResult::Bound; - } - } - - if self.command.is_empty() { - return ProbeResult::Fatal { - reason: String::from("empty command"), - }; - } - - let actual_path = if self.command[0].starts_with('/') { - self.command[0].clone() - } else { - format!("/usr/lib/drivers/{}", self.command[0]) - }; - - if !std::path::Path::new(&actual_path).exists() { - return ProbeResult::Deferred { - reason: format!("driver binary not found: {}", actual_path), - }; - } - - let deps = guess_dependencies(&self.name); - for dep in &deps { - if !check_scheme_available(dep) { - return ProbeResult::Deferred { - reason: format!("dependency scheme not ready: {}", dep), - }; - } - } - - log::info!("probing {} with driver {}", device_key, self.name); - - let (device_path, bind_handle) = match claim_pci_device(info) { - Ok(claimed) => claimed, - Err(result) => return result, - }; - - let channel_fd = match open_pcid_channel(&device_path) { - Ok(channel_fd) => channel_fd, - Err(result) => return result, - }; - - let mut cmd = Command::new(&actual_path); - for arg in &self.command[1..] { - cmd.arg(arg); - } - - cmd.env("PCID_CLIENT_CHANNEL", channel_fd.as_raw_fd().to_string()); - cmd.env("PCID_DEVICE_PATH", &device_path); - - match cmd.spawn() { - Ok(child) => { - let pid = child.id(); - log::info!( - "driver {} spawned (pid {}) for device {}", - self.name, pid, device_key - ); - let mut spawned = self.spawned.lock().unwrap(); - spawned.insert(device_key, SpawnedDriver { pid, bind_handle }); - ProbeResult::Bound - } - Err(e) => ProbeResult::Fatal { - reason: format!("spawn failed: {}", e), - }, - } - } - - fn remove(&self, info: &DeviceInfo) -> Result<(), DriverError> { - let device_key = info.id.path.clone(); - let binding = { - let mut spawned = self.spawned.lock().unwrap(); - spawned.remove(&device_key) - }; - - match binding { - Some(binding) => { - let bind_fd = binding.bind_handle.as_raw_fd(); - log::info!( - "unbound: device {} from driver {} (pid {}, bind fd {})", - device_key, - self.name, - binding.pid, - bind_fd - ); - Ok(()) - } - _ => { - log::warn!("driver {} not bound to device {}", self.name, device_key); - Err(DriverError::Other("not bound")) - } - } - } - - fn params(&self) -> DriverParams { - let mut p = DriverParams::new(); - p.define("enabled", "Whether this driver is active", ParamValue::Bool(true), true); - p.define( - "priority", - "Probe priority (higher = earlier)", - ParamValue::Int(self.priority as i64), - false, - ); - p - } -} - -fn guess_dependencies(driver_name: &str) -> Vec { - match driver_name { - "xhcid" | "usbhubd" | "usbctl" | "usbhidd" | "usbscsid" => { - vec![String::from("pci")] - } - "nvmed" | "ahcid" | "ided" | "virtio-blkd" => { - vec![String::from("pci")] - } - "e1000d" | "rtl8168d" | "rtl8139d" | "ixgbed" | "virtio-netd" => { - vec![String::from("pci")] - } - "vesad" | "virtio-gpud" | "redox-drm" => { - vec![String::from("pci")] - } - "ihdad" | "ac97d" | "sb16d" => { - vec![String::from("pci")] - } - "ps2d" => vec![String::from("serio")], - "i2c-hidd" => vec![String::from("i2c")], - "dw-acpi-i2cd" | "amd-mp2-i2cd" | "intel-lpss-i2cd" => { - vec![String::from("acpi"), String::from("i2c")] - } - _ => vec![String::from("pci")], - } -} - -#[derive(Deserialize)] -struct RawDriverToml { - driver: Vec, -} - -#[derive(Deserialize)] -struct RawDriverEntry { - name: String, - #[serde(default)] - description: String, - #[serde(default)] - priority: i32, - #[serde(default)] - command: Vec, - #[serde(rename = "match")] - r#match: Vec, -} diff --git a/local/recipes/system/driver-manager/src/exec.rs b/local/recipes/system/driver-manager/src/exec.rs deleted file mode 100644 index b5e9960b2c..0000000000 --- a/local/recipes/system/driver-manager/src/exec.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::process::Command; - -#[allow(dead_code)] -pub fn spawn_driver(command: &[String]) -> Result { - if command.is_empty() { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "empty command", - )); - } - - let mut cmd = Command::new(&command[0]); - for arg in &command[1..] { - cmd.arg(arg); - } - - cmd.spawn() -} diff --git a/local/recipes/system/driver-manager/src/hotplug.rs b/local/recipes/system/driver-manager/src/hotplug.rs deleted file mode 100644 index fe1f2f325c..0000000000 --- a/local/recipes/system/driver-manager/src/hotplug.rs +++ /dev/null @@ -1,74 +0,0 @@ -use std::thread; -use std::time::Duration; -use std::sync::{Arc, Mutex}; - -use redox_driver_core::manager::DeviceManager; -use redox_driver_core::manager::ProbeEvent; -use redox_driver_core::driver::ProbeResult; - -pub fn run_hotplug_loop( - manager: Arc>, - poll_interval_ms: u64, -) { - log::info!("hotplug: starting event loop ({} ms poll)", poll_interval_ms); - - loop { - thread::sleep(Duration::from_millis(poll_interval_ms)); - - let events = { - let mut mgr = manager.lock().unwrap(); - mgr.enumerate() - }; - - for event in &events { - match event { - ProbeEvent::ProbeCompleted { device, driver_name, result } => { - match result { - ProbeResult::Bound => { - log::info!("hotplug: bound {} -> {}", device.path, driver_name); - } - ProbeResult::Deferred { reason } => { - log::info!( - "hotplug: deferred {} -> {} ({})", - device.path, - driver_name, - reason - ); - } - ProbeResult::Fatal { reason } => { - log::error!( - "hotplug: fatal {} -> {} ({})", - device.path, - driver_name, - reason - ); - } - _ => {} - } - } - ProbeEvent::NoDriverFound { device } => { - log::debug!("hotplug: no driver for new device {}", device.path); - } - _ => {} - } - } - - let retry_events = { - let mut mgr = manager.lock().unwrap(); - mgr.retry_deferred() - }; - - let mut resolved = 0usize; - for event in &retry_events { - if let ProbeEvent::ProbeCompleted { result, .. } = event { - if *result == ProbeResult::Bound { - resolved += 1; - } - } - } - - if resolved > 0 { - log::info!("hotplug: resolved {} deferred probes", resolved); - } - } -} diff --git a/local/recipes/system/driver-manager/src/main.rs b/local/recipes/system/driver-manager/src/main.rs deleted file mode 100644 index 32a2b0d553..0000000000 --- a/local/recipes/system/driver-manager/src/main.rs +++ /dev/null @@ -1,184 +0,0 @@ -mod config; -mod exec; -mod hotplug; - -use std::sync::{Arc, Mutex}; -use std::thread; -use std::time::{Duration, Instant}; -use std::{process, env}; - -use redox_driver_core::manager::{DeviceManager, ManagerConfig, ProbeEvent}; -use redox_driver_core::driver::ProbeResult; -use redox_driver_pci::PciBus; - -use config::DriverConfig; - -struct StderrLogger; - -impl log::Log for StderrLogger { - fn enabled(&self, metadata: &log::Metadata) -> bool { - metadata.level() <= log::Level::Info - } - fn log(&self, record: &log::Record) { - if self.enabled(record.metadata()) { - eprintln!("[{}] driver-manager: {}", record.level(), record.args()); - } - } - fn flush(&self) {} -} - -fn run_enumeration( - manager: &Arc>, -) -> (usize, usize) { - let events = { - let mut mgr = manager.lock().unwrap(); - mgr.enumerate() - }; - - let mut bound = 0usize; - let mut deferred = 0usize; - let mut durations: Vec = Vec::new(); - - for event in &events { - match event { - ProbeEvent::ProbeCompleted { device, driver_name, result } => { - let start = Instant::now(); - match result { - ProbeResult::Bound => { - let duration = start.elapsed(); - durations.push(duration.as_millis()); - log::info!("probed: {} -> {} ({}ms)", device.path, driver_name, duration.as_millis()); - log::info!("bound: {} -> {}", device.path, driver_name); - bound += 1; - } - ProbeResult::Deferred { reason } => { - let duration = start.elapsed(); - log::info!("probed: {} -> {} ({}ms)", device.path, driver_name, duration.as_millis()); - log::info!("deferred: {} -> {} ({})", device.path, driver_name, reason); - deferred += 1; - } - ProbeResult::Fatal { reason } => { - let duration = start.elapsed(); - log::info!("probed: {} -> {} ({}ms)", device.path, driver_name, duration.as_millis()); - log::error!("fatal: {} -> {} ({})", device.path, driver_name, reason); - } - _ => {} - } - } - ProbeEvent::BusEnumerated { bus, device_count } => { - log::info!("bus {} enumerated {} device(s)", bus, device_count); - } - _ => {} - } - } - - if !durations.is_empty() { - let sum: u128 = durations.iter().sum(); - let avg = sum / durations.len() as u128; - let max = *durations.iter().max().unwrap_or(&0); - log::info!("probe summary: {} drivers, avg {}ms, max {}ms", durations.len(), avg, max); - } - - (bound, deferred) -} - -fn main() { - log::set_logger(&StderrLogger).ok(); - log::set_max_level(log::LevelFilter::Info); - - let args: Vec = env::args().collect(); - let initfs = args.iter().any(|a| a == "--initfs"); - let hotplug_mode = args.iter().any(|a| a == "--hotplug"); - - let config_dir = if initfs { - "/scheme/initfs/lib/drivers.d" - } else { - "/lib/drivers.d" - }; - - let driver_configs = match DriverConfig::load_all(config_dir) { - Ok(c) => c, - Err(e) => { - log::error!("failed to load driver configs: {}", e); - process::exit(1); - } - }; - - if driver_configs.is_empty() { - log::warn!("no driver configs found in {}", config_dir); - process::exit(0); - } - - log::info!("loaded {} driver config(s)", driver_configs.len()); - - let manager_config = ManagerConfig { - max_concurrent_probes: 4, - deferred_retry_ms: 500, - async_probe: true, - }; - - let manager = Arc::new(Mutex::new(DeviceManager::new(manager_config.clone()))); - - { - let mut mgr = manager.lock().unwrap(); - mgr.register_bus(Box::new(PciBus::new())); - - for dc in &driver_configs { - mgr.register_driver(Box::new(dc.clone())); - } - } - - let mgr_clone = Arc::clone(&manager); - - if manager_config.async_probe { - let handle = thread::spawn(move || { - let (bound, deferred) = run_enumeration(&mgr_clone); - log::info!("async enum: {} bound, {} deferred", bound, deferred); - }); - let _ = handle.join(); - } else { - let (bound, deferred) = run_enumeration(&manager); - log::info!("enum complete: {} bound, {} deferred", bound, deferred); - } - - if hotplug_mode { - log::info!("entering hotplug event loop"); - hotplug::run_hotplug_loop(manager.clone(), 2000); - return; - } - - let max_retries = 30u32; - for retry in 1..=max_retries { - thread::sleep(Duration::from_millis(500)); - - let retry_events = { - let mut mgr = manager.lock().unwrap(); - mgr.retry_deferred() - }; - - let mut remaining = 0; - let mut newly_bound = 0; - - for event in &retry_events { - if let ProbeEvent::ProbeCompleted { result, .. } = event { - match result { - ProbeResult::Bound => newly_bound += 1, - ProbeResult::Deferred { .. } => remaining += 1, - _ => {} - } - } - } - - if remaining == 0 { - log::info!("all deferred resolved after {} retries", retry); - return; - } - - if newly_bound > 0 { - log::info!("retry #{}: {} new, {} remaining", retry, newly_bound, remaining); - } - } - - log::warn!("deferred probe retry limit reached"); - process::exit(0); -}