Update Red Bear driver substrate
Red Bear OS Team
This commit is contained in:
@@ -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<daemon::Daemon>, 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<Source> =
|
||||
- EventQueue::new().expect("ps2d: failed to create event queue");
|
||||
+ let event_queue: EventQueue<Source> = 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<Self, Ps2Error> {
|
||||
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<File> {
|
||||
+ 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<File> {
|
||||
+ 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::<N>::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::<N>::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<const N: usize> DeviceEnumerator<N> {
|
||||
+ 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<PortFlags> {
|
||||
+ 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<Xhci<N>>) -> 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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef _LINUX_DEVICE_H
|
||||
#define _LINUX_DEVICE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include "types.h"
|
||||
#include <stddef.h>
|
||||
|
||||
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
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef _LINUX_FIRMWARE_H
|
||||
#define _LINUX_FIRMWARE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
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
|
||||
|
||||
@@ -3,29 +3,16 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
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))
|
||||
|
||||
@@ -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} }
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -11,39 +11,13 @@ struct timer_list {
|
||||
unsigned char __opaque[64];
|
||||
};
|
||||
|
||||
static inline void setup_timer(struct timer_list *timer,
|
||||
extern 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;
|
||||
}
|
||||
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) }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,87 @@
|
||||
use std::ptr;
|
||||
|
||||
fn firmware_search_roots() -> Vec<std::path::PathBuf> {
|
||||
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<String, i32> {
|
||||
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<Vec<u8>, 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<u8>) -> 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 {}",
|
||||
match load_firmware_bytes(&name_str) {
|
||||
Ok(data) => install_firmware(fw, data),
|
||||
Err(err) => err,
|
||||
}
|
||||
}
|
||||
|
||||
#[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<extern "C" fn(*const Firmware, *mut u8)>,
|
||||
) -> 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,
|
||||
firmware_path
|
||||
err
|
||||
);
|
||||
|
||||
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;
|
||||
ptr::null_mut()
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
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<Mutex<()>> =
|
||||
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::<u8>(), 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::<u8>(),
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::<usize, extern "C" fn(*mut u8)>(function_addr) };
|
||||
function(data_addr as *mut u8);
|
||||
unsafe { std::mem::transmute::<usize, extern "C" fn(c_ulong)>(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);
|
||||
|
||||
@@ -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,
|
||||
let map = Map {
|
||||
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| {
|
||||
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,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#[cfg(all(target_arch = "x86_64", target_os = "redox"))]
|
||||
use syscall as redox_syscall;
|
||||
|
||||
use crate::Result;
|
||||
|
||||
@@ -12,14 +12,18 @@
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use redox_driver_sys::pci::PciDevice;
|
||||
//! use redox_driver_sys::Result;
|
||||
//!
|
||||
//! fn example() -> Result<()> {
|
||||
//! // Open a PCI device by location
|
||||
//! let dev = PciDevice::open(0, 0x10, 0, 0)?;
|
||||
//! let vendor = dev.vendor_id();
|
||||
//! let mut 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);
|
||||
//! if let Some((addr, size)) = bars[0].memory_info() {
|
||||
//! let mmio = dev.map_bar(0, addr, size)?;
|
||||
//! let _reg = mmio.read32(0);
|
||||
//! }
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
|
||||
@@ -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 {
|
||||
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 {
|
||||
|
||||
@@ -585,7 +585,7 @@ impl std::io::Write for PciDevice {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enumerate_pci_class(class: u8) -> Result<Vec<PciDeviceInfo>> {
|
||||
fn enumerate_pci_filtered(class: Option<u8>) -> Result<Vec<PciDeviceInfo>> {
|
||||
let entries = std::fs::read_dir("/scheme/pci")?;
|
||||
let mut devices = Vec::new();
|
||||
|
||||
@@ -609,9 +609,11 @@ pub fn enumerate_pci_class(class: u8) -> Result<Vec<PciDeviceInfo>> {
|
||||
continue;
|
||||
}
|
||||
let class_code = data[0x0b];
|
||||
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]]);
|
||||
let subclass = data[0x0a];
|
||||
@@ -641,12 +643,23 @@ pub fn enumerate_pci_class(class: u8) -> Result<Vec<PciDeviceInfo>> {
|
||||
}
|
||||
|
||||
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<Vec<PciDeviceInfo>> {
|
||||
enumerate_pci_filtered(Some(class))
|
||||
}
|
||||
|
||||
pub fn enumerate_pci_all() -> Result<Vec<PciDeviceInfo>> {
|
||||
enumerate_pci_filtered(None)
|
||||
}
|
||||
|
||||
fn parse_scheme_entry(name: &str) -> Option<PciLocation> {
|
||||
let parts: Vec<&str> = name.splitn(3, "--").collect();
|
||||
if parts.len() != 3 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user