diff options
author | Christian Borntraeger <borntraeger@de.ibm.com> | 2011-07-24 04:48:17 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2011-07-24 04:48:20 -0400 |
commit | bb25b9ba3e33e941dc48048d0a784e6a05e5648a (patch) | |
tree | 687caacfbe9165dd80d2e29af4b68ce32216e28d /arch/s390 | |
parent | b02f0c2ea25781e0f94b4fc8f6f85582057857b3 (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.h | 1 | ||||
-rw-r--r-- | arch/s390/kvm/intercept.c | 1 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.c | 1 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.h | 1 | ||||
-rw-r--r-- | arch/s390/kvm/priv.c | 49 |
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 */ |
89 | int kvm_s390_handle_b2(struct kvm_vcpu *vcpu); | 89 | int kvm_s390_handle_b2(struct kvm_vcpu *vcpu); |
90 | int kvm_s390_handle_e5(struct kvm_vcpu *vcpu); | ||
90 | 91 | ||
91 | /* implemented in sigp.c */ | 92 | /* implemented in sigp.c */ |
92 | int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu); | 93 | int 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 | |||
330 | static 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(¤t->mm->mmap_sem); | ||
353 | vma = find_vma(current->mm, | ||
354 | (unsigned long) __guestaddr_to_user(vcpu, address1)); | ||
355 | if (!vma) { | ||
356 | up_read(¤t->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(¤t->mm->mmap_sem); | ||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | int 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 | |||