157 lines
4.4 KiB
Rust

use super::{CapFlags, SyscallMeta, domains};
/// Canonical identity triple for a syscall.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SyscallIdentity {
pub module: &'static str,
pub name: &'static str,
pub version: u16,
}
impl SyscallIdentity {
pub fn key(&self) -> (&'static str, &'static str, u16) {
(self.module, self.name, self.version)
}
}
/// Resolved syscall information provided to the loader/VM.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SyscallResolved {
pub id: u32,
pub meta: SyscallMeta,
}
/// Load-time error for syscall resolution.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LoadError {
UnknownSyscall {
module: &'static str,
name: &'static str,
version: u16,
},
MissingCapability {
required: CapFlags,
provided: CapFlags,
module: &'static str,
name: &'static str,
version: u16,
},
}
/// Load-time error for PBX-declared syscall resolution.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DeclaredLoadError {
UnknownSyscall {
module: String,
name: String,
version: u16,
},
MissingCapability {
required: CapFlags,
provided: CapFlags,
module: String,
name: String,
version: u16,
},
AbiMismatch {
module: String,
name: String,
version: u16,
declared_arg_slots: u16,
declared_ret_slots: u16,
expected_arg_slots: u16,
expected_ret_slots: u16,
},
}
fn resolve_syscall_impl(module: &str, name: &str, version: u16) -> Option<SyscallResolved> {
for entry in domains::all_entries() {
if entry.meta.module == module && entry.meta.name == name && entry.meta.version == version {
return Some(SyscallResolved { id: entry.meta.id, meta: entry.meta });
}
}
None
}
pub fn resolve_syscall(
module: &'static str,
name: &'static str,
version: u16,
) -> Option<SyscallResolved> {
resolve_syscall_impl(module, name, version)
}
pub fn resolve_program_syscalls(
declared: &[SyscallIdentity],
caps: CapFlags,
) -> Result<Vec<SyscallResolved>, LoadError> {
let mut out = Vec::with_capacity(declared.len());
for ident in declared {
let Some(res) = resolve_syscall(ident.module, ident.name, ident.version) else {
return Err(LoadError::UnknownSyscall {
module: ident.module,
name: ident.name,
version: ident.version,
});
};
let missing = res.meta.caps & !caps;
if missing != 0 {
return Err(LoadError::MissingCapability {
required: res.meta.caps,
provided: caps,
module: ident.module,
name: ident.name,
version: ident.version,
});
}
out.push(res);
}
Ok(out)
}
pub fn resolve_declared_program_syscalls(
declared: &[prometeu_bytecode::SyscallDecl],
caps: CapFlags,
) -> Result<Vec<SyscallResolved>, DeclaredLoadError> {
let mut out = Vec::with_capacity(declared.len());
for decl in declared {
let Some(res) = resolve_syscall_impl(&decl.module, &decl.name, decl.version) else {
return Err(DeclaredLoadError::UnknownSyscall {
module: decl.module.clone(),
name: decl.name.clone(),
version: decl.version,
});
};
let missing = res.meta.caps & !caps;
if missing != 0 {
return Err(DeclaredLoadError::MissingCapability {
required: res.meta.caps,
provided: caps,
module: decl.module.clone(),
name: decl.name.clone(),
version: decl.version,
});
}
let expected_arg_slots = u16::from(res.meta.arg_slots);
let expected_ret_slots = res.meta.ret_slots;
if decl.arg_slots != expected_arg_slots || decl.ret_slots != expected_ret_slots {
return Err(DeclaredLoadError::AbiMismatch {
module: decl.module.clone(),
name: decl.name.clone(),
version: decl.version,
declared_arg_slots: decl.arg_slots,
declared_ret_slots: decl.ret_slots,
expected_arg_slots,
expected_ret_slots,
});
}
out.push(res);
}
Ok(out)
}