Skip to content

Commit c6bcc56

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 c6bcc56

File tree

15 files changed

+1310
-93
lines changed

15 files changed

+1310
-93
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: 29 additions & 33 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

@@ -290,7 +293,16 @@ static uint32_t csr_csrrw(riscv_t *rv, uint32_t csr, uint32_t val)
290293
out &= FFLAG_MASK;
291294
#endif
292295

293-
*c = val;
296+
if (c == &rv->csr_satp) {
297+
const uint8_t mode_sv32 = val >> 31;
298+
if (mode_sv32)
299+
*c = val & MASK(22); /* store ppn */
300+
else /* bare mode */
301+
*c = 0; /* virtual mem addr maps to same physical mem addr directly
302+
*/
303+
} else {
304+
*c = val;
305+
}
294306

295307
return out;
296308
}
@@ -532,7 +544,7 @@ static bool do_fuse3(riscv_t *rv, rv_insn_t *ir, uint64_t cycle, uint32_t PC)
532544
for (int i = 0; i < ir->imm2; i++) {
533545
uint32_t addr = rv->X[fuse[i].rs1] + fuse[i].imm;
534546
RV_EXC_MISALIGN_HANDLER(3, store, false, 1);
535-
rv->io.mem_write_w(addr, rv->X[fuse[i].rs2]);
547+
rv->io.mem_write_w(rv, addr, rv->X[fuse[i].rs2]);
536548
}
537549
PC += ir->imm2 * 4;
538550
if (unlikely(RVOP_NO_NEXT(ir))) {
@@ -556,7 +568,7 @@ static bool do_fuse4(riscv_t *rv, rv_insn_t *ir, uint64_t cycle, uint32_t PC)
556568
for (int i = 0; i < ir->imm2; i++) {
557569
uint32_t addr = rv->X[fuse[i].rs1] + fuse[i].imm;
558570
RV_EXC_MISALIGN_HANDLER(3, load, false, 1);
559-
rv->X[fuse[i].rd] = rv->io.mem_read_w(addr);
571+
rv->X[fuse[i].rd] = rv->io.mem_read_w(rv, addr);
560572
}
561573
PC += ir->imm2 * 4;
562574
if (unlikely(RVOP_NO_NEXT(ir))) {
@@ -666,7 +678,7 @@ static void block_translate(riscv_t *rv, block_t *block)
666678
prev_ir->next = ir;
667679

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

671683
/* decode the instruction */
672684
if (!rv_decode(ir, insn)) {
@@ -1122,24 +1134,8 @@ void rv_step(void *arg)
11221134
}
11231135

11241136
#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
1137+
#include "system.c"
1138+
#endif /* SYSTEM */
11431139

11441140
void ebreak_handler(riscv_t *rv)
11451141
{

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: 41 additions & 5 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
@@ -356,7 +386,11 @@ void rv_run(riscv_t *rv)
356386
assert(rv);
357387

358388
vm_attr_t *attr = PRIV(rv);
389+
#if RV32_HAS(SYSTEM)
390+
assert(attr && attr->data.system && attr->data.system->elf_program);
391+
#else
359392
assert(attr && attr->data.user && attr->data.user->elf_program);
393+
#endif
360394

361395
if (attr->run_flag & RV_RUN_TRACE)
362396
rv_run_and_trace(rv);
@@ -516,6 +550,9 @@ void rv_reset(riscv_t *rv, riscv_word_t pc)
516550
/* reset sp pointing to argc */
517551
rv->X[rv_reg_sp] = stack_top;
518552

553+
/* reset privilege mode */
554+
rv->priv_mode = RV_PRIV_M_MODE;
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)