diff --git a/local/patches/base/redox.patch b/local/patches/base/redox.patch index e70d3718..f02a7125 100644 --- a/local/patches/base/redox.patch +++ b/local/patches/base/redox.patch @@ -1,5 +1,5 @@ diff --git a/Cargo.lock b/Cargo.lock -index 9fcbd662..b4ea6b1d 100644 +index 9fcbd662..760fd8b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,11 +31,20 @@ dependencies = [ @@ -237,7 +237,15 @@ index 9fcbd662..b4ea6b1d 100644 [[package]] name = "ioslice" version = "0.6.0" -@@ -2390,6 +2564,24 @@ version = "1.19.0" +@@ -1174,6 +1348,7 @@ dependencies = [ + "daemon", + "driver-network", + "libredox", ++ "log", + "pcid", + "redox_event", + "redox_syscall 0.7.4", +@@ -2390,6 +2565,24 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" @@ -309,8 +317,28 @@ index 9e776232..36d87870 100644 libc = "0.2.181" log = "0.4" libredox = "0.1.16" +diff --git a/audiod/src/main.rs b/audiod/src/main.rs +index 51b103af..2354cf5f 100644 +--- a/audiod/src/main.rs ++++ b/audiod/src/main.rs +@@ -48,7 +48,14 @@ fn daemon(daemon: SchemeDaemon) -> anyhow::Result<()> { + + let pid = libredox::call::getpid()?; + +- let hw_file = Fd::open("/scheme/audiohw", flag::O_WRONLY | flag::O_CLOEXEC, 0)?; ++ let hw_file = match Fd::open("/scheme/audiohw", flag::O_WRONLY | flag::O_CLOEXEC, 0) { ++ Ok(fd) => fd, ++ Err(err) if err.errno() == syscall::ENODEV => { ++ eprintln!("audiod: no audio hardware detected"); ++ return Ok(()); ++ } ++ Err(err) => return Err(err).context("failed to open /scheme/audiohw"), ++ }; + + let socket = Socket::create().context("failed to create scheme")?; + diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs -index 9f507221..a0ba9d88 100644 +index 9f507221..4e434082 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -11,12 +11,23 @@ use redox_scheme::Socket; @@ -392,7 +420,7 @@ index 9f507221..a0ba9d88 100644 } } } -@@ -94,12 +105,22 @@ impl SchemeDaemon { +@@ -96,13 +116,16 @@ impl SchemeDaemon { /// Notify the process that the scheme daemon is ready to accept requests. pub fn ready_with_fd(self, cap_fd: Fd) -> syscall::Result<()> { @@ -412,26 +440,6 @@ index 9f507221..a0ba9d88 100644 } /// Notify the process that the synchronous scheme daemon is ready to accept requests. -diff --git a/audiod/src/main.rs b/audiod/src/main.rs -index 5a8c8d06..c3a1d4f0 100644 ---- a/audiod/src/main.rs -+++ b/audiod/src/main.rs -@@ -48,7 +48,14 @@ fn daemon(daemon: SchemeDaemon) -> anyhow::Result<()> { - - let pid = libredox::call::getpid()?; - -- let hw_file = Fd::open("/scheme/audiohw", flag::O_WRONLY | flag::O_CLOEXEC, 0)?; -+ let hw_file = match Fd::open("/scheme/audiohw", flag::O_WRONLY | flag::O_CLOEXEC, 0) { -+ Ok(fd) => fd, -+ Err(err) if err.errno() == syscall::ENODEV => { -+ eprintln!("audiod: no audio hardware detected"); -+ return Ok(()); -+ } -+ Err(err) => return Err(err).context("failed to open /scheme/audiohw"), -+ }; - - let socket = Socket::create().context("failed to create scheme")?; - diff --git a/drivers/acpid/Cargo.toml b/drivers/acpid/Cargo.toml index 2d22a8f9..712b6d6e 100644 --- a/drivers/acpid/Cargo.toml @@ -4194,6 +4202,1156 @@ index ffa8a94b..e4dbf930 100644 } #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +diff --git a/drivers/audio/ihdad/src/hda/device.rs b/drivers/audio/ihdad/src/hda/device.rs +index 78e8f0a2..e5742f80 100755 +--- a/drivers/audio/ihdad/src/hda/device.rs ++++ b/drivers/audio/ihdad/src/hda/device.rs +@@ -1,6 +1,6 @@ + #![allow(dead_code)] + +-use std::collections::HashMap; ++use std::collections::{HashMap, HashSet}; + use std::fmt::Write; + use std::str; + use std::task::Poll; +@@ -14,20 +14,55 @@ use redox_scheme::scheme::SchemeSync; + use redox_scheme::CallerCtx; + use redox_scheme::OpenResult; + use scheme_utils::{FpathWriter, HandleMap}; +-use syscall::error::{Error, Result, EACCES, EBADF, EIO, ENODEV, EWOULDBLOCK}; ++use syscall::error::{Error, Result, EACCES, EBADF, EIO, ENODEV, ENOENT, EWOULDBLOCK}; + + use spin::Mutex; + use syscall::schemev2::NewFdFlags; + + use super::common::*; ++use super::parser::AutoPinConfig; + use super::BitsPerSample; + use super::BufferDescriptorListEntry; + use super::CommandBuffer; ++use super::digital::DigitalCodecInfo; ++use super::dispatch::RouteDecision; ++use super::FixupEngine; + use super::HDANode; ++use super::InputStream; + use super::OutputStream; + use super::StreamBuffer; + use super::StreamDescriptorRegs; + ++#[derive(Debug, Clone)] ++pub struct ControllerPolicy { ++ pub prefer_msi: bool, ++ pub single_cmd_fallback: bool, ++ pub probe_mask: u16, ++ pub position_fix: PositionFixPolicy, ++ pub poll_jack: bool, ++} ++ ++#[derive(Debug, Clone, Copy, PartialEq, Eq)] ++pub enum PositionFixPolicy { ++ Auto, ++ Lpib, ++ Posbuf, ++ Dpic, ++ None, ++} ++ ++impl Default for ControllerPolicy { ++ fn default() -> Self { ++ ControllerPolicy { ++ prefer_msi: true, ++ single_cmd_fallback: false, ++ probe_mask: 0xFFFF, ++ position_fix: PositionFixPolicy::Auto, ++ poll_jack: false, ++ } ++ } ++} ++ + // GCTL - Global Control + const CRST: u32 = 1 << 0; // 1 bit + const FNCTRL: u32 = 1 << 1; // 1 bit +@@ -55,6 +90,20 @@ const RIRBDMAEN: u8 = 1 << 1; // 1 bit + const ICB: u16 = 1 << 0; + const IRV: u16 = 1 << 1; + ++// INTCTL bits ++const INTCTL_GIE: u32 = 1 << 31; // Global Interrupt Enable ++const INTCTL_CIE: u32 = 1 << 30; // Controller Interrupt Enable ++ ++// RIRBSTS bits (write-1-to-clear) ++const RIRBSTS_RINTFL: u8 = 1 << 0; // Response Interrupt Flag ++const RIRBSTS_RIRBOIS: u8 = 1 << 2; // RIRB Overrun Interrupt Status ++ ++// CORBSTS bits (write-1-to-clear) ++const CORBSTS_CMEI: u8 = 1 << 0; // CORB Memory Error Interrupt ++ ++// STATESTS mask — one bit per codec slot (bits 0-14) ++const STATESTS_MASK: u16 = 0x7FFF; ++ + // CORB and RIRB offset + + const COMMAND_BUFFER_OFFSET: usize = 0x40; +@@ -63,9 +112,8 @@ const NUM_SUB_BUFFS: usize = 32; + const SUB_BUFF_SIZE: usize = 2048; + + enum Handle { +- Todo, +- Pcmout(usize, usize, usize), // Card, index, block_ptr +- Pcmin(usize, usize, usize), // Card, index, block_ptr ++ Pcmout { stream_index: usize }, ++ Pcmin { stream_index: usize }, + StrBuf(Vec), + SchemeRoot, + } +@@ -121,6 +169,34 @@ struct Regs { + dpubase: Mmio, // 0x74 + } + ++struct CodecTopology { ++ codec_addr: CodecAddr, ++ afgs: Vec, ++ widget_map: HashMap, ++ outputs: Vec, ++ inputs: Vec, ++ output_pins: Vec, ++ input_pins: Vec, ++ beep_addr: Option, ++ pin_config: AutoPinConfig, ++} ++ ++impl CodecTopology { ++ fn new(codec_addr: CodecAddr) -> Self { ++ CodecTopology { ++ codec_addr, ++ afgs: Vec::new(), ++ widget_map: HashMap::new(), ++ outputs: Vec::new(), ++ inputs: Vec::new(), ++ output_pins: Vec::new(), ++ input_pins: Vec::new(), ++ beep_addr: None, ++ pin_config: AutoPinConfig::default(), ++ } ++ } ++} ++ + pub struct IntelHDA { + vend_prod: u32, + +@@ -131,6 +207,7 @@ pub struct IntelHDA { + cmd: CommandBuffer, + + codecs: Vec, ++ codecs_topology: HashMap, + + outputs: Vec, + inputs: Vec, +@@ -140,15 +217,20 @@ pub struct IntelHDA { + output_pins: Vec, + input_pins: Vec, + +- beep_addr: WidgetAddr, ++ beep_addr: Option, + + buff_desc: Dma<[BufferDescriptorListEntry; 256]>, ++ input_buff_desc: Dma<[BufferDescriptorListEntry; 256]>, + + output_streams: Vec, ++ input_streams: Vec, + + buffs: Vec>, + + int_counter: usize, ++ policy: ControllerPolicy, ++ fixup_engine: FixupEngine, ++ digital_codecs: Vec, + handles: Mutex>, + } + +@@ -160,6 +242,10 @@ impl IntelHDA { + .expect("Could not allocate physical memory for buffer descriptor list.") + .assume_init(); + ++ let input_buff_desc = Dma::<[BufferDescriptorListEntry; 256]>::zeroed() ++ .expect("Could not allocate physical memory for input buffer descriptor list.") ++ .assume_init(); ++ + log::debug!( + "Virt: {:016X}, Phys: {:016X}", + buff_desc.as_ptr() as usize, +@@ -182,11 +268,12 @@ impl IntelHDA { + + cmd: CommandBuffer::new(base + COMMAND_BUFFER_OFFSET, cmd_buff), + +- beep_addr: (0, 0), ++ beep_addr: None, + + widget_map: HashMap::::new(), + + codecs: Vec::::new(), ++ codecs_topology: HashMap::::new(), + + outputs: Vec::::new(), + inputs: Vec::::new(), +@@ -195,21 +282,34 @@ impl IntelHDA { + input_pins: Vec::::new(), + + buff_desc, ++ input_buff_desc, + + output_streams: Vec::::new(), ++ input_streams: Vec::::new(), + + buffs: Vec::>::new(), + + int_counter: 0, ++ policy: ControllerPolicy::default(), ++ fixup_engine: FixupEngine::new(), ++ digital_codecs: Vec::new(), + handles: Mutex::new(HandleMap::new()), + }; + + module.init()?; + ++ let vendor_id = ((module.vend_prod >> 16) & 0xFFFF) as u16; ++ let device_id = (module.vend_prod & 0xFFFF) as u16; ++ let route = RouteDecision::decide(vendor_id, device_id, 0x04, 0x03); ++ log::info!("IHDA: audio route decision: {:?} ({})", route.route, route.reason); ++ + module.info(); + module.enumerate()?; + + module.configure()?; ++ if let Err(err) = module.configure_input() { ++ log::debug!("IHDA: input configuration skipped: {:?}", err); ++ } + log::debug!("IHDA: Initialization finished."); + Ok(module) + } +@@ -219,23 +319,28 @@ impl IntelHDA { + + let use_immediate_command_interface = match self.vend_prod { + 0x8086_2668 => false, +- _ => true, ++ _ => !self.policy.single_cmd_fallback, + }; + + self.cmd.init(use_immediate_command_interface)?; ++ ++ self.regs.gctl.writef(UNSOL, true); ++ + self.init_interrupts(); + + Ok(()) + } + + pub fn init_interrupts(&mut self) { +- // TODO: provide a function to enable certain interrupts +- // This just enables the first output stream interupt and the global interrupt +- + let iss = self.num_input_streams(); +- self.regs +- .intctl +- .write((1 << 31) | /* (1 << 30) |*/ (1 << iss)); ++ ++ let mut wakeen: u16 = 0; ++ for &codec in &self.codecs { ++ wakeen |= 1 << codec; ++ } ++ self.regs.wakeen.write(wakeen); ++ ++ self.regs.intctl.write(INTCTL_GIE | INTCTL_CIE | (1 << iss) | (1 << 0)); + } + + pub fn irq(&mut self) -> bool { +@@ -248,6 +353,19 @@ impl IntelHDA { + self.int_counter + } + ++ pub fn policy(&self) -> &ControllerPolicy { ++ &self.policy ++ } ++ ++ pub fn set_policy(&mut self, policy: ControllerPolicy) { ++ log::info!( ++ "IHDA: policy updated msi={} single_cmd={} probe_mask={:04X} pos_fix={:?} poll_jack={}", ++ policy.prefer_msi, policy.single_cmd_fallback, policy.probe_mask, ++ policy.position_fix, policy.poll_jack ++ ); ++ self.policy = policy; ++ } ++ + pub fn read_node(&mut self, addr: WidgetAddr) -> Result { + let mut node = HDANode::new(); + let mut temp: u64; +@@ -341,70 +459,156 @@ impl IntelHDA { + } + + pub fn enumerate(&mut self) -> Result<()> { ++ // Clear old global state (kept for migration safety) + self.output_pins.clear(); + self.input_pins.clear(); ++ self.outputs.clear(); ++ self.inputs.clear(); ++ self.widget_map.clear(); ++ self.codecs_topology.clear(); ++ ++ let codec_addrs = self.codecs.clone(); ++ for codec in codec_addrs { ++ let mut topo = CodecTopology::new(codec); ++ ++ let root = self.read_node((codec, 0))?; ++ log::debug!("{}", root); ++ ++ let root_count = root.subnode_count; ++ let root_start = root.subnode_start; ++ ++ for i in 0..root_count { ++ let afg = self.read_node((codec, root_start + i))?; ++ log::debug!("{}", afg); ++ ++ // Only process audio function groups (type 0x01) ++ if afg.function_group_type != 0x01 { ++ log::debug!( ++ "Codec {}: function group {} is type {}, not audio \u{2014} skipping", ++ codec, ++ afg.addr.1, ++ afg.function_group_type ++ ); ++ continue; ++ } ++ ++ topo.afgs.push(afg.addr.1); + +- let codec: u8 = 0; +- +- let root = self.read_node((codec, 0))?; +- +- log::debug!("{}", root); +- +- let root_count = root.subnode_count; +- let root_start = root.subnode_start; +- +- //FIXME: So basically the way this is set up is to only support one codec and hopes the first one is an audio +- for i in 0..root_count { +- let afg = self.read_node((codec, root_start + i))?; +- log::debug!("{}", afg); +- let afg_count = afg.subnode_count; +- let afg_start = afg.subnode_start; +- +- for j in 0..afg_count { +- let mut widget = self.read_node((codec, afg_start + j))?; +- widget.is_widget = true; +- match widget.widget_type() { +- HDAWidgetType::AudioOutput => self.outputs.push(widget.addr), +- HDAWidgetType::AudioInput => self.inputs.push(widget.addr), +- HDAWidgetType::BeepGenerator => self.beep_addr = widget.addr, +- HDAWidgetType::PinComplex => { +- let config = widget.configuration_default(); +- if config.is_output() { +- self.output_pins.push(widget.addr); +- } else if config.is_input() { +- self.input_pins.push(widget.addr); ++ let afg_count = afg.subnode_count; ++ let afg_start = afg.subnode_start; ++ ++ for j in 0..afg_count { ++ let mut widget = self.read_node((codec, afg_start + j))?; ++ widget.is_widget = true; ++ match widget.widget_type() { ++ HDAWidgetType::AudioOutput => { ++ self.outputs.push(widget.addr); ++ topo.outputs.push(widget.addr); ++ } ++ HDAWidgetType::AudioInput => { ++ self.inputs.push(widget.addr); ++ topo.inputs.push(widget.addr); + } ++ HDAWidgetType::BeepGenerator => { ++ self.beep_addr = Some(widget.addr); ++ topo.beep_addr = Some(widget.addr); ++ } ++ HDAWidgetType::PinComplex => { ++ let config = widget.configuration_default(); ++ if config.is_output() { ++ self.output_pins.push(widget.addr); ++ topo.output_pins.push(widget.addr); ++ } else if config.is_input() { ++ self.input_pins.push(widget.addr); ++ topo.input_pins.push(widget.addr); ++ } ++ } ++ _ => {} + } +- _ => {} ++ ++ log::debug!("{}", widget); ++ self.widget_map.insert(widget.addr(), widget.clone()); ++ topo.widget_map.insert(widget.addr(), widget); + } ++ } + +- log::debug!("{}", widget); +- self.widget_map.insert(widget.addr(), widget); ++ log::debug!( ++ "Codec {}: {} AFGs, {} outputs, {} inputs, {} output pins, {} input pins", ++ codec, ++ topo.afgs.len(), ++ topo.outputs.len(), ++ topo.inputs.len(), ++ topo.output_pins.len(), ++ topo.input_pins.len(), ++ ); ++ ++ let widget_list: Vec<(WidgetAddr, HDANode)> = topo.widget_map.iter().map(|(a, n)| (*a, n.clone())).collect(); ++ topo.pin_config = AutoPinConfig::parse(&widget_list); ++ ++ self.codecs_topology.insert(codec, topo); ++ } ++ ++ for (codec, topo) in &self.codecs_topology { ++ if let Some(digi) = DigitalCodecInfo::detect_from_topology(*codec, &topo.widget_map) { ++ log::info!( ++ "IHDA: digital codec detected at {} (hdmi={}, {} pins, {} converters)", ++ codec, digi.is_hdmi, digi.pin_widgets.len(), digi.converter_widgets.len() ++ ); ++ self.digital_codecs.push(digi); + } + } + + Ok(()) + } + +- pub fn find_best_output_pin(&mut self) -> Result { +- let outs = &self.output_pins; ++ fn pick_primary_codec_for_output(&self) -> Option { ++ let mut candidates: Vec = self ++ .codecs_topology ++ .values() ++ .filter(|topo| !topo.output_pins.is_empty() && !topo.outputs.is_empty()) ++ .map(|topo| topo.codec_addr) ++ .collect(); ++ candidates.sort(); ++ candidates.into_iter().next() ++ } ++ ++ pub fn find_best_output_pin(&mut self, codec: CodecAddr) -> Result { ++ let outs: Vec = self ++ .codecs_topology ++ .get(&codec) ++ .ok_or_else(|| { ++ log::error!("No topology for codec {}", codec); ++ Error::new(ENODEV) ++ })? ++ .output_pins ++ .clone(); ++ + if outs.len() == 1 { + return Ok(outs[0]); + } else if outs.len() > 1 { +- //TODO: change output based on "unsolicited response" interrupts +- // Check for devices in this order: Headphone, Speaker, Line Out + for supported_device in &[DefaultDevice::HPOut, DefaultDevice::Speaker] { +- for &out in outs { +- let widget = self.widget_map.get(&out).unwrap(); +- let cd = widget.configuration_default(); ++ for &out in &outs { ++ let (addr, config_default) = { ++ let widget = self ++ .codecs_topology ++ .get(&codec) ++ .and_then(|t| t.widget_map.get(&out)) ++ .ok_or_else(|| { ++ log::error!( ++ "Widget {:?} not found in codec {} topology", ++ out, ++ codec ++ ); ++ Error::new(ENODEV) ++ })?; ++ (widget.addr, widget.config_default) ++ }; ++ let cd = ConfigurationDefault::from_u32(config_default); + if cd.sequence() == 0 && &cd.default_device() == supported_device { +- // Check for jack detect bit +- let pin_caps = self.cmd.cmd12(widget.addr, 0xF00, 0x0C)?; ++ let pin_caps = self.cmd.cmd12(addr, 0xF00, 0x0C)?; + if pin_caps & (1 << 2) != 0 { +- // Check for presence +- let pin_sense = self.cmd.cmd12(widget.addr, 0xF09, 0)?; ++ let pin_sense = self.cmd.cmd12(addr, 0xF09, 0)?; + if pin_sense & (1 << 31) == 0 { +- // Skip if nothing is plugged in + continue; + } + } +@@ -416,13 +620,26 @@ impl IntelHDA { + Err(Error::new(ENODEV)) + } + +- pub fn find_path_to_dac(&self, addr: WidgetAddr) -> Option> { +- let widget = self.widget_map.get(&addr).unwrap(); ++ pub fn find_path_to_dac( ++ &self, ++ addr: WidgetAddr, ++ codec: CodecAddr, ++ visited: &mut HashSet, ++ ) -> Option> { ++ if visited.contains(&addr) { ++ log::warn!("Cycle detected in widget graph at {:?}", addr); ++ return None; ++ } ++ visited.insert(addr); ++ ++ let topo = self.codecs_topology.get(&codec)?; ++ let widget = topo.widget_map.get(&addr)?; ++ + if widget.widget_type() == HDAWidgetType::AudioOutput { + Some(vec![addr]) + } else { + let connection = widget.connections.get(widget.connection_default as usize)?; +- let mut path = self.find_path_to_dac(*connection)?; ++ let mut path = self.find_path_to_dac(*connection, codec, visited)?; + path.insert(0, addr); + Some(path) + } +@@ -466,72 +683,92 @@ impl IntelHDA { + } + + pub fn configure(&mut self) -> Result<()> { +- let outpin = self.find_best_output_pin()?; ++ let codec = self.pick_primary_codec_for_output().ok_or_else(|| { ++ log::error!("No suitable codec found for audio output"); ++ Error::new(ENODEV) ++ })?; ++ ++ log::debug!("Selected codec {} for output", codec); ++ ++ let topo = self.codecs_topology.get(&codec).ok_or_else(|| { ++ log::error!("No topology for codec {}", codec); ++ Error::new(ENODEV) ++ })?; ++ ++ let vendor_id = ((self.vend_prod >> 16) & 0xFFFF) as u16; ++ let device_id = (self.vend_prod & 0xFFFF) as u16; ++ self.fixup_engine.match_fixups(vendor_id, device_id, None, &topo.pin_config); ++ ++ let primary_pins = topo.pin_config.primary_output_pins(); ++ let outpin = primary_pins.first().map(|p| p.addr).ok_or_else(|| { ++ log::error!("No primary output pins found by parser on codec {}", codec); ++ Error::new(ENODEV) ++ })?; + + log::debug!("Best pin: {:01X}:{:02X}", outpin.0, outpin.1); + +- let path = self.find_path_to_dac(outpin).unwrap(); ++ let path = { ++ let mut visited = HashSet::new(); ++ self.find_path_to_dac(outpin, codec, &mut visited) ++ .ok_or_else(|| { ++ log::error!( ++ "No path to DAC from pin {:01X}:{:02X} on codec {}", ++ outpin.0, ++ outpin.1, ++ codec ++ ); ++ Error::new(ENODEV) ++ })? ++ }; + +- let dac = *path.last().unwrap(); +- let pin = *path.first().unwrap(); ++ let dac = *path.last().ok_or_else(|| { ++ log::error!("Empty DAC path for pin {:01X}:{:02X}", outpin.0, outpin.1); ++ Error::new(ENODEV) ++ })?; ++ let pin = *path.first().ok_or_else(|| { ++ log::error!("Empty path (no pin) for pin {:01X}:{:02X}", outpin.0, outpin.1); ++ Error::new(ENODEV) ++ })?; + + log::debug!("Path to DAC: {:X?}", path); + +- // Set power state 0 (on) for all widgets in path + for &addr in &path { + self.set_power_state(addr, 0)?; + } + +- // Pin enable (0x80 = headphone amp enable, 0x40 = output enable) + self.cmd.cmd12(pin, 0x707, 0xC0)?; +- +- // EAPD enable + self.cmd.cmd12(pin, 0x70C, 2)?; +- +- // Set DAC stream and channel ++ self.cmd.cmd4(pin, 0x708, (1 << 8) | 1)?; + self.set_stream_channel(dac, 1, 0)?; + + self.update_sound_buffers(); + +- log::debug!( +- "Supported Formats: {:08X}", +- self.get_supported_formats((0, 0x1))? +- ); +- log::debug!("Capabilities: {:08X}", self.get_capabilities(path[0])?); ++ let (rate, bps, channels) = self.negotiate_stream_format(dac)?; ++ log::debug!("IHDA: negotiated stream format bps={:?} ch={}", bps, channels); + +- // Create output stream + let output = self.get_output_stream_descriptor(0).unwrap(); + output.set_address(self.buff_desc.physical()); +- output.set_pcm_format(&super::SR_44_1, BitsPerSample::Bits16, 2); +- output.set_cyclic_buffer_length((NUM_SUB_BUFFS * SUB_BUFF_SIZE) as u32); // number of bytes ++ output.set_pcm_format(rate, bps, channels); ++ output.set_cyclic_buffer_length((NUM_SUB_BUFFS * SUB_BUFF_SIZE) as u32); + output.set_stream_number(1); + output.set_last_valid_index((NUM_SUB_BUFFS - 1) as u16); + output.set_interrupt_on_completion(true); + +- // Set DAC converter format +- self.set_converter_format(dac, &super::SR_44_1, BitsPerSample::Bits16, 2)?; ++ self.set_converter_format(dac, rate, bps, channels)?; + +- // Get DAC converter format +- //TODO: should validate? + self.cmd.cmd12(dac, 0xA00, 0)?; + +- // Unmute and set gain to 0db for input and output amplifiers on all widgets in path + for &addr in &path { +- // Read widget capabilities + let caps = self.cmd.cmd12(addr, 0xF00, 0x09)?; + +- //TODO: do we need to set any other indexes? + let left = true; + let right = true; + let index = 0; + let mute = false; + +- // Check for input amp + if (caps & (1 << 1)) != 0 { +- // Read input capabilities + let in_caps = self.cmd.cmd12(addr, 0xF00, 0x0D)?; + let in_gain = (in_caps & 0x7f) as u8; +- // Set input gain + let output = false; + let input = true; + self.set_amplifier_gain_mute( +@@ -540,12 +777,9 @@ impl IntelHDA { + log::debug!("Set {:X?} input gain to 0x{:X}", addr, in_gain); + } + +- // Check for output amp + if (caps & (1 << 2)) != 0 { +- // Read output capabilities + let out_caps = self.cmd.cmd12(addr, 0xF00, 0x12)?; + let out_gain = (out_caps & 0x7f) as u8; +- // Set output gain + let output = true; + let input = false; + self.set_amplifier_gain_mute( +@@ -555,8 +789,6 @@ impl IntelHDA { + } + } + +- //TODO: implement hda-verb? +- + output.run(); + { + log::debug!("Waiting for output 0 to start running..."); +@@ -632,20 +864,21 @@ impl IntelHDA { + + */ + +- pub fn dump_codec(&self, codec: u8) -> String { ++ pub fn dump_all_codecs(&self) -> String { + let mut string = String::new(); + +- for (_, widget) in self.widget_map.iter() { +- let _ = writeln!(string, "{}", widget); ++ for (&codec, topo) in &self.codecs_topology { ++ let _ = writeln!(string, "Codec {}:", codec); ++ for (_, widget) in topo.widget_map.iter() { ++ let _ = writeln!(string, " {}", widget); ++ } + } + + string + } + +- // BEEP!! + pub fn beep(&mut self, div: u8) { +- let addr = self.beep_addr; +- if addr != (0, 0) { ++ if let Some(addr) = self.beep_addr { + let _ = self.cmd.cmd12(addr, 0xF0A, div); + } + } +@@ -700,7 +933,7 @@ impl IntelHDA { + log::debug!("Statests: {:04X}", statests); + + for i in 0..15 { +- if (statests >> i) & 0x1 == 1 { ++ if (statests >> i) & 0x1 == 1 && (self.policy.probe_mask >> i) & 0x1 == 1 { + self.codecs.push(i as CodecAddr); + } + } +@@ -812,6 +1045,54 @@ impl IntelHDA { + Ok(self.cmd.cmd12(addr, 0xF00, 0x0A)? as u32) + } + ++ fn negotiate_stream_format( ++ &mut self, ++ dac: WidgetAddr, ++ ) -> Result<(&'static super::SampleRate, BitsPerSample, u8)> { ++ let fmt = self.get_supported_formats(dac)?; ++ log::debug!("IHDA: DAC {:01X}:{:02X} supported formats: {:08X}", dac.0, dac.1, fmt); ++ ++ let rate = if fmt & (1 << 14) != 0 { ++ &super::SR_48 ++ } else if fmt & (1 << 13) != 0 { ++ &super::SR_44_1 ++ } else if fmt & (1 << 12) != 0 { ++ &super::SR_32 ++ } else if fmt & (1 << 11) != 0 { ++ &super::SR_22_05 ++ } else if fmt & (1 << 10) != 0 { ++ &super::SR_16 ++ } else if fmt & (1 << 9) != 0 { ++ &super::SR_11_025 ++ } else if fmt & (1 << 8) != 0 { ++ &super::SR_8 ++ } else { ++ log::error!("IHDA: no supported sample rate found in format {:08X}", fmt); ++ return Err(Error::new(ENODEV)); ++ }; ++ ++ let bps = if fmt & (1 << 21) != 0 { ++ BitsPerSample::Bits16 ++ } else if fmt & (1 << 23) != 0 { ++ BitsPerSample::Bits24 ++ } else if fmt & (1 << 24) != 0 { ++ BitsPerSample::Bits32 ++ } else if fmt & (1 << 22) != 0 { ++ BitsPerSample::Bits20 ++ } else if fmt & (1 << 20) != 0 { ++ BitsPerSample::Bits8 ++ } else { ++ log::error!("IHDA: no supported bit depth found in format {:08X}", fmt); ++ return Err(Error::new(ENODEV)); ++ }; ++ ++ let caps = self.get_capabilities(dac)?; ++ let max_channels = ((caps >> 0) & 0xFF) as u8 + 1; ++ let channels = if max_channels >= 2 { 2 } else { max_channels }; ++ ++ Ok((rate, bps, channels)) ++ } ++ + fn get_capabilities(&mut self, addr: WidgetAddr) -> Result { + Ok(self.cmd.cmd12(addr, 0xF00, 0x09)? as u32) + } +@@ -873,13 +1154,98 @@ impl IntelHDA { + //log::trace!("Status: {:02X} Pos: {:08X} Output CTL: {:06X}", output.status(), output.link_position(), output.control()); + + if os.current_block() == (open_block + 3) % NUM_SUB_BUFFS { +- // Block if we already are 3 buffers ahead + Poll::Pending + } else { + Poll::Ready(os.write_block(buf)) + } + } + ++ pub fn configure_input(&mut self) -> Result<()> { ++ let primary_codec = match self.pick_primary_codec_for_output() { ++ Some(c) => c, ++ None => return Err(Error::new(ENODEV)), ++ }; ++ ++ let topo = self.codecs_topology.get(&primary_codec).ok_or_else(|| { ++ log::error!("IHDA: no topology for codec {}", primary_codec); ++ Error::new(ENODEV) ++ })?; ++ ++ let input_pin = topo.input_pins.first().cloned().ok_or_else(|| { ++ log::debug!("IHDA: no input pins found on codec {}", primary_codec); ++ Error::new(ENODEV) ++ })?; ++ ++ let adc = topo.inputs.first().cloned().ok_or_else(|| { ++ log::debug!("IHDA: no ADC widgets found on codec {}", primary_codec); ++ Error::new(ENODEV) ++ })?; ++ ++ log::debug!( ++ "IHDA: configuring input: pin={:01X}:{:02X} adc={:01X}:{:02X}", ++ input_pin.0, input_pin.1, adc.0, adc.1 ++ ); ++ ++ self.cmd.cmd12(input_pin, 0x707, 0xC0)?; ++ self.set_power_state(input_pin, 0)?; ++ self.set_power_state(adc, 0)?; ++ self.set_stream_channel(adc, 2, 0)?; ++ ++ let iss = self.num_input_streams(); ++ if iss == 0 { ++ log::warn!("IHDA: no input streams available"); ++ return Err(Error::new(ENODEV)); ++ } ++ ++ let input_regs = self.get_input_stream_descriptor(0).ok_or_else(|| { ++ log::error!("IHDA: failed to get input stream descriptor 0"); ++ Error::new(ENODEV) ++ })?; ++ ++ let mut input_stream = InputStream::new(NUM_SUB_BUFFS, SUB_BUFF_SIZE, input_regs); ++ ++ for i in 0..NUM_SUB_BUFFS { ++ self.input_buff_desc[i].set_address(input_stream.phys() as u64 + (i * SUB_BUFF_SIZE) as u64); ++ self.input_buff_desc[i].set_length(SUB_BUFF_SIZE as u32); ++ self.input_buff_desc[i].set_interrupt_on_complete(true); ++ } ++ ++ let (rate, bps, channels) = self.negotiate_stream_format(adc)?; ++ ++ input_stream.regs().set_address(self.input_buff_desc.physical()); ++ input_stream.regs().set_pcm_format(rate, bps, channels); ++ input_stream.regs().set_cyclic_buffer_length((NUM_SUB_BUFFS * SUB_BUFF_SIZE) as u32); ++ input_stream.regs().set_stream_number(2); ++ input_stream.regs().set_last_valid_index((NUM_SUB_BUFFS - 1) as u16); ++ input_stream.regs().set_interrupt_on_completion(true); ++ ++ self.set_converter_format(adc, rate, bps, channels)?; ++ ++ self.input_streams.push(input_stream); ++ ++ let input_ref = self.input_streams.last_mut().unwrap(); ++ input_ref.regs().run(); ++ ++ log::debug!("IHDA: input stream 0 configured and running"); ++ Ok(()) ++ } ++ ++ pub fn read_from_input(&mut self, index: usize, buf: &mut [u8]) -> Poll> { ++ let input_stream = match self.input_streams.get_mut(index) { ++ Some(s) => s, ++ None => return Poll::Ready(Err(Error::new(EBADF))), ++ }; ++ ++ let pos = input_stream.regs().link_position() as usize; ++ let hw_block = pos / input_stream.block_size(); ++ ++ if input_stream.current_block() == hw_block { ++ Poll::Pending ++ } else { ++ Poll::Ready(input_stream.read_block(buf)) ++ } ++ } ++ + pub fn handle_interrupts(&mut self) -> bool { + let intsts = self.regs.intsts.read(); + if ((intsts >> 31) & 1) == 1 { +@@ -897,7 +1263,56 @@ impl IntelHDA { + intsts != 0 + } + +- pub fn handle_controller_interrupt(&mut self) {} ++ pub fn handle_controller_interrupt(&mut self) { ++ let statests = self.regs.statests.read(); ++ if statests & STATESTS_MASK != 0 { ++ for i in 0..15 { ++ if (statests >> i) & 1 != 0 { ++ log::info!("IHDA: state change on codec {}", i); ++ } ++ } ++ self.regs.statests.write(statests); ++ } ++ ++ let rirbsts = self.regs.rirbsts.read(); ++ if rirbsts & (RIRBSTS_RINTFL | RIRBSTS_RIRBOIS) != 0 { ++ let rirbwp = self.regs.rirbwp.read(); ++ let wp = rirbwp & 0xFF; ++ if wp != 0 { ++ log::debug!("IHDA: RIRB response available, wp={}", wp); ++ } ++ if rirbsts & RIRBSTS_RIRBOIS != 0 { ++ log::warn!("IHDA: RIRB overrun, clearing"); ++ } ++ self.regs.rirbsts.write(rirbsts & (RIRBSTS_RINTFL | RIRBSTS_RIRBOIS)); ++ } ++ ++ let corbsts = self.regs.corbsts.read(); ++ if corbsts & CORBSTS_CMEI != 0 { ++ log::error!("IHDA: CORB memory error, clearing"); ++ self.regs.corbsts.write(CORBSTS_CMEI); ++ } ++ } ++ ++ fn handle_unsolicited_response(&mut self, codec_addr: CodecAddr, response: u32) { ++ let tag = (response >> 26) & 0xF; ++ let payload = response & 0x03FFFFFF; ++ ++ log::info!( ++ "IHDA: unsolicited response codec {} tag={} payload={:06X}", ++ codec_addr, tag, payload ++ ); ++ ++ if tag == 1 { ++ let pin_widget = payload & 0x7F; ++ let plugged = (payload >> 31) & 1; ++ log::info!( ++ "IHDA: jack sense codec {} pin {} {}", ++ codec_addr, pin_widget, ++ if plugged != 0 { "plugged" } else { "unplugged" } ++ ); ++ } ++ } + + pub fn handle_stream_interrupts(&mut self, sis: u32) { + let iss = self.num_input_streams(); +@@ -1017,9 +1432,10 @@ impl SchemeSync for IntelHDA { + return Err(Error::new(EACCES)); + } + let handle = match path.trim_matches('/') { +- //TODO: allow multiple codecs +- "codec" => Handle::StrBuf(self.dump_codec(0).into_bytes()), +- _ => Handle::Todo, ++ "codec" => Handle::StrBuf(self.dump_all_codecs().into_bytes()), ++ "" | "pcmout" | "pcmout0" => Handle::Pcmout { stream_index: 0 }, ++ "pcmin" | "pcmin0" => Handle::Pcmin { stream_index: 0 }, ++ _ => return Err(Error::new(ENOENT)), + }; + let id = self.handles.lock().insert(handle); + +@@ -1038,18 +1454,44 @@ impl SchemeSync for IntelHDA { + _flags: u32, + _ctx: &CallerCtx, + ) -> Result { +- let handles = self.handles.lock(); +- let Handle::StrBuf(strbuf) = handles.get(id)? else { +- return Err(Error::new(EBADF)); ++ let (is_strbuf, is_pcmin) = { ++ let handles = self.handles.lock(); ++ match handles.get(id)? { ++ Handle::StrBuf(_) => (true, false), ++ Handle::Pcmin { .. } => (false, true), ++ _ => return Err(Error::new(EBADF)), ++ } + }; + +- let src = usize::try_from(offset) +- .ok() +- .and_then(|o| strbuf.get(o..)) +- .unwrap_or(&[]); +- let len = src.len().min(buf.len()); +- buf[..len].copy_from_slice(&src[..len]); +- Ok(len) ++ if is_strbuf { ++ let handles = self.handles.lock(); ++ let Handle::StrBuf(strbuf) = handles.get(id)? else { ++ return Err(Error::new(EBADF)); ++ }; ++ let src = usize::try_from(offset) ++ .ok() ++ .and_then(|o| strbuf.get(o..)) ++ .unwrap_or(&[]); ++ let len = src.len().min(buf.len()); ++ buf[..len].copy_from_slice(&src[..len]); ++ return Ok(len); ++ } ++ ++ if is_pcmin { ++ let index = { ++ let handles = self.handles.lock(); ++ match handles.get(id)? { ++ Handle::Pcmin { stream_index, .. } => *stream_index, ++ _ => return Err(Error::new(EBADF)), ++ } ++ }; ++ return match self.read_from_input(index, buf) { ++ Poll::Ready(r) => r, ++ Poll::Pending => Err(Error::new(EWOULDBLOCK)), ++ }; ++ } ++ ++ Err(Error::new(EBADF)) + } + + fn write( +@@ -1061,23 +1503,29 @@ impl SchemeSync for IntelHDA { + _ctx: &CallerCtx, + ) -> Result { + let index = { +- let mut handles = self.handles.lock(); +- match handles.get_mut(id)? { +- Handle::Todo => 0, ++ let handles = self.handles.lock(); ++ match handles.get(id)? { ++ Handle::Pcmout { stream_index, .. } => *stream_index as u8, + _ => return Err(Error::new(EBADF)), + } + }; + +- //log::debug!("Int count: {}", self.int_counter); +- + match self.write_to_output(index, buf) { + Poll::Ready(r) => r, + Poll::Pending => Err(Error::new(EWOULDBLOCK)), + } + } + +- fn fpath(&mut self, _id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result { +- FpathWriter::with(buf, "audiohw", |_| Ok(())) ++ fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result { ++ let handles = self.handles.lock(); ++ let handle = handles.get(id)?; ++ let path = match handle { ++ Handle::Pcmout { .. } => "audiohw:pcmout0", ++ Handle::Pcmin { .. } => "audiohw:pcmin0", ++ Handle::StrBuf(_) => "audiohw:codec", ++ Handle::SchemeRoot => "audiohw:", ++ }; ++ FpathWriter::with(buf, path, |_| Ok(())) + } + + fn on_close(&mut self, id: usize) { +diff --git a/drivers/audio/ihdad/src/hda/mod.rs b/drivers/audio/ihdad/src/hda/mod.rs +index 7f01daf8..82ba89ae 100644 +--- a/drivers/audio/ihdad/src/hda/mod.rs ++++ b/drivers/audio/ihdad/src/hda/mod.rs +@@ -2,7 +2,11 @@ + pub mod cmdbuff; + pub mod common; + pub mod device; ++pub mod digital; ++pub mod dispatch; ++pub mod fixup; + pub mod node; ++pub mod parser; + pub mod stream; + + pub use self::node::*; +@@ -10,6 +14,8 @@ pub use self::stream::*; + + pub use self::cmdbuff::*; + pub use self::device::IntelHDA; ++pub use self::fixup::FixupEngine; ++pub use self::parser::AutoPinConfig; + pub use self::stream::BitsPerSample; + pub use self::stream::BufferDescriptorListEntry; + pub use self::stream::StreamBuffer; +diff --git a/drivers/audio/ihdad/src/hda/node.rs b/drivers/audio/ihdad/src/hda/node.rs +index 06c5121f..c1f9c31f 100644 +--- a/drivers/audio/ihdad/src/hda/node.rs ++++ b/drivers/audio/ihdad/src/hda/node.rs +@@ -1,7 +1,7 @@ + use super::common::*; + use std::{fmt, mem}; + +-#[derive(Clone)] ++#[derive(Clone, Debug)] + pub struct HDANode { + pub addr: WidgetAddr, + +diff --git a/drivers/audio/ihdad/src/hda/stream.rs b/drivers/audio/ihdad/src/hda/stream.rs +index caa3c364..a3f5ed73 100644 +--- a/drivers/audio/ihdad/src/hda/stream.rs ++++ b/drivers/audio/ihdad/src/hda/stream.rs +@@ -14,9 +14,9 @@ pub enum BaseRate { + } + + pub struct SampleRate { +- base: BaseRate, +- mult: u16, +- div: u16, ++ pub base: BaseRate, ++ pub mult: u16, ++ pub div: u16, + } + + use self::BaseRate::{BR44_1, BR48}; +@@ -78,6 +78,7 @@ pub const SR_192: SampleRate = SampleRate { + div: 1, + }; + ++#[derive(Debug, Clone, Copy)] + #[repr(u8)] + pub enum BitsPerSample { + Bits8 = 0, +@@ -271,6 +272,52 @@ impl OutputStream { + } + } + ++pub struct InputStream { ++ buff: StreamBuffer, ++ desc_regs: &'static mut StreamDescriptorRegs, ++} ++ ++impl InputStream { ++ pub fn new( ++ block_count: usize, ++ block_length: usize, ++ regs: &'static mut StreamDescriptorRegs, ++ ) -> InputStream { ++ InputStream { ++ buff: StreamBuffer::new(block_length, block_count).unwrap(), ++ desc_regs: regs, ++ } ++ } ++ ++ pub fn read_block(&mut self, buf: &mut [u8]) -> Result { ++ self.buff.read_block(buf) ++ } ++ ++ pub fn block_size(&self) -> usize { ++ self.buff.block_size() ++ } ++ ++ pub fn block_count(&self) -> usize { ++ self.buff.block_count() ++ } ++ ++ pub fn current_block(&self) -> usize { ++ self.buff.current_block() ++ } ++ ++ pub fn addr(&self) -> usize { ++ self.buff.addr() ++ } ++ ++ pub fn phys(&self) -> usize { ++ self.buff.phys() ++ } ++ ++ pub fn regs(&mut self) -> &mut StreamDescriptorRegs { ++ self.desc_regs ++ } ++} ++ + #[repr(C, packed)] + pub struct BufferDescriptorListEntry { + addr_low: Mmio, +@@ -379,6 +426,20 @@ impl StreamBuffer { + + Ok(len) + } ++ ++ pub fn read_block(&mut self, buf: &mut [u8]) -> Result { ++ let len = min(self.block_size(), buf.len()); ++ unsafe { ++ copy_nonoverlapping( ++ (self.addr() + self.current_block() * self.block_size()) as *const u8, ++ buf.as_mut_ptr(), ++ len, ++ ); ++ } ++ self.cur_pos += 1; ++ self.cur_pos %= self.block_count(); ++ Ok(len) ++ } + } + impl Drop for StreamBuffer { + fn drop(&mut self) { 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 @@ -6478,52 +7636,56 @@ index 5bf2be91..20d755d2 100644 let offscreen_ptr = self.ptr.as_ptr() as *mut u32; let onscreen_ptr = framebuffer.onscreen as *mut u32; // FIXME use as_mut_ptr once stable diff --git a/drivers/graphics/virtio-gpud/src/main.rs b/drivers/graphics/virtio-gpud/src/main.rs -index b27f4c56..0f1a9e4d 100644 +index b27f4c56..f3514c8e 100644 --- a/drivers/graphics/virtio-gpud/src/main.rs +++ b/drivers/graphics/virtio-gpud/src/main.rs -@@ -482,7 +482,10 @@ fn main() { +@@ -482,8 +482,11 @@ fn main() { } fn daemon_runner(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! { - deamon(daemon, pcid_handle).unwrap(); -+ if let Err(err) = deamon(daemon, pcid_handle) { -+ log::error!("virtio-gpud: startup failed: {err}"); +- unreachable!(); ++ deamon(daemon, pcid_handle).unwrap_or_else(|err| { ++ log::error!("virtio-gpud: daemon failed: {err}"); + std::process::exit(1); -+ } - unreachable!(); ++ }); ++ std::process::exit(0); } -@@ -500,7 +503,12 @@ fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow: + fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow::Result<()> { +@@ -500,7 +503,10 @@ fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow: // 0x1050 - virtio-gpu let pci_config = pcid_handle.config(); - assert_eq!(pci_config.func.full_device_id.device_id, 0x1050); + if pci_config.func.full_device_id.device_id != 0x1050 { -+ return Err(anyhow::anyhow!( -+ "unexpected virtio-gpu device id: {:04x}", -+ pci_config.func.full_device_id.device_id -+ )); ++ log::error!("virtio-gpud: unexpected device ID {:#06x}, expected 0x1050", pci_config.func.full_device_id.device_id); ++ std::process::exit(1); + } log::info!("virtio-gpu: initiating startup sequence :^)"); let device = DEVICE.try_call_once(|| virtio_core::probe_device(&mut pcid_handle))?; -@@ -530,8 +538,8 @@ fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow: - // Needs to be before GpuScheme::new to avoid a deadlock due to initnsmgr blocking on +@@ -531,7 +537,10 @@ fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow: // /scheme/event as it is already blocked on opening /scheme/display.virtio-gpu. // FIXME change the initnsmgr to not block on openat for the target scheme. -- let event_queue: EventQueue = + let event_queue: EventQueue = - EventQueue::new().expect("virtio-gpud: failed to create event queue"); -+ let event_queue: EventQueue = EventQueue::new() -+ .map_err(|err| anyhow::anyhow!("failed to create event queue: {err}"))?; ++ EventQueue::new().unwrap_or_else(|err| { ++ log::error!("virtio-gpud: failed to create event queue: {err}"); ++ std::process::exit(1); ++ }); let mut scheme = scheme::GpuScheme::new( config, -@@ -556,33 +564,40 @@ fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow: +@@ -556,33 +565,48 @@ fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow: Source::Input, event::EventFlags::READ, ) - .unwrap(); -+ .map_err(|err| anyhow::anyhow!("virtio-gpud: failed to subscribe to input events: {err}"))?; ++ .unwrap_or_else(|err| { ++ log::error!("virtio-gpud: failed to subscribe to input events: {err}"); ++ std::process::exit(1); ++ }); event_queue .subscribe( scheme.event_handle().raw(), @@ -6531,9 +7693,10 @@ index b27f4c56..0f1a9e4d 100644 event::EventFlags::READ, ) - .unwrap(); -+ .map_err(|err| { -+ anyhow::anyhow!("virtio-gpud: failed to subscribe to scheme events: {err}") -+ })?; ++ .unwrap_or_else(|err| { ++ log::error!("virtio-gpud: failed to subscribe to scheme events: {err}"); ++ std::process::exit(1); ++ }); event_queue .subscribe( device.irq_handle.as_raw_fd() as usize, @@ -6541,22 +7704,23 @@ index b27f4c56..0f1a9e4d 100644 event::EventFlags::READ, ) - .unwrap(); -+ .map_err(|err| { -+ anyhow::anyhow!("virtio-gpud: failed to subscribe to interrupt events: {err}") -+ })?; ++ .unwrap_or_else(|err| { ++ log::error!("virtio-gpud: failed to subscribe to interrupt events: {err}"); ++ std::process::exit(1); ++ }); let all = [Source::Input, Source::Scheme, Source::Interrupt]; -- for event in all -- .into_iter() + for event in all + .into_iter() - .chain(event_queue.map(|e| e.expect("virtio-gpud: failed to get next event").user_data)) -- { -+ for event in all.into_iter().chain(event_queue.filter_map(|e| match e { -+ Ok(ev) => Some(ev.user_data), -+ Err(err) => { -+ log::error!("virtio-gpud: failed to get next event: {err}"); -+ None -+ } -+ })) { ++ .chain(event_queue.filter_map(|e| match e { ++ Ok(ev) => Some(ev.user_data), ++ Err(err) => { ++ log::error!("virtio-gpud: failed to get next event: {err}"); ++ None ++ } ++ })) + { match event { Source::Input => scheme.handle_vt_events(), Source::Scheme => { @@ -6569,7 +7733,7 @@ index b27f4c56..0f1a9e4d 100644 } Source::Interrupt => loop { let before_gen = device.transport.config_generation(); -@@ -591,7 +606,11 @@ fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow: +@@ -591,7 +615,11 @@ fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow: if events & VIRTIO_GPU_EVENT_DISPLAY != 0 { let (adapter, objects) = scheme.adapter_and_kms_objects_mut(); @@ -6583,65 +7747,59 @@ index b27f4c56..0f1a9e4d 100644 adapter.probe_connector(objects, connector_id); } diff --git a/drivers/graphics/virtio-gpud/src/scheme.rs b/drivers/graphics/virtio-gpud/src/scheme.rs -index 22a985ee..075502a2 100644 +index 22a985ee..c60facfd 100644 --- a/drivers/graphics/virtio-gpud/src/scheme.rs +++ b/drivers/graphics/virtio-gpud/src/scheme.rs -@@ -10,7 +10,7 @@ use drm_sys::{ - DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT, - }; +@@ -64,10 +64,23 @@ impl DrmBuffer for VirtGpuFramebuffer<'_> { --use syscall::{EINVAL, PAGE_SIZE}; -+use syscall::{EIO, EINVAL, PAGE_SIZE}; - - use virtio_core::spec::{Buffer, ChainBuilder, DescriptorFlags}; - use virtio_core::transport::{Error, Queue, Transport}; -@@ -65,9 +65,21 @@ impl DrmBuffer for VirtGpuFramebuffer<'_> { impl Drop for VirtGpuFramebuffer<'_> { fn drop(&mut self) { - futures::executor::block_on(async { +- futures::executor::block_on(async { - let request = Dma::new(ResourceUnref::new(self.id)).unwrap(); -+ let request = match Dma::new(ResourceUnref::new(self.id)) { -+ Ok(r) => r, -+ Err(err) => { -+ log::error!("virtio-gpud: failed to allocate DMA for resource unref: {err}"); -+ return; -+ } -+ }; ++ let request = match Dma::new(ResourceUnref::new(self.id)) { ++ Ok(r) => r, ++ Err(err) => { ++ log::error!("virtio-gpud: failed to allocate unref request DMA: {err}"); ++ return; ++ } ++ }; ++ ++ let header = match Dma::new(ControlHeader::default()) { ++ Ok(h) => h, ++ Err(err) => { ++ log::error!("virtio-gpud: failed to allocate unref header DMA: {err}"); ++ return; ++ } ++ }; - let header = Dma::new(ControlHeader::default()).unwrap(); -+ let header = match Dma::new(ControlHeader::default()) { -+ Ok(h) => h, -+ Err(err) => { -+ log::error!("virtio-gpud: failed to allocate DMA for unref header: {err}"); -+ return; -+ } -+ }; ++ futures::executor::block_on(async { let command = ChainBuilder::new() .chain(Buffer::new(&request)) .chain(Buffer::new(&header).flags(DescriptorFlags::WRITE_ONLY)) -@@ -182,7 +194,9 @@ impl VirtGpuAdapter<'_> { +@@ -182,7 +195,9 @@ impl VirtGpuAdapter<'_> { .build(); self.control_queue.send(command).await; - assert!(response.header.ty == CommandTy::RespOkDisplayInfo); + if response.header.ty != CommandTy::RespOkDisplayInfo { -+ return Err(Error::QueueSetup("unexpected response type for display info")); ++ return Err(Error::Probe("unexpected display info response type")); + } Ok(response) } -@@ -197,7 +211,9 @@ impl VirtGpuAdapter<'_> { +@@ -197,7 +212,9 @@ impl VirtGpuAdapter<'_> { .build(); self.control_queue.send(command).await; - assert!(response.header.ty == CommandTy::RespOkEdid); + if response.header.ty != CommandTy::RespOkEdid { -+ return Err(Error::QueueSetup("unexpected response type for EDID")); ++ return Err(Error::Probe("unexpected EDID response type")); + } Ok(response) } -@@ -212,7 +228,7 @@ impl VirtGpuAdapter<'_> { +@@ -212,7 +229,7 @@ impl VirtGpuAdapter<'_> { ) { //Transfering cursor resource to host futures::executor::block_on(async { @@ -6650,7 +7808,7 @@ index 22a985ee..075502a2 100644 cursor.id, GpuRect { x: 0, -@@ -221,14 +237,38 @@ impl VirtGpuAdapter<'_> { +@@ -221,14 +238,33 @@ impl VirtGpuAdapter<'_> { height: 64, }, 0, @@ -6661,24 +7819,19 @@ index 22a985ee..075502a2 100644 + )) { + Ok(r) => r, + Err(err) => { -+ log::error!( -+ "virtio-gpud: failed to allocate DMA for cursor transfer: {err}" -+ ); ++ log::error!("virtio-gpud: failed to allocate cursor transfer DMA: {err}"); + return; + } + }; + let header = match self.send_request_fenced(transfer_request).await { + Ok(h) => h, + Err(err) => { -+ log::error!("virtio-gpud: failed to send cursor transfer request: {err}"); ++ log::error!("virtio-gpud: failed to send cursor transfer: {err}"); + return; + } + }; + if header.ty != CommandTy::RespOkNodata { -+ log::error!( -+ "virtio-gpud: unexpected response for cursor transfer: {:?}", -+ header.ty -+ ); ++ log::error!("virtio-gpud: cursor transfer returned {:?}", header.ty); + } }); @@ -6687,14 +7840,14 @@ index 22a985ee..075502a2 100644 + let request = match Dma::new(UpdateCursor::update_cursor(x, y, hot_x, hot_y, cursor.id)) { + Ok(r) => r, + Err(err) => { -+ log::error!("virtio-gpud: failed to allocate DMA for cursor update: {err}"); ++ log::error!("virtio-gpud: failed to allocate cursor update DMA: {err}"); + return; + } + }; futures::executor::block_on(async { let command = ChainBuilder::new().chain(Buffer::new(&request)).build(); self.cursor_queue.send(command).await; -@@ -236,7 +276,13 @@ impl VirtGpuAdapter<'_> { +@@ -236,7 +272,13 @@ impl VirtGpuAdapter<'_> { } fn move_cursor(&mut self, x: i32, y: i32) { @@ -6702,38 +7855,35 @@ index 22a985ee..075502a2 100644 + let request = match Dma::new(MoveCursor::move_cursor(x, y)) { + Ok(r) => r, + Err(err) => { -+ log::error!("virtio-gpud: failed to allocate DMA for cursor move: {err}"); ++ log::error!("virtio-gpud: failed to allocate move cursor DMA: {err}"); + return; + } + }; futures::executor::block_on(async { let command = ChainBuilder::new().chain(Buffer::new(&request)).build(); -@@ -246,7 +292,10 @@ impl VirtGpuAdapter<'_> { +@@ -246,7 +288,7 @@ impl VirtGpuAdapter<'_> { fn disable_cursor(&mut self) { if self.hidden_cursor.is_none() { - let (width, height) = self.hw_cursor_size().unwrap(); -+ let Some((width, height)) = self.hw_cursor_size() else { -+ log::error!("virtio-gpud: failed to get hardware cursor size"); -+ return; -+ }; ++ let (width, height) = self.hw_cursor_size().unwrap_or((64, 64)); let (cursor, stride) = self.create_dumb_buffer(width, height); unsafe { core::ptr::write_bytes( -@@ -257,8 +306,9 @@ impl VirtGpuAdapter<'_> { +@@ -257,7 +299,10 @@ impl VirtGpuAdapter<'_> { } self.hidden_cursor = Some(Arc::new(cursor)); } - let hidden_cursor = self.hidden_cursor.as_ref().unwrap().clone(); -- -+ let Some(hidden_cursor) = self.hidden_cursor.clone() else { -+ return; -+ }; ++ let hidden_cursor = self.hidden_cursor.as_ref().unwrap_or_else(|| { ++ log::error!("virtio-gpud: hidden_cursor missing after initialization"); ++ std::process::exit(1); ++ }).clone(); + self.update_cursor(&hidden_cursor, 0, 0, 0, 0); } - } -@@ -280,7 +330,9 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { +@@ -280,7 +325,9 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { fn init(&mut self, objects: &mut KmsObjects) { futures::executor::block_on(async { @@ -6744,49 +7894,34 @@ index 22a985ee..075502a2 100644 }); for display_id in 0..self.config.num_scanouts.get() { -@@ -310,7 +362,19 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { +@@ -310,7 +357,13 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { fn probe_connector(&mut self, objects: &mut KmsObjects, id: KmsObjectId) { futures::executor::block_on(async { - let mut connector = objects.get_connector(id).unwrap().lock().unwrap(); + let mut connector = match objects.get_connector(id) { -+ Ok(c) => match c.lock() { -+ Ok(guard) => guard, -+ Err(err) => { -+ log::error!("virtio-gpud: failed to lock connector: {err}"); -+ return; -+ } -+ }, -+ Err(e) => { -+ log::error!("virtio-gpud: connector not found: {e}"); ++ Ok(c) => c.lock().unwrap(), ++ Err(err) => { ++ log::error!("virtio-gpud: connector {:?} not found: {}", id, err); + return; + } + }; let display = &self.displays[connector.driver_data.display_id as usize]; connector.connection = if display.enabled { -@@ -325,7 +389,19 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { +@@ -325,7 +378,10 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { drop(connector); let blob = objects.add_blob(display.edid.clone()); - objects.get_connector(id).unwrap().lock().unwrap().edid = blob; + match objects.get_connector(id) { -+ Ok(c) => match c.lock() { -+ Ok(mut guard) => guard.edid = blob, -+ Err(err) => { -+ log::error!( -+ "virtio-gpud: failed to lock connector for EDID update: {err}" -+ ); -+ } -+ }, -+ Err(e) => { -+ log::error!("virtio-gpud: connector not found for EDID update: {e}"); -+ } ++ Ok(c) => c.lock().unwrap().edid = blob, ++ Err(err) => log::error!("virtio-gpud: connector {:?} not found on second access: {}", id, err), + } } else { connector.update_from_size(display.width, display.height); } -@@ -336,7 +412,13 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { +@@ -336,7 +392,13 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { futures::executor::block_on(async { let bpp = 32; let fb_size = width as usize * height as usize * bpp / 8; @@ -6794,14 +7929,14 @@ index 22a985ee..075502a2 100644 + let sgl = match sgl::Sgl::new(fb_size) { + Ok(s) => s, + Err(err) => { -+ log::error!("virtio-gpud: failed to allocate SGL for dumb buffer: {err}"); ++ log::error!("virtio-gpud: failed to allocate SGL: {err}"); + std::process::exit(1); + } + }; unsafe { core::ptr::write_bytes(sgl.as_ptr() as *mut u8, 255, fb_size); -@@ -345,22 +427,61 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { +@@ -345,22 +407,43 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { let res_id = ResourceId::alloc(); // Create a host resource using `VIRTIO_GPU_CMD_RESOURCE_CREATE_2D`. @@ -6816,34 +7951,23 @@ index 22a985ee..075502a2 100644 + )) { + Ok(r) => r, + Err(err) => { -+ log::error!("virtio-gpud: failed to allocate DMA for resource create: {err}"); -+ return ( -+ VirtGpuFramebuffer { -+ queue: self.control_queue.clone(), -+ id: res_id, -+ sgl, -+ width, -+ height, -+ }, -+ width * 4, -+ ); ++ log::error!("virtio-gpud: failed to allocate create 2d DMA: {err}"); ++ std::process::exit(1); + } + }; - let header = self.send_request(request).await.unwrap(); - assert_eq!(header.ty, CommandTy::RespOkNodata); -+ match self.send_request(request).await { -+ Ok(header) => { -+ if header.ty != CommandTy::RespOkNodata { -+ log::error!( -+ "virtio-gpud: unexpected response for resource create: {:?}", -+ header.ty -+ ); -+ } -+ } ++ let header = match self.send_request(request).await { ++ Ok(h) => h, + Err(err) => { -+ log::error!("virtio-gpud: failed to send resource create request: {err}"); ++ log::error!("virtio-gpud: failed to send create 2d: {err}"); ++ std::process::exit(1); + } ++ }; ++ if header.ty != CommandTy::RespOkNodata { ++ log::error!("virtio-gpud: create 2d returned {:?}", header.ty); ++ std::process::exit(1); + } // Use the allocated framebuffer from the guest ram, and attach it as backing @@ -6851,117 +7975,54 @@ index 22a985ee..075502a2 100644 - let mut mem_entries = - unsafe { Dma::zeroed_slice(sgl.chunks().len()).unwrap().assume_init() }; -+ let mut mem_entries = match unsafe { Dma::zeroed_slice(sgl.chunks().len()) } { -+ Ok(entries) => unsafe { entries.assume_init() }, -+ Err(err) => { -+ log::error!("virtio-gpud: failed to allocate DMA for memory entries: {err}"); -+ return ( -+ VirtGpuFramebuffer { -+ queue: self.control_queue.clone(), -+ id: res_id, -+ sgl, -+ width, -+ height, -+ }, -+ width * 4, -+ ); ++ let mut mem_entries = unsafe { ++ match Dma::zeroed_slice(sgl.chunks().len()) { ++ Ok(dma) => dma.assume_init(), ++ Err(err) => { ++ log::error!("virtio-gpud: failed to allocate mem entries DMA: {err}"); ++ std::process::exit(1); ++ } + } + }; for (entry, chunk) in mem_entries.iter_mut().zip(sgl.chunks().iter()) { *entry = MemEntry { address: chunk.phys as u64, -@@ -369,9 +490,43 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { +@@ -369,9 +452,20 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { }; } - let attach_request = - Dma::new(AttachBacking::new(res_id, mem_entries.len() as u32)).unwrap(); - let header = Dma::new(ControlHeader::default()).unwrap(); -+ let attach_request = match Dma::new(AttachBacking::new( -+ res_id, -+ mem_entries.len() as u32, -+ )) { ++ let attach_request = match Dma::new(AttachBacking::new(res_id, mem_entries.len() as u32)) { + Ok(r) => r, + Err(err) => { -+ log::error!( -+ "virtio-gpud: failed to allocate DMA for attach request: {err}" -+ ); -+ return ( -+ VirtGpuFramebuffer { -+ queue: self.control_queue.clone(), -+ id: res_id, -+ sgl, -+ width, -+ height, -+ }, -+ width * 4, -+ ); ++ log::error!("virtio-gpud: failed to allocate attach backing DMA: {err}"); ++ std::process::exit(1); + } + }; + let header = match Dma::new(ControlHeader::default()) { + Ok(h) => h, + Err(err) => { -+ log::error!("virtio-gpud: failed to allocate DMA for attach header: {err}"); -+ return ( -+ VirtGpuFramebuffer { -+ queue: self.control_queue.clone(), -+ id: res_id, -+ sgl, -+ width, -+ height, -+ }, -+ width * 4, -+ ); ++ log::error!("virtio-gpud: failed to allocate attach header DMA: {err}"); ++ std::process::exit(1); + } + }; let command = ChainBuilder::new() .chain(Buffer::new(&attach_request)) .chain(Buffer::new_unsized(&mem_entries)) -@@ -379,7 +534,12 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { +@@ -379,7 +473,9 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { .build(); self.control_queue.send(command).await; - assert_eq!(header.ty, CommandTy::RespOkNodata); + if header.ty != CommandTy::RespOkNodata { -+ log::error!( -+ "virtio-gpud: unexpected response for attach backing: {:?}", -+ header.ty -+ ); ++ log::error!("virtio-gpud: attach backing returned {:?}", header.ty); + } ( VirtGpuFramebuffer { -@@ -410,7 +570,13 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { - damage: Damage, - ) -> syscall::Result<()> { - futures::executor::block_on(async { -- let mut crtc = crtc.lock().unwrap(); -+ let mut crtc = match crtc.lock() { -+ Ok(guard) => guard, -+ Err(err) => { -+ log::error!("virtio-gpud: crtc mutex poisoned: {err}"); -+ return Err(syscall::Error::new(EIO)); -+ } -+ }; - let framebuffer = state - .fb_id - .map(|fb_id| objects.get_framebuffer(fb_id)) -@@ -418,7 +584,13 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { - crtc.state = state; - - for connector in objects.connectors() { -- let connector = connector.lock().unwrap(); -+ let connector = match connector.lock() { -+ Ok(guard) => guard, -+ Err(err) => { -+ log::error!("virtio-gpud: connector mutex poisoned: {err}"); -+ continue; -+ } -+ }; - - if connector.state.crtc_id != objects.crtc_ids()[crtc.crtc_index as usize] { - continue; -@@ -427,19 +599,40 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { +@@ -427,19 +523,27 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { let display_id = connector.driver_data.display_id; let Some(framebuffer) = framebuffer else { @@ -6977,27 +8038,14 @@ index 22a985ee..075502a2 100644 + )) { + Ok(r) => r, + Err(err) => { -+ log::error!( -+ "virtio-gpud: failed to allocate DMA for scanout: {err}" -+ ); -+ return Err(syscall::Error::new(EIO)); ++ log::error!("virtio-gpud: failed to allocate scanout clear DMA: {err}"); ++ return Ok(()); + } + }; -+ let header = match self.send_request(scanout_request).await { -+ Ok(h) => h, -+ Err(err) => { -+ log::error!( -+ "virtio-gpud: failed to send scanout request: {err}" -+ ); -+ return Err(syscall::Error::new(EIO)); -+ } -+ }; -+ if header.ty != CommandTy::RespOkNodata { -+ log::error!( -+ "virtio-gpud: unexpected response for scanout: {:?}", -+ header.ty -+ ); -+ return Err(syscall::Error::new(EIO)); ++ match self.send_request(scanout_request).await { ++ Ok(header) if header.ty == CommandTy::RespOkNodata => {} ++ Ok(header) => log::error!("virtio-gpud: scanout clear returned {:?}", header.ty), ++ Err(err) => log::error!("virtio-gpud: failed to send scanout clear: {err}"), + } self.displays[display_id as usize].active_resource = None; return Ok(()); @@ -7008,7 +8056,7 @@ index 22a985ee..075502a2 100644 framebuffer.buffer.id, GpuRect { x: 0, -@@ -448,22 +641,61 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { +@@ -448,22 +552,38 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { height: framebuffer.height, }, 0, @@ -7019,29 +8067,18 @@ index 22a985ee..075502a2 100644 + )) { + Ok(r) => r, + Err(err) => { -+ log::error!("virtio-gpud: failed to allocate DMA for transfer: {err}"); -+ return Err(syscall::Error::new(EIO)); ++ log::error!("virtio-gpud: failed to allocate xfer DMA: {err}"); ++ return Ok(()); + } + }; -+ let header = match self.send_request(req).await { -+ Ok(h) => h, -+ Err(err) => { -+ log::error!("virtio-gpud: failed to send transfer request: {err}"); -+ return Err(syscall::Error::new(EIO)); -+ } -+ }; -+ if header.ty != CommandTy::RespOkNodata { -+ log::error!( -+ "virtio-gpud: unexpected response for transfer: {:?}", -+ header.ty -+ ); -+ return Err(syscall::Error::new(EIO)); ++ match self.send_request(req).await { ++ Ok(header) if header.ty == CommandTy::RespOkNodata => {} ++ Ok(header) => log::error!("virtio-gpud: xfer returned {:?}", header.ty), ++ Err(err) => log::error!("virtio-gpud: failed to send xfer: {err}"), + } // FIXME once we support resizing we also need to check that the current and target size match -- if self.displays[display_id as usize].active_resource != Some(framebuffer.buffer.id) -+ if self.displays[display_id as usize].active_resource -+ != Some(framebuffer.buffer.id) + if self.displays[display_id as usize].active_resource != Some(framebuffer.buffer.id) { - let scanout_request = Dma::new(SetScanout::new( + let scanout_request = match Dma::new(SetScanout::new( @@ -7055,32 +8092,19 @@ index 22a985ee..075502a2 100644 + )) { + Ok(r) => r, + Err(err) => { -+ log::error!( -+ "virtio-gpud: failed to allocate DMA for scanout: {err}" -+ ); -+ return Err(syscall::Error::new(EIO)); ++ log::error!("virtio-gpud: failed to allocate scanout DMA: {err}"); ++ return Ok(()); + } + }; -+ let header = match self.send_request(scanout_request).await { -+ Ok(h) => h, -+ Err(err) => { -+ log::error!( -+ "virtio-gpud: failed to send scanout request: {err}" -+ ); -+ return Err(syscall::Error::new(EIO)); -+ } -+ }; -+ if header.ty != CommandTy::RespOkNodata { -+ log::error!( -+ "virtio-gpud: unexpected response for scanout: {:?}", -+ header.ty -+ ); -+ return Err(syscall::Error::new(EIO)); ++ match self.send_request(scanout_request).await { ++ Ok(header) if header.ty == CommandTy::RespOkNodata => {} ++ Ok(header) => log::error!("virtio-gpud: scanout returned {:?}", header.ty), ++ Err(err) => log::error!("virtio-gpud: failed to send scanout: {err}"), + } self.displays[display_id as usize].active_resource = Some(framebuffer.buffer.id); } -@@ -472,8 +704,27 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { +@@ -472,8 +592,18 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { framebuffer.buffer.id, damage.clip(framebuffer.width, framebuffer.height).into(), ); @@ -7089,23 +8113,14 @@ index 22a985ee..075502a2 100644 + let flush_dma = match Dma::new(flush) { + Ok(d) => d, + Err(err) => { -+ log::error!("virtio-gpud: failed to allocate DMA for flush: {err}"); -+ return Err(syscall::Error::new(EIO)); ++ log::error!("virtio-gpud: failed to allocate flush DMA: {err}"); ++ return Ok(()); + } + }; -+ let header = match self.send_request(flush_dma).await { -+ Ok(h) => h, -+ Err(err) => { -+ log::error!("virtio-gpud: failed to send flush request: {err}"); -+ return Err(syscall::Error::new(EIO)); -+ } -+ }; -+ if header.ty != CommandTy::RespOkNodata { -+ log::error!( -+ "virtio-gpud: unexpected response for flush: {:?}", -+ header.ty -+ ); -+ return Err(syscall::Error::new(EIO)); ++ match self.send_request(flush_dma).await { ++ Ok(header) if header.ty == CommandTy::RespOkNodata => {} ++ Ok(header) => log::error!("virtio-gpud: flush returned {:?}", header.ty), ++ Err(err) => log::error!("virtio-gpud: failed to send flush: {err}"), + } } @@ -7277,6 +8292,379 @@ index 79360e34..4a2b9469 100644 daemon.ready(); +diff --git a/drivers/input/ps2d/src/controller.rs b/drivers/input/ps2d/src/controller.rs +index d7af4cba..638b7cc1 100644 +--- a/drivers/input/ps2d/src/controller.rs ++++ b/drivers/input/ps2d/src/controller.rs +@@ -97,6 +97,14 @@ enum KeyboardCommandData { + const DEFAULT_TIMEOUT: u64 = 50_000; + // Reset timeout in microseconds + const RESET_TIMEOUT: u64 = 1_000_000; ++// Maximum bytes to drain during flush (Linux: I8042_BUFFER_SIZE) ++const FLUSH_LIMIT: usize = 4096; ++// Controller self-test pass value (Linux: I8042_RET_CTL_TEST) ++const SELFTEST_PASS: u8 = 0x55; ++// Controller self-test retries (Linux: 5 attempts) ++const SELFTEST_RETRIES: usize = 5; ++// AUX port test pass value (Linux returns 0x00 on success) ++const AUX_TEST_PASS: u8 = 0x00; + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub struct Ps2 { +@@ -271,6 +279,50 @@ impl Ps2 { + } + } + ++ /// Drain all pending bytes from the controller output buffer. ++ /// Borrowed from Linux i8042_flush(): stale firmware/BIOS bytes can be ++ /// misinterpreted as device responses during initialization. ++ fn flush(&mut self) -> usize { ++ let mut count = 0; ++ while self.status().contains(StatusFlags::OUTPUT_FULL) { ++ if count >= FLUSH_LIMIT { ++ warn!("flush: exceeded limit, controller may be stuck"); ++ break; ++ } ++ let data = self.data.read(); ++ trace!("flush: discarded {:02X}", data); ++ count += 1; ++ } ++ if count > 0 { ++ debug!("flushed {} stale bytes from controller", count); ++ } ++ count ++ } ++ ++ /// Test the AUX (mouse) port via controller command 0xA9. ++ /// Borrowed from Linux: verifies electrical connectivity before ++ /// attempting to talk to the mouse. Returns true if the port passed. ++ fn test_aux_port(&mut self) -> bool { ++ if let Err(err) = self.command(Command::TestSecond) { ++ warn!("aux port test command failed: {:?}", err); ++ return false; ++ } ++ match self.read() { ++ Ok(AUX_TEST_PASS) => { ++ debug!("aux port test passed"); ++ true ++ } ++ Ok(val) => { ++ warn!("aux port test failed: {:02X}", val); ++ false ++ } ++ Err(err) => { ++ warn!("aux port test read timeout: {:?}", err); ++ false ++ } ++ } ++ } ++ + pub fn init_keyboard(&mut self) -> Result<(), Error> { + let mut b; + +@@ -308,66 +360,125 @@ impl Ps2 { + } + + pub fn init(&mut self) -> Result<(), Error> { ++ // Linux i8042_controller_check(): verify controller is present by ++ // flushing any stale data. A stuck output buffer means no controller. ++ self.flush(); ++ ++ // Bare-metal controllers may be slow after firmware handoff. ++ // Give the controller a moment to finish POST before sending commands. ++ std::thread::sleep(std::time::Duration::from_millis(50)); ++ + { +- // Disable devices +- self.command(Command::DisableFirst)?; +- self.command(Command::DisableSecond)?; ++ // Disable both ports first — use retry because the controller ++ // may still be settling or temporarily unresponsive. ++ // Failure here is non-fatal: we continue and attempt the rest ++ // of initialization. A truly absent controller will fail later ++ // at self-test or keyboard reset. ++ if let Err(err) = self.retry( ++ format_args!("disable first port"), ++ 3, ++ |x| x.command(Command::DisableFirst), ++ ) { ++ warn!("disable first port failed: {:?}", err); ++ } ++ if let Err(err) = self.retry( ++ format_args!("disable second port"), ++ 3, ++ |x| x.command(Command::DisableSecond), ++ ) { ++ warn!("disable second port failed: {:?}", err); ++ } + } + +- // Disable clocks, disable interrupts, and disable translate ++ // Flush again after disabling — firmware may have queued more bytes ++ self.flush(); ++ ++ // Linux i8042_controller_init() step 1: write a known-safe config ++ // (interrupts off, both ports disabled) so stale config can't cause ++ // spurious interrupts during the rest of init. + { +- // Since the default config may have interrupts enabled, and the kernel may eat up +- // our data in that case, we will write a config without reading the current one + let config = ConfigFlags::POST_PASSED + | ConfigFlags::FIRST_DISABLED + | ConfigFlags::SECOND_DISABLED; + self.set_config(config)?; + } + +- // The keyboard seems to still collect bytes even when we disable +- // the port, so we must disable the keyboard too ++ // Linux i8042_controller_selftest(): retry up to 5 times with delay. ++ // "On some really fragile systems this does not take the first time." ++ { ++ let mut passed = false; ++ for attempt in 0..SELFTEST_RETRIES { ++ if let Err(err) = self.command(Command::TestController) { ++ warn!("self-test command failed (attempt {}): {:?}", attempt + 1, err); ++ continue; ++ } ++ match self.read() { ++ Ok(SELFTEST_PASS) => { ++ passed = true; ++ break; ++ } ++ Ok(val) => { ++ warn!( ++ "self-test unexpected value {:02X} (attempt {}/{})", ++ val, ++ attempt + 1, ++ SELFTEST_RETRIES ++ ); ++ } ++ Err(err) => { ++ warn!("self-test read timeout (attempt {}): {:?}", attempt + 1, err); ++ } ++ } ++ // Linux: msleep(50) between retries ++ std::thread::sleep(std::time::Duration::from_millis(50)); ++ } ++ if !passed { ++ // Linux on x86: "giving up on controller selftest, continuing anyway" ++ warn!("controller self-test did not pass after {} attempts, continuing", SELFTEST_RETRIES); ++ } ++ } ++ ++ // Flush any bytes the self-test may have left behind ++ self.flush(); ++ ++ // Linux i8042_controller_init() step 2: set keyboard defaults ++ // (disable scanning so keyboard doesn't send scancodes during init) + self.retry(format_args!("keyboard defaults"), 4, |x| { +- // Set defaults and disable scanning + let b = x.keyboard_command(KeyboardCommand::SetDefaultsDisable)?; + if b != 0xFA { + error!("keyboard failed to set defaults: {:02X}", b); + return Err(Error::CommandRetry); + } +- + Ok(b) + })?; + +- { +- // Perform the self test +- self.command(Command::TestController)?; +- let r = self.read()?; +- if r != 0x55 { +- warn!("self test unexpected value: {:02X}", r); +- } +- } +- + // Initialize keyboard + if let Err(err) = self.init_keyboard() { + error!("failed to initialize keyboard: {:?}", err); + return Err(err); + } + +- // Enable second device +- let enable_mouse = match self.command(Command::EnableSecond) { +- Ok(()) => true, +- Err(err) => { +- error!("failed to initialize mouse: {:?}", err); +- false ++ // Linux: test AUX port (command 0xA9) before enabling. ++ // Skips mouse init entirely if the port is not electrically present. ++ let aux_ok = self.test_aux_port(); ++ ++ // Enable second device (mouse) only if AUX port tested OK ++ let enable_mouse = if aux_ok { ++ match self.command(Command::EnableSecond) { ++ Ok(()) => true, ++ Err(err) => { ++ warn!("failed to enable aux port after test passed: {:?}", err); ++ false ++ } + } ++ } else { ++ info!("skipping mouse init: aux port test did not pass"); ++ false + }; + + { +- // Enable keyboard data reporting +- // Use inner function to prevent retries +- // Response is ignored since scanning is now on + if let Err(err) = self.keyboard_command_inner(KeyboardCommand::EnableReporting as u8) { + error!("failed to initialize keyboard reporting: {:?}", err); +- //TODO: fix by using interrupts? + } + } + +diff --git a/drivers/input/ps2d/src/mouse.rs b/drivers/input/ps2d/src/mouse.rs +index 9e95ab88..8087c8c4 100644 +--- a/drivers/input/ps2d/src/mouse.rs ++++ b/drivers/input/ps2d/src/mouse.rs +@@ -5,6 +5,11 @@ pub const RESET_RETRIES: usize = 10; + pub const RESET_TIMEOUT: Duration = Duration::from_millis(1000); + pub const COMMAND_TIMEOUT: Duration = Duration::from_millis(100); + ++const CMD_ACK: u8 = 0xFA; ++const CMD_RESEND: u8 = 0xFE; ++const BAT_COMPLETE: u8 = 0xAA; ++const BAT_FAIL: u8 = 0xFC; ++ + #[derive(Clone, Copy, Debug)] + #[repr(u8)] + #[allow(dead_code)] +@@ -58,9 +63,11 @@ impl MouseTx { + + fn handle(&mut self, data: u8, ps2: &mut Ps2) -> Result { + if self.write_i < self.write.len() { +- if data == 0xFA { ++ if data == CMD_ACK { + self.write_i += 1; + self.try_write(ps2)?; ++ } else if data == CMD_RESEND { ++ self.try_write(ps2)?; + } else { + log::error!("unknown mouse response {:02X}", data); + return Err(()); +@@ -251,25 +258,43 @@ impl MouseState { + MouseResult::None + } + MouseState::Reset => { +- if data == 0xFA { +- log::debug!("mouse reset ok"); ++ if data == CMD_ACK { ++ log::debug!("mouse reset ack"); + MouseResult::Timeout(RESET_TIMEOUT) +- } else if data == 0xAA { ++ } else if data == BAT_COMPLETE { + log::debug!("BAT completed"); + *self = MouseState::Bat; + MouseResult::Timeout(COMMAND_TIMEOUT) ++ } else if data == CMD_RESEND { ++ // Device asks us to resend the reset command (0xFF). ++ // Resend WITHOUT incrementing the retry counter — 0xFE is ++ // a normal protocol response, not a failure. ++ log::debug!("mouse requests resend during reset, resending 0xFF"); ++ match ps2.mouse_command_async(MouseCommand::Reset as u8) { ++ Ok(()) => MouseResult::Timeout(RESET_TIMEOUT), ++ Err(err) => { ++ log::error!("failed to resend mouse reset: {:?}", err); ++ self.reset(ps2) ++ } ++ } ++ } else if data == BAT_FAIL { ++ log::warn!("mouse BAT failed (0xFC)"); ++ self.reset(ps2) + } else { + log::warn!("unknown mouse response {:02X} after reset", data); + self.reset(ps2) + } + } + MouseState::Bat => { +- if data == MouseId::Base as u8 { +- // Enable intellimouse features ++ if data == CMD_RESEND { ++ // 0xFE after BAT is unusual — the device may be re-issuing ++ // BAT. Wait for the next byte (device ID or another BAT). ++ log::debug!("mouse resend (0xFE) during BAT, waiting"); ++ MouseResult::Timeout(COMMAND_TIMEOUT) ++ } else if data == MouseId::Base as u8 { + log::debug!("BAT mouse id {:02X} (base)", data); + self.identify_touchpad(ps2) + } else if data == MouseId::Intellimouse1 as u8 { +- // Extra packet already enabled + log::debug!("BAT mouse id {:02X} (intellimouse)", data); + self.enable_reporting(data, ps2) + } else { +@@ -320,10 +345,17 @@ impl MouseState { + } + } + MouseState::DeviceId => { +- if data == 0xFA { +- // Command OK response +- //TODO: handle this separately? ++ if data == CMD_ACK { + MouseResult::Timeout(COMMAND_TIMEOUT) ++ } else if data == CMD_RESEND { ++ log::debug!("mouse resend during DeviceId, resending GetDeviceId"); ++ match ps2.mouse_command_async(MouseCommand::GetDeviceId as u8) { ++ Ok(()) => MouseResult::Timeout(COMMAND_TIMEOUT), ++ Err(err) => { ++ log::error!("failed to resend GetDeviceId: {:?}", err); ++ self.reset(ps2) ++ } ++ } + } else if data == MouseId::Base as u8 || data == MouseId::Intellimouse1 as u8 { + log::debug!("mouse id {:02X}", data); + self.enable_reporting(data, ps2) +@@ -333,10 +365,28 @@ impl MouseState { + } + } + MouseState::EnableReporting { id } => { +- log::debug!("mouse id {:02X} enable reporting {:02X}", id, data); +- //TODO: handle response ok/error +- *self = MouseState::Streaming { id }; +- MouseResult::None ++ if data == CMD_ACK { ++ log::debug!("mouse id {:02X} reporting enabled", id); ++ *self = MouseState::Streaming { id }; ++ MouseResult::None ++ } else if data == CMD_RESEND { ++ log::debug!("mouse resend during EnableReporting, resending 0xF4"); ++ match ps2.mouse_command_async(MouseCommand::EnableReporting as u8) { ++ Ok(()) => MouseResult::Timeout(COMMAND_TIMEOUT), ++ Err(err) => { ++ log::error!("failed to resend EnableReporting: {:?}", err); ++ *self = MouseState::Streaming { id }; ++ MouseResult::None ++ } ++ } ++ } else { ++ log::warn!( ++ "unexpected mouse response {:02X} during enable reporting, streaming anyway", ++ data ++ ); ++ *self = MouseState::Streaming { id }; ++ MouseResult::None ++ } + } + MouseState::Streaming { id } => { + MouseResult::Packet(data, id == MouseId::Intellimouse1 as u8) +diff --git a/drivers/input/ps2d/src/state.rs b/drivers/input/ps2d/src/state.rs +index 9018dc6b..9e37b937 100644 +--- a/drivers/input/ps2d/src/state.rs ++++ b/drivers/input/ps2d/src/state.rs +@@ -61,7 +61,9 @@ pub struct Ps2d { + impl Ps2d { + pub fn new(input: ProducerHandle, time_file: File) -> Self { + let mut ps2 = Ps2::new(); +- ps2.init().expect("failed to initialize"); ++ if let Err(err) = ps2.init() { ++ log::error!("ps2d: controller init failed: {:?}", err); ++ } + + // FIXME add an option for orbital to disable this when an app captures the mouse. + let vmmouse_relative = false; diff --git a/drivers/inputd/src/lib.rs b/drivers/inputd/src/lib.rs index b68e8211..0627f301 100644 --- a/drivers/inputd/src/lib.rs @@ -7494,76 +8882,95 @@ index 07aa943e..61641b9f 100644 } } else { common::setup_logging( +diff --git a/drivers/net/e1000d/src/device.rs b/drivers/net/e1000d/src/device.rs +index 4c518f30..0e42d72b 100644 +--- a/drivers/net/e1000d/src/device.rs ++++ b/drivers/net/e1000d/src/device.rs +@@ -3,7 +3,7 @@ use std::{cmp, mem, ptr, slice, thread, time}; + + use driver_network::NetworkAdapter; + +-use syscall::error::Result; ++use syscall::error::{Error, Result, EIO}; + + use common::dma::Dma; + +@@ -207,11 +207,10 @@ impl NetworkAdapter for Intel8254x { + } + + fn dma_array() -> Result<[Dma; N]> { +- Ok((0..N) ++ let vec: Vec> = (0..N) + .map(|_| Ok(unsafe { Dma::zeroed()?.assume_init() })) +- .collect::>>()? +- .try_into() +- .unwrap_or_else(|_| unreachable!())) ++ .collect::>>()?; ++ vec.try_into().map_err(|_| Error::new(EIO)) + } + impl Intel8254x { + pub unsafe fn new(base: usize) -> Result { diff --git a/drivers/net/e1000d/src/main.rs b/drivers/net/e1000d/src/main.rs -index 373ea9b3..d971c0a1 100644 +index 373ea9b3..8ff57b33 100644 --- a/drivers/net/e1000d/src/main.rs +++ b/drivers/net/e1000d/src/main.rs -@@ -28,17 +28,38 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { - let irq = pci_config - .func - .legacy_interrupt_line +@@ -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"); -+ .unwrap_or_else(|| { ++ let irq = match pci_config.func.legacy_interrupt_line { ++ Some(irq) => irq, ++ None => { + log::error!("e1000d: no legacy interrupts supported"); -+ std::process::exit(1); -+ }); ++ process::exit(1); ++ } ++ }; log::info!("E1000 {}", pci_config.func.display()); -- let mut irq_file = irq.irq_handle("e1000d"); -+ let mut irq_file = match irq.try_irq_handle("e1000d") { -+ Ok(file) => file, -+ Err(err) => { -+ log::error!("e1000d: failed to open IRQ handle: {err}"); -+ std::process::exit(1); -+ } -+ }; - -- let address = unsafe { pcid_handle.map_bar(0) }.ptr.as_ptr() as usize; -+ let address = match unsafe { pcid_handle.try_map_bar(0) } { -+ Ok(bar) => bar.ptr.as_ptr() as usize, -+ Err(err) => { -+ log::error!("e1000d: failed to map BAR0: {err}"); -+ std::process::exit(1); -+ } -+ }; +@@ -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") -+ match device::Intel8254x::new(address) { -+ Ok(device) => device, -+ Err(err) => { -+ log::error!("e1000d: failed to allocate device: {err}"); -+ std::process::exit(1); -+ } -+ } ++ 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 +72,13 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { +@@ -51,7 +58,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { } } - let event_queue = EventQueue::::new().expect("e1000d: failed to create event queue"); -+ let event_queue = match EventQueue::::new() { -+ Ok(queue) => queue, -+ Err(err) => { -+ log::error!("e1000d: failed to create event queue: {err}"); -+ std::process::exit(1); -+ } -+ }; ++ let mut event_queue = EventQueue::::new().unwrap_or_else(|err| { ++ log::error!("e1000d: failed to create event queue: {err}"); ++ process::exit(1); ++ }); event_queue .subscribe( -@@ -59,32 +86,65 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { +@@ -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}"); -+ std::process::exit(1); ++ process::exit(1); + }); event_queue .subscribe( @@ -7574,132 +8981,298 @@ index 373ea9b3..d971c0a1 100644 - .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}"); -+ std::process::exit(1); ++ process::exit(1); + }); + -+ if let Err(err) = libredox::call::setrens(0, 0) { ++ libredox::call::setrens(0, 0).unwrap_or_else(|err| { + log::error!("e1000d: failed to enter null namespace: {err}"); -+ std::process::exit(1); -+ } - -- scheme.tick().unwrap(); ++ process::exit(1); ++ }); ++ + if let Err(err) = scheme.tick() { + log::error!("e1000d: failed initial scheme tick: {err}"); -+ std::process::exit(1); ++ process::exit(1); + } - for event in event_queue.map(|e| e.expect("e1000d: failed to get event")) { -+ for event in event_queue { -+ let event = match event { -+ Ok(event) => event, -+ Err(err) => { ++ loop { ++ let event = match event_queue.next() { ++ Some(Ok(event)) => event, ++ Some(Err(err)) => { + log::error!("e1000d: failed to get event: {err}"); -+ break; ++ 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 file: {err}"); -+ break; ++ 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 acknowledge IRQ: {err}"); -+ break; ++ log::error!("e1000d: failed to write IRQ: {err}"); ++ continue; + } + + if let Err(err) = scheme.tick() { + log::error!("e1000d: failed to handle IRQ: {err}"); -+ break; + } + } + } + Source::Scheme => { + if let Err(err) = scheme.tick() { + log::error!("e1000d: failed to handle scheme op: {err}"); -+ break; } } - Source::Scheme => scheme.tick().expect("e1000d: failed to handle scheme op"), } } - unreachable!() -+ std::process::exit(1) ++ ++ process::exit(0); } +diff --git a/drivers/net/ixgbed/Cargo.toml b/drivers/net/ixgbed/Cargo.toml +index d97ff398..fcaf4b19 100644 +--- a/drivers/net/ixgbed/Cargo.toml ++++ b/drivers/net/ixgbed/Cargo.toml +@@ -7,6 +7,7 @@ edition = "2021" + [dependencies] + bitflags.workspace = true + libredox.workspace = true ++log.workspace = true + redox_event.workspace = true + redox_syscall.workspace = true + +diff --git a/drivers/net/ixgbed/src/device.rs b/drivers/net/ixgbed/src/device.rs +index 0d59b46d..fc7c009f 100644 +--- a/drivers/net/ixgbed/src/device.rs ++++ b/drivers/net/ixgbed/src/device.rs +@@ -3,7 +3,7 @@ use std::time::{Duration, Instant}; + use std::{cmp, mem, ptr, slice, thread}; + + use driver_network::NetworkAdapter; +-use syscall::error::Result; ++use syscall::error::{Error, Result, EIO}; + + use common::dma::Dma; + +@@ -45,7 +45,12 @@ impl NetworkAdapter for Intel8259x { + + if (status & IXGBE_RXDADV_STAT_DD) != 0 { + if (status & IXGBE_RXDADV_STAT_EOP) == 0 { +- panic!("increase buffer size or decrease MTU") ++ log::error!("ixgbed: received fragmented packet, skipping descriptor"); ++ desc.read.pkt_addr = self.receive_buffer[self.receive_index].physical() as u64; ++ desc.read.hdr_addr = 0; ++ self.write_reg(IXGBE_RDT(0), self.receive_index as u32); ++ self.receive_index = wrap_ring(self.receive_index, self.receive_ring.len()); ++ return Ok(None); + } + + let data = unsafe { +@@ -132,13 +137,25 @@ impl Intel8259x { + .map(|_| Ok(unsafe { Dma::zeroed()?.assume_init() })) + .collect::>>()? + .try_into() +- .unwrap_or_else(|_| unreachable!()), ++ .map_err(|v: Vec<_>| { ++ log::error!( ++ "ixgbed: internal error: DMA buffer array conversion failed (got {} items, expected 32)", ++ v.len() ++ ); ++ Error::new(EIO) ++ })?, + receive_ring: unsafe { Dma::zeroed()?.assume_init() }, + transmit_buffer: (0..32) + .map(|_| Ok(unsafe { Dma::zeroed()?.assume_init() })) + .collect::>>()? + .try_into() +- .unwrap_or_else(|_| unreachable!()), ++ .map_err(|v: Vec<_>| { ++ log::error!( ++ "ixgbed: internal error: DMA buffer array conversion failed (got {} items, expected 32)", ++ v.len() ++ ); ++ Error::new(EIO) ++ })?, + receive_index: 0, + transmit_ring: unsafe { Dma::zeroed()?.assume_init() }, + transmit_ring_free: 32, +@@ -166,7 +183,7 @@ impl Intel8259x { + + if (status & IXGBE_RXDADV_STAT_DD) != 0 { + if (status & IXGBE_RXDADV_STAT_EOP) == 0 { +- panic!("increase buffer size or decrease MTU") ++ log::error!("ixgbed: received fragmented packet, buffer too small"); + } + + return unsafe { desc.wb.upper.length as usize }; +@@ -205,13 +222,8 @@ impl Intel8259x { + self.mac_address = mac; + } + +- /// Returns the register at `self.base` + `register`. +- /// +- /// # Panics +- /// +- /// Panics if `self.base` + `register` does not belong to the mapped memory of the PCIe device. + fn read_reg(&self, register: u32) -> u32 { +- assert!( ++ debug_assert!( + register as usize <= self.size - 4 as usize, + "MMIO access out of bounds" + ); +@@ -219,13 +231,8 @@ impl Intel8259x { + unsafe { ptr::read_volatile((self.base + register as usize) as *mut u32) } + } + +- /// Sets the register at `self.base` + `register`. +- /// +- /// # Panics +- /// +- /// Panics if `self.base` + `register` does not belong to the mapped memory of the PCIe device. + fn write_reg(&self, register: u32, data: u32) -> u32 { +- assert!( ++ debug_assert!( + register as usize <= self.size - 4 as usize, + "MMIO access out of bounds" + ); +@@ -279,7 +286,7 @@ impl Intel8259x { + + let mac = self.get_mac_addr(); + +- println!( ++ log::info!( + " - MAC: {:>02X}:{:>02X}:{:>02X}:{:>02X}:{:>02X}:{:>02X}", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] + ); +@@ -438,13 +445,11 @@ impl Intel8259x { + } + + /// Sets the rx queues` descriptors and enables the queues. +- /// +- /// # Panics +- /// Panics if length of `self.receive_ring` is not a power of 2. + fn start_rx_queue(&mut self, queue_id: u16) { +- if self.receive_ring.len() & (self.receive_ring.len() - 1) != 0 { +- panic!("number of receive queue entries must be a power of 2"); +- } ++ debug_assert!( ++ self.receive_ring.len() & (self.receive_ring.len() - 1) == 0, ++ "number of receive queue entries must be a power of 2" ++ ); + + for i in 0..self.receive_ring.len() { + self.receive_ring[i].read.pkt_addr = self.receive_buffer[i].physical() as u64; +@@ -466,13 +471,11 @@ impl Intel8259x { + } + + /// Enables the tx queues. +- /// +- /// # Panics +- /// Panics if length of `self.transmit_ring` is not a power of 2. + fn start_tx_queue(&mut self, queue_id: u16) { +- if self.transmit_ring.len() & (self.transmit_ring.len() - 1) != 0 { +- panic!("number of receive queue entries must be a power of 2"); +- } ++ debug_assert!( ++ self.transmit_ring.len() & (self.transmit_ring.len() - 1) == 0, ++ "number of transmit queue entries must be a power of 2" ++ ); + + for i in 0..self.transmit_ring.len() { + self.transmit_ring[i].read.buffer_addr = self.transmit_buffer[i].physical() as u64; +@@ -506,14 +509,14 @@ impl Intel8259x { + + /// Waits for the link to come up. + fn wait_for_link(&self) { +- println!(" - waiting for link"); ++ log::info!(" - waiting for link"); + let time = Instant::now(); + let mut speed = self.get_link_speed(); + while speed == 0 && time.elapsed().as_secs() < 10 { + thread::sleep(Duration::from_millis(100)); + speed = self.get_link_speed(); + } +- println!(" - link speed is {} Mbit/s", self.get_link_speed()); ++ log::info!(" - link speed is {} Mbit/s", self.get_link_speed()); + } + + /// Enables or disables promisc mode of this device. diff --git a/drivers/net/ixgbed/src/main.rs b/drivers/net/ixgbed/src/main.rs -index 4a6ce74d..a9b6dd82 100644 +index 4a6ce74d..855d339d 100644 --- a/drivers/net/ixgbed/src/main.rs +++ b/drivers/net/ixgbed/src/main.rs -@@ -22,20 +22,44 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { - let irq = pci_config - .func - .legacy_interrupt_line +@@ -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"); -+ .unwrap_or_else(|| { -+ eprintln!("ixgbed: no legacy interrupts supported"); -+ std::process::exit(1); -+ }); - - println!(" + IXGBE {}", pci_config.func.display()); - -- let mut irq_file = irq.irq_handle("ixgbed"); -+ let mut irq_file = match irq.try_irq_handle("ixgbed") { -+ Ok(file) => file, -+ Err(err) => { -+ eprintln!("ixgbed: failed to open IRQ handle: {err}"); -+ std::process::exit(1); ++ 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); + } + }; -- let mapped_bar = unsafe { pcid_handle.map_bar(0) }; -+ if let Err(err) = pci_config.func.bars[0].try_mem() { -+ eprintln!("ixgbed: invalid BAR0: {err}"); -+ std::process::exit(1); -+ } -+ let mapped_bar = match unsafe { pcid_handle.try_map_bar(0) } { -+ Ok(bar) => bar, -+ Err(err) => { -+ eprintln!("ixgbed: failed to map BAR0: {err}"); -+ std::process::exit(1); -+ } -+ }; - let address = mapped_bar.ptr.as_ptr(); - let size = mapped_bar.bar_size; +- 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") -+ match device::Intel8259x::new(address as usize, size) { -+ Ok(device) => device, -+ Err(err) => { -+ eprintln!("ixgbed: failed to allocate device: {err}"); -+ std::process::exit(1); -+ } -+ } ++ 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 +72,78 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { +@@ -48,41 +62,77 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { } } - let event_queue = EventQueue::::new().expect("ixgbed: Could not create event queue."); -+ let event_queue = match EventQueue::::new() { -+ Ok(queue) => queue, -+ Err(err) => { -+ eprintln!("ixgbed: could not create event queue: {err}"); -+ std::process::exit(1); -+ } -+ }; ++ let mut event_queue = EventQueue::::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, @@ -7708,8 +9281,8 @@ index 4a6ce74d..a9b6dd82 100644 ) - .unwrap(); + .unwrap_or_else(|err| { -+ eprintln!("ixgbed: failed to subscribe IRQ fd: {err}"); -+ std::process::exit(1); ++ log::error!("ixgbed: failed to subscribe to IRQ fd: {err}"); ++ process::exit(1); + }); event_queue .subscribe( @@ -7721,258 +9294,453 @@ index 4a6ce74d..a9b6dd82 100644 - - libredox::call::setrens(0, 0).expect("ixgbed: failed to enter null namespace"); + .unwrap_or_else(|err| { -+ eprintln!("ixgbed: failed to subscribe scheme fd: {err}"); -+ std::process::exit(1); ++ log::error!("ixgbed: failed to subscribe to scheme fd: {err}"); ++ process::exit(1); + }); + -+ if let Err(err) = libredox::call::setrens(0, 0) { -+ eprintln!("ixgbed: failed to enter null namespace: {err}"); -+ std::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(); -+ if let Err(err) = scheme.tick() { -+ eprintln!("ixgbed: failed initial scheme tick: {err}"); -+ std::process::exit(1); -+ } ++ 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")) { -+ for event in event_queue { -+ let event = match event { -+ Ok(event) => event, -+ Err(err) => { -+ eprintln!("ixgbed: failed to get next event: {err}"); -+ 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) { -+ eprintln!("ixgbed: failed to read IRQ file: {err}"); -+ break; ++ 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) { -+ eprintln!("ixgbed: failed to acknowledge IRQ: {err}"); -+ break; ++ log::error!("ixgbed: failed to write IRQ: {err}"); ++ continue; + } + + if let Err(err) = scheme.tick() { -+ eprintln!("ixgbed: failed to handle IRQ: {err}"); -+ break; ++ log::error!("ixgbed: failed to handle IRQ: {err}"); + } } } Source::Scheme => { - scheme.tick().unwrap(); + if let Err(err) = scheme.tick() { -+ eprintln!("ixgbed: failed to handle scheme op: {err}"); -+ break; ++ log::error!("ixgbed: failed to handle scheme op: {err}"); + } } } } - unreachable!() -+ std::process::exit(0) ++ ++ process::exit(0); } +diff --git a/drivers/net/rtl8139d/src/device.rs b/drivers/net/rtl8139d/src/device.rs +index 37167ee2..d7428132 100644 +--- a/drivers/net/rtl8139d/src/device.rs ++++ b/drivers/net/rtl8139d/src/device.rs +@@ -215,7 +215,7 @@ impl Rtl8139 { + .map(|_| Ok(Dma::zeroed()?.assume_init())) + .collect::>>()? + .try_into() +- .unwrap_or_else(|_| unreachable!()), ++ .map_err(|_| Error::new(EIO))?, + transmit_i: 0, + mac_address: [0; 6], + }; diff --git a/drivers/net/rtl8139d/src/main.rs b/drivers/net/rtl8139d/src/main.rs -index d470e814..aa377446 100644 +index d470e814..64335a23 100644 --- a/drivers/net/rtl8139d/src/main.rs +++ b/drivers/net/rtl8139d/src/main.rs -@@ -3,7 +3,7 @@ use std::os::unix::io::AsRawFd; +@@ -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}; --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 device; -@@ -20,19 +20,19 @@ where - } - } - --fn map_bar(pcid_handle: &mut PciFunctionHandle) -> *mut u8 { -+fn map_bar(pcid_handle: &mut PciFunctionHandle) -> Result<*mut u8, &'static str> { - let config = pcid_handle.config(); - - // RTL8139 uses BAR2, RTL8169 uses BAR1, search in that order - for &barnum in &[2, 1] { - match config.func.bars[usize::from(barnum)] { - pcid_interface::PciBar::Memory32 { .. } | pcid_interface::PciBar::Memory64 { .. } => unsafe { -- return pcid_handle.map_bar(barnum).ptr.as_ptr(); -+ return Ok(pcid_handle.map_bar(barnum).ptr.as_ptr()); - }, +@@ -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"); -+ Err("failed to find a usable MMIO BAR") ++ log::error!("rtl8139d: failed to find BAR"); ++ process::exit(1); } fn main() { -@@ -55,13 +55,31 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { - - log::info!(" + RTL8139 {}", pci_config.func.display()); - -- let bar = map_bar(&mut pcid_handle); -+ let bar = match map_bar(&mut pcid_handle) { -+ Ok(bar) => bar, -+ Err(err) => { -+ log::error!("rtl8139d: {err}"); -+ std::process::exit(1); -+ } -+ }; - -- let irq_file = pci_allocate_interrupt_vector(&mut pcid_handle, "rtl8139d"); -+ let irq_file = match try_pci_allocate_interrupt_vector(&mut pcid_handle, "rtl8139d") { -+ Ok(irq) => irq, -+ Err(err) => { -+ log::error!("rtl8139d: failed to allocate interrupt vector: {err}"); -+ std::process::exit(1); -+ } -+ }; +@@ -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") -+ match device::Rtl8139::new(bar as usize) { -+ Ok(device) => device, -+ Err(err) => { -+ log::error!("rtl8139d: failed to allocate device: {err}"); -+ std::process::exit(1); -+ } -+ } ++ 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::::new().expect("rtl8139d: Could not create event queue."); ++ let mut event_queue = EventQueue::::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/device.rs b/drivers/net/rtl8168d/src/device.rs +index ae545ec4..7229a52d 100644 +--- a/drivers/net/rtl8168d/src/device.rs ++++ b/drivers/net/rtl8168d/src/device.rs +@@ -177,7 +177,7 @@ impl Rtl8168 { + .map(|_| Ok(Dma::zeroed()?.assume_init())) + .collect::>>()? + .try_into() +- .unwrap_or_else(|_| unreachable!()), ++ .map_err(|_| Error::new(EIO))?, + + receive_ring: Dma::zeroed()?.assume_init(), + receive_i: 0, +@@ -185,7 +185,7 @@ impl Rtl8168 { + .map(|_| Ok(Dma::zeroed()?.assume_init())) + .collect::>>()? + .try_into() +- .unwrap_or_else(|_| unreachable!()), ++ .map_err(|_| Error::new(EIO))?, + transmit_ring: Dma::zeroed()?.assume_init(), + transmit_i: 0, + transmit_buffer_h: [Dma::zeroed()?.assume_init()], diff --git a/drivers/net/rtl8168d/src/main.rs b/drivers/net/rtl8168d/src/main.rs -index 1d9963a3..c6e21bd9 100644 +index 1d9963a3..bd2fcb1a 100644 --- a/drivers/net/rtl8168d/src/main.rs +++ b/drivers/net/rtl8168d/src/main.rs -@@ -3,7 +3,7 @@ use std::os::unix::io::AsRawFd; +@@ -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}; --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 device; -@@ -20,19 +20,19 @@ where - } - } - --fn map_bar(pcid_handle: &mut PciFunctionHandle) -> *mut u8 { -+fn map_bar(pcid_handle: &mut PciFunctionHandle) -> Result<*mut u8, &'static str> { - let config = pcid_handle.config(); - - // RTL8168 uses BAR2, RTL8169 uses BAR1, search in that order - for &barnum in &[2, 1] { - match config.func.bars[usize::from(barnum)] { - pcid_interface::PciBar::Memory32 { .. } | pcid_interface::PciBar::Memory64 { .. } => unsafe { -- return pcid_handle.map_bar(barnum).ptr.as_ptr(); -+ return Ok(pcid_handle.map_bar(barnum).ptr.as_ptr()); - }, +@@ -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"); -+ Err("failed to find a usable MMIO BAR") ++ log::error!("rtl8168d: failed to find BAR"); ++ process::exit(1); } fn main() { -@@ -55,13 +55,31 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { - - log::info!("RTL8168 {}", pci_config.func.display()); - -- let bar = map_bar(&mut pcid_handle); -+ let bar = match map_bar(&mut pcid_handle) { -+ Ok(bar) => bar, -+ Err(err) => { -+ log::error!("rtl8168d: {err}"); -+ std::process::exit(1); -+ } -+ }; - -- let irq_file = pci_allocate_interrupt_vector(&mut pcid_handle, "rtl8168d"); -+ let irq_file = match try_pci_allocate_interrupt_vector(&mut pcid_handle, "rtl8168d") { -+ Ok(irq) => irq, -+ Err(err) => { -+ log::error!("rtl8168d: failed to allocate interrupt vector: {err}"); -+ std::process::exit(1); -+ } -+ }; +@@ -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") -+ match device::Rtl8168::new(bar as usize) { -+ Ok(device) => device, -+ Err(err) => { -+ log::error!("rtl8168d: failed to allocate device: {err}"); -+ std::process::exit(1); -+ } -+ } ++ 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::::new().expect("rtl8168d: Could not create event queue."); ++ let mut event_queue = EventQueue::::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..56f2c045 100644 +index 17d168ef..adbd1086 100644 --- a/drivers/net/virtio-netd/src/main.rs +++ b/drivers/net/virtio-netd/src/main.rs -@@ -31,7 +31,10 @@ fn main() { +@@ -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(); -+ if let Err(err) = deamon(daemon, pcid_handle) { -+ log::error!("virtio-netd: startup failed: {err}"); -+ std::process::exit(1); -+ } - unreachable!(); +- unreachable!(); ++ deamon(daemon, pcid_handle).unwrap_or_else(|err| { ++ log::error!("virtio-netd: daemon failed: {err}"); ++ process::exit(1); ++ }); ++ process::exit(0); } -@@ -52,7 +55,13 @@ fn deamon( + 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 { -+ return Err(format!( -+ "unexpected virtio-net device id: {:04x}", -+ pci_config.func.full_device_id.device_id -+ ) -+ .into()); ++ 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 +93,7 @@ fn deamon( +@@ -84,7 +91,8 @@ fn deamon( device.transport.ack_driver_feature(VIRTIO_NET_F_MAC); mac } else { - unimplemented!() -+ return Err("virtio-net: device does not expose VIRTIO_NET_F_MAC".into()); ++ 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,7 +135,7 @@ fn deamon( +@@ -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)?; ++ libredox::call::setrens(0, 0).unwrap_or_else(|err| { ++ log::error!("virtio-netd: failed to enter null namespace: {err}"); ++ process::exit(1); ++ }); - scheme.tick()?; +- 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::()])?; // Wait for event +- scheme.tick()?; ++ if let Err(err) = event_queue.read(&mut [0; mem::size_of::()]) { ++ 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}"); ++ } + } + } +diff --git a/drivers/net/virtio-netd/src/scheme.rs b/drivers/net/virtio-netd/src/scheme.rs +index 59b3b93e..d0acb2ba 100644 +--- a/drivers/net/virtio-netd/src/scheme.rs ++++ b/drivers/net/virtio-netd/src/scheme.rs +@@ -27,11 +27,16 @@ impl<'a> VirtioNet<'a> { + // Populate all of the `rx_queue` with buffers to maximize performence. + let mut rx_buffers = vec![]; + for i in 0..(rx.descriptor_len() as usize) { +- rx_buffers.push(unsafe { +- Dma::<[u8]>::zeroed_slice(MAX_BUFFER_LEN) +- .unwrap() +- .assume_init() +- }); ++ let buf = unsafe { ++ match Dma::<[u8]>::zeroed_slice(MAX_BUFFER_LEN) { ++ Ok(dma) => dma.assume_init(), ++ Err(err) => { ++ log::error!("virtio-netd: failed to allocate rx buffer: {err}"); ++ continue; ++ } ++ } ++ }; ++ rx_buffers.push(buf); + + let chain = ChainBuilder::new() + .chain(Buffer::new_unsized(&rx_buffers[i]).flags(DescriptorFlags::WRITE_ONLY)) diff --git a/drivers/pcid-spawner/src/main.rs b/drivers/pcid-spawner/src/main.rs -index a968f4d4..a39b2af8 100644 +index a968f4d4..bfff05c3 100644 --- a/drivers/pcid-spawner/src/main.rs +++ b/drivers/pcid-spawner/src/main.rs @@ -1,11 +1,40 @@ @@ -8051,13 +9819,11 @@ index a968f4d4..a39b2af8 100644 continue; }; -@@ -85,16 +121,93 @@ fn main() -> Result<()> { +@@ -85,16 +121,105 @@ fn main() -> Result<()> { let mut command = Command::new(program); command.args(args); - log::info!("pcid-spawner: spawn {:?}", command); -- -- handle.enable_device(); + log::info!( + "pcid-spawner: matched {} to driver {:?}", + device_addr, @@ -8074,6 +9840,19 @@ index a968f4d4..a39b2af8 100644 + continue; + } +- handle.enable_device(); ++ let irq_info = handle.config().func.legacy_interrupt_line; ++ let irq_desc = match irq_info { ++ Some(irq) => format!("INTx#{irq}"), ++ None => "MSI/MSI-X only".to_string(), ++ }; ++ log::info!( ++ "pcid-spawner: {} enabled (interrupt: {}, driver: {:?})", ++ device_addr, ++ irq_desc, ++ driver.command, ++ ); + let channel_fd = handle.into_inner_fd(); command.env("PCID_CLIENT_CHANNEL", channel_fd.to_string()); @@ -8150,8 +9929,166 @@ index a968f4d4..a39b2af8 100644 } Ok(()) +diff --git a/drivers/pcid/src/cfg_access/fallback.rs b/drivers/pcid/src/cfg_access/fallback.rs +index 671d17f7..ea8f69f8 100644 +--- a/drivers/pcid/src/cfg_access/fallback.rs ++++ b/drivers/pcid/src/cfg_access/fallback.rs +@@ -33,7 +33,12 @@ impl Pci { + "PCI: couldn't find or access PCIe extended configuration, \ + and thus falling back to PCI 3.0 io ports" + ); +- common::acquire_port_io_rights().expect("pcid: failed to get IO port rights"); ++ common::acquire_port_io_rights() ++ .map_err(|err| { ++ log::error!("pcid: failed to get IO port rights: {err}"); ++ err ++ }) ++ .expect("pcid: IO port rights required for PCI 3.0 fallback"); + } + }); + } +@@ -61,8 +66,9 @@ impl ConfigRegionAccess for Pci { + + Self::set_iopl(); + +- let offset = +- u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space"); ++ let Ok(offset) = u8::try_from(offset) else { ++ return 0xFFFFFFFF; ++ }; + let address = Self::address(address, offset); + + Pio::::new(0xCF8).write(address); +@@ -74,8 +80,9 @@ impl ConfigRegionAccess for Pci { + + Self::set_iopl(); + +- let offset = +- u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space"); ++ let Ok(offset) = u8::try_from(offset) else { ++ return; ++ }; + let address = Self::address(address, offset); + + Pio::::new(0xCF8).write(address); +diff --git a/drivers/pcid/src/cfg_access/mod.rs b/drivers/pcid/src/cfg_access/mod.rs +index c2552448..0fe215a6 100644 +--- a/drivers/pcid/src/cfg_access/mod.rs ++++ b/drivers/pcid/src/cfg_access/mod.rs +@@ -38,42 +38,57 @@ fn locate_ecam_dtb( + ) + })?; + +- let address = node.reg().unwrap().next().unwrap().starting_address as u64; ++ let mut reg = node.reg().ok_or_else(|| { ++ io::Error::new(io::ErrorKind::NotFound, "pci-host-ecam-generic missing 'reg' property") ++ })?; ++ let address = reg.next().ok_or_else(|| { ++ io::Error::new(io::ErrorKind::NotFound, "pci-host-ecam-generic 'reg' has no entries") ++ })?.starting_address as u64; + +- let bus_range = node.property("bus-range").unwrap(); +- assert_eq!(bus_range.value.len(), 8); +- let start_bus = u32::from_be_bytes(<[u8; 4]>::try_from(&bus_range.value[0..4]).unwrap()); +- let end_bus = u32::from_be_bytes(<[u8; 4]>::try_from(&bus_range.value[4..8]).unwrap()); ++ let bus_range = node.property("bus-range").ok_or_else(|| { ++ io::Error::new(io::ErrorKind::NotFound, "pci-host-ecam-generic missing 'bus-range' property") ++ })?; ++ if bus_range.value.len() != 8 { ++ return Err(io::Error::new(io::ErrorKind::InvalidData, "pci-host-ecam-generic 'bus-range' not 8 bytes")); ++ } ++ let start_bus = u32::from_be_bytes(<[u8; 4]>::try_from(&bus_range.value[0..4]).map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "bus-range start parse failed"))?); ++ let end_bus = u32::from_be_bytes(<[u8; 4]>::try_from(&bus_range.value[4..8]).map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "bus-range end parse failed"))?); + +- // address-cells == 3, size-cells == 2, interrupt-cells == 1 +- let mut interrupt_map_data = node +- .property("interrupt-map") +- .unwrap() ++ let interrupt_map_prop = node.property("interrupt-map").ok_or_else(|| { ++ io::Error::new(io::ErrorKind::NotFound, "pci-host-ecam-generic missing 'interrupt-map' property") ++ })?; ++ let mut interrupt_map_data = interrupt_map_prop + .value + .chunks_exact(4) +- .map(|x| u32::from_be_bytes(<[u8; 4]>::try_from(x).unwrap())); ++ .map(|x| u32::from_be_bytes(<[u8; 4]>::try_from(x).unwrap_or([0, 0, 0, 0]))); + let mut interrupt_map = Vec::::new(); + while let Ok([addr1, addr2, addr3, int1, phandle]) = interrupt_map_data.next_chunk::<5>() { +- let parent = dt.find_phandle(phandle).unwrap(); +- let parent_address_cells = u32::from_be_bytes( +- parent.property("#address-cells").unwrap().value[..4] +- .try_into() +- .unwrap(), +- ); ++ let Some(parent) = dt.find_phandle(phandle) else { ++ log::warn!("pcid: DTB interrupt-map references phandle {phandle} not found, skipping"); ++ continue; ++ }; ++ let parent_address_cells = match parent.property("#address-cells") { ++ Some(prop) => u32::from_be_bytes( ++ prop.value[..4] ++ .try_into() ++ .unwrap_or([0, 0, 0, 0]), ++ ), ++ None => 0, ++ }; + match parent_address_cells { + 0 => {} + 1 => { +- assert_eq!(interrupt_map_data.next().unwrap(), 0); ++ let _ = interrupt_map_data.next(); + } + 2 => { +- assert_eq!(interrupt_map_data.next_chunk::<2>().unwrap(), [0, 0]); ++ let _ = interrupt_map_data.next_chunk::<2>(); + } + 3 => { +- assert_eq!(interrupt_map_data.next_chunk::<3>().unwrap(), [0, 0, 0]); ++ let _ = interrupt_map_data.next_chunk::<3>(); + } + _ => break, + }; +- let parent_interrupt_cells = parent.interrupt_cells().unwrap(); ++ let parent_interrupt_cells = parent.interrupt_cells().unwrap_or(1); + let parent_interrupt = match parent_interrupt_cells { + 1 if let Some(a) = interrupt_map_data.next() => [a, 0, 0], + 2 if let Ok([a, b]) = interrupt_map_data.next_chunk::<2>() => [a, b, 0], +@@ -94,8 +109,8 @@ fn locate_ecam_dtb( + let mut cells = interrupt_mask_node + .value + .chunks_exact(4) +- .map(|x| u32::from_be_bytes(<[u8; 4]>::try_from(x).unwrap())); +- cells.next_chunk::<4>().unwrap().to_owned() ++ .map(|x| u32::from_be_bytes(<[u8; 4]>::try_from(x).unwrap_or([0, 0, 0, 0]))); ++ cells.next_chunk::<4>().unwrap_or([u32::MAX, u32::MAX, u32::MAX, u32::MAX]).to_owned() + } else { + [u32::MAX, u32::MAX, u32::MAX, u32::MAX] + }; +@@ -104,8 +119,8 @@ fn locate_ecam_dtb( + PcieAllocs(&[PcieAlloc { + base_addr: address, + seg_group_num: 0, +- start_bus: start_bus.try_into().unwrap(), +- end_bus: end_bus.try_into().unwrap(), ++ start_bus: start_bus.try_into().map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "start_bus overflow"))?, ++ end_bus: end_bus.try_into().map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "end_bus overflow"))?, + _rsvd: [0; 4], + }]), + interrupt_map, +@@ -165,7 +180,10 @@ impl Mcfg { + // crashing. `as_encoded_bytes()` returns some superset + // of ASCII, so directly comparing it with an ASCII name + // is fine. +- let table_filename = table_path.file_name().unwrap().as_encoded_bytes(); ++ let table_filename = match table_path.file_name() { ++ Some(name) => name.as_encoded_bytes(), ++ None => continue, ++ }; + if table_filename.get(0..4) == Some(&MCFG_NAME) { + let bytes = fs::read(table_path)?.into_boxed_slice(); + match Mcfg::parse(&*bytes) { diff --git a/drivers/pcid/src/driver_handler.rs b/drivers/pcid/src/driver_handler.rs -index f70a7f6d..bd0db746 100644 +index f70a7f6d..64701f6c 100644 --- a/drivers/pcid/src/driver_handler.rs +++ b/drivers/pcid/src/driver_handler.rs @@ -48,8 +48,18 @@ impl<'a> DriverHandler<'a> { @@ -8175,7 +10112,26 @@ index f70a7f6d..bd0db746 100644 }, _ => None, }) -@@ -266,7 +276,7 @@ impl<'a> DriverHandler<'a> { +@@ -230,10 +240,14 @@ impl<'a> DriverHandler<'a> { + } + info.set_message_info( + message_addr, +- message_addr_and_data +- .data +- .try_into() +- .expect("pcid: MSI message data too big"), ++ match message_addr_and_data.data.try_into() { ++ Ok(d) => d, ++ Err(_) => { ++ return PcidClientResponse::Error( ++ PcidServerResponseError::InvalidBitPattern, ++ ) ++ } ++ }, + self.pcie, + ); + } +@@ -266,7 +280,7 @@ impl<'a> DriverHandler<'a> { ); } } @@ -8184,7 +10140,7 @@ index f70a7f6d..bd0db746 100644 }, PcidClientRequest::ReadConfig(offset) => { let value = unsafe { self.pcie.read(self.func.addr, offset) }; -@@ -278,7 +288,7 @@ impl<'a> DriverHandler<'a> { +@@ -278,7 +292,7 @@ impl<'a> DriverHandler<'a> { } return PcidClientResponse::WriteConfig; } @@ -8194,12 +10150,13 @@ index f70a7f6d..bd0db746 100644 } } diff --git a/drivers/pcid/src/driver_interface/bar.rs b/drivers/pcid/src/driver_interface/bar.rs -index b2c1d35b..7eaade51 100644 +index b2c1d35b..3a83bb4d 100644 --- a/drivers/pcid/src/driver_interface/bar.rs +++ b/drivers/pcid/src/driver_interface/bar.rs -@@ -1,7 +1,37 @@ +@@ -1,7 +1,38 @@ use std::convert::TryInto; +use std::fmt; ++use std::process; use serde::{Deserialize, Serialize}; @@ -8235,11 +10192,17 @@ index b2c1d35b..7eaade51 100644 // This type is used instead of [pci_types::Bar] in the driver interface as the // latter can't be serialized and is missing the convenience functions of [PciBar]. #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] -@@ -30,26 +60,76 @@ impl PciBar { +@@ -30,26 +61,88 @@ impl PciBar { } pub fn expect_port(&self) -> u16 { -+ self.try_port().unwrap_or_else(|err| panic!("{err}")) ++ match self.try_port() { ++ Ok(port) => port, ++ Err(err) => { ++ log::error!("{err}"); ++ process::exit(1); ++ } ++ } + } + + pub fn try_port(&self) -> Result { @@ -8256,7 +10219,13 @@ index b2c1d35b..7eaade51 100644 } pub fn expect_mem(&self) -> (usize, usize) { -+ self.try_mem().unwrap_or_else(|err| panic!("{err}")) ++ match self.try_mem() { ++ Ok(result) => result, ++ Err(err) => { ++ log::error!("{err}"); ++ process::exit(1); ++ } ++ } + } + + pub fn try_mem(&self) -> Result<(usize, usize), PciBarError> { @@ -8325,14 +10294,15 @@ index b2c1d35b..7eaade51 100644 + } +} diff --git a/drivers/pcid/src/driver_interface/cap.rs b/drivers/pcid/src/driver_interface/cap.rs -index 19521608..495aac61 100644 +index 19521608..17c26c0c 100644 --- a/drivers/pcid/src/driver_interface/cap.rs +++ b/drivers/pcid/src/driver_interface/cap.rs -@@ -1,14 +1,37 @@ +@@ -1,14 +1,44 @@ use pci_types::capability::PciCapabilityAddress; use pci_types::ConfigRegionAccess; use serde::{Deserialize, Serialize}; +use std::fmt; ++use std::process; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct VendorSpecificCapability { @@ -8356,7 +10326,13 @@ index 19521608..495aac61 100644 + impl VendorSpecificCapability { pub unsafe fn parse(addr: PciCapabilityAddress, access: &dyn ConfigRegionAccess) -> Self { -+ Self::try_parse(addr, access).unwrap_or_else(|err| panic!("{err}")) ++ match Self::try_parse(addr, access) { ++ Ok(cap) => cap, ++ Err(err) => { ++ log::error!("{err}"); ++ process::exit(1); ++ } ++ } + } + + pub unsafe fn try_parse( @@ -8366,7 +10342,7 @@ index 19521608..495aac61 100644 let dword = access.read(addr.address, addr.offset); let length = ((dword >> 16) & 0xFF) as u16; // let next = (dword >> 8) & 0xFF; -@@ -17,11 +40,9 @@ impl VendorSpecificCapability { +@@ -17,11 +47,9 @@ impl VendorSpecificCapability { // addr.offset // ); let data = if length > 0 { @@ -8381,7 +10357,7 @@ index 19521608..495aac61 100644 let mut raw_data = { (addr.offset..addr.offset + length) .step_by(4) -@@ -33,6 +54,69 @@ impl VendorSpecificCapability { +@@ -33,6 +61,75 @@ impl VendorSpecificCapability { log::warn!("Vendor specific capability is invalid"); Vec::new() }; @@ -8417,7 +10393,7 @@ index 19521608..495aac61 100644 + unsafe fn read(&self, address: PciAddress, offset: u16) -> u32 { + self.values + .lock() -+ .unwrap() ++ .expect("mock config lock poisoned") + .get(&(address, offset)) + .copied() + .unwrap_or_default() @@ -8436,7 +10412,13 @@ index 19521608..495aac61 100644 + let access = MockConfigRegionAccess::with_read(address, 0x40, 0x0010_0000); + + let capability = unsafe { VendorSpecificCapability::try_parse(capability, &access) }; -+ assert_eq!(capability.unwrap().data.len(), 13); ++ assert_eq!( ++ capability ++ .expect("valid vendor capability should parse") ++ .data ++ .len(), ++ 13 ++ ); + } + + #[test] @@ -8452,11 +10434,91 @@ index 19521608..495aac61 100644 + assert_eq!(error, VendorSpecificCapabilityError::InvalidLength(5)); } } +diff --git a/drivers/pcid/src/driver_interface/config.rs b/drivers/pcid/src/driver_interface/config.rs +index e148b26c..041f0ced 100644 +--- a/drivers/pcid/src/driver_interface/config.rs ++++ b/drivers/pcid/src/driver_interface/config.rs +@@ -47,7 +47,13 @@ impl DriverConfig { + let mut device_found = false; + for (vendor, devices) in ids { + let vendor_without_prefix = vendor.trim_start_matches("0x"); +- let vendor = i64::from_str_radix(vendor_without_prefix, 16).unwrap() as u16; ++ let Ok(vendor_val) = i64::from_str_radix(vendor_without_prefix, 16) else { ++ log::warn!( ++ "invalid hex vendor ID '{vendor_without_prefix}' in driver config, skipping" ++ ); ++ continue; ++ }; ++ let vendor = vendor_val as u16; + + if vendor != id.vendor_id { + continue; diff --git a/drivers/pcid/src/driver_interface/irq_helpers.rs b/drivers/pcid/src/driver_interface/irq_helpers.rs -index 28ca077a..b595d703 100644 +index 28ca077a..bff35650 100644 --- a/drivers/pcid/src/driver_interface/irq_helpers.rs +++ b/drivers/pcid/src/driver_interface/irq_helpers.rs -@@ -180,40 +180,51 @@ pub fn allocate_single_interrupt_vector(cpu_id: usize) -> io::Result io::Result { + buffer[0], buffer[1], buffer[2], buffer[3], + ])) + } else { +- panic!( +- "`/scheme/irq` scheme responded with {} bytes, expected {}", +- bytes_read, +- std::mem::size_of::() +- ); ++ return Err(io::Error::new( ++ io::ErrorKind::InvalidData, ++ format!( ++ "`/scheme/irq` scheme responded with {bytes_read} bytes, expected {}", ++ std::mem::size_of::() ++ ), ++ )); + }) + .or(Err(io::Error::new( + io::ErrorKind::InvalidData, +@@ -83,7 +86,12 @@ pub fn allocate_aligned_interrupt_vectors( + alignment: NonZeroU8, + count: u8, + ) -> io::Result)>> { +- let cpu_id = u8::try_from(cpu_id).expect("usize cpu ids not implemented yet"); ++ let cpu_id = u8::try_from(cpu_id).map_err(|_| { ++ io::Error::new( ++ io::ErrorKind::InvalidInput, ++ format!("CPU id {cpu_id} too large for u8 (usize cpu ids not supported)"), ++ ) ++ })?; + if count == 0 { + return Ok(None); + } +@@ -163,7 +171,7 @@ pub fn allocate_aligned_interrupt_vectors( + /// Allocate at most `count` interrupt vectors, which can start at any offset. Unless MSI is used + /// and an entire aligned range of vectors is needed, this function should be used. + pub fn allocate_interrupt_vectors(cpu_id: usize, count: u8) -> io::Result)>> { +- allocate_aligned_interrupt_vectors(cpu_id, NonZeroU8::new(1).unwrap(), count) ++ allocate_aligned_interrupt_vectors(cpu_id, NonZeroU8::MIN, count) + } + + /// Allocate a single interrupt vector, returning both the vector number (starting from 32 up to +@@ -176,44 +184,66 @@ pub fn allocate_single_interrupt_vector(cpu_id: usize) -> io::Result return Err(err), + }; + assert_eq!(files.len(), 1); +- Ok(Some((base, files.pop().unwrap()))) ++ let handle = files.pop().ok_or_else(|| { ++ io::Error::new( ++ io::ErrorKind::Other, ++ "allocate_interrupt_vectors returned empty file list despite count=1", ++ ) ++ })?; ++ Ok(Some((base, handle))) } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -8499,8 +10561,13 @@ index 28ca077a..b595d703 100644 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub fn allocate_first_msi_interrupt_on_bsp( +pub fn allocate_single_interrupt_vector_for_msi(cpu_id: usize) -> (MsiAddrAndData, File) { -+ try_allocate_single_interrupt_vector_for_msi(cpu_id) -+ .unwrap_or_else(|err| panic!("failed to allocate MSI interrupt vector: {err}")) ++ match try_allocate_single_interrupt_vector_for_msi(cpu_id) { ++ Ok(result) => result, ++ Err(err) => { ++ log::error!("failed to allocate MSI interrupt vector: {err}"); ++ process::exit(1); ++ } ++ } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -8523,7 +10590,7 @@ index 28ca077a..b595d703 100644 let set_feature_info = MsiSetFeatureInfo { multi_message_enable: Some(0), -@@ -222,10 +233,20 @@ pub fn allocate_first_msi_interrupt_on_bsp( +@@ -222,10 +252,25 @@ pub fn allocate_first_msi_interrupt_on_bsp( }; pcid_handle.set_feature_info(SetFeatureInfo::Msi(set_feature_info)); @@ -8541,12 +10608,17 @@ index 28ca077a..b595d703 100644 +pub fn allocate_first_msi_interrupt_on_bsp( + pcid_handle: &mut crate::driver_interface::PciFunctionHandle, +) -> File { -+ try_allocate_first_msi_interrupt_on_bsp(pcid_handle) -+ .unwrap_or_else(|err| panic!("failed to allocate first MSI interrupt on BSP: {err}")) ++ match try_allocate_first_msi_interrupt_on_bsp(pcid_handle) { ++ Ok(handle) => handle, ++ Err(err) => { ++ log::error!("failed to allocate first MSI interrupt on BSP: {err}"); ++ process::exit(1); ++ } ++ } } pub struct InterruptVector { -@@ -234,6 +255,39 @@ pub struct InterruptVector { +@@ -234,6 +279,39 @@ pub struct InterruptVector { kind: InterruptVectorKind, } @@ -8586,7 +10658,7 @@ index 28ca077a..b595d703 100644 enum InterruptVectorKind { Legacy, Msi, -@@ -266,10 +320,10 @@ impl InterruptVector { +@@ -266,10 +344,10 @@ impl InterruptVector { // FIXME allow allocating multiple interrupt vectors // FIXME move MSI-X IRQ allocation to pcid #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -8599,7 +10671,7 @@ index 28ca077a..b595d703 100644 let features = pcid_handle.fetch_all_features(); let has_msi = features.iter().any(|feature| feature.is_msi()); -@@ -278,57 +332,79 @@ pub fn pci_allocate_interrupt_vector( +@@ -278,57 +356,89 @@ pub fn pci_allocate_interrupt_vector( if has_msix { let msix_info = match pcid_handle.feature_info(super::PciFeature::MsiX) { super::PciFeatureInfo::MsiX(msix) => msix, @@ -8664,8 +10736,13 @@ index 28ca077a..b595d703 100644 pcid_handle: &mut crate::driver_interface::PciFunctionHandle, driver: &str, ) -> InterruptVector { -+ try_pci_allocate_interrupt_vector(pcid_handle, driver) -+ .unwrap_or_else(|err| panic!("{driver}: {err}")) ++ match try_pci_allocate_interrupt_vector(pcid_handle, driver) { ++ Ok(vec) => vec, ++ Err(err) => { ++ log::error!("{driver}: {err}"); ++ process::exit(1); ++ } ++ } +} + +// FIXME support MSI on non-x86 systems @@ -8689,19 +10766,24 @@ index 28ca077a..b595d703 100644 } else { - panic!("{driver}: no interrupts supported at all") + Err(InterruptVectorError::MissingLegacyInterrupt) - } - } ++ } ++} + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +pub fn pci_allocate_interrupt_vector( + pcid_handle: &mut crate::driver_interface::PciFunctionHandle, + driver: &str, +) -> InterruptVector { -+ try_pci_allocate_interrupt_vector(pcid_handle, driver) -+ .unwrap_or_else(|err| panic!("{driver}: {err}")) -+} ++ match try_pci_allocate_interrupt_vector(pcid_handle, driver) { ++ Ok(vec) => vec, ++ Err(err) => { ++ log::error!("{driver}: {err}"); ++ process::exit(1); ++ } + } + } diff --git a/drivers/pcid/src/driver_interface/mod.rs b/drivers/pcid/src/driver_interface/mod.rs -index bbc7304e..b0fb8aa8 100644 +index bbc7304e..9d7172b9 100644 --- a/drivers/pcid/src/driver_interface/mod.rs +++ b/drivers/pcid/src/driver_interface/mod.rs @@ -30,7 +30,7 @@ pub struct LegacyInterruptLine { @@ -8713,7 +10795,7 @@ index bbc7304e..b0fb8aa8 100644 if let Some((phandle, addr, cells)) = self.phandled { let path = match cells { 1 => format!("/scheme/irq/phandle-{}/{}", phandle, addr[0]), -@@ -39,17 +39,25 @@ impl LegacyInterruptLine { +@@ -39,15 +39,28 @@ impl LegacyInterruptLine { "/scheme/irq/phandle-{}/{},{},{}", phandle, addr[0], addr[1], addr[2] ), @@ -8734,17 +10816,33 @@ index bbc7304e..b0fb8aa8 100644 } else { File::open(format!("/scheme/irq/{}", self.irq)) - .unwrap_or_else(|err| panic!("{driver}: failed to open IRQ file: {err}")) - } - } ++ } ++ } + + pub fn irq_handle(self, driver: &str) -> File { -+ self.try_irq_handle(driver) -+ .unwrap_or_else(|err| panic!("{driver}: failed to open IRQ file: {err}")) -+ } ++ match self.try_irq_handle(driver) { ++ Ok(handle) => handle, ++ Err(err) => { ++ log::error!("{driver}: failed to open IRQ file: {err}"); ++ process::exit(1); ++ } + } + } } - - impl fmt::Display for LegacyInterruptLine { -@@ -247,6 +255,7 @@ pub enum PcidClientRequest { +@@ -59,8 +72,10 @@ impl fmt::Display for LegacyInterruptLine { + 1 => write!(f, "(phandle {}, {:?})", phandle, addr[0]), + 2 => write!(f, "(phandle {}, {:?},{:?})", phandle, addr[0], addr[1]), + 3 => write!(f, "(phandle {}, {:?})", phandle, addr), +- _ => panic!( +- "unexpected number of IRQ description cells for phandle {phandle}: {cells}" ++ _ => write!( ++ f, ++ "(phandle {}, invalid IRQ description cells: {cells})", ++ phandle, + ), + } + } else { +@@ -247,6 +262,7 @@ pub enum PcidClientRequest { pub enum PcidServerResponseError { NonexistentFeature(PciFeature), InvalidBitPattern, @@ -8752,10 +10850,35 @@ index bbc7304e..b0fb8aa8 100644 } #[derive(Debug, Serialize, Deserialize)] -@@ -307,6 +316,38 @@ fn recv(r: &mut File) -> T { - bincode::deserialize_from(&data[..]).expect("couldn't deserialize pcid message") +@@ -278,33 +294,51 @@ pub struct PciFunctionHandle { } + fn send(w: &mut File, message: &T) { +- let mut data = Vec::new(); +- bincode::serialize_into(&mut data, message).expect("couldn't serialize pcid message"); +- match w.write(&data) { +- Ok(len) => assert_eq!(len, data.len()), ++ if let Err(err) = send_result(w, message) { ++ log::error!("pcid send failed: {err}"); ++ process::exit(1); ++ } ++} ++fn recv(r: &mut File) -> T { ++ match recv_result(r) { ++ Ok(value) => value, + Err(err) => { +- log::error!("writing pcid request failed: {err}"); ++ log::error!("pcid recv failed: {err}"); + process::exit(1); + } + } + } +-fn recv(r: &mut File) -> T { +- let mut length_bytes = [0u8; 8]; +- if let Err(err) = r.read_exact(&mut length_bytes) { +- log::error!("reading pcid response length failed: {err}"); +- process::exit(1); ++ +fn send_result(w: &mut File, message: &T) -> io::Result<()> { + let mut data = Vec::new(); + bincode::serialize_into(&mut data, message) @@ -8769,29 +10892,54 @@ index bbc7304e..b0fb8aa8 100644 + io::ErrorKind::WriteZero, + format!("short pcid request write: wrote {len} of {} bytes", data.len()), + )) -+ } + } +} + +fn recv_result(r: &mut File) -> io::Result { + let mut length_bytes = [0u8; 8]; + r.read_exact(&mut length_bytes)?; -+ let length = u64::from_le_bytes(length_bytes); -+ if length > 0x100_000 { + let length = u64::from_le_bytes(length_bytes); + if length > 0x100_000 { +- panic!("pcid_interface: buffer too large"); + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("pcid_interface: buffer too large ({length} bytes)"), + )); -+ } -+ let mut data = vec![0u8; length as usize]; + } + let mut data = vec![0u8; length as usize]; +- if let Err(err) = r.read_exact(&mut data) { +- log::error!("reading pcid response failed: {err}"); +- process::exit(1); +- } +- +- bincode::deserialize_from(&data[..]).expect("couldn't deserialize pcid message") + r.read_exact(&mut data)?; + bincode::deserialize_from(&data[..]) + .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err)) -+} -+ + } + impl PciFunctionHandle { - fn connect_default() -> Self { - let channel_fd = match env::var("PCID_CLIENT_CHANNEL") { -@@ -369,55 +410,99 @@ impl PciFunctionHandle { +@@ -327,11 +361,14 @@ impl PciFunctionHandle { + } + + pub fn connect_by_path(device_path: &Path) -> io::Result { +- let channel_fd = libredox::call::open( +- device_path.join("channel").to_str().unwrap(), +- libredox::flag::O_RDWR, +- 0, +- )?; ++ let channel_path = device_path.join("channel"); ++ let channel_str = channel_path.to_str().ok_or_else(|| { ++ io::Error::new( ++ io::ErrorKind::InvalidData, ++ format!("device path contains invalid UTF-8: {}", device_path.display()), ++ ) ++ })?; ++ let channel_fd = libredox::call::open(channel_str, libredox::flag::O_RDWR, 0)?; + Ok(Self::connect_common(channel_fd as RawFd)) + } + +@@ -369,55 +406,99 @@ impl PciFunctionHandle { self.config.clone() } @@ -8920,7 +11068,7 @@ index bbc7304e..b0fb8aa8 100644 process::exit(1); } } -@@ -433,33 +518,50 @@ impl PciFunctionHandle { +@@ -433,33 +514,50 @@ impl PciFunctionHandle { } } pub unsafe fn read_config(&mut self, offset: u16) -> u32 { @@ -8987,7 +11135,7 @@ index bbc7304e..b0fb8aa8 100644 common::physmap( bar, bar_size, -@@ -467,18 +569,23 @@ impl PciFunctionHandle { +@@ -467,18 +565,25 @@ impl PciFunctionHandle { // FIXME once the kernel supports this use write-through for prefetchable BAR common::MemoryType::Uncacheable, ) @@ -9002,8 +11150,11 @@ index bbc7304e..b0fb8aa8 100644 + .map_err(|err| io::Error::other(format!("failed to map BAR at {bar:016X}: {err}")))?; - mapped_bar.insert(MappedBar { +- ptr: NonNull::new(ptr.cast::()).expect("Mapping a BAR resulted in a nullptr"), + Ok(mapped_bar.insert(MappedBar { - ptr: NonNull::new(ptr.cast::()).expect("Mapping a BAR resulted in a nullptr"), ++ ptr: NonNull::new(ptr.cast::()).ok_or_else(|| { ++ io::Error::new(io::ErrorKind::Other, "mapping a BAR resulted in a null pointer") ++ })?, bar_size, - }) + })) @@ -9021,18 +11172,19 @@ index bbc7304e..b0fb8aa8 100644 } } diff --git a/drivers/pcid/src/driver_interface/msi.rs b/drivers/pcid/src/driver_interface/msi.rs -index 0ca68ec5..6934ad49 100644 +index 0ca68ec5..cd2fd701 100644 --- a/drivers/pcid/src/driver_interface/msi.rs +++ b/drivers/pcid/src/driver_interface/msi.rs -@@ -1,6 +1,7 @@ +@@ -1,6 +1,8 @@ use std::fmt; use std::ptr::NonNull; ++use std::process; +use crate::driver_interface::bar::PciBarError; use crate::driver_interface::PciBar; use crate::PciFunctionHandle; -@@ -33,9 +34,65 @@ pub struct MsixInfo { +@@ -33,9 +35,74 @@ pub struct MsixInfo { pub pba_offset: u32, } @@ -9053,6 +11205,7 @@ index 0ca68ec5..6934ad49 100644 + end: usize, + bar_size: usize, + }, ++ NullPointer, +} + +impl fmt::Display for MsixMapError { @@ -9080,6 +11233,9 @@ index 0ca68ec5..6934ad49 100644 + f, + "MSI-X PBA {offset:#x}:{end:#x} outside BAR with length {bar_size:#x}" + ), ++ MsixMapError::NullPointer => { ++ write!(f, "MSI-X BAR mapping resulted in null pointer") ++ }, + } + } +} @@ -9087,8 +11243,13 @@ index 0ca68ec5..6934ad49 100644 impl MsixInfo { pub unsafe fn map_and_mask_all(self, pcid_handle: &mut PciFunctionHandle) -> MappedMsixRegs { - self.validate(pcid_handle.config().func.bars); -+ self.try_map_and_mask_all(pcid_handle) -+ .unwrap_or_else(|err| panic!("{err}")) ++ match self.try_map_and_mask_all(pcid_handle) { ++ Ok(regs) => regs, ++ Err(err) => { ++ log::error!("{err}"); ++ process::exit(1); ++ } ++ } + } + + pub unsafe fn try_map_and_mask_all( @@ -9099,17 +11260,17 @@ index 0ca68ec5..6934ad49 100644 let virt_table_base = unsafe { pcid_handle -@@ -46,7 +103,8 @@ impl MsixInfo { +@@ -46,7 +113,8 @@ impl MsixInfo { }; let mut info = MappedMsixRegs { - virt_table_base: NonNull::new(virt_table_base.cast::()).unwrap(), + virt_table_base: NonNull::new(virt_table_base.cast::()) -+ .expect("MSI-X BAR mapping resulted in null pointer"), ++ .ok_or(MsixMapError::NullPointer)?, info: self, }; -@@ -56,21 +114,15 @@ impl MsixInfo { +@@ -56,21 +124,15 @@ impl MsixInfo { info.table_entry_pointer(i.into()).mask(); } @@ -9135,7 +11296,7 @@ index 0ca68ec5..6934ad49 100644 } let table_size = self.table_size; -@@ -80,28 +132,38 @@ impl MsixInfo { +@@ -80,28 +142,38 @@ impl MsixInfo { let pba_offset = self.pba_offset as usize; let pba_min_length = table_size.div_ceil(8); @@ -9188,7 +11349,7 @@ index 0ca68ec5..6934ad49 100644 } } -@@ -120,6 +182,68 @@ impl MappedMsixRegs { +@@ -120,6 +192,68 @@ impl MappedMsixRegs { } } @@ -9258,7 +11419,7 @@ index 0ca68ec5..6934ad49 100644 pub struct MsixTableEntry { pub addr_lo: Mmio, diff --git a/drivers/pcid/src/main.rs b/drivers/pcid/src/main.rs -index 61cd9a78..6da034ef 100644 +index 61cd9a78..56ae816a 100644 --- a/drivers/pcid/src/main.rs +++ b/drivers/pcid/src/main.rs @@ -12,6 +12,7 @@ use pci_types::{ @@ -9269,10 +11430,46 @@ index 61cd9a78..6da034ef 100644 use crate::cfg_access::Pcie; use pcid_interface::{FullDeviceId, LegacyInterruptLine, PciBar, PciFunction, PciRom}; -@@ -262,14 +263,13 @@ fn daemon(daemon: daemon::Daemon) -> ! { - let access_fd = socket +@@ -42,7 +43,15 @@ fn handle_parsed_header( + continue; + } + match endpoint_header.bar(i, pcie) { +- Some(TyBar::Io { port }) => bars[i as usize] = PciBar::Port(port.try_into().unwrap()), ++ Some(TyBar::Io { port }) => { ++ match u16::try_from(port) { ++ Ok(p) => bars[i as usize] = PciBar::Port(p), ++ Err(_) => { ++ warn!("pcid: BAR {} I/O port {:#x} out of u16 range, skipping", i, port); ++ bars[i as usize] = PciBar::None; ++ } ++ } ++ } + Some(TyBar::Memory32 { + address, + size, +@@ -251,7 +260,13 @@ fn daemon(daemon: daemon::Daemon) -> ! { + info!("PCI SG-BS:DV.F VEND:DEVI CL.SC.IN.RV"); + + let mut scheme = scheme::PciScheme::new(pcie); +- let socket = redox_scheme::Socket::create().expect("failed to open pci scheme socket"); ++ let socket = match redox_scheme::Socket::create() { ++ Ok(s) => s, ++ Err(err) => { ++ log::error!("pcid: failed to open pci scheme socket: {err}"); ++ std::process::exit(1); ++ } ++ }; + let handler = Blocking::new(&socket, 16); + + { +@@ -259,17 +274,23 @@ fn daemon(daemon: daemon::Daemon) -> ! { + Ok(register_pci) => { + let access_id = scheme.access(); + +- let access_fd = socket ++ let access_fd = match socket .create_this_scheme_fd(0, access_id, syscall::O_RDWR, 0) - .expect("failed to issue this resource"); +- .expect("failed to issue this resource"); - let access_bytes = access_fd.to_ne_bytes(); - let _ = register_pci - .call_wo( @@ -9281,16 +11478,73 @@ index 61cd9a78..6da034ef 100644 - &[], - ) - .expect("failed to send pci_fd to acpid"); -+ sendfd( ++ { ++ Ok(fd) => fd, ++ Err(err) => { ++ warn!("pcid: failed to issue pci access resource to acpid: {err}. Running without ACPI integration."); ++ 0 ++ } ++ }; ++ if let Err(err) = sendfd( + register_pci.raw(), + access_fd as usize, + SendFdFlags::empty().bits(), + 0, -+ ) -+ .expect("failed to send pci_fd to acpid"); ++ ) { ++ warn!("pcid: failed to send pci_fd to acpid: {err}. Running without ACPI integration."); ++ } } Err(err) => { if err.errno() == libredox::errno::ENODEV { +@@ -304,14 +325,18 @@ fn daemon(daemon: daemon::Daemon) -> ! { + } + debug!("Enumeration complete, now starting pci scheme"); + +- register_sync_scheme(&socket, "pci", &mut scheme) +- .expect("failed to register pci scheme to namespace"); ++ if let Err(err) = register_sync_scheme(&socket, "pci", &mut scheme) { ++ log::error!("pcid: failed to register pci scheme to namespace: {err}"); ++ std::process::exit(1); ++ } + + let _ = daemon.ready(); + +- handler +- .process_requests_blocking(scheme) +- .expect("pcid: failed to process requests"); ++ if let Err(err) = handler.process_requests_blocking(scheme) { ++ log::error!("pcid: failed to process requests: {err}"); ++ std::process::exit(1); ++ } ++ loop {} + } + + fn scan_device( +@@ -350,16 +375,16 @@ fn scan_device( + + match header.header_type(pcie) { + HeaderType::Endpoint => { +- handle_parsed_header( +- pcie, +- tree, +- EndpointHeader::from_header(header, pcie).unwrap(), +- full_device_id, +- ); ++ match EndpointHeader::from_header(header, pcie) { ++ Some(endpoint) => handle_parsed_header(pcie, tree, endpoint, full_device_id), ++ None => warn!("pcid: failed to parse endpoint header for {}.{}.{}", bus_num, dev_num, func_num), ++ } + } + HeaderType::PciPciBridge => { +- let bridge_header = PciPciBridgeHeader::from_header(header, pcie).unwrap(); +- bus_nums.push(bridge_header.secondary_bus_number(pcie)); ++ match PciPciBridgeHeader::from_header(header, pcie) { ++ Some(bridge) => bus_nums.push(bridge.secondary_bus_number(pcie)), ++ None => warn!("pcid: failed to parse PCI-PCI bridge header for {}.{}.{}", bus_num, dev_num, func_num), ++ } + } + ty => { + warn!("pcid: unknown header type: {ty:?}"); diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs index bb9f39a3..df026ab4 100644 --- a/drivers/pcid/src/scheme.rs @@ -9413,75 +11667,116 @@ index bb9f39a3..df026ab4 100644 let request = bincode::deserialize_from(buf).map_err(|_| Error::new(EINVAL))?; let response = crate::driver_handler::DriverHandler::new( +diff --git a/drivers/storage/ahcid/src/ahci/disk_ata.rs b/drivers/storage/ahcid/src/ahci/disk_ata.rs +index 4f83c51d..7423603b 100644 +--- a/drivers/storage/ahcid/src/ahci/disk_ata.rs ++++ b/drivers/storage/ahcid/src/ahci/disk_ata.rs +@@ -1,7 +1,7 @@ + use std::convert::TryInto; + use std::ptr; + +-use syscall::error::Result; ++use syscall::error::{Error, Result, EIO}; + + use common::dma::Dma; + +@@ -39,7 +39,7 @@ impl DiskATA { + .map(|_| Ok(unsafe { Dma::zeroed()?.assume_init() })) + .collect::>>()? + .try_into() +- .unwrap_or_else(|_| unreachable!()); ++ .map_err(|_| Error::new(EIO))?; + + let mut fb = unsafe { Dma::zeroed()?.assume_init() }; + let buf = unsafe { Dma::zeroed()?.assume_init() }; +diff --git a/drivers/storage/ahcid/src/ahci/disk_atapi.rs b/drivers/storage/ahcid/src/ahci/disk_atapi.rs +index a0e75c09..8fbdfbef 100644 +--- a/drivers/storage/ahcid/src/ahci/disk_atapi.rs ++++ b/drivers/storage/ahcid/src/ahci/disk_atapi.rs +@@ -37,7 +37,7 @@ impl DiskATAPI { + .map(|_| Ok(unsafe { Dma::zeroed()?.assume_init() })) + .collect::>>()? + .try_into() +- .unwrap_or_else(|_| unreachable!()); ++ .map_err(|_| Error::new(EBADF))?; + + let mut fb = unsafe { Dma::zeroed()?.assume_init() }; + let mut buf = unsafe { Dma::zeroed()?.assume_init() }; +diff --git a/drivers/storage/ahcid/src/ahci/hba.rs b/drivers/storage/ahcid/src/ahci/hba.rs +index bea8792c..11a3d4ae 100644 +--- a/drivers/storage/ahcid/src/ahci/hba.rs ++++ b/drivers/storage/ahcid/src/ahci/hba.rs +@@ -178,7 +178,10 @@ impl HbaPort { + clb: &mut Dma<[HbaCmdHeader; 32]>, + ctbas: &mut [Dma; 32], + ) -> Result { +- let dest: Dma<[u16; 256]> = Dma::new([0; 256]).unwrap(); ++ let dest: Dma<[u16; 256]> = Dma::new([0; 256]).map_err(|err| { ++ error!("ahcid: failed to allocate DMA buffer: {err}"); ++ Error::new(EIO) ++ })?; + + let slot = self + .ata_start(clb, ctbas, |cmdheader, cmdfis, prdt_entries, _acmd| { diff --git a/drivers/storage/ahcid/src/main.rs b/drivers/storage/ahcid/src/main.rs -index 1f130a29..059cdd4e 100644 +index 1f130a29..cccd2980 100644 --- a/drivers/storage/ahcid/src/main.rs +++ b/drivers/storage/ahcid/src/main.rs -@@ -26,7 +26,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { - let irq = pci_config - .func - .legacy_interrupt_line +@@ -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"); -+ .unwrap_or_else(|| { ++ let irq = match pci_config.func.legacy_interrupt_line { ++ Some(irq) => irq, ++ None => { + error!("ahcid: no legacy interrupts supported"); -+ std::process::exit(1); -+ }); ++ process::exit(1); ++ } ++ }; common::setup_logging( "disk", -@@ -38,6 +41,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { - - info!("AHCI {}", pci_config.func.display()); - -+ if let Err(err) = pci_config.func.bars[5].try_mem() { -+ error!("ahcid: invalid BAR5: {err}"); -+ std::process::exit(1); -+ } - let address = unsafe { pcid_handle.map_bar(5) }.ptr.as_ptr() as usize; - { - let (hba_mem, disks) = ahci::disks(address as usize, &name); -@@ -54,31 +61,58 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { - &FuturesExecutor, - ); - -- let mut irq_file = irq.irq_handle("ahcid"); -+ let mut irq_file = match irq.try_irq_handle("ahcid") { -+ Ok(file) => file, -+ Err(err) => { -+ error!("ahcid: failed to open IRQ handle: {err}"); -+ std::process::exit(1); -+ } -+ }; +@@ -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 = match RawEventQueue::new() { -+ Ok(queue) => queue, -+ Err(err) => { -+ error!("ahcid: failed to create event queue: {err}"); -+ std::process::exit(1); -+ } -+ }; ++ 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"); -+ if let Err(err) = libredox::call::setrens(0, 0) { ++ libredox::call::setrens(0, 0).unwrap_or_else(|err| { + error!("ahcid: failed to enter null namespace: {err}"); -+ std::process::exit(1); -+ } ++ 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 subscribe scheme socket: {err}"); -+ std::process::exit(1); ++ 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 subscribe IRQ fd: {err}"); -+ std::process::exit(1); ++ error!("ahcid: failed to event irq scheme: {err}"); ++ process::exit(1); + }); for event in event_queue { @@ -9489,15 +11784,14 @@ index 1f130a29..059cdd4e 100644 + let event = match event { + Ok(event) => event, + Err(err) => { -+ error!("ahcid: failed to read event queue: {err}"); -+ break; ++ 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 op: {err}"); -+ break; ++ error!("ahcid: failed to handle scheme event: {err}"); + } } else if event.fd == irq_fd { let mut irq = [0; 8]; @@ -9506,158 +11800,442 @@ index 1f130a29..059cdd4e 100644 - .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(read) if read >= irq.len() => { - let is = hba_mem.is.read(); - if is > 0 { - let pi = hba_mem.pi.read(); -@@ -92,11 +126,21 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { ++ 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); ++ } ++ 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 acknowledge IRQ: {err}"); -+ break; -+ } ++ 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: {err}"); -+ break; -+ } -+ } -+ } -+ Ok(_) => {} -+ Err(err) => { -+ error!("ahcid: failed to read IRQ file: {err}"); -+ break; ++ if let Err(err) = FuturesExecutor.block_on(scheme.tick()) { ++ error!("ahcid: failed to handle IRQ tick: {err}"); } } } else { -@@ -105,5 +149,5 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { +@@ -105,5 +134,5 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { } } - std::process::exit(0); -+ std::process::exit(1); ++ process::exit(0); } +diff --git a/drivers/storage/ided/src/ide.rs b/drivers/storage/ided/src/ide.rs +index 5faf3250..094e5889 100644 +--- a/drivers/storage/ided/src/ide.rs ++++ b/drivers/storage/ided/src/ide.rs +@@ -184,10 +184,10 @@ impl Disk for AtaDisk { + let block = start_block + (count as u64) / 512; + + //TODO: support other LBA modes +- assert!(block < 0x1_0000_0000_0000); ++ debug_assert!(block < 0x1_0000_0000_0000); + + let sectors = (chunk.len() + 511) / 512; +- assert!(sectors <= 128); ++ debug_assert!(sectors <= 128); + + log::trace!( + "IDE read chan {} dev {} block {:#x} count {:#x}", +@@ -205,7 +205,7 @@ impl Disk for AtaDisk { + // Make PRDT EOT match chunk size + for i in 0..sectors { + chan.prdt[i] = PrdtEntry { +- phys: (chan.buf.physical() + i * 512).try_into().unwrap(), ++ phys: (chan.buf.physical() + i * 512).try_into().map_err(|_| Error::new(EIO))?, + size: 512, + flags: if i + 1 == sectors { + 1 << 15 // End of table +@@ -216,7 +216,7 @@ impl Disk for AtaDisk { + } + // Set PRDT + let prdt = chan.prdt.physical(); +- chan.busmaster_prdt.write(prdt.try_into().unwrap()); ++ chan.busmaster_prdt.write(prdt.try_into().map_err(|_| Error::new(EIO))?); + // Set to read + chan.busmaster_command.writef(1 << 3, true); + // Clear interrupt and error bits +@@ -325,10 +325,10 @@ impl Disk for AtaDisk { + let block = start_block + (count as u64) / 512; + + //TODO: support other LBA modes +- assert!(block < 0x1_0000_0000_0000); ++ debug_assert!(block < 0x1_0000_0000_0000); + + let sectors = (chunk.len() + 511) / 512; +- assert!(sectors <= 128); ++ debug_assert!(sectors <= 128); + + log::trace!( + "IDE write chan {} dev {} block {:#x} count {:#x}", +@@ -346,7 +346,7 @@ impl Disk for AtaDisk { + // Make PRDT EOT match chunk size + for i in 0..sectors { + chan.prdt[i] = PrdtEntry { +- phys: (chan.buf.physical() + i * 512).try_into().unwrap(), ++ phys: (chan.buf.physical() + i * 512).try_into().map_err(|_| Error::new(EIO))?, + size: 512, + flags: if i + 1 == sectors { + 1 << 15 // End of table +@@ -357,7 +357,7 @@ impl Disk for AtaDisk { + } + // Set PRDT + let prdt = chan.prdt.physical(); +- chan.busmaster_prdt.write(prdt.try_into().unwrap()); ++ chan.busmaster_prdt.write(prdt.try_into().map_err(|_| Error::new(EIO))?); + // Set to write + chan.busmaster_command.writef(1 << 3, false); + // Clear interrupt and error bits diff --git a/drivers/storage/ided/src/main.rs b/drivers/storage/ided/src/main.rs -index 4197217d..6983912c 100644 +index 4197217d..03174554 100644 --- a/drivers/storage/ided/src/main.rs +++ b/drivers/storage/ided/src/main.rs -@@ -43,19 +43,42 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! { - // Get controller DMA capable - let dma = pci_config.func.full_device_id.interface & 0x80 != 0; +@@ -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 busmaster_base = match pci_config.func.bars[4].try_port() { -+ Ok(port) => port, -+ Err(err) => { -+ error!("ided: missing/invalid busmaster BAR: {err}"); -+ std::process::exit(1); -+ } -+ }; + 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: PCI native primary IDE channel is not supported yet"); -+ std::process::exit(1); ++ error!("ided: IDE primary channel PCI native mode not supported"); ++ process::exit(1); } else { - (Channel::primary_compat(busmaster_base).unwrap(), 14) -+ match Channel::primary_compat(busmaster_base) { -+ Ok(channel) => (channel, 14), -+ Err(err) => { -+ error!("ided: failed to initialize primary IDE channel: {err}"); -+ std::process::exit(1); -+ } -+ } ++ ( ++ 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: PCI native secondary IDE channel is not supported yet"); -+ std::process::exit(1); ++ error!("ided: IDE secondary channel PCI native mode not supported"); ++ process::exit(1); } else { - (Channel::secondary_compat(busmaster_base + 8).unwrap(), 15) -+ match Channel::secondary_compat(busmaster_base + 8) { -+ Ok(channel) => (channel, 15), -+ Err(err) => { -+ error!("ided: failed to initialize secondary IDE channel: {err}"); -+ std::process::exit(1); -+ } -+ } ++ ( ++ 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"); -+ if let Err(err) = common::acquire_port_io_rights() { ++ common::acquire_port_io_rights().unwrap_or_else(|err| { + error!("ided: failed to get I/O privilege: {err}"); -+ std::process::exit(1); -+ } ++ 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..8c79ba5e 100644 +index beb1b689..3772f4e5 100644 --- a/drivers/storage/nvmed/src/main.rs +++ b/drivers/storage/nvmed/src/main.rs -@@ -75,30 +75,62 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { +@@ -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 { - log::debug!("NVME PCI CONFIG: {:?}", pci_config); + 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); ++ }) + } -+ if let Err(err) = pci_config.func.bars[0].try_mem() { -+ log::error!("nvmed: invalid BAR0: {err}"); -+ std::process::exit(1); -+ } - let address = unsafe { pcid_handle.map_bar(0).ptr }; + 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 interrupt_vector = match irq_helpers::try_pci_allocate_interrupt_vector(&mut pcid_handle, "nvmed") { -+ Ok(vector) => vector, -+ Err(err) => { -+ log::error!("nvmed: failed to allocate interrupt vector: {err}"); -+ std::process::exit(1); -+ } -+ }; + 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 = match interrupt_vector.irq_handle().try_clone() { -+ Ok(handle) => handle, -+ Err(err) => { -+ log::error!("nvmed: failed to clone IRQ handle: {err}"); -+ std::process::exit(1); -+ } -+ }; ++ 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) + let mut nvme = Nvme::new(address.as_ptr() as usize, interrupt_vector, pcid_handle) - .expect("nvmed: failed to allocate driver data"); -+ let mut nvme = match Nvme::new(address.as_ptr() as usize, interrupt_vector, pcid_handle) { -+ Ok(nvme) => nvme, -+ Err(err) => { -+ log::error!("nvmed: failed to allocate driver data: {err}"); -+ std::process::exit(1); -+ } -+ }; - +- - unsafe { nvme.init().expect("nvmed: failed to init") } -+ if let Err(err) = unsafe { nvme.init() } { -+ log::error!("nvmed: failed to init: {err}"); -+ std::process::exit(1); ++ .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)) + let mut time_handle = File::open(&format!("/scheme/time/{}", libredox::flag::CLOCK_MONOTONIC)) - .expect("failed to open time handle"); -+ let mut time_handle = match File::open(&format!("/scheme/time/{}", libredox::flag::CLOCK_MONOTONIC)) { -+ Ok(handle) => handle, -+ Err(err) => { ++ .unwrap_or_else(|err| { + log::error!("nvmed: failed to open time handle: {err}"); -+ std::process::exit(1); -+ } -+ }; ++ process::exit(1); ++ }); let mut time_events = Box::pin( executor.register_external_event(time_handle.as_raw_fd() as usize, event::EventFlags::READ), @@ -9665,165 +12243,630 @@ index beb1b689..8c79ba5e 100644 // Try to init namespaces for 5 seconds - time_arm(&mut time_handle, 5).expect("failed to arm timer"); -+ if let Err(err) = time_arm(&mut time_handle, 5) { -+ log::error!("nvmed: failed to arm init timer: {err}"); -+ std::process::exit(1); -+ } ++ 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 +138,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { +@@ -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 waiting for queue initialization"); -+ std::process::exit(1); ++ log::error!("nvmed: timeout on init"); ++ process::exit(1); + } } }); log::debug!("Initialized!"); -diff --git a/drivers/storage/usbscsid/src/main.rs b/drivers/storage/usbscsid/src/main.rs -index 5382d118..3a403bd3 100644 ---- a/drivers/storage/usbscsid/src/main.rs -+++ b/drivers/storage/usbscsid/src/main.rs -@@ -3,7 +3,7 @@ use std::env; +@@ -134,7 +158,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { + event::EventFlags::READ, + )); - use driver_block::{Disk, DiskScheme, ExecutorTrait}; - use syscall::{Error, EIO}; --use xhcid_interface::{ConfigureEndpointsReq, PortId, XhciClientHandle}; -+use xhcid_interface::{PortId, XhciClientHandle}; +- 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); ++ }); - pub mod protocol; - pub mod scsi; -@@ -12,9 +12,9 @@ use crate::protocol::Protocol; - use crate::scsi::Scsi; + log::debug!("Starting to listen for scheme events"); - fn main() { -- daemon::Daemon::new(daemon); -+ run(); +@@ -150,5 +177,5 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { + + //TODO: destroy NVMe stuff + +- std::process::exit(0); ++ process::exit(0); } --fn daemon(daemon: daemon::Daemon) -> ! { -+fn run() -> ! { - let mut args = env::args().skip(1); +diff --git a/drivers/storage/nvmed/src/nvme/executor.rs b/drivers/storage/nvmed/src/nvme/executor.rs +index 6242fa98..c1435e88 100644 +--- a/drivers/storage/nvmed/src/nvme/executor.rs ++++ b/drivers/storage/nvmed/src/nvme/executor.rs +@@ -34,7 +34,12 @@ impl Hardware for NvmeHw { + &VTABLE + } + fn current() -> std::rc::Rc> { +- THE_EXECUTOR.with(|exec| Rc::clone(exec.borrow().as_ref().unwrap())) ++ THE_EXECUTOR.with(|exec| { ++ Rc::clone(exec.borrow().as_ref().unwrap_or_else(|| { ++ log::error!("nvmed: internal error: executor not initialized"); ++ std::process::exit(1); ++ })) ++ }) + } + fn try_submit( + nvme: &Arc, +diff --git a/drivers/storage/nvmed/src/nvme/identify.rs b/drivers/storage/nvmed/src/nvme/identify.rs +index 05e5b9b2..b1b6e959 100644 +--- a/drivers/storage/nvmed/src/nvme/identify.rs ++++ b/drivers/storage/nvmed/src/nvme/identify.rs +@@ -126,7 +126,7 @@ impl LbaFormat { + 0b01 => RelativePerformance::Better, + 0b10 => RelativePerformance::Good, + 0b11 => RelativePerformance::Degraded, +- _ => unreachable!(), ++ _ => RelativePerformance::Degraded, + } + } + pub fn is_available(&self) -> bool { +@@ -153,7 +153,14 @@ impl Nvme { + /// Returns the serial number, model, and firmware, in that order. + pub async fn identify_controller(&self) { + // TODO: Use same buffer +- let data: Dma = unsafe { Dma::zeroed().unwrap().assume_init() }; ++ let data: Dma = unsafe { ++ Dma::zeroed() ++ .map(|dma| dma.assume_init()) ++ .unwrap_or_else(|err| { ++ log::error!("nvmed: failed to allocate identify DMA: {err}"); ++ std::process::exit(1); ++ }) ++ }; - const USAGE: &'static str = "usbscsid "; -@@ -67,15 +67,6 @@ fn daemon(daemon: daemon::Daemon) -> ! { - }) - .expect("Failed to find suitable configuration"); + // println!(" - Attempting to identify controller"); + let comp = self +@@ -182,7 +189,14 @@ impl Nvme { + } + pub async fn identify_namespace_list(&self, base: u32) -> Vec { + // TODO: Use buffer +- let data: Dma<[u32; 1024]> = unsafe { Dma::zeroed().unwrap().assume_init() }; ++ let data: Dma<[u32; 1024]> = unsafe { ++ Dma::zeroed() ++ .map(|dma| dma.assume_init()) ++ .unwrap_or_else(|err| { ++ log::error!("nvmed: failed to allocate namespace list DMA: {err}"); ++ std::process::exit(1); ++ }) ++ }; -- handle -- .configure_endpoints(&ConfigureEndpointsReq { -- config_desc: configuration_value, -- interface_desc: Some(interface_num), -- alternate_setting: Some(alternate_setting), -- hub_ports: None, -- }) -- .expect("Failed to configure endpoints"); + // println!(" - Attempting to retrieve namespace ID list"); + let comp = self +@@ -198,7 +212,14 @@ impl Nvme { + } + pub async fn identify_namespace(&self, nsid: u32) -> NvmeNamespace { + //TODO: Use buffer +- let data: Dma = unsafe { Dma::zeroed().unwrap().assume_init() }; ++ let data: Dma = unsafe { ++ Dma::zeroed() ++ .map(|dma| dma.assume_init()) ++ .unwrap_or_else(|err| { ++ log::error!("nvmed: failed to allocate namespace DMA: {err}"); ++ std::process::exit(1); ++ }) ++ }; + + log::debug!("Attempting to identify namespace {nsid}"); + let comp = self +@@ -216,7 +237,10 @@ impl Nvme { + let block_size = data + .formatted_lba_size() + .lba_data_size() +- .expect("nvmed: error: size outside 512-2^64 range"); ++ .unwrap_or_else(|| { ++ log::error!("nvmed: error: size outside 512-2^64 range"); ++ std::process::exit(1); ++ }); + log::debug!("NVME block size: {}", block_size); + + NvmeNamespace { +diff --git a/drivers/storage/nvmed/src/nvme/mod.rs b/drivers/storage/nvmed/src/nvme/mod.rs +index 682ee933..90a25d5b 100644 +--- a/drivers/storage/nvmed/src/nvme/mod.rs ++++ b/drivers/storage/nvmed/src/nvme/mod.rs +@@ -160,7 +160,15 @@ impl Nvme { + } + fn cur_thread_ctxt(&self) -> Arc> { + // TODO: multi-threading +- Arc::clone(self.thread_ctxts.read().get(&0).unwrap()) ++ Arc::clone( ++ self.thread_ctxts ++ .read() ++ .get(&0) ++ .unwrap_or_else(|| { ++ log::error!("nvmed: internal error: thread context 0 missing"); ++ std::process::exit(1); ++ }), ++ ) + } + + pub unsafe fn submission_queue_tail(&self, qid: u16, tail: u16) { +@@ -208,10 +216,22 @@ impl Nvme { + } + + for (qid, iv) in self.cq_ivs.get_mut().iter_mut() { +- let ctxt = thread_ctxts.get(&0).unwrap().lock(); ++ let ctxt = match thread_ctxts.get(&0) { ++ Some(c) => c.lock(), ++ None => { ++ log::error!("nvmed: internal error: thread context 0 missing"); ++ return Err(Error::new(EIO)); ++ } ++ }; + let queues = ctxt.queues.borrow(); + +- let &(ref cq, ref sq) = queues.get(qid).unwrap(); ++ let (cq, sq) = match queues.get(qid) { ++ Some(pair) => pair, ++ None => { ++ log::error!("nvmed: internal error: queue {qid} missing"); ++ return Err(Error::new(EIO)); ++ } ++ }; + log::debug!( + "iv {iv} [cq {qid}: {:X}, {}] [sq {qid}: {:X}, {}]", + cq.data.physical(), +@@ -222,7 +242,13 @@ impl Nvme { + } + + { +- let main_ctxt = thread_ctxts.get(&0).unwrap().lock(); ++ let main_ctxt = match thread_ctxts.get(&0) { ++ Some(c) => c.lock(), ++ None => { ++ log::error!("nvmed: internal error: thread context 0 missing"); ++ return Err(Error::new(EIO)); ++ } ++ }; + + for (i, prp) in main_ctxt.buffer_prp.borrow_mut().iter_mut().enumerate() { + *prp = (main_ctxt.buffer.borrow_mut().physical() + i * 4096) as u64; +@@ -231,7 +257,13 @@ impl Nvme { + let regs = self.regs.get_mut(); + + let mut queues = main_ctxt.queues.borrow_mut(); +- let (asq, acq) = queues.get_mut(&0).unwrap(); ++ let (asq, acq) = match queues.get_mut(&0) { ++ Some(pair) => pair, ++ None => { ++ log::error!("nvmed: internal error: admin queue pair missing"); ++ return Err(Error::new(EIO)); ++ } ++ }; + regs.aqa + .write(((acq.data.len() as u32 - 1) << 16) | (asq.data.len() as u32 - 1)); + regs.asq_low.write(asq.data.physical() as u32); +@@ -281,14 +313,14 @@ impl Nvme { + let vector = vector as u8; + + if masked { +- assert_ne!( ++ debug_assert_ne!( + to_clear & (1 << vector), + (1 << vector), + "nvmed: internal error: cannot both mask and set" + ); + to_mask |= 1 << vector; + } else { +- assert_ne!( ++ debug_assert_ne!( + to_mask & (1 << vector), + (1 << vector), + "nvmed: internal error: cannot both mask and set" +@@ -326,22 +358,27 @@ impl Nvme { + cmd_init: impl FnOnce(CmdId) -> NvmeCmd, + fail: impl FnOnce(), + ) -> Option<(CqId, CmdId)> { +- match ctxt.queues.borrow_mut().get_mut(&sq_id).unwrap() { +- (sq, _cq) => { +- if sq.is_full() { +- fail(); +- return None; +- } +- let cmd_id = sq.tail; +- let tail = sq.submit_unchecked(cmd_init(cmd_id)); - - let mut protocol = protocol::setup(&handle, protocol, &desc, &conf_desc, &if_desc) - .expect("Failed to setup protocol"); +- // TODO: Submit in bulk +- unsafe { +- self.submission_queue_tail(sq_id, tail); +- } +- Some((sq_id, cmd_id)) ++ let mut queues_ref = ctxt.queues.borrow_mut(); ++ let (sq, _cq) = match queues_ref.get_mut(&sq_id) { ++ Some(pair) => pair, ++ None => { ++ log::error!("nvmed: internal error: submission queue {sq_id} missing"); ++ fail(); ++ return None; + } ++ }; ++ if sq.is_full() { ++ fail(); ++ return None; ++ } ++ let cmd_id = sq.tail; ++ let tail = sq.submit_unchecked(cmd_init(cmd_id)); ++ ++ // TODO: Submit in bulk ++ unsafe { ++ self.submission_queue_tail(sq_id, tail); + } ++ Some((sq_id, cmd_id)) + } -@@ -108,9 +99,6 @@ fn daemon(daemon: daemon::Daemon) -> ! { - &driver_block::FuturesExecutor, - ); - -- // FIXME should this wait notifying readiness until the disk scheme is created? -- daemon.ready(); + pub async fn create_io_completion_queue( +@@ -349,13 +386,19 @@ impl Nvme { + io_cq_id: CqId, + vector: Option, + ) -> NvmeCompQueue { +- let queue = NvmeCompQueue::new().expect("nvmed: failed to allocate I/O completion queue"); - - //libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace"); +- let len = u16::try_from(queue.data.len()) +- .expect("nvmed: internal error: I/O CQ longer than 2^16 entries"); +- let raw_len = len +- .checked_sub(1) +- .expect("nvmed: internal error: CQID 0 for I/O CQ"); ++ let queue = NvmeCompQueue::new().unwrap_or_else(|err| { ++ log::error!("nvmed: failed to allocate I/O completion queue: {err}"); ++ std::process::exit(1); ++ }); ++ ++ let len = u16::try_from(queue.data.len()).unwrap_or_else(|_| { ++ log::error!("nvmed: internal error: I/O CQ longer than 2^16 entries"); ++ std::process::exit(1); ++ }); ++ let raw_len = len.checked_sub(1).unwrap_or_else(|| { ++ log::error!("nvmed: internal error: CQID 0 for I/O CQ"); ++ std::process::exit(1); ++ }); - event_queue -diff --git a/drivers/storage/usbscsid/src/protocol/bot.rs b/drivers/storage/usbscsid/src/protocol/bot.rs -index b751d51a..b5d43cba 100644 ---- a/drivers/storage/usbscsid/src/protocol/bot.rs -+++ b/drivers/storage/usbscsid/src/protocol/bot.rs -@@ -103,16 +103,22 @@ impl<'a> BulkOnlyTransport<'a> { - ) -> Result { - let endpoints = &if_desc.endpoints; + let comp = self + .submit_and_complete_admin_command(|cid| { +@@ -370,22 +413,28 @@ impl Nvme { + .await; -- let bulk_in_num = (endpoints -+ let bulk_in_num = endpoints - .iter() -- .position(|endpoint| endpoint.direction() == EndpDirection::In) + /*match comp.status.specific { +- 1 => panic!("invalid queue identifier"), +- 2 => panic!("invalid queue size"), +- 8 => panic!("invalid interrupt vector"), ++ 1 => { log::error!("nvmed: invalid queue identifier"); std::process::exit(1); } ++ 2 => { log::error!("nvmed: invalid queue size"); std::process::exit(1); } ++ 8 => { log::error!("nvmed: invalid interrupt vector"); std::process::exit(1); } + _ => (), + }*/ + + queue + } + pub async fn create_io_submission_queue(&self, io_sq_id: SqId, io_cq_id: CqId) -> NvmeCmdQueue { +- let q = NvmeCmdQueue::new().expect("failed to create submission queue"); +- +- let len = u16::try_from(q.data.len()) +- .expect("nvmed: internal error: I/O SQ longer than 2^16 entries"); +- let raw_len = len +- .checked_sub(1) +- .expect("nvmed: internal error: SQID 0 for I/O SQ"); ++ let q = NvmeCmdQueue::new().unwrap_or_else(|err| { ++ log::error!("nvmed: failed to create submission queue: {err}"); ++ std::process::exit(1); ++ }); ++ ++ let len = u16::try_from(q.data.len()).unwrap_or_else(|_| { ++ log::error!("nvmed: internal error: I/O SQ longer than 2^16 entries"); ++ std::process::exit(1); ++ }); ++ let raw_len = len.checked_sub(1).unwrap_or_else(|| { ++ log::error!("nvmed: internal error: SQID 0 for I/O SQ"); ++ std::process::exit(1); ++ }); + + let comp = self + .submit_and_complete_admin_command(|cid| { +@@ -399,9 +448,9 @@ impl Nvme { + }) + .await; + /*match comp.status.specific { +- 0 => panic!("completion queue invalid"), +- 1 => panic!("invalid queue identifier"), +- 2 => panic!("invalid queue size"), ++ 0 => { log::error!("nvmed: completion queue invalid"); std::process::exit(1); } ++ 1 => { log::error!("nvmed: invalid queue identifier"); std::process::exit(1); } ++ 2 => { log::error!("nvmed: invalid queue size"); std::process::exit(1); } + _ => (), + }*/ + +@@ -431,7 +480,10 @@ impl Nvme { + self.thread_ctxts + .read() + .get(&0) - .unwrap() -- + 1) as u8; -- let bulk_out_num = (endpoints -+ .find(|endpoint| endpoint.direction() == EndpDirection::In) -+ .map(|endpoint| endpoint.address & 0x0F) -+ .filter(|num| *num != 0) -+ .ok_or(ProtocolError::ProtocolError( -+ "missing bulk-in endpoint descriptor", -+ ))?; -+ let bulk_out_num = endpoints - .iter() -- .position(|endpoint| endpoint.direction() == EndpDirection::Out) -- .unwrap() -- + 1) as u8; -+ .find(|endpoint| endpoint.direction() == EndpDirection::Out) -+ .map(|endpoint| endpoint.address & 0x0F) -+ .filter(|num| *num != 0) -+ .ok_or(ProtocolError::ProtocolError( -+ "missing bulk-out endpoint descriptor", -+ ))?; ++ .unwrap_or_else(|| { ++ log::error!("nvmed: internal error: thread context 0 missing"); ++ std::process::exit(1); ++ }) + .lock() + .queues + .borrow_mut() +@@ -497,8 +549,8 @@ impl Nvme { + for chunk in buf.chunks_mut(/* TODO: buf len */ 8192) { + let blocks = (chunk.len() + block_size - 1) / block_size; - let max_lun = get_max_lun(handle, 0)?; - println!("BOT_MAX_LUN {}", max_lun); -diff --git a/drivers/storage/usbscsid/src/protocol/mod.rs b/drivers/storage/usbscsid/src/protocol/mod.rs -index a580765f..62edac60 100644 ---- a/drivers/storage/usbscsid/src/protocol/mod.rs -+++ b/drivers/storage/usbscsid/src/protocol/mod.rs -@@ -68,14 +68,14 @@ use bot::BulkOnlyTransport; - pub fn setup<'a>( - handle: &'a XhciClientHandle, - protocol: u8, -- dev_desc: &DevDesc, -+ _dev_desc: &DevDesc, - conf_desc: &ConfDesc, - if_desc: &IfDesc, --) -> Option> { -+) -> Result, ProtocolError> { - match protocol { -- 0x50 => Some(Box::new( -- BulkOnlyTransport::init(handle, conf_desc, if_desc).unwrap(), -+ 0x50 => Ok(Box::new(BulkOnlyTransport::init(handle, conf_desc, if_desc)?)), -+ _ => Err(ProtocolError::ProtocolError( -+ "unsupported USB mass-storage transport protocol", - )), -- _ => None, +- assert!(blocks > 0); +- assert!(blocks <= 0x1_0000); ++ debug_assert!(blocks > 0); ++ debug_assert!(blocks <= 0x1_0000); + + self.namespace_rw(&*ctxt, namespace, lba, (blocks - 1) as u16, false) + .await?; +@@ -525,8 +577,8 @@ impl Nvme { + for chunk in buf.chunks(/* TODO: buf len */ 8192) { + let blocks = (chunk.len() + block_size - 1) / block_size; + +- assert!(blocks > 0); +- assert!(blocks <= 0x1_0000); ++ debug_assert!(blocks > 0); ++ debug_assert!(blocks <= 0x1_0000); + + ctxt.buffer.borrow_mut()[..chunk.len()].copy_from_slice(chunk); + +diff --git a/drivers/storage/nvmed/src/nvme/queues.rs b/drivers/storage/nvmed/src/nvme/queues.rs +index a3712aeb..438c905c 100644 +--- a/drivers/storage/nvmed/src/nvme/queues.rs ++++ b/drivers/storage/nvmed/src/nvme/queues.rs +@@ -145,7 +145,7 @@ impl Status { + 3 => Self::PathRelatedStatus(code), + 4..=6 => Self::Rsvd(code), + 7 => Self::Vendor(code), +- _ => unreachable!(), ++ _ => Self::Vendor(code), + } } } diff --git a/drivers/storage/virtio-blkd/src/main.rs b/drivers/storage/virtio-blkd/src/main.rs -index d21236b3..f66f725d 100644 +index d21236b3..2b777937 100644 --- a/drivers/storage/virtio-blkd/src/main.rs +++ b/drivers/storage/virtio-blkd/src/main.rs -@@ -103,7 +103,10 @@ fn main() { +@@ -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, + >::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::() + .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(); -+ if let Err(err) = daemon(redox_daemon, pcid_handle) { -+ log::error!("virtio-blkd: startup failed: {err}"); -+ std::process::exit(1); -+ } - unreachable!(); +- unreachable!(); ++ daemon(redox_daemon, pcid_handle).unwrap_or_else(|err| { ++ log::error!("virtio-blkd: daemon failed: {err}"); ++ process::exit(1); ++ }); ++ process::exit(0); } -@@ -121,7 +124,12 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow: + 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 { -+ return Err(anyhow::anyhow!( -+ "unexpected virtio-blk device id: {:04x}", -+ pci_config.func.full_device_id.device_id -+ )); ++ 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}"); ++ } ++ } + } + } + +diff --git a/drivers/storage/virtio-blkd/src/scheme.rs b/drivers/storage/virtio-blkd/src/scheme.rs +index ec4ecf73..39fb24a8 100644 +--- a/drivers/storage/virtio-blkd/src/scheme.rs ++++ b/drivers/storage/virtio-blkd/src/scheme.rs +@@ -15,19 +15,34 @@ trait BlkExtension { + + impl BlkExtension for Queue<'_> { + async fn read(&self, block: u64, target: &mut [u8]) -> usize { +- let req = Dma::new(BlockVirtRequest { ++ let req = match Dma::new(BlockVirtRequest { + ty: BlockRequestTy::In, + reserved: 0, + sector: block, +- }) +- .unwrap(); ++ }) { ++ Ok(req) => req, ++ Err(err) => { ++ log::error!("virtio-blkd: failed to allocate read request DMA: {err}"); ++ return 0; ++ } ++ }; + + let result = unsafe { +- Dma::<[u8]>::zeroed_slice(target.len()) +- .unwrap() +- .assume_init() ++ match Dma::<[u8]>::zeroed_slice(target.len()) { ++ Ok(dma) => dma.assume_init(), ++ Err(err) => { ++ log::error!("virtio-blkd: failed to allocate read buffer DMA: {err}"); ++ return 0; ++ } ++ } ++ }; ++ let status = match Dma::new(u8::MAX) { ++ Ok(s) => s, ++ Err(err) => { ++ log::error!("virtio-blkd: failed to allocate read status DMA: {err}"); ++ return 0; ++ } + }; +- let status = Dma::new(u8::MAX).unwrap(); + + let chain = ChainBuilder::new() + .chain(Buffer::new(&req)) +@@ -37,28 +52,46 @@ impl BlkExtension for Queue<'_> { + + // XXX: Subtract 1 because the of status byte. + let written = self.send(chain).await as usize - 1; +- assert_eq!(*status, 0); ++ if *status != 0 { ++ log::error!("virtio-blkd: read failed with status {}", *status); ++ return 0; ++ } + + target[..written].copy_from_slice(&result); + written + } + + async fn write(&self, block: u64, target: &[u8]) -> usize { +- let req = Dma::new(BlockVirtRequest { ++ let req = match Dma::new(BlockVirtRequest { + ty: BlockRequestTy::Out, + reserved: 0, + sector: block, +- }) +- .unwrap(); ++ }) { ++ Ok(req) => req, ++ Err(err) => { ++ log::error!("virtio-blkd: failed to allocate write request DMA: {err}"); ++ return 0; ++ } ++ }; + + let mut result = unsafe { +- Dma::<[u8]>::zeroed_slice(target.len()) +- .unwrap() +- .assume_init() ++ match Dma::<[u8]>::zeroed_slice(target.len()) { ++ Ok(dma) => dma.assume_init(), ++ Err(err) => { ++ log::error!("virtio-blkd: failed to allocate write buffer DMA: {err}"); ++ return 0; ++ } ++ } + }; + result.copy_from_slice(target.as_ref()); + +- let status = Dma::new(u8::MAX).unwrap(); ++ let status = match Dma::new(u8::MAX) { ++ Ok(s) => s, ++ Err(err) => { ++ log::error!("virtio-blkd: failed to allocate write status DMA: {err}"); ++ return 0; ++ } ++ }; + + let chain = ChainBuilder::new() + .chain(Buffer::new(&req)) +@@ -67,7 +100,10 @@ impl BlkExtension for Queue<'_> { + .build(); + + self.send(chain).await as usize; +- assert_eq!(*status, 0); ++ if *status != 0 { ++ log::error!("virtio-blkd: write failed with status {}", *status); ++ return 0; ++ } + + target.len() + } diff --git a/drivers/usb/usbctl/src/main.rs b/drivers/usb/usbctl/src/main.rs index 9b5773d9..232f7cfc 100644 --- a/drivers/usb/usbctl/src/main.rs @@ -12093,56 +15136,42 @@ index bcb9bb15..b9e42d4a 100644 + std::process::exit(1); } diff --git a/drivers/virtio-core/src/arch/x86.rs b/drivers/virtio-core/src/arch/x86.rs -index aea86c4a..d8595645 100644 +index aea86c4a..c5b2767f 100644 --- a/drivers/virtio-core/src/arch/x86.rs +++ b/drivers/virtio-core/src/arch/x86.rs -@@ -1,6 +1,8 @@ - use crate::transport::Error; - --use pcid_interface::irq_helpers::{allocate_single_interrupt_vector_for_msi, read_bsp_apic_id}; -+use pcid_interface::irq_helpers::{ -+ read_bsp_apic_id, try_allocate_single_interrupt_vector_for_msi, -+}; - use std::fs::File; - - use crate::MSIX_PRIMARY_VECTOR; -@@ -11,9 +13,10 @@ pub fn enable_msix(pcid_handle: &mut PciFunctionHandle) -> Result { +@@ -11,7 +11,10 @@ pub fn enable_msix(pcid_handle: &mut PciFunctionHandle) -> Result { // Extended message signaled interrupts. let msix_info = match pcid_handle.feature_info(PciFeature::MsiX) { PciFeatureInfo::MsiX(capability) => capability, - _ => unreachable!(), -+ _ => return Err(Error::MissingMsix), ++ _ => { ++ 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) }; -+ let mut info = unsafe { msix_info.try_map_and_mask_all(pcid_handle) } -+ .map_err(|err| Error::MsixSetup(format!("failed to map MSI-X registers: {err}")))?; + let mut info = unsafe { msix_info.map_and_mask_all(pcid_handle) }; - // Allocate the primary MSI vector. - // FIXME allow the driver to register multiple MSI-X vectors -@@ -21,9 +24,12 @@ pub fn enable_msix(pcid_handle: &mut PciFunctionHandle) -> Result { +@@ -21,7 +24,10 @@ pub fn enable_msix(pcid_handle: &mut PciFunctionHandle) -> Result { 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 (msg_addr_and_data, interrupt_handle) = -- allocate_single_interrupt_vector_for_msi(destination_id); -+ let destination_id = read_bsp_apic_id() -+ .map_err(|err| Error::MsixSetup(format!("failed to read BSP APIC ID: {err}")))?; -+ let (msg_addr_and_data, interrupt_handle) = try_allocate_single_interrupt_vector_for_msi( -+ destination_id, -+ ) -+ .map_err(|err| Error::MsixSetup(format!("failed to allocate MSI-X vector: {err}")))?; ++ 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); - table_entry_pointer.unmask(); - diff --git a/drivers/virtio-core/src/probe.rs b/drivers/virtio-core/src/probe.rs -index 5631ef67..3367586a 100644 +index 5631ef67..eaef1b96 100644 --- a/drivers/virtio-core/src/probe.rs +++ b/drivers/virtio-core/src/probe.rs -@@ -32,21 +32,21 @@ pub const MSIX_PRIMARY_VECTOR: u16 = 0; +@@ -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 { @@ -12153,21 +15182,15 @@ index 5631ef67..3367586a 100644 - "virtio_core::probe_device: not a virtio device" - ); + if pci_config.func.full_device_id.vendor_id != 6900 { -+ return Err(Error::NotVirtio); ++ 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; - let mut device_addr = None; - -- for raw_capability in pcid_handle.get_vendor_capabilities() { -+ for raw_capability in pcid_handle -+ .try_get_vendor_capabilities() -+ .map_err(|err| Error::MsixSetup(format!("failed to fetch vendor capabilities: {err}")))? -+ { - // SAFETY: We have verified that the length of the data is correct. - let capability = unsafe { &*(raw_capability.data.as_ptr() as *const PciCapability) }; - @@ -55,7 +55,9 @@ pub fn probe_device(pcid_handle: &mut PciFunctionHandle) -> Result continue, } @@ -12175,11 +15198,11 @@ index 5631ef67..3367586a 100644 - 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::MissingCapability("capability BAR"))?; ++ .map_err(|_| Error::Probe("BAR is not memory-mapped"))?; let address = unsafe { let addr = addr + capability.offset as usize; -@@ -100,19 +102,18 @@ pub fn probe_device(pcid_handle: &mut PciFunctionHandle) -> Result Result Result Result Result<(), Error> { - .insert_status(DeviceStatusFlags::ACKNOWLEDGE); +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 { + } - device.transport.insert_status(DeviceStatusFlags::DRIVER); -+ device.transport.finalize_features(); - Ok(()) + pub fn build(mut self) -> Vec { +- 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..4e116d2e 100644 +index d3445d2d..99972c95 100644 --- a/drivers/virtio-core/src/transport.rs +++ b/drivers/virtio-core/src/transport.rs -@@ -19,6 +19,20 @@ pub enum Error { +@@ -19,6 +19,8 @@ pub enum Error { SyscallError(#[from] libredox::error::Error), #[error("the device is incapable of {0:?}")] InCapable(CfgType), -+ #[error("device is not a virtio device")] -+ NotVirtio, -+ #[error("virtio capability `{0}` is missing")] -+ MissingCapability(&'static str), -+ #[error("virtio notify capability has an invalid zero multiplier")] -+ InvalidNotifyMultiplier, -+ #[error("device does not support MSI-X")] -+ MissingMsix, -+ #[error("MSI-X setup failed: {0}")] -+ MsixSetup(String), -+ #[error("virtio feature negotiation failed")] -+ FeaturesNotAccepted, -+ #[error("virtio queue operation failed: {0}")] -+ QueueSetup(&'static str), ++ #[error("virtio probe: {0}")] ++ Probe(&'static str), } /// Returns the queue part sizes in bytes. -@@ -238,6 +252,26 @@ impl<'a> Queue<'a> { - } - } +@@ -59,14 +61,23 @@ pub fn spawn_irq_thread(irq_handle: &File, queue: &Arc>) { + let queue_copy = queue.clone(); -+fn finalize_features_checked(transport: &StandardTransport<'_>) -> Result<(), Error> { -+ if !transport.check_device_feature(VIRTIO_F_VERSION_1) { -+ return Err(Error::FeaturesNotAccepted); -+ } -+ transport.ack_driver_feature(VIRTIO_F_VERSION_1); -+ -+ let mut common = transport.common.lock().unwrap(); -+ -+ let status = common.device_status.get(); -+ common -+ .device_status -+ .set(status | DeviceStatusFlags::FEATURES_OK); -+ -+ let confirm = common.device_status.get(); -+ if (confirm & DeviceStatusFlags::FEATURES_OK) != DeviceStatusFlags::FEATURES_OK { -+ return Err(Error::FeaturesNotAccepted); -+ } -+ Ok(()) -+} -+ - unsafe impl Sync for Queue<'_> {} - unsafe impl Send for Queue<'_> {} + 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; ++ } ++ }; -@@ -590,21 +624,8 @@ impl Transport for StandardTransport<'_> { - } +- 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; ++ } - fn finalize_features(&self) { -- // Check VirtIO version 1 compliance. -- assert!(self.check_device_feature(VIRTIO_F_VERSION_1)); -- self.ack_driver_feature(VIRTIO_F_VERSION_1); -- -- let mut common = self.common.lock().unwrap(); -- -- let status = common.device_status.get(); -- common -- .device_status -- .set(status | DeviceStatusFlags::FEATURES_OK); -- -- // 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(); +- 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); -+ finalize_features_checked(self) -+ .unwrap_or_else(|err| panic!("{err}")) ++ 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 +661,9 @@ impl Transport for StandardTransport<'_> { +@@ -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 { -+ return Err(Error::QueueSetup("queue MSI-X vector was not accepted")); ++ 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 +708,9 @@ impl Transport for StandardTransport<'_> { +@@ -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 { -+ panic!("virtio queue MSI-X vector was not accepted during reinit"); ++ log::error!("virtio-core: MSI-X vector {:#x} was not accepted during reinit for queue {}", queue.vector, queue.queue_index); + } // Enable the queue.