]>
git.proxmox.com Git - mirror_qemu.git/blob - target-mips/helper.c
2 * MIPS emulation helpers for qemu.
4 * Copyright (c) 2004-2005 Jocelyn Mayer
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.
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.
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
22 /* MIPS32 4K MMU emulation */
23 #ifdef MIPS_USES_R4K_TLB
24 static int map_address (CPUState
*env
, target_ulong
*physical
, int *prot
,
25 target_ulong address
, int rw
, int access_type
)
34 tag
= (address
& 0xFFFFE000);
35 ASID
= env
->CP0_EntryHi
& 0x000000FF;
36 for (i
= 0; i
< 16; i
++) {
38 /* Check ASID, virtual page number & size */
39 if ((tlb
->G
== 1 || tlb
->ASID
== ASID
) &&
40 tlb
->VPN
== tag
&& address
< tlb
->end
) {
42 n
= (address
>> 12) & 1;
43 /* Check access rights */
44 if ((tlb
->V
[n
] & 2) && (rw
== 0 || (tlb
->D
[n
] & 4))) {
45 *physical
= tlb
->PFN
[n
] | (address
& 0xFFF);
50 } else if (!(tlb
->V
[n
] & 2)) {
62 int get_physical_address (CPUState
*env
, target_ulong
*physical
, int *prot
,
63 target_ulong address
, int rw
, int access_type
)
68 /* User mode can only access useg */
69 user_mode
= ((env
->hflags
& MIPS_HFLAG_MODE
) == MIPS_HFLAG_UM
) ? 1 : 0;
72 fprintf(logfile
, "user mode %d h %08x\n",
73 user_mode
, env
->hflags
);
76 if (user_mode
&& address
> 0x7FFFFFFFUL
)
79 if (address
< 0x80000000UL
) {
80 if (!(env
->hflags
& MIPS_HFLAG_ERL
)) {
81 #ifdef MIPS_USES_R4K_TLB
82 ret
= map_address(env
, physical
, prot
, address
, rw
, access_type
);
84 *physical
= address
+ 0x40000000UL
;
85 *prot
= PAGE_READ
| PAGE_WRITE
;
89 *prot
= PAGE_READ
| PAGE_WRITE
;
91 } else if (address
< 0xA0000000UL
) {
93 /* XXX: check supervisor mode */
94 *physical
= address
- 0x80000000UL
;
95 *prot
= PAGE_READ
| PAGE_WRITE
;
96 } else if (address
< 0xC0000000UL
) {
98 /* XXX: check supervisor mode */
99 *physical
= address
- 0xA0000000UL
;
100 *prot
= PAGE_READ
| PAGE_WRITE
;
101 } else if (address
< 0xE0000000UL
) {
103 #ifdef MIPS_USES_R4K_TLB
104 ret
= map_address(env
, physical
, prot
, address
, rw
, access_type
);
107 *prot
= PAGE_READ
| PAGE_WRITE
;
111 /* XXX: check supervisor mode */
112 /* XXX: debug segment is not emulated */
113 #ifdef MIPS_USES_R4K_TLB
114 ret
= map_address(env
, physical
, prot
, address
, rw
, access_type
);
117 *prot
= PAGE_READ
| PAGE_WRITE
;
122 fprintf(logfile
, "%08x %d %d => %08x %d (%d)\n", address
, rw
,
123 access_type
, *physical
, *prot
, ret
);
130 #if defined(CONFIG_USER_ONLY)
131 target_ulong
cpu_get_phys_page_debug(CPUState
*env
, target_ulong addr
)
136 target_ulong
cpu_get_phys_page_debug(CPUState
*env
, target_ulong addr
)
138 target_ulong phys_addr
;
141 if (get_physical_address(env
, &phys_addr
, &prot
, addr
, 0, ACCESS_INT
) != 0)
147 #if !defined(CONFIG_USER_ONLY)
149 #define MMUSUFFIX _mmu
150 #define GETPC() (__builtin_return_address(0))
153 #include "softmmu_template.h"
156 #include "softmmu_template.h"
159 #include "softmmu_template.h"
162 #include "softmmu_template.h"
164 void tlb_fill (target_ulong addr
, int is_write
, int is_user
, void *retaddr
)
166 TranslationBlock
*tb
;
171 /* XXX: hack to restore env in all cases, even if not called from
174 env
= cpu_single_env
;
175 ret
= cpu_mips_handle_mmu_fault(env
, addr
, is_write
, is_user
, 1);
178 /* now we have a real cpu fault */
179 pc
= (unsigned long)retaddr
;
182 /* the PC is inside the translated code. It means that we have
183 a virtual CPU fault */
184 cpu_restore_state(tb
, env
, pc
, NULL
);
187 do_raise_exception_err(env
->exception_index
, env
->error_code
);
192 void cpu_mips_init_mmu (CPUState
*env
)
196 #endif /* !defined(CONFIG_USER_ONLY) */
198 int cpu_mips_handle_mmu_fault (CPUState
*env
, target_ulong address
, int rw
,
199 int is_user
, int is_softmmu
)
201 target_ulong physical
;
203 int exception
= 0, error_code
= 0;
208 cpu_dump_state(env
, logfile
, fprintf
, 0);
209 fprintf(logfile
, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n",
210 __func__
, env
->PC
, address
, rw
, is_user
, is_softmmu
);
213 /* XXX: put correct access by using cpu_restore_state()
215 access_type
= ACCESS_INT
;
216 if (env
->user_mode_only
) {
217 /* user mode only emulation */
221 ret
= get_physical_address(env
, &physical
, &prot
,
222 address
, rw
, access_type
);
224 fprintf(logfile
, "%s address=%08x ret %d physical %08x prot %d\n",
225 __func__
, address
, ret
, physical
, prot
);
228 ret
= tlb_set_page(env
, address
& ~0xFFF, physical
& ~0xFFF, prot
,
229 is_user
, is_softmmu
);
230 } else if (ret
< 0) {
235 /* Reference to kernel address from user mode or supervisor mode */
236 /* Reference to supervisor address from user mode */
238 exception
= EXCP_AdES
;
240 exception
= EXCP_AdEL
;
243 /* No TLB match for a mapped address */
245 exception
= EXCP_TLBS
;
247 exception
= EXCP_TLBL
;
251 /* TLB match with no valid bit */
253 exception
= EXCP_TLBS
;
255 exception
= EXCP_TLBL
;
259 /* TLB match but 'D' bit is cleared */
260 exception
= EXCP_LTLBL
;
264 /* Raise exception */
265 env
->CP0_BadVAddr
= address
;
267 (env
->CP0_Context
& 0x00000FFF) | (address
& 0xFFFFF000);
269 (env
->CP0_EntryHi
& 0x00000FFF) | (address
& 0xFFFFF000);
270 env
->exception_index
= exception
;
271 env
->error_code
= error_code
;
278 void do_interrupt (CPUState
*env
)
280 target_ulong pc
, offset
;
283 if (logfile
&& env
->exception_index
!= EXCP_EXT_INTERRUPT
) {
284 fprintf(logfile
, "%s enter: PC %08x EPC %08x cause %d excp %d\n",
285 __func__
, env
->PC
, env
->CP0_EPC
, cause
, env
->exception_index
);
287 if (env
->exception_index
== EXCP_EXT_INTERRUPT
&&
288 (env
->hflags
& MIPS_HFLAG_DM
))
289 env
->exception_index
= EXCP_DINT
;
291 switch (env
->exception_index
) {
293 env
->CP0_Debug
|= 1 << CP0DB_DSS
;
294 /* Debug single step cannot be raised inside a delay slot and
295 * resume will always occur on the next instruction
296 * (but we assume the pc has always been updated during
299 env
->CP0_DEPC
= env
->PC
;
300 goto enter_debug_mode
;
302 env
->CP0_Debug
|= 1 << CP0DB_DINT
;
305 env
->CP0_Debug
|= 1 << CP0DB_DIB
;
308 env
->CP0_Debug
|= 1 << CP0DB_DBp
;
311 env
->CP0_Debug
|= 1 << CP0DB_DDBS
;
314 env
->CP0_Debug
|= 1 << CP0DB_DDBL
;
317 if (env
->hflags
& MIPS_HFLAG_DS
) {
318 /* If the exception was raised from a delay slot,
319 * come back to the jump
321 env
->CP0_DEPC
= env
->PC
- 4;
323 env
->CP0_DEPC
= env
->PC
;
326 env
->hflags
|= MIPS_HFLAG_DM
;
327 /* EJTAG probe trap enable is not implemented... */
331 #ifdef MIPS_USES_R4K_TLB
332 env
->CP0_random
= MIPS_TLB_NB
- 1;
335 env
->CP0_Config0
= MIPS_CONFIG0
;
336 #if defined (MIPS_CONFIG1)
337 env
->CP0_Config1
= MIPS_CONFIG1
;
339 #if defined (MIPS_CONFIG2)
340 env
->CP0_Config2
= MIPS_CONFIG2
;
342 #if defined (MIPS_CONFIG3)
343 env
->CP0_Config3
= MIPS_CONFIG3
;
345 env
->CP0_WatchLo
= 0;
346 env
->CP0_Status
= (1 << CP0St_CU0
) | (1 << CP0St_BEV
);
349 env
->CP0_Status
= (1 << CP0St_CU0
) | (1 << CP0St_BEV
) |
351 env
->CP0_WatchLo
= 0;
354 env
->CP0_Status
= (1 << CP0St_CU0
) | (1 << CP0St_BEV
) |
357 env
->hflags
= MIPS_HFLAG_ERL
;
358 if (env
->hflags
& MIPS_HFLAG_DS
) {
359 /* If the exception was raised from a delay slot,
360 * come back to the jump
362 env
->CP0_ErrorEPC
= env
->PC
- 4;
364 env
->CP0_ErrorEPC
= env
->PC
;
371 case EXCP_EXT_INTERRUPT
:
373 if (env
->CP0_Cause
& (1 << CP0Ca_IV
))
378 /* XXX: TODO: manage defered watch exceptions */
387 if (env
->error_code
== 1 && !(env
->hflags
& MIPS_HFLAG_EXL
))
407 /* XXX: fill in the faulty unit number */
421 if (env
->CP0_Status
& (1 << CP0St_BEV
)) {
426 env
->hflags
|= MIPS_HFLAG_EXL
;
428 env
->CP0_Cause
= (env
->CP0_Cause
& ~0x7C) | (cause
<< 2);
429 if (env
->hflags
& MIPS_HFLAG_DS
) {
430 /* If the exception was raised from a delay slot,
431 * come back to the jump
433 env
->CP0_EPC
= env
->PC
- 4;
434 env
->CP0_Cause
|= 0x80000000;
436 env
->CP0_EPC
= env
->PC
;
437 env
->CP0_Cause
&= ~0x80000000;
442 fprintf(logfile
, "Invalid MIPS exception %d. Exiting\n",
443 env
->exception_index
);
445 printf("Invalid MIPS exception %d. Exiting\n", env
->exception_index
);
449 if (logfile
&& env
->exception_index
!= EXCP_EXT_INTERRUPT
) {
450 fprintf(logfile
, "%s: PC %08x EPC %08x cause %d excp %d\n"
451 " S %08x C %08x A %08x D %08x\n",
452 __func__
, env
->PC
, env
->CP0_EPC
, cause
, env
->exception_index
,
453 env
->CP0_Status
, env
->CP0_Cause
, env
->CP0_BadVAddr
,
456 env
->exception_index
= EXCP_NONE
;