qemu/target-microblaze/mmu.c
<<
>>
Prefs
   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
  29static 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
  39static 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
  59static void mmu_change_pid(CPUState *env, unsigned int newpid) 
  60{
  61    struct microblaze_mmu *mmu = &env->mmu;
  62    unsigned int i;
  63    uint32_t t;
  64
  65    if (newpid & ~0xff)
  66        qemu_log("Illegal rpid=%x\n", newpid);
  67
  68    for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) {
  69        /* Lookup and decode.  */
  70        t = mmu->rams[RAM_TAG][i];
  71        if (t & TLB_VALID) {
  72            if (mmu->tids[i] && ((mmu->regs[MMU_R_PID] & 0xff) == mmu->tids[i]))
  73                mmu_flush_idx(env, i);
  74        }
  75    }
  76}
  77
  78/* rw - 0 = read, 1 = write, 2 = fetch.  */
  79unsigned int mmu_translate(struct microblaze_mmu *mmu,
  80                           struct microblaze_mmu_lookup *lu,
  81                           target_ulong vaddr, int rw, int mmu_idx)
  82{
  83    unsigned int i, hit = 0;
  84    unsigned int tlb_ex = 0, tlb_wr = 0, tlb_zsel;
  85    unsigned int tlb_size;
  86    uint32_t tlb_tag, tlb_rpn, mask, t0;
  87
  88    lu->err = ERR_MISS;
  89    for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) {
  90        uint32_t t, d;
  91
  92        /* Lookup and decode.  */
  93        t = mmu->rams[RAM_TAG][i];
  94        D(qemu_log("TLB %d valid=%d\n", i, t & TLB_VALID));
  95        if (t & TLB_VALID) {
  96            tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7);
  97            if (tlb_size < TARGET_PAGE_SIZE) {
  98                qemu_log("%d pages not supported\n", tlb_size);
  99                abort();
 100            }
 101
 102            mask = ~(tlb_size - 1);
 103            tlb_tag = t & TLB_EPN_MASK;
 104            if ((vaddr & mask) != (tlb_tag & mask)) {
 105                D(qemu_log("TLB %d vaddr=%x != tag=%x\n",
 106                           i, vaddr & mask, tlb_tag & mask));
 107                continue;
 108            }
 109            if (mmu->tids[i]
 110                && ((mmu->regs[MMU_R_PID] & 0xff) != mmu->tids[i])) {
 111                D(qemu_log("TLB %d pid=%x != tid=%x\n",
 112                           i, mmu->regs[MMU_R_PID], mmu->tids[i]));
 113                continue;
 114            }
 115
 116            /* Bring in the data part.  */
 117            d = mmu->rams[RAM_DATA][i];
 118            tlb_ex = d & TLB_EX;
 119            tlb_wr = d & TLB_WR;
 120
 121            /* Now lets see if there is a zone that overrides the protbits.  */
 122            tlb_zsel = (d >> 4) & 0xf;
 123            t0 = mmu->regs[MMU_R_ZPR] >> (30 - (tlb_zsel * 2));
 124            t0 &= 0x3;
 125
 126            if (tlb_zsel > mmu->c_mmu_zones) {
 127                qemu_log("tlb zone select out of range! %d\n", tlb_zsel);
 128                t0 = 1; /* Ignore.  */
 129            }
 130
 131            if (mmu->c_mmu == 1) {
 132                t0 = 1; /* Zones are disabled.  */
 133            }
 134
 135            switch (t0) {
 136                case 0:
 137                    if (mmu_idx == MMU_USER_IDX)
 138                        continue;
 139                    break;
 140                case 2:
 141                    if (mmu_idx != MMU_USER_IDX) {
 142                        tlb_ex = 1;
 143                        tlb_wr = 1;
 144                    }
 145                    break;
 146                case 3:
 147                    tlb_ex = 1;
 148                    tlb_wr = 1;
 149                    break;
 150                default: break;
 151            }
 152
 153            lu->err = ERR_PROT;
 154            lu->prot = PAGE_READ;
 155            if (tlb_wr)
 156                lu->prot |= PAGE_WRITE;
 157            else if (rw == 1)
 158                goto done;
 159            if (tlb_ex)
 160                lu->prot |=PAGE_EXEC;
 161            else if (rw == 2) {
 162                goto done;
 163            }
 164
 165            tlb_rpn = d & TLB_RPN_MASK;
 166
 167            lu->vaddr = tlb_tag;
 168            lu->paddr = tlb_rpn;
 169            lu->size = tlb_size;
 170            lu->err = ERR_HIT;
 171            lu->idx = i;
 172            hit = 1;
 173            goto done;
 174        }
 175    }
 176done:
 177    D(qemu_log("MMU vaddr=%x rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n",
 178              vaddr, rw, tlb_wr, tlb_ex, hit));
 179    return hit;
 180}
 181
 182/* Writes/reads to the MMU's special regs end up here.  */
 183uint32_t mmu_read(CPUState *env, uint32_t rn)
 184{
 185    unsigned int i;
 186    uint32_t r;
 187
 188    if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) {
 189        qemu_log("MMU access on MMU-less system\n");
 190        return 0;
 191    }
 192
 193    switch (rn) {
 194        /* Reads to HI/LO trig reads from the mmu rams.  */
 195        case MMU_R_TLBLO:
 196        case MMU_R_TLBHI:
 197            if (!(env->mmu.c_mmu_tlb_access & 1)) {
 198                qemu_log("Invalid access to MMU reg %d\n", rn);
 199                return 0;
 200            }
 201
 202            i = env->mmu.regs[MMU_R_TLBX] & 0xff;
 203            r = env->mmu.rams[rn & 1][i];
 204            if (rn == MMU_R_TLBHI)
 205                env->mmu.regs[MMU_R_PID] = env->mmu.tids[i];
 206            break;
 207        case MMU_R_PID:
 208        case MMU_R_ZPR:
 209            if (!(env->mmu.c_mmu_tlb_access & 1)) {
 210                qemu_log("Invalid access to MMU reg %d\n", rn);
 211                return 0;
 212            }
 213            r = env->mmu.regs[rn];
 214            break;
 215        default:
 216            r = env->mmu.regs[rn];
 217            break;
 218    }
 219    D(qemu_log("%s rn=%d=%x\n", __func__, rn, r));
 220    return r;
 221}
 222
 223void mmu_write(CPUState *env, uint32_t rn, uint32_t v)
 224{
 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("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("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("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(env, 1);
 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("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("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));
 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
 297void 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