Skip to content

Commit fc0d878

Browse files
committed
Preliminary support for trap handling during block emulation
Traps can occur during block emulation, such as from misaligned access. To handle a trap, we temporarily exit the block and transfer control to the trap handler, processing each instruction by stepping. After the trap is resolved, execution resumes from the point where it occurred. We use a flag called 'is_trapped' to indicate when the emulation is in trap-handling mode. Additionally, the 'sret' instruction is used to adjust the relevant supervisor CSR before returning from a trap. Support for S-mode has been enhanced by adding most S-mode CSRs to 'riscv_internal', along with helper macros for easier manipulation of S-mode and M-mode CSRs. Although M-mode CSRs are not directly used during emulation, the helpers have been added for potential future use. Conditional checks for 'SYSTEM' were added where necessary. The 'LOOKUP_OR_UPDATE_BRANCH_HISTORY_TABLE' functionality is disabled during trap handling, as traps are managed one instruction at a time rather than as a chained block. Related: #310
1 parent ea5b06a commit fc0d878

File tree

12 files changed

+337
-98
lines changed

12 files changed

+337
-98
lines changed

Makefile

Lines changed: 4 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))

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: 146 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

@@ -196,6 +242,26 @@ static uint32_t *csr_get_ptr(riscv_t *rv, uint32_t csr)
196242
case CSR_FCSR:
197243
return (uint32_t *) (&rv->csr_fcsr);
198244
#endif
245+
case CSR_SSTATUS:
246+
return (uint32_t *) (&rv->csr_sstatus);
247+
case CSR_SIE:
248+
return (uint32_t *) (&rv->csr_sie);
249+
case CSR_STVEC:
250+
return (uint32_t *) (&rv->csr_stvec);
251+
case CSR_SCOUNTEREN:
252+
return (uint32_t *) (&rv->csr_scounteren);
253+
case CSR_SSCRATCH:
254+
return (uint32_t *) (&rv->csr_sscratch);
255+
case CSR_SEPC:
256+
return (uint32_t *) (&rv->csr_sepc);
257+
case CSR_SCAUSE:
258+
return (uint32_t *) (&rv->csr_scause);
259+
case CSR_STVAL:
260+
return (uint32_t *) (&rv->csr_stval);
261+
case CSR_SIP:
262+
return (uint32_t *) (&rv->csr_sip);
263+
case CSR_SATP:
264+
return (uint32_t *) (&rv->csr_satp);
199265
default:
200266
return NULL;
201267
}
@@ -377,9 +443,10 @@ enum {
377443
};
378444

379445
#if RV32_HAS(GDBSTUB)
380-
#define RVOP_NO_NEXT(ir) (!ir->next | rv->debug_mode)
446+
#define RVOP_NO_NEXT(ir) \
447+
(!ir->next | rv->debug_mode IIF(RV32_HAS(SYSTEM))(| rv->is_trapped, ))
381448
#else
382-
#define RVOP_NO_NEXT(ir) (!ir->next)
449+
#define RVOP_NO_NEXT(ir) (!ir->next IIF(RV32_HAS(SYSTEM))(| rv->is_trapped, ))
383450
#endif
384451

385452
/* record whether the branch is taken or not during emulation */
@@ -565,8 +632,10 @@ FORCE_INLINE bool insn_is_unconditional_branch(uint8_t opcode)
565632
case rv_insn_ebreak:
566633
case rv_insn_jal:
567634
case rv_insn_jalr:
568-
case rv_insn_sret:
569635
case rv_insn_mret:
636+
#if RV32_HAS(SYSTEM)
637+
case rv_insn_sret:
638+
#endif
570639
#if RV32_HAS(EXT_C)
571640
case rv_insn_cj:
572641
case rv_insn_cjalr:
@@ -598,7 +667,7 @@ static void block_translate(riscv_t *rv, block_t *block)
598667
/* decode the instruction */
599668
if (!rv_decode(ir, insn)) {
600669
rv->compressed = is_compressed(insn);
601-
rv_except_illegal_insn(rv, insn);
670+
rv_trap_illegal_insn(rv, insn);
602671
break;
603672
}
604673
ir->impl = dispatch_table[ir->opcode];
@@ -1048,17 +1117,42 @@ void rv_step(void *arg)
10481117
#endif
10491118
}
10501119

1120+
#if RV32_HAS(SYSTEM)
1121+
static void trap_handler(riscv_t *rv)
1122+
{
1123+
rv_insn_t *ir = mpool_alloc(rv->block_ir_mp);
1124+
assert(ir);
1125+
1126+
/* set to false by sret/mret implementation */
1127+
uint32_t insn;
1128+
while (rv->is_trapped && !rv_has_halted(rv)) {
1129+
insn = rv->io.mem_ifetch(rv->PC);
1130+
assert(insn);
1131+
1132+
rv_decode(ir, insn);
1133+
ir->impl = dispatch_table[ir->opcode];
1134+
rv->compressed = is_compressed(insn);
1135+
ir->impl(rv, ir, rv->csr_cycle, rv->PC);
1136+
}
1137+
}
1138+
#endif
1139+
10511140
void ebreak_handler(riscv_t *rv)
10521141
{
10531142
assert(rv);
1054-
rv_except_breakpoint(rv, rv->PC);
1143+
rv_trap_breakpoint(rv, rv->PC);
10551144
}
10561145

10571146
void ecall_handler(riscv_t *rv)
10581147
{
10591148
assert(rv);
1060-
rv_except_ecall_M(rv, 0);
1149+
#if RV32_HAS(SYSTEM)
10611150
syscall_handler(rv);
1151+
rv->PC += 4;
1152+
#else
1153+
rv_trap_ecall_M(rv, 0);
1154+
syscall_handler(rv);
1155+
#endif
10621156
}
10631157

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