From ca6adeca1a41937347bcd1bc8d12e2f831e4cee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Thu, 29 Aug 2024 17:57:54 +0200 Subject: [PATCH 01/14] runtime: minimal compiled program --- Cargo.lock | 29 +++++++++++++++++++++++++++++ Cargo.toml | 1 + ceno_rt/.cargo/config.toml | 13 +++++++++++++ ceno_rt/Cargo.toml | 9 +++++++++ ceno_rt/README.md | 12 ++++++++++++ ceno_rt/build.rs | 13 +++++++++++++ ceno_rt/ceno_link.x | 8 ++++++++ ceno_rt/memory.x | 12 ++++++++++++ ceno_rt/src/main.rs | 19 +++++++++++++++++++ 9 files changed, 116 insertions(+) create mode 100644 ceno_rt/.cargo/config.toml create mode 100644 ceno_rt/Cargo.toml create mode 100644 ceno_rt/README.md create mode 100644 ceno_rt/build.rs create mode 100644 ceno_rt/ceno_link.x create mode 100644 ceno_rt/memory.x create mode 100644 ceno_rt/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index f83359d02..f897f592d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -269,6 +269,13 @@ dependencies = [ "tracing", ] +[[package]] +name = "ceno_rt" +version = "0.1.0" +dependencies = [ + "riscv", +] + [[package]] name = "ceno_zkvm" version = "0.1.0" @@ -473,6 +480,12 @@ dependencies = [ "itertools 0.10.5", ] +[[package]] +name = "critical-section" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f64009896348fc5af4222e9cf7d7d82a95a256c634ebcf61c53e4ea461422242" + [[package]] name = "crossbeam-channel" version = "0.5.13" @@ -568,6 +581,12 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + [[package]] name = "env_logger" version = "0.7.1" @@ -1539,6 +1558,16 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "riscv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9" +dependencies = [ + "critical-section", + "embedded-hal", +] + [[package]] name = "rustc-demangle" version = "0.1.23" diff --git a/Cargo.toml b/Cargo.toml index f8c1eb2bd..f55d1638b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "ceno_emul", + "ceno_rt", "gkr", "gkr-graph", "mpcs", diff --git a/ceno_rt/.cargo/config.toml b/ceno_rt/.cargo/config.toml new file mode 100644 index 000000000..d2a0dba12 --- /dev/null +++ b/ceno_rt/.cargo/config.toml @@ -0,0 +1,13 @@ +[target.riscv32im-unknown-none-elf] +rustflags = [ + "-C", "link-arg=-Tmemory.x", + #"-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tceno_link.x", +] + +[build] +target = "riscv32im-unknown-none-elf" + +[profile.release] +panic = "abort" +lto = true diff --git a/ceno_rt/Cargo.toml b/ceno_rt/Cargo.toml new file mode 100644 index 000000000..8c14cb02f --- /dev/null +++ b/ceno_rt/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ceno_rt" +version.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] +riscv = "0.11.1" +#riscv-rt = "0.12.2" diff --git a/ceno_rt/README.md b/ceno_rt/README.md new file mode 100644 index 000000000..a88723a4f --- /dev/null +++ b/ceno_rt/README.md @@ -0,0 +1,12 @@ + +```bash +rustup target add riscv32im-unknown-none-elf + +cargo build --release +``` + + +```bash +cargo install cargo-binutils +rustup component add llvm-tools +``` diff --git a/ceno_rt/build.rs b/ceno_rt/build.rs new file mode 100644 index 000000000..04f7d247e --- /dev/null +++ b/ceno_rt/build.rs @@ -0,0 +1,13 @@ +use std::{env, fs, path::PathBuf}; + +fn main() { + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + + // Put the linker script somewhere the linker can find it. + fs::write(out_dir.join("memory.x"), include_bytes!("memory.x")).unwrap(); + fs::write(out_dir.join("ceno_link.x"), include_bytes!("ceno_link.x")).unwrap(); + println!("cargo:rustc-link-search={}", out_dir.display()); + println!("cargo:rerun-if-changed=memory.x"); + println!("cargo:rerun-if-changed=ceno_link.x"); + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/ceno_rt/ceno_link.x b/ceno_rt/ceno_link.x new file mode 100644 index 000000000..c8a16cf19 --- /dev/null +++ b/ceno_rt/ceno_link.x @@ -0,0 +1,8 @@ +SECTIONS +{ + .text : + { + *(.text._start); + *(.text .text.*); + } > FLASH +} \ No newline at end of file diff --git a/ceno_rt/memory.x b/ceno_rt/memory.x new file mode 100644 index 000000000..9b971fe8e --- /dev/null +++ b/ceno_rt/memory.x @@ -0,0 +1,12 @@ +MEMORY +{ + RAM : ORIGIN = 0x80000000, LENGTH = 16M + FLASH : ORIGIN = 0x20000000, LENGTH = 16M +} + +REGION_ALIAS("REGION_TEXT", FLASH); +REGION_ALIAS("REGION_RODATA", FLASH); +REGION_ALIAS("REGION_DATA", RAM); +REGION_ALIAS("REGION_BSS", RAM); +REGION_ALIAS("REGION_HEAP", RAM); +REGION_ALIAS("REGION_STACK", RAM); diff --git a/ceno_rt/src/main.rs b/ceno_rt/src/main.rs new file mode 100644 index 000000000..0eadda7e9 --- /dev/null +++ b/ceno_rt/src/main.rs @@ -0,0 +1,19 @@ +#![no_main] +#![no_std] + +use core::panic::PanicInfo; +use riscv::asm; + +#[panic_handler] +#[inline(never)] +fn panic(_panic: &PanicInfo<'_>) -> ! { + loop {} +} + +#[no_mangle] +pub extern "C" fn _start() -> ! { + unsafe { + asm::ecall(); + } + unreachable!(); +} From ef357f5763bd400f7dc2f3e099b46be036d16c25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Thu, 29 Aug 2024 18:20:59 +0200 Subject: [PATCH 02/14] runtime: ecall halt --- ceno_rt/src/main.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/ceno_rt/src/main.rs b/ceno_rt/src/main.rs index 0eadda7e9..7ba064043 100644 --- a/ceno_rt/src/main.rs +++ b/ceno_rt/src/main.rs @@ -1,8 +1,6 @@ #![no_main] #![no_std] - -use core::panic::PanicInfo; -use riscv::asm; +use core::{arch::asm, panic::PanicInfo}; #[panic_handler] #[inline(never)] @@ -10,10 +8,16 @@ fn panic(_panic: &PanicInfo<'_>) -> ! { loop {} } -#[no_mangle] -pub extern "C" fn _start() -> ! { +fn halt(exit_code: u32) -> ! { unsafe { - asm::ecall(); + asm!("mv a0, {}", in(reg) exit_code); + asm!("li t0, 0x0"); + riscv::asm::ecall(); } unreachable!(); } + +#[no_mangle] +pub extern "C" fn _start() -> ! { + halt(0) +} From 5390280f4e68809c6baefa56de9f332cad1f7d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 30 Aug 2024 16:51:45 +0200 Subject: [PATCH 03/14] runtime: move to examples/ --- ceno_rt/README.md | 15 ++++++++++++++- ceno_rt/{src/main.rs => examples/ceno_rt_mini.rs} | 13 ++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) rename ceno_rt/{src/main.rs => examples/ceno_rt_mini.rs} (60%) diff --git a/ceno_rt/README.md b/ceno_rt/README.md index a88723a4f..72abeb763 100644 --- a/ceno_rt/README.md +++ b/ceno_rt/README.md @@ -1,12 +1,25 @@ +# Ceno VM Runtime + +This crate provides the runtime for program running on the Ceno VM. It provides: + +- Configuration of compilation and linking. +- Program startup and termination. +- Memory setup. + +### Build examples ```bash rustup target add riscv32im-unknown-none-elf -cargo build --release +cargo build --release --examples ``` +### Development tools ```bash cargo install cargo-binutils rustup component add llvm-tools + +# Look at the disassembly of a compiled program. +cargo objdump --release --example ceno_rt_mini -- --all-headers --disassemble ``` diff --git a/ceno_rt/src/main.rs b/ceno_rt/examples/ceno_rt_mini.rs similarity index 60% rename from ceno_rt/src/main.rs rename to ceno_rt/examples/ceno_rt_mini.rs index 7ba064043..520a28aaf 100644 --- a/ceno_rt/src/main.rs +++ b/ceno_rt/examples/ceno_rt_mini.rs @@ -5,19 +5,22 @@ use core::{arch::asm, panic::PanicInfo}; #[panic_handler] #[inline(never)] fn panic(_panic: &PanicInfo<'_>) -> ! { - loop {} + halt(1) } fn halt(exit_code: u32) -> ! { unsafe { - asm!("mv a0, {}", in(reg) exit_code); - asm!("li t0, 0x0"); + asm!( + "mv a0, {}", + "li t0, 0x0", + in(reg) exit_code, + ); riscv::asm::ecall(); } - unreachable!(); + unreachable!() } #[no_mangle] -pub extern "C" fn _start() -> ! { +pub fn _start() -> ! { halt(0) } From 1d7f95d860877975c72cddb1bfb52d9f35f5a2b7 Mon Sep 17 00:00:00 2001 From: naure Date: Sat, 31 Aug 2024 16:49:18 +0200 Subject: [PATCH 04/14] emul: Load and test an ELF (#178) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * emul-elf: introduce elf crate and risc0 load_elf() * emul-elf: load and execute a test program * emul-elf: use examples/ folder * runtime: example program using memory and stack --------- Co-authored-by: AurΓ©lien Nicolas --- Cargo.lock | 29 ++++++++ ceno_emul/Cargo.toml | 1 + ceno_emul/src/elf.rs | 112 ++++++++++++++++++++++++++++++ ceno_emul/src/lib.rs | 3 + ceno_emul/src/loader.rs | 0 ceno_emul/src/vm_state.rs | 4 +- ceno_emul/tests/data/README.md | 7 ++ ceno_emul/tests/data/ceno_rt_mem | Bin 0 -> 10136 bytes ceno_emul/tests/data/ceno_rt_mini | Bin 0 -> 5976 bytes ceno_emul/tests/test_elf.rs | 54 ++++++++++++++ ceno_rt/Cargo.toml | 2 +- ceno_rt/ceno_link.x | 21 +++++- ceno_rt/examples/ceno_rt_mem.rs | 46 ++++++++++++ ceno_rt/memory.x | 6 +- 14 files changed, 278 insertions(+), 7 deletions(-) create mode 100644 ceno_emul/src/elf.rs create mode 100644 ceno_emul/src/loader.rs create mode 100644 ceno_emul/tests/data/README.md create mode 100755 ceno_emul/tests/data/ceno_rt_mem create mode 100755 ceno_emul/tests/data/ceno_rt_mini create mode 100644 ceno_emul/tests/test_elf.rs create mode 100644 ceno_rt/examples/ceno_rt_mem.rs diff --git a/Cargo.lock b/Cargo.lock index f897f592d..b715cbdc8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,6 +266,7 @@ name = "ceno_emul" version = "0.1.0" dependencies = [ "anyhow", + "elf", "tracing", ] @@ -274,6 +275,7 @@ name = "ceno_rt" version = "0.1.0" dependencies = [ "riscv", + "riscv-rt", ] [[package]] @@ -581,6 +583,12 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +[[package]] +name = "elf" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" + [[package]] name = "embedded-hal" version = "1.0.0" @@ -1568,6 +1576,27 @@ dependencies = [ "embedded-hal", ] +[[package]] +name = "riscv-rt" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d35e32cf1383183e8885d8a9aa4402a087fd094dc34c2cb6df6687d0229dfe" +dependencies = [ + "riscv", + "riscv-rt-macros", +] + +[[package]] +name = "riscv-rt-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d100d466dbb76681ef6a9386f3da9abc570d57394e86da0ba5af8c4408486d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rustc-demangle" version = "0.1.23" diff --git a/ceno_emul/Cargo.toml b/ceno_emul/Cargo.toml index 5a80047a6..ed1096822 100644 --- a/ceno_emul/Cargo.toml +++ b/ceno_emul/Cargo.toml @@ -9,3 +9,4 @@ anyhow = { version = "1.0", default-features = false } tracing = { version = "0.1", default-features = false, features = [ "attributes", ] } +elf = { version = "0.7.4" } diff --git a/ceno_emul/src/elf.rs b/ceno_emul/src/elf.rs new file mode 100644 index 000000000..2842d6b33 --- /dev/null +++ b/ceno_emul/src/elf.rs @@ -0,0 +1,112 @@ +// Based on: https://github.com/risc0/risc0/blob/6b6daeafa1545984aa28581fca56d9ef13dcbae6/risc0/binfmt/src/elf.rs +// +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +extern crate alloc; + +use alloc::collections::BTreeMap; + +use crate::addr::WORD_SIZE; +use anyhow::{anyhow, bail, Context, Result}; +use elf::{endian::LittleEndian, file::Class, ElfBytes}; + +/// A RISC Zero program +pub struct Program { + /// The entrypoint of the program + pub entry: u32, + + /// The initial memory image + pub image: BTreeMap, +} + +impl Program { + /// Initialize a RISC Zero Program from an appropriate ELF file + pub fn load_elf(input: &[u8], max_mem: u32) -> Result { + let mut image: BTreeMap = BTreeMap::new(); + let elf = ElfBytes::::minimal_parse(input) + .map_err(|err| anyhow!("Elf parse error: {err}"))?; + if elf.ehdr.class != Class::ELF32 { + bail!("Not a 32-bit ELF"); + } + if elf.ehdr.e_machine != elf::abi::EM_RISCV { + bail!("Invalid machine type, must be RISC-V"); + } + if elf.ehdr.e_type != elf::abi::ET_EXEC { + bail!("Invalid ELF type, must be executable"); + } + let entry: u32 = elf + .ehdr + .e_entry + .try_into() + .map_err(|err| anyhow!("e_entry was larger than 32 bits. {err}"))?; + if entry >= max_mem || entry % WORD_SIZE as u32 != 0 { + bail!("Invalid entrypoint"); + } + let segments = elf.segments().ok_or(anyhow!("Missing segment table"))?; + if segments.len() > 256 { + bail!("Too many program headers"); + } + for segment in segments.iter().filter(|x| x.p_type == elf::abi::PT_LOAD) { + let file_size: u32 = segment + .p_filesz + .try_into() + .map_err(|err| anyhow!("filesize was larger than 32 bits. {err}"))?; + if file_size >= max_mem { + bail!("Invalid segment file_size"); + } + let mem_size: u32 = segment + .p_memsz + .try_into() + .map_err(|err| anyhow!("mem_size was larger than 32 bits {err}"))?; + if mem_size >= max_mem { + bail!("Invalid segment mem_size"); + } + let vaddr: u32 = segment + .p_vaddr + .try_into() + .map_err(|err| anyhow!("vaddr is larger than 32 bits. {err}"))?; + if vaddr % WORD_SIZE as u32 != 0 { + bail!("vaddr {vaddr:08x} is unaligned"); + } + let offset: u32 = segment + .p_offset + .try_into() + .map_err(|err| anyhow!("offset is larger than 32 bits. {err}"))?; + for i in (0..mem_size).step_by(WORD_SIZE) { + let addr = vaddr.checked_add(i).context("Invalid segment vaddr")?; + if addr >= max_mem { + bail!( + "Address [0x{addr:08x}] exceeds maximum address for guest programs [0x{max_mem:08x}]" + ); + } + if i >= file_size { + // Past the file size, all zeros. + image.insert(addr, 0); + } else { + let mut word = 0; + // Don't read past the end of the file. + let len = core::cmp::min(file_size - i, WORD_SIZE as u32); + for j in 0..len { + let offset = (offset + i + j) as usize; + let byte = input.get(offset).context("Invalid segment offset")?; + word |= (*byte as u32) << (j * 8); + } + image.insert(addr, word); + } + } + } + Ok(Program { entry, image }) + } +} diff --git a/ceno_emul/src/lib.rs b/ceno_emul/src/lib.rs index 51d101a38..323ef2fd1 100644 --- a/ceno_emul/src/lib.rs +++ b/ceno_emul/src/lib.rs @@ -11,3 +11,6 @@ mod vm_state; pub use vm_state::VMState; mod rv32im; + +mod elf; +pub use elf::Program; diff --git a/ceno_emul/src/loader.rs b/ceno_emul/src/loader.rs new file mode 100644 index 000000000..e69de29bb diff --git a/ceno_emul/src/vm_state.rs b/ceno_emul/src/vm_state.rs index 3996f2ad3..b4f31e5ea 100644 --- a/ceno_emul/src/vm_state.rs +++ b/ceno_emul/src/vm_state.rs @@ -80,8 +80,8 @@ impl EmuContext for VMState { // Expect an ecall to indicate a successful exit: // function HALT with argument SUCCESS. fn ecall(&mut self) -> Result { - let function = 0; // self.load_register(self.platform.reg_ecall())?; - let argument = 0; // self.load_register(self.platform.reg_arg0())?; + let function = self.load_register(self.platform.reg_ecall())?; + let argument = self.load_register(self.platform.reg_arg0())?; if function == self.platform.ecall_halt() && argument == self.platform.code_success() { self.succeeded = true; Ok(true) diff --git a/ceno_emul/tests/data/README.md b/ceno_emul/tests/data/README.md new file mode 100644 index 000000000..e71cf5728 --- /dev/null +++ b/ceno_emul/tests/data/README.md @@ -0,0 +1,7 @@ +### Generate test programs: + +```bash +cd ceno_rt +cargo build --release --examples +cp ../target/riscv32im-unknown-none-elf/release/examples/ceno_rt_mini ../ceno_emul/tests/data/ +``` \ No newline at end of file diff --git a/ceno_emul/tests/data/ceno_rt_mem b/ceno_emul/tests/data/ceno_rt_mem new file mode 100755 index 0000000000000000000000000000000000000000..0ce7cfc5add5f4728af2a3523863f60a1dd33b1e GIT binary patch literal 10136 zcmeHN-)kII6h5=vwG~21-6{nIrzDgL$#iCRvO7~jB26UV;x7^?B8)pfHam8Avdqq= zNfDepRQlk97*L-i4+;ezh58!s#s5RZ=b}C+*!4T}Bi&4DRSNq|+Hco91O)<-54B*WiRa&%cWiIp*X; zVD>7!;L(mP$vx$s;0)%y`D5&HShkc$@)Xf$z(Evk15yU3lN$}Q))v<@+I z>u^FH*VD?`*_Ayh?b!!hMqcJT>^XP>ZzIR+aqZfkbf>a1bA}=F2k@1@;6Vu!3J3*+ z0zv_yfKWgvAQTV^2nB=!LII(`|EfSV9>uP_?MI_vE0i^*Whv@n7;N_9{;qu4@0yO~ zxm{h>Roz%rjYZvf0XM=yc*Bpb%I)@wteJ|Y81%A?KO+i8?lwu!Ags>TqV0wr=tIpO z=tC8$6E^)@eUx{FIRl(R$V|=#iGC?zhS#V1{Z_)v?@aLza$M}M z%$M;M2sk~z%rm|-)t@o{{X7;YB_4+j^nV=BT@Fh9_`C<_xxa<^uq3_W)W%C0&LCdz zJUGJF{)R8tul>vV^8EiAzf;E^hJsPGQ;u$_Vg59zpsJ${VV5~l-S>iN%4DjH|h+0Qk>C<6nASd@I&O&e}APD z$5G&nV;|ILcMx|RjJ;7DWl=Vg>xWx*6x#!Tp!kldxmvU9`5m)sY5tIW?yr;DI zV)DfzrQHug--fh(-yX%CC{~%!va6QHdV0mu$Q$Vuy%49(w6vR+w$m)m>*-t_%BB5i zuxm%YJB~*Dar%#q-%9NHnJt+gtm3_QWc(S*b{fugc$A!-7en14;=+o$b4Z+l=byR> z$@zJ*ys)U_?;h@_=6SALD`Jeyu|E$-uOWui_&nLgJm!IG*fcort+2j~{b3^Sd0f6Nj?2B}~YsBq1wiB&}ta5K^)UIhaCAPhml^rLiqyYmKbk zZc3q>TMoHpA>@*ODNu6BC6^F-t;r#m1ogd<Nv-hZ{tF=|@vQaQKo#8-&wD22RH3_%b^9y*bq=*Ave9e#Yl`h|U)BRI6N%F2{+3!Q<57 zsbTO0ol6Bh;hZNskDnHBktd9wnUl>sH}rWl1g6cW@H13=il+_me&A&D{kP}v^OVTe zX|e#Z{x22+@AC^Z>R&DdKCfSpWB&h*-z;o6P2LvpTw4#BX|jwZw`ZCqSV=EI7FmLm z1-w?krwe$!fG;lKX(A9GU5^Sh3jF01>-+yC-48~r_G2*A!0knykpjcp8I4qkA^l4; zI&mBY-Q8GCz?j?}#hotN!6c4SFSX#QaL0{ecNBzy(d%}lkWI@Io-{XYvElW14WS)vh&ua7>$G3cCb$y*>(P1{M`^F|M z4KUinAXF}X>!ETdaVLre5e_X)T3WP79h6f0re1L;jO(qW-b*%gL}$t-q?^rdkuPxQ z64K0ix11jEc+F>{nt5NLB^~~yqX`vx)fiSs!1 zi_hc$E$L|B^Gcto0WRC96PQS!$HYnJ;t~3Hf#KPBa0=kY3?1!v1!MENzfgBAKMc9t F{TI!o2VDRF literal 0 HcmV?d00001 diff --git a/ceno_emul/tests/test_elf.rs b/ceno_emul/tests/test_elf.rs new file mode 100644 index 000000000..d9f006f43 --- /dev/null +++ b/ceno_emul/tests/test_elf.rs @@ -0,0 +1,54 @@ +use anyhow::Result; +use ceno_emul::{ByteAddr, Program, StepRecord, VMState, CENO_PLATFORM}; + +#[test] +fn test_ceno_rt_mini() -> Result<()> { + let mut state = VMState::new(CENO_PLATFORM); + + // Load an ELF program in memory. + let program_elf = include_bytes!("./data/ceno_rt_mini"); + let program = Program::load_elf(program_elf, u32::MAX)?; + for (addr, word) in program.image.iter() { + let addr = ByteAddr(*addr).waddr(); + state.init_memory(addr, *word); + } + assert_eq!(program.entry, CENO_PLATFORM.pc_start()); + + let _steps = run(&mut state)?; + + Ok(()) +} + +#[test] +fn test_ceno_rt_mem() -> Result<()> { + let mut state = VMState::new(CENO_PLATFORM); + + // Load an ELF program in memory. + let program_elf = include_bytes!("./data/ceno_rt_mem"); + let program = Program::load_elf(program_elf, u32::MAX)?; + for (addr, word) in program.image.iter() { + let addr = ByteAddr(*addr).waddr(); + state.init_memory(addr, *word); + } + assert_eq!(program.entry, CENO_PLATFORM.pc_start()); + + let mut prev_step = StepRecord::default(); + for step in state.iter_until_success() { + match step { + Ok(step) => { + println!("{:?}", step); + prev_step = step; + } + Err(e) => { + println!("pc = {:?}", prev_step.pc().after); + return Err(e); + } + } + } + + Ok(()) +} + +fn run(state: &mut VMState) -> Result> { + state.iter_until_success().collect() +} diff --git a/ceno_rt/Cargo.toml b/ceno_rt/Cargo.toml index 8c14cb02f..c295821f2 100644 --- a/ceno_rt/Cargo.toml +++ b/ceno_rt/Cargo.toml @@ -6,4 +6,4 @@ license.workspace = true [dependencies] riscv = "0.11.1" -#riscv-rt = "0.12.2" +riscv-rt = "0.12.2" diff --git a/ceno_rt/ceno_link.x b/ceno_rt/ceno_link.x index c8a16cf19..8c366949f 100644 --- a/ceno_rt/ceno_link.x +++ b/ceno_rt/ceno_link.x @@ -4,5 +4,24 @@ SECTIONS { *(.text._start); *(.text .text.*); - } > FLASH + } > ROM + + .rodata : ALIGN(4) + { + *(.srodata .srodata.*); + *(.rodata .rodata.*); + } > ROM + + .data : ALIGN(4) + { + *(.sdata .sdata.*); + *(.sdata2 .sdata2.*); + *(.data .data.*); + } > RAM + + .bss (NOLOAD) : ALIGN(4) + { + *(.sbss .sbss.*); + *(.bss .bss.*); + } > RAM } \ No newline at end of file diff --git a/ceno_rt/examples/ceno_rt_mem.rs b/ceno_rt/examples/ceno_rt_mem.rs new file mode 100644 index 000000000..e8c82e988 --- /dev/null +++ b/ceno_rt/examples/ceno_rt_mem.rs @@ -0,0 +1,46 @@ +#![no_main] +#![no_std] +use core::{arch::asm, panic::PanicInfo, ptr::addr_of_mut}; + +#[panic_handler] +#[inline(never)] +fn panic(_panic: &PanicInfo<'_>) -> ! { + halt(1) +} + +fn halt(exit_code: u32) -> ! { + unsafe { + asm!( + "mv a0, {}", + "li t0, 0x0", + in(reg) exit_code, + ); + riscv::asm::ecall(); + } + unreachable!() +} + +static mut OUTPUT: u32 = 0; + +#[inline(never)] +fn output(out: u32) { + // Volatile write to prevent the compiler from optimizing this away. + unsafe { core::ptr::write_volatile(addr_of_mut!(OUTPUT), out) }; +} + +#[no_mangle] +pub fn _start() -> ! { + let y = my_recurse(3, 0); + output(y); + halt(0) +} + +#[inline(never)] +#[no_mangle] +fn my_recurse(x: u32, y: u32) -> u32 { + if x == 0 { + y + } else { + my_recurse(x - 1, y * 3 + 5) + } +} diff --git a/ceno_rt/memory.x b/ceno_rt/memory.x index 9b971fe8e..cf1b9a74f 100644 --- a/ceno_rt/memory.x +++ b/ceno_rt/memory.x @@ -1,11 +1,11 @@ MEMORY { RAM : ORIGIN = 0x80000000, LENGTH = 16M - FLASH : ORIGIN = 0x20000000, LENGTH = 16M + ROM : ORIGIN = 0x20000000, LENGTH = 16M } -REGION_ALIAS("REGION_TEXT", FLASH); -REGION_ALIAS("REGION_RODATA", FLASH); +REGION_ALIAS("REGION_TEXT", ROM); +REGION_ALIAS("REGION_RODATA", ROM); REGION_ALIAS("REGION_DATA", RAM); REGION_ALIAS("REGION_BSS", RAM); REGION_ALIAS("REGION_HEAP", RAM); From ef1839cc0d5c97ccf40d74e6199fd55cd9aab908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Sun, 1 Sep 2024 09:07:46 +0200 Subject: [PATCH 05/14] runtime: use .init section --- ceno_emul/tests/data/ceno_rt_mem | Bin 10136 -> 10232 bytes ceno_emul/tests/data/ceno_rt_mini | Bin 5976 -> 5972 bytes ceno_rt/ceno_link.x | 3 ++- ceno_rt/examples/ceno_rt_mem.rs | 19 ++++++++++++------- ceno_rt/examples/ceno_rt_mini.rs | 1 + 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/ceno_emul/tests/data/ceno_rt_mem b/ceno_emul/tests/data/ceno_rt_mem index 0ce7cfc5add5f4728af2a3523863f60a1dd33b1e..6113567dd7e6938353132cd514f74b33d7647fc6 100755 GIT binary patch delta 1041 zcmZ9LPiPZS5XR@F30kOe+teCO#g~|*IkYvKzb)DtrJigpcn}dW*e2Qvsj>9XTO#qO z81oc!5PK0Uo)kemsH`4D5IlJZhzBp;1beE7;`+^|DVq$;y!m}IJIvc<>$87nt5Q_& z`;UYSB4T&JNh1O>3td z#-^%jiBiTi%^o~OZfhV7BOQa*zpe3^fp7gY%|X_t+}hMFL9AbZOY}x;I+md#JldLz zoVDBV2sDY4jc3(~9F$Nay*}3);P9)N-io^>t0fdAFXbSyk7(!X;C0l!I9vTlB^)N`5mAXV$$^E(h{wSgvhx5l5GLpoqMg5hbEw-dzzi8C zXcN)STb52`r&Yx#B9HY7Wm6Xv5n%Px@7p(mtcp?g-K$&?m(`)b2{j$i*j@AJTURFo zKAmUa-R{g{Hf+3rPFUHB)I|kV2pks6>TckuSXJ)=_eyt~9nA*XFl!xL@8Bok#CA84 zlb*KKo%FneUv%*6c0NyU+XedA#vA5mu(5L`2Yv0Re`~TF#8Q1E=sUQm{K4Px;Oc0M zvKP1$iB+jonn_Hym(s+V)>c7-<{(%oMUfs9L@KnU2#R1)BrAe= z5KQw9_EIb2q2NUkyeSJ_ya-TmV*v!D2dB694nK!#T`8~fm-!!VS zkv~$}AtH%1f;5p5#)_Je2*KgomF>NHC|x0j?HMXRg23Z`J(&tUUeae$wZGid2l=BO zU0IHzXb=|4#QV`W(yOTLPt^>_EP4>KFzD=pJ(;O9oRO~p?_(%t|u6is7J@=5RvbZOu93j;9}UB{f{Rd5sf zFjn!)o0tP9`ZeML>KKYXVHSgs@MoI@UQB(K68cYDF?NwpLA03A|2QoAdP4t47i^(> zLf7DO89kC4xVrFT?&Nc=g622*K~?2;{&aTM9xV`=sEzEvu#Nq$+X>^Vlo^@%EfD@`4kl60DiOB2UsnvrRiNBu*5)UWeBe?jf= v7e6-;|E+MXL;mG&4}`b^{b+E4x5h@&<3*zLeA5i^*D*bO2lPI1sBZcn`7eQ2 diff --git a/ceno_emul/tests/data/ceno_rt_mini b/ceno_emul/tests/data/ceno_rt_mini index fab9164192b89cfcec3e41fb4bdc64fc14264d87..0a2940dabb9130d1dedceb106c019c1f15068935 100755 GIT binary patch delta 381 zcmcbicSUc40;9-A#b`lBmC3b&@`7dz3=BLB3<^LB2pAcxC$9yHrUBU!NTS)3?*c_< zfYl(>&YjFFq|bP6vaL`&{k|W@(t5Y>{G|Y&Q9Wh`B(TpD)b5S7?kAZuYiNOp_-ez*G zNU}g$Qj(#8p@ng>L9(Hdsfoel_af#3Nht=&#>T0}1_qX?$rg#`o6SVo7&lvru`@C{ JPj(a+1^`#2Hdz1w diff --git a/ceno_rt/ceno_link.x b/ceno_rt/ceno_link.x index 8c366949f..16c008590 100644 --- a/ceno_rt/ceno_link.x +++ b/ceno_rt/ceno_link.x @@ -2,7 +2,8 @@ SECTIONS { .text : { - *(.text._start); + *(.init); + . = ALIGN(4); *(.text .text.*); } > ROM diff --git a/ceno_rt/examples/ceno_rt_mem.rs b/ceno_rt/examples/ceno_rt_mem.rs index e8c82e988..e97c1ff1e 100644 --- a/ceno_rt/examples/ceno_rt_mem.rs +++ b/ceno_rt/examples/ceno_rt_mem.rs @@ -20,6 +20,18 @@ fn halt(exit_code: u32) -> ! { unreachable!() } +#[link_section = ".init"] +#[no_mangle] +pub fn _start() -> ! { + main(); + halt(0) +} + +fn main() { + let y = my_recurse(3, 0); + output(y); +} + static mut OUTPUT: u32 = 0; #[inline(never)] @@ -28,13 +40,6 @@ fn output(out: u32) { unsafe { core::ptr::write_volatile(addr_of_mut!(OUTPUT), out) }; } -#[no_mangle] -pub fn _start() -> ! { - let y = my_recurse(3, 0); - output(y); - halt(0) -} - #[inline(never)] #[no_mangle] fn my_recurse(x: u32, y: u32) -> u32 { diff --git a/ceno_rt/examples/ceno_rt_mini.rs b/ceno_rt/examples/ceno_rt_mini.rs index 520a28aaf..4a20e1895 100644 --- a/ceno_rt/examples/ceno_rt_mini.rs +++ b/ceno_rt/examples/ceno_rt_mini.rs @@ -20,6 +20,7 @@ fn halt(exit_code: u32) -> ! { unreachable!() } +#[link_section = ".init"] #[no_mangle] pub fn _start() -> ! { halt(0) From 2b84514e3773fd287bb6ee42e985a95bfc30f99c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 2 Sep 2024 09:27:40 +0200 Subject: [PATCH 06/14] runtime: startup code in the crate --- ceno_emul/tests/data/ceno_rt_mem | Bin 10232 -> 10448 bytes ceno_emul/tests/test_elf.rs | 8 +++- ceno_rt/.cargo/config.toml | 2 +- ceno_rt/ceno_link.x | 14 ++++++- ceno_rt/examples/ceno_rt_mem.rs | 47 +++++++--------------- ceno_rt/examples/ceno_rt_mini.rs | 25 ++---------- ceno_rt/memory.x | 2 +- ceno_rt/src/lib.rs | 65 +++++++++++++++++++++++++++++++ 8 files changed, 104 insertions(+), 59 deletions(-) create mode 100644 ceno_rt/src/lib.rs diff --git a/ceno_emul/tests/data/ceno_rt_mem b/ceno_emul/tests/data/ceno_rt_mem index 6113567dd7e6938353132cd514f74b33d7647fc6..05d6aa9815be612201f08815340a702e0ea0eb84 100755 GIT binary patch literal 10448 zcmeHNPl()982>Wev8yg++tIdA4=EjW1vey_%w)%lTiBKgi&|+#^pIwf%uML~Ba`VL zEtsVgJ?Lq{>d9UTf)_=wMG#j!c@d8*c+(<+mqi3Y+VS@#lW8`s)QZRV!T0ih-|ze0 zd-L05hW9pa9$7sqNfM=2px_K$dc?_Ogm{33{AQTV^2nB=! zLII(GP(Uak6c7ps1%v|I6}Y^cPOYy=(LRZ;l%%zBX-1+xMLSE9G(PmiZYmX3TF1uz zGRl65BI%<1HGz}%VH4h>iv?${h@3yhrS~4C&x)VZ`lXZf)BfVE(xoD`BBE%Eqx3_%dR19Ds4`75Gvb+m*yR zh^dJ2_=Ff=BRIz%!!@0U`|rbf9$p8>kF7~>7gpwG88Uwz{`5caoFhyqAQTV^2nB=! zLII(GP(Uak6c7ps1%v|sMS);63?2E57YzM=Pu3K(uBeMWzts*q=j8>jxzwn;PE(h4 zRW}w@V^KGrB3ku(r@i1Ad3AL~)|M1aG3c<2*#m#*oFQp1x-;{|;7mpL^?_z}^#O0M z@38Fu>St(cTuw6N z;q&-xj=#VO*y<205}WSHXmb7;x`k%K%2#10iOfU=LPBd7z4s2`WbB&{|dYT z9-n!d|4t@x23Yrs3`R>E&2{MPtkoF~E}ZkQ(}^v;E(cx|g0*j{N=2i`@ z(Oh&|BSj@;b>IYE$7=g3mCtPpXdKYUdi8lt%Zya(kHWzy)XcW(X{u>Ex~tb~hTX7_ zaDmQE<<6=XPM&!2$=nX0Zj^|+#T zmx`p;YIb=Z!!Vsnt!bKW#q}x;H?A^ySxZ!Ql{z;X1VgJq!j9kbEGXactYK&eVZuNO z@RhhV;(xJMm=y9zc*Xjm!ZNEp|^H$(FqhRP!GK;d0t|IYYUWuLsmCi7u3o|UQbZwT8?IOp-L%+J!~ zJvyMo(!AY(X&$=KCmhIFnzs+JSvZ~swut29J-Ha6N%Q!bTv{AI}f}Isf5Yl{Vgc5c??&WdjcG3^0t)SGmczAVsi_TtCX91?lc1@!uJTzv07`l4`&cFwKR z&qoS3isuS4p7xxb+oob()8cE5uHD){{3||&z`p|##Z~4&7^0r3oe-SE>J3ry)zCEas_Jz&9x1jUz3Ct|q z>jKn&=a#1rGUWJI&=da<=Nu)90mXn~Krx^gPz)#r6a$I@#eiZ!F`yW@ zlMIA|e&mUbAndo-yTa5fj&3Y;+s#(Awk76+M%8tEuVD$xu zSXo&XW>q(Jo1PF@RoL!(8>HP2Ge1)ZH%eC9>Y25U)idDQCzHR`=jrNr4jI@R3*NKm zl3(%}$Aub|lX!|Bg@OIK;Qe}<&p3?-Nj$}`!uS+%-jk>KFa;E%$#V@Df)V2nFg^y1 zp1b*s0U;WFhj9{r4~!QV_N<>V+z^efVVuO*rJsg*8O31<1m_Dv8pxm5_G0#*pNKCY z&Uo_kOvbbMuO#_t|7@J`#fkomnJ=Ds{Vcb`9#U=>=X#ub2%gh^qY2!KFEQjOzva=1 zaSU;PB*rcd|0SNy-~6BX+4?(;-^r}!-3-2v!5;$KyNk%vM|G#^NZ}bPw*2pMd%P6$vIi3N|CsgI28();j^|Zof~ux8CUl zUHIkWLa#?r*meg|fKY#{6V+YJt$q~7Ry>mzbk}QPROJIR$880auzMO>e`O&1`X>Zr<6iV6yXaPA zdTC#Jd3_M|2D~EI@_ffbeo(QhPRW-A_WpurS5H5;dKv|#YQ0=;)D5HRIaSLm%YwyQ ze1!PJ)EXVWjBQuEQn^v7_$5Cmx$$Mn{OKz+=j&A-93D)A^p&-CH>lyOSPN?Xs2)a! zjF@t?R_&M)XYGdXY z>qAiV!k Result<()> { for step in state.iter_until_success() { match step { Ok(step) => { - println!("{:?}", step); + // println!("{:?}", step); prev_step = step; } Err(e) => { @@ -46,6 +46,12 @@ fn test_ceno_rt_mem() -> Result<()> { } } + for i in 0..4 { + let addr = ByteAddr(CENO_PLATFORM.ram_start()).waddr() + i as u32; + let value = state.peek_memory(addr); + println!("{:?} = 0x{:08x}", addr, value); + } + Ok(()) } diff --git a/ceno_rt/.cargo/config.toml b/ceno_rt/.cargo/config.toml index d2a0dba12..c08ff50ab 100644 --- a/ceno_rt/.cargo/config.toml +++ b/ceno_rt/.cargo/config.toml @@ -1,7 +1,7 @@ [target.riscv32im-unknown-none-elf] rustflags = [ "-C", "link-arg=-Tmemory.x", - #"-C", "link-arg=-Tlink.x", + #"-C", "link-arg=-Tlink.x", // Script from riscv_rt. "-C", "link-arg=-Tceno_link.x", ] diff --git a/ceno_rt/ceno_link.x b/ceno_rt/ceno_link.x index 16c008590..63fa1446a 100644 --- a/ceno_rt/ceno_link.x +++ b/ceno_rt/ceno_link.x @@ -1,8 +1,11 @@ + +_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK); + SECTIONS { .text : { - *(.init); + KEEP(*(.init)); . = ALIGN(4); *(.text .text.*); } > ROM @@ -15,9 +18,16 @@ SECTIONS .data : ALIGN(4) { + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.*); *(.sdata2 .sdata2.*); *(.data .data.*); + . = ALIGN(4); + + _sheap = .; + } > RAM .bss (NOLOAD) : ALIGN(4) @@ -25,4 +35,4 @@ SECTIONS *(.sbss .sbss.*); *(.bss .bss.*); } > RAM -} \ No newline at end of file +} diff --git a/ceno_rt/examples/ceno_rt_mem.rs b/ceno_rt/examples/ceno_rt_mem.rs index e97c1ff1e..746bdae5e 100644 --- a/ceno_rt/examples/ceno_rt_mem.rs +++ b/ceno_rt/examples/ceno_rt_mem.rs @@ -1,45 +1,17 @@ #![no_main] #![no_std] -use core::{arch::asm, panic::PanicInfo, ptr::addr_of_mut}; +use core::ptr::addr_of_mut; -#[panic_handler] -#[inline(never)] -fn panic(_panic: &PanicInfo<'_>) -> ! { - halt(1) -} - -fn halt(exit_code: u32) -> ! { - unsafe { - asm!( - "mv a0, {}", - "li t0, 0x0", - in(reg) exit_code, - ); - riscv::asm::ecall(); - } - unreachable!() -} +#[allow(unused_imports)] +use ceno_rt; -#[link_section = ".init"] #[no_mangle] -pub fn _start() -> ! { - main(); - halt(0) -} - fn main() { let y = my_recurse(3, 0); output(y); } -static mut OUTPUT: u32 = 0; - -#[inline(never)] -fn output(out: u32) { - // Volatile write to prevent the compiler from optimizing this away. - unsafe { core::ptr::write_volatile(addr_of_mut!(OUTPUT), out) }; -} - +// A sufficiently complicated function to test the stack. #[inline(never)] #[no_mangle] fn my_recurse(x: u32, y: u32) -> u32 { @@ -49,3 +21,14 @@ fn my_recurse(x: u32, y: u32) -> u32 { my_recurse(x - 1, y * 3 + 5) } } + +// A global variable to test writing to memory. +static mut OUTPUT: u32 = 0; + +#[inline(never)] +fn output(out: u32) { + unsafe { + // Volatile write to prevent the compiler from optimizing this away. + core::ptr::write_volatile(addr_of_mut!(OUTPUT), out); + } +} diff --git a/ceno_rt/examples/ceno_rt_mini.rs b/ceno_rt/examples/ceno_rt_mini.rs index 4a20e1895..c3f8b88ab 100644 --- a/ceno_rt/examples/ceno_rt_mini.rs +++ b/ceno_rt/examples/ceno_rt_mini.rs @@ -1,27 +1,8 @@ #![no_main] #![no_std] -use core::{arch::asm, panic::PanicInfo}; -#[panic_handler] -#[inline(never)] -fn panic(_panic: &PanicInfo<'_>) -> ! { - halt(1) -} +#[allow(unused_imports)] +use ceno_rt; -fn halt(exit_code: u32) -> ! { - unsafe { - asm!( - "mv a0, {}", - "li t0, 0x0", - in(reg) exit_code, - ); - riscv::asm::ecall(); - } - unreachable!() -} - -#[link_section = ".init"] #[no_mangle] -pub fn _start() -> ! { - halt(0) -} +fn main() {} diff --git a/ceno_rt/memory.x b/ceno_rt/memory.x index cf1b9a74f..712de56cd 100644 --- a/ceno_rt/memory.x +++ b/ceno_rt/memory.x @@ -1,6 +1,6 @@ MEMORY { - RAM : ORIGIN = 0x80000000, LENGTH = 16M + RAM : ORIGIN = 0x80000000, LENGTH = 1024M ROM : ORIGIN = 0x20000000, LENGTH = 16M } diff --git a/ceno_rt/src/lib.rs b/ceno_rt/src/lib.rs new file mode 100644 index 000000000..708b58988 --- /dev/null +++ b/ceno_rt/src/lib.rs @@ -0,0 +1,65 @@ +#![no_main] +#![no_std] +use core::{ + arch::{asm, global_asm}, + panic::PanicInfo, +}; + +#[panic_handler] +#[inline(never)] +pub fn panic_handler(_panic: &PanicInfo<'_>) -> ! { + halt(1) +} + +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(); + } + unreachable!() +} + +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 + ", +); + +#[no_mangle] +pub unsafe extern "C" fn _start_rust() -> ! { + main(); + halt(0) +} + +extern "C" { + fn main(); +} + +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; +} From 8982cc74dfe9f65ebf22d2a7dbdd447fe4f02441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 2 Sep 2024 10:20:57 +0200 Subject: [PATCH 07/14] runtime: detect busy loops --- ceno_emul/src/tracer.rs | 4 ++++ ceno_emul/src/vm_state.rs | 6 +++++- ceno_emul/tests/data/ceno_rt_mem | Bin 10448 -> 9520 bytes ceno_emul/tests/data/ceno_rt_mini | Bin 5972 -> 5208 bytes ceno_rt/ceno_link.x | 7 +++---- ceno_rt/src/lib.rs | 2 +- 6 files changed, 13 insertions(+), 6 deletions(-) diff --git a/ceno_emul/src/tracer.rs b/ceno_emul/src/tracer.rs index 5a1a56e86..732e96986 100644 --- a/ceno_emul/src/tracer.rs +++ b/ceno_emul/src/tracer.rs @@ -52,6 +52,10 @@ impl StepRecord { pub fn memory_op(&self) -> (WordAddr, Change) { self.memory_op } + + pub fn is_busy_loop(&self) -> bool { + self.pc.before == self.pc.after + } } #[derive(Debug, Default)] diff --git a/ceno_emul/src/vm_state.rs b/ceno_emul/src/vm_state.rs index b4f31e5ea..6bccfc035 100644 --- a/ceno_emul/src/vm_state.rs +++ b/ceno_emul/src/vm_state.rs @@ -72,7 +72,11 @@ impl VMState { fn step(&mut self, emu: &Emulator) -> Result { emu.step(self)?; let step = self.tracer().advance(); - Ok(step) + if step.is_busy_loop() && !self.succeeded() { + Err(anyhow!("Stuck in loop {}", "{}")) + } else { + Ok(step) + } } } diff --git a/ceno_emul/tests/data/ceno_rt_mem b/ceno_emul/tests/data/ceno_rt_mem index 05d6aa9815be612201f08815340a702e0ea0eb84..6cc8681c2421c6f2b174e939540d6efde6daf269 100755 GIT binary patch delta 739 zcmZ8fJxe4(6s+0pMFqvt@#Ms>#vOMyS{6G!7U=PQCpmyL7 zFflpMOH4$(NRxvTBO^5u3`7$HMb>)Le(dgpu6kAPbx(Is{~zBOThqDYG~x)Mp^!xl zs^Ey@#`umNR+3XmTTua!r#UTwX-t3#{rifz=&ocNx(@guX; zJ<;s{>EMG?Fu#J%b}kwf?0b>*<4`YL zXD)-lX*4z2z-cx4Z163|3ZKGT%xK`2+o`70!XYzdv3?Hw$CxmFbOEDgL3`Ojs?y39 MD`|VcxbroA2D#!@MgRZ+ literal 10448 zcmeHNPl()982>Wev8yg++tIdA4=EjW1vey_%w)%lTiBKgi&|+#^pIwf%uML~Ba`VL zEtsVgJ?Lq{>d9UTf)_=wMG#j!c@d8*c+(<+mqi3Y+VS@#lW8`s)QZRV!T0ih-|ze0 zd-L05hW9pa9$7sqNfM=2px_K$dc?_Ogm{33{AQTV^2nB=! zLII(GP(Uak6c7ps1%v|I6}Y^cPOYy=(LRZ;l%%zBX-1+xMLSE9G(PmiZYmX3TF1uz zGRl65BI%<1HGz}%VH4h>iv?${h@3yhrS~4C&x)VZ`lXZf)BfVE(xoD`BBE%Eqx3_%dR19Ds4`75Gvb+m*yR zh^dJ2_=Ff=BRIz%!!@0U`|rbf9$p8>kF7~>7gpwG88Uwz{`5caoFhyqAQTV^2nB=! zLII(GP(Uak6c7ps1%v|sMS);63?2E57YzM=Pu3K(uBeMWzts*q=j8>jxzwn;PE(h4 zRW}w@V^KGrB3ku(r@i1Ad3AL~)|M1aG3c<2*#m#*oFQp1x-;{|;7mpL^?_z}^#O0M z@38Fu>St(cTuw6N z;q&-xj=#VO*y<205}WSHXmb7;x`k%K%2#10iOfU=LPBd7z4s2`WbB&{|dYT z9-n!d|4t@x23Yrs3`R>E&2{MPtkoF~E}ZkQ(}^v;E(cx|g0*j{N=2i`@ z(Oh&|BSj@;b>IYE$7=g3mCtPpXdKYUdi8lt%Zya(kHWzy)XcW(X{u>Ex~tb~hTX7_ zaDmQE<<6=XPM&!2$=nX0Zj^|+#T zmx`p;YIb=Z!!Vsnt!bKW#q}x;H?A^ySxZ!Ql{z;X1VgJq!j9kbEGXactYK&eVZuNO z@RhhV;(xJMm=y9zc*Xjm!ZNEp|^H$(FqhRP!GK;d0t|IYYUWuLsmCi7u3o|UQbZwT8?IOp-L%+J!~ zJvyMo(!AY(X&$=KCmhIFnzs+JSvZ~swut29J-Ha6N%Q!bHV{5I?sGEdx^8AV6Yym4Z~H=E-)PCS@rgzJ@GaSQuQ}d2QS#wq?IY?L^DQ z+yNvOMmGKh{uy>67PfHbXG@7uH^z6;ef;h&?_GYehur&@gI9)OKq)Km8w$i6O#a$b z6$cos!ZzH7HC1oqd4`tlpdqcJ0_G2X${~eSxm6@q0TQV`pJ%qvlGad*yYsv@ngPv# zW7CS3Odo9{=l|s8Jk~LK7!`Fo5atHes zrR=+dNqio0hnCBYeY9?)^GwwTbl#gHCRfh&5M$b-l_lDzZIz(tBiKeQW+MonvF{KM op;$`({ znx;`P8uTaSNLV1RUdt4V1U2aI&2qs4Gxcpsqk&fw}^91?mda6{ssvSD>yyU4j2r zfi_;+zeURLr+2h=Yc2OrsoiSPHw~g0v`nW)Thr2wch+fR^D(uXnwIm=Ud*39%UT3@ zXMgk=X;}XY~?8))UzDUv!R%k7kK0j{SHR9!5gyJzF=nqHu4V96S^^{h&XxJvT6gX_(fQ zVQrb#Euujf?fdcjVldbdvad_sqK^b`7S7yb(zr#^Tx-S09Wyki(wUf31JrY4xp&c| zdAf-R)Ie|@8hK}fah9l2H`fb%8xFp=;2IV2j0-);^#bq1*@4crD&pY^7=}jQWsb-e zjlPG2>rjekTzC!C2{CWky zUBMd_e5-`#>3hKE*P}t(75#RG)&2h(e^@>7PZHAou@l6@iBGzFIGOknDD_Lzhe;BL zqoc&1kv@AkNroe=<5?1CUbd3!M~6DxSY zNgo`9k?-J3ANbBJ8ODjhLXW3DPc~0dky0w(R5Nx0ueOqEFWFU$%9MS|He1{wuX6bE zvE_YR!4G#_@5N~5-bJh&hrg4oCycnWT;rvP*n10fzTb!7mg9az+{eXX& RAM .bss (NOLOAD) : ALIGN(4) { *(.sbss .sbss.*); *(.bss .bss.*); + + . = ALIGN(4); + _sheap = .; } > RAM } diff --git a/ceno_rt/src/lib.rs b/ceno_rt/src/lib.rs index 708b58988..9d3eeada7 100644 --- a/ceno_rt/src/lib.rs +++ b/ceno_rt/src/lib.rs @@ -22,7 +22,7 @@ pub fn halt(exit_code: u32) -> ! { ); riscv::asm::ecall(); } - unreachable!() + loop {} } global_asm!( From 8285d59f46dcad29680a5c4644e3c33095d64216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 2 Sep 2024 12:27:33 +0200 Subject: [PATCH 08/14] runtime: test panic --- ceno_emul/src/rv32im.rs | 2 +- ceno_emul/src/vm_state.rs | 2 +- ceno_emul/tests/data/README.md | 2 +- ceno_emul/tests/data/ceno_rt_panic | Bin 0 -> 5968 bytes ceno_emul/tests/test_elf.rs | 19 +++++++++++++++++++ ceno_rt/examples/ceno_rt_panic.rs | 10 ++++++++++ 6 files changed, 32 insertions(+), 3 deletions(-) create mode 100755 ceno_emul/tests/data/ceno_rt_panic create mode 100644 ceno_rt/examples/ceno_rt_panic.rs diff --git a/ceno_emul/src/rv32im.rs b/ceno_emul/src/rv32im.rs index 0e69877e8..e50c184dc 100644 --- a/ceno_emul/src/rv32im.rs +++ b/ceno_emul/src/rv32im.rs @@ -91,7 +91,7 @@ pub enum TrapCause { LoadAccessFault(ByteAddr), StoreAddressMisaligned(ByteAddr), StoreAccessFault, - EnvironmentCallFromUserMode, + EcallError, } #[derive(Clone, Debug, Default)] diff --git a/ceno_emul/src/vm_state.rs b/ceno_emul/src/vm_state.rs index 6bccfc035..92d0b33a4 100644 --- a/ceno_emul/src/vm_state.rs +++ b/ceno_emul/src/vm_state.rs @@ -90,7 +90,7 @@ impl EmuContext for VMState { self.succeeded = true; Ok(true) } else { - self.trap(TrapCause::EnvironmentCallFromUserMode) + self.trap(TrapCause::EcallError) } } diff --git a/ceno_emul/tests/data/README.md b/ceno_emul/tests/data/README.md index e71cf5728..746886507 100644 --- a/ceno_emul/tests/data/README.md +++ b/ceno_emul/tests/data/README.md @@ -3,5 +3,5 @@ ```bash cd ceno_rt cargo build --release --examples -cp ../target/riscv32im-unknown-none-elf/release/examples/ceno_rt_mini ../ceno_emul/tests/data/ +cp ../target/riscv32im-unknown-none-elf/release/examples/ceno_rt_{mini,panic,mem} ../ceno_emul/tests/data/ ``` \ No newline at end of file diff --git a/ceno_emul/tests/data/ceno_rt_panic b/ceno_emul/tests/data/ceno_rt_panic new file mode 100755 index 0000000000000000000000000000000000000000..927fa7df0ce4bc15b76d78b84fad338b3a3e4fd6 GIT binary patch literal 5968 zcmeHL&x;#%6o20|v8$9NYg#MV!<4SIiaX3q#!aGv6}M6$2ue?0#>vb#$*{>J%zSt4 zis0@+5WEzM6hzR22mb^wLh+&(Po6w^Dn0ZRJb0?p&zm3HWQx?@=aKim@AE$I`|{13 zACUL?mHxXz2r8sTe^ZV$8^POWGR7v2>U4>op|y-}rg05cy9UGBfD+=o{~A`rn9~$6 zYaPn`2X~auZdsnn11E^)&5eibdAO{+pKDiAb)f1%)q$!5RR^jLR2`@~P<5c{K-Gb& z162pA4m?>0Zf(%VH;=^OSwY{o#8J|!3z`wtZlTQG!^^c+qe-nsgKpM{V#L5v&i&gG zBDrvJgDz|y5%!5Zem@%x;J^B(ACmY?h!;-P_-{Ww5B)Jok~OTfo{j?ORk+_Gc6upx zIgb^2=YTOspc}~J8nmU+dSP48ne2mB0BX=rE_g&?ZK8Rz|z1egsL$K*Xq`OK*!C+5GQ1QW3xdW`@SVftat>npY z=0wVwyJ6tzy>8F!c74wtUcgq{?=?3ZjxfjWaGY$;eyuK1JIbf3W(fhVHi)1gY zvm1n?%yWiQWp>84-SOJn!%oL<`?5XoucU7cXCOyG=?!=kTf>6n5GbOy;EEQlZ-8o;P|0R40S@RjI#xG>tL&w6iRhE-b&co|I zy~lWttKb(>Ignh>;4BE& Result<()> { Ok(()) } +#[test] +fn test_ceno_rt_panic() -> Result<()> { + let mut state = VMState::new(CENO_PLATFORM); + + // Load an ELF program in memory. + let program_elf = include_bytes!("./data/ceno_rt_panic"); + let program = Program::load_elf(program_elf, u32::MAX)?; + for (addr, word) in program.image.iter() { + let addr = ByteAddr(*addr).waddr(); + state.init_memory(addr, *word); + } + assert_eq!(program.entry, CENO_PLATFORM.pc_start()); + + let res = run(&mut state); + assert!(matches!(res, Err(e) if e.to_string().contains("EcallError"))); + + Ok(()) +} + #[test] fn test_ceno_rt_mem() -> Result<()> { let mut state = VMState::new(CENO_PLATFORM); diff --git a/ceno_rt/examples/ceno_rt_panic.rs b/ceno_rt/examples/ceno_rt_panic.rs new file mode 100644 index 000000000..24a0b20b4 --- /dev/null +++ b/ceno_rt/examples/ceno_rt_panic.rs @@ -0,0 +1,10 @@ +#![no_main] +#![no_std] + +#[allow(unused_imports)] +use ceno_rt; + +#[no_mangle] +fn main() { + panic!("This is a panic message!"); +} From c6b9c6f0c8ede7a5785e01f33ccb2945e87d6cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 2 Sep 2024 18:01:42 +0200 Subject: [PATCH 09/14] runtime: consolidate tests --- ceno_emul/src/vm_state.rs | 14 ++++++++ ceno_emul/tests/data/ceno_rt_mem | Bin 9520 -> 10748 bytes ceno_emul/tests/test_elf.rs | 60 +++++-------------------------- ceno_rt/examples/ceno_rt_mem.rs | 48 +++++++++++++++++-------- ceno_rt/src/lib.rs | 18 +++++----- 5 files changed, 66 insertions(+), 74 deletions(-) diff --git a/ceno_emul/src/vm_state.rs b/ceno_emul/src/vm_state.rs index 92d0b33a4..f677a29ae 100644 --- a/ceno_emul/src/vm_state.rs +++ b/ceno_emul/src/vm_state.rs @@ -6,6 +6,7 @@ use crate::{ platform::Platform, rv32im::{DecodedInstruction, Emulator, Instruction, TrapCause}, tracer::{Change, StepRecord, Tracer}, + Program, }; use anyhow::{anyhow, Result}; use std::iter::from_fn; @@ -35,6 +36,19 @@ impl VMState { } } + pub fn new_from_elf(platform: Platform, elf: &[u8]) -> Result { + let mut state = Self::new(platform); + let program = Program::load_elf(elf, u32::MAX).unwrap(); + for (addr, word) in program.image.iter() { + let addr = ByteAddr(*addr).waddr(); + state.init_memory(addr, *word); + } + if program.entry != state.platform.pc_start() { + return Err(anyhow!("Invalid entrypoint {:x}", program.entry)); + } + Ok(state) + } + pub fn succeeded(&self) -> bool { self.succeeded } diff --git a/ceno_emul/tests/data/ceno_rt_mem b/ceno_emul/tests/data/ceno_rt_mem index 6cc8681c2421c6f2b174e939540d6efde6daf269..6d266c4447dce7a75dad36b3c837141cac7166dd 100755 GIT binary patch literal 10748 zcmeHNU5Fc16h4#PiK|^|+AMZO!D+KCRl6bclVl@^E$-F|i?*Ur^NVs1Rs2`52YfAThKS3eezAj7eR_vL9y{WcP87}bXye!U*^KO_kQO) zKX=YeCOdcMt%b#dk|fcvrRZmhkYYeNzc*wIQb?w~w1w^u`Havj=#?HESY%c~&;Gq_ zLdTr)3oy$#WXK~Nw;sy`I+iqq`6J&4Ri2JE!9zJl)H$r&W_M!B!tptG(vk`!6-X+O zR3NE9Qh}rbNd=M$Bo#<1kW?V4KvIE?6}YgCj`dfh-VTW_W~G%uc3h%1p5C*PG|0*; z)R(W*1-Sgs+dsv++@5U_rc|V#IICPIaMBJunAd4+67ySSnR;h;FHkmpF_qhOg`D@k zO7-PJI=Aa8jX|H=b%~s*t0~x~>OG>|j70EJ`m#Kol@GvgCCan*+ih-Pgri@1O<8%r zl-qNO&hEZW&Z?a11Hr;ZqD;4rXNG5Vasb{{h<|UH8nrUM>vB28e`@z zJliBKsX$VJqyk9=k_sdhNGgz2AgMr7fusUS1^%B3@H42t+zlM%gzI;`c1uz7R1yBl{z;TsQed>Qs}{1Nap>^IaW=Kop5 z$K&mb@n3+C<1c{^gOAsbaoj(SZ$|ca<5_=f#Gm&kZhtTQJL2Dq=QyWE;`L&90RMCA zI7Kqfe9(HL$Np>yaR~AGj3(=N+u!h`_B;Ni{iy$cjh`G@k2-?OBlu}xV|@{6dUgY! zrsqfSizE1z5&ZfHwnlJ$1HLh60szE%P~r-WmU>*wP>hz zZPuwT=L?iy>^Q#LFqgan&79hxP|1Al1;c6kZlzy{g(GfX2C1HFg1N85bLv zToSYEIstxH*xAwM?5bYW^jfi|TeXEv{A!-vZds1wnZD~R`(2k;?YcF~nsdr-vFy|e zoB63CUwb*|EC=u?T6$U6tWwFX8oJ?>#4`0w(%retqj0aFIJH{Y)(Zu_;#8`_-QZdk z#gCUVYq=&qDGk@`29_U$R#**ei=sz%%*&!jILt*nO;~Coo+jdHIFl z#~_@C?fCmXpcEfl_0ONM=!tPou!O-q62Uq>6h2mVczhEV3kq}avG>H_N6r>6b6%`dl6h%sv6(UUp!*{N2@D2?X)jiUAeCM2def@d7 z(#)P8E_S}sG>vjuq8F4Q;UM|AnpPYVRHCzVfcB;JvdjxuaSMi3hZ6E~ywP*=9Q906MO~t59(CWU(PFcj?ypsue5!8mHs@TzpmWY zX+0r&s8dqC_dv!KO`BKG9Hwe*3u{cO;EVb;ehj#a>;IiR`akvOURCrGZ7&f;$m1GH z6a$I@#eiZ!F`yVw3@8Q^1BwB~fMP%~pcwc_41}{N_QggJMuW*%SVpUDnDy~sy&n%Z z#o1tOrQ7cLYqqdW+o_vQ-F7}F>I}x$gYXN{>0A)jieVWJofkN8IEefW(vBf4mGy9= zVGr!7<&NyBiPU>i{9k^FcILITL!?flY|&fXL}5SX4f6VV1S`M?;g_<>fD?uK2>BxB zb==QBpCl;a5ZNNWA^Rvv=MFHH0J%_De}4i0C_5-g-5&Z00nbpRQlg*pQ2G)*T)@9A z;NKVU9}D=&0)Dy+@62~W@cV36VrTvwnE4U#M@XZ8;Q9W*fyDp8Lj;>L>aRH1pWa^? zj33fCL66v!vHxelCCcmI%ZP)UF&RBC_Q>c)5gGnuGz!MZ$&ZTR#c??3&f);2Xmb>M zUHE0~gDgRmwG{`IsKpdWZsegGdR{K#0dSsTIgI6S+P3!a~peX!hzpX*qXTj(oF z-uDTTcqe&ghb1AA&gn=5llL7#%==~UG;!Z0;9R)IdJ>Ag-rf?=0i8lF7w%!HL$k9< uJQPI#qR40Y6w24-BlF*k>}-gLuAL_OT1KhK_xlpC*moa&)x3yW9=-u97}{(A diff --git a/ceno_emul/tests/test_elf.rs b/ceno_emul/tests/test_elf.rs index 9e0f516d3..d813ffc55 100644 --- a/ceno_emul/tests/test_elf.rs +++ b/ceno_emul/tests/test_elf.rs @@ -1,76 +1,32 @@ use anyhow::Result; -use ceno_emul::{ByteAddr, Program, StepRecord, VMState, CENO_PLATFORM}; +use ceno_emul::{ByteAddr, StepRecord, VMState, CENO_PLATFORM}; #[test] fn test_ceno_rt_mini() -> Result<()> { - let mut state = VMState::new(CENO_PLATFORM); - - // Load an ELF program in memory. let program_elf = include_bytes!("./data/ceno_rt_mini"); - let program = Program::load_elf(program_elf, u32::MAX)?; - for (addr, word) in program.image.iter() { - let addr = ByteAddr(*addr).waddr(); - state.init_memory(addr, *word); - } - assert_eq!(program.entry, CENO_PLATFORM.pc_start()); - + 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 mut state = VMState::new(CENO_PLATFORM); - - // Load an ELF program in memory. let program_elf = include_bytes!("./data/ceno_rt_panic"); - let program = Program::load_elf(program_elf, u32::MAX)?; - for (addr, word) in program.image.iter() { - let addr = ByteAddr(*addr).waddr(); - state.init_memory(addr, *word); - } - assert_eq!(program.entry, CENO_PLATFORM.pc_start()); - + let mut state = VMState::new_from_elf(CENO_PLATFORM, program_elf)?; let res = run(&mut state); - assert!(matches!(res, Err(e) if e.to_string().contains("EcallError"))); + assert!(matches!(res, Err(e) if e.to_string().contains("EcallError"))); Ok(()) } #[test] fn test_ceno_rt_mem() -> Result<()> { - let mut state = VMState::new(CENO_PLATFORM); - - // Load an ELF program in memory. let program_elf = include_bytes!("./data/ceno_rt_mem"); - let program = Program::load_elf(program_elf, u32::MAX)?; - for (addr, word) in program.image.iter() { - let addr = ByteAddr(*addr).waddr(); - state.init_memory(addr, *word); - } - assert_eq!(program.entry, CENO_PLATFORM.pc_start()); - - let mut prev_step = StepRecord::default(); - for step in state.iter_until_success() { - match step { - Ok(step) => { - // println!("{:?}", step); - prev_step = step; - } - Err(e) => { - println!("pc = {:?}", prev_step.pc().after); - return Err(e); - } - } - } - - for i in 0..4 { - let addr = ByteAddr(CENO_PLATFORM.ram_start()).waddr() + i as u32; - let value = state.peek_memory(addr); - println!("{:?} = 0x{:08x}", addr, value); - } + let mut state = VMState::new_from_elf(CENO_PLATFORM, program_elf)?; + let _steps = run(&mut state)?; + let value = state.peek_memory(ByteAddr(CENO_PLATFORM.ram_start()).waddr()); + assert_eq!(value, 6765, "Expected Fibonacci 20, got {}", value); Ok(()) } diff --git a/ceno_rt/examples/ceno_rt_mem.rs b/ceno_rt/examples/ceno_rt_mem.rs index 746bdae5e..382ce78c8 100644 --- a/ceno_rt/examples/ceno_rt_mem.rs +++ b/ceno_rt/examples/ceno_rt_mem.rs @@ -1,34 +1,54 @@ #![no_main] #![no_std] -use core::ptr::addr_of_mut; + +// Use volatile functions to prevent compiler optimizations. +use core::ptr::{read_volatile, write_volatile}; #[allow(unused_imports)] use ceno_rt; +const OUTPUT_ADDRESS: u32 = 0x8000_0000; #[no_mangle] +#[inline(never)] fn main() { - let y = my_recurse(3, 0); - output(y); + test_data_section(); + + let out = fibonacci_recurse(20, 0, 1); + test_output(out); +} + +/// Test the .data section is loaded and read/write works. +#[inline(never)] +fn test_data_section() { + // Use X[1] to be sure it is not the same as *OUTPUT_ADDRESS. + static mut X: [u32; 2] = [0, 42]; + + unsafe { + assert_eq!(read_volatile(&X[1]), 42); + write_volatile(&mut X[1], 99); + assert_eq!(read_volatile(&X[1]), 99); + } } // A sufficiently complicated function to test the stack. #[inline(never)] -#[no_mangle] -fn my_recurse(x: u32, y: u32) -> u32 { - if x == 0 { - y +fn fibonacci_recurse(count: u32, a: u32, b: u32) -> u32 { + let count = black_box(count); + if count == 0 { + a } else { - my_recurse(x - 1, y * 3 + 5) + fibonacci_recurse(count - 1, b, a + b) } } -// A global variable to test writing to memory. -static mut OUTPUT: u32 = 0; - +// Store the output to a specific memory location so the emulator tests can find it. #[inline(never)] -fn output(out: u32) { +fn test_output(out: u32) { unsafe { - // Volatile write to prevent the compiler from optimizing this away. - core::ptr::write_volatile(addr_of_mut!(OUTPUT), out); + write_volatile(OUTPUT_ADDRESS as *mut u32, out); } } + +fn black_box(x: T) -> T { + unsafe { read_volatile(&x) } +} diff --git a/ceno_rt/src/lib.rs b/ceno_rt/src/lib.rs index 9d3eeada7..e7ef45ebb 100644 --- a/ceno_rt/src/lib.rs +++ b/ceno_rt/src/lib.rs @@ -1,14 +1,16 @@ #![no_main] #![no_std] -use core::{ - arch::{asm, global_asm}, - panic::PanicInfo, -}; +use core::arch::{asm, global_asm}; -#[panic_handler] -#[inline(never)] -pub fn panic_handler(_panic: &PanicInfo<'_>) -> ! { - halt(1) +#[cfg(not(test))] +mod panic_handler { + use core::panic::PanicInfo; + + #[panic_handler] + #[inline(never)] + fn panic_handler(_panic: &PanicInfo<'_>) -> ! { + super::halt(1) + } } pub fn halt(exit_code: u32) -> ! { From c95e1a004b48db9e4001dcec36a5ff181757d638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 2 Sep 2024 18:34:26 +0200 Subject: [PATCH 10/14] runtime: clippy --- ceno_rt/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ceno_rt/src/lib.rs b/ceno_rt/src/lib.rs index e7ef45ebb..0ca1c4032 100644 --- a/ceno_rt/src/lib.rs +++ b/ceno_rt/src/lib.rs @@ -24,6 +24,7 @@ pub fn halt(exit_code: u32) -> ! { ); riscv::asm::ecall(); } + #[allow(clippy::empty_loop)] loop {} } @@ -49,8 +50,9 @@ _start: ", ); +/// _start_rust is called by the assembly entry point and it calls the Rust main(). #[no_mangle] -pub unsafe extern "C" fn _start_rust() -> ! { +unsafe extern "C" fn _start_rust() -> ! { main(); halt(0) } From 5084cedee80c55353ca34601dfb8de9987059961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 2 Sep 2024 20:11:09 +0200 Subject: [PATCH 11/14] runtime-io: experimental output mechanism --- ceno_emul/tests/data/ceno_rt_io | Bin 0 -> 9412 bytes ceno_emul/tests/test_elf.rs | 46 ++++++++++++++++++++++++++++++ ceno_rt/examples/ceno_rt_io.rs | 13 +++++++++ ceno_rt/src/io.rs | 48 ++++++++++++++++++++++++++++++++ ceno_rt/src/lib.rs | 5 +++- 5 files changed, 111 insertions(+), 1 deletion(-) create mode 100755 ceno_emul/tests/data/ceno_rt_io create mode 100644 ceno_rt/examples/ceno_rt_io.rs create mode 100644 ceno_rt/src/io.rs diff --git a/ceno_emul/tests/data/ceno_rt_io b/ceno_emul/tests/data/ceno_rt_io new file mode 100755 index 0000000000000000000000000000000000000000..0d08b2352c47609c94ddd1812732c3bcc67a19d1 GIT binary patch literal 9412 zcmeHNF^t<}6n@SnX^VuE)@rHLOs>6Xr&5DUwxqHFp+J85%ihuis|^xpq{?>+y2 z{?GQ}{GTs1mK8;ziRI`KjhUz-`Kgvwsw5~+C+Ps~%j%^xFA&8x0(${Y$fwr}CX}$} z5!j1xu7CJ=kP1iz zqyka_slXl-xPFi>Zd_H8R}|V*m8&B)uTVhA0jogCm#-Db+S>R@Efz=GFGJF9z_*5E zee89R`$GE-c<_c$i}xREe=rV_AN;4?1U~T+sm0|Z+Mh#=w-s#@xvg)njpxb%f0yDL zL&o2ycx#wbi<^XbbL!Via~s8N(l#IDG%=)BLZm9nNIP+mu47;SBxfG0_~4)x3bc_U zijWKTuwU6C6_5%@1*8H}0jYpgKq?>=kP1izqykcb|EU7^!u2S2#lQf$5m1Ycu?Z8UEu8|9OW0I>Ue8 z#dk)#DD=6tE3q@W1J3*9bq|q3cQNw*=EwX#MwsAGLj5lW`?LL(u)l}QiSBVIVg7$H z;+#d2@i`(r_AbYyuR5_qdNYbhcY|)%>mirkYTb$B&~L8C9!k-bZtOHMwxc-AP&Sh5 z^#VJLZ9l-{)pVQg+JalF)K8m^TXtLPdWG~x-wnNv-S#Uqe|cBJ;DkZ;CugnkJfru literal 0 HcmV?d00001 diff --git a/ceno_emul/tests/test_elf.rs b/ceno_emul/tests/test_elf.rs index d813ffc55..10cfeee58 100644 --- a/ceno_emul/tests/test_elf.rs +++ b/ceno_emul/tests/test_elf.rs @@ -30,6 +30,52 @@ fn test_ceno_rt_mem() -> Result<()> { Ok(()) } +#[test] +fn test_ceno_rt_io() -> Result<()> { + let program_elf = include_bytes!("./data/ceno_rt_io"); + let mut state = VMState::new_from_elf(CENO_PLATFORM, program_elf)?; + 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> { state.iter_until_success().collect() } + +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_rt/examples/ceno_rt_io.rs b/ceno_rt/examples/ceno_rt_io.rs new file mode 100644 index 000000000..e5de07a6b --- /dev/null +++ b/ceno_rt/examples/ceno_rt_io.rs @@ -0,0 +1,13 @@ +#![no_main] +#![no_std] + +#[allow(unused_imports)] +use ceno_rt; +use ceno_rt::write_info; + +#[no_mangle] +#[inline(never)] +fn main() { + write_info("πŸ“œπŸ“œπŸ“œ Hello, World!\n".as_bytes()); + write_info("🌏🌍🌎\n".as_bytes()); +} diff --git a/ceno_rt/src/io.rs b/ceno_rt/src/io.rs new file mode 100644 index 000000000..924dce6d0 --- /dev/null +++ b/ceno_rt/src/io.rs @@ -0,0 +1,48 @@ +use core::{ptr::write_volatile, slice}; + +const WORD_SIZE: usize = 4; + +const INFO_OUT_ADDR: u32 = 0xC000_0000; + +static mut INFO_OUT_CURSOR: *mut u32 = INFO_OUT_ADDR as *mut u32; + +pub fn write_info_u32(msg: &[u32]) { + let byte_len = msg.len() * WORD_SIZE; + write_info_u32_and_odd(msg, None, byte_len); +} + +pub fn write_info(msg: &[u8]) { + let byte_len = msg.len(); + let word_len_up = byte_len.div_ceil(WORD_SIZE); + let bytes_to_erase = word_len_up * WORD_SIZE - byte_len; + + let msg_words = unsafe { + // SAFETY: We only support aligned u32 reads. We do read beyond the slice, but reading the last odd bytes means reading the full word and masking the high bytes. + slice::from_raw_parts(msg.as_ptr() as *const u32, word_len_up) + }; + + if bytes_to_erase == 0 { + write_info_u32_and_odd(msg_words, None, byte_len); + } else { + // Truncate the last word to meet the actual length of the message. + let odd_word = msg_words[word_len_up - 1]; + let odd_word = (odd_word << (bytes_to_erase * 8)) >> (bytes_to_erase * 8); + write_info_u32_and_odd(&msg_words[..word_len_up - 1], Some(odd_word), byte_len); + }; +} + +fn write_info_u32_and_odd(msg: &[u32], odd_word: Option, byte_len: usize) { + unsafe { + let mut cursor = INFO_OUT_CURSOR.add(1); + for word in msg { + write_volatile(cursor, *word); + cursor = cursor.add(1); + } + if let Some(word) = odd_word { + write_volatile(cursor, word); + cursor = cursor.add(1); + } + write_volatile(INFO_OUT_CURSOR, byte_len as u32); + INFO_OUT_CURSOR = cursor; + } +} diff --git a/ceno_rt/src/lib.rs b/ceno_rt/src/lib.rs index 0ca1c4032..382a0624c 100644 --- a/ceno_rt/src/lib.rs +++ b/ceno_rt/src/lib.rs @@ -1,5 +1,8 @@ -#![no_main] #![no_std] + +mod io; +pub use io::{write_info, write_info_u32}; + use core::arch::{asm, global_asm}; #[cfg(not(test))] From c9c3324b19b129e0a2ec281ab1113ed29d0de3d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 2 Sep 2024 23:15:23 +0200 Subject: [PATCH 12/14] runtime-io: cleaner IOWriter and println --- ceno_emul/tests/data/ceno_rt_io | Bin 9412 -> 13380 bytes ceno_rt/examples/ceno_rt_io.rs | 8 +-- ceno_rt/src/io.rs | 98 +++++++++++++++++++------------- ceno_rt/src/lib.rs | 5 +- ceno_rt/src/params.rs | 3 + 5 files changed, 71 insertions(+), 43 deletions(-) create mode 100644 ceno_rt/src/params.rs diff --git a/ceno_emul/tests/data/ceno_rt_io b/ceno_emul/tests/data/ceno_rt_io index 0d08b2352c47609c94ddd1812732c3bcc67a19d1..d064ec255759464982081442cfa6d2473b7e5c68 100755 GIT binary patch literal 13380 zcmeHOTWB0r82)E(S!=9m2b)L>ow}i<(5y4F*Qj7BX&WK5R4cY3-R#cJW|wYum)%Kh zrQpVpnumI0#rC1Jf%ZY43|-<-Nbul!0?;qcswlN-EOEj07_Ulk7n%?$H9#X?}HQj$8xR}H~j-Jy3D8I z0ZvI@@lJUrx+{N#A<%Rgl(NTBX>tQU?uOA`FZghFm6uw9_qM^@^=pe$W0xV|xzb&E z?qZd{5rQetpP)2*UCM!ZPvuky_#5L~<(CUp=Q#&Vd0Tc=2*3Ckj3N#^>Er@ucrKvi zz$Xj<-?STu<42t^*MD^}57Ul{#|=uG$ALP!ltc5zI680~LMuo9ytfzT(pPaDzXLxU z24$@W0>>|c^CLHo^#`aN-vE`d`*5rm7p)o$lmX_?R!*BH^s3oxe!K8=zL-sJSu=n3 z#H(m8qrI}mIG(}tkmsKO?ij~yY<9pV)VK}p)MB;Tjw$r4ZRs81#vMJ)UV_9&(M}_; zAEYom`Pji$z*fLkz*fLkz*fLkz*fLkz*fLkz*gXYtH5+|sH7>vuv#o-@;O2BN4orC zXD&04*0M*14mA}`bS0IPEXbl9>=c8Ya&Q|!UnX~0Ej}dl_4No+)GzsiutPu%ikXr! z4BQ6Hx?0@D;eecx3sP)QE{IsPELeZ9cfmr{XLtxk!ohvs&l74PJkTfWtQc zvWV%OgN>MS;D9$V{S5IvHH-=Efdxv*V;W;CzJQqOV}ii}pJGh)2@$?zWXeY3f-f*b zV={sVzBIFX>VdBrJjdYc4E~wHzcKjlC3vB_1fqU8B}-VSdMudqCGv!2{w$Wz|5iNt zpJ(b%T5#CXAJqDzI!0KiKHPveJx%}a|LD)wXYqcfy@L!sgxGpL6^2hEo~_>xP8ekP z&3K+g`qBA{!@)tx!khYA^`h}v@fq~DVwNZTp+)bN@W%|#;?Eg=rUAx#T%dl;w`*n@ z{0)P@V=&(10@XI_v-n4bXYoA4F;E;PUz!?XAZ z!yjva%M3rx;1dn-YYhKJ1AL0%SvKnov2;d+_9IIQB#1q;CsJ!d&g7$y@7c9Cw)d(1 zQY0OTBtLX@5)L0@tb?&$Nz!XcK|tjSnXFojC5AFtEt4zx z1EHWO#^cFIGAIUBNz%2DsPfVd{e9U?PK}|YSv6MD;zdpG{kv9m}X+^ZN|>-Fy}s9PyoK*j8O2`tQpYEHeZ7PVDIo?w%=_=X`3&=Rb|b$r_K*!; z;^bD87^!rWx~Yp4>G)ln0a*T-L%;0MuSGq;+*qxRdgC3qOeGKRL5}z6GDZKc-b6<+34ZKVz;wytD{Ct^7>WS+>zgCX%FbV{AhOwEX1Goq)9 z`CMjpY3rDF36uwMx8`%nOA^g8 oqBZ>0KJZ>FT;Kh_W9SIbDt4IYY)i>UZ`NtF_PI&S{r=C~Uk@gez5oCK diff --git a/ceno_rt/examples/ceno_rt_io.rs b/ceno_rt/examples/ceno_rt_io.rs index e5de07a6b..a25f613d4 100644 --- a/ceno_rt/examples/ceno_rt_io.rs +++ b/ceno_rt/examples/ceno_rt_io.rs @@ -3,11 +3,11 @@ #[allow(unused_imports)] use ceno_rt; -use ceno_rt::write_info; +use ceno_rt::println; +use core::fmt::Write; #[no_mangle] -#[inline(never)] fn main() { - write_info("πŸ“œπŸ“œπŸ“œ Hello, World!\n".as_bytes()); - write_info("🌏🌍🌎\n".as_bytes()); + println!("πŸ“œπŸ“œπŸ“œ Hello, World!"); + println!("🌏🌍🌎"); } diff --git a/ceno_rt/src/io.rs b/ceno_rt/src/io.rs index 924dce6d0..c653957f8 100644 --- a/ceno_rt/src/io.rs +++ b/ceno_rt/src/io.rs @@ -1,48 +1,70 @@ -use core::{ptr::write_volatile, slice}; +use crate::{INFO_OUT_ADDR, WORD_SIZE}; +use core::{cell::Cell, fmt, mem::size_of, ptr::write_volatile, slice}; -const WORD_SIZE: usize = 4; +static INFO_OUT: IOWriter = IOWriter::new(INFO_OUT_ADDR); -const INFO_OUT_ADDR: u32 = 0xC000_0000; - -static mut INFO_OUT_CURSOR: *mut u32 = INFO_OUT_ADDR as *mut u32; - -pub fn write_info_u32(msg: &[u32]) { - let byte_len = msg.len() * WORD_SIZE; - write_info_u32_and_odd(msg, None, byte_len); +pub fn info_out() -> &'static IOWriter { + &INFO_OUT } -pub fn write_info(msg: &[u8]) { - let byte_len = msg.len(); - let word_len_up = byte_len.div_ceil(WORD_SIZE); - let bytes_to_erase = word_len_up * WORD_SIZE - byte_len; - - let msg_words = unsafe { - // SAFETY: We only support aligned u32 reads. We do read beyond the slice, but reading the last odd bytes means reading the full word and masking the high bytes. - slice::from_raw_parts(msg.as_ptr() as *const u32, word_len_up) - }; - - if bytes_to_erase == 0 { - write_info_u32_and_odd(msg_words, None, byte_len); - } else { - // Truncate the last word to meet the actual length of the message. - let odd_word = msg_words[word_len_up - 1]; - let odd_word = (odd_word << (bytes_to_erase * 8)) >> (bytes_to_erase * 8); - write_info_u32_and_odd(&msg_words[..word_len_up - 1], Some(odd_word), byte_len); - }; +pub struct IOWriter { + cursor: Cell<*mut u32>, } -fn write_info_u32_and_odd(msg: &[u32], odd_word: Option, byte_len: usize) { - unsafe { - let mut cursor = INFO_OUT_CURSOR.add(1); - for word in msg { - write_volatile(cursor, *word); - cursor = cursor.add(1); +// Safety: Only single-threaded programs are supported. +// TODO: There may be a better way to handle this. +unsafe impl Sync for IOWriter {} + +impl IOWriter { + const fn new(addr: u32) -> Self { + assert!(addr % WORD_SIZE as u32 == 0); + IOWriter { + cursor: Cell::new(addr as *mut u32), } - if let Some(word) = odd_word { - write_volatile(cursor, word); - cursor = cursor.add(1); + } + + pub fn alloc(&self, count: usize) -> &mut [T] { + let byte_len = count * size_of::(); + let cursor = self.cursor.get(); + + // Write the length of the message at the current cursor. + unsafe { + write_volatile(cursor, byte_len as u32); } - write_volatile(INFO_OUT_CURSOR, byte_len as u32); - INFO_OUT_CURSOR = cursor; + + // Bump the cursor to the next word-aligned address. + self.cursor + .set(unsafe { cursor.add(1 + byte_len.div_ceil(WORD_SIZE)) }); + + // Return a slice of the allocated memory after the length word. + unsafe { slice::from_raw_parts_mut(cursor.add(1) as *mut T, count) } + } + + pub fn write(&self, msg: &[u8]) { + let buf = self.alloc(msg.len()); + buf.copy_from_slice(msg); + } +} + +impl fmt::Write for &IOWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write(s.as_bytes()); + Ok(()) + } +} + +mod macros { + #[macro_export] + macro_rules! print { + ($($arg:tt)*) => { + let _ = core::write!($crate::info_out(), $($arg)*); + }; + } + + #[macro_export] + macro_rules! println { + ($($arg:tt)*) => { + let _ = core::writeln!($crate::info_out(), $($arg)*); + }; } } diff --git a/ceno_rt/src/lib.rs b/ceno_rt/src/lib.rs index 382a0624c..ce79c7f7d 100644 --- a/ceno_rt/src/lib.rs +++ b/ceno_rt/src/lib.rs @@ -1,10 +1,13 @@ #![no_std] mod io; -pub use io::{write_info, write_info_u32}; +pub use io::info_out; use core::arch::{asm, global_asm}; +mod params; +pub use params::*; + #[cfg(not(test))] mod panic_handler { use core::panic::PanicInfo; diff --git a/ceno_rt/src/params.rs b/ceno_rt/src/params.rs new file mode 100644 index 000000000..b8a30b24c --- /dev/null +++ b/ceno_rt/src/params.rs @@ -0,0 +1,3 @@ +pub const WORD_SIZE: usize = 4; + +pub const INFO_OUT_ADDR: u32 = 0xC000_0000; \ No newline at end of file From e13c635e444a14b8265d922ff38d821f2d44eb71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 3 Sep 2024 09:51:37 +0200 Subject: [PATCH 13/14] runtime-io: simplify --- ceno_emul/tests/data/ceno_rt_io | Bin 13380 -> 13380 bytes ceno_rt/src/io.rs | 24 ++++++++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/ceno_emul/tests/data/ceno_rt_io b/ceno_emul/tests/data/ceno_rt_io index d064ec255759464982081442cfa6d2473b7e5c68..852a85037644dde084cdf6cdf5d90487c6485de3 100755 GIT binary patch delta 28 kcmX?-aU^3ypa6fX3$wDq5(Z(`3k=E&mN0Ej5O|^t0FsLdrvLx| delta 28 kcmX?-aU^3ypa4JX1qS5>OPGXPU6_>>mN0Bi5O|^t0G8tkrvLx| diff --git a/ceno_rt/src/io.rs b/ceno_rt/src/io.rs index c653957f8..aaf473ab2 100644 --- a/ceno_rt/src/io.rs +++ b/ceno_rt/src/io.rs @@ -25,30 +25,34 @@ impl IOWriter { pub fn alloc(&self, count: usize) -> &mut [T] { let byte_len = count * size_of::(); + let word_len = byte_len.div_ceil(WORD_SIZE); let cursor = self.cursor.get(); - // Write the length of the message at the current cursor. - unsafe { - write_volatile(cursor, byte_len as u32); - } - // Bump the cursor to the next word-aligned address. - self.cursor - .set(unsafe { cursor.add(1 + byte_len.div_ceil(WORD_SIZE)) }); + self.cursor.set(unsafe { cursor.add(word_len) }); - // Return a slice of the allocated memory after the length word. - unsafe { slice::from_raw_parts_mut(cursor.add(1) as *mut T, count) } + // Return a slice of the allocated memory. + unsafe { slice::from_raw_parts_mut(cursor as *mut T, count) } } pub fn write(&self, msg: &[u8]) { let buf = self.alloc(msg.len()); buf.copy_from_slice(msg); } + + pub fn write_frame(&self, msg: &[u8]) { + let word_len = msg.len().div_ceil(WORD_SIZE); + let words: &mut [u32] = self.alloc(1 + word_len); + words[0] = msg.len() as u32; + let bytes = + unsafe { slice::from_raw_parts_mut(words[1..].as_mut_ptr() as *mut u8, msg.len()) }; + bytes.copy_from_slice(msg); + } } impl fmt::Write for &IOWriter { fn write_str(&mut self, s: &str) -> fmt::Result { - self.write(s.as_bytes()); + self.write_frame(s.as_bytes()); Ok(()) } } From 8cc885024c3cacb05ac326dd13feedf90cbc2843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 4 Sep 2024 16:02:37 +0200 Subject: [PATCH 14/14] runtime: fix after merge --- ceno_emul/tests/test_elf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ceno_emul/tests/test_elf.rs b/ceno_emul/tests/test_elf.rs index d813ffc55..7b7b8e13d 100644 --- a/ceno_emul/tests/test_elf.rs +++ b/ceno_emul/tests/test_elf.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use ceno_emul::{ByteAddr, StepRecord, VMState, CENO_PLATFORM}; +use ceno_emul::{ByteAddr, EmuContext, StepRecord, VMState, CENO_PLATFORM}; #[test] fn test_ceno_rt_mini() -> Result<()> {