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;
|