Skip to main content

javm_exec/
ecall.rs

1//! `EcallHandler` trait: how the execution engine dispatches ecalls
2//! to the integration layer.
3//!
4//! Per architecture: the engine knows there are ecalls and that each
5//! carries a kind (PVM `ecall` opcode 3 with no immediate, vs PVM
6//! `ecalli` opcode 10 with a u32 immediate). It doesn't know what the
7//! kind *means*. The caller supplies an `EcallHandler` that
8//! interprets ecalls as MGMT operations, host-call selectors, CALL /
9//! HALT / yield transfers, etc.
10//!
11//! The handler may either:
12//!
13//! - Return `Continue` — engine continues at the current PC
14//!   (already advanced past the ecall instruction before the handler
15//!   runs). Used for purely-stateful ecalls (MGMT_COPY, MGMT_MOVE,
16//!   etc.) that just mutate `regs` / `mem` and resume.
17//!
18//! - Return `Exit(reason)` — engine returns this `ExitReason` from
19//!   `execute()`. Used for control-flow ecalls (HALT, yield, CALL
20//!   into another Instance) that require the integration layer.
21
22use crate::exit::ExitReason;
23use crate::mem::Memory;
24use crate::regs::Regs;
25
26/// Which PVM ecall opcode triggered this invocation.
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28pub enum EcallKind {
29    /// PVM `ecall` (opcode 3). No immediate; the handler reads
30    /// `regs[11]` (mgmt op) and `regs[12]` (subject|object) per the
31    /// v3 ABI convention.
32    Ecall,
33    /// PVM `ecalli` (opcode 10). Carries a u32 immediate payload.
34    Ecalli(u32),
35}
36
37/// Result of handling one ecall.
38#[derive(Clone, Debug, PartialEq, Eq)]
39pub enum EcallResult {
40    /// Engine continues at the current PC (advanced past the ecall).
41    Continue,
42    /// Engine exits with the given reason.
43    Exit(ExitReason),
44}
45
46/// Trait the integration layer implements to interpret ecalls.
47///
48/// PC has been advanced past the instruction by the engine; the
49/// handler operates on the post-advance register/memory state.
50pub trait EcallHandler {
51    fn handle(&mut self, kind: EcallKind, regs: &mut Regs, mem: &mut dyn Memory) -> EcallResult;
52}
53
54/// A no-op handler: every ecall exits with `Panic`. Useful as a
55/// default for tests where the engine isn't supposed to encounter
56/// ecalls.
57#[derive(Debug, Default)]
58pub struct PanickingHandler;
59
60impl EcallHandler for PanickingHandler {
61    fn handle(&mut self, _kind: EcallKind, _regs: &mut Regs, _mem: &mut dyn Memory) -> EcallResult {
62        EcallResult::Exit(ExitReason::Panic)
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use crate::mem::Mem;
70
71    #[test]
72    fn panicking_handler_always_exits_panic() {
73        let mut h = PanickingHandler;
74        let mut regs = Regs::new();
75        let mut mem = Mem::new();
76        assert_eq!(
77            h.handle(EcallKind::Ecall, &mut regs, &mut mem),
78            EcallResult::Exit(ExitReason::Panic)
79        );
80        assert_eq!(
81            h.handle(EcallKind::Ecalli(42), &mut regs, &mut mem),
82            EcallResult::Exit(ExitReason::Panic)
83        );
84    }
85
86    /// A handler that increments φ₀ on every ecall (any kind).
87    struct CountingHandler {
88        count: u32,
89    }
90    impl EcallHandler for CountingHandler {
91        fn handle(
92            &mut self,
93            _kind: EcallKind,
94            regs: &mut Regs,
95            _mem: &mut dyn Memory,
96        ) -> EcallResult {
97            self.count += 1;
98            regs.write(0, regs.read(0).wrapping_add(1));
99            EcallResult::Continue
100        }
101    }
102
103    #[test]
104    fn counting_handler_mutates_regs_and_continues() {
105        let mut h = CountingHandler { count: 0 };
106        let mut regs = Regs::new();
107        let mut mem = Mem::new();
108        assert_eq!(
109            h.handle(EcallKind::Ecall, &mut regs, &mut mem),
110            EcallResult::Continue
111        );
112        assert_eq!(regs.read(0), 1);
113        assert_eq!(
114            h.handle(EcallKind::Ecalli(7), &mut regs, &mut mem),
115            EcallResult::Continue
116        );
117        assert_eq!(regs.read(0), 2);
118        assert_eq!(h.count, 2);
119    }
120}