diff --git a/Makefile b/Makefile index 71f4b9e2..068d87e7 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,8 @@ OBJS_EXT := ifeq ($(call has, SYSTEM), 1) OBJS_EXT += system.o +OBJS_EXT += plic.o +OBJS_EXT += uart.o endif # Integer Multiplication and Division instructions diff --git a/build/Image b/build/Image new file mode 100755 index 00000000..ca27c975 Binary files /dev/null and b/build/Image differ diff --git a/build/minimal.dtb b/build/minimal.dtb new file mode 100644 index 00000000..aed87df4 Binary files /dev/null and b/build/minimal.dtb differ diff --git a/build/rootfs.cpio b/build/rootfs.cpio new file mode 100644 index 00000000..4824f8f6 Binary files /dev/null and b/build/rootfs.cpio differ diff --git a/src/common.h b/src/common.h index da84ebdb..47e926c7 100644 --- a/src/common.h +++ b/src/common.h @@ -27,6 +27,21 @@ #define MASK(n) (~((~0U << (n)))) +/* + * Integer log base 2 + * + * The input will be ORed with 1 to prevent x = 0 since + * the result is undefined if x = 0 + * + */ +#if defined(__GNUC__) || defined(__clang__) +#define ilog2(x) 31 - __builtin_clz(x | 1) +#elif defined(_MSC_VER) +/* FIXME */ +#else /* unsupported compilers */ +#define ilog2(x) +#endif + /* Alignment macro */ #if defined(__GNUC__) || defined(__clang__) #define __ALIGNED(x) __attribute__((aligned(x))) diff --git a/src/decode.c b/src/decode.c index 248db479..0fbb3a8a 100644 --- a/src/decode.c +++ b/src/decode.c @@ -922,9 +922,7 @@ static inline bool op_misc_mem(rv_insn_t *ir, const uint32_t insn) * FENCE FM[3:0] pred[3:0] succ[3:0] rs1 000 rd 0001111 * FENCEI imm[11:0] rs1 001 rd 0001111 */ - const uint32_t funct3 = decode_funct3(insn); - switch (funct3) { case 0b000: ir->opcode = rv_insn_fence; diff --git a/src/decode.h b/src/decode.h index 865ba7cf..e2b2984c 100644 --- a/src/decode.h +++ b/src/decode.h @@ -90,7 +90,7 @@ enum op_field { ) \ /* RV32 Zicsr Standard Extension */ \ IIF(RV32_HAS(Zicsr))( \ - _(csrrw, 0, 4, 0, ENC(rs1, rd)) \ + _(csrrw, 1, 4, 0, ENC(rs1, rd)) \ _(csrrs, 0, 4, 0, ENC(rs1, rd)) \ _(csrrc, 0, 4, 0, ENC(rs1, rd)) \ _(csrrwi, 0, 4, 0, ENC(rs1, rd)) \ diff --git a/src/emulate.c b/src/emulate.c index cf4d5029..c1294cbe 100644 --- a/src/emulate.c +++ b/src/emulate.c @@ -10,6 +10,10 @@ #include #include +#if RV32_HAS(SYSTEM) +#include "plic.h" +#endif /* RV32_HAS(SYSTEM) */ + #ifdef __EMSCRIPTEN__ #include #endif @@ -41,6 +45,12 @@ extern struct target_ops gdbstub_ops; #define IF_rs2(i, r) (i->rs2 == rv_reg_##r) #define IF_imm(i, v) (i->imm == v) +#if RV32_HAS(SYSTEM) +static uint32_t reloc_enable_mmu_jalr_addr; +static bool reloc_enable_mmu = false; +bool need_retranslate = false; +#endif + static void rv_trap_default_handler(riscv_t *rv) { rv->csr_mepc += rv->compressed ? 2 : 4; @@ -81,6 +91,13 @@ static inline void update_time(riscv_t *rv) rv->csr_time[1] = t >> 32; } +#if RV32_HAS(SYSTEM) +static inline void get_time_now(struct timeval *tv) +{ + rv_gettimeofday(tv); +} +#endif + #if RV32_HAS(Zicsr) /* get a pointer to a CSR */ static uint32_t *csr_get_ptr(riscv_t *rv, uint32_t csr) @@ -177,6 +194,13 @@ static uint32_t csr_csrrw(riscv_t *rv, uint32_t csr, uint32_t val) *c = val; + /* + * guestOS's process might have same VA, + * so block_map cannot be reused + */ + if (c == &rv->csr_satp) + block_map_clear(rv); + return out; } @@ -349,6 +373,11 @@ static set_t pc_set; static bool has_loops = false; #endif +#if RV32_HAS(SYSTEM) +extern void emu_update_uart_interrupts(riscv_t *rv); +static uint32_t peripheral_update_ctr = 64; +#endif + /* Interpreter-based execution path */ #define RVOP(inst, code, asm) \ static bool do_##inst(riscv_t *rv, rv_insn_t *ir, uint64_t cycle, \ @@ -539,6 +568,8 @@ FORCE_INLINE bool insn_is_unconditional_branch(uint8_t opcode) static void block_translate(riscv_t *rv, block_t *block) { +retranslate: + memset(block, 0, sizeof(block_t)); block->pc_start = block->pc_end = rv->PC; rv_insn_t *prev_ir = NULL; @@ -551,7 +582,16 @@ static void block_translate(riscv_t *rv, block_t *block) prev_ir->next = ir; /* fetch the next instruction */ - const uint32_t insn = rv->io.mem_ifetch(rv, block->pc_end); + uint32_t insn = rv->io.mem_ifetch(rv, block->pc_end); + +#if RV32_HAS(SYSTEM) + if (!insn && need_retranslate) { + need_retranslate = false; + goto retranslate; + } +#endif + + assert(insn); /* decode the instruction */ if (!rv_decode(ir, insn)) { @@ -560,8 +600,7 @@ static void block_translate(riscv_t *rv, block_t *block) break; } ir->impl = dispatch_table[ir->opcode]; - ir->pc = block->pc_end; - /* compute the end of pc */ + ir->pc = block->pc_end; /* compute the end of pc */ block->pc_end += is_compressed(insn) ? 2 : 4; block->n_insn++; prev_ir = ir; @@ -878,6 +917,14 @@ static bool runtime_profiler(riscv_t *rv, block_t *block) } #endif +#if RV32_HAS(SYSTEM) +static bool rv_has_plic_trap(riscv_t *rv) +{ + return ((rv->csr_sstatus & SSTATUS_SIE || !rv->priv_mode) && + (rv->csr_sip & rv->csr_sie)); +} +#endif + void rv_step(void *arg) { assert(arg); @@ -891,6 +938,48 @@ void rv_step(void *arg) /* loop until hitting the cycle target */ while (rv->csr_cycle < cycles_target && !rv->halt) { +#if RV32_HAS(SYSTEM) + /* check for any interrupt after every block emulation */ + + /* now time */ + struct timeval tv; + + if (peripheral_update_ctr-- == 0) { + peripheral_update_ctr = 64; + + u8250_check_ready(PRIV(rv)->uart); + if (PRIV(rv)->uart->in_ready) + emu_update_uart_interrupts(rv); + } + + get_time_now(&tv); + uint64_t t = (uint64_t) (tv.tv_sec * 1e6) + (uint32_t) tv.tv_usec; + + if (t > attr->timer) { + rv->csr_sip |= RV_INT_STI; + } else { + rv->csr_sip &= ~RV_INT_STI; + } + + if (rv_has_plic_trap(rv)) { + uint32_t intr_applicable = rv->csr_sip & rv->csr_sie; + uint8_t intr_idx = ilog2(intr_applicable); + switch (intr_idx) { + case 1: + SET_CAUSE_AND_TVAL_THEN_TRAP(rv, SUPERVISOR_SW_INTR, 0); + break; + case 5: + SET_CAUSE_AND_TVAL_THEN_TRAP(rv, SUPERVISOR_TIMER_INTR, 0); + break; + case 9: + SET_CAUSE_AND_TVAL_THEN_TRAP(rv, SUPERVISOR_EXTERNAL_INTR, 0); + break; + default: + break; + } + } +#endif /* RV32_HAS(SYSTEM) */ + if (prev && prev->pc_start != last_pc) { /* update previous block */ #if !RV32_HAS(JIT) @@ -1018,6 +1107,8 @@ static void __trap_handler(riscv_t *rv) assert(insn); rv_decode(ir, insn); + reloc_enable_mmu_jalr_addr = rv->PC; + ir->impl = dispatch_table[ir->opcode]; rv->compressed = is_compressed(insn); ir->impl(rv, ir, rv->csr_cycle, rv->PC); @@ -1117,8 +1208,13 @@ void ecall_handler(riscv_t *rv) { assert(rv); #if RV32_HAS(SYSTEM) - syscall_handler(rv); - rv->PC += 4; + if (rv->priv_mode == RV_PRIV_U_MODE) { + SET_CAUSE_AND_TVAL_THEN_TRAP(rv, ECALL_U, 0); + } else if (rv->priv_mode == + RV_PRIV_S_MODE) { /* trap to SBI syscall handler */ + rv->PC += 4; + syscall_handler(rv); + } #else SET_CAUSE_AND_TVAL_THEN_TRAP(rv, ECALL_M, 0); syscall_handler(rv); diff --git a/src/io.c b/src/io.c index 2009e452..84be5751 100644 --- a/src/io.c +++ b/src/io.c @@ -15,6 +15,22 @@ #include "io.h" +u8250_state_t *u8250_new() +{ + u8250_state_t *uart = calloc(1, sizeof(u8250_state_t)); + assert(uart); + + return uart; +} + +plic_t *plic_new() +{ + plic_t *plic = calloc(1, sizeof(plic_t)); + assert(plic); + + return plic; +} + static uint8_t *data_memory_base; memory_t *memory_new(uint32_t size) diff --git a/src/io.h b/src/io.h index cfceee2f..5e74cd0e 100644 --- a/src/io.h +++ b/src/io.h @@ -5,9 +5,47 @@ #pragma once +#include #include #include +/* UART */ + +#define IRQ_UART 1 +#define IRQ_UART_BIT (1 << IRQ_UART) + +typedef struct { + uint8_t dll, dlh; /**< divisor (ignored) */ + uint8_t lcr; /**< UART config */ + uint8_t ier; /**< interrupt config */ + uint8_t current_int, pending_ints; /**< interrupt status */ + /* other output signals, loopback mode (ignored) */ + uint8_t mcr; + /* I/O handling */ + int in_fd, out_fd; + bool in_ready; +} u8250_state_t; +void u8250_update_interrupts(u8250_state_t *uart); +void u8250_check_ready(u8250_state_t *uart); + +uint32_t u8250_read(u8250_state_t *uart, uint32_t addr); + +void u8250_write(u8250_state_t *uart, uint32_t addr, uint32_t value); + +/* create a UART controller */ +u8250_state_t *u8250_new(); + +typedef struct { + uint32_t masked; + uint32_t ip; + uint32_t ie; + /* state of input interrupt lines (level-triggered), set by environment */ + uint32_t active; +} plic_t; + +/* create a PLIC core */ +plic_t *plic_new(); + typedef struct { uint8_t *mem_base; uint64_t mem_size; diff --git a/src/main.c b/src/main.c index d9b261e6..a587a416 100644 --- a/src/main.c +++ b/src/main.c @@ -209,7 +209,7 @@ int main(int argc, char **args) run_flag |= opt_prof_data << 2; vm_attr_t attr = { - .mem_size = MEM_SIZE, + .mem_size = 512 * 1024 * 1024, /* FIXME: variadic size */ .stack_size = STACK_SIZE, .args_offset_size = ARGS_OFFSET_SIZE, .argc = prog_argc, @@ -227,7 +227,9 @@ int main(int argc, char **args) }; #if RV32_HAS(SYSTEM) assert(attr.data.system); - attr.data.system->elf_program = opt_prog_name; + attr.data.system->kernel = "build/Image"; /* FIXME: hardcoded */ + attr.data.system->initrd = "build/rootfs.cpio"; /* FIXME: hardcoded */ + attr.data.system->dtb = "build/minimal.dtb"; /* FIXME: hardcoded */ #else assert(attr.data.user); attr.data.user->elf_program = opt_prog_name; diff --git a/src/plic.c b/src/plic.c new file mode 100644 index 00000000..77d45b09 --- /dev/null +++ b/src/plic.c @@ -0,0 +1,83 @@ +#include "plic.h" + +void plic_update_interrupts(riscv_t *rv) +{ + vm_attr_t *attr = PRIV(rv); + plic_t *plic = attr->plic; + + /* Update pending interrupts */ + plic->ip |= plic->active & ~plic->masked; + plic->masked |= plic->active; + /* Send interrupt to target */ + if (plic->ip & plic->ie) { + rv->csr_sip |= SIP_SEIP; + } else + rv->csr_sip &= ~SIP_SEIP; +} + +uint32_t plic_read(riscv_t *rv, const uint32_t addr) +{ + vm_attr_t *attr = PRIV(rv); + plic_t *plic = attr->plic; + + /* no priority support: source priority hardwired to 1 */ + if (1 <= addr && addr <= 31) + return 0; + + uint32_t plic_read_val = 0; + + switch (addr) { + case 0x400: + plic_read_val = plic->ip; + break; + case 0x800: + plic_read_val = plic->ie; + break; + case 0x80000: + /* no priority support: target priority threshold hardwired to 0 */ + plic_read_val = 0; + break; + case 0x80001: + /* claim */ + { + uint32_t intr_candidate = plic->ip & plic->ie; + if (intr_candidate) { + plic_read_val = ilog2(intr_candidate); + plic->ip &= ~(1U << (plic_read_val)); + } + break; + } + default: + return 0; + } + + return plic_read_val; +} + +void plic_write(riscv_t *rv, const uint32_t addr, uint32_t value) +{ + vm_attr_t *attr = PRIV(rv); + plic_t *plic = attr->plic; + + /* no priority support: source priority hardwired to 1 */ + if (1 <= addr && addr <= 31) + return; + + switch (addr) { + case 0x800: + plic->ie = (value & ~1); + break; + case 0x80000: + /* no priority support: target priority threshold hardwired to 0 */ + break; + case 0x80001: + /* completion */ + if (plic->ie & (1U << value)) + plic->masked &= ~(1U << value); + break; + default: + break; + } + + return; +} diff --git a/src/plic.h b/src/plic.h new file mode 100644 index 00000000..1be84e45 --- /dev/null +++ b/src/plic.h @@ -0,0 +1,7 @@ +#pragma once + +#include "riscv_private.h" + +void plic_update_interrupts(riscv_t *rv); +uint32_t plic_read(riscv_t *rv, const uint32_t addr); +void plic_write(riscv_t *rv, const uint32_t addr, uint32_t value); diff --git a/src/riscv.c b/src/riscv.c index c87dce55..d9c87164 100644 --- a/src/riscv.c +++ b/src/riscv.c @@ -209,6 +209,66 @@ static void *t2c_runloop(void *arg) } #endif +#include +#include +#include +static void map_file(char **ram_loc, const char *name) +{ + int fd = open(name, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "could not open %s\n", name); + exit(2); + } + + /* get file size */ + struct stat st; + fstat(fd, &st); + + /* remap to a memory region */ + *ram_loc = mmap(*ram_loc, st.st_size, PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE, fd, 0); + if (*ram_loc == MAP_FAILED) { + perror("mmap"); + printf("name: %s\n", name); + close(fd); + exit(2); + } + + // FIXME: used for unmap + // mapper[map_index].addr = *ram_loc; + // mapper[map_index].size = st.st_size; + // map_index++; + + /* The kernel selects a nearby page boundary and attempts to create + * the mapping. + */ + *ram_loc += st.st_size; + + close(fd); +} + +#include + +static void reset_keyboard_input() +{ + /* Re-enable echo, etc. on keyboard. */ + struct termios term; + tcgetattr(0, &term); + term.c_lflag |= ICANON | ECHO; + tcsetattr(0, TCSANOW, &term); +} +/* Asynchronous communication to capture all keyboard input for the VM. */ +static void capture_keyboard_input() +{ + /* Hook exit, because we want to re-enable keyboard. */ + atexit(reset_keyboard_input); + + struct termios term; + tcgetattr(0, &term); + term.c_lflag &= ~(ICANON | ECHO | ISIG); /* Disable echo as well */ + tcsetattr(0, TCSANOW, &term); +} + riscv_t *rv_create(riscv_user_t rv_attr) { assert(rv_attr); @@ -222,10 +282,14 @@ riscv_t *rv_create(riscv_user_t rv_attr) vm_attr_t *attr = PRIV(rv); attr->mem = memory_new(attr->mem_size); assert(attr->mem); + assert(!(((uintptr_t) attr->mem) & 0b11)); /* reset */ rv_reset(rv, 0U); + /* FIXME */ + // capture_keyboard_input(); + if (attr->data.user) { elf_t *elf = elf_new(); assert(elf && elf_open(elf, (attr->data.user)->elf_program)); @@ -266,32 +330,52 @@ riscv_t *rv_create(riscv_user_t rv_attr) } #if RV32_HAS(SYSTEM) else { - /* - * TODO: system emulator - * e.g., kernel image, dtb, rootfs - * - * The test suite is compiled into a single ELF file, so load it as - * an ELF executable, just like a userspace ELF. - * + /* *-----------------------------------------* + * | Memory layout | + * *----------------*----------------*-------* + * | kernel image | initrd image | dtb | + * *----------------*----------------*-------* */ - elf_t *elf = elf_new(); - assert(elf && elf_open(elf, (attr->data.system)->elf_program)); - - const struct Elf32_Sym *end; - if ((end = elf_get_symbol(elf, "_end"))) - attr->break_addr = end->st_value; - assert(elf_load(elf, attr->mem)); - - /* set the entry pc */ - const struct Elf32_Ehdr *hdr = get_elf_header(elf); - assert(rv_set_pc(rv, hdr->e_entry)); + // elf_t *elf = elf_new(); + char *ram_loc = (char *) attr->mem->mem_base; + map_file(&ram_loc, (attr->data.system)->kernel); - elf_delete(elf); + uint32_t dtb_addr = attr->mem->mem_size - (1 * 1024 * 1024); + ram_loc = ((char *) attr->mem->mem_base) + dtb_addr; + map_file(&ram_loc, (attr->data.system)->dtb); + /* Load optional initrd image at last 8 MiB before the dtb region to + * prevent kernel from overwritting it + */ + if ((attr->data.system)->initrd) { + uint32_t initrd_addr = dtb_addr - (8 * 1024 * 1024); + ram_loc = ((char *) attr->mem->mem_base) + initrd_addr; + map_file(&ram_loc, (attr->data.system)->initrd); + } /* this variable has external linkage to mmu_io defined in system.c */ extern riscv_io_t mmu_io; memcpy(&rv->io, &mmu_io, sizeof(riscv_io_t)); + + /* setup RISC-V hart */ + rv_set_reg(rv, rv_reg_a0, 0); + rv_set_reg(rv, rv_reg_a1, dtb_addr); + + /* setup timer */ + attr->timer = 0xFFFFFFFFFFFFFFF; + + /* setup PLIC */ + attr->plic = plic_new(); + assert(attr->plic); + + /* setup PLIC */ + attr->uart = u8250_new(); + assert(attr->uart); + attr->uart->in_fd = 0; + attr->uart->out_fd = 1; + + /* reset privilege mode */ + rv->priv_mode = RV_PRIV_S_MODE; } #endif /* SYSTEM */ @@ -343,7 +427,7 @@ static void rv_run_and_trace(riscv_t *rv) vm_attr_t *attr = PRIV(rv); assert(attr && attr->data.user && attr->data.user->elf_program); - attr->cycle_per_step = 1; + attr->cycle_per_step = 100000000; const char *prog_name = attr->data.user->elf_program; elf_t *elf = elf_new(); @@ -375,7 +459,8 @@ void rv_run(riscv_t *rv) vm_attr_t *attr = PRIV(rv); assert(attr && #if RV32_HAS(SYSTEM) - attr->data.system && attr->data.system->elf_program + attr->data.system && attr->data.system->kernel && + attr->data.system->initrd && attr->data.system->dtb #else attr->data.user && attr->data.user->elf_program #endif diff --git a/src/riscv.h b/src/riscv.h index 24e036a7..568efa70 100644 --- a/src/riscv.h +++ b/src/riscv.h @@ -169,6 +169,8 @@ enum SV32_PTE_PERM { RESRV_PAGE1 = 0b0101, RESRV_PAGE2 = 0b1101, }; +#define RV_INT_STI_SHIFT 5 +#define RV_INT_STI (1 << RV_INT_STI_SHIFT) /* * SBI functions must return a pair of values: @@ -273,16 +275,20 @@ enum SV32_PTE_PERM { /* clang-format off */ enum TRAP_CODE { #if !RV32_HAS(EXT_C) - INSN_MISALIGNED = 0, /* Instruction address misaligned */ + INSN_MISALIGNED = 0, /* Instruction address misaligned */ #endif /* !RV32_HAS(EXT_C) */ - ILLEGAL_INSN = 2, /* Illegal instruction */ - BREAKPOINT = 3, /* Breakpoint */ - LOAD_MISALIGNED = 4, /* Load address misaligned */ - STORE_MISALIGNED = 6, /* Store/AMO address misaligned */ + ILLEGAL_INSN = 2, /* Illegal instruction */ + BREAKPOINT = 3, /* Breakpoint */ + LOAD_MISALIGNED = 4, /* Load address misaligned */ + STORE_MISALIGNED = 6, /* Store/AMO address misaligned */ #if RV32_HAS(SYSTEM) - PAGEFAULT_INSN = 12, /* Instruction page fault */ - PAGEFAULT_LOAD = 13, /* Load page fault */ - PAGEFAULT_STORE = 15, /* Store page fault */ + PAGEFAULT_INSN = 12, /* Instruction page fault */ + PAGEFAULT_LOAD = 13, /* Load page fault */ + PAGEFAULT_STORE = 15, /* Store page fault */ + SUPERVISOR_SW_INTR = (1U << 31) | 1, /* Supervisor software interrupt */ + SUPERVISOR_TIMER_INTR = (1U << 31) | 5, /* Supervisor timer interrupt */ + SUPERVISOR_EXTERNAL_INTR = (1U << 31) | 9, /* Supervisor external interrupt */ + ECALL_U = 8, /* Environment call from U-mode */ #endif /* RV32_HAS(SYSTEM) */ #if !RV32_HAS(SYSTEM) ECALL_M = 11, /* Environment call from M-mode */ @@ -295,23 +301,25 @@ enum TRAP_CODE { * into a cause and tval identifier respectively. */ /* clang-format off */ -#define SET_CAUSE_AND_TVAL_THEN_TRAP(rv, cause, tval) \ - { \ - /* \ - * To align rv32emu behavior with Spike \ - * \ - * If not in system mode, the __trap_handler \ - * should be be invoked \ - */ \ - IIF(RV32_HAS(SYSTEM))(rv->is_trapped = true;, ); \ - if(RV_PRIV_IS_U_OR_S_MODE()){ \ - rv->csr_scause = cause; \ - rv->csr_stval = tval; \ - } else { \ - rv->csr_mcause = cause; \ - rv->csr_mtval = tval; \ - } \ - rv->io.on_trap(rv); \ +#define SET_CAUSE_AND_TVAL_THEN_TRAP(rv, cause, tval) \ + { \ + /* \ + * To align rv32emu behavior with Spike \ + * \ + * If not in system mode, the __trap_handler \ + * should be be invoked \ + * \ + * FIXME: ECALL_U cannot be trap directly to __trap_handler \ + */ \ + IIF(RV32_HAS(SYSTEM))(if (cause != ECALL_U) rv->is_trapped = true;, ); \ + if (RV_PRIV_IS_U_OR_S_MODE()) { \ + rv->csr_scause = cause; \ + rv->csr_stval = tval; \ + } else { \ + rv->csr_mcause = cause; \ + rv->csr_mtval = tval; \ + } \ + rv->io.on_trap(rv); \ } /* clang-format on */ @@ -517,7 +525,9 @@ typedef struct { /* FIXME: replace with kernel image, dtb, etc */ #if RV32_HAS(SYSTEM) typedef struct { - char *elf_program; + char *kernel; + char *initrd; + char *dtb; } vm_system_t; #endif /* SYSTEM */ @@ -531,6 +541,12 @@ typedef struct { } vm_data_t; typedef struct { + /* uart object */ + u8250_state_t *uart; + + /* plic object */ + plic_t *plic; + /* vm memory object */ memory_t *mem; diff --git a/src/rv32_template.c b/src/rv32_template.c index 0093b804..a4e38e2c 100644 --- a/src/rv32_template.c +++ b/src/rv32_template.c @@ -174,7 +174,11 @@ RVOP( goto end_op; #endif last_pc = PC; - MUST_TAIL return taken->impl(rv, taken, cycle, PC); +#if RV32_HAS(SYSTEM) + if (!rv->is_trapped) +#endif + MUST_TAIL + return taken->impl(rv, taken, cycle, PC); } goto end_op; }, @@ -197,26 +201,34 @@ RVOP( * table to link he indirect jump targets. */ #if !RV32_HAS(JIT) -#define LOOKUP_OR_UPDATE_BRANCH_HISTORY_TABLE() \ - /* lookup branch history table */ \ - IIF(RV32_HAS(SYSTEM)(if (!rv->is_trapped), )) \ - { \ - for (int i = 0; i < HISTORY_SIZE; i++) { \ - if (ir->branch_table->PC[i] == PC) { \ - MUST_TAIL return ir->branch_table->target[i]->impl( \ - rv, ir->branch_table->target[i], cycle, PC); \ - } \ - } \ - block_t *block = block_find(&rv->block_map, PC); \ - if (block) { \ - /* update branch history table */ \ - ir->branch_table->PC[ir->branch_table->idx] = PC; \ - ir->branch_table->target[ir->branch_table->idx] = block->ir_head; \ - ir->branch_table->idx = \ - (ir->branch_table->idx + 1) % HISTORY_SIZE; \ - MUST_TAIL return block->ir_head->impl(rv, block->ir_head, cycle, \ - PC); \ - } \ +#define LOOKUP_OR_UPDATE_BRANCH_HISTORY_TABLE() \ + /* \ + * lookup branch history table \ + * \ + * When handling trap, the branch history table should not be lookup since \ + * it causes return from the trap_handler. \ + * \ + * In addition, before relocate_enable_mmu, the block maybe retranslated, \ + * thus the branch history lookup table should not be updated too. \ + */ \ + IIF(RV32_HAS(SYSTEM)(if (!rv->is_trapped && !reloc_enable_mmu), )) \ + { \ + for (int i = 0; i < HISTORY_SIZE; i++) { \ + if (ir->branch_table->PC[i] == PC) { \ + MUST_TAIL return ir->branch_table->target[i]->impl( \ + rv, ir->branch_table->target[i], cycle, PC); \ + } \ + } \ + block_t *block = block_find(&rv->block_map, PC); \ + if (block) { \ + /* update branch history table */ \ + ir->branch_table->PC[ir->branch_table->idx] = PC; \ + ir->branch_table->target[ir->branch_table->idx] = block->ir_head; \ + ir->branch_table->idx = \ + (ir->branch_table->idx + 1) % HISTORY_SIZE; \ + MUST_TAIL return block->ir_head->impl(rv, block->ir_head, cycle, \ + PC); \ + } \ } #else #define LOOKUP_OR_UPDATE_BRANCH_HISTORY_TABLE() \ @@ -269,6 +281,29 @@ RVOP( RV_EXC_MISALIGN_HANDLER(pc, INSN, false, 0); #endif LOOKUP_OR_UPDATE_BRANCH_HISTORY_TABLE(); + +#if RV32_HAS(SYSTEM) + /* + * relocate_enable_mmu is the first function called to set up the MMU. + * Inside the function, at address 0x98, an invalid PTE is accessed, + * causing a fetch page fault and trapping into the trap_handler, and + * it will not return via sret. + * + * After the jalr instruction at physical address 0xc00000b4 + * (the final instruction of relocate_enable_mmu), the MMU becomes + * available. + * + * Based on this, we need to manually escape from the trap_handler after + * the jalr instruction is executed. + */ + if (!reloc_enable_mmu && reloc_enable_mmu_jalr_addr == 0xc00000b4) { + reloc_enable_mmu = true; + need_retranslate = true; + rv->is_trapped = false; + } + +#endif /* RV32_HAS(SYSTEM) */ + goto end_op; }, GEN({ @@ -312,7 +347,9 @@ RVOP( }, ); \ PC += 4; \ last_pc = PC; \ + IIF(RV32_HAS(SYSTEM)(if (!rv->is_trapped), )) \ MUST_TAIL return untaken->impl(rv, untaken, cycle, PC); \ + goto end_op; \ } \ is_branch_taken = true; \ PC += ir->imm; \ @@ -331,6 +368,7 @@ RVOP( goto end_op; \ }, ); \ last_pc = PC; \ + IIF(RV32_HAS(SYSTEM)(if (!rv->is_trapped), )) \ MUST_TAIL return taken->impl(rv, taken, cycle, PC); \ } \ goto end_op; @@ -512,8 +550,8 @@ RVOP( RVOP( lb, { - rv->X[ir->rd] = - sign_extend_b(rv->io.mem_read_b(rv, rv->X[ir->rs1] + ir->imm)); + uint32_t addr = rv->X[ir->rs1] + ir->imm; + rv->X[ir->rd] = sign_extend_b(rv->io.mem_read_b(rv, addr)); }, GEN({ mem; @@ -561,7 +599,10 @@ RVOP( /* LBU: Load Byte Unsigned */ RVOP( lbu, - { rv->X[ir->rd] = rv->io.mem_read_b(rv, rv->X[ir->rs1] + ir->imm); }, + { + uint32_t addr = rv->X[ir->rs1] + ir->imm; + rv->X[ir->rd] = rv->io.mem_read_b(rv, addr); + }, GEN({ mem; rald, VR0, rs1; @@ -597,7 +638,10 @@ RVOP( /* SB: Store Byte */ RVOP( sb, - { rv->io.mem_write_b(rv, rv->X[ir->rs1] + ir->imm, rv->X[ir->rs2]); }, + { + const uint32_t addr = rv->X[ir->rs1] + ir->imm; + rv->io.mem_write_b(rv, addr, rv->X[ir->rs2]); + }, GEN({ mem; rald, VR0, rs1; @@ -1023,6 +1067,7 @@ RVOP( rv->csr_sstatus |= SSTATUS_SPIE; rv->PC = rv->csr_sepc; + return true; }, GEN({ @@ -2004,7 +2049,12 @@ RVOP( goto end_op; #endif last_pc = PC; - MUST_TAIL return taken->impl(rv, taken, cycle, PC); + +#if RV32_HAS(SYSTEM) + if (!rv->is_trapped) +#endif + MUST_TAIL + return taken->impl(rv, taken, cycle, PC); } goto end_op; }, @@ -2165,7 +2215,11 @@ RVOP( goto end_op; #endif last_pc = PC; - MUST_TAIL return taken->impl(rv, taken, cycle, PC); +#if RV32_HAS(SYSTEM) + if (!rv->is_trapped) +#endif + MUST_TAIL + return taken->impl(rv, taken, cycle, PC); } goto end_op; }, @@ -2199,7 +2253,13 @@ RVOP( #endif PC += 2; last_pc = PC; - MUST_TAIL return untaken->impl(rv, untaken, cycle, PC); +#if RV32_HAS(SYSTEM) + if (!rv->is_trapped) +#endif + MUST_TAIL + return untaken->impl(rv, untaken, cycle, PC); + + goto end_op; } is_branch_taken = true; PC += ir->imm; @@ -2213,7 +2273,11 @@ RVOP( goto end_op; #endif last_pc = PC; - MUST_TAIL return taken->impl(rv, taken, cycle, PC); +#if RV32_HAS(SYSTEM) + if (!rv->is_trapped) +#endif + MUST_TAIL + return taken->impl(rv, taken, cycle, PC); } goto end_op; }, @@ -2256,7 +2320,13 @@ RVOP( #endif PC += 2; last_pc = PC; - MUST_TAIL return untaken->impl(rv, untaken, cycle, PC); +#if RV32_HAS(SYSTEM) + if (!rv->is_trapped) +#endif + MUST_TAIL + return untaken->impl(rv, untaken, cycle, PC); + + goto end_op; } is_branch_taken = true; PC += ir->imm; @@ -2270,7 +2340,11 @@ RVOP( goto end_op; #endif last_pc = PC; - MUST_TAIL return taken->impl(rv, taken, cycle, PC); +#if RV32_HAS(SYSTEM) + if (!rv->is_trapped) +#endif + MUST_TAIL + return taken->impl(rv, taken, cycle, PC); } goto end_op; }, diff --git a/src/system.c b/src/system.c index 24b6d264..b9427350 100644 --- a/src/system.c +++ b/src/system.c @@ -3,8 +3,80 @@ * "LICENSE" for information on usage and redistribution of this file. */ +#include "plic.h" #include "riscv_private.h" +void emu_update_uart_interrupts(riscv_t *rv) +{ + vm_attr_t *attr = PRIV(rv); + u8250_update_interrupts(attr->uart); + if (attr->uart->pending_ints) { + attr->plic->active |= IRQ_UART_BIT; + } else + attr->plic->active &= ~IRQ_UART_BIT; + plic_update_interrupts(rv); +} + +#define MMIO_PLIC 1 +#define MMIO_UART 0 +#define MMIO_R 1 +#define MMIO_W 0 + +uint8_t ret_char; +/* clang-format off */ +#define MMIO_OP(io, rw) \ + IIF(io)( /* PLIC */ \ + IIF(rw)( /* read */ \ + read_val = plic_read(rv, (addr & 0x3FFFFFF) >> 2); \ + plic_update_interrupts(rv); return read_val; \ + , /* write */ \ + plic_write(rv, (addr & 0x3FFFFFF) >> 2, val); \ + plic_update_interrupts(rv); return; \ + ) \ + , /* UART */ \ + IIF(rw)( /* read */ \ + /*return 0x60 | 0x1; */ \ + ret_char = u8250_read(PRIV(rv)->uart, addr & 0xFFFFF);\ + emu_update_uart_interrupts(rv);\ + return ret_char;\ + , /* write */ \ + u8250_write(PRIV(rv)->uart, addr & 0xFFFFF, val);\ + emu_update_uart_interrupts(rv);\ + return;\ + ) \ + ) +/* clang-format on */ + +#define MMIO_READ() \ + do { \ + uint32_t read_val; \ + if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */ \ + /* 256 regions of 1MiB */ \ + switch ((addr >> 20) & MASK(8)) { \ + case 0x0: \ + case 0x2: /* PLIC (0 - 0x3F) */ \ + MMIO_OP(MMIO_PLIC, MMIO_R); \ + case 0x40: /* UART */ \ + MMIO_OP(MMIO_UART, MMIO_R); \ + } \ + } \ + } while (0) + +#define MMIO_WRITE() \ + do { \ + if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */ \ + /* 256 regions of 1MiB */ \ + switch ((addr >> 20) & MASK(8)) { \ + case 0x0: \ + case 0x2: /* PLIC (0 - 0x3F) */ \ + MMIO_OP(MMIO_PLIC, MMIO_W); \ + case 0x40: /* UART */ \ + MMIO_OP(MMIO_UART, MMIO_W); \ + } \ + } \ + } while (0) + + static bool ppn_is_valid(riscv_t *rv, uint32_t ppn) { vm_attr_t *attr = PRIV(rv); @@ -170,6 +242,7 @@ MMU_FAULT_CHECK_IMPL(write, pagefault_store) * - mmu_write_s * - mmu_write_b */ +extern bool need_retranslate; static uint32_t mmu_ifetch(riscv_t *rv, const uint32_t addr) { if (!rv->csr_satp) @@ -182,6 +255,10 @@ static uint32_t mmu_ifetch(riscv_t *rv, const uint32_t addr) pte = mmu_walk(rv, addr, &level); } + if (need_retranslate) { + return 0; + } + get_ppn_and_offset(); return memory_ifetch(ppn | offset); } @@ -198,8 +275,15 @@ static uint32_t mmu_read_w(riscv_t *rv, const uint32_t addr) pte = mmu_walk(rv, addr, &level); } - get_ppn_and_offset(); - return memory_read_w(ppn | offset); + { + get_ppn_and_offset(); + const uint32_t addr = ppn | offset; + const vm_attr_t *attr = PRIV(rv); + if (addr < attr->mem->mem_size) + return memory_read_w(addr); + + MMIO_READ(); + } } static uint16_t mmu_read_s(riscv_t *rv, const uint32_t addr) @@ -230,8 +314,15 @@ static uint8_t mmu_read_b(riscv_t *rv, const uint32_t addr) pte = mmu_walk(rv, addr, &level); } - get_ppn_and_offset(); - return memory_read_b(ppn | offset); + { + get_ppn_and_offset(); + const uint32_t addr = ppn | offset; + const vm_attr_t *attr = PRIV(rv); + if (addr < attr->mem->mem_size) + return memory_read_b(addr); + + MMIO_READ(); + } } static void mmu_write_w(riscv_t *rv, const uint32_t addr, const uint32_t val) @@ -246,8 +337,17 @@ static void mmu_write_w(riscv_t *rv, const uint32_t addr, const uint32_t val) pte = mmu_walk(rv, addr, &level); } - get_ppn_and_offset(); - memory_write_w(ppn | offset, (uint8_t *) &val); + { + get_ppn_and_offset(); + const uint32_t addr = ppn | offset; + const vm_attr_t *attr = PRIV(rv); + if (addr < attr->mem->mem_size) { + memory_write_w(addr, (uint8_t *) &val); + return; + } + + MMIO_WRITE(); + } } static void mmu_write_s(riscv_t *rv, const uint32_t addr, const uint16_t val) @@ -278,8 +378,17 @@ static void mmu_write_b(riscv_t *rv, const uint32_t addr, const uint8_t val) pte = mmu_walk(rv, addr, &level); } - get_ppn_and_offset(); - memory_write_b(ppn | offset, (uint8_t *) &val); + { + get_ppn_and_offset(); + const uint32_t addr = ppn | offset; + const vm_attr_t *attr = PRIV(rv); + if (addr < attr->mem->mem_size) { + memory_write_b(addr, (uint8_t *) &val); + return; + } + + MMIO_WRITE(); + } } riscv_io_t mmu_io = { diff --git a/src/t2c_template.c b/src/t2c_template.c index 2ee33720..fb1f98b5 100644 --- a/src/t2c_template.c +++ b/src/t2c_template.c @@ -373,6 +373,7 @@ T2C_OP(sfencevma, { __UNREACHABLE; }) #if RV32_HAS(Zifencei) T2C_OP(fencei, { __UNREACHABLE; }) +T2C_OP(fence, { __UNREACHABLE; }) #endif #if RV32_HAS(Zicsr) diff --git a/src/uart.c b/src/uart.c new file mode 100644 index 00000000..6afd7762 --- /dev/null +++ b/src/uart.c @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include +#include + +#include "io.h" + +/* Emulate 8250 (plain, without loopback mode support) */ + +#define U8250_INT_THRE 1 + +void u8250_update_interrupts(u8250_state_t *uart) +{ + /* Some interrupts are level-generated. */ + /* TODO: does it also generate an LSR change interrupt? */ + if (uart->in_ready) + uart->pending_ints |= 1; + else + uart->pending_ints &= ~1; + + /* Prevent generating any disabled interrupts in the first place */ + uart->pending_ints &= uart->ier; + + /* Update current interrupt (higher bits -> more priority) */ + if (uart->pending_ints) + uart->current_int = ilog2(uart->pending_ints); +} + +void u8250_check_ready(u8250_state_t *uart) +{ + if (uart->in_ready) + return; + + struct pollfd pfd = {uart->in_fd, POLLIN, 0}; + poll(&pfd, 1, 0); + if (pfd.revents & POLLIN) + uart->in_ready = true; +} + +static void u8250_handle_out(u8250_state_t *uart, uint8_t value) +{ + if (write(uart->out_fd, &value, 1) < 1) + fprintf(stderr, "failed to write UART output: %s\n", strerror(errno)); +} + +static uint8_t u8250_handle_in(u8250_state_t *uart) +{ + uint8_t value = 0; + u8250_check_ready(uart); + if (!uart->in_ready) + return value; + + if (read(uart->in_fd, &value, 1) < 0) + fprintf(stderr, "failed to read UART input: %s\n", strerror(errno)); + // printf("success\n"); + uart->in_ready = false; + u8250_check_ready(uart); + + // if (value == 1) { /* start of heading (Ctrl-a) */ + // if (getchar() == 120) { /* keyboard x */ + // printf("\n"); /* end emulator with newline */ + // exit(0); + // } + // } + + return value; +} + +static uint8_t u8250_reg_read(u8250_state_t *uart, uint32_t addr) +{ + uint8_t ret; + switch (addr) { + case 0: + if (uart->lcr & (1 << 7)) { /* DLAB */ + return uart->dll; + } + return u8250_handle_in(uart); + case 1: + if (uart->lcr & (1 << 7)) { /* DLAB */ + return uart->dlh; + } + return uart->ier; + case 2: + ret = (uart->current_int << 1) | (uart->pending_ints ? 0 : 1); + if (uart->current_int == U8250_INT_THRE) + uart->pending_ints &= ~(1 << uart->current_int); + return ret; + case 3: + return uart->lcr; + case 4: + return uart->mcr; + break; + case 5: + /* LSR = no error, TX done & ready */ + return (0x60 | (uint8_t) uart->in_ready); + case 6: + /* MSR = carrier detect, no ring, data ready, clear to send. */ + return 0xb0; + /* no scratch register, so we should be detected as a plain 8250. */ + default: + return 0; + } + + return 0; +} + +static void u8250_reg_write(u8250_state_t *uart, uint32_t addr, uint8_t value) +{ + switch (addr) { + case 0: + if (uart->lcr & (1 << 7)) { /* DLAB */ + uart->dll = value; + break; + } + u8250_handle_out(uart, value); + uart->pending_ints |= 1 << U8250_INT_THRE; + break; + case 1: + if (uart->lcr & (1 << 7)) { /* DLAB */ + uart->dlh = value; + break; + } + uart->ier = value; + break; + case 3: + uart->lcr = value; + break; + case 4: + uart->mcr = value; + break; + } +} + +uint32_t u8250_read(u8250_state_t *uart, uint32_t addr) +{ + return (uint32_t) (int8_t) u8250_reg_read(uart, addr); +} + +void u8250_write(u8250_state_t *uart, uint32_t addr, uint32_t value) +{ + u8250_reg_write(uart, addr, value); +}