commit b227a8e9aa5f27d29f77ba90d5eb9d0662a1175e
Author: j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Date:   Sun Oct 14 10:21:20 2007 +0000

    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

diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 12c8e71..7631247 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)
