diff options
| -rw-r--r-- | arch/s390/kvm/Makefile | 2 | ||||
| -rw-r--r-- | arch/s390/kvm/intercept.c | 80 | ||||
| -rw-r--r-- | arch/s390/kvm/kvm-s390.c | 46 | ||||
| -rw-r--r-- | arch/s390/kvm/kvm-s390.h | 7 | ||||
| -rw-r--r-- | include/asm-s390/kvm_host.h | 4 | ||||
| -rw-r--r-- | include/linux/kvm.h | 9 |
6 files changed, 146 insertions, 2 deletions
diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile index 0d8d1135a278..27882b35ef04 100644 --- a/arch/s390/kvm/Makefile +++ b/arch/s390/kvm/Makefile | |||
| @@ -10,5 +10,5 @@ common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o) | |||
| 10 | 10 | ||
| 11 | EXTRA_CFLAGS += -Ivirt/kvm -Iarch/s390/kvm | 11 | EXTRA_CFLAGS += -Ivirt/kvm -Iarch/s390/kvm |
| 12 | 12 | ||
| 13 | kvm-objs := $(common-objs) kvm-s390.o sie64a.o | 13 | kvm-objs := $(common-objs) kvm-s390.o sie64a.o intercept.o |
| 14 | obj-$(CONFIG_KVM) += kvm.o | 14 | obj-$(CONFIG_KVM) += kvm.o |
diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c new file mode 100644 index 000000000000..e757230b982c --- /dev/null +++ b/arch/s390/kvm/intercept.c | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | /* | ||
| 2 | * intercept.c - in-kernel handling for sie intercepts | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2008 | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License (version 2 only) | ||
| 8 | * as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * Author(s): Carsten Otte <cotte@de.ibm.com> | ||
| 11 | * Christian Borntraeger <borntraeger@de.ibm.com> | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/kvm_host.h> | ||
| 15 | #include <linux/errno.h> | ||
| 16 | #include <linux/pagemap.h> | ||
| 17 | |||
| 18 | #include <asm/kvm_host.h> | ||
| 19 | |||
| 20 | #include "kvm-s390.h" | ||
| 21 | |||
| 22 | static int handle_noop(struct kvm_vcpu *vcpu) | ||
| 23 | { | ||
| 24 | switch (vcpu->arch.sie_block->icptcode) { | ||
| 25 | case 0x10: | ||
| 26 | vcpu->stat.exit_external_request++; | ||
| 27 | break; | ||
| 28 | case 0x14: | ||
| 29 | vcpu->stat.exit_external_interrupt++; | ||
| 30 | break; | ||
| 31 | default: | ||
| 32 | break; /* nothing */ | ||
| 33 | } | ||
| 34 | return 0; | ||
| 35 | } | ||
| 36 | |||
| 37 | static int handle_stop(struct kvm_vcpu *vcpu) | ||
| 38 | { | ||
| 39 | vcpu->stat.exit_stop_request++; | ||
| 40 | VCPU_EVENT(vcpu, 3, "%s", "cpu stopped"); | ||
| 41 | atomic_clear_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags); | ||
| 42 | return -ENOTSUPP; | ||
| 43 | } | ||
| 44 | |||
| 45 | static int handle_validity(struct kvm_vcpu *vcpu) | ||
| 46 | { | ||
| 47 | int viwhy = vcpu->arch.sie_block->ipb >> 16; | ||
| 48 | vcpu->stat.exit_validity++; | ||
| 49 | if (viwhy == 0x37) { | ||
| 50 | fault_in_pages_writeable((char __user *) | ||
| 51 | vcpu->kvm->arch.guest_origin + | ||
| 52 | vcpu->arch.sie_block->prefix, | ||
| 53 | PAGE_SIZE); | ||
| 54 | return 0; | ||
| 55 | } | ||
| 56 | VCPU_EVENT(vcpu, 2, "unhandled validity intercept code %d", | ||
| 57 | viwhy); | ||
| 58 | return -ENOTSUPP; | ||
| 59 | } | ||
| 60 | |||
| 61 | static const intercept_handler_t intercept_funcs[0x48 >> 2] = { | ||
| 62 | [0x00 >> 2] = handle_noop, | ||
| 63 | [0x10 >> 2] = handle_noop, | ||
| 64 | [0x14 >> 2] = handle_noop, | ||
| 65 | [0x20 >> 2] = handle_validity, | ||
| 66 | [0x28 >> 2] = handle_stop, | ||
| 67 | }; | ||
| 68 | |||
| 69 | int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu) | ||
| 70 | { | ||
| 71 | intercept_handler_t func; | ||
| 72 | u8 code = vcpu->arch.sie_block->icptcode; | ||
| 73 | |||
| 74 | if (code & 3 || code > 0x48) | ||
| 75 | return -ENOTSUPP; | ||
| 76 | func = intercept_funcs[code >> 2]; | ||
| 77 | if (func) | ||
| 78 | return func(vcpu); | ||
| 79 | return -ENOTSUPP; | ||
| 80 | } | ||
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 6e1e1d39ae15..a906499214bb 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c | |||
| @@ -23,12 +23,17 @@ | |||
| 23 | #include <asm/lowcore.h> | 23 | #include <asm/lowcore.h> |
| 24 | #include <asm/pgtable.h> | 24 | #include <asm/pgtable.h> |
| 25 | 25 | ||
| 26 | #include "kvm-s390.h" | ||
| 26 | #include "gaccess.h" | 27 | #include "gaccess.h" |
| 27 | 28 | ||
| 28 | #define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU | 29 | #define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU |
| 29 | 30 | ||
| 30 | struct kvm_stats_debugfs_item debugfs_entries[] = { | 31 | struct kvm_stats_debugfs_item debugfs_entries[] = { |
| 31 | { "userspace_handled", VCPU_STAT(exit_userspace) }, | 32 | { "userspace_handled", VCPU_STAT(exit_userspace) }, |
| 33 | { "exit_validity", VCPU_STAT(exit_validity) }, | ||
| 34 | { "exit_stop_request", VCPU_STAT(exit_stop_request) }, | ||
| 35 | { "exit_external_request", VCPU_STAT(exit_external_request) }, | ||
| 36 | { "exit_external_interrupt", VCPU_STAT(exit_external_interrupt) }, | ||
| 32 | { NULL } | 37 | { NULL } |
| 33 | }; | 38 | }; |
| 34 | 39 | ||
| @@ -380,6 +385,7 @@ static void __vcpu_run(struct kvm_vcpu *vcpu) | |||
| 380 | 385 | ||
| 381 | int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | 386 | int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) |
| 382 | { | 387 | { |
| 388 | int rc; | ||
| 383 | sigset_t sigsaved; | 389 | sigset_t sigsaved; |
| 384 | 390 | ||
| 385 | vcpu_load(vcpu); | 391 | vcpu_load(vcpu); |
| @@ -389,7 +395,45 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | |||
| 389 | 395 | ||
| 390 | atomic_set_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags); | 396 | atomic_set_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags); |
| 391 | 397 | ||
| 392 | __vcpu_run(vcpu); | 398 | switch (kvm_run->exit_reason) { |
| 399 | case KVM_EXIT_S390_SIEIC: | ||
| 400 | vcpu->arch.sie_block->gpsw.mask = kvm_run->s390_sieic.mask; | ||
| 401 | vcpu->arch.sie_block->gpsw.addr = kvm_run->s390_sieic.addr; | ||
| 402 | break; | ||
| 403 | case KVM_EXIT_UNKNOWN: | ||
| 404 | case KVM_EXIT_S390_RESET: | ||
| 405 | break; | ||
| 406 | default: | ||
| 407 | BUG(); | ||
| 408 | } | ||
| 409 | |||
| 410 | might_sleep(); | ||
| 411 | |||
| 412 | do { | ||
| 413 | __vcpu_run(vcpu); | ||
| 414 | |||
| 415 | rc = kvm_handle_sie_intercept(vcpu); | ||
| 416 | } while (!signal_pending(current) && !rc); | ||
| 417 | |||
| 418 | if (signal_pending(current) && !rc) | ||
| 419 | rc = -EINTR; | ||
| 420 | |||
| 421 | if (rc == -ENOTSUPP) { | ||
| 422 | /* intercept cannot be handled in-kernel, prepare kvm-run */ | ||
| 423 | kvm_run->exit_reason = KVM_EXIT_S390_SIEIC; | ||
| 424 | kvm_run->s390_sieic.icptcode = vcpu->arch.sie_block->icptcode; | ||
| 425 | kvm_run->s390_sieic.mask = vcpu->arch.sie_block->gpsw.mask; | ||
| 426 | kvm_run->s390_sieic.addr = vcpu->arch.sie_block->gpsw.addr; | ||
| 427 | kvm_run->s390_sieic.ipa = vcpu->arch.sie_block->ipa; | ||
| 428 | kvm_run->s390_sieic.ipb = vcpu->arch.sie_block->ipb; | ||
| 429 | rc = 0; | ||
| 430 | } | ||
| 431 | |||
| 432 | if (rc == -EREMOTE) { | ||
| 433 | /* intercept was handled, but userspace support is needed | ||
| 434 | * kvm_run has been prepared by the handler */ | ||
| 435 | rc = 0; | ||
| 436 | } | ||
| 393 | 437 | ||
| 394 | if (vcpu->sigset_active) | 438 | if (vcpu->sigset_active) |
| 395 | sigprocmask(SIG_SETMASK, &sigsaved, NULL); | 439 | sigprocmask(SIG_SETMASK, &sigsaved, NULL); |
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index ed64a22ca86f..5b82527b7f86 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h | |||
| @@ -13,6 +13,13 @@ | |||
| 13 | 13 | ||
| 14 | #ifndef ARCH_S390_KVM_S390_H | 14 | #ifndef ARCH_S390_KVM_S390_H |
| 15 | #define ARCH_S390_KVM_S390_H | 15 | #define ARCH_S390_KVM_S390_H |
| 16 | |||
| 17 | #include <linux/kvm_host.h> | ||
| 18 | |||
| 19 | typedef int (*intercept_handler_t)(struct kvm_vcpu *vcpu); | ||
| 20 | |||
| 21 | int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu); | ||
| 22 | |||
| 16 | #define VM_EVENT(d_kvm, d_loglevel, d_string, d_args...)\ | 23 | #define VM_EVENT(d_kvm, d_loglevel, d_string, d_args...)\ |
| 17 | do { \ | 24 | do { \ |
| 18 | debug_sprintf_event(d_kvm->arch.dbf, d_loglevel, d_string "\n", \ | 25 | debug_sprintf_event(d_kvm->arch.dbf, d_loglevel, d_string "\n", \ |
diff --git a/include/asm-s390/kvm_host.h b/include/asm-s390/kvm_host.h index c9d653333106..8965b38d0a32 100644 --- a/include/asm-s390/kvm_host.h +++ b/include/asm-s390/kvm_host.h | |||
| @@ -101,6 +101,10 @@ struct sie_block { | |||
| 101 | 101 | ||
| 102 | struct kvm_vcpu_stat { | 102 | struct kvm_vcpu_stat { |
| 103 | u32 exit_userspace; | 103 | u32 exit_userspace; |
| 104 | u32 exit_external_request; | ||
| 105 | u32 exit_external_interrupt; | ||
| 106 | u32 exit_stop_request; | ||
| 107 | u32 exit_validity; | ||
| 104 | }; | 108 | }; |
| 105 | 109 | ||
| 106 | struct kvm_vcpu_arch { | 110 | struct kvm_vcpu_arch { |
diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 2367ff0c5dd0..f2acd6b9ab4d 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h | |||
| @@ -74,6 +74,7 @@ struct kvm_irqchip { | |||
| 74 | #define KVM_EXIT_INTR 10 | 74 | #define KVM_EXIT_INTR 10 |
| 75 | #define KVM_EXIT_SET_TPR 11 | 75 | #define KVM_EXIT_SET_TPR 11 |
| 76 | #define KVM_EXIT_TPR_ACCESS 12 | 76 | #define KVM_EXIT_TPR_ACCESS 12 |
| 77 | #define KVM_EXIT_S390_SIEIC 13 | ||
| 77 | 78 | ||
| 78 | /* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */ | 79 | /* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */ |
| 79 | struct kvm_run { | 80 | struct kvm_run { |
| @@ -138,6 +139,14 @@ struct kvm_run { | |||
| 138 | __u32 is_write; | 139 | __u32 is_write; |
| 139 | __u32 pad; | 140 | __u32 pad; |
| 140 | } tpr_access; | 141 | } tpr_access; |
| 142 | /* KVM_EXIT_S390_SIEIC */ | ||
| 143 | struct { | ||
| 144 | __u8 icptcode; | ||
| 145 | __u64 mask; /* psw upper half */ | ||
| 146 | __u64 addr; /* psw lower half */ | ||
| 147 | __u16 ipa; | ||
| 148 | __u32 ipb; | ||
| 149 | } s390_sieic; | ||
| 141 | /* Fix the size of the union. */ | 150 | /* Fix the size of the union. */ |
| 142 | char padding[256]; | 151 | char padding[256]; |
| 143 | }; | 152 | }; |
