feat: proper IPC-based boot — init loads servers from storage via IPC, VirtIO GPU driver
Complete microkernel boot pipeline: adapter loads only init + storage + display from disk. Init bootstraps all other servers by reading binaries from the storage server via IPC, parsing ELF, creating processes, mapping pages, and spawning threads. New syscalls: - CreateEndpoint: userspace endpoint creation with full capability grant - DebugPutchar: debug output to UART from userspace - MapPage: cross-process page mapping (init loads binaries into children) - SpawnThreadIn: spawn thread in another process via capability Key fixes: - Per-process L1 page tables for VPN[2]=0 (prevents cross-process corruption) - Separate kernel_pt from page_tables[0] (init pid=0 overwrote kernel PT ref) - Thread Running state set in switch_to_next (syscalls rejected without it) - Pending IPC results applied during context switch - SIE disabled during boot (timer can't preempt into unready processes) - User heap VA starts at 0x40000 (avoids shared buffer at 0x30000) - RequestPages maps at user VAs via per-process heap allocator VirtIO GPU: - Adapter probes for GPU device, initializes resource/scanout/backing - Display server writes to mapped framebuffer, flushes via GPU virtqueue - 640x480 BGRA framebuffer with transfer-to-host and resource-flush Storage server: - Userspace VirtIO block driver using pre-mapped MMIO and DMA pages - FAT16 filesystem reader serving file-read requests via IPC - Shared buffer protocol: init writes filename, storage writes file data Echo app for IPC testing (adapter-loaded echo proved the pipeline end-to-end).
This commit is contained in:
@@ -1,120 +0,0 @@
|
||||
use crate::manifest::{BinarySource, BootManifest, EndpointBinding, HardwareCap};
|
||||
use crate::protocol_helpers;
|
||||
use crate::supervisor::Supervisor;
|
||||
use types::bounded::CapSlot;
|
||||
use types::id::ProcessId;
|
||||
use userspace::syscall;
|
||||
|
||||
const MAX_ENDPOINTS: usize = 16;
|
||||
const MAX_PIDS: usize = 16;
|
||||
|
||||
struct EndpointEntry {
|
||||
name: &'static str,
|
||||
slot: CapSlot,
|
||||
}
|
||||
|
||||
struct EndpointRegistry {
|
||||
entries: [Option<EndpointEntry>; MAX_ENDPOINTS],
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl EndpointRegistry {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
entries: [const { None }; MAX_ENDPOINTS],
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn register(&mut self, name: &'static str, slot: CapSlot) {
|
||||
if self.count < MAX_ENDPOINTS {
|
||||
self.entries[self.count] = Some(EndpointEntry { name, slot });
|
||||
self.count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn find(&self, name: &str) -> Option<CapSlot> {
|
||||
for entry in &self.entries {
|
||||
if let Some(e) = entry {
|
||||
if e.name == name {
|
||||
return Some(e.slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BootResult {
|
||||
pub storage_endpoint: CapSlot,
|
||||
pub create_cap: CapSlot,
|
||||
pub server_pids: [Option<ProcessId>; MAX_PIDS],
|
||||
pub server_count: usize,
|
||||
}
|
||||
|
||||
impl BootResult {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
storage_endpoint: CapSlot::new(0).unwrap_or_else(|| unreachable!()),
|
||||
create_cap: CapSlot::new(0).unwrap_or_else(|| unreachable!()),
|
||||
server_pids: [const { None }; MAX_PIDS],
|
||||
server_count: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn boot(manifest: &BootManifest, create_cap: CapSlot, result: &mut BootResult) {
|
||||
let mut endpoints = EndpointRegistry::new();
|
||||
result.create_cap = create_cap;
|
||||
result.storage_endpoint = create_cap;
|
||||
result.server_count = 0;
|
||||
|
||||
for (i, server) in manifest.servers.iter().enumerate() {
|
||||
let pid_raw = syscall::create_process(create_cap.value() as usize);
|
||||
let pid = ProcessId::from_raw(pid_raw as u16);
|
||||
if i < MAX_PIDS {
|
||||
result.server_pids[i] = Some(pid);
|
||||
}
|
||||
|
||||
for binding in server.endpoints {
|
||||
if let EndpointBinding::Own { name } = binding {
|
||||
if let Some(slot) = CapSlot::new(0) {
|
||||
endpoints.register(name, slot);
|
||||
if *name == "storage" {
|
||||
result.storage_endpoint = slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for hw_cap in server.hardware_caps {
|
||||
match hw_cap {
|
||||
HardwareCap::Framebuffer { .. } => {}
|
||||
HardwareCap::Irq { .. } => {}
|
||||
HardwareCap::Memory { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for server in manifest.servers {
|
||||
if let BinarySource::Storage { path } = server.binary {
|
||||
protocol_helpers::storage_write_binary(
|
||||
result.storage_endpoint,
|
||||
path.as_bytes(),
|
||||
&[],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
result.server_count = manifest.servers.len().min(MAX_PIDS);
|
||||
}
|
||||
|
||||
pub fn create_supervisor(manifest: &BootManifest, result: &BootResult) -> Supervisor {
|
||||
let mut supervisor = Supervisor::new(manifest, result.storage_endpoint, result.create_cap);
|
||||
for i in 0..result.server_count {
|
||||
if let Some(pid) = result.server_pids[i] {
|
||||
supervisor.register_pid(i, pid);
|
||||
}
|
||||
}
|
||||
supervisor
|
||||
}
|
||||
@@ -1,18 +1,174 @@
|
||||
#![no_std]
|
||||
|
||||
pub mod boot;
|
||||
pub mod manifest;
|
||||
pub mod protocol_helpers;
|
||||
pub mod shutdown;
|
||||
pub mod supervisor;
|
||||
use userspace::syscall;
|
||||
|
||||
pub fn run() -> ! {
|
||||
let create_cap = types::bounded::CapSlot::new(0)
|
||||
.unwrap_or_else(|| unreachable!());
|
||||
const PAGE_SIZE: usize = 4096;
|
||||
const USER_CODE_BASE: usize = 0x10000;
|
||||
const USER_STACK_TOP: usize = USER_CODE_BASE;
|
||||
const USER_STACK_PAGES: usize = 4;
|
||||
const CMD_READ_FILE: usize = 1;
|
||||
|
||||
let mut result = boot::BootResult::empty();
|
||||
boot::boot(&manifest::SNAKE_MANIFEST, create_cap, &mut result);
|
||||
const CREATE_CAP: usize = 0;
|
||||
const STORAGE_EP_CAP: usize = 2;
|
||||
|
||||
let mut sup = boot::create_supervisor(&manifest::SNAKE_MANIFEST, &result);
|
||||
sup.run(create_cap);
|
||||
fn debug_print(s: &[u8]) {
|
||||
for &b in s {
|
||||
syscall::debug_putchar(b);
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_hex(mut val: usize) {
|
||||
debug_print(b"0x");
|
||||
if val == 0 {
|
||||
syscall::debug_putchar(b'0');
|
||||
return;
|
||||
}
|
||||
let mut buf = [0u8; 16];
|
||||
let mut i = 0;
|
||||
while val > 0 {
|
||||
let d = (val & 0xF) as u8;
|
||||
buf[i] = if d < 10 { b'0' + d } else { b'a' + d - 10 };
|
||||
val >>= 4;
|
||||
i += 1;
|
||||
}
|
||||
while i > 0 {
|
||||
i -= 1;
|
||||
syscall::debug_putchar(buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
fn request_file(shared_buf: *mut u8, filename: &[u8]) -> usize {
|
||||
unsafe {
|
||||
for (i, &b) in filename.iter().enumerate() {
|
||||
core::ptr::write_volatile(shared_buf.add(i), b);
|
||||
}
|
||||
}
|
||||
syscall::send(STORAGE_EP_CAP, CMD_READ_FILE, filename.len(), 0, 0);
|
||||
let (file_size, _, _, _) = syscall::receive(STORAGE_EP_CAP);
|
||||
file_size
|
||||
}
|
||||
|
||||
struct LoadedProcess {
|
||||
process_cap: usize,
|
||||
entry_point: usize,
|
||||
}
|
||||
|
||||
fn load_binary(shared_buf: *mut u8, filename: &[u8]) -> Option<LoadedProcess> {
|
||||
let file_size = request_file(shared_buf, filename);
|
||||
if file_size == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let elf_data = unsafe { core::slice::from_raw_parts(shared_buf as *const u8, file_size) };
|
||||
let elf_info = match elf::parse(elf_data) {
|
||||
Ok(info) => info,
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
let process_cap = syscall::create_process(CREATE_CAP);
|
||||
if process_cap == usize::MAX {
|
||||
debug_print(b"create_process failed\n");
|
||||
return None;
|
||||
}
|
||||
|
||||
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 local_va = syscall::request_pages(seg_pages);
|
||||
if local_va == usize::MAX {
|
||||
debug_print(b"request_pages failed\n");
|
||||
return None;
|
||||
}
|
||||
|
||||
let local_ptr = local_va 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);
|
||||
if copy_len > 0 {
|
||||
let src = segment.file_offset;
|
||||
if src + copy_len <= file_size {
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(
|
||||
shared_buf.add(src),
|
||||
local_ptr.add(offset_in_page),
|
||||
copy_len,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for p in 0..seg_pages {
|
||||
let src_page = local_va + p * PAGE_SIZE;
|
||||
let dst_page = 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 stack_va = syscall::request_pages(1);
|
||||
if stack_va == usize::MAX { return None; }
|
||||
let dst = USER_STACK_TOP - (USER_STACK_PAGES - i) * PAGE_SIZE;
|
||||
syscall::map_page(process_cap, stack_va, dst, false);
|
||||
}
|
||||
|
||||
Some(LoadedProcess {
|
||||
process_cap,
|
||||
entry_point: elf_info.entry_point,
|
||||
})
|
||||
}
|
||||
|
||||
struct ServerInfo {
|
||||
filename: &'static [u8],
|
||||
name: &'static [u8],
|
||||
}
|
||||
|
||||
// display is loaded by the adapter (needs GPU MMIO access)
|
||||
const SERVERS: &[ServerInfo] = &[
|
||||
ServerInfo { filename: b"input.elf", name: b"input" },
|
||||
ServerInfo { filename: b"log.elf", name: b"log" },
|
||||
ServerInfo { filename: b"permis.elf", name: b"permis" },
|
||||
ServerInfo { filename: b"session.elf", name: b"session" },
|
||||
ServerInfo { filename: b"snake.elf", name: b"snake" },
|
||||
];
|
||||
|
||||
pub fn run(shared_buf_va: usize) -> ! {
|
||||
let shared_buf = shared_buf_va as *mut u8;
|
||||
|
||||
debug_print(b"init: started (pid=");
|
||||
debug_hex(syscall::get_process_id());
|
||||
debug_print(b")\n");
|
||||
|
||||
for server in SERVERS {
|
||||
debug_print(b"init: loading ");
|
||||
debug_print(server.name);
|
||||
debug_print(b"... ");
|
||||
|
||||
let loaded = match load_binary(shared_buf, server.filename) {
|
||||
Some(l) => l,
|
||||
None => {
|
||||
debug_print(b"SKIP\n");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let ep_cap = syscall::create_endpoint();
|
||||
let read_execute: usize = (1 << 0) | (1 << 2);
|
||||
syscall::delegate(ep_cap, loaded.process_cap, read_execute);
|
||||
syscall::spawn_thread_in(loaded.process_cap, loaded.entry_point, USER_STACK_TOP);
|
||||
|
||||
debug_print(b"ok (entry=");
|
||||
debug_hex(loaded.entry_point);
|
||||
debug_print(b")\n");
|
||||
}
|
||||
|
||||
debug_print(b"init: all loaded, supervising\n");
|
||||
|
||||
loop {
|
||||
syscall::yield_now();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
extern crate userspace_rt;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn main() {
|
||||
init::run();
|
||||
extern "C" fn main(shared_buf: usize) {
|
||||
init::run(shared_buf);
|
||||
}
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
use types::address::PhysicalAddress;
|
||||
use types::bounded::Priority;
|
||||
use types::id::IrqId;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum HardwareCap {
|
||||
Framebuffer { base: PhysicalAddress, pages: usize },
|
||||
Irq { irq: IrqId },
|
||||
Memory { base: PhysicalAddress, pages: usize },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum EndpointBinding {
|
||||
Own { name: &'static str },
|
||||
ConnectTo { server: &'static str, endpoint: &'static str },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum BinarySource {
|
||||
Ram { base: PhysicalAddress, size: usize },
|
||||
Storage { path: &'static str },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ServerEntry {
|
||||
pub name: &'static str,
|
||||
pub priority: Priority,
|
||||
pub memory_pages: usize,
|
||||
pub hardware_caps: &'static [HardwareCap],
|
||||
pub endpoints: &'static [EndpointBinding],
|
||||
pub restartable: bool,
|
||||
pub binary: BinarySource,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BootManifest {
|
||||
pub servers: &'static [ServerEntry],
|
||||
}
|
||||
|
||||
pub static SNAKE_MANIFEST: BootManifest = BootManifest {
|
||||
servers: &[
|
||||
ServerEntry {
|
||||
name: "log",
|
||||
priority: Priority::DEFAULT,
|
||||
memory_pages: 64,
|
||||
hardware_caps: &[],
|
||||
endpoints: &[EndpointBinding::Own { name: "log" }],
|
||||
restartable: true,
|
||||
binary: BinarySource::Storage { path: "/servers/log.elf" },
|
||||
},
|
||||
ServerEntry {
|
||||
name: "permission",
|
||||
priority: Priority::DEFAULT,
|
||||
memory_pages: 32,
|
||||
hardware_caps: &[],
|
||||
endpoints: &[EndpointBinding::Own { name: "permission" }],
|
||||
restartable: true,
|
||||
binary: BinarySource::Storage { path: "/servers/permission.elf" },
|
||||
},
|
||||
ServerEntry {
|
||||
name: "storage",
|
||||
priority: Priority::DEFAULT,
|
||||
memory_pages: 128,
|
||||
hardware_caps: &[],
|
||||
endpoints: &[EndpointBinding::Own { name: "storage" }],
|
||||
restartable: true,
|
||||
binary: BinarySource::Ram {
|
||||
base: PhysicalAddress::from_raw(0),
|
||||
size: 0,
|
||||
},
|
||||
},
|
||||
ServerEntry {
|
||||
name: "display",
|
||||
priority: Priority::DEFAULT,
|
||||
memory_pages: 256,
|
||||
hardware_caps: &[
|
||||
HardwareCap::Framebuffer {
|
||||
base: PhysicalAddress::from_raw(0x10008000),
|
||||
pages: 75,
|
||||
},
|
||||
],
|
||||
endpoints: &[EndpointBinding::Own { name: "display" }],
|
||||
restartable: true,
|
||||
binary: BinarySource::Storage { path: "/servers/display.elf" },
|
||||
},
|
||||
ServerEntry {
|
||||
name: "input",
|
||||
priority: Priority::DEFAULT,
|
||||
memory_pages: 32,
|
||||
hardware_caps: &[
|
||||
HardwareCap::Irq { irq: IrqId::from_raw(10) },
|
||||
],
|
||||
endpoints: &[EndpointBinding::Own { name: "input" }],
|
||||
restartable: true,
|
||||
binary: BinarySource::Storage { path: "/servers/input.elf" },
|
||||
},
|
||||
ServerEntry {
|
||||
name: "session",
|
||||
priority: Priority::DEFAULT,
|
||||
memory_pages: 64,
|
||||
hardware_caps: &[],
|
||||
endpoints: &[
|
||||
EndpointBinding::Own { name: "session" },
|
||||
EndpointBinding::ConnectTo { server: "display", endpoint: "display" },
|
||||
EndpointBinding::ConnectTo { server: "input", endpoint: "input" },
|
||||
EndpointBinding::ConnectTo { server: "storage", endpoint: "storage" },
|
||||
],
|
||||
restartable: true,
|
||||
binary: BinarySource::Storage { path: "/servers/session.elf" },
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,44 +0,0 @@
|
||||
use types::bounded::CapSlot;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct StorageHandle(u16);
|
||||
|
||||
impl StorageHandle {
|
||||
pub fn raw(&self) -> u16 { self.0 }
|
||||
}
|
||||
|
||||
fn pack_path(path: &[u8]) -> (usize, usize) {
|
||||
let mut w0 = [0u8; 8];
|
||||
let mut w1 = [0u8; 8];
|
||||
for (i, &b) in path.iter().enumerate() {
|
||||
if i < 8 { w0[i] = b; }
|
||||
else if i < 16 { w1[i - 8] = b; }
|
||||
}
|
||||
(usize::from_le_bytes(w0), usize::from_le_bytes(w1))
|
||||
}
|
||||
|
||||
pub fn storage_open(endpoint: CapSlot, path: &[u8]) -> Option<StorageHandle> {
|
||||
let (w0, w1) = pack_path(path);
|
||||
let _ = userspace::syscall::send(endpoint.value() as usize, 0, w0, w1, 0);
|
||||
let (tag, handle, _, _) = userspace::syscall::receive(endpoint.value() as usize);
|
||||
if tag == 0 {
|
||||
Some(StorageHandle(handle as u16))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn storage_close(endpoint: CapSlot, handle: StorageHandle) {
|
||||
let _ = userspace::syscall::send(
|
||||
endpoint.value() as usize,
|
||||
3,
|
||||
handle.raw() as usize,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn storage_write_binary(endpoint: CapSlot, path: &[u8], _binary: &[u8]) {
|
||||
let (w0, w1) = pack_path(path);
|
||||
let _ = userspace::syscall::send(endpoint.value() as usize, 5, w0, w1, 0);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
use types::bounded::CapSlot;
|
||||
|
||||
const SHUTDOWN_TIMEOUT_TICKS: usize = 50_000_000;
|
||||
const SHUTDOWN_SIGNAL: usize = 0xFFFF_FFFF;
|
||||
|
||||
/// Best-effort shutdown: notify all servers, wait briefly for them to flush, then halt.
|
||||
///
|
||||
/// Servers that receive the shutdown signal should flush state to storage and exit.
|
||||
/// We can't block on individual acks because a crashed server would deadlock us.
|
||||
/// Instead, we yield repeatedly until the timeout expires, giving servers time to finish.
|
||||
pub fn shutdown(server_endpoints: &[CapSlot], endpoint_count: usize) -> ! {
|
||||
for i in 0..endpoint_count {
|
||||
let ep = server_endpoints[i].value() as usize;
|
||||
userspace::syscall::send(ep, SHUTDOWN_SIGNAL, 0, 0, 0);
|
||||
}
|
||||
|
||||
let start = userspace::syscall::get_time();
|
||||
let deadline = start + SHUTDOWN_TIMEOUT_TICKS;
|
||||
|
||||
while userspace::syscall::get_time() < deadline {
|
||||
userspace::syscall::yield_now();
|
||||
}
|
||||
|
||||
userspace::syscall::halt();
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
use crate::manifest::{BinarySource, BootManifest};
|
||||
use types::bounded::CapSlot;
|
||||
use types::id::ProcessId;
|
||||
|
||||
const MAX_SERVERS: usize = 16;
|
||||
const MAX_RESTARTS: u8 = 5;
|
||||
const BACKOFF_TICKS: [usize; 5] = [0, 10_000_000, 50_000_000, 300_000_000, 300_000_000];
|
||||
|
||||
struct ServerState {
|
||||
name: &'static str,
|
||||
pid: Option<ProcessId>,
|
||||
restartable: bool,
|
||||
restart_count: u8,
|
||||
binary: BinarySource,
|
||||
}
|
||||
|
||||
pub struct Supervisor {
|
||||
servers: [Option<ServerState>; MAX_SERVERS],
|
||||
count: usize,
|
||||
storage_endpoint: CapSlot,
|
||||
create_cap: CapSlot,
|
||||
}
|
||||
|
||||
impl Supervisor {
|
||||
pub fn new(manifest: &BootManifest, storage_endpoint: CapSlot, create_cap: CapSlot) -> Self {
|
||||
let mut servers = [const { None }; MAX_SERVERS];
|
||||
for (i, entry) in manifest.servers.iter().enumerate() {
|
||||
if i < MAX_SERVERS {
|
||||
servers[i] = Some(ServerState {
|
||||
name: entry.name,
|
||||
pid: None,
|
||||
restartable: entry.restartable,
|
||||
restart_count: 0,
|
||||
binary: entry.binary,
|
||||
});
|
||||
}
|
||||
}
|
||||
Self {
|
||||
servers,
|
||||
count: manifest.servers.len().min(MAX_SERVERS),
|
||||
storage_endpoint,
|
||||
create_cap,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_pid(&mut self, index: usize, pid: ProcessId) {
|
||||
if let Some(state) = &mut self.servers[index] {
|
||||
state.pid = Some(pid);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn should_restart(&self, index: usize) -> bool {
|
||||
self.servers[index]
|
||||
.as_ref()
|
||||
.map(|s| s.restartable && s.restart_count < MAX_RESTARTS)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn find_by_pid(&self, pid: ProcessId) -> Option<usize> {
|
||||
for i in 0..self.count {
|
||||
if let Some(state) = &self.servers[i] {
|
||||
if state.pid == Some(pid) {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn backoff_delay(&self, restart_count: u8) -> usize {
|
||||
let idx = (restart_count as usize).min(BACKOFF_TICKS.len() - 1);
|
||||
BACKOFF_TICKS[idx]
|
||||
}
|
||||
|
||||
fn wait_ticks(&self, ticks: usize) {
|
||||
if ticks == 0 { return; }
|
||||
let start = userspace::syscall::get_time();
|
||||
let deadline = start + ticks;
|
||||
while userspace::syscall::get_time() < deadline {
|
||||
userspace::syscall::yield_now();
|
||||
}
|
||||
}
|
||||
|
||||
fn restart_server(&mut self, index: usize) {
|
||||
let (binary, restart_count) = match &self.servers[index] {
|
||||
Some(state) => (state.binary, state.restart_count),
|
||||
None => return,
|
||||
};
|
||||
|
||||
let delay = self.backoff_delay(restart_count);
|
||||
self.wait_ticks(delay);
|
||||
|
||||
match binary {
|
||||
BinarySource::Ram { .. } => {
|
||||
let new_pid = userspace::syscall::create_process(
|
||||
self.create_cap.value() as usize,
|
||||
);
|
||||
if let Some(state) = &mut self.servers[index] {
|
||||
state.pid = Some(ProcessId::from_raw(new_pid as u16));
|
||||
state.restart_count += 1;
|
||||
}
|
||||
}
|
||||
BinarySource::Storage { path } => {
|
||||
self.load_from_storage_and_restart(index, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_from_storage_and_restart(&mut self, index: usize, path: &str) {
|
||||
use crate::protocol_helpers;
|
||||
|
||||
let handle = protocol_helpers::storage_open(self.storage_endpoint, path.as_bytes());
|
||||
if handle.is_none() { return; }
|
||||
|
||||
let new_pid = userspace::syscall::create_process(
|
||||
self.create_cap.value() as usize,
|
||||
);
|
||||
|
||||
if let Some(h) = handle {
|
||||
protocol_helpers::storage_close(self.storage_endpoint, h);
|
||||
}
|
||||
|
||||
if let Some(state) = &mut self.servers[index] {
|
||||
state.pid = Some(ProcessId::from_raw(new_pid as u16));
|
||||
state.restart_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, fault_endpoint: CapSlot) -> ! {
|
||||
loop {
|
||||
let (crashed_pid_raw, _fault_code, _fault_addr, _fault_pc) =
|
||||
userspace::syscall::receive(fault_endpoint.value() as usize);
|
||||
|
||||
let crashed_pid = ProcessId::from_raw(crashed_pid_raw as u16);
|
||||
|
||||
if let Some(index) = self.find_by_pid(crashed_pid) {
|
||||
if self.should_restart(index) {
|
||||
self.restart_server(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user