1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | #include "cpu.h" |
22 | #include "helper.h" |
23 | #include "sysemu/kvm.h" |
24 | #include "kvm_ppc.h" |
25 | #include "mmu-hash32.h" |
26 | |
27 | |
28 | |
29 | |
30 | #ifdef DEBUG_MMU |
31 | # define LOG_MMU(...)do { } while (0) qemu_log(__VA_ARGS__) |
32 | # define LOG_MMU_STATE(cpu)do { } while (0) log_cpu_state((cpu), 0) |
33 | #else |
34 | # define LOG_MMU(...)do { } while (0) do { } while (0) |
35 | # define LOG_MMU_STATE(cpu)do { } while (0) do { } while (0) |
36 | #endif |
37 | |
38 | #ifdef DEBUG_BATS |
39 | # define LOG_BATS(...)do { } while (0) qemu_log(__VA_ARGS__) |
40 | #else |
41 | # define LOG_BATS(...)do { } while (0) do { } while (0) |
42 | #endif |
43 | |
44 | struct mmu_ctx_hash32 { |
45 | hwaddr raddr; |
46 | int prot; |
47 | int key; |
48 | }; |
49 | |
50 | static int ppc_hash32_pp_prot(int key, int pp, int nx) |
51 | { |
52 | int prot; |
53 | |
54 | if (key == 0) { |
55 | switch (pp) { |
56 | case 0x0: |
57 | case 0x1: |
58 | case 0x2: |
59 | prot = PAGE_READ0x0001 | PAGE_WRITE0x0002; |
60 | break; |
61 | |
62 | case 0x3: |
63 | prot = PAGE_READ0x0001; |
64 | break; |
65 | |
66 | default: |
67 | abort(); |
68 | } |
69 | } else { |
70 | switch (pp) { |
71 | case 0x0: |
72 | prot = 0; |
73 | break; |
74 | |
75 | case 0x1: |
76 | case 0x3: |
77 | prot = PAGE_READ0x0001; |
78 | break; |
79 | |
80 | case 0x2: |
81 | prot = PAGE_READ0x0001 | PAGE_WRITE0x0002; |
82 | break; |
83 | |
84 | default: |
85 | abort(); |
86 | } |
87 | } |
88 | if (nx == 0) { |
89 | prot |= PAGE_EXEC0x0004; |
90 | } |
91 | |
92 | return prot; |
93 | } |
94 | |
95 | static int ppc_hash32_pte_prot(CPUPPCState *env, |
96 | target_ulong sr, ppc_hash_pte32_t pte) |
97 | { |
98 | unsigned pp, key; |
99 | |
100 | key = !!(msr_pr((env->msr >> 14) & 1) ? (sr & SR32_KP0x20000000) : (sr & SR32_KS0x40000000)); |
101 | pp = pte.pte1 & HPTE32_R_PP0x00000003; |
102 | |
103 | return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX0x10000000)); |
104 | } |
105 | |
106 | static target_ulong hash32_bat_size(CPUPPCState *env, |
107 | target_ulong batu, target_ulong batl) |
108 | { |
109 | if ((msr_pr((env->msr >> 14) & 1) && !(batu & BATU32_VP0x00000001)) |
110 | || (!msr_pr((env->msr >> 14) & 1) && !(batu & BATU32_VS0x00000002))) { |
111 | return 0; |
112 | } |
113 | |
114 | return BATU32_BEPI0xfffe0000 & ~((batu & BATU32_BL0x00001ffc) << 15); |
115 | } |
116 | |
117 | static int hash32_bat_prot(CPUPPCState *env, |
118 | target_ulong batu, target_ulong batl) |
119 | { |
120 | int pp, prot; |
121 | |
122 | prot = 0; |
123 | pp = batl & BATL32_PP0x00000003; |
124 | if (pp != 0) { |
125 | prot = PAGE_READ0x0001 | PAGE_EXEC0x0004; |
126 | if (pp == 0x2) { |
127 | prot |= PAGE_WRITE0x0002; |
128 | } |
129 | } |
130 | return prot; |
131 | } |
132 | |
133 | static target_ulong hash32_bat_601_size(CPUPPCState *env, |
134 | target_ulong batu, target_ulong batl) |
135 | { |
136 | if (!(batl & BATL32_601_V0x00000040)) { |
137 | return 0; |
138 | } |
139 | |
140 | return BATU32_BEPI0xfffe0000 & ~((batl & BATL32_601_BL0x0000003f) << 17); |
141 | } |
142 | |
143 | static int hash32_bat_601_prot(CPUPPCState *env, |
144 | target_ulong batu, target_ulong batl) |
145 | { |
146 | int key, pp; |
147 | |
148 | pp = batu & BATU32_601_PP0x00000003; |
149 | if (msr_pr((env->msr >> 14) & 1) == 0) { |
150 | key = !!(batu & BATU32_601_KS0x00000008); |
151 | } else { |
152 | key = !!(batu & BATU32_601_KP0x00000004); |
153 | } |
154 | return ppc_hash32_pp_prot(key, pp, 0); |
155 | } |
156 | |
157 | static hwaddr ppc_hash32_bat_lookup(CPUPPCState *env, target_ulong ea, int rwx, |
158 | int *prot) |
159 | { |
160 | target_ulong *BATlt, *BATut; |
161 | int i; |
162 | |
163 | LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__,do { } while (0) |
164 | rwx == 2 ? 'I' : 'D', ea)do { } while (0); |
165 | if (rwx == 2) { |
166 | BATlt = env->IBAT[1]; |
167 | BATut = env->IBAT[0]; |
168 | } else { |
169 | BATlt = env->DBAT[1]; |
170 | BATut = env->DBAT[0]; |
171 | } |
172 | for (i = 0; i < env->nb_BATs; i++) { |
173 | target_ulong batu = BATut[i]; |
174 | target_ulong batl = BATlt[i]; |
175 | target_ulong mask; |
176 | |
177 | if (unlikely(env->mmu_model == POWERPC_MMU_601)__builtin_expect(!!(env->mmu_model == POWERPC_MMU_601), 0)) { |
178 | mask = hash32_bat_601_size(env, batu, batl); |
179 | } else { |
180 | mask = hash32_bat_size(env, batu, batl); |
181 | } |
182 | LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lxdo { } while (0) |
183 | " BATl " TARGET_FMT_lx "\n", __func__,do { } while (0) |
184 | type == ACCESS_CODE ? 'I' : 'D', i, ea, batu, batl)do { } while (0); |
185 | |
186 | if (mask && ((ea & mask) == (batu & BATU32_BEPI0xfffe0000))) { |
187 | hwaddr raddr = (batl & mask) | (ea & ~mask); |
188 | |
189 | if (unlikely(env->mmu_model == POWERPC_MMU_601)__builtin_expect(!!(env->mmu_model == POWERPC_MMU_601), 0)) { |
190 | *prot = hash32_bat_601_prot(env, batu, batl); |
191 | } else { |
192 | *prot = hash32_bat_prot(env, batu, batl); |
193 | } |
194 | |
195 | return raddr & TARGET_PAGE_MASK~((1 << 12) - 1); |
196 | } |
197 | } |
198 | |
199 | |
200 | #if defined(DEBUG_BATS) |
201 | if (qemu_log_enabled()) { |
202 | LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", ea)do { } while (0); |
203 | for (i = 0; i < 4; i++) { |
204 | BATu = &BATut[i]; |
205 | BATl = &BATlt[i]; |
206 | BEPIu = *BATu & BATU32_BEPIU; |
207 | BEPIl = *BATu & BATU32_BEPIL; |
208 | bl = (*BATu & 0x00001FFC) << 15; |
209 | LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lxdo { } while (0) |
210 | " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " "do { } while (0) |
211 | TARGET_FMT_lx " " TARGET_FMT_lx "\n",do { } while (0) |
212 | __func__, type == ACCESS_CODE ? 'I' : 'D', i, ea,do { } while (0) |
213 | *BATu, *BATl, BEPIu, BEPIl, bl)do { } while (0); |
214 | } |
215 | } |
216 | #endif |
217 | |
218 | return -1; |
219 | } |
220 | |
221 | static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, |
222 | target_ulong eaddr, int rwx, |
223 | hwaddr *raddr, int *prot) |
224 | { |
225 | int key = !!(msr_pr((env->msr >> 14) & 1) ? (sr & SR32_KP0x20000000) : (sr & SR32_KS0x40000000)); |
| 7 | | Within the expansion of the macro 'msr_pr':
| |
|
226 | |
227 | LOG_MMU("direct store...\n")do { } while (0); |
| 8 | | Within the expansion of the macro 'LOG_MMU':
| |
|
228 | |
229 | if ((sr & 0x1FF00000) >> 20 == 0x07f) { |
| |
230 | |
231 | |
232 | |
233 | |
234 | *raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); |
235 | *prot = PAGE_READ0x0001 | PAGE_WRITE0x0002 | PAGE_EXEC0x0004; |
236 | return 0; |
237 | } |
238 | |
239 | if (rwx == 2) { |
| |
240 | |
241 | env->exception_index = POWERPC_EXCP_ISI; |
242 | env->error_code = 0x10000000; |
243 | return 1; |
244 | } |
245 | |
246 | switch (env->access_type) { |
| 11 | | Control jumps to 'case ACCESS_CACHE:' at line 266 | |
|
247 | case ACCESS_INT: |
248 | |
249 | break; |
250 | case ACCESS_FLOAT: |
251 | |
252 | env->exception_index = POWERPC_EXCP_ALIGN; |
253 | env->error_code = POWERPC_EXCP_ALIGN_FP; |
254 | env->spr[SPR_DAR(0x013)] = eaddr; |
255 | return 1; |
256 | case ACCESS_RES: |
257 | |
258 | env->error_code = 0; |
259 | env->spr[SPR_DAR(0x013)] = eaddr; |
260 | if (rwx == 1) { |
261 | env->spr[SPR_DSISR(0x012)] = 0x06000000; |
262 | } else { |
263 | env->spr[SPR_DSISR(0x012)] = 0x04000000; |
264 | } |
265 | return 1; |
266 | case ACCESS_CACHE: |
267 | |
268 | |
269 | |
270 | |
271 | *raddr = eaddr; |
272 | return 0; |
273 | case ACCESS_EXT: |
274 | |
275 | env->exception_index = POWERPC_EXCP_DSI; |
276 | env->error_code = 0; |
277 | env->spr[SPR_DAR(0x013)] = eaddr; |
278 | if (rwx == 1) { |
279 | env->spr[SPR_DSISR(0x012)] = 0x06100000; |
280 | } else { |
281 | env->spr[SPR_DSISR(0x012)] = 0x04100000; |
282 | } |
283 | return 1; |
284 | default: |
285 | qemu_log("ERROR: instruction should not need " |
286 | "address translation\n"); |
287 | abort(); |
288 | } |
289 | if ((rwx == 1 || key != 1) && (rwx == 0 || key != 0)) { |
290 | *raddr = eaddr; |
291 | return 0; |
292 | } else { |
293 | env->exception_index = POWERPC_EXCP_DSI; |
294 | env->error_code = 0; |
295 | env->spr[SPR_DAR(0x013)] = eaddr; |
296 | if (rwx == 1) { |
297 | env->spr[SPR_DSISR(0x012)] = 0x0a000000; |
298 | } else { |
299 | env->spr[SPR_DSISR(0x012)] = 0x08000000; |
300 | } |
301 | return 1; |
302 | } |
303 | } |
304 | |
305 | hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash) |
306 | { |
307 | return (hash * HASH_PTEG_SIZE_32(8 * 8)) & env->htab_mask; |
308 | } |
309 | |
310 | static hwaddr ppc_hash32_pteg_search(CPUPPCState *env, hwaddr pteg_off, |
311 | bool_Bool secondary, target_ulong ptem, |
312 | ppc_hash_pte32_t *pte) |
313 | { |
314 | hwaddr pte_offset = pteg_off; |
315 | target_ulong pte0, pte1; |
316 | int i; |
317 | |
318 | for (i = 0; i < HPTES_PER_GROUP8; i++) { |
319 | pte0 = ppc_hash32_load_hpte0(env, pte_offset); |
320 | pte1 = ppc_hash32_load_hpte1(env, pte_offset); |
321 | |
322 | if ((pte0 & HPTE32_V_VALID0x80000000) |
323 | && (secondary == !!(pte0 & HPTE32_V_SECONDARY0x00000040)) |
324 | && HPTE32_V_COMPARE(pte0, ptem)(!(((pte0) ^ (ptem)) & 0x7fffffbf))) { |
325 | pte->pte0 = pte0; |
326 | pte->pte1 = pte1; |
327 | return pte_offset; |
328 | } |
329 | |
330 | pte_offset += HASH_PTE_SIZE_328; |
331 | } |
332 | |
333 | return -1; |
334 | } |
335 | |
336 | static hwaddr ppc_hash32_htab_lookup(CPUPPCState *env, |
337 | target_ulong sr, target_ulong eaddr, |
338 | ppc_hash_pte32_t *pte) |
339 | { |
340 | hwaddr pteg_off, pte_offset; |
341 | hwaddr hash; |
342 | uint32_t vsid, pgidx, ptem; |
343 | |
344 | vsid = sr & SR32_VSID0x00ffffff; |
345 | pgidx = (eaddr & ~SEGMENT_MASK_256M(~((1ULL << 28) - 1))) >> TARGET_PAGE_BITS12; |
346 | hash = vsid ^ pgidx; |
347 | ptem = (vsid << 7) | (pgidx >> 10); |
348 | |
349 | |
350 | LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plxdo { } while (0) |
351 | " hash " TARGET_FMT_plx "\n",do { } while (0) |
352 | env->htab_base, env->htab_mask, hash)do { } while (0); |
353 | |
354 | |
355 | LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plxdo { } while (0) |
356 | " vsid=%" PRIx32 " ptem=%" PRIx32do { } while (0) |
357 | " hash=" TARGET_FMT_plx "\n",do { } while (0) |
358 | env->htab_base, env->htab_mask, vsid, ptem, hash)do { } while (0); |
359 | pteg_off = get_pteg_offset32(env, hash); |
360 | pte_offset = ppc_hash32_pteg_search(env, pteg_off, 0, ptem, pte); |
361 | if (pte_offset == -1) { |
362 | |
363 | LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plxdo { } while (0) |
364 | " vsid=%" PRIx32 " api=%" PRIx32do { } while (0) |
365 | " hash=" TARGET_FMT_plx "\n", env->htab_base,do { } while (0) |
366 | env->htab_mask, vsid, ptem, ~hash)do { } while (0); |
367 | pteg_off = get_pteg_offset32(env, ~hash); |
368 | pte_offset = ppc_hash32_pteg_search(env, pteg_off, 1, ptem, pte); |
369 | } |
370 | |
371 | return pte_offset; |
372 | } |
373 | |
374 | static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, |
375 | target_ulong eaddr) |
376 | { |
377 | hwaddr rpn = pte.pte1 & HPTE32_R_RPN0xfffff000; |
378 | hwaddr mask = ~TARGET_PAGE_MASK~((1 << 12) - 1); |
379 | |
380 | return (rpn & ~mask) | (eaddr & mask); |
381 | } |
382 | |
383 | int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr, int rwx, |
384 | int mmu_idx) |
385 | { |
386 | target_ulong sr; |
387 | hwaddr pte_offset; |
388 | ppc_hash_pte32_t pte; |
389 | int prot; |
| 1 | 'prot' declared without an initial value | |
|
390 | uint32_t new_pte1; |
391 | const int need_prot[] = {PAGE_READ0x0001, PAGE_WRITE0x0002, PAGE_EXEC0x0004}; |
392 | hwaddr raddr; |
393 | |
394 | assert((rwx == 0) || (rwx == 1) || (rwx == 2))(((rwx == 0) || (rwx == 1) || (rwx == 2)) ? (void) (0) : __assert_fail ("(rwx == 0) || (rwx == 1) || (rwx == 2)", "/home/stefan/src/qemu/qemu.org/qemu/target-ppc/mmu-hash32.c" , 394, __PRETTY_FUNCTION__)); |
395 | |
396 | |
397 | if (((rwx == 2) && (msr_ir((env->msr >> 5) & 1) == 0)) || ((rwx != 2) && (msr_dr((env->msr >> 4) & 1) == 0))) { |
| |
398 | |
399 | raddr = eaddr; |
400 | tlb_set_page(env, eaddr & TARGET_PAGE_MASK~((1 << 12) - 1), raddr & TARGET_PAGE_MASK~((1 << 12) - 1), |
401 | PAGE_READ0x0001 | PAGE_WRITE0x0002 | PAGE_EXEC0x0004, mmu_idx, |
402 | TARGET_PAGE_SIZE(1 << 12)); |
403 | return 0; |
404 | } |
405 | |
406 | |
407 | if (env->nb_BATs != 0) { |
| |
408 | raddr = ppc_hash32_bat_lookup(env, eaddr, rwx, &prot); |
409 | if (raddr != -1) { |
410 | if (need_prot[rwx] & ~prot) { |
411 | if (rwx == 2) { |
412 | env->exception_index = POWERPC_EXCP_ISI; |
413 | env->error_code = 0x08000000; |
414 | } else { |
415 | env->exception_index = POWERPC_EXCP_DSI; |
416 | env->error_code = 0; |
417 | env->spr[SPR_DAR(0x013)] = eaddr; |
418 | if (rwx == 1) { |
419 | env->spr[SPR_DSISR(0x012)] = 0x0a000000; |
420 | } else { |
421 | env->spr[SPR_DSISR(0x012)] = 0x08000000; |
422 | } |
423 | } |
424 | return 1; |
425 | } |
426 | |
427 | tlb_set_page(env, eaddr & TARGET_PAGE_MASK~((1 << 12) - 1), |
428 | raddr & TARGET_PAGE_MASK~((1 << 12) - 1), prot, mmu_idx, |
429 | TARGET_PAGE_SIZE(1 << 12)); |
430 | return 0; |
431 | } |
432 | } |
433 | |
434 | |
435 | sr = env->sr[eaddr >> 28]; |
436 | |
437 | |
438 | if (sr & SR32_T0x80000000) { |
| 4 | | Assuming 'sr' is & -2147483648 | |
|
| |
439 | if (ppc_hash32_direct_store(env, sr, eaddr, rwx, |
| 6 | | Calling 'ppc_hash32_direct_store' | |
|
| 12 | | Returning from 'ppc_hash32_direct_store' | |
|
| |
440 | &raddr, &prot) == 0) { |
441 | tlb_set_page(env, eaddr & TARGET_PAGE_MASK~((1 << 12) - 1), |
| 14 | | Function call argument is an uninitialized value |
|
442 | raddr & TARGET_PAGE_MASK~((1 << 12) - 1), prot, mmu_idx, |
443 | TARGET_PAGE_SIZE(1 << 12)); |
444 | return 0; |
445 | } else { |
446 | return 1; |
447 | } |
448 | } |
449 | |
450 | |
451 | if ((rwx == 2) && (sr & SR32_NX0x10000000)) { |
452 | env->exception_index = POWERPC_EXCP_ISI; |
453 | env->error_code = 0x10000000; |
454 | return 1; |
455 | } |
456 | |
457 | |
458 | pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte); |
459 | if (pte_offset == -1) { |
460 | if (rwx == 2) { |
461 | env->exception_index = POWERPC_EXCP_ISI; |
462 | env->error_code = 0x40000000; |
463 | } else { |
464 | env->exception_index = POWERPC_EXCP_DSI; |
465 | env->error_code = 0; |
466 | env->spr[SPR_DAR(0x013)] = eaddr; |
467 | if (rwx == 1) { |
468 | env->spr[SPR_DSISR(0x012)] = 0x42000000; |
469 | } else { |
470 | env->spr[SPR_DSISR(0x012)] = 0x40000000; |
471 | } |
472 | } |
473 | |
474 | return 1; |
475 | } |
476 | LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset)do { } while (0); |
477 | |
478 | |
479 | |
480 | prot = ppc_hash32_pte_prot(env, sr, pte); |
481 | |
482 | if (need_prot[rwx] & ~prot) { |
483 | |
484 | LOG_MMU("PTE access rejected\n")do { } while (0); |
485 | if (rwx == 2) { |
486 | env->exception_index = POWERPC_EXCP_ISI; |
487 | env->error_code = 0x08000000; |
488 | } else { |
489 | env->exception_index = POWERPC_EXCP_DSI; |
490 | env->error_code = 0; |
491 | env->spr[SPR_DAR(0x013)] = eaddr; |
492 | if (rwx == 1) { |
493 | env->spr[SPR_DSISR(0x012)] = 0x0a000000; |
494 | } else { |
495 | env->spr[SPR_DSISR(0x012)] = 0x08000000; |
496 | } |
497 | } |
498 | return 1; |
499 | } |
500 | |
501 | LOG_MMU("PTE access granted !\n")do { } while (0); |
502 | |
503 | |
504 | |
505 | new_pte1 = pte.pte1 | HPTE32_R_R0x00000100; |
506 | if (rwx == 1) { |
507 | new_pte1 |= HPTE32_R_C0x00000080; |
508 | } else { |
509 | |
510 | |
511 | prot &= ~PAGE_WRITE0x0002; |
512 | } |
513 | |
514 | if (new_pte1 != pte.pte1) { |
515 | ppc_hash32_store_hpte1(env, pte_offset, new_pte1); |
516 | } |
517 | |
518 | |
519 | |
520 | raddr = ppc_hash32_pte_raddr(sr, pte, eaddr); |
521 | |
522 | tlb_set_page(env, eaddr & TARGET_PAGE_MASK~((1 << 12) - 1), raddr & TARGET_PAGE_MASK~((1 << 12) - 1), |
523 | prot, mmu_idx, TARGET_PAGE_SIZE(1 << 12)); |
524 | |
525 | return 0; |
526 | } |
527 | |
528 | hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong eaddr) |
529 | { |
530 | target_ulong sr; |
531 | hwaddr pte_offset; |
532 | ppc_hash_pte32_t pte; |
533 | int prot; |
534 | |
535 | if (msr_dr((env->msr >> 4) & 1) == 0) { |
536 | |
537 | return eaddr; |
538 | } |
539 | |
540 | if (env->nb_BATs != 0) { |
541 | hwaddr raddr = ppc_hash32_bat_lookup(env, eaddr, 0, &prot); |
542 | if (raddr != -1) { |
543 | return raddr; |
544 | } |
545 | } |
546 | |
547 | sr = env->sr[eaddr >> 28]; |
548 | |
549 | if (sr & SR32_T0x80000000) { |
550 | |
551 | return -1; |
552 | } |
553 | |
554 | pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte); |
555 | if (pte_offset == -1) { |
556 | return -1; |
557 | } |
558 | |
559 | return ppc_hash32_pte_raddr(sr, pte, eaddr) & TARGET_PAGE_MASK~((1 << 12) - 1); |
560 | } |