javm_cap/cap/instance.rs
1//! `InstanceCap` — Instance cap with mutable working state.
2//!
3//! Holds the mutable working state of a running Cap::Instance:
4//! image reference (by hash, since Images are immutable), root
5//! cnode reference (by hash when clean / inline `Owned` while the kernel
6//! frame mutates it), the read-write memory image, register file, PC,
7//! gas counter.
8
9use alloc::boxed::Box;
10
11use crate::cache::CapHashOrRef;
12
13use super::NUM_REGS;
14use super::data::{DataCap, PAGE_SIZE};
15use super::{Cap, CapHash};
16
17#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
18pub struct InstanceCap<O = Box<Cap>> {
19 /// Cumulative chain hash identifying the Instance's type.
20 pub image_hash_chain: CapHash,
21 /// Hash of the Image cap currently bound. Always content-
22 /// addressed (Images are immutable).
23 pub image_hash: CapHash,
24 /// Reference to the root cnode. `Hash` when clean / not yet
25 /// promoted for mutation; an inline `Owned` cnode cap while the
26 /// running kernel frame is mutating it.
27 pub root_cnode: CapHashOrRef<O>,
28 /// The Instance's read-write memory image: a dense `DataCap` covering the
29 /// data extent `[DATA_BASE, DATA_BASE + mem.size)`. Holds the initial
30 /// content at boot and the settled (folded) content after each HALT — the
31 /// **immutable backing** half of the Backing+View mutability model. A
32 /// running engine wraps this in a transient CoW `DataViewCap`; at settle the
33 /// View folds back into a fresh `DataCap` that replaces this field. Pinned
34 /// (read-only) mappings are **not** stored here — they stay in their own
35 /// `Cap::Data` and are mapped RO separately, so the pinned-RO gas tier is
36 /// preserved.
37 pub mem: DataCap,
38 /// PVM register file (`φ[0]..φ[12]`).
39 pub regs: [u64; NUM_REGS],
40 /// Current PC. Zero between calls; updated to entry_pc at
41 /// invoke start and to the post-execution PC on HALT.
42 pub pc: u64,
43 /// Gas left after the last call, for callers that want to
44 /// observe residual gas. Set to 0 between calls in V1.
45 pub gas_remaining: u64,
46}
47
48impl<O> ssz::HashTreeRoot for InstanceCap<O>
49where
50 CapHashOrRef<O>: ssz::HashTreeRoot,
51{
52 fn hash_tree_root<D: ssz::digest::Digest<OutputSize = ssz::digest::typenum::U32>>(
53 &self,
54 ) -> [u8; 32] {
55 let roots: [[u8; 32]; 7] = [
56 self.image_hash_chain.hash_tree_root::<D>(),
57 self.image_hash.hash_tree_root::<D>(),
58 self.root_cnode.hash_tree_root::<D>(),
59 self.mem.hash_tree_root::<D>(),
60 self.regs.hash_tree_root::<D>(),
61 self.pc.hash_tree_root::<D>(),
62 self.gas_remaining.hash_tree_root::<D>(),
63 ];
64 ssz::merkleize::<D>(&roots, roots.len())
65 }
66}
67
68impl<O> InstanceCap<O> {
69 /// Absolute exclusive top of the data region (`DATA_BASE + mem.size`). The
70 /// data extent `[DATA_BASE, mem_size)` is what the engines map; this matches
71 /// the legacy `mem_size` field's semantics for call sites.
72 pub fn mem_size(&self) -> u32 {
73 (crate::layout::DATA_BASE as u64 + self.mem.content_len()) as u32
74 }
75
76 /// The data extent (RW memory size above `DATA_BASE`) in bytes — always a
77 /// [`PAGE_SIZE`] multiple.
78 pub fn mem_extent(&self) -> u64 {
79 self.mem.content_len()
80 }
81
82 /// The data extent as a count of [`PAGE_SIZE`] pages.
83 pub fn mem_pages(&self) -> usize {
84 (self.mem.content_len() / PAGE_SIZE as u64) as usize
85 }
86}