migrate: apply remaining patches to source forks, clean up
Apply all active patches from archived patches to source forks: - kernel: 33 individual patches + consolidated mega-patch (34 total commits) - relibc: 33 individual patches (34 total commits) - base: 26 individual patches + redox.patch mega-patch (28 total commits) - bootloader: 4 patches (5 total commits) - installer: 1 patch (2 total commits) - redoxfs: 2 patches (3 total commits) - userutils: 2 patches (3 total commits) Cleanup: - Remove all .rej and .orig files from fork repos - Delete all absorbed/ subdirectories (71 already-folded patches) - Delete stale .patch files from recipe directories (~130 upstream port patches) - 282 remaining .patch files are for non-fork components (archived, not in build system)
This commit is contained in:
-55
@@ -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,7 +61,9 @@ 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.
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs
|
||||
--- a/daemon/src/lib.rs
|
||||
+++ b/daemon/src/lib.rs
|
||||
@@ -10,26 +10,35 @@ use libredox::Fd;
|
||||
use redox_scheme::scheme::{SchemeAsync, SchemeSync};
|
||||
use redox_scheme::Socket;
|
||||
|
||||
-unsafe fn get_fd(var: &str) -> RawFd {
|
||||
- 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,
|
||||
+unsafe fn get_fd(var: &str) -> Option<RawFd> {
|
||||
+ let value = match std::env::var(var) {
|
||||
+ Ok(value) => value,
|
||||
+ Err(_) => {
|
||||
+ let exe = std::env::args()
|
||||
+ .next()
|
||||
+ .unwrap_or_else(|| "daemon".to_string());
|
||||
+ eprintln!("daemon: {var} not set for {exe}; readiness notification disabled");
|
||||
+ return None;
|
||||
+ }
|
||||
+ };
|
||||
+ let fd: RawFd = match value.parse() {
|
||||
+ Ok(fd) => fd,
|
||||
+ Err(err) => {
|
||||
+ let exe = std::env::args()
|
||||
+ .next()
|
||||
+ .unwrap_or_else(|| "daemon".to_string());
|
||||
+ eprintln!("daemon: invalid {var} value {value:?} for {exe}: {err}; readiness notification disabled");
|
||||
+ return None;
|
||||
+ }
|
||||
};
|
||||
if unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC) } == -1 {
|
||||
eprintln!(
|
||||
"daemon: failed to set CLOEXEC flag for {var} fd: {}",
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
- return -1;
|
||||
+ return None;
|
||||
}
|
||||
- fd
|
||||
+ Some(fd)
|
||||
}
|
||||
|
||||
unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) {
|
||||
@@ -47,22 +56,20 @@ unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) {
|
||||
/// A long running background process that handles requests.
|
||||
#[must_use = "Daemon::ready must be called"]
|
||||
pub struct Daemon {
|
||||
- write_pipe: PipeWriter,
|
||||
+ write_pipe: Option<PipeWriter>,
|
||||
}
|
||||
|
||||
impl Daemon {
|
||||
/// Create a new daemon.
|
||||
pub fn new(f: impl FnOnce(Daemon) -> !) -> ! {
|
||||
- let write_pipe = unsafe { io::PipeWriter::from_raw_fd(get_fd("INIT_NOTIFY")) };
|
||||
+ let write_pipe = unsafe { get_fd("INIT_NOTIFY").map(io::PipeWriter::from_raw_fd) };
|
||||
|
||||
f(Daemon { write_pipe })
|
||||
}
|
||||
|
||||
/// Notify the process that the daemon is ready to accept requests.
|
||||
pub fn ready(mut self) {
|
||||
- if let Err(err) = self.write_pipe.write_all(&[0]) {
|
||||
- if err.kind() != io::ErrorKind::BrokenPipe {
|
||||
- eprintln!("daemon::ready write failed: {err}");
|
||||
- }
|
||||
+ if let Some(write_pipe) = self.write_pipe.as_mut() {
|
||||
+ write_pipe.write_all(&[0]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,24 +94,26 @@ impl Daemon {
|
||||
/// A long running background process that handles requests using schemes.
|
||||
#[must_use = "SchemeDaemon::ready must be called"]
|
||||
pub struct SchemeDaemon {
|
||||
- write_pipe: PipeWriter,
|
||||
+ write_pipe: Option<PipeWriter>,
|
||||
}
|
||||
|
||||
impl SchemeDaemon {
|
||||
/// Create a new daemon for use with schemes.
|
||||
pub fn new(f: impl FnOnce(SchemeDaemon) -> !) -> ! {
|
||||
- let write_pipe = unsafe { io::PipeWriter::from_raw_fd(get_fd("INIT_NOTIFY")) };
|
||||
+ let write_pipe = unsafe { get_fd("INIT_NOTIFY").map(io::PipeWriter::from_raw_fd) };
|
||||
|
||||
f(SchemeDaemon { write_pipe })
|
||||
}
|
||||
|
||||
/// Notify the process that the scheme daemon is ready to accept requests.
|
||||
pub fn ready_with_fd(self, cap_fd: Fd) -> syscall::Result<()> {
|
||||
- syscall::call_wo(
|
||||
- self.write_pipe.as_raw_fd() as usize,
|
||||
- &cap_fd.into_raw().to_ne_bytes(),
|
||||
- syscall::CallFlags::FD,
|
||||
- &[],
|
||||
- )?;
|
||||
+ if let Some(write_pipe) = self.write_pipe {
|
||||
+ syscall::call_wo(
|
||||
+ write_pipe.as_raw_fd() as usize,
|
||||
+ &cap_fd.into_raw().to_ne_bytes(),
|
||||
+ syscall::CallFlags::FD,
|
||||
+ &[],
|
||||
+ )?;
|
||||
+ }
|
||||
Ok(())
|
||||
}
|
||||
-522
@@ -1,522 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -51,31 +62,40 @@ 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]) {
|
||||
+ if err.kind() != io::ErrorKind::BrokenPipe {
|
||||
+ eprintln!("daemon::ready write failed: {err}");
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
/// Executes `Command` as a child process.
|
||||
// FIXME remove once the service spawning of hwd and pcid-spawner is moved to init
|
||||
#[deprecated]
|
||||
- pub fn spawn(mut cmd: Command) {
|
||||
- let (mut read_pipe, write_pipe) = io::pipe().unwrap();
|
||||
+ pub fn spawn(mut cmd: Command) -> io::Result<()> {
|
||||
+ let (mut read_pipe, write_pipe) = io::pipe().map_err(|err| {
|
||||
+ io::Error::new(err.kind(), format!("daemon: failed to create readiness pipe: {err}"))
|
||||
+ })?;
|
||||
|
||||
unsafe { pass_fd(&mut cmd, "INIT_NOTIFY", write_pipe.into()) };
|
||||
|
||||
- if let Err(err) = cmd.spawn() {
|
||||
- eprintln!("daemon: failed to execute {cmd:?}: {err}");
|
||||
- return;
|
||||
- }
|
||||
+ cmd.spawn().map_err(|err| {
|
||||
+ io::Error::new(err.kind(), format!("failed to execute {cmd:?}: {err}"))
|
||||
+ })?;
|
||||
|
||||
let mut data = [0];
|
||||
match read_pipe.read_exact(&mut data) {
|
||||
- Ok(()) => {}
|
||||
+ Ok(()) => Ok(()),
|
||||
Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => {
|
||||
- eprintln!("daemon: {cmd:?} exited without notifying readiness");
|
||||
- }
|
||||
- Err(err) => {
|
||||
- eprintln!("daemon: failed to wait for {cmd:?}: {err}");
|
||||
+ Err(io::Error::new(
|
||||
+ io::ErrorKind::UnexpectedEof,
|
||||
+ format!("{cmd:?} exited without notifying readiness"),
|
||||
+ ))
|
||||
}
|
||||
+ Err(err) => Err(io::Error::new(
|
||||
+ err.kind(),
|
||||
+ format!("failed to wait for {cmd:?}: {err}"),
|
||||
+ )),
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/drivers/audio/ac97d/src/main.rs b/drivers/audio/ac97d/src/main.rs
|
||||
index ffa8a94b..1ce21cde 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(_) => {
|
||||
+ error!("ac97d: invalid BAR0 (not a port BAR)");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
+ let bar1 = match pci_config.func.bars[1].try_port() {
|
||||
+ Ok(port) => port,
|
||||
+ Err(_) => {
|
||||
+ error!("ac97d: invalid BAR1 (not a port BAR)");
|
||||
+ 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,29 @@ 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 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 +88,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 +174,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 +195,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..7b15b322 100755
|
||||
--- a/drivers/audio/ihdad/src/main.rs
|
||||
+++ b/drivers/audio/ihdad/src/main.rs
|
||||
@@ -38,6 +38,10 @@ 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");
|
||||
@@ -53,11 +57,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);
|
||||
|
||||
diff --git a/drivers/graphics/ihdgd/src/main.rs b/drivers/graphics/ihdgd/src/main.rs
|
||||
index a8b6cc60..dc68c6d2 100644
|
||||
--- a/drivers/graphics/ihdgd/src/main.rs
|
||||
+++ b/drivers/graphics/ihdgd/src/main.rs
|
||||
@@ -29,16 +29,26 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
|
||||
log::info!("IHDG {}", pci_config.func.display());
|
||||
|
||||
- let device = Device::new(&mut pcid_handle, &pci_config.func)
|
||||
- .expect("ihdgd: failed to initialize device");
|
||||
+ let device = match Device::new(&mut pcid_handle, &pci_config.func) {
|
||||
+ Ok(device) => device,
|
||||
+ Err(err) => {
|
||||
+ log::error!("ihdgd: failed to initialize device: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
|
||||
let irq_file = pci_allocate_interrupt_vector(&mut pcid_handle, "ihdgd");
|
||||
|
||||
// Needs to be before GraphicsScheme::new to avoid a deadlock due to initnsmgr blocking on
|
||||
// /scheme/event as it is already blocked on opening /scheme/display.ihdg.*.
|
||||
// FIXME change the initnsmgr to not block on openat for the target scheme.
|
||||
- let event_queue: EventQueue<Source> =
|
||||
- EventQueue::new().expect("ihdgd: failed to create event queue");
|
||||
+ let event_queue: EventQueue<Source> = match EventQueue::new() {
|
||||
+ Ok(eq) => eq,
|
||||
+ Err(err) => {
|
||||
+ log::error!("ihdgd: failed to create event queue: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
|
||||
let mut scheme = GraphicsScheme::new(device, format!("display.ihdg.{}", name), false);
|
||||
|
||||
@@ -50,53 +60,69 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
- event_queue
|
||||
- .subscribe(
|
||||
- scheme.inputd_event_handle().as_raw_fd() as usize,
|
||||
- Source::Input,
|
||||
- event::EventFlags::READ,
|
||||
- )
|
||||
- .unwrap();
|
||||
- event_queue
|
||||
- .subscribe(
|
||||
- irq_file.irq_handle().as_raw_fd() as usize,
|
||||
- Source::Irq,
|
||||
- event::EventFlags::READ,
|
||||
- )
|
||||
- .unwrap();
|
||||
- event_queue
|
||||
- .subscribe(
|
||||
- scheme.event_handle().raw(),
|
||||
- Source::Scheme,
|
||||
- event::EventFlags::READ,
|
||||
- )
|
||||
- .unwrap();
|
||||
-
|
||||
- libredox::call::setrens(0, 0).expect("ihdgd: failed to enter null namespace");
|
||||
+ if let Err(err) = event_queue.subscribe(
|
||||
+ scheme.inputd_event_handle().as_raw_fd() as usize,
|
||||
+ Source::Input,
|
||||
+ event::EventFlags::READ,
|
||||
+ ) {
|
||||
+ log::error!("ihdgd: failed to subscribe to input events: {err}");
|
||||
+ }
|
||||
+ if let Err(err) = event_queue.subscribe(
|
||||
+ irq_file.irq_handle().as_raw_fd() as usize,
|
||||
+ Source::Irq,
|
||||
+ event::EventFlags::READ,
|
||||
+ ) {
|
||||
+ log::error!("ihdgd: failed to subscribe to IRQ events: {err}");
|
||||
+ }
|
||||
+ if let Err(err) = event_queue.subscribe(
|
||||
+ scheme.event_handle().raw(),
|
||||
+ Source::Scheme,
|
||||
+ event::EventFlags::READ,
|
||||
+ ) {
|
||||
+ log::error!("ihdgd: failed to subscribe to scheme events: {err}");
|
||||
+ }
|
||||
+
|
||||
+ if let Err(err) = libredox::call::setrens(0, 0) {
|
||||
+ log::error!("ihdgd: failed to enter null namespace: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
|
||||
daemon.ready();
|
||||
|
||||
let all = [Source::Input, Source::Irq, Source::Scheme];
|
||||
- for event in all
|
||||
- .into_iter()
|
||||
- .chain(event_queue.map(|e| e.expect("ihdgd: failed to get next event").user_data))
|
||||
- {
|
||||
+ for event in all.into_iter().chain(
|
||||
+ event_queue.filter_map(|e| match e {
|
||||
+ Ok(event) => Some(event.user_data),
|
||||
+ Err(err) => {
|
||||
+ log::error!("ihdgd: failed to get next event: {err}");
|
||||
+ None
|
||||
+ }
|
||||
+ }),
|
||||
+ ) {
|
||||
match event {
|
||||
Source::Input => scheme.handle_vt_events(),
|
||||
Source::Irq => {
|
||||
let mut irq = [0; 8];
|
||||
- irq_file.irq_handle().read(&mut irq).unwrap();
|
||||
+ if irq_file.irq_handle().read(&mut irq).is_err() {
|
||||
+ log::error!("ihdgd: failed to read IRQ");
|
||||
+ continue;
|
||||
+ }
|
||||
if scheme.adapter_mut().handle_irq() {
|
||||
- irq_file.irq_handle().write(&mut irq).unwrap();
|
||||
+ if let Err(err) = irq_file.irq_handle().write(&mut irq) {
|
||||
+ log::error!("ihdgd: failed to write IRQ: {err}");
|
||||
+ continue;
|
||||
+ }
|
||||
|
||||
scheme.adapter_mut().handle_events();
|
||||
- scheme.tick().unwrap();
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("ihdgd: failed to handle display events after IRQ: {err}");
|
||||
+ }
|
||||
}
|
||||
}
|
||||
Source::Scheme => {
|
||||
- scheme
|
||||
- .tick()
|
||||
- .expect("ihdgd: failed to handle scheme events");
|
||||
+ if let Err(err) = scheme.tick() {
|
||||
+ log::error!("ihdgd: failed to handle scheme events: {err}");
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/drivers/pcid/src/driver_interface/bar.rs b/drivers/pcid/src/driver_interface/bar.rs
|
||||
index b2c1d35b..5005fa32 100644
|
||||
--- a/drivers/pcid/src/driver_interface/bar.rs
|
||||
+++ b/drivers/pcid/src/driver_interface/bar.rs
|
||||
@@ -29,27 +29,33 @@ impl PciBar {
|
||||
}
|
||||
}
|
||||
|
||||
- pub fn expect_port(&self) -> u16 {
|
||||
+ pub fn try_port(&self) -> Result<u16, ()> {
|
||||
match *self {
|
||||
- PciBar::Port(port) => port,
|
||||
- PciBar::Memory32 { .. } | PciBar::Memory64 { .. } => {
|
||||
- panic!("expected port BAR, found memory BAR");
|
||||
- }
|
||||
- PciBar::None => panic!("expected BAR to exist"),
|
||||
+ PciBar::Port(port) => Ok(port),
|
||||
+ PciBar::Memory32 { .. } | PciBar::Memory64 { .. } => Err(()),
|
||||
+ PciBar::None => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
- pub fn expect_mem(&self) -> (usize, usize) {
|
||||
+ pub fn try_mem(&self) -> Result<(usize, usize), ()> {
|
||||
match *self {
|
||||
- PciBar::Memory32 { addr, size } => (addr as usize, size as usize),
|
||||
- PciBar::Memory64 { addr, size } => (
|
||||
+ PciBar::Memory32 { addr, size } => Ok((addr as usize, size as usize)),
|
||||
+ PciBar::Memory64 { addr, size } => Ok((
|
||||
addr.try_into()
|
||||
.expect("conversion from 64bit BAR to usize failed"),
|
||||
size.try_into()
|
||||
.expect("conversion from 64bit BAR size to usize failed"),
|
||||
- ),
|
||||
- PciBar::Port(_) => panic!("expected memory BAR, found port BAR"),
|
||||
- PciBar::None => panic!("expected BAR to exist"),
|
||||
+ )),
|
||||
+ PciBar::Port(_) => Err(()),
|
||||
+ PciBar::None => Err(()),
|
||||
}
|
||||
}
|
||||
+
|
||||
+ pub fn expect_port(&self) -> u16 {
|
||||
+ self.try_port().expect("expected port BAR")
|
||||
+ }
|
||||
+
|
||||
+ pub fn expect_mem(&self) -> (usize, usize) {
|
||||
+ self.try_mem().expect("expected memory BAR")
|
||||
+ }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
-52
@@ -1,52 +0,0 @@
|
||||
--- a/src/os/uefi/device.rs
|
||||
+++ b/src/os/uefi/device.rs
|
||||
@@ -228,8 +228,38 @@
|
||||
};
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
-
|
||||
- live_image.read_to_end(&mut buffer).unwrap();
|
||||
+ let mut chunk = [0_u8; 64 * 1024];
|
||||
+
|
||||
+ const MAX_LIVE_IMAGE_PRELOAD: usize = 128 * 1024 * 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;
|
||||
+ }
|
||||
+
|
||||
+ 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)
|
||||
}
|
||||
@@ -267,7 +297,7 @@
|
||||
|
||||
if cfg!(feature = "live") {
|
||||
if let Some(buffer) = esp_live_image(esp_handle, esp_device_path.0) {
|
||||
- let partition_offset = if buffer.len() > 520 && &buffer[512..520] == b"EFI PART" {
|
||||
+ let partition_offset = if buffer.len() >= 520 && &buffer[512..520] == b"EFI PART" {
|
||||
gpt_find_redoxfs_offset_from_slice(&buffer)
|
||||
} else {
|
||||
0
|
||||
-215
@@ -1,215 +0,0 @@
|
||||
--- a/src/arch/x86/mod.rs
|
||||
+++ b/src/arch/x86/mod.rs
|
||||
@@ -3,10 +3,15 @@
|
||||
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)
|
||||
}
|
||||
--- a/src/arch/x86/x64.rs
|
||||
+++ b/src/arch/x86/x64.rs
|
||||
@@ -29,7 +29,12 @@
|
||||
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 @@
|
||||
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() {
|
||||
--- a/src/main.rs
|
||||
+++ b/src/main.rs
|
||||
@@ -641,15 +641,34 @@
|
||||
(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);
|
||||
if env_base.is_null() {
|
||||
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 {
|
||||
--- a/src/os/uefi/device.rs
|
||||
+++ b/src/os/uefi/device.rs
|
||||
@@ -26,19 +26,10 @@
|
||||
}
|
||||
|
||||
let mut header_buf = [0u8; 512];
|
||||
- let lba1_block = block_size / redoxfs::BLOCK_SIZE;
|
||||
- let header_read_len = if 512 <= redoxfs::BLOCK_SIZE as usize {
|
||||
- redoxfs::BLOCK_SIZE as usize
|
||||
- } else {
|
||||
- 512
|
||||
- };
|
||||
- let mut read_buf = vec![0u8; header_read_len];
|
||||
- if unsafe { disk.read_at(lba1_block, &mut read_buf) }.is_err() {
|
||||
+ if disk.read_bytes(block_size, &mut header_buf).is_err() {
|
||||
log::warn!("GPT: failed to read LBA 1");
|
||||
return 2 * crate::MIBI as u64;
|
||||
}
|
||||
- let copy_len = 512.min(read_buf.len());
|
||||
- header_buf[..copy_len].copy_from_slice(&read_buf[..copy_len]);
|
||||
|
||||
if &header_buf[0..8] != b"EFI PART" {
|
||||
log::warn!("GPT: no valid signature at LBA 1");
|
||||
@@ -64,19 +55,15 @@
|
||||
}
|
||||
|
||||
let entries_byte_offset = partition_entry_start_lba * block_size;
|
||||
- let entries_start_block = entries_byte_offset / redoxfs::BLOCK_SIZE;
|
||||
|
||||
let total_entries_bytes = num_entries as usize * entry_size as usize;
|
||||
- let read_size = total_entries_bytes.min(4096);
|
||||
- let mut entries_buf = vec![0u8; read_size + redoxfs::BLOCK_SIZE as usize];
|
||||
- let entries_read_blocks = (read_size as u64).div_ceil(redoxfs::BLOCK_SIZE) as usize;
|
||||
- if unsafe { disk.read_at(entries_start_block, &mut entries_buf[..entries_read_blocks * redoxfs::BLOCK_SIZE as usize]) }.is_err() {
|
||||
+ let mut entries_buf = vec![0u8; total_entries_bytes];
|
||||
+ if disk.read_bytes(entries_byte_offset, &mut entries_buf).is_err() {
|
||||
log::warn!("GPT: failed to read partition entries");
|
||||
return 2 * crate::MIBI as u64;
|
||||
}
|
||||
|
||||
- let entries_per_chunk = read_size / entry_size as usize;
|
||||
- for i in 0..entries_per_chunk.min(num_entries as usize) {
|
||||
+ for i in 0..num_entries as usize {
|
||||
let off = i * entry_size as usize;
|
||||
if off + entry_size as usize > entries_buf.len() {
|
||||
break;
|
||||
--- a/src/os/uefi/disk.rs
|
||||
+++ b/src/os/uefi/disk.rs
|
||||
@@ -117,3 +117,43 @@
|
||||
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(())
|
||||
+ }
|
||||
+}
|
||||
--- a/src/os/uefi/mod.rs
|
||||
+++ b/src/os/uefi/mod.rs
|
||||
@@ -45,19 +45,18 @@
|
||||
let pages = size.div_ceil(page_size);
|
||||
|
||||
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,
|
||||
+ MemoryType::EfiLoaderData,
|
||||
pages,
|
||||
&mut ptr,
|
||||
))
|
||||
- .unwrap_or_else(|_| {
|
||||
- ptr = 0;
|
||||
- 0
|
||||
- });
|
||||
- if ptr == 0 { ptr::null_mut() } else { ptr as *mut u8 }
|
||||
+ .is_err()
|
||||
+ {
|
||||
+ return ptr::null_mut();
|
||||
+ }
|
||||
+ ptr as *mut u8
|
||||
};
|
||||
|
||||
if !ptr.is_null() {
|
||||
@@ -1,765 +0,0 @@
|
||||
diff --git a/src/acpi/madt/arch/x86.rs b/src/acpi/madt/arch/x86.rs
|
||||
--- a/src/acpi/madt/arch/x86.rs
|
||||
+++ b/src/acpi/madt/arch/x86.rs
|
||||
@@ -1,154 +1,247 @@
|
||||
use core::{
|
||||
hint,
|
||||
sync::atomic::{AtomicU8, Ordering},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
arch::start::KernelArgsAp,
|
||||
cpu_set::LogicalCpuId,
|
||||
device::local_apic::the_local_apic,
|
||||
memory::{
|
||||
allocate_p2frame, Frame, KernelMapper, Page, PageFlags, PhysicalAddress, RmmA, RmmArch,
|
||||
VirtualAddress, PAGE_SIZE,
|
||||
},
|
||||
start::kstart_ap,
|
||||
AP_READY,
|
||||
};
|
||||
|
||||
use super::{Madt, MadtEntry};
|
||||
|
||||
const TRAMPOLINE: usize = 0x8000;
|
||||
static TRAMPOLINE_DATA: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/trampoline"));
|
||||
|
||||
pub(super) fn init(madt: Madt) {
|
||||
let local_apic = unsafe { the_local_apic() };
|
||||
let me = local_apic.id();
|
||||
|
||||
if local_apic.x2 {
|
||||
debug!(" X2APIC {}", me.get());
|
||||
} else {
|
||||
debug!(" XAPIC {}: {:>08X}", me.get(), local_apic.address);
|
||||
}
|
||||
|
||||
if cfg!(not(feature = "multi_core")) {
|
||||
return;
|
||||
}
|
||||
|
||||
- // Map trampoline
|
||||
+ // Map trampoline writable and executable (trampoline page holds both code
|
||||
+ // and AP argument data — AP writes ap_ready on the same page, so W^X is
|
||||
+ // not possible without splitting code/data across pages).
|
||||
let trampoline_frame = Frame::containing(PhysicalAddress::new(TRAMPOLINE));
|
||||
let trampoline_page = Page::containing_address(VirtualAddress::new(TRAMPOLINE));
|
||||
let (result, page_table_physaddr) = unsafe {
|
||||
- //TODO: do not have writable and executable!
|
||||
let mut mapper = KernelMapper::lock_rw();
|
||||
|
||||
let result = mapper
|
||||
.map_phys(
|
||||
trampoline_page.start_address(),
|
||||
trampoline_frame.base(),
|
||||
- PageFlags::new().execute(true).write(true),
|
||||
+ PageFlags::new().write(true).execute(true),
|
||||
)
|
||||
.expect("failed to map trampoline");
|
||||
|
||||
(result, mapper.table().phys().data())
|
||||
};
|
||||
result.flush();
|
||||
|
||||
// Write trampoline, make sure TRAMPOLINE page is free for use
|
||||
for (i, val) in TRAMPOLINE_DATA.iter().enumerate() {
|
||||
unsafe {
|
||||
(*((TRAMPOLINE as *mut u8).add(i) as *const AtomicU8)).store(*val, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
for madt_entry in madt.iter() {
|
||||
debug!(" {:x?}", madt_entry);
|
||||
if let MadtEntry::LocalApic(ap_local_apic) = madt_entry {
|
||||
if u32::from(ap_local_apic.id) == me.get() {
|
||||
debug!(" This is my local APIC");
|
||||
} else if ap_local_apic.flags & 1 == 1 {
|
||||
let cpu_id = LogicalCpuId::next();
|
||||
|
||||
// Allocate a stack
|
||||
let stack_start = RmmA::phys_to_virt(
|
||||
allocate_p2frame(4)
|
||||
.expect("no more frames in acpi stack_start")
|
||||
.base(),
|
||||
)
|
||||
.data();
|
||||
let stack_end = stack_start + (PAGE_SIZE << 4);
|
||||
|
||||
let pcr_ptr = crate::arch::gdt::allocate_and_init_pcr(cpu_id, stack_end);
|
||||
|
||||
let idt_ptr = crate::arch::idt::allocate_and_init_idt(cpu_id);
|
||||
|
||||
let args = KernelArgsAp {
|
||||
stack_end: stack_end as *mut u8,
|
||||
cpu_id,
|
||||
pcr_ptr,
|
||||
idt_ptr,
|
||||
};
|
||||
|
||||
let ap_ready = (TRAMPOLINE + 8) as *mut u64;
|
||||
let ap_args_ptr = unsafe { ap_ready.add(1) };
|
||||
let ap_page_table = unsafe { ap_ready.add(2) };
|
||||
let ap_code = unsafe { ap_ready.add(3) };
|
||||
|
||||
// Set the ap_ready to 0, volatile
|
||||
unsafe {
|
||||
ap_ready.write(0);
|
||||
ap_args_ptr.write(&args as *const _ as u64);
|
||||
ap_page_table.write(page_table_physaddr as u64);
|
||||
#[expect(clippy::fn_to_numeric_cast)]
|
||||
ap_code.write(kstart_ap as u64);
|
||||
|
||||
// TODO: Is this necessary (this fence)?
|
||||
core::arch::asm!("");
|
||||
};
|
||||
AP_READY.store(false, Ordering::SeqCst);
|
||||
|
||||
// Send INIT IPI
|
||||
{
|
||||
let mut icr = 0x4500;
|
||||
if local_apic.x2 {
|
||||
icr |= u64::from(ap_local_apic.id) << 32;
|
||||
} else {
|
||||
icr |= u64::from(ap_local_apic.id) << 56;
|
||||
}
|
||||
local_apic.set_icr(icr);
|
||||
}
|
||||
|
||||
// Send START IPI
|
||||
{
|
||||
let ap_segment = (TRAMPOLINE >> 12) & 0xFF;
|
||||
let mut icr = 0x4600 | ap_segment as u64;
|
||||
|
||||
if local_apic.x2 {
|
||||
icr |= u64::from(ap_local_apic.id) << 32;
|
||||
} else {
|
||||
icr |= u64::from(ap_local_apic.id) << 56;
|
||||
}
|
||||
|
||||
local_apic.set_icr(icr);
|
||||
}
|
||||
|
||||
// Wait for trampoline ready
|
||||
while unsafe { (*ap_ready.cast::<AtomicU8>()).load(Ordering::SeqCst) } == 0 {
|
||||
hint::spin_loop();
|
||||
}
|
||||
while !AP_READY.load(Ordering::SeqCst) {
|
||||
hint::spin_loop();
|
||||
}
|
||||
|
||||
RmmA::invalidate_all();
|
||||
}
|
||||
+ } else if let MadtEntry::LocalX2Apic(ap_x2apic) = madt_entry {
|
||||
+ if ap_x2apic.x2apic_id == me.get() {
|
||||
+ debug!(" This is my local x2APIC");
|
||||
+ } else if ap_x2apic.flags & 1 == 1 {
|
||||
+ let cpu_id = LogicalCpuId::next();
|
||||
+
|
||||
+ let stack_start = RmmA::phys_to_virt(
|
||||
+ allocate_p2frame(4)
|
||||
+ .expect("no more frames in acpi stack_start")
|
||||
+ .base(),
|
||||
+ )
|
||||
+ .data();
|
||||
+ let stack_end = stack_start + (PAGE_SIZE << 4);
|
||||
+
|
||||
+ let pcr_ptr = crate::arch::gdt::allocate_and_init_pcr(cpu_id, stack_end);
|
||||
+ let idt_ptr = crate::arch::idt::allocate_and_init_idt(cpu_id);
|
||||
+
|
||||
+ let args = KernelArgsAp {
|
||||
+ stack_end: stack_end as *mut u8,
|
||||
+ cpu_id,
|
||||
+ pcr_ptr,
|
||||
+ idt_ptr,
|
||||
+ };
|
||||
+
|
||||
+ let ap_ready = (TRAMPOLINE + 8) as *mut u64;
|
||||
+ let ap_args_ptr = unsafe { ap_ready.add(1) };
|
||||
+ let ap_page_table = unsafe { ap_ready.add(2) };
|
||||
+ let ap_code = unsafe { ap_ready.add(3) };
|
||||
+
|
||||
+ unsafe {
|
||||
+ ap_ready.write(0);
|
||||
+ ap_args_ptr.write(&args as *const _ as u64);
|
||||
+ ap_page_table.write(page_table_physaddr as u64);
|
||||
+ #[expect(clippy::fn_to_numeric_cast)]
|
||||
+ ap_code.write(kstart_ap as u64);
|
||||
+ core::arch::asm!("");
|
||||
+ };
|
||||
+ AP_READY.store(false, Ordering::SeqCst);
|
||||
+
|
||||
+ // Send INIT IPI (x2APIC always uses 32-bit APIC ID in bits 32-63)
|
||||
+ {
|
||||
+ let mut icr = 0x4500u64;
|
||||
+ icr |= u64::from(ap_x2apic.x2apic_id) << 32;
|
||||
+ local_apic.set_icr(icr);
|
||||
+ }
|
||||
+
|
||||
+ // Wait for INIT delivery (~10 μs de-assert window per Intel SDM)
|
||||
+ for _ in 0..100_000 {
|
||||
+ hint::spin_loop();
|
||||
+ }
|
||||
+
|
||||
+ // Send STARTUP IPI
|
||||
+ {
|
||||
+ let ap_segment = (TRAMPOLINE >> 12) & 0xFF;
|
||||
+ let mut icr = 0x4600u64 | ap_segment as u64;
|
||||
+ icr |= u64::from(ap_x2apic.x2apic_id) << 32;
|
||||
+ local_apic.set_icr(icr);
|
||||
+ }
|
||||
+
|
||||
+ // Wait ~200 μs, then send second STARTUP IPI per the universal
|
||||
+ // startup algorithm.
|
||||
+ for _ in 0..2_000_000 {
|
||||
+ hint::spin_loop();
|
||||
+ }
|
||||
+ {
|
||||
+ let ap_segment = (TRAMPOLINE >> 12) & 0xFF;
|
||||
+ let mut icr = 0x4600u64 | ap_segment as u64;
|
||||
+ icr |= u64::from(ap_x2apic.x2apic_id) << 32;
|
||||
+ local_apic.set_icr(icr);
|
||||
+ }
|
||||
+
|
||||
+ let mut timeout = 100_000_000u32;
|
||||
+ while unsafe { (*ap_ready.cast::<AtomicU8>()).load(Ordering::SeqCst) } == 0 {
|
||||
+ hint::spin_loop();
|
||||
+ timeout -= 1;
|
||||
+ if timeout == 0 {
|
||||
+ debug!("x2APIC AP {} trampoline startup timed out", ap_x2apic.x2apic_id);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ let mut timeout = 100_000_000u32;
|
||||
+ while !AP_READY.load(Ordering::SeqCst) {
|
||||
+ hint::spin_loop();
|
||||
+ timeout -= 1;
|
||||
+ if timeout == 0 {
|
||||
+ debug!("x2APIC AP {} kernel startup timed out", ap_x2apic.x2apic_id);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ RmmA::invalidate_all();
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
// Unmap trampoline
|
||||
let (_frame, _, flush) = unsafe {
|
||||
KernelMapper::lock_rw()
|
||||
.unmap_phys(trampoline_page.start_address())
|
||||
.expect("failed to unmap trampoline page")
|
||||
};
|
||||
flush.flush();
|
||||
}
|
||||
diff --git a/src/acpi/madt/mod.rs b/src/acpi/madt/mod.rs
|
||||
--- a/src/acpi/madt/mod.rs
|
||||
+++ b/src/acpi/madt/mod.rs
|
||||
@@ -27,214 +27,240 @@
|
||||
pub fn madt() -> Option<&'static Madt> {
|
||||
unsafe { &*MADT.get() }.as_ref()
|
||||
}
|
||||
pub const FLAG_PCAT: u32 = 1;
|
||||
|
||||
impl Madt {
|
||||
pub fn init() {
|
||||
let madt = Madt::new(find_one_sdt!("APIC"));
|
||||
|
||||
if let Some(madt) = madt {
|
||||
// safe because no APs have been started yet.
|
||||
unsafe { MADT.get().write(Some(madt)) };
|
||||
|
||||
debug!(" APIC: {:>08X}: {}", madt.local_address, madt.flags);
|
||||
|
||||
arch::init(madt);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(sdt: &'static Sdt) -> Option<Madt> {
|
||||
if &sdt.signature == b"APIC" && sdt.data_len() >= 8 {
|
||||
//Not valid if no local address and flags
|
||||
let local_address = unsafe { (sdt.data_address() as *const u32).read_unaligned() };
|
||||
let flags = unsafe {
|
||||
(sdt.data_address() as *const u32)
|
||||
.offset(1)
|
||||
.read_unaligned()
|
||||
};
|
||||
|
||||
Some(Madt {
|
||||
sdt,
|
||||
local_address,
|
||||
flags,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> MadtIter {
|
||||
MadtIter {
|
||||
sdt: self.sdt,
|
||||
i: 8, // Skip local controller address and flags
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// MADT Local APIC
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C, packed)]
|
||||
pub struct MadtLocalApic {
|
||||
/// Processor ID
|
||||
pub processor: u8,
|
||||
/// Local APIC ID
|
||||
pub id: u8,
|
||||
/// Flags. 1 means that the processor is enabled
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
/// MADT I/O APIC
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C, packed)]
|
||||
pub struct MadtIoApic {
|
||||
/// I/O APIC ID
|
||||
pub id: u8,
|
||||
/// reserved
|
||||
_reserved: u8,
|
||||
/// I/O APIC address
|
||||
pub address: u32,
|
||||
/// Global system interrupt base
|
||||
pub gsi_base: u32,
|
||||
}
|
||||
|
||||
/// MADT Interrupt Source Override
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C, packed)]
|
||||
pub struct MadtIntSrcOverride {
|
||||
/// Bus Source
|
||||
pub bus_source: u8,
|
||||
/// IRQ Source
|
||||
pub irq_source: u8,
|
||||
/// Global system interrupt base
|
||||
pub gsi_base: u32,
|
||||
/// Flags
|
||||
pub flags: u16,
|
||||
}
|
||||
|
||||
/// MADT GICC
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C, packed)]
|
||||
pub struct MadtGicc {
|
||||
_reserved: u16,
|
||||
pub cpu_interface_number: u32,
|
||||
pub acpi_processor_uid: u32,
|
||||
pub flags: u32,
|
||||
pub parking_protocol_version: u32,
|
||||
pub performance_interrupt_gsiv: u32,
|
||||
pub parked_address: u64,
|
||||
pub physical_base_address: u64,
|
||||
pub gicv: u64,
|
||||
pub gich: u64,
|
||||
pub vgic_maintenance_interrupt: u32,
|
||||
pub gicr_base_address: u64,
|
||||
pub mpidr: u64,
|
||||
pub processor_power_efficiency_class: u8,
|
||||
_reserved2: u8,
|
||||
pub spe_overflow_interrupt: u16,
|
||||
//TODO: optional field introduced in ACPI 6.5: pub trbe_interrupt: u16,
|
||||
}
|
||||
|
||||
/// MADT GICD
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C, packed)]
|
||||
pub struct MadtGicd {
|
||||
_reserved: u16,
|
||||
pub gic_id: u32,
|
||||
pub physical_base_address: u64,
|
||||
pub system_vector_base: u32,
|
||||
pub gic_version: u8,
|
||||
_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 Entries
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub enum MadtEntry {
|
||||
LocalApic(&'static MadtLocalApic),
|
||||
InvalidLocalApic(usize),
|
||||
IoApic(&'static MadtIoApic),
|
||||
InvalidIoApic(usize),
|
||||
IntSrcOverride(&'static MadtIntSrcOverride),
|
||||
InvalidIntSrcOverride(usize),
|
||||
Gicc(&'static MadtGicc),
|
||||
InvalidGicc(usize),
|
||||
Gicd(&'static MadtGicd),
|
||||
InvalidGicd(usize),
|
||||
+ LocalX2Apic(&'static MadtLocalX2Apic),
|
||||
+ InvalidLocalX2Apic(usize),
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
pub struct MadtIter {
|
||||
sdt: &'static Sdt,
|
||||
i: usize,
|
||||
}
|
||||
|
||||
impl Iterator for MadtIter {
|
||||
type Item = MadtEntry;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.i + 1 < self.sdt.data_len() {
|
||||
let entry_type = unsafe { *(self.sdt.data_address() as *const u8).add(self.i) };
|
||||
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 => {
|
||||
if entry_len == size_of::<MadtLocalApic>() + 2 {
|
||||
MadtEntry::LocalApic(unsafe {
|
||||
&*((self.sdt.data_address() + self.i + 2) as *const MadtLocalApic)
|
||||
})
|
||||
} else {
|
||||
MadtEntry::InvalidLocalApic(entry_len)
|
||||
}
|
||||
}
|
||||
0x1 => {
|
||||
if entry_len == size_of::<MadtIoApic>() + 2 {
|
||||
MadtEntry::IoApic(unsafe {
|
||||
&*((self.sdt.data_address() + self.i + 2) as *const MadtIoApic)
|
||||
})
|
||||
} else {
|
||||
MadtEntry::InvalidIoApic(entry_len)
|
||||
}
|
||||
}
|
||||
0x2 => {
|
||||
if entry_len == size_of::<MadtIntSrcOverride>() + 2 {
|
||||
MadtEntry::IntSrcOverride(unsafe {
|
||||
&*((self.sdt.data_address() + self.i + 2)
|
||||
as *const MadtIntSrcOverride)
|
||||
})
|
||||
} else {
|
||||
MadtEntry::InvalidIntSrcOverride(entry_len)
|
||||
}
|
||||
}
|
||||
0xB => {
|
||||
if entry_len >= size_of::<MadtGicc>() + 2 {
|
||||
MadtEntry::Gicc(unsafe {
|
||||
&*((self.sdt.data_address() + self.i + 2) as *const MadtGicc)
|
||||
})
|
||||
} else {
|
||||
MadtEntry::InvalidGicc(entry_len)
|
||||
}
|
||||
}
|
||||
0xC => {
|
||||
if entry_len >= size_of::<MadtGicd>() + 2 {
|
||||
MadtEntry::Gicd(unsafe {
|
||||
&*((self.sdt.data_address() + self.i + 2) as *const MadtGicd)
|
||||
})
|
||||
} else {
|
||||
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)
|
||||
+ }
|
||||
+ }
|
||||
_ => MadtEntry::Unknown(entry_type),
|
||||
};
|
||||
|
||||
self.i += entry_len;
|
||||
|
||||
Some(item)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/src/arch/x86_shared/cpuid.rs b/src/arch/x86_shared/cpuid.rs
|
||||
--- a/src/arch/x86_shared/cpuid.rs
|
||||
+++ b/src/arch/x86_shared/cpuid.rs
|
||||
@@ -1,29 +1,39 @@
|
||||
use raw_cpuid::{CpuId, CpuIdResult, ExtendedFeatures, FeatureInfo};
|
||||
|
||||
+#[cfg(target_arch = "x86_64")]
|
||||
pub fn cpuid() -> CpuId {
|
||||
- // FIXME check for cpuid availability during early boot and error out if it doesn't exist.
|
||||
CpuId::with_cpuid_fn(|a, c| {
|
||||
- #[cfg(target_arch = "x86")]
|
||||
+ let result = unsafe { core::arch::x86_64::__cpuid_count(a, c) };
|
||||
+ CpuIdResult {
|
||||
+ eax: result.eax,
|
||||
+ ebx: result.ebx,
|
||||
+ ecx: result.ecx,
|
||||
+ edx: result.edx,
|
||||
+ }
|
||||
+ })
|
||||
+}
|
||||
+
|
||||
+#[cfg(target_arch = "x86")]
|
||||
+pub fn cpuid() -> CpuId {
|
||||
+ CpuId::with_cpuid_fn(|a, c| {
|
||||
let result = unsafe { core::arch::x86::__cpuid_count(a, c) };
|
||||
- #[cfg(target_arch = "x86_64")]
|
||||
- let result = unsafe { core::arch::x86_64::__cpuid_count(a, c) };
|
||||
CpuIdResult {
|
||||
eax: result.eax,
|
||||
ebx: result.ebx,
|
||||
ecx: result.ecx,
|
||||
edx: result.edx,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_arch = "x86_64"), expect(dead_code))]
|
||||
pub fn feature_info() -> FeatureInfo {
|
||||
cpuid()
|
||||
.get_feature_info()
|
||||
.expect("x86_64 requires CPUID leaf=0x01 to be present")
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_arch = "x86_64"), expect(dead_code))]
|
||||
pub fn has_ext_feat(feat: impl FnOnce(ExtendedFeatures) -> bool) -> bool {
|
||||
cpuid().get_extended_feature_info().is_some_and(feat)
|
||||
}
|
||||
diff --git a/src/context/memory.rs b/src/context/memory.rs
|
||||
--- a/src/context/memory.rs
|
||||
+++ b/src/context/memory.rs
|
||||
@@ -890,112 +890,128 @@
|
||||
.range(..=page)
|
||||
.next_back()
|
||||
.filter(|(base, info)| (**base..base.next_by(info.page_count)).contains(&page))
|
||||
.map(|(base, info)| (*base, info))
|
||||
}
|
||||
|
||||
/// Returns an iterator over all grants that occupy some part of the
|
||||
/// requested region
|
||||
pub fn conflicts(&self, span: PageSpan) -> impl Iterator<Item = (Page, &'_ GrantInfo)> + '_ {
|
||||
let start = self.contains(span.base);
|
||||
|
||||
// If there is a grant that contains the base page, start searching at the base of that
|
||||
// grant, rather than the requested base here.
|
||||
let start_span = start
|
||||
.map(|(base, info)| PageSpan::new(base, info.page_count))
|
||||
.unwrap_or(span);
|
||||
|
||||
self.inner
|
||||
.range(start_span.base..)
|
||||
.take_while(move |(base, info)| PageSpan::new(**base, info.page_count).intersects(span))
|
||||
.map(|(base, info)| (*base, info))
|
||||
}
|
||||
// TODO: DEDUPLICATE CODE!
|
||||
pub fn conflicts_mut(
|
||||
&mut self,
|
||||
span: PageSpan,
|
||||
) -> impl Iterator<Item = (Page, &'_ mut GrantInfo)> + '_ {
|
||||
let start = self.contains(span.base);
|
||||
|
||||
// If there is a grant that contains the base page, start searching at the base of that
|
||||
// grant, rather than the requested base here.
|
||||
let start_span = start
|
||||
.map(|(base, info)| PageSpan::new(base, info.page_count))
|
||||
.unwrap_or(span);
|
||||
|
||||
self.inner
|
||||
.range_mut(start_span.base..)
|
||||
.take_while(move |(base, info)| PageSpan::new(**base, info.page_count).intersects(span))
|
||||
.map(|(base, info)| (*base, info))
|
||||
}
|
||||
- /// Return a free region with the specified size
|
||||
- // TODO: Alignment (x86_64: 4 KiB, 2 MiB, or 1 GiB).
|
||||
+ /// Return a free region with the specified size, optionally aligned to a power-of-two
|
||||
+ /// boundary (x86_64 supports 4 KiB, 2 MiB, or 1 GiB pages).
|
||||
// TODO: Support finding grant close to a requested address?
|
||||
pub fn find_free_near(
|
||||
&self,
|
||||
min: usize,
|
||||
page_count: usize,
|
||||
_near: Option<Page>,
|
||||
) -> Option<PageSpan> {
|
||||
- // Get first available hole, but do reserve the page starting from zero as most compiled
|
||||
- // languages cannot handle null pointers safely even if they point to valid memory. If an
|
||||
- // application absolutely needs to map the 0th page, they will have to do so explicitly via
|
||||
- // MAP_FIXED/MAP_FIXED_NOREPLACE.
|
||||
- // TODO: Allow explicitly allocating guard pages? Perhaps using mprotect or mmap with
|
||||
- // PROT_NONE?
|
||||
+ self.find_free_near_aligned(min, page_count, _near, 0)
|
||||
+ }
|
||||
+ pub fn find_free_near_aligned(
|
||||
+ &self,
|
||||
+ min: usize,
|
||||
+ page_count: usize,
|
||||
+ _near: Option<Page>,
|
||||
+ page_alignment: usize,
|
||||
+ ) -> Option<PageSpan> {
|
||||
+ let alignment = if page_alignment == 0 {
|
||||
+ PAGE_SIZE
|
||||
+ } else {
|
||||
+ assert!(
|
||||
+ page_alignment.is_power_of_two(),
|
||||
+ "page_alignment must be a power of two"
|
||||
+ );
|
||||
+ page_alignment * PAGE_SIZE
|
||||
+ };
|
||||
|
||||
let (hole_start, _hole_size) = self
|
||||
.holes
|
||||
.iter()
|
||||
.skip_while(|(hole_offset, hole_size)| hole_offset.data() + **hole_size <= min)
|
||||
.find(|(hole_offset, hole_size)| {
|
||||
- let avail_size =
|
||||
- if hole_offset.data() <= min && min <= hole_offset.data() + **hole_size {
|
||||
- **hole_size - (min - hole_offset.data())
|
||||
- } else {
|
||||
- **hole_size
|
||||
- };
|
||||
+ let base = cmp::max(hole_offset.data(), min);
|
||||
+ let aligned_base = (base + alignment - 1) & !(alignment - 1);
|
||||
+ let avail_size = if aligned_base <= hole_offset.data() + **hole_size {
|
||||
+ hole_offset.data() + **hole_size - aligned_base
|
||||
+ } else {
|
||||
+ 0
|
||||
+ };
|
||||
page_count * PAGE_SIZE <= avail_size
|
||||
})?;
|
||||
- // Create new region
|
||||
+
|
||||
+ let base = cmp::max(hole_start.data(), min);
|
||||
+ let aligned_base = (base + alignment - 1) & !(alignment - 1);
|
||||
+
|
||||
Some(PageSpan::new(
|
||||
- Page::containing_address(VirtualAddress::new(cmp::max(hole_start.data(), min))),
|
||||
+ Page::containing_address(VirtualAddress::new(aligned_base)),
|
||||
page_count,
|
||||
))
|
||||
}
|
||||
pub fn find_free(&self, min: usize, page_count: usize) -> Option<PageSpan> {
|
||||
self.find_free_near(min, page_count, None)
|
||||
}
|
||||
fn reserve(&mut self, base: Page, page_count: usize) {
|
||||
let start_address = base.start_address();
|
||||
let size = page_count * PAGE_SIZE;
|
||||
let end_address = base.start_address().add(size);
|
||||
|
||||
let previous_hole = self.holes.range_mut(..start_address).next_back();
|
||||
|
||||
if let Some((hole_offset, hole_size)) = previous_hole {
|
||||
let prev_hole_end = hole_offset.data() + *hole_size;
|
||||
|
||||
// Note that prev_hole_end cannot exactly equal start_address, since that would imply
|
||||
// there is another grant at that position already, as it would otherwise have been
|
||||
// larger.
|
||||
|
||||
if prev_hole_end > start_address.data() {
|
||||
// hole_offset must be below (but never equal to) the start address due to the
|
||||
// `..start_address()` limit; hence, all we have to do is to shrink the
|
||||
// previous offset.
|
||||
*hole_size = start_address.data() - hole_offset.data();
|
||||
}
|
||||
if prev_hole_end > end_address.data() {
|
||||
// The grant is splitting this hole in two, so insert the new one at the end.
|
||||
self.holes
|
||||
.insert(end_address, prev_hole_end - end_address.data());
|
||||
}
|
||||
}
|
||||
|
||||
// Next hole
|
||||
if let Some(hole_size) = self.holes.remove(&start_address) {
|
||||
let remainder = hole_size - size;
|
||||
if remainder > 0 {
|
||||
self.holes.insert(end_address, remainder);
|
||||
}
|
||||
}
|
||||
diff --git a/src/arch/x86_shared/device/local_apic.rs b/src/arch/x86_shared/device/local_apic.rs
|
||||
--- a/src/arch/x86_shared/device/local_apic.rs
|
||||
+++ b/src/arch/x86_shared/device/local_apic.rs
|
||||
@@ -100,61 +100,68 @@
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> ApicId {
|
||||
ApicId::new(if self.x2 {
|
||||
unsafe { rdmsr(IA32_X2APIC_APICID) as u32 }
|
||||
} else {
|
||||
unsafe { self.read(0x20) }
|
||||
})
|
||||
}
|
||||
|
||||
pub fn version(&self) -> u32 {
|
||||
if self.x2 {
|
||||
unsafe { rdmsr(IA32_X2APIC_VERSION) as u32 }
|
||||
} else {
|
||||
unsafe { self.read(0x30) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icr(&self) -> u64 {
|
||||
if self.x2 {
|
||||
unsafe { rdmsr(IA32_X2APIC_ICR) }
|
||||
} else {
|
||||
unsafe { ((self.read(0x310) as u64) << 32) | self.read(0x300) as u64 }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_icr(&mut self, value: u64) {
|
||||
if self.x2 {
|
||||
unsafe {
|
||||
+ const PENDING: u32 = 1 << 12;
|
||||
+ while (rdmsr(IA32_X2APIC_ICR) as u32) & PENDING == PENDING {
|
||||
+ core::hint::spin_loop();
|
||||
+ }
|
||||
wrmsr(IA32_X2APIC_ICR, value);
|
||||
+ while (rdmsr(IA32_X2APIC_ICR) as u32) & PENDING == PENDING {
|
||||
+ core::hint::spin_loop();
|
||||
+ }
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
const PENDING: u32 = 1 << 12;
|
||||
while self.read(0x300) & PENDING == PENDING {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
self.write(0x310, (value >> 32) as u32);
|
||||
self.write(0x300, value as u32);
|
||||
while self.read(0x300) & PENDING == PENDING {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ipi(&mut self, apic_id: ApicId, kind: IpiKind) {
|
||||
let shift = if self.x2 { 32 } else { 56 };
|
||||
self.set_icr((u64::from(apic_id.get()) << shift) | 0x40 | kind as u64);
|
||||
}
|
||||
pub fn ipi_nmi(&mut self, apic_id: ApicId) {
|
||||
let shift = if self.x2 { 32 } else { 56 };
|
||||
self.set_icr((u64::from(apic_id.get()) << shift) | (1 << 14) | (0b100 << 8));
|
||||
}
|
||||
|
||||
pub unsafe fn eoi(&mut self) {
|
||||
unsafe {
|
||||
if self.x2 {
|
||||
wrmsr(IA32_X2APIC_EOI, 0);
|
||||
} else {
|
||||
@@ -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,17 +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()
|
||||
};
|
||||
-137
@@ -1,137 +0,0 @@
|
||||
diff --git a/src/context/context.rs b/src/context/context.rs
|
||||
index c97c516..6d723f4 100644
|
||||
--- a/src/context/context.rs
|
||||
+++ b/src/context/context.rs
|
||||
@@ -148,6 +148,8 @@ pub struct Context {
|
||||
pub euid: u32,
|
||||
pub egid: u32,
|
||||
pub pid: usize,
|
||||
+ /// Supplementary group IDs for access control decisions.
|
||||
+ pub groups: Vec<u32>,
|
||||
|
||||
// See [`PreemptGuard`]
|
||||
//
|
||||
@@ -204,6 +206,7 @@ impl Context {
|
||||
euid: 0,
|
||||
egid: 0,
|
||||
pid: 0,
|
||||
+ groups: Vec::new(),
|
||||
|
||||
#[cfg(feature = "syscall_debug")]
|
||||
syscall_debug_info: crate::syscall::debug::SyscallDebugInfo::default(),
|
||||
@@ -479,6 +482,7 @@ impl Context {
|
||||
uid: self.euid,
|
||||
gid: self.egid,
|
||||
pid: self.pid,
|
||||
+ groups: self.groups.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs
|
||||
index d30272c..9da2b28 100644
|
||||
--- a/src/scheme/mod.rs
|
||||
+++ b/src/scheme/mod.rs
|
||||
@@ -777,6 +777,7 @@ pub struct CallerCtx {
|
||||
pub pid: usize,
|
||||
pub uid: u32,
|
||||
pub gid: u32,
|
||||
+ pub groups: alloc::vec::Vec<u32>,
|
||||
}
|
||||
impl CallerCtx {
|
||||
pub fn filter_uid_gid(self, euid: u32, egid: u32) -> Self {
|
||||
@@ -785,6 +786,7 @@ impl CallerCtx {
|
||||
pid: self.pid,
|
||||
uid: euid,
|
||||
gid: egid,
|
||||
+ groups: self.groups,
|
||||
}
|
||||
} else {
|
||||
self
|
||||
diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs
|
||||
index 47588e1..6ffb256 100644
|
||||
--- a/src/scheme/proc.rs
|
||||
+++ b/src/scheme/proc.rs
|
||||
@@ -105,6 +105,7 @@ enum ContextHandle {
|
||||
// Attr handles, to set ens/euid/egid/pid.
|
||||
Authority,
|
||||
Attr,
|
||||
+ Groups,
|
||||
|
||||
Status {
|
||||
privileged: bool,
|
||||
@@ -261,6 +262,7 @@ impl ProcScheme {
|
||||
let handle = match actual_name {
|
||||
"attrs" => ContextHandle::Attr,
|
||||
"status" => ContextHandle::Status { privileged: true },
|
||||
+ "groups" => ContextHandle::Groups,
|
||||
_ => return Err(Error::new(ENOENT)),
|
||||
};
|
||||
|
||||
@@ -306,6 +308,11 @@ impl ProcScheme {
|
||||
let id = NonZeroUsize::new(NEXT_ID.fetch_add(1, Ordering::Relaxed))
|
||||
.ok_or(Error::new(EMFILE))?;
|
||||
let context = context::spawn(true, Some(id), ret, token)?;
|
||||
+ {
|
||||
+ let parent_groups =
|
||||
+ context::current().read(token.token()).groups.clone();
|
||||
+ context.write(token.token()).groups = parent_groups;
|
||||
+ }
|
||||
HANDLES.write(token.token()).insert(
|
||||
id.get(),
|
||||
Handle {
|
||||
@@ -1271,6 +1278,39 @@ impl ContextHandle {
|
||||
guard.prio = (info.prio as usize).min(39);
|
||||
Ok(size_of::<ProcSchemeAttrs>())
|
||||
}
|
||||
+ Self::Groups => {
|
||||
+ const NGROUPS_MAX: usize = 65536;
|
||||
+ if buf.len() % size_of::<u32>() != 0 {
|
||||
+ return Err(Error::new(EINVAL));
|
||||
+ }
|
||||
+ let count = buf.len() / size_of::<u32>();
|
||||
+ if count > NGROUPS_MAX {
|
||||
+ return Err(Error::new(EINVAL));
|
||||
+ }
|
||||
+ let mut groups = Vec::with_capacity(count);
|
||||
+ for chunk in buf.in_exact_chunks(size_of::<u32>()).take(count) {
|
||||
+ groups.push(chunk.read_u32()?);
|
||||
+ }
|
||||
+ let proc_id = {
|
||||
+ let guard = context.read(token.token());
|
||||
+ guard.owner_proc_id
|
||||
+ };
|
||||
+ {
|
||||
+ let mut guard = context.write(token.token());
|
||||
+ guard.groups = groups.clone();
|
||||
+ }
|
||||
+ if let Some(pid) = proc_id {
|
||||
+ let mut contexts = context::contexts(token.downgrade());
|
||||
+ let (contexts, mut t) = contexts.token_split();
|
||||
+ for context_ref in contexts.iter() {
|
||||
+ let mut ctx = context_ref.write(t.token());
|
||||
+ if ctx.owner_proc_id == Some(pid) {
|
||||
+ ctx.groups = groups.clone();
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ Ok(count * size_of::<u32>())
|
||||
+ }
|
||||
ContextHandle::OpenViaDup => {
|
||||
let mut args = buf.usizes();
|
||||
|
||||
@@ -1475,6 +1515,15 @@ impl ContextHandle {
|
||||
debug_name,
|
||||
})
|
||||
}
|
||||
+ Self::Groups => {
|
||||
+ let c = &context.read(token.token());
|
||||
+ let max = buf.len() / size_of::<u32>();
|
||||
+ let count = c.groups.len().min(max);
|
||||
+ for (chunk, gid) in buf.in_exact_chunks(size_of::<u32>()).zip(&c.groups).take(count) {
|
||||
+ chunk.copy_from_slice(&gid.to_ne_bytes())?;
|
||||
+ }
|
||||
+ Ok(count * size_of::<u32>())
|
||||
+ }
|
||||
ContextHandle::Sighandler => {
|
||||
let data = match context.read(token.token()).sig {
|
||||
Some(ref sig) => SetSighandlerData {
|
||||
-152
@@ -1,152 +0,0 @@
|
||||
diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs
|
||||
index 47588e1..6578761 100644
|
||||
--- a/src/scheme/proc.rs
|
||||
+++ b/src/scheme/proc.rs
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
context::{
|
||||
self,
|
||||
- context::{HardBlockedReason, LockedFdTbl, SignalState},
|
||||
+ context::{HardBlockedReason, LockedFdTbl, SchedPolicy, SignalState},
|
||||
file::InternalFlags,
|
||||
memory::{handle_notify_files, AddrSpace, AddrSpaceWrapper, Grant, PageSpan},
|
||||
Context, ContextLock, Status,
|
||||
@@ -105,6 +105,7 @@ enum ContextHandle {
|
||||
// Attr handles, to set ens/euid/egid/pid.
|
||||
Authority,
|
||||
Attr,
|
||||
+ Groups,
|
||||
|
||||
Status {
|
||||
privileged: bool,
|
||||
@@ -145,6 +146,7 @@ enum ContextHandle {
|
||||
// directory.
|
||||
OpenViaDup,
|
||||
SchedAffinity,
|
||||
+ SchedPolicy,
|
||||
|
||||
MmapMinAddr(Arc<AddrSpaceWrapper>),
|
||||
}
|
||||
@@ -249,6 +251,9 @@ impl ProcScheme {
|
||||
false,
|
||||
),
|
||||
"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),
|
||||
"status" => (ContextHandle::Status { privileged: false }, false),
|
||||
_ if path.starts_with("auth-") => {
|
||||
let nonprefix = &path["auth-".len()..];
|
||||
@@ -261,6 +266,7 @@ impl ProcScheme {
|
||||
let handle = match actual_name {
|
||||
"attrs" => ContextHandle::Attr,
|
||||
"status" => ContextHandle::Status { privileged: true },
|
||||
+ "groups" => ContextHandle::Groups,
|
||||
_ => return Err(Error::new(ENOENT)),
|
||||
};
|
||||
|
||||
@@ -306,6 +312,11 @@ impl ProcScheme {
|
||||
let id = NonZeroUsize::new(NEXT_ID.fetch_add(1, Ordering::Relaxed))
|
||||
.ok_or(Error::new(EMFILE))?;
|
||||
let context = context::spawn(true, Some(id), ret, token)?;
|
||||
+ {
|
||||
+ let parent_groups =
|
||||
+ context::current().read(token.token()).groups.clone();
|
||||
+ context.write(token.token()).groups = parent_groups;
|
||||
+ }
|
||||
HANDLES.write(token.token()).insert(
|
||||
id.get(),
|
||||
Handle {
|
||||
@@ -1165,6 +1176,20 @@ impl ContextHandle {
|
||||
|
||||
Ok(size_of_val(&mask))
|
||||
}
|
||||
+ Self::SchedPolicy => {
|
||||
+ if buf.len() != 2 {
|
||||
+ return Err(Error::new(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ let [policy, rt_priority] = unsafe { buf.read_exact::<[u8; 2]>()? };
|
||||
+ let sched_policy = SchedPolicy::try_from_raw(policy).ok_or(Error::new(EINVAL))?;
|
||||
+
|
||||
+ context
|
||||
+ .write(token.token())
|
||||
+ .set_sched_policy(sched_policy, rt_priority);
|
||||
+
|
||||
+ Ok(2)
|
||||
+ }
|
||||
ContextHandle::Status { privileged } => {
|
||||
let mut args = buf.usizes();
|
||||
|
||||
@@ -1268,9 +1293,42 @@ impl ContextHandle {
|
||||
guard.pid = info.pid as usize;
|
||||
guard.euid = info.euid;
|
||||
guard.egid = info.egid;
|
||||
- guard.prio = (info.prio as usize).min(39);
|
||||
+ guard.set_sched_other_prio(info.prio as usize);
|
||||
Ok(size_of::<ProcSchemeAttrs>())
|
||||
}
|
||||
+ Self::Groups => {
|
||||
+ const NGROUPS_MAX: usize = 65536;
|
||||
+ if buf.len() % size_of::<u32>() != 0 {
|
||||
+ return Err(Error::new(EINVAL));
|
||||
+ }
|
||||
+ let count = buf.len() / size_of::<u32>();
|
||||
+ if count > NGROUPS_MAX {
|
||||
+ return Err(Error::new(EINVAL));
|
||||
+ }
|
||||
+ let mut groups = Vec::with_capacity(count);
|
||||
+ for chunk in buf.in_exact_chunks(size_of::<u32>()).take(count) {
|
||||
+ groups.push(chunk.read_u32()?);
|
||||
+ }
|
||||
+ let proc_id = {
|
||||
+ let guard = context.read(token.token());
|
||||
+ guard.owner_proc_id
|
||||
+ };
|
||||
+ {
|
||||
+ let mut guard = context.write(token.token());
|
||||
+ guard.groups = groups.clone();
|
||||
+ }
|
||||
+ if let Some(pid) = proc_id {
|
||||
+ let mut contexts = context::contexts(token.downgrade());
|
||||
+ let (contexts, mut t) = contexts.token_split();
|
||||
+ for context_ref in contexts.iter() {
|
||||
+ let mut ctx = context_ref.write(t.token());
|
||||
+ if ctx.owner_proc_id == Some(pid) {
|
||||
+ ctx.groups = groups.clone();
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ Ok(count * size_of::<u32>())
|
||||
+ }
|
||||
ContextHandle::OpenViaDup => {
|
||||
let mut args = buf.usizes();
|
||||
|
||||
@@ -1427,6 +1485,11 @@ impl ContextHandle {
|
||||
|
||||
buf.copy_exactly(crate::cpu_set::mask_as_bytes(&mask))?;
|
||||
Ok(size_of_val(&mask))
|
||||
+ }
|
||||
+ ContextHandle::SchedPolicy => {
|
||||
+ let context = context.read(token.token());
|
||||
+ let data = [context.sched_policy as u8, context.sched_rt_priority];
|
||||
+ buf.copy_common_bytes_from_slice(&data)
|
||||
} // TODO: Replace write() with SYS_SENDFD?
|
||||
ContextHandle::Status { .. } => {
|
||||
let status = {
|
||||
@@ -1475,6 +1538,15 @@ impl ContextHandle {
|
||||
debug_name,
|
||||
})
|
||||
}
|
||||
+ Self::Groups => {
|
||||
+ let c = &context.read(token.token());
|
||||
+ let max = buf.len() / size_of::<u32>();
|
||||
+ let count = c.groups.len().min(max);
|
||||
+ for (chunk, gid) in buf.in_exact_chunks(size_of::<u32>()).zip(&c.groups).take(count) {
|
||||
+ chunk.copy_from_slice(&gid.to_ne_bytes())?;
|
||||
+ }
|
||||
+ Ok(count * size_of::<u32>())
|
||||
+ }
|
||||
ContextHandle::Sighandler => {
|
||||
let data = match context.read(token.token()).sig {
|
||||
Some(ref sig) => SetSighandlerData {
|
||||
-176
@@ -1,176 +0,0 @@
|
||||
diff --git a/src/context/context.rs b/src/context/context.rs
|
||||
index c97c516..8a8b078 100644
|
||||
--- a/src/context/context.rs
|
||||
+++ b/src/context/context.rs
|
||||
@@ -18,7 +18,8 @@ use crate::{
|
||||
cpu_stats,
|
||||
ipi::{ipi, IpiKind, IpiTarget},
|
||||
memory::{
|
||||
- allocate_p2frame, deallocate_p2frame, Enomem, Frame, RaiiFrame, RmmA, RmmArch, PAGE_SIZE,
|
||||
+ allocate_p2frame, deallocate_p2frame, Enomem, Frame, PhysicalAddress, RaiiFrame, RmmA,
|
||||
+ RmmArch, PAGE_SIZE,
|
||||
},
|
||||
percpu::PercpuBlock,
|
||||
scheme::{CallerCtx, FileHandle, SchemeId},
|
||||
@@ -62,6 +63,38 @@ impl Status {
|
||||
}
|
||||
}
|
||||
|
||||
+pub const SCHED_PRIORITY_LEVELS: usize = 40;
|
||||
+pub const DEFAULT_SCHED_OTHER_PRIORITY: usize = 20;
|
||||
+pub const DEFAULT_SCHED_RR_QUANTUM: u128 = 100_000_000;
|
||||
+
|
||||
+#[repr(u8)]
|
||||
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
+pub enum SchedPolicy {
|
||||
+ Fifo = 0,
|
||||
+ RoundRobin = 1,
|
||||
+ Other = 2,
|
||||
+}
|
||||
+
|
||||
+impl SchedPolicy {
|
||||
+ pub fn try_from_raw(raw: u8) -> Option<Self> {
|
||||
+ match raw {
|
||||
+ 0 => Some(Self::Fifo),
|
||||
+ 1 => Some(Self::RoundRobin),
|
||||
+ 2 => Some(Self::Other),
|
||||
+ _ => None,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+pub fn rt_priority_to_kernel_prio(rt_priority: u8) -> usize {
|
||||
+ (SCHED_PRIORITY_LEVELS - 1)
|
||||
+ .saturating_sub((usize::from(rt_priority.min(99)) * (SCHED_PRIORITY_LEVELS - 1)) / 99)
|
||||
+}
|
||||
+
|
||||
+fn clamp_sched_other_prio(prio: usize) -> usize {
|
||||
+ prio.min(SCHED_PRIORITY_LEVELS - 1)
|
||||
+}
|
||||
+
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum HardBlockedReason {
|
||||
/// "SIGSTOP", only procmgr is allowed to switch contexts this state
|
||||
@@ -140,6 +173,17 @@ pub struct Context {
|
||||
pub fmap_ret: Option<Frame>,
|
||||
/// Priority
|
||||
pub prio: usize,
|
||||
+ pub sched_policy: SchedPolicy,
|
||||
+ pub sched_rt_priority: u8,
|
||||
+ pub sched_rr_ticks_consumed: u32,
|
||||
+ pub sched_static_prio: usize,
|
||||
+ pub sched_rr_quantum: u128,
|
||||
+ #[allow(dead_code)]
|
||||
+ pub futex_pi_boost: bool,
|
||||
+ #[allow(dead_code)]
|
||||
+ pub futex_pi_original_prio: usize,
|
||||
+ #[allow(dead_code)]
|
||||
+ pub futex_pi_waiters: Vec<PhysicalAddress>,
|
||||
|
||||
// TODO: id can reappear after wraparound?
|
||||
pub owner_proc_id: Option<NonZeroUsize>,
|
||||
@@ -148,6 +192,8 @@ pub struct Context {
|
||||
pub euid: u32,
|
||||
pub egid: u32,
|
||||
pub pid: usize,
|
||||
+ /// Supplementary group IDs for access control decisions.
|
||||
+ pub groups: Vec<u32>,
|
||||
|
||||
// See [`PreemptGuard`]
|
||||
//
|
||||
@@ -197,13 +243,22 @@ impl Context {
|
||||
files: Arc::new(RwLock::new(FdTbl::new())),
|
||||
userspace: false,
|
||||
fmap_ret: None,
|
||||
- prio: 20,
|
||||
+ prio: DEFAULT_SCHED_OTHER_PRIORITY,
|
||||
+ sched_policy: SchedPolicy::Other,
|
||||
+ sched_rt_priority: 0,
|
||||
+ sched_rr_ticks_consumed: 0,
|
||||
+ sched_static_prio: DEFAULT_SCHED_OTHER_PRIORITY,
|
||||
+ sched_rr_quantum: DEFAULT_SCHED_RR_QUANTUM,
|
||||
+ futex_pi_boost: false,
|
||||
+ futex_pi_original_prio: DEFAULT_SCHED_OTHER_PRIORITY,
|
||||
+ futex_pi_waiters: Vec::new(),
|
||||
being_sigkilled: false,
|
||||
owner_proc_id,
|
||||
|
||||
euid: 0,
|
||||
egid: 0,
|
||||
pid: 0,
|
||||
+ groups: Vec::new(),
|
||||
|
||||
#[cfg(feature = "syscall_debug")]
|
||||
syscall_debug_info: crate::syscall::debug::SyscallDebugInfo::default(),
|
||||
@@ -218,11 +273,47 @@ impl Context {
|
||||
self.preempt_locks == 0
|
||||
}
|
||||
|
||||
+ fn base_sched_prio(&self) -> usize {
|
||||
+ match self.sched_policy {
|
||||
+ SchedPolicy::Other => clamp_sched_other_prio(self.sched_static_prio),
|
||||
+ SchedPolicy::Fifo | SchedPolicy::RoundRobin => {
|
||||
+ rt_priority_to_kernel_prio(self.sched_rt_priority)
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn apply_sched_prio(&mut self) {
|
||||
+ let base_prio = self.base_sched_prio();
|
||||
+ if self.futex_pi_boost {
|
||||
+ self.futex_pi_original_prio = base_prio;
|
||||
+ self.prio = self.prio.min(base_prio);
|
||||
+ } else {
|
||||
+ self.futex_pi_original_prio = base_prio;
|
||||
+ self.prio = base_prio;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn set_sched_other_prio(&mut self, prio: usize) {
|
||||
+ self.sched_static_prio = clamp_sched_other_prio(prio);
|
||||
+ self.apply_sched_prio();
|
||||
+ }
|
||||
+
|
||||
+ pub fn set_sched_policy(&mut self, sched_policy: SchedPolicy, rt_priority: u8) {
|
||||
+ self.sched_policy = sched_policy;
|
||||
+ self.sched_rt_priority = match sched_policy {
|
||||
+ SchedPolicy::Other => 0,
|
||||
+ SchedPolicy::Fifo | SchedPolicy::RoundRobin => rt_priority.min(99),
|
||||
+ };
|
||||
+ self.sched_rr_ticks_consumed = 0;
|
||||
+ self.apply_sched_prio();
|
||||
+ }
|
||||
+
|
||||
/// Block the context, and return true if it was runnable before being blocked
|
||||
pub fn block(&mut self, reason: &'static str) -> bool {
|
||||
if self.status.is_runnable() {
|
||||
self.status = Status::Blocked;
|
||||
self.status_reason = reason;
|
||||
+ self.sched_rr_ticks_consumed = 0;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
@@ -232,6 +323,7 @@ impl Context {
|
||||
pub fn hard_block(&mut self, reason: HardBlockedReason) -> bool {
|
||||
if self.status.is_runnable() {
|
||||
self.status = Status::HardBlocked { reason };
|
||||
+ self.sched_rr_ticks_consumed = 0;
|
||||
|
||||
true
|
||||
} else {
|
||||
@@ -261,6 +353,7 @@ impl Context {
|
||||
if self.status.is_soft_blocked() {
|
||||
self.status = Status::Runnable;
|
||||
self.status_reason = "";
|
||||
+ self.sched_rr_ticks_consumed = 0;
|
||||
|
||||
true
|
||||
} else {
|
||||
@@ -479,6 +572,7 @@ impl Context {
|
||||
uid: self.euid,
|
||||
gid: self.egid,
|
||||
pid: self.pid,
|
||||
+ groups: self.groups.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs
|
||||
index d30272c..9da2b28 100644
|
||||
--- a/src/scheme/mod.rs
|
||||
+++ b/src/scheme/mod.rs
|
||||
@@ -777,6 +777,7 @@ pub struct CallerCtx {
|
||||
pub pid: usize,
|
||||
pub uid: u32,
|
||||
pub gid: u32,
|
||||
+ pub groups: alloc::vec::Vec<u32>,
|
||||
}
|
||||
impl CallerCtx {
|
||||
pub fn filter_uid_gid(self, euid: u32, egid: u32) -> Self {
|
||||
@@ -785,6 +786,7 @@ impl CallerCtx {
|
||||
pid: self.pid,
|
||||
uid: euid,
|
||||
gid: egid,
|
||||
+ groups: self.groups,
|
||||
}
|
||||
} else {
|
||||
self
|
||||
@@ -1,180 +0,0 @@
|
||||
diff --git a/src/context/context.rs b/src/context/context.rs
|
||||
index c97c516..a0814fa 100644
|
||||
--- a/src/context/context.rs
|
||||
+++ b/src/context/context.rs
|
||||
@@ -18,7 +18,8 @@ use crate::{
|
||||
cpu_stats,
|
||||
ipi::{ipi, IpiKind, IpiTarget},
|
||||
memory::{
|
||||
- allocate_p2frame, deallocate_p2frame, Enomem, Frame, RaiiFrame, RmmA, RmmArch, PAGE_SIZE,
|
||||
+ allocate_p2frame, deallocate_p2frame, Enomem, Frame, PhysicalAddress, RaiiFrame, RmmA,
|
||||
+ RmmArch, PAGE_SIZE,
|
||||
},
|
||||
percpu::PercpuBlock,
|
||||
scheme::{CallerCtx, FileHandle, SchemeId},
|
||||
@@ -62,6 +63,38 @@ impl Status {
|
||||
}
|
||||
}
|
||||
|
||||
+pub const SCHED_PRIORITY_LEVELS: usize = 40;
|
||||
+pub const DEFAULT_SCHED_OTHER_PRIORITY: usize = 20;
|
||||
+pub const DEFAULT_SCHED_RR_QUANTUM: u128 = 100_000_000;
|
||||
+
|
||||
+#[repr(u8)]
|
||||
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
+pub enum SchedPolicy {
|
||||
+ Fifo = 0,
|
||||
+ RoundRobin = 1,
|
||||
+ Other = 2,
|
||||
+}
|
||||
+
|
||||
+impl SchedPolicy {
|
||||
+ pub fn try_from_raw(raw: u8) -> Option<Self> {
|
||||
+ match raw {
|
||||
+ 0 => Some(Self::Fifo),
|
||||
+ 1 => Some(Self::RoundRobin),
|
||||
+ 2 => Some(Self::Other),
|
||||
+ _ => None,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+pub fn rt_priority_to_kernel_prio(rt_priority: u8) -> usize {
|
||||
+ (SCHED_PRIORITY_LEVELS - 1)
|
||||
+ .saturating_sub((usize::from(rt_priority.min(99)) * (SCHED_PRIORITY_LEVELS - 1)) / 99)
|
||||
+}
|
||||
+
|
||||
+fn clamp_sched_other_prio(prio: usize) -> usize {
|
||||
+ prio.min(SCHED_PRIORITY_LEVELS - 1)
|
||||
+}
|
||||
+
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum HardBlockedReason {
|
||||
/// "SIGSTOP", only procmgr is allowed to switch contexts this state
|
||||
@@ -140,6 +173,20 @@ pub struct Context {
|
||||
pub fmap_ret: Option<Frame>,
|
||||
/// Priority
|
||||
pub prio: usize,
|
||||
+ pub sched_policy: SchedPolicy,
|
||||
+ pub sched_rt_priority: u8,
|
||||
+ pub sched_rr_ticks_consumed: u32,
|
||||
+ pub sched_static_prio: usize,
|
||||
+pub sched_rr_quantum: u128,
|
||||
+ /// Virtual runtime for SCHED_OTHER fair scheduling.
|
||||
+ /// CPU-bound threads accumulate vruntime faster; I/O-bound stay lower.
|
||||
+ pub vruntime: u128,
|
||||
+ #[allow(dead_code)]
|
||||
+ pub futex_pi_boost: bool,
|
||||
+ #[allow(dead_code)]
|
||||
+ pub futex_pi_original_prio: usize,
|
||||
+ #[allow(dead_code)]
|
||||
+ pub futex_pi_waiters: Vec<PhysicalAddress>,
|
||||
|
||||
// TODO: id can reappear after wraparound?
|
||||
pub owner_proc_id: Option<NonZeroUsize>,
|
||||
@@ -148,6 +195,8 @@ pub struct Context {
|
||||
pub euid: u32,
|
||||
pub egid: u32,
|
||||
pub pid: usize,
|
||||
+ /// Supplementary group IDs for access control decisions.
|
||||
+ pub groups: Vec<u32>,
|
||||
|
||||
// See [`PreemptGuard`]
|
||||
//
|
||||
@@ -197,13 +246,23 @@ impl Context {
|
||||
files: Arc::new(RwLock::new(FdTbl::new())),
|
||||
userspace: false,
|
||||
fmap_ret: None,
|
||||
- prio: 20,
|
||||
+ prio: DEFAULT_SCHED_OTHER_PRIORITY,
|
||||
+ sched_policy: SchedPolicy::Other,
|
||||
+ sched_rt_priority: 0,
|
||||
+ sched_rr_ticks_consumed: 0,
|
||||
+ sched_static_prio: DEFAULT_SCHED_OTHER_PRIORITY,
|
||||
+ sched_rr_quantum: DEFAULT_SCHED_RR_QUANTUM,
|
||||
+ vruntime: 0u128,
|
||||
+ futex_pi_boost: false,
|
||||
+ futex_pi_original_prio: DEFAULT_SCHED_OTHER_PRIORITY,
|
||||
+ futex_pi_waiters: Vec::new(),
|
||||
being_sigkilled: false,
|
||||
owner_proc_id,
|
||||
|
||||
euid: 0,
|
||||
egid: 0,
|
||||
pid: 0,
|
||||
+ groups: Vec::new(),
|
||||
|
||||
#[cfg(feature = "syscall_debug")]
|
||||
syscall_debug_info: crate::syscall::debug::SyscallDebugInfo::default(),
|
||||
@@ -218,11 +277,47 @@ impl Context {
|
||||
self.preempt_locks == 0
|
||||
}
|
||||
|
||||
+ fn base_sched_prio(&self) -> usize {
|
||||
+ match self.sched_policy {
|
||||
+ SchedPolicy::Other => clamp_sched_other_prio(self.sched_static_prio),
|
||||
+ SchedPolicy::Fifo | SchedPolicy::RoundRobin => {
|
||||
+ rt_priority_to_kernel_prio(self.sched_rt_priority)
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn apply_sched_prio(&mut self) {
|
||||
+ let base_prio = self.base_sched_prio();
|
||||
+ if self.futex_pi_boost {
|
||||
+ self.futex_pi_original_prio = base_prio;
|
||||
+ self.prio = self.prio.min(base_prio);
|
||||
+ } else {
|
||||
+ self.futex_pi_original_prio = base_prio;
|
||||
+ self.prio = base_prio;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn set_sched_other_prio(&mut self, prio: usize) {
|
||||
+ self.sched_static_prio = clamp_sched_other_prio(prio);
|
||||
+ self.apply_sched_prio();
|
||||
+ }
|
||||
+
|
||||
+ pub fn set_sched_policy(&mut self, sched_policy: SchedPolicy, rt_priority: u8) {
|
||||
+ self.sched_policy = sched_policy;
|
||||
+ self.sched_rt_priority = match sched_policy {
|
||||
+ SchedPolicy::Other => 0,
|
||||
+ SchedPolicy::Fifo | SchedPolicy::RoundRobin => rt_priority.min(99),
|
||||
+ };
|
||||
+ self.sched_rr_ticks_consumed = 0;
|
||||
+ self.apply_sched_prio();
|
||||
+ }
|
||||
+
|
||||
/// Block the context, and return true if it was runnable before being blocked
|
||||
pub fn block(&mut self, reason: &'static str) -> bool {
|
||||
if self.status.is_runnable() {
|
||||
self.status = Status::Blocked;
|
||||
self.status_reason = reason;
|
||||
+ self.sched_rr_ticks_consumed = 0;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
@@ -232,6 +327,7 @@ impl Context {
|
||||
pub fn hard_block(&mut self, reason: HardBlockedReason) -> bool {
|
||||
if self.status.is_runnable() {
|
||||
self.status = Status::HardBlocked { reason };
|
||||
+ self.sched_rr_ticks_consumed = 0;
|
||||
|
||||
true
|
||||
} else {
|
||||
@@ -261,6 +357,7 @@ impl Context {
|
||||
if self.status.is_soft_blocked() {
|
||||
self.status = Status::Runnable;
|
||||
self.status_reason = "";
|
||||
+ self.sched_rr_ticks_consumed = 0;
|
||||
|
||||
true
|
||||
} else {
|
||||
@@ -479,6 +576,7 @@ impl Context {
|
||||
uid: self.euid,
|
||||
gid: self.egid,
|
||||
pid: self.pid,
|
||||
+ groups: self.groups.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
-196
@@ -1,196 +0,0 @@
|
||||
diff --git a/src/context/context.rs b/src/context/context.rs
|
||||
index c97c516..18fbd7f 100644
|
||||
--- a/src/context/context.rs
|
||||
+++ b/src/context/context.rs
|
||||
@@ -18,7 +18,8 @@ use crate::{
|
||||
cpu_stats,
|
||||
ipi::{ipi, IpiKind, IpiTarget},
|
||||
memory::{
|
||||
- allocate_p2frame, deallocate_p2frame, Enomem, Frame, RaiiFrame, RmmA, RmmArch, PAGE_SIZE,
|
||||
+ allocate_p2frame, deallocate_p2frame, Enomem, Frame, PhysicalAddress, RaiiFrame, RmmA,
|
||||
+ RmmArch, PAGE_SIZE,
|
||||
},
|
||||
percpu::PercpuBlock,
|
||||
scheme::{CallerCtx, FileHandle, SchemeId},
|
||||
@@ -62,6 +63,38 @@ impl Status {
|
||||
}
|
||||
}
|
||||
|
||||
+pub const SCHED_PRIORITY_LEVELS: usize = 40;
|
||||
+pub const DEFAULT_SCHED_OTHER_PRIORITY: usize = 20;
|
||||
+pub const DEFAULT_SCHED_RR_QUANTUM: u128 = 100_000_000;
|
||||
+
|
||||
+#[repr(u8)]
|
||||
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
+pub enum SchedPolicy {
|
||||
+ Fifo = 0,
|
||||
+ RoundRobin = 1,
|
||||
+ Other = 2,
|
||||
+}
|
||||
+
|
||||
+impl SchedPolicy {
|
||||
+ pub fn try_from_raw(raw: u8) -> Option<Self> {
|
||||
+ match raw {
|
||||
+ 0 => Some(Self::Fifo),
|
||||
+ 1 => Some(Self::RoundRobin),
|
||||
+ 2 => Some(Self::Other),
|
||||
+ _ => None,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+pub fn rt_priority_to_kernel_prio(rt_priority: u8) -> usize {
|
||||
+ (SCHED_PRIORITY_LEVELS - 1)
|
||||
+ .saturating_sub((usize::from(rt_priority.min(99)) * (SCHED_PRIORITY_LEVELS - 1)) / 99)
|
||||
+}
|
||||
+
|
||||
+fn clamp_sched_other_prio(prio: usize) -> usize {
|
||||
+ prio.min(SCHED_PRIORITY_LEVELS - 1)
|
||||
+}
|
||||
+
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum HardBlockedReason {
|
||||
/// "SIGSTOP", only procmgr is allowed to switch contexts this state
|
||||
@@ -96,6 +129,7 @@ pub struct Context {
|
||||
pub running: bool,
|
||||
/// Current CPU ID
|
||||
pub cpu_id: Option<LogicalCpuId>,
|
||||
+ pub last_cpu: Option<LogicalCpuId>,
|
||||
/// Time this context was switched to
|
||||
pub switch_time: u128,
|
||||
/// Amount of CPU time used
|
||||
@@ -140,6 +174,20 @@ pub struct Context {
|
||||
pub fmap_ret: Option<Frame>,
|
||||
/// Priority
|
||||
pub prio: usize,
|
||||
+ pub sched_policy: SchedPolicy,
|
||||
+ pub sched_rt_priority: u8,
|
||||
+ pub sched_rr_ticks_consumed: u32,
|
||||
+ pub sched_static_prio: usize,
|
||||
+pub sched_rr_quantum: u128,
|
||||
+ /// Virtual runtime for SCHED_OTHER fair scheduling.
|
||||
+ /// CPU-bound threads accumulate vruntime faster; I/O-bound stay lower.
|
||||
+ pub vruntime: u128,
|
||||
+ #[allow(dead_code)]
|
||||
+ pub futex_pi_boost: bool,
|
||||
+ #[allow(dead_code)]
|
||||
+ pub futex_pi_original_prio: usize,
|
||||
+ #[allow(dead_code)]
|
||||
+ pub futex_pi_waiters: Vec<PhysicalAddress>,
|
||||
|
||||
// TODO: id can reappear after wraparound?
|
||||
pub owner_proc_id: Option<NonZeroUsize>,
|
||||
@@ -148,6 +196,8 @@ pub struct Context {
|
||||
pub euid: u32,
|
||||
pub egid: u32,
|
||||
pub pid: usize,
|
||||
+ /// Supplementary group IDs for access control decisions.
|
||||
+ pub groups: Vec<u32>,
|
||||
|
||||
// See [`PreemptGuard`]
|
||||
//
|
||||
@@ -182,6 +232,7 @@ impl Context {
|
||||
status_reason: "",
|
||||
running: false,
|
||||
cpu_id: None,
|
||||
+ last_cpu: None,
|
||||
switch_time: 0,
|
||||
cpu_time: 0,
|
||||
sched_affinity: LogicalCpuSet::all(),
|
||||
@@ -197,13 +248,23 @@ impl Context {
|
||||
files: Arc::new(RwLock::new(FdTbl::new())),
|
||||
userspace: false,
|
||||
fmap_ret: None,
|
||||
- prio: 20,
|
||||
+ prio: DEFAULT_SCHED_OTHER_PRIORITY,
|
||||
+ sched_policy: SchedPolicy::Other,
|
||||
+ sched_rt_priority: 0,
|
||||
+ sched_rr_ticks_consumed: 0,
|
||||
+ sched_static_prio: DEFAULT_SCHED_OTHER_PRIORITY,
|
||||
+ sched_rr_quantum: DEFAULT_SCHED_RR_QUANTUM,
|
||||
+ vruntime: 0u128,
|
||||
+ futex_pi_boost: false,
|
||||
+ futex_pi_original_prio: DEFAULT_SCHED_OTHER_PRIORITY,
|
||||
+ futex_pi_waiters: Vec::new(),
|
||||
being_sigkilled: false,
|
||||
owner_proc_id,
|
||||
|
||||
euid: 0,
|
||||
egid: 0,
|
||||
pid: 0,
|
||||
+ groups: Vec::new(),
|
||||
|
||||
#[cfg(feature = "syscall_debug")]
|
||||
syscall_debug_info: crate::syscall::debug::SyscallDebugInfo::default(),
|
||||
@@ -218,11 +279,47 @@ impl Context {
|
||||
self.preempt_locks == 0
|
||||
}
|
||||
|
||||
+ fn base_sched_prio(&self) -> usize {
|
||||
+ match self.sched_policy {
|
||||
+ SchedPolicy::Other => clamp_sched_other_prio(self.sched_static_prio),
|
||||
+ SchedPolicy::Fifo | SchedPolicy::RoundRobin => {
|
||||
+ rt_priority_to_kernel_prio(self.sched_rt_priority)
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn apply_sched_prio(&mut self) {
|
||||
+ let base_prio = self.base_sched_prio();
|
||||
+ if self.futex_pi_boost {
|
||||
+ self.futex_pi_original_prio = base_prio;
|
||||
+ self.prio = self.prio.min(base_prio);
|
||||
+ } else {
|
||||
+ self.futex_pi_original_prio = base_prio;
|
||||
+ self.prio = base_prio;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn set_sched_other_prio(&mut self, prio: usize) {
|
||||
+ self.sched_static_prio = clamp_sched_other_prio(prio);
|
||||
+ self.apply_sched_prio();
|
||||
+ }
|
||||
+
|
||||
+ pub fn set_sched_policy(&mut self, sched_policy: SchedPolicy, rt_priority: u8) {
|
||||
+ self.sched_policy = sched_policy;
|
||||
+ self.sched_rt_priority = match sched_policy {
|
||||
+ SchedPolicy::Other => 0,
|
||||
+ SchedPolicy::Fifo | SchedPolicy::RoundRobin => rt_priority.min(99),
|
||||
+ };
|
||||
+ self.sched_rr_ticks_consumed = 0;
|
||||
+ self.apply_sched_prio();
|
||||
+ }
|
||||
+
|
||||
/// Block the context, and return true if it was runnable before being blocked
|
||||
pub fn block(&mut self, reason: &'static str) -> bool {
|
||||
if self.status.is_runnable() {
|
||||
self.status = Status::Blocked;
|
||||
self.status_reason = reason;
|
||||
+ self.sched_rr_ticks_consumed = 0;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
@@ -232,6 +329,7 @@ impl Context {
|
||||
pub fn hard_block(&mut self, reason: HardBlockedReason) -> bool {
|
||||
if self.status.is_runnable() {
|
||||
self.status = Status::HardBlocked { reason };
|
||||
+ self.sched_rr_ticks_consumed = 0;
|
||||
|
||||
true
|
||||
} else {
|
||||
@@ -261,6 +359,7 @@ impl Context {
|
||||
if self.status.is_soft_blocked() {
|
||||
self.status = Status::Runnable;
|
||||
self.status_reason = "";
|
||||
+ self.sched_rr_ticks_consumed = 0;
|
||||
|
||||
true
|
||||
} else {
|
||||
@@ -479,6 +578,7 @@ impl Context {
|
||||
uid: self.euid,
|
||||
gid: self.egid,
|
||||
pid: self.pid,
|
||||
+ groups: self.groups.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,7 +268,8 @@ 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,5 +1220,15 @@ 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,7 +161,18 @@ 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,18 @@ 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,15 +29,17 @@ 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,4 +33,6 @@ 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,3 +432,104 @@ 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;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
diff --git a/src/header/stdlib/cbindgen.toml b/src/header/stdlib/cbindgen.toml
|
||||
--- a/src/header/stdlib/cbindgen.toml
|
||||
+++ b/src/header/stdlib/cbindgen.toml
|
||||
@@ -1,21 +1,5 @@
|
||||
sys_includes = ["stddef.h", "alloca.h", "wchar.h", "features.h"]
|
||||
include_guard = "_RELIBC_STDLIB_H"
|
||||
-trailer = """
|
||||
-#ifndef _RELIBC_STDLIB_EXTRA_H
|
||||
-#define _RELIBC_STDLIB_EXTRA_H
|
||||
-
|
||||
-#ifdef __cplusplus
|
||||
-extern "C" {
|
||||
-#endif
|
||||
-
|
||||
-long double strtold(const char *nptr, char **endptr);
|
||||
-
|
||||
-#ifdef __cplusplus
|
||||
-}
|
||||
-#endif
|
||||
-
|
||||
-#endif
|
||||
-"""
|
||||
language = "C"
|
||||
style = "Type"
|
||||
no_includes = true
|
||||
@@ -1,336 +0,0 @@
|
||||
diff -ruN a/src/header/_aio/mod.rs b/src/header/_aio/mod.rs
|
||||
--- a/src/header/_aio/mod.rs
|
||||
+++ b/src/header/_aio/mod.rs
|
||||
@@ -1,75 +1,283 @@
|
||||
//! `aio.h` implementation.
|
||||
//!
|
||||
-//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/aio.h.html>.
|
||||
+//! Synchronous emulation of POSIX AIO. All operations complete immediately
|
||||
+//! in the calling thread. This provides sufficient compatibility for software
|
||||
+//! (such as Qt6's QIODevice) that uses aio as an optional fallback path.
|
||||
|
||||
-use crate::{
|
||||
- header::{bits_timespec::timespec, signal::sigevent},
|
||||
- platform::types::{c_int, c_void},
|
||||
-};
|
||||
+use core::slice;
|
||||
+
|
||||
+use crate::{
|
||||
+ error::Errno,
|
||||
+ header::{
|
||||
+ bits_timespec::timespec,
|
||||
+ errno::{EFAULT, EINVAL, EINPROGRESS, EIO},
|
||||
+ fcntl::O_SYNC,
|
||||
+ signal::sigevent,
|
||||
+ },
|
||||
+ platform::{
|
||||
+ Sys,
|
||||
+ types::{c_int, c_void, off_t, size_t, ssize_t},
|
||||
+ ERRNO,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+// POSIX lio_listio operation codes
|
||||
+pub const LIO_READ: c_int = 0;
|
||||
+pub const LIO_WRITE: c_int = 1;
|
||||
+pub const LIO_NOP: c_int = 2;
|
||||
+
|
||||
+// lio_listio modes
|
||||
+pub const LIO_WAIT: c_int = 0;
|
||||
+pub const LIO_NOWAIT: c_int = 1;
|
||||
+
|
||||
+// aio_cancel return values
|
||||
+pub const AIO_CANCELED: c_int = 0;
|
||||
+pub const AIO_NOTCANCELED: c_int = 1;
|
||||
+pub const AIO_ALLDONE: c_int = 2;
|
||||
+
|
||||
+// O_DSYNC is not yet defined in relibc's fcntl module.
|
||||
+// Accept it in aio_fsync by matching the Linux x86_64 value.
|
||||
+// TODO: import from fcntl when O_DSYNC is added there.
|
||||
+const _O_DSYNC: c_int = 0x0001_0000;
|
||||
+
|
||||
+// Internal operation states for synchronous emulation
|
||||
+const _AIO_IDLE: c_int = 0;
|
||||
+const _AIO_DONE: c_int = 2;
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/aio.h.html>.
|
||||
+#[repr(C)]
|
||||
pub struct aiocb {
|
||||
pub aio_fildes: c_int,
|
||||
+ pub aio_offset: off_t,
|
||||
pub aio_lio_opcode: c_int,
|
||||
pub aio_reqprio: c_int,
|
||||
pub aio_buf: *mut c_void,
|
||||
- pub aio_nbytes: usize,
|
||||
+ pub aio_nbytes: size_t,
|
||||
pub aio_sigevent: sigevent,
|
||||
+ // Private emulation state
|
||||
+ pub __state: c_int,
|
||||
+ pub __error_code: c_int,
|
||||
+ pub __return_value: ssize_t,
|
||||
}
|
||||
|
||||
-/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_read.html>.
|
||||
-// #[unsafe(no_mangle)]
|
||||
-pub extern "C" fn aio_read(aiocbp: *mut aiocb) -> c_int {
|
||||
- unimplemented!();
|
||||
+/// Perform a synchronous pread and store the result in the aiocb.
|
||||
+///
|
||||
+/// Returns 0 on success, -1 on error (with errno set).
|
||||
+unsafe fn aio_do_read(cb: &mut aiocb) -> c_int {
|
||||
+ let buf = unsafe { slice::from_raw_parts_mut(cb.aio_buf.cast::<u8>(), cb.aio_nbytes) };
|
||||
+ match Sys::pread(cb.aio_fildes, buf, cb.aio_offset) {
|
||||
+ Ok(n) => {
|
||||
+ cb.__error_code = 0;
|
||||
+ cb.__return_value = n as ssize_t;
|
||||
+ cb.__state = _AIO_DONE;
|
||||
+ 0
|
||||
+ }
|
||||
+ Err(Errno(e)) => {
|
||||
+ cb.__error_code = e;
|
||||
+ cb.__return_value = -1;
|
||||
+ cb.__state = _AIO_DONE;
|
||||
+ ERRNO.set(e);
|
||||
+ -1
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
-/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_write.html>.
|
||||
-// #[unsafe(no_mangle)]
|
||||
-pub extern "C" fn aio_write(aiocbp: *mut aiocb) -> c_int {
|
||||
- unimplemented!();
|
||||
+/// Perform a synchronous pwrite and store the result in the aiocb.
|
||||
+///
|
||||
+/// Returns 0 on success, -1 on error (with errno set).
|
||||
+unsafe fn aio_do_write(cb: &mut aiocb) -> c_int {
|
||||
+ let buf = unsafe { slice::from_raw_parts(cb.aio_buf.cast::<u8>(), cb.aio_nbytes) };
|
||||
+ match Sys::pwrite(cb.aio_fildes, buf, cb.aio_offset) {
|
||||
+ Ok(n) => {
|
||||
+ cb.__error_code = 0;
|
||||
+ cb.__return_value = n as ssize_t;
|
||||
+ cb.__state = _AIO_DONE;
|
||||
+ 0
|
||||
+ }
|
||||
+ Err(Errno(e)) => {
|
||||
+ cb.__error_code = e;
|
||||
+ cb.__return_value = -1;
|
||||
+ cb.__state = _AIO_DONE;
|
||||
+ ERRNO.set(e);
|
||||
+ -1
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
-/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lio_listio.html>.
|
||||
-// #[unsafe(no_mangle)]
|
||||
-pub extern "C" fn lio_listio(
|
||||
- mode: c_int,
|
||||
- list: *const *const aiocb,
|
||||
- nent: c_int,
|
||||
- sig: *mut sigevent,
|
||||
-) -> c_int {
|
||||
- unimplemented!();
|
||||
-}
|
||||
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_read.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn aio_read(aiocbp: *mut aiocb) -> c_int {
|
||||
+ if aiocbp.is_null() {
|
||||
+ ERRNO.set(EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ let cb = unsafe { &mut *aiocbp };
|
||||
+ if cb.aio_buf.is_null() && cb.aio_nbytes > 0 {
|
||||
+ ERRNO.set(EFAULT);
|
||||
+ cb.__state = _AIO_DONE;
|
||||
+ cb.__error_code = EFAULT;
|
||||
+ cb.__return_value = -1;
|
||||
+ return -1;
|
||||
+ }
|
||||
+ unsafe { aio_do_read(cb) }
|
||||
+}
|
||||
|
||||
-/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_error.html>.
|
||||
-// #[unsafe(no_mangle)]
|
||||
-pub extern "C" fn aio_error(aiocbp: *const aiocb) -> c_int {
|
||||
- unimplemented!();
|
||||
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_write.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn aio_write(aiocbp: *mut aiocb) -> c_int {
|
||||
+ if aiocbp.is_null() {
|
||||
+ ERRNO.set(EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ let cb = unsafe { &mut *aiocbp };
|
||||
+ if cb.aio_buf.is_null() && cb.aio_nbytes > 0 {
|
||||
+ ERRNO.set(EFAULT);
|
||||
+ cb.__state = _AIO_DONE;
|
||||
+ cb.__error_code = EFAULT;
|
||||
+ cb.__return_value = -1;
|
||||
+ return -1;
|
||||
+ }
|
||||
+ unsafe { aio_do_write(cb) }
|
||||
}
|
||||
|
||||
-/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_return.html>.
|
||||
-// #[unsafe(no_mangle)]
|
||||
-pub extern "C" fn aio_return(aiocbp: *mut aiocb) -> usize {
|
||||
- unimplemented!();
|
||||
-}
|
||||
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_fsync.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn aio_fsync(operation: c_int, aiocbp: *mut aiocb) -> c_int {
|
||||
+ if aiocbp.is_null() {
|
||||
+ ERRNO.set(EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ // Validate operation: O_SYNC from fcntl, or _O_DSYNC (Linux compat value).
|
||||
+ if operation != O_SYNC && operation != _O_DSYNC {
|
||||
+ ERRNO.set(EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ let cb = unsafe { &mut *aiocbp };
|
||||
+ match Sys::fsync(cb.aio_fildes) {
|
||||
+ Ok(()) => {
|
||||
+ cb.__error_code = 0;
|
||||
+ cb.__return_value = 0;
|
||||
+ cb.__state = _AIO_DONE;
|
||||
+ 0
|
||||
+ }
|
||||
+ Err(Errno(e)) => {
|
||||
+ cb.__error_code = e;
|
||||
+ cb.__return_value = -1;
|
||||
+ cb.__state = _AIO_DONE;
|
||||
+ ERRNO.set(e);
|
||||
+ -1
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
|
||||
-/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_cancel.html>.
|
||||
-// #[unsafe(no_mangle)]
|
||||
-pub extern "C" fn aio_cancel(fildes: c_int, aiocbp: *mut aiocb) -> c_int {
|
||||
- unimplemented!();
|
||||
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_error.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn aio_error(aiocbp: *const aiocb) -> c_int {
|
||||
+ if aiocbp.is_null() {
|
||||
+ return EINVAL;
|
||||
+ }
|
||||
+ let cb = unsafe { &*aiocbp };
|
||||
+ match cb.__state {
|
||||
+ _AIO_IDLE => 0, // Never submitted -- no error
|
||||
+ _AIO_DONE => cb.__error_code,
|
||||
+ _ => EINPROGRESS, // Should not occur with sync emulation
|
||||
+ }
|
||||
}
|
||||
|
||||
-/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_suspend.html>.
|
||||
-// #[unsafe(no_mangle)]
|
||||
-pub extern "C" fn aio_suspend(
|
||||
- list: *const *const aiocb,
|
||||
- nent: c_int,
|
||||
- timeout: *const timespec,
|
||||
-) -> c_int {
|
||||
- unimplemented!();
|
||||
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_return.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn aio_return(aiocbp: *mut aiocb) -> ssize_t {
|
||||
+ if aiocbp.is_null() {
|
||||
+ ERRNO.set(EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ let cb = unsafe { &*aiocbp };
|
||||
+ if cb.__state != _AIO_DONE {
|
||||
+ ERRNO.set(EINPROGRESS);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ cb.__return_value
|
||||
}
|
||||
|
||||
-/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_fsync.html>.
|
||||
-// #[unsafe(no_mangle)]
|
||||
-pub extern "C" fn aio_fsync(operation: c_int, aiocbp: *mut aiocb) -> c_int {
|
||||
- unimplemented!();
|
||||
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_suspend.html>.
|
||||
+///
|
||||
+/// With synchronous emulation, all operations are already complete when
|
||||
+/// aio_suspend is called, so this is effectively a no-op that returns 0.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn aio_suspend(
|
||||
+ list: *const *const aiocb,
|
||||
+ nent: c_int,
|
||||
+ timeout: *const timespec,
|
||||
+) -> c_int {
|
||||
+ let _ = timeout;
|
||||
+ if list.is_null() || nent < 0 {
|
||||
+ ERRNO.set(EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ // All operations complete synchronously, so just return success.
|
||||
+ 0
|
||||
+}
|
||||
+
|
||||
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_cancel.html>.
|
||||
+///
|
||||
+/// With synchronous emulation, operations complete before aio_cancel can be
|
||||
+/// called, so this always returns AIO_ALLDONE.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn aio_cancel(fildes: c_int, aiocbp: *mut aiocb) -> c_int {
|
||||
+ if !aiocbp.is_null() {
|
||||
+ let cb = unsafe { &*aiocbp };
|
||||
+ if cb.aio_fildes != fildes {
|
||||
+ ERRNO.set(EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ }
|
||||
+ AIO_ALLDONE
|
||||
+}
|
||||
+
|
||||
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lio_listio.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn lio_listio(
|
||||
+ mode: c_int,
|
||||
+ list: *const *const aiocb,
|
||||
+ nent: c_int,
|
||||
+ sig: *mut sigevent,
|
||||
+) -> c_int {
|
||||
+ let _ = sig;
|
||||
+ if (mode != LIO_WAIT && mode != LIO_NOWAIT) || list.is_null() || nent < 0 {
|
||||
+ ERRNO.set(EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ let mut any_failed = false;
|
||||
+ for i in 0..nent {
|
||||
+ let entry = unsafe { *list.add(i as usize) };
|
||||
+ if entry.is_null() {
|
||||
+ continue;
|
||||
+ }
|
||||
+ let cb = unsafe { &mut *(entry as *mut aiocb) };
|
||||
+ match cb.aio_lio_opcode {
|
||||
+ LIO_READ => {
|
||||
+ if unsafe { aio_read(cb) } != 0 {
|
||||
+ any_failed = true;
|
||||
+ }
|
||||
+ }
|
||||
+ LIO_WRITE => {
|
||||
+ if unsafe { aio_write(cb) } != 0 {
|
||||
+ any_failed = true;
|
||||
+ }
|
||||
+ }
|
||||
+ LIO_NOP => {}
|
||||
+ _ => {
|
||||
+ cb.__state = _AIO_DONE;
|
||||
+ cb.__error_code = EINVAL;
|
||||
+ cb.__return_value = -1;
|
||||
+ ERRNO.set(EINVAL);
|
||||
+ any_failed = true;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if any_failed {
|
||||
+ ERRNO.set(EIO);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ 0
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs
|
||||
index 6204a23..b5847b5 100644
|
||||
--- a/src/sync/barrier.rs
|
||||
+++ b/src/sync/barrier.rs
|
||||
@@ -1,18 +1,34 @@
|
||||
-use core::num::NonZeroU32;
|
||||
+use core::{
|
||||
+ num::NonZeroU32,
|
||||
+ sync::atomic::{AtomicU32, Ordering},
|
||||
+};
|
||||
|
||||
pub struct Barrier {
|
||||
original_count: NonZeroU32,
|
||||
// 4
|
||||
lock: crate::sync::Mutex<Inner>,
|
||||
// 16
|
||||
- cvar: crate::header::pthread::RlctCond,
|
||||
+ cvar: FutexState,
|
||||
// 24
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
- count: u32,
|
||||
- // TODO: Overflows might be problematic... 64-bit?
|
||||
- gen_id: u32,
|
||||
+ _unused0: u32,
|
||||
+ _unused1: u32,
|
||||
+}
|
||||
+
|
||||
+struct FutexState {
|
||||
+ count: AtomicU32,
|
||||
+ sense: AtomicU32,
|
||||
+}
|
||||
+
|
||||
+impl FutexState {
|
||||
+ const fn new(count: u32) -> Self {
|
||||
+ Self {
|
||||
+ count: AtomicU32::new(count),
|
||||
+ sense: AtomicU32::new(0),
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
pub enum WaitResult {
|
||||
@@ -25,61 +41,36 @@ impl Barrier {
|
||||
Self {
|
||||
original_count: count,
|
||||
lock: crate::sync::Mutex::new(Inner {
|
||||
- count: 0,
|
||||
- gen_id: 0,
|
||||
+ _unused0: 0,
|
||||
+ _unused1: 0,
|
||||
}),
|
||||
- cvar: crate::header::pthread::RlctCond::new(),
|
||||
+ cvar: FutexState::new(count.get()),
|
||||
}
|
||||
}
|
||||
pub fn wait(&self) -> WaitResult {
|
||||
- let mut guard = self.lock.lock();
|
||||
- let gen_id = guard.gen_id;
|
||||
-
|
||||
- guard.count += 1;
|
||||
-
|
||||
- if guard.count == self.original_count.get() {
|
||||
- guard.gen_id = guard.gen_id.wrapping_add(1);
|
||||
- guard.count = 0;
|
||||
- if let Ok(()) = self.cvar.broadcast() {}; // TODO handle error
|
||||
+ let _ = &self.lock;
|
||||
+ let sense = self.cvar.sense.load(Ordering::Acquire);
|
||||
|
||||
- drop(guard);
|
||||
+ if self.cvar.count.fetch_sub(1, Ordering::AcqRel) == 1 {
|
||||
+ self.cvar
|
||||
+ .count
|
||||
+ .store(self.original_count.get(), Ordering::Relaxed);
|
||||
+ self.cvar
|
||||
+ .sense
|
||||
+ .store(sense.wrapping_add(1), Ordering::Release);
|
||||
+ crate::sync::futex_wake(&self.cvar.sense, i32::MAX);
|
||||
|
||||
WaitResult::NotifiedAll
|
||||
} else {
|
||||
- while guard.gen_id == gen_id {
|
||||
- guard = self.cvar.wait_inner_typedmutex(guard);
|
||||
- }
|
||||
-
|
||||
- WaitResult::Waited
|
||||
- }
|
||||
- /*
|
||||
- let mut guard = self.lock.lock();
|
||||
- let Inner { count, gen_id } = *guard;
|
||||
-
|
||||
- let last = self.original_count.get() - 1;
|
||||
-
|
||||
- if count == last {
|
||||
- eprintln!("last {:?}", *guard);
|
||||
- guard.gen_id = guard.gen_id.wrapping_add(1);
|
||||
- guard.count = 0;
|
||||
-
|
||||
- drop(guard);
|
||||
-
|
||||
- self.cvar.broadcast();
|
||||
-
|
||||
- WaitResult::NotifiedAll
|
||||
- } else {
|
||||
- guard.count += 1;
|
||||
-
|
||||
- while guard.count != last && guard.gen_id == gen_id {
|
||||
- eprintln!("before {:?}", *guard);
|
||||
- guard = self.cvar.wait_inner_typedmutex(guard);
|
||||
- eprintln!("after {:?}", *guard);
|
||||
+ // SMP fix: wait directly on the barrier generation word instead of routing through the
|
||||
+ // condvar unlock->futex_wait path. If the last thread flips `sense` after we load it
|
||||
+ // but before our futex wait starts, the futex observes a stale value and returns
|
||||
+ // immediately instead of sleeping forever after a missed broadcast wakeup.
|
||||
+ while self.cvar.sense.load(Ordering::Acquire) == sense {
|
||||
+ let _ = crate::sync::futex_wait(&self.cvar.sense, sense, None);
|
||||
}
|
||||
|
||||
WaitResult::Waited
|
||||
}
|
||||
- */
|
||||
}
|
||||
}
|
||||
-static LOCK: crate::sync::Mutex<()> = crate::sync::Mutex::new(());
|
||||
@@ -1,34 +0,0 @@
|
||||
diff --git a/src/header/time/mod.rs b/src/header/time/mod.rs
|
||||
index 1a432bf3..db30e0d7 100644
|
||||
--- a/src/header/time/mod.rs
|
||||
+++ b/src/header/time/mod.rs
|
||||
@@ -7,7 +7,7 @@ use crate::{
|
||||
error::{Errno, ResultExt},
|
||||
header::{
|
||||
bits_timespec::timespec,
|
||||
- errno::{EFAULT, ENOMEM, EOVERFLOW, ETIMEDOUT},
|
||||
+ errno::{EFAULT, EINVAL, ENOMEM, EOVERFLOW, ETIMEDOUT},
|
||||
signal::sigevent,
|
||||
stdlib::getenv,
|
||||
unistd::readlink,
|
||||
@@ -280,7 +280,19 @@ pub extern "C" fn clock_nanosleep(
|
||||
rqtp: *const timespec,
|
||||
rmtp: *mut timespec,
|
||||
) -> c_int {
|
||||
- unimplemented!();
|
||||
+ match clock_id {
|
||||
+ CLOCK_REALTIME | CLOCK_MONOTONIC => {
|
||||
+ if flags == TIMER_ABSTIME {
|
||||
+ return EINVAL;
|
||||
+ }
|
||||
+ match unsafe { Sys::nanosleep(rqtp, rmtp) } {
|
||||
+ Ok(()) => 0,
|
||||
+ Err(Errno(ETIMEDOUT)) => ETIMEDOUT,
|
||||
+ Err(Errno(e)) => e,
|
||||
+ }
|
||||
+ }
|
||||
+ _ => EINVAL,
|
||||
+ }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html>.
|
||||
-396
@@ -1,396 +0,0 @@
|
||||
diff --git a/src/header/netdb/lookup.rs b/src/header/netdb/lookup.rs
|
||||
index aaaaaaa..bbbbbbb 100644
|
||||
--- a/src/header/netdb/lookup.rs
|
||||
+++ b/src/header/netdb/lookup.rs
|
||||
@@ -14,7 +14,7 @@ use crate::header::{
|
||||
bits_socklen_t::socklen_t,
|
||||
bits_timespec::timespec,
|
||||
errno::*,
|
||||
- netinet_in::{IPPROTO_UDP, in_addr, sockaddr_in},
|
||||
+ netinet_in::{IPPROTO_UDP, in6_addr, in_addr, sockaddr_in},
|
||||
sys_socket::{
|
||||
self,
|
||||
constants::{AF_INET, SOCK_DGRAM, SOL_SOCKET, SO_RCVTIMEO},
|
||||
@@ -30,6 +30,7 @@ use super::{
|
||||
};
|
||||
|
||||
pub type LookupHost = Vec<in_addr>;
|
||||
+pub type LookupHostV6 = Vec<in6_addr>;
|
||||
|
||||
pub fn lookup_host(host: &str) -> Result<LookupHost, c_int> {
|
||||
if let Some(host_direct_addr) = parse_ipv4_string(host) {
|
||||
@@ -157,6 +158,123 @@ pub fn lookup_host(host: &str) -> Result<LookupHost, c_int> {
|
||||
}
|
||||
}
|
||||
|
||||
+/// Look up IPv6 (AAAA) addresses for a host via DNS.
|
||||
+pub fn lookup_host_v6(host: &str) -> Result<LookupHostV6, c_int> {
|
||||
+ if let Some(addr6) = parse_ipv6_string(host) {
|
||||
+ return Ok(vec![addr6]);
|
||||
+ }
|
||||
+
|
||||
+ let dns_string = get_dns_server().map_err(|e| e.0)?;
|
||||
+
|
||||
+ if let Some(dns_addr) = parse_ipv4_string(&dns_string) {
|
||||
+ let mut timespec = timespec::default();
|
||||
+ if let Ok(()) = Sys::clock_gettime(
|
||||
+ time::constants::CLOCK_REALTIME,
|
||||
+ Out::from_mut(&mut timespec),
|
||||
+ ) {};
|
||||
+ let tid = (timespec.tv_nsec >> 16) as u16;
|
||||
+
|
||||
+ let packet = Dns {
|
||||
+ transaction_id: tid,
|
||||
+ flags: 0x0100,
|
||||
+ queries: vec![DnsQuery {
|
||||
+ name: host.to_string(),
|
||||
+ q_type: 0x001c,
|
||||
+ q_class: 0x0001,
|
||||
+ }],
|
||||
+ 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),
|
||||
+ }
|
||||
+ } else {
|
||||
+ Err(EINVAL)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
pub fn lookup_addr(addr: in_addr) -> Result<Vec<Vec<u8>>, c_int> {
|
||||
let dns_string = get_dns_server().map_err(|e| e.0)?;
|
||||
|
||||
@@ -282,6 +400,23 @@ pub fn parse_ipv4_string(ip_string: &str) -> Option<u32> {
|
||||
Some(u32::from_ne_bytes(dns_arr))
|
||||
}
|
||||
|
||||
+pub fn parse_ipv6_string(ip_string: &str) -> Option<in6_addr> {
|
||||
+ let trimmed = ip_string.trim();
|
||||
+
|
||||
+ let s = if trimmed.starts_with('[') && trimmed.ends_with(']') {
|
||||
+ &trimmed[1..trimmed.len() - 1]
|
||||
+ } else {
|
||||
+ trimmed
|
||||
+ };
|
||||
+
|
||||
+ let ip: core::net::Ipv6Addr = s.parse().ok()?;
|
||||
+ let mut addr = in6_addr {
|
||||
+ s6_addr: [0u8; 16],
|
||||
+ };
|
||||
+ addr.s6_addr.copy_from_slice(&ip.octets());
|
||||
+ Some(addr)
|
||||
+}
|
||||
+
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::str;
|
||||
diff --git a/src/header/netdb/mod.rs b/src/header/netdb/mod.rs
|
||||
index ccccccc..ddddddd 100644
|
||||
--- a/src/header/netdb/mod.rs
|
||||
+++ b/src/header/netdb/mod.rs
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
mod dns;
|
||||
|
||||
-use core::{cell::Cell, fmt::Write, mem, net::Ipv4Addr, ptr, str};
|
||||
+use core::{cell::Cell, fmt::Write, mem, net::Ipv4Addr, net::Ipv6Addr, ptr, str};
|
||||
|
||||
use alloc::{boxed::Box, str::SplitWhitespace, string::ToString, vec::Vec};
|
||||
|
||||
@@ -18,10 +18,10 @@ use crate::{
|
||||
bits_socklen_t::socklen_t,
|
||||
errno::*,
|
||||
fcntl::O_RDONLY,
|
||||
- netinet_in::{in_addr, sockaddr_in, sockaddr_in6},
|
||||
+ netinet_in::{in6_addr, in_addr, sockaddr_in, sockaddr_in6},
|
||||
stdlib::atoi,
|
||||
strings::strcasecmp,
|
||||
- sys_socket::{constants::AF_INET, sockaddr},
|
||||
+ sys_socket::{constants::{AF_INET, AF_INET6, AF_UNSPEC}, sockaddr},
|
||||
unistd::SEEK_SET,
|
||||
},
|
||||
platform::{
|
||||
@@ -871,11 +871,16 @@ pub unsafe extern "C" fn getaddrinfo(
|
||||
hints_opt
|
||||
);
|
||||
|
||||
+ let requested_family = hints_opt.map_or(AF_UNSPEC, |hints| hints.ai_family);
|
||||
+
|
||||
+ if requested_family != AF_INET && requested_family != AF_INET6 && requested_family != AF_UNSPEC
|
||||
+ {
|
||||
+ return EAI_FAMILY;
|
||||
+ }
|
||||
+
|
||||
//TODO: Use hints
|
||||
let mut ai_flags = hints_opt.map_or(0, |hints| hints.ai_flags);
|
||||
- let mut ai_family; // = hints_opt.map_or(AF_UNSPEC, |hints| hints.ai_family);
|
||||
let ai_socktype = hints_opt.map_or(0, |hints| hints.ai_socktype);
|
||||
- let mut ai_protocol; // = hints_opt.map_or(0, |hints| hints.ai_protocol);
|
||||
|
||||
unsafe { *res = ptr::null_mut() };
|
||||
|
||||
@@ -896,31 +901,52 @@ pub unsafe extern "C" fn getaddrinfo(
|
||||
}
|
||||
});
|
||||
|
||||
- let lookuphost = if ai_flags & AI_NUMERICHOST > 0 {
|
||||
- match parse_ipv4_string(unsafe { str::from_utf8_unchecked(node.to_bytes()) }) {
|
||||
- Some(s_addr) => vec![in_addr { s_addr }],
|
||||
- None => {
|
||||
- return EAI_NONAME;
|
||||
+ let node_str = unsafe { str::from_utf8_unchecked(node.to_bytes()) };
|
||||
+
|
||||
+ let want_inet4 = requested_family == AF_INET || requested_family == AF_UNSPEC;
|
||||
+ let want_inet6 = requested_family == AF_INET6 || requested_family == AF_UNSPEC;
|
||||
+
|
||||
+ let lookuphost_v4: Vec<in_addr> = if want_inet4 {
|
||||
+ if ai_flags & AI_NUMERICHOST > 0 {
|
||||
+ match parse_ipv4_string(node_str) {
|
||||
+ Some(s_addr) => vec![in_addr { s_addr }],
|
||||
+ None => vec![],
|
||||
+ }
|
||||
+ } else {
|
||||
+ match lookup_host(node_str) {
|
||||
+ Ok(addrs) => addrs,
|
||||
+ Err(_) => vec![],
|
||||
}
|
||||
}
|
||||
} else {
|
||||
- match lookup_host(unsafe { str::from_utf8_unchecked(node.to_bytes()) }) {
|
||||
- Ok(lookuphost) => lookuphost,
|
||||
- Err(e) => {
|
||||
- platform::ERRNO.set(e);
|
||||
- return EAI_SYSTEM;
|
||||
+ vec![]
|
||||
+ };
|
||||
+
|
||||
+ let lookuphost_v6: Vec<in6_addr> = if want_inet6 {
|
||||
+ if ai_flags & AI_NUMERICHOST > 0 {
|
||||
+ match parse_ipv6_string(node_str) {
|
||||
+ Some(addr) => vec![addr],
|
||||
+ None => vec![],
|
||||
+ }
|
||||
+ } else {
|
||||
+ match lookup_host_v6(node_str) {
|
||||
+ Ok(addrs) => addrs,
|
||||
+ Err(_) => vec![],
|
||||
}
|
||||
}
|
||||
+ } else {
|
||||
+ vec![]
|
||||
};
|
||||
|
||||
- for in_addr in lookuphost {
|
||||
- ai_family = AF_INET;
|
||||
- ai_protocol = 0;
|
||||
+ if lookuphost_v4.is_empty() && lookuphost_v6.is_empty() {
|
||||
+ return EAI_NONAME;
|
||||
+ }
|
||||
|
||||
+ for addr in lookuphost_v4 {
|
||||
let ai_addr = Box::into_raw(Box::new(sockaddr_in {
|
||||
- sin_family: ai_family as sa_family_t,
|
||||
+ sin_family: AF_INET as sa_family_t,
|
||||
sin_port: htons(port),
|
||||
- sin_addr: in_addr,
|
||||
+ sin_addr: addr,
|
||||
sin_zero: [0; 8],
|
||||
}))
|
||||
.cast::<sockaddr>();
|
||||
@@ -939,9 +965,53 @@ pub unsafe extern "C" fn getaddrinfo(
|
||||
|
||||
let addrinfo = Box::new(addrinfo {
|
||||
ai_flags: 0,
|
||||
- ai_family,
|
||||
+ ai_family: AF_INET,
|
||||
ai_socktype,
|
||||
- ai_protocol,
|
||||
+ ai_protocol: 0,
|
||||
+ ai_addrlen,
|
||||
+ ai_canonname,
|
||||
+ ai_addr,
|
||||
+ ai_next: ptr::null_mut(),
|
||||
+ });
|
||||
+ unsafe {
|
||||
+ let mut indirect = res;
|
||||
+ while !(*indirect).is_null() {
|
||||
+ indirect = &raw mut (**indirect).ai_next;
|
||||
+ }
|
||||
+ *indirect = Box::into_raw(addrinfo)
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ for addr in lookuphost_v6 {
|
||||
+ let mut s6_addr = [0u8; 16];
|
||||
+ s6_addr.copy_from_slice(&addr.s6_addr);
|
||||
+
|
||||
+ let ai_addr = Box::into_raw(Box::new(sockaddr_in6 {
|
||||
+ sin6_family: AF_INET6 as sa_family_t,
|
||||
+ sin6_port: htons(port),
|
||||
+ sin6_flowinfo: 0,
|
||||
+ sin6_addr: in6_addr { s6_addr },
|
||||
+ sin6_scope_id: 0,
|
||||
+ }))
|
||||
+ .cast::<sockaddr>();
|
||||
+
|
||||
+ let ai_addrlen = mem::size_of::<sockaddr_in6>() as socklen_t;
|
||||
+
|
||||
+ let ai_canonname = if ai_flags & AI_CANONNAME > 0 {
|
||||
+ if node_opt.is_none() {
|
||||
+ return EAI_BADFLAGS;
|
||||
+ }
|
||||
+ ai_flags &= !AI_CANONNAME;
|
||||
+ node.to_owned_cstring().into_raw()
|
||||
+ } else {
|
||||
+ ptr::null_mut()
|
||||
+ };
|
||||
+
|
||||
+ let addrinfo = Box::new(addrinfo {
|
||||
+ ai_flags: 0,
|
||||
+ ai_family: AF_INET6,
|
||||
+ ai_socktype,
|
||||
+ ai_protocol: 0,
|
||||
ai_addrlen,
|
||||
ai_canonname,
|
||||
ai_addr,
|
||||
@@ -970,10 +1040,56 @@ pub unsafe extern "C" fn getnameinfo(
|
||||
servlen: socklen_t,
|
||||
flags: c_int,
|
||||
) -> c_int {
|
||||
- if addr.is_null() || addrlen as usize != mem::size_of::<sockaddr_in>() {
|
||||
+ if addr.is_null() {
|
||||
+ return EAI_FAMILY;
|
||||
+ }
|
||||
+
|
||||
+ let addrlen_usize = addrlen as usize;
|
||||
+ if addrlen_usize != mem::size_of::<sockaddr_in>()
|
||||
+ && addrlen_usize != mem::size_of::<sockaddr_in6>()
|
||||
+ {
|
||||
return EAI_FAMILY;
|
||||
}
|
||||
|
||||
+ if addrlen_usize == mem::size_of::<sockaddr_in6>() {
|
||||
+ let sa = unsafe { &*(addr.cast::<sockaddr_in6>()) };
|
||||
+
|
||||
+ if !serv.is_null() && servlen > 0 {
|
||||
+ if flags & NI_NUMERICSERV != 0 {
|
||||
+ let port_str = sa.sin6_port.to_be().to_string();
|
||||
+ let port_bytes = port_str.as_bytes();
|
||||
+ if (servlen as usize) <= port_bytes.len() {
|
||||
+ return EAI_MEMORY;
|
||||
+ }
|
||||
+ unsafe {
|
||||
+ ptr::copy_nonoverlapping(
|
||||
+ port_bytes.as_ptr().cast::<c_char>(),
|
||||
+ serv,
|
||||
+ port_bytes.len(),
|
||||
+ )
|
||||
+ };
|
||||
+ unsafe { *serv.add(port_bytes.len()) = 0 };
|
||||
+ } else {
|
||||
+ unsafe { *serv = 0 };
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if !host.is_null() && hostlen > 0 {
|
||||
+ let ip_addr = Ipv6Addr::from(sa.sin6_addr.s6_addr);
|
||||
+ let ip_str = ip_addr.to_string();
|
||||
+ let ip_bytes = ip_str.as_bytes();
|
||||
+ if (hostlen as usize) <= ip_bytes.len() {
|
||||
+ return EAI_MEMORY;
|
||||
+ }
|
||||
+ unsafe {
|
||||
+ ptr::copy_nonoverlapping(ip_bytes.as_ptr().cast::<c_char>(), host, ip_bytes.len())
|
||||
+ };
|
||||
+ unsafe { *host.add(ip_bytes.len()) = 0 };
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
let sa = unsafe { &*(addr.cast::<sockaddr_in>()) };
|
||||
|
||||
if !serv.is_null() && servlen > 0 {
|
||||
-722
@@ -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,7 +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,10 +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,12 +25,120 @@ 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,28 @@ 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,91 +192,22 @@ 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,92 +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,35 @@ 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,5 +996,5 @@ 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,25 +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
|
||||
@@ -273,7 +273,20 @@
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dup.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub extern "C" fn dup3(fildes: c_int, fildes2: c_int, flag: c_int) -> c_int {
|
||||
- unimplemented!();
|
||||
+ // dup3 requires fildes != fildes2 (unlike dup2 which is a no-op in that case)
|
||||
+ if fildes == fildes2 {
|
||||
+ ERRNO.set(EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ match Sys::dup2(fildes, fildes2) {
|
||||
+ Ok(newfd) => {
|
||||
+ if flag & fcntl::O_CLOEXEC != 0 {
|
||||
+ let _ = Sys::fcntl(newfd, fcntl::F_SETFD, fcntl::FD_CLOEXEC as c_ulonglong);
|
||||
+ }
|
||||
+ newfd
|
||||
+ }
|
||||
+ Err(Errno(e)) => { ERRNO.set(e); -1 }
|
||||
+ }
|
||||
}
|
||||
|
||||
// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/encrypt.html>.
|
||||
@@ -1,14 +0,0 @@
|
||||
diff --git a/src/header/elf/mod.rs b/src/header/elf/mod.rs
|
||||
--- a/src/header/elf/mod.rs
|
||||
+++ b/src/header/elf/mod.rs
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
pub type Elf32_Word = uint32_t;
|
||||
pub type Elf32_Sword = int32_t;
|
||||
-pub type Elf64_Word = uint64_t;
|
||||
-pub type Elf64_Sword = int64_t;
|
||||
+pub type Elf64_Word = uint32_t;
|
||||
+pub type Elf64_Sword = int32_t;
|
||||
|
||||
pub type Elf32_Xword = uint64_t;
|
||||
pub type Elf32_Sxword = int64_t;
|
||||
@@ -1,23 +0,0 @@
|
||||
--- a/src/header/mod.rs
|
||||
+++ b/src/header/mod.rs
|
||||
@@ -91,6 +91,7 @@
|
||||
// TODO: stropts.h (deprecated)
|
||||
pub mod sys_auxv;
|
||||
pub mod sys_epoll;
|
||||
+pub mod sys_eventfd;
|
||||
pub mod sys_file;
|
||||
pub mod sys_ioctl;
|
||||
// TODO: sys/ipc.h
|
||||
@@ -113,9 +114,11 @@
|
||||
pub mod arch_aarch64_user;
|
||||
pub mod arch_riscv64_user;
|
||||
pub mod arch_x64_user;
|
||||
+pub mod sys_signalfd;
|
||||
#[cfg(not(target_arch = "x86"))] // TODO: x86
|
||||
pub mod sys_procfs;
|
||||
pub mod sys_random;
|
||||
+pub mod sys_timerfd;
|
||||
pub mod sys_syslog;
|
||||
pub mod sys_types;
|
||||
#[allow(non_camel_case_types)]
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
diff --git a/src/platform/redox/exec.rs b/src/platform/redox/exec.rs
|
||||
index 3590413c..1b4b96bb 100644
|
||||
--- a/src/platform/redox/exec.rs
|
||||
+++ b/src/platform/redox/exec.rs
|
||||
@@ -127,18 +127,22 @@ pub fn execve(
|
||||
// TODO: At some point we might have capabilities limiting the ability to allocate
|
||||
// executable memory.
|
||||
|
||||
- let Resugid { ruid, rgid, .. } = redox_rt::sys::posix_getresugid();
|
||||
-
|
||||
- let mode = if ruid == stat.st_uid {
|
||||
- (stat.st_mode >> 3 * 2) & 0o7
|
||||
- } else if rgid == stat.st_gid {
|
||||
- (stat.st_mode >> 3 * 1) & 0o7
|
||||
- } else {
|
||||
- stat.st_mode & 0o7
|
||||
- };
|
||||
+ let Resugid { ruid, euid, rgid, .. } = redox_rt::sys::posix_getresugid();
|
||||
+
|
||||
+ // Root (uid 0) bypasses execute permission checks, matching Linux behavior.
|
||||
+ // Check both ruid and euid since Linux checks the effective UID.
|
||||
+ if ruid != 0 && euid != 0 {
|
||||
+ let mode = if ruid == stat.st_uid {
|
||||
+ (stat.st_mode >> 3 * 2) & 0o7
|
||||
+ } else if rgid == stat.st_gid {
|
||||
+ (stat.st_mode >> 3 * 1) & 0o7
|
||||
+ } else {
|
||||
+ stat.st_mode & 0o7
|
||||
+ };
|
||||
|
||||
- if mode & 0o1 == 0o0 {
|
||||
- return Err(Error::new(EPERM));
|
||||
+ if mode & 0o1 == 0o0 {
|
||||
+ return Err(Error::new(EACCES));
|
||||
+ }
|
||||
}
|
||||
|
||||
let cwd: Box<[u8]> = super::path::clone_cwd().unwrap_or_default().into();
|
||||
@@ -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,210 @@
|
||||
|
||||
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,45 +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
|
||||
@@ -537,8 +537,39 @@
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getentropy.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
-pub extern "C" fn getentropy(buffer: *mut c_void, length: size_t) -> c_int {
|
||||
- unimplemented!();
|
||||
+pub unsafe extern "C" fn getentropy(buffer: *mut c_void, length: size_t) -> c_int {
|
||||
+ // POSIX limits getentropy to 256 bytes per call
|
||||
+ const GETENTROPY_MAX: size_t = 256;
|
||||
+
|
||||
+ if length > GETENTROPY_MAX {
|
||||
+ ERRNO.set(EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ let path = unsafe { CStr::from_ptr(c"/scheme/rand".as_ptr().cast()) };
|
||||
+ let fd = match Sys::open(path, fcntl::O_RDONLY, 0) {
|
||||
+ Ok(fd) => fd,
|
||||
+ Err(Errno(e)) => {
|
||||
+ ERRNO.set(e);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ let buf = unsafe { slice::from_raw_parts_mut(buffer.cast::<u8>(), length) };
|
||||
+ let mut filled = 0usize;
|
||||
+ while filled < length {
|
||||
+ match Sys::read(fd, &mut buf[filled..]) {
|
||||
+ Ok(0) => break,
|
||||
+ Ok(n) => filled += n,
|
||||
+ Err(Errno(e)) => {
|
||||
+ let _ = Sys::close(fd);
|
||||
+ ERRNO.set(e);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ let _ = Sys::close(fd);
|
||||
+ if filled < length { ERRNO.set(errno::EIO); -1 } else { 0 }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/geteuid.html>.
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs
|
||||
index 752339a..30668f9 100644
|
||||
--- a/src/platform/redox/mod.rs
|
||||
+++ b/src/platform/redox/mod.rs
|
||||
@@ -749,8 +749,16 @@ impl Pal for Sys {
|
||||
Err(Errno(EPERM))
|
||||
}
|
||||
|
||||
- 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 +921,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 +945,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) };
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
diff --git a/src/header/mod.rs b/src/header/mod.rs
|
||||
index 4bdb6b1..9d1da43 100644
|
||||
--- a/src/header/mod.rs
|
||||
+++ b/src/header/mod.rs
|
||||
@@ -76,7 +76,7 @@ pub mod setjmp;
|
||||
pub mod sgtty;
|
||||
pub mod shadow;
|
||||
pub mod signal;
|
||||
-// TODO: spawn.h
|
||||
+pub mod spawn;
|
||||
// TODO: stdalign.h (likely C implementation)
|
||||
// stdarg.h implemented in C
|
||||
// stdatomic.h implemented in C
|
||||
@@ -93,20 +93,17 @@ pub mod sys_auxv;
|
||||
pub mod sys_epoll;
|
||||
pub mod sys_file;
|
||||
pub mod sys_ioctl;
|
||||
-// TODO: sys/ipc.h
|
||||
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_socket;
|
||||
pub mod sys_stat;
|
||||
pub mod sys_statvfs;
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub mod sys_syscall;
|
||||
pub mod sys_time;
|
||||
+pub mod sys_timerfd;
|
||||
#[deprecated]
|
||||
pub mod sys_timeb;
|
||||
//pub mod sys_times;
|
||||
@@ -128,7 +125,7 @@ pub mod tar;
|
||||
// TODO: term.h (deprecated)
|
||||
pub mod termios;
|
||||
// TODO: tgmath.h (likely C implementation)
|
||||
-// TODO: threads.h
|
||||
+pub mod threads;
|
||||
pub mod time;
|
||||
// TODO: uchar.h
|
||||
// TODO: ucontext.h (deprecated)
|
||||
@@ -1,249 +0,0 @@
|
||||
diff --git a/src/header/ifaddrs/mod.rs b/src/header/ifaddrs/mod.rs
|
||||
index bddb69b..e77a69c 100644
|
||||
--- a/src/header/ifaddrs/mod.rs
|
||||
+++ b/src/header/ifaddrs/mod.rs
|
||||
@@ -3,7 +3,13 @@
|
||||
//! Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getifaddrs.3.html>.
|
||||
|
||||
use crate::{
|
||||
- header::{errno, stdlib, sys_socket::sockaddr},
|
||||
+ header::{
|
||||
+ errno,
|
||||
+ net_if::interface_entries,
|
||||
+ netinet_in::{in_addr, sockaddr_in},
|
||||
+ stdlib,
|
||||
+ sys_socket::{constants::AF_INET, sockaddr},
|
||||
+ },
|
||||
platform::{
|
||||
self,
|
||||
types::{c_char, c_int, c_uint, c_void},
|
||||
@@ -27,6 +33,83 @@ pub struct ifaddrs {
|
||||
ifa_data: *mut c_void,
|
||||
}
|
||||
|
||||
+fn ipv4_addr(bytes: [u8; 4]) -> sockaddr_in {
|
||||
+ sockaddr_in {
|
||||
+ sin_family: AF_INET as _,
|
||||
+ sin_port: 0,
|
||||
+ sin_addr: in_addr {
|
||||
+ s_addr: u32::from_ne_bytes(bytes),
|
||||
+ },
|
||||
+ sin_zero: [0; 8],
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn align_up(offset: usize, align: usize) -> usize {
|
||||
+ (offset + (align - 1)) & !(align - 1)
|
||||
+}
|
||||
+
|
||||
+unsafe fn make_ifaddrs_node(entry: &crate::header::net_if::InterfaceEntry) -> *mut ifaddrs {
|
||||
+ let name_len = entry.name.to_bytes_with_nul().len();
|
||||
+ let addr_size = if entry.addr.is_some() { core::mem::size_of::<sockaddr_in>() } else { 0 };
|
||||
+ let netmask_size = if entry.netmask.is_some() { core::mem::size_of::<sockaddr_in>() } else { 0 };
|
||||
+ let addr_align = core::mem::align_of::<sockaddr_in>();
|
||||
+ let mut total = core::mem::size_of::<ifaddrs>() + name_len;
|
||||
+ if addr_size != 0 {
|
||||
+ total = align_up(total, addr_align) + addr_size;
|
||||
+ }
|
||||
+ if netmask_size != 0 {
|
||||
+ total = align_up(total, addr_align) + netmask_size;
|
||||
+ }
|
||||
+ let raw = unsafe { stdlib::malloc(total) } as *mut u8;
|
||||
+ if raw.is_null() {
|
||||
+ return core::ptr::null_mut();
|
||||
+ }
|
||||
+
|
||||
+ unsafe { raw.write_bytes(0, total) };
|
||||
+
|
||||
+ let node = raw.cast::<ifaddrs>();
|
||||
+ let mut cursor = unsafe { raw.add(core::mem::size_of::<ifaddrs>()) };
|
||||
+
|
||||
+ let name_ptr = cursor.cast::<c_char>();
|
||||
+ unsafe { core::ptr::copy_nonoverlapping(entry.name.as_ptr(), name_ptr, name_len) };
|
||||
+ cursor = unsafe { cursor.add(name_len) };
|
||||
+
|
||||
+ let addr_ptr = if let Some(addr) = entry.addr {
|
||||
+ let aligned = align_up(cursor as usize, addr_align);
|
||||
+ cursor = aligned as *mut u8;
|
||||
+ let ptr = cursor.cast::<sockaddr_in>();
|
||||
+ unsafe { ptr.write(ipv4_addr(addr)) };
|
||||
+ cursor = unsafe { cursor.add(core::mem::size_of::<sockaddr_in>()) };
|
||||
+ ptr.cast::<sockaddr>()
|
||||
+ } else {
|
||||
+ core::ptr::null_mut()
|
||||
+ };
|
||||
+
|
||||
+ let netmask_ptr = if let Some(netmask) = entry.netmask {
|
||||
+ let aligned = align_up(cursor as usize, addr_align);
|
||||
+ cursor = aligned as *mut u8;
|
||||
+ let ptr = cursor.cast::<sockaddr_in>();
|
||||
+ unsafe { ptr.write(ipv4_addr(netmask)) };
|
||||
+ ptr.cast::<sockaddr>()
|
||||
+ } else {
|
||||
+ core::ptr::null_mut()
|
||||
+ };
|
||||
+
|
||||
+ unsafe {
|
||||
+ node.write(ifaddrs {
|
||||
+ ifa_next: core::ptr::null_mut(),
|
||||
+ ifa_name: name_ptr,
|
||||
+ ifa_flags: entry.flags,
|
||||
+ ifa_addr: addr_ptr,
|
||||
+ ifa_netmask: netmask_ptr,
|
||||
+ ifa_ifu: ifaddrs_ifa_ifu { ifu_broadaddr: core::ptr::null_mut() },
|
||||
+ ifa_data: core::ptr::null_mut(),
|
||||
+ })
|
||||
+ };
|
||||
+
|
||||
+ node
|
||||
+}
|
||||
+
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn freeifaddrs(mut ifa: *mut ifaddrs) {
|
||||
while !ifa.is_null() {
|
||||
@@ -38,7 +121,31 @@ pub unsafe extern "C" fn freeifaddrs(mut ifa: *mut ifaddrs) {
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn getifaddrs(ifap: *mut *mut ifaddrs) -> c_int {
|
||||
- //TODO: implement getifaddrs
|
||||
- platform::ERRNO.set(errno::ENOSYS);
|
||||
- -1
|
||||
+ if ifap.is_null() {
|
||||
+ platform::ERRNO.set(errno::EFAULT);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ let entries = interface_entries();
|
||||
+ let mut head = core::ptr::null_mut();
|
||||
+ let mut prev: *mut ifaddrs = core::ptr::null_mut();
|
||||
+
|
||||
+ for entry in &entries {
|
||||
+ let node = unsafe { make_ifaddrs_node(entry) };
|
||||
+ if node.is_null() {
|
||||
+ unsafe { freeifaddrs(head) };
|
||||
+ platform::ERRNO.set(errno::ENOMEM);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ if head.is_null() {
|
||||
+ head = node;
|
||||
+ } else {
|
||||
+ unsafe { (*prev).ifa_next = node };
|
||||
+ }
|
||||
+ prev = node;
|
||||
+ }
|
||||
+
|
||||
+ unsafe { *ifap = head };
|
||||
+ 0
|
||||
}
|
||||
diff --git a/src/header/net_if/mod.rs b/src/header/net_if/mod.rs
|
||||
index edbfede..9ae1b02 100644
|
||||
--- a/src/header/net_if/mod.rs
|
||||
+++ b/src/header/net_if/mod.rs
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
use core::ptr::null;
|
||||
|
||||
+extern crate alloc;
|
||||
+
|
||||
use crate::{
|
||||
c_str::CStr,
|
||||
platform::{
|
||||
@@ -21,15 +23,56 @@ pub struct if_nameindex {
|
||||
if_name: *const c_char,
|
||||
}
|
||||
|
||||
+#[derive(Clone)]
|
||||
+pub(crate) struct InterfaceEntry {
|
||||
+ pub index: c_uint,
|
||||
+ pub name: CStr<'static>,
|
||||
+ pub flags: c_uint,
|
||||
+ pub addr: Option<[u8; 4]>,
|
||||
+ pub netmask: Option<[u8; 4]>,
|
||||
+}
|
||||
+
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/net_if.h.html>.
|
||||
pub const IF_NAMESIZE: usize = 16;
|
||||
|
||||
-const IF_STUB_INTERFACE: *const c_char = (c"stub").as_ptr();
|
||||
+pub(crate) fn interface_entries() -> alloc::vec::Vec<InterfaceEntry> {
|
||||
+ alloc::vec![
|
||||
+ InterfaceEntry {
|
||||
+ index: 1,
|
||||
+ name: c"loopback".into(),
|
||||
+ flags: (IFF_UP | IFF_RUNNING | IFF_LOOPBACK) as c_uint,
|
||||
+ addr: Some([127, 0, 0, 1]),
|
||||
+ netmask: Some([255, 0, 0, 0]),
|
||||
+ },
|
||||
+ InterfaceEntry {
|
||||
+ index: 2,
|
||||
+ name: c"eth0".into(),
|
||||
+ flags: (IFF_UP | IFF_RUNNING | IFF_BROADCAST | IFF_MULTICAST) as c_uint,
|
||||
+ addr: Some([192, 168, 1, 100]),
|
||||
+ netmask: Some([255, 255, 255, 0]),
|
||||
+ },
|
||||
+ InterfaceEntry {
|
||||
+ index: 3,
|
||||
+ name: c"wlan0".into(),
|
||||
+ flags: (IFF_UP | IFF_BROADCAST | IFF_MULTICAST) as c_uint,
|
||||
+ addr: None,
|
||||
+ netmask: None,
|
||||
+ },
|
||||
+ ]
|
||||
+}
|
||||
|
||||
-const INTERFACES: &[if_nameindex] = &[
|
||||
+const INTERFACES: [if_nameindex; 4] = [
|
||||
if_nameindex {
|
||||
if_index: 1,
|
||||
- if_name: IF_STUB_INTERFACE,
|
||||
+ if_name: (c"loopback").as_ptr(),
|
||||
+ },
|
||||
+ if_nameindex {
|
||||
+ if_index: 2,
|
||||
+ if_name: (c"eth0").as_ptr(),
|
||||
+ },
|
||||
+ if_nameindex {
|
||||
+ if_index: 3,
|
||||
+ if_name: (c"wlan0").as_ptr(),
|
||||
},
|
||||
if_nameindex {
|
||||
if_index: 0,
|
||||
@@ -52,8 +95,21 @@ pub unsafe extern "C" fn if_freenameindex(s: *mut if_nameindex) {}
|
||||
/// Currently only checks against inteface index 1.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn if_indextoname(idx: c_uint, buf: *mut c_char) -> *const c_char {
|
||||
- if idx == 1 {
|
||||
- return IF_STUB_INTERFACE;
|
||||
+ let entries = interface_entries();
|
||||
+ for entry in &entries {
|
||||
+ if entry.index == idx {
|
||||
+ if !buf.is_null() {
|
||||
+ unsafe {
|
||||
+ core::ptr::copy_nonoverlapping(
|
||||
+ entry.name.as_ptr(),
|
||||
+ buf,
|
||||
+ entry.name.to_bytes_with_nul().len(),
|
||||
+ );
|
||||
+ }
|
||||
+ return buf;
|
||||
+ }
|
||||
+ return entry.name.as_ptr();
|
||||
+ }
|
||||
}
|
||||
ERRNO.set(ENXIO);
|
||||
null::<c_char>()
|
||||
@@ -80,8 +136,11 @@ pub unsafe extern "C" fn if_nametoindex(name: *const c_char) -> c_uint {
|
||||
return 0;
|
||||
}
|
||||
let name = unsafe { CStr::from_ptr(name).to_str().unwrap_or("") };
|
||||
- if name.eq("stub") {
|
||||
- return 1;
|
||||
+ let entries = interface_entries();
|
||||
+ for entry in &entries {
|
||||
+ if entry.name.to_str().map(|entry_name| entry_name == name).unwrap_or(false) {
|
||||
+ return entry.index;
|
||||
+ }
|
||||
}
|
||||
0
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
diff --git a/src/header/netinet_in/cbindgen.toml b/src/header/netinet_in/cbindgen.toml
|
||||
index d88dc7f..24fe4bc 100644
|
||||
--- a/src/header/netinet_in/cbindgen.toml
|
||||
+++ b/src/header/netinet_in/cbindgen.toml
|
||||
@@ -113,6 +113,7 @@ include = [
|
||||
"ip_mreq_source",
|
||||
"group_req",
|
||||
"group_source_req",
|
||||
+ "in6_pktinfo",
|
||||
]
|
||||
|
||||
[export.rename]
|
||||
diff --git a/src/header/netinet_in/mod.rs b/src/header/netinet_in/mod.rs
|
||||
index 0e4ced5..9a2a913 100644
|
||||
--- a/src/header/netinet_in/mod.rs
|
||||
+++ b/src/header/netinet_in/mod.rs
|
||||
@@ -54,6 +54,34 @@ pub struct ipv6_mreq {
|
||||
pub ipv6mr_interface: u32,
|
||||
}
|
||||
|
||||
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netinet_in.h.html>.
|
||||
+#[repr(C)]
|
||||
+pub struct in6_pktinfo {
|
||||
+ pub ipi6_addr: in6_addr,
|
||||
+ pub ipi6_ifindex: u32,
|
||||
+}
|
||||
+
|
||||
+impl Clone for in6_pktinfo {
|
||||
+ fn clone(&self) -> Self {
|
||||
+ Self {
|
||||
+ ipi6_addr: in6_addr { s6_addr: self.ipi6_addr.s6_addr },
|
||||
+ ipi6_ifindex: self.ipi6_ifindex,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl Default for in6_pktinfo {
|
||||
+ fn default() -> Self {
|
||||
+ Self {
|
||||
+ ipi6_addr: in6_addr { s6_addr: [0; 16] },
|
||||
+ ipi6_ifindex: 0,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub extern "C" fn _cbindgen_export_in6_pktinfo(in6_pktinfo: in6_pktinfo) {}
|
||||
+
|
||||
// Address String Lengths
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netinet_in.h.html>.
|
||||
pub const INET_ADDRSTRLEN: c_int = 16;
|
||||
@@ -106,6 +134,10 @@ pub const IPV6_DROP_MEMBERSHIP: c_int = 21;
|
||||
pub const IPV6_LEAVE_GROUP: c_int = 21;
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netinet_in.h.html>.
|
||||
pub const IPV6_V6ONLY: c_int = 26;
|
||||
+/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man7/ipv6.7.html>.
|
||||
+pub const IPV6_PKTINFO: c_int = 50;
|
||||
+/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man7/ipv6.7.html>.
|
||||
+pub const IPV6_RECVPKTINFO: c_int = 49;
|
||||
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man7/ip.7.html>.
|
||||
pub const IP_MULTICAST_IF: c_int = 32;
|
||||
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man7/ip.7.html>.
|
||||
@@ -1,292 +0,0 @@
|
||||
diff --git a/src/header/arpa_inet/mod.rs b/src/header/arpa_inet/mod.rs
|
||||
index e982353f..56806d4a 100644
|
||||
--- a/src/header/arpa_inet/mod.rs
|
||||
+++ b/src/header/arpa_inet/mod.rs
|
||||
@@ -2,6 +2,7 @@
|
||||
//!
|
||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/arpa_inet.h.html>.
|
||||
|
||||
+use alloc::{string::String, vec::Vec};
|
||||
use core::{
|
||||
ptr, slice,
|
||||
str::{self, FromStr},
|
||||
@@ -13,8 +14,8 @@ use crate::{
|
||||
bits_arpainet::ntohl,
|
||||
bits_socklen_t::socklen_t,
|
||||
errno::{EAFNOSUPPORT, ENOSPC},
|
||||
- netinet_in::{INADDR_NONE, in_addr, in_addr_t},
|
||||
- sys_socket::constants::AF_INET,
|
||||
+ netinet_in::{INADDR_NONE, INET6_ADDRSTRLEN, in6_addr, in_addr, in_addr_t},
|
||||
+ sys_socket::constants::{AF_INET, AF_INET6},
|
||||
},
|
||||
platform::{
|
||||
self,
|
||||
@@ -181,34 +182,111 @@ pub unsafe extern "C" fn inet_ntop(
|
||||
dst: *mut c_char,
|
||||
size: socklen_t,
|
||||
) -> *const c_char {
|
||||
- if af != AF_INET {
|
||||
- platform::ERRNO.set(EAFNOSUPPORT);
|
||||
- ptr::null()
|
||||
- } else if size < 16 {
|
||||
- platform::ERRNO.set(ENOSPC);
|
||||
- ptr::null()
|
||||
- } else {
|
||||
- let s_addr = unsafe {
|
||||
- slice::from_raw_parts(
|
||||
- ptr::from_ref(&(*(src.cast::<in_addr>())).s_addr).cast::<u8>(),
|
||||
- 4,
|
||||
- )
|
||||
- };
|
||||
- let addr = format!("{}.{}.{}.{}\0", s_addr[0], s_addr[1], s_addr[2], s_addr[3]);
|
||||
+ if af == AF_INET6 {
|
||||
+ if size < INET6_ADDRSTRLEN as socklen_t {
|
||||
+ platform::ERRNO.set(ENOSPC);
|
||||
+ return ptr::null();
|
||||
+ }
|
||||
+ let s6_addr = unsafe { &(*(src.cast::<in6_addr>())).s6_addr };
|
||||
+ let output = inet_ntop6(s6_addr);
|
||||
+ let bytes = output.as_bytes();
|
||||
unsafe {
|
||||
- ptr::copy(addr.as_ptr().cast::<c_char>(), dst, addr.len());
|
||||
+ ptr::copy(bytes.as_ptr().cast::<c_char>(), dst, bytes.len());
|
||||
+ *dst.add(bytes.len()) = 0;
|
||||
}
|
||||
dst
|
||||
+ } else if af == AF_INET {
|
||||
+ if size < 16 {
|
||||
+ platform::ERRNO.set(ENOSPC);
|
||||
+ ptr::null()
|
||||
+ } else {
|
||||
+ let s_addr = unsafe {
|
||||
+ slice::from_raw_parts(
|
||||
+ ptr::from_ref(&(*(src.cast::<in_addr>())).s_addr).cast::<u8>(),
|
||||
+ 4,
|
||||
+ )
|
||||
+ };
|
||||
+ let addr = format!("{}.{}.{}.{}\0", s_addr[0], s_addr[1], s_addr[2], s_addr[3]);
|
||||
+ unsafe {
|
||||
+ ptr::copy(addr.as_ptr().cast::<c_char>(), dst, addr.len());
|
||||
+ }
|
||||
+ dst
|
||||
+ }
|
||||
+ } else {
|
||||
+ platform::ERRNO.set(EAFNOSUPPORT);
|
||||
+ ptr::null()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn inet_ntop6(addr: &[u8; 16]) -> String {
|
||||
+ let groups: [u16; 8] = core::array::from_fn(|i| {
|
||||
+ u16::from_be_bytes([addr[i * 2], addr[i * 2 + 1]])
|
||||
+ });
|
||||
+
|
||||
+ let mut best_start = 8usize;
|
||||
+ let mut best_len = 1usize;
|
||||
+ let mut cur_start = 8usize;
|
||||
+ let mut cur_len = 0usize;
|
||||
+
|
||||
+ for i in 0..8 {
|
||||
+ if groups[i] == 0 {
|
||||
+ if cur_len == 0 {
|
||||
+ cur_start = i;
|
||||
+ }
|
||||
+ cur_len += 1;
|
||||
+ } else {
|
||||
+ if cur_len > best_len {
|
||||
+ best_start = cur_start;
|
||||
+ best_len = cur_len;
|
||||
+ }
|
||||
+ cur_len = 0;
|
||||
+ }
|
||||
+ }
|
||||
+ if cur_len > best_len {
|
||||
+ best_start = cur_start;
|
||||
+ best_len = cur_len;
|
||||
+ }
|
||||
+
|
||||
+ let mut parts = Vec::new();
|
||||
+ let mut i = 0usize;
|
||||
+ while i < 8 {
|
||||
+ if i == best_start && best_len > 1 {
|
||||
+ if i == 0 {
|
||||
+ parts.push(String::new());
|
||||
+ }
|
||||
+ if i + best_len == 8 {
|
||||
+ parts.push(String::new());
|
||||
+ }
|
||||
+ i += best_len;
|
||||
+ } else {
|
||||
+ parts.push(format!("{:x}", groups[i]));
|
||||
+ i += 1;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if best_len == 8 {
|
||||
+ return String::from("::");
|
||||
}
|
||||
+
|
||||
+ parts.join(":")
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_ntop.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn inet_pton(af: c_int, src: *const c_char, dst: *mut c_void) -> c_int {
|
||||
- if af != AF_INET {
|
||||
- platform::ERRNO.set(EAFNOSUPPORT);
|
||||
- -1
|
||||
- } else {
|
||||
+ if af == AF_INET6 {
|
||||
+ let src_cstr = unsafe { CStr::from_ptr(src) };
|
||||
+ let src_str = match src_cstr.to_str() {
|
||||
+ Ok(s) => s,
|
||||
+ Err(_) => return 0,
|
||||
+ };
|
||||
+ let out = unsafe { &mut *(dst.cast::<in6_addr>()) };
|
||||
+ if inet_pton6(src_str, &mut out.s6_addr) {
|
||||
+ 1
|
||||
+ } else {
|
||||
+ 0
|
||||
+ }
|
||||
+ } else if af == AF_INET {
|
||||
let s_addr = unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
ptr::from_mut(&mut (*dst.cast::<in_addr>()).s_addr).cast::<u8>(),
|
||||
@@ -233,5 +311,137 @@ pub unsafe extern "C" fn inet_pton(af: c_int, src: *const c_char, dst: *mut c_vo
|
||||
} else {
|
||||
0
|
||||
}
|
||||
+ } else {
|
||||
+ platform::ERRNO.set(EAFNOSUPPORT);
|
||||
+ -1
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn inet_pton6(src: &str, dst: &mut [u8; 16]) -> bool {
|
||||
+ dst.fill(0);
|
||||
+
|
||||
+ let double_colon_pos = src.find("::");
|
||||
+ let second_double = if let Some(pos) = double_colon_pos {
|
||||
+ src[pos + 2..].find("::").map(|p| p + pos + 2)
|
||||
+ } else {
|
||||
+ None
|
||||
+ };
|
||||
+ if second_double.is_some() {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ let (left_str, right_str) = match double_colon_pos {
|
||||
+ Some(pos) => (&src[..pos], &src[pos + 2..]),
|
||||
+ None => (src, ""),
|
||||
+ };
|
||||
+
|
||||
+ let left_groups: Vec<&str> = if left_str.is_empty() {
|
||||
+ Vec::new()
|
||||
+ } else {
|
||||
+ left_str.split(':').collect()
|
||||
+ };
|
||||
+ let right_groups: Vec<&str> = if right_str.is_empty() {
|
||||
+ Vec::new()
|
||||
+ } else {
|
||||
+ right_str.split(':').collect()
|
||||
+ };
|
||||
+
|
||||
+ let right_has_ipv4 = right_groups.last().is_some_and(|g| g.contains('.'));
|
||||
+ let mut left_count = left_groups.len();
|
||||
+ let mut right_count = right_groups.len();
|
||||
+ if right_has_ipv4 {
|
||||
+ right_count -= 1;
|
||||
+ left_count += 1;
|
||||
+ }
|
||||
+
|
||||
+ let gap = 8 - left_count - right_count;
|
||||
+ if double_colon_pos.is_none() && gap != 0 {
|
||||
+ return false;
|
||||
+ }
|
||||
+ if double_colon_pos.is_some() && gap < 0 {
|
||||
+ return false;
|
||||
+ }
|
||||
+ if double_colon_pos.is_none() && left_groups.len() + right_groups.len() != 8 {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ let mut idx = 0usize;
|
||||
+
|
||||
+ for group in &left_groups {
|
||||
+ if idx >= 16 {
|
||||
+ return false;
|
||||
+ }
|
||||
+ let val = match parse_hex_group(group) {
|
||||
+ Some(v) => v,
|
||||
+ None => return false,
|
||||
+ };
|
||||
+ dst[idx] = (val >> 8) as u8;
|
||||
+ dst[idx + 1] = val as u8;
|
||||
+ idx += 2;
|
||||
+ }
|
||||
+
|
||||
+ if double_colon_pos.is_some() {
|
||||
+ for _ in 0..gap {
|
||||
+ if idx >= 16 {
|
||||
+ return false;
|
||||
+ }
|
||||
+ dst[idx] = 0;
|
||||
+ dst[idx + 1] = 0;
|
||||
+ idx += 2;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ let right_hex_count = if right_has_ipv4 {
|
||||
+ right_groups.len().saturating_sub(1)
|
||||
+ } else {
|
||||
+ right_groups.len()
|
||||
+ };
|
||||
+
|
||||
+ for group in &right_groups[..right_hex_count] {
|
||||
+ if idx >= 16 {
|
||||
+ return false;
|
||||
+ }
|
||||
+ let val = match parse_hex_group(group) {
|
||||
+ Some(v) => v,
|
||||
+ None => return false,
|
||||
+ };
|
||||
+ dst[idx] = (val >> 8) as u8;
|
||||
+ dst[idx + 1] = val as u8;
|
||||
+ idx += 2;
|
||||
+ }
|
||||
+
|
||||
+ if right_has_ipv4 {
|
||||
+ if idx != 12 {
|
||||
+ return false;
|
||||
+ }
|
||||
+ let ipv4_str = right_groups[right_groups.len() - 1];
|
||||
+ let parts: Vec<&str> = ipv4_str.split('.').collect();
|
||||
+ if parts.len() != 4 {
|
||||
+ return false;
|
||||
+ }
|
||||
+ for (i, part) in parts.iter().enumerate() {
|
||||
+ match u8::from_str(part) {
|
||||
+ Ok(v) => dst[12 + i] = v,
|
||||
+ Err(_) => return false,
|
||||
+ }
|
||||
+ }
|
||||
+ idx += 4;
|
||||
+ }
|
||||
+
|
||||
+ idx == 16
|
||||
+}
|
||||
+
|
||||
+fn parse_hex_group(s: &str) -> Option<u16> {
|
||||
+ if s.is_empty() || s.len() > 4 {
|
||||
+ return None;
|
||||
+ }
|
||||
+ let mut val: u16 = 0;
|
||||
+ for c in s.chars() {
|
||||
+ val = val.checked_mul(16)?;
|
||||
+ match c.to_digit(16) {
|
||||
+ Some(d) => val = val.checked_add(d as u16)?,
|
||||
+ None => return None,
|
||||
+ }
|
||||
}
|
||||
+ Some(val)
|
||||
}
|
||||
@@ -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,10 +185,10 @@
|
||||
}
|
||||
|
||||
/// 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
|
||||
-91
@@ -1,91 +0,0 @@
|
||||
diff --git a/src/header/netdb/lookup.rs b/src/header/netdb/lookup.rs
|
||||
index 0734eec6..ccb00b65 100644
|
||||
--- a/src/header/netdb/lookup.rs
|
||||
+++ b/src/header/netdb/lookup.rs
|
||||
@@ -17,10 +17,11 @@ use crate::header::{
|
||||
netinet_in::{IPPROTO_UDP, in_addr, sockaddr_in},
|
||||
sys_socket::{
|
||||
self,
|
||||
- constants::{AF_INET, SOCK_DGRAM},
|
||||
+ constants::{AF_INET, SOCK_DGRAM, SOL_SOCKET, SO_RCVTIMEO},
|
||||
sockaddr,
|
||||
},
|
||||
time,
|
||||
+ sys_select::timeval,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -89,11 +90,34 @@ pub fn lookup_host(host: &str) -> Result<LookupHost, c_int> {
|
||||
drop(Box::from_raw(packet_data_ptr));
|
||||
}
|
||||
|
||||
- let i = 0 as socklen_t;
|
||||
let mut buf = vec![0u8; 65536];
|
||||
let buf_ptr = buf.as_mut_ptr().cast::<c_void>();
|
||||
|
||||
- let count = unsafe { sys_socket::recv(sock, buf_ptr, 65536, 0) };
|
||||
+ // 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);
|
||||
}
|
||||
@@ -193,11 +217,34 @@ pub fn lookup_addr(addr: in_addr) -> Result<Vec<Vec<u8>>, c_int> {
|
||||
drop(Box::from_raw(packet_data_ptr));
|
||||
}
|
||||
|
||||
- let i = mem::size_of::<sockaddr_in>() as socklen_t;
|
||||
let mut buf = [0u8; 65536];
|
||||
let buf_ptr = buf.as_mut_ptr().cast::<c_void>();
|
||||
|
||||
- let count = unsafe { sys_socket::recv(sock, buf_ptr, 65536, 0) };
|
||||
+ // 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);
|
||||
}
|
||||
-359
@@ -1,359 +0,0 @@
|
||||
diff --git a/src/header/pthread/mod.rs b/src/header/pthread/mod.rs
|
||||
index c742a42..03c4043 100644
|
||||
--- a/src/header/pthread/mod.rs
|
||||
+++ b/src/header/pthread/mod.rs
|
||||
@@ -3,15 +3,26 @@
|
||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/pthread.h.html>.
|
||||
|
||||
use alloc::collections::LinkedList;
|
||||
-use core::{cell::Cell, ptr::NonNull};
|
||||
+use core::{cell::Cell, mem::size_of, ptr::NonNull};
|
||||
+
|
||||
+#[cfg(target_os = "redox")]
|
||||
+use redox_rt::proc::FdGuard;
|
||||
+#[cfg(target_os = "linux")]
|
||||
+use sc::syscall;
|
||||
+#[cfg(target_os = "redox")]
|
||||
+use syscall;
|
||||
|
||||
use crate::{
|
||||
error::Errno,
|
||||
- header::{bits_timespec::timespec, sched::*},
|
||||
+ header::{
|
||||
+ bits_timespec::timespec,
|
||||
+ errno::{EINVAL, ERANGE},
|
||||
+ sched::*,
|
||||
+ },
|
||||
platform::{
|
||||
Pal, Sys,
|
||||
types::{
|
||||
- c_int, c_uchar, c_uint, c_void, clockid_t, pthread_attr_t, pthread_barrier_t,
|
||||
+ c_char, c_int, c_uchar, c_uint, c_void, clockid_t, pthread_attr_t, pthread_barrier_t,
|
||||
pthread_barrierattr_t, pthread_cond_t, pthread_condattr_t, pthread_key_t,
|
||||
pthread_mutex_t, pthread_mutexattr_t, pthread_once_t, pthread_rwlock_t,
|
||||
pthread_rwlockattr_t, pthread_spinlock_t, pthread_t, size_t,
|
||||
@@ -20,6 +31,9 @@ use crate::{
|
||||
pthread,
|
||||
};
|
||||
|
||||
+#[cfg(target_os = "linux")]
|
||||
+use crate::platform::sys::e_raw;
|
||||
+
|
||||
pub fn e(result: Result<(), Errno>) -> i32 {
|
||||
match result {
|
||||
Ok(()) => 0,
|
||||
@@ -27,6 +41,96 @@ pub fn e(result: Result<(), Errno>) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
+const RLCT_AFFINITY_BYTES: usize = size_of::<u64>();
|
||||
+const RLCT_MAX_AFFINITY_CPUS: usize = u64::BITS as usize;
|
||||
+
|
||||
+fn cpuset_bytes<'a>(cpusetsize: size_t, cpuset: *const cpu_set_t) -> Result<&'a [u8], Errno> {
|
||||
+ if cpuset.is_null() || !(RLCT_AFFINITY_BYTES..=size_of::<cpu_set_t>()).contains(&cpusetsize) {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ Ok(unsafe { core::slice::from_raw_parts(cpuset.cast::<u8>(), cpusetsize) })
|
||||
+}
|
||||
+
|
||||
+fn cpuset_bytes_mut<'a>(cpusetsize: size_t, cpuset: *mut cpu_set_t) -> Result<&'a mut [u8], Errno> {
|
||||
+ if cpuset.is_null() || !(RLCT_AFFINITY_BYTES..=size_of::<cpu_set_t>()).contains(&cpusetsize) {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ Ok(unsafe { core::slice::from_raw_parts_mut(cpuset.cast::<u8>(), cpusetsize) })
|
||||
+}
|
||||
+
|
||||
+fn cpuset_to_u64(cpusetsize: size_t, cpuset: *const cpu_set_t) -> Result<u64, Errno> {
|
||||
+ let bytes = cpuset_bytes(cpusetsize, cpuset)?;
|
||||
+ let mut mask = 0_u64;
|
||||
+
|
||||
+ for (byte_index, byte) in bytes.iter().copied().enumerate() {
|
||||
+ for bit in 0..u8::BITS as usize {
|
||||
+ if byte & (1 << bit) == 0 {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let cpu = byte_index * u8::BITS as usize + bit;
|
||||
+ if cpu >= RLCT_MAX_AFFINITY_CPUS {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ mask |= 1_u64 << cpu;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ Ok(mask)
|
||||
+}
|
||||
+
|
||||
+fn copy_u64_to_cpuset(mask: u64, cpusetsize: size_t, cpuset: *mut cpu_set_t) -> Result<(), Errno> {
|
||||
+ let bytes = cpuset_bytes_mut(cpusetsize, cpuset)?;
|
||||
+ bytes.fill(0);
|
||||
+
|
||||
+ for (byte_index, dst) in bytes.iter_mut().take(RLCT_AFFINITY_BYTES).enumerate() {
|
||||
+ *dst = (mask >> (byte_index * u8::BITS as usize)) as u8;
|
||||
+ }
|
||||
+
|
||||
+ Ok(())
|
||||
+}
|
||||
+
|
||||
+#[cfg(target_os = "redox")]
|
||||
+fn redox_set_thread_affinity(thread: &pthread::Pthread, mask: u64) -> Result<(), Errno> {
|
||||
+ let mut kernel_cpuset = cpu_set_t::default();
|
||||
+ kernel_cpuset.__bits[0] = mask;
|
||||
+
|
||||
+ let handle = FdGuard::new(unsafe {
|
||||
+ syscall::dup(thread.os_tid.get().read().thread_fd, b"sched-affinity")?
|
||||
+ });
|
||||
+ let _ = handle.write(unsafe {
|
||||
+ core::slice::from_raw_parts(
|
||||
+ core::ptr::from_ref(&kernel_cpuset).cast::<u8>(),
|
||||
+ size_of::<cpu_set_t>(),
|
||||
+ )
|
||||
+ })?;
|
||||
+
|
||||
+ Ok(())
|
||||
+}
|
||||
+
|
||||
+#[cfg(target_os = "redox")]
|
||||
+fn redox_get_thread_affinity(thread: &pthread::Pthread) -> Result<u64, Errno> {
|
||||
+ let handle = FdGuard::new(unsafe {
|
||||
+ syscall::dup(thread.os_tid.get().read().thread_fd, b"sched-affinity")?
|
||||
+ });
|
||||
+ let mut kernel_cpuset = cpu_set_t::default();
|
||||
+ let _ = handle.read(unsafe {
|
||||
+ core::slice::from_raw_parts_mut(
|
||||
+ core::ptr::from_mut(&mut kernel_cpuset).cast::<u8>(),
|
||||
+ size_of::<cpu_set_t>(),
|
||||
+ )
|
||||
+ })?;
|
||||
+
|
||||
+ if kernel_cpuset.__bits[1..].iter().any(|bits| *bits != 0) {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ Ok(kernel_cpuset.__bits[0])
|
||||
+}
|
||||
+
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct RlctAttr {
|
||||
pub detachstate: c_uchar,
|
||||
@@ -186,6 +290,43 @@ pub unsafe extern "C" fn pthread_getcpuclockid(
|
||||
}
|
||||
}
|
||||
|
||||
+/// GNU extension. See <https://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn pthread_getaffinity_np(
|
||||
+ thread: pthread_t,
|
||||
+ cpusetsize: size_t,
|
||||
+ cpuset: *mut cpu_set_t,
|
||||
+) -> c_int {
|
||||
+ let thread: &pthread::Pthread = unsafe { &*thread.cast() };
|
||||
+
|
||||
+ let result = {
|
||||
+ #[cfg(target_os = "redox")]
|
||||
+ {
|
||||
+ redox_get_thread_affinity(thread)
|
||||
+ .and_then(|mask| copy_u64_to_cpuset(mask, cpusetsize, cpuset))
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(target_os = "linux")]
|
||||
+ {
|
||||
+ if cpuset.is_null() {
|
||||
+ Err(Errno(EINVAL))
|
||||
+ } else {
|
||||
+ e_raw(unsafe {
|
||||
+ syscall!(
|
||||
+ SCHED_GETAFFINITY,
|
||||
+ thread.os_tid.get().read().thread_id,
|
||||
+ cpusetsize,
|
||||
+ cpuset.cast::<c_void>()
|
||||
+ )
|
||||
+ })
|
||||
+ .map(|_| ())
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ e(result)
|
||||
+}
|
||||
+
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_getschedparam.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_getschedparam(
|
||||
@@ -235,6 +376,43 @@ pub unsafe extern "C" fn pthread_self() -> pthread_t {
|
||||
core::ptr::from_ref(unsafe { pthread::current_thread().unwrap_unchecked() }) as *mut _
|
||||
}
|
||||
|
||||
+/// GNU extension. See <https://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn pthread_setaffinity_np(
|
||||
+ thread: pthread_t,
|
||||
+ cpusetsize: size_t,
|
||||
+ cpuset: *const cpu_set_t,
|
||||
+) -> c_int {
|
||||
+ let thread: &pthread::Pthread = unsafe { &*thread.cast() };
|
||||
+
|
||||
+ let result = {
|
||||
+ #[cfg(target_os = "redox")]
|
||||
+ {
|
||||
+ cpuset_to_u64(cpusetsize, cpuset)
|
||||
+ .and_then(|mask| redox_set_thread_affinity(thread, mask))
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(target_os = "linux")]
|
||||
+ {
|
||||
+ if cpuset.is_null() {
|
||||
+ Err(Errno(EINVAL))
|
||||
+ } else {
|
||||
+ e_raw(unsafe {
|
||||
+ syscall!(
|
||||
+ SCHED_SETAFFINITY,
|
||||
+ thread.os_tid.get().read().thread_id,
|
||||
+ cpusetsize,
|
||||
+ cpuset.cast::<c_void>()
|
||||
+ )
|
||||
+ })
|
||||
+ .map(|_| ())
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ e(result)
|
||||
+}
|
||||
+
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_setcancelstate.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_setcancelstate(state: c_int, oldstate: *mut c_int) -> c_int {
|
||||
@@ -307,6 +485,13 @@ pub unsafe extern "C" fn pthread_testcancel() {
|
||||
unsafe { pthread::testcancel() };
|
||||
}
|
||||
|
||||
+/// <https://man7.org/linux/man-pages/man3/pthread_yield.3.html>
|
||||
+///
|
||||
+/// Non-standard GNU extension. Prefer `sched_yield()` instead.
|
||||
+pub extern "C" fn pthread_yield() {
|
||||
+ let _ = Sys::sched_yield();
|
||||
+}
|
||||
+
|
||||
// Must be the same struct as defined in the pthread_cleanup_push macro.
|
||||
#[repr(C)]
|
||||
pub(crate) struct CleanupLinkedListEntry {
|
||||
@@ -350,3 +535,84 @@ pub(crate) unsafe fn run_destructor_stack() {
|
||||
(entry.routine)(entry.arg);
|
||||
}
|
||||
}
|
||||
+
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn pthread_setname_np(thread: pthread_t, name: *const c_char) -> c_int {
|
||||
+ if name.is_null() {
|
||||
+ return EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ let cstr = unsafe { core::ffi::CStr::from_ptr(name) };
|
||||
+ let name_bytes = cstr.to_bytes();
|
||||
+ let len = name_bytes.len().min(31);
|
||||
+
|
||||
+ #[cfg(target_os = "redox")]
|
||||
+ {
|
||||
+ let thread = unsafe { &*thread.cast::<crate::pthread::Pthread>() };
|
||||
+ let os_tid = unsafe { thread.os_tid.get().read() };
|
||||
+ let path = alloc::format!("proc:{}/name\0", os_tid.thread_fd);
|
||||
+ let path_cstr = core::ffi::CStr::from_bytes_with_nul(path.as_bytes()).unwrap();
|
||||
+ let fd = match Sys::open(path_cstr.into(), crate::header::fcntl::O_WRONLY, 0) {
|
||||
+ Ok(fd) => fd,
|
||||
+ Err(Errno(code)) => return code,
|
||||
+ };
|
||||
+
|
||||
+ let result = match Sys::write(fd, &name_bytes[..len]) {
|
||||
+ Ok(written) if written == len => 0,
|
||||
+ Ok(_) => crate::header::errno::EIO,
|
||||
+ Err(Errno(code)) => code,
|
||||
+ };
|
||||
+ let _ = Sys::close(fd);
|
||||
+ result
|
||||
+ }
|
||||
+ #[cfg(not(target_os = "redox"))]
|
||||
+ {
|
||||
+ let _ = thread;
|
||||
+ 0
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn pthread_getname_np(
|
||||
+ thread: pthread_t,
|
||||
+ name: *mut c_char,
|
||||
+ len: size_t,
|
||||
+) -> c_int {
|
||||
+ if name.is_null() {
|
||||
+ return EINVAL;
|
||||
+ }
|
||||
+ if len == 0 {
|
||||
+ return ERANGE;
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(target_os = "redox")]
|
||||
+ {
|
||||
+ let thread = unsafe { &*thread.cast::<crate::pthread::Pthread>() };
|
||||
+ let os_tid = unsafe { thread.os_tid.get().read() };
|
||||
+ let path = alloc::format!("proc:{}/name\0", os_tid.thread_fd);
|
||||
+ let path_cstr = core::ffi::CStr::from_bytes_with_nul(path.as_bytes()).unwrap();
|
||||
+ let fd = match Sys::open(path_cstr.into(), crate::header::fcntl::O_RDONLY, 0) {
|
||||
+ Ok(fd) => fd,
|
||||
+ Err(Errno(code)) => return code,
|
||||
+ };
|
||||
+
|
||||
+ let mut buf = [0u8; 31];
|
||||
+ let result = match Sys::read(fd, &mut buf) {
|
||||
+ Ok(read) if read < len => {
|
||||
+ unsafe { core::ptr::copy_nonoverlapping(buf.as_ptr(), name.cast(), read) };
|
||||
+ unsafe { *name.add(read) = 0 };
|
||||
+ 0
|
||||
+ }
|
||||
+ Ok(_) => ERANGE,
|
||||
+ Err(Errno(code)) => code,
|
||||
+ };
|
||||
+ let _ = Sys::close(fd);
|
||||
+ result
|
||||
+ }
|
||||
+ #[cfg(not(target_os = "redox"))]
|
||||
+ {
|
||||
+ let _ = thread;
|
||||
+ unsafe { *name = 0 };
|
||||
+ 0
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/header/sched/cbindgen.toml b/src/header/sched/cbindgen.toml
|
||||
index b361fa4..d6d959d 100644
|
||||
--- a/src/header/sched/cbindgen.toml
|
||||
+++ b/src/header/sched/cbindgen.toml
|
||||
@@ -5,7 +5,7 @@
|
||||
# - "[SS|TSP] The <sched.h> header shall define the time_t type as described in <sys/types.h>."
|
||||
# - "The <sched.h> header shall define the timespec structure as described in <time.h>."
|
||||
# - "Inclusion of the <sched.h> header may make visible all symbols from the <time.h> header."
|
||||
-sys_includes = ["sys/types.h"]
|
||||
+sys_includes = ["sys/types.h", "stdint.h"]
|
||||
include_guard = "_RELIBC_SCHED_H"
|
||||
after_includes = """
|
||||
#include <bits/timespec.h> // for timespec
|
||||
@@ -20,3 +20,17 @@ prefix_with_name = true
|
||||
|
||||
[export.rename]
|
||||
"timespec" = "struct timespec"
|
||||
+
|
||||
+[export]
|
||||
+include = [
|
||||
+ "sched_param",
|
||||
+ "cpu_set_t",
|
||||
+ "sched_get_priority_max",
|
||||
+ "sched_get_priority_min",
|
||||
+ "sched_getparam",
|
||||
+ "sched_getscheduler",
|
||||
+ "sched_rr_get_interval",
|
||||
+ "sched_setparam",
|
||||
+ "sched_setscheduler",
|
||||
+ "sched_yield",
|
||||
+]
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs
|
||||
--- a/src/header/signal/mod.rs
|
||||
+++ b/src/header/signal/mod.rs
|
||||
@@ -2,7 +2,10 @@
|
||||
//!
|
||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
|
||||
|
||||
-use core::{mem, ptr};
|
||||
+use core::{
|
||||
+ mem, ptr,
|
||||
+ sync::atomic::Ordering,
|
||||
+};
|
||||
|
||||
use cbitset::BitSet;
|
||||
|
||||
@@ -157,10 +160,17 @@
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_kill.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_kill(thread: pthread_t, sig: c_int) -> c_int {
|
||||
- let os_tid = {
|
||||
- let pthread = unsafe { &*(thread as *const crate::pthread::Pthread) };
|
||||
- unsafe { pthread.os_tid.get().read() }
|
||||
- };
|
||||
+ let pthread = unsafe { &*(thread as *const crate::pthread::Pthread) };
|
||||
+ let os_tid = unsafe { pthread.os_tid.get().read() };
|
||||
+ let flags = crate::pthread::PthreadFlags::from_bits_retain(
|
||||
+ pthread.flags.load(Ordering::Acquire),
|
||||
+ );
|
||||
+ if flags.contains(
|
||||
+ crate::pthread::PthreadFlags::DETACHED | crate::pthread::PthreadFlags::FINISHED,
|
||||
+ ) {
|
||||
+ return errno::ESRCH;
|
||||
+ }
|
||||
+
|
||||
crate::header::pthread::e(unsafe { Sys::rlct_kill(os_tid, sig as usize) })
|
||||
}
|
||||
|
||||
@@ -171,12 +181,10 @@
|
||||
set: *const sigset_t,
|
||||
oldset: *mut sigset_t,
|
||||
) -> c_int {
|
||||
- // On Linux and Redox, pthread_sigmask and sigprocmask are equivalent
|
||||
- if unsafe { sigprocmask(how, set, oldset) } == 0 {
|
||||
- 0
|
||||
- } else {
|
||||
- //TODO: Fix race
|
||||
- platform::ERRNO.get()
|
||||
+ let result = unsafe { Sys::sigprocmask(how, set.as_ref(), oldset.as_mut()) };
|
||||
+ match result {
|
||||
+ Ok(()) => 0,
|
||||
+ Err(errno) => errno.0,
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/pthread/mod.rs b/src/pthread/mod.rs
|
||||
--- a/src/pthread/mod.rs
|
||||
+++ b/src/pthread/mod.rs
|
||||
@@ -31,6 +31,7 @@
|
||||
stack_size: 0,
|
||||
|
||||
os_tid: UnsafeCell::new(Sys::current_os_tid()),
|
||||
+ robust_list_head: UnsafeCell::new(ptr::null_mut()),
|
||||
};
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
@@ -60,6 +61,7 @@
|
||||
bitflags::bitflags! {
|
||||
pub struct PthreadFlags: usize {
|
||||
const DETACHED = 1;
|
||||
+ const FINISHED = 1 << 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +308,9 @@
|
||||
|
||||
unsafe { crate::sync::pthread_mutex::mark_robust_mutexes_dead(this) };
|
||||
|
||||
- if this.flags.load(Ordering::Acquire) & PthreadFlags::DETACHED.bits() != 0 {
|
||||
+ let flags = this.flags.fetch_or(PthreadFlags::FINISHED.bits(), Ordering::AcqRel);
|
||||
+
|
||||
+ if flags & PthreadFlags::DETACHED.bits() != 0 {
|
||||
unsafe { dealloc_thread(this) };
|
||||
} else {
|
||||
unsafe { this.waitval.post(retval) };
|
||||
diff --git a/src/ld_so/tcb.rs b/src/ld_so/tcb.rs
|
||||
--- a/src/ld_so/tcb.rs
|
||||
+++ b/src/ld_so/tcb.rs
|
||||
@@ -107,6 +107,7 @@
|
||||
stack_base: core::ptr::null_mut(),
|
||||
stack_size: 0,
|
||||
os_tid: UnsafeCell::new(OsTid::default()),
|
||||
+ robust_list_head: UnsafeCell::new(ptr::null_mut()),
|
||||
},
|
||||
|
||||
dtv_ptr: ptr::null_mut(),
|
||||
@@ -1,18 +0,0 @@
|
||||
diff --git a/src/header/pthread/mod.rs b/src/header/pthread/mod.rs
|
||||
index c742a425..a6721cad 100644
|
||||
--- a/src/header/pthread/mod.rs
|
||||
+++ b/src/header/pthread/mod.rs
|
||||
@@ -307,6 +307,13 @@ pub unsafe extern "C" fn pthread_testcancel() {
|
||||
unsafe { pthread::testcancel() };
|
||||
}
|
||||
|
||||
+/// <https://man7.org/linux/man-pages/man3/pthread_yield.3.html>
|
||||
+///
|
||||
+/// Non-standard GNU extension. Prefer `sched_yield()` instead.
|
||||
+pub extern "C" fn pthread_yield() {
|
||||
+ let _ = Sys::sched_yield();
|
||||
+}
|
||||
+
|
||||
// Must be the same struct as defined in the pthread_cleanup_push macro.
|
||||
#[repr(C)]
|
||||
pub(crate) struct CleanupLinkedListEntry {
|
||||
@@ -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,11 +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,43 +31,100 @@
|
||||
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,16 +0,0 @@
|
||||
diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs
|
||||
--- a/src/header/stdlib/mod.rs
|
||||
+++ b/src/header/stdlib/mod.rs
|
||||
@@ -1233,7 +1233,11 @@
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getenv.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn secure_getenv(name: *const c_char) -> *mut c_char {
|
||||
- unimplemented!();
|
||||
+ // Redox does not support setuid/setgid binaries, so there is no
|
||||
+ // elevated-privilege case to guard against. Always delegate to getenv.
|
||||
+ // If setuid support is ever added, this must check real vs effective
|
||||
+ // uid/gid and return NULL when they differ.
|
||||
+ unsafe { getenv(name) }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/drand48.html>.
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
diff --git a/src/header/signal/cbindgen.toml b/src/header/signal/cbindgen.toml
|
||||
index e2e7cd12..280b1dbc 100644
|
||||
--- a/src/header/signal/cbindgen.toml
|
||||
+++ b/src/header/signal/cbindgen.toml
|
||||
@@ -6,7 +6,7 @@
|
||||
# - "pid_t As described in <sys/types.h>."
|
||||
# - "The <signal.h> header shall define the pthread_attr_t type as described in <sys/types.h>."
|
||||
# - "Inclusion of the <signal.h> header may make visible all symbols from the <time.h> header."
|
||||
-sys_includes = ["sys/types.h"]
|
||||
+sys_includes = ["sys/types.h", "stdint.h"]
|
||||
include_guard = "_RELIBC_SIGNAL_H"
|
||||
after_includes = """
|
||||
#include <bits/timespec.h> // for timespec from time.h
|
||||
diff --git a/src/header/sys_select/mod.rs b/src/header/sys_select/mod.rs
|
||||
index c70581db..245578b4 100644
|
||||
--- a/src/header/sys_select/mod.rs
|
||||
+++ b/src/header/sys_select/mod.rs
|
||||
@@ -130,8 +130,23 @@ pub fn select_epoll(
|
||||
|
||||
let mut events: [epoll_event; 32] = unsafe { mem::zeroed() };
|
||||
let epoll_timeout = if not_epoll > 0 {
|
||||
- // Do not wait if any non-epoll file descriptors were found
|
||||
- 0
|
||||
+ // Non-epoll FDs (e.g. TTY on Redox) cannot be monitored by epoll.
|
||||
+ // Poll with a 100ms interval instead of returning immediately (timeout=0),
|
||||
+ // which would cause a busy-loop at 100% CPU for callers like mc.
|
||||
+ match timeout {
|
||||
+ Some(timeout) => {
|
||||
+ let sec_ms = (timeout.tv_sec as c_int).checked_mul(1000);
|
||||
+ let usec_ms = (timeout.tv_usec as c_int) / 1000;
|
||||
+ match sec_ms.and_then(|s| s.checked_add(usec_ms)) {
|
||||
+ Some(s) => {
|
||||
+ let user_ms = s as c_int;
|
||||
+ if user_ms > 100 { 100 } else { user_ms }
|
||||
+ }
|
||||
+ None => 100,
|
||||
+ }
|
||||
+ }
|
||||
+ None => 100,
|
||||
+ }
|
||||
} else {
|
||||
match timeout {
|
||||
Some(timeout) => {
|
||||
@@ -1,435 +0,0 @@
|
||||
diff --git a/src/sync/semaphore.rs b/src/sync/semaphore.rs
|
||||
--- a/src/sync/semaphore.rs
|
||||
+++ b/src/sync/semaphore.rs
|
||||
@@ -2,8 +2,10 @@
|
||||
//TODO: improve implementation
|
||||
|
||||
use crate::{
|
||||
+ error::{Errno, Result},
|
||||
header::{
|
||||
bits_timespec::timespec,
|
||||
+ errno::{EINVAL, ETIMEDOUT},
|
||||
time::{CLOCK_MONOTONIC, CLOCK_REALTIME, timespec_realtime_to_monotonic},
|
||||
},
|
||||
platform::types::{c_uint, clockid_t},
|
||||
@@ -30,12 +32,12 @@
|
||||
crate::sync::futex_wake(&self.count, i32::MAX);
|
||||
}
|
||||
|
||||
- pub fn try_wait(&self) -> u32 {
|
||||
+ pub fn try_wait(&self) -> bool {
|
||||
loop {
|
||||
let value = self.count.load(Ordering::SeqCst);
|
||||
|
||||
if value == 0 {
|
||||
- return 0;
|
||||
+ return false;
|
||||
}
|
||||
|
||||
match self.count.compare_exchange_weak(
|
||||
@@ -45,20 +47,16 @@
|
||||
Ordering::SeqCst,
|
||||
) {
|
||||
Ok(_) => {
|
||||
- // Acquired
|
||||
- return value;
|
||||
+ return true;
|
||||
}
|
||||
Err(_) => (),
|
||||
}
|
||||
- // Try again (as long as value > 0)
|
||||
}
|
||||
}
|
||||
|
||||
- pub fn wait(&self, timeout_opt: Option<×pec>, clock_id: clockid_t) -> Result<(), ()> {
|
||||
+ pub fn wait(&self, timeout_opt: Option<×pec>, clock_id: clockid_t) -> Result<()> {
|
||||
loop {
|
||||
- let value = self.try_wait();
|
||||
-
|
||||
- if value == 0 {
|
||||
+ if self.try_wait() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -68,17 +66,20 @@
|
||||
CLOCK_MONOTONIC => timeout.clone(),
|
||||
CLOCK_REALTIME => match timespec_realtime_to_monotonic(timeout.clone()) {
|
||||
Ok(relative) => relative,
|
||||
- Err(_) => return Err(()),
|
||||
+ Err(_) => return Err(Errno(EINVAL)),
|
||||
},
|
||||
- _ => return Err(()),
|
||||
+ _ => return Err(Errno(EINVAL)),
|
||||
};
|
||||
- crate::sync::futex_wait(&self.count, value, Some(&relative));
|
||||
+ match crate::sync::futex_wait(&self.count, 0, Some(&relative)) {
|
||||
+ crate::sync::FutexWaitResult::TimedOut => return Err(Errno(ETIMEDOUT)),
|
||||
+ crate::sync::FutexWaitResult::Waited | crate::sync::FutexWaitResult::Stale => {}
|
||||
+ }
|
||||
} else {
|
||||
- // Use futex to wait for the next change, without a timeout
|
||||
- crate::sync::futex_wait(&self.count, value, None);
|
||||
+ crate::sync::futex_wait(&self.count, 0, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
pub fn value(&self) -> c_uint {
|
||||
self.count.load(Ordering::SeqCst)
|
||||
}
|
||||
diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs
|
||||
--- a/src/header/semaphore/mod.rs
|
||||
+++ b/src/header/semaphore/mod.rs
|
||||
@@ -3,12 +3,20 @@
|
||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/semaphore.h.html>.
|
||||
|
||||
use crate::{
|
||||
+ error::{Errno, ResultExt},
|
||||
header::{
|
||||
bits_timespec::timespec,
|
||||
+ errno::{EAGAIN, EINVAL},
|
||||
+ fcntl::{O_CREAT, O_EXCL, O_RDWR},
|
||||
+ sys_mman::{MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE, mmap, munmap, shm_open, shm_unlink},
|
||||
+ sys_stat::stat,
|
||||
time::{CLOCK_MONOTONIC, CLOCK_REALTIME},
|
||||
+ unistd::close,
|
||||
},
|
||||
- platform::types::{c_char, c_int, c_long, c_uint, clockid_t},
|
||||
+ out::Out,
|
||||
+ platform::{ERRNO, Pal, Sys, types::{c_char, c_int, c_long, c_uint, clockid_t, mode_t, c_void}},
|
||||
};
|
||||
+use core::{mem, ptr};
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/semaphore.h.html>.
|
||||
// TODO: Statically verify size and align
|
||||
@@ -20,10 +28,90 @@
|
||||
}
|
||||
pub type RlctSempahore = crate::sync::Semaphore;
|
||||
|
||||
+#[repr(C)]
|
||||
+struct NamedSemaphore {
|
||||
+ sem: RlctSempahore,
|
||||
+}
|
||||
+
|
||||
+const SEM_FAILED_PTR: *mut sem_t = usize::MAX as *mut sem_t;
|
||||
+
|
||||
+unsafe fn map_named_semaphore(
|
||||
+ name: *const c_char,
|
||||
+ oflag: c_int,
|
||||
+ mode: mode_t,
|
||||
+ value: c_uint,
|
||||
+) -> Result<*mut sem_t, Errno> {
|
||||
+ if name.is_null() {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ let mut shm_flags = O_RDWR;
|
||||
+ if oflag & O_CREAT == O_CREAT {
|
||||
+ shm_flags |= O_CREAT;
|
||||
+ }
|
||||
+ if oflag & O_EXCL == O_EXCL {
|
||||
+ shm_flags |= O_EXCL;
|
||||
+ }
|
||||
+
|
||||
+ let fd = unsafe { shm_open(name, shm_flags, mode) };
|
||||
+ if fd < 0 {
|
||||
+ return Err(Errno(ERRNO.get()));
|
||||
+ }
|
||||
+
|
||||
+ let mut st = stat::default();
|
||||
+ if let Err(err) = Sys::fstat(fd, Out::from_mut(&mut st)) {
|
||||
+ let _ = close(fd);
|
||||
+ return Err(err);
|
||||
+ }
|
||||
+
|
||||
+ let size = mem::size_of::<NamedSemaphore>() as i64;
|
||||
+ let created = st.st_size == 0;
|
||||
+ if created {
|
||||
+ if let Err(err) = Sys::ftruncate(fd, size) {
|
||||
+ let _ = close(fd);
|
||||
+ return Err(err);
|
||||
+ }
|
||||
+ } else if st.st_size < size {
|
||||
+ let _ = close(fd);
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ let mapped = unsafe {
|
||||
+ mmap(
|
||||
+ ptr::null_mut(),
|
||||
+ size as usize,
|
||||
+ PROT_READ | PROT_WRITE,
|
||||
+ MAP_SHARED,
|
||||
+ fd,
|
||||
+ 0,
|
||||
+ )
|
||||
+ };
|
||||
+ let _ = close(fd);
|
||||
+ if mapped == MAP_FAILED {
|
||||
+ return Err(Errno(ERRNO.get()));
|
||||
+ }
|
||||
+
|
||||
+ let named = mapped.cast::<NamedSemaphore>();
|
||||
+ if created {
|
||||
+ unsafe {
|
||||
+ named.write(NamedSemaphore {
|
||||
+ sem: RlctSempahore::new(value),
|
||||
+ });
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ Ok(named.cast())
|
||||
+}
|
||||
+
|
||||
/// 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")
|
||||
+ if sem.is_null() || sem == SEM_FAILED_PTR {
|
||||
+ ERRNO.set(EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ unsafe { munmap(sem.cast::<c_void>(), mem::size_of::<NamedSemaphore>()) }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_destroy.html>.
|
||||
@@ -51,12 +139,25 @@
|
||||
|
||||
/// 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 args: ...,
|
||||
) -> *mut sem_t {
|
||||
- todo!("named semaphores")
|
||||
+ let (mode, value) = if oflag & O_CREAT == O_CREAT {
|
||||
+ (unsafe { args.arg::<mode_t>() }, unsafe { args.arg::<c_uint>() })
|
||||
+ } else {
|
||||
+ (0o600 as mode_t, 0)
|
||||
+ };
|
||||
+
|
||||
+ match unsafe { map_named_semaphore(name, oflag, mode, value) } {
|
||||
+ Ok(sem) => sem,
|
||||
+ Err(Errno(errno)) => {
|
||||
+ ERRNO.set(errno);
|
||||
+ SEM_FAILED_PTR
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_post.html>.
|
||||
@@ -70,23 +171,27 @@
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_trywait.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sem_trywait(sem: *mut sem_t) -> c_int {
|
||||
- unsafe { get(sem) }.try_wait();
|
||||
-
|
||||
- 0
|
||||
+ if unsafe { get(sem) }.try_wait() {
|
||||
+ 0
|
||||
+ } else {
|
||||
+ crate::platform::ERRNO.set(EAGAIN);
|
||||
+ -1
|
||||
+ }
|
||||
}
|
||||
|
||||
/// 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>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sem_wait(sem: *mut sem_t) -> c_int {
|
||||
- if let Ok(()) = unsafe { get(sem) }.wait(None, CLOCK_MONOTONIC) {}; // TODO handle error
|
||||
-
|
||||
- 0
|
||||
+ unsafe { get(sem) }
|
||||
+ .wait(None, CLOCK_MONOTONIC)
|
||||
+ .map(|()| 0)
|
||||
+ .or_minus_one_errno()
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_clockwait.html>.
|
||||
@@ -96,18 +201,19 @@
|
||||
clock_id: clockid_t,
|
||||
abstime: *const timespec,
|
||||
) -> c_int {
|
||||
- if let Ok(()) = unsafe { get(sem) }.wait(Some(&unsafe { (*abstime).clone() }), clock_id) {}; // TODO handle error
|
||||
-
|
||||
- 0
|
||||
+ unsafe { get(sem) }
|
||||
+ .wait(Some(&unsafe { (*abstime).clone() }), clock_id)
|
||||
+ .map(|()| 0)
|
||||
+ .or_minus_one_errno()
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_timedwait.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sem_timedwait(sem: *mut sem_t, abstime: *const timespec) -> c_int {
|
||||
- if let Ok(()) = unsafe { get(sem) }.wait(Some(&unsafe { (*abstime).clone() }), CLOCK_REALTIME) {
|
||||
- }; // TODO handle error
|
||||
-
|
||||
- 0
|
||||
+ unsafe { get(sem) }
|
||||
+ .wait(Some(&unsafe { (*abstime).clone() }), CLOCK_REALTIME)
|
||||
+ .map(|()| 0)
|
||||
+ .or_minus_one_errno()
|
||||
}
|
||||
|
||||
unsafe fn get<'any>(sem: *mut sem_t) -> &'any RlctSempahore {
|
||||
diff --git a/src/header/semaphore/cbindgen.toml b/src/header/semaphore/cbindgen.toml
|
||||
--- a/src/header/semaphore/cbindgen.toml
|
||||
+++ b/src/header/semaphore/cbindgen.toml
|
||||
@@ -2,6 +2,9 @@
|
||||
include_guard = "_RELIBC_SEMAPHORE_H"
|
||||
after_includes = """
|
||||
#include <bits/timespec.h> // for timespec
|
||||
+"""
|
||||
+trailer = """
|
||||
+#define SEM_FAILED ((sem_t *) -1)
|
||||
"""
|
||||
language = "C"
|
||||
style = "Type"
|
||||
diff --git a/tests/Makefile.tests.mk b/tests/Makefile.tests.mk
|
||||
--- a/tests/Makefile.tests.mk
|
||||
+++ b/tests/Makefile.tests.mk
|
||||
@@ -312,6 +312,8 @@
|
||||
grp/getgrgid_r \
|
||||
grp/getgrnam_r \
|
||||
grp/gr_iter \
|
||||
+ semaphore/named \
|
||||
+ semaphore/unnamed \
|
||||
waitid \
|
||||
waitpid \
|
||||
waitpid_multiple \
|
||||
diff --git a/tests/semaphore/unnamed.c b/tests/semaphore/unnamed.c
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/tests/semaphore/unnamed.c
|
||||
@@ -0,0 +1,57 @@
|
||||
+#include <assert.h>
|
||||
+#include <errno.h>
|
||||
+#include <pthread.h>
|
||||
+#include <semaphore.h>
|
||||
+#include <time.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+static sem_t sem;
|
||||
+
|
||||
+static void *post_after_delay(void *arg)
|
||||
+{
|
||||
+ (void)arg;
|
||||
+ usleep(50000);
|
||||
+ assert(sem_post(&sem) == 0);
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+static long elapsed_ns(const struct timespec *start, const struct timespec *end)
|
||||
+{
|
||||
+ return (end->tv_sec - start->tv_sec) * 1000000000L + (end->tv_nsec - start->tv_nsec);
|
||||
+}
|
||||
+
|
||||
+int main(void)
|
||||
+{
|
||||
+ assert(sem_init(&sem, 0, 0) == 0);
|
||||
+
|
||||
+ errno = 0;
|
||||
+ assert(sem_trywait(&sem) == -1);
|
||||
+ assert(errno == EAGAIN);
|
||||
+
|
||||
+ struct timespec deadline;
|
||||
+ assert(clock_gettime(CLOCK_REALTIME, &deadline) == 0);
|
||||
+ deadline.tv_nsec += 20000000L;
|
||||
+ if (deadline.tv_nsec >= 1000000000L) {
|
||||
+ deadline.tv_sec += 1;
|
||||
+ deadline.tv_nsec -= 1000000000L;
|
||||
+ }
|
||||
+
|
||||
+ errno = 0;
|
||||
+ assert(sem_timedwait(&sem, &deadline) == -1);
|
||||
+ assert(errno == ETIMEDOUT);
|
||||
+
|
||||
+ pthread_t thread;
|
||||
+ assert(pthread_create(&thread, NULL, post_after_delay, NULL) == 0);
|
||||
+
|
||||
+ struct timespec start;
|
||||
+ struct timespec end;
|
||||
+ assert(clock_gettime(CLOCK_MONOTONIC, &start) == 0);
|
||||
+ assert(sem_wait(&sem) == 0);
|
||||
+ assert(clock_gettime(CLOCK_MONOTONIC, &end) == 0);
|
||||
+ assert(elapsed_ns(&start, &end) >= 20000000L);
|
||||
+
|
||||
+ assert(pthread_join(thread, NULL) == 0);
|
||||
+ assert(sem_destroy(&sem) == 0);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
diff --git a/tests/semaphore/named.c b/tests/semaphore/named.c
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/tests/semaphore/named.c
|
||||
@@ -0,0 +1,54 @@
|
||||
+#include <assert.h>
|
||||
+#include <fcntl.h>
|
||||
+#include <pthread.h>
|
||||
+#include <semaphore.h>
|
||||
+#include <stdio.h>
|
||||
+#include <sys/types.h>
|
||||
+#include <time.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+static sem_t *first;
|
||||
+static sem_t *second;
|
||||
+
|
||||
+static void *post_after_delay(void *arg)
|
||||
+{
|
||||
+ (void)arg;
|
||||
+ usleep(50000);
|
||||
+ assert(sem_post(second) == 0);
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+static long elapsed_ns(const struct timespec *start, const struct timespec *end)
|
||||
+{
|
||||
+ return (end->tv_sec - start->tv_sec) * 1000000000L + (end->tv_nsec - start->tv_nsec);
|
||||
+}
|
||||
+
|
||||
+int main(void)
|
||||
+{
|
||||
+ char name[64];
|
||||
+ snprintf(name, sizeof(name), "/relibc_named_sem_%ld", (long)getpid());
|
||||
+
|
||||
+ sem_unlink(name);
|
||||
+
|
||||
+ first = sem_open(name, O_CREAT | O_EXCL, 0600, 0);
|
||||
+ assert(first != SEM_FAILED);
|
||||
+ second = sem_open(name, 0);
|
||||
+ assert(second != SEM_FAILED);
|
||||
+
|
||||
+ pthread_t thread;
|
||||
+ assert(pthread_create(&thread, NULL, post_after_delay, NULL) == 0);
|
||||
+
|
||||
+ struct timespec start;
|
||||
+ struct timespec end;
|
||||
+ assert(clock_gettime(CLOCK_MONOTONIC, &start) == 0);
|
||||
+ assert(sem_wait(first) == 0);
|
||||
+ assert(clock_gettime(CLOCK_MONOTONIC, &end) == 0);
|
||||
+ assert(elapsed_ns(&start, &end) >= 20000000L);
|
||||
+
|
||||
+ assert(pthread_join(thread, NULL) == 0);
|
||||
+ assert(sem_close(second) == 0);
|
||||
+ assert(sem_close(first) == 0);
|
||||
+ assert(sem_unlink(name) == 0);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
@@ -1,178 +0,0 @@
|
||||
diff --git a/src/header/sys_socket/mod.rs b/src/header/sys_socket/mod.rs
|
||||
--- a/src/header/sys_socket/mod.rs
|
||||
+++ b/src/header/sys_socket/mod.rs
|
||||
@@ -5,7 +5,7 @@
|
||||
use core::{mem, ptr};
|
||||
|
||||
use crate::{
|
||||
- error::ResultExt,
|
||||
+ error::{Errno, ResultExt},
|
||||
header::{bits_iovec::iovec, bits_safamily_t::sa_family_t, bits_socklen_t::socklen_t},
|
||||
platform::{
|
||||
PalSocket, Sys,
|
||||
@@ -236,6 +236,48 @@
|
||||
socket,
|
||||
address,
|
||||
address_len
|
||||
+ )
|
||||
+}
|
||||
+
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn getpeereid(socket: c_int, euid: *mut uid_t, egid: *mut gid_t) -> c_int {
|
||||
+ trace_expr!(
|
||||
+ (|| {
|
||||
+ if euid.is_null() || egid.is_null() {
|
||||
+ return Err(Errno(crate::header::errno::EFAULT));
|
||||
+ }
|
||||
+
|
||||
+ let mut cred = ucred {
|
||||
+ pid: 0,
|
||||
+ uid: 0,
|
||||
+ gid: 0,
|
||||
+ };
|
||||
+ let mut len = mem::size_of::<ucred>() as socklen_t;
|
||||
+ unsafe {
|
||||
+ Sys::getsockopt(
|
||||
+ socket,
|
||||
+ constants::SOL_SOCKET,
|
||||
+ constants::SO_PEERCRED,
|
||||
+ &mut cred as *mut ucred as *mut c_void,
|
||||
+ &mut len,
|
||||
+ )?;
|
||||
+ }
|
||||
+
|
||||
+ if (len as usize) < mem::size_of::<ucred>() {
|
||||
+ return Err(Errno(crate::header::errno::EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ unsafe {
|
||||
+ *euid = cred.uid;
|
||||
+ *egid = cred.gid;
|
||||
+ }
|
||||
+ Ok(0)
|
||||
+ })()
|
||||
+ .or_minus_one_errno(),
|
||||
+ "getpeereid({}, {:p}, {:p})",
|
||||
+ socket,
|
||||
+ euid,
|
||||
+ egid
|
||||
)
|
||||
}
|
||||
|
||||
diff --git a/tests/Makefile.tests.mk b/tests/Makefile.tests.mk
|
||||
--- a/tests/Makefile.tests.mk
|
||||
+++ b/tests/Makefile.tests.mk
|
||||
@@ -137,6 +137,8 @@
|
||||
string/stpcpy \
|
||||
string/stpncpy \
|
||||
strings \
|
||||
+ sys_socket/passcred \
|
||||
+ sys_socket/peercred \
|
||||
sys_socket/recv \
|
||||
sys_socket/recvfrom \
|
||||
sys_socket/unixrecv \
|
||||
diff --git a/tests/sys_socket/peercred.c b/tests/sys_socket/peercred.c
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/tests/sys_socket/peercred.c
|
||||
@@ -0,0 +1,33 @@
|
||||
+#include <assert.h>
|
||||
+#include <sys/socket.h>
|
||||
+#include <sys/types.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+#include "test_helpers.h"
|
||||
+
|
||||
+int main(void)
|
||||
+{
|
||||
+ int sv[2];
|
||||
+ int status = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||
+ ERROR_IF(socketpair, status, == -1);
|
||||
+
|
||||
+ struct ucred cred;
|
||||
+ socklen_t len = sizeof(cred);
|
||||
+ status = getsockopt(sv[0], SOL_SOCKET, SO_PEERCRED, &cred, &len);
|
||||
+ ERROR_IF(getsockopt, status, == -1);
|
||||
+ assert(len == sizeof(cred));
|
||||
+ assert(cred.uid == getuid());
|
||||
+ assert(cred.gid == getgid());
|
||||
+ assert(cred.pid > 0);
|
||||
+
|
||||
+ uid_t euid;
|
||||
+ gid_t egid;
|
||||
+ status = getpeereid(sv[0], &euid, &egid);
|
||||
+ ERROR_IF(getpeereid, status, == -1);
|
||||
+ assert(euid == getuid());
|
||||
+ assert(egid == getgid());
|
||||
+
|
||||
+ close(sv[0]);
|
||||
+ close(sv[1]);
|
||||
+ return 0;
|
||||
+}
|
||||
diff --git a/tests/sys_socket/passcred.c b/tests/sys_socket/passcred.c
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/tests/sys_socket/passcred.c
|
||||
@@ -0,0 +1,62 @@
|
||||
+#include <assert.h>
|
||||
+#include <string.h>
|
||||
+#include <sys/socket.h>
|
||||
+#include <sys/types.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+#include "test_helpers.h"
|
||||
+
|
||||
+int main(void)
|
||||
+{
|
||||
+ int sv[2];
|
||||
+ int status = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||
+ ERROR_IF(socketpair, status, == -1);
|
||||
+
|
||||
+ int one = 1;
|
||||
+ status = setsockopt(sv[1], SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
|
||||
+ ERROR_IF(setsockopt, status, == -1);
|
||||
+
|
||||
+ const char payload[] = "x";
|
||||
+ ssize_t sent = send(sv[0], payload, sizeof(payload), 0);
|
||||
+ ERROR_IF(send, sent, == -1);
|
||||
+ assert(sent == (ssize_t)sizeof(payload));
|
||||
+
|
||||
+ char byte = '\0';
|
||||
+ struct iovec iov = {
|
||||
+ .iov_base = &byte,
|
||||
+ .iov_len = sizeof(byte),
|
||||
+ };
|
||||
+ union {
|
||||
+ char buf[CMSG_SPACE(sizeof(struct ucred))];
|
||||
+ struct cmsghdr align;
|
||||
+ } control;
|
||||
+ memset(&control, 0, sizeof(control));
|
||||
+
|
||||
+ struct msghdr msg = {
|
||||
+ .msg_name = NULL,
|
||||
+ .msg_namelen = 0,
|
||||
+ .msg_iov = &iov,
|
||||
+ .msg_iovlen = 1,
|
||||
+ .msg_control = control.buf,
|
||||
+ .msg_controllen = sizeof(control.buf),
|
||||
+ .msg_flags = 0,
|
||||
+ };
|
||||
+
|
||||
+ ssize_t recvd = recvmsg(sv[1], &msg, 0);
|
||||
+ ERROR_IF(recvmsg, recvd, == -1);
|
||||
+ assert(recvd == (ssize_t)sizeof(payload));
|
||||
+ assert(byte == 'x');
|
||||
+
|
||||
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
+ assert(cmsg != NULL);
|
||||
+ assert(cmsg->cmsg_level == SOL_SOCKET);
|
||||
+ assert(cmsg->cmsg_type == SCM_CREDENTIALS);
|
||||
+ struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);
|
||||
+ assert(cred->uid == getuid());
|
||||
+ assert(cred->gid == getgid());
|
||||
+ assert(cred->pid > 0);
|
||||
+
|
||||
+ close(sv[0]);
|
||||
+ close(sv[1]);
|
||||
+ return 0;
|
||||
+}
|
||||
@@ -1,26 +0,0 @@
|
||||
diff --git a/src/header/sys_socket/constants.rs b/src/header/sys_socket/constants.rs
|
||||
--- a/src/header/sys_socket/constants.rs
|
||||
+++ b/src/header/sys_socket/constants.rs
|
||||
@@ -48,8 +48,9 @@ pub const MSG_OOB: c_int = 1;
|
||||
pub const MSG_PEEK: c_int = 2;
|
||||
pub const MSG_TRUNC: c_int = 32;
|
||||
pub const MSG_DONTWAIT: c_int = 64;
|
||||
pub const MSG_WAITALL: c_int = 256;
|
||||
pub const MSG_CMSG_CLOEXEC: c_int = 0x40000000;
|
||||
+pub const MSG_NOSIGNAL: c_int = 0x4000;
|
||||
|
||||
pub const IP_ADD_SOURCE_MEMBERSHIP: c_int = 70;
|
||||
pub const IP_DROP_SOURCE_MEMBERSHIP: c_int = 71;
|
||||
diff --git a/src/header/sys_socket/mod.rs b/src/header/sys_socket/mod.rs
|
||||
--- a/src/header/sys_socket/mod.rs
|
||||
+++ b/src/header/sys_socket/mod.rs
|
||||
@@ -330,7 +330,8 @@ pub unsafe extern "C" fn recvfrom(
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/recvmsg.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn recvmsg(socket: c_int, msg: *mut msghdr, flags: c_int) -> ssize_t {
|
||||
- unsafe { Sys::recvmsg(socket, msg, flags) }
|
||||
+ let flags = flags & !constants::MSG_NOSIGNAL;
|
||||
+ unsafe { Sys::recvmsg(socket, msg, flags) }
|
||||
.map(|r| r as ssize_t)
|
||||
.or_minus_one_errno()
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
diff --git a/redox-rt/src/proc.rs b/redox-rt/src/proc.rs
|
||||
index 05e42757..f2eba231 100644
|
||||
--- a/redox-rt/src/proc.rs
|
||||
+++ b/redox-rt/src/proc.rs
|
||||
@@ -1119,7 +1119,7 @@ pub fn new_child_process(args: &ForkArgs<'_>) -> Result<NewChildProc> {
|
||||
pid: 0,
|
||||
euid: 0,
|
||||
egid: 0,
|
||||
- ens: 1,
|
||||
+ prio: 1,
|
||||
debug_name: {
|
||||
let mut buf = [0; 32];
|
||||
let src = b"[init]";
|
||||
@@ -1,53 +0,0 @@
|
||||
diff --git a/src/header/sys_socket/constants.rs b/src/header/sys_socket/constants.rs
|
||||
index ec42889b..c91ffb1a 100644
|
||||
--- a/src/header/sys_socket/constants.rs
|
||||
+++ b/src/header/sys_socket/constants.rs
|
||||
@@ -75,3 +75,6 @@ pub const SHUT_WR: c_int = 1;
|
||||
|
||||
pub const SCM_RIGHTS: c_int = 1;
|
||||
pub const SCM_CREDENTIALS: c_int = 2;
|
||||
+
|
||||
+pub const IPPROTO_TCP: c_int = 6;
|
||||
+pub const TCP_NODELAY: c_int = 1;
|
||||
diff --git a/src/platform/redox/socket.rs b/src/platform/redox/socket.rs
|
||||
index d223c36f..2faae4f5 100644
|
||||
--- a/src/platform/redox/socket.rs
|
||||
+++ b/src/platform/redox/socket.rs
|
||||
@@ -1051,10 +1051,10 @@ impl PalSocket for Sys {
|
||||
_ => {
|
||||
let metadata = [SocketCall::SetSockOpt as u64, option_name as u64];
|
||||
let payload = unsafe {
|
||||
- slice::from_raw_parts_mut(option_value as *mut u8, option_len as usize)
|
||||
+ slice::from_raw_parts(option_value as *const u8, option_len as usize)
|
||||
};
|
||||
let call_flags = CallFlags::empty();
|
||||
- redox_rt::sys::sys_call_rw(
|
||||
+ redox_rt::sys::sys_call_wo(
|
||||
socket as usize,
|
||||
payload,
|
||||
CallFlags::empty(),
|
||||
@@ -1063,6 +1063,24 @@ impl PalSocket for Sys {
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
+ 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(());
|
||||
+ }
|
||||
+ _ => (),
|
||||
+ }
|
||||
+ }
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -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,21 +1069,16 @@ 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(());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
Fix sys/timerfd.h cbindgen.toml to generate proper C headers instead of C++.
|
||||
|
||||
The empty cbindgen.toml from P3-timerfd-relative.patch caused cbindgen to
|
||||
generate C++ output (cstdarg, constexpr, etc.) in the installed
|
||||
sys/timerfd.h. C compilers including this header would fail with
|
||||
"cstdarg: No such file or directory". Add language="C", after_includes
|
||||
for bits/timespec.h, and explicit export list for the timerfd constants.
|
||||
|
||||
diff --git a/src/header/sys_timerfd/cbindgen.toml b/src/header/sys_timerfd/cbindgen.toml
|
||||
--- a/src/header/sys_timerfd/cbindgen.toml
|
||||
+++ b/src/header/sys_timerfd/cbindgen.toml
|
||||
@@ -1,12 +1,23 @@
|
||||
sys_includes = ["time.h"]
|
||||
+after_includes = """
|
||||
+#include <bits/timespec.h> // for itimerspec
|
||||
+"""
|
||||
include_guard = "_SYS_TIMERFD_H"
|
||||
language = "C"
|
||||
style = "Tag"
|
||||
no_includes = true
|
||||
cpp_compat = true
|
||||
|
||||
[enum]
|
||||
prefix_with_name = true
|
||||
|
||||
+[export]
|
||||
+include = [
|
||||
+ "TFD_CLOEXEC",
|
||||
+ "TFD_NONBLOCK",
|
||||
+ "TFD_TIMER_ABSTIME",
|
||||
+ "TFD_TIMER_CANCEL_ON_SET",
|
||||
+]
|
||||
+
|
||||
[export.rename]
|
||||
"itimerspec" = "struct itimerspec"
|
||||
@@ -1,177 +0,0 @@
|
||||
diff --git a/src/header/sys_timerfd/cbindgen.toml b/src/header/sys_timerfd/cbindgen.toml
|
||||
new file mode 100644
|
||||
index 0000000..9469888
|
||||
--- /dev/null
|
||||
+++ b/src/header/sys_timerfd/cbindgen.toml
|
||||
@@ -0,0 +1,12 @@
|
||||
+sys_includes = ["time.h"]
|
||||
+include_guard = "_SYS_TIMERFD_H"
|
||||
+language = "C"
|
||||
+style = "Tag"
|
||||
+no_includes = true
|
||||
+cpp_compat = true
|
||||
+
|
||||
+[enum]
|
||||
+prefix_with_name = true
|
||||
+
|
||||
+[export.rename]
|
||||
+"itimerspec" = "struct itimerspec"
|
||||
diff --git a/src/header/sys_timerfd/mod.rs b/src/header/sys_timerfd/mod.rs
|
||||
index 0959d39..916066f 100644
|
||||
--- a/src/header/sys_timerfd/mod.rs
|
||||
+++ b/src/header/sys_timerfd/mod.rs
|
||||
@@ -1,10 +1,8 @@
|
||||
//! `sys/timerfd.h` implementation.
|
||||
//!
|
||||
//! Non-POSIX, see <https://man7.org/linux/man-pages/man2/timerfd_create.2.html>.
|
||||
-
|
||||
use alloc::{collections::BTreeMap, format};
|
||||
use core::{mem, slice, sync::atomic::{AtomicBool, Ordering}};
|
||||
-
|
||||
use crate::{
|
||||
c_str::{CStr, CString},
|
||||
error::{Errno, ResultExt},
|
||||
@@ -14,13 +12,9 @@ use crate::{
|
||||
fcntl::{O_CLOEXEC, O_NONBLOCK, O_RDWR},
|
||||
},
|
||||
out::Out,
|
||||
- platform::{
|
||||
- Pal, Sys,
|
||||
- types::{c_int, clockid_t},
|
||||
- },
|
||||
+ platform::{Pal, Sys, types::{c_int, clockid_t}},
|
||||
sync::Mutex,
|
||||
};
|
||||
-
|
||||
pub use crate::header::time::itimerspec;
|
||||
|
||||
pub const TFD_CLOEXEC: c_int = 0x80000;
|
||||
@@ -30,104 +24,64 @@ pub const TFD_TIMER_CANCEL_ON_SET: c_int = 0x2;
|
||||
|
||||
const NSEC_PER_SEC: i64 = 1_000_000_000;
|
||||
|
||||
+struct TimerState { clockid: clockid_t }
|
||||
+
|
||||
static MAP_INIT: AtomicBool = AtomicBool::new(false);
|
||||
-static TIMERFD_CLOCKIDS: Mutex<Option<BTreeMap<c_int, clockid_t>>> = Mutex::new(None);
|
||||
+static TIMERFD_STATE: Mutex<Option<BTreeMap<c_int, TimerState>>> = Mutex::new(None);
|
||||
|
||||
-fn with_map<R>(f: impl FnOnce(&mut BTreeMap<c_int, clockid_t>) -> R) -> R {
|
||||
- let mut guard = TIMERFD_CLOCKIDS.lock();
|
||||
- if !MAP_INIT.load(Ordering::Acquire) {
|
||||
- *guard = Some(BTreeMap::new());
|
||||
- MAP_INIT.store(true, Ordering::Release);
|
||||
- }
|
||||
+fn with_map<R>(f: impl FnOnce(&mut BTreeMap<c_int, TimerState>) -> R) -> R {
|
||||
+ let mut guard = TIMERFD_STATE.lock();
|
||||
+ if !MAP_INIT.load(Ordering::Acquire) { *guard = Some(BTreeMap::new()); MAP_INIT.store(true, Ordering::Release); }
|
||||
f(guard.as_mut().unwrap())
|
||||
}
|
||||
-
|
||||
fn add_timespec(a: ×pec, b: ×pec) -> timespec {
|
||||
let total_nsec = a.tv_nsec as i64 + b.tv_nsec as i64;
|
||||
let mut sec = a.tv_sec + b.tv_sec + (total_nsec / NSEC_PER_SEC) as i64;
|
||||
let mut nsec = (total_nsec % NSEC_PER_SEC) as i64;
|
||||
- if nsec < 0 {
|
||||
- sec -= 1;
|
||||
- nsec += NSEC_PER_SEC;
|
||||
- }
|
||||
+ if nsec < 0 { sec -= 1; nsec += NSEC_PER_SEC; }
|
||||
timespec { tv_sec: sec, tv_nsec: nsec }
|
||||
}
|
||||
-
|
||||
fn read_exact(fd: c_int, buf: &mut [u8]) -> Result<(), Errno> {
|
||||
- match Sys::read(fd, buf)? {
|
||||
- n if n == buf.len() => Ok(()),
|
||||
- _ => Err(Errno(EIO)),
|
||||
- }
|
||||
+ match Sys::read(fd, buf)? { n if n == buf.len() => Ok(()), _ => Err(Errno(EIO)) }
|
||||
}
|
||||
-
|
||||
fn write_exact(fd: c_int, buf: &[u8]) -> Result<(), Errno> {
|
||||
- match Sys::write(fd, buf)? {
|
||||
- n if n == buf.len() => Ok(()),
|
||||
- _ => Err(Errno(EIO)),
|
||||
- }
|
||||
+ match Sys::write(fd, buf)? { n if n == buf.len() => Ok(()), _ => Err(Errno(EIO)) }
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn timerfd_create(clockid: clockid_t, flags: c_int) -> c_int {
|
||||
- let supported = TFD_CLOEXEC | TFD_NONBLOCK;
|
||||
- if flags & !supported != 0 {
|
||||
- return Err::<c_int, _>(Errno(EINVAL)).or_minus_one_errno();
|
||||
- }
|
||||
-
|
||||
+ if flags & !(TFD_CLOEXEC | TFD_NONBLOCK) != 0 { return Err::<c_int,_>(Errno(EINVAL)).or_minus_one_errno(); }
|
||||
let mut oflag = O_RDWR;
|
||||
- if flags & TFD_CLOEXEC == TFD_CLOEXEC {
|
||||
- oflag |= O_CLOEXEC;
|
||||
- }
|
||||
- if flags & TFD_NONBLOCK == TFD_NONBLOCK {
|
||||
- oflag |= O_NONBLOCK;
|
||||
- }
|
||||
-
|
||||
- let path = match CString::new(format!("/scheme/time/{clockid}")) {
|
||||
- Ok(path) => path,
|
||||
- Err(_) => return Err::<c_int, _>(Errno(EINVAL)).or_minus_one_errno(),
|
||||
- };
|
||||
- let fd = Sys::open(CStr::borrow(&path), oflag, 0).or_minus_one_errno()?;
|
||||
-
|
||||
- // Register the clockid for this fd so timerfd_settime can convert relative times
|
||||
- with_map(|map| { map.insert(fd, clockid); });
|
||||
-
|
||||
+ if flags & TFD_CLOEXEC == TFD_CLOEXEC { oflag |= O_CLOEXEC; }
|
||||
+ if flags & TFD_NONBLOCK == TFD_NONBLOCK { oflag |= O_NONBLOCK; }
|
||||
+ let path = match CString::new(format!("/scheme/time/{clockid}")) { Ok(p) => p, Err(_) => return Err::<c_int,_>(Errno(EINVAL)).or_minus_one_errno() };
|
||||
+ let fd = match Sys::open(CStr::borrow(&path), oflag, 0) { Ok(fd) => fd, Err(Errno(e)) => return Err::<c_int,_>(Errno(e)).or_minus_one_errno() };
|
||||
+ with_map(|map| { map.insert(fd, TimerState { clockid }); });
|
||||
fd
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn timerfd_settime(fd: c_int, flags: c_int, new: *const itimerspec, old: *mut itimerspec) -> c_int {
|
||||
- let supported = TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET;
|
||||
- if flags & !supported != 0 {
|
||||
- return Err::<c_int, _>(Errno(EINVAL)).or_minus_one_errno();
|
||||
- }
|
||||
- if new.is_null() {
|
||||
- return Err::<c_int, _>(Errno(EFAULT)).or_minus_one_errno();
|
||||
- }
|
||||
- if !old.is_null() && unsafe { timerfd_gettime(fd, old) } < 0 {
|
||||
- return -1;
|
||||
- }
|
||||
+ if flags & !(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET) != 0 { return Err::<c_int,_>(Errno(EINVAL)).or_minus_one_errno(); }
|
||||
+ if new.is_null() { return Err::<c_int,_>(Errno(EFAULT)).or_minus_one_errno(); }
|
||||
+ if !old.is_null() && unsafe { timerfd_gettime(fd, old) } < 0 { return -1; }
|
||||
let spec = unsafe { &*new };
|
||||
- let mut value = spec.it_value;
|
||||
-
|
||||
- // Convert relative time to absolute if TFD_TIMER_ABSTIME is not set
|
||||
+ let mut value = spec.it_value.clone();
|
||||
+ // TFD_TIMER_CANCEL_ON_SET: flag accepted; clock-change notification requires
|
||||
+ // kernel infrastructure not yet available. This is a bounded compatibility surface.
|
||||
if flags & TFD_TIMER_ABSTIME == 0 {
|
||||
- let clockid = with_map(|map| map.get(&fd).copied()).unwrap_or(0);
|
||||
+ let clockid = with_map(|map| map.get(&fd).map_or(0, |s| s.clockid));
|
||||
let mut now = timespec::default();
|
||||
- if Sys::clock_gettime(clockid, Out::from_mut(&mut now)).is_err() {
|
||||
- return Err::<c_int, _>(Errno(EBADF)).or_minus_one_errno();
|
||||
- }
|
||||
+ if Sys::clock_gettime(clockid, Out::from_mut(&mut now)).is_err() { return Err::<c_int,_>(Errno(EBADF)).or_minus_one_errno(); }
|
||||
value = add_timespec(&now, &value);
|
||||
}
|
||||
-
|
||||
let buf = unsafe { slice::from_raw_parts((&raw const value).cast::<u8>(), mem::size_of::<timespec>()) };
|
||||
write_exact(fd, buf).map(|()| 0).or_minus_one_errno()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn timerfd_gettime(fd: c_int, curr: *mut itimerspec) -> c_int {
|
||||
- if curr.is_null() {
|
||||
- return Err::<c_int, _>(Errno(EFAULT)).or_minus_one_errno();
|
||||
- }
|
||||
+ if curr.is_null() { return Err::<c_int,_>(Errno(EFAULT)).or_minus_one_errno(); }
|
||||
let curr = unsafe { &mut *curr };
|
||||
curr.it_interval = timespec::default();
|
||||
let buf = unsafe { slice::from_raw_parts_mut((&raw mut curr.it_value).cast::<u8>(), mem::size_of::<timespec>()) };
|
||||
@@ -1,25 +0,0 @@
|
||||
diff --git a/src/header/sys_timerfd/mod.rs b/src/header/sys_timerfd/mod.rs
|
||||
index 0959d39..9a4a23e 100644
|
||||
--- a/src/header/sys_timerfd/mod.rs
|
||||
+++ b/src/header/sys_timerfd/mod.rs
|
||||
@@ -86,7 +86,10 @@ pub extern "C" fn timerfd_create(clockid: clockid_t, flags: c_int) -> c_int {
|
||||
Ok(path) => path,
|
||||
Err(_) => return Err::<c_int, _>(Errno(EINVAL)).or_minus_one_errno(),
|
||||
};
|
||||
- let fd = Sys::open(CStr::borrow(&path), oflag, 0).or_minus_one_errno()?;
|
||||
+ let fd = match Sys::open(CStr::borrow(&path), oflag, 0) {
|
||||
+ Ok(fd) => fd,
|
||||
+ Err(Errno(e)) => { return Err::<c_int, _>(Errno(e)).or_minus_one_errno(); }
|
||||
+ };
|
||||
|
||||
// Register the clockid for this fd so timerfd_settime can convert relative times
|
||||
with_map(|map| { map.insert(fd, clockid); });
|
||||
@@ -107,7 +110,7 @@ pub unsafe extern "C" fn timerfd_settime(fd: c_int, flags: c_int, new: *const it
|
||||
return -1;
|
||||
}
|
||||
let spec = unsafe { &*new };
|
||||
- let mut value = spec.it_value;
|
||||
+ let mut value = spec.it_value.clone();
|
||||
|
||||
// Convert relative time to absolute if TFD_TIMER_ABSTIME is not set
|
||||
if flags & TFD_TIMER_ABSTIME == 0 {
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
diff --git a/src/header/dl-tls/mod.rs b/src/header/dl-tls/mod.rs
|
||||
index 0196bf7b..785ab343 100644
|
||||
--- a/src/header/dl-tls/mod.rs
|
||||
+++ b/src/header/dl-tls/mod.rs
|
||||
@@ -51,6 +51,14 @@ pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void {
|
||||
let layout = Layout::from_size_align_unchecked(master.segment_size, 16);
|
||||
let ptr = alloc(layout);
|
||||
|
||||
+ if ptr.is_null() {
|
||||
+ eprintln!(
|
||||
+ "__tls_get_addr: TLS allocation failed for module {:#x} (size {})",
|
||||
+ ti.ti_module, master.segment_size
|
||||
+ );
|
||||
+ return ptr::null_mut();
|
||||
+ }
|
||||
+
|
||||
ptr::copy_nonoverlapping(master.ptr, ptr, master.image_size);
|
||||
ptr::write_bytes(
|
||||
ptr.add(master.image_size),
|
||||
@@ -68,10 +76,11 @@ pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void {
|
||||
let mut ptr = tcb.dtv_mut()[dtv_index];
|
||||
|
||||
if ptr.is_null() {
|
||||
- panic!(
|
||||
- "__tls_get_addr({ti:p}: {:#x}, {:#x})",
|
||||
- ti.ti_module, ti.ti_offset
|
||||
+ eprintln!(
|
||||
+ "__tls_get_addr({:p}: {:#x}, {:#x}): DTV entry is null, module {} TLS not available",
|
||||
+ ti, ti.ti_module, ti.ti_offset, ti.ti_module
|
||||
);
|
||||
+ return ptr::null_mut();
|
||||
}
|
||||
|
||||
if cfg!(target_arch = "riscv64") {
|
||||
@@ -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,8 +1262,8 @@
|
||||
/// 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,9 +0,0 @@
|
||||
diff --git a/src/header/sys_wait/cbindgen.toml b/src/header/sys_wait/cbindgen.toml
|
||||
--- a/src/header/sys_wait/cbindgen.toml
|
||||
+++ b/src/header/sys_wait/cbindgen.toml
|
||||
@@ -1,4 +1,4 @@
|
||||
-sys_includes = ["sys/types.h", "sys/resource.h"]
|
||||
+sys_includes = ["sys/types.h", "sys/resource.h", "signal.h"]
|
||||
include_guard = "_RELIBC_SYS_WAIT_H"
|
||||
after_includes = """
|
||||
|
||||
@@ -1,245 +0,0 @@
|
||||
diff --git a/src/header/sys_wait/mod.rs b/src/header/sys_wait/mod.rs
|
||||
--- a/src/header/sys_wait/mod.rs
|
||||
+++ b/src/header/sys_wait/mod.rs
|
||||
@@ -4,12 +4,16 @@
|
||||
|
||||
use crate::{
|
||||
error::ResultExt,
|
||||
+ header::signal::siginfo_t,
|
||||
out::Out,
|
||||
platform::{
|
||||
- Pal, Sys,
|
||||
- types::{c_int, pid_t},
|
||||
+ ERRNO, Pal, Sys,
|
||||
+ types::{c_int, c_uint, pid_t},
|
||||
},
|
||||
};
|
||||
+
|
||||
+pub type idtype_t = c_int;
|
||||
+pub type id_t = c_uint;
|
||||
|
||||
pub const WNOHANG: c_int = 1;
|
||||
pub const WUNTRACED: c_int = 2;
|
||||
@@ -24,25 +28,143 @@
|
||||
#[allow(overflowing_literals)]
|
||||
pub const __WCLONE: c_int = 0x8000_0000;
|
||||
|
||||
+pub const P_ALL: idtype_t = 0;
|
||||
+pub const P_PID: idtype_t = 1;
|
||||
+pub const P_PGID: idtype_t = 2;
|
||||
+
|
||||
+pub const CLD_EXITED: c_int = 1;
|
||||
+pub const CLD_KILLED: c_int = 2;
|
||||
+pub const CLD_DUMPED: c_int = 3;
|
||||
+pub const CLD_TRAPPED: c_int = 4;
|
||||
+pub const CLD_STOPPED: c_int = 5;
|
||||
+pub const CLD_CONTINUED: c_int = 6;
|
||||
+
|
||||
+fn wexitstatus(status: c_int) -> c_int {
|
||||
+ (status >> 8) & 0xff
|
||||
+}
|
||||
+
|
||||
+fn wtermsig(status: c_int) -> c_int {
|
||||
+ status & 0x7f
|
||||
+}
|
||||
+
|
||||
+fn wstopsig(status: c_int) -> c_int {
|
||||
+ wexitstatus(status)
|
||||
+}
|
||||
+
|
||||
+fn wcoredump(status: c_int) -> bool {
|
||||
+ (status & 0x80) != 0
|
||||
+}
|
||||
+
|
||||
+fn wifexited(status: c_int) -> bool {
|
||||
+ (status & 0x7f) == 0
|
||||
+}
|
||||
+
|
||||
+fn wifstopped(status: c_int) -> bool {
|
||||
+ (status & 0xff) == 0x7f
|
||||
+}
|
||||
+
|
||||
+fn wifcontinued(status: c_int) -> bool {
|
||||
+ status == 0xffff
|
||||
+}
|
||||
+
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn wait(stat_loc: *mut c_int) -> pid_t {
|
||||
unsafe { waitpid(!0, stat_loc, 0) }
|
||||
}
|
||||
|
||||
-/*
|
||||
- * TODO: implement idtype_t, id_t, and siginfo_t
|
||||
- *
|
||||
- * #[unsafe(no_mangle)]
|
||||
- * pub unsafe extern "C" fn waitid(
|
||||
- * idtype: idtype_t,
|
||||
- * id: id_t,
|
||||
- * infop: siginfo_t,
|
||||
- * options: c_int
|
||||
- * ) -> c_int {
|
||||
- * unimplemented!();
|
||||
- * }
|
||||
- */
|
||||
+fn map_waitid_target(idtype: idtype_t, id: id_t) -> Option<pid_t> {
|
||||
+ match idtype {
|
||||
+ P_ALL => Some(-1),
|
||||
+ P_PID => Some(id as pid_t),
|
||||
+ P_PGID => Some(if id == 0 { 0 } else { -(id as pid_t) }),
|
||||
+ _ => None,
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn map_waitid_options(options: c_int) -> Option<c_int> {
|
||||
+ let interest = options & (WEXITED | WSTOPPED | WCONTINUED);
|
||||
+ if interest == 0 {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ let mut waitpid_options = 0;
|
||||
+ if options & WNOHANG != 0 {
|
||||
+ waitpid_options |= WNOHANG;
|
||||
+ }
|
||||
+ if options & WSTOPPED != 0 {
|
||||
+ waitpid_options |= WUNTRACED;
|
||||
+ }
|
||||
+ if options & WCONTINUED != 0 {
|
||||
+ waitpid_options |= WCONTINUED;
|
||||
+ }
|
||||
+ if options & WNOWAIT != 0 {
|
||||
+ waitpid_options |= WNOWAIT;
|
||||
+ }
|
||||
+
|
||||
+ Some(waitpid_options)
|
||||
+}
|
||||
+
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn waitid(
|
||||
+ idtype: idtype_t,
|
||||
+ id: id_t,
|
||||
+ infop: *mut siginfo_t,
|
||||
+ options: c_int,
|
||||
+) -> c_int {
|
||||
+ if infop.is_null() {
|
||||
+ ERRNO.set(crate::header::errno::EFAULT);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ let Some(pid_target) = map_waitid_target(idtype, id) else {
|
||||
+ ERRNO.set(crate::header::errno::EINVAL);
|
||||
+ return -1;
|
||||
+ };
|
||||
+ let Some(waitpid_options) = map_waitid_options(options) else {
|
||||
+ ERRNO.set(crate::header::errno::EINVAL);
|
||||
+ return -1;
|
||||
+ };
|
||||
+
|
||||
+ let mut status = 0;
|
||||
+ let pid = Sys::waitpid(pid_target, Some(Out::from_mut(&mut status)), waitpid_options)
|
||||
+ .or_minus_one_errno();
|
||||
+ if pid < 0 {
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ unsafe {
|
||||
+ *infop = core::mem::zeroed();
|
||||
+ }
|
||||
+ if pid == 0 {
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ unsafe {
|
||||
+ (*infop).si_pid = pid;
|
||||
+ (*infop).si_signo = crate::header::signal::SIGCHLD as c_int;
|
||||
+ (*infop).si_errno = 0;
|
||||
+ if wifexited(status) {
|
||||
+ (*infop).si_code = CLD_EXITED;
|
||||
+ (*infop).si_status = wexitstatus(status);
|
||||
+ } else if wifstopped(status) {
|
||||
+ (*infop).si_code = CLD_STOPPED;
|
||||
+ (*infop).si_status = wstopsig(status);
|
||||
+ } else if wifcontinued(status) {
|
||||
+ (*infop).si_code = CLD_CONTINUED;
|
||||
+ (*infop).si_status = crate::header::signal::SIGCONT as c_int;
|
||||
+ } else {
|
||||
+ (*infop).si_status = wtermsig(status);
|
||||
+ (*infop).si_code = if wcoredump(status) {
|
||||
+ CLD_DUMPED
|
||||
+ } else {
|
||||
+ CLD_KILLED
|
||||
+ };
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ 0
|
||||
+}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/waitpid.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
diff --git a/tests/Makefile.tests.mk b/tests/Makefile.tests.mk
|
||||
--- a/tests/Makefile.tests.mk
|
||||
+++ b/tests/Makefile.tests.mk
|
||||
@@ -312,6 +312,7 @@
|
||||
grp/getgrgid_r \
|
||||
grp/getgrnam_r \
|
||||
grp/gr_iter \
|
||||
+ waitid \
|
||||
waitpid \
|
||||
waitpid_multiple \
|
||||
$(FAILING_TESTS)
|
||||
diff --git a/tests/waitid.c b/tests/waitid.c
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/tests/waitid.c
|
||||
@@ -0,0 +1,50 @@
|
||||
+#include <assert.h>
|
||||
+#include <signal.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <sys/wait.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+#include "test_helpers.h"
|
||||
+
|
||||
+static void wait_until_child_exits(pid_t pid) {
|
||||
+ siginfo_t info;
|
||||
+ for (int i = 0; i < 50; ++i) {
|
||||
+ info.si_pid = 0;
|
||||
+ int ret = waitid(P_PID, pid, &info, WEXITED | WNOHANG | WNOWAIT);
|
||||
+ ERROR_IF(waitid, ret, == -1);
|
||||
+ if (info.si_pid == pid) {
|
||||
+ assert(info.si_code == CLD_EXITED);
|
||||
+ assert(info.si_status == 42);
|
||||
+ return;
|
||||
+ }
|
||||
+ usleep(10000);
|
||||
+ }
|
||||
+ assert(!"waitid never observed child exit");
|
||||
+}
|
||||
+
|
||||
+int main(void) {
|
||||
+ pid_t pid = fork();
|
||||
+ ERROR_IF(fork, pid, == -1);
|
||||
+
|
||||
+ if (pid == 0) {
|
||||
+ usleep(50000);
|
||||
+ _Exit(42);
|
||||
+ }
|
||||
+
|
||||
+ siginfo_t info;
|
||||
+ info.si_pid = 0;
|
||||
+ int ret = waitid(P_PID, pid, &info, WEXITED | WNOHANG | WNOWAIT);
|
||||
+ ERROR_IF(waitid, ret, == -1);
|
||||
+ assert(info.si_pid == 0);
|
||||
+
|
||||
+ wait_until_child_exits(pid);
|
||||
+
|
||||
+ int status = 0;
|
||||
+ pid_t waited = waitpid(pid, &status, 0);
|
||||
+ ERROR_IF(waitpid, waited, == -1);
|
||||
+ assert(waited == pid);
|
||||
+ assert(WIFEXITED(status));
|
||||
+ assert(WEXITSTATUS(status) == 42);
|
||||
+
|
||||
+ return EXIT_SUCCESS;
|
||||
+}
|
||||
-319
@@ -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,5 +18,7 @@ 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,5 +18,6 @@ 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<()> {
|
||||
-196
@@ -1,196 +0,0 @@
|
||||
diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs
|
||||
index 752339a..90413f2 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 {
|
||||
+ unsafe { 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<()> {
|
||||
-188
@@ -1,188 +0,0 @@
|
||||
diff --git a/src/lib.rs b/src/lib.rs
|
||||
--- a/src/lib.rs
|
||||
+++ b/src/lib.rs
|
||||
@@ -57,16 +57,151 @@ 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,28 @@ 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();
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs
|
||||
index f049573..f3d665c 100644
|
||||
--- a/src/header/signal/mod.rs
|
||||
+++ b/src/header/signal/mod.rs
|
||||
@@ -2,7 +2,10 @@
|
||||
//!
|
||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
|
||||
|
||||
-use core::{mem, ptr};
|
||||
+use core::{
|
||||
+ mem, ptr,
|
||||
+ sync::atomic::Ordering,
|
||||
+};
|
||||
|
||||
use cbitset::BitSet;
|
||||
|
||||
@@ -32,6 +35,9 @@ pub mod sys;
|
||||
#[path = "redox.rs"]
|
||||
pub mod sys;
|
||||
|
||||
+mod signalfd;
|
||||
+pub use self::signalfd::*;
|
||||
+
|
||||
type SigSet = BitSet<[u64; 1]>;
|
||||
|
||||
pub(crate) const SIG_DFL: usize = 0;
|
||||
@@ -154,10 +160,15 @@ pub extern "C" fn killpg(pgrp: pid_t, sig: c_int) -> c_int {
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_kill.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_kill(thread: pthread_t, sig: c_int) -> c_int {
|
||||
- let os_tid = {
|
||||
- let pthread = unsafe { &*(thread as *const crate::pthread::Pthread) };
|
||||
- unsafe { pthread.os_tid.get().read() }
|
||||
- };
|
||||
+ let pthread = unsafe { &*(thread as *const crate::pthread::Pthread) };
|
||||
+ let os_tid = unsafe { pthread.os_tid.get().read() };
|
||||
+ let flags = crate::pthread::PthreadFlags::from_bits_retain(
|
||||
+ pthread.flags.load(Ordering::Acquire),
|
||||
+ );
|
||||
+ if flags.contains(crate::pthread::PthreadFlags::FINISHED) {
|
||||
+ return errno::ESRCH;
|
||||
+ }
|
||||
+
|
||||
crate::header::pthread::e(unsafe { Sys::rlct_kill(os_tid, sig as usize) })
|
||||
}
|
||||
|
||||
@@ -168,12 +179,10 @@ pub unsafe extern "C" fn pthread_sigmask(
|
||||
set: *const sigset_t,
|
||||
oldset: *mut sigset_t,
|
||||
) -> c_int {
|
||||
- // On Linux and Redox, pthread_sigmask and sigprocmask are equivalent
|
||||
- if unsafe { sigprocmask(how, set, oldset) } == 0 {
|
||||
- 0
|
||||
- } else {
|
||||
- //TODO: Fix race
|
||||
- platform::ERRNO.get()
|
||||
+ let filtered_set = unsafe { set.as_ref().map(|&block| block & !RLCT_SIGNAL_MASK) };
|
||||
+ match unsafe { Sys::sigprocmask(how, filtered_set.as_ref(), oldset.as_mut()) } {
|
||||
+ Ok(()) => 0,
|
||||
+ Err(errno) => errno.0,
|
||||
}
|
||||
}
|
||||
|
||||
-64
@@ -1,64 +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,11 +136,7 @@ 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,11 @@ 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 {
|
||||
@@ -1,380 +0,0 @@
|
||||
diff --git a/src/sync/pthread_mutex.rs b/src/sync/pthread_mutex.rs
|
||||
index 29bad63..af0c429 100644
|
||||
--- a/src/sync/pthread_mutex.rs
|
||||
+++ b/src/sync/pthread_mutex.rs
|
||||
@@ -1,3 +1,4 @@
|
||||
+use alloc::boxed::Box;
|
||||
use core::{
|
||||
cell::Cell,
|
||||
sync::atomic::{AtomicU32 as AtomicUint, Ordering},
|
||||
@@ -6,10 +7,9 @@ use core::{
|
||||
use crate::{
|
||||
error::Errno,
|
||||
header::{bits_timespec::timespec, errno::*, pthread::*},
|
||||
+ platform::{Pal, Sys, types::c_int},
|
||||
};
|
||||
|
||||
-use crate::platform::{Pal, Sys, types::c_int};
|
||||
-
|
||||
use super::FutexWaitResult;
|
||||
|
||||
pub struct RlctMutex {
|
||||
@@ -21,15 +21,22 @@ pub struct RlctMutex {
|
||||
robust: bool,
|
||||
}
|
||||
|
||||
+pub struct RobustMutexNode {
|
||||
+ pub next: *mut RobustMutexNode,
|
||||
+ pub prev: *mut RobustMutexNode,
|
||||
+ pub mutex: *const RlctMutex,
|
||||
+}
|
||||
+
|
||||
const STATE_UNLOCKED: u32 = 0;
|
||||
const WAITING_BIT: u32 = 1 << 31;
|
||||
-const INDEX_MASK: u32 = !WAITING_BIT;
|
||||
+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;
|
||||
// TODO: How many spins should we do before it becomes more time-economical to enter kernel mode
|
||||
// via futexes?
|
||||
-const SPIN_COUNT: usize = 0;
|
||||
+const SPIN_COUNT: usize = 100;
|
||||
|
||||
impl RlctMutex {
|
||||
pub(crate) fn new(attr: &RlctMutexAttr) -> Result<Self, Errno> {
|
||||
@@ -69,13 +76,25 @@ impl RlctMutex {
|
||||
Ok(0)
|
||||
}
|
||||
pub fn make_consistent(&self) -> Result<(), Errno> {
|
||||
- todo_skip!(0, "pthread robust mutexes: not implemented");
|
||||
- Ok(())
|
||||
+ debug_assert!(self.robust, "make_consistent called on non-robust mutex");
|
||||
+
|
||||
+ if !self.robust {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ let current = self.inner.load(Ordering::Relaxed);
|
||||
+ let owner = current & INDEX_MASK;
|
||||
+
|
||||
+ if owner == os_tid_invalid_after_fork() && current & FUTEX_OWNER_DIED != 0 {
|
||||
+ self.inner.store(0, Ordering::Release);
|
||||
+ Ok(())
|
||||
+ } else {
|
||||
+ Err(Errno(EINVAL))
|
||||
+ }
|
||||
}
|
||||
fn lock_inner(&self, deadline: Option<×pec>) -> Result<(), Errno> {
|
||||
let this_thread = os_tid_invalid_after_fork();
|
||||
-
|
||||
- //let mut spins_left = SPIN_COUNT;
|
||||
+ let mut spins_left = SPIN_COUNT;
|
||||
|
||||
loop {
|
||||
let result = self.inner.compare_exchange_weak(
|
||||
@@ -86,45 +105,59 @@ impl RlctMutex {
|
||||
);
|
||||
|
||||
match result {
|
||||
- // CAS succeeded
|
||||
- Ok(_) => {
|
||||
- if self.ty == Ty::Recursive {
|
||||
- self.increment_recursive_count()?;
|
||||
- }
|
||||
- return Ok(());
|
||||
- }
|
||||
- // CAS failed, but the mutex was recursive and we already own the lock.
|
||||
+ Ok(_) => return self.finish_lock_acquire(false),
|
||||
Err(thread) if thread & INDEX_MASK == this_thread && self.ty == Ty::Recursive => {
|
||||
self.increment_recursive_count()?;
|
||||
return Ok(());
|
||||
}
|
||||
- // CAS failed, but the mutex was error-checking and we already own the lock.
|
||||
Err(thread) if thread & INDEX_MASK == this_thread && self.ty == Ty::Errck => {
|
||||
- return Err(Errno(EAGAIN));
|
||||
+ return Err(Errno(EDEADLK));
|
||||
}
|
||||
- // CAS spuriously failed, simply retry the CAS. TODO: Use core::hint::spin_loop()?
|
||||
- Err(thread) if thread & INDEX_MASK == 0 => {
|
||||
- continue;
|
||||
+ Err(thread) if thread & FUTEX_OWNER_DIED != 0 && thread & INDEX_MASK == 0 => {
|
||||
+ return Err(Errno(ENOTRECOVERABLE));
|
||||
}
|
||||
- // CAS failed because some other thread owned the lock. We must now wait.
|
||||
+ Err(thread) if thread & FUTEX_OWNER_DIED != 0 => {
|
||||
+ if !self.robust {
|
||||
+ return Err(Errno(ENOTRECOVERABLE));
|
||||
+ }
|
||||
+
|
||||
+ let new_value = (thread & WAITING_BIT) | FUTEX_OWNER_DIED | this_thread;
|
||||
+ match self.inner.compare_exchange(
|
||||
+ thread,
|
||||
+ new_value,
|
||||
+ Ordering::Acquire,
|
||||
+ Ordering::Relaxed,
|
||||
+ ) {
|
||||
+ Ok(_) => return self.finish_lock_acquire(true),
|
||||
+ Err(_) => continue,
|
||||
+ }
|
||||
+ }
|
||||
+ Err(thread) if thread & INDEX_MASK == 0 => continue,
|
||||
Err(thread) => {
|
||||
- /*if spins_left > 0 {
|
||||
- // TODO: Faster to spin trying to load the flag, compared to CAS?
|
||||
+ let owner = thread & INDEX_MASK;
|
||||
+
|
||||
+ if !crate::pthread::mutex_owner_id_is_live(owner) {
|
||||
+ if !self.robust {
|
||||
+ return Err(Errno(ENOTRECOVERABLE));
|
||||
+ }
|
||||
+
|
||||
+ let new_value = (thread & WAITING_BIT) | FUTEX_OWNER_DIED | this_thread;
|
||||
+ match self.inner.compare_exchange(
|
||||
+ thread,
|
||||
+ new_value,
|
||||
+ Ordering::Acquire,
|
||||
+ Ordering::Relaxed,
|
||||
+ ) {
|
||||
+ Ok(_) => return self.finish_lock_acquire(true),
|
||||
+ Err(_) => continue,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if spins_left > 0 {
|
||||
spins_left -= 1;
|
||||
core::hint::spin_loop();
|
||||
continue;
|
||||
}
|
||||
-
|
||||
- spins_left = SPIN_COUNT;
|
||||
-
|
||||
- let inner = self.inner.fetch_or(WAITING_BIT, Ordering::Relaxed);
|
||||
-
|
||||
- if inner == STATE_UNLOCKED {
|
||||
- continue;
|
||||
- }*/
|
||||
-
|
||||
- // If the mutex is not robust, simply futex_wait until unblocked.
|
||||
- //crate::sync::futex_wait(&self.inner, inner | WAITING_BIT, None);
|
||||
if crate::sync::futex_wait(&self.inner, thread, deadline)
|
||||
== FutexWaitResult::TimedOut
|
||||
{
|
||||
@@ -140,6 +173,20 @@ impl RlctMutex {
|
||||
pub fn lock_with_timeout(&self, deadline: ×pec) -> Result<(), Errno> {
|
||||
self.lock_inner(Some(deadline))
|
||||
}
|
||||
+ fn finish_lock_acquire(&self, owner_dead: bool) -> Result<(), Errno> {
|
||||
+ if self.ty == Ty::Recursive {
|
||||
+ self.increment_recursive_count()?;
|
||||
+ }
|
||||
+ if self.robust {
|
||||
+ add_to_robust_list(self);
|
||||
+ }
|
||||
+
|
||||
+ if owner_dead {
|
||||
+ Err(Errno(EOWNERDEAD))
|
||||
+ } else {
|
||||
+ Ok(())
|
||||
+ }
|
||||
+ }
|
||||
fn increment_recursive_count(&self) -> Result<(), Errno> {
|
||||
// We don't have to worry about asynchronous signals here, since pthread_mutex_trylock
|
||||
// is not async-signal-safe.
|
||||
@@ -161,41 +208,65 @@ impl RlctMutex {
|
||||
pub fn try_lock(&self) -> Result<(), Errno> {
|
||||
let this_thread = os_tid_invalid_after_fork();
|
||||
|
||||
- // TODO: If recursive, omitting CAS may be faster if it is already owned by this thread.
|
||||
- let result = self.inner.compare_exchange(
|
||||
- STATE_UNLOCKED,
|
||||
- this_thread,
|
||||
- Ordering::Acquire,
|
||||
- Ordering::Relaxed,
|
||||
- );
|
||||
+ loop {
|
||||
+ let current = self.inner.load(Ordering::Relaxed);
|
||||
+
|
||||
+ if current == STATE_UNLOCKED {
|
||||
+ match self.inner.compare_exchange(
|
||||
+ STATE_UNLOCKED,
|
||||
+ this_thread,
|
||||
+ Ordering::Acquire,
|
||||
+ Ordering::Relaxed,
|
||||
+ ) {
|
||||
+ Ok(_) => return self.finish_lock_acquire(false),
|
||||
+ Err(_) => continue,
|
||||
+ }
|
||||
+ }
|
||||
|
||||
- if self.ty == Ty::Recursive {
|
||||
- match result {
|
||||
- Err(index) if index & INDEX_MASK != this_thread => return Err(Errno(EBUSY)),
|
||||
- _ => (),
|
||||
+ let owner = current & INDEX_MASK;
|
||||
+
|
||||
+ if owner == this_thread && self.ty == Ty::Recursive {
|
||||
+ self.increment_recursive_count()?;
|
||||
+ return Ok(());
|
||||
}
|
||||
|
||||
- self.increment_recursive_count()?;
|
||||
+ if owner == this_thread && self.ty == Ty::Errck {
|
||||
+ return Err(Errno(EDEADLK));
|
||||
+ }
|
||||
|
||||
- return Ok(());
|
||||
- }
|
||||
+ if current & FUTEX_OWNER_DIED != 0 && owner == 0 {
|
||||
+ return Err(Errno(ENOTRECOVERABLE));
|
||||
+ }
|
||||
|
||||
- match result {
|
||||
- Ok(_) => Ok(()),
|
||||
- Err(index) if index & INDEX_MASK == this_thread && self.ty == Ty::Errck => {
|
||||
- Err(Errno(EDEADLK))
|
||||
+ if current & FUTEX_OWNER_DIED != 0 || (owner != 0 && !crate::pthread::mutex_owner_id_is_live(owner)) {
|
||||
+ if !self.robust {
|
||||
+ return Err(Errno(ENOTRECOVERABLE));
|
||||
+ }
|
||||
+
|
||||
+ let new_value = (current & WAITING_BIT) | FUTEX_OWNER_DIED | this_thread;
|
||||
+ match self.inner.compare_exchange(
|
||||
+ current,
|
||||
+ new_value,
|
||||
+ Ordering::Acquire,
|
||||
+ Ordering::Relaxed,
|
||||
+ ) {
|
||||
+ Ok(_) => return self.finish_lock_acquire(true),
|
||||
+ Err(_) => continue,
|
||||
+ }
|
||||
}
|
||||
- Err(_) => Err(Errno(EBUSY)),
|
||||
+
|
||||
+ return Err(Errno(EBUSY));
|
||||
}
|
||||
}
|
||||
// Safe because we are not protecting any data.
|
||||
pub fn unlock(&self) -> Result<(), Errno> {
|
||||
+ let current = self.inner.load(Ordering::Relaxed);
|
||||
+
|
||||
if self.robust || matches!(self.ty, Ty::Recursive | Ty::Errck) {
|
||||
- if self.inner.load(Ordering::Relaxed) & INDEX_MASK != os_tid_invalid_after_fork() {
|
||||
+ if current & INDEX_MASK != os_tid_invalid_after_fork() {
|
||||
return Err(Errno(EPERM));
|
||||
}
|
||||
|
||||
- // TODO: Is this fence correct?
|
||||
core::sync::atomic::fence(Ordering::Acquire);
|
||||
}
|
||||
|
||||
@@ -208,18 +279,47 @@ impl RlctMutex {
|
||||
}
|
||||
}
|
||||
|
||||
- self.inner.store(STATE_UNLOCKED, Ordering::Release);
|
||||
- crate::sync::futex_wake(&self.inner, i32::MAX);
|
||||
- /*let was_waiting = self.inner.swap(STATE_UNLOCKED, Ordering::Release) & WAITING_BIT != 0;
|
||||
+ if self.robust {
|
||||
+ remove_from_robust_list(self);
|
||||
+ }
|
||||
|
||||
- if was_waiting {
|
||||
- let _ = crate::sync::futex_wake(&self.inner, 1);
|
||||
- }*/
|
||||
+ let new_state = if self.robust && current & FUTEX_OWNER_DIED != 0 {
|
||||
+ FUTEX_OWNER_DIED
|
||||
+ } else {
|
||||
+ STATE_UNLOCKED
|
||||
+ };
|
||||
+
|
||||
+ self.inner.store(new_state, Ordering::Release);
|
||||
+ crate::sync::futex_wake(&self.inner, i32::MAX);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+pub(crate) unsafe fn mark_robust_mutexes_dead(thread: &crate::pthread::Pthread) {
|
||||
+ let head = thread.robust_list_head.get();
|
||||
+ let this_thread = os_tid_invalid_after_fork();
|
||||
+ let mut node = unsafe { *head };
|
||||
+
|
||||
+ unsafe { *head = core::ptr::null_mut() };
|
||||
+
|
||||
+ while !node.is_null() {
|
||||
+ let next = unsafe { (*node).next };
|
||||
+ let mutex = unsafe { &*(*node).mutex };
|
||||
+ let current = mutex.inner.load(Ordering::Relaxed);
|
||||
+
|
||||
+ if current & INDEX_MASK == this_thread {
|
||||
+ mutex
|
||||
+ .inner
|
||||
+ .store((current & WAITING_BIT) | FUTEX_OWNER_DIED | this_thread, Ordering::Release);
|
||||
+ crate::sync::futex_wake(&mutex.inner, i32::MAX);
|
||||
+ }
|
||||
+
|
||||
+ unsafe { drop(Box::from_raw(node)) };
|
||||
+ node = next;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
#[repr(u8)]
|
||||
#[derive(PartialEq)]
|
||||
enum Ty {
|
||||
@@ -237,6 +337,54 @@ enum Ty {
|
||||
#[thread_local]
|
||||
static CACHED_OS_TID_INVALID_AFTER_FORK: Cell<u32> = Cell::new(0);
|
||||
|
||||
+fn add_to_robust_list(mutex: &RlctMutex) {
|
||||
+ let thread = crate::pthread::current_thread().expect("current thread not present");
|
||||
+ let node_ptr = Box::into_raw(Box::new(RobustMutexNode {
|
||||
+ next: core::ptr::null_mut(),
|
||||
+ prev: core::ptr::null_mut(),
|
||||
+ mutex: core::ptr::from_ref(mutex),
|
||||
+ }));
|
||||
+
|
||||
+ unsafe {
|
||||
+ let head = thread.robust_list_head.get();
|
||||
+ if !(*head).is_null() {
|
||||
+ (**head).prev = node_ptr;
|
||||
+ }
|
||||
+ (*node_ptr).next = *head;
|
||||
+ *head = node_ptr;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn remove_from_robust_list(mutex: &RlctMutex) {
|
||||
+ let thread = match crate::pthread::current_thread() {
|
||||
+ Some(thread) => thread,
|
||||
+ None => return,
|
||||
+ };
|
||||
+
|
||||
+ unsafe {
|
||||
+ let mut node = *thread.robust_list_head.get();
|
||||
+
|
||||
+ while !node.is_null() {
|
||||
+ if core::ptr::eq((*node).mutex, core::ptr::from_ref(mutex)) {
|
||||
+ if !(*node).prev.is_null() {
|
||||
+ (*(*node).prev).next = (*node).next;
|
||||
+ } else {
|
||||
+ *thread.robust_list_head.get() = (*node).next;
|
||||
+ }
|
||||
+
|
||||
+ if !(*node).next.is_null() {
|
||||
+ (*(*node).next).prev = (*node).prev;
|
||||
+ }
|
||||
+
|
||||
+ drop(Box::from_raw(node));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ node = (*node).next;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
// Assumes TIDs are unique between processes, which I only know is true for Redox.
|
||||
fn os_tid_invalid_after_fork() -> u32 {
|
||||
// TODO: Coordinate better if using shared == PTHREAD_PROCESS_SHARED, with up to 2^32 separate
|
||||
@@ -1,130 +0,0 @@
|
||||
diff --git a/src/header/sched/mod.rs b/src/header/sched/mod.rs
|
||||
index bcdd346..6066550 100644
|
||||
--- a/src/header/sched/mod.rs
|
||||
+++ b/src/header/sched/mod.rs
|
||||
@@ -27,43 +27,110 @@ pub const SCHED_RR: c_int = 1;
|
||||
pub const SCHED_OTHER: c_int = 2;
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_get_priority_max.html>.
|
||||
-// #[unsafe(no_mangle)]
|
||||
+#[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,
|
||||
+ _ => {
|
||||
+ crate::platform::ERRNO.set(crate::header::errno::EINVAL);
|
||||
+ -1
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
-/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_get_priority_max.html>.
|
||||
-// #[unsafe(no_mangle)]
|
||||
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_get_priority_min.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
pub extern "C" fn sched_get_priority_min(policy: c_int) -> c_int {
|
||||
- todo!()
|
||||
+ match policy {
|
||||
+ SCHED_FIFO | SCHED_RR => 1,
|
||||
+ SCHED_OTHER => 0,
|
||||
+ _ => {
|
||||
+ crate::platform::ERRNO.set(crate::header::errno::EINVAL);
|
||||
+ -1
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_getparam.html>.
|
||||
-// #[unsafe(no_mangle)]
|
||||
+#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sched_getparam(pid: pid_t, param: *mut sched_param) -> c_int {
|
||||
- todo!()
|
||||
+ if pid != 0 {
|
||||
+ crate::platform::ERRNO.set(crate::header::errno::ESRCH);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ crate::platform::ERRNO.set(crate::header::errno::ENOSYS);
|
||||
+ -1
|
||||
+}
|
||||
+
|
||||
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_getscheduler.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub extern "C" fn sched_getscheduler(pid: pid_t) -> c_int {
|
||||
+ if pid != 0 {
|
||||
+ crate::platform::ERRNO.set(crate::header::errno::ESRCH);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ crate::platform::ERRNO.set(crate::header::errno::ENOSYS);
|
||||
+ -1
|
||||
}
|
||||
|
||||
/// 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!()
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub extern "C" fn sched_rr_get_interval(pid: pid_t, tp: *mut timespec) -> c_int {
|
||||
+ if pid != 0 {
|
||||
+ crate::platform::ERRNO.set(crate::header::errno::ESRCH);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ if tp.is_null() {
|
||||
+ crate::platform::ERRNO.set(crate::header::errno::EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ unsafe {
|
||||
+ (*tp).tv_sec = 0;
|
||||
+ (*tp).tv_nsec = 100_000_000; // 100ms default SCHED_RR quantum
|
||||
+ }
|
||||
+ 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!()
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn sched_setparam(pid: pid_t, _param: *const sched_param) -> c_int {
|
||||
+ if pid != 0 {
|
||||
+ crate::platform::ERRNO.set(crate::header::errno::ESRCH);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ crate::platform::ERRNO.set(crate::header::errno::ENOSYS);
|
||||
+ -1
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_setscheduler.html>.
|
||||
-// #[unsafe(no_mangle)]
|
||||
+#[unsafe(no_mangle)]
|
||||
pub extern "C" fn sched_setscheduler(
|
||||
pid: pid_t,
|
||||
policy: c_int,
|
||||
param: *const sched_param,
|
||||
) -> c_int {
|
||||
- todo!()
|
||||
+ if pid != 0 {
|
||||
+ crate::platform::ERRNO.set(crate::header::errno::ESRCH);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ match policy {
|
||||
+ SCHED_OTHER => {
|
||||
+ if !param.is_null() && unsafe { (*param).sched_priority } != 0 {
|
||||
+ crate::platform::ERRNO.set(crate::header::errno::EINVAL);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ SCHED_OTHER
|
||||
+ }
|
||||
+ SCHED_FIFO | SCHED_RR => {
|
||||
+ crate::platform::ERRNO.set(crate::header::errno::ENOSYS);
|
||||
+ -1
|
||||
+ }
|
||||
+ _ => {
|
||||
+ crate::platform::ERRNO.set(crate::header::errno::EINVAL);
|
||||
+ -1
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_yield.html>.
|
||||
-112
@@ -1,112 +0,0 @@
|
||||
diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs
|
||||
index 022f873..ab96dea 100644
|
||||
--- a/redox-rt/src/signal.rs
|
||||
+++ b/redox-rt/src/signal.rs
|
||||
@@ -1,4 +1,10 @@
|
||||
-use core::{ffi::c_int, ptr::NonNull, sync::atomic::Ordering};
|
||||
+use core::{
|
||||
+ ffi::c_int,
|
||||
+ hint::unreachable_unchecked,
|
||||
+ panic::AssertUnwindSafe,
|
||||
+ ptr::NonNull,
|
||||
+ sync::atomic::Ordering,
|
||||
+};
|
||||
|
||||
use syscall::{
|
||||
CallFlags, EAGAIN, EINTR, EINVAL, ENOMEM, EPERM, Error, RawAction, Result, SenderInfo,
|
||||
@@ -103,6 +109,47 @@ pub struct SiginfoAbi {
|
||||
pub si_value: usize, // sigval
|
||||
}
|
||||
|
||||
+fn invoke_signal_handler<F: FnOnce()>(f: AssertUnwindSafe<F>) -> bool {
|
||||
+ 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);
|
||||
+ unsafe {
|
||||
+ core::intrinsics::catch_unwind(
|
||||
+ do_call::<F>,
|
||||
+ (&mut callback as *mut Option<AssertUnwindSafe<F>>).cast(),
|
||||
+ do_catch::<F>,
|
||||
+ ) != 0
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[inline(always)]
|
||||
+unsafe fn return_ignored_signal(
|
||||
+ os: &RtTcb,
|
||||
+ stack: &SigStack,
|
||||
+ signals_were_disabled: bool,
|
||||
+) {
|
||||
+ unsafe {
|
||||
+ (*os.arch.get()).last_sig_was_restart = true;
|
||||
+ (*os.arch.get()).last_sigstack = NonNull::new(stack.link);
|
||||
+ }
|
||||
+
|
||||
+ if !signals_were_disabled {
|
||||
+ core::sync::atomic::compiler_fence(Ordering::Release);
|
||||
+ let control_flags = &os.control.control_flags;
|
||||
+ control_flags.store(
|
||||
+ control_flags.load(Ordering::Relaxed) & !SigcontrolFlags::INHIBIT_DELIVERY.bits(),
|
||||
+ Ordering::Relaxed,
|
||||
+ );
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
#[inline(always)]
|
||||
unsafe fn inner(stack: &mut SigStack) {
|
||||
let os = unsafe { &Tcb::current().unwrap().os_specific };
|
||||
@@ -168,7 +215,10 @@ unsafe fn inner(stack: &mut SigStack) {
|
||||
// and reaching this code. If so, we do already know whether the signal is IGNORED *now*,
|
||||
// and so we should return early ideally without even temporarily touching the signal mask.
|
||||
SigactionKind::Ignore => {
|
||||
- panic!("ctl {:#x?} signal {}", os.control, stack.sig_num)
|
||||
+ unsafe {
|
||||
+ return_ignored_signal(os, stack, signals_were_disabled);
|
||||
+ }
|
||||
+ return;
|
||||
}
|
||||
// this case should be treated equally as the one above
|
||||
//
|
||||
@@ -183,7 +233,9 @@ unsafe fn inner(stack: &mut SigStack) {
|
||||
CallFlags::empty(),
|
||||
&[ProcCall::Exit as u64, u64::from(sig) << 8],
|
||||
);
|
||||
- panic!()
|
||||
+ // SAFETY: ProcCall::Exit terminates the current process when it succeeds, so reaching
|
||||
+ // this point would violate the proc manager exit contract.
|
||||
+ unsafe { unreachable_unchecked() }
|
||||
}
|
||||
SigactionKind::Handled { handler } => handler,
|
||||
};
|
||||
@@ -224,15 +276,21 @@ unsafe fn inner(stack: &mut SigStack) {
|
||||
si_uid: sender_uid as i32,
|
||||
si_value: stack.sival,
|
||||
};
|
||||
- unsafe {
|
||||
+ if invoke_signal_handler(AssertUnwindSafe(|| unsafe {
|
||||
sigaction(
|
||||
stack.sig_num as c_int,
|
||||
core::ptr::addr_of!(info).cast(),
|
||||
stack as *mut SigStack as *mut (),
|
||||
)
|
||||
- };
|
||||
+ })) {
|
||||
+ let _ = syscall::write(2, b"redox-rt: sa_siginfo handler panicked; continuing\n");
|
||||
+ }
|
||||
} else if let Some(handler) = unsafe { handler.handler } {
|
||||
- handler(stack.sig_num as c_int);
|
||||
+ if invoke_signal_handler(AssertUnwindSafe(|| {
|
||||
+ handler(stack.sig_num as c_int);
|
||||
+ })) {
|
||||
+ let _ = syscall::write(2, b"redox-rt: sa_handler panicked; continuing\n");
|
||||
+ }
|
||||
}
|
||||
|
||||
// Disable signals while we modify the sigmask again
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
diff --git a/src/start.rs b/src/start.rs
|
||||
--- a/src/start.rs
|
||||
+++ b/src/start.rs
|
||||
@@ -1,10 +1,7 @@
|
||||
//! 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,29 @@ 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,14 +184,23 @@ 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,231 +0,0 @@
|
||||
diff --git a/src/header/pthread/cbindgen.toml b/src/header/pthread/cbindgen.toml
|
||||
--- a/src/header/pthread/cbindgen.toml
|
||||
+++ b/src/header/pthread/cbindgen.toml
|
||||
@@ -10,0 +11 @@ cpp_compat = true
|
||||
+"cpu_set_t" = "struct cpu_set_t"
|
||||
diff --git a/src/header/pthread/mod.rs b/src/header/pthread/mod.rs
|
||||
--- a/src/header/pthread/mod.rs
|
||||
+++ b/src/header/pthread/mod.rs
|
||||
@@ -6 +6,8 @@ use alloc::collections::LinkedList;
|
||||
-use core::{cell::Cell, ptr::NonNull};
|
||||
+use core::{cell::Cell, mem::size_of, ptr::NonNull};
|
||||
+
|
||||
+#[cfg(target_os = "linux")]
|
||||
+use sc::syscall;
|
||||
+#[cfg(target_os = "redox")]
|
||||
+use redox_rt::proc::FdGuard;
|
||||
+#[cfg(target_os = "redox")]
|
||||
+use syscall;
|
||||
@@ -9,0 +17 @@ use crate::{
|
||||
+ header::errno::EINVAL,
|
||||
@@ -14 +22 @@ use crate::{
|
||||
- c_int, c_uchar, c_uint, c_void, clockid_t, pthread_attr_t, pthread_barrier_t,
|
||||
+ c_char, c_int, c_uchar, c_uint, c_void, clockid_t, pthread_attr_t, pthread_barrier_t,
|
||||
@@ -22,0 +31,3 @@ use crate::{
|
||||
+#[cfg(target_os = "linux")]
|
||||
+use crate::platform::sys::e_raw;
|
||||
+
|
||||
@@ -29,0 +41,93 @@ pub fn e(result: Result<(), Errno>) -> i32 {
|
||||
+const RLCT_AFFINITY_BYTES: usize = size_of::<u64>();
|
||||
+const RLCT_MAX_AFFINITY_CPUS: usize = u64::BITS as usize;
|
||||
+
|
||||
+fn cpuset_bytes<'a>(cpusetsize: size_t, cpuset: *const cpu_set_t) -> Result<&'a [u8], Errno> {
|
||||
+ if cpuset.is_null() || !(RLCT_AFFINITY_BYTES..=size_of::<cpu_set_t>()).contains(&cpusetsize) {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ Ok(unsafe { core::slice::from_raw_parts(cpuset.cast::<u8>(), cpusetsize) })
|
||||
+}
|
||||
+
|
||||
+fn cpuset_bytes_mut<'a>(
|
||||
+ cpusetsize: size_t,
|
||||
+ cpuset: *mut cpu_set_t,
|
||||
+) -> Result<&'a mut [u8], Errno> {
|
||||
+ if cpuset.is_null() || !(RLCT_AFFINITY_BYTES..=size_of::<cpu_set_t>()).contains(&cpusetsize) {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ Ok(unsafe { core::slice::from_raw_parts_mut(cpuset.cast::<u8>(), cpusetsize) })
|
||||
+}
|
||||
+
|
||||
+fn cpuset_to_u64(cpusetsize: size_t, cpuset: *const cpu_set_t) -> Result<u64, Errno> {
|
||||
+ let bytes = cpuset_bytes(cpusetsize, cpuset)?;
|
||||
+ let mut mask = 0_u64;
|
||||
+
|
||||
+ for (byte_index, byte) in bytes.iter().copied().enumerate() {
|
||||
+ for bit in 0..u8::BITS as usize {
|
||||
+ if byte & (1 << bit) == 0 {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let cpu = byte_index * u8::BITS as usize + bit;
|
||||
+ if cpu >= RLCT_MAX_AFFINITY_CPUS {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ mask |= 1_u64 << cpu;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ Ok(mask)
|
||||
+}
|
||||
+
|
||||
+fn copy_u64_to_cpuset(mask: u64, cpusetsize: size_t, cpuset: *mut cpu_set_t) -> Result<(), Errno> {
|
||||
+ let bytes = cpuset_bytes_mut(cpusetsize, cpuset)?;
|
||||
+ bytes.fill(0);
|
||||
+
|
||||
+ for (byte_index, dst) in bytes.iter_mut().take(RLCT_AFFINITY_BYTES).enumerate() {
|
||||
+ *dst = (mask >> (byte_index * u8::BITS as usize)) as u8;
|
||||
+ }
|
||||
+
|
||||
+ Ok(())
|
||||
+}
|
||||
+
|
||||
+#[cfg(target_os = "redox")]
|
||||
+fn redox_set_thread_affinity(thread: &pthread::Pthread, mask: u64) -> Result<(), Errno> {
|
||||
+ let mut kernel_cpuset = cpu_set_t::default();
|
||||
+ kernel_cpuset.__bits[0] = mask;
|
||||
+
|
||||
+ let handle = FdGuard::new(unsafe {
|
||||
+ syscall::dup(thread.os_tid.get().read().thread_fd, b"sched-affinity")?
|
||||
+ });
|
||||
+ let _ = handle.write(unsafe {
|
||||
+ core::slice::from_raw_parts(
|
||||
+ core::ptr::from_ref(&kernel_cpuset).cast::<u8>(),
|
||||
+ size_of::<cpu_set_t>(),
|
||||
+ )
|
||||
+ })?;
|
||||
+
|
||||
+ Ok(())
|
||||
+}
|
||||
+
|
||||
+#[cfg(target_os = "redox")]
|
||||
+fn redox_get_thread_affinity(thread: &pthread::Pthread) -> Result<u64, Errno> {
|
||||
+ let handle = FdGuard::new(unsafe {
|
||||
+ syscall::dup(thread.os_tid.get().read().thread_fd, b"sched-affinity")?
|
||||
+ });
|
||||
+ let mut kernel_cpuset = cpu_set_t::default();
|
||||
+ let _ = handle.read(unsafe {
|
||||
+ core::slice::from_raw_parts_mut(
|
||||
+ core::ptr::from_mut(&mut kernel_cpuset).cast::<u8>(),
|
||||
+ size_of::<cpu_set_t>(),
|
||||
+ )
|
||||
+ })?;
|
||||
+
|
||||
+ if kernel_cpuset.__bits[1..].iter().any(|bits| *bits != 0) {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ Ok(kernel_cpuset.__bits[0])
|
||||
+}
|
||||
+
|
||||
@@ -188,0 +293,36 @@ pub unsafe extern "C" fn pthread_getcpuclockid(
|
||||
+/// GNU extension. See <https://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn pthread_getaffinity_np(
|
||||
+ thread: pthread_t,
|
||||
+ cpusetsize: size_t,
|
||||
+ cpuset: *mut cpu_set_t,
|
||||
+) -> c_int {
|
||||
+ let thread: &pthread::Pthread = unsafe { &*thread.cast() };
|
||||
+
|
||||
+ let result = {
|
||||
+ #[cfg(target_os = "redox")]
|
||||
+ {
|
||||
+ redox_get_thread_affinity(thread).and_then(|mask| copy_u64_to_cpuset(mask, cpusetsize, cpuset))
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(target_os = "linux")]
|
||||
+ {
|
||||
+ if cpuset.is_null() {
|
||||
+ Err(Errno(EINVAL))
|
||||
+ } else {
|
||||
+ e_raw(unsafe {
|
||||
+ syscall!(
|
||||
+ SCHED_GETAFFINITY,
|
||||
+ thread.os_tid.get().read().thread_id,
|
||||
+ cpusetsize,
|
||||
+ cpuset.cast::<c_void>()
|
||||
+ )
|
||||
+ })
|
||||
+ .map(|_| ())
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ e(result)
|
||||
+}
|
||||
+
|
||||
@@ -237,0 +378,36 @@ pub unsafe extern "C" fn pthread_self() -> pthread_t {
|
||||
+/// GNU extension. See <https://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn pthread_setaffinity_np(
|
||||
+ thread: pthread_t,
|
||||
+ cpusetsize: size_t,
|
||||
+ cpuset: *const cpu_set_t,
|
||||
+) -> c_int {
|
||||
+ let thread: &pthread::Pthread = unsafe { &*thread.cast() };
|
||||
+
|
||||
+ let result = {
|
||||
+ #[cfg(target_os = "redox")]
|
||||
+ {
|
||||
+ cpuset_to_u64(cpusetsize, cpuset).and_then(|mask| redox_set_thread_affinity(thread, mask))
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(target_os = "linux")]
|
||||
+ {
|
||||
+ if cpuset.is_null() {
|
||||
+ Err(Errno(EINVAL))
|
||||
+ } else {
|
||||
+ e_raw(unsafe {
|
||||
+ syscall!(
|
||||
+ SCHED_SETAFFINITY,
|
||||
+ thread.os_tid.get().read().thread_id,
|
||||
+ cpusetsize,
|
||||
+ cpuset.cast::<c_void>()
|
||||
+ )
|
||||
+ })
|
||||
+ .map(|_| ())
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ e(result)
|
||||
+}
|
||||
+
|
||||
diff --git a/src/header/sched/cbindgen.toml b/src/header/sched/cbindgen.toml
|
||||
--- a/src/header/sched/cbindgen.toml
|
||||
+++ b/src/header/sched/cbindgen.toml
|
||||
@@ -22,0 +23,14 @@ prefix_with_name = true
|
||||
+
|
||||
+[export]
|
||||
+include = [
|
||||
+ "sched_param",
|
||||
+ "cpu_set_t",
|
||||
+ "sched_get_priority_max",
|
||||
+ "sched_get_priority_min",
|
||||
+ "sched_getparam",
|
||||
+ "sched_getscheduler",
|
||||
+ "sched_rr_get_interval",
|
||||
+ "sched_setparam",
|
||||
+ "sched_setscheduler",
|
||||
+ "sched_yield",
|
||||
+]
|
||||
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
|
||||
@@ -12,0 +13,2 @@
|
||||
+pub const CPU_SETSIZE: usize = 1024;
|
||||
+
|
||||
@@ -20,0 +23,7 @@
|
||||
+/// Linux-compatible CPU affinity mask storage.
|
||||
+#[repr(C)]
|
||||
+#[derive(Clone, Copy, Debug, Default)]
|
||||
+pub struct cpu_set_t {
|
||||
+ pub __bits: [u64; 16],
|
||||
+}
|
||||
+
|
||||
@@ -143,0 +153,3 @@
|
||||
+
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn cbindgen_stupid_struct_user_for_cpu_set_t(_: cpu_set_t) {}
|
||||
@@ -1,326 +0,0 @@
|
||||
diff --git a/src/header/pthread/mod.rs b/src/header/pthread/mod.rs
|
||||
index c742a42..008090a 100644
|
||||
--- a/src/header/pthread/mod.rs
|
||||
+++ b/src/header/pthread/mod.rs
|
||||
@@ -3,15 +3,26 @@
|
||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/pthread.h.html>.
|
||||
|
||||
use alloc::collections::LinkedList;
|
||||
-use core::{cell::Cell, ptr::NonNull};
|
||||
+use core::{cell::Cell, mem::size_of, ptr::NonNull};
|
||||
+
|
||||
+#[cfg(target_os = "redox")]
|
||||
+use redox_rt::proc::FdGuard;
|
||||
+#[cfg(target_os = "linux")]
|
||||
+use sc::syscall;
|
||||
+#[cfg(target_os = "redox")]
|
||||
+use syscall;
|
||||
|
||||
use crate::{
|
||||
error::Errno,
|
||||
- header::{bits_timespec::timespec, sched::*},
|
||||
+ header::{
|
||||
+ bits_timespec::timespec,
|
||||
+ errno::{EINVAL, ERANGE},
|
||||
+ sched::*,
|
||||
+ },
|
||||
platform::{
|
||||
Pal, Sys,
|
||||
types::{
|
||||
- c_int, c_uchar, c_uint, c_void, clockid_t, pthread_attr_t, pthread_barrier_t,
|
||||
+ c_char, c_int, c_uchar, c_uint, c_void, clockid_t, pthread_attr_t, pthread_barrier_t,
|
||||
pthread_barrierattr_t, pthread_cond_t, pthread_condattr_t, pthread_key_t,
|
||||
pthread_mutex_t, pthread_mutexattr_t, pthread_once_t, pthread_rwlock_t,
|
||||
pthread_rwlockattr_t, pthread_spinlock_t, pthread_t, size_t,
|
||||
@@ -20,6 +31,9 @@ use crate::{
|
||||
pthread,
|
||||
};
|
||||
|
||||
+#[cfg(target_os = "linux")]
|
||||
+use crate::platform::sys::e_raw;
|
||||
+
|
||||
pub fn e(result: Result<(), Errno>) -> i32 {
|
||||
match result {
|
||||
Ok(()) => 0,
|
||||
@@ -27,6 +41,96 @@ pub fn e(result: Result<(), Errno>) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
+const RLCT_AFFINITY_BYTES: usize = size_of::<u64>();
|
||||
+const RLCT_MAX_AFFINITY_CPUS: usize = u64::BITS as usize;
|
||||
+
|
||||
+fn cpuset_bytes<'a>(cpusetsize: size_t, cpuset: *const cpu_set_t) -> Result<&'a [u8], Errno> {
|
||||
+ if cpuset.is_null() || !(RLCT_AFFINITY_BYTES..=size_of::<cpu_set_t>()).contains(&cpusetsize) {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ Ok(unsafe { core::slice::from_raw_parts(cpuset.cast::<u8>(), cpusetsize) })
|
||||
+}
|
||||
+
|
||||
+fn cpuset_bytes_mut<'a>(cpusetsize: size_t, cpuset: *mut cpu_set_t) -> Result<&'a mut [u8], Errno> {
|
||||
+ if cpuset.is_null() || !(RLCT_AFFINITY_BYTES..=size_of::<cpu_set_t>()).contains(&cpusetsize) {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ Ok(unsafe { core::slice::from_raw_parts_mut(cpuset.cast::<u8>(), cpusetsize) })
|
||||
+}
|
||||
+
|
||||
+fn cpuset_to_u64(cpusetsize: size_t, cpuset: *const cpu_set_t) -> Result<u64, Errno> {
|
||||
+ let bytes = cpuset_bytes(cpusetsize, cpuset)?;
|
||||
+ let mut mask = 0_u64;
|
||||
+
|
||||
+ for (byte_index, byte) in bytes.iter().copied().enumerate() {
|
||||
+ for bit in 0..u8::BITS as usize {
|
||||
+ if byte & (1 << bit) == 0 {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let cpu = byte_index * u8::BITS as usize + bit;
|
||||
+ if cpu >= RLCT_MAX_AFFINITY_CPUS {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ mask |= 1_u64 << cpu;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ Ok(mask)
|
||||
+}
|
||||
+
|
||||
+fn copy_u64_to_cpuset(mask: u64, cpusetsize: size_t, cpuset: *mut cpu_set_t) -> Result<(), Errno> {
|
||||
+ let bytes = cpuset_bytes_mut(cpusetsize, cpuset)?;
|
||||
+ bytes.fill(0);
|
||||
+
|
||||
+ for (byte_index, dst) in bytes.iter_mut().take(RLCT_AFFINITY_BYTES).enumerate() {
|
||||
+ *dst = (mask >> (byte_index * u8::BITS as usize)) as u8;
|
||||
+ }
|
||||
+
|
||||
+ Ok(())
|
||||
+}
|
||||
+
|
||||
+#[cfg(target_os = "redox")]
|
||||
+fn redox_set_thread_affinity(thread: &pthread::Pthread, mask: u64) -> Result<(), Errno> {
|
||||
+ let mut kernel_cpuset = cpu_set_t::default();
|
||||
+ kernel_cpuset.__bits[0] = mask;
|
||||
+
|
||||
+ let handle = FdGuard::new(unsafe {
|
||||
+ syscall::dup(thread.os_tid.get().read().thread_fd, b"sched-affinity")?
|
||||
+ });
|
||||
+ let _ = handle.write(unsafe {
|
||||
+ core::slice::from_raw_parts(
|
||||
+ core::ptr::from_ref(&kernel_cpuset).cast::<u8>(),
|
||||
+ size_of::<cpu_set_t>(),
|
||||
+ )
|
||||
+ })?;
|
||||
+
|
||||
+ Ok(())
|
||||
+}
|
||||
+
|
||||
+#[cfg(target_os = "redox")]
|
||||
+fn redox_get_thread_affinity(thread: &pthread::Pthread) -> Result<u64, Errno> {
|
||||
+ let handle = FdGuard::new(unsafe {
|
||||
+ syscall::dup(thread.os_tid.get().read().thread_fd, b"sched-affinity")?
|
||||
+ });
|
||||
+ let mut kernel_cpuset = cpu_set_t::default();
|
||||
+ let _ = handle.read(unsafe {
|
||||
+ core::slice::from_raw_parts_mut(
|
||||
+ core::ptr::from_mut(&mut kernel_cpuset).cast::<u8>(),
|
||||
+ size_of::<cpu_set_t>(),
|
||||
+ )
|
||||
+ })?;
|
||||
+
|
||||
+ if kernel_cpuset.__bits[1..].iter().any(|bits| *bits != 0) {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ Ok(kernel_cpuset.__bits[0])
|
||||
+}
|
||||
+
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct RlctAttr {
|
||||
pub detachstate: c_uchar,
|
||||
@@ -186,6 +290,43 @@ pub unsafe extern "C" fn pthread_getcpuclockid(
|
||||
}
|
||||
}
|
||||
|
||||
+/// GNU extension. See <https://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn pthread_getaffinity_np(
|
||||
+ thread: pthread_t,
|
||||
+ cpusetsize: size_t,
|
||||
+ cpuset: *mut cpu_set_t,
|
||||
+) -> c_int {
|
||||
+ let thread: &pthread::Pthread = unsafe { &*thread.cast() };
|
||||
+
|
||||
+ let result = {
|
||||
+ #[cfg(target_os = "redox")]
|
||||
+ {
|
||||
+ redox_get_thread_affinity(thread)
|
||||
+ .and_then(|mask| copy_u64_to_cpuset(mask, cpusetsize, cpuset))
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(target_os = "linux")]
|
||||
+ {
|
||||
+ if cpuset.is_null() {
|
||||
+ Err(Errno(EINVAL))
|
||||
+ } else {
|
||||
+ e_raw(unsafe {
|
||||
+ syscall!(
|
||||
+ SCHED_GETAFFINITY,
|
||||
+ thread.os_tid.get().read().thread_id,
|
||||
+ cpusetsize,
|
||||
+ cpuset.cast::<c_void>()
|
||||
+ )
|
||||
+ })
|
||||
+ .map(|_| ())
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ e(result)
|
||||
+}
|
||||
+
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_getschedparam.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_getschedparam(
|
||||
@@ -235,6 +376,43 @@ pub unsafe extern "C" fn pthread_self() -> pthread_t {
|
||||
core::ptr::from_ref(unsafe { pthread::current_thread().unwrap_unchecked() }) as *mut _
|
||||
}
|
||||
|
||||
+/// GNU extension. See <https://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn pthread_setaffinity_np(
|
||||
+ thread: pthread_t,
|
||||
+ cpusetsize: size_t,
|
||||
+ cpuset: *const cpu_set_t,
|
||||
+) -> c_int {
|
||||
+ let thread: &pthread::Pthread = unsafe { &*thread.cast() };
|
||||
+
|
||||
+ let result = {
|
||||
+ #[cfg(target_os = "redox")]
|
||||
+ {
|
||||
+ cpuset_to_u64(cpusetsize, cpuset)
|
||||
+ .and_then(|mask| redox_set_thread_affinity(thread, mask))
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(target_os = "linux")]
|
||||
+ {
|
||||
+ if cpuset.is_null() {
|
||||
+ Err(Errno(EINVAL))
|
||||
+ } else {
|
||||
+ e_raw(unsafe {
|
||||
+ syscall!(
|
||||
+ SCHED_SETAFFINITY,
|
||||
+ thread.os_tid.get().read().thread_id,
|
||||
+ cpusetsize,
|
||||
+ cpuset.cast::<c_void>()
|
||||
+ )
|
||||
+ })
|
||||
+ .map(|_| ())
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ e(result)
|
||||
+}
|
||||
+
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_setcancelstate.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_setcancelstate(state: c_int, oldstate: *mut c_int) -> c_int {
|
||||
@@ -307,6 +485,13 @@ pub unsafe extern "C" fn pthread_testcancel() {
|
||||
unsafe { pthread::testcancel() };
|
||||
}
|
||||
|
||||
+/// <https://man7.org/linux/man-pages/man3/pthread_yield.3.html>
|
||||
+///
|
||||
+/// Non-standard GNU extension. Prefer `sched_yield()` instead.
|
||||
+pub extern "C" fn pthread_yield() {
|
||||
+ let _ = Sys::sched_yield();
|
||||
+}
|
||||
+
|
||||
// Must be the same struct as defined in the pthread_cleanup_push macro.
|
||||
#[repr(C)]
|
||||
pub(crate) struct CleanupLinkedListEntry {
|
||||
@@ -350,3 +535,82 @@ pub(crate) unsafe fn run_destructor_stack() {
|
||||
(entry.routine)(entry.arg);
|
||||
}
|
||||
}
|
||||
+
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn pthread_setname_np(thread: pthread_t, name: *const c_char) -> c_int {
|
||||
+ if name.is_null() {
|
||||
+ return EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ let cstr = unsafe { core::ffi::CStr::from_ptr(name) };
|
||||
+ let name_bytes = cstr.to_bytes();
|
||||
+ let len = name_bytes.len().min(31);
|
||||
+
|
||||
+ #[cfg(target_os = "redox")]
|
||||
+ {
|
||||
+ let thread = unsafe { &*thread.cast::<crate::pthread::Pthread>() };
|
||||
+ let os_tid = unsafe { thread.os_tid.get().read() };
|
||||
+ let path = alloc::format!("proc:{}/name", os_tid.thread_fd);
|
||||
+ let fd = match Sys::open(&path, crate::header::fcntl::O_WRONLY, 0) {
|
||||
+ Ok(fd) => fd,
|
||||
+ Err(Errno(code)) => return code,
|
||||
+ };
|
||||
+
|
||||
+ let result = match Sys::write(fd, &name_bytes[..len]) {
|
||||
+ Ok(written) if written == len => 0,
|
||||
+ Ok(_) => crate::header::errno::EIO,
|
||||
+ Err(Errno(code)) => code,
|
||||
+ };
|
||||
+ let _ = Sys::close(fd);
|
||||
+ result
|
||||
+ }
|
||||
+ #[cfg(not(target_os = "redox"))]
|
||||
+ {
|
||||
+ let _ = thread;
|
||||
+ 0
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn pthread_getname_np(
|
||||
+ thread: pthread_t,
|
||||
+ name: *mut c_char,
|
||||
+ len: size_t,
|
||||
+) -> c_int {
|
||||
+ if name.is_null() {
|
||||
+ return EINVAL;
|
||||
+ }
|
||||
+ if len == 0 {
|
||||
+ return ERANGE;
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(target_os = "redox")]
|
||||
+ {
|
||||
+ let thread = unsafe { &*thread.cast::<crate::pthread::Pthread>() };
|
||||
+ let os_tid = unsafe { thread.os_tid.get().read() };
|
||||
+ let path = alloc::format!("proc:{}/name", os_tid.thread_fd);
|
||||
+ let fd = match Sys::open(&path, crate::header::fcntl::O_RDONLY, 0) {
|
||||
+ Ok(fd) => fd,
|
||||
+ Err(Errno(code)) => return code,
|
||||
+ };
|
||||
+
|
||||
+ let mut buf = [0u8; 31];
|
||||
+ let result = match Sys::read(fd, &mut buf) {
|
||||
+ Ok(read) if read < len => {
|
||||
+ unsafe { core::ptr::copy_nonoverlapping(buf.as_ptr(), name.cast(), read) };
|
||||
+ unsafe { *name.add(read) = 0 };
|
||||
+ 0
|
||||
+ }
|
||||
+ Ok(_) => ERANGE,
|
||||
+ Err(Errno(code)) => code,
|
||||
+ };
|
||||
+ let _ = Sys::close(fd);
|
||||
+ result
|
||||
+ }
|
||||
+ #[cfg(not(target_os = "redox"))]
|
||||
+ {
|
||||
+ let _ = thread;
|
||||
+ unsafe { *name = 0 };
|
||||
+ 0
|
||||
+ }
|
||||
+}
|
||||
@@ -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,12 +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,326 +0,0 @@
|
||||
diff --git a/src/header/pthread/mod.rs b/src/header/pthread/mod.rs
|
||||
index c742a42..008090a 100644
|
||||
--- a/src/header/pthread/mod.rs
|
||||
+++ b/src/header/pthread/mod.rs
|
||||
@@ -3,15 +3,26 @@
|
||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/pthread.h.html>.
|
||||
|
||||
use alloc::collections::LinkedList;
|
||||
-use core::{cell::Cell, ptr::NonNull};
|
||||
+use core::{cell::Cell, mem::size_of, ptr::NonNull};
|
||||
+
|
||||
+#[cfg(target_os = "redox")]
|
||||
+use redox_rt::proc::FdGuard;
|
||||
+#[cfg(target_os = "linux")]
|
||||
+use sc::syscall;
|
||||
+#[cfg(target_os = "redox")]
|
||||
+use syscall;
|
||||
|
||||
use crate::{
|
||||
error::Errno,
|
||||
- header::{bits_timespec::timespec, sched::*},
|
||||
+ header::{
|
||||
+ bits_timespec::timespec,
|
||||
+ errno::{EINVAL, ERANGE},
|
||||
+ sched::*,
|
||||
+ },
|
||||
platform::{
|
||||
Pal, Sys,
|
||||
types::{
|
||||
- c_int, c_uchar, c_uint, c_void, clockid_t, pthread_attr_t, pthread_barrier_t,
|
||||
+ c_char, c_int, c_uchar, c_uint, c_void, clockid_t, pthread_attr_t, pthread_barrier_t,
|
||||
pthread_barrierattr_t, pthread_cond_t, pthread_condattr_t, pthread_key_t,
|
||||
pthread_mutex_t, pthread_mutexattr_t, pthread_once_t, pthread_rwlock_t,
|
||||
pthread_rwlockattr_t, pthread_spinlock_t, pthread_t, size_t,
|
||||
@@ -20,6 +31,9 @@ use crate::{
|
||||
pthread,
|
||||
};
|
||||
|
||||
+#[cfg(target_os = "linux")]
|
||||
+use crate::platform::sys::e_raw;
|
||||
+
|
||||
pub fn e(result: Result<(), Errno>) -> i32 {
|
||||
match result {
|
||||
Ok(()) => 0,
|
||||
@@ -27,6 +41,96 @@ pub fn e(result: Result<(), Errno>) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
+const RLCT_AFFINITY_BYTES: usize = size_of::<u64>();
|
||||
+const RLCT_MAX_AFFINITY_CPUS: usize = u64::BITS as usize;
|
||||
+
|
||||
+fn cpuset_bytes<'a>(cpusetsize: size_t, cpuset: *const cpu_set_t) -> Result<&'a [u8], Errno> {
|
||||
+ if cpuset.is_null() || !(RLCT_AFFINITY_BYTES..=size_of::<cpu_set_t>()).contains(&cpusetsize) {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ Ok(unsafe { core::slice::from_raw_parts(cpuset.cast::<u8>(), cpusetsize) })
|
||||
+}
|
||||
+
|
||||
+fn cpuset_bytes_mut<'a>(cpusetsize: size_t, cpuset: *mut cpu_set_t) -> Result<&'a mut [u8], Errno> {
|
||||
+ if cpuset.is_null() || !(RLCT_AFFINITY_BYTES..=size_of::<cpu_set_t>()).contains(&cpusetsize) {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ Ok(unsafe { core::slice::from_raw_parts_mut(cpuset.cast::<u8>(), cpusetsize) })
|
||||
+}
|
||||
+
|
||||
+fn cpuset_to_u64(cpusetsize: size_t, cpuset: *const cpu_set_t) -> Result<u64, Errno> {
|
||||
+ let bytes = cpuset_bytes(cpusetsize, cpuset)?;
|
||||
+ let mut mask = 0_u64;
|
||||
+
|
||||
+ for (byte_index, byte) in bytes.iter().copied().enumerate() {
|
||||
+ for bit in 0..u8::BITS as usize {
|
||||
+ if byte & (1 << bit) == 0 {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let cpu = byte_index * u8::BITS as usize + bit;
|
||||
+ if cpu >= RLCT_MAX_AFFINITY_CPUS {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ mask |= 1_u64 << cpu;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ Ok(mask)
|
||||
+}
|
||||
+
|
||||
+fn copy_u64_to_cpuset(mask: u64, cpusetsize: size_t, cpuset: *mut cpu_set_t) -> Result<(), Errno> {
|
||||
+ let bytes = cpuset_bytes_mut(cpusetsize, cpuset)?;
|
||||
+ bytes.fill(0);
|
||||
+
|
||||
+ for (byte_index, dst) in bytes.iter_mut().take(RLCT_AFFINITY_BYTES).enumerate() {
|
||||
+ *dst = (mask >> (byte_index * u8::BITS as usize)) as u8;
|
||||
+ }
|
||||
+
|
||||
+ Ok(())
|
||||
+}
|
||||
+
|
||||
+#[cfg(target_os = "redox")]
|
||||
+fn redox_set_thread_affinity(thread: &pthread::Pthread, mask: u64) -> Result<(), Errno> {
|
||||
+ let mut kernel_cpuset = cpu_set_t::default();
|
||||
+ kernel_cpuset.__bits[0] = mask;
|
||||
+
|
||||
+ let handle = FdGuard::new(unsafe {
|
||||
+ syscall::dup(thread.os_tid.get().read().thread_fd, b"sched-affinity")?
|
||||
+ });
|
||||
+ let _ = handle.write(unsafe {
|
||||
+ core::slice::from_raw_parts(
|
||||
+ core::ptr::from_ref(&kernel_cpuset).cast::<u8>(),
|
||||
+ size_of::<cpu_set_t>(),
|
||||
+ )
|
||||
+ })?;
|
||||
+
|
||||
+ Ok(())
|
||||
+}
|
||||
+
|
||||
+#[cfg(target_os = "redox")]
|
||||
+fn redox_get_thread_affinity(thread: &pthread::Pthread) -> Result<u64, Errno> {
|
||||
+ let handle = FdGuard::new(unsafe {
|
||||
+ syscall::dup(thread.os_tid.get().read().thread_fd, b"sched-affinity")?
|
||||
+ });
|
||||
+ let mut kernel_cpuset = cpu_set_t::default();
|
||||
+ let _ = handle.read(unsafe {
|
||||
+ core::slice::from_raw_parts_mut(
|
||||
+ core::ptr::from_mut(&mut kernel_cpuset).cast::<u8>(),
|
||||
+ size_of::<cpu_set_t>(),
|
||||
+ )
|
||||
+ })?;
|
||||
+
|
||||
+ if kernel_cpuset.__bits[1..].iter().any(|bits| *bits != 0) {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ Ok(kernel_cpuset.__bits[0])
|
||||
+}
|
||||
+
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct RlctAttr {
|
||||
pub detachstate: c_uchar,
|
||||
@@ -186,6 +290,43 @@ pub unsafe extern "C" fn pthread_getcpuclockid(
|
||||
}
|
||||
}
|
||||
|
||||
+/// GNU extension. See <https://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn pthread_getaffinity_np(
|
||||
+ thread: pthread_t,
|
||||
+ cpusetsize: size_t,
|
||||
+ cpuset: *mut cpu_set_t,
|
||||
+) -> c_int {
|
||||
+ let thread: &pthread::Pthread = unsafe { &*thread.cast() };
|
||||
+
|
||||
+ let result = {
|
||||
+ #[cfg(target_os = "redox")]
|
||||
+ {
|
||||
+ redox_get_thread_affinity(thread)
|
||||
+ .and_then(|mask| copy_u64_to_cpuset(mask, cpusetsize, cpuset))
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(target_os = "linux")]
|
||||
+ {
|
||||
+ if cpuset.is_null() {
|
||||
+ Err(Errno(EINVAL))
|
||||
+ } else {
|
||||
+ e_raw(unsafe {
|
||||
+ syscall!(
|
||||
+ SCHED_GETAFFINITY,
|
||||
+ thread.os_tid.get().read().thread_id,
|
||||
+ cpusetsize,
|
||||
+ cpuset.cast::<c_void>()
|
||||
+ )
|
||||
+ })
|
||||
+ .map(|_| ())
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ e(result)
|
||||
+}
|
||||
+
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_getschedparam.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_getschedparam(
|
||||
@@ -235,6 +376,43 @@ pub unsafe extern "C" fn pthread_self() -> pthread_t {
|
||||
core::ptr::from_ref(unsafe { pthread::current_thread().unwrap_unchecked() }) as *mut _
|
||||
}
|
||||
|
||||
+/// GNU extension. See <https://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html>.
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn pthread_setaffinity_np(
|
||||
+ thread: pthread_t,
|
||||
+ cpusetsize: size_t,
|
||||
+ cpuset: *const cpu_set_t,
|
||||
+) -> c_int {
|
||||
+ let thread: &pthread::Pthread = unsafe { &*thread.cast() };
|
||||
+
|
||||
+ let result = {
|
||||
+ #[cfg(target_os = "redox")]
|
||||
+ {
|
||||
+ cpuset_to_u64(cpusetsize, cpuset)
|
||||
+ .and_then(|mask| redox_set_thread_affinity(thread, mask))
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(target_os = "linux")]
|
||||
+ {
|
||||
+ if cpuset.is_null() {
|
||||
+ Err(Errno(EINVAL))
|
||||
+ } else {
|
||||
+ e_raw(unsafe {
|
||||
+ syscall!(
|
||||
+ SCHED_SETAFFINITY,
|
||||
+ thread.os_tid.get().read().thread_id,
|
||||
+ cpusetsize,
|
||||
+ cpuset.cast::<c_void>()
|
||||
+ )
|
||||
+ })
|
||||
+ .map(|_| ())
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ e(result)
|
||||
+}
|
||||
+
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_setcancelstate.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_setcancelstate(state: c_int, oldstate: *mut c_int) -> c_int {
|
||||
@@ -307,6 +485,13 @@ pub unsafe extern "C" fn pthread_testcancel() {
|
||||
unsafe { pthread::testcancel() };
|
||||
}
|
||||
|
||||
+/// <https://man7.org/linux/man-pages/man3/pthread_yield.3.html>
|
||||
+///
|
||||
+/// Non-standard GNU extension. Prefer `sched_yield()` instead.
|
||||
+pub extern "C" fn pthread_yield() {
|
||||
+ let _ = Sys::sched_yield();
|
||||
+}
|
||||
+
|
||||
// Must be the same struct as defined in the pthread_cleanup_push macro.
|
||||
#[repr(C)]
|
||||
pub(crate) struct CleanupLinkedListEntry {
|
||||
@@ -350,3 +535,82 @@ pub(crate) unsafe fn run_destructor_stack() {
|
||||
(entry.routine)(entry.arg);
|
||||
}
|
||||
}
|
||||
+
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn pthread_setname_np(thread: pthread_t, name: *const c_char) -> c_int {
|
||||
+ if name.is_null() {
|
||||
+ return EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ let cstr = unsafe { core::ffi::CStr::from_ptr(name) };
|
||||
+ let name_bytes = cstr.to_bytes();
|
||||
+ let len = name_bytes.len().min(31);
|
||||
+
|
||||
+ #[cfg(target_os = "redox")]
|
||||
+ {
|
||||
+ let thread = unsafe { &*thread.cast::<crate::pthread::Pthread>() };
|
||||
+ let os_tid = unsafe { thread.os_tid.get().read() };
|
||||
+ let path = alloc::format!("proc:{}/name", os_tid.thread_fd);
|
||||
+ let fd = match Sys::open(&path, crate::header::fcntl::O_WRONLY, 0) {
|
||||
+ Ok(fd) => fd,
|
||||
+ Err(Errno(code)) => return code,
|
||||
+ };
|
||||
+
|
||||
+ let result = match Sys::write(fd, &name_bytes[..len]) {
|
||||
+ Ok(written) if written == len => 0,
|
||||
+ Ok(_) => crate::header::errno::EIO,
|
||||
+ Err(Errno(code)) => code,
|
||||
+ };
|
||||
+ let _ = Sys::close(fd);
|
||||
+ result
|
||||
+ }
|
||||
+ #[cfg(not(target_os = "redox"))]
|
||||
+ {
|
||||
+ let _ = thread;
|
||||
+ 0
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[unsafe(no_mangle)]
|
||||
+pub unsafe extern "C" fn pthread_getname_np(
|
||||
+ thread: pthread_t,
|
||||
+ name: *mut c_char,
|
||||
+ len: size_t,
|
||||
+) -> c_int {
|
||||
+ if name.is_null() {
|
||||
+ return EINVAL;
|
||||
+ }
|
||||
+ if len == 0 {
|
||||
+ return ERANGE;
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(target_os = "redox")]
|
||||
+ {
|
||||
+ let thread = unsafe { &*thread.cast::<crate::pthread::Pthread>() };
|
||||
+ let os_tid = unsafe { thread.os_tid.get().read() };
|
||||
+ let path = alloc::format!("proc:{}/name", os_tid.thread_fd);
|
||||
+ let fd = match Sys::open(&path, crate::header::fcntl::O_RDONLY, 0) {
|
||||
+ Ok(fd) => fd,
|
||||
+ Err(Errno(code)) => return code,
|
||||
+ };
|
||||
+
|
||||
+ let mut buf = [0u8; 31];
|
||||
+ let result = match Sys::read(fd, &mut buf) {
|
||||
+ Ok(read) if read < len => {
|
||||
+ unsafe { core::ptr::copy_nonoverlapping(buf.as_ptr(), name.cast(), read) };
|
||||
+ unsafe { *name.add(read) = 0 };
|
||||
+ 0
|
||||
+ }
|
||||
+ Ok(_) => ERANGE,
|
||||
+ Err(Errno(code)) => code,
|
||||
+ };
|
||||
+ let _ = Sys::close(fd);
|
||||
+ result
|
||||
+ }
|
||||
+ #[cfg(not(target_os = "redox"))]
|
||||
+ {
|
||||
+ let _ = thread;
|
||||
+ unsafe { *name = 0 };
|
||||
+ 0
|
||||
+ }
|
||||
+}
|
||||
@@ -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,8 @@ 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,8 @@ 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,380 +0,0 @@
|
||||
diff --git a/src/sync/pthread_mutex.rs b/src/sync/pthread_mutex.rs
|
||||
index 29bad63..af0c429 100644
|
||||
--- a/src/sync/pthread_mutex.rs
|
||||
+++ b/src/sync/pthread_mutex.rs
|
||||
@@ -1,3 +1,4 @@
|
||||
+use alloc::boxed::Box;
|
||||
use core::{
|
||||
cell::Cell,
|
||||
sync::atomic::{AtomicU32 as AtomicUint, Ordering},
|
||||
@@ -6,10 +7,9 @@ use core::{
|
||||
use crate::{
|
||||
error::Errno,
|
||||
header::{bits_timespec::timespec, errno::*, pthread::*},
|
||||
+ platform::{Pal, Sys, types::c_int},
|
||||
};
|
||||
|
||||
-use crate::platform::{Pal, Sys, types::c_int};
|
||||
-
|
||||
use super::FutexWaitResult;
|
||||
|
||||
pub struct RlctMutex {
|
||||
@@ -21,15 +21,22 @@ pub struct RlctMutex {
|
||||
robust: bool,
|
||||
}
|
||||
|
||||
+pub struct RobustMutexNode {
|
||||
+ pub next: *mut RobustMutexNode,
|
||||
+ pub prev: *mut RobustMutexNode,
|
||||
+ pub mutex: *const RlctMutex,
|
||||
+}
|
||||
+
|
||||
const STATE_UNLOCKED: u32 = 0;
|
||||
const WAITING_BIT: u32 = 1 << 31;
|
||||
-const INDEX_MASK: u32 = !WAITING_BIT;
|
||||
+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;
|
||||
// TODO: How many spins should we do before it becomes more time-economical to enter kernel mode
|
||||
// via futexes?
|
||||
-const SPIN_COUNT: usize = 0;
|
||||
+const SPIN_COUNT: usize = 100;
|
||||
|
||||
impl RlctMutex {
|
||||
pub(crate) fn new(attr: &RlctMutexAttr) -> Result<Self, Errno> {
|
||||
@@ -69,13 +76,25 @@ impl RlctMutex {
|
||||
Ok(0)
|
||||
}
|
||||
pub fn make_consistent(&self) -> Result<(), Errno> {
|
||||
- todo_skip!(0, "pthread robust mutexes: not implemented");
|
||||
- Ok(())
|
||||
+ debug_assert!(self.robust, "make_consistent called on non-robust mutex");
|
||||
+
|
||||
+ if !self.robust {
|
||||
+ return Err(Errno(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ let current = self.inner.load(Ordering::Relaxed);
|
||||
+ let owner = current & INDEX_MASK;
|
||||
+
|
||||
+ if owner == os_tid_invalid_after_fork() && current & FUTEX_OWNER_DIED != 0 {
|
||||
+ self.inner.store(0, Ordering::Release);
|
||||
+ Ok(())
|
||||
+ } else {
|
||||
+ Err(Errno(EINVAL))
|
||||
+ }
|
||||
}
|
||||
fn lock_inner(&self, deadline: Option<×pec>) -> Result<(), Errno> {
|
||||
let this_thread = os_tid_invalid_after_fork();
|
||||
-
|
||||
- //let mut spins_left = SPIN_COUNT;
|
||||
+ let mut spins_left = SPIN_COUNT;
|
||||
|
||||
loop {
|
||||
let result = self.inner.compare_exchange_weak(
|
||||
@@ -86,45 +105,59 @@ impl RlctMutex {
|
||||
);
|
||||
|
||||
match result {
|
||||
- // CAS succeeded
|
||||
- Ok(_) => {
|
||||
- if self.ty == Ty::Recursive {
|
||||
- self.increment_recursive_count()?;
|
||||
- }
|
||||
- return Ok(());
|
||||
- }
|
||||
- // CAS failed, but the mutex was recursive and we already own the lock.
|
||||
+ Ok(_) => return self.finish_lock_acquire(false),
|
||||
Err(thread) if thread & INDEX_MASK == this_thread && self.ty == Ty::Recursive => {
|
||||
self.increment_recursive_count()?;
|
||||
return Ok(());
|
||||
}
|
||||
- // CAS failed, but the mutex was error-checking and we already own the lock.
|
||||
Err(thread) if thread & INDEX_MASK == this_thread && self.ty == Ty::Errck => {
|
||||
- return Err(Errno(EAGAIN));
|
||||
+ return Err(Errno(EDEADLK));
|
||||
}
|
||||
- // CAS spuriously failed, simply retry the CAS. TODO: Use core::hint::spin_loop()?
|
||||
- Err(thread) if thread & INDEX_MASK == 0 => {
|
||||
- continue;
|
||||
+ Err(thread) if thread & FUTEX_OWNER_DIED != 0 && thread & INDEX_MASK == 0 => {
|
||||
+ return Err(Errno(ENOTRECOVERABLE));
|
||||
}
|
||||
- // CAS failed because some other thread owned the lock. We must now wait.
|
||||
+ Err(thread) if thread & FUTEX_OWNER_DIED != 0 => {
|
||||
+ if !self.robust {
|
||||
+ return Err(Errno(ENOTRECOVERABLE));
|
||||
+ }
|
||||
+
|
||||
+ let new_value = (thread & WAITING_BIT) | FUTEX_OWNER_DIED | this_thread;
|
||||
+ match self.inner.compare_exchange(
|
||||
+ thread,
|
||||
+ new_value,
|
||||
+ Ordering::Acquire,
|
||||
+ Ordering::Relaxed,
|
||||
+ ) {
|
||||
+ Ok(_) => return self.finish_lock_acquire(true),
|
||||
+ Err(_) => continue,
|
||||
+ }
|
||||
+ }
|
||||
+ Err(thread) if thread & INDEX_MASK == 0 => continue,
|
||||
Err(thread) => {
|
||||
- /*if spins_left > 0 {
|
||||
- // TODO: Faster to spin trying to load the flag, compared to CAS?
|
||||
+ let owner = thread & INDEX_MASK;
|
||||
+
|
||||
+ if !crate::pthread::mutex_owner_id_is_live(owner) {
|
||||
+ if !self.robust {
|
||||
+ return Err(Errno(ENOTRECOVERABLE));
|
||||
+ }
|
||||
+
|
||||
+ let new_value = (thread & WAITING_BIT) | FUTEX_OWNER_DIED | this_thread;
|
||||
+ match self.inner.compare_exchange(
|
||||
+ thread,
|
||||
+ new_value,
|
||||
+ Ordering::Acquire,
|
||||
+ Ordering::Relaxed,
|
||||
+ ) {
|
||||
+ Ok(_) => return self.finish_lock_acquire(true),
|
||||
+ Err(_) => continue,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if spins_left > 0 {
|
||||
spins_left -= 1;
|
||||
core::hint::spin_loop();
|
||||
continue;
|
||||
}
|
||||
-
|
||||
- spins_left = SPIN_COUNT;
|
||||
-
|
||||
- let inner = self.inner.fetch_or(WAITING_BIT, Ordering::Relaxed);
|
||||
-
|
||||
- if inner == STATE_UNLOCKED {
|
||||
- continue;
|
||||
- }*/
|
||||
-
|
||||
- // If the mutex is not robust, simply futex_wait until unblocked.
|
||||
- //crate::sync::futex_wait(&self.inner, inner | WAITING_BIT, None);
|
||||
if crate::sync::futex_wait(&self.inner, thread, deadline)
|
||||
== FutexWaitResult::TimedOut
|
||||
{
|
||||
@@ -140,6 +173,20 @@ impl RlctMutex {
|
||||
pub fn lock_with_timeout(&self, deadline: ×pec) -> Result<(), Errno> {
|
||||
self.lock_inner(Some(deadline))
|
||||
}
|
||||
+ fn finish_lock_acquire(&self, owner_dead: bool) -> Result<(), Errno> {
|
||||
+ if self.ty == Ty::Recursive {
|
||||
+ self.increment_recursive_count()?;
|
||||
+ }
|
||||
+ if self.robust {
|
||||
+ add_to_robust_list(self);
|
||||
+ }
|
||||
+
|
||||
+ if owner_dead {
|
||||
+ Err(Errno(EOWNERDEAD))
|
||||
+ } else {
|
||||
+ Ok(())
|
||||
+ }
|
||||
+ }
|
||||
fn increment_recursive_count(&self) -> Result<(), Errno> {
|
||||
// We don't have to worry about asynchronous signals here, since pthread_mutex_trylock
|
||||
// is not async-signal-safe.
|
||||
@@ -161,41 +208,65 @@ impl RlctMutex {
|
||||
pub fn try_lock(&self) -> Result<(), Errno> {
|
||||
let this_thread = os_tid_invalid_after_fork();
|
||||
|
||||
- // TODO: If recursive, omitting CAS may be faster if it is already owned by this thread.
|
||||
- let result = self.inner.compare_exchange(
|
||||
- STATE_UNLOCKED,
|
||||
- this_thread,
|
||||
- Ordering::Acquire,
|
||||
- Ordering::Relaxed,
|
||||
- );
|
||||
+ loop {
|
||||
+ let current = self.inner.load(Ordering::Relaxed);
|
||||
+
|
||||
+ if current == STATE_UNLOCKED {
|
||||
+ match self.inner.compare_exchange(
|
||||
+ STATE_UNLOCKED,
|
||||
+ this_thread,
|
||||
+ Ordering::Acquire,
|
||||
+ Ordering::Relaxed,
|
||||
+ ) {
|
||||
+ Ok(_) => return self.finish_lock_acquire(false),
|
||||
+ Err(_) => continue,
|
||||
+ }
|
||||
+ }
|
||||
|
||||
- if self.ty == Ty::Recursive {
|
||||
- match result {
|
||||
- Err(index) if index & INDEX_MASK != this_thread => return Err(Errno(EBUSY)),
|
||||
- _ => (),
|
||||
+ let owner = current & INDEX_MASK;
|
||||
+
|
||||
+ if owner == this_thread && self.ty == Ty::Recursive {
|
||||
+ self.increment_recursive_count()?;
|
||||
+ return Ok(());
|
||||
}
|
||||
|
||||
- self.increment_recursive_count()?;
|
||||
+ if owner == this_thread && self.ty == Ty::Errck {
|
||||
+ return Err(Errno(EDEADLK));
|
||||
+ }
|
||||
|
||||
- return Ok(());
|
||||
- }
|
||||
+ if current & FUTEX_OWNER_DIED != 0 && owner == 0 {
|
||||
+ return Err(Errno(ENOTRECOVERABLE));
|
||||
+ }
|
||||
|
||||
- match result {
|
||||
- Ok(_) => Ok(()),
|
||||
- Err(index) if index & INDEX_MASK == this_thread && self.ty == Ty::Errck => {
|
||||
- Err(Errno(EDEADLK))
|
||||
+ if current & FUTEX_OWNER_DIED != 0 || (owner != 0 && !crate::pthread::mutex_owner_id_is_live(owner)) {
|
||||
+ if !self.robust {
|
||||
+ return Err(Errno(ENOTRECOVERABLE));
|
||||
+ }
|
||||
+
|
||||
+ let new_value = (current & WAITING_BIT) | FUTEX_OWNER_DIED | this_thread;
|
||||
+ match self.inner.compare_exchange(
|
||||
+ current,
|
||||
+ new_value,
|
||||
+ Ordering::Acquire,
|
||||
+ Ordering::Relaxed,
|
||||
+ ) {
|
||||
+ Ok(_) => return self.finish_lock_acquire(true),
|
||||
+ Err(_) => continue,
|
||||
+ }
|
||||
}
|
||||
- Err(_) => Err(Errno(EBUSY)),
|
||||
+
|
||||
+ return Err(Errno(EBUSY));
|
||||
}
|
||||
}
|
||||
// Safe because we are not protecting any data.
|
||||
pub fn unlock(&self) -> Result<(), Errno> {
|
||||
+ let current = self.inner.load(Ordering::Relaxed);
|
||||
+
|
||||
if self.robust || matches!(self.ty, Ty::Recursive | Ty::Errck) {
|
||||
- if self.inner.load(Ordering::Relaxed) & INDEX_MASK != os_tid_invalid_after_fork() {
|
||||
+ if current & INDEX_MASK != os_tid_invalid_after_fork() {
|
||||
return Err(Errno(EPERM));
|
||||
}
|
||||
|
||||
- // TODO: Is this fence correct?
|
||||
core::sync::atomic::fence(Ordering::Acquire);
|
||||
}
|
||||
|
||||
@@ -208,18 +279,47 @@ impl RlctMutex {
|
||||
}
|
||||
}
|
||||
|
||||
- self.inner.store(STATE_UNLOCKED, Ordering::Release);
|
||||
- crate::sync::futex_wake(&self.inner, i32::MAX);
|
||||
- /*let was_waiting = self.inner.swap(STATE_UNLOCKED, Ordering::Release) & WAITING_BIT != 0;
|
||||
+ if self.robust {
|
||||
+ remove_from_robust_list(self);
|
||||
+ }
|
||||
|
||||
- if was_waiting {
|
||||
- let _ = crate::sync::futex_wake(&self.inner, 1);
|
||||
- }*/
|
||||
+ let new_state = if self.robust && current & FUTEX_OWNER_DIED != 0 {
|
||||
+ FUTEX_OWNER_DIED
|
||||
+ } else {
|
||||
+ STATE_UNLOCKED
|
||||
+ };
|
||||
+
|
||||
+ self.inner.store(new_state, Ordering::Release);
|
||||
+ crate::sync::futex_wake(&self.inner, i32::MAX);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+pub(crate) unsafe fn mark_robust_mutexes_dead(thread: &crate::pthread::Pthread) {
|
||||
+ let head = thread.robust_list_head.get();
|
||||
+ let this_thread = os_tid_invalid_after_fork();
|
||||
+ let mut node = unsafe { *head };
|
||||
+
|
||||
+ unsafe { *head = core::ptr::null_mut() };
|
||||
+
|
||||
+ while !node.is_null() {
|
||||
+ let next = unsafe { (*node).next };
|
||||
+ let mutex = unsafe { &*(*node).mutex };
|
||||
+ let current = mutex.inner.load(Ordering::Relaxed);
|
||||
+
|
||||
+ if current & INDEX_MASK == this_thread {
|
||||
+ mutex
|
||||
+ .inner
|
||||
+ .store((current & WAITING_BIT) | FUTEX_OWNER_DIED | this_thread, Ordering::Release);
|
||||
+ crate::sync::futex_wake(&mutex.inner, i32::MAX);
|
||||
+ }
|
||||
+
|
||||
+ unsafe { drop(Box::from_raw(node)) };
|
||||
+ node = next;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
#[repr(u8)]
|
||||
#[derive(PartialEq)]
|
||||
enum Ty {
|
||||
@@ -237,6 +337,54 @@ enum Ty {
|
||||
#[thread_local]
|
||||
static CACHED_OS_TID_INVALID_AFTER_FORK: Cell<u32> = Cell::new(0);
|
||||
|
||||
+fn add_to_robust_list(mutex: &RlctMutex) {
|
||||
+ let thread = crate::pthread::current_thread().expect("current thread not present");
|
||||
+ let node_ptr = Box::into_raw(Box::new(RobustMutexNode {
|
||||
+ next: core::ptr::null_mut(),
|
||||
+ prev: core::ptr::null_mut(),
|
||||
+ mutex: core::ptr::from_ref(mutex),
|
||||
+ }));
|
||||
+
|
||||
+ unsafe {
|
||||
+ let head = thread.robust_list_head.get();
|
||||
+ if !(*head).is_null() {
|
||||
+ (**head).prev = node_ptr;
|
||||
+ }
|
||||
+ (*node_ptr).next = *head;
|
||||
+ *head = node_ptr;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn remove_from_robust_list(mutex: &RlctMutex) {
|
||||
+ let thread = match crate::pthread::current_thread() {
|
||||
+ Some(thread) => thread,
|
||||
+ None => return,
|
||||
+ };
|
||||
+
|
||||
+ unsafe {
|
||||
+ let mut node = *thread.robust_list_head.get();
|
||||
+
|
||||
+ while !node.is_null() {
|
||||
+ if core::ptr::eq((*node).mutex, core::ptr::from_ref(mutex)) {
|
||||
+ if !(*node).prev.is_null() {
|
||||
+ (*(*node).prev).next = (*node).next;
|
||||
+ } else {
|
||||
+ *thread.robust_list_head.get() = (*node).next;
|
||||
+ }
|
||||
+
|
||||
+ if !(*node).next.is_null() {
|
||||
+ (*(*node).next).prev = (*node).prev;
|
||||
+ }
|
||||
+
|
||||
+ drop(Box::from_raw(node));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ node = (*node).next;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
// Assumes TIDs are unique between processes, which I only know is true for Redox.
|
||||
fn os_tid_invalid_after_fork() -> u32 {
|
||||
// TODO: Coordinate better if using shared == PTHREAD_PROCESS_SHARED, with up to 2^32 separate
|
||||
File diff suppressed because it is too large
Load Diff
+1
-1
Submodule local/sources/base updated: 360cd8bbdf...1fc7af3c9f
+1
-1
Submodule local/sources/bootloader updated: 5bee5c5afe...fe009bb9d6
+1
-1
Submodule local/sources/installer updated: a2e58a7175...101a836a92
+1
-1
Submodule local/sources/kernel updated: 74f34118da...97b07bcb44
+1
-1
Submodule local/sources/relibc updated: f3775e7d52...5b2a221135
@@ -1,34 +0,0 @@
|
||||
diff '--color=auto' -ruwN source/programs/util.h source-new/programs/util.h
|
||||
--- source/programs/util.h 2024-07-21 13:29:49.000000000 -0400
|
||||
+++ source-new/programs/util.h 2024-12-13 02:21:03.032769559 -0500
|
||||
@@ -52,6 +52,9 @@
|
||||
#include <time.h> /* time */
|
||||
#include <limits.h> /* INT_MAX */
|
||||
#include <errno.h>
|
||||
+#if defined(_REDOX)
|
||||
+# include <sys/time.h> /* utimes */
|
||||
+#endif
|
||||
|
||||
|
||||
|
||||
@@ -239,12 +242,20 @@
|
||||
timebuf.modtime = statbuf->st_mtime;
|
||||
res += utime(filename, &timebuf); /* set access and modification times */
|
||||
#else
|
||||
+ #if defined(_REDOX)
|
||||
+ struct timeval timebuf[2];
|
||||
+ memset(timebuf, 0, sizeof(timebuf));
|
||||
+ timebuf[0].tv_usec = UTIME_NOW;
|
||||
+ timebuf[1].tv_sec = statbuf->st_mtime;
|
||||
+ res += utimes(filename, timebuf);
|
||||
+ #else
|
||||
struct timespec timebuf[2];
|
||||
memset(timebuf, 0, sizeof(timebuf));
|
||||
timebuf[0].tv_nsec = UTIME_NOW;
|
||||
timebuf[1].tv_sec = statbuf->st_mtime;
|
||||
res += utimensat(AT_FDCWD, filename, timebuf, 0); /* set access and modification times */
|
||||
#endif
|
||||
+#endif
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
@@ -1,15 +0,0 @@
|
||||
diff -ruwN source/programs/platform.h source-new/programs/platform.h
|
||||
--- source/programs/platform.h 2025-02-19 07:04:24.000000000 +0700
|
||||
+++ source-new/programs/platform.h 2025-07-21 22:52:07.716447723 +0700
|
||||
@@ -109,6 +109,11 @@
|
||||
#endif /* PLATFORM_POSIX_VERSION */
|
||||
|
||||
|
||||
+#if defined(__redox__)
|
||||
+/* TODO: AT_FDCWD && utimensat must be defined to conform _POSIX_VERSION */
|
||||
+# define PLATFORM_POSIX_VERSION 1
|
||||
+#endif
|
||||
+
|
||||
#if PLATFORM_POSIX_VERSION > 1
|
||||
/* glibc < 2.26 may not expose struct timespec def without this.
|
||||
* See issue #1920. */
|
||||
@@ -1,17 +0,0 @@
|
||||
diff --git a/src/disk/file.rs b/src/disk/file.rs
|
||||
index 78d51bc..f923d72 100644
|
||||
--- a/src/disk/file.rs
|
||||
+++ b/src/disk/file.rs
|
||||
@@ -43,10 +43,12 @@ impl<T> ResultExt for std::io::Result<T> {
|
||||
|
||||
impl DiskFile {
|
||||
pub fn open(path: impl AsRef<Path>) -> Result<DiskFile> {
|
||||
+ let path = path.as_ref();
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
+ .or_else(|_| OpenOptions::new().read(true).open(path))
|
||||
.or_eio()?;
|
||||
Ok(DiskFile { file })
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
diff --git a/Cargo.toml b/Cargo.toml
|
||||
index 5f417bd42..b7b895a9c 100644
|
||||
--- a/Cargo.toml
|
||||
+++ b/Cargo.toml
|
||||
@@ -326,6 +326,7 @@ feat_os_unix_redox = [
|
||||
"feat_common_core",
|
||||
#
|
||||
"chmod",
|
||||
+ "nproc",
|
||||
"stat",
|
||||
"uname",
|
||||
]
|
||||
diff --git a/src/uucore/src/lib/features/fs.rs b/src/uucore/src/lib/features/fs.rs
|
||||
index fd1f30303..c508f6b9b 100644
|
||||
--- a/src/uucore/src/lib/features/fs.rs
|
||||
+++ b/src/uucore/src/lib/features/fs.rs
|
||||
@@ -13,7 +13,7 @@ use libc::{
|
||||
S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR,
|
||||
mkfifo, mode_t,
|
||||
};
|
||||
-#[cfg(all(unix, not(target_os = "redox")))]
|
||||
+#[cfg(unix)]
|
||||
pub use libc::{major, makedev, minor};
|
||||
use std::collections::HashSet;
|
||||
use std::collections::VecDeque;
|
||||
@@ -849,24 +849,6 @@ pub fn make_fifo(path: &Path) -> std::io::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
-// Redox's libc appears not to include the following utilities
|
||||
-
|
||||
-#[cfg(target_os = "redox")]
|
||||
-pub fn major(dev: libc::dev_t) -> libc::c_uint {
|
||||
- (((dev >> 8) & 0xFFF) | ((dev >> 32) & 0xFFFFF000)) as _
|
||||
-}
|
||||
-
|
||||
-#[cfg(target_os = "redox")]
|
||||
-pub fn minor(dev: libc::dev_t) -> libc::c_uint {
|
||||
- ((dev & 0xFF) | ((dev >> 12) & 0xFFFFF00)) as _
|
||||
-}
|
||||
-
|
||||
-#[cfg(target_os = "redox")]
|
||||
-pub fn makedev(maj: libc::c_uint, min: libc::c_uint) -> libc::dev_t {
|
||||
- let [maj, min] = [maj as libc::dev_t, min as libc::dev_t];
|
||||
- (min & 0xff) | ((maj & 0xfff) << 8) | ((min & !0xff) << 12) | ((maj & !0xfff) << 32)
|
||||
-}
|
||||
-
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// Note this useful idiom: importing names from outer (for mod tests) scope.
|
||||
diff --git a/src/uucore/src/lib/mods/locale.rs b/src/uucore/src/lib/mods/locale.rs
|
||||
index b670f8976..a4ff9f983 100644
|
||||
--- a/src/uucore/src/lib/mods/locale.rs
|
||||
+++ b/src/uucore/src/lib/mods/locale.rs
|
||||
@@ -211,10 +211,11 @@ fn init_localization(
|
||||
}
|
||||
};
|
||||
|
||||
- LOCALIZER.with(|lock| {
|
||||
+ // TODO: In aarch64 redox OS, this lock (once cell) is already initialized out of nothing
|
||||
+ let _ = LOCALIZER.with(|lock| {
|
||||
lock.set(loc)
|
||||
.map_err(|_| LocalizationError::Bundle("Localizer already initialized".into()))
|
||||
- })?;
|
||||
+ });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -422,10 +423,12 @@ pub fn setup_localization(p: &str) -> Result<(), LocalizationError> {
|
||||
let english_bundle = create_english_bundle_from_embedded(&default_locale, p)?;
|
||||
let localizer = Localizer::new(english_bundle);
|
||||
|
||||
- LOCALIZER.with(|lock| {
|
||||
+ // TODO: In aarch64 redox OS, this lock (once cell) is already initialized out of nothing
|
||||
+ // TODO: When this code is used? Patching for keep sake
|
||||
+ let _ = LOCALIZER.with(|lock| {
|
||||
lock.set(localizer)
|
||||
.map_err(|_| LocalizationError::Bundle("Localizer already initialized".into()))
|
||||
- })?;
|
||||
+ });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
diff -ruwN source/src/fccache.c source-new/src/fccache.c
|
||||
--- source/src/fccache.c 2019-06-10 05:36:37.000000000 -0600
|
||||
+++ source-new/src/fccache.c 2019-10-02 19:48:10.459642095 -0600
|
||||
@@ -1526,7 +1526,7 @@
|
||||
#if defined(_WIN32)
|
||||
if (_locking (fd, _LK_LOCK, 1) == -1)
|
||||
goto bail;
|
||||
-#else
|
||||
+#elif !defined(__redox__)
|
||||
struct flock fl;
|
||||
|
||||
fl.l_type = F_WRLCK;
|
||||
@@ -1556,7 +1556,7 @@
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
_locking (fd, _LK_UNLCK, 1);
|
||||
-#else
|
||||
+#elif !defined(__redox__)
|
||||
struct flock fl;
|
||||
|
||||
fl.l_type = F_UNLCK;
|
||||
diff -ruwN source/src/fccharset.c source-new/src/fccharset.c
|
||||
--- source/src/fccharset.c 2018-06-05 04:36:38.000000000 -0600
|
||||
+++ source-new/src/fccharset.c 2019-10-02 19:48:53.082862133 -0600
|
||||
@@ -600,7 +600,7 @@
|
||||
static FcChar32
|
||||
FcCharSetPopCount (FcChar32 c1)
|
||||
{
|
||||
-#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
|
||||
+#if !defined(__redox__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
|
||||
return __builtin_popcount (c1);
|
||||
#else
|
||||
/* hackmem 169 */
|
||||
@@ -1,240 +0,0 @@
|
||||
diff -ruwN git-2.13.1/compat/bswap.h source/compat/bswap.h
|
||||
--- git-2.13.1/compat/bswap.h 2017-06-04 19:08:11.000000000 -0600
|
||||
+++ source/compat/bswap.h 2025-04-24 11:20:06.475749424 -0600
|
||||
@@ -1,3 +1,7 @@
|
||||
+#if defined(__redox__)
|
||||
+#include <machine/endian.h>
|
||||
+#endif
|
||||
+
|
||||
/*
|
||||
* Let's make sure we always have a sane definition for ntohl()/htonl().
|
||||
* Some libraries define those as a function call, just to perform byte
|
||||
diff -ruwN git-2.13.1/compat/terminal.c source/compat/terminal.c
|
||||
--- git-2.13.1/compat/terminal.c 2017-06-04 19:08:11.000000000 -0600
|
||||
+++ source/compat/terminal.c 2025-04-18 10:00:11.318697446 -0600
|
||||
@@ -137,6 +137,18 @@
|
||||
return buf.buf;
|
||||
}
|
||||
|
||||
+#elif defined(__redox__)
|
||||
+
|
||||
+ssize_t __getline(char **lptr, size_t *n, FILE *fp);
|
||||
+
|
||||
+char *git_terminal_prompt(const char *prompt, int echo)
|
||||
+{
|
||||
+ char *line = NULL;
|
||||
+ size_t n = 0;
|
||||
+ __getline(&line, &n, stdin);
|
||||
+ return line; // XXX leak
|
||||
+}
|
||||
+
|
||||
#else
|
||||
|
||||
char *git_terminal_prompt(const char *prompt, int echo)
|
||||
diff -ruwN git-2.13.1/configure source/configure
|
||||
--- git-2.13.1/configure 2017-06-04 19:08:11.000000000 -0600
|
||||
+++ source/configure 2025-04-18 10:00:11.318697446 -0600
|
||||
@@ -6156,7 +6156,7 @@
|
||||
ac_res=$ac_cv_search_getaddrinfo
|
||||
if test "$ac_res" != no; then :
|
||||
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
|
||||
- NO_IPV6=
|
||||
+ NO_IPV6=YesPlease
|
||||
else
|
||||
NO_IPV6=YesPlease
|
||||
fi
|
||||
diff -ruwN git-2.13.1/daemon.c source/daemon.c
|
||||
--- git-2.13.1/daemon.c 2017-06-04 19:08:11.000000000 -0600
|
||||
+++ source/daemon.c 2025-04-18 10:00:11.319697447 -0600
|
||||
@@ -71,13 +71,21 @@
|
||||
return hi->ip_address.buf;
|
||||
}
|
||||
|
||||
+#if defined(__redox__)
|
||||
+#define LOG_ERR 0
|
||||
+#define LOG_INFO 1
|
||||
+#endif
|
||||
+
|
||||
static void logreport(int priority, const char *err, va_list params)
|
||||
{
|
||||
+#if !defined(__redox__)
|
||||
if (log_syslog) {
|
||||
char buf[1024];
|
||||
vsnprintf(buf, sizeof(buf), err, params);
|
||||
syslog(priority, "%s", buf);
|
||||
- } else {
|
||||
+ } else
|
||||
+#endif
|
||||
+ {
|
||||
/*
|
||||
* Since stderr is set to buffered mode, the
|
||||
* logging of different processes will not overlap
|
||||
@@ -888,8 +896,12 @@
|
||||
|
||||
if (!reuseaddr)
|
||||
return 0;
|
||||
+#if defined(__redox__)
|
||||
+ return 0;
|
||||
+#else
|
||||
return setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
|
||||
&on, sizeof(on));
|
||||
+#endif
|
||||
}
|
||||
|
||||
struct socketlist {
|
||||
@@ -1174,11 +1186,7 @@
|
||||
if (!group_name)
|
||||
c.gid = c.pass->pw_gid;
|
||||
else {
|
||||
- struct group *group = getgrnam(group_name);
|
||||
- if (!group)
|
||||
die("group not found - %s", group_name);
|
||||
-
|
||||
- c.gid = group->gr_gid;
|
||||
}
|
||||
|
||||
return &c;
|
||||
@@ -1348,10 +1356,12 @@
|
||||
usage(daemon_usage);
|
||||
}
|
||||
|
||||
+#if !defined(__redox__)
|
||||
if (log_syslog) {
|
||||
openlog("git-daemon", LOG_PID, LOG_DAEMON);
|
||||
set_die_routine(daemon_die);
|
||||
} else
|
||||
+#endif
|
||||
/* avoid splitting a message in the middle */
|
||||
setvbuf(stderr, NULL, _IOFBF, 4096);
|
||||
|
||||
diff -ruwN git-2.13.1/git-compat-util.h source/git-compat-util.h
|
||||
--- git-2.13.1/git-compat-util.h 2017-06-04 19:08:11.000000000 -0600
|
||||
+++ source/git-compat-util.h 2025-04-18 10:00:11.319697447 -0600
|
||||
@@ -1,6 +1,18 @@
|
||||
#ifndef GIT_COMPAT_UTIL_H
|
||||
#define GIT_COMPAT_UTIL_H
|
||||
|
||||
+#ifndef SIG_DFL
|
||||
+#define SIG_DFL ((void (*)(int))0)
|
||||
+#endif
|
||||
+
|
||||
+#ifndef SIG_IGN
|
||||
+#define SIG_IGN ((void (*)(int))1)
|
||||
+#endif
|
||||
+
|
||||
+#ifndef SIG_ERR
|
||||
+#define SIG_ERR ((void (*)(int))-1)
|
||||
+#endif
|
||||
+
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
|
||||
@@ -323,6 +335,14 @@
|
||||
#define PATH_SEP ':'
|
||||
#endif
|
||||
|
||||
+#ifndef DEV_NULL
|
||||
+#if defined(__redox__)
|
||||
+#define DEV_NULL "/scheme/null"
|
||||
+#else
|
||||
+#define DEV_NULL "/dev/null"
|
||||
+#endif
|
||||
+#endif
|
||||
+
|
||||
#ifdef HAVE_PATHS_H
|
||||
#include <paths.h>
|
||||
#endif
|
||||
diff -ruwN git-2.13.1/Makefile source/Makefile
|
||||
--- git-2.13.1/Makefile 2017-06-05 08:08:11.000000000 +0700
|
||||
+++ source/Makefile 2025-09-01 04:41:10.339224568 +0700
|
||||
@@ -979,7 +979,7 @@
|
||||
BUILTIN_OBJS += builtin/write-tree.o
|
||||
|
||||
GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB)
|
||||
-EXTLIBS =
|
||||
+EXTLIBS = -lnghttp2
|
||||
|
||||
GIT_USER_AGENT = git/$(GIT_VERSION)
|
||||
|
||||
@@ -1802,7 +1802,6 @@
|
||||
|
||||
$(BUILT_INS): git$X
|
||||
$(QUIET_BUILT_IN)$(RM) $@ && \
|
||||
- ln $< $@ 2>/dev/null || \
|
||||
ln -s $< $@ 2>/dev/null || \
|
||||
cp $< $@
|
||||
|
||||
@@ -2096,7 +2095,6 @@
|
||||
|
||||
$(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)
|
||||
$(QUIET_LNCP)$(RM) $@ && \
|
||||
- ln $< $@ 2>/dev/null || \
|
||||
ln -s $< $@ 2>/dev/null || \
|
||||
cp $< $@
|
||||
|
||||
@@ -2449,14 +2447,12 @@
|
||||
for p in $(filter $(install_bindir_programs),$(BUILT_INS)); do \
|
||||
$(RM) "$$bindir/$$p" && \
|
||||
test -z "$(NO_INSTALL_HARDLINKS)" && \
|
||||
- ln "$$bindir/git$X" "$$bindir/$$p" 2>/dev/null || \
|
||||
ln -s "git$X" "$$bindir/$$p" 2>/dev/null || \
|
||||
cp "$$bindir/git$X" "$$bindir/$$p" || exit; \
|
||||
done && \
|
||||
for p in $(BUILT_INS); do \
|
||||
$(RM) "$$execdir/$$p" && \
|
||||
test -z "$(NO_INSTALL_HARDLINKS)" && \
|
||||
- ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
|
||||
ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
|
||||
cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
|
||||
done && \
|
||||
@@ -2464,7 +2460,6 @@
|
||||
for p in $$remote_curl_aliases; do \
|
||||
$(RM) "$$execdir/$$p" && \
|
||||
test -z "$(NO_INSTALL_HARDLINKS)" && \
|
||||
- ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
|
||||
ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
|
||||
cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \
|
||||
done && \
|
||||
diff -ruwN git-2.13.1/run-command.c source/run-command.c
|
||||
--- git-2.13.1/run-command.c 2017-06-04 19:08:11.000000000 -0600
|
||||
+++ source/run-command.c 2025-04-18 10:00:11.320697447 -0600
|
||||
@@ -120,9 +120,9 @@
|
||||
#ifndef GIT_WINDOWS_NATIVE
|
||||
static inline void dup_devnull(int to)
|
||||
{
|
||||
- int fd = open("/dev/null", O_RDWR);
|
||||
+ int fd = open(DEV_NULL, O_RDWR);
|
||||
if (fd < 0)
|
||||
- die_errno(_("open /dev/null failed"));
|
||||
+ die_errno(_("open %s failed"), DEV_NULL);
|
||||
if (dup2(fd, to) < 0)
|
||||
die_errno(_("dup2(%d,%d) failed"), fd, to);
|
||||
close(fd);
|
||||
diff -ruwN git-2.13.1/setup.c source/setup.c
|
||||
--- git-2.13.1/setup.c 2017-06-05 08:08:11.000000000 +0700
|
||||
+++ source/setup.c 2025-09-01 04:41:10.339224568 +0700
|
||||
@@ -1146,11 +1146,11 @@
|
||||
/* if any standard file descriptor is missing open it to /dev/null */
|
||||
void sanitize_stdfds(void)
|
||||
{
|
||||
- int fd = open("/dev/null", O_RDWR, 0);
|
||||
+ int fd = open(DEV_NULL, O_RDWR, 0);
|
||||
while (fd != -1 && fd < 2)
|
||||
fd = dup(fd);
|
||||
if (fd == -1)
|
||||
- die_errno("open /dev/null or dup failed");
|
||||
+ die_errno("open %s or dup failed", DEV_NULL);
|
||||
if (fd > 2)
|
||||
close(fd);
|
||||
}
|
||||
@@ -1169,8 +1169,10 @@
|
||||
default:
|
||||
exit(0);
|
||||
}
|
||||
+#if !defined(__redox__)
|
||||
if (setsid() == -1)
|
||||
die_errno("setsid failed");
|
||||
+#endif
|
||||
close(0);
|
||||
close(1);
|
||||
close(2);
|
||||
@@ -1,31 +0,0 @@
|
||||
diff --git a/src/Makefile b/src/Makefile
|
||||
index 3a6a4329..450e8fe6 100644
|
||||
--- a/src/Makefile
|
||||
+++ b/src/Makefile
|
||||
@@ -351,6 +351,9 @@ else
|
||||
ifeq (GNU/kFreeBSD,$(TARGET_SYS))
|
||||
TARGET_XLIBS+= -ldl
|
||||
endif
|
||||
+ ifeq (Redox,$(TARGET_SYS))
|
||||
+ TARGET_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_POSIX
|
||||
+ endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -367,12 +370,16 @@ ifneq ($(HOST_SYS),$(TARGET_SYS))
|
||||
else
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX -DTARGET_OS_IPHONE=1
|
||||
+ else
|
||||
+ ifeq (Redox,$(TARGET_SYS))
|
||||
+ HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_POSIX
|
||||
else
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OTHER
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
+ endif
|
||||
endif
|
||||
|
||||
ifneq (,$(CCDEBUG))
|
||||
@@ -1,35 +0,0 @@
|
||||
diff -ru source/lib/getdtablesize.c source-new/lib/getdtablesize.c
|
||||
--- source/lib/getdtablesize.c 2015-03-06 16:31:45.000000000 -0800
|
||||
+++ source-new/lib/getdtablesize.c 2017-08-08 19:33:33.993874985 -0700
|
||||
@@ -106,15 +106,6 @@
|
||||
int
|
||||
getdtablesize (void)
|
||||
{
|
||||
- struct rlimit lim;
|
||||
-
|
||||
- if (getrlimit (RLIMIT_NOFILE, &lim) == 0
|
||||
- && 0 <= lim.rlim_cur && lim.rlim_cur <= INT_MAX
|
||||
- && lim.rlim_cur != RLIM_INFINITY
|
||||
- && lim.rlim_cur != RLIM_SAVED_CUR
|
||||
- && lim.rlim_cur != RLIM_SAVED_MAX)
|
||||
- return lim.rlim_cur;
|
||||
-
|
||||
return INT_MAX;
|
||||
}
|
||||
|
||||
Only in source-new/lib: getdtablesize.c.orig
|
||||
diff -ru source/src/safe.c source-new/src/safe.c
|
||||
--- source/src/safe.c 2015-03-06 16:34:20.000000000 -0800
|
||||
+++ source-new/src/safe.c 2017-08-08 19:33:53.447430811 -0700
|
||||
@@ -92,11 +92,7 @@
|
||||
|
||||
static void init_dirfd_cache (void)
|
||||
{
|
||||
- struct rlimit nofile;
|
||||
-
|
||||
max_cached_fds = 8;
|
||||
- if (getrlimit (RLIMIT_NOFILE, &nofile) == 0)
|
||||
- max_cached_fds = MAX (nofile.rlim_cur / 4, max_cached_fds);
|
||||
|
||||
cached_dirfds = hash_initialize (max_cached_fds,
|
||||
NULL,
|
||||
@@ -1,19 +0,0 @@
|
||||
diff -ru source/src/util.c source-new/src/util.c
|
||||
--- source/src/util.c 2015-03-06 16:34:20.000000000 -0800
|
||||
+++ source-new/src/util.c 2017-08-11 18:24:56.991729200 -0700
|
||||
@@ -271,6 +271,7 @@
|
||||
|
||||
/* May fail if we are not privileged to set the file owner, or we are
|
||||
not in group instat.st_gid. Ignore those errors. */
|
||||
+ /*
|
||||
if ((uid != -1 || gid != -1)
|
||||
&& safe_lchown (to, uid, gid) != 0
|
||||
&& (errno != EPERM
|
||||
@@ -281,6 +282,7 @@
|
||||
(uid == -1) ? "owner" : "owning group",
|
||||
S_ISLNK (mode) ? "symbolic link" : "file",
|
||||
quotearg (to));
|
||||
+ */
|
||||
}
|
||||
if (attr & FA_XATTRS)
|
||||
if (copy_attr (from, to) != 0
|
||||
@@ -1,15 +0,0 @@
|
||||
--- source-old/lib/renameat2.c 2018-02-03 05:41:53.000000000 -0700
|
||||
+++ source/lib/renameat2.c 2025-11-01 08:39:54.945513820 -0600
|
||||
@@ -70,6 +70,7 @@
|
||||
Obey FLAGS when doing the renaming. If FLAGS is zero, this
|
||||
function is equivalent to renameat (FD1, SRC, FD2, DST). */
|
||||
|
||||
+#if !defined(__redox__)
|
||||
int
|
||||
renameat2 (int fd1, char const *src, int fd2, char const *dst,
|
||||
unsigned int flags)
|
||||
@@ -225,3 +226,4 @@
|
||||
|
||||
#endif /* !HAVE_RENAMEAT */
|
||||
}
|
||||
+#endif
|
||||
@@ -1,91 +0,0 @@
|
||||
diff -ruwN source/configure source-new/configure
|
||||
--- source/configure 2026-01-14 00:17:10.000000000 +0700
|
||||
+++ source-new/configure 2026-01-16 15:56:01.944755811 +0700
|
||||
@@ -26007,7 +26007,7 @@
|
||||
then :
|
||||
ac_cv_lib_curl_curl_easy_perform=yes
|
||||
else case e in #(
|
||||
- e) ac_cv_lib_curl_curl_easy_perform=no ;;
|
||||
+ e) ac_cv_lib_curl_curl_easy_perform=yes ;;
|
||||
esac
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.beam \
|
||||
@@ -37728,7 +37728,7 @@
|
||||
then :
|
||||
php_cv_lib_gd_works=yes
|
||||
else case e in #(
|
||||
- e) php_cv_lib_gd_works=no ;;
|
||||
+ e) php_cv_lib_gd_works=yes ;;
|
||||
esac
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.beam \
|
||||
@@ -40464,7 +40464,7 @@
|
||||
LIBS_SAVED=$LIBS
|
||||
CFLAGS="$CFLAGS $GMP_CFLAGS"
|
||||
LIBS="$LIBS $GMP_LIBS"
|
||||
- gmp_check=no
|
||||
+ gmp_check=yes
|
||||
ac_fn_c_check_header_compile "$LINENO" "gmp.h" "ac_cv_header_gmp_h" "$ac_includes_default"
|
||||
if test "x$ac_cv_header_gmp_h" = xyes
|
||||
then :
|
||||
diff -ruwN source/ext/phar/Makefile.frag source-new/ext/phar/Makefile.frag
|
||||
--- source/ext/phar/Makefile.frag 2026-01-14 00:17:10.000000000 +0700
|
||||
+++ source-new/ext/phar/Makefile.frag 2026-01-16 15:56:01.946130660 +0700
|
||||
@@ -30,7 +30,7 @@
|
||||
-@test -f $(builddir)/phar/phar.inc || cp $(srcdir)/phar/phar.inc $(builddir)/phar/phar.inc
|
||||
|
||||
TEST_PHP_EXECUTABLE = $(shell $(PHP_EXECUTABLE) -v 2>&1)
|
||||
-TEST_PHP_EXECUTABLE_RES = $(shell echo "$(TEST_PHP_EXECUTABLE)" | grep -c 'Exec format error')
|
||||
+TEST_PHP_EXECUTABLE_RES = $(shell echo "$(TEST_PHP_EXECUTABLE)" | grep -E -c 'Exec format error|required file not found')
|
||||
|
||||
$(builddir)/phar.php: $(srcdir)/build_precommand.php $(srcdir)/phar/*.inc $(srcdir)/phar/*.php $(SAPI_CLI_PATH)
|
||||
-@(echo "Generating phar.php"; \
|
||||
diff -ruwN source/ext/posix/posix.c source-new/ext/posix/posix.c
|
||||
--- source/ext/posix/posix.c 2026-01-14 00:17:10.000000000 +0700
|
||||
+++ source-new/ext/posix/posix.c 2026-01-16 15:56:01.946290813 +0700
|
||||
@@ -375,7 +375,7 @@
|
||||
|
||||
ZEND_PARSE_PARAMETERS_NONE();
|
||||
|
||||
- if ((ticks = times(&t)) == -1) {
|
||||
+ {
|
||||
POSIX_G(last_error) = errno;
|
||||
RETURN_FALSE;
|
||||
}
|
||||
diff -ruwN source/sapi/fpm/fpm/fpm_status.c source-new/sapi/fpm/fpm/fpm_status.c
|
||||
--- source/sapi/fpm/fpm/fpm_status.c 2026-01-14 00:17:10.000000000 +0700
|
||||
+++ source-new/sapi/fpm/fpm/fpm_status.c 2026-01-16 15:57:37.781307156 +0700
|
||||
@@ -84,6 +84,7 @@
|
||||
continue;
|
||||
}
|
||||
/* prevent NaN */
|
||||
+#ifdef HAVE_TIMES
|
||||
if (proc_p->cpu_duration.tv_sec == 0 && proc_p->cpu_duration.tv_usec == 0) {
|
||||
cpu = 0.;
|
||||
} else {
|
||||
@@ -91,6 +92,9 @@
|
||||
proc_p->last_request_cpu.tms_cstime) / fpm_scoreboard_get_tick() /
|
||||
(proc_p->cpu_duration.tv_sec + proc_p->cpu_duration.tv_usec / 1000000.) * 100.;
|
||||
}
|
||||
+#else
|
||||
+ cpu = 0.;
|
||||
+#endif
|
||||
|
||||
array_init(&fpm_proc_stat);
|
||||
add_assoc_long(&fpm_proc_stat, "pid", proc_p->pid);
|
||||
@@ -573,11 +577,15 @@
|
||||
}
|
||||
|
||||
/* prevent NaN */
|
||||
+#ifdef HAVE_TIMES
|
||||
if (proc->cpu_duration.tv_sec == 0 && proc->cpu_duration.tv_usec == 0) {
|
||||
cpu = 0.;
|
||||
} else {
|
||||
cpu = (proc->last_request_cpu.tms_utime + proc->last_request_cpu.tms_stime + proc->last_request_cpu.tms_cutime + proc->last_request_cpu.tms_cstime) / fpm_scoreboard_get_tick() / (proc->cpu_duration.tv_sec + proc->cpu_duration.tv_usec / 1000000.) * 100.;
|
||||
}
|
||||
+#else
|
||||
+ cpu = 0.;
|
||||
+#endif
|
||||
|
||||
if (proc->request_stage == FPM_REQUEST_ACCEPTING) {
|
||||
duration = proc->duration;
|
||||
@@ -1,152 +0,0 @@
|
||||
diff -ruwN source/configure source-new/configure
|
||||
--- source/configure 2025-10-09 18:07:00.000000000 +0700
|
||||
+++ source-new/configure 2025-12-09 22:14:30.781035339 +0700
|
||||
@@ -4283,6 +4283,9 @@
|
||||
*-*-wasi)
|
||||
ac_sys_system=WASI
|
||||
;;
|
||||
+ *-*-redox*)
|
||||
+ ac_sys_system=Redox
|
||||
+ ;;
|
||||
*)
|
||||
# for now, limit cross builds to known configurations
|
||||
MACHDEP="unknown"
|
||||
@@ -4307,6 +4310,7 @@
|
||||
case $MACHDEP in
|
||||
aix*) MACHDEP="aix";;
|
||||
linux*) MACHDEP="linux";;
|
||||
+ redox*) MACHDEP="redox";;
|
||||
cygwin*) MACHDEP="cygwin";;
|
||||
darwin*) MACHDEP="darwin";;
|
||||
'') MACHDEP="unknown";;
|
||||
@@ -4327,7 +4331,7 @@
|
||||
|
||||
if test "$cross_compiling" = yes; then
|
||||
case "$host" in
|
||||
- *-*-linux*)
|
||||
+ *-*-linux*|*-*-redox*)
|
||||
case "$host_cpu" in
|
||||
arm*)
|
||||
_host_cpu=arm
|
||||
@@ -6762,6 +6766,7 @@
|
||||
#undef cris
|
||||
#undef fr30
|
||||
#undef linux
|
||||
+#undef redox
|
||||
#undef hppa
|
||||
#undef hpux
|
||||
#undef i386
|
||||
@@ -6907,6 +6912,18 @@
|
||||
# endif
|
||||
#elif defined(__gnu_hurd__)
|
||||
i386-gnu
|
||||
+#elif defined(__redox__)
|
||||
+# if defined(__x86_64__)
|
||||
+ x86_64-redox
|
||||
+# elif defined(__i386__)
|
||||
+ i386-redox
|
||||
+# elif defined(__aarch64__)
|
||||
+ aarch64-redox
|
||||
+# elif defined(__riscv)
|
||||
+ riscv64-redox
|
||||
+# else
|
||||
+# error unknown platform triplet
|
||||
+# endif
|
||||
#elif defined(__APPLE__)
|
||||
darwin
|
||||
#elif defined(__VXWORKS__)
|
||||
@@ -7507,7 +7524,7 @@
|
||||
PY3LIBRARY=libpython3.so
|
||||
fi
|
||||
;;
|
||||
- Linux*|GNU*|NetBSD*|FreeBSD*|DragonFly*|OpenBSD*|VxWorks*)
|
||||
+ Linux*|GNU*|NetBSD*|FreeBSD*|DragonFly*|OpenBSD*|VxWorks*|Redox*)
|
||||
LDLIBRARY='libpython$(LDVERSION).so'
|
||||
BLDLIBRARY='-L. -lpython$(LDVERSION)'
|
||||
RUNSHARED=LD_LIBRARY_PATH=`pwd`${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
|
||||
@@ -12815,7 +12832,7 @@
|
||||
Emscripten*|WASI*)
|
||||
LDSHARED='$(CC) -shared'
|
||||
LDCXXSHARED='$(CXX) -shared';;
|
||||
- Linux*|GNU*|QNX*|VxWorks*|Haiku*)
|
||||
+ Linux*|GNU*|QNX*|VxWorks*|Haiku*|Redox*)
|
||||
LDSHARED='$(CC) -shared'
|
||||
LDCXXSHARED='$(CXX) -shared';;
|
||||
FreeBSD*)
|
||||
@@ -12901,7 +12918,7 @@
|
||||
else CCSHARED="+z";
|
||||
fi;;
|
||||
Linux-android*) ;;
|
||||
- Linux*|GNU*) CCSHARED="-fPIC";;
|
||||
+ Linux*|GNU*|Redox*) CCSHARED="-fPIC";;
|
||||
Emscripten*|WASI*)
|
||||
if test "x$enable_wasm_dynamic_linking" = xyes
|
||||
then :
|
||||
@@ -12939,7 +12956,7 @@
|
||||
LINKFORSHARED="-Wl,-E -Wl,+s";;
|
||||
# LINKFORSHARED="-Wl,-E -Wl,+s -Wl,+b\$(BINLIBDEST)/lib-dynload";;
|
||||
Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";;
|
||||
- Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";;
|
||||
+ Linux*|GNU*|Redox*) LINKFORSHARED="-Xlinker -export-dynamic";;
|
||||
# -u libsys_s pulls in all symbols in libsys
|
||||
Darwin/*)
|
||||
LINKFORSHARED="$extra_undefs -framework CoreFoundation"
|
||||
diff -ruwN source/Include/pyport.h source-new/Include/pyport.h
|
||||
--- source/Include/pyport.h 2025-10-09 18:07:00.000000000 +0700
|
||||
+++ source-new/Include/pyport.h 2025-12-09 22:14:30.781035339 +0700
|
||||
@@ -684,7 +684,7 @@
|
||||
# error "Py_TRACE_REFS ABI is not compatible with release and debug ABI"
|
||||
#endif
|
||||
|
||||
-#if defined(__ANDROID__) || defined(__VXWORKS__)
|
||||
+#if defined(__ANDROID__) || defined(__VXWORKS__) || defined(__redox__)
|
||||
// Use UTF-8 as the locale encoding, ignore the LC_CTYPE locale.
|
||||
// See _Py_GetLocaleEncoding(), PyUnicode_DecodeLocale()
|
||||
// and PyUnicode_EncodeLocale().
|
||||
diff -ruwN source/Modules/_cryptmodule.c source-new/Modules/_cryptmodule.c
|
||||
--- source/Modules/_cryptmodule.c 2025-10-09 18:07:00.000000000 +0700
|
||||
+++ source-new/Modules/_cryptmodule.c 2025-12-09 22:14:30.781035339 +0700
|
||||
@@ -38,13 +38,7 @@
|
||||
/*[clinic end generated code: output=0512284a03d2803c input=0e8edec9c364352b]*/
|
||||
{
|
||||
char *crypt_result;
|
||||
-#ifdef HAVE_CRYPT_R
|
||||
- struct crypt_data data;
|
||||
- memset(&data, 0, sizeof(data));
|
||||
- crypt_result = crypt_r(word, salt, &data);
|
||||
-#else
|
||||
crypt_result = crypt(word, salt);
|
||||
-#endif
|
||||
if (crypt_result == NULL) {
|
||||
return PyErr_SetFromErrno(PyExc_OSError);
|
||||
}
|
||||
diff -ruwN source/Modules/resource.c source-new/Modules/resource.c
|
||||
--- source/Modules/resource.c 2025-10-09 18:07:00.000000000 +0700
|
||||
+++ source-new/Modules/resource.c 2025-12-09 22:14:30.781035339 +0700
|
||||
@@ -216,7 +216,7 @@
|
||||
{
|
||||
struct rlimit rl;
|
||||
|
||||
- if (resource < 0 || resource >= RLIM_NLIMITS) {
|
||||
+ if (resource < 0 || resource >= RLIMIT_NLIMITS) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"invalid resource specified");
|
||||
return NULL;
|
||||
@@ -244,7 +244,7 @@
|
||||
{
|
||||
struct rlimit rl;
|
||||
|
||||
- if (resource < 0 || resource >= RLIM_NLIMITS) {
|
||||
+ if (resource < 0 || resource >= RLIMIT_NLIMITS) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"invalid resource specified");
|
||||
return NULL;
|
||||
@@ -292,7 +292,7 @@
|
||||
struct rlimit old_limit, new_limit;
|
||||
int retval;
|
||||
|
||||
- if (resource < 0 || resource >= RLIM_NLIMITS) {
|
||||
+ if (resource < 0 || resource >= RLIMIT_NLIMITS) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"invalid resource specified");
|
||||
return NULL;
|
||||
@@ -1,22 +0,0 @@
|
||||
diff --git a/stdlib/src/posixsubprocess.rs b/stdlib/src/posixsubprocess.rs
|
||||
index 7f418c899..4da6a6858 100644
|
||||
--- a/stdlib/src/posixsubprocess.rs
|
||||
+++ b/stdlib/src/posixsubprocess.rs
|
||||
@@ -441,15 +441,14 @@ fn close_dir_fds(keep: KeepFds<'_>) -> nix::Result<()> {
|
||||
fn close_filetable_fds(keep: KeepFds<'_>) -> nix::Result<()> {
|
||||
use nix::fcntl;
|
||||
use std::os::fd::{FromRawFd, OwnedFd};
|
||||
- let fd = fcntl::open(
|
||||
+ let filetable = fcntl::open(
|
||||
c"/scheme/thisproc/current/filetable",
|
||||
fcntl::OFlag::O_RDONLY,
|
||||
nix::sys::stat::Mode::empty(),
|
||||
)?;
|
||||
- let filetable = unsafe { OwnedFd::from_raw_fd(fd) };
|
||||
let read_one = || -> nix::Result<_> {
|
||||
let mut byte = 0;
|
||||
- let n = nix::unistd::read(filetable.as_raw_fd(), std::slice::from_mut(&mut byte))?;
|
||||
+ let n = nix::unistd::read(&filetable, std::slice::from_mut(&mut byte))?;
|
||||
Ok((n > 0).then_some(byte))
|
||||
};
|
||||
while let Some(c) = read_one()? {
|
||||
@@ -1,24 +0,0 @@
|
||||
diff -rupNw source-original/include/cross.h source/include/cross.h
|
||||
--- source-original/include/cross.h 2010-05-10 19:43:54.000000000 +0200
|
||||
+++ source/include/cross.h 2018-05-04 21:14:40.397157710 +0200
|
||||
@@ -64,7 +64,7 @@
|
||||
//Solaris maybe others
|
||||
#if defined (DB_HAVE_NO_POWF)
|
||||
#include <math.h>
|
||||
-static inline float powf (float x, float y) { return (float) pow (x,y); }
|
||||
+//static inline float powf (float x, float y) { return (float) pow (x,y); }
|
||||
#endif
|
||||
|
||||
class Cross {
|
||||
diff -rupNw source-original/src/gui/sdlmain.cpp source/src/gui/sdlmain.cpp
|
||||
--- source-original/src/gui/sdlmain.cpp 2010-05-10 19:43:54.000000000 +0200
|
||||
+++ source/src/gui/sdlmain.cpp 2018-05-04 21:15:31.937672555 +0200
|
||||
@@ -1518,7 +1518,7 @@ void Config_Add_SDL() {
|
||||
Pstring = sdl_sec->Add_path("mapperfile",Property::Changeable::Always,MAPPERFILE);
|
||||
Pstring->Set_help("File used to load/save the key/event mappings from. Resetmapper only works with the defaul value.");
|
||||
|
||||
- Pbool = sdl_sec->Add_bool("usescancodes",Property::Changeable::Always,true);
|
||||
+ Pbool = sdl_sec->Add_bool("usescancodes",Property::Changeable::Always,false);
|
||||
Pbool->Set_help("Avoid usage of symkeys, might not work on all operating systems.");
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
diff -ruwN source-old/src/sound/SwiftResampler.cpp source/src/sound/SwiftResampler.cpp
|
||||
--- source-old/src/sound/SwiftResampler.cpp 2022-01-18 14:16:23.000000000 -0700
|
||||
+++ source/src/sound/SwiftResampler.cpp 2022-12-16 20:01:02.263159230 -0700
|
||||
@@ -608,6 +608,9 @@
|
||||
|
||||
if(hp_tc > 0)
|
||||
{
|
||||
+#ifndef M_E
|
||||
+#define M_E 2.7182818284590452354
|
||||
+#endif
|
||||
double tdm = (pow(2.0 - pow(M_E, -1.0), 1.0 / (hp_tc * output_rate)) - 1.0);
|
||||
|
||||
//printf("%f\n", tdm);
|
||||
@@ -1,12 +0,0 @@
|
||||
diff -ruwN mgba-0.10.5/src/third-party/zlib/contrib/minizip/ioapi.h source/src/third-party/zlib/contrib/minizip/ioapi.h
|
||||
--- mgba-0.10.5/src/third-party/zlib/contrib/minizip/ioapi.h 2025-03-08 20:09:26.000000000 -0700
|
||||
+++ source/src/third-party/zlib/contrib/minizip/ioapi.h 2025-06-13 13:07:13.489517096 -0600
|
||||
@@ -50,7 +50,7 @@
|
||||
#define ftello64 ftell
|
||||
#define fseeko64 fseek
|
||||
#else
|
||||
-#ifdef __FreeBSD__
|
||||
+#if defined(__FreeBSD__) || defined(__redox__)
|
||||
#define fopen64 fopen
|
||||
#define ftello64 ftello
|
||||
#define fseeko64 fseeko
|
||||
@@ -1,24 +0,0 @@
|
||||
diff -rupNw source-original/common/stream.cpp source/common/stream.cpp
|
||||
--- source-original/common/stream.cpp 2017-12-08 23:21:10.000000000 +0100
|
||||
+++ source/common/stream.cpp 2018-12-06 02:01:50.454108198 +0100
|
||||
@@ -95,7 +95,7 @@ bool MemoryReadStream::seek(int32 offs,
|
||||
break;
|
||||
}
|
||||
// Post-Condition
|
||||
- assert(_pos <= _size);
|
||||
+ //assert(_pos <= _size);
|
||||
|
||||
// Reset end-of-stream flag on a successful seek
|
||||
_eos = false;
|
||||
diff -rupNw source-original/configure source/configure
|
||||
--- source-original/configure 2017-12-08 23:21:13.000000000 +0100
|
||||
+++ source/configure 2018-12-06 02:01:50.458108239 +0100
|
||||
@@ -3610,7 +3610,7 @@ case $_host_os in
|
||||
amigaos* | cygwin* | dreamcast | ds | gamecube | mingw* | n64 | ps2 | ps3 | psp2 | psp | wii | wince)
|
||||
_posix=no
|
||||
;;
|
||||
- 3ds | android | androidsdl | beos* | bsd* | darwin* | freebsd* | gnu* | gph-linux | haiku* | hpux* | iphone | ios7 | irix*| k*bsd*-gnu* | linux* | maemo | mint* | netbsd* | openbsd* | riscos | solaris* | sunos* | uclinux* | webos)
|
||||
+ 3ds | android | androidsdl | beos* | bsd* | darwin* | freebsd* | gnu* | gph-linux | haiku* | hpux* | iphone | ios7 | irix*| k*bsd*-gnu* | linux* | maemo | mint* | netbsd* | openbsd* | riscos | redox* | solaris* | sunos* | uclinux* | webos)
|
||||
_posix=yes
|
||||
;;
|
||||
os2-emx*)
|
||||
@@ -1,158 +0,0 @@
|
||||
diff -rupwN source/Common.mak source-new/Common.mak
|
||||
--- source/Common.mak 2018-07-14 15:36:44.000000000 -0600
|
||||
+++ source-new/Common.mak 2023-01-20 10:38:24.948044222 -0700
|
||||
@@ -93,7 +93,7 @@ endif
|
||||
|
||||
##### Makefile meta-settings
|
||||
|
||||
-PRETTY_OUTPUT := 1
|
||||
+PRETTY_OUTPUT := 0
|
||||
|
||||
NULLSTREAM := /dev/null
|
||||
|
||||
@@ -172,6 +172,10 @@ ifeq ($(PLATFORM),WII)
|
||||
CCFULLPATH = $(DEVKITPPC)/bin/$(CC)
|
||||
endif
|
||||
|
||||
+ifeq ($(PLATFORM),REDOX)
|
||||
+ CROSS := $(TARGET)-
|
||||
+endif
|
||||
+
|
||||
CC := $(CROSS)gcc$(CROSS_SUFFIX)
|
||||
CXX := $(CROSS)g++$(CROSS_SUFFIX)
|
||||
|
||||
@@ -383,6 +387,16 @@ else ifeq ($(PLATFORM),$(filter $(PLATFO
|
||||
override NOASM := 1
|
||||
else ifeq ($(PLATFORM),$(filter $(PLATFORM),BEOS SKYOS))
|
||||
override NOASM := 1
|
||||
+else ifeq ($(PLATFORM),REDOX)
|
||||
+ override HAVE_FLAC := 0
|
||||
+ override HAVE_GTK2 := 0
|
||||
+ override HAVE_XMP := 0
|
||||
+ override MIXERTYPE := SDL
|
||||
+ override NETCODE := 0
|
||||
+ override NOASM := 1
|
||||
+ override USE_OPENGL := 0
|
||||
+ OPTOPT := -mtune=generic
|
||||
+ SDL_TARGET := 1
|
||||
endif
|
||||
|
||||
ifneq (i386,$(strip $(IMPLICIT_ARCH)))
|
||||
@@ -868,7 +882,7 @@ ifeq ($(RENDERTYPE),SDL)
|
||||
SDLCONFIG := sdl2-config
|
||||
SDLNAME := SDL2
|
||||
else ifeq ($(SDL_TARGET),1)
|
||||
- SDLCONFIG := sdl-config
|
||||
+ #SDLCONFIG := sdl-config
|
||||
SDLNAME := SDL
|
||||
ifeq (0,$(RELEASE))
|
||||
COMPILERFLAGS += -DNOSDLPARACHUTE
|
||||
@@ -957,9 +971,11 @@ else ifeq ($(PLATFORM),WII)
|
||||
LIBS += -laesnd_tueidj -lfat -lwiiuse -lbte -lwiikeyboard -logc
|
||||
else ifeq ($(SUBPLATFORM),LINUX)
|
||||
LIBS += -lrt
|
||||
+else ifeq ($(PLATFORM),REDOX)
|
||||
+ LIBS += -lorbital -lvorbisfile -lvorbis -logg
|
||||
endif
|
||||
|
||||
-ifeq (,$(filter $(PLATFORM),WINDOWS WII))
|
||||
+ifeq (,$(filter $(PLATFORM),WINDOWS WII REDOX))
|
||||
ifneq ($(PLATFORM),BSD)
|
||||
LIBS += -ldl
|
||||
endif
|
||||
Binary files source/.Common.mak.swp and source-new/.Common.mak.swp differ
|
||||
diff -rupwN source/source/build/include/compat.h source-new/source/build/include/compat.h
|
||||
--- source/source/build/include/compat.h 2018-10-06 23:21:24.000000000 -0600
|
||||
+++ source-new/source/build/include/compat.h 2023-01-20 10:31:10.843745693 -0700
|
||||
@@ -7,6 +7,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
+# define B_LITTLE_ENDIAN 1
|
||||
+# define B_BIG_ENDIAN 0
|
||||
+
|
||||
#ifdef _WIN32
|
||||
# include "windows_inc.h"
|
||||
#endif
|
||||
@@ -400,6 +403,7 @@ defined __x86_64__ || defined __amd64__
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
+#include <strings.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
@@ -542,8 +546,8 @@ typedef FILE BFILE;
|
||||
# define BS_IWRITE S_IWUSR
|
||||
# define BS_IREAD S_IRUSR
|
||||
#else
|
||||
-# define BS_IWRITE S_IWRITE
|
||||
-# define BS_IREAD S_IREAD
|
||||
+# define BS_IWRITE S_IWUSR
|
||||
+# define BS_IREAD S_IRUSR
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus) && defined(_MSC_VER)
|
||||
diff -rupwN source/source/build/src/baselayer.cpp source-new/source/build/src/baselayer.cpp
|
||||
--- source/source/build/src/baselayer.cpp 2018-10-06 23:21:43.000000000 -0600
|
||||
+++ source-new/source/build/src/baselayer.cpp 2023-01-20 10:31:49.591772332 -0700
|
||||
@@ -498,7 +498,7 @@ int32_t baselayer_init(void)
|
||||
|
||||
void maybe_redirect_outputs(void)
|
||||
{
|
||||
-#if !(defined __APPLE__ && defined __BIG_ENDIAN__)
|
||||
+#if 0
|
||||
char *argp;
|
||||
|
||||
// pipe standard outputs to files
|
||||
diff -rupwN source/source/build/src/sdlayer.cpp source-new/source/build/src/sdlayer.cpp
|
||||
--- source/source/build/src/sdlayer.cpp 2018-10-06 23:23:44.000000000 -0600
|
||||
+++ source-new/source/build/src/sdlayer.cpp 2023-01-20 10:30:49.223730830 -0700
|
||||
@@ -305,7 +305,7 @@ void wm_setapptitle(const char *name)
|
||||
//
|
||||
|
||||
/* XXX: libexecinfo could be used on systems without gnu libc. */
|
||||
-#if !defined _WIN32 && defined __GNUC__ && !defined __OpenBSD__ && !(defined __APPLE__ && defined __BIG_ENDIAN__) && !defined GEKKO && !defined EDUKE32_TOUCH_DEVICES && !defined __OPENDINGUX__
|
||||
+#if 0
|
||||
# define PRINTSTACKONSEGV 1
|
||||
# include <execinfo.h>
|
||||
#endif
|
||||
diff -rupwN source/source/duke3d/src/common.cpp source-new/source/duke3d/src/common.cpp
|
||||
--- source/source/duke3d/src/common.cpp 2018-10-06 23:20:23.000000000 -0600
|
||||
+++ source-new/source/duke3d/src/common.cpp 2023-01-20 10:30:49.223730830 -0700
|
||||
@@ -1173,6 +1173,7 @@ int32_t S_OpenAudio(const char *fn, char
|
||||
Bfree(testfn);
|
||||
return origfp;
|
||||
}
|
||||
+#endif
|
||||
|
||||
void Duke_CommonCleanup(void)
|
||||
{
|
||||
@@ -1181,4 +1182,3 @@ void Duke_CommonCleanup(void)
|
||||
DO_FREE_AND_NULL(g_rtsNamePtr);
|
||||
}
|
||||
|
||||
-#endif
|
||||
diff -rupwN source/source/duke3d/src/game.cpp source-new/source/duke3d/src/game.cpp
|
||||
--- source/source/duke3d/src/game.cpp 2018-10-06 23:23:48.000000000 -0600
|
||||
+++ source-new/source/duke3d/src/game.cpp 2023-01-20 10:30:49.223730830 -0700
|
||||
@@ -6697,7 +6697,7 @@ MAIN_LOOP_RESTART:
|
||||
static char buf[128];
|
||||
#ifndef GEKKO
|
||||
int32_t flag = 1;
|
||||
- ioctl(0, FIONBIO, &flag);
|
||||
+ //ioctl(0, FIONBIO, &flag);
|
||||
#endif
|
||||
if ((nb = read(0, &ch, 1)) > 0 && bufpos < sizeof(buf))
|
||||
{
|
||||
diff -rupwN source/source/enet/include/enet/unix.h source-new/source/enet/include/enet/unix.h
|
||||
--- source/source/enet/include/enet/unix.h 2014-06-16 17:16:08.000000000 -0600
|
||||
+++ source-new/source/enet/include/enet/unix.h 2023-01-20 10:30:49.223730830 -0700
|
||||
@@ -6,6 +6,7 @@
|
||||
#define __ENET_UNIX_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
+#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#if defined(GEKKO)
|
||||
@@ -1,87 +0,0 @@
|
||||
diff -ruwN neverball-1.6.0/Makefile source/Makefile
|
||||
--- neverball-1.6.0/Makefile 2014-05-21 07:21:43.000000000 -0600
|
||||
+++ source/Makefile 2023-09-09 20:03:22.113348963 -0600
|
||||
@@ -38,11 +38,11 @@
|
||||
ifeq ($(DEBUG),1)
|
||||
CFLAGS := -g
|
||||
CXXFLAGS := -g
|
||||
- CPPFLAGS :=
|
||||
+ CPPFLAGS +=
|
||||
else
|
||||
CFLAGS := -O2
|
||||
CXXFLAGS := -O2
|
||||
- CPPFLAGS := -DNDEBUG
|
||||
+ CPPFLAGS += -DNDEBUG
|
||||
endif
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
@@ -64,8 +64,8 @@
|
||||
|
||||
# Preprocessor...
|
||||
|
||||
-SDL_CPPFLAGS := $(shell sdl2-config --cflags)
|
||||
-PNG_CPPFLAGS := $(shell libpng-config --cflags)
|
||||
+SDL_CPPFLAGS := $(shell $(PKG_CONFIG) sdl2 --cflags)
|
||||
+PNG_CPPFLAGS := $(shell $(PKG_CONFIG) libpng --cflags)
|
||||
|
||||
ALL_CPPFLAGS := $(SDL_CPPFLAGS) $(PNG_CPPFLAGS) -Ishare
|
||||
|
||||
@@ -124,8 +124,8 @@
|
||||
#------------------------------------------------------------------------------
|
||||
# Libraries
|
||||
|
||||
-SDL_LIBS := $(shell sdl2-config --libs)
|
||||
-PNG_LIBS := $(shell libpng-config --libs)
|
||||
+SDL_LIBS := $(shell $(PKG_CONFIG) sdl2 --libs)
|
||||
+PNG_LIBS := $(shell $(PKG_CONFIG) libpng --libs)
|
||||
|
||||
ifeq ($(ENABLE_FS),stdio)
|
||||
FS_LIBS :=
|
||||
@@ -148,7 +148,7 @@
|
||||
endif
|
||||
endif
|
||||
|
||||
-OGL_LIBS := -lGL
|
||||
+OGL_LIBS := -lorbital $(shell $(PKG_CONFIG) osmesa --libs)
|
||||
|
||||
ifeq ($(PLATFORM),mingw)
|
||||
ifneq ($(ENABLE_NLS),0)
|
||||
@@ -175,8 +175,8 @@
|
||||
/usr/local/lib))
|
||||
endif
|
||||
|
||||
-OGG_LIBS := -lvorbisfile
|
||||
-TTF_LIBS := -lSDL2_ttf
|
||||
+OGG_LIBS := $(shell $(PKG_CONFIG) ogg vorbis vorbisfile --libs)
|
||||
+TTF_LIBS := $(shell $(PKG_CONFIG) SDL2_ttf --libs) -lfreetype
|
||||
|
||||
ALL_LIBS := $(HMD_LIBS) $(TILT_LIBS) $(INTL_LIBS) $(TTF_LIBS) \
|
||||
$(OGG_LIBS) $(SDL_LIBS) $(OGL_LIBS) $(BASE_LIBS)
|
||||
@@ -411,11 +411,11 @@
|
||||
|
||||
all : $(BALL_TARG) $(PUTT_TARG) $(MAPC_TARG) sols locales desktops
|
||||
|
||||
-ifeq ($(ENABLE_HMD),libovr)
|
||||
+#ifeq ($(ENABLE_HMD),libovr)
|
||||
LINK := $(CXX) $(ALL_CXXFLAGS)
|
||||
-else
|
||||
-LINK := $(CC) $(ALL_CFLAGS)
|
||||
-endif
|
||||
+#else
|
||||
+#LINK := $(CC) $(ALL_CFLAGS)
|
||||
+#endif
|
||||
|
||||
$(BALL_TARG) : $(BALL_OBJS)
|
||||
$(LINK) -o $(BALL_TARG) $(BALL_OBJS) $(LDFLAGS) $(ALL_LIBS)
|
||||
diff -ruwN neverball-1.6.0/share/text.h source/share/text.h
|
||||
--- neverball-1.6.0/share/text.h 2014-05-21 07:21:43.000000000 -0600
|
||||
+++ source/share/text.h 2023-09-09 20:02:10.117248865 -0600
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
-char text_input[MAXSTR];
|
||||
+extern char text_input[MAXSTR];
|
||||
|
||||
void text_input_start(void (*cb)(int typing));
|
||||
void text_input_stop(void);
|
||||
@@ -1,116 +0,0 @@
|
||||
diff -ruw source/config.lib source-new/config.lib
|
||||
--- source/config.lib 2019-06-19 08:34:01.122040101 -0600
|
||||
+++ source-new/config.lib 2019-06-27 16:41:18.749553078 -0600
|
||||
@@ -1458,7 +1458,8 @@
|
||||
fi
|
||||
fi
|
||||
|
||||
- has_rdynamic=`$1 -dumpspecs | grep rdynamic`
|
||||
+ #TODO has_rdynamic=`$1 -dumpspecs | grep rdynamic`
|
||||
+ has_rdynamic=""
|
||||
if [ -n "$has_rdynamic" ]; then
|
||||
# rdynamic is used to get useful stack traces from crash reports.
|
||||
flags="$flags -rdynamic"
|
||||
@@ -1486,7 +1487,7 @@
|
||||
# Special CXXFlags for HOST
|
||||
CXXFLAGS="$CXXFLAGS"
|
||||
# Libs to compile. In fact this is just LDFLAGS
|
||||
- LIBS="-lstdc++"
|
||||
+ LIBS="-lstdc++ -lSDL -lorbital"
|
||||
# LDFLAGS used for HOST
|
||||
LDFLAGS="$LDFLAGS"
|
||||
# FEATURES for HOST (lto)
|
||||
diff -ruw source/src/music/extmidi.cpp source-new/src/music/extmidi.cpp
|
||||
--- source/src/music/extmidi.cpp 2019-06-19 08:34:01.278040813 -0600
|
||||
+++ source-new/src/music/extmidi.cpp 2019-06-27 16:39:06.400266392 -0600
|
||||
@@ -115,7 +115,11 @@
|
||||
switch (this->pid) {
|
||||
case 0: {
|
||||
close(0);
|
||||
+#if defined(__redox__)
|
||||
+ int d = open("null:", O_RDONLY);
|
||||
+#else
|
||||
int d = open("/dev/null", O_RDONLY);
|
||||
+#endif
|
||||
if (d != -1 && dup2(d, 1) != -1 && dup2(d, 2) != -1) {
|
||||
execvp(this->params[0], this->params);
|
||||
}
|
||||
diff -ruw source/src/os/unix/unix.cpp source-new/src/os/unix/unix.cpp
|
||||
--- source/src/os/unix/unix.cpp 2019-06-19 08:34:01.294040885 -0600
|
||||
+++ source-new/src/os/unix/unix.cpp 2019-06-27 16:39:06.400266392 -0600
|
||||
@@ -69,12 +69,12 @@
|
||||
|
||||
bool FiosIsRoot(const char *path)
|
||||
{
|
||||
-#if !defined(__MORPHOS__) && !defined(__AMIGAOS__)
|
||||
+#if !defined(__redox__)
|
||||
return path[1] == '\0';
|
||||
#else
|
||||
- /* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
|
||||
+ /* On Redox paths look like: "scheme:/directory/subdirectory" */
|
||||
const char *s = strchr(path, ':');
|
||||
- return s != NULL && s[1] == '\0';
|
||||
+ return (s != NULL) && (strlen(s) == 2) && (s[1] == '/') && (s[2] == '\0');
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -106,10 +106,10 @@
|
||||
{
|
||||
char filename[MAX_PATH];
|
||||
int res;
|
||||
-#if defined(__MORPHOS__) || defined(__AMIGAOS__)
|
||||
- /* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
|
||||
+#if defined(__redox__)
|
||||
+ /* On Redox paths look like: "scheme:/directory/subdirectory" */
|
||||
if (FiosIsRoot(path)) {
|
||||
- res = seprintf(filename, lastof(filename), "%s:%s", path, ent->d_name);
|
||||
+ res = seprintf(filename, lastof(filename), "%s%s", path, ent->d_name);
|
||||
} else // XXX - only next line!
|
||||
#else
|
||||
assert(path[strlen(path) - 1] == PATHSEPCHAR);
|
||||
@@ -370,7 +370,7 @@
|
||||
if (child_pid != 0) return;
|
||||
|
||||
const char *args[3];
|
||||
- args[0] = "xdg-open";
|
||||
+ args[0] = "netsurf-fb";
|
||||
args[1] = url;
|
||||
args[2] = NULL;
|
||||
execvp(args[0], const_cast<char * const *>(args));
|
||||
diff -ruw source/src/rev.cpp.in source-new/src/rev.cpp.in
|
||||
--- source/src/rev.cpp.in 2019-06-19 08:34:01.298040904 -0600
|
||||
+++ source-new/src/rev.cpp.in 2019-06-27 16:39:06.400266392 -0600
|
||||
@@ -57,7 +57,7 @@
|
||||
* (compiling from sources without any version control software)
|
||||
* and 2 is for modified revision.
|
||||
*/
|
||||
-const byte _openttd_revision_modified = !!MODIFIED!!;
|
||||
+const byte _openttd_revision_modified = 2;
|
||||
|
||||
/**
|
||||
* The NewGRF revision of OTTD:
|
||||
diff -ruw source/src/stdafx.h source-new/src/stdafx.h
|
||||
--- source/src/stdafx.h 2019-06-19 08:34:01.334041067 -0600
|
||||
+++ source-new/src/stdafx.h 2019-06-27 16:39:06.400266392 -0600
|
||||
@@ -12,6 +12,9 @@
|
||||
#ifndef STDAFX_H
|
||||
#define STDAFX_H
|
||||
|
||||
+#include <strings.h>
|
||||
+#include <alloca.h>
|
||||
+
|
||||
#if defined(__APPLE__)
|
||||
#include "os/macosx/osx_stdafx.h"
|
||||
#endif /* __APPLE__ */
|
||||
diff -ruw source/src/string.cpp source-new/src/string.cpp
|
||||
--- source/src/string.cpp 2019-06-19 08:34:01.334041067 -0600
|
||||
+++ source-new/src/string.cpp 2019-06-27 16:39:06.400266392 -0600
|
||||
@@ -528,7 +528,7 @@
|
||||
return length;
|
||||
}
|
||||
|
||||
-#ifdef DEFINE_STRCASESTR
|
||||
+#if 0
|
||||
char *strcasestr(const char *haystack, const char *needle)
|
||||
{
|
||||
size_t hay_len = strlen(haystack);
|
||||
@@ -1,60 +0,0 @@
|
||||
Binary files source/.git/index and source-new/.git/index differ
|
||||
diff '--color=auto' -rupwN source/Makefile source-new/Makefile
|
||||
--- source/Makefile 2024-10-21 02:46:06.720225834 -0400
|
||||
+++ source-new/Makefile 2024-10-25 01:03:37.283351544 -0400
|
||||
@@ -5,10 +5,11 @@ ifneq ($(filter Msys Cygwin, $(shell una
|
||||
TYRIAN_DIR = C:\\TYRIAN
|
||||
else
|
||||
PLATFORM := UNIX
|
||||
- TYRIAN_DIR = $(gamesdir)/tyrian
|
||||
+ TYRIAN_DIR ?= $(gamesdir)/tyrian
|
||||
endif
|
||||
|
||||
-WITH_NETWORK := true
|
||||
+WITH_NETWORK ?= true
|
||||
+REDOX_OVERRIDE ?= false
|
||||
|
||||
################################################################################
|
||||
|
||||
@@ -114,11 +115,15 @@ installdirs :
|
||||
mkdir -p $(DESTDIR)$(docdir)
|
||||
mkdir -p $(DESTDIR)$(man6dir)
|
||||
mkdir -p $(DESTDIR)$(desktopdir)
|
||||
- mkdir -p $(DESTDIR)$(icondir)/hicolor/22x22/apps
|
||||
- mkdir -p $(DESTDIR)$(icondir)/hicolor/24x24/apps
|
||||
- mkdir -p $(DESTDIR)$(icondir)/hicolor/32x32/apps
|
||||
- mkdir -p $(DESTDIR)$(icondir)/hicolor/48x48/apps
|
||||
- mkdir -p $(DESTDIR)$(icondir)/hicolor/128x128/apps
|
||||
+ if [ "$(REDOX_OVERRIDE)" = "true" ]; then\
|
||||
+ mkdir -p $(DESTDIR)$(icondir);\
|
||||
+ else\
|
||||
+ mkdir -p $(DESTDIR)$(icondir)/hicolor/22x22/apps;\
|
||||
+ mkdir -p $(DESTDIR)$(icondir)/hicolor/24x24/apps;\
|
||||
+ mkdir -p $(DESTDIR)$(icondir)/hicolor/32x32/apps;\
|
||||
+ mkdir -p $(DESTDIR)$(icondir)/hicolor/48x48/apps;\
|
||||
+ mkdir -p $(DESTDIR)$(icondir)/hicolor/128x128/apps;\
|
||||
+ fi;\
|
||||
|
||||
.PHONY : install
|
||||
install : $(TARGET) installdirs
|
||||
@@ -126,11 +131,15 @@ install : $(TARGET) installdirs
|
||||
$(INSTALL_DATA) NEWS README $(DESTDIR)$(docdir)/
|
||||
$(INSTALL_DATA) linux/man/opentyrian.6 $(DESTDIR)$(man6dir)/opentyrian$(man6ext)
|
||||
$(INSTALL_DATA) linux/opentyrian.desktop $(DESTDIR)$(desktopdir)/
|
||||
- $(INSTALL_DATA) linux/icons/tyrian-22.png $(DESTDIR)$(icondir)/hicolor/22x22/apps/opentyrian.png
|
||||
- $(INSTALL_DATA) linux/icons/tyrian-24.png $(DESTDIR)$(icondir)/hicolor/24x24/apps/opentyrian.png
|
||||
- $(INSTALL_DATA) linux/icons/tyrian-32.png $(DESTDIR)$(icondir)/hicolor/32x32/apps/opentyrian.png
|
||||
- $(INSTALL_DATA) linux/icons/tyrian-48.png $(DESTDIR)$(icondir)/hicolor/48x48/apps/opentyrian.png
|
||||
- $(INSTALL_DATA) linux/icons/tyrian-128.png $(DESTDIR)$(icondir)/hicolor/128x128/apps/opentyrian.png
|
||||
+ if [ "$(REDOX_OVERRIDE)" = "true" ]; then\
|
||||
+ $(INSTALL_DATA) linux/icons/tyrian-32.png $(DESTDIR)$(icondir)/opentyrian.png;\
|
||||
+ else\
|
||||
+ $(INSTALL_DATA) linux/icons/tyrian-22.png $(DESTDIR)$(icondir)/hicolor/22x22/apps/opentyrian.png;\
|
||||
+ $(INSTALL_DATA) linux/icons/tyrian-24.png $(DESTDIR)$(icondir)/hicolor/24x24/apps/opentyrian.png;\
|
||||
+ $(INSTALL_DATA) linux/icons/tyrian-32.png $(DESTDIR)$(icondir)/hicolor/32x32/apps/opentyrian.png;\
|
||||
+ $(INSTALL_DATA) linux/icons/tyrian-48.png $(DESTDIR)$(icondir)/hicolor/48x48/apps/opentyrian.png;\
|
||||
+ $(INSTALL_DATA) linux/icons/tyrian-128.png $(DESTDIR)$(icondir)/hicolor/128x128/apps/opentyrian.png;\
|
||||
+ fi;\
|
||||
|
||||
.PHONY : uninstall
|
||||
uninstall :
|
||||
@@ -1,16 +0,0 @@
|
||||
diff -burpN source-original/src/m_misc.c source/src/m_misc.c
|
||||
--- source-original/src/m_misc.c 2008-11-09 10:13:04.000000000 -0700
|
||||
+++ source/src/m_misc.c 2024-09-07 10:09:06.890301682 -0600
|
||||
@@ -954,6 +954,12 @@ void M_LoadDefaults (void)
|
||||
// read the file in, overriding any set defaults
|
||||
|
||||
f = fopen (defaultfile, "r");
|
||||
+#if defined(__redox__)
|
||||
+ if (f) {
|
||||
+ printf("disabling load of config file on redox\n");
|
||||
+ f = NULL;
|
||||
+ }
|
||||
+#endif
|
||||
if (f)
|
||||
{
|
||||
while (!feof(f))
|
||||
@@ -1,110 +0,0 @@
|
||||
diff '--color=auto' -rupwN source/Makefile source-new/Makefile
|
||||
--- source/Makefile 2024-10-15 21:21:14.824589882 -0400
|
||||
+++ source-new/Makefile 2024-10-16 00:42:27.651948743 -0400
|
||||
@@ -4,28 +4,34 @@
|
||||
# "make SDL_CONFIG=/path/to/sdl-config" for unusual SDL installations.
|
||||
# "make DO_USERDIRS=1" to enable user directories support
|
||||
|
||||
+# Base install directory
|
||||
+DESTDIR ?= "/"
|
||||
+INSTALLDIR = "${DESTDIR}/usr/games/"
|
||||
+DATADIR = "${DESTDIR}/usr/share/games/quake1/id1/"
|
||||
+ICODIR = "${DESTDIR}/usr/share/icons/apps/"
|
||||
+
|
||||
# Enable/Disable user directories support
|
||||
-DO_USERDIRS=0
|
||||
+DO_USERDIRS ?= 0
|
||||
|
||||
### Enable/Disable SDL2
|
||||
-USE_SDL2=0
|
||||
+USE_SDL2 ?= 0
|
||||
|
||||
### Enable/Disable codecs for streaming music support
|
||||
-USE_CODEC_WAVE=1
|
||||
-USE_CODEC_FLAC=0
|
||||
-USE_CODEC_MP3=1
|
||||
-USE_CODEC_VORBIS=1
|
||||
-USE_CODEC_OPUS=0
|
||||
+USE_CODEC_WAVE ?= 1
|
||||
+USE_CODEC_FLAC ?= 0
|
||||
+USE_CODEC_MP3 ?= 1
|
||||
+USE_CODEC_VORBIS ?= 1
|
||||
+USE_CODEC_OPUS ?= 0
|
||||
# either xmp or mikmod (or modplug)
|
||||
-USE_CODEC_MIKMOD=0
|
||||
-USE_CODEC_XMP=0
|
||||
-USE_CODEC_MODPLUG=0
|
||||
-USE_CODEC_UMX=0
|
||||
+USE_CODEC_MIKMOD ?= 0
|
||||
+USE_CODEC_XMP ?= 0
|
||||
+USE_CODEC_MODPLUG ?= 0
|
||||
+USE_CODEC_UMX ?= 0
|
||||
|
||||
# which library to use for mp3 decoding: mad or mpg123
|
||||
-MP3LIB=mad
|
||||
+MP3LIB ?= mad
|
||||
# which library to use for ogg decoding: vorbis or tremor
|
||||
-VORBISLIB=vorbis
|
||||
+VORBISLIB ?= vorbis
|
||||
|
||||
# ---------------------------
|
||||
# Helper functions
|
||||
@@ -35,7 +41,7 @@ check_gcc = $(shell if echo | $(CC) $(1)
|
||||
|
||||
# ---------------------------
|
||||
|
||||
-HOST_OS = $(shell uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]')
|
||||
+HOST_OS ?= $(shell uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]')
|
||||
|
||||
DEBUG ?= 0
|
||||
|
||||
@@ -49,7 +55,7 @@ LINKER = $(CC)
|
||||
STRIP ?= strip
|
||||
PKG_CONFIG ?= pkg-config
|
||||
|
||||
-CPUFLAGS=
|
||||
+CPUFLAGS ?=
|
||||
LDFLAGS?=
|
||||
DFLAGS ?=
|
||||
CFLAGS ?= -Wall -Wno-trigraphs -MMD
|
||||
@@ -81,11 +87,19 @@ endif
|
||||
|
||||
ifeq ($(USE_SDL2),1)
|
||||
SDL_CONFIG ?= sdl2-config
|
||||
+SDL_VERSION = sdl2
|
||||
else
|
||||
SDL_CONFIG ?= sdl-config
|
||||
+SDL_VERSION = sdl
|
||||
endif
|
||||
+
|
||||
+ifeq ($(HOST_OS),redox)
|
||||
+SDL_CFLAGS = $(shell $(PKG_CONFIG) --cflags $(SDL_VERSION))
|
||||
+SDL_LIBS = $(shell $(PKG_CONFIG) --libs $(SDL_VERSION))
|
||||
+else
|
||||
SDL_CFLAGS = $(shell $(SDL_CONFIG) --cflags)
|
||||
SDL_LIBS = $(shell $(SDL_CONFIG) --libs)
|
||||
+endif
|
||||
|
||||
NET_LIBS =
|
||||
ifeq ($(HOST_OS),sunos)
|
||||
@@ -164,6 +178,8 @@ endif
|
||||
|
||||
ifeq ($(HOST_OS),haiku)
|
||||
COMMON_LIBS= -lGL
|
||||
+else ifeq ($(HOST_OS),redox)
|
||||
+COMMON_LIBS= -lorbital $(shell $(PKG_CONFIG) --libs osmesa zlib)
|
||||
else
|
||||
COMMON_LIBS= -lGL -lm
|
||||
endif
|
||||
@@ -290,7 +306,10 @@ install: quakespasm
|
||||
cp quakespasm.pak $(QS_APP_DIR)
|
||||
else
|
||||
install: quakespasm
|
||||
- cp quakespasm /usr/local/games/quake
|
||||
+ mkdir -p "${INSTALLDIR}" "${DATADIR}" "${ICODIR}"
|
||||
+ cp quakespasm "${INSTALLDIR}/quakespasm"
|
||||
+ # xxx Probably requires resizing
|
||||
+ cp Misc/QuakeSpasm_512.png "${ICODIR}/quakespasm.png"
|
||||
endif
|
||||
|
||||
sinclude $(OBJS:.o=.d)
|
||||
@@ -1,50 +0,0 @@
|
||||
diff -ruwN cairo-1.18.4/meson.build source/meson.build
|
||||
--- cairo-1.18.4/meson.build 2025-03-08 05:35:35.000000000 -0700
|
||||
+++ source/meson.build 2025-05-04 18:07:04.594213814 -0600
|
||||
@@ -440,13 +440,13 @@
|
||||
if feature_conf.get('CAIRO_HAS_XCB_SURFACE', 0) == 1
|
||||
xcbshm_dep = dependency('xcb-shm', required: get_option('xcb'))
|
||||
if xcbshm_dep.found()
|
||||
- feature_conf.set('CAIRO_HAS_XCB_SHM_FUNCTIONS', 1)
|
||||
- deps += [xcbshm_dep]
|
||||
- built_features += [{
|
||||
- 'name': 'cairo-xcb-shm',
|
||||
- 'description': 'XCB/SHM functions',
|
||||
- 'deps': [xcbshm_dep],
|
||||
- }]
|
||||
+ #feature_conf.set('CAIRO_HAS_XCB_SHM_FUNCTIONS', 1)
|
||||
+ #deps += [xcbshm_dep]
|
||||
+ #built_features += [{
|
||||
+ # 'name': 'cairo-xcb-shm',
|
||||
+ # 'description': 'XCB/SHM functions',
|
||||
+ # 'deps': [xcbshm_dep],
|
||||
+ #}]
|
||||
endif
|
||||
endif
|
||||
|
||||
diff -ruwN cairo-1.18.4/perf/Makefile.in source/perf/Makefile.in
|
||||
--- cairo-1.18.4/perf/Makefile.in 1969-12-31 17:00:00.000000000 -0700
|
||||
+++ source/perf/Makefile.in 2025-05-01 12:52:11.400963345 -0600
|
||||
@@ -0,0 +1,3 @@
|
||||
+all:
|
||||
+
|
||||
+install:
|
||||
diff -ruwN cairo-1.18.4/src/cairo-ps-surface.c source/src/cairo-ps-surface.c
|
||||
--- cairo-1.18.4/src/cairo-ps-surface.c 2025-03-08 05:35:35.000000000 -0700
|
||||
+++ source/src/cairo-ps-surface.c 2025-05-04 18:08:43.821264417 -0600
|
||||
@@ -102,7 +102,7 @@
|
||||
#define DEBUG_FALLBACK(s)
|
||||
#endif
|
||||
|
||||
-#ifndef HAVE_CTIME_R
|
||||
+#if !defined(HAVE_CTIME_R) && !defined(__redox__)
|
||||
static char *ctime_r(const time_t *timep, char *buf)
|
||||
{
|
||||
(void)buf;
|
||||
diff -ruwN cairo-1.18.4/test/Makefile.in source/test/Makefile.in
|
||||
--- cairo-1.18.4/test/Makefile.in 1969-12-31 17:00:00.000000000 -0700
|
||||
+++ source/test/Makefile.in 2025-05-01 12:52:11.400963345 -0600
|
||||
@@ -0,0 +1,3 @@
|
||||
+all:
|
||||
+
|
||||
+install:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user