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