Skip to main content

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}