8acc73d774
Phase 1 (Runtime Substrate): 4 check binaries, --probe, POSIX tests Phase 2 (Wayland Compositor): bounded scaffold, zero warnings Phase 3 (KWin Session): preflight checker (KWin stub, gated on Qt6Quick) Phase 4 (KDE Plasma): 18 KF6 enabled, preflight checker Phase 5 (Hardware GPU): DRM/firmware/Mesa preflight checker Build: zero warnings, all scripts syntax-clean. Oracle-verified.
355 lines
11 KiB
Rust
355 lines
11 KiB
Rust
use alloc::string::ToString;
|
|
use alloc::sync::Arc;
|
|
use alloc::vec::Vec;
|
|
use core::ffi::CStr;
|
|
use core::str::FromStr;
|
|
use hashbrown::HashMap;
|
|
use redox_scheme::Socket;
|
|
|
|
use syscall::CallFlags;
|
|
use syscall::data::{GlobalSchemes, KernelSchemeInfo};
|
|
use syscall::flag::{O_CLOEXEC, O_RDONLY, O_STAT};
|
|
use syscall::{EINTR, Error};
|
|
|
|
use redox_rt::proc::*;
|
|
|
|
use crate::KernelSchemeMap;
|
|
|
|
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) {}
|
|
}
|
|
|
|
const KERNEL_METADATA_BASE: usize = crate::arch::USERMODE_END - syscall::KERNEL_METADATA_SIZE;
|
|
|
|
pub fn main() -> ! {
|
|
let mut cursor = KERNEL_METADATA_BASE;
|
|
let kernel_scheme_infos = unsafe {
|
|
let base_ptr = cursor as *const u8;
|
|
let infos_len = *(base_ptr as *const usize);
|
|
let infos_ptr = base_ptr.add(core::mem::size_of::<usize>()) as *const KernelSchemeInfo;
|
|
let slice = core::slice::from_raw_parts(infos_ptr, infos_len);
|
|
cursor += core::mem::size_of::<usize>() // kernel scheme number size
|
|
+ infos_len // kernel scheme number
|
|
* core::mem::size_of::<KernelSchemeInfo>();
|
|
slice
|
|
};
|
|
let scheme_creation_cap = unsafe {
|
|
let base_ptr = cursor as *const u8;
|
|
FdGuard::new(*(base_ptr as *const usize))
|
|
};
|
|
|
|
let mut kernel_schemes = KernelSchemeMap::new(kernel_scheme_infos);
|
|
|
|
let auth = kernel_schemes
|
|
.0
|
|
.remove(&GlobalSchemes::Proc)
|
|
.expect("failed to get proc fd");
|
|
|
|
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) };
|
|
|
|
let mut env_bytes = [0_u8; 4096];
|
|
let mut envs = {
|
|
let fd = FdGuard::new(
|
|
syscall::openat(
|
|
kernel_schemes
|
|
.get(GlobalSchemes::Sys)
|
|
.expect("failed to get sys fd")
|
|
.as_raw_fd(),
|
|
"env",
|
|
O_RDONLY | O_CLOEXEC,
|
|
0,
|
|
)
|
|
.expect("bootstrap: failed to open env"),
|
|
);
|
|
let bytes_read = fd
|
|
.read(&mut env_bytes)
|
|
.expect("bootstrap: failed to read env");
|
|
|
|
if bytes_read >= env_bytes.len() {
|
|
// TODO: Handle this, we can allocate as much as we want in theory.
|
|
panic!("env is too large");
|
|
}
|
|
let env_bytes = &mut env_bytes[..bytes_read];
|
|
|
|
env_bytes
|
|
.split(|&c| c == b'\n')
|
|
.filter(|var| !var.is_empty())
|
|
.filter(|var| !var.starts_with(b"INITFS_"))
|
|
.collect::<Vec<_>>()
|
|
};
|
|
envs.push(b"RUST_BACKTRACE=1");
|
|
//envs.push(b"LD_DEBUG=all");
|
|
envs.push(b"LD_LIBRARY_PATH=/scheme/initfs/lib");
|
|
|
|
log::set_max_level(log::LevelFilter::Warn);
|
|
|
|
if let Some(log_env) = envs
|
|
.iter()
|
|
.find_map(|var| var.strip_prefix(b"BOOTSTRAP_LOG_LEVEL="))
|
|
{
|
|
if let Ok(Ok(log_level)) = str::from_utf8(&log_env).map(|s| log::LevelFilter::from_str(s)) {
|
|
log::set_max_level(log_level);
|
|
}
|
|
}
|
|
|
|
let _ = log::set_logger(&Logger);
|
|
|
|
unsafe extern "C" {
|
|
// The linker script will define this as the location of the initfs header.
|
|
static __initfs_header: u8;
|
|
|
|
// The linker script will define this as the end of the executable (excluding initfs).
|
|
static __bss_end: u8;
|
|
}
|
|
|
|
let initfs_start = core::ptr::addr_of!(__initfs_header);
|
|
let initfs_length = unsafe {
|
|
(*(core::ptr::addr_of!(__initfs_header) as *const redox_initfs::types::Header))
|
|
.initfs_size
|
|
.get() as usize
|
|
};
|
|
|
|
let (scheme_creation_cap, auth, kernel_schemes, initfs_fd) = spawn(
|
|
"initfs daemon",
|
|
auth,
|
|
&this_thr_fd,
|
|
scheme_creation_cap,
|
|
kernel_schemes,
|
|
false,
|
|
|write_fd, socket, _, _| unsafe {
|
|
crate::initfs::run(
|
|
core::slice::from_raw_parts(initfs_start, initfs_length),
|
|
write_fd,
|
|
socket,
|
|
);
|
|
},
|
|
);
|
|
|
|
// Unmap initfs data as only the initfs scheme implementation needs it.
|
|
unsafe {
|
|
let executable_end = core::ptr::addr_of!(__bss_end)
|
|
.add(core::ptr::addr_of!(__bss_end).align_offset(syscall::PAGE_SIZE));
|
|
syscall::funmap(
|
|
executable_end as usize,
|
|
initfs_length.next_multiple_of(syscall::PAGE_SIZE)
|
|
- (executable_end.offset_from(initfs_start) as usize),
|
|
)
|
|
.unwrap();
|
|
}
|
|
|
|
let (scheme_creation_cap, auth, kernel_schemes, proc_fd) = spawn(
|
|
"process manager",
|
|
auth,
|
|
&this_thr_fd,
|
|
scheme_creation_cap,
|
|
kernel_schemes,
|
|
true,
|
|
|write_fd, socket, auth, mut kernel_schemes| {
|
|
let event = kernel_schemes
|
|
.0
|
|
.remove(&GlobalSchemes::Event)
|
|
.expect("failed to get event fd");
|
|
drop(kernel_schemes);
|
|
crate::procmgr::run(write_fd, socket, auth, event)
|
|
},
|
|
);
|
|
|
|
let scheme_creation_cap_dup = scheme_creation_cap
|
|
.dup(b"")
|
|
.expect("failed to dup scheme creation cap");
|
|
let (_, _, _, initns_fd) = spawn(
|
|
"init namespace manager",
|
|
auth,
|
|
&this_thr_fd,
|
|
scheme_creation_cap,
|
|
kernel_schemes,
|
|
false,
|
|
|write_fd, socket, _, kernel_schemes| {
|
|
let mut schemes = HashMap::default();
|
|
for (scheme, fd) in kernel_schemes.0.into_iter() {
|
|
schemes.insert(scheme.as_str().to_string(), Arc::new(fd));
|
|
}
|
|
schemes.insert(
|
|
"proc".to_string(),
|
|
// A bit dirty, but necessary as the parent process still needs access to it. Rust
|
|
// doesn't know that the fd got cloned by fork.
|
|
Arc::new(FdGuard::new(proc_fd.as_raw_fd())),
|
|
);
|
|
schemes.insert("initfs".to_string(), Arc::new(initfs_fd));
|
|
|
|
crate::initnsmgr::run(write_fd, socket, schemes, scheme_creation_cap_dup)
|
|
},
|
|
);
|
|
|
|
let (init_proc_fd, init_thr_fd) = unsafe { make_init(proc_fd.take()) };
|
|
// from this point, this_thr_fd is no longer valid
|
|
|
|
const CWD: &[u8] = b"/scheme/initfs";
|
|
let cwd_fd = FdGuard::new(
|
|
syscall::openat(initns_fd.as_raw_fd(), "/scheme/initfs", O_STAT, 0)
|
|
.expect("failed to open cwd fd"),
|
|
)
|
|
.to_upper()
|
|
.unwrap();
|
|
let extrainfo = ExtraInfo {
|
|
cwd: Some(CWD),
|
|
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(),
|
|
ns_fd: Some(initns_fd.take()),
|
|
cwd_fd: Some(cwd_fd.as_raw_fd()),
|
|
};
|
|
|
|
let exe_path = "/scheme/initfs/bin/init";
|
|
|
|
let image_file = FdGuard::new(
|
|
syscall::openat(extrainfo.ns_fd.unwrap(), exe_path, O_RDONLY | O_CLOEXEC, 0)
|
|
.expect("failed to open init"),
|
|
)
|
|
.to_upper()
|
|
.unwrap();
|
|
|
|
let FexecResult::Interp {
|
|
path: interp_path,
|
|
interp_override,
|
|
} = fexec_impl(
|
|
image_file,
|
|
init_thr_fd,
|
|
init_proc_fd,
|
|
exe_path.as_bytes(),
|
|
&[exe_path.as_bytes()],
|
|
&envs,
|
|
&extrainfo,
|
|
None,
|
|
)
|
|
.expect("failed to execute init");
|
|
|
|
// According to elf(5), PT_INTERP requires that the interpreter path be
|
|
// null-terminated. Violating this should therefore give the "format error" ENOEXEC.
|
|
let interp_cstr = CStr::from_bytes_with_nul(&interp_path).expect("interpreter not valid C str");
|
|
let interp_file = FdGuard::new(
|
|
syscall::openat(
|
|
extrainfo.ns_fd.unwrap(), // initns, not initfs!
|
|
interp_cstr.to_str().expect("interpreter not UTF-8"),
|
|
O_RDONLY | O_CLOEXEC,
|
|
0,
|
|
)
|
|
.expect("failed to open dynamic linker"),
|
|
)
|
|
.to_upper()
|
|
.unwrap();
|
|
|
|
fexec_impl(
|
|
interp_file,
|
|
init_thr_fd,
|
|
init_proc_fd,
|
|
exe_path.as_bytes(),
|
|
&[exe_path.as_bytes()],
|
|
&envs,
|
|
&extrainfo,
|
|
Some(interp_override),
|
|
)
|
|
.expect("failed to execute init");
|
|
|
|
unreachable!()
|
|
}
|
|
|
|
pub(crate) fn spawn(
|
|
name: &str,
|
|
auth: FdGuard,
|
|
this_thr_fd: &FdGuardUpper,
|
|
scheme_creation_cap: FdGuard,
|
|
kernel_schemes: KernelSchemeMap,
|
|
nonblock: bool,
|
|
inner: impl FnOnce(FdGuard, Socket, FdGuard, KernelSchemeMap) -> !,
|
|
) -> (FdGuard, FdGuard, KernelSchemeMap, FdGuard) {
|
|
let read = FdGuard::new(
|
|
syscall::openat(
|
|
kernel_schemes
|
|
.get(GlobalSchemes::Pipe)
|
|
.expect("failed to get pipe fd")
|
|
.as_raw_fd(),
|
|
"",
|
|
O_CLOEXEC,
|
|
0,
|
|
)
|
|
.expect("failed to open sync read pipe"),
|
|
);
|
|
|
|
// The write pipe will not inherit O_CLOEXEC, but is closed by the daemon later.
|
|
let write = FdGuard::new(
|
|
syscall::dup(read.as_raw_fd(), b"write").expect("failed to open sync write pipe"),
|
|
);
|
|
|
|
match fork_impl(&ForkArgs::Init {
|
|
this_thr_fd,
|
|
auth: &auth,
|
|
}) {
|
|
Err(err) => {
|
|
panic!("Failed to fork in order to start {name}: {err}");
|
|
}
|
|
// Continue serving the scheme as the child.
|
|
Ok(0) => {
|
|
drop(read);
|
|
|
|
let socket = Socket::create_inner(scheme_creation_cap.as_raw_fd(), nonblock)
|
|
.expect("failed to open proc scheme socket");
|
|
drop(scheme_creation_cap);
|
|
|
|
inner(write, socket, auth, kernel_schemes)
|
|
}
|
|
// Return in order to execute init, as the parent.
|
|
Ok(_) => {
|
|
drop(write);
|
|
|
|
let mut new_fd = usize::MAX;
|
|
let fd_bytes = unsafe {
|
|
core::slice::from_raw_parts_mut(
|
|
core::slice::from_mut(&mut new_fd).as_mut_ptr() as *mut u8,
|
|
core::mem::size_of::<usize>(),
|
|
)
|
|
};
|
|
loop {
|
|
match syscall::call_ro(
|
|
read.as_raw_fd(),
|
|
fd_bytes,
|
|
CallFlags::FD | CallFlags::FD_UPPER,
|
|
&[],
|
|
) {
|
|
Err(Error { errno: EINTR }) => continue,
|
|
_ => break,
|
|
}
|
|
}
|
|
|
|
(
|
|
scheme_creation_cap,
|
|
auth,
|
|
kernel_schemes,
|
|
FdGuard::new(new_fd),
|
|
)
|
|
}
|
|
}
|
|
}
|