feat(kernel): console cap gates DebugPutchar, no uart fast path; spawn start arg in a0; delegate returns slot

This commit is contained in:
2026-10-27 14:54:36 +01:00
parent 33e1c849d1
commit 7415bab7f5
31 changed files with 290 additions and 64 deletions

View File

@@ -4,8 +4,11 @@ struct Writer;
impl fmt::Write for Writer {
fn write_str(&mut self, s: &str) -> fmt::Result {
for &b in s.as_bytes() {
crate::syscall::debug_putchar(b);
// Without a console capability, output goes nowhere.
if let Some(console) = crate::start::console() {
for &b in s.as_bytes() {
crate::syscall::debug_putchar(console, b);
}
}
Ok(())
}

View File

@@ -10,6 +10,7 @@ pub mod fs;
pub mod input;
pub mod io;
mod rt;
pub mod start;
pub mod syscall;
#[cfg(target_os = "none")]

View File

@@ -7,6 +7,8 @@ core::arch::global_asm!(
.section .text.entry
.globl _start
_start:
lla t0, LIBKOS_START_ARG
sd a0, 0(t0)
call main
li a7, 16
ecall

View File

@@ -0,0 +1,36 @@
//! What this process was handed at start (see `types::start_arg`).
use core::sync::atomic::{AtomicUsize, Ordering};
use types::bounded::CapSlot;
use types::start_arg;
/// Set by `_start` from `a0` before `main` runs.
#[unsafe(no_mangle)]
static LIBKOS_START_ARG: AtomicUsize = AtomicUsize::new(0);
/// Overrides the start argument's console once set; `NONE` drops output.
static CONSOLE: AtomicUsize = AtomicUsize::new(UNSET);
const UNSET: usize = usize::MAX;
const NONE: usize = usize::MAX - 1;
fn arg() -> usize {
LIBKOS_START_ARG.load(Ordering::Relaxed)
}
/// The console capability this process prints through, if any.
pub fn console() -> Option<CapSlot> {
match CONSOLE.load(Ordering::Relaxed) {
UNSET => start_arg::console(arg()),
v => CapSlot::new(u8::try_from(v).ok()?),
}
}
/// For a process that gets its console some other way (init, from BootInfo).
pub fn set_console(slot: Option<CapSlot>) {
CONSOLE.store(slot.map_or(NONE, |s| s.value() as usize), Ordering::Relaxed);
}
/// The log server endpoint this process was given, if any.
pub fn log() -> Option<CapSlot> {
start_arg::log(arg())
}

View File

@@ -8,8 +8,9 @@ use types::syscall_abi::*;
// ---- capabilities ----
pub fn delegate(slot: CapSlot, to: CapSlot, permissions: PermissionSet) -> bool {
raw(
/// Returns the slot the copy landed in, in `to`'s table.
pub fn delegate(slot: CapSlot, to: CapSlot, permissions: PermissionSet) -> Option<CapSlot> {
slot_of(raw(
SYS_DELEGATE,
[
slot.value() as usize,
@@ -19,8 +20,8 @@ pub fn delegate(slot: CapSlot, to: CapSlot, permissions: PermissionSet) -> bool
0,
0,
],
)
.is_ok()
))
.ok()
}
/// Copies an unbadged endpoint or notification capability with `badge`

View File

@@ -46,18 +46,19 @@ pub fn create_process(create_cap: CapSlot) -> Option<CapSlot> {
.ok()
}
pub fn spawn_thread(entry: EntryPoint, stack: StackPointer) -> usize {
raw(SYS_SPAWN_THREAD, [entry.raw(), stack.raw(), 0, 0, 0, 0]).map_or(NONE, |r| r[1])
/// `arg` arrives in the new thread's `a0` (see `libkos::start`).
pub fn spawn_thread(entry: EntryPoint, stack: StackPointer, arg: usize) -> usize {
raw(SYS_SPAWN_THREAD, [entry.raw(), stack.raw(), arg, 0, 0, 0]).map_or(NONE, |r| r[1])
}
pub fn spawn_thread_in(process_cap: CapSlot, entry: EntryPoint, stack: StackPointer) -> usize {
pub fn spawn_thread_in(process_cap: CapSlot, entry: EntryPoint, stack: StackPointer, arg: usize) -> usize {
raw(
SYS_SPAWN_THREAD_IN,
[
process_cap.value() as usize,
entry.raw(),
stack.raw(),
0,
arg,
0,
0,
],
@@ -69,6 +70,6 @@ pub fn kill(process_cap: CapSlot) -> bool {
raw(SYS_KILL, [process_cap.value() as usize, 0, 0, 0, 0, 0]).is_ok()
}
pub fn debug_putchar(byte: u8) {
let _ = raw(SYS_DEBUG_PUTCHAR, [byte as usize, 0, 0, 0, 0, 0]);
pub fn debug_putchar(console: CapSlot, byte: u8) {
let _ = raw(SYS_DEBUG_PUTCHAR, [console.value() as usize, byte as usize, 0, 0, 0, 0]);
}