feat(kernel): shared ABI decoder+proofs, no-panic gate, syscall storm, kani on shrunk kernel

This commit is contained in:
2026-10-26 23:44:09 +01:00
parent 154599e3af
commit 130a82f50d
41 changed files with 1583 additions and 404 deletions

View File

@@ -144,152 +144,18 @@ pub fn set_kernel_stack(kernel_sp: usize) {
}
}
/// Decodes the syscall in `frame`, or `None` for an unknown number or an
/// argument that is not representable (e.g. a slot out of range).
fn decode_syscall(frame: &TrapFrame) -> Option<domain::syscall::Syscall> {
use domain::message::Message;
use domain::syscall::Syscall;
use types::address::{DataRef, EntryPoint, StackPointer, VirtualAddress};
use types::badge::Badge;
use types::bounded::{CapSlot, PageCount};
use types::permission::PermissionSet;
use types::syscall_abi::*;
let nr = frame.regs[17]; // a7
let a = |i: usize| frame.regs[10 + i];
let slot = |i: usize| CapSlot::new(u8::try_from(a(i)).ok()?);
let message = || -> Option<Message> {
let msg = Message::from_words([a(1), a(2), a(3), a(4)]);
Some(match a(5) {
NONE => msg,
_ => msg.with_cap(slot(5)?),
})
};
Some(match nr {
SYS_EXIT => Syscall::Exit,
SYS_HALT => Syscall::Halt,
SYS_EXIT_THREAD => Syscall::ExitThread,
SYS_YIELD => Syscall::Yield,
SYS_GET_TIME => Syscall::GetTime,
SYS_GET_PROCESS_ID => Syscall::GetProcessId,
SYS_SEND => Syscall::Send {
endpoint: slot(0)?,
message: message()?,
},
SYS_CALL => Syscall::Call {
endpoint: slot(0)?,
message: message()?,
},
SYS_RECEIVE => Syscall::Receive { endpoint: slot(0)? },
SYS_REPLY => Syscall::Reply {
message: message()?,
},
SYS_REPLY_RECV => Syscall::ReplyRecv {
endpoint: slot(0)?,
message: message()?,
},
SYS_DELEGATE => Syscall::Delegate {
slot: slot(0)?,
to_endpoint: slot(1)?,
permissions: PermissionSet::from_bits(u8::try_from(a(2)).ok()?),
},
SYS_MINT => Syscall::Mint {
slot: slot(0)?,
badge: Badge::new(a(1)),
permissions: PermissionSet::from_bits(u8::try_from(a(2)).ok()?),
},
SYS_CREATE_NOTIFICATION => Syscall::CreateNotification,
SYS_SIGNAL => Syscall::Signal {
notification: slot(0)?,
},
SYS_WAIT => Syscall::Wait {
notification: slot(0)?,
},
SYS_POLL => Syscall::Poll {
notification: slot(0)?,
},
SYS_BIND_NOTIFICATION => Syscall::BindNotification {
notification: slot(0)?,
},
SYS_SET_IRQ_NOTIFICATION => Syscall::SetIrqNotification {
irq: slot(0)?,
notification: slot(1)?,
},
SYS_ACKNOWLEDGE_INTERRUPT => Syscall::AcknowledgeInterrupt { irq: slot(0)? },
SYS_CREATE_PROCESS => Syscall::CreateProcess {
create_cap: slot(0)?,
image: DataRef::empty(),
},
SYS_KILL => Syscall::Kill { process: slot(0)? },
SYS_REVOKE => Syscall::Revoke { slot: slot(0)? },
SYS_DROP_CAP => Syscall::DropCap { slot: slot(0)? },
SYS_SPAWN_THREAD => Syscall::SpawnThread {
entry: EntryPoint::from_raw(a(0)),
stack: StackPointer::from_raw(a(1)),
},
SYS_REQUEST_PAGES => Syscall::RequestPages {
count: PageCount::new(u16::try_from(a(0)).ok()?)?,
},
SYS_RELEASE_PAGES => Syscall::ReleasePages {
addr: VirtualAddress::from_raw(a(0)),
count: PageCount::new(u16::try_from(a(1)).ok()?)?,
},
SYS_SPAWN_THREAD_IN => Syscall::SpawnThreadIn {
process: slot(0)?,
entry: EntryPoint::from_raw(a(1)),
stack: StackPointer::from_raw(a(2)),
},
SYS_CREATE_ENDPOINT => Syscall::CreateEndpoint,
SYS_CREATE_REGION => Syscall::CreateRegion {
pages: PageCount::new(u16::try_from(a(0)).ok()?)?,
},
SYS_MAP_REGION => Syscall::MapRegion {
region: slot(0)?,
va: VirtualAddress::from_raw(a(1)),
perms: match a(2) {
PERMS_READ_ONLY => types::mapping::PagePerms::ReadOnly,
PERMS_READ_WRITE => types::mapping::PagePerms::ReadWrite,
PERMS_READ_EXECUTE => types::mapping::PagePerms::ReadExecute,
_ => return None,
},
},
SYS_DEBUG_PUTCHAR => Syscall::DebugPutchar { byte: a(0) as u8 },
SYS_MAP_PAGE => Syscall::MapPage {
process: slot(0)?,
src_va: VirtualAddress::from_raw(a(1)),
dst_va: VirtualAddress::from_raw(a(2)),
executable: a(3) != 0,
},
_ => return None,
})
fn args(frame: &TrapFrame) -> [usize; 6] {
let mut a = [0usize; 6];
a.copy_from_slice(&frame.regs[10..16]);
a
}
/// Writes a completed syscall's result per the register ABI: status in
/// `a0`, results in `a1..`.
fn write_result(frame: &mut TrapFrame, result: &kernel::SyscallResult) {
use kernel::SyscallResult;
use kernel::syscall::DeliveryKind;
use types::syscall_abi::{KIND_CALL, KIND_MESSAGE, KIND_SIGNAL, NONE};
fn set_results(frame: &mut TrapFrame, regs: [usize; 8]) {
frame.regs[10..18].copy_from_slice(&regs);
}
let r = &mut frame.regs;
r[10] = 0;
match result {
SyscallResult::Ok => {}
SyscallResult::Value(v) => r[11] = *v,
SyscallResult::Slot(s) => r[11] = s.value() as usize,
SyscallResult::Received(d) => {
r[11] = d.badge;
r[12..16].copy_from_slice(&d.words);
r[16] = d.cap.map_or(NONE, |s| s.value() as usize);
r[17] = match d.kind {
DeliveryKind::Message => KIND_MESSAGE,
DeliveryKind::Call => KIND_CALL,
DeliveryKind::Signal => KIND_SIGNAL,
};
}
SyscallResult::Error(e) => r[10] = kernel::syscall::error_code(e) as usize,
}
fn write_result(frame: &mut TrapFrame, result: &kernel::SyscallResult) {
set_results(frame, kernel::abi::encode(result));
}
fn save_frame_to_context(frame: &TrapFrame, ctx: &mut domain::thread::SavedContext) {
@@ -424,8 +290,8 @@ fn handle_exception(frame: &mut TrapFrame, scause: usize) {
CAUSE_ECALL_U => {
frame.sepc += 4;
let Some(syscall) = decode_syscall(frame) else {
frame.regs[10] = types::syscall_abi::ErrorCode::UnknownSyscall as usize;
let Some(syscall) = kernel::abi::decode(frame.regs[17], &args(frame)) else {
set_results(frame, kernel::abi::unknown_syscall());
return;
};
{