|
|
1.1 root 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;
1.1.1.3 ! root 63: uint32_t t;
1.1 root 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. */
79: unsigned 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;
1.1.1.2 root 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:
1.1 root 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;
1.1.1.2 root 150: default: break;
1.1 root 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: }
176: done:
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. */
183: uint32_t mmu_read(CPUState *env, uint32_t rn)
184: {
185: unsigned int i;
186: uint32_t r;
187:
1.1.1.2 root 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:
1.1 root 193: switch (rn) {
194: /* Reads to HI/LO trig reads from the mmu rams. */
195: case MMU_R_TLBLO:
196: case MMU_R_TLBHI:
1.1.1.2 root 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:
1.1 root 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;
1.1.1.2 root 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;
1.1 root 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:
223: void 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:
1.1.1.2 root 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:
1.1 root 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:
1.1.1.2 root 250: if (env->mmu.c_mmu_tlb_access <= 1) {
251: qemu_log("Invalid access to MMU reg %d\n", rn);
252: return;
253: }
254:
1.1 root 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:
1.1.1.2 root 263: if (env->mmu.c_mmu_tlb_access <= 1) {
264: qemu_log("Invalid access to MMU reg %d\n", rn);
265: return;
266: }
267:
1.1 root 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;
1.1.1.2 root 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:
1.1 root 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:
297: void mmu_init(struct microblaze_mmu *mmu)
298: {
1.1.1.2 root 299: int i;
300: for (i = 0; i < ARRAY_SIZE(mmu->regs); i++) {
301: mmu->regs[i] = 0;
302: }
1.1 root 303: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.