Add support for managing kvm virtual machines through plain aarch64 tools. No support for purecap guests just yet.
Signed-off-by: Beata Michalska beata.michalska@arm.com --- Documentation/virt/kvm/api.rst | 10 +- arch/arm64/include/uapi/asm/kvm.h | 6 +- arch/arm64/kvm/arch_timer.c | 4 +- arch/arm64/kvm/arm.c | 205 ++++++++++++++++-------- arch/arm64/kvm/guest.c | 10 +- arch/arm64/kvm/hypercalls.c | 4 +- arch/arm64/kvm/pmu-emul.c | 8 +- arch/arm64/kvm/sys_regs.c | 10 +- arch/arm64/kvm/vgic/vgic-its.c | 8 +- arch/arm64/kvm/vgic/vgic-kvm-device.c | 11 +- include/linux/kvm_host.h | 8 +- include/uapi/linux/kvm.h | 10 +- tools/arch/arm64/include/uapi/asm/kvm.h | 6 +- tools/include/uapi/linux/kvm.h | 12 +- virt/kvm/Kconfig | 2 +- virt/kvm/kvm_main.c | 99 +++++++++--- virt/kvm/vfio.c | 2 +- 17 files changed, 272 insertions(+), 143 deletions(-)
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 7025b3751027..7a8370fbb357 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -2308,8 +2308,8 @@ code being returned in a specific situation.) ::
struct kvm_one_reg { - __u64 id; - __u64 addr; + __u64 id; + __kernel_uintptr_t addr; };
Using this ioctl, a single vcpu register can be set to a specific value @@ -6160,9 +6160,9 @@ applied. #define KVM_ARM_FEATURE_ID_RANGE_SIZE (3 * 8 * 8)
struct reg_mask_range { - __u64 addr; /* Pointer to mask array */ - __u32 range; /* Requested range */ - __u32 reserved[13]; + __kernel_uintptr_t addr; /* Pointer to mask array */ + __u32 range; /* Requested range */ + __u32 reserved[13]; };
This ioctl copies the writable masks for a selected range of registers to diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index 89d2fc872d9f..4f854ef1f76d 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -532,9 +532,9 @@ struct kvm_smccc_filter { #define KVM_ARM_FEATURE_ID_RANGE_SIZE (3 * 8 * 8)
struct reg_mask_range { - __u64 addr; /* Pointer to mask array */ - __u32 range; /* Requested range */ - __u32 reserved[13]; + __kernel_uintptr_t addr; /* Pointer to mask array */ + __u32 range; /* Requested range */ + __u32 reserved[13]; };
#endif diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index 13ba691b848f..8b70eeeebf33 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -1559,7 +1559,7 @@ void kvm_timer_init_vhe(void)
int kvm_arm_timer_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) { - int __user *uaddr = (int __user *)(long)attr->addr; + int __user *uaddr = (int __user *)attr->addr; int irq, idx, ret = 0;
if (!irqchip_in_kernel(vcpu->kvm)) @@ -1611,7 +1611,7 @@ int kvm_arm_timer_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
int kvm_arm_timer_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) { - int __user *uaddr = (int __user *)(long)attr->addr; + int __user *uaddr = (int __user *)attr->addr; struct arch_timer_context *timer; int irq;
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index bc945eac681d..a49f7603f03e 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -1512,33 +1512,38 @@ long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, user_uintptr_t arg) { struct kvm_vcpu *vcpu = filp->private_data; - void __user *argp = (void __user *)arg; + void __user *argp; struct kvm_device_attr attr; long r;
- switch (ioctl) { - case KVM_ARM_VCPU_INIT: { - struct kvm_vcpu_init init; - - r = -EFAULT; - if (copy_from_user(&init, argp, sizeof(init))) - break; + argp = in_compat64_syscall() ? compat_ptr(arg) : (void __user *)arg;
- r = kvm_arch_vcpu_ioctl_vcpu_init(vcpu, &init); - break; - } - case KVM_SET_ONE_REG: - case KVM_GET_ONE_REG: { + switch (_IOC_NR(ioctl)) { + case _IOC_NR(KVM_SET_ONE_REG): + case _IOC_NR(KVM_GET_ONE_REG): { struct kvm_one_reg reg;
r = -ENOEXEC; if (unlikely(!kvm_vcpu_initialized(vcpu))) - break; + goto done;
r = -EFAULT; - if (copy_from_user(®, argp, sizeof(reg))) - break;
+ if (!in_compat64_syscall()) { + if (copy_from_user_with_ptr(®, argp, sizeof(reg))) + goto done; + } else { + struct { + __u64 id; + compat_uptr_t addr; + } __reg; + + if (copy_from_user(&__reg, argp, sizeof(__reg))) + goto done; + + reg.id = __reg.id; + reg.addr = (__kernel_uintptr_t)compat_ptr(__reg.addr); + } /* * We could owe a reset due to PSCI. Handle the pending reset * here to ensure userspace register accesses are ordered after @@ -1547,10 +1552,47 @@ long kvm_arch_vcpu_ioctl(struct file *filp, if (kvm_check_request(KVM_REQ_VCPU_RESET, vcpu)) kvm_reset_vcpu(vcpu);
- if (ioctl == KVM_SET_ONE_REG) + if (_IOC_NR(ioctl) == _IOC_NR(KVM_SET_ONE_REG)) r = kvm_arm_set_reg(vcpu, ®); else r = kvm_arm_get_reg(vcpu, ®); + + goto done; + } + case _IOC_NR(KVM_SET_DEVICE_ATTR): + case _IOC_NR(KVM_GET_DEVICE_ATTR): + case _IOC_NR(KVM_HAS_DEVICE_ATTR): { + r = -EFAULT; + + if (!in_compat64_syscall()) { + if (copy_from_user_with_ptr(&attr, argp, sizeof(attr))) + goto done; + } else { + r = kvm_get_device_attr_compat(ioctl, arg, &attr); + if (r) goto done; + } + + if (_IOC_NR(ioctl) == _IOC_NR(KVM_SET_DEVICE_ATTR)) + r = kvm_arm_vcpu_set_attr(vcpu, &attr); + else if (_IOC_NR(ioctl) == _IOC_NR(KVM_GET_DEVICE_ATTR)) + r = kvm_arm_vcpu_get_attr(vcpu, &attr); + else + r = kvm_arm_vcpu_has_attr(vcpu, &attr); + goto done; + } + default: + break; + } + + switch (ioctl) { + case KVM_ARM_VCPU_INIT: { + struct kvm_vcpu_init init; + + r = -EFAULT; + if (copy_from_user(&init, argp, sizeof(init))) + break; + + r = kvm_arch_vcpu_ioctl_vcpu_init(vcpu, &init); break; } case KVM_GET_REG_LIST: { @@ -1579,27 +1621,6 @@ long kvm_arch_vcpu_ioctl(struct file *filp, r = kvm_arm_copy_reg_indices(vcpu, user_list->reg); break; } - case KVM_SET_DEVICE_ATTR: { - r = -EFAULT; - if (copy_from_user(&attr, argp, sizeof(attr))) - break; - r = kvm_arm_vcpu_set_attr(vcpu, &attr); - break; - } - case KVM_GET_DEVICE_ATTR: { - r = -EFAULT; - if (copy_from_user(&attr, argp, sizeof(attr))) - break; - r = kvm_arm_vcpu_get_attr(vcpu, &attr); - break; - } - case KVM_HAS_DEVICE_ATTR: { - r = -EFAULT; - if (copy_from_user(&attr, argp, sizeof(attr))) - break; - r = kvm_arm_vcpu_has_attr(vcpu, &attr); - break; - } case KVM_GET_VCPU_EVENTS: { struct kvm_vcpu_events events;
@@ -1633,7 +1654,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp, default: r = -EINVAL; } - +done: return r; }
@@ -1678,9 +1699,88 @@ static int kvm_vm_set_attr(struct kvm *kvm, struct kvm_device_attr *attr) int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, user_uintptr_t arg) { struct kvm *kvm = filp->private_data; - void __user *argp = (void __user *)arg; + void __user *argp; struct kvm_device_attr attr;
+ argp = in_compat64_syscall() ? compat_ptr(arg) : (void __user *)arg; + + switch (_IOC_NR(ioctl)) { + case _IOC_NR(KVM_ARM_MTE_COPY_TAGS): { + struct kvm_arm_copy_mte_tags copy_tags; + + if (!in_compat64_syscall()) { + if (copy_from_user_with_ptr(©_tags, argp, + sizeof(copy_tags))) + return -EFAULT; + } else { + struct { + __u64 guest_ipa; + __u64 length; + compat_uptr_t addr; + __u64 flags; + __u64 reserved[2]; + } __copy_tags; + + if (copy_from_user(&__copy_tags, argp, + sizeof(__copy_tags))) + return -EFAULT; + + copy_tags.guest_ipa = __copy_tags.guest_ipa; + copy_tags.length = __copy_tags.length; + copy_tags.addr = compat_ptr(__copy_tags.addr); + copy_tags.flags = __copy_tags.flags; + + memcpy(copy_tags.reserved, __copy_tags.reserved, + sizeof(__copy_tags.reserved)); + } + + return kvm_vm_ioctl_mte_copy_tags(kvm, ©_tags); + } + case _IOC_NR(KVM_HAS_DEVICE_ATTR): + case _IOC_NR(KVM_SET_DEVICE_ATTR): { + if (!in_compat64_syscall()) { + if (copy_from_user_with_ptr(&attr, argp, sizeof(attr))) + return -EFAULT; + } else { + int ret; + + ret = kvm_get_device_attr_compat(ioctl, arg, &attr); + if (ret) + return ret; + } + + return _IOC_NR(ioctl) == _IOC_NR(KVM_HAS_DEVICE_ATTR) + ? kvm_vm_has_attr(kvm, &attr) + : kvm_vm_set_attr(kvm, &attr); + } + case _IOC_NR(KVM_ARM_GET_REG_WRITABLE_MASKS): { + struct reg_mask_range range; + + if (!in_compat64_syscall()) { + if (copy_from_user_with_ptr(&range, argp, sizeof(range))) + return -EFAULT; + } else { + struct reg_mask_range { + compat_uptr_t addr; + __u32 range; + __u32 reserved[13]; + } __range; + + if (copy_from_user(&__range, argp, sizeof(__range))) + return -EFAULT; + + range.addr = (__kernel_uintptr_t)compat_ptr(__range.addr); + range.range = __range.range; + + memcpy(range.reserved, __range.reserved, + sizeof(__range.reserved)); + } + return kvm_vm_ioctl_get_reg_writable_masks(kvm, &range); + } + default: + break; + } + switch (ioctl) { case KVM_CREATE_IRQCHIP: { int ret; @@ -1708,13 +1808,6 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, user_uintptr_t arg)
return 0; } - case KVM_ARM_MTE_COPY_TAGS: { - struct kvm_arm_copy_mte_tags copy_tags; - - if (copy_from_user(©_tags, argp, sizeof(copy_tags))) - return -EFAULT; - return kvm_vm_ioctl_mte_copy_tags(kvm, ©_tags); - } case KVM_ARM_SET_COUNTER_OFFSET: { struct kvm_arm_counter_offset offset;
@@ -1722,25 +1815,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, user_uintptr_t arg) return -EFAULT; return kvm_vm_ioctl_set_counter_offset(kvm, &offset); } - case KVM_HAS_DEVICE_ATTR: { - if (copy_from_user(&attr, argp, sizeof(attr))) - return -EFAULT; - - return kvm_vm_has_attr(kvm, &attr); - } - case KVM_SET_DEVICE_ATTR: { - if (copy_from_user(&attr, argp, sizeof(attr))) - return -EFAULT;
- return kvm_vm_set_attr(kvm, &attr); - } - case KVM_ARM_GET_REG_WRITABLE_MASKS: { - struct reg_mask_range range; - - if (copy_from_user(&range, argp, sizeof(range))) - return -EFAULT; - return kvm_vm_ioctl_get_reg_writable_masks(kvm, &range); - } default: return -EINVAL; } diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c index aaf1d4939739..13c7813ed9ca 100644 --- a/arch/arm64/kvm/guest.c +++ b/arch/arm64/kvm/guest.c @@ -191,7 +191,7 @@ static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) * array. Hence below, nr_regs is the number of entries, and * off the index in the "array". */ - __u32 __user *uaddr = (__u32 __user *)(unsigned long)reg->addr; + __u32 __user *uaddr = (__u32 __user *)reg->addr; int nr_regs = sizeof(struct kvm_regs) / sizeof(__u32); void *addr; u32 off; @@ -214,7 +214,7 @@ static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { - __u32 __user *uaddr = (__u32 __user *)(unsigned long)reg->addr; + __u32 __user *uaddr = (__u32 __user *)reg->addr; int nr_regs = sizeof(struct kvm_regs) / sizeof(__u32); __uint128_t tmp; void *valp = &tmp, *addr; @@ -628,7 +628,7 @@ static int copy_timer_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
static int set_timer_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { - void __user *uaddr = (void __user *)(long)reg->addr; + void __user *uaddr = (void __user *)reg->addr; u64 val; int ret;
@@ -641,7 +641,7 @@ static int set_timer_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
static int get_timer_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { - void __user *uaddr = (void __user *)(long)reg->addr; + void __user *uaddr = (void __user *)reg->addr; u64 val;
val = kvm_arm_timer_get_reg(vcpu, reg->id); @@ -786,7 +786,7 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) return -EINVAL;
switch (reg->id & KVM_REG_ARM_COPROC_MASK) { - case KVM_REG_ARM_CORE: return set_core_reg(vcpu, reg); + case KVM_REG_ARM_CORE: return set_core_reg(vcpu, reg); case KVM_REG_ARM_FW: case KVM_REG_ARM_FW_FEAT_BMAP: return kvm_arm_set_fw_reg(vcpu, reg); diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c index 5763d979d8ca..8d3c96be4a4f 100644 --- a/arch/arm64/kvm/hypercalls.c +++ b/arch/arm64/kvm/hypercalls.c @@ -476,7 +476,7 @@ static int get_kernel_wa_level(u64 regid) int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat; - void __user *uaddr = (void __user *)(long)reg->addr; + void __user *uaddr = (void __user *)reg->addr; u64 val;
switch (reg->id) { @@ -550,7 +550,7 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { - void __user *uaddr = (void __user *)(long)reg->addr; + void __user *uaddr = (void __user *)reg->addr; u64 val; int wa_level;
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index fe99b3dab6ce..5ff14b6dedc1 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -992,7 +992,7 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
switch (attr->attr) { case KVM_ARM_VCPU_PMU_V3_IRQ: { - int __user *uaddr = (int __user *)(long)attr->addr; + int __user *uaddr = (int __user *)attr->addr; int irq;
if (!irqchip_in_kernel(kvm)) @@ -1028,7 +1028,7 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) */ nr_events = __kvm_pmu_event_mask(pmuver) + 1;
- uaddr = (struct kvm_pmu_event_filter __user *)(long)attr->addr; + uaddr = (struct kvm_pmu_event_filter __user *)attr->addr;
if (copy_from_user(&filter, uaddr, sizeof(filter))) return -EFAULT; @@ -1066,7 +1066,7 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) return 0; } case KVM_ARM_VCPU_PMU_V3_SET_PMU: { - int __user *uaddr = (int __user *)(long)attr->addr; + int __user *uaddr = (int __user *)attr->addr; int pmu_id;
if (get_user(pmu_id, uaddr)) @@ -1085,7 +1085,7 @@ int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) { switch (attr->attr) { case KVM_ARM_VCPU_PMU_V3_IRQ: { - int __user *uaddr = (int __user *)(long)attr->addr; + int __user *uaddr = (int __user *)attr->addr; int irq;
if (!irqchip_in_kernel(vcpu->kvm)) diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 1c5d24fb7733..2146e2e8ace4 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -3541,7 +3541,7 @@ static int demux_c15_set(struct kvm_vcpu *vcpu, u64 id, void __user *uaddr) int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg, const struct sys_reg_desc table[], unsigned int num) { - u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr; + u64 __user *uaddr = (u64 __user *)reg->addr; const struct sys_reg_desc *r; u64 val; int ret; @@ -3559,13 +3559,12 @@ int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
if (!ret) ret = put_user(val, uaddr); - return ret; }
int kvm_arm_sys_reg_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { - void __user *uaddr = (void __user *)(unsigned long)reg->addr; + void __user *uaddr = (void __user *)reg->addr; int err;
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX) @@ -3582,7 +3581,7 @@ int kvm_arm_sys_reg_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg, const struct sys_reg_desc table[], unsigned int num) { - u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr; + u64 __user *uaddr = (u64 __user *)reg->addr; const struct sys_reg_desc *r; u64 val; int ret; @@ -3609,7 +3608,7 @@ int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { - void __user *uaddr = (void __user *)(unsigned long)reg->addr; + void __user *uaddr = (void __user *)reg->addr; int err;
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX) @@ -3618,7 +3617,6 @@ int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg err = set_invariant_sys_reg(reg->id, uaddr); if (err != -ENOENT) return err; - return kvm_sys_reg_set_user(vcpu, reg, sys_reg_descs, ARRAY_SIZE(sys_reg_descs)); } diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 2dad2d095160..adea62ba0fe6 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -2826,7 +2826,7 @@ static int vgic_its_set_attr(struct kvm_device *dev,
switch (attr->group) { case KVM_DEV_ARM_VGIC_GRP_ADDR: { - u64 __user *uaddr = (u64 __user *)(long)attr->addr; + u64 __user *uaddr = (u64 __user *)attr->addr; unsigned long type = (unsigned long)attr->attr; u64 addr;
@@ -2846,7 +2846,7 @@ static int vgic_its_set_attr(struct kvm_device *dev, case KVM_DEV_ARM_VGIC_GRP_CTRL: return vgic_its_ctrl(dev->kvm, its, attr->attr); case KVM_DEV_ARM_VGIC_GRP_ITS_REGS: { - u64 __user *uaddr = (u64 __user *)(long)attr->addr; + u64 __user *uaddr = (u64 __user *)attr->addr; u64 reg;
if (get_user(reg, uaddr)) @@ -2865,7 +2865,7 @@ static int vgic_its_get_attr(struct kvm_device *dev, case KVM_DEV_ARM_VGIC_GRP_ADDR: { struct vgic_its *its = dev->private; u64 addr = its->vgic_its_base; - u64 __user *uaddr = (u64 __user *)(long)attr->addr; + u64 __user *uaddr = (u64 __user *)attr->addr; unsigned long type = (unsigned long)attr->attr;
if (type != KVM_VGIC_ITS_ADDR_TYPE) @@ -2876,7 +2876,7 @@ static int vgic_its_get_attr(struct kvm_device *dev, break; } case KVM_DEV_ARM_VGIC_GRP_ITS_REGS: { - u64 __user *uaddr = (u64 __user *)(long)attr->addr; + u64 __user *uaddr = (u64 __user *)attr->addr; u64 reg; int ret;
diff --git a/arch/arm64/kvm/vgic/vgic-kvm-device.c b/arch/arm64/kvm/vgic/vgic-kvm-device.c index f48b8dab8b3d..6608a25c95bc 100644 --- a/arch/arm64/kvm/vgic/vgic-kvm-device.c +++ b/arch/arm64/kvm/vgic/vgic-kvm-device.c @@ -216,7 +216,7 @@ static int vgic_set_common_attr(struct kvm_device *dev, r = kvm_vgic_addr(dev->kvm, attr, true); return (r == -ENODEV) ? -ENXIO : r; case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: { - u32 __user *uaddr = (u32 __user *)(long)attr->addr; + u32 __user *uaddr = (u32 __user *)attr->addr; u32 val; int ret = 0;
@@ -292,7 +292,7 @@ static int vgic_get_common_attr(struct kvm_device *dev, r = kvm_vgic_addr(dev->kvm, attr, false); return (r == -ENODEV) ? -ENXIO : r; case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: { - u32 __user *uaddr = (u32 __user *)(long)attr->addr; + u32 __user *uaddr = (u32 __user *)attr->addr;
r = put_user(dev->kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS, uaddr); @@ -359,7 +359,7 @@ static int vgic_v2_attr_regs_access(struct kvm_device *dev, struct kvm_device_attr *attr, bool is_write) { - u32 __user *uaddr = (u32 __user *)(unsigned long)attr->addr; + u32 __user *uaddr = (u32 __user *)attr->addr; struct vgic_reg_attr reg_attr; gpa_t addr; struct kvm_vcpu *vcpu; @@ -533,7 +533,7 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev, }
if (uaccess && is_write) { - u32 __user *uaddr = (u32 __user *)(unsigned long)attr->addr; + u32 __user *uaddr = (u32 __user *)attr->addr; if (get_user(val, uaddr)) return -EFAULT; } @@ -552,6 +552,7 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev, goto out; }
+ switch (attr->group) { case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: ret = vgic_v3_dist_uaccess(vcpu, is_write, addr, &val); @@ -588,7 +589,7 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev, mutex_unlock(&dev->kvm->lock);
if (!ret && uaccess && !is_write) { - u32 __user *uaddr = (u32 __user *)(unsigned long)attr->addr; + u32 __user *uaddr = (u32 __user *)attr->addr; ret = put_user(val, uaddr); }
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index b011b4ec2ddf..c2f743462148 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -1215,9 +1215,10 @@ int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc, #define __kvm_get_guest(kvm, gfn, offset, v) \ ({ \ unsigned long __addr = gfn_to_hva(kvm, gfn); \ - typeof(v) __user *__uaddr = (typeof(__uaddr))(__addr + offset); \ + typeof(v) __user *__uaddr; \ int __ret = -EFAULT; \ \ + __uaddr = uaddr_to_user_ptr(__addr + offset); \ if (!kvm_is_error_hva(__addr)) \ __ret = get_user(v, __uaddr); \ __ret; \ @@ -1235,9 +1236,10 @@ int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc, #define __kvm_put_guest(kvm, gfn, offset, v) \ ({ \ unsigned long __addr = gfn_to_hva(kvm, gfn); \ - typeof(v) __user *__uaddr = (typeof(__uaddr))(__addr + offset); \ + typeof(v) __user *__uaddr; \ int __ret = -EFAULT; \ \ + __uaddr = uaddr_to_user_ptr(__addr + offset); \ if (!kvm_is_error_hva(__addr)) \ __ret = put_user(v, __uaddr); \ if (!__ret) \ @@ -1405,6 +1407,8 @@ vm_fault_t kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf);
int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext);
+int kvm_get_device_attr_compat(unsigned int ioctl, unsigned long arg, + struct kvm_device_attr *attr); void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn_offset, diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 211b86de35ac..1e80277bfef5 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1392,7 +1392,7 @@ struct kvm_reg_list {
struct kvm_one_reg { __u64 id; - __u64 addr; + __kernel_uintptr_t addr; };
#define KVM_MSI_VALID_DEVID (1U << 0) @@ -1422,10 +1422,10 @@ struct kvm_create_device { };
struct kvm_device_attr { - __u32 flags; /* no flags currently defined */ - __u32 group; /* device-defined */ - __u64 attr; /* group-defined */ - __u64 addr; /* userspace address of attr data */ + __u32 flags; /* no flags currently defined */ + __u32 group; /* device-defined */ + __u64 attr; /* group-defined */ + __kernel_uintptr_t addr; /* userspace address of attr data */ };
#define KVM_DEV_VFIO_FILE 1 diff --git a/tools/arch/arm64/include/uapi/asm/kvm.h b/tools/arch/arm64/include/uapi/asm/kvm.h index 89d2fc872d9f..4f854ef1f76d 100644 --- a/tools/arch/arm64/include/uapi/asm/kvm.h +++ b/tools/arch/arm64/include/uapi/asm/kvm.h @@ -532,9 +532,9 @@ struct kvm_smccc_filter { #define KVM_ARM_FEATURE_ID_RANGE_SIZE (3 * 8 * 8)
struct reg_mask_range { - __u64 addr; /* Pointer to mask array */ - __u32 range; /* Requested range */ - __u32 reserved[13]; + __kernel_uintptr_t addr; /* Pointer to mask array */ + __u32 range; /* Requested range */ + __u32 reserved[13]; };
#endif diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h index 211b86de35ac..fd70c2bdf989 100644 --- a/tools/include/uapi/linux/kvm.h +++ b/tools/include/uapi/linux/kvm.h @@ -1391,8 +1391,8 @@ struct kvm_reg_list { };
struct kvm_one_reg { - __u64 id; - __u64 addr; + __u64 id; + __kernel_uintptr_t addr; };
#define KVM_MSI_VALID_DEVID (1U << 0) @@ -1422,10 +1422,10 @@ struct kvm_create_device { };
struct kvm_device_attr { - __u32 flags; /* no flags currently defined */ - __u32 group; /* device-defined */ - __u64 attr; /* group-defined */ - __u64 addr; /* userspace address of attr data */ + __u32 flags; /* no flags currently defined */ + __u32 group; /* device-defined */ + __u64 attr; /* group-defined */ + __kernel_uintptr_t addr; /* userspace address of attr data */ };
#define KVM_DEV_VFIO_FILE 1 diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig index 484d0873061c..b6ed8ed3cc4d 100644 --- a/virt/kvm/Kconfig +++ b/virt/kvm/Kconfig @@ -70,7 +70,7 @@ config KVM_GENERIC_DIRTYLOG_READ_PROTECT
config KVM_COMPAT def_bool y - depends on KVM && COMPAT && !(S390 || ARM64 || RISCV) + depends on KVM && COMPAT && !(S390 || RISCV || (ARM64 &&!ARM64_MORELLO))
config HAVE_KVM_IRQ_BYPASS bool diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index d79fd04d8b9e..bbddb168430c 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -120,7 +120,14 @@ static long kvm_vcpu_ioctl(struct file *file, unsigned int ioctl, #ifdef CONFIG_KVM_COMPAT static long kvm_vcpu_compat_ioctl(struct file *file, unsigned int ioctl, unsigned long arg); +#ifndef CONFIG_COMPAT64 #define KVM_COMPAT(c) .compat_ioctl = (c) +#define KVM_COMPAT_DIRECT(c) KVM_COMPAT(c) +#else +#define KVM_COMPAT(c) .compat_ioctl = compat_ptr_ioctl, \ + .unlocked_ioctl = (c) +#define KVM_COMPAT_DIRECT(c) .compat_ioctl = (c) +#endif #else /* * For architectures that don't implement a compat infrastructure, @@ -138,6 +145,7 @@ static int kvm_no_compat_open(struct inode *inode, struct file *file) } #define KVM_COMPAT(c) .compat_ioctl = kvm_no_compat_ioctl, \ .open = kvm_no_compat_open +#define KVM_COMPAT_DIRECT(c) KVM_COMPAT(c) #endif static int hardware_enable_all(void); static void hardware_disable_all(void); @@ -3885,7 +3893,7 @@ static struct file_operations kvm_vcpu_fops = { .unlocked_ioctl = kvm_vcpu_ioctl, .mmap = kvm_vcpu_mmap, .llseek = noop_llseek, - KVM_COMPAT(kvm_vcpu_compat_ioctl), + KVM_COMPAT_DIRECT(kvm_vcpu_compat_ioctl), };
/* @@ -4110,7 +4118,7 @@ static long kvm_vcpu_ioctl(struct file *filp, unsigned int ioctl, user_uintptr_t arg) { struct kvm_vcpu *vcpu = filp->private_data; - void __user *argp = (void __user *)arg; + void __user *argp; int r; struct kvm_fpu *fpu = NULL; struct kvm_sregs *kvm_sregs = NULL; @@ -4121,6 +4129,7 @@ static long kvm_vcpu_ioctl(struct file *filp, if (unlikely(_IOC_TYPE(ioctl) != KVMIO)) return -EINVAL;
+ argp = in_compat64_syscall() ? compat_ptr(arg) : (void __user *)arg; /* * Some architectures have vcpu ioctls that are asynchronous to vcpu * execution; mutex_lock() would break them. @@ -4317,6 +4326,45 @@ static long kvm_vcpu_ioctl(struct file *filp, return r; }
+int kvm_get_device_attr_compat(unsigned int ioctl, unsigned long arg, + struct kvm_device_attr *attr) +{ + int ret = -EINVAL; +#ifdef CONFIG_KVM_COMPAT + if (!in_compat64_syscall()) + return ret; + + switch (_IOC_NR(ioctl)) { + case _IOC_NR(KVM_HAS_DEVICE_ATTR): + case _IOC_NR(KVM_GET_DEVICE_ATTR): + case _IOC_NR(KVM_SET_DEVICE_ATTR): { + struct { + __u32 flags; + __u32 group; + __u64 attr; + compat_uptr_t addr; + } __attr; + + if (_IOC_SIZE(ioctl) != sizeof(__attr)) + break; + + ret = -EFAULT; + if (copy_from_user(&__attr, compat_ptr(arg), sizeof(__attr))) + break; + + attr->flags = __attr.flags; + attr->group = __attr.group; + attr->attr = __attr.attr; + attr->addr = (__kernel_uintptr_t)compat_ptr(__attr.addr); + ret = 0; + } + default: + break; + } +#endif + return ret; +} + #ifdef CONFIG_KVM_COMPAT static long kvm_vcpu_compat_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) @@ -4370,37 +4418,38 @@ static int kvm_device_mmap(struct file *filp, struct vm_area_struct *vma) return -ENODEV; }
-static int kvm_device_ioctl_attr(struct kvm_device *dev, +static inline int kvm_device_ioctl_attr(struct kvm_device *dev, int (*accessor)(struct kvm_device *dev, struct kvm_device_attr *attr), - user_uintptr_t arg) + struct kvm_device_attr *attr) { - struct kvm_device_attr attr; - if (!accessor) return -EPERM;
- if (copy_from_user(&attr, (void __user *)arg, sizeof(attr))) - return -EFAULT; - - return accessor(dev, &attr); + return accessor(dev, attr); }
static long kvm_device_ioctl(struct file *filp, unsigned int ioctl, user_uintptr_t arg) { struct kvm_device *dev = filp->private_data; + struct kvm_device_attr attr; + int ret;
if (dev->kvm->mm != current->mm || dev->kvm->vm_dead) return -EIO;
- switch (ioctl) { - case KVM_SET_DEVICE_ATTR: - return kvm_device_ioctl_attr(dev, dev->ops->set_attr, arg); - case KVM_GET_DEVICE_ATTR: - return kvm_device_ioctl_attr(dev, dev->ops->get_attr, arg); - case KVM_HAS_DEVICE_ATTR: - return kvm_device_ioctl_attr(dev, dev->ops->has_attr, arg); + ret = kvm_get_device_attr_compat(ioctl, arg, &attr); + if (ret) + return ret; + + switch (_IOC_NR(ioctl)) { + case _IOC_NR(KVM_SET_DEVICE_ATTR): + return kvm_device_ioctl_attr(dev, dev->ops->set_attr, &attr); + case _IOC_NR(KVM_GET_DEVICE_ATTR): + return kvm_device_ioctl_attr(dev, dev->ops->get_attr, &attr); + case _IOC_NR(KVM_HAS_DEVICE_ATTR): + return kvm_device_ioctl_attr(dev, dev->ops->has_attr, &attr); default: if (dev->ops->ioctl) return dev->ops->ioctl(dev, ioctl, arg); @@ -4787,11 +4836,13 @@ static long kvm_vm_ioctl(struct file *filp, unsigned int ioctl, user_uintptr_t arg) { struct kvm *kvm = filp->private_data; - void __user *argp = (void __user *)arg; + void __user *argp; int r;
if (kvm->mm != current->mm || kvm->vm_dead) return -EIO; + + argp = in_compat64_syscall() ? compat_ptr(arg) : (void __user *)arg; switch (ioctl) { case KVM_CREATE_VCPU: r = kvm_vm_ioctl_create_vcpu(kvm, arg); @@ -5014,13 +5065,13 @@ static long kvm_vm_compat_ioctl(struct file *filp, if (r != -ENOTTY) return r;
- switch (ioctl) { + switch (_IOC_NR(ioctl)) { #ifdef CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT - case KVM_CLEAR_DIRTY_LOG: { + case _IOC_NR(KVM_CLEAR_DIRTY_LOG): { struct compat_kvm_clear_dirty_log compat_log; struct kvm_clear_dirty_log log;
- if (copy_from_user(&compat_log, (void __user *)arg, + if (copy_from_user(&compat_log, compat_ptr(arg), sizeof(compat_log))) return -EFAULT; log.slot = compat_log.slot; @@ -5033,11 +5084,11 @@ static long kvm_vm_compat_ioctl(struct file *filp, break; } #endif - case KVM_GET_DIRTY_LOG: { + case _IOC_NR(KVM_GET_DIRTY_LOG): { struct compat_kvm_dirty_log compat_log; struct kvm_dirty_log log;
- if (copy_from_user(&compat_log, (void __user *)arg, + if (copy_from_user(&compat_log, compat_ptr(arg), sizeof(compat_log))) return -EFAULT; log.slot = compat_log.slot; @@ -5059,7 +5110,7 @@ static struct file_operations kvm_vm_fops = { .release = kvm_vm_release, .unlocked_ioctl = kvm_vm_ioctl, .llseek = noop_llseek, - KVM_COMPAT(kvm_vm_compat_ioctl), + KVM_COMPAT_DIRECT(kvm_vm_compat_ioctl), };
bool file_is_kvm(struct file *file) diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c index ca24ce120906..304abc96272a 100644 --- a/virt/kvm/vfio.c +++ b/virt/kvm/vfio.c @@ -303,7 +303,7 @@ static int kvm_vfio_set_attr(struct kvm_device *dev, switch (attr->group) { case KVM_DEV_VFIO_FILE: return kvm_vfio_set_file(dev, attr->attr, - u64_to_user_ptr(attr->addr)); + as_user_ptr(attr->addr)); }
return -ENXIO;