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