]>
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
39 /* MIPS32 4K MMU emulation */
40 #ifdef MIPS_USES_R4K_TLB
41 static int map_address (CPUState
*env
, target_ulong
*physical
, int *prot
,
42 target_ulong address
, int rw
, int access_type
)
44 target_ulong tag
= address
& (TARGET_PAGE_MASK
<< 1);
45 uint8_t ASID
= env
->CP0_EntryHi
& 0xFF;
49 for (i
= 0; i
< env
->tlb_in_use
; i
++) {
51 /* Check ASID, virtual page number & size */
52 if ((tlb
->G
== 1 || tlb
->ASID
== ASID
) &&
53 tlb
->VPN
== tag
&& address
< tlb
->end2
) {
55 n
= (address
>> TARGET_PAGE_BITS
) & 1;
56 /* Check access rights */
57 if (!(n
? tlb
->V1
: tlb
->V0
))
58 return TLBRET_INVALID
;
59 if (rw
== 0 || (n
? tlb
->D1
: tlb
->D0
)) {
60 *physical
= tlb
->PFN
[n
] | (address
& ~TARGET_PAGE_MASK
);
62 if (n
? tlb
->D1
: tlb
->D0
)
69 return TLBRET_NOMATCH
;
73 static int get_physical_address (CPUState
*env
, target_ulong
*physical
,
74 int *prot
, target_ulong address
,
75 int rw
, int access_type
)
77 /* User mode can only access useg */
78 int user_mode
= (env
->hflags
& MIPS_HFLAG_MODE
) == MIPS_HFLAG_UM
;
79 int ret
= TLBRET_MATCH
;
83 fprintf(logfile
, "user mode %d h %08x\n",
84 user_mode
, env
->hflags
);
87 if (user_mode
&& address
> 0x7FFFFFFFUL
)
88 return TLBRET_BADADDR
;
89 if (address
< 0x80000000UL
) {
90 if (!(env
->hflags
& MIPS_HFLAG_ERL
)) {
91 #ifdef MIPS_USES_R4K_TLB
92 ret
= map_address(env
, physical
, prot
, address
, rw
, access_type
);
94 *physical
= address
+ 0x40000000UL
;
95 *prot
= PAGE_READ
| PAGE_WRITE
;
99 *prot
= PAGE_READ
| PAGE_WRITE
;
101 } else if (address
< 0xA0000000UL
) {
103 /* XXX: check supervisor mode */
104 *physical
= address
- 0x80000000UL
;
105 *prot
= PAGE_READ
| PAGE_WRITE
;
106 } else if (address
< 0xC0000000UL
) {
108 /* XXX: check supervisor mode */
109 *physical
= address
- 0xA0000000UL
;
110 *prot
= PAGE_READ
| PAGE_WRITE
;
111 } else if (address
< 0xE0000000UL
) {
113 #ifdef MIPS_USES_R4K_TLB
114 ret
= map_address(env
, physical
, prot
, address
, rw
, access_type
);
117 *prot
= PAGE_READ
| PAGE_WRITE
;
121 /* XXX: check supervisor mode */
122 /* XXX: debug segment is not emulated */
123 #ifdef MIPS_USES_R4K_TLB
124 ret
= map_address(env
, physical
, prot
, address
, rw
, access_type
);
127 *prot
= PAGE_READ
| PAGE_WRITE
;
132 fprintf(logfile
, "%08x %d %d => %08x %d (%d)\n", address
, rw
,
133 access_type
, *physical
, *prot
, ret
);
140 #if defined(CONFIG_USER_ONLY)
141 target_ulong
cpu_get_phys_page_debug(CPUState
*env
, target_ulong addr
)
146 target_ulong
cpu_get_phys_page_debug(CPUState
*env
, target_ulong addr
)
148 target_ulong phys_addr
;
151 if (get_physical_address(env
, &phys_addr
, &prot
, addr
, 0, ACCESS_INT
) != 0)
156 void cpu_mips_init_mmu (CPUState
*env
)
159 #endif /* !defined(CONFIG_USER_ONLY) */
161 int cpu_mips_handle_mmu_fault (CPUState
*env
, target_ulong address
, int rw
,
162 int is_user
, int is_softmmu
)
164 target_ulong physical
;
166 int exception
= 0, error_code
= 0;
172 cpu_dump_state(env
, logfile
, fprintf
, 0);
174 fprintf(logfile
, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n",
175 __func__
, env
->PC
, address
, rw
, is_user
, is_softmmu
);
181 /* XXX: put correct access by using cpu_restore_state()
183 access_type
= ACCESS_INT
;
184 if (env
->user_mode_only
) {
185 /* user mode only emulation */
186 ret
= TLBRET_NOMATCH
;
189 ret
= get_physical_address(env
, &physical
, &prot
,
190 address
, rw
, access_type
);
192 fprintf(logfile
, "%s address=%08x ret %d physical %08x prot %d\n",
193 __func__
, address
, ret
, physical
, prot
);
195 if (ret
== TLBRET_MATCH
) {
196 ret
= tlb_set_page(env
, address
& TARGET_PAGE_MASK
,
197 physical
& TARGET_PAGE_MASK
, prot
,
198 is_user
, is_softmmu
);
199 } else if (ret
< 0) {
204 /* Reference to kernel address from user mode or supervisor mode */
205 /* Reference to supervisor address from user mode */
207 exception
= EXCP_AdES
;
209 exception
= EXCP_AdEL
;
212 /* No TLB match for a mapped address */
214 exception
= EXCP_TLBS
;
216 exception
= EXCP_TLBL
;
220 /* TLB match with no valid bit */
222 exception
= EXCP_TLBS
;
224 exception
= EXCP_TLBL
;
227 /* TLB match but 'D' bit is cleared */
228 exception
= EXCP_LTLBL
;
232 /* Raise exception */
233 env
->CP0_BadVAddr
= address
;
234 env
->CP0_Context
= (env
->CP0_Context
& 0xff800000) |
235 ((address
>> 9) & 0x007ffff0);
237 (env
->CP0_EntryHi
& 0xFF) | (address
& (TARGET_PAGE_MASK
<< 1));
238 env
->exception_index
= exception
;
239 env
->error_code
= error_code
;
246 void do_interrupt (CPUState
*env
)
251 if (logfile
&& env
->exception_index
!= EXCP_EXT_INTERRUPT
) {
252 fprintf(logfile
, "%s enter: PC %08x EPC %08x cause %d excp %d\n",
253 __func__
, env
->PC
, env
->CP0_EPC
, cause
, env
->exception_index
);
255 if (env
->exception_index
== EXCP_EXT_INTERRUPT
&&
256 (env
->hflags
& MIPS_HFLAG_DM
))
257 env
->exception_index
= EXCP_DINT
;
259 switch (env
->exception_index
) {
261 env
->CP0_Debug
|= 1 << CP0DB_DSS
;
262 /* Debug single step cannot be raised inside a delay slot and
263 * resume will always occur on the next instruction
264 * (but we assume the pc has always been updated during
267 env
->CP0_DEPC
= env
->PC
;
268 goto enter_debug_mode
;
270 env
->CP0_Debug
|= 1 << CP0DB_DINT
;
273 env
->CP0_Debug
|= 1 << CP0DB_DIB
;
276 env
->CP0_Debug
|= 1 << CP0DB_DBp
;
279 env
->CP0_Debug
|= 1 << CP0DB_DDBS
;
282 env
->CP0_Debug
|= 1 << CP0DB_DDBL
;
285 if (env
->hflags
& MIPS_HFLAG_BMASK
) {
286 /* If the exception was raised from a delay slot,
287 come back to the jump. */
288 env
->CP0_DEPC
= env
->PC
- 4;
289 env
->hflags
&= ~MIPS_HFLAG_BMASK
;
291 env
->CP0_DEPC
= env
->PC
;
294 env
->hflags
|= MIPS_HFLAG_DM
;
295 /* EJTAG probe trap enable is not implemented... */
296 env
->PC
= 0xBFC00480;
302 env
->CP0_Status
= (1 << CP0St_SR
);
303 env
->CP0_WatchLo
= 0;
306 env
->CP0_Status
= (1 << CP0St_NMI
);
308 if (env
->hflags
& MIPS_HFLAG_BMASK
) {
309 /* If the exception was raised from a delay slot,
310 come back to the jump. */
311 env
->CP0_ErrorEPC
= env
->PC
- 4;
312 env
->hflags
&= ~MIPS_HFLAG_BMASK
;
314 env
->CP0_ErrorEPC
= env
->PC
;
316 env
->hflags
|= MIPS_HFLAG_ERL
;
317 env
->CP0_Status
|= (1 << CP0St_ERL
) | (1 << CP0St_BEV
);
318 env
->PC
= 0xBFC00000;
323 case EXCP_EXT_INTERRUPT
:
325 if (env
->CP0_Cause
& (1 << CP0Ca_IV
))
330 /* XXX: TODO: manage defered watch exceptions */
338 if (env
->error_code
== 1 && !(env
->hflags
& MIPS_HFLAG_EXL
))
358 env
->CP0_Cause
= (env
->CP0_Cause
& ~0x03000000) | (env
->error_code
<< 28);
371 if (env
->error_code
== 1 && !(env
->hflags
& MIPS_HFLAG_EXL
))
375 if (env
->hflags
& MIPS_HFLAG_BMASK
) {
376 /* If the exception was raised from a delay slot,
377 come back to the jump. */
378 env
->CP0_EPC
= env
->PC
- 4;
379 env
->CP0_Cause
|= 0x80000000;
380 env
->hflags
&= ~MIPS_HFLAG_BMASK
;
382 env
->CP0_EPC
= env
->PC
;
383 env
->CP0_Cause
&= ~0x80000000;
385 if (env
->CP0_Status
& (1 << CP0St_BEV
)) {
386 env
->PC
= 0xBFC00200;
388 env
->PC
= 0x80000000;
390 env
->hflags
|= MIPS_HFLAG_EXL
;
391 env
->CP0_Status
|= (1 << CP0St_EXL
);
393 env
->CP0_Cause
= (env
->CP0_Cause
& ~0x7C) | (cause
<< 2);
397 fprintf(logfile
, "Invalid MIPS exception %d. Exiting\n",
398 env
->exception_index
);
400 printf("Invalid MIPS exception %d. Exiting\n", env
->exception_index
);
403 if (logfile
&& env
->exception_index
!= EXCP_EXT_INTERRUPT
) {
404 fprintf(logfile
, "%s: PC %08x EPC %08x cause %d excp %d\n"
405 " S %08x C %08x A %08x D %08x\n",
406 __func__
, env
->PC
, env
->CP0_EPC
, cause
, env
->exception_index
,
407 env
->CP0_Status
, env
->CP0_Cause
, env
->CP0_BadVAddr
,
410 env
->exception_index
= EXCP_NONE
;