1 /* 2 * CRIS mmu emulation. 3 * 4 * Copyright (c) 2007 AXIS Communications AB 5 * Written by Edgar E. Iglesias. 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 #include "mmu.h" 25 26 #ifdef DEBUG 27 #define D(x) x 28 #define D_LOG(...) qemu_log(__VA_ARGS__) 29 #else 30 #define D(x) do { } while (0) 31 #define D_LOG(...) do { } while (0) 32 #endif 33 34 void cris_mmu_init(CPUCRISState *env) 35 { 36 env->mmu_rand_lfsr = 0xcccc; 37 } 38 39 #define SR_POLYNOM 0x8805 40 static inline unsigned int compute_polynom(unsigned int sr) 41 { 42 unsigned int i; 43 unsigned int f; 44 45 f = 0; 46 for (i = 0; i < 16; i++) 47 f += ((SR_POLYNOM >> i) & 1) & ((sr >> i) & 1); 48 49 return f; 50 } 51 52 static void cris_mmu_update_rand_lfsr(CPUCRISState *env) 53 { 54 unsigned int f; 55 56 /* Update lfsr at every fault. */ 57 f = compute_polynom(env->mmu_rand_lfsr); 58 env->mmu_rand_lfsr >>= 1; 59 env->mmu_rand_lfsr |= (f << 15); 60 env->mmu_rand_lfsr &= 0xffff; 61 } 62 63 static inline int cris_mmu_enabled(uint32_t rw_gc_cfg) 64 { 65 return (rw_gc_cfg & 12) != 0; 66 } 67 68 static inline int cris_mmu_segmented_addr(int seg, uint32_t rw_mm_cfg) 69 { 70 return (1 << seg) & rw_mm_cfg; 71 } 72 73 static uint32_t cris_mmu_translate_seg(CPUCRISState *env, int seg) 74 { 75 uint32_t base; 76 int i; 77 78 if (seg < 8) 79 base = env->sregs[SFR_RW_MM_KBASE_LO]; 80 else 81 base = env->sregs[SFR_RW_MM_KBASE_HI]; 82 83 i = seg & 7; 84 base >>= i * 4; 85 base &= 15; 86 87 base <<= 28; 88 return base; 89 } 90 /* Used by the tlb decoder. */ 91 #define EXTRACT_FIELD(src, start, end) \ 92 (((src) >> start) & ((1 << (end - start + 1)) - 1)) 93 94 static inline void set_field(uint32_t *dst, unsigned int val, 95 unsigned int offset, unsigned int width) 96 { 97 uint32_t mask; 98 99 mask = (1 << width) - 1; 100 mask <<= offset; 101 val <<= offset; 102 103 val &= mask; 104 *dst &= ~(mask); 105 *dst |= val; 106 } 107 108 #ifdef DEBUG 109 static void dump_tlb(CPUCRISState *env, int mmu) 110 { 111 int set; 112 int idx; 113 uint32_t hi, lo, tlb_vpn, tlb_pfn; 114 115 for (set = 0; set < 4; set++) { 116 for (idx = 0; idx < 16; idx++) { 117 lo = env->tlbsets[mmu][set][idx].lo; 118 hi = env->tlbsets[mmu][set][idx].hi; 119 tlb_vpn = EXTRACT_FIELD(hi, 13, 31); 120 tlb_pfn = EXTRACT_FIELD(lo, 13, 31); 121 122 printf ("TLB: [%d][%d] hi=%x lo=%x v=%x p=%x\n", 123 set, idx, hi, lo, tlb_vpn, tlb_pfn); 124 } 125 } 126 } 127 #endif 128 129 /* rw 0 = read, 1 = write, 2 = exec. */ 130 static int cris_mmu_translate_page(struct cris_mmu_result *res, 131 CPUCRISState *env, uint32_t vaddr, 132 int rw, int usermode, int debug) 133 { 134 unsigned int vpage; 135 unsigned int idx; 136 uint32_t pid, lo, hi; 137 uint32_t tlb_vpn, tlb_pfn = 0; 138 int tlb_pid, tlb_g, tlb_v, tlb_k, tlb_w, tlb_x; 139 int cfg_v, cfg_k, cfg_w, cfg_x; 140 int set, match = 0; 141 uint32_t r_cause; 142 uint32_t r_cfg; 143 int rwcause; 144 int mmu = 1; /* Data mmu is default. */ 145 int vect_base; 146 147 r_cause = env->sregs[SFR_R_MM_CAUSE]; 148 r_cfg = env->sregs[SFR_RW_MM_CFG]; 149 pid = env->pregs[PR_PID] & 0xff; 150 151 switch (rw) { 152 case 2: rwcause = CRIS_MMU_ERR_EXEC; mmu = 0; break; 153 case 1: rwcause = CRIS_MMU_ERR_WRITE; break; 154 default: 155 case 0: rwcause = CRIS_MMU_ERR_READ; break; 156 } 157 158 /* I exception vectors 4 - 7, D 8 - 11. */ 159 vect_base = (mmu + 1) * 4; 160 161 vpage = vaddr >> 13; 162 163 /* We know the index which to check on each set. 164 Scan both I and D. */ 165 #if 0 166 for (set = 0; set < 4; set++) { 167 for (idx = 0; idx < 16; idx++) { 168 lo = env->tlbsets[mmu][set][idx].lo; 169 hi = env->tlbsets[mmu][set][idx].hi; 170 tlb_vpn = EXTRACT_FIELD(hi, 13, 31); 171 tlb_pfn = EXTRACT_FIELD(lo, 13, 31); 172 173 printf ("TLB: [%d][%d] hi=%x lo=%x v=%x p=%x\n", 174 set, idx, hi, lo, tlb_vpn, tlb_pfn); 175 } 176 } 177 #endif 178 179 idx = vpage & 15; 180 for (set = 0; set < 4; set++) 181 { 182 lo = env->tlbsets[mmu][set][idx].lo; 183 hi = env->tlbsets[mmu][set][idx].hi; 184 185 tlb_vpn = hi >> 13; 186 tlb_pid = EXTRACT_FIELD(hi, 0, 7); 187 tlb_g = EXTRACT_FIELD(lo, 4, 4); 188 189 D_LOG("TLB[%d][%d][%d] v=%x vpage=%x lo=%x hi=%x\n", 190 mmu, set, idx, tlb_vpn, vpage, lo, hi); 191 if ((tlb_g || (tlb_pid == pid)) 192 && tlb_vpn == vpage) { 193 match = 1; 194 break; 195 } 196 } 197 198 res->bf_vec = vect_base; 199 if (match) { 200 cfg_w = EXTRACT_FIELD(r_cfg, 19, 19); 201 cfg_k = EXTRACT_FIELD(r_cfg, 18, 18); 202 cfg_x = EXTRACT_FIELD(r_cfg, 17, 17); 203 cfg_v = EXTRACT_FIELD(r_cfg, 16, 16); 204 205 tlb_pfn = EXTRACT_FIELD(lo, 13, 31); 206 tlb_v = EXTRACT_FIELD(lo, 3, 3); 207 tlb_k = EXTRACT_FIELD(lo, 2, 2); 208 tlb_w = EXTRACT_FIELD(lo, 1, 1); 209 tlb_x = EXTRACT_FIELD(lo, 0, 0); 210 211 /* 212 set_exception_vector(0x04, i_mmu_refill); 213 set_exception_vector(0x05, i_mmu_invalid); 214 set_exception_vector(0x06, i_mmu_access); 215 set_exception_vector(0x07, i_mmu_execute); 216 set_exception_vector(0x08, d_mmu_refill); 217 set_exception_vector(0x09, d_mmu_invalid); 218 set_exception_vector(0x0a, d_mmu_access); 219 set_exception_vector(0x0b, d_mmu_write); 220 */ 221 if (cfg_k && tlb_k && usermode) { 222 D(printf ("tlb: kernel protected %x lo=%x pc=%x\n", 223 vaddr, lo, env->pc)); 224 match = 0; 225 res->bf_vec = vect_base + 2; 226 } else if (rw == 1 && cfg_w && !tlb_w) { 227 D(printf ("tlb: write protected %x lo=%x pc=%x\n", 228 vaddr, lo, env->pc)); 229 match = 0; 230 /* write accesses never go through the I mmu. */ 231 res->bf_vec = vect_base + 3; 232 } else if (rw == 2 && cfg_x && !tlb_x) { 233 D(printf ("tlb: exec protected %x lo=%x pc=%x\n", 234 vaddr, lo, env->pc)); 235 match = 0; 236 res->bf_vec = vect_base + 3; 237 } else if (cfg_v && !tlb_v) { 238 D(printf ("tlb: invalid %x\n", vaddr)); 239 match = 0; 240 res->bf_vec = vect_base + 1; 241 } 242 243 res->prot = 0; 244 if (match) { 245 res->prot |= PAGE_READ; 246 if (tlb_w) 247 res->prot |= PAGE_WRITE; 248 if (mmu == 0 && (cfg_x || tlb_x)) 249 res->prot |= PAGE_EXEC; 250 } 251 else 252 D(dump_tlb(env, mmu)); 253 } else { 254 /* If refill, provide a randomized set. */ 255 set = env->mmu_rand_lfsr & 3; 256 } 257 258 if (!match && !debug) { 259 cris_mmu_update_rand_lfsr(env); 260 261 /* Compute index. */ 262 idx = vpage & 15; 263 264 /* Update RW_MM_TLB_SEL. */ 265 env->sregs[SFR_RW_MM_TLB_SEL] = 0; 266 set_field(&env->sregs[SFR_RW_MM_TLB_SEL], idx, 0, 4); 267 set_field(&env->sregs[SFR_RW_MM_TLB_SEL], set, 4, 2); 268 269 /* Update RW_MM_CAUSE. */ 270 set_field(&r_cause, rwcause, 8, 2); 271 set_field(&r_cause, vpage, 13, 19); 272 set_field(&r_cause, pid, 0, 8); 273 env->sregs[SFR_R_MM_CAUSE] = r_cause; 274 D(printf("refill vaddr=%x pc=%x\n", vaddr, env->pc)); 275 } 276 277 D(printf ("%s rw=%d mtch=%d pc=%x va=%x vpn=%x tlbvpn=%x pfn=%x pid=%x" 278 " %x cause=%x sel=%x sp=%x %x %x\n", 279 __func__, rw, match, env->pc, 280 vaddr, vpage, 281 tlb_vpn, tlb_pfn, tlb_pid, 282 pid, 283 r_cause, 284 env->sregs[SFR_RW_MM_TLB_SEL], 285 env->regs[R_SP], env->pregs[PR_USP], env->ksp)); 286 287 res->phy = tlb_pfn << TARGET_PAGE_BITS; 288 return !match; 289 } 290 291 void cris_mmu_flush_pid(CPUCRISState *env, uint32_t pid) 292 { 293 CRISCPU *cpu = cris_env_get_cpu(env); 294 target_ulong vaddr; 295 unsigned int idx; 296 uint32_t lo, hi; 297 uint32_t tlb_vpn; 298 int tlb_pid, tlb_g, tlb_v; 299 unsigned int set; 300 unsigned int mmu; 301 302 pid &= 0xff; 303 for (mmu = 0; mmu < 2; mmu++) { 304 for (set = 0; set < 4; set++) 305 { 306 for (idx = 0; idx < 16; idx++) { 307 lo = env->tlbsets[mmu][set][idx].lo; 308 hi = env->tlbsets[mmu][set][idx].hi; 309 310 tlb_vpn = EXTRACT_FIELD(hi, 13, 31); 311 tlb_pid = EXTRACT_FIELD(hi, 0, 7); 312 tlb_g = EXTRACT_FIELD(lo, 4, 4); 313 tlb_v = EXTRACT_FIELD(lo, 3, 3); 314 315 if (tlb_v && !tlb_g && (tlb_pid == pid)) { 316 vaddr = tlb_vpn << TARGET_PAGE_BITS; 317 D_LOG("flush pid=%x vaddr=%x\n", 318 pid, vaddr); 319 tlb_flush_page(CPU(cpu), vaddr); 320 } 321 } 322 } 323 } 324 } 325 326 int cris_mmu_translate(struct cris_mmu_result *res, 327 CPUCRISState *env, uint32_t vaddr, 328 int rw, int mmu_idx, int debug) 329 { 330 int seg; 331 int miss = 0; 332 int is_user = mmu_idx == MMU_USER_IDX; 333 uint32_t old_srs; 334 335 old_srs= env->pregs[PR_SRS]; 336 337 /* rw == 2 means exec, map the access to the insn mmu. */ 338 env->pregs[PR_SRS] = rw == 2 ? 1 : 2; 339 340 if (!cris_mmu_enabled(env->sregs[SFR_RW_GC_CFG])) { 341 res->phy = vaddr; 342 res->prot = PAGE_BITS; 343 goto done; 344 } 345 346 seg = vaddr >> 28; 347 if (!is_user && cris_mmu_segmented_addr(seg, env->sregs[SFR_RW_MM_CFG])) 348 { 349 uint32_t base; 350 351 miss = 0; 352 base = cris_mmu_translate_seg(env, seg); 353 res->phy = base | (0x0fffffff & vaddr); 354 res->prot = PAGE_BITS; 355 } else { 356 miss = cris_mmu_translate_page(res, env, vaddr, rw, 357 is_user, debug); 358 } 359 done: 360 env->pregs[PR_SRS] = old_srs; 361 return miss; 362 } 363