* Microblaze MMU emulation for qemu.
*
* Copyright (c) 2009 Edgar E. Iglesias
+ * Copyright (c) 2009-2012 PetaLogix Qld Pty Ltd.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* 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>
-#include <assert.h>
-#include "config.h"
#include "cpu.h"
-#include "exec-all.h"
#define D(x)
return sizes[f];
}
-static void mmu_flush_idx(CPUState *env, unsigned int idx)
+static void mmu_flush_idx(CPUMBState *env, unsigned int idx)
{
struct microblaze_mmu *mmu = &env->mmu;
unsigned int tlb_size;
}
}
-static void mmu_change_pid(CPUState *env, unsigned int newpid)
+static void mmu_change_pid(CPUMBState *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);
/* 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);
}
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)
tlb_ex = 1;
tlb_wr = 1;
break;
+ default: break;
}
-
lu->err = ERR_PROT;
lu->prot = PAGE_READ;
if (tlb_wr)
}
/* Writes/reads to the MMU's special regs end up here. */
-uint32_t mmu_read(CPUState *env, uint32_t rn)
+uint32_t mmu_read(CPUMBState *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;
return r;
}
-void mmu_write(CPUState *env, uint32_t rn, uint32_t v)
+void mmu_write(CPUMBState *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:
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]) {
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;
{
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) {
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;
+ }
}