Skip to content

Commit

Permalink
Preliminary support for MMU emulation
Browse files Browse the repository at this point in the history
To boot a 32-bit RISC-V Linux with MMU, we must first support MMU
emulation. The virtual memory scheme to be supported is SV32. The major
changes in this commit include implementing the MMU-related riscv_io_t
interface and binding it during RISC-V instance initialization. To
reuse the riscv_io_t interface, we modified its prototype.

For each memory access, the page table is walked to get the
corresponding PTE. Depending on the PTE retrieval, several page faults
may need handling. Thus, three exception handlers have been introduced:
insn_pgfault, load_pgfault, and store_pgfault, used in MMU_CHECK_FAULT.
This commit does not fully handle access faults since they are related
to PMA and PMP, which may not be necessary for booting 32-bit RISC-V
Linux.

Since Linux has not been booted yet, a test suite is needed to test the
MMU emulation. This commit includes a test suite that implements a
simple kernel space supervisor and a user space application. The
supervisor prepares the page table and then passes control to the user
space application to test the aforementioned page faults.

Related: sysprog21#310
  • Loading branch information
ChinYikMing committed Oct 23, 2024
1 parent edb5a1b commit 634dbd0
Show file tree
Hide file tree
Showing 14 changed files with 1,026 additions and 109 deletions.
5 changes: 2 additions & 3 deletions src/decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -907,9 +907,8 @@ static inline bool op_system(rv_insn_t *ir, const uint32_t insn)
default: /* illegal instruction */
return false;
}
if (!csr_is_writable(ir->imm) && ir->rs1 != rv_reg_zero)
return false;
return true;

return csr_is_writable(ir->imm) || (ir->rs1 == rv_reg_zero);
}

/* MISC-MEM: I-type
Expand Down
55 changes: 20 additions & 35 deletions src/emulate.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,21 @@ extern struct target_ops gdbstub_ops;
#define IF_rs2(i, r) (i->rs2 == rv_reg_##r)
#define IF_imm(i, v) (i->imm == v)

/* RISC-V exception code list */
/* RISC-V trap code list */
/* clang-format off */
#define RV_TRAP_LIST \
IIF(RV32_HAS(EXT_C))(, \
_(insn_misaligned, 0) /* Instruction address misaligned */ \
) \
_(illegal_insn, 2) /* Illegal instruction */ \
_(breakpoint, 3) /* Breakpoint */ \
_(load_misaligned, 4) /* Load address misaligned */ \
_(store_misaligned, 6) /* Store/AMO address misaligned */ \
IIF(RV32_HAS(SYSTEM))(, \
_(ecall_M, 11) /* Environment call from M-mode */ \
#define RV_TRAP_LIST \
IIF(RV32_HAS(EXT_C))(, \
_(insn_misaligned, 0) /* Instruction address misaligned */ \
) \
_(illegal_insn, 2) /* Illegal instruction */ \
_(breakpoint, 3) /* Breakpoint */ \
_(load_misaligned, 4) /* Load address misaligned */ \
_(store_misaligned, 6) /* Store/AMO address misaligned */ \
IIF(RV32_HAS(SYSTEM))( \
_(pagefault_insn, 12) /* Instruction page fault */ \
_(pagefault_load, 13) /* Load page fault */ \
_(pagefault_store, 15), /* Store page fault */ \
_(ecall_M, 11) /* Environment call from M-mode */ \
)
/* clang-format on */

Expand All @@ -75,9 +78,7 @@ static void rv_trap_default_handler(riscv_t *rv)
* the registered trap handler, PC by PC. Once the trap is handled,
* resume the previous execution flow where cause the trap.
*
* Since the system emulation has not yet included in rv32emu, the page
* fault is not practical in current test suite. Instead, we try to
* emulate the misaligned handling in the test suite.
* Now, rv32emu supports misaligned access and page fault handling.
*/
#if RV32_HAS(SYSTEM)
static void trap_handler(riscv_t *rv);
Expand Down Expand Up @@ -532,7 +533,7 @@ static bool do_fuse3(riscv_t *rv, rv_insn_t *ir, uint64_t cycle, uint32_t PC)
for (int i = 0; i < ir->imm2; i++) {
uint32_t addr = rv->X[fuse[i].rs1] + fuse[i].imm;
RV_EXC_MISALIGN_HANDLER(3, store, false, 1);
rv->io.mem_write_w(addr, rv->X[fuse[i].rs2]);
rv->io.mem_write_w(rv, addr, rv->X[fuse[i].rs2]);
}
PC += ir->imm2 * 4;
if (unlikely(RVOP_NO_NEXT(ir))) {
Expand All @@ -556,7 +557,7 @@ static bool do_fuse4(riscv_t *rv, rv_insn_t *ir, uint64_t cycle, uint32_t PC)
for (int i = 0; i < ir->imm2; i++) {
uint32_t addr = rv->X[fuse[i].rs1] + fuse[i].imm;
RV_EXC_MISALIGN_HANDLER(3, load, false, 1);
rv->X[fuse[i].rd] = rv->io.mem_read_w(addr);
rv->X[fuse[i].rd] = rv->io.mem_read_w(rv, addr);
}
PC += ir->imm2 * 4;
if (unlikely(RVOP_NO_NEXT(ir))) {
Expand Down Expand Up @@ -666,7 +667,7 @@ 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(block->pc_end);
const uint32_t insn = rv->io.mem_ifetch(rv, block->pc_end);

/* decode the instruction */
if (!rv_decode(ir, insn)) {
Expand Down Expand Up @@ -1122,24 +1123,8 @@ void rv_step(void *arg)
}

#if RV32_HAS(SYSTEM)
static void trap_handler(riscv_t *rv)
{
rv_insn_t *ir = mpool_alloc(rv->block_ir_mp);
assert(ir);

/* set to false by sret/mret implementation */
uint32_t insn;
while (rv->is_trapped && !rv_has_halted(rv)) {
insn = rv->io.mem_ifetch(rv->PC);
assert(insn);

rv_decode(ir, insn);
ir->impl = dispatch_table[ir->opcode];
rv->compressed = is_compressed(insn);
ir->impl(rv, ir, rv->csr_cycle, rv->PC);
}
}
#endif
#include "system.c"
#endif /* SYSTEM */

void ebreak_handler(riscv_t *rv)
{
Expand Down
5 changes: 5 additions & 0 deletions src/feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,10 @@
#define RV32_FEATURE_T2C 0
#endif

/* System */
#ifndef RV32_FEATURE_SYSTEM
#define RV32_FEATURE_SYSTEM 0
#endif

/* Feature test macro */
#define RV32_HAS(x) RV32_FEATURE_##x
4 changes: 2 additions & 2 deletions src/gdbstub.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ static int rv_read_mem(void *args, size_t addr, size_t len, void *val)
* an invalid address. We may have to do error handling in the
* mem_read_* function directly.
*/
*((uint8_t *) val + i) = rv->io.mem_read_b(addr + i);
*((uint8_t *) val + i) = rv->io.mem_read_b(rv, addr + i);
}

return err;
Expand All @@ -66,7 +66,7 @@ static int rv_write_mem(void *args, size_t addr, size_t len, void *val)
riscv_t *rv = (riscv_t *) args;

for (size_t i = 0; i < len; i++)
rv->io.mem_write_b(addr + i, *((uint8_t *) val + i));
rv->io.mem_write_b(rv, addr + i, *((uint8_t *) val + i));

return 0;
}
Expand Down
9 changes: 9 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,21 @@ int main(int argc, char **args)
.log_level = 0,
.run_flag = run_flag,
.profile_output_file = prof_out_file,
#if RV32_HAS(SYSTEM)
.data.system = malloc(sizeof(vm_system_t)),
#else
.data.user = malloc(sizeof(vm_user_t)),
#endif
.cycle_per_step = CYCLE_PER_STEP,
.allow_misalign = opt_misaligned,
};
#if RV32_HAS(SYSTEM)
assert(attr.data.system);
attr.data.system->elf_program = opt_prog_name;
#else
assert(attr.data.user);
attr.data.user->elf_program = opt_prog_name;
#endif

/* create the RISC-V runtime */
rv = rv_create(&attr);
Expand Down
72 changes: 53 additions & 19 deletions src/riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,10 @@ void rv_remap_stdstream(riscv_t *rv, fd_stream_pair_t *fsp, uint32_t fsp_size)
#define MEMIO(op) on_mem_##op
#define IO_HANDLER_IMPL(type, op, RW) \
static IIF(RW)( \
/* W */ void MEMIO(op)(riscv_word_t addr, riscv_##type##_t data), \
/* R */ riscv_##type##_t MEMIO(op)(riscv_word_t addr)) \
/* W */ void MEMIO(op)(UNUSED riscv_t * rv, riscv_word_t addr, \
riscv_##type##_t data), \
/* R */ riscv_##type##_t MEMIO(op)(UNUSED riscv_t * rv, \
riscv_word_t addr)) \
{ \
IIF(RW) \
(memory_##op(addr, (uint8_t *) &data), return memory_##op(addr)); \
Expand Down Expand Up @@ -260,9 +262,37 @@ riscv_t *rv_create(riscv_user_t rv_attr)
.on_memset = memset_handler,
};
memcpy(&rv->io, &io, sizeof(riscv_io_t));
} else {
/* TODO: system emulator */
}
#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.
*
*/
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_delete(elf);

/* 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));
}
#endif /* SYSTEM */

/* default standard stream.
* rv_remap_stdstream can be called to overwrite them
Expand Down Expand Up @@ -303,20 +333,6 @@ riscv_t *rv_create(riscv_user_t rv_attr)
#endif
#endif

#if RV32_HAS(SYSTEM)
/*
* System simulation defaults to S-mode as
* it does not rely on M-mode software like OpenSBI.
*/
rv->priv_mode = RV_PRIV_S_MODE;

/* not being trapped */
rv->is_trapped = false;
#else
/* ISA simulation defaults to M-mode */
rv->priv_mode = RV_PRIV_M_MODE;
#endif

return rv;
}

Expand Down Expand Up @@ -356,7 +372,11 @@ void rv_run(riscv_t *rv)
assert(rv);

vm_attr_t *attr = PRIV(rv);
#if RV32_HAS(SYSTEM)
assert(attr && attr->data.system && attr->data.system->elf_program);
#else
assert(attr && attr->data.user && attr->data.user->elf_program);
#endif

if (attr->run_flag & RV_RUN_TRACE)
rv_run_and_trace(rv);
Expand Down Expand Up @@ -516,6 +536,21 @@ void rv_reset(riscv_t *rv, riscv_word_t pc)
/* reset sp pointing to argc */
rv->X[rv_reg_sp] = stack_top;

/* reset privilege mode */
#if RV32_HAS(SYSTEM)
/*
* System simulation defaults to S-mode as
* it does not rely on M-mode software like OpenSBI.
*/
rv->priv_mode = RV_PRIV_S_MODE;

/* not being trapped */
rv->is_trapped = false;
#else
/* ISA simulation defaults to M-mode */
rv->priv_mode = RV_PRIV_M_MODE;
#endif

/* reset the csrs */
rv->csr_mtvec = 0;
rv->csr_cycle = 0;
Expand All @@ -541,7 +576,6 @@ void rv_reset(riscv_t *rv, riscv_word_t pc)
rv->csr_misa |= MISA_M;
#endif


rv->halt = false;
}

Expand Down
Loading

0 comments on commit 634dbd0

Please sign in to comment.