pub mod core; pub mod mem; pub mod state; use crate::core::*; use crate::mem::{CpuMemory, LogicalMemory}; use crate::state::CpuState; const MEMORY_SIZE: usize = 64 * 1024 * 1024; const REGISTER_NAME_WIDTH: u32 = 0b11111; const RO_TARGET: u32 = 0x80000000; const FDT_LOCATION: u32 = 0x83000000; const RINGBUF_ADDR: usize = 0x82000000; const RINGBUF_SIZE: usize = 0x100000; fn write_all(memory: &mut CpuMemory, value: &[u8], addr: u32, size: u32) { let mut remaining_size = size; let mut curr_addr = addr; let mut idx = 0; while remaining_size >= 4 { memory .write_word( curr_addr, u32::from_le_bytes(value[idx..(idx + 4)].try_into().unwrap()), ) .unwrap(); remaining_size -= 4; curr_addr += 4; idx += 4; } while remaining_size >= 2 { memory .write_halfword( curr_addr, u16::from_le_bytes(value[idx..(idx + 2)].try_into().unwrap()), ) .unwrap(); remaining_size -= 2; curr_addr += 2; idx += 2; } while remaining_size >= 1 { memory.write(curr_addr, value[idx]).unwrap(); remaining_size -= 1; curr_addr += 1; idx += 1; } } fn read_all(memory: &CpuMemory, addr: u32, size: u32) -> Vec { let mut ret = Vec::new(); for a in addr..addr + size { ret.push(memory.read(a).unwrap()); } ret } fn main() { let path = std::path::PathBuf::from("wkern.bin"); let f_dat = std::fs::read(path).expect("Unable to read file"); let dtb_dat = std::fs::read(env!("DTB_PATH")).expect("Unable to read file"); let mut memory = CpuMemory { memory: vec![0u8; MEMORY_SIZE].into_boxed_slice(), devices: Vec::new(), }; let ram_device = LogicalMemory { mode: false, asid: 0, ppn: 0, base: 0x80000000, }; memory.register_device( 0x80000000..0x80000000 + MEMORY_SIZE as u32, Box::new(ram_device), ); let mut state = CpuState::new(memory); write_all( &mut state.memory, f_dat.as_slice(), RO_TARGET, f_dat.len() as u32, ); write_all( &mut state.memory, dtb_dat.as_slice(), FDT_LOCATION, dtb_dat.len() as u32, ); state.pc = 0x80000000; state.registers[10] = 0; state.registers[11] = FDT_LOCATION; println!("loaded!"); loop { { handle_instruction(&mut state); state.registers[0] = 0; } } } fn handle_instruction(state: &mut CpuState) { let pc = state.pc; let word = state.memory.read_word(pc).unwrap(); let opcode = word & 0x7f; match opcode { 0b0110011 => { let f7 = word >> 25; let f3 = (word >> 12) & 0b111; let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; match (f7 << 3) | f3 { 0 => add(state, rd, rs1, rs2), 256 => sub(state, rd, rs1, rs2), 4 => xor(state, rd, rs1, rs2), 6 => or(state, rd, rs1, rs2), 7 => and(state, rd, rs1, rs2), 1 => sll(state, rd, rs1, rs2), 5 => srl(state, rd, rs1, rs2), 261 => sra(state, rd, rs1, rs2), 2 => slt(state, rd, rs1, rs2), 3 => sltu(state, rd, rs1, rs2), 8 => mul(state, rd, rs1, rs2), 9 => mulh(state, rd, rs1, rs2), 10 => mulhsu(state, rd, rs1, rs2), 11 => mulhu(state, rd, rs1, rs2), 12 => div(state, rd, rs1, rs2), 13 => divu(state, rd, rs1, rs2), 14 => rem(state, rd, rs1, rs2), 15 => remu(state, rd, rs1, rs2), _ => panic!("Illegal instruction: {:x}", word), } } 0b0010011 => { let imm = ((word as i32) >> 20) as u32; let f3 = (word >> 12) & 0b111; let rs1 = ((word >> 15) & 0b11111) as usize; let rd = ((word >> 7) & 0b11111) as usize; match f3 { 0 => addi(state, rd, rs1, imm), 4 => xori(state, rd, rs1, imm), 6 => ori(state, rd, rs1, imm), 7 => andi(state, rd, rs1, imm), 1 => { if (imm >> 5) != 0 { panic!("Illegal instruction: {:x}", word); } slli(state, rd, rs1, imm); } 5 => { if (imm >> 5) != 0 { panic!("Illegal instruction: {:x}", word); } if (imm >> 5) & 0b1111111 == 0 { srli(state, rd, rs1, imm); } else { srai(state, rd, rs1, imm); } } 2 => slti(state, rd, rs1, imm), 3 => sltiu(state, rd, rs1, imm), _ => panic!("Illegal instruction: {:x}", word), } } 0b0000011 => { let imm = ((word as i32) >> 20) as u32; let f3 = (word >> 12) & 0b111; let rs1 = ((word >> 15) & 0b11111) as usize; let rd = ((word >> 7) & 0b11111) as usize; match f3 { 0 => lb(state, rd, rs1, imm), 1 => lh(state, rd, rs1, imm), 2 => lw(state, rd, rs1, imm), 4 => lbu(state, rd, rs1, imm), 5 => lhu(state, rd, rs1, imm), _ => panic!("Illegal instruction: {:x}", word), } } 0b0100011 => { let imm_low = (word >> 7) & 0b11111; let imm_high = (word >> 25) & 0b1111111; let f3 = (word >> 12) & 0b111; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize; let imm = ((((imm_high << 5) | imm_low) as i32) << 20 >> 20) as u32; match f3 { 0 => sb(state, rs1, rs2, imm), 1 => sh(state, rs1, rs2, imm), 2 => sw(state, rs1, rs2, imm), _ => panic!("Illegal instruction: {:x}", word), }; } 0b1100011 => { let f3 = (word >> 12) & 0b111; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize; let imm = (((word >> 31) & 0x1) << 12) | (((word >> 7) & 0x1) << 11) | (((word >> 25) & 0x3f) << 5) | (((word >> 8) & 0xf) << 1); let imm = ((imm as i32) << 19 >> 19) as u32; match f3 { 0 => beq(state, rs1, rs2, imm), 1 => bne(state, rs1, rs2, imm), 4 => blt(state, rs1, rs2, imm), 5 => bge(state, rs1, rs2, imm), 6 => bltu(state, rs1, rs2, imm), 7 => bgeu(state, rs1, rs2, imm), _ => panic!("Illegal instruction: {:x}", word), } return; } 0b1101111 => { let rd = (word >> 7) & REGISTER_NAME_WIDTH; let imm = (((word >> 31) & 0x1) << 20) | (((word >> 12) & 0xFF) << 12) | (((word >> 20) & 0x1) << 11) | (((word >> 21) & 0x3FF) << 1); let imm = ((imm as i32) << 11 >> 11) as u32; jal(state, rd as usize, imm); return; } 0b1100111 => { let rs1 = (word >> 15) & REGISTER_NAME_WIDTH; let rd = (word >> 7) & REGISTER_NAME_WIDTH; let imm = word >> 20; let sentinel = (word >> 12) & 0b111; if sentinel != 0 { panic!("Illegal instruction: {:x}", word); } jalr(state, rd as usize, rs1 as usize, imm); return; } 0b0110111 => { let imm = word >> 12; let rd = ((word >> 7) & 0b11111) as usize; lui(state, rd, imm); } 0b0010111 => { let imm = word >> 12; let rd = (word >> 7) & REGISTER_NAME_WIDTH; auipc(state, rd as usize, imm); } 0b1110011 => { let imm = word >> 20; let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let f3 = (word >> 12) & 0b111; match f3 { 0 => { match imm { 0 => trap_to_kernel(state), // ecall 258 => return_to_userspace(state), // sret 261 => { println!("wfi"); exit(state); } _ => panic!("Illegal instruction: {:x}", word), } } // ecall 1 => csrrw(state, rd, rs1, imm as usize), 2 => csrrs(state, rd, rs1, imm as usize), 3 => csrrc(state, rd, rs1, imm as usize), 5 => csrrwi(state, rd, rs1 as u32, imm as usize), 6 => csrrsi(state, rd, rs1 as u32, imm as usize), 7 => csrrci(state, rd, rs1 as u32, imm as usize), _ => panic!("Illegal instruction: {:x}", word), } } // ecall (ebreak not implemented) 0b0101111 => { let f5 = word >> 27; let f3 = (word >> 12) & 0b111; let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize; if f3 != 2 { panic!("Illegal instruction: {:x}", word); } match f5 { 2 => lr_w(state, rd, rs1), 3 => sc_w(state, rd, rs1, rs2), 1 => amoswap_w(state, rd, rs1, rs2), 0 => amoadd_w(state, rd, rs1, rs2), 12 => amoand_w(state, rd, rs1, rs2), 8 => amoor_w(state, rd, rs1, rs2), 4 => amoxor_w(state, rd, rs1, rs2), 20 => amomax_w(state, rd, rs1, rs2), 16 => amomin_w(state, rd, rs1, rs2), 28 => amomaxu_w(state, rd, rs1, rs2), 24 => amominu_w(state, rd, rs1, rs2), _ => panic!("Illegal instruction: {:x}", word), } } 0b1000011 => { // todo: handle rm bit let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize; let rs3 = ((word >> 27) & REGISTER_NAME_WIDTH) as usize; fmadd_s(state, rd, rs1, rs2, rs3); } 0b1000111 => { // todo: handle rm bit let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize; let rs3 = ((word >> 27) & REGISTER_NAME_WIDTH) as usize; fmsub_s(state, rd, rs1, rs2, rs3); } 0b1001011 => { // todo: handle rm bit let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize; let rs3 = ((word >> 27) & REGISTER_NAME_WIDTH) as usize; fnmsub_s(state, rd, rs1, rs2, rs3); } 0b1001111 => { // todo: handle rm bit let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize; let rs3 = ((word >> 27) & REGISTER_NAME_WIDTH) as usize; fnmadd_s(state, rd, rs1, rs2, rs3); } 0b1010011 => { // todo: handle rm bit let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize; let f5 = word >> 27; let rm = (word >> 12) & 0b111; match f5 { 0 => fadd_s(state, rd, rs1, rs2), 1 => fsub_s(state, rd, rs1, rs2), 2 => fmul_s(state, rd, rs1, rs2), 3 => fdiv_s(state, rd, rs1, rs2), 11 => fsqrt_s(state, rd, rs1), 4 => match rm { 0 => fsgnj_s(state, rd, rs1, rs2), 1 => fsgnjn_s(state, rd, rs1, rs2), 2 => fsgnjx_s(state, rd, rs1, rs2), _ => panic!("Illegal instruction: {:x}", rm), }, 5 => match rm { 0 => fmin_s(state, rd, rs1, rs2), 1 => fmax_s(state, rd, rs1, rs2), _ => panic!("Illegal instruction: {:x}", rm), }, 24 => { fcvt_wu_s(state, rd, rs1); } 28 => { match rm { 0 => fmv_x_w(state, rd, rs1), 1 => fclass_s(state, rd, rs1), _ => panic!("Illegal instruction: {:x}", rm), }; } 20 => match rm { 0 => feq_s(state, rd, rs1, rs2), 1 => flt_s(state, rd, rs1, rs2), 2 => fle_s(state, rd, rs1, rs2), _ => panic!("Illegal instruction: {:x}", rm), }, _ => panic!("Illegal instruction: {:x}", word), } } 0b1010101 => { let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; fcvt_w_s(state, rd, rs1); } _ => panic!("Illegal instruction or todo: {:032b}", word), } state.pc += 4; } fn trap_to_kernel(state: &mut CpuState) { state.csr[0x141 /* sepc */] = state.pc; state.pc = state.csr[0x105 /* stvec */]; } fn return_to_userspace(state: &mut CpuState) { state.pc = state.csr[0x141 /* sepc */] + 4; } fn exit(state: &mut CpuState) { let data = read_all(&state.memory, RINGBUF_ADDR as u32, RINGBUF_SIZE as u32); std::fs::write("core_dump.txt", data).unwrap(); std::process::exit(0); }