|
@@ -25,6 +25,7 @@
|
|
|
#include <linux/random.h>
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/timer.h>
|
|
|
+#include <linux/vmalloc.h>
|
|
|
#include <asm/asm-offsets.h>
|
|
|
#include <asm/lowcore.h>
|
|
|
#include <asm/pgtable.h>
|
|
@@ -38,6 +39,8 @@
|
|
|
#include "trace.h"
|
|
|
#include "trace-s390.h"
|
|
|
|
|
|
+#define MEM_OP_MAX_SIZE 65536 /* Maximum transfer size for KVM_S390_MEM_OP */
|
|
|
+
|
|
|
#define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU
|
|
|
|
|
|
struct kvm_stats_debugfs_item debugfs_entries[] = {
|
|
@@ -177,6 +180,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
|
|
case KVM_CAP_S390_USER_SIGP:
|
|
|
r = 1;
|
|
|
break;
|
|
|
+ case KVM_CAP_S390_MEM_OP:
|
|
|
+ r = MEM_OP_MAX_SIZE;
|
|
|
+ break;
|
|
|
case KVM_CAP_NR_VCPUS:
|
|
|
case KVM_CAP_MAX_VCPUS:
|
|
|
r = KVM_MAX_VCPUS;
|
|
@@ -2185,6 +2191,65 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
|
|
|
return r;
|
|
|
}
|
|
|
|
|
|
+static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu,
|
|
|
+ struct kvm_s390_mem_op *mop)
|
|
|
+{
|
|
|
+ void __user *uaddr = (void __user *)mop->buf;
|
|
|
+ void *tmpbuf = NULL;
|
|
|
+ int r, srcu_idx;
|
|
|
+ const u64 supported_flags = KVM_S390_MEMOP_F_INJECT_EXCEPTION
|
|
|
+ | KVM_S390_MEMOP_F_CHECK_ONLY;
|
|
|
+
|
|
|
+ if (mop->flags & ~supported_flags)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (mop->size > MEM_OP_MAX_SIZE)
|
|
|
+ return -E2BIG;
|
|
|
+
|
|
|
+ if (!(mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY)) {
|
|
|
+ tmpbuf = vmalloc(mop->size);
|
|
|
+ if (!tmpbuf)
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
|
|
|
+
|
|
|
+ switch (mop->op) {
|
|
|
+ case KVM_S390_MEMOP_LOGICAL_READ:
|
|
|
+ if (mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY) {
|
|
|
+ r = check_gva_range(vcpu, mop->gaddr, mop->ar, mop->size, false);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ r = read_guest(vcpu, mop->gaddr, mop->ar, tmpbuf, mop->size);
|
|
|
+ if (r == 0) {
|
|
|
+ if (copy_to_user(uaddr, tmpbuf, mop->size))
|
|
|
+ r = -EFAULT;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case KVM_S390_MEMOP_LOGICAL_WRITE:
|
|
|
+ if (mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY) {
|
|
|
+ r = check_gva_range(vcpu, mop->gaddr, mop->ar, mop->size, true);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (copy_from_user(tmpbuf, uaddr, mop->size)) {
|
|
|
+ r = -EFAULT;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ r = write_guest(vcpu, mop->gaddr, mop->ar, tmpbuf, mop->size);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ r = -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ srcu_read_unlock(&vcpu->kvm->srcu, srcu_idx);
|
|
|
+
|
|
|
+ if (r > 0 && (mop->flags & KVM_S390_MEMOP_F_INJECT_EXCEPTION) != 0)
|
|
|
+ kvm_s390_inject_prog_irq(vcpu, &vcpu->arch.pgm);
|
|
|
+
|
|
|
+ vfree(tmpbuf);
|
|
|
+ return r;
|
|
|
+}
|
|
|
+
|
|
|
long kvm_arch_vcpu_ioctl(struct file *filp,
|
|
|
unsigned int ioctl, unsigned long arg)
|
|
|
{
|
|
@@ -2284,6 +2349,15 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
|
|
|
r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap);
|
|
|
break;
|
|
|
}
|
|
|
+ case KVM_S390_MEM_OP: {
|
|
|
+ struct kvm_s390_mem_op mem_op;
|
|
|
+
|
|
|
+ if (copy_from_user(&mem_op, argp, sizeof(mem_op)) == 0)
|
|
|
+ r = kvm_s390_guest_mem_op(vcpu, &mem_op);
|
|
|
+ else
|
|
|
+ r = -EFAULT;
|
|
|
+ break;
|
|
|
+ }
|
|
|
default:
|
|
|
r = -ENOTTY;
|
|
|
}
|