KVM: PPC: Book3S: Introduce new hcall H_COPY_TOFROM_GUEST to access quadrants 1 & 2
authorSuraj Jitindar Singh <sjitindarsingh@gmail.com>
Fri, 14 Dec 2018 05:29:09 +0000 (16:29 +1100)
committerPaul Mackerras <paulus@ozlabs.org>
Mon, 17 Dec 2018 00:33:50 +0000 (11:33 +1100)
A guest cannot access quadrants 1 or 2 as this would result in an
exception. Thus introduce the hcall H_COPY_TOFROM_GUEST to be used by a
guest when it wants to perform an access to quadrants 1 or 2, for
example when it wants to access memory for one of its nested guests.

Also provide an implementation for the kvm-hv module.

Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com>
Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
arch/powerpc/include/asm/hvcall.h
arch/powerpc/include/asm/kvm_book3s.h
arch/powerpc/kvm/book3s_64_mmu_radix.c
arch/powerpc/kvm/book3s_hv.c
arch/powerpc/kvm/book3s_hv_nested.c

index 33a4fc891947326f3a76518310b80e44f21674bd..463c63a9fcf16c34be39be93e963af3c13db6a97 100644 (file)
 #define H_SET_PARTITION_TABLE  0xF800
 #define H_ENTER_NESTED         0xF804
 #define H_TLB_INVALIDATE       0xF808
+#define H_COPY_TOFROM_GUEST    0xF80C
 
 /* Values for 2nd argument to H_SET_MODE */
 #define H_SET_MODE_RESOURCE_SET_CIABR          1
index 616b28802a198056b17ab25d8c170cc2e4ff903f..82a0a9f3c39c3379b289c92684ecd5eff40e019e 100644 (file)
@@ -188,6 +188,9 @@ extern int kvmppc_book3s_hcall_implemented(struct kvm *kvm, unsigned long hc);
 extern int kvmppc_book3s_radix_page_fault(struct kvm_run *run,
                        struct kvm_vcpu *vcpu,
                        unsigned long ea, unsigned long dsisr);
+extern unsigned long __kvmhv_copy_tofrom_guest_radix(int lpid, int pid,
+                                       gva_t eaddr, void *to, void *from,
+                                       unsigned long n);
 extern long kvmhv_copy_from_guest_radix(struct kvm_vcpu *vcpu, gva_t eaddr,
                                        void *to, unsigned long n);
 extern long kvmhv_copy_to_guest_radix(struct kvm_vcpu *vcpu, gva_t eaddr,
@@ -301,6 +304,7 @@ long kvmhv_nested_init(void);
 void kvmhv_nested_exit(void);
 void kvmhv_vm_nested_init(struct kvm *kvm);
 long kvmhv_set_partition_table(struct kvm_vcpu *vcpu);
+long kvmhv_copy_tofrom_guest_nested(struct kvm_vcpu *vcpu);
 void kvmhv_set_ptbl_entry(unsigned int lpid, u64 dw0, u64 dw1);
 void kvmhv_release_all_nested(struct kvm *kvm);
 long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu);
index c3f85c1b60d65c07d8e6f6a5d2911104274ac4b0..5b3f266422d6e6943d0007008860cca17c826dd1 100644 (file)
@@ -29,9 +29,9 @@
  */
 static int p9_supported_radix_bits[4] = { 5, 9, 9, 13 };
 
-static unsigned long __kvmhv_copy_tofrom_guest_radix(int lpid, int pid,
-                                       gva_t eaddr, void *to, void *from,
-                                       unsigned long n)
+unsigned long __kvmhv_copy_tofrom_guest_radix(int lpid, int pid,
+                                             gva_t eaddr, void *to, void *from,
+                                             unsigned long n)
 {
        unsigned long quadrant, ret = n;
        int old_pid, old_lpid;
@@ -82,6 +82,7 @@ static unsigned long __kvmhv_copy_tofrom_guest_radix(int lpid, int pid,
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(__kvmhv_copy_tofrom_guest_radix);
 
 static long kvmhv_copy_tofrom_guest_radix(struct kvm_vcpu *vcpu, gva_t eaddr,
                                          void *to, void *from, unsigned long n)
index ca7de15f4a70bec7b049f76f56565ec09a2785d9..5a066fc299e17189bd587ad20ddba86b956c6614 100644 (file)
@@ -996,7 +996,11 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu)
                if (nesting_enabled(vcpu->kvm))
                        ret = kvmhv_do_nested_tlbie(vcpu);
                break;
-
+       case H_COPY_TOFROM_GUEST:
+               ret = H_FUNCTION;
+               if (nesting_enabled(vcpu->kvm))
+                       ret = kvmhv_copy_tofrom_guest_nested(vcpu);
+               break;
        default:
                return RESUME_HOST;
        }
index aeb0bb2075fad308063dd5b41a24ab71425e32ee..e41a4d196a917605e9e5493d5ddb326185587cb5 100644 (file)
@@ -461,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.