76b75d80b4
Consolidate ~30 absorbed base patches into surviving carriers. Add new init service files, driver sources, and network/storage modules for the base recipe. Move absorbed patches to local/patches/base/absorbed/. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
374 lines
14 KiB
Diff
374 lines
14 KiB
Diff
--- a/drivers/usb/xhcid/src/main.rs
|
|
+++ b/drivers/usb/xhcid/src/main.rs
|
|
@@ -33,7 +33,7 @@
|
|
use pcid_interface::irq_helpers::read_bsp_apic_id;
|
|
#[cfg(target_arch = "x86_64")]
|
|
use pcid_interface::irq_helpers::{
|
|
- allocate_first_msi_interrupt_on_bsp, allocate_single_interrupt_vector_for_msi,
|
|
+ try_allocate_first_msi_interrupt_on_bsp, try_allocate_single_interrupt_vector_for_msi,
|
|
};
|
|
use pcid_interface::{PciFeature, PciFeatureInfo, PciFunctionHandle};
|
|
|
|
@@ -61,11 +61,24 @@
|
|
let has_msix = all_pci_features.iter().any(|feature| feature.is_msix());
|
|
|
|
if has_msix {
|
|
- let msix_info = match pcid_handle.feature_info(PciFeature::MsiX) {
|
|
- PciFeatureInfo::Msi(_) => panic!(),
|
|
- PciFeatureInfo::MsiX(s) => s,
|
|
+ let msix_info = match pcid_handle.try_feature_info(PciFeature::MsiX) {
|
|
+ Ok(PciFeatureInfo::MsiX(s)) => s,
|
|
+ Ok(PciFeatureInfo::Msi(_)) => {
|
|
+ log::error!("xhcid: invalid MSI-X feature response payload");
|
|
+ return (None, InterruptMethod::Polling);
|
|
+ }
|
|
+ Err(err) => {
|
|
+ log::error!("xhcid: failed to fetch MSI-X feature info: {err}");
|
|
+ return (None, InterruptMethod::Polling);
|
|
+ }
|
|
};
|
|
- let mut info = unsafe { msix_info.map_and_mask_all(pcid_handle) };
|
|
+ let mut info = match unsafe { msix_info.try_map_and_mask_all(pcid_handle) } {
|
|
+ Ok(info) => info,
|
|
+ Err(err) => {
|
|
+ log::error!("xhcid: failed to map MSI-X registers: {err}");
|
|
+ return (None, InterruptMethod::Polling);
|
|
+ }
|
|
+ };
|
|
|
|
// Allocate one msi vector.
|
|
|
|
@@ -75,27 +88,53 @@
|
|
|
|
let table_entry_pointer = info.table_entry_pointer(k);
|
|
|
|
- let destination_id = read_bsp_apic_id().expect("xhcid: failed to read BSP apic id");
|
|
+ let destination_id = match read_bsp_apic_id() {
|
|
+ Ok(id) => id,
|
|
+ Err(err) => {
|
|
+ log::error!("xhcid: failed to read BSP APIC ID: {err}");
|
|
+ return (None, InterruptMethod::Polling);
|
|
+ }
|
|
+ };
|
|
let (msg_addr_and_data, interrupt_handle) =
|
|
- allocate_single_interrupt_vector_for_msi(destination_id);
|
|
+ match try_allocate_single_interrupt_vector_for_msi(destination_id) {
|
|
+ Ok(result) => result,
|
|
+ Err(err) => {
|
|
+ log::error!("xhcid: failed to allocate MSI-X vector: {err}");
|
|
+ return (None, InterruptMethod::Polling);
|
|
+ }
|
|
+ };
|
|
table_entry_pointer.write_addr_and_data(msg_addr_and_data);
|
|
table_entry_pointer.unmask();
|
|
|
|
(Some(interrupt_handle), InterruptMethod::Msi)
|
|
};
|
|
|
|
- pcid_handle.enable_feature(PciFeature::MsiX);
|
|
+ if let Err(err) = pcid_handle.try_enable_feature(PciFeature::MsiX) {
|
|
+ log::error!("xhcid: failed to enable MSI-X: {err}");
|
|
+ return (None, InterruptMethod::Polling);
|
|
+ }
|
|
log::debug!("Enabled MSI-X");
|
|
|
|
method
|
|
} else if has_msi {
|
|
- let interrupt_handle = allocate_first_msi_interrupt_on_bsp(pcid_handle);
|
|
- (Some(interrupt_handle), InterruptMethod::Msi)
|
|
+ match try_allocate_first_msi_interrupt_on_bsp(pcid_handle) {
|
|
+ Ok(interrupt_handle) => (Some(interrupt_handle), InterruptMethod::Msi),
|
|
+ Err(err) => {
|
|
+ log::error!("xhcid: failed to allocate MSI interrupt: {err}");
|
|
+ (None, InterruptMethod::Polling)
|
|
+ }
|
|
+ }
|
|
} else if let Some(irq) = pci_config.func.legacy_interrupt_line {
|
|
log::debug!("Legacy IRQ {}", irq);
|
|
|
|
// legacy INTx# interrupt pins.
|
|
- (Some(irq.irq_handle("xhcid")), InterruptMethod::Intx)
|
|
+ match irq.try_irq_handle("xhcid") {
|
|
+ Ok(file) => (Some(file), InterruptMethod::Intx),
|
|
+ Err(err) => {
|
|
+ log::error!("xhcid: failed to open legacy IRQ handle: {err}");
|
|
+ (None, InterruptMethod::Polling)
|
|
+ }
|
|
+ }
|
|
} else {
|
|
// no interrupts at all
|
|
(None, InterruptMethod::Polling)
|
|
@@ -109,7 +148,13 @@
|
|
|
|
if let Some(irq) = pci_config.func.legacy_interrupt_line {
|
|
// legacy INTx# interrupt pins.
|
|
- (Some(irq.irq_handle("xhcid")), InterruptMethod::Intx)
|
|
+ match irq.try_irq_handle("xhcid") {
|
|
+ Ok(file) => (Some(file), InterruptMethod::Intx),
|
|
+ Err(err) => {
|
|
+ log::error!("xhcid: failed to open legacy IRQ handle: {err}");
|
|
+ (None, InterruptMethod::Polling)
|
|
+ }
|
|
+ }
|
|
} else {
|
|
// no interrupts at all
|
|
(None, InterruptMethod::Polling)
|
|
@@ -136,23 +181,48 @@
|
|
|
|
log::debug!("XHCI PCI CONFIG: {:?}", pci_config);
|
|
|
|
- let address = unsafe { pcid_handle.map_bar(0) }.ptr.as_ptr() as usize;
|
|
-
|
|
- let (irq_file, interrupt_method) = (None, InterruptMethod::Polling); //get_int_method(&mut pcid_handle);
|
|
- //TODO: Fix interrupts.
|
|
+ let address = match unsafe { pcid_handle.try_map_bar(0) } {
|
|
+ Ok(bar) => bar.ptr.as_ptr() as usize,
|
|
+ Err(err) => {
|
|
+ log::error!("xhcid: failed to map BAR0: {err}");
|
|
+ std::process::exit(1);
|
|
+ }
|
|
+ };
|
|
+
|
|
+ let (irq_file, interrupt_method) = get_int_method(&mut pcid_handle);
|
|
+
|
|
+ match interrupt_method {
|
|
+ InterruptMethod::Msi => log::info!("xhcid: using MSI/MSI-X interrupt delivery"),
|
|
+ InterruptMethod::Intx => log::info!("xhcid: using legacy INTx interrupt delivery"),
|
|
+ InterruptMethod::Polling => log::warn!("xhcid: falling back to polling mode"),
|
|
+ }
|
|
|
|
log::info!("XHCI {}", pci_config.func.display());
|
|
|
|
let scheme_name = format!("usb.{}", name);
|
|
- let socket = Socket::create().expect("xhcid: failed to create usb scheme");
|
|
+ let socket = match Socket::create() {
|
|
+ Ok(socket) => socket,
|
|
+ Err(err) => {
|
|
+ log::error!("xhcid: failed to create usb scheme: {err}");
|
|
+ std::process::exit(1);
|
|
+ }
|
|
+ };
|
|
let handler = Blocking::new(&socket, 16);
|
|
|
|
let hci = Arc::new(
|
|
- Xhci::<N>::new(scheme_name.clone(), address, interrupt_method, pcid_handle)
|
|
- .expect("xhcid: failed to allocate device"),
|
|
+ match Xhci::<N>::new(scheme_name.clone(), address, interrupt_method, pcid_handle) {
|
|
+ Ok(hci) => hci,
|
|
+ Err(err) => {
|
|
+ log::error!("xhcid: failed to allocate device: {err}");
|
|
+ std::process::exit(1);
|
|
+ }
|
|
+ },
|
|
);
|
|
register_sync_scheme(&socket, &scheme_name, &mut &*hci)
|
|
- .expect("xhcid: failed to regsiter scheme to namespace");
|
|
+ .unwrap_or_else(|err| {
|
|
+ log::error!("xhcid: failed to register scheme to namespace: {err}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
|
|
daemon.ready();
|
|
|
|
@@ -163,7 +233,10 @@
|
|
|
|
handler
|
|
.process_requests_blocking(&*hci)
|
|
- .expect("xhcid: failed to process requests");
|
|
+ .unwrap_or_else(|err| {
|
|
+ log::error!("xhcid: failed to process requests: {err}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
}
|
|
|
|
fn main() {
|
|
@@ -171,7 +244,13 @@
|
|
}
|
|
|
|
fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
|
- 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!("xhcid: failed to map BAR0: {err}");
|
|
+ std::process::exit(1);
|
|
+ }
|
|
+ };
|
|
let cap = unsafe { &mut *(address as *mut xhci::CapabilityRegs) };
|
|
if cap.csz() {
|
|
daemon_with_context_size::<{ xhci::CONTEXT_64 }>(daemon, pcid_handle)
|
|
|
|
--- a/drivers/usb/xhcid/src/xhci/device_enumerator.rs
|
|
+++ b/drivers/usb/xhcid/src/xhci/device_enumerator.rs
|
|
@@ -4,8 +4,10 @@
|
|
use crossbeam_channel;
|
|
use log::{debug, info, warn};
|
|
use std::sync::Arc;
|
|
-use std::time::Duration;
|
|
+use std::time::{Duration, Instant};
|
|
use syscall::EAGAIN;
|
|
+
|
|
+const DEFAULT_PORT_RESET_SETTLE_MS: u64 = 16;
|
|
|
|
pub struct DeviceEnumerationRequest {
|
|
pub port_id: PortId,
|
|
@@ -28,7 +30,11 @@
|
|
let request = match self.request_queue.recv() {
|
|
Ok(req) => req,
|
|
Err(err) => {
|
|
- panic!("Failed to received an enumeration request! error: {}", err)
|
|
+ warn!(
|
|
+ "device enumerator stopping after request queue closed: {}",
|
|
+ err
|
|
+ );
|
|
+ break;
|
|
}
|
|
};
|
|
|
|
@@ -38,7 +44,11 @@
|
|
debug!("Device Enumerator request for port {}", port_id);
|
|
|
|
let (len, flags) = {
|
|
- let ports = self.hci.ports.lock().unwrap();
|
|
+ let ports = self
|
|
+ .hci
|
|
+ .ports
|
|
+ .lock()
|
|
+ .unwrap_or_else(|poisoned| poisoned.into_inner());
|
|
|
|
let len = ports.len();
|
|
|
|
@@ -62,43 +72,52 @@
|
|
//A USB3 port won't generate a Connect Status Change until it's already enabled, so this check
|
|
//will always be skipped for USB3 ports
|
|
if !flags.contains(PortFlags::PED) {
|
|
- let disabled_state = flags.contains(PortFlags::PP)
|
|
- && flags.contains(PortFlags::CCS)
|
|
- && !flags.contains(PortFlags::PED)
|
|
- && !flags.contains(PortFlags::PR);
|
|
+ let disabled_state = Self::port_is_disabled(&flags);
|
|
|
|
if !disabled_state {
|
|
- panic!(
|
|
- "Port {} isn't in the disabled state! Current flags: {:?}",
|
|
+ warn!(
|
|
+ "Port {} never reached the disabled state before reset-driven enumeration; current flags: {:?}",
|
|
port_id, flags
|
|
);
|
|
+ continue;
|
|
} else {
|
|
debug!("Port {} has entered the disabled state.", port_id);
|
|
}
|
|
|
|
//THIS LOCKS THE PORTS. DO NOT LOCK PORTS BEFORE THIS POINT
|
|
debug!("Received a device connect on port {}, but it's not enabled. Resetting the port.", port_id);
|
|
- let _ = self.hci.reset_port(port_id);
|
|
-
|
|
- let mut ports = self.hci.ports.lock().unwrap();
|
|
+ if let Err(err) = self.hci.reset_port(port_id) {
|
|
+ warn!(
|
|
+ "failed to reset port {} before enumeration; skipping attach: {}",
|
|
+ port_id, err
|
|
+ );
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ let mut ports = self
|
|
+ .hci
|
|
+ .ports
|
|
+ .lock()
|
|
+ .unwrap_or_else(|poisoned| poisoned.into_inner());
|
|
let port = &mut ports[port_array_index];
|
|
|
|
port.clear_prc();
|
|
|
|
- std::thread::sleep(Duration::from_millis(16)); //Some controllers need some extra time to make the transition.
|
|
-
|
|
- let flags = port.flags();
|
|
-
|
|
- let enabled_state = flags.contains(PortFlags::PP)
|
|
- && flags.contains(PortFlags::CCS)
|
|
- && flags.contains(PortFlags::PED)
|
|
- && !flags.contains(PortFlags::PR);
|
|
+ drop(ports);
|
|
+
|
|
+ let flags = self.wait_for_port_enabled_state(
|
|
+ port_array_index,
|
|
+ Duration::from_millis(DEFAULT_PORT_RESET_SETTLE_MS),
|
|
+ );
|
|
+
|
|
+ let enabled_state = Self::port_is_enabled(&flags);
|
|
|
|
if !enabled_state {
|
|
warn!(
|
|
- "Port {} isn't in the enabled state! Current flags: {:?}",
|
|
+ "Port {} isn't in the enabled state after bounded reset settle; current flags: {:?}",
|
|
port_id, flags
|
|
);
|
|
+ continue;
|
|
} else {
|
|
debug!(
|
|
"Port {} is in the enabled state. Proceeding with enumeration",
|
|
@@ -131,13 +150,60 @@
|
|
Ok(was_connected) => {
|
|
if was_connected {
|
|
info!("Device on port {} was detached", port_id);
|
|
+ } else {
|
|
+ debug!(
|
|
+ "Ignoring duplicate or out-of-order detach event for unattached port {}",
|
|
+ port_id
|
|
+ );
|
|
}
|
|
}
|
|
Err(err) => {
|
|
- warn!("processing of device attach request failed! Error: {}", err);
|
|
+ warn!("processing of device detach request failed! Error: {}", err);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
+
|
|
+ fn port_is_disabled(flags: &PortFlags) -> bool {
|
|
+ flags.contains(PortFlags::PP)
|
|
+ && flags.contains(PortFlags::CCS)
|
|
+ && !flags.contains(PortFlags::PED)
|
|
+ && !flags.contains(PortFlags::PR)
|
|
+ }
|
|
+
|
|
+ fn port_is_enabled(flags: &PortFlags) -> bool {
|
|
+ flags.contains(PortFlags::PP)
|
|
+ && flags.contains(PortFlags::CCS)
|
|
+ && flags.contains(PortFlags::PED)
|
|
+ && !flags.contains(PortFlags::PR)
|
|
+ }
|
|
+
|
|
+ fn wait_for_port_enabled_state(
|
|
+ &self,
|
|
+ port_array_index: usize,
|
|
+ settle_timeout: Duration,
|
|
+ ) -> PortFlags {
|
|
+ let start = Instant::now();
|
|
+
|
|
+ loop {
|
|
+ let flags = {
|
|
+ let ports = self
|
|
+ .hci
|
|
+ .ports
|
|
+ .lock()
|
|
+ .unwrap_or_else(|poisoned| poisoned.into_inner());
|
|
+ ports[port_array_index].flags()
|
|
+ };
|
|
+
|
|
+ if Self::port_is_enabled(&flags)
|
|
+ || !flags.contains(PortFlags::PR)
|
|
+ || start.elapsed() >= settle_timeout
|
|
+ {
|
|
+ return flags;
|
|
+ }
|
|
+
|
|
+ std::thread::sleep(Duration::from_millis(1));
|
|
+ }
|
|
+ }
|
|
}
|