aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390
diff options
context:
space:
mode:
authorChristian Borntraeger <borntraeger@de.ibm.com>2011-07-24 04:48:17 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2011-07-24 04:48:20 -0400
commitbb25b9ba3e33e941dc48048d0a784e6a05e5648a (patch)
tree687caacfbe9165dd80d2e29af4b68ce32216e28d /arch/s390
parentb02f0c2ea25781e0f94b4fc8f6f85582057857b3 (diff)
[S390] kvm: handle tprot intercepts
When running a kvm guest we can get intercepts for tprot, if the host page table is read-only or not populated. This patch implements the most common case (linux memory detection). This also allows host copy on write for guest memory on newer systems. Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390')
-rw-r--r--arch/s390/include/asm/kvm_host.h1
-rw-r--r--arch/s390/kvm/intercept.c1
-rw-r--r--arch/s390/kvm/kvm-s390.c1
-rw-r--r--arch/s390/kvm/kvm-s390.h1
-rw-r--r--arch/s390/kvm/priv.c49
5 files changed, 53 insertions, 0 deletions
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index cef7dbf69dfc..e9bcdca32a32 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -138,6 +138,7 @@ struct kvm_vcpu_stat {
138 u32 instruction_chsc; 138 u32 instruction_chsc;
139 u32 instruction_stsi; 139 u32 instruction_stsi;
140 u32 instruction_stfl; 140 u32 instruction_stfl;
141 u32 instruction_tprot;
141 u32 instruction_sigp_sense; 142 u32 instruction_sigp_sense;
142 u32 instruction_sigp_emergency; 143 u32 instruction_sigp_emergency;
143 u32 instruction_sigp_stop; 144 u32 instruction_sigp_stop;
diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c
index f7b6df45d8be..b5312050b224 100644
--- a/arch/s390/kvm/intercept.c
+++ b/arch/s390/kvm/intercept.c
@@ -105,6 +105,7 @@ static intercept_handler_t instruction_handlers[256] = {
105 [0xae] = kvm_s390_handle_sigp, 105 [0xae] = kvm_s390_handle_sigp,
106 [0xb2] = kvm_s390_handle_b2, 106 [0xb2] = kvm_s390_handle_b2,
107 [0xb7] = handle_lctl, 107 [0xb7] = handle_lctl,
108 [0xe5] = kvm_s390_handle_e5,
108 [0xeb] = handle_lctlg, 109 [0xeb] = handle_lctlg,
109}; 110};
110 111
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 67345ae7ce8d..123ebea72282 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -62,6 +62,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
62 { "instruction_chsc", VCPU_STAT(instruction_chsc) }, 62 { "instruction_chsc", VCPU_STAT(instruction_chsc) },
63 { "instruction_stsi", VCPU_STAT(instruction_stsi) }, 63 { "instruction_stsi", VCPU_STAT(instruction_stsi) },
64 { "instruction_stfl", VCPU_STAT(instruction_stfl) }, 64 { "instruction_stfl", VCPU_STAT(instruction_stfl) },
65 { "instruction_tprot", VCPU_STAT(instruction_tprot) },
65 { "instruction_sigp_sense", VCPU_STAT(instruction_sigp_sense) }, 66 { "instruction_sigp_sense", VCPU_STAT(instruction_sigp_sense) },
66 { "instruction_sigp_emergency", VCPU_STAT(instruction_sigp_emergency) }, 67 { "instruction_sigp_emergency", VCPU_STAT(instruction_sigp_emergency) },
67 { "instruction_sigp_stop", VCPU_STAT(instruction_sigp_stop) }, 68 { "instruction_sigp_stop", VCPU_STAT(instruction_sigp_stop) },
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h
index a7b7586626db..65e2201a555b 100644
--- a/arch/s390/kvm/kvm-s390.h
+++ b/arch/s390/kvm/kvm-s390.h
@@ -87,6 +87,7 @@ static inline void kvm_s390_vcpu_set_mem(struct kvm_vcpu *vcpu)
87 87
88/* implemented in priv.c */ 88/* implemented in priv.c */
89int kvm_s390_handle_b2(struct kvm_vcpu *vcpu); 89int kvm_s390_handle_b2(struct kvm_vcpu *vcpu);
90int kvm_s390_handle_e5(struct kvm_vcpu *vcpu);
90 91
91/* implemented in sigp.c */ 92/* implemented in sigp.c */
92int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu); 93int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu);
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index 73c47bd95db3..391626361084 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -326,3 +326,52 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu)
326 } 326 }
327 return -EOPNOTSUPP; 327 return -EOPNOTSUPP;
328} 328}
329
330static int handle_tprot(struct kvm_vcpu *vcpu)
331{
332 int base1 = (vcpu->arch.sie_block->ipb & 0xf0000000) >> 28;
333 int disp1 = (vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16;
334 int base2 = (vcpu->arch.sie_block->ipb & 0xf000) >> 12;
335 int disp2 = vcpu->arch.sie_block->ipb & 0x0fff;
336 u64 address1 = disp1 + base1 ? vcpu->arch.guest_gprs[base1] : 0;
337 u64 address2 = disp2 + base2 ? vcpu->arch.guest_gprs[base2] : 0;
338 struct vm_area_struct *vma;
339
340 vcpu->stat.instruction_tprot++;
341
342 /* we only handle the Linux memory detection case:
343 * access key == 0
344 * guest DAT == off
345 * everything else goes to userspace. */
346 if (address2 & 0xf0)
347 return -EOPNOTSUPP;
348 if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_DAT)
349 return -EOPNOTSUPP;
350
351
352 down_read(&current->mm->mmap_sem);
353 vma = find_vma(current->mm,
354 (unsigned long) __guestaddr_to_user(vcpu, address1));
355 if (!vma) {
356 up_read(&current->mm->mmap_sem);
357 return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
358 }
359
360 vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
361 if (!(vma->vm_flags & VM_WRITE) && (vma->vm_flags & VM_READ))
362 vcpu->arch.sie_block->gpsw.mask |= (1ul << 44);
363 if (!(vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_READ))
364 vcpu->arch.sie_block->gpsw.mask |= (2ul << 44);
365
366 up_read(&current->mm->mmap_sem);
367 return 0;
368}
369
370int kvm_s390_handle_e5(struct kvm_vcpu *vcpu)
371{
372 /* For e5xx... instructions we only handle TPROT */
373 if ((vcpu->arch.sie_block->ipa & 0x00ff) == 0x01)
374 return handle_tprot(vcpu);
375 return -EOPNOTSUPP;
376}
377