feat(userspace): log server sole console holder; init boots from manifest; adapter loads only init+storage
This commit is contained in:
115
crates/init/src/launch.rs
Normal file
115
crates/init/src/launch.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
//! Starting a manifest program: load it, hand it what the manifest says
|
||||
//! and nothing else, run it.
|
||||
|
||||
use libkos::device::Device as BoardDevice;
|
||||
use libkos::fs::Storage;
|
||||
use libkos::syscall;
|
||||
use types::address::StackPointer;
|
||||
use types::bounded::CapSlot;
|
||||
use types::permission::{Permission, PermissionSet};
|
||||
|
||||
use crate::devices::Devices;
|
||||
use crate::load::{self, LoadedProcess};
|
||||
use crate::manifest::{Device, Program};
|
||||
|
||||
const USER_STACK_TOP: StackPointer = StackPointer::from_raw(0x10000);
|
||||
const MAX_SERVED: usize = 8;
|
||||
|
||||
fn perms(list: &[Permission]) -> PermissionSet {
|
||||
list.iter().fold(PermissionSet::empty(), |s, p| s.with(*p))
|
||||
}
|
||||
|
||||
pub(crate) struct Launcher<'a> {
|
||||
storage: &'a mut Storage,
|
||||
scratch: &'a mut [u8],
|
||||
devices: Devices,
|
||||
/// The endpoint of every server started so far, by name.
|
||||
served: [Option<(&'static str, CapSlot)>; MAX_SERVED],
|
||||
}
|
||||
|
||||
impl<'a> Launcher<'a> {
|
||||
pub(crate) fn new(storage: &'a mut Storage, scratch: &'a mut [u8], devices: Devices) -> Self {
|
||||
Self { storage, scratch, devices, served: [None; MAX_SERVED] }
|
||||
}
|
||||
|
||||
pub(crate) fn storage(&mut self) -> (&mut Storage, &mut [u8]) {
|
||||
(self.storage, self.scratch)
|
||||
}
|
||||
|
||||
fn endpoint(&self, name: &str) -> Option<CapSlot> {
|
||||
self.served.iter().flatten().find(|(n, _)| *n == name).map(|(_, ep)| *ep)
|
||||
}
|
||||
|
||||
/// What became of the program's device, if it has one.
|
||||
pub(crate) fn launch(&mut self, p: &Program) -> Result<&'static str, &'static str> {
|
||||
let loaded =
|
||||
load::load_binary(self.storage, self.scratch, p.file, p.limits).ok_or("cannot load")?;
|
||||
let child = loaded.process_cap;
|
||||
let ep = match p.serves {
|
||||
true => {
|
||||
let ep = syscall::create_endpoint().ok_or("no endpoint")?;
|
||||
// Read to receive on it; init keeps the original.
|
||||
let server = perms(&[Permission::Read, Permission::Execute]);
|
||||
syscall::delegate(ep, child, server).ok_or("cannot give its endpoint")?;
|
||||
Some(ep)
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
// A client only calls: Execute, never Read, so it cannot pose as the
|
||||
// server and take other clients' requests.
|
||||
for name in p.uses {
|
||||
let ep = self.endpoint(name).ok_or("needs a server that is not running")?;
|
||||
syscall::delegate(ep, child, perms(&[Permission::Execute])).ok_or("cannot give an endpoint")?;
|
||||
}
|
||||
let console = libkos::start::console()
|
||||
.filter(|_| p.console)
|
||||
.and_then(|c| syscall::delegate(c, child, perms(&[Permission::Write])));
|
||||
start(&loaded, console);
|
||||
|
||||
let mut note = "";
|
||||
if let Some(ep) = ep {
|
||||
if let Some(free) = self.served.iter_mut().find(|s| s.is_none()) {
|
||||
*free = Some((p.name, ep));
|
||||
}
|
||||
if let Some(device) = p.device {
|
||||
note = self.give(device, ep);
|
||||
}
|
||||
if console.is_some() {
|
||||
hand_over_console(ep);
|
||||
note = ", console handed over";
|
||||
}
|
||||
}
|
||||
Ok(note)
|
||||
}
|
||||
|
||||
/// The driver waits for its device before serving, and runs without
|
||||
/// one (headless) if the board has none.
|
||||
fn give(&self, device: Device, ep: CapSlot) -> &'static str {
|
||||
let (dev, given, none): (BoardDevice, _, _) = match device {
|
||||
Device::Gpu => (self.devices.gpu, ", gpu handed over", ", no gpu"),
|
||||
Device::Input => (self.devices.input, ", input device handed over", ", no input device"),
|
||||
};
|
||||
match libkos::device::give_device(ep, dev).is_ok() && dev.mmio.is_some() {
|
||||
true => given,
|
||||
false => none,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// From here on init prints through the log server, like everyone else,
|
||||
/// and holds no console of its own.
|
||||
fn hand_over_console(log: CapSlot) {
|
||||
if let Some(console) = libkos::start::console() {
|
||||
syscall::drop_cap(console);
|
||||
}
|
||||
libkos::start::set_console(None);
|
||||
libkos::start::set_log(Some(log));
|
||||
}
|
||||
|
||||
/// Starts `program` with `console` and, once there is one, the log.
|
||||
pub(crate) fn start(program: &LoadedProcess, console: Option<CapSlot>) {
|
||||
let log = libkos::start::log()
|
||||
.and_then(|l| syscall::delegate(l, program.process_cap, perms(&[Permission::Execute])));
|
||||
let arg = types::start_arg::encode(console, log);
|
||||
syscall::spawn_thread_in(program.process_cap, program.entry_point, USER_STACK_TOP, arg);
|
||||
}
|
||||
@@ -3,224 +3,80 @@
|
||||
extern crate alloc;
|
||||
|
||||
mod devices;
|
||||
mod launch;
|
||||
mod load;
|
||||
mod manifest;
|
||||
mod selftest;
|
||||
|
||||
use types::address::{EntryPoint, StackPointer, VirtualAddress};
|
||||
use types::bounded::{CapSlot, PageCount};
|
||||
use types::budget::Limits;
|
||||
use types::permission::{Permission, PermissionSet};
|
||||
use libkos::fs::Storage;
|
||||
use libkos::syscall;
|
||||
use types::badge::Badge;
|
||||
use types::bounded::CapSlot;
|
||||
use types::permission::{Permission, PermissionSet};
|
||||
|
||||
const PAGE_SIZE: usize = 4096;
|
||||
const USER_CODE_BASE: usize = 0x10000;
|
||||
pub(crate) const USER_STACK_TOP: StackPointer = StackPointer::from_raw(USER_CODE_BASE);
|
||||
const USER_STACK_PAGES: usize = 4;
|
||||
/// Where a program file is read to before it is loaded (16 pages: the
|
||||
/// largest program init will load).
|
||||
const SCRATCH_VA: usize = 0x30000;
|
||||
const SCRATCH_PAGES: u16 = 16;
|
||||
/// Where the buffer storage shares with init is mapped.
|
||||
const STORAGE_BUF_VA: usize = 0x0180_0000;
|
||||
|
||||
const CREATE_CAP: CapSlot = CapSlot::const_new(0);
|
||||
/// Boot loads storage before init and leaves its endpoint here.
|
||||
const STORAGE_EP: CapSlot = CapSlot::const_new(2);
|
||||
const DISPLAY_EP: CapSlot = CapSlot::const_new(3);
|
||||
const INPUT_EP: CapSlot = CapSlot::const_new(5);
|
||||
|
||||
/// Reads all of `filename` into `scratch`; its size, or `None` if it is
|
||||
/// missing, unreadable or larger than `scratch`.
|
||||
fn read_file(storage: &mut Storage, scratch: &mut [u8], filename: &[u8]) -> Option<usize> {
|
||||
let handle = storage.open(filename).ok()?;
|
||||
let size = storage.file_size(handle).ok().filter(|s| *s <= scratch.len());
|
||||
let read = size.and_then(|s| storage.read(handle, &mut scratch[..s]).ok());
|
||||
let _ = storage.close(handle);
|
||||
read.filter(|r| Some(*r) == size)
|
||||
}
|
||||
|
||||
pub(crate) struct LoadedProcess {
|
||||
pub(crate) process_cap: CapSlot,
|
||||
pub(crate) entry_point: EntryPoint,
|
||||
}
|
||||
|
||||
pub(crate) fn load_binary(
|
||||
storage: &mut Storage,
|
||||
scratch: &mut [u8],
|
||||
filename: &[u8],
|
||||
limits: Limits,
|
||||
) -> Option<LoadedProcess> {
|
||||
let file_size = read_file(storage, scratch, filename)?;
|
||||
let elf_data = &scratch[..file_size];
|
||||
let elf_info = match elf::parse(elf_data) {
|
||||
Ok(info) => info,
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
let process_cap = syscall::create_process(CREATE_CAP, limits)?;
|
||||
|
||||
for segment in elf_info.segments() {
|
||||
let page_start = segment.vaddr & !0xFFF;
|
||||
let offset_in_page = segment.vaddr & 0xFFF;
|
||||
let total_size = offset_in_page + segment.mem_size;
|
||||
let seg_pages = (total_size + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
|
||||
let page_count = PageCount::new(seg_pages as u16)?;
|
||||
let local_va = syscall::request_pages(page_count)?;
|
||||
|
||||
let local_ptr = local_va.raw() as *mut u8;
|
||||
unsafe {
|
||||
core::ptr::write_bytes(local_ptr, 0, seg_pages * PAGE_SIZE);
|
||||
}
|
||||
|
||||
let copy_len = segment.file_size.min(segment.mem_size);
|
||||
let src = segment
|
||||
.file_offset
|
||||
.checked_add(copy_len)
|
||||
.and_then(|end| elf_data.get(segment.file_offset..end));
|
||||
if let Some(src) = src.filter(|_| copy_len > 0) {
|
||||
// SAFETY: the pages just requested hold seg_pages * PAGE_SIZE
|
||||
// bytes, which covers offset_in_page + copy_len.
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(src.as_ptr(), local_ptr.add(offset_in_page), copy_len);
|
||||
}
|
||||
}
|
||||
|
||||
for p in 0..seg_pages {
|
||||
let src_page = VirtualAddress::from_raw(local_va.raw() + p * PAGE_SIZE);
|
||||
let dst_page = VirtualAddress::from_raw(page_start + p * PAGE_SIZE);
|
||||
syscall::map_page(process_cap, src_page, dst_page, segment.flags.execute);
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..USER_STACK_PAGES {
|
||||
let page_count = PageCount::new(1)?;
|
||||
let stack_va = syscall::request_pages(page_count)?;
|
||||
let dst = VirtualAddress::from_raw(USER_CODE_BASE - (USER_STACK_PAGES - i) * PAGE_SIZE);
|
||||
syscall::map_page(process_cap, stack_va, dst, false);
|
||||
}
|
||||
|
||||
Some(LoadedProcess {
|
||||
process_cap,
|
||||
entry_point: EntryPoint::from_raw(elf_info.entry_point),
|
||||
})
|
||||
}
|
||||
|
||||
struct ServerInfo {
|
||||
filename: &'static [u8],
|
||||
name: &'static str,
|
||||
limits: Limits,
|
||||
}
|
||||
|
||||
/// Enough for a small program: its image, stack and a little heap.
|
||||
pub(crate) const SMALL: Limits = Limits {
|
||||
pages: 64,
|
||||
endpoints: 2,
|
||||
notifications: 2,
|
||||
regions: 2,
|
||||
};
|
||||
|
||||
const SERVERS: &[ServerInfo] = &[
|
||||
ServerInfo { filename: b"log.elf", name: "log", limits: SMALL },
|
||||
ServerInfo { filename: b"permis.elf", name: "permis", limits: SMALL },
|
||||
ServerInfo { filename: b"session.elf", name: "session", limits: SMALL },
|
||||
];
|
||||
|
||||
const SNAKE: Limits = Limits { pages: 128, ..SMALL };
|
||||
|
||||
pub fn run() -> ! {
|
||||
libkos::start::set_console(devices::console());
|
||||
libkos::println!("init: started (pid={:#x})", syscall::get_process_id().raw());
|
||||
|
||||
// Drivers wait for their device before serving, so this comes first:
|
||||
// storage needs its disk before init can load anything.
|
||||
// Storage waits for its disk before serving, so this comes first.
|
||||
let found = devices::discover();
|
||||
for (name, ep, dev) in [
|
||||
("disk", STORAGE_EP, found.disk),
|
||||
("gpu", DISPLAY_EP, found.gpu),
|
||||
("input", INPUT_EP, found.input),
|
||||
] {
|
||||
let given = libkos::device::give_device(ep, dev).is_ok() && dev.mmio.is_some();
|
||||
libkos::println!("init: {}: {}", name, if given { "handed to its driver" } else { "none" });
|
||||
}
|
||||
let disk = libkos::device::give_device(STORAGE_EP, found.disk).is_ok() && found.disk.mmio.is_some();
|
||||
libkos::println!("init: disk: {}", if disk { "handed to its driver" } else { "none" });
|
||||
|
||||
let Ok(mut storage) = Storage::attach(STORAGE_EP, STORAGE_BUF_VA) else {
|
||||
libkos::println!("init: cannot reach storage, nothing to load");
|
||||
idle();
|
||||
};
|
||||
let Some(scratch) = scratch() else {
|
||||
// Storage has nothing to print with yet; init speaks for it.
|
||||
libkos::println!("init: storage: up");
|
||||
let Some(scratch) = load::scratch() else {
|
||||
libkos::println!("init: no memory to load programs into");
|
||||
idle();
|
||||
};
|
||||
|
||||
let ipc_perms = PermissionSet::empty()
|
||||
.with(Permission::Read)
|
||||
.with(Permission::Execute);
|
||||
|
||||
for server in SERVERS {
|
||||
libkos::print!("init: loading {}... ", server.name);
|
||||
|
||||
let loaded = match load_binary(&mut storage, scratch, server.filename, server.limits) {
|
||||
Some(l) => l,
|
||||
None => {
|
||||
libkos::println!("SKIP");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(ep_cap) = syscall::create_endpoint() {
|
||||
syscall::delegate(ep_cap, loaded.process_cap, ipc_perms);
|
||||
start(&loaded, true);
|
||||
}
|
||||
|
||||
libkos::println!("ok (entry={:#x})", loaded.entry_point.raw());
|
||||
}
|
||||
let mut launcher = launch::Launcher::new(&mut storage, scratch, found);
|
||||
launch_all(&mut launcher, manifest::SERVERS);
|
||||
give_storage_the_log();
|
||||
|
||||
// The test image carries the SMP self-test; it runs instead of the
|
||||
// desktop and halts the machine.
|
||||
let (storage, scratch) = launcher.storage();
|
||||
if storage.open(b"smptest.elf").is_ok_and(|h| storage.close(h).is_ok()) {
|
||||
libkos::println!("init: running the SMP self-test");
|
||||
selftest::run(&mut storage, scratch);
|
||||
}
|
||||
|
||||
libkos::print!("init: loading snake... ");
|
||||
if let Some(snake) = load_binary(&mut storage, scratch, b"snake.elf", SNAKE) {
|
||||
// A client only calls: Execute, never Read, so it cannot pose as
|
||||
// the server and take other clients' requests.
|
||||
let client = PermissionSet::empty().with(Permission::Execute);
|
||||
syscall::delegate(INPUT_EP, snake.process_cap, client);
|
||||
syscall::delegate(DISPLAY_EP, snake.process_cap, client);
|
||||
start(&snake, false);
|
||||
libkos::println!("ok");
|
||||
} else {
|
||||
libkos::println!("SKIP");
|
||||
selftest::run(storage, scratch);
|
||||
}
|
||||
launch_all(&mut launcher, manifest::APPS);
|
||||
|
||||
libkos::println!("init: all loaded, supervising");
|
||||
|
||||
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);
|
||||
fn launch_all(launcher: &mut launch::Launcher, programs: &[manifest::Program]) {
|
||||
for p in programs {
|
||||
libkos::print!("init: loading {}... ", p.name);
|
||||
match launcher.launch(p) {
|
||||
Ok(note) => libkos::println!("ok{}", note),
|
||||
Err(why) => libkos::println!("SKIP ({})", why),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A region of init's own to read program files into.
|
||||
fn scratch() -> Option<&'static mut [u8]> {
|
||||
let region = syscall::create_region(PageCount::new(SCRATCH_PAGES)?).ok()?;
|
||||
syscall::map_region(
|
||||
region,
|
||||
VirtualAddress::from_raw(SCRATCH_VA),
|
||||
types::mapping::PagePerms::ReadWrite,
|
||||
)
|
||||
.ok()?;
|
||||
// SAFETY: the region is mapped read-write at SCRATCH_VA for good.
|
||||
Some(unsafe { core::slice::from_raw_parts_mut(SCRATCH_VA as *mut u8, SCRATCH_PAGES as usize * PAGE_SIZE) })
|
||||
/// Storage started before the log server; it gets a call-only copy of
|
||||
/// the log endpoint now (Delegate only to send it: transfer strips it).
|
||||
fn give_storage_the_log() {
|
||||
let Some(log) = libkos::start::log() else {
|
||||
return;
|
||||
};
|
||||
let call_only = PermissionSet::empty().with(Permission::Execute).with(Permission::Delegate);
|
||||
if let Ok(copy) = syscall::mint(log, Badge::NONE, call_only) {
|
||||
let _ = syscall::call(STORAGE_EP, [types::storage::op::SET_LOG, 0, 0, 0], Some(copy));
|
||||
syscall::drop_cap(copy);
|
||||
}
|
||||
}
|
||||
|
||||
/// Nothing to supervise yet: block on a notification nobody signals
|
||||
|
||||
106
crates/init/src/load.rs
Normal file
106
crates/init/src/load.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
//! Reading program files from storage and loading them into new processes.
|
||||
|
||||
use libkos::fs::Storage;
|
||||
use libkos::syscall;
|
||||
use types::address::{EntryPoint, VirtualAddress};
|
||||
use types::bounded::{CapSlot, PageCount};
|
||||
use types::budget::Limits;
|
||||
|
||||
const PAGE_SIZE: usize = 4096;
|
||||
const USER_CODE_BASE: usize = 0x10000;
|
||||
const USER_STACK_PAGES: usize = 4;
|
||||
const CREATE_CAP: CapSlot = CapSlot::const_new(0);
|
||||
/// Where a program file is read to before it is loaded (16 pages: the
|
||||
/// largest program init will load).
|
||||
const SCRATCH_VA: usize = 0x30000;
|
||||
const SCRATCH_PAGES: u16 = 16;
|
||||
|
||||
pub(crate) struct LoadedProcess {
|
||||
pub(crate) process_cap: CapSlot,
|
||||
pub(crate) entry_point: EntryPoint,
|
||||
}
|
||||
|
||||
/// Reads all of `filename` into `scratch`; its size, or `None` if it is
|
||||
/// missing, unreadable or larger than `scratch`.
|
||||
fn read_file(storage: &mut Storage, scratch: &mut [u8], filename: &[u8]) -> Option<usize> {
|
||||
let handle = storage.open(filename).ok()?;
|
||||
let size = storage.file_size(handle).ok().filter(|s| *s <= scratch.len());
|
||||
let read = size.and_then(|s| storage.read(handle, &mut scratch[..s]).ok());
|
||||
let _ = storage.close(handle);
|
||||
read.filter(|r| Some(*r) == size)
|
||||
}
|
||||
|
||||
pub(crate) fn load_binary(
|
||||
storage: &mut Storage,
|
||||
scratch: &mut [u8],
|
||||
filename: &[u8],
|
||||
limits: Limits,
|
||||
) -> Option<LoadedProcess> {
|
||||
let file_size = read_file(storage, scratch, filename)?;
|
||||
let elf_data = &scratch[..file_size];
|
||||
let elf_info = match elf::parse(elf_data) {
|
||||
Ok(info) => info,
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
let process_cap = syscall::create_process(CREATE_CAP, limits)?;
|
||||
|
||||
for segment in elf_info.segments() {
|
||||
let page_start = segment.vaddr & !0xFFF;
|
||||
let offset_in_page = segment.vaddr & 0xFFF;
|
||||
let total_size = offset_in_page + segment.mem_size;
|
||||
let seg_pages = (total_size + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
|
||||
let page_count = PageCount::new(seg_pages as u16)?;
|
||||
let local_va = syscall::request_pages(page_count)?;
|
||||
|
||||
let local_ptr = local_va.raw() as *mut u8;
|
||||
unsafe {
|
||||
core::ptr::write_bytes(local_ptr, 0, seg_pages * PAGE_SIZE);
|
||||
}
|
||||
|
||||
let copy_len = segment.file_size.min(segment.mem_size);
|
||||
let src = segment
|
||||
.file_offset
|
||||
.checked_add(copy_len)
|
||||
.and_then(|end| elf_data.get(segment.file_offset..end));
|
||||
if let Some(src) = src.filter(|_| copy_len > 0) {
|
||||
// SAFETY: the pages just requested hold seg_pages * PAGE_SIZE
|
||||
// bytes, which covers offset_in_page + copy_len.
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(src.as_ptr(), local_ptr.add(offset_in_page), copy_len);
|
||||
}
|
||||
}
|
||||
|
||||
for p in 0..seg_pages {
|
||||
let src_page = VirtualAddress::from_raw(local_va.raw() + p * PAGE_SIZE);
|
||||
let dst_page = VirtualAddress::from_raw(page_start + p * PAGE_SIZE);
|
||||
syscall::map_page(process_cap, src_page, dst_page, segment.flags.execute);
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..USER_STACK_PAGES {
|
||||
let page_count = PageCount::new(1)?;
|
||||
let stack_va = syscall::request_pages(page_count)?;
|
||||
let dst = VirtualAddress::from_raw(USER_CODE_BASE - (USER_STACK_PAGES - i) * PAGE_SIZE);
|
||||
syscall::map_page(process_cap, stack_va, dst, false);
|
||||
}
|
||||
|
||||
Some(LoadedProcess {
|
||||
process_cap,
|
||||
entry_point: EntryPoint::from_raw(elf_info.entry_point),
|
||||
})
|
||||
}
|
||||
|
||||
/// A region of init's own to read program files into.
|
||||
pub(crate) fn scratch() -> Option<&'static mut [u8]> {
|
||||
let region = syscall::create_region(PageCount::new(SCRATCH_PAGES)?).ok()?;
|
||||
syscall::map_region(
|
||||
region,
|
||||
VirtualAddress::from_raw(SCRATCH_VA),
|
||||
types::mapping::PagePerms::ReadWrite,
|
||||
)
|
||||
.ok()?;
|
||||
// SAFETY: the region is mapped read-write at SCRATCH_VA for good.
|
||||
Some(unsafe { core::slice::from_raw_parts_mut(SCRATCH_VA as *mut u8, SCRATCH_PAGES as usize * PAGE_SIZE) })
|
||||
}
|
||||
55
crates/init/src/manifest.rs
Normal file
55
crates/init/src/manifest.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
//! What init starts, in order: the system's boot policy, as data.
|
||||
|
||||
use types::budget::Limits;
|
||||
|
||||
/// A board device init hands to a program over its endpoint.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Device {
|
||||
Gpu,
|
||||
Input,
|
||||
}
|
||||
|
||||
pub struct Program {
|
||||
pub name: &'static str,
|
||||
pub file: &'static [u8],
|
||||
pub limits: Limits,
|
||||
/// Serves an endpoint, which it finds in slot 0.
|
||||
pub serves: bool,
|
||||
pub device: Option<Device>,
|
||||
/// Earlier programs' endpoints it may call, in its next slots.
|
||||
pub uses: &'static [&'static str],
|
||||
/// Takes the console over from init; everyone else prints through it.
|
||||
pub console: bool,
|
||||
}
|
||||
|
||||
/// Enough for a small program: its image, stack and a little heap.
|
||||
pub const SMALL: Limits = Limits {
|
||||
pages: 64,
|
||||
endpoints: 2,
|
||||
notifications: 2,
|
||||
regions: 2,
|
||||
};
|
||||
|
||||
const fn server(name: &'static str, file: &'static [u8], limits: Limits) -> Program {
|
||||
Program { name, file, limits, serves: true, device: None, uses: &[], console: false }
|
||||
}
|
||||
|
||||
/// Started on every boot. The log server comes first so the rest can print.
|
||||
pub const SERVERS: &[Program] = &[
|
||||
Program { console: true, ..server("log", b"log.elf", SMALL) },
|
||||
Program {
|
||||
device: Some(Device::Gpu),
|
||||
// The framebuffer alone is ~1000 pages at 1280x800.
|
||||
..server("display", b"display.elf", Limits { pages: 1280, regions: 4, ..SMALL })
|
||||
},
|
||||
Program { device: Some(Device::Input), ..server("input", b"input.elf", Limits { regions: 4, ..SMALL }) },
|
||||
server("permis", b"permis.elf", SMALL),
|
||||
server("session", b"session.elf", SMALL),
|
||||
];
|
||||
|
||||
/// The desktop; the test image runs the SMP self-test instead.
|
||||
pub const APPS: &[Program] = &[Program {
|
||||
serves: false,
|
||||
uses: &["input", "display"],
|
||||
..server("snake", b"snake.elf", Limits { pages: 128, ..SMALL })
|
||||
}];
|
||||
@@ -40,7 +40,7 @@ fn start_spinner(storage: &mut Storage, scratch: &mut [u8], page: CapSlot) -> Re
|
||||
for i in 0..=THREADS {
|
||||
word(i).store(0, Ordering::SeqCst);
|
||||
}
|
||||
let spinner = crate::load_binary(storage, scratch, b"smptest.elf", crate::SMALL).ok_or("cannot load smptest.elf")?;
|
||||
let spinner = crate::load::load_binary(storage, scratch, b"smptest.elf", crate::manifest::SMALL).ok_or("cannot load smptest.elf")?;
|
||||
let rw = PermissionSet::empty()
|
||||
.with(Permission::Read)
|
||||
.with(Permission::Write)
|
||||
@@ -49,7 +49,7 @@ fn start_spinner(storage: &mut Storage, scratch: &mut [u8], page: CapSlot) -> Re
|
||||
if syscall::delegate(page, spinner.process_cap, rw).is_none() {
|
||||
return Err("cannot give the spinner its page");
|
||||
}
|
||||
crate::start(&spinner, true);
|
||||
crate::launch::start(&spinner, None);
|
||||
if !wait_until(3000, || counters().iter().all(|c| *c > 0)) {
|
||||
return Err("spinner threads did not all start");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user