Skip to content

Commit

Permalink
Merge branch 'master' into feat/mem_proof_v2
Browse files Browse the repository at this point in the history
  • Loading branch information
Aurélien Nicolas committed Oct 29, 2024
2 parents c64e2d2 + cc41908 commit 4a6023f
Show file tree
Hide file tree
Showing 62 changed files with 874 additions and 1,377 deletions.
5 changes: 3 additions & 2 deletions ceno_emul/src/addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use std::{fmt, ops};

pub const WORD_SIZE: usize = 4;
pub const PC_WORD_SIZE: usize = 4;
pub const PC_STEP_SIZE: usize = 4;

// Type aliases to clarify the code without wrapper types.
Expand All @@ -26,10 +27,10 @@ pub type Addr = u32;
pub type Cycle = u64;
pub type RegIdx = usize;

#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ByteAddr(pub u32);

#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WordAddr(u32);

impl From<ByteAddr> for WordAddr {
Expand Down
68 changes: 64 additions & 4 deletions ceno_emul/src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,47 @@ use alloc::collections::BTreeMap;

use crate::addr::WORD_SIZE;
use anyhow::{Context, Result, anyhow, bail};
use elf::{ElfBytes, endian::LittleEndian, file::Class};
use elf::{
ElfBytes,
abi::{PF_R, PF_W, PF_X},
endian::LittleEndian,
file::Class,
};

/// A RISC Zero program
#[derive(Clone, Debug)]
pub struct Program {
/// The entrypoint of the program
pub entry: u32,

/// This is the lowest address of the program's executable code
pub base_address: u32,
/// The instructions of the program
pub instructions: Vec<u32>,
/// The initial memory image
pub image: BTreeMap<u32, u32>,
}

impl Program {
/// Create program
pub fn new(
entry: u32,
base_address: u32,
instructions: Vec<u32>,
image: BTreeMap<u32, u32>,
) -> Program {
Self {
entry,
base_address,
instructions,
image,
}
}
/// Initialize a RISC Zero Program from an appropriate ELF file
pub fn load_elf(input: &[u8], max_mem: u32) -> Result<Program> {
let mut instructions: Vec<u32> = Vec::new();
let mut image: BTreeMap<u32, u32> = BTreeMap::new();
let mut base_address = None;

let elf = ElfBytes::<LittleEndian>::minimal_parse(input)
.map_err(|err| anyhow!("Elf parse error: {err}"))?;
if elf.ehdr.class != Class::ELF32 {
Expand All @@ -58,7 +84,18 @@ impl Program {
if segments.len() > 256 {
bail!("Too many program headers");
}
for segment in segments.iter().filter(|x| x.p_type == elf::abi::PT_LOAD) {
for (idx, segment) in segments
.iter()
.filter(|x| x.p_type == elf::abi::PT_LOAD)
.enumerate()
{
tracing::debug!(
"loadable segement {}: PF_R={}, PF_W={}, PF_X={}",
idx,
segment.p_flags & PF_R != 0,
segment.p_flags & PF_W != 0,
segment.p_flags & PF_X != 0,
);
let file_size: u32 = segment
.p_filesz
.try_into()
Expand All @@ -77,6 +114,13 @@ impl Program {
.p_vaddr
.try_into()
.map_err(|err| anyhow!("vaddr is larger than 32 bits. {err}"))?;
if (segment.p_flags & PF_X) != 0 {
if base_address.is_none() {
base_address = Some(vaddr);
} else {
return Err(anyhow!("only support one executable segment"));
}
}
if vaddr % WORD_SIZE as u32 != 0 {
bail!("vaddr {vaddr:08x} is unaligned");
}
Expand Down Expand Up @@ -104,9 +148,25 @@ impl Program {
word |= (*byte as u32) << (j * 8);
}
image.insert(addr, word);
if (segment.p_flags & PF_X) != 0 {
instructions.push(word);
}
}
}
}
Ok(Program { entry, image })

if base_address.is_none() {
return Err(anyhow!("does not have executable segment"));
}
let base_address = base_address.unwrap();
assert!(entry >= base_address);
assert!((entry - base_address) as usize <= instructions.len() * WORD_SIZE);

Ok(Program {
entry,
base_address,
image,
instructions,
})
}
}
30 changes: 20 additions & 10 deletions ceno_emul/src/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,30 @@ use crate::addr::{Addr, RegIdx};
/// - the layout of virtual memory,
/// - special addresses, such as the initial PC,
/// - codes of environment calls.
pub struct Platform;
#[derive(Copy, Clone)]
pub struct Platform {
pub rom_start: Addr,
pub rom_end: Addr,
pub ram_start: Addr,
pub ram_end: Addr,
}

pub const CENO_PLATFORM: Platform = Platform;
pub const CENO_PLATFORM: Platform = Platform {
rom_start: 0x2000_0000,
rom_end: 0x3000_0000 - 1,
ram_start: 0x8000_0000,
ram_end: 0xFFFF_FFFF,
};

impl Platform {
// Virtual memory layout.

pub const fn rom_start(&self) -> Addr {
0x2000_0000
self.rom_start
}

pub const fn rom_end(&self) -> Addr {
0x3000_0000 - 1
self.rom_end
}

pub fn is_rom(&self, addr: Addr) -> bool {
Expand All @@ -43,18 +54,17 @@ impl Platform {
}

pub const fn ram_start(&self) -> Addr {
let ram_start = 0x8000_0000;
if cfg!(feature = "forbid_overflow") {
// -1<<11 == 0x800 is the smallest negative 'immediate'
// offset we can have in memory instructions.
// So if we stay away from it, we are safe.
assert!(ram_start >= 0x800);
assert!(self.ram_start >= 0x800);
}
ram_start
self.ram_start
}

pub const fn ram_end(&self) -> Addr {
0xFFFF_FFFF
self.ram_end
- if cfg!(feature = "forbid_overflow") {
// (1<<11) - 1 == 0x7ff is the largest positive 'immediate'
// offset we can have in memory instructions.
Expand Down Expand Up @@ -95,7 +105,7 @@ impl Platform {

// Startup.

pub const fn pc_start(&self) -> Addr {
pub const fn pc_base(&self) -> Addr {
self.rom_start()
}

Expand Down Expand Up @@ -148,7 +158,7 @@ mod tests {
#[test]
fn test_no_overlap() {
let p = CENO_PLATFORM;
assert!(p.can_execute(p.pc_start()));
assert!(p.can_execute(p.pc_base()));
// ROM and RAM do not overlap.
assert!(!p.is_rom(p.ram_start()));
assert!(!p.is_rom(p.ram_end()));
Expand Down
3 changes: 2 additions & 1 deletion ceno_emul/src/rv32im.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ pub struct Emulator {
}

#[derive(Debug)]
#[allow(dead_code)]
pub enum TrapCause {
InstructionAddressMisaligned,
InstructionAccessFault,
Expand Down Expand Up @@ -557,6 +556,7 @@ impl Emulator {
let decoded = DecodedInstruction::new(word);
let insn = self.table.lookup(&decoded);
ctx.on_insn_decoded(&decoded);
tracing::trace!("pc: {:x}, kind: {:?}", pc.0, insn.kind);

if match insn.category {
InsnCategory::Compute => self.step_compute(ctx, insn.kind, &decoded)?,
Expand Down Expand Up @@ -776,6 +776,7 @@ impl Emulator {
let addr = ByteAddr(rs1.wrapping_add(decoded.imm_s()));
let shift = 8 * (addr.0 & 3);
if !ctx.check_data_store(addr) {
tracing::error!("mstore: addr={:x?},rs1={:x}", addr, rs1);
return ctx.trap(TrapCause::StoreAccessFault);
}
let mut data = ctx.peek_memory(addr.waddr());
Expand Down
29 changes: 29 additions & 0 deletions ceno_emul/src/tracer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,35 @@ impl StepRecord {
)
}

pub fn new_im_instruction(
cycle: Cycle,
pc: ByteAddr,
insn_code: u32,
rs1_read: Word,
rd: Change<Word>,
mem_op: ReadOp,
prev_cycle: Cycle,
) -> StepRecord {
let pc = Change::new(pc, pc + PC_STEP_SIZE);
StepRecord::new_insn(
cycle,
pc,
insn_code,
Some(rs1_read),
None,
Some(rd),
Some(WriteOp {
addr: mem_op.addr,
value: Change {
before: mem_op.value,
after: mem_op.value,
},
previous_cycle: mem_op.previous_cycle,
}),
prev_cycle,
)
}

pub fn new_u_instruction(
cycle: Cycle,
pc: ByteAddr,
Expand Down
44 changes: 30 additions & 14 deletions ceno_emul/src/vm_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ use crate::{
tracer::{Change, StepRecord, Tracer},
};
use anyhow::{Result, anyhow};
use std::iter::from_fn;
use std::{iter::from_fn, ops::Deref, sync::Arc};

/// An implementation of the machine state and of the side-effects of operations.
pub struct VMState {
program: Arc<Program>,
platform: Platform,
pc: Word,
/// Map a word-address (addr/4) to a word.
Expand All @@ -24,28 +25,39 @@ pub struct VMState {
}

impl VMState {
pub fn new(platform: Platform) -> Self {
let pc = platform.pc_start();
Self {
platform,
pub fn new(platform: Platform, program: Program) -> Self {
let pc = program.entry;
let program = Arc::new(program);

let mut vm = Self {
pc,
platform,
program: program.clone(),
memory: HashMap::new(),
registers: [0; 32],
halted: false,
tracer: Tracer::new(),
};

// init memory from program.image
for (&addr, &value) in program.image.iter() {
vm.init_memory(ByteAddr(addr).waddr(), value);
}

vm
}

pub fn new_from_elf(platform: Platform, elf: &[u8]) -> Result<Self> {
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));
let program = Program::load_elf(elf, u32::MAX)?;
let state = Self::new(platform, program);

if state.program.base_address != state.platform.rom_start() {
return Err(anyhow!(
"Invalid base_address {:x}",
state.program.base_address
));
}

Ok(state)
}

Expand All @@ -57,7 +69,11 @@ impl VMState {
&self.tracer
}

/// Set a word in memory without side-effects.
pub fn program(&self) -> &Program {
self.program.deref()
}

/// Set a word in memory without side effects.
pub fn init_memory(&mut self, addr: WordAddr, value: Word) {
self.memory.insert(addr, value);
}
Expand Down
35 changes: 26 additions & 9 deletions ceno_emul/tests/test_vm_trace.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
#![allow(clippy::unusual_byte_groupings)]
use anyhow::Result;
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};

use ceno_emul::{
ByteAddr, CENO_PLATFORM, Cycle, EmuContext, InsnKind, StepRecord, Tracer, VMState, WordAddr,
CENO_PLATFORM, Cycle, EmuContext, InsnKind, Program, StepRecord, Tracer, VMState, WORD_SIZE,
WordAddr,
};

#[test]
fn test_vm_trace() -> Result<()> {
let mut ctx = VMState::new(CENO_PLATFORM);

let pc_start = ByteAddr(CENO_PLATFORM.pc_start()).waddr();
for (i, &inst) in PROGRAM_FIBONACCI_20.iter().enumerate() {
ctx.init_memory(pc_start + i as u32, inst);
}
let program = Program::new(
CENO_PLATFORM.pc_base(),
CENO_PLATFORM.pc_base(),
PROGRAM_FIBONACCI_20.to_vec(),
PROGRAM_FIBONACCI_20
.iter()
.enumerate()
.map(|(insn_idx, &insn)| {
(
CENO_PLATFORM.pc_base() + (WORD_SIZE * insn_idx) as u32,
insn,
)
})
.collect(),
);
let mut ctx = VMState::new(CENO_PLATFORM, program);

let steps = run(&mut ctx)?;

Expand All @@ -35,7 +46,13 @@ fn test_vm_trace() -> Result<()> {

#[test]
fn test_empty_program() -> Result<()> {
let mut ctx = VMState::new(CENO_PLATFORM);
let empty_program = Program::new(
CENO_PLATFORM.pc_base(),
CENO_PLATFORM.pc_base(),
vec![],
BTreeMap::new(),
);
let mut ctx = VMState::new(CENO_PLATFORM, empty_program);
let res = run(&mut ctx);
assert!(matches!(res, Err(e) if e.to_string().contains("IllegalInstruction(0)")));
Ok(())
Expand Down
Loading

0 comments on commit 4a6023f

Please sign in to comment.