diff --git a/Cargo.toml b/Cargo.toml index cc01e722..1e1bbd35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,8 @@ edition = "2021" #plonky2_util = { path = "../plonky2/util" } #plonky2_maybe_rayon = { path = "../plonky2/maybe_rayon" } +bincode = "1.3.3" + plonky2 = { git = "https://github.com/zkMIPS/plonky2.git", branch = "zkm_dev" } #starky = { git = "https://github.com/zkMIPS/plonky2.git", branch = "zkm_dev" } plonky2_util = { git = "https://github.com/zkMIPS/plonky2.git", branch = "zkm_dev" } diff --git a/examples/README.md b/examples/README.md index a0fcfb34..0752d4a9 100644 --- a/examples/README.md +++ b/examples/README.md @@ -38,4 +38,36 @@ BASEDIR=test-vectors RUST_LOG=info BLOCK_NO=13284491 SEG_FILE_DIR="/tmp/output" cargo run --release --example zkmips aggregate_proof_all ``` +- run bench + + - download/install toolchain for mips + + ``` + wget http://musl.cc/mips-linux-muslsf-cross.tgz + tar -zxvf mips-linux-muslsf-cross.tgz + ``` + + - modify ~/.cargo/config: + + ``` + [target.mips-unknown-linux-musl] + linker = /mips-linux-muslsf-gcc" + rustflags = ["--cfg", 'target_os="zkvm"',"-C", "target-feature=+crt-static", "-C", "link-arg=-g"] + ``` + + - build sha2 + + ``` + cd examples/sha2 + cargo build --target=mips-unknown-linux-musl + cd ../../ + ``` + + - run bench + + ``` + RUST_LOG=info ELF_PATH=examples/sha2/target/mips-unknown-linux-musl/debug/sha2-bench SEG_OUTPUT=/tmp/output cargo run --release --example zkmips bench + ``` + Basically, you can run the example on a 32G RAM machine, if you get OOM error, please read https://github.com/zkMIPS/zkm/issues/97. + diff --git a/examples/sha2/Cargo.lock b/examples/sha2/Cargo.lock new file mode 100644 index 00000000..10dd144c --- /dev/null +++ b/examples/sha2/Cargo.lock @@ -0,0 +1,350 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2-bench" +version = "0.1.0" +dependencies = [ + "sha2", + "zkm-runtime", +] + +[[package]] +name = "syn" +version = "2.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zkm-precompiles" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "getrandom", + "hex", + "num", + "rand", + "serde", +] + +[[package]] +name = "zkm-runtime" +version = "0.1.0" +dependencies = [ + "bincode", + "cfg-if", + "getrandom", + "lazy_static", + "libm", + "once_cell", + "rand", + "serde", + "sha2", + "zkm-precompiles", +] diff --git a/examples/sha2/Cargo.toml b/examples/sha2/Cargo.toml new file mode 100644 index 00000000..cc8c6751 --- /dev/null +++ b/examples/sha2/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +[package] +version = "0.1.0" +name = "sha2-bench" +edition = "2021" + +[dependencies] +zkm-runtime = { path = "../../runtime/entrypoint" } +sha2 = { version = "0.10.8", default-features = false } diff --git a/examples/sha2/rust-toolchain.toml b/examples/sha2/rust-toolchain.toml new file mode 100644 index 00000000..6650ae6e --- /dev/null +++ b/examples/sha2/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly-2023-03-06-x86_64-unknown-linux-gnu" +targets = ["mips-unknown-linux-musl"] +profile = "minimal" diff --git a/examples/sha2/src/main.rs b/examples/sha2/src/main.rs new file mode 100644 index 00000000..891a530f --- /dev/null +++ b/examples/sha2/src/main.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] + +use sha2::{Digest, Sha256}; +extern crate alloc; +use alloc::vec::Vec; + +zkm_runtime::entrypoint!(main); + +pub fn main() { + let input: Vec = zkm_runtime::io::read(); + + let mut hasher = Sha256::new(); + hasher.update(input); + let _result = hasher.finalize(); +} diff --git a/examples/zkmips.rs b/examples/zkmips.rs index 6ad4cee9..40f92e7e 100644 --- a/examples/zkmips.rs +++ b/examples/zkmips.rs @@ -75,6 +75,67 @@ fn split_elf_into_segs() { instrumented_state.dump_memory(); } +fn prove_sha2_bench() { + // 1. split ELF into segs + let elf_path = env::var("ELF_PATH").expect("ELF file is missing"); + let seg_path = env::var("SEG_OUTPUT").expect("Segment output path is missing"); + + let data = fs::read(elf_path).expect("could not read file"); + let file = + ElfBytes::::minimal_parse(data.as_slice()).expect("opening elf file failed"); + let (mut state, _) = State::load_elf(&file); + state.patch_elf(&file); + state.patch_stack(vec![]); + + // load input + let input = [5u8; 32]; + state.add_input_stream(&input.to_vec()); + + let mut instrumented_state: Box = + InstrumentedState::new(state, "".to_string()); + std::fs::create_dir_all(&seg_path).unwrap(); + let new_writer = |_: &str| -> Option { None }; + instrumented_state.split_segment(false, &seg_path, new_writer); + let new_writer = |name: &str| -> Option { File::create(name).ok() }; + loop { + if instrumented_state.state.exited { + break; + } + instrumented_state.step(); + } + instrumented_state.split_segment(true, &seg_path, new_writer); + log::info!("Split done {}", instrumented_state.state.step); + + let seg_file = format!("{seg_path}/{}", 0); + let seg_reader = BufReader::new(File::open(seg_file).unwrap()); + let kernel = segment_kernel( + "", + "", + "", + seg_reader, + instrumented_state.state.step as usize, + ); + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let allstark: AllStark = AllStark::default(); + let config = StarkConfig::standard_fast_config(); + let mut timing = TimingTree::new("prove", log::Level::Info); + let allproof: proof::AllProof = + prove(&allstark, &kernel, &config, &mut timing).unwrap(); + let mut count_bytes = 0; + for (row, proof) in allproof.stark_proofs.clone().iter().enumerate() { + let proof_str = serde_json::to_string(&proof.proof).unwrap(); + log::info!("row:{} proof bytes:{}", row, proof_str.len()); + count_bytes += proof_str.len(); + } + timing.filter(Duration::from_millis(100)).print(); + log::info!("total proof bytes:{}KB", count_bytes / 1024); + verify_proof(&allstark, allproof, &config).unwrap(); + log::info!("Prove done"); +} + fn prove_single_seg() { let basedir = env::var("BASEDIR").unwrap_or("/tmp/cannon".to_string()); let block = env::var("BLOCK_NO").unwrap_or("".to_string()); @@ -115,7 +176,7 @@ fn main() { let args: Vec = env::args().collect(); let helper = || { log::info!( - "Help: {} split | prove | aggregate_proof | aggregate_proof_all | prove_groth16", + "Help: {} split | prove | aggregate_proof | aggregate_proof_all | prove_groth16 | bench", args[0] ); std::process::exit(-1); @@ -129,6 +190,7 @@ fn main() { "aggregate_proof" => aggregate_proof().unwrap(), "aggregate_proof_all" => aggregate_proof_all().unwrap(), "prove_groth16" => prove_groth16(), + "bench" => prove_sha2_bench(), _ => helper(), }; } diff --git a/runtime/entrypoint/Cargo.toml b/runtime/entrypoint/Cargo.toml new file mode 100644 index 00000000..b7ae47ff --- /dev/null +++ b/runtime/entrypoint/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "zkm-runtime" +version = "0.1.0" +edition = "2021" + +[dependencies] +zkm-precompiles = { path = "../precompiles" } +bincode = "1.3.3" +cfg-if = "1.0.0" +getrandom = { version = "0.2.14", features = ["custom"] } +once_cell = "1.19.0" +rand = "0.8.5" +serde = { version = "1.0.201", features = ["derive"] } +libm = { version = "0.2.8", optional = true } +sha2 = { version = "0.10.8" } +lazy_static = "1.4.0" + +[features] +default = ["libm"] +libm = ["dep:libm"] diff --git a/runtime/entrypoint/src/heap.rs b/runtime/entrypoint/src/heap.rs new file mode 100644 index 00000000..345c5276 --- /dev/null +++ b/runtime/entrypoint/src/heap.rs @@ -0,0 +1,18 @@ +//! Ported from Entrypoint for SP1 zkVM. + +use core::alloc::{GlobalAlloc, Layout}; + +use crate::syscalls::sys_alloc_aligned; + +/// A simple heap allocator. +/// +/// Allocates memory from left to right, without any deallocation. +pub struct SimpleAlloc; + +unsafe impl GlobalAlloc for SimpleAlloc { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + sys_alloc_aligned(layout.size(), layout.align()) + } + + unsafe fn dealloc(&self, _: *mut u8, _: Layout) {} +} diff --git a/runtime/entrypoint/src/lib.rs b/runtime/entrypoint/src/lib.rs new file mode 100644 index 00000000..a367a91c --- /dev/null +++ b/runtime/entrypoint/src/lib.rs @@ -0,0 +1,77 @@ +//! Ported from Entrypoint for SP1 zkVM. + +#![feature(asm_experimental_arch)] +pub mod heap; +pub mod syscalls; +pub mod io { + pub use zkm_precompiles::io::*; +} +pub mod precompiles { + pub use zkm_precompiles::*; +} + +extern crate alloc; + +#[macro_export] +macro_rules! entrypoint { + ($path:path) => { + const ZKVM_ENTRY: fn() = $path; + + use $crate::heap::SimpleAlloc; + + #[global_allocator] + static HEAP: SimpleAlloc = SimpleAlloc; + + mod zkvm_generated_main { + + #[no_mangle] + fn start() { + super::ZKVM_ENTRY() + } + } + }; +} + +mod libm; + +/// The number of 32 bit words that the public values digest is composed of. +pub const PV_DIGEST_NUM_WORDS: usize = 8; +pub const POSEIDON_NUM_WORDS: usize = 8; + +#[cfg(target_os = "zkvm")] +mod zkvm { + use crate::syscalls::syscall_halt; + + use getrandom::{register_custom_getrandom, Error}; + use sha2::{Digest, Sha256}; + + pub static mut PUBLIC_VALUES_HASHER: Option = None; + + #[cfg(not(feature = "interface"))] + #[no_mangle] + fn main() { + unsafe { + PUBLIC_VALUES_HASHER = Some(Sha256::new()); + + extern "C" { + fn start(); + } + start() + } + + syscall_halt(0); + } + + core::arch::global_asm!(include_str!("memset.s")); + core::arch::global_asm!(include_str!("memcpy.s")); + + fn zkvm_getrandom(s: &mut [u8]) -> Result<(), Error> { + unsafe { + crate::syscalls::sys_rand(s.as_mut_ptr(), s.len()); + } + + Ok(()) + } + + register_custom_getrandom!(zkvm_getrandom); +} diff --git a/runtime/entrypoint/src/libm.rs b/runtime/entrypoint/src/libm.rs new file mode 100644 index 00000000..ceb7be4d --- /dev/null +++ b/runtime/entrypoint/src/libm.rs @@ -0,0 +1,561 @@ +//! Ported from Entrypoint for SP1 zkVM. +//! +#[no_mangle] +pub extern "C" fn acos(x: f64) -> f64 { + libm::acos(x) +} + +#[no_mangle] +pub extern "C" fn acosf(x: f32) -> f32 { + libm::acosf(x) +} + +#[no_mangle] +pub extern "C" fn acosh(x: f64) -> f64 { + libm::acosh(x) +} + +#[no_mangle] +pub extern "C" fn acoshf(x: f32) -> f32 { + libm::acoshf(x) +} + +#[no_mangle] +pub extern "C" fn asin(x: f64) -> f64 { + libm::asin(x) +} + +#[no_mangle] +pub extern "C" fn asinf(x: f32) -> f32 { + libm::asinf(x) +} + +#[no_mangle] +pub extern "C" fn asinh(x: f64) -> f64 { + libm::asinh(x) +} + +#[no_mangle] +pub extern "C" fn asinhf(x: f32) -> f32 { + libm::asinhf(x) +} + +#[no_mangle] +pub extern "C" fn atan(x: f64) -> f64 { + libm::atan(x) +} + +#[no_mangle] +pub extern "C" fn atan2(y: f64, x: f64) -> f64 { + libm::atan2(y, x) +} + +#[no_mangle] +pub extern "C" fn atan2f(y: f32, x: f32) -> f32 { + libm::atan2f(y, x) +} + +#[no_mangle] +pub extern "C" fn atanf(x: f32) -> f32 { + libm::atanf(x) +} + +#[no_mangle] +pub extern "C" fn atanh(x: f64) -> f64 { + libm::atanh(x) +} + +#[no_mangle] +pub extern "C" fn atanhf(x: f32) -> f32 { + libm::atanhf(x) +} + +#[no_mangle] +pub extern "C" fn cbrt(x: f64) -> f64 { + libm::cbrt(x) +} + +#[no_mangle] +pub extern "C" fn cbrtf(x: f32) -> f32 { + libm::cbrtf(x) +} + +#[no_mangle] +pub extern "C" fn ceil(x: f64) -> f64 { + libm::ceil(x) +} + +#[no_mangle] +pub extern "C" fn ceilf(x: f32) -> f32 { + libm::ceilf(x) +} + +#[no_mangle] +pub extern "C" fn copysign(x: f64, y: f64) -> f64 { + libm::copysign(x, y) +} + +#[no_mangle] +pub extern "C" fn copysignf(x: f32, y: f32) -> f32 { + libm::copysignf(x, y) +} + +#[no_mangle] +pub extern "C" fn cos(x: f64) -> f64 { + libm::cos(x) +} + +#[no_mangle] +pub extern "C" fn cosf(x: f32) -> f32 { + libm::cosf(x) +} + +#[no_mangle] +pub extern "C" fn cosh(x: f64) -> f64 { + libm::cosh(x) +} + +#[no_mangle] +pub extern "C" fn coshf(x: f32) -> f32 { + libm::coshf(x) +} + +#[no_mangle] +pub extern "C" fn erf(x: f64) -> f64 { + libm::erf(x) +} + +#[no_mangle] +pub extern "C" fn erfc(x: f64) -> f64 { + libm::erfc(x) +} + +#[no_mangle] +pub extern "C" fn erfcf(x: f32) -> f32 { + libm::erfcf(x) +} + +#[no_mangle] +pub extern "C" fn erff(x: f32) -> f32 { + libm::erff(x) +} + +#[no_mangle] +pub extern "C" fn exp(x: f64) -> f64 { + libm::exp(x) +} + +#[no_mangle] +pub extern "C" fn exp2(x: f64) -> f64 { + libm::exp2(x) +} + +#[no_mangle] +pub extern "C" fn exp2f(x: f32) -> f32 { + libm::exp2f(x) +} + +#[no_mangle] +pub extern "C" fn exp10(x: f64) -> f64 { + libm::exp10(x) +} + +#[no_mangle] +pub extern "C" fn exp10f(x: f32) -> f32 { + libm::exp10f(x) +} + +#[no_mangle] +pub extern "C" fn expf(x: f32) -> f32 { + libm::expf(x) +} + +#[no_mangle] +pub extern "C" fn expm1(x: f64) -> f64 { + libm::expm1(x) +} + +#[no_mangle] +pub extern "C" fn expm1f(x: f32) -> f32 { + libm::expm1f(x) +} + +#[no_mangle] +pub extern "C" fn fabs(x: f64) -> f64 { + libm::fabs(x) +} + +#[no_mangle] +pub extern "C" fn fabsf(x: f32) -> f32 { + libm::fabsf(x) +} + +#[no_mangle] +pub extern "C" fn fdim(x: f64, y: f64) -> f64 { + libm::fdim(x, y) +} + +#[no_mangle] +pub extern "C" fn fdimf(x: f32, y: f32) -> f32 { + libm::fdimf(x, y) +} + +#[no_mangle] +pub extern "C" fn floor(x: f64) -> f64 { + libm::floor(x) +} + +#[no_mangle] +pub extern "C" fn floorf(x: f32) -> f32 { + libm::floorf(x) +} + +#[no_mangle] +pub extern "C" fn fma(x: f64, y: f64, z: f64) -> f64 { + libm::fma(x, y, z) +} + +#[no_mangle] +pub extern "C" fn fmaf(x: f32, y: f32, z: f32) -> f32 { + libm::fmaf(x, y, z) +} + +#[no_mangle] +pub extern "C" fn fmax(x: f64, y: f64) -> f64 { + libm::fmax(x, y) +} + +#[no_mangle] +pub extern "C" fn fmaxf(x: f32, y: f32) -> f32 { + libm::fmaxf(x, y) +} + +#[no_mangle] +pub extern "C" fn fmin(x: f64, y: f64) -> f64 { + libm::fmin(x, y) +} + +#[no_mangle] +pub extern "C" fn fminf(x: f32, y: f32) -> f32 { + libm::fminf(x, y) +} + +#[no_mangle] +pub extern "C" fn fmod(x: f64, y: f64) -> f64 { + libm::fmod(x, y) +} + +#[no_mangle] +pub extern "C" fn fmodf(x: f32, y: f32) -> f32 { + libm::fmodf(x, y) +} + +#[no_mangle] +pub fn frexp(x: f64) -> (f64, i32) { + libm::frexp(x) +} + +#[no_mangle] +pub fn frexpf(x: f32) -> (f32, i32) { + libm::frexpf(x) +} + +#[no_mangle] +pub extern "C" fn hypot(x: f64, y: f64) -> f64 { + libm::hypot(x, y) +} + +#[no_mangle] +pub extern "C" fn hypotf(x: f32, y: f32) -> f32 { + libm::hypotf(x, y) +} + +#[no_mangle] +pub extern "C" fn ilogb(x: f64) -> i32 { + libm::ilogb(x) +} + +#[no_mangle] +pub extern "C" fn ilogbf(x: f32) -> i32 { + libm::ilogbf(x) +} + +#[no_mangle] +pub extern "C" fn j0(x: f64) -> f64 { + libm::j0(x) +} + +#[no_mangle] +pub extern "C" fn j0f(x: f32) -> f32 { + libm::j0f(x) +} + +#[no_mangle] +pub extern "C" fn j1(x: f64) -> f64 { + libm::j1(x) +} + +#[no_mangle] +pub extern "C" fn j1f(x: f32) -> f32 { + libm::j1f(x) +} + +#[no_mangle] +pub extern "C" fn jn(n: i32, x: f64) -> f64 { + libm::jn(n, x) +} + +#[no_mangle] +pub extern "C" fn jnf(n: i32, x: f32) -> f32 { + libm::jnf(n, x) +} + +#[no_mangle] +pub extern "C" fn ldexp(x: f64, n: i32) -> f64 { + libm::ldexp(x, n) +} + +#[no_mangle] +pub extern "C" fn ldexpf(x: f32, n: i32) -> f32 { + libm::ldexpf(x, n) +} + +#[no_mangle] +pub extern "C" fn lgamma(x: f64) -> f64 { + libm::lgamma(x) +} + +#[no_mangle] +pub fn lgamma_r(x: f64) -> (f64, i32) { + libm::lgamma_r(x) +} + +#[no_mangle] +pub fn lgammaf(x: f32) -> f32 { + libm::lgammaf(x) +} + +#[no_mangle] +pub fn lgammaf_r(x: f32) -> (f32, i32) { + libm::lgammaf_r(x) +} + +#[no_mangle] +pub extern "C" fn log(x: f64) -> f64 { + libm::log(x) +} + +#[no_mangle] +pub extern "C" fn log1p(x: f64) -> f64 { + libm::log1p(x) +} + +#[no_mangle] +pub extern "C" fn log1pf(x: f32) -> f32 { + libm::log1pf(x) +} + +#[no_mangle] +pub extern "C" fn log2(x: f64) -> f64 { + libm::log2(x) +} + +#[no_mangle] +pub extern "C" fn log2f(x: f32) -> f32 { + libm::log2f(x) +} + +#[no_mangle] +pub extern "C" fn log10(x: f64) -> f64 { + libm::log10(x) +} + +#[no_mangle] +pub extern "C" fn log10f(x: f32) -> f32 { + libm::log10f(x) +} + +#[no_mangle] +pub extern "C" fn logf(x: f32) -> f32 { + libm::logf(x) +} + +#[no_mangle] +pub fn modf(x: f64) -> (f64, f64) { + libm::modf(x) +} + +#[no_mangle] +pub fn modff(x: f32) -> (f32, f32) { + libm::modff(x) +} + +#[no_mangle] +pub extern "C" fn nextafter(x: f64, y: f64) -> f64 { + libm::nextafter(x, y) +} + +#[no_mangle] +pub extern "C" fn nextafterf(x: f32, y: f32) -> f32 { + libm::nextafterf(x, y) +} + +#[no_mangle] +pub extern "C" fn pow(x: f64, y: f64) -> f64 { + libm::pow(x, y) +} + +#[no_mangle] +pub extern "C" fn powf(x: f32, y: f32) -> f32 { + libm::powf(x, y) +} + +#[no_mangle] +pub extern "C" fn remainder(x: f64, y: f64) -> f64 { + libm::remainder(x, y) +} + +#[no_mangle] +pub extern "C" fn remainderf(x: f32, y: f32) -> f32 { + libm::remainderf(x, y) +} + +#[no_mangle] +pub fn remquo(x: f64, y: f64) -> (f64, i32) { + libm::remquo(x, y) +} + +#[no_mangle] +pub fn remquof(x: f32, y: f32) -> (f32, i32) { + libm::remquof(x, y) +} + +#[no_mangle] +pub extern "C" fn round(x: f64) -> f64 { + libm::round(x) +} + +#[no_mangle] +pub extern "C" fn roundf(x: f32) -> f32 { + libm::roundf(x) +} + +#[no_mangle] +pub extern "C" fn scalbn(x: f64, n: i32) -> f64 { + libm::scalbn(x, n) +} + +#[no_mangle] +pub extern "C" fn scalbnf(x: f32, n: i32) -> f32 { + libm::scalbnf(x, n) +} + +#[no_mangle] +pub extern "C" fn sin(x: f64) -> f64 { + libm::sin(x) +} + +#[no_mangle] +pub fn sincos(x: f64) -> (f64, f64) { + libm::sincos(x) +} + +#[no_mangle] +pub fn sincosf(x: f32) -> (f32, f32) { + libm::sincosf(x) +} + +#[no_mangle] +pub extern "C" fn sinf(x: f32) -> f32 { + libm::sinf(x) +} + +#[no_mangle] +pub extern "C" fn sinh(x: f64) -> f64 { + libm::sinh(x) +} + +#[no_mangle] +pub extern "C" fn sinhf(x: f32) -> f32 { + libm::sinhf(x) +} + +#[no_mangle] +pub extern "C" fn sqrt(x: f64) -> f64 { + libm::sqrt(x) +} + +#[no_mangle] +pub extern "C" fn sqrtf(x: f32) -> f32 { + libm::sqrtf(x) +} + +#[no_mangle] +pub extern "C" fn tan(x: f64) -> f64 { + libm::tan(x) +} + +#[no_mangle] +pub extern "C" fn tanf(x: f32) -> f32 { + libm::tanf(x) +} + +#[no_mangle] +pub extern "C" fn tanh(x: f64) -> f64 { + libm::tanh(x) +} + +#[no_mangle] +pub extern "C" fn tanhf(x: f32) -> f32 { + libm::tanhf(x) +} + +#[no_mangle] +pub extern "C" fn tgamma(x: f64) -> f64 { + libm::tgamma(x) +} + +#[no_mangle] +pub extern "C" fn tgammaf(x: f32) -> f32 { + libm::tgammaf(x) +} + +#[no_mangle] +pub extern "C" fn trunc(x: f64) -> f64 { + libm::trunc(x) +} + +#[no_mangle] +pub extern "C" fn truncf(x: f32) -> f32 { + libm::truncf(x) +} + +#[no_mangle] +pub extern "C" fn y0(x: f64) -> f64 { + libm::y0(x) +} + +#[no_mangle] +pub extern "C" fn y0f(x: f32) -> f32 { + libm::y0f(x) +} + +#[no_mangle] +pub extern "C" fn y1(x: f64) -> f64 { + libm::y1(x) +} + +#[no_mangle] +pub extern "C" fn y1f(x: f32) -> f32 { + libm::y1f(x) +} + +#[no_mangle] +pub extern "C" fn yn(n: i32, x: f64) -> f64 { + libm::yn(n, x) +} + +#[no_mangle] +pub extern "C" fn ynf(n: i32, x: f32) -> f32 { + libm::ynf(n, x) +} diff --git a/runtime/entrypoint/src/memcpy.s b/runtime/entrypoint/src/memcpy.s new file mode 100644 index 00000000..f983a45a --- /dev/null +++ b/runtime/entrypoint/src/memcpy.s @@ -0,0 +1,335 @@ +// This is musl-libc commit 3b0a370020c4d5b80ff32a609e5322b7760f0dc4: +// +// src/string/memcpy.c +// +// This was compiled into assembly with: +// +// clang-10 -target mips -O3 -S memcpy.c -nostdlib -fno-builtin -funroll-loops +// +// and labels manually updated to not conflict. +// +// musl as a whole is licensed under the following standard MIT license: +// +// ---------------------------------------------------------------------- +// Copyright © 2005-2020 Rich Felker, et al. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ---------------------------------------------------------------------- +// +// Authors/contributors include: +// +// A. Wilcox +// Ada Worcester +// Alex Dowad +// Alex Suykov +// Alexander Monakov +// Andre McCurdy +// Andrew Kelley +// Anthony G. Basile +// Aric Belsito +// Arvid Picciani +// Bartosz Brachaczek +// Benjamin Peterson +// Bobby Bingham +// Boris Brezillon +// Brent Cook +// Chris Spiegel +// Clément Vasseur +// Daniel Micay +// Daniel Sabogal +// Daurnimator +// David Carlier +// David Edelsohn +// Denys Vlasenko +// Dmitry Ivanov +// Dmitry V. Levin +// Drew DeVault +// Emil Renner Berthing +// Fangrui Song +// Felix Fietkau +// Felix Janda +// Gianluca Anzolin +// Hauke Mehrtens +// He X +// Hiltjo Posthuma +// Isaac Dunham +// Jaydeep Patil +// Jens Gustedt +// Jeremy Huntwork +// Jo-Philipp Wich +// Joakim Sindholt +// John Spencer +// Julien Ramseier +// Justin Cormack +// Kaarle Ritvanen +// Khem Raj +// Kylie McClain +// Leah Neukirchen +// Luca Barbato +// Luka Perkov +// M Farkas-Dyck (Strake) +// Mahesh Bodapati +// Markus Wichmann +// Masanori Ogino +// Michael Clark +// Michael Forney +// Mikhail Kremnyov +// Natanael Copa +// Nicholas J. Kain +// orc +// Pascal Cuoq +// Patrick Oppenlander +// Petr Hosek +// Petr Skocik +// Pierre Carrier +// Reini Urban +// Rich Felker +// Richard Pennington +// Ryan Fairfax +// Samuel Holland +// Segev Finer +// Shiz +// sin +// Solar Designer +// Stefan Kristiansson +// Stefan O'Rear +// Szabolcs Nagy +// Timo Teräs +// Trutz Behn +// Valentin Ochs +// Will Dietz +// William Haddon +// William Pitcock +// +// Portions of this software are derived from third-party works licensed +// under terms compatible with the above MIT license: +// +// The TRE regular expression implementation (src/regex/reg* and +// src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed +// under a 2-clause BSD license (license text in the source files). The +// included version has been heavily modified by Rich Felker in 2012, in +// the interests of size, simplicity, and namespace cleanliness. +// +// Much of the math library code (src/math/* and src/complex/*) is +// Copyright © 1993,2004 Sun Microsystems or +// Copyright © 2003-2011 David Schultz or +// Copyright © 2003-2009 Steven G. Kargl or +// Copyright © 2003-2009 Bruce D. Evans or +// Copyright © 2008 Stephen L. Moshier or +// Copyright © 2017-2018 Arm Limited +// and labelled as such in comments in the individual source files. All +// have been licensed under extremely permissive terms. +// +// The ARM memcpy code (src/string/arm/memcpy.S) is Copyright © 2008 +// The Android Open Source Project and is licensed under a two-clause BSD +// license. It was taken from Bionic libc, used on Android. +// +// The AArch64 memcpy and memset code (src/string/aarch64/*) are +// Copyright © 1999-2019, Arm Limited. +// +// The implementation of DES for crypt (src/crypt/crypt_des.c) is +// Copyright © 1994 David Burren. It is licensed under a BSD license. +// +// The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was +// originally written by Solar Designer and placed into the public +// domain. The code also comes with a fallback permissive license for use +// in jurisdictions that may not recognize the public domain. +// +// The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011 +// Valentin Ochs and is licensed under an MIT-style license. +// +// The x86_64 port was written by Nicholas J. Kain and is licensed under +// the standard MIT terms. +// +// The mips and microblaze ports were originally written by Richard +// Pennington for use in the ellcc project. The original code was adapted +// by Rich Felker for build system and code conventions during upstream +// integration. It is licensed under the standard MIT terms. +// +// The mips64 port was contributed by Imagination Technologies and is +// licensed under the standard MIT terms. +// +// The powerpc port was also originally written by Richard Pennington, +// and later supplemented and integrated by John Spencer. It is licensed +// under the standard MIT terms. +// +// All other files which have no copyright comments are original works +// produced specifically for use as part of this library, written either +// by Rich Felker, the main author of the library, or by one or more +// contibutors listed above. Details on authorship of individual files +// can be found in the git version control history of the project. The +// omission of copyright and license comments in each file is in the +// interest of source tree size. +// +// In addition, permission is hereby granted for all public header files +// (include/* and arch/* /bits/* ) and crt files intended to be linked into +// applications (crt/*, ldso/dlstart.c, and arch/* /crt_arch.h) to omit +// the copyright notice and permission notice otherwise required by the +// license, and to use these files without any requirement of +// attribution. These files include substantial contributions from: +// +// Bobby Bingham +// John Spencer +// Nicholas J. Kain +// Rich Felker +// Richard Pennington +// Stefan Kristiansson +// Szabolcs Nagy +// +// all of whom have explicitly granted such permission. +// +// This file previously contained text expressing a belief that most of +// the files covered by the above exception were sufficiently trivial not +// to be subject to copyright, resulting in confusion over whether it +// negated the permissions granted in the license. In the spirit of +// permissive licensing, and of not having licensing issues being an +// obstacle to adoption, that text has been removed. + .text + .file "memcpy.c" + .globl memccpy # -- Begin function memccpy + .p2align 2 + .type memccpy,@function + .set nomicromips + .set nomips16 + .ent memccpy +memccpy: # @memccpy + .frame $fp,8,$ra + .mask 0xc0000000,-4 + .fmask 0x00000000,0 + .set noreorder + .set nomacro + .set noat +# %bb.0: + addiu $sp, $sp, -8 + sw $ra, 4($sp) # 4-byte Folded Spill + sw $fp, 0($sp) # 4-byte Folded Spill + move $fp, $sp + xor $1, $5, $4 + andi $1, $1, 3 + beqz $1, $BBmemcpy0_7 + andi $3, $6, 255 +$BBmemcpy0_1: + beqz $7, $BBmemcpy0_5 + nop +# %bb.2: + addiu $2, $4, 1 +$BBmemcpy0_3: # =>This Inner Loop Header: Depth=1 + lbu $1, 0($5) + beq $1, $3, $BBmemcpy0_6 + sb $1, -1($2) +# %bb.4: # in Loop: Header=BBmemcpy0_3 Depth=1 + addiu $2, $2, 1 + addiu $7, $7, -1 + bnez $7, $BBmemcpy0_3 + addiu $5, $5, 1 +$BBmemcpy0_5: + addiu $2, $zero, 0 +$BBmemcpy0_6: + move $sp, $fp + lw $fp, 0($sp) # 4-byte Folded Reload + lw $ra, 4($sp) # 4-byte Folded Reload + jr $ra + addiu $sp, $sp, 8 +$BBmemcpy0_7: + andi $6, $5, 3 + beqz $7, $BBmemcpy0_14 + sltu $2, $zero, $6 +# %bb.8: + beqz $6, $BBmemcpy0_14 + nop +# %bb.9: + addiu $2, $7, -1 + addiu $6, $zero, 0 +$BBmemcpy0_10: # =>This Inner Loop Header: Depth=1 + addu $9, $5, $6 + addu $8, $4, $6 + lbu $1, 0($9) + beq $1, $3, $BBmemcpy0_22 + sb $1, 0($8) +# %bb.11: # in Loop: Header=BBmemcpy0_10 Depth=1 + addiu $1, $9, 1 + addiu $8, $6, 1 + beq $2, $6, $BBmemcpy0_13 + andi $9, $1, 3 +# %bb.12: # in Loop: Header=BBmemcpy0_10 Depth=1 + bnez $9, $BBmemcpy0_10 + move $6, $8 +$BBmemcpy0_13: + sltu $2, $zero, $9 + subu $7, $7, $8 + addu $4, $4, $8 + addu $5, $5, $8 +$BBmemcpy0_14: + beqz $2, $BBmemcpy0_17 + nop +# %bb.15: + bnez $7, $BBmemcpy0_6 + addiu $2, $4, 1 +# %bb.16: + j $BBmemcpy0_5 + nop +$BBmemcpy0_17: + sltiu $1, $7, 4 + bnez $1, $BBmemcpy0_1 + nop +# %bb.18: + sll $1, $3, 8 + sll $2, $3, 16 + or $1, $1, $3 + or $1, $2, $1 + sll $2, $3, 24 + or $6, $2, $1 + lui $1, 65278 + andi $2, $7, 3 + ori $8, $1, 65279 + lui $1, 32896 + ori $9, $1, 32896 +$BBmemcpy0_19: # =>This Inner Loop Header: Depth=1 + lw $10, 0($5) + xor $1, $10, $6 + addu $11, $1, $8 + not $1, $1 + and $1, $1, $11 + and $1, $1, $9 + bnez $1, $BBmemcpy0_1 + nop +# %bb.20: # in Loop: Header=BBmemcpy0_19 Depth=1 + addiu $7, $7, -4 + sw $10, 0($4) + addiu $4, $4, 4 + sltiu $1, $7, 4 + beqz $1, $BBmemcpy0_19 + addiu $5, $5, 4 +# %bb.21: + j $BBmemcpy0_1 + move $7, $2 +$BBmemcpy0_22: + j $BBmemcpy0_6 + addiu $2, $8, 1 + .set at + .set macro + .set reorder + .end memccpy +$memcpy_func_end0: + .size memccpy, ($memcpy_func_end0)-memccpy + # -- End function + .ident "clang version 10.0.0-4ubuntu1 " + .section ".note.GNU-stack","",@progbits + .addrsig \ No newline at end of file diff --git a/runtime/entrypoint/src/memory.rs b/runtime/entrypoint/src/memory.rs new file mode 100644 index 00000000..b4c967ea --- /dev/null +++ b/runtime/entrypoint/src/memory.rs @@ -0,0 +1,51 @@ +// Copyright 2023 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. + +const SYSTEM_START: usize = 0x0C00_0000; + +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn sys_alloc_aligned(bytes: usize, align: usize) -> *mut u8 { + extern "C" { + // https://lld.llvm.org/ELF/linker_script.html#sections-command + static _end: u8; + } + + // Pointer to next heap address to use, or 0 if the heap has not yet been + // initialized. + static mut HEAP_POS: usize = 0; + + // SAFETY: Single threaded, so nothing else can touch this while we're working. + let mut heap_pos = unsafe { HEAP_POS }; + + if heap_pos == 0 { + heap_pos = unsafe { (&_end) as *const u8 as usize }; + } + + let offset = heap_pos & (align - 1); + if offset != 0 { + heap_pos += align - offset; + } + + let ptr = heap_pos as *mut u8; + heap_pos += bytes; + + // Check to make sure heap doesn't collide with SYSTEM memory. + if SYSTEM_START < heap_pos { + panic!(); + } + + unsafe { HEAP_POS = heap_pos }; + ptr +} diff --git a/runtime/entrypoint/src/memset.s b/runtime/entrypoint/src/memset.s new file mode 100644 index 00000000..a4840ec4 --- /dev/null +++ b/runtime/entrypoint/src/memset.s @@ -0,0 +1,316 @@ +// This is musl-libc memset commit 5613a1486e6a6fc3988be6561f41b07b2647d80f: +// +// src/string/memset.c +// +// This was compiled into assembly with: +// +// clang10 -target mips -O3 -S memset.c -nostdlib -fno-builtin -funroll-loops +// +// and labels manually updated to not conflict. +// +// musl as a whole is licensed under the following standard MIT license: +// +// ---------------------------------------------------------------------- +// Copyright © 2005-2020 Rich Felker, et al. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ---------------------------------------------------------------------- +// +// Authors/contributors include: +// +// A. Wilcox +// Ada Worcester +// Alex Dowad +// Alex Suykov +// Alexander Monakov +// Andre McCurdy +// Andrew Kelley +// Anthony G. Basile +// Aric Belsito +// Arvid Picciani +// Bartosz Brachaczek +// Benjamin Peterson +// Bobby Bingham +// Boris Brezillon +// Brent Cook +// Chris Spiegel +// Clément Vasseur +// Daniel Micay +// Daniel Sabogal +// Daurnimator +// David Carlier +// David Edelsohn +// Denys Vlasenko +// Dmitry Ivanov +// Dmitry V. Levin +// Drew DeVault +// Emil Renner Berthing +// Fangrui Song +// Felix Fietkau +// Felix Janda +// Gianluca Anzolin +// Hauke Mehrtens +// He X +// Hiltjo Posthuma +// Isaac Dunham +// Jaydeep Patil +// Jens Gustedt +// Jeremy Huntwork +// Jo-Philipp Wich +// Joakim Sindholt +// John Spencer +// Julien Ramseier +// Justin Cormack +// Kaarle Ritvanen +// Khem Raj +// Kylie McClain +// Leah Neukirchen +// Luca Barbato +// Luka Perkov +// M Farkas-Dyck (Strake) +// Mahesh Bodapati +// Markus Wichmann +// Masanori Ogino +// Michael Clark +// Michael Forney +// Mikhail Kremnyov +// Natanael Copa +// Nicholas J. Kain +// orc +// Pascal Cuoq +// Patrick Oppenlander +// Petr Hosek +// Petr Skocik +// Pierre Carrier +// Reini Urban +// Rich Felker +// Richard Pennington +// Ryan Fairfax +// Samuel Holland +// Segev Finer +// Shiz +// sin +// Solar Designer +// Stefan Kristiansson +// Stefan O'Rear +// Szabolcs Nagy +// Timo Teräs +// Trutz Behn +// Valentin Ochs +// Will Dietz +// William Haddon +// William Pitcock +// +// Portions of this software are derived from third-party works licensed +// under terms compatible with the above MIT license: +// +// The TRE regular expression implementation (src/regex/reg* and +// src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed +// under a 2-clause BSD license (license text in the source files). The +// included version has been heavily modified by Rich Felker in 2012, in +// the interests of size, simplicity, and namespace cleanliness. +// +// Much of the math library code (src/math/* and src/complex/*) is +// Copyright © 1993,2004 Sun Microsystems or +// Copyright © 2003-2011 David Schultz or +// Copyright © 2003-2009 Steven G. Kargl or +// Copyright © 2003-2009 Bruce D. Evans or +// Copyright © 2008 Stephen L. Moshier or +// Copyright © 2017-2018 Arm Limited +// and labelled as such in comments in the individual source files. All +// have been licensed under extremely permissive terms. +// +// The ARM memcpy code (src/string/arm/memcpy.S) is Copyright © 2008 +// The Android Open Source Project and is licensed under a two-clause BSD +// license. It was taken from Bionic libc, used on Android. +// +// The AArch64 memcpy and memset code (src/string/aarch64/*) are +// Copyright © 1999-2019, Arm Limited. +// +// The implementation of DES for crypt (src/crypt/crypt_des.c) is +// Copyright © 1994 David Burren. It is licensed under a BSD license. +// +// The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was +// originally written by Solar Designer and placed into the public +// domain. The code also comes with a fallback permissive license for use +// in jurisdictions that may not recognize the public domain. +// +// The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011 +// Valentin Ochs and is licensed under an MIT-style license. +// +// The x86_64 port was written by Nicholas J. Kain and is licensed under +// the standard MIT terms. +// +// The mips and microblaze ports were originally written by Richard +// Pennington for use in the ellcc project. The original code was adapted +// by Rich Felker for build system and code conventions during upstream +// integration. It is licensed under the standard MIT terms. +// +// The mips64 port was contributed by Imagination Technologies and is +// licensed under the standard MIT terms. +// +// The powerpc port was also originally written by Richard Pennington, +// and later supplemented and integrated by John Spencer. It is licensed +// under the standard MIT terms. +// +// All other files which have no copyright comments are original works +// produced specifically for use as part of this library, written either +// by Rich Felker, the main author of the library, or by one or more +// contibutors listed above. Details on authorship of individual files +// can be found in the git version control history of the project. The +// omission of copyright and license comments in each file is in the +// interest of source tree size. +// +// In addition, permission is hereby granted for all public header files +// (include/* and arch/* /bits/* ) and crt files intended to be linked into +// applications (crt/*, ldso/dlstart.c, and arch/* /crt_arch.h) to omit +// the copyright notice and permission notice otherwise required by the +// license, and to use these files without any requirement of +// attribution. These files include substantial contributions from: +// +// Bobby Bingham +// John Spencer +// Nicholas J. Kain +// Rich Felker +// Richard Pennington +// Stefan Kristiansson +// Szabolcs Nagy +// +// all of whom have explicitly granted such permission. +// +// This file previously contained text expressing a belief that most of +// the files covered by the above exception were sufficiently trivial not +// to be subject to copyright, resulting in confusion over whether it +// negated the permissions granted in the license. In the spirit of +// permissive licensing, and of not having licensing issues being an +// obstacle to adoption, that text has been removed. + .text + .file "memset.c" + .globl memset # -- Begin function memset + .p2align 2 + .type memset,@function + .set nomicromips + .set nomips16 + .ent memset +memset: # @memset + .frame $fp,8,$ra + .mask 0xc0000000,-4 + .fmask 0x00000000,0 + .set noreorder + .set nomacro + .set noat +# %bb.0: + addiu $sp, $sp, -8 + sw $ra, 4($sp) # 4-byte Folded Spill + sw $fp, 0($sp) # 4-byte Folded Spill + move $fp, $sp + beqz $6, $BBmemset0_9 + nop +# %bb.1: + addu $2, $6, $4 + sltiu $1, $6, 3 + sb $5, 0($4) + bnez $1, $BBmemset0_9 + sb $5, -1($2) +# %bb.2: + sltiu $1, $6, 7 + sb $5, 2($4) + sb $5, 1($4) + sb $5, -3($2) + bnez $1, $BBmemset0_9 + sb $5, -2($2) +# %bb.3: + sltiu $1, $6, 9 + sb $5, 3($4) + bnez $1, $BBmemset0_9 + sb $5, -4($2) +# %bb.4: + andi $2, $5, 255 + negu $1, $4 + sll $5, $2, 8 + sll $7, $2, 16 + andi $1, $1, 3 + or $5, $5, $2 + sll $2, $2, 24 + addu $3, $4, $1 + subu $1, $6, $1 + or $5, $7, $5 + or $2, $2, $5 + addiu $5, $zero, -4 + and $5, $1, $5 + sw $2, 0($3) + addu $6, $3, $5 + sltiu $1, $5, 9 + bnez $1, $BBmemset0_9 + sw $2, -4($6) +# %bb.5: + sltiu $1, $5, 25 + sw $2, 8($3) + sw $2, 4($3) + sw $2, -8($6) + bnez $1, $BBmemset0_9 + sw $2, -12($6) +# %bb.6: + andi $1, $3, 4 + sw $2, 24($3) + sw $2, 20($3) + sw $2, 16($3) + sw $2, 12($3) + sw $2, -16($6) + sw $2, -20($6) + sw $2, -24($6) + sw $2, -28($6) + ori $6, $1, 24 + subu $5, $5, $6 + sltiu $1, $5, 32 + bnez $1, $BBmemset0_9 + nop +# %bb.7: + addu $3, $3, $6 +$BBmemset0_8: # =>This Inner Loop Header: Depth=1 + addiu $5, $5, -32 + sw $2, 24($3) + sw $2, 16($3) + sw $2, 8($3) + sw $2, 0($3) + sw $2, 28($3) + sw $2, 20($3) + sw $2, 12($3) + sw $2, 4($3) + sltiu $1, $5, 32 + beqz $1, $BBmemset0_8 + addiu $3, $3, 32 +$BBmemset0_9: + move $2, $4 + move $sp, $fp + lw $fp, 0($sp) # 4-byte Folded Reload + lw $ra, 4($sp) # 4-byte Folded Reload + jr $ra + addiu $sp, $sp, 8 + .set at + .set macro + .set reorder + .end memset +$memset_func_end0: + .size memset, ($memset_func_end0)-memset + # -- End function + .ident "clang version 10.0.0-4ubuntu1 " + .section ".note.GNU-stack","",@progbits + .addrsig \ No newline at end of file diff --git a/runtime/entrypoint/src/syscalls/halt.rs b/runtime/entrypoint/src/syscalls/halt.rs new file mode 100644 index 00000000..83d791bb --- /dev/null +++ b/runtime/entrypoint/src/syscalls/halt.rs @@ -0,0 +1,26 @@ +//! Ported from Entrypoint for SP1 zkVM. + +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(target_os = "zkvm")] { + use core::arch::asm; + } +} + +/// Halts the program. +#[allow(unused_variables)] +pub extern "C" fn syscall_halt(exit_code: u8) -> ! { + #[cfg(target_os = "zkvm")] + unsafe { + asm!( + "syscall", + in("$2") crate::syscalls::HALT, + in("$4") exit_code + ); + unreachable!() + } + + #[cfg(not(target_os = "zkvm"))] + unreachable!() +} diff --git a/runtime/entrypoint/src/syscalls/io.rs b/runtime/entrypoint/src/syscalls/io.rs new file mode 100644 index 00000000..173074bb --- /dev/null +++ b/runtime/entrypoint/src/syscalls/io.rs @@ -0,0 +1,63 @@ +//! Ported from Entrypoint for SP1 zkVM. + +cfg_if::cfg_if! { + if #[cfg(target_os = "zkvm")] { + use core::arch::asm; + } +} + +/// Write data to the prover. +#[allow(unused_variables)] +#[no_mangle] +pub extern "C" fn syscall_write(fd: u32, write_buf: *const u8, nbytes: usize) { + cfg_if::cfg_if! { + if #[cfg(target_os = "zkvm")] { + unsafe { + asm!( + "syscall", + in("$2") crate::syscalls::WRITE, + in("$4") fd, + in("$5") write_buf, + in("$6") nbytes, + ); + } + } else { + unreachable!() + } + } +} + +#[allow(unused_variables)] +#[no_mangle] +pub extern "C" fn syscall_hint_len() -> usize { + #[cfg(target_os = "zkvm")] + unsafe { + let len; + asm!( + "syscall", + in("$2") crate::syscalls::HINT_LEN, + lateout("$2") len, + ); + len + } + + #[cfg(not(target_os = "zkvm"))] + unreachable!() +} + +#[allow(unused_variables)] +#[no_mangle] +pub extern "C" fn syscall_hint_read(ptr: *mut u8, len: usize) { + #[cfg(target_os = "zkvm")] + unsafe { + asm!( + "syscall", + in("$2") crate::syscalls::HINT_READ, + in("$4") ptr, + in("$5") len, + ); + } + + #[cfg(not(target_os = "zkvm"))] + unreachable!() +} diff --git a/runtime/entrypoint/src/syscalls/memory.rs b/runtime/entrypoint/src/syscalls/memory.rs new file mode 100644 index 00000000..b4c967ea --- /dev/null +++ b/runtime/entrypoint/src/syscalls/memory.rs @@ -0,0 +1,51 @@ +// Copyright 2023 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. + +const SYSTEM_START: usize = 0x0C00_0000; + +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn sys_alloc_aligned(bytes: usize, align: usize) -> *mut u8 { + extern "C" { + // https://lld.llvm.org/ELF/linker_script.html#sections-command + static _end: u8; + } + + // Pointer to next heap address to use, or 0 if the heap has not yet been + // initialized. + static mut HEAP_POS: usize = 0; + + // SAFETY: Single threaded, so nothing else can touch this while we're working. + let mut heap_pos = unsafe { HEAP_POS }; + + if heap_pos == 0 { + heap_pos = unsafe { (&_end) as *const u8 as usize }; + } + + let offset = heap_pos & (align - 1); + if offset != 0 { + heap_pos += align - offset; + } + + let ptr = heap_pos as *mut u8; + heap_pos += bytes; + + // Check to make sure heap doesn't collide with SYSTEM memory. + if SYSTEM_START < heap_pos { + panic!(); + } + + unsafe { HEAP_POS = heap_pos }; + ptr +} diff --git a/runtime/entrypoint/src/syscalls/mod.rs b/runtime/entrypoint/src/syscalls/mod.rs new file mode 100644 index 00000000..4e4020f7 --- /dev/null +++ b/runtime/entrypoint/src/syscalls/mod.rs @@ -0,0 +1,26 @@ +//! Ported from Entrypoint for SP1 zkVM. + +mod halt; +mod io; +mod memory; +mod sys; + +pub use halt::*; +pub use io::*; +pub use memory::*; +pub use sys::*; + +/// These codes MUST match the codes in `core/src/runtime/syscall.rs`. There is a derived test +/// that checks that the enum is consistent with the syscalls. + +/// Halts the program. +pub const HALT: u32 = 4246u32; + +/// Writes to a file descriptor. Currently only used for `STDOUT/STDERR`. +pub const WRITE: u32 = 4004u32; + +/// Executes `HINT_LEN`. +pub const HINT_LEN: u32 = 0x00_00_00_F0; + +/// Executes `HINT_READ`. +pub const HINT_READ: u32 = 0x00_00_00_F1; diff --git a/runtime/entrypoint/src/syscalls/sys.rs b/runtime/entrypoint/src/syscalls/sys.rs new file mode 100644 index 00000000..5fc1a3e6 --- /dev/null +++ b/runtime/entrypoint/src/syscalls/sys.rs @@ -0,0 +1,70 @@ +//! Ported from Entrypoint for SP1 zkVM. + +use std::sync::Mutex; + +use lazy_static::lazy_static; +use rand::{rngs::StdRng, Rng, SeedableRng}; + +use crate::syscalls::{syscall_halt, syscall_write}; + +/// The random number generator seed for the zkVM. +/// +/// In the future, we can pass in this seed from the host or have the verifier generate it. +const PRNG_SEED: u64 = 0x123456789abcdef0; + +lazy_static! { + /// A lazy static to generate a global random number generator. + static ref RNG: Mutex = Mutex::new(StdRng::seed_from_u64(PRNG_SEED)); +} + +/// A lazy static to print a warning once for using the `sys_rand` system call. +static SYS_RAND_WARNING: std::sync::Once = std::sync::Once::new(); + +/// Generates random bytes. +/// +/// # Safety +/// +/// Make sure that `buf` has at least `nwords` words. +#[no_mangle] +pub unsafe extern "C" fn sys_rand(recv_buf: *mut u8, words: usize) { + SYS_RAND_WARNING.call_once(|| { + println!("WARNING: Using insecure random number generator."); + }); + let mut rng = RNG.lock().unwrap(); + for i in 0..words { + let element = recv_buf.add(i); + *element = rng.gen(); + } +} + +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn sys_panic(msg_ptr: *const u8, len: usize) -> ! { + sys_write(2, msg_ptr, len); + syscall_halt(1); +} + +#[allow(unused_variables)] +#[no_mangle] +pub const fn sys_getenv( + recv_buf: *mut u32, + words: usize, + varname: *const u8, + varname_len: usize, +) -> usize { + 0 +} + +#[allow(unused_variables)] +#[no_mangle] +pub const fn sys_alloc_words(nwords: usize) -> *mut u32 { + core::ptr::null_mut() +} + +#[allow(unused_unsafe)] +#[no_mangle] +pub fn sys_write(fd: u32, write_buf: *const u8, nbytes: usize) { + unsafe { + syscall_write(fd, write_buf, nbytes); + } +} diff --git a/runtime/precompiles/Cargo.toml b/runtime/precompiles/Cargo.toml new file mode 100644 index 00000000..a017d35e --- /dev/null +++ b/runtime/precompiles/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "zkm-precompiles" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.83" +bincode = "1.3.3" +cfg-if = "1.0.0" +getrandom = { version = "0.2.14", features = ["custom"] } +hex = "0.4.3" +rand = "0.8.5" +serde = { version = "1.0.201", features = ["derive"] } +num = { version = "0.4.3" } diff --git a/runtime/precompiles/src/io.rs b/runtime/precompiles/src/io.rs new file mode 100644 index 00000000..01c8b4ab --- /dev/null +++ b/runtime/precompiles/src/io.rs @@ -0,0 +1,55 @@ +//! Ported from Precompiles for SP1 zkVM. + +#![allow(unused_unsafe)] +use crate::syscall_write; +use crate::{syscall_hint_len, syscall_hint_read}; +use serde::de::DeserializeOwned; +use std::alloc::Layout; + +pub struct SyscallWriter { + fd: u32, +} + +impl std::io::Write for SyscallWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + let nbytes = buf.len(); + let write_buf = buf.as_ptr(); + unsafe { + syscall_write(self.fd, write_buf, nbytes); + } + Ok(nbytes) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +pub fn read_vec() -> Vec { + let len = unsafe { syscall_hint_len() }; + // Round up to the nearest multiple of 4 so that the memory allocated is in whole words + let capacity = (len + 3) / 4 * 4; + + // Allocate a buffer of the required length that is 4 byte aligned + let layout = Layout::from_size_align(capacity, 4).expect("vec is too large"); + let ptr = unsafe { std::alloc::alloc(layout) }; + // SAFETY: + // 1. `ptr` was allocated using alloc + // 2. We assuume that the VM global allocator doesn't dealloc + // 3/6. Size is correct from above + // 4/5. Length is 0 + // 7. Layout::from_size_align already checks this + let mut vec = unsafe { Vec::from_raw_parts(ptr, 0, capacity) }; + // Read the vec into uninitialized memory. The syscall assumes the memory is uninitialized, + // which should be true because the allocator does not dealloc, so a new alloc should be fresh. + unsafe { + syscall_hint_read(ptr, len); + vec.set_len(len); + } + vec +} + +pub fn read() -> T { + let vec = read_vec(); + bincode::deserialize(&vec).expect("deserialization failed") +} \ No newline at end of file diff --git a/runtime/precompiles/src/lib.rs b/runtime/precompiles/src/lib.rs new file mode 100644 index 00000000..070485c7 --- /dev/null +++ b/runtime/precompiles/src/lib.rs @@ -0,0 +1,19 @@ +//! Ported from Precompiles for SP1 zkVM. +//! +//! Specifically, this crate contains user-friendly functions that call SP1 syscalls. Syscalls are +//! also declared here for convenience. In order to avoid duplicate symbol errors, the syscall +//! function impls must live in sp1-zkvm, which is only imported into the end user program crate. +//! In contrast, sp1-precompiles can be imported into any crate in the dependency tree. + +pub mod io; +pub mod utils; + +pub const BIGINT_WIDTH_WORDS: usize = 8; + +extern "C" { + pub fn syscall_halt(exit_code: u8) -> !; + pub fn syscall_write(fd: u32, write_buf: *const u8, nbytes: usize); + pub fn syscall_hint_len() -> usize; + pub fn syscall_hint_read(ptr: *mut u8, len: usize); + pub fn sys_alloc_aligned(bytes: usize, align: usize) -> *mut u8; +} diff --git a/runtime/precompiles/src/utils.rs b/runtime/precompiles/src/utils.rs new file mode 100644 index 00000000..3bc2676c --- /dev/null +++ b/runtime/precompiles/src/utils.rs @@ -0,0 +1,111 @@ +//! Ported from Precompiles for SP1 zkVM. + +pub trait CurveOperations { + const GENERATOR: [u32; NUM_WORDS]; + + fn add_assign(limbs: &mut [u32; NUM_WORDS], other: &[u32; NUM_WORDS]); + fn double(limbs: &mut [u32; NUM_WORDS]); +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct AffinePoint, const NUM_WORDS: usize> { + pub(crate) limbs: [u32; NUM_WORDS], + _marker: std::marker::PhantomData, +} + +impl + Copy, const NUM_WORDS: usize> AffinePoint { + const GENERATOR: [u32; NUM_WORDS] = C::GENERATOR; + + pub const fn generator_in_affine() -> Self { + Self { + limbs: Self::GENERATOR, + _marker: std::marker::PhantomData, + } + } + + pub const fn new(limbs: [u32; NUM_WORDS]) -> Self { + Self { + limbs, + _marker: std::marker::PhantomData, + } + } + + /// x_bytes and y_bytes are the concatenated little endian representations of the x and y coordinates. + /// The length of x_bytes and y_bytes must each be NUM_WORDS * 2. + pub fn from(x_bytes: &[u8], y_bytes: &[u8]) -> Self { + debug_assert!(x_bytes.len() == NUM_WORDS * 2); + debug_assert!(y_bytes.len() == NUM_WORDS * 2); + + let mut limbs = [0u32; NUM_WORDS]; + let x = bytes_to_words_le(x_bytes); + let y = bytes_to_words_le(y_bytes); + debug_assert!(x.len() == NUM_WORDS / 2); + debug_assert!(y.len() == NUM_WORDS / 2); + + limbs[..(NUM_WORDS / 2)].copy_from_slice(&x); + limbs[(NUM_WORDS / 2)..].copy_from_slice(&y); + Self::new(limbs) + } + + pub fn add_assign(&mut self, other: &AffinePoint) { + C::add_assign(&mut self.limbs, &other.limbs); + } + + pub fn double(&mut self) { + C::double(&mut self.limbs); + } + + pub fn mul_assign(&mut self, scalar: &[u32]) { + debug_assert!(scalar.len() == NUM_WORDS / 2); + + let mut res: Option = None; + let mut temp = *self; + + for &words in scalar.iter() { + for i in 0..32 { + if (words >> i) & 1 == 1 { + match res.as_mut() { + Some(res) => res.add_assign(&temp), + None => res = Some(temp), + }; + } + + temp.double(); + } + } + + *self = res.unwrap(); + } + + pub fn from_le_bytes(limbs: &[u8]) -> Self { + let u32_limbs = bytes_to_words_le(limbs); + debug_assert!(u32_limbs.len() == NUM_WORDS); + + Self { + limbs: u32_limbs.try_into().unwrap(), + _marker: std::marker::PhantomData, + } + } + + pub fn to_le_bytes(&self) -> Vec { + let le_bytes = words_to_bytes_le(&self.limbs); + debug_assert!(le_bytes.len() == NUM_WORDS * 4); + le_bytes + } +} + +/// Converts a slice of words to a byte array in little endian. +pub fn words_to_bytes_le(words: &[u32]) -> Vec { + words + .iter() + .flat_map(|word| word.to_le_bytes().to_vec()) + .collect::>() +} + +/// Converts a byte array in little endian to a slice of words. +pub fn bytes_to_words_le(bytes: &[u8]) -> Vec { + bytes + .chunks_exact(4) + .map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap())) + .collect::>() +} diff --git a/src/cpu/kernel/elf.rs b/src/cpu/kernel/elf.rs index 1f943a7e..92c1bd04 100644 --- a/src/cpu/kernel/elf.rs +++ b/src/cpu/kernel/elf.rs @@ -32,6 +32,8 @@ pub struct Program { pub pre_image_id: [u8; 32], pub pre_hash_root: [u8; 32], pub page_hash_root: [u8; 32], + pub input_stream: Vec>, + pub input_stream_ptr: usize, } impl Program { @@ -252,6 +254,8 @@ impl Program { pre_image_id: pre_image_id.try_into().unwrap(), pre_hash_root, page_hash_root, + input_stream: Vec::new(), + input_stream_ptr: 0, }) } @@ -330,6 +334,8 @@ impl Program { pre_image_id: segment.pre_image_id, pre_hash_root: segment.pre_hash_root, page_hash_root, + input_stream: segment.input_stream, + input_stream_ptr: segment.input_stream_ptr, }) } } diff --git a/src/generation/state.rs b/src/generation/state.rs index 6f720370..6cc7606e 100644 --- a/src/generation/state.rs +++ b/src/generation/state.rs @@ -16,6 +16,8 @@ pub(crate) struct GenerationStateCheckpoint { pub(crate) struct GenerationState { pub(crate) registers: RegistersState, pub(crate) memory: MemoryState, + pub(crate) input_stream: Vec>, + pub(crate) input_stream_ptr: usize, pub(crate) traces: Traces, pub(crate) step: usize, } @@ -26,6 +28,8 @@ impl GenerationState { registers: RegistersState::new(kernel), memory: MemoryState::new(&[]), // FIXME traces: Traces::default(), + input_stream: kernel.program.input_stream.clone(), + input_stream_ptr: kernel.program.input_stream_ptr, step, }) } diff --git a/src/mips_emulator/state.rs b/src/mips_emulator/state.rs index 165cf4eb..336a9b34 100644 --- a/src/mips_emulator/state.rs +++ b/src/mips_emulator/state.rs @@ -32,6 +32,8 @@ pub struct Segment { pub image_id: [u8; 32], pub page_hash_root: [u8; 32], pub end_pc: u32, + pub input_stream: Vec>, + pub input_stream_ptr: usize, } pub struct State { @@ -54,12 +56,18 @@ pub struct State { /// brk handles the brk syscall brk: u32, - // tlb addr + /// tlb addr local_user: u32, /// step tracks the total step has been executed. pub step: u64, + /// A stream of input values (global to the entire program). + pub input_stream: Vec>, + + /// A ptr to the current position in the input stream incremented by HINT_READ opcode. + pub input_stream_ptr: usize, + pub exited: bool, pub exit_code: u8, dump_info: bool, @@ -90,6 +98,8 @@ impl State { local_user: 0, step: 0, brk: 0, + input_stream: Vec::new(), + input_stream_ptr: 0, exited: false, exit_code: 0, dump_info: false, @@ -110,6 +120,8 @@ impl State { local_user: 0, step: 0, brk: 0, + input_stream: Vec::new(), + input_stream_ptr: 0, exited: false, exit_code: 0, dump_info: false, @@ -338,6 +350,12 @@ impl State { } } + pub fn add_input_stream(&mut self, input: &T) { + let mut buf = Vec::new(); + bincode::serialize_into(&mut buf, input).expect("serialization failed"); + self.input_stream.push(buf); + } + pub fn load_preimage(&mut self, blockpath: String) { let mut hash_bytes = [0u8; 32]; for i in 0..8 { @@ -426,6 +444,7 @@ pub struct InstrumentedState { pre_image_id: [u8; 32], pre_hash_root: [u8; 32], block_path: String, + pre_input_ptr: usize, } impl Display for InstrumentedState { @@ -445,6 +464,7 @@ impl InstrumentedState { pre_image_id: [0u8; 32], pre_hash_root: [0u8; 32], pre_segment_id: 0u32, + pre_input_ptr: 0, }) } @@ -462,6 +482,46 @@ impl InstrumentedState { log::debug!("syscall {}", syscall_num); match syscall_num { + 0xF0 => { + if self.state.input_stream_ptr >= self.state.input_stream.len() { + panic!("not enough vecs in hint input stream"); + } + log::debug!( + "hint len {:X}", + self.state.input_stream[self.state.input_stream_ptr].len() + ); + v0 = self.state.input_stream[self.state.input_stream_ptr].len() as u32 + } + 0xF1 => { + log::debug!("{:X} {:X} {:X}", a0, a1, a2); + if self.state.input_stream_ptr >= self.state.input_stream.len() { + warn!("not enough vecs in hint input stream"); + } + + let vec = &self.state.input_stream[self.state.input_stream_ptr]; + self.state.input_stream_ptr += 1; + assert_eq!( + vec.len() as u32, + a1, + "hint input stream read length mismatch" + ); + assert_eq!(a0 % 4, 0, "hint read address not aligned to 4 bytes"); + for i in (0..a1).step_by(4) { + // Get each byte in the chunk + let b1 = vec[i as usize]; + // In case the vec is not a multiple of 4, right-pad with 0s. This is fine because we + // are assuming the word is uninitialized, so filling it with 0s makes sense. + let b2 = vec.get(i as usize + 1).copied().unwrap_or(0); + let b3 = vec.get(i as usize + 2).copied().unwrap_or(0); + let b4 = vec.get(i as usize + 3).copied().unwrap_or(0); + let word = u32::from_le_bytes([b1, b2, b3, b4]); + + // Save the data into runtime state so the runtime will use the desired data instead of + // 0 when first reading/writing from this address. + self.state.memory.set_memory(a0 + i, word); + } + v0 = a2 + } 4020 => { // read preimage (getpid) self.state.load_preimage(self.block_path.clone()) @@ -1141,6 +1201,8 @@ impl InstrumentedState { image_id, end_pc: self.state.pc, page_hash_root, + input_stream: self.state.input_stream.clone(), + input_stream_ptr: self.pre_input_ptr, }; let name = format!("{output}/{}", self.pre_segment_id); log::debug!("split: file {}", name); @@ -1150,6 +1212,7 @@ impl InstrumentedState { self.pre_segment_id += 1; } + self.pre_input_ptr = self.state.input_stream_ptr; self.pre_pc = self.state.pc; self.pre_image_id = image_id; self.pre_hash_root = page_hash_root; diff --git a/src/witness/operation.rs b/src/witness/operation.rs index 2449b87d..a5e9fa72 100644 --- a/src/witness/operation.rs +++ b/src/witness/operation.rs @@ -76,6 +76,9 @@ pub(crate) const SYSWRITE: usize = 4004; pub(crate) const SYSFCNTL: usize = 4055; pub(crate) const SYSSETTHREADAREA: usize = 4283; +pub(crate) const SYSHINTLEN: usize = 240; +pub(crate) const SYSHINTREAD: usize = 241; + pub(crate) const FD_STDIN: usize = 0; pub(crate) const FD_STDOUT: usize = 1; pub(crate) const FD_STDERR: usize = 2; @@ -852,6 +855,47 @@ pub(crate) fn load_preimage( Ok(()) } +pub(crate) fn load_input( + state: &mut GenerationState, + addr: usize, + size: usize, +) -> Result<()> { + let map_addr = addr; + let vec = state.input_stream[state.input_stream_ptr].clone(); + state.input_stream_ptr += 1; + assert_eq!(vec.len(), size, "hint input stream read length mismatch"); + assert_eq!(addr % 4, 0, "hint read address not aligned to 4 bytes"); + + let mut cpu_row = CpuColumnsView::default(); + cpu_row.clock = F::from_canonical_usize(state.traces.clock()); + let mut j = 0; + for i in (0..size).step_by(4) { + // Get each byte in the chunk + let b1 = vec[i]; + // In case the vec is not a multiple of 4, right-pad with 0s. This is fine because we + // are assuming the word is uninitialized, so filling it with 0s makes sense. + let b2 = vec.get(i + 1).copied().unwrap_or(0); + let b3 = vec.get(i + 2).copied().unwrap_or(0); + let b4 = vec.get(i + 3).copied().unwrap_or(0); + let word = u32::from_le_bytes([b1, b2, b3, b4]); + + if j == 8 { + state.traces.push_cpu(cpu_row); + cpu_row = CpuColumnsView::default(); + cpu_row.clock = F::from_canonical_usize(state.traces.clock()); + j = 0; + } + let addr = MemoryAddress::new(0, Segment::Code, map_addr + i); + let mem_op = mem_write_gp_log_and_fill(j, addr, state, &mut cpu_row, word); + state.traces.push_memory(mem_op); + j += 1; + } + + state.traces.push_cpu(cpu_row); + + Ok(()) +} + pub(crate) fn generate_syscall( state: &mut GenerationState, mut row: CpuColumnsView, @@ -864,6 +908,7 @@ pub(crate) fn generate_syscall( let mut v0 = 0usize; let mut v1 = 0usize; let mut is_load_preimage = false; + let mut is_load_input = false; let result = match sys_num { SYSGETPID => { row.general.syscall_mut().sysnum[0] = F::ONE; @@ -992,6 +1037,20 @@ pub(crate) fn generate_syscall( state.traces.push_memory(localop); Ok(()) } + SYSHINTLEN => { + if state.input_stream_ptr >= state.input_stream.len() { + log::warn!("not enough vecs in hint input stream"); + } + v0 = state.input_stream[state.input_stream_ptr].len(); + Ok(()) + } + SYSHINTREAD => { + if state.input_stream_ptr >= state.input_stream.len() { + log::warn!("not enough vecs in hint input stream"); + } + is_load_input = true; + Ok(()) + } _ => { row.general.syscall_mut().sysnum[11] = F::ONE; Ok(()) @@ -1009,6 +1068,10 @@ pub(crate) fn generate_syscall( if is_load_preimage { let _ = load_preimage(state, kernel); } + + if is_load_input { + let _ = load_input(state, a0, a1); + } result }