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/12] 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/12] 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/12] 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/12] 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/12] 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/12] 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/12] 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/12] 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/12] 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/12] 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 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 11/12] 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<()> { From d3d943fe4b39165bc74c5e3898d7b3ee8e864c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 4 Sep 2024 20:51:52 +0200 Subject: [PATCH 12/12] runtime-alloc: Provide a GlobalAllocator and a test with Vec --- ceno_emul/src/vm_state.rs | 6 ++-- ceno_emul/tests/data/README.md | 2 +- ceno_emul/tests/data/ceno_rt_alloc | Bin 0 -> 150432 bytes ceno_emul/tests/test_elf.rs | 29 ++++++++++++++-- ceno_rt/examples/ceno_rt_alloc.rs | 30 +++++++++++++++++ ceno_rt/src/allocator.rs | 51 +++++++++++++++++++++++++++++ ceno_rt/src/lib.rs | 4 ++- 7 files changed, 115 insertions(+), 7 deletions(-) create mode 100755 ceno_emul/tests/data/ceno_rt_alloc create mode 100644 ceno_rt/examples/ceno_rt_alloc.rs create mode 100644 ceno_rt/src/allocator.rs diff --git a/ceno_emul/src/vm_state.rs b/ceno_emul/src/vm_state.rs index b478fc5ff..778c77839 100644 --- a/ceno_emul/src/vm_state.rs +++ b/ceno_emul/src/vm_state.rs @@ -53,8 +53,8 @@ impl VMState { self.succeeded } - pub fn tracer(&mut self) -> &mut Tracer { - &mut self.tracer + pub fn tracer(&self) -> &Tracer { + &self.tracer } /// Set a word in memory without side-effects. @@ -75,7 +75,7 @@ impl VMState { fn step(&mut self, emu: &Emulator) -> Result { emu.step(self)?; - let step = self.tracer().advance(); + let step = self.tracer.advance(); if step.is_busy_loop() && !self.succeeded() { Err(anyhow!("Stuck in loop {}", "{}")) } else { diff --git a/ceno_emul/tests/data/README.md b/ceno_emul/tests/data/README.md index 746886507..f098e90a6 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,panic,mem} ../ceno_emul/tests/data/ +cp ../target/riscv32im-unknown-none-elf/release/examples/ceno_rt_{mini,panic,mem,alloc} ../ceno_emul/tests/data/ ``` \ No newline at end of file diff --git a/ceno_emul/tests/data/ceno_rt_alloc b/ceno_emul/tests/data/ceno_rt_alloc new file mode 100755 index 0000000000000000000000000000000000000000..f8783e7f9bbcbf2a1a8d55315c3691daf2a759e6 GIT binary patch literal 150432 zcmeI%eQX*@uAq-kaQBa z4awS2NLGjbCgJ;s#BCgpt=t=v)#%qIa`EF6tIOY#(Yx0q7x{*CBT-32Bhoz_lf(tO z$A;unIa(P$F6R`^m+gW;-HoBoH9P(B^!OQFe@NFK()EWDI`{Anol|tHxSiJ*ig%Xdp+>kb*c;%7SkJ)uN&LPk^fsKJ9ek3){X_K81)cRVQW zCDBNDhmL>Q7q#vkqH_!B+(J6Hkj^D+47#`J_$N*b#yk1`yUuhY6Ve~^{fN z-h7TdVb-wrkg5OFpZfK^>*(Au2G^@uPj8<8u;+Y0(|eXj_xp zB3^%UdU#aJcAB!?TSoSp`n!j{DrUT+x_?~rrtgjN_nSJ;kvD5Nu6sxG`Uuh+)AaMp zUfHxM+r1^}jj4xbyfeBVe~$jV_3OHiX?{%WZ8V>f=D)9b@8P4h9@B&k0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|00D=ELfzW^7l$a3$2q1s}0tg_000IagfB*sryrcrH<)yZpJWy*b z)tilECRHq_(pNU>vvckFTar6#Go`8Wv^$ecX4BdHmFfJI+5BroChLuxYOU8NCnt9& zGo@4}m6u&fomQ*9`Vm*wEL|X@P*?M*{Q(4F^rnTt2%?Xr5J#1tJ|8F|EV$RShB|p^oXvk%-Aj z(mC7TPkQm=`raQem``ZKV7%RQ6q4l5K>ag;{w7Uc$1@|D@ov@FA&)@b<@@Lzc zqtolJdXe?*_I8}H`YTxR8#E4{?+GivN8_OP+pPT48XMm+9XJ$n?{*rP2xXV`BR(3f3xy-{Ir#~ zjV{rjT-dV2lHCi#9%|NZp3UVDM<{QgYx#zBpZ^MmvT^EsO{ zN3{Qc@G!sS{OzSV-)DdLzcl^zXZQblJ?$d-4XpR6xdF$-)qHR zw&JgA9Q=LoEi3*nK88fV zU3V&%PM2n;GX=MJr8~QvN=s^T(QVb{D|7Xc-d=TZ8uxxU6Yg7UcGDIwbyII zTzbYW<Yw;K2 zbz1R9Xc?cpS?lzCrP*9C;rIp3z9=61 zRBg82P-|1&bXzhnW%H8J>tZ^U$#}0>?=|PW=Jnc{uQzHH@AOpkEVbI^da}#>mg8QV zg7;eVUUdlVSI3x6WxX-7Ud^mGM%Ei6oA$S~#ZL?+3+hVakUu#vSmh1EF zdSfY-8_%cH)#`L{I-kzhG8ykO&zR}oobr11I^KBhIumawZzOLZZyaxm%J2dQOQ*GI zZ?H^%spqr!tS@Z&KCX9zh<~h?(CmD3sybg;Y}Om?T5HTpxHt9E3$+Dz@fPp0EuJ^; zoMsDt4ZSSNIk!BODW%8D`tWk+(sam+P0non z{w~pD)8F9xr`9|B-O10Hy!oG#_BB}hUafbjmi0GN$81I21GABNtv9k&|Hm=o onkl|n_ud-xNaTZiLh=sp$baeg_f`#q^?t1NCi;`tT)*DG0k7z+8vp Result<()> { @@ -25,11 +25,36 @@ fn test_ceno_rt_mem() -> Result<()> { 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()); + let value = state.peek_memory(CENO_PLATFORM.ram_start().into()); assert_eq!(value, 6765, "Expected Fibonacci 20, got {}", value); Ok(()) } +#[test] +fn test_ceno_rt_alloc() -> Result<()> { + let program_elf = include_bytes!("./data/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(()) +} + fn run(state: &mut VMState) -> Result> { state.iter_until_success().collect() } diff --git a/ceno_rt/examples/ceno_rt_alloc.rs b/ceno_rt/examples/ceno_rt_alloc.rs new file mode 100644 index 000000000..9545a41ab --- /dev/null +++ b/ceno_rt/examples/ceno_rt_alloc.rs @@ -0,0 +1,30 @@ +#![no_main] +#![no_std] +use core::ptr::{addr_of, read_volatile}; + +#[allow(unused_imports)] +use ceno_rt; + +extern crate alloc; +use alloc::{vec, vec::Vec}; + +static mut OUTPUT: u32 = 0; + +#[no_mangle] +fn main() { + // Test writing to a global variable. + unsafe { + OUTPUT = 0xf00d; + black_box(addr_of!(OUTPUT)); + } + + // Test writing to the heap. + let mut v: Vec = vec![]; + v.push(0xbeef); + black_box(&v[0]); +} + +/// Prevent compiler optimizations. +fn black_box(x: *const T) -> T { + unsafe { read_volatile(x) } +} diff --git a/ceno_rt/src/allocator.rs b/ceno_rt/src/allocator.rs new file mode 100644 index 000000000..51c71cbd0 --- /dev/null +++ b/ceno_rt/src/allocator.rs @@ -0,0 +1,51 @@ +//! A bump allocator. +//! Based on https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html + +use core::{ + alloc::{GlobalAlloc, Layout}, + cell::UnsafeCell, + ptr::null_mut, +}; + +const ARENA_SIZE: usize = 128 * 1024; +const MAX_SUPPORTED_ALIGN: usize = 4096; +#[repr(C, align(4096))] // 4096 == MAX_SUPPORTED_ALIGN +struct SimpleAllocator { + arena: UnsafeCell<[u8; ARENA_SIZE]>, + remaining: UnsafeCell, // we allocate from the top, counting down +} + +#[global_allocator] +static ALLOCATOR: SimpleAllocator = SimpleAllocator { + arena: UnsafeCell::new([0; ARENA_SIZE]), + remaining: UnsafeCell::new(ARENA_SIZE), +}; + +unsafe impl Sync for SimpleAllocator {} + +unsafe impl GlobalAlloc for SimpleAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let size = layout.size(); + let align = layout.align(); + + // `Layout` contract forbids making a `Layout` with align=0, or align not power of 2. + // So we can safely use a mask to ensure alignment without worrying about UB. + let align_mask_to_round_down = !(align - 1); + + if align > MAX_SUPPORTED_ALIGN { + return null_mut(); + } + + let remaining = self.remaining.get(); + if size > *remaining { + return null_mut(); + } + *remaining -= size; + *remaining &= align_mask_to_round_down; + + self.arena.get().cast::().add(*remaining) + } + + /// Never deallocate. + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} +} diff --git a/ceno_rt/src/lib.rs b/ceno_rt/src/lib.rs index 0ca1c4032..7470b9b65 100644 --- a/ceno_rt/src/lib.rs +++ b/ceno_rt/src/lib.rs @@ -1,7 +1,9 @@ -#![no_main] #![no_std] + use core::arch::{asm, global_asm}; +mod allocator; + #[cfg(not(test))] mod panic_handler { use core::panic::PanicInfo;