1use javm_cap::image_cap::ImageCap;
14use javm_cap::instance::InstanceCap;
15use javm_exec::{
16 Access, CopyingMemory, EcallHandler, EcallKind, EcallResult, ExitReason, GasCounter,
17 Interpreter, PAGE_SIZE, PvmProgram, Regs, gas_cost::DEFAULT_MEM_CYCLES, unpack_bitmask,
18};
19use nub_arch_x86_abi::InvocationResult;
20use nub_kernel::{Arch, CapHash, InstanceRef, InvokeOptions, InvokeOutcome};
21
22#[derive(Default)]
24pub struct LocalArch {
25 state_root: CapHash,
26}
27
28impl LocalArch {
29 pub fn new() -> Self {
30 Self::default()
31 }
32}
33
34#[derive(Debug)]
37pub enum LocalArchError {}
38
39impl Arch for LocalArch {
40 type Error = LocalArchError;
41
42 fn invoke(
43 &mut self,
44 _target: InstanceRef,
45 _endpoint: u16,
46 _args: &[u8],
47 _opts: InvokeOptions,
48 ) -> Result<InvokeOutcome, Self::Error> {
49 Ok(InvokeOutcome {
50 return_value: 42,
51 gas_used: 0,
52 })
53 }
54
55 fn state_root(&self) -> CapHash {
56 self.state_root
57 }
58}
59
60pub fn run_instance(
72 instance: &InstanceCap,
73 image: &ImageCap,
74 endpoint_idx: u8,
75 args: [u64; 4],
76 initial_gas: u64,
77) -> InvocationResult {
78 let unpacked_bitmask = unpack_bitmask(image.bitmask.as_slice(), image.code.len());
81 let program = PvmProgram::new(
82 image.code.as_slice().to_vec(),
83 unpacked_bitmask,
84 image.jump_table.as_slice().to_vec(),
85 DEFAULT_MEM_CYCLES,
86 )
87 .expect("PvmProgram (bitmask len must match code len)");
88
89 let mut mem = CopyingMemory::new();
90 let mem_size_pages = page_round_up_u64(instance.mem_size as u64);
91 mem.map_region(0, mem_size_pages, Access::ReadWrite, None)
92 .expect("map base RW region");
93 for overlay_entry in instance.rw_overlays.iter() {
94 overlay(
95 &mut mem,
96 overlay_entry.start,
97 overlay_entry.bytes.as_slice(),
98 Access::ReadWrite,
99 );
100 }
101
102 let endpoint = image
103 .endpoints
104 .get(endpoint_idx as usize)
105 .expect("endpoint index out of range");
106
107 let mut regs = Regs::new();
108 regs.pc = endpoint.entry_pc;
109 regs.gpr = endpoint.initial_regs;
113 for (i, v) in instance.regs.iter().enumerate() {
114 if *v != 0 {
115 regs.gpr[i] = *v;
116 }
117 }
118 for (i, v) in args.iter().enumerate() {
119 regs.gpr[7 + i] = *v;
120 }
121
122 let mut gas = GasCounter::new(initial_gas);
123 let mut handler = LocalEcallHandler;
124 let exit = Interpreter::run(&program, &mut regs, &mut mem, &mut gas, &mut handler);
125
126 let (exit_reason, exit_arg) = match exit {
127 ExitReason::Halt => (0, 0),
128 ExitReason::Panic => (1, 0),
129 ExitReason::OutOfGas => (2, 0),
130 ExitReason::PageFault(addr) => (3, addr),
131 ExitReason::HostCall(imm) => (4, imm),
132 ExitReason::Ecall => (6, 0),
133 ExitReason::Trap => (7, 0),
134 };
135 InvocationResult {
136 exit_reason,
137 exit_arg,
138 return_value: regs.gpr[7],
139 gas_remaining: gas.remaining(),
140 }
141}
142
143fn page_round_up_u64(n: u64) -> u64 {
144 let p = PAGE_SIZE as u64;
145 n.div_ceil(p) * p
146}
147
148fn overlay(mem: &mut CopyingMemory, start: u32, data: &[u8], access: Access) {
151 if data.is_empty() {
152 return;
153 }
154 let size = page_round_up_u64(data.len() as u64);
155 mem.map_region(start as u64, size, access, Some(data))
156 .expect("map_region overlay");
157}
158
159struct LocalEcallHandler;
163
164impl EcallHandler for LocalEcallHandler {
165 fn handle(
166 &mut self,
167 kind: EcallKind,
168 _regs: &mut Regs,
169 _mem: &mut dyn javm_exec::Memory,
170 ) -> EcallResult {
171 match kind {
172 EcallKind::Ecalli(imm) => EcallResult::Exit(ExitReason::HostCall(imm)),
173 EcallKind::Ecall => EcallResult::Exit(ExitReason::Ecall),
174 }
175 }
176}