@@ -1951,6 +1951,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
1951
1951
bool virt = env -> virt_enabled ;
1952
1952
bool write_gva = false;
1953
1953
bool always_storeamo = (env -> excp_uw2 & RISCV_UW2_ALWAYS_STORE_AMO );
1954
+ bool vsmode_exc ;
1954
1955
uint64_t s ;
1955
1956
int mode ;
1956
1957
@@ -1965,6 +1966,8 @@ void riscv_cpu_do_interrupt(CPUState *cs)
1965
1966
!(env -> mip & (1ULL << cause ));
1966
1967
bool vs_injected = env -> hvip & (1ULL << cause ) & env -> hvien &&
1967
1968
!(env -> mip & (1ULL << cause ));
1969
+ bool smode_double_trap = false;
1970
+ uint64_t hdeleg = async ? env -> hideleg : env -> hedeleg ;
1968
1971
target_ulong tval = 0 ;
1969
1972
target_ulong tinst = 0 ;
1970
1973
target_ulong htval = 0 ;
@@ -2088,6 +2091,30 @@ void riscv_cpu_do_interrupt(CPUState *cs)
2088
2091
mode = env -> priv <= PRV_S && cause < 64 &&
2089
2092
(((deleg >> cause ) & 1 ) || s_injected || vs_injected ) ? PRV_S : PRV_M ;
2090
2093
2094
+ vsmode_exc = env -> virt_enabled && (((hdeleg >> cause ) & 1 ) || vs_injected );
2095
+ /*
2096
+ * Check double trap condition only if already in S-mode and targeting
2097
+ * S-mode
2098
+ */
2099
+ if (cpu -> cfg .ext_ssdbltrp && env -> priv == PRV_S && mode == PRV_S ) {
2100
+ bool dte = (env -> menvcfg & MENVCFG_DTE ) != 0 ;
2101
+ bool sdt = (env -> mstatus & MSTATUS_SDT ) != 0 ;
2102
+ /* In VS or HS */
2103
+ if (riscv_has_ext (env , RVH )) {
2104
+ if (vsmode_exc ) {
2105
+ /* VS -> VS, use henvcfg instead of menvcfg*/
2106
+ dte = (env -> henvcfg & HENVCFG_DTE ) != 0 ;
2107
+ } else if (env -> virt_enabled ) {
2108
+ /* VS -> HS, use mstatus_hs */
2109
+ sdt = (env -> mstatus_hs & MSTATUS_SDT ) != 0 ;
2110
+ }
2111
+ }
2112
+ smode_double_trap = dte && sdt ;
2113
+ if (smode_double_trap ) {
2114
+ mode = PRV_M ;
2115
+ }
2116
+ }
2117
+
2091
2118
if (mode == PRV_S ) {
2092
2119
/* handle the trap in S-mode */
2093
2120
/* save elp status */
@@ -2096,10 +2123,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
2096
2123
}
2097
2124
2098
2125
if (riscv_has_ext (env , RVH )) {
2099
- uint64_t hdeleg = async ? env -> hideleg : env -> hedeleg ;
2100
-
2101
- if (env -> virt_enabled &&
2102
- (((hdeleg >> cause ) & 1 ) || vs_injected )) {
2126
+ if (vsmode_exc ) {
2103
2127
/* Trap to VS mode */
2104
2128
/*
2105
2129
* See if we need to adjust cause. Yes if its VS mode interrupt
@@ -2132,6 +2156,9 @@ void riscv_cpu_do_interrupt(CPUState *cs)
2132
2156
s = set_field (s , MSTATUS_SPIE , get_field (s , MSTATUS_SIE ));
2133
2157
s = set_field (s , MSTATUS_SPP , env -> priv );
2134
2158
s = set_field (s , MSTATUS_SIE , 0 );
2159
+ if (riscv_env_smode_dbltrp_enabled (env , virt )) {
2160
+ s = set_field (s , MSTATUS_SDT , 1 );
2161
+ }
2135
2162
env -> mstatus = s ;
2136
2163
sxlen = 16 << riscv_cpu_sxl (env );
2137
2164
env -> scause = cause | ((target_ulong )async << (sxlen - 1 ));
@@ -2184,9 +2211,14 @@ void riscv_cpu_do_interrupt(CPUState *cs)
2184
2211
s = set_field (s , MSTATUS_MIE , 0 );
2185
2212
env -> mstatus = s ;
2186
2213
env -> mcause = cause | ((target_ulong )async << (mxlen - 1 ));
2214
+ if (smode_double_trap ) {
2215
+ env -> mtval2 = env -> mcause ;
2216
+ env -> mcause = RISCV_EXCP_DOUBLE_TRAP ;
2217
+ } else {
2218
+ env -> mtval2 = mtval2 ;
2219
+ }
2187
2220
env -> mepc = env -> pc ;
2188
2221
env -> mtval = tval ;
2189
- env -> mtval2 = mtval2 ;
2190
2222
env -> mtinst = tinst ;
2191
2223
2192
2224
/*
0 commit comments