feat(kernel): console cap gates DebugPutchar, no uart fast path; spawn start arg in a0; delegate returns slot
This commit is contained in:
@@ -105,3 +105,18 @@ pub fn grant_all(
|
|||||||
|
|
||||||
uart.write_str("k-os: capabilities granted\n");
|
uart.write_str("k-os: capabilities granted\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Until the log server owns the console, the servers loaded here print
|
||||||
|
/// through a console cap of their own, its slot passed in `a0`.
|
||||||
|
pub fn grant_console(uart: &Uart, servers: &[super::loader::LoadedProcess]) {
|
||||||
|
let write =
|
||||||
|
types::permission::PermissionSet::empty().with(types::permission::Permission::Write);
|
||||||
|
for server in servers {
|
||||||
|
let slot = kern()
|
||||||
|
.grant_capability(server.pid, domain::resource::Resource::Console, write)
|
||||||
|
.unwrap_or_else(|_| crate::halt(uart, "console grant failed"));
|
||||||
|
if let Some(ctx) = kern().thread_context_mut(server.tid) {
|
||||||
|
ctx.registers_mut()[10] = types::start_arg::encode(Some(slot), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ const USER_VA_BASE: usize = 0x10000;
|
|||||||
const USER_STACK_PAGES: usize = USER_VA_BASE / PAGE_SIZE - 1;
|
const USER_STACK_PAGES: usize = USER_VA_BASE / PAGE_SIZE - 1;
|
||||||
const MAX_FILE_SIZE: usize = 64 * 1024;
|
const MAX_FILE_SIZE: usize = 64 * 1024;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub struct LoadedProcess {
|
pub struct LoadedProcess {
|
||||||
pub pid: ProcessId,
|
pub pid: ProcessId,
|
||||||
|
pub tid: ThreadId,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kern() -> &'static mut kernel::Kernel<crate::platform::RiscVPlatform> {
|
fn kern() -> &'static mut kernel::Kernel<crate::platform::RiscVPlatform> {
|
||||||
@@ -35,7 +37,7 @@ fn elf_buffer() -> &'static mut [u8] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_init(uart: &Uart, blk: &mut VirtioBlk) -> (LoadedProcess, ThreadId) {
|
pub fn load_init(uart: &Uart, blk: &mut VirtioBlk) -> LoadedProcess {
|
||||||
let (pid, tid) = kern()
|
let (pid, tid) = kern()
|
||||||
.boot(
|
.boot(
|
||||||
types::bounded::Priority::DEFAULT,
|
types::bounded::Priority::DEFAULT,
|
||||||
@@ -47,7 +49,7 @@ pub fn load_init(uart: &Uart, blk: &mut VirtioBlk) -> (LoadedProcess, ThreadId)
|
|||||||
setup_thread(tid, entry);
|
setup_thread(tid, entry);
|
||||||
uart.write_str("k-os: init loaded\n");
|
uart.write_str("k-os: init loaded\n");
|
||||||
|
|
||||||
(LoadedProcess { pid }, tid)
|
LoadedProcess { pid, tid }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_server(
|
pub fn load_server(
|
||||||
@@ -68,7 +70,7 @@ pub fn load_server(
|
|||||||
uart.write_str(label);
|
uart.write_str(label);
|
||||||
uart.write_str(" loaded\n");
|
uart.write_str(" loaded\n");
|
||||||
|
|
||||||
LoadedProcess { pid }
|
LoadedProcess { pid, tid }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_thread(tid: ThreadId, entry: usize) {
|
fn setup_thread(tid: ThreadId, entry: usize) {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
use crate::hw::uart::Uart;
|
use crate::hw::uart::Uart;
|
||||||
use types::address::VirtualAddress;
|
use types::address::VirtualAddress;
|
||||||
use types::bootinfo::{BOOT_INFO_VA, BootDevice, BootInfo, MAGIC, MAX_BOOT_DEVICES, NO_SLOT};
|
use types::bootinfo::{BOOT_INFO_VA, BootDevice, BootInfo, MAGIC, MAX_BOOT_DEVICES, NO_SLOT};
|
||||||
use types::bounded::CapSlot;
|
|
||||||
use types::id::ProcessId;
|
use types::id::ProcessId;
|
||||||
use types::mapping::PagePerms;
|
use types::mapping::PagePerms;
|
||||||
|
|
||||||
@@ -29,7 +28,7 @@ pub(crate) fn boot_info(
|
|||||||
board: &[kernel::BoardDevice],
|
board: &[kernel::BoardDevice],
|
||||||
kinds: &[u64],
|
kinds: &[u64],
|
||||||
placed: &[Option<kernel::BoardCaps>],
|
placed: &[Option<kernel::BoardCaps>],
|
||||||
system_control: CapSlot,
|
boot_caps: kernel::BootCaps,
|
||||||
) {
|
) {
|
||||||
let empty = BootDevice {
|
let empty = BootDevice {
|
||||||
kind: 0,
|
kind: 0,
|
||||||
@@ -42,7 +41,8 @@ pub(crate) fn boot_info(
|
|||||||
let mut info = BootInfo {
|
let mut info = BootInfo {
|
||||||
magic: MAGIC,
|
magic: MAGIC,
|
||||||
device_count: 0,
|
device_count: 0,
|
||||||
system_control_slot: system_control.value() as u64,
|
system_control_slot: boot_caps.system_control.value() as u64,
|
||||||
|
console_slot: boot_caps.console.value() as u64,
|
||||||
devices: [empty; MAX_BOOT_DEVICES],
|
devices: [empty; MAX_BOOT_DEVICES],
|
||||||
};
|
};
|
||||||
for (((d, kind), caps), out) in board
|
for (((d, kind), caps), out) in board
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ extern "C" fn kmain(hart: usize) -> ! {
|
|||||||
let mut blk =
|
let mut blk =
|
||||||
boot::hardware::probe_block_device(&uart).unwrap_or_else(|| halt(&uart, "no block device"));
|
boot::hardware::probe_block_device(&uart).unwrap_or_else(|| halt(&uart, "no block device"));
|
||||||
|
|
||||||
let (init, _) = boot::loader::load_init(&uart, &mut blk);
|
let init = boot::loader::load_init(&uart, &mut blk);
|
||||||
let storage = boot::loader::load_server(&uart, &mut blk, b"storage.elf", "storage");
|
let storage = boot::loader::load_server(&uart, &mut blk, b"storage.elf", "storage");
|
||||||
let display = boot::loader::load_server(&uart, &mut blk, b"display.elf", "display");
|
let display = boot::loader::load_server(&uart, &mut blk, b"display.elf", "display");
|
||||||
let input = boot::loader::load_server(&uart, &mut blk, b"input.elf", "input");
|
let input = boot::loader::load_server(&uart, &mut blk, b"input.elf", "input");
|
||||||
@@ -50,19 +50,14 @@ extern "C" fn kmain(hart: usize) -> ! {
|
|||||||
.unwrap_or_else(|_| halt(&uart, "board registration failed"));
|
.unwrap_or_else(|_| halt(&uart, "board registration failed"));
|
||||||
devices::display_quota(&uart, display.pid);
|
devices::display_quota(&uart, display.pid);
|
||||||
boot::caps::grant_all(&uart, init.pid, storage.pid, display.pid, input.pid);
|
boot::caps::grant_all(&uart, init.pid, storage.pid, display.pid, input.pid);
|
||||||
|
// Last, so the servers' fixed slots stay where they expect them.
|
||||||
|
boot::caps::grant_console(&uart, &[storage, display, input]);
|
||||||
// After grant_all: init already relies on those slots.
|
// After grant_all: init already relies on those slots.
|
||||||
let mut placed = [None; board::BOARD_DEVICES];
|
let mut placed = [None; board::BOARD_DEVICES];
|
||||||
let system_control = kern_mut()
|
let boot_caps = kern_mut()
|
||||||
.grant_boot_authority(init.pid, &board, &mut placed)
|
.grant_boot_authority(init.pid, &board, &mut placed)
|
||||||
.unwrap_or_else(|_| halt(&uart, "boot authority failed"));
|
.unwrap_or_else(|_| halt(&uart, "boot authority failed"));
|
||||||
devices::boot_info(
|
devices::boot_info(&uart, init.pid, &board, &board::kinds(), &placed, boot_caps);
|
||||||
&uart,
|
|
||||||
init.pid,
|
|
||||||
&board,
|
|
||||||
&board::kinds(),
|
|
||||||
&placed,
|
|
||||||
system_control,
|
|
||||||
);
|
|
||||||
|
|
||||||
start_other_harts(&uart, hart);
|
start_other_harts(&uart, hart);
|
||||||
uart.write_str("k-os: entering userspace\n");
|
uart.write_str("k-os: entering userspace\n");
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use super::{TrapFrame, write_result};
|
|||||||
/// Syscalls answered without the kernel lock because they touch nothing it
|
/// Syscalls answered without the kernel lock because they touch nothing it
|
||||||
/// guards. A pending IPI or reschedule request forces the slow path.
|
/// guards. A pending IPI or reschedule request forces the slow path.
|
||||||
pub(super) fn fast_syscall(frame: &mut TrapFrame) -> bool {
|
pub(super) fn fast_syscall(frame: &mut TrapFrame) -> bool {
|
||||||
use types::syscall_abi::{SYS_DEBUG_PUTCHAR, SYS_GET_PROCESS_ID, SYS_GET_TIME, SYS_YIELD};
|
use types::syscall_abi::{SYS_GET_PROCESS_ID, SYS_GET_TIME, SYS_YIELD};
|
||||||
if crate::smp::has_pending_request() {
|
if crate::smp::has_pending_request() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -13,10 +13,6 @@ pub(super) fn fast_syscall(frame: &mut TrapFrame) -> bool {
|
|||||||
Some(pid) => kernel::SyscallResult::Value(pid),
|
Some(pid) => kernel::SyscallResult::Value(pid),
|
||||||
None => return false,
|
None => return false,
|
||||||
},
|
},
|
||||||
SYS_DEBUG_PUTCHAR => {
|
|
||||||
crate::hw::uart::Uart::new().put_char(frame.regs[10] as u8);
|
|
||||||
kernel::SyscallResult::Ok
|
|
||||||
}
|
|
||||||
SYS_YIELD if crate::smp::yield_is_noop() => kernel::SyscallResult::Ok,
|
SYS_YIELD if crate::smp::yield_is_noop() => kernel::SyscallResult::Ok,
|
||||||
_ => return false,
|
_ => return false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ extern "C" fn main() {
|
|||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
let top = StackPointer::from_raw(stack.raw() + 4096);
|
let top = StackPointer::from_raw(stack.raw() + 4096);
|
||||||
syscall::spawn_thread(EntryPoint::from_raw(spin as *const () as usize), top);
|
syscall::spawn_thread(EntryPoint::from_raw(spin as *const () as usize), top, 0);
|
||||||
}
|
}
|
||||||
spin();
|
spin();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,4 +10,6 @@ pub enum Resource {
|
|||||||
ProcessCreate,
|
ProcessCreate,
|
||||||
/// Machine-wide controls such as halting; given only to init at boot.
|
/// Machine-wide controls such as halting; given only to init at boot.
|
||||||
SystemControl,
|
SystemControl,
|
||||||
|
/// The debug console (UART); held by the log server.
|
||||||
|
Console,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,9 +93,11 @@ pub enum Syscall {
|
|||||||
DropCap {
|
DropCap {
|
||||||
slot: CapSlot,
|
slot: CapSlot,
|
||||||
},
|
},
|
||||||
|
/// `arg` arrives in the new thread's `a0`.
|
||||||
SpawnThread {
|
SpawnThread {
|
||||||
entry: EntryPoint,
|
entry: EntryPoint,
|
||||||
stack: StackPointer,
|
stack: StackPointer,
|
||||||
|
arg: usize,
|
||||||
},
|
},
|
||||||
ExitThread,
|
ExitThread,
|
||||||
RequestPages {
|
RequestPages {
|
||||||
@@ -114,7 +116,9 @@ pub enum Syscall {
|
|||||||
control: CapSlot,
|
control: CapSlot,
|
||||||
},
|
},
|
||||||
CreateEndpoint,
|
CreateEndpoint,
|
||||||
|
/// Writes one byte to the console; needs a Console capability.
|
||||||
DebugPutchar {
|
DebugPutchar {
|
||||||
|
console: CapSlot,
|
||||||
byte: u8,
|
byte: u8,
|
||||||
},
|
},
|
||||||
MapPage {
|
MapPage {
|
||||||
@@ -127,5 +131,6 @@ pub enum Syscall {
|
|||||||
process: CapSlot,
|
process: CapSlot,
|
||||||
entry: EntryPoint,
|
entry: EntryPoint,
|
||||||
stack: StackPointer,
|
stack: StackPointer,
|
||||||
|
arg: usize,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,15 @@ pub fn system_control() -> Option<CapSlot> {
|
|||||||
slot(info.system_control_slot)
|
slot(info.system_control_slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The console capability, until init hands it to the log server.
|
||||||
|
pub fn console() -> Option<CapSlot> {
|
||||||
|
let info = boot_info();
|
||||||
|
if info.magic != types::bootinfo::MAGIC {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
slot(info.console_slot)
|
||||||
|
}
|
||||||
|
|
||||||
/// Sorts the board's devices out.
|
/// Sorts the board's devices out.
|
||||||
pub fn discover() -> Devices {
|
pub fn discover() -> Devices {
|
||||||
let info = boot_info();
|
let info = boot_info();
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ const SERVERS: &[ServerInfo] = &[
|
|||||||
];
|
];
|
||||||
|
|
||||||
pub fn run() -> ! {
|
pub fn run() -> ! {
|
||||||
|
libkos::start::set_console(devices::console());
|
||||||
libkos::println!("init: started (pid={:#x})", syscall::get_process_id().raw());
|
libkos::println!("init: started (pid={:#x})", syscall::get_process_id().raw());
|
||||||
|
|
||||||
// Drivers wait for their device before serving, so this comes first:
|
// Drivers wait for their device before serving, so this comes first:
|
||||||
@@ -151,7 +152,7 @@ pub fn run() -> ! {
|
|||||||
|
|
||||||
if let Some(ep_cap) = syscall::create_endpoint() {
|
if let Some(ep_cap) = syscall::create_endpoint() {
|
||||||
syscall::delegate(ep_cap, loaded.process_cap, ipc_perms);
|
syscall::delegate(ep_cap, loaded.process_cap, ipc_perms);
|
||||||
syscall::spawn_thread_in(loaded.process_cap, loaded.entry_point, USER_STACK_TOP);
|
start(&loaded, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
libkos::println!("ok (entry={:#x})", loaded.entry_point.raw());
|
libkos::println!("ok (entry={:#x})", loaded.entry_point.raw());
|
||||||
@@ -171,7 +172,7 @@ pub fn run() -> ! {
|
|||||||
let client = PermissionSet::empty().with(Permission::Execute);
|
let client = PermissionSet::empty().with(Permission::Execute);
|
||||||
syscall::delegate(INPUT_EP, snake.process_cap, client);
|
syscall::delegate(INPUT_EP, snake.process_cap, client);
|
||||||
syscall::delegate(DISPLAY_EP, snake.process_cap, client);
|
syscall::delegate(DISPLAY_EP, snake.process_cap, client);
|
||||||
syscall::spawn_thread_in(snake.process_cap, snake.entry_point, USER_STACK_TOP);
|
start(&snake, false);
|
||||||
libkos::println!("ok");
|
libkos::println!("ok");
|
||||||
} else {
|
} else {
|
||||||
libkos::println!("SKIP");
|
libkos::println!("SKIP");
|
||||||
@@ -182,6 +183,16 @@ pub fn run() -> ! {
|
|||||||
idle();
|
idle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Starts `program`, lending it init's console if `console`.
|
||||||
|
pub(crate) fn start(program: &LoadedProcess, console: bool) {
|
||||||
|
let write = PermissionSet::empty().with(Permission::Write);
|
||||||
|
let slot = libkos::start::console()
|
||||||
|
.filter(|_| console)
|
||||||
|
.and_then(|c| syscall::delegate(c, program.process_cap, write));
|
||||||
|
let arg = types::start_arg::encode(slot, None);
|
||||||
|
syscall::spawn_thread_in(program.process_cap, program.entry_point, USER_STACK_TOP, arg);
|
||||||
|
}
|
||||||
|
|
||||||
/// A region of init's own to read program files into.
|
/// A region of init's own to read program files into.
|
||||||
fn scratch() -> Option<&'static mut [u8]> {
|
fn scratch() -> Option<&'static mut [u8]> {
|
||||||
let region = syscall::create_region(PageCount::new(SCRATCH_PAGES)?).ok()?;
|
let region = syscall::create_region(PageCount::new(SCRATCH_PAGES)?).ok()?;
|
||||||
|
|||||||
@@ -45,10 +45,11 @@ fn start_spinner(storage: &mut Storage, scratch: &mut [u8], page: CapSlot) -> Re
|
|||||||
.with(Permission::Read)
|
.with(Permission::Read)
|
||||||
.with(Permission::Write)
|
.with(Permission::Write)
|
||||||
.with(Permission::Delegate);
|
.with(Permission::Delegate);
|
||||||
if !syscall::delegate(page, spinner.process_cap, rw) {
|
// The page first: the spinner expects it in slot 0.
|
||||||
|
if syscall::delegate(page, spinner.process_cap, rw).is_none() {
|
||||||
return Err("cannot give the spinner its page");
|
return Err("cannot give the spinner its page");
|
||||||
}
|
}
|
||||||
syscall::spawn_thread_in(spinner.process_cap, spinner.entry_point, crate::USER_STACK_TOP);
|
crate::start(&spinner, true);
|
||||||
if !wait_until(3000, || counters().iter().all(|c| *c > 0)) {
|
if !wait_until(3000, || counters().iter().all(|c| *c > 0)) {
|
||||||
return Err("spinner threads did not all start");
|
return Err("spinner threads did not all start");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ pub fn decode(nr: usize, a: &[usize; 6]) -> Option<Syscall> {
|
|||||||
SYS_SPAWN_THREAD => Syscall::SpawnThread {
|
SYS_SPAWN_THREAD => Syscall::SpawnThread {
|
||||||
entry: EntryPoint::from_raw(a[0]),
|
entry: EntryPoint::from_raw(a[0]),
|
||||||
stack: StackPointer::from_raw(a[1]),
|
stack: StackPointer::from_raw(a[1]),
|
||||||
|
arg: a[2],
|
||||||
},
|
},
|
||||||
SYS_REQUEST_PAGES => Syscall::RequestPages {
|
SYS_REQUEST_PAGES => Syscall::RequestPages {
|
||||||
count: pages(a[0])?,
|
count: pages(a[0])?,
|
||||||
@@ -115,6 +116,7 @@ pub fn decode(nr: usize, a: &[usize; 6]) -> Option<Syscall> {
|
|||||||
process: slot(a[0])?,
|
process: slot(a[0])?,
|
||||||
entry: EntryPoint::from_raw(a[1]),
|
entry: EntryPoint::from_raw(a[1]),
|
||||||
stack: StackPointer::from_raw(a[2]),
|
stack: StackPointer::from_raw(a[2]),
|
||||||
|
arg: a[3],
|
||||||
},
|
},
|
||||||
SYS_CREATE_ENDPOINT => Syscall::CreateEndpoint,
|
SYS_CREATE_ENDPOINT => Syscall::CreateEndpoint,
|
||||||
SYS_CREATE_REGION => Syscall::CreateRegion {
|
SYS_CREATE_REGION => Syscall::CreateRegion {
|
||||||
@@ -128,7 +130,10 @@ pub fn decode(nr: usize, a: &[usize; 6]) -> Option<Syscall> {
|
|||||||
SYS_REGION_ADDRESS => Syscall::RegionAddress {
|
SYS_REGION_ADDRESS => Syscall::RegionAddress {
|
||||||
region: slot(a[0])?,
|
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 {
|
SYS_MAP_PAGE => Syscall::MapPage {
|
||||||
process: slot(a[0])?,
|
process: slot(a[0])?,
|
||||||
src_va: VirtualAddress::from_raw(a[1]),
|
src_va: VirtualAddress::from_raw(a[1]),
|
||||||
|
|||||||
@@ -20,6 +20,13 @@ pub struct BoardDevice {
|
|||||||
pub irq: Option<IrqId>,
|
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.
|
/// Where `grant_boot_authority` put one device's capabilities in init.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct BoardCaps {
|
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.
|
/// 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(
|
pub fn grant_boot_authority(
|
||||||
&mut self,
|
&mut self,
|
||||||
init: ProcessId,
|
init: ProcessId,
|
||||||
devices: &[BoardDevice],
|
devices: &[BoardDevice],
|
||||||
placed: &mut [Option<BoardCaps>],
|
placed: &mut [Option<BoardCaps>],
|
||||||
) -> Result<CapSlot, KernelError> {
|
) -> Result<BootCaps, KernelError> {
|
||||||
let delegate = PermissionSet::empty().with(Permission::Delegate);
|
let delegate = PermissionSet::empty().with(Permission::Delegate);
|
||||||
let control = delegate.with(Permission::Execute);
|
let control = delegate.with(Permission::Execute);
|
||||||
let mmio = delegate.with(Permission::Read).with(Permission::Write);
|
let mmio = delegate.with(Permission::Read).with(Permission::Write);
|
||||||
@@ -70,7 +78,10 @@ impl<P: Platform> Kernel<P> {
|
|||||||
*slot = Some(BoardCaps { region, irq });
|
*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`.
|
/// The device region that starts at `base`.
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ mod smp;
|
|||||||
pub mod syscall;
|
pub mod syscall;
|
||||||
mod teardown;
|
mod teardown;
|
||||||
|
|
||||||
pub use board::{BoardCaps, BoardDevice};
|
pub use board::{BoardCaps, BoardDevice, BootCaps};
|
||||||
pub use syscall::{SyscallOutcome, SyscallResult};
|
pub use syscall::{SyscallOutcome, SyscallResult};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -287,9 +287,10 @@ impl<P: Platform> Kernel<P> {
|
|||||||
Resource::Memory(id) => self.regions.generation(*id),
|
Resource::Memory(id) => self.regions.generation(*id),
|
||||||
Resource::Notification(id) => self.notifications.generation(*id),
|
Resource::Notification(id) => self.notifications.generation(*id),
|
||||||
Resource::Process(pid) => self.processes.generation(*pid),
|
Resource::Process(pid) => self.processes.generation(*pid),
|
||||||
Resource::Interrupt(_) | Resource::ProcessCreate | Resource::SystemControl => {
|
Resource::Interrupt(_)
|
||||||
Some(Generation::initial())
|
| 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 parent = self.cap_ref(caller_pid, slot);
|
||||||
let inserted = self.insert_derived(target_pid, copy, parent);
|
// The slot it landed in, so the delegator can tell the target.
|
||||||
if let Err(e) = inserted {
|
match self.insert_derived(target_pid, copy, parent) {
|
||||||
return SyscallOutcome::Complete(SyscallResult::Error(e));
|
Ok(slot) => SyscallOutcome::Complete(SyscallResult::Slot(slot)),
|
||||||
|
Err(e) => SyscallOutcome::Complete(SyscallResult::Error(e)),
|
||||||
}
|
}
|
||||||
|
|
||||||
SyscallOutcome::Complete(SyscallResult::Ok)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_revoke(
|
pub(crate) fn handle_revoke(
|
||||||
|
|||||||
@@ -9,12 +9,14 @@ impl<P: Platform> Kernel<P> {
|
|||||||
pid: types::id::ProcessId,
|
pid: types::id::ProcessId,
|
||||||
entry: types::address::EntryPoint,
|
entry: types::address::EntryPoint,
|
||||||
stack: types::address::StackPointer,
|
stack: types::address::StackPointer,
|
||||||
|
arg: usize,
|
||||||
) -> SyscallOutcome {
|
) -> SyscallOutcome {
|
||||||
use domain::thread::SavedContext;
|
use domain::thread::SavedContext;
|
||||||
|
|
||||||
let priority = types::bounded::Priority::DEFAULT;
|
let priority = types::bounded::Priority::DEFAULT;
|
||||||
let time_slice = types::time::TimeSlice::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
|
let tid = match self
|
||||||
.threads
|
.threads
|
||||||
@@ -87,6 +89,7 @@ impl<P: Platform> Kernel<P> {
|
|||||||
process_cap: types::bounded::CapSlot,
|
process_cap: types::bounded::CapSlot,
|
||||||
entry: types::address::EntryPoint,
|
entry: types::address::EntryPoint,
|
||||||
stack: types::address::StackPointer,
|
stack: types::address::StackPointer,
|
||||||
|
arg: usize,
|
||||||
) -> SyscallOutcome {
|
) -> SyscallOutcome {
|
||||||
use domain::thread::SavedContext;
|
use domain::thread::SavedContext;
|
||||||
|
|
||||||
@@ -111,7 +114,8 @@ impl<P: Platform> Kernel<P> {
|
|||||||
|
|
||||||
let priority = types::bounded::Priority::DEFAULT;
|
let priority = types::bounded::Priority::DEFAULT;
|
||||||
let time_slice = types::time::TimeSlice::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
|
let tid = match self
|
||||||
.threads
|
.threads
|
||||||
|
|||||||
@@ -80,7 +80,9 @@ impl<P: Platform> Kernel<P> {
|
|||||||
Syscall::ExitThread => self.handle_exit_thread(tid, pid),
|
Syscall::ExitThread => self.handle_exit_thread(tid, pid),
|
||||||
Syscall::RequestPages { count } => self.handle_request_pages(pid, count),
|
Syscall::RequestPages { count } => self.handle_request_pages(pid, count),
|
||||||
Syscall::ReleasePages { addr, count } => self.handle_release_pages(pid, addr, 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 } => {
|
Syscall::CreateProcess { create_cap, image } => {
|
||||||
self.handle_create_process(pid, 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::Halt { control } => self.handle_halt(pid, control),
|
||||||
Syscall::CreateEndpoint => self.handle_create_endpoint(pid),
|
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 {
|
Syscall::MapPage {
|
||||||
process,
|
process,
|
||||||
src_va,
|
src_va,
|
||||||
@@ -155,7 +160,8 @@ impl<P: Platform> Kernel<P> {
|
|||||||
process,
|
process,
|
||||||
entry,
|
entry,
|
||||||
stack,
|
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
|
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);
|
self.platform.debug_putchar(byte);
|
||||||
SyscallOutcome::Complete(SyscallResult::Ok)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Halts the machine for a holder of a SystemControl capability with
|
/// 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()) {
|
for (b, a) in before.iter().zip(after.iter()) {
|
||||||
assert!(!b || *a, "an existing slot was disturbed");
|
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]
|
#[test]
|
||||||
@@ -342,5 +343,52 @@ fn boot_authority_reports_where_each_cap_went() {
|
|||||||
Some(slot_holding(&k, init, Resource::Interrupt(d.irq.unwrap())))
|
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!(
|
assert!(matches!(
|
||||||
outcome,
|
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 {
|
let outcome = k.handle_syscall(Syscall::SpawnThread {
|
||||||
entry: EntryPoint::from_raw(0x1000),
|
entry: EntryPoint::from_raw(0x1000),
|
||||||
stack: StackPointer::from_raw(0x2000),
|
stack: StackPointer::from_raw(0x2000),
|
||||||
|
arg: 0,
|
||||||
});
|
});
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
outcome,
|
outcome,
|
||||||
@@ -19,11 +20,12 @@ fn spawn_thread_creates_ready_thread() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn spawn_thread_preserves_entry_and_stack() {
|
fn spawn_thread_preserves_entry_stack_and_arg() {
|
||||||
let (mut k, _) = kernel_with_process();
|
let (mut k, _) = kernel_with_process();
|
||||||
let tid = match k.handle_syscall(Syscall::SpawnThread {
|
let tid = match k.handle_syscall(Syscall::SpawnThread {
|
||||||
entry: EntryPoint::from_raw(0xDEAD),
|
entry: EntryPoint::from_raw(0xDEAD),
|
||||||
stack: StackPointer::from_raw(0xBEEF),
|
stack: StackPointer::from_raw(0xBEEF),
|
||||||
|
arg: 0x5107,
|
||||||
}) {
|
}) {
|
||||||
SyscallOutcome::Complete(SyscallResult::Value(v)) => ThreadId::from_raw(v as u16),
|
SyscallOutcome::Complete(SyscallResult::Value(v)) => ThreadId::from_raw(v as u16),
|
||||||
_ => panic!("expected Value"),
|
_ => panic!("expected Value"),
|
||||||
@@ -31,6 +33,11 @@ fn spawn_thread_preserves_entry_and_stack() {
|
|||||||
let ctx = k.thread_context(tid).unwrap();
|
let ctx = k.thread_context(tid).unwrap();
|
||||||
assert_eq!(ctx.program_counter(), 0xDEAD);
|
assert_eq!(ctx.program_counter(), 0xDEAD);
|
||||||
assert_eq!(ctx.registers()[2], 0xBEEF);
|
assert_eq!(ctx.registers()[2], 0xBEEF);
|
||||||
|
assert_eq!(
|
||||||
|
ctx.registers()[10],
|
||||||
|
0x5107,
|
||||||
|
"the start argument arrives in a0"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ struct Writer;
|
|||||||
|
|
||||||
impl fmt::Write for Writer {
|
impl fmt::Write for Writer {
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
// Without a console capability, output goes nowhere.
|
||||||
|
if let Some(console) = crate::start::console() {
|
||||||
for &b in s.as_bytes() {
|
for &b in s.as_bytes() {
|
||||||
crate::syscall::debug_putchar(b);
|
crate::syscall::debug_putchar(console, b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ pub mod fs;
|
|||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
mod rt;
|
mod rt;
|
||||||
|
pub mod start;
|
||||||
pub mod syscall;
|
pub mod syscall;
|
||||||
|
|
||||||
#[cfg(target_os = "none")]
|
#[cfg(target_os = "none")]
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ core::arch::global_asm!(
|
|||||||
.section .text.entry
|
.section .text.entry
|
||||||
.globl _start
|
.globl _start
|
||||||
_start:
|
_start:
|
||||||
|
lla t0, LIBKOS_START_ARG
|
||||||
|
sd a0, 0(t0)
|
||||||
call main
|
call main
|
||||||
li a7, 16
|
li a7, 16
|
||||||
ecall
|
ecall
|
||||||
|
|||||||
36
crates/libkos/src/start.rs
Normal file
36
crates/libkos/src/start.rs
Normal 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())
|
||||||
|
}
|
||||||
@@ -8,8 +8,9 @@ use types::syscall_abi::*;
|
|||||||
|
|
||||||
// ---- capabilities ----
|
// ---- capabilities ----
|
||||||
|
|
||||||
pub fn delegate(slot: CapSlot, to: CapSlot, permissions: PermissionSet) -> bool {
|
/// Returns the slot the copy landed in, in `to`'s table.
|
||||||
raw(
|
pub fn delegate(slot: CapSlot, to: CapSlot, permissions: PermissionSet) -> Option<CapSlot> {
|
||||||
|
slot_of(raw(
|
||||||
SYS_DELEGATE,
|
SYS_DELEGATE,
|
||||||
[
|
[
|
||||||
slot.value() as usize,
|
slot.value() as usize,
|
||||||
@@ -19,8 +20,8 @@ pub fn delegate(slot: CapSlot, to: CapSlot, permissions: PermissionSet) -> bool
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
)
|
))
|
||||||
.is_ok()
|
.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copies an unbadged endpoint or notification capability with `badge`
|
/// Copies an unbadged endpoint or notification capability with `badge`
|
||||||
|
|||||||
@@ -46,18 +46,19 @@ pub fn create_process(create_cap: CapSlot) -> Option<CapSlot> {
|
|||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn_thread(entry: EntryPoint, stack: StackPointer) -> usize {
|
/// `arg` arrives in the new thread's `a0` (see `libkos::start`).
|
||||||
raw(SYS_SPAWN_THREAD, [entry.raw(), stack.raw(), 0, 0, 0, 0]).map_or(NONE, |r| r[1])
|
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(
|
raw(
|
||||||
SYS_SPAWN_THREAD_IN,
|
SYS_SPAWN_THREAD_IN,
|
||||||
[
|
[
|
||||||
process_cap.value() as usize,
|
process_cap.value() as usize,
|
||||||
entry.raw(),
|
entry.raw(),
|
||||||
stack.raw(),
|
stack.raw(),
|
||||||
0,
|
arg,
|
||||||
0,
|
0,
|
||||||
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()
|
raw(SYS_KILL, [process_cap.value() as usize, 0, 0, 0, 0, 0]).is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug_putchar(byte: u8) {
|
pub fn debug_putchar(console: CapSlot, byte: u8) {
|
||||||
let _ = raw(SYS_DEBUG_PUTCHAR, [byte as usize, 0, 0, 0, 0, 0]);
|
let _ = raw(SYS_DEBUG_PUTCHAR, [console.value() as usize, byte as usize, 0, 0, 0, 0]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ pub struct BootInfo {
|
|||||||
pub magic: u64,
|
pub magic: u64,
|
||||||
pub device_count: u64,
|
pub device_count: u64,
|
||||||
pub system_control_slot: u64,
|
pub system_control_slot: u64,
|
||||||
|
pub console_slot: u64,
|
||||||
pub devices: [BootDevice; MAX_BOOT_DEVICES],
|
pub devices: [BootDevice; MAX_BOOT_DEVICES],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ pub mod mapping;
|
|||||||
pub mod permission;
|
pub mod permission;
|
||||||
#[cfg(kani)]
|
#[cfg(kani)]
|
||||||
pub mod proofs;
|
pub mod proofs;
|
||||||
|
pub mod start_arg;
|
||||||
pub mod storage;
|
pub mod storage;
|
||||||
pub mod syscall_abi;
|
pub mod syscall_abi;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|||||||
43
crates/types/src/start_arg.rs
Normal file
43
crates/types/src/start_arg.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
//! The word a process's first thread gets in `a0`: the slots of the
|
||||||
|
//! capabilities any process may be handed at start (console, log endpoint).
|
||||||
|
//! Each field is slot + 1 in one byte; 0 means none.
|
||||||
|
|
||||||
|
use crate::bounded::CapSlot;
|
||||||
|
|
||||||
|
const CONSOLE_SHIFT: u32 = 0;
|
||||||
|
const LOG_SHIFT: u32 = 8;
|
||||||
|
|
||||||
|
fn field(arg: usize, shift: u32) -> Option<CapSlot> {
|
||||||
|
let v = (arg >> shift) & 0xFF;
|
||||||
|
CapSlot::new(u8::try_from(v.checked_sub(1)?).ok()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn put(slot: Option<CapSlot>, shift: u32) -> usize {
|
||||||
|
slot.map_or(0, |s| (s.value() as usize + 1) << shift)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(console: Option<CapSlot>, log: Option<CapSlot>) -> usize {
|
||||||
|
put(console, CONSOLE_SHIFT) | put(log, LOG_SHIFT)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn console(arg: usize) -> Option<CapSlot> {
|
||||||
|
field(arg, CONSOLE_SHIFT)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log(arg: usize) -> Option<CapSlot> {
|
||||||
|
field(arg, LOG_SHIFT)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn slots_round_trip_and_zero_is_none() {
|
||||||
|
let (c, l) = (CapSlot::new(0), CapSlot::new(63));
|
||||||
|
let arg = encode(c, l);
|
||||||
|
assert_eq!((console(arg), log(arg)), (c, l));
|
||||||
|
assert_eq!((console(0), log(0)), (None, None));
|
||||||
|
assert_eq!(log(encode(c, None)), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
docs/ipc.md
15
docs/ipc.md
@@ -63,7 +63,17 @@ No process is privileged by its id. At boot the kernel gives init:
|
|||||||
- for every device in the board table, a capability to its MMIO region
|
- for every device in the board table, a capability to its MMIO region
|
||||||
(`Read`, `Write`, `Delegate`; never `Execute`) and, if it interrupts, one
|
(`Read`, `Write`, `Delegate`; never `Execute`) and, if it interrupts, one
|
||||||
to its IRQ line (`Execute`, `Delegate`);
|
to its IRQ line (`Execute`, `Delegate`);
|
||||||
- one `SystemControl` capability (`Execute`, `Delegate`).
|
- one `SystemControl` capability (`Execute`, `Delegate`);
|
||||||
|
- one `Console` capability (`Write`, `Delegate`).
|
||||||
|
|
||||||
|
`DebugPutchar(console, byte)` needs a `Console` capability with `Write`;
|
||||||
|
there is no other way to reach the UART. Init lends its console to the
|
||||||
|
servers it starts (snake gets none). Until the log server owns the console,
|
||||||
|
boot code also grants one to the servers it loads itself.
|
||||||
|
|
||||||
|
A thread made by `SpawnThread`/`SpawnThreadIn` starts with the caller's
|
||||||
|
`arg` in `a0`. A process's first thread reads it as `types::start_arg`:
|
||||||
|
the slots of the console and log capabilities it was handed.
|
||||||
|
|
||||||
The board capabilities come after the capabilities boot code hands out
|
The board capabilities come after the capabilities boot code hands out
|
||||||
for the built-in servers, so those slots are unchanged. Device regions
|
for the built-in servers, so those slots are unchanged. Device regions
|
||||||
@@ -75,7 +85,8 @@ program a device's DMA; pool regions are physically contiguous.
|
|||||||
|
|
||||||
Boot also maps one read-only page into init at `types::bootinfo::BOOT_INFO_VA`
|
Boot also maps one read-only page into init at `types::bootinfo::BOOT_INFO_VA`
|
||||||
listing, for each board device, its kind, physical base, IRQ line and the
|
listing, for each board device, its kind, physical base, IRQ line and the
|
||||||
slots holding its region and IRQ capabilities, plus the SystemControl slot.
|
slots holding its region and IRQ capabilities, plus the SystemControl and
|
||||||
|
Console slots.
|
||||||
It is the only page boot writes for userspace.
|
It is the only page boot writes for userspace.
|
||||||
|
|
||||||
## Devices
|
## Devices
|
||||||
|
|||||||
Reference in New Issue
Block a user