Revision 43057ab1 target-mips/helper.c
b/target-mips/helper.c | ||
---|---|---|
28 | 28 |
#include "cpu.h" |
29 | 29 |
#include "exec-all.h" |
30 | 30 |
|
31 |
enum { |
|
32 |
TLBRET_DIRTY = -4, |
|
33 |
TLBRET_INVALID = -3, |
|
34 |
TLBRET_NOMATCH = -2, |
|
35 |
TLBRET_BADADDR = -1, |
|
36 |
TLBRET_MATCH = 0 |
|
37 |
}; |
|
38 |
|
|
31 | 39 |
/* MIPS32 4K MMU emulation */ |
32 | 40 |
#ifdef MIPS_USES_R4K_TLB |
33 | 41 |
static int map_address (CPUState *env, target_ulong *physical, int *prot, |
34 | 42 |
target_ulong address, int rw, int access_type) |
35 | 43 |
{ |
44 |
target_ulong tag = address & (TARGET_PAGE_MASK << 1); |
|
45 |
uint8_t ASID = env->CP0_EntryHi & 0xFF; |
|
36 | 46 |
tlb_t *tlb; |
37 |
target_ulong tag; |
|
38 |
uint8_t ASID; |
|
39 | 47 |
int i, n; |
40 |
int ret; |
|
41 | 48 |
|
42 |
ret = -2; |
|
43 |
tag = address & 0xFFFFE000; |
|
44 |
ASID = env->CP0_EntryHi & 0xFF; |
|
45 | 49 |
for (i = 0; i < MIPS_TLB_NB; i++) { |
46 | 50 |
tlb = &env->tlb[i]; |
47 | 51 |
/* Check ASID, virtual page number & size */ |
48 | 52 |
if ((tlb->G == 1 || tlb->ASID == ASID) && |
49 | 53 |
tlb->VPN == tag && address < tlb->end2) { |
50 | 54 |
/* TLB match */ |
51 |
n = (address >> 12) & 1;
|
|
55 |
n = (address >> TARGET_PAGE_BITS) & 1;
|
|
52 | 56 |
/* Check access rights */ |
53 |
if (!(n ? tlb->V1 : tlb->V0))
|
|
54 |
return -3;
|
|
55 |
if (rw == 0 || (n ? tlb->D1 : tlb->D0)) {
|
|
56 |
*physical = tlb->PFN[n] | (address & 0xFFF);
|
|
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);
|
|
57 | 61 |
*prot = PAGE_READ; |
58 | 62 |
if (n ? tlb->D1 : tlb->D0) |
59 | 63 |
*prot |= PAGE_WRITE; |
60 |
return 0;
|
|
64 |
return TLBRET_MATCH;
|
|
61 | 65 |
} |
62 |
return -4;
|
|
66 |
return TLBRET_DIRTY;
|
|
63 | 67 |
} |
64 | 68 |
} |
65 |
|
|
66 |
return ret; |
|
69 |
return TLBRET_NOMATCH; |
|
67 | 70 |
} |
68 | 71 |
#endif |
69 | 72 |
|
70 |
int get_physical_address (CPUState *env, target_ulong *physical, int *prot, |
|
71 |
target_ulong address, int rw, int access_type) |
|
73 |
static int get_physical_address (CPUState *env, target_ulong *physical, |
|
74 |
int *prot, target_ulong address, |
|
75 |
int rw, int access_type) |
|
72 | 76 |
{ |
73 |
int user_mode; |
|
74 |
int ret; |
|
75 |
|
|
76 | 77 |
/* User mode can only access useg */ |
77 |
user_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM; |
|
78 |
int user_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM; |
|
79 |
int ret = TLBRET_MATCH; |
|
80 |
|
|
78 | 81 |
#if 0 |
79 | 82 |
if (logfile) { |
80 | 83 |
fprintf(logfile, "user mode %d h %08x\n", |
... | ... | |
82 | 85 |
} |
83 | 86 |
#endif |
84 | 87 |
if (user_mode && address > 0x7FFFFFFFUL) |
85 |
return -1; |
|
86 |
ret = 0; |
|
88 |
return TLBRET_BADADDR; |
|
87 | 89 |
if (address < 0x80000000UL) { |
88 | 90 |
if (!(env->hflags & MIPS_HFLAG_ERL)) { |
89 | 91 |
#ifdef MIPS_USES_R4K_TLB |
... | ... | |
181 | 183 |
access_type = ACCESS_INT; |
182 | 184 |
if (env->user_mode_only) { |
183 | 185 |
/* user mode only emulation */ |
184 |
ret = -2;
|
|
186 |
ret = TLBRET_NOMATCH;
|
|
185 | 187 |
goto do_fault; |
186 | 188 |
} |
187 | 189 |
ret = get_physical_address(env, &physical, &prot, |
... | ... | |
190 | 192 |
fprintf(logfile, "%s address=%08x ret %d physical %08x prot %d\n", |
191 | 193 |
__func__, address, ret, physical, prot); |
192 | 194 |
} |
193 |
if (ret == 0) { |
|
194 |
ret = tlb_set_page(env, address & ~0xFFF, physical & ~0xFFF, prot, |
|
195 |
is_user, is_softmmu); |
|
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); |
|
196 | 199 |
} else if (ret < 0) { |
197 | 200 |
do_fault: |
198 | 201 |
switch (ret) { |
199 | 202 |
default: |
200 |
case -1:
|
|
203 |
case TLBRET_BADADDR:
|
|
201 | 204 |
/* Reference to kernel address from user mode or supervisor mode */ |
202 | 205 |
/* Reference to supervisor address from user mode */ |
203 | 206 |
if (rw) |
... | ... | |
205 | 208 |
else |
206 | 209 |
exception = EXCP_AdEL; |
207 | 210 |
break; |
208 |
case -2:
|
|
211 |
case TLBRET_NOMATCH:
|
|
209 | 212 |
/* No TLB match for a mapped address */ |
210 | 213 |
if (rw) |
211 | 214 |
exception = EXCP_TLBS; |
... | ... | |
213 | 216 |
exception = EXCP_TLBL; |
214 | 217 |
error_code = 1; |
215 | 218 |
break; |
216 |
case -3:
|
|
219 |
case TLBRET_INVALID:
|
|
217 | 220 |
/* TLB match with no valid bit */ |
218 | 221 |
if (rw) |
219 | 222 |
exception = EXCP_TLBS; |
220 | 223 |
else |
221 | 224 |
exception = EXCP_TLBL; |
222 | 225 |
break; |
223 |
case -4:
|
|
226 |
case TLBRET_DIRTY:
|
|
224 | 227 |
/* TLB match but 'D' bit is cleared */ |
225 | 228 |
exception = EXCP_LTLBL; |
226 | 229 |
break; |
... | ... | |
231 | 234 |
env->CP0_Context = (env->CP0_Context & 0xff800000) | |
232 | 235 |
((address >> 9) & 0x007ffff0); |
233 | 236 |
env->CP0_EntryHi = |
234 |
(env->CP0_EntryHi & 0xFF) | (address & 0xFFFFE000);
|
|
237 |
(env->CP0_EntryHi & 0xFF) | (address & (TARGET_PAGE_MASK << 1));
|
|
235 | 238 |
env->exception_index = exception; |
236 | 239 |
env->error_code = error_code; |
237 | 240 |
ret = 1; |
Also available in: Unified diff