diff --git a/common/src/lib.rs b/common/src/lib.rs index bad8da7..53ddfa5 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -2,8 +2,10 @@ extern crate alloc; -pub mod constants; -pub mod manifest; pub mod accumulator; pub mod client_commands; -pub mod vm; \ No newline at end of file +pub mod constants; +pub mod manifest; +pub mod vm; + +mod riscv; diff --git a/common/src/riscv/decode.rs b/common/src/riscv/decode.rs new file mode 100644 index 0000000..e6fde44 --- /dev/null +++ b/common/src/riscv/decode.rs @@ -0,0 +1,109 @@ +use super::op::Op; + +#[inline] +fn rd(inst: u32) -> u8 { + ((inst >> 7) & 0b11111) as u8 +} + +#[inline] +fn rs1(inst: u32) -> u8 { + ((inst >> 15) & 0b11111) as u8 +} + +#[inline] +fn rs2(inst: u32) -> u8 { + ((inst >> 20) & 0b11111) as u8 +} + +fn i_imm(inst: u32) -> i32 { + (inst as i32) >> 20 +} + +#[rustfmt::skip] +pub fn decode(inst: u32) -> Op { + match inst & 0x0000007f { + 0x00000037 => Op::Lui { rd: rd(inst), imm: i_imm(inst) }, + 0x00000017 => Op::Auipc { rd: rd(inst), imm: i_imm(inst) }, + 0x0000006f => Op::Jal { rd: rd(inst), imm: i_imm(inst) }, + 0x00000067 => match inst & 0x0000707f { + 0x00000067 => Op::Jalr { rd: rd(inst), rs1: rs1(inst), imm: i_imm(inst) }, + _ => Op::Unknown, + }, + 0x00000063 => match inst & 0x0000707f { + 0x00000063 => Op::Beq { rs1: rs1(inst), rs2: rs2(inst), imm: i_imm(inst) }, + 0x00001063 => Op::Bne { rs1: rs1(inst), rs2: rs2(inst), imm: i_imm(inst) }, + 0x00004063 => Op::Blt { rs1: rs1(inst), rs2: rs2(inst), imm: i_imm(inst) }, + 0x00005063 => Op::Bge { rs1: rs1(inst), rs2: rs2(inst), imm: i_imm(inst) }, + 0x00006063 => Op::Bltu { rs1: rs1(inst), rs2: rs2(inst), imm: i_imm(inst) }, + 0x00007063 => Op::Bgeu { rs1: rs1(inst), rs2: rs2(inst), imm: i_imm(inst) }, + _ => Op::Unknown, + }, + 0x00000003 => match inst & 0x0000707f { + 0x00000003 => Op::Lb { rd: rd(inst), rs1: rs1(inst), imm: i_imm(inst) }, + 0x00001003 => Op::Lh { rd: rd(inst), rs1: rs1(inst), imm: i_imm(inst) }, + 0x00002003 => Op::Lw { rd: rd(inst), rs1: rs1(inst), imm: i_imm(inst) }, + 0x00004003 => Op::Lbu { rd: rd(inst), rs1: rs1(inst), imm: i_imm(inst) }, + 0x00005003 => Op::Lhu { rd: rd(inst), rs1: rs1(inst), imm: i_imm(inst) }, + _ => Op::Unknown, + }, + 0x00000023 => match inst & 0x0000707f { + 0x00000023 => Op::Sb { rs1: rs1(inst), rs2: rs2(inst), imm: i_imm(inst) }, + 0x00001023 => Op::Sh { rs1: rs1(inst), rs2: rs2(inst), imm: i_imm(inst) }, + 0x00002023 => Op::Sw { rs1: rs1(inst), rs2: rs2(inst), imm: i_imm(inst) }, + _ => Op::Unknown, + }, + 0x00000013 => match inst & 0x0000707f { + 0x00000013 => Op::Addi { rd: rd(inst), rs1: rs1(inst), imm: i_imm(inst) }, + 0x00002013 => Op::Slti { rd: rd(inst), rs1: rs1(inst), imm: i_imm(inst) }, + 0x00003013 => Op::Sltiu { rd: rd(inst), rs1: rs1(inst), imm: i_imm(inst) }, + 0x00004013 => Op::Xori { rd: rd(inst), rs1: rs1(inst), imm: i_imm(inst) }, + 0x00006013 => Op::Ori { rd: rd(inst), rs1: rs1(inst), imm: i_imm(inst) }, + 0x00007013 => Op::Andi { rd: rd(inst), rs1: rs1(inst), imm: i_imm(inst) }, + 0x00001013 => match inst & 0xfe00707f { + 0x00001013 => Op::Slli { rd: rd(inst), rs1: rs1(inst), imm: i_imm(inst) }, + _ => Op::Unknown, + }, + 0x00005013 => match inst & 0xfe00707f { + 0x00005013 => Op::Srli { rd: rd(inst), rs1: rs1(inst), imm: i_imm(inst) }, + 0x40005013 => Op::Srai { rd: rd(inst), rs1: rs1(inst), imm: i_imm(inst) }, + _ => Op::Unknown, + }, + _ => Op::Unknown, + }, + 0x00000033 => match inst & 0xfe00707f { + 0x00000033 => Op::Add { rd: rd(inst), rs1: rs1(inst), rs2: rs2(inst) }, + 0x40000033 => Op::Sub { rd: rd(inst), rs1: rs1(inst), rs2: rs2(inst) }, + 0x00001033 => Op::Sll { rd: rd(inst), rs1: rs1(inst), rs2: rs2(inst) }, + 0x00002033 => Op::Slt { rd: rd(inst), rs1: rs1(inst), rs2: rs2(inst) }, + 0x00003033 => Op::Sltu { rd: rd(inst), rs1: rs1(inst), rs2: rs2(inst) }, + 0x00004033 => Op::Xor { rd: rd(inst), rs1: rs1(inst), rs2: rs2(inst) }, + 0x00005033 => Op::Srl { rd: rd(inst), rs1: rs1(inst), rs2: rs2(inst) }, + 0x40005033 => Op::Sra { rd: rd(inst), rs1: rs1(inst), rs2: rs2(inst) }, + 0x00006033 => Op::Or { rd: rd(inst), rs1: rs1(inst), rs2: rs2(inst) }, + 0x00007033 => Op::And { rd: rd(inst), rs1: rs1(inst), rs2: rs2(inst) }, + // 0x02000033 => Op::RV_OP_MUL, + // 0x02001033 => Op::RV_OP_MULH, + // 0x02002033 => Op::RV_OP_MULHSU, + // 0x02003033 => Op::RV_OP_MULHU, + // 0x02004033 => Op::RV_OP_DIV, + // 0x02005033 => Op::RV_OP_DIVU, + // 0x02006033 => Op::RV_OP_REM, + // 0x02007033 => Op::RV_OP_REMU, + _ => Op::Unknown, + }, + // 0x0000000f => match inst & 0x0000707f { + // 0x0000000f => match inst & 0xffffffff { + // 0x8330000f => Op::RV_OP_FENCE_TSO, + // 0x0100000f => Op::RV_OP_PAUSE, + // _ => Op::RV_OP_FENCE, + // }, + // _ => Op::Unknown, + // }, + 0x00000073 => match inst & 0xffffffff { + 0x00000073 => Op::Ecall, + 0x00100073 => Op::Break, + _ => Op::Unknown, + }, + _ => Op::Unknown, + } +} diff --git a/common/src/riscv/mod.rs b/common/src/riscv/mod.rs new file mode 100644 index 0000000..ba34641 --- /dev/null +++ b/common/src/riscv/mod.rs @@ -0,0 +1,4 @@ +pub mod decode; +pub mod op; + +pub const XLEN: usize = 32; diff --git a/common/src/riscv/op.rs b/common/src/riscv/op.rs new file mode 100644 index 0000000..dff446d --- /dev/null +++ b/common/src/riscv/op.rs @@ -0,0 +1,53 @@ +#[derive(Debug, Copy, Clone)] +pub enum Op { + Unknown, + Add { rd: u8, rs1: u8, rs2: u8 }, + Sub { rd: u8, rs1: u8, rs2: u8 }, + Sll { rd: u8, rs1: u8, rs2: u8 }, + Slt { rd: u8, rs1: u8, rs2: u8 }, + Sltu { rd: u8, rs1: u8, rs2: u8 }, + Xor { rd: u8, rs1: u8, rs2: u8 }, + Srl { rd: u8, rs1: u8, rs2: u8 }, + Sra { rd: u8, rs1: u8, rs2: u8 }, + Or { rd: u8, rs1: u8, rs2: u8 }, + And { rd: u8, rs1: u8, rs2: u8 }, + + Addi { rd: u8, rs1: u8, imm: i32 }, + + Andi { rd: u8, rs1: u8, imm: i32 }, + + Auipc { rd: u8, imm: i32 }, + Beq { rs1: u8, rs2: u8, imm: i32 }, + Bne { rs1: u8, rs2: u8, imm: i32 }, + Blt { rs1: u8, rs2: u8, imm: i32 }, + Bge { rs1: u8, rs2: u8, imm: i32 }, + Bltu { rs1: u8, rs2: u8, imm: i32 }, + Bgeu { rs1: u8, rs2: u8, imm: i32 }, + Jal { rd: u8, imm: i32 }, + Jalr { rd: u8, rs1: u8, imm: i32 }, + + Lb { rd: u8, rs1: u8, imm: i32 }, + Lh { rd: u8, rs1: u8, imm: i32 }, + Lw { rd: u8, rs1: u8, imm: i32 }, + Lbu { rd: u8, rs1: u8, imm: i32 }, + Lhu { rd: u8, rs1: u8, imm: i32 }, + + Lui { rd: u8, imm: i32 }, + + Ori { rd: u8, rs1: u8, imm: i32 }, + + Sb { rs1: u8, rs2: u8, imm: i32 }, + Sh { rs1: u8, rs2: u8, imm: i32 }, + Sw { rs1: u8, rs2: u8, imm: i32 }, + + Slli { rd: u8, rs1: u8, imm: i32 }, + Slti { rd: u8, rs1: u8, imm: i32 }, + Sltiu { rd: u8, rs1: u8, imm: i32 }, + Srli { rd: u8, rs1: u8, imm: i32 }, + Srai { rd: u8, rs1: u8, imm: i32 }, + + Xori { rd: u8, rs1: u8, imm: i32 }, + + Ecall, + Break, +} diff --git a/common/src/vm.rs b/common/src/vm.rs index 1ea4e2f..d361f99 100644 --- a/common/src/vm.rs +++ b/common/src/vm.rs @@ -3,7 +3,7 @@ use core::ops::{Deref, DerefMut}; -use crate::constants::PAGE_SIZE; +use crate::{constants::PAGE_SIZE, riscv::op::Op}; use alloc::vec::Vec; /// Represents a single page of memory. @@ -79,6 +79,12 @@ impl MemorySegment { }) } + #[inline] + /// Returns true if this segment contains the byte at the specified address. + pub fn contains(&self, address: u32) -> bool { + address >= self.start_address && address < self.start_address + self.size + } + /// Reads a byte from the specified address. #[inline] pub fn read_u8(&mut self, address: u32) -> Result { @@ -238,18 +244,75 @@ impl Cpu { } } + fn read_u8(&mut self, address: u32) -> Result { + if self.stack_seg.contains(address) { + return self.stack_seg.read_u8(address); + } else if self.data_seg.contains(address) { + return self.data_seg.read_u8(address); + } else if self.code_seg.contains(address) { + return self.code_seg.read_u8(address); + } + Err("Address out of bounds") + } + + fn read_u16(&mut self, address: u32) -> Result { + if self.stack_seg.contains(address) { + return self.stack_seg.read_u16(address); + } else if self.data_seg.contains(address) { + return self.data_seg.read_u16(address); + } else if self.code_seg.contains(address) { + return self.code_seg.read_u16(address); + } + Err("Address out of bounds") + } + + fn read_u32(&mut self, address: u32) -> Result { + if self.stack_seg.contains(address) { + return self.stack_seg.read_u32(address); + } else if self.data_seg.contains(address) { + return self.data_seg.read_u32(address); + } else if self.code_seg.contains(address) { + return self.code_seg.read_u32(address); + } + Err("Address out of bounds") + } + + fn write_u8(&mut self, address: u32, value: u8) -> Result<(), &'static str> { + if self.stack_seg.contains(address) { + return self.stack_seg.write_u8(address, value); + } else if self.data_seg.contains(address) { + return self.data_seg.write_u8(address, value); + } + Err("Address out of bounds") + } + + fn write_u16(&mut self, address: u32, value: u16) -> Result<(), &'static str> { + if self.stack_seg.contains(address) { + return self.stack_seg.write_u16(address, value); + } else if self.data_seg.contains(address) { + return self.data_seg.write_u16(address, value); + } + Err("Address out of bounds") + } + + fn write_u32(&mut self, address: u32, value: u32) -> Result<(), &'static str> { + if self.stack_seg.contains(address) { + return self.stack_seg.write_u32(address, value); + } else if self.data_seg.contains(address) { + return self.data_seg.write_u32(address, value); + } + Err("Address out of bounds") + } + #[inline(always)] /// Fetches the next instruction to be executed. - pub fn fetch_instruction(&mut self) -> u32 { - if let Ok(inst) = self.code_seg.read_u32(self.pc) { - inst - } else { - panic!("Failed to fetch page") - } + pub fn fetch_instruction(&mut self) -> Result { + self.code_seg.read_u32(self.pc) } + #[rustfmt::skip] #[inline(always)] - pub fn execute(&mut self, inst: u32) { + pub fn execute(&mut self, inst: u32) -> Result<(), &'static str> { // TODO: for now, treat everything as a NOP // This is a placeholder for actual instruction decoding and execution logic // match inst { @@ -257,8 +320,138 @@ impl Cpu { // _ => panic!("Unknown instruction"), // } + let op = crate::riscv::decode::decode(inst); + match op { + Op::Add { rd, rs1, rs2 } => { self.regs[rd as usize] = self.regs[rs1 as usize].wrapping_add(self.regs[rs2 as usize]); }, + Op::Sub { rd, rs1, rs2 } => { self.regs[rd as usize] = self.regs[rs1 as usize].wrapping_sub(self.regs[rs2 as usize]); }, + Op::Sll { rd, rs1, rs2 } => { self.regs[rd as usize] = self.regs[rs1 as usize] << (self.regs[rs2 as usize] & 0x1f); }, + Op::Slt { rd, rs1, rs2 } => { self.regs[rd as usize] = ((self.regs[rs1 as usize] as i32) < (self.regs[rs2 as usize] as i32)) as u32; }, + Op::Sltu { rd, rs1, rs2 } => { self.regs[rd as usize] = (self.regs[rs1 as usize] < self.regs[rs2 as usize]) as u32; }, + Op::Xor { rd, rs1, rs2 } => { self.regs[rd as usize] = self.regs[rs1 as usize] ^ self.regs[rs2 as usize]; }, + Op::Srl { rd, rs1, rs2 } => { self.regs[rd as usize] = self.regs[rs1 as usize] >> (self.regs[rs2 as usize] & 0x1f); }, + Op::Sra { rd, rs1, rs2 } => { self.regs[rd as usize] = ((self.regs[rs1 as usize] as i32) >> (self.regs[rs2 as usize] & 0x1f)) as u32; }, + Op::Or { rd, rs1, rs2 } => { self.regs[rd as usize] = self.regs[rs1 as usize] | self.regs[rs2 as usize]; }, + Op::And { rd, rs1, rs2 } => { self.regs[rd as usize] = self.regs[rs1 as usize] & self.regs[rs2 as usize]; }, + Op::Addi { rd, rs1, imm } => { self.regs[rd as usize] = self.regs[rs1 as usize].wrapping_add(imm as u32); }, + Op::Andi { rd, rs1, imm } => { self.regs[rd as usize] = self.regs[rs1 as usize] & (imm as u32); }, + Op::Auipc { rd, imm } => { self.regs[rd as usize] = self.pc.wrapping_add(imm as u32); }, + Op::Beq { rs1, rs2, imm } => { + if self.regs[rs1 as usize] == self.regs[rs2 as usize] { + self.pc = self.pc.wrapping_add(imm as u32); + } + }, + Op::Bne { rs1, rs2, imm } => { + if self.regs[rs1 as usize] != self.regs[rs2 as usize] { + self.pc = self.pc.wrapping_add(imm as u32); + } + }, + Op::Blt { rs1, rs2, imm } => { + if (self.regs[rs1 as usize] as i32) < (self.regs[rs2 as usize] as i32) { + self.pc = self.pc.wrapping_add(imm as u32); + } + }, + Op::Bge { rs1, rs2, imm } => { + if (self.regs[rs1 as usize] as i32) >= (self.regs[rs2 as usize] as i32) { + self.pc = self.pc.wrapping_add(imm as u32); + } + }, + Op::Bltu { rs1, rs2, imm } => { + if self.regs[rs1 as usize] < self.regs[rs2 as usize] { + self.pc = self.pc.wrapping_add(imm as u32); + } + }, + Op::Bgeu { rs1, rs2, imm } => { + if self.regs[rs1 as usize] >= self.regs[rs2 as usize] { + self.pc = self.pc.wrapping_add(imm as u32); + } + }, + Op::Jal { rd, imm } => { + self.regs[rd as usize] = self.pc.wrapping_add(4); + self.pc = self.pc.wrapping_add(imm as u32); + }, + Op::Jalr { rd, rs1, imm } => { + self.regs[rd as usize] = self.pc.wrapping_add(4); + self.pc = self.regs[rs1 as usize].wrapping_add(imm as u32); + }, + Op::Lb { rd, rs1, imm } => { + let addr = self.regs[rs1 as usize].wrapping_add(imm as u32); + let value = self.read_u8(addr)?; + self.regs[rd as usize] = value as i8 as i32 as u32; + }, + Op::Lh { rd, rs1, imm } => { + let addr = self.regs[rs1 as usize].wrapping_add(imm as u32); + if addr & 1 != 0 { + return Err("Unaligned 16-bit read"); + } + let value = self.read_u16(addr)?; + self.regs[rd as usize] = value as i16 as i32 as u32; + }, + Op::Lw { rd, rs1, imm } => { + let addr = self.regs[rs1 as usize].wrapping_add(imm as u32); + if addr & 3 != 0 { + return Err("Unaligned 32-bit read"); + } + let value = self.read_u32(addr)?; + self.regs[rd as usize] = value; + }, + Op::Lbu { rd, rs1, imm } => { + let addr = self.regs[rs1 as usize].wrapping_add(imm as u32); + let value = self.read_u8(addr)?; + self.regs[rd as usize] = value as u32; + }, + Op::Lhu { rd, rs1, imm } => { + let addr = self.regs[rs1 as usize].wrapping_add(imm as u32); + if addr & 1 != 0 { + return Err("Unaligned 16-bit read"); + } + let value = self.read_u16(addr)?; + self.regs[rd as usize] = value as u32; + }, + Op::Lui { rd, imm } => { self.regs[rd as usize] = imm as u32; }, + Op::Ori { rd, rs1, imm } => { self.regs[rd as usize] = self.regs[rs1 as usize] | (imm as u32); }, + Op::Sb { rs1, rs2, imm } => { + let addr = self.regs[rs1 as usize].wrapping_add(imm as u32); + let value = self.regs[rs2 as usize] as u8; + self.write_u8(addr, value)?; + }, + Op::Sh { rs1, rs2, imm } => { + let addr = self.regs[rs1 as usize].wrapping_add(imm as u32); + if addr & 1 != 0 { + return Err("Unaligned 16-bit write"); + } + let value = self.regs[rs2 as usize] as u16; + self.write_u16(addr, value)?; + }, + Op::Sw { rs1, rs2, imm } => { + let addr = self.regs[rs1 as usize].wrapping_add(imm as u32); + if addr & 3 != 0 { + return Err("Unaligned 32-bit write"); + } + let value = self.regs[rs2 as usize]; + self.write_u32(addr, value)?; + }, + Op::Slli { rd, rs1, imm } => { self.regs[rd as usize] = self.regs[rs1 as usize] << (imm & 0x1f); }, + Op::Slti { rd, rs1, imm } => { self.regs[rd as usize] = ((self.regs[rs1 as usize] as i32) < imm) as u32; }, + Op::Sltiu { rd, rs1, imm } => { self.regs[rd as usize] = (self.regs[rs1 as usize] < imm as u32) as u32; }, + Op::Srli { rd, rs1, imm } => { self.regs[rd as usize] = self.regs[rs1 as usize] >> (imm & 0x1f); }, + Op::Srai { rd, rs1, imm } => { self.regs[rd as usize] = ((self.regs[rs1 as usize] as i32) >> (imm & 0x1f)) as u32; }, + Op::Xori { rd, rs1, imm } => { self.regs[rd as usize] = self.regs[rs1 as usize] ^ (imm as u32); }, + + Op::Ecall => { + todo!(); + }, + Op::Break => { + todo!(); + }, + Op::Unknown => { + return Err("Unknown instruction"); + }, + } + self.pc += 4; self.regs[0] = 0; + + Ok(()) } } diff --git a/vm/src/handlers/start_vapp.rs b/vm/src/handlers/start_vapp.rs index 39631db..6c12865 100644 --- a/vm/src/handlers/start_vapp.rs +++ b/vm/src/handlers/start_vapp.rs @@ -1,4 +1,3 @@ - use core::cell::RefCell; use alloc::rc::Rc; @@ -7,16 +6,14 @@ use ledger_device_sdk::io; use common::manifest::Manifest; use common::vm::{Cpu, MemorySegment}; -use crate::{println, AppSW}; use super::lib::outsourced_mem::OutsourcedMemory; - +use crate::{println, AppSW}; pub fn handler_start_vapp(comm: &mut io::Comm) -> Result<(), AppSW> { let data_raw = comm.get_data().map_err(|_| AppSW::WrongApduLength)?; - let (manifest, hmac) = postcard::take_from_bytes::(data_raw) - .map_err(|_| AppSW::IncorrectData)?; - + let (manifest, hmac) = + postcard::take_from_bytes::(data_raw).map_err(|_| AppSW::IncorrectData)?; if hmac.len() != 32 { return Err(AppSW::IncorrectData); @@ -26,38 +23,35 @@ pub fn handler_start_vapp(comm: &mut io::Comm) -> Result<(), AppSW> { if hmac != [0x42u8; 32] { return Err(AppSW::SignatureFail); } - + println!("Running app with Manifest: {:?}", manifest); println!("hmac: {:?}", hmac); - let comm = Rc::new(RefCell::new(comm)); let code_seg = MemorySegment::::new( manifest.code_start, manifest.code_end - manifest.code_start, - OutsourcedMemory::new(comm.clone(), true) - ).unwrap(); + OutsourcedMemory::new(comm.clone(), true), + ) + .unwrap(); let data_seg = MemorySegment::::new( manifest.data_start, manifest.data_end - manifest.data_start, - OutsourcedMemory::new(comm.clone(), false) - ).unwrap(); + OutsourcedMemory::new(comm.clone(), false), + ) + .unwrap(); let stack_seg = MemorySegment::::new( manifest.stack_start, manifest.stack_end - manifest.stack_start, - OutsourcedMemory::new(comm.clone(), false) - ).unwrap(); - - let mut cpu = Cpu::new( - manifest.entrypoint, - code_seg, - data_seg, - stack_seg - ); - + OutsourcedMemory::new(comm.clone(), false), + ) + .unwrap(); + + let mut cpu = Cpu::new(manifest.entrypoint, code_seg, data_seg, stack_seg); + // x2 is the stack pointer, that grows backwards from the end of the stack // we make sure it's aligned to a multiple of 4 cpu.regs[2] = (manifest.stack_end - 4) & 0xfffffff0u32; @@ -65,10 +59,13 @@ pub fn handler_start_vapp(comm: &mut io::Comm) -> Result<(), AppSW> { assert!(cpu.pc % 4 == 0, "Unaligned entrypoint"); loop { - let instr = cpu.fetch_instruction(); + // TODO: handle errors + let instr = cpu + .fetch_instruction() + .expect("Failed to fetch instruction"); println!("{:08x?}: {:08x?}", cpu.pc, instr); - cpu.execute(instr); + cpu.execute(instr).expect("Failed to execute instruction"); } }