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
& 0x000000FF;
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
) ? 1 : 0;
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
;
225 /* TLB match but 'D' bit is cleared */
226 exception
= EXCP_LTLBL
;
230 /* Raise exception */
231 env
->CP0_BadVAddr
= address
;
232 env
->CP0_Context
= (env
->CP0_Context
& 0xff800000) |
233 ((address
>> 9) & 0x007ffff0);
235 (env
->CP0_EntryHi
& 0x000000FF) | (address
& 0xFFFFF000);
236 env
->exception_index
= exception
;
237 env
->error_code
= error_code
;
244 void do_interrupt (CPUState
*env
)
246 target_ulong pc
, offset
;
249 if (logfile
&& env
->exception_index
!= EXCP_EXT_INTERRUPT
) {
250 fprintf(logfile
, "%s enter: PC %08x EPC %08x cause %d excp %d\n",
251 __func__
, env
->PC
, env
->CP0_EPC
, cause
, env
->exception_index
);
253 if (env
->exception_index
== EXCP_EXT_INTERRUPT
&&
254 (env
->hflags
& MIPS_HFLAG_DM
))
255 env
->exception_index
= EXCP_DINT
;
257 switch (env
->exception_index
) {
259 env
->CP0_Debug
|= 1 << CP0DB_DSS
;
260 /* Debug single step cannot be raised inside a delay slot and
261 * resume will always occur on the next instruction
262 * (but we assume the pc has always been updated during
265 env
->CP0_DEPC
= env
->PC
;
266 goto enter_debug_mode
;
268 env
->CP0_Debug
|= 1 << CP0DB_DINT
;
271 env
->CP0_Debug
|= 1 << CP0DB_DIB
;
274 env
->CP0_Debug
|= 1 << CP0DB_DBp
;
277 env
->CP0_Debug
|= 1 << CP0DB_DDBS
;
280 env
->CP0_Debug
|= 1 << CP0DB_DDBL
;
283 if (env
->hflags
& MIPS_HFLAG_BMASK
) {
284 /* If the exception was raised from a delay slot,
285 * come back to the jump
287 env
->CP0_DEPC
= env
->PC
- 4;
288 env
->hflags
&= ~MIPS_HFLAG_BMASK
;
290 env
->CP0_DEPC
= env
->PC
;
293 env
->hflags
|= MIPS_HFLAG_DM
;
294 /* EJTAG probe trap enable is not implemented... */
298 #ifdef MIPS_USES_R4K_TLB
299 env
->CP0_random
= MIPS_TLB_NB
- 1;
302 env
->CP0_Config0
= MIPS_CONFIG0
;
303 #if defined (MIPS_CONFIG1)
304 env
->CP0_Config1
= MIPS_CONFIG1
;
306 #if defined (MIPS_CONFIG2)
307 env
->CP0_Config2
= MIPS_CONFIG2
;
309 #if defined (MIPS_CONFIG3)
310 env
->CP0_Config3
= MIPS_CONFIG3
;
312 env
->CP0_WatchLo
= 0;
313 env
->CP0_Status
= (1 << CP0St_CU0
) | (1 << CP0St_BEV
);
316 env
->CP0_Status
= (1 << CP0St_CU0
) | (1 << CP0St_BEV
) |
318 env
->CP0_WatchLo
= 0;
321 env
->CP0_Status
= (1 << CP0St_CU0
) | (1 << CP0St_BEV
) |
324 if (env
->hflags
& MIPS_HFLAG_BMASK
) {
325 /* If the exception was raised from a delay slot,
326 * come back to the jump
328 env
->CP0_ErrorEPC
= env
->PC
- 4;
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 */
354 if (env
->error_code
== 1 && !(env
->hflags
& MIPS_HFLAG_EXL
))
374 env
->CP0_Cause
= (env
->CP0_Cause
& ~0x03000000) | (env
->error_code
<< 28);
387 if (env
->error_code
== 1 && !(env
->hflags
& MIPS_HFLAG_EXL
))
391 if (env
->CP0_Status
& (1 << CP0St_BEV
)) {
396 env
->hflags
|= MIPS_HFLAG_EXL
;
398 env
->CP0_Cause
= (env
->CP0_Cause
& ~0x7C) | (cause
<< 2);
399 if (env
->hflags
& MIPS_HFLAG_BMASK
) {
400 /* If the exception was raised from a delay slot,
401 * come back to the jump
403 env
->CP0_EPC
= env
->PC
- 4;
404 env
->CP0_Cause
|= 0x80000000;
405 env
->hflags
&= ~MIPS_HFLAG_BMASK
;
407 env
->CP0_EPC
= env
->PC
;
408 env
->CP0_Cause
&= ~0x80000000;
413 fprintf(logfile
, "Invalid MIPS exception %d. Exiting\n",
414 env
->exception_index
);
416 printf("Invalid MIPS exception %d. Exiting\n", env
->exception_index
);
420 if (logfile
&& env
->exception_index
!= EXCP_EXT_INTERRUPT
) {
421 fprintf(logfile
, "%s: PC %08x EPC %08x cause %d excp %d\n"
422 " S %08x C %08x A %08x D %08x\n",
423 __func__
, env
->PC
, env
->CP0_EPC
, cause
, env
->exception_index
,
424 env
->CP0_Status
, env
->CP0_Cause
, env
->CP0_BadVAddr
,
427 env
->exception_index
= EXCP_NONE
;