Skip to content

Commit edb5a1b

Browse files
authored
Merge pull request #463 from ChinYikMing/trap
Preliminary support for trap handling during block emulation
2 parents a00c49f + a4f6dab commit edb5a1b

File tree

17 files changed

+509
-75
lines changed

17 files changed

+509
-75
lines changed

.github/workflows/main.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ jobs:
6464
make distclean && make ENABLE_EXT_F=0 check -j$(nproc)
6565
make distclean && make ENABLE_EXT_C=0 check -j$(nproc)
6666
make distclean && make ENABLE_SDL=0 check -j$(nproc)
67+
- name: misalignment test in block emulation
68+
run: |
69+
make -C tests/system/alignment/
70+
make distclean && make ENABLE_EXT_C=0 ENABLE_SYSTEM=1 misalign-in-blk-emu -j$(nproc)
6771
- name: gdbstub test
6872
run: |
6973
make distclean && make ENABLE_GDBSTUB=1 gdbstub-test -j$(nproc)

Makefile

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ CFLAGS = -std=gnu99 -O2 -Wall -Wextra
1111
CFLAGS += -Wno-unused-label
1212
CFLAGS += -include src/common.h
1313

14+
ENABLE_SYSTEM ?= 0
15+
$(call set-feature, SYSTEM)
16+
1417
# Enable link-time optimization (LTO)
1518
ENABLE_LTO ?= 1
1619
ifeq ($(call has, LTO), 1)
@@ -134,7 +137,7 @@ endif
134137
ENABLE_JIT ?= 0
135138
$(call set-feature, JIT)
136139
ifeq ($(call has, JIT), 1)
137-
OBJS_EXT += jit.o
140+
OBJS_EXT += jit.o
138141
# tier-2 JIT compiler powered LLVM
139142
LLVM_CONFIG = llvm-config-17
140143
LLVM_CONFIG := $(shell which $(LLVM_CONFIG))
@@ -286,6 +289,16 @@ misalign: $(BIN) artifact
286289
$(PRINTF) "Failed.\n"; \
287290
fi
288291

292+
EXPECTED_misalign = MISALIGNED INSTRUCTION FETCH TEST PASSED!
293+
misalign-in-blk-emu: $(BIN)
294+
$(Q)$(PRINTF) "Running misalign.elf ... "; \
295+
if [ "$(shell $(BIN) tests/system/alignment/misalign.elf | tail -n 2)" = "$(strip $(EXPECTED_misalign)) inferior exit code 0" ]; then \
296+
$(call notice, [OK]); \
297+
else \
298+
$(PRINTF) "Failed.\n"; \
299+
exit 1; \
300+
fi;
301+
289302
# Non-trivial demonstration programs
290303
ifeq ($(call has, SDL), 1)
291304
doom_action := (cd $(OUT); ../$(BIN) riscv32/doom)

src/common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
#define ARRAYS_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
2727

28+
#define MASK(n) (~((~0U << (n))))
29+
2830
/* Alignment macro */
2931
#if defined(__GNUC__) || defined(__clang__)
3032
#define __ALIGNED(x) __attribute__((aligned(x)))

src/decode.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,9 +841,11 @@ static inline bool op_system(rv_insn_t *ir, const uint32_t insn)
841841
case 0x202: /* HRET: return from traps in H-mode */
842842
/* illegal instruction */
843843
return false;
844+
#if RV32_HAS(SYSTEM)
844845
case 0x102: /* SRET: return from traps in S-mode */
845846
ir->opcode = rv_insn_sret;
846847
break;
848+
#endif
847849
case 0x302: /* MRET */
848850
ir->opcode = rv_insn_mret;
849851
break;

src/decode.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ enum op_field {
7878
/* RISC-V Privileged Instruction */ \
7979
_(wfi, 0, 4, 0, ENC(rs1, rd)) \
8080
_(uret, 0, 4, 0, ENC(rs1, rd)) \
81-
_(sret, 1, 4, 0, ENC(rs1, rd)) \
81+
IIF(RV32_HAS(SYSTEM))( \
82+
_(sret, 1, 4, 0, ENC(rs1, rd)) \
83+
) \
8284
_(hret, 0, 4, 0, ENC(rs1, rd)) \
8385
_(mret, 1, 4, 0, ENC(rs1, rd)) \
8486
_(sfencevma, 1, 4, 0, ENC(rs1, rs2, rd)) \

src/emulate.c

Lines changed: 150 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -43,82 +43,127 @@ extern struct target_ops gdbstub_ops;
4343

4444
/* RISC-V exception code list */
4545
/* clang-format off */
46-
#define RV_EXCEPTION_LIST \
46+
#define RV_TRAP_LIST \
4747
IIF(RV32_HAS(EXT_C))(, \
4848
_(insn_misaligned, 0) /* Instruction address misaligned */ \
4949
) \
5050
_(illegal_insn, 2) /* Illegal instruction */ \
5151
_(breakpoint, 3) /* Breakpoint */ \
5252
_(load_misaligned, 4) /* Load address misaligned */ \
5353
_(store_misaligned, 6) /* Store/AMO address misaligned */ \
54-
_(ecall_M, 11) /* Environment call from M-mode */
54+
IIF(RV32_HAS(SYSTEM))(, \
55+
_(ecall_M, 11) /* Environment call from M-mode */ \
56+
)
5557
/* clang-format on */
5658

5759
enum {
58-
#define _(type, code) rv_exception_code##type = code,
59-
RV_EXCEPTION_LIST
60+
#define _(type, code) rv_trap_code_##type = code,
61+
RV_TRAP_LIST
6062
#undef _
6163
};
6264

63-
static void rv_exception_default_handler(riscv_t *rv)
65+
static void rv_trap_default_handler(riscv_t *rv)
6466
{
6567
rv->csr_mepc += rv->compressed ? 2 : 4;
6668
rv->PC = rv->csr_mepc; /* mret */
6769
}
6870

69-
/* When a trap occurs in M-mode, mtval is either initialized to zero or
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+
*/
82+
#if RV32_HAS(SYSTEM)
83+
static void trap_handler(riscv_t *rv);
84+
#endif
85+
86+
/* When a trap occurs in M-mode/S-mode, m/stval is either initialized to zero or
7087
* populated with exception-specific details to assist software in managing
71-
* the trap. Otherwise, the implementation never modifies mtval, although
88+
* the trap. Otherwise, the implementation never modifies m/stval, although
7289
* software can explicitly write to it. The hardware platform will define
7390
* which exceptions are required to informatively set mtval and which may
7491
* consistently set it to zero.
7592
*
7693
* When a hardware breakpoint is triggered or an exception like address
7794
* misalignment, access fault, or page fault occurs during an instruction
78-
* fetch, load, or store operation, mtval is updated with the virtual address
79-
* that caused the fault. In the case of an illegal instruction trap, mtval
95+
* fetch, load, or store operation, m/stval is updated with the virtual address
96+
* that caused the fault. In the case of an illegal instruction trap, m/stval
8097
* might be updated with the first XLEN or ILEN bits of the offending
81-
* instruction. For all other traps, mtval is simply set to zero. However,
82-
* it is worth noting that a future standard could redefine how mtval is
98+
* instruction. For all other traps, m/stval is simply set to zero. However,
99+
* it is worth noting that a future standard could redefine how m/stval is
83100
* handled for different types of traps.
101+
*
102+
* For simplicity and clarity, abstracting stval and mtval into a single
103+
* identifier called tval, as both are handled by TRAP_HANDLER_IMPL.
84104
*/
85-
#define EXCEPTION_HANDLER_IMPL(type, code) \
86-
static void rv_except_##type(riscv_t *rv, uint32_t mtval) \
87-
{ \
88-
/* mtvec (Machine Trap-Vector Base Address Register) \
89-
* mtvec[MXLEN-1:2]: vector base address \
90-
* mtvec[1:0] : vector mode \
91-
*/ \
92-
const uint32_t base = rv->csr_mtvec & ~0x3; \
93-
const uint32_t mode = rv->csr_mtvec & 0x3; \
94-
/* mepc (Machine Exception Program Counter) \
95-
* mtval (Machine Trap Value Register) \
96-
* mcause (Machine Cause Register): store exception code \
97-
* mstatus (Machine Status Register): keep track of and controls the \
98-
* hart’s current operating state \
99-
*/ \
100-
rv->csr_mepc = rv->PC; \
101-
rv->csr_mtval = mtval; \
102-
rv->csr_mcause = code; \
103-
rv->csr_mstatus = MSTATUS_MPP; /* set privilege mode */ \
104-
if (!rv->csr_mtvec) { /* in case CSR is not configured */ \
105-
rv_exception_default_handler(rv); \
106-
return; \
107-
} \
108-
switch (mode) { \
109-
case 0: /* DIRECT: All exceptions set PC to base */ \
110-
rv->PC = base; \
111-
break; \
112-
/* VECTORED: Asynchronous interrupts set PC to base + 4 * code */ \
113-
case 1: \
114-
rv->PC = base + 4 * code; \
115-
break; \
116-
} \
105+
#define TRAP_HANDLER_IMPL(type, code) \
106+
static void rv_trap_##type(riscv_t *rv, uint32_t tval) \
107+
{ \
108+
/* m/stvec (Machine/Supervisor Trap-Vector Base Address Register) \
109+
* m/stvec[MXLEN-1:2]: vector base address \
110+
* m/stvec[1:0] : vector mode \
111+
* m/sepc (Machine/Supervisor Exception Program Counter) \
112+
* m/stval (Machine/Supervisor Trap Value Register) \
113+
* m/scause (Machine/Supervisor Cause Register): store exception code \
114+
* m/sstatus (Machine/Supervisor Status Register): keep track of and \
115+
* controls the hart’s current operating state \
116+
*/ \
117+
uint32_t base; \
118+
uint32_t mode; \
119+
/* user or supervisor */ \
120+
if (RV_PRIV_IS_U_OR_S_MODE()) { \
121+
const uint32_t sstatus_sie = \
122+
(rv->csr_sstatus & SSTATUS_SIE) >> SSTATUS_SIE_SHIFT; \
123+
rv->csr_sstatus |= (sstatus_sie << SSTATUS_SPIE_SHIFT); \
124+
rv->csr_sstatus &= ~(SSTATUS_SIE); \
125+
rv->csr_sstatus |= (rv->priv_mode << SSTATUS_SPP_SHIFT); \
126+
rv->priv_mode = RV_PRIV_S_MODE; \
127+
base = rv->csr_stvec & ~0x3; \
128+
mode = rv->csr_stvec & 0x3; \
129+
rv->csr_sepc = rv->PC; \
130+
rv->csr_stval = tval; \
131+
rv->csr_scause = code; \
132+
} else { /* machine */ \
133+
const uint32_t mstatus_mie = \
134+
(rv->csr_mstatus & MSTATUS_MIE) >> MSTATUS_MIE_SHIFT; \
135+
rv->csr_mstatus |= (mstatus_mie << MSTATUS_MPIE_SHIFT); \
136+
rv->csr_mstatus &= ~(MSTATUS_MIE); \
137+
rv->csr_mstatus |= (rv->priv_mode << MSTATUS_MPP_SHIFT); \
138+
rv->priv_mode = RV_PRIV_M_MODE; \
139+
base = rv->csr_mtvec & ~0x3; \
140+
mode = rv->csr_mtvec & 0x3; \
141+
rv->csr_mepc = rv->PC; \
142+
rv->csr_mtval = tval; \
143+
rv->csr_mcause = code; \
144+
if (!rv->csr_mtvec) { /* in case CSR is not configured */ \
145+
rv_trap_default_handler(rv); \
146+
return; \
147+
} \
148+
} \
149+
switch (mode) { \
150+
/* DIRECT: All traps set PC to base */ \
151+
case 0: \
152+
rv->PC = base; \
153+
break; \
154+
/* VECTORED: Asynchronous traps set PC to base + 4 * code */ \
155+
case 1: \
156+
/* MSB of code is used to indicate whether the trap is interrupt \
157+
* or exception, so it is not considered as the 'real' code */ \
158+
rv->PC = base + 4 * (code & MASK(31)); \
159+
break; \
160+
} \
161+
IIF(RV32_HAS(SYSTEM))(if (rv->is_trapped) trap_handler(rv);, ) \
117162
}
118163

119164
/* RISC-V exception handlers */
120-
#define _(type, code) EXCEPTION_HANDLER_IMPL(type, code)
121-
RV_EXCEPTION_LIST
165+
#define _(type, code) TRAP_HANDLER_IMPL(type, code)
166+
RV_TRAP_LIST
122167
#undef _
123168

124169
/* wrap load/store and insn misaligned handler
@@ -135,7 +180,8 @@ RV_EXCEPTION_LIST
135180
rv->compressed = compress; \
136181
rv->csr_cycle = cycle; \
137182
rv->PC = PC; \
138-
rv_except_##type##_misaligned(rv, IIF(IO)(addr, mask_or_pc)); \
183+
IIF(RV32_HAS(SYSTEM))(rv->is_trapped = true, ); \
184+
rv_trap_##type##_misaligned(rv, IIF(IO)(addr, mask_or_pc)); \
139185
return false; \
140186
}
141187

@@ -164,6 +210,10 @@ static uint32_t *csr_get_ptr(riscv_t *rv, uint32_t csr)
164210
return (uint32_t *) (&rv->csr_misa);
165211

166212
/* Machine Trap Handling */
213+
case CSR_MEDELEG: /* Machine Exception Delegation Register */
214+
return (uint32_t *) (&rv->csr_medeleg);
215+
case CSR_MIDELEG: /* Machine Interrupt Delegation Register */
216+
return (uint32_t *) (&rv->csr_mideleg);
167217
case CSR_MSCRATCH: /* Machine Scratch Register */
168218
return (uint32_t *) (&rv->csr_mscratch);
169219
case CSR_MEPC: /* Machine Exception Program Counter */
@@ -196,6 +246,26 @@ static uint32_t *csr_get_ptr(riscv_t *rv, uint32_t csr)
196246
case CSR_FCSR:
197247
return (uint32_t *) (&rv->csr_fcsr);
198248
#endif
249+
case CSR_SSTATUS:
250+
return (uint32_t *) (&rv->csr_sstatus);
251+
case CSR_SIE:
252+
return (uint32_t *) (&rv->csr_sie);
253+
case CSR_STVEC:
254+
return (uint32_t *) (&rv->csr_stvec);
255+
case CSR_SCOUNTEREN:
256+
return (uint32_t *) (&rv->csr_scounteren);
257+
case CSR_SSCRATCH:
258+
return (uint32_t *) (&rv->csr_sscratch);
259+
case CSR_SEPC:
260+
return (uint32_t *) (&rv->csr_sepc);
261+
case CSR_SCAUSE:
262+
return (uint32_t *) (&rv->csr_scause);
263+
case CSR_STVAL:
264+
return (uint32_t *) (&rv->csr_stval);
265+
case CSR_SIP:
266+
return (uint32_t *) (&rv->csr_sip);
267+
case CSR_SATP:
268+
return (uint32_t *) (&rv->csr_satp);
199269
default:
200270
return NULL;
201271
}
@@ -377,9 +447,10 @@ enum {
377447
};
378448

379449
#if RV32_HAS(GDBSTUB)
380-
#define RVOP_NO_NEXT(ir) (!ir->next | rv->debug_mode)
450+
#define RVOP_NO_NEXT(ir) \
451+
(!ir->next | rv->debug_mode IIF(RV32_HAS(SYSTEM))(| rv->is_trapped, ))
381452
#else
382-
#define RVOP_NO_NEXT(ir) (!ir->next)
453+
#define RVOP_NO_NEXT(ir) (!ir->next IIF(RV32_HAS(SYSTEM))(| rv->is_trapped, ))
383454
#endif
384455

385456
/* record whether the branch is taken or not during emulation */
@@ -565,8 +636,10 @@ FORCE_INLINE bool insn_is_unconditional_branch(uint8_t opcode)
565636
case rv_insn_ebreak:
566637
case rv_insn_jal:
567638
case rv_insn_jalr:
568-
case rv_insn_sret:
569639
case rv_insn_mret:
640+
#if RV32_HAS(SYSTEM)
641+
case rv_insn_sret:
642+
#endif
570643
#if RV32_HAS(EXT_C)
571644
case rv_insn_cj:
572645
case rv_insn_cjalr:
@@ -598,7 +671,7 @@ static void block_translate(riscv_t *rv, block_t *block)
598671
/* decode the instruction */
599672
if (!rv_decode(ir, insn)) {
600673
rv->compressed = is_compressed(insn);
601-
rv_except_illegal_insn(rv, insn);
674+
rv_trap_illegal_insn(rv, insn);
602675
break;
603676
}
604677
ir->impl = dispatch_table[ir->opcode];
@@ -1048,17 +1121,42 @@ void rv_step(void *arg)
10481121
#endif
10491122
}
10501123

1124+
#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
1143+
10511144
void ebreak_handler(riscv_t *rv)
10521145
{
10531146
assert(rv);
1054-
rv_except_breakpoint(rv, rv->PC);
1147+
rv_trap_breakpoint(rv, rv->PC);
10551148
}
10561149

10571150
void ecall_handler(riscv_t *rv)
10581151
{
10591152
assert(rv);
1060-
rv_except_ecall_M(rv, 0);
1153+
#if RV32_HAS(SYSTEM)
10611154
syscall_handler(rv);
1155+
rv->PC += 4;
1156+
#else
1157+
rv_trap_ecall_M(rv, 0);
1158+
syscall_handler(rv);
1159+
#endif
10621160
}
10631161

10641162
void memset_handler(riscv_t *rv)

src/riscv.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,20 @@ riscv_t *rv_create(riscv_user_t rv_attr)
303303
#endif
304304
#endif
305305

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+
306320
return rv;
307321
}
308322

0 commit comments

Comments
 (0)