1 /* 2 * Copyright (c) 2011 - 2019, Max Filippov, Open Source and Linux Lab. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * * Neither the name of the Open Source and Linux Lab nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "qemu/osdep.h" 29 #include "qemu/log.h" 30 #include "qemu/main-loop.h" 31 #include "cpu.h" 32 #include "exec/helper-proto.h" 33 #include "qemu/host-utils.h" 34 #include "qemu/atomic.h" 35 #include "exec/exec-all.h" 36 37 void HELPER(exception)(CPUXtensaState *env, uint32_t excp) 38 { 39 CPUState *cs = env_cpu(env); 40 41 cs->exception_index = excp; 42 if (excp == EXCP_YIELD) { 43 env->yield_needed = 0; 44 } 45 cpu_loop_exit(cs); 46 } 47 48 void HELPER(exception_cause)(CPUXtensaState *env, uint32_t pc, uint32_t cause) 49 { 50 uint32_t vector; 51 52 env->pc = pc; 53 if (env->sregs[PS] & PS_EXCM) { 54 if (env->config->ndepc) { 55 env->sregs[DEPC] = pc; 56 } else { 57 env->sregs[EPC1] = pc; 58 } 59 vector = EXC_DOUBLE; 60 } else { 61 env->sregs[EPC1] = pc; 62 vector = (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL; 63 } 64 65 env->sregs[EXCCAUSE] = cause; 66 env->sregs[PS] |= PS_EXCM; 67 68 HELPER(exception)(env, vector); 69 } 70 71 void HELPER(exception_cause_vaddr)(CPUXtensaState *env, 72 uint32_t pc, uint32_t cause, uint32_t vaddr) 73 { 74 env->sregs[EXCVADDR] = vaddr; 75 HELPER(exception_cause)(env, pc, cause); 76 } 77 78 void debug_exception_env(CPUXtensaState *env, uint32_t cause) 79 { 80 if (xtensa_get_cintlevel(env) < env->config->debug_level) { 81 HELPER(debug_exception)(env, env->pc, cause); 82 } 83 } 84 85 void HELPER(debug_exception)(CPUXtensaState *env, uint32_t pc, uint32_t cause) 86 { 87 unsigned level = env->config->debug_level; 88 89 env->pc = pc; 90 env->sregs[DEBUGCAUSE] = cause; 91 env->sregs[EPC1 + level - 1] = pc; 92 env->sregs[EPS2 + level - 2] = env->sregs[PS]; 93 env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | PS_EXCM | 94 (level << PS_INTLEVEL_SHIFT); 95 HELPER(exception)(env, EXC_DEBUG); 96 } 97 98 #ifndef CONFIG_USER_ONLY 99 100 void HELPER(waiti)(CPUXtensaState *env, uint32_t pc, uint32_t intlevel) 101 { 102 CPUState *cpu = env_cpu(env); 103 104 env->pc = pc; 105 env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | 106 (intlevel << PS_INTLEVEL_SHIFT); 107 108 bql_lock(); 109 check_interrupts(env); 110 bql_unlock(); 111 112 if (env->pending_irq_level) { 113 cpu_loop_exit(cpu); 114 return; 115 } 116 117 cpu->halted = 1; 118 HELPER(exception)(env, EXCP_HLT); 119 } 120 121 void HELPER(check_interrupts)(CPUXtensaState *env) 122 { 123 bql_lock(); 124 check_interrupts(env); 125 bql_unlock(); 126 } 127 128 void HELPER(intset)(CPUXtensaState *env, uint32_t v) 129 { 130 qatomic_or(&env->sregs[INTSET], 131 v & env->config->inttype_mask[INTTYPE_SOFTWARE]); 132 } 133 134 static void intclear(CPUXtensaState *env, uint32_t v) 135 { 136 qatomic_and(&env->sregs[INTSET], ~v); 137 } 138 139 void HELPER(intclear)(CPUXtensaState *env, uint32_t v) 140 { 141 intclear(env, v & (env->config->inttype_mask[INTTYPE_SOFTWARE] | 142 env->config->inttype_mask[INTTYPE_EDGE])); 143 } 144 145 static uint32_t relocated_vector(CPUXtensaState *env, uint32_t vector) 146 { 147 if (xtensa_option_enabled(env->config, 148 XTENSA_OPTION_RELOCATABLE_VECTOR)) { 149 return vector - env->config->vecbase + env->sregs[VECBASE]; 150 } else { 151 return vector; 152 } 153 } 154 155 /*! 156 * Handle penging IRQ. 157 * For the high priority interrupt jump to the corresponding interrupt vector. 158 * For the level-1 interrupt convert it to either user, kernel or double 159 * exception with the 'level-1 interrupt' exception cause. 160 */ 161 static void handle_interrupt(CPUXtensaState *env) 162 { 163 int level = env->pending_irq_level; 164 165 if ((level > xtensa_get_cintlevel(env) && 166 level <= env->config->nlevel && 167 (env->config->level_mask[level] & 168 env->sregs[INTSET] & env->sregs[INTENABLE])) || 169 level == env->config->nmi_level) { 170 CPUState *cs = env_cpu(env); 171 172 if (level > 1) { 173 /* env->config->nlevel check should have ensured this */ 174 assert(level < ARRAY_SIZE(env->config->interrupt_vector)); 175 176 env->sregs[EPC1 + level - 1] = env->pc; 177 env->sregs[EPS2 + level - 2] = env->sregs[PS]; 178 env->sregs[PS] = 179 (env->sregs[PS] & ~PS_INTLEVEL) | level | PS_EXCM; 180 env->pc = relocated_vector(env, 181 env->config->interrupt_vector[level]); 182 if (level == env->config->nmi_level) { 183 intclear(env, env->config->inttype_mask[INTTYPE_NMI]); 184 } 185 } else { 186 env->sregs[EXCCAUSE] = LEVEL1_INTERRUPT_CAUSE; 187 188 if (env->sregs[PS] & PS_EXCM) { 189 if (env->config->ndepc) { 190 env->sregs[DEPC] = env->pc; 191 } else { 192 env->sregs[EPC1] = env->pc; 193 } 194 cs->exception_index = EXC_DOUBLE; 195 } else { 196 env->sregs[EPC1] = env->pc; 197 cs->exception_index = 198 (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL; 199 } 200 env->sregs[PS] |= PS_EXCM; 201 } 202 } 203 } 204 205 /* Called from cpu_handle_interrupt with BQL held */ 206 void xtensa_cpu_do_interrupt(CPUState *cs) 207 { 208 CPUXtensaState *env = cpu_env(cs); 209 210 if (cs->exception_index == EXC_IRQ) { 211 qemu_log_mask(CPU_LOG_INT, 212 "%s(EXC_IRQ) level = %d, cintlevel = %d, " 213 "pc = %08x, a0 = %08x, ps = %08x, " 214 "intset = %08x, intenable = %08x, " 215 "ccount = %08x\n", 216 __func__, env->pending_irq_level, 217 xtensa_get_cintlevel(env), 218 env->pc, env->regs[0], env->sregs[PS], 219 env->sregs[INTSET], env->sregs[INTENABLE], 220 env->sregs[CCOUNT]); 221 handle_interrupt(env); 222 } 223 224 switch (cs->exception_index) { 225 case EXC_WINDOW_OVERFLOW4: 226 case EXC_WINDOW_UNDERFLOW4: 227 case EXC_WINDOW_OVERFLOW8: 228 case EXC_WINDOW_UNDERFLOW8: 229 case EXC_WINDOW_OVERFLOW12: 230 case EXC_WINDOW_UNDERFLOW12: 231 case EXC_KERNEL: 232 case EXC_USER: 233 case EXC_DOUBLE: 234 case EXC_DEBUG: 235 qemu_log_mask(CPU_LOG_INT, "%s(%d) " 236 "pc = %08x, a0 = %08x, ps = %08x, ccount = %08x\n", 237 __func__, cs->exception_index, 238 env->pc, env->regs[0], env->sregs[PS], 239 env->sregs[CCOUNT]); 240 if (env->config->exception_vector[cs->exception_index]) { 241 uint32_t vector; 242 243 vector = env->config->exception_vector[cs->exception_index]; 244 env->pc = relocated_vector(env, vector); 245 } else { 246 qemu_log_mask(CPU_LOG_INT, 247 "%s(pc = %08x) bad exception_index: %d\n", 248 __func__, env->pc, cs->exception_index); 249 } 250 break; 251 252 case EXC_IRQ: 253 break; 254 255 default: 256 qemu_log("%s(pc = %08x) unknown exception_index: %d\n", 257 __func__, env->pc, cs->exception_index); 258 break; 259 } 260 check_interrupts(env); 261 } 262 263 bool xtensa_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 264 { 265 if (interrupt_request & CPU_INTERRUPT_HARD) { 266 cs->exception_index = EXC_IRQ; 267 xtensa_cpu_do_interrupt(cs); 268 return true; 269 } 270 return false; 271 } 272 273 #endif /* !CONFIG_USER_ONLY */ 274