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/main-loop.h" 30 #include "cpu.h" 31 #include "exec/helper-proto.h" 32 #include "qemu/host-utils.h" 33 #include "exec/exec-all.h" 34 35 void HELPER(exception)(CPUXtensaState *env, uint32_t excp) 36 { 37 CPUState *cs = env_cpu(env); 38 39 cs->exception_index = excp; 40 if (excp == EXCP_YIELD) { 41 env->yield_needed = 0; 42 } 43 cpu_loop_exit(cs); 44 } 45 46 void HELPER(exception_cause)(CPUXtensaState *env, uint32_t pc, uint32_t cause) 47 { 48 uint32_t vector; 49 50 env->pc = pc; 51 if (env->sregs[PS] & PS_EXCM) { 52 if (env->config->ndepc) { 53 env->sregs[DEPC] = pc; 54 } else { 55 env->sregs[EPC1] = pc; 56 } 57 vector = EXC_DOUBLE; 58 } else { 59 env->sregs[EPC1] = pc; 60 vector = (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL; 61 } 62 63 env->sregs[EXCCAUSE] = cause; 64 env->sregs[PS] |= PS_EXCM; 65 66 HELPER(exception)(env, vector); 67 } 68 69 void HELPER(exception_cause_vaddr)(CPUXtensaState *env, 70 uint32_t pc, uint32_t cause, uint32_t vaddr) 71 { 72 env->sregs[EXCVADDR] = vaddr; 73 HELPER(exception_cause)(env, pc, cause); 74 } 75 76 void debug_exception_env(CPUXtensaState *env, uint32_t cause) 77 { 78 if (xtensa_get_cintlevel(env) < env->config->debug_level) { 79 HELPER(debug_exception)(env, env->pc, cause); 80 } 81 } 82 83 void HELPER(debug_exception)(CPUXtensaState *env, uint32_t pc, uint32_t cause) 84 { 85 unsigned level = env->config->debug_level; 86 87 env->pc = pc; 88 env->sregs[DEBUGCAUSE] = cause; 89 env->sregs[EPC1 + level - 1] = pc; 90 env->sregs[EPS2 + level - 2] = env->sregs[PS]; 91 env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | PS_EXCM | 92 (level << PS_INTLEVEL_SHIFT); 93 HELPER(exception)(env, EXC_DEBUG); 94 } 95 96 #ifndef CONFIG_USER_ONLY 97 98 void HELPER(waiti)(CPUXtensaState *env, uint32_t pc, uint32_t intlevel) 99 { 100 CPUState *cpu = env_cpu(env); 101 102 env->pc = pc; 103 env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | 104 (intlevel << PS_INTLEVEL_SHIFT); 105 106 qemu_mutex_lock_iothread(); 107 check_interrupts(env); 108 qemu_mutex_unlock_iothread(); 109 110 if (env->pending_irq_level) { 111 cpu_loop_exit(cpu); 112 return; 113 } 114 115 cpu->halted = 1; 116 HELPER(exception)(env, EXCP_HLT); 117 } 118 119 void HELPER(check_interrupts)(CPUXtensaState *env) 120 { 121 qemu_mutex_lock_iothread(); 122 check_interrupts(env); 123 qemu_mutex_unlock_iothread(); 124 } 125 126 void HELPER(intset)(CPUXtensaState *env, uint32_t v) 127 { 128 qatomic_or(&env->sregs[INTSET], 129 v & env->config->inttype_mask[INTTYPE_SOFTWARE]); 130 } 131 132 static void intclear(CPUXtensaState *env, uint32_t v) 133 { 134 qatomic_and(&env->sregs[INTSET], ~v); 135 } 136 137 void HELPER(intclear)(CPUXtensaState *env, uint32_t v) 138 { 139 intclear(env, v & (env->config->inttype_mask[INTTYPE_SOFTWARE] | 140 env->config->inttype_mask[INTTYPE_EDGE])); 141 } 142 143 static uint32_t relocated_vector(CPUXtensaState *env, uint32_t vector) 144 { 145 if (xtensa_option_enabled(env->config, 146 XTENSA_OPTION_RELOCATABLE_VECTOR)) { 147 return vector - env->config->vecbase + env->sregs[VECBASE]; 148 } else { 149 return vector; 150 } 151 } 152 153 /*! 154 * Handle penging IRQ. 155 * For the high priority interrupt jump to the corresponding interrupt vector. 156 * For the level-1 interrupt convert it to either user, kernel or double 157 * exception with the 'level-1 interrupt' exception cause. 158 */ 159 static void handle_interrupt(CPUXtensaState *env) 160 { 161 int level = env->pending_irq_level; 162 163 if ((level > xtensa_get_cintlevel(env) && 164 level <= env->config->nlevel && 165 (env->config->level_mask[level] & 166 env->sregs[INTSET] & env->sregs[INTENABLE])) || 167 level == env->config->nmi_level) { 168 CPUState *cs = env_cpu(env); 169 170 if (level > 1) { 171 env->sregs[EPC1 + level - 1] = env->pc; 172 env->sregs[EPS2 + level - 2] = env->sregs[PS]; 173 env->sregs[PS] = 174 (env->sregs[PS] & ~PS_INTLEVEL) | level | PS_EXCM; 175 env->pc = relocated_vector(env, 176 env->config->interrupt_vector[level]); 177 if (level == env->config->nmi_level) { 178 intclear(env, env->config->inttype_mask[INTTYPE_NMI]); 179 } 180 } else { 181 env->sregs[EXCCAUSE] = LEVEL1_INTERRUPT_CAUSE; 182 183 if (env->sregs[PS] & PS_EXCM) { 184 if (env->config->ndepc) { 185 env->sregs[DEPC] = env->pc; 186 } else { 187 env->sregs[EPC1] = env->pc; 188 } 189 cs->exception_index = EXC_DOUBLE; 190 } else { 191 env->sregs[EPC1] = env->pc; 192 cs->exception_index = 193 (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL; 194 } 195 env->sregs[PS] |= PS_EXCM; 196 } 197 } 198 } 199 200 /* Called from cpu_handle_interrupt with BQL held */ 201 void xtensa_cpu_do_interrupt(CPUState *cs) 202 { 203 XtensaCPU *cpu = XTENSA_CPU(cs); 204 CPUXtensaState *env = &cpu->env; 205 206 if (cs->exception_index == EXC_IRQ) { 207 qemu_log_mask(CPU_LOG_INT, 208 "%s(EXC_IRQ) level = %d, cintlevel = %d, " 209 "pc = %08x, a0 = %08x, ps = %08x, " 210 "intset = %08x, intenable = %08x, " 211 "ccount = %08x\n", 212 __func__, env->pending_irq_level, 213 xtensa_get_cintlevel(env), 214 env->pc, env->regs[0], env->sregs[PS], 215 env->sregs[INTSET], env->sregs[INTENABLE], 216 env->sregs[CCOUNT]); 217 handle_interrupt(env); 218 } 219 220 switch (cs->exception_index) { 221 case EXC_WINDOW_OVERFLOW4: 222 case EXC_WINDOW_UNDERFLOW4: 223 case EXC_WINDOW_OVERFLOW8: 224 case EXC_WINDOW_UNDERFLOW8: 225 case EXC_WINDOW_OVERFLOW12: 226 case EXC_WINDOW_UNDERFLOW12: 227 case EXC_KERNEL: 228 case EXC_USER: 229 case EXC_DOUBLE: 230 case EXC_DEBUG: 231 qemu_log_mask(CPU_LOG_INT, "%s(%d) " 232 "pc = %08x, a0 = %08x, ps = %08x, ccount = %08x\n", 233 __func__, cs->exception_index, 234 env->pc, env->regs[0], env->sregs[PS], 235 env->sregs[CCOUNT]); 236 if (env->config->exception_vector[cs->exception_index]) { 237 uint32_t vector; 238 239 vector = env->config->exception_vector[cs->exception_index]; 240 env->pc = relocated_vector(env, vector); 241 } else { 242 qemu_log_mask(CPU_LOG_INT, 243 "%s(pc = %08x) bad exception_index: %d\n", 244 __func__, env->pc, cs->exception_index); 245 } 246 break; 247 248 case EXC_IRQ: 249 break; 250 251 default: 252 qemu_log("%s(pc = %08x) unknown exception_index: %d\n", 253 __func__, env->pc, cs->exception_index); 254 break; 255 } 256 check_interrupts(env); 257 } 258 259 bool xtensa_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 260 { 261 if (interrupt_request & CPU_INTERRUPT_HARD) { 262 cs->exception_index = EXC_IRQ; 263 xtensa_cpu_do_interrupt(cs); 264 return true; 265 } 266 return false; 267 } 268 269 #endif /* !CONFIG_USER_ONLY */ 270