diff options
author | David Hildenbrand <dahi@linux.vnet.ibm.com> | 2014-01-23 06:26:52 -0500 |
---|---|---|
committer | Christian Borntraeger <borntraeger@de.ibm.com> | 2014-04-22 07:24:51 -0400 |
commit | 27291e2165b6de70c476b7b675308113edd69a60 (patch) | |
tree | 1508e3bb47e7171c176d82ab4fa231947a267140 /arch/s390/kvm/kvm-s390.c | |
parent | af1827e773c983f1d601d674447aea89efdb1acb (diff) |
KVM: s390: hardware support for guest debugging
This patch adds support to debug the guest using the PER facility on s390.
Single-stepping, hardware breakpoints and hardware watchpoints are supported. In
order to use the PER facility of the guest without it noticing it, the control
registers of the guest have to be patched and access to them has to be
intercepted(stctl, stctg, lctl, lctlg).
All PER program interrupts have to be intercepted and only the relevant PER
interrupts for the guest have to be given back. Special care has to be taken
about repeated exits on the same hardware breakpoint. The intervention of the
host in the guests PER configuration is not fully transparent. PER instruction
nullification can not be used by the guest and too many storage alteration
events may be reported to the guest (if it is activated for special address
ranges only) when the host concurrently debugging it.
Signed-off-by: David Hildenbrand <dahi@linux.vnet.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Diffstat (limited to 'arch/s390/kvm/kvm-s390.c')
-rw-r--r-- | arch/s390/kvm/kvm-s390.c | 53 |
1 files changed, 51 insertions, 2 deletions
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 7ae8c26065fb..e6bbfe1a9474 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c | |||
@@ -934,10 +934,40 @@ int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, | |||
934 | return -EINVAL; /* not implemented yet */ | 934 | return -EINVAL; /* not implemented yet */ |
935 | } | 935 | } |
936 | 936 | ||
937 | #define VALID_GUESTDBG_FLAGS (KVM_GUESTDBG_SINGLESTEP | \ | ||
938 | KVM_GUESTDBG_USE_HW_BP | \ | ||
939 | KVM_GUESTDBG_ENABLE) | ||
940 | |||
937 | int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, | 941 | int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, |
938 | struct kvm_guest_debug *dbg) | 942 | struct kvm_guest_debug *dbg) |
939 | { | 943 | { |
940 | return -EINVAL; /* not implemented yet */ | 944 | int rc = 0; |
945 | |||
946 | vcpu->guest_debug = 0; | ||
947 | kvm_s390_clear_bp_data(vcpu); | ||
948 | |||
949 | if (vcpu->guest_debug & ~VALID_GUESTDBG_FLAGS) | ||
950 | return -EINVAL; | ||
951 | |||
952 | if (dbg->control & KVM_GUESTDBG_ENABLE) { | ||
953 | vcpu->guest_debug = dbg->control; | ||
954 | /* enforce guest PER */ | ||
955 | atomic_set_mask(CPUSTAT_P, &vcpu->arch.sie_block->cpuflags); | ||
956 | |||
957 | if (dbg->control & KVM_GUESTDBG_USE_HW_BP) | ||
958 | rc = kvm_s390_import_bp_data(vcpu, dbg); | ||
959 | } else { | ||
960 | atomic_clear_mask(CPUSTAT_P, &vcpu->arch.sie_block->cpuflags); | ||
961 | vcpu->arch.guestdbg.last_bp = 0; | ||
962 | } | ||
963 | |||
964 | if (rc) { | ||
965 | vcpu->guest_debug = 0; | ||
966 | kvm_s390_clear_bp_data(vcpu); | ||
967 | atomic_clear_mask(CPUSTAT_P, &vcpu->arch.sie_block->cpuflags); | ||
968 | } | ||
969 | |||
970 | return rc; | ||
941 | } | 971 | } |
942 | 972 | ||
943 | int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, | 973 | int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, |
@@ -1095,6 +1125,11 @@ static int vcpu_pre_run(struct kvm_vcpu *vcpu) | |||
1095 | if (rc) | 1125 | if (rc) |
1096 | return rc; | 1126 | return rc; |
1097 | 1127 | ||
1128 | if (guestdbg_enabled(vcpu)) { | ||
1129 | kvm_s390_backup_guest_per_regs(vcpu); | ||
1130 | kvm_s390_patch_guest_per_regs(vcpu); | ||
1131 | } | ||
1132 | |||
1098 | vcpu->arch.sie_block->icptcode = 0; | 1133 | vcpu->arch.sie_block->icptcode = 0; |
1099 | cpuflags = atomic_read(&vcpu->arch.sie_block->cpuflags); | 1134 | cpuflags = atomic_read(&vcpu->arch.sie_block->cpuflags); |
1100 | VCPU_EVENT(vcpu, 6, "entering sie flags %x", cpuflags); | 1135 | VCPU_EVENT(vcpu, 6, "entering sie flags %x", cpuflags); |
@@ -1111,6 +1146,9 @@ static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason) | |||
1111 | vcpu->arch.sie_block->icptcode); | 1146 | vcpu->arch.sie_block->icptcode); |
1112 | trace_kvm_s390_sie_exit(vcpu, vcpu->arch.sie_block->icptcode); | 1147 | trace_kvm_s390_sie_exit(vcpu, vcpu->arch.sie_block->icptcode); |
1113 | 1148 | ||
1149 | if (guestdbg_enabled(vcpu)) | ||
1150 | kvm_s390_restore_guest_per_regs(vcpu); | ||
1151 | |||
1114 | if (exit_reason >= 0) { | 1152 | if (exit_reason >= 0) { |
1115 | rc = 0; | 1153 | rc = 0; |
1116 | } else if (kvm_is_ucontrol(vcpu->kvm)) { | 1154 | } else if (kvm_is_ucontrol(vcpu->kvm)) { |
@@ -1176,7 +1214,7 @@ static int __vcpu_run(struct kvm_vcpu *vcpu) | |||
1176 | vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); | 1214 | vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); |
1177 | 1215 | ||
1178 | rc = vcpu_post_run(vcpu, exit_reason); | 1216 | rc = vcpu_post_run(vcpu, exit_reason); |
1179 | } while (!signal_pending(current) && !rc); | 1217 | } while (!signal_pending(current) && !guestdbg_exit_pending(vcpu) && !rc); |
1180 | 1218 | ||
1181 | srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx); | 1219 | srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx); |
1182 | return rc; | 1220 | return rc; |
@@ -1187,6 +1225,11 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | |||
1187 | int rc; | 1225 | int rc; |
1188 | sigset_t sigsaved; | 1226 | sigset_t sigsaved; |
1189 | 1227 | ||
1228 | if (guestdbg_exit_pending(vcpu)) { | ||
1229 | kvm_s390_prepare_debug_exit(vcpu); | ||
1230 | return 0; | ||
1231 | } | ||
1232 | |||
1190 | if (vcpu->sigset_active) | 1233 | if (vcpu->sigset_active) |
1191 | sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); | 1234 | sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); |
1192 | 1235 | ||
@@ -1199,6 +1242,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | |||
1199 | case KVM_EXIT_S390_RESET: | 1242 | case KVM_EXIT_S390_RESET: |
1200 | case KVM_EXIT_S390_UCONTROL: | 1243 | case KVM_EXIT_S390_UCONTROL: |
1201 | case KVM_EXIT_S390_TSCH: | 1244 | case KVM_EXIT_S390_TSCH: |
1245 | case KVM_EXIT_DEBUG: | ||
1202 | break; | 1246 | break; |
1203 | default: | 1247 | default: |
1204 | BUG(); | 1248 | BUG(); |
@@ -1224,6 +1268,11 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | |||
1224 | rc = -EINTR; | 1268 | rc = -EINTR; |
1225 | } | 1269 | } |
1226 | 1270 | ||
1271 | if (guestdbg_exit_pending(vcpu) && !rc) { | ||
1272 | kvm_s390_prepare_debug_exit(vcpu); | ||
1273 | rc = 0; | ||
1274 | } | ||
1275 | |||
1227 | if (rc == -EOPNOTSUPP) { | 1276 | if (rc == -EOPNOTSUPP) { |
1228 | /* intercept cannot be handled in-kernel, prepare kvm-run */ | 1277 | /* intercept cannot be handled in-kernel, prepare kvm-run */ |
1229 | kvm_run->exit_reason = KVM_EXIT_S390_SIEIC; | 1278 | kvm_run->exit_reason = KVM_EXIT_S390_SIEIC; |