blob: b9ad090511fb851f8f3d30c86ae1064d03e44f19 [file] [log] [blame]
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -08001/*
2 * MIPS emulation helpers for qemu.
3 *
4 * Copyright (c) 2004-2005 Jocelyn Mayer
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 <stdarg.h>
20#include <stdlib.h>
21#include <stdio.h>
22#include <string.h>
23#include <inttypes.h>
24#include <signal.h>
25
26#include "cpu.h"
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -080027
28enum {
29 TLBRET_DIRTY = -4,
30 TLBRET_INVALID = -3,
31 TLBRET_NOMATCH = -2,
32 TLBRET_BADADDR = -1,
33 TLBRET_MATCH = 0
34};
35
36/* no MMU emulation */
David 'Digit' Turnere2678e12014-01-16 15:56:43 +010037int no_mmu_map_address (CPUMIPSState *env, hwaddr *physical, int *prot,
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -080038 target_ulong address, int rw, int access_type)
39{
40 *physical = address;
41 *prot = PAGE_READ | PAGE_WRITE;
42 return TLBRET_MATCH;
43}
44
45/* fixed mapping MMU emulation */
David 'Digit' Turnere2678e12014-01-16 15:56:43 +010046int fixed_mmu_map_address (CPUMIPSState *env, hwaddr *physical, int *prot,
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -080047 target_ulong address, int rw, int access_type)
48{
49 if (address <= (int32_t)0x7FFFFFFFUL) {
50 if (!(env->CP0_Status & (1 << CP0St_ERL)))
51 *physical = address + 0x40000000UL;
52 else
53 *physical = address;
54 } else if (address <= (int32_t)0xBFFFFFFFUL)
55 *physical = address & 0x1FFFFFFF;
56 else
57 *physical = address;
58
59 *prot = PAGE_READ | PAGE_WRITE;
60 return TLBRET_MATCH;
61}
62
63/* MIPS32/MIPS64 R4000-style MMU emulation */
David 'Digit' Turnere2678e12014-01-16 15:56:43 +010064int r4k_map_address (CPUMIPSState *env, hwaddr *physical, int *prot,
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -080065 target_ulong address, int rw, int access_type)
66{
67 uint8_t ASID = env->CP0_EntryHi & 0xFF;
Chris Dearman55ff3182012-08-03 14:35:52 -070068 r4k_tlb_t *tlb;
69 target_ulong mask;
70 target_ulong tag;
71 target_ulong VPN;
72 int n;
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -080073 int i;
74
Chris Dearman55ff3182012-08-03 14:35:52 -070075 for (i = 0; i < env->tlb->nb_tlb; i++) {
76 tlb = &env->tlb->mmu.r4k.tlb[i];
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -080077 /* 1k pages are not supported. */
Chris Dearman55ff3182012-08-03 14:35:52 -070078 mask = ~(TARGET_PAGE_MASK << 1);
79 tag = address & ~mask;
80 VPN = tlb->VPN & ~mask;
81
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -080082#if defined(TARGET_MIPS64)
83 tag &= env->SEGMask;
84#endif
85
86 /* Check ASID, virtual page number & size */
87 if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) {
88 /* TLB match */
Chris Dearman55ff3182012-08-03 14:35:52 -070089 n = !!(address & mask & ~(mask >> 1));
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -080090 /* Check access rights */
91 if (!(n ? tlb->V1 : tlb->V0))
92 return TLBRET_INVALID;
93 if (rw == 0 || (n ? tlb->D1 : tlb->D0)) {
94 *physical = tlb->PFN[n] | (address & (mask >> 1));
95 *prot = PAGE_READ;
96 if (n ? tlb->D1 : tlb->D0)
97 *prot |= PAGE_WRITE;
98 return TLBRET_MATCH;
99 }
100 return TLBRET_DIRTY;
101 }
102 }
103 return TLBRET_NOMATCH;
104}
105
106#if !defined(CONFIG_USER_ONLY)
David 'Digit' Turnere2678e12014-01-16 15:56:43 +0100107static int get_physical_address (CPUMIPSState *env, hwaddr *physical,
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800108 int *prot, target_ulong address,
109 int rw, int access_type)
110{
111 /* User mode can only access useg/xuseg */
112 int user_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM;
113 int supervisor_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_SM;
114 int kernel_mode = !user_mode && !supervisor_mode;
115#if defined(TARGET_MIPS64)
116 int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
117 int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
118 int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
119#endif
120 int ret = TLBRET_MATCH;
121
122#if 0
123 qemu_log("user mode %d h %08x\n", user_mode, env->hflags);
124#endif
125
126 if (address <= (int32_t)0x7FFFFFFFUL) {
127 /* useg */
Chris Dearman55ff3182012-08-03 14:35:52 -0700128 if (unlikely(env->CP0_Status & (1 << CP0St_ERL))) {
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800129 *physical = address & 0xFFFFFFFF;
130 *prot = PAGE_READ | PAGE_WRITE;
131 } else {
132 ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
133 }
134#if defined(TARGET_MIPS64)
135 } else if (address < 0x4000000000000000ULL) {
136 /* xuseg */
137 if (UX && address <= (0x3FFFFFFFFFFFFFFFULL & env->SEGMask)) {
138 ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
139 } else {
140 ret = TLBRET_BADADDR;
141 }
142 } else if (address < 0x8000000000000000ULL) {
143 /* xsseg */
144 if ((supervisor_mode || kernel_mode) &&
145 SX && address <= (0x7FFFFFFFFFFFFFFFULL & env->SEGMask)) {
146 ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
147 } else {
148 ret = TLBRET_BADADDR;
149 }
150 } else if (address < 0xC000000000000000ULL) {
151 /* xkphys */
152 if (kernel_mode && KX &&
153 (address & 0x07FFFFFFFFFFFFFFULL) <= env->PAMask) {
154 *physical = address & env->PAMask;
155 *prot = PAGE_READ | PAGE_WRITE;
156 } else {
157 ret = TLBRET_BADADDR;
158 }
159 } else if (address < 0xFFFFFFFF80000000ULL) {
160 /* xkseg */
161 if (kernel_mode && KX &&
162 address <= (0xFFFFFFFF7FFFFFFFULL & env->SEGMask)) {
163 ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
164 } else {
165 ret = TLBRET_BADADDR;
166 }
167#endif
168 } else if (address < (int32_t)0xA0000000UL) {
169 /* kseg0 */
170 if (kernel_mode) {
171 *physical = address - (int32_t)0x80000000UL;
172 *prot = PAGE_READ | PAGE_WRITE;
173 } else {
174 ret = TLBRET_BADADDR;
175 }
176 } else if (address < (int32_t)0xC0000000UL) {
177 /* kseg1 */
178 if (kernel_mode) {
179 *physical = address - (int32_t)0xA0000000UL;
180 *prot = PAGE_READ | PAGE_WRITE;
181 } else {
182 ret = TLBRET_BADADDR;
183 }
184 } else if (address < (int32_t)0xE0000000UL) {
185 /* sseg (kseg2) */
186 if (supervisor_mode || kernel_mode) {
187 ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
188 } else {
189 ret = TLBRET_BADADDR;
190 }
191 } else {
192 /* kseg3 */
193 /* XXX: debug segment is not emulated */
194 if (kernel_mode) {
195 ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
196 } else {
197 ret = TLBRET_BADADDR;
198 }
199 }
200#if 0
201 qemu_log(TARGET_FMT_lx " %d %d => " TARGET_FMT_lx " %d (%d)\n",
202 address, rw, access_type, *physical, *prot, ret);
203#endif
204
205 return ret;
206}
207#endif
208
David 'Digit' Turnere2678e12014-01-16 15:56:43 +0100209static void raise_mmu_exception(CPUMIPSState *env, target_ulong address,
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800210 int rw, int tlb_error)
211{
212 int exception = 0, error_code = 0;
213
214 switch (tlb_error) {
215 default:
216 case TLBRET_BADADDR:
217 /* Reference to kernel address from user mode or supervisor mode */
218 /* Reference to supervisor address from user mode */
219 if (rw)
220 exception = EXCP_AdES;
221 else
222 exception = EXCP_AdEL;
223 break;
224 case TLBRET_NOMATCH:
225 /* No TLB match for a mapped address */
226 if (rw)
227 exception = EXCP_TLBS;
228 else
229 exception = EXCP_TLBL;
230 error_code = 1;
231 break;
232 case TLBRET_INVALID:
233 /* TLB match with no valid bit */
234 if (rw)
235 exception = EXCP_TLBS;
236 else
237 exception = EXCP_TLBL;
238 break;
239 case TLBRET_DIRTY:
240 /* TLB match but 'D' bit is cleared */
241 exception = EXCP_LTLBL;
242 break;
243
244 }
245 /* Raise exception */
246 env->CP0_BadVAddr = address;
247 env->CP0_Context = (env->CP0_Context & ~0x007fffff) |
248 ((address >> 9) & 0x007ffff0);
249 env->CP0_EntryHi =
250 (env->CP0_EntryHi & 0xFF) | (address & (TARGET_PAGE_MASK << 1));
251#if defined(TARGET_MIPS64)
252 env->CP0_EntryHi &= env->SEGMask;
253 env->CP0_XContext = (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) |
254 ((address & 0xC00000000000ULL) >> (55 - env->SEGBITS)) |
255 ((address & ((1ULL << env->SEGBITS) - 1) & 0xFFFFFFFFFFFFE000ULL) >> 9);
256#endif
257 env->exception_index = exception;
258 env->error_code = error_code;
259}
260
Chris Dearman55ff3182012-08-03 14:35:52 -0700261/*
262 * Get the pgd_current from TLB exception handler
263 * The exception handler is generated by function build_r4000_tlb_refill_handler.
Chris Dearman55ff3182012-08-03 14:35:52 -0700264 */
Chris Dearman6e22b202013-03-25 13:56:40 -0700265
266static struct {
267 target_ulong pgd_current_p;
268 int softshift;
269} linux_pte_info = {0};
270
David 'Digit' Turnere2678e12014-01-16 15:56:43 +0100271static inline target_ulong cpu_mips_get_pgd(CPUMIPSState *env)
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800272{
Chris Dearman6e22b202013-03-25 13:56:40 -0700273 if (unlikely(linux_pte_info.pgd_current_p == 0)) {
274 int i;
275 uint32_t lui_ins, lw_ins, srl_ins;
276 uint32_t address;
Chris Dearman55ff3182012-08-03 14:35:52 -0700277 uint32_t ebase;
278
Chris Dearman6e22b202013-03-25 13:56:40 -0700279 /*
280 * The exact TLB refill code varies depeing on the kernel version
281 * and configuration. Examins the TLB handler to extract
282 * pgd_current_p and the shift required to convert in memory PTE
283 * to TLB format
284 */
285 static struct {
286 struct {
287 uint32_t off;
288 uint32_t op;
289 uint32_t mask;
290 } lui, lw, srl;
291 } handlers[] = {
292 /* 2.6.29+ */
293 {
294 {0x00, 0x3c1b0000, 0xffff0000}, /* 0x3c1b803f : lui k1,%hi(pgd_current_p) */
295 {0x08, 0x8f7b0000, 0xffff0000}, /* 0x8f7b3000 : lw k1,%lo(k1) */
296 {0x34, 0x001ad182, 0xffffffff} /* 0x001ad182 : srl k0,k0,0x6 */
297 },
298 /* 3.4+ */
299 {
300 {0x00, 0x3c1b0000, 0xffff0000}, /* 0x3c1b803f : lui k1,%hi(pgd_current_p) */
301 {0x08, 0x8f7b0000, 0xffff0000}, /* 0x8f7b3000 : lw k1,%lo(k1) */
302 {0x34, 0x001ad142, 0xffffffff} /* 0x001ad182 : srl k0,k0,0x5 */
303 }
304 };
Chris Dearman55ff3182012-08-03 14:35:52 -0700305
306 ebase = env->CP0_EBase - 0x80000000;
307
Chris Dearman6e22b202013-03-25 13:56:40 -0700308 /* Match the kernel TLB refill exception handler against known code */
309 for (i = 0; i < sizeof(handlers)/sizeof(handlers[0]); i++) {
310 lui_ins = ldl_phys(ebase + handlers[i].lui.off);
311 lw_ins = ldl_phys(ebase + handlers[i].lw.off);
312 srl_ins = ldl_phys(ebase + handlers[i].srl.off);
313 if (((lui_ins & handlers[i].lui.mask) == handlers[i].lui.op) &&
314 ((lw_ins & handlers[i].lw.mask) == handlers[i].lw.op) &&
315 ((srl_ins & handlers[i].srl.mask) == handlers[i].srl.op))
316 break;
317 }
318 if (i >= sizeof(handlers)/sizeof(handlers[0])) {
319 printf("TLBMiss handler dump:\n");
320 for (i = 0; i < 0x80; i+= 4)
321 printf("0x%08x: 0x%08x\n", ebase + i, ldl_phys(ebase + i));
322 cpu_abort(env, "TLBMiss handler signature not recognised\n");
323 }
Chris Dearman55ff3182012-08-03 14:35:52 -0700324
Chris Dearman6e22b202013-03-25 13:56:40 -0700325 address = (lui_ins & 0xffff) << 16;
326 address += (((int32_t)(lw_ins & 0xffff)) << 16) >> 16;
327 if (address >= 0x80000000 && address < 0xa0000000)
328 address -= 0x80000000;
329 else if (address >= 0xa0000000 && address <= 0xc0000000)
330 address -= 0xa0000000;
331 else
332 cpu_abort(env, "pgd_current_p not in KSEG0/KSEG1\n");
333
334 linux_pte_info.pgd_current_p = address;
335 linux_pte_info.softshift = (srl_ins >> 6) & 0x1f;
Chris Dearman55ff3182012-08-03 14:35:52 -0700336 }
Chris Dearman6e22b202013-03-25 13:56:40 -0700337
338 /* Get pgd_current */
339 return ldl_phys(linux_pte_info.pgd_current_p);
Chris Dearman55ff3182012-08-03 14:35:52 -0700340}
341
David 'Digit' Turnera2c14f92014-02-04 01:02:30 +0100342// in target-mips/op_helper.c
David 'Digit' Turnere2678e12014-01-16 15:56:43 +0100343extern void r4k_helper_ptw_tlbrefill(CPUMIPSState*);
David 'Digit' Turnera2c14f92014-02-04 01:02:30 +0100344
David 'Digit' Turnere2678e12014-01-16 15:56:43 +0100345static inline int cpu_mips_tlb_refill(CPUMIPSState *env, target_ulong address, int rw ,
Chris Dearman55ff3182012-08-03 14:35:52 -0700346 int mmu_idx, int is_softmmu)
347{
348 int32_t saved_hflags;
349 target_ulong saved_badvaddr,saved_entryhi,saved_context;
350
351 target_ulong pgd_addr,pt_addr,index;
David 'Digit' Turnera2c14f92014-02-04 01:02:30 +0100352 target_ulong fault_addr, ptw_phys;
Chris Dearman55ff3182012-08-03 14:35:52 -0700353 target_ulong elo_even,elo_odd;
Chris Dearman55ff3182012-08-03 14:35:52 -0700354 int ret;
355
356 saved_badvaddr = env->CP0_BadVAddr;
357 saved_context = env->CP0_Context;
358 saved_entryhi = env->CP0_EntryHi;
359 saved_hflags = env->hflags;
360
361 env->CP0_BadVAddr = address;
362 env->CP0_Context = (env->CP0_Context & ~0x007fffff) |
363 ((address >> 9) & 0x007ffff0);
364 env->CP0_EntryHi =
365 (env->CP0_EntryHi & 0xFF) | (address & (TARGET_PAGE_MASK << 1));
366
367 env->hflags = MIPS_HFLAG_KM;
368
369 fault_addr = env->CP0_BadVAddr;
Chris Dearman55ff3182012-08-03 14:35:52 -0700370
371 pgd_addr = cpu_mips_get_pgd(env);
372 if (unlikely(!pgd_addr))
373 {
374 /*not valid pgd_addr,just return.*/
375 //return TLBRET_NOMATCH;
376 ret = TLBRET_NOMATCH;
377 goto out;
378 }
379
380 ptw_phys = pgd_addr - (int32_t)0x80000000UL;
381 index = (fault_addr>>22)<<2;
382 ptw_phys += index;
383
384 pt_addr = ldl_phys(ptw_phys);
385
386 ptw_phys = pt_addr - (int32_t)0x80000000UL;
387 index = (env->CP0_Context>>1)&0xff8;
388 ptw_phys += index;
389
Chris Dearman6e22b202013-03-25 13:56:40 -0700390 /* get the page table entry*/
Chris Dearman55ff3182012-08-03 14:35:52 -0700391 elo_even = ldl_phys(ptw_phys);
392 elo_odd = ldl_phys(ptw_phys+4);
Chris Dearman6e22b202013-03-25 13:56:40 -0700393 elo_even = elo_even >> linux_pte_info.softshift;
394 elo_odd = elo_odd >> linux_pte_info.softshift;
Chris Dearman55ff3182012-08-03 14:35:52 -0700395 env->CP0_EntryLo0 = elo_even;
396 env->CP0_EntryLo1 = elo_odd;
Chris Dearman6e22b202013-03-25 13:56:40 -0700397 /* Done. refill the TLB */
Chris Dearman55ff3182012-08-03 14:35:52 -0700398 r4k_helper_ptw_tlbrefill(env);
399
400 /* Since we know the value of TLB entry, we can
401 * return the TLB lookup value here.
402 */
403
404 env->hflags = saved_hflags;
405
406 target_ulong mask = env->CP0_PageMask | ~(TARGET_PAGE_MASK << 1);
407 int n = !!(address & mask & ~(mask >> 1));
408 /* Check access rights */
409 if (!(n ? (elo_odd & 2) != 0 : (elo_even & 2) != 0))
410 {
411 ret = TLBRET_INVALID;
412 goto out;
413 }
414
415 if (rw == 0 || (n ? (elo_odd & 4) != 0 : (elo_even & 4) != 0)) {
416 target_ulong physical = (n?(elo_odd >> 6) << 12 : (elo_even >> 6) << 12);
417 physical |= (address & (mask >> 1));
418 int prot = PAGE_READ;
419 if (n ? (elo_odd & 4) != 0 : (elo_even & 4) != 0)
420 prot |= PAGE_WRITE;
421
422 tlb_set_page(env, address & TARGET_PAGE_MASK,
David 'Digit' Turner0d8b2352014-03-20 17:13:13 +0100423 physical & TARGET_PAGE_MASK, prot | PAGE_EXEC,
424 mmu_idx, TARGET_PAGE_SIZE);
Chris Dearman55ff3182012-08-03 14:35:52 -0700425 ret = TLBRET_MATCH;
426 goto out;
427 }
428 ret = TLBRET_DIRTY;
429
430out:
431 env->CP0_BadVAddr = saved_badvaddr;
432 env->CP0_Context = saved_context;
433 env->CP0_EntryHi = saved_entryhi;
434 env->hflags = saved_hflags;
435 return ret;
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800436}
437
David 'Digit' Turnere2678e12014-01-16 15:56:43 +0100438int cpu_mips_handle_mmu_fault (CPUMIPSState *env, target_ulong address, int rw,
David 'Digit' Turner0d8b2352014-03-20 17:13:13 +0100439 int mmu_idx)
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800440{
441#if !defined(CONFIG_USER_ONLY)
David 'Digit' Turnerbcde1092014-01-09 23:19:19 +0100442 hwaddr physical;
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800443 int prot;
444#endif
David 'Digit' Turnera2c14f92014-02-04 01:02:30 +0100445 //int exception = 0, error_code = 0;
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800446 int access_type;
447 int ret = 0;
448
449#if 0
450 log_cpu_state(env, 0);
451#endif
David 'Digit' Turner0d8b2352014-03-20 17:13:13 +0100452 qemu_log("%s pc " TARGET_FMT_lx " ad " TARGET_FMT_lx " rw %d mmu_idx %d\n",
453 __func__, env->active_tc.PC, address, rw, mmu_idx);
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800454
455 rw &= 1;
456
457 /* data access */
458 /* XXX: put correct access by using cpu_restore_state()
459 correctly */
460 access_type = ACCESS_INT;
461#if defined(CONFIG_USER_ONLY)
462 ret = TLBRET_NOMATCH;
463#else
464 ret = get_physical_address(env, &physical, &prot,
465 address, rw, access_type);
466 qemu_log("%s address=" TARGET_FMT_lx " ret %d physical " TARGET_FMT_plx " prot %d\n",
467 __func__, address, ret, physical, prot);
468 if (ret == TLBRET_MATCH) {
David 'Digit' Turner0d8b2352014-03-20 17:13:13 +0100469 tlb_set_page(env, address & TARGET_PAGE_MASK,
470 physical & TARGET_PAGE_MASK, prot | PAGE_EXEC,
471 mmu_idx, TARGET_PAGE_SIZE);
472 ret = 0;
Chris Dearman55ff3182012-08-03 14:35:52 -0700473 }
474 else if (ret == TLBRET_NOMATCH)
David 'Digit' Turner0d8b2352014-03-20 17:13:13 +0100475 ret = cpu_mips_tlb_refill(env,address,rw,mmu_idx,1);
Chris Dearman55ff3182012-08-03 14:35:52 -0700476 if (ret < 0)
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800477#endif
478 {
479 raise_mmu_exception(env, address, rw, ret);
480 ret = 1;
481 }
482
483 return ret;
484}
485
486#if !defined(CONFIG_USER_ONLY)
David 'Digit' Turnere2678e12014-01-16 15:56:43 +0100487hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address, int rw)
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800488{
David 'Digit' Turnerbcde1092014-01-09 23:19:19 +0100489 hwaddr physical;
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800490 int prot;
491 int access_type;
492 int ret = 0;
493
494 rw &= 1;
495
496 /* data access */
497 access_type = ACCESS_INT;
498 ret = get_physical_address(env, &physical, &prot,
499 address, rw, access_type);
Chris Dearman52846c82014-02-25 19:49:16 -0800500 if (ret != TLBRET_MATCH && ret != TLBRET_DIRTY) {
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800501 raise_mmu_exception(env, address, rw, ret);
502 return -1LL;
503 } else {
504 return physical;
505 }
506}
507#endif
508
David 'Digit' Turnere2678e12014-01-16 15:56:43 +0100509hwaddr cpu_get_phys_page_debug(CPUMIPSState *env, target_ulong addr)
Chris Dearman55ff3182012-08-03 14:35:52 -0700510{
511#if defined(CONFIG_USER_ONLY)
512 return addr;
513#else
David 'Digit' Turnerbcde1092014-01-09 23:19:19 +0100514 hwaddr phys_addr;
Chris Dearman55ff3182012-08-03 14:35:52 -0700515 int prot, ret;
516
517 ret = get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT);
518 if (ret != TLBRET_MATCH && ret != TLBRET_DIRTY) {
519 target_ulong pgd_addr = cpu_mips_get_pgd(env);
520 if (unlikely(!pgd_addr)) {
521 phys_addr = -1;
522 }
523 else {
524 target_ulong pgd_phys, pgd_index;
525 target_ulong pt_addr, pt_phys, pt_index;
526 target_ulong lo;
527 /* Mimic the steps taken for a TLB refill */
528 pgd_phys = pgd_addr - (int32_t)0x80000000UL;
529 pgd_index = (addr >> 22) << 2;
530 pt_addr = ldl_phys(pgd_phys + pgd_index);
531 pt_phys = pt_addr - (int32_t)0x80000000UL;
532 pt_index = (((addr >> 9) & 0x007ffff0) >> 1) & 0xff8;
533 /* get the entrylo value */
534 if (addr & 0x1000)
535 lo = ldl_phys(pt_phys + pt_index + 4);
536 else
537 lo = ldl_phys(pt_phys + pt_index);
538 /* convert software TLB entry to hardware value */
Chris Dearman6e22b202013-03-25 13:56:40 -0700539 lo >>= linux_pte_info.softshift;
Chris Dearman55ff3182012-08-03 14:35:52 -0700540 if (lo & 0x00000002)
541 /* It is valid */
542 phys_addr = (lo >> 6) << 12;
543 else
544 phys_addr = -1;
545 }
546 }
547 return phys_addr;
548#endif
549}
550
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800551static const char * const excp_names[EXCP_LAST + 1] = {
552 [EXCP_RESET] = "reset",
553 [EXCP_SRESET] = "soft reset",
554 [EXCP_DSS] = "debug single step",
555 [EXCP_DINT] = "debug interrupt",
556 [EXCP_NMI] = "non-maskable interrupt",
557 [EXCP_MCHECK] = "machine check",
558 [EXCP_EXT_INTERRUPT] = "interrupt",
559 [EXCP_DFWATCH] = "deferred watchpoint",
560 [EXCP_DIB] = "debug instruction breakpoint",
561 [EXCP_IWATCH] = "instruction fetch watchpoint",
562 [EXCP_AdEL] = "address error load",
563 [EXCP_AdES] = "address error store",
564 [EXCP_TLBF] = "TLB refill",
565 [EXCP_IBE] = "instruction bus error",
566 [EXCP_DBp] = "debug breakpoint",
567 [EXCP_SYSCALL] = "syscall",
568 [EXCP_BREAK] = "break",
569 [EXCP_CpU] = "coprocessor unusable",
570 [EXCP_RI] = "reserved instruction",
571 [EXCP_OVERFLOW] = "arithmetic overflow",
572 [EXCP_TRAP] = "trap",
573 [EXCP_FPE] = "floating point",
574 [EXCP_DDBS] = "debug data break store",
575 [EXCP_DWATCH] = "data watchpoint",
576 [EXCP_LTLBL] = "TLB modify",
577 [EXCP_TLBL] = "TLB load",
578 [EXCP_TLBS] = "TLB store",
579 [EXCP_DBE] = "data bus error",
580 [EXCP_DDBL] = "debug data break load",
581 [EXCP_THREAD] = "thread",
582 [EXCP_MDMX] = "MDMX",
583 [EXCP_C2E] = "precise coprocessor 2",
584 [EXCP_CACHE] = "cache error",
585};
586
David 'Digit' Turnere2678e12014-01-16 15:56:43 +0100587void do_interrupt (CPUMIPSState *env)
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800588{
589#if !defined(CONFIG_USER_ONLY)
590 target_ulong offset;
591 int cause = -1;
592 const char *name;
593
594 if (qemu_log_enabled() && env->exception_index != EXCP_EXT_INTERRUPT) {
595 if (env->exception_index < 0 || env->exception_index > EXCP_LAST)
596 name = "unknown";
597 else
598 name = excp_names[env->exception_index];
599
600 qemu_log("%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " %s exception\n",
601 __func__, env->active_tc.PC, env->CP0_EPC, name);
602 }
603 if (env->exception_index == EXCP_EXT_INTERRUPT &&
604 (env->hflags & MIPS_HFLAG_DM))
605 env->exception_index = EXCP_DINT;
606 offset = 0x180;
607 switch (env->exception_index) {
608 case EXCP_DSS:
609 env->CP0_Debug |= 1 << CP0DB_DSS;
610 /* Debug single step cannot be raised inside a delay slot and
611 resume will always occur on the next instruction
612 (but we assume the pc has always been updated during
613 code translation). */
614 env->CP0_DEPC = env->active_tc.PC;
615 goto enter_debug_mode;
616 case EXCP_DINT:
617 env->CP0_Debug |= 1 << CP0DB_DINT;
618 goto set_DEPC;
619 case EXCP_DIB:
620 env->CP0_Debug |= 1 << CP0DB_DIB;
621 goto set_DEPC;
622 case EXCP_DBp:
623 env->CP0_Debug |= 1 << CP0DB_DBp;
624 goto set_DEPC;
625 case EXCP_DDBS:
626 env->CP0_Debug |= 1 << CP0DB_DDBS;
627 goto set_DEPC;
628 case EXCP_DDBL:
629 env->CP0_Debug |= 1 << CP0DB_DDBL;
630 set_DEPC:
631 if (env->hflags & MIPS_HFLAG_BMASK) {
632 /* If the exception was raised from a delay slot,
633 come back to the jump. */
634 env->CP0_DEPC = env->active_tc.PC - 4;
635 env->hflags &= ~MIPS_HFLAG_BMASK;
636 } else {
637 env->CP0_DEPC = env->active_tc.PC;
638 }
639 enter_debug_mode:
640 env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
641 env->hflags &= ~(MIPS_HFLAG_KSU);
642 /* EJTAG probe trap enable is not implemented... */
643 if (!(env->CP0_Status & (1 << CP0St_EXL)))
644 env->CP0_Cause &= ~(1 << CP0Ca_BD);
645 env->active_tc.PC = (int32_t)0xBFC00480;
646 break;
647 case EXCP_RESET:
David 'Digit' Turnerbf7a22f2014-03-25 16:36:03 +0100648 cpu_reset(ENV_GET_CPU(env));
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800649 break;
650 case EXCP_SRESET:
651 env->CP0_Status |= (1 << CP0St_SR);
652 memset(env->CP0_WatchLo, 0, sizeof(*env->CP0_WatchLo));
653 goto set_error_EPC;
654 case EXCP_NMI:
655 env->CP0_Status |= (1 << CP0St_NMI);
656 set_error_EPC:
657 if (env->hflags & MIPS_HFLAG_BMASK) {
658 /* If the exception was raised from a delay slot,
659 come back to the jump. */
660 env->CP0_ErrorEPC = env->active_tc.PC - 4;
661 env->hflags &= ~MIPS_HFLAG_BMASK;
662 } else {
663 env->CP0_ErrorEPC = env->active_tc.PC;
664 }
665 env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV);
666 env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
667 env->hflags &= ~(MIPS_HFLAG_KSU);
668 if (!(env->CP0_Status & (1 << CP0St_EXL)))
669 env->CP0_Cause &= ~(1 << CP0Ca_BD);
670 env->active_tc.PC = (int32_t)0xBFC00000;
671 break;
672 case EXCP_EXT_INTERRUPT:
673 cause = 0;
674 if (env->CP0_Cause & (1 << CP0Ca_IV))
675 offset = 0x200;
676 goto set_EPC;
677 case EXCP_LTLBL:
678 cause = 1;
679 goto set_EPC;
680 case EXCP_TLBL:
681 cause = 2;
682 if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) {
683#if defined(TARGET_MIPS64)
684 int R = env->CP0_BadVAddr >> 62;
685 int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
686 int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
687 int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
688
689 if ((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX))
690 offset = 0x080;
691 else
692#endif
693 offset = 0x000;
694 }
695 goto set_EPC;
696 case EXCP_TLBS:
697 cause = 3;
698 if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) {
699#if defined(TARGET_MIPS64)
700 int R = env->CP0_BadVAddr >> 62;
701 int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
702 int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
703 int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
704
705 if ((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX))
706 offset = 0x080;
707 else
708#endif
709 offset = 0x000;
710 }
711 goto set_EPC;
712 case EXCP_AdEL:
713 cause = 4;
714 goto set_EPC;
715 case EXCP_AdES:
716 cause = 5;
717 goto set_EPC;
718 case EXCP_IBE:
719 cause = 6;
720 goto set_EPC;
721 case EXCP_DBE:
722 cause = 7;
723 goto set_EPC;
724 case EXCP_SYSCALL:
725 cause = 8;
726 goto set_EPC;
727 case EXCP_BREAK:
728 cause = 9;
729 goto set_EPC;
730 case EXCP_RI:
731 cause = 10;
732 goto set_EPC;
733 case EXCP_CpU:
734 cause = 11;
735 env->CP0_Cause = (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) |
736 (env->error_code << CP0Ca_CE);
737 goto set_EPC;
738 case EXCP_OVERFLOW:
739 cause = 12;
740 goto set_EPC;
741 case EXCP_TRAP:
742 cause = 13;
743 goto set_EPC;
744 case EXCP_FPE:
745 cause = 15;
746 goto set_EPC;
747 case EXCP_C2E:
748 cause = 18;
749 goto set_EPC;
750 case EXCP_MDMX:
751 cause = 22;
752 goto set_EPC;
753 case EXCP_DWATCH:
754 cause = 23;
755 /* XXX: TODO: manage defered watch exceptions */
756 goto set_EPC;
757 case EXCP_MCHECK:
758 cause = 24;
759 goto set_EPC;
760 case EXCP_THREAD:
761 cause = 25;
762 goto set_EPC;
763 case EXCP_CACHE:
764 cause = 30;
765 if (env->CP0_Status & (1 << CP0St_BEV)) {
766 offset = 0x100;
767 } else {
768 offset = 0x20000100;
769 }
770 set_EPC:
771 if (!(env->CP0_Status & (1 << CP0St_EXL))) {
772 if (env->hflags & MIPS_HFLAG_BMASK) {
773 /* If the exception was raised from a delay slot,
774 come back to the jump. */
775 env->CP0_EPC = env->active_tc.PC - 4;
776 env->CP0_Cause |= (1 << CP0Ca_BD);
777 } else {
778 env->CP0_EPC = env->active_tc.PC;
779 env->CP0_Cause &= ~(1 << CP0Ca_BD);
780 }
781 env->CP0_Status |= (1 << CP0St_EXL);
782 env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
783 env->hflags &= ~(MIPS_HFLAG_KSU);
784 }
785 env->hflags &= ~MIPS_HFLAG_BMASK;
786 if (env->CP0_Status & (1 << CP0St_BEV)) {
787 env->active_tc.PC = (int32_t)0xBFC00200;
788 } else {
789 env->active_tc.PC = (int32_t)(env->CP0_EBase & ~0x3ff);
790 }
791 env->active_tc.PC += offset;
792 env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | (cause << CP0Ca_EC);
793 break;
794 default:
795 qemu_log("Invalid MIPS exception %d. Exiting\n", env->exception_index);
796 printf("Invalid MIPS exception %d. Exiting\n", env->exception_index);
797 exit(1);
798 }
799 if (qemu_log_enabled() && env->exception_index != EXCP_EXT_INTERRUPT) {
800 qemu_log("%s: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " cause %d\n"
801 " S %08x C %08x A " TARGET_FMT_lx " D " TARGET_FMT_lx "\n",
802 __func__, env->active_tc.PC, env->CP0_EPC, cause,
803 env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr,
804 env->CP0_DEPC);
805 }
806#endif
807 env->exception_index = EXCP_NONE;
808}
809
David 'Digit' Turnere2678e12014-01-16 15:56:43 +0100810void r4k_invalidate_tlb (CPUMIPSState *env, int idx)
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800811{
812 r4k_tlb_t *tlb;
813 target_ulong addr;
814 target_ulong end;
815 uint8_t ASID = env->CP0_EntryHi & 0xFF;
816 target_ulong mask;
817
818 tlb = &env->tlb->mmu.r4k.tlb[idx];
819 /* The qemu TLB is flushed when the ASID changes, so no need to
820 flush these entries again. */
821 if (tlb->G == 0 && tlb->ASID != ASID) {
822 return;
823 }
824
Bhanu Chetlapalli409c7b62012-01-31 16:25:04 -0800825 /* 1k pages are not supported. */
826 mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
827 if (tlb->V0) {
828 addr = tlb->VPN & ~mask;
829#if defined(TARGET_MIPS64)
830 if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) {
831 addr |= 0x3FFFFF0000000000ULL;
832 }
833#endif
834 end = addr | (mask >> 1);
835 while (addr < end) {
836 tlb_flush_page (env, addr);
837 addr += TARGET_PAGE_SIZE;
838 }
839 }
840 if (tlb->V1) {
841 addr = (tlb->VPN & ~mask) | ((mask >> 1) + 1);
842#if defined(TARGET_MIPS64)
843 if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) {
844 addr |= 0x3FFFFF0000000000ULL;
845 }
846#endif
847 end = addr | mask;
848 while (addr - 1 < end) {
849 tlb_flush_page (env, addr);
850 addr += TARGET_PAGE_SIZE;
851 }
852 }
853}