diff mbox

[v2,2/2] KVM: PPC: Book3S HV: Get/set guest FP regs using the GET/SET_ONE_REG interface

Message ID 20120918071449.GC12437@bloggs.ozlabs.ibm.com (mailing list archive)
State New, archived
Headers show

Commit Message

Paul Mackerras Sept. 18, 2012, 7:14 a.m. UTC
This enables userspace to get and set all the guest floating-point
state using the KVM_[GS]ET_ONE_REG ioctls.  The floating-point state
includes all of the traditional floating-point registers and the
FPSCR (floating point status/control register), all the VMX/Altivec
vector registers and the VSCR (vector status/control register), and
on POWER7, the vector-scalar registers (note that each FP register
is the high-order half of the corresponding VSR).

Signed-off-by: Paul Mackerras <paulus@samba.org>
---
 Documentation/virtual/kvm/api.txt |   11 ++++
 arch/powerpc/include/asm/kvm.h    |   20 +++++++
 arch/powerpc/kvm/book3s_hv.c      |  112 +++++++++++++++++++++++++++++++++++++
 3 files changed, 143 insertions(+)
diff mbox

Patch

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 407556f..a02f0de 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -1759,6 +1759,17 @@  registers, find a list below:
   PPC   | KVM_REG_PPC_PMC6      | 32
   PPC   | KVM_REG_PPC_PMC7      | 32
   PPC   | KVM_REG_PPC_PMC8      | 32
+  PPC   | KVM_REG_PPC_FPR0	| 64
+          ...
+  PPC   | KVM_REG_PPC_FPR31     | 64
+  PPC   | KVM_REG_PPC_VR0	| 128
+          ...
+  PPC   | KVM_REG_PPC_VR31      | 128
+  PPC   | KVM_REG_PPC_VSR0	| 128
+          ...
+  PPC   | KVM_REG_PPC_VSR31     | 128
+  PPC   | KVM_REG_PPC_FPSCR     | 64
+  PPC   | KVM_REG_PPC_VSCR      | 32
 
 4.69 KVM_GET_ONE_REG
 
diff --git a/arch/powerpc/include/asm/kvm.h b/arch/powerpc/include/asm/kvm.h
index 9557576..1466975 100644
--- a/arch/powerpc/include/asm/kvm.h
+++ b/arch/powerpc/include/asm/kvm.h
@@ -360,4 +360,24 @@  struct kvm_book3e_206_tlb_params {
 #define KVM_REG_PPC_PMC7	(KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1e)
 #define KVM_REG_PPC_PMC8	(KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1f)
 
+/* 32 floating-point registers */
+#define KVM_REG_PPC_FPR0	(KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x20)
+#define KVM_REG_PPC_FPR(n)	(KVM_REG_PPC_FPR0 + (n))
+#define KVM_REG_PPC_FPR31	(KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x3f)
+
+/* 32 VMX/Altivec vector registers */
+#define KVM_REG_PPC_VR0		(KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x40)
+#define KVM_REG_PPC_VR(n)	(KVM_REG_PPC_VR0 + (n))
+#define KVM_REG_PPC_VR31	(KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x5f)
+
+/* 32 double-width FP registers for VSX */
+/* High-order halves overlap with FP regs */
+#define KVM_REG_PPC_VSR0	(KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x60)
+#define KVM_REG_PPC_VSR(n)	(KVM_REG_PPC_VSR0 + (n))
+#define KVM_REG_PPC_VSR31	(KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x7f)
+
+/* FP and vector status/control registers */
+#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)
+
 #endif /* __LINUX_KVM_POWERPC_H */
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index c4b5636..32defa0 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -585,6 +585,54 @@  int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
 		i = reg->id - KVM_REG_PPC_PMC1;
 		r = put_user(vcpu->arch.pmc[i], (u32 __user *)reg->addr);
 		break;
+
+	case KVM_REG_PPC_FPR0 ... KVM_REG_PPC_FPR31:
+		i = reg->id - KVM_REG_PPC_FPR0;
+#ifdef CONFIG_VSX
+		if (cpu_has_feature(CPU_FTR_VSX)) {
+			/* VSX => FP reg i is stored in arch.vsr[2*i] */
+			r = put_user(vcpu->arch.vsr[2 * i],
+				     (u64 __user *)reg->addr);
+			break;
+		}
+#endif
+		r = put_user(vcpu->arch.fpr[i], (u64 __user *)reg->addr);
+		break;
+	case KVM_REG_PPC_FPSCR:
+		r = put_user(vcpu->arch.fpscr, (u64 __user *)reg->addr);
+		break;
+
+#ifdef CONFIG_ALTIVEC
+	case KVM_REG_PPC_VR0 ... KVM_REG_PPC_VR31:
+		r = -ENXIO;
+		if (!cpu_has_feature(CPU_FTR_ALTIVEC))
+			break;
+		i = reg->id - KVM_REG_PPC_VR0;
+		r = -EFAULT;
+		if (!copy_to_user((char __user *)reg->addr,
+				  &vcpu->arch.vr[i], sizeof(vector128)))
+			r = 0;
+		break;
+	case KVM_REG_PPC_VSCR:
+		r = -ENXIO;
+		if (!cpu_has_feature(CPU_FTR_ALTIVEC))
+			break;
+		r = put_user(vcpu->arch.vscr.u[3], (u32 __user *)reg->addr);
+		break;
+#endif
+
+#ifdef CONFIG_VSX
+	case KVM_REG_PPC_VSR0 ... KVM_REG_PPC_VSR31:
+		r = -ENXIO;
+		if (!cpu_has_feature(CPU_FTR_VSX))
+			break;
+		i = (reg->id - KVM_REG_PPC_VR0) * 2;
+		r = -EFAULT;
+		if (!copy_to_user((char __user *)reg->addr,
+				  &vcpu->arch.vsr[i], 2 * sizeof(u64)))
+			r = 0;
+		break;
+#endif
 	default:
 		break;
 	}
@@ -661,6 +709,70 @@  int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
 		if (!r)
 			vcpu->arch.pmc[i] = wval;
 		break;
+
+	case KVM_REG_PPC_FPR0 ... KVM_REG_PPC_FPR31:
+		i = reg->id - KVM_REG_PPC_FPR0;
+		r = get_user(val, (u64 __user *)reg->addr);
+		if (r)
+			break;
+#ifdef CONFIG_VSX
+		if (cpu_has_feature(CPU_FTR_VSX)) {
+			/* VSX => FP reg i is stored in arch.vsr[2*i] */
+			vcpu->arch.vsr[2 * i] = val;
+			break;
+		}
+#endif
+		vcpu->arch.fpr[i] = r;
+		break;
+	case KVM_REG_PPC_FPSCR:
+		r = get_user(val, (u64 __user *)reg->addr);
+		if (!r)
+			vcpu->arch.fpscr = val;
+		break;
+
+#ifdef CONFIG_ALTIVEC
+	case KVM_REG_PPC_VR0 ... KVM_REG_PPC_VR31: {
+		vector128 tmp;
+
+		r = -ENXIO;
+		if (!cpu_has_feature(CPU_FTR_ALTIVEC))
+			break;
+		i = reg->id - KVM_REG_PPC_VR0;
+		r = -EFAULT;
+		if (copy_from_user(&tmp, (char __user *)reg->addr,
+				   sizeof(vector128)))
+			break;
+		r = 0;
+		vcpu->arch.vr[i] = tmp;
+		break;
+	}
+	case KVM_REG_PPC_VSCR:
+		r = -ENXIO;
+		if (!cpu_has_feature(CPU_FTR_ALTIVEC))
+			break;
+		r = get_user(wval, (u32 __user *)reg->addr);
+		if (!r)
+			vcpu->arch.vscr.u[3] = wval;
+		break;
+#endif
+#ifdef CONFIG_VSX
+	case KVM_REG_PPC_VSR0 ... KVM_REG_PPC_VSR31: {
+		u64 tmp[2];
+
+		r = -ENXIO;
+		if (!cpu_has_feature(CPU_FTR_VSX))
+			break;
+		i = (reg->id - KVM_REG_PPC_VSR0) * 2;
+		r = -EFAULT;
+		if (copy_from_user(tmp, (char __user *)reg->addr,
+				   2 * sizeof(u64)))
+			break;
+		r = 0;
+		vcpu->arch.vsr[i] = tmp[0];
+		vcpu->arch.vsr[i + 1] = tmp[1];
+		break;
+	}
+#endif
 	default:
 		break;
 	}