diff options
Diffstat (limited to 'arch/s390/kvm')
-rw-r--r-- | arch/s390/kvm/gaccess.c | 22 | ||||
-rw-r--r-- | arch/s390/kvm/gaccess.h | 2 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.c | 74 |
3 files changed, 98 insertions, 0 deletions
diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c index ea38d716e24d..a7559f7207df 100644 --- a/arch/s390/kvm/gaccess.c +++ b/arch/s390/kvm/gaccess.c | |||
@@ -864,6 +864,28 @@ int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar, | |||
864 | } | 864 | } |
865 | 865 | ||
866 | /** | 866 | /** |
867 | * check_gva_range - test a range of guest virtual addresses for accessibility | ||
868 | */ | ||
869 | int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar, | ||
870 | unsigned long length, int is_write) | ||
871 | { | ||
872 | unsigned long gpa; | ||
873 | unsigned long currlen; | ||
874 | int rc = 0; | ||
875 | |||
876 | ipte_lock(vcpu); | ||
877 | while (length > 0 && !rc) { | ||
878 | currlen = min(length, PAGE_SIZE - (gva % PAGE_SIZE)); | ||
879 | rc = guest_translate_address(vcpu, gva, ar, &gpa, is_write); | ||
880 | gva += currlen; | ||
881 | length -= currlen; | ||
882 | } | ||
883 | ipte_unlock(vcpu); | ||
884 | |||
885 | return rc; | ||
886 | } | ||
887 | |||
888 | /** | ||
867 | * kvm_s390_check_low_addr_prot_real - check for low-address protection | 889 | * kvm_s390_check_low_addr_prot_real - check for low-address protection |
868 | * @gra: Guest real address | 890 | * @gra: Guest real address |
869 | * | 891 | * |
diff --git a/arch/s390/kvm/gaccess.h b/arch/s390/kvm/gaccess.h index 835e557dabf4..ef03726cc661 100644 --- a/arch/s390/kvm/gaccess.h +++ b/arch/s390/kvm/gaccess.h | |||
@@ -157,6 +157,8 @@ int read_guest_lc(struct kvm_vcpu *vcpu, unsigned long gra, void *data, | |||
157 | 157 | ||
158 | int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, | 158 | int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, |
159 | ar_t ar, unsigned long *gpa, int write); | 159 | ar_t ar, unsigned long *gpa, int write); |
160 | int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar, | ||
161 | unsigned long length, int is_write); | ||
160 | 162 | ||
161 | int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data, | 163 | int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data, |
162 | unsigned long len, int write); | 164 | unsigned long len, int write); |
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 610e90afadf2..b7ecef98b668 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/random.h> | 25 | #include <linux/random.h> |
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | #include <linux/timer.h> | 27 | #include <linux/timer.h> |
28 | #include <linux/vmalloc.h> | ||
28 | #include <asm/asm-offsets.h> | 29 | #include <asm/asm-offsets.h> |
29 | #include <asm/lowcore.h> | 30 | #include <asm/lowcore.h> |
30 | #include <asm/pgtable.h> | 31 | #include <asm/pgtable.h> |
@@ -38,6 +39,8 @@ | |||
38 | #include "trace.h" | 39 | #include "trace.h" |
39 | #include "trace-s390.h" | 40 | #include "trace-s390.h" |
40 | 41 | ||
42 | #define MEM_OP_MAX_SIZE 65536 /* Maximum transfer size for KVM_S390_MEM_OP */ | ||
43 | |||
41 | #define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU | 44 | #define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU |
42 | 45 | ||
43 | struct kvm_stats_debugfs_item debugfs_entries[] = { | 46 | struct kvm_stats_debugfs_item debugfs_entries[] = { |
@@ -177,6 +180,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) | |||
177 | case KVM_CAP_S390_USER_SIGP: | 180 | case KVM_CAP_S390_USER_SIGP: |
178 | r = 1; | 181 | r = 1; |
179 | break; | 182 | break; |
183 | case KVM_CAP_S390_MEM_OP: | ||
184 | r = MEM_OP_MAX_SIZE; | ||
185 | break; | ||
180 | case KVM_CAP_NR_VCPUS: | 186 | case KVM_CAP_NR_VCPUS: |
181 | case KVM_CAP_MAX_VCPUS: | 187 | case KVM_CAP_MAX_VCPUS: |
182 | r = KVM_MAX_VCPUS; | 188 | r = KVM_MAX_VCPUS; |
@@ -2185,6 +2191,65 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, | |||
2185 | return r; | 2191 | return r; |
2186 | } | 2192 | } |
2187 | 2193 | ||
2194 | static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu, | ||
2195 | struct kvm_s390_mem_op *mop) | ||
2196 | { | ||
2197 | void __user *uaddr = (void __user *)mop->buf; | ||
2198 | void *tmpbuf = NULL; | ||
2199 | int r, srcu_idx; | ||
2200 | const u64 supported_flags = KVM_S390_MEMOP_F_INJECT_EXCEPTION | ||
2201 | | KVM_S390_MEMOP_F_CHECK_ONLY; | ||
2202 | |||
2203 | if (mop->flags & ~supported_flags) | ||
2204 | return -EINVAL; | ||
2205 | |||
2206 | if (mop->size > MEM_OP_MAX_SIZE) | ||
2207 | return -E2BIG; | ||
2208 | |||
2209 | if (!(mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY)) { | ||
2210 | tmpbuf = vmalloc(mop->size); | ||
2211 | if (!tmpbuf) | ||
2212 | return -ENOMEM; | ||
2213 | } | ||
2214 | |||
2215 | srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); | ||
2216 | |||
2217 | switch (mop->op) { | ||
2218 | case KVM_S390_MEMOP_LOGICAL_READ: | ||
2219 | if (mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY) { | ||
2220 | r = check_gva_range(vcpu, mop->gaddr, mop->ar, mop->size, false); | ||
2221 | break; | ||
2222 | } | ||
2223 | r = read_guest(vcpu, mop->gaddr, mop->ar, tmpbuf, mop->size); | ||
2224 | if (r == 0) { | ||
2225 | if (copy_to_user(uaddr, tmpbuf, mop->size)) | ||
2226 | r = -EFAULT; | ||
2227 | } | ||
2228 | break; | ||
2229 | case KVM_S390_MEMOP_LOGICAL_WRITE: | ||
2230 | if (mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY) { | ||
2231 | r = check_gva_range(vcpu, mop->gaddr, mop->ar, mop->size, true); | ||
2232 | break; | ||
2233 | } | ||
2234 | if (copy_from_user(tmpbuf, uaddr, mop->size)) { | ||
2235 | r = -EFAULT; | ||
2236 | break; | ||
2237 | } | ||
2238 | r = write_guest(vcpu, mop->gaddr, mop->ar, tmpbuf, mop->size); | ||
2239 | break; | ||
2240 | default: | ||
2241 | r = -EINVAL; | ||
2242 | } | ||
2243 | |||
2244 | srcu_read_unlock(&vcpu->kvm->srcu, srcu_idx); | ||
2245 | |||
2246 | if (r > 0 && (mop->flags & KVM_S390_MEMOP_F_INJECT_EXCEPTION) != 0) | ||
2247 | kvm_s390_inject_prog_irq(vcpu, &vcpu->arch.pgm); | ||
2248 | |||
2249 | vfree(tmpbuf); | ||
2250 | return r; | ||
2251 | } | ||
2252 | |||
2188 | long kvm_arch_vcpu_ioctl(struct file *filp, | 2253 | long kvm_arch_vcpu_ioctl(struct file *filp, |
2189 | unsigned int ioctl, unsigned long arg) | 2254 | unsigned int ioctl, unsigned long arg) |
2190 | { | 2255 | { |
@@ -2284,6 +2349,15 @@ long kvm_arch_vcpu_ioctl(struct file *filp, | |||
2284 | r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap); | 2349 | r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap); |
2285 | break; | 2350 | break; |
2286 | } | 2351 | } |
2352 | case KVM_S390_MEM_OP: { | ||
2353 | struct kvm_s390_mem_op mem_op; | ||
2354 | |||
2355 | if (copy_from_user(&mem_op, argp, sizeof(mem_op)) == 0) | ||
2356 | r = kvm_s390_guest_mem_op(vcpu, &mem_op); | ||
2357 | else | ||
2358 | r = -EFAULT; | ||
2359 | break; | ||
2360 | } | ||
2287 | default: | 2361 | default: |
2288 | r = -ENOTTY; | 2362 | r = -ENOTTY; |
2289 | } | 2363 | } |