File:  [Qemu by Fabrice Bellard] / qemu / target-microblaze / mmu.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:23:14 2018 UTC (23 months, 2 weeks ago) by root
Branches: qemu, MAIN
CVS tags: qemu0111, qemu0110, HEAD
qemu 0.11.0

    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: }

unix.superglobalmegacorp.com