add MMU support (SV32) and fix a bunch of bugs

This commit is contained in:
Stefan 2021-08-17 09:31:46 +02:00
parent 7f47c7655d
commit cb6d05178b
11 changed files with 566 additions and 90 deletions

3
.gitmodules vendored
View File

@ -10,3 +10,6 @@
[submodule "opensbi"]
path = opensbi
url = https://github.com/riscv/opensbi
[submodule "linux"]
path = linux
url = git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

1
linux Submodule

@ -0,0 +1 @@
Subproject commit 25423f4bd9a9ac3e6b0ce7ecfe56c36f4e514893

View File

@ -3,12 +3,13 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "types.h"
#include "mem.h"
#include "emu.h"
#include "csr.h"
#include "mmu.h"
cpu_t cpu_init(uint8_t *mem, uint8_t *dtb) {
cpu_t cpu_init(uint8_t *mem, uint8_t *dtb, uint8_t *mtd, uint mtd_size) {
cpu_t ret;
ret.clock = 0;
for (uint i = 0; i < 32; i++) {
@ -22,6 +23,8 @@ cpu_t cpu_init(uint8_t *mem, uint8_t *dtb) {
init_csrs(&ret);
ret.dtb = dtb;
ret.mtd = mtd;
ret.mtd_size = mtd_size;
ret.clint.msip = false;
ret.clint.mtimecmp_lo = 0;
@ -34,9 +37,14 @@ cpu_t cpu_init(uint8_t *mem, uint8_t *dtb) {
ret.uart.thre_ip = false;
ret.uart.interrupting = false;
ret.mmu.mode = 0;
ret.mmu.ppn = 0;
return ret;
}
static char *cmd_buf = NULL;
void cpu_dump(cpu_t *cpu) {
printf("CPU state @%d:\n", cpu->clock);
for (int i = 0; i < 32; i += 4) {
@ -47,7 +55,28 @@ void cpu_dump(cpu_t *cpu) {
i+3, cpu->xreg[i+3]);
}
printf(" .pc = %08x\n", cpu->pc);
printf(" next ins: %08x\n", *(uint*)(cpu->mem + (cpu->pc & 0x7FFFFFFF)));
/* ins_ret ret; */
/* printf(" next ins: %08x\n", *(uint*)(cpu->mem + mmu_translate(&ret, cpu->pc & 0x7FFFFFFF, MMU_ACCESS_FETCH))); */
/* if (cmd_buf == NULL) { */
/* cmd_buf = malloc(4096); */
/* } */
/* sprintf(cmd_buf, "addr2line -e testinit/init %x", cpu->pc); */
/* system(cmd_buf); */
uint arb[8];
for (int iarb = 0; iarb < 8; iarb++) {
uint arb_t = cpu->xreg[iarb*4+0] ^
cpu->xreg[iarb*4+1] ^
cpu->xreg[iarb*4+2] ^
cpu->xreg[iarb*4+3];
printf("arb%d=0x%08x ", iarb, arb_t);
if (iarb == 3) printf("\n");
arb[iarb] = arb_t;
}
printf("\nARB=0x%08x\n", arb[0] ^ arb[1] ^ arb[2] ^ arb[3] ^ arb[4] ^ arb[5] ^ arb[6] ^ arb[7] ^ cpu->pc);
printf("\n");
}
void cpu_tick(cpu_t *cpu) {

View File

@ -5,6 +5,7 @@
#include <stdio.h>
#include "types.h"
#include "trap.h"
#include "mmu.h"
const uint CSR_USTATUS = 0x000;
const uint CSR_UIE = 0x004;
@ -44,6 +45,16 @@ const uint CSR_TIME = 0xc01;
const uint _CSR_INSERT = 0xc02;
const uint CSR_MHARTID = 0xf14;
const uint CSR_MEMOP_OP = 0x0b0;
const uint CSR_MEMOP_SRC = 0x0b1;
const uint CSR_MEMOP_DST = 0x0b2;
const uint CSR_MEMOP_N = 0x0b3;
#define MMU_ACCESS_FETCH 0
#define MMU_ACCESS_READ 1
#define MMU_ACCESS_WRITE 2
extern uint mmu_translate(ins_ret *ins, uint addr, uint mode);
bool has_csr_access_privilege(cpu_t *cpu, uint addr) {
uint privilege = (addr >> 8) & 0x3;
return privilege <= cpu->csr.privilege;
@ -59,6 +70,7 @@ uint read_csr_raw(cpu_t *cpu, uint address) {
case CSR_MCYCLE: return cpu->clock;
case CSR_CYCLE: return cpu->clock;
case CSR_MHARTID: return 0;
case CSR_SATP: return (cpu->mmu.mode << 31) | cpu->mmu.ppn;
default: return cpu->csr.data[address & 0xffff];
}
}
@ -66,31 +78,33 @@ uint read_csr_raw(cpu_t *cpu, uint address) {
void write_csr_raw(cpu_t *cpu, uint address, uint value) {
switch (address) {
case CSR_SSTATUS:
cpu->csr.data[CSR_MSTATUS] &= !0x000de162;
cpu->csr.data[CSR_MSTATUS] &= ~0x000de162;
cpu->csr.data[CSR_MSTATUS] |= value & 0x000de162;
/* self.mmu.update_mstatus(self.read_csr_raw(CSR_MSTATUS)); */
break;
case CSR_SIE:
cpu->csr.data[CSR_MIE] &= !0x222;
cpu->csr.data[CSR_MIE] &= ~0x222;
cpu->csr.data[CSR_MIE] |= value & 0x222;
break;
case CSR_SIP:
cpu->csr.data[CSR_MIP] &= !0x222;
cpu->csr.data[CSR_MIP] &= ~0x222;
cpu->csr.data[CSR_MIP] |= value & 0x222;
break;
case CSR_MIDELEG:
cpu->csr.data[address] = value & 0x666; // from qemu
break;
/* case CSR_MSTATUS: */
/* cpu->csr.data[address] = value; */
/* self.mmu.update_mstatus(self.read_csr_raw(CSR_MSTATUS)); */
/* break; */
/* CSR_TIME => { */
/* self.mmu.get_mut_clint().write_mtime(value); */
/* }, */
case CSR_TIME:
// ignore writes
break;
case CSR_MEMOP_OP:
/* printf("MEMOP:%08x<-%08x(%d)\n", cpu->csr.data[CSR_MEMOP_DST], cpu->csr.data[CSR_MEMOP_SRC], cpu->csr.data[CSR_MEMOP_N]); */
{
ins_ret ins = ins_ret_noop(cpu);
uint src = mmu_translate(&ins, cpu->csr.data[CSR_MEMOP_SRC], MMU_ACCESS_READ);
uint dst = mmu_translate(&ins, cpu->csr.data[CSR_MEMOP_DST], MMU_ACCESS_WRITE);
uint n = cpu->csr.data[CSR_MEMOP_N];
memcpy(&cpu->mem[dst & (~0x80000000)], &cpu->mem[src & (~0x80000000)], n);
}
break;
default: cpu->csr.data[address] = value; break;
};
}
@ -99,7 +113,7 @@ void write_csr_raw(cpu_t *cpu, uint address, uint value) {
uint get_csr(cpu_t *cpu, uint address, ins_ret *ret) {
if (has_csr_access_privilege(cpu, address)) {
uint r = read_csr_raw(cpu, address);
if (VERBOSE >= 2)
if (VERBOSE >= 3)
printf("CSR read @%03x = %08x\n", address, r);
return r;
} else {
@ -111,8 +125,9 @@ uint get_csr(cpu_t *cpu, uint address, ins_ret *ret) {
}
void set_csr(cpu_t *cpu, uint address, uint value, ins_ret *ret) {
if (VERBOSE >= 2)
if (VERBOSE >= 3)
printf("CSR write @%03x = %08x\n", address, value);
if (has_csr_access_privilege(cpu, address)) {
bool read_only = ((address >> 10) & 0x3) == 0x3;
if (read_only) {
@ -121,9 +136,9 @@ void set_csr(cpu_t *cpu, uint address, uint value, ins_ret *ret) {
ret->trap.value = cpu->pc;
} else {
if (address == CSR_SATP) {
// TODO: update MMU addressing mode
if (VERBOSE >= 1)
printf("WARN: Ignoring write to CSR_SATP (%08x)\n", value);
printf("Write to CSR_SATP (%08x)\n", value);
mmu_update(value);
return;
}
write_csr_raw(cpu, address, value);

138
src/emu.h
View File

@ -6,6 +6,7 @@
#include "types.h"
#include "ins.h"
#include "mem.h"
#include "mmu.h"
#include "trap.h"
#include "csr.h"
@ -32,52 +33,70 @@ DEF(addi, FormatI, { // rv32i
WR_RD(AS_SIGNED(cpu->xreg[ins.rs1]) + AS_SIGNED(ins.imm));
})
DEF(amoswap_w, FormatR, { // rv32a
uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]);
mem_set_word(cpu, cpu->xreg[ins.rs1], cpu->xreg[ins.rs2]);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
if (ret->trap.en) return;
uint tmp = mem_get_word(cpu, addr);
mem_set_word(cpu, addr, cpu->xreg[ins.rs2]);
WR_RD(tmp)
})
DEF(amoadd_w, FormatR, { // rv32a
uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]);
mem_set_word(cpu, cpu->xreg[ins.rs1], cpu->xreg[ins.rs2] + tmp);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
if (ret->trap.en) return;
uint tmp = mem_get_word(cpu, addr);
mem_set_word(cpu, addr, cpu->xreg[ins.rs2] + tmp);
WR_RD(tmp)
})
DEF(amoxor_w, FormatR, { // rv32a
uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]);
mem_set_word(cpu, cpu->xreg[ins.rs1], cpu->xreg[ins.rs2] ^ tmp);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
if (ret->trap.en) return;
uint tmp = mem_get_word(cpu, addr);
mem_set_word(cpu, addr, cpu->xreg[ins.rs2] ^ tmp);
WR_RD(tmp)
})
DEF(amoand_w, FormatR, { // rv32a
uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]);
mem_set_word(cpu, cpu->xreg[ins.rs1], cpu->xreg[ins.rs2] & tmp);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
if (ret->trap.en) return;
uint tmp = mem_get_word(cpu, addr);
mem_set_word(cpu, addr, cpu->xreg[ins.rs2] & tmp);
WR_RD(tmp)
})
DEF(amoor_w, FormatR, { // rv32a
uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]);
mem_set_word(cpu, cpu->xreg[ins.rs1], cpu->xreg[ins.rs2] | tmp);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
if (ret->trap.en) return;
uint tmp = mem_get_word(cpu, addr);
mem_set_word(cpu, addr, cpu->xreg[ins.rs2] | tmp);
WR_RD(tmp)
})
DEF(amomin_w, FormatR, { // rv32a
uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
if (ret->trap.en) return;
uint tmp = mem_get_word(cpu, addr);
uint sec = cpu->xreg[ins.rs2];
mem_set_word(cpu, cpu->xreg[ins.rs1], AS_SIGNED(sec) < AS_SIGNED(tmp) ? sec : tmp);
mem_set_word(cpu, addr, AS_SIGNED(sec) < AS_SIGNED(tmp) ? sec : tmp);
WR_RD(tmp)
})
DEF(amomax_w, FormatR, { // rv32a
uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
if (ret->trap.en) return;
uint tmp = mem_get_word(cpu, addr);
uint sec = cpu->xreg[ins.rs2];
mem_set_word(cpu, cpu->xreg[ins.rs1], AS_SIGNED(sec) > AS_SIGNED(tmp) ? sec : tmp);
mem_set_word(cpu, addr, AS_SIGNED(sec) > AS_SIGNED(tmp) ? sec : tmp);
WR_RD(tmp)
})
DEF(amominu_w, FormatR, { // rv32a
uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
if (ret->trap.en) return;
uint tmp = mem_get_word(cpu, addr);
uint sec = cpu->xreg[ins.rs2];
mem_set_word(cpu, cpu->xreg[ins.rs1], sec < tmp ? sec : tmp);
mem_set_word(cpu, addr, sec < tmp ? sec : tmp);
WR_RD(tmp)
})
DEF(amomaxu_w, FormatR, { // rv32a
uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
if (ret->trap.en) return;
uint tmp = mem_get_word(cpu, addr);
uint sec = cpu->xreg[ins.rs2];
mem_set_word(cpu, cpu->xreg[ins.rs1], sec > tmp ? sec : tmp);
mem_set_word(cpu, addr, sec > tmp ? sec : tmp);
WR_RD(tmp)
})
DEF(and, FormatR, { // rv32i
@ -179,20 +198,23 @@ DEF(divu, FormatR, { // rv32m
WR_RD(result)
})
DEF(ebreak, FormatEmpty, { // system
// unnecessary?
printf("EBREAK!\n");
VERBOSE=4;
SINGLE_STEP=1;
})
DEF(ecall, FormatEmpty, { // system
if (cpu->xreg[17] == 93) {
// EXIT CALL
uint status = cpu->xreg[10] >> 1;
printf("ecall EXIT = %d (0x%x)\n", status, status);
exit(status);
}
/* if (cpu->xreg[17] == 93) { */
/* // EXIT CALL */
/* uint status = cpu->xreg[10] >> 1; */
/* printf("ecall EXIT = %d (0x%x)\n", status, status); */
/* exit(status); */
/* } */
ret->trap.en = true;
ret->trap.value = cpu->pc;
if (cpu->csr.privilege == PRIV_USER) {
ret->trap.type = trap_EnvironmentCallFromUMode;
/* printf("SYSCALL! nr=%d @0x%08x\n", cpu->xreg[17], cpu->pc); */
} else if (cpu->csr.privilege == PRIV_SUPERVISOR) {
ret->trap.type = trap_EnvironmentCallFromSMode;
} else { // PRIV_MACHINE
@ -214,23 +236,32 @@ DEF(jalr, FormatI, { // rv32i
WR_PC(cpu->xreg[ins.rs1] + ins.imm);
})
DEF(lb, FormatI, { // rv32i
uint tmp = sign_extend(mem_get_byte(cpu, cpu->xreg[ins.rs1] + ins.imm), 8);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_READ);
if (ret->trap.en) return;
uint tmp = sign_extend(mem_get_byte(cpu, addr), 8);
WR_RD(tmp)
})
DEF(lbu, FormatI, { // rv32i
uint tmp = mem_get_byte(cpu, cpu->xreg[ins.rs1] + ins.imm);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_READ);
if (ret->trap.en) return;
uint tmp = mem_get_byte(cpu, addr);
WR_RD(tmp)
})
DEF(lh, FormatI, { // rv32i
uint tmp = sign_extend(mem_get_half_word(cpu, cpu->xreg[ins.rs1] + ins.imm), 16);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_READ);
if (ret->trap.en) return;
uint tmp = sign_extend(mem_get_half_word(cpu, addr), 16);
WR_RD(tmp)
})
DEF(lhu, FormatI, { // rv32i
uint tmp = mem_get_half_word(cpu, cpu->xreg[ins.rs1] + ins.imm);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_READ);
if (ret->trap.en) return;
uint tmp = mem_get_half_word(cpu, addr);
WR_RD(tmp)
})
DEF(lr_w, FormatR, { // rv32a
uint addr = cpu->xreg[ins.rs1];
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_READ);
if (ret->trap.en) return;
uint tmp = mem_get_word(cpu, addr);
cpu->reservation_en = true;
cpu->reservation_addr = addr;
@ -241,7 +272,9 @@ DEF(lui, FormatU, { // rv32i
})
DEF(lw, FormatI, { // rv32i
// would need sign extend for xlen > 32
uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1] + ins.imm);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_READ);
if (ret->trap.en) return;
uint tmp = mem_get_word(cpu, addr);
WR_RD(tmp)
})
DEF(mret, FormatEmpty, { // system
@ -305,11 +338,13 @@ DEF(remu, FormatR, { // rv32m
WR_RD(result)
})
DEF(sb, FormatS, { // rv32i
mem_set_byte(cpu, cpu->xreg[ins.rs1] + ins.imm, cpu->xreg[ins.rs2]);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_WRITE);
if (ret->trap.en) return;
mem_set_byte(cpu, addr, cpu->xreg[ins.rs2]);
})
DEF(sc_w, FormatR, { // rv32a
// I'm pretty sure this is not it chief, but it does the trick for now
uint addr = cpu->xreg[ins.rs1];
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
if (cpu->reservation_en && cpu->reservation_addr == addr) {
mem_set_word(cpu, addr, cpu->xreg[ins.rs2]);
cpu->reservation_en = false;
@ -322,7 +357,9 @@ DEF(sfence_vma, FormatEmpty, { // system
// skip
})
DEF(sh, FormatS, { // rv32i
mem_set_half_word(cpu, cpu->xreg[ins.rs1] + ins.imm, cpu->xreg[ins.rs2]);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_WRITE);
if (ret->trap.en) return;
mem_set_half_word(cpu, addr, cpu->xreg[ins.rs2]);
})
DEF(sll, FormatR, { // rv32i
WR_RD(cpu->xreg[ins.rs1] << cpu->xreg[ins.rs2])
@ -394,7 +431,9 @@ DEF(sub, FormatR, { // rv32i
WR_RD(AS_SIGNED(cpu->xreg[ins.rs1]) - AS_SIGNED(cpu->xreg[ins.rs2]));
})
DEF(sw, FormatS, { // rv32i
mem_set_word(cpu, cpu->xreg[ins.rs1] + ins.imm, cpu->xreg[ins.rs2]);
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_WRITE);
if (ret->trap.en) return;
mem_set_word(cpu, addr, cpu->xreg[ins.rs2]);
})
DEF(uret, FormatEmpty, { // system
// unnecessary?
@ -536,6 +575,11 @@ ins_ret ins_select(cpu_t *cpu, uint ins_word) {
if (VERBOSE >= 1)
printf("Invalid instruction: %08x\n", ins_word);
printf("INVALID INS\n");
SINGLE_STEP = 1;
VERBOSE = 4;
ret.trap.en = true;
ret.trap.type = trap_IllegalInstruction;
ret.trap.value = ins_word;
@ -545,20 +589,22 @@ ins_ret ins_select(cpu_t *cpu, uint ins_word) {
void emulate(cpu_t *cpu) {
uint ins_word = 0;
ins_ret ret;
ins_ret ret = ins_ret_noop(cpu);
if ((cpu->pc & 0x3) == 0) {
ins_word = mem_get_word(cpu, cpu->pc);
ret = ins_select(cpu, ins_word);
uint ins_addr = mmu_translate(&ret, cpu->pc, MMU_ACCESS_FETCH);
if (!ret.trap.en) {
ins_word = mem_get_word(cpu, ins_addr);
ret = ins_select(cpu, ins_word);
if (ret.csr_write && !ret.trap.en) {
set_csr(cpu, ret.csr_write, ret.csr_val, &ret);
}
if (ret.csr_write && !ret.trap.en) {
set_csr(cpu, ret.csr_write, ret.csr_val, &ret);
}
if (!ret.trap.en && ret.write_reg < 32 && ret.write_reg > 0) {
cpu->xreg[ret.write_reg] = ret.write_val;
if (!ret.trap.en && ret.write_reg < 32 && ret.write_reg > 0) {
cpu->xreg[ret.write_reg] = ret.write_val;
}
}
} else {
ret = ins_ret_noop(cpu);
ret.trap.en = true;
ret.trap.type = trap_InstructionAddressMisaligned;
ret.trap.value = cpu->pc;
@ -572,8 +618,10 @@ void emulate(cpu_t *cpu) {
cpu->clint.mtime_lo++;
cpu->clint.mtime_hi += cpu->clint.mtime_lo == 0 ? 1 : 0;
if (cpu->clint.mtimecmp_lo != 0 && cpu->clint.mtimecmp_hi != 0 && (cpu->clint.mtime_hi > cpu->clint.mtimecmp_hi || (cpu->clint.mtime_hi == cpu->clint.mtimecmp_hi && cpu->clint.mtime_lo >= cpu->clint.mtimecmp_lo))) {
if ((cpu->clint.mtimecmp_lo != 0 || cpu->clint.mtimecmp_hi != 0) && (cpu->clint.mtime_hi > cpu->clint.mtimecmp_hi || (cpu->clint.mtime_hi == cpu->clint.mtimecmp_hi && cpu->clint.mtime_lo >= cpu->clint.mtimecmp_lo))) {
cpu->csr.data[CSR_MIP] |= MIP_MTIP;
if (VERBOSE >= 1)
printf("timer irq: %d >= %d\n", cpu->clint.mtime_lo, cpu->clint.mtimecmp_lo);
}
uart_tick(cpu);

View File

@ -7,13 +7,42 @@
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include "types.h"
#include "cpu.h"
#include "uart.h"
#include <termios.h>
#include "../elfy/elfy.h"
const int MEM_SIZE = 1024 * 1024 * 128;
struct termios t, t_orig;
void buf_off(void)
{
tcgetattr(STDIN_FILENO, &t);
t_orig = t;
t.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &t);
}
void buf_on(void)
{
tcsetattr(STDIN_FILENO, TCSANOW, &t_orig);
}
static uint tracebuf[64];
static uint tracebuf_idx = 0;
void print_tracebuf() {
if (cmd_buf == NULL) {
cmd_buf = malloc(4096);
}
for (uint i = tracebuf_idx + 1; i != tracebuf_idx; i = (i + 1) % (sizeof(tracebuf) / sizeof(uint))) {
printf("BACKTRACE: 0x %08x\n", tracebuf[i]);
fflush(stdout);
/* sprintf(cmd_buf, "addr2line -e linux/vmlinux %x", tracebuf[i]); */
/* system(cmd_buf); */
}
}
size_t get_filesize(const char* filename) {
struct stat st;
@ -29,17 +58,27 @@ uint8_t* get_mmap_ptr(const char* filename) {
}
void usage() {
printf("Usage: rvc (-e <ELF binary>|-b <raw binary>) [-d <device tree binary>] [-v (0|1|2|3)] [-s]\n");
printf("Usage: rvc (-e <ELF binary>|-b <raw binary>) [-d <device tree binary>] [-i <initramfs>] [-v (0|1|2|3|4)] [-s] [-t]\n");
exit(EXIT_FAILURE);
}
static cpu_t cpu;
void term(int signum)
{
printf("\n\nCaught signal!\n");
buf_on();
cpu_dump(&cpu);
printf("\n");
/* ins_ret ret; */
/* for (uint i = 0x10000; i < 0x11000; i += 4) { */
/* uint pa = mmu_translate(&ret, i, MMU_ACCESS_READ); */
/* uint val = mem_get_word(&cpu, pa); */
/* printf("%05x: %08x\n", i, val); */
/* } */
print_tracebuf();
/* printf("\n"); */
exit(EXIT_FAILURE);
}
@ -47,10 +86,13 @@ int main(int argc, char *argv[]) {
char *elf = NULL;
char *bin = NULL;
char *dtb = NULL;
char *initrd = NULL;
uint mtd_size = 0;
int c;
bool trace = false;
while ((c = getopt (argc, argv, "e:b:d:v:s")) != -1) {
while ((c = getopt (argc, argv, "e:b:d:v:i:st")) != -1) {
switch (c)
{
case 'e':
@ -68,6 +110,12 @@ int main(int argc, char *argv[]) {
case 's':
SINGLE_STEP = 1;
break;
case 't':
trace = true;
break;
case 'i':
initrd = optarg;
break;
case '?':
default:
usage();
@ -88,6 +136,14 @@ int main(int argc, char *argv[]) {
memcpy(mem, bin_ptr, get_filesize(bin));
}
uint8_t *mtd = NULL;
if (initrd) {
mtd = malloc(MEM_SIZE);
uint8_t *mtd_ptr = get_mmap_ptr(initrd);
mtd_size = get_filesize(initrd);
memcpy(mtd, mtd_ptr, mtd_size);
}
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_handler = term;
@ -96,7 +152,7 @@ int main(int argc, char *argv[]) {
fcntl(0, F_SETFL, O_NONBLOCK);
cpu = cpu_init(mem, dtb ? get_mmap_ptr(dtb) : NULL);
cpu = cpu_init(mem, dtb ? get_mmap_ptr(dtb) : NULL, mtd, mtd_size);
if (VERBOSE >= 1)
printf("CPU initialized!\n");
@ -104,16 +160,65 @@ int main(int argc, char *argv[]) {
if (VERBOSE >= 3)
cpu_dump(&cpu);
setvbuf(stdin, NULL, _IONBF, 0);
buf_off();
// LIMITER
const unsigned long restr = 200000*1000;
unsigned long cur = 0;
unsigned long t = time(NULL);
uint init_verbose = VERBOSE;
while (1) {
cur++;
unsigned long t2 = time(NULL);
if (t != t2) {
cur = 0;
t = t2;
}
if (cur >= restr) {
continue;
}
cpu_tick(&cpu);
if (VERBOSE >= 3)
if (trace)
printf("TRACE: PC=0x%08x\n", cpu.pc);
tracebuf[tracebuf_idx++] = cpu.pc;
if (tracebuf_idx >= (sizeof(tracebuf) / sizeof(uint))) {
tracebuf_idx = 0;
}
/* if (cpu.clock > 5000000 && cpu.clock % 100000 == 0) { */
/* uint arb[8]; */
/* for (int iarb = 0; iarb < 8; iarb++) { */
/* uint arb_t = cpu.xreg[iarb*4+0] ^ */
/* cpu.xreg[iarb*4+1] ^ */
/* cpu.xreg[iarb*4+2] ^ */
/* cpu.xreg[iarb*4+3]; */
/* arb[iarb] = arb_t; */
/* } */
/* uint arb_f = arb[0] ^ arb[1] ^ arb[2] ^ arb[3] ^ arb[4] ^ arb[5] ^ arb[6] ^ arb[7] ^ cpu.pc; */
/* printf("else if (cpu.clock == %d && arb != 0x%08x) ARB_FAIL\n", cpu.clock, arb_f); */
/* } */
if (cpu.pc == 0x80000118) {
/* if (cpu.clock == 6350505) { // divergence: 6350908 */
/* if (cpu.clock == 6400000) { */
printf("BREAKPOINT HIT\n");
SINGLE_STEP = 1;
VERBOSE = 4;
}
if (VERBOSE >= 4)
cpu_dump(&cpu);
/* char input = 0; */
/* if (!SINGLE_STEP && !uart_input_value && read(0, &input, 1)) { */
/* uart_input_value = input; */
/* } */
char input = 0;
if (!SINGLE_STEP && !uart_input_value && read(0, &input, 1)) {
uart_input_value = input;
}
if (SINGLE_STEP) {
fflush(stdout);
@ -122,7 +227,7 @@ int main(int argc, char *argv[]) {
gc: ch = getchar();
switch (ch) {
case '\n': break;
case 'c': SINGLE_STEP = false; break;
case 'c': SINGLE_STEP = false; VERBOSE = init_verbose; break;
default: goto gc;
}
}

View File

@ -6,10 +6,10 @@
#include "types.h"
#include "uart.h"
const int MEM_SIZE = 1024 * 1024 * 64 - 2048 * 128;
// little endian, zero extended
uint mem_get_byte(cpu_t *cpu, uint addr) {
if (VERBOSE >= 3)
printf("mem_get_byte(%08x)\n", addr);
uint do_mem_get_byte(cpu_t *cpu, uint addr) {
if (cpu->dtb != NULL && addr >= 0x1020 && addr <= 0x1fff) {
if (VERBOSE >= 2)
@ -17,6 +17,10 @@ uint mem_get_byte(cpu_t *cpu, uint addr) {
return cpu->dtb[addr - 0x1020];
}
/* if (cpu->mtd != NULL && addr >= 0x40000000 && addr < (0x40000000 + MEM_SIZE)) { */
/* return cpu->mtd[addr - 0x40000000]; */
/* } */
switch (addr) {
// CLINT
case 0x02000000: return cpu->clint.msip ? 1 : 0;
@ -59,20 +63,54 @@ uint mem_get_byte(cpu_t *cpu, uint addr) {
case 0x10000004: return UART_GET2(MCR);
case 0x10000005: return UART_GET2(LSR);
case 0x10000007: return UART_GET2(SCR);
/* case 0x80001000: return 0; */
/* case 0x80001001: return 0; */
/* case 0x80001002: return 0; */
/* case 0x80001003: return 0; */
/* case 0x80001004: return 0; */
/* case 0x80001005: return 0; */
/* case 0x80001006: return 0; */
/* case 0x80001007: return 0; */
/* case 0x80001040: return 0; */
/* case 0x80001041: return 0; */
/* case 0x80001042: return 0; */
/* case 0x80001043: return 0; */
/* case 0x80001044: return 0; */
/* case 0x80001045: return 0; */
/* case 0x80001046: return 0; */
/* case 0x80001047: return 0; */
}
if ((addr & 0x80000000) == 0) {
return 0;
}
return cpu->mem[addr & 0x7FFFFFFF];
addr = addr & 0x7FFFFFFF;
if (addr >= MEM_SIZE) {
if (VERBOSE >= 1) {
printf("WARN: out-of-bounds memory read @0x%08x\n", addr | 0x80000000);
}
return 0;
}
return cpu->mem[addr];
}
uint mem_get_byte(cpu_t *cpu, uint addr) {
uint ret = do_mem_get_byte(cpu, addr);
if (VERBOSE >= 3)
printf("mem_get_byte(%08x) = 0x%08x\n", addr, ret);
return ret;
}
uint mem_get_half_word(cpu_t *cpu, uint addr) {
/* assert((addr & ~(0x1)) == addr); */
return mem_get_byte(cpu, addr) | ((uint16_t)mem_get_byte(cpu, addr + 1) << 8);
}
uint mem_get_word(cpu_t *cpu, uint addr) {
/* assert((addr & ~(0x3)) == addr); */
return mem_get_byte(cpu, addr) |
((uint16_t)mem_get_byte(cpu, addr + 1) << 8) |
((uint16_t)mem_get_byte(cpu, addr + 2) << 16) |
@ -131,21 +169,49 @@ void mem_set_byte(cpu_t *cpu, uint addr, uint val) {
case 0x10000003: UART_SET2(LCR, val); return;
case 0x10000004: UART_SET2(MCR, val); return;
case 0x10000007: UART_SET2(SCR, val); return;
/* case 0x80001000: printf("%c", (unsigned char)val); return; */
/* case 0x80001001: return; */
/* case 0x80001002: return; */
/* case 0x80001003: return; */
/* case 0x80001004: return; */
/* case 0x80001005: return; */
/* case 0x80001006: return; */
/* case 0x80001007: return; */
/* case 0x80001040: return; */
/* case 0x80001041: return; */
/* case 0x80001042: return; */
/* case 0x80001043: return; */
/* case 0x80001044: return; */
/* case 0x80001045: return; */
/* case 0x80001046: return; */
/* case 0x80001047: return; */
}
if ((addr & 0x80000000) == 0) {
return;
}
cpu->mem[addr & 0x7FFFFFFF] = val;
const int MEM_SIZE = 1024 * 1024 * 128;
addr = addr & 0x7FFFFFFF;
if (addr >= MEM_SIZE) {
if (VERBOSE >= 1) {
printf("WARN: out-of-bounds memory write @0x%08x\n", addr | 0x80000000);
}
return;
}
cpu->mem[addr] = val;
}
void mem_set_half_word(cpu_t *cpu, uint addr, uint val) {
/* assert((addr & ~(0x1)) == addr); */
mem_set_byte(cpu, addr, val & 0xFF);
mem_set_byte(cpu, addr + 1, (val >> 8) & 0xFF);
}
void mem_set_word(cpu_t *cpu, uint addr, uint val) {
/* assert((addr & ~(0x3)) == addr); */
mem_set_byte(cpu, addr, val & 0xFF);
mem_set_byte(cpu, addr + 1, (val >> 8) & 0xFF);
mem_set_byte(cpu, addr + 2, (val >> 16) & 0xFF);

195
src/mmu.h Normal file
View File

@ -0,0 +1,195 @@
#ifndef MMU_H
#define MMU_H
#include <stdio.h>
#include <stdlib.h>
#include "types.h"
void mmu_update(uint satp) {
cpu.mmu.mode = satp >> 31;
cpu.mmu.ppn = satp & 0x7fffffff;
}
#include "mem.h"
#include "trap.h"
#define MMU_MODE_OFF 0
#define MMU_MODE_SV32 1
#define MMU_ACCESS_FETCH 0
#define MMU_ACCESS_READ 1
#define MMU_ACCESS_WRITE 2
#define PAGESIZE 4096
#define PTESIZE 4
#define ADDR_PART_OFFSET(x) ((x >> 0) & 0xfff)
#define ADDR_PART_PN0(x) ((x >> 12) & 0x3ff)
#define ADDR_PART_PN1(x) ((x >> 22) & 0xfff)
typedef struct {
bool v, r, w, x, u, g, a, d;
uint rsw;
uint ppn0;
uint ppn1;
} mmu_page;
uint get_trap_type(uint mode) {
return mode == MMU_ACCESS_FETCH ? trap_InstructionPageFault :
(mode == MMU_ACCESS_READ ? trap_LoadPageFault : trap_StorePageFault);
}
uint get_effective_privilege(uint *sum, uint *mxr) {
uint mstatus = read_csr_raw(&cpu, CSR_MSTATUS);
*sum = (mstatus >> 18) & 0x1;
*mxr = (mstatus >> 19) & 0x1;
if ((mstatus >> 17) & 0x1) {
// TODO: Check if this shouldn't be 9
return (mstatus >> 11) & 0x3;
}
return cpu.csr.privilege;
}
mmu_page load_page(uint addr) {
uint data = mem_get_word(&cpu, addr);
mmu_page ret;
#define BOOL(name, bit) ret.name = (data >> bit) & 0x1;
BOOL(v, 0)
BOOL(r, 1)
BOOL(w, 2)
BOOL(x, 3)
BOOL(u, 4)
BOOL(g, 5)
BOOL(a, 6)
BOOL(d, 7)
#undef BOOL
ret.rsw = (data >> 8) & 0x3;
ret.ppn0 = (data >> 10) & 0x3ff;
ret.ppn1 = (data >> 20) & 0xfff;
return ret;
}
static char *print_page_buf = NULL;
char *print_page(mmu_page page) {
if (print_page_buf == NULL) {
print_page_buf = (char*)malloc(4096);
}
memset(print_page_buf, 0, 4096);
sprintf(print_page_buf, "v=%d r=%d w=%d x=%d u=%d g=%d a=%d d=%d rsw=0x%02x ppn[0]=0x%03x ppn[1]=0x%03x", page.v, page.r, page.w, page.x, page.u, page.g, page.a, page.d, page.rsw, page.ppn0, page.ppn1);
return print_page_buf;
}
uint mmu_translate(ins_ret *ins, uint addr, uint mode) {
if (cpu.mmu.mode == MMU_MODE_OFF) {
return addr;
}
#define FAULT \
ins->trap.en = true; \
ins->trap.type = get_trap_type(mode); \
ins->trap.value = addr; \
return 0;
uint sum, mxr;
uint priv = get_effective_privilege(&sum, &mxr);
// machine mode fetch will always use physical addresses, otherwise 'mxr'
// defines if paging will be used
if (priv == PRIV_MACHINE || (cpu.csr.privilege == PRIV_MACHINE && mode == MMU_ACCESS_FETCH)) {
return addr;
}
mmu_page page;
bool super;
/* mmu_page page = load_page(cpu.mmu.ppn * PAGESIZE + ADDR_PART_PN1(addr) * PTESIZE); */
/* bool super = true; */
/* if (!page.v || (!page.r && page.w)) { */
/* if (VERBOSE >= 2) */
/* printf("page fault (invalid root): addr=0x%08x (%s)\n", addr, print_page(page)); */
/* FAULT */
/* } */
/* if (!page.r && !page.x) { */
/* // non-leaf PTE, we need to dig deeper */
/* page = load_page((page.ppn0 | (page.ppn1 << 10)) * PAGESIZE + ADDR_PART_PN0(addr) * PTESIZE); */
/* if (!page.v || (!page.r && page.w) || (!page.r && !page.x)) { */
/* if (VERBOSE >= 2) */
/* printf("page fault (invalid PTE): addr=0x%08x (%s)\n", addr, print_page(page)); */
/* FAULT */
/* } */
/* super = false; */
/* } */
for (uint im = 0; im < 2; im++) {
uint page_addr = im == 0 ?
(cpu.mmu.ppn * PAGESIZE + ADDR_PART_PN1(addr) * PTESIZE) :
((page.ppn0 | (page.ppn1 << 10)) * PAGESIZE + ADDR_PART_PN0(addr) * PTESIZE);
page = load_page(page_addr);
super = im == 0;
if (!page.v || (!page.r && page.w)) {
/* printf("page fault (invalid): addr=0x%08x (%s, %d)\n", addr, print_page(page), im); */
FAULT
}
if (page.r || page.x) {
break;
} else if (im == 1) {
// non-leaf page at bottom level
/* printf("page fault (non-leaf): addr=0x%08x (%s, %d)\n", addr, print_page(page), im); */
FAULT
}
}
// PTE has been found, permission check
bool perm =
priv == PRIV_MACHINE || // machine can read everything
(priv == PRIV_USER && page.u) || // this is a user page
(priv == PRIV_SUPERVISOR && (!page.u || sum)); // supervisor page or SUM
bool access =
(mode == MMU_ACCESS_FETCH && page.x) ||
(mode == MMU_ACCESS_READ && (page.r || (page.x && mxr))) ||
(mode == MMU_ACCESS_WRITE && page.w);
bool allowed = perm && access;
if (!allowed) {
if (VERBOSE >= 1)
printf("page fault (perm/access): addr=0x%08x perm=%d access=%d (%s)\n", addr, perm, access, print_page(page));
FAULT
}
if (super && page.ppn0 != 0) {
if (VERBOSE >= 1)
printf("page fault (misaligned super page): addr=0x%08x (%s)\n", addr, print_page(page));
FAULT
}
if (!page.a || (mode == MMU_ACCESS_WRITE && !page.d)) {
if (VERBOSE >= 2)
printf("page fault (a/d not set): addr=0x%08x (%s)\n", addr, print_page(page));
FAULT
}
// translation success
uint pa = ADDR_PART_OFFSET(addr);
pa |= super ? ADDR_PART_PN0(addr) << 12 : page.ppn0 << 12;
pa |= page.ppn1 << 22;
/* const int MEM_SIZE = 1024 * 1024 * 128; */
/* if (!(pa & 0x80000000) || (pa & 0x7fffffff) >= MEM_SIZE) { */
/* FAULT */
/* } */
if (VERBOSE >= 3)
printf("MMU translated (mode=%d): 0x%08x -> 0x%08x (super=%d) (%s)\n", mode, addr, pa, super, print_page(page));
return pa;
}
#endif

View File

@ -120,7 +120,7 @@ bool handle_trap(cpu_t *cpu, ins_ret *ret, bool is_interrupt) {
uint csr_tval_addr = new_privilege == PRIV_MACHINE ? CSR_MTVAL : (new_privilege == PRIV_SUPERVISOR ? CSR_STVAL : CSR_UTVAL);
uint csr_tvec_addr = new_privilege == PRIV_MACHINE ? CSR_MTVEC : (new_privilege == PRIV_SUPERVISOR ? CSR_STVEC : CSR_UTVEC);
write_csr_raw(cpu, csr_epc_addr, cpu->pc);
write_csr_raw(cpu, csr_epc_addr, is_interrupt ? ret->pc_val : cpu->pc);
write_csr_raw(cpu, csr_cause_addr, t.type);
write_csr_raw(cpu, csr_tval_addr, t.value);
ret->pc_val = read_csr_raw(cpu, csr_tvec_addr);
@ -133,16 +133,16 @@ bool handle_trap(cpu_t *cpu, ins_ret *ret, bool is_interrupt) {
// NOTE: No user mode interrupt/exception handling!
if (new_privilege == PRIV_MACHINE) {
uint mie = (mstatus >> 3) & 1;
uint new_status = (mstatus & !0x1888) | (mie << 7) | (current_privilege << 11);
uint new_status = (mstatus & ~0x1888) | (mie << 7) | (current_privilege << 11);
write_csr_raw(cpu, CSR_MSTATUS, new_status);
} else { // PRIV_SUPERVISOR
uint sie = (sstatus >> 3) & 1;
uint new_status = (sstatus & !0x122) | (sie << 5) | ((current_privilege & 1) << 8);
uint sie = (sstatus >> 1) & 1;
uint new_status = (sstatus & ~0x122) | (sie << 5) | ((current_privilege & 1) << 8);
write_csr_raw(cpu, CSR_SSTATUS, new_status);
}
if (VERBOSE >= 1)
printf("trap: type=%08x value=%08x (IRQ: %d) moved PC from @%08x to @%08x\n", t.type, t.value, is_interrupt, cpu->pc, ret->pc_val);
if (VERBOSE >= (is_interrupt ? 1 : 2))
printf("trap: type=%08x value=%08x (IRQ: %d) moved PC from @%08x (#%d) to @%08x (#%d)\n", t.type, t.value, is_interrupt, cpu->pc, current_privilege, ret->pc_val, new_privilege);
return true;
}
@ -172,6 +172,8 @@ void handle_irq_and_trap(cpu_t *cpu, ins_ret *ret) {
if (handled && irq) {
// reset MIP value since IRQ was handled
write_csr_raw(cpu, CSR_MIP, cur_mip & ~mip_reset);
if (VERBOSE >= 3)
printf("IRQ handled: old_mip=%03x new_mip=%03x\n", cur_mip, read_csr_raw(cpu, CSR_MIP));
}
}
}

View File

@ -31,15 +31,23 @@ typedef struct {
uint mtime_hi;
} clint_state;
typedef struct {
uint mode;
uint ppn;
} mmu_state;
typedef struct {
uint clock;
uint xreg[32];
uint pc;
uint8_t *mem;
uint8_t *dtb;
uint8_t *mtd;
uint mtd_size;
csr_state csr;
clint_state clint;
uart_state uart;
mmu_state mmu;
bool reservation_en;
uint reservation_addr;
@ -73,4 +81,6 @@ uint sign_extend(uint x, uint b) {
return (x ^ m) - m;
}
static cpu_t cpu;
#endif

View File

@ -40,10 +40,12 @@ void uart_update_iir(cpu_t *cpu) {
void uart_tick(cpu_t *cpu) {
bool rx_ip = false;
if ((cpu->clock % 0x38400) == 0 && UART_GET1(RBR) == 0) {
if ((cpu->clock % 0x1000) == 0 && UART_GET1(RBR) == 0) {
uint value = uart_input_value; // TODO: Add actual input logic
uart_input_value = 0;
if (value == '\n') value = '\r'; // This is necessary I think?
if (value != 0) {
/* fprintf(stderr, "I'%d'", value); */
UART_SET1(RBR, value);
UART_SET2(LSR, (UART_GET2(LSR) | LSR_DATA_AVAILABLE));
uart_update_iir(cpu);
@ -54,8 +56,8 @@ void uart_tick(cpu_t *cpu) {
}
uint thr = UART_GET1(THR);
if ((cpu->clock & 0x16) == 0 && thr != 0) {
printf("%c", (char)thr);
if ((cpu->clock % 0x16) == 0 && thr != 0) {
printf(" %d", (unsigned char)thr);
fflush(stdout);
UART_SET1(THR, 0);
UART_SET2(LSR, (UART_GET2(LSR) | LSR_THR_EMPTY));
@ -65,7 +67,7 @@ void uart_tick(cpu_t *cpu) {
}
}
if (cpu->uart.thre_ip || rx_ip) {
if (!cpu->uart.interrupting && (cpu->uart.thre_ip || rx_ip)) {
cpu->uart.interrupting = true;
cpu->uart.thre_ip = false;
} else {