mirror of
https://github.com/Noratrieb/rustv32i.git
synced 2026-01-14 13:25:01 +01:00
make it work
This commit is contained in:
parent
29bb73425b
commit
ab8e4ebc13
10 changed files with 608 additions and 186 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,4 +1,6 @@
|
||||||
/target
|
/target
|
||||||
/test/*
|
/test/*
|
||||||
!/test/*.c
|
!/test/*.c
|
||||||
|
!/test/*.rs
|
||||||
!/test/Makefile
|
!/test/Makefile
|
||||||
|
/sysroot
|
||||||
|
|
|
||||||
72
Cargo.lock
generated
72
Cargo.lock
generated
|
|
@ -2,35 +2,6 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aligned-vec"
|
|
||||||
version = "0.6.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "af15ccceeacb9304119d97925de463bc97ae3555ee8dc8056f67b119f66e5934"
|
|
||||||
dependencies = [
|
|
||||||
"equator",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "equator"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
|
|
||||||
dependencies = [
|
|
||||||
"equator-macro",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "equator-macro"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "eyre"
|
name = "eyre"
|
||||||
version = "0.6.12"
|
version = "0.6.12"
|
||||||
|
|
@ -47,51 +18,22 @@ version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.170"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.20.3"
|
version = "1.20.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.94"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.39"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustv32i"
|
name = "rustv32i"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aligned-vec",
|
|
||||||
"eyre",
|
"eyre",
|
||||||
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.99"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,8 @@ version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aligned-vec = "0.6.2"
|
|
||||||
eyre = "0.6.12"
|
eyre = "0.6.12"
|
||||||
|
libc = "0.2.170"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
debug = 1
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
{ pkgs ? import <nixpkgs> { } }: pkgs.mkShell {
|
{ pkgs ? import <nixpkgs> { } }: pkgs.mkShell {
|
||||||
packages = with pkgs; [ llvmPackages_18.clang-unwrapped llvmPackages_18.lld ];
|
nativeBuildInputs = with pkgs; [ cmake ninja ];
|
||||||
|
packages = with pkgs; [
|
||||||
|
llvmPackages_18.clang-unwrapped
|
||||||
|
llvmPackages_18.lld
|
||||||
|
llvmPackages_18.bintools-unwrapped
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
src/elf.rs
13
src/elf.rs
|
|
@ -4,6 +4,7 @@ pub struct Elf {
|
||||||
pub content: Vec<u8>,
|
pub content: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Header {
|
pub struct Header {
|
||||||
pub e_entry: u32,
|
pub e_entry: u32,
|
||||||
|
|
@ -18,6 +19,7 @@ pub struct Header {
|
||||||
pub e_shstrndx: u16,
|
pub e_shstrndx: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Phdr {
|
pub struct Phdr {
|
||||||
pub p_type: u32,
|
pub p_type: u32,
|
||||||
|
|
@ -42,9 +44,9 @@ impl Elf {
|
||||||
}
|
}
|
||||||
|
|
||||||
let (e_type, rest) = rest.split_u16()?;
|
let (e_type, rest) = rest.split_u16()?;
|
||||||
// ET_EXEC
|
// ET_EXEC|ET_DYN
|
||||||
if e_type != 2 {
|
if e_type != 2 && e_type != 3 {
|
||||||
bail!("not a static executable: {e_type}");
|
bail!("not an executable: {e_type}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let (e_machine, rest) = rest.split_u16()?;
|
let (e_machine, rest) = rest.split_u16()?;
|
||||||
|
|
@ -121,7 +123,6 @@ 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])> {
|
||||||
|
|
@ -138,8 +139,4 @@ 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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
527
src/emu.rs
527
src/emu.rs
|
|
@ -4,12 +4,8 @@ use std::{
|
||||||
u32,
|
u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
use aligned_vec::{AVec, ConstAlign};
|
|
||||||
|
|
||||||
use crate::PAGE_SIZE;
|
|
||||||
|
|
||||||
pub struct Memory {
|
pub struct Memory {
|
||||||
pub mem: AVec<u8, ConstAlign<PAGE_SIZE>>,
|
pub mem: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Memory {
|
impl Memory {
|
||||||
|
|
@ -23,7 +19,7 @@ impl Memory {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn index(&self, addr: u32, len: u32) -> Result<&[u8], Error> {
|
pub fn slice(&self, addr: u32, len: u32) -> Result<&[u8], Error> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.mem
|
.mem
|
||||||
.get((addr as usize)..)
|
.get((addr as usize)..)
|
||||||
|
|
@ -31,7 +27,7 @@ impl Memory {
|
||||||
.get(..(len as usize))
|
.get(..(len as usize))
|
||||||
.ok_or(Error::InvalidMemoryAccess(addr))?)
|
.ok_or(Error::InvalidMemoryAccess(addr))?)
|
||||||
}
|
}
|
||||||
fn index_mut(&mut self, addr: u32, len: u32) -> Result<&mut [u8], Error> {
|
pub fn slice_mut(&mut self, addr: u32, len: u32) -> Result<&mut [u8], Error> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.mem
|
.mem
|
||||||
.get_mut((addr as usize)..)
|
.get_mut((addr as usize)..)
|
||||||
|
|
@ -40,30 +36,30 @@ impl Memory {
|
||||||
.ok_or(Error::InvalidMemoryAccess(addr))?)
|
.ok_or(Error::InvalidMemoryAccess(addr))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_u8(&self, addr: u32) -> Result<u8, Error> {
|
pub fn load_u8(&self, addr: u32) -> Result<u8, Error> {
|
||||||
Ok(u8::from_le_bytes(self.index(addr, 1)?.try_into().unwrap()))
|
Ok(u8::from_le_bytes(self.slice(addr, 1)?.try_into().unwrap()))
|
||||||
}
|
}
|
||||||
fn load_u16(&self, addr: u32) -> Result<u16, Error> {
|
pub fn load_u16(&self, addr: u32) -> Result<u16, Error> {
|
||||||
Ok(u16::from_le_bytes(self.index(addr, 2)?.try_into().unwrap()))
|
Ok(u16::from_le_bytes(self.slice(addr, 2)?.try_into().unwrap()))
|
||||||
}
|
}
|
||||||
fn load_u32(&self, addr: u32) -> Result<u32, Error> {
|
pub fn load_u32(&self, addr: u32) -> Result<u32, Error> {
|
||||||
Ok(u32::from_le_bytes(self.index(addr, 4)?.try_into().unwrap()))
|
Ok(u32::from_le_bytes(self.slice(addr, 4)?.try_into().unwrap()))
|
||||||
}
|
}
|
||||||
fn store_u8(&mut self, addr: u32, value: u8) -> Result<(), Error> {
|
pub fn store_u8(&mut self, addr: u32, value: u8) -> Result<(), Error> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.index_mut(addr, 1)?
|
.slice_mut(addr, 1)?
|
||||||
.copy_from_slice(&value.to_le_bytes()))
|
.copy_from_slice(&value.to_le_bytes()))
|
||||||
}
|
}
|
||||||
fn store_u16(&mut self, addr: u32, value: u16) -> Result<(), Error> {
|
pub fn store_u16(&mut self, addr: u32, value: u16) -> Result<(), Error> {
|
||||||
self.check_align(addr, 2)?;
|
self.check_align(addr, 2)?;
|
||||||
Ok(self
|
Ok(self
|
||||||
.index_mut(addr, 2)?
|
.slice_mut(addr, 2)?
|
||||||
.copy_from_slice(&value.to_le_bytes()))
|
.copy_from_slice(&value.to_le_bytes()))
|
||||||
}
|
}
|
||||||
fn store_u32(&mut self, addr: u32, value: u32) -> Result<(), Error> {
|
pub fn store_u32(&mut self, addr: u32, value: u32) -> Result<(), Error> {
|
||||||
self.check_align(addr, 4)?;
|
self.check_align(addr, 4)?;
|
||||||
Ok(self
|
Ok(self
|
||||||
.index_mut(addr, 4)?
|
.slice_mut(addr, 4)?
|
||||||
.copy_from_slice(&value.to_le_bytes()))
|
.copy_from_slice(&value.to_le_bytes()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -71,10 +67,13 @@ impl Memory {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[expect(dead_code)]
|
#[expect(dead_code)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
Trap(&'static str),
|
||||||
IllegalInstruction(InstCode, &'static str),
|
IllegalInstruction(InstCode, &'static str),
|
||||||
InvalidMemoryAccess(u32),
|
InvalidMemoryAccess(u32),
|
||||||
UnalignedPc(u32),
|
UnalignedPc(u32),
|
||||||
UnaligneMemoryAccess { addr: u32, required_align: u32 },
|
UnaligneMemoryAccess { addr: u32, required_align: u32 },
|
||||||
|
Ebreak,
|
||||||
|
Exit { code: i32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Emulator {
|
pub struct Emulator {
|
||||||
|
|
@ -83,6 +82,10 @@ pub struct Emulator {
|
||||||
/// Written to insterad of xreg[0].
|
/// Written to insterad of xreg[0].
|
||||||
pub xreg0_value: u32,
|
pub xreg0_value: u32,
|
||||||
pub pc: u32,
|
pub pc: u32,
|
||||||
|
|
||||||
|
pub debug: bool,
|
||||||
|
|
||||||
|
pub ecall_handler: Box<dyn FnMut(&mut Memory, &mut [u32; 32]) -> Result<(), Error>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<Reg> for Emulator {
|
impl Index<Reg> for Emulator {
|
||||||
|
|
@ -102,11 +105,23 @@ impl IndexMut<Reg> for Emulator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Reg(u32);
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Reg(pub u32);
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
impl Reg {
|
impl Reg {
|
||||||
const RA: Reg = Reg(1);
|
pub const RA: Reg = Reg(1);
|
||||||
const SP: Reg = Reg(2);
|
pub const SP: Reg = Reg(2);
|
||||||
|
// arguments, return values:
|
||||||
|
pub const A0: Reg = Reg(10);
|
||||||
|
pub const A1: Reg = Reg(11);
|
||||||
|
// arguments:
|
||||||
|
pub const A2: Reg = Reg(12);
|
||||||
|
pub const A3: Reg = Reg(13);
|
||||||
|
pub const A4: Reg = Reg(14);
|
||||||
|
pub const A5: Reg = Reg(15);
|
||||||
|
pub const A6: Reg = Reg(16);
|
||||||
|
pub const A7: Reg = Reg(17);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Reg {
|
impl Display for Reg {
|
||||||
|
|
@ -122,9 +137,19 @@ impl Display for Reg {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Inst {
|
enum Inst {
|
||||||
|
Lui { uimm: u32, dest: Reg },
|
||||||
|
Auipc { uimm: u32, dest: Reg },
|
||||||
|
|
||||||
Jal { offset: u32, dest: Reg },
|
Jal { offset: u32, dest: Reg },
|
||||||
Jalr { offset: u32, base: Reg, dest: Reg },
|
Jalr { offset: u32, base: Reg, dest: Reg },
|
||||||
|
|
||||||
|
Beq { offset: u32, src1: Reg, src2: Reg },
|
||||||
|
Bne { offset: u32, src1: Reg, src2: Reg },
|
||||||
|
Blt { offset: u32, src1: Reg, src2: Reg },
|
||||||
|
Bge { offset: u32, src1: Reg, src2: Reg },
|
||||||
|
Bltu { offset: u32, src1: Reg, src2: Reg },
|
||||||
|
Bgeu { offset: u32, src1: Reg, src2: Reg },
|
||||||
|
|
||||||
Lb { offset: u32, dest: Reg, base: Reg },
|
Lb { offset: u32, dest: Reg, base: Reg },
|
||||||
Lbu { offset: u32, dest: Reg, base: Reg },
|
Lbu { offset: u32, dest: Reg, base: Reg },
|
||||||
Lh { offset: u32, dest: Reg, base: Reg },
|
Lh { offset: u32, dest: Reg, base: Reg },
|
||||||
|
|
@ -135,12 +160,136 @@ enum Inst {
|
||||||
Sh { offset: u32, src: Reg, base: Reg },
|
Sh { offset: u32, src: Reg, base: Reg },
|
||||||
Sw { offset: u32, src: Reg, base: Reg },
|
Sw { offset: u32, src: Reg, base: Reg },
|
||||||
|
|
||||||
Addi { imm: u32, rd: Reg, rs1: Reg },
|
Addi { imm: u32, dest: Reg, src1: Reg },
|
||||||
Slti { imm: u32, rd: Reg, rs1: Reg },
|
Slti { imm: u32, dest: Reg, src1: Reg },
|
||||||
Sltiu { imm: u32, rd: Reg, rs1: Reg },
|
Sltiu { imm: u32, dest: Reg, src1: Reg },
|
||||||
Andi { imm: u32, rd: Reg, rs1: Reg },
|
Xori { imm: u32, dest: Reg, src1: Reg },
|
||||||
Ori { imm: u32, rd: Reg, rs1: Reg },
|
Ori { imm: u32, dest: Reg, src1: Reg },
|
||||||
Xori { imm: u32, rd: Reg, rs1: Reg },
|
Andi { imm: u32, dest: Reg, src1: Reg },
|
||||||
|
Slli { imm: u32, dest: Reg, src1: Reg },
|
||||||
|
Srli { imm: u32, dest: Reg, src1: Reg },
|
||||||
|
Srai { imm: u32, dest: Reg, src1: Reg },
|
||||||
|
|
||||||
|
Add { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
Sub { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
Sll { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
Slt { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
Sltu { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
Xor { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
Srl { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
Sra { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
Or { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
And { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
|
||||||
|
Ecall,
|
||||||
|
Ebreak,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Inst {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Display::fmt(&self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for Inst {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Inst::Lui { uimm, dest } => write!(f, "lui {dest}, {}", uimm >> 12),
|
||||||
|
Inst::Auipc { uimm, dest } => write!(f, "auipc {dest}, {}", uimm >> 12),
|
||||||
|
Inst::Jal { offset, dest } => {
|
||||||
|
if dest.0 == 0 {
|
||||||
|
write!(f, "j {}", offset as i32)
|
||||||
|
} else {
|
||||||
|
write!(f, "jal {dest}, {}", offset as i32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Inst::Jalr { offset, base, dest } => {
|
||||||
|
write!(f, "jalr {dest}, {}({base})", offset as i32)
|
||||||
|
}
|
||||||
|
Inst::Beq { offset, src1, src2 } => write!(f, "beq {src1}, {src2}, {}", offset as i32),
|
||||||
|
Inst::Bne { offset, src1, src2 } => write!(f, "bne {src1}, {src2}, {}", offset as i32),
|
||||||
|
Inst::Blt { offset, src1, src2 } => write!(f, "blt {src1}, {src2}, {}", offset as i32),
|
||||||
|
Inst::Bge { offset, src1, src2 } => write!(f, "bge {src1}, {src2}, {}", offset as i32),
|
||||||
|
Inst::Bltu { offset, src1, src2 } => {
|
||||||
|
write!(f, "bltu {src1}, {src2}, {}", offset as i32)
|
||||||
|
}
|
||||||
|
Inst::Bgeu { offset, src1, src2 } => {
|
||||||
|
write!(f, "bgeu {src1}, {src2}, {}", offset as i32)
|
||||||
|
}
|
||||||
|
Inst::Lb { offset, dest, base } => write!(f, "lb {dest}, {}({base})", offset as i32),
|
||||||
|
Inst::Lbu { offset, dest, base } => write!(f, "lbu {dest}, {}({base})", offset as i32),
|
||||||
|
Inst::Lh { offset, dest, base } => write!(f, "lh {dest}, {}({base})", offset as i32),
|
||||||
|
Inst::Lhu { offset, dest, base } => write!(f, "lhu {dest}, {}({base})", offset as i32),
|
||||||
|
Inst::Lw { offset, dest, base } => write!(f, "lw {dest}, {}({base})", offset as i32),
|
||||||
|
Inst::Sb { offset, src, base } => write!(f, "sb {src}, {}({base})", offset as i32),
|
||||||
|
Inst::Sh { offset, src, base } => write!(f, "sh {src}, {}({base})", offset as i32),
|
||||||
|
Inst::Sw { offset, src, base } => write!(f, "sw {src}, {}({base})", offset as i32),
|
||||||
|
Inst::Addi {
|
||||||
|
imm,
|
||||||
|
dest: rd,
|
||||||
|
src1: rs1,
|
||||||
|
} => {
|
||||||
|
if rs1.0 == 0 {
|
||||||
|
write!(f, "li {rd}, {}", imm as i32)
|
||||||
|
} else if imm == 0 {
|
||||||
|
write!(f, "mv {rd}, {rs1}")
|
||||||
|
} else {
|
||||||
|
write!(f, "addi {rd}, {rs1}, {}", imm as i32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Inst::Slti {
|
||||||
|
imm,
|
||||||
|
dest,
|
||||||
|
src1: rs1,
|
||||||
|
} => write!(f, "slti {dest}, {rs1}, {}", imm as i32),
|
||||||
|
Inst::Sltiu {
|
||||||
|
imm,
|
||||||
|
dest,
|
||||||
|
src1: rs1,
|
||||||
|
} => write!(f, "sltiu {dest}, {rs1}, {}", imm as i32),
|
||||||
|
Inst::Andi {
|
||||||
|
imm,
|
||||||
|
dest,
|
||||||
|
src1: rs1,
|
||||||
|
} => write!(f, "andi {dest}, {rs1}, {}", imm as i32),
|
||||||
|
Inst::Ori {
|
||||||
|
imm,
|
||||||
|
dest,
|
||||||
|
src1: rs1,
|
||||||
|
} => write!(f, "ori {dest}, {rs1}, {}", imm as i32),
|
||||||
|
Inst::Xori {
|
||||||
|
imm,
|
||||||
|
dest,
|
||||||
|
src1: rs1,
|
||||||
|
} => write!(f, "xori {dest}, {rs1}, {}", imm as i32),
|
||||||
|
Inst::Slli {
|
||||||
|
imm,
|
||||||
|
dest,
|
||||||
|
src1: rs1,
|
||||||
|
} => write!(f, "slli {dest}, {rs1}, {}", imm as i32),
|
||||||
|
Inst::Srli {
|
||||||
|
imm,
|
||||||
|
dest,
|
||||||
|
src1: rs1,
|
||||||
|
} => write!(f, "srli {dest}, {rs1}, {}", imm as i32),
|
||||||
|
Inst::Srai {
|
||||||
|
imm,
|
||||||
|
dest,
|
||||||
|
src1: rs1,
|
||||||
|
} => write!(f, "srai {dest}, {rs1}, {}", imm as i32),
|
||||||
|
Inst::Add { dest, src1, src2 } => write!(f, "add {dest}, {src1}, {src2}"),
|
||||||
|
Inst::Sub { dest, src1, src2 } => write!(f, "sub {dest}, {src1}, {src2}"),
|
||||||
|
Inst::Sll { dest, src1, src2 } => write!(f, "sll {dest}, {src1}, {src2}"),
|
||||||
|
Inst::Slt { dest, src1, src2 } => write!(f, "slt {dest}, {src1}, {src2}"),
|
||||||
|
Inst::Sltu { dest, src1, src2 } => write!(f, "sltu {dest}, {src1}, {src2}"),
|
||||||
|
Inst::Xor { dest, src1, src2 } => write!(f, "xor {dest}, {src1}, {src2}"),
|
||||||
|
Inst::Srl { dest, src1, src2 } => write!(f, "srl {dest}, {src1}, {src2}"),
|
||||||
|
Inst::Sra { dest, src1, src2 } => write!(f, "sra {dest}, {src1}, {src2}"),
|
||||||
|
Inst::Or { dest, src1, src2 } => write!(f, "or {dest}, {src1}, {src2}"),
|
||||||
|
Inst::And { dest, src1, src2 } => write!(f, "and {dest}, {src1}, {src2}"),
|
||||||
|
Inst::Ecall => write!(f, "ecall"),
|
||||||
|
Inst::Ebreak => write!(f, "ebreak"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign_extend(value: u32, size: u32) -> u32 {
|
fn sign_extend(value: u32, size: u32) -> u32 {
|
||||||
|
|
@ -162,13 +311,15 @@ impl InstCode {
|
||||||
let end_span = 32 - (range.end() + 1);
|
let end_span = 32 - (range.end() + 1);
|
||||||
(self.0 << (end_span)) >> (end_span + range.start())
|
(self.0 << (end_span)) >> (end_span + range.start())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opcode(self) -> u32 {
|
fn opcode(self) -> u32 {
|
||||||
self.0 & 0b1111111
|
self.0 & 0b1111111
|
||||||
}
|
}
|
||||||
fn funct3(self) -> u32 {
|
fn funct3(self) -> u32 {
|
||||||
self.extract(12..=14)
|
self.extract(12..=14)
|
||||||
}
|
}
|
||||||
|
fn funct7(self) -> u32 {
|
||||||
|
self.extract(25..=31)
|
||||||
|
}
|
||||||
fn rs1(self) -> Reg {
|
fn rs1(self) -> Reg {
|
||||||
Reg(self.extract(15..=19))
|
Reg(self.extract(15..=19))
|
||||||
}
|
}
|
||||||
|
|
@ -182,18 +333,31 @@ impl InstCode {
|
||||||
let imm = self.extract(20..=31);
|
let imm = self.extract(20..=31);
|
||||||
sign_extend(imm, 12)
|
sign_extend(imm, 12)
|
||||||
}
|
}
|
||||||
fn imm_j(self) -> u32 {
|
|
||||||
let imm_20 = self.extract(31..=31);
|
|
||||||
let imm_10_1 = self.extract(21..=30);
|
|
||||||
let imm_19_12 = self.extract(12..=19);
|
|
||||||
let imm = (imm_20 << 19) | (imm_19_12 << 11) | imm_10_1;
|
|
||||||
sign_extend(imm, 20)
|
|
||||||
}
|
|
||||||
fn imm_s(self) -> u32 {
|
fn imm_s(self) -> u32 {
|
||||||
let imm_11_5 = self.extract(25..=31);
|
let imm_11_5 = self.extract(25..=31);
|
||||||
let imm_4_0 = self.extract(7..=11);
|
let imm_4_0 = self.extract(7..=11);
|
||||||
let full_imm = (imm_11_5 << 5) | imm_4_0;
|
let imm = (imm_11_5 << 5) | imm_4_0;
|
||||||
sign_extend(full_imm, 12)
|
sign_extend(imm, 12)
|
||||||
|
}
|
||||||
|
fn imm_b(self) -> u32 {
|
||||||
|
let imm_12 = self.extract(31..=31);
|
||||||
|
let imm_10_5 = self.extract(25..=30);
|
||||||
|
let imm_4_1 = self.extract(8..=11);
|
||||||
|
let imm_11 = self.extract(7..=7);
|
||||||
|
let imm = (imm_12 << 12) | (imm_11 << 11) | (imm_10_5 << 5) | (imm_4_1 << 1);
|
||||||
|
sign_extend(imm, 13) // 13 due to 2-byte immediate offset
|
||||||
|
}
|
||||||
|
fn imm_u(self) -> u32 {
|
||||||
|
let imm_12_31 = self.extract(12..=31);
|
||||||
|
imm_12_31 << 12
|
||||||
|
}
|
||||||
|
fn imm_j(self) -> u32 {
|
||||||
|
let imm_20 = self.extract(31..=31);
|
||||||
|
let imm_10_1 = self.extract(21..=30);
|
||||||
|
let imm_11 = self.extract(20..=20);
|
||||||
|
let imm_19_12 = self.extract(12..=19);
|
||||||
|
let imm = (imm_20 << 19) | (imm_19_12 << 11) | (imm_11 << 10) | imm_10_1;
|
||||||
|
sign_extend(imm, 20) << 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,8 +369,17 @@ impl Debug for InstCode {
|
||||||
|
|
||||||
impl Inst {
|
impl Inst {
|
||||||
fn decode(code: InstCode) -> Result<Inst, Error> {
|
fn decode(code: InstCode) -> Result<Inst, Error> {
|
||||||
eprintln!("decoding: {code:?}");
|
|
||||||
let inst = match code.opcode() {
|
let inst = match code.opcode() {
|
||||||
|
// LUI
|
||||||
|
0b0110111 => Inst::Lui {
|
||||||
|
uimm: code.imm_u(),
|
||||||
|
dest: code.rd(),
|
||||||
|
},
|
||||||
|
// AUIPC
|
||||||
|
0b0010111 => Inst::Auipc {
|
||||||
|
uimm: code.imm_u(),
|
||||||
|
dest: code.rd(),
|
||||||
|
},
|
||||||
// JAL
|
// JAL
|
||||||
0b1101111 => Inst::Jal {
|
0b1101111 => Inst::Jal {
|
||||||
offset: code.imm_j(),
|
offset: code.imm_j(),
|
||||||
|
|
@ -221,6 +394,40 @@ impl Inst {
|
||||||
},
|
},
|
||||||
_ => return Err(Error::IllegalInstruction(code, "funct3")),
|
_ => return Err(Error::IllegalInstruction(code, "funct3")),
|
||||||
},
|
},
|
||||||
|
// BRANCH
|
||||||
|
0b1100011 => match code.funct3() {
|
||||||
|
0b000 => Inst::Beq {
|
||||||
|
offset: code.imm_b(),
|
||||||
|
src1: code.rs1(),
|
||||||
|
src2: code.rs2(),
|
||||||
|
},
|
||||||
|
0b001 => Inst::Bne {
|
||||||
|
offset: code.imm_b(),
|
||||||
|
src1: code.rs1(),
|
||||||
|
src2: code.rs2(),
|
||||||
|
},
|
||||||
|
0b100 => Inst::Blt {
|
||||||
|
offset: code.imm_b(),
|
||||||
|
src1: code.rs1(),
|
||||||
|
src2: code.rs2(),
|
||||||
|
},
|
||||||
|
0b101 => Inst::Bge {
|
||||||
|
offset: code.imm_b(),
|
||||||
|
src1: code.rs1(),
|
||||||
|
src2: code.rs2(),
|
||||||
|
},
|
||||||
|
0b110 => Inst::Bltu {
|
||||||
|
offset: code.imm_b(),
|
||||||
|
src1: code.rs1(),
|
||||||
|
src2: code.rs2(),
|
||||||
|
},
|
||||||
|
0b111 => Inst::Bgeu {
|
||||||
|
offset: code.imm_b(),
|
||||||
|
src1: code.rs1(),
|
||||||
|
src2: code.rs2(),
|
||||||
|
},
|
||||||
|
_ => return Err(Error::IllegalInstruction(code, "funct3")),
|
||||||
|
},
|
||||||
// LOAD
|
// LOAD
|
||||||
0b0000011 => match code.funct3() {
|
0b0000011 => match code.funct3() {
|
||||||
0b000 => Inst::Lb {
|
0b000 => Inst::Lb {
|
||||||
|
|
@ -273,76 +480,114 @@ impl Inst {
|
||||||
0b0010011 => match code.funct3() {
|
0b0010011 => match code.funct3() {
|
||||||
0b000 => Inst::Addi {
|
0b000 => Inst::Addi {
|
||||||
imm: code.imm_i(),
|
imm: code.imm_i(),
|
||||||
rd: code.rd(),
|
dest: code.rd(),
|
||||||
rs1: code.rs1(),
|
src1: code.rs1(),
|
||||||
},
|
},
|
||||||
0b010 => Inst::Slti {
|
0b010 => Inst::Slti {
|
||||||
imm: code.imm_i(),
|
imm: code.imm_i(),
|
||||||
rd: code.rd(),
|
dest: code.rd(),
|
||||||
rs1: code.rs1(),
|
src1: code.rs1(),
|
||||||
},
|
},
|
||||||
0b011 => Inst::Sltiu {
|
0b011 => Inst::Sltiu {
|
||||||
imm: code.imm_i(),
|
imm: code.imm_i(),
|
||||||
rd: code.rd(),
|
dest: code.rd(),
|
||||||
rs1: code.rs1(),
|
src1: code.rs1(),
|
||||||
},
|
},
|
||||||
0b100 => Inst::Xori {
|
0b100 => Inst::Xori {
|
||||||
imm: code.imm_i(),
|
imm: code.imm_i(),
|
||||||
rd: code.rd(),
|
dest: code.rd(),
|
||||||
rs1: code.rs1(),
|
src1: code.rs1(),
|
||||||
},
|
},
|
||||||
0b110 => Inst::Ori {
|
0b110 => Inst::Ori {
|
||||||
imm: code.imm_i(),
|
imm: code.imm_i(),
|
||||||
rd: code.rd(),
|
dest: code.rd(),
|
||||||
rs1: code.rs1(),
|
src1: code.rs1(),
|
||||||
},
|
},
|
||||||
0b111 => Inst::Andi {
|
0b111 => Inst::Andi {
|
||||||
imm: code.imm_i(),
|
imm: code.imm_i(),
|
||||||
rd: code.rd(),
|
dest: code.rd(),
|
||||||
rs1: code.rs1(),
|
src1: code.rs1(),
|
||||||
|
},
|
||||||
|
0b001 => Inst::Slli {
|
||||||
|
imm: code.imm_i(),
|
||||||
|
dest: code.rd(),
|
||||||
|
src1: code.rs1(),
|
||||||
|
},
|
||||||
|
0b101 => match code.funct7() {
|
||||||
|
0b0000000 => Inst::Srli {
|
||||||
|
imm: code.rs2().0,
|
||||||
|
dest: code.rd(),
|
||||||
|
src1: code.rs1(),
|
||||||
|
},
|
||||||
|
0b0100000 => Inst::Srai {
|
||||||
|
imm: code.rs2().0,
|
||||||
|
dest: code.rd(),
|
||||||
|
src1: code.rs1(),
|
||||||
|
},
|
||||||
|
_ => return Err(Error::IllegalInstruction(code, "funct7")),
|
||||||
},
|
},
|
||||||
_ => return Err(Error::IllegalInstruction(code, "funct3")),
|
_ => return Err(Error::IllegalInstruction(code, "funct3")),
|
||||||
},
|
},
|
||||||
|
// OP
|
||||||
|
0b0110011 => {
|
||||||
|
let (dest, src1, src2) = (code.rd(), code.rs1(), code.rs2());
|
||||||
|
match (code.funct3(), code.funct7()) {
|
||||||
|
(0b000, 0b0000000) => Inst::Add { dest, src1, src2 },
|
||||||
|
(0b000, 0b0100000) => Inst::Sub { dest, src1, src2 },
|
||||||
|
(0b001, 0b0000000) => Inst::Sll { dest, src1, src2 },
|
||||||
|
(0b010, 0b0000000) => Inst::Slt { dest, src1, src2 },
|
||||||
|
(0b011, 0b0000000) => Inst::Sltu { dest, src1, src2 },
|
||||||
|
(0b100, 0b0000000) => Inst::Xor { dest, src1, src2 },
|
||||||
|
(0b101, 0b0000000) => Inst::Srl { dest, src1, src2 },
|
||||||
|
(0b101, 0b0100000) => Inst::Sra { dest, src1, src2 },
|
||||||
|
(0b110, 0b0000000) => Inst::Or { dest, src1, src2 },
|
||||||
|
(0b111, 0b0000000) => Inst::And { dest, src1, src2 },
|
||||||
|
_ => return Err(Error::IllegalInstruction(code, "funct3/funct7")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// SYSTEM
|
||||||
|
0b1110011 => {
|
||||||
|
if code.0 == 0b11000000000000000001000001110011 {
|
||||||
|
return Err(Error::Trap("unimp instruction"));
|
||||||
|
}
|
||||||
|
if code.rd().0 != 0 {
|
||||||
|
return Err(Error::IllegalInstruction(code, "rd"));
|
||||||
|
}
|
||||||
|
if code.funct3() != 0 {
|
||||||
|
return Err(Error::IllegalInstruction(code, "funct3"));
|
||||||
|
}
|
||||||
|
if code.rs1().0 != 0 {
|
||||||
|
return Err(Error::IllegalInstruction(code, "rs1"));
|
||||||
|
}
|
||||||
|
match code.imm_i() {
|
||||||
|
0b000000000000 => Inst::Ecall,
|
||||||
|
0b000000000001 => Inst::Ebreak,
|
||||||
|
_ => return Err(Error::IllegalInstruction(code, "imm")),
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => return Err(Error::IllegalInstruction(code, "opcode")),
|
_ => return Err(Error::IllegalInstruction(code, "opcode")),
|
||||||
};
|
};
|
||||||
eprintln!(" parsed {inst:?}");
|
|
||||||
Ok(inst)
|
Ok(inst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Inst {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
Display::fmt(&self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Inst {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Inst::Jal { offset, dest } => write!(f, "jal {dest}, {offset}"),
|
|
||||||
Inst::Jalr { offset, base, dest } => write!(f, "jalr {dest}, {offset}({base})"),
|
|
||||||
Inst::Lb { offset, dest, base } => write!(f, "lb {dest}, {offset}({base})"),
|
|
||||||
Inst::Lbu { offset, dest, base } => write!(f, "lbu {dest}, {offset}({base})"),
|
|
||||||
Inst::Lh { offset, dest, base } => write!(f, "lh {dest}, {offset}({base})"),
|
|
||||||
Inst::Lhu { offset, dest, base } => write!(f, "lhu {dest}, {offset}({base})"),
|
|
||||||
Inst::Lw { offset, dest, base } => write!(f, "lw {dest}, {offset}({base})"),
|
|
||||||
Inst::Sb { offset, src, base } => write!(f, "sb {src}, {offset}({base})"),
|
|
||||||
Inst::Sh { offset, src, base } => write!(f, "sh {src}, {offset}({base})"),
|
|
||||||
Inst::Sw { offset, src, base } => write!(f, "sw {src}, {offset}({base})"),
|
|
||||||
Inst::Addi { imm, rd, rs1 } => write!(f, "addi {rd}, {rs1}, {}", *imm as i32),
|
|
||||||
Inst::Slti { imm, rd, rs1 } => write!(f, "slti {rd}, {rs1}, {}", *imm as i32),
|
|
||||||
Inst::Sltiu { imm, rd, rs1 } => write!(f, "sltiu {rd}, {rs1}, {}", *imm as i32),
|
|
||||||
Inst::Andi { imm, rd, rs1 } => write!(f, "andi {rd}, {rs1}, {}", *imm as i32),
|
|
||||||
Inst::Ori { imm, rd, rs1 } => write!(f, "ori {rd}, {rs1}, {}", *imm as i32),
|
|
||||||
Inst::Xori { imm, rd, rs1 } => write!(f, "xori {rd}, {rs1}, {}", *imm as i32),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Emulator {
|
impl Emulator {
|
||||||
pub fn start_linux(&mut self) -> Result<(), Error> {
|
pub fn start_linux(&mut self) -> Result<(), Error> {
|
||||||
// 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?
|
||||||
self[Reg::SP] = 4096;
|
self[Reg::SP] = 4096;
|
||||||
|
|
||||||
|
match self.execute() {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(Error::Exit { code }) => {
|
||||||
|
eprintln!("exited with code {code}");
|
||||||
|
}
|
||||||
|
Err(Error::Trap(cause)) => eprintln!("program trapped: {cause}"),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute(&mut self) -> Result<(), Error> {
|
||||||
loop {
|
loop {
|
||||||
self.step()?;
|
self.step()?;
|
||||||
}
|
}
|
||||||
|
|
@ -357,20 +602,77 @@ impl Emulator {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step(&mut self) -> Result<(), Error> {
|
fn step(&mut self) -> Result<(), Error> {
|
||||||
let instruction = self.mem.load_u32(self.pc)?;
|
let code = self.mem.load_u32(self.pc)?;
|
||||||
|
let inst = Inst::decode(InstCode(code))?;
|
||||||
|
|
||||||
let instruction = Inst::decode(InstCode(instruction))?;
|
if self.debug {
|
||||||
|
println!("executing 0x{:x} {inst:?}", self.pc);
|
||||||
|
}
|
||||||
|
|
||||||
match instruction {
|
let mut jumped = false;
|
||||||
|
|
||||||
|
match inst {
|
||||||
|
Inst::Lui { uimm, dest } => self[dest] = uimm,
|
||||||
|
Inst::Auipc { uimm, dest } => self[dest] = self.pc.wrapping_add(uimm),
|
||||||
Inst::Jal { offset, dest } => {
|
Inst::Jal { offset, dest } => {
|
||||||
let target = self.pc.wrapping_add(offset);
|
let target = self.pc.wrapping_add(offset);
|
||||||
self[dest] = self.pc.wrapping_add(4);
|
self[dest] = self.pc.wrapping_add(4);
|
||||||
self.set_pc(target)?;
|
self.set_pc(target)?;
|
||||||
|
jumped = true;
|
||||||
}
|
}
|
||||||
Inst::Jalr { offset, base, dest } => {
|
Inst::Jalr { offset, base, dest } => {
|
||||||
let target = self[base].wrapping_add(offset.wrapping_mul(2)) & !1;
|
let target = self[base].wrapping_add(offset) & !1;
|
||||||
self[dest] = self.pc.wrapping_add(4);
|
self[dest] = self.pc.wrapping_add(4);
|
||||||
self.set_pc(target)?;
|
self.set_pc(target)?;
|
||||||
|
jumped = true;
|
||||||
|
}
|
||||||
|
Inst::Beq { offset, src1, src2 } => {
|
||||||
|
let take = self[src1] == self[src2];
|
||||||
|
if take {
|
||||||
|
let target = self.pc.wrapping_add(offset);
|
||||||
|
self.set_pc(target)?;
|
||||||
|
jumped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Inst::Bne { offset, src1, src2 } => {
|
||||||
|
let take = self[src1] != self[src2];
|
||||||
|
if take {
|
||||||
|
let target = self.pc.wrapping_add(offset);
|
||||||
|
self.set_pc(target)?;
|
||||||
|
jumped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Inst::Blt { offset, src1, src2 } => {
|
||||||
|
let take = (self[src1] as i32) < (self[src2] as i32);
|
||||||
|
if take {
|
||||||
|
let target = self.pc.wrapping_add(offset);
|
||||||
|
self.set_pc(target)?;
|
||||||
|
jumped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Inst::Bge { offset, src1, src2 } => {
|
||||||
|
let take = (self[src1] as i32) >= (self[src2] as i32);
|
||||||
|
if take {
|
||||||
|
let target = self.pc.wrapping_add(offset);
|
||||||
|
self.set_pc(target)?;
|
||||||
|
jumped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Inst::Bltu { offset, src1, src2 } => {
|
||||||
|
let take = self[src1] < self[src2];
|
||||||
|
if take {
|
||||||
|
let target = self.pc.wrapping_add(offset);
|
||||||
|
self.set_pc(target)?;
|
||||||
|
jumped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Inst::Bgeu { offset, src1, src2 } => {
|
||||||
|
let take = self[src1] >= self[src2];
|
||||||
|
if take {
|
||||||
|
let target = self.pc.wrapping_add(offset);
|
||||||
|
self.set_pc(target)?;
|
||||||
|
jumped = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Inst::Lb { offset, dest, base } => {
|
Inst::Lb { offset, dest, base } => {
|
||||||
let addr = self[base].wrapping_add(offset);
|
let addr = self[base].wrapping_add(offset);
|
||||||
|
|
@ -404,29 +706,56 @@ impl Emulator {
|
||||||
let addr = self[base].wrapping_add(offset);
|
let addr = self[base].wrapping_add(offset);
|
||||||
self.mem.store_u32(addr, self[src] as u32)?;
|
self.mem.store_u32(addr, self[src] as u32)?;
|
||||||
}
|
}
|
||||||
Inst::Addi { imm, rd, rs1 } => {
|
Inst::Addi { imm, dest, src1 } => {
|
||||||
self[rd] = self[rs1].wrapping_add(imm as u32);
|
self[dest] = self[src1].wrapping_add(imm as u32);
|
||||||
}
|
}
|
||||||
Inst::Slti { imm, rd, rs1 } => {
|
Inst::Slti { imm, dest, src1 } => {
|
||||||
let result = (self[rs1] as i32) < (imm as i32);
|
let result = (self[src1] as i32) < (imm as i32);
|
||||||
self[rd] = result as u32;
|
self[dest] = result as u32;
|
||||||
}
|
}
|
||||||
Inst::Sltiu { imm, rd, rs1 } => {
|
Inst::Sltiu { imm, dest, src1 } => {
|
||||||
let result = (self[rs1] as u32) < imm as u32;
|
let result = (self[src1] as u32) < imm as u32;
|
||||||
self[rd] = result as u32;
|
self[dest] = result as u32;
|
||||||
}
|
}
|
||||||
Inst::Andi { imm, rd, rs1 } => {
|
Inst::Andi { imm, dest, src1 } => {
|
||||||
self[rd] = self[rs1] & imm;
|
self[dest] = self[src1] & imm;
|
||||||
}
|
}
|
||||||
Inst::Ori { imm, rd, rs1 } => {
|
Inst::Ori { imm, dest, src1 } => {
|
||||||
self[rd] = self[rs1] | imm;
|
self[dest] = self[src1] | imm;
|
||||||
}
|
}
|
||||||
Inst::Xori { imm, rd, rs1 } => {
|
Inst::Xori { imm, dest, src1 } => {
|
||||||
self[rd] = self[rs1] ^ imm;
|
self[dest] = self[src1] ^ imm;
|
||||||
}
|
}
|
||||||
|
Inst::Slli { imm, dest, src1 } => self[dest] = self[src1].wrapping_shl(imm),
|
||||||
|
Inst::Srli { imm, dest, src1 } => self[dest] = self[src1].wrapping_shr(imm),
|
||||||
|
Inst::Srai { imm, dest, src1 } => {
|
||||||
|
self[dest] = (self[src1] as i32).wrapping_shr(imm) as u32
|
||||||
|
}
|
||||||
|
Inst::Add { dest, src1, src2 } => self[dest] = self[src1].wrapping_add(self[src2]),
|
||||||
|
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 } => {
|
||||||
|
self[dest] = ((self[src1] as i32) < (self[src2] as i32)) as u32;
|
||||||
|
}
|
||||||
|
Inst::Sltu { dest, src1, src2 } => {
|
||||||
|
self[dest] = (self[src1] < self[src2]) as u32;
|
||||||
|
}
|
||||||
|
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 } => {
|
||||||
|
self[dest] = (self[src1] as i32).wrapping_shr(self[src2]) as u32
|
||||||
|
}
|
||||||
|
Inst::Or { dest, src1, src2 } => self[dest] = self[src1] | self[src2],
|
||||||
|
Inst::And { dest, src1, src2 } => self[dest] = self[src1] & self[src2],
|
||||||
|
Inst::Ecall => {
|
||||||
|
(self.ecall_handler)(&mut self.mem, &mut self.xreg)?;
|
||||||
|
}
|
||||||
|
Inst::Ebreak => return Err(Error::Ebreak),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_pc(self.pc + 4)?;
|
if !jumped {
|
||||||
|
self.set_pc(self.pc + 4)?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
69
src/main.rs
69
src/main.rs
|
|
@ -1,11 +1,9 @@
|
||||||
use aligned_vec::avec;
|
use emu::{Memory, Reg};
|
||||||
use eyre::{OptionExt, bail};
|
use eyre::{OptionExt, bail};
|
||||||
|
|
||||||
mod elf;
|
mod elf;
|
||||||
mod emu;
|
mod emu;
|
||||||
|
|
||||||
const PAGE_SIZE: usize = 4096;
|
|
||||||
|
|
||||||
// 2 MiB
|
// 2 MiB
|
||||||
const MEMORY_SIZE: usize = 2 << 21;
|
const MEMORY_SIZE: usize = 2 << 21;
|
||||||
|
|
||||||
|
|
@ -15,16 +13,13 @@ fn main() -> eyre::Result<()> {
|
||||||
let elf = elf::Elf { content };
|
let elf = elf::Elf { content };
|
||||||
let header = elf.header()?;
|
let header = elf.header()?;
|
||||||
|
|
||||||
dbg!(&header);
|
|
||||||
|
|
||||||
let segments = elf.segments()?;
|
let segments = elf.segments()?;
|
||||||
|
|
||||||
let mut mem = emu::Memory {
|
let mut mem = emu::Memory {
|
||||||
mem: avec![[PAGE_SIZE]| 0; MEMORY_SIZE],
|
mem: vec![0; MEMORY_SIZE],
|
||||||
};
|
};
|
||||||
|
|
||||||
for phdr in segments {
|
for phdr in segments {
|
||||||
dbg!(&phdr);
|
|
||||||
match phdr.p_type {
|
match phdr.p_type {
|
||||||
// PT_NULL
|
// PT_NULL
|
||||||
0 => {}
|
0 => {}
|
||||||
|
|
@ -46,12 +41,16 @@ fn main() -> eyre::Result<()> {
|
||||||
.copy_from_slice(contents);
|
.copy_from_slice(contents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// PT_DYNAMIC
|
||||||
|
2 => {}
|
||||||
// PT_PHDR
|
// PT_PHDR
|
||||||
6 => {}
|
6 => {}
|
||||||
// PT_GNU_EH_FRAME
|
// PT_GNU_EH_FRAME
|
||||||
1685382480 => {}
|
1685382480 => {}
|
||||||
// PT_GNU_STACK
|
// PT_GNU_STACK
|
||||||
1685382481 => {}
|
1685382481 => {}
|
||||||
|
// PT_GNU_RELRO
|
||||||
|
1685382482 => {}
|
||||||
// PT_RISCV_ATTRIBUTES
|
// PT_RISCV_ATTRIBUTES
|
||||||
0x70000003 => {}
|
0x70000003 => {}
|
||||||
_ => bail!("unknown program header type: {}", phdr.p_type),
|
_ => bail!("unknown program header type: {}", phdr.p_type),
|
||||||
|
|
@ -65,8 +64,64 @@ fn main() -> eyre::Result<()> {
|
||||||
xreg: [0; 32],
|
xreg: [0; 32],
|
||||||
xreg0_value: 0,
|
xreg0_value: 0,
|
||||||
pc: start,
|
pc: start,
|
||||||
|
|
||||||
|
debug: std::env::args().any(|arg| arg == "--debug"),
|
||||||
|
|
||||||
|
ecall_handler: Box::new(ecall_handler),
|
||||||
};
|
};
|
||||||
emu.start_linux().unwrap();
|
emu.start_linux().unwrap();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ecall_handler(mem: &mut Memory, xreg: &mut [u32; 32]) -> Result<(), emu::Error> {
|
||||||
|
let nr = xreg[Reg::A7.0 as usize];
|
||||||
|
|
||||||
|
match nr {
|
||||||
|
// read
|
||||||
|
63 => {
|
||||||
|
let fd = xreg[Reg::A0.0 as usize];
|
||||||
|
let ptr = xreg[Reg::A1.0 as usize];
|
||||||
|
let len = xreg[Reg::A2.0 as usize];
|
||||||
|
|
||||||
|
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 {
|
||||||
|
(-std::io::Error::last_os_error().raw_os_error().unwrap_or(1)) as u32
|
||||||
|
} else {
|
||||||
|
len as u32
|
||||||
|
};
|
||||||
|
|
||||||
|
xreg[Reg::A0.0 as usize] = ret;
|
||||||
|
}
|
||||||
|
// write
|
||||||
|
64 => {
|
||||||
|
let fd = xreg[Reg::A0.0 as usize];
|
||||||
|
let ptr = xreg[Reg::A1.0 as usize];
|
||||||
|
let len = xreg[Reg::A2.0 as usize];
|
||||||
|
|
||||||
|
let data = mem.slice(ptr, len)?;
|
||||||
|
|
||||||
|
let len = unsafe { libc::write(fd as i32, data.as_ptr().cast(), data.len()) };
|
||||||
|
let ret = if len < 0 {
|
||||||
|
(-std::io::Error::last_os_error().raw_os_error().unwrap_or(1)) as u32
|
||||||
|
} else {
|
||||||
|
len as u32
|
||||||
|
};
|
||||||
|
|
||||||
|
xreg[Reg::A0.0 as usize] = ret;
|
||||||
|
}
|
||||||
|
// exit
|
||||||
|
93 => {
|
||||||
|
return Err(emu::Error::Exit {
|
||||||
|
code: xreg[Reg::A0.0 as usize] as i32,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
todo!("unkonwn syscall: {nr}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,12 @@
|
||||||
CC = clang -Wall -Wpedantic -target riscv32-unknown-linux-gnu -fuse-ld=lld -march=rv32i
|
CC = clang -Wall -Wpedantic -target riscv32-unknown-linux-gnu -fuse-ld=lld -march=rv32i
|
||||||
CC_STATIC = $(CC) -static -nostdlib -nodefaultlibs
|
CC_STATIC = $(CC) -static -nostdlib -nodefaultlibs
|
||||||
|
|
||||||
|
RUSTC = rustc --target riscv32i-unknown-none-elf
|
||||||
|
|
||||||
|
all: init init1
|
||||||
|
|
||||||
init: init.c
|
init: init.c
|
||||||
$(CC_STATIC) init.c -o init
|
$(CC_STATIC) init.c -o init
|
||||||
|
|
||||||
|
init1: init1.rs
|
||||||
|
$(RUSTC) init1.rs
|
||||||
|
|
|
||||||
17
test/init.c
17
test/init.c
|
|
@ -1,11 +1,20 @@
|
||||||
void _start(void)
|
// <https://jborza.com/post/2021-05-11-riscv-linux-syscalls/>
|
||||||
|
|
||||||
|
_Noreturn void __attribute__ ((noinline)) exit(int code)
|
||||||
{
|
{
|
||||||
__asm__ volatile (
|
__asm__ volatile(
|
||||||
"li a7, 93;" // exit
|
"li a7, 93;" // exit
|
||||||
"li a0, 0;" // code 0
|
"mv a0, %0;" // code
|
||||||
"ecall"
|
"ecall"
|
||||||
:
|
:
|
||||||
:
|
: "r"(code)
|
||||||
: "a7", "a0", "memory"
|
: "a7", "a0", "memory"
|
||||||
);
|
);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
_Noreturn void _start(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
exit(10);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
73
test/init1.rs
Normal file
73
test/init1.rs
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
fn exit(code: i32) -> ! {
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"li a7, 93",
|
||||||
|
"ecall",
|
||||||
|
in("a0") code,
|
||||||
|
options(noreturn),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(fd: i32, data: &[u8]) -> isize {
|
||||||
|
let mut out;
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"li a7, 64",
|
||||||
|
"ecall",
|
||||||
|
in("a0") fd,
|
||||||
|
in("a1") data.as_ptr(),
|
||||||
|
in("a2") data.len(),
|
||||||
|
lateout("a0") out,
|
||||||
|
out("a7") _,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(fd: i32, buf: &mut [u8]) -> isize {
|
||||||
|
let mut out;
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"li a7, 63",
|
||||||
|
"ecall",
|
||||||
|
in("a0") fd,
|
||||||
|
in("a1") buf.as_mut_ptr(),
|
||||||
|
in("a2") buf.len(),
|
||||||
|
lateout("a0") out,
|
||||||
|
out("a7") _,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Stderr;
|
||||||
|
|
||||||
|
impl core::fmt::Write for Stderr {
|
||||||
|
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||||
|
let ret = write(2, s.as_bytes());
|
||||||
|
if ret < 0 { Err(core::fmt::Error) } else { Ok(()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn handle(info: &core::panic::PanicInfo<'_>) -> ! {
|
||||||
|
let _ = writeln!(Stderr, "panicked: {}", info.message());
|
||||||
|
unsafe { core::arch::asm!("unimp", options(noreturn)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
fn _start() {
|
||||||
|
write(1, b"enter a number: ");
|
||||||
|
let mut buf = [0; 10];
|
||||||
|
let len = read(0, &mut buf);
|
||||||
|
let buf = &buf[..(len as usize)];
|
||||||
|
let n = str::from_utf8(buf).unwrap().trim().parse::<i32>().unwrap();
|
||||||
|
|
||||||
|
exit(n);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue