Revision 76a66253 target-ppc/helper.c
b/target-ppc/helper.c | ||
---|---|---|
1 | 1 |
/* |
2 | 2 |
* PowerPC emulation helpers for qemu. |
3 | 3 |
* |
4 |
* Copyright (c) 2003-2005 Jocelyn Mayer
|
|
4 |
* Copyright (c) 2003-2007 Jocelyn Mayer
|
|
5 | 5 |
* |
6 | 6 |
* This library is free software; you can redistribute it and/or |
7 | 7 |
* modify it under the terms of the GNU Lesser General Public |
... | ... | |
30 | 30 |
|
31 | 31 |
//#define DEBUG_MMU |
32 | 32 |
//#define DEBUG_BATS |
33 |
//#define DEBUG_SOFTWARE_TLB |
|
33 | 34 |
//#define DEBUG_EXCEPTIONS |
34 | 35 |
//#define FLUSH_ALL_TLBS |
35 | 36 |
|
... | ... | |
55 | 56 |
} |
56 | 57 |
env->exception_index = exception; |
57 | 58 |
env->error_code = error_code; |
59 |
|
|
58 | 60 |
return 1; |
59 | 61 |
} |
60 |
target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) |
|
62 |
|
|
63 |
target_ulong cpu_get_phys_page_debug (CPUState *env, target_ulong addr) |
|
61 | 64 |
{ |
62 | 65 |
return addr; |
63 | 66 |
} |
64 | 67 |
#else |
68 |
/* Common routines used by software and hardware TLBs emulation */ |
|
69 |
static inline int pte_is_valid (target_ulong pte0) |
|
70 |
{ |
|
71 |
return pte0 & 0x80000000 ? 1 : 0; |
|
72 |
} |
|
73 |
|
|
74 |
static inline void pte_invalidate (target_ulong *pte0) |
|
75 |
{ |
|
76 |
*pte0 &= ~0x80000000; |
|
77 |
} |
|
78 |
|
|
79 |
#define PTE_PTEM_MASK 0x7FFFFFBF |
|
80 |
#define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) |
|
81 |
|
|
82 |
static int pte_check (mmu_ctx_t *ctx, |
|
83 |
target_ulong pte0, target_ulong pte1, int h, int rw) |
|
84 |
{ |
|
85 |
int access, ret; |
|
86 |
|
|
87 |
access = 0; |
|
88 |
ret = -1; |
|
89 |
/* Check validity and table match */ |
|
90 |
if (pte_is_valid(pte0) && (h == ((pte0 >> 6) & 1))) { |
|
91 |
/* Check vsid & api */ |
|
92 |
if ((pte0 & PTE_PTEM_MASK) == ctx->ptem) { |
|
93 |
if (ctx->raddr != (target_ulong)-1) { |
|
94 |
/* all matches should have equal RPN, WIMG & PP */ |
|
95 |
if ((ctx->raddr & PTE_CHECK_MASK) != (pte1 & PTE_CHECK_MASK)) { |
|
96 |
if (loglevel > 0) |
|
97 |
fprintf(logfile, "Bad RPN/WIMG/PP\n"); |
|
98 |
return -3; |
|
99 |
} |
|
100 |
} |
|
101 |
/* Compute access rights */ |
|
102 |
if (ctx->key == 0) { |
|
103 |
access = PAGE_READ; |
|
104 |
if ((pte1 & 0x00000003) != 0x3) |
|
105 |
access |= PAGE_WRITE; |
|
106 |
} else { |
|
107 |
switch (pte1 & 0x00000003) { |
|
108 |
case 0x0: |
|
109 |
access = 0; |
|
110 |
break; |
|
111 |
case 0x1: |
|
112 |
case 0x3: |
|
113 |
access = PAGE_READ; |
|
114 |
break; |
|
115 |
case 0x2: |
|
116 |
access = PAGE_READ | PAGE_WRITE; |
|
117 |
break; |
|
118 |
} |
|
119 |
} |
|
120 |
/* Keep the matching PTE informations */ |
|
121 |
ctx->raddr = pte1; |
|
122 |
ctx->prot = access; |
|
123 |
if ((rw == 0 && (access & PAGE_READ)) || |
|
124 |
(rw == 1 && (access & PAGE_WRITE))) { |
|
125 |
/* Access granted */ |
|
126 |
#if defined (DEBUG_MMU) |
|
127 |
if (loglevel > 0) |
|
128 |
fprintf(logfile, "PTE access granted !\n"); |
|
129 |
#endif |
|
130 |
ret = 0; |
|
131 |
} else { |
|
132 |
/* Access right violation */ |
|
133 |
#if defined (DEBUG_MMU) |
|
134 |
if (loglevel > 0) |
|
135 |
fprintf(logfile, "PTE access rejected\n"); |
|
136 |
#endif |
|
137 |
ret = -2; |
|
138 |
} |
|
139 |
} |
|
140 |
} |
|
141 |
|
|
142 |
return ret; |
|
143 |
} |
|
144 |
|
|
145 |
static int pte_update_flags (mmu_ctx_t *ctx, target_ulong *pte1p, |
|
146 |
int ret, int rw) |
|
147 |
{ |
|
148 |
int store = 0; |
|
149 |
|
|
150 |
/* Update page flags */ |
|
151 |
if (!(*pte1p & 0x00000100)) { |
|
152 |
/* Update accessed flag */ |
|
153 |
*pte1p |= 0x00000100; |
|
154 |
store = 1; |
|
155 |
} |
|
156 |
if (!(*pte1p & 0x00000080)) { |
|
157 |
if (rw == 1 && ret == 0) { |
|
158 |
/* Update changed flag */ |
|
159 |
*pte1p |= 0x00000080; |
|
160 |
store = 1; |
|
161 |
} else { |
|
162 |
/* Force page fault for first write access */ |
|
163 |
ctx->prot &= ~PAGE_WRITE; |
|
164 |
} |
|
165 |
} |
|
166 |
|
|
167 |
return store; |
|
168 |
} |
|
169 |
|
|
170 |
/* Software driven TLB helpers */ |
|
171 |
static int ppc6xx_tlb_getnum (CPUState *env, target_ulong eaddr, |
|
172 |
int way, int is_code) |
|
173 |
{ |
|
174 |
int nr; |
|
175 |
|
|
176 |
/* Select TLB num in a way from address */ |
|
177 |
nr = (eaddr >> TARGET_PAGE_BITS) & (env->tlb_per_way - 1); |
|
178 |
/* Select TLB way */ |
|
179 |
nr += env->tlb_per_way * way; |
|
180 |
/* 6xx have separate TLBs for instructions and data */ |
|
181 |
if (is_code && env->id_tlbs == 1) |
|
182 |
nr += env->nb_tlb; |
|
183 |
|
|
184 |
return nr; |
|
185 |
} |
|
186 |
|
|
187 |
void ppc6xx_tlb_invalidate_all (CPUState *env) |
|
188 |
{ |
|
189 |
ppc_tlb_t *tlb; |
|
190 |
int nr, max; |
|
191 |
|
|
192 |
#if defined (DEBUG_SOFTWARE_TLB) && 0 |
|
193 |
if (loglevel != 0) { |
|
194 |
fprintf(logfile, "Invalidate all TLBs\n"); |
|
195 |
} |
|
196 |
#endif |
|
197 |
/* Invalidate all defined software TLB */ |
|
198 |
max = env->nb_tlb; |
|
199 |
if (env->id_tlbs == 1) |
|
200 |
max *= 2; |
|
201 |
for (nr = 0; nr < max; nr++) { |
|
202 |
tlb = &env->tlb[nr]; |
|
203 |
#if !defined(FLUSH_ALL_TLBS) |
|
204 |
tlb_flush_page(env, tlb->EPN); |
|
205 |
#endif |
|
206 |
pte_invalidate(&tlb->pte0); |
|
207 |
} |
|
208 |
#if defined(FLUSH_ALL_TLBS) |
|
209 |
tlb_flush(env, 1); |
|
210 |
#endif |
|
211 |
} |
|
212 |
|
|
213 |
static inline void __ppc6xx_tlb_invalidate_virt (CPUState *env, |
|
214 |
target_ulong eaddr, |
|
215 |
int is_code, int match_epn) |
|
216 |
{ |
|
217 |
ppc_tlb_t *tlb; |
|
218 |
int way, nr; |
|
219 |
|
|
220 |
#if !defined(FLUSH_ALL_TLBS) |
|
221 |
/* Invalidate ITLB + DTLB, all ways */ |
|
222 |
for (way = 0; way < env->nb_ways; way++) { |
|
223 |
nr = ppc6xx_tlb_getnum(env, eaddr, way, is_code); |
|
224 |
tlb = &env->tlb[nr]; |
|
225 |
if (pte_is_valid(tlb->pte0) && (match_epn == 0 || eaddr == tlb->EPN)) { |
|
226 |
#if defined (DEBUG_SOFTWARE_TLB) |
|
227 |
if (loglevel != 0) { |
|
228 |
fprintf(logfile, "TLB invalidate %d/%d %08x\n", |
|
229 |
nr, env->nb_tlb, eaddr); |
|
230 |
} |
|
231 |
#endif |
|
232 |
pte_invalidate(&tlb->pte0); |
|
233 |
tlb_flush_page(env, tlb->EPN); |
|
234 |
} |
|
235 |
} |
|
236 |
#else |
|
237 |
/* XXX: PowerPC specification say this is valid as well */ |
|
238 |
ppc6xx_tlb_invalidate_all(env); |
|
239 |
#endif |
|
240 |
} |
|
241 |
|
|
242 |
void ppc6xx_tlb_invalidate_virt (CPUState *env, target_ulong eaddr, |
|
243 |
int is_code) |
|
244 |
{ |
|
245 |
__ppc6xx_tlb_invalidate_virt(env, eaddr, is_code, 0); |
|
246 |
} |
|
247 |
|
|
248 |
void ppc6xx_tlb_store (CPUState *env, target_ulong EPN, int way, int is_code, |
|
249 |
target_ulong pte0, target_ulong pte1) |
|
250 |
{ |
|
251 |
ppc_tlb_t *tlb; |
|
252 |
int nr; |
|
253 |
|
|
254 |
nr = ppc6xx_tlb_getnum(env, EPN, way, is_code); |
|
255 |
tlb = &env->tlb[nr]; |
|
256 |
#if defined (DEBUG_SOFTWARE_TLB) |
|
257 |
if (loglevel != 0) { |
|
258 |
fprintf(logfile, "Set TLB %d/%d EPN %08lx PTE0 %08lx PTE1 %08lx\n", |
|
259 |
nr, env->nb_tlb, (unsigned long)EPN, |
|
260 |
(unsigned long)pte0, (unsigned long)pte1); |
|
261 |
} |
|
262 |
#endif |
|
263 |
/* Invalidate any pending reference in Qemu for this virtual address */ |
|
264 |
__ppc6xx_tlb_invalidate_virt(env, EPN, is_code, 1); |
|
265 |
tlb->pte0 = pte0; |
|
266 |
tlb->pte1 = pte1; |
|
267 |
tlb->EPN = EPN; |
|
268 |
tlb->PID = 0; |
|
269 |
tlb->size = 1; |
|
270 |
/* Store last way for LRU mechanism */ |
|
271 |
env->last_way = way; |
|
272 |
} |
|
273 |
|
|
274 |
static int ppc6xx_tlb_check (CPUState *env, mmu_ctx_t *ctx, |
|
275 |
target_ulong eaddr, int rw, int access_type) |
|
276 |
{ |
|
277 |
ppc_tlb_t *tlb; |
|
278 |
int nr, best, way; |
|
279 |
int ret; |
|
280 |
|
|
281 |
best = -1; |
|
282 |
ret = -1; /* No TLB found */ |
|
283 |
for (way = 0; way < env->nb_ways; way++) { |
|
284 |
nr = ppc6xx_tlb_getnum(env, eaddr, way, |
|
285 |
access_type == ACCESS_CODE ? 1 : 0); |
|
286 |
tlb = &env->tlb[nr]; |
|
287 |
/* This test "emulates" the PTE index match for hardware TLBs */ |
|
288 |
if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) { |
|
289 |
#if defined (DEBUG_SOFTWARE_TLB) |
|
290 |
if (loglevel != 0) { |
|
291 |
fprintf(logfile, "TLB %d/%d %s [%08x %08x] <> %08x\n", |
|
292 |
nr, env->nb_tlb, |
|
293 |
pte_is_valid(tlb->pte0) ? "valid" : "inval", |
|
294 |
tlb->EPN, tlb->EPN + TARGET_PAGE_SIZE, eaddr); |
|
295 |
} |
|
296 |
#endif |
|
297 |
continue; |
|
298 |
} |
|
299 |
#if defined (DEBUG_SOFTWARE_TLB) |
|
300 |
if (loglevel != 0) { |
|
301 |
fprintf(logfile, "TLB %d/%d %s %08x <> %08x %08x %c %c\n", |
|
302 |
nr, env->nb_tlb, |
|
303 |
pte_is_valid(tlb->pte0) ? "valid" : "inval", |
|
304 |
tlb->EPN, eaddr, tlb->pte1, |
|
305 |
rw ? 'S' : 'L', access_type == ACCESS_CODE ? 'I' : 'D'); |
|
306 |
} |
|
307 |
#endif |
|
308 |
switch (pte_check(ctx, tlb->pte0, tlb->pte1, 0, rw)) { |
|
309 |
case -3: |
|
310 |
/* TLB inconsistency */ |
|
311 |
return -1; |
|
312 |
case -2: |
|
313 |
/* Access violation */ |
|
314 |
ret = -2; |
|
315 |
best = nr; |
|
316 |
break; |
|
317 |
case -1: |
|
318 |
default: |
|
319 |
/* No match */ |
|
320 |
break; |
|
321 |
case 0: |
|
322 |
/* access granted */ |
|
323 |
/* XXX: we should go on looping to check all TLBs consistency |
|
324 |
* but we can speed-up the whole thing as the |
|
325 |
* result would be undefined if TLBs are not consistent. |
|
326 |
*/ |
|
327 |
ret = 0; |
|
328 |
best = nr; |
|
329 |
goto done; |
|
330 |
} |
|
331 |
} |
|
332 |
if (best != -1) { |
|
333 |
done: |
|
334 |
#if defined (DEBUG_SOFTWARE_TLB) |
|
335 |
if (loglevel > 0) { |
|
336 |
fprintf(logfile, "found TLB at addr 0x%08lx prot=0x%01x ret=%d\n", |
|
337 |
ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); |
|
338 |
} |
|
339 |
#endif |
|
340 |
/* Update page flags */ |
|
341 |
pte_update_flags(ctx, &env->tlb[best].pte1, ret, rw); |
|
342 |
} |
|
343 |
|
|
344 |
return ret; |
|
345 |
} |
|
346 |
|
|
65 | 347 |
/* Perform BAT hit & translation */ |
66 |
static int get_bat (CPUState *env, uint32_t *real, int *prot,
|
|
67 |
uint32_t virtual, int rw, int type)
|
|
348 |
static int get_bat (CPUState *env, mmu_ctx_t *ctx,
|
|
349 |
target_ulong virtual, int rw, int type)
|
|
68 | 350 |
{ |
69 |
uint32_t *BATlt, *BATut, *BATu, *BATl;
|
|
70 |
uint32_t base, BEPIl, BEPIu, bl;
|
|
351 |
target_ulong *BATlt, *BATut, *BATu, *BATl;
|
|
352 |
target_ulong base, BEPIl, BEPIu, bl;
|
|
71 | 353 |
int i; |
72 | 354 |
int ret = -1; |
73 | 355 |
|
74 | 356 |
#if defined (DEBUG_BATS) |
75 | 357 |
if (loglevel > 0) { |
76 | 358 |
fprintf(logfile, "%s: %cBAT v 0x%08x\n", __func__, |
77 |
type == ACCESS_CODE ? 'I' : 'D', virtual); |
|
359 |
type == ACCESS_CODE ? 'I' : 'D', virtual);
|
|
78 | 360 |
} |
79 | 361 |
#endif |
80 | 362 |
switch (type) { |
... | ... | |
90 | 372 |
#if defined (DEBUG_BATS) |
91 | 373 |
if (loglevel > 0) { |
92 | 374 |
fprintf(logfile, "%s...: %cBAT v 0x%08x\n", __func__, |
93 |
type == ACCESS_CODE ? 'I' : 'D', virtual); |
|
375 |
type == ACCESS_CODE ? 'I' : 'D', virtual);
|
|
94 | 376 |
} |
95 | 377 |
#endif |
96 | 378 |
base = virtual & 0xFFFC0000; |
... | ... | |
113 | 395 |
if ((msr_pr == 0 && (*BATu & 0x00000002)) || |
114 | 396 |
(msr_pr == 1 && (*BATu & 0x00000001))) { |
115 | 397 |
/* Get physical address */ |
116 |
*real = (*BATl & 0xF0000000) |
|
|
398 |
ctx->raddr = (*BATl & 0xF0000000) |
|
|
117 | 399 |
((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | |
118 | 400 |
(virtual & 0x0001F000); |
119 | 401 |
if (*BATl & 0x00000001) |
120 |
*prot = PAGE_READ;
|
|
402 |
ctx->prot = PAGE_READ;
|
|
121 | 403 |
if (*BATl & 0x00000002) |
122 |
*prot = PAGE_WRITE | PAGE_READ;
|
|
404 |
ctx->prot = PAGE_WRITE | PAGE_READ;
|
|
123 | 405 |
#if defined (DEBUG_BATS) |
124 | 406 |
if (loglevel > 0) { |
125 | 407 |
fprintf(logfile, "BAT %d match: r 0x%08x prot=%c%c\n", |
126 |
i, *real, *prot & PAGE_READ ? 'R' : '-',
|
|
127 |
*prot & PAGE_WRITE ? 'W' : '-');
|
|
408 |
i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-',
|
|
409 |
ctx->prot & PAGE_WRITE ? 'W' : '-');
|
|
128 | 410 |
} |
129 | 411 |
#endif |
130 | 412 |
ret = 0; |
... | ... | |
153 | 435 |
} |
154 | 436 |
|
155 | 437 |
/* PTE table lookup */ |
156 |
static int find_pte (uint32_t *RPN, int *prot, uint32_t base, uint32_t va, |
|
157 |
int h, int key, int rw) |
|
438 |
static int find_pte (mmu_ctx_t *ctx, int h, int rw) |
|
158 | 439 |
{ |
159 |
uint32_t pte0, pte1, keep = 0, access = 0;
|
|
160 |
int i, good = -1, store = 0;
|
|
161 |
int ret = -1; /* No entry found */
|
|
440 |
target_ulong base, pte0, pte1;
|
|
441 |
int i, good = -1; |
|
442 |
int ret;
|
|
162 | 443 |
|
444 |
ret = -1; /* No entry found */ |
|
445 |
base = ctx->pg_addr[h]; |
|
163 | 446 |
for (i = 0; i < 8; i++) { |
164 | 447 |
pte0 = ldl_phys(base + (i * 8)); |
165 | 448 |
pte1 = ldl_phys(base + (i * 8) + 4); |
166 | 449 |
#if defined (DEBUG_MMU) |
167 | 450 |
if (loglevel > 0) { |
168 |
fprintf(logfile, "Load pte from 0x%08x => 0x%08x 0x%08x " |
|
169 |
"%d %d %d 0x%08x\n", base + (i * 8), pte0, pte1, |
|
170 |
pte0 >> 31, h, (pte0 >> 6) & 1, va); |
|
171 |
} |
|
172 |
#endif |
|
173 |
/* Check validity and table match */ |
|
174 |
if (pte0 & 0x80000000 && (h == ((pte0 >> 6) & 1))) { |
|
175 |
/* Check vsid & api */ |
|
176 |
if ((pte0 & 0x7FFFFFBF) == va) { |
|
177 |
if (good == -1) { |
|
178 |
good = i; |
|
179 |
keep = pte1; |
|
180 |
} else { |
|
181 |
/* All matches should have equal RPN, WIMG & PP */ |
|
182 |
if ((keep & 0xFFFFF07B) != (pte1 & 0xFFFFF07B)) { |
|
183 |
if (loglevel > 0) |
|
184 |
fprintf(logfile, "Bad RPN/WIMG/PP\n"); |
|
185 |
return -1; |
|
186 |
} |
|
187 |
} |
|
188 |
/* Check access rights */ |
|
189 |
if (key == 0) { |
|
190 |
access = PAGE_READ; |
|
191 |
if ((pte1 & 0x00000003) != 0x3) |
|
192 |
access |= PAGE_WRITE; |
|
193 |
} else { |
|
194 |
switch (pte1 & 0x00000003) { |
|
195 |
case 0x0: |
|
196 |
access = 0; |
|
197 |
break; |
|
198 |
case 0x1: |
|
199 |
case 0x3: |
|
200 |
access = PAGE_READ; |
|
201 |
break; |
|
202 |
case 0x2: |
|
203 |
access = PAGE_READ | PAGE_WRITE; |
|
204 |
break; |
|
205 |
} |
|
206 |
} |
|
207 |
if (ret < 0) { |
|
208 |
if ((rw == 0 && (access & PAGE_READ)) || |
|
209 |
(rw == 1 && (access & PAGE_WRITE))) { |
|
210 |
#if defined (DEBUG_MMU) |
|
211 |
if (loglevel > 0) |
|
212 |
fprintf(logfile, "PTE access granted !\n"); |
|
213 |
#endif |
|
214 |
good = i; |
|
215 |
keep = pte1; |
|
216 |
ret = 0; |
|
217 |
} else { |
|
218 |
/* Access right violation */ |
|
219 |
ret = -2; |
|
220 |
#if defined (DEBUG_MMU) |
|
221 |
if (loglevel > 0) |
|
222 |
fprintf(logfile, "PTE access rejected\n"); |
|
451 |
fprintf(logfile, "Load pte from 0x%08x => 0x%08x 0x%08x " |
|
452 |
"%d %d %d 0x%08x\n", base + (i * 8), pte0, pte1, |
|
453 |
pte0 >> 31, h, (pte0 >> 6) & 1, ctx->ptem); |
|
454 |
} |
|
223 | 455 |
#endif |
224 |
} |
|
225 |
*prot = access; |
|
226 |
} |
|
227 |
} |
|
456 |
switch (pte_check(ctx, pte0, pte1, h, rw)) { |
|
457 |
case -3: |
|
458 |
/* PTE inconsistency */ |
|
459 |
return -1; |
|
460 |
case -2: |
|
461 |
/* Access violation */ |
|
462 |
ret = -2; |
|
463 |
good = i; |
|
464 |
break; |
|
465 |
case -1: |
|
466 |
default: |
|
467 |
/* No PTE match */ |
|
468 |
break; |
|
469 |
case 0: |
|
470 |
/* access granted */ |
|
471 |
/* XXX: we should go on looping to check all PTEs consistency |
|
472 |
* but if we can speed-up the whole thing as the |
|
473 |
* result would be undefined if PTEs are not consistent. |
|
474 |
*/ |
|
475 |
ret = 0; |
|
476 |
good = i; |
|
477 |
goto done; |
|
228 | 478 |
} |
229 | 479 |
} |
230 | 480 |
if (good != -1) { |
231 |
*RPN = keep & 0xFFFFF000;
|
|
481 |
done:
|
|
232 | 482 |
#if defined (DEBUG_MMU) |
233 | 483 |
if (loglevel > 0) { |
234 |
fprintf(logfile, "found PTE at addr 0x%08x prot=0x%01x ret=%d\n",
|
|
235 |
*RPN, *prot, ret);
|
|
236 |
}
|
|
484 |
fprintf(logfile, "found PTE at addr 0x%08x prot=0x%01x ret=%d\n",
|
|
485 |
ctx->raddr, ctx->prot, ret);
|
|
486 |
}
|
|
237 | 487 |
#endif |
238 | 488 |
/* Update page flags */ |
239 |
if (!(keep & 0x00000100)) { |
|
240 |
/* Access flag */ |
|
241 |
keep |= 0x00000100; |
|
242 |
store = 1; |
|
243 |
} |
|
244 |
if (!(keep & 0x00000080)) { |
|
245 |
if (rw && ret == 0) { |
|
246 |
/* Change flag */ |
|
247 |
keep |= 0x00000080; |
|
248 |
store = 1; |
|
249 |
} else { |
|
250 |
/* Force page fault for first write access */ |
|
251 |
*prot &= ~PAGE_WRITE; |
|
252 |
} |
|
253 |
} |
|
254 |
if (store) { |
|
255 |
stl_phys_notdirty(base + (good * 8) + 4, keep); |
|
256 |
} |
|
489 |
pte1 = ctx->raddr; |
|
490 |
if (pte_update_flags(ctx, &pte1, ret, rw) == 1) |
|
491 |
stl_phys_notdirty(base + (good * 8) + 4, pte1); |
|
257 | 492 |
} |
258 | 493 |
|
259 | 494 |
return ret; |
260 | 495 |
} |
261 | 496 |
|
262 |
static inline uint32_t get_pgaddr (uint32_t sdr1, uint32_t hash, uint32_t mask) |
|
497 |
static inline target_phys_addr_t get_pgaddr (target_phys_addr_t sdr1, |
|
498 |
target_phys_addr_t hash, |
|
499 |
target_phys_addr_t mask) |
|
263 | 500 |
{ |
264 | 501 |
return (sdr1 & 0xFFFF0000) | (hash & mask); |
265 | 502 |
} |
266 | 503 |
|
267 | 504 |
/* Perform segment based translation */ |
268 |
static int get_segment (CPUState *env, uint32_t *real, int *prot,
|
|
269 |
uint32_t virtual, int rw, int type)
|
|
505 |
static int get_segment (CPUState *env, mmu_ctx_t *ctx,
|
|
506 |
target_ulong eaddr, int rw, int type)
|
|
270 | 507 |
{ |
271 |
uint32_t pg_addr, sdr, ptem, vsid, pgidx; |
|
272 |
uint32_t hash, mask; |
|
273 |
uint32_t sr; |
|
274 |
int key; |
|
508 |
target_phys_addr_t sdr, hash, mask; |
|
509 |
target_ulong sr, vsid, pgidx; |
|
275 | 510 |
int ret = -1, ret2; |
276 | 511 |
|
277 |
sr = env->sr[virtual >> 28];
|
|
512 |
sr = env->sr[eaddr >> 28];
|
|
278 | 513 |
#if defined (DEBUG_MMU) |
279 | 514 |
if (loglevel > 0) { |
280 |
fprintf(logfile, "Check segment v=0x%08x %d 0x%08x nip=0x%08x "
|
|
281 |
"lr=0x%08x ir=%d dr=%d pr=%d %d t=%d\n",
|
|
282 |
virtual, virtual >> 28, sr, env->nip,
|
|
283 |
env->lr, msr_ir, msr_dr, msr_pr, rw, type);
|
|
515 |
fprintf(logfile, "Check segment v=0x%08x %d 0x%08x nip=0x%08x "
|
|
516 |
"lr=0x%08x ir=%d dr=%d pr=%d %d t=%d\n",
|
|
517 |
eaddr, eaddr >> 28, sr, env->nip,
|
|
518 |
env->lr, msr_ir, msr_dr, msr_pr, rw, type);
|
|
284 | 519 |
} |
285 | 520 |
#endif |
286 |
key = (((sr & 0x20000000) && msr_pr == 1) || |
|
287 |
((sr & 0x40000000) && msr_pr == 0)) ? 1 : 0; |
|
521 |
ctx->key = (((sr & 0x20000000) && msr_pr == 1) ||
|
|
522 |
((sr & 0x40000000) && msr_pr == 0)) ? 1 : 0;
|
|
288 | 523 |
if ((sr & 0x80000000) == 0) { |
289 | 524 |
#if defined (DEBUG_MMU) |
290 |
if (loglevel > 0) |
|
291 |
fprintf(logfile, "pte segment: key=%d n=0x%08x\n",
|
|
292 |
key, sr & 0x10000000);
|
|
525 |
if (loglevel > 0)
|
|
526 |
fprintf(logfile, "pte segment: key=%d n=0x%08x\n",
|
|
527 |
ctx->key, sr & 0x10000000);
|
|
293 | 528 |
#endif |
294 | 529 |
/* Check if instruction fetch is allowed, if needed */ |
295 | 530 |
if (type != ACCESS_CODE || (sr & 0x10000000) == 0) { |
296 | 531 |
/* Page address translation */ |
532 |
pgidx = (eaddr >> TARGET_PAGE_BITS) & 0xFFFF; |
|
297 | 533 |
vsid = sr & 0x00FFFFFF; |
298 |
pgidx = (virtual >> 12) & 0xFFFF; |
|
299 |
sdr = env->sdr1; |
|
300 | 534 |
hash = ((vsid ^ pgidx) & 0x0007FFFF) << 6; |
535 |
/* Primary table address */ |
|
536 |
sdr = env->sdr1; |
|
301 | 537 |
mask = ((sdr & 0x000001FF) << 16) | 0xFFC0; |
302 |
pg_addr = get_pgaddr(sdr, hash, mask); |
|
303 |
ptem = (vsid << 7) | (pgidx >> 10); |
|
538 |
ctx->pg_addr[0] = get_pgaddr(sdr, hash, mask); |
|
539 |
/* Secondary table address */ |
|
540 |
hash = (~hash) & 0x01FFFFC0; |
|
541 |
ctx->pg_addr[1] = get_pgaddr(sdr, hash, mask); |
|
542 |
ctx->ptem = (vsid << 7) | (pgidx >> 10); |
|
543 |
/* Initialize real address with an invalid value */ |
|
544 |
ctx->raddr = (target_ulong)-1; |
|
545 |
if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_6xx)) { |
|
546 |
/* Software TLB search */ |
|
547 |
ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type); |
|
548 |
} else if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_4xx)) { |
|
549 |
/* XXX: TODO */ |
|
550 |
} else { |
|
304 | 551 |
#if defined (DEBUG_MMU) |
305 |
if (loglevel > 0) {
|
|
306 |
fprintf(logfile, "0 sdr1=0x%08x vsid=0x%06x api=0x%04x "
|
|
307 |
"hash=0x%07x pg_addr=0x%08x\n", sdr, vsid, pgidx, hash,
|
|
308 |
pg_addr);
|
|
309 |
}
|
|
552 |
if (loglevel > 0) {
|
|
553 |
fprintf(logfile, "0 sdr1=0x%08x vsid=0x%06x api=0x%04x "
|
|
554 |
"hash=0x%07x pg_addr=0x%08x\n", sdr, vsid, pgidx,
|
|
555 |
hash, ctx->pg_addr[0]);
|
|
556 |
}
|
|
310 | 557 |
#endif |
311 |
/* Primary table lookup */ |
|
312 |
ret = find_pte(real, prot, pg_addr, ptem, 0, key, rw); |
|
313 |
if (ret < 0) { |
|
314 |
/* Secondary table lookup */ |
|
315 |
hash = (~hash) & 0x01FFFFC0; |
|
316 |
pg_addr = get_pgaddr(sdr, hash, mask); |
|
558 |
/* Primary table lookup */ |
|
559 |
ret = find_pte(ctx, 0, rw); |
|
560 |
if (ret < 0) { |
|
561 |
/* Secondary table lookup */ |
|
317 | 562 |
#if defined (DEBUG_MMU) |
318 |
if (virtual != 0xEFFFFFFF && loglevel > 0) { |
|
319 |
fprintf(logfile, "1 sdr1=0x%08x vsid=0x%06x api=0x%04x " |
|
320 |
"hash=0x%05x pg_addr=0x%08x\n", sdr, vsid, pgidx, |
|
321 |
hash, pg_addr); |
|
322 |
} |
|
563 |
if (eaddr != 0xEFFFFFFF && loglevel > 0) { |
|
564 |
fprintf(logfile, |
|
565 |
"1 sdr1=0x%08x vsid=0x%06x api=0x%04x " |
|
566 |
"hash=0x%05x pg_addr=0x%08x\n", sdr, vsid, |
|
567 |
pgidx, hash, ctx->pg_addr[1]); |
|
568 |
} |
|
323 | 569 |
#endif |
324 |
ret2 = find_pte(real, prot, pg_addr, ptem, 1, key, rw); |
|
325 |
if (ret2 != -1) |
|
326 |
ret = ret2; |
|
570 |
ret2 = find_pte(ctx, 1, rw); |
|
571 |
if (ret2 != -1) |
|
572 |
ret = ret2; |
|
573 |
} |
|
327 | 574 |
} |
328 | 575 |
} else { |
329 | 576 |
#if defined (DEBUG_MMU) |
330 |
if (loglevel > 0)
|
|
331 |
fprintf(logfile, "No access allowed\n");
|
|
577 |
if (loglevel > 0)
|
|
578 |
fprintf(logfile, "No access allowed\n");
|
|
332 | 579 |
#endif |
333 |
ret = -3;
|
|
580 |
ret = -3;
|
|
334 | 581 |
} |
335 | 582 |
} else { |
336 | 583 |
#if defined (DEBUG_MMU) |
337 | 584 |
if (loglevel > 0) |
338 |
fprintf(logfile, "direct store...\n");
|
|
585 |
fprintf(logfile, "direct store...\n");
|
|
339 | 586 |
#endif |
340 | 587 |
/* Direct-store segment : absolutely *BUGGY* for now */ |
341 | 588 |
switch (type) { |
... | ... | |
356 | 603 |
/* Should make the instruction do no-op. |
357 | 604 |
* As it already do no-op, it's quite easy :-) |
358 | 605 |
*/ |
359 |
*real = virtual;
|
|
606 |
ctx->raddr = eaddr;
|
|
360 | 607 |
return 0; |
361 | 608 |
case ACCESS_EXT: |
362 | 609 |
/* eciwx or ecowx */ |
... | ... | |
370 | 617 |
"address translation\n"); |
371 | 618 |
return -4; |
372 | 619 |
} |
373 |
if ((rw == 1 || key != 1) && (rw == 0 || key != 0)) {
|
|
374 |
*real = virtual;
|
|
620 |
if ((rw == 1 || ctx->key != 1) && (rw == 0 || ctx->key != 0)) {
|
|
621 |
ctx->raddr = eaddr;
|
|
375 | 622 |
ret = 2; |
376 | 623 |
} else { |
377 | 624 |
ret = -2; |
... | ... | |
381 | 628 |
return ret; |
382 | 629 |
} |
383 | 630 |
|
384 |
static int get_physical_address (CPUState *env, uint32_t *physical, int *prot, |
|
385 |
uint32_t address, int rw, int access_type) |
|
631 |
static int check_physical (CPUState *env, mmu_ctx_t *ctx, |
|
632 |
target_ulong eaddr, int rw) |
|
633 |
{ |
|
634 |
int in_plb, ret; |
|
635 |
|
|
636 |
ctx->raddr = eaddr; |
|
637 |
ctx->prot = PAGE_READ; |
|
638 |
ret = 0; |
|
639 |
if (unlikely(msr_pe != 0 && PPC_MMU(env) == PPC_FLAGS_MMU_403)) { |
|
640 |
/* 403 family add some particular protections, |
|
641 |
* using PBL/PBU registers for accesses with no translation. |
|
642 |
*/ |
|
643 |
in_plb = |
|
644 |
/* Check PLB validity */ |
|
645 |
(env->pb[0] < env->pb[1] && |
|
646 |
/* and address in plb area */ |
|
647 |
eaddr >= env->pb[0] && eaddr < env->pb[1]) || |
|
648 |
(env->pb[2] < env->pb[3] && |
|
649 |
eaddr >= env->pb[2] && eaddr < env->pb[3]) ? 1 : 0; |
|
650 |
if (in_plb ^ msr_px) { |
|
651 |
/* Access in protected area */ |
|
652 |
if (rw == 1) { |
|
653 |
/* Access is not allowed */ |
|
654 |
ret = -2; |
|
655 |
} |
|
656 |
} else { |
|
657 |
/* Read-write access is allowed */ |
|
658 |
ctx->prot |= PAGE_WRITE; |
|
659 |
} |
|
660 |
} else { |
|
661 |
ctx->prot |= PAGE_WRITE; |
|
662 |
} |
|
663 |
|
|
664 |
return ret; |
|
665 |
} |
|
666 |
|
|
667 |
int get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr, |
|
668 |
int rw, int access_type, int check_BATs) |
|
386 | 669 |
{ |
387 | 670 |
int ret; |
388 | 671 |
#if 0 |
... | ... | |
393 | 676 |
if ((access_type == ACCESS_CODE && msr_ir == 0) || |
394 | 677 |
(access_type != ACCESS_CODE && msr_dr == 0)) { |
395 | 678 |
/* No address translation */ |
396 |
*physical = address & ~0xFFF; |
|
397 |
*prot = PAGE_READ | PAGE_WRITE; |
|
398 |
ret = 0; |
|
679 |
ret = check_physical(env, ctx, eaddr, rw); |
|
399 | 680 |
} else { |
400 | 681 |
/* Try to find a BAT */ |
401 |
ret = get_bat(env, physical, prot, address, rw, access_type); |
|
682 |
ret = -1; |
|
683 |
if (check_BATs) |
|
684 |
ret = get_bat(env, ctx, eaddr, rw, access_type); |
|
402 | 685 |
if (ret < 0) { |
403 | 686 |
/* We didn't match any BAT entry */ |
404 |
ret = get_segment(env, physical, prot, address, rw, access_type);
|
|
687 |
ret = get_segment(env, ctx, eaddr, rw, access_type);
|
|
405 | 688 |
} |
406 | 689 |
} |
407 | 690 |
#if 0 |
408 | 691 |
if (loglevel > 0) { |
409 |
fprintf(logfile, "%s address %08x => %08x\n", |
|
410 |
__func__, address, *physical);
|
|
692 |
fprintf(logfile, "%s address %08x => %08lx\n",
|
|
693 |
__func__, eaddr, ctx->raddr);
|
|
411 | 694 |
} |
412 |
#endif |
|
695 |
#endif |
|
696 |
|
|
413 | 697 |
return ret; |
414 | 698 |
} |
415 | 699 |
|
416 |
target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) |
|
700 |
target_ulong cpu_get_phys_page_debug (CPUState *env, target_ulong addr)
|
|
417 | 701 |
{ |
418 |
uint32_t phys_addr; |
|
419 |
int prot; |
|
702 |
mmu_ctx_t ctx; |
|
420 | 703 |
|
421 |
if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0)
|
|
704 |
if (unlikely(get_physical_address(env, &ctx, addr, 0, ACCESS_INT, 1) != 0))
|
|
422 | 705 |
return -1; |
423 |
return phys_addr; |
|
706 |
|
|
707 |
return ctx.raddr & TARGET_PAGE_MASK; |
|
424 | 708 |
} |
425 | 709 |
|
426 | 710 |
/* Perform address translation */ |
427 | 711 |
int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, |
428 | 712 |
int is_user, int is_softmmu) |
429 | 713 |
{ |
430 |
uint32_t physical; |
|
431 |
int prot; |
|
714 |
mmu_ctx_t ctx; |
|
432 | 715 |
int exception = 0, error_code = 0; |
433 | 716 |
int access_type; |
434 | 717 |
int ret = 0; |
435 |
|
|
718 |
|
|
436 | 719 |
if (rw == 2) { |
437 | 720 |
/* code access */ |
438 | 721 |
rw = 0; |
... | ... | |
444 | 727 |
access_type = ACCESS_INT; |
445 | 728 |
// access_type = env->access_type; |
446 | 729 |
} |
447 |
if (env->user_mode_only) { |
|
448 |
/* user mode only emulation */ |
|
449 |
ret = -2; |
|
450 |
goto do_fault; |
|
451 |
} |
|
452 |
ret = get_physical_address(env, &physical, &prot, |
|
453 |
address, rw, access_type); |
|
730 |
ret = get_physical_address(env, &ctx, address, rw, access_type, 1); |
|
454 | 731 |
if (ret == 0) { |
455 |
ret = tlb_set_page(env, address & ~0xFFF, physical, prot, |
|
456 |
is_user, is_softmmu); |
|
732 |
ret = tlb_set_page(env, address & TARGET_PAGE_MASK, |
|
733 |
ctx.raddr & TARGET_PAGE_MASK, ctx.prot, |
|
734 |
is_user, is_softmmu); |
|
457 | 735 |
} else if (ret < 0) { |
458 |
do_fault: |
|
459 | 736 |
#if defined (DEBUG_MMU) |
460 |
if (loglevel > 0)
|
|
461 |
cpu_dump_state(env, logfile, fprintf, 0);
|
|
737 |
if (loglevel > 0)
|
|
738 |
cpu_dump_state(env, logfile, fprintf, 0);
|
|
462 | 739 |
#endif |
463 | 740 |
if (access_type == ACCESS_CODE) { |
464 | 741 |
exception = EXCP_ISI; |
465 | 742 |
switch (ret) { |
466 | 743 |
case -1: |
467 |
/* No matches in page tables */ |
|
468 |
error_code = 0x40000000; |
|
744 |
/* No matches in page tables or TLB */ |
|
745 |
if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_6xx)) { |
|
746 |
exception = EXCP_I_TLBMISS; |
|
747 |
env->spr[SPR_IMISS] = address; |
|
748 |
env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; |
|
749 |
error_code = 1 << 18; |
|
750 |
goto tlb_miss; |
|
751 |
} else if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_4xx)) { |
|
752 |
/* XXX: TODO */ |
|
753 |
} else { |
|
754 |
error_code = 0x40000000; |
|
755 |
} |
|
469 | 756 |
break; |
470 | 757 |
case -2: |
471 | 758 |
/* Access rights violation */ |
472 | 759 |
error_code = 0x08000000; |
473 | 760 |
break; |
474 | 761 |
case -3: |
475 |
/* No execute protection violation */
|
|
762 |
/* No execute protection violation */
|
|
476 | 763 |
error_code = 0x10000000; |
477 | 764 |
break; |
478 | 765 |
case -4: |
... | ... | |
490 | 777 |
exception = EXCP_DSI; |
491 | 778 |
switch (ret) { |
492 | 779 |
case -1: |
493 |
/* No matches in page tables */ |
|
494 |
error_code = 0x40000000; |
|
780 |
/* No matches in page tables or TLB */ |
|
781 |
if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_6xx)) { |
|
782 |
if (rw == 1) { |
|
783 |
exception = EXCP_DS_TLBMISS; |
|
784 |
error_code = 1 << 16; |
|
785 |
} else { |
|
786 |
exception = EXCP_DL_TLBMISS; |
|
787 |
error_code = 0; |
|
788 |
} |
|
789 |
env->spr[SPR_DMISS] = address; |
|
790 |
env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; |
|
791 |
tlb_miss: |
|
792 |
error_code |= ctx.key << 19; |
|
793 |
env->spr[SPR_HASH1] = ctx.pg_addr[0]; |
|
794 |
env->spr[SPR_HASH2] = ctx.pg_addr[1]; |
|
795 |
/* Do not alter DAR nor DSISR */ |
|
796 |
goto out; |
|
797 |
} else if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_4xx)) { |
|
798 |
/* XXX: TODO */ |
|
799 |
} else { |
|
800 |
error_code = 0x40000000; |
|
801 |
} |
|
495 | 802 |
break; |
496 | 803 |
case -2: |
497 | 804 |
/* Access rights violation */ |
... | ... | |
514 | 821 |
error_code = 0x04100000; |
515 | 822 |
break; |
516 | 823 |
default: |
517 |
printf("DSI: invalid exception (%d)\n", ret);
|
|
824 |
printf("DSI: invalid exception (%d)\n", ret);
|
|
518 | 825 |
exception = EXCP_PROGRAM; |
519 | 826 |
error_code = EXCP_INVAL | EXCP_INVAL_INVAL; |
520 | 827 |
break; |
... | ... | |
528 | 835 |
} |
529 | 836 |
if (exception == EXCP_DSI && rw == 1) |
530 | 837 |
error_code |= 0x02000000; |
531 |
/* Store fault address */
|
|
532 |
env->spr[SPR_DAR] = address;
|
|
838 |
/* Store fault address */
|
|
839 |
env->spr[SPR_DAR] = address;
|
|
533 | 840 |
env->spr[SPR_DSISR] = error_code; |
534 | 841 |
} |
842 |
out: |
|
535 | 843 |
#if 0 |
536 | 844 |
printf("%s: set exception to %d %02x\n", |
537 | 845 |
__func__, exception, error_code); |
... | ... | |
540 | 848 |
env->error_code = error_code; |
541 | 849 |
ret = 1; |
542 | 850 |
} |
851 |
|
|
543 | 852 |
return ret; |
544 | 853 |
} |
545 |
#endif |
|
546 | 854 |
|
547 | 855 |
/*****************************************************************************/ |
548 | 856 |
/* BATs management */ |
... | ... | |
551 | 859 |
target_ulong BATu, target_ulong mask) |
552 | 860 |
{ |
553 | 861 |
target_ulong base, end, page; |
862 |
|
|
554 | 863 |
base = BATu & ~0x0001FFFF; |
555 | 864 |
end = base + mask + 0x00020000; |
556 | 865 |
#if defined (DEBUG_BATS) |
557 |
if (loglevel != 0) |
|
558 |
fprintf(logfile, "Flush BAT from %08x to %08x (%08x)\n", base, end, mask); |
|
866 |
if (loglevel != 0) { |
|
867 |
fprintf(logfile, "Flush BAT from %08x to %08x (%08x)\n", |
|
868 |
base, end, mask); |
|
869 |
} |
|
559 | 870 |
#endif |
560 | 871 |
for (page = base; page != end; page += TARGET_PAGE_SIZE) |
561 | 872 |
tlb_flush_page(env, page); |
... | ... | |
608 | 919 |
(env->IBAT[1][nr] & ~0x0001FFFF & ~mask); |
609 | 920 |
#if !defined(FLUSH_ALL_TLBS) |
610 | 921 |
do_invalidate_BAT(env, env->IBAT[0][nr], mask); |
611 |
#endif |
|
612 |
#if defined(FLUSH_ALL_TLBS) |
|
922 |
#else |
|
613 | 923 |
tlb_flush(env, 1); |
614 | 924 |
#endif |
615 | 925 |
} |
... | ... | |
663 | 973 |
env->DBAT[1][nr] = value; |
664 | 974 |
} |
665 | 975 |
|
666 |
static inline void invalidate_all_tlbs (CPUPPCState *env) |
|
667 |
{ |
|
668 |
/* XXX: this needs to be completed for sotware driven TLB support */ |
|
669 |
tlb_flush(env, 1); |
|
670 |
} |
|
671 |
|
|
672 | 976 |
/*****************************************************************************/ |
673 | 977 |
/* Special registers manipulation */ |
674 |
target_ulong do_load_nip (CPUPPCState *env) |
|
675 |
{ |
|
676 |
return env->nip; |
|
677 |
} |
|
678 |
|
|
679 |
void do_store_nip (CPUPPCState *env, target_ulong value) |
|
680 |
{ |
|
681 |
env->nip = value; |
|
682 |
} |
|
683 |
|
|
684 | 978 |
target_ulong do_load_sdr1 (CPUPPCState *env) |
685 | 979 |
{ |
686 | 980 |
return env->sdr1; |
... | ... | |
695 | 989 |
#endif |
696 | 990 |
if (env->sdr1 != value) { |
697 | 991 |
env->sdr1 = value; |
698 |
invalidate_all_tlbs(env);
|
|
992 |
tlb_flush(env, 1);
|
|
699 | 993 |
} |
700 | 994 |
} |
701 | 995 |
|
... | ... | |
724 | 1018 |
tlb_flush_page(env, page); |
725 | 1019 |
} |
726 | 1020 |
#else |
727 |
invalidate_all_tlbs(env);
|
|
1021 |
tlb_flush(env, 1);
|
|
728 | 1022 |
#endif |
729 | 1023 |
} |
730 | 1024 |
} |
1025 |
#endif /* !defined (CONFIG_USER_ONLY) */ |
|
731 | 1026 |
|
732 |
uint32_t do_load_cr (CPUPPCState *env) |
|
733 |
{ |
|
734 |
return (env->crf[0] << 28) | |
|
735 |
(env->crf[1] << 24) | |
|
736 |
(env->crf[2] << 20) | |
|
737 |
(env->crf[3] << 16) | |
|
738 |
(env->crf[4] << 12) | |
|
739 |
(env->crf[5] << 8) | |
|
740 |
(env->crf[6] << 4) | |
|
741 |
(env->crf[7] << 0); |
|
742 |
} |
|
743 |
|
|
744 |
void do_store_cr (CPUPPCState *env, uint32_t value, uint32_t mask) |
|
745 |
{ |
|
746 |
int i, sh; |
|
747 |
|
|
748 |
for (i = 0, sh = 7; i < 8; i++, sh --) { |
|
749 |
if (mask & (1 << sh)) |
|
750 |
env->crf[i] = (value >> (sh * 4)) & 0xFUL; |
|
751 |
} |
|
752 |
} |
|
753 |
|
|
754 |
uint32_t do_load_xer (CPUPPCState *env) |
|
1027 |
uint32_t ppc_load_xer (CPUPPCState *env) |
|
755 | 1028 |
{ |
756 | 1029 |
return (xer_so << XER_SO) | |
757 | 1030 |
(xer_ov << XER_OV) | |
... | ... | |
760 | 1033 |
(xer_cmp << XER_CMP); |
761 | 1034 |
} |
762 | 1035 |
|
763 |
void do_store_xer (CPUPPCState *env, uint32_t value)
|
|
1036 |
void ppc_store_xer (CPUPPCState *env, uint32_t value)
|
|
764 | 1037 |
{ |
765 | 1038 |
xer_so = (value >> XER_SO) & 0x01; |
766 | 1039 |
xer_ov = (value >> XER_OV) & 0x01; |
... | ... | |
769 | 1042 |
xer_bc = (value >> XER_BC) & 0x3F; |
770 | 1043 |
} |
771 | 1044 |
|
772 |
target_ulong do_load_msr (CPUPPCState *env) |
|
1045 |
/* Swap temporary saved registers with GPRs */ |
|
1046 |
static inline void swap_gpr_tgpr (CPUPPCState *env) |
|
773 | 1047 |
{ |
774 |
return (msr_vr << MSR_VR) | |
|
775 |
(msr_ap << MSR_AP) | |
|
776 |
(msr_sa << MSR_SA) | |
|
777 |
(msr_key << MSR_KEY) | |
|
778 |
(msr_pow << MSR_POW) | |
|
779 |
(msr_tlb << MSR_TLB) | |
|
780 |
(msr_ile << MSR_ILE) | |
|
781 |
(msr_ee << MSR_EE) | |
|
782 |
(msr_pr << MSR_PR) | |
|
783 |
(msr_fp << MSR_FP) | |
|
784 |
(msr_me << MSR_ME) | |
|
785 |
(msr_fe0 << MSR_FE0) | |
|
786 |
(msr_se << MSR_SE) | |
|
787 |
(msr_be << MSR_BE) | |
|
788 |
(msr_fe1 << MSR_FE1) | |
|
789 |
(msr_al << MSR_AL) | |
|
790 |
(msr_ip << MSR_IP) | |
|
791 |
(msr_ir << MSR_IR) | |
|
792 |
(msr_dr << MSR_DR) | |
|
793 |
(msr_pe << MSR_PE) | |
|
794 |
(msr_px << MSR_PX) | |
|
795 |
(msr_ri << MSR_RI) | |
|
796 |
(msr_le << MSR_LE); |
|
1048 |
ppc_gpr_t tmp; |
|
1049 |
|
|
1050 |
tmp = env->gpr[0]; |
|
1051 |
env->gpr[0] = env->tgpr[0]; |
|
1052 |
env->tgpr[0] = tmp; |
|
1053 |
tmp = env->gpr[1]; |
|
1054 |
env->gpr[1] = env->tgpr[1]; |
|
1055 |
env->tgpr[1] = tmp; |
|
1056 |
tmp = env->gpr[2]; |
|
1057 |
env->gpr[2] = env->tgpr[2]; |
|
1058 |
env->tgpr[2] = tmp; |
|
1059 |
tmp = env->gpr[3]; |
|
1060 |
env->gpr[3] = env->tgpr[3]; |
|
1061 |
env->tgpr[3] = tmp; |
|
797 | 1062 |
} |
798 | 1063 |
|
799 |
void do_compute_hflags (CPUPPCState *env) |
|
1064 |
/* GDBstub can read and write MSR... */ |
|
1065 |
target_ulong do_load_msr (CPUPPCState *env) |
|
800 | 1066 |
{ |
801 |
/* Compute current hflags */ |
|
802 |
env->hflags = (msr_pr << MSR_PR) | (msr_le << MSR_LE) | |
|
803 |
(msr_fp << MSR_FP) | (msr_fe0 << MSR_FE0) | (msr_fe1 << MSR_FE1) | |
|
804 |
(msr_vr << MSR_VR) | (msr_ap << MSR_AP) | (msr_sa << MSR_SA) | |
|
805 |
(msr_se << MSR_SE) | (msr_be << MSR_BE); |
|
1067 |
return |
|
1068 |
#if defined (TARGET_PPC64) |
|
1069 |
(msr_sf << MSR_SF) | |
|
1070 |
(msr_isf << MSR_ISF) | |
|
1071 |
(msr_hv << MSR_HV) | |
|
1072 |
#endif |
|
1073 |
(msr_ucle << MSR_UCLE) | |
|
1074 |
(msr_vr << MSR_VR) | /* VR / SPE */ |
|
1075 |
(msr_ap << MSR_AP) | |
|
1076 |
(msr_sa << MSR_SA) | |
|
1077 |
(msr_key << MSR_KEY) | |
|
1078 |
(msr_pow << MSR_POW) | /* POW / WE */ |
|
1079 |
(msr_tlb << MSR_TLB) | /* TLB / TGPE / CE */ |
|
1080 |
(msr_ile << MSR_ILE) | |
|
1081 |
(msr_ee << MSR_EE) | |
|
1082 |
(msr_pr << MSR_PR) | |
|
1083 |
(msr_fp << MSR_FP) | |
|
1084 |
(msr_me << MSR_ME) | |
|
1085 |
(msr_fe0 << MSR_FE0) | |
|
1086 |
(msr_se << MSR_SE) | /* SE / DWE / UBLE */ |
|
1087 |
(msr_be << MSR_BE) | /* BE / DE */ |
|
1088 |
(msr_fe1 << MSR_FE1) | |
|
1089 |
(msr_al << MSR_AL) | |
|
1090 |
(msr_ip << MSR_IP) | |
|
1091 |
(msr_ir << MSR_IR) | /* IR / IS */ |
|
1092 |
(msr_dr << MSR_DR) | /* DR / DS */ |
|
1093 |
(msr_pe << MSR_PE) | /* PE / EP */ |
|
1094 |
(msr_px << MSR_PX) | /* PX / PMM */ |
|
1095 |
(msr_ri << MSR_RI) | |
|
1096 |
(msr_le << MSR_LE); |
|
806 | 1097 |
} |
807 | 1098 |
|
808 | 1099 |
void do_store_msr (CPUPPCState *env, target_ulong value) |
... | ... | |
812 | 1103 |
value &= env->msr_mask; |
813 | 1104 |
if (((value >> MSR_IR) & 1) != msr_ir || |
814 | 1105 |
((value >> MSR_DR) & 1) != msr_dr) { |
815 |
/* Flush all tlb when changing translation mode |
|
816 |
* When using software driven TLB, we may also need to reload |
|
817 |
* all defined TLBs |
|
818 |
*/ |
|
1106 |
/* Flush all tlb when changing translation mode */ |
|
819 | 1107 |
tlb_flush(env, 1); |
820 | 1108 |
env->interrupt_request |= CPU_INTERRUPT_EXITTB; |
821 | 1109 |
} |
... | ... | |
824 | 1112 |
fprintf(logfile, "%s: T0 %08lx\n", __func__, value); |
825 | 1113 |
} |
826 | 1114 |
#endif |
827 |
msr_vr = (value >> MSR_VR) & 1; |
|
828 |
msr_ap = (value >> MSR_AP) & 1; |
|
829 |
msr_sa = (value >> MSR_SA) & 1; |
|
830 |
msr_key = (value >> MSR_KEY) & 1; |
|
831 |
msr_pow = (value >> MSR_POW) & 1; |
|
832 |
msr_tlb = (value >> MSR_TLB) & 1; |
|
833 |
msr_ile = (value >> MSR_ILE) & 1; |
|
834 |
msr_ee = (value >> MSR_EE) & 1; |
|
835 |
msr_pr = (value >> MSR_PR) & 1; |
|
836 |
msr_fp = (value >> MSR_FP) & 1; |
|
837 |
msr_me = (value >> MSR_ME) & 1; |
|
838 |
msr_fe0 = (value >> MSR_FE0) & 1; |
|
839 |
msr_se = (value >> MSR_SE) & 1; |
|
840 |
msr_be = (value >> MSR_BE) & 1; |
|
841 |
msr_fe1 = (value >> MSR_FE1) & 1; |
|
842 |
msr_al = (value >> MSR_AL) & 1; |
|
843 |
msr_ip = (value >> MSR_IP) & 1; |
|
844 |
msr_ir = (value >> MSR_IR) & 1; |
|
845 |
msr_dr = (value >> MSR_DR) & 1; |
|
846 |
msr_pe = (value >> MSR_PE) & 1; |
|
847 |
msr_px = (value >> MSR_PX) & 1; |
|
848 |
msr_ri = (value >> MSR_RI) & 1; |
|
849 |
msr_le = (value >> MSR_LE) & 1; |
|
1115 |
switch (PPC_EXCP(env)) { |
|
1116 |
case PPC_FLAGS_EXCP_602: |
|
1117 |
case PPC_FLAGS_EXCP_603: |
|
1118 |
if (((value >> MSR_TGPR) & 1) != msr_tgpr) { |
|
1119 |
/* Swap temporary saved registers with GPRs */ |
|
1120 |
swap_gpr_tgpr(env); |
|
1121 |
} |
|
1122 |
break; |
|
1123 |
default: |
|
1124 |
break; |
|
1125 |
} |
|
1126 |
#if defined (TARGET_PPC64) |
|
1127 |
msr_sf = (value >> MSR_SF) & 1; |
|
1128 |
msr_isf = (value >> MSR_ISF) & 1; |
|
1129 |
msr_hv = (value >> MSR_HV) & 1; |
|
1130 |
#endif |
|
1131 |
msr_ucle = (value >> MSR_UCLE) & 1; |
|
1132 |
msr_vr = (value >> MSR_VR) & 1; /* VR / SPE */ |
|
1133 |
msr_ap = (value >> MSR_AP) & 1; |
|
1134 |
msr_sa = (value >> MSR_SA) & 1; |
|
1135 |
msr_key = (value >> MSR_KEY) & 1; |
|
1136 |
msr_pow = (value >> MSR_POW) & 1; /* POW / WE */ |
|
1137 |
msr_tlb = (value >> MSR_TLB) & 1; /* TLB / TGPR / CE */ |
|
1138 |
msr_ile = (value >> MSR_ILE) & 1; |
|
1139 |
msr_ee = (value >> MSR_EE) & 1; |
|
1140 |
msr_pr = (value >> MSR_PR) & 1; |
|
1141 |
msr_fp = (value >> MSR_FP) & 1; |
|
1142 |
msr_me = (value >> MSR_ME) & 1; |
|
1143 |
msr_fe0 = (value >> MSR_FE0) & 1; |
|
1144 |
msr_se = (value >> MSR_SE) & 1; /* SE / DWE / UBLE */ |
|
1145 |
msr_be = (value >> MSR_BE) & 1; /* BE / DE */ |
|
1146 |
msr_fe1 = (value >> MSR_FE1) & 1; |
|
1147 |
msr_al = (value >> MSR_AL) & 1; |
|
1148 |
msr_ip = (value >> MSR_IP) & 1; |
|
1149 |
msr_ir = (value >> MSR_IR) & 1; /* IR / IS */ |
|
1150 |
msr_dr = (value >> MSR_DR) & 1; /* DR / DS */ |
|
1151 |
msr_pe = (value >> MSR_PE) & 1; /* PE / EP */ |
|
1152 |
msr_px = (value >> MSR_PX) & 1; /* PX / PMM */ |
|
1153 |
msr_ri = (value >> MSR_RI) & 1; |
|
1154 |
msr_le = (value >> MSR_LE) & 1; |
|
850 | 1155 |
do_compute_hflags(env); |
851 | 1156 |
|
852 | 1157 |
enter_pm = 0; |
853 | 1158 |
switch (PPC_EXCP(env)) { |
854 | 1159 |
case PPC_FLAGS_EXCP_7x0: |
855 |
if (msr_pow == 1 && (env->spr[SPR_HID0] & 0x00E00000) != 0)
|
|
1160 |
if (msr_pow == 1 && (env->spr[SPR_HID0] & 0x00E00000) != 0)
|
|
856 | 1161 |
enter_pm = 1; |
857 | 1162 |
break; |
858 | 1163 |
default: |
... | ... | |
866 | 1171 |
} |
867 | 1172 |
} |
868 | 1173 |
|
869 |
float64 do_load_fpscr (CPUPPCState *env)
|
|
1174 |
void do_compute_hflags (CPUPPCState *env)
|
|
870 | 1175 |
{ |
871 |
/* The 32 MSB of the target fpr are undefined. |
|
872 |
* They'll be zero... |
|
873 |
*/ |
|
874 |
union { |
|
875 |
float64 d; |
|
876 |
struct { |
|
877 |
uint32_t u[2]; |
|
878 |
} s; |
|
879 |
} u; |
|
880 |
int i; |
|
881 |
|
|
882 |
#ifdef WORDS_BIGENDIAN |
|
883 |
#define WORD0 0 |
|
884 |
#define WORD1 1 |
|
885 |
#else |
|
886 |
#define WORD0 1 |
|
887 |
#define WORD1 0 |
|
1176 |
/* Compute current hflags */ |
|
1177 |
env->hflags = (msr_pr << MSR_PR) | (msr_le << MSR_LE) | |
|
1178 |
(msr_fp << MSR_FP) | (msr_fe0 << MSR_FE0) | (msr_fe1 << MSR_FE1) | |
|
1179 |
(msr_vr << MSR_VR) | (msr_ap << MSR_AP) | (msr_sa << MSR_SA) | |
|
1180 |
(msr_se << MSR_SE) | (msr_be << MSR_BE); |
|
1181 |
#if defined (TARGET_PPC64) |
|
1182 |
env->hflags |= (msr_sf << MSR_SF) | (msr_hv << MSR_HV); |
|
888 | 1183 |
#endif |
889 |
u.s.u[WORD0] = 0; |
|
890 |
u.s.u[WORD1] = 0; |
|
891 |
for (i = 0; i < 8; i++) |
|
892 |
u.s.u[WORD1] |= env->fpscr[i] << (4 * i); |
|
893 |
return u.d; |
|
894 |
} |
|
895 |
|
|
896 |
void do_store_fpscr (CPUPPCState *env, float64 f, uint32_t mask) |
|
897 |
{ |
|
898 |
/* |
|
899 |
* We use only the 32 LSB of the incoming fpr |
|
900 |
*/ |
|
901 |
union { |
|
902 |
double d; |
|
903 |
struct { |
|
904 |
uint32_t u[2]; |
|
905 |
} s; |
|
906 |
} u; |
|
907 |
int i, rnd_type; |
|
908 |
|
|
909 |
u.d = f; |
|
910 |
if (mask & 0x80) |
|
911 |
env->fpscr[0] = (env->fpscr[0] & 0x9) | ((u.s.u[WORD1] >> 28) & ~0x9); |
|
912 |
for (i = 1; i < 7; i++) { |
|
913 |
if (mask & (1 << (7 - i))) |
|
914 |
env->fpscr[i] = (u.s.u[WORD1] >> (4 * (7 - i))) & 0xF; |
|
915 |
} |
|
916 |
/* TODO: update FEX & VX */ |
|
917 |
/* Set rounding mode */ |
|
918 |
switch (env->fpscr[0] & 0x3) { |
|
919 |
case 0: |
|
920 |
/* Best approximation (round to nearest) */ |
|
921 |
rnd_type = float_round_nearest_even; |
|
922 |
break; |
|
923 |
case 1: |
|
924 |
/* Smaller magnitude (round toward zero) */ |
|
925 |
rnd_type = float_round_to_zero; |
|
926 |
break; |
|
927 |
case 2: |
|
928 |
/* Round toward +infinite */ |
|
929 |
rnd_type = float_round_up; |
|
930 |
break; |
|
931 |
default: |
|
932 |
case 3: |
|
933 |
/* Round toward -infinite */ |
|
934 |
rnd_type = float_round_down; |
|
935 |
break; |
|
936 |
} |
|
937 |
set_float_rounding_mode(rnd_type, &env->fp_status); |
|
938 | 1184 |
} |
939 | 1185 |
|
940 | 1186 |
/*****************************************************************************/ |
... | ... | |
944 | 1190 |
{ |
945 | 1191 |
env->exception_index = -1; |
946 | 1192 |
} |
947 |
#else |
|
1193 |
#else /* defined (CONFIG_USER_ONLY) */
|
|
948 | 1194 |
static void dump_syscall(CPUState *env) |
949 | 1195 |
{ |
950 |
fprintf(logfile, "syscall r0=0x%08x r3=0x%08x r4=0x%08x r5=0x%08x r6=0x%08x nip=0x%08x\n", |
|
1196 |
fprintf(logfile, "syscall r0=0x%08x r3=0x%08x r4=0x%08x " |
|
1197 |
"r5=0x%08x r6=0x%08x nip=0x%08x\n", |
|
951 | 1198 |
env->gpr[0], env->gpr[3], env->gpr[4], |
952 | 1199 |
env->gpr[5], env->gpr[6], env->nip); |
953 | 1200 |
} |
954 | 1201 |
|
955 | 1202 |
void do_interrupt (CPUState *env) |
956 | 1203 |
{ |
957 |
target_ulong msr, *srr_0, *srr_1, tmp;
|
|
1204 |
target_ulong msr, *srr_0, *srr_1; |
|
958 | 1205 |
int excp; |
959 | 1206 |
|
960 | 1207 |
excp = env->exception_index; |
... | ... | |
967 | 1214 |
if (loglevel != 0) { |
968 | 1215 |
fprintf(logfile, "Raise exception at 0x%08lx => 0x%08x (%02x)\n", |
969 | 1216 |
(unsigned long)env->nip, excp, env->error_code); |
970 |
cpu_dump_state(env, logfile, fprintf, 0);
|
|
1217 |
cpu_dump_state(env, logfile, fprintf, 0);
|
|
971 | 1218 |
} |
972 | 1219 |
} |
973 | 1220 |
#endif |
... | ... | |
978 | 1225 |
msr_pow = 0; |
979 | 1226 |
/* Generate informations in save/restore registers */ |
980 | 1227 |
switch (excp) { |
981 |
/* Generic PowerPC exceptions */
|
|
1228 |
/* Generic PowerPC exceptions */ |
|
982 | 1229 |
case EXCP_RESET: /* 0x0100 */ |
983 | 1230 |
if (PPC_EXCP(env) != PPC_FLAGS_EXCP_40x) { |
984 | 1231 |
if (msr_ip) |
... | ... | |
993 | 1240 |
if (msr_me == 0) { |
994 | 1241 |
cpu_abort(env, "Machine check exception while not allowed\n"); |
995 | 1242 |
} |
996 |
if (PPC_EXCP(env) == PPC_FLAGS_EXCP_40x) {
|
|
1243 |
if (unlikely(PPC_EXCP(env) == PPC_FLAGS_EXCP_40x)) {
|
|
997 | 1244 |
srr_0 = &env->spr[SPR_40x_SRR2]; |
998 | 1245 |
srr_1 = &env->spr[SPR_40x_SRR3]; |
999 | 1246 |
} |
... | ... | |
1004 | 1251 |
/* data location address has been stored |
1005 | 1252 |
* when the fault has been detected |
1006 | 1253 |
*/ |
1007 |
msr &= ~0xFFFF0000;
|
|
1254 |
msr &= ~0xFFFF0000;
|
|
1008 | 1255 |
#if defined (DEBUG_EXCEPTIONS) |
1009 |
if (loglevel) {
|
|
1010 |
fprintf(logfile, "DSI exception: DSISR=0x%08x, DAR=0x%08x\n",
|
|
1011 |
env->spr[SPR_DSISR], env->spr[SPR_DAR]);
|
|
1012 |
} else {
|
|
1013 |
printf("DSI exception: DSISR=0x%08x, DAR=0x%08x\n",
|
|
1014 |
env->spr[SPR_DSISR], env->spr[SPR_DAR]);
|
|
1015 |
}
|
|
1256 |
if (loglevel) {
|
|
1257 |
fprintf(logfile, "DSI exception: DSISR=0x%08x, DAR=0x%08x\n",
|
|
1258 |
env->spr[SPR_DSISR], env->spr[SPR_DAR]);
|
|
1259 |
} else {
|
|
1260 |
printf("DSI exception: DSISR=0x%08x, DAR=0x%08x\n",
|
|
1261 |
env->spr[SPR_DSISR], env->spr[SPR_DAR]);
|
|
1262 |
}
|
|
1016 | 1263 |
#endif |
1017 | 1264 |
goto store_next; |
1018 | 1265 |
case EXCP_ISI: /* 0x0400 */ |
1019 | 1266 |
/* Store exception cause */ |
1020 |
msr &= ~0xFFFF0000;
|
|
1267 |
msr &= ~0xFFFF0000;
|
|
1021 | 1268 |
msr |= env->error_code; |
1022 | 1269 |
#if defined (DEBUG_EXCEPTIONS) |
1023 |
if (loglevel != 0) {
|
|
1024 |
fprintf(logfile, "ISI exception: msr=0x%08x, nip=0x%08x\n",
|
|
1025 |
msr, env->nip);
|
|
1026 |
}
|
|
1270 |
if (loglevel != 0) {
|
|
1271 |
fprintf(logfile, "ISI exception: msr=0x%08x, nip=0x%08x\n",
|
|
1272 |
msr, env->nip);
|
|
1273 |
}
|
|
1027 | 1274 |
#endif |
1028 | 1275 |
goto store_next; |
1029 | 1276 |
case EXCP_EXTERNAL: /* 0x0500 */ |
... | ... | |
1039 | 1286 |
} |
1040 | 1287 |
goto store_next; |
1041 | 1288 |
case EXCP_ALIGN: /* 0x0600 */ |
1042 |
if (PPC_EXCP(env) != PPC_FLAGS_EXCP_601) {
|
|
1289 |
if (likely(PPC_EXCP(env) != PPC_FLAGS_EXCP_601)) {
|
|
1043 | 1290 |
/* Store exception cause */ |
1044 | 1291 |
/* Get rS/rD and rA from faulting opcode */ |
1045 | 1292 |
env->spr[SPR_DSISR] |= |
... | ... | |
1063 | 1310 |
printf("Ignore floating point exception\n"); |
1064 | 1311 |
#endif |
1065 | 1312 |
return; |
1066 |
} |
|
1313 |
}
|
|
1067 | 1314 |
msr |= 0x00100000; |
1068 | 1315 |
/* Set FX */ |
1069 | 1316 |
env->fpscr[7] |= 0x8; |
... | ... | |
1071 | 1318 |
if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) & |
1072 | 1319 |
((env->fpscr[1] << 1) | (env->fpscr[0] >> 3))) |
1073 | 1320 |
env->fpscr[7] |= 0x4; |
1074 |
break; |
|
1321 |
break;
|
|
1075 | 1322 |
case EXCP_INVAL: |
1076 |
// printf("Invalid instruction at 0x%08x\n", env->nip);
|
|
1323 |
// printf("Invalid instruction at 0x%08x\n", env->nip);
|
|
1077 | 1324 |
msr |= 0x00080000; |
1078 |
break; |
|
1325 |
break;
|
|
1079 | 1326 |
case EXCP_PRIV: |
1080 | 1327 |
msr |= 0x00040000; |
1081 |
break; |
|
1328 |
break;
|
|
1082 | 1329 |
case EXCP_TRAP: |
1083 | 1330 |
msr |= 0x00020000; |
1084 | 1331 |
break; |
1085 | 1332 |
default: |
1086 | 1333 |
/* Should never occur */ |
1087 |
break; |
|
1088 |
} |
|
1334 |
break;
|
|
1335 |
}
|
|
1089 | 1336 |
msr |= 0x00010000; |
1090 | 1337 |
goto store_current; |
1091 | 1338 |
case EXCP_NO_FP: /* 0x0800 */ |
... | ... | |
1125 | 1372 |
cpu_abort(env, "Floating point assist exception " |
1126 | 1373 |
"is not implemented yet !\n"); |
1127 | 1374 |
goto store_next; |
1128 |
/* 64 bits PowerPC exceptions */ |
|
1375 |
/* 64 bits PowerPC exceptions */
|
|
1129 | 1376 |
case EXCP_DSEG: /* 0x0380 */ |
1130 | 1377 |
/* XXX: TODO */ |
1131 | 1378 |
cpu_abort(env, "Data segment exception is not implemented yet !\n"); |
... | ... | |
1141 | 1388 |
/* Requeue it */ |
1142 | 1389 |
env->interrupt_request |= CPU_INTERRUPT_TIMER; |
1143 | 1390 |
#endif |
1144 |
return; |
|
1391 |
return;
|
|
1145 | 1392 |
} |
1146 |
cpu_abort(env, |
|
1147 |
"Hypervisor decrementer exception is not implemented yet !\n"); |
|
1393 |
/* XXX: TODO */ |
|
1394 |
cpu_abort(env, "Hypervisor decrementer exception is not implemented " |
|
1395 |
"yet !\n"); |
|
1148 | 1396 |
goto store_next; |
1149 | 1397 |
/* Implementation specific exceptions */ |
1150 | 1398 |
case 0x0A00: |
1151 |
if (PPC_EXCP(env) != PPC_FLAGS_EXCP_602) { |
|
1399 |
if (likely(env->spr[SPR_PVR] == CPU_PPC_G2 || |
|
1400 |
env->spr[SPR_PVR] == CPU_PPC_G2LE)) { |
|
1152 | 1401 |
/* Critical interrupt on G2 */ |
1153 | 1402 |
/* XXX: TODO */ |
1154 | 1403 |
cpu_abort(env, "G2 critical interrupt is not implemented yet !\n"); |
... | ... | |
1186 | 1435 |
case PPC_FLAGS_EXCP_602: |
1187 | 1436 |
case PPC_FLAGS_EXCP_603: |
1188 | 1437 |
/* ITLBMISS on 602/603 */ |
1189 |
msr &= ~0xF00F0000; |
|
1190 |
msr_tgpr = 1; |
|
1191 | 1438 |
goto store_gprs; |
1439 |
case PPC_FLAGS_EXCP_7x5: |
|
1440 |
/* ITLBMISS on 745/755 */ |
|
1441 |
goto tlb_miss; |
|
1192 | 1442 |
default: |
1193 | 1443 |
cpu_abort(env, "Invalid exception 0x1000 !\n"); |
1194 | 1444 |
break; |
... | ... | |
1198 | 1448 |
switch (PPC_EXCP(env)) { |
1199 | 1449 |
case PPC_FLAGS_EXCP_40x: |
1200 | 1450 |
/* FIT on 4xx */ |
1201 |
cpu_abort(env, "40x FIT exception is not implemented yet !\n"); |
|
1202 | 1451 |
/* XXX: TODO */ |
1452 |
cpu_abort(env, "40x FIT exception is not implemented yet !\n"); |
|
1203 | 1453 |
goto store_next; |
1204 | 1454 |
default: |
Also available in: Unified diff