make generic

This commit is contained in:
nora 2025-03-15 12:30:39 +01:00
parent d336c4af7d
commit d7425f460f
3 changed files with 361 additions and 146 deletions

View file

@ -4,11 +4,24 @@ pub struct Elf<'a> {
pub content: &'a [u8], pub content: &'a [u8],
} }
#[derive(Debug)]
pub struct Addr(pub u64);
#[derive(Debug)]
pub struct Offset(pub u64);
#[derive(Debug)]
pub enum ElfClass {
Elf32,
Elf64,
}
#[derive(Debug)] #[derive(Debug)]
pub struct Header { pub struct Header {
pub e_entry: u32, pub class: ElfClass,
pub e_phoff: u32, pub e_entry: Addr,
pub e_shoff: u32, pub e_phoff: Offset,
pub e_shoff: Offset,
pub e_flags: u32, pub e_flags: u32,
pub e_ehsize: u16, pub e_ehsize: u16,
pub e_phentsize: u16, pub e_phentsize: u16,
@ -19,7 +32,7 @@ pub struct Header {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Phdr { pub struct Phdr32 {
pub p_type: u32, pub p_type: u32,
pub p_offset: u32, pub p_offset: u32,
pub p_vaddr: u32, pub p_vaddr: u32,
@ -36,28 +49,74 @@ impl Elf<'_> {
if ident[..4] != *b"\x7fELF" { if ident[..4] != *b"\x7fELF" {
bail!("not an elf file (invalid magic)"); bail!("not an elf file (invalid magic)");
} }
// ELFCLASS32
let class = match ident[4] {
// ELFCLASS32
1 => ElfClass::Elf32,
// ELFCLASS64
2 => ElfClass::Elf64,
_ => bail!("not a ELF32 or ELF64 file (EI_CLASS={})", ident[4]),
};
// EV_CURRENT
if ident[5] != 1 { if ident[5] != 1 {
bail!("not a ELF32 file (EI_CLASS={})", ident[5]); bail!("not a LE file (EI_DATA={})", ident[5]);
}
// EV_CURRENT
if ident[6] != 1 {
bail!("invalid ELF version (EI_VERSION={})", ident[6]);
}
// ELFOSABI_NONE
if ident[7] != 0 {
bail!("invalid OS ABI (EI_OSABI={})", ident[7]);
}
if ident[8] != 0 {
bail!("invalid OS ABI version (EI_ABIVERSION={})", ident[8]);
} }
let (e_type, rest) = rest.split_u16()?; let (e_type, rest) = rest.split_u16()?;
// ET_EXEC|ET_DYN // ET_EXEC|ET_DYN
if e_type != 2 && e_type != 3 { if e_type != 2 && e_type != 3 {
bail!("not an executable: {e_type}"); bail!("not an executable: (e_type={e_type})");
} }
let (e_machine, rest) = rest.split_u16()?; let (e_machine, rest) = rest.split_u16()?;
// EM_RISCV // EM_RISCV
if e_machine != 243 { if e_machine != 243 {
bail!("not a RISC-V executable"); bail!("not a RISC-V executable (e_machine={e_machine})");
} }
let (_e_version, rest) = rest.split_u32()?; let (e_version, rest) = rest.split_u32()?;
// e_version
if e_version != 1 {
bail!("invalid OS ABI version (e_version={e_version})");
}
let (e_entry, e_phoff, e_shoff, rest) = match class {
ElfClass::Elf32 => {
let (e_entry, rest) = rest.split_u32()?;
let (e_phoff, rest) = rest.split_u32()?;
let (e_shoff, rest) = rest.split_u32()?;
(
Addr(e_entry as u64),
Offset(e_phoff as u64),
Offset(e_shoff as u64),
rest,
)
}
ElfClass::Elf64 => {
let (e_entry, rest) = rest.split_u64()?;
let (e_phoff, rest) = rest.split_u64()?;
let (e_shoff, rest) = rest.split_u64()?;
(
Addr(e_entry as u64),
Offset(e_phoff as u64),
Offset(e_shoff as u64),
rest,
)
}
};
let (e_entry, rest) = rest.split_u32()?;
let (e_phoff, rest) = rest.split_u32()?;
let (e_shoff, rest) = rest.split_u32()?;
let (e_flags, rest) = rest.split_u32()?; let (e_flags, rest) = rest.split_u32()?;
let (e_ehsize, rest) = rest.split_u16()?; let (e_ehsize, rest) = rest.split_u16()?;
let (e_phentsize, rest) = rest.split_u16()?; let (e_phentsize, rest) = rest.split_u16()?;
@ -67,6 +126,7 @@ impl Elf<'_> {
let (e_shstrndx, _) = rest.split_u16()?; let (e_shstrndx, _) = rest.split_u16()?;
Ok(Header { Ok(Header {
class,
e_entry, e_entry,
e_phoff, e_phoff,
e_shoff, e_shoff,
@ -80,10 +140,10 @@ impl Elf<'_> {
}) })
} }
pub fn segments(&self) -> Result<Vec<Phdr>> { pub fn segments_32(&self) -> Result<Vec<Phdr32>> {
let header = self.header()?; let header = self.header()?;
let (_, phdrs) = self.content.split_bytes(header.e_phoff 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)?;
let mut parsed_phdrs = vec![]; let mut parsed_phdrs = vec![];
@ -101,7 +161,7 @@ impl Elf<'_> {
let (p_flags, phdr) = phdr.split_u32()?; let (p_flags, phdr) = phdr.split_u32()?;
let (p_align, _) = phdr.split_u32()?; let (p_align, _) = phdr.split_u32()?;
parsed_phdrs.push(Phdr { parsed_phdrs.push(Phdr32 {
p_type, p_type,
p_offset, p_offset,
p_vaddr, p_vaddr,
@ -121,6 +181,7 @@ pub trait SplitAtCheckedErr {
fn split_bytes(&self, split: usize) -> Result<(&[u8], &[u8])>; fn split_bytes(&self, split: usize) -> Result<(&[u8], &[u8])>;
fn split_u16(&self) -> Result<(u16, &[u8])>; fn split_u16(&self) -> Result<(u16, &[u8])>;
fn split_u32(&self) -> Result<(u32, &[u8])>; fn split_u32(&self) -> Result<(u32, &[u8])>;
fn split_u64(&self) -> Result<(u64, &[u8])>;
} }
impl SplitAtCheckedErr for [u8] { impl SplitAtCheckedErr for [u8] {
fn split_bytes(&self, mid: usize) -> Result<(&[u8], &[u8])> { fn split_bytes(&self, mid: usize) -> Result<(&[u8], &[u8])> {
@ -137,4 +198,8 @@ impl SplitAtCheckedErr for [u8] {
let (bytes, rest) = self.split_bytes(4)?; let (bytes, rest) = self.split_bytes(4)?;
Ok((u32::from_le_bytes(bytes.try_into().unwrap()), rest)) Ok((u32::from_le_bytes(bytes.try_into().unwrap()), rest))
} }
fn split_u64(&self) -> Result<(u64, &[u8])> {
let (bytes, rest) = self.split_bytes(8)?;
Ok((u64::from_le_bytes(bytes.try_into().unwrap()), rest))
}
} }

View file

@ -10,52 +10,52 @@ pub struct Memory {
} }
impl Memory { impl Memory {
fn check_align(&self, addr: u32, align: u32) -> Result<(), Status> { fn check_align<XLEN: XLen>(&self, addr: XLEN, align: u32) -> Result<(), Status> {
if addr % 2 != 0 { if addr.as_usize() % 2 != 0 {
Err(Status::UnaligneMemoryAccess { Err(Status::UnaligneMemoryAccess {
addr, addr: addr.as_usize(),
required_align: align, required_align: align,
}) })
} else { } else {
Ok(()) Ok(())
} }
} }
pub fn slice(&self, addr: u32, len: u32) -> Result<&[u8], Status> { pub fn slice<XLEN: XLen>(&self, addr: XLEN, len: u32) -> Result<&[u8], Status> {
self.mem self.mem
.get((addr as usize)..) .get((addr.as_usize())..)
.ok_or(Status::InvalidMemoryAccess(addr))? .ok_or(Status::InvalidMemoryAccess(addr.as_usize()))?
.get(..(len as usize)) .get(..(len as usize))
.ok_or(Status::InvalidMemoryAccess(addr)) .ok_or(Status::InvalidMemoryAccess(addr.as_usize()))
} }
pub fn slice_mut(&mut self, addr: u32, len: u32) -> Result<&mut [u8], Status> { pub fn slice_mut<XLEN: XLen>(&mut self, addr: XLEN, len: u32) -> Result<&mut [u8], Status> {
self.mem self.mem
.get_mut((addr as usize)..) .get_mut((addr.as_usize())..)
.ok_or(Status::InvalidMemoryAccess(addr))? .ok_or(Status::InvalidMemoryAccess(addr.as_usize()))?
.get_mut(..(len as usize)) .get_mut(..(len as usize))
.ok_or(Status::InvalidMemoryAccess(addr)) .ok_or(Status::InvalidMemoryAccess(addr.as_usize()))
} }
pub fn load_u8(&self, addr: u32) -> 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, 1)?.try_into().unwrap()))
} }
pub fn load_u16(&self, addr: u32) -> 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, 2)?.try_into().unwrap()))
} }
pub fn load_u32(&self, addr: u32) -> 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, 4)?.try_into().unwrap()))
} }
pub fn store_u8(&mut self, addr: u32, 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, 1)?
.copy_from_slice(&value.to_le_bytes()); .copy_from_slice(&value.to_le_bytes());
Ok(()) Ok(())
} }
pub fn store_u16(&mut self, addr: u32, 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, 2)?
.copy_from_slice(&value.to_le_bytes()); .copy_from_slice(&value.to_le_bytes());
Ok(()) Ok(())
} }
pub fn store_u32(&mut self, addr: u32, 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, 4)?
.copy_from_slice(&value.to_le_bytes()); .copy_from_slice(&value.to_le_bytes());
@ -67,9 +67,9 @@ impl Memory {
pub enum Status { pub enum Status {
Trap(&'static str), Trap(&'static str),
IllegalInstruction(DecodeError), IllegalInstruction(DecodeError),
InvalidMemoryAccess(u32), InvalidMemoryAccess(usize),
UnalignedPc(u32), UnalignedPc(u32),
UnaligneMemoryAccess { addr: u32, required_align: u32 }, UnaligneMemoryAccess { addr: usize, required_align: u32 },
Ebreak, Ebreak,
Exit { code: i32 }, Exit { code: i32 },
} }
@ -80,31 +80,31 @@ impl From<DecodeError> for Status {
} }
} }
pub struct Emulator { pub struct Emulator<XLEN> {
pub mem: Memory, pub mem: Memory,
pub xreg: [u32; 32], pub xreg: [XLEN; 32],
/// Written to insterad of xreg[0]. /// Written to insterad of xreg[0].
pub xreg0_value: u32, pub xreg0_value: XLEN,
pub pc: u32, pub pc: XLEN,
/// We need to store the most recent reservation set for LR/SC /// We need to store the most recent reservation set for LR/SC
/// to make SC fail if it's not to this address. /// to make SC fail if it's not to this address.
pub reservation_set: Option<u32>, pub reservation_set: Option<XLEN>,
pub is_breaking: bool, pub is_breaking: bool,
pub debug: bool, pub debug: bool,
pub break_pc: u32, pub break_pc: XLEN,
pub ecall_handler: Box<dyn FnMut(&mut Memory, &mut [u32; 32]) -> Result<(), Status>>, pub ecall_handler: Box<dyn FnMut(&mut Memory, &mut [XLEN; 32]) -> Result<(), Status>>,
} }
impl Index<Reg> for Emulator { impl<XLEN> Index<Reg> for Emulator<XLEN> {
type Output = u32; type Output = XLEN;
fn index(&self, index: Reg) -> &Self::Output { fn index(&self, index: Reg) -> &Self::Output {
&self.xreg[index.0 as usize] &self.xreg[index.0 as usize]
} }
} }
impl IndexMut<Reg> for Emulator { impl<XLEN> IndexMut<Reg> for Emulator<XLEN> {
fn index_mut(&mut self, index: Reg) -> &mut Self::Output { fn index_mut(&mut self, index: Reg) -> &mut Self::Output {
if index.0 == 0 { if index.0 == 0 {
&mut self.xreg0_value &mut self.xreg0_value
@ -154,7 +154,7 @@ impl Display for Reg {
} }
} }
fn hash_color(value: u32) -> impl Display { fn hash_color(value: usize) -> impl Display {
use owo_colors::*; use owo_colors::*;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
let mut w = std::collections::hash_map::DefaultHasher::new(); let mut w = std::collections::hash_map::DefaultHasher::new();
@ -177,7 +177,7 @@ fn hash_color(value: u32) -> impl Display {
) )
} }
impl Emulator { impl<XLEN: XLen> Emulator<XLEN> {
pub fn start_linux(&mut self) -> Status { pub fn start_linux(&mut self) -> Status {
self.setup_linux_stack().unwrap(); self.setup_linux_stack().unwrap();
@ -186,8 +186,8 @@ impl Emulator {
fn setup_linux_stack(&mut self) -> Result<(), Status> { fn setup_linux_stack(&mut self) -> Result<(), Status> {
// set top of stack. just some yolo address. with no values there. who needs abi? // set top of stack. just some yolo address. with no values there. who needs abi?
let sp = 4096 * 16; let sp: u32 = 4096 * 16;
self[Reg::SP] = sp; self[Reg::SP] = XLEN::from_32_z(sp);
// The x86-64 psABI has a nice diagram of the stack layout (it's not arch specific). // The x86-64 psABI has a nice diagram of the stack layout (it's not arch specific).
@ -237,7 +237,7 @@ impl Emulator {
} }
} }
fn set_pc(&mut self, pc: u32) -> Result<(), Status> { fn set_pc(&mut self, pc: XLEN) -> Result<(), Status> {
self.pc = pc; self.pc = pc;
Ok(()) Ok(())
} }
@ -246,7 +246,7 @@ impl Emulator {
let code = self.mem.load_u32(self.pc)?; let code = self.mem.load_u32(self.pc)?;
if self.debug { if self.debug {
print!("0x{:x} ", self.pc); print!("0x{:x} ", self.pc.as_usize());
} }
let (inst, was_compressed) = Inst::decode(code)?; let (inst, was_compressed) = Inst::decode(code)?;
@ -258,7 +258,7 @@ impl Emulator {
IsCompressed::Yes => "C", IsCompressed::Yes => "C",
IsCompressed::No => " ", IsCompressed::No => " ",
}, },
hash_color(self.xreg[Reg::SP.0 as usize]) hash_color(self.xreg[Reg::SP.0 as usize].as_usize())
); );
} }
@ -270,23 +270,25 @@ impl Emulator {
self.debug_interactive(); self.debug_interactive();
} }
let next_pc = self.pc.wrapping_add(match was_compressed { let next_pc = self.pc.add(match was_compressed {
IsCompressed::Yes => 2, IsCompressed::Yes => XLEN::from_32_z(2),
IsCompressed::No => 4, IsCompressed::No => XLEN::from_32_z(4),
}); });
let mut jumped = false; let mut jumped = false;
match inst { match inst {
Inst::Lui { uimm, dest } => self[dest] = uimm, Inst::Lui { uimm, dest } => self[dest] = XLEN::from_32_z(uimm),
Inst::Auipc { uimm, dest } => self[dest] = self.pc.wrapping_add(uimm), Inst::Auipc { uimm, dest } => self[dest] = self.pc.add(XLEN::from_32_z(uimm)),
Inst::Jal { offset, dest } => { Inst::Jal { offset, dest } => {
let target = self.pc.wrapping_add(offset); let target = self.pc.add(XLEN::from_32_s(offset));
self[dest] = next_pc; self[dest] = next_pc;
self.set_pc(target)?; self.set_pc(target)?;
jumped = true; jumped = true;
} }
Inst::Jalr { offset, base, dest } => { Inst::Jalr { offset, base, dest } => {
let target = self[base].wrapping_add(offset) & !1; let target = self[base]
.add(XLEN::from_32_s(offset))
.and(XLEN::from_32_s(!1));
self[dest] = next_pc; self[dest] = next_pc;
self.set_pc(target)?; self.set_pc(target)?;
jumped = true; jumped = true;
@ -294,7 +296,7 @@ impl Emulator {
Inst::Beq { offset, src1, src2 } => { Inst::Beq { offset, src1, src2 } => {
let take = self[src1] == self[src2]; let take = self[src1] == self[src2];
if take { if take {
let target = self.pc.wrapping_add(offset); let target = self.pc.add(XLEN::from_32_s(offset));
self.set_pc(target)?; self.set_pc(target)?;
jumped = true; jumped = true;
} }
@ -302,116 +304,116 @@ impl Emulator {
Inst::Bne { offset, src1, src2 } => { Inst::Bne { offset, src1, src2 } => {
let take = self[src1] != self[src2]; let take = self[src1] != self[src2];
if take { if take {
let target = self.pc.wrapping_add(offset); let target = self.pc.add(XLEN::from_32_s(offset));
self.set_pc(target)?; self.set_pc(target)?;
jumped = true; jumped = true;
} }
} }
Inst::Blt { offset, src1, src2 } => { Inst::Blt { offset, src1, src2 } => {
let take = (self[src1] as i32) < (self[src2] as i32); let take = self[src1].signed_lt(self[src2]);
if take { if take {
let target = self.pc.wrapping_add(offset); let target = self.pc.add(XLEN::from_32_s(offset));
self.set_pc(target)?; self.set_pc(target)?;
jumped = true; jumped = true;
} }
} }
Inst::Bge { offset, src1, src2 } => { Inst::Bge { offset, src1, src2 } => {
let take = (self[src1] as i32) >= (self[src2] as i32); let take = self[src1].signed_ge(self[src2]);
if take { if take {
let target = self.pc.wrapping_add(offset); let target = self.pc.add(XLEN::from_32_s(offset));
self.set_pc(target)?; self.set_pc(target)?;
jumped = true; jumped = true;
} }
} }
Inst::Bltu { offset, src1, src2 } => { Inst::Bltu { offset, src1, src2 } => {
let take = self[src1] < self[src2]; let take = self[src1].unsigned_lt(self[src2]);
if take { if take {
let target = self.pc.wrapping_add(offset); let target = self.pc.add(XLEN::from_32_s(offset));
self.set_pc(target)?; self.set_pc(target)?;
jumped = true; jumped = true;
} }
} }
Inst::Bgeu { offset, src1, src2 } => { Inst::Bgeu { offset, src1, src2 } => {
let take = self[src1] >= self[src2]; let take = self[src1].unsigned_ge(self[src2]);
if take { if take {
let target = self.pc.wrapping_add(offset); let target = self.pc.add(XLEN::from_32_s(offset));
self.set_pc(target)?; self.set_pc(target)?;
jumped = true; jumped = true;
} }
} }
Inst::Lb { offset, dest, base } => { Inst::Lb { offset, dest, base } => {
let addr = self[base].wrapping_add(offset); let addr = self[base].add(XLEN::from_32_s(offset));
self[dest] = self.mem.load_u8(addr)? as i8 as i32 as u32; self[dest] = XLEN::from_8_s(self.mem.load_u8(addr)?);
} }
Inst::Lbu { offset, dest, base } => { Inst::Lbu { offset, dest, base } => {
let addr = self[base].wrapping_add(offset); let addr = self[base].add(XLEN::from_32_s(offset));
self[dest] = self.mem.load_u8(addr)? as u32; self[dest] = XLEN::from_8_z(self.mem.load_u8(addr)?);
} }
Inst::Lh { offset, dest, base } => { Inst::Lh { offset, dest, base } => {
let addr = self[base].wrapping_add(offset); let addr = self[base].add(XLEN::from_32_s(offset));
self[dest] = self.mem.load_u16(addr)? as i16 as i32 as u32; self[dest] = XLEN::from_16_s(self.mem.load_u16(addr)?);
} }
Inst::Lhu { offset, dest, base } => { Inst::Lhu { offset, dest, base } => {
let addr = self[base].wrapping_add(offset); let addr = self[base].add(XLEN::from_32_s(offset));
self[dest] = self.mem.load_u16(addr)? as u32; self[dest] = XLEN::from_16_z(self.mem.load_u16(addr)?);
} }
Inst::Lw { offset, dest, base } => { Inst::Lw { offset, dest, base } => {
let addr = self[base].wrapping_add(offset); let addr = self[base].add(XLEN::from_32_s(offset));
self[dest] = self.mem.load_u32(addr)?; self[dest] = XLEN::from_32_s(self.mem.load_u32(addr)?);
} }
Inst::Sb { offset, src, base } => { Inst::Sb { offset, src, base } => {
let addr = self[base].wrapping_add(offset); let addr = self[base].add(XLEN::from_32_s(offset));
self.mem.store_u8(addr, self[src] as u8)?; self.mem.store_u8(addr, self[src].truncate8())?;
} }
Inst::Sh { offset, src, base } => { Inst::Sh { offset, src, base } => {
let addr = self[base].wrapping_add(offset); let addr = self[base].add(XLEN::from_32_s(offset));
self.mem.store_u16(addr, self[src] as u16)?; self.mem.store_u16(addr, self[src].truncate16())?;
} }
Inst::Sw { offset, src, base } => { Inst::Sw { offset, src, base } => {
let addr = self[base].wrapping_add(offset); let addr = self[base].add(XLEN::from_32_s(offset));
self.mem.store_u32(addr, self[src])?; self.mem.store_u32(addr, self[src].truncate32())?;
} }
Inst::Addi { imm, dest, src1 } => { Inst::Addi { imm, dest, src1 } => {
self[dest] = self[src1].wrapping_add(imm); self[dest] = self[src1].add(XLEN::from_32_s(imm));
} }
Inst::Slti { imm, dest, src1 } => { Inst::Slti { imm, dest, src1 } => {
let result = (self[src1] as i32) < (imm as i32); let result = self[src1].signed_lt(XLEN::from_32_s(imm));
self[dest] = result as u32; self[dest] = XLEN::from_bool(result);
} }
Inst::Sltiu { imm, dest, src1 } => { Inst::Sltiu { imm, dest, src1 } => {
let result = self[src1] < imm; let result = self[src1].unsigned_lt(XLEN::from_32_s(imm));
self[dest] = result as u32; self[dest] = XLEN::from_bool(result);
} }
Inst::Andi { imm, dest, src1 } => { Inst::Andi { imm, dest, src1 } => {
self[dest] = self[src1] & imm; self[dest] = self[src1].and(XLEN::from_32_s(imm));
} }
Inst::Ori { imm, dest, src1 } => { Inst::Ori { imm, dest, src1 } => {
self[dest] = self[src1] | imm; self[dest] = self[src1].or(XLEN::from_32_s(imm));
} }
Inst::Xori { imm, dest, src1 } => { Inst::Xori { imm, dest, src1 } => {
self[dest] = self[src1] ^ imm; self[dest] = self[src1].xor(XLEN::from_32_s(imm));
} }
Inst::Slli { imm, dest, src1 } => self[dest] = self[src1].wrapping_shl(imm), Inst::Slli { imm, dest, src1 } => self[dest] = self[src1].shl(imm),
Inst::Srli { imm, dest, src1 } => self[dest] = self[src1].wrapping_shr(imm), Inst::Srli { imm, dest, src1 } => self[dest] = self[src1].unsigned_shr(imm),
Inst::Srai { imm, dest, src1 } => { Inst::Srai { imm, dest, src1 } => self[dest] = self[src1].signed_shr(imm),
self[dest] = (self[src1] as i32).wrapping_shr(imm) as u32 Inst::Add { dest, src1, src2 } => self[dest] = self[src1].add(self[src2]),
} Inst::Sub { dest, src1, src2 } => self[dest] = self[src1].sub(self[src2]),
Inst::Add { dest, src1, src2 } => self[dest] = self[src1].wrapping_add(self[src2]), Inst::Sll { dest, src1, src2 } => self[dest] = self[src1].shl(self[src2].truncate32()),
Inst::Sub { dest, src1, src2 } => self[dest] = self[src1].wrapping_sub(self[src2]),
Inst::Sll { dest, src1, src2 } => self[dest] = self[src1].wrapping_shl(self[src2]),
Inst::Slt { dest, src1, src2 } => { Inst::Slt { dest, src1, src2 } => {
self[dest] = ((self[src1] as i32) < (self[src2] as i32)) as u32; self[dest] = XLEN::from_bool(self[src1].signed_lt(self[src2]));
} }
Inst::Sltu { dest, src1, src2 } => { Inst::Sltu { dest, src1, src2 } => {
self[dest] = (self[src1] < self[src2]) as u32; self[dest] = XLEN::from_bool(self[src1].unsigned_lt(self[src2]));
}
Inst::Xor { dest, src1, src2 } => self[dest] = self[src1].xor(self[src2]),
Inst::Srl { dest, src1, src2 } => {
self[dest] = self[src1].unsigned_shr(self[src2].truncate32())
} }
Inst::Xor { dest, src1, src2 } => self[dest] = self[src1] ^ self[src2],
Inst::Srl { dest, src1, src2 } => self[dest] = self[src1].wrapping_shr(self[src2]),
Inst::Sra { dest, src1, src2 } => { Inst::Sra { dest, src1, src2 } => {
self[dest] = (self[src1] as i32).wrapping_shr(self[src2]) as u32 self[dest] = self[src1].signed_shr(self[src2].truncate32())
} }
Inst::Or { dest, src1, src2 } => self[dest] = self[src1] | self[src2], Inst::Or { dest, src1, src2 } => self[dest] = self[src1].or(self[src2]),
Inst::And { dest, src1, src2 } => self[dest] = self[src1] & self[src2], Inst::And { dest, src1, src2 } => self[dest] = self[src1].and(self[src2]),
Inst::Fence { fence: _ } => { /* dont care */ } Inst::Fence { fence: _ } => { /* dont care */ }
Inst::Ecall => { Inst::Ecall => {
(self.ecall_handler)(&mut self.mem, &mut self.xreg)?; (self.ecall_handler)(&mut self.mem, &mut self.xreg)?;
@ -424,48 +426,45 @@ impl Emulator {
} }
} }
Inst::Mul { dest, src1, src2 } => { Inst::Mul { dest, src1, src2 } => {
self[dest] = ((self[src1] as i32).wrapping_mul(self[src2] as i32)) as u32; self[dest] = self[src1].mul_lower(self[src2]);
} }
Inst::Mulh { dest, src1, src2 } => { Inst::Mulh { dest, src1, src2 } => {
let mul_result = (self[src1] as i32 as i64).wrapping_mul(self[src2] as i32 as i64); self[dest] = self[src1].signed_mul_upper(self[src2]);
let shifted = (mul_result as u64) >> 32;
self[dest] = shifted as u32;
} }
Inst::Mulhsu { .. } => todo!("mulhsu"), Inst::Mulhsu { .. } => todo!("mulhsu"),
Inst::Mulhu { dest, src1, src2 } => { Inst::Mulhu { dest, src1, src2 } => {
let shifted = ((self[src1] as u64).wrapping_mul(self[src2] as u64)) >> 32; self[dest] = self[src1].unsigned_mul_upper(self[src2]);
self[dest] = shifted as u32;
} }
Inst::Div { dest, src1, src2 } => { Inst::Div { dest, src1, src2 } => {
if self[src2] == 0 { if self[src2] == XLEN::ZERO {
self[dest] = u32::MAX; self[dest] = XLEN::MAX;
} else if self[src1] == i32::MIN as u32 && self[src2] == u32::MAX { } else if self[src1] == XLEN::SIGNED_MIN && self[src2] == XLEN::MAX {
self[dest] = u32::MAX; self[dest] = XLEN::MAX;
} else { } else {
self[dest] = ((self[src1] as i32) / (self[src2] as i32)) as u32; self[dest] = self[src1].signed_div(self[src2]);
} }
} }
Inst::Divu { dest, src1, src2 } => { Inst::Divu { dest, src1, src2 } => {
if self[src2] == 0 { if self[src2] == XLEN::ZERO {
self[dest] = u32::MAX; self[dest] = XLEN::MAX;
} else { } else {
self[dest] = self[src1] / self[src2]; self[dest] = self[src1].unsigned_div(self[src2]);
} }
} }
Inst::Rem { dest, src1, src2 } => { Inst::Rem { dest, src1, src2 } => {
if self[src2] == 0 { if self[src2] == XLEN::ZERO {
self[dest] = self[src1]; self[dest] = self[src1];
} else if self[src1] == i32::MIN as u32 && self[src2] == u32::MAX { } else if self[src1] == XLEN::SIGNED_MIN && self[src2] == XLEN::MAX {
self[dest] = 0; self[dest] = XLEN::ZERO;
} else { } else {
self[dest] = ((self[src1] as i32) % (self[src2] as i32)) as u32; self[dest] = self[src1].signed_rem(self[src2]);
} }
} }
Inst::Remu { dest, src1, src2 } => { Inst::Remu { dest, src1, src2 } => {
if self[src2] == 0 { if self[src2] == XLEN::ZERO {
self[dest] = self[src1]; self[dest] = self[src1];
} else { } else {
self[dest] = self[src1] % self[src2]; self[dest] = self[src1].unsigned_rem(self[src2]);
} }
} }
Inst::AmoW { Inst::AmoW {
@ -476,19 +475,19 @@ impl Emulator {
src, src,
} => { } => {
let addr = self[addr]; let addr = self[addr];
self[dest] = self.mem.load_u32(addr)?; self[dest] = XLEN::from_32_s(self.mem.load_u32(addr)?);
let result = match op { let result = match op {
AmoOp::Swap => self[src], AmoOp::Swap => self[src],
AmoOp::Add => self[dest].wrapping_add(self[src]), AmoOp::Add => self[dest].add(self[src]),
AmoOp::Xor => self[dest] ^ self[src], AmoOp::Xor => self[dest].xor(self[src]),
AmoOp::And => self[dest] & self[src], AmoOp::And => self[dest].and(self[src]),
AmoOp::Or => self[dest] | self[src], AmoOp::Or => self[dest].or(self[src]),
AmoOp::Min => (self[dest] as i32).min(self[src] as i32) as u32, AmoOp::Min => self[dest].signed_min(self[src]),
AmoOp::Max => (self[dest] as i32).max(self[src] as i32) as u32, AmoOp::Max => self[dest].signed_max(self[src]),
AmoOp::Minu => self[dest].min(self[src]), AmoOp::Minu => self[dest].unsigned_min(self[src]),
AmoOp::Maxu => self[dest].max(self[src]), AmoOp::Maxu => self[dest].unsigned_max(self[src]),
}; };
self.mem.store_u32(addr, result)?; self.mem.store_u32(addr, result.truncate32())?;
} }
Inst::LrW { Inst::LrW {
order: _, order: _,
@ -496,7 +495,7 @@ impl Emulator {
addr, addr,
} => { } => {
let addr = self[addr]; let addr = self[addr];
self[dest] = self.mem.load_u32(addr)?; self[dest] = XLEN::from_32_s(self.mem.load_u32(addr)?);
self.reservation_set = Some(addr); self.reservation_set = Some(addr);
} }
Inst::ScW { Inst::ScW {
@ -506,11 +505,11 @@ impl Emulator {
src, src,
} => { } => {
let addr = self[addr]; let addr = self[addr];
self.mem.store_u32(addr, self[src])?; self.mem.store_u32(addr, self[src].truncate32())?;
if self.reservation_set != Some(addr) { if self.reservation_set != Some(addr) {
self[dest] = 1; // error self[dest] = XLEN::from_32_z(1); // error
} else { } else {
self[dest] = 0; // success self[dest] = XLEN::from_32_z(0); // success
} }
self.reservation_set = None; self.reservation_set = None;
} }
@ -576,14 +575,14 @@ impl Emulator {
println!("{value} (0x{value:x})"); println!("{value} (0x{value:x})");
} }
"r" => { "r" => {
let format_value = |v: u32| { let format_value = |v: usize| {
if v == 0 { if v == 0 {
format!("{:0>8x}", v.black()) format!("{:0>8x}", v.black())
} else { } else {
format!("{:0>8x}", v) format!("{:0>8x}", v)
} }
}; };
let r = |i| format_value(self.xreg[i]); let r = |i: usize| format_value(self.xreg[i].as_usize());
println!( println!(
"{}: {} | {}: {} | {}: {} | {}: {}", "{}: {} | {}: {} | {}: {} | {}: {}",
"ra".red(), "ra".red(),
@ -670,7 +669,7 @@ impl Emulator {
"t6".yellow(), "t6".yellow(),
r(31), r(31),
"pc".red(), "pc".red(),
format_value(self.pc) format_value(self.pc.as_usize())
); );
} }
_ => println!( _ => println!(
@ -686,3 +685,154 @@ impl Emulator {
} }
} }
} }
pub trait XLen: Copy + PartialEq + Eq {
const ZERO: Self;
const SIGNED_MIN: Self;
const MAX: Self;
fn from_bool(v: bool) -> Self;
fn from_8_z(v: u8) -> Self {
Self::from_32_z(v as u32)
}
fn from_8_s(v: u8) -> Self {
Self::from_32_s(v as i8 as i32 as u32)
}
fn from_16_z(v: u16) -> Self {
Self::from_32_z(v as u32)
}
fn from_16_s(v: u16) -> Self {
Self::from_32_s(v as i16 as i32 as u32)
}
fn from_32_z(v: u32) -> Self;
fn from_32_s(v: u32) -> Self;
fn truncate8(self) -> u8 {
self.truncate32() as u8
}
fn truncate16(self) -> u16 {
self.truncate32() as u16
}
fn truncate32(self) -> u32;
fn as_usize(self) -> usize;
fn add(self, other: Self) -> Self;
fn sub(self, other: Self) -> Self;
fn and(self, other: Self) -> Self;
fn or(self, other: Self) -> Self;
fn xor(self, other: Self) -> Self;
fn signed_lt(self, other: Self) -> bool;
fn unsigned_lt(self, other: Self) -> bool;
fn signed_ge(self, other: Self) -> bool;
fn unsigned_ge(self, other: Self) -> bool;
fn shl(self, other: u32) -> Self;
fn signed_shr(self, other: u32) -> Self;
fn unsigned_shr(self, other: u32) -> Self;
fn signed_min(self, other: Self) -> Self;
fn unsigned_min(self, other: Self) -> Self;
fn signed_max(self, other: Self) -> Self;
fn unsigned_max(self, other: Self) -> Self;
fn mul_lower(self, other: Self) -> Self;
fn signed_mul_upper(self, other: Self) -> Self;
fn unsigned_mul_upper(self, other: Self) -> Self;
fn signed_div(self, other: Self) -> Self;
fn unsigned_div(self, other: Self) -> Self;
fn signed_rem(self, other: Self) -> Self;
fn unsigned_rem(self, other: Self) -> Self;
}
impl XLen for u32 {
const ZERO: Self = 0;
const SIGNED_MIN: Self = i32::MIN as u32;
const MAX: Self = u32::MAX;
fn from_bool(v: bool) -> Self {
v as u32
}
fn from_32_s(v: u32) -> Self {
v
}
fn from_32_z(v: u32) -> Self {
v
}
fn as_usize(self) -> usize {
self as usize
}
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
}
}

View file

@ -16,7 +16,7 @@ pub fn execute_linux_elf(
let elf = elf::Elf { content: elf }; let elf = elf::Elf { content: elf };
let header = elf.header()?; let header = elf.header()?;
let segments = elf.segments()?; let segments = elf.segments_32()?;
let mut mem = emu::Memory { let mut mem = emu::Memory {
mem: vec![0; MEMORY_SIZE], mem: vec![0; MEMORY_SIZE],
@ -66,7 +66,7 @@ pub fn execute_linux_elf(
mem, mem,
xreg: [0; 32], xreg: [0; 32],
xreg0_value: 0, xreg0_value: 0,
pc: start, pc: start.0 as u32,
reservation_set: None, reservation_set: None,
is_breaking: false, is_breaking: false,