From 6c418bb03b7f1ee50b30330b9329b0c381e77bfa Mon Sep 17 00:00:00 2001 From: Vasilito Date: Thu, 16 Apr 2026 12:43:10 +0100 Subject: [PATCH] Update Red Bear driver substrate Red Bear OS Team --- local/patches/base/redox.patch | 537 ++++++++++++++++++ local/patches/kernel/redox.patch | 37 ++ .../source/src/c_headers/linux/device.h | 8 +- .../source/src/c_headers/linux/firmware.h | 3 + .../source/src/c_headers/linux/interrupt.h | 29 +- .../source/src/c_headers/linux/mutex.h | 7 +- .../source/src/c_headers/linux/pci.h | 4 +- .../source/src/c_headers/linux/timer.h | 40 +- .../drivers/linux-kpi/source/src/lib.rs | 6 + .../linux-kpi/source/src/rust_impl/device.rs | 15 +- .../source/src/rust_impl/firmware.rs | 266 +++++++-- .../linux-kpi/source/src/rust_impl/memory.rs | 18 + .../linux-kpi/source/src/rust_impl/mod.rs | 3 + .../linux-kpi/source/src/rust_impl/pci.rs | 8 +- .../linux-kpi/source/src/rust_impl/sync.rs | 54 ++ .../linux-kpi/source/src/rust_impl/timer.rs | 14 +- .../redox-driver-sys/source/src/dma.rs | 24 +- .../drivers/redox-driver-sys/source/src/io.rs | 1 + .../redox-driver-sys/source/src/lib.rs | 18 +- .../redox-driver-sys/source/src/memory.rs | 28 +- .../redox-driver-sys/source/src/pci.rs | 21 +- mk/prefix.mk | 8 + mk/qemu.mk | 2 +- 23 files changed, 982 insertions(+), 169 deletions(-) diff --git a/local/patches/base/redox.patch b/local/patches/base/redox.patch index 943be899..75f3e8a7 100644 --- a/local/patches/base/redox.patch +++ b/local/patches/base/redox.patch @@ -1353,3 +1353,540 @@ diff --git a/drivers/usb/xhcid/src/xhci/scheme.rs b/drivers/usb/xhcid/src/xhci/s + endpoint_count + ); + } +diff --git a/drivers/audio/ac97d/src/main.rs b/drivers/audio/ac97d/src/main.rs +--- a/drivers/audio/ac97d/src/main.rs ++++ b/drivers/audio/ac97d/src/main.rs +@@ + fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! { + let pci_config = pcid_handle.config(); + + let mut name = pci_config.func.name(); + name.push_str("_ac97"); ++ ++ common::setup_logging( ++ "audio", ++ "pci", ++ &name, ++ common::output_level(), ++ common::file_level(), ++ ); ++ ++ let bar0 = match pci_config.func.bars[0] { ++ pcid_interface::PciBar::Port(port) => port, ++ ref other => { ++ log::warn!( ++ "ac97d: unsupported BAR0 layout for {}: expected port BAR, found {}", ++ pci_config.func.display(), ++ other.display(), ++ ); ++ std::process::exit(0); ++ } ++ }; ++ let bar1 = match pci_config.func.bars[1] { ++ pcid_interface::PciBar::Port(port) => port, ++ ref other => { ++ log::warn!( ++ "ac97d: unsupported BAR1 layout for {}: expected port BAR, found {}", ++ pci_config.func.display(), ++ other.display(), ++ ); ++ std::process::exit(0); ++ } ++ }; +- +- let bar0 = pci_config.func.bars[0].expect_port(); +- let bar1 = pci_config.func.bars[1].expect_port(); +@@ + println!(" + ac97 {}", pci_config.func.display()); +- +- common::setup_logging( +- "audio", +- "pci", +- &name, +- common::output_level(), +- common::file_level(), +- ); +diff --git a/drivers/input/ps2d/src/main.rs b/drivers/input/ps2d/src/main.rs +--- a/drivers/input/ps2d/src/main.rs ++++ b/drivers/input/ps2d/src/main.rs +@@ + use common::acquire_port_io_rights; + use event::{user_data, EventQueue}; + use inputd::ProducerHandle; ++use log::{error, warn}; +@@ + mod controller; + mod mouse; + mod state; + mod vm; ++ ++fn exit_bootsafe(daemon: &mut Option, reason: &str) -> ! { ++ warn!("ps2d: {}; disabling PS/2 input for this boot", reason); ++ if let Some(daemon) = daemon.take() { ++ daemon.ready(); ++ } ++ process::exit(0); ++} +@@ ++ let mut daemon = Some(daemon); ++ ++ if let Err(err) = acquire_port_io_rights() { ++ exit_bootsafe(&mut daemon, &format!("failed to get I/O permission: {err}")); ++ } + +- let input = ProducerHandle::new().expect("ps2d: failed to open input producer"); ++ let input = match ProducerHandle::new() { ++ Ok(input) => input, ++ Err(err) => exit_bootsafe(&mut daemon, &format!("failed to open input producer: {err}")), ++ }; +@@ +- let event_queue: EventQueue = +- EventQueue::new().expect("ps2d: failed to create event queue"); ++ let event_queue: EventQueue = match EventQueue::new() { ++ Ok(event_queue) => event_queue, ++ Err(err) => exit_bootsafe(&mut daemon, &format!("failed to create event queue: {err}")), ++ }; +@@ +- .open("/scheme/serio/0") +- .expect("ps2d: failed to open /scheme/serio/0"); ++ .open("/scheme/serio/0") ++ .unwrap_or_else(|err| { ++ exit_bootsafe(&mut daemon, &format!("failed to open /scheme/serio/0: {err}")) ++ }); + +- event_queue +- .subscribe( +- key_file.as_raw_fd() as usize, +- Source::Keyboard, +- event::EventFlags::READ, +- ) +- .unwrap(); ++ if let Err(err) = event_queue.subscribe( ++ key_file.as_raw_fd() as usize, ++ Source::Keyboard, ++ event::EventFlags::READ, ++ ) { ++ exit_bootsafe(&mut daemon, &format!("failed to subscribe keyboard serio fd: {err}")); ++ } +@@ +- .open("/scheme/serio/1") +- .expect("ps2d: failed to open /scheme/serio/1"); ++ .open("/scheme/serio/1") ++ .unwrap_or_else(|err| { ++ exit_bootsafe(&mut daemon, &format!("failed to open /scheme/serio/1: {err}")) ++ }); + +- event_queue +- .subscribe( +- mouse_file.as_raw_fd() as usize, +- Source::Mouse, +- event::EventFlags::READ, +- ) +- .unwrap(); ++ if let Err(err) = event_queue.subscribe( ++ mouse_file.as_raw_fd() as usize, ++ Source::Mouse, ++ event::EventFlags::READ, ++ ) { ++ exit_bootsafe(&mut daemon, &format!("failed to subscribe mouse serio fd: {err}")); ++ } +@@ +- .open(format!("/scheme/time/{}", syscall::CLOCK_MONOTONIC)) +- .expect("ps2d: failed to open /scheme/time"); ++ .open(format!("/scheme/time/{}", syscall::CLOCK_MONOTONIC)) ++ .unwrap_or_else(|err| { ++ exit_bootsafe(&mut daemon, &format!("failed to open /scheme/time: {err}")) ++ }); + +- event_queue +- .subscribe( +- time_file.as_raw_fd() as usize, +- Source::Time, +- event::EventFlags::READ, +- ) +- .unwrap(); ++ if let Err(err) = event_queue.subscribe( ++ time_file.as_raw_fd() as usize, ++ Source::Time, ++ event::EventFlags::READ, ++ ) { ++ exit_bootsafe(&mut daemon, &format!("failed to subscribe timer fd: {err}")); ++ } + +- libredox::call::setrens(0, 0).expect("ps2d: failed to enter null namespace"); ++ if let Err(err) = libredox::call::setrens(0, 0) { ++ exit_bootsafe(&mut daemon, &format!("failed to enter null namespace: {err}")); ++ } + +- daemon.ready(); +- +- let mut ps2d = Ps2d::new(input, time_file); ++ let mut ps2d = match Ps2d::new(input, time_file) { ++ Ok(ps2d) => ps2d, ++ Err(err) => exit_bootsafe( ++ &mut daemon, ++ &format!("PS/2 controller initialization failed: {err:?}"), ++ ), ++ }; ++ ++ daemon.take().unwrap().ready(); + + let mut data = [0; 256]; +- for event in event_queue.map(|e| e.expect("ps2d: failed to get next event").user_data) { ++ for event in event_queue { ++ let event = match event { ++ Ok(event) => event.user_data, ++ Err(err) => { ++ error!("ps2d: failed to get next event: {err}"); ++ break; ++ } ++ }; + // There are some gotchas with ps/2 controllers that require this weird +diff --git a/drivers/input/ps2d/src/state.rs b/drivers/input/ps2d/src/state.rs +--- a/drivers/input/ps2d/src/state.rs ++++ b/drivers/input/ps2d/src/state.rs +@@ +-use crate::controller::Ps2; ++use crate::controller::{Error as Ps2Error, Ps2}; +@@ + impl Ps2d { +- pub fn new(input: ProducerHandle, time_file: File) -> Self { ++ pub fn new(input: ProducerHandle, time_file: File) -> Result { + let mut ps2 = Ps2::new(); +- ps2.init().expect("failed to initialize"); ++ ps2.init()?; +@@ + if !this.vmmouse { + // This triggers initializing the mouse + this.handle_mouse(None); + } + +- this ++ Ok(this) + } +diff --git a/drivers/usb/xhcid/src/main.rs b/drivers/usb/xhcid/src/main.rs +--- a/drivers/usb/xhcid/src/main.rs ++++ b/drivers/usb/xhcid/src/main.rs +@@ ++use std::convert::TryFrom; + use std::fs::File; ++use std::io; + use std::sync::Arc; + + 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, +-}; ++use pcid_interface::irq_helpers::allocate_single_interrupt_vector; + use pcid_interface::{PciFeature, PciFeatureInfo, PciFunctionHandle}; ++#[cfg(target_arch = "x86_64")] ++use pcid_interface::{MsiSetFeatureInfo, SetFeatureInfo}; +@@ + mod usb; + mod xhci; + ++#[cfg(target_arch = "x86_64")] ++fn allocate_msix_interrupt( ++ pcid_handle: &mut PciFunctionHandle, ++ msix_info: pcid_interface::msi::MsixInfo, ++) -> io::Result { ++ let mut info = unsafe { msix_info.map_and_mask_all(pcid_handle) }; ++ let destination_id = read_bsp_apic_id()?; ++ let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id)? ++ .ok_or_else(|| io::Error::new(io::ErrorKind::WouldBlock, "no interrupt vectors left"))?; ++ let lapic_id = u8::try_from(destination_id) ++ .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "BSP apic id out of range"))?; ++ let msg_addr_and_data = pcid_interface::msi::MsiAddrAndData { ++ addr: pcid_interface::msi::x86::message_address(lapic_id, false, false), ++ data: pcid_interface::msi::x86::message_data_edge_triggered( ++ pcid_interface::msi::x86::DeliveryMode::Fixed, ++ vector, ++ ), ++ }; ++ ++ let table_entry_pointer = info.table_entry_pointer(0); ++ table_entry_pointer.write_addr_and_data(msg_addr_and_data); ++ table_entry_pointer.unmask(); ++ ++ pcid_handle.enable_feature(PciFeature::MsiX); ++ log::debug!("Enabled MSI-X"); ++ ++ Ok(interrupt_handle) ++} ++ ++#[cfg(target_arch = "x86_64")] ++fn allocate_msi_interrupt(pcid_handle: &mut PciFunctionHandle) -> io::Result { ++ let destination_id = read_bsp_apic_id()?; ++ let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id)? ++ .ok_or_else(|| io::Error::new(io::ErrorKind::WouldBlock, "no interrupt vectors left"))?; ++ let lapic_id = u8::try_from(destination_id) ++ .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "BSP apic id out of range"))?; ++ let set_feature_info = MsiSetFeatureInfo { ++ multi_message_enable: Some(0), ++ message_address_and_data: Some(pcid_interface::msi::MsiAddrAndData { ++ addr: pcid_interface::msi::x86::message_address(lapic_id, false, false), ++ data: pcid_interface::msi::x86::message_data_edge_triggered( ++ pcid_interface::msi::x86::DeliveryMode::Fixed, ++ vector, ++ ), ++ }), ++ mask_bits: None, ++ }; ++ ++ pcid_handle.set_feature_info(SetFeatureInfo::Msi(set_feature_info)); ++ pcid_handle.enable_feature(PciFeature::Msi); ++ log::debug!("Enabled MSI"); ++ ++ Ok(interrupt_handle) ++} +@@ + let has_msi = all_pci_features.iter().any(|feature| feature.is_msi()); + 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 mut info = unsafe { msix_info.map_and_mask_all(pcid_handle) }; +- +- // Allocate one msi vector. +- +- let method = { +- // primary interrupter +- let k = 0; +- +- 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 (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(); +- +- (Some(interrupt_handle), InterruptMethod::Msi) +- }; +- +- pcid_handle.enable_feature(PciFeature::MsiX); +- 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) +- } else if let Some(irq) = pci_config.func.legacy_interrupt_line { ++ match pcid_handle.feature_info(PciFeature::MsiX) { ++ PciFeatureInfo::MsiX(msix_info) => match allocate_msix_interrupt(pcid_handle, msix_info) ++ { ++ Ok(interrupt_handle) => return (Some(interrupt_handle), InterruptMethod::Msi), ++ Err(err) => { ++ log::warn!("xhcid: MSI-X setup failed, falling back: {err}"); ++ } ++ }, ++ feature_info => { ++ log::warn!( ++ "xhcid: MSI-X feature probe returned unexpected descriptor {:?}; falling back", ++ feature_info ++ ); ++ } ++ } ++ } ++ ++ if has_msi { ++ match allocate_msi_interrupt(pcid_handle) { ++ Ok(interrupt_handle) => return (Some(interrupt_handle), InterruptMethod::Msi), ++ Err(err) => { ++ log::warn!("xhcid: MSI setup failed, falling back: {err}"); ++ } ++ } ++ } ++ ++ if let Some(irq) = pci_config.func.legacy_interrupt_line { + log::debug!("Legacy IRQ {}", irq); +@@ + 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(0); ++ } ++ }; + + let mut state = SchemeState::new(); +- let hci = Arc::new( +- Xhci::::new(scheme_name.clone(), address, interrupt_method, pcid_handle) +- .expect("xhcid: failed to allocate device"), +- ); +- register_sync_scheme(&socket, &scheme_name, &mut &*hci) +- .expect("xhcid: failed to regsiter scheme to namespace"); ++ let hci = match Xhci::::new(scheme_name.clone(), address, interrupt_method, pcid_handle) { ++ Ok(hci) => Arc::new(hci), ++ Err(err) => { ++ log::error!("xhcid: failed to allocate device: {err}"); ++ std::process::exit(0); ++ } ++ }; ++ if let Err(err) = register_sync_scheme(&socket, &scheme_name, &mut &*hci) { ++ log::error!("xhcid: failed to register scheme to namespace: {err}"); ++ std::process::exit(0); ++ } +diff --git a/drivers/usb/xhcid/src/xhci/device_enumerator.rs b/drivers/usb/xhcid/src/xhci/device_enumerator.rs +--- a/drivers/usb/xhcid/src/xhci/device_enumerator.rs ++++ b/drivers/usb/xhcid/src/xhci/device_enumerator.rs +@@ + impl DeviceEnumerator { ++ fn is_port_disabled_state(flags: &PortFlags) -> bool { ++ flags.contains(PortFlags::PP) ++ && flags.contains(PortFlags::CCS) ++ && !flags.contains(PortFlags::PED) ++ && !flags.contains(PortFlags::PR) ++ } ++ ++ fn wait_for_stable_port_flags( ++ &self, ++ port_id: PortId, ++ mut flags: PortFlags, ++ ) -> Option { ++ const MAX_ATTEMPTS: usize = 8; ++ const STABILIZE_DELAY: Duration = Duration::from_millis(20); ++ ++ if flags.contains(PortFlags::PED) || Self::is_port_disabled_state(&flags) { ++ return Some(flags); ++ } ++ ++ for attempt in 0..MAX_ATTEMPTS { ++ debug!( ++ "Port {} reported transient flags {:?}; waiting for a stable state ({}/{})", ++ port_id, ++ flags, ++ attempt + 1, ++ MAX_ATTEMPTS ++ ); ++ std::thread::sleep(STABILIZE_DELAY); ++ ++ flags = { ++ let ports = self.hci.ports.lock().unwrap(); ++ let index = port_id.root_hub_port_index(); ++ if index >= ports.len() { ++ warn!( ++ "Port {} disappeared while waiting for a stable state", ++ port_id ++ ); ++ return None; ++ } ++ ports[index].flags() ++ }; ++ ++ if flags.contains(PortFlags::PED) || Self::is_port_disabled_state(&flags) { ++ return Some(flags); ++ } ++ } ++ ++ None ++ } ++ + pub fn new(hci: Arc>) -> Self { + let request_queue = hci.device_enumerator_receiver.clone(); + DeviceEnumerator { hci, request_queue } + } +@@ + loop { + debug!("Start Device Enumerator Loop"); + let request = match self.request_queue.recv() { + Ok(req) => req, +- Err(err) => { +- panic!("Failed to received an enumeration request! error: {}", err) +- } ++ Err(err) => { ++ warn!("xhcid: device enumerator shutting down: {}", err); ++ return; ++ } + }; +@@ +- if flags.contains(PortFlags::CCS) { ++ if flags.contains(PortFlags::CCS) { ++ let Some(flags) = self.wait_for_stable_port_flags(port_id, flags) else { ++ warn!( ++ "Port {} never reached a stable connected state; ignoring this change", ++ port_id ++ ); ++ continue; ++ }; ++ + debug!( + "Received Device Connect Port Status Change Event with port flags {:?}", + flags + ); +@@ + //If the port isn't enabled (i.e. it's a USB2 port), we need to reset it if it isn't resetting already + //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); +- +- if !disabled_state { +- panic!( +- "Port {} isn't in the disabled state! Current flags: {:?}", +- port_id, flags +- ); +- } else { +- debug!("Port {} has entered the disabled state.", port_id); +- } ++ debug!("Port {} has entered the disabled state.", port_id); +@@ + if !enabled_state { + warn!( + "Port {} isn't in the enabled state! Current flags: {:?}", + port_id, flags + ); +- } else { +- debug!( +- "Port {} is in the enabled state. Proceeding with enumeration", +- port_id +- ); ++ continue; + } ++ debug!( ++ "Port {} is in the enabled state. Proceeding with enumeration", ++ port_id ++ ); + } +diff --git a/drivers/usb/xhcid/src/xhci/irq_reactor.rs b/drivers/usb/xhcid/src/xhci/irq_reactor.rs +--- a/drivers/usb/xhcid/src/xhci/irq_reactor.rs ++++ b/drivers/usb/xhcid/src/xhci/irq_reactor.rs +@@ + let port_id = PortId { + root_hub_port_num, + route_string: 0, + }; + trace!("Received Port Status Change Request on port {}", port_id); +- self.device_enumerator_sender +- .send(DeviceEnumerationRequest { port_id }) +- .expect( +- format!( +- "Failed to transmit device numeration request on port {}", +- port_id +- ) +- .as_str(), +- ); ++ if let Err(err) = self ++ .device_enumerator_sender ++ .send(DeviceEnumerationRequest { port_id }) ++ { ++ warn!( ++ "Failed to transmit device enumeration request on port {}: {}", ++ port_id, ++ err ++ ); ++ return; ++ } + { + let mut ports = self.hci.ports.lock().unwrap(); diff --git a/local/patches/kernel/redox.patch b/local/patches/kernel/redox.patch index 0d69ead9..96864608 100644 --- a/local/patches/kernel/redox.patch +++ b/local/patches/kernel/redox.patch @@ -219,6 +219,43 @@ index b3683125..be7db1be 100644 pub fn feature_info() -> FeatureInfo { cpuid() diff --git a/src/context/memory.rs b/src/context/memory.rs +--- a/src/context/memory.rs ++++ b/src/context/memory.rs +@@ + let new_flags = grant_flags.write(grant_flags.has_write() && allow_writable); +- let Some(flush) = (unsafe { +- addr_space +- .table +- .utable +- .map_phys(faulting_page.start_address(), frame.base(), new_flags) +- }) else { +- // TODO +- return Err(PfError::Oom); +- }; ++ let flush = if faulting_frame_opt.is_some() { ++ let Some((_, _, flush)) = (unsafe { ++ addr_space ++ .table ++ .utable ++ .remap_with_full(faulting_page.start_address(), |_, _| { ++ Some((frame.base(), new_flags)) ++ }) ++ }) else { ++ return Err(PfError::Oom); ++ }; ++ flush ++ } else { ++ let Some(flush) = (unsafe { ++ addr_space ++ .table ++ .utable ++ .map_phys(faulting_page.start_address(), frame.base(), new_flags) ++ }) else { ++ return Err(PfError::Oom); ++ }; ++ flush ++ }; +diff --git a/src/context/memory.rs b/src/context/memory.rs index 94519448..368efb0d 100644 --- a/src/context/memory.rs +++ b/src/context/memory.rs diff --git a/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/device.h b/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/device.h index 472814f5..56babbeb 100644 --- a/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/device.h +++ b/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/device.h @@ -1,7 +1,7 @@ #ifndef _LINUX_DEVICE_H #define _LINUX_DEVICE_H -#include +#include "types.h" #include struct device_driver { @@ -27,11 +27,7 @@ static inline void dev_set_drvdata(struct device *dev, void *data) dev->driver_data = data; } -struct class { - const char *name; -}; - -extern struct device *devm_kzalloc(struct device *dev, size_t size, gfp_t flags); +extern void *devm_kzalloc(struct device *dev, size_t size, gfp_t flags); extern void devm_kfree(struct device *dev, void *ptr); #endif diff --git a/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/firmware.h b/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/firmware.h index c61646a6..7aba3945 100644 --- a/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/firmware.h +++ b/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/firmware.h @@ -1,6 +1,7 @@ #ifndef _LINUX_FIRMWARE_H #define _LINUX_FIRMWARE_H +#include #include struct firmware { @@ -23,4 +24,6 @@ extern int request_firmware_nowait( extern int request_firmware_direct(const struct firmware **fw, const char *name, struct device *dev); +#define FW_ACTION_HOTPLUG 0 + #endif diff --git a/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/interrupt.h b/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/interrupt.h index 8bf4faac..7016ae09 100644 --- a/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/interrupt.h +++ b/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/interrupt.h @@ -3,29 +3,16 @@ #include #include +#include -static inline int in_interrupt(void) -{ - return 0; -} +extern void local_irq_save(unsigned long *flags); +extern void local_irq_restore(unsigned long flags); +extern void local_irq_disable(void); +extern void local_irq_enable(void); +extern int irqs_disabled(void); -static inline int in_irq(void) -{ - return 0; -} - -static inline void local_irq_save(unsigned long *flags) -{ - (void)flags; -} - -static inline void local_irq_restore(unsigned long flags) -{ - (void)flags; -} - -static inline void local_irq_disable(void) {} -static inline void local_irq_enable(void) {} +static inline int in_interrupt(void) { return irqs_disabled(); } +static inline int in_irq(void) { return irqs_disabled(); } #define disable_irq_nosync(irq) ((void)(irq)) #define enable_irq(irq) ((void)(irq)) diff --git a/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/mutex.h b/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/mutex.h index 3fc37564..3c1596bf 100644 --- a/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/mutex.h +++ b/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/mutex.h @@ -11,12 +11,7 @@ extern void mutex_init(struct mutex *lock); extern void mutex_lock(struct mutex *lock); extern void mutex_unlock(struct mutex *lock); extern int mutex_is_locked(struct mutex *lock); - -static inline int mutex_trylock(struct mutex *lock) -{ - (void)lock; - return 1; -} +extern int mutex_trylock(struct mutex *lock); #define DEFINE_MUTEX(name) struct mutex name = { .__opaque = {0} } diff --git a/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/pci.h b/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/pci.h index 234e675d..d348435e 100644 --- a/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/pci.h +++ b/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/pci.h @@ -24,7 +24,7 @@ struct pci_device_id { struct pci_dev { u16 vendor; - u16 device; + u16 device_id; u8 bus_number; u8 dev_number; u8 func_number; @@ -33,7 +33,7 @@ struct pci_dev { u64 resource_start[6]; u64 resource_len[6]; void *driver_data; - struct device device; + struct device device_obj; }; struct pci_driver { diff --git a/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/timer.h b/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/timer.h index 7746f871..74991de1 100644 --- a/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/timer.h +++ b/local/recipes/drivers/linux-kpi/source/src/c_headers/linux/timer.h @@ -11,39 +11,13 @@ struct timer_list { unsigned char __opaque[64]; }; -static inline void setup_timer(struct timer_list *timer, - void (*function)(unsigned long), - unsigned long data) -{ - timer->function = function; - timer->data = data; - timer->expires = 0; -} - -static inline int mod_timer(struct timer_list *timer, unsigned long expires) -{ - (void)timer; - (void)expires; - return 0; -} - -static inline int del_timer(struct timer_list *timer) -{ - (void)timer; - return 0; -} - -static inline int del_timer_sync(struct timer_list *timer) -{ - (void)timer; - return 0; -} - -static inline int timer_pending(const struct timer_list *timer) -{ - (void)timer; - return 0; -} +extern void setup_timer(struct timer_list *timer, + void (*function)(unsigned long), + unsigned long data); +extern int mod_timer(struct timer_list *timer, unsigned long expires); +extern int del_timer(struct timer_list *timer); +extern int del_timer_sync(struct timer_list *timer); +extern int timer_pending(const struct timer_list *timer); #define DEFINE_TIMER(_name, _function, _flags, _data) \ struct timer_list _name = { .function = (_function), .data = (_data) } diff --git a/local/recipes/drivers/linux-kpi/source/src/lib.rs b/local/recipes/drivers/linux-kpi/source/src/lib.rs index 810c7834..94000312 100644 --- a/local/recipes/drivers/linux-kpi/source/src/lib.rs +++ b/local/recipes/drivers/linux-kpi/source/src/lib.rs @@ -2,13 +2,19 @@ pub mod rust_impl; +#[cfg(all(test, not(target_os = "redox")))] +mod test_host_redox_shims; + pub use rust_impl::device; pub use rust_impl::dma; pub use rust_impl::drm_shim; pub use rust_impl::firmware; pub use rust_impl::io; pub use rust_impl::irq; +pub use rust_impl::mac80211; pub use rust_impl::memory; +pub use rust_impl::net; pub use rust_impl::pci; pub use rust_impl::sync; +pub use rust_impl::wireless; pub use rust_impl::workqueue; diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/device.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/device.rs index 5e819e0c..2c13f4fa 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/device.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/device.rs @@ -6,11 +6,11 @@ const GFP_DMA32: u32 = 2; /// Wrapper to make raw pointers `Send`, required because `DEVRES_MAP` is a /// global `Mutex` (which needs `T: Send`). Raw pointers are not `Send` by -/// default since the compiler can't prove thread-safety. Here each `(ptr, -/// Layout)` pair is exclusively owned by the device that allocated it — only +/// default since the compiler can't prove thread-safety. Here each tracked +/// pointer is exclusively owned by the device that allocated it — only /// freed via `devm_kfree` or `devres_free_all` — so sending across threads is /// safe. -struct TrackedAlloc(*mut u8, Layout); +struct TrackedAlloc(*mut u8); unsafe impl Send for TrackedAlloc {} lazy_static::lazy_static! { @@ -42,16 +42,15 @@ pub extern "C" fn devm_kzalloc(dev: *mut u8, size: usize, flags: u32) -> *mut u8 return ptr; } - let layout = match tracked_layout(size, flags) { - Some(layout) => layout, - None => return ptr, - }; + if tracked_layout(size, flags).is_none() { + return ptr; + } if let Ok(mut devres_map) = DEVRES_MAP.lock() { devres_map .entry(dev as usize) .or_default() - .push(TrackedAlloc(ptr, layout)); + .push(TrackedAlloc(ptr)); } ptr diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/firmware.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/firmware.rs index ea818d4c..db51101b 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/firmware.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/firmware.rs @@ -1,5 +1,87 @@ use std::ptr; +fn firmware_search_roots() -> Vec { + let mut roots = Vec::new(); + if let Some(root) = std::env::var_os("REDBEAR_LINUX_KPI_FIRMWARE_ROOT") { + roots.push(root.into()); + } + roots.push("/scheme/firmware".into()); + roots.push("/lib/firmware".into()); + roots +} + +fn firmware_name(name: *const u8) -> Result { + if name.is_null() { + return Err(-22); + } + + let name_str = unsafe { + let len = { + let mut l = 0; + while *name.add(l) != 0 { + l += 1; + } + l + }; + let slice = std::slice::from_raw_parts(name, len); + match std::str::from_utf8(slice) { + Ok(s) => s.to_string(), + Err(_) => return Err(-22), + } + }; + + Ok(name_str) +} + +fn load_firmware_bytes(name: &str) -> Result, i32> { + for root in firmware_search_roots() { + let candidate = root.join(name); + match std::fs::read(&candidate) { + Ok(bytes) => { + log::info!( + "request_firmware: loaded '{}' via {}", + name, + candidate.display() + ); + return Ok(bytes); + } + Err(err) if err.kind() == std::io::ErrorKind::NotFound => continue, + Err(err) => { + log::error!( + "request_firmware: failed to load '{}' via {}: {}", + name, + candidate.display(), + err + ); + return Err(-5); + } + } + } + + log::error!("request_firmware: failed to locate '{}'", name); + Err(-2) +} + +fn install_firmware(fw: *mut *mut Firmware, data: Vec) -> i32 { + let size = data.len(); + let layout = match std::alloc::Layout::from_size_align(size, 1) { + Ok(l) => l, + Err(_) => return -12, + }; + let ptr = unsafe { std::alloc::alloc(layout) }; + if ptr.is_null() { + return -12; + } + unsafe { ptr::copy_nonoverlapping(data.as_ptr(), ptr, size) }; + + let firmware = Box::new(Firmware { + size, + data: ptr as *const u8, + }); + unsafe { *fw = Box::into_raw(firmware) }; + 0 +} + #[repr(C)] pub struct Firmware { pub size: usize, @@ -35,54 +117,68 @@ pub extern "C" fn request_firmware(fw: *mut *mut Firmware, name: *const u8, _dev return -22; } - let name_str = unsafe { - let len = { - let mut l = 0; - while *name.add(l) != 0 { - l += 1; - } - l - }; - let slice = std::slice::from_raw_parts(name, len); - match std::str::from_utf8(slice) { - Ok(s) => s, - Err(_) => return -22, - } + let name_str = match firmware_name(name) { + Ok(name_str) => name_str, + Err(err) => return err, }; - let firmware_path = format!("/scheme/firmware/{}", name_str); - log::info!( - "request_firmware: loading '{}' via {}", - name_str, - firmware_path - ); - - let data = match std::fs::read(&firmware_path) { - Ok(d) => d, - Err(e) => { - log::error!("request_firmware: failed to load '{}': {}", name_str, e); - return -2; - } - }; - - let size = data.len(); - let layout = match std::alloc::Layout::from_size_align(size, 1) { - Ok(l) => l, - Err(_) => return -12, - }; - let ptr = unsafe { std::alloc::alloc(layout) }; - if ptr.is_null() { - return -12; + match load_firmware_bytes(&name_str) { + Ok(data) => install_firmware(fw, data), + Err(err) => err, } - unsafe { ptr::copy_nonoverlapping(data.as_ptr(), ptr, size) }; +} - let firmware = Box::new(Firmware { - size, - data: ptr as *const u8, +#[no_mangle] +pub extern "C" fn request_firmware_direct( + fw: *mut *mut Firmware, + name: *const u8, + dev: *mut u8, +) -> i32 { + request_firmware(fw, name, dev) +} + +#[no_mangle] +pub extern "C" fn request_firmware_nowait( + _dev: *mut u8, + _uevent: i32, + name: *const u8, + context: *mut u8, + cont: Option, +) -> i32 { + let Some(cont) = cont else { + return -22; + }; + + let name_str = match firmware_name(name) { + Ok(name_str) => name_str, + Err(err) => return err, + }; + + let fw_ptr = match load_firmware_bytes(&name_str) { + Ok(data) => { + let mut fw_ptr: *mut Firmware = ptr::null_mut(); + let rc = install_firmware(&mut fw_ptr, data); + if rc != 0 { + return rc; + } + fw_ptr + } + Err(err) => { + log::warn!( + "request_firmware_nowait: unable to pre-load '{}': {}", + name_str, + err + ); + ptr::null_mut() + } + }; + + let fw_addr = fw_ptr as usize; + let context_addr = context as usize; + std::thread::spawn(move || { + cont(fw_addr as *const Firmware, context_addr as *mut u8); }); - unsafe { *fw = Box::into_raw(firmware) }; - log::info!("request_firmware: loaded {} bytes for '{}'", size, name_str); 0 } @@ -93,3 +189,89 @@ pub extern "C" fn release_firmware(fw: *mut Firmware) { } unsafe { drop(Box::from_raw(fw)) }; } + +#[cfg(test)] +mod tests { + use super::*; + use std::ffi::CString; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Mutex; + use std::time::{SystemTime, UNIX_EPOCH}; + + static TEST_ENV_LOCK: std::sync::LazyLock> = + std::sync::LazyLock::new(|| Mutex::new(())); + + fn temp_root(prefix: &str) -> std::path::PathBuf { + let stamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + let path = std::env::temp_dir().join(format!("{prefix}-{stamp}")); + std::fs::create_dir_all(&path).unwrap(); + path + } + + #[test] + fn request_firmware_direct_uses_override_root() { + let _guard = TEST_ENV_LOCK.lock().unwrap(); + let root = temp_root("rbos-linux-kpi-fw"); + std::fs::write(root.join("iwlwifi-test.ucode"), [1u8, 2, 3]).unwrap(); + unsafe { + std::env::set_var("REDBEAR_LINUX_KPI_FIRMWARE_ROOT", &root); + } + + let mut fw: *mut Firmware = ptr::null_mut(); + let name = CString::new("iwlwifi-test.ucode").unwrap(); + let rc = request_firmware_direct(&mut fw, name.as_ptr().cast::(), ptr::null_mut()); + assert_eq!(rc, 0); + assert!(!fw.is_null()); + assert_eq!(unsafe { (*fw).size }, 3); + release_firmware(fw); + unsafe { + std::env::remove_var("REDBEAR_LINUX_KPI_FIRMWARE_ROOT"); + } + } + + #[test] + fn request_firmware_nowait_invokes_callback() { + let _guard = TEST_ENV_LOCK.lock().unwrap(); + let root = temp_root("rbos-linux-kpi-fw-nowait"); + std::fs::write(root.join("iwlwifi-test-async.ucode"), [9u8, 8, 7]).unwrap(); + unsafe { + std::env::set_var("REDBEAR_LINUX_KPI_FIRMWARE_ROOT", &root); + } + + static CALLED: AtomicBool = AtomicBool::new(false); + + extern "C" fn callback(fw: *const Firmware, _context: *mut u8) { + assert!(!fw.is_null()); + CALLED.store(true, Ordering::Release); + release_firmware(fw as *mut Firmware); + } + + let name = CString::new("iwlwifi-test-async.ucode").unwrap(); + let rc = request_firmware_nowait( + ptr::null_mut(), + 0, + name.as_ptr().cast::(), + ptr::null_mut(), + Some(callback), + ); + assert_eq!(rc, 0); + + for _ in 0..100 { + if CALLED.load(Ordering::Acquire) { + unsafe { + std::env::remove_var("REDBEAR_LINUX_KPI_FIRMWARE_ROOT"); + } + return; + } + std::thread::sleep(std::time::Duration::from_millis(5)); + } + + unsafe { + std::env::remove_var("REDBEAR_LINUX_KPI_FIRMWARE_ROOT"); + } + panic!("request_firmware_nowait callback was not invoked"); + } +} diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/memory.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/memory.rs index 646b5922..0bbb3051 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/memory.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/memory.rs @@ -72,6 +72,24 @@ fn dma32_alloc(size: usize) -> *mut u8 { let phys = virt_to_phys(candidate as usize); if phys == 0 { + #[cfg(all(test, not(target_os = "redox")))] + let host_test_fallback = true; + #[cfg(not(all(test, not(target_os = "redox"))))] + let host_test_fallback = false; + + if host_test_fallback { + log::debug!( + "dma32_alloc: host test fallback for virt={:#x} without translation", + candidate as usize + ); + if let Ok(mut tracker) = DMA32_TRACKER.lock() { + tracker.insert(SendU8Ptr(candidate), layout); + return candidate; + } + unsafe { dealloc(candidate, layout) }; + return ptr::null_mut(); + } + log::warn!( "dma32_alloc: virt_to_phys failed for {:#x}", candidate as usize diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/mod.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/mod.rs index b9ac0ff5..eabfe939 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/mod.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/mod.rs @@ -2,6 +2,8 @@ pub mod device; pub mod dma; pub mod drm_shim; pub mod firmware; +pub mod mac80211; +pub mod net; pub mod idr; pub mod io; pub mod irq; @@ -10,4 +12,5 @@ pub mod pci; pub mod sync; pub mod timer; pub mod wait; +pub mod wireless; pub mod workqueue; diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/pci.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/pci.rs index ae065c4c..8b42ecfa 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/pci.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/pci.rs @@ -2,9 +2,7 @@ use std::os::raw::c_ulong; use std::ptr; use std::sync::Mutex; -use redox_driver_sys::pci::{ - enumerate_pci_class, PciDevice, PciDeviceInfo, PciLocation, PCI_CLASS_DISPLAY, -}; +use redox_driver_sys::pci::{enumerate_pci_all, PciDevice, PciDeviceInfo, PciLocation}; const EINVAL: i32 = 22; const ENODEV: i32 = 19; @@ -354,7 +352,7 @@ pub extern "C" fn pci_register_driver(drv: *mut PciDriver) -> i32 { } }; - let devices = match enumerate_pci_class(PCI_CLASS_DISPLAY) { + let devices = match enumerate_pci_all() { Ok(devices) => devices, Err(error) => { log::warn!("pci_register_driver: PCI enumeration failed: {}", error); @@ -365,7 +363,7 @@ pub extern "C" fn pci_register_driver(drv: *mut PciDriver) -> i32 { let Some((info, id_ptr)) = devices.into_iter().find_map(|candidate| { matching_id_entry(&candidate, driver.id_table).map(|id_ptr| (candidate, id_ptr)) }) else { - log::info!("pci_register_driver: no matching PCI display device found"); + log::info!("pci_register_driver: no matching PCI device found"); return -ENODEV; }; diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/sync.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/sync.rs index ae2b6d54..9a571cde 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/sync.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/sync.rs @@ -32,6 +32,22 @@ pub extern "C" fn mutex_lock(m: *mut LinuxMutex) { } } +#[no_mangle] +pub extern "C" fn mutex_trylock(m: *mut LinuxMutex) -> i32 { + if m.is_null() { + return 0; + } + if unsafe { &*m } + .state + .compare_exchange(UNLOCKED, LOCKED, Ordering::Acquire, Ordering::Relaxed) + .is_ok() + { + 1 + } else { + 0 + } +} + #[no_mangle] pub extern "C" fn mutex_unlock(m: *mut LinuxMutex) { if m.is_null() { @@ -121,6 +137,18 @@ pub extern "C" fn local_irq_restore(flags: u64) { IRQ_DEPTH.store(flags as u32, Ordering::Release); } +#[no_mangle] +pub extern "C" fn local_irq_disable() { + IRQ_DEPTH.fetch_add(1, Ordering::Acquire); +} + +#[no_mangle] +pub extern "C" fn local_irq_enable() { + let _ = IRQ_DEPTH.fetch_update(Ordering::AcqRel, Ordering::Relaxed, |depth| { + Some(depth.saturating_sub(1)) + }); +} + #[no_mangle] pub extern "C" fn irqs_disabled() -> bool { IRQ_DEPTH.load(Ordering::Acquire) > 0 @@ -175,3 +203,29 @@ pub extern "C" fn reinit_completion(c: *mut Completion) { } unsafe { &*c }.done.store(0, Ordering::Release); } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn mutex_trylock_reflects_lock_state() { + let mut lock = LinuxMutex { + state: AtomicU8::new(UNLOCKED), + }; + + assert_eq!(mutex_trylock(&mut lock), 1); + assert_eq!(mutex_trylock(&mut lock), 0); + mutex_unlock(&mut lock); + assert_eq!(mutex_trylock(&mut lock), 1); + } + + #[test] + fn local_irq_disable_enable_tracks_depth() { + IRQ_DEPTH.store(0, Ordering::Release); + local_irq_disable(); + assert!(irqs_disabled()); + local_irq_enable(); + assert!(!irqs_disabled()); + } +} diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/timer.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/timer.rs index 852499fb..e1c17f58 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/timer.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/timer.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use std::mem; -use std::os::raw::c_int; +use std::os::raw::{c_int, c_ulong}; use std::ptr; use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, OnceLock}; @@ -117,8 +117,8 @@ fn join_all_handles(entry: &TimerEntry) { #[no_mangle] pub extern "C" fn setup_timer( timer: *mut TimerList, - function: extern "C" fn(*mut u8), - data: *mut u8, + function: extern "C" fn(c_ulong), + data: c_ulong, ) { if timer.is_null() { return; @@ -131,13 +131,13 @@ pub extern "C" fn setup_timer( TimerList { expires: AtomicU64::new(0), function: AtomicPtr::new(function_ptr), - data: AtomicPtr::new(data), + data: AtomicPtr::new(data as usize as *mut u8), active: AtomicBool::new(false), }, ); } - reset_timer_entry(timer, function_ptr, data); + reset_timer_entry(timer, function_ptr, data as usize as *mut u8); } #[no_mangle] @@ -185,8 +185,8 @@ pub extern "C" fn mod_timer(timer: *mut TimerList, expires: u64) -> i32 { } let function = - unsafe { std::mem::transmute::(function_addr) }; - function(data_addr as *mut u8); + unsafe { std::mem::transmute::(function_addr) }; + function(data_addr as c_ulong); if entry_for_thread.generation.load(Ordering::Acquire) == generation { entry_for_thread.active.store(false, Ordering::Release); diff --git a/local/recipes/drivers/redox-driver-sys/source/src/dma.rs b/local/recipes/drivers/redox-driver-sys/source/src/dma.rs index ea26e538..701a87e2 100644 --- a/local/recipes/drivers/redox-driver-sys/source/src/dma.rs +++ b/local/recipes/drivers/redox-driver-sys/source/src/dma.rs @@ -1,7 +1,8 @@ use core::ptr::NonNull; use std::sync::atomic::{AtomicI32, Ordering}; -use redox_syscall::flag::{MAP_PRIVATE, O_CLOEXEC, PROT_READ, PROT_WRITE}; +use redox_syscall::data::Map; +use redox_syscall::flag::{MapFlags, MAP_PRIVATE, O_CLOEXEC, PROT_READ, PROT_WRITE}; use redox_syscall::PAGE_SIZE; use syscall as redox_syscall; @@ -155,18 +156,15 @@ impl DmaBuffer { let region_fd = libredox::call::openat(mem_fd as usize, &path, O_CLOEXEC as i32, 0) .map_err(|e| DriverError::Io(std::io::Error::from_raw_os_error(e.errno())))?; - // Map it into our address space - let ptr = unsafe { - libredox::call::mmap(libredox::call::MmapArgs { - fd: region_fd as usize, - offset: 0, - length: size, - flags: MAP_PRIVATE.bits() as u32, - prot: (PROT_READ | PROT_WRITE).bits() as u32, - addr: core::ptr::null_mut(), - }) - } - .map_err(|e| { + let map = Map { + offset: 0, + size, + flags: MapFlags::from_bits_truncate((MAP_PRIVATE | PROT_READ | PROT_WRITE).bits()), + address: 0, + }; + + // Map it into our address space through SYS_FMAP with combined map+prot flags. + let ptr = unsafe { redox_syscall::call::fmap(region_fd as usize, &map) }.map_err(|e| { let _ = libredox::call::close(region_fd as usize); DriverError::MappingFailed { phys: 0, diff --git a/local/recipes/drivers/redox-driver-sys/source/src/io.rs b/local/recipes/drivers/redox-driver-sys/source/src/io.rs index 5010d8b4..9160b3f0 100644 --- a/local/recipes/drivers/redox-driver-sys/source/src/io.rs +++ b/local/recipes/drivers/redox-driver-sys/source/src/io.rs @@ -1,3 +1,4 @@ +#[cfg(all(target_arch = "x86_64", target_os = "redox"))] use syscall as redox_syscall; use crate::Result; diff --git a/local/recipes/drivers/redox-driver-sys/source/src/lib.rs b/local/recipes/drivers/redox-driver-sys/source/src/lib.rs index 423008c8..859cc0e4 100644 --- a/local/recipes/drivers/redox-driver-sys/source/src/lib.rs +++ b/local/recipes/drivers/redox-driver-sys/source/src/lib.rs @@ -12,14 +12,18 @@ //! //! ```no_run //! use redox_driver_sys::pci::PciDevice; +//! use redox_driver_sys::Result; //! -//! // Open a PCI device by location -//! let dev = PciDevice::open(0, 0x10, 0, 0)?; -//! let vendor = dev.vendor_id(); -//! let bars = dev.parse_bars()?; -//! if let Some(bar) = bars[0].memory_info() { -//! let mmio = dev.map_bar(0, bar.addr, bar.size)?; -//! let reg = mmio.read32(0); +//! fn example() -> Result<()> { +//! // Open a PCI device by location +//! let mut dev = PciDevice::open(0, 0x10, 0, 0)?; +//! let _vendor = dev.vendor_id(); +//! let bars = dev.parse_bars()?; +//! if let Some((addr, size)) = bars[0].memory_info() { +//! let mmio = dev.map_bar(0, addr, size)?; +//! let _reg = mmio.read32(0); +//! } +//! Ok(()) //! } //! ``` diff --git a/local/recipes/drivers/redox-driver-sys/source/src/memory.rs b/local/recipes/drivers/redox-driver-sys/source/src/memory.rs index 6f9a6ba0..6292649a 100644 --- a/local/recipes/drivers/redox-driver-sys/source/src/memory.rs +++ b/local/recipes/drivers/redox-driver-sys/source/src/memory.rs @@ -1,6 +1,7 @@ use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; +use redox_syscall::data::Map; use redox_syscall::flag::{ MAP_SHARED, O_CLOEXEC, O_RDONLY, O_RDWR, O_WRONLY, PROT_READ, PROT_WRITE, }; @@ -110,20 +111,19 @@ impl MmioRegion { let root_fd = ensure_memory_root()?; let mem_fd = root_fd.openat(&path, (O_CLOEXEC | mode) as i32, 0)?; - let ptr = unsafe { - libredox::call::mmap(libredox::call::MmapArgs { - fd: mem_fd.raw(), - offset: phys_addr, - length: aligned_size, - flags: MAP_SHARED.bits() as u32, - prot: mmap_prot.bits() as u32, - addr: ptr::null_mut(), - }) - } - .map_err(|e| DriverError::MappingFailed { - phys: phys_addr, - size, - reason: format!("{e:?}"), + let map = Map { + offset: phys_addr as usize, + size: aligned_size, + flags: mmap_prot | redox_syscall::MapFlags::from_bits_truncate(MAP_SHARED.bits()), + address: 0, + }; + + let ptr = unsafe { redox_syscall::call::fmap(mem_fd.raw(), &map) }.map_err(|e| { + DriverError::MappingFailed { + phys: phys_addr, + size, + reason: format!("{e:?}"), + } })?; Ok(Self { diff --git a/local/recipes/drivers/redox-driver-sys/source/src/pci.rs b/local/recipes/drivers/redox-driver-sys/source/src/pci.rs index 15f273ae..0105fe4a 100644 --- a/local/recipes/drivers/redox-driver-sys/source/src/pci.rs +++ b/local/recipes/drivers/redox-driver-sys/source/src/pci.rs @@ -585,7 +585,7 @@ impl std::io::Write for PciDevice { } } -pub fn enumerate_pci_class(class: u8) -> Result> { +fn enumerate_pci_filtered(class: Option) -> Result> { let entries = std::fs::read_dir("/scheme/pci")?; let mut devices = Vec::new(); @@ -609,8 +609,10 @@ pub fn enumerate_pci_class(class: u8) -> Result> { continue; } let class_code = data[0x0b]; - if class_code != class { - continue; + if let Some(class) = class { + if class_code != class { + continue; + } } let vendor_id = u16::from_le_bytes([data[0x00], data[0x01]]); let device_id = u16::from_le_bytes([data[0x02], data[0x03]]); @@ -641,12 +643,23 @@ pub fn enumerate_pci_class(class: u8) -> Result> { } log::debug!( - "PCI enumeration for class {class:#04x}: found {} devices", + "PCI enumeration{}: found {} devices", + class + .map(|class| format!(" for class {class:#04x}")) + .unwrap_or_default(), devices.len() ); Ok(devices) } +pub fn enumerate_pci_class(class: u8) -> Result> { + enumerate_pci_filtered(Some(class)) +} + +pub fn enumerate_pci_all() -> Result> { + enumerate_pci_filtered(None) +} + fn parse_scheme_entry(name: &str) -> Option { let parts: Vec<&str> = name.splitn(3, "--").collect(); if parts.len() != 3 { diff --git a/mk/prefix.mk b/mk/prefix.mk index 96f47802..4dea92e8 100644 --- a/mk/prefix.mk +++ b/mk/prefix.mk @@ -1,6 +1,7 @@ # Configuration file for the Rust/GCC cross-compilers, relibc and libtool PREFIX=prefix/$(TARGET) +TOOLCHAIN_EXPORT_DIR?=$(ROOT)/build/toolchain-export/$(TARGET) PREFIX_INSTALL=$(PREFIX)/sysroot/ PREFIX_PATH=$(ROOT)/$(PREFIX_INSTALL)/bin @@ -25,6 +26,13 @@ PREFIX_CONFIG=CI=1 COOKBOOK_CLEAN_BUILD=true COOKBOOK_CLEAN_TARGET=false COOKBOO prefix: $(PREFIX)/sysroot +export-toolchain: $(PREFIX)/sysroot FORCE +ifeq ($(PODMAN_BUILD),1) + $(PODMAN_RUN) make $@ TOOLCHAIN_EXPORT_DIR="$(TOOLCHAIN_EXPORT_DIR)" +else + "$(ROOT)/local/scripts/export-$(TARGET)-toolchain.sh" "$(TOOLCHAIN_EXPORT_DIR)" +endif + # Remove prefix builds and downloads prefix_clean: rm -rf $(PREFIX) diff --git a/mk/qemu.mk b/mk/qemu.mk index d8274a80..084ee1fc 100644 --- a/mk/qemu.mk +++ b/mk/qemu.mk @@ -23,7 +23,7 @@ ifeq ($(ARCH),i586) kvm?=yes endif else ifeq ($(ARCH),x86_64) - gpu?=vga + gpu?=virtio uefi?=yes VGA_SUPPORTED=yes QEMU_ARCH=x86_64