diff options
| author | QingFeng Hao <haoqf@linux.vnet.ibm.com> | 2017-09-29 06:41:50 -0400 |
|---|---|---|
| committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2017-10-09 05:15:33 -0400 |
| commit | b7c92f1a4e131e459bcf53a570e7265e5ce64455 (patch) | |
| tree | dc227ee881d42de4577db06b2ed582058971fcc5 | |
| parent | 2bd6bf03f4c1c59381d62c61d03f6cc3fe71f66e (diff) | |
s390/sthyi: reorganize sthyi implementation
As we need to support sthyi instruction on LPAR too, move the common code
to kernel part and kvm related code to intercept.c for better reuse.
Signed-off-by: QingFeng Hao <haoqf@linux.vnet.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
| -rw-r--r-- | arch/s390/include/asm/sysinfo.h | 1 | ||||
| -rw-r--r-- | arch/s390/kernel/Makefile | 2 | ||||
| -rw-r--r-- | arch/s390/kernel/sthyi.c (renamed from arch/s390/kvm/sthyi.c) | 95 | ||||
| -rw-r--r-- | arch/s390/kvm/Makefile | 2 | ||||
| -rw-r--r-- | arch/s390/kvm/intercept.c | 66 | ||||
| -rw-r--r-- | arch/s390/kvm/kvm-s390.h | 5 |
6 files changed, 98 insertions, 73 deletions
diff --git a/arch/s390/include/asm/sysinfo.h b/arch/s390/include/asm/sysinfo.h index 2b498e58b914..e4a28307bc5d 100644 --- a/arch/s390/include/asm/sysinfo.h +++ b/arch/s390/include/asm/sysinfo.h | |||
| @@ -198,4 +198,5 @@ struct service_level { | |||
| 198 | int register_service_level(struct service_level *); | 198 | int register_service_level(struct service_level *); |
| 199 | int unregister_service_level(struct service_level *); | 199 | int unregister_service_level(struct service_level *); |
| 200 | 200 | ||
| 201 | int sthyi_fill(void *dst, u64 *rc); | ||
| 201 | #endif /* __ASM_S390_SYSINFO_H */ | 202 | #endif /* __ASM_S390_SYSINFO_H */ |
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index adb3fe2e3d42..1fefb7f9216f 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile | |||
| @@ -55,7 +55,7 @@ obj-y := traps.o time.o process.o base.o early.o setup.o idle.o vtime.o | |||
| 55 | obj-y += processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o | 55 | obj-y += processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o |
| 56 | obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o als.o | 56 | obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o als.o |
| 57 | obj-y += sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o | 57 | obj-y += sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o |
| 58 | obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o | 58 | obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o |
| 59 | obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o | 59 | obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o |
| 60 | 60 | ||
| 61 | extra-y += head.o head64.o vmlinux.lds | 61 | extra-y += head.o head64.o vmlinux.lds |
diff --git a/arch/s390/kvm/sthyi.c b/arch/s390/kernel/sthyi.c index 395926b8c1ed..3d51f86f9dec 100644 --- a/arch/s390/kvm/sthyi.c +++ b/arch/s390/kernel/sthyi.c | |||
| @@ -8,22 +8,17 @@ | |||
| 8 | * Copyright IBM Corp. 2016 | 8 | * Copyright IBM Corp. 2016 |
| 9 | * Author(s): Janosch Frank <frankja@linux.vnet.ibm.com> | 9 | * Author(s): Janosch Frank <frankja@linux.vnet.ibm.com> |
| 10 | */ | 10 | */ |
| 11 | #include <linux/kvm_host.h> | ||
| 12 | #include <linux/errno.h> | 11 | #include <linux/errno.h> |
| 13 | #include <linux/pagemap.h> | 12 | #include <linux/pagemap.h> |
| 14 | #include <linux/vmalloc.h> | 13 | #include <linux/vmalloc.h> |
| 15 | #include <linux/ratelimit.h> | 14 | #include <linux/ratelimit.h> |
| 16 | 15 | ||
| 17 | #include <asm/kvm_host.h> | ||
| 18 | #include <asm/asm-offsets.h> | 16 | #include <asm/asm-offsets.h> |
| 19 | #include <asm/sclp.h> | 17 | #include <asm/sclp.h> |
| 20 | #include <asm/diag.h> | 18 | #include <asm/diag.h> |
| 21 | #include <asm/sysinfo.h> | 19 | #include <asm/sysinfo.h> |
| 22 | #include <asm/ebcdic.h> | 20 | #include <asm/ebcdic.h> |
| 23 | 21 | #include <asm/facility.h> | |
| 24 | #include "kvm-s390.h" | ||
| 25 | #include "gaccess.h" | ||
| 26 | #include "trace.h" | ||
| 27 | 22 | ||
| 28 | #define DED_WEIGHT 0xffff | 23 | #define DED_WEIGHT 0xffff |
| 29 | /* | 24 | /* |
| @@ -382,88 +377,52 @@ out: | |||
| 382 | vfree(diag204_buf); | 377 | vfree(diag204_buf); |
| 383 | } | 378 | } |
| 384 | 379 | ||
| 385 | static int sthyi(u64 vaddr) | 380 | static int sthyi(u64 vaddr, u64 *rc) |
| 386 | { | 381 | { |
| 387 | register u64 code asm("0") = 0; | 382 | register u64 code asm("0") = 0; |
| 388 | register u64 addr asm("2") = vaddr; | 383 | register u64 addr asm("2") = vaddr; |
| 384 | register u64 rcode asm("3"); | ||
| 389 | int cc; | 385 | int cc; |
| 390 | 386 | ||
| 391 | asm volatile( | 387 | asm volatile( |
| 392 | ".insn rre,0xB2560000,%[code],%[addr]\n" | 388 | ".insn rre,0xB2560000,%[code],%[addr]\n" |
| 393 | "ipm %[cc]\n" | 389 | "ipm %[cc]\n" |
| 394 | "srl %[cc],28\n" | 390 | "srl %[cc],28\n" |
| 395 | : [cc] "=d" (cc) | 391 | : [cc] "=d" (cc), "=d" (rcode) |
| 396 | : [code] "d" (code), [addr] "a" (addr) | 392 | : [code] "d" (code), [addr] "a" (addr) |
| 397 | : "3", "memory", "cc"); | 393 | : "memory", "cc"); |
| 394 | *rc = rcode; | ||
| 398 | return cc; | 395 | return cc; |
| 399 | } | 396 | } |
| 400 | 397 | ||
| 401 | int handle_sthyi(struct kvm_vcpu *vcpu) | 398 | /* |
| 399 | * sthyi_fill - Fill page with data returned by the STHYI instruction | ||
| 400 | * | ||
| 401 | * @dst: Pointer to zeroed page | ||
| 402 | * @rc: Pointer for storing the return code of the instruction | ||
| 403 | * | ||
| 404 | * Fills the destination with system information returned by the STHYI | ||
| 405 | * instruction. The data is generated by emulation or execution of STHYI, | ||
| 406 | * if available. The return value is the condition code that would be | ||
| 407 | * returned, the rc parameter is the return code which is passed in | ||
| 408 | * register R2 + 1. | ||
| 409 | */ | ||
| 410 | int sthyi_fill(void *dst, u64 *rc) | ||
| 402 | { | 411 | { |
| 403 | int reg1, reg2, r = 0; | 412 | struct sthyi_sctns *sctns = (struct sthyi_sctns *)dst; |
| 404 | u64 code, addr, cc = 0; | ||
| 405 | struct sthyi_sctns *sctns = NULL; | ||
| 406 | |||
| 407 | if (!test_kvm_facility(vcpu->kvm, 74)) | ||
| 408 | return kvm_s390_inject_program_int(vcpu, PGM_OPERATION); | ||
| 409 | |||
| 410 | /* | ||
| 411 | * STHYI requires extensive locking in the higher hypervisors | ||
| 412 | * and is very computational/memory expensive. Therefore we | ||
| 413 | * ratelimit the executions per VM. | ||
| 414 | */ | ||
| 415 | if (!__ratelimit(&vcpu->kvm->arch.sthyi_limit)) { | ||
| 416 | kvm_s390_retry_instr(vcpu); | ||
| 417 | return 0; | ||
| 418 | } | ||
| 419 | |||
| 420 | kvm_s390_get_regs_rre(vcpu, ®1, ®2); | ||
| 421 | code = vcpu->run->s.regs.gprs[reg1]; | ||
| 422 | addr = vcpu->run->s.regs.gprs[reg2]; | ||
| 423 | |||
| 424 | vcpu->stat.instruction_sthyi++; | ||
| 425 | VCPU_EVENT(vcpu, 3, "STHYI: fc: %llu addr: 0x%016llx", code, addr); | ||
| 426 | trace_kvm_s390_handle_sthyi(vcpu, code, addr); | ||
| 427 | |||
| 428 | if (reg1 == reg2 || reg1 & 1 || reg2 & 1) | ||
| 429 | return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); | ||
| 430 | |||
| 431 | if (code & 0xffff) { | ||
| 432 | cc = 3; | ||
| 433 | goto out; | ||
| 434 | } | ||
| 435 | |||
| 436 | if (addr & ~PAGE_MASK) | ||
| 437 | return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); | ||
| 438 | |||
| 439 | sctns = (void *)get_zeroed_page(GFP_KERNEL); | ||
| 440 | if (!sctns) | ||
| 441 | return -ENOMEM; | ||
| 442 | 413 | ||
| 443 | /* | 414 | /* |
| 444 | * If we are a guest, we don't want to emulate an emulated | 415 | * If the facility is on, we don't want to emulate the instruction. |
| 445 | * instruction. We ask the hypervisor to provide the data. | 416 | * We ask the hypervisor to provide the data. |
| 446 | */ | 417 | */ |
| 447 | if (test_facility(74)) { | 418 | if (test_facility(74)) |
| 448 | cc = sthyi((u64)sctns); | 419 | return sthyi((u64)dst, rc); |
| 449 | goto out; | ||
| 450 | } | ||
| 451 | 420 | ||
| 452 | fill_hdr(sctns); | 421 | fill_hdr(sctns); |
| 453 | fill_stsi(sctns); | 422 | fill_stsi(sctns); |
| 454 | fill_diag(sctns); | 423 | fill_diag(sctns); |
| 455 | 424 | ||
| 456 | out: | 425 | *rc = 0; |
| 457 | if (!cc) { | 426 | return 0; |
| 458 | r = write_guest(vcpu, addr, reg2, sctns, PAGE_SIZE); | ||
| 459 | if (r) { | ||
| 460 | free_page((unsigned long)sctns); | ||
| 461 | return kvm_s390_inject_prog_cond(vcpu, r); | ||
| 462 | } | ||
| 463 | } | ||
| 464 | |||
| 465 | free_page((unsigned long)sctns); | ||
| 466 | vcpu->run->s.regs.gprs[reg2 + 1] = cc ? 4 : 0; | ||
| 467 | kvm_s390_set_psw_cc(vcpu, cc); | ||
| 468 | return r; | ||
| 469 | } | 427 | } |
| 428 | EXPORT_SYMBOL_GPL(sthyi_fill); | ||
diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile index 09a9e6dfc09f..6048b1c6e580 100644 --- a/arch/s390/kvm/Makefile +++ b/arch/s390/kvm/Makefile | |||
| @@ -12,6 +12,6 @@ common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o $(KVM)/async_pf.o $(KVM)/irqch | |||
| 12 | ccflags-y := -Ivirt/kvm -Iarch/s390/kvm | 12 | ccflags-y := -Ivirt/kvm -Iarch/s390/kvm |
| 13 | 13 | ||
| 14 | kvm-objs := $(common-objs) kvm-s390.o intercept.o interrupt.o priv.o sigp.o | 14 | kvm-objs := $(common-objs) kvm-s390.o intercept.o interrupt.o priv.o sigp.o |
| 15 | kvm-objs += diag.o gaccess.o guestdbg.o sthyi.o vsie.o | 15 | kvm-objs += diag.o gaccess.o guestdbg.o vsie.o |
| 16 | 16 | ||
| 17 | obj-$(CONFIG_KVM) += kvm.o | 17 | obj-$(CONFIG_KVM) += kvm.o |
diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index a4752bf6b526..46adda5e2b2c 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c | |||
| @@ -18,6 +18,7 @@ | |||
| 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 | #include <asm/irq.h> |
| 21 | #include <asm/sysinfo.h> | ||
| 21 | 22 | ||
| 22 | #include "kvm-s390.h" | 23 | #include "kvm-s390.h" |
| 23 | #include "gaccess.h" | 24 | #include "gaccess.h" |
| @@ -360,6 +361,71 @@ static int handle_partial_execution(struct kvm_vcpu *vcpu) | |||
| 360 | return -EOPNOTSUPP; | 361 | return -EOPNOTSUPP; |
| 361 | } | 362 | } |
| 362 | 363 | ||
| 364 | /* | ||
| 365 | * Handle the sthyi instruction that provides the guest with system | ||
| 366 | * information, like current CPU resources available at each level of | ||
| 367 | * the machine. | ||
| 368 | */ | ||
| 369 | int handle_sthyi(struct kvm_vcpu *vcpu) | ||
| 370 | { | ||
| 371 | int reg1, reg2, r = 0; | ||
| 372 | u64 code, addr, cc = 0, rc = 0; | ||
| 373 | struct sthyi_sctns *sctns = NULL; | ||
| 374 | |||
| 375 | if (!test_kvm_facility(vcpu->kvm, 74)) | ||
| 376 | return kvm_s390_inject_program_int(vcpu, PGM_OPERATION); | ||
| 377 | |||
| 378 | /* | ||
| 379 | * STHYI requires extensive locking in the higher hypervisors | ||
| 380 | * and is very computational/memory expensive. Therefore we | ||
| 381 | * ratelimit the executions per VM. | ||
| 382 | */ | ||
| 383 | if (!__ratelimit(&vcpu->kvm->arch.sthyi_limit)) { | ||
| 384 | kvm_s390_retry_instr(vcpu); | ||
| 385 | return 0; | ||
| 386 | } | ||
| 387 | |||
| 388 | kvm_s390_get_regs_rre(vcpu, ®1, ®2); | ||
| 389 | code = vcpu->run->s.regs.gprs[reg1]; | ||
| 390 | addr = vcpu->run->s.regs.gprs[reg2]; | ||
| 391 | |||
| 392 | vcpu->stat.instruction_sthyi++; | ||
| 393 | VCPU_EVENT(vcpu, 3, "STHYI: fc: %llu addr: 0x%016llx", code, addr); | ||
| 394 | trace_kvm_s390_handle_sthyi(vcpu, code, addr); | ||
| 395 | |||
| 396 | if (reg1 == reg2 || reg1 & 1 || reg2 & 1) | ||
| 397 | return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); | ||
| 398 | |||
| 399 | if (code & 0xffff) { | ||
| 400 | cc = 3; | ||
| 401 | rc = 4; | ||
| 402 | goto out; | ||
| 403 | } | ||
| 404 | |||
| 405 | if (addr & ~PAGE_MASK) | ||
| 406 | return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); | ||
| 407 | |||
| 408 | sctns = (void *)get_zeroed_page(GFP_KERNEL); | ||
| 409 | if (!sctns) | ||
| 410 | return -ENOMEM; | ||
| 411 | |||
| 412 | cc = sthyi_fill(sctns, &rc); | ||
| 413 | |||
| 414 | out: | ||
| 415 | if (!cc) { | ||
| 416 | r = write_guest(vcpu, addr, reg2, sctns, PAGE_SIZE); | ||
| 417 | if (r) { | ||
| 418 | free_page((unsigned long)sctns); | ||
| 419 | return kvm_s390_inject_prog_cond(vcpu, r); | ||
| 420 | } | ||
| 421 | } | ||
| 422 | |||
| 423 | free_page((unsigned long)sctns); | ||
| 424 | vcpu->run->s.regs.gprs[reg2 + 1] = rc; | ||
| 425 | kvm_s390_set_psw_cc(vcpu, cc); | ||
| 426 | return r; | ||
| 427 | } | ||
| 428 | |||
| 363 | static int handle_operexc(struct kvm_vcpu *vcpu) | 429 | static int handle_operexc(struct kvm_vcpu *vcpu) |
| 364 | { | 430 | { |
| 365 | psw_t oldpsw, newpsw; | 431 | psw_t oldpsw, newpsw; |
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index 9f8fdd7b2311..10d65dfbc306 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h | |||
| @@ -242,6 +242,8 @@ static inline void kvm_s390_retry_instr(struct kvm_vcpu *vcpu) | |||
| 242 | kvm_s390_rewind_psw(vcpu, kvm_s390_get_ilen(vcpu)); | 242 | kvm_s390_rewind_psw(vcpu, kvm_s390_get_ilen(vcpu)); |
| 243 | } | 243 | } |
| 244 | 244 | ||
| 245 | int handle_sthyi(struct kvm_vcpu *vcpu); | ||
| 246 | |||
| 245 | /* implemented in priv.c */ | 247 | /* implemented in priv.c */ |
| 246 | int is_valid_psw(psw_t *psw); | 248 | int is_valid_psw(psw_t *psw); |
| 247 | int kvm_s390_handle_aa(struct kvm_vcpu *vcpu); | 249 | int kvm_s390_handle_aa(struct kvm_vcpu *vcpu); |
| @@ -268,9 +270,6 @@ void kvm_s390_vsie_destroy(struct kvm *kvm); | |||
| 268 | int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu); | 270 | int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu); |
| 269 | int kvm_s390_handle_sigp_pei(struct kvm_vcpu *vcpu); | 271 | int kvm_s390_handle_sigp_pei(struct kvm_vcpu *vcpu); |
| 270 | 272 | ||
| 271 | /* implemented in sthyi.c */ | ||
| 272 | int handle_sthyi(struct kvm_vcpu *vcpu); | ||
| 273 | |||
| 274 | /* implemented in kvm-s390.c */ | 273 | /* implemented in kvm-s390.c */ |
| 275 | void kvm_s390_set_tod_clock_ext(struct kvm *kvm, | 274 | void kvm_s390_set_tod_clock_ext(struct kvm *kvm, |
| 276 | const struct kvm_s390_vm_tod_clock *gtod); | 275 | const struct kvm_s390_vm_tod_clock *gtod); |
