Skip to content

Commit 947f3a2

Browse files
committed
Support trap handling during block emulation
Trap might occurs during block emulation, for example, page fault. In order to handle trap, we have to temporarily escape from block and jump to the trap handler to handle the trap PC by PC. Once trap is handled, resume the previous execution flow where cause the trap. To support this, we leverage setjmp function to store the state and a flag called is_trapped to indicate the emulation is in a trap handling mode currently. The sret and mret implementation are refactored to control the corresponding CSR before get out from trap. Some S-mode CSRs are added to riscv_internal to support S-mode. S-mode, and M-mode CSR helper macros are also introduced, so that the CSR can be manipulated in an easier manner. A test suite shall be introduced with this PR, please do not merge yet. Related: #310
1 parent 618f22a commit 947f3a2

File tree

6 files changed

+277
-75
lines changed

6 files changed

+277
-75
lines changed

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/emulate.c

Lines changed: 134 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
#include <assert.h>
7+
#include <setjmp.h>
78
#include <stdbool.h>
89
#include <stdint.h>
910
#include <stdio.h>
@@ -43,7 +44,7 @@ extern struct target_ops gdbstub_ops;
4344

4445
/* RISC-V exception code list */
4546
/* clang-format off */
46-
#define RV_EXCEPTION_LIST \
47+
#define RV_TRAP_LIST \
4748
IIF(RV32_HAS(EXT_C))(, \
4849
_(insn_misaligned, 0) /* Instruction address misaligned */ \
4950
) \
@@ -55,17 +56,32 @@ extern struct target_ops gdbstub_ops;
5556
/* clang-format on */
5657

5758
enum {
58-
#define _(type, code) rv_exception_code##type = code,
59-
RV_EXCEPTION_LIST
59+
#define _(type, code) rv_trap_code_##type = code,
60+
RV_TRAP_LIST
6061
#undef _
6162
};
6263

63-
static void rv_exception_default_handler(riscv_t *rv)
64+
static void rv_trap_default_handler(riscv_t *rv)
6465
{
6566
rv->csr_mepc += rv->compressed ? 2 : 4;
6667
rv->PC = rv->csr_mepc; /* mret */
6768
}
6869

70+
/* Trap might occurs during block emulation. For instance, page fault.
71+
* In order to handle trap, we have to escape from block and execute
72+
* registered trap handler. This trap_handler function helps to execute
73+
* the registered trap handler, PC by PC. Once the trap is handled,
74+
* resume the previous execution flow where cause the trap.
75+
*/
76+
#if RV32_HAS(SYSTEM)
77+
static void trap_handler(riscv_t *rv);
78+
#else
79+
/* should not be called in non-SYSTEM mode since default trap handler is capable
80+
* to handle traps
81+
*/
82+
static void trap_handler(riscv_t *rv UNUSED) {}
83+
#endif
84+
6985
/* When a trap occurs in M-mode, mtval is either initialized to zero or
7086
* populated with exception-specific details to assist software in managing
7187
* the trap. Otherwise, the implementation never modifies mtval, although
@@ -82,43 +98,80 @@ static void rv_exception_default_handler(riscv_t *rv)
8298
* it is worth noting that a future standard could redefine how mtval is
8399
* handled for different types of traps.
84100
*/
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-
} \
101+
static jmp_buf env;
102+
#define TRAP_HANDLER_IMPL(type, code) \
103+
static void rv_trap_##type(riscv_t *rv, uint32_t mtval) \
104+
{ \
105+
/* m/stvec (Machine/Supervisor Trap-Vector Base Address Register) \
106+
* m/stvec[MXLEN-1:2]: vector base address \
107+
* m/stvec[1:0] : vector mode \
108+
*/ \
109+
uint32_t base; \
110+
uint32_t 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+
/* supervisor */ \
118+
if (rv->csr_medeleg & (1U << code) || \
119+
rv->csr_mideleg & (1U << code)) { \
120+
const uint32_t sstatus_sie = \
121+
(rv->csr_sstatus & SSTATUS_SIE) >> SSTATUS_SIE_SHIFT; \
122+
rv->csr_sstatus |= (sstatus_sie << SSTATUS_SPIE_SHIFT); \
123+
rv->csr_sstatus &= ~(SSTATUS_SIE); \
124+
rv->csr_sstatus |= (rv->priv_mode << SSTATUS_SPP_SHIFT); \
125+
rv->priv_mode = RV_PRIV_S_MODE; \
126+
base = rv->csr_stvec & ~0x3; \
127+
mode = rv->csr_stvec & 0x3; \
128+
rv->csr_sepc = rv->PC; \
129+
rv->csr_stval = mtval; \
130+
rv->csr_scause = code; \
131+
rv->csr_sstatus |= SSTATUS_SPP; /* set privilege mode */ \
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 = mtval; \
143+
rv->csr_mcause = code; \
144+
rv->csr_mstatus |= MSTATUS_MPP; /* set privilege mode */ \
145+
if (!rv->csr_mtvec) { /* in case CSR is not configured */ \
146+
rv_trap_default_handler(rv); \
147+
return; \
148+
} \
149+
} \
150+
switch (mode) { \
151+
/* DIRECT: All traps set PC to base */ \
152+
case 0: \
153+
rv->PC = base; \
154+
break; \
155+
/* VECTORED: Asynchronous traps set PC to base + 4 * code */ \
156+
case 1: \
157+
/* MSB of code is used to indicate whether the trap is interrupt \
158+
* or exception, so it is not considered as the 'real' code */ \
159+
rv->PC = base + 4 * (code & MASK(31)); \
160+
break; \
161+
} \
162+
/* block escaping for trap handling */ \
163+
if (rv->is_trapped) { \
164+
if (setjmp(env) == 0) { \
165+
trap_handler(rv); \
166+
} else { \
167+
fprintf(stderr, "setjmp failed"); \
168+
} \
169+
} \
117170
}
118171

119172
/* RISC-V exception handlers */
120-
#define _(type, code) EXCEPTION_HANDLER_IMPL(type, code)
121-
RV_EXCEPTION_LIST
173+
#define _(type, code) TRAP_HANDLER_IMPL(type, code)
174+
RV_TRAP_LIST
122175
#undef _
123176

124177
/* wrap load/store and insn misaligned handler
@@ -135,7 +188,7 @@ RV_EXCEPTION_LIST
135188
rv->compressed = compress; \
136189
rv->csr_cycle = cycle; \
137190
rv->PC = PC; \
138-
rv_except_##type##_misaligned(rv, IIF(IO)(addr, mask_or_pc)); \
191+
rv_trap_##type##_misaligned(rv, IIF(IO)(addr, mask_or_pc)); \
139192
return false; \
140193
}
141194

@@ -196,6 +249,26 @@ static uint32_t *csr_get_ptr(riscv_t *rv, uint32_t csr)
196249
case CSR_FCSR:
197250
return (uint32_t *) (&rv->csr_fcsr);
198251
#endif
252+
case CSR_SSTATUS:
253+
return (uint32_t *) (&rv->csr_sstatus);
254+
case CSR_SIE:
255+
return (uint32_t *) (&rv->csr_sie);
256+
case CSR_STVEC:
257+
return (uint32_t *) (&rv->csr_stvec);
258+
case CSR_SCOUNTEREN:
259+
return (uint32_t *) (&rv->csr_scounteren);
260+
case CSR_SSCRATCH:
261+
return (uint32_t *) (&rv->csr_sscratch);
262+
case CSR_SEPC:
263+
return (uint32_t *) (&rv->csr_sepc);
264+
case CSR_SCAUSE:
265+
return (uint32_t *) (&rv->csr_scause);
266+
case CSR_STVAL:
267+
return (uint32_t *) (&rv->csr_stval);
268+
case CSR_SIP:
269+
return (uint32_t *) (&rv->csr_sip);
270+
case CSR_SATP:
271+
return (uint32_t *) (&rv->csr_satp);
199272
default:
200273
return NULL;
201274
}
@@ -377,9 +450,9 @@ enum {
377450
};
378451

379452
#if RV32_HAS(GDBSTUB)
380-
#define RVOP_NO_NEXT(ir) (!ir->next | rv->debug_mode)
453+
#define RVOP_NO_NEXT(ir) (!ir->next | rv->debug_mode | rv->is_trapped)
381454
#else
382-
#define RVOP_NO_NEXT(ir) (!ir->next)
455+
#define RVOP_NO_NEXT(ir) (!ir->next | rv->is_trapped)
383456
#endif
384457

385458
/* record whether the branch is taken or not during emulation */
@@ -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,16 +1121,33 @@ 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+
uint32_t insn;
1131+
while (rv->is_trapped) { /* set to false by sret/mret implementation */
1132+
insn = rv->io.mem_ifetch(rv, rv->PC);
1133+
rv_decode(ir, insn);
1134+
ir->impl = dispatch_table[ir->opcode];
1135+
rv->compressed = is_compressed(insn);
1136+
ir->impl(rv, ir, rv->csr_cycle, rv->PC);
1137+
}
1138+
}
1139+
#endif
1140+
10511141
void ebreak_handler(riscv_t *rv)
10521142
{
10531143
assert(rv);
1054-
rv_except_breakpoint(rv, rv->PC);
1144+
rv_trap_breakpoint(rv, rv->PC);
10551145
}
10561146

10571147
void ecall_handler(riscv_t *rv)
10581148
{
10591149
assert(rv);
1060-
rv_except_ecall_M(rv, 0);
1150+
rv_trap_ecall_M(rv, 0);
10611151
syscall_handler(rv);
10621152
}
10631153

src/riscv.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ riscv_t *rv_create(riscv_user_t rv_attr)
221221
attr->mem = memory_new(attr->mem_size);
222222
assert(attr->mem);
223223

224+
/* not being trapped */
225+
rv->is_trapped = false;
226+
224227
/* reset */
225228
rv_reset(rv, 0U);
226229

src/riscv.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,60 @@ enum {
9696
#define MSTATUS_MPIE (1 << MSTATUS_MPIE_SHIFT)
9797
#define MSTATUS_MPP (3 << MSTATUS_MPP_SHIFT)
9898

99+
/* The mstatus register keeps track of and controls the hart’s current operating
100+
* state */
101+
#define MSTATUS_SIE_SHIFT 1
102+
#define MSTATUS_MIE_SHIFT 3
103+
#define MSTATUS_SPIE_SHIFT 5
104+
#define MSTATUS_UBE_SHIFT 6
105+
#define MSTATUS_MPIE_SHIFT 7
106+
#define MSTATUS_SPP_SHIFT 8
107+
#define MSTATUS_MPP_SHIFT 11
108+
#define MSTATUS_MPRV_SHIFT 17
109+
#define MSTATUS_SUM_SHIFT 18
110+
#define MSTATUS_MXR_SHIFT 18
111+
#define MSTATUS_TVM_SHIFT 20
112+
#define MSTATUS_TW_SHIFT 21
113+
#define MSTATUS_TSR_SHIFT 22
114+
#define MSTATUS_SIE (1 << MSTATUS_SIE_SHIFT)
115+
#define MSTATUS_MIE (1 << MSTATUS_MIE_SHIFT)
116+
#define MSTATUS_SPIE (1 << MSTATUS_SPIE_SHIFT)
117+
#define MSTATUS_UBE (1 << MSTATUS_UBE_SHIFT)
118+
#define MSTATUS_MPIE (1 << MSTATUS_MPIE_SHIFT)
119+
#define MSTATUS_SPP (1 << MSTATUS_SPP_SHIFT)
120+
#define MSTATUS_MPP (3 << MSTATUS_MPP_SHIFT)
121+
#define MSTATUS_MPRV (1 << MSTATUS_MPRV_SHIFT)
122+
#define MSTATUS_SUM (1 << MSTATUS_SUM_SHIFT)
123+
#define MSTATUS_MXR (1 << MSTATUS_MXR_SHIFT)
124+
#define MSTATUS_TVM (1 << MSTATUS_TVM_SHIFT)
125+
#define MSTATUS_TW (1 << MSTATUS_TW_SHIFT)
126+
#define MSTATUS_TSR (1 << MSTATUS_TSR_SHIFT)
127+
128+
/* A restricted view of mstatus */
129+
#define SSTATUS_SIE_SHIFT 1
130+
#define SSTATUS_SPIE_SHIFT 5
131+
#define SSTATUS_UBE_SHIFT 6
132+
#define SSTATUS_SPP_SHIFT 8
133+
#define SSTATUS_SUM_SHIFT 18
134+
#define SSTATUS_MXR_SHIFT 19
135+
#define SSTATUS_SIE (1 << SSTATUS_SIE_SHIFT)
136+
#define SSTATUS_SPIE (1 << SSTATUS_SPIE_SHIFT)
137+
#define SSTATUS_UBE (1 << SSTATUS_UBE_SHIFT)
138+
#define SSTATUS_SPP (1 << SSTATUS_SPP_SHIFT)
139+
#define SSTATUS_SUM (1 << SSTATUS_SUM_SHIFT)
140+
#define SSTATUS_MXR (1 << SSTATUS_MXR_SHIFT)
141+
142+
#define SIP_SSIP_SHIFT 1
143+
#define SIP_STIP_SHIFT 5
144+
#define SIP_SEIP_SHIFT 9
145+
#define SIP_SSIP (1 << SIP_SSIP_SHIFT)
146+
#define SIP_STIP (1 << SIP_STIP_SHIFT)
147+
#define SIP_SEIP (1 << SIP_SEIP_SHIFT)
148+
149+
#define RV_PRIV_U_MODE 0
150+
#define RV_PRIV_S_MODE 1
151+
#define RV_PRIV_M_MODE 3
152+
99153
/*
100154
* SBI functions must return a pair of values:
101155
*

0 commit comments

Comments
 (0)