576c2cdc76
According to pages 9-31 - 9-34 of "SuperSPARC & MultiCache Controller User's Manual": 1. "A lower priority fault may not overwrite the MFSR status of a higher priority fault." 2. The MFAR is overwritten according to the policy defined for the MFSR 3. The overwrite bit is asserted if the fault status register (MFSR) has been written more than once by faults of the same class 4. SuperSPARC will never place instruction fault addresses in the MFAR. Implementation of points 1-3 allows booting Solaris 2.6 and 2.5.1. v2: CODING_STYLE fixes Signed-off-by: Artyom Tarasenko <atar4qemu@gmail.com> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
3821 lines
104 KiB
C
3821 lines
104 KiB
C
#include "exec.h"
|
|
#include "host-utils.h"
|
|
#include "helper.h"
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
#include "softmmu_exec.h"
|
|
#endif /* !defined(CONFIG_USER_ONLY) */
|
|
|
|
//#define DEBUG_MMU
|
|
//#define DEBUG_MXCC
|
|
//#define DEBUG_UNALIGNED
|
|
//#define DEBUG_UNASSIGNED
|
|
//#define DEBUG_ASI
|
|
//#define DEBUG_PCALL
|
|
//#define DEBUG_PSTATE
|
|
|
|
#ifdef DEBUG_MMU
|
|
#define DPRINTF_MMU(fmt, ...) \
|
|
do { printf("MMU: " fmt , ## __VA_ARGS__); } while (0)
|
|
#else
|
|
#define DPRINTF_MMU(fmt, ...) do {} while (0)
|
|
#endif
|
|
|
|
#ifdef DEBUG_MXCC
|
|
#define DPRINTF_MXCC(fmt, ...) \
|
|
do { printf("MXCC: " fmt , ## __VA_ARGS__); } while (0)
|
|
#else
|
|
#define DPRINTF_MXCC(fmt, ...) do {} while (0)
|
|
#endif
|
|
|
|
#ifdef DEBUG_ASI
|
|
#define DPRINTF_ASI(fmt, ...) \
|
|
do { printf("ASI: " fmt , ## __VA_ARGS__); } while (0)
|
|
#endif
|
|
|
|
#ifdef DEBUG_PSTATE
|
|
#define DPRINTF_PSTATE(fmt, ...) \
|
|
do { printf("PSTATE: " fmt , ## __VA_ARGS__); } while (0)
|
|
#else
|
|
#define DPRINTF_PSTATE(fmt, ...) do {} while (0)
|
|
#endif
|
|
|
|
#ifdef TARGET_SPARC64
|
|
#ifndef TARGET_ABI32
|
|
#define AM_CHECK(env1) ((env1)->pstate & PS_AM)
|
|
#else
|
|
#define AM_CHECK(env1) (1)
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
|
|
// Calculates TSB pointer value for fault page size 8k or 64k
|
|
static uint64_t ultrasparc_tsb_pointer(uint64_t tsb_register,
|
|
uint64_t tag_access_register,
|
|
int page_size)
|
|
{
|
|
uint64_t tsb_base = tsb_register & ~0x1fffULL;
|
|
int tsb_split = (tsb_register & 0x1000ULL) ? 1 : 0;
|
|
int tsb_size = tsb_register & 0xf;
|
|
|
|
// discard lower 13 bits which hold tag access context
|
|
uint64_t tag_access_va = tag_access_register & ~0x1fffULL;
|
|
|
|
// now reorder bits
|
|
uint64_t tsb_base_mask = ~0x1fffULL;
|
|
uint64_t va = tag_access_va;
|
|
|
|
// move va bits to correct position
|
|
if (page_size == 8*1024) {
|
|
va >>= 9;
|
|
} else if (page_size == 64*1024) {
|
|
va >>= 12;
|
|
}
|
|
|
|
if (tsb_size) {
|
|
tsb_base_mask <<= tsb_size;
|
|
}
|
|
|
|
// calculate tsb_base mask and adjust va if split is in use
|
|
if (tsb_split) {
|
|
if (page_size == 8*1024) {
|
|
va &= ~(1ULL << (13 + tsb_size));
|
|
} else if (page_size == 64*1024) {
|
|
va |= (1ULL << (13 + tsb_size));
|
|
}
|
|
tsb_base_mask <<= 1;
|
|
}
|
|
|
|
return ((tsb_base & tsb_base_mask) | (va & ~tsb_base_mask)) & ~0xfULL;
|
|
}
|
|
|
|
// Calculates tag target register value by reordering bits
|
|
// in tag access register
|
|
static uint64_t ultrasparc_tag_target(uint64_t tag_access_register)
|
|
{
|
|
return ((tag_access_register & 0x1fff) << 48) | (tag_access_register >> 22);
|
|
}
|
|
|
|
static void replace_tlb_entry(SparcTLBEntry *tlb,
|
|
uint64_t tlb_tag, uint64_t tlb_tte,
|
|
CPUState *env1)
|
|
{
|
|
target_ulong mask, size, va, offset;
|
|
|
|
// flush page range if translation is valid
|
|
if (TTE_IS_VALID(tlb->tte)) {
|
|
|
|
mask = 0xffffffffffffe000ULL;
|
|
mask <<= 3 * ((tlb->tte >> 61) & 3);
|
|
size = ~mask + 1;
|
|
|
|
va = tlb->tag & mask;
|
|
|
|
for (offset = 0; offset < size; offset += TARGET_PAGE_SIZE) {
|
|
tlb_flush_page(env1, va + offset);
|
|
}
|
|
}
|
|
|
|
tlb->tag = tlb_tag;
|
|
tlb->tte = tlb_tte;
|
|
}
|
|
|
|
static void demap_tlb(SparcTLBEntry *tlb, target_ulong demap_addr,
|
|
const char* strmmu, CPUState *env1)
|
|
{
|
|
unsigned int i;
|
|
target_ulong mask;
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
if (TTE_IS_VALID(tlb[i].tte)) {
|
|
|
|
mask = 0xffffffffffffe000ULL;
|
|
mask <<= 3 * ((tlb[i].tte >> 61) & 3);
|
|
|
|
if ((demap_addr & mask) == (tlb[i].tag & mask)) {
|
|
replace_tlb_entry(&tlb[i], 0, 0, env1);
|
|
#ifdef DEBUG_MMU
|
|
DPRINTF_MMU("%s demap invalidated entry [%02u]\n", strmmu, i);
|
|
dump_mmu(env1);
|
|
#endif
|
|
}
|
|
//return;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static void replace_tlb_1bit_lru(SparcTLBEntry *tlb,
|
|
uint64_t tlb_tag, uint64_t tlb_tte,
|
|
const char* strmmu, CPUState *env1)
|
|
{
|
|
unsigned int i, replace_used;
|
|
|
|
// Try replacing invalid entry
|
|
for (i = 0; i < 64; i++) {
|
|
if (!TTE_IS_VALID(tlb[i].tte)) {
|
|
replace_tlb_entry(&tlb[i], tlb_tag, tlb_tte, env1);
|
|
#ifdef DEBUG_MMU
|
|
DPRINTF_MMU("%s lru replaced invalid entry [%i]\n", strmmu, i);
|
|
dump_mmu(env1);
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
|
|
// All entries are valid, try replacing unlocked entry
|
|
|
|
for (replace_used = 0; replace_used < 2; ++replace_used) {
|
|
|
|
// Used entries are not replaced on first pass
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
if (!TTE_IS_LOCKED(tlb[i].tte) && !TTE_IS_USED(tlb[i].tte)) {
|
|
|
|
replace_tlb_entry(&tlb[i], tlb_tag, tlb_tte, env1);
|
|
#ifdef DEBUG_MMU
|
|
DPRINTF_MMU("%s lru replaced unlocked %s entry [%i]\n",
|
|
strmmu, (replace_used?"used":"unused"), i);
|
|
dump_mmu(env1);
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Now reset used bit and search for unused entries again
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
TTE_SET_UNUSED(tlb[i].tte);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_MMU
|
|
DPRINTF_MMU("%s lru replacement failed: no entries available\n", strmmu);
|
|
#endif
|
|
// error state?
|
|
}
|
|
|
|
#endif
|
|
|
|
static inline void address_mask(CPUState *env1, target_ulong *addr)
|
|
{
|
|
#ifdef TARGET_SPARC64
|
|
if (AM_CHECK(env1))
|
|
*addr &= 0xffffffffULL;
|
|
#endif
|
|
}
|
|
|
|
static void raise_exception(int tt)
|
|
{
|
|
env->exception_index = tt;
|
|
cpu_loop_exit();
|
|
}
|
|
|
|
void HELPER(raise_exception)(int tt)
|
|
{
|
|
raise_exception(tt);
|
|
}
|
|
|
|
static inline void set_cwp(int new_cwp)
|
|
{
|
|
cpu_set_cwp(env, new_cwp);
|
|
}
|
|
|
|
void helper_check_align(target_ulong addr, uint32_t align)
|
|
{
|
|
if (addr & align) {
|
|
#ifdef DEBUG_UNALIGNED
|
|
printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx
|
|
"\n", addr, env->pc);
|
|
#endif
|
|
raise_exception(TT_UNALIGNED);
|
|
}
|
|
}
|
|
|
|
#define F_HELPER(name, p) void helper_f##name##p(void)
|
|
|
|
#define F_BINOP(name) \
|
|
float32 helper_f ## name ## s (float32 src1, float32 src2) \
|
|
{ \
|
|
return float32_ ## name (src1, src2, &env->fp_status); \
|
|
} \
|
|
F_HELPER(name, d) \
|
|
{ \
|
|
DT0 = float64_ ## name (DT0, DT1, &env->fp_status); \
|
|
} \
|
|
F_HELPER(name, q) \
|
|
{ \
|
|
QT0 = float128_ ## name (QT0, QT1, &env->fp_status); \
|
|
}
|
|
|
|
F_BINOP(add);
|
|
F_BINOP(sub);
|
|
F_BINOP(mul);
|
|
F_BINOP(div);
|
|
#undef F_BINOP
|
|
|
|
void helper_fsmuld(float32 src1, float32 src2)
|
|
{
|
|
DT0 = float64_mul(float32_to_float64(src1, &env->fp_status),
|
|
float32_to_float64(src2, &env->fp_status),
|
|
&env->fp_status);
|
|
}
|
|
|
|
void helper_fdmulq(void)
|
|
{
|
|
QT0 = float128_mul(float64_to_float128(DT0, &env->fp_status),
|
|
float64_to_float128(DT1, &env->fp_status),
|
|
&env->fp_status);
|
|
}
|
|
|
|
float32 helper_fnegs(float32 src)
|
|
{
|
|
return float32_chs(src);
|
|
}
|
|
|
|
#ifdef TARGET_SPARC64
|
|
F_HELPER(neg, d)
|
|
{
|
|
DT0 = float64_chs(DT1);
|
|
}
|
|
|
|
F_HELPER(neg, q)
|
|
{
|
|
QT0 = float128_chs(QT1);
|
|
}
|
|
#endif
|
|
|
|
/* Integer to float conversion. */
|
|
float32 helper_fitos(int32_t src)
|
|
{
|
|
return int32_to_float32(src, &env->fp_status);
|
|
}
|
|
|
|
void helper_fitod(int32_t src)
|
|
{
|
|
DT0 = int32_to_float64(src, &env->fp_status);
|
|
}
|
|
|
|
void helper_fitoq(int32_t src)
|
|
{
|
|
QT0 = int32_to_float128(src, &env->fp_status);
|
|
}
|
|
|
|
#ifdef TARGET_SPARC64
|
|
float32 helper_fxtos(void)
|
|
{
|
|
return int64_to_float32(*((int64_t *)&DT1), &env->fp_status);
|
|
}
|
|
|
|
F_HELPER(xto, d)
|
|
{
|
|
DT0 = int64_to_float64(*((int64_t *)&DT1), &env->fp_status);
|
|
}
|
|
|
|
F_HELPER(xto, q)
|
|
{
|
|
QT0 = int64_to_float128(*((int64_t *)&DT1), &env->fp_status);
|
|
}
|
|
#endif
|
|
#undef F_HELPER
|
|
|
|
/* floating point conversion */
|
|
float32 helper_fdtos(void)
|
|
{
|
|
return float64_to_float32(DT1, &env->fp_status);
|
|
}
|
|
|
|
void helper_fstod(float32 src)
|
|
{
|
|
DT0 = float32_to_float64(src, &env->fp_status);
|
|
}
|
|
|
|
float32 helper_fqtos(void)
|
|
{
|
|
return float128_to_float32(QT1, &env->fp_status);
|
|
}
|
|
|
|
void helper_fstoq(float32 src)
|
|
{
|
|
QT0 = float32_to_float128(src, &env->fp_status);
|
|
}
|
|
|
|
void helper_fqtod(void)
|
|
{
|
|
DT0 = float128_to_float64(QT1, &env->fp_status);
|
|
}
|
|
|
|
void helper_fdtoq(void)
|
|
{
|
|
QT0 = float64_to_float128(DT1, &env->fp_status);
|
|
}
|
|
|
|
/* Float to integer conversion. */
|
|
int32_t helper_fstoi(float32 src)
|
|
{
|
|
return float32_to_int32_round_to_zero(src, &env->fp_status);
|
|
}
|
|
|
|
int32_t helper_fdtoi(void)
|
|
{
|
|
return float64_to_int32_round_to_zero(DT1, &env->fp_status);
|
|
}
|
|
|
|
int32_t helper_fqtoi(void)
|
|
{
|
|
return float128_to_int32_round_to_zero(QT1, &env->fp_status);
|
|
}
|
|
|
|
#ifdef TARGET_SPARC64
|
|
void helper_fstox(float32 src)
|
|
{
|
|
*((int64_t *)&DT0) = float32_to_int64_round_to_zero(src, &env->fp_status);
|
|
}
|
|
|
|
void helper_fdtox(void)
|
|
{
|
|
*((int64_t *)&DT0) = float64_to_int64_round_to_zero(DT1, &env->fp_status);
|
|
}
|
|
|
|
void helper_fqtox(void)
|
|
{
|
|
*((int64_t *)&DT0) = float128_to_int64_round_to_zero(QT1, &env->fp_status);
|
|
}
|
|
|
|
void helper_faligndata(void)
|
|
{
|
|
uint64_t tmp;
|
|
|
|
tmp = (*((uint64_t *)&DT0)) << ((env->gsr & 7) * 8);
|
|
/* on many architectures a shift of 64 does nothing */
|
|
if ((env->gsr & 7) != 0) {
|
|
tmp |= (*((uint64_t *)&DT1)) >> (64 - (env->gsr & 7) * 8);
|
|
}
|
|
*((uint64_t *)&DT0) = tmp;
|
|
}
|
|
|
|
#ifdef HOST_WORDS_BIGENDIAN
|
|
#define VIS_B64(n) b[7 - (n)]
|
|
#define VIS_W64(n) w[3 - (n)]
|
|
#define VIS_SW64(n) sw[3 - (n)]
|
|
#define VIS_L64(n) l[1 - (n)]
|
|
#define VIS_B32(n) b[3 - (n)]
|
|
#define VIS_W32(n) w[1 - (n)]
|
|
#else
|
|
#define VIS_B64(n) b[n]
|
|
#define VIS_W64(n) w[n]
|
|
#define VIS_SW64(n) sw[n]
|
|
#define VIS_L64(n) l[n]
|
|
#define VIS_B32(n) b[n]
|
|
#define VIS_W32(n) w[n]
|
|
#endif
|
|
|
|
typedef union {
|
|
uint8_t b[8];
|
|
uint16_t w[4];
|
|
int16_t sw[4];
|
|
uint32_t l[2];
|
|
float64 d;
|
|
} vis64;
|
|
|
|
typedef union {
|
|
uint8_t b[4];
|
|
uint16_t w[2];
|
|
uint32_t l;
|
|
float32 f;
|
|
} vis32;
|
|
|
|
void helper_fpmerge(void)
|
|
{
|
|
vis64 s, d;
|
|
|
|
s.d = DT0;
|
|
d.d = DT1;
|
|
|
|
// Reverse calculation order to handle overlap
|
|
d.VIS_B64(7) = s.VIS_B64(3);
|
|
d.VIS_B64(6) = d.VIS_B64(3);
|
|
d.VIS_B64(5) = s.VIS_B64(2);
|
|
d.VIS_B64(4) = d.VIS_B64(2);
|
|
d.VIS_B64(3) = s.VIS_B64(1);
|
|
d.VIS_B64(2) = d.VIS_B64(1);
|
|
d.VIS_B64(1) = s.VIS_B64(0);
|
|
//d.VIS_B64(0) = d.VIS_B64(0);
|
|
|
|
DT0 = d.d;
|
|
}
|
|
|
|
void helper_fmul8x16(void)
|
|
{
|
|
vis64 s, d;
|
|
uint32_t tmp;
|
|
|
|
s.d = DT0;
|
|
d.d = DT1;
|
|
|
|
#define PMUL(r) \
|
|
tmp = (int32_t)d.VIS_SW64(r) * (int32_t)s.VIS_B64(r); \
|
|
if ((tmp & 0xff) > 0x7f) \
|
|
tmp += 0x100; \
|
|
d.VIS_W64(r) = tmp >> 8;
|
|
|
|
PMUL(0);
|
|
PMUL(1);
|
|
PMUL(2);
|
|
PMUL(3);
|
|
#undef PMUL
|
|
|
|
DT0 = d.d;
|
|
}
|
|
|
|
void helper_fmul8x16al(void)
|
|
{
|
|
vis64 s, d;
|
|
uint32_t tmp;
|
|
|
|
s.d = DT0;
|
|
d.d = DT1;
|
|
|
|
#define PMUL(r) \
|
|
tmp = (int32_t)d.VIS_SW64(1) * (int32_t)s.VIS_B64(r); \
|
|
if ((tmp & 0xff) > 0x7f) \
|
|
tmp += 0x100; \
|
|
d.VIS_W64(r) = tmp >> 8;
|
|
|
|
PMUL(0);
|
|
PMUL(1);
|
|
PMUL(2);
|
|
PMUL(3);
|
|
#undef PMUL
|
|
|
|
DT0 = d.d;
|
|
}
|
|
|
|
void helper_fmul8x16au(void)
|
|
{
|
|
vis64 s, d;
|
|
uint32_t tmp;
|
|
|
|
s.d = DT0;
|
|
d.d = DT1;
|
|
|
|
#define PMUL(r) \
|
|
tmp = (int32_t)d.VIS_SW64(0) * (int32_t)s.VIS_B64(r); \
|
|
if ((tmp & 0xff) > 0x7f) \
|
|
tmp += 0x100; \
|
|
d.VIS_W64(r) = tmp >> 8;
|
|
|
|
PMUL(0);
|
|
PMUL(1);
|
|
PMUL(2);
|
|
PMUL(3);
|
|
#undef PMUL
|
|
|
|
DT0 = d.d;
|
|
}
|
|
|
|
void helper_fmul8sux16(void)
|
|
{
|
|
vis64 s, d;
|
|
uint32_t tmp;
|
|
|
|
s.d = DT0;
|
|
d.d = DT1;
|
|
|
|
#define PMUL(r) \
|
|
tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8); \
|
|
if ((tmp & 0xff) > 0x7f) \
|
|
tmp += 0x100; \
|
|
d.VIS_W64(r) = tmp >> 8;
|
|
|
|
PMUL(0);
|
|
PMUL(1);
|
|
PMUL(2);
|
|
PMUL(3);
|
|
#undef PMUL
|
|
|
|
DT0 = d.d;
|
|
}
|
|
|
|
void helper_fmul8ulx16(void)
|
|
{
|
|
vis64 s, d;
|
|
uint32_t tmp;
|
|
|
|
s.d = DT0;
|
|
d.d = DT1;
|
|
|
|
#define PMUL(r) \
|
|
tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2)); \
|
|
if ((tmp & 0xff) > 0x7f) \
|
|
tmp += 0x100; \
|
|
d.VIS_W64(r) = tmp >> 8;
|
|
|
|
PMUL(0);
|
|
PMUL(1);
|
|
PMUL(2);
|
|
PMUL(3);
|
|
#undef PMUL
|
|
|
|
DT0 = d.d;
|
|
}
|
|
|
|
void helper_fmuld8sux16(void)
|
|
{
|
|
vis64 s, d;
|
|
uint32_t tmp;
|
|
|
|
s.d = DT0;
|
|
d.d = DT1;
|
|
|
|
#define PMUL(r) \
|
|
tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8); \
|
|
if ((tmp & 0xff) > 0x7f) \
|
|
tmp += 0x100; \
|
|
d.VIS_L64(r) = tmp;
|
|
|
|
// Reverse calculation order to handle overlap
|
|
PMUL(1);
|
|
PMUL(0);
|
|
#undef PMUL
|
|
|
|
DT0 = d.d;
|
|
}
|
|
|
|
void helper_fmuld8ulx16(void)
|
|
{
|
|
vis64 s, d;
|
|
uint32_t tmp;
|
|
|
|
s.d = DT0;
|
|
d.d = DT1;
|
|
|
|
#define PMUL(r) \
|
|
tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2)); \
|
|
if ((tmp & 0xff) > 0x7f) \
|
|
tmp += 0x100; \
|
|
d.VIS_L64(r) = tmp;
|
|
|
|
// Reverse calculation order to handle overlap
|
|
PMUL(1);
|
|
PMUL(0);
|
|
#undef PMUL
|
|
|
|
DT0 = d.d;
|
|
}
|
|
|
|
void helper_fexpand(void)
|
|
{
|
|
vis32 s;
|
|
vis64 d;
|
|
|
|
s.l = (uint32_t)(*(uint64_t *)&DT0 & 0xffffffff);
|
|
d.d = DT1;
|
|
d.VIS_W64(0) = s.VIS_B32(0) << 4;
|
|
d.VIS_W64(1) = s.VIS_B32(1) << 4;
|
|
d.VIS_W64(2) = s.VIS_B32(2) << 4;
|
|
d.VIS_W64(3) = s.VIS_B32(3) << 4;
|
|
|
|
DT0 = d.d;
|
|
}
|
|
|
|
#define VIS_HELPER(name, F) \
|
|
void name##16(void) \
|
|
{ \
|
|
vis64 s, d; \
|
|
\
|
|
s.d = DT0; \
|
|
d.d = DT1; \
|
|
\
|
|
d.VIS_W64(0) = F(d.VIS_W64(0), s.VIS_W64(0)); \
|
|
d.VIS_W64(1) = F(d.VIS_W64(1), s.VIS_W64(1)); \
|
|
d.VIS_W64(2) = F(d.VIS_W64(2), s.VIS_W64(2)); \
|
|
d.VIS_W64(3) = F(d.VIS_W64(3), s.VIS_W64(3)); \
|
|
\
|
|
DT0 = d.d; \
|
|
} \
|
|
\
|
|
uint32_t name##16s(uint32_t src1, uint32_t src2) \
|
|
{ \
|
|
vis32 s, d; \
|
|
\
|
|
s.l = src1; \
|
|
d.l = src2; \
|
|
\
|
|
d.VIS_W32(0) = F(d.VIS_W32(0), s.VIS_W32(0)); \
|
|
d.VIS_W32(1) = F(d.VIS_W32(1), s.VIS_W32(1)); \
|
|
\
|
|
return d.l; \
|
|
} \
|
|
\
|
|
void name##32(void) \
|
|
{ \
|
|
vis64 s, d; \
|
|
\
|
|
s.d = DT0; \
|
|
d.d = DT1; \
|
|
\
|
|
d.VIS_L64(0) = F(d.VIS_L64(0), s.VIS_L64(0)); \
|
|
d.VIS_L64(1) = F(d.VIS_L64(1), s.VIS_L64(1)); \
|
|
\
|
|
DT0 = d.d; \
|
|
} \
|
|
\
|
|
uint32_t name##32s(uint32_t src1, uint32_t src2) \
|
|
{ \
|
|
vis32 s, d; \
|
|
\
|
|
s.l = src1; \
|
|
d.l = src2; \
|
|
\
|
|
d.l = F(d.l, s.l); \
|
|
\
|
|
return d.l; \
|
|
}
|
|
|
|
#define FADD(a, b) ((a) + (b))
|
|
#define FSUB(a, b) ((a) - (b))
|
|
VIS_HELPER(helper_fpadd, FADD)
|
|
VIS_HELPER(helper_fpsub, FSUB)
|
|
|
|
#define VIS_CMPHELPER(name, F) \
|
|
void name##16(void) \
|
|
{ \
|
|
vis64 s, d; \
|
|
\
|
|
s.d = DT0; \
|
|
d.d = DT1; \
|
|
\
|
|
d.VIS_W64(0) = F(d.VIS_W64(0), s.VIS_W64(0))? 1: 0; \
|
|
d.VIS_W64(0) |= F(d.VIS_W64(1), s.VIS_W64(1))? 2: 0; \
|
|
d.VIS_W64(0) |= F(d.VIS_W64(2), s.VIS_W64(2))? 4: 0; \
|
|
d.VIS_W64(0) |= F(d.VIS_W64(3), s.VIS_W64(3))? 8: 0; \
|
|
\
|
|
DT0 = d.d; \
|
|
} \
|
|
\
|
|
void name##32(void) \
|
|
{ \
|
|
vis64 s, d; \
|
|
\
|
|
s.d = DT0; \
|
|
d.d = DT1; \
|
|
\
|
|
d.VIS_L64(0) = F(d.VIS_L64(0), s.VIS_L64(0))? 1: 0; \
|
|
d.VIS_L64(0) |= F(d.VIS_L64(1), s.VIS_L64(1))? 2: 0; \
|
|
\
|
|
DT0 = d.d; \
|
|
}
|
|
|
|
#define FCMPGT(a, b) ((a) > (b))
|
|
#define FCMPEQ(a, b) ((a) == (b))
|
|
#define FCMPLE(a, b) ((a) <= (b))
|
|
#define FCMPNE(a, b) ((a) != (b))
|
|
|
|
VIS_CMPHELPER(helper_fcmpgt, FCMPGT)
|
|
VIS_CMPHELPER(helper_fcmpeq, FCMPEQ)
|
|
VIS_CMPHELPER(helper_fcmple, FCMPLE)
|
|
VIS_CMPHELPER(helper_fcmpne, FCMPNE)
|
|
#endif
|
|
|
|
void helper_check_ieee_exceptions(void)
|
|
{
|
|
target_ulong status;
|
|
|
|
status = get_float_exception_flags(&env->fp_status);
|
|
if (status) {
|
|
/* Copy IEEE 754 flags into FSR */
|
|
if (status & float_flag_invalid)
|
|
env->fsr |= FSR_NVC;
|
|
if (status & float_flag_overflow)
|
|
env->fsr |= FSR_OFC;
|
|
if (status & float_flag_underflow)
|
|
env->fsr |= FSR_UFC;
|
|
if (status & float_flag_divbyzero)
|
|
env->fsr |= FSR_DZC;
|
|
if (status & float_flag_inexact)
|
|
env->fsr |= FSR_NXC;
|
|
|
|
if ((env->fsr & FSR_CEXC_MASK) & ((env->fsr & FSR_TEM_MASK) >> 23)) {
|
|
/* Unmasked exception, generate a trap */
|
|
env->fsr |= FSR_FTT_IEEE_EXCP;
|
|
raise_exception(TT_FP_EXCP);
|
|
} else {
|
|
/* Accumulate exceptions */
|
|
env->fsr |= (env->fsr & FSR_CEXC_MASK) << 5;
|
|
}
|
|
}
|
|
}
|
|
|
|
void helper_clear_float_exceptions(void)
|
|
{
|
|
set_float_exception_flags(0, &env->fp_status);
|
|
}
|
|
|
|
float32 helper_fabss(float32 src)
|
|
{
|
|
return float32_abs(src);
|
|
}
|
|
|
|
#ifdef TARGET_SPARC64
|
|
void helper_fabsd(void)
|
|
{
|
|
DT0 = float64_abs(DT1);
|
|
}
|
|
|
|
void helper_fabsq(void)
|
|
{
|
|
QT0 = float128_abs(QT1);
|
|
}
|
|
#endif
|
|
|
|
float32 helper_fsqrts(float32 src)
|
|
{
|
|
return float32_sqrt(src, &env->fp_status);
|
|
}
|
|
|
|
void helper_fsqrtd(void)
|
|
{
|
|
DT0 = float64_sqrt(DT1, &env->fp_status);
|
|
}
|
|
|
|
void helper_fsqrtq(void)
|
|
{
|
|
QT0 = float128_sqrt(QT1, &env->fp_status);
|
|
}
|
|
|
|
#define GEN_FCMP(name, size, reg1, reg2, FS, TRAP) \
|
|
void glue(helper_, name) (void) \
|
|
{ \
|
|
target_ulong new_fsr; \
|
|
\
|
|
env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \
|
|
switch (glue(size, _compare) (reg1, reg2, &env->fp_status)) { \
|
|
case float_relation_unordered: \
|
|
new_fsr = (FSR_FCC1 | FSR_FCC0) << FS; \
|
|
if ((env->fsr & FSR_NVM) || TRAP) { \
|
|
env->fsr |= new_fsr; \
|
|
env->fsr |= FSR_NVC; \
|
|
env->fsr |= FSR_FTT_IEEE_EXCP; \
|
|
raise_exception(TT_FP_EXCP); \
|
|
} else { \
|
|
env->fsr |= FSR_NVA; \
|
|
} \
|
|
break; \
|
|
case float_relation_less: \
|
|
new_fsr = FSR_FCC0 << FS; \
|
|
break; \
|
|
case float_relation_greater: \
|
|
new_fsr = FSR_FCC1 << FS; \
|
|
break; \
|
|
default: \
|
|
new_fsr = 0; \
|
|
break; \
|
|
} \
|
|
env->fsr |= new_fsr; \
|
|
}
|
|
#define GEN_FCMPS(name, size, FS, TRAP) \
|
|
void glue(helper_, name)(float32 src1, float32 src2) \
|
|
{ \
|
|
target_ulong new_fsr; \
|
|
\
|
|
env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \
|
|
switch (glue(size, _compare) (src1, src2, &env->fp_status)) { \
|
|
case float_relation_unordered: \
|
|
new_fsr = (FSR_FCC1 | FSR_FCC0) << FS; \
|
|
if ((env->fsr & FSR_NVM) || TRAP) { \
|
|
env->fsr |= new_fsr; \
|
|
env->fsr |= FSR_NVC; \
|
|
env->fsr |= FSR_FTT_IEEE_EXCP; \
|
|
raise_exception(TT_FP_EXCP); \
|
|
} else { \
|
|
env->fsr |= FSR_NVA; \
|
|
} \
|
|
break; \
|
|
case float_relation_less: \
|
|
new_fsr = FSR_FCC0 << FS; \
|
|
break; \
|
|
case float_relation_greater: \
|
|
new_fsr = FSR_FCC1 << FS; \
|
|
break; \
|
|
default: \
|
|
new_fsr = 0; \
|
|
break; \
|
|
} \
|
|
env->fsr |= new_fsr; \
|
|
}
|
|
|
|
GEN_FCMPS(fcmps, float32, 0, 0);
|
|
GEN_FCMP(fcmpd, float64, DT0, DT1, 0, 0);
|
|
|
|
GEN_FCMPS(fcmpes, float32, 0, 1);
|
|
GEN_FCMP(fcmped, float64, DT0, DT1, 0, 1);
|
|
|
|
GEN_FCMP(fcmpq, float128, QT0, QT1, 0, 0);
|
|
GEN_FCMP(fcmpeq, float128, QT0, QT1, 0, 1);
|
|
|
|
static uint32_t compute_all_flags(void)
|
|
{
|
|
return env->psr & PSR_ICC;
|
|
}
|
|
|
|
static uint32_t compute_C_flags(void)
|
|
{
|
|
return env->psr & PSR_CARRY;
|
|
}
|
|
|
|
static inline uint32_t get_NZ_icc(target_ulong dst)
|
|
{
|
|
uint32_t ret = 0;
|
|
|
|
if (!(dst & 0xffffffffULL))
|
|
ret |= PSR_ZERO;
|
|
if ((int32_t) (dst & 0xffffffffULL) < 0)
|
|
ret |= PSR_NEG;
|
|
return ret;
|
|
}
|
|
|
|
#ifdef TARGET_SPARC64
|
|
static uint32_t compute_all_flags_xcc(void)
|
|
{
|
|
return env->xcc & PSR_ICC;
|
|
}
|
|
|
|
static uint32_t compute_C_flags_xcc(void)
|
|
{
|
|
return env->xcc & PSR_CARRY;
|
|
}
|
|
|
|
static inline uint32_t get_NZ_xcc(target_ulong dst)
|
|
{
|
|
uint32_t ret = 0;
|
|
|
|
if (!dst)
|
|
ret |= PSR_ZERO;
|
|
if ((int64_t)dst < 0)
|
|
ret |= PSR_NEG;
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static inline uint32_t get_V_div_icc(target_ulong src2)
|
|
{
|
|
uint32_t ret = 0;
|
|
|
|
if (src2 != 0)
|
|
ret |= PSR_OVF;
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t compute_all_div(void)
|
|
{
|
|
uint32_t ret;
|
|
|
|
ret = get_NZ_icc(CC_DST);
|
|
ret |= get_V_div_icc(CC_SRC2);
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t compute_C_div(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* carry = (src1[31] & src2[31]) | ( ~dst[31] & (src1[31] | src2[31])) */
|
|
static inline uint32_t get_C_add_icc(target_ulong dst, target_ulong src1,
|
|
target_ulong src2)
|
|
{
|
|
uint32_t ret = 0;
|
|
|
|
if (((src1 & (1ULL << 31)) & (src2 & (1ULL << 31)))
|
|
| ((~(dst & (1ULL << 31)))
|
|
& ((src1 & (1ULL << 31)) | (src2 & (1ULL << 31)))))
|
|
ret |= PSR_CARRY;
|
|
return ret;
|
|
}
|
|
|
|
static inline uint32_t get_V_add_icc(target_ulong dst, target_ulong src1,
|
|
target_ulong src2)
|
|
{
|
|
uint32_t ret = 0;
|
|
|
|
if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1ULL << 31))
|
|
ret |= PSR_OVF;
|
|
return ret;
|
|
}
|
|
|
|
#ifdef TARGET_SPARC64
|
|
static inline uint32_t get_C_add_xcc(target_ulong dst, target_ulong src1)
|
|
{
|
|
uint32_t ret = 0;
|
|
|
|
if (dst < src1)
|
|
ret |= PSR_CARRY;
|
|
return ret;
|
|
}
|
|
|
|
static inline uint32_t get_V_add_xcc(target_ulong dst, target_ulong src1,
|
|
target_ulong src2)
|
|
{
|
|
uint32_t ret = 0;
|
|
|
|
if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1ULL << 63))
|
|
ret |= PSR_OVF;
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t compute_all_add_xcc(void)
|
|
{
|
|
uint32_t ret;
|
|
|
|
ret = get_NZ_xcc(CC_DST);
|
|
ret |= get_C_add_xcc(CC_DST, CC_SRC);
|
|
ret |= get_V_add_xcc(CC_DST, CC_SRC, CC_SRC2);
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t compute_C_add_xcc(void)
|
|
{
|
|
return get_C_add_xcc(CC_DST, CC_SRC);
|
|
}
|
|
#endif
|
|
|
|
static uint32_t compute_all_add(void)
|
|
{
|
|
uint32_t ret;
|
|
|
|
ret = get_NZ_icc(CC_DST);
|
|
ret |= get_C_add_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t compute_C_add(void)
|
|
{
|
|
return get_C_add_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
}
|
|
|
|
#ifdef TARGET_SPARC64
|
|
static uint32_t compute_all_addx_xcc(void)
|
|
{
|
|
uint32_t ret;
|
|
|
|
ret = get_NZ_xcc(CC_DST);
|
|
ret |= get_C_add_xcc(CC_DST - CC_SRC2, CC_SRC);
|
|
ret |= get_C_add_xcc(CC_DST, CC_SRC);
|
|
ret |= get_V_add_xcc(CC_DST, CC_SRC, CC_SRC2);
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t compute_C_addx_xcc(void)
|
|
{
|
|
uint32_t ret;
|
|
|
|
ret = get_C_add_xcc(CC_DST - CC_SRC2, CC_SRC);
|
|
ret |= get_C_add_xcc(CC_DST, CC_SRC);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static inline uint32_t get_V_tag_icc(target_ulong src1, target_ulong src2)
|
|
{
|
|
uint32_t ret = 0;
|
|
|
|
if ((src1 | src2) & 0x3)
|
|
ret |= PSR_OVF;
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t compute_all_tadd(void)
|
|
{
|
|
uint32_t ret;
|
|
|
|
ret = get_NZ_icc(CC_DST);
|
|
ret |= get_C_add_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
ret |= get_V_tag_icc(CC_SRC, CC_SRC2);
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t compute_C_tadd(void)
|
|
{
|
|
return get_C_add_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
}
|
|
|
|
static uint32_t compute_all_taddtv(void)
|
|
{
|
|
uint32_t ret;
|
|
|
|
ret = get_NZ_icc(CC_DST);
|
|
ret |= get_C_add_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t compute_C_taddtv(void)
|
|
{
|
|
return get_C_add_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
}
|
|
|
|
/* carry = (~src1[31] & src2[31]) | ( dst[31] & (~src1[31] | src2[31])) */
|
|
static inline uint32_t get_C_sub_icc(target_ulong dst, target_ulong src1,
|
|
target_ulong src2)
|
|
{
|
|
uint32_t ret = 0;
|
|
|
|
if (((~(src1 & (1ULL << 31))) & (src2 & (1ULL << 31)))
|
|
| ((dst & (1ULL << 31)) & (( ~(src1 & (1ULL << 31)))
|
|
| (src2 & (1ULL << 31)))))
|
|
ret |= PSR_CARRY;
|
|
return ret;
|
|
}
|
|
|
|
static inline uint32_t get_V_sub_icc(target_ulong dst, target_ulong src1,
|
|
target_ulong src2)
|
|
{
|
|
uint32_t ret = 0;
|
|
|
|
if (((src1 ^ src2) & (src1 ^ dst)) & (1ULL << 31))
|
|
ret |= PSR_OVF;
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifdef TARGET_SPARC64
|
|
static inline uint32_t get_C_sub_xcc(target_ulong src1, target_ulong src2)
|
|
{
|
|
uint32_t ret = 0;
|
|
|
|
if (src1 < src2)
|
|
ret |= PSR_CARRY;
|
|
return ret;
|
|
}
|
|
|
|
static inline uint32_t get_V_sub_xcc(target_ulong dst, target_ulong src1,
|
|
target_ulong src2)
|
|
{
|
|
uint32_t ret = 0;
|
|
|
|
if (((src1 ^ src2) & (src1 ^ dst)) & (1ULL << 63))
|
|
ret |= PSR_OVF;
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t compute_all_sub_xcc(void)
|
|
{
|
|
uint32_t ret;
|
|
|
|
ret = get_NZ_xcc(CC_DST);
|
|
ret |= get_C_sub_xcc(CC_SRC, CC_SRC2);
|
|
ret |= get_V_sub_xcc(CC_DST, CC_SRC, CC_SRC2);
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t compute_C_sub_xcc(void)
|
|
{
|
|
return get_C_sub_xcc(CC_SRC, CC_SRC2);
|
|
}
|
|
#endif
|
|
|
|
static uint32_t compute_all_sub(void)
|
|
{
|
|
uint32_t ret;
|
|
|
|
ret = get_NZ_icc(CC_DST);
|
|
ret |= get_C_sub_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t compute_C_sub(void)
|
|
{
|
|
return get_C_sub_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
}
|
|
|
|
#ifdef TARGET_SPARC64
|
|
static uint32_t compute_all_subx_xcc(void)
|
|
{
|
|
uint32_t ret;
|
|
|
|
ret = get_NZ_xcc(CC_DST);
|
|
ret |= get_C_sub_xcc(CC_DST - CC_SRC2, CC_SRC);
|
|
ret |= get_C_sub_xcc(CC_DST, CC_SRC2);
|
|
ret |= get_V_sub_xcc(CC_DST, CC_SRC, CC_SRC2);
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t compute_C_subx_xcc(void)
|
|
{
|
|
uint32_t ret;
|
|
|
|
ret = get_C_sub_xcc(CC_DST - CC_SRC2, CC_SRC);
|
|
ret |= get_C_sub_xcc(CC_DST, CC_SRC2);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static uint32_t compute_all_tsub(void)
|
|
{
|
|
uint32_t ret;
|
|
|
|
ret = get_NZ_icc(CC_DST);
|
|
ret |= get_C_sub_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
ret |= get_V_tag_icc(CC_SRC, CC_SRC2);
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t compute_C_tsub(void)
|
|
{
|
|
return get_C_sub_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
}
|
|
|
|
static uint32_t compute_all_tsubtv(void)
|
|
{
|
|
uint32_t ret;
|
|
|
|
ret = get_NZ_icc(CC_DST);
|
|
ret |= get_C_sub_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t compute_C_tsubtv(void)
|
|
{
|
|
return get_C_sub_icc(CC_DST, CC_SRC, CC_SRC2);
|
|
}
|
|
|
|
static uint32_t compute_all_logic(void)
|
|
{
|
|
return get_NZ_icc(CC_DST);
|
|
}
|
|
|
|
static uint32_t compute_C_logic(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#ifdef TARGET_SPARC64
|
|
static uint32_t compute_all_logic_xcc(void)
|
|
{
|
|
return get_NZ_xcc(CC_DST);
|
|
}
|
|
#endif
|
|
|
|
typedef struct CCTable {
|
|
uint32_t (*compute_all)(void); /* return all the flags */
|
|
uint32_t (*compute_c)(void); /* return the C flag */
|
|
} CCTable;
|
|
|
|
static const CCTable icc_table[CC_OP_NB] = {
|
|
/* CC_OP_DYNAMIC should never happen */
|
|
[CC_OP_FLAGS] = { compute_all_flags, compute_C_flags },
|
|
[CC_OP_DIV] = { compute_all_div, compute_C_div },
|
|
[CC_OP_ADD] = { compute_all_add, compute_C_add },
|
|
[CC_OP_ADDX] = { compute_all_add, compute_C_add },
|
|
[CC_OP_TADD] = { compute_all_tadd, compute_C_tadd },
|
|
[CC_OP_TADDTV] = { compute_all_taddtv, compute_C_taddtv },
|
|
[CC_OP_SUB] = { compute_all_sub, compute_C_sub },
|
|
[CC_OP_SUBX] = { compute_all_sub, compute_C_sub },
|
|
[CC_OP_TSUB] = { compute_all_tsub, compute_C_tsub },
|
|
[CC_OP_TSUBTV] = { compute_all_tsubtv, compute_C_tsubtv },
|
|
[CC_OP_LOGIC] = { compute_all_logic, compute_C_logic },
|
|
};
|
|
|
|
#ifdef TARGET_SPARC64
|
|
static const CCTable xcc_table[CC_OP_NB] = {
|
|
/* CC_OP_DYNAMIC should never happen */
|
|
[CC_OP_FLAGS] = { compute_all_flags_xcc, compute_C_flags_xcc },
|
|
[CC_OP_DIV] = { compute_all_logic_xcc, compute_C_logic },
|
|
[CC_OP_ADD] = { compute_all_add_xcc, compute_C_add_xcc },
|
|
[CC_OP_ADDX] = { compute_all_addx_xcc, compute_C_addx_xcc },
|
|
[CC_OP_TADD] = { compute_all_add_xcc, compute_C_add_xcc },
|
|
[CC_OP_TADDTV] = { compute_all_add_xcc, compute_C_add_xcc },
|
|
[CC_OP_SUB] = { compute_all_sub_xcc, compute_C_sub_xcc },
|
|
[CC_OP_SUBX] = { compute_all_subx_xcc, compute_C_subx_xcc },
|
|
[CC_OP_TSUB] = { compute_all_sub_xcc, compute_C_sub_xcc },
|
|
[CC_OP_TSUBTV] = { compute_all_sub_xcc, compute_C_sub_xcc },
|
|
[CC_OP_LOGIC] = { compute_all_logic_xcc, compute_C_logic },
|
|
};
|
|
#endif
|
|
|
|
void helper_compute_psr(void)
|
|
{
|
|
uint32_t new_psr;
|
|
|
|
new_psr = icc_table[CC_OP].compute_all();
|
|
env->psr = new_psr;
|
|
#ifdef TARGET_SPARC64
|
|
new_psr = xcc_table[CC_OP].compute_all();
|
|
env->xcc = new_psr;
|
|
#endif
|
|
CC_OP = CC_OP_FLAGS;
|
|
}
|
|
|
|
uint32_t helper_compute_C_icc(void)
|
|
{
|
|
uint32_t ret;
|
|
|
|
ret = icc_table[CC_OP].compute_c() >> PSR_CARRY_SHIFT;
|
|
return ret;
|
|
}
|
|
|
|
#ifdef TARGET_SPARC64
|
|
GEN_FCMPS(fcmps_fcc1, float32, 22, 0);
|
|
GEN_FCMP(fcmpd_fcc1, float64, DT0, DT1, 22, 0);
|
|
GEN_FCMP(fcmpq_fcc1, float128, QT0, QT1, 22, 0);
|
|
|
|
GEN_FCMPS(fcmps_fcc2, float32, 24, 0);
|
|
GEN_FCMP(fcmpd_fcc2, float64, DT0, DT1, 24, 0);
|
|
GEN_FCMP(fcmpq_fcc2, float128, QT0, QT1, 24, 0);
|
|
|
|
GEN_FCMPS(fcmps_fcc3, float32, 26, 0);
|
|
GEN_FCMP(fcmpd_fcc3, float64, DT0, DT1, 26, 0);
|
|
GEN_FCMP(fcmpq_fcc3, float128, QT0, QT1, 26, 0);
|
|
|
|
GEN_FCMPS(fcmpes_fcc1, float32, 22, 1);
|
|
GEN_FCMP(fcmped_fcc1, float64, DT0, DT1, 22, 1);
|
|
GEN_FCMP(fcmpeq_fcc1, float128, QT0, QT1, 22, 1);
|
|
|
|
GEN_FCMPS(fcmpes_fcc2, float32, 24, 1);
|
|
GEN_FCMP(fcmped_fcc2, float64, DT0, DT1, 24, 1);
|
|
GEN_FCMP(fcmpeq_fcc2, float128, QT0, QT1, 24, 1);
|
|
|
|
GEN_FCMPS(fcmpes_fcc3, float32, 26, 1);
|
|
GEN_FCMP(fcmped_fcc3, float64, DT0, DT1, 26, 1);
|
|
GEN_FCMP(fcmpeq_fcc3, float128, QT0, QT1, 26, 1);
|
|
#endif
|
|
#undef GEN_FCMPS
|
|
|
|
#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) && \
|
|
defined(DEBUG_MXCC)
|
|
static void dump_mxcc(CPUState *env)
|
|
{
|
|
printf("mxccdata: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64
|
|
"\n",
|
|
env->mxccdata[0], env->mxccdata[1],
|
|
env->mxccdata[2], env->mxccdata[3]);
|
|
printf("mxccregs: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64
|
|
"\n"
|
|
" %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64
|
|
"\n",
|
|
env->mxccregs[0], env->mxccregs[1],
|
|
env->mxccregs[2], env->mxccregs[3],
|
|
env->mxccregs[4], env->mxccregs[5],
|
|
env->mxccregs[6], env->mxccregs[7]);
|
|
}
|
|
#endif
|
|
|
|
#if (defined(TARGET_SPARC64) || !defined(CONFIG_USER_ONLY)) \
|
|
&& defined(DEBUG_ASI)
|
|
static void dump_asi(const char *txt, target_ulong addr, int asi, int size,
|
|
uint64_t r1)
|
|
{
|
|
switch (size)
|
|
{
|
|
case 1:
|
|
DPRINTF_ASI("%s "TARGET_FMT_lx " asi 0x%02x = %02" PRIx64 "\n", txt,
|
|
addr, asi, r1 & 0xff);
|
|
break;
|
|
case 2:
|
|
DPRINTF_ASI("%s "TARGET_FMT_lx " asi 0x%02x = %04" PRIx64 "\n", txt,
|
|
addr, asi, r1 & 0xffff);
|
|
break;
|
|
case 4:
|
|
DPRINTF_ASI("%s "TARGET_FMT_lx " asi 0x%02x = %08" PRIx64 "\n", txt,
|
|
addr, asi, r1 & 0xffffffff);
|
|
break;
|
|
case 8:
|
|
DPRINTF_ASI("%s "TARGET_FMT_lx " asi 0x%02x = %016" PRIx64 "\n", txt,
|
|
addr, asi, r1);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef TARGET_SPARC64
|
|
#ifndef CONFIG_USER_ONLY
|
|
uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
|
|
{
|
|
uint64_t ret = 0;
|
|
#if defined(DEBUG_MXCC) || defined(DEBUG_ASI)
|
|
uint32_t last_addr = addr;
|
|
#endif
|
|
|
|
helper_check_align(addr, size - 1);
|
|
switch (asi) {
|
|
case 2: /* SuperSparc MXCC registers */
|
|
switch (addr) {
|
|
case 0x01c00a00: /* MXCC control register */
|
|
if (size == 8)
|
|
ret = env->mxccregs[3];
|
|
else
|
|
DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
|
|
size);
|
|
break;
|
|
case 0x01c00a04: /* MXCC control register */
|
|
if (size == 4)
|
|
ret = env->mxccregs[3];
|
|
else
|
|
DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
|
|
size);
|
|
break;
|
|
case 0x01c00c00: /* Module reset register */
|
|
if (size == 8) {
|
|
ret = env->mxccregs[5];
|
|
// should we do something here?
|
|
} else
|
|
DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
|
|
size);
|
|
break;
|
|
case 0x01c00f00: /* MBus port address register */
|
|
if (size == 8)
|
|
ret = env->mxccregs[7];
|
|
else
|
|
DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
|
|
size);
|
|
break;
|
|
default:
|
|
DPRINTF_MXCC("%08x: unimplemented address, size: %d\n", addr,
|
|
size);
|
|
break;
|
|
}
|
|
DPRINTF_MXCC("asi = %d, size = %d, sign = %d, "
|
|
"addr = %08x -> ret = %" PRIx64 ","
|
|
"addr = %08x\n", asi, size, sign, last_addr, ret, addr);
|
|
#ifdef DEBUG_MXCC
|
|
dump_mxcc(env);
|
|
#endif
|
|
break;
|
|
case 3: /* MMU probe */
|
|
{
|
|
int mmulev;
|
|
|
|
mmulev = (addr >> 8) & 15;
|
|
if (mmulev > 4)
|
|
ret = 0;
|
|
else
|
|
ret = mmu_probe(env, addr, mmulev);
|
|
DPRINTF_MMU("mmu_probe: 0x%08x (lev %d) -> 0x%08" PRIx64 "\n",
|
|
addr, mmulev, ret);
|
|
}
|
|
break;
|
|
case 4: /* read MMU regs */
|
|
{
|
|
int reg = (addr >> 8) & 0x1f;
|
|
|
|
ret = env->mmuregs[reg];
|
|
if (reg == 3) /* Fault status cleared on read */
|
|
env->mmuregs[3] = 0;
|
|
else if (reg == 0x13) /* Fault status read */
|
|
ret = env->mmuregs[3];
|
|
else if (reg == 0x14) /* Fault address read */
|
|
ret = env->mmuregs[4];
|
|
DPRINTF_MMU("mmu_read: reg[%d] = 0x%08" PRIx64 "\n", reg, ret);
|
|
}
|
|
break;
|
|
case 5: // Turbosparc ITLB Diagnostic
|
|
case 6: // Turbosparc DTLB Diagnostic
|
|
case 7: // Turbosparc IOTLB Diagnostic
|
|
break;
|
|
case 9: /* Supervisor code access */
|
|
switch(size) {
|
|
case 1:
|
|
ret = ldub_code(addr);
|
|
break;
|
|
case 2:
|
|
ret = lduw_code(addr);
|
|
break;
|
|
default:
|
|
case 4:
|
|
ret = ldl_code(addr);
|
|
break;
|
|
case 8:
|
|
ret = ldq_code(addr);
|
|
break;
|
|
}
|
|
break;
|
|
case 0xa: /* User data access */
|
|
switch(size) {
|
|
case 1:
|
|
ret = ldub_user(addr);
|
|
break;
|
|
case 2:
|
|
ret = lduw_user(addr);
|
|
break;
|
|
default:
|
|
case 4:
|
|
ret = ldl_user(addr);
|
|
break;
|
|
case 8:
|
|
ret = ldq_user(addr);
|
|
break;
|
|
}
|
|
break;
|
|
case 0xb: /* Supervisor data access */
|
|
switch(size) {
|
|
case 1:
|
|
ret = ldub_kernel(addr);
|
|
break;
|
|
case 2:
|
|
ret = lduw_kernel(addr);
|
|
break;
|
|
default:
|
|
case 4:
|
|
ret = ldl_kernel(addr);
|
|
break;
|
|
case 8:
|
|
ret = ldq_kernel(addr);
|
|
break;
|
|
}
|
|
break;
|
|
case 0xc: /* I-cache tag */
|
|
case 0xd: /* I-cache data */
|
|
case 0xe: /* D-cache tag */
|
|
case 0xf: /* D-cache data */
|
|
break;
|
|
case 0x20: /* MMU passthrough */
|
|
switch(size) {
|
|
case 1:
|
|
ret = ldub_phys(addr);
|
|
break;
|
|
case 2:
|
|
ret = lduw_phys(addr);
|
|
break;
|
|
default:
|
|
case 4:
|
|
ret = ldl_phys(addr);
|
|
break;
|
|
case 8:
|
|
ret = ldq_phys(addr);
|
|
break;
|
|
}
|
|
break;
|
|
case 0x21 ... 0x2f: /* MMU passthrough, 0x100000000 to 0xfffffffff */
|
|
switch(size) {
|
|
case 1:
|
|
ret = ldub_phys((target_phys_addr_t)addr
|
|
| ((target_phys_addr_t)(asi & 0xf) << 32));
|
|
break;
|
|
case 2:
|
|
ret = lduw_phys((target_phys_addr_t)addr
|
|
| ((target_phys_addr_t)(asi & 0xf) << 32));
|
|
break;
|
|
default:
|
|
case 4:
|
|
ret = ldl_phys((target_phys_addr_t)addr
|
|
| ((target_phys_addr_t)(asi & 0xf) << 32));
|
|
break;
|
|
case 8:
|
|
ret = ldq_phys((target_phys_addr_t)addr
|
|
| ((target_phys_addr_t)(asi & 0xf) << 32));
|
|
break;
|
|
}
|
|
break;
|
|
case 0x30: // Turbosparc secondary cache diagnostic
|
|
case 0x31: // Turbosparc RAM snoop
|
|
case 0x32: // Turbosparc page table descriptor diagnostic
|
|
case 0x39: /* data cache diagnostic register */
|
|
ret = 0;
|
|
break;
|
|
case 0x38: /* SuperSPARC MMU Breakpoint Control Registers */
|
|
{
|
|
int reg = (addr >> 8) & 3;
|
|
|
|
switch(reg) {
|
|
case 0: /* Breakpoint Value (Addr) */
|
|
ret = env->mmubpregs[reg];
|
|
break;
|
|
case 1: /* Breakpoint Mask */
|
|
ret = env->mmubpregs[reg];
|
|
break;
|
|
case 2: /* Breakpoint Control */
|
|
ret = env->mmubpregs[reg];
|
|
break;
|
|
case 3: /* Breakpoint Status */
|
|
ret = env->mmubpregs[reg];
|
|
env->mmubpregs[reg] = 0ULL;
|
|
break;
|
|
}
|
|
DPRINTF_MMU("read breakpoint reg[%d] 0x%016" PRIx64 "\n", reg,
|
|
ret);
|
|
}
|
|
break;
|
|
case 8: /* User code access, XXX */
|
|
default:
|
|
do_unassigned_access(addr, 0, 0, asi, size);
|
|
ret = 0;
|
|
break;
|
|
}
|
|
if (sign) {
|
|
switch(size) {
|
|
case 1:
|
|
ret = (int8_t) ret;
|
|
break;
|
|
case 2:
|
|
ret = (int16_t) ret;
|
|
break;
|
|
case 4:
|
|
ret = (int32_t) ret;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#ifdef DEBUG_ASI
|
|
dump_asi("read ", last_addr, asi, size, ret);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
void helper_st_asi(target_ulong addr, uint64_t val, int asi, int size)
|
|
{
|
|
helper_check_align(addr, size - 1);
|
|
switch(asi) {
|
|
case 2: /* SuperSparc MXCC registers */
|
|
switch (addr) {
|
|
case 0x01c00000: /* MXCC stream data register 0 */
|
|
if (size == 8)
|
|
env->mxccdata[0] = val;
|
|
else
|
|
DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
|
|
size);
|
|
break;
|
|
case 0x01c00008: /* MXCC stream data register 1 */
|
|
if (size == 8)
|
|
env->mxccdata[1] = val;
|
|
else
|
|
DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
|
|
size);
|
|
break;
|
|
case 0x01c00010: /* MXCC stream data register 2 */
|
|
if (size == 8)
|
|
env->mxccdata[2] = val;
|
|
else
|
|
DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
|
|
size);
|
|
break;
|
|
case 0x01c00018: /* MXCC stream data register 3 */
|
|
if (size == 8)
|
|
env->mxccdata[3] = val;
|
|
else
|
|
DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
|
|
size);
|
|
break;
|
|
case 0x01c00100: /* MXCC stream source */
|
|
if (size == 8)
|
|
env->mxccregs[0] = val;
|
|
else
|
|
DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
|
|
size);
|
|
env->mxccdata[0] = ldq_phys((env->mxccregs[0] & 0xffffffffULL) +
|
|
0);
|
|
env->mxccdata[1] = ldq_phys((env->mxccregs[0] & 0xffffffffULL) +
|
|
8);
|
|
env->mxccdata[2] = ldq_phys((env->mxccregs[0] & 0xffffffffULL) +
|
|
16);
|
|
env->mxccdata[3] = ldq_phys((env->mxccregs[0] & 0xffffffffULL) +
|
|
24);
|
|
break;
|
|
case 0x01c00200: /* MXCC stream destination */
|
|
if (size == 8)
|
|
env->mxccregs[1] = val;
|
|
else
|
|
DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
|
|
size);
|
|
stq_phys((env->mxccregs[1] & 0xffffffffULL) + 0,
|
|
env->mxccdata[0]);
|
|
stq_phys((env->mxccregs[1] & 0xffffffffULL) + 8,
|
|
env->mxccdata[1]);
|
|
stq_phys((env->mxccregs[1] & 0xffffffffULL) + 16,
|
|
env->mxccdata[2]);
|
|
stq_phys((env->mxccregs[1] & 0xffffffffULL) + 24,
|
|
env->mxccdata[3]);
|
|
break;
|
|
case 0x01c00a00: /* MXCC control register */
|
|
if (size == 8)
|
|
env->mxccregs[3] = val;
|
|
else
|
|
DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
|
|
size);
|
|
break;
|
|
case 0x01c00a04: /* MXCC control register */
|
|
if (size == 4)
|
|
env->mxccregs[3] = (env->mxccregs[3] & 0xffffffff00000000ULL)
|
|
| val;
|
|
else
|
|
DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
|
|
size);
|
|
break;
|
|
case 0x01c00e00: /* MXCC error register */
|
|
// writing a 1 bit clears the error
|
|
if (size == 8)
|
|
env->mxccregs[6] &= ~val;
|
|
else
|
|
DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
|
|
size);
|
|
break;
|
|
case 0x01c00f00: /* MBus port address register */
|
|
if (size == 8)
|
|
env->mxccregs[7] = val;
|
|
else
|
|
DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
|
|
size);
|
|
break;
|
|
default:
|
|
DPRINTF_MXCC("%08x: unimplemented address, size: %d\n", addr,
|
|
size);
|
|
break;
|
|
}
|
|
DPRINTF_MXCC("asi = %d, size = %d, addr = %08x, val = %" PRIx64 "\n",
|
|
asi, size, addr, val);
|
|
#ifdef DEBUG_MXCC
|
|
dump_mxcc(env);
|
|
#endif
|
|
break;
|
|
case 3: /* MMU flush */
|
|
{
|
|
int mmulev;
|
|
|
|
mmulev = (addr >> 8) & 15;
|
|
DPRINTF_MMU("mmu flush level %d\n", mmulev);
|
|
switch (mmulev) {
|
|
case 0: // flush page
|
|
tlb_flush_page(env, addr & 0xfffff000);
|
|
break;
|
|
case 1: // flush segment (256k)
|
|
case 2: // flush region (16M)
|
|
case 3: // flush context (4G)
|
|
case 4: // flush entire
|
|
tlb_flush(env, 1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#ifdef DEBUG_MMU
|
|
dump_mmu(env);
|
|
#endif
|
|
}
|
|
break;
|
|
case 4: /* write MMU regs */
|
|
{
|
|
int reg = (addr >> 8) & 0x1f;
|
|
uint32_t oldreg;
|
|
|
|
oldreg = env->mmuregs[reg];
|
|
switch(reg) {
|
|
case 0: // Control Register
|
|
env->mmuregs[reg] = (env->mmuregs[reg] & 0xff000000) |
|
|
(val & 0x00ffffff);
|
|
// Mappings generated during no-fault mode or MMU
|
|
// disabled mode are invalid in normal mode
|
|
if ((oldreg & (MMU_E | MMU_NF | env->def->mmu_bm)) !=
|
|
(env->mmuregs[reg] & (MMU_E | MMU_NF | env->def->mmu_bm)))
|
|
tlb_flush(env, 1);
|
|
break;
|
|
case 1: // Context Table Pointer Register
|
|
env->mmuregs[reg] = val & env->def->mmu_ctpr_mask;
|
|
break;
|
|
case 2: // Context Register
|
|
env->mmuregs[reg] = val & env->def->mmu_cxr_mask;
|
|
if (oldreg != env->mmuregs[reg]) {
|
|
/* we flush when the MMU context changes because
|
|
QEMU has no MMU context support */
|
|
tlb_flush(env, 1);
|
|
}
|
|
break;
|
|
case 3: // Synchronous Fault Status Register with Clear
|
|
case 4: // Synchronous Fault Address Register
|
|
break;
|
|
case 0x10: // TLB Replacement Control Register
|
|
env->mmuregs[reg] = val & env->def->mmu_trcr_mask;
|
|
break;
|
|
case 0x13: // Synchronous Fault Status Register with Read and Clear
|
|
env->mmuregs[3] = val & env->def->mmu_sfsr_mask;
|
|
break;
|
|
case 0x14: // Synchronous Fault Address Register
|
|
env->mmuregs[4] = val;
|
|
break;
|
|
default:
|
|
env->mmuregs[reg] = val;
|
|
break;
|
|
}
|
|
if (oldreg != env->mmuregs[reg]) {
|
|
DPRINTF_MMU("mmu change reg[%d]: 0x%08x -> 0x%08x\n",
|
|
reg, oldreg, env->mmuregs[reg]);
|
|
}
|
|
#ifdef DEBUG_MMU
|
|
dump_mmu(env);
|
|
#endif
|
|
}
|
|
break;
|
|
case 5: // Turbosparc ITLB Diagnostic
|
|
case 6: // Turbosparc DTLB Diagnostic
|
|
case 7: // Turbosparc IOTLB Diagnostic
|
|
break;
|
|
case 0xa: /* User data access */
|
|
switch(size) {
|
|
case 1:
|
|
stb_user(addr, val);
|
|
break;
|
|
case 2:
|
|
stw_user(addr, val);
|
|
break;
|
|
default:
|
|
case 4:
|
|
stl_user(addr, val);
|
|
break;
|
|
case 8:
|
|
stq_user(addr, val);
|
|
break;
|
|
}
|
|
break;
|
|
case 0xb: /* Supervisor data access */
|
|
switch(size) {
|
|
case 1:
|
|
stb_kernel(addr, val);
|
|
break;
|
|
case 2:
|
|
stw_kernel(addr, val);
|
|
break;
|
|
default:
|
|
case 4:
|
|
stl_kernel(addr, val);
|
|
break;
|
|
case 8:
|
|
stq_kernel(addr, val);
|
|
break;
|
|
}
|
|
break;
|
|
case 0xc: /* I-cache tag */
|
|
case 0xd: /* I-cache data */
|
|
case 0xe: /* D-cache tag */
|
|
case 0xf: /* D-cache data */
|
|
case 0x10: /* I/D-cache flush page */
|
|
case 0x11: /* I/D-cache flush segment */
|
|
case 0x12: /* I/D-cache flush region */
|
|
case 0x13: /* I/D-cache flush context */
|
|
case 0x14: /* I/D-cache flush user */
|
|
break;
|
|
case 0x17: /* Block copy, sta access */
|
|
{
|
|
// val = src
|
|
// addr = dst
|
|
// copy 32 bytes
|
|
unsigned int i;
|
|
uint32_t src = val & ~3, dst = addr & ~3, temp;
|
|
|
|
for (i = 0; i < 32; i += 4, src += 4, dst += 4) {
|
|
temp = ldl_kernel(src);
|
|
stl_kernel(dst, temp);
|
|
}
|
|
}
|
|
break;
|
|
case 0x1f: /* Block fill, stda access */
|
|
{
|
|
// addr = dst
|
|
// fill 32 bytes with val
|
|
unsigned int i;
|
|
uint32_t dst = addr & 7;
|
|
|
|
for (i = 0; i < 32; i += 8, dst += 8)
|
|
stq_kernel(dst, val);
|
|
}
|
|
break;
|
|
case 0x20: /* MMU passthrough */
|
|
{
|
|
switch(size) {
|
|
case 1:
|
|
stb_phys(addr, val);
|
|
break;
|
|
case 2:
|
|
stw_phys(addr, val);
|
|
break;
|
|
case 4:
|
|
default:
|
|
stl_phys(addr, val);
|
|
break;
|
|
case 8:
|
|
stq_phys(addr, val);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 0x21 ... 0x2f: /* MMU passthrough, 0x100000000 to 0xfffffffff */
|
|
{
|
|
switch(size) {
|
|
case 1:
|
|
stb_phys((target_phys_addr_t)addr
|
|
| ((target_phys_addr_t)(asi & 0xf) << 32), val);
|
|
break;
|
|
case 2:
|
|
stw_phys((target_phys_addr_t)addr
|
|
| ((target_phys_addr_t)(asi & 0xf) << 32), val);
|
|
break;
|
|
case 4:
|
|
default:
|
|
stl_phys((target_phys_addr_t)addr
|
|
| ((target_phys_addr_t)(asi & 0xf) << 32), val);
|
|
break;
|
|
case 8:
|
|
stq_phys((target_phys_addr_t)addr
|
|
| ((target_phys_addr_t)(asi & 0xf) << 32), val);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 0x30: // store buffer tags or Turbosparc secondary cache diagnostic
|
|
case 0x31: // store buffer data, Ross RT620 I-cache flush or
|
|
// Turbosparc snoop RAM
|
|
case 0x32: // store buffer control or Turbosparc page table
|
|
// descriptor diagnostic
|
|
case 0x36: /* I-cache flash clear */
|
|
case 0x37: /* D-cache flash clear */
|
|
case 0x4c: /* breakpoint action */
|
|
break;
|
|
case 0x38: /* SuperSPARC MMU Breakpoint Control Registers*/
|
|
{
|
|
int reg = (addr >> 8) & 3;
|
|
|
|
switch(reg) {
|
|
case 0: /* Breakpoint Value (Addr) */
|
|
env->mmubpregs[reg] = (val & 0xfffffffffULL);
|
|
break;
|
|
case 1: /* Breakpoint Mask */
|
|
env->mmubpregs[reg] = (val & 0xfffffffffULL);
|
|
break;
|
|
case 2: /* Breakpoint Control */
|
|
env->mmubpregs[reg] = (val & 0x7fULL);
|
|
break;
|
|
case 3: /* Breakpoint Status */
|
|
env->mmubpregs[reg] = (val & 0xfULL);
|
|
break;
|
|
}
|
|
DPRINTF_MMU("write breakpoint reg[%d] 0x%016x\n", reg,
|
|
env->mmuregs[reg]);
|
|
}
|
|
break;
|
|
case 8: /* User code access, XXX */
|
|
case 9: /* Supervisor code access, XXX */
|
|
default:
|
|
do_unassigned_access(addr, 1, 0, asi, size);
|
|
break;
|
|
}
|
|
#ifdef DEBUG_ASI
|
|
dump_asi("write", addr, asi, size, val);
|
|
#endif
|
|
}
|
|
|
|
#endif /* CONFIG_USER_ONLY */
|
|
#else /* TARGET_SPARC64 */
|
|
|
|
#ifdef CONFIG_USER_ONLY
|
|
uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
|
|
{
|
|
uint64_t ret = 0;
|
|
#if defined(DEBUG_ASI)
|
|
target_ulong last_addr = addr;
|
|
#endif
|
|
|
|
if (asi < 0x80)
|
|
raise_exception(TT_PRIV_ACT);
|
|
|
|
helper_check_align(addr, size - 1);
|
|
address_mask(env, &addr);
|
|
|
|
switch (asi) {
|
|
case 0x82: // Primary no-fault
|
|
case 0x8a: // Primary no-fault LE
|
|
if (page_check_range(addr, size, PAGE_READ) == -1) {
|
|
#ifdef DEBUG_ASI
|
|
dump_asi("read ", last_addr, asi, size, ret);
|
|
#endif
|
|
return 0;
|
|
}
|
|
// Fall through
|
|
case 0x80: // Primary
|
|
case 0x88: // Primary LE
|
|
{
|
|
switch(size) {
|
|
case 1:
|
|
ret = ldub_raw(addr);
|
|
break;
|
|
case 2:
|
|
ret = lduw_raw(addr);
|
|
break;
|
|
case 4:
|
|
ret = ldl_raw(addr);
|
|
break;
|
|
default:
|
|
case 8:
|
|
ret = ldq_raw(addr);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 0x83: // Secondary no-fault
|
|
case 0x8b: // Secondary no-fault LE
|
|
if (page_check_range(addr, size, PAGE_READ) == -1) {
|
|
#ifdef DEBUG_ASI
|
|
dump_asi("read ", last_addr, asi, size, ret);
|
|
#endif
|
|
return 0;
|
|
}
|
|
// Fall through
|
|
case 0x81: // Secondary
|
|
case 0x89: // Secondary LE
|
|
// XXX
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Convert from little endian */
|
|
switch (asi) {
|
|
case 0x88: // Primary LE
|
|
case 0x89: // Secondary LE
|
|
case 0x8a: // Primary no-fault LE
|
|
case 0x8b: // Secondary no-fault LE
|
|
switch(size) {
|
|
case 2:
|
|
ret = bswap16(ret);
|
|
break;
|
|
case 4:
|
|
ret = bswap32(ret);
|
|
break;
|
|
case 8:
|
|
ret = bswap64(ret);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Convert to signed number */
|
|
if (sign) {
|
|
switch(size) {
|
|
case 1:
|
|
ret = (int8_t) ret;
|
|
break;
|
|
case 2:
|
|
ret = (int16_t) ret;
|
|
break;
|
|
case 4:
|
|
ret = (int32_t) ret;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#ifdef DEBUG_ASI
|
|
dump_asi("read ", last_addr, asi, size, ret);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size)
|
|
{
|
|
#ifdef DEBUG_ASI
|
|
dump_asi("write", addr, asi, size, val);
|
|
#endif
|
|
if (asi < 0x80)
|
|
raise_exception(TT_PRIV_ACT);
|
|
|
|
helper_check_align(addr, size - 1);
|
|
address_mask(env, &addr);
|
|
|
|
/* Convert to little endian */
|
|
switch (asi) {
|
|
case 0x88: // Primary LE
|
|
case 0x89: // Secondary LE
|
|
switch(size) {
|
|
case 2:
|
|
val = bswap16(val);
|
|
break;
|
|
case 4:
|
|
val = bswap32(val);
|
|
break;
|
|
case 8:
|
|
val = bswap64(val);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch(asi) {
|
|
case 0x80: // Primary
|
|
case 0x88: // Primary LE
|
|
{
|
|
switch(size) {
|
|
case 1:
|
|
stb_raw(addr, val);
|
|
break;
|
|
case 2:
|
|
stw_raw(addr, val);
|
|
break;
|
|
case 4:
|
|
stl_raw(addr, val);
|
|
break;
|
|
case 8:
|
|
default:
|
|
stq_raw(addr, val);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 0x81: // Secondary
|
|
case 0x89: // Secondary LE
|
|
// XXX
|
|
return;
|
|
|
|
case 0x82: // Primary no-fault, RO
|
|
case 0x83: // Secondary no-fault, RO
|
|
case 0x8a: // Primary no-fault LE, RO
|
|
case 0x8b: // Secondary no-fault LE, RO
|
|
default:
|
|
do_unassigned_access(addr, 1, 0, 1, size);
|
|
return;
|
|
}
|
|
}
|
|
|
|
#else /* CONFIG_USER_ONLY */
|
|
|
|
uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
|
|
{
|
|
uint64_t ret = 0;
|
|
#if defined(DEBUG_ASI)
|
|
target_ulong last_addr = addr;
|
|
#endif
|
|
|
|
asi &= 0xff;
|
|
|
|
if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0)
|
|
|| ((env->def->features & CPU_FEATURE_HYPV)
|
|
&& asi >= 0x30 && asi < 0x80
|
|
&& !(env->hpstate & HS_PRIV)))
|
|
raise_exception(TT_PRIV_ACT);
|
|
|
|
helper_check_align(addr, size - 1);
|
|
switch (asi) {
|
|
case 0x82: // Primary no-fault
|
|
case 0x8a: // Primary no-fault LE
|
|
if (cpu_get_phys_page_debug(env, addr) == -1ULL) {
|
|
#ifdef DEBUG_ASI
|
|
dump_asi("read ", last_addr, asi, size, ret);
|
|
#endif
|
|
return 0;
|
|
}
|
|
// Fall through
|
|
case 0x10: // As if user primary
|
|
case 0x18: // As if user primary LE
|
|
case 0x80: // Primary
|
|
case 0x88: // Primary LE
|
|
case 0xe2: // UA2007 Primary block init
|
|
case 0xe3: // UA2007 Secondary block init
|
|
if ((asi & 0x80) && (env->pstate & PS_PRIV)) {
|
|
if ((env->def->features & CPU_FEATURE_HYPV)
|
|
&& env->hpstate & HS_PRIV) {
|
|
switch(size) {
|
|
case 1:
|
|
ret = ldub_hypv(addr);
|
|
break;
|
|
case 2:
|
|
ret = lduw_hypv(addr);
|
|
break;
|
|
case 4:
|
|
ret = ldl_hypv(addr);
|
|
break;
|
|
default:
|
|
case 8:
|
|
ret = ldq_hypv(addr);
|
|
break;
|
|
}
|
|
} else {
|
|
switch(size) {
|
|
case 1:
|
|
ret = ldub_kernel(addr);
|
|
break;
|
|
case 2:
|
|
ret = lduw_kernel(addr);
|
|
break;
|
|
case 4:
|
|
ret = ldl_kernel(addr);
|
|
break;
|
|
default:
|
|
case 8:
|
|
ret = ldq_kernel(addr);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
switch(size) {
|
|
case 1:
|
|
ret = ldub_user(addr);
|
|
break;
|
|
case 2:
|
|
ret = lduw_user(addr);
|
|
break;
|
|
case 4:
|
|
ret = ldl_user(addr);
|
|
break;
|
|
default:
|
|
case 8:
|
|
ret = ldq_user(addr);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 0x14: // Bypass
|
|
case 0x15: // Bypass, non-cacheable
|
|
case 0x1c: // Bypass LE
|
|
case 0x1d: // Bypass, non-cacheable LE
|
|
{
|
|
switch(size) {
|
|
case 1:
|
|
ret = ldub_phys(addr);
|
|
break;
|
|
case 2:
|
|
ret = lduw_phys(addr);
|
|
break;
|
|
case 4:
|
|
ret = ldl_phys(addr);
|
|
break;
|
|
default:
|
|
case 8:
|
|
ret = ldq_phys(addr);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case 0x24: // Nucleus quad LDD 128 bit atomic
|
|
case 0x2c: // Nucleus quad LDD 128 bit atomic LE
|
|
// Only ldda allowed
|
|
raise_exception(TT_ILL_INSN);
|
|
return 0;
|
|
case 0x83: // Secondary no-fault
|
|
case 0x8b: // Secondary no-fault LE
|
|
if (cpu_get_phys_page_debug(env, addr) == -1ULL) {
|
|
#ifdef DEBUG_ASI
|
|
dump_asi("read ", last_addr, asi, size, ret);
|
|
#endif
|
|
return 0;
|
|
}
|
|
// Fall through
|
|
case 0x04: // Nucleus
|
|
case 0x0c: // Nucleus Little Endian (LE)
|
|
case 0x11: // As if user secondary
|
|
case 0x19: // As if user secondary LE
|
|
case 0x4a: // UPA config
|
|
case 0x81: // Secondary
|
|
case 0x89: // Secondary LE
|
|
// XXX
|
|
break;
|
|
case 0x45: // LSU
|
|
ret = env->lsu;
|
|
break;
|
|
case 0x50: // I-MMU regs
|
|
{
|
|
int reg = (addr >> 3) & 0xf;
|
|
|
|
if (reg == 0) {
|
|
// I-TSB Tag Target register
|
|
ret = ultrasparc_tag_target(env->immu.tag_access);
|
|
} else {
|
|
ret = env->immuregs[reg];
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 0x51: // I-MMU 8k TSB pointer
|
|
{
|
|
// env->immuregs[5] holds I-MMU TSB register value
|
|
// env->immuregs[6] holds I-MMU Tag Access register value
|
|
ret = ultrasparc_tsb_pointer(env->immu.tsb, env->immu.tag_access,
|
|
8*1024);
|
|
break;
|
|
}
|
|
case 0x52: // I-MMU 64k TSB pointer
|
|
{
|
|
// env->immuregs[5] holds I-MMU TSB register value
|
|
// env->immuregs[6] holds I-MMU Tag Access register value
|
|
ret = ultrasparc_tsb_pointer(env->immu.tsb, env->immu.tag_access,
|
|
64*1024);
|
|
break;
|
|
}
|
|
case 0x55: // I-MMU data access
|
|
{
|
|
int reg = (addr >> 3) & 0x3f;
|
|
|
|
ret = env->itlb[reg].tte;
|
|
break;
|
|
}
|
|
case 0x56: // I-MMU tag read
|
|
{
|
|
int reg = (addr >> 3) & 0x3f;
|
|
|
|
ret = env->itlb[reg].tag;
|
|
break;
|
|
}
|
|
case 0x58: // D-MMU regs
|
|
{
|
|
int reg = (addr >> 3) & 0xf;
|
|
|
|
if (reg == 0) {
|
|
// D-TSB Tag Target register
|
|
ret = ultrasparc_tag_target(env->dmmu.tag_access);
|
|
} else {
|
|
ret = env->dmmuregs[reg];
|
|
}
|
|
break;
|
|
}
|
|
case 0x59: // D-MMU 8k TSB pointer
|
|
{
|
|
// env->dmmuregs[5] holds D-MMU TSB register value
|
|
// env->dmmuregs[6] holds D-MMU Tag Access register value
|
|
ret = ultrasparc_tsb_pointer(env->dmmu.tsb, env->dmmu.tag_access,
|
|
8*1024);
|
|
break;
|
|
}
|
|
case 0x5a: // D-MMU 64k TSB pointer
|
|
{
|
|
// env->dmmuregs[5] holds D-MMU TSB register value
|
|
// env->dmmuregs[6] holds D-MMU Tag Access register value
|
|
ret = ultrasparc_tsb_pointer(env->dmmu.tsb, env->dmmu.tag_access,
|
|
64*1024);
|
|
break;
|
|
}
|
|
case 0x5d: // D-MMU data access
|
|
{
|
|
int reg = (addr >> 3) & 0x3f;
|
|
|
|
ret = env->dtlb[reg].tte;
|
|
break;
|
|
}
|
|
case 0x5e: // D-MMU tag read
|
|
{
|
|
int reg = (addr >> 3) & 0x3f;
|
|
|
|
ret = env->dtlb[reg].tag;
|
|
break;
|
|
}
|
|
case 0x46: // D-cache data
|
|
case 0x47: // D-cache tag access
|
|
case 0x4b: // E-cache error enable
|
|
case 0x4c: // E-cache asynchronous fault status
|
|
case 0x4d: // E-cache asynchronous fault address
|
|
case 0x4e: // E-cache tag data
|
|
case 0x66: // I-cache instruction access
|
|
case 0x67: // I-cache tag access
|
|
case 0x6e: // I-cache predecode
|
|
case 0x6f: // I-cache LRU etc.
|
|
case 0x76: // E-cache tag
|
|
case 0x7e: // E-cache tag
|
|
break;
|
|
case 0x5b: // D-MMU data pointer
|
|
case 0x48: // Interrupt dispatch, RO
|
|
case 0x49: // Interrupt data receive
|
|
case 0x7f: // Incoming interrupt vector, RO
|
|
// XXX
|
|
break;
|
|
case 0x54: // I-MMU data in, WO
|
|
case 0x57: // I-MMU demap, WO
|
|
case 0x5c: // D-MMU data in, WO
|
|
case 0x5f: // D-MMU demap, WO
|
|
case 0x77: // Interrupt vector, WO
|
|
default:
|
|
do_unassigned_access(addr, 0, 0, 1, size);
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
/* Convert from little endian */
|
|
switch (asi) {
|
|
case 0x0c: // Nucleus Little Endian (LE)
|
|
case 0x18: // As if user primary LE
|
|
case 0x19: // As if user secondary LE
|
|
case 0x1c: // Bypass LE
|
|
case 0x1d: // Bypass, non-cacheable LE
|
|
case 0x88: // Primary LE
|
|
case 0x89: // Secondary LE
|
|
case 0x8a: // Primary no-fault LE
|
|
case 0x8b: // Secondary no-fault LE
|
|
switch(size) {
|
|
case 2:
|
|
ret = bswap16(ret);
|
|
break;
|
|
case 4:
|
|
ret = bswap32(ret);
|
|
break;
|
|
case 8:
|
|
ret = bswap64(ret);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Convert to signed number */
|
|
if (sign) {
|
|
switch(size) {
|
|
case 1:
|
|
ret = (int8_t) ret;
|
|
break;
|
|
case 2:
|
|
ret = (int16_t) ret;
|
|
break;
|
|
case 4:
|
|
ret = (int32_t) ret;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#ifdef DEBUG_ASI
|
|
dump_asi("read ", last_addr, asi, size, ret);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size)
|
|
{
|
|
#ifdef DEBUG_ASI
|
|
dump_asi("write", addr, asi, size, val);
|
|
#endif
|
|
|
|
asi &= 0xff;
|
|
|
|
if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0)
|
|
|| ((env->def->features & CPU_FEATURE_HYPV)
|
|
&& asi >= 0x30 && asi < 0x80
|
|
&& !(env->hpstate & HS_PRIV)))
|
|
raise_exception(TT_PRIV_ACT);
|
|
|
|
helper_check_align(addr, size - 1);
|
|
/* Convert to little endian */
|
|
switch (asi) {
|
|
case 0x0c: // Nucleus Little Endian (LE)
|
|
case 0x18: // As if user primary LE
|
|
case 0x19: // As if user secondary LE
|
|
case 0x1c: // Bypass LE
|
|
case 0x1d: // Bypass, non-cacheable LE
|
|
case 0x88: // Primary LE
|
|
case 0x89: // Secondary LE
|
|
switch(size) {
|
|
case 2:
|
|
val = bswap16(val);
|
|
break;
|
|
case 4:
|
|
val = bswap32(val);
|
|
break;
|
|
case 8:
|
|
val = bswap64(val);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch(asi) {
|
|
case 0x10: // As if user primary
|
|
case 0x18: // As if user primary LE
|
|
case 0x80: // Primary
|
|
case 0x88: // Primary LE
|
|
case 0xe2: // UA2007 Primary block init
|
|
case 0xe3: // UA2007 Secondary block init
|
|
if ((asi & 0x80) && (env->pstate & PS_PRIV)) {
|
|
if ((env->def->features & CPU_FEATURE_HYPV)
|
|
&& env->hpstate & HS_PRIV) {
|
|
switch(size) {
|
|
case 1:
|
|
stb_hypv(addr, val);
|
|
break;
|
|
case 2:
|
|
stw_hypv(addr, val);
|
|
break;
|
|
case 4:
|
|
stl_hypv(addr, val);
|
|
break;
|
|
case 8:
|
|
default:
|
|
stq_hypv(addr, val);
|
|
break;
|
|
}
|
|
} else {
|
|
switch(size) {
|
|
case 1:
|
|
stb_kernel(addr, val);
|
|
break;
|
|
case 2:
|
|
stw_kernel(addr, val);
|
|
break;
|
|
case 4:
|
|
stl_kernel(addr, val);
|
|
break;
|
|
case 8:
|
|
default:
|
|
stq_kernel(addr, val);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
switch(size) {
|
|
case 1:
|
|
stb_user(addr, val);
|
|
break;
|
|
case 2:
|
|
stw_user(addr, val);
|
|
break;
|
|
case 4:
|
|
stl_user(addr, val);
|
|
break;
|
|
case 8:
|
|
default:
|
|
stq_user(addr, val);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 0x14: // Bypass
|
|
case 0x15: // Bypass, non-cacheable
|
|
case 0x1c: // Bypass LE
|
|
case 0x1d: // Bypass, non-cacheable LE
|
|
{
|
|
switch(size) {
|
|
case 1:
|
|
stb_phys(addr, val);
|
|
break;
|
|
case 2:
|
|
stw_phys(addr, val);
|
|
break;
|
|
case 4:
|
|
stl_phys(addr, val);
|
|
break;
|
|
case 8:
|
|
default:
|
|
stq_phys(addr, val);
|
|
break;
|
|
}
|
|
}
|
|
return;
|
|
case 0x24: // Nucleus quad LDD 128 bit atomic
|
|
case 0x2c: // Nucleus quad LDD 128 bit atomic LE
|
|
// Only ldda allowed
|
|
raise_exception(TT_ILL_INSN);
|
|
return;
|
|
case 0x04: // Nucleus
|
|
case 0x0c: // Nucleus Little Endian (LE)
|
|
case 0x11: // As if user secondary
|
|
case 0x19: // As if user secondary LE
|
|
case 0x4a: // UPA config
|
|
case 0x81: // Secondary
|
|
case 0x89: // Secondary LE
|
|
// XXX
|
|
return;
|
|
case 0x45: // LSU
|
|
{
|
|
uint64_t oldreg;
|
|
|
|
oldreg = env->lsu;
|
|
env->lsu = val & (DMMU_E | IMMU_E);
|
|
// Mappings generated during D/I MMU disabled mode are
|
|
// invalid in normal mode
|
|
if (oldreg != env->lsu) {
|
|
DPRINTF_MMU("LSU change: 0x%" PRIx64 " -> 0x%" PRIx64 "\n",
|
|
oldreg, env->lsu);
|
|
#ifdef DEBUG_MMU
|
|
dump_mmu(env);
|
|
#endif
|
|
tlb_flush(env, 1);
|
|
}
|
|
return;
|
|
}
|
|
case 0x50: // I-MMU regs
|
|
{
|
|
int reg = (addr >> 3) & 0xf;
|
|
uint64_t oldreg;
|
|
|
|
oldreg = env->immuregs[reg];
|
|
switch(reg) {
|
|
case 0: // RO
|
|
return;
|
|
case 1: // Not in I-MMU
|
|
case 2:
|
|
return;
|
|
case 3: // SFSR
|
|
if ((val & 1) == 0)
|
|
val = 0; // Clear SFSR
|
|
env->immu.sfsr = val;
|
|
break;
|
|
case 4: // RO
|
|
return;
|
|
case 5: // TSB access
|
|
DPRINTF_MMU("immu TSB write: 0x%016" PRIx64 " -> 0x%016"
|
|
PRIx64 "\n", env->immu.tsb, val);
|
|
env->immu.tsb = val;
|
|
break;
|
|
case 6: // Tag access
|
|
env->immu.tag_access = val;
|
|
break;
|
|
case 7:
|
|
case 8:
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (oldreg != env->immuregs[reg]) {
|
|
DPRINTF_MMU("immu change reg[%d]: 0x%016" PRIx64 " -> 0x%016"
|
|
PRIx64 "\n", reg, oldreg, env->immuregs[reg]);
|
|
}
|
|
#ifdef DEBUG_MMU
|
|
dump_mmu(env);
|
|
#endif
|
|
return;
|
|
}
|
|
case 0x54: // I-MMU data in
|
|
replace_tlb_1bit_lru(env->itlb, env->immu.tag_access, val, "immu", env);
|
|
return;
|
|
case 0x55: // I-MMU data access
|
|
{
|
|
// TODO: auto demap
|
|
|
|
unsigned int i = (addr >> 3) & 0x3f;
|
|
|
|
replace_tlb_entry(&env->itlb[i], env->immu.tag_access, val, env);
|
|
|
|
#ifdef DEBUG_MMU
|
|
DPRINTF_MMU("immu data access replaced entry [%i]\n", i);
|
|
dump_mmu(env);
|
|
#endif
|
|
return;
|
|
}
|
|
case 0x57: // I-MMU demap
|
|
demap_tlb(env->itlb, val, "immu", env);
|
|
return;
|
|
case 0x58: // D-MMU regs
|
|
{
|
|
int reg = (addr >> 3) & 0xf;
|
|
uint64_t oldreg;
|
|
|
|
oldreg = env->dmmuregs[reg];
|
|
switch(reg) {
|
|
case 0: // RO
|
|
case 4:
|
|
return;
|
|
case 3: // SFSR
|
|
if ((val & 1) == 0) {
|
|
val = 0; // Clear SFSR, Fault address
|
|
env->dmmu.sfar = 0;
|
|
}
|
|
env->dmmu.sfsr = val;
|
|
break;
|
|
case 1: // Primary context
|
|
env->dmmu.mmu_primary_context = val;
|
|
break;
|
|
case 2: // Secondary context
|
|
env->dmmu.mmu_secondary_context = val;
|
|
break;
|
|
case 5: // TSB access
|
|
DPRINTF_MMU("dmmu TSB write: 0x%016" PRIx64 " -> 0x%016"
|
|
PRIx64 "\n", env->dmmu.tsb, val);
|
|
env->dmmu.tsb = val;
|
|
break;
|
|
case 6: // Tag access
|
|
env->dmmu.tag_access = val;
|
|
break;
|
|
case 7: // Virtual Watchpoint
|
|
case 8: // Physical Watchpoint
|
|
default:
|
|
env->dmmuregs[reg] = val;
|
|
break;
|
|
}
|
|
|
|
if (oldreg != env->dmmuregs[reg]) {
|
|
DPRINTF_MMU("dmmu change reg[%d]: 0x%016" PRIx64 " -> 0x%016"
|
|
PRIx64 "\n", reg, oldreg, env->dmmuregs[reg]);
|
|
}
|
|
#ifdef DEBUG_MMU
|
|
dump_mmu(env);
|
|
#endif
|
|
return;
|
|
}
|
|
case 0x5c: // D-MMU data in
|
|
replace_tlb_1bit_lru(env->dtlb, env->dmmu.tag_access, val, "dmmu", env);
|
|
return;
|
|
case 0x5d: // D-MMU data access
|
|
{
|
|
unsigned int i = (addr >> 3) & 0x3f;
|
|
|
|
replace_tlb_entry(&env->dtlb[i], env->dmmu.tag_access, val, env);
|
|
|
|
#ifdef DEBUG_MMU
|
|
DPRINTF_MMU("dmmu data access replaced entry [%i]\n", i);
|
|
dump_mmu(env);
|
|
#endif
|
|
return;
|
|
}
|
|
case 0x5f: // D-MMU demap
|
|
demap_tlb(env->dtlb, val, "dmmu", env);
|
|
return;
|
|
case 0x49: // Interrupt data receive
|
|
// XXX
|
|
return;
|
|
case 0x46: // D-cache data
|
|
case 0x47: // D-cache tag access
|
|
case 0x4b: // E-cache error enable
|
|
case 0x4c: // E-cache asynchronous fault status
|
|
case 0x4d: // E-cache asynchronous fault address
|
|
case 0x4e: // E-cache tag data
|
|
case 0x66: // I-cache instruction access
|
|
case 0x67: // I-cache tag access
|
|
case 0x6e: // I-cache predecode
|
|
case 0x6f: // I-cache LRU etc.
|
|
case 0x76: // E-cache tag
|
|
case 0x7e: // E-cache tag
|
|
return;
|
|
case 0x51: // I-MMU 8k TSB pointer, RO
|
|
case 0x52: // I-MMU 64k TSB pointer, RO
|
|
case 0x56: // I-MMU tag read, RO
|
|
case 0x59: // D-MMU 8k TSB pointer, RO
|
|
case 0x5a: // D-MMU 64k TSB pointer, RO
|
|
case 0x5b: // D-MMU data pointer, RO
|
|
case 0x5e: // D-MMU tag read, RO
|
|
case 0x48: // Interrupt dispatch, RO
|
|
case 0x7f: // Incoming interrupt vector, RO
|
|
case 0x82: // Primary no-fault, RO
|
|
case 0x83: // Secondary no-fault, RO
|
|
case 0x8a: // Primary no-fault LE, RO
|
|
case 0x8b: // Secondary no-fault LE, RO
|
|
default:
|
|
do_unassigned_access(addr, 1, 0, 1, size);
|
|
return;
|
|
}
|
|
}
|
|
#endif /* CONFIG_USER_ONLY */
|
|
|
|
void helper_ldda_asi(target_ulong addr, int asi, int rd)
|
|
{
|
|
if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0)
|
|
|| ((env->def->features & CPU_FEATURE_HYPV)
|
|
&& asi >= 0x30 && asi < 0x80
|
|
&& !(env->hpstate & HS_PRIV)))
|
|
raise_exception(TT_PRIV_ACT);
|
|
|
|
switch (asi) {
|
|
case 0x24: // Nucleus quad LDD 128 bit atomic
|
|
case 0x2c: // Nucleus quad LDD 128 bit atomic LE
|
|
helper_check_align(addr, 0xf);
|
|
if (rd == 0) {
|
|
env->gregs[1] = ldq_kernel(addr + 8);
|
|
if (asi == 0x2c)
|
|
bswap64s(&env->gregs[1]);
|
|
} else if (rd < 8) {
|
|
env->gregs[rd] = ldq_kernel(addr);
|
|
env->gregs[rd + 1] = ldq_kernel(addr + 8);
|
|
if (asi == 0x2c) {
|
|
bswap64s(&env->gregs[rd]);
|
|
bswap64s(&env->gregs[rd + 1]);
|
|
}
|
|
} else {
|
|
env->regwptr[rd] = ldq_kernel(addr);
|
|
env->regwptr[rd + 1] = ldq_kernel(addr + 8);
|
|
if (asi == 0x2c) {
|
|
bswap64s(&env->regwptr[rd]);
|
|
bswap64s(&env->regwptr[rd + 1]);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
helper_check_align(addr, 0x3);
|
|
if (rd == 0)
|
|
env->gregs[1] = helper_ld_asi(addr + 4, asi, 4, 0);
|
|
else if (rd < 8) {
|
|
env->gregs[rd] = helper_ld_asi(addr, asi, 4, 0);
|
|
env->gregs[rd + 1] = helper_ld_asi(addr + 4, asi, 4, 0);
|
|
} else {
|
|
env->regwptr[rd] = helper_ld_asi(addr, asi, 4, 0);
|
|
env->regwptr[rd + 1] = helper_ld_asi(addr + 4, asi, 4, 0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void helper_ldf_asi(target_ulong addr, int asi, int size, int rd)
|
|
{
|
|
unsigned int i;
|
|
target_ulong val;
|
|
|
|
helper_check_align(addr, 3);
|
|
switch (asi) {
|
|
case 0xf0: // Block load primary
|
|
case 0xf1: // Block load secondary
|
|
case 0xf8: // Block load primary LE
|
|
case 0xf9: // Block load secondary LE
|
|
if (rd & 7) {
|
|
raise_exception(TT_ILL_INSN);
|
|
return;
|
|
}
|
|
helper_check_align(addr, 0x3f);
|
|
for (i = 0; i < 16; i++) {
|
|
*(uint32_t *)&env->fpr[rd++] = helper_ld_asi(addr, asi & 0x8f, 4,
|
|
0);
|
|
addr += 4;
|
|
}
|
|
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
val = helper_ld_asi(addr, asi, size, 0);
|
|
switch(size) {
|
|
default:
|
|
case 4:
|
|
*((uint32_t *)&env->fpr[rd]) = val;
|
|
break;
|
|
case 8:
|
|
*((int64_t *)&DT0) = val;
|
|
break;
|
|
case 16:
|
|
// XXX
|
|
break;
|
|
}
|
|
}
|
|
|
|
void helper_stf_asi(target_ulong addr, int asi, int size, int rd)
|
|
{
|
|
unsigned int i;
|
|
target_ulong val = 0;
|
|
|
|
helper_check_align(addr, 3);
|
|
switch (asi) {
|
|
case 0xe0: // UA2007 Block commit store primary (cache flush)
|
|
case 0xe1: // UA2007 Block commit store secondary (cache flush)
|
|
case 0xf0: // Block store primary
|
|
case 0xf1: // Block store secondary
|
|
case 0xf8: // Block store primary LE
|
|
case 0xf9: // Block store secondary LE
|
|
if (rd & 7) {
|
|
raise_exception(TT_ILL_INSN);
|
|
return;
|
|
}
|
|
helper_check_align(addr, 0x3f);
|
|
for (i = 0; i < 16; i++) {
|
|
val = *(uint32_t *)&env->fpr[rd++];
|
|
helper_st_asi(addr, val, asi & 0x8f, 4);
|
|
addr += 4;
|
|
}
|
|
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch(size) {
|
|
default:
|
|
case 4:
|
|
val = *((uint32_t *)&env->fpr[rd]);
|
|
break;
|
|
case 8:
|
|
val = *((int64_t *)&DT0);
|
|
break;
|
|
case 16:
|
|
// XXX
|
|
break;
|
|
}
|
|
helper_st_asi(addr, val, asi, size);
|
|
}
|
|
|
|
target_ulong helper_cas_asi(target_ulong addr, target_ulong val1,
|
|
target_ulong val2, uint32_t asi)
|
|
{
|
|
target_ulong ret;
|
|
|
|
val2 &= 0xffffffffUL;
|
|
ret = helper_ld_asi(addr, asi, 4, 0);
|
|
ret &= 0xffffffffUL;
|
|
if (val2 == ret)
|
|
helper_st_asi(addr, val1 & 0xffffffffUL, asi, 4);
|
|
return ret;
|
|
}
|
|
|
|
target_ulong helper_casx_asi(target_ulong addr, target_ulong val1,
|
|
target_ulong val2, uint32_t asi)
|
|
{
|
|
target_ulong ret;
|
|
|
|
ret = helper_ld_asi(addr, asi, 8, 0);
|
|
if (val2 == ret)
|
|
helper_st_asi(addr, val1, asi, 8);
|
|
return ret;
|
|
}
|
|
#endif /* TARGET_SPARC64 */
|
|
|
|
#ifndef TARGET_SPARC64
|
|
void helper_rett(void)
|
|
{
|
|
unsigned int cwp;
|
|
|
|
if (env->psret == 1)
|
|
raise_exception(TT_ILL_INSN);
|
|
|
|
env->psret = 1;
|
|
cwp = cpu_cwp_inc(env, env->cwp + 1) ;
|
|
if (env->wim & (1 << cwp)) {
|
|
raise_exception(TT_WIN_UNF);
|
|
}
|
|
set_cwp(cwp);
|
|
env->psrs = env->psrps;
|
|
}
|
|
#endif
|
|
|
|
target_ulong helper_udiv(target_ulong a, target_ulong b)
|
|
{
|
|
uint64_t x0;
|
|
uint32_t x1;
|
|
|
|
x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32);
|
|
x1 = b;
|
|
|
|
if (x1 == 0) {
|
|
raise_exception(TT_DIV_ZERO);
|
|
}
|
|
|
|
x0 = x0 / x1;
|
|
if (x0 > 0xffffffff) {
|
|
env->cc_src2 = 1;
|
|
return 0xffffffff;
|
|
} else {
|
|
env->cc_src2 = 0;
|
|
return x0;
|
|
}
|
|
}
|
|
|
|
target_ulong helper_sdiv(target_ulong a, target_ulong b)
|
|
{
|
|
int64_t x0;
|
|
int32_t x1;
|
|
|
|
x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32);
|
|
x1 = b;
|
|
|
|
if (x1 == 0) {
|
|
raise_exception(TT_DIV_ZERO);
|
|
}
|
|
|
|
x0 = x0 / x1;
|
|
if ((int32_t) x0 != x0) {
|
|
env->cc_src2 = 1;
|
|
return x0 < 0? 0x80000000: 0x7fffffff;
|
|
} else {
|
|
env->cc_src2 = 0;
|
|
return x0;
|
|
}
|
|
}
|
|
|
|
void helper_stdf(target_ulong addr, int mem_idx)
|
|
{
|
|
helper_check_align(addr, 7);
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
switch (mem_idx) {
|
|
case 0:
|
|
stfq_user(addr, DT0);
|
|
break;
|
|
case 1:
|
|
stfq_kernel(addr, DT0);
|
|
break;
|
|
#ifdef TARGET_SPARC64
|
|
case 2:
|
|
stfq_hypv(addr, DT0);
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
#else
|
|
address_mask(env, &addr);
|
|
stfq_raw(addr, DT0);
|
|
#endif
|
|
}
|
|
|
|
void helper_lddf(target_ulong addr, int mem_idx)
|
|
{
|
|
helper_check_align(addr, 7);
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
switch (mem_idx) {
|
|
case 0:
|
|
DT0 = ldfq_user(addr);
|
|
break;
|
|
case 1:
|
|
DT0 = ldfq_kernel(addr);
|
|
break;
|
|
#ifdef TARGET_SPARC64
|
|
case 2:
|
|
DT0 = ldfq_hypv(addr);
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
#else
|
|
address_mask(env, &addr);
|
|
DT0 = ldfq_raw(addr);
|
|
#endif
|
|
}
|
|
|
|
void helper_ldqf(target_ulong addr, int mem_idx)
|
|
{
|
|
// XXX add 128 bit load
|
|
CPU_QuadU u;
|
|
|
|
helper_check_align(addr, 7);
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
switch (mem_idx) {
|
|
case 0:
|
|
u.ll.upper = ldq_user(addr);
|
|
u.ll.lower = ldq_user(addr + 8);
|
|
QT0 = u.q;
|
|
break;
|
|
case 1:
|
|
u.ll.upper = ldq_kernel(addr);
|
|
u.ll.lower = ldq_kernel(addr + 8);
|
|
QT0 = u.q;
|
|
break;
|
|
#ifdef TARGET_SPARC64
|
|
case 2:
|
|
u.ll.upper = ldq_hypv(addr);
|
|
u.ll.lower = ldq_hypv(addr + 8);
|
|
QT0 = u.q;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
#else
|
|
address_mask(env, &addr);
|
|
u.ll.upper = ldq_raw(addr);
|
|
u.ll.lower = ldq_raw((addr + 8) & 0xffffffffULL);
|
|
QT0 = u.q;
|
|
#endif
|
|
}
|
|
|
|
void helper_stqf(target_ulong addr, int mem_idx)
|
|
{
|
|
// XXX add 128 bit store
|
|
CPU_QuadU u;
|
|
|
|
helper_check_align(addr, 7);
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
switch (mem_idx) {
|
|
case 0:
|
|
u.q = QT0;
|
|
stq_user(addr, u.ll.upper);
|
|
stq_user(addr + 8, u.ll.lower);
|
|
break;
|
|
case 1:
|
|
u.q = QT0;
|
|
stq_kernel(addr, u.ll.upper);
|
|
stq_kernel(addr + 8, u.ll.lower);
|
|
break;
|
|
#ifdef TARGET_SPARC64
|
|
case 2:
|
|
u.q = QT0;
|
|
stq_hypv(addr, u.ll.upper);
|
|
stq_hypv(addr + 8, u.ll.lower);
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
#else
|
|
u.q = QT0;
|
|
address_mask(env, &addr);
|
|
stq_raw(addr, u.ll.upper);
|
|
stq_raw((addr + 8) & 0xffffffffULL, u.ll.lower);
|
|
#endif
|
|
}
|
|
|
|
static inline void set_fsr(void)
|
|
{
|
|
int rnd_mode;
|
|
|
|
switch (env->fsr & FSR_RD_MASK) {
|
|
case FSR_RD_NEAREST:
|
|
rnd_mode = float_round_nearest_even;
|
|
break;
|
|
default:
|
|
case FSR_RD_ZERO:
|
|
rnd_mode = float_round_to_zero;
|
|
break;
|
|
case FSR_RD_POS:
|
|
rnd_mode = float_round_up;
|
|
break;
|
|
case FSR_RD_NEG:
|
|
rnd_mode = float_round_down;
|
|
break;
|
|
}
|
|
set_float_rounding_mode(rnd_mode, &env->fp_status);
|
|
}
|
|
|
|
void helper_ldfsr(uint32_t new_fsr)
|
|
{
|
|
env->fsr = (new_fsr & FSR_LDFSR_MASK) | (env->fsr & FSR_LDFSR_OLDMASK);
|
|
set_fsr();
|
|
}
|
|
|
|
#ifdef TARGET_SPARC64
|
|
void helper_ldxfsr(uint64_t new_fsr)
|
|
{
|
|
env->fsr = (new_fsr & FSR_LDXFSR_MASK) | (env->fsr & FSR_LDXFSR_OLDMASK);
|
|
set_fsr();
|
|
}
|
|
#endif
|
|
|
|
void helper_debug(void)
|
|
{
|
|
env->exception_index = EXCP_DEBUG;
|
|
cpu_loop_exit();
|
|
}
|
|
|
|
#ifndef TARGET_SPARC64
|
|
/* XXX: use another pointer for %iN registers to avoid slow wrapping
|
|
handling ? */
|
|
void helper_save(void)
|
|
{
|
|
uint32_t cwp;
|
|
|
|
cwp = cpu_cwp_dec(env, env->cwp - 1);
|
|
if (env->wim & (1 << cwp)) {
|
|
raise_exception(TT_WIN_OVF);
|
|
}
|
|
set_cwp(cwp);
|
|
}
|
|
|
|
void helper_restore(void)
|
|
{
|
|
uint32_t cwp;
|
|
|
|
cwp = cpu_cwp_inc(env, env->cwp + 1);
|
|
if (env->wim & (1 << cwp)) {
|
|
raise_exception(TT_WIN_UNF);
|
|
}
|
|
set_cwp(cwp);
|
|
}
|
|
|
|
void helper_wrpsr(target_ulong new_psr)
|
|
{
|
|
if ((new_psr & PSR_CWP) >= env->nwindows)
|
|
raise_exception(TT_ILL_INSN);
|
|
else
|
|
PUT_PSR(env, new_psr);
|
|
}
|
|
|
|
target_ulong helper_rdpsr(void)
|
|
{
|
|
return GET_PSR(env);
|
|
}
|
|
|
|
#else
|
|
/* XXX: use another pointer for %iN registers to avoid slow wrapping
|
|
handling ? */
|
|
void helper_save(void)
|
|
{
|
|
uint32_t cwp;
|
|
|
|
cwp = cpu_cwp_dec(env, env->cwp - 1);
|
|
if (env->cansave == 0) {
|
|
raise_exception(TT_SPILL | (env->otherwin != 0 ?
|
|
(TT_WOTHER | ((env->wstate & 0x38) >> 1)):
|
|
((env->wstate & 0x7) << 2)));
|
|
} else {
|
|
if (env->cleanwin - env->canrestore == 0) {
|
|
// XXX Clean windows without trap
|
|
raise_exception(TT_CLRWIN);
|
|
} else {
|
|
env->cansave--;
|
|
env->canrestore++;
|
|
set_cwp(cwp);
|
|
}
|
|
}
|
|
}
|
|
|
|
void helper_restore(void)
|
|
{
|
|
uint32_t cwp;
|
|
|
|
cwp = cpu_cwp_inc(env, env->cwp + 1);
|
|
if (env->canrestore == 0) {
|
|
raise_exception(TT_FILL | (env->otherwin != 0 ?
|
|
(TT_WOTHER | ((env->wstate & 0x38) >> 1)):
|
|
((env->wstate & 0x7) << 2)));
|
|
} else {
|
|
env->cansave++;
|
|
env->canrestore--;
|
|
set_cwp(cwp);
|
|
}
|
|
}
|
|
|
|
void helper_flushw(void)
|
|
{
|
|
if (env->cansave != env->nwindows - 2) {
|
|
raise_exception(TT_SPILL | (env->otherwin != 0 ?
|
|
(TT_WOTHER | ((env->wstate & 0x38) >> 1)):
|
|
((env->wstate & 0x7) << 2)));
|
|
}
|
|
}
|
|
|
|
void helper_saved(void)
|
|
{
|
|
env->cansave++;
|
|
if (env->otherwin == 0)
|
|
env->canrestore--;
|
|
else
|
|
env->otherwin--;
|
|
}
|
|
|
|
void helper_restored(void)
|
|
{
|
|
env->canrestore++;
|
|
if (env->cleanwin < env->nwindows - 1)
|
|
env->cleanwin++;
|
|
if (env->otherwin == 0)
|
|
env->cansave--;
|
|
else
|
|
env->otherwin--;
|
|
}
|
|
|
|
target_ulong helper_rdccr(void)
|
|
{
|
|
return GET_CCR(env);
|
|
}
|
|
|
|
void helper_wrccr(target_ulong new_ccr)
|
|
{
|
|
PUT_CCR(env, new_ccr);
|
|
}
|
|
|
|
// CWP handling is reversed in V9, but we still use the V8 register
|
|
// order.
|
|
target_ulong helper_rdcwp(void)
|
|
{
|
|
return GET_CWP64(env);
|
|
}
|
|
|
|
void helper_wrcwp(target_ulong new_cwp)
|
|
{
|
|
PUT_CWP64(env, new_cwp);
|
|
}
|
|
|
|
// This function uses non-native bit order
|
|
#define GET_FIELD(X, FROM, TO) \
|
|
((X) >> (63 - (TO)) & ((1ULL << ((TO) - (FROM) + 1)) - 1))
|
|
|
|
// This function uses the order in the manuals, i.e. bit 0 is 2^0
|
|
#define GET_FIELD_SP(X, FROM, TO) \
|
|
GET_FIELD(X, 63 - (TO), 63 - (FROM))
|
|
|
|
target_ulong helper_array8(target_ulong pixel_addr, target_ulong cubesize)
|
|
{
|
|
return (GET_FIELD_SP(pixel_addr, 60, 63) << (17 + 2 * cubesize)) |
|
|
(GET_FIELD_SP(pixel_addr, 39, 39 + cubesize - 1) << (17 + cubesize)) |
|
|
(GET_FIELD_SP(pixel_addr, 17 + cubesize - 1, 17) << 17) |
|
|
(GET_FIELD_SP(pixel_addr, 56, 59) << 13) |
|
|
(GET_FIELD_SP(pixel_addr, 35, 38) << 9) |
|
|
(GET_FIELD_SP(pixel_addr, 13, 16) << 5) |
|
|
(((pixel_addr >> 55) & 1) << 4) |
|
|
(GET_FIELD_SP(pixel_addr, 33, 34) << 2) |
|
|
GET_FIELD_SP(pixel_addr, 11, 12);
|
|
}
|
|
|
|
target_ulong helper_alignaddr(target_ulong addr, target_ulong offset)
|
|
{
|
|
uint64_t tmp;
|
|
|
|
tmp = addr + offset;
|
|
env->gsr &= ~7ULL;
|
|
env->gsr |= tmp & 7ULL;
|
|
return tmp & ~7ULL;
|
|
}
|
|
|
|
target_ulong helper_popc(target_ulong val)
|
|
{
|
|
return ctpop64(val);
|
|
}
|
|
|
|
static inline uint64_t *get_gregset(uint32_t pstate)
|
|
{
|
|
switch (pstate) {
|
|
default:
|
|
DPRINTF_PSTATE("ERROR in get_gregset: active pstate bits=%x%s%s%s\n",
|
|
pstate,
|
|
(pstate & PS_IG) ? " IG" : "",
|
|
(pstate & PS_MG) ? " MG" : "",
|
|
(pstate & PS_AG) ? " AG" : "");
|
|
/* pass through to normal set of global registers */
|
|
case 0:
|
|
return env->bgregs;
|
|
case PS_AG:
|
|
return env->agregs;
|
|
case PS_MG:
|
|
return env->mgregs;
|
|
case PS_IG:
|
|
return env->igregs;
|
|
}
|
|
}
|
|
|
|
static inline void change_pstate(uint32_t new_pstate)
|
|
{
|
|
uint32_t pstate_regs, new_pstate_regs;
|
|
uint64_t *src, *dst;
|
|
|
|
if (env->def->features & CPU_FEATURE_GL) {
|
|
// PS_AG is not implemented in this case
|
|
new_pstate &= ~PS_AG;
|
|
}
|
|
|
|
pstate_regs = env->pstate & 0xc01;
|
|
new_pstate_regs = new_pstate & 0xc01;
|
|
|
|
if (new_pstate_regs != pstate_regs) {
|
|
DPRINTF_PSTATE("change_pstate: switching regs old=%x new=%x\n",
|
|
pstate_regs, new_pstate_regs);
|
|
// Switch global register bank
|
|
src = get_gregset(new_pstate_regs);
|
|
dst = get_gregset(pstate_regs);
|
|
memcpy32(dst, env->gregs);
|
|
memcpy32(env->gregs, src);
|
|
}
|
|
else {
|
|
DPRINTF_PSTATE("change_pstate: regs new=%x (unchanged)\n",
|
|
new_pstate_regs);
|
|
}
|
|
env->pstate = new_pstate;
|
|
}
|
|
|
|
void helper_wrpstate(target_ulong new_state)
|
|
{
|
|
change_pstate(new_state & 0xf3f);
|
|
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
if (cpu_interrupts_enabled(env)) {
|
|
cpu_check_irqs(env);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void helper_wrpil(target_ulong new_pil)
|
|
{
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
DPRINTF_PSTATE("helper_wrpil old=%x new=%x\n",
|
|
env->psrpil, (uint32_t)new_pil);
|
|
|
|
env->psrpil = new_pil;
|
|
|
|
if (cpu_interrupts_enabled(env)) {
|
|
cpu_check_irqs(env);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void helper_done(void)
|
|
{
|
|
trap_state* tsptr = cpu_tsptr(env);
|
|
|
|
env->pc = tsptr->tnpc;
|
|
env->npc = tsptr->tnpc + 4;
|
|
PUT_CCR(env, tsptr->tstate >> 32);
|
|
env->asi = (tsptr->tstate >> 24) & 0xff;
|
|
change_pstate((tsptr->tstate >> 8) & 0xf3f);
|
|
PUT_CWP64(env, tsptr->tstate & 0xff);
|
|
env->tl--;
|
|
|
|
DPRINTF_PSTATE("... helper_done tl=%d\n", env->tl);
|
|
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
if (cpu_interrupts_enabled(env)) {
|
|
cpu_check_irqs(env);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void helper_retry(void)
|
|
{
|
|
trap_state* tsptr = cpu_tsptr(env);
|
|
|
|
env->pc = tsptr->tpc;
|
|
env->npc = tsptr->tnpc;
|
|
PUT_CCR(env, tsptr->tstate >> 32);
|
|
env->asi = (tsptr->tstate >> 24) & 0xff;
|
|
change_pstate((tsptr->tstate >> 8) & 0xf3f);
|
|
PUT_CWP64(env, tsptr->tstate & 0xff);
|
|
env->tl--;
|
|
|
|
DPRINTF_PSTATE("... helper_retry tl=%d\n", env->tl);
|
|
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
if (cpu_interrupts_enabled(env)) {
|
|
cpu_check_irqs(env);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void do_modify_softint(const char* operation, uint32_t value)
|
|
{
|
|
if (env->softint != value) {
|
|
env->softint = value;
|
|
DPRINTF_PSTATE(": %s new %08x\n", operation, env->softint);
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
if (cpu_interrupts_enabled(env)) {
|
|
cpu_check_irqs(env);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void helper_set_softint(uint64_t value)
|
|
{
|
|
do_modify_softint("helper_set_softint", env->softint | (uint32_t)value);
|
|
}
|
|
|
|
void helper_clear_softint(uint64_t value)
|
|
{
|
|
do_modify_softint("helper_clear_softint", env->softint & (uint32_t)~value);
|
|
}
|
|
|
|
void helper_write_softint(uint64_t value)
|
|
{
|
|
do_modify_softint("helper_write_softint", (uint32_t)value);
|
|
}
|
|
#endif
|
|
|
|
void helper_flush(target_ulong addr)
|
|
{
|
|
addr &= ~7;
|
|
tb_invalidate_page_range(addr, addr + 8);
|
|
}
|
|
|
|
#ifdef TARGET_SPARC64
|
|
#ifdef DEBUG_PCALL
|
|
static const char * const excp_names[0x80] = {
|
|
[TT_TFAULT] = "Instruction Access Fault",
|
|
[TT_TMISS] = "Instruction Access MMU Miss",
|
|
[TT_CODE_ACCESS] = "Instruction Access Error",
|
|
[TT_ILL_INSN] = "Illegal Instruction",
|
|
[TT_PRIV_INSN] = "Privileged Instruction",
|
|
[TT_NFPU_INSN] = "FPU Disabled",
|
|
[TT_FP_EXCP] = "FPU Exception",
|
|
[TT_TOVF] = "Tag Overflow",
|
|
[TT_CLRWIN] = "Clean Windows",
|
|
[TT_DIV_ZERO] = "Division By Zero",
|
|
[TT_DFAULT] = "Data Access Fault",
|
|
[TT_DMISS] = "Data Access MMU Miss",
|
|
[TT_DATA_ACCESS] = "Data Access Error",
|
|
[TT_DPROT] = "Data Protection Error",
|
|
[TT_UNALIGNED] = "Unaligned Memory Access",
|
|
[TT_PRIV_ACT] = "Privileged Action",
|
|
[TT_EXTINT | 0x1] = "External Interrupt 1",
|
|
[TT_EXTINT | 0x2] = "External Interrupt 2",
|
|
[TT_EXTINT | 0x3] = "External Interrupt 3",
|
|
[TT_EXTINT | 0x4] = "External Interrupt 4",
|
|
[TT_EXTINT | 0x5] = "External Interrupt 5",
|
|
[TT_EXTINT | 0x6] = "External Interrupt 6",
|
|
[TT_EXTINT | 0x7] = "External Interrupt 7",
|
|
[TT_EXTINT | 0x8] = "External Interrupt 8",
|
|
[TT_EXTINT | 0x9] = "External Interrupt 9",
|
|
[TT_EXTINT | 0xa] = "External Interrupt 10",
|
|
[TT_EXTINT | 0xb] = "External Interrupt 11",
|
|
[TT_EXTINT | 0xc] = "External Interrupt 12",
|
|
[TT_EXTINT | 0xd] = "External Interrupt 13",
|
|
[TT_EXTINT | 0xe] = "External Interrupt 14",
|
|
[TT_EXTINT | 0xf] = "External Interrupt 15",
|
|
};
|
|
#endif
|
|
|
|
trap_state* cpu_tsptr(CPUState* env)
|
|
{
|
|
return &env->ts[env->tl & MAXTL_MASK];
|
|
}
|
|
|
|
void do_interrupt(CPUState *env)
|
|
{
|
|
int intno = env->exception_index;
|
|
trap_state* tsptr;
|
|
|
|
#ifdef DEBUG_PCALL
|
|
if (qemu_loglevel_mask(CPU_LOG_INT)) {
|
|
static int count;
|
|
const char *name;
|
|
|
|
if (intno < 0 || intno >= 0x180)
|
|
name = "Unknown";
|
|
else if (intno >= 0x100)
|
|
name = "Trap Instruction";
|
|
else if (intno >= 0xc0)
|
|
name = "Window Fill";
|
|
else if (intno >= 0x80)
|
|
name = "Window Spill";
|
|
else {
|
|
name = excp_names[intno];
|
|
if (!name)
|
|
name = "Unknown";
|
|
}
|
|
|
|
qemu_log("%6d: %s (v=%04x) pc=%016" PRIx64 " npc=%016" PRIx64
|
|
" SP=%016" PRIx64 "\n",
|
|
count, name, intno,
|
|
env->pc,
|
|
env->npc, env->regwptr[6]);
|
|
log_cpu_state(env, 0);
|
|
#if 0
|
|
{
|
|
int i;
|
|
uint8_t *ptr;
|
|
|
|
qemu_log(" code=");
|
|
ptr = (uint8_t *)env->pc;
|
|
for(i = 0; i < 16; i++) {
|
|
qemu_log(" %02x", ldub(ptr + i));
|
|
}
|
|
qemu_log("\n");
|
|
}
|
|
#endif
|
|
count++;
|
|
}
|
|
#endif
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
if (env->tl >= env->maxtl) {
|
|
cpu_abort(env, "Trap 0x%04x while trap level (%d) >= MAXTL (%d),"
|
|
" Error state", env->exception_index, env->tl, env->maxtl);
|
|
return;
|
|
}
|
|
#endif
|
|
if (env->tl < env->maxtl - 1) {
|
|
env->tl++;
|
|
} else {
|
|
env->pstate |= PS_RED;
|
|
if (env->tl < env->maxtl)
|
|
env->tl++;
|
|
}
|
|
tsptr = cpu_tsptr(env);
|
|
|
|
tsptr->tstate = ((uint64_t)GET_CCR(env) << 32) |
|
|
((env->asi & 0xff) << 24) | ((env->pstate & 0xf3f) << 8) |
|
|
GET_CWP64(env);
|
|
tsptr->tpc = env->pc;
|
|
tsptr->tnpc = env->npc;
|
|
tsptr->tt = intno;
|
|
|
|
switch (intno) {
|
|
case TT_IVEC:
|
|
change_pstate(PS_PEF | PS_PRIV | PS_IG);
|
|
break;
|
|
case TT_TFAULT:
|
|
case TT_DFAULT:
|
|
case TT_TMISS ... TT_TMISS + 3:
|
|
case TT_DMISS ... TT_DMISS + 3:
|
|
case TT_DPROT ... TT_DPROT + 3:
|
|
change_pstate(PS_PEF | PS_PRIV | PS_MG);
|
|
break;
|
|
default:
|
|
change_pstate(PS_PEF | PS_PRIV | PS_AG);
|
|
break;
|
|
}
|
|
|
|
if (intno == TT_CLRWIN)
|
|
cpu_set_cwp(env, cpu_cwp_dec(env, env->cwp - 1));
|
|
else if ((intno & 0x1c0) == TT_SPILL)
|
|
cpu_set_cwp(env, cpu_cwp_dec(env, env->cwp - env->cansave - 2));
|
|
else if ((intno & 0x1c0) == TT_FILL)
|
|
cpu_set_cwp(env, cpu_cwp_inc(env, env->cwp + 1));
|
|
env->tbr &= ~0x7fffULL;
|
|
env->tbr |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5);
|
|
env->pc = env->tbr;
|
|
env->npc = env->pc + 4;
|
|
env->exception_index = -1;
|
|
}
|
|
#else
|
|
#ifdef DEBUG_PCALL
|
|
static const char * const excp_names[0x80] = {
|
|
[TT_TFAULT] = "Instruction Access Fault",
|
|
[TT_ILL_INSN] = "Illegal Instruction",
|
|
[TT_PRIV_INSN] = "Privileged Instruction",
|
|
[TT_NFPU_INSN] = "FPU Disabled",
|
|
[TT_WIN_OVF] = "Window Overflow",
|
|
[TT_WIN_UNF] = "Window Underflow",
|
|
[TT_UNALIGNED] = "Unaligned Memory Access",
|
|
[TT_FP_EXCP] = "FPU Exception",
|
|
[TT_DFAULT] = "Data Access Fault",
|
|
[TT_TOVF] = "Tag Overflow",
|
|
[TT_EXTINT | 0x1] = "External Interrupt 1",
|
|
[TT_EXTINT | 0x2] = "External Interrupt 2",
|
|
[TT_EXTINT | 0x3] = "External Interrupt 3",
|
|
[TT_EXTINT | 0x4] = "External Interrupt 4",
|
|
[TT_EXTINT | 0x5] = "External Interrupt 5",
|
|
[TT_EXTINT | 0x6] = "External Interrupt 6",
|
|
[TT_EXTINT | 0x7] = "External Interrupt 7",
|
|
[TT_EXTINT | 0x8] = "External Interrupt 8",
|
|
[TT_EXTINT | 0x9] = "External Interrupt 9",
|
|
[TT_EXTINT | 0xa] = "External Interrupt 10",
|
|
[TT_EXTINT | 0xb] = "External Interrupt 11",
|
|
[TT_EXTINT | 0xc] = "External Interrupt 12",
|
|
[TT_EXTINT | 0xd] = "External Interrupt 13",
|
|
[TT_EXTINT | 0xe] = "External Interrupt 14",
|
|
[TT_EXTINT | 0xf] = "External Interrupt 15",
|
|
[TT_TOVF] = "Tag Overflow",
|
|
[TT_CODE_ACCESS] = "Instruction Access Error",
|
|
[TT_DATA_ACCESS] = "Data Access Error",
|
|
[TT_DIV_ZERO] = "Division By Zero",
|
|
[TT_NCP_INSN] = "Coprocessor Disabled",
|
|
};
|
|
#endif
|
|
|
|
void do_interrupt(CPUState *env)
|
|
{
|
|
int cwp, intno = env->exception_index;
|
|
|
|
#ifdef DEBUG_PCALL
|
|
if (qemu_loglevel_mask(CPU_LOG_INT)) {
|
|
static int count;
|
|
const char *name;
|
|
|
|
if (intno < 0 || intno >= 0x100)
|
|
name = "Unknown";
|
|
else if (intno >= 0x80)
|
|
name = "Trap Instruction";
|
|
else {
|
|
name = excp_names[intno];
|
|
if (!name)
|
|
name = "Unknown";
|
|
}
|
|
|
|
qemu_log("%6d: %s (v=%02x) pc=%08x npc=%08x SP=%08x\n",
|
|
count, name, intno,
|
|
env->pc,
|
|
env->npc, env->regwptr[6]);
|
|
log_cpu_state(env, 0);
|
|
#if 0
|
|
{
|
|
int i;
|
|
uint8_t *ptr;
|
|
|
|
qemu_log(" code=");
|
|
ptr = (uint8_t *)env->pc;
|
|
for(i = 0; i < 16; i++) {
|
|
qemu_log(" %02x", ldub(ptr + i));
|
|
}
|
|
qemu_log("\n");
|
|
}
|
|
#endif
|
|
count++;
|
|
}
|
|
#endif
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
if (env->psret == 0) {
|
|
cpu_abort(env, "Trap 0x%02x while interrupts disabled, Error state",
|
|
env->exception_index);
|
|
return;
|
|
}
|
|
#endif
|
|
env->psret = 0;
|
|
cwp = cpu_cwp_dec(env, env->cwp - 1);
|
|
cpu_set_cwp(env, cwp);
|
|
env->regwptr[9] = env->pc;
|
|
env->regwptr[10] = env->npc;
|
|
env->psrps = env->psrs;
|
|
env->psrs = 1;
|
|
env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4);
|
|
env->pc = env->tbr;
|
|
env->npc = env->pc + 4;
|
|
env->exception_index = -1;
|
|
}
|
|
#endif
|
|
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
|
|
static void do_unaligned_access(target_ulong addr, int is_write, int is_user,
|
|
void *retaddr);
|
|
|
|
#define MMUSUFFIX _mmu
|
|
#define ALIGNED_ONLY
|
|
|
|
#define SHIFT 0
|
|
#include "softmmu_template.h"
|
|
|
|
#define SHIFT 1
|
|
#include "softmmu_template.h"
|
|
|
|
#define SHIFT 2
|
|
#include "softmmu_template.h"
|
|
|
|
#define SHIFT 3
|
|
#include "softmmu_template.h"
|
|
|
|
/* XXX: make it generic ? */
|
|
static void cpu_restore_state2(void *retaddr)
|
|
{
|
|
TranslationBlock *tb;
|
|
unsigned long pc;
|
|
|
|
if (retaddr) {
|
|
/* now we have a real cpu fault */
|
|
pc = (unsigned long)retaddr;
|
|
tb = tb_find_pc(pc);
|
|
if (tb) {
|
|
/* the PC is inside the translated code. It means that we have
|
|
a virtual CPU fault */
|
|
cpu_restore_state(tb, env, pc, (void *)(long)env->cond);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void do_unaligned_access(target_ulong addr, int is_write, int is_user,
|
|
void *retaddr)
|
|
{
|
|
#ifdef DEBUG_UNALIGNED
|
|
printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx
|
|
"\n", addr, env->pc);
|
|
#endif
|
|
cpu_restore_state2(retaddr);
|
|
raise_exception(TT_UNALIGNED);
|
|
}
|
|
|
|
/* try to fill the TLB and return an exception if error. If retaddr is
|
|
NULL, it means that the function was called in C code (i.e. not
|
|
from generated code or from helper.c) */
|
|
/* XXX: fix it to restore all registers */
|
|
void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr)
|
|
{
|
|
int ret;
|
|
CPUState *saved_env;
|
|
|
|
/* XXX: hack to restore env in all cases, even if not called from
|
|
generated code */
|
|
saved_env = env;
|
|
env = cpu_single_env;
|
|
|
|
ret = cpu_sparc_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
|
|
if (ret) {
|
|
cpu_restore_state2(retaddr);
|
|
cpu_loop_exit();
|
|
}
|
|
env = saved_env;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef TARGET_SPARC64
|
|
void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
|
|
int is_asi, int size)
|
|
{
|
|
CPUState *saved_env;
|
|
int fault_type;
|
|
|
|
/* XXX: hack to restore env in all cases, even if not called from
|
|
generated code */
|
|
saved_env = env;
|
|
env = cpu_single_env;
|
|
#ifdef DEBUG_UNASSIGNED
|
|
if (is_asi)
|
|
printf("Unassigned mem %s access of %d byte%s to " TARGET_FMT_plx
|
|
" asi 0x%02x from " TARGET_FMT_lx "\n",
|
|
is_exec ? "exec" : is_write ? "write" : "read", size,
|
|
size == 1 ? "" : "s", addr, is_asi, env->pc);
|
|
else
|
|
printf("Unassigned mem %s access of %d byte%s to " TARGET_FMT_plx
|
|
" from " TARGET_FMT_lx "\n",
|
|
is_exec ? "exec" : is_write ? "write" : "read", size,
|
|
size == 1 ? "" : "s", addr, env->pc);
|
|
#endif
|
|
/* Don't overwrite translation and access faults */
|
|
fault_type = (env->mmuregs[3] & 0x1c) >> 2;
|
|
if ((fault_type > 4) || (fault_type == 0)) {
|
|
env->mmuregs[3] = 0; /* Fault status register */
|
|
if (is_asi)
|
|
env->mmuregs[3] |= 1 << 16;
|
|
if (env->psrs)
|
|
env->mmuregs[3] |= 1 << 5;
|
|
if (is_exec)
|
|
env->mmuregs[3] |= 1 << 6;
|
|
if (is_write)
|
|
env->mmuregs[3] |= 1 << 7;
|
|
env->mmuregs[3] |= (5 << 2) | 2;
|
|
/* SuperSPARC will never place instruction fault addresses in the FAR */
|
|
if (!is_exec) {
|
|
env->mmuregs[4] = addr; /* Fault address register */
|
|
}
|
|
}
|
|
/* overflow (same type fault was not read before another fault) */
|
|
if (fault_type == ((env->mmuregs[3] & 0x1c)) >> 2) {
|
|
env->mmuregs[3] |= 1;
|
|
}
|
|
|
|
if ((env->mmuregs[0] & MMU_E) && !(env->mmuregs[0] & MMU_NF)) {
|
|
if (is_exec)
|
|
raise_exception(TT_CODE_ACCESS);
|
|
else
|
|
raise_exception(TT_DATA_ACCESS);
|
|
}
|
|
env = saved_env;
|
|
|
|
/* flush neverland mappings created during no-fault mode,
|
|
so the sequential MMU faults report proper fault types */
|
|
if (env->mmuregs[0] & MMU_NF) {
|
|
tlb_flush(env, 1);
|
|
}
|
|
}
|
|
#else
|
|
void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
|
|
int is_asi, int size)
|
|
{
|
|
CPUState *saved_env;
|
|
|
|
/* XXX: hack to restore env in all cases, even if not called from
|
|
generated code */
|
|
saved_env = env;
|
|
env = cpu_single_env;
|
|
|
|
#ifdef DEBUG_UNASSIGNED
|
|
printf("Unassigned mem access to " TARGET_FMT_plx " from " TARGET_FMT_lx
|
|
"\n", addr, env->pc);
|
|
#endif
|
|
|
|
if (is_exec)
|
|
raise_exception(TT_CODE_ACCESS);
|
|
else
|
|
raise_exception(TT_DATA_ACCESS);
|
|
|
|
env = saved_env;
|
|
}
|
|
#endif
|
|
|
|
#ifdef TARGET_SPARC64
|
|
void helper_tick_set_count(void *opaque, uint64_t count)
|
|
{
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
cpu_tick_set_count(opaque, count);
|
|
#endif
|
|
}
|
|
|
|
uint64_t helper_tick_get_count(void *opaque)
|
|
{
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
return cpu_tick_get_count(opaque);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
void helper_tick_set_limit(void *opaque, uint64_t limit)
|
|
{
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
cpu_tick_set_limit(opaque, limit);
|
|
#endif
|
|
}
|
|
#endif
|