72d318384a20e483f47c75e7df9d7cc9be1fd808
[qemu] / target-microblaze / mmu.c
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 int 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 }