Merge tag 'xtensa-20190803' of git://github.com/jcmvbkbc/linux-xtensa
[linux-2.6-microblaze.git] / arch / powerpc / kvm / book3s_hv_nested.c
index 401d2ec..735e0ac 100644 (file)
@@ -195,6 +195,26 @@ void kvmhv_restore_hv_return_state(struct kvm_vcpu *vcpu,
        vcpu->arch.ppr = hr->ppr;
 }
 
+static void kvmhv_nested_mmio_needed(struct kvm_vcpu *vcpu, u64 regs_ptr)
+{
+       /* No need to reflect the page fault to L1, we've handled it */
+       vcpu->arch.trap = 0;
+
+       /*
+        * Since the L2 gprs have already been written back into L1 memory when
+        * we complete the mmio, store the L1 memory location of the L2 gpr
+        * being loaded into by the mmio so that the loaded value can be
+        * written there in kvmppc_complete_mmio_load()
+        */
+       if (((vcpu->arch.io_gpr & KVM_MMIO_REG_EXT_MASK) == KVM_MMIO_REG_GPR)
+           && (vcpu->mmio_is_write == 0)) {
+               vcpu->arch.nested_io_gpr = (gpa_t) regs_ptr +
+                                          offsetof(struct pt_regs,
+                                                   gpr[vcpu->arch.io_gpr]);
+               vcpu->arch.io_gpr = KVM_MMIO_REG_NESTED_GPR;
+       }
+}
+
 long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
 {
        long int err, r;
@@ -316,6 +336,11 @@ long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
        if (r == -EINTR)
                return H_INTERRUPT;
 
+       if (vcpu->mmio_needed) {
+               kvmhv_nested_mmio_needed(vcpu, regs_ptr);
+               return H_TOO_HARD;
+       }
+
        return vcpu->arch.trap;
 }
 
@@ -436,6 +461,81 @@ long kvmhv_set_partition_table(struct kvm_vcpu *vcpu)
        return ret;
 }
 
+/*
+ * Handle the H_COPY_TOFROM_GUEST hcall.
+ * r4 = L1 lpid of nested guest
+ * r5 = pid
+ * r6 = eaddr to access
+ * r7 = to buffer (L1 gpa)
+ * r8 = from buffer (L1 gpa)
+ * r9 = n bytes to copy
+ */
+long kvmhv_copy_tofrom_guest_nested(struct kvm_vcpu *vcpu)
+{
+       struct kvm_nested_guest *gp;
+       int l1_lpid = kvmppc_get_gpr(vcpu, 4);
+       int pid = kvmppc_get_gpr(vcpu, 5);
+       gva_t eaddr = kvmppc_get_gpr(vcpu, 6);
+       gpa_t gp_to = (gpa_t) kvmppc_get_gpr(vcpu, 7);
+       gpa_t gp_from = (gpa_t) kvmppc_get_gpr(vcpu, 8);
+       void *buf;
+       unsigned long n = kvmppc_get_gpr(vcpu, 9);
+       bool is_load = !!gp_to;
+       long rc;
+
+       if (gp_to && gp_from) /* One must be NULL to determine the direction */
+               return H_PARAMETER;
+
+       if (eaddr & (0xFFFUL << 52))
+               return H_PARAMETER;
+
+       buf = kzalloc(n, GFP_KERNEL);
+       if (!buf)
+               return H_NO_MEM;
+
+       gp = kvmhv_get_nested(vcpu->kvm, l1_lpid, false);
+       if (!gp) {
+               rc = H_PARAMETER;
+               goto out_free;
+       }
+
+       mutex_lock(&gp->tlb_lock);
+
+       if (is_load) {
+               /* Load from the nested guest into our buffer */
+               rc = __kvmhv_copy_tofrom_guest_radix(gp->shadow_lpid, pid,
+                                                    eaddr, buf, NULL, n);
+               if (rc)
+                       goto not_found;
+
+               /* Write what was loaded into our buffer back to the L1 guest */
+               rc = kvm_vcpu_write_guest(vcpu, gp_to, buf, n);
+               if (rc)
+                       goto not_found;
+       } else {
+               /* Load the data to be stored from the L1 guest into our buf */
+               rc = kvm_vcpu_read_guest(vcpu, gp_from, buf, n);
+               if (rc)
+                       goto not_found;
+
+               /* Store from our buffer into the nested guest */
+               rc = __kvmhv_copy_tofrom_guest_radix(gp->shadow_lpid, pid,
+                                                    eaddr, NULL, buf, n);
+               if (rc)
+                       goto not_found;
+       }
+
+out_unlock:
+       mutex_unlock(&gp->tlb_lock);
+       kvmhv_put_nested(gp);
+out_free:
+       kfree(buf);
+       return rc;
+not_found:
+       rc = H_NOT_FOUND;
+       goto out_unlock;
+}
+
 /*
  * Reload the partition table entry for a guest.
  * Caller must hold gp->tlb_lock.
@@ -480,6 +580,7 @@ struct kvm_nested_guest *kvmhv_alloc_nested(struct kvm *kvm, unsigned int lpid)
        if (shadow_lpid < 0)
                goto out_free2;
        gp->shadow_lpid = shadow_lpid;
+       gp->radix = 1;
 
        memset(gp->prev_cpu, -1, sizeof(gp->prev_cpu));
 
@@ -687,6 +788,57 @@ void kvmhv_insert_nest_rmap(struct kvm *kvm, unsigned long *rmapp,
        *n_rmap = NULL;
 }
 
+static void kvmhv_update_nest_rmap_rc(struct kvm *kvm, u64 n_rmap,
+                                     unsigned long clr, unsigned long set,
+                                     unsigned long hpa, unsigned long mask)
+{
+       struct kvm_nested_guest *gp;
+       unsigned long gpa;
+       unsigned int shift, lpid;
+       pte_t *ptep;
+
+       gpa = n_rmap & RMAP_NESTED_GPA_MASK;
+       lpid = (n_rmap & RMAP_NESTED_LPID_MASK) >> RMAP_NESTED_LPID_SHIFT;
+       gp = kvmhv_find_nested(kvm, lpid);
+       if (!gp)
+               return;
+
+       /* Find the pte */
+       ptep = __find_linux_pte(gp->shadow_pgtable, gpa, NULL, &shift);
+       /*
+        * If the pte is present and the pfn is still the same, update the pte.
+        * If the pfn has changed then this is a stale rmap entry, the nested
+        * gpa actually points somewhere else now, and there is nothing to do.
+        * XXX A future optimisation would be to remove the rmap entry here.
+        */
+       if (ptep && pte_present(*ptep) && ((pte_val(*ptep) & mask) == hpa)) {
+               __radix_pte_update(ptep, clr, set);
+               kvmppc_radix_tlbie_page(kvm, gpa, shift, lpid);
+       }
+}
+
+/*
+ * For a given list of rmap entries, update the rc bits in all ptes in shadow
+ * page tables for nested guests which are referenced by the rmap list.
+ */
+void kvmhv_update_nest_rmap_rc_list(struct kvm *kvm, unsigned long *rmapp,
+                                   unsigned long clr, unsigned long set,
+                                   unsigned long hpa, unsigned long nbytes)
+{
+       struct llist_node *entry = ((struct llist_head *) rmapp)->first;
+       struct rmap_nested *cursor;
+       unsigned long rmap, mask;
+
+       if ((clr | set) & ~(_PAGE_DIRTY | _PAGE_ACCESSED))
+               return;
+
+       mask = PTE_RPN_MASK & ~(nbytes - 1);
+       hpa &= mask;
+
+       for_each_nest_rmap_safe(cursor, entry, &rmap)
+               kvmhv_update_nest_rmap_rc(kvm, rmap, clr, set, hpa, mask);
+}
+
 static void kvmhv_remove_nest_rmap(struct kvm *kvm, u64 n_rmap,
                                   unsigned long hpa, unsigned long mask)
 {
@@ -723,7 +875,7 @@ static void kvmhv_remove_nest_rmap_list(struct kvm *kvm, unsigned long *rmapp,
 
 /* called with kvm->mmu_lock held */
 void kvmhv_remove_nest_rmap_range(struct kvm *kvm,
-                                 struct kvm_memory_slot *memslot,
+                                 const struct kvm_memory_slot *memslot,
                                  unsigned long gpa, unsigned long hpa,
                                  unsigned long nbytes)
 {
@@ -1049,7 +1201,7 @@ static long kvmhv_handle_nested_set_rc(struct kvm_vcpu *vcpu,
        struct kvm *kvm = vcpu->kvm;
        bool writing = !!(dsisr & DSISR_ISSTORE);
        u64 pgflags;
-       bool ret;
+       long ret;
 
        /* Are the rc bits set in the L1 partition scoped pte? */
        pgflags = _PAGE_ACCESSED;
@@ -1062,16 +1214,22 @@ static long kvmhv_handle_nested_set_rc(struct kvm_vcpu *vcpu,
        /* Set the rc bit in the pte of our (L0) pgtable for the L1 guest */
        ret = kvmppc_hv_handle_set_rc(kvm, kvm->arch.pgtable, writing,
                                     gpte.raddr, kvm->arch.lpid);
-       spin_unlock(&kvm->mmu_lock);
-       if (!ret)
-               return -EINVAL;
+       if (!ret) {
+               ret = -EINVAL;
+               goto out_unlock;
+       }
 
        /* Set the rc bit in the pte of the shadow_pgtable for the nest guest */
        ret = kvmppc_hv_handle_set_rc(kvm, gp->shadow_pgtable, writing, n_gpa,
                                      gp->shadow_lpid);
        if (!ret)
-               return -EINVAL;
-       return 0;
+               ret = -EINVAL;
+       else
+               ret = 0;
+
+out_unlock:
+       spin_unlock(&kvm->mmu_lock);
+       return ret;
 }
 
 static inline int kvmppc_radix_level_to_shift(int level)
@@ -1099,7 +1257,8 @@ static inline int kvmppc_radix_shift_to_level(int shift)
 }
 
 /* called with gp->tlb_lock held */
-static long int __kvmhv_nested_page_fault(struct kvm_vcpu *vcpu,
+static long int __kvmhv_nested_page_fault(struct kvm_run *run,
+                                         struct kvm_vcpu *vcpu,
                                          struct kvm_nested_guest *gp)
 {
        struct kvm *kvm = vcpu->kvm;
@@ -1180,9 +1339,9 @@ static long int __kvmhv_nested_page_fault(struct kvm_vcpu *vcpu,
                        kvmppc_core_queue_data_storage(vcpu, ea, dsisr);
                        return RESUME_GUEST;
                }
-               /* passthrough of emulated MMIO case... */
-               pr_err("emulated MMIO passthrough?\n");
-               return -EINVAL;
+
+               /* passthrough of emulated MMIO case */
+               return kvmppc_hv_emulate_mmio(run, vcpu, gpa, ea, writing);
        }
        if (memslot->flags & KVM_MEM_READONLY) {
                if (writing) {
@@ -1220,6 +1379,8 @@ static long int __kvmhv_nested_page_fault(struct kvm_vcpu *vcpu,
                        return ret;
                shift = kvmppc_radix_level_to_shift(level);
        }
+       /* Align gfn to the start of the page */
+       gfn = (gpa & ~((1UL << shift) - 1)) >> PAGE_SHIFT;
 
        /* 3. Compute the pte we need to insert for nest_gpa -> host r_addr */
 
@@ -1227,6 +1388,9 @@ static long int __kvmhv_nested_page_fault(struct kvm_vcpu *vcpu,
        perm |= gpte.may_read ? 0UL : _PAGE_READ;
        perm |= gpte.may_write ? 0UL : _PAGE_WRITE;
        perm |= gpte.may_execute ? 0UL : _PAGE_EXEC;
+       /* Only set accessed/dirty (rc) bits if set in host and l1 guest ptes */
+       perm |= (gpte.rc & _PAGE_ACCESSED) ? 0UL : _PAGE_ACCESSED;
+       perm |= ((gpte.rc & _PAGE_DIRTY) && writing) ? 0UL : _PAGE_DIRTY;
        pte = __pte(pte_val(pte) & ~perm);
 
        /* What size pte can we insert? */
@@ -1264,13 +1428,13 @@ static long int __kvmhv_nested_page_fault(struct kvm_vcpu *vcpu,
        return RESUME_GUEST;
 }
 
-long int kvmhv_nested_page_fault(struct kvm_vcpu *vcpu)
+long int kvmhv_nested_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu)
 {
        struct kvm_nested_guest *gp = vcpu->arch.nested;
        long int ret;
 
        mutex_lock(&gp->tlb_lock);
-       ret = __kvmhv_nested_page_fault(vcpu, gp);
+       ret = __kvmhv_nested_page_fault(run, vcpu, gp);
        mutex_unlock(&gp->tlb_lock);
        return ret;
 }