Skip to content

Commit 592aff0

Browse files
committed
Preliminary support for MMU emulation
To boot a 32-bit RISC-V Linux with MMU, MMU emulation support is essential. The virtual memory scheme required is SV32. 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, its prototype is modified to allow access to the RISC-V core instance as the first parameter, since MMU-enabled I/O requires access to the SATP CSR. Additionally, a trap_handler callback is added to the riscv_io_t interface to route the actual trap handler. This approach keeps the dispatch_table and TRAP_HANDLER_IMPL static within emulate.c, aligning the schema with other handlers like ebreak_handler and ecall_handler. The SET_CAUSE_AND_TVAL_THEN_TRAP macro is introduced to simplify the dispatch process when invoking a trap. 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 (possibly supported in the future). 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 three aforementioned page faults. Related: #310
1 parent edb5a1b commit 592aff0

File tree

17 files changed

+1495
-144
lines changed

17 files changed

+1495
-144
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ CFLAGS += $(CFLAGS_NO_CET)
4545

4646
OBJS_EXT :=
4747

48+
ifeq ($(call has, SYSTEM), 1)
49+
OBJS_EXT += system.o
50+
endif
51+
4852
# Integer Multiplication and Division instructions
4953
ENABLE_EXT_M ?= 1
5054
$(call set-feature, EXT_M)

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: 86 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
#include <emscripten.h>
1515
#endif
1616

17+
#if RV32_HAS(SYSTEM)
18+
#include "system.h"
19+
#endif /* RV32_HAS(SYSTEM) */
20+
1721
#if RV32_HAS(EXT_F)
1822
#include <math.h>
1923
#include "softfloat.h"
@@ -41,18 +45,21 @@ extern struct target_ops gdbstub_ops;
4145
#define IF_rs2(i, r) (i->rs2 == rv_reg_##r)
4246
#define IF_imm(i, v) (i->imm == v)
4347

44-
/* RISC-V exception code list */
48+
/* RISC-V trap code list */
4549
/* 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 */ \
50+
#define RV_TRAP_LIST \
51+
IIF(RV32_HAS(EXT_C))(, \
52+
_(insn_misaligned, INSN_MISALIGNED) /* Instruction address misaligned */ \
53+
) \
54+
_(illegal_insn, ILLEGAL_INSN) /* Illegal instruction */ \
55+
_(breakpoint, BREAKPOINT) /* Breakpoint */ \
56+
_(load_misaligned, LOAD_MISALIGNED) /* Load address misaligned */ \
57+
_(store_misaligned, STORE_MISALIGNED) /* Store/AMO address misaligned */ \
58+
IIF(RV32_HAS(SYSTEM))( \
59+
_(pagefault_insn, PAGEFAULT_INSN) /* Instruction page fault */ \
60+
_(pagefault_load, PAGEFAULT_LOAD) /* Load page fault */ \
61+
_(pagefault_store, PAGEFAULT_STORE), /* Store page fault */ \
62+
_(ecall_M, ECALL_M) /* Environment call from M-mode */ \
5663
)
5764
/* clang-format on */
5865

@@ -68,20 +75,9 @@ static void rv_trap_default_handler(riscv_t *rv)
6875
rv->PC = rv->csr_mepc; /* mret */
6976
}
7077

71-
/*
72-
* Trap might occurs during block emulation. For instance, page fault.
73-
* In order to handle trap, we have to escape from block and execute
74-
* registered trap handler. This trap_handler function helps to execute
75-
* the registered trap handler, PC by PC. Once the trap is handled,
76-
* resume the previous execution flow where cause the trap.
77-
*
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-
*/
8278
#if RV32_HAS(SYSTEM)
83-
static void trap_handler(riscv_t *rv);
84-
#endif
79+
static void __trap_handler(riscv_t *rv);
80+
#endif /* SYSTEM */
8581

8682
/* When a trap occurs in M-mode/S-mode, m/stval is either initialized to zero or
8783
* populated with exception-specific details to assist software in managing
@@ -158,7 +154,7 @@ static void trap_handler(riscv_t *rv);
158154
rv->PC = base + 4 * (code & MASK(31)); \
159155
break; \
160156
} \
161-
IIF(RV32_HAS(SYSTEM))(if (rv->is_trapped) trap_handler(rv);, ) \
157+
IIF(RV32_HAS(SYSTEM))(if (rv->is_trapped) __trap_handler(rv);, ) \
162158
}
163159

164160
/* RISC-V exception handlers */
@@ -180,8 +176,8 @@ RV_TRAP_LIST
180176
rv->compressed = compress; \
181177
rv->csr_cycle = cycle; \
182178
rv->PC = PC; \
183-
IIF(RV32_HAS(SYSTEM))(rv->is_trapped = true, ); \
184-
rv_trap_##type##_misaligned(rv, IIF(IO)(addr, mask_or_pc)); \
179+
SET_CAUSE_AND_TVAL_THEN_TRAP(rv, type##_MISALIGNED, \
180+
IIF(IO)(addr, mask_or_pc)); \
185181
return false; \
186182
}
187183

@@ -531,8 +527,8 @@ static bool do_fuse3(riscv_t *rv, rv_insn_t *ir, uint64_t cycle, uint32_t PC)
531527
*/
532528
for (int i = 0; i < ir->imm2; i++) {
533529
uint32_t addr = rv->X[fuse[i].rs1] + fuse[i].imm;
534-
RV_EXC_MISALIGN_HANDLER(3, store, false, 1);
535-
rv->io.mem_write_w(addr, rv->X[fuse[i].rs2]);
530+
RV_EXC_MISALIGN_HANDLER(3, STORE, false, 1);
531+
rv->io.mem_write_w(rv, addr, rv->X[fuse[i].rs2]);
536532
}
537533
PC += ir->imm2 * 4;
538534
if (unlikely(RVOP_NO_NEXT(ir))) {
@@ -555,8 +551,8 @@ static bool do_fuse4(riscv_t *rv, rv_insn_t *ir, uint64_t cycle, uint32_t PC)
555551
*/
556552
for (int i = 0; i < ir->imm2; i++) {
557553
uint32_t addr = rv->X[fuse[i].rs1] + fuse[i].imm;
558-
RV_EXC_MISALIGN_HANDLER(3, load, false, 1);
559-
rv->X[fuse[i].rd] = rv->io.mem_read_w(addr);
554+
RV_EXC_MISALIGN_HANDLER(3, LOAD, false, 1);
555+
rv->X[fuse[i].rd] = rv->io.mem_read_w(rv, addr);
560556
}
561557
PC += ir->imm2 * 4;
562558
if (unlikely(RVOP_NO_NEXT(ir))) {
@@ -666,12 +662,12 @@ static void block_translate(riscv_t *rv, block_t *block)
666662
prev_ir->next = ir;
667663

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

671667
/* decode the instruction */
672668
if (!rv_decode(ir, insn)) {
673669
rv->compressed = is_compressed(insn);
674-
rv_trap_illegal_insn(rv, insn);
670+
SET_CAUSE_AND_TVAL_THEN_TRAP(rv, INSN_MISALIGNED, insn);
675671
break;
676672
}
677673
ir->impl = dispatch_table[ir->opcode];
@@ -1122,15 +1118,14 @@ void rv_step(void *arg)
11221118
}
11231119

11241120
#if RV32_HAS(SYSTEM)
1125-
static void trap_handler(riscv_t *rv)
1121+
static void __trap_handler(riscv_t *rv)
11261122
{
11271123
rv_insn_t *ir = mpool_alloc(rv->block_ir_mp);
11281124
assert(ir);
11291125

1130-
/* set to false by sret/mret implementation */
1131-
uint32_t insn;
1126+
/* set to false by sret implementation */
11321127
while (rv->is_trapped && !rv_has_halted(rv)) {
1133-
insn = rv->io.mem_ifetch(rv->PC);
1128+
uint32_t insn = rv->io.mem_ifetch(rv, rv->PC);
11341129
assert(insn);
11351130

11361131
rv_decode(ir, insn);
@@ -1139,12 +1134,63 @@ static void trap_handler(riscv_t *rv)
11391134
ir->impl(rv, ir, rv->csr_cycle, rv->PC);
11401135
}
11411136
}
1142-
#endif
1137+
#endif /* SYSTEM */
1138+
1139+
static void _trap_handler(riscv_t *rv)
1140+
{
1141+
uint32_t cause = RV_PRIV_IS_U_OR_S_MODE() ? rv->csr_scause : rv->csr_mcause;
1142+
uint32_t tval = RV_PRIV_IS_U_OR_S_MODE() ? rv->csr_stval : rv->csr_mtval;
1143+
1144+
switch (cause) {
1145+
#if !RV32_HAS(EXT_C)
1146+
case INSN_MISALIGNED:
1147+
rv_trap_insn_misaligned(rv, tval);
1148+
break;
1149+
#endif /* EXT_C */
1150+
case ILLEGAL_INSN:
1151+
rv_trap_illegal_insn(rv, tval);
1152+
break;
1153+
case BREAKPOINT:
1154+
rv_trap_breakpoint(rv, tval);
1155+
break;
1156+
case LOAD_MISALIGNED:
1157+
rv_trap_load_misaligned(rv, tval);
1158+
break;
1159+
case STORE_MISALIGNED:
1160+
rv_trap_store_misaligned(rv, tval);
1161+
break;
1162+
#if RV32_HAS(SYSTEM)
1163+
case PAGEFAULT_INSN:
1164+
rv_trap_pagefault_insn(rv, tval);
1165+
break;
1166+
case PAGEFAULT_LOAD:
1167+
rv_trap_pagefault_load(rv, tval);
1168+
break;
1169+
case PAGEFAULT_STORE:
1170+
rv_trap_pagefault_store(rv, tval);
1171+
break;
1172+
#endif /* SYSTEM */
1173+
#if !RV32_HAS(SYSTEM)
1174+
case ECALL_M:
1175+
rv_trap_ecall_M(rv, tval);
1176+
break;
1177+
#endif /* SYSTEM */
1178+
default:
1179+
__UNREACHABLE;
1180+
break;
1181+
}
1182+
}
1183+
1184+
void trap_handler(riscv_t *rv)
1185+
{
1186+
assert(rv);
1187+
_trap_handler(rv);
1188+
}
11431189

11441190
void ebreak_handler(riscv_t *rv)
11451191
{
11461192
assert(rv);
1147-
rv_trap_breakpoint(rv, rv->PC);
1193+
SET_CAUSE_AND_TVAL_THEN_TRAP(rv, BREAKPOINT, rv->PC);
11481194
}
11491195

11501196
void ecall_handler(riscv_t *rv)
@@ -1154,7 +1200,7 @@ void ecall_handler(riscv_t *rv)
11541200
syscall_handler(rv);
11551201
rv->PC += 4;
11561202
#else
1157-
rv_trap_ecall_M(rv, 0);
1203+
SET_CAUSE_AND_TVAL_THEN_TRAP(rv, ECALL_M, 0);
11581204
syscall_handler(rv);
11591205
#endif
11601206
}

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);

0 commit comments

Comments
 (0)