summaryrefslogtreecommitdiff
path: root/arch/mips/kvm/mmu.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kvm/mmu.c')
-rw-r--r--arch/mips/kvm/mmu.c405
1 files changed, 0 insertions, 405 deletions
diff --git a/arch/mips/kvm/mmu.c b/arch/mips/kvm/mmu.c
index 3dabeda82458..190ca2451851 100644
--- a/arch/mips/kvm/mmu.c
+++ b/arch/mips/kvm/mmu.c
@@ -756,209 +756,6 @@ out:
return err;
}
-static pte_t *kvm_trap_emul_pte_for_gva(struct kvm_vcpu *vcpu,
- unsigned long addr)
-{
- struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache;
- pgd_t *pgdp;
- int ret;
-
- /* We need a minimum of cached pages ready for page table creation */
- ret = kvm_mmu_topup_memory_cache(memcache, KVM_MMU_CACHE_MIN_PAGES);
- if (ret)
- return NULL;
-
- if (KVM_GUEST_KERNEL_MODE(vcpu))
- pgdp = vcpu->arch.guest_kernel_mm.pgd;
- else
- pgdp = vcpu->arch.guest_user_mm.pgd;
-
- return kvm_mips_walk_pgd(pgdp, memcache, addr);
-}
-
-void kvm_trap_emul_invalidate_gva(struct kvm_vcpu *vcpu, unsigned long addr,
- bool user)
-{
- pgd_t *pgdp;
- pte_t *ptep;
-
- addr &= PAGE_MASK << 1;
-
- pgdp = vcpu->arch.guest_kernel_mm.pgd;
- ptep = kvm_mips_walk_pgd(pgdp, NULL, addr);
- if (ptep) {
- ptep[0] = pfn_pte(0, __pgprot(0));
- ptep[1] = pfn_pte(0, __pgprot(0));
- }
-
- if (user) {
- pgdp = vcpu->arch.guest_user_mm.pgd;
- ptep = kvm_mips_walk_pgd(pgdp, NULL, addr);
- if (ptep) {
- ptep[0] = pfn_pte(0, __pgprot(0));
- ptep[1] = pfn_pte(0, __pgprot(0));
- }
- }
-}
-
-/*
- * kvm_mips_flush_gva_{pte,pmd,pud,pgd,pt}.
- * Flush a range of guest physical address space from the VM's GPA page tables.
- */
-
-static bool kvm_mips_flush_gva_pte(pte_t *pte, unsigned long start_gva,
- unsigned long end_gva)
-{
- int i_min = pte_index(start_gva);
- int i_max = pte_index(end_gva);
- bool safe_to_remove = (i_min == 0 && i_max == PTRS_PER_PTE - 1);
- int i;
-
- /*
- * There's no freeing to do, so there's no point clearing individual
- * entries unless only part of the last level page table needs flushing.
- */
- if (safe_to_remove)
- return true;
-
- for (i = i_min; i <= i_max; ++i) {
- if (!pte_present(pte[i]))
- continue;
-
- set_pte(pte + i, __pte(0));
- }
- return false;
-}
-
-static bool kvm_mips_flush_gva_pmd(pmd_t *pmd, unsigned long start_gva,
- unsigned long end_gva)
-{
- pte_t *pte;
- unsigned long end = ~0ul;
- int i_min = pmd_index(start_gva);
- int i_max = pmd_index(end_gva);
- bool safe_to_remove = (i_min == 0 && i_max == PTRS_PER_PMD - 1);
- int i;
-
- for (i = i_min; i <= i_max; ++i, start_gva = 0) {
- if (!pmd_present(pmd[i]))
- continue;
-
- pte = pte_offset_kernel(pmd + i, 0);
- if (i == i_max)
- end = end_gva;
-
- if (kvm_mips_flush_gva_pte(pte, start_gva, end)) {
- pmd_clear(pmd + i);
- pte_free_kernel(NULL, pte);
- } else {
- safe_to_remove = false;
- }
- }
- return safe_to_remove;
-}
-
-static bool kvm_mips_flush_gva_pud(pud_t *pud, unsigned long start_gva,
- unsigned long end_gva)
-{
- pmd_t *pmd;
- unsigned long end = ~0ul;
- int i_min = pud_index(start_gva);
- int i_max = pud_index(end_gva);
- bool safe_to_remove = (i_min == 0 && i_max == PTRS_PER_PUD - 1);
- int i;
-
- for (i = i_min; i <= i_max; ++i, start_gva = 0) {
- if (!pud_present(pud[i]))
- continue;
-
- pmd = pmd_offset(pud + i, 0);
- if (i == i_max)
- end = end_gva;
-
- if (kvm_mips_flush_gva_pmd(pmd, start_gva, end)) {
- pud_clear(pud + i);
- pmd_free(NULL, pmd);
- } else {
- safe_to_remove = false;
- }
- }
- return safe_to_remove;
-}
-
-static bool kvm_mips_flush_gva_pgd(pgd_t *pgd, unsigned long start_gva,
- unsigned long end_gva)
-{
- p4d_t *p4d;
- pud_t *pud;
- unsigned long end = ~0ul;
- int i_min = pgd_index(start_gva);
- int i_max = pgd_index(end_gva);
- bool safe_to_remove = (i_min == 0 && i_max == PTRS_PER_PGD - 1);
- int i;
-
- for (i = i_min; i <= i_max; ++i, start_gva = 0) {
- if (!pgd_present(pgd[i]))
- continue;
-
- p4d = p4d_offset(pgd, 0);
- pud = pud_offset(p4d + i, 0);
- if (i == i_max)
- end = end_gva;
-
- if (kvm_mips_flush_gva_pud(pud, start_gva, end)) {
- pgd_clear(pgd + i);
- pud_free(NULL, pud);
- } else {
- safe_to_remove = false;
- }
- }
- return safe_to_remove;
-}
-
-void kvm_mips_flush_gva_pt(pgd_t *pgd, enum kvm_mips_flush flags)
-{
- if (flags & KMF_GPA) {
- /* all of guest virtual address space could be affected */
- if (flags & KMF_KERN)
- /* useg, kseg0, seg2/3 */
- kvm_mips_flush_gva_pgd(pgd, 0, 0x7fffffff);
- else
- /* useg */
- kvm_mips_flush_gva_pgd(pgd, 0, 0x3fffffff);
- } else {
- /* useg */
- kvm_mips_flush_gva_pgd(pgd, 0, 0x3fffffff);
-
- /* kseg2/3 */
- if (flags & KMF_KERN)
- kvm_mips_flush_gva_pgd(pgd, 0x60000000, 0x7fffffff);
- }
-}
-
-static pte_t kvm_mips_gpa_pte_to_gva_unmapped(pte_t pte)
-{
- /*
- * Don't leak writeable but clean entries from GPA page tables. We don't
- * want the normal Linux tlbmod handler to handle dirtying when KVM
- * accesses guest memory.
- */
- if (!pte_dirty(pte))
- pte = pte_wrprotect(pte);
-
- return pte;
-}
-
-static pte_t kvm_mips_gpa_pte_to_gva_mapped(pte_t pte, long entrylo)
-{
- /* Guest EntryLo overrides host EntryLo */
- if (!(entrylo & ENTRYLO_D))
- pte = pte_mkclean(pte);
-
- return kvm_mips_gpa_pte_to_gva_unmapped(pte);
-}
-
-#ifdef CONFIG_KVM_MIPS_VZ
int kvm_mips_handle_vz_root_tlb_fault(unsigned long badvaddr,
struct kvm_vcpu *vcpu,
bool write_fault)
@@ -972,125 +769,6 @@ int kvm_mips_handle_vz_root_tlb_fault(unsigned long badvaddr,
/* Invalidate this entry in the TLB */
return kvm_vz_host_tlb_inv(vcpu, badvaddr);
}
-#endif
-
-/* XXXKYMA: Must be called with interrupts disabled */
-int kvm_mips_handle_kseg0_tlb_fault(unsigned long badvaddr,
- struct kvm_vcpu *vcpu,
- bool write_fault)
-{
- unsigned long gpa;
- pte_t pte_gpa[2], *ptep_gva;
- int idx;
-
- if (KVM_GUEST_KSEGX(badvaddr) != KVM_GUEST_KSEG0) {
- kvm_err("%s: Invalid BadVaddr: %#lx\n", __func__, badvaddr);
- kvm_mips_dump_host_tlbs();
- return -1;
- }
-
- /* Get the GPA page table entry */
- gpa = KVM_GUEST_CPHYSADDR(badvaddr);
- idx = (badvaddr >> PAGE_SHIFT) & 1;
- if (kvm_mips_map_page(vcpu, gpa, write_fault, &pte_gpa[idx],
- &pte_gpa[!idx]) < 0)
- return -1;
-
- /* Get the GVA page table entry */
- ptep_gva = kvm_trap_emul_pte_for_gva(vcpu, badvaddr & ~PAGE_SIZE);
- if (!ptep_gva) {
- kvm_err("No ptep for gva %lx\n", badvaddr);
- return -1;
- }
-
- /* Copy a pair of entries from GPA page table to GVA page table */
- ptep_gva[0] = kvm_mips_gpa_pte_to_gva_unmapped(pte_gpa[0]);
- ptep_gva[1] = kvm_mips_gpa_pte_to_gva_unmapped(pte_gpa[1]);
-
- /* Invalidate this entry in the TLB, guest kernel ASID only */
- kvm_mips_host_tlb_inv(vcpu, badvaddr, false, true);
- return 0;
-}
-
-int kvm_mips_handle_mapped_seg_tlb_fault(struct kvm_vcpu *vcpu,
- struct kvm_mips_tlb *tlb,
- unsigned long gva,
- bool write_fault)
-{
- struct kvm *kvm = vcpu->kvm;
- long tlb_lo[2];
- pte_t pte_gpa[2], *ptep_buddy, *ptep_gva;
- unsigned int idx = TLB_LO_IDX(*tlb, gva);
- bool kernel = KVM_GUEST_KERNEL_MODE(vcpu);
-
- tlb_lo[0] = tlb->tlb_lo[0];
- tlb_lo[1] = tlb->tlb_lo[1];
-
- /*
- * The commpage address must not be mapped to anything else if the guest
- * TLB contains entries nearby, or commpage accesses will break.
- */
- if (!((gva ^ KVM_GUEST_COMMPAGE_ADDR) & VPN2_MASK & (PAGE_MASK << 1)))
- tlb_lo[TLB_LO_IDX(*tlb, KVM_GUEST_COMMPAGE_ADDR)] = 0;
-
- /* Get the GPA page table entry */
- if (kvm_mips_map_page(vcpu, mips3_tlbpfn_to_paddr(tlb_lo[idx]),
- write_fault, &pte_gpa[idx], NULL) < 0)
- return -1;
-
- /* And its GVA buddy's GPA page table entry if it also exists */
- pte_gpa[!idx] = pfn_pte(0, __pgprot(0));
- if (tlb_lo[!idx] & ENTRYLO_V) {
- spin_lock(&kvm->mmu_lock);
- ptep_buddy = kvm_mips_pte_for_gpa(kvm, NULL,
- mips3_tlbpfn_to_paddr(tlb_lo[!idx]));
- if (ptep_buddy)
- pte_gpa[!idx] = *ptep_buddy;
- spin_unlock(&kvm->mmu_lock);
- }
-
- /* Get the GVA page table entry pair */
- ptep_gva = kvm_trap_emul_pte_for_gva(vcpu, gva & ~PAGE_SIZE);
- if (!ptep_gva) {
- kvm_err("No ptep for gva %lx\n", gva);
- return -1;
- }
-
- /* Copy a pair of entries from GPA page table to GVA page table */
- ptep_gva[0] = kvm_mips_gpa_pte_to_gva_mapped(pte_gpa[0], tlb_lo[0]);
- ptep_gva[1] = kvm_mips_gpa_pte_to_gva_mapped(pte_gpa[1], tlb_lo[1]);
-
- /* Invalidate this entry in the TLB, current guest mode ASID only */
- kvm_mips_host_tlb_inv(vcpu, gva, !kernel, kernel);
-
- kvm_debug("@ %#lx tlb_lo0: 0x%08lx tlb_lo1: 0x%08lx\n", vcpu->arch.pc,
- tlb->tlb_lo[0], tlb->tlb_lo[1]);
-
- return 0;
-}
-
-int kvm_mips_handle_commpage_tlb_fault(unsigned long badvaddr,
- struct kvm_vcpu *vcpu)
-{
- kvm_pfn_t pfn;
- pte_t *ptep;
- pgprot_t prot;
-
- ptep = kvm_trap_emul_pte_for_gva(vcpu, badvaddr);
- if (!ptep) {
- kvm_err("No ptep for commpage %lx\n", badvaddr);
- return -1;
- }
-
- pfn = PFN_DOWN(virt_to_phys(vcpu->arch.kseg0_commpage));
- /* Also set valid and dirty, so refill handler doesn't have to */
- prot = vm_get_page_prot(VM_READ|VM_WRITE|VM_SHARED);
- *ptep = pte_mkyoung(pte_mkdirty(pfn_pte(pfn, prot)));
-
- /* Invalidate this entry in the TLB, guest kernel ASID only */
- kvm_mips_host_tlb_inv(vcpu, badvaddr, false, true);
- return 0;
-}
/**
* kvm_mips_migrate_count() - Migrate timer.
@@ -1153,86 +831,3 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
local_irq_restore(flags);
}
-
-/**
- * kvm_trap_emul_gva_fault() - Safely attempt to handle a GVA access fault.
- * @vcpu: Virtual CPU.
- * @gva: Guest virtual address to be accessed.
- * @write: True if write attempted (must be dirtied and made writable).
- *
- * Safely attempt to handle a GVA fault, mapping GVA pages if necessary, and
- * dirtying the page if @write so that guest instructions can be modified.
- *
- * Returns: KVM_MIPS_MAPPED on success.
- * KVM_MIPS_GVA if bad guest virtual address.
- * KVM_MIPS_GPA if bad guest physical address.
- * KVM_MIPS_TLB if guest TLB not present.
- * KVM_MIPS_TLBINV if guest TLB present but not valid.
- * KVM_MIPS_TLBMOD if guest TLB read only.
- */
-enum kvm_mips_fault_result kvm_trap_emul_gva_fault(struct kvm_vcpu *vcpu,
- unsigned long gva,
- bool write)
-{
- struct mips_coproc *cop0 = vcpu->arch.cop0;
- struct kvm_mips_tlb *tlb;
- int index;
-
- if (KVM_GUEST_KSEGX(gva) == KVM_GUEST_KSEG0) {
- if (kvm_mips_handle_kseg0_tlb_fault(gva, vcpu, write) < 0)
- return KVM_MIPS_GPA;
- } else if ((KVM_GUEST_KSEGX(gva) < KVM_GUEST_KSEG0) ||
- KVM_GUEST_KSEGX(gva) == KVM_GUEST_KSEG23) {
- /* Address should be in the guest TLB */
- index = kvm_mips_guest_tlb_lookup(vcpu, (gva & VPN2_MASK) |
- (kvm_read_c0_guest_entryhi(cop0) & KVM_ENTRYHI_ASID));
- if (index < 0)
- return KVM_MIPS_TLB;
- tlb = &vcpu->arch.guest_tlb[index];
-
- /* Entry should be valid, and dirty for writes */
- if (!TLB_IS_VALID(*tlb, gva))
- return KVM_MIPS_TLBINV;
- if (write && !TLB_IS_DIRTY(*tlb, gva))
- return KVM_MIPS_TLBMOD;
-
- if (kvm_mips_handle_mapped_seg_tlb_fault(vcpu, tlb, gva, write))
- return KVM_MIPS_GPA;
- } else {
- return KVM_MIPS_GVA;
- }
-
- return KVM_MIPS_MAPPED;
-}
-
-int kvm_get_inst(u32 *opc, struct kvm_vcpu *vcpu, u32 *out)
-{
- int err;
-
- if (WARN(IS_ENABLED(CONFIG_KVM_MIPS_VZ),
- "Expect BadInstr/BadInstrP registers to be used with VZ\n"))
- return -EINVAL;
-
-retry:
- kvm_trap_emul_gva_lockless_begin(vcpu);
- err = get_user(*out, opc);
- kvm_trap_emul_gva_lockless_end(vcpu);
-
- if (unlikely(err)) {
- /*
- * Try to handle the fault, maybe we just raced with a GVA
- * invalidation.
- */
- err = kvm_trap_emul_gva_fault(vcpu, (unsigned long)opc,
- false);
- if (unlikely(err)) {
- kvm_err("%s: illegal address: %p\n",
- __func__, opc);
- return -EFAULT;
- }
-
- /* Hopefully it'll work now */
- goto retry;
- }
- return 0;
-}