|
|
1.1 ! root 1: /* ! 2: * Microblaze MMU emulation for qemu. ! 3: * ! 4: * Copyright (c) 2009 Edgar E. Iglesias ! 5: * ! 6: * This library is free software; you can redistribute it and/or ! 7: * modify it under the terms of the GNU Lesser General Public ! 8: * License as published by the Free Software Foundation; either ! 9: * version 2 of the License, or (at your option) any later version. ! 10: * ! 11: * This library is distributed in the hope that it will be useful, ! 12: * but WITHOUT ANY WARRANTY; without even the implied warranty of ! 13: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ! 14: * Lesser General Public License for more details. ! 15: * ! 16: * You should have received a copy of the GNU Lesser General Public ! 17: * License along with this library; if not, see <http://www.gnu.org/licenses/>. ! 18: */ ! 19: #include <stdio.h> ! 20: #include <stdlib.h> ! 21: #include <assert.h> ! 22: ! 23: #include "config.h" ! 24: #include "cpu.h" ! 25: #include "exec-all.h" ! 26: ! 27: #define D(x) ! 28: ! 29: static unsigned int tlb_decode_size(unsigned int f) ! 30: { ! 31: static const unsigned int sizes[] = { ! 32: 1 * 1024, 4 * 1024, 16 * 1024, 64 * 1024, 256 * 1024, ! 33: 1 * 1024 * 1024, 4 * 1024 * 1024, 16 * 1024 * 1024 ! 34: }; ! 35: assert(f < ARRAY_SIZE(sizes)); ! 36: return sizes[f]; ! 37: } ! 38: ! 39: static void mmu_flush_idx(CPUState *env, unsigned int idx) ! 40: { ! 41: struct microblaze_mmu *mmu = &env->mmu; ! 42: unsigned int tlb_size; ! 43: uint32_t tlb_tag, end, t; ! 44: ! 45: t = mmu->rams[RAM_TAG][idx]; ! 46: if (!(t & TLB_VALID)) ! 47: return; ! 48: ! 49: tlb_tag = t & TLB_EPN_MASK; ! 50: tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7); ! 51: end = tlb_tag + tlb_size; ! 52: ! 53: while (tlb_tag < end) { ! 54: tlb_flush_page(env, tlb_tag); ! 55: tlb_tag += TARGET_PAGE_SIZE; ! 56: } ! 57: } ! 58: ! 59: static void mmu_change_pid(CPUState *env, unsigned int newpid) ! 60: { ! 61: struct microblaze_mmu *mmu = &env->mmu; ! 62: unsigned int i; ! 63: unsigned int tlb_size; ! 64: uint32_t tlb_tag, mask, t; ! 65: ! 66: if (newpid & ~0xff) ! 67: qemu_log("Illegal rpid=%x\n", newpid); ! 68: ! 69: for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) { ! 70: /* Lookup and decode. */ ! 71: t = mmu->rams[RAM_TAG][i]; ! 72: if (t & TLB_VALID) { ! 73: tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7); ! 74: mask = ~(tlb_size - 1); ! 75: ! 76: tlb_tag = t & TLB_EPN_MASK; ! 77: if (mmu->tids[i] && ((mmu->regs[MMU_R_PID] & 0xff) == mmu->tids[i])) ! 78: mmu_flush_idx(env, i); ! 79: } ! 80: } ! 81: } ! 82: ! 83: /* rw - 0 = read, 1 = write, 2 = fetch. */ ! 84: unsigned int mmu_translate(struct microblaze_mmu *mmu, ! 85: struct microblaze_mmu_lookup *lu, ! 86: target_ulong vaddr, int rw, int mmu_idx) ! 87: { ! 88: unsigned int i, hit = 0; ! 89: unsigned int tlb_ex = 0, tlb_wr = 0, tlb_zsel; ! 90: unsigned int tlb_size; ! 91: uint32_t tlb_tag, tlb_rpn, mask, t0; ! 92: ! 93: lu->err = ERR_MISS; ! 94: for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) { ! 95: uint32_t t, d; ! 96: ! 97: /* Lookup and decode. */ ! 98: t = mmu->rams[RAM_TAG][i]; ! 99: D(qemu_log("TLB %d valid=%d\n", i, t & TLB_VALID)); ! 100: if (t & TLB_VALID) { ! 101: tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7); ! 102: if (tlb_size < TARGET_PAGE_SIZE) { ! 103: qemu_log("%d pages not supported\n", tlb_size); ! 104: abort(); ! 105: } ! 106: ! 107: mask = ~(tlb_size - 1); ! 108: tlb_tag = t & TLB_EPN_MASK; ! 109: if ((vaddr & mask) != (tlb_tag & mask)) { ! 110: D(qemu_log("TLB %d vaddr=%x != tag=%x\n", ! 111: i, vaddr & mask, tlb_tag & mask)); ! 112: continue; ! 113: } ! 114: if (mmu->tids[i] ! 115: && ((mmu->regs[MMU_R_PID] & 0xff) != mmu->tids[i])) { ! 116: D(qemu_log("TLB %d pid=%x != tid=%x\n", ! 117: i, mmu->regs[MMU_R_PID], mmu->tids[i])); ! 118: continue; ! 119: } ! 120: ! 121: /* Bring in the data part. */ ! 122: d = mmu->rams[RAM_DATA][i]; ! 123: tlb_ex = d & TLB_EX; ! 124: tlb_wr = d & TLB_WR; ! 125: ! 126: /* Now lets see if there is a zone that overrides the protbits. */ ! 127: tlb_zsel = (d >> 4) & 0xf; ! 128: t0 = mmu->regs[MMU_R_ZPR] >> (30 - (tlb_zsel * 2)); ! 129: t0 &= 0x3; ! 130: switch (t0) { ! 131: case 0: ! 132: if (mmu_idx == MMU_USER_IDX) ! 133: continue; ! 134: break; ! 135: case 2: ! 136: if (mmu_idx != MMU_USER_IDX) { ! 137: tlb_ex = 1; ! 138: tlb_wr = 1; ! 139: } ! 140: break; ! 141: case 3: ! 142: tlb_ex = 1; ! 143: tlb_wr = 1; ! 144: break; ! 145: } ! 146: ! 147: ! 148: lu->err = ERR_PROT; ! 149: lu->prot = PAGE_READ; ! 150: if (tlb_wr) ! 151: lu->prot |= PAGE_WRITE; ! 152: else if (rw == 1) ! 153: goto done; ! 154: if (tlb_ex) ! 155: lu->prot |=PAGE_EXEC; ! 156: else if (rw == 2) { ! 157: goto done; ! 158: } ! 159: ! 160: tlb_rpn = d & TLB_RPN_MASK; ! 161: ! 162: lu->vaddr = tlb_tag; ! 163: lu->paddr = tlb_rpn; ! 164: lu->size = tlb_size; ! 165: lu->err = ERR_HIT; ! 166: lu->idx = i; ! 167: hit = 1; ! 168: goto done; ! 169: } ! 170: } ! 171: done: ! 172: D(qemu_log("MMU vaddr=%x rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", ! 173: vaddr, rw, tlb_wr, tlb_ex, hit)); ! 174: return hit; ! 175: } ! 176: ! 177: /* Writes/reads to the MMU's special regs end up here. */ ! 178: uint32_t mmu_read(CPUState *env, uint32_t rn) ! 179: { ! 180: unsigned int i; ! 181: uint32_t r; ! 182: ! 183: switch (rn) { ! 184: /* Reads to HI/LO trig reads from the mmu rams. */ ! 185: case MMU_R_TLBLO: ! 186: case MMU_R_TLBHI: ! 187: i = env->mmu.regs[MMU_R_TLBX] & 0xff; ! 188: r = env->mmu.rams[rn & 1][i]; ! 189: if (rn == MMU_R_TLBHI) ! 190: env->mmu.regs[MMU_R_PID] = env->mmu.tids[i]; ! 191: break; ! 192: default: ! 193: r = env->mmu.regs[rn]; ! 194: break; ! 195: } ! 196: D(qemu_log("%s rn=%d=%x\n", __func__, rn, r)); ! 197: return r; ! 198: } ! 199: ! 200: void mmu_write(CPUState *env, uint32_t rn, uint32_t v) ! 201: { ! 202: unsigned int i; ! 203: D(qemu_log("%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn])); ! 204: ! 205: switch (rn) { ! 206: /* Writes to HI/LO trig writes to the mmu rams. */ ! 207: case MMU_R_TLBLO: ! 208: case MMU_R_TLBHI: ! 209: i = env->mmu.regs[MMU_R_TLBX] & 0xff; ! 210: if (rn == MMU_R_TLBHI) { ! 211: if (i < 3 && !(v & TLB_VALID) && qemu_loglevel_mask(~0)) ! 212: qemu_log("invalidating index %x at pc=%x\n", ! 213: i, env->sregs[SR_PC]); ! 214: env->mmu.tids[i] = env->mmu.regs[MMU_R_PID] & 0xff; ! 215: mmu_flush_idx(env, i); ! 216: } ! 217: env->mmu.rams[rn & 1][i] = v; ! 218: ! 219: D(qemu_log("%s ram[%d][%d]=%x\n", __func__, rn & 1, i, v)); ! 220: break; ! 221: case MMU_R_ZPR: ! 222: /* Changes to the zone protection reg flush the QEMU TLB. ! 223: Fortunately, these are very uncommon. */ ! 224: if (v != env->mmu.regs[rn]) { ! 225: tlb_flush(env, 1); ! 226: } ! 227: env->mmu.regs[rn] = v; ! 228: break; ! 229: case MMU_R_PID: ! 230: if (v != env->mmu.regs[rn]) { ! 231: mmu_change_pid(env, v); ! 232: env->mmu.regs[rn] = v; ! 233: } ! 234: break; ! 235: case MMU_R_TLBSX: ! 236: { ! 237: struct microblaze_mmu_lookup lu; ! 238: int hit; ! 239: hit = mmu_translate(&env->mmu, &lu, ! 240: v & TLB_EPN_MASK, 0, cpu_mmu_index(env)); ! 241: if (hit) { ! 242: env->mmu.regs[MMU_R_TLBX] = lu.idx; ! 243: } else ! 244: env->mmu.regs[MMU_R_TLBX] |= 0x80000000; ! 245: break; ! 246: } ! 247: default: ! 248: env->mmu.regs[rn] = v; ! 249: break; ! 250: } ! 251: } ! 252: ! 253: void mmu_init(struct microblaze_mmu *mmu) ! 254: { ! 255: memset(mmu, 0, sizeof *mmu); ! 256: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.