Path: blob/master/arch/powerpc/kvm/book3s_64_mmu.c
10820 views
/*1* This program is free software; you can redistribute it and/or modify2* it under the terms of the GNU General Public License, version 2, as3* published by the Free Software Foundation.4*5* This program is distributed in the hope that it will be useful,6* but WITHOUT ANY WARRANTY; without even the implied warranty of7* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the8* GNU General Public License for more details.9*10* You should have received a copy of the GNU General Public License11* along with this program; if not, write to the Free Software12* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.13*14* Copyright SUSE Linux Products GmbH 200915*16* Authors: Alexander Graf <[email protected]>17*/1819#include <linux/types.h>20#include <linux/string.h>21#include <linux/kvm.h>22#include <linux/kvm_host.h>23#include <linux/highmem.h>2425#include <asm/tlbflush.h>26#include <asm/kvm_ppc.h>27#include <asm/kvm_book3s.h>2829/* #define DEBUG_MMU */3031#ifdef DEBUG_MMU32#define dprintk(X...) printk(KERN_INFO X)33#else34#define dprintk(X...) do { } while(0)35#endif3637static void kvmppc_mmu_book3s_64_reset_msr(struct kvm_vcpu *vcpu)38{39kvmppc_set_msr(vcpu, MSR_SF);40}4142static struct kvmppc_slb *kvmppc_mmu_book3s_64_find_slbe(43struct kvmppc_vcpu_book3s *vcpu_book3s,44gva_t eaddr)45{46int i;47u64 esid = GET_ESID(eaddr);48u64 esid_1t = GET_ESID_1T(eaddr);4950for (i = 0; i < vcpu_book3s->slb_nr; i++) {51u64 cmp_esid = esid;5253if (!vcpu_book3s->slb[i].valid)54continue;5556if (vcpu_book3s->slb[i].tb)57cmp_esid = esid_1t;5859if (vcpu_book3s->slb[i].esid == cmp_esid)60return &vcpu_book3s->slb[i];61}6263dprintk("KVM: No SLB entry found for 0x%lx [%llx | %llx]\n",64eaddr, esid, esid_1t);65for (i = 0; i < vcpu_book3s->slb_nr; i++) {66if (vcpu_book3s->slb[i].vsid)67dprintk(" %d: %c%c%c %llx %llx\n", i,68vcpu_book3s->slb[i].valid ? 'v' : ' ',69vcpu_book3s->slb[i].large ? 'l' : ' ',70vcpu_book3s->slb[i].tb ? 't' : ' ',71vcpu_book3s->slb[i].esid,72vcpu_book3s->slb[i].vsid);73}7475return NULL;76}7778static u64 kvmppc_mmu_book3s_64_ea_to_vp(struct kvm_vcpu *vcpu, gva_t eaddr,79bool data)80{81struct kvmppc_slb *slb;8283slb = kvmppc_mmu_book3s_64_find_slbe(to_book3s(vcpu), eaddr);84if (!slb)85return 0;8687if (slb->tb)88return (((u64)eaddr >> 12) & 0xfffffff) |89(((u64)slb->vsid) << 28);9091return (((u64)eaddr >> 12) & 0xffff) | (((u64)slb->vsid) << 16);92}9394static int kvmppc_mmu_book3s_64_get_pagesize(struct kvmppc_slb *slbe)95{96return slbe->large ? 24 : 12;97}9899static u32 kvmppc_mmu_book3s_64_get_page(struct kvmppc_slb *slbe, gva_t eaddr)100{101int p = kvmppc_mmu_book3s_64_get_pagesize(slbe);102return ((eaddr & 0xfffffff) >> p);103}104105static hva_t kvmppc_mmu_book3s_64_get_pteg(106struct kvmppc_vcpu_book3s *vcpu_book3s,107struct kvmppc_slb *slbe, gva_t eaddr,108bool second)109{110u64 hash, pteg, htabsize;111u32 page;112hva_t r;113114page = kvmppc_mmu_book3s_64_get_page(slbe, eaddr);115htabsize = ((1 << ((vcpu_book3s->sdr1 & 0x1f) + 11)) - 1);116117hash = slbe->vsid ^ page;118if (second)119hash = ~hash;120hash &= ((1ULL << 39ULL) - 1ULL);121hash &= htabsize;122hash <<= 7ULL;123124pteg = vcpu_book3s->sdr1 & 0xfffffffffffc0000ULL;125pteg |= hash;126127dprintk("MMU: page=0x%x sdr1=0x%llx pteg=0x%llx vsid=0x%llx\n",128page, vcpu_book3s->sdr1, pteg, slbe->vsid);129130r = gfn_to_hva(vcpu_book3s->vcpu.kvm, pteg >> PAGE_SHIFT);131if (kvm_is_error_hva(r))132return r;133return r | (pteg & ~PAGE_MASK);134}135136static u64 kvmppc_mmu_book3s_64_get_avpn(struct kvmppc_slb *slbe, gva_t eaddr)137{138int p = kvmppc_mmu_book3s_64_get_pagesize(slbe);139u64 avpn;140141avpn = kvmppc_mmu_book3s_64_get_page(slbe, eaddr);142avpn |= slbe->vsid << (28 - p);143144if (p < 24)145avpn >>= ((80 - p) - 56) - 8;146else147avpn <<= 8;148149return avpn;150}151152static int kvmppc_mmu_book3s_64_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,153struct kvmppc_pte *gpte, bool data)154{155struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);156struct kvmppc_slb *slbe;157hva_t ptegp;158u64 pteg[16];159u64 avpn = 0;160int i;161u8 key = 0;162bool found = false;163bool perm_err = false;164int second = 0;165ulong mp_ea = vcpu->arch.magic_page_ea;166167/* Magic page override */168if (unlikely(mp_ea) &&169unlikely((eaddr & ~0xfffULL) == (mp_ea & ~0xfffULL)) &&170!(vcpu->arch.shared->msr & MSR_PR)) {171gpte->eaddr = eaddr;172gpte->vpage = kvmppc_mmu_book3s_64_ea_to_vp(vcpu, eaddr, data);173gpte->raddr = vcpu->arch.magic_page_pa | (gpte->raddr & 0xfff);174gpte->raddr &= KVM_PAM;175gpte->may_execute = true;176gpte->may_read = true;177gpte->may_write = true;178179return 0;180}181182slbe = kvmppc_mmu_book3s_64_find_slbe(vcpu_book3s, eaddr);183if (!slbe)184goto no_seg_found;185186do_second:187ptegp = kvmppc_mmu_book3s_64_get_pteg(vcpu_book3s, slbe, eaddr, second);188if (kvm_is_error_hva(ptegp))189goto no_page_found;190191avpn = kvmppc_mmu_book3s_64_get_avpn(slbe, eaddr);192193if(copy_from_user(pteg, (void __user *)ptegp, sizeof(pteg))) {194printk(KERN_ERR "KVM can't copy data from 0x%lx!\n", ptegp);195goto no_page_found;196}197198if ((vcpu->arch.shared->msr & MSR_PR) && slbe->Kp)199key = 4;200else if (!(vcpu->arch.shared->msr & MSR_PR) && slbe->Ks)201key = 4;202203for (i=0; i<16; i+=2) {204u64 v = pteg[i];205u64 r = pteg[i+1];206207/* Valid check */208if (!(v & HPTE_V_VALID))209continue;210/* Hash check */211if ((v & HPTE_V_SECONDARY) != second)212continue;213214/* AVPN compare */215if (HPTE_V_AVPN_VAL(avpn) == HPTE_V_AVPN_VAL(v)) {216u8 pp = (r & HPTE_R_PP) | key;217int eaddr_mask = 0xFFF;218219gpte->eaddr = eaddr;220gpte->vpage = kvmppc_mmu_book3s_64_ea_to_vp(vcpu,221eaddr,222data);223if (slbe->large)224eaddr_mask = 0xFFFFFF;225gpte->raddr = (r & HPTE_R_RPN) | (eaddr & eaddr_mask);226gpte->may_execute = ((r & HPTE_R_N) ? false : true);227gpte->may_read = false;228gpte->may_write = false;229230switch (pp) {231case 0:232case 1:233case 2:234case 6:235gpte->may_write = true;236/* fall through */237case 3:238case 5:239case 7:240gpte->may_read = true;241break;242}243244if (!gpte->may_read) {245perm_err = true;246continue;247}248249dprintk("KVM MMU: Translated 0x%lx [0x%llx] -> 0x%llx "250"-> 0x%lx\n",251eaddr, avpn, gpte->vpage, gpte->raddr);252found = true;253break;254}255}256257/* Update PTE R and C bits, so the guest's swapper knows we used the258* page */259if (found) {260u32 oldr = pteg[i+1];261262if (gpte->may_read) {263/* Set the accessed flag */264pteg[i+1] |= HPTE_R_R;265}266if (gpte->may_write) {267/* Set the dirty flag */268pteg[i+1] |= HPTE_R_C;269} else {270dprintk("KVM: Mapping read-only page!\n");271}272273/* Write back into the PTEG */274if (pteg[i+1] != oldr)275copy_to_user((void __user *)ptegp, pteg, sizeof(pteg));276277return 0;278} else {279dprintk("KVM MMU: No PTE found (ea=0x%lx sdr1=0x%llx "280"ptegp=0x%lx)\n",281eaddr, to_book3s(vcpu)->sdr1, ptegp);282for (i = 0; i < 16; i += 2)283dprintk(" %02d: 0x%llx - 0x%llx (0x%llx)\n",284i, pteg[i], pteg[i+1], avpn);285286if (!second) {287second = HPTE_V_SECONDARY;288goto do_second;289}290}291292293no_page_found:294295296if (perm_err)297return -EPERM;298299return -ENOENT;300301no_seg_found:302303dprintk("KVM MMU: Trigger segment fault\n");304return -EINVAL;305}306307static void kvmppc_mmu_book3s_64_slbmte(struct kvm_vcpu *vcpu, u64 rs, u64 rb)308{309struct kvmppc_vcpu_book3s *vcpu_book3s;310u64 esid, esid_1t;311int slb_nr;312struct kvmppc_slb *slbe;313314dprintk("KVM MMU: slbmte(0x%llx, 0x%llx)\n", rs, rb);315316vcpu_book3s = to_book3s(vcpu);317318esid = GET_ESID(rb);319esid_1t = GET_ESID_1T(rb);320slb_nr = rb & 0xfff;321322if (slb_nr > vcpu_book3s->slb_nr)323return;324325slbe = &vcpu_book3s->slb[slb_nr];326327slbe->large = (rs & SLB_VSID_L) ? 1 : 0;328slbe->tb = (rs & SLB_VSID_B_1T) ? 1 : 0;329slbe->esid = slbe->tb ? esid_1t : esid;330slbe->vsid = rs >> 12;331slbe->valid = (rb & SLB_ESID_V) ? 1 : 0;332slbe->Ks = (rs & SLB_VSID_KS) ? 1 : 0;333slbe->Kp = (rs & SLB_VSID_KP) ? 1 : 0;334slbe->nx = (rs & SLB_VSID_N) ? 1 : 0;335slbe->class = (rs & SLB_VSID_C) ? 1 : 0;336337slbe->orige = rb & (ESID_MASK | SLB_ESID_V);338slbe->origv = rs;339340/* Map the new segment */341kvmppc_mmu_map_segment(vcpu, esid << SID_SHIFT);342}343344static u64 kvmppc_mmu_book3s_64_slbmfee(struct kvm_vcpu *vcpu, u64 slb_nr)345{346struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);347struct kvmppc_slb *slbe;348349if (slb_nr > vcpu_book3s->slb_nr)350return 0;351352slbe = &vcpu_book3s->slb[slb_nr];353354return slbe->orige;355}356357static u64 kvmppc_mmu_book3s_64_slbmfev(struct kvm_vcpu *vcpu, u64 slb_nr)358{359struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);360struct kvmppc_slb *slbe;361362if (slb_nr > vcpu_book3s->slb_nr)363return 0;364365slbe = &vcpu_book3s->slb[slb_nr];366367return slbe->origv;368}369370static void kvmppc_mmu_book3s_64_slbie(struct kvm_vcpu *vcpu, u64 ea)371{372struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);373struct kvmppc_slb *slbe;374375dprintk("KVM MMU: slbie(0x%llx)\n", ea);376377slbe = kvmppc_mmu_book3s_64_find_slbe(vcpu_book3s, ea);378379if (!slbe)380return;381382dprintk("KVM MMU: slbie(0x%llx, 0x%llx)\n", ea, slbe->esid);383384slbe->valid = false;385386kvmppc_mmu_map_segment(vcpu, ea);387}388389static void kvmppc_mmu_book3s_64_slbia(struct kvm_vcpu *vcpu)390{391struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);392int i;393394dprintk("KVM MMU: slbia()\n");395396for (i = 1; i < vcpu_book3s->slb_nr; i++)397vcpu_book3s->slb[i].valid = false;398399if (vcpu->arch.shared->msr & MSR_IR) {400kvmppc_mmu_flush_segments(vcpu);401kvmppc_mmu_map_segment(vcpu, kvmppc_get_pc(vcpu));402}403}404405static void kvmppc_mmu_book3s_64_mtsrin(struct kvm_vcpu *vcpu, u32 srnum,406ulong value)407{408u64 rb = 0, rs = 0;409410/*411* According to Book3 2.01 mtsrin is implemented as:412*413* The SLB entry specified by (RB)32:35 is loaded from register414* RS, as follows.415*416* SLBE Bit Source SLB Field417*418* 0:31 0x0000_0000 ESID-0:31419* 32:35 (RB)32:35 ESID-32:35420* 36 0b1 V421* 37:61 0x00_0000|| 0b0 VSID-0:24422* 62:88 (RS)37:63 VSID-25:51423* 89:91 (RS)33:35 Ks Kp N424* 92 (RS)36 L ((RS)36 must be 0b0)425* 93 0b0 C426*/427428dprintk("KVM MMU: mtsrin(0x%x, 0x%lx)\n", srnum, value);429430/* ESID = srnum */431rb |= (srnum & 0xf) << 28;432/* Set the valid bit */433rb |= 1 << 27;434/* Index = ESID */435rb |= srnum;436437/* VSID = VSID */438rs |= (value & 0xfffffff) << 12;439/* flags = flags */440rs |= ((value >> 28) & 0x7) << 9;441442kvmppc_mmu_book3s_64_slbmte(vcpu, rs, rb);443}444445static void kvmppc_mmu_book3s_64_tlbie(struct kvm_vcpu *vcpu, ulong va,446bool large)447{448u64 mask = 0xFFFFFFFFFULL;449450dprintk("KVM MMU: tlbie(0x%lx)\n", va);451452if (large)453mask = 0xFFFFFF000ULL;454kvmppc_mmu_pte_vflush(vcpu, va >> 12, mask);455}456457static int kvmppc_mmu_book3s_64_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,458u64 *vsid)459{460ulong ea = esid << SID_SHIFT;461struct kvmppc_slb *slb;462u64 gvsid = esid;463ulong mp_ea = vcpu->arch.magic_page_ea;464465if (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) {466slb = kvmppc_mmu_book3s_64_find_slbe(to_book3s(vcpu), ea);467if (slb)468gvsid = slb->vsid;469}470471switch (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) {472case 0:473*vsid = VSID_REAL | esid;474break;475case MSR_IR:476*vsid = VSID_REAL_IR | gvsid;477break;478case MSR_DR:479*vsid = VSID_REAL_DR | gvsid;480break;481case MSR_DR|MSR_IR:482if (!slb)483goto no_slb;484485*vsid = gvsid;486break;487default:488BUG();489break;490}491492if (vcpu->arch.shared->msr & MSR_PR)493*vsid |= VSID_PR;494495return 0;496497no_slb:498/* Catch magic page case */499if (unlikely(mp_ea) &&500unlikely(esid == (mp_ea >> SID_SHIFT)) &&501!(vcpu->arch.shared->msr & MSR_PR)) {502*vsid = VSID_REAL | esid;503return 0;504}505506return -EINVAL;507}508509static bool kvmppc_mmu_book3s_64_is_dcbz32(struct kvm_vcpu *vcpu)510{511return (to_book3s(vcpu)->hid[5] & 0x80);512}513514void kvmppc_mmu_book3s_64_init(struct kvm_vcpu *vcpu)515{516struct kvmppc_mmu *mmu = &vcpu->arch.mmu;517518mmu->mfsrin = NULL;519mmu->mtsrin = kvmppc_mmu_book3s_64_mtsrin;520mmu->slbmte = kvmppc_mmu_book3s_64_slbmte;521mmu->slbmfee = kvmppc_mmu_book3s_64_slbmfee;522mmu->slbmfev = kvmppc_mmu_book3s_64_slbmfev;523mmu->slbie = kvmppc_mmu_book3s_64_slbie;524mmu->slbia = kvmppc_mmu_book3s_64_slbia;525mmu->xlate = kvmppc_mmu_book3s_64_xlate;526mmu->reset_msr = kvmppc_mmu_book3s_64_reset_msr;527mmu->tlbie = kvmppc_mmu_book3s_64_tlbie;528mmu->esid_to_vsid = kvmppc_mmu_book3s_64_esid_to_vsid;529mmu->ea_to_vp = kvmppc_mmu_book3s_64_ea_to_vp;530mmu->is_dcbz32 = kvmppc_mmu_book3s_64_is_dcbz32;531532vcpu->arch.hflags |= BOOK3S_HFLAG_SLB;533}534535536