diff --git a/.gitignore b/.gitignore index f006348..333347c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /target /test/* !/test/*.c -!/test/Makefile \ No newline at end of file +!/test/*.rs +!/test/Makefile +/sysroot diff --git a/Cargo.lock b/Cargo.lock index 391ab15..6e65352 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,35 +2,6 @@ # It is not intended for manual editing. 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]] name = "eyre" version = "0.6.12" @@ -47,51 +18,22 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "libc" +version = "0.2.170" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" + [[package]] name = "once_cell" version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "rustv32i" version = "0.1.0" dependencies = [ - "aligned-vec", "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" diff --git a/Cargo.toml b/Cargo.toml index 4b684f0..858ea20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,8 @@ version = "0.1.0" edition = "2024" [dependencies] -aligned-vec = "0.6.2" eyre = "0.6.12" +libc = "0.2.170" + +[profile.release] +debug = 1 diff --git a/shell.nix b/shell.nix index 303101f..eb6eaec 100644 --- a/shell.nix +++ b/shell.nix @@ -1,3 +1,8 @@ { pkgs ? import { } }: 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 + ]; } diff --git a/src/elf.rs b/src/elf.rs index 2b15fc0..cb6767c 100644 --- a/src/elf.rs +++ b/src/elf.rs @@ -4,6 +4,7 @@ pub struct Elf { pub content: Vec, } +#[expect(dead_code)] #[derive(Debug)] pub struct Header { pub e_entry: u32, @@ -18,6 +19,7 @@ pub struct Header { pub e_shstrndx: u16, } +#[expect(dead_code)] #[derive(Debug)] pub struct Phdr { pub p_type: u32, @@ -42,9 +44,9 @@ impl Elf { } let (e_type, rest) = rest.split_u16()?; - // ET_EXEC - if e_type != 2 { - bail!("not a static executable: {e_type}"); + // ET_EXEC|ET_DYN + if e_type != 2 && e_type != 3 { + bail!("not an executable: {e_type}"); } 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_u16(&self) -> Result<(u16, &[u8])>; fn split_u32(&self) -> Result<(u32, &[u8])>; - fn split_u64(&self) -> Result<(u64, &[u8])>; } impl SplitAtCheckedErr for [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)?; 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)) - } } diff --git a/src/emu.rs b/src/emu.rs index f9a92f0..a83c5cb 100644 --- a/src/emu.rs +++ b/src/emu.rs @@ -4,12 +4,8 @@ use std::{ u32, }; -use aligned_vec::{AVec, ConstAlign}; - -use crate::PAGE_SIZE; - pub struct Memory { - pub mem: AVec>, + pub mem: Vec, } impl Memory { @@ -23,7 +19,7 @@ impl Memory { Ok(()) } } - fn index(&self, addr: u32, len: u32) -> Result<&[u8], Error> { + pub fn slice(&self, addr: u32, len: u32) -> Result<&[u8], Error> { Ok(self .mem .get((addr as usize)..) @@ -31,7 +27,7 @@ impl Memory { .get(..(len as usize)) .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 .mem .get_mut((addr as usize)..) @@ -40,30 +36,30 @@ impl Memory { .ok_or(Error::InvalidMemoryAccess(addr))?) } - fn load_u8(&self, addr: u32) -> Result { - Ok(u8::from_le_bytes(self.index(addr, 1)?.try_into().unwrap())) + pub fn load_u8(&self, addr: u32) -> Result { + Ok(u8::from_le_bytes(self.slice(addr, 1)?.try_into().unwrap())) } - fn load_u16(&self, addr: u32) -> Result { - Ok(u16::from_le_bytes(self.index(addr, 2)?.try_into().unwrap())) + pub fn load_u16(&self, addr: u32) -> Result { + Ok(u16::from_le_bytes(self.slice(addr, 2)?.try_into().unwrap())) } - fn load_u32(&self, addr: u32) -> Result { - Ok(u32::from_le_bytes(self.index(addr, 4)?.try_into().unwrap())) + pub fn load_u32(&self, addr: u32) -> Result { + 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 - .index_mut(addr, 1)? + .slice_mut(addr, 1)? .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)?; Ok(self - .index_mut(addr, 2)? + .slice_mut(addr, 2)? .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)?; Ok(self - .index_mut(addr, 4)? + .slice_mut(addr, 4)? .copy_from_slice(&value.to_le_bytes())) } } @@ -71,10 +67,13 @@ impl Memory { #[derive(Debug)] #[expect(dead_code)] pub enum Error { + Trap(&'static str), IllegalInstruction(InstCode, &'static str), InvalidMemoryAccess(u32), UnalignedPc(u32), UnaligneMemoryAccess { addr: u32, required_align: u32 }, + Ebreak, + Exit { code: i32 }, } pub struct Emulator { @@ -83,6 +82,10 @@ pub struct Emulator { /// Written to insterad of xreg[0]. pub xreg0_value: u32, pub pc: u32, + + pub debug: bool, + + pub ecall_handler: Box Result<(), Error>>, } impl Index for Emulator { @@ -102,11 +105,23 @@ impl IndexMut for Emulator { } } -struct Reg(u32); +#[derive(Clone, Copy)] +pub struct Reg(pub u32); +#[expect(dead_code)] impl Reg { - const RA: Reg = Reg(1); - const SP: Reg = Reg(2); + pub const RA: Reg = Reg(1); + 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 { @@ -122,9 +137,19 @@ impl Display for Reg { } enum Inst { + Lui { uimm: u32, dest: Reg }, + Auipc { uimm: u32, dest: Reg }, + Jal { offset: u32, 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 }, Lbu { 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 }, Sw { offset: u32, src: Reg, base: Reg }, - Addi { imm: u32, rd: Reg, rs1: Reg }, - Slti { imm: u32, rd: Reg, rs1: Reg }, - Sltiu { imm: u32, rd: Reg, rs1: Reg }, - Andi { imm: u32, rd: Reg, rs1: Reg }, - Ori { imm: u32, rd: Reg, rs1: Reg }, - Xori { imm: u32, rd: Reg, rs1: Reg }, + Addi { imm: u32, dest: Reg, src1: Reg }, + Slti { imm: u32, dest: Reg, src1: Reg }, + Sltiu { imm: u32, dest: Reg, src1: Reg }, + Xori { imm: u32, dest: Reg, src1: Reg }, + Ori { imm: u32, dest: Reg, src1: 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 { @@ -162,13 +311,15 @@ impl InstCode { let end_span = 32 - (range.end() + 1); (self.0 << (end_span)) >> (end_span + range.start()) } - fn opcode(self) -> u32 { self.0 & 0b1111111 } fn funct3(self) -> u32 { self.extract(12..=14) } + fn funct7(self) -> u32 { + self.extract(25..=31) + } fn rs1(self) -> Reg { Reg(self.extract(15..=19)) } @@ -182,18 +333,31 @@ impl InstCode { let imm = self.extract(20..=31); 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 { let imm_11_5 = self.extract(25..=31); let imm_4_0 = self.extract(7..=11); - let full_imm = (imm_11_5 << 5) | imm_4_0; - sign_extend(full_imm, 12) + let imm = (imm_11_5 << 5) | imm_4_0; + 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 { fn decode(code: InstCode) -> Result { - eprintln!("decoding: {code:?}"); 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 0b1101111 => Inst::Jal { offset: code.imm_j(), @@ -221,6 +394,40 @@ impl Inst { }, _ => 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 0b0000011 => match code.funct3() { 0b000 => Inst::Lb { @@ -273,76 +480,114 @@ impl Inst { 0b0010011 => match code.funct3() { 0b000 => Inst::Addi { imm: code.imm_i(), - rd: code.rd(), - rs1: code.rs1(), + dest: code.rd(), + src1: code.rs1(), }, 0b010 => Inst::Slti { imm: code.imm_i(), - rd: code.rd(), - rs1: code.rs1(), + dest: code.rd(), + src1: code.rs1(), }, 0b011 => Inst::Sltiu { imm: code.imm_i(), - rd: code.rd(), - rs1: code.rs1(), + dest: code.rd(), + src1: code.rs1(), }, 0b100 => Inst::Xori { imm: code.imm_i(), - rd: code.rd(), - rs1: code.rs1(), + dest: code.rd(), + src1: code.rs1(), }, 0b110 => Inst::Ori { imm: code.imm_i(), - rd: code.rd(), - rs1: code.rs1(), + dest: code.rd(), + src1: code.rs1(), }, 0b111 => Inst::Andi { imm: code.imm_i(), - rd: code.rd(), - rs1: code.rs1(), + dest: code.rd(), + 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")), }, + // 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")), }; - eprintln!(" parsed {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 { pub fn start_linux(&mut self) -> Result<(), Error> { // set top of stack. just some yolo address. with no values there. who needs abi? 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 { self.step()?; } @@ -357,20 +602,77 @@ impl Emulator { } 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 } => { let target = self.pc.wrapping_add(offset); self[dest] = self.pc.wrapping_add(4); self.set_pc(target)?; + jumped = true; } 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.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 } => { let addr = self[base].wrapping_add(offset); @@ -404,29 +706,56 @@ impl Emulator { let addr = self[base].wrapping_add(offset); self.mem.store_u32(addr, self[src] as u32)?; } - Inst::Addi { imm, rd, rs1 } => { - self[rd] = self[rs1].wrapping_add(imm as u32); + Inst::Addi { imm, dest, src1 } => { + self[dest] = self[src1].wrapping_add(imm as u32); } - Inst::Slti { imm, rd, rs1 } => { - let result = (self[rs1] as i32) < (imm as i32); - self[rd] = result as u32; + Inst::Slti { imm, dest, src1 } => { + let result = (self[src1] as i32) < (imm as i32); + self[dest] = result as u32; } - Inst::Sltiu { imm, rd, rs1 } => { - let result = (self[rs1] as u32) < imm as u32; - self[rd] = result as u32; + Inst::Sltiu { imm, dest, src1 } => { + let result = (self[src1] as u32) < imm as u32; + self[dest] = result as u32; } - Inst::Andi { imm, rd, rs1 } => { - self[rd] = self[rs1] & imm; + Inst::Andi { imm, dest, src1 } => { + self[dest] = self[src1] & imm; } - Inst::Ori { imm, rd, rs1 } => { - self[rd] = self[rs1] | imm; + Inst::Ori { imm, dest, src1 } => { + self[dest] = self[src1] | imm; } - Inst::Xori { imm, rd, rs1 } => { - self[rd] = self[rs1] ^ imm; + Inst::Xori { imm, dest, src1 } => { + 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(()) } } diff --git a/src/main.rs b/src/main.rs index f6887b5..d07160c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,9 @@ -use aligned_vec::avec; +use emu::{Memory, Reg}; use eyre::{OptionExt, bail}; mod elf; mod emu; -const PAGE_SIZE: usize = 4096; - // 2 MiB const MEMORY_SIZE: usize = 2 << 21; @@ -15,16 +13,13 @@ fn main() -> eyre::Result<()> { let elf = elf::Elf { content }; let header = elf.header()?; - dbg!(&header); - let segments = elf.segments()?; let mut mem = emu::Memory { - mem: avec![[PAGE_SIZE]| 0; MEMORY_SIZE], + mem: vec![0; MEMORY_SIZE], }; for phdr in segments { - dbg!(&phdr); match phdr.p_type { // PT_NULL 0 => {} @@ -46,12 +41,16 @@ fn main() -> eyre::Result<()> { .copy_from_slice(contents); } } + // PT_DYNAMIC + 2 => {} // PT_PHDR 6 => {} // PT_GNU_EH_FRAME 1685382480 => {} // PT_GNU_STACK 1685382481 => {} + // PT_GNU_RELRO + 1685382482 => {} // PT_RISCV_ATTRIBUTES 0x70000003 => {} _ => bail!("unknown program header type: {}", phdr.p_type), @@ -65,8 +64,64 @@ fn main() -> eyre::Result<()> { xreg: [0; 32], xreg0_value: 0, pc: start, + + debug: std::env::args().any(|arg| arg == "--debug"), + + ecall_handler: Box::new(ecall_handler), }; emu.start_linux().unwrap(); 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(()) +} diff --git a/test/Makefile b/test/Makefile index ad2b298..b9eccec 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,5 +1,12 @@ CC = clang -Wall -Wpedantic -target riscv32-unknown-linux-gnu -fuse-ld=lld -march=rv32i CC_STATIC = $(CC) -static -nostdlib -nodefaultlibs +RUSTC = rustc --target riscv32i-unknown-none-elf + +all: init init1 + init: init.c $(CC_STATIC) init.c -o init + +init1: init1.rs + $(RUSTC) init1.rs diff --git a/test/init.c b/test/init.c index 6d147da..a4fa34a 100644 --- a/test/init.c +++ b/test/init.c @@ -1,11 +1,20 @@ -void _start(void) +// + +_Noreturn void __attribute__ ((noinline)) exit(int code) { - __asm__ volatile ( + __asm__ volatile( "li a7, 93;" // exit - "li a0, 0;" // code 0 + "mv a0, %0;" // code "ecall" : - : + : "r"(code) : "a7", "a0", "memory" ); + __builtin_unreachable(); +} + +_Noreturn void _start(void) +{ + + exit(10); } diff --git a/test/init1.rs b/test/init1.rs new file mode 100644 index 0000000..f3e7a8e --- /dev/null +++ b/test/init1.rs @@ -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::().unwrap(); + + exit(n); +}