Complete base patch split and update rust toolchain
Base patch extraction (12 new patches, 11,017 lines from the 17k monolith): - P2-acpid-core-refactor: acpi.rs, dmar, aml_physmem, ec, scheme (3,150 lines) - P2-ihdad-device-refactor: CodecTopology, ControllerPolicy, InputStream (1,022 lines) - P2-ac97d-ihdad-main: AC97 + ihdad daemon error handling (287 lines) - P2-inputd: inputd lib + main with named producers (896 lines) - P2-network-driver-mains: e1000/ixgbe/rtl8139/rtl8168d/virtio-net mains (607 lines) - P2-pcid-driver-interface: BAR, cap, config, IRQ helpers, MSI, scheme (1,463 lines) - P2-storage-driver-mains: ahcid/ided/nvmed/virtio-blk main.rs files (625 lines) - P2-xhcid-remaining: xhcid main, device_enumerator, xhci mod+scheme (2,033 lines) - P2-virtio-core-vbox: virtio-core arch/probe/transport + vboxd (413 lines) - P2-init-subsystems: scheduler, service, unit management (292 lines) - P2-logd: logd main + scheme (164 lines) - P2-hwd-misc: hwd Cargo.toml + main.rs (64 lines) Graphics drivers (ihdgd, vesad, virtio-gpud, fbcond scheme/text, graphics-ipc) already fully covered by existing P2-daemon-hardening.patch — no duplicates created. Rust toolchain: nightly-2025-10-03 → nightly-2026-04-01 (1.96.0-nightly). Cookbook builds clean, no feature gates in codebase.
This commit is contained in:
@@ -0,0 +1,287 @@
|
|||||||
|
# P2-ac97d-ihdad-main.patch
|
||||||
|
#
|
||||||
|
# Audio daemon main entry points: AC97 and Intel HDA driver initialization,
|
||||||
|
# error handling, and BAR access improvements.
|
||||||
|
#
|
||||||
|
# Covers:
|
||||||
|
# - ac97d/src/main.rs: BAR access, error handling, codec initialization
|
||||||
|
# - ihdad/src/main.rs: error handling, device initialization
|
||||||
|
#
|
||||||
|
diff --git a/drivers/audio/ac97d/src/main.rs b/drivers/audio/ac97d/src/main.rs
|
||||||
|
index ffa8a94b..e4dbf930 100644
|
||||||
|
--- a/drivers/audio/ac97d/src/main.rs
|
||||||
|
+++ b/drivers/audio/ac97d/src/main.rs
|
||||||
|
@@ -3,6 +3,7 @@ use std::os::unix::io::AsRawFd;
|
||||||
|
use std::usize;
|
||||||
|
|
||||||
|
use event::{user_data, EventQueue};
|
||||||
|
+use log::error;
|
||||||
|
use pcid_interface::PciFunctionHandle;
|
||||||
|
use redox_scheme::scheme::register_sync_scheme;
|
||||||
|
use redox_scheme::Socket;
|
||||||
|
@@ -22,13 +23,28 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
let mut name = pci_config.func.name();
|
||||||
|
name.push_str("_ac97");
|
||||||
|
|
||||||
|
- let bar0 = pci_config.func.bars[0].expect_port();
|
||||||
|
- let bar1 = pci_config.func.bars[1].expect_port();
|
||||||
|
+ let bar0 = match pci_config.func.bars[0].try_port() {
|
||||||
|
+ Ok(port) => port,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ error!("ac97d: invalid BAR0: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ let bar1 = match pci_config.func.bars[1].try_port() {
|
||||||
|
+ Ok(port) => port,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ error!("ac97d: invalid BAR1: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
|
||||||
|
let irq = pci_config
|
||||||
|
.func
|
||||||
|
.legacy_interrupt_line
|
||||||
|
- .expect("ac97d: no legacy interrupts supported");
|
||||||
|
+ .unwrap_or_else(|| {
|
||||||
|
+ error!("ac97d: no legacy interrupts supported");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
println!(" + ac97 {}", pci_config.func.display());
|
||||||
|
|
||||||
|
@@ -40,13 +56,35 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
common::file_level(),
|
||||||
|
);
|
||||||
|
|
||||||
|
- common::acquire_port_io_rights().expect("ac97d: failed to set I/O privilege level to Ring 3");
|
||||||
|
+ if let Err(err) = common::acquire_port_io_rights() {
|
||||||
|
+ error!("ac97d: failed to set I/O privilege level to Ring 3: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- let mut irq_file = irq.irq_handle("ac97d");
|
||||||
|
+ let mut irq_file = match irq.try_irq_handle("ac97d") {
|
||||||
|
+ Ok(file) => file,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ error!("ac97d: failed to open IRQ handle: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
|
||||||
|
- let socket = Socket::nonblock().expect("ac97d: failed to create socket");
|
||||||
|
- let mut device =
|
||||||
|
- unsafe { device::Ac97::new(bar0, bar1).expect("ac97d: failed to allocate device") };
|
||||||
|
+ let socket = match Socket::nonblock() {
|
||||||
|
+ Ok(socket) => socket,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ error!("ac97d: failed to create socket: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ let mut device = unsafe {
|
||||||
|
+ match device::Ac97::new(bar0, bar1) {
|
||||||
|
+ Ok(device) => device,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ error!("ac97d: failed to allocate device: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
let mut readiness_based = ReadinessBased::new(&socket, 16);
|
||||||
|
|
||||||
|
user_data! {
|
||||||
|
@@ -56,49 +94,81 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- let event_queue = EventQueue::<Source>::new().expect("ac97d: Could not create event queue.");
|
||||||
|
+ let event_queue = match EventQueue::<Source>::new() {
|
||||||
|
+ Ok(queue) => queue,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ error!("ac97d: could not create event queue: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
event_queue
|
||||||
|
.subscribe(
|
||||||
|
irq_file.as_raw_fd() as usize,
|
||||||
|
Source::Irq,
|
||||||
|
event::EventFlags::READ,
|
||||||
|
)
|
||||||
|
- .unwrap();
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ error!("ac97d: failed to subscribe IRQ fd: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ });
|
||||||
|
event_queue
|
||||||
|
.subscribe(
|
||||||
|
socket.inner().raw(),
|
||||||
|
Source::Scheme,
|
||||||
|
event::EventFlags::READ,
|
||||||
|
)
|
||||||
|
- .unwrap();
|
||||||
|
-
|
||||||
|
- register_sync_scheme(&socket, "audiohw", &mut device)
|
||||||
|
- .expect("ac97d: failed to register audiohw scheme to namespace");
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ error!("ac97d: failed to subscribe scheme fd: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ register_sync_scheme(&socket, "audiohw", &mut device).unwrap_or_else(|err| {
|
||||||
|
+ error!("ac97d: failed to register audiohw scheme to namespace: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ });
|
||||||
|
daemon.ready();
|
||||||
|
|
||||||
|
- libredox::call::setrens(0, 0).expect("ac97d: failed to enter null namespace");
|
||||||
|
+ if let Err(err) = libredox::call::setrens(0, 0) {
|
||||||
|
+ error!("ac97d: failed to enter null namespace: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
let all = [Source::Irq, Source::Scheme];
|
||||||
|
- for event in all
|
||||||
|
- .into_iter()
|
||||||
|
- .chain(event_queue.map(|e| e.expect("ac97d: failed to get next event").user_data))
|
||||||
|
- {
|
||||||
|
+ for event in all.into_iter().chain(event_queue.map(|e| match e {
|
||||||
|
+ Ok(event) => event.user_data,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ error!("ac97d: failed to get next event: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ })) {
|
||||||
|
match event {
|
||||||
|
Source::Irq => {
|
||||||
|
let mut irq = [0; 8];
|
||||||
|
- irq_file.read(&mut irq).unwrap();
|
||||||
|
+ if let Err(err) = irq_file.read(&mut irq) {
|
||||||
|
+ error!("ac97d: failed to read IRQ file: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
if !device.irq() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
- irq_file.write(&mut irq).unwrap();
|
||||||
|
+ if let Err(err) = irq_file.write(&mut irq) {
|
||||||
|
+ error!("ac97d: failed to acknowledge IRQ: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
readiness_based
|
||||||
|
.poll_all_requests(&mut device)
|
||||||
|
- .expect("ac97d: failed to poll requests");
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ error!("ac97d: failed to poll requests: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ });
|
||||||
|
readiness_based
|
||||||
|
.write_responses()
|
||||||
|
- .expect("ac97d: failed to write to socket");
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ error!("ac97d: failed to write to socket: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
/*
|
||||||
|
let next_read = device_irq.next_read();
|
||||||
|
@@ -110,10 +180,16 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
Source::Scheme => {
|
||||||
|
readiness_based
|
||||||
|
.read_and_process_requests(&mut device)
|
||||||
|
- .expect("ac97d: failed to read from socket");
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ error!("ac97d: failed to read from socket: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ });
|
||||||
|
readiness_based
|
||||||
|
.write_responses()
|
||||||
|
- .expect("ac97d: failed to write to socket");
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ error!("ac97d: failed to write to socket: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
/*
|
||||||
|
let next_read = device.borrow().next_read();
|
||||||
|
@@ -125,7 +201,7 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- std::process::exit(0);
|
||||||
|
+ std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||||
|
|
||||||
|
diff --git a/drivers/audio/ihdad/src/main.rs b/drivers/audio/ihdad/src/main.rs
|
||||||
|
index 31a2add7..11d80133 100755
|
||||||
|
--- a/drivers/audio/ihdad/src/main.rs
|
||||||
|
+++ b/drivers/audio/ihdad/src/main.rs
|
||||||
|
@@ -6,7 +6,7 @@ use std::os::unix::io::AsRawFd;
|
||||||
|
use std::usize;
|
||||||
|
|
||||||
|
use event::{user_data, EventQueue};
|
||||||
|
-use pcid_interface::irq_helpers::pci_allocate_interrupt_vector;
|
||||||
|
+use pcid_interface::irq_helpers::try_pci_allocate_interrupt_vector;
|
||||||
|
use pcid_interface::PciFunctionHandle;
|
||||||
|
|
||||||
|
pub mod hda;
|
||||||
|
@@ -38,9 +38,19 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
|
||||||
|
log::info!("IHDA {}", pci_config.func.display());
|
||||||
|
|
||||||
|
+ if let Err(err) = pci_config.func.bars[0].try_mem() {
|
||||||
|
+ log::error!("ihdad: invalid BAR0: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
let address = unsafe { pcid_handle.map_bar(0) }.ptr.as_ptr() as usize;
|
||||||
|
|
||||||
|
- let irq_file = pci_allocate_interrupt_vector(&mut pcid_handle, "ihdad");
|
||||||
|
+ let irq_file = match try_pci_allocate_interrupt_vector(&mut pcid_handle, "ihdad") {
|
||||||
|
+ Ok(irq) => irq,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ log::error!("ihdad: failed to allocate interrupt vector: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
|
||||||
|
{
|
||||||
|
let vend_prod: u32 = ((pci_config.func.full_device_id.vendor_id as u32) << 16)
|
||||||
|
@@ -53,11 +63,28 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- let event_queue =
|
||||||
|
- EventQueue::<Source>::new().expect("ihdad: Could not create event queue.");
|
||||||
|
- let socket = Socket::nonblock().expect("ihdad: failed to create socket");
|
||||||
|
+ let event_queue = match EventQueue::<Source>::new() {
|
||||||
|
+ Ok(queue) => queue,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ log::error!("ihdad: could not create event queue: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ let socket = match Socket::nonblock() {
|
||||||
|
+ Ok(socket) => socket,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ log::error!("ihdad: failed to create socket: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
let mut device = unsafe {
|
||||||
|
- hda::IntelHDA::new(address, vend_prod).expect("ihdad: failed to allocate device")
|
||||||
|
+ match hda::IntelHDA::new(address, vend_prod) {
|
||||||
|
+ Ok(device) => device,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ log::error!("ihdad: failed to allocate device: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
};
|
||||||
|
let mut readiness_based = ReadinessBased::new(&socket, 16);
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,64 @@
|
|||||||
|
# P2-hwd-misc.patch
|
||||||
|
# Extract hwd (hardware daemon) Cargo.toml and main.rs improvements.
|
||||||
|
#
|
||||||
|
# Files: drivers/hwd/Cargo.toml, drivers/hwd/src/main.rs
|
||||||
|
|
||||||
|
diff --git a/drivers/hwd/Cargo.toml b/drivers/hwd/Cargo.toml
|
||||||
|
index 3d37cfb3..40b51a1b 100644
|
||||||
|
--- a/drivers/hwd/Cargo.toml
|
||||||
|
+++ b/drivers/hwd/Cargo.toml
|
||||||
|
@@ -6,6 +6,7 @@ edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
fdt.workspace = true
|
||||||
|
+libc.workspace = true
|
||||||
|
log.workspace = true
|
||||||
|
ron.workspace = true
|
||||||
|
libredox = { workspace = true, default-features = false, features = ["std", "call"] }
|
||||||
|
diff --git a/drivers/hwd/src/main.rs b/drivers/hwd/src/main.rs
|
||||||
|
index 79360e34..a0462f51 100644
|
||||||
|
--- a/drivers/hwd/src/main.rs
|
||||||
|
+++ b/drivers/hwd/src/main.rs
|
||||||
|
@@ -1,3 +1,5 @@
|
||||||
|
+use std::os::fd::AsRawFd;
|
||||||
|
+use std::os::unix::process::CommandExt;
|
||||||
|
use std::process;
|
||||||
|
|
||||||
|
mod backend;
|
||||||
|
@@ -37,8 +39,34 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||||
|
|
||||||
|
//TODO: launch pcid based on backend information?
|
||||||
|
// Must launch after acpid but before probe calls /scheme/acpi/symbols
|
||||||
|
- #[allow(deprecated, reason = "we can't yet move this to init")]
|
||||||
|
- daemon::Daemon::spawn(process::Command::new("pcid"));
|
||||||
|
+ // Fire-and-forget: daemon::Daemon::spawn blocks until pcid signals readiness,
|
||||||
|
+ // but pcid only signals after full PCI enumeration. If enumeration hangs on
|
||||||
|
+ // real hardware (unresponsive device, complex AML), hwd deadlocks initfs.
|
||||||
|
+ {
|
||||||
|
+ match std::io::pipe() {
|
||||||
|
+ Ok((_read_end, write_end)) => {
|
||||||
|
+ let write_fd: std::os::fd::OwnedFd = write_end.into();
|
||||||
|
+ let raw_fd = write_fd.as_raw_fd();
|
||||||
|
+ let mut cmd = std::process::Command::new("pcid");
|
||||||
|
+ cmd.env("INIT_NOTIFY", raw_fd.to_string());
|
||||||
|
+ unsafe {
|
||||||
|
+ cmd.pre_exec(move || {
|
||||||
|
+ if libc::fcntl(raw_fd, libc::F_SETFD, 0) == -1 {
|
||||||
|
+ return Err(std::io::Error::last_os_error());
|
||||||
|
+ }
|
||||||
|
+ Ok(())
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
|
+ match cmd.spawn() {
|
||||||
|
+ Ok(_) => {}
|
||||||
|
+ Err(err) => log::error!("hwd: failed to spawn pcid: {}", err),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ Err(err) => {
|
||||||
|
+ log::error!("hwd: failed to create pcid notification pipe: {}", err);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
|
||||||
|
daemon.ready();
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,292 @@
|
|||||||
|
# P2-init-subsystems.patch
|
||||||
|
# Extract init subsystem hardening: service respawn, unit store Option returns,
|
||||||
|
# scheduler PID tracking, and service spawn error handling.
|
||||||
|
#
|
||||||
|
# Files: init/src/scheduler.rs, init/src/service.rs, init/src/unit.rs
|
||||||
|
|
||||||
|
diff --git a/init/src/scheduler.rs b/init/src/scheduler.rs
|
||||||
|
index d42a4e57..64e64e1e 100644
|
||||||
|
--- a/init/src/scheduler.rs
|
||||||
|
+++ b/init/src/scheduler.rs
|
||||||
|
@@ -5,6 +5,7 @@ use crate::unit::{Unit, UnitId, UnitKind, UnitStore};
|
||||||
|
|
||||||
|
pub struct Scheduler {
|
||||||
|
pending: VecDeque<Job>,
|
||||||
|
+ pub respawn_pids: Vec<(UnitId, u32)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Job {
|
||||||
|
@@ -20,6 +21,7 @@ impl Scheduler {
|
||||||
|
pub fn new() -> Scheduler {
|
||||||
|
Scheduler {
|
||||||
|
pending: VecDeque::new(),
|
||||||
|
+ respawn_pids: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -43,7 +45,10 @@ impl Scheduler {
|
||||||
|
) {
|
||||||
|
let loaded_units = unit_store.load_units(unit_id.clone(), errors);
|
||||||
|
for unit_id in loaded_units {
|
||||||
|
- if !unit_store.unit(&unit_id).conditions_met() {
|
||||||
|
+ let Some(unit) = unit_store.unit(&unit_id) else {
|
||||||
|
+ continue;
|
||||||
|
+ };
|
||||||
|
+ if !unit.conditions_met() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -62,7 +67,10 @@ impl Scheduler {
|
||||||
|
|
||||||
|
match job.kind {
|
||||||
|
JobKind::Start => {
|
||||||
|
- let unit = unit_store.unit_mut(&job.unit);
|
||||||
|
+ let Some(unit) = unit_store.unit_mut(&job.unit) else {
|
||||||
|
+ eprintln!("init: unit {} not found in store, skipping", job.unit.0);
|
||||||
|
+ continue 'a;
|
||||||
|
+ };
|
||||||
|
|
||||||
|
for dep in &unit.info.requires_weak {
|
||||||
|
for pending_job in &self.pending {
|
||||||
|
@@ -73,14 +81,17 @@ impl Scheduler {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- run(unit, init_config);
|
||||||
|
+ let pid = run(unit, init_config);
|
||||||
|
+ if let Some(pid) = pid {
|
||||||
|
+ self.respawn_pids.push((job.unit.clone(), pid));
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-fn run(unit: &mut Unit, config: &mut InitConfig) {
|
||||||
|
+fn run(unit: &mut Unit, config: &mut InitConfig) -> Option<u32> {
|
||||||
|
match &unit.kind {
|
||||||
|
UnitKind::LegacyScript { script } => {
|
||||||
|
for cmd in script.clone() {
|
||||||
|
@@ -92,25 +103,30 @@ fn run(unit: &mut Unit, config: &mut InitConfig) {
|
||||||
|
}
|
||||||
|
UnitKind::Service { service } => {
|
||||||
|
if config.skip_cmd.contains(&service.cmd) {
|
||||||
|
- eprintln!("Skipping '{} {}'", service.cmd, service.args.join(" "));
|
||||||
|
- return;
|
||||||
|
+ eprintln!("init: skipping {} {}", service.cmd, service.args.join(" "));
|
||||||
|
+ return None;
|
||||||
|
}
|
||||||
|
- if config.log_debug {
|
||||||
|
+ eprintln!(
|
||||||
|
+ "init: starting {} ({})",
|
||||||
|
+ unit.info.description.as_ref().unwrap_or(&unit.id.0),
|
||||||
|
+ service.cmd,
|
||||||
|
+ );
|
||||||
|
+ let pid = service.spawn(&config.envs);
|
||||||
|
+ if pid.is_some() {
|
||||||
|
eprintln!(
|
||||||
|
- "Starting {} ({})",
|
||||||
|
+ "init: started {} (pid {})",
|
||||||
|
unit.info.description.as_ref().unwrap_or(&unit.id.0),
|
||||||
|
- service.cmd,
|
||||||
|
+ pid.unwrap_or(0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
- service.spawn(&config.envs);
|
||||||
|
+ return pid;
|
||||||
|
}
|
||||||
|
UnitKind::Target {} => {
|
||||||
|
- if config.log_debug {
|
||||||
|
- eprintln!(
|
||||||
|
- "Reached target {}",
|
||||||
|
- unit.info.description.as_ref().unwrap_or(&unit.id.0),
|
||||||
|
- );
|
||||||
|
- }
|
||||||
|
+ eprintln!(
|
||||||
|
+ "init: reached target {}",
|
||||||
|
+ unit.info.description.as_ref().unwrap_or(&unit.id.0),
|
||||||
|
+ );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ None
|
||||||
|
}
|
||||||
|
diff --git a/init/src/service.rs b/init/src/service.rs
|
||||||
|
index ed0023e9..cc95d02b 100644
|
||||||
|
--- a/init/src/service.rs
|
||||||
|
+++ b/init/src/service.rs
|
||||||
|
@@ -22,6 +22,8 @@ pub struct Service {
|
||||||
|
pub inherit_envs: BTreeSet<String>,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub type_: ServiceType,
|
||||||
|
+ #[serde(default)]
|
||||||
|
+ pub respawn: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize)]
|
||||||
|
@@ -35,7 +37,7 @@ pub enum ServiceType {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Service {
|
||||||
|
- pub fn spawn(&self, base_envs: &BTreeMap<String, OsString>) {
|
||||||
|
+ pub fn spawn(&self, base_envs: &BTreeMap<String, OsString>) -> Option<u32> {
|
||||||
|
let mut command = Command::new(&self.cmd);
|
||||||
|
command.args(self.args.iter().map(|arg| subst_env(arg)));
|
||||||
|
command.env_clear();
|
||||||
|
@@ -46,20 +48,28 @@ impl Service {
|
||||||
|
}
|
||||||
|
command.envs(base_envs).envs(&self.envs);
|
||||||
|
|
||||||
|
- let (mut read_pipe, write_pipe) = io::pipe().unwrap();
|
||||||
|
+ let (mut read_pipe, write_pipe) = match io::pipe() {
|
||||||
|
+ Ok(pair) => pair,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ eprintln!("init: failed to create readiness pipe for {:?}: {err}", command);
|
||||||
|
+ return None;
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
unsafe { pass_fd(&mut command, "INIT_NOTIFY", write_pipe.into()) };
|
||||||
|
|
||||||
|
let mut child = match command.spawn() {
|
||||||
|
Ok(child) => child,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("init: failed to execute {:?}: {}", command, err);
|
||||||
|
- return;
|
||||||
|
+ return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match &self.type_ {
|
||||||
|
ServiceType::Notify => match read_pipe.read_exact(&mut [0]) {
|
||||||
|
- Ok(()) => {}
|
||||||
|
+ Ok(()) => {
|
||||||
|
+ eprintln!("init: {} ready (notify)", self.cmd);
|
||||||
|
+ }
|
||||||
|
Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => {
|
||||||
|
eprintln!("init: {command:?} exited without notifying readiness");
|
||||||
|
}
|
||||||
|
@@ -81,23 +91,34 @@ impl Service {
|
||||||
|
}) => continue,
|
||||||
|
Ok(0) => {
|
||||||
|
eprintln!("init: {command:?} exited without notifying readiness");
|
||||||
|
- return;
|
||||||
|
+ return None;
|
||||||
|
}
|
||||||
|
Ok(1) => break,
|
||||||
|
Ok(n) => {
|
||||||
|
eprintln!("init: incorrect amount of fds {n} returned");
|
||||||
|
- return;
|
||||||
|
+ return None;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("init: failed to wait for {command:?}: {err}");
|
||||||
|
- return;
|
||||||
|
+ return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- let current_namespace_fd = libredox::call::getns().expect("TODO");
|
||||||
|
- libredox::call::register_scheme_to_ns(current_namespace_fd, scheme, new_fd)
|
||||||
|
- .expect("TODO");
|
||||||
|
+ let current_namespace_fd = match libredox::call::getns() {
|
||||||
|
+ Ok(fd) => fd,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ eprintln!("init: failed to get current namespace for {command:?}: {err}");
|
||||||
|
+ return None;
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ if let Err(err) =
|
||||||
|
+ libredox::call::register_scheme_to_ns(current_namespace_fd, scheme, new_fd)
|
||||||
|
+ {
|
||||||
|
+ eprintln!("init: failed to register scheme {scheme:?} for {command:?}: {err}");
|
||||||
|
+ } else {
|
||||||
|
+ eprintln!("init: {} ready (scheme {})", self.cmd, scheme);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
ServiceType::Oneshot => {
|
||||||
|
drop(read_pipe);
|
||||||
|
@@ -105,6 +126,8 @@ impl Service {
|
||||||
|
Ok(exit_status) => {
|
||||||
|
if !exit_status.success() {
|
||||||
|
eprintln!("init: {command:?} failed with {exit_status}");
|
||||||
|
+ } else {
|
||||||
|
+ eprintln!("init: {} done (oneshot)", self.cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
@@ -112,8 +135,13 @@ impl Service {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- ServiceType::OneshotAsync => {}
|
||||||
|
+ ServiceType::OneshotAsync => {
|
||||||
|
+ if self.respawn {
|
||||||
|
+ return Some(child.id());
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
+ None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/init/src/unit.rs b/init/src/unit.rs
|
||||||
|
index 98053cb2..a58bfb96 100644
|
||||||
|
--- a/init/src/unit.rs
|
||||||
|
+++ b/init/src/unit.rs
|
||||||
|
@@ -23,8 +23,14 @@ impl UnitStore {
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_runtime_target(&mut self, unit_id: UnitId) {
|
||||||
|
- assert!(self.runtime_target.is_none());
|
||||||
|
- assert!(self.units.contains_key(&unit_id));
|
||||||
|
+ if self.runtime_target.is_some() {
|
||||||
|
+ eprintln!("init: runtime target already set, ignoring {}", unit_id.0);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ if !self.units.contains_key(&unit_id) {
|
||||||
|
+ eprintln!("init: runtime target {} not found in unit store", unit_id.0);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
self.runtime_target = Some(unit_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -85,8 +91,10 @@ impl UnitStore {
|
||||||
|
let unit = self.load_single_unit(unit_id, errors);
|
||||||
|
if let Some(unit) = unit {
|
||||||
|
loaded_units.push(unit.clone());
|
||||||
|
- for dep in &self.unit(&unit).info.requires_weak {
|
||||||
|
- pending_units.push(dep.clone());
|
||||||
|
+ if let Some(u) = self.unit(&unit) {
|
||||||
|
+ for dep in &u.info.requires_weak {
|
||||||
|
+ pending_units.push(dep.clone());
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -94,12 +102,12 @@ impl UnitStore {
|
||||||
|
loaded_units
|
||||||
|
}
|
||||||
|
|
||||||
|
- pub fn unit(&self, unit: &UnitId) -> &Unit {
|
||||||
|
- self.units.get(unit).unwrap()
|
||||||
|
+ pub fn unit(&self, unit: &UnitId) -> Option<&Unit> {
|
||||||
|
+ self.units.get(unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
- pub fn unit_mut(&mut self, unit: &UnitId) -> &mut Unit {
|
||||||
|
- self.units.get_mut(unit).unwrap()
|
||||||
|
+ pub fn unit_mut(&mut self, unit: &UnitId) -> Option<&mut Unit> {
|
||||||
|
+ self.units.get_mut(unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -180,7 +188,7 @@ impl Unit {
|
||||||
|
) -> io::Result<Self> {
|
||||||
|
let config = fs::read_to_string(config_path)?;
|
||||||
|
|
||||||
|
- let Some(ext) = config_path.extension().map(|ext| ext.to_str().unwrap()) else {
|
||||||
|
+ let Some(ext) = config_path.extension().and_then(|ext| ext.to_str()) else {
|
||||||
|
let script = Script::from_str(&config, errors)?;
|
||||||
|
return Ok(Unit {
|
||||||
|
id,
|
||||||
@@ -0,0 +1,896 @@
|
|||||||
|
# P2-inputd.patch
|
||||||
|
# Extract inputd improvements: named producers, device consumers, hotplug events,
|
||||||
|
# error handling, and input scheme extensions.
|
||||||
|
#
|
||||||
|
# Files: drivers/inputd/src/lib.rs, drivers/inputd/src/main.rs
|
||||||
|
|
||||||
|
diff --git a/drivers/inputd/src/lib.rs b/drivers/inputd/src/lib.rs
|
||||||
|
index b68e8211..f07a411d 100644
|
||||||
|
--- a/drivers/inputd/src/lib.rs
|
||||||
|
+++ b/drivers/inputd/src/lib.rs
|
||||||
|
@@ -64,25 +64,53 @@ impl ConsumerHandle {
|
||||||
|
let fd = self.0.as_raw_fd();
|
||||||
|
let written = libredox::call::fpath(fd as usize, &mut buffer)?;
|
||||||
|
|
||||||
|
- assert!(written <= buffer.len());
|
||||||
|
-
|
||||||
|
- let mut display_path = PathBuf::from(
|
||||||
|
- std::str::from_utf8(&buffer[..written])
|
||||||
|
- .expect("init: display path UTF-8 check failed")
|
||||||
|
- .to_owned(),
|
||||||
|
- );
|
||||||
|
- display_path.set_file_name(format!(
|
||||||
|
- "v2/{}",
|
||||||
|
- display_path.file_name().unwrap().to_str().unwrap()
|
||||||
|
- ));
|
||||||
|
- let display_path = display_path.to_str().unwrap();
|
||||||
|
+ if written > buffer.len() {
|
||||||
|
+ return Err(io::Error::new(
|
||||||
|
+ io::ErrorKind::InvalidData,
|
||||||
|
+ "inputd: display path exceeded buffer size",
|
||||||
|
+ ));
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let path_str = std::str::from_utf8(&buffer[..written]).map_err(|e| {
|
||||||
|
+ io::Error::new(
|
||||||
|
+ io::ErrorKind::InvalidData,
|
||||||
|
+ format!("inputd: display path is not valid UTF-8: {e}"),
|
||||||
|
+ )
|
||||||
|
+ })?;
|
||||||
|
+ let mut display_path = PathBuf::from(path_str.to_owned());
|
||||||
|
+
|
||||||
|
+ let file_name = display_path
|
||||||
|
+ .file_name()
|
||||||
|
+ .and_then(|n| n.to_str())
|
||||||
|
+ .ok_or_else(|| {
|
||||||
|
+ io::Error::new(
|
||||||
|
+ io::ErrorKind::InvalidData,
|
||||||
|
+ format!(
|
||||||
|
+ "inputd: display path has no valid file name: {}",
|
||||||
|
+ display_path.display()
|
||||||
|
+ ),
|
||||||
|
+ )
|
||||||
|
+ })?;
|
||||||
|
+ display_path.set_file_name(format!("v2/{file_name}"));
|
||||||
|
+ let display_path_str = display_path.to_str().ok_or_else(|| {
|
||||||
|
+ io::Error::new(
|
||||||
|
+ io::ErrorKind::InvalidData,
|
||||||
|
+ format!(
|
||||||
|
+ "inputd: constructed display path is not valid UTF-8: {}",
|
||||||
|
+ display_path.display()
|
||||||
|
+ ),
|
||||||
|
+ )
|
||||||
|
+ })?;
|
||||||
|
|
||||||
|
let display_file =
|
||||||
|
- libredox::call::open(display_path, (O_CLOEXEC | O_NONBLOCK | O_RDWR) as _, 0)
|
||||||
|
+ libredox::call::open(display_path_str, (O_CLOEXEC | O_NONBLOCK | O_RDWR) as _, 0)
|
||||||
|
.map(|socket| unsafe { File::from_raw_fd(socket as RawFd) })
|
||||||
|
- .unwrap_or_else(|err| {
|
||||||
|
- panic!("failed to open display {}: {}", display_path, err);
|
||||||
|
- });
|
||||||
|
+ .map_err(|err| {
|
||||||
|
+ io::Error::new(
|
||||||
|
+ io::ErrorKind::Other,
|
||||||
|
+ format!("inputd: failed to open display {display_path_str}: {err}"),
|
||||||
|
+ )
|
||||||
|
+ })?;
|
||||||
|
|
||||||
|
Ok(display_file)
|
||||||
|
}
|
||||||
|
@@ -152,8 +180,12 @@ impl DisplayHandle {
|
||||||
|
|
||||||
|
if nread == 0 {
|
||||||
|
Ok(None)
|
||||||
|
+ } else if nread != size_of::<VtEvent>() {
|
||||||
|
+ Err(io::Error::new(
|
||||||
|
+ io::ErrorKind::InvalidData,
|
||||||
|
+ format!("inputd: partial vt event read: got {nread}, expected {}", size_of::<VtEvent>()),
|
||||||
|
+ ))
|
||||||
|
} else {
|
||||||
|
- assert_eq!(nread, size_of::<VtEvent>());
|
||||||
|
Ok(Some(event))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -171,13 +203,11 @@ impl ControlHandle {
|
||||||
|
Ok(Self(File::open(path)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
- /// Sent to Handle::Display
|
||||||
|
pub fn activate_vt(&mut self, vt: usize) -> io::Result<usize> {
|
||||||
|
let cmd = ControlEvent::from(VtActivate { vt });
|
||||||
|
self.0.write(unsafe { any_as_u8_slice(&cmd) })
|
||||||
|
}
|
||||||
|
|
||||||
|
- /// Sent to Handle::Producer
|
||||||
|
pub fn activate_keymap(&mut self, keymap: usize) -> io::Result<usize> {
|
||||||
|
let cmd = ControlEvent::from(KeymapActivate { keymap });
|
||||||
|
self.0.write(unsafe { any_as_u8_slice(&cmd) })
|
||||||
|
@@ -209,3 +239,195 @@ impl ProducerHandle {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+pub struct NamedProducerHandle(File);
|
||||||
|
+
|
||||||
|
+impl NamedProducerHandle {
|
||||||
|
+ pub fn new(name: &str) -> io::Result<Self> {
|
||||||
|
+ let path = format!("/scheme/input/producer/{name}");
|
||||||
|
+ Ok(Self(File::open(path)?))
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ pub fn write_event(&mut self, event: orbclient::Event) -> io::Result<()> {
|
||||||
|
+ self.0.write(&event)?;
|
||||||
|
+ Ok(())
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/// Convenience wrapper that tries a named producer first,
|
||||||
|
+/// falling back to the legacy anonymous producer on failure.
|
||||||
|
+pub enum InputProducer {
|
||||||
|
+ Named(NamedProducerHandle),
|
||||||
|
+ Legacy(ProducerHandle),
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl InputProducer {
|
||||||
|
+ /// Open a named producer (`/scheme/input/producer/{name}`).
|
||||||
|
+ /// If the named path is unavailable, fall back to the legacy
|
||||||
|
+ /// `/scheme/input/producer` path so the driver keeps working on
|
||||||
|
+ /// older inputd builds or degraded schemes.
|
||||||
|
+ pub fn new_named_or_fallback(name: &str) -> io::Result<Self> {
|
||||||
|
+ match NamedProducerHandle::new(name) {
|
||||||
|
+ Ok(named) => Ok(InputProducer::Named(named)),
|
||||||
|
+ Err(named_err) => {
|
||||||
|
+ log::debug!(
|
||||||
|
+ "inputd: named producer '{}' unavailable ({}), falling back to legacy",
|
||||||
|
+ name,
|
||||||
|
+ named_err
|
||||||
|
+ );
|
||||||
|
+ ProducerHandle::new().map(InputProducer::Legacy)
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /// Open the legacy anonymous producer directly.
|
||||||
|
+ pub fn new_legacy() -> io::Result<Self> {
|
||||||
|
+ ProducerHandle::new().map(InputProducer::Legacy)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ pub fn write_event(&mut self, event: orbclient::Event) -> io::Result<()> {
|
||||||
|
+ match self {
|
||||||
|
+ InputProducer::Named(h) => h.write_event(event),
|
||||||
|
+ InputProducer::Legacy(h) => h.write_event(event),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+pub struct DeviceConsumerHandle(File);
|
||||||
|
+
|
||||||
|
+pub enum DeviceConsumerHandleEvent<'a> {
|
||||||
|
+ Events(&'a [Event]),
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl DeviceConsumerHandle {
|
||||||
|
+ pub fn new(device_name: &str) -> io::Result<Self> {
|
||||||
|
+ let path = format!("/scheme/input/{device_name}");
|
||||||
|
+ Ok(Self(File::open(path)?))
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ pub fn event_handle(&self) -> BorrowedFd<'_> {
|
||||||
|
+ self.0.as_fd()
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ pub fn read_events<'a>(
|
||||||
|
+ &self,
|
||||||
|
+ events: &'a mut [Event],
|
||||||
|
+ ) -> io::Result<DeviceConsumerHandleEvent<'a>> {
|
||||||
|
+ match read_to_slice(self.0.as_fd(), events) {
|
||||||
|
+ Ok(count) => Ok(DeviceConsumerHandleEvent::Events(&events[..count])),
|
||||||
|
+ Err(err) => Err(err.into()),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#[derive(Debug, Clone)]
|
||||||
|
+#[repr(C)]
|
||||||
|
+pub struct HotplugEventHeader {
|
||||||
|
+ pub kind: u32,
|
||||||
|
+ pub device_id: u32,
|
||||||
|
+ pub name_len: u32,
|
||||||
|
+ pub reserved: u32,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#[derive(Debug, Clone)]
|
||||||
|
+pub struct HotplugEvent {
|
||||||
|
+ pub kind: u32,
|
||||||
|
+ pub device_id: u32,
|
||||||
|
+ pub name: String,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+pub struct HotplugHandle {
|
||||||
|
+ file: File,
|
||||||
|
+ partial: Vec<u8>,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl HotplugHandle {
|
||||||
|
+ pub fn new() -> io::Result<Self> {
|
||||||
|
+ let file = File::open("/scheme/input/events")?;
|
||||||
|
+ Ok(Self {
|
||||||
|
+ file,
|
||||||
|
+ partial: Vec::new(),
|
||||||
|
+ })
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ pub fn event_handle(&self) -> BorrowedFd<'_> {
|
||||||
|
+ self.file.as_fd()
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ pub fn read_event(&mut self) -> io::Result<Option<HotplugEvent>> {
|
||||||
|
+ let mut tmp = [0u8; 256];
|
||||||
|
+ match self.file.read(&mut tmp) {
|
||||||
|
+ Ok(0) => {}
|
||||||
|
+ Ok(n) => self.partial.extend_from_slice(&tmp[..n]),
|
||||||
|
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
|
||||||
|
+ Err(e) => return Err(e),
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if self.partial.len() < 16 {
|
||||||
|
+ return Ok(None);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let header = HotplugEventHeader {
|
||||||
|
+ kind: u32::from_ne_bytes(self.partial[0..4].try_into().map_err(|_| {
|
||||||
|
+ io::Error::new(io::ErrorKind::InvalidData, "header parse failed")
|
||||||
|
+ })?),
|
||||||
|
+ device_id: u32::from_ne_bytes(self.partial[4..8].try_into().map_err(|_| {
|
||||||
|
+ io::Error::new(io::ErrorKind::InvalidData, "header parse failed")
|
||||||
|
+ })?),
|
||||||
|
+ name_len: u32::from_ne_bytes(self.partial[8..12].try_into().map_err(|_| {
|
||||||
|
+ io::Error::new(io::ErrorKind::InvalidData, "header parse failed")
|
||||||
|
+ })?),
|
||||||
|
+ reserved: 0,
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ let total_len = 16 + header.name_len as usize;
|
||||||
|
+ if self.partial.len() < total_len {
|
||||||
|
+ return Ok(None);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let name = String::from_utf8(self.partial[16..total_len].to_vec()).map_err(|e| {
|
||||||
|
+ io::Error::new(io::ErrorKind::InvalidData, format!("invalid UTF-8: {e}"))
|
||||||
|
+ })?;
|
||||||
|
+
|
||||||
|
+ self.partial.drain(..total_len);
|
||||||
|
+
|
||||||
|
+ Ok(Some(HotplugEvent {
|
||||||
|
+ kind: header.kind,
|
||||||
|
+ device_id: header.device_id,
|
||||||
|
+ name,
|
||||||
|
+ }))
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+pub const RESERVED_DEVICE_NAMES: &[&str] = &[
|
||||||
|
+ "producer",
|
||||||
|
+ "consumer",
|
||||||
|
+ "consumer_bootlog",
|
||||||
|
+ "events",
|
||||||
|
+ "handle",
|
||||||
|
+ "handle_early",
|
||||||
|
+ "control",
|
||||||
|
+];
|
||||||
|
+
|
||||||
|
+pub struct InputDeviceLister;
|
||||||
|
+
|
||||||
|
+impl InputDeviceLister {
|
||||||
|
+ pub fn list() -> io::Result<Vec<String>> {
|
||||||
|
+ let mut dir = std::fs::read_dir("/scheme/input/")?;
|
||||||
|
+ let mut devices = Vec::new();
|
||||||
|
+ loop {
|
||||||
|
+ match dir.next() {
|
||||||
|
+ Some(Ok(entry)) => {
|
||||||
|
+ if let Some(name) = entry.file_name().to_str() {
|
||||||
|
+ if !RESERVED_DEVICE_NAMES.contains(&name) {
|
||||||
|
+ devices.push(name.to_owned());
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ Some(Err(e)) => return Err(e),
|
||||||
|
+ None => break,
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ Ok(devices)
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
diff --git a/drivers/inputd/src/main.rs b/drivers/inputd/src/main.rs
|
||||||
|
index 07aa943e..89018568 100644
|
||||||
|
--- a/drivers/inputd/src/main.rs
|
||||||
|
+++ b/drivers/inputd/src/main.rs
|
||||||
|
@@ -13,7 +13,7 @@
|
||||||
|
|
||||||
|
use core::mem::size_of;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
-use std::collections::BTreeSet;
|
||||||
|
+use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
use std::mem::transmute;
|
||||||
|
use std::ops::ControlFlow;
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
@@ -26,8 +26,9 @@ use redox_scheme::{CallerCtx, OpenResult, Response, SignalBehavior, Socket};
|
||||||
|
|
||||||
|
use orbclient::{Event, EventOption};
|
||||||
|
use scheme_utils::{Blocking, FpathWriter, HandleMap};
|
||||||
|
+use syscall::dirent::{DirEntry, DirentBuf, DirentKind};
|
||||||
|
use syscall::schemev2::NewFdFlags;
|
||||||
|
-use syscall::{Error as SysError, EventFlags, EACCES, EBADF, EEXIST, EINVAL};
|
||||||
|
+use syscall::{Error as SysError, EventFlags, EACCES, EBADF, EEXIST, EINVAL, ENOENT, ENOTDIR};
|
||||||
|
|
||||||
|
pub mod keymap;
|
||||||
|
|
||||||
|
@@ -35,8 +36,57 @@ use keymap::KeymapKind;
|
||||||
|
|
||||||
|
use crate::keymap::KeymapData;
|
||||||
|
|
||||||
|
+const DEVICE_ADD: u32 = 1;
|
||||||
|
+const DEVICE_REMOVE: u32 = 2;
|
||||||
|
+
|
||||||
|
+fn validate_producer_name(name: &str) -> Result<(), SysError> {
|
||||||
|
+ if name.is_empty() || name.contains('/') {
|
||||||
|
+ return Err(SysError::new(EINVAL));
|
||||||
|
+ }
|
||||||
|
+ if inputd::RESERVED_DEVICE_NAMES.contains(&name) {
|
||||||
|
+ return Err(SysError::new(EINVAL));
|
||||||
|
+ }
|
||||||
|
+ Ok(())
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+fn serialize_hotplug(kind: u32, device_id: u32, name: &str) -> Vec<u8> {
|
||||||
|
+ let name_bytes = name.as_bytes();
|
||||||
|
+ let header = HotplugHeader {
|
||||||
|
+ kind,
|
||||||
|
+ device_id,
|
||||||
|
+ name_len: name_bytes.len() as u32,
|
||||||
|
+ _reserved: 0,
|
||||||
|
+ };
|
||||||
|
+ let mut out = Vec::with_capacity(16 + name_bytes.len());
|
||||||
|
+ out.extend_from_slice(&header.to_bytes());
|
||||||
|
+ out.extend_from_slice(name_bytes);
|
||||||
|
+ out
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#[repr(C)]
|
||||||
|
+struct HotplugHeader {
|
||||||
|
+ kind: u32,
|
||||||
|
+ device_id: u32,
|
||||||
|
+ name_len: u32,
|
||||||
|
+ _reserved: u32,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl HotplugHeader {
|
||||||
|
+ fn to_bytes(&self) -> [u8; 16] {
|
||||||
|
+ let mut buf = [0u8; 16];
|
||||||
|
+ buf[0..4].copy_from_slice(&self.kind.to_ne_bytes());
|
||||||
|
+ buf[4..8].copy_from_slice(&self.device_id.to_ne_bytes());
|
||||||
|
+ buf[8..12].copy_from_slice(&self.name_len.to_ne_bytes());
|
||||||
|
+ buf[12..16].copy_from_slice(&self._reserved.to_ne_bytes());
|
||||||
|
+ buf
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
enum Handle {
|
||||||
|
Producer,
|
||||||
|
+ NamedProducer {
|
||||||
|
+ name: String,
|
||||||
|
+ },
|
||||||
|
Consumer {
|
||||||
|
events: EventFlags,
|
||||||
|
pending: Vec<u8>,
|
||||||
|
@@ -46,6 +96,17 @@ enum Handle {
|
||||||
|
notified: bool,
|
||||||
|
vt: usize,
|
||||||
|
},
|
||||||
|
+ DeviceConsumer {
|
||||||
|
+ device_name: String,
|
||||||
|
+ events: EventFlags,
|
||||||
|
+ pending: Vec<u8>,
|
||||||
|
+ notified: bool,
|
||||||
|
+ },
|
||||||
|
+ HotplugEvents {
|
||||||
|
+ events: EventFlags,
|
||||||
|
+ pending: Vec<u8>,
|
||||||
|
+ notified: bool,
|
||||||
|
+ },
|
||||||
|
Display {
|
||||||
|
events: EventFlags,
|
||||||
|
pending: Vec<VtEvent>,
|
||||||
|
@@ -72,6 +133,9 @@ struct InputScheme {
|
||||||
|
rshift: bool,
|
||||||
|
|
||||||
|
has_new_events: bool,
|
||||||
|
+
|
||||||
|
+ devices: BTreeMap<String, u32>,
|
||||||
|
+ next_device_id: AtomicUsize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputScheme {
|
||||||
|
@@ -90,9 +154,28 @@ impl InputScheme {
|
||||||
|
lshift: false,
|
||||||
|
rshift: false,
|
||||||
|
has_new_events: false,
|
||||||
|
+
|
||||||
|
+ devices: BTreeMap::new(),
|
||||||
|
+ next_device_id: AtomicUsize::new(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ fn emit_hotplug(&mut self, kind: u32, device_id: u32, name: &str) {
|
||||||
|
+ let record = serialize_hotplug(kind, device_id, name);
|
||||||
|
+ for handle in self.handles.values_mut() {
|
||||||
|
+ if let Handle::HotplugEvents {
|
||||||
|
+ pending,
|
||||||
|
+ notified,
|
||||||
|
+ ..
|
||||||
|
+ } = handle
|
||||||
|
+ {
|
||||||
|
+ pending.extend_from_slice(&record);
|
||||||
|
+ *notified = false;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ self.has_new_events = true;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
fn switch_vt(&mut self, new_active: usize) {
|
||||||
|
if let Some(active_vt) = self.active_vt {
|
||||||
|
if new_active == active_vt {
|
||||||
|
@@ -146,6 +229,43 @@ impl InputScheme {
|
||||||
|
|
||||||
|
self.active_keymap = KeymapData::new(new_active.into());
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ fn deliver_to_legacy_consumers(&mut self, buf: &[u8]) {
|
||||||
|
+ if let Some(active_vt) = self.active_vt {
|
||||||
|
+ for handle in self.handles.values_mut() {
|
||||||
|
+ if let Handle::Consumer {
|
||||||
|
+ pending,
|
||||||
|
+ notified,
|
||||||
|
+ vt,
|
||||||
|
+ ..
|
||||||
|
+ } = handle
|
||||||
|
+ {
|
||||||
|
+ if *vt != active_vt {
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ pending.extend_from_slice(buf);
|
||||||
|
+ *notified = false;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ fn deliver_to_device_consumers(&mut self, name: &str, buf: &[u8]) {
|
||||||
|
+ for handle in self.handles.values_mut() {
|
||||||
|
+ if let Handle::DeviceConsumer {
|
||||||
|
+ device_name,
|
||||||
|
+ pending,
|
||||||
|
+ notified,
|
||||||
|
+ ..
|
||||||
|
+ } = handle
|
||||||
|
+ {
|
||||||
|
+ if device_name == name {
|
||||||
|
+ pending.extend_from_slice(buf);
|
||||||
|
+ *notified = false;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SchemeSync for InputScheme {
|
||||||
|
@@ -170,7 +290,23 @@ impl SchemeSync for InputScheme {
|
||||||
|
let command = path_parts.next().ok_or(SysError::new(EINVAL))?;
|
||||||
|
|
||||||
|
let handle_ty = match command {
|
||||||
|
- "producer" => Handle::Producer,
|
||||||
|
+ "producer" => {
|
||||||
|
+ if let Some(name) = path_parts.next() {
|
||||||
|
+ validate_producer_name(name)?;
|
||||||
|
+ if self.devices.contains_key(name) {
|
||||||
|
+ return Err(SysError::new(EEXIST));
|
||||||
|
+ }
|
||||||
|
+ let device_id = self.next_device_id.fetch_add(1, Ordering::SeqCst) as u32;
|
||||||
|
+ self.devices.insert(name.to_owned(), device_id);
|
||||||
|
+ let handle = Handle::NamedProducer {
|
||||||
|
+ name: name.to_owned(),
|
||||||
|
+ };
|
||||||
|
+ self.emit_hotplug(DEVICE_ADD, device_id, name);
|
||||||
|
+ handle
|
||||||
|
+ } else {
|
||||||
|
+ Handle::Producer
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
"consumer" => {
|
||||||
|
let vt = self.next_vt_id.fetch_add(1, Ordering::Relaxed);
|
||||||
|
self.vts.insert(vt);
|
||||||
|
@@ -253,9 +389,23 @@ impl SchemeSync for InputScheme {
|
||||||
|
}
|
||||||
|
"control" => Handle::Control,
|
||||||
|
|
||||||
|
- _ => {
|
||||||
|
- log::error!("invalid path '{path}'");
|
||||||
|
- return Err(SysError::new(EINVAL));
|
||||||
|
+ "events" => Handle::HotplugEvents {
|
||||||
|
+ events: EventFlags::empty(),
|
||||||
|
+ pending: Vec::new(),
|
||||||
|
+ notified: false,
|
||||||
|
+ },
|
||||||
|
+
|
||||||
|
+ // dynamic device consumer: must be a currently registered device
|
||||||
|
+ name => {
|
||||||
|
+ if !self.devices.contains_key(name) {
|
||||||
|
+ return Err(SysError::new(ENOENT));
|
||||||
|
+ }
|
||||||
|
+ Handle::DeviceConsumer {
|
||||||
|
+ device_name: name.to_owned(),
|
||||||
|
+ events: EventFlags::empty(),
|
||||||
|
+ pending: Vec::new(),
|
||||||
|
+ notified: false,
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -274,7 +424,7 @@ impl SchemeSync for InputScheme {
|
||||||
|
let handle = self.handles.get(id)?;
|
||||||
|
|
||||||
|
if let Handle::Consumer { vt, .. } = handle {
|
||||||
|
- write!(w, "{vt}").unwrap();
|
||||||
|
+ write!(w, "{vt}").map_err(|_| SysError::new(EINVAL))?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(SysError::new(EINVAL))
|
||||||
|
@@ -282,6 +432,50 @@ impl SchemeSync for InputScheme {
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
+ fn getdents<'buf>(
|
||||||
|
+ &mut self,
|
||||||
|
+ id: usize,
|
||||||
|
+ mut buf: DirentBuf<&'buf mut [u8]>,
|
||||||
|
+ opaque_offset: u64,
|
||||||
|
+ ) -> syscall::Result<DirentBuf<&'buf mut [u8]>> {
|
||||||
|
+ let handle = self.handles.get(id)?;
|
||||||
|
+ if !matches!(handle, Handle::SchemeRoot) {
|
||||||
|
+ return Err(SysError::new(ENOTDIR));
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let static_entries: &[&str] = &[
|
||||||
|
+ "producer",
|
||||||
|
+ "consumer",
|
||||||
|
+ "consumer_bootlog",
|
||||||
|
+ "events",
|
||||||
|
+ "handle",
|
||||||
|
+ "handle_early",
|
||||||
|
+ "control",
|
||||||
|
+ ];
|
||||||
|
+
|
||||||
|
+ let device_names: Vec<&str> = self.devices.keys().map(|s| s.as_str()).collect();
|
||||||
|
+
|
||||||
|
+ let all_entries: Vec<(&str, DirentKind)> = static_entries
|
||||||
|
+ .iter()
|
||||||
|
+ .map(|&name| (name, DirentKind::Directory))
|
||||||
|
+ .chain(device_names.iter().map(|&name| (name, DirentKind::Unspecified)))
|
||||||
|
+ .collect();
|
||||||
|
+
|
||||||
|
+ for (idx, (name, kind)) in all_entries
|
||||||
|
+ .iter()
|
||||||
|
+ .enumerate()
|
||||||
|
+ .skip(opaque_offset as usize)
|
||||||
|
+ {
|
||||||
|
+ buf.entry(DirEntry {
|
||||||
|
+ inode: 0,
|
||||||
|
+ next_opaque_id: idx as u64 + 1,
|
||||||
|
+ name,
|
||||||
|
+ kind: *kind,
|
||||||
|
+ })?;
|
||||||
|
+ }
|
||||||
|
+ Ok(buf)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
fn read(
|
||||||
|
&mut self,
|
||||||
|
id: usize,
|
||||||
|
@@ -313,6 +507,22 @@ impl SchemeSync for InputScheme {
|
||||||
|
Ok(copy)
|
||||||
|
}
|
||||||
|
|
||||||
|
+ Handle::DeviceConsumer { pending, .. } => {
|
||||||
|
+ let copy = core::cmp::min(pending.len(), buf.len());
|
||||||
|
+ for (i, byte) in pending.drain(..copy).enumerate() {
|
||||||
|
+ buf[i] = byte;
|
||||||
|
+ }
|
||||||
|
+ Ok(copy)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ Handle::HotplugEvents { pending, .. } => {
|
||||||
|
+ let copy = core::cmp::min(pending.len(), buf.len());
|
||||||
|
+ for (i, byte) in pending.drain(..copy).enumerate() {
|
||||||
|
+ buf[i] = byte;
|
||||||
|
+ }
|
||||||
|
+ Ok(copy)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
Handle::Display { pending, .. } => {
|
||||||
|
if buf.len() % size_of::<VtEvent>() == 0 {
|
||||||
|
let copy = core::cmp::min(pending.len(), buf.len() / size_of::<VtEvent>());
|
||||||
|
@@ -334,6 +544,10 @@ impl SchemeSync for InputScheme {
|
||||||
|
log::error!("producer tried to read");
|
||||||
|
return Err(SysError::new(EINVAL));
|
||||||
|
}
|
||||||
|
+ Handle::NamedProducer { .. } => {
|
||||||
|
+ log::error!("named producer tried to read");
|
||||||
|
+ return Err(SysError::new(EINVAL));
|
||||||
|
+ }
|
||||||
|
Handle::Control => {
|
||||||
|
log::error!("control tried to read");
|
||||||
|
return Err(SysError::new(EINVAL));
|
||||||
|
@@ -379,11 +593,20 @@ impl SchemeSync for InputScheme {
|
||||||
|
log::error!("consumer tried to write");
|
||||||
|
return Err(SysError::new(EINVAL));
|
||||||
|
}
|
||||||
|
+ Handle::DeviceConsumer { .. } => {
|
||||||
|
+ log::error!("device consumer tried to write");
|
||||||
|
+ return Err(SysError::new(EINVAL));
|
||||||
|
+ }
|
||||||
|
+ Handle::HotplugEvents { .. } => {
|
||||||
|
+ log::error!("hotplug events tried to write");
|
||||||
|
+ return Err(SysError::new(EINVAL));
|
||||||
|
+ }
|
||||||
|
Handle::Display { .. } => {
|
||||||
|
log::error!("display tried to write");
|
||||||
|
return Err(SysError::new(EINVAL));
|
||||||
|
}
|
||||||
|
Handle::Producer => {}
|
||||||
|
+ Handle::NamedProducer { .. } => {}
|
||||||
|
Handle::SchemeRoot => return Err(SysError::new(EBADF)),
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -397,6 +620,11 @@ impl SchemeSync for InputScheme {
|
||||||
|
buf.len() / size_of::<Event>(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
+ let producer_name = match self.handles.get(id)? {
|
||||||
|
+ Handle::NamedProducer { ref name } => Some(name.clone()),
|
||||||
|
+ Handle::Producer => None,
|
||||||
|
+ _ => return Err(SysError::new(EBADF)),
|
||||||
|
+ };
|
||||||
|
|
||||||
|
for i in 0..events.len() {
|
||||||
|
let mut new_active_opt = None;
|
||||||
|
@@ -437,38 +665,21 @@ impl SchemeSync for InputScheme {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- let handle = self.handles.get_mut(id)?;
|
||||||
|
- assert!(matches!(handle, Handle::Producer));
|
||||||
|
-
|
||||||
|
- let buf = unsafe {
|
||||||
|
+ let serialized = unsafe {
|
||||||
|
core::slice::from_raw_parts(
|
||||||
|
(events.as_ptr()) as *const u8,
|
||||||
|
events.len() * size_of::<Event>(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
- if let Some(active_vt) = self.active_vt {
|
||||||
|
- for handle in self.handles.values_mut() {
|
||||||
|
- match handle {
|
||||||
|
- Handle::Consumer {
|
||||||
|
- pending,
|
||||||
|
- notified,
|
||||||
|
- vt,
|
||||||
|
- ..
|
||||||
|
- } => {
|
||||||
|
- if *vt != active_vt {
|
||||||
|
- continue;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- pending.extend_from_slice(buf);
|
||||||
|
- *notified = false;
|
||||||
|
- }
|
||||||
|
- _ => continue,
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
+ if let Some(ref name) = producer_name {
|
||||||
|
+ self.deliver_to_device_consumers(name, serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
- Ok(buf.len())
|
||||||
|
+ // named producers also feed the legacy path; legacy producers only feed legacy
|
||||||
|
+ self.deliver_to_legacy_consumers(serialized);
|
||||||
|
+
|
||||||
|
+ Ok(serialized.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fevent(
|
||||||
|
@@ -487,6 +698,24 @@ impl SchemeSync for InputScheme {
|
||||||
|
*notified = false;
|
||||||
|
Ok(EventFlags::empty())
|
||||||
|
}
|
||||||
|
+ Handle::DeviceConsumer {
|
||||||
|
+ ref mut events,
|
||||||
|
+ ref mut notified,
|
||||||
|
+ ..
|
||||||
|
+ } => {
|
||||||
|
+ *events = flags;
|
||||||
|
+ *notified = false;
|
||||||
|
+ Ok(EventFlags::empty())
|
||||||
|
+ }
|
||||||
|
+ Handle::HotplugEvents {
|
||||||
|
+ ref mut events,
|
||||||
|
+ ref mut notified,
|
||||||
|
+ ..
|
||||||
|
+ } => {
|
||||||
|
+ *events = flags;
|
||||||
|
+ *notified = false;
|
||||||
|
+ Ok(EventFlags::empty())
|
||||||
|
+ }
|
||||||
|
Handle::Display {
|
||||||
|
ref mut events,
|
||||||
|
ref mut notified,
|
||||||
|
@@ -496,7 +725,7 @@ impl SchemeSync for InputScheme {
|
||||||
|
*notified = false;
|
||||||
|
Ok(EventFlags::empty())
|
||||||
|
}
|
||||||
|
- Handle::Producer | Handle::Control => {
|
||||||
|
+ Handle::Producer | Handle::NamedProducer { .. } | Handle::Control => {
|
||||||
|
log::error!("producer or control tried to use an event queue");
|
||||||
|
Err(SysError::new(EINVAL))
|
||||||
|
}
|
||||||
|
@@ -505,8 +734,8 @@ impl SchemeSync for InputScheme {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_close(&mut self, id: usize) {
|
||||||
|
- match self.handles.remove(id).unwrap() {
|
||||||
|
- Handle::Consumer { vt, .. } => {
|
||||||
|
+ match self.handles.remove(id) {
|
||||||
|
+ Some(Handle::Consumer { vt, .. }) => {
|
||||||
|
self.vts.remove(&vt);
|
||||||
|
if self.active_vt == Some(vt) {
|
||||||
|
if let Some(&new_vt) = self.vts.last() {
|
||||||
|
@@ -516,7 +745,15 @@ impl SchemeSync for InputScheme {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- _ => {}
|
||||||
|
+ Some(Handle::NamedProducer { name, .. }) => {
|
||||||
|
+ if let Some(device_id) = self.devices.remove(&name) {
|
||||||
|
+ self.emit_hotplug(DEVICE_REMOVE, device_id, &name);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ Some(_) => {}
|
||||||
|
+ None => {
|
||||||
|
+ log::warn!("inputd: on_close called with unknown handle id {id}");
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -564,6 +801,39 @@ fn deamon(daemon: daemon::SchemeDaemon) -> anyhow::Result<()> {
|
||||||
|
|
||||||
|
*notified = true;
|
||||||
|
}
|
||||||
|
+ Handle::DeviceConsumer {
|
||||||
|
+ events,
|
||||||
|
+ pending,
|
||||||
|
+ ref mut notified,
|
||||||
|
+ ..
|
||||||
|
+ } => {
|
||||||
|
+ if pending.is_empty() || *notified || !events.contains(EventFlags::EVENT_READ) {
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ socket_file.write_response(
|
||||||
|
+ Response::post_fevent(*id, EventFlags::EVENT_READ.bits()),
|
||||||
|
+ SignalBehavior::Restart,
|
||||||
|
+ )?;
|
||||||
|
+
|
||||||
|
+ *notified = true;
|
||||||
|
+ }
|
||||||
|
+ Handle::HotplugEvents {
|
||||||
|
+ events,
|
||||||
|
+ pending,
|
||||||
|
+ ref mut notified,
|
||||||
|
+ } => {
|
||||||
|
+ if pending.is_empty() || *notified || !events.contains(EventFlags::EVENT_READ) {
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ socket_file.write_response(
|
||||||
|
+ Response::post_fevent(*id, EventFlags::EVENT_READ.bits()),
|
||||||
|
+ SignalBehavior::Restart,
|
||||||
|
+ )?;
|
||||||
|
+
|
||||||
|
+ *notified = true;
|
||||||
|
+ }
|
||||||
|
Handle::Display {
|
||||||
|
events,
|
||||||
|
pending,
|
||||||
|
@@ -589,8 +859,11 @@ fn deamon(daemon: daemon::SchemeDaemon) -> anyhow::Result<()> {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn daemon_runner(daemon: daemon::SchemeDaemon) -> ! {
|
||||||
|
- deamon(daemon).unwrap();
|
||||||
|
- unreachable!();
|
||||||
|
+ if let Err(err) = deamon(daemon) {
|
||||||
|
+ log::error!("inputd: scheme daemon failed: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
const HELP: &str = r#"
|
||||||
|
@@ -608,13 +881,26 @@ fn main() {
|
||||||
|
match val.as_ref() {
|
||||||
|
// Activates a VT.
|
||||||
|
"-A" => {
|
||||||
|
- let vt = args.next().unwrap().parse::<usize>().unwrap();
|
||||||
|
+ let vt_str = args.next().unwrap_or_else(|| {
|
||||||
|
+ eprintln!("inputd: -A requires a VT number argument");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ });
|
||||||
|
+ let vt = vt_str.parse::<usize>().unwrap_or_else(|_| {
|
||||||
|
+ eprintln!("inputd: invalid VT number: {vt_str}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
- let mut handle =
|
||||||
|
- inputd::ControlHandle::new().expect("inputd: failed to open control handle");
|
||||||
|
- handle
|
||||||
|
- .activate_vt(vt)
|
||||||
|
- .expect("inputd: failed to activate VT");
|
||||||
|
+ let mut handle = match inputd::ControlHandle::new() {
|
||||||
|
+ Ok(h) => h,
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("inputd: failed to open control handle: {e}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ if let Err(e) = handle.activate_vt(vt) {
|
||||||
|
+ eprintln!("inputd: failed to activate VT {vt}: {e}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
// Activates a keymap.
|
||||||
|
"-K" => {
|
||||||
|
@@ -630,11 +916,17 @@ fn main() {
|
||||||
|
std::process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
- let mut handle =
|
||||||
|
- inputd::ControlHandle::new().expect("inputd: failed to open control handle");
|
||||||
|
- handle
|
||||||
|
- .activate_keymap(vt as usize)
|
||||||
|
- .expect("inputd: failed to activate keymap");
|
||||||
|
+ let mut handle = match inputd::ControlHandle::new() {
|
||||||
|
+ Ok(h) => h,
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("inputd: failed to open control handle: {e}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ if let Err(e) = handle.activate_keymap(vt as usize) {
|
||||||
|
+ eprintln!("inputd: failed to activate keymap: {e}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
// List available keymaps
|
||||||
|
"--keymaps" => {
|
||||||
|
@@ -647,7 +939,10 @@ fn main() {
|
||||||
|
println!("{}", HELP);
|
||||||
|
}
|
||||||
|
|
||||||
|
- _ => panic!("inputd: invalid argument: {}", val),
|
||||||
|
+ _ => {
|
||||||
|
+ eprintln!("inputd: invalid argument: {val}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
common::setup_logging(
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
# P2-logd.patch
|
||||||
|
# Extract logd hardening: optional kernel debug/syslog handles, graceful error
|
||||||
|
# handling, and resilient request processing loop.
|
||||||
|
#
|
||||||
|
# Files: logd/src/main.rs, logd/src/scheme.rs
|
||||||
|
|
||||||
|
diff --git a/logd/src/main.rs b/logd/src/main.rs
|
||||||
|
index 3636f1fa..559d8993 100644
|
||||||
|
--- a/logd/src/main.rs
|
||||||
|
+++ b/logd/src/main.rs
|
||||||
|
@@ -6,18 +6,30 @@ use crate::scheme::LogScheme;
|
||||||
|
mod scheme;
|
||||||
|
|
||||||
|
fn daemon(daemon: daemon::SchemeDaemon) -> ! {
|
||||||
|
- let socket = Socket::create().expect("logd: failed to create log scheme");
|
||||||
|
+ let socket = match Socket::create() {
|
||||||
|
+ Ok(s) => s,
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("logd: failed to create log scheme: {e}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
|
||||||
|
let mut scheme = LogScheme::new(&socket);
|
||||||
|
let handler = Blocking::new(&socket, 16);
|
||||||
|
|
||||||
|
let _ = daemon.ready_sync_scheme(&socket, &mut scheme);
|
||||||
|
|
||||||
|
- libredox::call::setrens(0, 0).expect("logd: failed to enter null namespace");
|
||||||
|
-
|
||||||
|
- handler
|
||||||
|
- .process_requests_blocking(scheme)
|
||||||
|
- .expect("logd: failed to process requests");
|
||||||
|
+ if let Err(e) = libredox::call::setrens(0, 0) {
|
||||||
|
+ eprintln!("logd: failed to enter null namespace: {e}");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ match handler.process_requests_blocking(scheme) {
|
||||||
|
+ Ok(never) => match never {},
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("logd: failed to process requests: {e}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
diff --git a/logd/src/scheme.rs b/logd/src/scheme.rs
|
||||||
|
index 070de3d6..ef3e175c 100644
|
||||||
|
--- a/logd/src/scheme.rs
|
||||||
|
+++ b/logd/src/scheme.rs
|
||||||
|
@@ -22,7 +22,7 @@ pub enum LogHandle {
|
||||||
|
|
||||||
|
pub struct LogScheme<'sock> {
|
||||||
|
socket: &'sock Socket,
|
||||||
|
- kernel_debug: File,
|
||||||
|
+ kernel_debug: Option<File>,
|
||||||
|
output_tx: Sender<OutputCmd>,
|
||||||
|
handles: HandleMap<LogHandle>,
|
||||||
|
}
|
||||||
|
@@ -34,12 +34,24 @@ enum OutputCmd {
|
||||||
|
|
||||||
|
impl<'sock> LogScheme<'sock> {
|
||||||
|
pub fn new(socket: &'sock Socket) -> Self {
|
||||||
|
- let kernel_debug = OpenOptions::new()
|
||||||
|
+ let kernel_debug = match OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.open("/scheme/debug")
|
||||||
|
- .unwrap();
|
||||||
|
+ {
|
||||||
|
+ Ok(f) => Some(f),
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("logd: failed to open /scheme/debug: {e}");
|
||||||
|
+ None
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
|
||||||
|
- let mut kernel_sys_log = std::fs::File::open("/scheme/sys/log").unwrap();
|
||||||
|
+ let kernel_sys_log = match std::fs::File::open("/scheme/sys/log") {
|
||||||
|
+ Ok(f) => Some(f),
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("logd: failed to open /scheme/sys/log: {e}");
|
||||||
|
+ None
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
|
||||||
|
let (output_tx, output_rx) = mpsc::channel::<OutputCmd>();
|
||||||
|
|
||||||
|
@@ -72,20 +84,28 @@ impl<'sock> LogScheme<'sock> {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
- let output_tx2 = output_tx.clone();
|
||||||
|
- std::thread::spawn(move || {
|
||||||
|
- let mut handle_buf = vec![];
|
||||||
|
- let mut buf = [0; 4096];
|
||||||
|
- buf[.."kernel: ".len()].copy_from_slice(b"kernel: ");
|
||||||
|
- loop {
|
||||||
|
- let n = kernel_sys_log.read(&mut buf["kernel: ".len()..]).unwrap();
|
||||||
|
- if n == 0 {
|
||||||
|
- // FIXME currently possible as /scheme/log/kernel presents a snapshot of the log queue
|
||||||
|
- break;
|
||||||
|
+ if let Some(mut kernel_sys_log) = kernel_sys_log {
|
||||||
|
+ let output_tx2 = output_tx.clone();
|
||||||
|
+ std::thread::spawn(move || {
|
||||||
|
+ let mut handle_buf = vec![];
|
||||||
|
+ let mut buf = [0; 4096];
|
||||||
|
+ buf[.."kernel: ".len()].copy_from_slice(b"kernel: ");
|
||||||
|
+ loop {
|
||||||
|
+ let n = match kernel_sys_log.read(&mut buf["kernel: ".len()..]) {
|
||||||
|
+ Ok(n) => n,
|
||||||
|
+ Err(e) => {
|
||||||
|
+ eprintln!("logd: error reading kernel log: {e}");
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ if n == 0 {
|
||||||
|
+ // FIXME currently possible as /scheme/log/kernel presents a snapshot of the log queue
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ Self::write_logs(&output_tx2, &mut handle_buf, "kernel", &buf, None);
|
||||||
|
}
|
||||||
|
- Self::write_logs(&output_tx2, &mut handle_buf, "kernel", &buf, None);
|
||||||
|
- }
|
||||||
|
- });
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
|
|
||||||
|
LogScheme {
|
||||||
|
socket,
|
||||||
|
@@ -120,9 +140,9 @@ impl<'sock> LogScheme<'sock> {
|
||||||
|
let _ = kernel_debug.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
- output_tx
|
||||||
|
- .send(OutputCmd::Log(mem::take(handle_buf)))
|
||||||
|
- .unwrap();
|
||||||
|
+ if let Err(e) = output_tx.send(OutputCmd::Log(mem::take(handle_buf))) {
|
||||||
|
+ eprintln!("logd: failed to send log output: {e}");
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
@@ -196,7 +216,7 @@ impl<'sock> SchemeSync for LogScheme<'sock> {
|
||||||
|
handle_buf,
|
||||||
|
context,
|
||||||
|
buf,
|
||||||
|
- Some(&mut self.kernel_debug),
|
||||||
|
+ self.kernel_debug.as_mut(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(buf.len())
|
||||||
|
@@ -217,7 +237,10 @@ impl<'sock> SchemeSync for LogScheme<'sock> {
|
||||||
|
) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
- self.output_tx.send(OutputCmd::AddSink(new_fd)).unwrap();
|
||||||
|
+ if let Err(e) = self.output_tx.send(OutputCmd::AddSink(new_fd)) {
|
||||||
|
+ eprintln!("logd: failed to add log sink: {e}");
|
||||||
|
+ return Err(Error::new(EIO));
|
||||||
|
+ }
|
||||||
|
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,607 @@
|
|||||||
|
# P2-network-driver-mains.patch
|
||||||
|
# Extract network driver main.rs hardening: replace panic/unwrap/expect with
|
||||||
|
# proper error handling and graceful exits.
|
||||||
|
#
|
||||||
|
# Files: drivers/net/e1000d/src/main.rs, drivers/net/ixgbed/src/main.rs,
|
||||||
|
# drivers/net/rtl8139d/src/main.rs, drivers/net/rtl8168d/src/main.rs,
|
||||||
|
# drivers/net/virtio-netd/src/main.rs
|
||||||
|
|
||||||
|
diff --git a/drivers/net/e1000d/src/main.rs b/drivers/net/e1000d/src/main.rs
|
||||||
|
index 373ea9b3..8ff57b33 100644
|
||||||
|
--- a/drivers/net/e1000d/src/main.rs
|
||||||
|
+++ b/drivers/net/e1000d/src/main.rs
|
||||||
|
@@ -1,5 +1,6 @@
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
+use std::process;
|
||||||
|
|
||||||
|
use driver_network::NetworkScheme;
|
||||||
|
use event::{user_data, EventQueue};
|
||||||
|
@@ -25,10 +26,13 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
common::file_level(),
|
||||||
|
);
|
||||||
|
|
||||||
|
- let irq = pci_config
|
||||||
|
- .func
|
||||||
|
- .legacy_interrupt_line
|
||||||
|
- .expect("e1000d: no legacy interrupts supported");
|
||||||
|
+ let irq = match pci_config.func.legacy_interrupt_line {
|
||||||
|
+ Some(irq) => irq,
|
||||||
|
+ None => {
|
||||||
|
+ log::error!("e1000d: no legacy interrupts supported");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
|
||||||
|
log::info!("E1000 {}", pci_config.func.display());
|
||||||
|
|
||||||
|
@@ -38,7 +42,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
|
||||||
|
let mut scheme = NetworkScheme::new(
|
||||||
|
move || unsafe {
|
||||||
|
- device::Intel8254x::new(address).expect("e1000d: failed to allocate device")
|
||||||
|
+ device::Intel8254x::new(address).unwrap_or_else(|err| {
|
||||||
|
+ log::error!("e1000d: failed to allocate device: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ })
|
||||||
|
},
|
||||||
|
daemon,
|
||||||
|
format!("network.{name}"),
|
||||||
|
@@ -51,7 +58,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- let event_queue = EventQueue::<Source>::new().expect("e1000d: failed to create event queue");
|
||||||
|
+ let mut event_queue = EventQueue::<Source>::new().unwrap_or_else(|err| {
|
||||||
|
+ log::error!("e1000d: failed to create event queue: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
event_queue
|
||||||
|
.subscribe(
|
||||||
|
@@ -59,32 +69,65 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
Source::Irq,
|
||||||
|
event::EventFlags::READ,
|
||||||
|
)
|
||||||
|
- .expect("e1000d: failed to subscribe to IRQ fd");
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ log::error!("e1000d: failed to subscribe to IRQ fd: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
event_queue
|
||||||
|
.subscribe(
|
||||||
|
scheme.event_handle().raw(),
|
||||||
|
Source::Scheme,
|
||||||
|
event::EventFlags::READ,
|
||||||
|
)
|
||||||
|
- .expect("e1000d: failed to subscribe to scheme fd");
|
||||||
|
-
|
||||||
|
- libredox::call::setrens(0, 0).expect("e1000d: failed to enter null namespace");
|
||||||
|
-
|
||||||
|
- scheme.tick().unwrap();
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ log::error!("e1000d: failed to subscribe to scheme fd: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ libredox::call::setrens(0, 0).unwrap_or_else(|err| {
|
||||||
|
+ log::error!("e1000d: failed to enter null namespace: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ if let Err(err) = scheme.tick() {
|
||||||
|
+ log::error!("e1000d: failed initial scheme tick: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- for event in event_queue.map(|e| e.expect("e1000d: failed to get event")) {
|
||||||
|
+ loop {
|
||||||
|
+ let event = match event_queue.next() {
|
||||||
|
+ Some(Ok(event)) => event,
|
||||||
|
+ Some(Err(err)) => {
|
||||||
|
+ log::error!("e1000d: failed to get event: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ None => break,
|
||||||
|
+ };
|
||||||
|
match event.user_data {
|
||||||
|
Source::Irq => {
|
||||||
|
let mut irq = [0; 8];
|
||||||
|
- irq_file.read(&mut irq).unwrap();
|
||||||
|
+ if let Err(err) = irq_file.read(&mut irq) {
|
||||||
|
+ log::error!("e1000d: failed to read IRQ: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
if unsafe { scheme.adapter().irq() } {
|
||||||
|
- irq_file.write(&mut irq).unwrap();
|
||||||
|
-
|
||||||
|
- scheme.tick().expect("e1000d: failed to handle IRQ")
|
||||||
|
+ if let Err(err) = irq_file.write(&mut irq) {
|
||||||
|
+ log::error!("e1000d: failed to write IRQ: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if let Err(err) = scheme.tick() {
|
||||||
|
+ log::error!("e1000d: failed to handle IRQ: {err}");
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ Source::Scheme => {
|
||||||
|
+ if let Err(err) = scheme.tick() {
|
||||||
|
+ log::error!("e1000d: failed to handle scheme op: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- Source::Scheme => scheme.tick().expect("e1000d: failed to handle scheme op"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- unreachable!()
|
||||||
|
+
|
||||||
|
+ process::exit(0);
|
||||||
|
}
|
||||||
|
diff --git a/drivers/net/ixgbed/src/main.rs b/drivers/net/ixgbed/src/main.rs
|
||||||
|
index 4a6ce74d..855d339d 100644
|
||||||
|
--- a/drivers/net/ixgbed/src/main.rs
|
||||||
|
+++ b/drivers/net/ixgbed/src/main.rs
|
||||||
|
@@ -1,5 +1,6 @@
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
+use std::process;
|
||||||
|
|
||||||
|
use driver_network::NetworkScheme;
|
||||||
|
use event::{user_data, EventQueue};
|
||||||
|
@@ -19,12 +20,23 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
let mut name = pci_config.func.name();
|
||||||
|
name.push_str("_ixgbe");
|
||||||
|
|
||||||
|
- let irq = pci_config
|
||||||
|
- .func
|
||||||
|
- .legacy_interrupt_line
|
||||||
|
- .expect("ixgbed: no legacy interrupts supported");
|
||||||
|
+ common::setup_logging(
|
||||||
|
+ "net",
|
||||||
|
+ "pci",
|
||||||
|
+ &name,
|
||||||
|
+ common::output_level(),
|
||||||
|
+ common::file_level(),
|
||||||
|
+ );
|
||||||
|
+
|
||||||
|
+ let irq = match pci_config.func.legacy_interrupt_line {
|
||||||
|
+ Some(irq) => irq,
|
||||||
|
+ None => {
|
||||||
|
+ log::error!("ixgbed: no legacy interrupts supported");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
|
||||||
|
- println!(" + IXGBE {}", pci_config.func.display());
|
||||||
|
+ log::info!("IXGBE {}", pci_config.func.display());
|
||||||
|
|
||||||
|
let mut irq_file = irq.irq_handle("ixgbed");
|
||||||
|
|
||||||
|
@@ -34,8 +46,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
|
||||||
|
let mut scheme = NetworkScheme::new(
|
||||||
|
move || {
|
||||||
|
- device::Intel8259x::new(address as usize, size)
|
||||||
|
- .expect("ixgbed: failed to allocate device")
|
||||||
|
+ device::Intel8259x::new(address as usize, size).unwrap_or_else(|err| {
|
||||||
|
+ log::error!("ixgbed: failed to allocate device: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ })
|
||||||
|
},
|
||||||
|
daemon,
|
||||||
|
format!("network.{name}"),
|
||||||
|
@@ -48,41 +62,77 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- let event_queue = EventQueue::<Source>::new().expect("ixgbed: Could not create event queue.");
|
||||||
|
+ let mut event_queue = EventQueue::<Source>::new().unwrap_or_else(|err| {
|
||||||
|
+ log::error!("ixgbed: failed to create event queue: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
event_queue
|
||||||
|
.subscribe(
|
||||||
|
irq_file.as_raw_fd() as usize,
|
||||||
|
Source::Irq,
|
||||||
|
event::EventFlags::READ,
|
||||||
|
)
|
||||||
|
- .unwrap();
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ log::error!("ixgbed: failed to subscribe to IRQ fd: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
event_queue
|
||||||
|
.subscribe(
|
||||||
|
scheme.event_handle().raw(),
|
||||||
|
Source::Scheme,
|
||||||
|
event::EventFlags::READ,
|
||||||
|
)
|
||||||
|
- .unwrap();
|
||||||
|
-
|
||||||
|
- libredox::call::setrens(0, 0).expect("ixgbed: failed to enter null namespace");
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ log::error!("ixgbed: failed to subscribe to scheme fd: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ libredox::call::setrens(0, 0).unwrap_or_else(|err| {
|
||||||
|
+ log::error!("ixgbed: failed to enter null namespace: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ if let Err(err) = scheme.tick() {
|
||||||
|
+ log::error!("ixgbed: failed initial scheme tick: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- scheme.tick().unwrap();
|
||||||
|
+ loop {
|
||||||
|
+ let event = match event_queue.next() {
|
||||||
|
+ Some(Ok(event)) => event,
|
||||||
|
+ Some(Err(err)) => {
|
||||||
|
+ log::error!("ixgbed: failed to get event: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ None => break,
|
||||||
|
+ };
|
||||||
|
|
||||||
|
- for event in event_queue.map(|e| e.expect("ixgbed: failed to get next event")) {
|
||||||
|
match event.user_data {
|
||||||
|
Source::Irq => {
|
||||||
|
let mut irq = [0; 8];
|
||||||
|
- irq_file.read(&mut irq).unwrap();
|
||||||
|
+ if let Err(err) = irq_file.read(&mut irq) {
|
||||||
|
+ log::error!("ixgbed: failed to read IRQ: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
if scheme.adapter().irq() {
|
||||||
|
- irq_file.write(&mut irq).unwrap();
|
||||||
|
-
|
||||||
|
- scheme.tick().unwrap();
|
||||||
|
+ if let Err(err) = irq_file.write(&mut irq) {
|
||||||
|
+ log::error!("ixgbed: failed to write IRQ: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if let Err(err) = scheme.tick() {
|
||||||
|
+ log::error!("ixgbed: failed to handle IRQ: {err}");
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Source::Scheme => {
|
||||||
|
- scheme.tick().unwrap();
|
||||||
|
+ if let Err(err) = scheme.tick() {
|
||||||
|
+ log::error!("ixgbed: failed to handle scheme op: {err}");
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- unreachable!()
|
||||||
|
+
|
||||||
|
+ process::exit(0);
|
||||||
|
}
|
||||||
|
diff --git a/drivers/net/rtl8139d/src/main.rs b/drivers/net/rtl8139d/src/main.rs
|
||||||
|
index d470e814..64335a23 100644
|
||||||
|
--- a/drivers/net/rtl8139d/src/main.rs
|
||||||
|
+++ b/drivers/net/rtl8139d/src/main.rs
|
||||||
|
@@ -1,5 +1,6 @@
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
+use std::process;
|
||||||
|
|
||||||
|
use driver_network::NetworkScheme;
|
||||||
|
use event::{user_data, EventQueue};
|
||||||
|
@@ -32,7 +33,8 @@ fn map_bar(pcid_handle: &mut PciFunctionHandle) -> *mut u8 {
|
||||||
|
other => log::warn!("BAR {} is {:?} instead of memory BAR", barnum, other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- panic!("rtl8139d: failed to find BAR");
|
||||||
|
+ log::error!("rtl8139d: failed to find BAR");
|
||||||
|
+ process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
@@ -61,7 +63,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
|
||||||
|
let mut scheme = NetworkScheme::new(
|
||||||
|
move || unsafe {
|
||||||
|
- device::Rtl8139::new(bar as usize).expect("rtl8139d: failed to allocate device")
|
||||||
|
+ device::Rtl8139::new(bar as usize).unwrap_or_else(|err| {
|
||||||
|
+ log::error!("rtl8139d: failed to allocate device: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ })
|
||||||
|
},
|
||||||
|
daemon,
|
||||||
|
format!("network.{name}"),
|
||||||
|
@@ -74,42 +79,76 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- let event_queue = EventQueue::<Source>::new().expect("rtl8139d: Could not create event queue.");
|
||||||
|
+ let mut event_queue = EventQueue::<Source>::new().unwrap_or_else(|err| {
|
||||||
|
+ log::error!("rtl8139d: failed to create event queue: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
event_queue
|
||||||
|
.subscribe(
|
||||||
|
irq_file.irq_handle().as_raw_fd() as usize,
|
||||||
|
Source::Irq,
|
||||||
|
event::EventFlags::READ,
|
||||||
|
)
|
||||||
|
- .unwrap();
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ log::error!("rtl8139d: failed to subscribe to IRQ fd: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
event_queue
|
||||||
|
.subscribe(
|
||||||
|
scheme.event_handle().raw(),
|
||||||
|
Source::Scheme,
|
||||||
|
event::EventFlags::READ,
|
||||||
|
)
|
||||||
|
- .unwrap();
|
||||||
|
-
|
||||||
|
- libredox::call::setrens(0, 0).expect("rtl8139d: failed to enter null namespace");
|
||||||
|
-
|
||||||
|
- scheme.tick().unwrap();
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ log::error!("rtl8139d: failed to subscribe to scheme fd: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ libredox::call::setrens(0, 0).unwrap_or_else(|err| {
|
||||||
|
+ log::error!("rtl8139d: failed to enter null namespace: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ if let Err(err) = scheme.tick() {
|
||||||
|
+ log::error!("rtl8139d: failed initial scheme tick: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- for event in event_queue.map(|e| e.expect("rtl8139d: failed to get next event")) {
|
||||||
|
+ loop {
|
||||||
|
+ let event = match event_queue.next() {
|
||||||
|
+ Some(Ok(event)) => event,
|
||||||
|
+ Some(Err(err)) => {
|
||||||
|
+ log::error!("rtl8139d: failed to get next event: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ None => break,
|
||||||
|
+ };
|
||||||
|
match event.user_data {
|
||||||
|
Source::Irq => {
|
||||||
|
let mut irq = [0; 8];
|
||||||
|
- irq_file.irq_handle().read(&mut irq).unwrap();
|
||||||
|
+ if let Err(err) = irq_file.irq_handle().read(&mut irq) {
|
||||||
|
+ log::error!("rtl8139d: failed to read IRQ: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
//TODO: This may be causing spurious interrupts
|
||||||
|
if unsafe { scheme.adapter_mut().irq() } {
|
||||||
|
- irq_file.irq_handle().write(&mut irq).unwrap();
|
||||||
|
-
|
||||||
|
- scheme.tick().unwrap();
|
||||||
|
+ if let Err(err) = irq_file.irq_handle().write(&mut irq) {
|
||||||
|
+ log::error!("rtl8139d: failed to write IRQ: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if let Err(err) = scheme.tick() {
|
||||||
|
+ log::error!("rtl8139d: failed to handle IRQ tick: {err}");
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Source::Scheme => {
|
||||||
|
- scheme.tick().unwrap();
|
||||||
|
+ if let Err(err) = scheme.tick() {
|
||||||
|
+ log::error!("rtl8139d: failed to handle scheme op: {err}");
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- unreachable!()
|
||||||
|
+
|
||||||
|
+ process::exit(0);
|
||||||
|
}
|
||||||
|
diff --git a/drivers/net/rtl8168d/src/main.rs b/drivers/net/rtl8168d/src/main.rs
|
||||||
|
index 1d9963a3..bd2fcb1a 100644
|
||||||
|
--- a/drivers/net/rtl8168d/src/main.rs
|
||||||
|
+++ b/drivers/net/rtl8168d/src/main.rs
|
||||||
|
@@ -1,5 +1,6 @@
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
+use std::process;
|
||||||
|
|
||||||
|
use driver_network::NetworkScheme;
|
||||||
|
use event::{user_data, EventQueue};
|
||||||
|
@@ -32,7 +33,8 @@ fn map_bar(pcid_handle: &mut PciFunctionHandle) -> *mut u8 {
|
||||||
|
other => log::warn!("BAR {} is {:?} instead of memory BAR", barnum, other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- panic!("rtl8168d: failed to find BAR");
|
||||||
|
+ log::error!("rtl8168d: failed to find BAR");
|
||||||
|
+ process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
@@ -61,7 +63,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
|
||||||
|
let mut scheme = NetworkScheme::new(
|
||||||
|
move || unsafe {
|
||||||
|
- device::Rtl8168::new(bar as usize).expect("rtl8168d: failed to allocate device")
|
||||||
|
+ device::Rtl8168::new(bar as usize).unwrap_or_else(|err| {
|
||||||
|
+ log::error!("rtl8168d: failed to allocate device: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ })
|
||||||
|
},
|
||||||
|
daemon,
|
||||||
|
format!("network.{name}"),
|
||||||
|
@@ -74,42 +79,76 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- let event_queue = EventQueue::<Source>::new().expect("rtl8168d: Could not create event queue.");
|
||||||
|
+ let mut event_queue = EventQueue::<Source>::new().unwrap_or_else(|err| {
|
||||||
|
+ log::error!("rtl8168d: failed to create event queue: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
event_queue
|
||||||
|
.subscribe(
|
||||||
|
irq_file.irq_handle().as_raw_fd() as usize,
|
||||||
|
Source::Irq,
|
||||||
|
event::EventFlags::READ,
|
||||||
|
)
|
||||||
|
- .unwrap();
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ log::error!("rtl8168d: failed to subscribe to IRQ fd: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
event_queue
|
||||||
|
.subscribe(
|
||||||
|
scheme.event_handle().raw(),
|
||||||
|
Source::Scheme,
|
||||||
|
event::EventFlags::READ,
|
||||||
|
)
|
||||||
|
- .unwrap();
|
||||||
|
-
|
||||||
|
- libredox::call::setrens(0, 0).expect("rtl8168d: failed to enter null namespace");
|
||||||
|
-
|
||||||
|
- scheme.tick().unwrap();
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ log::error!("rtl8168d: failed to subscribe to scheme fd: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ libredox::call::setrens(0, 0).unwrap_or_else(|err| {
|
||||||
|
+ log::error!("rtl8168d: failed to enter null namespace: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ if let Err(err) = scheme.tick() {
|
||||||
|
+ log::error!("rtl8168d: failed initial scheme tick: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- for event in event_queue.map(|e| e.expect("rtl8168d: failed to get next event")) {
|
||||||
|
+ loop {
|
||||||
|
+ let event = match event_queue.next() {
|
||||||
|
+ Some(Ok(event)) => event,
|
||||||
|
+ Some(Err(err)) => {
|
||||||
|
+ log::error!("rtl8168d: failed to get next event: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ None => break,
|
||||||
|
+ };
|
||||||
|
match event.user_data {
|
||||||
|
Source::Irq => {
|
||||||
|
let mut irq = [0; 8];
|
||||||
|
- irq_file.irq_handle().read(&mut irq).unwrap();
|
||||||
|
+ if let Err(err) = irq_file.irq_handle().read(&mut irq) {
|
||||||
|
+ log::error!("rtl8168d: failed to read IRQ: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
//TODO: This may be causing spurious interrupts
|
||||||
|
if unsafe { scheme.adapter_mut().irq() } {
|
||||||
|
- irq_file.irq_handle().write(&mut irq).unwrap();
|
||||||
|
-
|
||||||
|
- scheme.tick().unwrap();
|
||||||
|
+ if let Err(err) = irq_file.irq_handle().write(&mut irq) {
|
||||||
|
+ log::error!("rtl8168d: failed to write IRQ: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if let Err(err) = scheme.tick() {
|
||||||
|
+ log::error!("rtl8168d: failed to handle IRQ tick: {err}");
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Source::Scheme => {
|
||||||
|
- scheme.tick().unwrap();
|
||||||
|
+ if let Err(err) = scheme.tick() {
|
||||||
|
+ log::error!("rtl8168d: failed to handle scheme op: {err}");
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- unreachable!()
|
||||||
|
+
|
||||||
|
+ process::exit(0);
|
||||||
|
}
|
||||||
|
diff --git a/drivers/net/virtio-netd/src/main.rs b/drivers/net/virtio-netd/src/main.rs
|
||||||
|
index 17d168ef..adbd1086 100644
|
||||||
|
--- a/drivers/net/virtio-netd/src/main.rs
|
||||||
|
+++ b/drivers/net/virtio-netd/src/main.rs
|
||||||
|
@@ -3,6 +3,7 @@ mod scheme;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::mem;
|
||||||
|
+use std::process;
|
||||||
|
|
||||||
|
use driver_network::NetworkScheme;
|
||||||
|
use pcid_interface::PciFunctionHandle;
|
||||||
|
@@ -31,8 +32,11 @@ fn main() {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn daemon_runner(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
- deamon(daemon, pcid_handle).unwrap();
|
||||||
|
- unreachable!();
|
||||||
|
+ deamon(daemon, pcid_handle).unwrap_or_else(|err| {
|
||||||
|
+ log::error!("virtio-netd: daemon failed: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
+ process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deamon(
|
||||||
|
@@ -52,7 +56,10 @@ fn deamon(
|
||||||
|
// 0x1000 - virtio-net
|
||||||
|
let pci_config = pcid_handle.config();
|
||||||
|
|
||||||
|
- assert_eq!(pci_config.func.full_device_id.device_id, 0x1000);
|
||||||
|
+ if pci_config.func.full_device_id.device_id != 0x1000 {
|
||||||
|
+ log::error!("virtio-netd: unexpected device ID {:#06x}, expected 0x1000", pci_config.func.full_device_id.device_id);
|
||||||
|
+ process::exit(1);
|
||||||
|
+ }
|
||||||
|
log::info!("virtio-net: initiating startup sequence :^)");
|
||||||
|
|
||||||
|
let device = virtio_core::probe_device(&mut pcid_handle)?;
|
||||||
|
@@ -84,7 +91,8 @@ fn deamon(
|
||||||
|
device.transport.ack_driver_feature(VIRTIO_NET_F_MAC);
|
||||||
|
mac
|
||||||
|
} else {
|
||||||
|
- unimplemented!()
|
||||||
|
+ log::error!("virtio-netd: device does not support MAC feature");
|
||||||
|
+ return Err("virtio-netd: VIRTIO_NET_F_MAC not supported".into());
|
||||||
|
};
|
||||||
|
|
||||||
|
device.transport.finalize_features();
|
||||||
|
@@ -126,12 +134,23 @@ fn deamon(
|
||||||
|
data: 0,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
- libredox::call::setrens(0, 0).expect("virtio-netd: failed to enter null namespace");
|
||||||
|
+ libredox::call::setrens(0, 0).unwrap_or_else(|err| {
|
||||||
|
+ log::error!("virtio-netd: failed to enter null namespace: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
- scheme.tick()?;
|
||||||
|
+ if let Err(err) = scheme.tick() {
|
||||||
|
+ log::error!("virtio-netd: failed initial scheme tick: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
loop {
|
||||||
|
- event_queue.read(&mut [0; mem::size_of::<syscall::Event>()])?; // Wait for event
|
||||||
|
- scheme.tick()?;
|
||||||
|
+ if let Err(err) = event_queue.read(&mut [0; mem::size_of::<syscall::Event>()]) {
|
||||||
|
+ log::error!("virtio-netd: failed to read event: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ if let Err(err) = scheme.tick() {
|
||||||
|
+ log::error!("virtio-netd: failed to handle scheme event: {err}");
|
||||||
|
+ }
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,625 @@
|
|||||||
|
# P2-storage-driver-mains.patch
|
||||||
|
# Extract storage driver main.rs hardening: replace panic/unwrap/expect with
|
||||||
|
# proper error handling, debug_assert for invariant checks, and graceful exits.
|
||||||
|
#
|
||||||
|
# Files: drivers/storage/ahcid/src/main.rs, drivers/storage/ided/src/main.rs,
|
||||||
|
# drivers/storage/nvmed/src/main.rs, drivers/storage/virtio-blkd/src/main.rs
|
||||||
|
|
||||||
|
diff --git a/drivers/storage/ahcid/src/main.rs b/drivers/storage/ahcid/src/main.rs
|
||||||
|
index 1f130a29..cccd2980 100644
|
||||||
|
--- a/drivers/storage/ahcid/src/main.rs
|
||||||
|
+++ b/drivers/storage/ahcid/src/main.rs
|
||||||
|
@@ -2,6 +2,7 @@
|
||||||
|
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::os::fd::AsRawFd;
|
||||||
|
+use std::process;
|
||||||
|
use std::usize;
|
||||||
|
|
||||||
|
use common::io::Io;
|
||||||
|
@@ -23,10 +24,13 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
let mut name = pci_config.func.name();
|
||||||
|
name.push_str("_ahci");
|
||||||
|
|
||||||
|
- let irq = pci_config
|
||||||
|
- .func
|
||||||
|
- .legacy_interrupt_line
|
||||||
|
- .expect("ahcid: no legacy interrupts supported");
|
||||||
|
+ let irq = match pci_config.func.legacy_interrupt_line {
|
||||||
|
+ Some(irq) => irq,
|
||||||
|
+ None => {
|
||||||
|
+ error!("ahcid: no legacy interrupts supported");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
|
||||||
|
common::setup_logging(
|
||||||
|
"disk",
|
||||||
|
@@ -57,46 +61,71 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
let mut irq_file = irq.irq_handle("ahcid");
|
||||||
|
let irq_fd = irq_file.as_raw_fd() as usize;
|
||||||
|
|
||||||
|
- let event_queue = RawEventQueue::new().expect("ahcid: failed to create event queue");
|
||||||
|
+ let event_queue = RawEventQueue::new().unwrap_or_else(|err| {
|
||||||
|
+ error!("ahcid: failed to create event queue: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
- libredox::call::setrens(0, 0).expect("ahcid: failed to enter null namespace");
|
||||||
|
+ libredox::call::setrens(0, 0).unwrap_or_else(|err| {
|
||||||
|
+ error!("ahcid: failed to enter null namespace: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
event_queue
|
||||||
|
.subscribe(scheme.event_handle().raw(), 1, EventFlags::READ)
|
||||||
|
- .expect("ahcid: failed to event scheme socket");
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ error!("ahcid: failed to event scheme socket: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
event_queue
|
||||||
|
.subscribe(irq_fd, 1, EventFlags::READ)
|
||||||
|
- .expect("ahcid: failed to event irq scheme");
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ error!("ahcid: failed to event irq scheme: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
for event in event_queue {
|
||||||
|
- let event = event.unwrap();
|
||||||
|
+ let event = match event {
|
||||||
|
+ Ok(event) => event,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ error!("ahcid: failed to get event: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
if event.fd == scheme.event_handle().raw() {
|
||||||
|
- FuturesExecutor.block_on(scheme.tick()).unwrap();
|
||||||
|
+ if let Err(err) = FuturesExecutor.block_on(scheme.tick()) {
|
||||||
|
+ error!("ahcid: failed to handle scheme event: {err}");
|
||||||
|
+ }
|
||||||
|
} else if event.fd == irq_fd {
|
||||||
|
let mut irq = [0; 8];
|
||||||
|
- if irq_file
|
||||||
|
- .read(&mut irq)
|
||||||
|
- .expect("ahcid: failed to read irq file")
|
||||||
|
- >= irq.len()
|
||||||
|
- {
|
||||||
|
- let is = hba_mem.is.read();
|
||||||
|
- if is > 0 {
|
||||||
|
- let pi = hba_mem.pi.read();
|
||||||
|
- let pi_is = pi & is;
|
||||||
|
- for i in 0..hba_mem.ports.len() {
|
||||||
|
- if pi_is & 1 << i > 0 {
|
||||||
|
- let port = &mut hba_mem.ports[i];
|
||||||
|
- let is = port.is.read();
|
||||||
|
- port.is.write(is);
|
||||||
|
- }
|
||||||
|
+ match irq_file.read(&mut irq) {
|
||||||
|
+ Ok(count) if count >= irq.len() => {}
|
||||||
|
+ Ok(_) => continue,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ error!("ahcid: failed to read irq file: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ let is = hba_mem.is.read();
|
||||||
|
+ if is > 0 {
|
||||||
|
+ let pi = hba_mem.pi.read();
|
||||||
|
+ let pi_is = pi & is;
|
||||||
|
+ for i in 0..hba_mem.ports.len() {
|
||||||
|
+ if pi_is & 1 << i > 0 {
|
||||||
|
+ let port = &mut hba_mem.ports[i];
|
||||||
|
+ let is = port.is.read();
|
||||||
|
+ port.is.write(is);
|
||||||
|
}
|
||||||
|
- hba_mem.is.write(is);
|
||||||
|
+ }
|
||||||
|
+ hba_mem.is.write(is);
|
||||||
|
|
||||||
|
- irq_file
|
||||||
|
- .write(&irq)
|
||||||
|
- .expect("ahcid: failed to write irq file");
|
||||||
|
+ if let Err(err) = irq_file.write(&irq) {
|
||||||
|
+ error!("ahcid: failed to write irq file: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- FuturesExecutor.block_on(scheme.tick()).unwrap();
|
||||||
|
+ if let Err(err) = FuturesExecutor.block_on(scheme.tick()) {
|
||||||
|
+ error!("ahcid: failed to handle IRQ tick: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
@@ -105,5 +134,5 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- std::process::exit(0);
|
||||||
|
+ process::exit(0);
|
||||||
|
}
|
||||||
|
diff --git a/drivers/storage/ided/src/main.rs b/drivers/storage/ided/src/main.rs
|
||||||
|
index 4197217d..03174554 100644
|
||||||
|
--- a/drivers/storage/ided/src/main.rs
|
||||||
|
+++ b/drivers/storage/ided/src/main.rs
|
||||||
|
@@ -8,6 +8,7 @@ use std::{
|
||||||
|
fs::File,
|
||||||
|
io::{Read, Write},
|
||||||
|
os::unix::io::{FromRawFd, RawFd},
|
||||||
|
+ process,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
thread::{self, sleep},
|
||||||
|
time::Duration,
|
||||||
|
@@ -45,17 +46,34 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
|
||||||
|
let busmaster_base = pci_config.func.bars[4].expect_port();
|
||||||
|
let (primary, primary_irq) = if pci_config.func.full_device_id.interface & 1 != 0 {
|
||||||
|
- panic!("TODO: IDE primary channel is PCI native");
|
||||||
|
+ error!("ided: IDE primary channel PCI native mode not supported");
|
||||||
|
+ process::exit(1);
|
||||||
|
} else {
|
||||||
|
- (Channel::primary_compat(busmaster_base).unwrap(), 14)
|
||||||
|
+ (
|
||||||
|
+ Channel::primary_compat(busmaster_base).unwrap_or_else(|err| {
|
||||||
|
+ error!("ided: failed to init primary channel: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ }),
|
||||||
|
+ 14,
|
||||||
|
+ )
|
||||||
|
};
|
||||||
|
let (secondary, secondary_irq) = if pci_config.func.full_device_id.interface & 1 != 0 {
|
||||||
|
- panic!("TODO: IDE secondary channel is PCI native");
|
||||||
|
+ error!("ided: IDE secondary channel PCI native mode not supported");
|
||||||
|
+ process::exit(1);
|
||||||
|
} else {
|
||||||
|
- (Channel::secondary_compat(busmaster_base + 8).unwrap(), 15)
|
||||||
|
+ (
|
||||||
|
+ Channel::secondary_compat(busmaster_base + 8).unwrap_or_else(|err| {
|
||||||
|
+ error!("ided: failed to init secondary channel: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ }),
|
||||||
|
+ 15,
|
||||||
|
+ )
|
||||||
|
};
|
||||||
|
|
||||||
|
- common::acquire_port_io_rights().expect("ided: failed to get I/O privilege");
|
||||||
|
+ common::acquire_port_io_rights().unwrap_or_else(|err| {
|
||||||
|
+ error!("ided: failed to get I/O privilege: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
//TODO: move this to ide.rs?
|
||||||
|
let chans = vec![
|
||||||
|
@@ -87,13 +105,13 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
for (chan_i, chan_lock) in chans.iter().enumerate() {
|
||||||
|
let mut chan = chan_lock.lock().unwrap();
|
||||||
|
|
||||||
|
- println!(" - channel {}", chan_i);
|
||||||
|
+ log::info!(" - channel {}", chan_i);
|
||||||
|
|
||||||
|
// Disable IRQs
|
||||||
|
chan.control.write(2);
|
||||||
|
|
||||||
|
for dev in 0..=1 {
|
||||||
|
- println!(" - device {}", dev);
|
||||||
|
+ log::info!(" - device {}", dev);
|
||||||
|
|
||||||
|
// Select device
|
||||||
|
chan.device_select.write(0xA0 | (dev << 4));
|
||||||
|
@@ -105,7 +123,7 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
|
||||||
|
// Check if device exists
|
||||||
|
if chan.status.read() == 0 {
|
||||||
|
- println!(" not found");
|
||||||
|
+ log::info!(" not found");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -125,7 +143,7 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
|
||||||
|
//TODO: probe ATAPI
|
||||||
|
if error {
|
||||||
|
- println!(" error");
|
||||||
|
+ log::info!(" error");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -189,12 +207,12 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
48
|
||||||
|
};
|
||||||
|
|
||||||
|
- println!(" Serial: {}", serial.trim());
|
||||||
|
- println!(" Firmware: {}", firmware.trim());
|
||||||
|
- println!(" Model: {}", model.trim());
|
||||||
|
- println!(" Size: {} MB", sectors / 2048);
|
||||||
|
- println!(" DMA: {}", dma);
|
||||||
|
- println!(" {}-bit LBA", lba_bits);
|
||||||
|
+ log::info!(" Serial: {}", serial.trim());
|
||||||
|
+ log::info!(" Firmware: {}", firmware.trim());
|
||||||
|
+ log::info!(" Model: {}", model.trim());
|
||||||
|
+ log::info!(" Size: {} MB", sectors / 2048);
|
||||||
|
+ log::info!(" DMA: {}", dma);
|
||||||
|
+ log::info!(" {}-bit LBA", lba_bits);
|
||||||
|
|
||||||
|
disks.push(AnyDisk::Ata(AtaDisk {
|
||||||
|
chan: chan_lock.clone(),
|
||||||
|
@@ -227,7 +245,10 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
flag::O_RDWR | flag::O_NONBLOCK,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
- .expect("ided: failed to open irq file");
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ error!("ided: failed to open primary irq file: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
let mut primary_irq_file = unsafe { File::from_raw_fd(primary_irq_fd as RawFd) };
|
||||||
|
|
||||||
|
let secondary_irq_fd = libredox::call::open(
|
||||||
|
@@ -235,70 +256,107 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
flag::O_RDWR | flag::O_NONBLOCK,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
- .expect("ided: failed to open irq file");
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ error!("ided: failed to open secondary irq file: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
let mut secondary_irq_file = unsafe { File::from_raw_fd(secondary_irq_fd as RawFd) };
|
||||||
|
|
||||||
|
- let event_queue = RawEventQueue::new().expect("ided: failed to open event file");
|
||||||
|
+ let event_queue = RawEventQueue::new().unwrap_or_else(|err| {
|
||||||
|
+ error!("ided: failed to open event file: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
- libredox::call::setrens(0, 0).expect("ided: failed to enter null namespace");
|
||||||
|
+ libredox::call::setrens(0, 0).unwrap_or_else(|err| {
|
||||||
|
+ error!("ided: failed to enter null namespace: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
event_queue
|
||||||
|
.subscribe(scheme.event_handle().raw(), 0, EventFlags::READ)
|
||||||
|
- .expect("ided: failed to event disk scheme");
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ error!("ided: failed to event disk scheme: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
event_queue
|
||||||
|
.subscribe(primary_irq_fd, 0, EventFlags::READ)
|
||||||
|
- .expect("ided: failed to event irq scheme");
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ error!("ided: failed to event primary irq: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
event_queue
|
||||||
|
.subscribe(secondary_irq_fd, 0, EventFlags::READ)
|
||||||
|
- .expect("ided: failed to event irq scheme");
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ error!("ided: failed to event secondary irq: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
for event in event_queue {
|
||||||
|
- let event = event.unwrap();
|
||||||
|
+ let event = match event {
|
||||||
|
+ Ok(event) => event,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ error!("ided: failed to get event: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
if event.fd == scheme.event_handle().raw() {
|
||||||
|
- FuturesExecutor.block_on(scheme.tick()).unwrap();
|
||||||
|
+ if let Err(err) = FuturesExecutor.block_on(scheme.tick()) {
|
||||||
|
+ error!("ided: failed to handle scheme event: {err}");
|
||||||
|
+ }
|
||||||
|
} else if event.fd == primary_irq_fd {
|
||||||
|
let mut irq = [0; 8];
|
||||||
|
- if primary_irq_file
|
||||||
|
- .read(&mut irq)
|
||||||
|
- .expect("ided: failed to read irq file")
|
||||||
|
- >= irq.len()
|
||||||
|
- {
|
||||||
|
- let _chan = chans[0].lock().unwrap();
|
||||||
|
- //TODO: check chan for irq
|
||||||
|
+ match primary_irq_file.read(&mut irq) {
|
||||||
|
+ Ok(count) if count >= irq.len() => {}
|
||||||
|
+ Ok(_) => continue,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ error!("ided: failed to read primary irq file: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ let _chan = chans[0].lock().unwrap();
|
||||||
|
+ //TODO: check chan for irq
|
||||||
|
|
||||||
|
- primary_irq_file
|
||||||
|
- .write(&irq)
|
||||||
|
- .expect("ided: failed to write irq file");
|
||||||
|
+ if let Err(err) = primary_irq_file.write(&irq) {
|
||||||
|
+ error!("ided: failed to write primary irq file: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- FuturesExecutor.block_on(scheme.tick()).unwrap();
|
||||||
|
+ if let Err(err) = FuturesExecutor.block_on(scheme.tick()) {
|
||||||
|
+ error!("ided: failed to handle primary IRQ tick: {err}");
|
||||||
|
}
|
||||||
|
} else if event.fd == secondary_irq_fd {
|
||||||
|
let mut irq = [0; 8];
|
||||||
|
- if secondary_irq_file
|
||||||
|
- .read(&mut irq)
|
||||||
|
- .expect("ided: failed to read irq file")
|
||||||
|
- >= irq.len()
|
||||||
|
- {
|
||||||
|
- let _chan = chans[1].lock().unwrap();
|
||||||
|
- //TODO: check chan for irq
|
||||||
|
+ match secondary_irq_file.read(&mut irq) {
|
||||||
|
+ Ok(count) if count >= irq.len() => {}
|
||||||
|
+ Ok(_) => continue,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ error!("ided: failed to read secondary irq file: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ let _chan = chans[1].lock().unwrap();
|
||||||
|
+ //TODO: check chan for irq
|
||||||
|
|
||||||
|
- secondary_irq_file
|
||||||
|
- .write(&irq)
|
||||||
|
- .expect("ided: failed to write irq file");
|
||||||
|
+ if let Err(err) = secondary_irq_file.write(&irq) {
|
||||||
|
+ error!("ided: failed to write secondary irq file: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- FuturesExecutor.block_on(scheme.tick()).unwrap();
|
||||||
|
+ if let Err(err) = FuturesExecutor.block_on(scheme.tick()) {
|
||||||
|
+ error!("ided: failed to handle secondary IRQ tick: {err}");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("Unknown event {}", event.fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- std::process::exit(0);
|
||||||
|
+ process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||||
|
fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
- unimplemented!()
|
||||||
|
+ log::error!("ided: unsupported architecture");
|
||||||
|
+ process::exit(1);
|
||||||
|
}
|
||||||
|
diff --git a/drivers/storage/nvmed/src/main.rs b/drivers/storage/nvmed/src/main.rs
|
||||||
|
index beb1b689..3772f4e5 100644
|
||||||
|
--- a/drivers/storage/nvmed/src/main.rs
|
||||||
|
+++ b/drivers/storage/nvmed/src/main.rs
|
||||||
|
@@ -2,6 +2,7 @@ use std::cell::RefCell;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::os::fd::AsRawFd;
|
||||||
|
+use std::process;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::usize;
|
||||||
|
@@ -22,7 +23,10 @@ struct NvmeDisk {
|
||||||
|
|
||||||
|
impl Disk for NvmeDisk {
|
||||||
|
fn block_size(&self) -> u32 {
|
||||||
|
- self.ns.block_size.try_into().unwrap()
|
||||||
|
+ self.ns.block_size.try_into().unwrap_or_else(|_| {
|
||||||
|
+ log::error!("nvmed: block size {} does not fit in u32", self.ns.block_size);
|
||||||
|
+ process::exit(1);
|
||||||
|
+ })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> u64 {
|
||||||
|
@@ -79,26 +83,43 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
|
||||||
|
let interrupt_vector = irq_helpers::pci_allocate_interrupt_vector(&mut pcid_handle, "nvmed");
|
||||||
|
let iv = interrupt_vector.vector();
|
||||||
|
- let irq_handle = interrupt_vector.irq_handle().try_clone().unwrap();
|
||||||
|
+ let irq_handle = interrupt_vector.irq_handle().try_clone().unwrap_or_else(|err| {
|
||||||
|
+ log::error!("nvmed: failed to clone IRQ handle: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
let mut nvme = Nvme::new(address.as_ptr() as usize, interrupt_vector, pcid_handle)
|
||||||
|
- .expect("nvmed: failed to allocate driver data");
|
||||||
|
-
|
||||||
|
- unsafe { nvme.init().expect("nvmed: failed to init") }
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ log::error!("nvmed: failed to allocate driver data: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ unsafe {
|
||||||
|
+ nvme.init().unwrap_or_else(|err| {
|
||||||
|
+ log::error!("nvmed: failed to init: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
|
log::debug!("Finished base initialization");
|
||||||
|
let nvme = Arc::new(nvme);
|
||||||
|
|
||||||
|
let executor = nvme::executor::init(Arc::clone(&nvme), iv, false /* FIXME */, irq_handle);
|
||||||
|
|
||||||
|
let mut time_handle = File::open(&format!("/scheme/time/{}", libredox::flag::CLOCK_MONOTONIC))
|
||||||
|
- .expect("failed to open time handle");
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ log::error!("nvmed: failed to open time handle: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
let mut time_events = Box::pin(
|
||||||
|
executor.register_external_event(time_handle.as_raw_fd() as usize, event::EventFlags::READ),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Try to init namespaces for 5 seconds
|
||||||
|
- time_arm(&mut time_handle, 5).expect("failed to arm timer");
|
||||||
|
+ time_arm(&mut time_handle, 5).unwrap_or_else(|err| {
|
||||||
|
+ log::error!("nvmed: failed to arm timer: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
let namespaces = executor.block_on(async {
|
||||||
|
let namespaces_future = nvme.init_with_queues();
|
||||||
|
let time_future = time_events.as_mut().next();
|
||||||
|
@@ -106,7 +127,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
futures::pin_mut!(time_future);
|
||||||
|
match futures::future::select(namespaces_future, time_future).await {
|
||||||
|
futures::future::Either::Left((namespaces, _)) => namespaces,
|
||||||
|
- futures::future::Either::Right(_) => panic!("timeout on init"),
|
||||||
|
+ futures::future::Either::Right(_) => {
|
||||||
|
+ log::error!("nvmed: timeout on init");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
log::debug!("Initialized!");
|
||||||
|
@@ -134,7 +158,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
event::EventFlags::READ,
|
||||||
|
));
|
||||||
|
|
||||||
|
- libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace");
|
||||||
|
+ libredox::call::setrens(0, 0).unwrap_or_else(|err| {
|
||||||
|
+ log::error!("nvmed: failed to enter null namespace: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
log::debug!("Starting to listen for scheme events");
|
||||||
|
|
||||||
|
@@ -150,5 +177,5 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
|
||||||
|
//TODO: destroy NVMe stuff
|
||||||
|
|
||||||
|
- std::process::exit(0);
|
||||||
|
+ process::exit(0);
|
||||||
|
}
|
||||||
|
diff --git a/drivers/storage/virtio-blkd/src/main.rs b/drivers/storage/virtio-blkd/src/main.rs
|
||||||
|
index d21236b3..2b777937 100644
|
||||||
|
--- a/drivers/storage/virtio-blkd/src/main.rs
|
||||||
|
+++ b/drivers/storage/virtio-blkd/src/main.rs
|
||||||
|
@@ -1,6 +1,7 @@
|
||||||
|
#![deny(trivial_numeric_casts, unused_allocation)]
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
+use std::process;
|
||||||
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
|
use driver_block::DiskScheme;
|
||||||
|
@@ -59,14 +60,23 @@ impl BlockDeviceConfig {
|
||||||
|
T: Sized + TryFrom<u64>,
|
||||||
|
<T as TryFrom<u64>>::Error: std::fmt::Debug,
|
||||||
|
{
|
||||||
|
- let transport = self.0.upgrade().unwrap();
|
||||||
|
+ let transport = self.0.upgrade().unwrap_or_else(|| {
|
||||||
|
+ log::error!("virtio-blkd: transport handle dropped");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
let size = core::mem::size_of::<T>()
|
||||||
|
.try_into()
|
||||||
|
- .expect("load_config: invalid size");
|
||||||
|
+ .unwrap_or_else(|_| {
|
||||||
|
+ log::error!("virtio-blkd: load_config: invalid size");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
let value = transport.load_config(ty as u8, size);
|
||||||
|
- T::try_from(value).unwrap()
|
||||||
|
+ T::try_from(value).unwrap_or_else(|_| {
|
||||||
|
+ log::error!("virtio-blkd: load_config: invalid config value");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the capacity of the block device in bytes.
|
||||||
|
@@ -103,8 +113,11 @@ fn main() {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn daemon_runner(redox_daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
- daemon(redox_daemon, pcid_handle).unwrap();
|
||||||
|
- unreachable!();
|
||||||
|
+ daemon(redox_daemon, pcid_handle).unwrap_or_else(|err| {
|
||||||
|
+ log::error!("virtio-blkd: daemon failed: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
+ process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow::Result<()> {
|
||||||
|
@@ -121,7 +134,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow:
|
||||||
|
// 0x1001 - virtio-blk
|
||||||
|
let pci_config = pcid_handle.config();
|
||||||
|
|
||||||
|
- assert_eq!(pci_config.func.full_device_id.device_id, 0x1001);
|
||||||
|
+ if pci_config.func.full_device_id.device_id != 0x1001 {
|
||||||
|
+ log::error!("virtio-blkd: unexpected device ID {:#06x}, expected 0x1001", pci_config.func.full_device_id.device_id);
|
||||||
|
+ process::exit(1);
|
||||||
|
+ }
|
||||||
|
log::info!("virtio-blk: initiating startup sequence :^)");
|
||||||
|
|
||||||
|
let device = virtio_core::probe_device(&mut pcid_handle)?;
|
||||||
|
@@ -147,7 +163,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow:
|
||||||
|
|
||||||
|
let scheme_name = format!("disk.{}", name);
|
||||||
|
|
||||||
|
- let event_queue = event::EventQueue::new().unwrap();
|
||||||
|
+ let mut event_queue = event::EventQueue::new().unwrap_or_else(|err| {
|
||||||
|
+ log::error!("virtio-blkd: failed to create event queue: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
event::user_data! {
|
||||||
|
enum Event {
|
||||||
|
@@ -162,7 +181,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow:
|
||||||
|
&driver_block::FuturesExecutor,
|
||||||
|
);
|
||||||
|
|
||||||
|
- libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace");
|
||||||
|
+ libredox::call::setrens(0, 0).unwrap_or_else(|err| {
|
||||||
|
+ log::error!("virtio-blkd: failed to enter null namespace: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
event_queue
|
||||||
|
.subscribe(
|
||||||
|
@@ -170,11 +192,26 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow:
|
||||||
|
Event::Scheme,
|
||||||
|
event::EventFlags::READ,
|
||||||
|
)
|
||||||
|
- .unwrap();
|
||||||
|
-
|
||||||
|
- for event in event_queue {
|
||||||
|
- match event.unwrap().user_data {
|
||||||
|
- Event::Scheme => futures::executor::block_on(scheme.tick()).unwrap(),
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ log::error!("virtio-blkd: failed to subscribe to scheme events: {err}");
|
||||||
|
+ process::exit(1);
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ loop {
|
||||||
|
+ let event = match event_queue.next() {
|
||||||
|
+ Some(Ok(event)) => event,
|
||||||
|
+ Some(Err(err)) => {
|
||||||
|
+ log::error!("virtio-blkd: failed to get event: {err}");
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ None => break,
|
||||||
|
+ };
|
||||||
|
+ match event.user_data {
|
||||||
|
+ Event::Scheme => {
|
||||||
|
+ if let Err(err) = futures::executor::block_on(scheme.tick()) {
|
||||||
|
+ log::error!("virtio-blkd: failed to handle scheme event: {err}");
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,413 @@
|
|||||||
|
# P2-virtio-core-vbox.patch
|
||||||
|
# Extract virtio-core probe/transport hardening and VirtualBox guest driver
|
||||||
|
# error handling improvements.
|
||||||
|
#
|
||||||
|
# Files: drivers/vboxd/src/main.rs, drivers/virtio-core/src/arch/x86.rs,
|
||||||
|
# drivers/virtio-core/src/probe.rs, drivers/virtio-core/src/spec/split_virtqueue.rs,
|
||||||
|
# drivers/virtio-core/src/transport.rs
|
||||||
|
|
||||||
|
diff --git a/drivers/vboxd/src/main.rs b/drivers/vboxd/src/main.rs
|
||||||
|
index bcb9bb15..b9e42d4a 100644
|
||||||
|
--- a/drivers/vboxd/src/main.rs
|
||||||
|
+++ b/drivers/vboxd/src/main.rs
|
||||||
|
@@ -199,16 +199,28 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
let mut name = pci_config.func.name();
|
||||||
|
name.push_str("_vbox");
|
||||||
|
|
||||||
|
- let bar0 = pci_config.func.bars[0].expect_port();
|
||||||
|
+ let bar0 = match pci_config.func.bars[0].try_port() {
|
||||||
|
+ Ok(port) => port,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ eprintln!("vboxd: invalid BAR0: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
|
||||||
|
let irq = pci_config
|
||||||
|
.func
|
||||||
|
.legacy_interrupt_line
|
||||||
|
- .expect("vboxd: no legacy interrupts supported");
|
||||||
|
+ .unwrap_or_else(|| {
|
||||||
|
+ eprintln!("vboxd: no legacy interrupts supported");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
println!(" + VirtualBox {}", pci_config.func.display());
|
||||||
|
|
||||||
|
- common::acquire_port_io_rights().expect("vboxd: failed to get I/O permission");
|
||||||
|
+ if let Err(err) = common::acquire_port_io_rights() {
|
||||||
|
+ eprintln!("vboxd: failed to get I/O permission: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
let mut width = 0;
|
||||||
|
let mut height = 0;
|
||||||
|
@@ -233,25 +245,55 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- let mut irq_file = irq.irq_handle("vboxd");
|
||||||
|
+ let mut irq_file = match irq.try_irq_handle("vboxd") {
|
||||||
|
+ Ok(file) => file,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ eprintln!("vboxd: failed to open IRQ handle: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
|
||||||
|
- let address = unsafe { pcid_handle.map_bar(1) }.ptr.as_ptr();
|
||||||
|
+ let address = match unsafe { pcid_handle.try_map_bar(1) } {
|
||||||
|
+ Ok(bar) => bar.ptr.as_ptr(),
|
||||||
|
+ Err(err) => {
|
||||||
|
+ eprintln!("vboxd: failed to map BAR1: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
|
{
|
||||||
|
let mut port = common::io::Pio::<u32>::new(bar0 as u16);
|
||||||
|
|
||||||
|
let vmmdev = unsafe { &mut *(address as *mut VboxVmmDev) };
|
||||||
|
|
||||||
|
- let mut guest_info = VboxGuestInfo::new().expect("vboxd: failed to map GuestInfo");
|
||||||
|
+ let mut guest_info = match VboxGuestInfo::new() {
|
||||||
|
+ Ok(value) => value,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ eprintln!("vboxd: failed to map GuestInfo: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
guest_info.version.write(VBOX_VMMDEV_VERSION);
|
||||||
|
guest_info.ostype.write(0x100);
|
||||||
|
port.write(guest_info.physical() as u32);
|
||||||
|
|
||||||
|
- let mut guest_caps = VboxGuestCaps::new().expect("vboxd: failed to map GuestCaps");
|
||||||
|
+ let mut guest_caps = match VboxGuestCaps::new() {
|
||||||
|
+ Ok(value) => value,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ eprintln!("vboxd: failed to map GuestCaps: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
guest_caps.caps.write(1 << 2);
|
||||||
|
port.write(guest_caps.physical() as u32);
|
||||||
|
|
||||||
|
- let mut set_mouse = VboxSetMouse::new().expect("vboxd: failed to map SetMouse");
|
||||||
|
+ let mut set_mouse = match VboxSetMouse::new() {
|
||||||
|
+ Ok(value) => value,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ eprintln!("vboxd: failed to map SetMouse: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
set_mouse.features.write(1 << 4 | 1);
|
||||||
|
port.write(set_mouse.physical() as u32);
|
||||||
|
|
||||||
|
@@ -265,34 +307,71 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- let event_queue =
|
||||||
|
- EventQueue::<Source>::new().expect("vboxd: Could not create event queue.");
|
||||||
|
+ let event_queue = match EventQueue::<Source>::new() {
|
||||||
|
+ Ok(queue) => queue,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ eprintln!("vboxd: could not create event queue: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
event_queue
|
||||||
|
.subscribe(
|
||||||
|
irq_file.as_raw_fd() as usize,
|
||||||
|
Source::Irq,
|
||||||
|
event::EventFlags::READ,
|
||||||
|
)
|
||||||
|
- .unwrap();
|
||||||
|
+ .unwrap_or_else(|err| {
|
||||||
|
+ eprintln!("vboxd: failed to subscribe IRQ fd: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ });
|
||||||
|
|
||||||
|
daemon.ready();
|
||||||
|
|
||||||
|
- libredox::call::setrens(0, 0).expect("vboxd: failed to enter null namespace");
|
||||||
|
+ if let Err(err) = libredox::call::setrens(0, 0) {
|
||||||
|
+ eprintln!("vboxd: failed to enter null namespace: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
let mut bga = crate::bga::Bga::new();
|
||||||
|
- let get_mouse = VboxGetMouse::new().expect("vboxd: failed to map GetMouse");
|
||||||
|
- let display_change = VboxDisplayChange::new().expect("vboxd: failed to map DisplayChange");
|
||||||
|
- let ack_events = VboxAckEvents::new().expect("vboxd: failed to map AckEvents");
|
||||||
|
+ let get_mouse = match VboxGetMouse::new() {
|
||||||
|
+ Ok(value) => value,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ eprintln!("vboxd: failed to map GetMouse: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ let display_change = match VboxDisplayChange::new() {
|
||||||
|
+ Ok(value) => value,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ eprintln!("vboxd: failed to map DisplayChange: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ let ack_events = match VboxAckEvents::new() {
|
||||||
|
+ Ok(value) => value,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ eprintln!("vboxd: failed to map AckEvents: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
|
||||||
|
- for Source::Irq in iter::once(Source::Irq)
|
||||||
|
- .chain(event_queue.map(|e| e.expect("vboxd: failed to get next event").user_data))
|
||||||
|
- {
|
||||||
|
+ for Source::Irq in iter::once(Source::Irq).chain(event_queue.map(|e| match e {
|
||||||
|
+ Ok(event) => event.user_data,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ eprintln!("vboxd: failed to get next event: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ })) {
|
||||||
|
let mut irq = [0; 8];
|
||||||
|
- if irq_file.read(&mut irq).unwrap() >= irq.len() {
|
||||||
|
+ match irq_file.read(&mut irq) {
|
||||||
|
+ Ok(read) if read >= irq.len() => {
|
||||||
|
let host_events = vmmdev.host_events.read();
|
||||||
|
if host_events != 0 {
|
||||||
|
port.write(ack_events.physical() as u32);
|
||||||
|
- irq_file.write(&irq).unwrap();
|
||||||
|
+ if let Err(err) = irq_file.write(&irq) {
|
||||||
|
+ eprintln!("vboxd: failed to acknowledge IRQ: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
if host_events & VBOX_EVENT_DISPLAY == VBOX_EVENT_DISPLAY {
|
||||||
|
port.write(display_change.physical() as u32);
|
||||||
|
@@ -326,8 +405,14 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ Ok(_) => {}
|
||||||
|
+ Err(err) => {
|
||||||
|
+ eprintln!("vboxd: failed to read IRQ file: {err}");
|
||||||
|
+ std::process::exit(1);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- std::process::exit(0);
|
||||||
|
+ std::process::exit(1);
|
||||||
|
}
|
||||||
|
diff --git a/drivers/virtio-core/src/arch/x86.rs b/drivers/virtio-core/src/arch/x86.rs
|
||||||
|
index aea86c4a..c5b2767f 100644
|
||||||
|
--- a/drivers/virtio-core/src/arch/x86.rs
|
||||||
|
+++ b/drivers/virtio-core/src/arch/x86.rs
|
||||||
|
@@ -11,7 +11,10 @@ pub fn enable_msix(pcid_handle: &mut PciFunctionHandle) -> Result<File, Error> {
|
||||||
|
// Extended message signaled interrupts.
|
||||||
|
let msix_info = match pcid_handle.feature_info(PciFeature::MsiX) {
|
||||||
|
PciFeatureInfo::MsiX(capability) => capability,
|
||||||
|
- _ => unreachable!(),
|
||||||
|
+ _ => {
|
||||||
|
+ log::warn!("virtio_core::enable_msix: expected MSI-X feature info");
|
||||||
|
+ return Err(Error::Probe("unexpected PCI feature info for MSI-X"));
|
||||||
|
+ }
|
||||||
|
};
|
||||||
|
let mut info = unsafe { msix_info.map_and_mask_all(pcid_handle) };
|
||||||
|
|
||||||
|
@@ -21,7 +24,10 @@ pub fn enable_msix(pcid_handle: &mut PciFunctionHandle) -> Result<File, Error> {
|
||||||
|
let interrupt_handle = {
|
||||||
|
let table_entry_pointer = info.table_entry_pointer(MSIX_PRIMARY_VECTOR as usize);
|
||||||
|
|
||||||
|
- let destination_id = read_bsp_apic_id().expect("virtio_core: `read_bsp_apic_id()` failed");
|
||||||
|
+ let destination_id = read_bsp_apic_id().map_err(|e| {
|
||||||
|
+ log::warn!("virtio_core::enable_msix: read_bsp_apic_id failed: {e}");
|
||||||
|
+ Error::Probe("read_bsp_apic_id failed")
|
||||||
|
+ })?;
|
||||||
|
let (msg_addr_and_data, interrupt_handle) =
|
||||||
|
allocate_single_interrupt_vector_for_msi(destination_id);
|
||||||
|
table_entry_pointer.write_addr_and_data(msg_addr_and_data);
|
||||||
|
diff --git a/drivers/virtio-core/src/probe.rs b/drivers/virtio-core/src/probe.rs
|
||||||
|
index 5631ef67..eaef1b96 100644
|
||||||
|
--- a/drivers/virtio-core/src/probe.rs
|
||||||
|
+++ b/drivers/virtio-core/src/probe.rs
|
||||||
|
@@ -31,16 +31,16 @@ pub const MSIX_PRIMARY_VECTOR: u16 = 0;
|
||||||
|
/// before starting the device.
|
||||||
|
/// * Finally start the device (via [`StandardTransport::run_device`]). At this point, the device
|
||||||
|
/// is alive.
|
||||||
|
-///
|
||||||
|
-/// ## Panics
|
||||||
|
-/// This function panics if the device is not a virtio device.
|
||||||
|
pub fn probe_device(pcid_handle: &mut PciFunctionHandle) -> Result<Device, Error> {
|
||||||
|
let pci_config = pcid_handle.config();
|
||||||
|
|
||||||
|
- assert_eq!(
|
||||||
|
- pci_config.func.full_device_id.vendor_id, 6900,
|
||||||
|
- "virtio_core::probe_device: not a virtio device"
|
||||||
|
- );
|
||||||
|
+ if pci_config.func.full_device_id.vendor_id != 6900 {
|
||||||
|
+ log::warn!(
|
||||||
|
+ "virtio_core::probe_device: skipping non-virtio device (vendor ID {:#06x})",
|
||||||
|
+ pci_config.func.full_device_id.vendor_id
|
||||||
|
+ );
|
||||||
|
+ return Err(Error::Probe("not a virtio device"));
|
||||||
|
+ }
|
||||||
|
|
||||||
|
let mut common_addr = None;
|
||||||
|
let mut notify_addr = None;
|
||||||
|
@@ -55,7 +55,9 @@ pub fn probe_device(pcid_handle: &mut PciFunctionHandle) -> Result<Device, Error
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
- let (addr, _) = pci_config.func.bars[capability.bar as usize].expect_mem();
|
||||||
|
+ let (addr, _) = pci_config.func.bars[capability.bar as usize]
|
||||||
|
+ .try_mem()
|
||||||
|
+ .map_err(|_| Error::Probe("BAR is not memory-mapped"))?;
|
||||||
|
|
||||||
|
let address = unsafe {
|
||||||
|
let addr = addr + capability.offset as usize;
|
||||||
|
@@ -100,19 +102,23 @@ pub fn probe_device(pcid_handle: &mut PciFunctionHandle) -> Result<Device, Error
|
||||||
|
device_addr = Some(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
- _ => unreachable!(),
|
||||||
|
+ _ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- let common_addr = common_addr.expect("virtio common capability missing");
|
||||||
|
- let device_addr = device_addr.expect("virtio device capability missing");
|
||||||
|
- let (notify_addr, notify_multiplier) = notify_addr.expect("virtio notify capability missing");
|
||||||
|
-
|
||||||
|
- // FIXME this is explicitly allowed by the virtio specification to happen
|
||||||
|
- assert!(
|
||||||
|
- notify_multiplier != 0,
|
||||||
|
- "virtio-core::device_probe: device uses the same Queue Notify addresses for all queues"
|
||||||
|
- );
|
||||||
|
+ let common_addr = common_addr.ok_or(Error::InCapable(CfgType::Common))?;
|
||||||
|
+ let device_addr = device_addr.ok_or(Error::InCapable(CfgType::Device))?;
|
||||||
|
+ let (notify_addr, notify_multiplier) =
|
||||||
|
+ notify_addr.ok_or(Error::InCapable(CfgType::Notify))?;
|
||||||
|
+
|
||||||
|
+ // The virtio specification explicitly allows a zero notify_off_multiplier,
|
||||||
|
+ // meaning all queues share the same notification address. Handle gracefully.
|
||||||
|
+ if notify_multiplier == 0 {
|
||||||
|
+ log::warn!(
|
||||||
|
+ "virtio_core::probe_device: device uses the same Queue Notify address for all queues"
|
||||||
|
+ );
|
||||||
|
+ return Err(Error::Probe("zero notify_off_multiplier"));
|
||||||
|
+ }
|
||||||
|
|
||||||
|
let common = unsafe { &mut *(common_addr as *mut CommonCfg) };
|
||||||
|
let device_space = unsafe { &mut *(device_addr as *mut u8) };
|
||||||
|
@@ -128,8 +134,10 @@ pub fn probe_device(pcid_handle: &mut PciFunctionHandle) -> Result<Device, Error
|
||||||
|
let all_pci_features = pcid_handle.fetch_all_features();
|
||||||
|
let has_msix = all_pci_features.iter().any(|feature| feature.is_msix());
|
||||||
|
|
||||||
|
- // According to the virtio specification, the device REQUIRED to support MSI-X.
|
||||||
|
- assert!(has_msix, "virtio: device does not support MSI-X");
|
||||||
|
+ if !has_msix {
|
||||||
|
+ log::warn!("virtio_core::probe_device: device does not support MSI-X");
|
||||||
|
+ return Err(Error::Probe("device does not support MSI-X"));
|
||||||
|
+ }
|
||||||
|
let irq_handle = crate::arch::enable_msix(pcid_handle)?;
|
||||||
|
|
||||||
|
log::debug!("virtio: using standard PCI transport");
|
||||||
|
diff --git a/drivers/virtio-core/src/spec/split_virtqueue.rs b/drivers/virtio-core/src/spec/split_virtqueue.rs
|
||||||
|
index b9636711..23aa5484 100644
|
||||||
|
--- a/drivers/virtio-core/src/spec/split_virtqueue.rs
|
||||||
|
+++ b/drivers/virtio-core/src/spec/split_virtqueue.rs
|
||||||
|
@@ -197,9 +197,9 @@ impl ChainBuilder {
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(mut self) -> Vec<Buffer> {
|
||||||
|
- let last_buffer = self.buffers.last_mut().expect("virtio-core: empty chain");
|
||||||
|
- last_buffer.flags.remove(DescriptorFlags::NEXT);
|
||||||
|
-
|
||||||
|
+ if let Some(last_buffer) = self.buffers.last_mut() {
|
||||||
|
+ last_buffer.flags.remove(DescriptorFlags::NEXT);
|
||||||
|
+ }
|
||||||
|
self.buffers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diff --git a/drivers/virtio-core/src/transport.rs b/drivers/virtio-core/src/transport.rs
|
||||||
|
index d3445d2d..99972c95 100644
|
||||||
|
--- a/drivers/virtio-core/src/transport.rs
|
||||||
|
+++ b/drivers/virtio-core/src/transport.rs
|
||||||
|
@@ -19,6 +19,8 @@ pub enum Error {
|
||||||
|
SyscallError(#[from] libredox::error::Error),
|
||||||
|
#[error("the device is incapable of {0:?}")]
|
||||||
|
InCapable(CfgType),
|
||||||
|
+ #[error("virtio probe: {0}")]
|
||||||
|
+ Probe(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the queue part sizes in bytes.
|
||||||
|
@@ -59,14 +61,23 @@ pub fn spawn_irq_thread(irq_handle: &File, queue: &Arc<Queue<'static>>) {
|
||||||
|
let queue_copy = queue.clone();
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
- let event_queue = RawEventQueue::new().unwrap();
|
||||||
|
+ let event_queue = match RawEventQueue::new() {
|
||||||
|
+ Ok(eq) => eq,
|
||||||
|
+ Err(err) => {
|
||||||
|
+ log::error!("virtio-core: failed to create event queue for IRQ thread: {err}");
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
|
||||||
|
- event_queue
|
||||||
|
- .subscribe(irq_fd as usize, 0, event::EventFlags::READ)
|
||||||
|
- .unwrap();
|
||||||
|
+ if let Err(err) = event_queue.subscribe(irq_fd as usize, 0, event::EventFlags::READ) {
|
||||||
|
+ log::error!("virtio-core: failed to subscribe to IRQ fd: {err}");
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- for _ in event_queue.map(Result::unwrap) {
|
||||||
|
- // Wake up the tasks waiting on the queue.
|
||||||
|
+ for event_result in event_queue.map(|res| res) {
|
||||||
|
+ if event_result.is_err() {
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
for (_, task) in queue_copy.waker.lock().unwrap().iter() {
|
||||||
|
task.wake_by_ref();
|
||||||
|
}
|
||||||
|
@@ -604,7 +615,9 @@ impl Transport for StandardTransport<'_> {
|
||||||
|
// Re-read device status to ensure the `FEATURES_OK` bit is still set: otherwise,
|
||||||
|
// the device does not support our subset of features and the device is unusable.
|
||||||
|
let confirm = common.device_status.get();
|
||||||
|
- assert!((confirm & DeviceStatusFlags::FEATURES_OK) == DeviceStatusFlags::FEATURES_OK);
|
||||||
|
+ if (confirm & DeviceStatusFlags::FEATURES_OK) != DeviceStatusFlags::FEATURES_OK {
|
||||||
|
+ log::error!("virtio-core: device rejected feature set (FEATURES_OK cleared after negotiation)");
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_config_notify(&self, vector: u16) {
|
||||||
|
@@ -640,7 +653,10 @@ impl Transport for StandardTransport<'_> {
|
||||||
|
|
||||||
|
// Set the MSI-X vector.
|
||||||
|
common.queue_msix_vector.set(vector);
|
||||||
|
- assert!(common.queue_msix_vector.get() == vector);
|
||||||
|
+ if common.queue_msix_vector.get() != vector {
|
||||||
|
+ log::error!("virtio-core: MSI-X vector {vector:#x} was not accepted by device for queue {queue_index}");
|
||||||
|
+ return Err(Error::SyscallError(libredox::error::Error::new(libredox::errno::EIO)));
|
||||||
|
+ }
|
||||||
|
|
||||||
|
// Enable the queue.
|
||||||
|
common.queue_enable.set(1);
|
||||||
|
@@ -685,7 +701,9 @@ impl Transport for StandardTransport<'_> {
|
||||||
|
|
||||||
|
// Set the MSI-X vector.
|
||||||
|
common.queue_msix_vector.set(queue.vector);
|
||||||
|
- assert!(common.queue_msix_vector.get() == queue.vector);
|
||||||
|
+ if common.queue_msix_vector.get() != queue.vector {
|
||||||
|
+ log::error!("virtio-core: MSI-X vector {:#x} was not accepted during reinit for queue {}", queue.vector, queue.queue_index);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
// Enable the queue.
|
||||||
|
common.queue_enable.set(1);
|
||||||
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "nightly-2025-10-03"
|
channel = "nightly-2026-04-01"
|
||||||
components = ["rust-src", "rustfmt", "clippy"]
|
components = ["rust-src", "rustfmt", "clippy"]
|
||||||
profile = "minimal"
|
profile = "minimal"
|
||||||
|
|||||||
Reference in New Issue
Block a user