]>
git.proxmox.com Git - 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
31 /* MIPS32 4K MMU emulation */
32 #ifdef MIPS_USES_R4K_TLB
33 static int map_address (CPUState
*env
, target_ulong
*physical
, int *prot
,
34 target_ulong address
, int rw
, int access_type
)
43 tag
= address
& 0xFFFFE000;
44 ASID
= env
->CP0_EntryHi
& 0xFF;
45 for (i
= 0; i
< MIPS_TLB_NB
; i
++) {
47 /* Check ASID, virtual page number & size */
48 if ((tlb
->G
== 1 || tlb
->ASID
== ASID
) &&
49 tlb
->VPN
== tag
&& address
< tlb
->end2
) {
51 n
= (address
>> 12) & 1;
52 /* Check access rights */
53 if (!(n
? tlb
->V1
: tlb
->V0
))
55 if (rw
== 0 || (n
? tlb
->D1
: tlb
->D0
)) {
56 *physical
= tlb
->PFN
[n
] | (address
& 0xFFF);
58 if (n
? tlb
->D1
: tlb
->D0
)
70 int get_physical_address (CPUState
*env
, target_ulong
*physical
, int *prot
,
71 target_ulong address
, int rw
, int access_type
)
76 /* User mode can only access useg */
77 user_mode
= (env
->hflags
& MIPS_HFLAG_MODE
) == MIPS_HFLAG_UM
;
80 fprintf(logfile
, "user mode %d h %08x\n",
81 user_mode
, env
->hflags
);
84 if (user_mode
&& address
> 0x7FFFFFFFUL
)
87 if (address
< 0x80000000UL
) {
88 if (!(env
->hflags
& MIPS_HFLAG_ERL
)) {
89 #ifdef MIPS_USES_R4K_TLB
90 ret
= map_address(env
, physical
, prot
, address
, rw
, access_type
);
92 *physical
= address
+ 0x40000000UL
;
93 *prot
= PAGE_READ
| PAGE_WRITE
;
97 *prot
= PAGE_READ
| PAGE_WRITE
;
99 } else if (address
< 0xA0000000UL
) {
101 /* XXX: check supervisor mode */
102 *physical
= address
- 0x80000000UL
;
103 *prot
= PAGE_READ
| PAGE_WRITE
;
104 } else if (address
< 0xC0000000UL
) {
106 /* XXX: check supervisor mode */
107 *physical
= address
- 0xA0000000UL
;
108 *prot
= PAGE_READ
| PAGE_WRITE
;
109 } else if (address
< 0xE0000000UL
) {
111 #ifdef MIPS_USES_R4K_TLB
112 ret
= map_address(env
, physical
, prot
, address
, rw
, access_type
);
115 *prot
= PAGE_READ
| PAGE_WRITE
;
119 /* XXX: check supervisor mode */
120 /* XXX: debug segment is not emulated */
121 #ifdef MIPS_USES_R4K_TLB
122 ret
= map_address(env
, physical
, prot
, address
, rw
, access_type
);
125 *prot
= PAGE_READ
| PAGE_WRITE
;
130 fprintf(logfile
, "%08x %d %d => %08x %d (%d)\n", address
, rw
,
131 access_type
, *physical
, *prot
, ret
);
138 #if defined(CONFIG_USER_ONLY)
139 target_ulong
cpu_get_phys_page_debug(CPUState
*env
, target_ulong addr
)
144 target_ulong
cpu_get_phys_page_debug(CPUState
*env
, target_ulong addr
)
146 target_ulong phys_addr
;
149 if (get_physical_address(env
, &phys_addr
, &prot
, addr
, 0, ACCESS_INT
) != 0)
154 void cpu_mips_init_mmu (CPUState
*env
)
157 #endif /* !defined(CONFIG_USER_ONLY) */
159 int cpu_mips_handle_mmu_fault (CPUState
*env
, target_ulong address
, int rw
,
160 int is_user
, int is_softmmu
)
162 target_ulong physical
;
164 int exception
= 0, error_code
= 0;
170 cpu_dump_state(env
, logfile
, fprintf
, 0);
172 fprintf(logfile
, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n",
173 __func__
, env
->PC
, address
, rw
, is_user
, is_softmmu
);
179 /* XXX: put correct access by using cpu_restore_state()
181 access_type
= ACCESS_INT
;
182 if (env
->user_mode_only
) {
183 /* user mode only emulation */
187 ret
= get_physical_address(env
, &physical
, &prot
,
188 address
, rw
, access_type
);
190 fprintf(logfile
, "%s address=%08x ret %d physical %08x prot %d\n",
191 __func__
, address
, ret
, physical
, prot
);
194 ret
= tlb_set_page(env
, address
& ~0xFFF, physical
& ~0xFFF, prot
,
195 is_user
, is_softmmu
);
196 } else if (ret
< 0) {
201 /* Reference to kernel address from user mode or supervisor mode */
202 /* Reference to supervisor address from user mode */
204 exception
= EXCP_AdES
;
206 exception
= EXCP_AdEL
;
209 /* No TLB match for a mapped address */
211 exception
= EXCP_TLBS
;
213 exception
= EXCP_TLBL
;
217 /* TLB match with no valid bit */
219 exception
= EXCP_TLBS
;
221 exception
= EXCP_TLBL
;
224 /* TLB match but 'D' bit is cleared */
225 exception
= EXCP_LTLBL
;
229 /* Raise exception */
230 env
->CP0_BadVAddr
= address
;
231 env
->CP0_Context
= (env
->CP0_Context
& 0xff800000) |
232 ((address
>> 9) & 0x007ffff0);
234 (env
->CP0_EntryHi
& 0xFF) | (address
& 0xFFFFF000);
235 env
->exception_index
= exception
;
236 env
->error_code
= error_code
;
243 void do_interrupt (CPUState
*env
)
245 target_ulong pc
, offset
;
248 if (logfile
&& env
->exception_index
!= EXCP_EXT_INTERRUPT
) {
249 fprintf(logfile
, "%s enter: PC %08x EPC %08x cause %d excp %d\n",
250 __func__
, env
->PC
, env
->CP0_EPC
, cause
, env
->exception_index
);
252 if (env
->exception_index
== EXCP_EXT_INTERRUPT
&&
253 (env
->hflags
& MIPS_HFLAG_DM
))
254 env
->exception_index
= EXCP_DINT
;
256 switch (env
->exception_index
) {
258 env
->CP0_Debug
|= 1 << CP0DB_DSS
;
259 /* Debug single step cannot be raised inside a delay slot and
260 * resume will always occur on the next instruction
261 * (but we assume the pc has always been updated during
264 env
->CP0_DEPC
= env
->PC
;
265 goto enter_debug_mode
;
267 env
->CP0_Debug
|= 1 << CP0DB_DINT
;
270 env
->CP0_Debug
|= 1 << CP0DB_DIB
;
273 env
->CP0_Debug
|= 1 << CP0DB_DBp
;
276 env
->CP0_Debug
|= 1 << CP0DB_DDBS
;
279 env
->CP0_Debug
|= 1 << CP0DB_DDBL
;
282 if (env
->hflags
& MIPS_HFLAG_BMASK
) {
283 /* If the exception was raised from a delay slot,
284 * come back to the jump
286 env
->CP0_DEPC
= env
->PC
- 4;
287 env
->hflags
&= ~MIPS_HFLAG_BMASK
;
289 env
->CP0_DEPC
= env
->PC
;
292 env
->hflags
|= MIPS_HFLAG_DM
;
293 /* EJTAG probe trap enable is not implemented... */
297 #ifdef MIPS_USES_R4K_TLB
298 env
->CP0_random
= MIPS_TLB_NB
- 1;
301 env
->CP0_Config0
= MIPS_CONFIG0
;
302 #if defined (MIPS_CONFIG1)
303 env
->CP0_Config1
= MIPS_CONFIG1
;
305 #if defined (MIPS_CONFIG2)
306 env
->CP0_Config2
= MIPS_CONFIG2
;
308 #if defined (MIPS_CONFIG3)
309 env
->CP0_Config3
= MIPS_CONFIG3
;
311 env
->CP0_WatchLo
= 0;
312 env
->CP0_Status
= (1 << CP0St_CU0
) | (1 << CP0St_BEV
);
315 env
->CP0_Status
= (1 << CP0St_CU0
) | (1 << CP0St_BEV
) |
317 env
->CP0_WatchLo
= 0;
320 env
->CP0_Status
= (1 << CP0St_CU0
) | (1 << CP0St_BEV
) |
323 if (env
->hflags
& MIPS_HFLAG_BMASK
) {
324 /* If the exception was raised from a delay slot,
325 * come back to the jump
327 env
->CP0_ErrorEPC
= env
->PC
- 4;
328 env
->hflags
&= ~MIPS_HFLAG_BMASK
;
330 env
->CP0_ErrorEPC
= env
->PC
;
332 env
->hflags
= MIPS_HFLAG_ERL
;
338 case EXCP_EXT_INTERRUPT
:
340 if (env
->CP0_Cause
& (1 << CP0Ca_IV
))
345 /* XXX: TODO: manage defered watch exceptions */
353 if (env
->error_code
== 1 && !(env
->hflags
& MIPS_HFLAG_EXL
))
373 env
->CP0_Cause
= (env
->CP0_Cause
& ~0x03000000) | (env
->error_code
<< 28);
386 if (env
->error_code
== 1 && !(env
->hflags
& MIPS_HFLAG_EXL
))
390 if (env
->CP0_Status
& (1 << CP0St_BEV
)) {
395 env
->hflags
|= MIPS_HFLAG_EXL
;
397 env
->CP0_Cause
= (env
->CP0_Cause
& ~0x7C) | (cause
<< 2);
398 if (env
->hflags
& MIPS_HFLAG_BMASK
) {
399 /* If the exception was raised from a delay slot,
400 * come back to the jump
402 env
->CP0_EPC
= env
->PC
- 4;
403 env
->CP0_Cause
|= 0x80000000;
404 env
->hflags
&= ~MIPS_HFLAG_BMASK
;
406 env
->CP0_EPC
= env
->PC
;
407 env
->CP0_Cause
&= ~0x80000000;
412 fprintf(logfile
, "Invalid MIPS exception %d. Exiting\n",
413 env
->exception_index
);
415 printf("Invalid MIPS exception %d. Exiting\n", env
->exception_index
);
419 if (logfile
&& env
->exception_index
!= EXCP_EXT_INTERRUPT
) {
420 fprintf(logfile
, "%s: PC %08x EPC %08x cause %d excp %d\n"
421 " S %08x C %08x A %08x D %08x\n",
422 __func__
, env
->PC
, env
->CP0_EPC
, cause
, env
->exception_index
,
423 env
->CP0_Status
, env
->CP0_Cause
, env
->CP0_BadVAddr
,
426 env
->exception_index
= EXCP_NONE
;