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
5758enum {
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+
10511141void 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
10571147void 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
0 commit comments