feat(userspace): shell runs programs (ls, cat) via spawn w/ terminal+storage+args page; ext4 holes read as zeros; storage re-attach same buffer
This commit is contained in:
@@ -4,21 +4,26 @@ struct Writer;
|
||||
|
||||
impl fmt::Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
// The log server first; a process without either prints nothing.
|
||||
// A call, not a send: the text is out before the caller goes on.
|
||||
if let Some(log) = crate::start::log() {
|
||||
for chunk in s.as_bytes().chunks(types::log::TEXT_BYTES) {
|
||||
let _ = crate::syscall::call(log, types::log::text_words(chunk), None);
|
||||
}
|
||||
} else if let Some(console) = crate::start::console() {
|
||||
for &b in s.as_bytes() {
|
||||
crate::syscall::debug_putchar(console, b);
|
||||
}
|
||||
}
|
||||
write_bytes(s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Bytes as they are to where `print!` goes: the log endpoint (the log
|
||||
/// server, or a terminal), else the console; nowhere without either.
|
||||
pub fn write_bytes(bytes: &[u8]) {
|
||||
// A call, not a send: the text is out before the caller goes on.
|
||||
if let Some(log) = crate::start::log() {
|
||||
for chunk in bytes.chunks(types::log::TEXT_BYTES) {
|
||||
let _ = crate::syscall::call(log, types::log::text_words(chunk), None);
|
||||
}
|
||||
} else if let Some(console) = crate::start::console() {
|
||||
for &b in bytes {
|
||||
crate::syscall::debug_putchar(console, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn _print(args: fmt::Arguments) {
|
||||
use fmt::Write;
|
||||
Writer.write_fmt(args).ok();
|
||||
|
||||
@@ -6,7 +6,9 @@ use crate::syscall;
|
||||
use types::address::{EntryPoint, StackPointer};
|
||||
use types::bounded::CapSlot;
|
||||
use types::budget::Limits;
|
||||
use types::permission::{Permission, PermissionSet};
|
||||
use types::spawn::{STACK_TOP, reply, request};
|
||||
use types::start_arg::{ARGS_SIZE, ARGS_VA, StartArg};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SpawnError {
|
||||
@@ -44,10 +46,46 @@ pub fn spawn(init: CapSlot, name: &[u8], limits: Limits) -> Result<Spawned, Spaw
|
||||
}
|
||||
|
||||
impl Spawned {
|
||||
/// Runs the program, telling it where its log endpoint is.
|
||||
pub fn start(&self) -> bool {
|
||||
let arg = types::start_arg::encode(None, self.log);
|
||||
/// The log endpoint init gave the program.
|
||||
pub fn log(&self) -> Option<CapSlot> {
|
||||
self.log
|
||||
}
|
||||
|
||||
/// A copy of `cap` for the program, with at most `perms` (and never
|
||||
/// Delegate: it is the program's, not to pass on); its slot there.
|
||||
pub fn give(&self, cap: CapSlot, perms: PermissionSet) -> Option<CapSlot> {
|
||||
syscall::delegate(cap, self.process, perms.without(Permission::Delegate))
|
||||
}
|
||||
|
||||
/// Puts `args` (its name first) where the program finds them: a page
|
||||
/// this process fills and then moves into the program, which owns it.
|
||||
pub fn set_args(&self, args: &[&str]) -> bool {
|
||||
let need: usize = args.iter().map(|a| a.len() + 1).sum::<usize>() + 1;
|
||||
if need > ARGS_SIZE || args.iter().any(|a| a.contains('\0')) {
|
||||
return false;
|
||||
}
|
||||
let Some(page) = types::bounded::PageCount::new(1).and_then(syscall::request_pages) else {
|
||||
return false;
|
||||
};
|
||||
// SAFETY: the page was just mapped read-write here, ARGS_SIZE bytes.
|
||||
let buf = unsafe { core::slice::from_raw_parts_mut(page.raw() as *mut u8, ARGS_SIZE) };
|
||||
buf.fill(0);
|
||||
let mut at = 0;
|
||||
for a in args {
|
||||
buf[at..at + a.len()].copy_from_slice(a.as_bytes());
|
||||
at += a.len() + 1;
|
||||
}
|
||||
syscall::map_page(self.process, page, types::address::VirtualAddress::from_raw(ARGS_VA), false)
|
||||
}
|
||||
|
||||
/// Runs the program with `arg` in its `a0` (see `types::start_arg`).
|
||||
pub fn start_with(&self, arg: StartArg) -> bool {
|
||||
let stack = StackPointer::from_raw(STACK_TOP);
|
||||
syscall::spawn_thread_in(self.process, self.entry, stack, arg) != usize::MAX
|
||||
syscall::spawn_thread_in(self.process, self.entry, stack, arg.encode()) != usize::MAX
|
||||
}
|
||||
|
||||
/// Runs the program, printing to the log init gave it.
|
||||
pub fn start(&self) -> bool {
|
||||
self.start_with(StartArg { log: self.log, ..StartArg::default() })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use types::bounded::CapSlot;
|
||||
use types::start_arg;
|
||||
use types::start_arg::{ARGS_SIZE, ARGS_VA, StartArg};
|
||||
|
||||
/// Set by `_start` from `a0` before `main` runs.
|
||||
#[unsafe(no_mangle)]
|
||||
@@ -20,7 +20,7 @@ fn arg() -> usize {
|
||||
|
||||
/// The console capability this process prints through, if any.
|
||||
pub fn console() -> Option<CapSlot> {
|
||||
read(&CONSOLE, start_arg::console)
|
||||
read(&CONSOLE, |a| StartArg::decode(a).console)
|
||||
}
|
||||
|
||||
/// For a process that gets its console some other way (init, from BootInfo).
|
||||
@@ -30,7 +30,25 @@ pub fn set_console(slot: Option<CapSlot>) {
|
||||
|
||||
/// The log server endpoint this process prints to, if any.
|
||||
pub fn log() -> Option<CapSlot> {
|
||||
read(&LOG, start_arg::log)
|
||||
read(&LOG, |a| StartArg::decode(a).log)
|
||||
}
|
||||
|
||||
/// The storage server endpoint this process was given, if any.
|
||||
pub fn storage() -> Option<CapSlot> {
|
||||
StartArg::decode(arg()).storage
|
||||
}
|
||||
|
||||
/// This program's arguments, its name first; none without an args page.
|
||||
pub fn args() -> impl Iterator<Item = &'static str> {
|
||||
let page: &'static [u8] = if StartArg::decode(arg()).args {
|
||||
// SAFETY: the args flag says the parent mapped this page here
|
||||
// before starting the program; nothing writes it afterwards.
|
||||
unsafe { core::slice::from_raw_parts(ARGS_VA as *const u8, ARGS_SIZE) }
|
||||
} else {
|
||||
&[]
|
||||
};
|
||||
let end = page.windows(2).position(|w| w == [0, 0]).map_or(0, |i| i + 1);
|
||||
page[..end].split(|b| *b == 0).filter(|a| !a.is_empty()).filter_map(|a| core::str::from_utf8(a).ok())
|
||||
}
|
||||
|
||||
/// For a process given its log endpoint after it started.
|
||||
|
||||
Reference in New Issue
Block a user