diff --git a/.gitignore b/.gitignore index bc46f232f..e751f11fb 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ report.json table_cache_dev_* .DS_Store .env +proptest-regressions/ diff --git a/Cargo.lock b/Cargo.lock index 35ff0505a..e60101e45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -197,6 +197,29 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "bytecheck" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c8f430744b23b54ad15161fcbc22d82a29b73eacbe425fea23ec822600bc6f" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "rancor", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523363cbe1df49b68215efdf500b103ac3b0fb4836aed6d15689a076eadb8fff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "bytemuck" version = "1.20.0" @@ -217,9 +240,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.3" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" +checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" dependencies = [ "shlex", ] @@ -233,7 +256,6 @@ name = "ceno_emul" version = "0.1.0" dependencies = [ "anyhow", - "ceno-examples", "elf", "itertools 0.13.0", "num-derive", @@ -244,6 +266,18 @@ dependencies = [ "tracing", ] +[[package]] +name = "ceno_host" +version = "0.1.0" +dependencies = [ + "anyhow", + "ceno-examples", + "ceno_emul", + "itertools 0.13.0", + "rand", + "rkyv", +] + [[package]] name = "ceno_zkvm" version = "0.1.0" @@ -1065,6 +1099,26 @@ dependencies = [ "tracing", ] +[[package]] +name = "munge" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64142d38c84badf60abf06ff9bd80ad2174306a5b11bd4706535090a30a419df" +dependencies = [ + "munge_macro", +] + +[[package]] +name = "munge_macro" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "nix" version = "0.27.1" @@ -1397,7 +1451,7 @@ dependencies = [ "smallvec", "symbolic-demangle", "tempfile", - "thiserror 2.0.6", + "thiserror 2.0.7", ] [[package]] @@ -1452,6 +1506,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "ptr_meta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "quick-xml" version = "0.37.1" @@ -1476,6 +1550,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rancor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947" +dependencies = [ + "ptr_meta", +] + [[package]] name = "rand" version = "0.8.5" @@ -1591,6 +1674,15 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "rend" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35e8a6bf28cd121053a66aa2e6a2e3eaffad4a60012179f0e864aa5ffeff215" +dependencies = [ + "bytecheck", +] + [[package]] name = "rgb" version = "0.8.50" @@ -1600,6 +1692,33 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "rkyv" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11a153aec4a6ab60795f8ebe2923c597b16b05bb1504377451e705ef1a45323" +dependencies = [ + "bytecheck", + "hashbrown 0.15.2", + "munge", + "ptr_meta", + "rancor", + "rend", + "rkyv_derive", + "tinyvec", +] + +[[package]] +name = "rkyv_derive" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beb382a4d9f53bd5c0be86b10d8179c3f8a14c30bf774ff77096ed6581e35981" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "rrs-succinct" version = "0.1.0" @@ -1704,6 +1823,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "smallvec" version = "1.13.2" @@ -1869,11 +1994,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.6" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767" dependencies = [ - "thiserror-impl 2.0.6", + "thiserror-impl 2.0.7", ] [[package]] @@ -1889,9 +2014,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.6" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36" dependencies = [ "proc-macro2", "quote", @@ -1927,6 +2052,21 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "toml_datetime" version = "0.6.8" diff --git a/Cargo.toml b/Cargo.toml index 93cabd8d9..5a8c224d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,15 @@ [workspace] -exclude = ["guest"] +exclude = ["ceno_rt", "examples"] members = [ "ceno_emul", + "ceno_host", + "ceno_zkvm", "examples-builder", "mpcs", "multilinear_extensions", + "poseidon", "sumcheck", "transcript", - "ceno_zkvm", - "poseidon", ] resolver = "2" @@ -22,6 +23,7 @@ repository = "https://github.com/scroll-tech/ceno" version = "0.1.0" [workspace.dependencies] +anyhow = { version = "1.0", default-features = false } ark-std = "0.4" cfg-if = "1.0" criterion = { version = "0.5", features = ["html_reports"] } diff --git a/ceno_emul/Cargo.toml b/ceno_emul/Cargo.toml index 74562142a..c566df0f5 100644 --- a/ceno_emul/Cargo.toml +++ b/ceno_emul/Cargo.toml @@ -10,7 +10,7 @@ repository.workspace = true version.workspace = true [dependencies] -anyhow = { version = "1.0", default-features = false } +anyhow.workspace = true elf = "0.7" itertools.workspace = true num-derive.workspace = true @@ -20,9 +20,6 @@ strum.workspace = true strum_macros.workspace = true tracing.workspace = true -[dev-dependencies] -ceno-examples = { path = "../examples-builder" } - [features] default = ["forbid_overflow"] forbid_overflow = [] diff --git a/ceno_emul/src/addr.rs b/ceno_emul/src/addr.rs index ff8f7c0a0..d9069e1a0 100644 --- a/ceno_emul/src/addr.rs +++ b/ceno_emul/src/addr.rs @@ -16,6 +16,7 @@ use std::{ fmt, + iter::Step, ops::{self, Range}, }; @@ -106,6 +107,18 @@ impl WordAddr { } } +impl Step for WordAddr { + fn steps_between(start: &Self, end: &Self) -> (usize, Option) { + u32::steps_between(&start.0, &end.0) + } + fn forward_checked(start: Self, count: usize) -> Option { + u32::forward_checked(start.0, count).map(Self) + } + fn backward_checked(start: Self, count: usize) -> Option { + u32::backward_checked(start.0, count).map(Self) + } +} + impl fmt::Debug for ByteAddr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "0x{:08x}", self.0) diff --git a/ceno_emul/src/host_utils.rs b/ceno_emul/src/host_utils.rs new file mode 100644 index 000000000..460635f83 --- /dev/null +++ b/ceno_emul/src/host_utils.rs @@ -0,0 +1,32 @@ +use std::iter::from_fn; + +use crate::{ByteAddr, EmuContext, VMState, WordAddr}; + +const WORD_SIZE: usize = 4; +const INFO_OUT_ADDR: WordAddr = ByteAddr(0xC000_0000).waddr(); + +pub fn read_all_messages(state: &VMState) -> Vec { + let mut offset: WordAddr = WordAddr::from(0); + from_fn(move || match read_message(state, offset) { + out if out.is_empty() => None, + out => { + offset += out.len().div_ceil(WORD_SIZE) as u32 + 1; + Some(out) + } + }) + .collect() +} + +fn read_message(state: &VMState, offset: WordAddr) -> String { + let out_addr = INFO_OUT_ADDR + offset; + let byte_len = state.peek_memory(out_addr) as usize; + + String::from_utf8_lossy( + &(out_addr + 1_usize..) + .map(|memory| state.peek_memory(memory)) + .flat_map(u32::to_le_bytes) + .take(byte_len) + .collect::>(), + ) + .to_string() +} diff --git a/ceno_emul/src/lib.rs b/ceno_emul/src/lib.rs index 1a855006e..ba4195815 100644 --- a/ceno_emul/src/lib.rs +++ b/ceno_emul/src/lib.rs @@ -1,4 +1,5 @@ #![deny(clippy::cargo)] +#![feature(step_trait)] mod addr; pub use addr::*; @@ -20,3 +21,4 @@ mod elf; pub use elf::Program; pub mod disassemble; +pub mod host_utils; diff --git a/ceno_emul/tests/test_elf.rs b/ceno_emul/tests/test_elf.rs deleted file mode 100644 index c7a52685f..000000000 --- a/ceno_emul/tests/test_elf.rs +++ /dev/null @@ -1,119 +0,0 @@ -use std::{collections::HashSet, sync::Arc}; - -use anyhow::Result; -use ceno_emul::{ - ByteAddr, CENO_PLATFORM, EmuContext, InsnKind, Platform, Program, StepRecord, VMState, -}; - -#[test] -fn test_ceno_rt_mini() -> Result<()> { - let program_elf = ceno_examples::ceno_rt_mini; - let mut state = VMState::new_from_elf(CENO_PLATFORM, program_elf)?; - let _steps = run(&mut state)?; - Ok(()) -} - -#[test] -fn test_ceno_rt_panic() -> Result<()> { - let program_elf = ceno_examples::ceno_rt_panic; - let mut state = VMState::new_from_elf(CENO_PLATFORM, program_elf)?; - let steps = run(&mut state)?; - let last = steps.last().unwrap(); - assert_eq!(last.insn().kind, InsnKind::ECALL); - assert_eq!(last.rs1().unwrap().value, Platform::ecall_halt()); - assert_eq!(last.rs2().unwrap().value, 1); // panic / halt(1) - Ok(()) -} - -#[test] -fn test_ceno_rt_mem() -> Result<()> { - let program_elf = ceno_examples::ceno_rt_mem; - let mut state = VMState::new_from_elf(CENO_PLATFORM, program_elf)?; - let _steps = run(&mut state)?; - - let value = state.peek_memory(CENO_PLATFORM.heap.start.into()); - assert_eq!(value, 6765, "Expected Fibonacci 20, got {}", value); - Ok(()) -} - -#[test] -fn test_ceno_rt_alloc() -> Result<()> { - let program_elf = ceno_examples::ceno_rt_alloc; - let mut state = VMState::new_from_elf(CENO_PLATFORM, program_elf)?; - let _steps = run(&mut state)?; - - // Search for the RAM action of the test program. - let mut found = (false, false); - for &addr in state.tracer().final_accesses().keys() { - if !CENO_PLATFORM.is_ram(addr.into()) { - continue; - } - let value = state.peek_memory(addr); - if value == 0xf00d { - found.0 = true; - } - if value == 0xbeef { - found.1 = true; - } - } - assert!(found.0); - assert!(found.1); - Ok(()) -} - -#[test] -fn test_ceno_rt_io() -> Result<()> { - let program_elf = ceno_examples::ceno_rt_io; - let program = Program::load_elf(program_elf, u32::MAX)?; - let platform = Platform { - prog_data: Some(program.image.keys().copied().collect::>()), - ..CENO_PLATFORM - }; - let mut state = VMState::new(platform, Arc::new(program)); - let _steps = run(&mut state)?; - - let all_messages = read_all_messages(&state); - for msg in &all_messages { - print!("{}", String::from_utf8_lossy(msg)); - } - assert_eq!(&all_messages[0], "📜📜📜 Hello, World!\n".as_bytes()); - assert_eq!(&all_messages[1], "🌏🌍🌎\n".as_bytes()); - Ok(()) -} - -fn run(state: &mut VMState) -> Result> { - let steps = state.iter_until_halt().collect::>>()?; - eprintln!("Emulator ran for {} steps.", steps.len()); - Ok(steps) -} - -const WORD_SIZE: usize = 4; -const INFO_OUT_ADDR: u32 = 0xC000_0000; - -fn read_all_messages(state: &VMState) -> Vec> { - let mut all_messages = Vec::new(); - let mut word_offset = 0; - loop { - let out = read_message(state, word_offset); - if out.is_empty() { - break; - } - word_offset += out.len().div_ceil(WORD_SIZE) as u32 + 1; - all_messages.push(out); - } - all_messages -} - -fn read_message(state: &VMState, word_offset: u32) -> Vec { - let out_addr = ByteAddr(INFO_OUT_ADDR).waddr() + word_offset; - let byte_len = state.peek_memory(out_addr); - let word_len_up = byte_len.div_ceil(4); - - let mut info_out = Vec::with_capacity(WORD_SIZE * word_len_up as usize); - for i in 1..1 + word_len_up { - let value = state.peek_memory(out_addr + i); - info_out.extend_from_slice(&value.to_le_bytes()); - } - info_out.truncate(byte_len as usize); - info_out -} diff --git a/ceno_host/Cargo.toml b/ceno_host/Cargo.toml new file mode 100644 index 000000000..ad2b6e044 --- /dev/null +++ b/ceno_host/Cargo.toml @@ -0,0 +1,20 @@ +[package] +categories.workspace = true +description = "Support for the host port of a Ceno application" +edition.workspace = true +keywords.workspace = true +license.workspace = true +name = "ceno_host" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +anyhow.workspace = true +ceno_emul = { path = "../ceno_emul" } +itertools.workspace = true +rkyv = { version = "0.8.9", default-features = false, features = ["alloc", "bytecheck"] } + +[dev-dependencies] +ceno-examples = { path = "../examples-builder" } +rand.workspace = true diff --git a/ceno_host/src/lib.rs b/ceno_host/src/lib.rs new file mode 100644 index 000000000..49b58f3ee --- /dev/null +++ b/ceno_host/src/lib.rs @@ -0,0 +1,91 @@ +use std::{collections::HashSet, iter::zip, sync::Arc}; + +use anyhow::Result; +use ceno_emul::{IterAddresses, Platform, Program, VMState, host_utils::read_all_messages}; +use itertools::izip; +use rkyv::{ + Serialize, api::high::HighSerializer, rancor::Error, ser::allocator::ArenaHandle, to_bytes, + util::AlignedVec, +}; + +// We want to get access to the default value of `AlignedVec::ALIGNMENT`, and using it directly like this +// pub const RKVY_ALIGNMENT: usize = rkyv::util::AlignedVec::ALIGNMENT; +// doesn't work: +pub const RKYV_ALIGNMENT: usize = { + type AlignedVec = rkyv::util::AlignedVec; + AlignedVec::ALIGNMENT +}; + +#[derive(Default)] +pub struct CenoStdin { + pub items: Vec, +} + +impl CenoStdin { + pub fn write_slice(&mut self, bytes: AlignedVec) { + self.items.push(bytes); + } + + pub fn write( + &mut self, + item: &impl for<'a> Serialize, Error>>, + ) -> Result<(), Error> { + to_bytes::(item).map(|bytes| self.write_slice(bytes)) + } + + pub fn finalise(&self) -> Vec { + let initial_offset = (size_of::() * self.items.len()).next_multiple_of(RKYV_ALIGNMENT); + let offsets: Vec = self + .items + .iter() + .scan(initial_offset, |acc, bytes| { + let output = (*acc + bytes.len()) as u32; + *acc += bytes.len().next_multiple_of(RKYV_ALIGNMENT); + Some(output) + }) + .collect(); + let offsets_u8: Vec = offsets.iter().copied().flat_map(u32::to_le_bytes).collect(); + let mut buf: AlignedVec = AlignedVec::new(); + buf.extend_from_slice(&offsets_u8); + buf.extend_from_slice(&vec![ + 0; + buf.len().next_multiple_of(RKYV_ALIGNMENT) - buf.len() + ]); + for (offset, item) in izip!(offsets, &self.items) { + buf.extend_from_slice(item); + buf.resize(buf.len().next_multiple_of(RKYV_ALIGNMENT), 0); + assert_eq!( + buf.len(), + (offset as usize).next_multiple_of(RKYV_ALIGNMENT) + ); + } + let (prefix, hints, postfix): (_, &[u32], _) = unsafe { buf.align_to() }; + assert_eq!(prefix, &[]); + assert_eq!(postfix, &[]); + hints.to_vec() + } +} + +pub fn run(platform: Platform, elf: &[u8], hints: &CenoStdin) -> Vec { + let program = Program::load_elf(elf, u32::MAX).unwrap(); + let platform = Platform { + prog_data: Some(program.image.keys().copied().collect::>()), + ..platform + }; + + let hints: Vec = hints.finalise(); + let hints_range = platform.hints.clone(); + + let mut state = VMState::new(platform, Arc::new(program)); + + for (addr, value) in zip(hints_range.iter_addresses(), hints) { + state.init_memory(addr.into(), value); + } + + let steps = state + .iter_until_halt() + .collect::>>() + .expect("Failed to run the program"); + eprintln!("Emulator ran for {} steps.", steps.len()); + read_all_messages(&state) +} diff --git a/ceno_host/tests/test_elf.rs b/ceno_host/tests/test_elf.rs new file mode 100644 index 000000000..62118f424 --- /dev/null +++ b/ceno_host/tests/test_elf.rs @@ -0,0 +1,131 @@ +use std::{collections::HashSet, sync::Arc}; + +use anyhow::Result; +use ceno_emul::{ + CENO_PLATFORM, EmuContext, InsnKind, Platform, Program, StepRecord, VMState, + host_utils::read_all_messages, +}; +use ceno_host::CenoStdin; +use itertools::enumerate; + +#[test] +fn test_ceno_rt_mini() -> Result<()> { + let program_elf = ceno_examples::ceno_rt_mini; + let program = Program::load_elf(program_elf, u32::MAX)?; + let platform = Platform { + prog_data: Some(program.image.keys().copied().collect::>()), + ..CENO_PLATFORM + }; + let mut state = VMState::new(platform, Arc::new(program)); + let _steps = run(&mut state)?; + Ok(()) +} + +// TODO(Matthias): We are using Rust's standard library's default panic handler now, +// and they are indicated with a different instruction than our ecall. (But still work, +// as you can tell, because this tests panics.) However, we should adapt this test +// to properly check for the conventional Rust panic. +#[test] +#[should_panic(expected = "Trap IllegalInstruction")] +fn test_ceno_rt_panic() { + let program_elf = ceno_examples::ceno_rt_panic; + let program = Program::load_elf(program_elf, u32::MAX).unwrap(); + let platform = Platform { + prog_data: Some(program.image.keys().copied().collect::>()), + ..CENO_PLATFORM + }; + let mut state = VMState::new(platform, Arc::new(program)); + let steps = run(&mut state).unwrap(); + let last = steps.last().unwrap(); + assert_eq!(last.insn().kind, InsnKind::ECALL); + assert_eq!(last.rs1().unwrap().value, Platform::ecall_halt()); + assert_eq!(last.rs2().unwrap().value, 1); // panic / halt(1) +} + +#[test] +fn test_ceno_rt_mem() -> Result<()> { + let program_elf = ceno_examples::ceno_rt_mem; + let program = Program::load_elf(program_elf, u32::MAX)?; + let platform = Platform { + prog_data: Some(program.image.keys().copied().collect::>()), + ..CENO_PLATFORM + }; + let mut state = VMState::new(platform.clone(), Arc::new(program)); + let _steps = run(&mut state)?; + + let value = state.peek_memory(platform.heap.start.into()); + assert_eq!(value, 6765, "Expected Fibonacci 20, got {}", value); + Ok(()) +} + +#[test] +fn test_ceno_rt_alloc() -> Result<()> { + let program_elf = ceno_examples::ceno_rt_alloc; + let program = Program::load_elf(program_elf, u32::MAX)?; + let platform = Platform { + prog_data: Some(program.image.keys().copied().collect::>()), + ..CENO_PLATFORM + }; + let mut state = VMState::new(platform, Arc::new(program)); + let _steps = run(&mut state)?; + + // Search for the RAM action of the test program. + let mut found = (false, false); + for &addr in state.tracer().final_accesses().keys() { + if !CENO_PLATFORM.is_ram(addr.into()) { + continue; + } + let value = state.peek_memory(addr); + if value == 0xf00d { + found.0 = true; + } + if value == 0xbeef { + found.1 = true; + } + } + assert!(found.0); + assert!(found.1); + Ok(()) +} + +#[test] +fn test_ceno_rt_io() -> Result<()> { + let program_elf = ceno_examples::ceno_rt_io; + let program = Program::load_elf(program_elf, u32::MAX)?; + let platform = Platform { + prog_data: Some(program.image.keys().copied().collect::>()), + ..CENO_PLATFORM + }; + let mut state = VMState::new(platform, Arc::new(program)); + let _steps = run(&mut state)?; + + let all_messages = read_all_messages(&state); + for msg in &all_messages { + print!("{msg}"); + } + assert_eq!(&all_messages[0], "📜📜📜 Hello, World!\n"); + assert_eq!(&all_messages[1], "🌏🌍🌎\n"); + Ok(()) +} + +#[test] +fn test_hints() -> Result<()> { + let mut hints = CenoStdin::default(); + hints.write(&true)?; + hints.write(&"This is my hint string.".to_string())?; + hints.write(&1997_u32)?; + hints.write(&1999_u32)?; + + let all_messages = ceno_host::run(CENO_PLATFORM, ceno_examples::hints, &hints); + for (i, msg) in enumerate(&all_messages) { + println!("{i}: {msg}"); + } + assert_eq!(all_messages[0], "3992003"); + Ok(()) +} + +fn run(state: &mut VMState) -> Result> { + let steps = state.iter_until_halt().collect::>>()?; + eprintln!("Emulator ran for {} steps.", steps.len()); + Ok(steps) +} diff --git a/ceno_rt/Cargo.lock b/ceno_rt/Cargo.lock new file mode 100644 index 000000000..8157a3858 --- /dev/null +++ b/ceno_rt/Cargo.lock @@ -0,0 +1,180 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bytecheck" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c8f430744b23b54ad15161fcbc22d82a29b73eacbe425fea23ec822600bc6f" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "rancor", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523363cbe1df49b68215efdf500b103ac3b0fb4836aed6d15689a076eadb8fff" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ceno_rt" +version = "0.1.0" +dependencies = [ + "rkyv", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "munge" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64142d38c84badf60abf06ff9bd80ad2174306a5b11bd4706535090a30a419df" +dependencies = [ + "munge_macro", +] + +[[package]] +name = "munge_macro" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "ptr_meta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rancor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947" +dependencies = [ + "ptr_meta", +] + +[[package]] +name = "rend" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35e8a6bf28cd121053a66aa2e6a2e3eaffad4a60012179f0e864aa5ffeff215" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rkyv" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11a153aec4a6ab60795f8ebe2923c597b16b05bb1504377451e705ef1a45323" +dependencies = [ + "bytecheck", + "hashbrown", + "munge", + "ptr_meta", + "rancor", + "rend", + "rkyv_derive", + "tinyvec", +] + +[[package]] +name = "rkyv_derive" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beb382a4d9f53bd5c0be86b10d8179c3f8a14c30bf774ff77096ed6581e35981" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" diff --git a/guest/Cargo.toml b/ceno_rt/Cargo.toml similarity index 54% rename from guest/Cargo.toml rename to ceno_rt/Cargo.toml index 0aa06be48..d9a85ab12 100644 --- a/guest/Cargo.toml +++ b/ceno_rt/Cargo.toml @@ -1,15 +1,16 @@ -[workspace] -members = ["examples", "ceno_rt"] -resolver = "2" - -[workspace.package] +[package] categories = ["cryptography", "zk", "blockchain", "ceno"] +description = "Ceno runtime library" edition = "2021" keywords = ["cryptography", "zk", "blockchain", "ceno"] license = "MIT OR Apache-2.0" -readme = "../README.md" +name = "ceno_rt" +readme = "README.md" repository = "https://github.com/scroll-tech/ceno" version = "0.1.0" -[profile.release] -lto = "thin" +[dependencies] +rkyv = { version = "0.8", default-features = false, features = [ + "alloc", + "bytecheck", +] } diff --git a/guest/ceno_rt/README.md b/ceno_rt/README.md similarity index 100% rename from guest/ceno_rt/README.md rename to ceno_rt/README.md diff --git a/guest/ceno_rt/build.rs b/ceno_rt/build.rs similarity index 100% rename from guest/ceno_rt/build.rs rename to ceno_rt/build.rs diff --git a/guest/ceno_rt/ceno_link.x b/ceno_rt/ceno_link.x similarity index 71% rename from guest/ceno_rt/ceno_link.x rename to ceno_rt/ceno_link.x index 00b2ea282..4c2e9e257 100644 --- a/guest/ceno_rt/ceno_link.x +++ b/ceno_rt/ceno_link.x @@ -1,5 +1,7 @@ _stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK); +_hints_start = ORIGIN(REGION_HINTS); +_hints_end = ORIGIN(REGION_HINTS) + LENGTH(REGION_HINTS); SECTIONS { @@ -34,4 +36,10 @@ SECTIONS . = ALIGN(4); _sheap = .; } > RAM + + /* Define a section for runtime-populated EEPROM-like HINTS data */ + .hints (NOLOAD) : ALIGN(4) + { + *(.hints .hints.*); + } > HINTS } diff --git a/guest/ceno_rt/memory.x b/ceno_rt/memory.x similarity index 78% rename from guest/ceno_rt/memory.x rename to ceno_rt/memory.x index 712de56cd..0baf12fcd 100644 --- a/guest/ceno_rt/memory.x +++ b/ceno_rt/memory.x @@ -2,6 +2,7 @@ MEMORY { RAM : ORIGIN = 0x80000000, LENGTH = 1024M ROM : ORIGIN = 0x20000000, LENGTH = 16M + HINTS: ORIGIN = 0x40000000, LENGTH = 1024M } REGION_ALIAS("REGION_TEXT", ROM); @@ -10,3 +11,5 @@ REGION_ALIAS("REGION_DATA", RAM); REGION_ALIAS("REGION_BSS", RAM); REGION_ALIAS("REGION_HEAP", RAM); REGION_ALIAS("REGION_STACK", RAM); + +REGION_ALIAS("REGION_HINTS", HINTS); diff --git a/guest/ceno_rt/src/allocator.rs b/ceno_rt/src/allocator.rs similarity index 55% rename from guest/ceno_rt/src/allocator.rs rename to ceno_rt/src/allocator.rs index c5202f1de..91c697b7f 100644 --- a/guest/ceno_rt/src/allocator.rs +++ b/ceno_rt/src/allocator.rs @@ -4,7 +4,7 @@ use core::alloc::{GlobalAlloc, Layout}; struct SimpleAllocator { - next_alloc: usize, + next_alloc: *mut u8, } unsafe impl GlobalAlloc for SimpleAllocator { @@ -14,16 +14,14 @@ unsafe impl GlobalAlloc for SimpleAllocator { let align = layout.align(); // `Layout` contract forbids making a `Layout` with align=0, or align not power of 2. - // So we can safely use subtraction and a mask to ensure alignment without worrying about UB. - let offset = heap_pos & (align - 1); - if offset != 0 { - heap_pos = heap_pos.strict_add(align.strict_sub(offset)); - } - - let ptr = heap_pos as *mut u8; - // Panic on overflow. We don't want to wrap around, and overwrite stack etc. + core::hint::assert_unchecked(align.is_power_of_two()); + core::hint::assert_unchecked(align != 0); + heap_pos = heap_pos.add(heap_pos.align_offset(align)); + + let ptr = heap_pos; + // We don't want to wrap around, and overwrite stack etc. // (We could also return a null pointer, but only malicious programs would ever hit this.) - heap_pos = heap_pos.strict_add(layout.size()); + heap_pos = heap_pos.add(layout.size()); HEAP.next_alloc = heap_pos; ptr @@ -37,13 +35,14 @@ unsafe impl GlobalAlloc for SimpleAllocator { unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} } +extern "C" { + /// The address of this variable is the start of the heap (growing upwards). + /// + /// It is defined in the linker script. + static mut _sheap: u8; +} + #[global_allocator] -// We initialize `next_alloc` to 0xFFFF_FFFF to indicate that the heap has not been initialized. -// The value is chosen to make any premature allocation fail. static mut HEAP: SimpleAllocator = SimpleAllocator { - next_alloc: 0xFFFF_FFFF, + next_alloc: &raw mut _sheap, }; - -pub unsafe fn init_heap() { - HEAP.next_alloc = core::ptr::from_ref::(&crate::_sheap).cast::() as usize; -} diff --git a/guest/ceno_rt/src/io.rs b/ceno_rt/src/io.rs similarity index 100% rename from guest/ceno_rt/src/io.rs rename to ceno_rt/src/io.rs diff --git a/ceno_rt/src/lib.rs b/ceno_rt/src/lib.rs new file mode 100644 index 000000000..b8a4ae779 --- /dev/null +++ b/ceno_rt/src/lib.rs @@ -0,0 +1,74 @@ +#![deny(clippy::cargo)] +#![feature(strict_overflow_ops)] +#![feature(linkage)] + +#[cfg(target_arch = "riscv32")] +use core::arch::{asm, global_asm}; + +mod allocator; + +mod mmio; +pub use mmio::{read, read_slice}; + +mod io; +pub use io::info_out; + +mod params; +pub use params::*; + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn sys_write(_fd: i32, _buf: *const u8, _count: usize) -> isize { + unimplemented!(); +} + +pub fn halt(exit_code: u32) -> ! { + #[cfg(target_arch = "riscv32")] + unsafe { + asm!( + "ecall", + in ("a0") exit_code, + in ("t0") 0, + ); + unreachable!(); + } + #[cfg(not(target_arch = "riscv32"))] + unimplemented!("Halt is not implemented for this target: {}", exit_code); +} + +#[cfg(target_arch = "riscv32")] +global_asm!( + " +// The entry point for the program. +.section .init +.global _start +_start: + + // Set the global pointer somewhere towards the start of RAM. + .option push + .option norelax + la gp, __global_pointer$ + .option pop + + // Set the stack pointer and frame pointer to the top of the stack. + la sp, _stack_start + mv fp, sp + + // Call the Rust start function. + // jal zero, _start_rust + call main + + // If we return from main, we halt with success: + + // Set the ecall code HALT. + li t0, 0 + // Set successful exit code, ie 0: + li a0, 0 + ecall + ", +); + +extern "C" { + // The address of this variable is the start of the stack (growing downwards). + static _stack_start: u8; +} diff --git a/ceno_rt/src/mmio.rs b/ceno_rt/src/mmio.rs new file mode 100644 index 000000000..1f7bddc44 --- /dev/null +++ b/ceno_rt/src/mmio.rs @@ -0,0 +1,36 @@ +//! Memory-mapped I/O (MMIO) functions. + +use rkyv::{Portable, api::high::HighValidator, bytecheck::CheckBytes, rancor::Failure}; + +use core::slice::from_raw_parts; + +extern "C" { + /// The address of this variable is the start of the hints ROM. + /// + /// It is defined in the linker script. + static _hints_start: u8; + static _hints_end: u8; +} + +static mut NEXT_HINT_LEN_AT: *const u8 = &raw const _hints_start; + +pub fn read_slice<'a>() -> &'a [u8] { + unsafe { + let len: u32 = core::ptr::read(NEXT_HINT_LEN_AT as *const u32); + NEXT_HINT_LEN_AT = NEXT_HINT_LEN_AT.add(4); + + let hints_region = { + let total_length = (&raw const _hints_end).offset_from(&_hints_start) as usize; + from_raw_parts(&_hints_start, total_length) + }; + + &hints_region[..len as usize] + } +} + +pub fn read<'a, T>() -> &'a T +where + T: Portable + for<'c> CheckBytes>, +{ + rkyv::access::(read_slice()).expect("Deserialised access failed.") +} diff --git a/guest/ceno_rt/src/params.rs b/ceno_rt/src/params.rs similarity index 100% rename from guest/ceno_rt/src/params.rs rename to ceno_rt/src/params.rs diff --git a/ceno_zkvm/plan.markdown b/ceno_zkvm/plan.markdown new file mode 100644 index 000000000..21f045ac9 --- /dev/null +++ b/ceno_zkvm/plan.markdown @@ -0,0 +1,3 @@ +- [ ] Change standalone guest to use a path dependency for now. +- [ ] Move stuff into cento_rt, and perhaps ceno_host. +- [ ] Oputional: Make ceno-rt buildable on native? Later. diff --git a/examples-builder/build.rs b/examples-builder/build.rs index 0e8d90441..7d01eb3ad 100644 --- a/examples-builder/build.rs +++ b/examples-builder/build.rs @@ -14,6 +14,7 @@ const EXAMPLES: &[&str] = &[ "ceno_rt_mem", "ceno_rt_mini", "ceno_rt_panic", + "hints", ]; const CARGO_MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR"); @@ -26,7 +27,7 @@ fn build_elfs() { // See git history for an attempt to do this. let output = Command::new("cargo") .args(["build", "--release", "--examples"]) - .current_dir("../guest/examples") + .current_dir("../examples") .env_clear() .envs(std::env::vars().filter(|x| !x.0.starts_with("CARGO_"))) .output() @@ -41,13 +42,13 @@ fn build_elfs() { dest, r#"#[allow(non_upper_case_globals)] pub const {example}: &[u8] = - include_bytes!(r"{CARGO_MANIFEST_DIR}/../guest/target/riscv32im-unknown-none-elf/release/examples/{example}");"# + include_bytes!(r"{CARGO_MANIFEST_DIR}/../examples/target/riscv32im-ceno-zkvm-elf/release/examples/{example}");"# ).expect("failed to write vars.rs"); } - let input_path = "../guest/"; - let elfs_path = "../guest/target/riscv32im-unknown-none-elf/release/examples/"; - println!("cargo:rerun-if-changed={input_path}"); + println!("cargo:rerun-if-changed=../examples/"); + println!("cargo:rerun-if-changed=../ceno_rt/"); + let elfs_path = "../examples/target/riscv32im-ceno-zkvm-elf/release/examples/"; println!("cargo:rerun-if-changed={elfs_path}"); } diff --git a/examples/.cargo/config.toml b/examples/.cargo/config.toml new file mode 100644 index 000000000..0b0339c7f --- /dev/null +++ b/examples/.cargo/config.toml @@ -0,0 +1,29 @@ +[unstable] +build-std = [ + "alloc", + "core", + "compiler_builtins", + "std", + "panic_abort", + "proc_macro", +] +build-std-features = [ + "compiler-builtins-mem", + "panic_immediate_abort", + "default", +] + +[profile.dev] +panic = "abort" + +[build] +rustflags = [ + "-C", + "link-arg=-Tmemory.x", + "-C", + "link-arg=-Tceno_link.x", + "-Zlocation-detail=none", + "-C", + "passes=lower-atomic", +] +target = ".cargo/riscv32im-ceno-zkvm-elf.json" diff --git a/examples/.cargo/riscv32im-ceno-zkvm-elf.json b/examples/.cargo/riscv32im-ceno-zkvm-elf.json new file mode 100644 index 000000000..be67a8bae --- /dev/null +++ b/examples/.cargo/riscv32im-ceno-zkvm-elf.json @@ -0,0 +1,31 @@ +{ + "arch": "riscv32", + "atomic-cas": true, + "cpu": "generic-rv32", + "crt-objects-fallback": "false", + "data-layout": "e-m:e-p:32:32-i64:64-n32-S128", + "eh-frame-header": false, + "emit-debug-gdb-scripts": false, + "env": "", + "executables": true, + "features": "+m,+forced-atomics", + "linker-flavor": "gnu-lld", + "linker": "rust-lld", + "llvm-abiname": "ilp32", + "llvm-target": "riscv32", + "main-needs-argc-argv": false, + "metadata": { + "description": null, + "host_tools": false, + "std": true, + "tier": 2 + }, + "max-atomic-width": 64, + "os": "zkvm", + "panic-strategy": "abort", + "relocation-model": "static", + "singlethread": true, + "target-c-int-width": "32", + "target-endian": "little", + "target-pointer-width": "32" +} diff --git a/examples/Cargo.lock b/examples/Cargo.lock new file mode 100644 index 000000000..c0c2418cc --- /dev/null +++ b/examples/Cargo.lock @@ -0,0 +1,188 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bytecheck" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c8f430744b23b54ad15161fcbc22d82a29b73eacbe425fea23ec822600bc6f" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "rancor", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523363cbe1df49b68215efdf500b103ac3b0fb4836aed6d15689a076eadb8fff" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ceno_rt" +version = "0.1.0" +dependencies = [ + "rkyv", +] + +[[package]] +name = "examples" +version = "0.1.0" +dependencies = [ + "ceno_rt", + "rkyv", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "munge" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64142d38c84badf60abf06ff9bd80ad2174306a5b11bd4706535090a30a419df" +dependencies = [ + "munge_macro", +] + +[[package]] +name = "munge_macro" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "ptr_meta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rancor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947" +dependencies = [ + "ptr_meta", +] + +[[package]] +name = "rend" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35e8a6bf28cd121053a66aa2e6a2e3eaffad4a60012179f0e864aa5ffeff215" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rkyv" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11a153aec4a6ab60795f8ebe2923c597b16b05bb1504377451e705ef1a45323" +dependencies = [ + "bytecheck", + "hashbrown", + "munge", + "ptr_meta", + "rancor", + "rend", + "rkyv_derive", + "tinyvec", +] + +[[package]] +name = "rkyv_derive" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beb382a4d9f53bd5c0be86b10d8179c3f8a14c30bf774ff77096ed6581e35981" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" diff --git a/examples/Cargo.toml b/examples/Cargo.toml new file mode 100644 index 000000000..6be7ab795 --- /dev/null +++ b/examples/Cargo.toml @@ -0,0 +1,17 @@ +[package] +categories = ["cryptography", "zk", "blockchain", "ceno"] +description = "Ceno RiscV guest examples" +edition = "2021" +keywords = ["cryptography", "zk", "blockchain", "ceno"] +license = "MIT OR Apache-2.0" +name = "examples" +readme = "README.md" +repository = "https://github.com/scroll-tech/ceno" +version = "0.1.0" + +[dependencies] +ceno_rt = { path = "../ceno_rt" } +rkyv = { version = "0.8", default-features = false, features = [ + "alloc", + "bytecheck", +] } diff --git a/guest/examples/examples/ceno_rt_alloc.rs b/examples/examples/ceno_rt_alloc.rs similarity index 93% rename from guest/examples/examples/ceno_rt_alloc.rs rename to examples/examples/ceno_rt_alloc.rs index 3e169d9b3..71efbeb28 100644 --- a/guest/examples/examples/ceno_rt_alloc.rs +++ b/examples/examples/ceno_rt_alloc.rs @@ -1,5 +1,3 @@ -#![no_main] -#![no_std] use core::ptr::{addr_of, read_volatile}; extern crate ceno_rt; @@ -9,7 +7,6 @@ use alloc::{vec, vec::Vec}; static mut OUTPUT: u32 = 0; -ceno_rt::entry!(main); fn main() { // Test writing to a global variable. unsafe { diff --git a/guest/examples/examples/ceno_rt_io.rs b/examples/examples/ceno_rt_io.rs similarity index 76% rename from guest/examples/examples/ceno_rt_io.rs rename to examples/examples/ceno_rt_io.rs index 8d2afa6aa..76988c9a2 100644 --- a/guest/examples/examples/ceno_rt_io.rs +++ b/examples/examples/ceno_rt_io.rs @@ -1,11 +1,7 @@ -#![no_main] -#![no_std] - extern crate ceno_rt; use ceno_rt::println; use core::fmt::Write; -ceno_rt::entry!(main); fn main() { println!("📜📜📜 Hello, World!"); println!("🌏🌍🌎"); diff --git a/guest/examples/examples/ceno_rt_mem.rs b/examples/examples/ceno_rt_mem.rs similarity index 96% rename from guest/examples/examples/ceno_rt_mem.rs rename to examples/examples/ceno_rt_mem.rs index 2cc484f80..5adbcb130 100644 --- a/guest/examples/examples/ceno_rt_mem.rs +++ b/examples/examples/ceno_rt_mem.rs @@ -1,13 +1,9 @@ -#![no_main] -#![no_std] - // Use volatile functions to prevent compiler optimizations. use core::ptr::{read_volatile, write_volatile}; extern crate ceno_rt; const OUTPUT_ADDRESS: u32 = 0x8000_0000; -ceno_rt::entry!(main); #[inline(never)] fn main() { test_data_section(); diff --git a/examples/examples/ceno_rt_mini.rs b/examples/examples/ceno_rt_mini.rs new file mode 100644 index 000000000..505d649ba --- /dev/null +++ b/examples/examples/ceno_rt_mini.rs @@ -0,0 +1,3 @@ +extern crate ceno_rt; + +fn main() {} diff --git a/guest/examples/examples/ceno_rt_panic.rs b/examples/examples/ceno_rt_panic.rs similarity index 62% rename from guest/examples/examples/ceno_rt_panic.rs rename to examples/examples/ceno_rt_panic.rs index dc8c2fa17..10eda5842 100644 --- a/guest/examples/examples/ceno_rt_panic.rs +++ b/examples/examples/ceno_rt_panic.rs @@ -1,9 +1,5 @@ -#![no_main] -#![no_std] - extern crate ceno_rt; -ceno_rt::entry!(main); fn main() { panic!("This is a panic message!"); } diff --git a/examples/examples/hints.rs b/examples/examples/hints.rs new file mode 100644 index 000000000..b591758d6 --- /dev/null +++ b/examples/examples/hints.rs @@ -0,0 +1,18 @@ +extern crate ceno_rt; +use ceno_rt::println; +use core::fmt::Write; +use rkyv::{Archived, string::ArchivedString}; + +fn main() { + let condition: &bool = ceno_rt::read(); + assert!(*condition); + let msg: &ArchivedString = ceno_rt::read(); + + let a: &Archived = ceno_rt::read(); + let b: &Archived = ceno_rt::read(); + let product: u32 = a * b; + + assert_eq!(product, 3992003); + println!("{product}"); + println!("This message is a hint: {msg}"); +} diff --git a/guest/.cargo/config.toml b/guest/.cargo/config.toml deleted file mode 100644 index 9cea5ae7c..000000000 --- a/guest/.cargo/config.toml +++ /dev/null @@ -1,15 +0,0 @@ -[target.riscv32im-unknown-none-elf] -rustflags = [ - "-C", - "link-arg=-Tmemory.x", - #"-C", "link-arg=-Tlink.x", // Script from riscv_rt. - "-C", - "link-arg=-Tceno_link.x", -] - -[build] -target = "riscv32im-unknown-none-elf" - -[profile.release] -lto = true -panic = "abort" diff --git a/guest/Cargo.lock b/guest/Cargo.lock deleted file mode 100644 index 84c6c3ab6..000000000 --- a/guest/Cargo.lock +++ /dev/null @@ -1,100 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "ceno_rt" -version = "0.1.0" -dependencies = [ - "riscv", -] - -[[package]] -name = "critical-section" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" - -[[package]] -name = "embedded-hal" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" - -[[package]] -name = "examples" -version = "0.1.0" -dependencies = [ - "ceno_rt", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "proc-macro2" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "riscv" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea8ff73d3720bdd0a97925f0bf79ad2744b6da8ff36be3840c48ac81191d7a7" -dependencies = [ - "critical-section", - "embedded-hal", - "paste", - "riscv-macros", - "riscv-pac", -] - -[[package]] -name = "riscv-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f265be5d634272320a7de94cea15c22a3bfdd4eb42eb43edc528415f066a1f25" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "riscv-pac" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" - -[[package]] -name = "syn" -version = "2.0.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" diff --git a/guest/ceno_rt/Cargo.toml b/guest/ceno_rt/Cargo.toml deleted file mode 100644 index dfdc87ad2..000000000 --- a/guest/ceno_rt/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -categories.workspace = true -description = "Ceno runtime library" -edition.workspace = true -keywords.workspace = true -license.workspace = true -name = "ceno_rt" -readme = "README.md" -repository.workspace = true -version.workspace = true - -[dependencies] -riscv = "0.12" diff --git a/guest/ceno_rt/src/lib.rs b/guest/ceno_rt/src/lib.rs deleted file mode 100644 index 8de456c41..000000000 --- a/guest/ceno_rt/src/lib.rs +++ /dev/null @@ -1,97 +0,0 @@ -#![deny(clippy::cargo)] -#![feature(strict_overflow_ops)] -#![no_std] - -use core::arch::{asm, global_asm}; - -mod allocator; - -mod io; -pub use io::info_out; - -mod params; -pub use params::*; - -#[cfg(not(test))] -mod panic_handler { - use core::panic::PanicInfo; - - #[panic_handler] - #[inline(never)] - fn panic_handler(_panic: &PanicInfo<'_>) -> ! { - super::halt(1) - } -} - -#[allow(asm_sub_register)] -pub fn halt(exit_code: u32) -> ! { - unsafe { - asm!( - // Set the first argument. - "mv a0, {}", - // Set the ecall code HALT. - "li t0, 0x0", - in(reg) exit_code, - ); - riscv::asm::ecall(); - } - #[allow(clippy::empty_loop)] - loop {} -} - -global_asm!( - " -// The entry point for the program. -.section .init -.global _start -_start: - - // Set the global pointer somewhere towards the start of RAM. - .option push - .option norelax - la gp, __global_pointer$ - .option pop - - // Set the stack pointer and frame pointer to the top of the stack. - la sp, _stack_start - mv fp, sp - - // Call the Rust start function. - jal zero, _start_rust - ", -); - -#[macro_export] -macro_rules! entry { - ($path:path) => { - // Type check the given path - const CENO_ENTRY: fn() = $path; - - mod ceno_generated_main { - #[no_mangle] - extern "C" fn bespoke_entrypoint() { - super::CENO_ENTRY(); - } - } - }; -} - -/// _start_rust is called by the assembly entry point and it calls the Rust main(). -#[no_mangle] -unsafe extern "C" fn _start_rust() -> ! { - allocator::init_heap(); - { - extern "C" { - fn bespoke_entrypoint(); - } - bespoke_entrypoint(); - } - halt(0) -} - -extern "C" { - // The address of this variable is the start of the stack (growing downwards). - static _stack_start: u8; - // The address of this variable is the start of the heap (growing upwards). - static _sheap: u8; -} diff --git a/guest/examples/Cargo.toml b/guest/examples/Cargo.toml deleted file mode 100644 index 397e743b1..000000000 --- a/guest/examples/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -categories.workspace = true -description = "Ceno RiscV guest examples" -edition.workspace = true -keywords.workspace = true -license.workspace = true -name = "examples" -readme = "README.md" -repository.workspace = true -version.workspace = true - -[dependencies] -ceno_rt = { path = "../ceno_rt" } diff --git a/guest/examples/examples/ceno_rt_mini.rs b/guest/examples/examples/ceno_rt_mini.rs deleted file mode 100644 index 70ec64153..000000000 --- a/guest/examples/examples/ceno_rt_mini.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] -#![no_std] - -extern crate ceno_rt; - -ceno_rt::entry!(main); -fn main() {} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index eb12bd38f..0d9c05484 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,3 @@ [toolchain] channel = "nightly-2024-12-06" +components = ["rust-src"]