Skip to main content

corevm_engine/
host_calls.rs

1use alloc::vec::Vec;
2use bytes::Bytes;
3use core::ops::Range;
4use corevm_host::{
5	fs::{self, ReadBlock},
6	PageNum, PAGE_SIZE,
7};
8use jam_pvm_common::{ApiError, InvokeOutcome};
9use jam_types::{SignedGas, SEGMENT_LEN};
10
11/// A trait that abstracts JAM calls related to an inner VM.
12pub trait InnerVm {
13	fn void(&mut self, page: PageNum, num_pages: u32) -> Result<(), ApiError>;
14	fn zero(&mut self, page: PageNum, num_pages: u32) -> Result<(), ApiError>;
15	fn poke(&mut self, outer_src: &[u8], inner_dst: u32) -> Result<(), ApiError>;
16	fn peek_into(&mut self, outer_dst: &mut [u8], inner_src: u32) -> Result<(), ApiError>;
17	fn invoke(
18		&mut self,
19		gas: SignedGas,
20		regs: [u64; 13],
21	) -> Result<(InvokeOutcome, SignedGas, [u64; 13]), ApiError>;
22	fn expunge(self) -> Result<u64, ApiError>;
23
24	fn zero_poke(&mut self, outer_src: &[u8], inner_dst: u32) -> Result<(), ApiError> {
25		// Make pages writable by calling `zero`.
26		let page = PageNum::from_address(inner_dst);
27		let num_pages = (outer_src.len() as u32).div_ceil(PAGE_SIZE);
28		self.zero(page, num_pages)?;
29		// Copy the data.
30		self.poke(outer_src, inner_dst)?;
31		Ok(())
32	}
33
34	fn poke_ro_rw_data_page(
35		&mut self,
36		data: &[u8],
37		address_range: &Range<u32>,
38		address: u32,
39	) -> Result<(), ApiError> {
40		debug_assert_eq!(0, address % PAGE_SIZE);
41		debug_assert!(address_range.contains(&address));
42		let data_start = address - address_range.start;
43		if data_start < data.len() as u32 {
44			// copy non-zero data
45			let data_start = data_start as usize;
46			let data_end = (data_start + PAGE_SIZE as usize).min(data.len());
47			let data_range = data_start..data_end;
48			self.zero_poke(&data[data_range], address)?;
49		} else {
50			// zero-out the remaining data
51			let page = PageNum::from_address(address);
52			self.zero(page, 1)?;
53		}
54		Ok(())
55	}
56}
57
58/// A trait that abstracts JAM host-calls available in a refine environment and related to an outer
59/// VM.
60pub trait OuterVm {
61	/// Inner VM wrapper.
62	type InnerVm: crate::InnerVm;
63
64	/// Export `segment` from the work package.
65	///
66	/// The segment length must not be longer than [`SEGMENT_LEN`](jam_types::SEGMENT_LEN).
67	/// If it's shorter, the rest of of the bytes are zeroed.
68	fn export(&mut self, segment: &[u8; SEGMENT_LEN]) -> Result<(), ApiError>;
69
70	/// Reads file system block specified by its reference.
71	fn read_file_block(&mut self, block_ref: &fs::BlockRef) -> Option<Bytes>;
72
73	/// Creates a new inner VM and returns its wrapped handle.
74	fn machine(&mut self, code: &[u8], program_counter: u64) -> Result<Self::InnerVm, ApiError>;
75
76	/// Returns the contents of a file specified by its main block reference.
77	///
78	/// Uses [`read_file_block`](Self::read_file_block) to read file blocks.
79	fn read_file(&mut self, block_ref: &fs::BlockRef) -> Result<Vec<u8>, fs::Error> {
80		let mut block_reader = self.block_reader();
81		let buf = fs::read(block_ref, &mut block_reader)?;
82		Ok(buf)
83	}
84
85	/// Returns file system block reader that uses [`read_file_block`](Self::read_file_block) to
86	/// read file blocks.
87	fn block_reader(&mut self) -> Lookup<'_, Self> {
88		Lookup { outer_vm: self }
89	}
90}
91
92/// An implementation of [`ReadBlock`] that uses `OuterVM::read_file_block` to read files blocks.
93pub struct Lookup<'a, O: OuterVm + ?Sized> {
94	pub outer_vm: &'a mut O,
95}
96
97impl<O: OuterVm + ?Sized> ReadBlock for Lookup<'_, O> {
98	fn read_block(&mut self, block_ref: &fs::BlockRef) -> Result<Bytes, fs::IoError> {
99		self.outer_vm.read_file_block(block_ref).ok_or(fs::IoError)
100	}
101}