]> git.proxmox.com Git - mirror_qemu.git/blobdiff - target-microblaze/mmu.c
Fix unassigned memory access handling
[mirror_qemu.git] / target-microblaze / mmu.c
index f3dfa89bcefc07e6db0d6183ef821377bb65210e..281fc8d8c45224dc23221e34beb888abff90c223 100644 (file)
@@ -14,8 +14,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 #include <stdio.h>
 #include <stdlib.h>
@@ -23,7 +22,6 @@
 
 #include "config.h"
 #include "cpu.h"
-#include "exec-all.h"
 
 #define D(x)
 
@@ -37,7 +35,7 @@ static unsigned int tlb_decode_size(unsigned int f)
     return sizes[f];
 }
 
-static void mmu_flush_idx(CPUState *env, unsigned idx)
+static void mmu_flush_idx(CPUState *env, unsigned int idx)
 {
     struct microblaze_mmu *mmu = &env->mmu;
     unsigned int tlb_size;
@@ -61,8 +59,7 @@ static void mmu_change_pid(CPUState *env, unsigned int newpid)
 {
     struct microblaze_mmu *mmu = &env->mmu;
     unsigned int i;
-    unsigned int tlb_size;
-    uint32_t tlb_tag, mask, t;
+    uint32_t t;
 
     if (newpid & ~0xff)
         qemu_log("Illegal rpid=%x\n", newpid);
@@ -71,10 +68,6 @@ static void mmu_change_pid(CPUState *env, unsigned int newpid)
         /* Lookup and decode.  */
         t = mmu->rams[RAM_TAG][i];
         if (t & TLB_VALID) {
-            tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7);
-            mask = ~(tlb_size - 1);
-
-            tlb_tag = t & TLB_EPN_MASK;
             if (mmu->tids[i] && ((mmu->regs[MMU_R_PID] & 0xff) == mmu->tids[i]))
                 mmu_flush_idx(env, i);
         }
@@ -128,6 +121,16 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu,
             tlb_zsel = (d >> 4) & 0xf;
             t0 = mmu->regs[MMU_R_ZPR] >> (30 - (tlb_zsel * 2));
             t0 &= 0x3;
+
+            if (tlb_zsel > mmu->c_mmu_zones) {
+                qemu_log("tlb zone select out of range! %d\n", tlb_zsel);
+                t0 = 1; /* Ignore.  */
+            }
+
+            if (mmu->c_mmu == 1) {
+                t0 = 1; /* Zones are disabled.  */
+            }
+
             switch (t0) {
                 case 0:
                     if (mmu_idx == MMU_USER_IDX)
@@ -143,9 +146,9 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu,
                     tlb_ex = 1;
                     tlb_wr = 1;
                     break;
+                default: break;
             }
 
-
             lu->err = ERR_PROT;
             lu->prot = PAGE_READ;
             if (tlb_wr)
@@ -181,15 +184,33 @@ uint32_t mmu_read(CPUState *env, uint32_t rn)
     unsigned int i;
     uint32_t r;
 
+    if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) {
+        qemu_log("MMU access on MMU-less system\n");
+        return 0;
+    }
+
     switch (rn) {
         /* Reads to HI/LO trig reads from the mmu rams.  */
         case MMU_R_TLBLO:
         case MMU_R_TLBHI:
+            if (!(env->mmu.c_mmu_tlb_access & 1)) {
+                qemu_log("Invalid access to MMU reg %d\n", rn);
+                return 0;
+            }
+
             i = env->mmu.regs[MMU_R_TLBX] & 0xff;
             r = env->mmu.rams[rn & 1][i];
             if (rn == MMU_R_TLBHI)
                 env->mmu.regs[MMU_R_PID] = env->mmu.tids[i];
             break;
+        case MMU_R_PID:
+        case MMU_R_ZPR:
+            if (!(env->mmu.c_mmu_tlb_access & 1)) {
+                qemu_log("Invalid access to MMU reg %d\n", rn);
+                return 0;
+            }
+            r = env->mmu.regs[rn];
+            break;
         default:
             r = env->mmu.regs[rn];
             break;
@@ -203,6 +224,11 @@ void mmu_write(CPUState *env, uint32_t rn, uint32_t v)
     unsigned int i;
     D(qemu_log("%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn]));
 
+    if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) {
+        qemu_log("MMU access on MMU-less system\n");
+        return;
+    }
+
     switch (rn) {
         /* Writes to HI/LO trig writes to the mmu rams.  */
         case MMU_R_TLBLO:
@@ -220,7 +246,24 @@ void mmu_write(CPUState *env, uint32_t rn, uint32_t v)
             D(qemu_log("%s ram[%d][%d]=%x\n", __func__, rn & 1, i, v));
             break;
         case MMU_R_ZPR:
+            if (env->mmu.c_mmu_tlb_access <= 1) {
+                qemu_log("Invalid access to MMU reg %d\n", rn);
+                return;
+            }
+
+            /* Changes to the zone protection reg flush the QEMU TLB.
+               Fortunately, these are very uncommon.  */
+            if (v != env->mmu.regs[rn]) {
+                tlb_flush(env, 1);
+            }
+            env->mmu.regs[rn] = v;
+            break;
         case MMU_R_PID:
+            if (env->mmu.c_mmu_tlb_access <= 1) {
+                qemu_log("Invalid access to MMU reg %d\n", rn);
+                return;
+            }
+
             if (v != env->mmu.regs[rn]) {
                 mmu_change_pid(env, v);
                 env->mmu.regs[rn] = v;
@@ -230,6 +273,12 @@ void mmu_write(CPUState *env, uint32_t rn, uint32_t v)
         {
             struct microblaze_mmu_lookup lu;
             int hit;
+
+            if (env->mmu.c_mmu_tlb_access <= 1) {
+                qemu_log("Invalid access to MMU reg %d\n", rn);
+                return;
+            }
+
             hit = mmu_translate(&env->mmu, &lu,
                                 v & TLB_EPN_MASK, 0, cpu_mmu_index(env));
             if (hit) {
@@ -246,5 +295,8 @@ void mmu_write(CPUState *env, uint32_t rn, uint32_t v)
 
 void mmu_init(struct microblaze_mmu *mmu)
 {
-    memset(mmu, 0, sizeof *mmu);
+    int i;
+    for (i = 0; i < ARRAY_SIZE(mmu->regs); i++) {
+        mmu->regs[i] = 0;
+    }
 }