diff options
-rw-r--r-- | Documentation/virtual/kvm/api.txt | 2 | ||||
-rw-r--r-- | arch/s390/include/asm/kvm_host.h | 5 | ||||
-rw-r--r-- | arch/s390/kvm/guestdbg.c | 5 | ||||
-rw-r--r-- | arch/s390/kvm/intercept.c | 49 | ||||
-rw-r--r-- | arch/s390/kvm/interrupt.c | 32 | ||||
-rw-r--r-- | include/uapi/linux/kvm.h | 2 |
6 files changed, 88 insertions, 7 deletions
diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 2014ff12b492..0581f6c40f2b 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt | |||
@@ -2211,6 +2211,8 @@ KVM_S390_SIGP_STOP (vcpu) - sigp restart | |||
2211 | KVM_S390_PROGRAM_INT (vcpu) - program check; code in parm | 2211 | KVM_S390_PROGRAM_INT (vcpu) - program check; code in parm |
2212 | KVM_S390_SIGP_SET_PREFIX (vcpu) - sigp set prefix; prefix address in parm | 2212 | KVM_S390_SIGP_SET_PREFIX (vcpu) - sigp set prefix; prefix address in parm |
2213 | KVM_S390_RESTART (vcpu) - restart | 2213 | KVM_S390_RESTART (vcpu) - restart |
2214 | KVM_S390_INT_CLOCK_COMP (vcpu) - clock comparator interrupt | ||
2215 | KVM_S390_INT_CPU_TIMER (vcpu) - CPU timer interrupt | ||
2214 | KVM_S390_INT_VIRTIO (vm) - virtio external interrupt; external interrupt | 2216 | KVM_S390_INT_VIRTIO (vm) - virtio external interrupt; external interrupt |
2215 | parameters in parm and parm64 | 2217 | parameters in parm and parm64 |
2216 | KVM_S390_INT_SERVICE (vm) - sclp external interrupt; sclp parameter in parm | 2218 | KVM_S390_INT_SERVICE (vm) - sclp external interrupt; sclp parameter in parm |
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index f0a1dc5e5d1f..96b8a67ddaf8 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h | |||
@@ -132,7 +132,10 @@ struct kvm_s390_sie_block { | |||
132 | psw_t gpsw; /* 0x0090 */ | 132 | psw_t gpsw; /* 0x0090 */ |
133 | __u64 gg14; /* 0x00a0 */ | 133 | __u64 gg14; /* 0x00a0 */ |
134 | __u64 gg15; /* 0x00a8 */ | 134 | __u64 gg15; /* 0x00a8 */ |
135 | __u8 reservedb0[28]; /* 0x00b0 */ | 135 | __u8 reservedb0[20]; /* 0x00b0 */ |
136 | __u16 extcpuaddr; /* 0x00c4 */ | ||
137 | __u16 eic; /* 0x00c6 */ | ||
138 | __u32 reservedc8; /* 0x00c8 */ | ||
136 | __u16 pgmilc; /* 0x00cc */ | 139 | __u16 pgmilc; /* 0x00cc */ |
137 | __u16 iprcc; /* 0x00ce */ | 140 | __u16 iprcc; /* 0x00ce */ |
138 | __u32 dxc; /* 0x00d0 */ | 141 | __u32 dxc; /* 0x00d0 */ |
diff --git a/arch/s390/kvm/guestdbg.c b/arch/s390/kvm/guestdbg.c index 757ccef62fd5..3e8d4092ce30 100644 --- a/arch/s390/kvm/guestdbg.c +++ b/arch/s390/kvm/guestdbg.c | |||
@@ -223,9 +223,10 @@ int kvm_s390_import_bp_data(struct kvm_vcpu *vcpu, | |||
223 | goto error; | 223 | goto error; |
224 | } | 224 | } |
225 | 225 | ||
226 | ret = copy_from_user(bp_data, dbg->arch.hw_bp, size); | 226 | if (copy_from_user(bp_data, dbg->arch.hw_bp, size)) { |
227 | if (ret) | 227 | ret = -EFAULT; |
228 | goto error; | 228 | goto error; |
229 | } | ||
229 | 230 | ||
230 | for (i = 0; i < dbg->arch.nr_hw_bp; i++) { | 231 | for (i = 0; i < dbg->arch.nr_hw_bp; i++) { |
231 | switch (bp_data[i].type) { | 232 | switch (bp_data[i].type) { |
diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index 99e4b76e3487..bd607cf01a5d 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c | |||
@@ -17,6 +17,7 @@ | |||
17 | 17 | ||
18 | #include <asm/kvm_host.h> | 18 | #include <asm/kvm_host.h> |
19 | #include <asm/asm-offsets.h> | 19 | #include <asm/asm-offsets.h> |
20 | #include <asm/irq.h> | ||
20 | 21 | ||
21 | #include "kvm-s390.h" | 22 | #include "kvm-s390.h" |
22 | #include "gaccess.h" | 23 | #include "gaccess.h" |
@@ -46,9 +47,6 @@ static int handle_noop(struct kvm_vcpu *vcpu) | |||
46 | case 0x10: | 47 | case 0x10: |
47 | vcpu->stat.exit_external_request++; | 48 | vcpu->stat.exit_external_request++; |
48 | break; | 49 | break; |
49 | case 0x14: | ||
50 | vcpu->stat.exit_external_interrupt++; | ||
51 | break; | ||
52 | default: | 50 | default: |
53 | break; /* nothing */ | 51 | break; /* nothing */ |
54 | } | 52 | } |
@@ -234,6 +232,49 @@ static int handle_instruction_and_prog(struct kvm_vcpu *vcpu) | |||
234 | } | 232 | } |
235 | 233 | ||
236 | /** | 234 | /** |
235 | * handle_external_interrupt - used for external interruption interceptions | ||
236 | * | ||
237 | * This interception only occurs if the CPUSTAT_EXT_INT bit was set, or if | ||
238 | * the new PSW does not have external interrupts disabled. In the first case, | ||
239 | * we've got to deliver the interrupt manually, and in the second case, we | ||
240 | * drop to userspace to handle the situation there. | ||
241 | */ | ||
242 | static int handle_external_interrupt(struct kvm_vcpu *vcpu) | ||
243 | { | ||
244 | u16 eic = vcpu->arch.sie_block->eic; | ||
245 | struct kvm_s390_interrupt irq; | ||
246 | psw_t newpsw; | ||
247 | int rc; | ||
248 | |||
249 | vcpu->stat.exit_external_interrupt++; | ||
250 | |||
251 | rc = read_guest_lc(vcpu, __LC_EXT_NEW_PSW, &newpsw, sizeof(psw_t)); | ||
252 | if (rc) | ||
253 | return rc; | ||
254 | /* We can not handle clock comparator or timer interrupt with bad PSW */ | ||
255 | if ((eic == EXT_IRQ_CLK_COMP || eic == EXT_IRQ_CPU_TIMER) && | ||
256 | (newpsw.mask & PSW_MASK_EXT)) | ||
257 | return -EOPNOTSUPP; | ||
258 | |||
259 | switch (eic) { | ||
260 | case EXT_IRQ_CLK_COMP: | ||
261 | irq.type = KVM_S390_INT_CLOCK_COMP; | ||
262 | break; | ||
263 | case EXT_IRQ_CPU_TIMER: | ||
264 | irq.type = KVM_S390_INT_CPU_TIMER; | ||
265 | break; | ||
266 | case EXT_IRQ_EXTERNAL_CALL: | ||
267 | irq.type = KVM_S390_INT_EXTERNAL_CALL; | ||
268 | irq.parm = vcpu->arch.sie_block->extcpuaddr; | ||
269 | break; | ||
270 | default: | ||
271 | return -EOPNOTSUPP; | ||
272 | } | ||
273 | |||
274 | return kvm_s390_inject_vcpu(vcpu, &irq); | ||
275 | } | ||
276 | |||
277 | /** | ||
237 | * Handle MOVE PAGE partial execution interception. | 278 | * Handle MOVE PAGE partial execution interception. |
238 | * | 279 | * |
239 | * This interception can only happen for guests with DAT disabled and | 280 | * This interception can only happen for guests with DAT disabled and |
@@ -291,7 +332,7 @@ static const intercept_handler_t intercept_funcs[] = { | |||
291 | [0x08 >> 2] = handle_prog, | 332 | [0x08 >> 2] = handle_prog, |
292 | [0x0C >> 2] = handle_instruction_and_prog, | 333 | [0x0C >> 2] = handle_instruction_and_prog, |
293 | [0x10 >> 2] = handle_noop, | 334 | [0x10 >> 2] = handle_noop, |
294 | [0x14 >> 2] = handle_noop, | 335 | [0x14 >> 2] = handle_external_interrupt, |
295 | [0x18 >> 2] = handle_noop, | 336 | [0x18 >> 2] = handle_noop, |
296 | [0x1C >> 2] = kvm_s390_handle_wait, | 337 | [0x1C >> 2] = kvm_s390_handle_wait, |
297 | [0x20 >> 2] = handle_validity, | 338 | [0x20 >> 2] = handle_validity, |
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index d9526bb29194..75cd3217cd5a 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c | |||
@@ -27,6 +27,8 @@ | |||
27 | #define IOINT_CSSID_MASK 0x03fc0000 | 27 | #define IOINT_CSSID_MASK 0x03fc0000 |
28 | #define IOINT_AI_MASK 0x04000000 | 28 | #define IOINT_AI_MASK 0x04000000 |
29 | 29 | ||
30 | static void deliver_ckc_interrupt(struct kvm_vcpu *vcpu); | ||
31 | |||
30 | static int is_ioint(u64 type) | 32 | static int is_ioint(u64 type) |
31 | { | 33 | { |
32 | return ((type & 0xfffe0000u) != 0xfffe0000u); | 34 | return ((type & 0xfffe0000u) != 0xfffe0000u); |
@@ -89,6 +91,14 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu, | |||
89 | if (vcpu->arch.sie_block->gcr[0] & 0x4000ul) | 91 | if (vcpu->arch.sie_block->gcr[0] & 0x4000ul) |
90 | return 1; | 92 | return 1; |
91 | return 0; | 93 | return 0; |
94 | case KVM_S390_INT_CLOCK_COMP: | ||
95 | return ckc_interrupts_enabled(vcpu); | ||
96 | case KVM_S390_INT_CPU_TIMER: | ||
97 | if (psw_extint_disabled(vcpu)) | ||
98 | return 0; | ||
99 | if (vcpu->arch.sie_block->gcr[0] & 0x400ul) | ||
100 | return 1; | ||
101 | return 0; | ||
92 | case KVM_S390_INT_SERVICE: | 102 | case KVM_S390_INT_SERVICE: |
93 | case KVM_S390_INT_PFAULT_INIT: | 103 | case KVM_S390_INT_PFAULT_INIT: |
94 | case KVM_S390_INT_PFAULT_DONE: | 104 | case KVM_S390_INT_PFAULT_DONE: |
@@ -166,6 +176,8 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu, | |||
166 | case KVM_S390_INT_PFAULT_INIT: | 176 | case KVM_S390_INT_PFAULT_INIT: |
167 | case KVM_S390_INT_PFAULT_DONE: | 177 | case KVM_S390_INT_PFAULT_DONE: |
168 | case KVM_S390_INT_VIRTIO: | 178 | case KVM_S390_INT_VIRTIO: |
179 | case KVM_S390_INT_CLOCK_COMP: | ||
180 | case KVM_S390_INT_CPU_TIMER: | ||
169 | if (psw_extint_disabled(vcpu)) | 181 | if (psw_extint_disabled(vcpu)) |
170 | __set_cpuflag(vcpu, CPUSTAT_EXT_INT); | 182 | __set_cpuflag(vcpu, CPUSTAT_EXT_INT); |
171 | else | 183 | else |
@@ -326,6 +338,24 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu, | |||
326 | &vcpu->arch.sie_block->gpsw, | 338 | &vcpu->arch.sie_block->gpsw, |
327 | sizeof(psw_t)); | 339 | sizeof(psw_t)); |
328 | break; | 340 | break; |
341 | case KVM_S390_INT_CLOCK_COMP: | ||
342 | trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, | ||
343 | inti->ext.ext_params, 0); | ||
344 | deliver_ckc_interrupt(vcpu); | ||
345 | break; | ||
346 | case KVM_S390_INT_CPU_TIMER: | ||
347 | trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, | ||
348 | inti->ext.ext_params, 0); | ||
349 | rc = put_guest_lc(vcpu, EXT_IRQ_CPU_TIMER, | ||
350 | (u16 *)__LC_EXT_INT_CODE); | ||
351 | rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, | ||
352 | &vcpu->arch.sie_block->gpsw, | ||
353 | sizeof(psw_t)); | ||
354 | rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, | ||
355 | &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); | ||
356 | rc |= put_guest_lc(vcpu, inti->ext.ext_params, | ||
357 | (u32 *)__LC_EXT_PARAMS); | ||
358 | break; | ||
329 | case KVM_S390_INT_SERVICE: | 359 | case KVM_S390_INT_SERVICE: |
330 | VCPU_EVENT(vcpu, 4, "interrupt: sclp parm:%x", | 360 | VCPU_EVENT(vcpu, 4, "interrupt: sclp parm:%x", |
331 | inti->ext.ext_params); | 361 | inti->ext.ext_params); |
@@ -984,6 +1014,8 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, | |||
984 | break; | 1014 | break; |
985 | case KVM_S390_SIGP_STOP: | 1015 | case KVM_S390_SIGP_STOP: |
986 | case KVM_S390_RESTART: | 1016 | case KVM_S390_RESTART: |
1017 | case KVM_S390_INT_CLOCK_COMP: | ||
1018 | case KVM_S390_INT_CPU_TIMER: | ||
987 | VCPU_EVENT(vcpu, 3, "inject: type %x", s390int->type); | 1019 | VCPU_EVENT(vcpu, 3, "inject: type %x", s390int->type); |
988 | inti->type = s390int->type; | 1020 | inti->type = s390int->type; |
989 | break; | 1021 | break; |
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 836e15b7abc8..2b83cf35437a 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h | |||
@@ -416,6 +416,8 @@ struct kvm_s390_psw { | |||
416 | #define KVM_S390_INT_PFAULT_INIT 0xfffe0004u | 416 | #define KVM_S390_INT_PFAULT_INIT 0xfffe0004u |
417 | #define KVM_S390_INT_PFAULT_DONE 0xfffe0005u | 417 | #define KVM_S390_INT_PFAULT_DONE 0xfffe0005u |
418 | #define KVM_S390_MCHK 0xfffe1000u | 418 | #define KVM_S390_MCHK 0xfffe1000u |
419 | #define KVM_S390_INT_CLOCK_COMP 0xffff1004u | ||
420 | #define KVM_S390_INT_CPU_TIMER 0xffff1005u | ||
419 | #define KVM_S390_INT_VIRTIO 0xffff2603u | 421 | #define KVM_S390_INT_VIRTIO 0xffff2603u |
420 | #define KVM_S390_INT_SERVICE 0xffff2401u | 422 | #define KVM_S390_INT_SERVICE 0xffff2401u |
421 | #define KVM_S390_INT_EMERGENCY 0xffff1201u | 423 | #define KVM_S390_INT_EMERGENCY 0xffff1201u |