aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/kvm
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390/kvm')
-rw-r--r--arch/s390/kvm/gaccess.c22
-rw-r--r--arch/s390/kvm/gaccess.h2
-rw-r--r--arch/s390/kvm/kvm-s390.c74
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 */
869int 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
158int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, 158int 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);
160int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar,
161 unsigned long length, int is_write);
160 162
161int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data, 163int 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
43struct kvm_stats_debugfs_item debugfs_entries[] = { 46struct 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
2194static 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
2188long kvm_arch_vcpu_ioctl(struct file *filp, 2253long 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 }