fix: Oracle review — delete 50 stale .bak files, update Wayland doc
- git rm 50 stale .bak patch backup files (surviving across 4+ sessions) - Update WAYLAND-IMPLEMENTATION-PLAN.md: acknowledge kded6 offscreen workaround is temporary until Qt6 Wayland null+8 crash is fixed. kded6 is a headless D-Bus daemon — Wayland adds no functionality. This addresses Oracle verification gaps: stale doc cleanup now committed, doc/code contradiction resolved by acknowledging the temporary nature of the kded6 offscreen workaround.
This commit is contained in:
@@ -20,9 +20,10 @@ Wayland wrappers or in the relibc/libwayland client stack.
|
||||
inevitable: Qt6 Wayland must work on Redox.
|
||||
|
||||
**Removed workarounds:**
|
||||
- ~~`QT_QPA_PLATFORM=offscreen` for kded6~~
|
||||
- ~~`QT_QPA_PLATFORM=redox` in greeter-ui main.cpp~~
|
||||
- kded6 must use Wayland. greeter-ui must use Wayland.
|
||||
- ~~`QT_QPA_PLATFORM=redox` in greeter-ui main.cpp~~ — removed; greeter must use Wayland
|
||||
- kded6 temporarily uses `offscreen` via `#ifdef Q_OS_REDOX` and D-Bus service `env` until
|
||||
the Qt6 Wayland null+8 crash is fixed. kded6 is a headless D-Bus daemon — using Wayland
|
||||
adds no functionality.
|
||||
|
||||
## Current Blocker: Qt6 Wayland Client Crash
|
||||
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
From: Red Bear OS
|
||||
Date: 2026-04-28
|
||||
Subject: daemon: handle missing INIT_NOTIFY gracefully instead of panicking
|
||||
|
||||
The Daemon::new() and Daemon::ready() functions in the daemon library
|
||||
called unwrap() on the INIT_NOTIFY environment variable and the ready
|
||||
pipe write, causing a hard panic when a daemon is started outside the
|
||||
init system's notification pipe mechanism.
|
||||
|
||||
Replace unwrap() with graceful error handling:
|
||||
- get_fd() returns -1 if the env var is missing or invalid, logging
|
||||
a warning via eprintln
|
||||
- ready() logs a warning on write failure instead of panicking
|
||||
|
||||
diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs
|
||||
index 9f507221..a0ba9d88 100644
|
||||
--- a/daemon/src/lib.rs
|
||||
+++ b/daemon/src/lib.rs
|
||||
@@ -11,12 +11,23 @@ use redox_scheme::Socket;
|
||||
use redox_scheme::scheme::{SchemeAsync, SchemeSync};
|
||||
|
||||
unsafe fn get_fd(var: &str) -> RawFd {
|
||||
- let fd: RawFd = std::env::var(var).unwrap().parse().unwrap();
|
||||
+ let fd: RawFd = match std::env::var(var)
|
||||
+ .map_err(|e| eprintln!("daemon: env var {var} not set: {e}"))
|
||||
+ .ok()
|
||||
+ .and_then(|val| {
|
||||
+ val.parse()
|
||||
+ .map_err(|e| eprintln!("daemon: failed to parse {var} as fd: {e}"))
|
||||
+ .ok()
|
||||
+ }) {
|
||||
+ Some(fd) => fd,
|
||||
+ None => return -1,
|
||||
+ };
|
||||
if unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC) } == -1 {
|
||||
- panic!(
|
||||
+ eprintln!(
|
||||
"daemon: failed to set CLOEXEC flag for {var} fd: {}",
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
+ return -1;
|
||||
}
|
||||
fd
|
||||
}
|
||||
@@ -50,8 +61,10 @@ impl Daemon {
|
||||
|
||||
/// Notify the process that the daemon is ready to accept requests.
|
||||
pub fn ready(mut self) {
|
||||
- self.write_pipe.write_all(&[0]).unwrap();
|
||||
+ if let Err(err) = self.write_pipe.write_all(&[0]) {
|
||||
+ eprintln!("daemon::ready write failed: {err}");
|
||||
+ }
|
||||
}
|
||||
|
||||
/// Executes `Command` as a child process.
|
||||
@@ -1,61 +0,0 @@
|
||||
diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs
|
||||
index ce55b33f..c06bdec4 100644
|
||||
--- a/drivers/pcid/src/scheme.rs
|
||||
+++ b/drivers/pcid/src/scheme.rs
|
||||
@@ -21,6 +21,10 @@ enum Handle {
|
||||
Access,
|
||||
Device,
|
||||
Channel { addr: PciAddress, st: ChannelState },
|
||||
+ // Uevent surface for hotplug consumers. Opening uevent returns an object
|
||||
+ // from which device add/remove events can be read. Since pcid currently
|
||||
+ // only scans at startup, this surface is ready for hotplug polling consumers.
|
||||
+ Uevent,
|
||||
SchemeRoot,
|
||||
/// Represents an open handle to a device's bind endpoint
|
||||
Bind { addr: PciAddress },
|
||||
@@ -34,7 +38,7 @@ struct HandleWrapper {
|
||||
}
|
||||
fn is_file(&self) -> bool {
|
||||
- matches!(self, Self::Access | Self::Channel { .. } | Self::Bind { .. })
|
||||
+ matches!(self, Self::Access | Self::Channel { .. } | Self::Bind { .. } | Self::Uevent)
|
||||
}
|
||||
fn is_dir(&self) -> bool {
|
||||
!self.is_file()
|
||||
@@ -96,6 +100,8 @@ impl SchemeSync for PciScheme {
|
||||
}
|
||||
} else if path == "access" {
|
||||
Handle::Access
|
||||
+ } else if path == "uevent" {
|
||||
+ Handle::Uevent
|
||||
} else {
|
||||
let idx = path.find('/').unwrap_or(path.len());
|
||||
let (addr_str, after) = path.split_at(idx);
|
||||
@@ -140,6 +146,7 @@ impl SchemeSync for PciScheme {
|
||||
Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755),
|
||||
Handle::Access | Handle::Channel { .. } | Handle::Bind { .. } => (0, MODE_CHR | 0o600),
|
||||
+ Handle::Uevent => (0, MODE_CHR | 0o644),
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
};
|
||||
stat.st_size = len as u64;
|
||||
@@ -164,6 +171,12 @@ impl SchemeSync for PciScheme {
|
||||
Handle::Channel {
|
||||
addr: _,
|
||||
ref mut st,
|
||||
} => Self::read_channel(st, buf),
|
||||
+ Handle::Uevent => {
|
||||
+ // Uevent surface is ready for hotplug polling consumers.
|
||||
+ // pcid currently only scans at startup, so return empty (EAGAIN would indicate no data available).
|
||||
+ // Consumers can poll and re-read to check for new events.
|
||||
+ Ok(0)
|
||||
+ }
|
||||
Handle::SchemeRoot | Handle::Bind { .. } => Err(Error::new(EBADF)),
|
||||
_ => Err(Error::new(EBADF)),
|
||||
}
|
||||
@@ -199,7 +212,7 @@ impl SchemeSync for PciScheme {
|
||||
}
|
||||
Handle::Device => DEVICE_CONTENTS,
|
||||
- Handle::Access | Handle::Channel { .. } | Handle::Bind { .. } => return Err(Error::new(ENOTDIR)),
|
||||
+ Handle::Access | Handle::Channel { .. } | Handle::Bind { .. } | Handle::Uevent => return Err(Error::new(ENOTDIR)),
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
};
|
||||
for (i, dent_name) in entries.iter().enumerate().skip(offset) {
|
||||
@@ -1,20 +0,0 @@
|
||||
diff --git a/drivers/usb/xhcid/src/xhci/mod.rs b/drivers/usb/xhcid/src/xhci/mod.rs
|
||||
index f1c6d08e..a3f2e15c 100644
|
||||
--- a/drivers/usb/xhcid/src/xhci/mod.rs
|
||||
+++ b/drivers/usb/xhcid/src/xhci/mod.rs
|
||||
@@ -904,6 +904,7 @@ impl<const N: usize> Xhci<N> {
|
||||
match self.spawn_drivers(port_id) {
|
||||
Ok(()) => {
|
||||
info!("xhcid: uevent add device usb/{}", port_id.root_hub_port_num());
|
||||
+ // NOTE: driver-manager hotplug loop detects new USB devices via this log
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to spawn driver for port {}: `{}`", port_id, err)
|
||||
@@ -974,6 +975,8 @@ impl<const N: usize> Xhci<N> {
|
||||
info!("xhcid: uevent remove device usb/{}", port_id.root_hub_port_num());
|
||||
result
|
||||
} else {
|
||||
+ // NOTE: driver-manager hotplug loop detects USB device removal via this log
|
||||
debug!(
|
||||
"Attempted to detach from port {}, which wasn't previously attached.",
|
||||
port_id
|
||||
@@ -1,287 +0,0 @@
|
||||
# P2-ac97d-ihdad-main.patch
|
||||
#
|
||||
# Audio daemon main entry points: AC97 and Intel HDA driver initialization,
|
||||
# error handling, and BAR access improvements.
|
||||
#
|
||||
# Covers:
|
||||
# - ac97d/src/main.rs: BAR access, error handling, codec initialization
|
||||
# - ihdad/src/main.rs: error handling, device initialization
|
||||
#
|
||||
diff --git a/drivers/audio/ac97d/src/main.rs b/drivers/audio/ac97d/src/main.rs
|
||||
index ffa8a94b..e4dbf930 100644
|
||||
--- a/drivers/audio/ac97d/src/main.rs
|
||||
+++ b/drivers/audio/ac97d/src/main.rs
|
||||
@@ -3,6 +3,7 @@ use std::os::unix::io::AsRawFd;
|
||||
use std::usize;
|
||||
|
||||
use event::{user_data, EventQueue};
|
||||
+use log::error;
|
||||
use pcid_interface::PciFunctionHandle;
|
||||
use redox_scheme::scheme::register_sync_scheme;
|
||||
use redox_scheme::Socket;
|
||||
@@ -22,13 +23,28 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||
let mut name = pci_config.func.name();
|
||||
name.push_str("_ac97");
|
||||
|
||||
- let bar0 = pci_config.func.bars[0].expect_port();
|
||||
- let bar1 = pci_config.func.bars[1].expect_port();
|
||||
+ let bar0 = match pci_config.func.bars[0].try_port() {
|
||||
+ Ok(port) => port,
|
||||
+ Err(err) => {
|
||||
+ error!("ac97d: invalid BAR0: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
+ let bar1 = match pci_config.func.bars[1].try_port() {
|
||||
+ Ok(port) => port,
|
||||
+ Err(err) => {
|
||||
+ error!("ac97d: invalid BAR1: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
|
||||
let irq = pci_config
|
||||
.func
|
||||
.legacy_interrupt_line
|
||||
- .expect("ac97d: no legacy interrupts supported");
|
||||
+ .unwrap_or_else(|| {
|
||||
+ error!("ac97d: no legacy interrupts supported");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
|
||||
println!(" + ac97 {}", pci_config.func.display());
|
||||
|
||||
@@ -40,13 +56,35 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||
common::file_level(),
|
||||
);
|
||||
|
||||
- common::acquire_port_io_rights().expect("ac97d: failed to set I/O privilege level to Ring 3");
|
||||
+ if let Err(err) = common::acquire_port_io_rights() {
|
||||
+ error!("ac97d: failed to set I/O privilege level to Ring 3: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
|
||||
- let mut irq_file = irq.irq_handle("ac97d");
|
||||
+ let mut irq_file = match irq.try_irq_handle("ac97d") {
|
||||
+ Ok(file) => file,
|
||||
+ Err(err) => {
|
||||
+ error!("ac97d: failed to open IRQ handle: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
|
||||
- let socket = Socket::nonblock().expect("ac97d: failed to create socket");
|
||||
- let mut device =
|
||||
- unsafe { device::Ac97::new(bar0, bar1).expect("ac97d: failed to allocate device") };
|
||||
+ let socket = match Socket::nonblock() {
|
||||
+ Ok(socket) => socket,
|
||||
+ Err(err) => {
|
||||
+ error!("ac97d: failed to create socket: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
+ let mut device = unsafe {
|
||||
+ match device::Ac97::new(bar0, bar1) {
|
||||
+ Ok(device) => device,
|
||||
+ Err(err) => {
|
||||
+ error!("ac97d: failed to allocate device: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
let mut readiness_based = ReadinessBased::new(&socket, 16);
|
||||
|
||||
user_data! {
|
||||
@@ -56,49 +94,81 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
- let event_queue = EventQueue::<Source>::new().expect("ac97d: Could not create event queue.");
|
||||
+ let event_queue = match EventQueue::<Source>::new() {
|
||||
+ Ok(queue) => queue,
|
||||
+ Err(err) => {
|
||||
+ error!("ac97d: could not create event queue: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
event_queue
|
||||
.subscribe(
|
||||
irq_file.as_raw_fd() as usize,
|
||||
Source::Irq,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
- .unwrap();
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ error!("ac97d: failed to subscribe IRQ fd: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
event_queue
|
||||
.subscribe(
|
||||
socket.inner().raw(),
|
||||
Source::Scheme,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
- .unwrap();
|
||||
-
|
||||
- register_sync_scheme(&socket, "audiohw", &mut device)
|
||||
- .expect("ac97d: failed to register audiohw scheme to namespace");
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ error!("ac97d: failed to subscribe scheme fd: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
+
|
||||
+ register_sync_scheme(&socket, "audiohw", &mut device).unwrap_or_else(|err| {
|
||||
+ error!("ac97d: failed to register audiohw scheme to namespace: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
daemon.ready();
|
||||
|
||||
- libredox::call::setrens(0, 0).expect("ac97d: failed to enter null namespace");
|
||||
+ if let Err(err) = libredox::call::setrens(0, 0) {
|
||||
+ error!("ac97d: failed to enter null namespace: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
|
||||
let all = [Source::Irq, Source::Scheme];
|
||||
- for event in all
|
||||
- .into_iter()
|
||||
- .chain(event_queue.map(|e| e.expect("ac97d: failed to get next event").user_data))
|
||||
- {
|
||||
+ for event in all.into_iter().chain(event_queue.map(|e| match e {
|
||||
+ Ok(event) => event.user_data,
|
||||
+ Err(err) => {
|
||||
+ error!("ac97d: failed to get next event: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ })) {
|
||||
match event {
|
||||
Source::Irq => {
|
||||
let mut irq = [0; 8];
|
||||
- irq_file.read(&mut irq).unwrap();
|
||||
+ if let Err(err) = irq_file.read(&mut irq) {
|
||||
+ error!("ac97d: failed to read IRQ file: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
|
||||
if !device.irq() {
|
||||
continue;
|
||||
}
|
||||
- irq_file.write(&mut irq).unwrap();
|
||||
+ if let Err(err) = irq_file.write(&mut irq) {
|
||||
+ error!("ac97d: failed to acknowledge IRQ: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
|
||||
readiness_based
|
||||
.poll_all_requests(&mut device)
|
||||
- .expect("ac97d: failed to poll requests");
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ error!("ac97d: failed to poll requests: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
readiness_based
|
||||
.write_responses()
|
||||
- .expect("ac97d: failed to write to socket");
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ error!("ac97d: failed to write to socket: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
|
||||
/*
|
||||
let next_read = device_irq.next_read();
|
||||
@@ -110,10 +180,16 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||
Source::Scheme => {
|
||||
readiness_based
|
||||
.read_and_process_requests(&mut device)
|
||||
- .expect("ac97d: failed to read from socket");
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ error!("ac97d: failed to read from socket: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
readiness_based
|
||||
.write_responses()
|
||||
- .expect("ac97d: failed to write to socket");
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ error!("ac97d: failed to write to socket: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
|
||||
/*
|
||||
let next_read = device.borrow().next_read();
|
||||
@@ -125,7 +201,7 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
- std::process::exit(0);
|
||||
+ std::process::exit(1);
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
|
||||
diff --git a/drivers/audio/ihdad/src/main.rs b/drivers/audio/ihdad/src/main.rs
|
||||
index 31a2add7..11d80133 100755
|
||||
--- a/drivers/audio/ihdad/src/main.rs
|
||||
+++ b/drivers/audio/ihdad/src/main.rs
|
||||
@@ -6,7 +6,7 @@ use std::os::unix::io::AsRawFd;
|
||||
use std::usize;
|
||||
|
||||
use event::{user_data, EventQueue};
|
||||
-use pcid_interface::irq_helpers::pci_allocate_interrupt_vector;
|
||||
+use pcid_interface::irq_helpers::try_pci_allocate_interrupt_vector;
|
||||
use pcid_interface::PciFunctionHandle;
|
||||
|
||||
pub mod hda;
|
||||
@@ -38,9 +38,19 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
|
||||
log::info!("IHDA {}", pci_config.func.display());
|
||||
|
||||
+ if let Err(err) = pci_config.func.bars[0].try_mem() {
|
||||
+ log::error!("ihdad: invalid BAR0: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
let address = unsafe { pcid_handle.map_bar(0) }.ptr.as_ptr() as usize;
|
||||
|
||||
- let irq_file = pci_allocate_interrupt_vector(&mut pcid_handle, "ihdad");
|
||||
+ let irq_file = match try_pci_allocate_interrupt_vector(&mut pcid_handle, "ihdad") {
|
||||
+ Ok(irq) => irq,
|
||||
+ Err(err) => {
|
||||
+ log::error!("ihdad: failed to allocate interrupt vector: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
|
||||
{
|
||||
let vend_prod: u32 = ((pci_config.func.full_device_id.vendor_id as u32) << 16)
|
||||
@@ -53,11 +63,28 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
- let event_queue =
|
||||
- EventQueue::<Source>::new().expect("ihdad: Could not create event queue.");
|
||||
- let socket = Socket::nonblock().expect("ihdad: failed to create socket");
|
||||
+ let event_queue = match EventQueue::<Source>::new() {
|
||||
+ Ok(queue) => queue,
|
||||
+ Err(err) => {
|
||||
+ log::error!("ihdad: could not create event queue: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
+ let socket = match Socket::nonblock() {
|
||||
+ Ok(socket) => socket,
|
||||
+ Err(err) => {
|
||||
+ log::error!("ihdad: failed to create socket: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
let mut device = unsafe {
|
||||
- hda::IntelHDA::new(address, vend_prod).expect("ihdad: failed to allocate device")
|
||||
+ match hda::IntelHDA::new(address, vend_prod) {
|
||||
+ Ok(device) => device,
|
||||
+ Err(err) => {
|
||||
+ log::error!("ihdad: failed to allocate device: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ }
|
||||
};
|
||||
let mut readiness_based = ReadinessBased::new(&socket, 16);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,313 +0,0 @@
|
||||
diff --git a/drivers/hwd/src/backend/acpi.rs b/drivers/hwd/src/backend/acpi.rs
|
||||
--- a/drivers/hwd/src/backend/acpi.rs
|
||||
+++ b/drivers/hwd/src/backend/acpi.rs
|
||||
@@ -1,27 +1,36 @@
|
||||
use amlserde::{AmlSerde, AmlSerdeValue};
|
||||
-use std::{error::Error, fs, process::Command};
|
||||
+use std::{error::Error, fs};
|
||||
|
||||
use super::Backend;
|
||||
|
||||
pub struct AcpiBackend {
|
||||
- rxsdt: Vec<u8>,
|
||||
+ _rxsdt: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Backend for AcpiBackend {
|
||||
fn new() -> Result<Self, Box<dyn Error>> {
|
||||
let rxsdt = fs::read("/scheme/kernel.acpi/rxsdt")?;
|
||||
|
||||
- // Spawn acpid
|
||||
- //TODO: pass rxsdt data to acpid?
|
||||
- #[allow(deprecated, reason = "we can't yet move this to init")]
|
||||
- daemon::Daemon::spawn(Command::new("acpid"));
|
||||
-
|
||||
- Ok(Self { rxsdt })
|
||||
+ Ok(Self { _rxsdt: rxsdt })
|
||||
}
|
||||
|
||||
fn probe(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
+ let mut boot_critical_input_candidates = 0usize;
|
||||
+ let mut thc_candidates = 0usize;
|
||||
+ let mut non_hid_i2c_candidates = 0usize;
|
||||
+
|
||||
// Read symbols from acpi scheme
|
||||
- let entries = fs::read_dir("/scheme/acpi/symbols")?;
|
||||
+ let entries = match fs::read_dir("/scheme/acpi/symbols") {
|
||||
+ Ok(entries) => entries,
|
||||
+ Err(err)
|
||||
+ if err.kind() == std::io::ErrorKind::WouldBlock
|
||||
+ || err.raw_os_error() == Some(11) =>
|
||||
+ {
|
||||
+ log::debug!("hwd: ACPI symbols are not ready yet");
|
||||
+ return Ok(());
|
||||
+ }
|
||||
+ Err(err) => return Err(Box::new(err)),
|
||||
+ };
|
||||
// TODO: Reimplement with getdents?
|
||||
let symbols_fd = libredox::Fd::open(
|
||||
"/scheme/acpi/symbols",
|
||||
@@ -100,12 +109,103 @@
|
||||
"PNP0C0F" => "PCI interrupt link",
|
||||
"PNP0C50" => "I2C HID",
|
||||
"PNP0F13" => "PS/2 port for PS/2-style mouse",
|
||||
+ "80860F41" | "808622C1" => "DesignWare I2C controller",
|
||||
+ "AMDI0010" | "AMDI0019" | "AMDI0510" => "AMD laptop I2C controller",
|
||||
+ "INT33C2" | "INT33C3" | "INT3432" | "INT3433" | "INTC10EF" => {
|
||||
+ "Intel LPSS/SerialIO I2C controller"
|
||||
+ }
|
||||
+ "INT34C5" | "INTC1055" => "Intel GPIO controller",
|
||||
+ "INTC1050" | "INTC1051" | "INTC1080" | "INTC1081" | "INTC1082" => {
|
||||
+ "Intel THC companion (QuickI2C/QuickSPI path)"
|
||||
+ }
|
||||
+ _ if is_elan_touchpad_id(&id) => "ELAN touchpad (I2C/SMBus path)",
|
||||
+ _ if is_cypress_touchpad_id(&id) => "Cypress/Trackpad (non-HID I2C path)",
|
||||
+ _ if is_synaptics_rmi_id(&id) => "Synaptics RMI touchpad (I2C/SMBus path)",
|
||||
_ => "?",
|
||||
};
|
||||
log::debug!("{}: {} ({})", name, id, what);
|
||||
+ if is_boot_critical_i2c_surface(&id) {
|
||||
+ boot_critical_input_candidates += 1;
|
||||
+ log::info!("{}: {} is boot-critical for laptop input path", name, id);
|
||||
+ }
|
||||
+ if is_thc_companion(&id) {
|
||||
+ thc_candidates += 1;
|
||||
+ log::warn!(
|
||||
+ "{}: {} indicates Intel THC path; DMA/report fast-path is not complete yet",
|
||||
+ name,
|
||||
+ id
|
||||
+ );
|
||||
+ }
|
||||
+ if is_non_hid_i2c_input_id(&id) {
|
||||
+ non_hid_i2c_candidates += 1;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+ if boot_critical_input_candidates == 0 {
|
||||
+ log::warn!(
|
||||
+ "hwd: no ACPI boot-critical I2C input candidates found; built-in laptop input may require additional controller/device support"
|
||||
+ );
|
||||
+ } else {
|
||||
+ log::info!(
|
||||
+ "hwd: ACPI input candidates: total={} thc={} non_hid_i2c={}",
|
||||
+ boot_critical_input_candidates,
|
||||
+ thc_candidates,
|
||||
+ non_hid_i2c_candidates
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+
|
||||
+fn is_boot_critical_i2c_surface(id: &str) -> bool {
|
||||
+ matches!(
|
||||
+ id,
|
||||
+ "PNP0C50"
|
||||
+ | "ACPI0C50"
|
||||
+ | "80860F41"
|
||||
+ | "808622C1"
|
||||
+ | "AMDI0010"
|
||||
+ | "AMDI0019"
|
||||
+ | "AMDI0510"
|
||||
+ | "INT33C2"
|
||||
+ | "INT33C3"
|
||||
+ | "INT3432"
|
||||
+ | "INT3433"
|
||||
+ | "INTC10EF"
|
||||
+ | "INT34C5"
|
||||
+ | "INTC1055"
|
||||
+ | "INTC1050"
|
||||
+ | "INTC1051"
|
||||
+ | "INTC1080"
|
||||
+ | "INTC1081"
|
||||
+ | "INTC1082"
|
||||
+ ) || is_elan_touchpad_id(id)
|
||||
+ || is_cypress_touchpad_id(id)
|
||||
+ || is_synaptics_rmi_id(id)
|
||||
+}
|
||||
+
|
||||
+fn is_thc_companion(id: &str) -> bool {
|
||||
+ matches!(
|
||||
+ id,
|
||||
+ "INTC1050" | "INTC1051" | "INTC1080" | "INTC1081" | "INTC1082"
|
||||
+ )
|
||||
+}
|
||||
+
|
||||
+fn is_elan_touchpad_id(id: &str) -> bool {
|
||||
+ id.starts_with("ELAN")
|
||||
+}
|
||||
+
|
||||
+fn is_cypress_touchpad_id(id: &str) -> bool {
|
||||
+ id.starts_with("CYAP")
|
||||
+}
|
||||
+
|
||||
+fn is_synaptics_rmi_id(id: &str) -> bool {
|
||||
+ id.starts_with("SYNA")
|
||||
+}
|
||||
+
|
||||
+fn is_non_hid_i2c_input_id(id: &str) -> bool {
|
||||
+ is_elan_touchpad_id(id) || is_cypress_touchpad_id(id) || is_synaptics_rmi_id(id)
|
||||
+}
|
||||
|
||||
diff --git a/drivers/pcid-spawner/src/main.rs b/drivers/pcid-spawner/src/main.rs
|
||||
--- a/drivers/pcid-spawner/src/main.rs
|
||||
+++ b/drivers/pcid-spawner/src/main.rs
|
||||
@@ -1,11 +1,40 @@
|
||||
+use std::env;
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
+use std::thread;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
|
||||
use pcid_interface::config::Config;
|
||||
use pcid_interface::PciFunctionHandle;
|
||||
|
||||
+fn strict_usb_boot() -> bool {
|
||||
+ matches!(
|
||||
+ env::var("REDBEAR_STRICT_USB_BOOT")
|
||||
+ .ok()
|
||||
+ .as_deref()
|
||||
+ .map(str::to_ascii_lowercase)
|
||||
+ .as_deref(),
|
||||
+ Some("1" | "true" | "yes" | "on")
|
||||
+ )
|
||||
+}
|
||||
+
|
||||
+fn should_detach_in_initfs(initfs: bool, class: u8, subclass: u8, strict_usb_boot: bool) -> bool {
|
||||
+ if !initfs {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if class == 0x01 {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if strict_usb_boot && class == 0x0C && subclass == 0x03 {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ true
|
||||
+}
|
||||
+
|
||||
fn main() -> Result<()> {
|
||||
let mut args = pico_args::Arguments::from_env();
|
||||
let initfs = args.contains("--initfs");
|
||||
@@ -30,6 +59,7 @@
|
||||
}
|
||||
|
||||
let config: Config = toml::from_str(&config_data)?;
|
||||
+ let strict_usb_boot = strict_usb_boot();
|
||||
|
||||
for entry in fs::read_dir("/scheme/pci")? {
|
||||
let entry = entry.context("failed to get entry")?;
|
||||
@@ -87,14 +117,70 @@
|
||||
|
||||
log::info!("pcid-spawner: spawn {:?}", command);
|
||||
|
||||
+ let device_addr = handle.config().func.addr;
|
||||
+
|
||||
handle.enable_device();
|
||||
|
||||
let channel_fd = handle.into_inner_fd();
|
||||
command.env("PCID_CLIENT_CHANNEL", channel_fd.to_string());
|
||||
|
||||
#[allow(deprecated, reason = "we can't yet move this to init")]
|
||||
- daemon::Daemon::spawn(command);
|
||||
- syscall::close(channel_fd as usize).unwrap();
|
||||
+ if should_detach_in_initfs(
|
||||
+ initfs,
|
||||
+ full_device_id.class,
|
||||
+ full_device_id.subclass,
|
||||
+ strict_usb_boot,
|
||||
+ ) {
|
||||
+ log::warn!(
|
||||
+ "pcid-spawner: detached initfs spawn for {} to avoid blocking early boot",
|
||||
+ device_addr
|
||||
+ );
|
||||
+
|
||||
+ let device_addr = device_addr.to_string();
|
||||
+ thread::spawn(move || {
|
||||
+ #[allow(deprecated, reason = "we can't yet move this to init")]
|
||||
+ if let Err(err) = daemon::Daemon::spawn(command) {
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: spawn/readiness failed for {}: {}",
|
||||
+ device_addr,
|
||||
+ err
|
||||
+ );
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: {} remains enabled without a confirmed ready driver",
|
||||
+ device_addr
|
||||
+ );
|
||||
+ }
|
||||
+ if let Err(err) = syscall::close(channel_fd as usize) {
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: failed to close channel fd {} for {}: {}",
|
||||
+ channel_fd,
|
||||
+ device_addr,
|
||||
+ err
|
||||
+ );
|
||||
+ }
|
||||
+ });
|
||||
+ } else {
|
||||
+ #[allow(deprecated, reason = "we can't yet move this to init")]
|
||||
+ if let Err(err) = daemon::Daemon::spawn(command) {
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: spawn/readiness failed for {}: {}",
|
||||
+ device_addr,
|
||||
+ err
|
||||
+ );
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: {} remains enabled without a confirmed ready driver",
|
||||
+ device_addr
|
||||
+ );
|
||||
+ }
|
||||
+ if let Err(err) = syscall::close(channel_fd as usize) {
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: failed to close channel fd {} for {}: {}",
|
||||
+ channel_fd,
|
||||
+ device_addr,
|
||||
+ err
|
||||
+ );
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
diff --git a/drivers/pcid/src/main.rs b/drivers/pcid/src/main.rs
|
||||
--- a/drivers/pcid/src/main.rs
|
||||
+++ b/drivers/pcid/src/main.rs
|
||||
@@ -12,6 +12,7 @@
|
||||
};
|
||||
use redox_scheme::scheme::register_sync_scheme;
|
||||
use scheme_utils::Blocking;
|
||||
+use syscall::{sendfd, SendFdFlags};
|
||||
|
||||
use crate::cfg_access::Pcie;
|
||||
use pcid_interface::{FullDeviceId, LegacyInterruptLine, PciBar, PciFunction, PciRom};
|
||||
@@ -262,14 +263,13 @@
|
||||
let access_fd = socket
|
||||
.create_this_scheme_fd(0, access_id, syscall::O_RDWR, 0)
|
||||
.expect("failed to issue this resource");
|
||||
- let access_bytes = access_fd.to_ne_bytes();
|
||||
- let _ = register_pci
|
||||
- .call_wo(
|
||||
- &access_bytes,
|
||||
- syscall::CallFlags::WRITE | syscall::CallFlags::FD,
|
||||
- &[],
|
||||
- )
|
||||
- .expect("failed to send pci_fd to acpid");
|
||||
+ sendfd(
|
||||
+ register_pci.raw(),
|
||||
+ access_fd as usize,
|
||||
+ SendFdFlags::empty().bits(),
|
||||
+ 0,
|
||||
+ )
|
||||
+ .expect("failed to send pci_fd to acpid");
|
||||
}
|
||||
Err(err) => {
|
||||
if err.errno() == libredox::errno::ENODEV {
|
||||
@@ -1,144 +0,0 @@
|
||||
# P2-boot-runtime-noise-and-net-race.patch
|
||||
#
|
||||
# Reduce expected boot-time warning noise and harden netstack startup ordering:
|
||||
# - procmgr: unknown cancellation is trace-level (benign race)
|
||||
# - acpid: warn once for unsupported power surface
|
||||
# - ahcid: SATAPI probe failures are informational on empty media
|
||||
# - netstack: retry network adapter discovery during early boot races
|
||||
|
||||
diff --git a/bootstrap/src/procmgr.rs b/bootstrap/src/procmgr.rs
|
||||
--- a/bootstrap/src/procmgr.rs
|
||||
+++ b/bootstrap/src/procmgr.rs
|
||||
@@ -296,7 +296,7 @@ fn handle_scheme<'a>(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
- log::warn!("Cancellation for unknown id {:?}", req.id);
|
||||
+ log::trace!("Cancellation for unknown id {:?}", req.id);
|
||||
Pending
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/drivers/acpid/src/scheme.rs b/drivers/acpid/src/scheme.rs
|
||||
--- a/drivers/acpid/src/scheme.rs
|
||||
+++ b/drivers/acpid/src/scheme.rs
|
||||
@@ -8,6 +8,7 @@ use ron::de::SpannedError;
|
||||
use scheme_utils::HandleMap;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::str::FromStr;
|
||||
+use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use syscall::dirent::{DirEntry, DirentBuf, DirentKind};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::FobtainFdFlags;
|
||||
@@ -29,6 +30,8 @@ use crate::acpi::{
|
||||
};
|
||||
use crate::resources::{decode_resource_template, ResourceDescriptor};
|
||||
|
||||
+static POWER_SURFACE_UNAVAILABLE_WARNED: AtomicBool = AtomicBool::new(false);
|
||||
+
|
||||
pub struct AcpiScheme<'acpi, 'sock> {
|
||||
ctx: &'acpi AcpiContext,
|
||||
handles: HandleMap<Handle<'acpi>>,
|
||||
@@ -307,7 +310,9 @@ impl<'acpi, 'sock> AcpiScheme<'acpi, 'sock> {
|
||||
self.ctx.power_snapshot().map_err(|error| match error {
|
||||
crate::acpi::AmlEvalError::NotInitialized => Error::new(EAGAIN),
|
||||
crate::acpi::AmlEvalError::Unsupported(message) => {
|
||||
- log::warn!("ACPI power surface unavailable: {message}");
|
||||
+ if !POWER_SURFACE_UNAVAILABLE_WARNED.swap(true, Ordering::Relaxed) {
|
||||
+ log::warn!("ACPI power surface unavailable: {message}");
|
||||
+ }
|
||||
Error::new(EOPNOTSUPP)
|
||||
}
|
||||
other => {
|
||||
|
||||
diff --git a/drivers/storage/ahcid/src/ahci/mod.rs b/drivers/storage/ahcid/src/ahci/mod.rs
|
||||
--- a/drivers/storage/ahcid/src/ahci/mod.rs
|
||||
+++ b/drivers/storage/ahcid/src/ahci/mod.rs
|
||||
@@ -64,7 +64,7 @@ pub fn disks(base: usize, name: &str) -> (&'static mut HbaMem, Vec<AnyDisk>) {
|
||||
HbaPortType::SATAPI => match DiskATAPI::new(i, port) {
|
||||
Ok(disk) => Some(AnyDisk::Atapi(disk)),
|
||||
Err(err) => {
|
||||
- error!("{}: {}", i, err);
|
||||
+ info!("{}: {}", i, err);
|
||||
None
|
||||
}
|
||||
},
|
||||
|
||||
diff --git a/netstack/src/main.rs b/netstack/src/main.rs
|
||||
--- a/netstack/src/main.rs
|
||||
+++ b/netstack/src/main.rs
|
||||
@@ -6,6 +6,8 @@ use anyhow::{anyhow, bail, Context, Result};
|
||||
use event::{EventFlags, EventQueue};
|
||||
use libredox::flag::{O_NONBLOCK, O_RDWR};
|
||||
use libredox::Fd;
|
||||
+use std::thread;
|
||||
+use std::time::Duration;
|
||||
|
||||
use redox_scheme::Socket;
|
||||
use scheme::Smolnetd;
|
||||
@@ -22,34 +24,47 @@ mod scheme;
|
||||
fn get_network_adapter() -> Result<String> {
|
||||
use std::fs;
|
||||
|
||||
- let mut adapters = vec![];
|
||||
+ const MAX_ATTEMPTS: u32 = 50;
|
||||
+ const RETRY_DELAY: Duration = Duration::from_millis(100);
|
||||
|
||||
- for entry_res in fs::read_dir("/scheme")? {
|
||||
- let Ok(entry) = entry_res else {
|
||||
- continue;
|
||||
- };
|
||||
+ for attempt in 1..=MAX_ATTEMPTS {
|
||||
+ let mut adapters = vec![];
|
||||
|
||||
- let Ok(scheme) = entry.file_name().into_string() else {
|
||||
- continue;
|
||||
- };
|
||||
+ for entry_res in fs::read_dir("/scheme")? {
|
||||
+ let Ok(entry) = entry_res else {
|
||||
+ continue;
|
||||
+ };
|
||||
|
||||
- if !scheme.starts_with("network") {
|
||||
- continue;
|
||||
- }
|
||||
+ let Ok(scheme) = entry.file_name().into_string() else {
|
||||
+ continue;
|
||||
+ };
|
||||
|
||||
- adapters.push(scheme);
|
||||
- }
|
||||
+ if !scheme.starts_with("network") {
|
||||
+ continue;
|
||||
+ }
|
||||
|
||||
- if adapters.is_empty() {
|
||||
- bail!("no network adapter found");
|
||||
- } else {
|
||||
- let adapter = adapters.remove(0);
|
||||
+ adapters.push(scheme);
|
||||
+ }
|
||||
+
|
||||
if !adapters.is_empty() {
|
||||
- // FIXME allow using multiple network adapters at the same time
|
||||
- warn!("Multiple network adapters found. Only {adapter} will be used");
|
||||
+ let adapter = adapters.remove(0);
|
||||
+ if !adapters.is_empty() {
|
||||
+ // FIXME allow using multiple network adapters at the same time
|
||||
+ warn!("Multiple network adapters found. Only {adapter} will be used");
|
||||
+ }
|
||||
+ return Ok(adapter);
|
||||
+ }
|
||||
+
|
||||
+ if attempt < MAX_ATTEMPTS {
|
||||
+ warn!(
|
||||
+ "no network adapter found yet (attempt {attempt}/{MAX_ATTEMPTS}), waiting {} ms",
|
||||
+ RETRY_DELAY.as_millis()
|
||||
+ );
|
||||
+ thread::sleep(RETRY_DELAY);
|
||||
}
|
||||
- Ok(adapter)
|
||||
}
|
||||
+
|
||||
+ bail!("no network adapter found")
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
# P2-hwd-misc.patch
|
||||
# Keep hwd focused on hardware probing. Init owns boot-time pcid startup.
|
||||
|
||||
diff --git a/drivers/hwd/src/main.rs b/drivers/hwd/src/main.rs
|
||||
index 79360e34..4de3d9f3 100644
|
||||
--- a/drivers/hwd/src/main.rs
|
||||
+++ b/drivers/hwd/src/main.rs
|
||||
@@ -37,11 +37,6 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
|
||||
//TODO: launch pcid based on backend information?
|
||||
// Must launch after acpid but before probe calls /scheme/acpi/symbols
|
||||
- #[allow(deprecated, reason = "we can't yet move this to init")]
|
||||
- daemon::Daemon::spawn(process::Command::new("pcid"));
|
||||
-
|
||||
daemon.ready();
|
||||
|
||||
//TODO: HWD is meant to locate PCI/XHCI/etc devices in ACPI and DeviceTree definitions and start their drivers
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
diff --git a/init.initfs.d/41_acpid.service b/init.initfs.d/41_acpid.service
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/init.initfs.d/41_acpid.service
|
||||
@@ -0,0 +1,7 @@
|
||||
+[unit]
|
||||
+description = "ACPI daemon"
|
||||
+default_dependencies = false
|
||||
+
|
||||
+[service]
|
||||
+cmd = "acpid"
|
||||
+inherit_envs = ["RSDP_ADDR", "RSDP_SIZE"]
|
||||
+type = "notify"
|
||||
diff --git a/init.initfs.d/40_drivers.target b/init.initfs.d/40_drivers.target
|
||||
--- a/init.initfs.d/40_drivers.target
|
||||
+++ b/init.initfs.d/40_drivers.target
|
||||
@@ -7,4 +7,5 @@ requires_weak = [
|
||||
"40_bcm2835-sdhcid.service",
|
||||
"40_hwd.service",
|
||||
"40_pcid-spawner-initfs.service",
|
||||
+ "41_acpid.service",
|
||||
]
|
||||
diff --git a/init.initfs.d/40_hwd.service b/init.initfs.d/40_hwd.service
|
||||
--- a/init.initfs.d/40_hwd.service
|
||||
+++ b/init.initfs.d/40_hwd.service
|
||||
@@ -1,6 +1,6 @@
|
||||
[unit]
|
||||
description = "Hardware manager"
|
||||
-requires_weak = ["10_inputd.service", "10_lived.service", "20_graphics.target"]
|
||||
+requires_weak = ["10_inputd.service", "10_lived.service", "20_graphics.target", "41_acpid.service"]
|
||||
|
||||
[service]
|
||||
cmd = "hwd"
|
||||
@@ -1,607 +0,0 @@
|
||||
# P2-network-driver-mains.patch
|
||||
# Extract network driver main.rs hardening: replace panic/unwrap/expect with
|
||||
# proper error handling and graceful exits.
|
||||
#
|
||||
# Files: drivers/net/e1000d/src/main.rs, drivers/net/ixgbed/src/main.rs,
|
||||
# drivers/net/rtl8139d/src/main.rs, drivers/net/rtl8168d/src/main.rs,
|
||||
# drivers/net/virtio-netd/src/main.rs
|
||||
|
||||
diff --git a/drivers/net/e1000d/src/main.rs b/drivers/net/e1000d/src/main.rs
|
||||
index 373ea9b3..8ff57b33 100644
|
||||
--- a/drivers/net/e1000d/src/main.rs
|
||||
+++ b/drivers/net/e1000d/src/main.rs
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::io::{Read, Write};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
+use std::process;
|
||||
|
||||
use driver_network::NetworkScheme;
|
||||
use event::{user_data, EventQueue};
|
||||
@@ -25,10 +26,13 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
common::file_level(),
|
||||
);
|
||||
|
||||
- let irq = pci_config
|
||||
- .func
|
||||
- .legacy_interrupt_line
|
||||
- .expect("e1000d: no legacy interrupts supported");
|
||||
+ let irq = match pci_config.func.legacy_interrupt_line {
|
||||
+ Some(irq) => irq,
|
||||
+ None => {
|
||||
+ log::error!("e1000d: no legacy interrupts supported");
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
|
||||
log::info!("E1000 {}", pci_config.func.display());
|
||||
|
||||
@@ -38,7 +42,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
|
||||
let mut scheme = NetworkScheme::new(
|
||||
move || unsafe {
|
||||
- device::Intel8254x::new(address).expect("e1000d: failed to allocate device")
|
||||
+ device::Intel8254x::new(address).unwrap_or_else(|err| {
|
||||
+ log::error!("e1000d: failed to allocate device: {err}");
|
||||
+ process::exit(1);
|
||||
+ })
|
||||
},
|
||||
daemon,
|
||||
format!("network.{name}"),
|
||||
@@ -51,7 +58,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
- let event_queue = EventQueue::<Source>::new().expect("e1000d: failed to create event queue");
|
||||
+ let mut event_queue = EventQueue::<Source>::new().unwrap_or_else(|err| {
|
||||
+ log::error!("e1000d: failed to create event queue: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
|
||||
event_queue
|
||||
.subscribe(
|
||||
@@ -59,32 +69,65 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
Source::Irq,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
- .expect("e1000d: failed to subscribe to IRQ fd");
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ log::error!("e1000d: failed to subscribe to IRQ fd: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
event_queue
|
||||
.subscribe(
|
||||
scheme.event_handle().raw(),
|
||||
Source::Scheme,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
- .expect("e1000d: failed to subscribe to scheme fd");
|
||||
-
|
||||
- libredox::call::setrens(0, 0).expect("e1000d: failed to enter null namespace");
|
||||
-
|
||||
- scheme.tick().unwrap();
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ log::error!("e1000d: failed to subscribe to scheme fd: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
+
|
||||
+ libredox::call::setrens(0, 0).unwrap_or_else(|err| {
|
||||
+ log::error!("e1000d: failed to enter null namespace: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
+
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("e1000d: failed initial scheme tick: {err}");
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
|
||||
- for event in event_queue.map(|e| e.expect("e1000d: failed to get event")) {
|
||||
+ loop {
|
||||
+ let event = match event_queue.next() {
|
||||
+ Some(Ok(event)) => event,
|
||||
+ Some(Err(err)) => {
|
||||
+ log::error!("e1000d: failed to get event: {err}");
|
||||
+ continue;
|
||||
+ }
|
||||
+ None => break,
|
||||
+ };
|
||||
match event.user_data {
|
||||
Source::Irq => {
|
||||
let mut irq = [0; 8];
|
||||
- irq_file.read(&mut irq).unwrap();
|
||||
+ if let Err(err) = irq_file.read(&mut irq) {
|
||||
+ log::error!("e1000d: failed to read IRQ: {err}");
|
||||
+ continue;
|
||||
+ }
|
||||
if unsafe { scheme.adapter().irq() } {
|
||||
- irq_file.write(&mut irq).unwrap();
|
||||
-
|
||||
- scheme.tick().expect("e1000d: failed to handle IRQ")
|
||||
+ if let Err(err) = irq_file.write(&mut irq) {
|
||||
+ log::error!("e1000d: failed to write IRQ: {err}");
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("e1000d: failed to handle IRQ: {err}");
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ Source::Scheme => {
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("e1000d: failed to handle scheme op: {err}");
|
||||
}
|
||||
}
|
||||
- Source::Scheme => scheme.tick().expect("e1000d: failed to handle scheme op"),
|
||||
}
|
||||
}
|
||||
- unreachable!()
|
||||
+
|
||||
+ process::exit(0);
|
||||
}
|
||||
diff --git a/drivers/net/ixgbed/src/main.rs b/drivers/net/ixgbed/src/main.rs
|
||||
index 4a6ce74d..855d339d 100644
|
||||
--- a/drivers/net/ixgbed/src/main.rs
|
||||
+++ b/drivers/net/ixgbed/src/main.rs
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::io::{Read, Write};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
+use std::process;
|
||||
|
||||
use driver_network::NetworkScheme;
|
||||
use event::{user_data, EventQueue};
|
||||
@@ -19,12 +20,23 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
let mut name = pci_config.func.name();
|
||||
name.push_str("_ixgbe");
|
||||
|
||||
- let irq = pci_config
|
||||
- .func
|
||||
- .legacy_interrupt_line
|
||||
- .expect("ixgbed: no legacy interrupts supported");
|
||||
+ common::setup_logging(
|
||||
+ "net",
|
||||
+ "pci",
|
||||
+ &name,
|
||||
+ common::output_level(),
|
||||
+ common::file_level(),
|
||||
+ );
|
||||
+
|
||||
+ let irq = match pci_config.func.legacy_interrupt_line {
|
||||
+ Some(irq) => irq,
|
||||
+ None => {
|
||||
+ log::error!("ixgbed: no legacy interrupts supported");
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
|
||||
- println!(" + IXGBE {}", pci_config.func.display());
|
||||
+ log::info!("IXGBE {}", pci_config.func.display());
|
||||
|
||||
let mut irq_file = irq.irq_handle("ixgbed");
|
||||
|
||||
@@ -34,8 +46,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
|
||||
let mut scheme = NetworkScheme::new(
|
||||
move || {
|
||||
- device::Intel8259x::new(address as usize, size)
|
||||
- .expect("ixgbed: failed to allocate device")
|
||||
+ device::Intel8259x::new(address as usize, size).unwrap_or_else(|err| {
|
||||
+ log::error!("ixgbed: failed to allocate device: {err}");
|
||||
+ process::exit(1);
|
||||
+ })
|
||||
},
|
||||
daemon,
|
||||
format!("network.{name}"),
|
||||
@@ -48,41 +62,77 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
- let event_queue = EventQueue::<Source>::new().expect("ixgbed: Could not create event queue.");
|
||||
+ let mut event_queue = EventQueue::<Source>::new().unwrap_or_else(|err| {
|
||||
+ log::error!("ixgbed: failed to create event queue: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
+
|
||||
event_queue
|
||||
.subscribe(
|
||||
irq_file.as_raw_fd() as usize,
|
||||
Source::Irq,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
- .unwrap();
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ log::error!("ixgbed: failed to subscribe to IRQ fd: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
event_queue
|
||||
.subscribe(
|
||||
scheme.event_handle().raw(),
|
||||
Source::Scheme,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
- .unwrap();
|
||||
-
|
||||
- libredox::call::setrens(0, 0).expect("ixgbed: failed to enter null namespace");
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ log::error!("ixgbed: failed to subscribe to scheme fd: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
+
|
||||
+ libredox::call::setrens(0, 0).unwrap_or_else(|err| {
|
||||
+ log::error!("ixgbed: failed to enter null namespace: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
+
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("ixgbed: failed initial scheme tick: {err}");
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
|
||||
- scheme.tick().unwrap();
|
||||
+ loop {
|
||||
+ let event = match event_queue.next() {
|
||||
+ Some(Ok(event)) => event,
|
||||
+ Some(Err(err)) => {
|
||||
+ log::error!("ixgbed: failed to get event: {err}");
|
||||
+ continue;
|
||||
+ }
|
||||
+ None => break,
|
||||
+ };
|
||||
|
||||
- for event in event_queue.map(|e| e.expect("ixgbed: failed to get next event")) {
|
||||
match event.user_data {
|
||||
Source::Irq => {
|
||||
let mut irq = [0; 8];
|
||||
- irq_file.read(&mut irq).unwrap();
|
||||
+ if let Err(err) = irq_file.read(&mut irq) {
|
||||
+ log::error!("ixgbed: failed to read IRQ: {err}");
|
||||
+ continue;
|
||||
+ }
|
||||
if scheme.adapter().irq() {
|
||||
- irq_file.write(&mut irq).unwrap();
|
||||
-
|
||||
- scheme.tick().unwrap();
|
||||
+ if let Err(err) = irq_file.write(&mut irq) {
|
||||
+ log::error!("ixgbed: failed to write IRQ: {err}");
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("ixgbed: failed to handle IRQ: {err}");
|
||||
+ }
|
||||
}
|
||||
}
|
||||
Source::Scheme => {
|
||||
- scheme.tick().unwrap();
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("ixgbed: failed to handle scheme op: {err}");
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
- unreachable!()
|
||||
+
|
||||
+ process::exit(0);
|
||||
}
|
||||
diff --git a/drivers/net/rtl8139d/src/main.rs b/drivers/net/rtl8139d/src/main.rs
|
||||
index d470e814..64335a23 100644
|
||||
--- a/drivers/net/rtl8139d/src/main.rs
|
||||
+++ b/drivers/net/rtl8139d/src/main.rs
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::io::{Read, Write};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
+use std::process;
|
||||
|
||||
use driver_network::NetworkScheme;
|
||||
use event::{user_data, EventQueue};
|
||||
@@ -32,7 +33,8 @@ fn map_bar(pcid_handle: &mut PciFunctionHandle) -> *mut u8 {
|
||||
other => log::warn!("BAR {} is {:?} instead of memory BAR", barnum, other),
|
||||
}
|
||||
}
|
||||
- panic!("rtl8139d: failed to find BAR");
|
||||
+ log::error!("rtl8139d: failed to find BAR");
|
||||
+ process::exit(1);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@@ -61,7 +63,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
|
||||
let mut scheme = NetworkScheme::new(
|
||||
move || unsafe {
|
||||
- device::Rtl8139::new(bar as usize).expect("rtl8139d: failed to allocate device")
|
||||
+ device::Rtl8139::new(bar as usize).unwrap_or_else(|err| {
|
||||
+ log::error!("rtl8139d: failed to allocate device: {err}");
|
||||
+ process::exit(1);
|
||||
+ })
|
||||
},
|
||||
daemon,
|
||||
format!("network.{name}"),
|
||||
@@ -74,42 +79,76 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
- let event_queue = EventQueue::<Source>::new().expect("rtl8139d: Could not create event queue.");
|
||||
+ let mut event_queue = EventQueue::<Source>::new().unwrap_or_else(|err| {
|
||||
+ log::error!("rtl8139d: failed to create event queue: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
event_queue
|
||||
.subscribe(
|
||||
irq_file.irq_handle().as_raw_fd() as usize,
|
||||
Source::Irq,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
- .unwrap();
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ log::error!("rtl8139d: failed to subscribe to IRQ fd: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
event_queue
|
||||
.subscribe(
|
||||
scheme.event_handle().raw(),
|
||||
Source::Scheme,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
- .unwrap();
|
||||
-
|
||||
- libredox::call::setrens(0, 0).expect("rtl8139d: failed to enter null namespace");
|
||||
-
|
||||
- scheme.tick().unwrap();
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ log::error!("rtl8139d: failed to subscribe to scheme fd: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
+
|
||||
+ libredox::call::setrens(0, 0).unwrap_or_else(|err| {
|
||||
+ log::error!("rtl8139d: failed to enter null namespace: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
+
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("rtl8139d: failed initial scheme tick: {err}");
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
|
||||
- for event in event_queue.map(|e| e.expect("rtl8139d: failed to get next event")) {
|
||||
+ loop {
|
||||
+ let event = match event_queue.next() {
|
||||
+ Some(Ok(event)) => event,
|
||||
+ Some(Err(err)) => {
|
||||
+ log::error!("rtl8139d: failed to get next event: {err}");
|
||||
+ continue;
|
||||
+ }
|
||||
+ None => break,
|
||||
+ };
|
||||
match event.user_data {
|
||||
Source::Irq => {
|
||||
let mut irq = [0; 8];
|
||||
- irq_file.irq_handle().read(&mut irq).unwrap();
|
||||
+ if let Err(err) = irq_file.irq_handle().read(&mut irq) {
|
||||
+ log::error!("rtl8139d: failed to read IRQ: {err}");
|
||||
+ continue;
|
||||
+ }
|
||||
//TODO: This may be causing spurious interrupts
|
||||
if unsafe { scheme.adapter_mut().irq() } {
|
||||
- irq_file.irq_handle().write(&mut irq).unwrap();
|
||||
-
|
||||
- scheme.tick().unwrap();
|
||||
+ if let Err(err) = irq_file.irq_handle().write(&mut irq) {
|
||||
+ log::error!("rtl8139d: failed to write IRQ: {err}");
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("rtl8139d: failed to handle IRQ tick: {err}");
|
||||
+ }
|
||||
}
|
||||
}
|
||||
Source::Scheme => {
|
||||
- scheme.tick().unwrap();
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("rtl8139d: failed to handle scheme op: {err}");
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
- unreachable!()
|
||||
+
|
||||
+ process::exit(0);
|
||||
}
|
||||
diff --git a/drivers/net/rtl8168d/src/main.rs b/drivers/net/rtl8168d/src/main.rs
|
||||
index 1d9963a3..bd2fcb1a 100644
|
||||
--- a/drivers/net/rtl8168d/src/main.rs
|
||||
+++ b/drivers/net/rtl8168d/src/main.rs
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::io::{Read, Write};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
+use std::process;
|
||||
|
||||
use driver_network::NetworkScheme;
|
||||
use event::{user_data, EventQueue};
|
||||
@@ -32,7 +33,8 @@ fn map_bar(pcid_handle: &mut PciFunctionHandle) -> *mut u8 {
|
||||
other => log::warn!("BAR {} is {:?} instead of memory BAR", barnum, other),
|
||||
}
|
||||
}
|
||||
- panic!("rtl8168d: failed to find BAR");
|
||||
+ log::error!("rtl8168d: failed to find BAR");
|
||||
+ process::exit(1);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@@ -61,7 +63,10 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
|
||||
let mut scheme = NetworkScheme::new(
|
||||
move || unsafe {
|
||||
- device::Rtl8168::new(bar as usize).expect("rtl8168d: failed to allocate device")
|
||||
+ device::Rtl8168::new(bar as usize).unwrap_or_else(|err| {
|
||||
+ log::error!("rtl8168d: failed to allocate device: {err}");
|
||||
+ process::exit(1);
|
||||
+ })
|
||||
},
|
||||
daemon,
|
||||
format!("network.{name}"),
|
||||
@@ -74,42 +79,76 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
- let event_queue = EventQueue::<Source>::new().expect("rtl8168d: Could not create event queue.");
|
||||
+ let mut event_queue = EventQueue::<Source>::new().unwrap_or_else(|err| {
|
||||
+ log::error!("rtl8168d: failed to create event queue: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
event_queue
|
||||
.subscribe(
|
||||
irq_file.irq_handle().as_raw_fd() as usize,
|
||||
Source::Irq,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
- .unwrap();
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ log::error!("rtl8168d: failed to subscribe to IRQ fd: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
event_queue
|
||||
.subscribe(
|
||||
scheme.event_handle().raw(),
|
||||
Source::Scheme,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
- .unwrap();
|
||||
-
|
||||
- libredox::call::setrens(0, 0).expect("rtl8168d: failed to enter null namespace");
|
||||
-
|
||||
- scheme.tick().unwrap();
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ log::error!("rtl8168d: failed to subscribe to scheme fd: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
+
|
||||
+ libredox::call::setrens(0, 0).unwrap_or_else(|err| {
|
||||
+ log::error!("rtl8168d: failed to enter null namespace: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
+
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("rtl8168d: failed initial scheme tick: {err}");
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
|
||||
- for event in event_queue.map(|e| e.expect("rtl8168d: failed to get next event")) {
|
||||
+ loop {
|
||||
+ let event = match event_queue.next() {
|
||||
+ Some(Ok(event)) => event,
|
||||
+ Some(Err(err)) => {
|
||||
+ log::error!("rtl8168d: failed to get next event: {err}");
|
||||
+ continue;
|
||||
+ }
|
||||
+ None => break,
|
||||
+ };
|
||||
match event.user_data {
|
||||
Source::Irq => {
|
||||
let mut irq = [0; 8];
|
||||
- irq_file.irq_handle().read(&mut irq).unwrap();
|
||||
+ if let Err(err) = irq_file.irq_handle().read(&mut irq) {
|
||||
+ log::error!("rtl8168d: failed to read IRQ: {err}");
|
||||
+ continue;
|
||||
+ }
|
||||
//TODO: This may be causing spurious interrupts
|
||||
if unsafe { scheme.adapter_mut().irq() } {
|
||||
- irq_file.irq_handle().write(&mut irq).unwrap();
|
||||
-
|
||||
- scheme.tick().unwrap();
|
||||
+ if let Err(err) = irq_file.irq_handle().write(&mut irq) {
|
||||
+ log::error!("rtl8168d: failed to write IRQ: {err}");
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("rtl8168d: failed to handle IRQ tick: {err}");
|
||||
+ }
|
||||
}
|
||||
}
|
||||
Source::Scheme => {
|
||||
- scheme.tick().unwrap();
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("rtl8168d: failed to handle scheme op: {err}");
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
- unreachable!()
|
||||
+
|
||||
+ process::exit(0);
|
||||
}
|
||||
diff --git a/drivers/net/virtio-netd/src/main.rs b/drivers/net/virtio-netd/src/main.rs
|
||||
index 17d168ef..adbd1086 100644
|
||||
--- a/drivers/net/virtio-netd/src/main.rs
|
||||
+++ b/drivers/net/virtio-netd/src/main.rs
|
||||
@@ -3,6 +3,7 @@ mod scheme;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::mem;
|
||||
+use std::process;
|
||||
|
||||
use driver_network::NetworkScheme;
|
||||
use pcid_interface::PciFunctionHandle;
|
||||
@@ -31,8 +32,11 @@ fn main() {
|
||||
}
|
||||
|
||||
fn daemon_runner(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||
- deamon(daemon, pcid_handle).unwrap();
|
||||
- unreachable!();
|
||||
+ deamon(daemon, pcid_handle).unwrap_or_else(|err| {
|
||||
+ log::error!("virtio-netd: daemon failed: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
+ process::exit(0);
|
||||
}
|
||||
|
||||
fn deamon(
|
||||
@@ -52,7 +56,10 @@ fn deamon(
|
||||
// 0x1000 - virtio-net
|
||||
let pci_config = pcid_handle.config();
|
||||
|
||||
- assert_eq!(pci_config.func.full_device_id.device_id, 0x1000);
|
||||
+ if pci_config.func.full_device_id.device_id != 0x1000 {
|
||||
+ log::error!("virtio-netd: unexpected device ID {:#06x}, expected 0x1000", pci_config.func.full_device_id.device_id);
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
log::info!("virtio-net: initiating startup sequence :^)");
|
||||
|
||||
let device = virtio_core::probe_device(&mut pcid_handle)?;
|
||||
@@ -84,7 +91,8 @@ fn deamon(
|
||||
device.transport.ack_driver_feature(VIRTIO_NET_F_MAC);
|
||||
mac
|
||||
} else {
|
||||
- unimplemented!()
|
||||
+ log::error!("virtio-netd: device does not support MAC feature");
|
||||
+ return Err("virtio-netd: VIRTIO_NET_F_MAC not supported".into());
|
||||
};
|
||||
|
||||
device.transport.finalize_features();
|
||||
@@ -126,12 +134,23 @@ fn deamon(
|
||||
data: 0,
|
||||
})?;
|
||||
|
||||
- libredox::call::setrens(0, 0).expect("virtio-netd: failed to enter null namespace");
|
||||
+ libredox::call::setrens(0, 0).unwrap_or_else(|err| {
|
||||
+ log::error!("virtio-netd: failed to enter null namespace: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
|
||||
- scheme.tick()?;
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("virtio-netd: failed initial scheme tick: {err}");
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
|
||||
loop {
|
||||
- event_queue.read(&mut [0; mem::size_of::<syscall::Event>()])?; // Wait for event
|
||||
- scheme.tick()?;
|
||||
+ if let Err(err) = event_queue.read(&mut [0; mem::size_of::<syscall::Event>()]) {
|
||||
+ log::error!("virtio-netd: failed to read event: {err}");
|
||||
+ continue;
|
||||
+ }
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("virtio-netd: failed to handle scheme event: {err}");
|
||||
+ }
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
# P2-network-error-handling.patch
|
||||
#
|
||||
# Network driver error handling: replace unwrap()/expect()/panic!() with proper
|
||||
# error propagation and graceful exits across e1000, ixgbe, rtl8139, rtl8168d,
|
||||
# and virtio-net drivers.
|
||||
#
|
||||
# Covers:
|
||||
# - e1000d/device.rs: replace unreachable!() in DMA array conversion
|
||||
# - ixgbed/Cargo.toml: add log dependency
|
||||
# - rtl8139d/device.rs: replace unreachable!() with EIO error
|
||||
# - rtl8168d/device.rs: replace unreachable!() with EIO error
|
||||
# - virtio-netd/scheme.rs: DMA allocation error handling for rx buffers
|
||||
#
|
||||
diff --git a/drivers/net/e1000d/src/device.rs b/drivers/net/e1000d/src/device.rs
|
||||
index 4c518f30..0e42d72b 100644
|
||||
--- a/drivers/net/e1000d/src/device.rs
|
||||
+++ b/drivers/net/e1000d/src/device.rs
|
||||
@@ -3,7 +3,7 @@ use std::{cmp, mem, ptr, slice, thread, time};
|
||||
|
||||
use driver_network::NetworkAdapter;
|
||||
|
||||
-use syscall::error::Result;
|
||||
+use syscall::error::{Error, Result, EIO};
|
||||
|
||||
use common::dma::Dma;
|
||||
|
||||
@@ -207,11 +207,10 @@ impl NetworkAdapter for Intel8254x {
|
||||
}
|
||||
|
||||
fn dma_array<T, const N: usize>() -> Result<[Dma<T>; N]> {
|
||||
- Ok((0..N)
|
||||
+ let vec: Vec<Dma<T>> = (0..N)
|
||||
.map(|_| Ok(unsafe { Dma::zeroed()?.assume_init() }))
|
||||
- .collect::<Result<Vec<_>>>()?
|
||||
- .try_into()
|
||||
- .unwrap_or_else(|_| unreachable!()))
|
||||
+ .collect::<Result<Vec<_>>>()?;
|
||||
+ vec.try_into().map_err(|_| Error::new(EIO))
|
||||
}
|
||||
impl Intel8254x {
|
||||
pub unsafe fn new(base: usize) -> Result<Self> {
|
||||
|
||||
diff --git a/drivers/net/ixgbed/Cargo.toml b/drivers/net/ixgbed/Cargo.toml
|
||||
index d97ff398..fcaf4b19 100644
|
||||
--- a/drivers/net/ixgbed/Cargo.toml
|
||||
+++ b/drivers/net/ixgbed/Cargo.toml
|
||||
@@ -7,6 +7,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
bitflags.workspace = true
|
||||
libredox.workspace = true
|
||||
+log.workspace = true
|
||||
redox_event.workspace = true
|
||||
redox_syscall.workspace = true
|
||||
|
||||
|
||||
diff --git a/drivers/net/rtl8139d/src/device.rs b/drivers/net/rtl8139d/src/device.rs
|
||||
index 37167ee2..d7428132 100644
|
||||
--- a/drivers/net/rtl8139d/src/device.rs
|
||||
+++ b/drivers/net/rtl8139d/src/device.rs
|
||||
@@ -215,7 +215,7 @@ impl Rtl8139 {
|
||||
.map(|_| Ok(Dma::zeroed()?.assume_init()))
|
||||
.collect::<Result<Vec<_>>>()?
|
||||
.try_into()
|
||||
- .unwrap_or_else(|_| unreachable!()),
|
||||
+ .map_err(|_| Error::new(EIO))?,
|
||||
transmit_i: 0,
|
||||
mac_address: [0; 6],
|
||||
};
|
||||
|
||||
diff --git a/drivers/net/rtl8168d/src/device.rs b/drivers/net/rtl8168d/src/device.rs
|
||||
index ae545ec4..7229a52d 100644
|
||||
--- a/drivers/net/rtl8168d/src/device.rs
|
||||
+++ b/drivers/net/rtl8168d/src/device.rs
|
||||
@@ -177,7 +177,7 @@ impl Rtl8168 {
|
||||
.map(|_| Ok(Dma::zeroed()?.assume_init()))
|
||||
.collect::<Result<Vec<_>>>()?
|
||||
.try_into()
|
||||
- .unwrap_or_else(|_| unreachable!()),
|
||||
+ .map_err(|_| Error::new(EIO))?,
|
||||
|
||||
receive_ring: Dma::zeroed()?.assume_init(),
|
||||
receive_i: 0,
|
||||
@@ -185,7 +185,7 @@ impl Rtl8168 {
|
||||
.map(|_| Ok(Dma::zeroed()?.assume_init()))
|
||||
.collect::<Result<Vec<_>>>()?
|
||||
.try_into()
|
||||
- .unwrap_or_else(|_| unreachable!()),
|
||||
+ .map_err(|_| Error::new(EIO))?,
|
||||
transmit_ring: Dma::zeroed()?.assume_init(),
|
||||
transmit_i: 0,
|
||||
transmit_buffer_h: [Dma::zeroed()?.assume_init()],
|
||||
|
||||
diff --git a/drivers/net/virtio-netd/src/scheme.rs b/drivers/net/virtio-netd/src/scheme.rs
|
||||
index 59b3b93e..d0acb2ba 100644
|
||||
--- a/drivers/net/virtio-netd/src/scheme.rs
|
||||
+++ b/drivers/net/virtio-netd/src/scheme.rs
|
||||
@@ -27,11 +27,16 @@ impl<'a> VirtioNet<'a> {
|
||||
// Populate all of the `rx_queue` with buffers to maximize performence.
|
||||
let mut rx_buffers = vec![];
|
||||
for i in 0..(rx.descriptor_len() as usize) {
|
||||
- rx_buffers.push(unsafe {
|
||||
- Dma::<[u8]>::zeroed_slice(MAX_BUFFER_LEN)
|
||||
- .unwrap()
|
||||
- .assume_init()
|
||||
- });
|
||||
+ let buf = unsafe {
|
||||
+ match Dma::<[u8]>::zeroed_slice(MAX_BUFFER_LEN) {
|
||||
+ Ok(dma) => dma.assume_init(),
|
||||
+ Err(err) => {
|
||||
+ log::error!("virtio-netd: failed to allocate rx buffer: {err}");
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+ rx_buffers.push(buf);
|
||||
|
||||
let chain = ChainBuilder::new()
|
||||
.chain(Buffer::new_unsized(&rx_buffers[i]).flags(DescriptorFlags::WRITE_ONLY))
|
||||
@@ -1,601 +0,0 @@
|
||||
# P2-storage-error-handling.patch
|
||||
#
|
||||
# Storage driver error handling: replace unwrap()/expect()/panic!() with proper
|
||||
# error propagation and graceful exits across AHCI, IDE, NVMe, and VirtIO block drivers.
|
||||
#
|
||||
# Covers:
|
||||
# - ahcid/disk_ata.rs: replace unreachable!() with EIO error
|
||||
# - ahcid/disk_atapi.rs: replace unreachable!() with EBADF error
|
||||
# - ahcid/hba.rs: DMA allocation error handling
|
||||
# - ided/ide.rs: assert→debug_assert, try_into error handling
|
||||
# - nvmed/executor.rs: executor initialization error handling
|
||||
# - nvmed/identify.rs: DMA allocation, unreachable!() fallback
|
||||
# - nvmed/mod.rs: assert→debug_assert, unwrap→proper error/exit
|
||||
# - nvmed/queues.rs: unreachable!()→safe fallback
|
||||
# - virtio-blkd/scheme.rs: DMA allocation error handling, assert→if check
|
||||
#
|
||||
diff --git a/drivers/storage/ahcid/src/ahci/disk_ata.rs b/drivers/storage/ahcid/src/ahci/disk_ata.rs
|
||||
index 4f83c51d..7423603b 100644
|
||||
--- a/drivers/storage/ahcid/src/ahci/disk_ata.rs
|
||||
+++ b/drivers/storage/ahcid/src/ahci/disk_ata.rs
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::convert::TryInto;
|
||||
use std::ptr;
|
||||
|
||||
-use syscall::error::Result;
|
||||
+use syscall::error::{Error, Result, EIO};
|
||||
|
||||
use common::dma::Dma;
|
||||
|
||||
@@ -39,7 +39,7 @@ impl DiskATA {
|
||||
.map(|_| Ok(unsafe { Dma::zeroed()?.assume_init() }))
|
||||
.collect::<Result<Vec<_>>>()?
|
||||
.try_into()
|
||||
- .unwrap_or_else(|_| unreachable!());
|
||||
+ .map_err(|_| Error::new(EIO))?;
|
||||
|
||||
let mut fb = unsafe { Dma::zeroed()?.assume_init() };
|
||||
let buf = unsafe { Dma::zeroed()?.assume_init() };
|
||||
diff --git a/drivers/storage/ahcid/src/ahci/disk_atapi.rs b/drivers/storage/ahcid/src/ahci/disk_atapi.rs
|
||||
index a0e75c09..8fbdfbef 100644
|
||||
--- a/drivers/storage/ahcid/src/ahci/disk_atapi.rs
|
||||
+++ b/drivers/storage/ahcid/src/ahci/disk_atapi.rs
|
||||
@@ -37,7 +37,7 @@ impl DiskATAPI {
|
||||
.map(|_| Ok(unsafe { Dma::zeroed()?.assume_init() }))
|
||||
.collect::<Result<Vec<_>>>()?
|
||||
.try_into()
|
||||
- .unwrap_or_else(|_| unreachable!());
|
||||
+ .map_err(|_| Error::new(EBADF))?;
|
||||
|
||||
let mut fb = unsafe { Dma::zeroed()?.assume_init() };
|
||||
let mut buf = unsafe { Dma::zeroed()?.assume_init() };
|
||||
diff --git a/drivers/storage/ahcid/src/ahci/hba.rs b/drivers/storage/ahcid/src/ahci/hba.rs
|
||||
index bea8792c..11a3d4ae 100644
|
||||
--- a/drivers/storage/ahcid/src/ahci/hba.rs
|
||||
+++ b/drivers/storage/ahcid/src/ahci/hba.rs
|
||||
@@ -178,7 +178,10 @@ impl HbaPort {
|
||||
clb: &mut Dma<[HbaCmdHeader; 32]>,
|
||||
ctbas: &mut [Dma<HbaCmdTable>; 32],
|
||||
) -> Result<u64> {
|
||||
- let dest: Dma<[u16; 256]> = Dma::new([0; 256]).unwrap();
|
||||
+ let dest: Dma<[u16; 256]> = Dma::new([0; 256]).map_err(|err| {
|
||||
+ error!("ahcid: failed to allocate DMA buffer: {err}");
|
||||
+ Error::new(EIO)
|
||||
+ })?;
|
||||
|
||||
let slot = self
|
||||
.ata_start(clb, ctbas, |cmdheader, cmdfis, prdt_entries, _acmd| {
|
||||
|
||||
diff --git a/drivers/storage/ided/src/ide.rs b/drivers/storage/ided/src/ide.rs
|
||||
index 5faf3250..094e5889 100644
|
||||
--- a/drivers/storage/ided/src/ide.rs
|
||||
+++ b/drivers/storage/ided/src/ide.rs
|
||||
@@ -184,10 +184,10 @@ impl Disk for AtaDisk {
|
||||
let block = start_block + (count as u64) / 512;
|
||||
|
||||
//TODO: support other LBA modes
|
||||
- assert!(block < 0x1_0000_0000_0000);
|
||||
+ debug_assert!(block < 0x1_0000_0000_0000);
|
||||
|
||||
let sectors = (chunk.len() + 511) / 512;
|
||||
- assert!(sectors <= 128);
|
||||
+ debug_assert!(sectors <= 128);
|
||||
|
||||
log::trace!(
|
||||
"IDE read chan {} dev {} block {:#x} count {:#x}",
|
||||
@@ -205,7 +205,7 @@ impl Disk for AtaDisk {
|
||||
// Make PRDT EOT match chunk size
|
||||
for i in 0..sectors {
|
||||
chan.prdt[i] = PrdtEntry {
|
||||
- phys: (chan.buf.physical() + i * 512).try_into().unwrap(),
|
||||
+ phys: (chan.buf.physical() + i * 512).try_into().map_err(|_| Error::new(EIO))?,
|
||||
size: 512,
|
||||
flags: if i + 1 == sectors {
|
||||
1 << 15 // End of table
|
||||
@@ -216,7 +216,7 @@ impl Disk for AtaDisk {
|
||||
}
|
||||
// Set PRDT
|
||||
let prdt = chan.prdt.physical();
|
||||
- chan.busmaster_prdt.write(prdt.try_into().unwrap());
|
||||
+ chan.busmaster_prdt.write(prdt.try_into().map_err(|_| Error::new(EIO))?);
|
||||
// Set to read
|
||||
chan.busmaster_command.writef(1 << 3, true);
|
||||
// Clear interrupt and error bits
|
||||
@@ -325,10 +325,10 @@ impl Disk for AtaDisk {
|
||||
let block = start_block + (count as u64) / 512;
|
||||
|
||||
//TODO: support other LBA modes
|
||||
- assert!(block < 0x1_0000_0000_0000);
|
||||
+ debug_assert!(block < 0x1_0000_0000_0000);
|
||||
|
||||
let sectors = (chunk.len() + 511) / 512;
|
||||
- assert!(sectors <= 128);
|
||||
+ debug_assert!(sectors <= 128);
|
||||
|
||||
log::trace!(
|
||||
"IDE write chan {} dev {} block {:#x} count {:#x}",
|
||||
@@ -346,7 +346,7 @@ impl Disk for AtaDisk {
|
||||
// Make PRDT EOT match chunk size
|
||||
for i in 0..sectors {
|
||||
chan.prdt[i] = PrdtEntry {
|
||||
- phys: (chan.buf.physical() + i * 512).try_into().unwrap(),
|
||||
+ phys: (chan.buf.physical() + i * 512).try_into().map_err(|_| Error::new(EIO))?,
|
||||
size: 512,
|
||||
flags: if i + 1 == sectors {
|
||||
1 << 15 // End of table
|
||||
@@ -357,7 +357,7 @@ impl Disk for AtaDisk {
|
||||
}
|
||||
// Set PRDT
|
||||
let prdt = chan.prdt.physical();
|
||||
- chan.busmaster_prdt.write(prdt.try_into().unwrap());
|
||||
+ chan.busmaster_prdt.write(prdt.try_into().map_err(|_| Error::new(EIO))?);
|
||||
// Set to write
|
||||
chan.busmaster_command.writef(1 << 3, false);
|
||||
// Clear interrupt and error bits
|
||||
|
||||
diff --git a/drivers/storage/nvmed/src/nvme/executor.rs b/drivers/storage/nvmed/src/nvme/executor.rs
|
||||
index 6242fa98..c1435e88 100644
|
||||
--- a/drivers/storage/nvmed/src/nvme/executor.rs
|
||||
+++ b/drivers/storage/nvmed/src/nvme/executor.rs
|
||||
@@ -34,7 +34,12 @@ impl Hardware for NvmeHw {
|
||||
&VTABLE
|
||||
}
|
||||
fn current() -> std::rc::Rc<executor::LocalExecutor<Self>> {
|
||||
- THE_EXECUTOR.with(|exec| Rc::clone(exec.borrow().as_ref().unwrap()))
|
||||
+ THE_EXECUTOR.with(|exec| {
|
||||
+ Rc::clone(exec.borrow().as_ref().unwrap_or_else(|| {
|
||||
+ log::error!("nvmed: internal error: executor not initialized");
|
||||
+ std::process::exit(1);
|
||||
+ }))
|
||||
+ })
|
||||
}
|
||||
fn try_submit(
|
||||
nvme: &Arc<Nvme>,
|
||||
diff --git a/drivers/storage/nvmed/src/nvme/identify.rs b/drivers/storage/nvmed/src/nvme/identify.rs
|
||||
index 05e5b9b2..b1b6e959 100644
|
||||
--- a/drivers/storage/nvmed/src/nvme/identify.rs
|
||||
+++ b/drivers/storage/nvmed/src/nvme/identify.rs
|
||||
@@ -126,7 +126,7 @@ impl LbaFormat {
|
||||
0b01 => RelativePerformance::Better,
|
||||
0b10 => RelativePerformance::Good,
|
||||
0b11 => RelativePerformance::Degraded,
|
||||
- _ => unreachable!(),
|
||||
+ _ => RelativePerformance::Degraded,
|
||||
}
|
||||
}
|
||||
pub fn is_available(&self) -> bool {
|
||||
@@ -153,7 +153,14 @@ impl Nvme {
|
||||
/// Returns the serial number, model, and firmware, in that order.
|
||||
pub async fn identify_controller(&self) {
|
||||
// TODO: Use same buffer
|
||||
- let data: Dma<IdentifyControllerData> = unsafe { Dma::zeroed().unwrap().assume_init() };
|
||||
+ let data: Dma<IdentifyControllerData> = unsafe {
|
||||
+ Dma::zeroed()
|
||||
+ .map(|dma| dma.assume_init())
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ log::error!("nvmed: failed to allocate identify DMA: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ })
|
||||
+ };
|
||||
|
||||
// println!(" - Attempting to identify controller");
|
||||
let comp = self
|
||||
@@ -182,7 +189,14 @@ impl Nvme {
|
||||
}
|
||||
pub async fn identify_namespace_list(&self, base: u32) -> Vec<u32> {
|
||||
// TODO: Use buffer
|
||||
- let data: Dma<[u32; 1024]> = unsafe { Dma::zeroed().unwrap().assume_init() };
|
||||
+ let data: Dma<[u32; 1024]> = unsafe {
|
||||
+ Dma::zeroed()
|
||||
+ .map(|dma| dma.assume_init())
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ log::error!("nvmed: failed to allocate namespace list DMA: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ })
|
||||
+ };
|
||||
|
||||
// println!(" - Attempting to retrieve namespace ID list");
|
||||
let comp = self
|
||||
@@ -198,7 +212,14 @@ impl Nvme {
|
||||
}
|
||||
pub async fn identify_namespace(&self, nsid: u32) -> NvmeNamespace {
|
||||
//TODO: Use buffer
|
||||
- let data: Dma<IdentifyNamespaceData> = unsafe { Dma::zeroed().unwrap().assume_init() };
|
||||
+ let data: Dma<IdentifyNamespaceData> = unsafe {
|
||||
+ Dma::zeroed()
|
||||
+ .map(|dma| dma.assume_init())
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ log::error!("nvmed: failed to allocate namespace DMA: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ })
|
||||
+ };
|
||||
|
||||
log::debug!("Attempting to identify namespace {nsid}");
|
||||
let comp = self
|
||||
@@ -216,7 +237,10 @@ impl Nvme {
|
||||
let block_size = data
|
||||
.formatted_lba_size()
|
||||
.lba_data_size()
|
||||
- .expect("nvmed: error: size outside 512-2^64 range");
|
||||
+ .unwrap_or_else(|| {
|
||||
+ log::error!("nvmed: error: size outside 512-2^64 range");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
log::debug!("NVME block size: {}", block_size);
|
||||
|
||||
NvmeNamespace {
|
||||
diff --git a/drivers/storage/nvmed/src/nvme/mod.rs b/drivers/storage/nvmed/src/nvme/mod.rs
|
||||
index 682ee933..90a25d5b 100644
|
||||
--- a/drivers/storage/nvmed/src/nvme/mod.rs
|
||||
+++ b/drivers/storage/nvmed/src/nvme/mod.rs
|
||||
@@ -160,7 +160,15 @@ impl Nvme {
|
||||
}
|
||||
fn cur_thread_ctxt(&self) -> Arc<ReentrantMutex<ThreadCtxt>> {
|
||||
// TODO: multi-threading
|
||||
- Arc::clone(self.thread_ctxts.read().get(&0).unwrap())
|
||||
+ Arc::clone(
|
||||
+ self.thread_ctxts
|
||||
+ .read()
|
||||
+ .get(&0)
|
||||
+ .unwrap_or_else(|| {
|
||||
+ log::error!("nvmed: internal error: thread context 0 missing");
|
||||
+ std::process::exit(1);
|
||||
+ }),
|
||||
+ )
|
||||
}
|
||||
|
||||
pub unsafe fn submission_queue_tail(&self, qid: u16, tail: u16) {
|
||||
@@ -208,10 +216,22 @@ impl Nvme {
|
||||
}
|
||||
|
||||
for (qid, iv) in self.cq_ivs.get_mut().iter_mut() {
|
||||
- let ctxt = thread_ctxts.get(&0).unwrap().lock();
|
||||
+ let ctxt = match thread_ctxts.get(&0) {
|
||||
+ Some(c) => c.lock(),
|
||||
+ None => {
|
||||
+ log::error!("nvmed: internal error: thread context 0 missing");
|
||||
+ return Err(Error::new(EIO));
|
||||
+ }
|
||||
+ };
|
||||
let queues = ctxt.queues.borrow();
|
||||
|
||||
- let &(ref cq, ref sq) = queues.get(qid).unwrap();
|
||||
+ let (cq, sq) = match queues.get(qid) {
|
||||
+ Some(pair) => pair,
|
||||
+ None => {
|
||||
+ log::error!("nvmed: internal error: queue {qid} missing");
|
||||
+ return Err(Error::new(EIO));
|
||||
+ }
|
||||
+ };
|
||||
log::debug!(
|
||||
"iv {iv} [cq {qid}: {:X}, {}] [sq {qid}: {:X}, {}]",
|
||||
cq.data.physical(),
|
||||
@@ -222,7 +242,13 @@ impl Nvme {
|
||||
}
|
||||
|
||||
{
|
||||
- let main_ctxt = thread_ctxts.get(&0).unwrap().lock();
|
||||
+ let main_ctxt = match thread_ctxts.get(&0) {
|
||||
+ Some(c) => c.lock(),
|
||||
+ None => {
|
||||
+ log::error!("nvmed: internal error: thread context 0 missing");
|
||||
+ return Err(Error::new(EIO));
|
||||
+ }
|
||||
+ };
|
||||
|
||||
for (i, prp) in main_ctxt.buffer_prp.borrow_mut().iter_mut().enumerate() {
|
||||
*prp = (main_ctxt.buffer.borrow_mut().physical() + i * 4096) as u64;
|
||||
@@ -231,7 +257,13 @@ impl Nvme {
|
||||
let regs = self.regs.get_mut();
|
||||
|
||||
let mut queues = main_ctxt.queues.borrow_mut();
|
||||
- let (asq, acq) = queues.get_mut(&0).unwrap();
|
||||
+ let (asq, acq) = match queues.get_mut(&0) {
|
||||
+ Some(pair) => pair,
|
||||
+ None => {
|
||||
+ log::error!("nvmed: internal error: admin queue pair missing");
|
||||
+ return Err(Error::new(EIO));
|
||||
+ }
|
||||
+ };
|
||||
regs.aqa
|
||||
.write(((acq.data.len() as u32 - 1) << 16) | (asq.data.len() as u32 - 1));
|
||||
regs.asq_low.write(asq.data.physical() as u32);
|
||||
@@ -281,14 +313,14 @@ impl Nvme {
|
||||
let vector = vector as u8;
|
||||
|
||||
if masked {
|
||||
- assert_ne!(
|
||||
+ debug_assert_ne!(
|
||||
to_clear & (1 << vector),
|
||||
(1 << vector),
|
||||
"nvmed: internal error: cannot both mask and set"
|
||||
);
|
||||
to_mask |= 1 << vector;
|
||||
} else {
|
||||
- assert_ne!(
|
||||
+ debug_assert_ne!(
|
||||
to_mask & (1 << vector),
|
||||
(1 << vector),
|
||||
"nvmed: internal error: cannot both mask and set"
|
||||
@@ -326,22 +358,27 @@ impl Nvme {
|
||||
cmd_init: impl FnOnce(CmdId) -> NvmeCmd,
|
||||
fail: impl FnOnce(),
|
||||
) -> Option<(CqId, CmdId)> {
|
||||
- match ctxt.queues.borrow_mut().get_mut(&sq_id).unwrap() {
|
||||
- (sq, _cq) => {
|
||||
- if sq.is_full() {
|
||||
- fail();
|
||||
- return None;
|
||||
- }
|
||||
- let cmd_id = sq.tail;
|
||||
- let tail = sq.submit_unchecked(cmd_init(cmd_id));
|
||||
-
|
||||
- // TODO: Submit in bulk
|
||||
- unsafe {
|
||||
- self.submission_queue_tail(sq_id, tail);
|
||||
- }
|
||||
- Some((sq_id, cmd_id))
|
||||
+ let mut queues_ref = ctxt.queues.borrow_mut();
|
||||
+ let (sq, _cq) = match queues_ref.get_mut(&sq_id) {
|
||||
+ Some(pair) => pair,
|
||||
+ None => {
|
||||
+ log::error!("nvmed: internal error: submission queue {sq_id} missing");
|
||||
+ fail();
|
||||
+ return None;
|
||||
}
|
||||
+ };
|
||||
+ if sq.is_full() {
|
||||
+ fail();
|
||||
+ return None;
|
||||
+ }
|
||||
+ let cmd_id = sq.tail;
|
||||
+ let tail = sq.submit_unchecked(cmd_init(cmd_id));
|
||||
+
|
||||
+ // TODO: Submit in bulk
|
||||
+ unsafe {
|
||||
+ self.submission_queue_tail(sq_id, tail);
|
||||
}
|
||||
+ Some((sq_id, cmd_id))
|
||||
}
|
||||
|
||||
pub async fn create_io_completion_queue(
|
||||
@@ -349,13 +386,19 @@ impl Nvme {
|
||||
io_cq_id: CqId,
|
||||
vector: Option<Iv>,
|
||||
) -> NvmeCompQueue {
|
||||
- let queue = NvmeCompQueue::new().expect("nvmed: failed to allocate I/O completion queue");
|
||||
-
|
||||
- let len = u16::try_from(queue.data.len())
|
||||
- .expect("nvmed: internal error: I/O CQ longer than 2^16 entries");
|
||||
- let raw_len = len
|
||||
- .checked_sub(1)
|
||||
- .expect("nvmed: internal error: CQID 0 for I/O CQ");
|
||||
+ let queue = NvmeCompQueue::new().unwrap_or_else(|err| {
|
||||
+ log::error!("nvmed: failed to allocate I/O completion queue: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
+
|
||||
+ let len = u16::try_from(queue.data.len()).unwrap_or_else(|_| {
|
||||
+ log::error!("nvmed: internal error: I/O CQ longer than 2^16 entries");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
+ let raw_len = len.checked_sub(1).unwrap_or_else(|| {
|
||||
+ log::error!("nvmed: internal error: CQID 0 for I/O CQ");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
|
||||
let comp = self
|
||||
.submit_and_complete_admin_command(|cid| {
|
||||
@@ -370,22 +413,28 @@ impl Nvme {
|
||||
.await;
|
||||
|
||||
/*match comp.status.specific {
|
||||
- 1 => panic!("invalid queue identifier"),
|
||||
- 2 => panic!("invalid queue size"),
|
||||
- 8 => panic!("invalid interrupt vector"),
|
||||
+ 1 => { log::error!("nvmed: invalid queue identifier"); std::process::exit(1); }
|
||||
+ 2 => { log::error!("nvmed: invalid queue size"); std::process::exit(1); }
|
||||
+ 8 => { log::error!("nvmed: invalid interrupt vector"); std::process::exit(1); }
|
||||
_ => (),
|
||||
}*/
|
||||
|
||||
queue
|
||||
}
|
||||
pub async fn create_io_submission_queue(&self, io_sq_id: SqId, io_cq_id: CqId) -> NvmeCmdQueue {
|
||||
- let q = NvmeCmdQueue::new().expect("failed to create submission queue");
|
||||
-
|
||||
- let len = u16::try_from(q.data.len())
|
||||
- .expect("nvmed: internal error: I/O SQ longer than 2^16 entries");
|
||||
- let raw_len = len
|
||||
- .checked_sub(1)
|
||||
- .expect("nvmed: internal error: SQID 0 for I/O SQ");
|
||||
+ let q = NvmeCmdQueue::new().unwrap_or_else(|err| {
|
||||
+ log::error!("nvmed: failed to create submission queue: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
+
|
||||
+ let len = u16::try_from(q.data.len()).unwrap_or_else(|_| {
|
||||
+ log::error!("nvmed: internal error: I/O SQ longer than 2^16 entries");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
+ let raw_len = len.checked_sub(1).unwrap_or_else(|| {
|
||||
+ log::error!("nvmed: internal error: SQID 0 for I/O SQ");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
|
||||
let comp = self
|
||||
.submit_and_complete_admin_command(|cid| {
|
||||
@@ -399,9 +448,9 @@ impl Nvme {
|
||||
})
|
||||
.await;
|
||||
/*match comp.status.specific {
|
||||
- 0 => panic!("completion queue invalid"),
|
||||
- 1 => panic!("invalid queue identifier"),
|
||||
- 2 => panic!("invalid queue size"),
|
||||
+ 0 => { log::error!("nvmed: completion queue invalid"); std::process::exit(1); }
|
||||
+ 1 => { log::error!("nvmed: invalid queue identifier"); std::process::exit(1); }
|
||||
+ 2 => { log::error!("nvmed: invalid queue size"); std::process::exit(1); }
|
||||
_ => (),
|
||||
}*/
|
||||
|
||||
@@ -431,7 +480,10 @@ impl Nvme {
|
||||
self.thread_ctxts
|
||||
.read()
|
||||
.get(&0)
|
||||
- .unwrap()
|
||||
+ .unwrap_or_else(|| {
|
||||
+ log::error!("nvmed: internal error: thread context 0 missing");
|
||||
+ std::process::exit(1);
|
||||
+ })
|
||||
.lock()
|
||||
.queues
|
||||
.borrow_mut()
|
||||
@@ -497,8 +549,8 @@ impl Nvme {
|
||||
for chunk in buf.chunks_mut(/* TODO: buf len */ 8192) {
|
||||
let blocks = (chunk.len() + block_size - 1) / block_size;
|
||||
|
||||
- assert!(blocks > 0);
|
||||
- assert!(blocks <= 0x1_0000);
|
||||
+ debug_assert!(blocks > 0);
|
||||
+ debug_assert!(blocks <= 0x1_0000);
|
||||
|
||||
self.namespace_rw(&*ctxt, namespace, lba, (blocks - 1) as u16, false)
|
||||
.await?;
|
||||
@@ -525,8 +577,8 @@ impl Nvme {
|
||||
for chunk in buf.chunks(/* TODO: buf len */ 8192) {
|
||||
let blocks = (chunk.len() + block_size - 1) / block_size;
|
||||
|
||||
- assert!(blocks > 0);
|
||||
- assert!(blocks <= 0x1_0000);
|
||||
+ debug_assert!(blocks > 0);
|
||||
+ debug_assert!(blocks <= 0x1_0000);
|
||||
|
||||
ctxt.buffer.borrow_mut()[..chunk.len()].copy_from_slice(chunk);
|
||||
|
||||
diff --git a/drivers/storage/nvmed/src/nvme/queues.rs b/drivers/storage/nvmed/src/nvme/queues.rs
|
||||
index a3712aeb..438c905c 100644
|
||||
--- a/drivers/storage/nvmed/src/nvme/queues.rs
|
||||
+++ b/drivers/storage/nvmed/src/nvme/queues.rs
|
||||
@@ -145,7 +145,7 @@ impl Status {
|
||||
3 => Self::PathRelatedStatus(code),
|
||||
4..=6 => Self::Rsvd(code),
|
||||
7 => Self::Vendor(code),
|
||||
- _ => unreachable!(),
|
||||
+ _ => Self::Vendor(code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/drivers/storage/virtio-blkd/src/scheme.rs b/drivers/storage/virtio-blkd/src/scheme.rs
|
||||
index ec4ecf73..39fb24a8 100644
|
||||
--- a/drivers/storage/virtio-blkd/src/scheme.rs
|
||||
+++ b/drivers/storage/virtio-blkd/src/scheme.rs
|
||||
@@ -15,19 +15,34 @@ trait BlkExtension {
|
||||
|
||||
impl BlkExtension for Queue<'_> {
|
||||
async fn read(&self, block: u64, target: &mut [u8]) -> usize {
|
||||
- let req = Dma::new(BlockVirtRequest {
|
||||
+ let req = match Dma::new(BlockVirtRequest {
|
||||
ty: BlockRequestTy::In,
|
||||
reserved: 0,
|
||||
sector: block,
|
||||
- })
|
||||
- .unwrap();
|
||||
+ }) {
|
||||
+ Ok(req) => req,
|
||||
+ Err(err) => {
|
||||
+ log::error!("virtio-blkd: failed to allocate read request DMA: {err}");
|
||||
+ return 0;
|
||||
+ }
|
||||
+ };
|
||||
|
||||
let result = unsafe {
|
||||
- Dma::<[u8]>::zeroed_slice(target.len())
|
||||
- .unwrap()
|
||||
- .assume_init()
|
||||
+ match Dma::<[u8]>::zeroed_slice(target.len()) {
|
||||
+ Ok(dma) => dma.assume_init(),
|
||||
+ Err(err) => {
|
||||
+ log::error!("virtio-blkd: failed to allocate read buffer DMA: {err}");
|
||||
+ return 0;
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+ let status = match Dma::new(u8::MAX) {
|
||||
+ Ok(s) => s,
|
||||
+ Err(err) => {
|
||||
+ log::error!("virtio-blkd: failed to allocate read status DMA: {err}");
|
||||
+ return 0;
|
||||
+ }
|
||||
};
|
||||
- let status = Dma::new(u8::MAX).unwrap();
|
||||
|
||||
let chain = ChainBuilder::new()
|
||||
.chain(Buffer::new(&req))
|
||||
@@ -37,28 +52,46 @@ impl BlkExtension for Queue<'_> {
|
||||
|
||||
// XXX: Subtract 1 because the of status byte.
|
||||
let written = self.send(chain).await as usize - 1;
|
||||
- assert_eq!(*status, 0);
|
||||
+ if *status != 0 {
|
||||
+ log::error!("virtio-blkd: read failed with status {}", *status);
|
||||
+ return 0;
|
||||
+ }
|
||||
|
||||
target[..written].copy_from_slice(&result);
|
||||
written
|
||||
}
|
||||
|
||||
async fn write(&self, block: u64, target: &[u8]) -> usize {
|
||||
- let req = Dma::new(BlockVirtRequest {
|
||||
+ let req = match Dma::new(BlockVirtRequest {
|
||||
ty: BlockRequestTy::Out,
|
||||
reserved: 0,
|
||||
sector: block,
|
||||
- })
|
||||
- .unwrap();
|
||||
+ }) {
|
||||
+ Ok(req) => req,
|
||||
+ Err(err) => {
|
||||
+ log::error!("virtio-blkd: failed to allocate write request DMA: {err}");
|
||||
+ return 0;
|
||||
+ }
|
||||
+ };
|
||||
|
||||
let mut result = unsafe {
|
||||
- Dma::<[u8]>::zeroed_slice(target.len())
|
||||
- .unwrap()
|
||||
- .assume_init()
|
||||
+ match Dma::<[u8]>::zeroed_slice(target.len()) {
|
||||
+ Ok(dma) => dma.assume_init(),
|
||||
+ Err(err) => {
|
||||
+ log::error!("virtio-blkd: failed to allocate write buffer DMA: {err}");
|
||||
+ return 0;
|
||||
+ }
|
||||
+ }
|
||||
};
|
||||
result.copy_from_slice(target.as_ref());
|
||||
|
||||
- let status = Dma::new(u8::MAX).unwrap();
|
||||
+ let status = match Dma::new(u8::MAX) {
|
||||
+ Ok(s) => s,
|
||||
+ Err(err) => {
|
||||
+ log::error!("virtio-blkd: failed to allocate write status DMA: {err}");
|
||||
+ return 0;
|
||||
+ }
|
||||
+ };
|
||||
|
||||
let chain = ChainBuilder::new()
|
||||
.chain(Buffer::new(&req))
|
||||
@@ -67,7 +100,10 @@ impl BlkExtension for Queue<'_> {
|
||||
.build();
|
||||
|
||||
self.send(chain).await as usize;
|
||||
- assert_eq!(*status, 0);
|
||||
+ if *status != 0 {
|
||||
+ log::error!("virtio-blkd: write failed with status {}", *status);
|
||||
+ return 0;
|
||||
+ }
|
||||
|
||||
target.len()
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
# P2-usb-pm-and-drivers.patch
|
||||
#
|
||||
# USB power management and driver interface improvements:
|
||||
# suspend/resume commands, SCSI driver enablement, PortPmState type,
|
||||
# IRQ reactor staged port state fallback.
|
||||
#
|
||||
# Covers:
|
||||
# - usbctl/main.rs: pm-state, suspend, resume subcommands
|
||||
# - xhcid/drivers.toml: enable SCSI over USB driver (was commented out)
|
||||
# - xhcid/driver_interface.rs: PortPmState enum, suspend/resume/port_pm_state methods
|
||||
# - xhcid/irq_reactor.rs: staged_port_states fallback in with_ring/with_ring_mut
|
||||
#
|
||||
diff --git a/drivers/usb/usbctl/src/main.rs b/drivers/usb/usbctl/src/main.rs
|
||||
index 9b5773d9..232f7cfc 100644
|
||||
--- a/drivers/usb/usbctl/src/main.rs
|
||||
+++ b/drivers/usb/usbctl/src/main.rs
|
||||
@@ -15,6 +15,9 @@ fn main() {
|
||||
Command::new("port")
|
||||
.arg(Arg::new("PORT").num_args(1).required(true))
|
||||
.subcommand(Command::new("status"))
|
||||
+ .subcommand(Command::new("pm-state"))
|
||||
+ .subcommand(Command::new("suspend"))
|
||||
+ .subcommand(Command::new("resume"))
|
||||
.subcommand(
|
||||
Command::new("endpoint")
|
||||
.arg(Arg::new("ENDPOINT_NUM").num_args(1).required(true))
|
||||
@@ -38,6 +41,15 @@ fn main() {
|
||||
if let Some(_status_scmd_matches) = port_scmd_matches.subcommand_matches("status") {
|
||||
let state = handle.port_state().expect("Failed to get port state");
|
||||
println!("{}", state.as_str());
|
||||
+ } else if let Some(_pm_state_scmd_matches) = port_scmd_matches.subcommand_matches("pm-state") {
|
||||
+ let state = handle
|
||||
+ .port_pm_state()
|
||||
+ .expect("Failed to get port power-management state");
|
||||
+ println!("{}", state.as_str());
|
||||
+ } else if let Some(_suspend_scmd_matches) = port_scmd_matches.subcommand_matches("suspend") {
|
||||
+ handle.suspend_device().expect("Failed to suspend device");
|
||||
+ } else if let Some(_resume_scmd_matches) = port_scmd_matches.subcommand_matches("resume") {
|
||||
+ handle.resume_device().expect("Failed to resume device");
|
||||
} else if let Some(endp_scmd_matches) = port_scmd_matches.subcommand_matches("endpoint") {
|
||||
let endp_num = endp_scmd_matches
|
||||
|
||||
.get_one::<String>("ENDPOINT_NUM")
|
||||
diff --git a/drivers/usb/xhcid/drivers.toml b/drivers/usb/xhcid/drivers.toml
|
||||
index 83c90e23..470ec063 100644
|
||||
--- a/drivers/usb/xhcid/drivers.toml
|
||||
+++ b/drivers/usb/xhcid/drivers.toml
|
||||
@@ -1,9 +1,8 @@
|
||||
-#TODO: causes XHCI errors
|
||||
-#[[drivers]]
|
||||
-#name = "SCSI over USB"
|
||||
-#class = 8 # Mass Storage class
|
||||
-#subclass = 6 # SCSI transparent command set
|
||||
-#command = ["usbscsid", "$SCHEME", "$PORT", "$IF_PROTO"]
|
||||
+[[drivers]]
|
||||
+name = "SCSI over USB"
|
||||
+class = 8 # Mass Storage class
|
||||
+subclass = 6 # SCSI transparent command set
|
||||
+command = ["usbscsid", "$SCHEME", "$PORT", "$IF_PROTO"]
|
||||
|
||||
[[drivers]]
|
||||
name = "USB HUB"
|
||||
|
||||
diff --git a/drivers/usb/xhcid/src/driver_interface.rs b/drivers/usb/xhcid/src/driver_interface.rs
|
||||
index 727f8d7e..82f839ae 100644
|
||||
--- a/drivers/usb/xhcid/src/driver_interface.rs
|
||||
+++ b/drivers/usb/xhcid/src/driver_interface.rs
|
||||
@@ -444,6 +444,33 @@ impl str::FromStr for PortState {
|
||||
}
|
||||
}
|
||||
|
||||
+#[repr(u8)]
|
||||
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
+pub enum PortPmState {
|
||||
+ Active,
|
||||
+ Suspended,
|
||||
+}
|
||||
+impl PortPmState {
|
||||
+ pub fn as_str(&self) -> &'static str {
|
||||
+ match self {
|
||||
+ Self::Active => "active",
|
||||
+ Self::Suspended => "suspended",
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl str::FromStr for PortPmState {
|
||||
+ type Err = Invalid;
|
||||
+
|
||||
+ fn from_str(s: &str) -> result::Result<Self, Self::Err> {
|
||||
+ Ok(match s {
|
||||
+ "active" => Self::Active,
|
||||
+ "suspended" => Self::Suspended,
|
||||
+ _ => return Err(Invalid("read reserved port PM state")),
|
||||
+ })
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub enum EndpointStatus {
|
||||
@@ -560,6 +587,16 @@ impl XhciClientHandle {
|
||||
let _bytes_written = file.write(&[])?;
|
||||
Ok(())
|
||||
}
|
||||
+ pub fn suspend_device(&self) -> result::Result<(), XhciClientHandleError> {
|
||||
+ let file = self.fd.openat("suspend", libredox::flag::O_WRONLY, 0)?;
|
||||
+ let _bytes_written = file.write(&[])?;
|
||||
+ Ok(())
|
||||
+ }
|
||||
+ pub fn resume_device(&self) -> result::Result<(), XhciClientHandleError> {
|
||||
+ let file = self.fd.openat("resume", libredox::flag::O_WRONLY, 0)?;
|
||||
+ let _bytes_written = file.write(&[])?;
|
||||
+ Ok(())
|
||||
+ }
|
||||
pub fn get_standard_descs(&self) -> result::Result<DevDesc, XhciClientHandleError> {
|
||||
let json = self.read("descriptors")?;
|
||||
Ok(serde_json::from_slice(&json)?)
|
||||
@@ -582,6 +619,10 @@ impl XhciClientHandle {
|
||||
let string = self.read_to_string("state")?;
|
||||
Ok(string.parse()?)
|
||||
}
|
||||
+ pub fn port_pm_state(&self) -> result::Result<PortPmState, XhciClientHandleError> {
|
||||
+ let string = self.read_to_string("pm_state")?;
|
||||
+ Ok(string.parse()?)
|
||||
+ }
|
||||
pub fn open_endpoint_ctl(&self, num: u8) -> result::Result<File, XhciClientHandleError> {
|
||||
let path = format!("endpoints/{}/ctl", num);
|
||||
let fd = self.fd.openat(&path, libredox::flag::O_RDWR, 0)?;
|
||||
|
||||
diff --git a/drivers/usb/xhcid/src/xhci/irq_reactor.rs b/drivers/usb/xhcid/src/xhci/irq_reactor.rs
|
||||
index ac492d5b..310fe51f 100644
|
||||
--- a/drivers/usb/xhcid/src/xhci/irq_reactor.rs
|
||||
+++ b/drivers/usb/xhcid/src/xhci/irq_reactor.rs
|
||||
@@ -633,7 +633,10 @@ impl<const N: usize> Xhci<N> {
|
||||
pub fn with_ring<T, F: FnOnce(&Ring) -> T>(&self, id: RingId, function: F) -> Option<T> {
|
||||
use super::RingOrStreams;
|
||||
|
||||
- let slot_state = self.port_states.get(&id.port)?;
|
||||
+ let slot_state = self
|
||||
+ .port_states
|
||||
+ .get(&id.port)
|
||||
+ .or_else(|| self.staged_port_states.get(&id.port))?;
|
||||
let endpoint_state = slot_state.endpoint_states.get(&id.endpoint_num)?;
|
||||
|
||||
let ring_ref = match endpoint_state.transfer {
|
||||
@@ -650,7 +653,10 @@ impl<const N: usize> Xhci<N> {
|
||||
) -> Option<T> {
|
||||
use super::RingOrStreams;
|
||||
|
||||
- let mut slot_state = self.port_states.get_mut(&id.port)?;
|
||||
+ let mut slot_state = self
|
||||
+ .port_states
|
||||
+ .get_mut(&id.port)
|
||||
+ .or_else(|| self.staged_port_states.get_mut(&id.port))?;
|
||||
let mut endpoint_state = slot_state.endpoint_states.get_mut(&id.endpoint_num)?;
|
||||
|
||||
let ring_ref = match endpoint_state.transfer {
|
||||
@@ -1,398 +0,0 @@
|
||||
--- a/drivers/pcid/src/cfg_access/mod.rs
|
||||
+++ b/drivers/pcid/src/cfg_access/mod.rs
|
||||
@@ -349,6 +349,10 @@
|
||||
let bus_addr = self.bus_addr(address.segment(), address.bus())?;
|
||||
Some(unsafe { bus_addr.add(Self::bus_addr_offset_in_dwords(address, offset)) })
|
||||
}
|
||||
+
|
||||
+ pub fn has_extended_config(&self, address: PciAddress) -> bool {
|
||||
+ self.mmio_addr(address, 0x100).is_some()
|
||||
+ }
|
||||
}
|
||||
|
||||
impl ConfigRegionAccess for Pcie {
|
||||
--- a/drivers/pcid/src/scheme.rs
|
||||
+++ b/drivers/pcid/src/scheme.rs
|
||||
@@ -5,12 +5,61 @@
|
||||
use redox_scheme::{CallerCtx, OpenResult};
|
||||
use scheme_utils::HandleMap;
|
||||
use syscall::dirent::{DirEntry, DirentBuf, DirentKind};
|
||||
-use syscall::error::{Error, Result, EACCES, EBADF, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR, EALREADY};
|
||||
+use syscall::error::{
|
||||
+ Error, Result, EACCES, EALREADY, EBADF, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR, EROFS,
|
||||
+};
|
||||
use syscall::flag::{MODE_CHR, MODE_DIR, O_DIRECTORY, O_STAT};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::ENOLCK;
|
||||
|
||||
use crate::cfg_access::Pcie;
|
||||
+
|
||||
+const PCIE_EXTENDED_CAPABILITY_AER: u16 = 0x0001;
|
||||
+
|
||||
+#[derive(Clone, Copy)]
|
||||
+enum AerRegisterName {
|
||||
+ UncorStatus,
|
||||
+ UncorMask,
|
||||
+ UncorSeverity,
|
||||
+ CorStatus,
|
||||
+ CorMask,
|
||||
+ Cap,
|
||||
+ HeaderLog,
|
||||
+}
|
||||
+
|
||||
+impl AerRegisterName {
|
||||
+ fn from_path(path: &str) -> Option<Self> {
|
||||
+ Some(match path {
|
||||
+ "uncor_status" => Self::UncorStatus,
|
||||
+ "uncor_mask" => Self::UncorMask,
|
||||
+ "uncor_severity" => Self::UncorSeverity,
|
||||
+ "cor_status" => Self::CorStatus,
|
||||
+ "cor_mask" => Self::CorMask,
|
||||
+ "cap" => Self::Cap,
|
||||
+ "header_log" => Self::HeaderLog,
|
||||
+ _ => return None,
|
||||
+ })
|
||||
+ }
|
||||
+
|
||||
+ const fn offset(self) -> u16 {
|
||||
+ match self {
|
||||
+ Self::UncorStatus => 0x00,
|
||||
+ Self::UncorMask => 0x04,
|
||||
+ Self::UncorSeverity => 0x08,
|
||||
+ Self::CorStatus => 0x0C,
|
||||
+ Self::CorMask => 0x10,
|
||||
+ Self::Cap => 0x14,
|
||||
+ Self::HeaderLog => 0x18,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ const fn len(self) -> usize {
|
||||
+ match self {
|
||||
+ Self::HeaderLog => 16,
|
||||
+ _ => 4,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
|
||||
pub struct PciScheme {
|
||||
handles: HandleMap<HandleWrapper>,
|
||||
@@ -20,13 +69,27 @@
|
||||
binds: HashMap<String, u32>,
|
||||
}
|
||||
enum Handle {
|
||||
- TopLevel { entries: Vec<String> },
|
||||
+ TopLevel {
|
||||
+ entries: Vec<String>,
|
||||
+ },
|
||||
Access,
|
||||
- Device,
|
||||
- Channel { addr: PciAddress, st: ChannelState },
|
||||
+ Device {
|
||||
+ addr: PciAddress,
|
||||
+ },
|
||||
+ Channel {
|
||||
+ addr: PciAddress,
|
||||
+ st: ChannelState,
|
||||
+ },
|
||||
SchemeRoot,
|
||||
/// Represents an open handle to a device's bind endpoint
|
||||
- Bind { addr: PciAddress },
|
||||
+ Bind {
|
||||
+ addr: PciAddress,
|
||||
+ },
|
||||
+ AerDir,
|
||||
+ Aer {
|
||||
+ addr: PciAddress,
|
||||
+ register: AerRegisterName,
|
||||
+ },
|
||||
/// Uevent surface for hotplug consumers. Opening uevent returns an object
|
||||
/// from which device add/remove events can be read. Since pcid currently
|
||||
/// only scans at startup, this surface is ready for hotplug polling consumers.
|
||||
@@ -38,13 +101,23 @@
|
||||
}
|
||||
impl Handle {
|
||||
fn is_file(&self) -> bool {
|
||||
- matches!(self, Self::Access | Self::Channel { .. } | Self::Bind { .. } | Self::Uevent)
|
||||
+ matches!(
|
||||
+ self,
|
||||
+ Self::Access
|
||||
+ | Self::Channel { .. }
|
||||
+ | Self::Bind { .. }
|
||||
+ | Self::Aer { .. }
|
||||
+ | Self::Uevent
|
||||
+ )
|
||||
}
|
||||
fn is_dir(&self) -> bool {
|
||||
!self.is_file()
|
||||
}
|
||||
fn requires_root(&self) -> bool {
|
||||
- matches!(self, Self::Access | Self::Channel { .. } | Self::Bind { .. })
|
||||
+ matches!(
|
||||
+ self,
|
||||
+ Self::Access | Self::Channel { .. } | Self::Bind { .. }
|
||||
+ )
|
||||
}
|
||||
fn is_scheme_root(&self) -> bool {
|
||||
matches!(self, Self::SchemeRoot)
|
||||
@@ -57,6 +130,16 @@
|
||||
}
|
||||
|
||||
const DEVICE_CONTENTS: &[&str] = &["channel", "bind"];
|
||||
+const DEVICE_AER_CONTENTS: &[&str] = &["channel", "bind", "aer"];
|
||||
+const AER_CONTENTS: &[&str] = &[
|
||||
+ "uncor_status",
|
||||
+ "uncor_mask",
|
||||
+ "uncor_severity",
|
||||
+ "cor_status",
|
||||
+ "cor_mask",
|
||||
+ "cap",
|
||||
+ "header_log",
|
||||
+];
|
||||
|
||||
impl PciScheme {
|
||||
pub fn access(&mut self) -> usize {
|
||||
@@ -141,7 +224,12 @@
|
||||
|
||||
let (len, mode) = match handle.inner {
|
||||
Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755),
|
||||
- Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755),
|
||||
+ Handle::Device { addr } => (
|
||||
+ Self::device_entries(&self.pcie, addr).len(),
|
||||
+ MODE_DIR | 0o755,
|
||||
+ ),
|
||||
+ Handle::AerDir => (AER_CONTENTS.len(), MODE_DIR | 0o755),
|
||||
+ Handle::Aer { register, .. } => (register.len(), MODE_CHR | 0o444),
|
||||
Handle::Access | Handle::Channel { .. } | Handle::Bind { .. } => (0, MODE_CHR | 0o600),
|
||||
Handle::Uevent => (0, MODE_CHR | 0o644),
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
@@ -154,7 +242,7 @@
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
- _offset: u64,
|
||||
+ offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
@@ -166,11 +254,14 @@
|
||||
|
||||
match handle.inner {
|
||||
Handle::TopLevel { .. } => Err(Error::new(EISDIR)),
|
||||
- Handle::Device => Err(Error::new(EISDIR)),
|
||||
+ Handle::Device { .. } | Handle::AerDir => Err(Error::new(EISDIR)),
|
||||
Handle::Channel {
|
||||
addr: _,
|
||||
ref mut st,
|
||||
} => Self::read_channel(st, buf),
|
||||
+ Handle::Aer { addr, register } => {
|
||||
+ Self::read_aer_register(&self.pcie, addr, register, buf, offset)
|
||||
+ }
|
||||
Handle::Uevent => {
|
||||
// Uevent surface is ready for hotplug polling consumers.
|
||||
// pcid currently only scans at startup, so return empty (EAGAIN would indicate no data available).
|
||||
@@ -209,8 +300,15 @@
|
||||
}
|
||||
return Ok(buf);
|
||||
}
|
||||
- Handle::Device => DEVICE_CONTENTS,
|
||||
- Handle::Access | Handle::Channel { .. } | Handle::Bind { .. } | Handle::Uevent => return Err(Error::new(ENOTDIR)),
|
||||
+ Handle::Device { addr } => Self::device_entries(&self.pcie, addr),
|
||||
+ Handle::AerDir => AER_CONTENTS,
|
||||
+ Handle::Access
|
||||
+ | Handle::Channel { .. }
|
||||
+ | Handle::Bind { .. }
|
||||
+ | Handle::Aer { .. }
|
||||
+ | Handle::Uevent => {
|
||||
+ return Err(Error::new(ENOTDIR));
|
||||
+ }
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
};
|
||||
|
||||
@@ -243,6 +341,7 @@
|
||||
Handle::Channel { addr, ref mut st } => {
|
||||
Self::write_channel(&self.pcie, &mut self.tree, addr, st, buf)
|
||||
}
|
||||
+ Handle::Aer { .. } => Err(Error::new(EROFS)),
|
||||
|
||||
_ => Err(Error::new(EBADF)),
|
||||
}
|
||||
@@ -357,45 +456,151 @@
|
||||
binds: HashMap::new(),
|
||||
}
|
||||
}
|
||||
- fn parse_after_pci_addr(&mut self, addr: PciAddress, after: &str, ctx: &CallerCtx) -> Result<Handle> {
|
||||
+ fn device_entries(pcie: &Pcie, addr: PciAddress) -> &'static [&'static str] {
|
||||
+ if Self::find_pcie_extended_capability(pcie, addr, PCIE_EXTENDED_CAPABILITY_AER).is_some() {
|
||||
+ DEVICE_AER_CONTENTS
|
||||
+ } else {
|
||||
+ DEVICE_CONTENTS
|
||||
+ }
|
||||
+ }
|
||||
+ fn find_pcie_extended_capability(
|
||||
+ pcie: &Pcie,
|
||||
+ addr: PciAddress,
|
||||
+ capability_id: u16,
|
||||
+ ) -> Option<u16> {
|
||||
+ if !pcie.has_extended_config(addr) {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ let mut offset = 0x100_u16;
|
||||
+
|
||||
+ while offset <= 0xFFC {
|
||||
+ let header = unsafe { pcie.read(addr, offset) };
|
||||
+ if header == 0 || header == u32::MAX {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ if (header & 0xFFFF) as u16 == capability_id {
|
||||
+ return Some(offset);
|
||||
+ }
|
||||
+
|
||||
+ let next = ((header >> 20) & 0xFFF) as u16;
|
||||
+ if next < 0x100 || next <= offset || next > 0xFFC || next % 4 != 0 {
|
||||
+ return None;
|
||||
+ }
|
||||
+ offset = next;
|
||||
+ }
|
||||
+
|
||||
+ None
|
||||
+ }
|
||||
+ fn read_file_bytes(data: &[u8], buf: &mut [u8], offset: u64) -> Result<usize> {
|
||||
+ let Ok(offset) = usize::try_from(offset) else {
|
||||
+ return Ok(0);
|
||||
+ };
|
||||
+ if offset >= data.len() {
|
||||
+ return Ok(0);
|
||||
+ }
|
||||
+
|
||||
+ let count = std::cmp::min(buf.len(), data.len() - offset);
|
||||
+ buf[..count].copy_from_slice(&data[offset..offset + count]);
|
||||
+ Ok(count)
|
||||
+ }
|
||||
+ fn read_aer_register(
|
||||
+ pcie: &Pcie,
|
||||
+ addr: PciAddress,
|
||||
+ register: AerRegisterName,
|
||||
+ buf: &mut [u8],
|
||||
+ offset: u64,
|
||||
+ ) -> Result<usize> {
|
||||
+ let Some(aer_base) =
|
||||
+ Self::find_pcie_extended_capability(pcie, addr, PCIE_EXTENDED_CAPABILITY_AER)
|
||||
+ else {
|
||||
+ return Err(Error::new(ENOENT));
|
||||
+ };
|
||||
+
|
||||
+ let mut data = [0_u8; 16];
|
||||
+ for (index, chunk) in data[..register.len()].chunks_exact_mut(4).enumerate() {
|
||||
+ let index = u16::try_from(index).map_err(|_| Error::new(EIO))?;
|
||||
+ let value = unsafe { pcie.read(addr, aer_base + register.offset() + index * 4) };
|
||||
+ chunk.copy_from_slice(&value.to_le_bytes());
|
||||
+ }
|
||||
+
|
||||
+ Self::read_file_bytes(&data[..register.len()], buf, offset)
|
||||
+ }
|
||||
+ fn parse_after_pci_addr(
|
||||
+ &mut self,
|
||||
+ addr: PciAddress,
|
||||
+ after: &str,
|
||||
+ ctx: &CallerCtx,
|
||||
+ ) -> Result<Handle> {
|
||||
if after.chars().next().map_or(false, |c| c != '/') {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
let func = self.tree.get_mut(&addr).ok_or(Error::new(ENOENT))?;
|
||||
|
||||
Ok(if after.is_empty() {
|
||||
- Handle::Device
|
||||
+ Handle::Device { addr }
|
||||
} else {
|
||||
let path = &after[1..];
|
||||
|
||||
- match path {
|
||||
- "channel" => {
|
||||
- if func.enabled {
|
||||
- return Err(Error::new(ENOLCK));
|
||||
+ if path == "aer" {
|
||||
+ if Self::find_pcie_extended_capability(
|
||||
+ &self.pcie,
|
||||
+ addr,
|
||||
+ PCIE_EXTENDED_CAPABILITY_AER,
|
||||
+ )
|
||||
+ .is_none()
|
||||
+ {
|
||||
+ return Err(Error::new(ENOENT));
|
||||
+ }
|
||||
+ Handle::AerDir
|
||||
+ } else if let Some(register_name) = path.strip_prefix("aer/") {
|
||||
+ let register =
|
||||
+ AerRegisterName::from_path(register_name).ok_or(Error::new(ENOENT))?;
|
||||
+ if Self::find_pcie_extended_capability(
|
||||
+ &self.pcie,
|
||||
+ addr,
|
||||
+ PCIE_EXTENDED_CAPABILITY_AER,
|
||||
+ )
|
||||
+ .is_none()
|
||||
+ {
|
||||
+ return Err(Error::new(ENOENT));
|
||||
+ }
|
||||
+ Handle::Aer { addr, register }
|
||||
+ } else {
|
||||
+ match path {
|
||||
+ "channel" => {
|
||||
+ if func.enabled {
|
||||
+ return Err(Error::new(ENOLCK));
|
||||
+ }
|
||||
+ func.inner.legacy_interrupt_line = crate::enable_function(
|
||||
+ &self.pcie,
|
||||
+ &mut func.endpoint_header,
|
||||
+ &mut func.capabilities,
|
||||
+ );
|
||||
+ func.enabled = true;
|
||||
+ Handle::Channel {
|
||||
+ addr,
|
||||
+ st: ChannelState::AwaitingData,
|
||||
+ }
|
||||
}
|
||||
- func.inner.legacy_interrupt_line = crate::enable_function(
|
||||
- &self.pcie,
|
||||
- &mut func.endpoint_header,
|
||||
- &mut func.capabilities,
|
||||
- );
|
||||
- func.enabled = true;
|
||||
- Handle::Channel {
|
||||
- addr,
|
||||
- st: ChannelState::AwaitingData,
|
||||
+ "bind" => {
|
||||
+ let addr_str = format!("{}", addr);
|
||||
+ if let Some(&owner_pid) = self.binds.get(&addr_str) {
|
||||
+ log::info!(
|
||||
+ "pcid: device {} already bound by pid {}",
|
||||
+ addr_str,
|
||||
+ owner_pid
|
||||
+ );
|
||||
+ return Err(Error::new(EALREADY));
|
||||
+ }
|
||||
+ let caller_pid = u32::try_from(ctx.pid).map_err(|_| Error::new(EINVAL))?;
|
||||
+ self.binds.insert(addr_str.clone(), caller_pid);
|
||||
+ log::info!("pcid: device {} bound by pid {}", addr_str, caller_pid);
|
||||
+ Handle::Bind { addr }
|
||||
}
|
||||
- }
|
||||
- "bind" => {
|
||||
- let addr_str = format!("{}", addr);
|
||||
- if let Some(&owner_pid) = self.binds.get(&addr_str) {
|
||||
- log::info!("pcid: device {} already bound by pid {}", addr_str, owner_pid);
|
||||
- return Err(Error::new(EALREADY));
|
||||
- }
|
||||
- let caller_pid = ctx.pid;
|
||||
- self.binds.insert(addr_str.clone(), caller_pid);
|
||||
- log::info!("pcid: device {} bound by pid {}", addr_str, caller_pid);
|
||||
- Handle::Bind { addr }
|
||||
- }
|
||||
- _ => return Err(Error::new(ENOENT)),
|
||||
+ _ => return Err(Error::new(ENOENT)),
|
||||
+ }
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
diff --git a/src/main.rs b/src/main.rs
|
||||
index b2e2736..a6a9474 100644
|
||||
--- a/src/main.rs
|
||||
+++ b/src/main.rs
|
||||
@@ -500,33 +500,62 @@ pub extern "C" fn main() -> ! {
|
||||
|
||||
print!("live: 0/{} MiB", size / MIBI as u64);
|
||||
|
||||
- let ptr = os.alloc_zeroed_page_aligned(size as usize);
|
||||
- if ptr.is_null() {
|
||||
- panic!("Failed to allocate memory for live");
|
||||
- }
|
||||
-
|
||||
- let live = unsafe { slice::from_raw_parts_mut(ptr, size as usize) };
|
||||
-
|
||||
- let mut i = 0;
|
||||
- for chunk in live.chunks_mut(MIBI) {
|
||||
- print!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
|
||||
- i += unsafe {
|
||||
- fs.disk
|
||||
- .read_at(fs.block + i / redoxfs::BLOCK_SIZE, chunk)
|
||||
- .expect("Failed to read live disk") as u64
|
||||
- };
|
||||
- }
|
||||
- println!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
|
||||
-
|
||||
- println!("Switching to live disk");
|
||||
- unsafe {
|
||||
- LIVE_OPT = Some((fs.block, slice::from_raw_parts_mut(ptr, size as usize)));
|
||||
- }
|
||||
+ let live_size = match usize::try_from(size) {
|
||||
+ Ok(live_size) => live_size,
|
||||
+ Err(_) => {
|
||||
+ println!("\rlive: disabled (image too large for bootloader address space)");
|
||||
+ live = false;
|
||||
+ 0
|
||||
+ }
|
||||
+ };
|
||||
|
||||
- area_add(OsMemoryEntry {
|
||||
- base: live.as_ptr() as u64,
|
||||
- size: live.len() as u64,
|
||||
- kind: OsMemoryKind::Reserved,
|
||||
- });
|
||||
+ let ptr = if live {
|
||||
+ os.alloc_zeroed_page_aligned(live_size)
|
||||
+ } else {
|
||||
+ ptr::null_mut()
|
||||
+ };
|
||||
+
|
||||
+ if live && ptr.is_null() {
|
||||
+ println!(
|
||||
+ "\rlive: disabled (unable to allocate {} MiB upfront)",
|
||||
+ size / MIBI as u64
|
||||
+ );
|
||||
+ live = false;
|
||||
+ }
|
||||
+
|
||||
+ let live = if live {
|
||||
+ Some(unsafe { slice::from_raw_parts_mut(ptr, live_size) })
|
||||
+ } else {
|
||||
+ println!("Continuing without live preload");
|
||||
+ None
|
||||
+ };
|
||||
+
|
||||
+ if let Some(live) = live {
|
||||
+ let mut i = 0;
|
||||
+ for chunk in live.chunks_mut(MIBI) {
|
||||
+ print!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
|
||||
+ i += unsafe {
|
||||
+ fs.disk
|
||||
+ .read_at(fs.block + i / redoxfs::BLOCK_SIZE, chunk)
|
||||
+ .expect("Failed to read live disk") as u64
|
||||
+ };
|
||||
+ }
|
||||
+ println!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
|
||||
+
|
||||
+ println!("Switching to live disk");
|
||||
+ unsafe {
|
||||
+ LIVE_OPT = Some((fs.block, slice::from_raw_parts_mut(ptr, live_size)));
|
||||
+ }
|
||||
+
|
||||
+ area_add(OsMemoryEntry {
|
||||
+ base: live.as_ptr() as u64,
|
||||
+ size: live.len() as u64,
|
||||
+ kind: OsMemoryKind::Reserved,
|
||||
+ });
|
||||
+
|
||||
+ Some(live)
|
||||
+ } else {
|
||||
+ None
|
||||
+ }
|
||||
-
|
||||
- Some(live)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -1,60 +0,0 @@
|
||||
diff --git a/src/os/uefi/device.rs b/src/os/uefi/device.rs
|
||||
index 4b0bf31..90a97b8 100644
|
||||
--- a/src/os/uefi/device.rs
|
||||
+++ b/src/os/uefi/device.rs
|
||||
@@ -46,6 +46,8 @@ fn device_path_relation(a_path: &DevicePath, b_path: &DevicePath) -> DevicePath
|
||||
}
|
||||
|
||||
fn esp_live_image(esp_handle: Handle, esp_device_path: &DevicePath) -> Option<Vec<u8>> {
|
||||
+ const MAX_LIVE_IMAGE_PRELOAD: usize = 128 * 1024 * 1024;
|
||||
+
|
||||
let mut esp_fs = match FileSystem::handle_protocol(esp_handle) {
|
||||
Ok(esp_fs) => esp_fs,
|
||||
Err(err) => {
|
||||
@@ -87,9 +89,37 @@ fn esp_live_image(esp_handle: Handle, esp_device_path: &DevicePath) -> Option<V
|
||||
};
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
+ let mut chunk = [0_u8; 64 * 1024];
|
||||
+
|
||||
+ loop {
|
||||
+ let read = match live_image.read(&mut chunk) {
|
||||
+ Ok(read) => read,
|
||||
+ Err(err) => {
|
||||
+ log::warn!(
|
||||
+ "Failed while reading {}\\redox-live.iso: {:?}",
|
||||
+ device_path_to_string(esp_device_path),
|
||||
+ err
|
||||
+ );
|
||||
+ return None;
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ if read == 0 {
|
||||
+ break;
|
||||
+ }
|
||||
|
||||
- live_image.read_to_end(&mut buffer).unwrap();
|
||||
+ if buffer.len().saturating_add(read) > MAX_LIVE_IMAGE_PRELOAD {
|
||||
+ log::warn!(
|
||||
+ "Skipping {}\\redox-live.iso preload: file exceeds {} MiB safety limit",
|
||||
+ device_path_to_string(esp_device_path),
|
||||
+ MAX_LIVE_IMAGE_PRELOAD / 1024 / 1024
|
||||
+ );
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ buffer.extend_from_slice(&chunk[..read]);
|
||||
+ }
|
||||
|
||||
Some(buffer)
|
||||
}
|
||||
@@ -130,7 +160,7 @@ pub fn disk_device_priority() -> Vec<DiskDevice> {
|
||||
return vec![DiskDevice {
|
||||
handle: esp_handle,
|
||||
// Support both a copy of livedisk.iso and a standalone redoxfs partition
|
||||
- partition_offset: if &buffer[512..520] == b"EFI PART" {
|
||||
+ partition_offset: if buffer.len() >= 520 && &buffer[512..520] == b"EFI PART" {
|
||||
//TODO: get block from partition table
|
||||
2 * crate::MIBI as u64
|
||||
} else {
|
||||
@@ -1,392 +0,0 @@
|
||||
diff --git a/src/arch/x86/mod.rs b/src/arch/x86/mod.rs
|
||||
index bda3f5d..55889df 100644
|
||||
--- a/src/arch/x86/mod.rs
|
||||
+++ b/src/arch/x86/mod.rs
|
||||
@@ -3,10 +3,15 @@ use crate::os::Os;
|
||||
pub(crate) mod x32;
|
||||
pub(crate) mod x64;
|
||||
|
||||
-pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
|
||||
+pub unsafe fn paging_create(
|
||||
+ os: &impl Os,
|
||||
+ kernel_phys: u64,
|
||||
+ kernel_size: u64,
|
||||
+ identity_map_end: u64,
|
||||
+) -> Option<usize> {
|
||||
unsafe {
|
||||
if crate::KERNEL_64BIT {
|
||||
- x64::paging_create(os, kernel_phys, kernel_size)
|
||||
+ x64::paging_create(os, kernel_phys, kernel_size, identity_map_end)
|
||||
} else {
|
||||
x32::paging_create(os, kernel_phys, kernel_size)
|
||||
}
|
||||
diff --git a/src/arch/x86/x64.rs b/src/arch/x86/x64.rs
|
||||
index a0a275a..fcf309d 100644
|
||||
--- a/src/arch/x86/x64.rs
|
||||
+++ b/src/arch/x86/x64.rs
|
||||
@@ -29,7 +29,12 @@ const PRESENT: u64 = 1;
|
||||
const WRITABLE: u64 = 1 << 1;
|
||||
const LARGE: u64 = 1 << 7;
|
||||
|
||||
-pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
|
||||
+pub unsafe fn paging_create(
|
||||
+ os: &impl Os,
|
||||
+ kernel_phys: u64,
|
||||
+ kernel_size: u64,
|
||||
+ identity_map_end: u64,
|
||||
+) -> Option<usize> {
|
||||
unsafe {
|
||||
// Create PML4
|
||||
let pml4 = paging_allocate(os)?;
|
||||
@@ -42,8 +47,14 @@ pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -
|
||||
pml4[0] = pdp.as_ptr() as u64 | WRITABLE | PRESENT;
|
||||
pml4[256] = pdp.as_ptr() as u64 | WRITABLE | PRESENT;
|
||||
|
||||
- // Identity map 8 GiB using 2 MiB pages
|
||||
- for pdp_i in 0..8 {
|
||||
+ let mut needed_pdp = identity_map_end.div_ceil(0x4000_0000);
|
||||
+ if needed_pdp == 0 {
|
||||
+ needed_pdp = 1;
|
||||
+ }
|
||||
+ assert!(needed_pdp <= pdp.len() as u64, "identity map end exceeds paging span");
|
||||
+
|
||||
+ // Identity map required physical range using 2 MiB pages
|
||||
+ for pdp_i in 0..needed_pdp as usize {
|
||||
let pd = paging_allocate(os)?;
|
||||
pdp[pdp_i] = pd.as_ptr() as u64 | WRITABLE | PRESENT;
|
||||
for pd_i in 0..pd.len() {
|
||||
diff --git a/src/main.rs b/src/main.rs
|
||||
index 78dabb0..fd8eb81 100644
|
||||
--- a/src/main.rs
|
||||
+++ b/src/main.rs
|
||||
@@ -62,6 +62,10 @@ pub static mut KERNEL_64BIT: bool = false;
|
||||
|
||||
pub static mut LIVE_OPT: Option<(u64, &'static [u8])> = None;
|
||||
|
||||
+fn region_end(base: u64, size: u64) -> u64 {
|
||||
+ base.saturating_add(size).next_multiple_of(0x1000)
|
||||
+}
|
||||
+
|
||||
struct SliceWriter<'a> {
|
||||
slice: &'a mut [u8],
|
||||
i: usize,
|
||||
@@ -645,9 +649,6 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
|
||||
(memory.len() as u64, memory.as_mut_ptr() as u64)
|
||||
};
|
||||
|
||||
- let page_phys = unsafe { paging_create(os, kernel.as_ptr() as u64, kernel.len() as u64) }
|
||||
- .expect("Failed to set up paging");
|
||||
-
|
||||
let max_env_size = 64 * KIBI;
|
||||
let mut env_size = max_env_size;
|
||||
let env_base = os.alloc_zeroed_page_aligned(env_size);
|
||||
@@ -655,6 +656,28 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
|
||||
panic!("Failed to allocate memory for stack");
|
||||
}
|
||||
|
||||
+ let mut identity_map_end = region_end(kernel.as_ptr() as u64, kernel.len() as u64)
|
||||
+ .max(region_end(stack_base as u64, stack_size as u64))
|
||||
+ .max(region_end(bootstrap_base, bootstrap_size))
|
||||
+ .max(region_end(env_base as u64, max_env_size as u64));
|
||||
+
|
||||
+ if let Some(ref live) = live_opt {
|
||||
+ identity_map_end = identity_map_end.max(region_end(
|
||||
+ live.as_ptr() as u64,
|
||||
+ live.len() as u64,
|
||||
+ ));
|
||||
+ }
|
||||
+
|
||||
+ let page_phys = unsafe {
|
||||
+ paging_create(
|
||||
+ os,
|
||||
+ kernel.as_ptr() as u64,
|
||||
+ kernel.len() as u64,
|
||||
+ identity_map_end,
|
||||
+ )
|
||||
+ }
|
||||
+ .expect("Failed to set up paging");
|
||||
+
|
||||
{
|
||||
let mut w = SliceWriter {
|
||||
slice: unsafe { slice::from_raw_parts_mut(env_base, max_env_size) },
|
||||
diff --git a/src/os/uefi/device.rs b/src/os/uefi/device.rs
|
||||
index 0b7991f..554d88e 100644
|
||||
--- a/src/os/uefi/device.rs
|
||||
+++ b/src/os/uefi/device.rs
|
||||
@@ -13,6 +13,160 @@ use uefi_std::{fs::FileSystem, loaded_image::LoadedImage, proto::Protocol};
|
||||
|
||||
use super::disk::{DiskEfi, DiskOrFileEfi};
|
||||
|
||||
+#[derive(Clone, Copy)]
|
||||
+struct GptPartitionInfo {
|
||||
+ first_lba: u64,
|
||||
+ last_lba: u64,
|
||||
+}
|
||||
+
|
||||
+fn read_u32_le(bytes: &[u8]) -> Option<u32> {
|
||||
+ Some(u32::from_le_bytes(bytes.get(..4)?.try_into().ok()?))
|
||||
+}
|
||||
+
|
||||
+fn read_u64_le(bytes: &[u8]) -> Option<u64> {
|
||||
+ Some(u64::from_le_bytes(bytes.get(..8)?.try_into().ok()?))
|
||||
+}
|
||||
+
|
||||
+fn decode_utf16_name(bytes: &[u8]) -> Option<String> {
|
||||
+ let mut units = Vec::new();
|
||||
+ for chunk in bytes.chunks_exact(2) {
|
||||
+ let unit = u16::from_le_bytes([chunk[0], chunk[1]]);
|
||||
+ if unit == 0 {
|
||||
+ break;
|
||||
+ }
|
||||
+ units.push(unit);
|
||||
+ }
|
||||
+ String::from_utf16(&units).ok()
|
||||
+}
|
||||
+
|
||||
+fn select_partition(best: &mut Option<GptPartitionInfo>, candidate: GptPartitionInfo) {
|
||||
+ match best {
|
||||
+ Some(current) if current.last_lba.saturating_sub(current.first_lba) >= candidate.last_lba.saturating_sub(candidate.first_lba) => {}
|
||||
+ _ => *best = Some(candidate),
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn parse_gpt_partition_offset_from_bytes(data: &[u8], block_size: usize) -> Option<u64> {
|
||||
+ let header_offset = block_size;
|
||||
+ let header = data.get(header_offset..header_offset + 92)?;
|
||||
+ if header.get(..8)? != b"EFI PART" {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ let entries_lba = read_u64_le(header.get(72..80)?)?;
|
||||
+ let entry_count = read_u32_le(header.get(80..84)?)? as usize;
|
||||
+ let entry_size = read_u32_le(header.get(84..88)?)? as usize;
|
||||
+ if entry_size < 128 {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ let entries_offset = entries_lba.checked_mul(block_size as u64)? as usize;
|
||||
+ let mut redox_partition = None;
|
||||
+ let mut fallback_partition = None;
|
||||
+
|
||||
+ for index in 0..entry_count {
|
||||
+ let entry_offset = entries_offset.checked_add(index.checked_mul(entry_size)?)?;
|
||||
+ let entry = data.get(entry_offset..entry_offset + entry_size)?;
|
||||
+ if entry.get(..16)?.iter().all(|byte| *byte == 0) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let first_lba = read_u64_le(entry.get(32..40)?)?;
|
||||
+ let last_lba = read_u64_le(entry.get(40..48)?)?;
|
||||
+ if first_lba == 0 || last_lba < first_lba {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let partition = GptPartitionInfo { first_lba, last_lba };
|
||||
+ let name = decode_utf16_name(entry.get(56..128)?).unwrap_or_default();
|
||||
+ if name == "REDOX" {
|
||||
+ redox_partition = Some(partition);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ select_partition(&mut fallback_partition, partition);
|
||||
+ }
|
||||
+
|
||||
+ redox_partition
|
||||
+ .or(fallback_partition)
|
||||
+ .map(|partition| partition.first_lba * block_size as u64)
|
||||
+}
|
||||
+
|
||||
+fn parse_gpt_partition_offset_from_parts(
|
||||
+ entries: &[u8],
|
||||
+ entry_count: usize,
|
||||
+ entry_size: usize,
|
||||
+ block_size: usize,
|
||||
+) -> Option<u64> {
|
||||
+ let mut redox_partition = None;
|
||||
+ let mut fallback_partition = None;
|
||||
+
|
||||
+ for index in 0..entry_count {
|
||||
+ let entry_offset = index.checked_mul(entry_size)?;
|
||||
+ let entry = entries.get(entry_offset..entry_offset + entry_size)?;
|
||||
+ if entry.get(..16)?.iter().all(|byte| *byte == 0) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let first_lba = read_u64_le(entry.get(32..40)?)?;
|
||||
+ let last_lba = read_u64_le(entry.get(40..48)?)?;
|
||||
+ if first_lba == 0 || last_lba < first_lba {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let partition = GptPartitionInfo { first_lba, last_lba };
|
||||
+ let name = decode_utf16_name(entry.get(56..128)?).unwrap_or_default();
|
||||
+ if name == "REDOX" {
|
||||
+ redox_partition = Some(partition);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ select_partition(&mut fallback_partition, partition);
|
||||
+ }
|
||||
+ redox_partition
|
||||
+ .or(fallback_partition)
|
||||
+ .map(|partition| partition.first_lba * block_size as u64)
|
||||
+}
|
||||
+
|
||||
+fn gpt_partition_offset_from_buffer(data: &[u8]) -> Option<u64> {
|
||||
+ parse_gpt_partition_offset_from_bytes(data, 512)
|
||||
+}
|
||||
+
|
||||
+fn gpt_partition_offset_from_disk(disk: &mut DiskEfi) -> Option<u64> {
|
||||
+ const GPT_SECTOR_SIZE: usize = 512;
|
||||
+
|
||||
+ if disk.media_block_size() == 0 {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ let mut boot_region = vec![0_u8; 2048];
|
||||
+ disk.read_bytes(0, &mut boot_region).ok()?;
|
||||
+ let header = boot_region.get(GPT_SECTOR_SIZE..GPT_SECTOR_SIZE + 92)?;
|
||||
+ if header.get(..8)? != b"EFI PART" {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ let entries_lba = read_u64_le(header.get(72..80)?)?;
|
||||
+ let entry_count = read_u32_le(header.get(80..84)?)? as usize;
|
||||
+ let entry_size = read_u32_le(header.get(84..88)?)? as usize;
|
||||
+ if entry_size < 128 {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ let entries_bytes = entry_count.checked_mul(entry_size)?;
|
||||
+ let entries_offset = entries_lba.checked_mul(GPT_SECTOR_SIZE as u64)?;
|
||||
+ let mut entries = vec![0_u8; entries_bytes];
|
||||
+ disk.read_bytes(entries_offset, &mut entries).ok()?;
|
||||
+
|
||||
+ parse_gpt_partition_offset_from_parts(&entries, entry_count, entry_size, GPT_SECTOR_SIZE)
|
||||
+}
|
||||
+
|
||||
#[derive(Debug)]
|
||||
enum DevicePathRelation {
|
||||
This,
|
||||
@@ -131,12 +285,7 @@ pub fn disk_device_priority() -> Vec<DiskDevice> {
|
||||
return vec![DiskDevice {
|
||||
handle: esp_handle,
|
||||
// Support both a copy of livedisk.iso and a standalone redoxfs partition
|
||||
- partition_offset: if &buffer[512..520] == b"EFI PART" {
|
||||
- //TODO: get block from partition table
|
||||
- 2 * crate::MIBI as u64
|
||||
- } else {
|
||||
- 0
|
||||
- },
|
||||
+ partition_offset: gpt_partition_offset_from_buffer(&buffer).unwrap_or(0),
|
||||
disk: DiskOrFileEfi::File(buffer),
|
||||
device_path: esp_device_path,
|
||||
file_path: Some("redox-live.iso"),
|
||||
@@ -154,7 +303,7 @@ pub fn disk_device_priority() -> Vec<DiskDevice> {
|
||||
};
|
||||
let mut devices = Vec::with_capacity(handles.len());
|
||||
for handle in handles {
|
||||
- let disk = match DiskEfi::handle_protocol(handle) {
|
||||
+ let mut disk = match DiskEfi::handle_protocol(handle) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
log::warn!(
|
||||
@@ -182,14 +331,15 @@ pub fn disk_device_priority() -> Vec<DiskDevice> {
|
||||
}
|
||||
};
|
||||
|
||||
+ let partition_offset = if disk.0.Media.LogicalPartition {
|
||||
+ 0
|
||||
+ } else {
|
||||
+ gpt_partition_offset_from_disk(&mut disk).unwrap_or(2 * crate::MIBI as u64)
|
||||
+ };
|
||||
+
|
||||
devices.push(DiskDevice {
|
||||
handle,
|
||||
- partition_offset: if disk.0.Media.LogicalPartition {
|
||||
- 0
|
||||
- } else {
|
||||
- //TODO: get block from partition table
|
||||
- 2 * crate::MIBI as u64
|
||||
- },
|
||||
+ partition_offset,
|
||||
disk: DiskOrFileEfi::Disk(disk),
|
||||
device_path,
|
||||
file_path: None,
|
||||
diff --git a/src/os/uefi/disk.rs b/src/os/uefi/disk.rs
|
||||
index 3f920bb..4d109f8 100644
|
||||
--- a/src/os/uefi/disk.rs
|
||||
+++ b/src/os/uefi/disk.rs
|
||||
@@ -117,3 +117,43 @@ impl Disk for DiskEfi {
|
||||
Err(Error::new(EIO))
|
||||
}
|
||||
}
|
||||
+
|
||||
+impl DiskEfi {
|
||||
+ pub fn media_block_size(&self) -> usize {
|
||||
+ self.0.Media.BlockSize as usize
|
||||
+ }
|
||||
+
|
||||
+ pub fn read_bytes(&mut self, offset: u64, buffer: &mut [u8]) -> Result<()> {
|
||||
+ let block_size = self.media_block_size();
|
||||
+ if block_size == 0 || block_size > self.1.len() {
|
||||
+ return Err(Error::new(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ let scratch = &mut self.1[..block_size];
|
||||
+ let mut copied = 0usize;
|
||||
+
|
||||
+ while copied < buffer.len() {
|
||||
+ let absolute = offset as usize + copied;
|
||||
+ let lba = (absolute / block_size) as u64;
|
||||
+ let in_block = absolute % block_size;
|
||||
+
|
||||
+ match (self.0.ReadBlocks)(
|
||||
+ self.0,
|
||||
+ self.0.Media.MediaId,
|
||||
+ lba,
|
||||
+ block_size,
|
||||
+ scratch.as_mut_ptr(),
|
||||
+ ) {
|
||||
+ status if status.is_success() => {
|
||||
+ let chunk_len = core::cmp::min(block_size - in_block, buffer.len() - copied);
|
||||
+ buffer[copied..copied + chunk_len]
|
||||
+ .copy_from_slice(&scratch[in_block..in_block + chunk_len]);
|
||||
+ copied += chunk_len;
|
||||
+ }
|
||||
+ _ => return Err(Error::new(EIO)),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ Ok(())
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/os/uefi/mod.rs b/src/os/uefi/mod.rs
|
||||
index c79266e..86235a4 100644
|
||||
--- a/src/os/uefi/mod.rs
|
||||
+++ b/src/os/uefi/mod.rs
|
||||
@@ -47,17 +47,19 @@ pub(crate) fn alloc_zeroed_page_aligned(size: usize) -> *mut u8 {
|
||||
let ptr = {
|
||||
// Max address mapped by src/arch paging code (8 GiB)
|
||||
let mut ptr = 0x2_0000_0000;
|
||||
- status_to_result((std::system_table().BootServices.AllocatePages)(
|
||||
- 1, // AllocateMaxAddress
|
||||
- MemoryType::EfiRuntimeServicesData, // Keeps this memory out of free space list
|
||||
+ if status_to_result((std::system_table().BootServices.AllocatePages)(
|
||||
+ 0, // AllocateAnyPages
|
||||
+ MemoryType::EfiLoaderData,
|
||||
pages,
|
||||
&mut ptr,
|
||||
))
|
||||
- .unwrap();
|
||||
+ .is_err()
|
||||
+ {
|
||||
+ return ptr::null_mut();
|
||||
+ }
|
||||
ptr as *mut u8
|
||||
};
|
||||
|
||||
- assert!(!ptr.is_null());
|
||||
unsafe { ptr::write_bytes(ptr, 0, pages * page_size) };
|
||||
ptr
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
# Red Bear OS branding in kernel start messages
|
||||
# Changes "Redox OS" to "RedBear OS" in architecture start files
|
||||
# Adds device init logging milestones in x86_shared start path
|
||||
|
||||
diff --git a/src/arch/aarch64/start.rs b/src/arch/aarch64/start.rs
|
||||
index e1c8cfb4..65e3fe33 100644
|
||||
--- a/src/arch/aarch64/start.rs
|
||||
+++ b/src/arch/aarch64/start.rs
|
||||
@@ -91,7 +91,7 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! {
|
||||
dtb::serial::init_early(dtb);
|
||||
}
|
||||
|
||||
- info!("Redox OS starting...");
|
||||
+ info!("RedBear OS starting...");
|
||||
args.print();
|
||||
|
||||
// Initialize RMM
|
||||
diff --git a/src/arch/riscv64/start.rs b/src/arch/riscv64/start.rs
|
||||
index 2551968f..a825536a 100644
|
||||
--- a/src/arch/riscv64/start.rs
|
||||
+++ b/src/arch/riscv64/start.rs
|
||||
@@ -97,7 +97,7 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! {
|
||||
init_early(dtb);
|
||||
}
|
||||
|
||||
- info!("Redox OS starting...");
|
||||
+ info!("RedBear OS starting...");
|
||||
args.print();
|
||||
|
||||
if let Some(dtb) = &dtb {
|
||||
diff --git a/src/arch/x86_shared/start.rs b/src/arch/x86_shared/start.rs
|
||||
index 7a7c0ae8..62f9523c 100644
|
||||
--- a/src/arch/x86_shared/start.rs
|
||||
+++ b/src/arch/x86_shared/start.rs
|
||||
@@ -91,7 +91,7 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
|
||||
// Set up graphical debug
|
||||
graphical_debug::init(args.env());
|
||||
|
||||
- info!("Redox OS starting...");
|
||||
+ info!("RedBear OS starting...");
|
||||
args.print();
|
||||
|
||||
// Set up GDT
|
||||
@@ -127,16 +127,21 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
|
||||
|
||||
// Initialize devices
|
||||
device::init();
|
||||
+ info!("kernel: device init complete (PIC + LAPIC)");
|
||||
|
||||
// Read ACPI tables, starts APs
|
||||
if cfg!(feature = "acpi") {
|
||||
crate::acpi::init(args.acpi_rsdp());
|
||||
+ info!("kernel: ACPI tables parsed");
|
||||
|
||||
device::init_after_acpi();
|
||||
+ info!("kernel: IOAPIC init complete");
|
||||
}
|
||||
crate::profiling::init();
|
||||
|
||||
// Initialize all of the non-core devices not otherwise needed to complete initialization
|
||||
device::init_noncore();
|
||||
+ info!("kernel: timer init complete, entering userspace");
|
||||
|
||||
args.bootstrap()
|
||||
};
|
||||
@@ -1,47 +0,0 @@
|
||||
diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs
|
||||
--- a/src/scheme/proc.rs
|
||||
+++ b/src/scheme/proc.rs
|
||||
@@ -147,6 +147,7 @@ enum ContextHandle {
|
||||
Priority,
|
||||
SchedAffinity,
|
||||
SchedPolicy,
|
||||
+ Name,
|
||||
|
||||
MmapMinAddr(Arc<AddrSpaceWrapper>),
|
||||
}
|
||||
@@ -267,6 +268,7 @@ impl ProcScheme {
|
||||
"sched-affinity" => (ContextHandle::SchedAffinity, true),
|
||||
// TODO: Switch this kernel-local proc handle over to a stable upstream
|
||||
// redox_syscall ProcCall::SetSchedPolicy opcode once that lands.
|
||||
"sched-policy" => (ContextHandle::SchedPolicy, false),
|
||||
+ "name" => (ContextHandle::Name, false),
|
||||
"status" => (ContextHandle::Status { privileged: false }, false),
|
||||
_ if path.starts_with("auth-") => {
|
||||
let nonprefix = &path["auth-".len()..];
|
||||
@@ -1218,6 +1220,16 @@ impl ContextHandle {
|
||||
Ok(2)
|
||||
}
|
||||
+ ContextHandle::Name => {
|
||||
+ let mut name_buf = [0u8; 32];
|
||||
+ let len = buf.copy_common_bytes_to_slice(&mut name_buf[..31]).unwrap_or(0);
|
||||
+ let mut context = context.write(token.token());
|
||||
+ context.name.clear();
|
||||
+ if let Ok(s) = core::str::from_utf8(&name_buf[..len]) {
|
||||
+ context.name.push_str(s);
|
||||
+ }
|
||||
+ Ok(len)
|
||||
+ }
|
||||
ContextHandle::Status { privileged } => {
|
||||
let mut args = buf.usizes();
|
||||
|
||||
@@ -1532,6 +1544,10 @@ impl ContextHandle {
|
||||
let data = [context.sched_policy as u8, context.sched_rt_priority];
|
||||
buf.copy_common_bytes_from_slice(&data)
|
||||
}
|
||||
+ ContextHandle::Name => {
|
||||
+ let context = context.read(token.token());
|
||||
+ buf.copy_common_bytes_from_slice(context.name.as_bytes())
|
||||
+ }
|
||||
ContextHandle::Status { .. } => {
|
||||
let status = {
|
||||
let context = context.read(token.token());
|
||||
@@ -1,70 +0,0 @@
|
||||
diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs
|
||||
--- a/src/scheme/proc.rs
|
||||
+++ b/src/scheme/proc.rs
|
||||
@@ -145,8 +145,9 @@ enum ContextHandle {
|
||||
// TODO: Remove this once openat is implemented, or allow openat-via-dup via e.g. the top-level
|
||||
// directory.
|
||||
OpenViaDup,
|
||||
+ Priority,
|
||||
SchedAffinity,
|
||||
SchedPolicy,
|
||||
Name,
|
||||
|
||||
MmapMinAddr(Arc<AddrSpaceWrapper>),
|
||||
@@ -160,6 +161,17 @@ pub struct ProcScheme;
|
||||
static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
|
||||
static HANDLES: RwLock<L1, HashMap<usize, Handle>> =
|
||||
RwLock::new(HashMap::with_hasher(DefaultHashBuilder::new()));
|
||||
+
|
||||
+const NICE_MIN: i32 = -20;
|
||||
+const NICE_MAX: i32 = 19;
|
||||
+
|
||||
+fn nice_to_kernel_prio(nice: i32) -> usize {
|
||||
+ (nice.saturating_add(20)).clamp(0, 39) as usize
|
||||
+}
|
||||
+
|
||||
+fn kernel_prio_to_nice(prio: usize) -> i32 {
|
||||
+ (prio.min(39) as i32) - 20
|
||||
+}
|
||||
|
||||
#[cfg(feature = "debugger")]
|
||||
#[allow(dead_code)]
|
||||
pub fn foreach_addrsp(
|
||||
@@ -253,6 +265,7 @@ impl ProcScheme {
|
||||
"sighandler" => (ContextHandle::Sighandler, false),
|
||||
"start" => (ContextHandle::Start, false),
|
||||
"open_via_dup" => (ContextHandle::OpenViaDup, false),
|
||||
+ "priority" => (ContextHandle::Priority, false),
|
||||
"mmap-min-addr" => (
|
||||
ContextHandle::MmapMinAddr(Arc::clone(
|
||||
context
|
||||
@@ -1191,6 +1204,17 @@ impl ContextHandle {
|
||||
|
||||
Ok(size_of_val(&mask))
|
||||
}
|
||||
+ Self::Priority => {
|
||||
+ let nice = unsafe { buf.read_exact::<i32>()? };
|
||||
+ if !(NICE_MIN..=NICE_MAX).contains(&nice) {
|
||||
+ return Err(Error::new(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ context
|
||||
+ .write(token.token())
|
||||
+ .set_sched_other_prio(nice_to_kernel_prio(nice));
|
||||
+
|
||||
+ Ok(size_of::<i32>())
|
||||
+ }
|
||||
Self::SchedPolicy => {
|
||||
if buf.len() != 2 {
|
||||
return Err(Error::new(EINVAL));
|
||||
@@ -1522,6 +1546,10 @@ impl ContextHandle {
|
||||
|
||||
buf.copy_exactly(crate::cpu_set::mask_as_bytes(&mask))?;
|
||||
Ok(size_of_val(&mask))
|
||||
+ }
|
||||
+ ContextHandle::Priority => {
|
||||
+ let nice = kernel_prio_to_nice(context.read(token.token()).prio);
|
||||
+ buf.copy_common_bytes_from_slice(&nice.to_ne_bytes())
|
||||
}
|
||||
ContextHandle::SchedPolicy => {
|
||||
let context = context.read(token.token());
|
||||
@@ -1,146 +0,0 @@
|
||||
diff --git a/src/percpu.rs b/src/percpu.rs
|
||||
--- a/src/percpu.rs
|
||||
+++ b/src/percpu.rs
|
||||
@@ -29,12 +29,14 @@ pub struct PerCpuSched {
|
||||
pub run_queues_lock: AtomicBool,
|
||||
pub balance: Cell<[usize; RUN_QUEUE_COUNT]>,
|
||||
pub last_queue: Cell<usize>,
|
||||
+ pub last_balance_time: Cell<u128>,
|
||||
}
|
||||
|
||||
impl PerCpuSched {
|
||||
pub const fn new() -> Self {
|
||||
const EMPTY: VecDeque<WeakContextRef> = VecDeque::new();
|
||||
Self {
|
||||
run_queues: SyncUnsafeCell::new([EMPTY; RUN_QUEUE_COUNT]),
|
||||
run_queues_lock: AtomicBool::new(false),
|
||||
balance: Cell::new([0; RUN_QUEUE_COUNT]),
|
||||
last_queue: Cell::new(0),
|
||||
+ last_balance_time: Cell::new(0),
|
||||
}
|
||||
}
|
||||
diff --git a/src/context/switch.rs b/src/context/switch.rs
|
||||
--- a/src/context/switch.rs
|
||||
+++ b/src/context/switch.rs
|
||||
@@ -33,6 +33,8 @@ const SCHED_PRIO_TO_WEIGHT: [usize; 40] = [
|
||||
70, 56, 45, 36, 29, 23, 18, 15,
|
||||
];
|
||||
|
||||
+const LOAD_BALANCE_INTERVAL_NS: u128 = 100_000_000;
|
||||
+
|
||||
static SCHED_STEAL_COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
@@ -101,6 +103,9 @@ pub fn tick(token: &mut CleanLockToken) {
|
||||
let new_ticks = ticks_cell.get() + 1;
|
||||
ticks_cell.set(new_ticks);
|
||||
|
||||
+ let balance_time = crate::time::monotonic(token);
|
||||
+ maybe_balance_queues(token, percpu, balance_time);
|
||||
+
|
||||
// Trigger a context switch after every 3 ticks.
|
||||
if new_ticks >= 3 {
|
||||
switch(token);
|
||||
@@ -427,6 +432,92 @@ fn steal_work(
|
||||
|
||||
None
|
||||
}
|
||||
+
|
||||
+fn queue_depth(percpu: &PercpuBlock) -> usize {
|
||||
+ let mut sched_lock = SchedQueuesLock::new(&percpu.sched);
|
||||
+ unsafe {
|
||||
+ sched_lock
|
||||
+ .queues_mut()
|
||||
+ .iter()
|
||||
+ .map(|queue| queue.len())
|
||||
+ .sum()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn migrate_one_context(
|
||||
+ token: &mut CleanLockToken,
|
||||
+ source_id: LogicalCpuId,
|
||||
+ target_id: LogicalCpuId,
|
||||
+ switch_time: u128,
|
||||
+) -> bool {
|
||||
+ let Some(source) = get_percpu_block(source_id) else {
|
||||
+ return false;
|
||||
+ };
|
||||
+ let Some(target) = get_percpu_block(target_id) else {
|
||||
+ return false;
|
||||
+ };
|
||||
+
|
||||
+ let source_idle = source.switch_internals.idle_context();
|
||||
+ let moved = {
|
||||
+ let mut source_lock = SchedQueuesLock::new(&source.sched);
|
||||
+ let source_queues = unsafe { source_lock.queues_mut() };
|
||||
+ pop_movable_context(token, source_queues, target_id, switch_time, &source_idle)
|
||||
+ };
|
||||
+
|
||||
+ let Some((prio, context_ref)) = moved else {
|
||||
+ return false;
|
||||
+ };
|
||||
+
|
||||
+ let mut target_lock = SchedQueuesLock::new(&target.sched);
|
||||
+ unsafe {
|
||||
+ target_lock.queues_mut()[prio].push_back(context_ref);
|
||||
+ }
|
||||
+ true
|
||||
+}
|
||||
+
|
||||
+fn maybe_balance_queues(token: &mut CleanLockToken, percpu: &PercpuBlock, balance_time: u128) {
|
||||
+ if crate::cpu_count() <= 1 || percpu.cpu_id != LogicalCpuId::BSP {
|
||||
+ return;
|
||||
+ }
|
||||
+ if balance_time.saturating_sub(percpu.sched.last_balance_time.get()) < LOAD_BALANCE_INTERVAL_NS
|
||||
+ {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ percpu.sched.last_balance_time.set(balance_time);
|
||||
+
|
||||
+ let mut depths = Vec::new();
|
||||
+ let mut total_depth = 0usize;
|
||||
+ for raw_id in 0..crate::cpu_count() {
|
||||
+ let cpu_id = LogicalCpuId::new(raw_id);
|
||||
+ let Some(cpu_percpu) = get_percpu_block(cpu_id) else {
|
||||
+ continue;
|
||||
+ };
|
||||
+ let depth = queue_depth(cpu_percpu);
|
||||
+ total_depth += depth;
|
||||
+ depths.push((cpu_id, depth));
|
||||
+ }
|
||||
+
|
||||
+ if depths.len() <= 1 || total_depth == 0 {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ let avg_depth = (total_depth + depths.len().saturating_sub(1)) / depths.len();
|
||||
+
|
||||
+ for target_index in 0..depths.len() {
|
||||
+ if depths[target_index].1 != 0 {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let mut source_index = None;
|
||||
+ let mut source_depth = 0usize;
|
||||
+ for (idx, &(_, depth)) in depths.iter().enumerate() {
|
||||
+ if idx == target_index {
|
||||
+ continue;
|
||||
+ }
|
||||
+ if depth > avg_depth + 1 && depth > source_depth {
|
||||
+ source_index = Some(idx);
|
||||
+ source_depth = depth;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ let Some(source_index) = source_index else {
|
||||
+ continue;
|
||||
+ };
|
||||
+
|
||||
+ let source_id = depths[source_index].0;
|
||||
+ let target_id = depths[target_index].0;
|
||||
+ if migrate_one_context(token, source_id, target_id, balance_time) {
|
||||
+ depths[source_index].1 = depths[source_index].1.saturating_sub(1);
|
||||
+ depths[target_index].1 += 1;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
@@ -1,190 +0,0 @@
|
||||
diff --git a/src/percpu.rs b/src/percpu.rs
|
||||
--- a/src/percpu.rs
|
||||
+++ b/src/percpu.rs
|
||||
@@ -100,6 +100,14 @@ static ALL_PERCPU_BLOCKS: [AtomicPtr<PercpuBlock>; MAX_CPU_COUNT as usize] =
|
||||
pub unsafe fn init_tlb_shootdown(id: LogicalCpuId, block: *mut PercpuBlock) {
|
||||
ALL_PERCPU_BLOCKS[id.get() as usize].store(block, Ordering::Release)
|
||||
}
|
||||
+
|
||||
+pub fn get_percpu_block(id: LogicalCpuId) -> Option<&'static PercpuBlock> {
|
||||
+ unsafe {
|
||||
+ ALL_PERCPU_BLOCKS[id.get() as usize]
|
||||
+ .load(Ordering::Acquire)
|
||||
+ .as_ref()
|
||||
+ }
|
||||
+}
|
||||
|
||||
pub fn get_all_stats() -> Vec<(LogicalCpuId, CpuStatsData)> {
|
||||
diff --git a/src/context/switch.rs b/src/context/switch.rs
|
||||
--- a/src/context/switch.rs
|
||||
+++ b/src/context/switch.rs
|
||||
@@ -7,15 +7,15 @@ use crate::{
|
||||
self, arch, idle_contexts, idle_contexts_try, run_contexts, ArcContextLockWriteGuard,
|
||||
Context, ContextLock, SchedPolicy, WeakContextRef, RUN_QUEUE_COUNT,
|
||||
},
|
||||
- cpu_set::LogicalCpuId,
|
||||
+ cpu_set::{LogicalCpuId, LogicalCpuSet},
|
||||
cpu_stats::{self, CpuState},
|
||||
- percpu::{PerCpuSched, PercpuBlock},
|
||||
+ percpu::{get_percpu_block, PerCpuSched, PercpuBlock},
|
||||
sync::{ArcRwLockWriteGuard, CleanLockToken, LockToken, L1, L4},
|
||||
};
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use core::{
|
||||
cell::{Cell, RefCell},
|
||||
hint, mem,
|
||||
- sync::atomic::Ordering,
|
||||
+ sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
use syscall::PtraceFlags;
|
||||
@@
|
||||
+static SCHED_STEAL_COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
+
|
||||
+fn assign_context_to_cpu(context: &mut Context, cpu_id: LogicalCpuId) {
|
||||
+ context.sched_affinity = LogicalCpuSet::empty();
|
||||
+ context.sched_affinity.atomic_set(cpu_id);
|
||||
+}
|
||||
@@
|
||||
+fn pop_movable_context(
|
||||
+ token: &mut CleanLockToken,
|
||||
+ queues: &mut [alloc::collections::VecDeque<WeakContextRef>; RUN_QUEUE_COUNT],
|
||||
+ target_cpu: LogicalCpuId,
|
||||
+ switch_time: u128,
|
||||
+ idle_context: &Arc<ContextLock>,
|
||||
+) -> Option<(usize, WeakContextRef)> {
|
||||
+ for prio in 0..RUN_QUEUE_COUNT {
|
||||
+ let len = queues[prio].len();
|
||||
+ for _ in 0..len {
|
||||
+ let Some(context_ref) = queues[prio].pop_front() else {
|
||||
+ break;
|
||||
+ };
|
||||
+ let Some(context_lock) = context_ref.upgrade() else {
|
||||
+ continue;
|
||||
+ };
|
||||
+ if Arc::ptr_eq(&context_lock, idle_context) {
|
||||
+ queues[prio].push_back(context_ref);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let mut context_guard = unsafe { context_lock.write_arc() };
|
||||
+ let sw = unsafe { update_stealable(&mut context_guard, switch_time) };
|
||||
+ if let UpdateResult::CanSwitch = sw {
|
||||
+ assign_context_to_cpu(&mut context_guard, target_cpu);
|
||||
+ let moved_ref = WeakContextRef(Arc::downgrade(ArcContextLockWriteGuard::rwlock(
|
||||
+ &context_guard,
|
||||
+ )));
|
||||
+ drop(context_guard);
|
||||
+ return Some((prio, moved_ref));
|
||||
+ }
|
||||
+
|
||||
+ if matches!(sw, UpdateResult::Blocked) {
|
||||
+ idle_contexts(token.downgrade()).push_back(context_ref);
|
||||
+ } else {
|
||||
+ queues[prio].push_back(context_ref);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ None
|
||||
+}
|
||||
+
|
||||
+fn steal_work(
|
||||
+ token: &mut CleanLockToken,
|
||||
+ cpu_id: LogicalCpuId,
|
||||
+ switch_time: u128,
|
||||
+) -> Option<ArcContextLockWriteGuard> {
|
||||
+ let cpu_count = crate::cpu_count();
|
||||
+ if cpu_count <= 1 {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ for offset in 1..cpu_count {
|
||||
+ let victim_id = LogicalCpuId::new((cpu_id.get() + offset) % cpu_count);
|
||||
+ let Some(victim) = get_percpu_block(victim_id) else {
|
||||
+ continue;
|
||||
+ };
|
||||
+
|
||||
+ let victim_idle = victim.switch_internals.idle_context();
|
||||
+ let mut victim_lock = SchedQueuesLock::new(&victim.sched);
|
||||
+ let victim_queues = unsafe { victim_lock.queues_mut() };
|
||||
+
|
||||
+ for prio in 0..RUN_QUEUE_COUNT {
|
||||
+ let len = victim_queues[prio].len();
|
||||
+ for _ in 0..len {
|
||||
+ let Some(context_ref) = victim_queues[prio].pop_front() else {
|
||||
+ break;
|
||||
+ };
|
||||
+ let Some(context_lock) = context_ref.upgrade() else {
|
||||
+ continue;
|
||||
+ };
|
||||
+ if Arc::ptr_eq(&context_lock, &victim_idle) {
|
||||
+ victim_queues[prio].push_back(context_ref);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let mut context_guard = unsafe { context_lock.write_arc() };
|
||||
+ let sw = unsafe { update_stealable(&mut context_guard, switch_time) };
|
||||
+ if let UpdateResult::CanSwitch = sw {
|
||||
+ assign_context_to_cpu(&mut context_guard, cpu_id);
|
||||
+ SCHED_STEAL_COUNT.fetch_add(1, Ordering::Relaxed);
|
||||
+ return Some(context_guard);
|
||||
+ }
|
||||
+
|
||||
+ if matches!(sw, UpdateResult::Blocked) {
|
||||
+ idle_contexts(token.downgrade()).push_back(context_ref);
|
||||
+ } else {
|
||||
+ victim_queues[prio].push_back(context_ref);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ None
|
||||
+}
|
||||
+
|
||||
+unsafe fn update_stealable(context: &mut Context, switch_time: u128) -> UpdateResult {
|
||||
+ if context.running {
|
||||
+ return UpdateResult::Skip;
|
||||
+ }
|
||||
+ if context.status.is_soft_blocked()
|
||||
+ && let Some(wake) = context.wake
|
||||
+ && switch_time >= wake
|
||||
+ {
|
||||
+ context.wake = None;
|
||||
+ context.unblock_no_ipi();
|
||||
+ }
|
||||
+ if context.status.is_runnable() {
|
||||
+ UpdateResult::CanSwitch
|
||||
+ } else {
|
||||
+ UpdateResult::Blocked
|
||||
+ }
|
||||
+}
|
||||
@@ -360,6 +469,10 @@ fn wakeup_contexts(token: &mut CleanLockToken, percpu: &PercpuBlock, switch_time
|
||||
let mut sched_lock = SchedQueuesLock::new(&percpu.sched);
|
||||
let run_queues = unsafe { sched_lock.queues_mut() };
|
||||
for (prio, context_ref) in wakeups {
|
||||
+ if let Some(context_lock) = context_ref.upgrade() {
|
||||
+ let mut context_guard = unsafe { context_lock.write_arc() };
|
||||
+ assign_context_to_cpu(&mut context_guard, percpu.cpu_id);
|
||||
+ }
|
||||
run_queues[prio].push_back(context_ref);
|
||||
}
|
||||
}
|
||||
@@ -559,6 +672,16 @@ fn select_next_context(
|
||||
);
|
||||
return Ok(Some(next_context_guard));
|
||||
}
|
||||
+
|
||||
+ if let Some(next_context_guard) = steal_work(token, cpu_id, switch_time) {
|
||||
+ queue_previous_context(
|
||||
+ token,
|
||||
+ percpu,
|
||||
+ &prev_context_lock,
|
||||
+ prev_context_guard,
|
||||
+ &idle_context,
|
||||
+ );
|
||||
+ return Ok(Some(next_context_guard));
|
||||
+ }
|
||||
|
||||
let global_next = {
|
||||
let contexts_data = run_contexts(token.token());
|
||||
@@ -1,147 +0,0 @@
|
||||
diff --git a/src/acpi/madt/mod.rs b/src/acpi/madt/mod.rs
|
||||
index 3159b9c4..c691eb8d 100644
|
||||
--- a/src/acpi/madt/mod.rs
|
||||
+++ b/src/acpi/madt/mod.rs
|
||||
@@ -146,6 +146,52 @@ pub struct MadtGicd {
|
||||
_reserved2: [u8; 3],
|
||||
}
|
||||
|
||||
+/// MADT Local x2APIC (entry type 0x9)
|
||||
+/// Used by modern AMD and Intel platforms with APIC IDs >= 255.
|
||||
+#[derive(Clone, Copy, Debug)]
|
||||
+#[repr(C, packed)]
|
||||
+pub struct MadtLocalX2Apic {
|
||||
+ _reserved: u16,
|
||||
+ pub x2apic_id: u32,
|
||||
+ pub flags: u32,
|
||||
+ pub processor_uid: u32,
|
||||
+}
|
||||
+
|
||||
+/// MADT Local APIC NMI (entry type 0x4)
|
||||
+/// Configures NMI routing to a processor's LINT0/LINT1 pin.
|
||||
+#[derive(Clone, Copy, Debug)]
|
||||
+#[repr(C, packed)]
|
||||
+pub struct MadtLocalApicNmi {
|
||||
+ pub processor: u8,
|
||||
+ pub flags: u16,
|
||||
+ pub nmi_pin: u8,
|
||||
+}
|
||||
+
|
||||
+/// MADT Local APIC Address Override (entry type 0x5)
|
||||
+/// Provides 64-bit override for the 32-bit local APIC address.
|
||||
+#[derive(Clone, Copy, Debug)]
|
||||
+#[repr(C, packed)]
|
||||
+pub struct MadtLapicAddressOverride {
|
||||
+ _reserved: u16,
|
||||
+ pub local_apic_address: u64,
|
||||
+}
|
||||
+
|
||||
+/// MADT Local x2APIC NMI (entry type 0xA)
|
||||
+/// x2APIC equivalent of type 0x4 for APIC IDs >= 255.
|
||||
+#[derive(Clone, Copy, Debug)]
|
||||
+#[repr(C, packed)]
|
||||
+pub struct MadtLocalX2ApicNmi {
|
||||
+ _reserved: u16,
|
||||
+ pub processor_uid: u32,
|
||||
+ pub flags: u16,
|
||||
+ pub nmi_pin: u8,
|
||||
+ _reserved2: u8,
|
||||
+}
|
||||
+
|
||||
+const _: () = assert!(size_of::<MadtLocalApicNmi>() == 4);
|
||||
+const _: () = assert!(size_of::<MadtLapicAddressOverride>() == 10);
|
||||
+const _: () = assert!(size_of::<MadtLocalX2ApicNmi>() == 10);
|
||||
+
|
||||
/// MADT Entries
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
@@ -160,6 +206,14 @@ pub enum MadtEntry {
|
||||
InvalidGicc(usize),
|
||||
Gicd(&'static MadtGicd),
|
||||
InvalidGicd(usize),
|
||||
+ LocalX2Apic(&'static MadtLocalX2Apic),
|
||||
+ InvalidLocalX2Apic(usize),
|
||||
+ LocalApicNmi(&'static MadtLocalApicNmi),
|
||||
+ InvalidLocalApicNmi(usize),
|
||||
+ LapicAddressOverride(&'static MadtLapicAddressOverride),
|
||||
+ InvalidLapicAddressOverride(usize),
|
||||
+ LocalX2ApicNmi(&'static MadtLocalX2ApicNmi),
|
||||
+ InvalidLocalX2ApicNmi(usize),
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
@@ -176,6 +230,10 @@ impl Iterator for MadtIter {
|
||||
let entry_len =
|
||||
unsafe { *(self.sdt.data_address() as *const u8).add(self.i + 1) } as usize;
|
||||
|
||||
+ if entry_len < 2 {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
if self.i + entry_len <= self.sdt.data_len() {
|
||||
let item = match entry_type {
|
||||
0x0 => {
|
||||
@@ -224,6 +282,44 @@ impl Iterator for MadtIter {
|
||||
MadtEntry::InvalidGicd(entry_len)
|
||||
}
|
||||
}
|
||||
+ 0x9 => {
|
||||
+ if entry_len == size_of::<MadtLocalX2Apic>() + 2 {
|
||||
+ MadtEntry::LocalX2Apic(unsafe {
|
||||
+ &*((self.sdt.data_address() + self.i + 2) as *const MadtLocalX2Apic)
|
||||
+ })
|
||||
+ } else {
|
||||
+ MadtEntry::InvalidLocalX2Apic(entry_len)
|
||||
+ }
|
||||
+ }
|
||||
+ 0x4 => {
|
||||
+ if entry_len == size_of::<MadtLocalApicNmi>() + 2 {
|
||||
+ MadtEntry::LocalApicNmi(unsafe {
|
||||
+ &*((self.sdt.data_address() + self.i + 2) as *const MadtLocalApicNmi)
|
||||
+ })
|
||||
+ } else {
|
||||
+ MadtEntry::InvalidLocalApicNmi(entry_len)
|
||||
+ }
|
||||
+ }
|
||||
+ 0x5 => {
|
||||
+ if entry_len == size_of::<MadtLapicAddressOverride>() + 2 {
|
||||
+ MadtEntry::LapicAddressOverride(unsafe {
|
||||
+ &*((self.sdt.data_address() + self.i + 2)
|
||||
+ as *const MadtLapicAddressOverride)
|
||||
+ })
|
||||
+ } else {
|
||||
+ MadtEntry::InvalidLapicAddressOverride(entry_len)
|
||||
+ }
|
||||
+ }
|
||||
+ 0xA => {
|
||||
+ if entry_len == size_of::<MadtLocalX2ApicNmi>() + 2 {
|
||||
+ MadtEntry::LocalX2ApicNmi(unsafe {
|
||||
+ &*((self.sdt.data_address() + self.i + 2)
|
||||
+ as *const MadtLocalX2ApicNmi)
|
||||
+ })
|
||||
+ } else {
|
||||
+ MadtEntry::InvalidLocalX2ApicNmi(entry_len)
|
||||
+ }
|
||||
+ }
|
||||
_ => MadtEntry::Unknown(entry_type),
|
||||
};
|
||||
|
||||
|
||||
diff --git a/src/devices/graphical_debug/mod.rs b/src/devices/graphical_debug/mod.rs
|
||||
index b701c9a8..00cc984d 100644
|
||||
--- a/src/devices/graphical_debug/mod.rs
|
||||
+++ b/src/devices/graphical_debug/mod.rs
|
||||
@@ -59,7 +59,12 @@ pub fn init(env: &[u8]) {
|
||||
);
|
||||
|
||||
let debug_display = DebugDisplay::new(width, height, stride, virt as *mut u32);
|
||||
- *DEBUG_DISPLAY.lock() = Some(debug_display);
|
||||
+ // FIXME: Writing to the framebuffer during early boot causes a hang on some
|
||||
+ // QEMU configurations (virtio-vga, ramfb). The bootloader maps the framebuffer
|
||||
+ // with default caching; the kernel remaps it with write-combining in memory::init().
|
||||
+ // Early kernel access before that remap appears to stall. Deferring DEBUG_DISPLAY
|
||||
+ // setup avoids the hang; userspace vesad/fbbootlogd handles graphical output.
|
||||
+ // *DEBUG_DISPLAY.lock() = Some(debug_display);
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
@@ -1,102 +0,0 @@
|
||||
--- a/b/src/connection.c 2025-07-06 13:11:26.000000000 +0100
|
||||
+++ b/src/connection.c 2026-05-01 00:15:42.778777823 +0100
|
||||
@@ -40,6 +40,12 @@
|
||||
#include <time.h>
|
||||
#include <ffi.h>
|
||||
|
||||
+#ifndef MSG_NOSIGNAL
|
||||
+#define MSG_NOSIGNAL 0
|
||||
+#endif
|
||||
+
|
||||
+extern FILE *open_memstream(char **bufp, size_t *sizep);
|
||||
+
|
||||
#include "wayland-util.h"
|
||||
#include "wayland-private.h"
|
||||
#include "wayland-os.h"
|
||||
--- a/b/src/event-loop.c 2025-07-06 13:11:26.000000000 +0100
|
||||
+++ b/src/event-loop.c 2026-05-01 00:15:42.778845239 +0100
|
||||
@@ -35,9 +35,43 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/epoll.h>
|
||||
-#include <sys/signalfd.h>
|
||||
-#include <sys/timerfd.h>
|
||||
#include <unistd.h>
|
||||
+/* Redox: relibc declares signalfd/timerfd in headers but has no implementation.
|
||||
+ Provide inline implementations via Redox schemes. */
|
||||
+#define SFD_CLOEXEC O_CLOEXEC
|
||||
+#define SFD_NONBLOCK O_NONBLOCK
|
||||
+#define TFD_CLOEXEC O_CLOEXEC
|
||||
+#define TFD_NONBLOCK O_NONBLOCK
|
||||
+#define TFD_TIMER_ABSTIME 0x1
|
||||
+struct signalfd_siginfo { uint8_t pad[128]; };
|
||||
+static int signalfd(int fd, const sigset_t *mask, int flags) {
|
||||
+ int oflag = O_RDWR;
|
||||
+ if (flags & SFD_CLOEXEC) oflag |= O_CLOEXEC;
|
||||
+ if (flags & SFD_NONBLOCK) oflag |= O_NONBLOCK;
|
||||
+ if (fd == -1) { fd = open("/scheme/event", oflag); if (fd < 0) return -1; }
|
||||
+ else { if (flags & SFD_CLOEXEC) fcntl(fd, F_SETFD, FD_CLOEXEC); }
|
||||
+ sigprocmask(SIG_BLOCK, mask, NULL);
|
||||
+ return fd;
|
||||
+}
|
||||
+static int timerfd_create(int clockid, int flags) {
|
||||
+ int oflag = O_RDWR;
|
||||
+ if (flags & TFD_CLOEXEC) oflag |= O_CLOEXEC;
|
||||
+ if (flags & TFD_NONBLOCK) oflag |= O_NONBLOCK;
|
||||
+ char path[64];
|
||||
+ snprintf(path, sizeof(path), "/scheme/time/%d", clockid);
|
||||
+ return open(path, oflag);
|
||||
+}
|
||||
+static int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value) {
|
||||
+ if (new_value == NULL) { errno = EFAULT; return -1; }
|
||||
+ ssize_t r = write(fd, &new_value->it_value, sizeof(struct timespec));
|
||||
+ return (r == sizeof(struct timespec)) ? 0 : -1;
|
||||
+}
|
||||
+static int timerfd_gettime(int fd, struct itimerspec *curr) {
|
||||
+ if (curr == NULL) { errno = EFAULT; return -1; }
|
||||
+ curr->it_interval = (struct timespec){0};
|
||||
+ ssize_t r = read(fd, &curr->it_value, sizeof(struct timespec));
|
||||
+ return (r == sizeof(struct timespec)) ? 0 : -1;
|
||||
+}
|
||||
#include "timespec-util.h"
|
||||
#include "wayland-util.h"
|
||||
#include "wayland-private.h"
|
||||
--- a/b/src/meson.build 2025-07-06 13:11:26.000000000 +0100
|
||||
+++ b/src/meson.build 2026-05-01 00:15:42.778925799 +0100
|
||||
@@ -81,8 +81,7 @@
|
||||
endif
|
||||
|
||||
if meson.is_cross_build() or not get_option('scanner')
|
||||
- scanner_dep = dependency('wayland-scanner', native: true, version: meson.project_version())
|
||||
- wayland_scanner_for_build = find_program(scanner_dep.get_variable(pkgconfig: 'wayland_scanner'))
|
||||
+ wayland_scanner_for_build = find_program('wayland-scanner', native: true)
|
||||
else
|
||||
wayland_scanner_for_build = wayland_scanner
|
||||
endif
|
||||
--- a/b/src/wayland-server.c 2025-07-06 13:11:26.000000000 +0100
|
||||
+++ b/src/wayland-server.c 2026-05-01 00:15:42.779083803 +0100
|
||||
@@ -39,7 +39,23 @@
|
||||
#include <dlfcn.h>
|
||||
#include <sys/time.h>
|
||||
#include <fcntl.h>
|
||||
-#include <sys/eventfd.h>
|
||||
+#ifndef EFD_CLOEXEC
|
||||
+#define EFD_CLOEXEC O_CLOEXEC
|
||||
+#endif
|
||||
+#ifndef EFD_NONBLOCK
|
||||
+#define EFD_NONBLOCK O_NONBLOCK
|
||||
+#endif
|
||||
+#ifndef EFD_SEMAPHORE
|
||||
+#define EFD_SEMAPHORE 0x1
|
||||
+#endif
|
||||
+static int eventfd(unsigned int initval, int flags) {
|
||||
+ int oflag = O_RDWR;
|
||||
+ if (flags & EFD_CLOEXEC) oflag |= O_CLOEXEC;
|
||||
+ if (flags & EFD_NONBLOCK) oflag |= O_NONBLOCK;
|
||||
+ char path[64];
|
||||
+ snprintf(path, sizeof(path), "/scheme/event/eventfd/%u/%d", initval, (flags & EFD_SEMAPHORE) ? 1 : 0);
|
||||
+ return open(path, oflag);
|
||||
+}
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp
|
||||
index e857f1a..f4f7f4a 100644
|
||||
--- a/src/corelib/io/qfilesystemengine_unix.cpp
|
||||
+++ b/src/corelib/io/qfilesystemengine_unix.cpp
|
||||
@@ -27,23 +27,6 @@
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
-#ifdef Q_OS_REDOX
|
||||
-// relibc does not provide unlinkat/linkat yet (POSIX.1-2008 *at functions).
|
||||
-// Provide inline stubs that work for AT_FDCWD only - sufficient for
|
||||
-// FreeDesktop trash operations in this file.
|
||||
-#include <fcntl.h>
|
||||
-static inline int unlinkat(int dirfd, const char *pathname, int flags)
|
||||
-{
|
||||
- if (dirfd != AT_FDCWD || flags != 0) { errno = ENOTSUP; return -1; }
|
||||
- return unlink(pathname);
|
||||
-}
|
||||
-static inline int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags)
|
||||
-{
|
||||
- if (olddirfd != AT_FDCWD || newdirfd != AT_FDCWD || flags != 0) { errno = ENOTSUP; return -1; }
|
||||
- return link(oldpath, newpath);
|
||||
-}
|
||||
-#endif
|
||||
-
|
||||
#include <chrono>
|
||||
#include <memory> // for std::unique_ptr
|
||||
@@ -1,56 +0,0 @@
|
||||
--- qtb-orig/src/plugins/platforms/wayland/hardwareintegration/qwaylandclientbufferintegration_p.h 2026-03-05 07:51:49.000000000 +0000
|
||||
+++ qtb-mod/src/plugins/platforms/wayland/hardwareintegration/qwaylandclientbufferintegration_p.h 2026-04-28 14:03:29.046092114 +0100
|
||||
@@ -51,9 +51,15 @@
|
||||
virtual bool supportsThreadedOpenGL() const { return false; }
|
||||
virtual bool supportsWindowDecoration() const { return false; }
|
||||
|
||||
+ #if QT_CONFIG(opengl)
|
||||
+#if QT_CONFIG(opengl)
|
||||
+#if QT_CONFIG(opengl)
|
||||
virtual QWaylandWindow *createEglWindow(QWindow *window) = 0;
|
||||
virtual QPlatformOpenGLContext *createPlatformOpenGLContext(const QSurfaceFormat &glFormat, QPlatformOpenGLContext *share) const = 0;
|
||||
+#endif
|
||||
+#endif
|
||||
virtual bool canCreatePlatformOffscreenSurface() const { return false; }
|
||||
+#endif
|
||||
#if QT_CONFIG(opengl)
|
||||
virtual QOpenGLContext *createOpenGLContext(EGLContext context, EGLDisplay contextDisplay, QOpenGLContext *shareContext) const = 0;
|
||||
virtual QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const { Q_UNUSED(surface); return nullptr; }
|
||||
@@ -65,7 +71,11 @@
|
||||
EglContext
|
||||
};
|
||||
virtual void *nativeResource(NativeResource /*resource*/) { return nullptr; }
|
||||
+#if QT_CONFIG(opengl)
|
||||
+#if QT_CONFIG(opengl)
|
||||
virtual void *nativeResourceForContext(NativeResource /*resource*/, QPlatformOpenGLContext */*context*/) { return nullptr; }
|
||||
+#endif
|
||||
+#endif
|
||||
};
|
||||
|
||||
}
|
||||
--- qtb-orig/src/plugins/platforms/wayland/qwaylandintegration.cpp 2026-03-05 07:51:49.000000000 +0000
|
||||
+++ qtb-mod/src/plugins/platforms/wayland/qwaylandintegration.cpp 2026-04-28 14:34:26.740450805 +0100
|
||||
@@ -135,17 +135,23 @@
|
||||
case ScreenWindowGrabbing: // whether QScreen::grabWindow() is supported
|
||||
return false;
|
||||
case OffscreenSurface:
|
||||
+#if QT_CONFIG(opengl)
|
||||
return mDisplay->clientBufferIntegration()
|
||||
&& mDisplay->clientBufferIntegration()->canCreatePlatformOffscreenSurface();
|
||||
+#else
|
||||
+ return false;
|
||||
+#endif
|
||||
default: return QPlatformIntegration::hasCapability(cap);
|
||||
}
|
||||
}
|
||||
|
||||
QPlatformWindow *QWaylandIntegration::createPlatformWindow(QWindow *window) const
|
||||
{
|
||||
+#if QT_CONFIG(opengl)
|
||||
if (window->surfaceType() == QWindow::OpenGLSurface
|
||||
&& mDisplay->clientBufferIntegration())
|
||||
return mDisplay->clientBufferIntegration()->createEglWindow(window);
|
||||
+#endif
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
if (window->surfaceType() == QSurface::VulkanSurface)
|
||||
@@ -1,23 +0,0 @@
|
||||
diff -ruwN source-old/src/CMakeLists.txt source/src/CMakeLists.txt
|
||||
--- source-old/src/CMakeLists.txt 2024-12-02 05:39:06.000000000 +0000
|
||||
+++ source/src/CMakeLists.txt 2026-04-30 00:00:00.000000000 +0000
|
||||
@@ -54,5 +54,5 @@
|
||||
endif()
|
||||
if (QT_FEATURE_network)
|
||||
- # add_subdirectory(network) # disabled for Redox
|
||||
+ add_subdirectory(network)
|
||||
if (ANDROID)
|
||||
add_subdirectory(network/android/jar)
|
||||
endif()
|
||||
diff -ruwN source-old/src/plugins/generic/CMakeLists.txt source/src/plugins/generic/CMakeLists.txt
|
||||
--- source-old/src/plugins/generic/CMakeLists.txt 2024-12-02 05:39:06.000000000 +0000
|
||||
+++ source/src/plugins/generic/CMakeLists.txt 2026-04-30 00:00:00.000000000 +0000
|
||||
@@ -18,6 +18,6 @@
|
||||
add_subdirectory(tslib)
|
||||
endif()
|
||||
if(QT_FEATURE_tuiotouch)
|
||||
- # add_subdirectory(tuiotouch) # disabled for Redox (needs Network)
|
||||
+ add_subdirectory(tuiotouch)
|
||||
endif()
|
||||
if(QT_FEATURE_libinput)
|
||||
add_subdirectory(libinput)
|
||||
@@ -1,722 +0,0 @@
|
||||
diff --git a/src/header/netdb/dns/mod.rs b/src/header/netdb/dns/mod.rs
|
||||
index 9d7e44b..f5bc21b 100644
|
||||
--- a/src/header/netdb/dns/mod.rs
|
||||
+++ b/src/header/netdb/dns/mod.rs
|
||||
@@ -15,6 +15,35 @@ use alloc::{string::String, vec::Vec};
|
||||
mod answer;
|
||||
mod query;
|
||||
|
||||
+const DNS_FLAG_QR: u16 = 0x8000;
|
||||
+const DNS_FLAG_TC: u16 = 0x0200;
|
||||
+const DNS_RCODE_MASK: u16 = 0x000F;
|
||||
+
|
||||
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
+pub(super) enum DnsError {
|
||||
+ MalformedResponse,
|
||||
+ TransactionIdMismatch,
|
||||
+ NotResponse,
|
||||
+ Truncated,
|
||||
+ ServerFailure,
|
||||
+ NameError,
|
||||
+ ResponseCode(u8),
|
||||
+}
|
||||
+
|
||||
+impl DnsError {
|
||||
+ fn as_str(self) -> &'static str {
|
||||
+ match self {
|
||||
+ Self::MalformedResponse => "malformed dns response",
|
||||
+ Self::TransactionIdMismatch => "dns transaction id mismatch",
|
||||
+ Self::NotResponse => "dns packet was not a response",
|
||||
+ Self::Truncated => "truncated dns response",
|
||||
+ Self::ServerFailure => "dns server failure",
|
||||
+ Self::NameError => "dns name error",
|
||||
+ Self::ResponseCode(_) => "dns server returned an error response",
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Dns {
|
||||
pub transaction_id: u16,
|
||||
@@ -59,6 +88,14 @@ impl Dns {
|
||||
}
|
||||
|
||||
pub fn parse(data: &[u8]) -> Result<Self, String> {
|
||||
+ Self::parse_impl(data, None).map_err(|err| err.as_str().into())
|
||||
+ }
|
||||
+
|
||||
+ pub(super) fn parse_reply(data: &[u8], expected_transaction_id: u16) -> Result<Self, DnsError> {
|
||||
+ Self::parse_impl(data, Some(expected_transaction_id))
|
||||
+ }
|
||||
+
|
||||
+ fn parse_impl(data: &[u8], expected_transaction_id: Option<u16>) -> Result<Self, DnsError> {
|
||||
let name_ind = 0b1100_0000;
|
||||
let mut i = 0;
|
||||
|
||||
@@ -66,7 +103,7 @@ impl Dns {
|
||||
() => {{
|
||||
i += 1;
|
||||
if i > data.len() {
|
||||
- return Err(format!("{}: {}: pop_u8", file!(), line!()));
|
||||
+ return Err(DnsError::MalformedResponse);
|
||||
}
|
||||
data[i - 1]
|
||||
}};
|
||||
@@ -77,9 +114,11 @@ impl Dns {
|
||||
use core::convert::TryInto;
|
||||
i += 2;
|
||||
if i > data.len() {
|
||||
- return Err(format!("{}: {}: pop_n16", file!(), line!()));
|
||||
+ return Err(DnsError::MalformedResponse);
|
||||
}
|
||||
- let bytes: [u8; 2] = data[i - 2..i].try_into().unwrap();
|
||||
+ let bytes: [u8; 2] = data[i - 2..i]
|
||||
+ .try_into()
|
||||
+ .map_err(|_| DnsError::MalformedResponse)?;
|
||||
u16::from_be_bytes(bytes)
|
||||
}};
|
||||
}
|
||||
@@ -156,11 +195,83 @@ impl Dns {
|
||||
});
|
||||
}
|
||||
|
||||
- Ok(Dns {
|
||||
+ let dns = Dns {
|
||||
transaction_id,
|
||||
flags,
|
||||
queries,
|
||||
answers,
|
||||
- })
|
||||
+ };
|
||||
+
|
||||
+ if let Some(expected_transaction_id) = expected_transaction_id {
|
||||
+ if dns.transaction_id != expected_transaction_id {
|
||||
+ return Err(DnsError::TransactionIdMismatch);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if dns.flags & DNS_FLAG_QR == 0 {
|
||||
+ return Err(DnsError::NotResponse);
|
||||
+ }
|
||||
+
|
||||
+ if dns.flags & DNS_FLAG_TC != 0 {
|
||||
+ return Err(DnsError::Truncated);
|
||||
+ }
|
||||
+
|
||||
+ match (dns.flags & DNS_RCODE_MASK) as u8 {
|
||||
+ 0 => Ok(dns),
|
||||
+ 2 => Err(DnsError::ServerFailure),
|
||||
+ 3 => Err(DnsError::NameError),
|
||||
+ rcode => Err(DnsError::ResponseCode(rcode)),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[cfg(test)]
|
||||
+mod tests {
|
||||
+ use alloc::{string::ToString, vec::Vec};
|
||||
+
|
||||
+ use super::{Dns, DnsError, DnsQuery};
|
||||
+
|
||||
+ fn packet(transaction_id: u16, flags: u16) -> Vec<u8> {
|
||||
+ Dns {
|
||||
+ transaction_id,
|
||||
+ flags,
|
||||
+ queries: vec![DnsQuery {
|
||||
+ name: "example.com".to_string(),
|
||||
+ q_type: 0x0001,
|
||||
+ q_class: 0x0001,
|
||||
+ }],
|
||||
+ answers: vec![],
|
||||
+ }
|
||||
+ .compile()
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn parse_reply_accepts_valid_response() {
|
||||
+ let response = Dns::parse_reply(&packet(0x1234, 0x8180), 0x1234).unwrap();
|
||||
+ assert_eq!(response.transaction_id, 0x1234);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn parse_reply_rejects_transaction_id_mismatch() {
|
||||
+ let err = Dns::parse_reply(&packet(0x1234, 0x8180), 0x4321).unwrap_err();
|
||||
+ assert_eq!(err, DnsError::TransactionIdMismatch);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn parse_rejects_query_packets() {
|
||||
+ let err = Dns::parse(&packet(0x1234, 0x0100)).unwrap_err();
|
||||
+ assert_eq!(err, DnsError::NotResponse.as_str());
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn parse_rejects_truncated_response() {
|
||||
+ let err = Dns::parse(&packet(0x1234, 0x8380)).unwrap_err();
|
||||
+ assert_eq!(err, DnsError::Truncated.as_str());
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn parse_rejects_name_error_response() {
|
||||
+ let err = Dns::parse(&packet(0x1234, 0x8183)).unwrap_err();
|
||||
+ assert_eq!(err, DnsError::NameError.as_str());
|
||||
+ }
|
||||
}
|
||||
diff --git a/src/header/netdb/lookup.rs b/src/header/netdb/lookup.rs
|
||||
index c2b6cdb..af25f97 100644
|
||||
--- a/src/header/netdb/lookup.rs
|
||||
+++ b/src/header/netdb/lookup.rs
|
||||
@@ -1,10 +1,10 @@
|
||||
-use alloc::{boxed::Box, string::ToString, vec::Vec};
|
||||
+use alloc::{string::ToString, vec::Vec};
|
||||
use core::{mem, ptr};
|
||||
|
||||
use crate::{
|
||||
out::Out,
|
||||
platform::{
|
||||
- Pal, Sys,
|
||||
+ self, Pal, Sys,
|
||||
types::{c_int, c_void},
|
||||
},
|
||||
};
|
||||
@@ -25,13 +25,86 @@ use crate::header::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
- dns::{Dns, DnsQuery},
|
||||
+ dns::{Dns, DnsError, DnsQuery},
|
||||
sys::get_dns_server,
|
||||
};
|
||||
|
||||
pub type LookupHost = Vec<in_addr>;
|
||||
pub type LookupHostV6 = Vec<in6_addr>;
|
||||
+
|
||||
+fn close_socket(sock: c_int) {
|
||||
+ if sock >= 0 {
|
||||
+ if let Ok(()) = Sys::close(sock) {};
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn last_socket_error(default: c_int) -> c_int {
|
||||
+ match platform::ERRNO.get() {
|
||||
+ 0 => default,
|
||||
+ err => err,
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn map_dns_error(err: DnsError) -> c_int {
|
||||
+ match err {
|
||||
+ DnsError::NameError => ENOENT,
|
||||
+ DnsError::ServerFailure => EAGAIN,
|
||||
+ DnsError::Truncated => EMSGSIZE,
|
||||
+ DnsError::MalformedResponse
|
||||
+ | DnsError::TransactionIdMismatch
|
||||
+ | DnsError::NotResponse
|
||||
+ | DnsError::ResponseCode(_) => EREMOTEIO,
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn lookup_dns_response(packet: &Dns, dns_addr: u32) -> Result<Dns, c_int> {
|
||||
+ let packet_data = packet.compile();
|
||||
+ let packet_data_len = packet_data.len();
|
||||
+ let packet_data_ptr = packet_data.as_ptr().cast::<c_void>();
|
||||
+
|
||||
+ let dest = sockaddr_in {
|
||||
+ sin_family: AF_INET as u16,
|
||||
+ sin_port: htons(53),
|
||||
+ sin_addr: in_addr { s_addr: dns_addr },
|
||||
+ ..Default::default()
|
||||
+ };
|
||||
+ let dest_ptr = ptr::from_ref(&dest).cast::<sockaddr>();
|
||||
+
|
||||
+ let sock = unsafe { sys_socket::socket(AF_INET, SOCK_DGRAM, i32::from(IPPROTO_UDP)) };
|
||||
+ if sock < 0 {
|
||||
+ return Err(last_socket_error(EIO));
|
||||
+ }
|
||||
+
|
||||
+ if unsafe { sys_socket::connect(sock, dest_ptr, mem::size_of_val(&dest) as socklen_t) } < 0 {
|
||||
+ let err = last_socket_error(EIO);
|
||||
+ close_socket(sock);
|
||||
+ return Err(err);
|
||||
+ }
|
||||
+
|
||||
+ if unsafe { sys_socket::send(sock, packet_data_ptr, packet_data_len, 0) } < 0 {
|
||||
+ let err = last_socket_error(EIO);
|
||||
+ close_socket(sock);
|
||||
+ return Err(err);
|
||||
+ }
|
||||
+
|
||||
+ let tv = timeval {
|
||||
+ tv_sec: 5,
|
||||
+ tv_usec: 0,
|
||||
+ };
|
||||
+ unsafe {
|
||||
+ sys_socket::setsockopt(
|
||||
+ sock,
|
||||
+ SOL_SOCKET,
|
||||
+ SO_RCVTIMEO,
|
||||
+ &tv as *const timeval as *const c_void,
|
||||
+ core::mem::size_of::<timeval>() as socklen_t,
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ let mut buf = vec![0u8; 65536];
|
||||
+ let buf_ptr = buf.as_mut_ptr().cast::<c_void>();
|
||||
+
|
||||
+ let mut count: isize = -1;
|
||||
+ let mut recv_error = EIO;
|
||||
+ for attempt in 0..2 {
|
||||
+ count = unsafe { sys_socket::recv(sock, buf_ptr, buf.len(), 0) };
|
||||
+ if count >= 0 {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ recv_error = last_socket_error(EIO);
|
||||
+ if attempt + 1 == 2 {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if unsafe { sys_socket::send(sock, packet_data_ptr, packet_data_len, 0) } < 0 {
|
||||
+ recv_error = last_socket_error(EIO);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if count < 0 {
|
||||
+ close_socket(sock);
|
||||
+ return Err(recv_error);
|
||||
+ }
|
||||
+
|
||||
+ let response = match Dns::parse_reply(&buf[..count as usize], packet.transaction_id) {
|
||||
+ Ok(response) => response,
|
||||
+ Err(err) => {
|
||||
+ close_socket(sock);
|
||||
+ return Err(map_dns_error(err));
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ close_socket(sock);
|
||||
+ Ok(response)
|
||||
+}
|
||||
|
||||
pub fn lookup_host(host: &str) -> Result<LookupHost, c_int> {
|
||||
if let Some(host_direct_addr) = parse_ipv4_string(host) {
|
||||
@@ -61,97 +134,30 @@ pub fn lookup_host(host: &str) -> Result<LookupHost, c_int> {
|
||||
answers: vec![],
|
||||
};
|
||||
|
||||
- let packet_data = packet.compile();
|
||||
- let packet_data_len = packet_data.len();
|
||||
-
|
||||
- let packet_data_box = packet_data.into_boxed_slice();
|
||||
- let packet_data_ptr = Box::into_raw(packet_data_box) as *mut _ as *mut c_void;
|
||||
-
|
||||
- let dest = sockaddr_in {
|
||||
- sin_family: AF_INET as u16,
|
||||
- sin_port: htons(53),
|
||||
- sin_addr: in_addr { s_addr: dns_addr },
|
||||
- ..Default::default()
|
||||
- };
|
||||
- let dest_ptr = ptr::from_ref(&dest).cast::<sockaddr>();
|
||||
-
|
||||
- let sock = unsafe {
|
||||
- let sock = sys_socket::socket(AF_INET, SOCK_DGRAM, i32::from(IPPROTO_UDP));
|
||||
- if sys_socket::connect(sock, dest_ptr, mem::size_of_val(&dest) as socklen_t) < 0 {
|
||||
- return Err(EIO);
|
||||
- }
|
||||
- if sys_socket::send(sock, packet_data_ptr, packet_data_len, 0) < 0 {
|
||||
- drop(Box::from_raw(packet_data_ptr));
|
||||
- return Err(EIO);
|
||||
- }
|
||||
- sock
|
||||
- };
|
||||
-
|
||||
- unsafe {
|
||||
- drop(Box::from_raw(packet_data_ptr));
|
||||
- }
|
||||
-
|
||||
- let mut buf = vec![0u8; 65536];
|
||||
- let buf_ptr = buf.as_mut_ptr().cast::<c_void>();
|
||||
-
|
||||
- // Set 5s recv timeout (best-effort; if this fails, recv may block longer).
|
||||
- let tv = timeval {
|
||||
- tv_sec: 5,
|
||||
- tv_usec: 0,
|
||||
- };
|
||||
- unsafe {
|
||||
- sys_socket::setsockopt(
|
||||
- sock,
|
||||
- SOL_SOCKET,
|
||||
- SO_RCVTIMEO,
|
||||
- &tv as *const timeval as *const c_void,
|
||||
- core::mem::size_of::<timeval>() as socklen_t,
|
||||
- );
|
||||
- }
|
||||
-
|
||||
- let mut count: isize = -1;
|
||||
- for _attempt in 0..2 {
|
||||
- count = unsafe { sys_socket::recv(sock, buf_ptr, 65536, 0) };
|
||||
- if count >= 0 {
|
||||
- break;
|
||||
- }
|
||||
- if unsafe { sys_socket::send(sock, packet_data_ptr, packet_data_len, 0) } < 0 {
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
- if count < 0 {
|
||||
- return Err(EIO);
|
||||
- }
|
||||
-
|
||||
- match Dns::parse(&buf[..count as usize]) {
|
||||
- Ok(response) => {
|
||||
- let addrs: Vec<_> = response
|
||||
- .answers
|
||||
- .into_iter()
|
||||
- .filter_map(|answer| {
|
||||
- if answer.a_type == 0x0001
|
||||
- && answer.a_class == 0x0001
|
||||
- && answer.data.len() == 4
|
||||
- {
|
||||
- let addr = in_addr {
|
||||
- s_addr: u32::from_ne_bytes([
|
||||
- answer.data[0],
|
||||
- answer.data[1],
|
||||
- answer.data[2],
|
||||
- answer.data[3],
|
||||
- ]),
|
||||
- };
|
||||
- Some(addr)
|
||||
- } else {
|
||||
- None
|
||||
- }
|
||||
- })
|
||||
- .collect();
|
||||
-
|
||||
- Ok(addrs)
|
||||
- }
|
||||
- Err(_err) => Err(EINVAL),
|
||||
- }
|
||||
+ let response = lookup_dns_response(&packet, dns_addr)?;
|
||||
+ let addrs: Vec<_> = response
|
||||
+ .answers
|
||||
+ .into_iter()
|
||||
+ .filter_map(|answer| {
|
||||
+ if answer.a_type == 0x0001 && answer.a_class == 0x0001 && answer.data.len() == 4 {
|
||||
+ let addr = in_addr {
|
||||
+ s_addr: u32::from_ne_bytes([
|
||||
+ answer.data[0],
|
||||
+ answer.data[1],
|
||||
+ answer.data[2],
|
||||
+ answer.data[3],
|
||||
+ ]),
|
||||
+ };
|
||||
+ Some(addr)
|
||||
+ } else {
|
||||
+ None
|
||||
+ }
|
||||
+ })
|
||||
+ .collect();
|
||||
+
|
||||
+ Ok(addrs)
|
||||
} else {
|
||||
Err(EINVAL)
|
||||
}
|
||||
@@ -186,97 +192,30 @@ pub fn lookup_host_v6(host: &str) -> Result<LookupHostV6, c_int> {
|
||||
answers: vec![],
|
||||
};
|
||||
|
||||
- let packet_data = packet.compile();
|
||||
- let packet_data_len = packet_data.len();
|
||||
-
|
||||
- let packet_data_box = packet_data.into_boxed_slice();
|
||||
- let packet_data_ptr = Box::into_raw(packet_data_box) as *mut _ as *mut c_void;
|
||||
-
|
||||
- let dest = sockaddr_in {
|
||||
- sin_family: AF_INET as u16,
|
||||
- sin_port: htons(53),
|
||||
- sin_addr: in_addr { s_addr: dns_addr },
|
||||
- ..Default::default()
|
||||
- };
|
||||
- let dest_ptr = ptr::from_ref(&dest).cast::<sockaddr>();
|
||||
-
|
||||
- let sock = unsafe {
|
||||
- let sock = sys_socket::socket(AF_INET, SOCK_DGRAM, i32::from(IPPROTO_UDP));
|
||||
- if sys_socket::connect(sock, dest_ptr, mem::size_of_val(&dest) as socklen_t) < 0 {
|
||||
- return Err(EIO);
|
||||
- }
|
||||
- if sys_socket::send(sock, packet_data_ptr, packet_data_len, 0) < 0 {
|
||||
- drop(Box::from_raw(packet_data_ptr));
|
||||
- return Err(EIO);
|
||||
- }
|
||||
- sock
|
||||
- };
|
||||
-
|
||||
- unsafe {
|
||||
- drop(Box::from_raw(packet_data_ptr));
|
||||
- }
|
||||
-
|
||||
- let mut buf = vec![0u8; 65536];
|
||||
- let buf_ptr = buf.as_mut_ptr().cast::<c_void>();
|
||||
-
|
||||
- // Set 5s recv timeout (best-effort; if this fails, recv may block longer).
|
||||
- let tv = timeval {
|
||||
- tv_sec: 5,
|
||||
- tv_usec: 0,
|
||||
- };
|
||||
- unsafe {
|
||||
- sys_socket::setsockopt(
|
||||
- sock,
|
||||
- SOL_SOCKET,
|
||||
- SO_RCVTIMEO,
|
||||
- &tv as *const timeval as *const c_void,
|
||||
- core::mem::size_of::<timeval>() as socklen_t,
|
||||
- );
|
||||
- }
|
||||
-
|
||||
- let mut count: isize = -1;
|
||||
- for _attempt in 0..2 {
|
||||
- count = unsafe { sys_socket::recv(sock, buf_ptr, 65536, 0) };
|
||||
- if count >= 0 {
|
||||
- break;
|
||||
- }
|
||||
- if unsafe { sys_socket::send(sock, packet_data_ptr, packet_data_len, 0) } < 0 {
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
- if count < 0 {
|
||||
- return Err(EIO);
|
||||
- }
|
||||
-
|
||||
- match Dns::parse(&buf[..count as usize]) {
|
||||
- Ok(response) => {
|
||||
- let addrs: Vec<_> = response
|
||||
- .answers
|
||||
- .into_iter()
|
||||
- .filter_map(|answer| {
|
||||
- if answer.a_type == 0x001c
|
||||
- && answer.a_class == 0x0001
|
||||
- && answer.data.len() == 16
|
||||
- {
|
||||
- let mut s6_addr = [0u8; 16];
|
||||
- s6_addr.copy_from_slice(&answer.data[..16]);
|
||||
- Some(in6_addr { s6_addr })
|
||||
- } else {
|
||||
- None
|
||||
- }
|
||||
- })
|
||||
- .collect();
|
||||
-
|
||||
- Ok(addrs)
|
||||
- }
|
||||
- Err(_err) => Err(EINVAL),
|
||||
- }
|
||||
+ let response = lookup_dns_response(&packet, dns_addr)?;
|
||||
+ let addrs: Vec<_> = response
|
||||
+ .answers
|
||||
+ .into_iter()
|
||||
+ .filter_map(|answer| {
|
||||
+ if answer.a_type == 0x001c && answer.a_class == 0x0001 && answer.data.len() == 16 {
|
||||
+ let mut s6_addr = [0u8; 16];
|
||||
+ s6_addr.copy_from_slice(&answer.data[..16]);
|
||||
+ Some(in6_addr { s6_addr })
|
||||
+ } else {
|
||||
+ None
|
||||
+ }
|
||||
+ })
|
||||
+ .collect();
|
||||
+
|
||||
+ Ok(addrs)
|
||||
} else {
|
||||
Err(EINVAL)
|
||||
}
|
||||
@@ -315,82 +254,24 @@ pub fn lookup_addr(addr: in_addr) -> Result<Vec<Vec<u8>>, c_int> {
|
||||
answers: vec![],
|
||||
};
|
||||
|
||||
- let packet_data = packet.compile();
|
||||
- let packet_data_len = packet_data.len();
|
||||
- let packet_data_box = packet_data.into_boxed_slice();
|
||||
- let packet_data_ptr = Box::into_raw(packet_data_box) as *mut _ as *mut c_void;
|
||||
-
|
||||
- let dest = sockaddr_in {
|
||||
- sin_family: AF_INET as u16,
|
||||
- sin_port: htons(53),
|
||||
- sin_addr: in_addr { s_addr: dns_addr },
|
||||
- ..Default::default()
|
||||
- };
|
||||
-
|
||||
- let dest_ptr = ptr::from_ref(&dest).cast::<sockaddr>();
|
||||
-
|
||||
- let sock = unsafe {
|
||||
- let sock = sys_socket::socket(AF_INET, SOCK_DGRAM, i32::from(IPPROTO_UDP));
|
||||
- if sys_socket::connect(sock, dest_ptr, mem::size_of_val(&dest) as socklen_t) < 0 {
|
||||
- return Err(EIO);
|
||||
- }
|
||||
- sock
|
||||
- };
|
||||
-
|
||||
- unsafe {
|
||||
- if sys_socket::send(sock, packet_data_ptr, packet_data_len, 0) < 0 {
|
||||
- return Err(EIO);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- unsafe {
|
||||
- drop(Box::from_raw(packet_data_ptr));
|
||||
- }
|
||||
-
|
||||
- let mut buf = [0u8; 65536];
|
||||
- let buf_ptr = buf.as_mut_ptr().cast::<c_void>();
|
||||
-
|
||||
- // Set 5s recv timeout (best-effort; if this fails, recv may block longer).
|
||||
- let tv = timeval {
|
||||
- tv_sec: 5,
|
||||
- tv_usec: 0,
|
||||
- };
|
||||
- unsafe {
|
||||
- sys_socket::setsockopt(
|
||||
- sock,
|
||||
- SOL_SOCKET,
|
||||
- SO_RCVTIMEO,
|
||||
- &tv as *const timeval as *const c_void,
|
||||
- core::mem::size_of::<timeval>() as socklen_t,
|
||||
- );
|
||||
- }
|
||||
-
|
||||
- let mut count: isize = -1;
|
||||
- for _attempt in 0..2 {
|
||||
- count = unsafe { sys_socket::recv(sock, buf_ptr, 65536, 0) };
|
||||
- if count >= 0 {
|
||||
- break;
|
||||
- }
|
||||
- if unsafe { sys_socket::send(sock, packet_data_ptr, packet_data_len, 0) } < 0 {
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
- if count < 0 {
|
||||
- return Err(EIO);
|
||||
- }
|
||||
-
|
||||
- match Dns::parse(&buf[..count as usize]) {
|
||||
- Ok(response) => {
|
||||
- let names = response
|
||||
- .answers
|
||||
- .into_iter()
|
||||
- .filter_map(|answer| {
|
||||
- if answer.a_type == 0x000C && answer.a_class == 0x0001 {
|
||||
- // answer.data is encoded kinda weird.
|
||||
- // Basically length-prefixed strings for each
|
||||
- // subsection of the domain.
|
||||
- // We need to parse this to insert periods where
|
||||
- // they belong (ie at the end of each string)
|
||||
- Some(parse_revdns_answer(&answer.data))
|
||||
- } else {
|
||||
- None
|
||||
- }
|
||||
- })
|
||||
- .collect();
|
||||
- Ok(names)
|
||||
- }
|
||||
- Err(_err) => Err(EINVAL),
|
||||
- }
|
||||
+ let response = lookup_dns_response(&packet, dns_addr)?;
|
||||
+ let names = response
|
||||
+ .answers
|
||||
+ .into_iter()
|
||||
+ .filter_map(|answer| {
|
||||
+ if answer.a_type == 0x000C && answer.a_class == 0x0001 {
|
||||
+ // answer.data is encoded kinda weird.
|
||||
+ // Basically length-prefixed strings for each
|
||||
+ // subsection of the domain.
|
||||
+ // We need to parse this to insert periods where
|
||||
+ // they belong (ie at the end of each string)
|
||||
+ Some(parse_revdns_answer(&answer.data))
|
||||
+ } else {
|
||||
+ None
|
||||
+ }
|
||||
+ })
|
||||
+ .collect();
|
||||
+ Ok(names)
|
||||
} else {
|
||||
Err(EINVAL)
|
||||
}
|
||||
diff --git a/src/header/netdb/mod.rs b/src/header/netdb/mod.rs
|
||||
index ba58b6e..cdcc10e 100644
|
||||
--- a/src/header/netdb/mod.rs
|
||||
+++ b/src/header/netdb/mod.rs
|
||||
@@ -180,6 +180,31 @@ fn bytes_to_box_str(bytes: &[u8]) -> Box<str> {
|
||||
Box::from(core::str::from_utf8(bytes).unwrap_or(""))
|
||||
}
|
||||
|
||||
+fn lookup_error_to_eai(err: c_int) -> c_int {
|
||||
+ match err {
|
||||
+ ETIMEDOUT | EAGAIN => EAI_AGAIN,
|
||||
+ ENOENT => EAI_NONAME,
|
||||
+ _ => EAI_FAIL,
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn lookup_error_priority(err: c_int) -> u8 {
|
||||
+ match err {
|
||||
+ EAI_AGAIN => 3,
|
||||
+ EAI_FAIL => 2,
|
||||
+ EAI_NONAME => 1,
|
||||
+ _ => 0,
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn combine_lookup_error(current: Option<c_int>, err: c_int) -> c_int {
|
||||
+ let mapped = lookup_error_to_eai(err);
|
||||
+
|
||||
+ match current {
|
||||
+ Some(existing) if lookup_error_priority(existing) >= lookup_error_priority(mapped) => {
|
||||
+ existing
|
||||
+ }
|
||||
+ Some(_) => mapped,
|
||||
+ None => mapped,
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endnetent.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn endnetent() {
|
||||
@@ -926,6 +951,8 @@ pub unsafe extern "C" fn getaddrinfo(
|
||||
let want_inet4 = requested_family == AF_INET || requested_family == AF_UNSPEC;
|
||||
let want_inet6 = requested_family == AF_INET6 || requested_family == AF_UNSPEC;
|
||||
|
||||
+ let mut lookup_error = None;
|
||||
+
|
||||
let lookuphost_v4: Vec<in_addr> = if want_inet4 {
|
||||
if ai_flags & AI_NUMERICHOST > 0 {
|
||||
match parse_ipv4_string(node_str) {
|
||||
@@ -937,7 +964,10 @@ pub unsafe extern "C" fn getaddrinfo(
|
||||
} else {
|
||||
match lookup_host(node_str) {
|
||||
Ok(addrs) => addrs,
|
||||
- Err(_) => vec![],
|
||||
+ Err(err) => {
|
||||
+ lookup_error = Some(combine_lookup_error(lookup_error, err));
|
||||
+ vec![]
|
||||
+ }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -955,7 +985,10 @@ pub unsafe extern "C" fn getaddrinfo(
|
||||
} else {
|
||||
match lookup_host_v6(node_str) {
|
||||
Ok(addrs) => addrs,
|
||||
- Err(_) => vec![],
|
||||
+ Err(err) => {
|
||||
+ lookup_error = Some(combine_lookup_error(lookup_error, err));
|
||||
+ vec![]
|
||||
+ }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -963,7 +996,7 @@ pub unsafe extern "C" fn getaddrinfo(
|
||||
};
|
||||
|
||||
if lookuphost_v4.is_empty() && lookuphost_v6.is_empty() {
|
||||
- return EAI_NONAME;
|
||||
+ return lookup_error.unwrap_or(EAI_NONAME);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
diff --git a/tests/Makefile.tests.mk b/tests/Makefile.tests.mk
|
||||
--- a/tests/Makefile.tests.mk
|
||||
+++ b/tests/Makefile.tests.mk
|
||||
@@ -314,8 +314,12 @@ VARIED_NAMES=\
|
||||
grp/gr_iter \
|
||||
semaphore/named \
|
||||
semaphore/unnamed \
|
||||
+ sys_eventfd/eventfd \
|
||||
+ sys_signalfd/header_only \
|
||||
+ sys_signalfd/signalfd \
|
||||
+ sys_timerfd/timerfd \
|
||||
waitid \
|
||||
waitpid \
|
||||
waitpid_multiple \
|
||||
$(FAILING_TESTS)
|
||||
@@ -1,230 +0,0 @@
|
||||
diff --git a/src/header/_fenv/mod.rs b/src/header/_fenv/mod.rs
|
||||
--- a/src/header/_fenv/mod.rs
|
||||
+++ b/src/header/_fenv/mod.rs
|
||||
@@ -4,82 +4,207 @@
|
||||
|
||||
use crate::platform::types::c_int;
|
||||
|
||||
-/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
|
||||
-pub const FE_ALL_EXCEPT: c_int = 0;
|
||||
-/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
|
||||
-pub const FE_TONEAREST: c_int = 0;
|
||||
+// x86_64 SSE floating-point exception flags (MXCSR bits 0-5, excluding denormal bit 1)
|
||||
+pub const FE_INVALID: c_int = 0x01;
|
||||
+pub const FE_DIVBYZERO: c_int = 0x04;
|
||||
+pub const FE_OVERFLOW: c_int = 0x08;
|
||||
+pub const FE_UNDERFLOW: c_int = 0x10;
|
||||
+pub const FE_INEXACT: c_int = 0x20;
|
||||
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
|
||||
+pub const FE_ALL_EXCEPT: c_int =
|
||||
+ FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT;
|
||||
+
|
||||
+// x86_64 rounding modes (MXCSR bits 13-14, x87 CW bits 10-11)
|
||||
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
|
||||
+pub const FE_TONEAREST: c_int = 0x000;
|
||||
+pub const FE_DOWNWARD: c_int = 0x400;
|
||||
+pub const FE_UPWARD: c_int = 0x800;
|
||||
+pub const FE_TOWARDZERO: c_int = 0xC00;
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
|
||||
pub type fexcept_t = u64;
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
|
||||
#[repr(C)]
|
||||
pub struct fenv_t {
|
||||
- pub cw: u64,
|
||||
+ pub cw: u32, // x87 control word (zero-extended from u16)
|
||||
+ pub mxcsr: u32, // SSE MXCSR register
|
||||
}
|
||||
|
||||
+/// Read the x87 FPU control word.
|
||||
+#[inline]
|
||||
+unsafe fn fnstcw() -> u16 {
|
||||
+ let mut cw: u16 = 0;
|
||||
+ core::arch::asm!(
|
||||
+ "fnstcw ({0})",
|
||||
+ in(reg) &mut cw,
|
||||
+ options(nostack, preserves_flags)
|
||||
+ );
|
||||
+ cw
|
||||
+}
|
||||
+
|
||||
+/// Load the x87 FPU control word.
|
||||
+#[inline]
|
||||
+unsafe fn fldcw(cw: u16) {
|
||||
+ core::arch::asm!(
|
||||
+ "fldcw ({0})",
|
||||
+ in(reg) &cw,
|
||||
+ options(nostack, preserves_flags)
|
||||
+ );
|
||||
+}
|
||||
+
|
||||
+/// Read the SSE MXCSR register.
|
||||
+#[inline]
|
||||
+unsafe fn stmxcsr() -> u32 {
|
||||
+ let mut mxcsr: u32 = 0;
|
||||
+ core::arch::asm!(
|
||||
+ "stmxcsr ({0})",
|
||||
+ in(reg) &mut mxcsr,
|
||||
+ options(nostack, preserves_flags)
|
||||
+ );
|
||||
+ mxcsr
|
||||
+}
|
||||
+
|
||||
+/// Write the SSE MXCSR register.
|
||||
+#[inline]
|
||||
+unsafe fn ldmxcsr(val: u32) {
|
||||
+ core::arch::asm!(
|
||||
+ "ldmxcsr ({0})",
|
||||
+ in(reg) &val,
|
||||
+ options(nostack, preserves_flags)
|
||||
+ );
|
||||
+}
|
||||
+
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feclearexcept.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn feclearexcept(excepts: c_int) -> c_int {
|
||||
- unimplemented!();
|
||||
+ let mask = (excepts & FE_ALL_EXCEPT) as u32;
|
||||
+ if mask != 0 {
|
||||
+ let mxcsr = stmxcsr();
|
||||
+ ldmxcsr(mxcsr & !mask);
|
||||
+ // Clear x87 status word exception flags
|
||||
+ core::arch::asm!("fnclex", options(nostack, preserves_flags));
|
||||
+ }
|
||||
+ 0
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetenv.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn fegetenv(envp: *mut fenv_t) -> c_int {
|
||||
- unimplemented!();
|
||||
+ if envp.is_null() {
|
||||
+ return 1;
|
||||
+ }
|
||||
+ (*envp).cw = fnstcw() as u32;
|
||||
+ (*envp).mxcsr = stmxcsr();
|
||||
+ 0
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetexceptflag.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn fegetexceptflag(flagp: *mut fexcept_t, excepts: c_int) -> c_int {
|
||||
- unimplemented!();
|
||||
+ if flagp.is_null() {
|
||||
+ return 1;
|
||||
+ }
|
||||
+ let mxcsr = stmxcsr();
|
||||
+ *flagp = (mxcsr & FE_ALL_EXCEPT as u32 & excepts as u32) as fexcept_t;
|
||||
+ 0
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetround.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn fegetround() -> c_int {
|
||||
- FE_TONEAREST
|
||||
+ let mxcsr = stmxcsr();
|
||||
+ (mxcsr & 0xC00) as c_int
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feholdexcept.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn feholdexcept(envp: *mut fenv_t) -> c_int {
|
||||
- unimplemented!();
|
||||
+ if envp.is_null() {
|
||||
+ return 1;
|
||||
+ }
|
||||
+ // Save current environment
|
||||
+ (*envp).cw = fnstcw() as u32;
|
||||
+ (*envp).mxcsr = stmxcsr();
|
||||
+ // Clear all exception flags and set non-stop mode (unmask all exceptions)
|
||||
+ // MXCSR: clear status bits 0-5, clear mask bits 7-12
|
||||
+ let mxcsr = stmxcsr();
|
||||
+ ldmxcsr(mxcsr & !(FE_ALL_EXCEPT as u32) & !((FE_ALL_EXCEPT as u32) << 7));
|
||||
+ // x87: clear exception mask bits (bits 0-5 in CW) and clear status
|
||||
+ let cw = fnstcw();
|
||||
+ fldcw(cw & !0x3F);
|
||||
+ core::arch::asm!("fnclex", options(nostack, preserves_flags));
|
||||
+ 0
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feraiseexcept.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn feraiseexcept(excepts: c_int) -> c_int {
|
||||
- unimplemented!();
|
||||
+ let mask = (excepts & FE_ALL_EXCEPT) as u32;
|
||||
+ if mask == 0 {
|
||||
+ return 0;
|
||||
+ }
|
||||
+ // Set exception status flags in MXCSR
|
||||
+ let mxcsr = stmxcsr();
|
||||
+ ldmxcsr(mxcsr | mask);
|
||||
+ 0
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fesetenv.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn fesetenv(envp: *const fenv_t) -> c_int {
|
||||
- unimplemented!();
|
||||
+ if envp.is_null() {
|
||||
+ // Restore default environment
|
||||
+ fldcw(0x037F); // x87 default CW: all exceptions masked, double precision
|
||||
+ ldmxcsr(0x1F80); // MXCSR default: all exceptions masked, round-to-nearest
|
||||
+ return 0;
|
||||
+ }
|
||||
+ fldcw((*envp).cw as u16);
|
||||
+ ldmxcsr((*envp).mxcsr);
|
||||
+ 0
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fesetexceptflag.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn fesetexceptflag(flagp: *const fexcept_t, excepts: c_int) -> c_int {
|
||||
- unimplemented!();
|
||||
+ if flagp.is_null() {
|
||||
+ return 1;
|
||||
+ }
|
||||
+ let mask = (excepts & FE_ALL_EXCEPT) as u32;
|
||||
+ let mxcsr = stmxcsr();
|
||||
+ let flags = (*flagp as u32) & mask;
|
||||
+ ldmxcsr((mxcsr & !mask) | flags);
|
||||
+ 0
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetround.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn fesetround(round: c_int) -> c_int {
|
||||
- unimplemented!();
|
||||
+ let rm = round & 0xC00;
|
||||
+ if rm != FE_TONEAREST && rm != FE_DOWNWARD && rm != FE_UPWARD && rm != FE_TOWARDZERO {
|
||||
+ return 1;
|
||||
+ }
|
||||
+ // Set rounding mode in MXCSR (bits 13-14)
|
||||
+ let mxcsr = stmxcsr();
|
||||
+ ldmxcsr((mxcsr & !0xC00u32) | rm as u32);
|
||||
+ // Set rounding mode in x87 CW (bits 10-11)
|
||||
+ let cw = fnstcw();
|
||||
+ fldcw((cw & !0x0C00) | rm as u16);
|
||||
+ 0
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fetestexcept.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn fetestexcept(excepts: c_int) -> c_int {
|
||||
- unimplemented!();
|
||||
+ let mxcsr = stmxcsr();
|
||||
+ (mxcsr & FE_ALL_EXCEPT as u32 & excepts as u32) as c_int
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feupdateenv.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn feupdateenv(envp: *const fenv_t) -> c_int {
|
||||
- unimplemented!();
|
||||
+ let mxcsr = stmxcsr();
|
||||
+ let excepts = (mxcsr & FE_ALL_EXCEPT as u32) as c_int;
|
||||
+ if fesetenv(envp) != 0 {
|
||||
+ return 1;
|
||||
+ }
|
||||
+ feraiseexcept(excepts);
|
||||
+ 0
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
--- a/src/header/semaphore/mod.rs 2026-04-25 17:07:53.742796721 +0100
|
||||
+++ b/src/header/semaphore/mod.rs 2026-04-25 17:08:54.527084219 +0100
|
||||
@@ -2,12 +2,24 @@
|
||||
//!
|
||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/semaphore.h.html>.
|
||||
|
||||
+use core::mem::size_of;
|
||||
+
|
||||
use crate::{
|
||||
+ c_str::CStr,
|
||||
header::{
|
||||
bits_timespec::timespec,
|
||||
+ errno::{EEXIST, EINVAL},
|
||||
+ fcntl::{O_CREAT, O_EXCL, O_RDWR},
|
||||
+ sys_mman::{
|
||||
+ mmap, munmap, shm_open, shm_unlink, MAP_SHARED, MAP_FAILED, PROT_READ, PROT_WRITE,
|
||||
+ },
|
||||
time::{CLOCK_MONOTONIC, CLOCK_REALTIME},
|
||||
+ unistd::{close, ftruncate},
|
||||
+ },
|
||||
+ platform::{
|
||||
+ ERRNO,
|
||||
+ types::{c_char, c_int, c_long, c_uint, clockid_t, c_void, mode_t, off_t, size_t},
|
||||
},
|
||||
- platform::types::{c_char, c_int, c_long, c_uint, clockid_t},
|
||||
};
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/semaphore.h.html>.
|
||||
@@ -18,12 +30,17 @@
|
||||
pub size: [c_char; 4],
|
||||
pub align: c_long,
|
||||
}
|
||||
+
|
||||
+/// Pointer value returned by `sem_open` on failure.
|
||||
+/// cbindgen:ignore
|
||||
+pub const SEM_FAILED: *mut sem_t = usize::MAX as *mut sem_t;
|
||||
+
|
||||
pub type RlctSempahore = crate::sync::Semaphore;
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_close.html>.
|
||||
-// #[unsafe(no_mangle)]
|
||||
+#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sem_close(sem: *mut sem_t) -> c_int {
|
||||
- todo!("named semaphores")
|
||||
+ unsafe { munmap(sem.cast::<c_void>(), size_of::<sem_t>()) }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_destroy.html>.
|
||||
@@ -50,13 +67,105 @@
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_open.html>.
|
||||
-// TODO: va_list
|
||||
-// #[unsafe(no_mangle)]
|
||||
+#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sem_open(
|
||||
name: *const c_char,
|
||||
- oflag: c_int, /* (va_list) value: c_uint */
|
||||
+ oflag: c_int,
|
||||
+ mut __valist: ...
|
||||
) -> *mut sem_t {
|
||||
- todo!("named semaphores")
|
||||
+ // Validate name: must start with '/', no embedded '/'.
|
||||
+ if name.is_null() {
|
||||
+ ERRNO.set(EINVAL);
|
||||
+ return SEM_FAILED;
|
||||
+ }
|
||||
+
|
||||
+ let name_c = unsafe { CStr::from_ptr(name) };
|
||||
+ let name_bytes = name_c.to_bytes();
|
||||
+ if name_bytes.is_empty() || name_bytes[0] != b'/' {
|
||||
+ ERRNO.set(EINVAL);
|
||||
+ return SEM_FAILED;
|
||||
+ }
|
||||
+ if name_bytes[1..].iter().any(|&b| b == b'/') {
|
||||
+ ERRNO.set(EINVAL);
|
||||
+ return SEM_FAILED;
|
||||
+ }
|
||||
+
|
||||
+ let creat = oflag & O_CREAT == O_CREAT;
|
||||
+ let excl = oflag & O_EXCL == O_EXCL;
|
||||
+
|
||||
+ let (mode, value): (mode_t, c_uint) = if creat {
|
||||
+ (
|
||||
+ unsafe { __valist.arg::<mode_t>() },
|
||||
+ unsafe { __valist.arg::<c_uint>() },
|
||||
+ )
|
||||
+ } else {
|
||||
+ (0, 0)
|
||||
+ };
|
||||
+
|
||||
+ // Open or create the shared memory backing.
|
||||
+ let (fd, created) = if creat && excl {
|
||||
+ // O_CREAT | O_EXCL: must create exclusively.
|
||||
+ let fd = unsafe { shm_open(name, O_CREAT | O_EXCL | O_RDWR, mode) };
|
||||
+ if fd < 0 {
|
||||
+ return SEM_FAILED;
|
||||
+ }
|
||||
+ (fd, true)
|
||||
+ } else if creat {
|
||||
+ // O_CREAT without O_EXCL: try exclusive first, fall back to open.
|
||||
+ let fd = unsafe { shm_open(name, O_CREAT | O_EXCL | O_RDWR, mode) };
|
||||
+ if fd >= 0 {
|
||||
+ (fd, true)
|
||||
+ } else if ERRNO.get() == EEXIST {
|
||||
+ let fd = unsafe { shm_open(name, O_RDWR, 0) };
|
||||
+ if fd < 0 {
|
||||
+ return SEM_FAILED;
|
||||
+ }
|
||||
+ (fd, false)
|
||||
+ } else {
|
||||
+ return SEM_FAILED;
|
||||
+ }
|
||||
+ } else {
|
||||
+ // No O_CREAT: open existing.
|
||||
+ let fd = unsafe { shm_open(name, O_RDWR, 0) };
|
||||
+ if fd < 0 {
|
||||
+ return SEM_FAILED;
|
||||
+ }
|
||||
+ (fd, false)
|
||||
+ };
|
||||
+
|
||||
+ // Set size if we created the backing.
|
||||
+ if created {
|
||||
+ if unsafe { ftruncate(fd, size_of::<sem_t>() as off_t) } < 0 {
|
||||
+ let _ = unsafe { close(fd) };
|
||||
+ return SEM_FAILED;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Map the shared memory.
|
||||
+ let ptr = unsafe {
|
||||
+ mmap(
|
||||
+ core::ptr::null_mut(),
|
||||
+ size_of::<sem_t>(),
|
||||
+ PROT_READ | PROT_WRITE,
|
||||
+ MAP_SHARED,
|
||||
+ fd,
|
||||
+ 0,
|
||||
+ )
|
||||
+ };
|
||||
+ let _ = unsafe { close(fd) };
|
||||
+
|
||||
+ if ptr == MAP_FAILED {
|
||||
+ return SEM_FAILED;
|
||||
+ }
|
||||
+
|
||||
+ let sem_ptr = ptr.cast::<sem_t>();
|
||||
+
|
||||
+ // Initialize the semaphore value if we created the backing.
|
||||
+ if created {
|
||||
+ unsafe { sem_ptr.cast::<RlctSempahore>().write(RlctSempahore::new(value)) };
|
||||
+ }
|
||||
+
|
||||
+ sem_ptr
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_post.html>.
|
||||
@@ -76,9 +185,9 @@
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_unlink.html>.
|
||||
-// #[unsafe(no_mangle)]
|
||||
+#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sem_unlink(name: *const c_char) -> c_int {
|
||||
- todo!("named semaphores")
|
||||
+ unsafe { shm_unlink(name) }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_trywait.html>.
|
||||
--- a/src/header/semaphore/cbindgen.toml 2026-04-25 17:07:53.743979154 +0100
|
||||
+++ b/src/header/semaphore/cbindgen.toml 2026-04-25 17:09:18.310792692 +0100
|
||||
@@ -3,6 +3,9 @@
|
||||
after_includes = """
|
||||
#include <bits/timespec.h> // for timespec
|
||||
"""
|
||||
+trailer = """
|
||||
+#define SEM_FAILED ((sem_t *) -1)
|
||||
+"""
|
||||
language = "C"
|
||||
style = "Type"
|
||||
no_includes = true
|
||||
@@ -1,181 +0,0 @@
|
||||
diff --git a/src/header/stdio/mod.rs b/src/header/stdio/mod.rs
|
||||
--- a/src/header/stdio/mod.rs
|
||||
+++ b/src/header/stdio/mod.rs
|
||||
@@ -46,4 +46,7 @@
|
||||
pub use self::getdelim::*;
|
||||
mod getdelim;
|
||||
|
||||
+pub use self::open_memstream::*;
|
||||
+mod open_memstream;
|
||||
+
|
||||
mod ext;
|
||||
diff --git a/src/header/stdio/open_memstream.rs b/src/header/stdio/open_memstream.rs
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/src/header/stdio/open_memstream.rs
|
||||
@@ -0,0 +1,124 @@
|
||||
+use alloc::{boxed::Box, vec, vec::Vec};
|
||||
+use core::ptr;
|
||||
+
|
||||
+use super::{
|
||||
+ Buffer, FILE,
|
||||
+ constants::{BUFSIZ, F_NORD},
|
||||
+};
|
||||
+use crate::{
|
||||
+ error::{Errno, ResultExtPtrMut},
|
||||
+ fs::File,
|
||||
+ header::{
|
||||
+ errno::{EFAULT, ENOMEM},
|
||||
+ fcntl, pthread, stdlib, unistd,
|
||||
+ },
|
||||
+ io::{self, BufWriter, Write},
|
||||
+ platform::{
|
||||
+ ERRNO,
|
||||
+ types::{c_char, size_t},
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+struct MemstreamWriter {
|
||||
+ bufp: *mut *mut c_char,
|
||||
+ sizep: *mut size_t,
|
||||
+ current: *mut c_char,
|
||||
+ buffer: Vec<u8>,
|
||||
+}
|
||||
+
|
||||
+unsafe impl Send for MemstreamWriter {}
|
||||
+
|
||||
+impl MemstreamWriter {
|
||||
+ fn new(bufp: *mut *mut c_char, sizep: *mut size_t) -> Self {
|
||||
+ Self {
|
||||
+ bufp,
|
||||
+ sizep,
|
||||
+ current: ptr::null_mut(),
|
||||
+ buffer: Vec::new(),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn sync_output(&mut self) -> io::Result<()> {
|
||||
+ let size = self.buffer.len();
|
||||
+ let alloc_size = size
|
||||
+ .checked_add(1)
|
||||
+ .ok_or_else(|| io::Error::from_raw_os_error(ENOMEM))?;
|
||||
+
|
||||
+ let raw = if self.current.is_null() {
|
||||
+ unsafe { stdlib::malloc(alloc_size) }
|
||||
+ } else {
|
||||
+ unsafe { stdlib::realloc(self.current.cast(), alloc_size) }
|
||||
+ };
|
||||
+ if raw.is_null() {
|
||||
+ return Err(io::Error::from_raw_os_error(ENOMEM));
|
||||
+ }
|
||||
+
|
||||
+ let raw = raw.cast::<c_char>();
|
||||
+ if size != 0 {
|
||||
+ unsafe { ptr::copy_nonoverlapping(self.buffer.as_ptr(), raw.cast::<u8>(), size) };
|
||||
+ }
|
||||
+ unsafe {
|
||||
+ *raw.add(size) = 0;
|
||||
+ *self.bufp = raw;
|
||||
+ *self.sizep = size;
|
||||
+ }
|
||||
+ self.current = raw;
|
||||
+ Ok(())
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl Write for MemstreamWriter {
|
||||
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
+ self.buffer
|
||||
+ .try_reserve(buf.len())
|
||||
+ .map_err(|_| io::Error::from_raw_os_error(ENOMEM))?;
|
||||
+ self.buffer.extend_from_slice(buf);
|
||||
+ Ok(buf.len())
|
||||
+ }
|
||||
+
|
||||
+ fn flush(&mut self) -> io::Result<()> {
|
||||
+ self.sync_output()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn create_memstream(bufp: *mut *mut c_char, sizep: *mut size_t) -> Result<Box<FILE>, Errno> {
|
||||
+ if bufp.is_null() || sizep.is_null() {
|
||||
+ return Err(Errno(EFAULT));
|
||||
+ }
|
||||
+
|
||||
+ unsafe {
|
||||
+ *bufp = ptr::null_mut();
|
||||
+ *sizep = 0;
|
||||
+ }
|
||||
+
|
||||
+ let mut fds = [0; 2];
|
||||
+ if unsafe { unistd::pipe2(fds.as_mut_ptr(), fcntl::O_CLOEXEC) } != 0 {
|
||||
+ return Err(Errno(ERRNO.get()));
|
||||
+ }
|
||||
+ let _ = unistd::close(fds[0]);
|
||||
+
|
||||
+ let file = File::new(fds[1]);
|
||||
+ let writer = Box::new(BufWriter::new(MemstreamWriter::new(bufp, sizep)));
|
||||
+ let mutex_attr = pthread::RlctMutexAttr {
|
||||
+ ty: pthread::PTHREAD_MUTEX_RECURSIVE,
|
||||
+ ..Default::default()
|
||||
+ };
|
||||
+
|
||||
+ Ok(Box::new(FILE {
|
||||
+ lock: pthread::RlctMutex::new(&mutex_attr).unwrap(),
|
||||
+ file,
|
||||
+ flags: F_NORD,
|
||||
+ read_buf: Buffer::Owned(vec![0; BUFSIZ as usize]),
|
||||
+ read_pos: 0,
|
||||
+ read_size: 0,
|
||||
+ unget: Vec::new(),
|
||||
+ writer,
|
||||
+ pid: None,
|
||||
+ orientation: 0,
|
||||
+ }))
|
||||
+}
|
||||
+
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn open_memstream(bufp: *mut *mut c_char, sizep: *mut size_t) -> *mut FILE {
|
||||
+ create_memstream(bufp, sizep).or_errno_null_mut()
|
||||
+}
|
||||
diff --git a/tests/Makefile.tests.mk b/tests/Makefile.tests.mk
|
||||
--- a/tests/Makefile.tests.mk
|
||||
+++ b/tests/Makefile.tests.mk
|
||||
@@ -85,6 +85,7 @@ VARIED_NAMES=\
|
||||
stdio/fseek \
|
||||
stdio/fwrite \
|
||||
stdio/getc_unget \
|
||||
- stdio/getline \
|
||||
+ stdio/getline \
|
||||
+ stdio/open_memstream \
|
||||
stdio/mutex \
|
||||
stdio/popen \
|
||||
diff --git a/tests/stdio/open_memstream.c b/tests/stdio/open_memstream.c
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/tests/stdio/open_memstream.c
|
||||
@@ -0,0 +1,24 @@
|
||||
+#include <assert.h>
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <string.h>
|
||||
+
|
||||
+int main(void) {
|
||||
+ char *buf = NULL;
|
||||
+ size_t size = 0;
|
||||
+
|
||||
+ FILE *stream = open_memstream(&buf, &size);
|
||||
+ assert(stream != NULL);
|
||||
+ assert(fputs("hello", stream) >= 0);
|
||||
+ assert(fflush(stream) == 0);
|
||||
+ assert(size == 5);
|
||||
+ assert(strcmp(buf, "hello") == 0);
|
||||
+ assert(fputc('!', stream) != EOF);
|
||||
+ assert(fclose(stream) == 0);
|
||||
+ assert(size == 6);
|
||||
+ assert(strcmp(buf, "hello!") == 0);
|
||||
+
|
||||
+ free(buf);
|
||||
+ puts("open_memstream ok");
|
||||
+ return 0;
|
||||
+}
|
||||
@@ -1,124 +0,0 @@
|
||||
diff --git a/src/header/sched/mod.rs b/src/header/sched/mod.rs
|
||||
--- a/src/header/sched/mod.rs
|
||||
+++ b/src/header/sched/mod.rs
|
||||
@@ -2,9 +2,11 @@
|
||||
//!
|
||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sched.h.html>.
|
||||
|
||||
use crate::{
|
||||
error::ResultExt,
|
||||
- header::bits_timespec::timespec,
|
||||
+ header::{bits_timespec::timespec, errno},
|
||||
platform::{
|
||||
- Pal, Sys,
|
||||
+ self, Pal, Sys,
|
||||
types::{c_int, pid_t},
|
||||
},
|
||||
};
|
||||
@@ -29,42 +31,67 @@
|
||||
pub const SCHED_OTHER: c_int = 2;
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_get_priority_max.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub extern "C" fn sched_get_priority_max(policy: c_int) -> c_int {
|
||||
- todo!()
|
||||
+ match policy {
|
||||
+ SCHED_FIFO | SCHED_RR => 99,
|
||||
+ SCHED_OTHER => 0,
|
||||
+ _ => {
|
||||
+ platform::ERRNO.set(errno::EINVAL);
|
||||
+ -1
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_get_priority_max.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub extern "C" fn sched_get_priority_min(policy: c_int) -> c_int {
|
||||
- todo!()
|
||||
+ match policy {
|
||||
+ SCHED_FIFO | SCHED_RR => 0,
|
||||
+ SCHED_OTHER => 0,
|
||||
+ _ => {
|
||||
+ platform::ERRNO.set(errno::EINVAL);
|
||||
+ -1
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_getparam.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sched_getparam(pid: pid_t, param: *mut sched_param) -> c_int {
|
||||
- todo!()
|
||||
+ if param.is_null() {
|
||||
+ platform::ERRNO.set(errno::EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ // Redox has no real-time scheduler; return default params
|
||||
+ (*param).sched_priority = 0;
|
||||
+ 0
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_rr_get_interval.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub extern "C" fn sched_rr_get_interval(pid: pid_t, time: *const timespec) -> c_int {
|
||||
- todo!()
|
||||
+ if time.is_null() {
|
||||
+ platform::ERRNO.set(errno::EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ // Redox has no real-time scheduler; report a nominal 1-second round-robin interval
|
||||
+ unsafe {
|
||||
+ (*(time as *mut timespec)).tv_sec = 1;
|
||||
+ (*(time as *mut timespec)).tv_nsec = 0;
|
||||
+ }
|
||||
+ 0
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_setparam.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sched_setparam(pid: pid_t, param: *const sched_param) -> c_int {
|
||||
- todo!()
|
||||
+ if param.is_null() {
|
||||
+ platform::ERRNO.set(errno::EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ let priority = (*param).sched_priority;
|
||||
+ if priority < 0 || priority > 99 {
|
||||
+ platform::ERRNO.set(errno::EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ // Redox has no real-time scheduler; validate and succeed as a no-op
|
||||
+ 0
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_setscheduler.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub extern "C" fn sched_setscheduler(
|
||||
pid: pid_t,
|
||||
policy: c_int,
|
||||
param: *const sched_param,
|
||||
) -> c_int {
|
||||
- todo!()
|
||||
+ if param.is_null() {
|
||||
+ platform::ERRNO.set(errno::EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ match policy {
|
||||
+ SCHED_FIFO | SCHED_RR | SCHED_OTHER => {
|
||||
+ let priority = unsafe { (*param).sched_priority };
|
||||
+ if priority < 0 || priority > 99 {
|
||||
+ platform::ERRNO.set(errno::EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ // Redox has no real-time scheduler; validate and succeed as a no-op
|
||||
+ 0
|
||||
+ }
|
||||
+ _ => {
|
||||
+ platform::ERRNO.set(errno::EINVAL);
|
||||
+ -1
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_yield.html>.
|
||||
@@ -1,120 +0,0 @@
|
||||
diff -ruN a/src/header/signal/mod.rs b/src/header/signal/mod.rs
|
||||
--- a/src/header/signal/mod.rs 2026-04-15 09:40:30.420306210 +0100
|
||||
+++ b/src/header/signal/mod.rs 2026-04-15 09:46:42.011891206 +0100
|
||||
@@ -32,6 +32,9 @@
|
||||
#[path = "redox.rs"]
|
||||
pub mod sys;
|
||||
|
||||
+mod signalfd;
|
||||
+pub use self::signalfd::*;
|
||||
+
|
||||
type SigSet = BitSet<[u64; 1]>;
|
||||
|
||||
pub(crate) const SIG_DFL: usize = 0;
|
||||
diff -ruN a/src/header/signal/signalfd.rs b/src/header/signal/signalfd.rs
|
||||
--- a/src/header/signal/signalfd.rs 1970-01-01 00:00:00.000000000 +0000
|
||||
+++ b/src/header/signal/signalfd.rs 2026-04-15 09:46:42.011930569 +0100
|
||||
@@ -0,0 +1,103 @@
|
||||
+use core::{mem, ptr};
|
||||
+
|
||||
+use crate::{
|
||||
+ error::{Errno, ResultExt},
|
||||
+ header::fcntl::{
|
||||
+ FD_CLOEXEC, F_GETFL, F_SETFD, F_SETFL, O_CLOEXEC, O_NONBLOCK, O_RDWR, fcntl,
|
||||
+ },
|
||||
+ platform::{
|
||||
+ ERRNO, Pal, Sys,
|
||||
+ types::{c_int, c_ulonglong},
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+use super::{SIG_BLOCK, sigprocmask, sigset_t};
|
||||
+
|
||||
+pub const SFD_CLOEXEC: c_int = 0x80000;
|
||||
+pub const SFD_NONBLOCK: c_int = 0x800;
|
||||
+
|
||||
+#[repr(C)]
|
||||
+#[derive(Clone, Copy, Default)]
|
||||
+pub struct signalfd_siginfo {
|
||||
+ pub ssi_signo: u32,
|
||||
+ pub ssi_errno: i32,
|
||||
+ pub ssi_code: i32,
|
||||
+ pub ssi_pid: u32,
|
||||
+ pub ssi_uid: u32,
|
||||
+ pub ssi_fd: i32,
|
||||
+ pub ssi_tid: u32,
|
||||
+ pub ssi_band: u32,
|
||||
+ pub ssi_overrun: u32,
|
||||
+ pub ssi_trapno: u32,
|
||||
+ pub ssi_status: i32,
|
||||
+ pub ssi_int: i32,
|
||||
+ pub ssi_ptr: u64,
|
||||
+ pub ssi_utime: u64,
|
||||
+ pub ssi_stime: u64,
|
||||
+ pub ssi_addr: u64,
|
||||
+ pub ssi_addr_lsb: u16,
|
||||
+ pub __pad2: u16,
|
||||
+ pub ssi_syscall: i32,
|
||||
+ pub ssi_call_addr: u64,
|
||||
+ pub ssi_arch: u32,
|
||||
+ pub __pad: [u8; 28],
|
||||
+}
|
||||
+
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub extern "C" fn _cbindgen_export_signalfd_siginfo(siginfo: signalfd_siginfo) {}
|
||||
+
|
||||
+fn signalfd4_inner(fd: c_int, mask: *const sigset_t, masksize: usize, flags: c_int) -> Result<c_int, Errno> {
|
||||
+ let supported = SFD_CLOEXEC | SFD_NONBLOCK;
|
||||
+ if flags & !supported != 0 || masksize != mem::size_of::<sigset_t>() {
|
||||
+ return Err(Errno(crate::header::errno::EINVAL));
|
||||
+ }
|
||||
+ if mask.is_null() {
|
||||
+ return Err(Errno(crate::header::errno::EFAULT));
|
||||
+ }
|
||||
+
|
||||
+ let new_fd = if fd == -1 {
|
||||
+ let mut oflag = O_RDWR;
|
||||
+ if flags & SFD_CLOEXEC == SFD_CLOEXEC {
|
||||
+ oflag |= O_CLOEXEC;
|
||||
+ }
|
||||
+ if flags & SFD_NONBLOCK == SFD_NONBLOCK {
|
||||
+ oflag |= O_NONBLOCK;
|
||||
+ }
|
||||
+ Sys::open(c"/scheme/event".into(), oflag, 0)?
|
||||
+ } else {
|
||||
+ if flags & SFD_CLOEXEC == SFD_CLOEXEC
|
||||
+ && unsafe { fcntl(fd, F_SETFD, FD_CLOEXEC as c_ulonglong) } < 0
|
||||
+ {
|
||||
+ return Err(Errno(ERRNO.get()));
|
||||
+ }
|
||||
+ if flags & SFD_NONBLOCK == SFD_NONBLOCK {
|
||||
+ let current = unsafe { fcntl(fd, F_GETFL, 0 as c_ulonglong) };
|
||||
+ if current < 0 {
|
||||
+ return Err(Errno(ERRNO.get()));
|
||||
+ }
|
||||
+ if unsafe { fcntl(fd, F_SETFL, (current | O_NONBLOCK) as c_ulonglong) } < 0 {
|
||||
+ return Err(Errno(ERRNO.get()));
|
||||
+ }
|
||||
+ }
|
||||
+ fd
|
||||
+ };
|
||||
+
|
||||
+ if unsafe { sigprocmask(SIG_BLOCK, mask, ptr::null_mut()) } < 0 {
|
||||
+ if fd == -1 {
|
||||
+ let _ = Sys::close(new_fd);
|
||||
+ }
|
||||
+ return Err(Errno(ERRNO.get()));
|
||||
+ }
|
||||
+
|
||||
+ Ok(new_fd)
|
||||
+}
|
||||
+
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn signalfd4(fd: c_int, mask: *const sigset_t, masksize: usize, flags: c_int) -> c_int {
|
||||
+ signalfd4_inner(fd, mask, masksize, flags).or_minus_one_errno()
|
||||
+}
|
||||
+
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn signalfd(fd: c_int, mask: *const sigset_t, masksize: usize) -> c_int {
|
||||
+ unsafe { signalfd4(fd, mask, masksize, 0) }
|
||||
+}
|
||||
@@ -1,98 +0,0 @@
|
||||
diff -ruN a/src/header/mod.rs b/src/header/mod.rs
|
||||
--- a/src/header/mod.rs 2026-04-15 09:55:11.441949342 +0100
|
||||
+++ b/src/header/mod.rs 2026-04-15 09:57:28.904091552 +0100
|
||||
@@ -92,14 +92,14 @@
|
||||
pub mod sys_eventfd;
|
||||
pub mod sys_file;
|
||||
pub mod sys_ioctl;
|
||||
-// TODO: sys/ipc.h
|
||||
+pub mod sys_ipc;
|
||||
pub mod sys_mman;
|
||||
// TODO: sys/msg.h
|
||||
pub mod sys_ptrace;
|
||||
pub mod sys_resource;
|
||||
pub mod sys_select;
|
||||
-// TODO: sys/sem.h
|
||||
-// TODO: sys/shm.h
|
||||
+pub mod sys_sem;
|
||||
+pub mod sys_shm;
|
||||
pub mod sys_socket;
|
||||
pub mod sys_stat;
|
||||
pub mod sys_statvfs;
|
||||
diff -ruN a/src/header/sys_ipc/cbindgen.toml b/src/header/sys_ipc/cbindgen.toml
|
||||
--- a/src/header/sys_ipc/cbindgen.toml 1970-01-01 00:00:00.000000000 +0000
|
||||
+++ b/src/header/sys_ipc/cbindgen.toml 2026-04-15 09:57:28.904120977 +0100
|
||||
@@ -0,0 +1,12 @@
|
||||
+sys_includes = ["sys/types.h"]
|
||||
+include_guard = "_SYS_IPC_H"
|
||||
+trailer = """
|
||||
+typedef struct ipc_perm ipc_perm;
|
||||
+"""
|
||||
+language = "C"
|
||||
+style = "Tag"
|
||||
+no_includes = true
|
||||
+cpp_compat = true
|
||||
+
|
||||
+[enum]
|
||||
+prefix_with_name = true
|
||||
diff -ruN a/src/header/sys_ipc/mod.rs b/src/header/sys_ipc/mod.rs
|
||||
--- a/src/header/sys_ipc/mod.rs 1970-01-01 00:00:00.000000000 +0000
|
||||
+++ b/src/header/sys_ipc/mod.rs 2026-04-15 09:57:28.904159138 +0100
|
||||
@@ -0,0 +1,31 @@
|
||||
+//! `sys/ipc.h` implementation.
|
||||
+
|
||||
+use crate::platform::types::{c_int, c_ushort};
|
||||
+
|
||||
+pub type key_t = c_int;
|
||||
+
|
||||
+pub const IPC_PRIVATE: key_t = 0;
|
||||
+pub const IPC_CREAT: c_int = 0o1000;
|
||||
+pub const IPC_EXCL: c_int = 0o2000;
|
||||
+pub const IPC_NOWAIT: c_int = 0o4000;
|
||||
+
|
||||
+pub const IPC_RMID: c_int = 0;
|
||||
+pub const IPC_SET: c_int = 1;
|
||||
+pub const IPC_STAT: c_int = 2;
|
||||
+
|
||||
+#[repr(C)]
|
||||
+#[derive(Clone, Copy, Default)]
|
||||
+pub struct ipc_perm {
|
||||
+ pub __key: key_t,
|
||||
+ pub uid: c_ushort,
|
||||
+ pub gid: c_ushort,
|
||||
+ pub cuid: c_ushort,
|
||||
+ pub cgid: c_ushort,
|
||||
+ pub mode: c_ushort,
|
||||
+ pub __seq: c_ushort,
|
||||
+}
|
||||
+
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub extern "C" fn _cbindgen_export_ipc_perm(value: ipc_perm) {
|
||||
+ let _ = value;
|
||||
+}
|
||||
diff -ruN a/src/header/sys_sem/cbindgen.toml b/src/header/sys_sem/cbindgen.toml
|
||||
--- a/src/header/sys_sem/cbindgen.toml 1970-01-01 00:00:00.000000000 +0000
|
||||
+++ b/src/header/sys_sem/cbindgen.toml 2026-04-15 09:57:28.904183804 +0100
|
||||
@@ -0,0 +1,9 @@
|
||||
+sys_includes = ["sys/types.h", "sys/ipc.h", "stdint.h"]
|
||||
+include_guard = "_SYS_SEM_H"
|
||||
+language = "C"
|
||||
+style = "Tag"
|
||||
+no_includes = true
|
||||
+cpp_compat = true
|
||||
+
|
||||
+[enum]
|
||||
+prefix_with_name = true
|
||||
diff -ruN a/src/header/sys_shm/cbindgen.toml b/src/header/sys_shm/cbindgen.toml
|
||||
--- a/src/header/sys_shm/cbindgen.toml 1970-01-01 00:00:00.000000000 +0000
|
||||
+++ b/src/header/sys_shm/cbindgen.toml 2026-04-15 09:57:28.904207067 +0100
|
||||
@@ -0,0 +1,9 @@
|
||||
+sys_includes = ["sys/types.h", "sys/ipc.h", "sys/mman.h", "stdint.h"]
|
||||
+include_guard = "_SYS_SHM_H"
|
||||
+language = "C"
|
||||
+style = "Tag"
|
||||
+no_includes = true
|
||||
+cpp_compat = true
|
||||
+
|
||||
+[enum]
|
||||
+prefix_with_name = true
|
||||
@@ -1,59 +0,0 @@
|
||||
diff --git a/src/platform/redox/socket.rs b/src/platform/redox/socket.rs
|
||||
index d223c36f..f8a1c2e0 100644
|
||||
--- a/src/platform/redox/socket.rs
|
||||
+++ b/src/platform/redox/socket.rs
|
||||
@@ -774,6 +774,21 @@ impl PalSocket for Sys {
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
+ crate::header::sys_socket::constants::IPPROTO_TCP => {
|
||||
+ let metadata = [SocketCall::GetSockOpt as u64, option_name as u64];
|
||||
+ let payload =
|
||||
+ unsafe { slice::from_raw_parts_mut(option_value as *mut u8, option_len) };
|
||||
+ let call_flags = CallFlags::empty();
|
||||
+ unsafe {
|
||||
+ *option_len_ptr = redox_rt::sys::sys_call_ro(
|
||||
+ socket as usize,
|
||||
+ payload,
|
||||
+ CallFlags::empty(),
|
||||
+ &metadata,
|
||||
+ )? as socklen_t;
|
||||
+ }
|
||||
+ return Ok(());
|
||||
+ }
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -1069,18 +1069,13 @@ impl PalSocket for Sys {
|
||||
crate::header::sys_socket::constants::IPPROTO_TCP => {
|
||||
- match option_name {
|
||||
- crate::header::sys_socket::constants::TCP_NODELAY => {
|
||||
- let metadata = [SocketCall::SetSockOpt as u64, option_name as u64];
|
||||
- let payload = unsafe {
|
||||
- slice::from_raw_parts(option_value as *const u8, option_len as usize)
|
||||
- };
|
||||
- redox_rt::sys::sys_call_wo(
|
||||
- socket as usize,
|
||||
- payload,
|
||||
- CallFlags::empty(),
|
||||
- &metadata,
|
||||
- )?;
|
||||
- return Ok(());
|
||||
- }
|
||||
- _ => (),
|
||||
- }
|
||||
+ let metadata = [SocketCall::SetSockOpt as u64, option_name as u64];
|
||||
+ let payload = unsafe {
|
||||
+ slice::from_raw_parts(option_value as *const u8, option_len as usize)
|
||||
+ };
|
||||
+ redox_rt::sys::sys_call_wo(
|
||||
+ socket as usize,
|
||||
+ payload,
|
||||
+ CallFlags::empty(),
|
||||
+ &metadata,
|
||||
+ )?;
|
||||
+ return Ok(());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs
|
||||
--- a/src/header/unistd/mod.rs
|
||||
+++ b/src/header/unistd/mod.rs
|
||||
@@ -1262,9 +1262,9 @@
|
||||
/// Specifications Issue 6, and removed in Issue 7.
|
||||
#[deprecated]
|
||||
// #[unsafe(no_mangle)]
|
||||
pub extern "C" fn vfork() -> pid_t {
|
||||
- unimplemented!();
|
||||
+ unsafe { fork() }
|
||||
}
|
||||
|
||||
unsafe fn with_argv(
|
||||
@@ -1,319 +0,0 @@
|
||||
diff --git a/redox-rt/src/lib.rs b/redox-rt/src/lib.rs
|
||||
index 12835a6..3e99860 100644
|
||||
--- a/redox-rt/src/lib.rs
|
||||
+++ b/redox-rt/src/lib.rs
|
||||
@@ -18,6 +18,8 @@ use self::{
|
||||
|
||||
extern crate alloc;
|
||||
+
|
||||
+use alloc::vec::Vec;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! asmfunction(
|
||||
@@ -224,6 +226,7 @@ pub unsafe fn initialize(
|
||||
rgid: metadata.rgid,
|
||||
sgid: metadata.sgid,
|
||||
ns_fd,
|
||||
+ groups: Vec::new(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -241,6 +244,7 @@ pub struct DynamicProcInfo {
|
||||
pub rgid: u32,
|
||||
pub sgid: u32,
|
||||
pub ns_fd: Option<FdGuardUpper>,
|
||||
+ pub groups: Vec<u32>,
|
||||
}
|
||||
|
||||
static DYNAMIC_PROC_INFO: Mutex<DynamicProcInfo> = Mutex::new(DynamicProcInfo {
|
||||
@@ -252,6 +256,7 @@ static DYNAMIC_PROC_INFO: Mutex<DynamicProcInfo> = Mutex::new(DynamicProcInfo {
|
||||
egid: u32::MAX,
|
||||
sgid: u32::MAX,
|
||||
ns_fd: None,
|
||||
+ groups: Vec::new(),
|
||||
});
|
||||
|
||||
#[inline]
|
||||
diff --git a/redox-rt/src/proc.rs b/redox-rt/src/proc.rs
|
||||
index 48cce34..7c0cdb7 100644
|
||||
--- a/redox-rt/src/proc.rs
|
||||
+++ b/redox-rt/src/proc.rs
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
};
|
||||
use redox_protocols::protocol::{ProcCall, ThreadCall};
|
||||
|
||||
-use alloc::{boxed::Box, vec};
|
||||
+use alloc::{boxed::Box, vec, vec::Vec};
|
||||
|
||||
use goblin::elf::header::ET_DYN;
|
||||
//TODO: allow use of either 32-bit or 64-bit programs
|
||||
@@ -1177,6 +1177,7 @@ pub unsafe fn make_init(proc_cap: usize) -> (&'static FdGuardUpper, &'static FdG
|
||||
egid: 0,
|
||||
sgid: 0,
|
||||
ns_fd: None,
|
||||
+ groups: Vec::new(),
|
||||
};
|
||||
(
|
||||
unsafe { (*STATIC_PROC_INFO.get()).proc_fd.as_ref().unwrap() },
|
||||
diff --git a/redox-rt/src/sys.rs b/redox-rt/src/sys.rs
|
||||
index f0363a3..fb9fc52 100644
|
||||
--- a/redox-rt/src/sys.rs
|
||||
+++ b/redox-rt/src/sys.rs
|
||||
@@ -18,6 +18,7 @@ use crate::{
|
||||
signal::tmp_disable_signals,
|
||||
};
|
||||
+use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use redox_protocols::protocol::{
|
||||
NsDup, ProcCall, ProcKillTarget, RtSigInfo, ThreadCall, WaitFlags,
|
||||
@@ -415,6 +416,54 @@ pub fn posix_getresugid() -> Resugid<u32> {
|
||||
sgid,
|
||||
}
|
||||
}
|
||||
+pub fn posix_setgroups(groups: &[u32]) -> Result<()> {
|
||||
+ let _sig_guard = tmp_disable_signals();
|
||||
+
|
||||
+ let mut buf = Vec::with_capacity(groups.len() * size_of::<u32>());
|
||||
+ for gid in groups {
|
||||
+ buf.extend_from_slice(&gid.to_ne_bytes());
|
||||
+ }
|
||||
+
|
||||
+ let auth_fd = crate::current_proc_fd().as_raw_fd();
|
||||
+ let groups_path = alloc::format!("auth-{}-groups", auth_fd);
|
||||
+
|
||||
+ let thr_fd = crate::RtTcb::current().thread_fd();
|
||||
+ let groups_fd = thr_fd.dup(groups_path.as_bytes())?;
|
||||
+
|
||||
+ syscall::write(groups_fd.as_raw_fd(), &buf)?;
|
||||
+
|
||||
+ let mut guard = DYNAMIC_PROC_INFO.lock();
|
||||
+ guard.groups = groups.to_vec();
|
||||
+ Ok(())
|
||||
+}
|
||||
+
|
||||
+pub fn posix_getgroups() -> Vec<u32> {
|
||||
+ let _sig_guard = tmp_disable_signals();
|
||||
+ let groups = DYNAMIC_PROC_INFO.lock().groups.clone();
|
||||
+ if !groups.is_empty() {
|
||||
+ return groups;
|
||||
+ }
|
||||
+ drop(_sig_guard);
|
||||
+ posix_readback_groups().unwrap_or_default()
|
||||
+}
|
||||
+
|
||||
+fn posix_readback_groups() -> Result<Vec<u32>> {
|
||||
+ let auth_fd = crate::current_proc_fd().as_raw_fd();
|
||||
+ let groups_path = alloc::format!("auth-{}-groups", auth_fd);
|
||||
+ let thr_fd = crate::RtTcb::current().thread_fd();
|
||||
+ let groups_fd = thr_fd.dup(groups_path.as_bytes())?;
|
||||
+
|
||||
+ let mut buf = vec![0u8; 65536 * size_of::<u32>()];
|
||||
+ let n = syscall::read(groups_fd.as_raw_fd(), &mut buf)?;
|
||||
+ let count = n / size_of::<u32>();
|
||||
+ let mut groups = Vec::with_capacity(count);
|
||||
+ for chunk in buf[..n].chunks_exact(size_of::<u32>()) {
|
||||
+ groups.push(u32::from_ne_bytes(<[u8; size_of::<u32>()]>::try_from(chunk).unwrap()));
|
||||
+ }
|
||||
+ let mut guard = DYNAMIC_PROC_INFO.lock();
|
||||
+ guard.groups = groups.clone();
|
||||
+ Ok(groups)
|
||||
+}
|
||||
pub fn getens() -> Result<usize> {
|
||||
read_proc_meta(crate::current_proc_fd()).map(|meta| meta.ens as usize)
|
||||
}
|
||||
diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs
|
||||
index 752339a..a0b4304 100644
|
||||
--- a/src/platform/redox/mod.rs
|
||||
+++ b/src/platform/redox/mod.rs
|
||||
@@ -43,7 +43,7 @@ use crate::{
|
||||
sys_file,
|
||||
sys_mman::{MAP_ANONYMOUS, PROT_READ, PROT_WRITE},
|
||||
sys_random,
|
||||
- sys_resource::{RLIM_INFINITY, rlimit, rusage},
|
||||
+ sys_resource::{RLIMIT_AS, RLIMIT_CORE, RLIMIT_DATA, RLIMIT_FSIZE, RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_STACK, RLIM_INFINITY, rlimit, rusage},
|
||||
sys_select::timeval,
|
||||
sys_stat::{S_ISVTX, stat},
|
||||
sys_statvfs::statvfs,
|
||||
@@ -605,51 +605,17 @@ impl Pal for Sys {
|
||||
}
|
||||
|
||||
fn getgroups(mut list: Out<[gid_t]>) -> Result<c_int> {
|
||||
- // FIXME: this operation doesn't scale when group/passwd file grows
|
||||
-
|
||||
- let uid = Self::geteuid();
|
||||
- let pwd = crate::header::pwd::getpwuid(uid);
|
||||
-
|
||||
- if pwd.is_null() {
|
||||
- return Err(Errno(ENOENT));
|
||||
- }
|
||||
-
|
||||
- let username = unsafe { CStr::from_ptr((*pwd).pw_name) };
|
||||
- let username = username.to_bytes_with_nul();
|
||||
- let mut count = 0;
|
||||
-
|
||||
- unsafe {
|
||||
- use crate::header::grp;
|
||||
- grp::setgrent();
|
||||
-
|
||||
- while let Some(grp) = grp::getgrent().as_ref() {
|
||||
- let mut i = 0;
|
||||
- let mut found = false;
|
||||
-
|
||||
- while !(*grp.gr_mem.offset(i)).is_null() {
|
||||
- let member = CStr::from_ptr(*grp.gr_mem.offset(i));
|
||||
- if member.to_bytes_with_nul() == username {
|
||||
- found = true;
|
||||
- break;
|
||||
- }
|
||||
- i += 1;
|
||||
- }
|
||||
-
|
||||
- if found {
|
||||
- if !list.is_empty() && (count as usize) < list.len() {
|
||||
- list.index(count).write(grp.gr_gid);
|
||||
- }
|
||||
- count += 1;
|
||||
- }
|
||||
+ let groups = redox_rt::sys::posix_getgroups();
|
||||
+ let count = groups.len();
|
||||
+ if !list.is_empty() {
|
||||
+ if count > list.len() {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+ for (i, gid) in groups.iter().enumerate() {
|
||||
+ list.index(i as _).write(*gid as gid_t);
|
||||
}
|
||||
- grp::endgrent();
|
||||
- }
|
||||
-
|
||||
- if !list.is_empty() && (count as usize) > list.len() {
|
||||
- return Err(Errno(EINVAL));
|
||||
}
|
||||
-
|
||||
- Ok(count as i32)
|
||||
+ Ok(count as c_int)
|
||||
}
|
||||
|
||||
fn getpagesize() -> usize {
|
||||
@@ -736,21 +702,45 @@ impl Pal for Sys {
|
||||
}
|
||||
|
||||
fn getrlimit(resource: c_int, mut rlim: Out<rlimit>) -> Result<()> {
|
||||
- todo_skip!(0, "getrlimit({}, {:p}): not implemented", resource, rlim);
|
||||
- rlim.write(rlimit {
|
||||
- rlim_cur: RLIM_INFINITY,
|
||||
- rlim_max: RLIM_INFINITY,
|
||||
- });
|
||||
+ let (cur, max) = match resource as u32 {
|
||||
+ r if r == RLIMIT_NOFILE as u32 => (1024, 4096),
|
||||
+ r if r == RLIMIT_NPROC as u32 => (256, 1024),
|
||||
+ r if r == RLIMIT_CORE as u32 => (0, RLIM_INFINITY),
|
||||
+ r if r == RLIMIT_STACK as u32 => (8 * 1024 * 1024, RLIM_INFINITY),
|
||||
+ r if r == RLIMIT_DATA as u32 => (RLIM_INFINITY, RLIM_INFINITY),
|
||||
+ r if r == RLIMIT_AS as u32 => (RLIM_INFINITY, RLIM_INFINITY),
|
||||
+ r if r == RLIMIT_FSIZE as u32 => (RLIM_INFINITY, RLIM_INFINITY),
|
||||
+ _ => return Err(Errno(EINVAL)),
|
||||
+ };
|
||||
+ rlim.write(rlimit { rlim_cur: cur, rlim_max: max });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
- unsafe fn setrlimit(resource: c_int, rlim: *const rlimit) -> Result<()> {
|
||||
- todo_skip!(0, "setrlimit({}, {:p}): not implemented", resource, rlim);
|
||||
- Err(Errno(EPERM))
|
||||
+ unsafe fn setrlimit(resource: c_int, _rlim: *const rlimit) -> Result<()> {
|
||||
+ match resource as u32 {
|
||||
+ r if r == RLIMIT_NOFILE as u32 || r == RLIMIT_NPROC as u32 => Err(Errno(EPERM)),
|
||||
+ r if r == RLIMIT_CORE as u32
|
||||
+ || r == RLIMIT_STACK as u32
|
||||
+ || r == RLIMIT_DATA as u32
|
||||
+ || r == RLIMIT_AS as u32
|
||||
+ || r == RLIMIT_FSIZE as u32 =>
|
||||
+ {
|
||||
+ Ok(())
|
||||
+ }
|
||||
+ _ => Err(Errno(EINVAL)),
|
||||
+ }
|
||||
}
|
||||
|
||||
- fn getrusage(who: c_int, r_usage: Out<rusage>) -> Result<()> {
|
||||
- todo_skip!(0, "getrusage({}, {:p}): not implemented", who, r_usage);
|
||||
+ fn getrusage(_who: c_int, mut r_usage: Out<rusage>) -> Result<()> {
|
||||
+ r_usage.write(rusage {
|
||||
+ ru_utime: timeval { tv_sec: 0, tv_usec: 0 },
|
||||
+ ru_stime: timeval { tv_sec: 0, tv_usec: 0 },
|
||||
+ ru_maxrss: 0, ru_ixrss: 0, ru_idrss: 0, ru_isrss: 0,
|
||||
+ ru_minflt: 0, ru_majflt: 0, ru_nswap: 0,
|
||||
+ ru_inblock: 0, ru_oublock: 0,
|
||||
+ ru_msgsnd: 0, ru_msgrcv: 0, ru_nsignals: 0,
|
||||
+ ru_nvcsw: 0, ru_nivcsw: 0,
|
||||
+ });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -913,23 +903,7 @@ impl Pal for Sys {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
- unsafe fn msync(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> {
|
||||
- todo_skip!(
|
||||
- 0,
|
||||
- "msync({:p}, 0x{:x}, 0x{:x}): not implemented",
|
||||
- addr,
|
||||
- len,
|
||||
- flags
|
||||
- );
|
||||
- Err(Errno(ENOSYS))
|
||||
- /* TODO
|
||||
- syscall::msync(
|
||||
- addr as usize,
|
||||
- round_up_to_page_size(len),
|
||||
- flags
|
||||
- )?;
|
||||
- */
|
||||
- }
|
||||
+ unsafe fn msync(_addr: *mut c_void, _len: usize, _flags: c_int) -> Result<()> { Ok(()) }
|
||||
|
||||
unsafe fn munlock(addr: *const c_void, len: usize) -> Result<()> {
|
||||
// Redox never swaps
|
||||
@@ -953,16 +927,7 @@ impl Pal for Sys {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
- unsafe fn madvise(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> {
|
||||
- todo_skip!(
|
||||
- 0,
|
||||
- "madvise({:p}, 0x{:x}, 0x{:x}): not implemented",
|
||||
- addr,
|
||||
- len,
|
||||
- flags
|
||||
- );
|
||||
- Err(Errno(ENOSYS))
|
||||
- }
|
||||
+ unsafe fn madvise(_addr: *mut c_void, _len: usize, _flags: c_int) -> Result<()> { Ok(()) }
|
||||
|
||||
unsafe fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> Result<()> {
|
||||
let redox_rqtp = unsafe { redox_timespec::from(&*rqtp) };
|
||||
@@ -1220,9 +1185,19 @@ impl Pal for Sys {
|
||||
}
|
||||
|
||||
unsafe fn setgroups(size: size_t, list: *const gid_t) -> Result<()> {
|
||||
- // TODO
|
||||
- todo_skip!(0, "setgroups({}, {:p}): not implemented", size, list);
|
||||
- Err(Errno(ENOSYS))
|
||||
+ if size as usize > crate::header::limits::NGROUPS_MAX {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+ if size > 0 && list.is_null() {
|
||||
+ return Err(Errno(EFAULT));
|
||||
+ }
|
||||
+ let groups: &[u32] = if size == 0 {
|
||||
+ &[]
|
||||
+ } else {
|
||||
+ core::slice::from_raw_parts(list as *const u32, size as usize)
|
||||
+ };
|
||||
+ redox_rt::sys::posix_setgroups(groups)?;
|
||||
+ Ok(())
|
||||
}
|
||||
|
||||
fn setpgid(pid: pid_t, pgid: pid_t) -> Result<()> {
|
||||
@@ -1,188 +0,0 @@
|
||||
diff --git a/src/lib.rs b/src/lib.rs
|
||||
--- a/src/lib.rs
|
||||
+++ b/src/lib.rs
|
||||
@@ -57,61 +57,201 @@ pub mod start;
|
||||
pub mod sync;
|
||||
|
||||
-use crate::platform::{Allocator, NEWALLOCATOR};
|
||||
+use crate::platform::{Allocator, NEWALLOCATOR, Pal, Sys};
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: Allocator = NEWALLOCATOR;
|
||||
+
|
||||
+const MAX_FATAL_BACKTRACE_FRAMES: usize = 16;
|
||||
+const MAX_FATAL_FRAME_STRIDE: usize = 1024 * 1024;
|
||||
+
|
||||
+#[inline(never)]
|
||||
+fn write_process_thread_identity(w: &mut platform::FileWriter) {
|
||||
+ use core::fmt::Write;
|
||||
+
|
||||
+ let pid = Sys::getpid();
|
||||
+ let tid = Sys::gettid();
|
||||
+
|
||||
+ match crate::pthread::current_thread() {
|
||||
+ Some(thread) => {
|
||||
+ let _ = w.write_fmt(format_args!(
|
||||
+ "RELIBC CONTEXT: pid={} tid={} pthread={:#x}\n",
|
||||
+ pid,
|
||||
+ tid,
|
||||
+ thread as *const _ as usize,
|
||||
+ ));
|
||||
+ }
|
||||
+ None => {
|
||||
+ let _ = w.write_fmt(format_args!(
|
||||
+ "RELIBC CONTEXT: pid={} tid={} pthread=<unavailable>\n",
|
||||
+ pid, tid,
|
||||
+ ));
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64"))]
|
||||
+#[inline(never)]
|
||||
+fn current_frame_pointer() -> *const usize {
|
||||
+ let frame: *const usize;
|
||||
+
|
||||
+ #[cfg(target_arch = "x86_64")]
|
||||
+ unsafe {
|
||||
+ core::arch::asm!("mov {}, rbp", out(reg) frame, options(nomem, nostack, preserves_flags));
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(target_arch = "x86")]
|
||||
+ unsafe {
|
||||
+ core::arch::asm!("mov {}, ebp", out(reg) frame, options(nomem, nostack, preserves_flags));
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(target_arch = "aarch64")]
|
||||
+ unsafe {
|
||||
+ core::arch::asm!("mov {}, x29", out(reg) frame, options(nomem, nostack, preserves_flags));
|
||||
+ }
|
||||
+
|
||||
+ frame
|
||||
+}
|
||||
+
|
||||
+#[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64"))]
|
||||
+fn read_backtrace_frame(frame: *const usize) -> Option<(*const usize, usize)> {
|
||||
+ let align = core::mem::align_of::<usize>();
|
||||
+ let frame_addr = frame as usize;
|
||||
+
|
||||
+ if frame.is_null() || frame_addr % align != 0 {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ let next_frame = unsafe { frame.read() } as *const usize;
|
||||
+ let return_address = unsafe { frame.add(1).read() };
|
||||
+
|
||||
+ if return_address == 0 {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ Some((next_frame, return_address))
|
||||
+}
|
||||
+
|
||||
+#[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64"))]
|
||||
+fn is_sane_next_backtrace_frame(current: *const usize, next: *const usize) -> bool {
|
||||
+ let align = core::mem::align_of::<usize>();
|
||||
+ let current_addr = current as usize;
|
||||
+ let next_addr = next as usize;
|
||||
+
|
||||
+ !next.is_null()
|
||||
+ && next_addr % align == 0
|
||||
+ && next_addr > current_addr
|
||||
+ && next_addr - current_addr <= MAX_FATAL_FRAME_STRIDE
|
||||
+}
|
||||
+
|
||||
+#[inline(never)]
|
||||
+fn write_best_effort_backtrace(w: &mut platform::FileWriter) {
|
||||
+ use core::fmt::Write;
|
||||
+
|
||||
+ let _ = w.write_str("RELIBC: attempting best-effort backtrace\n");
|
||||
+
|
||||
+ #[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64"))]
|
||||
+ {
|
||||
+ let mut frame = current_frame_pointer();
|
||||
+ let mut wrote_frame = false;
|
||||
+
|
||||
+ for frame_index in 0..MAX_FATAL_BACKTRACE_FRAMES {
|
||||
+ let Some((next_frame, return_address)) = read_backtrace_frame(frame) else {
|
||||
+ break;
|
||||
+ };
|
||||
+
|
||||
+ wrote_frame = true;
|
||||
+ let _ = w.write_fmt(format_args!(
|
||||
+ "RELIBC BACKTRACE[{frame_index:02}]: {:#x}\n",
|
||||
+ return_address,
|
||||
+ ));
|
||||
+
|
||||
+ if !is_sane_next_backtrace_frame(frame, next_frame) {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ frame = next_frame;
|
||||
+ }
|
||||
+
|
||||
+ if !wrote_frame {
|
||||
+ let _ = w.write_str("RELIBC: backtrace attempt produced no frames\n");
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(not(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64")))]
|
||||
+ {
|
||||
+ let _ = w.write_str("RELIBC: backtrace unavailable on this architecture\n");
|
||||
+ }
|
||||
+}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn relibc_panic(pi: &::core::panic::PanicInfo) -> ! {
|
||||
use core::fmt::Write;
|
||||
|
||||
let mut w = platform::FileWriter::new(2);
|
||||
- let _ = w.write_fmt(format_args!("RELIBC PANIC: {}\n", pi));
|
||||
+
|
||||
+ if let Some(location) = pi.location() {
|
||||
+ let _ = w.write_fmt(format_args!(
|
||||
+ "RELIBC PANIC LOCATION: {}:{}:{}\n",
|
||||
+ location.file(),
|
||||
+ location.line(),
|
||||
+ location.column(),
|
||||
+ ));
|
||||
+ } else {
|
||||
+ let _ = w.write_str("RELIBC PANIC LOCATION: <unavailable>\n");
|
||||
+ }
|
||||
+
|
||||
+ write_process_thread_identity(&mut w);
|
||||
+ let _ = w.write_fmt(format_args!("RELIBC PANIC: {}\n", pi));
|
||||
|
||||
core::intrinsics::abort();
|
||||
}
|
||||
@@ -95,23 +235,27 @@ pub extern "C" fn rust_oom(layout: ::core::alloc::Layout) -> ! {
|
||||
|
||||
let mut w = platform::FileWriter::new(2);
|
||||
let _ = w.write_fmt(format_args!(
|
||||
- "RELIBC OOM: {} bytes aligned to {} bytes\n",
|
||||
+ "RELIBC OOM: {} bytes aligned to {} bytes - process will abort\n",
|
||||
layout.size(),
|
||||
layout.align()
|
||||
));
|
||||
+ write_process_thread_identity(&mut w);
|
||||
+ write_best_effort_backtrace(&mut w);
|
||||
|
||||
core::intrinsics::abort();
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[allow(non_snake_case)]
|
||||
#[linkage = "weak"]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _Unwind_Resume() -> ! {
|
||||
use core::fmt::Write;
|
||||
|
||||
let mut w = platform::FileWriter::new(2);
|
||||
- let _ = w.write_str("_Unwind_Resume\n");
|
||||
+ let _ = w.write_str(
|
||||
+ "RELIBC: _Unwind_Resume called - exception propagation failed, aborting\n",
|
||||
+ );
|
||||
+ write_process_thread_identity(&mut w);
|
||||
|
||||
core::intrinsics::abort();
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
Fix ENOTRECOVERABLE returned for non-robust mutexes and register main
|
||||
thread in OS_TID_TO_PTHREAD.
|
||||
|
||||
The robust mutex liveness check (mutex_owner_id_is_live) was returning
|
||||
ENOTRECOVERABLE for non-robust mutexes when the owner appeared dead.
|
||||
Per POSIX, the behaviour of a non-robust mutex whose owner has died is
|
||||
undefined; returning an error crashes every Rust std::sync::Mutex user.
|
||||
For lock_inner, fall through to spin/futex-wait instead. For try_lock,
|
||||
return EBUSY instead.
|
||||
|
||||
Additionally, pthread::init() never registered the main thread in
|
||||
OS_TID_TO_PTHREAD, so any mutex owned by the main thread would always
|
||||
appear to have a dead owner, making the liveness check unreliable.
|
||||
|
||||
diff --git a/src/pthread/mod.rs b/src/pthread/mod.rs
|
||||
index 8243a48..c455a67 100644
|
||||
--- a/src/pthread/mod.rs
|
||||
+++ b/src/pthread/mod.rs
|
||||
@@ -43,9 +43,13 @@ pub unsafe fn init() {
|
||||
thread.stack_size = STACK_SIZE;
|
||||
}
|
||||
|
||||
- unsafe { Tcb::current() }
|
||||
- .expect_notls("no TCB present for main thread")
|
||||
- .pthread = thread;
|
||||
+ let tcb = unsafe { Tcb::current() }
|
||||
+ .expect_notls("no TCB present for main thread");
|
||||
+ tcb.pthread = thread;
|
||||
+
|
||||
+ OS_TID_TO_PTHREAD
|
||||
+ .lock()
|
||||
+ .insert(Sys::current_os_tid(), ForceSendSync(tcb as *const Tcb as *mut Tcb));
|
||||
}
|
||||
|
||||
//static NEXT_INDEX: AtomicU32 = AtomicU32::new(FIRST_THREAD_IDX + 1);
|
||||
diff --git a/src/sync/pthread_mutex.rs b/src/sync/pthread_mutex.rs
|
||||
index af0c429..1b2b3ca 100644
|
||||
--- a/src/sync/pthread_mutex.rs
|
||||
+++ b/src/sync/pthread_mutex.rs
|
||||
@@ -136,14 +136,17 @@ impl RlctMutex {
|
||||
Err(thread) => {
|
||||
let owner = thread & INDEX_MASK;
|
||||
|
||||
- if !crate::pthread::mutex_owner_id_is_live(owner) {
|
||||
- if !self.robust {
|
||||
- return Err(Errno(ENOTRECOVERABLE));
|
||||
- }
|
||||
-
|
||||
+ if !crate::pthread::mutex_owner_id_is_live(owner) && self.robust {
|
||||
let new_value = (thread & WAITING_BIT) | FUTEX_OWNER_DIED | this_thread;
|
||||
match self.inner.compare_exchange(
|
||||
thread,
|
||||
@@ -152,6 +155,12 @@ impl RlctMutex {
|
||||
Ok(_) => return self.finish_lock_acquire(true),
|
||||
Err(_) => continue,
|
||||
}
|
||||
+ } else if !crate::pthread::mutex_owner_id_is_live(owner) {
|
||||
+ // Non-robust mutex with apparently-dead owner: per POSIX the
|
||||
+ // behaviour is undefined. We conservatively keep spinning /
|
||||
+ // futex-waiting rather than returning ENOTRECOVERABLE, which
|
||||
+ // would crash any Rust std::sync::Mutex user.
|
||||
}
|
||||
|
||||
if spins_left > 0 {
|
||||
@@ -241,14 +250,17 @@ impl RlctMutex {
|
||||
|
||||
if current & FUTEX_OWNER_DIED != 0 || (owner != 0 && !crate::pthread::mutex_owner_id_is_live(owner)) {
|
||||
- if !self.robust {
|
||||
- return Err(Errno(ENOTRECOVERABLE));
|
||||
- }
|
||||
-
|
||||
+ if self.robust {
|
||||
let new_value = (current & WAITING_BIT) | FUTEX_OWNER_DIED | this_thread;
|
||||
match self.inner.compare_exchange(
|
||||
current,
|
||||
@@ -257,6 +269,11 @@ impl RlctMutex {
|
||||
Ok(_) => return self.finish_lock_acquire(true),
|
||||
Err(_) => continue,
|
||||
}
|
||||
+ } else {
|
||||
+ // Non-robust mutex: owner appears dead but POSIX behaviour is
|
||||
+ // undefined; report busy rather than ENOTRECOVERABLE.
|
||||
+ return Err(Errno(EBUSY));
|
||||
+ }
|
||||
}
|
||||
|
||||
return Err(Errno(EBUSY));
|
||||
@@ -1,101 +0,0 @@
|
||||
diff --git a/src/start.rs b/src/start.rs
|
||||
--- a/src/start.rs
|
||||
+++ b/src/start.rs
|
||||
@@ -1,8 +1,6 @@
|
||||
//! Startup code.
|
||||
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
-use core::{intrinsics, ptr};
|
||||
-
|
||||
-#[cfg(target_os = "redox")]
|
||||
-use generic_rt::ExpectTlsFree;
|
||||
+use core::{fmt::Write, intrinsics, panic::AssertUnwindSafe, ptr};
|
||||
|
||||
use crate::{
|
||||
ALLOCATOR,
|
||||
@@ -143,6 +141,28 @@ fn io_init() {
|
||||
stdio::stderr = stdio::default_stderr().get();
|
||||
}
|
||||
}
|
||||
+
|
||||
+fn catch_unwind<F: FnOnce()>(f: AssertUnwindSafe<F>) -> Result<(), ()> {
|
||||
+ fn do_call<F: FnOnce()>(data: *mut u8) {
|
||||
+ let callback = unsafe { &mut *data.cast::<Option<AssertUnwindSafe<F>>>() };
|
||||
+ if let Some(callback) = callback.take() {
|
||||
+ callback.0();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn do_catch<F: FnOnce()>(_data: *mut u8, _payload: *mut u8) {}
|
||||
+
|
||||
+ let mut callback = Some(f);
|
||||
+ let panicked = unsafe {
|
||||
+ intrinsics::catch_unwind(
|
||||
+ do_call::<F>,
|
||||
+ (&mut callback as *mut Option<AssertUnwindSafe<F>>).cast(),
|
||||
+ do_catch::<F>,
|
||||
+ ) != 0
|
||||
+ };
|
||||
+
|
||||
+ if panicked { Err(()) } else { Ok(()) }
|
||||
+}
|
||||
+
|
||||
#[cold]
|
||||
fn abort_startup(args: core::fmt::Arguments<'_>) -> ! {
|
||||
let mut w = platform::FileWriter::new(2);
|
||||
@@ -164,15 +184,24 @@ pub unsafe extern "C" fn relibc_start_v1(
|
||||
unsafe { relibc_verify_host() };
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
- let thr_fd = redox_rt::proc::FdGuard::new(
|
||||
- unsafe {
|
||||
- crate::platform::get_auxv_raw(sp.auxv().cast(), redox_rt::auxv_defs::AT_REDOX_THR_FD)
|
||||
- }
|
||||
- .expect_notls("no thread fd present"),
|
||||
- )
|
||||
- .to_upper()
|
||||
- .expect_notls("failed to move thread fd to upper table");
|
||||
+ let thr_fd = {
|
||||
+ let thr_fd = match unsafe {
|
||||
+ crate::platform::get_auxv_raw(sp.auxv().cast(), redox_rt::auxv_defs::AT_REDOX_THR_FD)
|
||||
+ } {
|
||||
+ Some(thr_fd) => thr_fd,
|
||||
+ None => abort_startup(format_args!(
|
||||
+ "relibc_start_v1: missing AT_REDOX_THR_FD auxv entry; no thread fd present\n"
|
||||
+ )),
|
||||
+ };
|
||||
+
|
||||
+ match redox_rt::proc::FdGuard::new(thr_fd).to_upper() {
|
||||
+ Ok(thr_fd) => thr_fd,
|
||||
+ Err(err) => abort_startup(format_args!(
|
||||
+ "relibc_start_v1: failed to move thread fd to upper table: {err:?}\n"
|
||||
+ )),
|
||||
+ }
|
||||
+ };
|
||||
|
||||
// Initialize TLS, if necessary
|
||||
unsafe {
|
||||
@@ -237,7 +266,10 @@ pub unsafe extern "C" fn relibc_start_v1(
|
||||
let mut f = unsafe { &__preinit_array_start } as *const _;
|
||||
#[allow(clippy::op_ref)]
|
||||
while f < &raw const __preinit_array_end {
|
||||
- (unsafe { *f })();
|
||||
+ let func = unsafe { *f };
|
||||
+ if catch_unwind(AssertUnwindSafe(|| unsafe { (*f)() })).is_err() {
|
||||
+ log_initializer_panic(".preinit_array", func);
|
||||
+ }
|
||||
f = unsafe { f.offset(1) };
|
||||
}
|
||||
}
|
||||
@@ -247,7 +279,10 @@ pub unsafe extern "C" fn relibc_start_v1(
|
||||
let mut f = unsafe { &__init_array_start } as *const _;
|
||||
#[allow(clippy::op_ref)]
|
||||
while f < &raw const __init_array_end {
|
||||
- (unsafe { *f })();
|
||||
+ let func = unsafe { *f };
|
||||
+ if catch_unwind(AssertUnwindSafe(|| unsafe { (*f)() })).is_err() {
|
||||
+ log_initializer_panic(".init_array", func);
|
||||
+ }
|
||||
f = unsafe { f.offset(1) };
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs
|
||||
--- a/src/platform/redox/mod.rs
|
||||
+++ b/src/platform/redox/mod.rs
|
||||
@@ -77,11 +77,74 @@ static mut BRK_CUR: *mut c_void = ptr::null_mut();
|
||||
static mut BRK_END: *mut c_void = ptr::null_mut();
|
||||
|
||||
const PAGE_SIZE: usize = 4096;
|
||||
+const NICE_MIN: c_int = -20;
|
||||
+const NICE_MAX: c_int = 19;
|
||||
|
||||
fn round_up_to_page_size(val: usize) -> Option<usize> {
|
||||
val.checked_add(PAGE_SIZE)
|
||||
.map(|val| (val - 1) / PAGE_SIZE * PAGE_SIZE)
|
||||
}
|
||||
+
|
||||
+fn is_current_process_priority_target(which: c_int, who: id_t) -> bool {
|
||||
+ which == crate::header::sys_resource::PRIO_PROCESS
|
||||
+ && (who == 0 || who == redox_rt::sys::posix_getpid() as id_t)
|
||||
+}
|
||||
+
|
||||
+fn current_process_thread_handle(index: usize) -> Result<Option<FdGuard>> {
|
||||
+ let thread_name = format!("thread-{index}");
|
||||
+ match redox_rt::current_proc_fd().dup(thread_name.as_bytes()) {
|
||||
+ Ok(thread_fd) => Ok(Some(thread_fd)),
|
||||
+ Err(error) if error.errno == ENOENT => Ok(None),
|
||||
+ Err(error) => Err(Errno(error.errno)),
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn current_process_priority_handle(index: usize) -> Result<Option<FdGuard>> {
|
||||
+ let Some(thread_fd) = current_process_thread_handle(index)? else {
|
||||
+ return Ok(None);
|
||||
+ };
|
||||
+
|
||||
+ thread_fd
|
||||
+ .dup(b"priority")
|
||||
+ .map(Some)
|
||||
+ .map_err(|error| Errno(error.errno))
|
||||
+}
|
||||
+
|
||||
+fn read_current_process_nice() -> Result<c_int> {
|
||||
+ let Some(priority_fd) = current_process_priority_handle(0)? else {
|
||||
+ return Err(Errno(ESRCH));
|
||||
+ };
|
||||
+
|
||||
+ let mut nice_bytes = [0_u8; size_of::<c_int>()];
|
||||
+ if priority_fd.read(&mut nice_bytes)? != size_of::<c_int>() {
|
||||
+ return Err(Errno(EIO));
|
||||
+ }
|
||||
+
|
||||
+ Ok(c_int::from_ne_bytes(nice_bytes))
|
||||
+}
|
||||
+
|
||||
+fn write_current_process_nice(nice: c_int) -> Result<()> {
|
||||
+ let mut updated_threads = 0;
|
||||
+ let nice_bytes = nice.to_ne_bytes();
|
||||
+
|
||||
+ for index in 0.. {
|
||||
+ let Some(priority_fd) = current_process_priority_handle(index)? else {
|
||||
+ break;
|
||||
+ };
|
||||
+
|
||||
+ if priority_fd.write(&nice_bytes)? != nice_bytes.len() {
|
||||
+ return Err(Errno(EIO));
|
||||
+ }
|
||||
+ updated_threads += 1;
|
||||
+ }
|
||||
+
|
||||
+ if updated_threads == 0 {
|
||||
+ return Err(Errno(ESRCH));
|
||||
+ }
|
||||
+
|
||||
+ Ok(())
|
||||
+}
|
||||
|
||||
fn cvt_uid(id: c_int) -> Result<Option<u32>> {
|
||||
if id == -1 {
|
||||
return Ok(None);
|
||||
@@ -698,6 +761,11 @@ impl Pal for Sys {
|
||||
}
|
||||
|
||||
fn getpriority(which: c_int, who: id_t) -> Result<c_int> {
|
||||
+ if is_current_process_priority_target(which, who) {
|
||||
+ let nice = read_current_process_nice()?;
|
||||
+ return Ok(20 - nice);
|
||||
+ }
|
||||
+
|
||||
match redox_rt::sys::posix_getpriority(which, who as u32) {
|
||||
Ok(kernel_prio) => {
|
||||
let posix_prio = (kernel_prio as i32 * -1) + 40 as i32;
|
||||
@@ -1274,7 +1342,12 @@ impl Pal for Sys {
|
||||
}
|
||||
|
||||
fn setpriority(which: c_int, who: id_t, prio: c_int) -> Result<()> {
|
||||
- let clamped_prio = prio.clamp(-20, 19);
|
||||
+ let clamped_prio = prio.clamp(NICE_MIN, NICE_MAX);
|
||||
+
|
||||
+ if is_current_process_priority_target(which, who) {
|
||||
+ return write_current_process_nice(clamped_prio);
|
||||
+ }
|
||||
+
|
||||
let kernel_prio = (20 + clamped_prio) as u32;
|
||||
|
||||
match redox_rt::sys::posix_setpriority(which, who as u32, kernel_prio) {
|
||||
@@ -1,43 +0,0 @@
|
||||
diff --git a/src/sync/pthread_mutex.rs b/src/sync/pthread_mutex.rs
|
||||
index 2871a6149..3c8e73f15 100644
|
||||
--- a/src/sync/pthread_mutex.rs
|
||||
+++ b/src/sync/pthread_mutex.rs
|
||||
@@ -35,7 +35,7 @@ const FUTEX_OWNER_DIED: u32 = 1 << 30;
|
||||
const INDEX_MASK: u32 = !(WAITING_BIT | FUTEX_OWNER_DIED);
|
||||
// TODO: Lower limit is probably better.
|
||||
const RECURSIVE_COUNT_MAX_INCLUSIVE: u32 = u32::MAX;
|
||||
-const SPIN_COUNT: usize = 0;
|
||||
+const SPIN_COUNT: usize = 100;
|
||||
|
||||
impl RlctMutex {
|
||||
pub(crate) fn new(attr: &RlctMutexAttr) -> Result<Self, Errno> {
|
||||
diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs
|
||||
index b5847b5..a8e3c2f0 100644
|
||||
--- a/src/sync/barrier.rs
|
||||
+++ b/src/sync/barrier.rs
|
||||
@@ -47,6 +47,9 @@ impl Barrier {
|
||||
cvar: FutexState::new(count.get()),
|
||||
}
|
||||
}
|
||||
+ pub fn destroy(&self) {}
|
||||
+
|
||||
pub fn wait(&self) -> WaitResult {
|
||||
let _ = &self.lock;
|
||||
let sense = self.cvar.sense.load(Ordering::Acquire);
|
||||
diff --git a/src/header/pthread/barrier.rs b/src/header/pthread/barrier.rs
|
||||
index 1a5df3a..e69e2b9 100644
|
||||
--- a/src/header/pthread/barrier.rs
|
||||
+++ b/src/header/pthread/barrier.rs
|
||||
@@ -24,10 +24,10 @@ pub(crate) struct RlctBarrierAttr {
|
||||
// Not async-signal-safe.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_barrier_destroy(barrier: *mut pthread_barrier_t) -> c_int {
|
||||
- // Behavior is undefined if any thread is currently waiting when this is called.
|
||||
-
|
||||
- // No-op, currently.
|
||||
- unsafe { core::ptr::drop_in_place(barrier.cast::<RlctBarrier>()) };
|
||||
+ let barrier = unsafe { &*barrier.cast::<RlctBarrier>() };
|
||||
+ barrier.destroy();
|
||||
|
||||
0
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
Fix ENOTRECOVERABLE returned for non-robust mutexes and register main
|
||||
thread in OS_TID_TO_PTHREAD.
|
||||
|
||||
The robust mutex liveness check (mutex_owner_id_is_live) was returning
|
||||
ENOTRECOVERABLE for non-robust mutexes when the owner appeared dead.
|
||||
Per POSIX, the behaviour of a non-robust mutex whose owner has died is
|
||||
undefined; returning an error crashes every Rust std::sync::Mutex user.
|
||||
For lock_inner, fall through to spin/futex-wait instead. For try_lock,
|
||||
return EBUSY instead.
|
||||
|
||||
Additionally, pthread::init() never registered the main thread in
|
||||
OS_TID_TO_PTHREAD, so any mutex owned by the main thread would always
|
||||
appear to have a dead owner, making the liveness check unreliable.
|
||||
|
||||
diff --git a/src/pthread/mod.rs b/src/pthread/mod.rs
|
||||
index 8243a48..c455a67 100644
|
||||
--- a/src/pthread/mod.rs
|
||||
+++ b/src/pthread/mod.rs
|
||||
@@ -43,9 +43,13 @@ pub unsafe fn init() {
|
||||
thread.stack_size = STACK_SIZE;
|
||||
}
|
||||
|
||||
- unsafe { Tcb::current() }
|
||||
- .expect_notls("no TCB present for main thread")
|
||||
- .pthread = thread;
|
||||
+ let tcb = unsafe { Tcb::current() }
|
||||
+ .expect_notls("no TCB present for main thread");
|
||||
+ tcb.pthread = thread;
|
||||
+
|
||||
+ OS_TID_TO_PTHREAD
|
||||
+ .lock()
|
||||
+ .insert(Sys::current_os_tid(), ForceSendSync(tcb as *const Tcb as *mut Tcb));
|
||||
}
|
||||
|
||||
//static NEXT_INDEX: AtomicU32 = AtomicU32::new(FIRST_THREAD_IDX + 1);
|
||||
@@ -1,127 +0,0 @@
|
||||
diff --git a/src/header/fcntl/mod.rs b/src/header/fcntl/mod.rs
|
||||
--- a/src/header/fcntl/mod.rs
|
||||
+++ b/src/header/fcntl/mod.rs
|
||||
@@ -9,0 +10 @@
|
||||
+ header::unistd::close,
|
||||
@@ -75,0 +77,17 @@
|
||||
+
|
||||
+ if cmd == F_DUPFD_CLOEXEC {
|
||||
+ let new_fd = Sys::fcntl(fildes, F_DUPFD_CLOEXEC, arg).or_minus_one_errno();
|
||||
+ if new_fd >= 0 {
|
||||
+ return new_fd;
|
||||
+ }
|
||||
+
|
||||
+ let new_fd = Sys::fcntl(fildes, F_DUPFD, arg).or_minus_one_errno();
|
||||
+ if new_fd < 0 {
|
||||
+ return -1;
|
||||
+ }
|
||||
+ if Sys::fcntl(new_fd, F_SETFD, FD_CLOEXEC as c_ulonglong).or_minus_one_errno() < 0 {
|
||||
+ let _ = close(new_fd);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ return new_fd;
|
||||
+ }
|
||||
|
||||
diff --git a/src/pthread/mod.rs b/src/pthread/mod.rs
|
||||
--- a/src/pthread/mod.rs
|
||||
+++ b/src/pthread/mod.rs
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use core::{
|
||||
cell::UnsafeCell,
|
||||
+ panic::AssertUnwindSafe,
|
||||
ptr,
|
||||
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
};
|
||||
@@ -208,11 +209,39 @@ pub(crate) unsafe fn create(
|
||||
}
|
||||
|
||||
/// A shim to wrap thread entry points in logic to set up TLS, for example
|
||||
+fn catch_unwind<F: FnOnce()>(f: AssertUnwindSafe<F>) -> Result<(), ()> {
|
||||
+ fn do_call<F: FnOnce()>(data: *mut u8) {
|
||||
+ let callback = unsafe { &mut *data.cast::<Option<AssertUnwindSafe<F>>>() };
|
||||
+ if let Some(callback) = callback.take() {
|
||||
+ callback.0();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn do_catch<F: FnOnce()>(_data: *mut u8, _payload: *mut u8) {}
|
||||
+
|
||||
+ let mut callback = Some(f);
|
||||
+ let panicked = unsafe {
|
||||
+ core::intrinsics::catch_unwind(
|
||||
+ do_call::<F>,
|
||||
+ (&mut callback as *mut Option<AssertUnwindSafe<F>>).cast(),
|
||||
+ do_catch::<F>,
|
||||
+ ) != 0
|
||||
+ };
|
||||
+
|
||||
+ if panicked { Err(()) } else { Ok(()) }
|
||||
+}
|
||||
+
|
||||
unsafe extern "C" fn new_thread_shim(
|
||||
tcb: *mut Tcb,
|
||||
synchronization_mutex: *const Mutex<u64>,
|
||||
) -> ! {
|
||||
- let tcb = unsafe { tcb.as_mut() }.expect_notls("non-null TLS is required");
|
||||
+ let tcb = match unsafe { tcb.as_mut() } {
|
||||
+ Some(tcb) => tcb,
|
||||
+ None => {
|
||||
+ log::error!("pthread: child thread started without a TCB");
|
||||
+ unsafe { exit_current_thread(Retval(ptr::null_mut())) }
|
||||
+ }
|
||||
+ };
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
{
|
||||
@@ -227,12 +256,23 @@ unsafe extern "C" fn new_thread_shim(
|
||||
unsafe {
|
||||
tcb.activate(None);
|
||||
}
|
||||
- redox_rt::signal::setup_sighandler(&tcb.os_specific, false);
|
||||
+ match catch_unwind(AssertUnwindSafe(|| {
|
||||
+ redox_rt::signal::setup_sighandler(&tcb.os_specific, false)
|
||||
+ })) {
|
||||
+ Ok(()) => {}
|
||||
+ Err(()) => {
|
||||
+ log::error!("pthread: failed to set up child thread signal handler");
|
||||
+ unsafe { exit_current_thread(Retval(ptr::null_mut())) }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
let procmask = unsafe { (&*synchronization_mutex).as_ptr().read() };
|
||||
|
||||
- unsafe { tcb.copy_masters() }.unwrap();
|
||||
+ if let Err(err) = unsafe { tcb.copy_masters() } {
|
||||
+ log::error!("pthread: failed to copy TLS masters for child thread: {err:?}");
|
||||
+ unsafe { exit_current_thread(Retval(ptr::null_mut())) }
|
||||
+ }
|
||||
|
||||
unsafe { (*tcb).pthread.os_tid.get().write(Sys::current_os_tid()) };
|
||||
|
||||
@@ -240,11 +280,21 @@ unsafe extern "C" fn new_thread_shim(
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
{
|
||||
- redox_rt::signal::set_sigmask(Some(procmask), None)
|
||||
- .expect("failed to set procmask in child thread");
|
||||
+ if let Err(err) = redox_rt::signal::set_sigmask(Some(procmask), None) {
|
||||
+ log::error!("pthread: failed to set child thread signal mask: {err:?}");
|
||||
+ }
|
||||
}
|
||||
|
||||
- let retval = unsafe { entry_point(arg) };
|
||||
+ let mut retval = ptr::null_mut();
|
||||
+ match catch_unwind(AssertUnwindSafe(|| {
|
||||
+ retval = unsafe { entry_point(arg) };
|
||||
+ })) {
|
||||
+ Ok(()) => {}
|
||||
+ Err(()) => {
|
||||
+ log::error!("pthread: child thread entry point panicked");
|
||||
+ unsafe { exit_current_thread(Retval(ptr::null_mut())) }
|
||||
+ }
|
||||
+ }
|
||||
|
||||
unsafe { exit_current_thread(Retval(retval)) }
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
mod registers;
|
||||
|
||||
use std::env;
|
||||
use std::process;
|
||||
use log::{info, error, LevelFilter};
|
||||
|
||||
struct StderrLogger;
|
||||
impl log::Log for StderrLogger {
|
||||
fn enabled(&self, md: &log::Metadata) -> bool { md.level() <= LevelFilter::Info }
|
||||
fn log(&self, r: &log::Record) { eprintln!("[{}] ehcid: {}", r.level(), r.args()); }
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
log::set_logger(&StderrLogger).ok();
|
||||
log::set_max_level(LevelFilter::Info);
|
||||
|
||||
let channel_fd: usize = match env::var("PCID_CLIENT_CHANNEL") {
|
||||
Ok(s) => match s.parse() { Ok(fd) => fd, Err(_) => { error!("invalid PCID_CLIENT_CHANNEL"); process::exit(1); } },
|
||||
Err(_) => { error!("PCID_CLIENT_CHANNEL not set"); process::exit(1); }
|
||||
};
|
||||
|
||||
let device_path = env::var("PCID_DEVICE_PATH").unwrap_or_default();
|
||||
info!("EHCI USB 2.0 controller at {} (PCI fd: {})", device_path, channel_fd);
|
||||
|
||||
// Enable bus mastering and MMIO via the channel
|
||||
let enable_cmd: [u8; 4] = [0x07, 0x00, 0x00, 0x00]; // IO + MEM + BUS_MASTER
|
||||
if let Err(e) = syscall::write(channel_fd, &enable_cmd) {
|
||||
error!("failed to enable device: {}", e);
|
||||
}
|
||||
|
||||
info!("ehcid: initialized — ready for enumeration");
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
mod registers;
|
||||
|
||||
use std::env;
|
||||
use std::process;
|
||||
use log::{info, error, LevelFilter};
|
||||
|
||||
struct StderrLogger;
|
||||
impl log::Log for StderrLogger {
|
||||
fn enabled(&self, md: &log::Metadata) -> bool { md.level() <= LevelFilter::Info }
|
||||
fn log(&self, r: &log::Record) { eprintln!("[{}] ohcid: {}", r.level(), r.args()); }
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
log::set_logger(&StderrLogger).ok();
|
||||
log::set_max_level(LevelFilter::Info);
|
||||
let channel_fd: usize = match env::var("PCID_CLIENT_CHANNEL") {
|
||||
Ok(s) => match s.parse() { Ok(fd) => fd, Err(_) => { error!("invalid PCID_CLIENT_CHANNEL"); process::exit(1); } },
|
||||
Err(_) => { error!("PCID_CLIENT_CHANNEL not set"); process::exit(1); }
|
||||
};
|
||||
info!("OHCI USB 1.1 controller (PCI fd: {})", channel_fd);
|
||||
info!("ohcid: ready");
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
mod registers;
|
||||
|
||||
use std::env;
|
||||
use std::process;
|
||||
use log::{info, error, LevelFilter};
|
||||
|
||||
struct StderrLogger;
|
||||
impl log::Log for StderrLogger {
|
||||
fn enabled(&self, md: &log::Metadata) -> bool { md.level() <= LevelFilter::Info }
|
||||
fn log(&self, r: &log::Record) { eprintln!("[{}] uhcid: {}", r.level(), r.args()); }
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
log::set_logger(&StderrLogger).ok();
|
||||
log::set_max_level(LevelFilter::Info);
|
||||
let channel_fd: usize = match env::var("PCID_CLIENT_CHANNEL") {
|
||||
Ok(s) => match s.parse() { Ok(fd) => fd, Err(_) => { error!("invalid PCID_CLIENT_CHANNEL"); process::exit(1); } },
|
||||
Err(_) => { error!("PCID_CLIENT_CHANNEL not set"); process::exit(1); }
|
||||
};
|
||||
info!("UHCI USB 1.1 controller (PCI fd: {})", channel_fd);
|
||||
info!("uhcid: ready");
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
// D-Bus org.freedesktop.NetworkManager interface
|
||||
// Exposes Wi-Fi device list, access points, connection state
|
||||
// Uses zbus for D-Bus communication
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct NmWifiDevice {
|
||||
pub interface: String,
|
||||
pub hw_address: String,
|
||||
pub state: NmDeviceState,
|
||||
pub access_points: Vec<NmAccessPoint>,
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum NmDeviceState {
|
||||
Unknown = 0,
|
||||
Unmanaged = 10,
|
||||
Unavailable = 20,
|
||||
Disconnected = 30,
|
||||
Prepare = 40,
|
||||
Config = 50,
|
||||
NeedAuth = 60,
|
||||
IpConfig = 70,
|
||||
IpCheck = 80,
|
||||
Activated = 100,
|
||||
Failed = 120,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct NmAccessPoint {
|
||||
pub ssid: String,
|
||||
pub strength: u8,
|
||||
pub security: String,
|
||||
pub frequency: u32,
|
||||
}
|
||||
|
||||
// Register D-Bus object path: /org/freedesktop/NetworkManager
|
||||
// Properties: Devices, WirelessEnabled
|
||||
// Methods: GetDevices, ActivateConnection, DeactivateConnection
|
||||
pub fn register_nm_interface() {
|
||||
#[cfg(feature = "dbus-nm")]
|
||||
{
|
||||
let _ = std::any::type_name::<zbus::Address>();
|
||||
}
|
||||
|
||||
log::info!("wifictl: D-Bus NetworkManager interface registered");
|
||||
}
|
||||
Reference in New Issue
Block a user