~ahs3/+junk/cq-qemu

« back to all changes in this revision

Viewing changes to target-cris/mmu.c

  • Committer: Al Stone
  • Date: 2012-02-09 01:17:20 UTC
  • Revision ID: albert.stone@canonical.com-20120209011720-tztl7ik3qayz80p4
first commit to bzr for qemu

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  CRIS mmu emulation.
 
3
 *
 
4
 *  Copyright (c) 2007 AXIS Communications AB
 
5
 *  Written by Edgar E. Iglesias.
 
6
 *
 
7
 * This library is free software; you can redistribute it and/or
 
8
 * modify it under the terms of the GNU Lesser General Public
 
9
 * License as published by the Free Software Foundation; either
 
10
 * version 2 of the License, or (at your option) any later version.
 
11
 *
 
12
 * This library is distributed in the hope that it will be useful,
 
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
15
 * Lesser General Public License for more details.
 
16
 *
 
17
 * You should have received a copy of the GNU Lesser General Public
 
18
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 
19
 */
 
20
 
 
21
#ifndef CONFIG_USER_ONLY
 
22
 
 
23
#include <stdio.h>
 
24
#include <string.h>
 
25
#include <stdlib.h>
 
26
 
 
27
#include "config.h"
 
28
#include "cpu.h"
 
29
#include "mmu.h"
 
30
 
 
31
#ifdef DEBUG
 
32
#define D(x) x
 
33
#define D_LOG(...) qemu_log(__VA_ARGS__)
 
34
#else
 
35
#define D(x) do { } while (0)
 
36
#define D_LOG(...) do { } while (0)
 
37
#endif
 
38
 
 
39
void cris_mmu_init(CPUState *env)
 
40
{
 
41
        env->mmu_rand_lfsr = 0xcccc;
 
42
}
 
43
 
 
44
#define SR_POLYNOM 0x8805
 
45
static inline unsigned int compute_polynom(unsigned int sr)
 
46
{
 
47
        unsigned int i;
 
48
        unsigned int f;
 
49
 
 
50
        f = 0;
 
51
        for (i = 0; i < 16; i++)
 
52
                f += ((SR_POLYNOM >> i) & 1) & ((sr >> i) & 1);
 
53
 
 
54
        return f;
 
55
}
 
56
 
 
57
static void cris_mmu_update_rand_lfsr(CPUState *env)
 
58
{
 
59
        unsigned int f;
 
60
 
 
61
        /* Update lfsr at every fault.  */
 
62
        f = compute_polynom(env->mmu_rand_lfsr);
 
63
        env->mmu_rand_lfsr >>= 1;
 
64
        env->mmu_rand_lfsr |= (f << 15);
 
65
        env->mmu_rand_lfsr &= 0xffff;
 
66
}
 
67
 
 
68
static inline int cris_mmu_enabled(uint32_t rw_gc_cfg)
 
69
{
 
70
        return (rw_gc_cfg & 12) != 0;
 
71
}
 
72
 
 
73
static inline int cris_mmu_segmented_addr(int seg, uint32_t rw_mm_cfg)
 
74
{
 
75
        return (1 << seg) & rw_mm_cfg;
 
76
}
 
77
 
 
78
static uint32_t cris_mmu_translate_seg(CPUState *env, int seg)
 
79
{
 
80
        uint32_t base;
 
81
        int i;
 
82
 
 
83
        if (seg < 8)
 
84
                base = env->sregs[SFR_RW_MM_KBASE_LO];
 
85
        else
 
86
                base = env->sregs[SFR_RW_MM_KBASE_HI];
 
87
 
 
88
        i = seg & 7;
 
89
        base >>= i * 4;
 
90
        base &= 15;
 
91
 
 
92
        base <<= 28;
 
93
        return base;
 
94
}
 
95
/* Used by the tlb decoder.  */
 
96
#define EXTRACT_FIELD(src, start, end) \
 
97
            (((src) >> start) & ((1 << (end - start + 1)) - 1))
 
98
 
 
99
static inline void set_field(uint32_t *dst, unsigned int val, 
 
100
                             unsigned int offset, unsigned int width)
 
101
{
 
102
        uint32_t mask;
 
103
 
 
104
        mask = (1 << width) - 1;
 
105
        mask <<= offset;
 
106
        val <<= offset;
 
107
 
 
108
        val &= mask;
 
109
        *dst &= ~(mask);
 
110
        *dst |= val;
 
111
}
 
112
 
 
113
#ifdef DEBUG
 
114
static void dump_tlb(CPUState *env, int mmu)
 
115
{
 
116
        int set;
 
117
        int idx;
 
118
        uint32_t hi, lo, tlb_vpn, tlb_pfn;
 
119
 
 
120
        for (set = 0; set < 4; set++) {
 
121
                for (idx = 0; idx < 16; idx++) {
 
122
                        lo = env->tlbsets[mmu][set][idx].lo;
 
123
                        hi = env->tlbsets[mmu][set][idx].hi;
 
124
                        tlb_vpn = EXTRACT_FIELD(hi, 13, 31);
 
125
                        tlb_pfn = EXTRACT_FIELD(lo, 13, 31);
 
126
 
 
127
                        printf ("TLB: [%d][%d] hi=%x lo=%x v=%x p=%x\n", 
 
128
                                        set, idx, hi, lo, tlb_vpn, tlb_pfn);
 
129
                }
 
130
        }
 
131
}
 
132
#endif
 
133
 
 
134
/* rw 0 = read, 1 = write, 2 = exec.  */
 
135
static int cris_mmu_translate_page(struct cris_mmu_result *res,
 
136
                                   CPUState *env, uint32_t vaddr,
 
137
                                   int rw, int usermode, int debug)
 
138
{
 
139
        unsigned int vpage;
 
140
        unsigned int idx;
 
141
        uint32_t pid, lo, hi;
 
142
        uint32_t tlb_vpn, tlb_pfn = 0;
 
143
        int tlb_pid, tlb_g, tlb_v, tlb_k, tlb_w, tlb_x;
 
144
        int cfg_v, cfg_k, cfg_w, cfg_x; 
 
145
        int set, match = 0;
 
146
        uint32_t r_cause;
 
147
        uint32_t r_cfg;
 
148
        int rwcause;
 
149
        int mmu = 1; /* Data mmu is default.  */
 
150
        int vect_base;
 
151
 
 
152
        r_cause = env->sregs[SFR_R_MM_CAUSE];
 
153
        r_cfg = env->sregs[SFR_RW_MM_CFG];
 
154
        pid = env->pregs[PR_PID] & 0xff;
 
155
 
 
156
        switch (rw) {
 
157
                case 2: rwcause = CRIS_MMU_ERR_EXEC; mmu = 0; break;
 
158
                case 1: rwcause = CRIS_MMU_ERR_WRITE; break;
 
159
                default:
 
160
                case 0: rwcause = CRIS_MMU_ERR_READ; break;
 
161
        }
 
162
 
 
163
        /* I exception vectors 4 - 7, D 8 - 11.  */
 
164
        vect_base = (mmu + 1) * 4;
 
165
 
 
166
        vpage = vaddr >> 13;
 
167
 
 
168
        /* We know the index which to check on each set.
 
169
           Scan both I and D.  */
 
170
#if 0
 
171
        for (set = 0; set < 4; set++) {
 
172
                for (idx = 0; idx < 16; idx++) {
 
173
                        lo = env->tlbsets[mmu][set][idx].lo;
 
174
                        hi = env->tlbsets[mmu][set][idx].hi;
 
175
                        tlb_vpn = EXTRACT_FIELD(hi, 13, 31);
 
176
                        tlb_pfn = EXTRACT_FIELD(lo, 13, 31);
 
177
 
 
178
                        printf ("TLB: [%d][%d] hi=%x lo=%x v=%x p=%x\n", 
 
179
                                        set, idx, hi, lo, tlb_vpn, tlb_pfn);
 
180
                }
 
181
        }
 
182
#endif
 
183
 
 
184
        idx = vpage & 15;
 
185
        for (set = 0; set < 4; set++)
 
186
        {
 
187
                lo = env->tlbsets[mmu][set][idx].lo;
 
188
                hi = env->tlbsets[mmu][set][idx].hi;
 
189
 
 
190
                tlb_vpn = hi >> 13;
 
191
                tlb_pid = EXTRACT_FIELD(hi, 0, 7);
 
192
                tlb_g  = EXTRACT_FIELD(lo, 4, 4);
 
193
 
 
194
                D_LOG("TLB[%d][%d][%d] v=%x vpage=%x lo=%x hi=%x\n", 
 
195
                         mmu, set, idx, tlb_vpn, vpage, lo, hi);
 
196
                if ((tlb_g || (tlb_pid == pid))
 
197
                    && tlb_vpn == vpage) {
 
198
                        match = 1;
 
199
                        break;
 
200
                }
 
201
        }
 
202
 
 
203
        res->bf_vec = vect_base;
 
204
        if (match) {
 
205
                cfg_w  = EXTRACT_FIELD(r_cfg, 19, 19);
 
206
                cfg_k  = EXTRACT_FIELD(r_cfg, 18, 18);
 
207
                cfg_x  = EXTRACT_FIELD(r_cfg, 17, 17);
 
208
                cfg_v  = EXTRACT_FIELD(r_cfg, 16, 16);
 
209
 
 
210
                tlb_pfn = EXTRACT_FIELD(lo, 13, 31);
 
211
                tlb_v = EXTRACT_FIELD(lo, 3, 3);
 
212
                tlb_k = EXTRACT_FIELD(lo, 2, 2);
 
213
                tlb_w = EXTRACT_FIELD(lo, 1, 1);
 
214
                tlb_x = EXTRACT_FIELD(lo, 0, 0);
 
215
 
 
216
                /*
 
217
                set_exception_vector(0x04, i_mmu_refill);
 
218
                set_exception_vector(0x05, i_mmu_invalid);
 
219
                set_exception_vector(0x06, i_mmu_access);
 
220
                set_exception_vector(0x07, i_mmu_execute);
 
221
                set_exception_vector(0x08, d_mmu_refill);
 
222
                set_exception_vector(0x09, d_mmu_invalid);
 
223
                set_exception_vector(0x0a, d_mmu_access);
 
224
                set_exception_vector(0x0b, d_mmu_write);
 
225
                */
 
226
                if (cfg_k && tlb_k && usermode) {
 
227
                        D(printf ("tlb: kernel protected %x lo=%x pc=%x\n", 
 
228
                                  vaddr, lo, env->pc));
 
229
                        match = 0;
 
230
                        res->bf_vec = vect_base + 2;
 
231
                } else if (rw == 1 && cfg_w && !tlb_w) {
 
232
                        D(printf ("tlb: write protected %x lo=%x pc=%x\n", 
 
233
                                  vaddr, lo, env->pc));
 
234
                        match = 0;
 
235
                        /* write accesses never go through the I mmu.  */
 
236
                        res->bf_vec = vect_base + 3;
 
237
                } else if (rw == 2 && cfg_x && !tlb_x) {
 
238
                        D(printf ("tlb: exec protected %x lo=%x pc=%x\n", 
 
239
                                 vaddr, lo, env->pc));
 
240
                        match = 0;
 
241
                        res->bf_vec = vect_base + 3;
 
242
                } else if (cfg_v && !tlb_v) {
 
243
                        D(printf ("tlb: invalid %x\n", vaddr));
 
244
                        match = 0;
 
245
                        res->bf_vec = vect_base + 1;
 
246
                }
 
247
 
 
248
                res->prot = 0;
 
249
                if (match) {
 
250
                        res->prot |= PAGE_READ;
 
251
                        if (tlb_w)
 
252
                                res->prot |= PAGE_WRITE;
 
253
                        if (mmu == 0 && (cfg_x || tlb_x))
 
254
                                res->prot |= PAGE_EXEC;
 
255
                }
 
256
                else
 
257
                        D(dump_tlb(env, mmu));
 
258
        } else {
 
259
                /* If refill, provide a randomized set.  */
 
260
                set = env->mmu_rand_lfsr & 3;
 
261
        }
 
262
 
 
263
        if (!match && !debug) {
 
264
                cris_mmu_update_rand_lfsr(env);
 
265
 
 
266
                /* Compute index.  */
 
267
                idx = vpage & 15;
 
268
 
 
269
                /* Update RW_MM_TLB_SEL.  */
 
270
                env->sregs[SFR_RW_MM_TLB_SEL] = 0;
 
271
                set_field(&env->sregs[SFR_RW_MM_TLB_SEL], idx, 0, 4);
 
272
                set_field(&env->sregs[SFR_RW_MM_TLB_SEL], set, 4, 2);
 
273
 
 
274
                /* Update RW_MM_CAUSE.  */
 
275
                set_field(&r_cause, rwcause, 8, 2);
 
276
                set_field(&r_cause, vpage, 13, 19);
 
277
                set_field(&r_cause, pid, 0, 8);
 
278
                env->sregs[SFR_R_MM_CAUSE] = r_cause;
 
279
                D(printf("refill vaddr=%x pc=%x\n", vaddr, env->pc));
 
280
        }
 
281
 
 
282
        D(printf ("%s rw=%d mtch=%d pc=%x va=%x vpn=%x tlbvpn=%x pfn=%x pid=%x"
 
283
                  " %x cause=%x sel=%x sp=%x %x %x\n",
 
284
                  __func__, rw, match, env->pc,
 
285
                  vaddr, vpage,
 
286
                  tlb_vpn, tlb_pfn, tlb_pid, 
 
287
                  pid,
 
288
                  r_cause,
 
289
                  env->sregs[SFR_RW_MM_TLB_SEL],
 
290
                  env->regs[R_SP], env->pregs[PR_USP], env->ksp));
 
291
 
 
292
        res->phy = tlb_pfn << TARGET_PAGE_BITS;
 
293
        return !match;
 
294
}
 
295
 
 
296
void cris_mmu_flush_pid(CPUState *env, uint32_t pid)
 
297
{
 
298
        target_ulong vaddr;
 
299
        unsigned int idx;
 
300
        uint32_t lo, hi;
 
301
        uint32_t tlb_vpn;
 
302
        int tlb_pid, tlb_g, tlb_v;
 
303
        unsigned int set;
 
304
        unsigned int mmu;
 
305
 
 
306
        pid &= 0xff;
 
307
        for (mmu = 0; mmu < 2; mmu++) {
 
308
                for (set = 0; set < 4; set++)
 
309
                {
 
310
                        for (idx = 0; idx < 16; idx++) {
 
311
                                lo = env->tlbsets[mmu][set][idx].lo;
 
312
                                hi = env->tlbsets[mmu][set][idx].hi;
 
313
                                
 
314
                                tlb_vpn = EXTRACT_FIELD(hi, 13, 31);
 
315
                                tlb_pid = EXTRACT_FIELD(hi, 0, 7);
 
316
                                tlb_g  = EXTRACT_FIELD(lo, 4, 4);
 
317
                                tlb_v = EXTRACT_FIELD(lo, 3, 3);
 
318
 
 
319
                                if (tlb_v && !tlb_g && (tlb_pid == pid)) {
 
320
                                        vaddr = tlb_vpn << TARGET_PAGE_BITS;
 
321
                                        D_LOG("flush pid=%x vaddr=%x\n", 
 
322
                                                  pid, vaddr);
 
323
                                        tlb_flush_page(env, vaddr);
 
324
                                }
 
325
                        }
 
326
                }
 
327
        }
 
328
}
 
329
 
 
330
int cris_mmu_translate(struct cris_mmu_result *res,
 
331
                       CPUState *env, uint32_t vaddr,
 
332
                       int rw, int mmu_idx, int debug)
 
333
{
 
334
        int seg;
 
335
        int miss = 0;
 
336
        int is_user = mmu_idx == MMU_USER_IDX;
 
337
        uint32_t old_srs;
 
338
 
 
339
        old_srs= env->pregs[PR_SRS];
 
340
 
 
341
        /* rw == 2 means exec, map the access to the insn mmu.  */
 
342
        env->pregs[PR_SRS] = rw == 2 ? 1 : 2;
 
343
 
 
344
        if (!cris_mmu_enabled(env->sregs[SFR_RW_GC_CFG])) {
 
345
                res->phy = vaddr;
 
346
                res->prot = PAGE_BITS;
 
347
                goto done;
 
348
        }
 
349
 
 
350
        seg = vaddr >> 28;
 
351
        if (!is_user && cris_mmu_segmented_addr(seg, env->sregs[SFR_RW_MM_CFG]))
 
352
        {
 
353
                uint32_t base;
 
354
 
 
355
                miss = 0;
 
356
                base = cris_mmu_translate_seg(env, seg);
 
357
                res->phy = base | (0x0fffffff & vaddr);
 
358
                res->prot = PAGE_BITS;
 
359
        } else {
 
360
                miss = cris_mmu_translate_page(res, env, vaddr, rw,
 
361
                                               is_user, debug);
 
362
        }
 
363
  done:
 
364
        env->pregs[PR_SRS] = old_srs;
 
365
        return miss;
 
366
}
 
367
#endif