Merge branch 'procmgr' into 'master'

Implement process manager

See merge request redox-os/bootstrap!18
This commit is contained in:
Jeremy Soller
2025-04-19 18:13:01 +00:00
9 changed files with 2552 additions and 91 deletions
Generated
+41 -13
View File
@@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "autocfg"
version = "1.4.0"
@@ -18,19 +24,29 @@ checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
name = "bootstrap"
version = "0.0.0"
dependencies = [
"arrayvec",
"hashbrown",
"linked_list_allocator",
"log",
"plain",
"redox-initfs",
"redox-path",
"redox-rt",
"redox-scheme",
"redox_syscall",
"slab",
]
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "generic-rt"
version = "0.1.0"
source = "git+https://gitlab.redox-os.org/redox-os/relibc#5ea71a02c7f9e436ed0dcf53bc7a5bdb311279cf"
source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#040009ab3d384e75d62b7e7dd3176d910203501a"
[[package]]
name = "goblin"
@@ -45,15 +61,18 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.14.5"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
dependencies = [
"foldhash",
]
[[package]]
name = "libc"
version = "0.2.171"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "libredox"
@@ -99,9 +118,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "proc-macro2"
version = "1.0.94"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
@@ -132,7 +151,7 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717"
[[package]]
name = "redox-rt"
version = "0.1.0"
source = "git+https://gitlab.redox-os.org/redox-os/relibc#5ea71a02c7f9e436ed0dcf53bc7a5bdb311279cf"
source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#040009ab3d384e75d62b7e7dd3176d910203501a"
dependencies = [
"bitflags",
"generic-rt",
@@ -143,8 +162,9 @@ dependencies = [
[[package]]
name = "redox-scheme"
version = "0.2.3"
source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git?tag=v0.2.3#dc6a047842353ed49c1788396d9135de2c3d7382"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6469e083bf650071e9a80003c53171dfccc5ef17ae1d24972396c3c792e7c709"
dependencies = [
"libredox",
"redox_syscall",
@@ -152,9 +172,8 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
version = "0.5.11"
source = "git+https://gitlab.redox-os.org/redox-os/syscall.git?branch=master#8f646a63ac9c3aa532a37ddfdc136ec19878f0d6"
dependencies = [
"bitflags",
]
@@ -185,6 +204,15 @@ dependencies = [
"syn",
]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]]
name = "spinning_top"
version = "0.2.5"
+12 -4
View File
@@ -10,15 +10,20 @@ name = "bootstrap"
crate-type = ["staticlib"]
[dependencies]
hashbrown = { version = "0.14", default-features = false, features = [
hashbrown = { version = "0.15", default-features = false, features = [
"inline-more",
"default-hasher",
] }
linked_list_allocator = "0.10"
log = { version = "0.4", default-features = false }
plain = "0.2"
redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false }
redox_syscall = "0.5.4"
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git", tag = "v0.2.3" }
redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false }
redox_syscall = { version = "0.5.4", default-features = false }
redox-scheme = { version = "0.6", default-features = false }
redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc.git", default-features = false }
redox-path = "0.3.1"
slab = { version = "0.4.9", default-features = false }
arrayvec = { version = "0.7.6", default-features = false }
[profile.release]
panic = "abort"
@@ -26,3 +31,6 @@ lto = "fat"
[profile.dev]
panic = "abort"
[patch.crates-io]
redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git", branch = "master" }
+7
View File
@@ -17,3 +17,10 @@ To learn how to do development with this system component inside the Redox build
To build this system component you need to download the Redox build system, you can learn how to do it on the [Building Redox](https://doc.redox-os.org/book/podman-build.html) page.
This is necessary because they only work with cross-compilation to a Redox virtual machine, but you can do some testing from Linux.
## Funding - Process Manager (`src/procmgr.rs`)
The _Unix-style Signals and Process Management_ project is funded through [NGI Zero Core](https://nlnet.nl/core), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/RedoxOS-Signals).
[<img src="https://nlnet.nl/logo/banner.png" alt="NLnet foundation logo" width="20%" />](https://nlnet.nl)
[<img src="https://nlnet.nl/image/logos/NGI0_tag.svg" alt="NGI Zero Logo" width="20%" />](https://nlnet.nl/core)
+4 -8
View File
@@ -1,9 +1,5 @@
use core::mem;
use syscall::{
data::Map,
flag::MapFlags,
number::SYS_FMAP,
};
use syscall::{data::Map, flag::MapFlags, number::SYS_FMAP};
pub const USERMODE_END: usize = 0x0000_8000_0000_0000;
pub const STACK_START: usize = USERMODE_END - STACK_SIZE;
@@ -13,9 +9,9 @@ static MAP: Map = Map {
offset: 0,
size: STACK_SIZE,
flags: MapFlags::PROT_READ
.union(MapFlags::PROT_WRITE)
.union(MapFlags::MAP_PRIVATE)
.union(MapFlags::MAP_FIXED_NOREPLACE),
.union(MapFlags::PROT_WRITE)
.union(MapFlags::MAP_PRIVATE)
.union(MapFlags::MAP_FIXED_NOREPLACE),
address: STACK_START, // highest possible user address
};
+61 -20
View File
@@ -6,7 +6,37 @@ use syscall::{Error, EINTR};
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::new(
syscall::open("/scheme/kernel.proc/authority", O_CLOEXEC)
.expect("failed to get proc authority"),
);
let this_thr_fd =
FdGuard::new(syscall::dup(*auth, b"cur-context").expect("failed to open open_via_dup"));
let this_thr_fd = unsafe { redox_rt::initialize_freestanding(this_thr_fd) };
log::set_max_level(log::LevelFilter::Debug);
let _ = log::set_logger(&Logger);
let envs = {
let mut env = [0_u8; 4096];
@@ -37,14 +67,29 @@ pub fn main() -> ! {
(*(core::ptr::addr_of!(__initfs_header) as *const redox_initfs::types::Header)).initfs_size
};
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.
spawn_initfs(
core::ptr::addr_of!(__initfs_header),
initfs_length.get() as usize,
);
}
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 {
@@ -53,6 +98,8 @@ pub fn main() -> ! {
sigprocmask: 0,
sigignmask: 0,
umask: redox_rt::sys::get_umask(),
thr_fd: **init_thr_fd,
proc_fd: **init_proc_fd,
};
let path = "/scheme/initfs/bin/init";
@@ -66,15 +113,12 @@ pub fn main() -> ! {
+ 1;
let image_file = FdGuard::new(syscall::open(path, O_RDONLY).expect("failed to open init"));
let open_via_dup = FdGuard::new(
syscall::open("/scheme/thisproc/current/open_via_dup", 0)
.expect("failed to open open_via_dup"),
);
let memory = FdGuard::new(syscall::open("/scheme/memory", 0).expect("failed to open memory"));
fexec_impl(
image_file,
open_via_dup,
init_thr_fd,
init_proc_fd,
&memory,
path.as_bytes(),
[path],
@@ -88,15 +132,15 @@ pub fn main() -> ! {
unreachable!()
}
unsafe fn spawn_initfs(initfs_start: *const u8, initfs_length: usize) {
pub(crate) fn spawn(name: &str, auth: &FdGuard, this_thr_fd: &FdGuard, 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() {
match fork_impl(&ForkArgs::Init { this_thr_fd, auth }) {
Err(err) => {
panic!("Failed to fork in order to start initfs daemon: {}", err);
panic!("Failed to fork in order to start {name}: {err}");
}
// Continue serving the scheme as the child.
Ok(0) => {
@@ -115,8 +159,5 @@ unsafe fn spawn_initfs(initfs_start: *const u8, initfs_length: usize) {
return;
}
}
crate::initfs::run(
core::slice::from_raw_parts(initfs_start, initfs_length),
write,
);
inner(write);
}
+31 -36
View File
@@ -9,14 +9,9 @@ use hashbrown::HashMap;
use redox_initfs::{types::Timespec, InitFs, Inode, InodeDir, InodeKind, InodeStruct};
use redox_path::canonicalize_to_standard;
use redox_scheme::CallerCtx;
use redox_scheme::OpenResult;
use redox_scheme::RequestKind;
use redox_scheme::SchemeMut;
use redox_scheme::{scheme::SchemeSync, CallerCtx, OpenResult, RequestKind};
use redox_scheme::SignalBehavior;
use redox_scheme::Socket;
use redox_scheme::V2;
use redox_scheme::{SignalBehavior, Socket};
use syscall::data::Stat;
use syscall::dirent::DirEntry;
use syscall::dirent::DirentBuf;
@@ -93,8 +88,8 @@ fn inode_len(inode: InodeStruct<'static>) -> Result<usize> {
})
}
impl SchemeMut for InitFsScheme {
fn xopen(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result<OpenResult> {
impl SchemeSync for InitFsScheme {
fn open(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result<OpenResult> {
let mut components = path
// trim leading and trailing slash
.trim_matches('/')
@@ -180,6 +175,7 @@ impl SchemeMut for InitFsScheme {
buffer: &mut [u8],
offset: u64,
_fcntl_flags: u32,
_ctx: &CallerCtx,
) -> Result<usize> {
let Ok(offset) = usize::try_from(offset) else {
return Ok(0);
@@ -248,19 +244,19 @@ impl SchemeMut for InitFsScheme {
Ok(buf)
}
fn fsize(&mut self, id: usize) -> Result<u64> {
fn fsize(&mut self, id: usize, _ctx: &CallerCtx) -> Result<u64> {
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
Ok(inode_len(Self::get_inode(&self.fs, handle.inode)?)? as u64)
}
fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize, _ctx: &CallerCtx) -> Result<usize> {
let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
Ok(0)
}
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<usize> {
fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
// TODO: Copy scheme part in kernel
@@ -275,7 +271,7 @@ impl SchemeMut for InitFsScheme {
Ok(scheme_bytes + path_bytes)
}
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<usize> {
fn fstat(&mut self, id: usize, stat: &mut Stat, _ctx: &CallerCtx) -> Result<()> {
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
let Timespec { sec, nsec } = self.fs.image_creation_time();
@@ -297,52 +293,51 @@ impl SchemeMut for InitFsScheme {
stat.st_mtime = sec.get();
stat.st_mtime_nsec = nsec.get();
Ok(0)
Ok(())
}
fn fsync(&mut self, id: usize) -> Result<usize> {
fn fsync(&mut self, id: usize, _ctx: &CallerCtx) -> Result<()> {
if !self.handles.contains_key(&id) {
return Err(Error::new(EBADF));
}
Ok(0)
}
fn close(&mut self, id: usize) -> Result<usize> {
let _ = self.handles.remove(&id).ok_or(Error::new(EBADF))?;
Ok(0)
Ok(())
}
}
pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! {
let mut scheme = InitFsScheme::new(bytes);
let socket = Socket::<V2>::create("initfs").expect("failed to open initfs scheme socket");
let socket = Socket::create("initfs").expect("failed to open initfs scheme socket");
let _ = syscall::write(sync_pipe, &[0]);
let _ = syscall::close(sync_pipe);
loop {
let RequestKind::Call(req) = (match socket
let Some(req) = socket
.next_request(SignalBehavior::Restart)
.expect("bootstrap: failed to read scheme request from kernel")
{
Some(req) => req.kind(),
None => break,
}) else {
continue;
};
let resp = req.handle_scheme_mut(&mut scheme);
if !socket
.write_response(resp, SignalBehavior::Restart)
.expect("bootstrap: failed to write scheme response to kernel")
{
else {
break;
};
match req.kind() {
RequestKind::Call(req) => {
let resp = req.handle_sync(&mut scheme);
if !socket
.write_response(resp, SignalBehavior::Restart)
.expect("bootstrap: failed to write scheme response to kernel")
{
break;
}
}
RequestKind::OnClose { id } => {
scheme.handles.remove(&id);
}
_ => (),
}
}
syscall::exit(0).expect("initfs: failed to exit");
unreachable!()
}
+24 -8
View File
@@ -1,6 +1,6 @@
#![no_std]
#![allow(internal_features)]
#![feature(asm_const, core_intrinsics, str_from_raw_parts, iter_intersperse)]
#![feature(core_intrinsics, let_chains, iter_intersperse, str_from_raw_parts)]
#[cfg(target_arch = "aarch64")]
#[path = "aarch64.rs"]
@@ -20,10 +20,13 @@ pub mod arch;
pub mod exec;
pub mod initfs;
pub mod procmgr;
pub mod start;
extern crate alloc;
use core::cell::UnsafeCell;
use syscall::data::Map;
use syscall::flag::MapFlags;
@@ -51,15 +54,26 @@ struct Allocator;
#[global_allocator]
static ALLOCATOR: Allocator = Allocator;
static mut HEAP: Option<linked_list_allocator::Heap> = None;
static mut HEAP_TOP: usize = HEAP_OFF + SIZE;
struct AllocStateInner {
heap: Option<linked_list_allocator::Heap>,
heap_top: usize,
}
struct AllocState(UnsafeCell<AllocStateInner>);
unsafe impl Send for AllocState {}
unsafe impl Sync for AllocState {}
static ALLOC_STATE: AllocState = AllocState(UnsafeCell::new(AllocStateInner {
heap: None,
heap_top: HEAP_OFF + SIZE,
}));
const SIZE: usize = 1024 * 1024;
const HEAP_INCREASE_BY: usize = SIZE;
unsafe impl alloc::alloc::GlobalAlloc for Allocator {
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
let heap = HEAP.get_or_insert_with(|| {
HEAP_TOP = HEAP_OFF + SIZE;
let state = &mut (*ALLOC_STATE.0.get());
let heap = state.heap.get_or_insert_with(|| {
state.heap_top = HEAP_OFF + SIZE;
let _ = syscall::fmap(
!0,
&Map {
@@ -88,7 +102,7 @@ unsafe impl alloc::alloc::GlobalAlloc for Allocator {
&Map {
offset: 0,
size: HEAP_INCREASE_BY,
address: HEAP_TOP,
address: state.heap_top,
flags: MapFlags::PROT_WRITE
| MapFlags::PROT_READ
| MapFlags::MAP_PRIVATE
@@ -97,14 +111,16 @@ unsafe impl alloc::alloc::GlobalAlloc for Allocator {
)
.expect("failed to extend heap");
heap.extend(HEAP_INCREASE_BY);
HEAP_TOP += HEAP_INCREASE_BY;
state.heap_top += HEAP_INCREASE_BY;
return self.alloc(layout);
}
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
HEAP.as_mut()
(&mut *ALLOC_STATE.0.get())
.heap
.as_mut()
.unwrap()
.deallocate(core::ptr::NonNull::new(ptr).unwrap(), layout)
}
+2372
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -78,8 +78,6 @@ pub unsafe extern "C" fn start() -> ! {
)
.expect("mprotect failed for rest of memory");
redox_rt::initialize_freestanding();
// FIXME make the initfs read-only
crate::exec::main();