diff --git a/rvdc/CHANGELOG.md b/rvdc/CHANGELOG.md index 20faa39..e0c8a42 100644 --- a/rvdc/CHANGELOG.md +++ b/rvdc/CHANGELOG.md @@ -1,10 +1,8 @@ -## 0.2.0 +## 2.0.0 - BREAKING CHANGE: Make `Inst` `#[non_exhaustive]` - BREAKING CHANGE: Change immediate fields in `Inst` to `Imm` -- Improve error messages - ## 0.1.1 - Add `Fence::is_tso` diff --git a/rvdc/src/lib.rs b/rvdc/src/lib.rs index 3eec47c..7b7de4a 100644 --- a/rvdc/src/lib.rs +++ b/rvdc/src/lib.rs @@ -5,27 +5,6 @@ use core::fmt::{self, Debug, Display}; use core::ops::RangeInclusive; -/// The register size of the ISA, RV32 or RV64. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Xlen { - /// 32 bit - Rv32, - /// 64 bit - Rv64, -} - -impl Xlen { - /// Whether this is [`Xlen::Rv32`]. - pub fn is_32(self) -> bool { - matches!(self, Self::Rv32) - } - - /// Whether this is [`Xlen::Rv64`]. - pub fn is_64(self) -> bool { - matches!(self, Self::Rv64) - } -} - /// A decoded RISC-V integer register. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Reg(pub u8); @@ -130,7 +109,7 @@ impl Display for Reg { /// /// This type is XLEN-agnostic, use the XLEN-specific accessors to get the correct value. #[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct Imm(u64); +pub struct Imm(u32); impl Imm { /// The immediate `0`. @@ -139,33 +118,23 @@ impl Imm { /// Create a new immediate from the (if necessary) sign-extended value. pub const fn new_i32(value: i32) -> Self { - Self(value as i64 as u64) + Self(value as u32) } /// Create a new immediate from the (if necessary) zero-extended value. - pub const fn new_u32(value: u32) -> Self { - Self(value as u64) + pub const fn new_u32(value: u32) -> Self { + Self(value) } /// Get the `u32` (RV32) value of the immediate. pub const fn as_u32(self) -> u32 { - self.0 as u32 + self.0 } /// Get the `i32` (RV32) value of the immediate. pub const fn as_i32(self) -> i32 { self.0 as i32 } - - /// Get the `u64` (RV64) value of the immediate. - pub const fn as_u64(self) -> u64 { - self.0 as u64 - } - - /// Get the `i64` (RV64) value of the immediate. - pub const fn as_i64(self) -> i64 { - self.0 as i64 - } } impl From for Imm { @@ -247,8 +216,6 @@ pub enum Inst { /// Add Immediate Addi { imm: Imm, dest: Reg, src1: Reg }, - /// Add Immediate 32-bit (**RV64 only**) - AddiW { imm: Imm, dest: Reg, src1: Reg }, /// Set Less Than Immediate (signed) Slti { imm: Imm, dest: Reg, src1: Reg }, /// Set Less Than Immediate Unsigned @@ -261,16 +228,10 @@ pub enum Inst { Andi { imm: Imm, dest: Reg, src1: Reg }, /// Shift Left Logical Immediate Slli { imm: Imm, dest: Reg, src1: Reg }, - /// Shift Left Logical Immediate 32-bit (**RV64 only**) - SlliW { imm: Imm, dest: Reg, src1: Reg }, /// Shift Right Logical Immediate (unsigned) Srli { imm: Imm, dest: Reg, src1: Reg }, - /// Shift Right Logical Immediate (unsigned) 32-bit (**RV64 only**) - SrliW { imm: Imm, dest: Reg, src1: Reg }, /// Shift Right Arithmetic Immediate (signed) Srai { imm: Imm, dest: Reg, src1: Reg }, - /// Shift Right Arithmetic Immediate (signed) 32-bit (**RV64 only**) - SraiW { imm: Imm, dest: Reg, src1: Reg }, /// Add Add { dest: Reg, src1: Reg, src2: Reg }, @@ -541,13 +502,6 @@ impl Display for Inst { write!(f, "addi {dest}, {src1}, {}", imm.as_i32()) } } - Inst::AddiW { imm, dest, src1 } => { - if imm.as_u32() == 0 { - write!(f, "sext.w {dest}, {src1}") - } else { - write!(f, "addi.w {dest}, {src1}, {}", imm.as_i32()) - } - } Inst::Slti { imm, dest, @@ -578,31 +532,16 @@ impl Display for Inst { dest, src1: rs1, } => write!(f, "slli {dest}, {rs1}, {}", imm.as_i32()), - Inst::SlliW { - imm, - dest, - src1: rs1, - } => write!(f, "slliw {dest}, {rs1}, {}", imm.as_i32()), Inst::Srli { imm, dest, src1: rs1, } => write!(f, "srli {dest}, {rs1}, {}", imm.as_i32()), - Inst::SrliW { - imm, - dest, - src1: rs1, - } => write!(f, "srliw {dest}, {rs1}, {}", imm.as_i32()), Inst::Srai { imm, dest, src1: rs1, } => write!(f, "srai {dest}, {rs1}, {}", imm.as_i32()), - Inst::SraiW { - imm, - dest, - src1: rs1, - } => write!(f, "sraiw {dest}, {rs1}, {}", imm.as_i32()), Inst::Add { dest, src1, src2 } => { write!(f, "add {dest}, {src1}, {src2}") } @@ -908,15 +847,12 @@ impl Inst { /// If the caller wants to avoid reading more bytes than necessary, [`Self::first_byte_is_compressed`] /// can be used to check, read the required bytes, and then call [`Self::decode_compressed`] or /// [`Self::decode_normal`] directly. - pub fn decode(code: u32, xlen: Xlen) -> Result<(Inst, IsCompressed), DecodeError> { + pub fn decode(code: u32) -> Result<(Inst, IsCompressed), DecodeError> { let is_compressed = (code & 0b11) != 0b11; if is_compressed { - Ok(( - Self::decode_compressed(code as u16, xlen)?, - IsCompressed::Yes, - )) + Ok((Self::decode_compressed(code as u16)?, IsCompressed::Yes)) } else { - Ok((Self::decode_normal(code, xlen)?, IsCompressed::No)) + Ok((Self::decode_normal(code)?, IsCompressed::No)) } } @@ -931,7 +867,7 @@ impl Inst { /// let inst = rvdc::Inst::decode_compressed(x).unwrap(); /// assert_eq!(inst, expected); /// ``` - pub fn decode_compressed(code: u16, xlen: Xlen) -> Result { + pub fn decode_compressed(code: u16) -> Result { let code = InstCodeC(code); if code.0 == 0 { return Err(decode_error(code, "null instruction")); @@ -964,7 +900,7 @@ impl Inst { src: code.rs2_short(), base: code.rs1_short(), }, - _ => return Err(decode_error(code, "C0 funct3")), + _ => return Err(decode_error(code, "funct3")), }, // C1 0b01 => match code.funct3() { @@ -1001,7 +937,7 @@ impl Inst { // C.SRLI -> srli \rd', \rd', \imm 0b00 => { if bit12 != 0 { - return Err(decode_error(code, "C.SRLI imm")); + return Err(decode_error(code, "imm")); } Inst::Srli { @@ -1013,7 +949,7 @@ impl Inst { // C.SRAI -> srai \rd', \rd', \imm 0b01 => { if bit12 != 0 { - return Err(decode_error(code, "C.SRLI imm")); + return Err(decode_error(code, "imm")); } Inst::Srai { @@ -1030,7 +966,7 @@ impl Inst { }, 0b11 => { if bit12 != 0 { - return Err(decode_error(code, "C1 Arith bit 12")); + return Err(decode_error(code, "bit 12")); } let funct2 = code.extract(5..=6); match funct2 { @@ -1096,7 +1032,7 @@ impl Inst { _ => { let uimm = code.immediate_s(&[(2..=6, 12), (12..=12, 17)]); if uimm.as_u32() == 0 { - return Err(decode_error(code, "C.LUI imm")); + return Err(decode_error(code, "imm")); } Inst::Lui { uimm, @@ -1129,7 +1065,7 @@ impl Inst { src1: code.rs1_short(), src2: Reg::ZERO, }, - _ => return Err(decode_error(code, "C1 funct3")), + _ => return Err(decode_error(code, "funct3")), }, // C2 0b10 => match code.funct3() { @@ -1148,7 +1084,7 @@ impl Inst { 0b010 => { let dest = code.rd(); if dest.0 == 0 { - return Err(decode_error(code, "C.LWSP rd")); + return Err(decode_error(code, "rd")); } Inst::Lw { @@ -1165,7 +1101,7 @@ impl Inst { // C.JR -> jalr zero, 0(\rs1) (0, _, 0) => { if rd_rs1.0 == 0 { - return Err(decode_error(code, "C.JR rs1")); + return Err(decode_error(code, "rs1")); } Inst::Jalr { offset: Imm::ZERO, @@ -1193,7 +1129,7 @@ impl Inst { src1: rd_rs1, src2: rs2, }, - _ => return Err(decode_error(code, "C2 funct=100 inst")), + _ => return Err(decode_error(code, "inst")), } } // C.SWSP -> sw \reg \offset(sp) @@ -1202,7 +1138,7 @@ impl Inst { src: code.rs2(), base: Reg::SP, }, - _ => return Err(decode_error(code, "C2 funct3")), + _ => return Err(decode_error(code, "funct3")), }, _ => return Err(decode_error(code, "instruction is not compressed")), }; @@ -1210,7 +1146,7 @@ impl Inst { } /// Decode a normal (not compressed) instruction. - pub fn decode_normal(code: u32, xlen: Xlen) -> Result { + pub fn decode_normal(code: u32) -> Result { let code = InstCode(code); let inst = match code.opcode() { // LUI @@ -1235,7 +1171,7 @@ impl Inst { base: code.rs1(), dest: code.rd(), }, - _ => return Err(decode_error(code, "JALR funct3")), + _ => return Err(decode_error(code, "funct3")), }, // BRANCH 0b1100011 => match code.funct3() { @@ -1269,7 +1205,7 @@ impl Inst { src1: code.rs1(), src2: code.rs2(), }, - _ => return Err(decode_error(code, "BRANCH funct3")), + _ => return Err(decode_error(code, "funct3")), }, // LOAD 0b0000011 => match code.funct3() { @@ -1298,7 +1234,7 @@ impl Inst { dest: code.rd(), base: code.rs1(), }, - _ => return Err(decode_error(code, "LOAD funct3")), + _ => return Err(decode_error(code, "funct3")), }, // STORE 0b0100011 => match code.funct3() { @@ -1317,7 +1253,7 @@ impl Inst { src: code.rs2(), base: code.rs1(), }, - _ => return Err(decode_error(code, "STORE funct3")), + _ => return Err(decode_error(code, "funct3")), }, // OP-IMM 0b0010011 => match code.funct3() { @@ -1372,51 +1308,10 @@ impl Inst { dest: code.rd(), src1: code.rs1(), }, - _ => return Err(decode_error(code, "OP-IMM funct7")), + _ => return Err(decode_error(code, "funct7")), }, - _ => return Err(decode_error(code, "OP-IMM funct3")), + _ => return Err(decode_error(code, "funct3")), }, - // OP-IMM-32 - 0b0011011 => { - if !xlen.is_64() { - return Err(decode_error(code, "OP-IMM-32 only on RV64")); - } - - match code.funct3() { - 0b000 => Inst::AddiW { - imm: code.imm_i(), - dest: code.rd(), - src1: code.rs1(), - }, - // SLLIW - 0b001 => { - if code.funct7() != 0 { - return Err(decode_error(code, "SLLIW funct7")); - } - - Inst::SlliW { - imm: Imm::new_u32(code.rs2_imm()), - dest: code.rd(), - src1: code.rs1(), - } - } - - 0b101 => match code.funct7() { - 0b0000000 => Inst::SrliW { - imm: Imm::new_u32(code.rs2_imm()), - dest: code.rd(), - src1: code.rs1(), - }, - 0b0100000 => Inst::SraiW { - imm: Imm::new_u32(code.rs2_imm()), - dest: code.rd(), - src1: code.rs1(), - }, - _ => return Err(decode_error(code, "OP-IMM-32 funct7")), - }, - _ => return Err(decode_error(code, "OP-IMM-32 funct3")), - } - } // OP 0b0110011 => { let (dest, src1, src2) = (code.rd(), code.rs1(), code.rs2()); @@ -1440,7 +1335,7 @@ impl Inst { (0b101, 0b0000001) => Inst::Divu { dest, src1, src2 }, (0b110, 0b0000001) => Inst::Rem { dest, src1, src2 }, (0b111, 0b0000001) => Inst::Remu { dest, src1, src2 }, - _ => return Err(decode_error(code, "OP funct3/funct7")), + _ => return Err(decode_error(code, "funct3/funct7")), } } // MISC-MEM @@ -1469,7 +1364,7 @@ impl Inst { src: code.rs1(), }, }, - _ => return Err(decode_error(code, "MISC-MEM funct3")), + _ => return Err(decode_error(code, "funct3")), } } // SYSTEM @@ -1478,25 +1373,25 @@ impl Inst { return Err(decode_error(code, "unimp instruction")); } if code.rd().0 != 0 { - return Err(decode_error(code, "SYSTEM rd")); + return Err(decode_error(code, "rd")); } if code.funct3() != 0 { - return Err(decode_error(code, "SYSTEM funct3")); + return Err(decode_error(code, "funct3")); } if code.rs1().0 != 0 { - return Err(decode_error(code, "SYSTEM rs1")); + return Err(decode_error(code, "rs1")); } match code.imm_i().as_u32() { 0b000000000000 => Inst::Ecall, 0b000000000001 => Inst::Ebreak, - _ => return Err(decode_error(code, "SYSTEM imm")), + _ => return Err(decode_error(code, "imm")), } } // AMO 0b00101111 => { // width must be W if code.funct3() != 0b010 { - return Err(decode_error(code, "AMO width funct3")); + return Err(decode_error(code, "funct3")); } let kind = code.extract(27..=31); @@ -1509,7 +1404,7 @@ impl Inst { // LR 0b00010 => { if code.rs2().0 != 0 { - return Err(decode_error(code, "AMO.LR rs2")); + return Err(decode_error(code, "rs2")); } Inst::LrW { @@ -1536,7 +1431,7 @@ impl Inst { 0b10100 => AmoOp::Max, 0b11000 => AmoOp::Minu, 0b11100 => AmoOp::Maxu, - _ => return Err(decode_error(code, "AMO op funct7")), + _ => return Err(decode_error(code, "funct7")), }; Inst::AmoW { order, @@ -1570,7 +1465,6 @@ mod tests { use crate::Imm; use crate::Inst; use crate::Reg; - use crate::Xlen; #[test] #[cfg_attr(not(slow_tests), ignore = "cfg(slow_tests) not enabled")] @@ -1582,20 +1476,9 @@ mod tests { std::print!("\r{}{}", "#".repeat(done), "-".repeat(100 - done)); std::io::stdout().flush().unwrap(); } - let _ = Inst::decode(i, Xlen::Rv32); + let _ = Inst::decode(i); } - let _ = Inst::decode(u32::MAX, Xlen::Rv32); - - for i in 0..u32::MAX { - if (i % (2 << 25)) == 0 { - let percent = i as f32 / (u32::MAX as f32); - let done = (100.0 * percent) as usize; - std::print!("\r{}{}", "#".repeat(done), "-".repeat(100 - done)); - std::io::stdout().flush().unwrap(); - } - let _ = Inst::decode(i, Xlen::Rv64); - } - let _ = Inst::decode(u32::MAX, Xlen::Rv64); + let _ = Inst::decode(u32::MAX); } #[test] @@ -1708,7 +1591,7 @@ mod tests { let start_time = std::time::Instant::now(); let insts = (start..=start.saturating_add(CHUNK_SIZE)) - .filter_map(|code| Some((code, Inst::decode_normal(code, Xlen::Rv32).ok()?))) + .filter_map(|code| Some((code, Inst::decode_normal(code).ok()?))) .filter(|(_, inst)| is_inst_supposed_to_roundtrip(inst)) .collect::>(); @@ -1748,7 +1631,7 @@ mod tests { #[ignore = "this doesn't quite work yet because there is often a non-canonical encoding"] fn compressed_clang_roundtrip() { let insts = (0..=u16::MAX) - .filter_map(|code| Some((code, Inst::decode_compressed(code, Xlen::Rv32).ok()?))) + .filter_map(|code| Some((code, Inst::decode_compressed(code).ok()?))) .filter(|(_, inst)| is_compressed_inst_supposed_to_roundtrip(inst)) .collect::>(); diff --git a/src/elf.rs b/src/elf.rs index fcf9d06..4cf23f1 100644 --- a/src/elf.rs +++ b/src/elf.rs @@ -32,30 +32,18 @@ pub struct Header { } #[derive(Debug)] -pub struct Phdr { +pub struct Phdr32 { pub p_type: u32, - pub p_offset: Offset, - pub p_vaddr: Addr, - pub p_paddr: Addr, - pub p_filesz: u64, - pub p_memsz: u64, + pub p_offset: u32, + pub p_vaddr: u32, + pub p_paddr: u32, + pub p_filesz: u32, + pub p_memsz: u32, pub p_flags: u32, - pub p_align: u64, + pub p_align: u32, } impl Elf<'_> { - fn class(&self) -> Result { - let (_, class) = self.content.split_u32()?; - let (class, _) = class.split_bytes(1)?; - Ok(match class[0] { - // ELFCLASS32 - 1 => ElfClass::Elf32, - // ELFCLASS64 - 2 => ElfClass::Elf64, - _ => bail!("not a ELF32 or ELF64 file (EI_CLASS={})", class[0]), - }) - } - pub fn header(&self) -> Result
{ let (ident, rest) = self.content.split_bytes(16)?; if ident[..4] != *b"\x7fELF" { @@ -152,9 +140,8 @@ impl Elf<'_> { }) } - pub fn segments(&self) -> Result> { + pub fn segments_32(&self) -> Result> { let header = self.header()?; - let class = self.class()?; let (_, phdrs) = self.content.split_bytes(header.e_phoff.0 as usize)?; let (mut phdrs, _) = phdrs.split_bytes((header.e_phentsize * header.e_phnum) as usize)?; @@ -165,51 +152,25 @@ impl Elf<'_> { let phdr; (phdr, phdrs) = phdrs.split_bytes(header.e_phentsize as usize)?; - let phdr = match class { - ElfClass::Elf32 => { - let (p_type, phdr) = phdr.split_u32()?; - let (p_offset, phdr) = phdr.split_u32()?; - let (p_vaddr, phdr) = phdr.split_u32()?; - let (p_paddr, phdr) = phdr.split_u32()?; - let (p_filesz, phdr) = phdr.split_u32()?; - let (p_memsz, phdr) = phdr.split_u32()?; - let (p_flags, phdr) = phdr.split_u32()?; - let (p_align, _) = phdr.split_u32()?; + let (p_type, phdr) = phdr.split_u32()?; + let (p_offset, phdr) = phdr.split_u32()?; + let (p_vaddr, phdr) = phdr.split_u32()?; + let (p_paddr, phdr) = phdr.split_u32()?; + let (p_filesz, phdr) = phdr.split_u32()?; + let (p_memsz, phdr) = phdr.split_u32()?; + let (p_flags, phdr) = phdr.split_u32()?; + let (p_align, _) = phdr.split_u32()?; - Phdr { - p_type, - p_offset: Offset(p_offset as u64), - p_vaddr: Addr(p_vaddr as u64), - p_paddr: Addr(p_paddr as u64), - p_filesz: p_filesz as u64, - p_memsz: p_memsz as u64, - p_flags, - p_align: p_align as u64, - } - } - ElfClass::Elf64 => { - let (p_type, phdr) = phdr.split_u32()?; - let (p_flags, phdr) = phdr.split_u32()?; - let (p_offset, phdr) = phdr.split_u64()?; - let (p_vaddr, phdr) = phdr.split_u64()?; - let (p_paddr, phdr) = phdr.split_u64()?; - let (p_filesz, phdr) = phdr.split_u64()?; - let (p_memsz, phdr) = phdr.split_u64()?; - let (p_align, _) = phdr.split_u64()?; - - Phdr { - p_type, - p_offset: Offset(p_offset), - p_vaddr: Addr(p_vaddr), - p_paddr: Addr(p_paddr), - p_filesz, - p_memsz, - p_flags, - p_align, - } - } - }; - parsed_phdrs.push(phdr); + parsed_phdrs.push(Phdr32 { + p_type, + p_offset, + p_vaddr, + p_paddr, + p_filesz, + p_memsz, + p_flags, + p_align, + }); } Ok(parsed_phdrs) diff --git a/src/emu.rs b/src/emu.rs index 2881412..6bfaa6b 100644 --- a/src/emu.rs +++ b/src/emu.rs @@ -20,50 +20,44 @@ impl Memory { Ok(()) } } - pub fn slice(&self, addr: XLEN, len: XLEN) -> Result<&[u8], Status> { + pub fn slice(&self, addr: XLEN, len: u32) -> Result<&[u8], Status> { self.mem .get((addr.as_usize())..) .ok_or(Status::InvalidMemoryAccess(addr.as_usize()))? - .get(..(len.as_usize())) + .get(..(len as usize)) .ok_or(Status::InvalidMemoryAccess(addr.as_usize())) } - pub fn slice_mut(&mut self, addr: XLEN, len: XLEN) -> Result<&mut [u8], Status> { + pub fn slice_mut(&mut self, addr: XLEN, len: u32) -> Result<&mut [u8], Status> { self.mem .get_mut((addr.as_usize())..) .ok_or(Status::InvalidMemoryAccess(addr.as_usize()))? - .get_mut(..(len.as_usize())) + .get_mut(..(len as usize)) .ok_or(Status::InvalidMemoryAccess(addr.as_usize())) } pub fn load_u8(&self, addr: XLEN) -> Result { - Ok(u8::from_le_bytes( - self.slice(addr, XLEN::from_32_z(1))?.try_into().unwrap(), - )) + Ok(u8::from_le_bytes(self.slice(addr, 1)?.try_into().unwrap())) } pub fn load_u16(&self, addr: XLEN) -> Result { - Ok(u16::from_le_bytes( - self.slice(addr, XLEN::from_32_z(2))?.try_into().unwrap(), - )) + Ok(u16::from_le_bytes(self.slice(addr, 2)?.try_into().unwrap())) } pub fn load_u32(&self, addr: XLEN) -> Result { - Ok(u32::from_le_bytes( - self.slice(addr, XLEN::from_32_z(4))?.try_into().unwrap(), - )) + Ok(u32::from_le_bytes(self.slice(addr, 4)?.try_into().unwrap())) } pub fn store_u8(&mut self, addr: XLEN, value: u8) -> Result<(), Status> { - self.slice_mut(addr, XLEN::from_32_z(1))? + self.slice_mut(addr, 1)? .copy_from_slice(&value.to_le_bytes()); Ok(()) } pub fn store_u16(&mut self, addr: XLEN, value: u16) -> Result<(), Status> { self.check_align(addr, 2)?; - self.slice_mut(addr, XLEN::from_32_z(2))? + self.slice_mut(addr, 2)? .copy_from_slice(&value.to_le_bytes()); Ok(()) } pub fn store_u32(&mut self, addr: XLEN, value: u32) -> Result<(), Status> { self.check_align(addr, 4)?; - self.slice_mut(addr, XLEN::from_32_z(4))? + self.slice_mut(addr, 4)? .copy_from_slice(&value.to_le_bytes()); Ok(()) } @@ -145,28 +139,6 @@ fn hash_color(value: usize) -> impl Display { } impl Emulator { - pub fn new( - mem: Memory, - start: XLEN, - break_addr: XLEN, - debug: bool, - ecall_handler: Box Result<(), Status>>, - ) -> Self { - Self { - mem, - xreg: [XLEN::ZERO; 32], - xreg0_value: XLEN::ZERO, - pc: start, - reservation_set: None, - - is_breaking: false, - - break_pc: break_addr, - debug, - ecall_handler, - } - } - pub fn start_linux(&mut self) -> Status { self.setup_linux_stack().unwrap(); @@ -238,7 +210,7 @@ impl Emulator { print!("0x{:x} ", self.pc.as_usize()); } - let (inst, was_compressed) = Inst::decode(code, XLEN::XLEN)?; + let (inst, was_compressed) = Inst::decode(code)?; if self.debug { println!( @@ -677,21 +649,11 @@ impl Emulator { } pub trait XLen: Copy + PartialEq + Eq { - type Signed; - type NextUnsigned; - type NextSigned; - - const XLEN: rvdc::Xlen; - const ZERO: Self; const SIGNED_MIN: Self; const MAX: Self; - fn switch(self, on_32: impl FnOnce(u32) -> R, on_64: impl FnOnce(u64) -> R) -> R; - - fn from_bool(v: bool) -> Self { - Self::from_32_z(v as u32) - } + fn from_bool(v: bool) -> Self; fn from_8_z(v: u8) -> Self { Self::from_32_z(v as u32) } @@ -742,107 +704,14 @@ pub trait XLen: Copy + PartialEq + Eq { fn unsigned_rem(self, other: Self) -> Self; } -macro_rules! xlen_impl { - () => { - const ZERO: Self = 0; - const SIGNED_MIN: Self = Self::Signed::MIN as Self; - const MAX: Self = Self::MAX; - - fn as_usize(self) -> usize { - self as usize - } - fn truncate32(self) -> u32 { - self as u32 - } - - fn add(self, other: Self) -> Self { - self.wrapping_add(other) - } - fn sub(self, other: Self) -> Self { - self.wrapping_sub(other) - } - fn and(self, other: Self) -> Self { - self & other - } - fn or(self, other: Self) -> Self { - self | other - } - fn xor(self, other: Self) -> Self { - self ^ other - } - fn signed_lt(self, other: Self) -> bool { - (self as Self::Signed) < (other as Self::Signed) - } - fn unsigned_lt(self, other: Self) -> bool { - self < other - } - fn signed_ge(self, other: Self) -> bool { - (self as Self::Signed) >= (other as Self::Signed) - } - fn unsigned_ge(self, other: Self) -> bool { - self >= other - } - fn shl(self, other: u32) -> Self { - self.wrapping_shl(other) - } - fn unsigned_shr(self, other: u32) -> Self { - self.wrapping_shr(other) - } - fn signed_shr(self, other: u32) -> Self { - ((self as Self::Signed).wrapping_shr(other)) as Self - } - fn signed_min(self, other: Self) -> Self { - (self as Self::Signed).min(other as Self::Signed) as Self - } - fn unsigned_min(self, other: Self) -> Self { - self.min(other) - } - fn signed_max(self, other: Self) -> Self { - (self as Self::Signed).max(other as Self::Signed) as Self - } - fn unsigned_max(self, other: Self) -> Self { - self.max(other) - } - fn mul_lower(self, other: Self) -> Self { - (self as Self::Signed).wrapping_mul(other as Self::Signed) as Self - } - fn signed_mul_upper(self, other: Self) -> Self { - let mul_result = (self as Self::Signed as Self::NextSigned) - .wrapping_mul(other as Self::Signed as Self::NextSigned); - let shifted = (mul_result as Self::NextUnsigned) >> Self::BITS; - shifted as Self - } - fn unsigned_mul_upper(self, other: Self) -> Self { - let shifted = ((self as Self::NextUnsigned).wrapping_mul(other as Self::NextUnsigned)) - >> Self::BITS; - shifted as Self - } - fn signed_div(self, other: Self) -> Self { - ((self as Self::Signed) / (other as Self::Signed)) as Self - } - fn unsigned_div(self, other: Self) -> Self { - self / other - } - fn signed_rem(self, other: Self) -> Self { - ((self as Self::Signed) % (other as Self::Signed)) as Self - } - fn unsigned_rem(self, other: Self) -> Self { - self % other - } - }; -} - impl XLen for u32 { - type Signed = i32; - type NextUnsigned = u64; - type NextSigned = i64; + const ZERO: Self = 0; + const SIGNED_MIN: Self = i32::MIN as u32; + const MAX: Self = u32::MAX; - const XLEN: rvdc::Xlen = rvdc::Xlen::Rv32; - - fn switch(self, on_32: impl FnOnce(u32) -> R, _: impl FnOnce(u64) -> R) -> R { - on_32(self) + fn from_bool(v: bool) -> Self { + v as u32 } - fn from_32_s(v: u32) -> Self { v } @@ -852,30 +721,84 @@ impl XLen for u32 { fn from_imm(v: Imm) -> Self { v.as_u32() } + fn as_usize(self) -> usize { + self as usize + } - xlen_impl!(); -} - -impl XLen for u64 { - type Signed = i64; - type NextUnsigned = u128; - type NextSigned = i128; - - const XLEN: rvdc::Xlen = rvdc::Xlen::Rv64; - - fn switch(self, _: impl FnOnce(u32) -> R, on_64: impl FnOnce(u64) -> R) -> R { - on_64(self) - } - - fn from_32_s(v: u32) -> Self { - v as i32 as i64 as u64 - } - fn from_32_z(v: u32) -> Self { - v as u64 - } - fn from_imm(v: Imm) -> Self { - v.as_u64() - } - - xlen_impl!(); + fn truncate32(self) -> u32 { + self + } + + fn add(self, other: Self) -> Self { + self.wrapping_add(other) + } + fn sub(self, other: Self) -> Self { + self.wrapping_sub(other) + } + fn and(self, other: Self) -> Self { + self & other + } + fn or(self, other: Self) -> Self { + self | other + } + fn xor(self, other: Self) -> Self { + self ^ other + } + fn signed_lt(self, other: Self) -> bool { + (self as i32) < (other as i32) + } + fn unsigned_lt(self, other: Self) -> bool { + self < other + } + fn signed_ge(self, other: Self) -> bool { + (self as i32) >= (other as i32) + } + fn unsigned_ge(self, other: Self) -> bool { + self >= other + } + fn shl(self, other: u32) -> Self { + self.wrapping_shl(other) + } + fn unsigned_shr(self, other: u32) -> Self { + self.wrapping_shr(other) + } + fn signed_shr(self, other: u32) -> Self { + ((self as i32).wrapping_shr(other)) as u32 + } + fn signed_min(self, other: Self) -> Self { + (self as i32).min(other as i32) as u32 + } + fn unsigned_min(self, other: Self) -> Self { + self.min(other) + } + fn signed_max(self, other: Self) -> Self { + (self as i32).max(other as i32) as u32 + } + fn unsigned_max(self, other: Self) -> Self { + self.max(other) + } + fn mul_lower(self, other: Self) -> Self { + (self as i32).wrapping_mul(other as i32) as u32 + } + fn signed_mul_upper(self, other: Self) -> Self { + let mul_result = (self as i32 as i64).wrapping_mul(other as i32 as i64); + let shifted = (mul_result as u64) >> 32; + shifted as u32 + } + fn unsigned_mul_upper(self, other: Self) -> Self { + let shifted = ((self as u64).wrapping_mul(other as u64)) >> 32; + shifted as u32 + } + fn signed_div(self, other: Self) -> Self { + ((self as i32) / (other as i32)) as u32 + } + fn unsigned_div(self, other: Self) -> Self { + self / other + } + fn signed_rem(self, other: Self) -> Self { + ((self as i32) % (other as i32)) as u32 + } + fn unsigned_rem(self, other: Self) -> Self { + self % other + } } diff --git a/src/lib.rs b/src/lib.rs index 2c8a904..934cf7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,14 +9,13 @@ const MEMORY_SIZE: usize = 2 << 21; pub fn execute_linux_elf( elf: &[u8], debug: bool, - break_addr: u64, - ecall_handler32: Box Result<(), emu::Status>>, - ecall_handler64: Box Result<(), emu::Status>>, + break_addr: u32, + ecall_handler: Box Result<(), emu::Status>>, ) -> eyre::Result { let elf = elf::Elf { content: elf }; let header = elf.header()?; - let segments = elf.segments()?; + let segments = elf.segments_32()?; let mut mem = emu::Memory { mem: vec![0; MEMORY_SIZE], @@ -31,13 +30,13 @@ pub fn execute_linux_elf( if phdr.p_filesz > 0 { let contents = &elf .content - .get((phdr.p_offset.0 as usize)..) + .get((phdr.p_offset as usize)..) .ok_or_eyre("invalid offset")? .get(..(phdr.p_filesz as usize)) .ok_or_eyre("invalid offset")?; mem.mem - .get_mut((phdr.p_vaddr.0 as usize)..) + .get_mut((phdr.p_vaddr as usize)..) .ok_or_eyre("invalid offset")? .get_mut(..(phdr.p_filesz as usize)) .ok_or_eyre("invalid offset")? @@ -62,16 +61,19 @@ pub fn execute_linux_elf( let start = header.e_entry; - match header.class { - elf::ElfClass::Elf32 => { - let mut emu = - emu::Emulator::::new(mem, start.0 as u32, break_addr as u32, debug, ecall_handler32); - Ok(emu.start_linux()) - } - elf::ElfClass::Elf64 => { - let mut emu = - emu::Emulator::::new(mem, start.0, break_addr, debug, ecall_handler64); - Ok(emu.start_linux()) - } - } + let mut emu = emu::Emulator { + mem, + xreg: [0; 32], + xreg0_value: 0, + pc: start.0 as u32, + reservation_set: None, + + is_breaking: false, + + break_pc: break_addr, + debug, + ecall_handler, + }; + + Ok(emu.start_linux()) } diff --git a/src/main.rs b/src/main.rs index 7d845ad..f70208d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ -use std::{cell::RefCell, io::Write, sync::Arc}; +use std::io::Write; use eyre::eyre; -use rustv32i::emu::{self, Memory, XLen}; +use rustv32i::emu::{self, Memory}; use rvdc::Reg; fn main() -> eyre::Result<()> { @@ -12,26 +12,22 @@ fn main() -> eyre::Result<()> { .nth(1) .map(|addr| { if let Some(addr) = addr.strip_prefix("0x") { - u64::from_str_radix(addr, 16) + u32::from_str_radix(addr, 16) } else { - u64::from_str_radix(&addr, 10) + u32::from_str_radix(&addr, 10) } }) .unwrap_or(Ok(0))?; let debug = std::env::args().any(|arg| arg == "--debug"); - let syscall_state = Arc::new(RefCell::new(SyscallState { set_child_tid: 0 })); - let syscall_state64 = syscall_state.clone(); + let mut syscall_state = SyscallState { set_child_tid: 0 }; let status = rustv32i::execute_linux_elf( &content, debug, break_addr, - Box::new(move |mem, xreg| ecall_handler::(mem, xreg, &mut syscall_state.borrow_mut())), - Box::new(move |mem, xreg| { - ecall_handler::(mem, xreg, &mut syscall_state64.borrow_mut()) - }), + Box::new(move |mem, xreg| ecall_handler(mem, xreg, &mut syscall_state)), )?; std::io::stdout().flush()?; @@ -48,12 +44,12 @@ fn main() -> eyre::Result<()> { } struct SyscallState { - set_child_tid: u64, + set_child_tid: u32, } -fn ecall_handler + From + XLen>( +fn ecall_handler( mem: &mut Memory, - xreg: &mut [XLEN; 32], + xreg: &mut [u32; 32], syscall_state: &mut SyscallState, ) -> Result<(), emu::Status> { let nr = xreg[Reg::A7.0 as usize]; @@ -64,22 +60,22 @@ fn ecall_handler + From + XLen>( // https://jborza.com/post/2021-05-11-riscv-linux-syscalls/ // https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h - match nr.into() { + match nr { // ioctl 29 => { let fd = arg0; let request = arg1; - match request.into() { + match request { // TIOCGWINSZ 0x5413 => { - let wsz_ptr = xreg[Reg::A2.0 as usize].into(); + let wsz_ptr = xreg[Reg::A2.0 as usize]; let mut wsz: libc::winsize = unsafe { std::mem::zeroed() }; - let r = unsafe { libc::ioctl(fd.into() as i32, libc::TIOCGWINSZ, &mut wsz) }; + let r = unsafe { libc::ioctl(fd as i32, libc::TIOCGWINSZ, &mut wsz) }; - xreg[Reg::A0.0 as usize] = (r as u32).into(); + xreg[Reg::A0.0 as usize] = r as u32; if r >= 0 { mem.store_u16(wsz_ptr, wsz.ws_row)?; mem.store_u16(wsz_ptr + 2, wsz.ws_col)?; @@ -87,16 +83,16 @@ fn ecall_handler + From + XLen>( mem.store_u16(wsz_ptr + 6, wsz.ws_ypixel)?; } } - _ => todo!("unknown ioctl: {}", request.into()), + _ => todo!("unknown ioctl: {request}"), } } // read 63 => { - let fd = arg0.into(); + let fd = arg0; let ptr = xreg[Reg::A1.0 as usize]; let len = xreg[Reg::A2.0 as usize]; - let buf = mem.slice_mut::(ptr, len)?; + let buf = mem.slice_mut(ptr, len)?; let len = unsafe { libc::read(fd as i32, buf.as_mut_ptr().cast(), buf.len()) }; let ret = if len < 0 { @@ -105,11 +101,11 @@ fn ecall_handler + From + XLen>( len as u32 }; - xreg[Reg::A0.0 as usize] = ret.into(); + xreg[Reg::A0.0 as usize] = ret; } // write 64 => { - let fd = arg0.into(); + let fd = arg0; let ptr = xreg[Reg::A1.0 as usize]; let len = xreg[Reg::A2.0 as usize]; @@ -122,13 +118,13 @@ fn ecall_handler + From + XLen>( len as u32 }; - xreg[Reg::A0.0 as usize] = ret.into(); + xreg[Reg::A0.0 as usize] = ret; } // https://man7.org/linux/man-pages/man3/writev.3p.html 66 => { - let fd = arg0.into(); - let iovec = arg1.into(); - let iovcnt = arg2.into(); + let fd = arg0; + let iovec = arg1; + let iovcnt = arg2; let mut written = 0; @@ -146,27 +142,27 @@ fn ecall_handler + From + XLen>( }; if (ret as i32) < 0 { - xreg[Reg::A0.0 as usize] = ret.into(); + xreg[Reg::A0.0 as usize] = ret; return Ok(()); } else { written += ret; } } - xreg[Reg::A0.0 as usize] = written.into(); + xreg[Reg::A0.0 as usize] = written; } // exit | exit_group 93 | 94 => { return Err(emu::Status::Exit { - code: xreg[Reg::A0.0 as usize].into() as i32, + code: xreg[Reg::A0.0 as usize] as i32, }); } // 96 => { let tidptr = arg0; - syscall_state.set_child_tid = tidptr.into(); + syscall_state.set_child_tid = tidptr; - xreg[Reg::A0.0 as usize] = 1.into(); // thread ID + xreg[Reg::A0.0 as usize] = 1; // thread ID } // ppoll - called for some stdin/stdout/stderr check. 414 => { @@ -174,10 +170,10 @@ fn ecall_handler + From + XLen>( // and opens /dev/null for them if they are. // They're always valid here, so just get out. - xreg[Reg::A0.0 as usize] = 0.into(); + xreg[Reg::A0.0 as usize] = 0; } _ => { - todo!("unkonwn syscall: {}", nr.into()); + todo!("unkonwn syscall: {nr}"); } } diff --git a/tests/check.rs b/tests/check.rs index 9dd4fc2..a0310c6 100644 --- a/tests/check.rs +++ b/tests/check.rs @@ -27,30 +27,21 @@ fn check() -> eyre::Result<()> { continue; } - test_case(tmpdir.path(), &file, name, 32, "rv32ima")?; - test_case(tmpdir.path(), &file, name, 32, "rv32imac")?; - - // test_case(tmpdir.path(), &file, name, 64, "rv64ima")?; - // test_case(tmpdir.path(), &file, name, 64, "rv64imac")?; + test_case(tmpdir.path(), &file, name, "rv32ima")?; + test_case(tmpdir.path(), &file, name, "rv32imac")?; } Ok(()) } -fn test_case( - tmpdir: &Path, - file: &DirEntry, - name: &str, - size: u8, - march: &str, -) -> eyre::Result<()> { +fn test_case(tmpdir: &Path, file: &DirEntry, name: &str, march: &str) -> eyre::Result<()> { let name = format!("{name} ({march})"); write!(std::io::stdout(), "test {name} ...")?; std::io::stdout().flush()?; eprintln!("---- START TEST {name} -----"); - let output = build(&tmpdir, &file.path(), size, march).wrap_err(format!("building {name}"))?; + let output = build(&tmpdir, &file.path(), march).wrap_err(format!("building {name}"))?; let content = std::fs::read(&output).wrap_err(format!("reading output from {}", output.display()))?; @@ -69,17 +60,6 @@ fn test_case( Err(rustv32i::emu::Status::Trap("wrong syscall")) } }), - Box::new(|_, xreg| { - if xreg[Reg::A7.0 as usize] == u32::MAX as u64 { - if xreg[Reg::A0.0 as usize] == 1 { - Err(rustv32i::emu::Status::Exit { code: 0 }) - } else { - Err(rustv32i::emu::Status::Trap("fail")) - } - } else { - Err(rustv32i::emu::Status::Trap("wrong syscall")) - } - }), ) .wrap_err(format!("{name} failed"))?; @@ -91,12 +71,11 @@ fn test_case( } } -fn build(tmpdir: &Path, src: &Path, size: u8, march: &str) -> eyre::Result { +fn build(tmpdir: &Path, src: &Path, march: &str) -> eyre::Result { let out_path = tmpdir.join(Path::new(src.file_name().unwrap()).with_extension("")); let mut cmd = std::process::Command::new("clang"); - cmd.args(["-target", &format!("riscv{size}-unknown-none-elf")]); - cmd.arg("-nostdlib"); + cmd.args(["-target", "riscv32-unknown-none-elf", "-nostdlib"]); cmd.arg(format!("-march={march}")); cmd.arg(src); cmd.arg("-o");