Commit afeeceb0d79be886e83b56fb91cc6a2ca99888be

Authored by Edgar E. Iglesias
1 parent e90e390c

microblaze: Add MMU emulation.

Signed-off-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
target-microblaze/mmu.c 0 → 100644
  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, write to the Free Software
  18 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA
  19 + */
  20 +#include <stdio.h>
  21 +#include <stdlib.h>
  22 +#include <assert.h>
  23 +
  24 +#include "config.h"
  25 +#include "cpu.h"
  26 +#include "exec-all.h"
  27 +
  28 +#define D(x)
  29 +
  30 +static unsigned int tlb_decode_size(unsigned int f)
  31 +{
  32 + static const unsigned int sizes[] = {
  33 + 1 * 1024, 4 * 1024, 16 * 1024, 64 * 1024, 256 * 1024,
  34 + 1 * 1024 * 1024, 4 * 1024 * 1024, 16 * 1024 * 1024
  35 + };
  36 + assert(f < ARRAY_SIZE(sizes));
  37 + return sizes[f];
  38 +}
  39 +
  40 +static void mmu_flush_idx(CPUState *env, unsigned idx)
  41 +{
  42 + struct microblaze_mmu *mmu = &env->mmu;
  43 + unsigned int tlb_size;
  44 + uint32_t tlb_tag, end, t;
  45 +
  46 + t = mmu->rams[RAM_TAG][idx];
  47 + if (!(t & TLB_VALID))
  48 + return;
  49 +
  50 + tlb_tag = t & TLB_EPN_MASK;
  51 + tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7);
  52 + end = tlb_tag + tlb_size;
  53 +
  54 + while (tlb_tag < end) {
  55 + tlb_flush_page(env, tlb_tag);
  56 + tlb_tag += TARGET_PAGE_SIZE;
  57 + }
  58 +}
  59 +
  60 +static void mmu_change_pid(CPUState *env, unsigned int newpid)
  61 +{
  62 + struct microblaze_mmu *mmu = &env->mmu;
  63 + unsigned int i;
  64 + unsigned int tlb_size;
  65 + uint32_t tlb_tag, mask, t;
  66 +
  67 + if (newpid & ~0xff)
  68 + qemu_log("Illegal rpid=%x\n", newpid);
  69 +
  70 + for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) {
  71 + /* Lookup and decode. */
  72 + t = mmu->rams[RAM_TAG][i];
  73 + if (t & TLB_VALID) {
  74 + tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7);
  75 + mask = ~(tlb_size - 1);
  76 +
  77 + tlb_tag = t & TLB_EPN_MASK;
  78 + if (mmu->tids[i] && ((mmu->regs[MMU_R_PID] & 0xff) == mmu->tids[i]))
  79 + mmu_flush_idx(env, i);
  80 + }
  81 + }
  82 +}
  83 +
  84 +/* rw - 0 = read, 1 = write, 2 = fetch. */
  85 +unsigned int mmu_translate(struct microblaze_mmu *mmu,
  86 + struct microblaze_mmu_lookup *lu,
  87 + target_ulong vaddr, int rw, int mmu_idx)
  88 +{
  89 + unsigned int i, hit = 0;
  90 + unsigned int tlb_ex = 0, tlb_wr = 0, tlb_zsel;
  91 + unsigned int tlb_size;
  92 + uint32_t tlb_tag, tlb_rpn, mask, t0;
  93 +
  94 + lu->err = ERR_MISS;
  95 + for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) {
  96 + uint32_t t, d;
  97 +
  98 + /* Lookup and decode. */
  99 + t = mmu->rams[RAM_TAG][i];
  100 + D(qemu_log("TLB %d valid=%d\n", i, t & TLB_VALID));
  101 + if (t & TLB_VALID) {
  102 + tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7);
  103 + if (tlb_size < TARGET_PAGE_SIZE) {
  104 + qemu_log("%d pages not supported\n", tlb_size);
  105 + abort();
  106 + }
  107 +
  108 + mask = ~(tlb_size - 1);
  109 + tlb_tag = t & TLB_EPN_MASK;
  110 + if ((vaddr & mask) != (tlb_tag & mask)) {
  111 + D(qemu_log("TLB %d vaddr=%x != tag=%x\n",
  112 + i, vaddr & mask, tlb_tag & mask));
  113 + continue;
  114 + }
  115 + if (mmu->tids[i]
  116 + && ((mmu->regs[MMU_R_PID] & 0xff) != mmu->tids[i])) {
  117 + D(qemu_log("TLB %d pid=%x != tid=%x\n",
  118 + i, mmu->regs[MMU_R_PID], mmu->tids[i]));
  119 + continue;
  120 + }
  121 +
  122 + /* Bring in the data part. */
  123 + d = mmu->rams[RAM_DATA][i];
  124 + tlb_ex = d & TLB_EX;
  125 + tlb_wr = d & TLB_WR;
  126 +
  127 + /* Now lets see if there is a zone that overrides the protbits. */
  128 + tlb_zsel = (d >> 4) & 0xf;
  129 + t0 = mmu->regs[MMU_R_ZPR] >> (30 - (tlb_zsel * 2));
  130 + t0 &= 0x3;
  131 + switch (t0) {
  132 + case 0:
  133 + if (mmu_idx == MMU_USER_IDX)
  134 + continue;
  135 + break;
  136 + case 2:
  137 + if (mmu_idx != MMU_USER_IDX) {
  138 + tlb_ex = 1;
  139 + tlb_wr = 1;
  140 + }
  141 + break;
  142 + case 3:
  143 + tlb_ex = 1;
  144 + tlb_wr = 1;
  145 + break;
  146 + }
  147 +
  148 +
  149 + lu->err = ERR_PROT;
  150 + lu->prot = PAGE_READ;
  151 + if (tlb_wr)
  152 + lu->prot |= PAGE_WRITE;
  153 + else if (rw == 1)
  154 + goto done;
  155 + if (tlb_ex)
  156 + lu->prot |=PAGE_EXEC;
  157 + else if (rw == 2) {
  158 + goto done;
  159 + }
  160 +
  161 + tlb_rpn = d & TLB_RPN_MASK;
  162 +
  163 + lu->vaddr = tlb_tag;
  164 + lu->paddr = tlb_rpn;
  165 + lu->size = tlb_size;
  166 + lu->err = ERR_HIT;
  167 + lu->idx = i;
  168 + hit = 1;
  169 + goto done;
  170 + }
  171 + }
  172 +done:
  173 + D(qemu_log("MMU vaddr=%x rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n",
  174 + vaddr, rw, tlb_wr, tlb_ex, hit));
  175 + return hit;
  176 +}
  177 +
  178 +/* Writes/reads to the MMU's special regs end up here. */
  179 +uint32_t mmu_read(CPUState *env, uint32_t rn)
  180 +{
  181 + unsigned int i;
  182 + uint32_t r;
  183 +
  184 + switch (rn) {
  185 + /* Reads to HI/LO trig reads from the mmu rams. */
  186 + case MMU_R_TLBLO:
  187 + case MMU_R_TLBHI:
  188 + i = env->mmu.regs[MMU_R_TLBX] & 0xff;
  189 + r = env->mmu.rams[rn & 1][i];
  190 + if (rn == MMU_R_TLBHI)
  191 + env->mmu.regs[MMU_R_PID] = env->mmu.tids[i];
  192 + break;
  193 + default:
  194 + r = env->mmu.regs[rn];
  195 + break;
  196 + }
  197 + D(qemu_log("%s rn=%d=%x\n", __func__, rn, r));
  198 + return r;
  199 +}
  200 +
  201 +void mmu_write(CPUState *env, uint32_t rn, uint32_t v)
  202 +{
  203 + unsigned int i;
  204 + D(qemu_log("%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn]));
  205 +
  206 + switch (rn) {
  207 + /* Writes to HI/LO trig writes to the mmu rams. */
  208 + case MMU_R_TLBLO:
  209 + case MMU_R_TLBHI:
  210 + i = env->mmu.regs[MMU_R_TLBX] & 0xff;
  211 + if (rn == MMU_R_TLBHI) {
  212 + if (i < 3 && !(v & TLB_VALID) && qemu_loglevel_mask(~0))
  213 + qemu_log("invalidating index %x at pc=%x\n",
  214 + i, env->sregs[SR_PC]);
  215 + env->mmu.tids[i] = env->mmu.regs[MMU_R_PID] & 0xff;
  216 + mmu_flush_idx(env, i);
  217 + }
  218 + env->mmu.rams[rn & 1][i] = v;
  219 +
  220 + D(qemu_log("%s ram[%d][%d]=%x\n", __func__, rn & 1, i, v));
  221 + break;
  222 + case MMU_R_ZPR:
  223 + case MMU_R_PID:
  224 + if (v != env->mmu.regs[rn]) {
  225 + mmu_change_pid(env, v);
  226 + env->mmu.regs[rn] = v;
  227 + }
  228 + break;
  229 + case MMU_R_TLBSX:
  230 + {
  231 + struct microblaze_mmu_lookup lu;
  232 + int hit;
  233 + hit = mmu_translate(&env->mmu, &lu,
  234 + v & TLB_EPN_MASK, 0, cpu_mmu_index(env));
  235 + if (hit) {
  236 + env->mmu.regs[MMU_R_TLBX] = lu.idx;
  237 + } else
  238 + env->mmu.regs[MMU_R_TLBX] |= 0x80000000;
  239 + break;
  240 + }
  241 + default:
  242 + env->mmu.regs[rn] = v;
  243 + break;
  244 + }
  245 +}
  246 +
  247 +void mmu_init(struct microblaze_mmu *mmu)
  248 +{
  249 + memset(mmu, 0, sizeof *mmu);
  250 +}
... ...
target-microblaze/mmu.h 0 → 100644
  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, write to the Free Software
  18 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA
  19 + */
  20 +
  21 +#define MMU_R_PID 0
  22 +#define MMU_R_ZPR 1
  23 +#define MMU_R_TLBX 2
  24 +#define MMU_R_TLBLO 3
  25 +#define MMU_R_TLBHI 4
  26 +#define MMU_R_TLBSX 5
  27 +
  28 +#define RAM_DATA 1
  29 +#define RAM_TAG 0
  30 +
  31 +/* Tag portion */
  32 +#define TLB_EPN_MASK 0xFFFFFC00 /* Effective Page Number */
  33 +#define TLB_PAGESZ_MASK 0x00000380
  34 +#define TLB_PAGESZ(x) (((x) & 0x7) << 7)
  35 +#define PAGESZ_1K 0
  36 +#define PAGESZ_4K 1
  37 +#define PAGESZ_16K 2
  38 +#define PAGESZ_64K 3
  39 +#define PAGESZ_256K 4
  40 +#define PAGESZ_1M 5
  41 +#define PAGESZ_4M 6
  42 +#define PAGESZ_16M 7
  43 +#define TLB_VALID 0x00000040 /* Entry is valid */
  44 +
  45 +/* Data portion */
  46 +#define TLB_RPN_MASK 0xFFFFFC00 /* Real Page Number */
  47 +#define TLB_PERM_MASK 0x00000300
  48 +#define TLB_EX 0x00000200 /* Instruction execution allowed */
  49 +#define TLB_WR 0x00000100 /* Writes permitted */
  50 +#define TLB_ZSEL_MASK 0x000000F0
  51 +#define TLB_ZSEL(x) (((x) & 0xF) << 4)
  52 +#define TLB_ATTR_MASK 0x0000000F
  53 +#define TLB_W 0x00000008 /* Caching is write-through */
  54 +#define TLB_I 0x00000004 /* Caching is inhibited */
  55 +#define TLB_M 0x00000002 /* Memory is coherent */
  56 +#define TLB_G 0x00000001 /* Memory is guarded from prefetch */
  57 +
  58 +#define TLB_ENTRIES 64
  59 +
  60 +struct microblaze_mmu
  61 +{
  62 + /* Data and tag brams. */
  63 + uint32_t rams[2][TLB_ENTRIES];
  64 + /* We keep a separate ram for the tids to avoid the 48 bit tag width. */
  65 + uint8_t tids[TLB_ENTRIES];
  66 + /* Control flops. */
  67 + uint32_t regs[8];;
  68 +};
  69 +
  70 +struct microblaze_mmu_lookup
  71 +{
  72 + uint32_t paddr;
  73 + uint32_t vaddr;
  74 + unsigned int size;
  75 + unsigned int idx;
  76 + int prot;
  77 + enum {
  78 + ERR_PROT, ERR_MISS, ERR_HIT
  79 + } err;
  80 +};
  81 +
  82 +void mmu_flip_um(CPUState *env, unsigned int um);
  83 +unsigned int mmu_translate(struct microblaze_mmu *mmu,
  84 + struct microblaze_mmu_lookup *lu,
  85 + target_ulong vaddr, int rw, int mmu_idx);
  86 +uint32_t mmu_read(CPUState *env, uint32_t rn);
  87 +void mmu_write(CPUState *env, uint32_t rn, uint32_t v);
  88 +void mmu_init(struct microblaze_mmu *mmu);
... ...