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