diff options
-rw-r--r-- | target-ppc/cpu.h | 1 | ||||
-rw-r--r-- | target-ppc/helper.c | 265 |
2 files changed, 139 insertions, 127 deletions
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index c83d0baae3..aeaacb2579 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -596,6 +596,7 @@ struct mmu_ctx_t { target_phys_addr_t pg_addr[2]; /* PTE tables base addresses */ target_ulong ptem; /* Virtual segment ID | API */ int key; /* Access key */ + int nx; /* Non-execute area */ }; /*****************************************************************************/ diff --git a/target-ppc/helper.c b/target-ppc/helper.c index 12c8e71afa..76312476a0 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -96,12 +96,76 @@ static always_inline void pte64_invalidate (target_ulong *pte0) #define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F) #endif +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 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,11 +186,15 @@ static always_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) { @@ -138,42 +206,23 @@ static always_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,17 +230,17 @@ static always_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 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 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 @@ -353,7 +402,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; @@ -398,7 +447,7 @@ static int get_bat (CPUState *env, mmu_ctx_t *ctx, { target_ulong *BATlt, *BATut, *BATu, *BATl; target_ulong base, BEPIl, BEPIu, bl; - int i; + int i, pp; int ret = -1; #if defined (DEBUG_BATS) @@ -447,19 +496,23 @@ static int get_bat (CPUState *env, mmu_ctx_t *ctx, 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 */ + pp = *BATl & 0x00000003; + ctx->prot = 0; + if (pp != 0) { + ctx->prot = PAGE_READ | PAGE_EXEC; + if (pp == 0x2) + ctx->prot |= PAGE_WRITE; + } + ret = check_prot(ctx->prot, rw, type); #if defined (DEBUG_BATS) - if (loglevel != 0) { + if (ret == 0 && loglevel != 0) { fprintf(logfile, "BAT %d match: r 0x" PADDRX " prot=%c%c\n", i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', ctx->prot & PAGE_WRITE ? 'W' : '-'); } #endif - ret = 0; break; } } @@ -483,12 +536,14 @@ static int get_bat (CPUState *env, mmu_ctx_t *ctx, } #endif } + /* No hit */ return ret; } /* PTE table lookup */ -static always_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; @@ -501,7 +556,7 @@ static always_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 0x" ADDRX " => 0x" ADDRX @@ -516,7 +571,7 @@ static always_inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h, int rw) { 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 @@ -577,27 +632,27 @@ static always_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 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 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 always_inline int find_pte (CPUState *env, mmu_ctx_t *ctx, - int h, int rw) + int h, int rw, int type) { #if defined(TARGET_PPC64) if (env->mmu_model == POWERPC_MMU_64B) - return find_pte64(ctx, h, rw); + 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) @@ -796,7 +851,7 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, #if defined(TARGET_PPC64) int attr; #endif - int ds, nx, vsid_sh, sdr_sh; + int ds, vsid_sh, sdr_sh; int ret, ret2; #if defined(TARGET_PPC64) @@ -812,7 +867,7 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, ctx->key = ((attr & 0x40) && msr_pr == 1) || ((attr & 0x80) && msr_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; @@ -825,7 +880,7 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, ctx->key = (((sr & 0x20000000) && msr_pr == 1) || ((sr & 0x40000000) && msr_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; @@ -844,13 +899,13 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, #if defined (DEBUG_MMU) if (loglevel != 0) { fprintf(logfile, "pte segment: key=%d ds %d nx %d vsid " ADDRX "\n", - ctx->key, ds, nx, vsid); + 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 */ /* Primary table address */ sdr = env->sdr1; @@ -909,7 +964,7 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, } #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) @@ -921,7 +976,7 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, (uint32_t)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; } @@ -1119,76 +1174,32 @@ 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 (msr_pr) + 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 (msr_pr) { + 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; @@ -1274,7 +1285,7 @@ static int check_physical (CPUState *env, mmu_ctx_t *ctx, 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: @@ -1421,9 +1432,9 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, } ret = get_physical_address(env, &ctx, address, rw, access_type, 1); if (ret == 0) { - ret = tlb_set_page(env, address & TARGET_PAGE_MASK, - ctx.raddr & TARGET_PAGE_MASK, ctx.prot, - mmu_idx, 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) |