implements PR-026
This commit is contained in:
parent
67e7acf73b
commit
0ff4909079
@ -181,7 +181,6 @@ mod tests {
|
|||||||
title: "Broken Cart".into(),
|
title: "Broken Cart".into(),
|
||||||
app_version: "1.0.0".into(),
|
app_version: "1.0.0".into(),
|
||||||
app_mode: AppMode::Game,
|
app_mode: AppMode::Game,
|
||||||
entrypoint: "".into(),
|
|
||||||
capabilities: 0,
|
capabilities: 0,
|
||||||
program: vec![0, 0, 0, 0],
|
program: vec![0, 0, 0, 0],
|
||||||
assets: AssetsPayloadSource::empty(),
|
assets: AssetsPayloadSource::empty(),
|
||||||
@ -218,7 +217,6 @@ mod tests {
|
|||||||
title: "Trap Cart".into(),
|
title: "Trap Cart".into(),
|
||||||
app_version: "1.0.0".into(),
|
app_version: "1.0.0".into(),
|
||||||
app_mode: AppMode::Game,
|
app_mode: AppMode::Game,
|
||||||
entrypoint: "".into(),
|
|
||||||
capabilities: caps::GFX,
|
capabilities: caps::GFX,
|
||||||
program,
|
program,
|
||||||
assets: AssetsPayloadSource::empty(),
|
assets: AssetsPayloadSource::empty(),
|
||||||
|
|||||||
@ -22,7 +22,6 @@ pub struct Cartridge {
|
|||||||
pub title: String,
|
pub title: String,
|
||||||
pub app_version: String,
|
pub app_version: String,
|
||||||
pub app_mode: AppMode,
|
pub app_mode: AppMode,
|
||||||
pub entrypoint: String,
|
|
||||||
pub capabilities: CapFlags,
|
pub capabilities: CapFlags,
|
||||||
pub program: Vec<u8>,
|
pub program: Vec<u8>,
|
||||||
pub assets: AssetsPayloadSource,
|
pub assets: AssetsPayloadSource,
|
||||||
@ -36,7 +35,6 @@ pub struct CartridgeDTO {
|
|||||||
pub title: String,
|
pub title: String,
|
||||||
pub app_version: String,
|
pub app_version: String,
|
||||||
pub app_mode: AppMode,
|
pub app_mode: AppMode,
|
||||||
pub entrypoint: String,
|
|
||||||
pub capabilities: CapFlags,
|
pub capabilities: CapFlags,
|
||||||
pub program: Vec<u8>,
|
pub program: Vec<u8>,
|
||||||
pub assets: AssetsPayloadSource,
|
pub assets: AssetsPayloadSource,
|
||||||
@ -51,7 +49,6 @@ impl From<CartridgeDTO> for Cartridge {
|
|||||||
title: dto.title,
|
title: dto.title,
|
||||||
app_version: dto.app_version,
|
app_version: dto.app_version,
|
||||||
app_mode: dto.app_mode,
|
app_mode: dto.app_mode,
|
||||||
entrypoint: dto.entrypoint,
|
|
||||||
capabilities: dto.capabilities,
|
capabilities: dto.capabilities,
|
||||||
program: dto.program,
|
program: dto.program,
|
||||||
assets: dto.assets,
|
assets: dto.assets,
|
||||||
@ -305,7 +302,6 @@ pub struct CartridgeManifest {
|
|||||||
pub title: String,
|
pub title: String,
|
||||||
pub app_version: String,
|
pub app_version: String,
|
||||||
pub app_mode: AppMode,
|
pub app_mode: AppMode,
|
||||||
pub entrypoint: String,
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub capabilities: Vec<Capability>,
|
pub capabilities: Vec<Capability>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -85,7 +85,6 @@ impl DirectoryCartridgeLoader {
|
|||||||
title: manifest.title,
|
title: manifest.title,
|
||||||
app_version: manifest.app_version,
|
app_version: manifest.app_version,
|
||||||
app_mode: manifest.app_mode,
|
app_mode: manifest.app_mode,
|
||||||
entrypoint: manifest.entrypoint,
|
|
||||||
capabilities,
|
capabilities,
|
||||||
program,
|
program,
|
||||||
assets,
|
assets,
|
||||||
@ -277,8 +276,7 @@ mod tests {
|
|||||||
"app_id": 1001,
|
"app_id": 1001,
|
||||||
"title": "Example",
|
"title": "Example",
|
||||||
"app_version": "1.0.0",
|
"app_version": "1.0.0",
|
||||||
"app_mode": "Game",
|
"app_mode": "Game"
|
||||||
"entrypoint": "main"
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(capabilities) = capabilities {
|
if let Some(capabilities) = capabilities {
|
||||||
|
|||||||
@ -30,7 +30,6 @@ pub struct VirtualMachineRuntime {
|
|||||||
pub current_cartridge_title: String,
|
pub current_cartridge_title: String,
|
||||||
pub current_cartridge_app_version: String,
|
pub current_cartridge_app_version: String,
|
||||||
pub current_cartridge_app_mode: AppMode,
|
pub current_cartridge_app_mode: AppMode,
|
||||||
pub current_entrypoint: String,
|
|
||||||
pub logs_written_this_frame: HashMap<u32, u32>,
|
pub logs_written_this_frame: HashMap<u32, u32>,
|
||||||
pub telemetry_current: TelemetryFrame,
|
pub telemetry_current: TelemetryFrame,
|
||||||
pub telemetry_last: TelemetryFrame,
|
pub telemetry_last: TelemetryFrame,
|
||||||
|
|||||||
@ -23,7 +23,6 @@ impl VirtualMachineRuntime {
|
|||||||
current_cartridge_title: String::new(),
|
current_cartridge_title: String::new(),
|
||||||
current_cartridge_app_version: String::new(),
|
current_cartridge_app_version: String::new(),
|
||||||
current_cartridge_app_mode: AppMode::Game,
|
current_cartridge_app_mode: AppMode::Game,
|
||||||
current_entrypoint: String::new(),
|
|
||||||
logs_written_this_frame: HashMap::new(),
|
logs_written_this_frame: HashMap::new(),
|
||||||
telemetry_current: TelemetryFrame::default(),
|
telemetry_current: TelemetryFrame::default(),
|
||||||
telemetry_last: TelemetryFrame::default(),
|
telemetry_last: TelemetryFrame::default(),
|
||||||
@ -97,7 +96,6 @@ impl VirtualMachineRuntime {
|
|||||||
self.current_cartridge_title.clear();
|
self.current_cartridge_title.clear();
|
||||||
self.current_cartridge_app_version.clear();
|
self.current_cartridge_app_version.clear();
|
||||||
self.current_cartridge_app_mode = AppMode::Game;
|
self.current_cartridge_app_mode = AppMode::Game;
|
||||||
self.current_entrypoint.clear();
|
|
||||||
self.logs_written_this_frame.clear();
|
self.logs_written_this_frame.clear();
|
||||||
|
|
||||||
self.telemetry_current = TelemetryFrame::default();
|
self.telemetry_current = TelemetryFrame::default();
|
||||||
@ -122,13 +120,12 @@ impl VirtualMachineRuntime {
|
|||||||
self.clear_cartridge_state();
|
self.clear_cartridge_state();
|
||||||
vm.set_capabilities(cartridge.capabilities);
|
vm.set_capabilities(cartridge.capabilities);
|
||||||
|
|
||||||
match vm.initialize(cartridge.program.clone(), &cartridge.entrypoint) {
|
match vm.initialize(cartridge.program.clone()) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.current_app_id = cartridge.app_id;
|
self.current_app_id = cartridge.app_id;
|
||||||
self.current_cartridge_title = cartridge.title.clone();
|
self.current_cartridge_title = cartridge.title.clone();
|
||||||
self.current_cartridge_app_version = cartridge.app_version.clone();
|
self.current_cartridge_app_version = cartridge.app_version.clone();
|
||||||
self.current_cartridge_app_mode = cartridge.app_mode;
|
self.current_cartridge_app_mode = cartridge.app_mode;
|
||||||
self.current_entrypoint = cartridge.entrypoint.clone();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
@ -59,7 +59,6 @@ fn cartridge_with_program(program: Vec<u8>, capabilities: u64) -> Cartridge {
|
|||||||
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: AppMode::Game,
|
||||||
entrypoint: "".into(),
|
|
||||||
capabilities,
|
capabilities,
|
||||||
program,
|
program,
|
||||||
assets: AssetsPayloadSource::empty(),
|
assets: AssetsPayloadSource::empty(),
|
||||||
@ -270,7 +269,6 @@ fn reset_clears_cartridge_scoped_runtime_state() {
|
|||||||
runtime.current_cartridge_title = "Cart".into();
|
runtime.current_cartridge_title = "Cart".into();
|
||||||
runtime.current_cartridge_app_version = "1.2.3".into();
|
runtime.current_cartridge_app_version = "1.2.3".into();
|
||||||
runtime.current_cartridge_app_mode = AppMode::System;
|
runtime.current_cartridge_app_mode = AppMode::System;
|
||||||
runtime.current_entrypoint = "main".into();
|
|
||||||
runtime.logs_written_this_frame.insert(42, 3);
|
runtime.logs_written_this_frame.insert(42, 3);
|
||||||
runtime.telemetry_current.frame_index = 8;
|
runtime.telemetry_current.frame_index = 8;
|
||||||
runtime.telemetry_current.cycles_used = 99;
|
runtime.telemetry_current.cycles_used = 99;
|
||||||
@ -296,7 +294,6 @@ fn reset_clears_cartridge_scoped_runtime_state() {
|
|||||||
assert!(runtime.current_cartridge_title.is_empty());
|
assert!(runtime.current_cartridge_title.is_empty());
|
||||||
assert!(runtime.current_cartridge_app_version.is_empty());
|
assert!(runtime.current_cartridge_app_version.is_empty());
|
||||||
assert_eq!(runtime.current_cartridge_app_mode, AppMode::Game);
|
assert_eq!(runtime.current_cartridge_app_mode, AppMode::Game);
|
||||||
assert!(runtime.current_entrypoint.is_empty());
|
|
||||||
assert!(runtime.logs_written_this_frame.is_empty());
|
assert!(runtime.logs_written_this_frame.is_empty());
|
||||||
assert_eq!(runtime.telemetry_current.frame_index, 0);
|
assert_eq!(runtime.telemetry_current.frame_index, 0);
|
||||||
assert_eq!(runtime.telemetry_current.cycles_used, 0);
|
assert_eq!(runtime.telemetry_current.cycles_used, 0);
|
||||||
@ -352,7 +349,6 @@ fn initialize_vm_failure_clears_previous_identity_and_handles() {
|
|||||||
assert!(runtime.current_cartridge_title.is_empty());
|
assert!(runtime.current_cartridge_title.is_empty());
|
||||||
assert!(runtime.current_cartridge_app_version.is_empty());
|
assert!(runtime.current_cartridge_app_version.is_empty());
|
||||||
assert_eq!(runtime.current_cartridge_app_mode, AppMode::Game);
|
assert_eq!(runtime.current_cartridge_app_mode, AppMode::Game);
|
||||||
assert!(runtime.current_entrypoint.is_empty());
|
|
||||||
assert!(runtime.open_files.is_empty());
|
assert!(runtime.open_files.is_empty());
|
||||||
assert_eq!(runtime.next_handle, 1);
|
assert_eq!(runtime.next_handle, 1);
|
||||||
assert!(!runtime.paused);
|
assert!(!runtime.paused);
|
||||||
@ -1030,7 +1026,6 @@ fn tick_memcard_slot_roundtrip_for_game_profile() {
|
|||||||
title: "Memcard Game".into(),
|
title: "Memcard Game".into(),
|
||||||
app_version: "1.0.0".into(),
|
app_version: "1.0.0".into(),
|
||||||
app_mode: AppMode::Game,
|
app_mode: AppMode::Game,
|
||||||
entrypoint: "".into(),
|
|
||||||
capabilities: caps::FS,
|
capabilities: caps::FS,
|
||||||
program,
|
program,
|
||||||
assets: AssetsPayloadSource::empty(),
|
assets: AssetsPayloadSource::empty(),
|
||||||
@ -1070,7 +1065,6 @@ fn tick_memcard_access_is_denied_for_non_game_profile() {
|
|||||||
title: "System App".into(),
|
title: "System App".into(),
|
||||||
app_version: "1.0.0".into(),
|
app_version: "1.0.0".into(),
|
||||||
app_mode: AppMode::System,
|
app_mode: AppMode::System,
|
||||||
entrypoint: "".into(),
|
|
||||||
capabilities: caps::FS,
|
capabilities: caps::FS,
|
||||||
program,
|
program,
|
||||||
assets: AssetsPayloadSource::empty(),
|
assets: AssetsPayloadSource::empty(),
|
||||||
|
|||||||
@ -58,7 +58,7 @@ impl VirtualMachineRuntime {
|
|||||||
self.begin_logical_frame(signals, hw);
|
self.begin_logical_frame(signals, hw);
|
||||||
|
|
||||||
if self.needs_prepare_entry_call || vm.call_stack_is_empty() {
|
if self.needs_prepare_entry_call || vm.call_stack_is_empty() {
|
||||||
vm.prepare_call(&self.current_entrypoint);
|
vm.prepare_boot_call();
|
||||||
self.needs_prepare_entry_call = false;
|
self.needs_prepare_entry_call = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2598,7 +2598,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_loader_hardening_invalid_magic() {
|
fn test_loader_hardening_invalid_magic() {
|
||||||
let mut vm = VirtualMachine::default();
|
let mut vm = VirtualMachine::default();
|
||||||
let res = vm.initialize(vec![0, 0, 0, 0], "");
|
let res = vm.initialize(vec![0, 0, 0, 0]);
|
||||||
assert_eq!(res, Err(VmInitError::InvalidFormat));
|
assert_eq!(res, Err(VmInitError::InvalidFormat));
|
||||||
// VM should remain empty
|
// VM should remain empty
|
||||||
assert_eq!(vm.program.rom.len(), 0);
|
assert_eq!(vm.program.rom.len(), 0);
|
||||||
@ -2611,7 +2611,7 @@ mod tests {
|
|||||||
header[0..4].copy_from_slice(b"PBS\0");
|
header[0..4].copy_from_slice(b"PBS\0");
|
||||||
header[4..6].copy_from_slice(&1u16.to_le_bytes()); // version 1 (unsupported)
|
header[4..6].copy_from_slice(&1u16.to_le_bytes()); // version 1 (unsupported)
|
||||||
|
|
||||||
let res = vm.initialize(header, "");
|
let res = vm.initialize(header);
|
||||||
assert_eq!(res, Err(VmInitError::UnsupportedFormat));
|
assert_eq!(res, Err(VmInitError::UnsupportedFormat));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2622,7 +2622,7 @@ mod tests {
|
|||||||
header[0..4].copy_from_slice(b"PBS\0");
|
header[0..4].copy_from_slice(b"PBS\0");
|
||||||
header[8..12].copy_from_slice(&1u32.to_le_bytes()); // 1 section claimed but none provided
|
header[8..12].copy_from_slice(&1u32.to_le_bytes()); // 1 section claimed but none provided
|
||||||
|
|
||||||
let res = vm.initialize(header, "");
|
let res = vm.initialize(header);
|
||||||
match res {
|
match res {
|
||||||
Err(VmInitError::ImageLoadFailed(prometeu_bytecode::LoadError::UnexpectedEof)) => {}
|
Err(VmInitError::ImageLoadFailed(prometeu_bytecode::LoadError::UnexpectedEof)) => {}
|
||||||
_ => panic!("Expected PbsV0LoadFailed(UnexpectedEof), got {:?}", res),
|
_ => panic!("Expected PbsV0LoadFailed(UnexpectedEof), got {:?}", res),
|
||||||
@ -2630,7 +2630,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_loader_hardening_entrypoint_not_found() {
|
fn test_loader_hardening_boot_protocol_entrypoint_not_found() {
|
||||||
let mut vm = VirtualMachine::default();
|
let mut vm = VirtualMachine::default();
|
||||||
let header = prometeu_bytecode::model::BytecodeModule {
|
let header = prometeu_bytecode::model::BytecodeModule {
|
||||||
version: 0,
|
version: 0,
|
||||||
@ -2643,8 +2643,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
.serialize();
|
.serialize();
|
||||||
|
|
||||||
// Try to initialize with numeric entrypoint 10 (out of bounds for empty ROM)
|
let res = vm.initialize(header);
|
||||||
let res = vm.initialize(header, "10");
|
|
||||||
assert_eq!(res, Err(VmInitError::EntrypointNotFound));
|
assert_eq!(res, Err(VmInitError::EntrypointNotFound));
|
||||||
|
|
||||||
// VM state should not be updated
|
// VM state should not be updated
|
||||||
@ -2656,22 +2655,27 @@ mod tests {
|
|||||||
fn test_loader_hardening_successful_init() {
|
fn test_loader_hardening_successful_init() {
|
||||||
let mut vm = VirtualMachine::default();
|
let mut vm = VirtualMachine::default();
|
||||||
vm.pc = 123; // Pollution
|
vm.pc = 123; // Pollution
|
||||||
|
let code = assemble("HALT").expect("assemble");
|
||||||
|
|
||||||
let header = prometeu_bytecode::model::BytecodeModule {
|
let header = prometeu_bytecode::model::BytecodeModule {
|
||||||
version: 0,
|
version: 0,
|
||||||
const_pool: vec![],
|
const_pool: vec![],
|
||||||
functions: vec![],
|
functions: vec![FunctionMeta {
|
||||||
code: vec![],
|
code_offset: 0,
|
||||||
|
code_len: code.len() as u32,
|
||||||
|
..Default::default()
|
||||||
|
}],
|
||||||
|
code,
|
||||||
debug_info: None,
|
debug_info: None,
|
||||||
exports: vec![],
|
exports: vec![],
|
||||||
syscalls: vec![],
|
syscalls: vec![],
|
||||||
}
|
}
|
||||||
.serialize();
|
.serialize();
|
||||||
|
|
||||||
let res = vm.initialize(header, "");
|
let res = vm.initialize(header);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
assert_eq!(vm.pc, 0);
|
assert_eq!(vm.pc, 0);
|
||||||
assert_eq!(vm.program.rom.len(), 0);
|
assert_eq!(vm.program.rom.len(), 2);
|
||||||
assert_eq!(vm.cycles, 0);
|
assert_eq!(vm.cycles, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2692,7 +2696,7 @@ mod tests {
|
|||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
let res = vm.initialize(bytes, "");
|
let res = vm.initialize(bytes);
|
||||||
|
|
||||||
assert!(res.is_ok(), "patched hostcall should initialize");
|
assert!(res.is_ok(), "patched hostcall should initialize");
|
||||||
|
|
||||||
@ -2718,7 +2722,7 @@ mod tests {
|
|||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
let res = vm.initialize(bytes, "");
|
let res = vm.initialize(bytes);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
@ -2758,7 +2762,7 @@ mod tests {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
let res = vm.initialize(bytes, "");
|
let res = vm.initialize(bytes);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
@ -2790,7 +2794,7 @@ mod tests {
|
|||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
let res = vm.initialize(bytes, "");
|
let res = vm.initialize(bytes);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
@ -2820,7 +2824,7 @@ mod tests {
|
|||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
let res = vm.initialize(bytes, "");
|
let res = vm.initialize(bytes);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
@ -2853,7 +2857,7 @@ mod tests {
|
|||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
let res = vm.initialize(bytes, "");
|
let res = vm.initialize(bytes);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
@ -2888,7 +2892,7 @@ mod tests {
|
|||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
let res = vm.initialize(bytes, "");
|
let res = vm.initialize(bytes);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
@ -2959,7 +2963,7 @@ mod tests {
|
|||||||
let mut vm = VirtualMachine::default();
|
let mut vm = VirtualMachine::default();
|
||||||
vm.set_capabilities(prometeu_hal::syscalls::caps::ALL);
|
vm.set_capabilities(prometeu_hal::syscalls::caps::ALL);
|
||||||
let bytes = serialized_single_hostcall_module(syscall);
|
let bytes = serialized_single_hostcall_module(syscall);
|
||||||
let res = vm.initialize(bytes, "");
|
let res = vm.initialize(bytes);
|
||||||
assert!(res.is_ok(), "status-first signature must be accepted: {:?}", res);
|
assert!(res.is_ok(), "status-first signature must be accepted: {:?}", res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3015,7 +3019,7 @@ mod tests {
|
|||||||
let mut vm = VirtualMachine::default();
|
let mut vm = VirtualMachine::default();
|
||||||
vm.set_capabilities(prometeu_hal::syscalls::caps::ALL);
|
vm.set_capabilities(prometeu_hal::syscalls::caps::ALL);
|
||||||
let bytes = serialized_single_hostcall_module(syscall.clone());
|
let bytes = serialized_single_hostcall_module(syscall.clone());
|
||||||
let err = vm.initialize(bytes, "").expect_err("legacy ABI must be rejected");
|
let err = vm.initialize(bytes).expect_err("legacy ABI must be rejected");
|
||||||
match err {
|
match err {
|
||||||
VmInitError::LoaderPatchFailed(crate::vm_init_error::LoaderPatchError::ResolveFailed(
|
VmInitError::LoaderPatchFailed(crate::vm_init_error::LoaderPatchError::ResolveFailed(
|
||||||
prometeu_hal::syscalls::DeclaredLoadError::AbiMismatch {
|
prometeu_hal::syscalls::DeclaredLoadError::AbiMismatch {
|
||||||
@ -3048,7 +3052,7 @@ mod tests {
|
|||||||
let mut header = vec![0u8; 32];
|
let mut header = vec![0u8; 32];
|
||||||
header[0..4].copy_from_slice(b"PBS\0");
|
header[0..4].copy_from_slice(b"PBS\0");
|
||||||
|
|
||||||
let res = vm.initialize(header, "");
|
let res = vm.initialize(header);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
@ -3080,7 +3084,7 @@ mod tests {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
let res = vm.initialize(bytes, "");
|
let res = vm.initialize(bytes);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
|
|||||||
@ -68,11 +68,7 @@ fn patch_module_hostcalls(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VirtualMachine {
|
impl VirtualMachine {
|
||||||
pub fn initialize(
|
pub fn initialize(&mut self, program_bytes: Vec<u8>) -> Result<(), VmInitError> {
|
||||||
&mut self,
|
|
||||||
program_bytes: Vec<u8>,
|
|
||||||
entrypoint: &str,
|
|
||||||
) -> Result<(), VmInitError> {
|
|
||||||
self.program = ProgramImage::default();
|
self.program = ProgramImage::default();
|
||||||
self.pc = 0;
|
self.pc = 0;
|
||||||
self.operand_stack.clear();
|
self.operand_stack.clear();
|
||||||
@ -113,23 +109,11 @@ impl VirtualMachine {
|
|||||||
return Err(VmInitError::InvalidFormat);
|
return Err(VmInitError::InvalidFormat);
|
||||||
};
|
};
|
||||||
|
|
||||||
let pc = if entrypoint.is_empty() {
|
let pc = program
|
||||||
program.functions.first().map(|f| f.code_offset as usize).unwrap_or(0)
|
.functions
|
||||||
} else if let Ok(func_idx) = entrypoint.parse::<usize>() {
|
.first()
|
||||||
program
|
.map(|f| f.code_offset as usize)
|
||||||
.functions
|
.ok_or(VmInitError::EntrypointNotFound)?;
|
||||||
.get(func_idx)
|
|
||||||
.map(|f| f.code_offset as usize)
|
|
||||||
.ok_or(VmInitError::EntrypointNotFound)?
|
|
||||||
} else if let Some(&func_idx) = program.exports.get(entrypoint) {
|
|
||||||
program
|
|
||||||
.functions
|
|
||||||
.get(func_idx as usize)
|
|
||||||
.map(|f| f.code_offset as usize)
|
|
||||||
.ok_or(VmInitError::EntrypointNotFound)?
|
|
||||||
} else {
|
|
||||||
return Err(VmInitError::EntrypointNotFound);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.program = program;
|
self.program = program;
|
||||||
self.pc = pc;
|
self.pc = pc;
|
||||||
@ -142,6 +126,10 @@ impl VirtualMachine {
|
|||||||
self.capabilities = caps;
|
self.capabilities = caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prepare_boot_call(&mut self) {
|
||||||
|
self.prepare_call_by_index(0);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn prepare_call(&mut self, entrypoint: &str) {
|
pub fn prepare_call(&mut self, entrypoint: &str) {
|
||||||
let func_idx = if let Ok(idx) = entrypoint.parse::<usize>() {
|
let func_idx = if let Ok(idx) = entrypoint.parse::<usize>() {
|
||||||
idx
|
idx
|
||||||
@ -149,6 +137,10 @@ impl VirtualMachine {
|
|||||||
self.program.exports.get(entrypoint).map(|&idx| idx as usize).ok_or(()).unwrap_or(0)
|
self.program.exports.get(entrypoint).map(|&idx| idx as usize).ok_or(()).unwrap_or(0)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.prepare_call_by_index(func_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_call_by_index(&mut self, func_idx: usize) {
|
||||||
let callee = self.program.functions.get(func_idx).cloned().unwrap_or_default();
|
let callee = self.program.functions.get(func_idx).cloned().unwrap_or_default();
|
||||||
self.pc = callee.code_offset as usize;
|
self.pc = callee.code_offset as usize;
|
||||||
self.halted = false;
|
self.halted = false;
|
||||||
|
|||||||
@ -5,6 +5,6 @@ fn invalid_image_format_is_rejected_before_execution() {
|
|||||||
// Provide bytes that are not a valid PBS image. The VM must reject it with InvalidFormat.
|
// Provide bytes that are not a valid PBS image. The VM must reject it with InvalidFormat.
|
||||||
let program_bytes = b"NOT_PBS_IMAGE".to_vec();
|
let program_bytes = b"NOT_PBS_IMAGE".to_vec();
|
||||||
let mut vm = VirtualMachine::default();
|
let mut vm = VirtualMachine::default();
|
||||||
let result = vm.initialize(program_bytes, "0");
|
let result = vm.initialize(program_bytes);
|
||||||
assert!(matches!(result, Err(VmInitError::InvalidFormat)));
|
assert!(matches!(result, Err(VmInitError::InvalidFormat)));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -93,7 +93,7 @@ pub fn generate() -> Result<()> {
|
|||||||
if assets_pa_path.exists() {
|
if assets_pa_path.exists() {
|
||||||
fs::remove_file(&assets_pa_path)?;
|
fs::remove_file(&assets_pa_path)?;
|
||||||
}
|
}
|
||||||
fs::write(out_dir.join("manifest.json"), b"{\n \"magic\": \"PMTU\",\n \"cartridge_version\": 1,\n \"app_id\": 1,\n \"title\": \"Stress Console\",\n \"app_version\": \"0.1.0\",\n \"app_mode\": \"Game\",\n \"entrypoint\": \"main\",\n \"capabilities\": [\"gfx\", \"log\"]\n}\n")?;
|
fs::write(out_dir.join("manifest.json"), b"{\n \"magic\": \"PMTU\",\n \"cartridge_version\": 1,\n \"app_id\": 1,\n \"title\": \"Stress Console\",\n \"app_version\": \"0.1.0\",\n \"app_mode\": \"Game\",\n \"capabilities\": [\"gfx\", \"log\"]\n}\n")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user