mirror of
https://github.com/Noratrieb/rustv32i.git
synced 2026-01-14 13:25:01 +01:00
This commit is contained in:
parent
0144740228
commit
e9a689aa1a
6 changed files with 449 additions and 193 deletions
145
rvdc/src/lib.rs
145
rvdc/src/lib.rs
|
|
@ -5,6 +5,27 @@
|
||||||
use core::fmt::{self, Debug, Display};
|
use core::fmt::{self, Debug, Display};
|
||||||
use core::ops::RangeInclusive;
|
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.
|
/// A decoded RISC-V integer register.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct Reg(pub u8);
|
pub struct Reg(pub u8);
|
||||||
|
|
@ -109,7 +130,7 @@ impl Display for Reg {
|
||||||
///
|
///
|
||||||
/// This type is XLEN-agnostic, use the XLEN-specific accessors to get the correct value.
|
/// This type is XLEN-agnostic, use the XLEN-specific accessors to get the correct value.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Imm(u32);
|
pub struct Imm(u64);
|
||||||
|
|
||||||
impl Imm {
|
impl Imm {
|
||||||
/// The immediate `0`.
|
/// The immediate `0`.
|
||||||
|
|
@ -118,23 +139,33 @@ impl Imm {
|
||||||
|
|
||||||
/// Create a new immediate from the (if necessary) sign-extended value.
|
/// Create a new immediate from the (if necessary) sign-extended value.
|
||||||
pub const fn new_i32(value: i32) -> Self {
|
pub const fn new_i32(value: i32) -> Self {
|
||||||
Self(value as u32)
|
Self(value as i64 as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new immediate from the (if necessary) zero-extended value.
|
/// Create a new immediate from the (if necessary) zero-extended value.
|
||||||
pub const fn new_u32(value: u32) -> Self {
|
pub const fn new_u32(value: u32) -> Self {
|
||||||
Self(value)
|
Self(value as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the `u32` (RV32) value of the immediate.
|
/// Get the `u32` (RV32) value of the immediate.
|
||||||
pub const fn as_u32(self) -> u32 {
|
pub const fn as_u32(self) -> u32 {
|
||||||
self.0
|
self.0 as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the `i32` (RV32) value of the immediate.
|
/// Get the `i32` (RV32) value of the immediate.
|
||||||
pub const fn as_i32(self) -> i32 {
|
pub const fn as_i32(self) -> i32 {
|
||||||
self.0 as 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<i32> for Imm {
|
impl From<i32> for Imm {
|
||||||
|
|
@ -216,6 +247,8 @@ pub enum Inst {
|
||||||
|
|
||||||
/// Add Immediate
|
/// Add Immediate
|
||||||
Addi { imm: Imm, dest: Reg, src1: Reg },
|
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)
|
/// Set Less Than Immediate (signed)
|
||||||
Slti { imm: Imm, dest: Reg, src1: Reg },
|
Slti { imm: Imm, dest: Reg, src1: Reg },
|
||||||
/// Set Less Than Immediate Unsigned
|
/// Set Less Than Immediate Unsigned
|
||||||
|
|
@ -228,10 +261,16 @@ pub enum Inst {
|
||||||
Andi { imm: Imm, dest: Reg, src1: Reg },
|
Andi { imm: Imm, dest: Reg, src1: Reg },
|
||||||
/// Shift Left Logical Immediate
|
/// Shift Left Logical Immediate
|
||||||
Slli { imm: Imm, dest: Reg, src1: Reg },
|
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)
|
/// Shift Right Logical Immediate (unsigned)
|
||||||
Srli { imm: Imm, dest: Reg, src1: Reg },
|
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)
|
/// Shift Right Arithmetic Immediate (signed)
|
||||||
Srai { imm: Imm, dest: Reg, src1: Reg },
|
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
|
||||||
Add { dest: Reg, src1: Reg, src2: Reg },
|
Add { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
|
@ -502,6 +541,13 @@ impl Display for Inst {
|
||||||
write!(f, "addi {dest}, {src1}, {}", imm.as_i32())
|
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 {
|
Inst::Slti {
|
||||||
imm,
|
imm,
|
||||||
dest,
|
dest,
|
||||||
|
|
@ -532,16 +578,31 @@ impl Display for Inst {
|
||||||
dest,
|
dest,
|
||||||
src1: rs1,
|
src1: rs1,
|
||||||
} => write!(f, "slli {dest}, {rs1}, {}", imm.as_i32()),
|
} => write!(f, "slli {dest}, {rs1}, {}", imm.as_i32()),
|
||||||
|
Inst::SlliW {
|
||||||
|
imm,
|
||||||
|
dest,
|
||||||
|
src1: rs1,
|
||||||
|
} => write!(f, "slliw {dest}, {rs1}, {}", imm.as_i32()),
|
||||||
Inst::Srli {
|
Inst::Srli {
|
||||||
imm,
|
imm,
|
||||||
dest,
|
dest,
|
||||||
src1: rs1,
|
src1: rs1,
|
||||||
} => write!(f, "srli {dest}, {rs1}, {}", imm.as_i32()),
|
} => write!(f, "srli {dest}, {rs1}, {}", imm.as_i32()),
|
||||||
|
Inst::SrliW {
|
||||||
|
imm,
|
||||||
|
dest,
|
||||||
|
src1: rs1,
|
||||||
|
} => write!(f, "srliw {dest}, {rs1}, {}", imm.as_i32()),
|
||||||
Inst::Srai {
|
Inst::Srai {
|
||||||
imm,
|
imm,
|
||||||
dest,
|
dest,
|
||||||
src1: rs1,
|
src1: rs1,
|
||||||
} => write!(f, "srai {dest}, {rs1}, {}", imm.as_i32()),
|
} => 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 } => {
|
Inst::Add { dest, src1, src2 } => {
|
||||||
write!(f, "add {dest}, {src1}, {src2}")
|
write!(f, "add {dest}, {src1}, {src2}")
|
||||||
}
|
}
|
||||||
|
|
@ -847,12 +908,15 @@ impl Inst {
|
||||||
/// If the caller wants to avoid reading more bytes than necessary, [`Self::first_byte_is_compressed`]
|
/// 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
|
/// can be used to check, read the required bytes, and then call [`Self::decode_compressed`] or
|
||||||
/// [`Self::decode_normal`] directly.
|
/// [`Self::decode_normal`] directly.
|
||||||
pub fn decode(code: u32) -> Result<(Inst, IsCompressed), DecodeError> {
|
pub fn decode(code: u32, xlen: Xlen) -> Result<(Inst, IsCompressed), DecodeError> {
|
||||||
let is_compressed = (code & 0b11) != 0b11;
|
let is_compressed = (code & 0b11) != 0b11;
|
||||||
if is_compressed {
|
if is_compressed {
|
||||||
Ok((Self::decode_compressed(code as u16)?, IsCompressed::Yes))
|
Ok((
|
||||||
|
Self::decode_compressed(code as u16, xlen)?,
|
||||||
|
IsCompressed::Yes,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok((Self::decode_normal(code)?, IsCompressed::No))
|
Ok((Self::decode_normal(code, xlen)?, IsCompressed::No))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -867,7 +931,7 @@ impl Inst {
|
||||||
/// let inst = rvdc::Inst::decode_compressed(x).unwrap();
|
/// let inst = rvdc::Inst::decode_compressed(x).unwrap();
|
||||||
/// assert_eq!(inst, expected);
|
/// assert_eq!(inst, expected);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn decode_compressed(code: u16) -> Result<Inst, DecodeError> {
|
pub fn decode_compressed(code: u16, xlen: Xlen) -> Result<Inst, DecodeError> {
|
||||||
let code = InstCodeC(code);
|
let code = InstCodeC(code);
|
||||||
if code.0 == 0 {
|
if code.0 == 0 {
|
||||||
return Err(decode_error(code, "null instruction"));
|
return Err(decode_error(code, "null instruction"));
|
||||||
|
|
@ -1146,7 +1210,7 @@ impl Inst {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decode a normal (not compressed) instruction.
|
/// Decode a normal (not compressed) instruction.
|
||||||
pub fn decode_normal(code: u32) -> Result<Inst, DecodeError> {
|
pub fn decode_normal(code: u32, xlen: Xlen) -> Result<Inst, DecodeError> {
|
||||||
let code = InstCode(code);
|
let code = InstCode(code);
|
||||||
let inst = match code.opcode() {
|
let inst = match code.opcode() {
|
||||||
// LUI
|
// LUI
|
||||||
|
|
@ -1312,6 +1376,47 @@ impl Inst {
|
||||||
},
|
},
|
||||||
_ => return Err(decode_error(code, "OP-IMM funct3")),
|
_ => return Err(decode_error(code, "OP-IMM 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
|
// OP
|
||||||
0b0110011 => {
|
0b0110011 => {
|
||||||
let (dest, src1, src2) = (code.rd(), code.rs1(), code.rs2());
|
let (dest, src1, src2) = (code.rd(), code.rs1(), code.rs2());
|
||||||
|
|
@ -1465,6 +1570,7 @@ mod tests {
|
||||||
use crate::Imm;
|
use crate::Imm;
|
||||||
use crate::Inst;
|
use crate::Inst;
|
||||||
use crate::Reg;
|
use crate::Reg;
|
||||||
|
use crate::Xlen;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(not(slow_tests), ignore = "cfg(slow_tests) not enabled")]
|
#[cfg_attr(not(slow_tests), ignore = "cfg(slow_tests) not enabled")]
|
||||||
|
|
@ -1476,9 +1582,20 @@ mod tests {
|
||||||
std::print!("\r{}{}", "#".repeat(done), "-".repeat(100 - done));
|
std::print!("\r{}{}", "#".repeat(done), "-".repeat(100 - done));
|
||||||
std::io::stdout().flush().unwrap();
|
std::io::stdout().flush().unwrap();
|
||||||
}
|
}
|
||||||
let _ = Inst::decode(i);
|
let _ = Inst::decode(i, Xlen::Rv32);
|
||||||
}
|
}
|
||||||
let _ = Inst::decode(u32::MAX);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -1591,7 +1708,7 @@ mod tests {
|
||||||
let start_time = std::time::Instant::now();
|
let start_time = std::time::Instant::now();
|
||||||
|
|
||||||
let insts = (start..=start.saturating_add(CHUNK_SIZE))
|
let insts = (start..=start.saturating_add(CHUNK_SIZE))
|
||||||
.filter_map(|code| Some((code, Inst::decode_normal(code).ok()?)))
|
.filter_map(|code| Some((code, Inst::decode_normal(code, Xlen::Rv32).ok()?)))
|
||||||
.filter(|(_, inst)| is_inst_supposed_to_roundtrip(inst))
|
.filter(|(_, inst)| is_inst_supposed_to_roundtrip(inst))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
|
@ -1631,7 +1748,7 @@ mod tests {
|
||||||
#[ignore = "this doesn't quite work yet because there is often a non-canonical encoding"]
|
#[ignore = "this doesn't quite work yet because there is often a non-canonical encoding"]
|
||||||
fn compressed_clang_roundtrip() {
|
fn compressed_clang_roundtrip() {
|
||||||
let insts = (0..=u16::MAX)
|
let insts = (0..=u16::MAX)
|
||||||
.filter_map(|code| Some((code, Inst::decode_compressed(code).ok()?)))
|
.filter_map(|code| Some((code, Inst::decode_compressed(code, Xlen::Rv32).ok()?)))
|
||||||
.filter(|(_, inst)| is_compressed_inst_supposed_to_roundtrip(inst))
|
.filter(|(_, inst)| is_compressed_inst_supposed_to_roundtrip(inst))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
|
|
||||||
91
src/elf.rs
91
src/elf.rs
|
|
@ -32,18 +32,30 @@ pub struct Header {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Phdr32 {
|
pub struct Phdr {
|
||||||
pub p_type: u32,
|
pub p_type: u32,
|
||||||
pub p_offset: u32,
|
pub p_offset: Offset,
|
||||||
pub p_vaddr: u32,
|
pub p_vaddr: Addr,
|
||||||
pub p_paddr: u32,
|
pub p_paddr: Addr,
|
||||||
pub p_filesz: u32,
|
pub p_filesz: u64,
|
||||||
pub p_memsz: u32,
|
pub p_memsz: u64,
|
||||||
pub p_flags: u32,
|
pub p_flags: u32,
|
||||||
pub p_align: u32,
|
pub p_align: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Elf<'_> {
|
impl Elf<'_> {
|
||||||
|
fn class(&self) -> Result<ElfClass> {
|
||||||
|
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<Header> {
|
pub fn header(&self) -> Result<Header> {
|
||||||
let (ident, rest) = self.content.split_bytes(16)?;
|
let (ident, rest) = self.content.split_bytes(16)?;
|
||||||
if ident[..4] != *b"\x7fELF" {
|
if ident[..4] != *b"\x7fELF" {
|
||||||
|
|
@ -140,8 +152,9 @@ impl Elf<'_> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn segments_32(&self) -> Result<Vec<Phdr32>> {
|
pub fn segments(&self) -> Result<Vec<Phdr>> {
|
||||||
let header = self.header()?;
|
let header = self.header()?;
|
||||||
|
let class = self.class()?;
|
||||||
|
|
||||||
let (_, phdrs) = self.content.split_bytes(header.e_phoff.0 as usize)?;
|
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)?;
|
let (mut phdrs, _) = phdrs.split_bytes((header.e_phentsize * header.e_phnum) as usize)?;
|
||||||
|
|
@ -152,25 +165,51 @@ impl Elf<'_> {
|
||||||
let phdr;
|
let phdr;
|
||||||
(phdr, phdrs) = phdrs.split_bytes(header.e_phentsize as usize)?;
|
(phdr, phdrs) = phdrs.split_bytes(header.e_phentsize as usize)?;
|
||||||
|
|
||||||
let (p_type, phdr) = phdr.split_u32()?;
|
let phdr = match class {
|
||||||
let (p_offset, phdr) = phdr.split_u32()?;
|
ElfClass::Elf32 => {
|
||||||
let (p_vaddr, phdr) = phdr.split_u32()?;
|
let (p_type, phdr) = phdr.split_u32()?;
|
||||||
let (p_paddr, phdr) = phdr.split_u32()?;
|
let (p_offset, phdr) = phdr.split_u32()?;
|
||||||
let (p_filesz, phdr) = phdr.split_u32()?;
|
let (p_vaddr, phdr) = phdr.split_u32()?;
|
||||||
let (p_memsz, phdr) = phdr.split_u32()?;
|
let (p_paddr, phdr) = phdr.split_u32()?;
|
||||||
let (p_flags, phdr) = phdr.split_u32()?;
|
let (p_filesz, phdr) = phdr.split_u32()?;
|
||||||
let (p_align, _) = phdr.split_u32()?;
|
let (p_memsz, phdr) = phdr.split_u32()?;
|
||||||
|
let (p_flags, phdr) = phdr.split_u32()?;
|
||||||
|
let (p_align, _) = phdr.split_u32()?;
|
||||||
|
|
||||||
parsed_phdrs.push(Phdr32 {
|
Phdr {
|
||||||
p_type,
|
p_type,
|
||||||
p_offset,
|
p_offset: Offset(p_offset as u64),
|
||||||
p_vaddr,
|
p_vaddr: Addr(p_vaddr as u64),
|
||||||
p_paddr,
|
p_paddr: Addr(p_paddr as u64),
|
||||||
p_filesz,
|
p_filesz: p_filesz as u64,
|
||||||
p_memsz,
|
p_memsz: p_memsz as u64,
|
||||||
p_flags,
|
p_flags,
|
||||||
p_align,
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(parsed_phdrs)
|
Ok(parsed_phdrs)
|
||||||
|
|
|
||||||
271
src/emu.rs
271
src/emu.rs
|
|
@ -20,44 +20,50 @@ impl Memory {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn slice<XLEN: XLen>(&self, addr: XLEN, len: u32) -> Result<&[u8], Status> {
|
pub fn slice<XLEN: XLen>(&self, addr: XLEN, len: XLEN) -> Result<&[u8], Status> {
|
||||||
self.mem
|
self.mem
|
||||||
.get((addr.as_usize())..)
|
.get((addr.as_usize())..)
|
||||||
.ok_or(Status::InvalidMemoryAccess(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()))
|
.ok_or(Status::InvalidMemoryAccess(addr.as_usize()))
|
||||||
}
|
}
|
||||||
pub fn slice_mut<XLEN: XLen>(&mut self, addr: XLEN, len: u32) -> Result<&mut [u8], Status> {
|
pub fn slice_mut<XLEN: XLen>(&mut self, addr: XLEN, len: XLEN) -> Result<&mut [u8], Status> {
|
||||||
self.mem
|
self.mem
|
||||||
.get_mut((addr.as_usize())..)
|
.get_mut((addr.as_usize())..)
|
||||||
.ok_or(Status::InvalidMemoryAccess(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()))
|
.ok_or(Status::InvalidMemoryAccess(addr.as_usize()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_u8<XLEN: XLen>(&self, addr: XLEN) -> Result<u8, Status> {
|
pub fn load_u8<XLEN: XLen>(&self, addr: XLEN) -> Result<u8, Status> {
|
||||||
Ok(u8::from_le_bytes(self.slice(addr, 1)?.try_into().unwrap()))
|
Ok(u8::from_le_bytes(
|
||||||
|
self.slice(addr, XLEN::from_32_z(1))?.try_into().unwrap(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
pub fn load_u16<XLEN: XLen>(&self, addr: XLEN) -> Result<u16, Status> {
|
pub fn load_u16<XLEN: XLen>(&self, addr: XLEN) -> Result<u16, Status> {
|
||||||
Ok(u16::from_le_bytes(self.slice(addr, 2)?.try_into().unwrap()))
|
Ok(u16::from_le_bytes(
|
||||||
|
self.slice(addr, XLEN::from_32_z(2))?.try_into().unwrap(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
pub fn load_u32<XLEN: XLen>(&self, addr: XLEN) -> Result<u32, Status> {
|
pub fn load_u32<XLEN: XLen>(&self, addr: XLEN) -> Result<u32, Status> {
|
||||||
Ok(u32::from_le_bytes(self.slice(addr, 4)?.try_into().unwrap()))
|
Ok(u32::from_le_bytes(
|
||||||
|
self.slice(addr, XLEN::from_32_z(4))?.try_into().unwrap(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
pub fn store_u8<XLEN: XLen>(&mut self, addr: XLEN, value: u8) -> Result<(), Status> {
|
pub fn store_u8<XLEN: XLen>(&mut self, addr: XLEN, value: u8) -> Result<(), Status> {
|
||||||
self.slice_mut(addr, 1)?
|
self.slice_mut(addr, XLEN::from_32_z(1))?
|
||||||
.copy_from_slice(&value.to_le_bytes());
|
.copy_from_slice(&value.to_le_bytes());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn store_u16<XLEN: XLen>(&mut self, addr: XLEN, value: u16) -> Result<(), Status> {
|
pub fn store_u16<XLEN: XLen>(&mut self, addr: XLEN, value: u16) -> Result<(), Status> {
|
||||||
self.check_align(addr, 2)?;
|
self.check_align(addr, 2)?;
|
||||||
self.slice_mut(addr, 2)?
|
self.slice_mut(addr, XLEN::from_32_z(2))?
|
||||||
.copy_from_slice(&value.to_le_bytes());
|
.copy_from_slice(&value.to_le_bytes());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn store_u32<XLEN: XLen>(&mut self, addr: XLEN, value: u32) -> Result<(), Status> {
|
pub fn store_u32<XLEN: XLen>(&mut self, addr: XLEN, value: u32) -> Result<(), Status> {
|
||||||
self.check_align(addr, 4)?;
|
self.check_align(addr, 4)?;
|
||||||
self.slice_mut(addr, 4)?
|
self.slice_mut(addr, XLEN::from_32_z(4))?
|
||||||
.copy_from_slice(&value.to_le_bytes());
|
.copy_from_slice(&value.to_le_bytes());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -139,6 +145,28 @@ fn hash_color(value: usize) -> impl Display {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<XLEN: XLen> Emulator<XLEN> {
|
impl<XLEN: XLen> Emulator<XLEN> {
|
||||||
|
pub fn new(
|
||||||
|
mem: Memory,
|
||||||
|
start: XLEN,
|
||||||
|
break_addr: XLEN,
|
||||||
|
debug: bool,
|
||||||
|
ecall_handler: Box<dyn FnMut(&mut Memory, &mut [XLEN; 32]) -> 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 {
|
pub fn start_linux(&mut self) -> Status {
|
||||||
self.setup_linux_stack().unwrap();
|
self.setup_linux_stack().unwrap();
|
||||||
|
|
||||||
|
|
@ -210,7 +238,7 @@ impl<XLEN: XLen> Emulator<XLEN> {
|
||||||
print!("0x{:x} ", self.pc.as_usize());
|
print!("0x{:x} ", self.pc.as_usize());
|
||||||
}
|
}
|
||||||
|
|
||||||
let (inst, was_compressed) = Inst::decode(code)?;
|
let (inst, was_compressed) = Inst::decode(code, XLEN::XLEN)?;
|
||||||
|
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!(
|
println!(
|
||||||
|
|
@ -649,11 +677,21 @@ impl<XLEN: XLen> Emulator<XLEN> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait XLen: Copy + PartialEq + Eq {
|
pub trait XLen: Copy + PartialEq + Eq {
|
||||||
|
type Signed;
|
||||||
|
type NextUnsigned;
|
||||||
|
type NextSigned;
|
||||||
|
|
||||||
|
const XLEN: rvdc::Xlen;
|
||||||
|
|
||||||
const ZERO: Self;
|
const ZERO: Self;
|
||||||
const SIGNED_MIN: Self;
|
const SIGNED_MIN: Self;
|
||||||
const MAX: Self;
|
const MAX: Self;
|
||||||
|
|
||||||
fn from_bool(v: bool) -> Self;
|
fn switch<R>(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_8_z(v: u8) -> Self {
|
fn from_8_z(v: u8) -> Self {
|
||||||
Self::from_32_z(v as u32)
|
Self::from_32_z(v as u32)
|
||||||
}
|
}
|
||||||
|
|
@ -704,14 +742,107 @@ pub trait XLen: Copy + PartialEq + Eq {
|
||||||
fn unsigned_rem(self, other: Self) -> Self;
|
fn unsigned_rem(self, other: Self) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XLen for u32 {
|
macro_rules! xlen_impl {
|
||||||
const ZERO: Self = 0;
|
() => {
|
||||||
const SIGNED_MIN: Self = i32::MIN as u32;
|
const ZERO: Self = 0;
|
||||||
const MAX: Self = u32::MAX;
|
const SIGNED_MIN: Self = Self::Signed::MIN as Self;
|
||||||
|
const MAX: Self = Self::MAX;
|
||||||
|
|
||||||
fn from_bool(v: bool) -> Self {
|
fn as_usize(self) -> usize {
|
||||||
v as u32
|
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 XLEN: rvdc::Xlen = rvdc::Xlen::Rv32;
|
||||||
|
|
||||||
|
fn switch<R>(self, on_32: impl FnOnce(u32) -> R, _: impl FnOnce(u64) -> R) -> R {
|
||||||
|
on_32(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_32_s(v: u32) -> Self {
|
fn from_32_s(v: u32) -> Self {
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
@ -721,84 +852,30 @@ impl XLen for u32 {
|
||||||
fn from_imm(v: Imm) -> Self {
|
fn from_imm(v: Imm) -> Self {
|
||||||
v.as_u32()
|
v.as_u32()
|
||||||
}
|
}
|
||||||
fn as_usize(self) -> usize {
|
|
||||||
self as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
fn truncate32(self) -> u32 {
|
xlen_impl!();
|
||||||
self
|
}
|
||||||
}
|
|
||||||
|
impl XLen for u64 {
|
||||||
fn add(self, other: Self) -> Self {
|
type Signed = i64;
|
||||||
self.wrapping_add(other)
|
type NextUnsigned = u128;
|
||||||
}
|
type NextSigned = i128;
|
||||||
fn sub(self, other: Self) -> Self {
|
|
||||||
self.wrapping_sub(other)
|
const XLEN: rvdc::Xlen = rvdc::Xlen::Rv64;
|
||||||
}
|
|
||||||
fn and(self, other: Self) -> Self {
|
fn switch<R>(self, _: impl FnOnce(u32) -> R, on_64: impl FnOnce(u64) -> R) -> R {
|
||||||
self & other
|
on_64(self)
|
||||||
}
|
}
|
||||||
fn or(self, other: Self) -> Self {
|
|
||||||
self | other
|
fn from_32_s(v: u32) -> Self {
|
||||||
}
|
v as i32 as i64 as u64
|
||||||
fn xor(self, other: Self) -> Self {
|
}
|
||||||
self ^ other
|
fn from_32_z(v: u32) -> Self {
|
||||||
}
|
v as u64
|
||||||
fn signed_lt(self, other: Self) -> bool {
|
}
|
||||||
(self as i32) < (other as i32)
|
fn from_imm(v: Imm) -> Self {
|
||||||
}
|
v.as_u64()
|
||||||
fn unsigned_lt(self, other: Self) -> bool {
|
}
|
||||||
self < other
|
|
||||||
}
|
xlen_impl!();
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
38
src/lib.rs
38
src/lib.rs
|
|
@ -9,13 +9,14 @@ const MEMORY_SIZE: usize = 2 << 21;
|
||||||
pub fn execute_linux_elf(
|
pub fn execute_linux_elf(
|
||||||
elf: &[u8],
|
elf: &[u8],
|
||||||
debug: bool,
|
debug: bool,
|
||||||
break_addr: u32,
|
break_addr: u64,
|
||||||
ecall_handler: Box<dyn FnMut(&mut emu::Memory, &mut [u32; 32]) -> Result<(), emu::Status>>,
|
ecall_handler32: Box<dyn FnMut(&mut emu::Memory, &mut [u32; 32]) -> Result<(), emu::Status>>,
|
||||||
|
ecall_handler64: Box<dyn FnMut(&mut emu::Memory, &mut [u64; 32]) -> Result<(), emu::Status>>,
|
||||||
) -> eyre::Result<emu::Status> {
|
) -> eyre::Result<emu::Status> {
|
||||||
let elf = elf::Elf { content: elf };
|
let elf = elf::Elf { content: elf };
|
||||||
let header = elf.header()?;
|
let header = elf.header()?;
|
||||||
|
|
||||||
let segments = elf.segments_32()?;
|
let segments = elf.segments()?;
|
||||||
|
|
||||||
let mut mem = emu::Memory {
|
let mut mem = emu::Memory {
|
||||||
mem: vec![0; MEMORY_SIZE],
|
mem: vec![0; MEMORY_SIZE],
|
||||||
|
|
@ -30,13 +31,13 @@ pub fn execute_linux_elf(
|
||||||
if phdr.p_filesz > 0 {
|
if phdr.p_filesz > 0 {
|
||||||
let contents = &elf
|
let contents = &elf
|
||||||
.content
|
.content
|
||||||
.get((phdr.p_offset as usize)..)
|
.get((phdr.p_offset.0 as usize)..)
|
||||||
.ok_or_eyre("invalid offset")?
|
.ok_or_eyre("invalid offset")?
|
||||||
.get(..(phdr.p_filesz as usize))
|
.get(..(phdr.p_filesz as usize))
|
||||||
.ok_or_eyre("invalid offset")?;
|
.ok_or_eyre("invalid offset")?;
|
||||||
|
|
||||||
mem.mem
|
mem.mem
|
||||||
.get_mut((phdr.p_vaddr as usize)..)
|
.get_mut((phdr.p_vaddr.0 as usize)..)
|
||||||
.ok_or_eyre("invalid offset")?
|
.ok_or_eyre("invalid offset")?
|
||||||
.get_mut(..(phdr.p_filesz as usize))
|
.get_mut(..(phdr.p_filesz as usize))
|
||||||
.ok_or_eyre("invalid offset")?
|
.ok_or_eyre("invalid offset")?
|
||||||
|
|
@ -61,19 +62,16 @@ pub fn execute_linux_elf(
|
||||||
|
|
||||||
let start = header.e_entry;
|
let start = header.e_entry;
|
||||||
|
|
||||||
let mut emu = emu::Emulator {
|
match header.class {
|
||||||
mem,
|
elf::ElfClass::Elf32 => {
|
||||||
xreg: [0; 32],
|
let mut emu =
|
||||||
xreg0_value: 0,
|
emu::Emulator::<u32>::new(mem, start.0 as u32, break_addr as u32, debug, ecall_handler32);
|
||||||
pc: start.0 as u32,
|
Ok(emu.start_linux())
|
||||||
reservation_set: None,
|
}
|
||||||
|
elf::ElfClass::Elf64 => {
|
||||||
is_breaking: false,
|
let mut emu =
|
||||||
|
emu::Emulator::<u64>::new(mem, start.0, break_addr, debug, ecall_handler64);
|
||||||
break_pc: break_addr,
|
Ok(emu.start_linux())
|
||||||
debug,
|
}
|
||||||
ecall_handler,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
Ok(emu.start_linux())
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
64
src/main.rs
64
src/main.rs
|
|
@ -1,7 +1,7 @@
|
||||||
use std::io::Write;
|
use std::{cell::RefCell, io::Write, sync::Arc};
|
||||||
|
|
||||||
use eyre::eyre;
|
use eyre::eyre;
|
||||||
use rustv32i::emu::{self, Memory};
|
use rustv32i::emu::{self, Memory, XLen};
|
||||||
use rvdc::Reg;
|
use rvdc::Reg;
|
||||||
|
|
||||||
fn main() -> eyre::Result<()> {
|
fn main() -> eyre::Result<()> {
|
||||||
|
|
@ -12,22 +12,26 @@ fn main() -> eyre::Result<()> {
|
||||||
.nth(1)
|
.nth(1)
|
||||||
.map(|addr| {
|
.map(|addr| {
|
||||||
if let Some(addr) = addr.strip_prefix("0x") {
|
if let Some(addr) = addr.strip_prefix("0x") {
|
||||||
u32::from_str_radix(addr, 16)
|
u64::from_str_radix(addr, 16)
|
||||||
} else {
|
} else {
|
||||||
u32::from_str_radix(&addr, 10)
|
u64::from_str_radix(&addr, 10)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap_or(Ok(0))?;
|
.unwrap_or(Ok(0))?;
|
||||||
|
|
||||||
let debug = std::env::args().any(|arg| arg == "--debug");
|
let debug = std::env::args().any(|arg| arg == "--debug");
|
||||||
|
|
||||||
let mut syscall_state = SyscallState { set_child_tid: 0 };
|
let syscall_state = Arc::new(RefCell::new(SyscallState { set_child_tid: 0 }));
|
||||||
|
let syscall_state64 = syscall_state.clone();
|
||||||
|
|
||||||
let status = rustv32i::execute_linux_elf(
|
let status = rustv32i::execute_linux_elf(
|
||||||
&content,
|
&content,
|
||||||
debug,
|
debug,
|
||||||
break_addr,
|
break_addr,
|
||||||
Box::new(move |mem, xreg| ecall_handler(mem, xreg, &mut syscall_state)),
|
Box::new(move |mem, xreg| ecall_handler::<u32>(mem, xreg, &mut syscall_state.borrow_mut())),
|
||||||
|
Box::new(move |mem, xreg| {
|
||||||
|
ecall_handler::<u64>(mem, xreg, &mut syscall_state64.borrow_mut())
|
||||||
|
}),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
std::io::stdout().flush()?;
|
std::io::stdout().flush()?;
|
||||||
|
|
@ -44,12 +48,12 @@ fn main() -> eyre::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SyscallState {
|
struct SyscallState {
|
||||||
set_child_tid: u32,
|
set_child_tid: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ecall_handler(
|
fn ecall_handler<XLEN: Into<u64> + From<u32> + XLen>(
|
||||||
mem: &mut Memory,
|
mem: &mut Memory,
|
||||||
xreg: &mut [u32; 32],
|
xreg: &mut [XLEN; 32],
|
||||||
syscall_state: &mut SyscallState,
|
syscall_state: &mut SyscallState,
|
||||||
) -> Result<(), emu::Status> {
|
) -> Result<(), emu::Status> {
|
||||||
let nr = xreg[Reg::A7.0 as usize];
|
let nr = xreg[Reg::A7.0 as usize];
|
||||||
|
|
@ -60,22 +64,22 @@ fn ecall_handler(
|
||||||
|
|
||||||
// https://jborza.com/post/2021-05-11-riscv-linux-syscalls/
|
// https://jborza.com/post/2021-05-11-riscv-linux-syscalls/
|
||||||
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h
|
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h
|
||||||
match nr {
|
match nr.into() {
|
||||||
// ioctl
|
// ioctl
|
||||||
29 => {
|
29 => {
|
||||||
let fd = arg0;
|
let fd = arg0;
|
||||||
let request = arg1;
|
let request = arg1;
|
||||||
|
|
||||||
match request {
|
match request.into() {
|
||||||
// TIOCGWINSZ
|
// TIOCGWINSZ
|
||||||
0x5413 => {
|
0x5413 => {
|
||||||
let wsz_ptr = xreg[Reg::A2.0 as usize];
|
let wsz_ptr = xreg[Reg::A2.0 as usize].into();
|
||||||
|
|
||||||
let mut wsz: libc::winsize = unsafe { std::mem::zeroed() };
|
let mut wsz: libc::winsize = unsafe { std::mem::zeroed() };
|
||||||
|
|
||||||
let r = unsafe { libc::ioctl(fd as i32, libc::TIOCGWINSZ, &mut wsz) };
|
let r = unsafe { libc::ioctl(fd.into() as i32, libc::TIOCGWINSZ, &mut wsz) };
|
||||||
|
|
||||||
xreg[Reg::A0.0 as usize] = r as u32;
|
xreg[Reg::A0.0 as usize] = (r as u32).into();
|
||||||
if r >= 0 {
|
if r >= 0 {
|
||||||
mem.store_u16(wsz_ptr, wsz.ws_row)?;
|
mem.store_u16(wsz_ptr, wsz.ws_row)?;
|
||||||
mem.store_u16(wsz_ptr + 2, wsz.ws_col)?;
|
mem.store_u16(wsz_ptr + 2, wsz.ws_col)?;
|
||||||
|
|
@ -83,16 +87,16 @@ fn ecall_handler(
|
||||||
mem.store_u16(wsz_ptr + 6, wsz.ws_ypixel)?;
|
mem.store_u16(wsz_ptr + 6, wsz.ws_ypixel)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => todo!("unknown ioctl: {request}"),
|
_ => todo!("unknown ioctl: {}", request.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// read
|
// read
|
||||||
63 => {
|
63 => {
|
||||||
let fd = arg0;
|
let fd = arg0.into();
|
||||||
let ptr = xreg[Reg::A1.0 as usize];
|
let ptr = xreg[Reg::A1.0 as usize];
|
||||||
let len = xreg[Reg::A2.0 as usize];
|
let len = xreg[Reg::A2.0 as usize];
|
||||||
|
|
||||||
let buf = mem.slice_mut(ptr, len)?;
|
let buf = mem.slice_mut::<XLEN>(ptr, len)?;
|
||||||
|
|
||||||
let len = unsafe { libc::read(fd as i32, buf.as_mut_ptr().cast(), buf.len()) };
|
let len = unsafe { libc::read(fd as i32, buf.as_mut_ptr().cast(), buf.len()) };
|
||||||
let ret = if len < 0 {
|
let ret = if len < 0 {
|
||||||
|
|
@ -101,11 +105,11 @@ fn ecall_handler(
|
||||||
len as u32
|
len as u32
|
||||||
};
|
};
|
||||||
|
|
||||||
xreg[Reg::A0.0 as usize] = ret;
|
xreg[Reg::A0.0 as usize] = ret.into();
|
||||||
}
|
}
|
||||||
// write
|
// write
|
||||||
64 => {
|
64 => {
|
||||||
let fd = arg0;
|
let fd = arg0.into();
|
||||||
let ptr = xreg[Reg::A1.0 as usize];
|
let ptr = xreg[Reg::A1.0 as usize];
|
||||||
let len = xreg[Reg::A2.0 as usize];
|
let len = xreg[Reg::A2.0 as usize];
|
||||||
|
|
||||||
|
|
@ -118,13 +122,13 @@ fn ecall_handler(
|
||||||
len as u32
|
len as u32
|
||||||
};
|
};
|
||||||
|
|
||||||
xreg[Reg::A0.0 as usize] = ret;
|
xreg[Reg::A0.0 as usize] = ret.into();
|
||||||
}
|
}
|
||||||
// https://man7.org/linux/man-pages/man3/writev.3p.html
|
// https://man7.org/linux/man-pages/man3/writev.3p.html
|
||||||
66 => {
|
66 => {
|
||||||
let fd = arg0;
|
let fd = arg0.into();
|
||||||
let iovec = arg1;
|
let iovec = arg1.into();
|
||||||
let iovcnt = arg2;
|
let iovcnt = arg2.into();
|
||||||
|
|
||||||
let mut written = 0;
|
let mut written = 0;
|
||||||
|
|
||||||
|
|
@ -142,27 +146,27 @@ fn ecall_handler(
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ret as i32) < 0 {
|
if (ret as i32) < 0 {
|
||||||
xreg[Reg::A0.0 as usize] = ret;
|
xreg[Reg::A0.0 as usize] = ret.into();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
} else {
|
||||||
written += ret;
|
written += ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xreg[Reg::A0.0 as usize] = written;
|
xreg[Reg::A0.0 as usize] = written.into();
|
||||||
}
|
}
|
||||||
// exit | exit_group
|
// exit | exit_group
|
||||||
93 | 94 => {
|
93 | 94 => {
|
||||||
return Err(emu::Status::Exit {
|
return Err(emu::Status::Exit {
|
||||||
code: xreg[Reg::A0.0 as usize] as i32,
|
code: xreg[Reg::A0.0 as usize].into() as i32,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// <https://man7.org/linux/man-pages/man2/set_tid_address.2.html>
|
// <https://man7.org/linux/man-pages/man2/set_tid_address.2.html>
|
||||||
96 => {
|
96 => {
|
||||||
let tidptr = arg0;
|
let tidptr = arg0;
|
||||||
syscall_state.set_child_tid = tidptr;
|
syscall_state.set_child_tid = tidptr.into();
|
||||||
|
|
||||||
xreg[Reg::A0.0 as usize] = 1; // thread ID
|
xreg[Reg::A0.0 as usize] = 1.into(); // thread ID
|
||||||
}
|
}
|
||||||
// ppoll - called for some stdin/stdout/stderr check.
|
// ppoll - called for some stdin/stdout/stderr check.
|
||||||
414 => {
|
414 => {
|
||||||
|
|
@ -170,10 +174,10 @@ fn ecall_handler(
|
||||||
// and opens /dev/null for them if they are.
|
// and opens /dev/null for them if they are.
|
||||||
// They're always valid here, so just get out.
|
// They're always valid here, so just get out.
|
||||||
|
|
||||||
xreg[Reg::A0.0 as usize] = 0;
|
xreg[Reg::A0.0 as usize] = 0.into();
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
todo!("unkonwn syscall: {nr}");
|
todo!("unkonwn syscall: {}", nr.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,21 +27,30 @@ fn check() -> eyre::Result<()> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
test_case(tmpdir.path(), &file, name, "rv32ima")?;
|
test_case(tmpdir.path(), &file, name, 32, "rv32ima")?;
|
||||||
test_case(tmpdir.path(), &file, name, "rv32imac")?;
|
test_case(tmpdir.path(), &file, name, 32, "rv32imac")?;
|
||||||
|
|
||||||
|
// test_case(tmpdir.path(), &file, name, 64, "rv64ima")?;
|
||||||
|
// test_case(tmpdir.path(), &file, name, 64, "rv64imac")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_case(tmpdir: &Path, file: &DirEntry, name: &str, march: &str) -> eyre::Result<()> {
|
fn test_case(
|
||||||
|
tmpdir: &Path,
|
||||||
|
file: &DirEntry,
|
||||||
|
name: &str,
|
||||||
|
size: u8,
|
||||||
|
march: &str,
|
||||||
|
) -> eyre::Result<()> {
|
||||||
let name = format!("{name} ({march})");
|
let name = format!("{name} ({march})");
|
||||||
write!(std::io::stdout(), "test {name} ...")?;
|
write!(std::io::stdout(), "test {name} ...")?;
|
||||||
std::io::stdout().flush()?;
|
std::io::stdout().flush()?;
|
||||||
|
|
||||||
eprintln!("---- START TEST {name} -----");
|
eprintln!("---- START TEST {name} -----");
|
||||||
|
|
||||||
let output = build(&tmpdir, &file.path(), march).wrap_err(format!("building {name}"))?;
|
let output = build(&tmpdir, &file.path(), size, march).wrap_err(format!("building {name}"))?;
|
||||||
let content =
|
let content =
|
||||||
std::fs::read(&output).wrap_err(format!("reading output from {}", output.display()))?;
|
std::fs::read(&output).wrap_err(format!("reading output from {}", output.display()))?;
|
||||||
|
|
||||||
|
|
@ -60,6 +69,17 @@ fn test_case(tmpdir: &Path, file: &DirEntry, name: &str, march: &str) -> eyre::R
|
||||||
Err(rustv32i::emu::Status::Trap("wrong syscall"))
|
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"))?;
|
.wrap_err(format!("{name} failed"))?;
|
||||||
|
|
||||||
|
|
@ -71,11 +91,12 @@ fn test_case(tmpdir: &Path, file: &DirEntry, name: &str, march: &str) -> eyre::R
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(tmpdir: &Path, src: &Path, march: &str) -> eyre::Result<PathBuf> {
|
fn build(tmpdir: &Path, src: &Path, size: u8, march: &str) -> eyre::Result<PathBuf> {
|
||||||
let out_path = tmpdir.join(Path::new(src.file_name().unwrap()).with_extension(""));
|
let out_path = tmpdir.join(Path::new(src.file_name().unwrap()).with_extension(""));
|
||||||
|
|
||||||
let mut cmd = std::process::Command::new("clang");
|
let mut cmd = std::process::Command::new("clang");
|
||||||
cmd.args(["-target", "riscv32-unknown-none-elf", "-nostdlib"]);
|
cmd.args(["-target", &format!("riscv{size}-unknown-none-elf")]);
|
||||||
|
cmd.arg("-nostdlib");
|
||||||
cmd.arg(format!("-march={march}"));
|
cmd.arg(format!("-march={march}"));
|
||||||
cmd.arg(src);
|
cmd.arg(src);
|
||||||
cmd.arg("-o");
|
cmd.arg("-o");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue