]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * PowerPC MMU, TLB, SLB and BAT emulation helpers for QEMU. | |
3 | * | |
4 | * Copyright (c) 2003-2007 Jocelyn Mayer | |
5 | * Copyright (c) 2013 David Gibson, IBM Corporation | |
6 | * | |
7 | * This library is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
10 | * version 2 of the License, or (at your option) any later version. | |
11 | * | |
12 | * This library is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
18 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | #include "cpu.h" | |
21 | #include "helper.h" | |
22 | #include "sysemu/kvm.h" | |
23 | #include "kvm_ppc.h" | |
24 | #include "mmu-hash64.h" | |
25 | ||
26 | //#define DEBUG_MMU | |
27 | //#define DEBUG_SLB | |
28 | ||
29 | #ifdef DEBUG_MMU | |
30 | # define LOG_MMU(...) qemu_log(__VA_ARGS__) | |
31 | # define LOG_MMU_STATE(env) log_cpu_state((env), 0) | |
32 | #else | |
33 | # define LOG_MMU(...) do { } while (0) | |
34 | # define LOG_MMU_STATE(...) do { } while (0) | |
35 | #endif | |
36 | ||
37 | #ifdef DEBUG_SLB | |
38 | # define LOG_SLB(...) qemu_log(__VA_ARGS__) | |
39 | #else | |
40 | # define LOG_SLB(...) do { } while (0) | |
41 | #endif | |
42 | ||
43 | /* | |
44 | * SLB handling | |
45 | */ | |
46 | ||
47 | static ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr) | |
48 | { | |
49 | uint64_t esid_256M, esid_1T; | |
50 | int n; | |
51 | ||
52 | LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr); | |
53 | ||
54 | esid_256M = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V; | |
55 | esid_1T = (eaddr & SEGMENT_MASK_1T) | SLB_ESID_V; | |
56 | ||
57 | for (n = 0; n < env->slb_nr; n++) { | |
58 | ppc_slb_t *slb = &env->slb[n]; | |
59 | ||
60 | LOG_SLB("%s: slot %d %016" PRIx64 " %016" | |
61 | PRIx64 "\n", __func__, n, slb->esid, slb->vsid); | |
62 | /* We check for 1T matches on all MMUs here - if the MMU | |
63 | * doesn't have 1T segment support, we will have prevented 1T | |
64 | * entries from being inserted in the slbmte code. */ | |
65 | if (((slb->esid == esid_256M) && | |
66 | ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_256M)) | |
67 | || ((slb->esid == esid_1T) && | |
68 | ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_1T))) { | |
69 | return slb; | |
70 | } | |
71 | } | |
72 | ||
73 | return NULL; | |
74 | } | |
75 | ||
76 | void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env) | |
77 | { | |
78 | int i; | |
79 | uint64_t slbe, slbv; | |
80 | ||
81 | cpu_synchronize_state(CPU(ppc_env_get_cpu(env))); | |
82 | ||
83 | cpu_fprintf(f, "SLB\tESID\t\t\tVSID\n"); | |
84 | for (i = 0; i < env->slb_nr; i++) { | |
85 | slbe = env->slb[i].esid; | |
86 | slbv = env->slb[i].vsid; | |
87 | if (slbe == 0 && slbv == 0) { | |
88 | continue; | |
89 | } | |
90 | cpu_fprintf(f, "%d\t0x%016" PRIx64 "\t0x%016" PRIx64 "\n", | |
91 | i, slbe, slbv); | |
92 | } | |
93 | } | |
94 | ||
95 | void helper_slbia(CPUPPCState *env) | |
96 | { | |
97 | int n, do_invalidate; | |
98 | ||
99 | do_invalidate = 0; | |
100 | /* XXX: Warning: slbia never invalidates the first segment */ | |
101 | for (n = 1; n < env->slb_nr; n++) { | |
102 | ppc_slb_t *slb = &env->slb[n]; | |
103 | ||
104 | if (slb->esid & SLB_ESID_V) { | |
105 | slb->esid &= ~SLB_ESID_V; | |
106 | /* XXX: given the fact that segment size is 256 MB or 1TB, | |
107 | * and we still don't have a tlb_flush_mask(env, n, mask) | |
108 | * in QEMU, we just invalidate all TLBs | |
109 | */ | |
110 | do_invalidate = 1; | |
111 | } | |
112 | } | |
113 | if (do_invalidate) { | |
114 | tlb_flush(env, 1); | |
115 | } | |
116 | } | |
117 | ||
118 | void helper_slbie(CPUPPCState *env, target_ulong addr) | |
119 | { | |
120 | ppc_slb_t *slb; | |
121 | ||
122 | slb = slb_lookup(env, addr); | |
123 | if (!slb) { | |
124 | return; | |
125 | } | |
126 | ||
127 | if (slb->esid & SLB_ESID_V) { | |
128 | slb->esid &= ~SLB_ESID_V; | |
129 | ||
130 | /* XXX: given the fact that segment size is 256 MB or 1TB, | |
131 | * and we still don't have a tlb_flush_mask(env, n, mask) | |
132 | * in QEMU, we just invalidate all TLBs | |
133 | */ | |
134 | tlb_flush(env, 1); | |
135 | } | |
136 | } | |
137 | ||
138 | int ppc_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) | |
139 | { | |
140 | int slot = rb & 0xfff; | |
141 | ppc_slb_t *slb = &env->slb[slot]; | |
142 | ||
143 | if (rb & (0x1000 - env->slb_nr)) { | |
144 | return -1; /* Reserved bits set or slot too high */ | |
145 | } | |
146 | if (rs & (SLB_VSID_B & ~SLB_VSID_B_1T)) { | |
147 | return -1; /* Bad segment size */ | |
148 | } | |
149 | if ((rs & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) { | |
150 | return -1; /* 1T segment on MMU that doesn't support it */ | |
151 | } | |
152 | ||
153 | /* Mask out the slot number as we store the entry */ | |
154 | slb->esid = rb & (SLB_ESID_ESID | SLB_ESID_V); | |
155 | slb->vsid = rs; | |
156 | ||
157 | LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64 | |
158 | " %016" PRIx64 "\n", __func__, slot, rb, rs, | |
159 | slb->esid, slb->vsid); | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
164 | static int ppc_load_slb_esid(CPUPPCState *env, target_ulong rb, | |
165 | target_ulong *rt) | |
166 | { | |
167 | int slot = rb & 0xfff; | |
168 | ppc_slb_t *slb = &env->slb[slot]; | |
169 | ||
170 | if (slot >= env->slb_nr) { | |
171 | return -1; | |
172 | } | |
173 | ||
174 | *rt = slb->esid; | |
175 | return 0; | |
176 | } | |
177 | ||
178 | static int ppc_load_slb_vsid(CPUPPCState *env, target_ulong rb, | |
179 | target_ulong *rt) | |
180 | { | |
181 | int slot = rb & 0xfff; | |
182 | ppc_slb_t *slb = &env->slb[slot]; | |
183 | ||
184 | if (slot >= env->slb_nr) { | |
185 | return -1; | |
186 | } | |
187 | ||
188 | *rt = slb->vsid; | |
189 | return 0; | |
190 | } | |
191 | ||
192 | void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) | |
193 | { | |
194 | if (ppc_store_slb(env, rb, rs) < 0) { | |
195 | helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, | |
196 | POWERPC_EXCP_INVAL); | |
197 | } | |
198 | } | |
199 | ||
200 | target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb) | |
201 | { | |
202 | target_ulong rt = 0; | |
203 | ||
204 | if (ppc_load_slb_esid(env, rb, &rt) < 0) { | |
205 | helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, | |
206 | POWERPC_EXCP_INVAL); | |
207 | } | |
208 | return rt; | |
209 | } | |
210 | ||
211 | target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) | |
212 | { | |
213 | target_ulong rt = 0; | |
214 | ||
215 | if (ppc_load_slb_vsid(env, rb, &rt) < 0) { | |
216 | helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, | |
217 | POWERPC_EXCP_INVAL); | |
218 | } | |
219 | return rt; | |
220 | } | |
221 | ||
222 | /* | |
223 | * 64-bit hash table MMU handling | |
224 | */ | |
225 | ||
226 | static int ppc_hash64_pte_prot(CPUPPCState *env, | |
227 | ppc_slb_t *slb, ppc_hash_pte64_t pte) | |
228 | { | |
229 | unsigned pp, key; | |
230 | /* Some pp bit combinations have undefined behaviour, so default | |
231 | * to no access in those cases */ | |
232 | int prot = 0; | |
233 | ||
234 | key = !!(msr_pr ? (slb->vsid & SLB_VSID_KP) | |
235 | : (slb->vsid & SLB_VSID_KS)); | |
236 | pp = (pte.pte1 & HPTE64_R_PP) | ((pte.pte1 & HPTE64_R_PP0) >> 61); | |
237 | ||
238 | if (key == 0) { | |
239 | switch (pp) { | |
240 | case 0x0: | |
241 | case 0x1: | |
242 | case 0x2: | |
243 | prot = PAGE_READ | PAGE_WRITE; | |
244 | break; | |
245 | ||
246 | case 0x3: | |
247 | case 0x6: | |
248 | prot = PAGE_READ; | |
249 | break; | |
250 | } | |
251 | } else { | |
252 | switch (pp) { | |
253 | case 0x0: | |
254 | case 0x6: | |
255 | prot = 0; | |
256 | break; | |
257 | ||
258 | case 0x1: | |
259 | case 0x3: | |
260 | prot = PAGE_READ; | |
261 | break; | |
262 | ||
263 | case 0x2: | |
264 | prot = PAGE_READ | PAGE_WRITE; | |
265 | break; | |
266 | } | |
267 | } | |
268 | ||
269 | /* No execute if either noexec or guarded bits set */ | |
270 | if (!(pte.pte1 & HPTE64_R_N) || (pte.pte1 & HPTE64_R_G) | |
271 | || (slb->vsid & SLB_VSID_N)) { | |
272 | prot |= PAGE_EXEC; | |
273 | } | |
274 | ||
275 | return prot; | |
276 | } | |
277 | ||
278 | static int ppc_hash64_amr_prot(CPUPPCState *env, ppc_hash_pte64_t pte) | |
279 | { | |
280 | int key, amrbits; | |
281 | int prot = PAGE_EXEC; | |
282 | ||
283 | ||
284 | /* Only recent MMUs implement Virtual Page Class Key Protection */ | |
285 | if (!(env->mmu_model & POWERPC_MMU_AMR)) { | |
286 | return PAGE_READ | PAGE_WRITE | PAGE_EXEC; | |
287 | } | |
288 | ||
289 | key = HPTE64_R_KEY(pte.pte1); | |
290 | amrbits = (env->spr[SPR_AMR] >> 2*(31 - key)) & 0x3; | |
291 | ||
292 | /* fprintf(stderr, "AMR protection: key=%d AMR=0x%" PRIx64 "\n", key, */ | |
293 | /* env->spr[SPR_AMR]); */ | |
294 | ||
295 | if (amrbits & 0x2) { | |
296 | prot |= PAGE_WRITE; | |
297 | } | |
298 | if (amrbits & 0x1) { | |
299 | prot |= PAGE_READ; | |
300 | } | |
301 | ||
302 | return prot; | |
303 | } | |
304 | ||
305 | static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr pteg_off, | |
306 | bool secondary, target_ulong ptem, | |
307 | ppc_hash_pte64_t *pte) | |
308 | { | |
309 | hwaddr pte_offset = pteg_off; | |
310 | target_ulong pte0, pte1; | |
311 | int i; | |
312 | ||
313 | for (i = 0; i < HPTES_PER_GROUP; i++) { | |
314 | pte0 = ppc_hash64_load_hpte0(env, pte_offset); | |
315 | pte1 = ppc_hash64_load_hpte1(env, pte_offset); | |
316 | ||
317 | if ((pte0 & HPTE64_V_VALID) | |
318 | && (secondary == !!(pte0 & HPTE64_V_SECONDARY)) | |
319 | && HPTE64_V_COMPARE(pte0, ptem)) { | |
320 | pte->pte0 = pte0; | |
321 | pte->pte1 = pte1; | |
322 | return pte_offset; | |
323 | } | |
324 | ||
325 | pte_offset += HASH_PTE_SIZE_64; | |
326 | } | |
327 | ||
328 | return -1; | |
329 | } | |
330 | ||
331 | static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, | |
332 | ppc_slb_t *slb, target_ulong eaddr, | |
333 | ppc_hash_pte64_t *pte) | |
334 | { | |
335 | hwaddr pteg_off, pte_offset; | |
336 | hwaddr hash; | |
337 | uint64_t vsid, epnshift, epnmask, epn, ptem; | |
338 | ||
339 | /* Page size according to the SLB, which we use to generate the | |
340 | * EPN for hash table lookup.. When we implement more recent MMU | |
341 | * extensions this might be different from the actual page size | |
342 | * encoded in the PTE */ | |
343 | epnshift = (slb->vsid & SLB_VSID_L) | |
344 | ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; | |
345 | epnmask = ~((1ULL << epnshift) - 1); | |
346 | ||
347 | if (slb->vsid & SLB_VSID_B) { | |
348 | /* 1TB segment */ | |
349 | vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T; | |
350 | epn = (eaddr & ~SEGMENT_MASK_1T) & epnmask; | |
351 | hash = vsid ^ (vsid << 25) ^ (epn >> epnshift); | |
352 | } else { | |
353 | /* 256M segment */ | |
354 | vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT; | |
355 | epn = (eaddr & ~SEGMENT_MASK_256M) & epnmask; | |
356 | hash = vsid ^ (epn >> epnshift); | |
357 | } | |
358 | ptem = (slb->vsid & SLB_VSID_PTEM) | ((epn >> 16) & HPTE64_V_AVPN); | |
359 | ||
360 | /* Page address translation */ | |
361 | LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx | |
362 | " hash " TARGET_FMT_plx "\n", | |
363 | env->htab_base, env->htab_mask, hash); | |
364 | ||
365 | /* Primary PTEG lookup */ | |
366 | LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx | |
367 | " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx | |
368 | " hash=" TARGET_FMT_plx "\n", | |
369 | env->htab_base, env->htab_mask, vsid, ptem, hash); | |
370 | pteg_off = (hash * HASH_PTEG_SIZE_64) & env->htab_mask; | |
371 | pte_offset = ppc_hash64_pteg_search(env, pteg_off, 0, ptem, pte); | |
372 | ||
373 | if (pte_offset == -1) { | |
374 | /* Secondary PTEG lookup */ | |
375 | LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx | |
376 | " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx | |
377 | " hash=" TARGET_FMT_plx "\n", env->htab_base, | |
378 | env->htab_mask, vsid, ptem, ~hash); | |
379 | ||
380 | pteg_off = (~hash * HASH_PTEG_SIZE_64) & env->htab_mask; | |
381 | pte_offset = ppc_hash64_pteg_search(env, pteg_off, 1, ptem, pte); | |
382 | } | |
383 | ||
384 | return pte_offset; | |
385 | } | |
386 | ||
387 | static hwaddr ppc_hash64_pte_raddr(ppc_slb_t *slb, ppc_hash_pte64_t pte, | |
388 | target_ulong eaddr) | |
389 | { | |
390 | hwaddr rpn = pte.pte1 & HPTE64_R_RPN; | |
391 | /* FIXME: Add support for SLLP extended page sizes */ | |
392 | int target_page_bits = (slb->vsid & SLB_VSID_L) | |
393 | ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; | |
394 | hwaddr mask = (1ULL << target_page_bits) - 1; | |
395 | ||
396 | return (rpn & ~mask) | (eaddr & mask); | |
397 | } | |
398 | ||
399 | int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr, | |
400 | int rwx, int mmu_idx) | |
401 | { | |
402 | ppc_slb_t *slb; | |
403 | hwaddr pte_offset; | |
404 | ppc_hash_pte64_t pte; | |
405 | int pp_prot, amr_prot, prot; | |
406 | uint64_t new_pte1; | |
407 | const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; | |
408 | hwaddr raddr; | |
409 | ||
410 | assert((rwx == 0) || (rwx == 1) || (rwx == 2)); | |
411 | ||
412 | /* 1. Handle real mode accesses */ | |
413 | if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { | |
414 | /* Translation is off */ | |
415 | /* In real mode the top 4 effective address bits are ignored */ | |
416 | raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; | |
417 | tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, | |
418 | PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, | |
419 | TARGET_PAGE_SIZE); | |
420 | return 0; | |
421 | } | |
422 | ||
423 | /* 2. Translation is on, so look up the SLB */ | |
424 | slb = slb_lookup(env, eaddr); | |
425 | ||
426 | if (!slb) { | |
427 | if (rwx == 2) { | |
428 | env->exception_index = POWERPC_EXCP_ISEG; | |
429 | env->error_code = 0; | |
430 | } else { | |
431 | env->exception_index = POWERPC_EXCP_DSEG; | |
432 | env->error_code = 0; | |
433 | env->spr[SPR_DAR] = eaddr; | |
434 | } | |
435 | return 1; | |
436 | } | |
437 | ||
438 | /* 3. Check for segment level no-execute violation */ | |
439 | if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) { | |
440 | env->exception_index = POWERPC_EXCP_ISI; | |
441 | env->error_code = 0x10000000; | |
442 | return 1; | |
443 | } | |
444 | ||
445 | /* 4. Locate the PTE in the hash table */ | |
446 | pte_offset = ppc_hash64_htab_lookup(env, slb, eaddr, &pte); | |
447 | if (pte_offset == -1) { | |
448 | if (rwx == 2) { | |
449 | env->exception_index = POWERPC_EXCP_ISI; | |
450 | env->error_code = 0x40000000; | |
451 | } else { | |
452 | env->exception_index = POWERPC_EXCP_DSI; | |
453 | env->error_code = 0; | |
454 | env->spr[SPR_DAR] = eaddr; | |
455 | if (rwx == 1) { | |
456 | env->spr[SPR_DSISR] = 0x42000000; | |
457 | } else { | |
458 | env->spr[SPR_DSISR] = 0x40000000; | |
459 | } | |
460 | } | |
461 | return 1; | |
462 | } | |
463 | LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); | |
464 | ||
465 | /* 5. Check access permissions */ | |
466 | ||
467 | pp_prot = ppc_hash64_pte_prot(env, slb, pte); | |
468 | amr_prot = ppc_hash64_amr_prot(env, pte); | |
469 | prot = pp_prot & amr_prot; | |
470 | ||
471 | if ((need_prot[rwx] & ~prot) != 0) { | |
472 | /* Access right violation */ | |
473 | LOG_MMU("PTE access rejected\n"); | |
474 | if (rwx == 2) { | |
475 | env->exception_index = POWERPC_EXCP_ISI; | |
476 | env->error_code = 0x08000000; | |
477 | } else { | |
478 | target_ulong dsisr = 0; | |
479 | ||
480 | env->exception_index = POWERPC_EXCP_DSI; | |
481 | env->error_code = 0; | |
482 | env->spr[SPR_DAR] = eaddr; | |
483 | if (need_prot[rwx] & ~pp_prot) { | |
484 | dsisr |= 0x08000000; | |
485 | } | |
486 | if (rwx == 1) { | |
487 | dsisr |= 0x02000000; | |
488 | } | |
489 | if (need_prot[rwx] & ~amr_prot) { | |
490 | dsisr |= 0x00200000; | |
491 | } | |
492 | env->spr[SPR_DSISR] = dsisr; | |
493 | } | |
494 | return 1; | |
495 | } | |
496 | ||
497 | LOG_MMU("PTE access granted !\n"); | |
498 | ||
499 | /* 6. Update PTE referenced and changed bits if necessary */ | |
500 | ||
501 | new_pte1 = pte.pte1 | HPTE64_R_R; /* set referenced bit */ | |
502 | if (rwx == 1) { | |
503 | new_pte1 |= HPTE64_R_C; /* set changed (dirty) bit */ | |
504 | } else { | |
505 | /* Treat the page as read-only for now, so that a later write | |
506 | * will pass through this function again to set the C bit */ | |
507 | prot &= ~PAGE_WRITE; | |
508 | } | |
509 | ||
510 | if (new_pte1 != pte.pte1) { | |
511 | ppc_hash64_store_hpte1(env, pte_offset, new_pte1); | |
512 | } | |
513 | ||
514 | /* 7. Determine the real address from the PTE */ | |
515 | ||
516 | raddr = ppc_hash64_pte_raddr(slb, pte, eaddr); | |
517 | ||
518 | tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, | |
519 | prot, mmu_idx, TARGET_PAGE_SIZE); | |
520 | ||
521 | return 0; | |
522 | } | |
523 | ||
524 | hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr) | |
525 | { | |
526 | ppc_slb_t *slb; | |
527 | hwaddr pte_offset; | |
528 | ppc_hash_pte64_t pte; | |
529 | ||
530 | if (msr_dr == 0) { | |
531 | /* In real mode the top 4 effective address bits are ignored */ | |
532 | return addr & 0x0FFFFFFFFFFFFFFFULL; | |
533 | } | |
534 | ||
535 | slb = slb_lookup(env, addr); | |
536 | if (!slb) { | |
537 | return -1; | |
538 | } | |
539 | ||
540 | pte_offset = ppc_hash64_htab_lookup(env, slb, addr, &pte); | |
541 | if (pte_offset == -1) { | |
542 | return -1; | |
543 | } | |
544 | ||
545 | return ppc_hash64_pte_raddr(slb, pte, addr) & TARGET_PAGE_MASK; | |
546 | } |