Files
RedBear-OS/bootstrap/src/exec.rs
T
2025-11-16 08:37:46 -07:00

174 lines
5.1 KiB
Rust

use alloc::borrow::ToOwned;
use alloc::vec::Vec;
use syscall::flag::{O_CLOEXEC, O_RDONLY};
use syscall::{EINTR, Error};
use redox_rt::proc::*;
struct Logger;
impl log::Log for Logger {
fn enabled(&self, metadata: &log::Metadata) -> bool {
metadata.level() <= log::max_level()
}
fn log(&self, record: &log::Record) {
let file = record.file().unwrap_or("");
let line = record.line().unwrap_or(0);
let level = record.level();
let msg = record.args();
let _ = syscall::write(
1,
alloc::format!("[{file}:{line} {level}] {msg}\n").as_bytes(),
);
}
fn flush(&self) {}
}
pub fn main() -> ! {
let auth = FdGuard::open("/scheme/kernel.proc/authority", O_CLOEXEC)
.expect("failed to get proc authority");
let this_thr_fd = auth
.dup(b"cur-context")
.expect("failed to open open_via_dup")
.to_upper()
.unwrap();
let this_thr_fd = unsafe { redox_rt::initialize_freestanding(this_thr_fd) };
log::set_max_level(log::LevelFilter::Warn);
let _ = log::set_logger(&Logger);
let envs = {
let mut env = [0_u8; 4096];
let fd = FdGuard::open("/scheme/sys/env", O_RDONLY).expect("bootstrap: failed to open env");
let bytes_read = fd.read(&mut env).expect("bootstrap: failed to read env");
if bytes_read >= env.len() {
// TODO: Handle this, we can allocate as much as we want in theory.
panic!("env is too large");
}
let env = &mut env[..bytes_read];
let raw_iter = || env.split(|c| *c == b'\n').filter(|var| !var.is_empty());
let iter = || raw_iter().filter(|var| !var.starts_with(b"INITFS_"));
iter().map(|var| var.to_owned()).collect::<Vec<_>>()
};
unsafe extern "C" {
// The linker script will define this as the location of the initfs header.
static __initfs_header: u8;
}
let initfs_length = unsafe {
(*(core::ptr::addr_of!(__initfs_header) as *const redox_initfs::types::Header)).initfs_size
};
spawn(
"initfs daemon",
&auth,
&this_thr_fd,
move |write_fd| unsafe {
// Creating a reference to NULL is UB. Mask the UB for now using black_box.
// FIXME use a raw pointer and inline asm for reading instead for the initfs header.
let initfs_start = core::ptr::addr_of!(__initfs_header);
let initfs_length = initfs_length.get() as usize;
crate::initfs::run(
core::slice::from_raw_parts(initfs_start, initfs_length),
write_fd,
);
},
);
spawn("process manager", &auth, &this_thr_fd, |write_fd| {
crate::procmgr::run(write_fd, &auth)
});
let (init_proc_fd, init_thr_fd) = unsafe { redox_rt::proc::make_init() };
// from this point, this_thr_fd is no longer valid
const CWD: &[u8] = b"/scheme/initfs";
const DEFAULT_SCHEME: &[u8] = b"initfs";
let extrainfo = ExtraInfo {
cwd: Some(CWD),
default_scheme: Some(DEFAULT_SCHEME),
sigprocmask: 0,
sigignmask: 0,
umask: redox_rt::sys::get_umask(),
thr_fd: init_thr_fd.as_raw_fd(),
proc_fd: init_proc_fd.as_raw_fd(),
};
let path = "/scheme/initfs/bin/init";
let total_args_envs_auxvpointee_size = path.len()
+ 1
+ envs.len()
+ envs.iter().map(|v| v.len()).sum::<usize>()
+ CWD.len()
+ 1
+ DEFAULT_SCHEME.len()
+ 1;
let image_file = FdGuard::open(path, O_RDONLY)
.expect("failed to open init")
.to_upper()
.unwrap();
let memory = FdGuard::open("/scheme/memory", 0)
.expect("failed to open memory")
.to_upper()
.unwrap();
fexec_impl(
image_file,
init_thr_fd,
init_proc_fd,
&memory,
path.as_bytes(),
[path],
envs.iter(),
total_args_envs_auxvpointee_size,
&extrainfo,
None,
)
.expect("failed to execute init");
unreachable!()
}
pub(crate) fn spawn(
name: &str,
auth: &FdGuard,
this_thr_fd: &FdGuardUpper,
inner: impl FnOnce(usize),
) {
let read = syscall::open("/scheme/pipe", O_CLOEXEC).expect("failed to open sync read pipe");
// The write pipe will not inherit O_CLOEXEC, but is closed by the daemon later.
let write = syscall::dup(read, b"write").expect("failed to open sync write pipe");
match fork_impl(&ForkArgs::Init { this_thr_fd, auth }) {
Err(err) => {
panic!("Failed to fork in order to start {name}: {err}");
}
// Continue serving the scheme as the child.
Ok(0) => {
let _ = syscall::close(read);
}
// Return in order to execute init, as the parent.
Ok(_) => {
let _ = syscall::close(write);
loop {
match syscall::read(read, &mut [0]) {
Err(Error { errno: EINTR }) => continue,
_ => break,
}
}
return;
}
}
inner(write);
}