Skip to content

Commit 172e59d

Browse files
committed
Preliminary support for MMU emulation
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: #310
1 parent edb5a1b commit 172e59d

File tree

14 files changed

+1024
-110
lines changed

14 files changed

+1024
-110
lines changed

src/decode.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -907,9 +907,8 @@ static inline bool op_system(rv_insn_t *ir, const uint32_t insn)
907907
default: /* illegal instruction */
908908
return false;
909909
}
910-
if (!csr_is_writable(ir->imm) && ir->rs1 != rv_reg_zero)
911-
return false;
912-
return true;
910+
911+
return csr_is_writable(ir->imm) || (ir->rs1 == rv_reg_zero);
913912
}
914913

915914
/* MISC-MEM: I-type

src/emulate.c

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,21 @@ extern struct target_ops gdbstub_ops;
4141
#define IF_rs2(i, r) (i->rs2 == rv_reg_##r)
4242
#define IF_imm(i, v) (i->imm == v)
4343

44-
/* RISC-V exception code list */
44+
/* RISC-V trap code list */
4545
/* clang-format off */
46-
#define RV_TRAP_LIST \
47-
IIF(RV32_HAS(EXT_C))(, \
48-
_(insn_misaligned, 0) /* Instruction address misaligned */ \
49-
) \
50-
_(illegal_insn, 2) /* Illegal instruction */ \
51-
_(breakpoint, 3) /* Breakpoint */ \
52-
_(load_misaligned, 4) /* Load address misaligned */ \
53-
_(store_misaligned, 6) /* Store/AMO address misaligned */ \
54-
IIF(RV32_HAS(SYSTEM))(, \
55-
_(ecall_M, 11) /* Environment call from M-mode */ \
46+
#define RV_TRAP_LIST \
47+
IIF(RV32_HAS(EXT_C))(, \
48+
_(insn_misaligned, 0) /* Instruction address misaligned */ \
49+
) \
50+
_(illegal_insn, 2) /* Illegal instruction */ \
51+
_(breakpoint, 3) /* Breakpoint */ \
52+
_(load_misaligned, 4) /* Load address misaligned */ \
53+
_(store_misaligned, 6) /* Store/AMO address misaligned */ \
54+
IIF(RV32_HAS(SYSTEM))( \
55+
_(pagefault_insn, 12) /* Instruction page fault */ \
56+
_(pagefault_load, 13) /* Load page fault */ \
57+
_(pagefault_store, 15), /* Store page fault */ \
58+
_(ecall_M, 11) /* Environment call from M-mode */ \
5659
)
5760
/* clang-format on */
5861

@@ -75,9 +78,7 @@ static void rv_trap_default_handler(riscv_t *rv)
7578
* the registered trap handler, PC by PC. Once the trap is handled,
7679
* resume the previous execution flow where cause the trap.
7780
*
78-
* Since the system emulation has not yet included in rv32emu, the page
79-
* fault is not practical in current test suite. Instead, we try to
80-
* emulate the misaligned handling in the test suite.
81+
* Now, rv32emu supports misaligned access and page fault handling.
8182
*/
8283
#if RV32_HAS(SYSTEM)
8384
static void trap_handler(riscv_t *rv);
@@ -532,7 +533,7 @@ static bool do_fuse3(riscv_t *rv, rv_insn_t *ir, uint64_t cycle, uint32_t PC)
532533
for (int i = 0; i < ir->imm2; i++) {
533534
uint32_t addr = rv->X[fuse[i].rs1] + fuse[i].imm;
534535
RV_EXC_MISALIGN_HANDLER(3, store, false, 1);
535-
rv->io.mem_write_w(addr, rv->X[fuse[i].rs2]);
536+
rv->io.mem_write_w(rv, addr, rv->X[fuse[i].rs2]);
536537
}
537538
PC += ir->imm2 * 4;
538539
if (unlikely(RVOP_NO_NEXT(ir))) {
@@ -556,7 +557,7 @@ static bool do_fuse4(riscv_t *rv, rv_insn_t *ir, uint64_t cycle, uint32_t PC)
556557
for (int i = 0; i < ir->imm2; i++) {
557558
uint32_t addr = rv->X[fuse[i].rs1] + fuse[i].imm;
558559
RV_EXC_MISALIGN_HANDLER(3, load, false, 1);
559-
rv->X[fuse[i].rd] = rv->io.mem_read_w(addr);
560+
rv->X[fuse[i].rd] = rv->io.mem_read_w(rv, addr);
560561
}
561562
PC += ir->imm2 * 4;
562563
if (unlikely(RVOP_NO_NEXT(ir))) {
@@ -666,7 +667,7 @@ static void block_translate(riscv_t *rv, block_t *block)
666667
prev_ir->next = ir;
667668

668669
/* fetch the next instruction */
669-
const uint32_t insn = rv->io.mem_ifetch(block->pc_end);
670+
const uint32_t insn = rv->io.mem_ifetch(rv, block->pc_end);
670671

671672
/* decode the instruction */
672673
if (!rv_decode(ir, insn)) {
@@ -1122,24 +1123,8 @@ void rv_step(void *arg)
11221123
}
11231124

11241125
#if RV32_HAS(SYSTEM)
1125-
static void trap_handler(riscv_t *rv)
1126-
{
1127-
rv_insn_t *ir = mpool_alloc(rv->block_ir_mp);
1128-
assert(ir);
1129-
1130-
/* set to false by sret/mret implementation */
1131-
uint32_t insn;
1132-
while (rv->is_trapped && !rv_has_halted(rv)) {
1133-
insn = rv->io.mem_ifetch(rv->PC);
1134-
assert(insn);
1135-
1136-
rv_decode(ir, insn);
1137-
ir->impl = dispatch_table[ir->opcode];
1138-
rv->compressed = is_compressed(insn);
1139-
ir->impl(rv, ir, rv->csr_cycle, rv->PC);
1140-
}
1141-
}
1142-
#endif
1126+
#include "system.c"
1127+
#endif /* SYSTEM */
11431128

11441129
void ebreak_handler(riscv_t *rv)
11451130
{

src/feature.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,10 @@
6363
#define RV32_FEATURE_T2C 0
6464
#endif
6565

66+
/* System */
67+
#ifndef RV32_FEATURE_SYSTEM
68+
#define RV32_FEATURE_SYSTEM 0
69+
#endif
70+
6671
/* Feature test macro */
6772
#define RV32_HAS(x) RV32_FEATURE_##x

src/gdbstub.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ static int rv_read_mem(void *args, size_t addr, size_t len, void *val)
5555
* an invalid address. We may have to do error handling in the
5656
* mem_read_* function directly.
5757
*/
58-
*((uint8_t *) val + i) = rv->io.mem_read_b(addr + i);
58+
*((uint8_t *) val + i) = rv->io.mem_read_b(rv, addr + i);
5959
}
6060

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

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

7171
return 0;
7272
}

src/main.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,12 +217,21 @@ int main(int argc, char **args)
217217
.log_level = 0,
218218
.run_flag = run_flag,
219219
.profile_output_file = prof_out_file,
220+
#if RV32_HAS(SYSTEM)
221+
.data.system = malloc(sizeof(vm_system_t)),
222+
#else
220223
.data.user = malloc(sizeof(vm_user_t)),
224+
#endif
221225
.cycle_per_step = CYCLE_PER_STEP,
222226
.allow_misalign = opt_misaligned,
223227
};
228+
#if RV32_HAS(SYSTEM)
229+
assert(attr.data.system);
230+
attr.data.system->elf_program = opt_prog_name;
231+
#else
224232
assert(attr.data.user);
225233
attr.data.user->elf_program = opt_prog_name;
234+
#endif
226235

227236
/* create the RISC-V runtime */
228237
rv = rv_create(&attr);

src/riscv.c

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,10 @@ void rv_remap_stdstream(riscv_t *rv, fd_stream_pair_t *fsp, uint32_t fsp_size)
165165
#define MEMIO(op) on_mem_##op
166166
#define IO_HANDLER_IMPL(type, op, RW) \
167167
static IIF(RW)( \
168-
/* W */ void MEMIO(op)(riscv_word_t addr, riscv_##type##_t data), \
169-
/* R */ riscv_##type##_t MEMIO(op)(riscv_word_t addr)) \
168+
/* W */ void MEMIO(op)(UNUSED riscv_t * rv, riscv_word_t addr, \
169+
riscv_##type##_t data), \
170+
/* R */ riscv_##type##_t MEMIO(op)(UNUSED riscv_t * rv, \
171+
riscv_word_t addr)) \
170172
{ \
171173
IIF(RW) \
172174
(memory_##op(addr, (uint8_t *) &data), return memory_##op(addr)); \
@@ -260,9 +262,37 @@ riscv_t *rv_create(riscv_user_t rv_attr)
260262
.on_memset = memset_handler,
261263
};
262264
memcpy(&rv->io, &io, sizeof(riscv_io_t));
263-
} else {
264-
/* TODO: system emulator */
265265
}
266+
#if RV32_HAS(SYSTEM)
267+
else {
268+
/*
269+
* TODO: system emulator
270+
* e.g., kernel image, dtb, rootfs
271+
*
272+
* The test suite is compiled into a single ELF file, so load it as
273+
* an ELF executable, just like a userspace ELF.
274+
*
275+
*/
276+
elf_t *elf = elf_new();
277+
assert(elf && elf_open(elf, (attr->data.system)->elf_program));
278+
279+
const struct Elf32_Sym *end;
280+
if ((end = elf_get_symbol(elf, "_end")))
281+
attr->break_addr = end->st_value;
282+
283+
assert(elf_load(elf, attr->mem));
284+
285+
/* set the entry pc */
286+
const struct Elf32_Ehdr *hdr = get_elf_header(elf);
287+
assert(rv_set_pc(rv, hdr->e_entry));
288+
289+
elf_delete(elf);
290+
291+
/* this variable has external linkage to mmu_io defined in system.c */
292+
extern riscv_io_t mmu_io;
293+
memcpy(&rv->io, &mmu_io, sizeof(riscv_io_t));
294+
}
295+
#endif /* SYSTEM */
266296

267297
/* default standard stream.
268298
* rv_remap_stdstream can be called to overwrite them
@@ -303,20 +333,6 @@ riscv_t *rv_create(riscv_user_t rv_attr)
303333
#endif
304334
#endif
305335

306-
#if RV32_HAS(SYSTEM)
307-
/*
308-
* System simulation defaults to S-mode as
309-
* it does not rely on M-mode software like OpenSBI.
310-
*/
311-
rv->priv_mode = RV_PRIV_S_MODE;
312-
313-
/* not being trapped */
314-
rv->is_trapped = false;
315-
#else
316-
/* ISA simulation defaults to M-mode */
317-
rv->priv_mode = RV_PRIV_M_MODE;
318-
#endif
319-
320336
return rv;
321337
}
322338

@@ -356,7 +372,13 @@ void rv_run(riscv_t *rv)
356372
assert(rv);
357373

358374
vm_attr_t *attr = PRIV(rv);
359-
assert(attr && attr->data.user && attr->data.user->elf_program);
375+
assert(attr &&
376+
#if RV32_HAS(SYSTEM)
377+
attr->data.system && attr->data.system->elf_program
378+
#else
379+
attr->data.user && attr->data.user->elf_program
380+
#endif
381+
);
360382

361383
if (attr->run_flag & RV_RUN_TRACE)
362384
rv_run_and_trace(rv);
@@ -516,6 +538,21 @@ void rv_reset(riscv_t *rv, riscv_word_t pc)
516538
/* reset sp pointing to argc */
517539
rv->X[rv_reg_sp] = stack_top;
518540

541+
/* reset privilege mode */
542+
#if RV32_HAS(SYSTEM)
543+
/*
544+
* System simulation defaults to S-mode as
545+
* it does not rely on M-mode software like OpenSBI.
546+
*/
547+
rv->priv_mode = RV_PRIV_S_MODE;
548+
549+
/* not being trapped */
550+
rv->is_trapped = false;
551+
#else
552+
/* ISA simulation defaults to M-mode */
553+
rv->priv_mode = RV_PRIV_M_MODE;
554+
#endif
555+
519556
/* reset the csrs */
520557
rv->csr_mtvec = 0;
521558
rv->csr_cycle = 0;
@@ -541,7 +578,6 @@ void rv_reset(riscv_t *rv, riscv_word_t pc)
541578
rv->csr_misa |= MISA_M;
542579
#endif
543580

544-
545581
rv->halt = false;
546582
}
547583

0 commit comments

Comments
 (0)