xref: /openbmc/qemu/target/ppc/mmu-booke.c (revision e7baac64)
1 /*
2  *  PowerPC BookE MMU, TLB emulation helpers for QEMU.
3  *
4  *  Copyright (c) 2003-2007 Jocelyn Mayer
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.1 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, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "qemu/osdep.h"
21 #include "exec/page-protection.h"
22 #include "exec/log.h"
23 #include "cpu.h"
24 #include "internal.h"
25 #include "mmu-booke.h"
26 
27 /* Generic TLB check function for embedded PowerPC implementations */
ppcemb_tlb_check(CPUPPCState * env,ppcemb_tlb_t * tlb,hwaddr * raddrp,target_ulong address,uint32_t pid,int i)28 static bool ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb,
29                              hwaddr *raddrp,
30                              target_ulong address, uint32_t pid, int i)
31 {
32     target_ulong mask;
33 
34     /* Check valid flag */
35     if (!(tlb->prot & PAGE_VALID)) {
36         return false;
37     }
38     mask = ~(tlb->size - 1);
39     qemu_log_mask(CPU_LOG_MMU, "%s: TLB %d address " TARGET_FMT_lx
40                   " PID %u <=> " TARGET_FMT_lx " " TARGET_FMT_lx " %u %x\n",
41                   __func__, i, address, pid, tlb->EPN,
42                   mask, (uint32_t)tlb->PID, tlb->prot);
43     /* Check PID */
44     if (tlb->PID != 0 && tlb->PID != pid) {
45         return false;
46     }
47     /* Check effective address */
48     if ((address & mask) != tlb->EPN) {
49         return false;
50     }
51     *raddrp = (tlb->RPN & mask) | (address & ~mask);
52     return true;
53 }
54 
55 /* Generic TLB search function for PowerPC embedded implementations */
ppcemb_tlb_search(CPUPPCState * env,target_ulong address,uint32_t pid)56 int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid)
57 {
58     ppcemb_tlb_t *tlb;
59     hwaddr raddr;
60     int i;
61 
62     for (i = 0; i < env->nb_tlb; i++) {
63         tlb = &env->tlb.tlbe[i];
64         if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, i)) {
65             return i;
66         }
67     }
68     return -1;
69 }
70 
mmu40x_get_physical_address(CPUPPCState * env,hwaddr * raddr,int * prot,target_ulong address,MMUAccessType access_type)71 int mmu40x_get_physical_address(CPUPPCState *env, hwaddr *raddr, int *prot,
72                                 target_ulong address,
73                                 MMUAccessType access_type)
74 {
75     ppcemb_tlb_t *tlb;
76     int i, ret, zsel, zpr, pr;
77 
78     ret = -1;
79     pr = FIELD_EX64(env->msr, MSR, PR);
80     for (i = 0; i < env->nb_tlb; i++) {
81         tlb = &env->tlb.tlbe[i];
82         if (!ppcemb_tlb_check(env, tlb, raddr, address,
83                               env->spr[SPR_40x_PID], i)) {
84             continue;
85         }
86         zsel = (tlb->attr >> 4) & 0xF;
87         zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3;
88         qemu_log_mask(CPU_LOG_MMU,
89                       "%s: TLB %d zsel %d zpr %d ty %d attr %08x\n",
90                       __func__, i, zsel, zpr, access_type, tlb->attr);
91         /* Check execute enable bit */
92         switch (zpr) {
93         case 0x2:
94             if (pr != 0) {
95                 goto check_perms;
96             }
97             /* fall through */
98         case 0x3:
99             /* All accesses granted */
100             *prot = PAGE_RWX;
101             ret = 0;
102             break;
103 
104         case 0x0:
105             if (pr != 0) {
106                 /* Raise Zone protection fault.  */
107                 env->spr[SPR_40x_ESR] = 1 << 22;
108                 *prot = 0;
109                 ret = -2;
110                 break;
111             }
112             /* fall through */
113         case 0x1:
114 check_perms:
115             /* Check from TLB entry */
116             *prot = tlb->prot;
117             if (check_prot_access_type(*prot, access_type)) {
118                 ret = 0;
119             } else {
120                 env->spr[SPR_40x_ESR] = 0;
121                 ret = -2;
122             }
123             break;
124         }
125     }
126     qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => "
127                   HWADDR_FMT_plx " %d %d\n",  __func__,
128                   ret < 0 ? "refused" : "granted", address,
129                   ret < 0 ? 0 : *raddr, *prot, ret);
130 
131     return ret;
132 }
133 
mmubooke_check_pid(CPUPPCState * env,ppcemb_tlb_t * tlb,hwaddr * raddr,target_ulong addr,int i)134 static bool mmubooke_check_pid(CPUPPCState *env, ppcemb_tlb_t *tlb,
135                                hwaddr *raddr, target_ulong addr, int i)
136 {
137     if (ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID], i)) {
138         if (!env->nb_pids) {
139             /* Extend the physical address to 36 bits */
140             *raddr |= (uint64_t)(tlb->RPN & 0xF) << 32;
141         }
142         return true;
143     } else if (!env->nb_pids) {
144         return false;
145     }
146     if (env->spr[SPR_BOOKE_PID1] &&
147         ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID1], i)) {
148         return true;
149     }
150     if (env->spr[SPR_BOOKE_PID2] &&
151         ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID2], i)) {
152         return true;
153     }
154     return false;
155 }
156 
mmubooke_check_tlb(CPUPPCState * env,ppcemb_tlb_t * tlb,hwaddr * raddr,int * prot,target_ulong address,MMUAccessType access_type,int i)157 static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb,
158                               hwaddr *raddr, int *prot, target_ulong address,
159                               MMUAccessType access_type, int i)
160 {
161     if (!mmubooke_check_pid(env, tlb, raddr, address, i)) {
162         qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__);
163         return -1;
164     }
165 
166     /* Check the address space */
167     if ((access_type == MMU_INST_FETCH ?
168         FIELD_EX64(env->msr, MSR, IR) :
169         FIELD_EX64(env->msr, MSR, DR)) != (tlb->attr & 1)) {
170         qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__);
171         return -1;
172     }
173 
174     if (FIELD_EX64(env->msr, MSR, PR)) {
175         *prot = tlb->prot & 0xF;
176     } else {
177         *prot = (tlb->prot >> 4) & 0xF;
178     }
179     if (check_prot_access_type(*prot, access_type)) {
180         qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__);
181         return 0;
182     }
183 
184     qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot);
185     return access_type == MMU_INST_FETCH ? -3 : -2;
186 }
187 
mmubooke_get_physical_address(CPUPPCState * env,hwaddr * raddr,int * prot,target_ulong address,MMUAccessType access_type)188 static int mmubooke_get_physical_address(CPUPPCState *env, hwaddr *raddr,
189                                          int *prot, target_ulong address,
190                                          MMUAccessType access_type)
191 {
192     ppcemb_tlb_t *tlb;
193     int i, ret = -1;
194 
195     for (i = 0; i < env->nb_tlb; i++) {
196         tlb = &env->tlb.tlbe[i];
197         ret = mmubooke_check_tlb(env, tlb, raddr, prot, address,
198                                  access_type, i);
199         if (ret != -1) {
200             break;
201         }
202     }
203     qemu_log_mask(CPU_LOG_MMU,
204                   "%s: access %s " TARGET_FMT_lx " => " HWADDR_FMT_plx
205                   " %d %d\n", __func__, ret < 0 ? "refused" : "granted",
206                   address, ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret);
207     return ret;
208 }
209 
booke206_tlb_to_page_size(CPUPPCState * env,ppcmas_tlb_t * tlb)210 hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb)
211 {
212     int tlbm_size;
213 
214     tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT;
215 
216     return 1024ULL << tlbm_size;
217 }
218 
219 /* TLB check function for MAS based SoftTLBs */
ppcmas_tlb_check(CPUPPCState * env,ppcmas_tlb_t * tlb,hwaddr * raddrp,target_ulong address,uint32_t pid)220 int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp,
221                      target_ulong address, uint32_t pid)
222 {
223     hwaddr mask;
224     uint32_t tlb_pid;
225 
226     if (!FIELD_EX64(env->msr, MSR, CM)) {
227         /* In 32bit mode we can only address 32bit EAs */
228         address = (uint32_t)address;
229     }
230 
231     /* Check valid flag */
232     if (!(tlb->mas1 & MAS1_VALID)) {
233         return -1;
234     }
235 
236     mask = ~(booke206_tlb_to_page_size(env, tlb) - 1);
237     qemu_log_mask(CPU_LOG_MMU, "%s: TLB ADDR=0x" TARGET_FMT_lx
238                   " PID=0x%x MAS1=0x%x MAS2=0x%" PRIx64 " mask=0x%"
239                   HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%" PRIx32 "\n",
240                   __func__, address, pid, tlb->mas1, tlb->mas2, mask,
241                   tlb->mas7_3, tlb->mas8);
242 
243     /* Check PID */
244     tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT;
245     if (tlb_pid != 0 && tlb_pid != pid) {
246         return -1;
247     }
248 
249     /* Check effective address */
250     if ((address & mask) != (tlb->mas2 & MAS2_EPN_MASK)) {
251         return -1;
252     }
253 
254     if (raddrp) {
255         *raddrp = (tlb->mas7_3 & mask) | (address & ~mask);
256     }
257 
258     return 0;
259 }
260 
is_epid_mmu(int mmu_idx)261 static bool is_epid_mmu(int mmu_idx)
262 {
263     return mmu_idx == PPC_TLB_EPID_STORE || mmu_idx == PPC_TLB_EPID_LOAD;
264 }
265 
mmubooke206_esr(int mmu_idx,MMUAccessType access_type)266 static uint32_t mmubooke206_esr(int mmu_idx, MMUAccessType access_type)
267 {
268     uint32_t esr = 0;
269     if (access_type == MMU_DATA_STORE) {
270         esr |= ESR_ST;
271     }
272     if (is_epid_mmu(mmu_idx)) {
273         esr |= ESR_EPID;
274     }
275     return esr;
276 }
277 
278 /*
279  * Get EPID register given the mmu_idx. If this is regular load,
280  * construct the EPID access bits from current processor state
281  *
282  * Get the effective AS and PR bits and the PID. The PID is returned
283  * only if EPID load is requested, otherwise the caller must detect
284  * the correct EPID.  Return true if valid EPID is returned.
285  */
mmubooke206_get_as(CPUPPCState * env,int mmu_idx,uint32_t * epid_out,bool * as_out,bool * pr_out)286 static bool mmubooke206_get_as(CPUPPCState *env,
287                                int mmu_idx, uint32_t *epid_out,
288                                bool *as_out, bool *pr_out)
289 {
290     if (is_epid_mmu(mmu_idx)) {
291         uint32_t epidr;
292         if (mmu_idx == PPC_TLB_EPID_STORE) {
293             epidr = env->spr[SPR_BOOKE_EPSC];
294         } else {
295             epidr = env->spr[SPR_BOOKE_EPLC];
296         }
297         *epid_out = (epidr & EPID_EPID) >> EPID_EPID_SHIFT;
298         *as_out = !!(epidr & EPID_EAS);
299         *pr_out = !!(epidr & EPID_EPR);
300         return true;
301     } else {
302         *as_out = FIELD_EX64(env->msr, MSR, DS);
303         *pr_out = FIELD_EX64(env->msr, MSR, PR);
304         return false;
305     }
306 }
307 
308 /* Check if the tlb found by hashing really matches */
mmubooke206_check_tlb(CPUPPCState * env,ppcmas_tlb_t * tlb,hwaddr * raddr,int * prot,target_ulong address,MMUAccessType access_type,int mmu_idx)309 static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb,
310                                  hwaddr *raddr, int *prot,
311                                  target_ulong address,
312                                  MMUAccessType access_type, int mmu_idx)
313 {
314     uint32_t epid;
315     bool as, pr;
316     bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr);
317 
318     if (!use_epid) {
319         if (ppcmas_tlb_check(env, tlb, raddr, address,
320                              env->spr[SPR_BOOKE_PID]) >= 0) {
321             goto found_tlb;
322         }
323 
324         if (env->spr[SPR_BOOKE_PID1] &&
325             ppcmas_tlb_check(env, tlb, raddr, address,
326                              env->spr[SPR_BOOKE_PID1]) >= 0) {
327             goto found_tlb;
328         }
329 
330         if (env->spr[SPR_BOOKE_PID2] &&
331             ppcmas_tlb_check(env, tlb, raddr, address,
332                              env->spr[SPR_BOOKE_PID2]) >= 0) {
333             goto found_tlb;
334         }
335     } else {
336         if (ppcmas_tlb_check(env, tlb, raddr, address, epid) >= 0) {
337             goto found_tlb;
338         }
339     }
340 
341     qemu_log_mask(CPU_LOG_MMU, "%s: No TLB entry found for effective address "
342                   "0x" TARGET_FMT_lx "\n", __func__, address);
343     return -1;
344 
345 found_tlb:
346 
347     /* Check the address space and permissions */
348     if (access_type == MMU_INST_FETCH) {
349         /* There is no way to fetch code using epid load */
350         assert(!use_epid);
351         as = FIELD_EX64(env->msr, MSR, IR);
352     }
353 
354     if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) {
355         qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__);
356         return -1;
357     }
358 
359     *prot = 0;
360     if (pr) {
361         if (tlb->mas7_3 & MAS3_UR) {
362             *prot |= PAGE_READ;
363         }
364         if (tlb->mas7_3 & MAS3_UW) {
365             *prot |= PAGE_WRITE;
366         }
367         if (tlb->mas7_3 & MAS3_UX) {
368             *prot |= PAGE_EXEC;
369         }
370     } else {
371         if (tlb->mas7_3 & MAS3_SR) {
372             *prot |= PAGE_READ;
373         }
374         if (tlb->mas7_3 & MAS3_SW) {
375             *prot |= PAGE_WRITE;
376         }
377         if (tlb->mas7_3 & MAS3_SX) {
378             *prot |= PAGE_EXEC;
379         }
380     }
381     if (check_prot_access_type(*prot, access_type)) {
382         qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__);
383         return 0;
384     }
385 
386     qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot);
387     return access_type == MMU_INST_FETCH ? -3 : -2;
388 }
389 
mmubooke206_get_physical_address(CPUPPCState * env,hwaddr * raddr,int * prot,target_ulong address,MMUAccessType access_type,int mmu_idx)390 static int mmubooke206_get_physical_address(CPUPPCState *env, hwaddr *raddr,
391                                             int *prot, target_ulong address,
392                                             MMUAccessType access_type,
393                                             int mmu_idx)
394 {
395     ppcmas_tlb_t *tlb;
396     int i, j, ret = -1;
397 
398     for (i = 0; i < BOOKE206_MAX_TLBN; i++) {
399         int ways = booke206_tlb_ways(env, i);
400         for (j = 0; j < ways; j++) {
401             tlb = booke206_get_tlbm(env, i, address, j);
402             if (!tlb) {
403                 continue;
404             }
405             ret = mmubooke206_check_tlb(env, tlb, raddr, prot, address,
406                                         access_type, mmu_idx);
407             if (ret != -1) {
408                 goto found_tlb;
409             }
410         }
411     }
412 
413 found_tlb:
414 
415     qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => "
416                   HWADDR_FMT_plx " %d %d\n", __func__,
417                   ret < 0 ? "refused" : "granted", address,
418                   ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret);
419     return ret;
420 }
421 
booke206_update_mas_tlb_miss(CPUPPCState * env,target_ulong address,MMUAccessType access_type,int mmu_idx)422 static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address,
423                                          MMUAccessType access_type, int mmu_idx)
424 {
425     uint32_t epid;
426     bool as, pr;
427     uint32_t missed_tid = 0;
428     bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr);
429 
430     if (access_type == MMU_INST_FETCH) {
431         as = FIELD_EX64(env->msr, MSR, IR);
432     }
433     env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK;
434     env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK;
435     env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK;
436     env->spr[SPR_BOOKE_MAS3] = 0;
437     env->spr[SPR_BOOKE_MAS6] = 0;
438     env->spr[SPR_BOOKE_MAS7] = 0;
439 
440     /* AS */
441     if (as) {
442         env->spr[SPR_BOOKE_MAS1] |= MAS1_TS;
443         env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS;
444     }
445 
446     env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID;
447     env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK;
448 
449     if (!use_epid) {
450         switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) {
451         case MAS4_TIDSELD_PID0:
452             missed_tid = env->spr[SPR_BOOKE_PID];
453             break;
454         case MAS4_TIDSELD_PID1:
455             missed_tid = env->spr[SPR_BOOKE_PID1];
456             break;
457         case MAS4_TIDSELD_PID2:
458             missed_tid = env->spr[SPR_BOOKE_PID2];
459             break;
460         }
461         env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16;
462     } else {
463         missed_tid = epid;
464         env->spr[SPR_BOOKE_MAS6] |= missed_tid << 16;
465     }
466     env->spr[SPR_BOOKE_MAS1] |= (missed_tid << MAS1_TID_SHIFT);
467 
468 
469     /* next victim logic */
470     env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT;
471     env->last_way++;
472     env->last_way &= booke206_tlb_ways(env, 0) - 1;
473     env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT;
474 }
475 
ppc_booke_xlate(PowerPCCPU * cpu,vaddr eaddr,MMUAccessType access_type,hwaddr * raddrp,int * psizep,int * protp,int mmu_idx,bool guest_visible)476 bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type,
477                      hwaddr *raddrp, int *psizep, int *protp, int mmu_idx,
478                      bool guest_visible)
479 {
480     CPUState *cs = CPU(cpu);
481     CPUPPCState *env = &cpu->env;
482     hwaddr raddr;
483     int prot, ret;
484 
485     if (env->mmu_model == POWERPC_MMU_BOOKE206) {
486         ret = mmubooke206_get_physical_address(env, &raddr, &prot, eaddr,
487                                                access_type, mmu_idx);
488     } else {
489         ret = mmubooke_get_physical_address(env, &raddr, &prot, eaddr,
490                                             access_type);
491     }
492     if (ret == 0) {
493         *raddrp = raddr;
494         *protp = prot;
495         *psizep = TARGET_PAGE_BITS;
496         return true;
497     } else if (!guest_visible) {
498         return false;
499     }
500 
501     log_cpu_state_mask(CPU_LOG_MMU, cs, 0);
502     env->error_code = 0;
503     switch (ret) {
504     case -1:
505         /* No matches in page tables or TLB */
506         if (env->mmu_model == POWERPC_MMU_BOOKE206) {
507             booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx);
508         }
509         cs->exception_index = (access_type == MMU_INST_FETCH) ?
510                               POWERPC_EXCP_ITLB : POWERPC_EXCP_DTLB;
511         env->spr[SPR_BOOKE_DEAR] = eaddr;
512         env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type);
513         break;
514     case -2:
515         /* Access rights violation */
516         cs->exception_index = (access_type == MMU_INST_FETCH) ?
517                               POWERPC_EXCP_ISI : POWERPC_EXCP_DSI;
518         if (access_type != MMU_INST_FETCH) {
519             env->spr[SPR_BOOKE_DEAR] = eaddr;
520             env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type);
521         }
522         break;
523     case -3:
524         /* No execute protection violation */
525         cs->exception_index = POWERPC_EXCP_ISI;
526         env->spr[SPR_BOOKE_ESR] = 0;
527         break;
528     }
529 
530     return false;
531 }
532