const TEST_MEMORY_CAPACITY: u64 = 1024 * 512;
const PROGRAM_MEMORY_CAPACITY: u64 = 1024 * 1024 * 128;
pub mod cpu;
pub mod terminal;
pub mod default_terminal;
pub mod memory;
pub mod mmu;
pub mod device;
use cpu::{Cpu, Xlen};
use terminal::Terminal;
pub struct Emulator {
cpu: Cpu,
is_test: bool,
tohost_addr: u64
}
struct SectionHeader {
sh_name: u64,
_sh_type: u64,
_sh_flags: u64,
sh_addr: u64,
sh_offset: u64,
sh_size: u64,
_sh_link: u64,
_sh_info: u64,
_sh_addralign: u64,
_sh_entsize: u64
}
impl Emulator {
pub fn new(terminal: Box<dyn Terminal>) -> Self {
Emulator {
cpu: Cpu::new(terminal),
is_test: false,
tohost_addr: 0
}
}
pub fn run(&mut self) {
match self.is_test {
true => self.run_test(),
false => self.run_program()
};
}
pub fn run_program(&mut self) {
loop {
self.tick();
}
}
pub fn run_test(&mut self) {
println!("This elf file seems riscv-tests elf file. Running in test mode.");
loop {
let disas = self.cpu.disassemble_next_instruction();
self.put_bytes_to_terminal(disas.as_bytes());
self.put_bytes_to_terminal(&[10]);
self.tick();
let endcode = self.cpu.get_mut_mmu().load_word_raw(self.tohost_addr);
if endcode != 0 {
match endcode {
1 => {
self.put_bytes_to_terminal(format!("Test Passed with {:X}\n", endcode).as_bytes())
},
_ => {
self.put_bytes_to_terminal(format!("Test Failed with {:X}\n", endcode).as_bytes())
}
};
break;
}
}
}
fn put_bytes_to_terminal(&mut self, bytes: &[u8]) {
for i in 0..bytes.len() {
self.cpu.get_mut_terminal().put_byte(bytes[i]);
}
}
pub fn tick(&mut self) {
self.cpu.tick();
}
pub fn setup_program(&mut self, data: Vec<u8>) {
if data[0] != 0x7f || data[1] != 0x45 || data[2] != 0x4c || data[3] != 0x46 {
panic!("This file does not seem ELF file");
}
let e_class = data[4];
let e_width = match e_class {
1 => 32,
2 => 64,
_ => panic!("Unknown e_class:{:X}", e_class)
};
let _e_endian = data[5];
let _e_elf_version = data[6];
let _e_osabi = data[7];
let _e_abi_version = data[8];
let mut offset = 0x10;
let mut _e_type = 0 as u64;
for i in 0..2 {
_e_type |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut _e_machine = 0 as u64;
for i in 0..2 {
_e_machine |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut _e_version = 0 as u64;
for i in 0..4 {
_e_version |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut e_entry = 0 as u64;
for i in 0..e_width / 8 {
e_entry |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut _e_phoff = 0 as u64;
for i in 0..e_width / 8 {
_e_phoff |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut e_shoff = 0 as u64;
for i in 0..e_width / 8 {
e_shoff |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut _e_flags = 0 as u64;
for i in 0..4 {
_e_flags |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut _e_ehsize = 0 as u64;
for i in 0..2 {
_e_ehsize |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut _e_phentsize = 0 as u64;
for i in 0..2 {
_e_phentsize |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut _e_phnum = 0 as u64;
for i in 0..2 {
_e_phnum |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut _e_shentsize = 0 as u64;
for i in 0..2 {
_e_shentsize |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut e_shnum = 0 as u64;
for i in 0..2 {
e_shnum |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut _e_shstrndx = 0 as u64;
for i in 0..2 {
_e_shstrndx |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut program_data_section_headers = vec![];
let mut string_table_section_headers = vec![];
offset = e_shoff as usize;
for _i in 0..e_shnum {
let mut sh_name = 0 as u64;
for i in 0..4 {
sh_name |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut sh_type = 0 as u64;
for i in 0..4 {
sh_type |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut sh_flags = 0 as u64;
for i in 0..e_width / 8 {
sh_flags |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut sh_addr = 0 as u64;
for i in 0..e_width / 8 {
sh_addr |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut sh_offset = 0 as u64;
for i in 0..e_width / 8 {
sh_offset |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut sh_size = 0 as u64;
for i in 0..e_width / 8 {
sh_size |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut sh_link = 0 as u64;
for i in 0..4 {
sh_link |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut sh_info = 0 as u64;
for i in 0..4 {
sh_info |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut sh_addralign = 0 as u64;
for i in 0..e_width / 8 {
sh_addralign |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let mut sh_entsize = 0 as u64;
for i in 0..e_width / 8 {
sh_entsize |= (data[offset] as u64) << (8 * i);
offset += 1;
}
let section_header = SectionHeader {
sh_name: sh_name,
_sh_type: sh_type,
_sh_flags: sh_flags,
sh_addr: sh_addr,
sh_offset: sh_offset,
sh_size: sh_size,
_sh_link: sh_link,
_sh_info: sh_info,
_sh_addralign: sh_addralign,
_sh_entsize: sh_entsize
};
if sh_type == 1 {
program_data_section_headers.push(section_header);
} else if sh_type == 3 {
string_table_section_headers.push(section_header);
}
}
let tohost_values = vec![0x2e, 0x74, 0x6f, 0x68, 0x6f, 0x73, 0x74, 0x00]; let mut tohost_addr = 0; for i in 0..program_data_section_headers.len() {
let sh_addr = program_data_section_headers[i].sh_addr;
let sh_name = program_data_section_headers[i].sh_name;
for j in 0..string_table_section_headers.len() {
let sh_offset = string_table_section_headers[j].sh_offset;
let sh_size = string_table_section_headers[j].sh_size;
let mut found = true;
for k in 0..tohost_values.len() as u64{
let addr = sh_offset + sh_name + k;
if addr >= sh_offset + sh_size || data[addr as usize] != tohost_values[k as usize] {
found = false;
break;
}
}
if found {
tohost_addr = sh_addr;
}
}
if tohost_addr != 0 {
break;
}
}
self.cpu.update_xlen(match e_width {
32 => Xlen::Bit32,
64 => Xlen::Bit64,
_ => panic!("No happen")
});
if tohost_addr != 0 {
self.is_test = true;
self.tohost_addr = tohost_addr;
self.cpu.get_mut_mmu().init_memory(TEST_MEMORY_CAPACITY);
} else {
self.is_test = false;
self.tohost_addr = 0;
self.cpu.get_mut_mmu().init_memory(PROGRAM_MEMORY_CAPACITY);
}
for i in 0..program_data_section_headers.len() {
let sh_addr = program_data_section_headers[i].sh_addr;
let sh_offset = program_data_section_headers[i].sh_offset;
let sh_size = program_data_section_headers[i].sh_size;
if sh_addr >= 0x80000000 && sh_offset > 0 && sh_size > 0 {
for j in 0..sh_size as usize {
self.cpu.get_mut_mmu().store_raw(sh_addr + j as u64, data[sh_offset as usize + j]);
}
}
}
self.cpu.update_pc(e_entry);
}
pub fn setup_filesystem(&mut self, content: Vec<u8>) {
self.cpu.get_mut_mmu().init_disk(content);
}
pub fn setup_dtb(&mut self, content: Vec<u8>) {
self.cpu.get_mut_mmu().init_dtb(content);
}
pub fn update_xlen(&mut self, xlen: Xlen) {
self.cpu.update_xlen(xlen);
}
pub fn get_mut_terminal(&mut self) -> &mut Box<dyn Terminal> {
self.cpu.get_mut_terminal()
}
pub fn get_cpu(&self) -> &Cpu {
&self.cpu
}
pub fn get_mut_cpu(&mut self) -> &mut Cpu {
&mut self.cpu
}
}