qemu/target/ppc/mmu-booke.c
<<
>>
Prefs
   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 */
  28static 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 */
  56int 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
  71int 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:
 114check_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
 134static 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
 157static 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
 188static 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
 210hwaddr 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 */
 220int 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
 261static bool is_epid_mmu(int mmu_idx)
 262{
 263    return mmu_idx == PPC_TLB_EPID_STORE || mmu_idx == PPC_TLB_EPID_LOAD;
 264}
 265
 266static 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 */
 286static 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 */
 309static 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
 345found_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
 390static 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
 413found_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
 422static 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
 476bool 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