diff options
Diffstat (limited to 'target-ppc/helper.c')
-rw-r--r-- | target-ppc/helper.c | 1746 |
1 files changed, 960 insertions, 786 deletions
diff --git a/target-ppc/helper.c b/target-ppc/helper.c index 5b0fd09b9..a808454a2 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -27,10 +27,13 @@ #include "cpu.h" #include "exec-all.h" +#include "helper_regs.h" //#define DEBUG_MMU //#define DEBUG_BATS +//#define DEBUG_SLB //#define DEBUG_SOFTWARE_TLB +//#define DUMP_PAGE_TABLES //#define DEBUG_EXCEPTIONS //#define FLUSH_ALL_TLBS @@ -39,16 +42,16 @@ #if defined(CONFIG_USER_ONLY) int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, - int is_user, int is_softmmu) + int mmu_idx, int is_softmmu) { int exception, error_code; if (rw == 2) { exception = POWERPC_EXCP_ISI; - error_code = 0; + error_code = 0x40000000; } else { exception = POWERPC_EXCP_DSI; - error_code = 0; + error_code = 0x40000000; if (rw) error_code |= 0x02000000; env->spr[SPR_DAR] = address; @@ -67,23 +70,23 @@ target_phys_addr_t cpu_get_phys_page_debug (CPUState *env, target_ulong addr) #else /* Common routines used by software and hardware TLBs emulation */ -static inline int pte_is_valid (target_ulong pte0) +static always_inline int pte_is_valid (target_ulong pte0) { return pte0 & 0x80000000 ? 1 : 0; } -static inline void pte_invalidate (target_ulong *pte0) +static always_inline void pte_invalidate (target_ulong *pte0) { *pte0 &= ~0x80000000; } #if defined(TARGET_PPC64) -static inline int pte64_is_valid (target_ulong pte0) +static always_inline int pte64_is_valid (target_ulong pte0) { return pte0 & 0x0000000000000001ULL ? 1 : 0; } -static inline void pte64_invalidate (target_ulong *pte0) +static always_inline void pte64_invalidate (target_ulong *pte0) { *pte0 &= ~0x0000000000000001ULL; } @@ -96,12 +99,76 @@ static inline void pte64_invalidate (target_ulong *pte0) #define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F) #endif -static inline int _pte_check (mmu_ctx_t *ctx, int is_64b, - target_ulong pte0, target_ulong pte1, - int h, int rw) +static always_inline int pp_check (int key, int pp, int nx) +{ + int access; + + /* Compute access rights */ + /* When pp is 3/7, the result is undefined. Set it to noaccess */ + access = 0; + if (key == 0) { + switch (pp) { + case 0x0: + case 0x1: + case 0x2: + access |= PAGE_WRITE; + /* No break here */ + case 0x3: + case 0x6: + access |= PAGE_READ; + break; + } + } else { + switch (pp) { + case 0x0: + case 0x6: + access = 0; + break; + case 0x1: + case 0x3: + access = PAGE_READ; + break; + case 0x2: + access = PAGE_READ | PAGE_WRITE; + break; + } + } + if (nx == 0) + access |= PAGE_EXEC; + + return access; +} + +static always_inline int check_prot (int prot, int rw, int access_type) +{ + int ret; + + if (access_type == ACCESS_CODE) { + if (prot & PAGE_EXEC) + ret = 0; + else + ret = -2; + } else if (rw) { + if (prot & PAGE_WRITE) + ret = 0; + else + ret = -2; + } else { + if (prot & PAGE_READ) + ret = 0; + else + ret = -2; + } + + return ret; +} + +static always_inline int _pte_check (mmu_ctx_t *ctx, int is_64b, + target_ulong pte0, target_ulong pte1, + int h, int rw, int type) { target_ulong ptem, mmask; - int access, ret, pteh, ptev; + int access, ret, pteh, ptev, pp; access = 0; ret = -1; @@ -122,14 +189,18 @@ static inline int _pte_check (mmu_ctx_t *ctx, int is_64b, if (is_64b) { ptem = pte0 & PTE64_PTEM_MASK; mmask = PTE64_CHECK_MASK; + pp = (pte1 & 0x00000003) | ((pte1 >> 61) & 0x00000004); + ctx->nx |= (pte1 >> 2) & 1; /* No execute bit */ + ctx->nx |= (pte1 >> 3) & 1; /* Guarded bit */ } else #endif { ptem = pte0 & PTE_PTEM_MASK; mmask = PTE_CHECK_MASK; + pp = pte1 & 0x00000003; } if (ptem == ctx->ptem) { - if (ctx->raddr != (target_ulong)-1) { + if (ctx->raddr != (target_phys_addr_t)-1ULL) { /* all matches should have equal RPN, WIMG & PP */ if ((ctx->raddr & mmask) != (pte1 & mmask)) { if (loglevel != 0) @@ -138,42 +209,23 @@ static inline int _pte_check (mmu_ctx_t *ctx, int is_64b, } } /* Compute access rights */ - if (ctx->key == 0) { - access = PAGE_READ; - if ((pte1 & 0x00000003) != 0x3) - access |= PAGE_WRITE; - } else { - switch (pte1 & 0x00000003) { - case 0x0: - access = 0; - break; - case 0x1: - case 0x3: - access = PAGE_READ; - break; - case 0x2: - access = PAGE_READ | PAGE_WRITE; - break; - } - } + access = pp_check(ctx->key, pp, ctx->nx); /* Keep the matching PTE informations */ ctx->raddr = pte1; ctx->prot = access; - if ((rw == 0 && (access & PAGE_READ)) || - (rw == 1 && (access & PAGE_WRITE))) { + ret = check_prot(ctx->prot, rw, type); + if (ret == 0) { /* Access granted */ #if defined (DEBUG_MMU) if (loglevel != 0) fprintf(logfile, "PTE access granted !\n"); #endif - ret = 0; } else { /* Access right violation */ #if defined (DEBUG_MMU) if (loglevel != 0) fprintf(logfile, "PTE access rejected\n"); #endif - ret = -2; } } } @@ -181,22 +233,24 @@ static inline int _pte_check (mmu_ctx_t *ctx, int is_64b, return ret; } -static int pte32_check (mmu_ctx_t *ctx, - target_ulong pte0, target_ulong pte1, int h, int rw) +static always_inline int pte32_check (mmu_ctx_t *ctx, + target_ulong pte0, target_ulong pte1, + int h, int rw, int type) { - return _pte_check(ctx, 0, pte0, pte1, h, rw); + return _pte_check(ctx, 0, pte0, pte1, h, rw, type); } #if defined(TARGET_PPC64) -static int pte64_check (mmu_ctx_t *ctx, - target_ulong pte0, target_ulong pte1, int h, int rw) +static always_inline int pte64_check (mmu_ctx_t *ctx, + target_ulong pte0, target_ulong pte1, + int h, int rw, int type) { - return _pte_check(ctx, 1, pte0, pte1, h, rw); + return _pte_check(ctx, 1, pte0, pte1, h, rw, type); } #endif -static int pte_update_flags (mmu_ctx_t *ctx, target_ulong *pte1p, - int ret, int rw) +static always_inline int pte_update_flags (mmu_ctx_t *ctx, target_ulong *pte1p, + int ret, int rw) { int store = 0; @@ -221,8 +275,8 @@ static int pte_update_flags (mmu_ctx_t *ctx, target_ulong *pte1p, } /* Software driven TLB helpers */ -static int ppc6xx_tlb_getnum (CPUState *env, target_ulong eaddr, - int way, int is_code) +static always_inline int ppc6xx_tlb_getnum (CPUState *env, target_ulong eaddr, + int way, int is_code) { int nr; @@ -237,7 +291,7 @@ static int ppc6xx_tlb_getnum (CPUState *env, target_ulong eaddr, return nr; } -static void ppc6xx_tlb_invalidate_all (CPUState *env) +static always_inline void ppc6xx_tlb_invalidate_all (CPUState *env) { ppc6xx_tlb_t *tlb; int nr, max; @@ -258,9 +312,10 @@ static void ppc6xx_tlb_invalidate_all (CPUState *env) tlb_flush(env, 1); } -static inline void __ppc6xx_tlb_invalidate_virt (CPUState *env, - target_ulong eaddr, - int is_code, int match_epn) +static always_inline void __ppc6xx_tlb_invalidate_virt (CPUState *env, + target_ulong eaddr, + int is_code, + int match_epn) { #if !defined(FLUSH_ALL_TLBS) ppc6xx_tlb_t *tlb; @@ -287,8 +342,9 @@ static inline void __ppc6xx_tlb_invalidate_virt (CPUState *env, #endif } -static void ppc6xx_tlb_invalidate_virt (CPUState *env, target_ulong eaddr, - int is_code) +static always_inline void ppc6xx_tlb_invalidate_virt (CPUState *env, + target_ulong eaddr, + int is_code) { __ppc6xx_tlb_invalidate_virt(env, eaddr, is_code, 0); } @@ -316,8 +372,9 @@ void ppc6xx_tlb_store (CPUState *env, target_ulong EPN, int way, int is_code, env->last_way = way; } -static int ppc6xx_tlb_check (CPUState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int access_type) +static always_inline int ppc6xx_tlb_check (CPUState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, + int access_type) { ppc6xx_tlb_t *tlb; int nr, best, way; @@ -352,7 +409,7 @@ static int ppc6xx_tlb_check (CPUState *env, mmu_ctx_t *ctx, rw ? 'S' : 'L', access_type == ACCESS_CODE ? 'I' : 'D'); } #endif - switch (pte32_check(ctx, tlb->pte0, tlb->pte1, 0, rw)) { + switch (pte32_check(ctx, tlb->pte0, tlb->pte1, 0, rw, access_type)) { case -3: /* TLB inconsistency */ return -1; @@ -380,7 +437,7 @@ static int ppc6xx_tlb_check (CPUState *env, mmu_ctx_t *ctx, done: #if defined (DEBUG_SOFTWARE_TLB) if (loglevel != 0) { - fprintf(logfile, "found TLB at addr 0x%08lx prot=0x%01x ret=%d\n", + fprintf(logfile, "found TLB at addr " PADDRX " prot=%01x ret=%d\n", ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); } #endif @@ -392,17 +449,72 @@ static int ppc6xx_tlb_check (CPUState *env, mmu_ctx_t *ctx, } /* Perform BAT hit & translation */ -static int get_bat (CPUState *env, mmu_ctx_t *ctx, - target_ulong virtual, int rw, int type) +static always_inline void bat_size_prot (CPUState *env, target_ulong *blp, + int *validp, int *protp, + target_ulong *BATu, target_ulong *BATl) +{ + target_ulong bl; + int pp, valid, prot; + + bl = (*BATu & 0x00001FFC) << 15; + valid = 0; + prot = 0; + if (((msr_pr == 0) && (*BATu & 0x00000002)) || + ((msr_pr != 0) && (*BATu & 0x00000001))) { + valid = 1; + pp = *BATl & 0x00000003; + if (pp != 0) { + prot = PAGE_READ | PAGE_EXEC; + if (pp == 0x2) + prot |= PAGE_WRITE; + } + } + *blp = bl; + *validp = valid; + *protp = prot; +} + +static always_inline void bat_601_size_prot (CPUState *env,target_ulong *blp, + int *validp, int *protp, + target_ulong *BATu, + target_ulong *BATl) +{ + target_ulong bl; + int key, pp, valid, prot; + + bl = (*BATl & 0x0000003F) << 17; +#if defined (DEBUG_BATS) + if (loglevel != 0) { + fprintf(logfile, "b %02x ==> bl " ADDRX " msk " ADDRX "\n", + (uint8_t)(*BATl & 0x0000003F), bl, ~bl); + } +#endif + prot = 0; + valid = (*BATl >> 6) & 1; + if (valid) { + pp = *BATu & 0x00000003; + if (msr_pr == 0) + key = (*BATu >> 3) & 1; + else + key = (*BATu >> 2) & 1; + prot = pp_check(key, pp, 0); + } + *blp = bl; + *validp = valid; + *protp = prot; +} + +static always_inline int get_bat (CPUState *env, mmu_ctx_t *ctx, + target_ulong virtual, int rw, int type) { target_ulong *BATlt, *BATut, *BATu, *BATl; target_ulong base, BEPIl, BEPIu, bl; - int i; + int i, valid, prot; int ret = -1; #if defined (DEBUG_BATS) if (loglevel != 0) { - fprintf(logfile, "%s: %cBAT v 0x" ADDRX "\n", __func__, + fprintf(logfile, "%s: %cBAT v " ADDRX "\n", __func__, type == ACCESS_CODE ? 'I' : 'D', virtual); } #endif @@ -416,49 +528,42 @@ static int get_bat (CPUState *env, mmu_ctx_t *ctx, BATut = env->DBAT[0]; break; } -#if defined (DEBUG_BATS) - if (loglevel != 0) { - fprintf(logfile, "%s...: %cBAT v 0x" ADDRX "\n", __func__, - type == ACCESS_CODE ? 'I' : 'D', virtual); - } -#endif base = virtual & 0xFFFC0000; - for (i = 0; i < 4; i++) { + for (i = 0; i < env->nb_BATs; i++) { BATu = &BATut[i]; BATl = &BATlt[i]; BEPIu = *BATu & 0xF0000000; BEPIl = *BATu & 0x0FFE0000; - bl = (*BATu & 0x00001FFC) << 15; + if (unlikely(env->mmu_model == POWERPC_MMU_601)) { + bat_601_size_prot(env, &bl, &valid, &prot, BATu, BATl); + } else { + bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); + } #if defined (DEBUG_BATS) if (loglevel != 0) { - fprintf(logfile, "%s: %cBAT%d v 0x" ADDRX " BATu 0x" ADDRX - " BATl 0x" ADDRX "\n", - __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, - *BATu, *BATl); + fprintf(logfile, "%s: %cBAT%d v " ADDRX " BATu " ADDRX + " BATl " ADDRX "\n", __func__, + type == ACCESS_CODE ? 'I' : 'D', i, virtual, *BATu, *BATl); } #endif if ((virtual & 0xF0000000) == BEPIu && ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { /* BAT matches */ - if ((msr_pr == 0 && (*BATu & 0x00000002)) || - (msr_pr == 1 && (*BATu & 0x00000001))) { + if (valid != 0) { /* Get physical address */ ctx->raddr = (*BATl & 0xF0000000) | ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | (virtual & 0x0001F000); - if (*BATl & 0x00000001) - ctx->prot = PAGE_READ; - if (*BATl & 0x00000002) - ctx->prot = PAGE_WRITE | PAGE_READ; + /* Compute access rights */ + ctx->prot = prot; + ret = check_prot(ctx->prot, rw, type); #if defined (DEBUG_BATS) - if (loglevel != 0) { - fprintf(logfile, "BAT %d match: r 0x" PADDRX - " prot=%c%c\n", + if (ret == 0 && loglevel != 0) { + fprintf(logfile, "BAT %d match: r " PADDRX " prot=%c%c\n", i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', ctx->prot & PAGE_WRITE ? 'W' : '-'); } #endif - ret = 0; break; } } @@ -466,28 +571,29 @@ static int get_bat (CPUState *env, mmu_ctx_t *ctx, if (ret < 0) { #if defined (DEBUG_BATS) if (loglevel != 0) { - fprintf(logfile, "no BAT match for 0x" ADDRX ":\n", virtual); + fprintf(logfile, "no BAT match for " ADDRX ":\n", virtual); for (i = 0; i < 4; i++) { BATu = &BATut[i]; BATl = &BATlt[i]; BEPIu = *BATu & 0xF0000000; BEPIl = *BATu & 0x0FFE0000; bl = (*BATu & 0x00001FFC) << 15; - fprintf(logfile, "%s: %cBAT%d v 0x" ADDRX " BATu 0x" ADDRX - " BATl 0x" ADDRX " \n\t" - "0x" ADDRX " 0x" ADDRX " 0x" ADDRX "\n", + fprintf(logfile, "%s: %cBAT%d v " ADDRX " BATu " ADDRX + " BATl " ADDRX " \n\t" ADDRX " " ADDRX " " ADDRX "\n", __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, *BATu, *BATl, BEPIu, BEPIl, bl); } } #endif } + /* No hit */ return ret; } /* PTE table lookup */ -static inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h, int rw) +static always_inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h, + int rw, int type) { target_ulong base, pte0, pte1; int i, good = -1; @@ -500,22 +606,32 @@ static inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h, int rw) if (is_64b) { pte0 = ldq_phys(base + (i * 16)); pte1 = ldq_phys(base + (i * 16) + 8); - r = pte64_check(ctx, pte0, pte1, h, rw); + r = pte64_check(ctx, pte0, pte1, h, rw, type); +#if defined (DEBUG_MMU) + if (loglevel != 0) { + fprintf(logfile, "Load pte from " ADDRX " => " ADDRX " " ADDRX + " %d %d %d " ADDRX "\n", + base + (i * 16), pte0, pte1, + (int)(pte0 & 1), h, (int)((pte0 >> 1) & 1), + ctx->ptem); + } +#endif } else #endif { pte0 = ldl_phys(base + (i * 8)); pte1 = ldl_phys(base + (i * 8) + 4); - r = pte32_check(ctx, pte0, pte1, h, rw); - } + r = pte32_check(ctx, pte0, pte1, h, rw, type); #if defined (DEBUG_MMU) - if (loglevel != 0) { - fprintf(logfile, "Load pte from 0x" ADDRX " => 0x" ADDRX - " 0x" ADDRX " %d %d %d 0x" ADDRX "\n", - base + (i * 8), pte0, pte1, - (int)(pte0 >> 31), h, (int)((pte0 >> 6) & 1), ctx->ptem); - } + if (loglevel != 0) { + fprintf(logfile, "Load pte from " ADDRX " => " ADDRX " " ADDRX + " %d %d %d " ADDRX "\n", + base + (i * 8), pte0, pte1, + (int)(pte0 >> 31), h, (int)((pte0 >> 6) & 1), + ctx->ptem); + } #endif + } switch (r) { case -3: /* PTE inconsistency */ @@ -544,8 +660,7 @@ static inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h, int rw) done: #if defined (DEBUG_MMU) if (loglevel != 0) { - fprintf(logfile, "found PTE at addr 0x" PADDRX " prot=0x%01x " - "ret=%d\n", + fprintf(logfile, "found PTE at addr " PADDRX " prot=%01x ret=%d\n", ctx->raddr, ctx->prot, ret); } #endif @@ -566,59 +681,69 @@ static inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h, int rw) return ret; } -static int find_pte32 (mmu_ctx_t *ctx, int h, int rw) +static always_inline int find_pte32 (mmu_ctx_t *ctx, int h, int rw, int type) { - return _find_pte(ctx, 0, h, rw); + return _find_pte(ctx, 0, h, rw, type); } #if defined(TARGET_PPC64) -static int find_pte64 (mmu_ctx_t *ctx, int h, int rw) +static always_inline int find_pte64 (mmu_ctx_t *ctx, int h, int rw, int type) { - return _find_pte(ctx, 1, h, rw); + return _find_pte(ctx, 1, h, rw, type); } #endif -static inline int find_pte (CPUState *env, mmu_ctx_t *ctx, int h, int rw) +static always_inline int find_pte (CPUState *env, mmu_ctx_t *ctx, + int h, int rw, int type) { #if defined(TARGET_PPC64) - if (env->mmu_model == POWERPC_MMU_64B || - env->mmu_model == POWERPC_MMU_64BRIDGE) - return find_pte64(ctx, h, rw); + if (env->mmu_model & POWERPC_MMU_64) + return find_pte64(ctx, h, rw, type); #endif - return find_pte32(ctx, h, rw); + return find_pte32(ctx, h, rw, type); +} + +#if defined(TARGET_PPC64) +static always_inline int slb_is_valid (uint64_t slb64) +{ + return slb64 & 0x0000000008000000ULL ? 1 : 0; } -static inline target_phys_addr_t get_pgaddr (target_phys_addr_t sdr1, - int sdr_sh, - target_phys_addr_t hash, - target_phys_addr_t mask) +static always_inline void slb_invalidate (uint64_t *slb64) { - return (sdr1 & ((target_ulong)(-1ULL) << sdr_sh)) | (hash & mask); + *slb64 &= ~0x0000000008000000ULL; } -#if defined(TARGET_PPC64) -static int slb_lookup (CPUState *env, target_ulong eaddr, - target_ulong *vsid, target_ulong *page_mask, int *attr) +static always_inline int slb_lookup (CPUPPCState *env, target_ulong eaddr, + target_ulong *vsid, + target_ulong *page_mask, int *attr) { target_phys_addr_t sr_base; target_ulong mask; uint64_t tmp64; uint32_t tmp; int n, ret; - int slb_nr; ret = -5; sr_base = env->spr[SPR_ASR]; - mask = 0x0000000000000000ULL; /* Avoid gcc warning */ -#if 0 /* XXX: Fix this */ - slb_nr = env->slb_nr; -#else - slb_nr = 32; +#if defined(DEBUG_SLB) + if (loglevel != 0) { + fprintf(logfile, "%s: eaddr " ADDRX " base " PADDRX "\n", + __func__, eaddr, sr_base); + } #endif - for (n = 0; n < slb_nr; n++) { + mask = 0x0000000000000000ULL; /* Avoid gcc warning */ + for (n = 0; n < env->slb_nr; n++) { tmp64 = ldq_phys(sr_base); - if (tmp64 & 0x0000000008000000ULL) { + tmp = ldl_phys(sr_base + 8); +#if defined(DEBUG_SLB) + if (loglevel != 0) { + fprintf(logfile, "%s: seg %d " PADDRX " %016" PRIx64 " %08" + PRIx32 "\n", __func__, n, sr_base, tmp64, tmp); + } +#endif + if (slb_is_valid(tmp64)) { /* SLB entry is valid */ switch (tmp64 & 0x0000000006000000ULL) { case 0x0000000000000000ULL: @@ -636,11 +761,10 @@ static int slb_lookup (CPUState *env, target_ulong eaddr, } if ((eaddr & mask) == (tmp64 & mask)) { /* SLB match */ - tmp = ldl_phys(sr_base + 8); *vsid = ((tmp64 << 24) | (tmp >> 8)) & 0x0003FFFFFFFFFFFFULL; *page_mask = ~mask; *attr = tmp & 0xFF; - ret = 0; + ret = n; break; } } @@ -649,30 +773,154 @@ static int slb_lookup (CPUState *env, target_ulong eaddr, return ret; } + +void ppc_slb_invalidate_all (CPUPPCState *env) +{ + target_phys_addr_t sr_base; + uint64_t tmp64; + int n, do_invalidate; + + do_invalidate = 0; + sr_base = env->spr[SPR_ASR]; + /* XXX: Warning: slbia never invalidates the first segment */ + for (n = 1; n < env->slb_nr; n++) { + tmp64 = ldq_phys(sr_base); + if (slb_is_valid(tmp64)) { + slb_invalidate(&tmp64); + stq_phys(sr_base, tmp64); + /* XXX: given the fact that segment size is 256 MB or 1TB, + * and we still don't have a tlb_flush_mask(env, n, mask) + * in Qemu, we just invalidate all TLBs + */ + do_invalidate = 1; + } + sr_base += 12; + } + if (do_invalidate) + tlb_flush(env, 1); +} + +void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0) +{ + target_phys_addr_t sr_base; + target_ulong vsid, page_mask; + uint64_t tmp64; + int attr; + int n; + + n = slb_lookup(env, T0, &vsid, &page_mask, &attr); + if (n >= 0) { + sr_base = env->spr[SPR_ASR]; + sr_base += 12 * n; + tmp64 = ldq_phys(sr_base); + if (slb_is_valid(tmp64)) { + slb_invalidate(&tmp64); + stq_phys(sr_base, tmp64); + /* XXX: given the fact that segment size is 256 MB or 1TB, + * and we still don't have a tlb_flush_mask(env, n, mask) + * in Qemu, we just invalidate all TLBs + */ + tlb_flush(env, 1); + } + } +} + +target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr) +{ + target_phys_addr_t sr_base; + target_ulong rt; + uint64_t tmp64; + uint32_t tmp; + + sr_base = env->spr[SPR_ASR]; + sr_base += 12 * slb_nr; + tmp64 = ldq_phys(sr_base); + tmp = ldl_phys(sr_base + 8); + if (tmp64 & 0x0000000008000000ULL) { + /* SLB entry is valid */ + /* Copy SLB bits 62:88 to Rt 37:63 (VSID 23:49) */ + rt = tmp >> 8; /* 65:88 => 40:63 */ + rt |= (tmp64 & 0x7) << 24; /* 62:64 => 37:39 */ + /* Copy SLB bits 89:92 to Rt 33:36 (KsKpNL) */ + rt |= ((tmp >> 4) & 0xF) << 27; + } else { + rt = 0; + } +#if defined(DEBUG_SLB) + if (loglevel != 0) { + fprintf(logfile, "%s: " PADDRX " %016" PRIx64 " %08" PRIx32 " => %d " + ADDRX "\n", __func__, sr_base, tmp64, tmp, slb_nr, rt); + } +#endif + + return rt; +} + +void ppc_store_slb (CPUPPCState *env, int slb_nr, target_ulong rs) +{ + target_phys_addr_t sr_base; + uint64_t tmp64; + uint32_t tmp; + + sr_base = env->spr[SPR_ASR]; + sr_base += 12 * slb_nr; + /* Copy Rs bits 37:63 to SLB 62:88 */ + tmp = rs << 8; + tmp64 = (rs >> 24) & 0x7; + /* Copy Rs bits 33:36 to SLB 89:92 */ + tmp |= ((rs >> 27) & 0xF) << 4; + /* Set the valid bit */ + tmp64 |= 1 << 27; + /* Set ESID */ + tmp64 |= (uint32_t)slb_nr << 28; +#if defined(DEBUG_SLB) + if (loglevel != 0) { + fprintf(logfile, "%s: %d " ADDRX " => " PADDRX " %016" PRIx64 + " %08" PRIx32 "\n", __func__, + slb_nr, rs, sr_base, tmp64, tmp); + } +#endif + /* Write SLB entry to memory */ + stq_phys(sr_base, tmp64); + stl_phys(sr_base + 8, tmp); +} #endif /* defined(TARGET_PPC64) */ /* Perform segment based translation */ -static int get_segment (CPUState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int type) +static always_inline target_phys_addr_t get_pgaddr (target_phys_addr_t sdr1, + int sdr_sh, + target_phys_addr_t hash, + target_phys_addr_t mask) +{ + return (sdr1 & ((target_phys_addr_t)(-1ULL) << sdr_sh)) | (hash & mask); +} + +static always_inline int get_segment (CPUState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int type) { - target_phys_addr_t sdr, hash, mask, sdr_mask; + target_phys_addr_t sdr, hash, mask, sdr_mask, htab_mask; target_ulong sr, vsid, vsid_mask, pgidx, page_mask; #if defined(TARGET_PPC64) int attr; #endif - int ds, nx, vsid_sh, sdr_sh; + int ds, vsid_sh, sdr_sh, pr; int ret, ret2; + pr = msr_pr; #if defined(TARGET_PPC64) - if (env->mmu_model == POWERPC_MMU_64B || - env->mmu_model == POWERPC_MMU_64BRIDGE) { + if (env->mmu_model & POWERPC_MMU_64) { +#if defined (DEBUG_MMU) + if (loglevel != 0) { + fprintf(logfile, "Check SLBs\n"); + } +#endif ret = slb_lookup(env, eaddr, &vsid, &page_mask, &attr); if (ret < 0) return ret; - ctx->key = ((attr & 0x40) && msr_pr == 1) || - ((attr & 0x80) && msr_pr == 0) ? 1 : 0; + ctx->key = ((attr & 0x40) && (pr != 0)) || + ((attr & 0x80) && (pr == 0)) ? 1 : 0; ds = 0; - nx = attr & 0x20 ? 1 : 0; + ctx->nx = attr & 0x20 ? 1 : 0; vsid_mask = 0x00003FFFFFFFFF80ULL; vsid_sh = 7; sdr_sh = 18; @@ -682,10 +930,10 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, { sr = env->sr[eaddr >> 28]; page_mask = 0x0FFFFFFF; - ctx->key = (((sr & 0x20000000) && msr_pr == 1) || - ((sr & 0x40000000) && msr_pr == 0)) ? 1 : 0; + ctx->key = (((sr & 0x20000000) && (pr != 0)) || + ((sr & 0x40000000) && (pr == 0))) ? 1 : 0; ds = sr & 0x80000000 ? 1 : 0; - nx = sr & 0x10000000 ? 1 : 0; + ctx->nx = sr & 0x10000000 ? 1 : 0; vsid = sr & 0x00FFFFFF; vsid_mask = 0x01FFFFC0; vsid_sh = 6; @@ -693,35 +941,60 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, sdr_mask = 0xFFC0; #if defined (DEBUG_MMU) if (loglevel != 0) { - fprintf(logfile, "Check segment v=0x" ADDRX " %d 0x" ADDRX - " nip=0x" ADDRX " lr=0x" ADDRX - " ir=%d dr=%d pr=%d %d t=%d\n", + fprintf(logfile, "Check segment v=" ADDRX " %d " ADDRX + " nip=" ADDRX " lr=" ADDRX " ir=%d dr=%d pr=%d %d t=%d\n", eaddr, (int)(eaddr >> 28), sr, env->nip, - env->lr, msr_ir, msr_dr, msr_pr, rw, type); - } - if (!ds && loglevel != 0) { - fprintf(logfile, "pte segment: key=%d n=0x" ADDRX "\n", - ctx->key, sr & 0x10000000); + env->lr, (int)msr_ir, (int)msr_dr, pr != 0 ? 1 : 0, + rw, type); } #endif } +#if defined (DEBUG_MMU) + if (loglevel != 0) { + fprintf(logfile, "pte segment: key=%d ds %d nx %d vsid " ADDRX "\n", + ctx->key, ds, ctx->nx, vsid); + } +#endif ret = -1; if (!ds) { /* Check if instruction fetch is allowed, if needed */ - if (type != ACCESS_CODE || nx == 0) { + if (type != ACCESS_CODE || ctx->nx == 0) { /* Page address translation */ - pgidx = (eaddr & page_mask) >> TARGET_PAGE_BITS; - hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask; /* Primary table address */ sdr = env->sdr1; - mask = ((sdr & 0x000001FF) << sdr_sh) | sdr_mask; + pgidx = (eaddr & page_mask) >> TARGET_PAGE_BITS; +#if defined(TARGET_PPC64) + if (env->mmu_model & POWERPC_MMU_64) { + htab_mask = 0x0FFFFFFF >> (28 - (sdr & 0x1F)); + /* XXX: this is false for 1 TB segments */ + hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask; + } else +#endif + { + htab_mask = sdr & 0x000001FF; + hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask; + } + mask = (htab_mask << sdr_sh) | sdr_mask; +#if defined (DEBUG_MMU) + if (loglevel != 0) { + fprintf(logfile, "sdr " PADDRX " sh %d hash " PADDRX + " mask " PADDRX " " ADDRX "\n", + sdr, sdr_sh, hash, mask, page_mask); + } +#endif ctx->pg_addr[0] = get_pgaddr(sdr, sdr_sh, hash, mask); /* Secondary table address */ hash = (~hash) & vsid_mask; +#if defined (DEBUG_MMU) + if (loglevel != 0) { + fprintf(logfile, "sdr " PADDRX " sh %d hash " PADDRX + " mask " PADDRX "\n", + sdr, sdr_sh, hash, mask); + } +#endif ctx->pg_addr[1] = get_pgaddr(sdr, sdr_sh, hash, mask); #if defined(TARGET_PPC64) - if (env->mmu_model == POWERPC_MMU_64B || - env->mmu_model == POWERPC_MMU_64BRIDGE) { + if (env->mmu_model & POWERPC_MMU_64) { /* Only 5 bits of the page index are used in the AVPN */ ctx->ptem = (vsid << 12) | ((pgidx >> 4) & 0x0F80); } else @@ -730,7 +1003,7 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, ctx->ptem = (vsid << 7) | (pgidx >> 10); } /* Initialize real address with an invalid value */ - ctx->raddr = (target_ulong)-1; + ctx->raddr = (target_phys_addr_t)-1ULL; if (unlikely(env->mmu_model == POWERPC_MMU_SOFT_6xx || env->mmu_model == POWERPC_MMU_SOFT_74xx)) { /* Software TLB search */ @@ -738,30 +1011,48 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, } else { #if defined (DEBUG_MMU) if (loglevel != 0) { - fprintf(logfile, "0 sdr1=0x" PADDRX " vsid=0x%06x " - "api=0x%04x hash=0x%07x pg_addr=0x" PADDRX "\n", - sdr, (uint32_t)vsid, (uint32_t)pgidx, - (uint32_t)hash, ctx->pg_addr[0]); + fprintf(logfile, "0 sdr1=" PADDRX " vsid=" ADDRX " " + "api=" ADDRX " hash=" PADDRX + " pg_addr=" PADDRX "\n", + sdr, vsid, pgidx, hash, ctx->pg_addr[0]); } #endif /* Primary table lookup */ - ret = find_pte(env, ctx, 0, rw); + ret = find_pte(env, ctx, 0, rw, type); if (ret < 0) { /* Secondary table lookup */ #if defined (DEBUG_MMU) if (eaddr != 0xEFFFFFFF && loglevel != 0) { - fprintf(logfile, - "1 sdr1=0x" PADDRX " vsid=0x%06x api=0x%04x " - "hash=0x%05x pg_addr=0x" PADDRX "\n", - sdr, (uint32_t)vsid, (uint32_t)pgidx, - (uint32_t)hash, ctx->pg_addr[1]); + fprintf(logfile, "1 sdr1=" PADDRX " vsid=" ADDRX " " + "api=" ADDRX " hash=" PADDRX + " pg_addr=" PADDRX "\n", + sdr, vsid, pgidx, hash, ctx->pg_addr[1]); } #endif - ret2 = find_pte(env, ctx, 1, rw); + ret2 = find_pte(env, ctx, 1, rw, type); if (ret2 != -1) ret = ret2; } } +#if defined (DUMP_PAGE_TABLES) + if (loglevel != 0) { + target_phys_addr_t curaddr; + uint32_t a0, a1, a2, a3; + fprintf(logfile, "Page table: " PADDRX " len " PADDRX "\n", + sdr, mask + 0x80); + for (curaddr = sdr; curaddr < (sdr + mask + 0x80); + curaddr += 16) { + a0 = ldl_phys(curaddr); + a1 = ldl_phys(curaddr + 4); + a2 = ldl_phys(curaddr + 8); + a3 = ldl_phys(curaddr + 12); + if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { + fprintf(logfile, PADDRX ": %08x %08x %08x %08x\n", + curaddr, a0, a1, a2, a3); + } + } + } +#endif } else { #if defined (DEBUG_MMU) if (loglevel != 0) @@ -817,10 +1108,10 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, } /* Generic TLB check function for embedded PowerPC implementations */ -static int ppcemb_tlb_check (CPUState *env, ppcemb_tlb_t *tlb, - target_phys_addr_t *raddrp, - target_ulong address, - uint32_t pid, int ext, int i) +static always_inline int ppcemb_tlb_check (CPUState *env, ppcemb_tlb_t *tlb, + target_phys_addr_t *raddrp, + target_ulong address, + uint32_t pid, int ext, int i) { target_ulong mask; @@ -833,9 +1124,9 @@ static int ppcemb_tlb_check (CPUState *env, ppcemb_tlb_t *tlb, mask = ~(tlb->size - 1); #if defined (DEBUG_SOFTWARE_TLB) if (loglevel != 0) { - fprintf(logfile, "%s: TLB %d address " ADDRX " PID %d <=> " - ADDRX " " ADDRX " %d\n", - __func__, i, address, pid, tlb->EPN, mask, (int)tlb->PID); + fprintf(logfile, "%s: TLB %d address " ADDRX " PID %u <=> " ADDRX + " " ADDRX " %u\n", + __func__, i, address, pid, tlb->EPN, mask, (uint32_t)tlb->PID); } #endif /* Check PID */ @@ -876,7 +1167,7 @@ int ppcemb_tlb_search (CPUPPCState *env, target_ulong address, uint32_t pid) } /* Helpers specific to PowerPC 40x implementations */ -static void ppc4xx_tlb_invalidate_all (CPUState *env) +static always_inline void ppc4xx_tlb_invalidate_all (CPUState *env) { ppcemb_tlb_t *tlb; int i; @@ -888,8 +1179,9 @@ static void ppc4xx_tlb_invalidate_all (CPUState *env) tlb_flush(env, 1); } -static void ppc4xx_tlb_invalidate_virt (CPUState *env, target_ulong eaddr, - uint32_t pid) +static always_inline void ppc4xx_tlb_invalidate_virt (CPUState *env, + target_ulong eaddr, + uint32_t pid) { #if !defined(FLUSH_ALL_TLBS) ppcemb_tlb_t *tlb; @@ -917,10 +1209,11 @@ int mmu40x_get_physical_address (CPUState *env, mmu_ctx_t *ctx, { ppcemb_tlb_t *tlb; target_phys_addr_t raddr; - int i, ret, zsel, zpr; + int i, ret, zsel, zpr, pr; ret = -1; - raddr = -1; + raddr = (target_phys_addr_t)-1ULL; + pr = msr_pr; for (i = 0; i < env->nb_tlb; i++) { tlb = &env->tlb[i].tlbe; if (ppcemb_tlb_check(env, tlb, &raddr, address, @@ -934,82 +1227,38 @@ int mmu40x_get_physical_address (CPUState *env, mmu_ctx_t *ctx, __func__, i, zsel, zpr, rw, tlb->attr); } #endif - if (access_type == ACCESS_CODE) { - /* Check execute enable bit */ - switch (zpr) { - case 0x2: - if (msr_pr) - goto check_exec_perm; - goto exec_granted; - case 0x0: - if (msr_pr) { - ctx->prot = 0; - ret = -3; - break; - } - /* No break here */ - case 0x1: - check_exec_perm: - /* Check from TLB entry */ - if (!(tlb->prot & PAGE_EXEC)) { - ret = -3; - } else { - if (tlb->prot & PAGE_WRITE) { - ctx->prot = PAGE_READ | PAGE_WRITE; - } else { - ctx->prot = PAGE_READ; - } - ret = 0; - } - break; - case 0x3: - exec_granted: - /* All accesses granted */ - ctx->prot = PAGE_READ | PAGE_WRITE; - ret = 0; - break; - } - } else { - switch (zpr) { - case 0x2: - if (msr_pr) - goto check_rw_perm; - goto rw_granted; - case 0x0: - if (msr_pr) { - ctx->prot = 0; - ret = -2; - break; - } - /* No break here */ - case 0x1: - check_rw_perm: - /* Check from TLB entry */ - /* Check write protection bit */ - if (tlb->prot & PAGE_WRITE) { - ctx->prot = PAGE_READ | PAGE_WRITE; - ret = 0; - } else { - ctx->prot = PAGE_READ; - if (rw) - ret = -2; - else - ret = 0; - } - break; - case 0x3: - rw_granted: - /* All accesses granted */ - ctx->prot = PAGE_READ | PAGE_WRITE; - ret = 0; + /* Check execute enable bit */ + switch (zpr) { + case 0x2: + if (pr != 0) + goto check_perms; + /* No break here */ + case 0x3: + /* All accesses granted */ + ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + ret = 0; + break; + case 0x0: + if (pr != 0) { + ctx->prot = 0; + ret = -2; break; } + /* No break here */ + case 0x1: + check_perms: + /* Check from TLB entry */ + /* XXX: there is a problem here or in the TLB fill code... */ + ctx->prot = tlb->prot; + ctx->prot |= PAGE_EXEC; + ret = check_prot(ctx->prot, rw, access_type); + break; } if (ret >= 0) { ctx->raddr = raddr; #if defined (DEBUG_SOFTWARE_TLB) if (loglevel != 0) { - fprintf(logfile, "%s: access granted " ADDRX " => " REGX + fprintf(logfile, "%s: access granted " ADDRX " => " PADDRX " %d %d\n", __func__, address, ctx->raddr, ctx->prot, ret); } @@ -1019,7 +1268,7 @@ int mmu40x_get_physical_address (CPUState *env, mmu_ctx_t *ctx, } #if defined (DEBUG_SOFTWARE_TLB) if (loglevel != 0) { - fprintf(logfile, "%s: access refused " ADDRX " => " REGX + fprintf(logfile, "%s: access refused " ADDRX " => " PADDRX " %d %d\n", __func__, address, raddr, ctx->prot, ret); } @@ -1046,19 +1295,19 @@ int mmubooke_get_physical_address (CPUState *env, mmu_ctx_t *ctx, int i, prot, ret; ret = -1; - raddr = -1; + raddr = (target_phys_addr_t)-1ULL; for (i = 0; i < env->nb_tlb; i++) { tlb = &env->tlb[i].tlbe; if (ppcemb_tlb_check(env, tlb, &raddr, address, env->spr[SPR_BOOKE_PID], 1, i) < 0) continue; - if (msr_pr) + if (msr_pr != 0) prot = tlb->prot & 0xF; else prot = (tlb->prot >> 4) & 0xF; /* Check the address space */ if (access_type == ACCESS_CODE) { - if (msr_is != (tlb->attr & 1)) + if (msr_ir != (tlb->attr & 1)) continue; ctx->prot = prot; if (prot & PAGE_EXEC) { @@ -1067,7 +1316,7 @@ int mmubooke_get_physical_address (CPUState *env, mmu_ctx_t *ctx, } ret = -3; } else { - if (msr_ds != (tlb->attr & 1)) + if (msr_dr != (tlb->attr & 1)) continue; ctx->prot = prot; if ((!rw && prot & PAGE_READ) || (rw && (prot & PAGE_WRITE))) { @@ -1083,27 +1332,27 @@ int mmubooke_get_physical_address (CPUState *env, mmu_ctx_t *ctx, return ret; } -static int check_physical (CPUState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw) +static always_inline int check_physical (CPUState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw) { int in_plb, ret; ctx->raddr = eaddr; - ctx->prot = PAGE_READ; + ctx->prot = PAGE_READ | PAGE_EXEC; ret = 0; switch (env->mmu_model) { case POWERPC_MMU_32B: + case POWERPC_MMU_601: case POWERPC_MMU_SOFT_6xx: case POWERPC_MMU_SOFT_74xx: - case POWERPC_MMU_601: case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_REAL_4xx: + case POWERPC_MMU_REAL: case POWERPC_MMU_BOOKE: ctx->prot |= PAGE_WRITE; break; #if defined(TARGET_PPC64) + case POWERPC_MMU_620: case POWERPC_MMU_64B: - case POWERPC_MMU_64BRIDGE: /* Real address are 60 bits long */ ctx->raddr &= 0x0FFFFFFFFFFFFFFFULL; ctx->prot |= PAGE_WRITE; @@ -1133,6 +1382,10 @@ static int check_physical (CPUState *env, mmu_ctx_t *ctx, } } break; + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(env, "MPC8xx MMU model is not implemented\n"); + break; case POWERPC_MMU_BOOKE_FSL: /* XXX: TODO */ cpu_abort(env, "BookE FSL MMU model not implemented\n"); @@ -1146,9 +1399,10 @@ static int check_physical (CPUState *env, mmu_ctx_t *ctx, } int get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr, - int rw, int access_type, int check_BATs) + int rw, int access_type) { int ret; + #if 0 if (loglevel != 0) { fprintf(logfile, "%s\n", __func__); @@ -1162,16 +1416,16 @@ int get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr, ret = -1; switch (env->mmu_model) { case POWERPC_MMU_32B: + case POWERPC_MMU_601: case POWERPC_MMU_SOFT_6xx: case POWERPC_MMU_SOFT_74xx: - /* Try to find a BAT */ - if (check_BATs) - ret = get_bat(env, ctx, eaddr, rw, access_type); - /* No break here */ #if defined(TARGET_PPC64) + case POWERPC_MMU_620: case POWERPC_MMU_64B: - case POWERPC_MMU_64BRIDGE: #endif + /* Try to find a BAT */ + if (env->nb_BATs != 0) + ret = get_bat(env, ctx, eaddr, rw, access_type); if (ret < 0) { /* We didn't match any BAT entry or don't have BATs */ ret = get_segment(env, ctx, eaddr, rw, access_type); @@ -1182,20 +1436,20 @@ int get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr, ret = mmu40x_get_physical_address(env, ctx, eaddr, rw, access_type); break; - case POWERPC_MMU_601: - /* XXX: TODO */ - cpu_abort(env, "601 MMU model not implemented\n"); - return -1; case POWERPC_MMU_BOOKE: ret = mmubooke_get_physical_address(env, ctx, eaddr, rw, access_type); break; + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(env, "MPC8xx MMU model is not implemented\n"); + break; case POWERPC_MMU_BOOKE_FSL: /* XXX: TODO */ cpu_abort(env, "BookE FSL MMU model not implemented\n"); return -1; - case POWERPC_MMU_REAL_4xx: - cpu_abort(env, "PowerPC 401 does not do any translation\n"); + case POWERPC_MMU_REAL: + cpu_abort(env, "PowerPC in real mode do not do any translation\n"); return -1; default: cpu_abort(env, "Unknown or invalid MMU model\n"); @@ -1216,7 +1470,7 @@ target_phys_addr_t cpu_get_phys_page_debug (CPUState *env, target_ulong addr) { mmu_ctx_t ctx; - if (unlikely(get_physical_address(env, &ctx, addr, 0, ACCESS_INT, 1) != 0)) + if (unlikely(get_physical_address(env, &ctx, addr, 0, ACCESS_INT) != 0)) return -1; return ctx.raddr & TARGET_PAGE_MASK; @@ -1224,10 +1478,9 @@ target_phys_addr_t cpu_get_phys_page_debug (CPUState *env, target_ulong addr) /* Perform address translation */ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, - int is_user, int is_softmmu) + int mmu_idx, int is_softmmu) { mmu_ctx_t ctx; - int exception = 0, error_code = 0; int access_type; int ret = 0; @@ -1242,66 +1495,61 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, access_type = ACCESS_INT; // access_type = env->access_type; } - ret = get_physical_address(env, &ctx, address, rw, access_type, 1); + ret = get_physical_address(env, &ctx, address, rw, access_type); if (ret == 0) { - ret = tlb_set_page(env, address & TARGET_PAGE_MASK, - ctx.raddr & TARGET_PAGE_MASK, ctx.prot, - is_user, is_softmmu); + ret = tlb_set_page_exec(env, address & TARGET_PAGE_MASK, + ctx.raddr & TARGET_PAGE_MASK, ctx.prot, + mmu_idx, is_softmmu); } else if (ret < 0) { #if defined (DEBUG_MMU) if (loglevel != 0) cpu_dump_state(env, logfile, fprintf, 0); #endif if (access_type == ACCESS_CODE) { - exception = POWERPC_EXCP_ISI; switch (ret) { case -1: /* No matches in page tables or TLB */ switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: - exception = POWERPC_EXCP_IFTLB; + env->exception_index = POWERPC_EXCP_IFTLB; + env->error_code = 1 << 18; env->spr[SPR_IMISS] = address; env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; - error_code = 1 << 18; goto tlb_miss; case POWERPC_MMU_SOFT_74xx: - exception = POWERPC_EXCP_IFTLB; + env->exception_index = POWERPC_EXCP_IFTLB; goto tlb_miss_74xx; case POWERPC_MMU_SOFT_4xx: case POWERPC_MMU_SOFT_4xx_Z: - exception = POWERPC_EXCP_ITLB; - error_code = 0; + env->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; env->spr[SPR_40x_DEAR] = address; env->spr[SPR_40x_ESR] = 0x00000000; break; case POWERPC_MMU_32B: - error_code = 0x40000000; - break; + case POWERPC_MMU_601: #if defined(TARGET_PPC64) + case POWERPC_MMU_620: case POWERPC_MMU_64B: - /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); - return -1; - case POWERPC_MMU_64BRIDGE: - /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); - return -1; #endif - case POWERPC_MMU_601: - /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); - return -1; + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x40000000; + break; case POWERPC_MMU_BOOKE: /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); + cpu_abort(env, "BookE MMU model is not implemented\n"); return -1; case POWERPC_MMU_BOOKE_FSL: /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); + cpu_abort(env, "BookE FSL MMU model is not implemented\n"); return -1; - case POWERPC_MMU_REAL_4xx: - cpu_abort(env, "PowerPC 401 should never raise any MMU " - "exceptions\n"); + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(env, "MPC8xx MMU model is not implemented\n"); + break; + case POWERPC_MMU_REAL: + cpu_abort(env, "PowerPC in real mode should never raise " + "any MMU exceptions\n"); return -1; default: cpu_abort(env, "Unknown or invalid MMU model\n"); @@ -1310,64 +1558,71 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, break; case -2: /* Access rights violation */ - error_code = 0x08000000; + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; break; case -3: /* No execute protection violation */ - error_code = 0x10000000; + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; break; case -4: /* Direct store exception */ /* No code fetch is allowed in direct-store areas */ - error_code = 0x10000000; + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; break; #if defined(TARGET_PPC64) case -5: /* No match in segment table */ - exception = POWERPC_EXCP_ISEG; - error_code = 0; + if (env->mmu_model == POWERPC_MMU_620) { + env->exception_index = POWERPC_EXCP_ISI; + /* XXX: this might be incorrect */ + env->error_code = 0x40000000; + } else { + env->exception_index = POWERPC_EXCP_ISEG; + env->error_code = 0; + } break; #endif } } else { - exception = POWERPC_EXCP_DSI; switch (ret) { case -1: /* No matches in page tables or TLB */ switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: if (rw == 1) { - exception = POWERPC_EXCP_DSTLB; - error_code = 1 << 16; + env->exception_index = POWERPC_EXCP_DSTLB; + env->error_code = 1 << 16; } else { - exception = POWERPC_EXCP_DLTLB; - error_code = 0; + env->exception_index = POWERPC_EXCP_DLTLB; + env->error_code = 0; } env->spr[SPR_DMISS] = address; env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; tlb_miss: - error_code |= ctx.key << 19; + env->error_code |= ctx.key << 19; env->spr[SPR_HASH1] = ctx.pg_addr[0]; env->spr[SPR_HASH2] = ctx.pg_addr[1]; - /* Do not alter DAR nor DSISR */ - goto out; + break; case POWERPC_MMU_SOFT_74xx: if (rw == 1) { - exception = POWERPC_EXCP_DSTLB; + env->exception_index = POWERPC_EXCP_DSTLB; } else { - exception = POWERPC_EXCP_DLTLB; + env->exception_index = POWERPC_EXCP_DLTLB; } tlb_miss_74xx: /* Implement LRU algorithm */ + env->error_code = ctx.key << 19; env->spr[SPR_TLBMISS] = (address & ~((target_ulong)0x3)) | ((env->last_way + 1) & (env->nb_ways - 1)); env->spr[SPR_PTEHI] = 0x80000000 | ctx.ptem; - error_code = ctx.key << 19; break; case POWERPC_MMU_SOFT_4xx: case POWERPC_MMU_SOFT_4xx_Z: - exception = POWERPC_EXCP_DTLB; - error_code = 0; + env->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; env->spr[SPR_40x_DEAR] = address; if (rw) env->spr[SPR_40x_ESR] = 0x00800000; @@ -1375,33 +1630,34 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, env->spr[SPR_40x_ESR] = 0x00000000; break; case POWERPC_MMU_32B: - error_code = 0x40000000; - break; + case POWERPC_MMU_601: #if defined(TARGET_PPC64) + case POWERPC_MMU_620: case POWERPC_MMU_64B: - /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); - return -1; - case POWERPC_MMU_64BRIDGE: - /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); - return -1; #endif - case POWERPC_MMU_601: + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + if (rw == 1) + env->spr[SPR_DSISR] = 0x42000000; + else + env->spr[SPR_DSISR] = 0x40000000; + break; + case POWERPC_MMU_MPC8xx: /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); - return -1; + cpu_abort(env, "MPC8xx MMU model is not implemented\n"); + break; case POWERPC_MMU_BOOKE: /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); + cpu_abort(env, "BookE MMU model is not implemented\n"); return -1; case POWERPC_MMU_BOOKE_FSL: /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); + cpu_abort(env, "BookE FSL MMU model is not implemented\n"); return -1; - case POWERPC_MMU_REAL_4xx: - cpu_abort(env, "PowerPC 401 should never raise any MMU " - "exceptions\n"); + case POWERPC_MMU_REAL: + cpu_abort(env, "PowerPC in real mode should never raise " + "any MMU exceptions\n"); return -1; default: cpu_abort(env, "Unknown or invalid MMU model\n"); @@ -1410,52 +1666,77 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, break; case -2: /* Access rights violation */ - error_code = 0x08000000; + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + if (rw == 1) + env->spr[SPR_DSISR] = 0x0A000000; + else + env->spr[SPR_DSISR] = 0x08000000; break; case -4: /* Direct store exception */ switch (access_type) { case ACCESS_FLOAT: /* Floating point load/store */ - exception = POWERPC_EXCP_ALIGN; - error_code = POWERPC_EXCP_ALIGN_FP; + env->exception_index = POWERPC_EXCP_ALIGN; + env->error_code = POWERPC_EXCP_ALIGN_FP; + env->spr[SPR_DAR] = address; break; case ACCESS_RES: - /* lwarx, ldarx or srwcx. */ - error_code = 0x04000000; + /* lwarx, ldarx or stwcx. */ + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + if (rw == 1) + env->spr[SPR_DSISR] = 0x06000000; + else + env->spr[SPR_DSISR] = 0x04000000; break; case ACCESS_EXT: /* eciwx or ecowx */ - error_code = 0x04100000; + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + if (rw == 1) + env->spr[SPR_DSISR] = 0x06100000; + else + env->spr[SPR_DSISR] = 0x04100000; break; default: printf("DSI: invalid exception (%d)\n", ret); - exception = POWERPC_EXCP_PROGRAM; - error_code = POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; + env->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = + POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; + env->spr[SPR_DAR] = address; break; } break; #if defined(TARGET_PPC64) case -5: /* No match in segment table */ - exception = POWERPC_EXCP_DSEG; - error_code = 0; + if (env->mmu_model == POWERPC_MMU_620) { + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + /* XXX: this might be incorrect */ + if (rw == 1) + env->spr[SPR_DSISR] = 0x42000000; + else + env->spr[SPR_DSISR] = 0x40000000; + } else { + env->exception_index = POWERPC_EXCP_DSEG; + env->error_code = 0; + env->spr[SPR_DAR] = address; + } break; #endif } - if (exception == POWERPC_EXCP_DSI && rw == 1) - error_code |= 0x02000000; - /* Store fault address */ - env->spr[SPR_DAR] = address; - env->spr[SPR_DSISR] = error_code; } - out: #if 0 - printf("%s: set exception to %d %02x\n", - __func__, exception, error_code); + printf("%s: set exception to %d %02x\n", __func__, + env->exception, env->error_code); #endif - env->exception_index = exception; - env->error_code = error_code; ret = 1; } @@ -1465,8 +1746,9 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, /*****************************************************************************/ /* BATs management */ #if !defined(FLUSH_ALL_TLBS) -static inline void do_invalidate_BAT (CPUPPCState *env, - target_ulong BATu, target_ulong mask) +static always_inline void do_invalidate_BAT (CPUPPCState *env, + target_ulong BATu, + target_ulong mask) { target_ulong base, end, page; @@ -1487,12 +1769,12 @@ static inline void do_invalidate_BAT (CPUPPCState *env, } #endif -static inline void dump_store_bat (CPUPPCState *env, char ID, int ul, int nr, - target_ulong value) +static always_inline void dump_store_bat (CPUPPCState *env, char ID, + int ul, int nr, target_ulong value) { #if defined (DEBUG_BATS) if (loglevel != 0) { - fprintf(logfile, "Set %cBAT%d%c to 0x" ADDRX " (0x" ADDRX ")\n", + fprintf(logfile, "Set %cBAT%d%c to " ADDRX " (" ADDRX ")\n", ID, nr, ul == 0 ? 'u' : 'l', value, env->nip); } #endif @@ -1582,6 +1864,75 @@ void do_store_dbatl (CPUPPCState *env, int nr, target_ulong value) env->DBAT[1][nr] = value; } +void do_store_ibatu_601 (CPUPPCState *env, int nr, target_ulong value) +{ + target_ulong mask; + int do_inval; + + dump_store_bat(env, 'I', 0, nr, value); + if (env->IBAT[0][nr] != value) { + do_inval = 0; + mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL; + if (env->IBAT[1][nr] & 0x40) { + /* Invalidate BAT only if it is valid */ +#if !defined(FLUSH_ALL_TLBS) + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#else + do_inval = 1; +#endif + } + /* When storing valid upper BAT, mask BEPI and BRPN + * and invalidate all TLBs covered by this BAT + */ + env->IBAT[0][nr] = (value & 0x00001FFFUL) | + (value & ~0x0001FFFFUL & ~mask); + env->DBAT[0][nr] = env->IBAT[0][nr]; + if (env->IBAT[1][nr] & 0x40) { +#if !defined(FLUSH_ALL_TLBS) + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#else + do_inval = 1; +#endif + } +#if defined(FLUSH_ALL_TLBS) + if (do_inval) + tlb_flush(env, 1); +#endif + } +} + +void do_store_ibatl_601 (CPUPPCState *env, int nr, target_ulong value) +{ + target_ulong mask; + int do_inval; + + dump_store_bat(env, 'I', 1, nr, value); + if (env->IBAT[1][nr] != value) { + do_inval = 0; + if (env->IBAT[1][nr] & 0x40) { +#if !defined(FLUSH_ALL_TLBS) + mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL; + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#else + do_inval = 1; +#endif + } + if (value & 0x40) { +#if !defined(FLUSH_ALL_TLBS) + mask = (value << 17) & 0x0FFE0000UL; + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#else + do_inval = 1; +#endif + } + env->IBAT[1][nr] = value; + env->DBAT[1][nr] = value; +#if defined(FLUSH_ALL_TLBS) + if (do_inval) + tlb_flush(env, 1); +#endif + } +} /*****************************************************************************/ /* TLB management */ @@ -1596,26 +1947,33 @@ void ppc_tlb_invalidate_all (CPUPPCState *env) case POWERPC_MMU_SOFT_4xx_Z: ppc4xx_tlb_invalidate_all(env); break; - case POWERPC_MMU_REAL_4xx: + case POWERPC_MMU_REAL: cpu_abort(env, "No TLB for PowerPC 4xx in real mode\n"); break; - case POWERPC_MMU_BOOKE: + case POWERPC_MMU_MPC8xx: /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); + cpu_abort(env, "MPC8xx MMU model is not implemented\n"); break; - case POWERPC_MMU_BOOKE_FSL: + case POWERPC_MMU_BOOKE: /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); + cpu_abort(env, "BookE MMU model is not implemented\n"); break; - case POWERPC_MMU_601: + case POWERPC_MMU_BOOKE_FSL: /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); + cpu_abort(env, "BookE MMU model is not implemented\n"); break; case POWERPC_MMU_32B: + case POWERPC_MMU_601: +#if defined(TARGET_PPC64) + case POWERPC_MMU_620: case POWERPC_MMU_64B: - case POWERPC_MMU_64BRIDGE: +#endif /* defined(TARGET_PPC64) */ tlb_flush(env, 1); break; + default: + /* XXX: TODO */ + cpu_abort(env, "Unknown MMU model\n"); + break; } } @@ -1634,24 +1992,25 @@ void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr) case POWERPC_MMU_SOFT_4xx_Z: ppc4xx_tlb_invalidate_virt(env, addr, env->spr[SPR_40x_PID]); break; - case POWERPC_MMU_REAL_4xx: + case POWERPC_MMU_REAL: cpu_abort(env, "No TLB for PowerPC 4xx in real mode\n"); break; - case POWERPC_MMU_BOOKE: + case POWERPC_MMU_MPC8xx: /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); + cpu_abort(env, "MPC8xx MMU model is not implemented\n"); break; - case POWERPC_MMU_BOOKE_FSL: + case POWERPC_MMU_BOOKE: /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); + cpu_abort(env, "BookE MMU model is not implemented\n"); break; - case POWERPC_MMU_601: + case POWERPC_MMU_BOOKE_FSL: /* XXX: TODO */ - cpu_abort(env, "MMU model not implemented\n"); + cpu_abort(env, "BookE FSL MMU model is not implemented\n"); break; case POWERPC_MMU_32B: + case POWERPC_MMU_601: /* tlbie invalidate TLBs for all segments */ - addr &= ~((target_ulong)-1 << 28); + addr &= ~((target_ulong)-1ULL << 28); /* XXX: this case should be optimized, * giving a mask to tlb_flush_page */ @@ -1672,35 +2031,27 @@ void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr) tlb_flush_page(env, addr | (0xE << 28)); tlb_flush_page(env, addr | (0xF << 28)); break; +#if defined(TARGET_PPC64) + case POWERPC_MMU_620: case POWERPC_MMU_64B: - case POWERPC_MMU_64BRIDGE: /* tlbie invalidate TLBs for all segments */ /* XXX: given the fact that there are too many segments to invalidate, + * and we still don't have a tlb_flush_mask(env, n, mask) in Qemu, * we just invalidate all TLBs */ tlb_flush(env, 1); break; +#endif /* defined(TARGET_PPC64) */ + default: + /* XXX: TODO */ + cpu_abort(env, "Unknown MMU model\n"); + break; } #else ppc_tlb_invalidate_all(env); #endif } -#if defined(TARGET_PPC64) -void ppc_slb_invalidate_all (CPUPPCState *env) -{ - /* XXX: TODO */ - tlb_flush(env, 1); -} - -void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0) -{ - /* XXX: TODO */ - tlb_flush(env, 1); -} -#endif - - /*****************************************************************************/ /* Special registers manipulation */ #if defined(TARGET_PPC64) @@ -1727,25 +2078,30 @@ void do_store_sdr1 (CPUPPCState *env, target_ulong value) { #if defined (DEBUG_MMU) if (loglevel != 0) { - fprintf(logfile, "%s: 0x" ADDRX "\n", __func__, value); + fprintf(logfile, "%s: " ADDRX "\n", __func__, value); } #endif if (env->sdr1 != value) { + /* XXX: for PowerPC 64, should check that the HTABSIZE value + * is <= 28 + */ env->sdr1 = value; tlb_flush(env, 1); } } +#if 0 // Unused target_ulong do_load_sr (CPUPPCState *env, int srnum) { return env->sr[srnum]; } +#endif void do_store_sr (CPUPPCState *env, int srnum, target_ulong value) { #if defined (DEBUG_MMU) if (loglevel != 0) { - fprintf(logfile, "%s: reg=%d 0x" ADDRX " " ADDRX "\n", + fprintf(logfile, "%s: reg=%d " ADDRX " " ADDRX "\n", __func__, srnum, value, env->sr[srnum]); } #endif @@ -1769,188 +2125,18 @@ void do_store_sr (CPUPPCState *env, int srnum, target_ulong value) target_ulong ppc_load_xer (CPUPPCState *env) { - return (xer_so << XER_SO) | - (xer_ov << XER_OV) | - (xer_ca << XER_CA) | - (xer_bc << XER_BC) | - (xer_cmp << XER_CMP); + return hreg_load_xer(env); } void ppc_store_xer (CPUPPCState *env, target_ulong value) { - xer_so = (value >> XER_SO) & 0x01; - xer_ov = (value >> XER_OV) & 0x01; - xer_ca = (value >> XER_CA) & 0x01; - xer_cmp = (value >> XER_CMP) & 0xFF; - xer_bc = (value >> XER_BC) & 0x7F; -} - -/* Swap temporary saved registers with GPRs */ -static inline void swap_gpr_tgpr (CPUPPCState *env) -{ - ppc_gpr_t tmp; - - tmp = env->gpr[0]; - env->gpr[0] = env->tgpr[0]; - env->tgpr[0] = tmp; - tmp = env->gpr[1]; - env->gpr[1] = env->tgpr[1]; - env->tgpr[1] = tmp; - tmp = env->gpr[2]; - env->gpr[2] = env->tgpr[2]; - env->tgpr[2] = tmp; - tmp = env->gpr[3]; - env->gpr[3] = env->tgpr[3]; - env->tgpr[3] = tmp; + hreg_store_xer(env, value); } /* GDBstub can read and write MSR... */ -target_ulong do_load_msr (CPUPPCState *env) -{ - return -#if defined (TARGET_PPC64) - ((target_ulong)msr_sf << MSR_SF) | - ((target_ulong)msr_isf << MSR_ISF) | - ((target_ulong)msr_hv << MSR_HV) | -#endif - ((target_ulong)msr_ucle << MSR_UCLE) | - ((target_ulong)msr_vr << MSR_VR) | /* VR / SPE */ - ((target_ulong)msr_ap << MSR_AP) | - ((target_ulong)msr_sa << MSR_SA) | - ((target_ulong)msr_key << MSR_KEY) | - ((target_ulong)msr_pow << MSR_POW) | /* POW / WE */ - ((target_ulong)msr_tlb << MSR_TLB) | /* TLB / TGPE / CE */ - ((target_ulong)msr_ile << MSR_ILE) | - ((target_ulong)msr_ee << MSR_EE) | - ((target_ulong)msr_pr << MSR_PR) | - ((target_ulong)msr_fp << MSR_FP) | - ((target_ulong)msr_me << MSR_ME) | - ((target_ulong)msr_fe0 << MSR_FE0) | - ((target_ulong)msr_se << MSR_SE) | /* SE / DWE / UBLE */ - ((target_ulong)msr_be << MSR_BE) | /* BE / DE */ - ((target_ulong)msr_fe1 << MSR_FE1) | - ((target_ulong)msr_al << MSR_AL) | - ((target_ulong)msr_ip << MSR_IP) | - ((target_ulong)msr_ir << MSR_IR) | /* IR / IS */ - ((target_ulong)msr_dr << MSR_DR) | /* DR / DS */ - ((target_ulong)msr_pe << MSR_PE) | /* PE / EP */ - ((target_ulong)msr_px << MSR_PX) | /* PX / PMM */ - ((target_ulong)msr_ri << MSR_RI) | - ((target_ulong)msr_le << MSR_LE); -} - -void do_store_msr (CPUPPCState *env, target_ulong value) -{ - int enter_pm; - - value &= env->msr_mask; - if (((value >> MSR_IR) & 1) != msr_ir || - ((value >> MSR_DR) & 1) != msr_dr) { - /* Flush all tlb when changing translation mode */ - tlb_flush(env, 1); - env->interrupt_request |= CPU_INTERRUPT_EXITTB; - } -#if 0 - if (loglevel != 0) { - fprintf(logfile, "%s: T0 %08lx\n", __func__, value); - } -#endif - switch (env->excp_model) { - case POWERPC_EXCP_602: - case POWERPC_EXCP_603: - case POWERPC_EXCP_603E: - case POWERPC_EXCP_G2: - if (((value >> MSR_TGPR) & 1) != msr_tgpr) { - /* Swap temporary saved registers with GPRs */ - swap_gpr_tgpr(env); - } - break; - default: - break; - } -#if defined (TARGET_PPC64) - msr_sf = (value >> MSR_SF) & 1; - msr_isf = (value >> MSR_ISF) & 1; - msr_hv = (value >> MSR_HV) & 1; -#endif - msr_ucle = (value >> MSR_UCLE) & 1; - msr_vr = (value >> MSR_VR) & 1; /* VR / SPE */ - msr_ap = (value >> MSR_AP) & 1; - msr_sa = (value >> MSR_SA) & 1; - msr_key = (value >> MSR_KEY) & 1; - msr_pow = (value >> MSR_POW) & 1; /* POW / WE */ - msr_tlb = (value >> MSR_TLB) & 1; /* TLB / TGPR / CE */ - msr_ile = (value >> MSR_ILE) & 1; - msr_ee = (value >> MSR_EE) & 1; - msr_pr = (value >> MSR_PR) & 1; - msr_fp = (value >> MSR_FP) & 1; - msr_me = (value >> MSR_ME) & 1; - msr_fe0 = (value >> MSR_FE0) & 1; - msr_se = (value >> MSR_SE) & 1; /* SE / DWE / UBLE */ - msr_be = (value >> MSR_BE) & 1; /* BE / DE */ - msr_fe1 = (value >> MSR_FE1) & 1; - msr_al = (value >> MSR_AL) & 1; - msr_ip = (value >> MSR_IP) & 1; - msr_ir = (value >> MSR_IR) & 1; /* IR / IS */ - msr_dr = (value >> MSR_DR) & 1; /* DR / DS */ - msr_pe = (value >> MSR_PE) & 1; /* PE / EP */ - msr_px = (value >> MSR_PX) & 1; /* PX / PMM */ - msr_ri = (value >> MSR_RI) & 1; - msr_le = (value >> MSR_LE) & 1; - do_compute_hflags(env); - - enter_pm = 0; - switch (env->excp_model) { - case POWERPC_EXCP_603: - case POWERPC_EXCP_603E: - case POWERPC_EXCP_G2: - /* Don't handle SLEEP mode: we should disable all clocks... - * No dynamic power-management. - */ - if (msr_pow == 1 && (env->spr[SPR_HID0] & 0x00C00000) != 0) - enter_pm = 1; - break; - case POWERPC_EXCP_604: - if (msr_pow == 1) - enter_pm = 1; - break; - case POWERPC_EXCP_7x0: - if (msr_pow == 1 && (env->spr[SPR_HID0] & 0x00E00000) != 0) - enter_pm = 1; - break; - default: - break; - } - if (enter_pm) { - if (likely(!env->halted)) { - /* power save: exit cpu loop */ - env->halted = 1; - env->exception_index = EXCP_HLT; - cpu_loop_exit(); - } - } -} - -#if defined(TARGET_PPC64) -void ppc_store_msr_32 (CPUPPCState *env, uint32_t value) +void ppc_store_msr (CPUPPCState *env, target_ulong value) { - do_store_msr(env, - (do_load_msr(env) & ~0xFFFFFFFFULL) | (value & 0xFFFFFFFF)); -} -#endif - -void do_compute_hflags (CPUPPCState *env) -{ - /* Compute current hflags */ - env->hflags = (msr_vr << MSR_VR) | - (msr_ap << MSR_AP) | (msr_sa << MSR_SA) | (msr_pr << MSR_PR) | - (msr_fp << MSR_FP) | (msr_fe0 << MSR_FE0) | (msr_se << MSR_SE) | - (msr_be << MSR_BE) | (msr_fe1 << MSR_FE1) | (msr_le << MSR_LE); -#if defined (TARGET_PPC64) - env->hflags |= msr_cm << MSR_CM; - env->hflags |= (uint64_t)msr_sf << MSR_SF; - env->hflags |= (uint64_t)msr_hv << MSR_HV; -#endif + hreg_store_msr(env, value, 0); } /*****************************************************************************/ @@ -1968,12 +2154,12 @@ void ppc_hw_interrupt (CPUState *env) env->error_code = 0; } #else /* defined (CONFIG_USER_ONLY) */ -static void dump_syscall (CPUState *env) +static always_inline void dump_syscall (CPUState *env) { - fprintf(logfile, "syscall r0=0x" REGX " r3=0x" REGX " r4=0x" REGX - " r5=0x" REGX " r6=0x" REGX " nip=0x" ADDRX "\n", - env->gpr[0], env->gpr[3], env->gpr[4], - env->gpr[5], env->gpr[6], env->nip); + fprintf(logfile, "syscall r0=" REGX " r3=" REGX " r4=" REGX + " r5=" REGX " r6=" REGX " nip=" ADDRX "\n", + ppc_dump_gpr(env, 0), ppc_dump_gpr(env, 3), ppc_dump_gpr(env, 4), + ppc_dump_gpr(env, 5), ppc_dump_gpr(env, 6), env->nip); } /* Note that this function should be greatly optimized @@ -1982,14 +2168,26 @@ static void dump_syscall (CPUState *env) static always_inline void powerpc_excp (CPUState *env, int excp_model, int excp) { - target_ulong msr, vector; + target_ulong msr, new_msr, vector; int srr0, srr1, asrr0, asrr1; + int lpes0, lpes1, lev; + + if (0) { + /* XXX: find a suitable condition to enable the hypervisor mode */ + lpes0 = (env->spr[SPR_LPCR] >> 1) & 1; + lpes1 = (env->spr[SPR_LPCR] >> 2) & 1; + } else { + /* Those values ensure we won't enter the hypervisor mode */ + lpes0 = 0; + lpes1 = 1; + } if (loglevel & CPU_LOG_INT) { - fprintf(logfile, "Raise exception at 0x" ADDRX " => 0x%08x (%02x)\n", + fprintf(logfile, "Raise exception at " ADDRX " => %08x (%02x)\n", env->nip, excp, env->error_code); } - msr = do_load_msr(env); + msr = env->msr; + new_msr = msr; srr0 = SPR_SRR0; srr1 = SPR_SRR1; asrr0 = -1; @@ -2000,7 +2198,7 @@ static always_inline void powerpc_excp (CPUState *env, /* Should never happen */ return; case POWERPC_EXCP_CRITICAL: /* Critical input */ - msr_ri = 0; /* XXX: check this */ + new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ switch (excp_model) { case POWERPC_EXCP_40x: srr0 = SPR_40x_SRR2; @@ -2018,16 +2216,25 @@ static always_inline void powerpc_excp (CPUState *env, goto store_next; case POWERPC_EXCP_MCHECK: /* Machine check exception */ if (msr_me == 0) { - /* Machine check exception is not enabled */ - /* XXX: we may just stop the processor here, to allow debugging */ - excp = POWERPC_EXCP_RESET; - goto excp_reset; + /* Machine check exception is not enabled. + * Enter checkstop state. + */ + if (loglevel != 0) { + fprintf(logfile, "Machine check while not allowed. " + "Entering checkstop state\n"); + } else { + fprintf(stderr, "Machine check while not allowed. " + "Entering checkstop state\n"); + } + env->halted = 1; + env->interrupt_request |= CPU_INTERRUPT_EXITTB; + } + new_msr &= ~((target_ulong)1 << MSR_RI); + new_msr &= ~((target_ulong)1 << MSR_ME); + if (0) { + /* XXX: find a suitable condition to enable the hypervisor mode */ + new_msr |= (target_ulong)MSR_HVB; } - msr_ri = 0; - msr_me = 0; -#if defined(TARGET_PPC64H) - msr_hv = 1; -#endif /* XXX: should also have something loaded in DAR / DSISR */ switch (excp_model) { case POWERPC_EXCP_40x: @@ -2047,43 +2254,35 @@ static always_inline void powerpc_excp (CPUState *env, case POWERPC_EXCP_DSI: /* Data storage exception */ #if defined (DEBUG_EXCEPTIONS) if (loglevel != 0) { - fprintf(logfile, "DSI exception: DSISR=0x" ADDRX" DAR=0x" ADDRX - "\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]); + fprintf(logfile, "DSI exception: DSISR=" ADDRX" DAR=" ADDRX "\n", + env->spr[SPR_DSISR], env->spr[SPR_DAR]); } #endif - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) - msr_hv = 1; -#endif + new_msr |= (target_ulong)MSR_HVB; goto store_next; case POWERPC_EXCP_ISI: /* Instruction storage exception */ #if defined (DEBUG_EXCEPTIONS) if (loglevel != 0) { - fprintf(logfile, "ISI exception: msr=0x" ADDRX ", nip=0x" ADDRX - "\n", msr, env->nip); + fprintf(logfile, "ISI exception: msr=" ADDRX ", nip=" ADDRX "\n", + msr, env->nip); } #endif - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) - msr_hv = 1; -#endif + new_msr |= (target_ulong)MSR_HVB; msr |= env->error_code; goto store_next; case POWERPC_EXCP_EXTERNAL: /* External input */ - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes0 == 1) - msr_hv = 1; -#endif + new_msr |= (target_ulong)MSR_HVB; goto store_next; case POWERPC_EXCP_ALIGN: /* Alignment exception */ - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) - msr_hv = 1; -#endif + new_msr |= (target_ulong)MSR_HVB; /* XXX: this is false */ /* Get rS/rD and rA from faulting opcode */ env->spr[SPR_DSISR] |= (ldl_code((env->nip - 4)) & 0x03FF0000) >> 16; @@ -2097,53 +2296,40 @@ static always_inline void powerpc_excp (CPUState *env, fprintf(logfile, "Ignore floating point exception\n"); } #endif + env->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; return; } - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) - msr_hv = 1; -#endif + new_msr |= (target_ulong)MSR_HVB; msr |= 0x00100000; - /* Set FX */ - env->fpscr[7] |= 0x8; - /* Finally, update FEX */ - if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) & - ((env->fpscr[1] << 1) | (env->fpscr[0] >> 3))) - env->fpscr[7] |= 0x4; - if (msr_fe0 != msr_fe1) { - msr |= 0x00010000; - goto store_current; - } + if (msr_fe0 == msr_fe1) + goto store_next; + msr |= 0x00010000; break; case POWERPC_EXCP_INVAL: #if defined (DEBUG_EXCEPTIONS) if (loglevel != 0) { - fprintf(logfile, "Invalid instruction at 0x" ADDRX "\n", + fprintf(logfile, "Invalid instruction at " ADDRX "\n", env->nip); } #endif - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) - msr_hv = 1; -#endif + new_msr |= (target_ulong)MSR_HVB; msr |= 0x00080000; break; case POWERPC_EXCP_PRIV: - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) - msr_hv = 1; -#endif + new_msr |= (target_ulong)MSR_HVB; msr |= 0x00040000; break; case POWERPC_EXCP_TRAP: - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) - msr_hv = 1; -#endif + new_msr |= (target_ulong)MSR_HVB; msr |= 0x00020000; break; default: @@ -2152,13 +2338,11 @@ static always_inline void powerpc_excp (CPUState *env, env->error_code); break; } - goto store_next; + goto store_current; case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) - msr_hv = 1; -#endif + new_msr |= (target_ulong)MSR_HVB; goto store_current; case POWERPC_EXCP_SYSCALL: /* System call exception */ /* NOTE: this is a temporary hack to support graphics OSI @@ -2166,27 +2350,27 @@ static always_inline void powerpc_excp (CPUState *env, /* XXX: To be removed */ if (env->gpr[3] == 0x113724fa && env->gpr[4] == 0x77810f9b && env->osi_call) { - if (env->osi_call(env) != 0) + if (env->osi_call(env) != 0) { + env->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; return; + } } if (loglevel & CPU_LOG_INT) { dump_syscall(env); } - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); + lev = env->error_code; if (lev == 1 || (lpes0 == 0 && lpes1 == 0)) - msr_hv = 1; -#endif + new_msr |= (target_ulong)MSR_HVB; goto store_next; case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ - msr_ri = 0; + new_msr &= ~((target_ulong)1 << MSR_RI); goto store_current; case POWERPC_EXCP_DECR: /* Decrementer exception */ - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) - msr_hv = 1; -#endif + new_msr |= (target_ulong)MSR_HVB; goto store_next; case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ /* FIT on 4xx */ @@ -2194,7 +2378,7 @@ static always_inline void powerpc_excp (CPUState *env, if (loglevel != 0) fprintf(logfile, "FIT exception\n"); #endif - msr_ri = 0; /* XXX: check this */ + new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ goto store_next; case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ #if defined (DEBUG_EXCEPTIONS) @@ -2209,13 +2393,13 @@ static always_inline void powerpc_excp (CPUState *env, default: break; } - msr_ri = 0; /* XXX: check this */ + new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ goto store_next; case POWERPC_EXCP_DTLB: /* Data TLB error */ - msr_ri = 0; /* XXX: check this */ + new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ goto store_next; case POWERPC_EXCP_ITLB: /* Instruction TLB error */ - msr_ri = 0; /* XXX: check this */ + new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ goto store_next; case POWERPC_EXCP_DEBUG: /* Debug interrupt */ switch (excp_model) { @@ -2231,9 +2415,8 @@ static always_inline void powerpc_excp (CPUState *env, /* XXX: TODO */ cpu_abort(env, "Debug exception is not implemented yet !\n"); goto store_next; -#if defined(TARGET_PPCEMB) case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */ - msr_ri = 0; /* XXX: check this */ + new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ goto store_current; case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ /* XXX: TODO */ @@ -2246,7 +2429,7 @@ static always_inline void powerpc_excp (CPUState *env, "is not implemented yet !\n"); goto store_next; case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ - msr_ri = 0; + new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: TODO */ cpu_abort(env, "Performance counter exception is not implemented yet !\n"); @@ -2269,87 +2452,64 @@ static always_inline void powerpc_excp (CPUState *env, cpu_abort(env, "Embedded doorbell critical interrupt " "is not implemented yet !\n"); goto store_next; -#endif /* defined(TARGET_PPCEMB) */ case POWERPC_EXCP_RESET: /* System reset exception */ - msr_ri = 0; -#if defined(TARGET_PPC64H) - msr_hv = 1; -#endif - excp_reset: + new_msr &= ~((target_ulong)1 << MSR_RI); + if (0) { + /* XXX: find a suitable condition to enable the hypervisor mode */ + new_msr |= (target_ulong)MSR_HVB; + } goto store_next; -#if defined(TARGET_PPC64) case POWERPC_EXCP_DSEG: /* Data segment exception */ - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) - msr_hv = 1; -#endif - /* XXX: TODO */ - cpu_abort(env, "Data segment exception is not implemented yet !\n"); + new_msr |= (target_ulong)MSR_HVB; goto store_next; case POWERPC_EXCP_ISEG: /* Instruction segment exception */ - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) - msr_hv = 1; -#endif - /* XXX: TODO */ - cpu_abort(env, - "Instruction segment exception is not implemented yet !\n"); + new_msr |= (target_ulong)MSR_HVB; goto store_next; -#endif /* defined(TARGET_PPC64) */ -#if defined(TARGET_PPC64H) case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ srr0 = SPR_HSRR0; - srr1 = SPR_HSSR1; - msr_hv = 1; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; goto store_next; -#endif case POWERPC_EXCP_TRACE: /* Trace exception */ - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) - msr_hv = 1; -#endif + new_msr |= (target_ulong)MSR_HVB; goto store_next; -#if defined(TARGET_PPC64H) case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ srr0 = SPR_HSRR0; - srr1 = SPR_HSSR1; - msr_hv = 1; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; goto store_next; case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ srr0 = SPR_HSRR0; - srr1 = SPR_HSSR1; - msr_hv = 1; - /* XXX: TODO */ - cpu_abort(env, "Hypervisor instruction storage exception " - "is not implemented yet !\n"); + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; goto store_next; case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ srr0 = SPR_HSRR0; - srr1 = SPR_HSSR1; - msr_hv = 1; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; goto store_next; case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ srr0 = SPR_HSRR0; - srr1 = SPR_HSSR1; - msr_hv = 1; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; goto store_next; -#endif /* defined(TARGET_PPC64H) */ case POWERPC_EXCP_VPU: /* Vector unavailable exception */ - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) - msr_hv = 1; -#endif + new_msr |= (target_ulong)MSR_HVB; goto store_current; case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ #if defined (DEBUG_EXCEPTIONS) if (loglevel != 0) fprintf(logfile, "PIT exception\n"); #endif - msr_ri = 0; /* XXX: check this */ + new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ goto store_next; case POWERPC_EXCP_IO: /* IO error exception */ /* XXX: TODO */ @@ -2365,11 +2525,9 @@ static always_inline void powerpc_excp (CPUState *env, "is not implemented yet !\n"); goto store_next; case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ - msr_ri = 0; /* XXX: check this */ -#if defined(TARGET_PPC64H) /* XXX: check this */ - if (lpes1 == 0) - msr_hv = 1; -#endif + new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ + if (lpes1 == 0) /* XXX: check this */ + new_msr |= (target_ulong)MSR_HVB; switch (excp_model) { case POWERPC_EXCP_602: case POWERPC_EXCP_603: @@ -2386,11 +2544,9 @@ static always_inline void powerpc_excp (CPUState *env, } break; case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ - msr_ri = 0; /* XXX: check this */ -#if defined(TARGET_PPC64H) /* XXX: check this */ - if (lpes1 == 0) - msr_hv = 1; -#endif + new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ + if (lpes1 == 0) /* XXX: check this */ + new_msr |= (target_ulong)MSR_HVB; switch (excp_model) { case POWERPC_EXCP_602: case POWERPC_EXCP_603: @@ -2407,11 +2563,9 @@ static always_inline void powerpc_excp (CPUState *env, } break; case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ - msr_ri = 0; /* XXX: check this */ -#if defined(TARGET_PPC64H) /* XXX: check this */ - if (lpes1 == 0) - msr_hv = 1; -#endif + new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ + if (lpes1 == 0) /* XXX: check this */ + new_msr |= (target_ulong)MSR_HVB; switch (excp_model) { case POWERPC_EXCP_602: case POWERPC_EXCP_603: @@ -2419,8 +2573,10 @@ static always_inline void powerpc_excp (CPUState *env, case POWERPC_EXCP_G2: tlb_miss_tgpr: /* Swap temporary saved registers with GPRs */ - swap_gpr_tgpr(env); - msr_tgpr = 1; + if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { + new_msr |= (target_ulong)1 << MSR_TGPR; + hreg_swap_gpr_tgpr(env); + } goto tlb_miss; case POWERPC_EXCP_7x5: tlb_miss: @@ -2465,8 +2621,8 @@ static always_inline void powerpc_excp (CPUState *env, if (excp == POWERPC_EXCP_IFTLB) { es = "I"; en = 'I'; - miss = &env->spr[SPR_IMISS]; - cmp = &env->spr[SPR_ICMP]; + miss = &env->spr[SPR_TLBMISS]; + cmp = &env->spr[SPR_PTEHI]; } else { if (excp == POWERPC_EXCP_DLTLB) es = "DL"; @@ -2493,6 +2649,10 @@ static always_inline void powerpc_excp (CPUState *env, cpu_abort(env, "Floating point assist exception " "is not implemented yet !\n"); goto store_next; + case POWERPC_EXCP_DABR: /* Data address breakpoint */ + /* XXX: TODO */ + cpu_abort(env, "DABR exception is not implemented yet !\n"); + goto store_next; case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ /* XXX: TODO */ cpu_abort(env, "IABR exception is not implemented yet !\n"); @@ -2507,11 +2667,9 @@ static always_inline void powerpc_excp (CPUState *env, "is not implemented yet !\n"); goto store_next; case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ - msr_ri = 0; -#if defined(TARGET_PPC64H) + new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) - msr_hv = 1; -#endif + new_msr |= (target_ulong)MSR_HVB; /* XXX: TODO */ cpu_abort(env, "Performance counter exception is not implemented yet !\n"); @@ -2530,6 +2688,16 @@ static always_inline void powerpc_excp (CPUState *env, cpu_abort(env, "970 maintenance exception is not implemented yet !\n"); goto store_next; + case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ + /* XXX: TODO */ + cpu_abort(env, "Maskable external exception " + "is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ + /* XXX: TODO */ + cpu_abort(env, "Non maskable external exception " + "is not implemented yet !\n"); + goto store_next; default: excp_invalid: cpu_abort(env, "Invalid PowerPC exception %d. Aborting\n", excp); @@ -2551,41 +2719,55 @@ static always_inline void powerpc_excp (CPUState *env, if (asrr1 != -1) env->spr[asrr1] = env->spr[srr1]; /* If we disactivated any translation, flush TLBs */ - if (msr_ir || msr_dr) + if (new_msr & ((1 << MSR_IR) | (1 << MSR_DR))) tlb_flush(env, 1); /* reload MSR with correct bits */ - msr_ee = 0; - msr_pr = 0; - msr_fp = 0; - msr_fe0 = 0; - msr_se = 0; - msr_be = 0; - msr_fe1 = 0; - msr_ir = 0; - msr_dr = 0; + new_msr &= ~((target_ulong)1 << MSR_EE); + new_msr &= ~((target_ulong)1 << MSR_PR); + new_msr &= ~((target_ulong)1 << MSR_FP); + new_msr &= ~((target_ulong)1 << MSR_FE0); + new_msr &= ~((target_ulong)1 << MSR_SE); + new_msr &= ~((target_ulong)1 << MSR_BE); + new_msr &= ~((target_ulong)1 << MSR_FE1); + new_msr &= ~((target_ulong)1 << MSR_IR); + new_msr &= ~((target_ulong)1 << MSR_DR); #if 0 /* Fix this: not on all targets */ - msr_pmm = 0; + new_msr &= ~((target_ulong)1 << MSR_PMM); #endif - msr_le = msr_ile; - do_compute_hflags(env); + new_msr &= ~((target_ulong)1 << MSR_LE); + if (msr_ile) + new_msr |= (target_ulong)1 << MSR_LE; + else + new_msr &= ~((target_ulong)1 << MSR_LE); /* Jump to handler */ vector = env->excp_vectors[excp]; - if (vector == (target_ulong)-1) { + if (vector == (target_ulong)-1ULL) { cpu_abort(env, "Raised an exception without defined vector %d\n", excp); } vector |= env->excp_prefix; #if defined(TARGET_PPC64) if (excp_model == POWERPC_EXCP_BOOKE) { - msr_cm = msr_icm; - if (!msr_cm) + if (!msr_icm) { + new_msr &= ~((target_ulong)1 << MSR_CM); vector = (uint32_t)vector; + } else { + new_msr |= (target_ulong)1 << MSR_CM; + } } else { - msr_sf = msr_isf; - if (!msr_sf) + if (!msr_isf) { + new_msr &= ~((target_ulong)1 << MSR_SF); vector = (uint32_t)vector; + } else { + new_msr |= (target_ulong)1 << MSR_SF; + } } #endif + /* XXX: we don't use hreg_store_msr here as already have treated + * any special case that could occur. Just store MSR and update hflags + */ + env->msr = new_msr & env->msr_mask; + hreg_compute_hflags(env); env->nip = vector; /* Reset exception state */ env->exception_index = POWERPC_EXCP_NONE; @@ -2599,11 +2781,13 @@ void do_interrupt (CPUState *env) void ppc_hw_interrupt (CPUPPCState *env) { -#if 1 + int hdice; + +#if 0 if (loglevel & CPU_LOG_INT) { fprintf(logfile, "%s: %p pending %08x req %08x me %d ee %d\n", __func__, env, env->pending_interrupts, - env->interrupt_request, msr_me, msr_ee); + env->interrupt_request, (int)msr_me, (int)msr_ee); } #endif /* External reset */ @@ -2626,8 +2810,13 @@ void ppc_hw_interrupt (CPUPPCState *env) return; } #endif -#if defined(TARGET_PPC64H) - if ((msr_ee != 0 || msr_hv == 0 || msr_pr == 1) & hdice != 0) { + if (0) { + /* XXX: find a suitable condition to enable the hypervisor mode */ + hdice = env->spr[SPR_LPCR] & 1; + } else { + hdice = 0; + } + if ((msr_ee != 0 || msr_hv == 0 || msr_pr != 0) && hdice != 0) { /* Hypervisor decrementer exception */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) { env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); @@ -2635,7 +2824,6 @@ void ppc_hw_interrupt (CPUPPCState *env) return; } } -#endif if (msr_ce != 0) { /* External critical interrupt */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) { @@ -2656,26 +2844,11 @@ void ppc_hw_interrupt (CPUPPCState *env) powerpc_excp(env, env->excp_model, POWERPC_EXCP_WDT); return; } -#if defined(TARGET_PPCEMB) if (env->pending_interrupts & (1 << PPC_INTERRUPT_CDOORBELL)) { env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CDOORBELL); powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORCI); return; } -#endif -#if defined(TARGET_PPCEMB) - /* External interrupt */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { - /* Taking an external interrupt does not clear the external - * interrupt status - */ -#if 0 - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EXT); -#endif - powerpc_excp(env, env->excp_model, POWERPC_EXCP_EXTERNAL); - return; - } -#endif /* Fixed interval timer on embedded PowerPC */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) { env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT); @@ -2694,7 +2867,6 @@ void ppc_hw_interrupt (CPUPPCState *env) powerpc_excp(env, env->excp_model, POWERPC_EXCP_DECR); return; } -#if !defined(TARGET_PPCEMB) /* External interrupt */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { /* Taking an external interrupt does not clear the external @@ -2706,14 +2878,11 @@ void ppc_hw_interrupt (CPUPPCState *env) powerpc_excp(env, env->excp_model, POWERPC_EXCP_EXTERNAL); return; } -#endif -#if defined(TARGET_PPCEMB) if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) { env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORI); return; } -#endif if (env->pending_interrupts & (1 << PPC_INTERRUPT_PERFM)) { env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PERFM); powerpc_excp(env, env->excp_model, POWERPC_EXCP_PERFM); @@ -2759,34 +2928,33 @@ void cpu_dump_rfi (target_ulong RA, target_ulong msr) void cpu_ppc_reset (void *opaque) { CPUPPCState *env; - int i; + target_ulong msr; env = opaque; - /* XXX: some of those flags initialisation values could depend - * on the actual PowerPC implementation - */ - for (i = 0; i < 63; i++) - env->msr[i] = 0; -#if defined(TARGET_PPC64) - msr_hv = 0; /* Should be 1... */ -#endif - msr_ap = 0; /* TO BE CHECKED */ - msr_sa = 0; /* TO BE CHECKED */ - msr_ip = 0; /* TO BE CHECKED */ + msr = (target_ulong)0; + if (0) { + /* XXX: find a suitable condition to enable the hypervisor mode */ + msr |= (target_ulong)MSR_HVB; + } + msr |= (target_ulong)0 << MSR_AP; /* TO BE CHECKED */ + msr |= (target_ulong)0 << MSR_SA; /* TO BE CHECKED */ + msr |= (target_ulong)1 << MSR_EP; #if defined (DO_SINGLE_STEP) && 0 /* Single step trace mode */ - msr_se = 1; - msr_be = 1; + msr |= (target_ulong)1 << MSR_SE; + msr |= (target_ulong)1 << MSR_BE; #endif #if defined(CONFIG_USER_ONLY) - msr_fp = 1; /* Allow floating point exceptions */ - msr_pr = 1; + msr |= (target_ulong)1 << MSR_FP; /* Allow floating point usage */ + msr |= (target_ulong)1 << MSR_PR; #else - env->nip = 0xFFFFFFFC; - ppc_tlb_invalidate_all(env); + env->nip = env->hreset_vector | env->excp_prefix; + if (env->mmu_model != POWERPC_MMU_REAL) + ppc_tlb_invalidate_all(env); #endif - do_compute_hflags(env); - env->reserve = -1; + env->msr = msr; + hreg_compute_hflags(env); + env->reserve = (target_ulong)-1ULL; /* Be sure no exception or interrupt is pending */ env->pending_interrupts = 0; env->exception_index = POWERPC_EXCP_NONE; @@ -2795,21 +2963,27 @@ void cpu_ppc_reset (void *opaque) tlb_flush(env, 1); } -CPUPPCState *cpu_ppc_init (void) +CPUPPCState *cpu_ppc_init (const char *cpu_model) { CPUPPCState *env; + const ppc_def_t *def; + + def = cpu_ppc_find_by_name(cpu_model); + if (!def) + return NULL; env = qemu_mallocz(sizeof(CPUPPCState)); if (!env) return NULL; cpu_exec_init(env); + env->cpu_model_str = cpu_model; + cpu_ppc_register_internal(env, def); cpu_ppc_reset(env); - return env; } void cpu_ppc_close (CPUPPCState *env) { /* Should also remove all opcode tables... */ - free(env); + qemu_free(env); } |