implements PLN-0049 system profile abi gates
This commit is contained in:
parent
20e8439416
commit
6de583c9d4
@ -84,6 +84,42 @@ impl VirtualMachineRuntime {
|
|||||||
fn int_arg_to_u16_status(value: i64) -> Result<u16, ComposerOpStatus> {
|
fn int_arg_to_u16_status(value: i64) -> Result<u16, ComposerOpStatus> {
|
||||||
u16::try_from(value).map_err(|_| ComposerOpStatus::ArgRangeInvalid)
|
u16::try_from(value).map_err(|_| ComposerOpStatus::ArgRangeInvalid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_game_profile_syscall(syscall: Syscall) -> bool {
|
||||||
|
matches!(
|
||||||
|
syscall,
|
||||||
|
Syscall::GfxClear
|
||||||
|
| Syscall::GfxFillRect
|
||||||
|
| Syscall::GfxDrawLine
|
||||||
|
| Syscall::GfxDrawCircle
|
||||||
|
| Syscall::GfxDrawDisc
|
||||||
|
| Syscall::GfxDrawSquare
|
||||||
|
| Syscall::GfxDrawText
|
||||||
|
| Syscall::GfxClear565
|
||||||
|
| Syscall::ComposerBindScene
|
||||||
|
| Syscall::ComposerUnbindScene
|
||||||
|
| Syscall::ComposerSetCamera
|
||||||
|
| Syscall::ComposerEmitSprite
|
||||||
|
| Syscall::AssetLoad
|
||||||
|
| Syscall::AssetStatus
|
||||||
|
| Syscall::AssetCommit
|
||||||
|
| Syscall::AssetCancel
|
||||||
|
| Syscall::BankInfo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_game_profile_syscall(&self, syscall: Syscall) -> Result<(), VmFault> {
|
||||||
|
if Self::is_game_profile_syscall(syscall)
|
||||||
|
&& self.current_cartridge_app_mode != AppMode::Game
|
||||||
|
{
|
||||||
|
return Err(VmFault::Trap(
|
||||||
|
TRAP_INVALID_SYSCALL,
|
||||||
|
format!("{} is only available to the Game profile", syscall.name()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NativeInterface for VirtualMachineRuntime {
|
impl NativeInterface for VirtualMachineRuntime {
|
||||||
@ -108,6 +144,8 @@ impl NativeInterface for VirtualMachineRuntime {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.ensure_game_profile_syscall(syscall)?;
|
||||||
|
|
||||||
let hw = ctx.require_hw()?;
|
let hw = ctx.require_hw()?;
|
||||||
|
|
||||||
match syscall {
|
match syscall {
|
||||||
|
|||||||
@ -64,12 +64,16 @@ impl FsBackend for MemFsBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cartridge_with_program(program: Vec<u8>, capabilities: u64) -> Cartridge {
|
fn cartridge_with_program_and_mode(
|
||||||
|
program: Vec<u8>,
|
||||||
|
capabilities: u64,
|
||||||
|
app_mode: AppMode,
|
||||||
|
) -> Cartridge {
|
||||||
Cartridge {
|
Cartridge {
|
||||||
app_id: 42,
|
app_id: 42,
|
||||||
title: "Test Cart".into(),
|
title: "Test Cart".into(),
|
||||||
app_version: "1.0.0".into(),
|
app_version: "1.0.0".into(),
|
||||||
app_mode: AppMode::Game,
|
app_mode,
|
||||||
capabilities,
|
capabilities,
|
||||||
program,
|
program,
|
||||||
assets: AssetsPayloadSource::empty(),
|
assets: AssetsPayloadSource::empty(),
|
||||||
@ -78,6 +82,10 @@ fn cartridge_with_program(program: Vec<u8>, capabilities: u64) -> Cartridge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cartridge_with_program(program: Vec<u8>, capabilities: u64) -> Cartridge {
|
||||||
|
cartridge_with_program_and_mode(program, capabilities, AppMode::Game)
|
||||||
|
}
|
||||||
|
|
||||||
fn serialized_single_function_module(code: Vec<u8>, syscalls: Vec<SyscallDecl>) -> Vec<u8> {
|
fn serialized_single_function_module(code: Vec<u8>, syscalls: Vec<SyscallDecl>) -> Vec<u8> {
|
||||||
serialized_single_function_module_with_consts(code, vec![], syscalls)
|
serialized_single_function_module_with_consts(code, vec![], syscalls)
|
||||||
}
|
}
|
||||||
@ -260,6 +268,130 @@ fn tick_returns_error_when_vm_ends_slice_with_trap() {
|
|||||||
assert!(matches!(runtime.last_crash_report, Some(CrashReport::VmTrap { .. })));
|
assert!(matches!(runtime.last_crash_report, Some(CrashReport::VmTrap { .. })));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tick_system_profile_rejects_gfx_game_surface() {
|
||||||
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
|
let mut vm = VirtualMachine::default();
|
||||||
|
let mut hardware = Hardware::new();
|
||||||
|
let signals = InputSignals::default();
|
||||||
|
let code = assemble("PUSH_I32 0\nHOSTCALL 0\nHALT").expect("assemble");
|
||||||
|
let program = serialized_single_function_module(
|
||||||
|
code,
|
||||||
|
vec![SyscallDecl {
|
||||||
|
module: "gfx".into(),
|
||||||
|
name: "clear".into(),
|
||||||
|
version: 1,
|
||||||
|
arg_slots: 1,
|
||||||
|
ret_slots: 0,
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
let cartridge = cartridge_with_program_and_mode(program, caps::GFX, AppMode::System);
|
||||||
|
|
||||||
|
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||||
|
let report = runtime.tick(&mut vm, &signals, &mut hardware).expect("system gfx call must trap");
|
||||||
|
|
||||||
|
match report {
|
||||||
|
CrashReport::VmTrap { trap } => {
|
||||||
|
assert_eq!(trap.code, prometeu_bytecode::TRAP_INVALID_SYSCALL);
|
||||||
|
assert!(trap.message.contains("GfxClear is only available to the Game profile"));
|
||||||
|
}
|
||||||
|
other => panic!("expected VmTrap crash report, got {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tick_system_profile_rejects_composer_game_surface() {
|
||||||
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
|
let mut vm = VirtualMachine::default();
|
||||||
|
let mut hardware = Hardware::new();
|
||||||
|
let signals = InputSignals::default();
|
||||||
|
let code = assemble("PUSH_I32 0\nHOSTCALL 0\nHALT").expect("assemble");
|
||||||
|
let program = serialized_single_function_module(
|
||||||
|
code,
|
||||||
|
vec![SyscallDecl {
|
||||||
|
module: "composer".into(),
|
||||||
|
name: "bind_scene".into(),
|
||||||
|
version: 1,
|
||||||
|
arg_slots: 1,
|
||||||
|
ret_slots: 1,
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
let cartridge = cartridge_with_program_and_mode(program, caps::GFX, AppMode::System);
|
||||||
|
|
||||||
|
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||||
|
let report =
|
||||||
|
runtime.tick(&mut vm, &signals, &mut hardware).expect("system composer call must trap");
|
||||||
|
|
||||||
|
match report {
|
||||||
|
CrashReport::VmTrap { trap } => {
|
||||||
|
assert_eq!(trap.code, prometeu_bytecode::TRAP_INVALID_SYSCALL);
|
||||||
|
assert!(
|
||||||
|
trap.message.contains("ComposerBindScene is only available to the Game profile")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
other => panic!("expected VmTrap crash report, got {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tick_system_profile_rejects_bank_game_surface() {
|
||||||
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
|
let mut vm = VirtualMachine::default();
|
||||||
|
let mut hardware = Hardware::new();
|
||||||
|
let signals = InputSignals::default();
|
||||||
|
let code = assemble("PUSH_I32 0\nHOSTCALL 0\nHALT").expect("assemble");
|
||||||
|
let program = serialized_single_function_module(
|
||||||
|
code,
|
||||||
|
vec![SyscallDecl {
|
||||||
|
module: "bank".into(),
|
||||||
|
name: "info".into(),
|
||||||
|
version: 1,
|
||||||
|
arg_slots: 1,
|
||||||
|
ret_slots: 2,
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
let cartridge = cartridge_with_program_and_mode(program, caps::BANK, AppMode::System);
|
||||||
|
|
||||||
|
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||||
|
let report =
|
||||||
|
runtime.tick(&mut vm, &signals, &mut hardware).expect("system bank call must trap");
|
||||||
|
|
||||||
|
match report {
|
||||||
|
CrashReport::VmTrap { trap } => {
|
||||||
|
assert_eq!(trap.code, prometeu_bytecode::TRAP_INVALID_SYSCALL);
|
||||||
|
assert!(trap.message.contains("BankInfo is only available to the Game profile"));
|
||||||
|
}
|
||||||
|
other => panic!("expected VmTrap crash report, got {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tick_system_profile_can_use_shared_log_transport() {
|
||||||
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
|
let mut vm = VirtualMachine::default();
|
||||||
|
let mut hardware = Hardware::new();
|
||||||
|
let signals = InputSignals::default();
|
||||||
|
let code = assemble("PUSH_I32 2\nPUSH_CONST 0\nHOSTCALL 0\nHALT").expect("assemble");
|
||||||
|
let program = serialized_single_function_module_with_consts(
|
||||||
|
code,
|
||||||
|
vec![ConstantPoolEntry::String("system log".into())],
|
||||||
|
vec![SyscallDecl {
|
||||||
|
module: "log".into(),
|
||||||
|
name: "write".into(),
|
||||||
|
version: 1,
|
||||||
|
arg_slots: 2,
|
||||||
|
ret_slots: 0,
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
let cartridge = cartridge_with_program_and_mode(program, caps::LOG, AppMode::System);
|
||||||
|
|
||||||
|
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||||
|
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||||
|
|
||||||
|
assert!(report.is_none(), "system log transport must remain internally shared");
|
||||||
|
assert!(vm.is_halted());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tick_returns_panic_report_distinct_from_trap() {
|
fn tick_returns_panic_report_distinct_from_trap() {
|
||||||
let mut runtime = VirtualMachineRuntime::new(None);
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
|
|||||||
@ -29,4 +29,4 @@
|
|||||||
{"type":"discussion","id":"DSC-0018","status":"done","ticket":"asset-load-asset-id-int-contract","title":"Asset Load Asset ID Int Contract","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["asset","runtime","abi"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0019","file":"discussion/lessons/DSC-0018-asset-load-asset-id-int-contract/LSN-0019-asset-load-id-abi-convergence.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
|
{"type":"discussion","id":"DSC-0018","status":"done","ticket":"asset-load-asset-id-int-contract","title":"Asset Load Asset ID Int Contract","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["asset","runtime","abi"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0019","file":"discussion/lessons/DSC-0018-asset-load-asset-id-int-contract/LSN-0019-asset-load-id-abi-convergence.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
|
||||||
{"type":"discussion","id":"DSC-0019","status":"done","ticket":"jenkinsfile-correction","title":"Jenkinsfile Correction and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins"],"agendas":[{"id":"AGD-0017","file":"AGD-0017-jenkinsfile-correction.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0002","file":"DEC-0002-jenkinsfile-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0002","file":"PLN-0002-jenkinsfile-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0020","file":"discussion/lessons/DSC-0019-jenkins-ci-standardization/LSN-0020-jenkins-standard-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}
|
{"type":"discussion","id":"DSC-0019","status":"done","ticket":"jenkinsfile-correction","title":"Jenkinsfile Correction and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins"],"agendas":[{"id":"AGD-0017","file":"AGD-0017-jenkinsfile-correction.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0002","file":"DEC-0002-jenkinsfile-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0002","file":"PLN-0002-jenkinsfile-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0020","file":"discussion/lessons/DSC-0019-jenkins-ci-standardization/LSN-0020-jenkins-standard-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}
|
||||||
{"type":"discussion","id":"DSC-0030","status":"done","ticket":"internal-viewport-270p","title":"Agenda - Internal Viewport 270p (480x270)","created_at":"2026-04-27","updated_at":"2026-04-28","tags":["gfx","runtime","viewport","resolution","frame-composer","host"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0039","file":"discussion/lessons/DSC-0030-internal-viewport-270p/LSN-0039-resolution-baseline-changes-are-runtime-wide-contracts.md","status":"done","created_at":"2026-04-28","updated_at":"2026-04-28"}]}
|
{"type":"discussion","id":"DSC-0030","status":"done","ticket":"internal-viewport-270p","title":"Agenda - Internal Viewport 270p (480x270)","created_at":"2026-04-27","updated_at":"2026-04-28","tags":["gfx","runtime","viewport","resolution","frame-composer","host"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0039","file":"discussion/lessons/DSC-0030-internal-viewport-270p/LSN-0039-resolution-baseline-changes-are-runtime-wide-contracts.md","status":"done","created_at":"2026-04-28","updated_at":"2026-04-28"}]}
|
||||||
{"type":"discussion","id":"DSC-0031","status":"in_progress","ticket":"runtime-mode-separation-game-system","title":"Agenda - Runtime Mode Separation: Game and System","created_at":"2026-05-11","updated_at":"2026-05-14","tags":["runtime","firmware","hub","system-apps","game-mode","scheduler"],"agendas":[{"id":"AGD-0031","file":"AGD-0031-runtime-mode-separation-game-system.md","status":"accepted","created_at":"2026-05-11","updated_at":"2026-05-14"}],"decisions":[{"id":"DEC-0023","file":"DEC-0023-system-pipeline-separation.md","status":"accepted","created_at":"2026-05-14","updated_at":"2026-05-14"}],"plans":[{"id":"PLN-0046","file":"PLN-0046-profile-separation-specification.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0047","file":"PLN-0047-firmware-runtime-profile-dispatch.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0048","file":"PLN-0048-minimal-system-hub-pipeline.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0049","file":"PLN-0049-system-profile-abi-gates.md","status":"open","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0050","file":"PLN-0050-system-pipeline-evidence-and-learning.md","status":"open","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]}],"lessons":[]}
|
{"type":"discussion","id":"DSC-0031","status":"in_progress","ticket":"runtime-mode-separation-game-system","title":"Agenda - Runtime Mode Separation: Game and System","created_at":"2026-05-11","updated_at":"2026-05-14","tags":["runtime","firmware","hub","system-apps","game-mode","scheduler"],"agendas":[{"id":"AGD-0031","file":"AGD-0031-runtime-mode-separation-game-system.md","status":"accepted","created_at":"2026-05-11","updated_at":"2026-05-14"}],"decisions":[{"id":"DEC-0023","file":"DEC-0023-system-pipeline-separation.md","status":"accepted","created_at":"2026-05-14","updated_at":"2026-05-14"}],"plans":[{"id":"PLN-0046","file":"PLN-0046-profile-separation-specification.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0047","file":"PLN-0047-firmware-runtime-profile-dispatch.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0048","file":"PLN-0048-minimal-system-hub-pipeline.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0049","file":"PLN-0049-system-profile-abi-gates.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0050","file":"PLN-0050-system-pipeline-evidence-and-learning.md","status":"open","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]}],"lessons":[]}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
id: PLN-0049
|
id: PLN-0049
|
||||||
ticket: runtime-mode-separation-game-system
|
ticket: runtime-mode-separation-game-system
|
||||||
title: System Profile ABI Gates
|
title: System Profile ABI Gates
|
||||||
status: open
|
status: done
|
||||||
created: 2026-05-14
|
created: 2026-05-14
|
||||||
completed:
|
completed:
|
||||||
ref_decisions: [DEC-0023]
|
ref_decisions: [DEC-0023]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user