]>
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
23 /* MIPS32 4K MMU emulation */
25 static int map_address (CPUState
*env
, target_ulong
*physical
, int *prot
,
26 target_ulong address
, int rw
, int access_type
)
35 tag
= (address
& 0xFFFFE000);
36 ASID
= env
->CP0_EntryHi
& 0x000000FF;
37 for (i
= 0; i
< 16; i
++) {
39 /* Check ASID, virtual page number & size */
40 if ((tlb
->G
== 1 || tlb
->ASID
== ASID
) &&
41 tlb
->VPN
== tag
&& address
< tlb
->end
) {
43 n
= (address
>> 12) & 1;
44 /* Check access rights */
45 if ((tlb
->V
[n
] & 2) && (rw
== 0 || (tlb
->D
[n
] & 4))) {
46 *physical
= tlb
->PFN
[n
] | (address
& 0xFFF);
51 } else if (!(tlb
->V
[n
] & 2)) {
63 int get_physical_address (CPUState
*env
, target_ulong
*physical
, int *prot
,
64 target_ulong address
, int rw
, int access_type
)
69 /* User mode can only access useg */
70 user_mode
= ((env
->hflags
& MIPS_HFLAG_MODE
) == MIPS_HFLAG_UM
) ? 1 : 0;
73 fprintf(logfile
, "user mode %d h %08x\n",
74 user_mode
, env
->hflags
);
77 if (user_mode
&& address
> 0x7FFFFFFFUL
)
80 if (address
< 0x80000000UL
) {
81 if (user_mode
|| !(env
->hflags
& MIPS_HFLAG_ERL
)) {
83 ret
= map_address(env
, physical
, prot
, address
, rw
);
85 *physical
= address
+ 0x40000000UL
;
86 *prot
= PAGE_READ
| PAGE_WRITE
;
90 *prot
= PAGE_READ
| PAGE_WRITE
;
92 } else if (address
< 0xA0000000UL
) {
94 /* XXX: check supervisor mode */
95 *physical
= address
- 0x80000000UL
;
96 *prot
= PAGE_READ
| PAGE_WRITE
;
97 } else if (address
< 0xC0000000UL
) {
99 /* XXX: check supervisor mode */
100 *physical
= address
- 0xA0000000UL
;
101 *prot
= PAGE_READ
| PAGE_WRITE
;
102 } else if (address
< 0xE0000000UL
) {
105 ret
= map_address(env
, physical
, prot
, address
, rw
);
108 *prot
= PAGE_READ
| PAGE_WRITE
;
112 /* XXX: check supervisor mode */
113 /* XXX: debug segment is not emulated */
115 ret
= map_address(env
, physical
, prot
, address
, rw
);
118 *prot
= PAGE_READ
| PAGE_WRITE
;
123 fprintf(logfile
, "%08x %d %d => %08x %d (%d)\n", address
, rw
,
124 access_type
, *physical
, *prot
, ret
);
131 #if defined(CONFIG_USER_ONLY)
132 target_ulong
cpu_get_phys_page_debug(CPUState
*env
, target_ulong addr
)
137 target_ulong
cpu_get_phys_page_debug(CPUState
*env
, target_ulong addr
)
139 target_ulong phys_addr
;
142 if (get_physical_address(env
, &phys_addr
, &prot
, addr
, 0, ACCESS_INT
) != 0)
148 #if !defined(CONFIG_USER_ONLY)
150 #define MMUSUFFIX _mmu
151 #define GETPC() (__builtin_return_address(0))
154 #include "softmmu_template.h"
157 #include "softmmu_template.h"
160 #include "softmmu_template.h"
163 #include "softmmu_template.h"
165 void tlb_fill (target_ulong addr
, int is_write
, int is_user
, void *retaddr
)
167 TranslationBlock
*tb
;
172 /* XXX: hack to restore env in all cases, even if not called from
175 env
= cpu_single_env
;
176 ret
= cpu_mips_handle_mmu_fault(env
, addr
, is_write
, is_user
, 1);
179 /* now we have a real cpu fault */
180 pc
= (unsigned long)retaddr
;
183 /* the PC is inside the translated code. It means that we have
184 a virtual CPU fault */
185 cpu_restore_state(tb
, env
, pc
, NULL
);
188 do_raise_exception_err(env
->exception_index
, env
->error_code
);
193 void cpu_mips_init_mmu (CPUState
*env
)
197 #endif /* !defined(CONFIG_USER_ONLY) */
199 int cpu_mips_handle_mmu_fault (CPUState
*env
, target_ulong address
, int rw
,
200 int is_user
, int is_softmmu
)
202 target_ulong physical
;
204 int exception
= 0, error_code
= 0;
209 cpu_dump_state(env
, logfile
, fprintf
, 0);
210 fprintf(logfile
, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n",
211 __func__
, env
->PC
, address
, rw
, is_user
, is_softmmu
);
214 /* XXX: put correct access by using cpu_restore_state()
216 access_type
= ACCESS_INT
;
217 if (env
->user_mode_only
) {
218 /* user mode only emulation */
222 ret
= get_physical_address(env
, &physical
, &prot
,
223 address
, rw
, access_type
);
225 fprintf(logfile
, "%s address=%08x ret %d physical %08x prot %d\n",
226 __func__
, address
, ret
, physical
, prot
);
229 ret
= tlb_set_page(env
, address
& ~0xFFF, physical
& ~0xFFF, prot
,
230 is_user
, is_softmmu
);
231 } else if (ret
< 0) {
236 /* Reference to kernel address from user mode or supervisor mode */
237 /* Reference to supervisor address from user mode */
239 exception
= EXCP_AdES
;
241 exception
= EXCP_AdEL
;
244 /* No TLB match for a mapped address */
246 exception
= EXCP_TLBS
;
248 exception
= EXCP_TLBL
;
252 /* TLB match with no valid bit */
254 exception
= EXCP_TLBS
;
256 exception
= EXCP_TLBL
;
260 /* TLB match but 'D' bit is cleared */
261 exception
= EXCP_LTLBL
;
266 exception
= EXCP_AdEL
;
268 /* Raise exception */
269 env
->CP0_BadVAddr
= address
;
271 (env
->CP0_Context
& 0x00000FFF) | (address
& 0xFFFFF000);
273 (env
->CP0_EntryHi
& 0x00000FFF) | (address
& 0xFFFFF000);
274 env
->exception_index
= exception
;
275 env
->error_code
= error_code
;
282 void do_interrupt (CPUState
*env
)
284 target_ulong pc
, offset
;
287 if (logfile
&& env
->exception_index
!= EXCP_EXT_INTERRUPT
) {
288 fprintf(logfile
, "%s enter: PC %08x EPC %08x cause %d excp %d\n",
289 __func__
, env
->PC
, env
->CP0_EPC
, cause
, env
->exception_index
);
291 if (env
->exception_index
== EXCP_EXT_INTERRUPT
&&
292 (env
->hflags
& MIPS_HFLAG_DM
))
293 env
->exception_index
= EXCP_DINT
;
295 switch (env
->exception_index
) {
297 env
->CP0_Debug
|= 1 << CP0DB_DSS
;
298 /* Debug single step cannot be raised inside a delay slot and
299 * resume will always occur on the next instruction
300 * (but we assume the pc has always been updated during
303 env
->CP0_DEPC
= env
->PC
;
304 goto enter_debug_mode
;
306 env
->CP0_Debug
|= 1 << CP0DB_DINT
;
309 env
->CP0_Debug
|= 1 << CP0DB_DIB
;
312 env
->CP0_Debug
|= 1 << CP0DB_DBp
;
315 env
->CP0_Debug
|= 1 << CP0DB_DDBS
;
318 env
->CP0_Debug
|= 1 << CP0DB_DDBL
;
321 if (env
->hflags
& MIPS_HFLAG_DS
) {
322 /* If the exception was raised from a delay slot,
323 * come back to the jump
325 env
->CP0_DEPC
= env
->PC
- 4;
327 env
->CP0_DEPC
= env
->PC
;
330 env
->hflags
|= MIPS_HFLAG_DM
;
331 /* EJTAG probe trap enable is not implemented... */
335 #if defined (MIPS_USES_R4K_TLB)
336 env
->CP0_random
= MIPS_TLB_NB
- 1;
339 env
->CP0_Config0
= MIPS_CONFIG0
;
340 #if defined (MIPS_CONFIG1)
341 env
->CP0_Config1
= MIPS_CONFIG1
;
343 #if defined (MIPS_CONFIG2)
344 env
->CP0_Config2
= MIPS_CONFIG2
;
346 #if defined (MIPS_CONFIG3)
347 env
->CP0_Config3
= MIPS_CONFIG3
;
349 env
->CP0_WatchLo
= 0;
350 env
->CP0_Status
= (1 << CP0St_CU0
) | (1 << CP0St_BEV
);
353 env
->CP0_Status
= (1 << CP0St_CU0
) | (1 << CP0St_BEV
) |
355 env
->CP0_WatchLo
= 0;
358 env
->CP0_Status
= (1 << CP0St_CU0
) | (1 << CP0St_BEV
) |
361 env
->hflags
= MIPS_HFLAG_ERL
;
362 if (env
->hflags
& MIPS_HFLAG_DS
) {
363 /* If the exception was raised from a delay slot,
364 * come back to the jump
366 env
->CP0_ErrorEPC
= env
->PC
- 4;
368 env
->CP0_ErrorEPC
= env
->PC
;
375 case EXCP_EXT_INTERRUPT
:
377 if (env
->CP0_Cause
& (1 << CP0Ca_IV
))
382 /* XXX: TODO: manage defered watch exceptions */
391 if (env
->error_code
== 1 && !(env
->hflags
& MIPS_HFLAG_EXL
))
411 /* XXX: fill in the faulty unit number */
425 if (env
->CP0_Status
& (1 << CP0St_BEV
)) {
430 env
->hflags
|= MIPS_HFLAG_EXL
;
432 env
->CP0_Cause
= (env
->CP0_Cause
& ~0x7C) | (cause
<< 2);
433 if (env
->hflags
& MIPS_HFLAG_DS
) {
434 /* If the exception was raised from a delay slot,
435 * come back to the jump
437 env
->CP0_EPC
= env
->PC
- 4;
438 env
->CP0_Cause
|= 0x80000000;
440 env
->CP0_EPC
= env
->PC
;
441 env
->CP0_Cause
&= ~0x80000000;
446 fprintf(logfile
, "Invalid MIPS exception %d. Exiting\n",
447 env
->exception_index
);
449 printf("Invalid MIPS exception %d. Exiting\n", env
->exception_index
);
453 if (logfile
&& env
->exception_index
!= EXCP_EXT_INTERRUPT
) {
454 fprintf(logfile
, "%s: PC %08x EPC %08x cause %d excp %d\n"
455 " S %08x C %08x A %08x D %08x\n",
456 __func__
, env
->PC
, env
->CP0_EPC
, cause
, env
->exception_index
,
457 env
->CP0_Status
, env
->CP0_Cause
, env
->CP0_BadVAddr
,
460 env
->exception_index
= EXCP_NONE
;