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