aboutsummaryrefslogtreecommitdiff
path: root/target-ppc/helper.c
diff options
context:
space:
mode:
authorj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>2007-10-14 10:21:20 +0000
committerj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>2007-10-14 10:21:20 +0000
commitb227a8e9aa5f27d29f77ba90d5eb9d0662a1175e (patch)
tree04750069b22e9597204dc8a08cbf7ebdf1030610 /target-ppc/helper.c
parentdbdd25065e90ba78c01b03d238785f25476c04a1 (diff)
Properly implement non-execute bit on PowerPC segments and PTEs.
Fix page protection bits for PowerPC 64 MMU. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3395 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'target-ppc/helper.c')
-rw-r--r--target-ppc/helper.c265
1 files changed, 138 insertions, 127 deletions
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)