diff options
Diffstat (limited to 'target-sparc/ldst_helper.c')
| -rw-r--r-- | target-sparc/ldst_helper.c | 1021 |
1 files changed, 171 insertions, 850 deletions
diff --git a/target-sparc/ldst_helper.c b/target-sparc/ldst_helper.c index 6ce5ccc37f..de7d53ae20 100644 --- a/target-sparc/ldst_helper.c +++ b/target-sparc/ldst_helper.c @@ -254,18 +254,6 @@ static void replace_tlb_1bit_lru(SparcTLBEntry *tlb, #endif -#if defined(TARGET_SPARC64) || defined(CONFIG_USER_ONLY) -static inline target_ulong address_mask(CPUSPARCState *env1, target_ulong addr) -{ -#ifdef TARGET_SPARC64 - if (AM_CHECK(env1)) { - addr &= 0xffffffffULL; - } -#endif - return addr; -} -#endif - #ifdef TARGET_SPARC64 /* returns true if access using this ASI is to have address translated by MMU otherwise access is to raw physical address */ @@ -290,28 +278,41 @@ static inline int is_translating_asi(int asi) } } +static inline target_ulong address_mask(CPUSPARCState *env1, target_ulong addr) +{ + if (AM_CHECK(env1)) { + addr &= 0xffffffffULL; + } + return addr; +} + static inline target_ulong asi_address_mask(CPUSPARCState *env, int asi, target_ulong addr) { if (is_translating_asi(asi)) { - return address_mask(env, addr); - } else { - return addr; + addr = address_mask(env, addr); } + return addr; } #endif -void helper_check_align(CPUSPARCState *env, target_ulong addr, uint32_t align) +static void do_check_align(CPUSPARCState *env, target_ulong addr, + uint32_t align, uintptr_t ra) { if (addr & align) { #ifdef DEBUG_UNALIGNED printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx "\n", addr, env->pc); #endif - helper_raise_exception(env, TT_UNALIGNED); + cpu_raise_exception_ra(env, TT_UNALIGNED, ra); } } +void helper_check_align(CPUSPARCState *env, target_ulong addr, uint32_t align) +{ + do_check_align(env, addr, align, GETPC()); +} + #if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) && \ defined(DEBUG_MXCC) static void dump_mxcc(CPUSPARCState *env) @@ -440,7 +441,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, uint32_t last_addr = addr; #endif - helper_check_align(env, addr, size - 1); + do_check_align(env, addr, size - 1, GETPC()); switch (asi) { case ASI_M_MXCC: /* SuperSparc MXCC registers, or... */ /* case ASI_LEON_CACHEREGS: Leon3 cache control */ @@ -554,64 +555,11 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, break; } break; - case ASI_USERDATA: /* User data access */ - switch (size) { - case 1: - ret = cpu_ldub_user(env, addr); - break; - case 2: - ret = cpu_lduw_user(env, addr); - break; - default: - case 4: - ret = cpu_ldl_user(env, addr); - break; - case 8: - ret = cpu_ldq_user(env, addr); - break; - } - break; - case ASI_KERNELDATA: /* Supervisor data access */ - case ASI_P: /* Implicit primary context data access (v9 only?) */ - switch (size) { - case 1: - ret = cpu_ldub_kernel(env, addr); - break; - case 2: - ret = cpu_lduw_kernel(env, addr); - break; - default: - case 4: - ret = cpu_ldl_kernel(env, addr); - break; - case 8: - ret = cpu_ldq_kernel(env, addr); - break; - } - break; case ASI_M_TXTC_TAG: /* SparcStation 5 I-cache tag */ case ASI_M_TXTC_DATA: /* SparcStation 5 I-cache data */ case ASI_M_DATAC_TAG: /* SparcStation 5 D-cache tag */ case ASI_M_DATAC_DATA: /* SparcStation 5 D-cache data */ break; - case ASI_M_BYPASS: /* MMU passthrough */ - case ASI_LEON_BYPASS: /* LEON MMU passthrough */ - switch (size) { - case 1: - ret = ldub_phys(cs->as, addr); - break; - case 2: - ret = lduw_phys(cs->as, addr); - break; - default: - case 4: - ret = ldl_phys(cs->as, addr); - break; - case 8: - ret = ldq_phys(cs->as, addr); - break; - } - break; case 0x21 ... 0x2f: /* MMU passthrough, 0x100000000 to 0xfffffffff */ switch (size) { case 1: @@ -679,6 +627,14 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, cpu_unassigned_access(cs, addr, false, false, asi, size); ret = 0; break; + + case ASI_USERDATA: /* User data access */ + case ASI_KERNELDATA: /* Supervisor data access */ + case ASI_P: /* Implicit primary context data access (v9 only?) */ + case ASI_M_BYPASS: /* MMU passthrough */ + case ASI_LEON_BYPASS: /* LEON MMU passthrough */ + /* These are always handled inline. */ + g_assert_not_reached(); } if (sign) { switch (size) { @@ -708,7 +664,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, SPARCCPU *cpu = sparc_env_get_cpu(env); CPUState *cs = CPU(cpu); - helper_check_align(env, addr, size - 1); + do_check_align(env, addr, size - 1, GETPC()); switch (asi) { case ASI_M_MXCC: /* SuperSparc MXCC registers, or... */ /* case ASI_LEON_CACHEREGS: Leon3 cache control */ @@ -881,10 +837,10 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, 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))) { + /* Mappings generated during no-fault mode + are invalid in normal mode. */ + if ((oldreg ^ env->mmuregs[reg]) + & (MMU_NF | env->def->mmu_bm)) { tlb_flush(CPU(cpu), 1); } break; @@ -929,41 +885,6 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, case ASI_M_DIAGS: /* Turbosparc DTLB Diagnostic */ case ASI_M_IODIAG: /* Turbosparc IOTLB Diagnostic */ break; - case ASI_USERDATA: /* User data access */ - switch (size) { - case 1: - cpu_stb_user(env, addr, val); - break; - case 2: - cpu_stw_user(env, addr, val); - break; - default: - case 4: - cpu_stl_user(env, addr, val); - break; - case 8: - cpu_stq_user(env, addr, val); - break; - } - break; - case ASI_KERNELDATA: /* Supervisor data access */ - case ASI_P: - switch (size) { - case 1: - cpu_stb_kernel(env, addr, val); - break; - case 2: - cpu_stw_kernel(env, addr, val); - break; - default: - case 4: - cpu_stl_kernel(env, addr, val); - break; - case 8: - cpu_stq_kernel(env, addr, val); - break; - } - break; case ASI_M_TXTC_TAG: /* I-cache tag */ case ASI_M_TXTC_DATA: /* I-cache data */ case ASI_M_DATAC_TAG: /* D-cache tag */ @@ -974,52 +895,6 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, case ASI_M_FLUSH_CTX: /* I/D-cache flush context */ case ASI_M_FLUSH_USER: /* I/D-cache flush user */ break; - case ASI_M_BCOPY: /* 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 = cpu_ldl_kernel(env, src); - cpu_stl_kernel(env, dst, temp); - } - } - break; - case ASI_M_BFILL: /* 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) { - cpu_stq_kernel(env, dst, val); - } - } - break; - case ASI_M_BYPASS: /* MMU passthrough */ - case ASI_LEON_BYPASS: /* LEON MMU passthrough */ - { - switch (size) { - case 1: - stb_phys(cs->as, addr, val); - break; - case 2: - stw_phys(cs->as, addr, val); - break; - case 4: - default: - stl_phys(cs->as, addr, val); - break; - case 8: - stq_phys(cs->as, addr, val); - break; - } - } - break; case 0x21 ... 0x2f: /* MMU passthrough, 0x100000000 to 0xfffffffff */ { switch (size) { @@ -1091,6 +966,16 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, cpu_unassigned_access(CPU(sparc_env_get_cpu(env)), addr, true, false, asi, size); break; + + case ASI_USERDATA: /* User data access */ + case ASI_KERNELDATA: /* Supervisor data access */ + case ASI_P: + case ASI_M_BYPASS: /* MMU passthrough */ + case ASI_LEON_BYPASS: /* LEON MMU passthrough */ + case ASI_M_BCOPY: /* Block copy, sta access */ + case ASI_M_BFILL: /* Block fill, stda access */ + /* These are always handled inline. */ + g_assert_not_reached(); } #ifdef DEBUG_ASI dump_asi("write", addr, asi, size, val); @@ -1107,68 +992,54 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, int size = 1 << (memop & MO_SIZE); int sign = memop & MO_SIGN; uint64_t ret = 0; -#if defined(DEBUG_ASI) - target_ulong last_addr = addr; -#endif if (asi < 0x80) { - helper_raise_exception(env, TT_PRIV_ACT); + cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC()); } - - helper_check_align(env, addr, size - 1); + do_check_align(env, addr, size - 1, GETPC()); addr = asi_address_mask(env, asi, addr); switch (asi) { case ASI_PNF: /* Primary no-fault */ case ASI_PNFL: /* 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 ASI_P: /* Primary */ - case ASI_PL: /* Primary LE */ - { - switch (size) { - case 1: - ret = cpu_ldub_data(env, addr); - break; - case 2: - ret = cpu_lduw_data(env, addr); - break; - case 4: - ret = cpu_ldl_data(env, addr); - break; - default: - case 8: - ret = cpu_ldq_data(env, addr); - break; - } - } - break; case ASI_SNF: /* Secondary no-fault */ case ASI_SNFL: /* 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; + ret = 0; + break; + } + switch (size) { + case 1: + ret = cpu_ldub_data(env, addr); + break; + case 2: + ret = cpu_lduw_data(env, addr); + break; + case 4: + ret = cpu_ldl_data(env, addr); + break; + case 8: + ret = cpu_ldq_data(env, addr); + break; + default: + g_assert_not_reached(); } - /* Fall through */ + break; + break; + + case ASI_P: /* Primary */ + case ASI_PL: /* Primary LE */ case ASI_S: /* Secondary */ case ASI_SL: /* Secondary LE */ - /* XXX */ - break; + /* These are always handled inline. */ + g_assert_not_reached(); + default: - break; + cpu_raise_exception_ra(env, TT_DATA_ACCESS, GETPC()); } /* Convert from little endian */ switch (asi) { - case ASI_PL: /* Primary LE */ - case ASI_SL: /* Secondary LE */ case ASI_PNFL: /* Primary no-fault LE */ case ASI_SNFL: /* Secondary no-fault LE */ switch (size) { @@ -1181,11 +1052,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, case 8: ret = bswap64(ret); break; - default: - break; } - default: - break; } /* Convert to signed number */ @@ -1200,12 +1067,10 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, case 4: ret = (int32_t) ret; break; - default: - break; } } #ifdef DEBUG_ASI - dump_asi("read ", last_addr, asi, size, ret); + dump_asi("read", addr, asi, size, ret); #endif return ret; } @@ -1218,66 +1083,24 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, dump_asi("write", addr, asi, size, val); #endif if (asi < 0x80) { - helper_raise_exception(env, TT_PRIV_ACT); - } - - helper_check_align(env, addr, size - 1); - addr = asi_address_mask(env, asi, addr); - - /* Convert to little endian */ - switch (asi) { - case ASI_PL: /* Primary LE */ - case ASI_SL: /* 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; + cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC()); } + do_check_align(env, addr, size - 1, GETPC()); switch (asi) { case ASI_P: /* Primary */ case ASI_PL: /* Primary LE */ - { - switch (size) { - case 1: - cpu_stb_data(env, addr, val); - break; - case 2: - cpu_stw_data(env, addr, val); - break; - case 4: - cpu_stl_data(env, addr, val); - break; - case 8: - default: - cpu_stq_data(env, addr, val); - break; - } - } - break; case ASI_S: /* Secondary */ case ASI_SL: /* Secondary LE */ - /* XXX */ - return; + /* These are always handled inline. */ + g_assert_not_reached(); case ASI_PNF: /* Primary no-fault, RO */ case ASI_SNF: /* Secondary no-fault, RO */ case ASI_PNFL: /* Primary no-fault LE, RO */ case ASI_SNFL: /* Secondary no-fault LE, RO */ default: - helper_raise_exception(env, TT_DATA_ACCESS); - return; + cpu_raise_exception_ra(env, TT_DATA_ACCESS, GETPC()); } } @@ -1300,176 +1123,94 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, || (cpu_has_hypervisor(env) && asi >= 0x30 && asi < 0x80 && !(env->hpstate & HS_PRIV))) { - helper_raise_exception(env, TT_PRIV_ACT); + cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC()); } - helper_check_align(env, addr, size - 1); + do_check_align(env, addr, size - 1, GETPC()); addr = asi_address_mask(env, asi, addr); - /* process nonfaulting loads first */ - if ((asi & 0xf6) == 0x82) { - int mmu_idx; - - /* secondary space access has lowest asi bit equal to 1 */ - if (env->pstate & PS_PRIV) { - mmu_idx = (asi & 1) ? MMU_KERNEL_SECONDARY_IDX : MMU_KERNEL_IDX; - } else { - mmu_idx = (asi & 1) ? MMU_USER_SECONDARY_IDX : MMU_USER_IDX; - } + switch (asi) { + case ASI_PNF: + case ASI_PNFL: + case ASI_SNF: + case ASI_SNFL: + { + TCGMemOpIdx oi; + int idx = (env->pstate & PS_PRIV + ? (asi & 1 ? MMU_KERNEL_SECONDARY_IDX : MMU_KERNEL_IDX) + : (asi & 1 ? MMU_USER_SECONDARY_IDX : MMU_USER_IDX)); - if (cpu_get_phys_page_nofault(env, addr, mmu_idx) == -1ULL) { + if (cpu_get_phys_page_nofault(env, addr, idx) == -1ULL) { #ifdef DEBUG_ASI - dump_asi("read ", last_addr, asi, size, ret); + dump_asi("read ", last_addr, asi, size, ret); #endif - /* env->exception_index is set in get_physical_address_data(). */ - helper_raise_exception(env, cs->exception_index); - } - - /* convert nonfaulting load ASIs to normal load ASIs */ - asi &= ~0x02; - } - - switch (asi) { - case ASI_AIUP: /* As if user primary */ - case ASI_AIUS: /* As if user secondary */ - case ASI_AIUPL: /* As if user primary LE */ - case ASI_AIUSL: /* As if user secondary LE */ - case ASI_P: /* Primary */ - case ASI_S: /* Secondary */ - case ASI_PL: /* Primary LE */ - case ASI_SL: /* Secondary LE */ - if ((asi & 0x80) && (env->pstate & PS_PRIV)) { - if (cpu_hypervisor_mode(env)) { - switch (size) { - case 1: - ret = cpu_ldub_hypv(env, addr); - break; - case 2: - ret = cpu_lduw_hypv(env, addr); - break; - case 4: - ret = cpu_ldl_hypv(env, addr); - break; - default: - case 8: - ret = cpu_ldq_hypv(env, addr); - break; - } - } else { - /* secondary space access has lowest asi bit equal to 1 */ - if (asi & 1) { - switch (size) { - case 1: - ret = cpu_ldub_kernel_secondary(env, addr); - break; - case 2: - ret = cpu_lduw_kernel_secondary(env, addr); - break; - case 4: - ret = cpu_ldl_kernel_secondary(env, addr); - break; - default: - case 8: - ret = cpu_ldq_kernel_secondary(env, addr); - break; - } - } else { - switch (size) { - case 1: - ret = cpu_ldub_kernel(env, addr); - break; - case 2: - ret = cpu_lduw_kernel(env, addr); - break; - case 4: - ret = cpu_ldl_kernel(env, addr); - break; - default: - case 8: - ret = cpu_ldq_kernel(env, addr); - break; - } - } + /* exception_index is set in get_physical_address_data. */ + cpu_raise_exception_ra(env, cs->exception_index, GETPC()); } - } else { - /* secondary space access has lowest asi bit equal to 1 */ - if (asi & 1) { - switch (size) { - case 1: - ret = cpu_ldub_user_secondary(env, addr); - break; - case 2: - ret = cpu_lduw_user_secondary(env, addr); - break; - case 4: - ret = cpu_ldl_user_secondary(env, addr); - break; - default: - case 8: - ret = cpu_ldq_user_secondary(env, addr); - break; - } - } else { - switch (size) { - case 1: - ret = cpu_ldub_user(env, addr); - break; - case 2: - ret = cpu_lduw_user(env, addr); - break; - case 4: - ret = cpu_ldl_user(env, addr); - break; - default: - case 8: - ret = cpu_ldq_user(env, addr); - break; - } - } - } - break; - case ASI_REAL: /* Bypass */ - case ASI_REAL_IO: /* Bypass, non-cacheable */ - case ASI_REAL_L: /* Bypass LE */ - case ASI_REAL_IO_L: /* Bypass, non-cacheable LE */ - { + oi = make_memop_idx(memop, idx); switch (size) { case 1: - ret = ldub_phys(cs->as, addr); + ret = helper_ret_ldub_mmu(env, addr, oi, GETPC()); break; case 2: - ret = lduw_phys(cs->as, addr); + if (asi & 8) { + ret = helper_le_lduw_mmu(env, addr, oi, GETPC()); + } else { + ret = helper_be_lduw_mmu(env, addr, oi, GETPC()); + } break; case 4: - ret = ldl_phys(cs->as, addr); + if (asi & 8) { + ret = helper_le_ldul_mmu(env, addr, oi, GETPC()); + } else { + ret = helper_be_ldul_mmu(env, addr, oi, GETPC()); + } break; - default: case 8: - ret = ldq_phys(cs->as, addr); + if (asi & 8) { + ret = helper_le_ldq_mmu(env, addr, oi, GETPC()); + } else { + ret = helper_be_ldq_mmu(env, addr, oi, GETPC()); + } break; + default: + g_assert_not_reached(); } - break; } + break; + + case ASI_AIUP: /* As if user primary */ + case ASI_AIUS: /* As if user secondary */ + case ASI_AIUPL: /* As if user primary LE */ + case ASI_AIUSL: /* As if user secondary LE */ + case ASI_P: /* Primary */ + case ASI_S: /* Secondary */ + case ASI_PL: /* Primary LE */ + case ASI_SL: /* Secondary LE */ + case ASI_REAL: /* Bypass */ + case ASI_REAL_IO: /* Bypass, non-cacheable */ + case ASI_REAL_L: /* Bypass LE */ + case ASI_REAL_IO_L: /* Bypass, non-cacheable LE */ case ASI_N: /* Nucleus */ case ASI_NL: /* Nucleus Little Endian (LE) */ - { - switch (size) { - case 1: - ret = cpu_ldub_nucleus(env, addr); - break; - case 2: - ret = cpu_lduw_nucleus(env, addr); - break; - case 4: - ret = cpu_ldl_nucleus(env, addr); - break; - default: - case 8: - ret = cpu_ldq_nucleus(env, addr); - break; - } - break; - } + case ASI_NUCLEUS_QUAD_LDD: /* Nucleus quad LDD 128 bit atomic */ + case ASI_NUCLEUS_QUAD_LDD_L: /* Nucleus quad LDD 128 bit atomic LE */ + case ASI_TWINX_AIUP: /* As if user primary, twinx */ + case ASI_TWINX_AIUS: /* As if user secondary, twinx */ + case ASI_TWINX_REAL: /* Real address, twinx */ + case ASI_TWINX_AIUP_L: /* As if user primary, twinx, LE */ + case ASI_TWINX_AIUS_L: /* As if user secondary, twinx, LE */ + case ASI_TWINX_REAL_L: /* Real address, twinx, LE */ + case ASI_TWINX_N: /* Nucleus, twinx */ + case ASI_TWINX_NL: /* Nucleus, twinx, LE */ + /* ??? From the UA2011 document; overlaps BLK_INIT_QUAD_LDD_* */ + case ASI_TWINX_P: /* Primary, twinx */ + case ASI_TWINX_PL: /* Primary, twinx, LE */ + case ASI_TWINX_S: /* Secondary, twinx */ + case ASI_TWINX_SL: /* Secondary, twinx, LE */ + /* These are always handled inline. */ + g_assert_not_reached(); + case ASI_UPA_CONFIG: /* UPA config */ /* XXX */ break; @@ -1597,51 +1338,6 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, cpu_unassigned_access(cs, addr, false, false, 1, size); ret = 0; break; - - case ASI_NUCLEUS_QUAD_LDD: /* Nucleus quad LDD 128 bit atomic */ - case ASI_NUCLEUS_QUAD_LDD_L: /* Nucleus quad LDD 128 bit atomic LE */ - case ASI_TWINX_AIUP: /* As if user primary, twinx */ - case ASI_TWINX_AIUS: /* As if user secondary, twinx */ - case ASI_TWINX_REAL: /* Real address, twinx */ - case ASI_TWINX_AIUP_L: /* As if user primary, twinx, LE */ - case ASI_TWINX_AIUS_L: /* As if user secondary, twinx, LE */ - case ASI_TWINX_REAL_L: /* Real address, twinx, LE */ - case ASI_TWINX_N: /* Nucleus, twinx */ - case ASI_TWINX_NL: /* Nucleus, twinx, LE */ - /* ??? From the UA2011 document; overlaps BLK_INIT_QUAD_LDD_* */ - case ASI_TWINX_P: /* Primary, twinx */ - case ASI_TWINX_PL: /* Primary, twinx, LE */ - case ASI_TWINX_S: /* Secondary, twinx */ - case ASI_TWINX_SL: /* Secondary, twinx, LE */ - /* These are all 128-bit atomic; only ldda (now ldtxa) allowed */ - helper_raise_exception(env, TT_ILL_INSN); - return 0; - } - - /* Convert from little endian */ - switch (asi) { - case ASI_NL: /* Nucleus Little Endian (LE) */ - case ASI_AIUPL: /* As if user primary LE */ - case ASI_AIUSL: /* As if user secondary LE */ - case ASI_REAL_L: /* Bypass LE */ - case ASI_REAL_IO_L: /* Bypass, non-cacheable LE */ - case ASI_PL: /* Primary LE */ - case ASI_SL: /* Secondary 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 */ @@ -1683,38 +1379,12 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, || (cpu_has_hypervisor(env) && asi >= 0x30 && asi < 0x80 && !(env->hpstate & HS_PRIV))) { - helper_raise_exception(env, TT_PRIV_ACT); + cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC()); } - helper_check_align(env, addr, size - 1); + do_check_align(env, addr, size - 1, GETPC()); addr = asi_address_mask(env, asi, addr); - /* Convert to little endian */ - switch (asi) { - case ASI_NL: /* Nucleus Little Endian (LE) */ - case ASI_AIUPL: /* As if user primary LE */ - case ASI_AIUSL: /* As if user secondary LE */ - case ASI_REAL_L: /* Bypass LE */ - case ASI_REAL_IO_L: /* Bypass, non-cacheable LE */ - case ASI_PL: /* Primary LE */ - case ASI_SL: /* 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 ASI_AIUP: /* As if user primary */ case ASI_AIUS: /* As if user secondary */ @@ -1724,160 +1394,36 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, case ASI_S: /* Secondary */ case ASI_PL: /* Primary LE */ case ASI_SL: /* Secondary LE */ - if ((asi & 0x80) && (env->pstate & PS_PRIV)) { - if (cpu_hypervisor_mode(env)) { - switch (size) { - case 1: - cpu_stb_hypv(env, addr, val); - break; - case 2: - cpu_stw_hypv(env, addr, val); - break; - case 4: - cpu_stl_hypv(env, addr, val); - break; - case 8: - default: - cpu_stq_hypv(env, addr, val); - break; - } - } else { - /* secondary space access has lowest asi bit equal to 1 */ - if (asi & 1) { - switch (size) { - case 1: - cpu_stb_kernel_secondary(env, addr, val); - break; - case 2: - cpu_stw_kernel_secondary(env, addr, val); - break; - case 4: - cpu_stl_kernel_secondary(env, addr, val); - break; - case 8: - default: - cpu_stq_kernel_secondary(env, addr, val); - break; - } - } else { - switch (size) { - case 1: - cpu_stb_kernel(env, addr, val); - break; - case 2: - cpu_stw_kernel(env, addr, val); - break; - case 4: - cpu_stl_kernel(env, addr, val); - break; - case 8: - default: - cpu_stq_kernel(env, addr, val); - break; - } - } - } - } else { - /* secondary space access has lowest asi bit equal to 1 */ - if (asi & 1) { - switch (size) { - case 1: - cpu_stb_user_secondary(env, addr, val); - break; - case 2: - cpu_stw_user_secondary(env, addr, val); - break; - case 4: - cpu_stl_user_secondary(env, addr, val); - break; - case 8: - default: - cpu_stq_user_secondary(env, addr, val); - break; - } - } else { - switch (size) { - case 1: - cpu_stb_user(env, addr, val); - break; - case 2: - cpu_stw_user(env, addr, val); - break; - case 4: - cpu_stl_user(env, addr, val); - break; - case 8: - default: - cpu_stq_user(env, addr, val); - break; - } - } - } - break; case ASI_REAL: /* Bypass */ case ASI_REAL_IO: /* Bypass, non-cacheable */ case ASI_REAL_L: /* Bypass LE */ case ASI_REAL_IO_L: /* Bypass, non-cacheable LE */ - { - switch (size) { - case 1: - stb_phys(cs->as, addr, val); - break; - case 2: - stw_phys(cs->as, addr, val); - break; - case 4: - stl_phys(cs->as, addr, val); - break; - case 8: - default: - stq_phys(cs->as, addr, val); - break; - } - } - return; case ASI_N: /* Nucleus */ case ASI_NL: /* Nucleus Little Endian (LE) */ - { - switch (size) { - case 1: - cpu_stb_nucleus(env, addr, val); - break; - case 2: - cpu_stw_nucleus(env, addr, val); - break; - case 4: - cpu_stl_nucleus(env, addr, val); - break; - default: - case 8: - cpu_stq_nucleus(env, addr, val); - break; - } - break; - } + case ASI_NUCLEUS_QUAD_LDD: /* Nucleus quad LDD 128 bit atomic */ + case ASI_NUCLEUS_QUAD_LDD_L: /* Nucleus quad LDD 128 bit atomic LE */ + case ASI_TWINX_AIUP: /* As if user primary, twinx */ + case ASI_TWINX_AIUS: /* As if user secondary, twinx */ + case ASI_TWINX_REAL: /* Real address, twinx */ + case ASI_TWINX_AIUP_L: /* As if user primary, twinx, LE */ + case ASI_TWINX_AIUS_L: /* As if user secondary, twinx, LE */ + case ASI_TWINX_REAL_L: /* Real address, twinx, LE */ + case ASI_TWINX_N: /* Nucleus, twinx */ + case ASI_TWINX_NL: /* Nucleus, twinx, LE */ + /* ??? From the UA2011 document; overlaps BLK_INIT_QUAD_LDD_* */ + case ASI_TWINX_P: /* Primary, twinx */ + case ASI_TWINX_PL: /* Primary, twinx, LE */ + case ASI_TWINX_S: /* Secondary, twinx */ + case ASI_TWINX_SL: /* Secondary, twinx, LE */ + /* These are always handled inline. */ + g_assert_not_reached(); case ASI_UPA_CONFIG: /* UPA config */ /* XXX */ return; case ASI_LSU_CONTROL: /* 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(stdout, fprintf, env); -#endif - tlb_flush(CPU(cpu), 1); - } - return; - } + env->lsu = val & (DMMU_E | IMMU_E); + return; case ASI_IMMU: /* I-MMU regs */ { int reg = (addr >> 3) & 0xf; @@ -2016,24 +1562,6 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, case ASI_INTR_RECEIVE: /* Interrupt data receive */ env->ivec_status = val & 0x20; return; - case ASI_NUCLEUS_QUAD_LDD: /* Nucleus quad LDD 128 bit atomic */ - case ASI_NUCLEUS_QUAD_LDD_L: /* Nucleus quad LDD 128 bit atomic LE */ - case ASI_TWINX_AIUP: /* As if user primary, twinx */ - case ASI_TWINX_AIUS: /* As if user secondary, twinx */ - case ASI_TWINX_REAL: /* Real address, twinx */ - case ASI_TWINX_AIUP_L: /* As if user primary, twinx, LE */ - case ASI_TWINX_AIUS_L: /* As if user secondary, twinx, LE */ - case ASI_TWINX_REAL_L: /* Real address, twinx, LE */ - case ASI_TWINX_N: /* Nucleus, twinx */ - case ASI_TWINX_NL: /* Nucleus, twinx, LE */ - /* ??? From the UA2011 document; overlaps BLK_INIT_QUAD_LDD_* */ - case ASI_TWINX_P: /* Primary, twinx */ - case ASI_TWINX_PL: /* Primary, twinx, LE */ - case ASI_TWINX_S: /* Secondary, twinx */ - case ASI_TWINX_SL: /* Secondary, twinx, LE */ - /* Only stda allowed */ - helper_raise_exception(env, TT_ILL_INSN); - return; case ASI_DCACHE_DATA: /* D-cache data */ case ASI_DCACHE_TAG: /* D-cache tag access */ case ASI_ESTATE_ERROR_EN: /* E-cache error enable */ @@ -2066,203 +1594,8 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, } } #endif /* CONFIG_USER_ONLY */ - -/* 128-bit LDDA; result returned in QT0. */ -void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi) -{ - uint64_t h, l; - - if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0) - || (cpu_has_hypervisor(env) - && asi >= 0x30 && asi < 0x80 - && !(env->hpstate & HS_PRIV))) { - helper_raise_exception(env, TT_PRIV_ACT); - } - - addr = asi_address_mask(env, asi, addr); - - switch (asi) { -#if !defined(CONFIG_USER_ONLY) - case ASI_TWINX_AIUP: /* As if user primary, twinx */ - case ASI_TWINX_AIUP_L: /* As if user primary, twinx, LE */ - helper_check_align(env, addr, 0xf); - h = cpu_ldq_user(env, addr); - l = cpu_ldq_user(env, addr + 8); - break; - case ASI_TWINX_AIUS: /* As if user secondary, twinx */ - case ASI_TWINX_AIUS_L: /* As if user secondary, twinx, LE */ - helper_check_align(env, addr, 0xf); - h = cpu_ldq_user_secondary(env, addr); - l = cpu_ldq_user_secondary(env, addr + 8); - break; - case ASI_TWINX_REAL: /* Real address, twinx */ - case ASI_TWINX_REAL_L: /* Real address, twinx, LE */ - helper_check_align(env, addr, 0xf); - { - CPUState *cs = CPU(sparc_env_get_cpu(env)); - h = ldq_phys(cs->as, addr); - l = ldq_phys(cs->as, addr + 8); - } - break; - case ASI_NUCLEUS_QUAD_LDD: - case ASI_NUCLEUS_QUAD_LDD_L: - case ASI_TWINX_N: /* Nucleus, twinx */ - case ASI_TWINX_NL: /* Nucleus, twinx, LE */ - helper_check_align(env, addr, 0xf); - h = cpu_ldq_nucleus(env, addr); - l = cpu_ldq_nucleus(env, addr + 8); - break; - case ASI_TWINX_S: /* Secondary, twinx */ - case ASI_TWINX_SL: /* Secondary, twinx, LE */ - if (!cpu_hypervisor_mode(env)) { - helper_check_align(env, addr, 0xf); - if (env->pstate & PS_PRIV) { - h = cpu_ldq_kernel_secondary(env, addr); - l = cpu_ldq_kernel_secondary(env, addr + 8); - } else { - h = cpu_ldq_user_secondary(env, addr); - l = cpu_ldq_user_secondary(env, addr + 8); - } - break; - } - /* fallthru */ - case ASI_TWINX_P: /* Primary, twinx */ - case ASI_TWINX_PL: /* Primary, twinx, LE */ - helper_check_align(env, addr, 0xf); - h = cpu_ldq_data(env, addr); - l = cpu_ldq_data(env, addr + 8); - break; -#else - case ASI_TWINX_P: /* Primary, twinx */ - case ASI_TWINX_PL: /* Primary, twinx, LE */ - case ASI_TWINX_S: /* Primary, twinx */ - case ASI_TWINX_SL: /* Primary, twinx, LE */ - /* ??? Should be available, but we need to implement - an atomic 128-bit load. */ - helper_raise_exception(env, TT_PRIV_ACT); -#endif - default: - /* Non-twinx asi, so this is the legacy ldda insn, which - performs two word sized operations. */ - /* ??? The UA2011 manual recommends emulating this with - a single 64-bit load. However, LE asis *are* treated - as two 32-bit loads individually byte swapped. */ - helper_check_align(env, addr, 0x7); - QT0.high = (uint32_t)helper_ld_asi(env, addr, asi, MO_UL); - QT0.low = (uint32_t)helper_ld_asi(env, addr + 4, asi, MO_UL); - return; - } - - if (asi & 8) { - h = bswap64(h); - l = bswap64(l); - } - QT0.high = h; - QT0.low = l; -} - -target_ulong helper_casx_asi(CPUSPARCState *env, target_ulong addr, - target_ulong val1, target_ulong val2, - uint32_t asi) -{ - target_ulong ret; - - ret = helper_ld_asi(env, addr, asi, MO_Q); - if (val2 == ret) { - helper_st_asi(env, addr, val1, asi, MO_Q); - } - return ret; -} #endif /* TARGET_SPARC64 */ -#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) -target_ulong helper_cas_asi(CPUSPARCState *env, target_ulong addr, - target_ulong val1, target_ulong val2, uint32_t asi) -{ - target_ulong ret; - - val2 &= 0xffffffffUL; - ret = helper_ld_asi(env, addr, asi, MO_UL); - ret &= 0xffffffffUL; - if (val2 == ret) { - helper_st_asi(env, addr, val1 & 0xffffffffUL, asi, MO_UL); - } - return ret; -} -#endif /* !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) */ - -void helper_ldqf(CPUSPARCState *env, target_ulong addr, int mem_idx) -{ - /* XXX add 128 bit load */ - CPU_QuadU u; - - helper_check_align(env, addr, 7); -#if !defined(CONFIG_USER_ONLY) - switch (mem_idx) { - case MMU_USER_IDX: - u.ll.upper = cpu_ldq_user(env, addr); - u.ll.lower = cpu_ldq_user(env, addr + 8); - QT0 = u.q; - break; - case MMU_KERNEL_IDX: - u.ll.upper = cpu_ldq_kernel(env, addr); - u.ll.lower = cpu_ldq_kernel(env, addr + 8); - QT0 = u.q; - break; -#ifdef TARGET_SPARC64 - case MMU_HYPV_IDX: - u.ll.upper = cpu_ldq_hypv(env, addr); - u.ll.lower = cpu_ldq_hypv(env, addr + 8); - QT0 = u.q; - break; -#endif - default: - DPRINTF_MMU("helper_ldqf: need to check MMU idx %d\n", mem_idx); - break; - } -#else - u.ll.upper = cpu_ldq_data(env, address_mask(env, addr)); - u.ll.lower = cpu_ldq_data(env, address_mask(env, addr + 8)); - QT0 = u.q; -#endif -} - -void helper_stqf(CPUSPARCState *env, target_ulong addr, int mem_idx) -{ - /* XXX add 128 bit store */ - CPU_QuadU u; - - helper_check_align(env, addr, 7); -#if !defined(CONFIG_USER_ONLY) - switch (mem_idx) { - case MMU_USER_IDX: - u.q = QT0; - cpu_stq_user(env, addr, u.ll.upper); - cpu_stq_user(env, addr + 8, u.ll.lower); - break; - case MMU_KERNEL_IDX: - u.q = QT0; - cpu_stq_kernel(env, addr, u.ll.upper); - cpu_stq_kernel(env, addr + 8, u.ll.lower); - break; -#ifdef TARGET_SPARC64 - case MMU_HYPV_IDX: - u.q = QT0; - cpu_stq_hypv(env, addr, u.ll.upper); - cpu_stq_hypv(env, addr + 8, u.ll.lower); - break; -#endif - default: - DPRINTF_MMU("helper_stqf: need to check MMU idx %d\n", mem_idx); - break; - } -#else - u.q = QT0; - cpu_stq_data(env, address_mask(env, addr), u.ll.upper); - cpu_stq_data(env, address_mask(env, addr + 8), u.ll.lower); -#endif -} - #if !defined(CONFIG_USER_ONLY) #ifndef TARGET_SPARC64 void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr, @@ -2314,11 +1647,8 @@ void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr, } if ((env->mmuregs[0] & MMU_E) && !(env->mmuregs[0] & MMU_NF)) { - if (is_exec) { - helper_raise_exception(env, TT_CODE_ACCESS); - } else { - helper_raise_exception(env, TT_DATA_ACCESS); - } + int tt = is_exec ? TT_CODE_ACCESS : TT_DATA_ACCESS; + cpu_raise_exception_ra(env, tt, GETPC()); } /* flush neverland mappings created during no-fault mode, @@ -2334,17 +1664,14 @@ void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr, { SPARCCPU *cpu = SPARC_CPU(cs); CPUSPARCState *env = &cpu->env; + int tt = is_exec ? TT_CODE_ACCESS : TT_DATA_ACCESS; #ifdef DEBUG_UNASSIGNED printf("Unassigned mem access to " TARGET_FMT_plx " from " TARGET_FMT_lx "\n", addr, env->pc); #endif - if (is_exec) { - helper_raise_exception(env, TT_CODE_ACCESS); - } else { - helper_raise_exception(env, TT_DATA_ACCESS); - } + cpu_raise_exception_ra(env, tt, GETPC()); } #endif #endif @@ -2362,10 +1689,7 @@ void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx "\n", addr, env->pc); #endif - if (retaddr) { - cpu_restore_state(CPU(cpu), retaddr); - } - helper_raise_exception(env, TT_UNALIGNED); + cpu_raise_exception_ra(env, TT_UNALIGNED, retaddr); } /* try to fill the TLB and return an exception if error. If retaddr is @@ -2379,10 +1703,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type, ret = sparc_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx); if (ret) { - if (retaddr) { - cpu_restore_state(cs, retaddr); - } - cpu_loop_exit(cs); + cpu_loop_exit_restore(cs, retaddr); } } #endif |