Skip to main content

sbpf_vm/
vm.rs

1use {
2    crate::{
3        compute::ComputeMeter,
4        errors::{SbpfVmError, SbpfVmResult},
5        memory::Memory,
6        syscalls::SyscallHandler,
7    },
8    sbpf_common::{
9        errors::ExecutionError,
10        execute::{self, Vm},
11        instruction::Instruction,
12        opcode::Opcode,
13    },
14    serde::{Deserialize, Serialize},
15};
16
17/// VM configuration
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct SbpfVmConfig {
20    pub max_call_depth: usize,
21    pub compute_unit_limit: u64,
22    pub heap_size: usize,
23}
24
25impl Default for SbpfVmConfig {
26    fn default() -> Self {
27        Self {
28            max_call_depth: 64,
29            compute_unit_limit: 1_400_000,
30            heap_size: Memory::DEFAULT_HEAP_SIZE,
31        }
32    }
33}
34
35/// Call frame for internal function calls
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct CallFrame {
38    pub return_pc: usize,
39    pub saved_registers: [u64; 4], // callee-saved registers (r6-r9)
40    pub saved_frame_pointer: u64,
41}
42
43/// sBPF Virtual Machine
44pub struct SbpfVm<H: SyscallHandler> {
45    pub config: SbpfVmConfig,
46    pub registers: [u64; 11],
47    pub pc: usize,
48    pub call_stack: Vec<CallFrame>,
49    pub memory: Memory,
50    pub program: Vec<Instruction>,
51    pub halted: bool,
52    pub exit_code: Option<u64>,
53    pub compute_meter: ComputeMeter,
54    pub syscall_handler: H,
55}
56
57impl<H: SyscallHandler> SbpfVm<H> {
58    pub fn new(
59        program: Vec<Instruction>,
60        input: Vec<u8>,
61        rodata: Vec<u8>,
62        syscall_handler: H,
63    ) -> Self {
64        Self::new_with_config(
65            program,
66            input,
67            rodata,
68            syscall_handler,
69            SbpfVmConfig::default(),
70        )
71    }
72
73    pub fn new_with_config(
74        program: Vec<Instruction>,
75        input: Vec<u8>,
76        rodata: Vec<u8>,
77        syscall_handler: H,
78        config: SbpfVmConfig,
79    ) -> Self {
80        let memory = Memory::new(
81            input,
82            rodata,
83            Memory::stack_size(config.max_call_depth),
84            config.heap_size,
85        );
86
87        let mut registers = [0u64; 11];
88        registers[1] = Memory::INPUT_START;
89        registers[10] = memory.initial_frame_pointer();
90
91        Self {
92            registers,
93            pc: 0,
94            call_stack: Vec::new(),
95            memory,
96            program,
97            halted: false,
98            exit_code: None,
99            compute_meter: ComputeMeter::new(config.compute_unit_limit),
100            syscall_handler,
101            config,
102        }
103    }
104
105    pub fn reset(&mut self) {
106        self.registers = [0u64; 11];
107        self.registers[1] = Memory::INPUT_START;
108        self.registers[10] = self.memory.initial_frame_pointer();
109        self.pc = 0;
110        self.call_stack.clear();
111        self.halted = false;
112        self.exit_code = None;
113        self.compute_meter.reset();
114        self.memory.reset_heap();
115    }
116
117    pub fn current_instruction(&self) -> SbpfVmResult<&Instruction> {
118        self.program
119            .get(self.pc)
120            .ok_or(SbpfVmError::PcOutOfBounds(self.pc))
121    }
122
123    pub fn set_entrypoint(&mut self, pc: usize) {
124        self.pc = pc;
125    }
126
127    pub fn is_pc_valid(&self) -> bool {
128        self.pc < self.program.len()
129    }
130
131    pub fn get_remaining(&self) -> u64 {
132        self.compute_meter.get_remaining()
133    }
134
135    pub fn step(&mut self) -> SbpfVmResult<()> {
136        if self.halted {
137            return Ok(());
138        }
139
140        if !self.is_pc_valid() {
141            return Err(SbpfVmError::PcOutOfBounds(self.pc));
142        }
143
144        self.compute_meter.consume(1)?;
145
146        let inst = self.current_instruction()?.clone();
147        self.execute_instruction(&inst)?;
148
149        Ok(())
150    }
151
152    fn execute_instruction(&mut self, inst: &Instruction) -> SbpfVmResult<()> {
153        match inst.opcode {
154            // ALU 64-bit instructions
155            Opcode::Add64Imm
156            | Opcode::Sub64Imm
157            | Opcode::Mul64Imm
158            | Opcode::Div64Imm
159            | Opcode::Or64Imm
160            | Opcode::And64Imm
161            | Opcode::Lsh64Imm
162            | Opcode::Rsh64Imm
163            | Opcode::Mod64Imm
164            | Opcode::Xor64Imm
165            | Opcode::Mov64Imm
166            | Opcode::Arsh64Imm => execute::execute_binary_immediate(self, inst)?,
167            Opcode::Add64Reg
168            | Opcode::Sub64Reg
169            | Opcode::Mul64Reg
170            | Opcode::Div64Reg
171            | Opcode::Or64Reg
172            | Opcode::And64Reg
173            | Opcode::Lsh64Reg
174            | Opcode::Rsh64Reg
175            | Opcode::Mod64Reg
176            | Opcode::Xor64Reg
177            | Opcode::Mov64Reg
178            | Opcode::Arsh64Reg => execute::execute_binary_register(self, inst)?,
179
180            // ALU 32-bit instructions
181            Opcode::Add32Imm
182            | Opcode::Sub32Imm
183            | Opcode::Mul32Imm
184            | Opcode::Div32Imm
185            | Opcode::Or32Imm
186            | Opcode::And32Imm
187            | Opcode::Lsh32Imm
188            | Opcode::Rsh32Imm
189            | Opcode::Mod32Imm
190            | Opcode::Xor32Imm
191            | Opcode::Mov32Imm
192            | Opcode::Arsh32Imm => execute::execute_binary_immediate(self, inst)?,
193            Opcode::Add32Reg
194            | Opcode::Sub32Reg
195            | Opcode::Mul32Reg
196            | Opcode::Div32Reg
197            | Opcode::Or32Reg
198            | Opcode::And32Reg
199            | Opcode::Lsh32Reg
200            | Opcode::Rsh32Reg
201            | Opcode::Mod32Reg
202            | Opcode::Xor32Reg
203            | Opcode::Mov32Reg
204            | Opcode::Arsh32Reg => execute::execute_binary_register(self, inst)?,
205
206            // Unary and endian instructions
207            Opcode::Neg64 | Opcode::Neg32 | Opcode::Le | Opcode::Be => {
208                execute::execute_unary(self, inst)?
209            }
210
211            // Load instructions
212            Opcode::Lddw => execute::execute_load_immediate(self, inst)?,
213            Opcode::Ldxb | Opcode::Ldxh | Opcode::Ldxw | Opcode::Ldxdw => {
214                execute::execute_load_memory(self, inst)?
215            }
216
217            // Store immediate instructions
218            Opcode::Stb | Opcode::Sth | Opcode::Stw | Opcode::Stdw => {
219                execute::execute_store_immediate(self, inst)?
220            }
221
222            // Store register instructions
223            Opcode::Stxb | Opcode::Stxh | Opcode::Stxw | Opcode::Stxdw => {
224                execute::execute_store_register(self, inst)?
225            }
226
227            // Jump instructions
228            Opcode::Ja => execute::execute_jump(self, inst)?,
229            Opcode::JeqImm
230            | Opcode::JgtImm
231            | Opcode::JgeImm
232            | Opcode::JltImm
233            | Opcode::JleImm
234            | Opcode::JsetImm
235            | Opcode::JneImm
236            | Opcode::JsgtImm
237            | Opcode::JsgeImm
238            | Opcode::JsltImm
239            | Opcode::JsleImm => execute::execute_jump_immediate(self, inst)?,
240            Opcode::JeqReg
241            | Opcode::JgtReg
242            | Opcode::JgeReg
243            | Opcode::JltReg
244            | Opcode::JleReg
245            | Opcode::JsetReg
246            | Opcode::JneReg
247            | Opcode::JsgtReg
248            | Opcode::JsgeReg
249            | Opcode::JsltReg
250            | Opcode::JsleReg => execute::execute_jump_register(self, inst)?,
251
252            // Call instructions
253            Opcode::Call => execute::execute_call_immediate(self, inst)?,
254            Opcode::Callx => execute::execute_call_register(self, inst)?,
255
256            // Exit instruction
257            Opcode::Exit => execute::execute_exit(self, inst)?,
258
259            _ => return Err(SbpfVmError::InvalidInstruction),
260        }
261        Ok(())
262    }
263
264    pub fn run(&mut self) -> SbpfVmResult<()> {
265        let mut steps = 0;
266
267        while !self.halted && steps < self.config.compute_unit_limit {
268            self.step()?;
269            steps += 1;
270        }
271
272        if !self.halted && steps >= self.config.compute_unit_limit {
273            return Err(SbpfVmError::ExecutionLimitReached(
274                self.config.compute_unit_limit,
275            ));
276        }
277
278        Ok(())
279    }
280}
281
282impl<H: SyscallHandler> Vm for SbpfVm<H> {
283    fn get_register(&self, reg: usize) -> u64 {
284        self.registers[reg]
285    }
286
287    fn set_register(&mut self, reg: usize, value: u64) {
288        self.registers[reg] = value;
289    }
290
291    fn get_pc(&self) -> usize {
292        self.pc
293    }
294
295    fn set_pc(&mut self, pc: usize) {
296        self.pc = pc;
297    }
298
299    fn read_u8(&self, addr: u64) -> Result<u8, ExecutionError> {
300        self.memory
301            .read_u8(addr)
302            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
303    }
304
305    fn read_u16(&self, addr: u64) -> Result<u16, ExecutionError> {
306        self.memory
307            .read_u16(addr)
308            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
309    }
310
311    fn read_u32(&self, addr: u64) -> Result<u32, ExecutionError> {
312        self.memory
313            .read_u32(addr)
314            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
315    }
316
317    fn read_u64(&self, addr: u64) -> Result<u64, ExecutionError> {
318        self.memory
319            .read_u64(addr)
320            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
321    }
322
323    fn write_u8(&mut self, addr: u64, value: u8) -> Result<(), ExecutionError> {
324        self.memory
325            .write_u8(addr, value)
326            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
327    }
328
329    fn write_u16(&mut self, addr: u64, value: u16) -> Result<(), ExecutionError> {
330        self.memory
331            .write_u16(addr, value)
332            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
333    }
334
335    fn write_u32(&mut self, addr: u64, value: u32) -> Result<(), ExecutionError> {
336        self.memory
337            .write_u32(addr, value)
338            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
339    }
340
341    fn write_u64(&mut self, addr: u64, value: u64) -> Result<(), ExecutionError> {
342        self.memory
343            .write_u64(addr, value)
344            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
345    }
346
347    fn get_call_depth(&self) -> usize {
348        self.call_stack.len()
349    }
350
351    fn max_call_depth(&self) -> usize {
352        self.config.max_call_depth
353    }
354
355    fn push_frame(
356        &mut self,
357        return_pc: usize,
358        saved_registers: [u64; 4],
359        saved_frame_pointer: u64,
360    ) -> Result<(), ExecutionError> {
361        self.call_stack.push(CallFrame {
362            return_pc,
363            saved_registers,
364            saved_frame_pointer,
365        });
366        Ok(())
367    }
368
369    fn pop_frame(&mut self) -> Option<(usize, [u64; 4], u64)> {
370        self.call_stack.pop().map(|frame| {
371            (
372                frame.return_pc,
373                frame.saved_registers,
374                frame.saved_frame_pointer,
375            )
376        })
377    }
378
379    fn halt(&mut self, exit_code: u64) {
380        self.halted = true;
381        self.exit_code = Some(exit_code);
382    }
383
384    fn get_stack_frame_size(&self) -> u64 {
385        Memory::STACK_FRAME_SIZE
386    }
387
388    fn handle_syscall(&mut self, name: &str) -> Result<u64, ExecutionError> {
389        let registers = [
390            self.registers[1],
391            self.registers[2],
392            self.registers[3],
393            self.registers[4],
394            self.registers[5],
395        ];
396        self.syscall_handler
397            .handle(
398                name,
399                registers,
400                &mut self.memory,
401                self.compute_meter.clone(),
402            )
403            .map_err(|e| ExecutionError::SyscallError(e.to_string()))
404    }
405}
406
407#[cfg(test)]
408mod tests {
409    use {
410        super::*,
411        crate::syscalls::MockSyscallHandler,
412        either::Either,
413        sbpf_common::{
414            inst_param::{Number, Register},
415            opcode::Opcode,
416        },
417    };
418
419    fn make_test_instruction(
420        opcode: sbpf_common::opcode::Opcode,
421        dst: Option<sbpf_common::inst_param::Register>,
422        src: Option<sbpf_common::inst_param::Register>,
423        off: Option<Either<String, i16>>,
424        imm: Option<Either<String, Number>>,
425    ) -> Instruction {
426        Instruction {
427            opcode,
428            dst,
429            src,
430            off,
431            imm,
432            span: 0..0,
433        }
434    }
435
436    #[test]
437    fn test_vm_initialization() {
438        let program = vec![make_test_instruction(Opcode::Exit, None, None, None, None)];
439        let vm = SbpfVm::new(
440            program,
441            vec![1, 2, 3, 4],
442            vec![],
443            MockSyscallHandler::default(),
444        );
445
446        assert_eq!(vm.pc, 0);
447        assert_eq!(vm.registers[1], Memory::INPUT_START);
448        assert_eq!(
449            vm.registers[10],
450            Memory::STACK_START + Memory::STACK_FRAME_SIZE
451        );
452        assert!(!vm.halted);
453        assert_eq!(vm.exit_code, None);
454    }
455
456    #[test]
457    fn test_vm_reset() {
458        let program = vec![make_test_instruction(Opcode::Exit, None, None, None, None)];
459        let mut vm = SbpfVm::new(
460            program,
461            vec![1, 2, 3, 4],
462            vec![],
463            MockSyscallHandler::default(),
464        );
465
466        // modify vm
467        vm.registers[0] = 11;
468        vm.pc = 10;
469        vm.halted = true;
470        vm.exit_code = Some(1);
471
472        // reset
473        vm.reset();
474
475        assert_eq!(vm.pc, 0);
476        assert_eq!(vm.registers[0], 0);
477        assert_eq!(vm.registers[1], Memory::INPUT_START);
478        assert!(!vm.halted);
479        assert_eq!(vm.exit_code, None);
480    }
481
482    #[test]
483    fn test_current_instruction() {
484        let program = vec![
485            make_test_instruction(
486                Opcode::Mov64Imm,
487                Some(Register { n: 0 }),
488                None,
489                None,
490                Some(Either::Right(Number::Int(123))),
491            ),
492            make_test_instruction(Opcode::Exit, None, None, None, None),
493        ];
494        let vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
495
496        let inst = vm.current_instruction().unwrap();
497        assert_eq!(inst.opcode, Opcode::Mov64Imm);
498    }
499
500    #[test]
501    fn test_load_store() {
502        // lddw r1, 0x12345
503        // mov64 r2, r10
504        // sub r2, 8
505        // stxdw [r2 + 0], r1
506        // ldxdw r3, [r2 + 0]
507        let program = vec![
508            make_test_instruction(
509                Opcode::Lddw,
510                Some(Register { n: 1 }),
511                None,
512                None,
513                Some(Either::Right(Number::Int(0x12345u64 as i64))),
514            ),
515            make_test_instruction(
516                Opcode::Mov64Reg,
517                Some(Register { n: 2 }),
518                Some(Register { n: 10 }),
519                None,
520                None,
521            ),
522            make_test_instruction(
523                Opcode::Sub64Imm,
524                Some(Register { n: 2 }),
525                None,
526                None,
527                Some(Either::Right(Number::Int(8))),
528            ),
529            make_test_instruction(
530                Opcode::Stxdw,
531                Some(Register { n: 2 }),
532                Some(Register { n: 1 }),
533                Some(Either::Right(0)),
534                None,
535            ),
536            make_test_instruction(
537                Opcode::Ldxdw,
538                Some(Register { n: 3 }),
539                Some(Register { n: 2 }),
540                Some(Either::Right(0)),
541                None,
542            ),
543        ];
544
545        let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
546
547        for _ in 0..5 {
548            vm.step().unwrap();
549        }
550
551        assert_eq!(vm.registers[3], 0x12345);
552    }
553
554    #[test]
555    fn test_alu64_operations() {
556        // mov64 r1, 10
557        // add64 r1, 5
558        // mul r1, 2
559        let program = vec![
560            make_test_instruction(
561                Opcode::Mov64Imm,
562                Some(Register { n: 1 }),
563                None,
564                None,
565                Some(Either::Right(Number::Int(10))),
566            ),
567            make_test_instruction(
568                Opcode::Add64Imm,
569                Some(Register { n: 1 }),
570                None,
571                None,
572                Some(Either::Right(Number::Int(5))),
573            ),
574            make_test_instruction(
575                Opcode::Mul64Imm,
576                Some(Register { n: 1 }),
577                None,
578                None,
579                Some(Either::Right(Number::Int(2))),
580            ),
581        ];
582
583        let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
584
585        vm.step().unwrap();
586        assert_eq!(vm.registers[1], 10);
587
588        vm.step().unwrap();
589        assert_eq!(vm.registers[1], 15);
590
591        vm.step().unwrap();
592        assert_eq!(vm.registers[1], 30);
593    }
594
595    #[test]
596    fn test_memory_regions() {
597        // Check input region
598        let input = vec![1, 2, 3, 4, 5, 6, 7, 8];
599        let rodata = vec![10, 20, 30, 40];
600
601        let program = vec![make_test_instruction(Opcode::Exit, None, None, None, None)];
602        let vm = SbpfVm::new(program, input, rodata, MockSyscallHandler::default());
603
604        assert_eq!(vm.memory.read_u8(Memory::INPUT_START).unwrap(), 1);
605        assert_eq!(
606            vm.memory.read_u64(Memory::INPUT_START).unwrap(),
607            0x0807060504030201u64
608        );
609
610        // Check rodata region
611        assert_eq!(vm.memory.read_u8(Memory::RODATA_START).unwrap(), 10);
612    }
613
614    #[test]
615    fn test_program_without_exit() {
616        let program = vec![
617            make_test_instruction(
618                Opcode::Mov64Imm,
619                Some(Register { n: 0 }),
620                None,
621                None,
622                Some(Either::Right(Number::Int(10))),
623            ),
624            make_test_instruction(
625                Opcode::Add64Imm,
626                Some(Register { n: 0 }),
627                None,
628                None,
629                Some(Either::Right(Number::Int(8))),
630            ),
631            // no exit instruction
632        ];
633
634        let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
635
636        vm.step().unwrap();
637        assert_eq!(vm.pc, 1);
638
639        vm.step().unwrap();
640        assert_eq!(vm.pc, 2);
641
642        let result = vm.step();
643        assert!(result.is_err());
644        assert!(matches!(result, Err(SbpfVmError::PcOutOfBounds(2))));
645    }
646
647    #[test]
648    fn test_step_complete_program() {
649        // mov64 r1, 10
650        // add64 r1, 5
651        // mul r1, 3
652        // sub r1, 7
653        // exit
654        let program = vec![
655            make_test_instruction(
656                Opcode::Mov64Imm,
657                Some(Register { n: 1 }),
658                None,
659                None,
660                Some(Either::Right(Number::Int(10))),
661            ),
662            make_test_instruction(
663                Opcode::Add64Imm,
664                Some(Register { n: 1 }),
665                None,
666                None,
667                Some(Either::Right(Number::Int(5))),
668            ),
669            make_test_instruction(
670                Opcode::Mul64Imm,
671                Some(Register { n: 1 }),
672                None,
673                None,
674                Some(Either::Right(Number::Int(3))),
675            ),
676            make_test_instruction(
677                Opcode::Sub64Imm,
678                Some(Register { n: 1 }),
679                None,
680                None,
681                Some(Either::Right(Number::Int(7))),
682            ),
683            make_test_instruction(Opcode::Exit, None, None, None, None),
684        ];
685
686        let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
687
688        vm.step().unwrap();
689        assert_eq!(vm.pc, 1);
690        assert_eq!(vm.registers[1], 10);
691        assert_eq!(vm.compute_meter.get_consumed(), 1);
692        assert!(!vm.halted);
693
694        vm.step().unwrap();
695        assert_eq!(vm.pc, 2);
696        assert_eq!(vm.registers[1], 15);
697        assert_eq!(vm.compute_meter.get_consumed(), 2);
698        assert!(!vm.halted);
699
700        vm.step().unwrap();
701        assert_eq!(vm.pc, 3);
702        assert_eq!(vm.registers[1], 45);
703        assert_eq!(vm.compute_meter.get_consumed(), 3);
704        assert!(!vm.halted);
705
706        vm.step().unwrap();
707        assert_eq!(vm.pc, 4);
708        assert_eq!(vm.registers[1], 38);
709        assert_eq!(vm.compute_meter.get_consumed(), 4);
710        assert!(!vm.halted);
711
712        vm.step().unwrap();
713        assert_eq!(vm.pc, 4);
714        assert_eq!(vm.registers[1], 38);
715        assert_eq!(vm.compute_meter.get_consumed(), 5);
716        assert!(vm.halted);
717    }
718
719    #[test]
720    fn test_run_complete_program() {
721        // mov64 r1, 10
722        // add64 r1, 5
723        // mul r1, 3
724        // sub r1, 7
725        // exit
726        let program = vec![
727            make_test_instruction(
728                Opcode::Mov64Imm,
729                Some(Register { n: 1 }),
730                None,
731                None,
732                Some(Either::Right(Number::Int(10))),
733            ),
734            make_test_instruction(
735                Opcode::Add64Imm,
736                Some(Register { n: 1 }),
737                None,
738                None,
739                Some(Either::Right(Number::Int(5))),
740            ),
741            make_test_instruction(
742                Opcode::Mul64Imm,
743                Some(Register { n: 1 }),
744                None,
745                None,
746                Some(Either::Right(Number::Int(3))),
747            ),
748            make_test_instruction(
749                Opcode::Sub64Imm,
750                Some(Register { n: 1 }),
751                None,
752                None,
753                Some(Either::Right(Number::Int(7))),
754            ),
755            make_test_instruction(Opcode::Exit, None, None, None, None),
756        ];
757
758        let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
759
760        vm.run().unwrap();
761
762        assert!(vm.halted);
763        assert_eq!(vm.registers[1], 38);
764        assert_eq!(vm.pc, 4);
765        assert_eq!(vm.compute_meter.get_consumed(), 5);
766    }
767
768    #[test]
769    fn test_program_with_input() {
770        // ldxdw r2, [r1 + 0]
771        // ldxdw r3, [r1 + 8]
772        // mov64 r4, r2
773        // add64 r4, r3
774        // exit
775
776        let mut input = Vec::new();
777        input.extend_from_slice(&10u64.to_le_bytes());
778        input.extend_from_slice(&20u64.to_le_bytes());
779
780        let program = vec![
781            make_test_instruction(
782                Opcode::Ldxdw,
783                Some(Register { n: 2 }),
784                Some(Register { n: 1 }),
785                Some(Either::Right(0)),
786                None,
787            ),
788            make_test_instruction(
789                Opcode::Ldxdw,
790                Some(Register { n: 3 }),
791                Some(Register { n: 1 }),
792                Some(Either::Right(8)),
793                None,
794            ),
795            make_test_instruction(
796                Opcode::Mov64Reg,
797                Some(Register { n: 4 }),
798                Some(Register { n: 2 }),
799                None,
800                None,
801            ),
802            make_test_instruction(
803                Opcode::Add64Reg,
804                Some(Register { n: 4 }),
805                Some(Register { n: 3 }),
806                None,
807                None,
808            ),
809            make_test_instruction(Opcode::Exit, None, None, None, None),
810        ];
811
812        let mut vm = SbpfVm::new(program, input, vec![], MockSyscallHandler::default());
813
814        vm.run().unwrap();
815
816        assert!(vm.halted);
817        assert_eq!(vm.registers[2], 10);
818        assert_eq!(vm.registers[3], 20);
819        assert_eq!(vm.registers[4], 30);
820        assert_eq!(vm.compute_meter.get_consumed(), 5);
821    }
822
823    #[test]
824    fn test_program_with_internal_function_call() {
825        // call test
826        // lddw r2, 0x2
827        // exit
828        //
829        // test:
830        //   lddw r1, 0x1
831        //   exit
832        let program = vec![
833            make_test_instruction(
834                Opcode::Call,
835                None,
836                None,
837                None,
838                Some(Either::Right(Number::Int(2))),
839            ),
840            make_test_instruction(
841                Opcode::Lddw,
842                Some(Register { n: 2 }),
843                None,
844                None,
845                Some(Either::Right(Number::Int(0x2))),
846            ),
847            make_test_instruction(Opcode::Exit, None, None, None, None),
848            make_test_instruction(
849                Opcode::Lddw,
850                Some(Register { n: 1 }),
851                None,
852                None,
853                Some(Either::Right(Number::Int(0x1))),
854            ),
855            make_test_instruction(Opcode::Exit, None, None, None, None),
856        ];
857
858        let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
859
860        vm.run().unwrap();
861
862        assert!(vm.halted);
863        assert_eq!(vm.registers[1], 0x1);
864        assert_eq!(vm.registers[2], 0x2);
865    }
866}