pr5
This commit is contained in:
parent
b9723cfc40
commit
a7ddbcabb2
@ -129,6 +129,154 @@ pub enum Syscall {
|
|||||||
BankSlotInfo = 0x6102,
|
BankSlotInfo = 0x6102,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Canonical metadata describing a syscall using the unified slot-based ABI.
|
||||||
|
///
|
||||||
|
/// This structure is the single source of truth for:
|
||||||
|
/// - Argument slot count (inputs pulled from the VM stack)
|
||||||
|
/// - Return slot count (values pushed back to the VM stack)
|
||||||
|
/// - Capability flags (what permission is required to call this syscall)
|
||||||
|
/// - Determinism characteristics (if known/defined by the spec)
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct SyscallMeta {
|
||||||
|
/// Numeric identifier of the syscall (matches the enum discriminant).
|
||||||
|
pub id: u32,
|
||||||
|
/// Number of input slots consumed from the VM stack.
|
||||||
|
pub arg_slots: u8,
|
||||||
|
/// Number of output slots produced onto the VM stack.
|
||||||
|
pub ret_slots: u16,
|
||||||
|
/// Capability flags required for this syscall.
|
||||||
|
pub caps: CapFlags,
|
||||||
|
/// Determinism characteristics for the syscall.
|
||||||
|
pub determinism: Determinism,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bitflags representing capabilities required to invoke a syscall.
|
||||||
|
///
|
||||||
|
/// This avoids adding a new dependency; flags are represented in a plain
|
||||||
|
/// `u64` and combined via bitwise OR. Extend as needed as the capability
|
||||||
|
/// model evolves.
|
||||||
|
pub type CapFlags = u64;
|
||||||
|
|
||||||
|
pub mod caps {
|
||||||
|
use super::CapFlags;
|
||||||
|
pub const NONE: CapFlags = 0;
|
||||||
|
pub const SYSTEM: CapFlags = 1 << 0;
|
||||||
|
pub const GFX: CapFlags = 1 << 1;
|
||||||
|
pub const INPUT: CapFlags = 1 << 2;
|
||||||
|
pub const AUDIO: CapFlags = 1 << 3;
|
||||||
|
pub const FS: CapFlags = 1 << 4;
|
||||||
|
pub const LOG: CapFlags = 1 << 5;
|
||||||
|
pub const ASSET: CapFlags = 1 << 6;
|
||||||
|
pub const BANK: CapFlags = 1 << 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determinism flags for a syscall.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Determinism {
|
||||||
|
/// Determinism is not specified in the current spec.
|
||||||
|
Unknown,
|
||||||
|
/// Given the same VM state and inputs, result is deterministic.
|
||||||
|
Deterministic,
|
||||||
|
/// May vary across runs (e.g., time, external IO race), even with same inputs.
|
||||||
|
NonDeterministic,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pairing of a strongly-typed syscall and its metadata.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct SyscallRegistryEntry {
|
||||||
|
pub syscall: Syscall,
|
||||||
|
pub meta: SyscallMeta,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Canonical registry of all syscalls and their metadata.
|
||||||
|
///
|
||||||
|
/// IMPORTANT: This table is the single authoritative source for the slot-based
|
||||||
|
/// ABI. All helper methods (e.g., `args_count`/`results_count`) must read from
|
||||||
|
/// this table.
|
||||||
|
pub const SYSCALL_TABLE: &[SyscallRegistryEntry] = &[
|
||||||
|
// --- System ---
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::SystemHasCart, meta: SyscallMeta { id: 0x0001, arg_slots: 0, ret_slots: 1, caps: caps::SYSTEM, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::SystemRunCart, meta: SyscallMeta { id: 0x0002, arg_slots: 0, ret_slots: 0, caps: caps::SYSTEM, determinism: Determinism::NonDeterministic } },
|
||||||
|
|
||||||
|
// --- GFX ---
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::GfxClear, meta: SyscallMeta { id: 0x1001, arg_slots: 1, ret_slots: 0, caps: caps::GFX, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::GfxFillRect, meta: SyscallMeta { id: 0x1002, arg_slots: 5, ret_slots: 0, caps: caps::GFX, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::GfxDrawLine, meta: SyscallMeta { id: 0x1003, arg_slots: 5, ret_slots: 0, caps: caps::GFX, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::GfxDrawCircle, meta: SyscallMeta { id: 0x1004, arg_slots: 4, ret_slots: 0, caps: caps::GFX, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::GfxDrawDisc, meta: SyscallMeta { id: 0x1005, arg_slots: 5, ret_slots: 0, caps: caps::GFX, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::GfxDrawSquare, meta: SyscallMeta { id: 0x1006, arg_slots: 6, ret_slots: 0, caps: caps::GFX, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::GfxSetSprite, meta: SyscallMeta { id: 0x1007, arg_slots: 10, ret_slots: 0, caps: caps::GFX, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::GfxDrawText, meta: SyscallMeta { id: 0x1008, arg_slots: 4, ret_slots: 0, caps: caps::GFX, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::GfxClear565, meta: SyscallMeta { id: 0x1010, arg_slots: 1, ret_slots: 0, caps: caps::GFX, determinism: Determinism::Deterministic } },
|
||||||
|
|
||||||
|
// --- Input ---
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::InputGetPad, meta: SyscallMeta { id: 0x2001, arg_slots: 1, ret_slots: 1, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::InputGetPadPressed, meta: SyscallMeta { id: 0x2002, arg_slots: 1, ret_slots: 1, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::InputGetPadReleased, meta: SyscallMeta { id: 0x2003, arg_slots: 1, ret_slots: 1, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::InputGetPadHold, meta: SyscallMeta { id: 0x2004, arg_slots: 1, ret_slots: 1, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::InputPadSnapshot, meta: SyscallMeta { id: 0x2010, arg_slots: 0, ret_slots: 48, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::InputTouchSnapshot, meta: SyscallMeta { id: 0x2011, arg_slots: 0, ret_slots: 6, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::TouchGetX, meta: SyscallMeta { id: 0x2101, arg_slots: 0, ret_slots: 1, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::TouchGetY, meta: SyscallMeta { id: 0x2102, arg_slots: 0, ret_slots: 1, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::TouchIsDown, meta: SyscallMeta { id: 0x2103, arg_slots: 0, ret_slots: 1, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::TouchIsPressed, meta: SyscallMeta { id: 0x2104, arg_slots: 0, ret_slots: 1, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::TouchIsReleased, meta: SyscallMeta { id: 0x2105, arg_slots: 0, ret_slots: 1, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::TouchGetHold, meta: SyscallMeta { id: 0x2106, arg_slots: 0, ret_slots: 1, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::TouchGetFinger, meta: SyscallMeta { id: 0x2107, arg_slots: 0, ret_slots: 4, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
|
||||||
|
// --- Input (Pad service-based) ---
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::PadGetUp, meta: SyscallMeta { id: 0x2200, arg_slots: 0, ret_slots: 4, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::PadGetDown, meta: SyscallMeta { id: 0x2201, arg_slots: 0, ret_slots: 4, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::PadGetLeft, meta: SyscallMeta { id: 0x2202, arg_slots: 0, ret_slots: 4, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::PadGetRight, meta: SyscallMeta { id: 0x2203, arg_slots: 0, ret_slots: 4, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::PadGetA, meta: SyscallMeta { id: 0x2204, arg_slots: 0, ret_slots: 4, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::PadGetB, meta: SyscallMeta { id: 0x2205, arg_slots: 0, ret_slots: 4, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::PadGetX, meta: SyscallMeta { id: 0x2206, arg_slots: 0, ret_slots: 4, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::PadGetY, meta: SyscallMeta { id: 0x2207, arg_slots: 0, ret_slots: 4, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::PadGetL, meta: SyscallMeta { id: 0x2208, arg_slots: 0, ret_slots: 4, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::PadGetR, meta: SyscallMeta { id: 0x2209, arg_slots: 0, ret_slots: 4, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::PadGetStart, meta: SyscallMeta { id: 0x220A, arg_slots: 0, ret_slots: 4, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::PadGetSelect, meta: SyscallMeta { id: 0x220B, arg_slots: 0, ret_slots: 4, caps: caps::INPUT, determinism: Determinism::Deterministic } },
|
||||||
|
|
||||||
|
// --- Audio ---
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::AudioPlaySample, meta: SyscallMeta { id: 0x3001, arg_slots: 5, ret_slots: 0, caps: caps::AUDIO, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::AudioPlay, meta: SyscallMeta { id: 0x3002, arg_slots: 7, ret_slots: 0, caps: caps::AUDIO, determinism: Determinism::Deterministic } },
|
||||||
|
|
||||||
|
// --- FS ---
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::FsOpen, meta: SyscallMeta { id: 0x4001, arg_slots: 1, ret_slots: 1, caps: caps::FS, determinism: Determinism::NonDeterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::FsRead, meta: SyscallMeta { id: 0x4002, arg_slots: 1, ret_slots: 1, caps: caps::FS, determinism: Determinism::NonDeterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::FsWrite, meta: SyscallMeta { id: 0x4003, arg_slots: 2, ret_slots: 1, caps: caps::FS, determinism: Determinism::NonDeterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::FsClose, meta: SyscallMeta { id: 0x4004, arg_slots: 1, ret_slots: 0, caps: caps::FS, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::FsListDir, meta: SyscallMeta { id: 0x4005, arg_slots: 1, ret_slots: 1, caps: caps::FS, determinism: Determinism::NonDeterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::FsExists, meta: SyscallMeta { id: 0x4006, arg_slots: 1, ret_slots: 1, caps: caps::FS, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::FsDelete, meta: SyscallMeta { id: 0x4007, arg_slots: 1, ret_slots: 0, caps: caps::FS, determinism: Determinism::NonDeterministic } },
|
||||||
|
|
||||||
|
// --- Log ---
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::LogWrite, meta: SyscallMeta { id: 0x5001, arg_slots: 2, ret_slots: 0, caps: caps::LOG, determinism: Determinism::NonDeterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::LogWriteTag, meta: SyscallMeta { id: 0x5002, arg_slots: 3, ret_slots: 0, caps: caps::LOG, determinism: Determinism::NonDeterministic } },
|
||||||
|
|
||||||
|
// --- Asset/Bank ---
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::AssetLoad, meta: SyscallMeta { id: 0x6001, arg_slots: 3, ret_slots: 1, caps: caps::ASSET, determinism: Determinism::NonDeterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::AssetStatus, meta: SyscallMeta { id: 0x6002, arg_slots: 1, ret_slots: 1, caps: caps::ASSET, determinism: Determinism::NonDeterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::AssetCommit, meta: SyscallMeta { id: 0x6003, arg_slots: 1, ret_slots: 0, caps: caps::ASSET, determinism: Determinism::NonDeterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::AssetCancel, meta: SyscallMeta { id: 0x6004, arg_slots: 1, ret_slots: 0, caps: caps::ASSET, determinism: Determinism::NonDeterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::BankInfo, meta: SyscallMeta { id: 0x6101, arg_slots: 1, ret_slots: 1, caps: caps::BANK, determinism: Determinism::Deterministic } },
|
||||||
|
SyscallRegistryEntry { syscall: Syscall::BankSlotInfo, meta: SyscallMeta { id: 0x6102, arg_slots: 2, ret_slots: 1, caps: caps::BANK, determinism: Determinism::Deterministic } },
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Returns the metadata associated with this syscall.
|
||||||
|
pub fn meta_for(syscall: Syscall) -> &'static SyscallMeta {
|
||||||
|
// Linear scan is acceptable given the very small number of syscalls.
|
||||||
|
// If this grows substantially, replace with a perfect hash or match.
|
||||||
|
for entry in SYSCALL_TABLE {
|
||||||
|
if entry.syscall == syscall {
|
||||||
|
return &entry.meta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!("Missing SyscallMeta for {:?}", syscall);
|
||||||
|
}
|
||||||
|
|
||||||
impl Syscall {
|
impl Syscall {
|
||||||
pub fn from_u32(id: u32) -> Option<Self> {
|
pub fn from_u32(id: u32) -> Option<Self> {
|
||||||
match id {
|
match id {
|
||||||
@ -190,135 +338,11 @@ impl Syscall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn args_count(&self) -> usize {
|
pub fn args_count(&self) -> usize {
|
||||||
match self {
|
meta_for(*self).arg_slots as usize
|
||||||
Self::SystemHasCart => 0,
|
|
||||||
Self::SystemRunCart => 0,
|
|
||||||
Self::GfxClear => 1,
|
|
||||||
Self::GfxFillRect => 5,
|
|
||||||
Self::GfxDrawLine => 5,
|
|
||||||
Self::GfxDrawCircle => 4,
|
|
||||||
Self::GfxDrawDisc => 5,
|
|
||||||
Self::GfxDrawSquare => 6,
|
|
||||||
Self::GfxSetSprite => 10,
|
|
||||||
Self::GfxDrawText => 4,
|
|
||||||
Self::GfxClear565 => 1,
|
|
||||||
Self::InputGetPad => 1,
|
|
||||||
Self::InputGetPadPressed => 1,
|
|
||||||
Self::InputGetPadReleased => 1,
|
|
||||||
Self::InputGetPadHold => 1,
|
|
||||||
Self::InputPadSnapshot => 0,
|
|
||||||
Self::InputTouchSnapshot => 0,
|
|
||||||
Self::TouchGetX => 0,
|
|
||||||
Self::TouchGetY => 0,
|
|
||||||
Self::TouchIsDown => 0,
|
|
||||||
Self::TouchIsPressed => 0,
|
|
||||||
Self::TouchIsReleased => 0,
|
|
||||||
Self::TouchGetHold => 0,
|
|
||||||
Self::TouchGetFinger => 0,
|
|
||||||
Self::PadGetUp => 0,
|
|
||||||
Self::PadGetDown => 0,
|
|
||||||
Self::PadGetLeft => 0,
|
|
||||||
Self::PadGetRight => 0,
|
|
||||||
Self::PadGetA => 0,
|
|
||||||
Self::PadGetB => 0,
|
|
||||||
Self::PadGetX => 0,
|
|
||||||
Self::PadGetY => 0,
|
|
||||||
Self::PadGetL => 0,
|
|
||||||
Self::PadGetR => 0,
|
|
||||||
Self::PadGetStart => 0,
|
|
||||||
Self::PadGetSelect => 0,
|
|
||||||
Self::AudioPlaySample => 5,
|
|
||||||
Self::AudioPlay => 7,
|
|
||||||
Self::FsOpen => 1,
|
|
||||||
Self::FsRead => 1,
|
|
||||||
Self::FsWrite => 2,
|
|
||||||
Self::FsClose => 1,
|
|
||||||
Self::FsListDir => 1,
|
|
||||||
Self::FsExists => 1,
|
|
||||||
Self::FsDelete => 1,
|
|
||||||
Self::LogWrite => 2,
|
|
||||||
Self::LogWriteTag => 3,
|
|
||||||
Self::AssetLoad => 3,
|
|
||||||
Self::AssetStatus => 1,
|
|
||||||
Self::AssetCommit => 1,
|
|
||||||
Self::AssetCancel => 1,
|
|
||||||
Self::BankInfo => 1,
|
|
||||||
Self::BankSlotInfo => 2,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn results_count(&self) -> usize {
|
pub fn results_count(&self) -> usize {
|
||||||
match self {
|
meta_for(*self).ret_slots as usize
|
||||||
// --- System ---
|
|
||||||
Self::SystemHasCart => 1,
|
|
||||||
Self::SystemRunCart => 0,
|
|
||||||
|
|
||||||
// --- GFX (void) ---
|
|
||||||
Self::GfxClear => 0,
|
|
||||||
Self::GfxFillRect => 0,
|
|
||||||
Self::GfxDrawLine => 0,
|
|
||||||
Self::GfxDrawCircle => 0,
|
|
||||||
Self::GfxDrawDisc => 0,
|
|
||||||
Self::GfxDrawSquare => 0,
|
|
||||||
Self::GfxSetSprite => 0,
|
|
||||||
Self::GfxDrawText => 0,
|
|
||||||
Self::GfxClear565 => 0,
|
|
||||||
|
|
||||||
// --- Input (scalar/snapshots) ---
|
|
||||||
Self::InputGetPad => 1,
|
|
||||||
Self::InputGetPadPressed => 1,
|
|
||||||
Self::InputGetPadReleased => 1,
|
|
||||||
Self::InputGetPadHold => 1,
|
|
||||||
Self::InputPadSnapshot => 48,
|
|
||||||
Self::InputTouchSnapshot => 6,
|
|
||||||
|
|
||||||
// --- Touch (scalars/struct) ---
|
|
||||||
Self::TouchGetX => 1,
|
|
||||||
Self::TouchGetY => 1,
|
|
||||||
Self::TouchIsDown => 1,
|
|
||||||
Self::TouchIsPressed => 1,
|
|
||||||
Self::TouchIsReleased => 1,
|
|
||||||
Self::TouchGetHold => 1,
|
|
||||||
Self::TouchGetFinger => 4, // Button struct (4 slots)
|
|
||||||
|
|
||||||
// --- Pad (per-button struct: 4 slots) ---
|
|
||||||
Self::PadGetUp
|
|
||||||
| Self::PadGetDown
|
|
||||||
| Self::PadGetLeft
|
|
||||||
| Self::PadGetRight
|
|
||||||
| Self::PadGetA
|
|
||||||
| Self::PadGetB
|
|
||||||
| Self::PadGetX
|
|
||||||
| Self::PadGetY
|
|
||||||
| Self::PadGetL
|
|
||||||
| Self::PadGetR
|
|
||||||
| Self::PadGetStart
|
|
||||||
| Self::PadGetSelect => 4,
|
|
||||||
|
|
||||||
// --- Audio (void) ---
|
|
||||||
Self::AudioPlaySample => 0,
|
|
||||||
Self::AudioPlay => 0,
|
|
||||||
|
|
||||||
// --- FS ---
|
|
||||||
Self::FsOpen => 1,
|
|
||||||
Self::FsRead => 1, // bytes read
|
|
||||||
Self::FsWrite => 1, // bytes written
|
|
||||||
Self::FsClose => 0,
|
|
||||||
Self::FsListDir => 1, // entries count/handle (TBD)
|
|
||||||
Self::FsExists => 1,
|
|
||||||
Self::FsDelete => 0,
|
|
||||||
|
|
||||||
// --- Log (void) ---
|
|
||||||
Self::LogWrite | Self::LogWriteTag => 0,
|
|
||||||
|
|
||||||
// --- Asset/Bank (conservador) ---
|
|
||||||
Self::AssetLoad => 1,
|
|
||||||
Self::AssetStatus => 1,
|
|
||||||
Self::AssetCommit => 0,
|
|
||||||
Self::AssetCancel => 0,
|
|
||||||
Self::BankInfo => 1,
|
|
||||||
Self::BankSlotInfo => 1,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
@ -379,3 +403,95 @@ impl Syscall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn all_syscalls() -> &'static [Syscall] {
|
||||||
|
&[
|
||||||
|
// System
|
||||||
|
Syscall::SystemHasCart,
|
||||||
|
Syscall::SystemRunCart,
|
||||||
|
// GFX
|
||||||
|
Syscall::GfxClear,
|
||||||
|
Syscall::GfxFillRect,
|
||||||
|
Syscall::GfxDrawLine,
|
||||||
|
Syscall::GfxDrawCircle,
|
||||||
|
Syscall::GfxDrawDisc,
|
||||||
|
Syscall::GfxDrawSquare,
|
||||||
|
Syscall::GfxSetSprite,
|
||||||
|
Syscall::GfxDrawText,
|
||||||
|
Syscall::GfxClear565,
|
||||||
|
// Input
|
||||||
|
Syscall::InputGetPad,
|
||||||
|
Syscall::InputGetPadPressed,
|
||||||
|
Syscall::InputGetPadReleased,
|
||||||
|
Syscall::InputGetPadHold,
|
||||||
|
Syscall::InputPadSnapshot,
|
||||||
|
Syscall::InputTouchSnapshot,
|
||||||
|
Syscall::TouchGetX,
|
||||||
|
Syscall::TouchGetY,
|
||||||
|
Syscall::TouchIsDown,
|
||||||
|
Syscall::TouchIsPressed,
|
||||||
|
Syscall::TouchIsReleased,
|
||||||
|
Syscall::TouchGetHold,
|
||||||
|
Syscall::TouchGetFinger,
|
||||||
|
// Pad service
|
||||||
|
Syscall::PadGetUp,
|
||||||
|
Syscall::PadGetDown,
|
||||||
|
Syscall::PadGetLeft,
|
||||||
|
Syscall::PadGetRight,
|
||||||
|
Syscall::PadGetA,
|
||||||
|
Syscall::PadGetB,
|
||||||
|
Syscall::PadGetX,
|
||||||
|
Syscall::PadGetY,
|
||||||
|
Syscall::PadGetL,
|
||||||
|
Syscall::PadGetR,
|
||||||
|
Syscall::PadGetStart,
|
||||||
|
Syscall::PadGetSelect,
|
||||||
|
// Audio
|
||||||
|
Syscall::AudioPlaySample,
|
||||||
|
Syscall::AudioPlay,
|
||||||
|
// FS
|
||||||
|
Syscall::FsOpen,
|
||||||
|
Syscall::FsRead,
|
||||||
|
Syscall::FsWrite,
|
||||||
|
Syscall::FsClose,
|
||||||
|
Syscall::FsListDir,
|
||||||
|
Syscall::FsExists,
|
||||||
|
Syscall::FsDelete,
|
||||||
|
// Log
|
||||||
|
Syscall::LogWrite,
|
||||||
|
Syscall::LogWriteTag,
|
||||||
|
// Asset/Bank
|
||||||
|
Syscall::AssetLoad,
|
||||||
|
Syscall::AssetStatus,
|
||||||
|
Syscall::AssetCommit,
|
||||||
|
Syscall::AssetCancel,
|
||||||
|
Syscall::BankInfo,
|
||||||
|
Syscall::BankSlotInfo,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn every_syscall_has_metadata() {
|
||||||
|
// 1) Every enum variant must appear in the table.
|
||||||
|
for sc in all_syscalls() {
|
||||||
|
let m = meta_for(*sc);
|
||||||
|
assert_eq!(m.id, *sc as u32, "id mismatch for {:?}", sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Table must not contain duplicates and must map back to a valid enum.
|
||||||
|
use std::collections::HashSet;
|
||||||
|
let mut ids = HashSet::new();
|
||||||
|
for e in SYSCALL_TABLE {
|
||||||
|
assert!(ids.insert(e.meta.id), "duplicate syscall id 0x{:08X}", e.meta.id);
|
||||||
|
let parsed = Syscall::from_u32(e.meta.id).expect("id not recognized by enum mapping");
|
||||||
|
assert_eq!(parsed as u32, e.meta.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) Table and explicit list sizes must match (guard against omissions).
|
||||||
|
assert_eq!(SYSCALL_TABLE.len(), all_syscalls().len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -80,6 +80,9 @@ pub struct VirtualMachine {
|
|||||||
pub gc_alloc_threshold: usize,
|
pub gc_alloc_threshold: usize,
|
||||||
/// GC: snapshot of live objects count after the last collection (or VM init).
|
/// GC: snapshot of live objects count after the last collection (or VM init).
|
||||||
last_gc_live_count: usize,
|
last_gc_live_count: usize,
|
||||||
|
/// Capability flags granted to the currently running program/cart.
|
||||||
|
/// Syscalls are capability-gated using `prometeu_hal::syscalls::SyscallMeta::caps`.
|
||||||
|
pub capabilities: prometeu_hal::syscalls::CapFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -110,6 +113,9 @@ impl VirtualMachine {
|
|||||||
breakpoints: std::collections::HashSet::new(),
|
breakpoints: std::collections::HashSet::new(),
|
||||||
gc_alloc_threshold: 1024, // conservative default; tests may override
|
gc_alloc_threshold: 1024, // conservative default; tests may override
|
||||||
last_gc_live_count: 0,
|
last_gc_live_count: 0,
|
||||||
|
// Default to all capabilities allowed for backward compatibility.
|
||||||
|
// Tests can narrow this via `set_capabilities`.
|
||||||
|
capabilities: u64::MAX,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,6 +137,7 @@ impl VirtualMachine {
|
|||||||
self.cycles = 0;
|
self.cycles = 0;
|
||||||
self.halted = true; // execution is impossible until a successful load
|
self.halted = true; // execution is impossible until a successful load
|
||||||
self.last_gc_live_count = 0;
|
self.last_gc_live_count = 0;
|
||||||
|
// Preserve capabilities across loads; firmware may set them per cart.
|
||||||
|
|
||||||
// Only recognized format is loadable: PBS v0 industrial format
|
// Only recognized format is loadable: PBS v0 industrial format
|
||||||
let program = if program_bytes.starts_with(b"PBS\0") {
|
let program = if program_bytes.starts_with(b"PBS\0") {
|
||||||
@ -191,6 +198,11 @@ impl VirtualMachine {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the capability flags for the current program.
|
||||||
|
pub fn set_capabilities(&mut self, caps: prometeu_hal::syscalls::CapFlags) {
|
||||||
|
self.capabilities = caps;
|
||||||
|
}
|
||||||
|
|
||||||
/// Prepares the VM to execute a specific entrypoint by setting the PC and
|
/// Prepares the VM to execute a specific entrypoint by setting the PC and
|
||||||
/// pushing an initial call frame.
|
/// pushing an initial call frame.
|
||||||
pub fn prepare_call(&mut self, entrypoint: &str) {
|
pub fn prepare_call(&mut self, entrypoint: &str) {
|
||||||
@ -912,6 +924,20 @@ impl VirtualMachine {
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
// Capability check before any side effects or argument consumption.
|
||||||
|
let meta = prometeu_hal::syscalls::meta_for(syscall);
|
||||||
|
if (self.capabilities & meta.caps) != meta.caps {
|
||||||
|
return Err(self.trap(
|
||||||
|
TRAP_INVALID_SYSCALL,
|
||||||
|
OpCode::Syscall as u16,
|
||||||
|
format!(
|
||||||
|
"Missing capability for syscall {} (required=0x{:X})",
|
||||||
|
syscall.name(), meta.caps
|
||||||
|
),
|
||||||
|
pc_at_syscall,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let args_count = syscall.args_count();
|
let args_count = syscall.args_count();
|
||||||
|
|
||||||
let mut args = Vec::with_capacity(args_count);
|
let mut args = Vec::with_capacity(args_count);
|
||||||
@ -1846,6 +1872,59 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_syscall_missing_capability_trap() {
|
||||||
|
// Program: directly call GfxClear (0x1001). We check caps before args, so no underflow.
|
||||||
|
let rom = vec![
|
||||||
|
0x70, 0x00, // Syscall + Reserved
|
||||||
|
0x01, 0x10, 0x00, 0x00, // Syscall ID 0x1001 (LE)
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||||
|
// Remove all capabilities
|
||||||
|
vm.set_capabilities(0);
|
||||||
|
let mut native = MockNative;
|
||||||
|
let mut ctx = HostContext::new(None);
|
||||||
|
|
||||||
|
vm.prepare_call("0");
|
||||||
|
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||||
|
|
||||||
|
match report.reason {
|
||||||
|
LogicalFrameEndingReason::Trap(trap) => {
|
||||||
|
assert_eq!(trap.code, TRAP_INVALID_SYSCALL);
|
||||||
|
assert_eq!(trap.opcode, OpCode::Syscall as u16);
|
||||||
|
assert!(trap.message.contains("Missing capability"));
|
||||||
|
assert_eq!(trap.pc, 0);
|
||||||
|
}
|
||||||
|
other => panic!("Expected Trap, got {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_syscall_with_capability_success() {
|
||||||
|
// Program: push arg 0; call GfxClear (0x1001)
|
||||||
|
let mut rom = Vec::new();
|
||||||
|
rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&0i32.to_le_bytes());
|
||||||
|
rom.extend_from_slice(&(OpCode::Syscall as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&0x1001u32.to_le_bytes()); // GfxClear
|
||||||
|
|
||||||
|
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||||
|
// Grant only GFX capability
|
||||||
|
vm.set_capabilities(prometeu_hal::syscalls::caps::GFX);
|
||||||
|
let mut native = MockNative;
|
||||||
|
let mut ctx = HostContext::new(None);
|
||||||
|
|
||||||
|
vm.prepare_call("0");
|
||||||
|
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||||
|
|
||||||
|
// Any non-trap outcome is considered success here
|
||||||
|
match report.reason {
|
||||||
|
LogicalFrameEndingReason::Trap(trap) => panic!("Unexpected trap: {:?}", trap),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_syscall_results_count_mismatch_panic() {
|
fn test_syscall_results_count_mismatch_panic() {
|
||||||
// GfxClear565 (0x1010) expects 0 results
|
// GfxClear565 (0x1010) expects 0 results
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# **Host ABI and Syscalls**
|
# **Host ABI and Syscalls**
|
||||||
|
|
||||||
This chapter defines the Application Binary Interface (ABI) between the Prometeu Virtual Machine (PVM) and the host environment. It specifies how syscalls are encoded, invoked, verified, and accounted for.
|
This chapter defines the Application Binary Interface (ABI) between the Prometeu Virtual Machine (PVM) and the host environment. It specifies how syscalls are identified, resolved, invoked, verified, and accounted for.
|
||||||
|
|
||||||
Syscalls provide controlled access to host-managed subsystems such as graphics, audio, input, asset banks, and persistent storage.
|
Syscalls provide controlled access to host-managed subsystems such as graphics, audio, input, asset banks, and persistent storage.
|
||||||
|
|
||||||
@ -20,10 +20,89 @@ The syscall system follows these rules:
|
|||||||
4. **Capability-gated**: Each syscall requires a declared capability.
|
4. **Capability-gated**: Each syscall requires a declared capability.
|
||||||
5. **Stack-based ABI**: Arguments and return values are passed via VM slots.
|
5. **Stack-based ABI**: Arguments and return values are passed via VM slots.
|
||||||
6. **Not first-class**: Syscalls are callable but cannot be stored as values.
|
6. **Not first-class**: Syscalls are callable but cannot be stored as values.
|
||||||
|
7. **Language-agnostic identity**: Syscalls are identified by canonical names, not language syntax.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2 Syscall Instruction Semantics
|
## 2 Canonical Syscall Identity
|
||||||
|
|
||||||
|
Syscalls are identified by a **canonical triple**:
|
||||||
|
|
||||||
|
```
|
||||||
|
(module, name, version)
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
("gfx", "present", 1)
|
||||||
|
("input", "state", 1)
|
||||||
|
("audio", "play", 2)
|
||||||
|
```
|
||||||
|
|
||||||
|
This identity is:
|
||||||
|
|
||||||
|
* Independent of language syntax.
|
||||||
|
* Stable across languages and toolchains.
|
||||||
|
* Used for capability checks and linking.
|
||||||
|
|
||||||
|
### Language independence
|
||||||
|
|
||||||
|
Different languages may reference the same syscall using different syntax:
|
||||||
|
|
||||||
|
| Language style | Reference form |
|
||||||
|
| -------------- | --------------- |
|
||||||
|
| Dot-based | `gfx.present` |
|
||||||
|
| Namespace | `gfx::present` |
|
||||||
|
| Object-style | `Gfx.present()` |
|
||||||
|
| Functional | `present(gfx)` |
|
||||||
|
|
||||||
|
All of these map to the same canonical identity:
|
||||||
|
|
||||||
|
```
|
||||||
|
("gfx", "present", 1)
|
||||||
|
```
|
||||||
|
|
||||||
|
The compiler is responsible for this mapping.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 Syscall Resolution
|
||||||
|
|
||||||
|
The host maintains a **Syscall Registry**:
|
||||||
|
|
||||||
|
```
|
||||||
|
(module, name, version) -> syscall_id
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
("gfx", "present", 1) -> 0x0101
|
||||||
|
("gfx", "submit", 1) -> 0x0102
|
||||||
|
("audio", "play", 1) -> 0x0201
|
||||||
|
```
|
||||||
|
|
||||||
|
### Resolution process
|
||||||
|
|
||||||
|
At cartridge load time:
|
||||||
|
|
||||||
|
1. The cartridge declares required syscalls using canonical identities.
|
||||||
|
2. The host verifies capabilities.
|
||||||
|
3. The host resolves each canonical identity to a numeric `syscall_id`.
|
||||||
|
4. The VM stores the resolved table for fast execution.
|
||||||
|
|
||||||
|
At runtime, the VM executes:
|
||||||
|
|
||||||
|
```
|
||||||
|
SYSCALL <id>
|
||||||
|
```
|
||||||
|
|
||||||
|
Only numeric IDs are used during execution.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 Syscall Instruction Semantics
|
||||||
|
|
||||||
The VM provides a single instruction:
|
The VM provides a single instruction:
|
||||||
|
|
||||||
@ -37,7 +116,7 @@ Where:
|
|||||||
|
|
||||||
Execution steps:
|
Execution steps:
|
||||||
|
|
||||||
1. The VM looks up the syscall metadata using `<id>`.
|
1. The VM looks up syscall metadata using `<id>`.
|
||||||
2. The VM verifies that enough arguments exist on the stack.
|
2. The VM verifies that enough arguments exist on the stack.
|
||||||
3. The VM checks capability requirements.
|
3. The VM checks capability requirements.
|
||||||
4. The syscall executes in the host environment.
|
4. The syscall executes in the host environment.
|
||||||
@ -47,7 +126,7 @@ If any contract rule is violated, the VM traps.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3 Syscall Metadata Table
|
## 5 Syscall Metadata Table
|
||||||
|
|
||||||
Each syscall is defined by a metadata entry.
|
Each syscall is defined by a metadata entry.
|
||||||
|
|
||||||
@ -56,7 +135,9 @@ Each syscall is defined by a metadata entry.
|
|||||||
```
|
```
|
||||||
SyscallMeta {
|
SyscallMeta {
|
||||||
id: u32
|
id: u32
|
||||||
|
module: string
|
||||||
name: string
|
name: string
|
||||||
|
version: u16
|
||||||
arg_slots: u8
|
arg_slots: u8
|
||||||
ret_slots: u8
|
ret_slots: u8
|
||||||
capability: CapabilityId
|
capability: CapabilityId
|
||||||
@ -69,19 +150,21 @@ Fields:
|
|||||||
|
|
||||||
| Field | Description |
|
| Field | Description |
|
||||||
| -------------- | ------------------------------------------------ |
|
| -------------- | ------------------------------------------------ |
|
||||||
| `id` | Unique syscall identifier |
|
| `id` | Unique numeric syscall identifier |
|
||||||
| `name` | Human-readable name |
|
| `module` | Canonical module name |
|
||||||
|
| `name` | Canonical syscall name |
|
||||||
|
| `version` | ABI version of the syscall |
|
||||||
| `arg_slots` | Number of input stack slots |
|
| `arg_slots` | Number of input stack slots |
|
||||||
| `ret_slots` | Number of return stack slots |
|
| `ret_slots` | Number of return stack slots |
|
||||||
| `capability` | Required capability |
|
| `capability` | Required capability |
|
||||||
| `may_allocate` | Whether the syscall may allocate VM heap objects |
|
| `may_allocate` | Whether the syscall may allocate VM heap objects |
|
||||||
| `cost_hint` | Expected cycle cost (for analysis/profiling) |
|
| `cost_hint` | Expected cycle cost |
|
||||||
|
|
||||||
The verifier uses this table to validate stack effects.
|
The verifier uses this table to validate stack effects.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4 Arguments and Return Values
|
## 6 Arguments and Return Values
|
||||||
|
|
||||||
Syscalls use the same slot-based ABI as functions.
|
Syscalls use the same slot-based ABI as functions.
|
||||||
|
|
||||||
@ -109,21 +192,11 @@ SYSCALL input_state
|
|||||||
// after: [held, pressed, released]
|
// after: [held, pressed, released]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Slot types
|
|
||||||
|
|
||||||
Each slot contains one of the VM value types:
|
|
||||||
|
|
||||||
* int
|
|
||||||
* bool
|
|
||||||
* float
|
|
||||||
* handle
|
|
||||||
* null
|
|
||||||
|
|
||||||
Composite return values are represented as multiple slots (stack tuples).
|
Composite return values are represented as multiple slots (stack tuples).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5 Syscalls as Callable Entities (Not First-Class)
|
## 7 Syscalls as Callable Entities (Not First-Class)
|
||||||
|
|
||||||
Syscalls behave like functions in terms of arguments and return values, but they are **not first-class values**.
|
Syscalls behave like functions in terms of arguments and return values, but they are **not first-class values**.
|
||||||
|
|
||||||
@ -136,7 +209,7 @@ This means:
|
|||||||
|
|
||||||
Only user-defined functions and closures are first-class.
|
Only user-defined functions and closures are first-class.
|
||||||
|
|
||||||
### Example declaration (conceptual)
|
### Conceptual declaration
|
||||||
|
|
||||||
```
|
```
|
||||||
host fn input_state() -> (int, int, int)
|
host fn input_state() -> (int, int, int)
|
||||||
@ -146,7 +219,7 @@ This represents a syscall with three return values, but it cannot be treated as
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6 Error Model: Traps vs Status Codes
|
## 8 Error Model: Traps vs Status Codes
|
||||||
|
|
||||||
Syscalls use a hybrid error model.
|
Syscalls use a hybrid error model.
|
||||||
|
|
||||||
@ -155,11 +228,12 @@ Syscalls use a hybrid error model.
|
|||||||
The VM traps when:
|
The VM traps when:
|
||||||
|
|
||||||
* The syscall id is invalid.
|
* The syscall id is invalid.
|
||||||
|
* The canonical identity cannot be resolved.
|
||||||
* The required capability is missing.
|
* The required capability is missing.
|
||||||
* The stack does not contain enough arguments.
|
* The stack does not contain enough arguments.
|
||||||
* A handle is invalid or dead.
|
* A handle is invalid or dead.
|
||||||
|
|
||||||
These are considered fatal contract violations.
|
These are fatal contract violations.
|
||||||
|
|
||||||
### Status returns (domain conditions)
|
### Status returns (domain conditions)
|
||||||
|
|
||||||
@ -175,7 +249,7 @@ These are represented by status codes in return slots.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7 Capability System
|
## 9 Capability System
|
||||||
|
|
||||||
Each syscall requires a capability.
|
Each syscall requires a capability.
|
||||||
|
|
||||||
@ -195,7 +269,7 @@ If a syscall is invoked without the required capability:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 8 Interaction with the Garbage Collector
|
## 10 Interaction with the Garbage Collector
|
||||||
|
|
||||||
The VM heap is managed by the GC. Host-managed memory is separate.
|
The VM heap is managed by the GC. Host-managed memory is separate.
|
||||||
|
|
||||||
@ -220,7 +294,7 @@ This rule applies only to VM heap objects (such as closures or user objects), no
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 9 Determinism Rules
|
## 11 Determinism Rules
|
||||||
|
|
||||||
Syscalls must obey deterministic execution rules.
|
Syscalls must obey deterministic execution rules.
|
||||||
|
|
||||||
@ -238,7 +312,7 @@ Allowed patterns:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 10 Cost Model and Budgeting
|
## 12 Cost Model and Budgeting
|
||||||
|
|
||||||
Each syscall contributes to frame cost.
|
Each syscall contributes to frame cost.
|
||||||
|
|
||||||
@ -261,7 +335,7 @@ Nothing is free.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 11 Blocking and Long Operations
|
## 13 Blocking and Long Operations
|
||||||
|
|
||||||
Syscalls must not block.
|
Syscalls must not block.
|
||||||
|
|
||||||
@ -280,15 +354,19 @@ status, progress = asset.status(id)
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 12 Summary
|
## 14 Summary
|
||||||
|
|
||||||
* Syscalls are deterministic, synchronous, and non-blocking.
|
* Syscalls are deterministic, synchronous, and non-blocking.
|
||||||
* They use the same slot-based ABI as functions.
|
* They use the same slot-based ABI as functions.
|
||||||
* They are callable but not first-class.
|
* They are callable but not first-class.
|
||||||
|
* They are identified canonically by `(module, name, version)`.
|
||||||
|
* Language syntax does not affect the ABI.
|
||||||
|
* The host resolves canonical identities to numeric syscall IDs.
|
||||||
* Capabilities control access to host subsystems.
|
* Capabilities control access to host subsystems.
|
||||||
* GC only manages VM heap objects.
|
* GC only manages VM heap objects.
|
||||||
* Host-held heap objects must be registered as roots.
|
* Host-held heap objects must be registered as roots.
|
||||||
* All syscall costs are tracked per frame.
|
* All syscall costs are tracked per frame.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
< [Back](chapter-15.md) | [Summary](table-of-contents.md) >
|
< [Back](chapter-15.md) | [Summary](table-of-contents.md) >
|
||||||
157
files/TODOs.md
157
files/TODOs.md
@ -1,160 +1,3 @@
|
|||||||
# PR-5.1 — Define Canonical Syscall Metadata Table
|
|
||||||
|
|
||||||
### Briefing
|
|
||||||
|
|
||||||
Syscalls must follow a unified, function-like ABI based on slot counts and capabilities. This PR introduces the canonical metadata table describing every syscall.
|
|
||||||
|
|
||||||
### Target
|
|
||||||
|
|
||||||
* Define a single authoritative metadata structure for syscalls.
|
|
||||||
* Ensure all syscalls are described in terms of slot-based ABI.
|
|
||||||
|
|
||||||
### Work items
|
|
||||||
|
|
||||||
* Introduce a `SyscallMeta` struct containing:
|
|
||||||
|
|
||||||
* Syscall identifier.
|
|
||||||
* `arg_slots`.
|
|
||||||
* `ret_slots`.
|
|
||||||
* Capability flags.
|
|
||||||
* Determinism flags (if defined in spec).
|
|
||||||
* Create a canonical syscall table/registry.
|
|
||||||
* Ensure all existing syscalls are registered in this table.
|
|
||||||
* Document the structure in code comments (English).
|
|
||||||
|
|
||||||
### Acceptance checklist
|
|
||||||
|
|
||||||
* [ ] All syscalls have a `SyscallMeta` entry.
|
|
||||||
* [ ] Metadata includes arg and return slot counts.
|
|
||||||
* [ ] No syscall is invoked without metadata.
|
|
||||||
* [ ] `cargo test` passes.
|
|
||||||
|
|
||||||
### Tests
|
|
||||||
|
|
||||||
* Add a test ensuring all registered syscalls have metadata.
|
|
||||||
|
|
||||||
### Junie instructions
|
|
||||||
|
|
||||||
**You MAY:**
|
|
||||||
|
|
||||||
* Introduce a syscall metadata struct.
|
|
||||||
* Add a central registry for syscalls.
|
|
||||||
|
|
||||||
**You MUST NOT:**
|
|
||||||
|
|
||||||
* Change existing syscall semantics.
|
|
||||||
* Add new syscalls.
|
|
||||||
|
|
||||||
**If unclear:**
|
|
||||||
|
|
||||||
* Ask before defining metadata fields.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# PR-5.2 — Implement Slot-Based Syscall Calling Convention
|
|
||||||
|
|
||||||
### Briefing
|
|
||||||
|
|
||||||
Syscalls must behave like functions using the stack-based slot ABI. This PR implements the unified calling convention.
|
|
||||||
|
|
||||||
### Target
|
|
||||||
|
|
||||||
* Ensure syscalls read arguments from the stack.
|
|
||||||
* Ensure syscalls push return values based on `ret_slots`.
|
|
||||||
|
|
||||||
### Work items
|
|
||||||
|
|
||||||
* Modify the VM syscall dispatch logic to:
|
|
||||||
|
|
||||||
* Look up `SyscallMeta`.
|
|
||||||
* Pop `arg_slots` from the stack.
|
|
||||||
* Invoke the syscall.
|
|
||||||
* Push `ret_slots` results.
|
|
||||||
* Remove any legacy argument handling paths.
|
|
||||||
|
|
||||||
### Acceptance checklist
|
|
||||||
|
|
||||||
* [ ] All syscalls use slot-based calling convention.
|
|
||||||
* [ ] Argument and return slot counts are enforced.
|
|
||||||
* [ ] No legacy calling paths remain.
|
|
||||||
* [ ] `cargo test` passes.
|
|
||||||
|
|
||||||
### Tests
|
|
||||||
|
|
||||||
* Add tests:
|
|
||||||
|
|
||||||
* Syscall with correct arg/ret slots → passes.
|
|
||||||
* Syscall with insufficient args → trap or verifier failure.
|
|
||||||
|
|
||||||
### Junie instructions
|
|
||||||
|
|
||||||
**You MAY:**
|
|
||||||
|
|
||||||
* Refactor syscall dispatch code.
|
|
||||||
* Use metadata to enforce slot counts.
|
|
||||||
|
|
||||||
**You MUST NOT:**
|
|
||||||
|
|
||||||
* Change the stack model.
|
|
||||||
* Add compatibility layers.
|
|
||||||
|
|
||||||
**If unclear:**
|
|
||||||
|
|
||||||
* Ask before modifying dispatch semantics.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# PR-5.3 — Enforce Capability Checks in Syscall Dispatch
|
|
||||||
|
|
||||||
### Briefing
|
|
||||||
|
|
||||||
Syscalls must be capability-gated. The runtime must verify that the current program has permission to invoke each syscall.
|
|
||||||
|
|
||||||
### Target
|
|
||||||
|
|
||||||
* Enforce capability checks before syscall execution.
|
|
||||||
|
|
||||||
### Work items
|
|
||||||
|
|
||||||
* Extend `SyscallMeta` with capability requirements.
|
|
||||||
* Add capability data to the VM or execution context.
|
|
||||||
* In syscall dispatch:
|
|
||||||
|
|
||||||
* Check capabilities.
|
|
||||||
* If missing, trigger `TRAP_INVALID_SYSCALL` or equivalent.
|
|
||||||
|
|
||||||
### Acceptance checklist
|
|
||||||
|
|
||||||
* [ ] Capability checks occur before syscall execution.
|
|
||||||
* [ ] Missing capability leads to deterministic trap.
|
|
||||||
* [ ] Valid capabilities allow execution.
|
|
||||||
* [ ] `cargo test` passes.
|
|
||||||
|
|
||||||
### Tests
|
|
||||||
|
|
||||||
* Add tests:
|
|
||||||
|
|
||||||
* Syscall without capability → trap.
|
|
||||||
* Syscall with capability → success.
|
|
||||||
|
|
||||||
### Junie instructions
|
|
||||||
|
|
||||||
**You MAY:**
|
|
||||||
|
|
||||||
* Add capability checks in dispatch.
|
|
||||||
* Extend metadata with capability fields.
|
|
||||||
|
|
||||||
**You MUST NOT:**
|
|
||||||
|
|
||||||
* Change trap semantics.
|
|
||||||
* Introduce dynamic or nondeterministic permission systems.
|
|
||||||
|
|
||||||
**If unclear:**
|
|
||||||
|
|
||||||
* Ask before defining capability behavior.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# PR-5.4 — Verifier Integration for Syscall Slot Rules
|
# PR-5.4 — Verifier Integration for Syscall Slot Rules
|
||||||
|
|
||||||
### Briefing
|
### Briefing
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user