]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
KVM: PPC: Book3S HV: Provide a way for userspace to get/set per-vCPU areas
authorPaul Mackerras <paulus@samba.org>
Tue, 25 Sep 2012 20:33:06 +0000 (20:33 +0000)
committerAlexander Graf <agraf@suse.de>
Fri, 28 Sep 2012 00:19:23 +0000 (02:19 +0200)
The PAPR paravirtualization interface lets guests register three
different types of per-vCPU buffer areas in its memory for communication
with the hypervisor.  These are called virtual processor areas (VPAs).
Currently the hypercalls to register and unregister VPAs are handled
by KVM in the kernel, and userspace has no way to know about or save
and restore these registrations across a migration.

This adds "register" codes for these three areas that userspace can
use with the KVM_GET/SET_ONE_REG ioctls to see what addresses have
been registered, and to register or unregister them.  This will be
needed for guest hibernation and migration, and is also needed so
that userspace can unregister them on reset (otherwise we corrupt
guest memory after reboot by writing to the VPAs registered by the
previous kernel).

The "register" for the VPA is a 64-bit value containing the address,
since the length of the VPA is fixed.  The "registers" for the SLB
shadow buffer and dispatch trace log (DTL) are 128 bits long,
consisting of the guest physical address in the high (first) 64 bits
and the length in the low 64 bits.

This also fixes a bug where we were calling init_vpa unconditionally,
leading to an oops when unregistering the VPA.

Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Alexander Graf <agraf@suse.de>
Documentation/virtual/kvm/api.txt
arch/powerpc/include/asm/kvm.h
arch/powerpc/include/asm/kvm_ppc.h
arch/powerpc/kvm/book3s_hv.c

index 975bb4c7f7e156647eada6d205c5da08386b857a..2a4d868a3df1db9bbe9c27f0c4e2672bed91db67 100644 (file)
@@ -1770,6 +1770,9 @@ registers, find a list below:
   PPC   | KVM_REG_PPC_VSR31     | 128
   PPC   | KVM_REG_PPC_FPSCR     | 64
   PPC   | KVM_REG_PPC_VSCR      | 32
+  PPC   | KVM_REG_PPC_VPA_ADDR  | 64
+  PPC   | KVM_REG_PPC_VPA_SLB   | 128
+  PPC   | KVM_REG_PPC_VPA_DTL   | 128
 
 4.69 KVM_GET_ONE_REG
 
index 1466975129c7b21ec508b767dd629a7a95b816fd..b89ae4db45ced4523e101c18a5477c62c928fd0a 100644 (file)
@@ -380,4 +380,10 @@ struct kvm_book3e_206_tlb_params {
 #define KVM_REG_PPC_FPSCR      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x80)
 #define KVM_REG_PPC_VSCR       (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x81)
 
+/* Virtual processor areas */
+/* For SLB & DTL, address in high (first) half, length in low half */
+#define KVM_REG_PPC_VPA_ADDR   (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x82)
+#define KVM_REG_PPC_VPA_SLB    (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x83)
+#define KVM_REG_PPC_VPA_DTL    (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x84)
+
 #endif /* __LINUX_KVM_POWERPC_H */
index 51604a16c8a5a50f20bd030f6d806865717c9905..609cca3e942621a7380c79724cd6e72d2e6d9b5c 100644 (file)
@@ -202,6 +202,10 @@ union kvmppc_one_reg {
        u64     dval;
        vector128 vval;
        u64     vsxval[2];
+       struct {
+               u64     addr;
+               u64     length;
+       }       vpaval;
 };
 
 #define one_reg_size(id)       \
index 94ec0e30969d1b58dada27eca491ed0eacff827c..9a15da76e56bed6aa702e5d0682abc1e6983867e 100644 (file)
@@ -143,6 +143,22 @@ static void init_vpa(struct kvm_vcpu *vcpu, struct lppaca *vpa)
        vpa->yield_count = 1;
 }
 
+static int set_vpa(struct kvm_vcpu *vcpu, struct kvmppc_vpa *v,
+                  unsigned long addr, unsigned long len)
+{
+       /* check address is cacheline aligned */
+       if (addr & (L1_CACHE_BYTES - 1))
+               return -EINVAL;
+       spin_lock(&vcpu->arch.vpa_update_lock);
+       if (v->next_gpa != addr || v->len != len) {
+               v->next_gpa = addr;
+               v->len = addr ? len : 0;
+               v->update_pending = 1;
+       }
+       spin_unlock(&vcpu->arch.vpa_update_lock);
+       return 0;
+}
+
 /* Length for a per-processor buffer is passed in at offset 4 in the buffer */
 struct reg_vpa {
        u32 dummy;
@@ -321,7 +337,8 @@ static void kvmppc_update_vpas(struct kvm_vcpu *vcpu)
        spin_lock(&vcpu->arch.vpa_update_lock);
        if (vcpu->arch.vpa.update_pending) {
                kvmppc_update_vpa(vcpu, &vcpu->arch.vpa);
-               init_vpa(vcpu, vcpu->arch.vpa.pinned_addr);
+               if (vcpu->arch.vpa.pinned_addr)
+                       init_vpa(vcpu, vcpu->arch.vpa.pinned_addr);
        }
        if (vcpu->arch.dtl.update_pending) {
                kvmppc_update_vpa(vcpu, &vcpu->arch.dtl);
@@ -600,6 +617,23 @@ int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val)
                }
                break;
 #endif /* CONFIG_VSX */
+       case KVM_REG_PPC_VPA_ADDR:
+               spin_lock(&vcpu->arch.vpa_update_lock);
+               *val = get_reg_val(id, vcpu->arch.vpa.next_gpa);
+               spin_unlock(&vcpu->arch.vpa_update_lock);
+               break;
+       case KVM_REG_PPC_VPA_SLB:
+               spin_lock(&vcpu->arch.vpa_update_lock);
+               val->vpaval.addr = vcpu->arch.slb_shadow.next_gpa;
+               val->vpaval.length = vcpu->arch.slb_shadow.len;
+               spin_unlock(&vcpu->arch.vpa_update_lock);
+               break;
+       case KVM_REG_PPC_VPA_DTL:
+               spin_lock(&vcpu->arch.vpa_update_lock);
+               val->vpaval.addr = vcpu->arch.dtl.next_gpa;
+               val->vpaval.length = vcpu->arch.dtl.len;
+               spin_unlock(&vcpu->arch.vpa_update_lock);
+               break;
        default:
                r = -EINVAL;
                break;
@@ -612,6 +646,7 @@ int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val)
 {
        int r = 0;
        long int i;
+       unsigned long addr, len;
 
        switch (id) {
        case KVM_REG_PPC_HIOR:
@@ -666,6 +701,33 @@ int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id, union kvmppc_one_reg *val)
                }
                break;
 #endif /* CONFIG_VSX */
+       case KVM_REG_PPC_VPA_ADDR:
+               addr = set_reg_val(id, *val);
+               r = -EINVAL;
+               if (!addr && (vcpu->arch.slb_shadow.next_gpa ||
+                             vcpu->arch.dtl.next_gpa))
+                       break;
+               r = set_vpa(vcpu, &vcpu->arch.vpa, addr, sizeof(struct lppaca));
+               break;
+       case KVM_REG_PPC_VPA_SLB:
+               addr = val->vpaval.addr;
+               len = val->vpaval.length;
+               r = -EINVAL;
+               if (addr && !vcpu->arch.vpa.next_gpa)
+                       break;
+               r = set_vpa(vcpu, &vcpu->arch.slb_shadow, addr, len);
+               break;
+       case KVM_REG_PPC_VPA_DTL:
+               addr = val->vpaval.addr;
+               len = val->vpaval.length;
+               r = -EINVAL;
+               if (len < sizeof(struct dtl_entry))
+                       break;
+               if (addr && !vcpu->arch.vpa.next_gpa)
+                       break;
+               len -= len % sizeof(struct dtl_entry);
+               r = set_vpa(vcpu, &vcpu->arch.dtl, addr, len);
+               break;
        default:
                r = -EINVAL;
                break;