xref: /openbmc/qemu/target/sparc/win_helper.c (revision 9468484fe904ab4691de6d9c34616667f377ceac)
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.1 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  
memcpy32(target_ulong * dst,const target_ulong * src)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  
cpu_set_cwp(CPUSPARCState * env,int new_cwp)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  
cpu_get_psr(CPUSPARCState * env)54  target_ulong cpu_get_psr(CPUSPARCState *env)
55  {
56      target_ulong icc = 0;
57  
58      icc |= ((int32_t)env->cc_N < 0) << PSR_NEG_SHIFT;
59      icc |= ((int32_t)env->cc_V < 0) << PSR_OVF_SHIFT;
60      icc |= ((int32_t)env->icc_Z == 0) << PSR_ZERO_SHIFT;
61      if (TARGET_LONG_BITS == 64) {
62          icc |= extract64(env->icc_C, 32, 1) << PSR_CARRY_SHIFT;
63      } else {
64          icc |= env->icc_C << PSR_CARRY_SHIFT;
65      }
66  
67  #if !defined(TARGET_SPARC64)
68      return env->version | icc |
69          (env->psref ? PSR_EF : 0) |
70          (env->psrpil << 8) |
71          (env->psrs ? PSR_S : 0) |
72          (env->psrps ? PSR_PS : 0) |
73          (env->psret ? PSR_ET : 0) | env->cwp;
74  #else
75      return icc;
76  #endif
77  }
78  
cpu_put_psr_icc(CPUSPARCState * env,target_ulong val)79  void cpu_put_psr_icc(CPUSPARCState *env, target_ulong val)
80  {
81      if (TARGET_LONG_BITS == 64) {
82          /* Do not clobber xcc.[NV] */
83          env->cc_N = deposit64(env->cc_N, 0, 32, -(val & PSR_NEG));
84          env->cc_V = deposit64(env->cc_V, 0, 32, -(val & PSR_OVF));
85          env->icc_C = -(val & PSR_CARRY);
86      } else {
87          env->cc_N = -(val & PSR_NEG);
88          env->cc_V = -(val & PSR_OVF);
89          env->icc_C = (val >> PSR_CARRY_SHIFT) & 1;
90      }
91      env->icc_Z = ~val & PSR_ZERO;
92  }
93  
cpu_put_psr_raw(CPUSPARCState * env,target_ulong val)94  void cpu_put_psr_raw(CPUSPARCState *env, target_ulong val)
95  {
96      cpu_put_psr_icc(env, val);
97  #if !defined(TARGET_SPARC64)
98      env->psref = (val & PSR_EF) ? 1 : 0;
99      env->psrpil = (val & PSR_PIL) >> 8;
100      env->psrs = (val & PSR_S) ? 1 : 0;
101      env->psrps = (val & PSR_PS) ? 1 : 0;
102      env->psret = (val & PSR_ET) ? 1 : 0;
103  #endif
104  #if !defined(TARGET_SPARC64)
105      cpu_set_cwp(env, val & PSR_CWP);
106  #endif
107  }
108  
109  /* Called with BQL held */
cpu_put_psr(CPUSPARCState * env,target_ulong val)110  void cpu_put_psr(CPUSPARCState *env, target_ulong val)
111  {
112      cpu_put_psr_raw(env, val);
113  #if ((!defined(TARGET_SPARC64)) && !defined(CONFIG_USER_ONLY))
114      cpu_check_irqs(env);
115  #endif
116  }
117  
cpu_cwp_inc(CPUSPARCState * env,int cwp)118  int cpu_cwp_inc(CPUSPARCState *env, int cwp)
119  {
120      if (unlikely(cwp >= env->nwindows)) {
121          cwp -= env->nwindows;
122      }
123      return cwp;
124  }
125  
cpu_cwp_dec(CPUSPARCState * env,int cwp)126  int cpu_cwp_dec(CPUSPARCState *env, int cwp)
127  {
128      if (unlikely(cwp < 0)) {
129          cwp += env->nwindows;
130      }
131      return cwp;
132  }
133  
134  #ifndef TARGET_SPARC64
helper_rett(CPUSPARCState * env)135  void helper_rett(CPUSPARCState *env)
136  {
137      unsigned int cwp;
138  
139      if (env->psret == 1) {
140          cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC());
141      }
142  
143      env->psret = 1;
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      env->psrs = env->psrps;
150  }
151  
152  /* XXX: use another pointer for %iN registers to avoid slow wrapping
153     handling ? */
helper_save(CPUSPARCState * env)154  void helper_save(CPUSPARCState *env)
155  {
156      uint32_t cwp;
157  
158      cwp = cpu_cwp_dec(env, env->cwp - 1);
159      if (env->wim & (1 << cwp)) {
160          cpu_raise_exception_ra(env, TT_WIN_OVF, GETPC());
161      }
162      cpu_set_cwp(env, cwp);
163  }
164  
helper_restore(CPUSPARCState * env)165  void helper_restore(CPUSPARCState *env)
166  {
167      uint32_t cwp;
168  
169      cwp = cpu_cwp_inc(env, env->cwp + 1);
170      if (env->wim & (1 << cwp)) {
171          cpu_raise_exception_ra(env, TT_WIN_UNF, GETPC());
172      }
173      cpu_set_cwp(env, cwp);
174  }
175  
helper_wrpsr(CPUSPARCState * env,target_ulong new_psr)176  void helper_wrpsr(CPUSPARCState *env, target_ulong new_psr)
177  {
178      if ((new_psr & PSR_CWP) >= env->nwindows) {
179          cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC());
180      } else {
181          /* cpu_put_psr may trigger interrupts, hence BQL */
182          bql_lock();
183          cpu_put_psr(env, new_psr);
184          bql_unlock();
185      }
186  }
187  
helper_rdpsr(CPUSPARCState * env)188  target_ulong helper_rdpsr(CPUSPARCState *env)
189  {
190      return cpu_get_psr(env);
191  }
192  
193  #else
194  /* XXX: use another pointer for %iN registers to avoid slow wrapping
195     handling ? */
helper_save(CPUSPARCState * env)196  void helper_save(CPUSPARCState *env)
197  {
198      uint32_t cwp;
199  
200      cwp = cpu_cwp_dec(env, env->cwp - 1);
201      if (env->cansave == 0) {
202          int tt = TT_SPILL | (env->otherwin != 0
203                               ? (TT_WOTHER | ((env->wstate & 0x38) >> 1))
204                               : ((env->wstate & 0x7) << 2));
205          cpu_raise_exception_ra(env, tt, GETPC());
206      } else {
207          if (env->cleanwin - env->canrestore == 0) {
208              /* XXX Clean windows without trap */
209              cpu_raise_exception_ra(env, TT_CLRWIN, GETPC());
210          } else {
211              env->cansave--;
212              env->canrestore++;
213              cpu_set_cwp(env, cwp);
214          }
215      }
216  }
217  
helper_restore(CPUSPARCState * env)218  void helper_restore(CPUSPARCState *env)
219  {
220      uint32_t cwp;
221  
222      cwp = cpu_cwp_inc(env, env->cwp + 1);
223      if (env->canrestore == 0) {
224          int tt = TT_FILL | (env->otherwin != 0
225                              ? (TT_WOTHER | ((env->wstate & 0x38) >> 1))
226                              : ((env->wstate & 0x7) << 2));
227          cpu_raise_exception_ra(env, tt, GETPC());
228      } else {
229          env->cansave++;
230          env->canrestore--;
231          cpu_set_cwp(env, cwp);
232      }
233  }
234  
helper_flushw(CPUSPARCState * env)235  void helper_flushw(CPUSPARCState *env)
236  {
237      if (env->cansave != env->nwindows - 2) {
238          int tt = TT_SPILL | (env->otherwin != 0
239                               ? (TT_WOTHER | ((env->wstate & 0x38) >> 1))
240                               : ((env->wstate & 0x7) << 2));
241          cpu_raise_exception_ra(env, tt, GETPC());
242      }
243  }
244  
helper_saved(CPUSPARCState * env)245  void helper_saved(CPUSPARCState *env)
246  {
247      env->cansave++;
248      if (env->otherwin == 0) {
249          env->canrestore--;
250      } else {
251          env->otherwin--;
252      }
253  }
254  
helper_restored(CPUSPARCState * env)255  void helper_restored(CPUSPARCState *env)
256  {
257      env->canrestore++;
258      if (env->cleanwin < env->nwindows - 1) {
259          env->cleanwin++;
260      }
261      if (env->otherwin == 0) {
262          env->cansave--;
263      } else {
264          env->otherwin--;
265      }
266  }
267  
cpu_get_ccr(CPUSPARCState * env)268  target_ulong cpu_get_ccr(CPUSPARCState *env)
269  {
270      target_ulong ccr = 0;
271  
272      ccr |= (env->icc_C >> 32) & 1;
273      ccr |= ((int32_t)env->cc_V < 0) << 1;
274      ccr |= ((int32_t)env->icc_Z == 0) << 2;
275      ccr |= ((int32_t)env->cc_N < 0) << 3;
276  
277      ccr |= env->xcc_C << 4;
278      ccr |= (env->cc_V < 0) << 5;
279      ccr |= (env->xcc_Z == 0) << 6;
280      ccr |= (env->cc_N < 0) << 7;
281  
282      return ccr;
283  }
284  
cpu_put_ccr(CPUSPARCState * env,target_ulong val)285  void cpu_put_ccr(CPUSPARCState *env, target_ulong val)
286  {
287      env->cc_N = deposit64(-(val & 0x08), 32, 32, -(val & 0x80));
288      env->cc_V = deposit64(-(val & 0x02), 32, 32, -(val & 0x20));
289      env->icc_C = (uint64_t)val << 32;
290      env->xcc_C = (val >> 4) & 1;
291      env->icc_Z = ~val & 0x04;
292      env->xcc_Z = ~val & 0x40;
293  }
294  
cpu_get_cwp64(CPUSPARCState * env)295  target_ulong cpu_get_cwp64(CPUSPARCState *env)
296  {
297      return env->nwindows - 1 - env->cwp;
298  }
299  
cpu_put_cwp64(CPUSPARCState * env,int cwp)300  void cpu_put_cwp64(CPUSPARCState *env, int cwp)
301  {
302      if (unlikely(cwp >= env->nwindows || cwp < 0)) {
303          cwp %= env->nwindows;
304      }
305      cpu_set_cwp(env, env->nwindows - 1 - cwp);
306  }
307  
helper_rdccr(CPUSPARCState * env)308  target_ulong helper_rdccr(CPUSPARCState *env)
309  {
310      return cpu_get_ccr(env);
311  }
312  
helper_wrccr(CPUSPARCState * env,target_ulong new_ccr)313  void helper_wrccr(CPUSPARCState *env, target_ulong new_ccr)
314  {
315      cpu_put_ccr(env, new_ccr);
316  }
317  
318  /* CWP handling is reversed in V9, but we still use the V8 register
319     order. */
helper_rdcwp(CPUSPARCState * env)320  target_ulong helper_rdcwp(CPUSPARCState *env)
321  {
322      return cpu_get_cwp64(env);
323  }
324  
helper_wrcwp(CPUSPARCState * env,target_ulong new_cwp)325  void helper_wrcwp(CPUSPARCState *env, target_ulong new_cwp)
326  {
327      cpu_put_cwp64(env, new_cwp);
328  }
329  
get_gregset(CPUSPARCState * env,uint32_t pstate)330  static inline uint64_t *get_gregset(CPUSPARCState *env, uint32_t pstate)
331  {
332      if (env->def.features & CPU_FEATURE_GL) {
333          return env->glregs + (env->gl & 7) * 8;
334      }
335  
336      switch (pstate) {
337      default:
338          trace_win_helper_gregset_error(pstate);
339          /* fall through to normal set of global registers */
340      case 0:
341          return env->bgregs;
342      case PS_AG:
343          return env->agregs;
344      case PS_MG:
345          return env->mgregs;
346      case PS_IG:
347          return env->igregs;
348      }
349  }
350  
get_gl_gregset(CPUSPARCState * env,uint32_t gl)351  static inline uint64_t *get_gl_gregset(CPUSPARCState *env, uint32_t gl)
352  {
353      return env->glregs + (gl & 7) * 8;
354  }
355  
356  /* Switch global register bank */
cpu_gl_switch_gregs(CPUSPARCState * env,uint32_t new_gl)357  void cpu_gl_switch_gregs(CPUSPARCState *env, uint32_t new_gl)
358  {
359      uint64_t *src, *dst;
360      src = get_gl_gregset(env, new_gl);
361      dst = get_gl_gregset(env, env->gl);
362  
363      if (src != dst) {
364          memcpy32(dst, env->gregs);
365          memcpy32(env->gregs, src);
366      }
367  }
368  
helper_wrgl(CPUSPARCState * env,target_ulong new_gl)369  void helper_wrgl(CPUSPARCState *env, target_ulong new_gl)
370  {
371      cpu_gl_switch_gregs(env, new_gl & 7);
372      env->gl = new_gl & 7;
373  }
374  
cpu_change_pstate(CPUSPARCState * env,uint32_t new_pstate)375  void cpu_change_pstate(CPUSPARCState *env, uint32_t new_pstate)
376  {
377      uint32_t pstate_regs, new_pstate_regs;
378      uint64_t *src, *dst;
379  
380      if (env->def.features & CPU_FEATURE_GL) {
381          /* PS_AG, IG and MG are not implemented in this case */
382          new_pstate &= ~(PS_AG | PS_IG | PS_MG);
383          env->pstate = new_pstate;
384          return;
385      }
386  
387      pstate_regs = env->pstate & 0xc01;
388      new_pstate_regs = new_pstate & 0xc01;
389  
390      if (new_pstate_regs != pstate_regs) {
391          trace_win_helper_switch_pstate(pstate_regs, new_pstate_regs);
392  
393          /* Switch global register bank */
394          src = get_gregset(env, new_pstate_regs);
395          dst = get_gregset(env, pstate_regs);
396          memcpy32(dst, env->gregs);
397          memcpy32(env->gregs, src);
398      } else {
399          trace_win_helper_no_switch_pstate(new_pstate_regs);
400      }
401      env->pstate = new_pstate;
402  }
403  
helper_wrpstate(CPUSPARCState * env,target_ulong new_state)404  void helper_wrpstate(CPUSPARCState *env, target_ulong new_state)
405  {
406      cpu_change_pstate(env, new_state & 0xf3f);
407  
408  #if !defined(CONFIG_USER_ONLY)
409      if (cpu_interrupts_enabled(env)) {
410          bql_lock();
411          cpu_check_irqs(env);
412          bql_unlock();
413      }
414  #endif
415  }
416  
helper_wrpil(CPUSPARCState * env,target_ulong new_pil)417  void helper_wrpil(CPUSPARCState *env, target_ulong new_pil)
418  {
419  #if !defined(CONFIG_USER_ONLY)
420      trace_win_helper_wrpil(env->psrpil, (uint32_t)new_pil);
421  
422      env->psrpil = new_pil;
423  
424      if (cpu_interrupts_enabled(env)) {
425          bql_lock();
426          cpu_check_irqs(env);
427          bql_unlock();
428      }
429  #endif
430  }
431  
helper_done(CPUSPARCState * env)432  void helper_done(CPUSPARCState *env)
433  {
434      trap_state *tsptr = cpu_tsptr(env);
435  
436      env->pc = tsptr->tnpc;
437      env->npc = tsptr->tnpc + 4;
438      cpu_put_ccr(env, tsptr->tstate >> 32);
439      env->asi = (tsptr->tstate >> 24) & 0xff;
440      cpu_change_pstate(env, (tsptr->tstate >> 8) & 0xf3f);
441      cpu_put_cwp64(env, tsptr->tstate & 0xff);
442      if (cpu_has_hypervisor(env)) {
443          uint32_t new_gl = (tsptr->tstate >> 40) & 7;
444          env->hpstate = env->htstate[env->tl];
445          cpu_gl_switch_gregs(env, new_gl);
446          env->gl = new_gl;
447      }
448      env->tl--;
449  
450      trace_win_helper_done(env->tl);
451  
452  #if !defined(CONFIG_USER_ONLY)
453      if (cpu_interrupts_enabled(env)) {
454          bql_lock();
455          cpu_check_irqs(env);
456          bql_unlock();
457      }
458  #endif
459  }
460  
helper_retry(CPUSPARCState * env)461  void helper_retry(CPUSPARCState *env)
462  {
463      trap_state *tsptr = cpu_tsptr(env);
464  
465      env->pc = tsptr->tpc;
466      env->npc = tsptr->tnpc;
467      cpu_put_ccr(env, tsptr->tstate >> 32);
468      env->asi = (tsptr->tstate >> 24) & 0xff;
469      cpu_change_pstate(env, (tsptr->tstate >> 8) & 0xf3f);
470      cpu_put_cwp64(env, tsptr->tstate & 0xff);
471      if (cpu_has_hypervisor(env)) {
472          uint32_t new_gl = (tsptr->tstate >> 40) & 7;
473          env->hpstate = env->htstate[env->tl];
474          cpu_gl_switch_gregs(env, new_gl);
475          env->gl = new_gl;
476      }
477      env->tl--;
478  
479      trace_win_helper_retry(env->tl);
480  
481  #if !defined(CONFIG_USER_ONLY)
482      if (cpu_interrupts_enabled(env)) {
483          bql_lock();
484          cpu_check_irqs(env);
485          bql_unlock();
486      }
487  #endif
488  }
489  #endif
490