1 /* 2 * qemu user cpu loop 3 * 4 * Copyright (c) 2003-2008 Fabrice Bellard 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "qemu/osdep.h" 21 #include "qemu-common.h" 22 #include "qemu.h" 23 #include "user-internals.h" 24 #include "cpu_loop-common.h" 25 #include "signal-common.h" 26 27 #define SPARC64_STACK_BIAS 2047 28 29 //#define DEBUG_WIN 30 31 /* WARNING: dealing with register windows _is_ complicated. More info 32 can be found at http://www.sics.se/~psm/sparcstack.html */ 33 static inline int get_reg_index(CPUSPARCState *env, int cwp, int index) 34 { 35 index = (index + cwp * 16) % (16 * env->nwindows); 36 /* wrap handling : if cwp is on the last window, then we use the 37 registers 'after' the end */ 38 if (index < 8 && env->cwp == env->nwindows - 1) 39 index += 16 * env->nwindows; 40 return index; 41 } 42 43 /* save the register window 'cwp1' */ 44 static inline void save_window_offset(CPUSPARCState *env, int cwp1) 45 { 46 unsigned int i; 47 abi_ulong sp_ptr; 48 49 sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; 50 #ifdef TARGET_SPARC64 51 if (sp_ptr & 3) 52 sp_ptr += SPARC64_STACK_BIAS; 53 #endif 54 #if defined(DEBUG_WIN) 55 printf("win_overflow: sp_ptr=0x" TARGET_ABI_FMT_lx " save_cwp=%d\n", 56 sp_ptr, cwp1); 57 #endif 58 for(i = 0; i < 16; i++) { 59 /* FIXME - what to do if put_user() fails? */ 60 put_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); 61 sp_ptr += sizeof(abi_ulong); 62 } 63 } 64 65 static void save_window(CPUSPARCState *env) 66 { 67 #ifndef TARGET_SPARC64 68 unsigned int new_wim; 69 new_wim = ((env->wim >> 1) | (env->wim << (env->nwindows - 1))) & 70 ((1LL << env->nwindows) - 1); 71 save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); 72 env->wim = new_wim; 73 #else 74 /* 75 * cansave is zero if the spill trap handler is triggered by `save` and 76 * nonzero if triggered by a `flushw` 77 */ 78 save_window_offset(env, cpu_cwp_dec(env, env->cwp - env->cansave - 2)); 79 env->cansave++; 80 env->canrestore--; 81 #endif 82 } 83 84 static void restore_window(CPUSPARCState *env) 85 { 86 #ifndef TARGET_SPARC64 87 unsigned int new_wim; 88 #endif 89 unsigned int i, cwp1; 90 abi_ulong sp_ptr; 91 92 #ifndef TARGET_SPARC64 93 new_wim = ((env->wim << 1) | (env->wim >> (env->nwindows - 1))) & 94 ((1LL << env->nwindows) - 1); 95 #endif 96 97 /* restore the invalid window */ 98 cwp1 = cpu_cwp_inc(env, env->cwp + 1); 99 sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; 100 #ifdef TARGET_SPARC64 101 if (sp_ptr & 3) 102 sp_ptr += SPARC64_STACK_BIAS; 103 #endif 104 #if defined(DEBUG_WIN) 105 printf("win_underflow: sp_ptr=0x" TARGET_ABI_FMT_lx " load_cwp=%d\n", 106 sp_ptr, cwp1); 107 #endif 108 for(i = 0; i < 16; i++) { 109 /* FIXME - what to do if get_user() fails? */ 110 get_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); 111 sp_ptr += sizeof(abi_ulong); 112 } 113 #ifdef TARGET_SPARC64 114 env->canrestore++; 115 if (env->cleanwin < env->nwindows - 1) 116 env->cleanwin++; 117 env->cansave--; 118 #else 119 env->wim = new_wim; 120 #endif 121 } 122 123 static void flush_windows(CPUSPARCState *env) 124 { 125 int offset, cwp1; 126 127 offset = 1; 128 for(;;) { 129 /* if restore would invoke restore_window(), then we can stop */ 130 cwp1 = cpu_cwp_inc(env, env->cwp + offset); 131 #ifndef TARGET_SPARC64 132 if (env->wim & (1 << cwp1)) 133 break; 134 #else 135 if (env->canrestore == 0) 136 break; 137 env->cansave++; 138 env->canrestore--; 139 #endif 140 save_window_offset(env, cwp1); 141 offset++; 142 } 143 cwp1 = cpu_cwp_inc(env, env->cwp + 1); 144 #ifndef TARGET_SPARC64 145 /* set wim so that restore will reload the registers */ 146 env->wim = 1 << cwp1; 147 #endif 148 #if defined(DEBUG_WIN) 149 printf("flush_windows: nb=%d\n", offset - 1); 150 #endif 151 } 152 153 void cpu_loop (CPUSPARCState *env) 154 { 155 CPUState *cs = env_cpu(env); 156 int trapnr; 157 abi_long ret; 158 target_siginfo_t info; 159 160 while (1) { 161 cpu_exec_start(cs); 162 trapnr = cpu_exec(cs); 163 cpu_exec_end(cs); 164 process_queued_cpu_work(cs); 165 166 /* Compute PSR before exposing state. */ 167 if (env->cc_op != CC_OP_FLAGS) { 168 cpu_get_psr(env); 169 } 170 171 switch (trapnr) { 172 #ifndef TARGET_SPARC64 173 case 0x88: 174 case 0x90: 175 #else 176 case 0x110: 177 case 0x16d: 178 #endif 179 ret = do_syscall (env, env->gregs[1], 180 env->regwptr[0], env->regwptr[1], 181 env->regwptr[2], env->regwptr[3], 182 env->regwptr[4], env->regwptr[5], 183 0, 0); 184 if (ret == -TARGET_ERESTARTSYS || ret == -TARGET_QEMU_ESIGRETURN) { 185 break; 186 } 187 if ((abi_ulong)ret >= (abi_ulong)(-515)) { 188 #if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) 189 env->xcc |= PSR_CARRY; 190 #else 191 env->psr |= PSR_CARRY; 192 #endif 193 ret = -ret; 194 } else { 195 #if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) 196 env->xcc &= ~PSR_CARRY; 197 #else 198 env->psr &= ~PSR_CARRY; 199 #endif 200 } 201 env->regwptr[0] = ret; 202 /* next instruction */ 203 env->pc = env->npc; 204 env->npc = env->npc + 4; 205 break; 206 case 0x83: /* flush windows */ 207 #ifdef TARGET_ABI32 208 case 0x103: 209 #endif 210 flush_windows(env); 211 /* next instruction */ 212 env->pc = env->npc; 213 env->npc = env->npc + 4; 214 break; 215 #ifndef TARGET_SPARC64 216 case TT_WIN_OVF: /* window overflow */ 217 save_window(env); 218 break; 219 case TT_WIN_UNF: /* window underflow */ 220 restore_window(env); 221 break; 222 case TT_TFAULT: 223 case TT_DFAULT: 224 { 225 info.si_signo = TARGET_SIGSEGV; 226 info.si_errno = 0; 227 /* XXX: check env->error_code */ 228 info.si_code = TARGET_SEGV_MAPERR; 229 info._sifields._sigfault._addr = env->mmuregs[4]; 230 queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); 231 } 232 break; 233 #else 234 case TT_SPILL: /* window overflow */ 235 save_window(env); 236 break; 237 case TT_FILL: /* window underflow */ 238 restore_window(env); 239 break; 240 case TT_TFAULT: 241 case TT_DFAULT: 242 { 243 info.si_signo = TARGET_SIGSEGV; 244 info.si_errno = 0; 245 /* XXX: check env->error_code */ 246 info.si_code = TARGET_SEGV_MAPERR; 247 if (trapnr == TT_DFAULT) 248 info._sifields._sigfault._addr = env->dmmu.mmuregs[4]; 249 else 250 info._sifields._sigfault._addr = cpu_tsptr(env)->tpc; 251 queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); 252 } 253 break; 254 #ifndef TARGET_ABI32 255 case 0x16e: 256 flush_windows(env); 257 sparc64_get_context(env); 258 break; 259 case 0x16f: 260 flush_windows(env); 261 sparc64_set_context(env); 262 break; 263 #endif 264 #endif 265 case EXCP_INTERRUPT: 266 /* just indicate that signals should be handled asap */ 267 break; 268 case TT_ILL_INSN: 269 { 270 info.si_signo = TARGET_SIGILL; 271 info.si_errno = 0; 272 info.si_code = TARGET_ILL_ILLOPC; 273 info._sifields._sigfault._addr = env->pc; 274 queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); 275 } 276 break; 277 case EXCP_DEBUG: 278 info.si_signo = TARGET_SIGTRAP; 279 info.si_errno = 0; 280 info.si_code = TARGET_TRAP_BRKPT; 281 queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); 282 break; 283 case EXCP_ATOMIC: 284 cpu_exec_step_atomic(cs); 285 break; 286 default: 287 fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); 288 cpu_dump_state(cs, stderr, 0); 289 exit(EXIT_FAILURE); 290 } 291 process_pending_signals (env); 292 } 293 } 294 295 void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) 296 { 297 int i; 298 env->pc = regs->pc; 299 env->npc = regs->npc; 300 env->y = regs->y; 301 for(i = 0; i < 8; i++) 302 env->gregs[i] = regs->u_regs[i]; 303 for(i = 0; i < 8; i++) 304 env->regwptr[i] = regs->u_regs[i + 8]; 305 } 306