1 /* 2 * Helpers for CWP and PSTATE handling 3 * 4 * Copyright (c) 2003-2005 Fabrice Bellard 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library 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 GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "qemu/osdep.h" 21 #include "qemu/main-loop.h" 22 #include "cpu.h" 23 #include "exec/exec-all.h" 24 #include "exec/helper-proto.h" 25 #include "trace.h" 26 27 static inline void memcpy32(target_ulong *dst, const target_ulong *src) 28 { 29 dst[0] = src[0]; 30 dst[1] = src[1]; 31 dst[2] = src[2]; 32 dst[3] = src[3]; 33 dst[4] = src[4]; 34 dst[5] = src[5]; 35 dst[6] = src[6]; 36 dst[7] = src[7]; 37 } 38 39 void cpu_set_cwp(CPUSPARCState *env, int new_cwp) 40 { 41 /* put the modified wrap registers at their proper location */ 42 if (env->cwp == env->nwindows - 1) { 43 memcpy32(env->regbase, env->regbase + env->nwindows * 16); 44 } 45 env->cwp = new_cwp; 46 47 /* put the wrap registers at their temporary location */ 48 if (new_cwp == env->nwindows - 1) { 49 memcpy32(env->regbase + env->nwindows * 16, env->regbase); 50 } 51 env->regwptr = env->regbase + (new_cwp * 16); 52 } 53 54 target_ulong cpu_get_psr(CPUSPARCState *env) 55 { 56 helper_compute_psr(env); 57 58 #if !defined(TARGET_SPARC64) 59 return env->version | (env->psr & PSR_ICC) | 60 (env->psref ? PSR_EF : 0) | 61 (env->psrpil << 8) | 62 (env->psrs ? PSR_S : 0) | 63 (env->psrps ? PSR_PS : 0) | 64 (env->psret ? PSR_ET : 0) | env->cwp; 65 #else 66 return env->psr & PSR_ICC; 67 #endif 68 } 69 70 void cpu_put_psr_raw(CPUSPARCState *env, target_ulong val) 71 { 72 env->psr = val & PSR_ICC; 73 #if !defined(TARGET_SPARC64) 74 env->psref = (val & PSR_EF) ? 1 : 0; 75 env->psrpil = (val & PSR_PIL) >> 8; 76 env->psrs = (val & PSR_S) ? 1 : 0; 77 env->psrps = (val & PSR_PS) ? 1 : 0; 78 env->psret = (val & PSR_ET) ? 1 : 0; 79 #endif 80 env->cc_op = CC_OP_FLAGS; 81 #if !defined(TARGET_SPARC64) 82 cpu_set_cwp(env, val & PSR_CWP); 83 #endif 84 } 85 86 /* Called with BQL held */ 87 void cpu_put_psr(CPUSPARCState *env, target_ulong val) 88 { 89 cpu_put_psr_raw(env, val); 90 #if ((!defined(TARGET_SPARC64)) && !defined(CONFIG_USER_ONLY)) 91 cpu_check_irqs(env); 92 #endif 93 } 94 95 int cpu_cwp_inc(CPUSPARCState *env, int cwp) 96 { 97 if (unlikely(cwp >= env->nwindows)) { 98 cwp -= env->nwindows; 99 } 100 return cwp; 101 } 102 103 int cpu_cwp_dec(CPUSPARCState *env, int cwp) 104 { 105 if (unlikely(cwp < 0)) { 106 cwp += env->nwindows; 107 } 108 return cwp; 109 } 110 111 #ifndef TARGET_SPARC64 112 void helper_rett(CPUSPARCState *env) 113 { 114 unsigned int cwp; 115 116 if (env->psret == 1) { 117 cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC()); 118 } 119 120 env->psret = 1; 121 cwp = cpu_cwp_inc(env, env->cwp + 1) ; 122 if (env->wim & (1 << cwp)) { 123 cpu_raise_exception_ra(env, TT_WIN_UNF, GETPC()); 124 } 125 cpu_set_cwp(env, cwp); 126 env->psrs = env->psrps; 127 } 128 129 /* XXX: use another pointer for %iN registers to avoid slow wrapping 130 handling ? */ 131 void helper_save(CPUSPARCState *env) 132 { 133 uint32_t cwp; 134 135 cwp = cpu_cwp_dec(env, env->cwp - 1); 136 if (env->wim & (1 << cwp)) { 137 cpu_raise_exception_ra(env, TT_WIN_OVF, GETPC()); 138 } 139 cpu_set_cwp(env, cwp); 140 } 141 142 void helper_restore(CPUSPARCState *env) 143 { 144 uint32_t cwp; 145 146 cwp = cpu_cwp_inc(env, env->cwp + 1); 147 if (env->wim & (1 << cwp)) { 148 cpu_raise_exception_ra(env, TT_WIN_UNF, GETPC()); 149 } 150 cpu_set_cwp(env, cwp); 151 } 152 153 void helper_wrpsr(CPUSPARCState *env, target_ulong new_psr) 154 { 155 if ((new_psr & PSR_CWP) >= env->nwindows) { 156 cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC()); 157 } else { 158 /* cpu_put_psr may trigger interrupts, hence BQL */ 159 qemu_mutex_lock_iothread(); 160 cpu_put_psr(env, new_psr); 161 qemu_mutex_unlock_iothread(); 162 } 163 } 164 165 target_ulong helper_rdpsr(CPUSPARCState *env) 166 { 167 return cpu_get_psr(env); 168 } 169 170 #else 171 /* XXX: use another pointer for %iN registers to avoid slow wrapping 172 handling ? */ 173 void helper_save(CPUSPARCState *env) 174 { 175 uint32_t cwp; 176 177 cwp = cpu_cwp_dec(env, env->cwp - 1); 178 if (env->cansave == 0) { 179 int tt = TT_SPILL | (env->otherwin != 0 180 ? (TT_WOTHER | ((env->wstate & 0x38) >> 1)) 181 : ((env->wstate & 0x7) << 2)); 182 cpu_raise_exception_ra(env, tt, GETPC()); 183 } else { 184 if (env->cleanwin - env->canrestore == 0) { 185 /* XXX Clean windows without trap */ 186 cpu_raise_exception_ra(env, TT_CLRWIN, GETPC()); 187 } else { 188 env->cansave--; 189 env->canrestore++; 190 cpu_set_cwp(env, cwp); 191 } 192 } 193 } 194 195 void helper_restore(CPUSPARCState *env) 196 { 197 uint32_t cwp; 198 199 cwp = cpu_cwp_inc(env, env->cwp + 1); 200 if (env->canrestore == 0) { 201 int tt = TT_FILL | (env->otherwin != 0 202 ? (TT_WOTHER | ((env->wstate & 0x38) >> 1)) 203 : ((env->wstate & 0x7) << 2)); 204 cpu_raise_exception_ra(env, tt, GETPC()); 205 } else { 206 env->cansave++; 207 env->canrestore--; 208 cpu_set_cwp(env, cwp); 209 } 210 } 211 212 void helper_flushw(CPUSPARCState *env) 213 { 214 if (env->cansave != env->nwindows - 2) { 215 int tt = TT_SPILL | (env->otherwin != 0 216 ? (TT_WOTHER | ((env->wstate & 0x38) >> 1)) 217 : ((env->wstate & 0x7) << 2)); 218 cpu_raise_exception_ra(env, tt, GETPC()); 219 } 220 } 221 222 void helper_saved(CPUSPARCState *env) 223 { 224 env->cansave++; 225 if (env->otherwin == 0) { 226 env->canrestore--; 227 } else { 228 env->otherwin--; 229 } 230 } 231 232 void helper_restored(CPUSPARCState *env) 233 { 234 env->canrestore++; 235 if (env->cleanwin < env->nwindows - 1) { 236 env->cleanwin++; 237 } 238 if (env->otherwin == 0) { 239 env->cansave--; 240 } else { 241 env->otherwin--; 242 } 243 } 244 245 target_ulong cpu_get_ccr(CPUSPARCState *env) 246 { 247 target_ulong psr; 248 249 psr = cpu_get_psr(env); 250 251 return ((env->xcc >> 20) << 4) | ((psr & PSR_ICC) >> 20); 252 } 253 254 void cpu_put_ccr(CPUSPARCState *env, target_ulong val) 255 { 256 env->xcc = (val >> 4) << 20; 257 env->psr = (val & 0xf) << 20; 258 CC_OP = CC_OP_FLAGS; 259 } 260 261 target_ulong cpu_get_cwp64(CPUSPARCState *env) 262 { 263 return env->nwindows - 1 - env->cwp; 264 } 265 266 void cpu_put_cwp64(CPUSPARCState *env, int cwp) 267 { 268 if (unlikely(cwp >= env->nwindows || cwp < 0)) { 269 cwp %= env->nwindows; 270 } 271 cpu_set_cwp(env, env->nwindows - 1 - cwp); 272 } 273 274 target_ulong helper_rdccr(CPUSPARCState *env) 275 { 276 return cpu_get_ccr(env); 277 } 278 279 void helper_wrccr(CPUSPARCState *env, target_ulong new_ccr) 280 { 281 cpu_put_ccr(env, new_ccr); 282 } 283 284 /* CWP handling is reversed in V9, but we still use the V8 register 285 order. */ 286 target_ulong helper_rdcwp(CPUSPARCState *env) 287 { 288 return cpu_get_cwp64(env); 289 } 290 291 void helper_wrcwp(CPUSPARCState *env, target_ulong new_cwp) 292 { 293 cpu_put_cwp64(env, new_cwp); 294 } 295 296 static inline uint64_t *get_gregset(CPUSPARCState *env, uint32_t pstate) 297 { 298 if (env->def.features & CPU_FEATURE_GL) { 299 return env->glregs + (env->gl & 7) * 8; 300 } 301 302 switch (pstate) { 303 default: 304 trace_win_helper_gregset_error(pstate); 305 /* pass through to normal set of global registers */ 306 case 0: 307 return env->bgregs; 308 case PS_AG: 309 return env->agregs; 310 case PS_MG: 311 return env->mgregs; 312 case PS_IG: 313 return env->igregs; 314 } 315 } 316 317 static inline uint64_t *get_gl_gregset(CPUSPARCState *env, uint32_t gl) 318 { 319 return env->glregs + (gl & 7) * 8; 320 } 321 322 /* Switch global register bank */ 323 void cpu_gl_switch_gregs(CPUSPARCState *env, uint32_t new_gl) 324 { 325 uint64_t *src, *dst; 326 src = get_gl_gregset(env, new_gl); 327 dst = get_gl_gregset(env, env->gl); 328 329 if (src != dst) { 330 memcpy32(dst, env->gregs); 331 memcpy32(env->gregs, src); 332 } 333 } 334 335 void helper_wrgl(CPUSPARCState *env, target_ulong new_gl) 336 { 337 cpu_gl_switch_gregs(env, new_gl & 7); 338 env->gl = new_gl & 7; 339 } 340 341 void cpu_change_pstate(CPUSPARCState *env, uint32_t new_pstate) 342 { 343 uint32_t pstate_regs, new_pstate_regs; 344 uint64_t *src, *dst; 345 346 if (env->def.features & CPU_FEATURE_GL) { 347 /* PS_AG, IG and MG are not implemented in this case */ 348 new_pstate &= ~(PS_AG | PS_IG | PS_MG); 349 env->pstate = new_pstate; 350 return; 351 } 352 353 pstate_regs = env->pstate & 0xc01; 354 new_pstate_regs = new_pstate & 0xc01; 355 356 if (new_pstate_regs != pstate_regs) { 357 trace_win_helper_switch_pstate(pstate_regs, new_pstate_regs); 358 359 /* Switch global register bank */ 360 src = get_gregset(env, new_pstate_regs); 361 dst = get_gregset(env, pstate_regs); 362 memcpy32(dst, env->gregs); 363 memcpy32(env->gregs, src); 364 } else { 365 trace_win_helper_no_switch_pstate(new_pstate_regs); 366 } 367 env->pstate = new_pstate; 368 } 369 370 void helper_wrpstate(CPUSPARCState *env, target_ulong new_state) 371 { 372 cpu_change_pstate(env, new_state & 0xf3f); 373 374 #if !defined(CONFIG_USER_ONLY) 375 if (cpu_interrupts_enabled(env)) { 376 qemu_mutex_lock_iothread(); 377 cpu_check_irqs(env); 378 qemu_mutex_unlock_iothread(); 379 } 380 #endif 381 } 382 383 void helper_wrpil(CPUSPARCState *env, target_ulong new_pil) 384 { 385 #if !defined(CONFIG_USER_ONLY) 386 trace_win_helper_wrpil(env->psrpil, (uint32_t)new_pil); 387 388 env->psrpil = new_pil; 389 390 if (cpu_interrupts_enabled(env)) { 391 qemu_mutex_lock_iothread(); 392 cpu_check_irqs(env); 393 qemu_mutex_unlock_iothread(); 394 } 395 #endif 396 } 397 398 void helper_done(CPUSPARCState *env) 399 { 400 trap_state *tsptr = cpu_tsptr(env); 401 402 env->pc = tsptr->tnpc; 403 env->npc = tsptr->tnpc + 4; 404 cpu_put_ccr(env, tsptr->tstate >> 32); 405 env->asi = (tsptr->tstate >> 24) & 0xff; 406 cpu_change_pstate(env, (tsptr->tstate >> 8) & 0xf3f); 407 cpu_put_cwp64(env, tsptr->tstate & 0xff); 408 if (cpu_has_hypervisor(env)) { 409 uint32_t new_gl = (tsptr->tstate >> 40) & 7; 410 env->hpstate = env->htstate[env->tl]; 411 cpu_gl_switch_gregs(env, new_gl); 412 env->gl = new_gl; 413 } 414 env->tl--; 415 416 trace_win_helper_done(env->tl); 417 418 #if !defined(CONFIG_USER_ONLY) 419 if (cpu_interrupts_enabled(env)) { 420 qemu_mutex_lock_iothread(); 421 cpu_check_irqs(env); 422 qemu_mutex_unlock_iothread(); 423 } 424 #endif 425 } 426 427 void helper_retry(CPUSPARCState *env) 428 { 429 trap_state *tsptr = cpu_tsptr(env); 430 431 env->pc = tsptr->tpc; 432 env->npc = tsptr->tnpc; 433 cpu_put_ccr(env, tsptr->tstate >> 32); 434 env->asi = (tsptr->tstate >> 24) & 0xff; 435 cpu_change_pstate(env, (tsptr->tstate >> 8) & 0xf3f); 436 cpu_put_cwp64(env, tsptr->tstate & 0xff); 437 if (cpu_has_hypervisor(env)) { 438 uint32_t new_gl = (tsptr->tstate >> 40) & 7; 439 env->hpstate = env->htstate[env->tl]; 440 cpu_gl_switch_gregs(env, new_gl); 441 env->gl = new_gl; 442 } 443 env->tl--; 444 445 trace_win_helper_retry(env->tl); 446 447 #if !defined(CONFIG_USER_ONLY) 448 if (cpu_interrupts_enabled(env)) { 449 qemu_mutex_lock_iothread(); 450 cpu_check_irqs(env); 451 qemu_mutex_unlock_iothread(); 452 } 453 #endif 454 } 455 #endif 456