/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright 2010 Paul Mackerras, IBM Corp. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define VRMA_VSID 0x1ffffffUL /* 1TB VSID reserved for VRMA */ /* POWER7 has 10-bit LPIDs, PPC970 has 6-bit LPIDs */ #define MAX_LPID_970 63 #define NR_LPIDS (LPID_RSVD + 1) unsigned long lpid_inuse[BITS_TO_LONGS(NR_LPIDS)]; long kvmppc_alloc_hpt(struct kvm *kvm) { unsigned long hpt; unsigned long lpid; struct revmap_entry *rev; /* Allocate guest's hashed page table */ hpt = __get_free_pages(GFP_KERNEL|__GFP_ZERO|__GFP_REPEAT|__GFP_NOWARN, HPT_ORDER - PAGE_SHIFT); if (!hpt) { pr_err("kvm_alloc_hpt: Couldn't alloc HPT\n"); return -ENOMEM; } kvm->arch.hpt_virt = hpt; /* Allocate reverse map array */ rev = vmalloc(sizeof(struct revmap_entry) * HPT_NPTE); if (!rev) { pr_err("kvmppc_alloc_hpt: Couldn't alloc reverse map array\n"); goto out_freehpt; } kvm->arch.revmap = rev; /* Allocate the guest's logical partition ID */ do { lpid = find_first_zero_bit(lpid_inuse, NR_LPIDS); if (lpid >= NR_LPIDS) { pr_err("kvm_alloc_hpt: No LPIDs free\n"); goto out_freeboth; } } while (test_and_set_bit(lpid, lpid_inuse)); kvm->arch.sdr1 = __pa(hpt) | (HPT_ORDER - 18); kvm->arch.lpid = lpid; pr_info("KVM guest htab at %lx, LPID %lx\n", hpt, lpid); return 0; out_freeboth: vfree(rev); out_freehpt: free_pages(hpt, HPT_ORDER - PAGE_SHIFT); return -ENOMEM; } void kvmppc_free_hpt(struct kvm *kvm) { clear_bit(kvm->arch.lpid, lpid_inuse); vfree(kvm->arch.revmap); free_pages(kvm->arch.hpt_virt, HPT_ORDER - PAGE_SHIFT); } /* Bits in first HPTE dword for pagesize 4k, 64k or 16M */ static inline unsigned long hpte0_pgsize_encoding(unsigned long pgsize) { return (pgsize > 0x1000) ? HPTE_V_LARGE : 0; } /* Bits in second HPTE dword for pagesize 4k, 64k or 16M */ static inline unsigned long hpte1_pgsize_encoding(unsigned long pgsize) { return (pgsize == 0x10000) ? 0x1000 : 0; } void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, unsigned long porder) { unsigned long i; unsigned long npages; unsigned long hp_v, hp_r; unsigned long addr, hash; unsigned long psize; unsigned long hp0, hp1; long ret; psize = 1ul << porder; npages = memslot->npages >> (porder - PAGE_SHIFT); /* VRMA can't be > 1TB */ if (npages > 1ul << (40 - porder)) npages = 1ul << (40 - porder); /* Can't use more than 1 HPTE per HPTEG */ if (npages > HPT_NPTEG) npages = HPT_NPTEG; hp0 = HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16)) | HPTE_V_BOLTED | hpte0_pgsize_encoding(psize); hp1 = hpte1_pgsize_encoding(psize) | HPTE_R_R | HPTE_R_C | HPTE_R_M | PP_RWXX; for (i = 0; i < npages; ++i) { addr = i << porder; /* can't use hpt_hash since va > 64 bits */ hash = (i ^ (VRMA_VSID ^ (VRMA_VSID << 25))) & HPT_HASH_MASK; /* * We assume that the hash table is empty and no * vcpus are using it at this stage. Since we create * at most one HPTE per HPTEG, we just assume entry 7 * is available and use it. */ hash = (hash << 3) + 7; hp_v = hp0 | ((addr >> 16) & ~0x7fUL); hp_r = hp1 | addr; ret = kvmppc_virtmode_h_enter(vcpu, H_EXACT, hash, hp_v, hp_r); if (ret != H_SUCCESS) { pr_err("KVM: map_vrma at %lx failed, ret=%ld\n", addr, ret); break; } } } int kvmppc_mmu_hv_init(void) { unsigned long host_lpid, rsvd_lpid; if (!cpu_has_feature(CPU_FTR_HVMODE)) return -EINVAL; memset(lpid_inuse, 0, sizeof(lpid_inuse)); if (cpu_has_feature(CPU_FTR_ARCH_206)) { host_lpid = mfspr(SPRN_LPID); /* POWER7 */ rsvd_lpid = LPID_RSVD; } else { host_lpid = 0; /* PPC970 */ rsvd_lpid = MAX_LPID_970; } set_bit(host_lpid, lpid_inuse); /* rsvd_lpid is reserved for use in partition switching */ set_bit(rsvd_lpid, lpid_inuse); return 0; } void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu) { } static void kvmppc_mmu_book3s_64_hv_reset_msr(struct kvm_vcpu *vcpu) { kvmppc_set_msr(vcpu, MSR_SF | MSR_ME); } /* * This is called to get a reference to a guest page if there isn't * one already in the kvm->arch.slot_phys[][] arrays. */ static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn, struct kvm_memory_slot *memslot, unsigned long psize) { unsigned long start; long np, err; struct page *page, *hpage, *pages[1]; unsigned long s, pgsize; unsigned long *physp; unsigned int got, pgorder; unsigned long pfn, i, npages; physp = kvm->arch.slot_phys[memslot->id]; if (!physp) return -EINVAL; if (physp[gfn - memslot->base_gfn]) return 0; page = NULL; pgsize = psize; start = gfn_to_hva_memslot(memslot, gfn); /* Instantiate and get the page we want access to */ np = get_user_pages_fast(start, 1, 1, pages); if (np != 1) return -EINVAL; page = pages[0]; got = KVMPPC_GOT_PAGE; /* See if this is a large page */ s = PAGE_SIZE; if (PageHuge(page)) { hpage = compound_head(page); s <<= compound_order(hpage); /* Get the whole large page if slot alignment is ok */ if (s > psize && slot_is_aligned(memslot, s) && !(memslot->userspace_addr & (s - 1))) { start &= ~(s - 1); pgsize = s; page = hpage; } } err = -EINVAL; if (s < psize) goto out; pfn = page_to_pfn(page); npages = pgsize >> PAGE_SHIFT; pgorder = __ilog2(npages); physp += (gfn - memslot->base_gfn) & ~(npages - 1); spin_lock(&kvm->arch.slot_phys_lock); for (i = 0; i < npages; ++i) { if (!physp[i]) { physp[i] = ((pfn + i) << PAGE_SHIFT) + got + pgorder; got = 0; } } spin_unlock(&kvm->arch.slot_phys_lock); err = 0; out: if (got) { if (PageHuge(page)) page = compound_head(page); put_page(page); } return err; } /* * We come here on a H_ENTER call from the guest when * we don't have the requested page pinned already. */ long kvmppc_virtmode_h_enter(struct kvm_vcpu *vcpu, unsigned long flags, long pte_index, unsigned long pteh, unsigned long ptel) { struct kvm *kvm = vcpu->kvm; unsigned long psize, gpa, gfn; struct kvm_memory_slot *memslot; long ret; psize = hpte_page_size(pteh, ptel); if (!psize) return H_PARAMETER; /* Find the memslot (if any) for this address */ gpa = (ptel & HPTE_R_RPN) & ~(psize - 1); gfn = gpa >> PAGE_SHIFT; memslot = gfn_to_memslot(kvm, gfn); if (!memslot || (memslot->flags & KVM_MEMSLOT_INVALID)) return H_PARAMETER; if (!slot_is_aligned(memslot, psize)) return H_PARAMETER; if (kvmppc_get_guest_page(kvm, gfn, memslot, psize) < 0) return H_PARAMETER; preempt_disable(); ret = kvmppc_h_enter(vcpu, flags, pte_index, pteh, ptel); preempt_enable(); if (ret == H_TOO_HARD) { /* this can't happen */ pr_err("KVM: Oops, kvmppc_h_enter returned too hard!\n"); ret = H_RESOURCE; /* or something */ } return ret; } static int kvmppc_mmu_book3s_64_hv_xlate(struct kvm_vcpu *vcpu, gva_t eaddr, struct kvmppc_pte *gpte, bool data) { return -ENOENT; } void *kvmppc_pin_guest_page(struct kvm *kvm, unsigned long gpa, unsigned long *nb_ret) { struct kvm_memory_slot *memslot; unsigned long gfn = gpa >> PAGE_SHIFT; struct page *page; unsigned long psize, offset; unsigned long pa; unsigned long *physp; memslot = gfn_to_memslot(kvm, gfn); if (!memslot || (memslot->flags & KVM_MEMSLOT_INVALID)) return NULL; physp = kvm->arch.slot_phys[memslot->id]; if (!physp) return NULL; physp += gfn - memslot->base_gfn; pa = *physp; if (!pa) { if (kvmppc_get_guest_page(kvm, gfn, memslot, PAGE_SIZE) < 0) return NULL; pa = *physp; } page = pfn_to_page(pa >> PAGE_SHIFT); psize = PAGE_SIZE; if (PageHuge(page)) { page = compound_head(page); psize <<= compound_order(page); } get_page(page); offset = gpa & (psize - 1); if (nb_ret) *nb_ret = psize - offset; return page_address(page) + offset; } void kvmppc_unpin_guest_page(struct kvm *kvm, void *va) { struct page *page = virt_to_page(va); page = compound_head(page); put_page(page); } void kvmppc_mmu_book3s_hv_init(struct kvm_vcpu *vcpu) { struct kvmppc_mmu *mmu = &vcpu->arch.mmu; if (cpu_has_feature(CPU_FTR_ARCH_206)) vcpu->arch.slb_nr = 32; /* POWER7 */ else vcpu->arch.slb_nr = 64; mmu->xlate = kvmppc_mmu_book3s_64_hv_xlate; mmu->reset_msr = kvmppc_mmu_book3s_64_hv_reset_msr; vcpu->arch.hflags |= BOOK3S_HFLAG_SLB; }