risc0_circuit_rv32im/execute/
segment.rs

1// Copyright 2025 RISC Zero, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::cell::Cell;
16
17use anyhow::Result;
18use derive_more::Debug;
19use risc0_binfmt::MemoryImage;
20use serde::{Deserialize, Serialize};
21
22use crate::{
23    execute::{testutil, Executor},
24    Rv32imV2Claim, MAX_INSN_CYCLES, MAX_INSN_CYCLES_LOWER_PO2,
25};
26
27use super::{Syscall, SyscallContext};
28
29#[derive(Clone, Debug, Serialize, Deserialize)]
30#[non_exhaustive]
31pub struct Segment {
32    /// Initial sparse memory state for the segment
33    pub partial_image: MemoryImage,
34
35    pub claim: Rv32imV2Claim,
36
37    /// Recorded host->guest IO, one entry per read
38    #[debug("{}", read_record.len())]
39    pub read_record: Vec<Vec<u8>>,
40
41    /// Recorded rlen of guest->host IO, one entry per write
42    #[debug("{}", write_record.len())]
43    pub write_record: Vec<u32>,
44
45    /// Cycle at which we suspend
46    pub suspend_cycle: u32,
47
48    /// Total paging cycles
49    pub paging_cycles: u32,
50
51    pub segment_threshold: u32,
52
53    pub po2: u32,
54
55    pub index: u64,
56}
57
58impl Segment {
59    pub fn encode(&self) -> Result<Vec<u8>> {
60        Ok(postcard::to_stdvec(self)?)
61    }
62
63    pub fn decode(bytes: &[u8]) -> Result<Self> {
64        Ok(postcard::from_bytes(bytes)?)
65    }
66
67    pub fn execute(&self) -> Result<()> {
68        let handler = SegmentSyscallHandler {
69            segment: self,
70            read_pos: Cell::new(0),
71            write_pos: Cell::new(0),
72        };
73
74        let max_insn_cycles = if self.po2 >= 15 {
75            MAX_INSN_CYCLES
76        } else {
77            MAX_INSN_CYCLES_LOWER_PO2
78        };
79        Executor::new(self.partial_image.clone(), &handler, None, vec![]).run(
80            self.po2 as usize,
81            max_insn_cycles,
82            testutil::DEFAULT_SESSION_LIMIT,
83            |_| Ok(()),
84        )?;
85        Ok(())
86    }
87}
88
89struct SegmentSyscallHandler<'a> {
90    segment: &'a Segment,
91    read_pos: Cell<usize>,
92    write_pos: Cell<usize>,
93}
94
95impl Syscall for SegmentSyscallHandler<'_> {
96    fn host_read(&self, _ctx: &mut dyn SyscallContext, _fd: u32, buf: &mut [u8]) -> Result<u32> {
97        let pos = self.read_pos.replace(self.read_pos.get() + 1);
98        let read_record = &self.segment.read_record[pos];
99        buf.copy_from_slice(read_record);
100        Ok(read_record.len() as u32)
101    }
102
103    fn host_write(&self, _ctx: &mut dyn SyscallContext, _fd: u32, _buf: &[u8]) -> Result<u32> {
104        let pos = self.write_pos.replace(self.read_pos.get() + 1);
105        Ok(self.segment.write_record[pos])
106    }
107}