qemu/target/ppc/mmu-radix64.c
<<
>>
Prefs
   1/*
   2 *  PowerPC Radix MMU mulation helpers for QEMU.
   3 *
   4 *  Copyright (c) 2016 Suraj Jitindar Singh, IBM Corporation
   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, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include "qemu/osdep.h"
  21#include "cpu.h"
  22#include "exec/exec-all.h"
  23#include "exec/helper-proto.h"
  24#include "qemu/error-report.h"
  25#include "sysemu/kvm.h"
  26#include "kvm_ppc.h"
  27#include "exec/log.h"
  28#include "mmu-radix64.h"
  29#include "mmu-book3s-v3.h"
  30
  31static bool ppc_radix64_get_fully_qualified_addr(CPUPPCState *env, vaddr eaddr,
  32                                                 uint64_t *lpid, uint64_t *pid)
  33{
  34    /* We don't have HV support yet and shouldn't get here with it set anyway */
  35    assert(!msr_hv);
  36
  37    if (!msr_hv) { /* !MSR[HV] -> Guest */
  38        switch (eaddr & R_EADDR_QUADRANT) {
  39        case R_EADDR_QUADRANT0: /* Guest application */
  40            *lpid = env->spr[SPR_LPIDR];
  41            *pid = env->spr[SPR_BOOKS_PID];
  42            break;
  43        case R_EADDR_QUADRANT1: /* Illegal */
  44        case R_EADDR_QUADRANT2:
  45            return false;
  46        case R_EADDR_QUADRANT3: /* Guest OS */
  47            *lpid = env->spr[SPR_LPIDR];
  48            *pid = 0; /* pid set to 0 -> addresses guest operating system */
  49            break;
  50        }
  51    }
  52
  53    return true;
  54}
  55
  56static void ppc_radix64_raise_segi(PowerPCCPU *cpu, int rwx, vaddr eaddr)
  57{
  58    CPUState *cs = CPU(cpu);
  59    CPUPPCState *env = &cpu->env;
  60
  61    if (rwx == 2) { /* Instruction Segment Interrupt */
  62        cs->exception_index = POWERPC_EXCP_ISEG;
  63    } else { /* Data Segment Interrupt */
  64        cs->exception_index = POWERPC_EXCP_DSEG;
  65        env->spr[SPR_DAR] = eaddr;
  66    }
  67    env->error_code = 0;
  68}
  69
  70static void ppc_radix64_raise_si(PowerPCCPU *cpu, int rwx, vaddr eaddr,
  71                                uint32_t cause)
  72{
  73    CPUState *cs = CPU(cpu);
  74    CPUPPCState *env = &cpu->env;
  75
  76    if (rwx == 2) { /* Instruction Storage Interrupt */
  77        cs->exception_index = POWERPC_EXCP_ISI;
  78        env->error_code = cause;
  79    } else { /* Data Storage Interrupt */
  80        cs->exception_index = POWERPC_EXCP_DSI;
  81        if (rwx == 1) { /* Write -> Store */
  82            cause |= DSISR_ISSTORE;
  83        }
  84        env->spr[SPR_DSISR] = cause;
  85        env->spr[SPR_DAR] = eaddr;
  86        env->error_code = 0;
  87    }
  88}
  89
  90
  91static bool ppc_radix64_check_prot(PowerPCCPU *cpu, int rwx, uint64_t pte,
  92                                   int *fault_cause, int *prot)
  93{
  94    CPUPPCState *env = &cpu->env;
  95    const int need_prot[] = { PAGE_READ, PAGE_WRITE, PAGE_EXEC };
  96
  97    /* Check Page Attributes (pte58:59) */
  98    if (((pte & R_PTE_ATT) == R_PTE_ATT_NI_IO) && (rwx == 2)) {
  99        /*
 100         * Radix PTE entries with the non-idempotent I/O attribute are treated
 101         * as guarded storage
 102         */
 103        *fault_cause |= SRR1_NOEXEC_GUARD;
 104        return true;
 105    }
 106
 107    /* Determine permissions allowed by Encoded Access Authority */
 108    if ((pte & R_PTE_EAA_PRIV) && msr_pr) { /* Insufficient Privilege */
 109        *prot = 0;
 110    } else if (msr_pr || (pte & R_PTE_EAA_PRIV)) {
 111        *prot = ppc_radix64_get_prot_eaa(pte);
 112    } else { /* !msr_pr && !(pte & R_PTE_EAA_PRIV) */
 113        *prot = ppc_radix64_get_prot_eaa(pte);
 114        *prot &= ppc_radix64_get_prot_amr(cpu); /* Least combined permissions */
 115    }
 116
 117    /* Check if requested access type is allowed */
 118    if (need_prot[rwx] & ~(*prot)) { /* Page Protected for that Access */
 119        *fault_cause |= DSISR_PROTFAULT;
 120        return true;
 121    }
 122
 123    return false;
 124}
 125
 126static void ppc_radix64_set_rc(PowerPCCPU *cpu, int rwx, uint64_t pte,
 127                               hwaddr pte_addr, int *prot)
 128{
 129    CPUState *cs = CPU(cpu);
 130    uint64_t npte;
 131
 132    npte = pte | R_PTE_R; /* Always set reference bit */
 133
 134    if (rwx == 1) { /* Store/Write */
 135        npte |= R_PTE_C; /* Set change bit */
 136    } else {
 137        /*
 138         * Treat the page as read-only for now, so that a later write
 139         * will pass through this function again to set the C bit.
 140         */
 141        *prot &= ~PAGE_WRITE;
 142    }
 143
 144    if (pte ^ npte) { /* If pte has changed then write it back */
 145        stq_phys(cs->as, pte_addr, npte);
 146    }
 147}
 148
 149static uint64_t ppc_radix64_walk_tree(PowerPCCPU *cpu, vaddr eaddr,
 150                                      uint64_t base_addr, uint64_t nls,
 151                                      hwaddr *raddr, int *psize,
 152                                      int *fault_cause, hwaddr *pte_addr)
 153{
 154    CPUState *cs = CPU(cpu);
 155    uint64_t index, pde;
 156
 157    if (nls < 5) { /* Directory maps less than 2**5 entries */
 158        *fault_cause |= DSISR_R_BADCONFIG;
 159        return 0;
 160    }
 161
 162    /* Read page <directory/table> entry from guest address space */
 163    index = eaddr >> (*psize - nls); /* Shift */
 164    index &= ((1UL << nls) - 1); /* Mask */
 165    pde = ldq_phys(cs->as, base_addr + (index * sizeof(pde)));
 166    if (!(pde & R_PTE_VALID)) { /* Invalid Entry */
 167        *fault_cause |= DSISR_NOPTE;
 168        return 0;
 169    }
 170
 171    *psize -= nls;
 172
 173    /* Check if Leaf Entry -> Page Table Entry -> Stop the Search */
 174    if (pde & R_PTE_LEAF) {
 175        uint64_t rpn = pde & R_PTE_RPN;
 176        uint64_t mask = (1UL << *psize) - 1;
 177
 178        /* Or high bits of rpn and low bits to ea to form whole real addr */
 179        *raddr = (rpn & ~mask) | (eaddr & mask);
 180        *pte_addr = base_addr + (index * sizeof(pde));
 181        return pde;
 182    }
 183
 184    /* Next Level of Radix Tree */
 185    return ppc_radix64_walk_tree(cpu, eaddr, pde & R_PDE_NLB, pde & R_PDE_NLS,
 186                                 raddr, psize, fault_cause, pte_addr);
 187}
 188
 189int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
 190                                 int mmu_idx)
 191{
 192    CPUState *cs = CPU(cpu);
 193    CPUPPCState *env = &cpu->env;
 194    PPCVirtualHypervisorClass *vhc =
 195        PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
 196    hwaddr raddr, pte_addr;
 197    uint64_t lpid = 0, pid = 0, offset, size, patbe, prtbe0, pte;
 198    int page_size, prot, fault_cause = 0;
 199
 200    assert((rwx == 0) || (rwx == 1) || (rwx == 2));
 201    assert(!msr_hv); /* For now there is no Radix PowerNV Support */
 202    assert(cpu->vhyp);
 203    assert(ppc64_use_proc_tbl(cpu));
 204
 205    /* Real Mode Access */
 206    if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) {
 207        /* In real mode top 4 effective addr bits (mostly) ignored */
 208        raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL;
 209
 210        tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
 211                     PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
 212                     TARGET_PAGE_SIZE);
 213        return 0;
 214    }
 215
 216    /* Virtual Mode Access - get the fully qualified address */
 217    if (!ppc_radix64_get_fully_qualified_addr(env, eaddr, &lpid, &pid)) {
 218        ppc_radix64_raise_segi(cpu, rwx, eaddr);
 219        return 1;
 220    }
 221
 222    /* Get Process Table */
 223    patbe = vhc->get_patbe(cpu->vhyp);
 224
 225    /* Index Process Table by PID to Find Corresponding Process Table Entry */
 226    offset = pid * sizeof(struct prtb_entry);
 227    size = 1ULL << ((patbe & PATBE1_R_PRTS) + 12);
 228    if (offset >= size) {
 229        /* offset exceeds size of the process table */
 230        ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_NOPTE);
 231        return 1;
 232    }
 233    prtbe0 = ldq_phys(cs->as, (patbe & PATBE1_R_PRTB) + offset);
 234
 235    /* Walk Radix Tree from Process Table Entry to Convert EA to RA */
 236    page_size = PRTBE_R_GET_RTS(prtbe0);
 237    pte = ppc_radix64_walk_tree(cpu, eaddr & R_EADDR_MASK,
 238                                prtbe0 & PRTBE_R_RPDB, prtbe0 & PRTBE_R_RPDS,
 239                                &raddr, &page_size, &fault_cause, &pte_addr);
 240    if (!pte || ppc_radix64_check_prot(cpu, rwx, pte, &fault_cause, &prot)) {
 241        /* Couldn't get pte or access denied due to protection */
 242        ppc_radix64_raise_si(cpu, rwx, eaddr, fault_cause);
 243        return 1;
 244    }
 245
 246    /* Update Reference and Change Bits */
 247    ppc_radix64_set_rc(cpu, rwx, pte, pte_addr, &prot);
 248
 249    tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
 250                 prot, mmu_idx, 1UL << page_size);
 251    return 0;
 252}
 253
 254hwaddr ppc_radix64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr)
 255{
 256    CPUState *cs = CPU(cpu);
 257    CPUPPCState *env = &cpu->env;
 258    PPCVirtualHypervisorClass *vhc =
 259        PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
 260    hwaddr raddr, pte_addr;
 261    uint64_t lpid = 0, pid = 0, offset, size, patbe, prtbe0, pte;
 262    int page_size, fault_cause = 0;
 263
 264    /* Handle Real Mode */
 265    if (msr_dr == 0) {
 266        /* In real mode top 4 effective addr bits (mostly) ignored */
 267        return eaddr & 0x0FFFFFFFFFFFFFFFULL;
 268    }
 269
 270    /* Virtual Mode Access - get the fully qualified address */
 271    if (!ppc_radix64_get_fully_qualified_addr(env, eaddr, &lpid, &pid)) {
 272        return -1;
 273    }
 274
 275    /* Get Process Table */
 276    patbe = vhc->get_patbe(cpu->vhyp);
 277
 278    /* Index Process Table by PID to Find Corresponding Process Table Entry */
 279    offset = pid * sizeof(struct prtb_entry);
 280    size = 1ULL << ((patbe & PATBE1_R_PRTS) + 12);
 281    if (offset >= size) {
 282        /* offset exceeds size of the process table */
 283        return -1;
 284    }
 285    prtbe0 = ldq_phys(cs->as, (patbe & PATBE1_R_PRTB) + offset);
 286
 287    /* Walk Radix Tree from Process Table Entry to Convert EA to RA */
 288    page_size = PRTBE_R_GET_RTS(prtbe0);
 289    pte = ppc_radix64_walk_tree(cpu, eaddr & R_EADDR_MASK,
 290                                prtbe0 & PRTBE_R_RPDB, prtbe0 & PRTBE_R_RPDS,
 291                                &raddr, &page_size, &fault_cause, &pte_addr);
 292    if (!pte) {
 293        return -1;
 294    }
 295
 296    return raddr & TARGET_PAGE_MASK;
 297}
 298