zkvmc-core 0.0.1

zkVMc core library
Documentation
mod forkable;
mod mmap;

use core::ptr::copy_nonoverlapping;
use std::{
    ptr::{self, NonNull},
    slice::from_raw_parts,
};

pub use forkable::ForkableMemory;
pub use mmap::{Mmap, MmapOffset};

use crate::send_sync_ptr::SendSyncPtr;

/// Memory access functions.
pub unsafe fn read_bytes<'a>(mem: *const u8, addr: u32, len: usize) -> &'a [u8] {
    from_raw_parts(mem.add(addr as usize), len)
}

pub unsafe fn read_words<'a>(mem: *const u8, addr: u32, len: usize) -> &'a [u32] {
    let bytes = read_bytes(mem, addr, len * 4);
    from_raw_parts(bytes.as_ptr() as _, bytes.len() / 4)
}

pub unsafe fn read_word(mem: *const u8, addr: u32) -> u32 {
    read_words(mem, addr, 1)[0]
}

/// Writes bytes to the context memory at the given addr.
pub unsafe fn write_bytes(mem: *mut u8, addr: u32, bytes: &[u8]) {
    copy_nonoverlapping(bytes.as_ptr(), mem.add(addr as usize), bytes.len());
}

pub unsafe fn write_word(mem: *mut u8, addr: u32, word: u32) {
    write_words(mem, addr, &[word]);
}

pub unsafe fn write_words(mem: *mut u8, addr: u32, words: &[u32]) {
    let bytes = from_raw_parts(words.as_ptr() as _, words.len() * 4);
    write_bytes(mem, addr, bytes);
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Layout {
    pub size: usize,
    pub align: usize,
}

impl Layout {
    pub fn new(size: usize, align: usize) -> Self {
        Self { size, align }
    }

    pub fn empty() -> Self {
        Self::new(0, 0)
    }
}

fn empty_mmap() -> SendSyncPtr<[u8]> {
    // Callers of this API assume that `.as_ptr()` below returns something
    // page-aligned and non-null. This is because the pointer returned from
    // that location is casted to other types which reside at a higher
    // alignment than a byte for example. Despite the length being zero we
    // still need to ensure that the pointer is suitably aligned.
    //
    // To handle that do a bit of trickery here to get the compiler to
    // generate an empty array to a high-alignment type (here 4k which is
    // the min page size we work with today). Then use this empty array as
    // the source pointer for an empty byte slice. It's a bit wonky but this
    // makes it such that the returned length is always zero (so this is
    // safe) but the pointer is always 4096 or suitably aligned.
    #[repr(C, align(4096))]
    struct PageAligned;
    let empty_page_alloc: &mut [PageAligned] = &mut [];
    let empty = NonNull::new(ptr::slice_from_raw_parts_mut(
        empty_page_alloc.as_mut_ptr().cast(),
        0,
    ))
    .unwrap();
    SendSyncPtr::from(empty)
}