1fcf5ef2aSThomas Huth /* 2fcf5ef2aSThomas Huth * Microblaze MMU emulation for qemu. 3fcf5ef2aSThomas Huth * 4fcf5ef2aSThomas Huth * Copyright (c) 2009 Edgar E. Iglesias 5fcf5ef2aSThomas Huth * Copyright (c) 2009-2012 PetaLogix Qld Pty Ltd. 6fcf5ef2aSThomas Huth * 7fcf5ef2aSThomas Huth * This library is free software; you can redistribute it and/or 8fcf5ef2aSThomas Huth * modify it under the terms of the GNU Lesser General Public 9fcf5ef2aSThomas Huth * License as published by the Free Software Foundation; either 10fcf5ef2aSThomas Huth * version 2 of the License, or (at your option) any later version. 11fcf5ef2aSThomas Huth * 12fcf5ef2aSThomas Huth * This library is distributed in the hope that it will be useful, 13fcf5ef2aSThomas Huth * but WITHOUT ANY WARRANTY; without even the implied warranty of 14fcf5ef2aSThomas Huth * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15fcf5ef2aSThomas Huth * Lesser General Public License for more details. 16fcf5ef2aSThomas Huth * 17fcf5ef2aSThomas Huth * You should have received a copy of the GNU Lesser General Public 18fcf5ef2aSThomas Huth * License along with this library; if not, see <http://www.gnu.org/licenses/>. 19fcf5ef2aSThomas Huth */ 20fcf5ef2aSThomas Huth 21fcf5ef2aSThomas Huth #include "qemu/osdep.h" 22fcf5ef2aSThomas Huth #include "cpu.h" 23fcf5ef2aSThomas Huth #include "exec/exec-all.h" 24fcf5ef2aSThomas Huth 25fcf5ef2aSThomas Huth static unsigned int tlb_decode_size(unsigned int f) 26fcf5ef2aSThomas Huth { 27fcf5ef2aSThomas Huth static const unsigned int sizes[] = { 28fcf5ef2aSThomas Huth 1 * 1024, 4 * 1024, 16 * 1024, 64 * 1024, 256 * 1024, 29fcf5ef2aSThomas Huth 1 * 1024 * 1024, 4 * 1024 * 1024, 16 * 1024 * 1024 30fcf5ef2aSThomas Huth }; 31fcf5ef2aSThomas Huth assert(f < ARRAY_SIZE(sizes)); 32fcf5ef2aSThomas Huth return sizes[f]; 33fcf5ef2aSThomas Huth } 34fcf5ef2aSThomas Huth 35fcf5ef2aSThomas Huth static void mmu_flush_idx(CPUMBState *env, unsigned int idx) 36fcf5ef2aSThomas Huth { 37f5c7e93aSRichard Henderson CPUState *cs = env_cpu(env); 38fcf5ef2aSThomas Huth struct microblaze_mmu *mmu = &env->mmu; 39fcf5ef2aSThomas Huth unsigned int tlb_size; 40fcf5ef2aSThomas Huth uint32_t tlb_tag, end, t; 41fcf5ef2aSThomas Huth 42fcf5ef2aSThomas Huth t = mmu->rams[RAM_TAG][idx]; 43fcf5ef2aSThomas Huth if (!(t & TLB_VALID)) 44fcf5ef2aSThomas Huth return; 45fcf5ef2aSThomas Huth 46fcf5ef2aSThomas Huth tlb_tag = t & TLB_EPN_MASK; 47fcf5ef2aSThomas Huth tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7); 48fcf5ef2aSThomas Huth end = tlb_tag + tlb_size; 49fcf5ef2aSThomas Huth 50fcf5ef2aSThomas Huth while (tlb_tag < end) { 51fcf5ef2aSThomas Huth tlb_flush_page(cs, tlb_tag); 52fcf5ef2aSThomas Huth tlb_tag += TARGET_PAGE_SIZE; 53fcf5ef2aSThomas Huth } 54fcf5ef2aSThomas Huth } 55fcf5ef2aSThomas Huth 56fcf5ef2aSThomas Huth static void mmu_change_pid(CPUMBState *env, unsigned int newpid) 57fcf5ef2aSThomas Huth { 58fcf5ef2aSThomas Huth struct microblaze_mmu *mmu = &env->mmu; 59fcf5ef2aSThomas Huth unsigned int i; 60fcf5ef2aSThomas Huth uint32_t t; 61fcf5ef2aSThomas Huth 62fcf5ef2aSThomas Huth if (newpid & ~0xff) 63fcf5ef2aSThomas Huth qemu_log_mask(LOG_GUEST_ERROR, "Illegal rpid=%x\n", newpid); 64fcf5ef2aSThomas Huth 65fcf5ef2aSThomas Huth for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) { 66fcf5ef2aSThomas Huth /* Lookup and decode. */ 67fcf5ef2aSThomas Huth t = mmu->rams[RAM_TAG][i]; 68fcf5ef2aSThomas Huth if (t & TLB_VALID) { 69fcf5ef2aSThomas Huth if (mmu->tids[i] && ((mmu->regs[MMU_R_PID] & 0xff) == mmu->tids[i])) 70fcf5ef2aSThomas Huth mmu_flush_idx(env, i); 71fcf5ef2aSThomas Huth } 72fcf5ef2aSThomas Huth } 73fcf5ef2aSThomas Huth } 74fcf5ef2aSThomas Huth 75fcf5ef2aSThomas Huth /* rw - 0 = read, 1 = write, 2 = fetch. */ 76fcf5ef2aSThomas Huth unsigned int mmu_translate(struct microblaze_mmu *mmu, 77fcf5ef2aSThomas Huth struct microblaze_mmu_lookup *lu, 78fcf5ef2aSThomas Huth target_ulong vaddr, int rw, int mmu_idx) 79fcf5ef2aSThomas Huth { 80fcf5ef2aSThomas Huth unsigned int i, hit = 0; 81fcf5ef2aSThomas Huth unsigned int tlb_ex = 0, tlb_wr = 0, tlb_zsel; 82d2f004c3SEdgar E. Iglesias uint64_t tlb_tag, tlb_rpn, mask; 83d2f004c3SEdgar E. Iglesias uint32_t tlb_size, t0; 84fcf5ef2aSThomas Huth 85fcf5ef2aSThomas Huth lu->err = ERR_MISS; 86fcf5ef2aSThomas Huth for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) { 87d2f004c3SEdgar E. Iglesias uint64_t t, d; 88fcf5ef2aSThomas Huth 89fcf5ef2aSThomas Huth /* Lookup and decode. */ 90fcf5ef2aSThomas Huth t = mmu->rams[RAM_TAG][i]; 91fcf5ef2aSThomas Huth if (t & TLB_VALID) { 92fcf5ef2aSThomas Huth tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7); 93fcf5ef2aSThomas Huth if (tlb_size < TARGET_PAGE_SIZE) { 9475c9ddceSEdgar E. Iglesias qemu_log_mask(LOG_UNIMP, "%d pages not supported\n", tlb_size); 95fcf5ef2aSThomas Huth abort(); 96fcf5ef2aSThomas Huth } 97fcf5ef2aSThomas Huth 98d2f004c3SEdgar E. Iglesias mask = ~((uint64_t)tlb_size - 1); 99fcf5ef2aSThomas Huth tlb_tag = t & TLB_EPN_MASK; 100fcf5ef2aSThomas Huth if ((vaddr & mask) != (tlb_tag & mask)) { 101fcf5ef2aSThomas Huth continue; 102fcf5ef2aSThomas Huth } 103fcf5ef2aSThomas Huth if (mmu->tids[i] 104fcf5ef2aSThomas Huth && ((mmu->regs[MMU_R_PID] & 0xff) != mmu->tids[i])) { 105fcf5ef2aSThomas Huth continue; 106fcf5ef2aSThomas Huth } 107fcf5ef2aSThomas Huth 108fcf5ef2aSThomas Huth /* Bring in the data part. */ 109fcf5ef2aSThomas Huth d = mmu->rams[RAM_DATA][i]; 110fcf5ef2aSThomas Huth tlb_ex = d & TLB_EX; 111fcf5ef2aSThomas Huth tlb_wr = d & TLB_WR; 112fcf5ef2aSThomas Huth 113fcf5ef2aSThomas Huth /* Now let's see if there is a zone that overrides the protbits. */ 114fcf5ef2aSThomas Huth tlb_zsel = (d >> 4) & 0xf; 115fcf5ef2aSThomas Huth t0 = mmu->regs[MMU_R_ZPR] >> (30 - (tlb_zsel * 2)); 116fcf5ef2aSThomas Huth t0 &= 0x3; 117fcf5ef2aSThomas Huth 118fcf5ef2aSThomas Huth if (tlb_zsel > mmu->c_mmu_zones) { 11975c9ddceSEdgar E. Iglesias qemu_log_mask(LOG_GUEST_ERROR, 12075c9ddceSEdgar E. Iglesias "tlb zone select out of range! %d\n", tlb_zsel); 121fcf5ef2aSThomas Huth t0 = 1; /* Ignore. */ 122fcf5ef2aSThomas Huth } 123fcf5ef2aSThomas Huth 124fcf5ef2aSThomas Huth if (mmu->c_mmu == 1) { 125fcf5ef2aSThomas Huth t0 = 1; /* Zones are disabled. */ 126fcf5ef2aSThomas Huth } 127fcf5ef2aSThomas Huth 128fcf5ef2aSThomas Huth switch (t0) { 129fcf5ef2aSThomas Huth case 0: 130fcf5ef2aSThomas Huth if (mmu_idx == MMU_USER_IDX) 131fcf5ef2aSThomas Huth continue; 132fcf5ef2aSThomas Huth break; 133fcf5ef2aSThomas Huth case 2: 134fcf5ef2aSThomas Huth if (mmu_idx != MMU_USER_IDX) { 135fcf5ef2aSThomas Huth tlb_ex = 1; 136fcf5ef2aSThomas Huth tlb_wr = 1; 137fcf5ef2aSThomas Huth } 138fcf5ef2aSThomas Huth break; 139fcf5ef2aSThomas Huth case 3: 140fcf5ef2aSThomas Huth tlb_ex = 1; 141fcf5ef2aSThomas Huth tlb_wr = 1; 142fcf5ef2aSThomas Huth break; 143fcf5ef2aSThomas Huth default: break; 144fcf5ef2aSThomas Huth } 145fcf5ef2aSThomas Huth 146fcf5ef2aSThomas Huth lu->err = ERR_PROT; 147fcf5ef2aSThomas Huth lu->prot = PAGE_READ; 148fcf5ef2aSThomas Huth if (tlb_wr) 149fcf5ef2aSThomas Huth lu->prot |= PAGE_WRITE; 150fcf5ef2aSThomas Huth else if (rw == 1) 151fcf5ef2aSThomas Huth goto done; 152fcf5ef2aSThomas Huth if (tlb_ex) 153fcf5ef2aSThomas Huth lu->prot |=PAGE_EXEC; 154fcf5ef2aSThomas Huth else if (rw == 2) { 155fcf5ef2aSThomas Huth goto done; 156fcf5ef2aSThomas Huth } 157fcf5ef2aSThomas Huth 158fcf5ef2aSThomas Huth tlb_rpn = d & TLB_RPN_MASK; 159fcf5ef2aSThomas Huth 160fcf5ef2aSThomas Huth lu->vaddr = tlb_tag; 1613924a9aaSEdgar E. Iglesias lu->paddr = tlb_rpn & mmu->c_addr_mask; 162fcf5ef2aSThomas Huth lu->size = tlb_size; 163fcf5ef2aSThomas Huth lu->err = ERR_HIT; 164fcf5ef2aSThomas Huth lu->idx = i; 165fcf5ef2aSThomas Huth hit = 1; 166fcf5ef2aSThomas Huth goto done; 167fcf5ef2aSThomas Huth } 168fcf5ef2aSThomas Huth } 169fcf5ef2aSThomas Huth done: 17075c9ddceSEdgar E. Iglesias qemu_log_mask(CPU_LOG_MMU, 17175c9ddceSEdgar E. Iglesias "MMU vaddr=%" PRIx64 " rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", 17275c9ddceSEdgar E. Iglesias vaddr, rw, tlb_wr, tlb_ex, hit); 173fcf5ef2aSThomas Huth return hit; 174fcf5ef2aSThomas Huth } 175fcf5ef2aSThomas Huth 176fcf5ef2aSThomas Huth /* Writes/reads to the MMU's special regs end up here. */ 177f0f7e7f7SEdgar E. Iglesias uint32_t mmu_read(CPUMBState *env, bool ext, uint32_t rn) 178fcf5ef2aSThomas Huth { 179fcf5ef2aSThomas Huth unsigned int i; 180bd9e6608SEdgar E. Iglesias uint32_t r = 0; 181fcf5ef2aSThomas Huth 182fcf5ef2aSThomas Huth if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) { 183fcf5ef2aSThomas Huth qemu_log_mask(LOG_GUEST_ERROR, "MMU access on MMU-less system\n"); 184fcf5ef2aSThomas Huth return 0; 185fcf5ef2aSThomas Huth } 186f0f7e7f7SEdgar E. Iglesias if (ext && rn != MMU_R_TLBLO) { 187f0f7e7f7SEdgar E. Iglesias qemu_log_mask(LOG_GUEST_ERROR, "Extended access only to TLBLO.\n"); 188f0f7e7f7SEdgar E. Iglesias return 0; 189f0f7e7f7SEdgar E. Iglesias } 190fcf5ef2aSThomas Huth 191fcf5ef2aSThomas Huth switch (rn) { 192fcf5ef2aSThomas Huth /* Reads to HI/LO trig reads from the mmu rams. */ 193fcf5ef2aSThomas Huth case MMU_R_TLBLO: 194fcf5ef2aSThomas Huth case MMU_R_TLBHI: 195fcf5ef2aSThomas Huth if (!(env->mmu.c_mmu_tlb_access & 1)) { 19675c9ddceSEdgar E. Iglesias qemu_log_mask(LOG_GUEST_ERROR, 19775c9ddceSEdgar E. Iglesias "Invalid access to MMU reg %d\n", rn); 198fcf5ef2aSThomas Huth return 0; 199fcf5ef2aSThomas Huth } 200fcf5ef2aSThomas Huth 201fcf5ef2aSThomas Huth i = env->mmu.regs[MMU_R_TLBX] & 0xff; 202f0f7e7f7SEdgar E. Iglesias r = extract64(env->mmu.rams[rn & 1][i], ext * 32, 32); 203fcf5ef2aSThomas Huth if (rn == MMU_R_TLBHI) 204fcf5ef2aSThomas Huth env->mmu.regs[MMU_R_PID] = env->mmu.tids[i]; 205fcf5ef2aSThomas Huth break; 206fcf5ef2aSThomas Huth case MMU_R_PID: 207fcf5ef2aSThomas Huth case MMU_R_ZPR: 208fcf5ef2aSThomas Huth if (!(env->mmu.c_mmu_tlb_access & 1)) { 20975c9ddceSEdgar E. Iglesias qemu_log_mask(LOG_GUEST_ERROR, 21075c9ddceSEdgar E. Iglesias "Invalid access to MMU reg %d\n", rn); 211fcf5ef2aSThomas Huth return 0; 212fcf5ef2aSThomas Huth } 213fcf5ef2aSThomas Huth r = env->mmu.regs[rn]; 214fcf5ef2aSThomas Huth break; 21596716533SEdgar E. Iglesias case MMU_R_TLBX: 21696716533SEdgar E. Iglesias r = env->mmu.regs[rn]; 21796716533SEdgar E. Iglesias break; 218bd9e6608SEdgar E. Iglesias case MMU_R_TLBSX: 219bd9e6608SEdgar E. Iglesias qemu_log_mask(LOG_GUEST_ERROR, "TLBSX is write-only.\n"); 220bd9e6608SEdgar E. Iglesias break; 221fcf5ef2aSThomas Huth default: 22296716533SEdgar E. Iglesias qemu_log_mask(LOG_GUEST_ERROR, "Invalid MMU register %d.\n", rn); 223fcf5ef2aSThomas Huth break; 224fcf5ef2aSThomas Huth } 22575c9ddceSEdgar E. Iglesias qemu_log_mask(CPU_LOG_MMU, "%s rn=%d=%x\n", __func__, rn, r); 226fcf5ef2aSThomas Huth return r; 227fcf5ef2aSThomas Huth } 228fcf5ef2aSThomas Huth 229f0f7e7f7SEdgar E. Iglesias void mmu_write(CPUMBState *env, bool ext, uint32_t rn, uint32_t v) 230fcf5ef2aSThomas Huth { 231f0f7e7f7SEdgar E. Iglesias uint64_t tmp64; 232fcf5ef2aSThomas Huth unsigned int i; 23375c9ddceSEdgar E. Iglesias qemu_log_mask(CPU_LOG_MMU, 23475c9ddceSEdgar E. Iglesias "%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn]); 235fcf5ef2aSThomas Huth 236fcf5ef2aSThomas Huth if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) { 237fcf5ef2aSThomas Huth qemu_log_mask(LOG_GUEST_ERROR, "MMU access on MMU-less system\n"); 238fcf5ef2aSThomas Huth return; 239fcf5ef2aSThomas Huth } 240f0f7e7f7SEdgar E. Iglesias if (ext && rn != MMU_R_TLBLO) { 241f0f7e7f7SEdgar E. Iglesias qemu_log_mask(LOG_GUEST_ERROR, "Extended access only to TLBLO.\n"); 242f0f7e7f7SEdgar E. Iglesias return; 243f0f7e7f7SEdgar E. Iglesias } 244fcf5ef2aSThomas Huth 245fcf5ef2aSThomas Huth switch (rn) { 246fcf5ef2aSThomas Huth /* Writes to HI/LO trig writes to the mmu rams. */ 247fcf5ef2aSThomas Huth case MMU_R_TLBLO: 248fcf5ef2aSThomas Huth case MMU_R_TLBHI: 249fcf5ef2aSThomas Huth i = env->mmu.regs[MMU_R_TLBX] & 0xff; 250fcf5ef2aSThomas Huth if (rn == MMU_R_TLBHI) { 251fcf5ef2aSThomas Huth if (i < 3 && !(v & TLB_VALID) && qemu_loglevel_mask(~0)) 2520a22f8cfSEdgar E. Iglesias qemu_log_mask(LOG_GUEST_ERROR, 253*0f96e96bSRichard Henderson "invalidating index %x at pc=%x\n", 25476e8187dSRichard Henderson i, env->pc); 255fcf5ef2aSThomas Huth env->mmu.tids[i] = env->mmu.regs[MMU_R_PID] & 0xff; 256fcf5ef2aSThomas Huth mmu_flush_idx(env, i); 257fcf5ef2aSThomas Huth } 258f0f7e7f7SEdgar E. Iglesias tmp64 = env->mmu.rams[rn & 1][i]; 259f0f7e7f7SEdgar E. Iglesias env->mmu.rams[rn & 1][i] = deposit64(tmp64, ext * 32, 32, v); 260fcf5ef2aSThomas Huth break; 261fcf5ef2aSThomas Huth case MMU_R_ZPR: 262fcf5ef2aSThomas Huth if (env->mmu.c_mmu_tlb_access <= 1) { 26375c9ddceSEdgar E. Iglesias qemu_log_mask(LOG_GUEST_ERROR, 26475c9ddceSEdgar E. Iglesias "Invalid access to MMU reg %d\n", rn); 265fcf5ef2aSThomas Huth return; 266fcf5ef2aSThomas Huth } 267fcf5ef2aSThomas Huth 268fcf5ef2aSThomas Huth /* Changes to the zone protection reg flush the QEMU TLB. 269fcf5ef2aSThomas Huth Fortunately, these are very uncommon. */ 270fcf5ef2aSThomas Huth if (v != env->mmu.regs[rn]) { 271f5c7e93aSRichard Henderson tlb_flush(env_cpu(env)); 272fcf5ef2aSThomas Huth } 273fcf5ef2aSThomas Huth env->mmu.regs[rn] = v; 274fcf5ef2aSThomas Huth break; 275fcf5ef2aSThomas Huth case MMU_R_PID: 276fcf5ef2aSThomas Huth if (env->mmu.c_mmu_tlb_access <= 1) { 27775c9ddceSEdgar E. Iglesias qemu_log_mask(LOG_GUEST_ERROR, 27875c9ddceSEdgar E. Iglesias "Invalid access to MMU reg %d\n", rn); 279fcf5ef2aSThomas Huth return; 280fcf5ef2aSThomas Huth } 281fcf5ef2aSThomas Huth 282fcf5ef2aSThomas Huth if (v != env->mmu.regs[rn]) { 283fcf5ef2aSThomas Huth mmu_change_pid(env, v); 284fcf5ef2aSThomas Huth env->mmu.regs[rn] = v; 285fcf5ef2aSThomas Huth } 286fcf5ef2aSThomas Huth break; 287fce6a8ecSEdgar E. Iglesias case MMU_R_TLBX: 288fce6a8ecSEdgar E. Iglesias /* Bit 31 is read-only. */ 289fce6a8ecSEdgar E. Iglesias env->mmu.regs[rn] = deposit32(env->mmu.regs[rn], 0, 31, v); 290fce6a8ecSEdgar E. Iglesias break; 291fcf5ef2aSThomas Huth case MMU_R_TLBSX: 292fcf5ef2aSThomas Huth { 293fcf5ef2aSThomas Huth struct microblaze_mmu_lookup lu; 294fcf5ef2aSThomas Huth int hit; 295fcf5ef2aSThomas Huth 296fcf5ef2aSThomas Huth if (env->mmu.c_mmu_tlb_access <= 1) { 29775c9ddceSEdgar E. Iglesias qemu_log_mask(LOG_GUEST_ERROR, 29875c9ddceSEdgar E. Iglesias "Invalid access to MMU reg %d\n", rn); 299fcf5ef2aSThomas Huth return; 300fcf5ef2aSThomas Huth } 301fcf5ef2aSThomas Huth 302fcf5ef2aSThomas Huth hit = mmu_translate(&env->mmu, &lu, 303fcf5ef2aSThomas Huth v & TLB_EPN_MASK, 0, cpu_mmu_index(env, false)); 304fcf5ef2aSThomas Huth if (hit) { 305fcf5ef2aSThomas Huth env->mmu.regs[MMU_R_TLBX] = lu.idx; 306a2207b59SEdgar E. Iglesias } else { 307a2207b59SEdgar E. Iglesias env->mmu.regs[MMU_R_TLBX] |= R_TBLX_MISS_MASK; 308a2207b59SEdgar E. Iglesias } 309fcf5ef2aSThomas Huth break; 310fcf5ef2aSThomas Huth } 311fcf5ef2aSThomas Huth default: 31296716533SEdgar E. Iglesias qemu_log_mask(LOG_GUEST_ERROR, "Invalid MMU register %d.\n", rn); 313fcf5ef2aSThomas Huth break; 314fcf5ef2aSThomas Huth } 315fcf5ef2aSThomas Huth } 316fcf5ef2aSThomas Huth 317fcf5ef2aSThomas Huth void mmu_init(struct microblaze_mmu *mmu) 318fcf5ef2aSThomas Huth { 319fcf5ef2aSThomas Huth int i; 320fcf5ef2aSThomas Huth for (i = 0; i < ARRAY_SIZE(mmu->regs); i++) { 321fcf5ef2aSThomas Huth mmu->regs[i] = 0; 322fcf5ef2aSThomas Huth } 323fcf5ef2aSThomas Huth } 324