]>
Commit | Line | Data |
---|---|---|
79aceca5 | 1 | /* |
3fc6c082 | 2 | * PowerPC emulation helpers for qemu. |
5fafdf24 | 3 | * |
76a66253 | 4 | * Copyright (c) 2003-2007 Jocelyn Mayer |
79aceca5 FB |
5 | * |
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. | |
10 | * | |
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. | |
15 | * | |
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 | |
19 | */ | |
fdabc366 FB |
20 | #include <stdarg.h> |
21 | #include <stdlib.h> | |
22 | #include <stdio.h> | |
23 | #include <string.h> | |
24 | #include <inttypes.h> | |
25 | #include <signal.h> | |
26 | #include <assert.h> | |
27 | ||
28 | #include "cpu.h" | |
29 | #include "exec-all.h" | |
9a64fbe4 FB |
30 | |
31 | //#define DEBUG_MMU | |
32 | //#define DEBUG_BATS | |
76a66253 | 33 | //#define DEBUG_SOFTWARE_TLB |
9a64fbe4 | 34 | //#define DEBUG_EXCEPTIONS |
fdabc366 | 35 | //#define FLUSH_ALL_TLBS |
9a64fbe4 | 36 | |
9a64fbe4 | 37 | /*****************************************************************************/ |
3fc6c082 | 38 | /* PowerPC MMU emulation */ |
a541f297 | 39 | |
d9bce9d9 | 40 | #if defined(CONFIG_USER_ONLY) |
e96efcfc | 41 | int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, |
24741ef3 FB |
42 | int is_user, int is_softmmu) |
43 | { | |
44 | int exception, error_code; | |
d9bce9d9 | 45 | |
24741ef3 | 46 | if (rw == 2) { |
e1833e1f | 47 | exception = POWERPC_EXCP_ISI; |
24741ef3 FB |
48 | error_code = 0; |
49 | } else { | |
e1833e1f | 50 | exception = POWERPC_EXCP_DSI; |
24741ef3 FB |
51 | error_code = 0; |
52 | if (rw) | |
53 | error_code |= 0x02000000; | |
54 | env->spr[SPR_DAR] = address; | |
55 | env->spr[SPR_DSISR] = error_code; | |
56 | } | |
57 | env->exception_index = exception; | |
58 | env->error_code = error_code; | |
76a66253 | 59 | |
24741ef3 FB |
60 | return 1; |
61 | } | |
76a66253 | 62 | |
9b3c35e0 | 63 | target_phys_addr_t cpu_get_phys_page_debug (CPUState *env, target_ulong addr) |
24741ef3 FB |
64 | { |
65 | return addr; | |
66 | } | |
36081602 | 67 | |
24741ef3 | 68 | #else |
76a66253 JM |
69 | /* Common routines used by software and hardware TLBs emulation */ |
70 | static inline int pte_is_valid (target_ulong pte0) | |
71 | { | |
72 | return pte0 & 0x80000000 ? 1 : 0; | |
73 | } | |
74 | ||
75 | static inline void pte_invalidate (target_ulong *pte0) | |
76 | { | |
77 | *pte0 &= ~0x80000000; | |
78 | } | |
79 | ||
caa4039c JM |
80 | #if defined(TARGET_PPC64) |
81 | static inline int pte64_is_valid (target_ulong pte0) | |
82 | { | |
83 | return pte0 & 0x0000000000000001ULL ? 1 : 0; | |
84 | } | |
85 | ||
86 | static inline void pte64_invalidate (target_ulong *pte0) | |
87 | { | |
88 | *pte0 &= ~0x0000000000000001ULL; | |
89 | } | |
90 | #endif | |
91 | ||
76a66253 JM |
92 | #define PTE_PTEM_MASK 0x7FFFFFBF |
93 | #define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) | |
caa4039c JM |
94 | #if defined(TARGET_PPC64) |
95 | #define PTE64_PTEM_MASK 0xFFFFFFFFFFFFFF80ULL | |
96 | #define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F) | |
97 | #endif | |
76a66253 | 98 | |
caa4039c JM |
99 | static inline int _pte_check (mmu_ctx_t *ctx, int is_64b, |
100 | target_ulong pte0, target_ulong pte1, | |
101 | int h, int rw) | |
76a66253 | 102 | { |
caa4039c JM |
103 | target_ulong ptem, mmask; |
104 | int access, ret, pteh, ptev; | |
76a66253 JM |
105 | |
106 | access = 0; | |
107 | ret = -1; | |
108 | /* Check validity and table match */ | |
caa4039c JM |
109 | #if defined(TARGET_PPC64) |
110 | if (is_64b) { | |
111 | ptev = pte64_is_valid(pte0); | |
112 | pteh = (pte0 >> 1) & 1; | |
113 | } else | |
114 | #endif | |
115 | { | |
116 | ptev = pte_is_valid(pte0); | |
117 | pteh = (pte0 >> 6) & 1; | |
118 | } | |
119 | if (ptev && h == pteh) { | |
76a66253 | 120 | /* Check vsid & api */ |
caa4039c JM |
121 | #if defined(TARGET_PPC64) |
122 | if (is_64b) { | |
123 | ptem = pte0 & PTE64_PTEM_MASK; | |
124 | mmask = PTE64_CHECK_MASK; | |
125 | } else | |
126 | #endif | |
127 | { | |
128 | ptem = pte0 & PTE_PTEM_MASK; | |
129 | mmask = PTE_CHECK_MASK; | |
130 | } | |
131 | if (ptem == ctx->ptem) { | |
76a66253 JM |
132 | if (ctx->raddr != (target_ulong)-1) { |
133 | /* all matches should have equal RPN, WIMG & PP */ | |
caa4039c JM |
134 | if ((ctx->raddr & mmask) != (pte1 & mmask)) { |
135 | if (loglevel != 0) | |
76a66253 JM |
136 | fprintf(logfile, "Bad RPN/WIMG/PP\n"); |
137 | return -3; | |
138 | } | |
139 | } | |
140 | /* Compute access rights */ | |
141 | if (ctx->key == 0) { | |
142 | access = PAGE_READ; | |
143 | if ((pte1 & 0x00000003) != 0x3) | |
144 | access |= PAGE_WRITE; | |
145 | } else { | |
146 | switch (pte1 & 0x00000003) { | |
147 | case 0x0: | |
148 | access = 0; | |
149 | break; | |
150 | case 0x1: | |
151 | case 0x3: | |
152 | access = PAGE_READ; | |
153 | break; | |
154 | case 0x2: | |
155 | access = PAGE_READ | PAGE_WRITE; | |
156 | break; | |
157 | } | |
158 | } | |
159 | /* Keep the matching PTE informations */ | |
160 | ctx->raddr = pte1; | |
161 | ctx->prot = access; | |
162 | if ((rw == 0 && (access & PAGE_READ)) || | |
163 | (rw == 1 && (access & PAGE_WRITE))) { | |
164 | /* Access granted */ | |
165 | #if defined (DEBUG_MMU) | |
4a057712 | 166 | if (loglevel != 0) |
76a66253 JM |
167 | fprintf(logfile, "PTE access granted !\n"); |
168 | #endif | |
169 | ret = 0; | |
170 | } else { | |
171 | /* Access right violation */ | |
172 | #if defined (DEBUG_MMU) | |
4a057712 | 173 | if (loglevel != 0) |
76a66253 JM |
174 | fprintf(logfile, "PTE access rejected\n"); |
175 | #endif | |
176 | ret = -2; | |
177 | } | |
178 | } | |
179 | } | |
180 | ||
181 | return ret; | |
182 | } | |
183 | ||
caa4039c JM |
184 | static int pte32_check (mmu_ctx_t *ctx, |
185 | target_ulong pte0, target_ulong pte1, int h, int rw) | |
186 | { | |
187 | return _pte_check(ctx, 0, pte0, pte1, h, rw); | |
188 | } | |
189 | ||
190 | #if defined(TARGET_PPC64) | |
191 | static int pte64_check (mmu_ctx_t *ctx, | |
192 | target_ulong pte0, target_ulong pte1, int h, int rw) | |
193 | { | |
194 | return _pte_check(ctx, 1, pte0, pte1, h, rw); | |
195 | } | |
196 | #endif | |
197 | ||
76a66253 JM |
198 | static int pte_update_flags (mmu_ctx_t *ctx, target_ulong *pte1p, |
199 | int ret, int rw) | |
200 | { | |
201 | int store = 0; | |
202 | ||
203 | /* Update page flags */ | |
204 | if (!(*pte1p & 0x00000100)) { | |
205 | /* Update accessed flag */ | |
206 | *pte1p |= 0x00000100; | |
207 | store = 1; | |
208 | } | |
209 | if (!(*pte1p & 0x00000080)) { | |
210 | if (rw == 1 && ret == 0) { | |
211 | /* Update changed flag */ | |
212 | *pte1p |= 0x00000080; | |
213 | store = 1; | |
214 | } else { | |
215 | /* Force page fault for first write access */ | |
216 | ctx->prot &= ~PAGE_WRITE; | |
217 | } | |
218 | } | |
219 | ||
220 | return store; | |
221 | } | |
222 | ||
223 | /* Software driven TLB helpers */ | |
224 | static int ppc6xx_tlb_getnum (CPUState *env, target_ulong eaddr, | |
225 | int way, int is_code) | |
226 | { | |
227 | int nr; | |
228 | ||
229 | /* Select TLB num in a way from address */ | |
230 | nr = (eaddr >> TARGET_PAGE_BITS) & (env->tlb_per_way - 1); | |
231 | /* Select TLB way */ | |
232 | nr += env->tlb_per_way * way; | |
233 | /* 6xx have separate TLBs for instructions and data */ | |
234 | if (is_code && env->id_tlbs == 1) | |
235 | nr += env->nb_tlb; | |
236 | ||
237 | return nr; | |
238 | } | |
239 | ||
daf4f96e | 240 | static void ppc6xx_tlb_invalidate_all (CPUState *env) |
76a66253 | 241 | { |
1d0a48fb | 242 | ppc6xx_tlb_t *tlb; |
76a66253 JM |
243 | int nr, max; |
244 | ||
245 | #if defined (DEBUG_SOFTWARE_TLB) && 0 | |
246 | if (loglevel != 0) { | |
247 | fprintf(logfile, "Invalidate all TLBs\n"); | |
248 | } | |
249 | #endif | |
250 | /* Invalidate all defined software TLB */ | |
251 | max = env->nb_tlb; | |
252 | if (env->id_tlbs == 1) | |
253 | max *= 2; | |
254 | for (nr = 0; nr < max; nr++) { | |
1d0a48fb | 255 | tlb = &env->tlb[nr].tlb6; |
76a66253 JM |
256 | pte_invalidate(&tlb->pte0); |
257 | } | |
76a66253 | 258 | tlb_flush(env, 1); |
76a66253 JM |
259 | } |
260 | ||
261 | static inline void __ppc6xx_tlb_invalidate_virt (CPUState *env, | |
262 | target_ulong eaddr, | |
263 | int is_code, int match_epn) | |
264 | { | |
4a057712 | 265 | #if !defined(FLUSH_ALL_TLBS) |
1d0a48fb | 266 | ppc6xx_tlb_t *tlb; |
76a66253 JM |
267 | int way, nr; |
268 | ||
76a66253 JM |
269 | /* Invalidate ITLB + DTLB, all ways */ |
270 | for (way = 0; way < env->nb_ways; way++) { | |
271 | nr = ppc6xx_tlb_getnum(env, eaddr, way, is_code); | |
1d0a48fb | 272 | tlb = &env->tlb[nr].tlb6; |
76a66253 JM |
273 | if (pte_is_valid(tlb->pte0) && (match_epn == 0 || eaddr == tlb->EPN)) { |
274 | #if defined (DEBUG_SOFTWARE_TLB) | |
275 | if (loglevel != 0) { | |
1b9eb036 | 276 | fprintf(logfile, "TLB invalidate %d/%d " ADDRX "\n", |
76a66253 JM |
277 | nr, env->nb_tlb, eaddr); |
278 | } | |
279 | #endif | |
280 | pte_invalidate(&tlb->pte0); | |
281 | tlb_flush_page(env, tlb->EPN); | |
282 | } | |
283 | } | |
284 | #else | |
285 | /* XXX: PowerPC specification say this is valid as well */ | |
286 | ppc6xx_tlb_invalidate_all(env); | |
287 | #endif | |
288 | } | |
289 | ||
daf4f96e JM |
290 | static void ppc6xx_tlb_invalidate_virt (CPUState *env, target_ulong eaddr, |
291 | int is_code) | |
76a66253 JM |
292 | { |
293 | __ppc6xx_tlb_invalidate_virt(env, eaddr, is_code, 0); | |
294 | } | |
295 | ||
296 | void ppc6xx_tlb_store (CPUState *env, target_ulong EPN, int way, int is_code, | |
297 | target_ulong pte0, target_ulong pte1) | |
298 | { | |
1d0a48fb | 299 | ppc6xx_tlb_t *tlb; |
76a66253 JM |
300 | int nr; |
301 | ||
302 | nr = ppc6xx_tlb_getnum(env, EPN, way, is_code); | |
1d0a48fb | 303 | tlb = &env->tlb[nr].tlb6; |
76a66253 JM |
304 | #if defined (DEBUG_SOFTWARE_TLB) |
305 | if (loglevel != 0) { | |
5fafdf24 | 306 | fprintf(logfile, "Set TLB %d/%d EPN " ADDRX " PTE0 " ADDRX |
1b9eb036 | 307 | " PTE1 " ADDRX "\n", nr, env->nb_tlb, EPN, pte0, pte1); |
76a66253 JM |
308 | } |
309 | #endif | |
310 | /* Invalidate any pending reference in Qemu for this virtual address */ | |
311 | __ppc6xx_tlb_invalidate_virt(env, EPN, is_code, 1); | |
312 | tlb->pte0 = pte0; | |
313 | tlb->pte1 = pte1; | |
314 | tlb->EPN = EPN; | |
76a66253 JM |
315 | /* Store last way for LRU mechanism */ |
316 | env->last_way = way; | |
317 | } | |
318 | ||
319 | static int ppc6xx_tlb_check (CPUState *env, mmu_ctx_t *ctx, | |
320 | target_ulong eaddr, int rw, int access_type) | |
321 | { | |
1d0a48fb | 322 | ppc6xx_tlb_t *tlb; |
76a66253 JM |
323 | int nr, best, way; |
324 | int ret; | |
d9bce9d9 | 325 | |
76a66253 JM |
326 | best = -1; |
327 | ret = -1; /* No TLB found */ | |
328 | for (way = 0; way < env->nb_ways; way++) { | |
329 | nr = ppc6xx_tlb_getnum(env, eaddr, way, | |
330 | access_type == ACCESS_CODE ? 1 : 0); | |
1d0a48fb | 331 | tlb = &env->tlb[nr].tlb6; |
76a66253 JM |
332 | /* This test "emulates" the PTE index match for hardware TLBs */ |
333 | if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) { | |
334 | #if defined (DEBUG_SOFTWARE_TLB) | |
335 | if (loglevel != 0) { | |
1b9eb036 JM |
336 | fprintf(logfile, "TLB %d/%d %s [" ADDRX " " ADDRX |
337 | "] <> " ADDRX "\n", | |
76a66253 JM |
338 | nr, env->nb_tlb, |
339 | pte_is_valid(tlb->pte0) ? "valid" : "inval", | |
340 | tlb->EPN, tlb->EPN + TARGET_PAGE_SIZE, eaddr); | |
341 | } | |
342 | #endif | |
343 | continue; | |
344 | } | |
345 | #if defined (DEBUG_SOFTWARE_TLB) | |
346 | if (loglevel != 0) { | |
1b9eb036 JM |
347 | fprintf(logfile, "TLB %d/%d %s " ADDRX " <> " ADDRX " " ADDRX |
348 | " %c %c\n", | |
76a66253 JM |
349 | nr, env->nb_tlb, |
350 | pte_is_valid(tlb->pte0) ? "valid" : "inval", | |
351 | tlb->EPN, eaddr, tlb->pte1, | |
352 | rw ? 'S' : 'L', access_type == ACCESS_CODE ? 'I' : 'D'); | |
353 | } | |
354 | #endif | |
caa4039c | 355 | switch (pte32_check(ctx, tlb->pte0, tlb->pte1, 0, rw)) { |
76a66253 JM |
356 | case -3: |
357 | /* TLB inconsistency */ | |
358 | return -1; | |
359 | case -2: | |
360 | /* Access violation */ | |
361 | ret = -2; | |
362 | best = nr; | |
363 | break; | |
364 | case -1: | |
365 | default: | |
366 | /* No match */ | |
367 | break; | |
368 | case 0: | |
369 | /* access granted */ | |
370 | /* XXX: we should go on looping to check all TLBs consistency | |
371 | * but we can speed-up the whole thing as the | |
372 | * result would be undefined if TLBs are not consistent. | |
373 | */ | |
374 | ret = 0; | |
375 | best = nr; | |
376 | goto done; | |
377 | } | |
378 | } | |
379 | if (best != -1) { | |
380 | done: | |
381 | #if defined (DEBUG_SOFTWARE_TLB) | |
4a057712 | 382 | if (loglevel != 0) { |
76a66253 JM |
383 | fprintf(logfile, "found TLB at addr 0x%08lx prot=0x%01x ret=%d\n", |
384 | ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); | |
385 | } | |
386 | #endif | |
387 | /* Update page flags */ | |
1d0a48fb | 388 | pte_update_flags(ctx, &env->tlb[best].tlb6.pte1, ret, rw); |
76a66253 JM |
389 | } |
390 | ||
391 | return ret; | |
392 | } | |
393 | ||
9a64fbe4 | 394 | /* Perform BAT hit & translation */ |
76a66253 JM |
395 | static int get_bat (CPUState *env, mmu_ctx_t *ctx, |
396 | target_ulong virtual, int rw, int type) | |
9a64fbe4 | 397 | { |
76a66253 JM |
398 | target_ulong *BATlt, *BATut, *BATu, *BATl; |
399 | target_ulong base, BEPIl, BEPIu, bl; | |
9a64fbe4 FB |
400 | int i; |
401 | int ret = -1; | |
402 | ||
403 | #if defined (DEBUG_BATS) | |
4a057712 | 404 | if (loglevel != 0) { |
1b9eb036 | 405 | fprintf(logfile, "%s: %cBAT v 0x" ADDRX "\n", __func__, |
76a66253 | 406 | type == ACCESS_CODE ? 'I' : 'D', virtual); |
9a64fbe4 | 407 | } |
9a64fbe4 FB |
408 | #endif |
409 | switch (type) { | |
410 | case ACCESS_CODE: | |
411 | BATlt = env->IBAT[1]; | |
412 | BATut = env->IBAT[0]; | |
413 | break; | |
414 | default: | |
415 | BATlt = env->DBAT[1]; | |
416 | BATut = env->DBAT[0]; | |
417 | break; | |
418 | } | |
419 | #if defined (DEBUG_BATS) | |
4a057712 | 420 | if (loglevel != 0) { |
1b9eb036 | 421 | fprintf(logfile, "%s...: %cBAT v 0x" ADDRX "\n", __func__, |
76a66253 | 422 | type == ACCESS_CODE ? 'I' : 'D', virtual); |
9a64fbe4 | 423 | } |
9a64fbe4 FB |
424 | #endif |
425 | base = virtual & 0xFFFC0000; | |
426 | for (i = 0; i < 4; i++) { | |
427 | BATu = &BATut[i]; | |
428 | BATl = &BATlt[i]; | |
429 | BEPIu = *BATu & 0xF0000000; | |
430 | BEPIl = *BATu & 0x0FFE0000; | |
431 | bl = (*BATu & 0x00001FFC) << 15; | |
432 | #if defined (DEBUG_BATS) | |
4a057712 | 433 | if (loglevel != 0) { |
5fafdf24 | 434 | fprintf(logfile, "%s: %cBAT%d v 0x" ADDRX " BATu 0x" ADDRX |
1b9eb036 | 435 | " BATl 0x" ADDRX "\n", |
9a64fbe4 FB |
436 | __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, |
437 | *BATu, *BATl); | |
9a64fbe4 FB |
438 | } |
439 | #endif | |
440 | if ((virtual & 0xF0000000) == BEPIu && | |
441 | ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { | |
442 | /* BAT matches */ | |
443 | if ((msr_pr == 0 && (*BATu & 0x00000002)) || | |
444 | (msr_pr == 1 && (*BATu & 0x00000001))) { | |
445 | /* Get physical address */ | |
76a66253 | 446 | ctx->raddr = (*BATl & 0xF0000000) | |
9a64fbe4 | 447 | ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | |
a541f297 | 448 | (virtual & 0x0001F000); |
9a64fbe4 | 449 | if (*BATl & 0x00000001) |
76a66253 | 450 | ctx->prot = PAGE_READ; |
9a64fbe4 | 451 | if (*BATl & 0x00000002) |
76a66253 | 452 | ctx->prot = PAGE_WRITE | PAGE_READ; |
9a64fbe4 | 453 | #if defined (DEBUG_BATS) |
4a057712 JM |
454 | if (loglevel != 0) { |
455 | fprintf(logfile, "BAT %d match: r 0x" PADDRX | |
1b9eb036 | 456 | " prot=%c%c\n", |
76a66253 JM |
457 | i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', |
458 | ctx->prot & PAGE_WRITE ? 'W' : '-'); | |
9a64fbe4 FB |
459 | } |
460 | #endif | |
461 | ret = 0; | |
462 | break; | |
463 | } | |
464 | } | |
465 | } | |
466 | if (ret < 0) { | |
467 | #if defined (DEBUG_BATS) | |
4a057712 JM |
468 | if (loglevel != 0) { |
469 | fprintf(logfile, "no BAT match for 0x" ADDRX ":\n", virtual); | |
470 | for (i = 0; i < 4; i++) { | |
471 | BATu = &BATut[i]; | |
472 | BATl = &BATlt[i]; | |
473 | BEPIu = *BATu & 0xF0000000; | |
474 | BEPIl = *BATu & 0x0FFE0000; | |
475 | bl = (*BATu & 0x00001FFC) << 15; | |
476 | fprintf(logfile, "%s: %cBAT%d v 0x" ADDRX " BATu 0x" ADDRX | |
477 | " BATl 0x" ADDRX " \n\t" | |
478 | "0x" ADDRX " 0x" ADDRX " 0x" ADDRX "\n", | |
479 | __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, | |
480 | *BATu, *BATl, BEPIu, BEPIl, bl); | |
481 | } | |
9a64fbe4 FB |
482 | } |
483 | #endif | |
9a64fbe4 FB |
484 | } |
485 | /* No hit */ | |
486 | return ret; | |
487 | } | |
488 | ||
489 | /* PTE table lookup */ | |
caa4039c | 490 | static inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h, int rw) |
9a64fbe4 | 491 | { |
76a66253 JM |
492 | target_ulong base, pte0, pte1; |
493 | int i, good = -1; | |
caa4039c | 494 | int ret, r; |
9a64fbe4 | 495 | |
76a66253 JM |
496 | ret = -1; /* No entry found */ |
497 | base = ctx->pg_addr[h]; | |
9a64fbe4 | 498 | for (i = 0; i < 8; i++) { |
caa4039c JM |
499 | #if defined(TARGET_PPC64) |
500 | if (is_64b) { | |
501 | pte0 = ldq_phys(base + (i * 16)); | |
502 | pte1 = ldq_phys(base + (i * 16) + 8); | |
503 | r = pte64_check(ctx, pte0, pte1, h, rw); | |
504 | } else | |
505 | #endif | |
506 | { | |
507 | pte0 = ldl_phys(base + (i * 8)); | |
508 | pte1 = ldl_phys(base + (i * 8) + 4); | |
509 | r = pte32_check(ctx, pte0, pte1, h, rw); | |
510 | } | |
9a64fbe4 | 511 | #if defined (DEBUG_MMU) |
caa4039c | 512 | if (loglevel != 0) { |
5fafdf24 | 513 | fprintf(logfile, "Load pte from 0x" ADDRX " => 0x" ADDRX |
1b9eb036 JM |
514 | " 0x" ADDRX " %d %d %d 0x" ADDRX "\n", |
515 | base + (i * 8), pte0, pte1, | |
caa4039c | 516 | (int)(pte0 >> 31), h, (int)((pte0 >> 6) & 1), ctx->ptem); |
76a66253 | 517 | } |
9a64fbe4 | 518 | #endif |
caa4039c | 519 | switch (r) { |
76a66253 JM |
520 | case -3: |
521 | /* PTE inconsistency */ | |
522 | return -1; | |
523 | case -2: | |
524 | /* Access violation */ | |
525 | ret = -2; | |
526 | good = i; | |
527 | break; | |
528 | case -1: | |
529 | default: | |
530 | /* No PTE match */ | |
531 | break; | |
532 | case 0: | |
533 | /* access granted */ | |
534 | /* XXX: we should go on looping to check all PTEs consistency | |
535 | * but if we can speed-up the whole thing as the | |
536 | * result would be undefined if PTEs are not consistent. | |
537 | */ | |
538 | ret = 0; | |
539 | good = i; | |
540 | goto done; | |
9a64fbe4 FB |
541 | } |
542 | } | |
543 | if (good != -1) { | |
76a66253 | 544 | done: |
9a64fbe4 | 545 | #if defined (DEBUG_MMU) |
4a057712 JM |
546 | if (loglevel != 0) { |
547 | fprintf(logfile, "found PTE at addr 0x" PADDRX " prot=0x%01x " | |
1b9eb036 | 548 | "ret=%d\n", |
76a66253 JM |
549 | ctx->raddr, ctx->prot, ret); |
550 | } | |
9a64fbe4 FB |
551 | #endif |
552 | /* Update page flags */ | |
76a66253 | 553 | pte1 = ctx->raddr; |
caa4039c JM |
554 | if (pte_update_flags(ctx, &pte1, ret, rw) == 1) { |
555 | #if defined(TARGET_PPC64) | |
556 | if (is_64b) { | |
557 | stq_phys_notdirty(base + (good * 16) + 8, pte1); | |
558 | } else | |
559 | #endif | |
560 | { | |
561 | stl_phys_notdirty(base + (good * 8) + 4, pte1); | |
562 | } | |
563 | } | |
9a64fbe4 FB |
564 | } |
565 | ||
566 | return ret; | |
79aceca5 FB |
567 | } |
568 | ||
caa4039c JM |
569 | static int find_pte32 (mmu_ctx_t *ctx, int h, int rw) |
570 | { | |
571 | return _find_pte(ctx, 0, h, rw); | |
572 | } | |
573 | ||
574 | #if defined(TARGET_PPC64) | |
575 | static int find_pte64 (mmu_ctx_t *ctx, int h, int rw) | |
576 | { | |
577 | return _find_pte(ctx, 1, h, rw); | |
578 | } | |
579 | #endif | |
580 | ||
581 | static inline int find_pte (CPUState *env, mmu_ctx_t *ctx, int h, int rw) | |
582 | { | |
583 | #if defined(TARGET_PPC64) | |
a750fc0b JM |
584 | if (env->mmu_model == POWERPC_MMU_64B || |
585 | env->mmu_model == POWERPC_MMU_64BRIDGE) | |
caa4039c JM |
586 | return find_pte64(ctx, h, rw); |
587 | #endif | |
588 | ||
589 | return find_pte32(ctx, h, rw); | |
590 | } | |
591 | ||
76a66253 | 592 | static inline target_phys_addr_t get_pgaddr (target_phys_addr_t sdr1, |
caa4039c | 593 | int sdr_sh, |
76a66253 JM |
594 | target_phys_addr_t hash, |
595 | target_phys_addr_t mask) | |
79aceca5 | 596 | { |
caa4039c JM |
597 | return (sdr1 & ((target_ulong)(-1ULL) << sdr_sh)) | (hash & mask); |
598 | } | |
599 | ||
600 | #if defined(TARGET_PPC64) | |
601 | static int slb_lookup (CPUState *env, target_ulong eaddr, | |
602 | target_ulong *vsid, target_ulong *page_mask, int *attr) | |
603 | { | |
604 | target_phys_addr_t sr_base; | |
605 | target_ulong mask; | |
606 | uint64_t tmp64; | |
607 | uint32_t tmp; | |
608 | int n, ret; | |
609 | int slb_nr; | |
610 | ||
611 | ret = -5; | |
612 | sr_base = env->spr[SPR_ASR]; | |
613 | mask = 0x0000000000000000ULL; /* Avoid gcc warning */ | |
614 | #if 0 /* XXX: Fix this */ | |
615 | slb_nr = env->slb_nr; | |
616 | #else | |
617 | slb_nr = 32; | |
618 | #endif | |
619 | for (n = 0; n < slb_nr; n++) { | |
620 | tmp64 = ldq_phys(sr_base); | |
621 | if (tmp64 & 0x0000000008000000ULL) { | |
622 | /* SLB entry is valid */ | |
623 | switch (tmp64 & 0x0000000006000000ULL) { | |
624 | case 0x0000000000000000ULL: | |
625 | /* 256 MB segment */ | |
626 | mask = 0xFFFFFFFFF0000000ULL; | |
627 | break; | |
628 | case 0x0000000002000000ULL: | |
629 | /* 1 TB segment */ | |
630 | mask = 0xFFFF000000000000ULL; | |
631 | break; | |
632 | case 0x0000000004000000ULL: | |
633 | case 0x0000000006000000ULL: | |
634 | /* Reserved => segment is invalid */ | |
635 | continue; | |
636 | } | |
637 | if ((eaddr & mask) == (tmp64 & mask)) { | |
638 | /* SLB match */ | |
639 | tmp = ldl_phys(sr_base + 8); | |
640 | *vsid = ((tmp64 << 24) | (tmp >> 8)) & 0x0003FFFFFFFFFFFFULL; | |
641 | *page_mask = ~mask; | |
642 | *attr = tmp & 0xFF; | |
643 | ret = 0; | |
644 | break; | |
645 | } | |
646 | } | |
647 | sr_base += 12; | |
648 | } | |
649 | ||
650 | return ret; | |
79aceca5 | 651 | } |
caa4039c | 652 | #endif /* defined(TARGET_PPC64) */ |
79aceca5 | 653 | |
9a64fbe4 | 654 | /* Perform segment based translation */ |
76a66253 JM |
655 | static int get_segment (CPUState *env, mmu_ctx_t *ctx, |
656 | target_ulong eaddr, int rw, int type) | |
79aceca5 | 657 | { |
caa4039c JM |
658 | target_phys_addr_t sdr, hash, mask, sdr_mask; |
659 | target_ulong sr, vsid, vsid_mask, pgidx, page_mask; | |
660 | #if defined(TARGET_PPC64) | |
661 | int attr; | |
9a64fbe4 | 662 | #endif |
caa4039c JM |
663 | int ds, nx, vsid_sh, sdr_sh; |
664 | int ret, ret2; | |
665 | ||
666 | #if defined(TARGET_PPC64) | |
7dbe11ac JM |
667 | if (env->mmu_model == POWERPC_MMU_64B || |
668 | env->mmu_model == POWERPC_MMU_64BRIDGE) { | |
caa4039c JM |
669 | ret = slb_lookup(env, eaddr, &vsid, &page_mask, &attr); |
670 | if (ret < 0) | |
671 | return ret; | |
672 | ctx->key = ((attr & 0x40) && msr_pr == 1) || | |
673 | ((attr & 0x80) && msr_pr == 0) ? 1 : 0; | |
674 | ds = 0; | |
675 | nx = attr & 0x20 ? 1 : 0; | |
676 | vsid_mask = 0x00003FFFFFFFFF80ULL; | |
677 | vsid_sh = 7; | |
678 | sdr_sh = 18; | |
679 | sdr_mask = 0x3FF80; | |
680 | } else | |
681 | #endif /* defined(TARGET_PPC64) */ | |
682 | { | |
683 | sr = env->sr[eaddr >> 28]; | |
684 | page_mask = 0x0FFFFFFF; | |
685 | ctx->key = (((sr & 0x20000000) && msr_pr == 1) || | |
686 | ((sr & 0x40000000) && msr_pr == 0)) ? 1 : 0; | |
687 | ds = sr & 0x80000000 ? 1 : 0; | |
688 | nx = sr & 0x10000000 ? 1 : 0; | |
689 | vsid = sr & 0x00FFFFFF; | |
690 | vsid_mask = 0x01FFFFC0; | |
691 | vsid_sh = 6; | |
692 | sdr_sh = 16; | |
693 | sdr_mask = 0xFFC0; | |
9a64fbe4 | 694 | #if defined (DEBUG_MMU) |
caa4039c JM |
695 | if (loglevel != 0) { |
696 | fprintf(logfile, "Check segment v=0x" ADDRX " %d 0x" ADDRX | |
697 | " nip=0x" ADDRX " lr=0x" ADDRX | |
698 | " ir=%d dr=%d pr=%d %d t=%d\n", | |
699 | eaddr, (int)(eaddr >> 28), sr, env->nip, | |
700 | env->lr, msr_ir, msr_dr, msr_pr, rw, type); | |
701 | } | |
702 | if (!ds && loglevel != 0) { | |
1b9eb036 | 703 | fprintf(logfile, "pte segment: key=%d n=0x" ADDRX "\n", |
76a66253 | 704 | ctx->key, sr & 0x10000000); |
caa4039c | 705 | } |
9a64fbe4 | 706 | #endif |
caa4039c JM |
707 | } |
708 | ret = -1; | |
709 | if (!ds) { | |
9a64fbe4 | 710 | /* Check if instruction fetch is allowed, if needed */ |
caa4039c | 711 | if (type != ACCESS_CODE || nx == 0) { |
9a64fbe4 | 712 | /* Page address translation */ |
caa4039c JM |
713 | pgidx = (eaddr & page_mask) >> TARGET_PAGE_BITS; |
714 | hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask; | |
76a66253 JM |
715 | /* Primary table address */ |
716 | sdr = env->sdr1; | |
caa4039c JM |
717 | mask = ((sdr & 0x000001FF) << sdr_sh) | sdr_mask; |
718 | ctx->pg_addr[0] = get_pgaddr(sdr, sdr_sh, hash, mask); | |
76a66253 | 719 | /* Secondary table address */ |
caa4039c JM |
720 | hash = (~hash) & vsid_mask; |
721 | ctx->pg_addr[1] = get_pgaddr(sdr, sdr_sh, hash, mask); | |
722 | #if defined(TARGET_PPC64) | |
a750fc0b JM |
723 | if (env->mmu_model == POWERPC_MMU_64B || |
724 | env->mmu_model == POWERPC_MMU_64BRIDGE) { | |
caa4039c JM |
725 | /* Only 5 bits of the page index are used in the AVPN */ |
726 | ctx->ptem = (vsid << 12) | ((pgidx >> 4) & 0x0F80); | |
727 | } else | |
728 | #endif | |
729 | { | |
730 | ctx->ptem = (vsid << 7) | (pgidx >> 10); | |
731 | } | |
76a66253 JM |
732 | /* Initialize real address with an invalid value */ |
733 | ctx->raddr = (target_ulong)-1; | |
7dbe11ac JM |
734 | if (unlikely(env->mmu_model == POWERPC_MMU_SOFT_6xx || |
735 | env->mmu_model == POWERPC_MMU_SOFT_74xx)) { | |
76a66253 JM |
736 | /* Software TLB search */ |
737 | ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type); | |
76a66253 | 738 | } else { |
9a64fbe4 | 739 | #if defined (DEBUG_MMU) |
4a057712 JM |
740 | if (loglevel != 0) { |
741 | fprintf(logfile, "0 sdr1=0x" PADDRX " vsid=0x%06x " | |
742 | "api=0x%04x hash=0x%07x pg_addr=0x" PADDRX "\n", | |
743 | sdr, (uint32_t)vsid, (uint32_t)pgidx, | |
744 | (uint32_t)hash, ctx->pg_addr[0]); | |
76a66253 | 745 | } |
9a64fbe4 | 746 | #endif |
76a66253 | 747 | /* Primary table lookup */ |
caa4039c | 748 | ret = find_pte(env, ctx, 0, rw); |
76a66253 JM |
749 | if (ret < 0) { |
750 | /* Secondary table lookup */ | |
9a64fbe4 | 751 | #if defined (DEBUG_MMU) |
4a057712 | 752 | if (eaddr != 0xEFFFFFFF && loglevel != 0) { |
76a66253 | 753 | fprintf(logfile, |
4a057712 JM |
754 | "1 sdr1=0x" PADDRX " vsid=0x%06x api=0x%04x " |
755 | "hash=0x%05x pg_addr=0x" PADDRX "\n", | |
756 | sdr, (uint32_t)vsid, (uint32_t)pgidx, | |
757 | (uint32_t)hash, ctx->pg_addr[1]); | |
76a66253 | 758 | } |
9a64fbe4 | 759 | #endif |
caa4039c | 760 | ret2 = find_pte(env, ctx, 1, rw); |
76a66253 JM |
761 | if (ret2 != -1) |
762 | ret = ret2; | |
763 | } | |
9a64fbe4 | 764 | } |
9a64fbe4 FB |
765 | } else { |
766 | #if defined (DEBUG_MMU) | |
4a057712 | 767 | if (loglevel != 0) |
76a66253 | 768 | fprintf(logfile, "No access allowed\n"); |
9a64fbe4 | 769 | #endif |
76a66253 | 770 | ret = -3; |
9a64fbe4 FB |
771 | } |
772 | } else { | |
773 | #if defined (DEBUG_MMU) | |
4a057712 | 774 | if (loglevel != 0) |
76a66253 | 775 | fprintf(logfile, "direct store...\n"); |
9a64fbe4 FB |
776 | #endif |
777 | /* Direct-store segment : absolutely *BUGGY* for now */ | |
778 | switch (type) { | |
779 | case ACCESS_INT: | |
780 | /* Integer load/store : only access allowed */ | |
781 | break; | |
782 | case ACCESS_CODE: | |
783 | /* No code fetch is allowed in direct-store areas */ | |
784 | return -4; | |
785 | case ACCESS_FLOAT: | |
786 | /* Floating point load/store */ | |
787 | return -4; | |
788 | case ACCESS_RES: | |
789 | /* lwarx, ldarx or srwcx. */ | |
790 | return -4; | |
791 | case ACCESS_CACHE: | |
792 | /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */ | |
793 | /* Should make the instruction do no-op. | |
794 | * As it already do no-op, it's quite easy :-) | |
795 | */ | |
76a66253 | 796 | ctx->raddr = eaddr; |
9a64fbe4 FB |
797 | return 0; |
798 | case ACCESS_EXT: | |
799 | /* eciwx or ecowx */ | |
800 | return -4; | |
801 | default: | |
802 | if (logfile) { | |
803 | fprintf(logfile, "ERROR: instruction should not need " | |
804 | "address translation\n"); | |
805 | } | |
9a64fbe4 FB |
806 | return -4; |
807 | } | |
76a66253 JM |
808 | if ((rw == 1 || ctx->key != 1) && (rw == 0 || ctx->key != 0)) { |
809 | ctx->raddr = eaddr; | |
9a64fbe4 FB |
810 | ret = 2; |
811 | } else { | |
812 | ret = -2; | |
813 | } | |
79aceca5 | 814 | } |
9a64fbe4 FB |
815 | |
816 | return ret; | |
79aceca5 FB |
817 | } |
818 | ||
c294fc58 JM |
819 | /* Generic TLB check function for embedded PowerPC implementations */ |
820 | static int ppcemb_tlb_check (CPUState *env, ppcemb_tlb_t *tlb, | |
821 | target_phys_addr_t *raddrp, | |
36081602 JM |
822 | target_ulong address, |
823 | uint32_t pid, int ext, int i) | |
c294fc58 JM |
824 | { |
825 | target_ulong mask; | |
826 | ||
827 | /* Check valid flag */ | |
828 | if (!(tlb->prot & PAGE_VALID)) { | |
829 | if (loglevel != 0) | |
830 | fprintf(logfile, "%s: TLB %d not valid\n", __func__, i); | |
831 | return -1; | |
832 | } | |
833 | mask = ~(tlb->size - 1); | |
daf4f96e | 834 | #if defined (DEBUG_SOFTWARE_TLB) |
c294fc58 JM |
835 | if (loglevel != 0) { |
836 | fprintf(logfile, "%s: TLB %d address " ADDRX " PID %d <=> " | |
837 | ADDRX " " ADDRX " %d\n", | |
36081602 | 838 | __func__, i, address, pid, tlb->EPN, mask, (int)tlb->PID); |
c294fc58 | 839 | } |
daf4f96e | 840 | #endif |
c294fc58 | 841 | /* Check PID */ |
36081602 | 842 | if (tlb->PID != 0 && tlb->PID != pid) |
c294fc58 JM |
843 | return -1; |
844 | /* Check effective address */ | |
845 | if ((address & mask) != tlb->EPN) | |
846 | return -1; | |
847 | *raddrp = (tlb->RPN & mask) | (address & ~mask); | |
9706285b | 848 | #if (TARGET_PHYS_ADDR_BITS >= 36) |
36081602 JM |
849 | if (ext) { |
850 | /* Extend the physical address to 36 bits */ | |
851 | *raddrp |= (target_phys_addr_t)(tlb->RPN & 0xF) << 32; | |
852 | } | |
9706285b | 853 | #endif |
c294fc58 JM |
854 | |
855 | return 0; | |
856 | } | |
857 | ||
858 | /* Generic TLB search function for PowerPC embedded implementations */ | |
36081602 | 859 | int ppcemb_tlb_search (CPUPPCState *env, target_ulong address, uint32_t pid) |
c294fc58 JM |
860 | { |
861 | ppcemb_tlb_t *tlb; | |
862 | target_phys_addr_t raddr; | |
863 | int i, ret; | |
864 | ||
865 | /* Default return value is no match */ | |
866 | ret = -1; | |
a750fc0b | 867 | for (i = 0; i < env->nb_tlb; i++) { |
c294fc58 | 868 | tlb = &env->tlb[i].tlbe; |
36081602 | 869 | if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, 0, i) == 0) { |
c294fc58 JM |
870 | ret = i; |
871 | break; | |
872 | } | |
873 | } | |
874 | ||
875 | return ret; | |
876 | } | |
877 | ||
daf4f96e JM |
878 | /* Helpers specific to PowerPC 40x implementations */ |
879 | static void ppc4xx_tlb_invalidate_all (CPUState *env) | |
a750fc0b JM |
880 | { |
881 | ppcemb_tlb_t *tlb; | |
a750fc0b JM |
882 | int i; |
883 | ||
884 | for (i = 0; i < env->nb_tlb; i++) { | |
885 | tlb = &env->tlb[i].tlbe; | |
daf4f96e | 886 | tlb->prot &= ~PAGE_VALID; |
a750fc0b | 887 | } |
daf4f96e | 888 | tlb_flush(env, 1); |
a750fc0b JM |
889 | } |
890 | ||
daf4f96e JM |
891 | static void ppc4xx_tlb_invalidate_virt (CPUState *env, target_ulong eaddr, |
892 | uint32_t pid) | |
0a032cbe | 893 | { |
daf4f96e | 894 | #if !defined(FLUSH_ALL_TLBS) |
0a032cbe | 895 | ppcemb_tlb_t *tlb; |
daf4f96e JM |
896 | target_phys_addr_t raddr; |
897 | target_ulong page, end; | |
0a032cbe JM |
898 | int i; |
899 | ||
900 | for (i = 0; i < env->nb_tlb; i++) { | |
901 | tlb = &env->tlb[i].tlbe; | |
daf4f96e | 902 | if (ppcemb_tlb_check(env, tlb, &raddr, eaddr, pid, 0, i) == 0) { |
0a032cbe JM |
903 | end = tlb->EPN + tlb->size; |
904 | for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) | |
905 | tlb_flush_page(env, page); | |
0a032cbe | 906 | tlb->prot &= ~PAGE_VALID; |
daf4f96e | 907 | break; |
0a032cbe JM |
908 | } |
909 | } | |
daf4f96e JM |
910 | #else |
911 | ppc4xx_tlb_invalidate_all(env); | |
912 | #endif | |
0a032cbe JM |
913 | } |
914 | ||
36081602 | 915 | int mmu40x_get_physical_address (CPUState *env, mmu_ctx_t *ctx, |
e96efcfc | 916 | target_ulong address, int rw, int access_type) |
a8dea12f JM |
917 | { |
918 | ppcemb_tlb_t *tlb; | |
919 | target_phys_addr_t raddr; | |
a8dea12f | 920 | int i, ret, zsel, zpr; |
3b46e624 | 921 | |
c55e9aef JM |
922 | ret = -1; |
923 | raddr = -1; | |
a8dea12f JM |
924 | for (i = 0; i < env->nb_tlb; i++) { |
925 | tlb = &env->tlb[i].tlbe; | |
36081602 JM |
926 | if (ppcemb_tlb_check(env, tlb, &raddr, address, |
927 | env->spr[SPR_40x_PID], 0, i) < 0) | |
a8dea12f | 928 | continue; |
a8dea12f JM |
929 | zsel = (tlb->attr >> 4) & 0xF; |
930 | zpr = (env->spr[SPR_40x_ZPR] >> (28 - (2 * zsel))) & 0x3; | |
daf4f96e | 931 | #if defined (DEBUG_SOFTWARE_TLB) |
4a057712 | 932 | if (loglevel != 0) { |
a8dea12f JM |
933 | fprintf(logfile, "%s: TLB %d zsel %d zpr %d rw %d attr %08x\n", |
934 | __func__, i, zsel, zpr, rw, tlb->attr); | |
935 | } | |
daf4f96e | 936 | #endif |
a8dea12f JM |
937 | if (access_type == ACCESS_CODE) { |
938 | /* Check execute enable bit */ | |
939 | switch (zpr) { | |
c294fc58 JM |
940 | case 0x2: |
941 | if (msr_pr) | |
942 | goto check_exec_perm; | |
943 | goto exec_granted; | |
a8dea12f JM |
944 | case 0x0: |
945 | if (msr_pr) { | |
a8dea12f | 946 | ctx->prot = 0; |
c55e9aef | 947 | ret = -3; |
a8dea12f JM |
948 | break; |
949 | } | |
950 | /* No break here */ | |
951 | case 0x1: | |
c294fc58 | 952 | check_exec_perm: |
a8dea12f JM |
953 | /* Check from TLB entry */ |
954 | if (!(tlb->prot & PAGE_EXEC)) { | |
955 | ret = -3; | |
956 | } else { | |
c55e9aef | 957 | if (tlb->prot & PAGE_WRITE) { |
a8dea12f | 958 | ctx->prot = PAGE_READ | PAGE_WRITE; |
c55e9aef | 959 | } else { |
a8dea12f | 960 | ctx->prot = PAGE_READ; |
c55e9aef | 961 | } |
a8dea12f JM |
962 | ret = 0; |
963 | } | |
964 | break; | |
965 | case 0x3: | |
c294fc58 | 966 | exec_granted: |
a8dea12f | 967 | /* All accesses granted */ |
a8dea12f | 968 | ctx->prot = PAGE_READ | PAGE_WRITE; |
c55e9aef | 969 | ret = 0; |
a8dea12f JM |
970 | break; |
971 | } | |
972 | } else { | |
973 | switch (zpr) { | |
c294fc58 JM |
974 | case 0x2: |
975 | if (msr_pr) | |
976 | goto check_rw_perm; | |
977 | goto rw_granted; | |
a8dea12f JM |
978 | case 0x0: |
979 | if (msr_pr) { | |
a8dea12f | 980 | ctx->prot = 0; |
c55e9aef | 981 | ret = -2; |
a8dea12f JM |
982 | break; |
983 | } | |
984 | /* No break here */ | |
985 | case 0x1: | |
c294fc58 | 986 | check_rw_perm: |
a8dea12f JM |
987 | /* Check from TLB entry */ |
988 | /* Check write protection bit */ | |
c55e9aef JM |
989 | if (tlb->prot & PAGE_WRITE) { |
990 | ctx->prot = PAGE_READ | PAGE_WRITE; | |
991 | ret = 0; | |
a8dea12f | 992 | } else { |
c55e9aef JM |
993 | ctx->prot = PAGE_READ; |
994 | if (rw) | |
995 | ret = -2; | |
a8dea12f | 996 | else |
c55e9aef | 997 | ret = 0; |
a8dea12f JM |
998 | } |
999 | break; | |
1000 | case 0x3: | |
c294fc58 | 1001 | rw_granted: |
a8dea12f | 1002 | /* All accesses granted */ |
a8dea12f | 1003 | ctx->prot = PAGE_READ | PAGE_WRITE; |
c55e9aef | 1004 | ret = 0; |
a8dea12f JM |
1005 | break; |
1006 | } | |
1007 | } | |
1008 | if (ret >= 0) { | |
1009 | ctx->raddr = raddr; | |
daf4f96e | 1010 | #if defined (DEBUG_SOFTWARE_TLB) |
4a057712 | 1011 | if (loglevel != 0) { |
a8dea12f | 1012 | fprintf(logfile, "%s: access granted " ADDRX " => " REGX |
c55e9aef JM |
1013 | " %d %d\n", __func__, address, ctx->raddr, ctx->prot, |
1014 | ret); | |
a8dea12f | 1015 | } |
daf4f96e | 1016 | #endif |
c55e9aef | 1017 | return 0; |
a8dea12f JM |
1018 | } |
1019 | } | |
daf4f96e | 1020 | #if defined (DEBUG_SOFTWARE_TLB) |
4a057712 | 1021 | if (loglevel != 0) { |
c55e9aef JM |
1022 | fprintf(logfile, "%s: access refused " ADDRX " => " REGX |
1023 | " %d %d\n", __func__, address, raddr, ctx->prot, | |
1024 | ret); | |
1025 | } | |
daf4f96e | 1026 | #endif |
3b46e624 | 1027 | |
a8dea12f JM |
1028 | return ret; |
1029 | } | |
1030 | ||
c294fc58 JM |
1031 | void store_40x_sler (CPUPPCState *env, uint32_t val) |
1032 | { | |
1033 | /* XXX: TO BE FIXED */ | |
1034 | if (val != 0x00000000) { | |
1035 | cpu_abort(env, "Little-endian regions are not supported by now\n"); | |
1036 | } | |
1037 | env->spr[SPR_405_SLER] = val; | |
1038 | } | |
1039 | ||
5eb7995e JM |
1040 | int mmubooke_get_physical_address (CPUState *env, mmu_ctx_t *ctx, |
1041 | target_ulong address, int rw, | |
1042 | int access_type) | |
1043 | { | |
1044 | ppcemb_tlb_t *tlb; | |
1045 | target_phys_addr_t raddr; | |
1046 | int i, prot, ret; | |
1047 | ||
1048 | ret = -1; | |
1049 | raddr = -1; | |
1050 | for (i = 0; i < env->nb_tlb; i++) { | |
1051 | tlb = &env->tlb[i].tlbe; | |
1052 | if (ppcemb_tlb_check(env, tlb, &raddr, address, | |
1053 | env->spr[SPR_BOOKE_PID], 1, i) < 0) | |
1054 | continue; | |
1055 | if (msr_pr) | |
1056 | prot = tlb->prot & 0xF; | |
1057 | else | |
1058 | prot = (tlb->prot >> 4) & 0xF; | |
1059 | /* Check the address space */ | |
1060 | if (access_type == ACCESS_CODE) { | |
1061 | if (msr_is != (tlb->attr & 1)) | |
1062 | continue; | |
1063 | ctx->prot = prot; | |
1064 | if (prot & PAGE_EXEC) { | |
1065 | ret = 0; | |
1066 | break; | |
1067 | } | |
1068 | ret = -3; | |
1069 | } else { | |
1070 | if (msr_ds != (tlb->attr & 1)) | |
1071 | continue; | |
1072 | ctx->prot = prot; | |
1073 | if ((!rw && prot & PAGE_READ) || (rw && (prot & PAGE_WRITE))) { | |
1074 | ret = 0; | |
1075 | break; | |
1076 | } | |
1077 | ret = -2; | |
1078 | } | |
1079 | } | |
1080 | if (ret >= 0) | |
1081 | ctx->raddr = raddr; | |
1082 | ||
1083 | return ret; | |
1084 | } | |
1085 | ||
76a66253 JM |
1086 | static int check_physical (CPUState *env, mmu_ctx_t *ctx, |
1087 | target_ulong eaddr, int rw) | |
1088 | { | |
1089 | int in_plb, ret; | |
3b46e624 | 1090 | |
76a66253 JM |
1091 | ctx->raddr = eaddr; |
1092 | ctx->prot = PAGE_READ; | |
1093 | ret = 0; | |
a750fc0b JM |
1094 | switch (env->mmu_model) { |
1095 | case POWERPC_MMU_32B: | |
1096 | case POWERPC_MMU_SOFT_6xx: | |
7dbe11ac | 1097 | case POWERPC_MMU_SOFT_74xx: |
a750fc0b JM |
1098 | case POWERPC_MMU_601: |
1099 | case POWERPC_MMU_SOFT_4xx: | |
1100 | case POWERPC_MMU_REAL_4xx: | |
7dbe11ac | 1101 | case POWERPC_MMU_BOOKE: |
caa4039c JM |
1102 | ctx->prot |= PAGE_WRITE; |
1103 | break; | |
1104 | #if defined(TARGET_PPC64) | |
a750fc0b JM |
1105 | case POWERPC_MMU_64B: |
1106 | case POWERPC_MMU_64BRIDGE: | |
caa4039c | 1107 | /* Real address are 60 bits long */ |
a750fc0b | 1108 | ctx->raddr &= 0x0FFFFFFFFFFFFFFFULL; |
caa4039c JM |
1109 | ctx->prot |= PAGE_WRITE; |
1110 | break; | |
9706285b | 1111 | #endif |
a750fc0b | 1112 | case POWERPC_MMU_SOFT_4xx_Z: |
caa4039c JM |
1113 | if (unlikely(msr_pe != 0)) { |
1114 | /* 403 family add some particular protections, | |
1115 | * using PBL/PBU registers for accesses with no translation. | |
1116 | */ | |
1117 | in_plb = | |
1118 | /* Check PLB validity */ | |
1119 | (env->pb[0] < env->pb[1] && | |
1120 | /* and address in plb area */ | |
1121 | eaddr >= env->pb[0] && eaddr < env->pb[1]) || | |
1122 | (env->pb[2] < env->pb[3] && | |
1123 | eaddr >= env->pb[2] && eaddr < env->pb[3]) ? 1 : 0; | |
1124 | if (in_plb ^ msr_px) { | |
1125 | /* Access in protected area */ | |
1126 | if (rw == 1) { | |
1127 | /* Access is not allowed */ | |
1128 | ret = -2; | |
1129 | } | |
1130 | } else { | |
1131 | /* Read-write access is allowed */ | |
1132 | ctx->prot |= PAGE_WRITE; | |
76a66253 | 1133 | } |
76a66253 | 1134 | } |
e1833e1f | 1135 | break; |
a750fc0b | 1136 | case POWERPC_MMU_BOOKE_FSL: |
caa4039c JM |
1137 | /* XXX: TODO */ |
1138 | cpu_abort(env, "BookE FSL MMU model not implemented\n"); | |
1139 | break; | |
1140 | default: | |
1141 | cpu_abort(env, "Unknown or invalid MMU model\n"); | |
1142 | return -1; | |
76a66253 JM |
1143 | } |
1144 | ||
1145 | return ret; | |
1146 | } | |
1147 | ||
1148 | int get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr, | |
1149 | int rw, int access_type, int check_BATs) | |
9a64fbe4 FB |
1150 | { |
1151 | int ret; | |
514fb8c1 | 1152 | #if 0 |
4a057712 | 1153 | if (loglevel != 0) { |
9a64fbe4 FB |
1154 | fprintf(logfile, "%s\n", __func__); |
1155 | } | |
d9bce9d9 | 1156 | #endif |
4b3686fa FB |
1157 | if ((access_type == ACCESS_CODE && msr_ir == 0) || |
1158 | (access_type != ACCESS_CODE && msr_dr == 0)) { | |
9a64fbe4 | 1159 | /* No address translation */ |
76a66253 | 1160 | ret = check_physical(env, ctx, eaddr, rw); |
9a64fbe4 | 1161 | } else { |
c55e9aef | 1162 | ret = -1; |
a750fc0b JM |
1163 | switch (env->mmu_model) { |
1164 | case POWERPC_MMU_32B: | |
1165 | case POWERPC_MMU_SOFT_6xx: | |
7dbe11ac | 1166 | case POWERPC_MMU_SOFT_74xx: |
a8dea12f | 1167 | /* Try to find a BAT */ |
a8dea12f JM |
1168 | if (check_BATs) |
1169 | ret = get_bat(env, ctx, eaddr, rw, access_type); | |
c55e9aef JM |
1170 | /* No break here */ |
1171 | #if defined(TARGET_PPC64) | |
a750fc0b JM |
1172 | case POWERPC_MMU_64B: |
1173 | case POWERPC_MMU_64BRIDGE: | |
c55e9aef | 1174 | #endif |
a8dea12f | 1175 | if (ret < 0) { |
c55e9aef | 1176 | /* We didn't match any BAT entry or don't have BATs */ |
a8dea12f JM |
1177 | ret = get_segment(env, ctx, eaddr, rw, access_type); |
1178 | } | |
1179 | break; | |
a750fc0b JM |
1180 | case POWERPC_MMU_SOFT_4xx: |
1181 | case POWERPC_MMU_SOFT_4xx_Z: | |
36081602 | 1182 | ret = mmu40x_get_physical_address(env, ctx, eaddr, |
a8dea12f JM |
1183 | rw, access_type); |
1184 | break; | |
a750fc0b | 1185 | case POWERPC_MMU_601: |
c55e9aef JM |
1186 | /* XXX: TODO */ |
1187 | cpu_abort(env, "601 MMU model not implemented\n"); | |
1188 | return -1; | |
a750fc0b | 1189 | case POWERPC_MMU_BOOKE: |
5eb7995e JM |
1190 | ret = mmubooke_get_physical_address(env, ctx, eaddr, |
1191 | rw, access_type); | |
1192 | break; | |
a750fc0b | 1193 | case POWERPC_MMU_BOOKE_FSL: |
c55e9aef JM |
1194 | /* XXX: TODO */ |
1195 | cpu_abort(env, "BookE FSL MMU model not implemented\n"); | |
1196 | return -1; | |
a750fc0b | 1197 | case POWERPC_MMU_REAL_4xx: |
2662a059 JM |
1198 | cpu_abort(env, "PowerPC 401 does not do any translation\n"); |
1199 | return -1; | |
c55e9aef JM |
1200 | default: |
1201 | cpu_abort(env, "Unknown or invalid MMU model\n"); | |
a8dea12f | 1202 | return -1; |
9a64fbe4 FB |
1203 | } |
1204 | } | |
514fb8c1 | 1205 | #if 0 |
4a057712 JM |
1206 | if (loglevel != 0) { |
1207 | fprintf(logfile, "%s address " ADDRX " => %d " PADDRX "\n", | |
c55e9aef | 1208 | __func__, eaddr, ret, ctx->raddr); |
a541f297 | 1209 | } |
76a66253 | 1210 | #endif |
d9bce9d9 | 1211 | |
9a64fbe4 FB |
1212 | return ret; |
1213 | } | |
1214 | ||
9b3c35e0 | 1215 | target_phys_addr_t cpu_get_phys_page_debug (CPUState *env, target_ulong addr) |
a6b025d3 | 1216 | { |
76a66253 | 1217 | mmu_ctx_t ctx; |
a6b025d3 | 1218 | |
76a66253 | 1219 | if (unlikely(get_physical_address(env, &ctx, addr, 0, ACCESS_INT, 1) != 0)) |
a6b025d3 | 1220 | return -1; |
76a66253 JM |
1221 | |
1222 | return ctx.raddr & TARGET_PAGE_MASK; | |
a6b025d3 | 1223 | } |
9a64fbe4 | 1224 | |
9a64fbe4 | 1225 | /* Perform address translation */ |
e96efcfc | 1226 | int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, |
a541f297 | 1227 | int is_user, int is_softmmu) |
9a64fbe4 | 1228 | { |
76a66253 | 1229 | mmu_ctx_t ctx; |
9a64fbe4 | 1230 | int exception = 0, error_code = 0; |
a541f297 | 1231 | int access_type; |
9a64fbe4 | 1232 | int ret = 0; |
d9bce9d9 | 1233 | |
b769d8fe FB |
1234 | if (rw == 2) { |
1235 | /* code access */ | |
1236 | rw = 0; | |
1237 | access_type = ACCESS_CODE; | |
1238 | } else { | |
1239 | /* data access */ | |
1240 | /* XXX: put correct access by using cpu_restore_state() | |
1241 | correctly */ | |
1242 | access_type = ACCESS_INT; | |
1243 | // access_type = env->access_type; | |
1244 | } | |
76a66253 | 1245 | ret = get_physical_address(env, &ctx, address, rw, access_type, 1); |
9a64fbe4 | 1246 | if (ret == 0) { |
76a66253 JM |
1247 | ret = tlb_set_page(env, address & TARGET_PAGE_MASK, |
1248 | ctx.raddr & TARGET_PAGE_MASK, ctx.prot, | |
1249 | is_user, is_softmmu); | |
9a64fbe4 | 1250 | } else if (ret < 0) { |
9a64fbe4 | 1251 | #if defined (DEBUG_MMU) |
4a057712 | 1252 | if (loglevel != 0) |
76a66253 | 1253 | cpu_dump_state(env, logfile, fprintf, 0); |
9a64fbe4 FB |
1254 | #endif |
1255 | if (access_type == ACCESS_CODE) { | |
e1833e1f | 1256 | exception = POWERPC_EXCP_ISI; |
9a64fbe4 FB |
1257 | switch (ret) { |
1258 | case -1: | |
76a66253 | 1259 | /* No matches in page tables or TLB */ |
a750fc0b JM |
1260 | switch (env->mmu_model) { |
1261 | case POWERPC_MMU_SOFT_6xx: | |
e1833e1f | 1262 | exception = POWERPC_EXCP_IFTLB; |
76a66253 JM |
1263 | env->spr[SPR_IMISS] = address; |
1264 | env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; | |
1265 | error_code = 1 << 18; | |
1266 | goto tlb_miss; | |
7dbe11ac JM |
1267 | case POWERPC_MMU_SOFT_74xx: |
1268 | exception = POWERPC_EXCP_IFTLB; | |
1269 | goto tlb_miss_74xx; | |
a750fc0b JM |
1270 | case POWERPC_MMU_SOFT_4xx: |
1271 | case POWERPC_MMU_SOFT_4xx_Z: | |
e1833e1f | 1272 | exception = POWERPC_EXCP_ITLB; |
a8dea12f JM |
1273 | error_code = 0; |
1274 | env->spr[SPR_40x_DEAR] = address; | |
1275 | env->spr[SPR_40x_ESR] = 0x00000000; | |
c55e9aef | 1276 | break; |
a750fc0b | 1277 | case POWERPC_MMU_32B: |
76a66253 | 1278 | error_code = 0x40000000; |
c55e9aef JM |
1279 | break; |
1280 | #if defined(TARGET_PPC64) | |
a750fc0b | 1281 | case POWERPC_MMU_64B: |
c55e9aef JM |
1282 | /* XXX: TODO */ |
1283 | cpu_abort(env, "MMU model not implemented\n"); | |
1284 | return -1; | |
a750fc0b | 1285 | case POWERPC_MMU_64BRIDGE: |
c55e9aef JM |
1286 | /* XXX: TODO */ |
1287 | cpu_abort(env, "MMU model not implemented\n"); | |
1288 | return -1; | |
1289 | #endif | |
a750fc0b | 1290 | case POWERPC_MMU_601: |
c55e9aef JM |
1291 | /* XXX: TODO */ |
1292 | cpu_abort(env, "MMU model not implemented\n"); | |
1293 | return -1; | |
a750fc0b | 1294 | case POWERPC_MMU_BOOKE: |
c55e9aef JM |
1295 | /* XXX: TODO */ |
1296 | cpu_abort(env, "MMU model not implemented\n"); | |
1297 | return -1; | |
a750fc0b | 1298 | case POWERPC_MMU_BOOKE_FSL: |
c55e9aef JM |
1299 | /* XXX: TODO */ |
1300 | cpu_abort(env, "MMU model not implemented\n"); | |
1301 | return -1; | |
a750fc0b | 1302 | case POWERPC_MMU_REAL_4xx: |
2662a059 JM |
1303 | cpu_abort(env, "PowerPC 401 should never raise any MMU " |
1304 | "exceptions\n"); | |
1305 | return -1; | |
c55e9aef JM |
1306 | default: |
1307 | cpu_abort(env, "Unknown or invalid MMU model\n"); | |
1308 | return -1; | |
76a66253 | 1309 | } |
9a64fbe4 FB |
1310 | break; |
1311 | case -2: | |
1312 | /* Access rights violation */ | |
2be0071f | 1313 | error_code = 0x08000000; |
9a64fbe4 FB |
1314 | break; |
1315 | case -3: | |
76a66253 | 1316 | /* No execute protection violation */ |
2be0071f | 1317 | error_code = 0x10000000; |
9a64fbe4 FB |
1318 | break; |
1319 | case -4: | |
1320 | /* Direct store exception */ | |
1321 | /* No code fetch is allowed in direct-store areas */ | |
2be0071f FB |
1322 | error_code = 0x10000000; |
1323 | break; | |
e1833e1f | 1324 | #if defined(TARGET_PPC64) |
2be0071f FB |
1325 | case -5: |
1326 | /* No match in segment table */ | |
e1833e1f | 1327 | exception = POWERPC_EXCP_ISEG; |
2be0071f | 1328 | error_code = 0; |
9a64fbe4 | 1329 | break; |
e1833e1f | 1330 | #endif |
9a64fbe4 FB |
1331 | } |
1332 | } else { | |
e1833e1f | 1333 | exception = POWERPC_EXCP_DSI; |
9a64fbe4 FB |
1334 | switch (ret) { |
1335 | case -1: | |
76a66253 | 1336 | /* No matches in page tables or TLB */ |
a750fc0b JM |
1337 | switch (env->mmu_model) { |
1338 | case POWERPC_MMU_SOFT_6xx: | |
76a66253 | 1339 | if (rw == 1) { |
e1833e1f | 1340 | exception = POWERPC_EXCP_DSTLB; |
76a66253 JM |
1341 | error_code = 1 << 16; |
1342 | } else { | |
e1833e1f | 1343 | exception = POWERPC_EXCP_DLTLB; |
76a66253 JM |
1344 | error_code = 0; |
1345 | } | |
1346 | env->spr[SPR_DMISS] = address; | |
1347 | env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; | |
1348 | tlb_miss: | |
1349 | error_code |= ctx.key << 19; | |
1350 | env->spr[SPR_HASH1] = ctx.pg_addr[0]; | |
1351 | env->spr[SPR_HASH2] = ctx.pg_addr[1]; | |
1352 | /* Do not alter DAR nor DSISR */ | |
1353 | goto out; | |
7dbe11ac JM |
1354 | case POWERPC_MMU_SOFT_74xx: |
1355 | if (rw == 1) { | |
1356 | exception = POWERPC_EXCP_DSTLB; | |
1357 | } else { | |
1358 | exception = POWERPC_EXCP_DLTLB; | |
1359 | } | |
1360 | tlb_miss_74xx: | |
1361 | /* Implement LRU algorithm */ | |
1362 | env->spr[SPR_TLBMISS] = (address & ~((target_ulong)0x3)) | | |
1363 | ((env->last_way + 1) & (env->nb_ways - 1)); | |
1364 | env->spr[SPR_PTEHI] = 0x80000000 | ctx.ptem; | |
1365 | error_code = ctx.key << 19; | |
1366 | break; | |
a750fc0b JM |
1367 | case POWERPC_MMU_SOFT_4xx: |
1368 | case POWERPC_MMU_SOFT_4xx_Z: | |
e1833e1f | 1369 | exception = POWERPC_EXCP_DTLB; |
a8dea12f JM |
1370 | error_code = 0; |
1371 | env->spr[SPR_40x_DEAR] = address; | |
1372 | if (rw) | |
1373 | env->spr[SPR_40x_ESR] = 0x00800000; | |
1374 | else | |
1375 | env->spr[SPR_40x_ESR] = 0x00000000; | |
c55e9aef | 1376 | break; |
a750fc0b | 1377 | case POWERPC_MMU_32B: |
76a66253 | 1378 | error_code = 0x40000000; |
c55e9aef JM |
1379 | break; |
1380 | #if defined(TARGET_PPC64) | |
a750fc0b | 1381 | case POWERPC_MMU_64B: |
c55e9aef JM |
1382 | /* XXX: TODO */ |
1383 | cpu_abort(env, "MMU model not implemented\n"); | |
1384 | return -1; | |
a750fc0b | 1385 | case POWERPC_MMU_64BRIDGE: |
c55e9aef JM |
1386 | /* XXX: TODO */ |
1387 | cpu_abort(env, "MMU model not implemented\n"); | |
1388 | return -1; | |
1389 | #endif | |
a750fc0b | 1390 | case POWERPC_MMU_601: |
c55e9aef JM |
1391 | /* XXX: TODO */ |
1392 | cpu_abort(env, "MMU model not implemented\n"); | |
1393 | return -1; | |
a750fc0b | 1394 | case POWERPC_MMU_BOOKE: |
c55e9aef JM |
1395 | /* XXX: TODO */ |
1396 | cpu_abort(env, "MMU model not implemented\n"); | |
1397 | return -1; | |
a750fc0b | 1398 | case POWERPC_MMU_BOOKE_FSL: |
c55e9aef JM |
1399 | /* XXX: TODO */ |
1400 | cpu_abort(env, "MMU model not implemented\n"); | |
1401 | return -1; | |
a750fc0b | 1402 | case POWERPC_MMU_REAL_4xx: |
2662a059 JM |
1403 | cpu_abort(env, "PowerPC 401 should never raise any MMU " |
1404 | "exceptions\n"); | |
1405 | return -1; | |
c55e9aef JM |
1406 | default: |
1407 | cpu_abort(env, "Unknown or invalid MMU model\n"); | |
1408 | return -1; | |
76a66253 | 1409 | } |
9a64fbe4 FB |
1410 | break; |
1411 | case -2: | |
1412 | /* Access rights violation */ | |
2be0071f | 1413 | error_code = 0x08000000; |
9a64fbe4 FB |
1414 | break; |
1415 | case -4: | |
1416 | /* Direct store exception */ | |
1417 | switch (access_type) { | |
1418 | case ACCESS_FLOAT: | |
1419 | /* Floating point load/store */ | |
e1833e1f JM |
1420 | exception = POWERPC_EXCP_ALIGN; |
1421 | error_code = POWERPC_EXCP_ALIGN_FP; | |
9a64fbe4 FB |
1422 | break; |
1423 | case ACCESS_RES: | |
1424 | /* lwarx, ldarx or srwcx. */ | |
2be0071f | 1425 | error_code = 0x04000000; |
9a64fbe4 FB |
1426 | break; |
1427 | case ACCESS_EXT: | |
1428 | /* eciwx or ecowx */ | |
2be0071f | 1429 | error_code = 0x04100000; |
9a64fbe4 FB |
1430 | break; |
1431 | default: | |
76a66253 | 1432 | printf("DSI: invalid exception (%d)\n", ret); |
e1833e1f JM |
1433 | exception = POWERPC_EXCP_PROGRAM; |
1434 | error_code = POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; | |
9a64fbe4 FB |
1435 | break; |
1436 | } | |
fdabc366 | 1437 | break; |
e1833e1f | 1438 | #if defined(TARGET_PPC64) |
2be0071f FB |
1439 | case -5: |
1440 | /* No match in segment table */ | |
e1833e1f | 1441 | exception = POWERPC_EXCP_DSEG; |
2be0071f FB |
1442 | error_code = 0; |
1443 | break; | |
e1833e1f | 1444 | #endif |
9a64fbe4 | 1445 | } |
e1833e1f | 1446 | if (exception == POWERPC_EXCP_DSI && rw == 1) |
2be0071f | 1447 | error_code |= 0x02000000; |
76a66253 JM |
1448 | /* Store fault address */ |
1449 | env->spr[SPR_DAR] = address; | |
2be0071f | 1450 | env->spr[SPR_DSISR] = error_code; |
9a64fbe4 | 1451 | } |
76a66253 | 1452 | out: |
9a64fbe4 FB |
1453 | #if 0 |
1454 | printf("%s: set exception to %d %02x\n", | |
1455 | __func__, exception, error_code); | |
1456 | #endif | |
1457 | env->exception_index = exception; | |
1458 | env->error_code = error_code; | |
9a64fbe4 FB |
1459 | ret = 1; |
1460 | } | |
76a66253 | 1461 | |
9a64fbe4 FB |
1462 | return ret; |
1463 | } | |
1464 | ||
3fc6c082 FB |
1465 | /*****************************************************************************/ |
1466 | /* BATs management */ | |
1467 | #if !defined(FLUSH_ALL_TLBS) | |
1468 | static inline void do_invalidate_BAT (CPUPPCState *env, | |
1469 | target_ulong BATu, target_ulong mask) | |
1470 | { | |
1471 | target_ulong base, end, page; | |
76a66253 | 1472 | |
3fc6c082 FB |
1473 | base = BATu & ~0x0001FFFF; |
1474 | end = base + mask + 0x00020000; | |
1475 | #if defined (DEBUG_BATS) | |
76a66253 | 1476 | if (loglevel != 0) { |
1b9eb036 | 1477 | fprintf(logfile, "Flush BAT from " ADDRX " to " ADDRX " (" ADDRX ")\n", |
76a66253 JM |
1478 | base, end, mask); |
1479 | } | |
3fc6c082 FB |
1480 | #endif |
1481 | for (page = base; page != end; page += TARGET_PAGE_SIZE) | |
1482 | tlb_flush_page(env, page); | |
1483 | #if defined (DEBUG_BATS) | |
1484 | if (loglevel != 0) | |
1485 | fprintf(logfile, "Flush done\n"); | |
1486 | #endif | |
1487 | } | |
1488 | #endif | |
1489 | ||
1490 | static inline void dump_store_bat (CPUPPCState *env, char ID, int ul, int nr, | |
1491 | target_ulong value) | |
1492 | { | |
1493 | #if defined (DEBUG_BATS) | |
1494 | if (loglevel != 0) { | |
1b9eb036 JM |
1495 | fprintf(logfile, "Set %cBAT%d%c to 0x" ADDRX " (0x" ADDRX ")\n", |
1496 | ID, nr, ul == 0 ? 'u' : 'l', value, env->nip); | |
3fc6c082 FB |
1497 | } |
1498 | #endif | |
1499 | } | |
1500 | ||
1501 | target_ulong do_load_ibatu (CPUPPCState *env, int nr) | |
1502 | { | |
1503 | return env->IBAT[0][nr]; | |
1504 | } | |
1505 | ||
1506 | target_ulong do_load_ibatl (CPUPPCState *env, int nr) | |
1507 | { | |
1508 | return env->IBAT[1][nr]; | |
1509 | } | |
1510 | ||
1511 | void do_store_ibatu (CPUPPCState *env, int nr, target_ulong value) | |
1512 | { | |
1513 | target_ulong mask; | |
1514 | ||
1515 | dump_store_bat(env, 'I', 0, nr, value); | |
1516 | if (env->IBAT[0][nr] != value) { | |
1517 | mask = (value << 15) & 0x0FFE0000UL; | |
1518 | #if !defined(FLUSH_ALL_TLBS) | |
1519 | do_invalidate_BAT(env, env->IBAT[0][nr], mask); | |
1520 | #endif | |
1521 | /* When storing valid upper BAT, mask BEPI and BRPN | |
1522 | * and invalidate all TLBs covered by this BAT | |
1523 | */ | |
1524 | mask = (value << 15) & 0x0FFE0000UL; | |
1525 | env->IBAT[0][nr] = (value & 0x00001FFFUL) | | |
1526 | (value & ~0x0001FFFFUL & ~mask); | |
1527 | env->IBAT[1][nr] = (env->IBAT[1][nr] & 0x0000007B) | | |
1528 | (env->IBAT[1][nr] & ~0x0001FFFF & ~mask); | |
1529 | #if !defined(FLUSH_ALL_TLBS) | |
1530 | do_invalidate_BAT(env, env->IBAT[0][nr], mask); | |
76a66253 | 1531 | #else |
3fc6c082 FB |
1532 | tlb_flush(env, 1); |
1533 | #endif | |
1534 | } | |
1535 | } | |
1536 | ||
1537 | void do_store_ibatl (CPUPPCState *env, int nr, target_ulong value) | |
1538 | { | |
1539 | dump_store_bat(env, 'I', 1, nr, value); | |
1540 | env->IBAT[1][nr] = value; | |
1541 | } | |
1542 | ||
1543 | target_ulong do_load_dbatu (CPUPPCState *env, int nr) | |
1544 | { | |
1545 | return env->DBAT[0][nr]; | |
1546 | } | |
1547 | ||
1548 | target_ulong do_load_dbatl (CPUPPCState *env, int nr) | |
1549 | { | |
1550 | return env->DBAT[1][nr]; | |
1551 | } | |
1552 | ||
1553 | void do_store_dbatu (CPUPPCState *env, int nr, target_ulong value) | |
1554 | { | |
1555 | target_ulong mask; | |
1556 | ||
1557 | dump_store_bat(env, 'D', 0, nr, value); | |
1558 | if (env->DBAT[0][nr] != value) { | |
1559 | /* When storing valid upper BAT, mask BEPI and BRPN | |
1560 | * and invalidate all TLBs covered by this BAT | |
1561 | */ | |
1562 | mask = (value << 15) & 0x0FFE0000UL; | |
1563 | #if !defined(FLUSH_ALL_TLBS) | |
1564 | do_invalidate_BAT(env, env->DBAT[0][nr], mask); | |
1565 | #endif | |
1566 | mask = (value << 15) & 0x0FFE0000UL; | |
1567 | env->DBAT[0][nr] = (value & 0x00001FFFUL) | | |
1568 | (value & ~0x0001FFFFUL & ~mask); | |
1569 | env->DBAT[1][nr] = (env->DBAT[1][nr] & 0x0000007B) | | |
1570 | (env->DBAT[1][nr] & ~0x0001FFFF & ~mask); | |
1571 | #if !defined(FLUSH_ALL_TLBS) | |
1572 | do_invalidate_BAT(env, env->DBAT[0][nr], mask); | |
1573 | #else | |
1574 | tlb_flush(env, 1); | |
1575 | #endif | |
1576 | } | |
1577 | } | |
1578 | ||
1579 | void do_store_dbatl (CPUPPCState *env, int nr, target_ulong value) | |
1580 | { | |
1581 | dump_store_bat(env, 'D', 1, nr, value); | |
1582 | env->DBAT[1][nr] = value; | |
1583 | } | |
1584 | ||
0a032cbe JM |
1585 | |
1586 | /*****************************************************************************/ | |
1587 | /* TLB management */ | |
1588 | void ppc_tlb_invalidate_all (CPUPPCState *env) | |
1589 | { | |
daf4f96e JM |
1590 | switch (env->mmu_model) { |
1591 | case POWERPC_MMU_SOFT_6xx: | |
7dbe11ac | 1592 | case POWERPC_MMU_SOFT_74xx: |
0a032cbe | 1593 | ppc6xx_tlb_invalidate_all(env); |
daf4f96e JM |
1594 | break; |
1595 | case POWERPC_MMU_SOFT_4xx: | |
1596 | case POWERPC_MMU_SOFT_4xx_Z: | |
0a032cbe | 1597 | ppc4xx_tlb_invalidate_all(env); |
daf4f96e | 1598 | break; |
7dbe11ac JM |
1599 | case POWERPC_MMU_REAL_4xx: |
1600 | cpu_abort(env, "No TLB for PowerPC 4xx in real mode\n"); | |
1601 | break; | |
1602 | case POWERPC_MMU_BOOKE: | |
1603 | /* XXX: TODO */ | |
1604 | cpu_abort(env, "MMU model not implemented\n"); | |
1605 | break; | |
1606 | case POWERPC_MMU_BOOKE_FSL: | |
1607 | /* XXX: TODO */ | |
1608 | cpu_abort(env, "MMU model not implemented\n"); | |
1609 | break; | |
1610 | case POWERPC_MMU_601: | |
1611 | /* XXX: TODO */ | |
1612 | cpu_abort(env, "MMU model not implemented\n"); | |
1613 | break; | |
1614 | case POWERPC_MMU_32B: | |
00af685f | 1615 | #if defined(TARGET_PPC64) |
7dbe11ac JM |
1616 | case POWERPC_MMU_64B: |
1617 | case POWERPC_MMU_64BRIDGE: | |
00af685f | 1618 | #endif /* defined(TARGET_PPC64) */ |
0a032cbe | 1619 | tlb_flush(env, 1); |
daf4f96e | 1620 | break; |
00af685f JM |
1621 | default: |
1622 | /* XXX: TODO */ | |
1623 | cpu_abort(env, "Unknown MMU model %d\n", env->mmu_model); | |
1624 | break; | |
0a032cbe JM |
1625 | } |
1626 | } | |
1627 | ||
daf4f96e JM |
1628 | void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr) |
1629 | { | |
1630 | #if !defined(FLUSH_ALL_TLBS) | |
1631 | addr &= TARGET_PAGE_MASK; | |
1632 | switch (env->mmu_model) { | |
1633 | case POWERPC_MMU_SOFT_6xx: | |
7dbe11ac | 1634 | case POWERPC_MMU_SOFT_74xx: |
daf4f96e JM |
1635 | ppc6xx_tlb_invalidate_virt(env, addr, 0); |
1636 | if (env->id_tlbs == 1) | |
1637 | ppc6xx_tlb_invalidate_virt(env, addr, 1); | |
1638 | break; | |
1639 | case POWERPC_MMU_SOFT_4xx: | |
1640 | case POWERPC_MMU_SOFT_4xx_Z: | |
1641 | ppc4xx_tlb_invalidate_virt(env, addr, env->spr[SPR_40x_PID]); | |
1642 | break; | |
7dbe11ac JM |
1643 | case POWERPC_MMU_REAL_4xx: |
1644 | cpu_abort(env, "No TLB for PowerPC 4xx in real mode\n"); | |
1645 | break; | |
1646 | case POWERPC_MMU_BOOKE: | |
1647 | /* XXX: TODO */ | |
1648 | cpu_abort(env, "MMU model not implemented\n"); | |
1649 | break; | |
1650 | case POWERPC_MMU_BOOKE_FSL: | |
1651 | /* XXX: TODO */ | |
1652 | cpu_abort(env, "MMU model not implemented\n"); | |
1653 | break; | |
1654 | case POWERPC_MMU_601: | |
1655 | /* XXX: TODO */ | |
1656 | cpu_abort(env, "MMU model not implemented\n"); | |
1657 | break; | |
1658 | case POWERPC_MMU_32B: | |
daf4f96e JM |
1659 | /* tlbie invalidate TLBs for all segments */ |
1660 | addr &= ~((target_ulong)-1 << 28); | |
1661 | /* XXX: this case should be optimized, | |
1662 | * giving a mask to tlb_flush_page | |
1663 | */ | |
1664 | tlb_flush_page(env, addr | (0x0 << 28)); | |
1665 | tlb_flush_page(env, addr | (0x1 << 28)); | |
1666 | tlb_flush_page(env, addr | (0x2 << 28)); | |
1667 | tlb_flush_page(env, addr | (0x3 << 28)); | |
1668 | tlb_flush_page(env, addr | (0x4 << 28)); | |
1669 | tlb_flush_page(env, addr | (0x5 << 28)); | |
1670 | tlb_flush_page(env, addr | (0x6 << 28)); | |
1671 | tlb_flush_page(env, addr | (0x7 << 28)); | |
1672 | tlb_flush_page(env, addr | (0x8 << 28)); | |
1673 | tlb_flush_page(env, addr | (0x9 << 28)); | |
1674 | tlb_flush_page(env, addr | (0xA << 28)); | |
1675 | tlb_flush_page(env, addr | (0xB << 28)); | |
1676 | tlb_flush_page(env, addr | (0xC << 28)); | |
1677 | tlb_flush_page(env, addr | (0xD << 28)); | |
1678 | tlb_flush_page(env, addr | (0xE << 28)); | |
1679 | tlb_flush_page(env, addr | (0xF << 28)); | |
7dbe11ac | 1680 | break; |
00af685f | 1681 | #if defined(TARGET_PPC64) |
7dbe11ac JM |
1682 | case POWERPC_MMU_64B: |
1683 | case POWERPC_MMU_64BRIDGE: | |
1684 | /* tlbie invalidate TLBs for all segments */ | |
1685 | /* XXX: given the fact that there are too many segments to invalidate, | |
00af685f | 1686 | * and we still don't have a tlb_flush_mask(env, n, mask) in Qemu, |
7dbe11ac JM |
1687 | * we just invalidate all TLBs |
1688 | */ | |
1689 | tlb_flush(env, 1); | |
1690 | break; | |
00af685f JM |
1691 | #endif /* defined(TARGET_PPC64) */ |
1692 | default: | |
1693 | /* XXX: TODO */ | |
1694 | cpu_abort(env, "Unknown MMU model 2\n"); | |
1695 | break; | |
daf4f96e JM |
1696 | } |
1697 | #else | |
1698 | ppc_tlb_invalidate_all(env); | |
1699 | #endif | |
1700 | } | |
1701 | ||
1702 | #if defined(TARGET_PPC64) | |
1703 | void ppc_slb_invalidate_all (CPUPPCState *env) | |
1704 | { | |
1705 | /* XXX: TODO */ | |
1706 | tlb_flush(env, 1); | |
1707 | } | |
1708 | ||
1709 | void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0) | |
1710 | { | |
1711 | /* XXX: TODO */ | |
1712 | tlb_flush(env, 1); | |
1713 | } | |
1714 | #endif | |
1715 | ||
1716 | ||
3fc6c082 FB |
1717 | /*****************************************************************************/ |
1718 | /* Special registers manipulation */ | |
d9bce9d9 JM |
1719 | #if defined(TARGET_PPC64) |
1720 | target_ulong ppc_load_asr (CPUPPCState *env) | |
1721 | { | |
1722 | return env->asr; | |
1723 | } | |
1724 | ||
1725 | void ppc_store_asr (CPUPPCState *env, target_ulong value) | |
1726 | { | |
1727 | if (env->asr != value) { | |
1728 | env->asr = value; | |
1729 | tlb_flush(env, 1); | |
1730 | } | |
1731 | } | |
1732 | #endif | |
1733 | ||
3fc6c082 FB |
1734 | target_ulong do_load_sdr1 (CPUPPCState *env) |
1735 | { | |
1736 | return env->sdr1; | |
1737 | } | |
1738 | ||
1739 | void do_store_sdr1 (CPUPPCState *env, target_ulong value) | |
1740 | { | |
1741 | #if defined (DEBUG_MMU) | |
1742 | if (loglevel != 0) { | |
1b9eb036 | 1743 | fprintf(logfile, "%s: 0x" ADDRX "\n", __func__, value); |
3fc6c082 FB |
1744 | } |
1745 | #endif | |
1746 | if (env->sdr1 != value) { | |
1747 | env->sdr1 = value; | |
76a66253 | 1748 | tlb_flush(env, 1); |
3fc6c082 FB |
1749 | } |
1750 | } | |
1751 | ||
1752 | target_ulong do_load_sr (CPUPPCState *env, int srnum) | |
1753 | { | |
1754 | return env->sr[srnum]; | |
1755 | } | |
1756 | ||
1757 | void do_store_sr (CPUPPCState *env, int srnum, target_ulong value) | |
1758 | { | |
1759 | #if defined (DEBUG_MMU) | |
1760 | if (loglevel != 0) { | |
1b9eb036 JM |
1761 | fprintf(logfile, "%s: reg=%d 0x" ADDRX " " ADDRX "\n", |
1762 | __func__, srnum, value, env->sr[srnum]); | |
3fc6c082 FB |
1763 | } |
1764 | #endif | |
1765 | if (env->sr[srnum] != value) { | |
1766 | env->sr[srnum] = value; | |
1767 | #if !defined(FLUSH_ALL_TLBS) && 0 | |
1768 | { | |
1769 | target_ulong page, end; | |
1770 | /* Invalidate 256 MB of virtual memory */ | |
1771 | page = (16 << 20) * srnum; | |
1772 | end = page + (16 << 20); | |
1773 | for (; page != end; page += TARGET_PAGE_SIZE) | |
1774 | tlb_flush_page(env, page); | |
1775 | } | |
1776 | #else | |
76a66253 | 1777 | tlb_flush(env, 1); |
3fc6c082 FB |
1778 | #endif |
1779 | } | |
1780 | } | |
76a66253 | 1781 | #endif /* !defined (CONFIG_USER_ONLY) */ |
3fc6c082 | 1782 | |
bfa1e5cf | 1783 | target_ulong ppc_load_xer (CPUPPCState *env) |
79aceca5 FB |
1784 | { |
1785 | return (xer_so << XER_SO) | | |
1786 | (xer_ov << XER_OV) | | |
1787 | (xer_ca << XER_CA) | | |
3fc6c082 FB |
1788 | (xer_bc << XER_BC) | |
1789 | (xer_cmp << XER_CMP); | |
79aceca5 FB |
1790 | } |
1791 | ||
bfa1e5cf | 1792 | void ppc_store_xer (CPUPPCState *env, target_ulong value) |
79aceca5 FB |
1793 | { |
1794 | xer_so = (value >> XER_SO) & 0x01; | |
1795 | xer_ov = (value >> XER_OV) & 0x01; | |
1796 | xer_ca = (value >> XER_CA) & 0x01; | |
3fc6c082 | 1797 | xer_cmp = (value >> XER_CMP) & 0xFF; |
d9bce9d9 | 1798 | xer_bc = (value >> XER_BC) & 0x7F; |
79aceca5 FB |
1799 | } |
1800 | ||
76a66253 JM |
1801 | /* Swap temporary saved registers with GPRs */ |
1802 | static inline void swap_gpr_tgpr (CPUPPCState *env) | |
79aceca5 | 1803 | { |
76a66253 JM |
1804 | ppc_gpr_t tmp; |
1805 | ||
1806 | tmp = env->gpr[0]; | |
1807 | env->gpr[0] = env->tgpr[0]; | |
1808 | env->tgpr[0] = tmp; | |
1809 | tmp = env->gpr[1]; | |
1810 | env->gpr[1] = env->tgpr[1]; | |
1811 | env->tgpr[1] = tmp; | |
1812 | tmp = env->gpr[2]; | |
1813 | env->gpr[2] = env->tgpr[2]; | |
1814 | env->tgpr[2] = tmp; | |
1815 | tmp = env->gpr[3]; | |
1816 | env->gpr[3] = env->tgpr[3]; | |
1817 | env->tgpr[3] = tmp; | |
79aceca5 FB |
1818 | } |
1819 | ||
76a66253 JM |
1820 | /* GDBstub can read and write MSR... */ |
1821 | target_ulong do_load_msr (CPUPPCState *env) | |
79aceca5 | 1822 | { |
76a66253 JM |
1823 | return |
1824 | #if defined (TARGET_PPC64) | |
d9bce9d9 JM |
1825 | ((target_ulong)msr_sf << MSR_SF) | |
1826 | ((target_ulong)msr_isf << MSR_ISF) | | |
1827 | ((target_ulong)msr_hv << MSR_HV) | | |
76a66253 | 1828 | #endif |
d9bce9d9 JM |
1829 | ((target_ulong)msr_ucle << MSR_UCLE) | |
1830 | ((target_ulong)msr_vr << MSR_VR) | /* VR / SPE */ | |
1831 | ((target_ulong)msr_ap << MSR_AP) | | |
1832 | ((target_ulong)msr_sa << MSR_SA) | | |
1833 | ((target_ulong)msr_key << MSR_KEY) | | |
1834 | ((target_ulong)msr_pow << MSR_POW) | /* POW / WE */ | |
1835 | ((target_ulong)msr_tlb << MSR_TLB) | /* TLB / TGPE / CE */ | |
1836 | ((target_ulong)msr_ile << MSR_ILE) | | |
1837 | ((target_ulong)msr_ee << MSR_EE) | | |
1838 | ((target_ulong)msr_pr << MSR_PR) | | |
1839 | ((target_ulong)msr_fp << MSR_FP) | | |
1840 | ((target_ulong)msr_me << MSR_ME) | | |
1841 | ((target_ulong)msr_fe0 << MSR_FE0) | | |
1842 | ((target_ulong)msr_se << MSR_SE) | /* SE / DWE / UBLE */ | |
1843 | ((target_ulong)msr_be << MSR_BE) | /* BE / DE */ | |
1844 | ((target_ulong)msr_fe1 << MSR_FE1) | | |
1845 | ((target_ulong)msr_al << MSR_AL) | | |
1846 | ((target_ulong)msr_ip << MSR_IP) | | |
1847 | ((target_ulong)msr_ir << MSR_IR) | /* IR / IS */ | |
1848 | ((target_ulong)msr_dr << MSR_DR) | /* DR / DS */ | |
1849 | ((target_ulong)msr_pe << MSR_PE) | /* PE / EP */ | |
1850 | ((target_ulong)msr_px << MSR_PX) | /* PX / PMM */ | |
1851 | ((target_ulong)msr_ri << MSR_RI) | | |
1852 | ((target_ulong)msr_le << MSR_LE); | |
3fc6c082 FB |
1853 | } |
1854 | ||
a97fed52 | 1855 | int do_store_msr (CPUPPCState *env, target_ulong value) |
313adae9 | 1856 | { |
50443c98 FB |
1857 | int enter_pm; |
1858 | ||
3fc6c082 FB |
1859 | value &= env->msr_mask; |
1860 | if (((value >> MSR_IR) & 1) != msr_ir || | |
1861 | ((value >> MSR_DR) & 1) != msr_dr) { | |
76a66253 | 1862 | /* Flush all tlb when changing translation mode */ |
d094807b | 1863 | tlb_flush(env, 1); |
3fc6c082 | 1864 | env->interrupt_request |= CPU_INTERRUPT_EXITTB; |
a541f297 | 1865 | } |
3fc6c082 FB |
1866 | #if 0 |
1867 | if (loglevel != 0) { | |
1868 | fprintf(logfile, "%s: T0 %08lx\n", __func__, value); | |
1869 | } | |
1870 | #endif | |
a750fc0b JM |
1871 | switch (env->excp_model) { |
1872 | case POWERPC_EXCP_602: | |
1873 | case POWERPC_EXCP_603: | |
1874 | case POWERPC_EXCP_603E: | |
1875 | case POWERPC_EXCP_G2: | |
76a66253 JM |
1876 | if (((value >> MSR_TGPR) & 1) != msr_tgpr) { |
1877 | /* Swap temporary saved registers with GPRs */ | |
1878 | swap_gpr_tgpr(env); | |
1879 | } | |
1880 | break; | |
1881 | default: | |
1882 | break; | |
1883 | } | |
1884 | #if defined (TARGET_PPC64) | |
1885 | msr_sf = (value >> MSR_SF) & 1; | |
1886 | msr_isf = (value >> MSR_ISF) & 1; | |
1887 | msr_hv = (value >> MSR_HV) & 1; | |
1888 | #endif | |
1889 | msr_ucle = (value >> MSR_UCLE) & 1; | |
1890 | msr_vr = (value >> MSR_VR) & 1; /* VR / SPE */ | |
1891 | msr_ap = (value >> MSR_AP) & 1; | |
1892 | msr_sa = (value >> MSR_SA) & 1; | |
1893 | msr_key = (value >> MSR_KEY) & 1; | |
1894 | msr_pow = (value >> MSR_POW) & 1; /* POW / WE */ | |
1895 | msr_tlb = (value >> MSR_TLB) & 1; /* TLB / TGPR / CE */ | |
1896 | msr_ile = (value >> MSR_ILE) & 1; | |
1897 | msr_ee = (value >> MSR_EE) & 1; | |
1898 | msr_pr = (value >> MSR_PR) & 1; | |
1899 | msr_fp = (value >> MSR_FP) & 1; | |
1900 | msr_me = (value >> MSR_ME) & 1; | |
1901 | msr_fe0 = (value >> MSR_FE0) & 1; | |
1902 | msr_se = (value >> MSR_SE) & 1; /* SE / DWE / UBLE */ | |
1903 | msr_be = (value >> MSR_BE) & 1; /* BE / DE */ | |
1904 | msr_fe1 = (value >> MSR_FE1) & 1; | |
1905 | msr_al = (value >> MSR_AL) & 1; | |
1906 | msr_ip = (value >> MSR_IP) & 1; | |
1907 | msr_ir = (value >> MSR_IR) & 1; /* IR / IS */ | |
1908 | msr_dr = (value >> MSR_DR) & 1; /* DR / DS */ | |
1909 | msr_pe = (value >> MSR_PE) & 1; /* PE / EP */ | |
1910 | msr_px = (value >> MSR_PX) & 1; /* PX / PMM */ | |
1911 | msr_ri = (value >> MSR_RI) & 1; | |
1912 | msr_le = (value >> MSR_LE) & 1; | |
3fc6c082 | 1913 | do_compute_hflags(env); |
50443c98 FB |
1914 | |
1915 | enter_pm = 0; | |
a750fc0b JM |
1916 | switch (env->excp_model) { |
1917 | case POWERPC_EXCP_603: | |
1918 | case POWERPC_EXCP_603E: | |
1919 | case POWERPC_EXCP_G2: | |
d9bce9d9 JM |
1920 | /* Don't handle SLEEP mode: we should disable all clocks... |
1921 | * No dynamic power-management. | |
1922 | */ | |
1923 | if (msr_pow == 1 && (env->spr[SPR_HID0] & 0x00C00000) != 0) | |
1924 | enter_pm = 1; | |
1925 | break; | |
a750fc0b | 1926 | case POWERPC_EXCP_604: |
d9bce9d9 JM |
1927 | if (msr_pow == 1) |
1928 | enter_pm = 1; | |
1929 | break; | |
a750fc0b | 1930 | case POWERPC_EXCP_7x0: |
76a66253 | 1931 | if (msr_pow == 1 && (env->spr[SPR_HID0] & 0x00E00000) != 0) |
50443c98 FB |
1932 | enter_pm = 1; |
1933 | break; | |
1934 | default: | |
1935 | break; | |
1936 | } | |
a97fed52 JM |
1937 | |
1938 | return enter_pm; | |
3fc6c082 FB |
1939 | } |
1940 | ||
d9bce9d9 | 1941 | #if defined(TARGET_PPC64) |
a97fed52 | 1942 | int ppc_store_msr_32 (CPUPPCState *env, uint32_t value) |
d9bce9d9 | 1943 | { |
a97fed52 JM |
1944 | return do_store_msr(env, (do_load_msr(env) & ~0xFFFFFFFFULL) | |
1945 | (value & 0xFFFFFFFF)); | |
d9bce9d9 JM |
1946 | } |
1947 | #endif | |
1948 | ||
76a66253 | 1949 | void do_compute_hflags (CPUPPCState *env) |
3fc6c082 | 1950 | { |
76a66253 | 1951 | /* Compute current hflags */ |
4296f459 | 1952 | env->hflags = (msr_vr << MSR_VR) | |
c62db105 JM |
1953 | (msr_ap << MSR_AP) | (msr_sa << MSR_SA) | (msr_pr << MSR_PR) | |
1954 | (msr_fp << MSR_FP) | (msr_fe0 << MSR_FE0) | (msr_se << MSR_SE) | | |
1955 | (msr_be << MSR_BE) | (msr_fe1 << MSR_FE1) | (msr_le << MSR_LE); | |
76a66253 | 1956 | #if defined (TARGET_PPC64) |
4296f459 JM |
1957 | env->hflags |= msr_cm << MSR_CM; |
1958 | env->hflags |= (uint64_t)msr_sf << MSR_SF; | |
1959 | env->hflags |= (uint64_t)msr_hv << MSR_HV; | |
4b3686fa | 1960 | #endif |
3fc6c082 FB |
1961 | } |
1962 | ||
1963 | /*****************************************************************************/ | |
1964 | /* Exception processing */ | |
18fba28c | 1965 | #if defined (CONFIG_USER_ONLY) |
9a64fbe4 | 1966 | void do_interrupt (CPUState *env) |
79aceca5 | 1967 | { |
e1833e1f JM |
1968 | env->exception_index = POWERPC_EXCP_NONE; |
1969 | env->error_code = 0; | |
18fba28c | 1970 | } |
47103572 | 1971 | |
e9df014c | 1972 | void ppc_hw_interrupt (CPUState *env) |
47103572 | 1973 | { |
e1833e1f JM |
1974 | env->exception_index = POWERPC_EXCP_NONE; |
1975 | env->error_code = 0; | |
47103572 | 1976 | } |
76a66253 | 1977 | #else /* defined (CONFIG_USER_ONLY) */ |
36081602 | 1978 | static void dump_syscall (CPUState *env) |
d094807b | 1979 | { |
d9bce9d9 | 1980 | fprintf(logfile, "syscall r0=0x" REGX " r3=0x" REGX " r4=0x" REGX |
1b9eb036 | 1981 | " r5=0x" REGX " r6=0x" REGX " nip=0x" ADDRX "\n", |
d094807b FB |
1982 | env->gpr[0], env->gpr[3], env->gpr[4], |
1983 | env->gpr[5], env->gpr[6], env->nip); | |
1984 | } | |
1985 | ||
e1833e1f JM |
1986 | /* Note that this function should be greatly optimized |
1987 | * when called with a constant excp, from ppc_hw_interrupt | |
1988 | */ | |
1989 | static always_inline void powerpc_excp (CPUState *env, | |
1990 | int excp_model, int excp) | |
18fba28c | 1991 | { |
e1833e1f JM |
1992 | target_ulong msr, vector; |
1993 | int srr0, srr1, asrr0, asrr1; | |
79aceca5 | 1994 | |
b769d8fe | 1995 | if (loglevel & CPU_LOG_INT) { |
1b9eb036 JM |
1996 | fprintf(logfile, "Raise exception at 0x" ADDRX " => 0x%08x (%02x)\n", |
1997 | env->nip, excp, env->error_code); | |
b769d8fe | 1998 | } |
e1833e1f JM |
1999 | msr = do_load_msr(env); |
2000 | srr0 = SPR_SRR0; | |
2001 | srr1 = SPR_SRR1; | |
2002 | asrr0 = -1; | |
2003 | asrr1 = -1; | |
2004 | msr &= ~((target_ulong)0x783F0000); | |
9a64fbe4 | 2005 | switch (excp) { |
e1833e1f JM |
2006 | case POWERPC_EXCP_NONE: |
2007 | /* Should never happen */ | |
2008 | return; | |
2009 | case POWERPC_EXCP_CRITICAL: /* Critical input */ | |
2010 | msr_ri = 0; /* XXX: check this */ | |
2011 | switch (excp_model) { | |
a750fc0b | 2012 | case POWERPC_EXCP_40x: |
e1833e1f JM |
2013 | srr0 = SPR_40x_SRR2; |
2014 | srr1 = SPR_40x_SRR3; | |
c62db105 | 2015 | break; |
a750fc0b | 2016 | case POWERPC_EXCP_BOOKE: |
e1833e1f JM |
2017 | srr0 = SPR_BOOKE_CSRR0; |
2018 | srr1 = SPR_BOOKE_CSRR1; | |
c62db105 | 2019 | break; |
e1833e1f | 2020 | case POWERPC_EXCP_G2: |
c62db105 | 2021 | break; |
e1833e1f JM |
2022 | default: |
2023 | goto excp_invalid; | |
2be0071f | 2024 | } |
9a64fbe4 | 2025 | goto store_next; |
e1833e1f JM |
2026 | case POWERPC_EXCP_MCHECK: /* Machine check exception */ |
2027 | if (msr_me == 0) { | |
2028 | /* Machine check exception is not enabled */ | |
2029 | /* XXX: we may just stop the processor here, to allow debugging */ | |
2030 | excp = POWERPC_EXCP_RESET; | |
2031 | goto excp_reset; | |
2032 | } | |
2033 | msr_ri = 0; | |
2034 | msr_me = 0; | |
2035 | #if defined(TARGET_PPC64H) | |
2036 | msr_hv = 1; | |
2037 | #endif | |
2038 | /* XXX: should also have something loaded in DAR / DSISR */ | |
2039 | switch (excp_model) { | |
a750fc0b | 2040 | case POWERPC_EXCP_40x: |
e1833e1f JM |
2041 | srr0 = SPR_40x_SRR2; |
2042 | srr1 = SPR_40x_SRR3; | |
c62db105 | 2043 | break; |
a750fc0b | 2044 | case POWERPC_EXCP_BOOKE: |
e1833e1f JM |
2045 | srr0 = SPR_BOOKE_MCSRR0; |
2046 | srr1 = SPR_BOOKE_MCSRR1; | |
2047 | asrr0 = SPR_BOOKE_CSRR0; | |
2048 | asrr1 = SPR_BOOKE_CSRR1; | |
c62db105 JM |
2049 | break; |
2050 | default: | |
2051 | break; | |
2be0071f | 2052 | } |
e1833e1f JM |
2053 | goto store_next; |
2054 | case POWERPC_EXCP_DSI: /* Data storage exception */ | |
a541f297 | 2055 | #if defined (DEBUG_EXCEPTIONS) |
4a057712 | 2056 | if (loglevel != 0) { |
1b9eb036 JM |
2057 | fprintf(logfile, "DSI exception: DSISR=0x" ADDRX" DAR=0x" ADDRX |
2058 | "\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]); | |
76a66253 | 2059 | } |
e1833e1f JM |
2060 | #endif |
2061 | msr_ri = 0; | |
2062 | #if defined(TARGET_PPC64H) | |
2063 | if (lpes1 == 0) | |
2064 | msr_hv = 1; | |
a541f297 FB |
2065 | #endif |
2066 | goto store_next; | |
e1833e1f | 2067 | case POWERPC_EXCP_ISI: /* Instruction storage exception */ |
a541f297 | 2068 | #if defined (DEBUG_EXCEPTIONS) |
76a66253 | 2069 | if (loglevel != 0) { |
1b9eb036 JM |
2070 | fprintf(logfile, "ISI exception: msr=0x" ADDRX ", nip=0x" ADDRX |
2071 | "\n", msr, env->nip); | |
76a66253 | 2072 | } |
a541f297 | 2073 | #endif |
e1833e1f JM |
2074 | msr_ri = 0; |
2075 | #if defined(TARGET_PPC64H) | |
2076 | if (lpes1 == 0) | |
2077 | msr_hv = 1; | |
2078 | #endif | |
2079 | msr |= env->error_code; | |
9a64fbe4 | 2080 | goto store_next; |
e1833e1f JM |
2081 | case POWERPC_EXCP_EXTERNAL: /* External input */ |
2082 | msr_ri = 0; | |
2083 | #if defined(TARGET_PPC64H) | |
2084 | if (lpes0 == 1) | |
2085 | msr_hv = 1; | |
2086 | #endif | |
9a64fbe4 | 2087 | goto store_next; |
e1833e1f JM |
2088 | case POWERPC_EXCP_ALIGN: /* Alignment exception */ |
2089 | msr_ri = 0; | |
2090 | #if defined(TARGET_PPC64H) | |
2091 | if (lpes1 == 0) | |
2092 | msr_hv = 1; | |
2093 | #endif | |
2094 | /* XXX: this is false */ | |
2095 | /* Get rS/rD and rA from faulting opcode */ | |
2096 | env->spr[SPR_DSISR] |= (ldl_code((env->nip - 4)) & 0x03FF0000) >> 16; | |
9a64fbe4 | 2097 | goto store_current; |
e1833e1f | 2098 | case POWERPC_EXCP_PROGRAM: /* Program exception */ |
9a64fbe4 | 2099 | switch (env->error_code & ~0xF) { |
e1833e1f JM |
2100 | case POWERPC_EXCP_FP: |
2101 | if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) { | |
9a64fbe4 | 2102 | #if defined (DEBUG_EXCEPTIONS) |
4a057712 | 2103 | if (loglevel != 0) { |
a496775f JM |
2104 | fprintf(logfile, "Ignore floating point exception\n"); |
2105 | } | |
9a64fbe4 FB |
2106 | #endif |
2107 | return; | |
76a66253 | 2108 | } |
e1833e1f JM |
2109 | msr_ri = 0; |
2110 | #if defined(TARGET_PPC64H) | |
2111 | if (lpes1 == 0) | |
2112 | msr_hv = 1; | |
2113 | #endif | |
9a64fbe4 FB |
2114 | msr |= 0x00100000; |
2115 | /* Set FX */ | |
2116 | env->fpscr[7] |= 0x8; | |
2117 | /* Finally, update FEX */ | |
2118 | if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) & | |
2119 | ((env->fpscr[1] << 1) | (env->fpscr[0] >> 3))) | |
2120 | env->fpscr[7] |= 0x4; | |
e1833e1f JM |
2121 | if (msr_fe0 != msr_fe1) { |
2122 | msr |= 0x00010000; | |
2123 | goto store_current; | |
2124 | } | |
76a66253 | 2125 | break; |
e1833e1f | 2126 | case POWERPC_EXCP_INVAL: |
a496775f | 2127 | #if defined (DEBUG_EXCEPTIONS) |
4a057712 | 2128 | if (loglevel != 0) { |
a496775f JM |
2129 | fprintf(logfile, "Invalid instruction at 0x" ADDRX "\n", |
2130 | env->nip); | |
2131 | } | |
e1833e1f JM |
2132 | #endif |
2133 | msr_ri = 0; | |
2134 | #if defined(TARGET_PPC64H) | |
2135 | if (lpes1 == 0) | |
2136 | msr_hv = 1; | |
a496775f | 2137 | #endif |
9a64fbe4 | 2138 | msr |= 0x00080000; |
76a66253 | 2139 | break; |
e1833e1f JM |
2140 | case POWERPC_EXCP_PRIV: |
2141 | msr_ri = 0; | |
2142 | #if defined(TARGET_PPC64H) | |
2143 | if (lpes1 == 0) | |
2144 | msr_hv = 1; | |
2145 | #endif | |
9a64fbe4 | 2146 | msr |= 0x00040000; |
76a66253 | 2147 | break; |
e1833e1f JM |
2148 | case POWERPC_EXCP_TRAP: |
2149 | msr_ri = 0; | |
2150 | #if defined(TARGET_PPC64H) | |
2151 | if (lpes1 == 0) | |
2152 | msr_hv = 1; | |
2153 | #endif | |
9a64fbe4 FB |
2154 | msr |= 0x00020000; |
2155 | break; | |
2156 | default: | |
2157 | /* Should never occur */ | |
e1833e1f JM |
2158 | cpu_abort(env, "Invalid program exception %d. Aborting\n", |
2159 | env->error_code); | |
76a66253 JM |
2160 | break; |
2161 | } | |
9a64fbe4 | 2162 | goto store_next; |
e1833e1f JM |
2163 | case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ |
2164 | msr_ri = 0; | |
2165 | #if defined(TARGET_PPC64H) | |
2166 | if (lpes1 == 0) | |
2167 | msr_hv = 1; | |
2168 | #endif | |
2169 | goto store_current; | |
2170 | case POWERPC_EXCP_SYSCALL: /* System call exception */ | |
d094807b FB |
2171 | /* NOTE: this is a temporary hack to support graphics OSI |
2172 | calls from the MOL driver */ | |
e1833e1f | 2173 | /* XXX: To be removed */ |
d094807b FB |
2174 | if (env->gpr[3] == 0x113724fa && env->gpr[4] == 0x77810f9b && |
2175 | env->osi_call) { | |
2176 | if (env->osi_call(env) != 0) | |
2177 | return; | |
2178 | } | |
b769d8fe | 2179 | if (loglevel & CPU_LOG_INT) { |
d094807b | 2180 | dump_syscall(env); |
b769d8fe | 2181 | } |
e1833e1f JM |
2182 | msr_ri = 0; |
2183 | #if defined(TARGET_PPC64H) | |
2184 | if (lev == 1 || (lpes0 == 0 && lpes1 == 0)) | |
2185 | msr_hv = 1; | |
2186 | #endif | |
2187 | goto store_next; | |
2188 | case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ | |
2189 | msr_ri = 0; | |
2190 | goto store_current; | |
2191 | case POWERPC_EXCP_DECR: /* Decrementer exception */ | |
2192 | msr_ri = 0; | |
2193 | #if defined(TARGET_PPC64H) | |
2194 | if (lpes1 == 0) | |
2195 | msr_hv = 1; | |
2196 | #endif | |
2197 | goto store_next; | |
2198 | case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ | |
2199 | /* FIT on 4xx */ | |
2200 | #if defined (DEBUG_EXCEPTIONS) | |
2201 | if (loglevel != 0) | |
2202 | fprintf(logfile, "FIT exception\n"); | |
2203 | #endif | |
2204 | msr_ri = 0; /* XXX: check this */ | |
9a64fbe4 | 2205 | goto store_next; |
e1833e1f JM |
2206 | case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ |
2207 | #if defined (DEBUG_EXCEPTIONS) | |
2208 | if (loglevel != 0) | |
2209 | fprintf(logfile, "WDT exception\n"); | |
2210 | #endif | |
2211 | switch (excp_model) { | |
2212 | case POWERPC_EXCP_BOOKE: | |
2213 | srr0 = SPR_BOOKE_CSRR0; | |
2214 | srr1 = SPR_BOOKE_CSRR1; | |
2215 | break; | |
2216 | default: | |
2217 | break; | |
2218 | } | |
2219 | msr_ri = 0; /* XXX: check this */ | |
2be0071f | 2220 | goto store_next; |
e1833e1f JM |
2221 | case POWERPC_EXCP_DTLB: /* Data TLB error */ |
2222 | msr_ri = 0; /* XXX: check this */ | |
2223 | goto store_next; | |
2224 | case POWERPC_EXCP_ITLB: /* Instruction TLB error */ | |
2225 | msr_ri = 0; /* XXX: check this */ | |
2226 | goto store_next; | |
2227 | case POWERPC_EXCP_DEBUG: /* Debug interrupt */ | |
2228 | switch (excp_model) { | |
2229 | case POWERPC_EXCP_BOOKE: | |
2230 | srr0 = SPR_BOOKE_DSRR0; | |
2231 | srr1 = SPR_BOOKE_DSRR1; | |
2232 | asrr0 = SPR_BOOKE_CSRR0; | |
2233 | asrr1 = SPR_BOOKE_CSRR1; | |
2234 | break; | |
2235 | default: | |
2236 | break; | |
2237 | } | |
2be0071f | 2238 | /* XXX: TODO */ |
e1833e1f | 2239 | cpu_abort(env, "Debug exception is not implemented yet !\n"); |
2be0071f | 2240 | goto store_next; |
e1833e1f JM |
2241 | #if defined(TARGET_PPCEMB) |
2242 | case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */ | |
2243 | msr_ri = 0; /* XXX: check this */ | |
2244 | goto store_current; | |
2245 | case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ | |
2be0071f | 2246 | /* XXX: TODO */ |
e1833e1f | 2247 | cpu_abort(env, "Embedded floating point data exception " |
2be0071f FB |
2248 | "is not implemented yet !\n"); |
2249 | goto store_next; | |
e1833e1f | 2250 | case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ |
2be0071f | 2251 | /* XXX: TODO */ |
e1833e1f JM |
2252 | cpu_abort(env, "Embedded floating point round exception " |
2253 | "is not implemented yet !\n"); | |
9a64fbe4 | 2254 | goto store_next; |
e1833e1f JM |
2255 | case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ |
2256 | msr_ri = 0; | |
2be0071f FB |
2257 | /* XXX: TODO */ |
2258 | cpu_abort(env, | |
e1833e1f | 2259 | "Performance counter exception is not implemented yet !\n"); |
9a64fbe4 | 2260 | goto store_next; |
e1833e1f | 2261 | case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ |
76a66253 | 2262 | /* XXX: TODO */ |
e1833e1f JM |
2263 | cpu_abort(env, |
2264 | "Embedded doorbell interrupt is not implemented yet !\n"); | |
2be0071f | 2265 | goto store_next; |
e1833e1f JM |
2266 | case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ |
2267 | switch (excp_model) { | |
2268 | case POWERPC_EXCP_BOOKE: | |
2269 | srr0 = SPR_BOOKE_CSRR0; | |
2270 | srr1 = SPR_BOOKE_CSRR1; | |
a750fc0b | 2271 | break; |
2be0071f | 2272 | default: |
2be0071f FB |
2273 | break; |
2274 | } | |
e1833e1f JM |
2275 | /* XXX: TODO */ |
2276 | cpu_abort(env, "Embedded doorbell critical interrupt " | |
2277 | "is not implemented yet !\n"); | |
2278 | goto store_next; | |
2279 | #endif /* defined(TARGET_PPCEMB) */ | |
2280 | case POWERPC_EXCP_RESET: /* System reset exception */ | |
2281 | msr_ri = 0; | |
2282 | #if defined(TARGET_PPC64H) | |
2283 | msr_hv = 1; | |
2284 | #endif | |
2285 | excp_reset: | |
2286 | goto store_next; | |
2287 | #if defined(TARGET_PPC64) | |
2288 | case POWERPC_EXCP_DSEG: /* Data segment exception */ | |
2289 | msr_ri = 0; | |
2290 | #if defined(TARGET_PPC64H) | |
2291 | if (lpes1 == 0) | |
2292 | msr_hv = 1; | |
2293 | #endif | |
2294 | /* XXX: TODO */ | |
2295 | cpu_abort(env, "Data segment exception is not implemented yet !\n"); | |
2296 | goto store_next; | |
2297 | case POWERPC_EXCP_ISEG: /* Instruction segment exception */ | |
2298 | msr_ri = 0; | |
2299 | #if defined(TARGET_PPC64H) | |
2300 | if (lpes1 == 0) | |
2301 | msr_hv = 1; | |
2302 | #endif | |
2303 | /* XXX: TODO */ | |
2304 | cpu_abort(env, | |
2305 | "Instruction segment exception is not implemented yet !\n"); | |
2306 | goto store_next; | |
2307 | #endif /* defined(TARGET_PPC64) */ | |
2308 | #if defined(TARGET_PPC64H) | |
2309 | case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ | |
2310 | srr0 = SPR_HSRR0; | |
2311 | srr1 = SPR_HSSR1; | |
2312 | msr_hv = 1; | |
2313 | goto store_next; | |
2314 | #endif | |
2315 | case POWERPC_EXCP_TRACE: /* Trace exception */ | |
2316 | msr_ri = 0; | |
2317 | #if defined(TARGET_PPC64H) | |
2318 | if (lpes1 == 0) | |
2319 | msr_hv = 1; | |
2320 | #endif | |
2321 | goto store_next; | |
2322 | #if defined(TARGET_PPC64H) | |
2323 | case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ | |
2324 | srr0 = SPR_HSRR0; | |
2325 | srr1 = SPR_HSSR1; | |
2326 | msr_hv = 1; | |
2327 | goto store_next; | |
2328 | case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ | |
2329 | srr0 = SPR_HSRR0; | |
2330 | srr1 = SPR_HSSR1; | |
2331 | msr_hv = 1; | |
2332 | /* XXX: TODO */ | |
2333 | cpu_abort(env, "Hypervisor instruction storage exception " | |
2334 | "is not implemented yet !\n"); | |
2335 | goto store_next; | |
2336 | case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ | |
2337 | srr0 = SPR_HSRR0; | |
2338 | srr1 = SPR_HSSR1; | |
2339 | msr_hv = 1; | |
2340 | goto store_next; | |
2341 | case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ | |
2342 | srr0 = SPR_HSRR0; | |
2343 | srr1 = SPR_HSSR1; | |
2344 | msr_hv = 1; | |
2345 | goto store_next; | |
2346 | #endif /* defined(TARGET_PPC64H) */ | |
2347 | case POWERPC_EXCP_VPU: /* Vector unavailable exception */ | |
2348 | msr_ri = 0; | |
2349 | #if defined(TARGET_PPC64H) | |
2350 | if (lpes1 == 0) | |
2351 | msr_hv = 1; | |
2352 | #endif | |
2353 | goto store_current; | |
2354 | case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ | |
a496775f | 2355 | #if defined (DEBUG_EXCEPTIONS) |
e1833e1f JM |
2356 | if (loglevel != 0) |
2357 | fprintf(logfile, "PIT exception\n"); | |
2358 | #endif | |
2359 | msr_ri = 0; /* XXX: check this */ | |
2360 | goto store_next; | |
2361 | case POWERPC_EXCP_IO: /* IO error exception */ | |
2362 | /* XXX: TODO */ | |
2363 | cpu_abort(env, "601 IO error exception is not implemented yet !\n"); | |
2364 | goto store_next; | |
2365 | case POWERPC_EXCP_RUNM: /* Run mode exception */ | |
2366 | /* XXX: TODO */ | |
2367 | cpu_abort(env, "601 run mode exception is not implemented yet !\n"); | |
2368 | goto store_next; | |
2369 | case POWERPC_EXCP_EMUL: /* Emulation trap exception */ | |
2370 | /* XXX: TODO */ | |
2371 | cpu_abort(env, "602 emulation trap exception " | |
2372 | "is not implemented yet !\n"); | |
2373 | goto store_next; | |
2374 | case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ | |
2375 | msr_ri = 0; /* XXX: check this */ | |
2376 | #if defined(TARGET_PPC64H) /* XXX: check this */ | |
2377 | if (lpes1 == 0) | |
2378 | msr_hv = 1; | |
a496775f | 2379 | #endif |
e1833e1f | 2380 | switch (excp_model) { |
a750fc0b JM |
2381 | case POWERPC_EXCP_602: |
2382 | case POWERPC_EXCP_603: | |
2383 | case POWERPC_EXCP_603E: | |
2384 | case POWERPC_EXCP_G2: | |
e1833e1f | 2385 | goto tlb_miss_tgpr; |
a750fc0b | 2386 | case POWERPC_EXCP_7x5: |
76a66253 | 2387 | goto tlb_miss; |
7dbe11ac JM |
2388 | case POWERPC_EXCP_74xx: |
2389 | goto tlb_miss_74xx; | |
2be0071f | 2390 | default: |
e1833e1f | 2391 | cpu_abort(env, "Invalid instruction TLB miss exception\n"); |
2be0071f FB |
2392 | break; |
2393 | } | |
e1833e1f JM |
2394 | break; |
2395 | case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ | |
2396 | msr_ri = 0; /* XXX: check this */ | |
2397 | #if defined(TARGET_PPC64H) /* XXX: check this */ | |
2398 | if (lpes1 == 0) | |
2399 | msr_hv = 1; | |
a496775f | 2400 | #endif |
e1833e1f | 2401 | switch (excp_model) { |
a750fc0b JM |
2402 | case POWERPC_EXCP_602: |
2403 | case POWERPC_EXCP_603: | |
2404 | case POWERPC_EXCP_603E: | |
2405 | case POWERPC_EXCP_G2: | |
e1833e1f | 2406 | goto tlb_miss_tgpr; |
a750fc0b | 2407 | case POWERPC_EXCP_7x5: |
76a66253 | 2408 | goto tlb_miss; |
7dbe11ac JM |
2409 | case POWERPC_EXCP_74xx: |
2410 | goto tlb_miss_74xx; | |
2be0071f | 2411 | default: |
e1833e1f | 2412 | cpu_abort(env, "Invalid data load TLB miss exception\n"); |
2be0071f FB |
2413 | break; |
2414 | } | |
e1833e1f JM |
2415 | break; |
2416 | case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ | |
2417 | msr_ri = 0; /* XXX: check this */ | |
2418 | #if defined(TARGET_PPC64H) /* XXX: check this */ | |
2419 | if (lpes1 == 0) | |
2420 | msr_hv = 1; | |
2421 | #endif | |
2422 | switch (excp_model) { | |
a750fc0b JM |
2423 | case POWERPC_EXCP_602: |
2424 | case POWERPC_EXCP_603: | |
2425 | case POWERPC_EXCP_603E: | |
2426 | case POWERPC_EXCP_G2: | |
e1833e1f | 2427 | tlb_miss_tgpr: |
76a66253 JM |
2428 | /* Swap temporary saved registers with GPRs */ |
2429 | swap_gpr_tgpr(env); | |
2430 | msr_tgpr = 1; | |
e1833e1f JM |
2431 | goto tlb_miss; |
2432 | case POWERPC_EXCP_7x5: | |
2433 | tlb_miss: | |
2be0071f FB |
2434 | #if defined (DEBUG_SOFTWARE_TLB) |
2435 | if (loglevel != 0) { | |
76a66253 JM |
2436 | const unsigned char *es; |
2437 | target_ulong *miss, *cmp; | |
2438 | int en; | |
1e6784f9 | 2439 | if (excp == POWERPC_EXCP_IFTLB) { |
76a66253 JM |
2440 | es = "I"; |
2441 | en = 'I'; | |
2442 | miss = &env->spr[SPR_IMISS]; | |
2443 | cmp = &env->spr[SPR_ICMP]; | |
2444 | } else { | |
1e6784f9 | 2445 | if (excp == POWERPC_EXCP_DLTLB) |
76a66253 JM |
2446 | es = "DL"; |
2447 | else | |
2448 | es = "DS"; | |
2449 | en = 'D'; | |
2450 | miss = &env->spr[SPR_DMISS]; | |
2451 | cmp = &env->spr[SPR_DCMP]; | |
2452 | } | |
1b9eb036 | 2453 | fprintf(logfile, "6xx %sTLB miss: %cM " ADDRX " %cC " ADDRX |
4a057712 | 2454 | " H1 " ADDRX " H2 " ADDRX " %08x\n", |
1b9eb036 | 2455 | es, en, *miss, en, *cmp, |
76a66253 | 2456 | env->spr[SPR_HASH1], env->spr[SPR_HASH2], |
2be0071f FB |
2457 | env->error_code); |
2458 | } | |
9a64fbe4 | 2459 | #endif |
2be0071f FB |
2460 | msr |= env->crf[0] << 28; |
2461 | msr |= env->error_code; /* key, D/I, S/L bits */ | |
2462 | /* Set way using a LRU mechanism */ | |
76a66253 | 2463 | msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; |
c62db105 | 2464 | break; |
7dbe11ac JM |
2465 | case POWERPC_EXCP_74xx: |
2466 | tlb_miss_74xx: | |
2467 | #if defined (DEBUG_SOFTWARE_TLB) | |
2468 | if (loglevel != 0) { | |
2469 | const unsigned char *es; | |
2470 | target_ulong *miss, *cmp; | |
2471 | int en; | |
2472 | if (excp == POWERPC_EXCP_IFTLB) { | |
2473 | es = "I"; | |
2474 | en = 'I'; | |
2475 | miss = &env->spr[SPR_IMISS]; | |
2476 | cmp = &env->spr[SPR_ICMP]; | |
2477 | } else { | |
2478 | if (excp == POWERPC_EXCP_DLTLB) | |
2479 | es = "DL"; | |
2480 | else | |
2481 | es = "DS"; | |
2482 | en = 'D'; | |
2483 | miss = &env->spr[SPR_TLBMISS]; | |
2484 | cmp = &env->spr[SPR_PTEHI]; | |
2485 | } | |
2486 | fprintf(logfile, "74xx %sTLB miss: %cM " ADDRX " %cC " ADDRX | |
2487 | " %08x\n", | |
2488 | es, en, *miss, en, *cmp, env->error_code); | |
2489 | } | |
2490 | #endif | |
2491 | msr |= env->error_code; /* key bit */ | |
2492 | break; | |
2be0071f | 2493 | default: |
e1833e1f | 2494 | cpu_abort(env, "Invalid data store TLB miss exception\n"); |
2be0071f FB |
2495 | break; |
2496 | } | |
e1833e1f JM |
2497 | goto store_next; |
2498 | case POWERPC_EXCP_FPA: /* Floating-point assist exception */ | |
2499 | /* XXX: TODO */ | |
2500 | cpu_abort(env, "Floating point assist exception " | |
2501 | "is not implemented yet !\n"); | |
2502 | goto store_next; | |
2503 | case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ | |
2504 | /* XXX: TODO */ | |
2505 | cpu_abort(env, "IABR exception is not implemented yet !\n"); | |
2506 | goto store_next; | |
2507 | case POWERPC_EXCP_SMI: /* System management interrupt */ | |
2508 | /* XXX: TODO */ | |
2509 | cpu_abort(env, "SMI exception is not implemented yet !\n"); | |
2510 | goto store_next; | |
2511 | case POWERPC_EXCP_THERM: /* Thermal interrupt */ | |
2512 | /* XXX: TODO */ | |
2513 | cpu_abort(env, "Thermal management exception " | |
2514 | "is not implemented yet !\n"); | |
2515 | goto store_next; | |
2516 | case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ | |
2517 | msr_ri = 0; | |
2518 | #if defined(TARGET_PPC64H) | |
2519 | if (lpes1 == 0) | |
2520 | msr_hv = 1; | |
2521 | #endif | |
2522 | /* XXX: TODO */ | |
2523 | cpu_abort(env, | |
2524 | "Performance counter exception is not implemented yet !\n"); | |
2525 | goto store_next; | |
2526 | case POWERPC_EXCP_VPUA: /* Vector assist exception */ | |
2527 | /* XXX: TODO */ | |
2528 | cpu_abort(env, "VPU assist exception is not implemented yet !\n"); | |
2529 | goto store_next; | |
2530 | case POWERPC_EXCP_SOFTP: /* Soft patch exception */ | |
2531 | /* XXX: TODO */ | |
2532 | cpu_abort(env, | |
2533 | "970 soft-patch exception is not implemented yet !\n"); | |
2534 | goto store_next; | |
2535 | case POWERPC_EXCP_MAINT: /* Maintenance exception */ | |
2536 | /* XXX: TODO */ | |
2537 | cpu_abort(env, | |
2538 | "970 maintenance exception is not implemented yet !\n"); | |
2539 | goto store_next; | |
2be0071f | 2540 | default: |
e1833e1f JM |
2541 | excp_invalid: |
2542 | cpu_abort(env, "Invalid PowerPC exception %d. Aborting\n", excp); | |
2543 | break; | |
9a64fbe4 | 2544 | store_current: |
2be0071f | 2545 | /* save current instruction location */ |
e1833e1f | 2546 | env->spr[srr0] = env->nip - 4; |
9a64fbe4 FB |
2547 | break; |
2548 | store_next: | |
2be0071f | 2549 | /* save next instruction location */ |
e1833e1f | 2550 | env->spr[srr0] = env->nip; |
9a64fbe4 FB |
2551 | break; |
2552 | } | |
e1833e1f JM |
2553 | /* Save MSR */ |
2554 | env->spr[srr1] = msr; | |
2555 | /* If any alternate SRR register are defined, duplicate saved values */ | |
2556 | if (asrr0 != -1) | |
2557 | env->spr[asrr0] = env->spr[srr0]; | |
2558 | if (asrr1 != -1) | |
2559 | env->spr[asrr1] = env->spr[srr1]; | |
2be0071f | 2560 | /* If we disactivated any translation, flush TLBs */ |
e1833e1f | 2561 | if (msr_ir || msr_dr) |
2be0071f | 2562 | tlb_flush(env, 1); |
9a64fbe4 | 2563 | /* reload MSR with correct bits */ |
9a64fbe4 FB |
2564 | msr_ee = 0; |
2565 | msr_pr = 0; | |
2566 | msr_fp = 0; | |
2567 | msr_fe0 = 0; | |
2568 | msr_se = 0; | |
2569 | msr_be = 0; | |
2570 | msr_fe1 = 0; | |
2571 | msr_ir = 0; | |
2572 | msr_dr = 0; | |
e1833e1f JM |
2573 | #if 0 /* Fix this: not on all targets */ |
2574 | msr_pmm = 0; | |
2575 | #endif | |
9a64fbe4 | 2576 | msr_le = msr_ile; |
e1833e1f JM |
2577 | do_compute_hflags(env); |
2578 | /* Jump to handler */ | |
2579 | vector = env->excp_vectors[excp]; | |
2580 | if (vector == (target_ulong)-1) { | |
2581 | cpu_abort(env, "Raised an exception without defined vector %d\n", | |
2582 | excp); | |
2583 | } | |
2584 | vector |= env->excp_prefix; | |
c62db105 | 2585 | #if defined(TARGET_PPC64) |
e1833e1f JM |
2586 | if (excp_model == POWERPC_EXCP_BOOKE) { |
2587 | msr_cm = msr_icm; | |
2588 | if (!msr_cm) | |
2589 | vector = (uint32_t)vector; | |
c62db105 JM |
2590 | } else { |
2591 | msr_sf = msr_isf; | |
e1833e1f JM |
2592 | if (!msr_sf) |
2593 | vector = (uint32_t)vector; | |
c62db105 | 2594 | } |
e1833e1f JM |
2595 | #endif |
2596 | env->nip = vector; | |
2597 | /* Reset exception state */ | |
2598 | env->exception_index = POWERPC_EXCP_NONE; | |
2599 | env->error_code = 0; | |
fb0eaffc | 2600 | } |
47103572 | 2601 | |
e1833e1f | 2602 | void do_interrupt (CPUState *env) |
47103572 | 2603 | { |
e1833e1f JM |
2604 | powerpc_excp(env, env->excp_model, env->exception_index); |
2605 | } | |
47103572 | 2606 | |
e1833e1f JM |
2607 | void ppc_hw_interrupt (CPUPPCState *env) |
2608 | { | |
a496775f JM |
2609 | #if 1 |
2610 | if (loglevel & CPU_LOG_INT) { | |
2611 | fprintf(logfile, "%s: %p pending %08x req %08x me %d ee %d\n", | |
2612 | __func__, env, env->pending_interrupts, | |
2613 | env->interrupt_request, msr_me, msr_ee); | |
2614 | } | |
47103572 | 2615 | #endif |
e1833e1f | 2616 | /* External reset */ |
47103572 | 2617 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) { |
47103572 | 2618 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET); |
e1833e1f JM |
2619 | powerpc_excp(env, env->excp_model, POWERPC_EXCP_RESET); |
2620 | return; | |
2621 | } | |
2622 | /* Machine check exception */ | |
2623 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) { | |
2624 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK); | |
2625 | powerpc_excp(env, env->excp_model, POWERPC_EXCP_MCHECK); | |
2626 | return; | |
47103572 | 2627 | } |
e1833e1f JM |
2628 | #if 0 /* TODO */ |
2629 | /* External debug exception */ | |
2630 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) { | |
2631 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG); | |
2632 | powerpc_excp(env, env->excp_model, POWERPC_EXCP_DEBUG); | |
2633 | return; | |
2634 | } | |
2635 | #endif | |
2636 | #if defined(TARGET_PPC64H) | |
2637 | if ((msr_ee != 0 || msr_hv == 0 || msr_pr == 1) & hdice != 0) { | |
47103572 JM |
2638 | /* Hypervisor decrementer exception */ |
2639 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) { | |
47103572 | 2640 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); |
e1833e1f JM |
2641 | powerpc_excp(env, env->excp_model, POWERPC_EXCP_HDECR); |
2642 | return; | |
2643 | } | |
2644 | } | |
2645 | #endif | |
2646 | if (msr_ce != 0) { | |
2647 | /* External critical interrupt */ | |
2648 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) { | |
2649 | /* Taking a critical external interrupt does not clear the external | |
2650 | * critical interrupt status | |
2651 | */ | |
2652 | #if 0 | |
2653 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CEXT); | |
47103572 | 2654 | #endif |
e1833e1f JM |
2655 | powerpc_excp(env, env->excp_model, POWERPC_EXCP_CRITICAL); |
2656 | return; | |
2657 | } | |
2658 | } | |
2659 | if (msr_ee != 0) { | |
2660 | /* Watchdog timer on embedded PowerPC */ | |
2661 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) { | |
2662 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT); | |
2663 | powerpc_excp(env, env->excp_model, POWERPC_EXCP_WDT); | |
2664 | return; | |
2665 | } | |
2666 | #if defined(TARGET_PPCEMB) | |
2667 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_CDOORBELL)) { | |
2668 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CDOORBELL); | |
2669 | powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORCI); | |
2670 | return; | |
2671 | } | |
2672 | #endif | |
2673 | #if defined(TARGET_PPCEMB) | |
2674 | /* External interrupt */ | |
2675 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { | |
2676 | /* Taking an external interrupt does not clear the external | |
2677 | * interrupt status | |
2678 | */ | |
2679 | #if 0 | |
2680 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EXT); | |
2681 | #endif | |
2682 | powerpc_excp(env, env->excp_model, POWERPC_EXCP_EXTERNAL); | |
2683 | return; | |
2684 | } | |
2685 | #endif | |
2686 | /* Fixed interval timer on embedded PowerPC */ | |
2687 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) { | |
2688 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT); | |
2689 | powerpc_excp(env, env->excp_model, POWERPC_EXCP_FIT); | |
2690 | return; | |
2691 | } | |
2692 | /* Programmable interval timer on embedded PowerPC */ | |
2693 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) { | |
2694 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT); | |
2695 | powerpc_excp(env, env->excp_model, POWERPC_EXCP_PIT); | |
2696 | return; | |
2697 | } | |
47103572 JM |
2698 | /* Decrementer exception */ |
2699 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_DECR)) { | |
47103572 | 2700 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DECR); |
e1833e1f JM |
2701 | powerpc_excp(env, env->excp_model, POWERPC_EXCP_DECR); |
2702 | return; | |
2703 | } | |
2704 | #if !defined(TARGET_PPCEMB) | |
47103572 | 2705 | /* External interrupt */ |
e1833e1f | 2706 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { |
e9df014c JM |
2707 | /* Taking an external interrupt does not clear the external |
2708 | * interrupt status | |
2709 | */ | |
2710 | #if 0 | |
47103572 | 2711 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EXT); |
e9df014c | 2712 | #endif |
e1833e1f JM |
2713 | powerpc_excp(env, env->excp_model, POWERPC_EXCP_EXTERNAL); |
2714 | return; | |
2715 | } | |
d0dfae6e | 2716 | #endif |
e1833e1f JM |
2717 | #if defined(TARGET_PPCEMB) |
2718 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) { | |
2719 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); | |
2720 | powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORI); | |
2721 | return; | |
47103572 | 2722 | } |
47103572 | 2723 | #endif |
e1833e1f JM |
2724 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_PERFM)) { |
2725 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PERFM); | |
2726 | powerpc_excp(env, env->excp_model, POWERPC_EXCP_PERFM); | |
2727 | return; | |
2728 | } | |
2729 | /* Thermal interrupt */ | |
2730 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_THERM)) { | |
2731 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_THERM); | |
2732 | powerpc_excp(env, env->excp_model, POWERPC_EXCP_THERM); | |
2733 | return; | |
2734 | } | |
47103572 | 2735 | } |
47103572 | 2736 | } |
18fba28c | 2737 | #endif /* !CONFIG_USER_ONLY */ |
a496775f JM |
2738 | |
2739 | void cpu_dump_EA (target_ulong EA) | |
2740 | { | |
2741 | FILE *f; | |
2742 | ||
2743 | if (logfile) { | |
2744 | f = logfile; | |
2745 | } else { | |
2746 | f = stdout; | |
2747 | return; | |
2748 | } | |
4a057712 JM |
2749 | fprintf(f, "Memory access at address " ADDRX "\n", EA); |
2750 | } | |
2751 | ||
2752 | void cpu_dump_rfi (target_ulong RA, target_ulong msr) | |
2753 | { | |
2754 | FILE *f; | |
2755 | ||
2756 | if (logfile) { | |
2757 | f = logfile; | |
2758 | } else { | |
2759 | f = stdout; | |
2760 | return; | |
2761 | } | |
2762 | fprintf(f, "Return from exception at " ADDRX " with flags " ADDRX "\n", | |
2763 | RA, msr); | |
a496775f JM |
2764 | } |
2765 | ||
0a032cbe JM |
2766 | void cpu_ppc_reset (void *opaque) |
2767 | { | |
2768 | CPUPPCState *env; | |
5eb7995e | 2769 | int i; |
0a032cbe JM |
2770 | |
2771 | env = opaque; | |
5eb7995e JM |
2772 | /* XXX: some of those flags initialisation values could depend |
2773 | * on the actual PowerPC implementation | |
2774 | */ | |
2775 | for (i = 0; i < 63; i++) | |
2776 | env->msr[i] = 0; | |
2777 | #if defined(TARGET_PPC64) | |
2778 | msr_hv = 0; /* Should be 1... */ | |
2779 | #endif | |
2780 | msr_ap = 0; /* TO BE CHECKED */ | |
2781 | msr_sa = 0; /* TO BE CHECKED */ | |
2782 | msr_ip = 0; /* TO BE CHECKED */ | |
0a032cbe JM |
2783 | #if defined (DO_SINGLE_STEP) && 0 |
2784 | /* Single step trace mode */ | |
2785 | msr_se = 1; | |
2786 | msr_be = 1; | |
0a032cbe JM |
2787 | #endif |
2788 | #if defined(CONFIG_USER_ONLY) | |
5eb7995e | 2789 | msr_fp = 1; /* Allow floating point exceptions */ |
0a032cbe | 2790 | msr_pr = 1; |
0a032cbe JM |
2791 | #else |
2792 | env->nip = 0xFFFFFFFC; | |
2793 | ppc_tlb_invalidate_all(env); | |
2794 | #endif | |
2795 | do_compute_hflags(env); | |
2796 | env->reserve = -1; | |
5eb7995e JM |
2797 | /* Be sure no exception or interrupt is pending */ |
2798 | env->pending_interrupts = 0; | |
e1833e1f JM |
2799 | env->exception_index = POWERPC_EXCP_NONE; |
2800 | env->error_code = 0; | |
5eb7995e JM |
2801 | /* Flush all TLBs */ |
2802 | tlb_flush(env, 1); | |
0a032cbe JM |
2803 | } |
2804 | ||
2805 | CPUPPCState *cpu_ppc_init (void) | |
2806 | { | |
2807 | CPUPPCState *env; | |
2808 | ||
2809 | env = qemu_mallocz(sizeof(CPUPPCState)); | |
2810 | if (!env) | |
2811 | return NULL; | |
2812 | cpu_exec_init(env); | |
2813 | cpu_ppc_reset(env); | |
2814 | ||
2815 | return env; | |
2816 | } | |
2817 | ||
2818 | void cpu_ppc_close (CPUPPCState *env) | |
2819 | { | |
2820 | /* Should also remove all opcode tables... */ | |
2821 | free(env); | |
2822 | } |