1use javm_cap::cap::image::ImageCap;
14use javm_cap::cap::instance::InstanceCap;
15use javm_exec::{
16 Access, CopyingMemory, EcallHandler, EcallKind, EcallResult, ExitReason, GasCounter, PAGE_SIZE,
17 Regs, gas_const, interp::Interpreter, predecode::predecode_rv_with_mem_cycles,
18};
19use nub_arch_x86_abi::{InvocationResult, SCRATCHPAD_HEAD_LEN};
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 mut mem = CopyingMemory::new();
82 mem.base = javm_cap::layout::DATA_BASE;
83 let data_extent = instance.mem.content_len();
84 let mut mem_image = vec![0u8; data_extent as usize];
85 if data_extent > 0 {
86 instance.mem.copy_into(0, &mut mem_image);
89 mem.map_region(
90 javm_cap::layout::DATA_BASE as u64,
91 data_extent,
92 Access::ReadWrite,
93 Some(&mem_image),
94 )
95 .expect("map base RW region");
96 }
97 let data_base = javm_cap::layout::DATA_BASE as u64;
100 for m in image.mappings.iter() {
101 if m.path().is_empty() || !image.mapping_is_pinned(m.start as u32) {
102 continue;
103 }
104 let off = (m.start.saturating_sub(data_base)) as usize;
105 let len = (m.size as usize).min(mem_image.len().saturating_sub(off));
106 if len > 0 {
107 overlay(
108 &mut mem,
109 m.start as u32,
110 &mem_image[off..off + len],
111 Access::ReadOnly,
112 );
113 }
114 }
115
116 let target = javm_cap::Key::from(endpoint_idx);
119 let (_, endpoint) = image
120 .endpoints
121 .iter()
122 .find(|(k, _)| *k == target)
123 .expect("endpoint key not defined");
124
125 let mut regs = Regs::new();
126 regs.pc = endpoint.entry_pc;
127 regs.gpr[..javm_cap::NUM_REGS].copy_from_slice(&endpoint.initial_regs);
133 for (i, v) in instance.regs.iter().enumerate() {
134 if *v != 0 {
135 regs.gpr[i] = *v;
136 }
137 }
138 for (i, v) in args.iter().enumerate() {
139 regs.gpr[7 + i] = *v;
140 }
141
142 let mut gas = GasCounter::new(initial_gas);
143 let mut handler = LocalEcallHandler;
144
145 let (code_base, code_bytes) = image
148 .code_mapping()
149 .expect("image has no executable code mapping");
150
151 mem.set_code_region(code_base, code_bytes.len() as u32);
155
156 let mem_cycles = gas_const::mem_cycles_for(gas_const::accessible_pages(
161 instance.mem_size(),
162 javm_cap::layout::DATA_BASE,
163 ));
164 let predecode = predecode_rv_with_mem_cycles(code_bytes, mem_cycles);
165 let exit = Interpreter::run(
166 &predecode,
167 code_bytes,
168 code_base,
169 &mut regs,
170 &mut mem,
171 &mut gas,
172 &mut handler,
173 );
174
175 let (exit_reason, exit_arg) = match exit {
176 ExitReason::Halt => (0, 0),
177 ExitReason::Panic => (1, 0),
178 ExitReason::OutOfGas => (2, 0),
179 ExitReason::PageFault(addr) => (3, addr),
180 ExitReason::HostCall(imm) => (4, imm),
181 ExitReason::Ecall => (6, 0),
182 ExitReason::Trap => (7, 0),
183 };
184
185 let mut scratchpad_head = [0u8; SCRATCHPAD_HEAD_LEN];
191 let sig_base = javm_cap::layout::DATA_BASE;
192 for (i, byte) in scratchpad_head.iter_mut().enumerate() {
193 *byte = mem.read_u8(sig_base + i as u32).unwrap_or(0);
194 }
195
196 InvocationResult {
197 exit_reason,
198 exit_arg,
199 return_value: regs.gpr[7],
200 gas_remaining: gas.remaining(),
201 scratchpad_head,
202 }
203}
204
205fn page_round_up_u64(n: u64) -> u64 {
206 let p = PAGE_SIZE as u64;
207 n.div_ceil(p) * p
208}
209
210fn overlay(mem: &mut CopyingMemory, start: u32, data: &[u8], access: Access) {
213 if data.is_empty() {
214 return;
215 }
216 let size = page_round_up_u64(data.len() as u64);
217 mem.map_region(start as u64, size, access, Some(data))
218 .expect("map_region overlay");
219}
220
221struct LocalEcallHandler;
225
226impl EcallHandler for LocalEcallHandler {
227 fn handle(
228 &mut self,
229 kind: EcallKind,
230 _regs: &mut Regs,
231 _mem: &mut dyn javm_exec::Memory,
232 ) -> EcallResult {
233 match kind {
234 EcallKind::Ecalli(imm) => EcallResult::Exit(ExitReason::HostCall(imm)),
235 EcallKind::Ecall => EcallResult::Exit(ExitReason::Ecall),
236 }
237 }
238}