feat(kernel): console cap gates DebugPutchar, no uart fast path; spawn start arg in a0; delegate returns slot
This commit is contained in:
@@ -103,6 +103,7 @@ pub fn decode(nr: usize, a: &[usize; 6]) -> Option<Syscall> {
|
||||
SYS_SPAWN_THREAD => Syscall::SpawnThread {
|
||||
entry: EntryPoint::from_raw(a[0]),
|
||||
stack: StackPointer::from_raw(a[1]),
|
||||
arg: a[2],
|
||||
},
|
||||
SYS_REQUEST_PAGES => Syscall::RequestPages {
|
||||
count: pages(a[0])?,
|
||||
@@ -115,6 +116,7 @@ pub fn decode(nr: usize, a: &[usize; 6]) -> Option<Syscall> {
|
||||
process: slot(a[0])?,
|
||||
entry: EntryPoint::from_raw(a[1]),
|
||||
stack: StackPointer::from_raw(a[2]),
|
||||
arg: a[3],
|
||||
},
|
||||
SYS_CREATE_ENDPOINT => Syscall::CreateEndpoint,
|
||||
SYS_CREATE_REGION => Syscall::CreateRegion {
|
||||
@@ -128,7 +130,10 @@ pub fn decode(nr: usize, a: &[usize; 6]) -> Option<Syscall> {
|
||||
SYS_REGION_ADDRESS => Syscall::RegionAddress {
|
||||
region: slot(a[0])?,
|
||||
},
|
||||
SYS_DEBUG_PUTCHAR => Syscall::DebugPutchar { byte: a[0] as u8 },
|
||||
SYS_DEBUG_PUTCHAR => Syscall::DebugPutchar {
|
||||
console: slot(a[0])?,
|
||||
byte: a[1] as u8,
|
||||
},
|
||||
SYS_MAP_PAGE => Syscall::MapPage {
|
||||
process: slot(a[0])?,
|
||||
src_va: VirtualAddress::from_raw(a[1]),
|
||||
|
||||
@@ -20,6 +20,13 @@ pub struct BoardDevice {
|
||||
pub irq: Option<IrqId>,
|
||||
}
|
||||
|
||||
/// Where `grant_boot_authority` put init's machine-wide capabilities.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct BootCaps {
|
||||
pub system_control: CapSlot,
|
||||
pub console: CapSlot,
|
||||
}
|
||||
|
||||
/// Where `grant_boot_authority` put one device's capabilities in init.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct BoardCaps {
|
||||
@@ -46,13 +53,14 @@ impl<P: Platform> Kernel<P> {
|
||||
}
|
||||
|
||||
/// Gives `init` (with Delegate) caps to every device region and IRQ, then SystemControl.
|
||||
/// Writes each device's slots into `placed` in order; returns the SystemControl slot.
|
||||
/// Writes each device's slots into `placed` in order; returns the SystemControl
|
||||
/// and Console slots.
|
||||
pub fn grant_boot_authority(
|
||||
&mut self,
|
||||
init: ProcessId,
|
||||
devices: &[BoardDevice],
|
||||
placed: &mut [Option<BoardCaps>],
|
||||
) -> Result<CapSlot, KernelError> {
|
||||
) -> Result<BootCaps, KernelError> {
|
||||
let delegate = PermissionSet::empty().with(Permission::Delegate);
|
||||
let control = delegate.with(Permission::Execute);
|
||||
let mmio = delegate.with(Permission::Read).with(Permission::Write);
|
||||
@@ -70,7 +78,10 @@ impl<P: Platform> Kernel<P> {
|
||||
*slot = Some(BoardCaps { region, irq });
|
||||
}
|
||||
}
|
||||
self.grant_cap(init, Resource::SystemControl, control)
|
||||
Ok(BootCaps {
|
||||
system_control: self.grant_cap(init, Resource::SystemControl, control)?,
|
||||
console: self.grant_cap(init, Resource::Console, delegate.with(Permission::Write))?,
|
||||
})
|
||||
}
|
||||
|
||||
/// The device region that starts at `base`.
|
||||
|
||||
@@ -25,7 +25,7 @@ mod smp;
|
||||
pub mod syscall;
|
||||
mod teardown;
|
||||
|
||||
pub use board::{BoardCaps, BoardDevice};
|
||||
pub use board::{BoardCaps, BoardDevice, BootCaps};
|
||||
pub use syscall::{SyscallOutcome, SyscallResult};
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -287,9 +287,10 @@ impl<P: Platform> Kernel<P> {
|
||||
Resource::Memory(id) => self.regions.generation(*id),
|
||||
Resource::Notification(id) => self.notifications.generation(*id),
|
||||
Resource::Process(pid) => self.processes.generation(*pid),
|
||||
Resource::Interrupt(_) | Resource::ProcessCreate | Resource::SystemControl => {
|
||||
Some(Generation::initial())
|
||||
}
|
||||
Resource::Interrupt(_)
|
||||
| Resource::ProcessCreate
|
||||
| Resource::SystemControl
|
||||
| Resource::Console => Some(Generation::initial()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,12 +43,11 @@ impl<P: Platform> Kernel<P> {
|
||||
};
|
||||
|
||||
let parent = self.cap_ref(caller_pid, slot);
|
||||
let inserted = self.insert_derived(target_pid, copy, parent);
|
||||
if let Err(e) = inserted {
|
||||
return SyscallOutcome::Complete(SyscallResult::Error(e));
|
||||
// The slot it landed in, so the delegator can tell the target.
|
||||
match self.insert_derived(target_pid, copy, parent) {
|
||||
Ok(slot) => SyscallOutcome::Complete(SyscallResult::Slot(slot)),
|
||||
Err(e) => SyscallOutcome::Complete(SyscallResult::Error(e)),
|
||||
}
|
||||
|
||||
SyscallOutcome::Complete(SyscallResult::Ok)
|
||||
}
|
||||
|
||||
pub(crate) fn handle_revoke(
|
||||
|
||||
@@ -9,12 +9,14 @@ impl<P: Platform> Kernel<P> {
|
||||
pid: types::id::ProcessId,
|
||||
entry: types::address::EntryPoint,
|
||||
stack: types::address::StackPointer,
|
||||
arg: usize,
|
||||
) -> SyscallOutcome {
|
||||
use domain::thread::SavedContext;
|
||||
|
||||
let priority = types::bounded::Priority::DEFAULT;
|
||||
let time_slice = types::time::TimeSlice::DEFAULT;
|
||||
let context = SavedContext::with_entry_and_stack(entry, stack);
|
||||
let mut context = SavedContext::with_entry_and_stack(entry, stack);
|
||||
context.registers_mut()[10] = arg;
|
||||
|
||||
let tid = match self
|
||||
.threads
|
||||
@@ -87,6 +89,7 @@ impl<P: Platform> Kernel<P> {
|
||||
process_cap: types::bounded::CapSlot,
|
||||
entry: types::address::EntryPoint,
|
||||
stack: types::address::StackPointer,
|
||||
arg: usize,
|
||||
) -> SyscallOutcome {
|
||||
use domain::thread::SavedContext;
|
||||
|
||||
@@ -111,7 +114,8 @@ impl<P: Platform> Kernel<P> {
|
||||
|
||||
let priority = types::bounded::Priority::DEFAULT;
|
||||
let time_slice = types::time::TimeSlice::DEFAULT;
|
||||
let context = SavedContext::with_entry_and_stack(entry, stack);
|
||||
let mut context = SavedContext::with_entry_and_stack(entry, stack);
|
||||
context.registers_mut()[10] = arg;
|
||||
|
||||
let tid = match self
|
||||
.threads
|
||||
|
||||
@@ -80,7 +80,9 @@ impl<P: Platform> Kernel<P> {
|
||||
Syscall::ExitThread => self.handle_exit_thread(tid, pid),
|
||||
Syscall::RequestPages { count } => self.handle_request_pages(pid, count),
|
||||
Syscall::ReleasePages { addr, count } => self.handle_release_pages(pid, addr, count),
|
||||
Syscall::SpawnThread { entry, stack } => self.handle_spawn_thread(pid, entry, stack),
|
||||
Syscall::SpawnThread { entry, stack, arg } => {
|
||||
self.handle_spawn_thread(pid, entry, stack, arg)
|
||||
}
|
||||
Syscall::CreateProcess { create_cap, image } => {
|
||||
self.handle_create_process(pid, create_cap, image)
|
||||
}
|
||||
@@ -144,7 +146,10 @@ impl<P: Platform> Kernel<P> {
|
||||
}
|
||||
Syscall::Halt { control } => self.handle_halt(pid, control),
|
||||
Syscall::CreateEndpoint => self.handle_create_endpoint(pid),
|
||||
Syscall::DebugPutchar { byte } => self.handle_debug_putchar(byte),
|
||||
Syscall::DebugPutchar { console, byte } => complete(
|
||||
self.debug_putchar(pid, console, byte)
|
||||
.map(|()| SyscallResult::Ok),
|
||||
),
|
||||
Syscall::MapPage {
|
||||
process,
|
||||
src_va,
|
||||
@@ -155,7 +160,8 @@ impl<P: Platform> Kernel<P> {
|
||||
process,
|
||||
entry,
|
||||
stack,
|
||||
} => self.handle_spawn_thread_in(pid, process, entry, stack),
|
||||
arg,
|
||||
} => self.handle_spawn_thread_in(pid, process, entry, stack, arg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,9 +84,18 @@ impl<P: Platform> Kernel<P> {
|
||||
SyscallOutcome::TerminateCaller
|
||||
}
|
||||
|
||||
pub(crate) fn handle_debug_putchar(&self, byte: u8) -> SyscallOutcome {
|
||||
/// Console output is a capability like any other hardware access.
|
||||
pub(crate) fn debug_putchar(
|
||||
&self,
|
||||
pid: ProcessId,
|
||||
console: CapSlot,
|
||||
byte: u8,
|
||||
) -> Result<(), KernelError> {
|
||||
if *self.check_cap(pid, console, Permission::Write)?.resource() != Resource::Console {
|
||||
return Err(KernelError::WrongObject);
|
||||
}
|
||||
self.platform.debug_putchar(byte);
|
||||
SyscallOutcome::Complete(SyscallResult::Ok)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Halts the machine for a holder of a SystemControl capability with
|
||||
|
||||
@@ -154,7 +154,8 @@ fn boot_authority_keeps_existing_slots() {
|
||||
for (b, a) in before.iter().zip(after.iter()) {
|
||||
assert!(!b || *a, "an existing slot was disturbed");
|
||||
}
|
||||
assert_eq!(after.iter().filter(|s| **s).count(), 1 + 2 + 2 + 1);
|
||||
// ProcessCreate, two devices with an IRQ each, SystemControl, Console.
|
||||
assert_eq!(after.iter().filter(|s| **s).count(), 1 + 2 + 2 + 1 + 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -342,5 +343,52 @@ fn boot_authority_reports_where_each_cap_went() {
|
||||
Some(slot_holding(&k, init, Resource::Interrupt(d.irq.unwrap())))
|
||||
);
|
||||
}
|
||||
assert_eq!(control, slot_holding(&k, init, Resource::SystemControl));
|
||||
assert_eq!(
|
||||
control.system_control,
|
||||
slot_holding(&k, init, Resource::SystemControl)
|
||||
);
|
||||
assert_eq!(control.console, slot_holding(&k, init, Resource::Console));
|
||||
}
|
||||
|
||||
// ---- console ----
|
||||
|
||||
#[test]
|
||||
fn printing_needs_a_console_cap() {
|
||||
let (mut k, init) = kernel_with_process();
|
||||
let none = k.handle_syscall(Syscall::DebugPutchar {
|
||||
console: CapSlot::new(9).unwrap(),
|
||||
byte: b'x',
|
||||
});
|
||||
assert!(matches!(error_of(none), KernelError::InvalidSlot(_)));
|
||||
let not_console = k.grant_cap_for_test(
|
||||
init,
|
||||
Resource::SystemControl,
|
||||
PermissionSet::empty().with(Permission::Write),
|
||||
);
|
||||
let wrong = k.handle_syscall(Syscall::DebugPutchar {
|
||||
console: not_console,
|
||||
byte: b'x',
|
||||
});
|
||||
assert!(matches!(error_of(wrong), KernelError::WrongObject));
|
||||
let read_only = k.grant_cap_for_test(
|
||||
init,
|
||||
Resource::Console,
|
||||
PermissionSet::empty().with(Permission::Read),
|
||||
);
|
||||
let ro = k.handle_syscall(Syscall::DebugPutchar {
|
||||
console: read_only,
|
||||
byte: b'x',
|
||||
});
|
||||
assert!(matches!(error_of(ro), KernelError::PermissionDenied { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_boot_console_cap_prints() {
|
||||
let (mut k, init) = booted();
|
||||
let console = slot_holding(&k, init, Resource::Console);
|
||||
let out = k.handle_syscall(Syscall::DebugPutchar {
|
||||
console,
|
||||
byte: b'x',
|
||||
});
|
||||
assert!(matches!(out, SyscallOutcome::Complete(SyscallResult::Ok)));
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ fn delegate_with_valid_cap_succeeds() {
|
||||
});
|
||||
assert!(matches!(
|
||||
outcome,
|
||||
SyscallOutcome::Complete(SyscallResult::Ok)
|
||||
SyscallOutcome::Complete(SyscallResult::Slot(_))
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ fn spawn_thread_creates_ready_thread() {
|
||||
let outcome = k.handle_syscall(Syscall::SpawnThread {
|
||||
entry: EntryPoint::from_raw(0x1000),
|
||||
stack: StackPointer::from_raw(0x2000),
|
||||
arg: 0,
|
||||
});
|
||||
assert!(matches!(
|
||||
outcome,
|
||||
@@ -19,11 +20,12 @@ fn spawn_thread_creates_ready_thread() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_thread_preserves_entry_and_stack() {
|
||||
fn spawn_thread_preserves_entry_stack_and_arg() {
|
||||
let (mut k, _) = kernel_with_process();
|
||||
let tid = match k.handle_syscall(Syscall::SpawnThread {
|
||||
entry: EntryPoint::from_raw(0xDEAD),
|
||||
stack: StackPointer::from_raw(0xBEEF),
|
||||
arg: 0x5107,
|
||||
}) {
|
||||
SyscallOutcome::Complete(SyscallResult::Value(v)) => ThreadId::from_raw(v as u16),
|
||||
_ => panic!("expected Value"),
|
||||
@@ -31,6 +33,11 @@ fn spawn_thread_preserves_entry_and_stack() {
|
||||
let ctx = k.thread_context(tid).unwrap();
|
||||
assert_eq!(ctx.program_counter(), 0xDEAD);
|
||||
assert_eq!(ctx.registers()[2], 0xBEEF);
|
||||
assert_eq!(
|
||||
ctx.registers()[10],
|
||||
0x5107,
|
||||
"the start argument arrives in a0"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user