]>
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
< (int32_t)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
< (int32_t)0xA0000000UL
) {
103 /* XXX: check supervisor mode */
104 *physical
= address
- (int32_t)0x80000000UL
;
105 *prot
= PAGE_READ
| PAGE_WRITE
;
106 } else if (address
< (int32_t)0xC0000000UL
) {
108 /* XXX: check supervisor mode */
109 *physical
= address
- (int32_t)0xA0000000UL
;
110 *prot
= PAGE_READ
| PAGE_WRITE
;
111 } else if (address
< (int32_t)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
, TLSZ
" %d %d => " TLSZ
" %d (%d)\n",
133 address
, rw
, 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 " TLSZ
" ad " TLSZ
" 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=" TLSZ
" ret %d physical " TLSZ
" 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 #if defined(CONFIG_USER_ONLY)
247 void do_interrupt (CPUState
*env
)
249 env
->exception_index
= EXCP_NONE
;
252 void do_interrupt (CPUState
*env
)
257 if (logfile
&& env
->exception_index
!= EXCP_EXT_INTERRUPT
) {
258 fprintf(logfile
, "%s enter: PC " TLSZ
" EPC " TLSZ
" cause %d excp %d\n",
259 __func__
, env
->PC
, env
->CP0_EPC
, cause
, env
->exception_index
);
261 if (env
->exception_index
== EXCP_EXT_INTERRUPT
&&
262 (env
->hflags
& MIPS_HFLAG_DM
))
263 env
->exception_index
= EXCP_DINT
;
265 switch (env
->exception_index
) {
267 env
->CP0_Debug
|= 1 << CP0DB_DSS
;
268 /* Debug single step cannot be raised inside a delay slot and
269 * resume will always occur on the next instruction
270 * (but we assume the pc has always been updated during
273 env
->CP0_DEPC
= env
->PC
;
274 goto enter_debug_mode
;
276 env
->CP0_Debug
|= 1 << CP0DB_DINT
;
279 env
->CP0_Debug
|= 1 << CP0DB_DIB
;
282 env
->CP0_Debug
|= 1 << CP0DB_DBp
;
285 env
->CP0_Debug
|= 1 << CP0DB_DDBS
;
288 env
->CP0_Debug
|= 1 << CP0DB_DDBL
;
291 if (env
->hflags
& MIPS_HFLAG_BMASK
) {
292 /* If the exception was raised from a delay slot,
293 come back to the jump. */
294 env
->CP0_DEPC
= env
->PC
- 4;
295 env
->hflags
&= ~MIPS_HFLAG_BMASK
;
297 env
->CP0_DEPC
= env
->PC
;
300 env
->hflags
|= MIPS_HFLAG_DM
;
301 /* EJTAG probe trap enable is not implemented... */
302 env
->PC
= (int32_t)0xBFC00480;
308 env
->CP0_Status
= (1 << CP0St_SR
);
309 env
->CP0_WatchLo
= 0;
312 env
->CP0_Status
= (1 << CP0St_NMI
);
314 if (env
->hflags
& MIPS_HFLAG_BMASK
) {
315 /* If the exception was raised from a delay slot,
316 come back to the jump. */
317 env
->CP0_ErrorEPC
= env
->PC
- 4;
318 env
->hflags
&= ~MIPS_HFLAG_BMASK
;
320 env
->CP0_ErrorEPC
= env
->PC
;
322 env
->hflags
|= MIPS_HFLAG_ERL
;
323 env
->CP0_Status
|= (1 << CP0St_ERL
) | (1 << CP0St_BEV
);
324 env
->PC
= (int32_t)0xBFC00000;
329 case EXCP_EXT_INTERRUPT
:
331 if (env
->CP0_Cause
& (1 << CP0Ca_IV
))
336 /* XXX: TODO: manage defered watch exceptions */
344 if (env
->error_code
== 1 && !(env
->hflags
& MIPS_HFLAG_EXL
))
364 env
->CP0_Cause
= (env
->CP0_Cause
& ~0x03000000) | (env
->error_code
<< 28);
377 if (env
->error_code
== 1 && !(env
->hflags
& MIPS_HFLAG_EXL
))
381 if (env
->hflags
& MIPS_HFLAG_BMASK
) {
382 /* If the exception was raised from a delay slot,
383 come back to the jump. */
384 env
->CP0_EPC
= env
->PC
- 4;
385 env
->CP0_Cause
|= 0x80000000;
386 env
->hflags
&= ~MIPS_HFLAG_BMASK
;
388 env
->CP0_EPC
= env
->PC
;
389 env
->CP0_Cause
&= ~0x80000000;
391 if (env
->CP0_Status
& (1 << CP0St_BEV
)) {
392 env
->PC
= (int32_t)0xBFC00200;
394 env
->PC
= (int32_t)0x80000000;
396 env
->hflags
|= MIPS_HFLAG_EXL
;
397 env
->CP0_Status
|= (1 << CP0St_EXL
);
399 env
->CP0_Cause
= (env
->CP0_Cause
& ~0x7C) | (cause
<< 2);
403 fprintf(logfile
, "Invalid MIPS exception %d. Exiting\n",
404 env
->exception_index
);
406 printf("Invalid MIPS exception %d. Exiting\n", env
->exception_index
);
409 if (logfile
&& env
->exception_index
!= EXCP_EXT_INTERRUPT
) {
410 fprintf(logfile
, "%s: PC " TLSZ
" EPC " TLSZ
" cause %d excp %d\n"
411 " S %08x C %08x A " TLSZ
" D " TLSZ
"\n",
412 __func__
, env
->PC
, env
->CP0_EPC
, cause
, env
->exception_index
,
413 env
->CP0_Status
, env
->CP0_Cause
, env
->CP0_BadVAddr
,
416 env
->exception_index
= EXCP_NONE
;
418 #endif /* !defined(CONFIG_USER_ONLY) */