xref: /openbmc/qemu/target/sparc/helper.c (revision 6c7937ec)
1 /*
2  *  Misc Sparc helpers
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 "cpu.h"
22 #include "exec/exec-all.h"
23 #include "qemu/timer.h"
24 #include "qemu/host-utils.h"
25 #include "exec/helper-proto.h"
26 
27 void cpu_raise_exception_ra(CPUSPARCState *env, int tt, uintptr_t ra)
28 {
29     CPUState *cs = env_cpu(env);
30 
31     cs->exception_index = tt;
32     cpu_loop_exit_restore(cs, ra);
33 }
34 
35 void helper_raise_exception(CPUSPARCState *env, int tt)
36 {
37     CPUState *cs = env_cpu(env);
38 
39     cs->exception_index = tt;
40     cpu_loop_exit(cs);
41 }
42 
43 void helper_debug(CPUSPARCState *env)
44 {
45     CPUState *cs = env_cpu(env);
46 
47     cs->exception_index = EXCP_DEBUG;
48     cpu_loop_exit(cs);
49 }
50 
51 #ifdef TARGET_SPARC64
52 void helper_tick_set_count(void *opaque, uint64_t count)
53 {
54 #if !defined(CONFIG_USER_ONLY)
55     cpu_tick_set_count(opaque, count);
56 #endif
57 }
58 
59 uint64_t helper_tick_get_count(CPUSPARCState *env, void *opaque, int mem_idx)
60 {
61 #if !defined(CONFIG_USER_ONLY)
62     CPUTimer *timer = opaque;
63 
64     if (timer->npt && mem_idx < MMU_KERNEL_IDX) {
65         cpu_raise_exception_ra(env, TT_PRIV_INSN, GETPC());
66     }
67 
68     return cpu_tick_get_count(timer);
69 #else
70     /* In user-mode, QEMU_CLOCK_VIRTUAL doesn't exist.
71        Just pass through the host cpu clock ticks.  */
72     return cpu_get_host_ticks();
73 #endif
74 }
75 
76 void helper_tick_set_limit(void *opaque, uint64_t limit)
77 {
78 #if !defined(CONFIG_USER_ONLY)
79     cpu_tick_set_limit(opaque, limit);
80 #endif
81 }
82 #endif
83 
84 uint64_t helper_udiv(CPUSPARCState *env, target_ulong a, target_ulong b)
85 {
86     uint64_t a64 = (uint32_t)a | ((uint64_t)env->y << 32);
87     uint32_t b32 = b;
88     uint32_t r;
89 
90     if (b32 == 0) {
91         cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC());
92     }
93 
94     a64 /= b32;
95     r = a64;
96     if (unlikely(a64 > UINT32_MAX)) {
97         return -1; /* r = UINT32_MAX, v = 1 */
98     }
99     return r;
100 }
101 
102 uint64_t helper_sdiv(CPUSPARCState *env, target_ulong a, target_ulong b)
103 {
104     int64_t a64 = (uint32_t)a | ((uint64_t)env->y << 32);
105     int32_t b32 = b;
106     int32_t r;
107 
108     if (b32 == 0) {
109         cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC());
110     }
111 
112     if (unlikely(a64 == INT64_MIN)) {
113         /*
114          * Special case INT64_MIN / -1 is required to avoid trap on x86 host.
115          * However, with a dividend of INT64_MIN, there is no 32-bit divisor
116          * which can yield a 32-bit result:
117          *    INT64_MIN / INT32_MIN =  0x1_0000_0000
118          *    INT64_MIN / INT32_MAX = -0x1_0000_0002
119          * Therefore we know we must overflow and saturate.
120          */
121         return (uint32_t)(b32 < 0 ? INT32_MAX : INT32_MIN) | (-1ull << 32);
122     }
123 
124     a64 /= b;
125     r = a64;
126     if (unlikely(r != a64)) {
127         return (uint32_t)(a64 < 0 ? INT32_MIN : INT32_MAX) | (-1ull << 32);
128     }
129     return (uint32_t)r;
130 }
131 
132 target_ulong helper_taddcctv(CPUSPARCState *env, target_ulong src1,
133                              target_ulong src2)
134 {
135     target_ulong dst, v;
136 
137     /* Tag overflow occurs if either input has bits 0 or 1 set.  */
138     if ((src1 | src2) & 3) {
139         goto tag_overflow;
140     }
141 
142     dst = src1 + src2;
143 
144     /* Tag overflow occurs if the addition overflows.  */
145     v = ~(src1 ^ src2) & (src1 ^ dst);
146     if (v & (1u << 31)) {
147         goto tag_overflow;
148     }
149 
150     /* Only modify the CC after any exceptions have been generated.  */
151     env->cc_V = v;
152     env->cc_N = dst;
153     env->icc_Z = dst;
154 #ifdef TARGET_SPARC64
155     env->xcc_Z = dst;
156     env->icc_C = dst ^ src1 ^ src2;
157     env->xcc_C = dst < src1;
158 #else
159     env->icc_C = dst < src1;
160 #endif
161 
162     return dst;
163 
164  tag_overflow:
165     cpu_raise_exception_ra(env, TT_TOVF, GETPC());
166 }
167 
168 target_ulong helper_tsubcctv(CPUSPARCState *env, target_ulong src1,
169                              target_ulong src2)
170 {
171     target_ulong dst, v;
172 
173     /* Tag overflow occurs if either input has bits 0 or 1 set.  */
174     if ((src1 | src2) & 3) {
175         goto tag_overflow;
176     }
177 
178     dst = src1 - src2;
179 
180     /* Tag overflow occurs if the subtraction overflows.  */
181     v = (src1 ^ src2) & (src1 ^ dst);
182     if (v & (1u << 31)) {
183         goto tag_overflow;
184     }
185 
186     /* Only modify the CC after any exceptions have been generated.  */
187     env->cc_V = v;
188     env->cc_N = dst;
189     env->icc_Z = dst;
190 #ifdef TARGET_SPARC64
191     env->xcc_Z = dst;
192     env->icc_C = dst ^ src1 ^ src2;
193     env->xcc_C = src1 < src2;
194 #else
195     env->icc_C = src1 < src2;
196 #endif
197 
198     return dst;
199 
200  tag_overflow:
201     cpu_raise_exception_ra(env, TT_TOVF, GETPC());
202 }
203 
204 #ifndef TARGET_SPARC64
205 void helper_power_down(CPUSPARCState *env)
206 {
207     CPUState *cs = env_cpu(env);
208 
209     cs->halted = 1;
210     cs->exception_index = EXCP_HLT;
211     env->pc = env->npc;
212     env->npc = env->pc + 4;
213     cpu_loop_exit(cs);
214 }
215 #endif
216