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:
@@ -135,7 +135,7 @@ pub fn init() {
|
||||
let addr = trap_vector as usize;
|
||||
core::arch::asm!("csrw stvec, {0}", in(reg) addr);
|
||||
core::arch::asm!("csrw sscratch, zero");
|
||||
core::arch::asm!("csrs sstatus, {0}", in(reg) 1usize << 1);
|
||||
// SIE stays clear — interrupts are enabled only when entering userspace via sret
|
||||
let sie_bits = (1usize << 5) | (1usize << 9) | (1usize << 1);
|
||||
core::arch::asm!("csrs sie, {0}", in(reg) sie_bits);
|
||||
}
|
||||
@@ -230,6 +230,25 @@ fn decode_syscall(frame: &TrapFrame) -> Option<domain::syscall::Syscall> {
|
||||
count,
|
||||
})
|
||||
}
|
||||
SYS_SPAWN_THREAD_IN => {
|
||||
let slot = types::bounded::CapSlot::new(a0 as u8)?;
|
||||
Some(Syscall::SpawnThreadIn {
|
||||
process: slot,
|
||||
entry: types::address::EntryPoint::from_raw(a1),
|
||||
stack: types::address::StackPointer::from_raw(a2),
|
||||
})
|
||||
}
|
||||
SYS_CREATE_ENDPOINT => Some(Syscall::CreateEndpoint),
|
||||
SYS_DEBUG_PUTCHAR => Some(Syscall::DebugPutchar { byte: a0 as u8 }),
|
||||
SYS_MAP_PAGE => {
|
||||
let slot = types::bounded::CapSlot::new(a0 as u8)?;
|
||||
Some(Syscall::MapPage {
|
||||
process: slot,
|
||||
src_va: types::address::VirtualAddress::from_raw(a1),
|
||||
dst_va: types::address::VirtualAddress::from_raw(a2),
|
||||
executable: a3 != 0,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -272,6 +291,30 @@ fn load_context_to_frame(ctx: &domain::thread::SavedContext, frame: &mut TrapFra
|
||||
frame.sstatus |= 1 << 5; // set SPIE
|
||||
}
|
||||
|
||||
fn apply_pending_result(frame: &mut TrapFrame, tid: types::id::ThreadId) {
|
||||
let kern = crate::kernel_ref();
|
||||
if let Some(result) = kern.take_pending_result(tid) {
|
||||
write_result_direct(frame, &result);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_result_direct(frame: &mut TrapFrame, result: &kernel::SyscallResult) {
|
||||
use kernel::SyscallResult;
|
||||
match result {
|
||||
SyscallResult::Ok => frame.regs[10] = 0,
|
||||
SyscallResult::Value(v) => frame.regs[10] = *v,
|
||||
SyscallResult::Slot(s) => frame.regs[10] = s.value() as usize,
|
||||
SyscallResult::Message(m) => {
|
||||
frame.regs[10] = m.word(0);
|
||||
frame.regs[11] = m.word(1);
|
||||
frame.regs[12] = m.word(2);
|
||||
frame.regs[13] = m.word(3);
|
||||
}
|
||||
SyscallResult::Error(_) => frame.regs[10] = usize::MAX,
|
||||
SyscallResult::WakeReason(_) => frame.regs[10] = 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn switch_to_thread(frame: &mut TrapFrame, current_tid: types::id::ThreadId, next_tid: types::id::ThreadId) {
|
||||
let kern = crate::kernel_ref();
|
||||
|
||||
@@ -286,6 +329,8 @@ fn switch_to_thread(frame: &mut TrapFrame, current_tid: types::id::ThreadId, nex
|
||||
load_context_to_frame(ctx, frame);
|
||||
}
|
||||
|
||||
apply_pending_result(frame, next_tid);
|
||||
|
||||
if next_pid != current_pid {
|
||||
if let Some(pid) = next_pid {
|
||||
let pt_addr = kern.platform_mut().page_table_for(pid);
|
||||
@@ -308,10 +353,15 @@ fn switch_to_next(frame: &mut TrapFrame, current_tid: types::id::ThreadId) {
|
||||
let next_pid = kern.thread_process(next_tid);
|
||||
let current_pid = kern.thread_process(current_tid);
|
||||
|
||||
let core = types::id::CoreId::from_raw(0);
|
||||
let _ = kern.set_thread_running(next_tid, core);
|
||||
|
||||
if let Some(ctx) = kern.thread_context(next_tid) {
|
||||
load_context_to_frame(ctx, frame);
|
||||
}
|
||||
|
||||
apply_pending_result(frame, next_tid);
|
||||
|
||||
if next_pid != current_pid {
|
||||
if let Some(pid) = next_pid {
|
||||
let pt_addr = kern.platform_mut().page_table_for(pid);
|
||||
@@ -322,7 +372,6 @@ fn switch_to_next(frame: &mut TrapFrame, current_tid: types::id::ThreadId) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no runnable thread — idle
|
||||
loop { unsafe { core::arch::asm!("wfi") }; }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user