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>
This commit is contained in:
parent
2bd6bf03f4
commit
b7c92f1a4e
|
@ -198,4 +198,5 @@ struct service_level {
|
|||
int register_service_level(struct service_level *);
|
||||
int unregister_service_level(struct service_level *);
|
||||
|
||||
int sthyi_fill(void *dst, u64 *rc);
|
||||
#endif /* __ASM_S390_SYSINFO_H */
|
||||
|
|
|
@ -55,7 +55,7 @@ obj-y := traps.o time.o process.o base.o early.o setup.o idle.o vtime.o
|
|||
obj-y += processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o
|
||||
obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o als.o
|
||||
obj-y += sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o
|
||||
obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o
|
||||
obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o
|
||||
obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o
|
||||
|
||||
extra-y += head.o head64.o vmlinux.lds
|
||||
|
|
|
@ -8,22 +8,17 @@
|
|||
* Copyright IBM Corp. 2016
|
||||
* Author(s): Janosch Frank <frankja@linux.vnet.ibm.com>
|
||||
*/
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/ratelimit.h>
|
||||
|
||||
#include <asm/kvm_host.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/sclp.h>
|
||||
#include <asm/diag.h>
|
||||
#include <asm/sysinfo.h>
|
||||
#include <asm/ebcdic.h>
|
||||
|
||||
#include "kvm-s390.h"
|
||||
#include "gaccess.h"
|
||||
#include "trace.h"
|
||||
#include <asm/facility.h>
|
||||
|
||||
#define DED_WEIGHT 0xffff
|
||||
/*
|
||||
|
@ -382,88 +377,52 @@ static void fill_diag(struct sthyi_sctns *sctns)
|
|||
vfree(diag204_buf);
|
||||
}
|
||||
|
||||
static int sthyi(u64 vaddr)
|
||||
static int sthyi(u64 vaddr, u64 *rc)
|
||||
{
|
||||
register u64 code asm("0") = 0;
|
||||
register u64 addr asm("2") = vaddr;
|
||||
register u64 rcode asm("3");
|
||||
int cc;
|
||||
|
||||
asm volatile(
|
||||
".insn rre,0xB2560000,%[code],%[addr]\n"
|
||||
"ipm %[cc]\n"
|
||||
"srl %[cc],28\n"
|
||||
: [cc] "=d" (cc)
|
||||
: [cc] "=d" (cc), "=d" (rcode)
|
||||
: [code] "d" (code), [addr] "a" (addr)
|
||||
: "3", "memory", "cc");
|
||||
: "memory", "cc");
|
||||
*rc = rcode;
|
||||
return cc;
|
||||
}
|
||||
|
||||
int handle_sthyi(struct kvm_vcpu *vcpu)
|
||||
/*
|
||||
* sthyi_fill - Fill page with data returned by the STHYI instruction
|
||||
*
|
||||
* @dst: Pointer to zeroed page
|
||||
* @rc: Pointer for storing the return code of the instruction
|
||||
*
|
||||
* Fills the destination with system information returned by the STHYI
|
||||
* instruction. The data is generated by emulation or execution of STHYI,
|
||||
* if available. The return value is the condition code that would be
|
||||
* returned, the rc parameter is the return code which is passed in
|
||||
* register R2 + 1.
|
||||
*/
|
||||
int sthyi_fill(void *dst, u64 *rc)
|
||||
{
|
||||
int reg1, reg2, r = 0;
|
||||
u64 code, addr, cc = 0;
|
||||
struct sthyi_sctns *sctns = NULL;
|
||||
|
||||
if (!test_kvm_facility(vcpu->kvm, 74))
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
|
||||
struct sthyi_sctns *sctns = (struct sthyi_sctns *)dst;
|
||||
|
||||
/*
|
||||
* STHYI requires extensive locking in the higher hypervisors
|
||||
* and is very computational/memory expensive. Therefore we
|
||||
* ratelimit the executions per VM.
|
||||
* If the facility is on, we don't want to emulate the instruction.
|
||||
* We ask the hypervisor to provide the data.
|
||||
*/
|
||||
if (!__ratelimit(&vcpu->kvm->arch.sthyi_limit)) {
|
||||
kvm_s390_retry_instr(vcpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
kvm_s390_get_regs_rre(vcpu, ®1, ®2);
|
||||
code = vcpu->run->s.regs.gprs[reg1];
|
||||
addr = vcpu->run->s.regs.gprs[reg2];
|
||||
|
||||
vcpu->stat.instruction_sthyi++;
|
||||
VCPU_EVENT(vcpu, 3, "STHYI: fc: %llu addr: 0x%016llx", code, addr);
|
||||
trace_kvm_s390_handle_sthyi(vcpu, code, addr);
|
||||
|
||||
if (reg1 == reg2 || reg1 & 1 || reg2 & 1)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
||||
|
||||
if (code & 0xffff) {
|
||||
cc = 3;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (addr & ~PAGE_MASK)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
||||
|
||||
sctns = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!sctns)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* If we are a guest, we don't want to emulate an emulated
|
||||
* instruction. We ask the hypervisor to provide the data.
|
||||
*/
|
||||
if (test_facility(74)) {
|
||||
cc = sthyi((u64)sctns);
|
||||
goto out;
|
||||
}
|
||||
if (test_facility(74))
|
||||
return sthyi((u64)dst, rc);
|
||||
|
||||
fill_hdr(sctns);
|
||||
fill_stsi(sctns);
|
||||
fill_diag(sctns);
|
||||
|
||||
out:
|
||||
if (!cc) {
|
||||
r = write_guest(vcpu, addr, reg2, sctns, PAGE_SIZE);
|
||||
if (r) {
|
||||
free_page((unsigned long)sctns);
|
||||
return kvm_s390_inject_prog_cond(vcpu, r);
|
||||
}
|
||||
}
|
||||
|
||||
free_page((unsigned long)sctns);
|
||||
vcpu->run->s.regs.gprs[reg2 + 1] = cc ? 4 : 0;
|
||||
kvm_s390_set_psw_cc(vcpu, cc);
|
||||
return r;
|
||||
*rc = 0;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sthyi_fill);
|
|
@ -12,6 +12,6 @@ common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o $(KVM)/async_pf.o $(KVM)/irqch
|
|||
ccflags-y := -Ivirt/kvm -Iarch/s390/kvm
|
||||
|
||||
kvm-objs := $(common-objs) kvm-s390.o intercept.o interrupt.o priv.o sigp.o
|
||||
kvm-objs += diag.o gaccess.o guestdbg.o sthyi.o vsie.o
|
||||
kvm-objs += diag.o gaccess.o guestdbg.o vsie.o
|
||||
|
||||
obj-$(CONFIG_KVM) += kvm.o
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <asm/kvm_host.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/sysinfo.h>
|
||||
|
||||
#include "kvm-s390.h"
|
||||
#include "gaccess.h"
|
||||
|
@ -360,6 +361,71 @@ static int handle_partial_execution(struct kvm_vcpu *vcpu)
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the sthyi instruction that provides the guest with system
|
||||
* information, like current CPU resources available at each level of
|
||||
* the machine.
|
||||
*/
|
||||
int handle_sthyi(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int reg1, reg2, r = 0;
|
||||
u64 code, addr, cc = 0, rc = 0;
|
||||
struct sthyi_sctns *sctns = NULL;
|
||||
|
||||
if (!test_kvm_facility(vcpu->kvm, 74))
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
|
||||
|
||||
/*
|
||||
* STHYI requires extensive locking in the higher hypervisors
|
||||
* and is very computational/memory expensive. Therefore we
|
||||
* ratelimit the executions per VM.
|
||||
*/
|
||||
if (!__ratelimit(&vcpu->kvm->arch.sthyi_limit)) {
|
||||
kvm_s390_retry_instr(vcpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
kvm_s390_get_regs_rre(vcpu, ®1, ®2);
|
||||
code = vcpu->run->s.regs.gprs[reg1];
|
||||
addr = vcpu->run->s.regs.gprs[reg2];
|
||||
|
||||
vcpu->stat.instruction_sthyi++;
|
||||
VCPU_EVENT(vcpu, 3, "STHYI: fc: %llu addr: 0x%016llx", code, addr);
|
||||
trace_kvm_s390_handle_sthyi(vcpu, code, addr);
|
||||
|
||||
if (reg1 == reg2 || reg1 & 1 || reg2 & 1)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
||||
|
||||
if (code & 0xffff) {
|
||||
cc = 3;
|
||||
rc = 4;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (addr & ~PAGE_MASK)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
||||
|
||||
sctns = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!sctns)
|
||||
return -ENOMEM;
|
||||
|
||||
cc = sthyi_fill(sctns, &rc);
|
||||
|
||||
out:
|
||||
if (!cc) {
|
||||
r = write_guest(vcpu, addr, reg2, sctns, PAGE_SIZE);
|
||||
if (r) {
|
||||
free_page((unsigned long)sctns);
|
||||
return kvm_s390_inject_prog_cond(vcpu, r);
|
||||
}
|
||||
}
|
||||
|
||||
free_page((unsigned long)sctns);
|
||||
vcpu->run->s.regs.gprs[reg2 + 1] = rc;
|
||||
kvm_s390_set_psw_cc(vcpu, cc);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int handle_operexc(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
psw_t oldpsw, newpsw;
|
||||
|
|
|
@ -242,6 +242,8 @@ static inline void kvm_s390_retry_instr(struct kvm_vcpu *vcpu)
|
|||
kvm_s390_rewind_psw(vcpu, kvm_s390_get_ilen(vcpu));
|
||||
}
|
||||
|
||||
int handle_sthyi(struct kvm_vcpu *vcpu);
|
||||
|
||||
/* implemented in priv.c */
|
||||
int is_valid_psw(psw_t *psw);
|
||||
int kvm_s390_handle_aa(struct kvm_vcpu *vcpu);
|
||||
|
@ -268,9 +270,6 @@ void kvm_s390_vsie_destroy(struct kvm *kvm);
|
|||
int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu);
|
||||
int kvm_s390_handle_sigp_pei(struct kvm_vcpu *vcpu);
|
||||
|
||||
/* implemented in sthyi.c */
|
||||
int handle_sthyi(struct kvm_vcpu *vcpu);
|
||||
|
||||
/* implemented in kvm-s390.c */
|
||||
void kvm_s390_set_tod_clock_ext(struct kvm *kvm,
|
||||
const struct kvm_s390_vm_tod_clock *gtod);
|
||||
|
|
Loading…
Reference in New Issue