[RFC 5/5] KVM: ARM: Access all registers via KVM_GET_ONE_REG/KVM_SET_ONE_REG.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



No structures at all any more.

Note: the encoding of general registers makes sense, but it's not
completely logical so the code is quite messy.  It would actually be
far neater to expose the struct kvm_vcpu_regs, and use offsets into
that as the ABI.

Peter?

Signed-off-by: Rusty Russell <rusty.russell@xxxxxxxxxx>

diff --git a/arch/arm/include/asm/kvm.h b/arch/arm/include/asm/kvm.h
index a3daa67..1f7190b 100644
--- a/arch/arm/include/asm/kvm.h
+++ b/arch/arm/include/asm/kvm.h
@@ -96,4 +96,28 @@ struct kvm_arch_memory_slot {
 #define KVM_REG_ARM_32_CRN_START	11	/* Mask: 0x00007800 */
 #define KVM_REG_ARM_32_CRN_LEN		4
 
+/* Normal registers are mapped as coprocessor 0. */
+#define KVM_REG_ARM_CORE		(0x0000 << KVM_REG_ARM_COPROC_START)
+#define KVM_REG_ARM_CORE_REG_START	0	/* Mask: 0x000000FF */
+#define KVM_REG_ARM_CORE_REG_LEN	8
+#define KVM_REG_ARM_CORE_MODE_START	8	/* Mask: 0x00002F00 */
+#define KVM_REG_ARM_CORE_MODE_LEN	6
+
+/* Registers r0-r15 are just 0 to 15 as expected. */
+#define KVM_REG_ARM_CORE_REG_PC		0x0000000F
+#define KVM_REG_ARM_CORE_REG_SPSR	0x00000010
+#define KVM_REG_ARM_CORE_REG_CPSR	0x00000011
+
+/* Banked registers appear in multiple modes. */
+#define KVM_REG_ARM_CORE_MODE_USR	0x00001000
+#define KVM_REG_ARM_CORE_MODE_FIQ	0x00001100
+#define KVM_REG_ARM_CORE_MODE_IRQ	0x00001200
+#define KVM_REG_ARM_CORE_MODE_SVC	0x00001300
+#define KVM_REG_ARM_CORE_MODE_MON	0x00001600
+#define KVM_REG_ARM_CORE_MODE_ABT	0x00001700
+#define KVM_REG_ARM_CORE_MODE_HYP	0x00001A00
+#define KVM_REG_ARM_CORE_MODE_UND	0x00001B00
+#define KVM_REG_ARM_CORE_MODE_SYS	0x00001F00
+#define KVM_REG_ARM_CORE_MODE_UNBANKED	0x00002000
+
 #endif /* __ARM_KVM_H__ */
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
index 64fa65f..340fada 100644
--- a/arch/arm/include/asm/kvm_host.h
+++ b/arch/arm/include/asm/kvm_host.h
@@ -192,4 +192,10 @@ static inline int kvm_test_age_hva(struct kvm *kvm, unsigned long hva)
 {
 	return 0;
 }
+
+int kvm_arm_copy_coproc_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
+unsigned long kvm_arm_num_coproc_regs(struct kvm_vcpu *vcpu);
+struct kvm_one_reg;
+int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *);
+int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *);
 #endif /* __ARM_KVM_HOST_H__ */
diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c
index 0b9e8b4..ed78a66 100644
--- a/arch/arm/kvm/coproc.c
+++ b/arch/arm/kvm/coproc.c
@@ -756,7 +756,7 @@ static int set_invariant_cp15(u64 index, void __user *uaddr)
 	return 0;
 }
 
-int kvm_arch_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
 	const struct coproc_reg *r;
 	void __user *uaddr = (void __user *)(long)reg->addr;
@@ -772,7 +772,7 @@ int kvm_arch_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	return reg_to_user(uaddr, &vcpu->arch.cp15[r->reg], reg->id);
 }
 
-int kvm_arch_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
 	const struct coproc_reg *r;
 	void __user *uaddr = (void __user *)(long)reg->addr;
@@ -876,27 +876,18 @@ static int walk_msrs(struct kvm_vcpu *vcpu, u64 __user *uind)
 	return total;
 }
 
-/**
- * kvm_arch_num_regs - how many registers do we present via KVM_GET_ONE_REG
- *
- * This is for special registers, particularly cp15.
- */
-unsigned long kvm_arch_num_regs(struct kvm_vcpu *vcpu)
+unsigned long kvm_arm_num_coproc_regs(struct kvm_vcpu *vcpu)
 {
-	return ARRAY_SIZE(invariant_cp15) + walk_msrs(vcpu, (u64 __user *)NULL);
+	return ARRAY_SIZE(invariant_cp15)
+		+ walk_msrs(vcpu, (u64 __user *)NULL);
 }
 
-/**
- * kvm_arch_copy_reg_indices - copy a series of coprocessor registers.
- *
- * This is for special registers, particularly cp15.
- */
-int kvm_arch_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
+int kvm_arm_copy_coproc_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 {
 	unsigned int i;
 	int err;
 
-	/* First give them all the invariant registers' indices. */
+	/* Then give them all the invariant registers' indices. */
 	for (i = 0; i < ARRAY_SIZE(invariant_cp15); i++) {
 		if (put_user(cp15_to_index(&invariant_cp15[i]), uindices))
 			return -EFAULT;
diff --git a/arch/arm/kvm/guest.c b/arch/arm/kvm/guest.c
index 53f72a0..a9a7d72 100644
--- a/arch/arm/kvm/guest.c
+++ b/arch/arm/kvm/guest.c
@@ -38,75 +38,256 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
 	return 0;
 }
 
-int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
+static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
+	u32 __user *uaddr = (u32 __user *)(long)reg->addr;
 	struct kvm_vcpu_regs *vcpu_regs = &vcpu->arch.regs;
+	u32 mode, r;
 
-	/*
-	 * GPRs and PSRs
-	 */
-	memcpy(regs->regs0_7, &(vcpu_regs->usr_regs[0]), sizeof(u32) * 8);
-	memcpy(regs->usr_regs8_12, &(vcpu_regs->usr_regs[8]), sizeof(u32) * 5);
-	memcpy(regs->fiq_regs8_12, &(vcpu_regs->fiq_regs[0]), sizeof(u32) * 5);
-	regs->reg13[MODE_FIQ] = vcpu_regs->fiq_regs[5];
-	regs->reg14[MODE_FIQ] = vcpu_regs->fiq_regs[6];
-	regs->reg13[MODE_IRQ] = vcpu_regs->irq_regs[0];
-	regs->reg14[MODE_IRQ] = vcpu_regs->irq_regs[1];
-	regs->reg13[MODE_SVC] = vcpu_regs->svc_regs[0];
-	regs->reg14[MODE_SVC] = vcpu_regs->svc_regs[1];
-	regs->reg13[MODE_ABT] = vcpu_regs->abt_regs[0];
-	regs->reg14[MODE_ABT] = vcpu_regs->abt_regs[1];
-	regs->reg13[MODE_UND] = vcpu_regs->und_regs[0];
-	regs->reg14[MODE_UND] = vcpu_regs->und_regs[1];
-	regs->reg13[MODE_USR] = vcpu_regs->usr_regs[13];
-	regs->reg14[MODE_USR] = vcpu_regs->usr_regs[14];
-
-	regs->spsr[MODE_FIQ]  = vcpu_regs->fiq_regs[7];
-	regs->spsr[MODE_IRQ]  = vcpu_regs->irq_regs[2];
-	regs->spsr[MODE_SVC]  = vcpu_regs->svc_regs[2];
-	regs->spsr[MODE_ABT]  = vcpu_regs->abt_regs[2];
-	regs->spsr[MODE_UND]  = vcpu_regs->und_regs[2];
-
-	regs->reg15 = vcpu_regs->pc;
-	regs->cpsr = vcpu_regs->cpsr;
+	if (KVM_REG_LEN(reg->id) != 4)
+		return -ENOENT;
 
-	return 0;
+	/* This var deliberately includes the unused bits. */
+	mode = reg->id >> KVM_REG_ARM_CORE_MODE_START;
+	r = (reg->id >> KVM_REG_ARM_CORE_REG_START)
+		& ((1 << KVM_REG_ARM_CORE_REG_LEN) - 1);
+
+	switch (mode) {
+	case KVM_REG_ARM_CORE_MODE_UNBANKED:
+		if (r < 8)
+			return put_user(vcpu_regs->usr_regs[r], uaddr);
+		else if (r == KVM_REG_ARM_CORE_REG_PC)
+			return put_user(vcpu_regs->pc, uaddr);
+		else if (r == KVM_REG_ARM_CORE_REG_CPSR)
+			return put_user(vcpu_regs->cpsr, uaddr);
+		break;
+	case KVM_REG_ARM_CORE_MODE_USR:
+		if (r >= 8 && r <= 14)
+			return put_user(vcpu_regs->usr_regs[r], uaddr);
+		break;
+	case KVM_REG_ARM_CORE_MODE_FIQ:
+		if (r >= 8 && r <= 14)
+			return put_user(vcpu_regs->fiq_regs[r - 8], uaddr);
+		if (r == KVM_REG_ARM_CORE_REG_SPSR)
+			return put_user(vcpu_regs->fiq_regs[7], uaddr);
+		break;
+	case KVM_REG_ARM_CORE_MODE_IRQ:
+		if (r == 13 || r == 14)
+			return put_user(vcpu_regs->irq_regs[r - 13], uaddr);
+		if (r == KVM_REG_ARM_CORE_REG_SPSR)
+			return put_user(vcpu_regs->irq_regs[2], uaddr);
+		break;
+	case KVM_REG_ARM_CORE_MODE_SVC:
+		if (r == 13 || r == 14)
+			return put_user(vcpu_regs->svc_regs[r - 13], uaddr);
+		if (r == KVM_REG_ARM_CORE_REG_SPSR)
+			return put_user(vcpu_regs->svc_regs[2], uaddr);
+		break;
+	case KVM_REG_ARM_CORE_MODE_ABT:
+		if (r == 13 || r == 14)
+			return put_user(vcpu_regs->abt_regs[r - 13], uaddr);
+		if (r == KVM_REG_ARM_CORE_REG_SPSR)
+			return put_user(vcpu_regs->abt_regs[2], uaddr);
+		break;
+	case KVM_REG_ARM_CORE_MODE_UND:
+		if (r == 13 || r == 14)
+			return put_user(vcpu_regs->und_regs[r - 13], uaddr);
+		if (r == KVM_REG_ARM_CORE_REG_SPSR)
+			return put_user(vcpu_regs->und_regs[2], uaddr);
+		break;
+	}
+
+	return -ENOENT;
 }
 
-int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
+static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
+	const u32 __user *uaddr = (u32 __user *)(long)reg->addr;
 	struct kvm_vcpu_regs *vcpu_regs = &vcpu->arch.regs;
+	u32 mode, r;
+
+	if (KVM_REG_LEN(reg->id) != 4)
+		return -ENOENT;
+
+	mode = reg->id >> KVM_REG_ARM_CORE_MODE_START;
+	r = (reg->id >> KVM_REG_ARM_CORE_REG_START)
+		& ((1 << KVM_REG_ARM_CORE_REG_LEN) - 1);
+
+	switch (mode) {
+	case KVM_REG_ARM_CORE_MODE_UNBANKED:
+		if (r < 8)
+			return get_user(vcpu_regs->usr_regs[r], uaddr);
+		else if (r == KVM_REG_ARM_CORE_REG_PC)
+			return get_user(vcpu_regs->pc, uaddr);
+		else if (r == KVM_REG_ARM_CORE_REG_CPSR) {
+			u32 cpsr;
+			if (get_user(cpsr, uaddr))
+				return -EFAULT;
+
+			if (__vcpu_mode(cpsr) == 0xf)
+				return -EINVAL;
+			vcpu_regs->cpsr = cpsr;
+			return 0;
+		}
+		break;
+	case KVM_REG_ARM_CORE_MODE_USR:
+		if (r >= 8 && r <= 14)
+			return get_user(vcpu_regs->usr_regs[r], uaddr);
+		break;
+	case KVM_REG_ARM_CORE_MODE_FIQ:
+		if (r >= 8 && r <= 14)
+			return get_user(vcpu_regs->fiq_regs[r - 8], uaddr);
+		if (r == KVM_REG_ARM_CORE_REG_SPSR)
+			return get_user(vcpu_regs->fiq_regs[7], uaddr);
+		break;
+	case KVM_REG_ARM_CORE_MODE_IRQ:
+		if (r == 13 || r == 14)
+			return get_user(vcpu_regs->irq_regs[r - 13], uaddr);
+		if (r == KVM_REG_ARM_CORE_REG_SPSR)
+			return get_user(vcpu_regs->irq_regs[2], uaddr);
+		break;
+	case KVM_REG_ARM_CORE_MODE_SVC:
+		if (r == 13 || r == 14)
+			return get_user(vcpu_regs->svc_regs[r - 13], uaddr);
+		if (r == KVM_REG_ARM_CORE_REG_SPSR)
+			return get_user(vcpu_regs->svc_regs[2], uaddr);
+		break;
+	case KVM_REG_ARM_CORE_MODE_ABT:
+		if (r == 13 || r == 14)
+			return get_user(vcpu_regs->abt_regs[r - 13], uaddr);
+		if (r == KVM_REG_ARM_CORE_REG_SPSR)
+			return get_user(vcpu_regs->abt_regs[2], uaddr);
+		break;
+	case KVM_REG_ARM_CORE_MODE_UND:
+		if (r == 13 || r == 14)
+			return get_user(vcpu_regs->und_regs[r - 13], uaddr);
+		if (r == KVM_REG_ARM_CORE_REG_SPSR)
+			return get_user(vcpu_regs->und_regs[2], uaddr);
+		break;
+	}
+
+	return -ENOENT;
+}
+
+int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
+{
+	return -EINVAL;
+}
+
+int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
+{
+	return -EINVAL;
+}
+
+static unsigned long num_core_regs(void)
+{
+	return sizeof(struct kvm_vcpu_regs) / sizeof(u32);
+}
+
+/**
+ * kvm_arch_num_regs - how many registers do we present via KVM_GET_ONE_REG
+ *
+ * This is for all registers.
+ */
+unsigned long kvm_arch_num_regs(struct kvm_vcpu *vcpu)
+{
+	return num_core_regs() + kvm_arm_num_coproc_regs(vcpu);
+}
+
+/**
+ * kvm_arch_copy_reg_indices - get indices of all registers.
+ *
+ * We do core registers right here, then we apppend coproc regs.
+ */
+int kvm_arch_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
+{
+	unsigned int i;
+	u64 __user *start = uindices;
+	u64 mode;
+	const int banked_modes[] = { KVM_REG_ARM_CORE_MODE_USR,
+				     KVM_REG_ARM_CORE_MODE_SVC,
+				     KVM_REG_ARM_CORE_MODE_ABT,
+				     KVM_REG_ARM_CORE_MODE_UND,
+				     KVM_REG_ARM_CORE_MODE_IRQ,
+				     KVM_REG_ARM_CORE_MODE_FIQ,
+	};
+
+	/* R0 - R7 (unbanked) */
+	mode = KVM_REG_ARM|KVM_REG_ARM_CORE_MODE_UNBANKED;
+	for (i = 0; i <= 7; i++) {
+		if (put_user(mode|i, uindices))
+			return -EFAULT;
+		uindices++;
+	}
+	/* PC and CPSR (unbanked) */
+	if (put_user(mode|KVM_REG_ARM_CORE_REG_PC, uindices))
+		return -EFAULT;
+	uindices++;
+	if (put_user(mode|KVM_REG_ARM_CORE_REG_CPSR, uindices))
+		return -EFAULT;
+	uindices++;
+
+	/* USR and FIQ have banked R8-R12. */
+	mode = KVM_REG_ARM|KVM_REG_ARM_CORE_MODE_USR;
+	for (i = 8; i <= 12; i++) {
+		if (put_user(mode|i, uindices))
+			return -EFAULT;
+		uindices++;
+	}
+	mode = KVM_REG_ARM|KVM_REG_ARM_CORE_MODE_FIQ;
+	for (i = 8; i <= 12; i++) {
+		if (put_user(mode|i, uindices))
+			return -EFAULT;
+		uindices++;
+	}
 
-	if (__vcpu_mode(regs->cpsr) == 0xf)
+	/* Banked SP, LR and SPSR for each mode (no SPSR for USR mode though) */
+	for (i = 0; i < ARRAY_SIZE(banked_modes); i++) {
+		mode = KVM_REG_ARM|banked_modes[i];
+		if (put_user(mode|13, uindices))
+			return -EFAULT;
+		uindices++;
+		if (put_user(mode|14, uindices))
+			return -EFAULT;
+		uindices++;
+		if (banked_modes[i] != KVM_REG_ARM_CORE_MODE_USR) {
+			if (put_user(mode|KVM_REG_ARM_CORE_REG_SPSR, uindices))
+				return -EFAULT;
+			uindices++;
+		}
+	}
+
+	BUG_ON(start - uindices != num_core_regs());
+
+	return kvm_arm_copy_coproc_indices(vcpu, uindices);
+}
+
+int kvm_arch_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+	/* We currently only use lower 32 bits for arch-specific stuff. */
+	if ((reg->id & KVM_REG_ARCH_MASK & ~KVM_REG_SIZE_MASK & 0xFFFFFFFFUL)
+	    != KVM_REG_ARM)
 		return -EINVAL;
 
-	memcpy(&(vcpu_regs->usr_regs[0]), regs->regs0_7, sizeof(u32) * 8);
-	memcpy(&(vcpu_regs->usr_regs[8]), regs->usr_regs8_12, sizeof(u32) * 5);
-	memcpy(&(vcpu_regs->fiq_regs[0]), regs->fiq_regs8_12, sizeof(u32) * 5);
-
-	vcpu_regs->fiq_regs[5] = regs->reg13[MODE_FIQ];
-	vcpu_regs->fiq_regs[6] = regs->reg14[MODE_FIQ];
-	vcpu_regs->irq_regs[0] = regs->reg13[MODE_IRQ];
-	vcpu_regs->irq_regs[1] = regs->reg14[MODE_IRQ];
-	vcpu_regs->svc_regs[0] = regs->reg13[MODE_SVC];
-	vcpu_regs->svc_regs[1] = regs->reg14[MODE_SVC];
-	vcpu_regs->abt_regs[0] = regs->reg13[MODE_ABT];
-	vcpu_regs->abt_regs[1] = regs->reg14[MODE_ABT];
-	vcpu_regs->und_regs[0] = regs->reg13[MODE_UND];
-	vcpu_regs->und_regs[1] = regs->reg14[MODE_UND];
-	vcpu_regs->usr_regs[13] = regs->reg13[MODE_USR];
-	vcpu_regs->usr_regs[14] = regs->reg14[MODE_USR];
-
-	vcpu_regs->fiq_regs[7] = regs->spsr[MODE_FIQ];
-	vcpu_regs->irq_regs[2] = regs->spsr[MODE_IRQ];
-	vcpu_regs->svc_regs[2] = regs->spsr[MODE_SVC];
-	vcpu_regs->abt_regs[2] = regs->spsr[MODE_ABT];
-	vcpu_regs->und_regs[2] = regs->spsr[MODE_UND];
-
-	vcpu_regs->pc = regs->reg15;
-	vcpu_regs->cpsr = regs->cpsr;
+	/* Coprocessor 0 means we want a core register. */
+	if ((u32)reg->id >> KVM_REG_ARM_COPROC_START == 0)
+		return get_core_reg(vcpu, reg);
 
-	return 0;
+	return kvm_arm_coproc_get_reg(vcpu, reg);
+}
+
+int kvm_arch_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+	/* We currently only use lower 32 bits for arch-specific stuff. */
+	if ((reg->id & KVM_REG_ARCH_MASK & ~KVM_REG_SIZE_MASK & 0xFFFFFFFFUL)
+	    != KVM_REG_ARM)
+		return -EINVAL;
+
+	/* Coprocessor 0 means we want a core register. */
+	if ((u32)reg->id >> KVM_REG_ARM_COPROC_START == 0)
+		return set_core_reg(vcpu, reg);
+
+	return kvm_arm_coproc_set_reg(vcpu, reg);
 }
 
 int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux