diff --git a/crates/fpvm/src/mips/mips_isa.rs b/crates/fpvm/src/mips/mips_isa.rs index 2a12bca..502649e 100644 --- a/crates/fpvm/src/mips/mips_isa.rs +++ b/crates/fpvm/src/mips/mips_isa.rs @@ -257,13 +257,17 @@ mod test { #[test] fn decode_j_type() { + #[allow(clippy::unusual_byte_groupings)] let example_j_type = 0b010101_10101010101010101010101010; + assert_eq!(JType::decode(example_j_type).unwrap().address, 0b10101010101010101010101010); } #[test] fn decode_r_type() { + #[allow(clippy::unusual_byte_groupings)] let example_r_type = 0b000000_00001_00010_00011_00100_000101; + let r_type = RType::decode(example_r_type).unwrap(); assert_eq!(r_type.rs, 0b00001); assert_eq!(r_type.rt, 0b00010); @@ -274,9 +278,10 @@ mod test { #[test] fn decode_i_type() { + #[allow(clippy::unusual_byte_groupings)] let example_i_type = 0b010111_11111_00001_1111111111111111; - let i_type = IType::decode(example_i_type).unwrap(); + let i_type = IType::decode(example_i_type).unwrap(); assert_eq!(i_type.rs, 0b11111); assert_eq!(i_type.rt, 0b00001); assert_eq!(i_type.imm, 0b1111111111111111); diff --git a/crates/fpvm/src/mips/mips_linux.rs b/crates/fpvm/src/mips/mips_linux.rs index 28874df..fabaca2 100644 --- a/crates/fpvm/src/mips/mips_linux.rs +++ b/crates/fpvm/src/mips/mips_linux.rs @@ -1,4 +1,22 @@ -//! Linux-specific MIPS64 constructs. +//! Linux-specific MIPS64 constructs for Howitzer. +//! +//! This module contains the MIPS64-specific [Syscall] definitions and [Syscall] handling logic for +//! the MIPS emulator. + +use crate::{ + memory::page, + types::{Address, DoubleWord, Fd}, + InstrumentedState, MemoryReader, +}; +use anyhow::Result; +use kona_preimage::{HintRouter, PreimageFetcher}; +use std::io::{self, Read, Write}; + +/// https://www.cs.cmu.edu/afs/club/usr/jhutz/project/Linux/src/include/asm-mips/errno.h +const MIPS_EBADF: u64 = 0x9; + +/// https://www.cs.cmu.edu/afs/club/usr/jhutz/project/Linux/src/include/asm-mips/errno.h +const MIPS_EINVAL: u64 = 0x16; /// A [Syscall] is a system call that can be made to the kernel from userspace within the emulator. /// @@ -29,3 +47,202 @@ impl TryFrom for Syscall { } } } + +impl InstrumentedState +where + O: Write, + E: Write, + P: HintRouter + PreimageFetcher, +{ + /// Handles a syscall within the MIPS thread context emulation. + /// + /// ### Returns + /// - A [Result] indicating if the syscall dispatch was successful. + #[inline] + pub(crate) async fn handle_syscall(&mut self) -> Result<()> { + let mut v0 = 0; + let mut v1 = 0; + + let (a0, a1, mut a2) = + (self.state.registers[4], self.state.registers[5], self.state.registers[6]); + + if let Ok(syscall) = Syscall::try_from(self.state.registers[2]) { + match syscall { + Syscall::Mmap => { + let mut sz = a1; + + // Adjust the size to align with the page size if the size + // cannot fit within the page address mask. + let masked_size = sz & page::PAGE_ADDRESS_MASK as u64; + if masked_size != 0 { + sz += page::PAGE_SIZE as u64 - masked_size; + } + + if a0 == 0 { + v0 = self.state.heap; + self.state.heap += sz; + } else { + v0 = a0; + } + } + Syscall::Brk => { + // TODO(clabby): Prob wrong for 64-bit? + v0 = 0x40000000; + } + Syscall::Clone => { + // Clone is not supported, set the virtual register to 1. + v0 = 1; + } + Syscall::ExitGroup => { + self.state.exited = true; + self.state.exit_code = a0 as u8; + return Ok(()); + } + Syscall::Read => match (a0 as u8).try_into() { + Ok(Fd::StdIn) => { + // Nothing to do; Leave v0 and v1 zero, read nothing, and give no error. + } + Ok(Fd::PreimageRead) => { + let effective_address = (a1 & 0xFFFFFFFFFFFFFFF8) as Address; + + self.track_mem_access(effective_address)?; + let memory = self.state.memory.get_memory_doubleword(effective_address)?; + + let (data, mut data_len) = self + .read_preimage(self.state.preimage_key, self.state.preimage_offset) + .await?; + + let alignment = (a1 & 0x7) as usize; + let space = 8 - alignment; + if space < data_len { + data_len = space; + } + if (a2 as usize) < data_len { + data_len = a2 as usize; + } + + let mut out_mem = memory.to_be_bytes(); + out_mem[alignment..alignment + data_len].copy_from_slice(&data[..data_len]); + self.state.memory.set_memory_doubleword( + effective_address, + u64::from_be_bytes(out_mem), + )?; + self.state.preimage_offset += data_len as u64; + v0 = data_len as DoubleWord; + } + Ok(Fd::HintRead) => { + // Don't actually read anything into memory, just say we read it. The + // result is ignored anyways. + v0 = a2; + } + _ => { + v0 = DoubleWord::MAX; + v1 = MIPS_EBADF; + } + }, + Syscall::Write => match (a0 as u8).try_into() { + Ok(fd @ (Fd::Stdout | Fd::StdErr)) => { + let mut reader = + MemoryReader::new(&mut self.state.memory, a1 as Address, a2); + let writer: &mut dyn Write = if matches!(fd, Fd::Stdout) { + &mut self.std_out + } else { + &mut self.std_err + }; + io::copy(&mut reader, writer)?; + v0 = a2; + } + Ok(Fd::HintWrite) => { + let mut reader = + MemoryReader::new(&mut self.state.memory, a1 as Address, a2); + let mut hint_data = Vec::with_capacity(a2 as usize); + reader.read_to_end(&mut hint_data)?; + self.state.last_hint.extend(hint_data); + + // Continue processing while there is enough data to check if there are any + // hints. + while self.state.last_hint.len() >= 4 { + let hint_len = + u32::from_be_bytes(self.state.last_hint[..4].try_into()?); + if hint_len >= self.state.last_hint.len() as u32 - 4 { + let hint = &self.state.last_hint[4..4 + hint_len as usize]; + + // TODO(clabby): Ordering could be an issue here. + self.preimage_oracle + .route_hint( + String::from_utf8(hint.to_vec()) + .map_err(|e| anyhow::anyhow!(e))?, + ) + .await?; + self.state.last_hint = + self.state.last_hint[4 + hint_len as usize..].into(); + } else { + break; + } + } + v0 = a2; + } + Ok(Fd::PreimageWrite) => { + let effective_address = a1 & 0xFFFFFFFFFFFFFFF8; + self.track_mem_access(effective_address as Address)?; + + let memory = self + .state + .memory + .get_memory_doubleword(effective_address as Address)?; + let mut key = self.state.preimage_key; + let alignment = a1 & 0x7; + let space = 8 - alignment; + + if space < a2 { + a2 = space; + } + + let key_copy = key; + io::copy(&mut key_copy[a2 as usize..].as_ref(), &mut key.as_mut_slice())?; + + let _ = memory.to_be_bytes()[alignment as usize..] + .as_ref() + .read(&mut key.as_mut_slice()[32 - a2 as usize..])?; + + self.state.preimage_key = key; + self.state.preimage_offset = 0; + v0 = a2; + } + _ => { + v0 = DoubleWord::MAX; + v1 = MIPS_EBADF; + } + }, + Syscall::Fcntl => { + if a1 == 3 { + match (a0 as u8).try_into() { + Ok(Fd::StdIn | Fd::PreimageRead | Fd::HintRead) => { + v0 = 0; // O_RDONLY + } + Ok(Fd::Stdout | Fd::StdErr | Fd::PreimageWrite | Fd::HintWrite) => { + v0 = 1; // O_WRONLY + } + _ => { + v0 = DoubleWord::MAX; + v1 = MIPS_EBADF; + } + } + } else { + // The command is not recognized by this kernel. + v0 = DoubleWord::MAX; + v1 = MIPS_EINVAL; + } + } + } + } + + self.state.registers[2] = v0; + self.state.registers[7] = v1; + + self.state.pc = self.state.next_pc; + self.state.next_pc += 4; + + Ok(()) + } +} diff --git a/crates/fpvm/src/mips/mips_vm.rs b/crates/fpvm/src/mips/mips_vm.rs index 7da9524..84f02cf 100644 --- a/crates/fpvm/src/mips/mips_vm.rs +++ b/crates/fpvm/src/mips/mips_vm.rs @@ -1,21 +1,16 @@ //! This module contains the MIPS VM implementation for the [InstrumentedState]. -use super::{ - mips_isa::{IType, JType, Opcode, RType, RegImmFunction, Special2Function, SpecialFunction}, - mips_linux::Syscall, +use super::mips_isa::{ + IType, JType, Opcode, RType, RegImmFunction, Special2Function, SpecialFunction, }; use crate::{ - memory::{page, MemoryReader}, - types::{Address, DoubleWord, Fd, Word}, + types::{Address, DoubleWord, Word}, utils::sign_extend, InstrumentedState, }; use anyhow::Result; use kona_preimage::{HintRouter, PreimageFetcher}; -use std::io::{self, BufReader, Read, Write}; - -pub(crate) const MIPS_EBADF: u64 = 0x9; -pub(crate) const MIPS_EINVAL: u64 = 0x16; +use std::io::{BufReader, Read, Write}; const DOUBLEWORD_MASK: DoubleWord = DoubleWord::MAX; const WORD_MASK: DoubleWord = Word::MAX as DoubleWord; @@ -453,198 +448,6 @@ where } } - /// Handles a syscall within the MIPS thread context emulation. - /// - /// ### Returns - /// - A [Result] indicating if the syscall dispatch was successful. - #[inline] - pub(crate) async fn handle_syscall(&mut self) -> Result<()> { - let mut v0 = 0; - let mut v1 = 0; - - let (a0, a1, mut a2) = - (self.state.registers[4], self.state.registers[5], self.state.registers[6]); - - if let Ok(syscall) = Syscall::try_from(self.state.registers[2]) { - match syscall { - Syscall::Mmap => { - let mut sz = a1; - - // Adjust the size to align with the page size if the size - // cannot fit within the page address mask. - let masked_size = sz & page::PAGE_ADDRESS_MASK as u64; - if masked_size != 0 { - sz += page::PAGE_SIZE as u64 - masked_size; - } - - if a0 == 0 { - v0 = self.state.heap; - self.state.heap += sz; - } else { - v0 = a0; - } - } - Syscall::Brk => { - // TODO(clabby): Prob wrong for 64-bit? - v0 = 0x40000000; - } - Syscall::Clone => { - // Clone is not supported, set the virtual register to 1. - v0 = 1; - } - Syscall::ExitGroup => { - self.state.exited = true; - self.state.exit_code = a0 as u8; - return Ok(()); - } - Syscall::Read => match (a0 as u8).try_into() { - Ok(Fd::StdIn) => { - // Nothing to do; Leave v0 and v1 zero, read nothing, and give no error. - } - Ok(Fd::PreimageRead) => { - let effective_address = (a1 & 0xFFFFFFFFFFFFFFF8) as Address; - - self.track_mem_access(effective_address)?; - let memory = self.state.memory.get_memory_doubleword(effective_address)?; - - let (data, mut data_len) = self - .read_preimage(self.state.preimage_key, self.state.preimage_offset) - .await?; - - let alignment = (a1 & 0x7) as usize; - let space = 8 - alignment; - if space < data_len { - data_len = space; - } - if (a2 as usize) < data_len { - data_len = a2 as usize; - } - - let mut out_mem = memory.to_be_bytes(); - out_mem[alignment..alignment + data_len].copy_from_slice(&data[..data_len]); - self.state.memory.set_memory_doubleword( - effective_address, - u64::from_be_bytes(out_mem), - )?; - self.state.preimage_offset += data_len as u64; - v0 = data_len as DoubleWord; - } - Ok(Fd::HintRead) => { - // Don't actually read anything into memory, just say we read it. The - // result is ignored anyways. - v0 = a2; - } - _ => { - v0 = DoubleWord::MAX; - v1 = MIPS_EBADF; - } - }, - Syscall::Write => match (a0 as u8).try_into() { - Ok(fd @ (Fd::Stdout | Fd::StdErr)) => { - let mut reader = - MemoryReader::new(&mut self.state.memory, a1 as Address, a2); - let writer: &mut dyn Write = if matches!(fd, Fd::Stdout) { - &mut self.std_out - } else { - &mut self.std_err - }; - io::copy(&mut reader, writer)?; - v0 = a2; - } - Ok(Fd::HintWrite) => { - let mut reader = - MemoryReader::new(&mut self.state.memory, a1 as Address, a2); - let mut hint_data = Vec::with_capacity(a2 as usize); - reader.read_to_end(&mut hint_data)?; - self.state.last_hint.extend(hint_data); - - // Continue processing while there is enough data to check if there are any - // hints. - while self.state.last_hint.len() >= 4 { - let hint_len = - u32::from_be_bytes(self.state.last_hint[..4].try_into()?); - if hint_len >= self.state.last_hint.len() as u32 - 4 { - let hint = &self.state.last_hint[4..4 + hint_len as usize]; - - // TODO(clabby): Ordering could be an issue here. - self.preimage_oracle - .route_hint( - String::from_utf8(hint.to_vec()) - .map_err(|e| anyhow::anyhow!(e))?, - ) - .await?; - self.state.last_hint = - self.state.last_hint[4 + hint_len as usize..].into(); - } else { - break; - } - } - v0 = a2; - } - Ok(Fd::PreimageWrite) => { - let effective_address = a1 & 0xFFFFFFFFFFFFFFF8; - self.track_mem_access(effective_address as Address)?; - - let memory = self - .state - .memory - .get_memory_doubleword(effective_address as Address)?; - let mut key = self.state.preimage_key; - let alignment = a1 & 0x7; - let space = 8 - alignment; - - if space < a2 { - a2 = space; - } - - let key_copy = key; - io::copy(&mut key_copy[a2 as usize..].as_ref(), &mut key.as_mut_slice())?; - - let _ = memory.to_be_bytes()[alignment as usize..] - .as_ref() - .read(&mut key.as_mut_slice()[32 - a2 as usize..])?; - - self.state.preimage_key = key; - self.state.preimage_offset = 0; - v0 = a2; - } - _ => { - v0 = DoubleWord::MAX; - v1 = MIPS_EBADF; - } - }, - Syscall::Fcntl => { - if a1 == 3 { - match (a0 as u8).try_into() { - Ok(Fd::StdIn | Fd::PreimageRead | Fd::HintRead) => { - v0 = 0; // O_RDONLY - } - Ok(Fd::Stdout | Fd::StdErr | Fd::PreimageWrite | Fd::HintWrite) => { - v0 = 1; // O_WRONLY - } - _ => { - v0 = DoubleWord::MAX; - v1 = MIPS_EBADF; - } - } - } else { - // The command is not recognized by this kernel. - v0 = DoubleWord::MAX; - v1 = MIPS_EINVAL; - } - } - } - } - - self.state.registers[2] = v0; - self.state.registers[7] = v1; - - self.state.pc = self.state.next_pc; - self.state.next_pc += 4; - - Ok(()) - } - /// Handles a branch within the MIPS thread context emulation. /// /// ### Takes