]>
Commit | Line | Data |
---|---|---|
e8af50a3 FB |
1 | #include "exec.h" |
2 | ||
83469015 | 3 | //#define DEBUG_PCALL |
e80cfcfc FB |
4 | //#define DEBUG_MMU |
5 | ||
9d893301 FB |
6 | void raise_exception(int tt) |
7 | { | |
8 | env->exception_index = tt; | |
9 | cpu_loop_exit(); | |
10 | } | |
11 | ||
a0c4cb4a FB |
12 | #ifdef USE_INT_TO_FLOAT_HELPERS |
13 | void do_fitos(void) | |
14 | { | |
15 | FT0 = (float) *((int32_t *)&FT1); | |
16 | } | |
17 | ||
18 | void do_fitod(void) | |
19 | { | |
20 | DT0 = (double) *((int32_t *)&FT1); | |
21 | } | |
22 | #endif | |
23 | ||
24 | void do_fabss(void) | |
e8af50a3 | 25 | { |
7a0e1f41 | 26 | FT0 = float32_abs(FT1); |
e8af50a3 FB |
27 | } |
28 | ||
3475187d FB |
29 | #ifdef TARGET_SPARC64 |
30 | void do_fabsd(void) | |
31 | { | |
32 | DT0 = float64_abs(DT1); | |
33 | } | |
34 | #endif | |
35 | ||
a0c4cb4a | 36 | void do_fsqrts(void) |
e8af50a3 | 37 | { |
7a0e1f41 | 38 | FT0 = float32_sqrt(FT1, &env->fp_status); |
e8af50a3 FB |
39 | } |
40 | ||
a0c4cb4a | 41 | void do_fsqrtd(void) |
e8af50a3 | 42 | { |
7a0e1f41 | 43 | DT0 = float64_sqrt(DT1, &env->fp_status); |
e8af50a3 FB |
44 | } |
45 | ||
3475187d | 46 | #define FS 0 |
a0c4cb4a | 47 | void do_fcmps (void) |
e8af50a3 | 48 | { |
3475187d | 49 | env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); |
e8af50a3 | 50 | if (isnan(FT0) || isnan(FT1)) { |
3475187d | 51 | T0 = (FSR_FCC1 | FSR_FCC0) << FS; |
e80cfcfc | 52 | if (env->fsr & FSR_NVM) { |
3475187d | 53 | env->fsr |= T0; |
e80cfcfc FB |
54 | raise_exception(TT_FP_EXCP); |
55 | } else { | |
56 | env->fsr |= FSR_NVA; | |
57 | } | |
e8af50a3 | 58 | } else if (FT0 < FT1) { |
3475187d | 59 | T0 = FSR_FCC0 << FS; |
e8af50a3 | 60 | } else if (FT0 > FT1) { |
3475187d | 61 | T0 = FSR_FCC1 << FS; |
e8af50a3 FB |
62 | } else { |
63 | T0 = 0; | |
64 | } | |
3475187d | 65 | env->fsr |= T0; |
e8af50a3 FB |
66 | } |
67 | ||
a0c4cb4a | 68 | void do_fcmpd (void) |
e8af50a3 | 69 | { |
3475187d FB |
70 | env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); |
71 | if (isnan(DT0) || isnan(DT1)) { | |
72 | T0 = (FSR_FCC1 | FSR_FCC0) << FS; | |
73 | if (env->fsr & FSR_NVM) { | |
74 | env->fsr |= T0; | |
75 | raise_exception(TT_FP_EXCP); | |
76 | } else { | |
77 | env->fsr |= FSR_NVA; | |
78 | } | |
79 | } else if (DT0 < DT1) { | |
80 | T0 = FSR_FCC0 << FS; | |
81 | } else if (DT0 > DT1) { | |
82 | T0 = FSR_FCC1 << FS; | |
83 | } else { | |
84 | T0 = 0; | |
85 | } | |
86 | env->fsr |= T0; | |
87 | } | |
88 | ||
89 | #ifdef TARGET_SPARC64 | |
90 | #undef FS | |
91 | #define FS 22 | |
92 | void do_fcmps_fcc1 (void) | |
93 | { | |
94 | env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); | |
95 | if (isnan(FT0) || isnan(FT1)) { | |
96 | T0 = (FSR_FCC1 | FSR_FCC0) << FS; | |
97 | if (env->fsr & FSR_NVM) { | |
98 | env->fsr |= T0; | |
99 | raise_exception(TT_FP_EXCP); | |
100 | } else { | |
101 | env->fsr |= FSR_NVA; | |
102 | } | |
103 | } else if (FT0 < FT1) { | |
104 | T0 = FSR_FCC0 << FS; | |
105 | } else if (FT0 > FT1) { | |
106 | T0 = FSR_FCC1 << FS; | |
107 | } else { | |
108 | T0 = 0; | |
109 | } | |
110 | env->fsr |= T0; | |
111 | } | |
112 | ||
113 | void do_fcmpd_fcc1 (void) | |
114 | { | |
115 | env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); | |
116 | if (isnan(DT0) || isnan(DT1)) { | |
117 | T0 = (FSR_FCC1 | FSR_FCC0) << FS; | |
118 | if (env->fsr & FSR_NVM) { | |
119 | env->fsr |= T0; | |
120 | raise_exception(TT_FP_EXCP); | |
121 | } else { | |
122 | env->fsr |= FSR_NVA; | |
123 | } | |
124 | } else if (DT0 < DT1) { | |
125 | T0 = FSR_FCC0 << FS; | |
126 | } else if (DT0 > DT1) { | |
127 | T0 = FSR_FCC1 << FS; | |
128 | } else { | |
129 | T0 = 0; | |
130 | } | |
131 | env->fsr |= T0; | |
132 | } | |
133 | ||
134 | #undef FS | |
135 | #define FS 24 | |
136 | void do_fcmps_fcc2 (void) | |
137 | { | |
138 | env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); | |
139 | if (isnan(FT0) || isnan(FT1)) { | |
140 | T0 = (FSR_FCC1 | FSR_FCC0) << FS; | |
141 | if (env->fsr & FSR_NVM) { | |
142 | env->fsr |= T0; | |
143 | raise_exception(TT_FP_EXCP); | |
144 | } else { | |
145 | env->fsr |= FSR_NVA; | |
146 | } | |
147 | } else if (FT0 < FT1) { | |
148 | T0 = FSR_FCC0 << FS; | |
149 | } else if (FT0 > FT1) { | |
150 | T0 = FSR_FCC1 << FS; | |
151 | } else { | |
152 | T0 = 0; | |
153 | } | |
154 | env->fsr |= T0; | |
155 | } | |
156 | ||
157 | void do_fcmpd_fcc2 (void) | |
158 | { | |
159 | env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); | |
e8af50a3 | 160 | if (isnan(DT0) || isnan(DT1)) { |
3475187d | 161 | T0 = (FSR_FCC1 | FSR_FCC0) << FS; |
e80cfcfc | 162 | if (env->fsr & FSR_NVM) { |
3475187d | 163 | env->fsr |= T0; |
e80cfcfc FB |
164 | raise_exception(TT_FP_EXCP); |
165 | } else { | |
166 | env->fsr |= FSR_NVA; | |
167 | } | |
e8af50a3 | 168 | } else if (DT0 < DT1) { |
3475187d | 169 | T0 = FSR_FCC0 << FS; |
e8af50a3 | 170 | } else if (DT0 > DT1) { |
3475187d FB |
171 | T0 = FSR_FCC1 << FS; |
172 | } else { | |
173 | T0 = 0; | |
174 | } | |
175 | env->fsr |= T0; | |
176 | } | |
177 | ||
178 | #undef FS | |
179 | #define FS 26 | |
180 | void do_fcmps_fcc3 (void) | |
181 | { | |
182 | env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); | |
183 | if (isnan(FT0) || isnan(FT1)) { | |
184 | T0 = (FSR_FCC1 | FSR_FCC0) << FS; | |
185 | if (env->fsr & FSR_NVM) { | |
186 | env->fsr |= T0; | |
187 | raise_exception(TT_FP_EXCP); | |
188 | } else { | |
189 | env->fsr |= FSR_NVA; | |
190 | } | |
191 | } else if (FT0 < FT1) { | |
192 | T0 = FSR_FCC0 << FS; | |
193 | } else if (FT0 > FT1) { | |
194 | T0 = FSR_FCC1 << FS; | |
e8af50a3 FB |
195 | } else { |
196 | T0 = 0; | |
197 | } | |
3475187d | 198 | env->fsr |= T0; |
e8af50a3 FB |
199 | } |
200 | ||
3475187d FB |
201 | void do_fcmpd_fcc3 (void) |
202 | { | |
203 | env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); | |
204 | if (isnan(DT0) || isnan(DT1)) { | |
205 | T0 = (FSR_FCC1 | FSR_FCC0) << FS; | |
206 | if (env->fsr & FSR_NVM) { | |
207 | env->fsr |= T0; | |
208 | raise_exception(TT_FP_EXCP); | |
209 | } else { | |
210 | env->fsr |= FSR_NVA; | |
211 | } | |
212 | } else if (DT0 < DT1) { | |
213 | T0 = FSR_FCC0 << FS; | |
214 | } else if (DT0 > DT1) { | |
215 | T0 = FSR_FCC1 << FS; | |
216 | } else { | |
217 | T0 = 0; | |
218 | } | |
219 | env->fsr |= T0; | |
220 | } | |
221 | #undef FS | |
222 | #endif | |
223 | ||
24741ef3 FB |
224 | #if defined(CONFIG_USER_ONLY) |
225 | void helper_ld_asi(int asi, int size, int sign) | |
226 | { | |
227 | } | |
228 | ||
229 | void helper_st_asi(int asi, int size, int sign) | |
230 | { | |
231 | } | |
232 | #else | |
3475187d | 233 | #ifndef TARGET_SPARC64 |
a0c4cb4a | 234 | void helper_ld_asi(int asi, int size, int sign) |
e8af50a3 | 235 | { |
83469015 | 236 | uint32_t ret = 0; |
e80cfcfc FB |
237 | |
238 | switch (asi) { | |
e8af50a3 | 239 | case 3: /* MMU probe */ |
e80cfcfc FB |
240 | { |
241 | int mmulev; | |
242 | ||
243 | mmulev = (T0 >> 8) & 15; | |
244 | if (mmulev > 4) | |
245 | ret = 0; | |
246 | else { | |
ee5bbe38 | 247 | ret = mmu_probe(env, T0, mmulev); |
e80cfcfc FB |
248 | //bswap32s(&ret); |
249 | } | |
250 | #ifdef DEBUG_MMU | |
251 | printf("mmu_probe: 0x%08x (lev %d) -> 0x%08x\n", T0, mmulev, ret); | |
252 | #endif | |
253 | } | |
254 | break; | |
e8af50a3 FB |
255 | case 4: /* read MMU regs */ |
256 | { | |
e80cfcfc | 257 | int reg = (T0 >> 8) & 0xf; |
e8af50a3 | 258 | |
e80cfcfc | 259 | ret = env->mmuregs[reg]; |
55754d9e FB |
260 | if (reg == 3) /* Fault status cleared on read */ |
261 | env->mmuregs[reg] = 0; | |
262 | #ifdef DEBUG_MMU | |
263 | printf("mmu_read: reg[%d] = 0x%08x\n", reg, ret); | |
264 | #endif | |
e8af50a3 | 265 | } |
e80cfcfc | 266 | break; |
e8af50a3 | 267 | case 0x20 ... 0x2f: /* MMU passthrough */ |
02aab46a FB |
268 | switch(size) { |
269 | case 1: | |
270 | ret = ldub_phys(T0); | |
271 | break; | |
272 | case 2: | |
273 | ret = lduw_phys(T0 & ~1); | |
274 | break; | |
275 | default: | |
276 | case 4: | |
277 | ret = ldl_phys(T0 & ~3); | |
278 | break; | |
9e61bde5 FB |
279 | case 8: |
280 | ret = ldl_phys(T0 & ~3); | |
281 | T0 = ldl_phys((T0 + 4) & ~3); | |
282 | break; | |
02aab46a | 283 | } |
e80cfcfc | 284 | break; |
e8af50a3 | 285 | default: |
e80cfcfc FB |
286 | ret = 0; |
287 | break; | |
e8af50a3 | 288 | } |
e80cfcfc | 289 | T1 = ret; |
e8af50a3 FB |
290 | } |
291 | ||
a0c4cb4a | 292 | void helper_st_asi(int asi, int size, int sign) |
e8af50a3 FB |
293 | { |
294 | switch(asi) { | |
295 | case 3: /* MMU flush */ | |
e80cfcfc FB |
296 | { |
297 | int mmulev; | |
298 | ||
299 | mmulev = (T0 >> 8) & 15; | |
55754d9e FB |
300 | #ifdef DEBUG_MMU |
301 | printf("mmu flush level %d\n", mmulev); | |
302 | #endif | |
e80cfcfc FB |
303 | switch (mmulev) { |
304 | case 0: // flush page | |
55754d9e | 305 | tlb_flush_page(env, T0 & 0xfffff000); |
e80cfcfc FB |
306 | break; |
307 | case 1: // flush segment (256k) | |
308 | case 2: // flush region (16M) | |
309 | case 3: // flush context (4G) | |
310 | case 4: // flush entire | |
55754d9e | 311 | tlb_flush(env, 1); |
e80cfcfc FB |
312 | break; |
313 | default: | |
314 | break; | |
315 | } | |
55754d9e | 316 | #ifdef DEBUG_MMU |
ee5bbe38 | 317 | dump_mmu(env); |
55754d9e | 318 | #endif |
e80cfcfc FB |
319 | return; |
320 | } | |
e8af50a3 FB |
321 | case 4: /* write MMU regs */ |
322 | { | |
83469015 FB |
323 | int reg = (T0 >> 8) & 0xf; |
324 | uint32_t oldreg; | |
e80cfcfc FB |
325 | |
326 | oldreg = env->mmuregs[reg]; | |
55754d9e FB |
327 | switch(reg) { |
328 | case 0: | |
e8af50a3 FB |
329 | env->mmuregs[reg] &= ~(MMU_E | MMU_NF); |
330 | env->mmuregs[reg] |= T1 & (MMU_E | MMU_NF); | |
6f7e9aec FB |
331 | // Mappings generated during no-fault mode or MMU |
332 | // disabled mode are invalid in normal mode | |
333 | if (oldreg != env->mmuregs[reg]) | |
55754d9e FB |
334 | tlb_flush(env, 1); |
335 | break; | |
336 | case 2: | |
e8af50a3 | 337 | env->mmuregs[reg] = T1; |
55754d9e FB |
338 | if (oldreg != env->mmuregs[reg]) { |
339 | /* we flush when the MMU context changes because | |
340 | QEMU has no MMU context support */ | |
341 | tlb_flush(env, 1); | |
342 | } | |
343 | break; | |
344 | case 3: | |
345 | case 4: | |
346 | break; | |
347 | default: | |
348 | env->mmuregs[reg] = T1; | |
349 | break; | |
350 | } | |
351 | #ifdef DEBUG_MMU | |
352 | if (oldreg != env->mmuregs[reg]) { | |
353 | printf("mmu change reg[%d]: 0x%08x -> 0x%08x\n", reg, oldreg, env->mmuregs[reg]); | |
354 | } | |
ee5bbe38 | 355 | dump_mmu(env); |
55754d9e | 356 | #endif |
e8af50a3 FB |
357 | return; |
358 | } | |
e80cfcfc FB |
359 | case 0x17: /* Block copy, sta access */ |
360 | { | |
361 | // value (T1) = src | |
362 | // address (T0) = dst | |
363 | // copy 32 bytes | |
83469015 | 364 | uint32_t src = T1, dst = T0; |
e80cfcfc FB |
365 | uint8_t temp[32]; |
366 | ||
49be8030 | 367 | tswap32s(&src); |
e80cfcfc FB |
368 | |
369 | cpu_physical_memory_read(src, (void *) &temp, 32); | |
370 | cpu_physical_memory_write(dst, (void *) &temp, 32); | |
371 | } | |
372 | return; | |
373 | case 0x1f: /* Block fill, stda access */ | |
374 | { | |
375 | // value (T1, T2) | |
376 | // address (T0) = dst | |
377 | // fill 32 bytes | |
83469015 FB |
378 | int i; |
379 | uint32_t dst = T0; | |
e80cfcfc FB |
380 | uint64_t val; |
381 | ||
382 | val = (((uint64_t)T1) << 32) | T2; | |
49be8030 | 383 | tswap64s(&val); |
e80cfcfc FB |
384 | |
385 | for (i = 0; i < 32; i += 8, dst += 8) { | |
386 | cpu_physical_memory_write(dst, (void *) &val, 8); | |
387 | } | |
388 | } | |
389 | return; | |
e8af50a3 FB |
390 | case 0x20 ... 0x2f: /* MMU passthrough */ |
391 | { | |
02aab46a FB |
392 | switch(size) { |
393 | case 1: | |
394 | stb_phys(T0, T1); | |
395 | break; | |
396 | case 2: | |
397 | stw_phys(T0 & ~1, T1); | |
398 | break; | |
399 | case 4: | |
400 | default: | |
401 | stl_phys(T0 & ~3, T1); | |
402 | break; | |
9e61bde5 FB |
403 | case 8: |
404 | stl_phys(T0 & ~3, T1); | |
405 | stl_phys((T0 + 4) & ~3, T2); | |
406 | break; | |
02aab46a | 407 | } |
e8af50a3 FB |
408 | } |
409 | return; | |
410 | default: | |
411 | return; | |
412 | } | |
413 | } | |
414 | ||
3475187d FB |
415 | #else |
416 | ||
417 | void helper_ld_asi(int asi, int size, int sign) | |
418 | { | |
83469015 | 419 | uint64_t ret = 0; |
3475187d FB |
420 | |
421 | if (asi < 0x80 && (env->pstate & PS_PRIV) == 0) | |
83469015 | 422 | raise_exception(TT_PRIV_ACT); |
3475187d FB |
423 | |
424 | switch (asi) { | |
425 | case 0x14: // Bypass | |
426 | case 0x15: // Bypass, non-cacheable | |
427 | { | |
02aab46a FB |
428 | switch(size) { |
429 | case 1: | |
430 | ret = ldub_phys(T0); | |
431 | break; | |
432 | case 2: | |
433 | ret = lduw_phys(T0 & ~1); | |
434 | break; | |
435 | case 4: | |
436 | ret = ldl_phys(T0 & ~3); | |
437 | break; | |
438 | default: | |
439 | case 8: | |
440 | ret = ldq_phys(T0 & ~7); | |
441 | break; | |
442 | } | |
3475187d FB |
443 | break; |
444 | } | |
83469015 FB |
445 | case 0x04: // Nucleus |
446 | case 0x0c: // Nucleus Little Endian (LE) | |
447 | case 0x10: // As if user primary | |
448 | case 0x11: // As if user secondary | |
449 | case 0x18: // As if user primary LE | |
450 | case 0x19: // As if user secondary LE | |
3475187d FB |
451 | case 0x1c: // Bypass LE |
452 | case 0x1d: // Bypass, non-cacheable LE | |
83469015 FB |
453 | case 0x24: // Nucleus quad LDD 128 bit atomic |
454 | case 0x2c: // Nucleus quad LDD 128 bit atomic | |
455 | case 0x4a: // UPA config | |
456 | case 0x82: // Primary no-fault | |
457 | case 0x83: // Secondary no-fault | |
458 | case 0x88: // Primary LE | |
459 | case 0x89: // Secondary LE | |
460 | case 0x8a: // Primary no-fault LE | |
461 | case 0x8b: // Secondary no-fault LE | |
3475187d FB |
462 | // XXX |
463 | break; | |
464 | case 0x45: // LSU | |
465 | ret = env->lsu; | |
466 | break; | |
467 | case 0x50: // I-MMU regs | |
468 | { | |
469 | int reg = (T0 >> 3) & 0xf; | |
470 | ||
471 | ret = env->immuregs[reg]; | |
472 | break; | |
473 | } | |
474 | case 0x51: // I-MMU 8k TSB pointer | |
475 | case 0x52: // I-MMU 64k TSB pointer | |
476 | case 0x55: // I-MMU data access | |
83469015 | 477 | // XXX |
3475187d | 478 | break; |
83469015 FB |
479 | case 0x56: // I-MMU tag read |
480 | { | |
481 | unsigned int i; | |
482 | ||
483 | for (i = 0; i < 64; i++) { | |
484 | // Valid, ctx match, vaddr match | |
485 | if ((env->itlb_tte[i] & 0x8000000000000000ULL) != 0 && | |
486 | env->itlb_tag[i] == T0) { | |
487 | ret = env->itlb_tag[i]; | |
488 | break; | |
489 | } | |
490 | } | |
491 | break; | |
492 | } | |
3475187d FB |
493 | case 0x58: // D-MMU regs |
494 | { | |
495 | int reg = (T0 >> 3) & 0xf; | |
496 | ||
497 | ret = env->dmmuregs[reg]; | |
498 | break; | |
499 | } | |
83469015 FB |
500 | case 0x5e: // D-MMU tag read |
501 | { | |
502 | unsigned int i; | |
503 | ||
504 | for (i = 0; i < 64; i++) { | |
505 | // Valid, ctx match, vaddr match | |
506 | if ((env->dtlb_tte[i] & 0x8000000000000000ULL) != 0 && | |
507 | env->dtlb_tag[i] == T0) { | |
508 | ret = env->dtlb_tag[i]; | |
509 | break; | |
510 | } | |
511 | } | |
512 | break; | |
513 | } | |
3475187d FB |
514 | case 0x59: // D-MMU 8k TSB pointer |
515 | case 0x5a: // D-MMU 64k TSB pointer | |
516 | case 0x5b: // D-MMU data pointer | |
517 | case 0x5d: // D-MMU data access | |
83469015 FB |
518 | case 0x48: // Interrupt dispatch, RO |
519 | case 0x49: // Interrupt data receive | |
520 | case 0x7f: // Incoming interrupt vector, RO | |
521 | // XXX | |
3475187d FB |
522 | break; |
523 | case 0x54: // I-MMU data in, WO | |
524 | case 0x57: // I-MMU demap, WO | |
525 | case 0x5c: // D-MMU data in, WO | |
526 | case 0x5f: // D-MMU demap, WO | |
83469015 | 527 | case 0x77: // Interrupt vector, WO |
3475187d FB |
528 | default: |
529 | ret = 0; | |
530 | break; | |
531 | } | |
532 | T1 = ret; | |
533 | } | |
534 | ||
535 | void helper_st_asi(int asi, int size, int sign) | |
536 | { | |
537 | if (asi < 0x80 && (env->pstate & PS_PRIV) == 0) | |
83469015 | 538 | raise_exception(TT_PRIV_ACT); |
3475187d FB |
539 | |
540 | switch(asi) { | |
541 | case 0x14: // Bypass | |
542 | case 0x15: // Bypass, non-cacheable | |
543 | { | |
02aab46a FB |
544 | switch(size) { |
545 | case 1: | |
546 | stb_phys(T0, T1); | |
547 | break; | |
548 | case 2: | |
549 | stw_phys(T0 & ~1, T1); | |
550 | break; | |
551 | case 4: | |
552 | stl_phys(T0 & ~3, T1); | |
553 | break; | |
554 | case 8: | |
555 | default: | |
556 | stq_phys(T0 & ~7, T1); | |
557 | break; | |
558 | } | |
3475187d FB |
559 | } |
560 | return; | |
83469015 FB |
561 | case 0x04: // Nucleus |
562 | case 0x0c: // Nucleus Little Endian (LE) | |
563 | case 0x10: // As if user primary | |
564 | case 0x11: // As if user secondary | |
565 | case 0x18: // As if user primary LE | |
566 | case 0x19: // As if user secondary LE | |
3475187d FB |
567 | case 0x1c: // Bypass LE |
568 | case 0x1d: // Bypass, non-cacheable LE | |
83469015 FB |
569 | case 0x24: // Nucleus quad LDD 128 bit atomic |
570 | case 0x2c: // Nucleus quad LDD 128 bit atomic | |
571 | case 0x4a: // UPA config | |
572 | case 0x88: // Primary LE | |
573 | case 0x89: // Secondary LE | |
3475187d FB |
574 | // XXX |
575 | return; | |
576 | case 0x45: // LSU | |
577 | { | |
578 | uint64_t oldreg; | |
579 | ||
580 | oldreg = env->lsu; | |
581 | env->lsu = T1 & (DMMU_E | IMMU_E); | |
582 | // Mappings generated during D/I MMU disabled mode are | |
583 | // invalid in normal mode | |
83469015 FB |
584 | if (oldreg != env->lsu) { |
585 | #ifdef DEBUG_MMU | |
586 | printf("LSU change: 0x%llx -> 0x%llx\n", oldreg, env->lsu); | |
587 | dump_mmu(env); | |
588 | #endif | |
3475187d | 589 | tlb_flush(env, 1); |
83469015 | 590 | } |
3475187d FB |
591 | return; |
592 | } | |
593 | case 0x50: // I-MMU regs | |
594 | { | |
595 | int reg = (T0 >> 3) & 0xf; | |
596 | uint64_t oldreg; | |
597 | ||
598 | oldreg = env->immuregs[reg]; | |
599 | switch(reg) { | |
600 | case 0: // RO | |
601 | case 4: | |
602 | return; | |
603 | case 1: // Not in I-MMU | |
604 | case 2: | |
605 | case 7: | |
606 | case 8: | |
607 | return; | |
608 | case 3: // SFSR | |
609 | if ((T1 & 1) == 0) | |
610 | T1 = 0; // Clear SFSR | |
611 | break; | |
612 | case 5: // TSB access | |
613 | case 6: // Tag access | |
614 | default: | |
615 | break; | |
616 | } | |
617 | env->immuregs[reg] = T1; | |
618 | #ifdef DEBUG_MMU | |
619 | if (oldreg != env->immuregs[reg]) { | |
83469015 | 620 | printf("mmu change reg[%d]: 0x%08llx -> 0x%08llx\n", reg, oldreg, env->immuregs[reg]); |
3475187d | 621 | } |
ee5bbe38 | 622 | dump_mmu(env); |
3475187d FB |
623 | #endif |
624 | return; | |
625 | } | |
626 | case 0x54: // I-MMU data in | |
627 | { | |
628 | unsigned int i; | |
629 | ||
630 | // Try finding an invalid entry | |
631 | for (i = 0; i < 64; i++) { | |
632 | if ((env->itlb_tte[i] & 0x8000000000000000ULL) == 0) { | |
633 | env->itlb_tag[i] = env->immuregs[6]; | |
634 | env->itlb_tte[i] = T1; | |
635 | return; | |
636 | } | |
637 | } | |
638 | // Try finding an unlocked entry | |
639 | for (i = 0; i < 64; i++) { | |
640 | if ((env->itlb_tte[i] & 0x40) == 0) { | |
641 | env->itlb_tag[i] = env->immuregs[6]; | |
642 | env->itlb_tte[i] = T1; | |
643 | return; | |
644 | } | |
645 | } | |
646 | // error state? | |
647 | return; | |
648 | } | |
649 | case 0x55: // I-MMU data access | |
650 | { | |
651 | unsigned int i = (T0 >> 3) & 0x3f; | |
652 | ||
653 | env->itlb_tag[i] = env->immuregs[6]; | |
654 | env->itlb_tte[i] = T1; | |
655 | return; | |
656 | } | |
657 | case 0x57: // I-MMU demap | |
83469015 | 658 | // XXX |
3475187d FB |
659 | return; |
660 | case 0x58: // D-MMU regs | |
661 | { | |
662 | int reg = (T0 >> 3) & 0xf; | |
663 | uint64_t oldreg; | |
664 | ||
665 | oldreg = env->dmmuregs[reg]; | |
666 | switch(reg) { | |
667 | case 0: // RO | |
668 | case 4: | |
669 | return; | |
670 | case 3: // SFSR | |
671 | if ((T1 & 1) == 0) { | |
672 | T1 = 0; // Clear SFSR, Fault address | |
673 | env->dmmuregs[4] = 0; | |
674 | } | |
675 | env->dmmuregs[reg] = T1; | |
676 | break; | |
677 | case 1: // Primary context | |
678 | case 2: // Secondary context | |
679 | case 5: // TSB access | |
680 | case 6: // Tag access | |
681 | case 7: // Virtual Watchpoint | |
682 | case 8: // Physical Watchpoint | |
683 | default: | |
684 | break; | |
685 | } | |
686 | env->dmmuregs[reg] = T1; | |
687 | #ifdef DEBUG_MMU | |
688 | if (oldreg != env->dmmuregs[reg]) { | |
83469015 | 689 | printf("mmu change reg[%d]: 0x%08llx -> 0x%08llx\n", reg, oldreg, env->dmmuregs[reg]); |
3475187d | 690 | } |
ee5bbe38 | 691 | dump_mmu(env); |
3475187d FB |
692 | #endif |
693 | return; | |
694 | } | |
695 | case 0x5c: // D-MMU data in | |
696 | { | |
697 | unsigned int i; | |
698 | ||
699 | // Try finding an invalid entry | |
700 | for (i = 0; i < 64; i++) { | |
701 | if ((env->dtlb_tte[i] & 0x8000000000000000ULL) == 0) { | |
702 | env->dtlb_tag[i] = env->dmmuregs[6]; | |
703 | env->dtlb_tte[i] = T1; | |
704 | return; | |
705 | } | |
706 | } | |
707 | // Try finding an unlocked entry | |
708 | for (i = 0; i < 64; i++) { | |
709 | if ((env->dtlb_tte[i] & 0x40) == 0) { | |
710 | env->dtlb_tag[i] = env->dmmuregs[6]; | |
711 | env->dtlb_tte[i] = T1; | |
712 | return; | |
713 | } | |
714 | } | |
715 | // error state? | |
716 | return; | |
717 | } | |
718 | case 0x5d: // D-MMU data access | |
719 | { | |
720 | unsigned int i = (T0 >> 3) & 0x3f; | |
721 | ||
722 | env->dtlb_tag[i] = env->dmmuregs[6]; | |
723 | env->dtlb_tte[i] = T1; | |
724 | return; | |
725 | } | |
726 | case 0x5f: // D-MMU demap | |
83469015 FB |
727 | case 0x49: // Interrupt data receive |
728 | // XXX | |
3475187d FB |
729 | return; |
730 | case 0x51: // I-MMU 8k TSB pointer, RO | |
731 | case 0x52: // I-MMU 64k TSB pointer, RO | |
732 | case 0x56: // I-MMU tag read, RO | |
733 | case 0x59: // D-MMU 8k TSB pointer, RO | |
734 | case 0x5a: // D-MMU 64k TSB pointer, RO | |
735 | case 0x5b: // D-MMU data pointer, RO | |
736 | case 0x5e: // D-MMU tag read, RO | |
83469015 FB |
737 | case 0x48: // Interrupt dispatch, RO |
738 | case 0x7f: // Incoming interrupt vector, RO | |
739 | case 0x82: // Primary no-fault, RO | |
740 | case 0x83: // Secondary no-fault, RO | |
741 | case 0x8a: // Primary no-fault LE, RO | |
742 | case 0x8b: // Secondary no-fault LE, RO | |
3475187d FB |
743 | default: |
744 | return; | |
745 | } | |
746 | } | |
3475187d | 747 | #endif |
24741ef3 | 748 | #endif /* !CONFIG_USER_ONLY */ |
3475187d FB |
749 | |
750 | #ifndef TARGET_SPARC64 | |
a0c4cb4a | 751 | void helper_rett() |
e8af50a3 | 752 | { |
af7bf89b FB |
753 | unsigned int cwp; |
754 | ||
e8af50a3 FB |
755 | env->psret = 1; |
756 | cwp = (env->cwp + 1) & (NWINDOWS - 1); | |
757 | if (env->wim & (1 << cwp)) { | |
758 | raise_exception(TT_WIN_UNF); | |
759 | } | |
760 | set_cwp(cwp); | |
761 | env->psrs = env->psrps; | |
762 | } | |
3475187d | 763 | #endif |
e8af50a3 | 764 | |
8d5f07fa | 765 | void helper_ldfsr(void) |
e8af50a3 | 766 | { |
7a0e1f41 | 767 | int rnd_mode; |
e8af50a3 FB |
768 | switch (env->fsr & FSR_RD_MASK) { |
769 | case FSR_RD_NEAREST: | |
7a0e1f41 | 770 | rnd_mode = float_round_nearest_even; |
e8af50a3 | 771 | break; |
ed910241 | 772 | default: |
e8af50a3 | 773 | case FSR_RD_ZERO: |
7a0e1f41 | 774 | rnd_mode = float_round_to_zero; |
e8af50a3 FB |
775 | break; |
776 | case FSR_RD_POS: | |
7a0e1f41 | 777 | rnd_mode = float_round_up; |
e8af50a3 FB |
778 | break; |
779 | case FSR_RD_NEG: | |
7a0e1f41 | 780 | rnd_mode = float_round_down; |
e8af50a3 FB |
781 | break; |
782 | } | |
7a0e1f41 | 783 | set_float_rounding_mode(rnd_mode, &env->fp_status); |
e8af50a3 | 784 | } |
e80cfcfc FB |
785 | |
786 | void cpu_get_fp64(uint64_t *pmant, uint16_t *pexp, double f) | |
787 | { | |
788 | int exptemp; | |
789 | ||
790 | *pmant = ldexp(frexp(f, &exptemp), 53); | |
791 | *pexp = exptemp; | |
792 | } | |
793 | ||
794 | double cpu_put_fp64(uint64_t mant, uint16_t exp) | |
795 | { | |
796 | return ldexp((double) mant, exp - 53); | |
797 | } | |
798 | ||
799 | void helper_debug() | |
800 | { | |
801 | env->exception_index = EXCP_DEBUG; | |
802 | cpu_loop_exit(); | |
803 | } | |
af7bf89b | 804 | |
3475187d | 805 | #ifndef TARGET_SPARC64 |
af7bf89b FB |
806 | void do_wrpsr() |
807 | { | |
808 | PUT_PSR(env, T0); | |
809 | } | |
810 | ||
811 | void do_rdpsr() | |
812 | { | |
813 | T0 = GET_PSR(env); | |
814 | } | |
3475187d FB |
815 | |
816 | #else | |
817 | ||
818 | void do_popc() | |
819 | { | |
820 | T0 = (T1 & 0x5555555555555555ULL) + ((T1 >> 1) & 0x5555555555555555ULL); | |
821 | T0 = (T0 & 0x3333333333333333ULL) + ((T0 >> 2) & 0x3333333333333333ULL); | |
822 | T0 = (T0 & 0x0f0f0f0f0f0f0f0fULL) + ((T0 >> 4) & 0x0f0f0f0f0f0f0f0fULL); | |
823 | T0 = (T0 & 0x00ff00ff00ff00ffULL) + ((T0 >> 8) & 0x00ff00ff00ff00ffULL); | |
824 | T0 = (T0 & 0x0000ffff0000ffffULL) + ((T0 >> 16) & 0x0000ffff0000ffffULL); | |
825 | T0 = (T0 & 0x00000000ffffffffULL) + ((T0 >> 32) & 0x00000000ffffffffULL); | |
826 | } | |
83469015 FB |
827 | |
828 | static inline uint64_t *get_gregset(uint64_t pstate) | |
829 | { | |
830 | switch (pstate) { | |
831 | default: | |
832 | case 0: | |
833 | return env->bgregs; | |
834 | case PS_AG: | |
835 | return env->agregs; | |
836 | case PS_MG: | |
837 | return env->mgregs; | |
838 | case PS_IG: | |
839 | return env->igregs; | |
840 | } | |
841 | } | |
842 | ||
843 | void do_wrpstate() | |
844 | { | |
845 | uint64_t new_pstate, pstate_regs, new_pstate_regs; | |
846 | uint64_t *src, *dst; | |
847 | ||
848 | new_pstate = T0 & 0xf3f; | |
849 | pstate_regs = env->pstate & 0xc01; | |
850 | new_pstate_regs = new_pstate & 0xc01; | |
851 | if (new_pstate_regs != pstate_regs) { | |
852 | // Switch global register bank | |
853 | src = get_gregset(new_pstate_regs); | |
854 | dst = get_gregset(pstate_regs); | |
855 | memcpy32(dst, env->gregs); | |
856 | memcpy32(env->gregs, src); | |
857 | } | |
858 | env->pstate = new_pstate; | |
859 | } | |
860 | ||
861 | void do_done(void) | |
862 | { | |
863 | env->tl--; | |
864 | env->pc = env->tnpc[env->tl]; | |
865 | env->npc = env->tnpc[env->tl] + 4; | |
866 | PUT_CCR(env, env->tstate[env->tl] >> 32); | |
867 | env->asi = (env->tstate[env->tl] >> 24) & 0xff; | |
868 | env->pstate = (env->tstate[env->tl] >> 8) & 0xfff; | |
869 | set_cwp(env->tstate[env->tl] & 0xff); | |
870 | } | |
871 | ||
872 | void do_retry(void) | |
873 | { | |
874 | env->tl--; | |
875 | env->pc = env->tpc[env->tl]; | |
876 | env->npc = env->tnpc[env->tl]; | |
877 | PUT_CCR(env, env->tstate[env->tl] >> 32); | |
878 | env->asi = (env->tstate[env->tl] >> 24) & 0xff; | |
879 | env->pstate = (env->tstate[env->tl] >> 8) & 0xfff; | |
880 | set_cwp(env->tstate[env->tl] & 0xff); | |
881 | } | |
3475187d | 882 | #endif |
ee5bbe38 FB |
883 | |
884 | void set_cwp(int new_cwp) | |
885 | { | |
886 | /* put the modified wrap registers at their proper location */ | |
887 | if (env->cwp == (NWINDOWS - 1)) | |
888 | memcpy32(env->regbase, env->regbase + NWINDOWS * 16); | |
889 | env->cwp = new_cwp; | |
890 | /* put the wrap registers at their temporary location */ | |
891 | if (new_cwp == (NWINDOWS - 1)) | |
892 | memcpy32(env->regbase + NWINDOWS * 16, env->regbase); | |
893 | env->regwptr = env->regbase + (new_cwp * 16); | |
894 | REGWPTR = env->regwptr; | |
895 | } | |
896 | ||
897 | void cpu_set_cwp(CPUState *env1, int new_cwp) | |
898 | { | |
899 | CPUState *saved_env; | |
900 | #ifdef reg_REGWPTR | |
901 | target_ulong *saved_regwptr; | |
902 | #endif | |
903 | ||
904 | saved_env = env; | |
905 | #ifdef reg_REGWPTR | |
906 | saved_regwptr = REGWPTR; | |
907 | #endif | |
908 | env = env1; | |
909 | set_cwp(new_cwp); | |
910 | env = saved_env; | |
911 | #ifdef reg_REGWPTR | |
912 | REGWPTR = saved_regwptr; | |
913 | #endif | |
914 | } | |
915 | ||
916 | #ifdef TARGET_SPARC64 | |
917 | void do_interrupt(int intno) | |
918 | { | |
919 | #ifdef DEBUG_PCALL | |
920 | if (loglevel & CPU_LOG_INT) { | |
921 | static int count; | |
83469015 | 922 | fprintf(logfile, "%6d: v=%04x pc=%016llx npc=%016llx SP=%016llx\n", |
ee5bbe38 FB |
923 | count, intno, |
924 | env->pc, | |
925 | env->npc, env->regwptr[6]); | |
926 | cpu_dump_state(env, logfile, fprintf, 0); | |
927 | #if 0 | |
928 | { | |
929 | int i; | |
930 | uint8_t *ptr; | |
931 | ||
932 | fprintf(logfile, " code="); | |
933 | ptr = (uint8_t *)env->pc; | |
934 | for(i = 0; i < 16; i++) { | |
935 | fprintf(logfile, " %02x", ldub(ptr + i)); | |
936 | } | |
937 | fprintf(logfile, "\n"); | |
938 | } | |
939 | #endif | |
940 | count++; | |
941 | } | |
942 | #endif | |
943 | #if !defined(CONFIG_USER_ONLY) | |
83469015 | 944 | if (env->tl == MAXTL) { |
c68ea704 | 945 | cpu_abort(env, "Trap 0x%04x while trap level is MAXTL, Error state", env->exception_index); |
ee5bbe38 FB |
946 | return; |
947 | } | |
948 | #endif | |
949 | env->tstate[env->tl] = ((uint64_t)GET_CCR(env) << 32) | ((env->asi & 0xff) << 24) | | |
950 | ((env->pstate & 0xfff) << 8) | (env->cwp & 0xff); | |
951 | env->tpc[env->tl] = env->pc; | |
952 | env->tnpc[env->tl] = env->npc; | |
953 | env->tt[env->tl] = intno; | |
83469015 FB |
954 | env->pstate = PS_PEF | PS_PRIV | PS_AG; |
955 | env->tbr &= ~0x7fffULL; | |
956 | env->tbr |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5); | |
957 | if (env->tl < MAXTL - 1) { | |
958 | env->tl++; | |
959 | } else { | |
960 | env->pstate |= PS_RED; | |
961 | if (env->tl != MAXTL) | |
962 | env->tl++; | |
963 | } | |
ee5bbe38 FB |
964 | env->pc = env->tbr; |
965 | env->npc = env->pc + 4; | |
966 | env->exception_index = 0; | |
967 | } | |
968 | #else | |
969 | void do_interrupt(int intno) | |
970 | { | |
971 | int cwp; | |
972 | ||
973 | #ifdef DEBUG_PCALL | |
974 | if (loglevel & CPU_LOG_INT) { | |
975 | static int count; | |
976 | fprintf(logfile, "%6d: v=%02x pc=%08x npc=%08x SP=%08x\n", | |
977 | count, intno, | |
978 | env->pc, | |
979 | env->npc, env->regwptr[6]); | |
980 | cpu_dump_state(env, logfile, fprintf, 0); | |
981 | #if 0 | |
982 | { | |
983 | int i; | |
984 | uint8_t *ptr; | |
985 | ||
986 | fprintf(logfile, " code="); | |
987 | ptr = (uint8_t *)env->pc; | |
988 | for(i = 0; i < 16; i++) { | |
989 | fprintf(logfile, " %02x", ldub(ptr + i)); | |
990 | } | |
991 | fprintf(logfile, "\n"); | |
992 | } | |
993 | #endif | |
994 | count++; | |
995 | } | |
996 | #endif | |
997 | #if !defined(CONFIG_USER_ONLY) | |
998 | if (env->psret == 0) { | |
c68ea704 | 999 | cpu_abort(env, "Trap 0x%02x while interrupts disabled, Error state", env->exception_index); |
ee5bbe38 FB |
1000 | return; |
1001 | } | |
1002 | #endif | |
1003 | env->psret = 0; | |
1004 | cwp = (env->cwp - 1) & (NWINDOWS - 1); | |
1005 | set_cwp(cwp); | |
1006 | env->regwptr[9] = env->pc; | |
1007 | env->regwptr[10] = env->npc; | |
1008 | env->psrps = env->psrs; | |
1009 | env->psrs = 1; | |
1010 | env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4); | |
1011 | env->pc = env->tbr; | |
1012 | env->npc = env->pc + 4; | |
1013 | env->exception_index = 0; | |
1014 | } | |
1015 | #endif | |
1016 | ||
1017 | #if !defined(CONFIG_USER_ONLY) | |
1018 | ||
1019 | #define MMUSUFFIX _mmu | |
1020 | #define GETPC() (__builtin_return_address(0)) | |
1021 | ||
1022 | #define SHIFT 0 | |
1023 | #include "softmmu_template.h" | |
1024 | ||
1025 | #define SHIFT 1 | |
1026 | #include "softmmu_template.h" | |
1027 | ||
1028 | #define SHIFT 2 | |
1029 | #include "softmmu_template.h" | |
1030 | ||
1031 | #define SHIFT 3 | |
1032 | #include "softmmu_template.h" | |
1033 | ||
1034 | ||
1035 | /* try to fill the TLB and return an exception if error. If retaddr is | |
1036 | NULL, it means that the function was called in C code (i.e. not | |
1037 | from generated code or from helper.c) */ | |
1038 | /* XXX: fix it to restore all registers */ | |
1039 | void tlb_fill(target_ulong addr, int is_write, int is_user, void *retaddr) | |
1040 | { | |
1041 | TranslationBlock *tb; | |
1042 | int ret; | |
1043 | unsigned long pc; | |
1044 | CPUState *saved_env; | |
1045 | ||
1046 | /* XXX: hack to restore env in all cases, even if not called from | |
1047 | generated code */ | |
1048 | saved_env = env; | |
1049 | env = cpu_single_env; | |
1050 | ||
1051 | ret = cpu_sparc_handle_mmu_fault(env, addr, is_write, is_user, 1); | |
1052 | if (ret) { | |
1053 | if (retaddr) { | |
1054 | /* now we have a real cpu fault */ | |
1055 | pc = (unsigned long)retaddr; | |
1056 | tb = tb_find_pc(pc); | |
1057 | if (tb) { | |
1058 | /* the PC is inside the translated code. It means that we have | |
1059 | a virtual CPU fault */ | |
1060 | cpu_restore_state(tb, env, pc, (void *)T2); | |
1061 | } | |
1062 | } | |
1063 | cpu_loop_exit(); | |
1064 | } | |
1065 | env = saved_env; | |
1066 | } | |
1067 | ||
1068 | #endif |