Update USB documentation.
[qemu] / target-sh4 / helper.c
1 /*
2  *  SH4 emulation
3  * 
4  *  Copyright (c) 2005 Samuel Tardieu
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <stdarg.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <inttypes.h>
25 #include <signal.h>
26 #include <assert.h>
27
28 #include "cpu.h"
29 #include "exec-all.h"
30
31 #define MMU_OK                   0
32 #define MMU_ITLB_MISS            (-1)
33 #define MMU_ITLB_MULTIPLE        (-2)
34 #define MMU_ITLB_VIOLATION       (-3)
35 #define MMU_DTLB_MISS_READ       (-4)
36 #define MMU_DTLB_MISS_WRITE      (-5)
37 #define MMU_DTLB_INITIAL_WRITE   (-6)
38 #define MMU_DTLB_VIOLATION_READ  (-7)
39 #define MMU_DTLB_VIOLATION_WRITE (-8)
40 #define MMU_DTLB_MULTIPLE        (-9)
41 #define MMU_DTLB_MISS            (-10)
42
43 void do_interrupt(CPUState * env)
44 {
45     if (loglevel & CPU_LOG_INT) {
46         const char *expname;
47         switch (env->exception_index) {
48         case 0x0e0:
49             expname = "addr_error";
50             break;
51         case 0x040:
52             expname = "tlb_miss";
53             break;
54         case 0x0a0:
55             expname = "tlb_violation";
56             break;
57         case 0x180:
58             expname = "illegal_instruction";
59             break;
60         case 0x1a0:
61             expname = "slot_illegal_instruction";
62             break;
63         case 0x800:
64             expname = "fpu_disable";
65             break;
66         case 0x820:
67             expname = "slot_fpu";
68             break;
69         case 0x100:
70             expname = "data_write";
71             break;
72         case 0x060:
73             expname = "dtlb_miss_write";
74             break;
75         case 0x0c0:
76             expname = "dtlb_violation_write";
77             break;
78         case 0x120:
79             expname = "fpu_exception";
80             break;
81         case 0x080:
82             expname = "initial_page_write";
83             break;
84         case 0x160:
85             expname = "trapa";
86             break;
87         default:
88             expname = "???";
89             break;
90         }
91         fprintf(logfile, "exception 0x%03x [%s] raised\n",
92                 env->exception_index, expname);
93         cpu_dump_state(env, logfile, fprintf, 0);
94     }
95
96     env->ssr = env->sr;
97     env->spc = env->spc;
98     env->sgr = env->gregs[15];
99     env->sr |= SR_BL | SR_MD | SR_RB;
100
101     env->expevt = env->exception_index & 0x7ff;
102     switch (env->exception_index) {
103     case 0x040:
104     case 0x060:
105     case 0x080:
106         env->pc = env->vbr + 0x400;
107         break;
108     case 0x140:
109         env->pc = 0xa0000000;
110         break;
111     default:
112         env->pc = env->vbr + 0x100;
113         break;
114     }
115 }
116
117 static void update_itlb_use(CPUState * env, int itlbnb)
118 {
119     uint8_t or_mask = 0, and_mask = (uint8_t) - 1;
120
121     switch (itlbnb) {
122     case 0:
123         and_mask = 0x7f;
124         break;
125     case 1:
126         and_mask = 0xe7;
127         or_mask = 0x80;
128         break;
129     case 2:
130         and_mask = 0xfb;
131         or_mask = 0x50;
132         break;
133     case 3:
134         or_mask = 0x2c;
135         break;
136     }
137
138     env->mmucr &= (and_mask << 24);
139     env->mmucr |= (or_mask << 24);
140 }
141
142 static int itlb_replacement(CPUState * env)
143 {
144     if ((env->mmucr & 0xe0000000) == 0xe0000000)
145         return 0;
146     if ((env->mmucr & 0x98000000) == 0x08000000)
147         return 1;
148     if ((env->mmucr & 0x54000000) == 0x04000000)
149         return 2;
150     if ((env->mmucr & 0x2c000000) == 0x00000000)
151         return 3;
152     assert(0);
153 }
154
155 /* Find the corresponding entry in the right TLB
156    Return entry, MMU_DTLB_MISS or MMU_DTLB_MULTIPLE
157 */
158 static int find_tlb_entry(CPUState * env, target_ulong address,
159                           tlb_t * entries, uint8_t nbtlb, int use_asid)
160 {
161     int match = MMU_DTLB_MISS;
162     uint32_t start, end;
163     uint8_t asid;
164     int i;
165
166     asid = env->pteh & 0xff;
167
168     for (i = 0; i < nbtlb; i++) {
169         if (!entries[i].v)
170             continue;           /* Invalid entry */
171         if (use_asid && entries[i].asid != asid && !entries[i].sh)
172             continue;           /* Bad ASID */
173 #if 0
174         switch (entries[i].sz) {
175         case 0:
176             size = 1024;        /* 1kB */
177             break;
178         case 1:
179             size = 4 * 1024;    /* 4kB */
180             break;
181         case 2:
182             size = 64 * 1024;   /* 64kB */
183             break;
184         case 3:
185             size = 1024 * 1024; /* 1MB */
186             break;
187         default:
188             assert(0);
189         }
190 #endif
191         start = (entries[i].vpn << 10) & ~(entries[i].size - 1);
192         end = start + entries[i].size - 1;
193         if (address >= start && address <= end) {       /* Match */
194             if (match != -1)
195                 return MMU_DTLB_MULTIPLE;       /* Multiple match */
196             match = i;
197         }
198     }
199     return match;
200 }
201
202 /* Find itlb entry - update itlb from utlb if necessary and asked for
203    Return entry, MMU_ITLB_MISS, MMU_ITLB_MULTIPLE or MMU_DTLB_MULTIPLE
204    Update the itlb from utlb if update is not 0
205 */
206 int find_itlb_entry(CPUState * env, target_ulong address,
207                     int use_asid, int update)
208 {
209     int e, n;
210
211     e = find_tlb_entry(env, address, env->itlb, ITLB_SIZE, use_asid);
212     if (e == MMU_DTLB_MULTIPLE)
213         e = MMU_ITLB_MULTIPLE;
214     else if (e == MMU_DTLB_MISS && update) {
215         e = find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid);
216         if (e >= 0) {
217             n = itlb_replacement(env);
218             env->itlb[n] = env->utlb[e];
219             e = n;
220         }
221     }
222     if (e >= 0)
223         update_itlb_use(env, e);
224     return e;
225 }
226
227 /* Find utlb entry
228    Return entry, MMU_DTLB_MISS, MMU_DTLB_MULTIPLE */
229 int find_utlb_entry(CPUState * env, target_ulong address, int use_asid)
230 {
231     uint8_t urb, urc;
232
233     /* Increment URC */
234     urb = ((env->mmucr) >> 18) & 0x3f;
235     urc = ((env->mmucr) >> 10) & 0x3f;
236     urc++;
237     if (urc == urb || urc == UTLB_SIZE - 1)
238         urc = 0;
239     env->mmucr = (env->mmucr & 0xffff03ff) | (urc << 10);
240
241     /* Return entry */
242     return find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid);
243 }
244
245 /* Match address against MMU
246    Return MMU_OK, MMU_DTLB_MISS_READ, MMU_DTLB_MISS_WRITE,
247    MMU_DTLB_INITIAL_WRITE, MMU_DTLB_VIOLATION_READ,
248    MMU_DTLB_VIOLATION_WRITE, MMU_ITLB_MISS,
249    MMU_ITLB_MULTIPLE, MMU_ITLB_VIOLATION
250 */
251 static int get_mmu_address(CPUState * env, target_ulong * physical,
252                            int *prot, target_ulong address,
253                            int rw, int access_type)
254 {
255     int use_asid, is_code, n;
256     tlb_t *matching = NULL;
257
258     use_asid = (env->mmucr & MMUCR_SV) == 0 && (env->sr & SR_MD) == 0;
259     is_code = env->pc == address;       /* Hack */
260
261     /* Use a hack to find if this is an instruction or data access */
262     if (env->pc == address && !(rw & PAGE_WRITE)) {
263         n = find_itlb_entry(env, address, use_asid, 1);
264         if (n >= 0) {
265             matching = &env->itlb[n];
266             if ((env->sr & SR_MD) & !(matching->pr & 2))
267                 n = MMU_ITLB_VIOLATION;
268             else
269                 *prot = PAGE_READ;
270         }
271     } else {
272         n = find_utlb_entry(env, address, use_asid);
273         if (n >= 0) {
274             matching = &env->utlb[n];
275             switch ((matching->pr << 1) | ((env->sr & SR_MD) ? 1 : 0)) {
276             case 0:             /* 000 */
277             case 2:             /* 010 */
278                 n = (rw & PAGE_WRITE) ? MMU_DTLB_VIOLATION_WRITE :
279                     MMU_DTLB_VIOLATION_READ;
280                 break;
281             case 1:             /* 001 */
282             case 4:             /* 100 */
283             case 5:             /* 101 */
284                 if (rw & PAGE_WRITE)
285                     n = MMU_DTLB_VIOLATION_WRITE;
286                 else
287                     *prot = PAGE_READ;
288                 break;
289             case 3:             /* 011 */
290             case 6:             /* 110 */
291             case 7:             /* 111 */
292                 *prot = rw & (PAGE_READ | PAGE_WRITE);
293                 break;
294             }
295         } else if (n == MMU_DTLB_MISS) {
296             n = (rw & PAGE_WRITE) ? MMU_DTLB_MISS_WRITE :
297                 MMU_DTLB_MISS_READ;
298         }
299     }
300     if (n >= 0) {
301         *physical = ((matching->ppn << 10) & ~(matching->size - 1)) |
302             (address & (matching->size - 1));
303         if ((rw & PAGE_WRITE) & !matching->d)
304             n = MMU_DTLB_INITIAL_WRITE;
305         else
306             n = MMU_OK;
307     }
308     return n;
309 }
310
311 int get_physical_address(CPUState * env, target_ulong * physical,
312                          int *prot, target_ulong address,
313                          int rw, int access_type)
314 {
315     /* P1, P2 and P4 areas do not use translation */
316     if ((address >= 0x80000000 && address < 0xc0000000) ||
317         address >= 0xe0000000) {
318         if (!(env->sr & SR_MD)
319             && (address < 0xe0000000 || address > 0xe4000000)) {
320             /* Unauthorized access in user mode (only store queues are available) */
321             fprintf(stderr, "Unauthorized access\n");
322             return (rw & PAGE_WRITE) ? MMU_DTLB_MISS_WRITE :
323                 MMU_DTLB_MISS_READ;
324         }
325         /* Mask upper 3 bits */
326         *physical = address & 0x1FFFFFFF;
327         *prot = PAGE_READ | PAGE_WRITE;
328         return MMU_OK;
329     }
330
331     /* If MMU is disabled, return the corresponding physical page */
332     if (!env->mmucr & MMUCR_AT) {
333         *physical = address & 0x1FFFFFFF;
334         *prot = PAGE_READ | PAGE_WRITE;
335         return MMU_OK;
336     }
337
338     /* We need to resort to the MMU */
339     return get_mmu_address(env, physical, prot, address, rw, access_type);
340 }
341
342 int cpu_sh4_handle_mmu_fault(CPUState * env, target_ulong address, int rw,
343                              int is_user, int is_softmmu)
344 {
345     target_ulong physical, page_offset, page_size;
346     int prot, ret, access_type;
347
348     /* XXXXX */
349 #if 0
350     fprintf(stderr, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n",
351             __func__, env->pc, address, rw, is_user, is_softmmu);
352 #endif
353
354     access_type = ACCESS_INT;
355     ret =
356         get_physical_address(env, &physical, &prot, address, rw,
357                              access_type);
358
359     if (ret != MMU_OK) {
360         env->tea = address;
361         switch (ret) {
362         case MMU_ITLB_MISS:
363         case MMU_DTLB_MISS_READ:
364             env->exception_index = 0x040;
365             break;
366         case MMU_DTLB_MULTIPLE:
367         case MMU_ITLB_MULTIPLE:
368             env->exception_index = 0x140;
369             break;
370         case MMU_ITLB_VIOLATION:
371             env->exception_index = 0x0a0;
372             break;
373         case MMU_DTLB_MISS_WRITE:
374             env->exception_index = 0x060;
375             break;
376         case MMU_DTLB_INITIAL_WRITE:
377             env->exception_index = 0x080;
378             break;
379         case MMU_DTLB_VIOLATION_READ:
380             env->exception_index = 0x0a0;
381             break;
382         case MMU_DTLB_VIOLATION_WRITE:
383             env->exception_index = 0x0c0;
384             break;
385         default:
386             assert(0);
387         }
388         return 1;
389     }
390
391     page_size = TARGET_PAGE_SIZE;
392     page_offset =
393         (address - (address & TARGET_PAGE_MASK)) & ~(page_size - 1);
394     address = (address & TARGET_PAGE_MASK) + page_offset;
395     physical = (physical & TARGET_PAGE_MASK) + page_offset;
396
397     return tlb_set_page(env, address, physical, prot, is_user, is_softmmu);
398 }