mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
KVM: s390: arm64: Implement required functions
Implement the mostly trivial functions that the shared arm64 (kvm) code & headers oblige s390 to implement. MAINTAINERS: Add arch/s390/include/arm64/* to KVM/s390 Implement a very basic smccc handler that (non-compliantly) is just able to stop a vcpu. Signed-off-by: Steffen Eiden <seiden@linux.ibm.com> Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
This commit is contained in:
parent
761404d7ac
commit
3fb328f76b
|
|
@ -14372,6 +14372,7 @@ L: kvm@vger.kernel.org
|
|||
S: Supported
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux.git
|
||||
F: Documentation/virt/kvm/s390*
|
||||
F: arch/s390/include/arm64/*
|
||||
F: arch/s390/include/asm/gmap_helpers.h
|
||||
F: arch/s390/include/asm/kvm*
|
||||
F: arch/s390/include/uapi/asm/kvm*
|
||||
|
|
|
|||
130
arch/s390/include/arm64/kvm_emulate.h
Normal file
130
arch/s390/include/arm64/kvm_emulate.h
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef __S390_ARM64_KVM_EMULATE_H__
|
||||
#define __S390_ARM64_KVM_EMULATE_H__
|
||||
|
||||
#include <asm/fault.h>
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include <arm64/kvm_nested.h>
|
||||
#include <arm64/ptrace.h>
|
||||
#include <arm64/kvm_arm.h>
|
||||
#include <arm64/sysreg.h>
|
||||
|
||||
static __always_inline unsigned long *vcpu_pc(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return (unsigned long *)&vcpu->arch.sae_block.pc;
|
||||
}
|
||||
|
||||
static __always_inline unsigned long *vcpu_cpsr(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return (unsigned long *)&vcpu->arch.sae_block.pstate;
|
||||
}
|
||||
|
||||
static __always_inline unsigned long *vcpu_sp_el0(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return (unsigned long *)&vcpu->arch.sae_block.sp_el0;
|
||||
}
|
||||
|
||||
static __always_inline u64 *vcpu_sp_el1(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return &vcpu->arch.sae_block.sp_el1;
|
||||
}
|
||||
|
||||
static __always_inline __vector128 *vcpu_vreg(struct kvm_vcpu *vcpu, int off)
|
||||
{
|
||||
return &vcpu->arch.ctxt.vregs[off];
|
||||
}
|
||||
|
||||
static __always_inline u32 *vcpu_fpsr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return &vcpu->arch.sae_block.fpsr;
|
||||
}
|
||||
|
||||
static __always_inline u32 *vcpu_fpcr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return &vcpu->arch.sae_block.fpcr;
|
||||
}
|
||||
|
||||
static __always_inline bool vcpu_mode_is_32bit(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static __always_inline u64 kvm_vcpu_get_esr(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->arch.sae_block.hai.esr_el2;
|
||||
}
|
||||
|
||||
static inline unsigned long *vcpu_hcr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return (unsigned long *)&vcpu->arch.hcr_el2;
|
||||
}
|
||||
|
||||
static __always_inline unsigned long kvm_vcpu_get_hfar(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->arch.sae_block.hai.far_el2;
|
||||
}
|
||||
|
||||
static __always_inline phys_addr_t kvm_vcpu_get_fault_ipa(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return gfn_to_gpa(vcpu->arch.sae_block.hai.teid.addr);
|
||||
}
|
||||
|
||||
static inline u16 kvm_vcpu_fault_pic(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->arch.sae_block.hai.pic & PGM_INT_CODE_MASK;
|
||||
}
|
||||
|
||||
static __always_inline
|
||||
bool kvm_vcpu_trap_is_permission_fault(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return kvm_vcpu_fault_pic(vcpu) == PGM_PROTECTION;
|
||||
}
|
||||
|
||||
static __always_inline bool kvm_condition_valid(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static __always_inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool kvm_vcpu_is_be(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int kvm_vcpu_abt_gltl(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->arch.sae_block.hai.gltl;
|
||||
}
|
||||
|
||||
static inline bool vcpu_mode_priv(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 mode = *vcpu_cpsr(vcpu) & PSR_MODE_MASK;
|
||||
|
||||
return mode != PSR_MODE_EL0t;
|
||||
}
|
||||
|
||||
static inline void kvm_skip_instr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
*vcpu_pc(vcpu) += 4;
|
||||
*vcpu_cpsr(vcpu) &= ~PSR_BTYPE_MASK;
|
||||
|
||||
/* advance the singlestep state machine */
|
||||
*vcpu_cpsr(vcpu) &= ~SPSR_ELx_SS;
|
||||
}
|
||||
|
||||
static inline void kvm_reset_fpsimd(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
memset(vcpu->arch.ctxt.vregs, 0, sizeof(vcpu->arch.ctxt.vregs));
|
||||
vcpu->arch.sae_block.fpsr = 0;
|
||||
vcpu->arch.sae_block.fpcr = 0;
|
||||
}
|
||||
|
||||
#include <arm64/kvm_emulate-gen.h>
|
||||
|
||||
#endif /* __S390_ARM64_KVM_EMULATE_H__ */
|
||||
11
arch/s390/include/arm64/kvm_nested.h
Normal file
11
arch/s390/include/arm64/kvm_nested.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef ASM_KVM_NESTED_H
|
||||
#define ASM_KVM_NESTED_H
|
||||
|
||||
static inline bool vcpu_has_nv(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* ASM_KVM_NESTED_H */
|
||||
11
arch/s390/include/arm64/ptrace.h
Normal file
11
arch/s390/include/arm64/ptrace.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef __S390_ARM64_PTRACE_H__
|
||||
#define __S390_ARM64_PTRACE_H__
|
||||
|
||||
#include <arm64/sysreg.h>
|
||||
#include <uapi/arm64/ptrace.h>
|
||||
|
||||
#include <arm64/ptrace-gen.h>
|
||||
|
||||
#endif /* __S390_ARM64_PTRACE_H__ */
|
||||
13
arch/s390/include/arm64/sysreg.h
Normal file
13
arch/s390/include/arm64/sysreg.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef __S390_ARM64_SYSREG_H__
|
||||
#define __S390_ARM64_SYSREG_H__
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/kasan-tags.h>
|
||||
#include <linux/kconfig.h>
|
||||
|
||||
#include <arm64/sysreg-gen.h>
|
||||
|
||||
#endif /* __S390_ARM64_SYSREG_H__ */
|
||||
127
arch/s390/kvm/arm64/handle_exit.c
Normal file
127
arch/s390/kvm/arm64/handle_exit.c
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include <arm64/esr.h>
|
||||
#include <arm64/kvm_emulate.h>
|
||||
|
||||
typedef int (*exit_handle_fn)(struct kvm_vcpu *);
|
||||
|
||||
#define PSCI_0_2_FN_SYSTEM_OFF 0x84000008
|
||||
#define PSCI_RET_NOT_SUPPORTED -1
|
||||
#define PSCI_RET_INTERNAL_FAILURE -6
|
||||
/*
|
||||
* Temporary smc/hvc handler. Non-compliant implementation (features missing).
|
||||
* Implements only system off so that test programs are able to end their execution.
|
||||
* Multi-vCPU power state management and KVM_REQ_SLEEP handling are deferred
|
||||
* to future SMP series.
|
||||
*/
|
||||
static int kvm_smccc_call_handler(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 func_id = vcpu_get_reg(vcpu, 0);
|
||||
u64 val = PSCI_RET_NOT_SUPPORTED;
|
||||
int ret = 1;
|
||||
|
||||
if (func_id == PSCI_0_2_FN_SYSTEM_OFF) {
|
||||
scoped_guard(spinlock, &vcpu->arch.mp_state_lock) {
|
||||
WRITE_ONCE(vcpu->arch.mp_state.mp_state, KVM_MP_STATE_STOPPED);
|
||||
}
|
||||
memset(&vcpu->run->system_event, 0,
|
||||
sizeof(vcpu->run->system_event));
|
||||
vcpu->run->system_event.type = KVM_SYSTEM_EVENT_SHUTDOWN;
|
||||
vcpu->run->system_event.ndata = 1;
|
||||
vcpu->run->system_event.data[0] = 0;
|
||||
vcpu->run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
|
||||
val = PSCI_RET_INTERNAL_FAILURE;
|
||||
ret = 0;
|
||||
}
|
||||
vcpu_set_reg(vcpu, 0, val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int handle_hvc(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu->stat.hvc_exit_stat++;
|
||||
return kvm_smccc_call_handler(vcpu);
|
||||
}
|
||||
|
||||
static int kvm_handle_unknown_ec(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 esr = kvm_vcpu_get_esr(vcpu);
|
||||
|
||||
kvm_pr_unimpl("Unknown exception class: esr: %#016llx -- %s\n",
|
||||
esr, esr_get_class_string(esr));
|
||||
|
||||
kvm_inject_undefined(vcpu);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static exit_handle_fn arm_exit_handlers[] = {
|
||||
[0 ... ESR_ELx_EC_MAX] = kvm_handle_unknown_ec,
|
||||
[ESR_ELx_EC_HVC64] = handle_hvc,
|
||||
};
|
||||
|
||||
static exit_handle_fn kvm_get_exit_handler(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 esr = kvm_vcpu_get_esr(vcpu);
|
||||
u8 esr_ec = ESR_ELx_EC(esr);
|
||||
|
||||
return arm_exit_handlers[esr_ec];
|
||||
}
|
||||
|
||||
/*
|
||||
* We may be single-stepping an emulated instruction. If the emulation
|
||||
* has been completed in the kernel, we can return to userspace with a
|
||||
* KVM_EXIT_DEBUG, otherwise userspace needs to complete its
|
||||
* emulation first.
|
||||
*/
|
||||
static int handle_trap_exceptions(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
exit_handle_fn exit_handler;
|
||||
|
||||
exit_handler = kvm_get_exit_handler(vcpu);
|
||||
return exit_handler(vcpu);
|
||||
}
|
||||
|
||||
/* manually copied from arch/arm64/kernel/traps.c */
|
||||
static const char * const esr_class_str[] = {
|
||||
[0 ... ESR_ELx_EC_MAX] = "UNRECOGNIZED EC",
|
||||
[ESR_ELx_EC_UNKNOWN] = "Unknown/Uncategorized",
|
||||
[ESR_ELx_EC_WFx] = "WFI/WFE",
|
||||
[ESR_ELx_EC_FP_ASIMD] = "ASIMD",
|
||||
[ESR_ELx_EC_PAC] = "PAC",
|
||||
[ESR_ELx_EC_BTI] = "BTI",
|
||||
[ESR_ELx_EC_ILL] = "PSTATE.IL",
|
||||
[ESR_ELx_EC_SVC64] = "SVC (AArch64)",
|
||||
[ESR_ELx_EC_HVC64] = "HVC (AArch64)",
|
||||
[ESR_ELx_EC_SMC64] = "SMC (AArch64)",
|
||||
[ESR_ELx_EC_SYS64] = "MSR/MRS (AArch64)",
|
||||
[ESR_ELx_EC_SVE] = "SVE",
|
||||
[ESR_ELx_EC_ERET] = "ERET/ERETAA/ERETAB",
|
||||
[ESR_ELx_EC_FPAC] = "FPAC",
|
||||
[ESR_ELx_EC_SME] = "SME",
|
||||
[ESR_ELx_EC_IMP_DEF] = "EL3 IMP DEF",
|
||||
[ESR_ELx_EC_IABT_LOW] = "IABT (lower EL)",
|
||||
[ESR_ELx_EC_IABT_CUR] = "IABT (current EL)",
|
||||
[ESR_ELx_EC_PC_ALIGN] = "PC Alignment",
|
||||
[ESR_ELx_EC_DABT_LOW] = "DABT (lower EL)",
|
||||
[ESR_ELx_EC_DABT_CUR] = "DABT (current EL)",
|
||||
[ESR_ELx_EC_SP_ALIGN] = "SP Alignment",
|
||||
[ESR_ELx_EC_MOPS] = "MOPS",
|
||||
[ESR_ELx_EC_FP_EXC64] = "FP (AArch64)",
|
||||
[ESR_ELx_EC_GCS] = "Guarded Control Stack",
|
||||
[ESR_ELx_EC_SERROR] = "SError",
|
||||
[ESR_ELx_EC_BREAKPT_LOW] = "Breakpoint (lower EL)",
|
||||
[ESR_ELx_EC_BREAKPT_CUR] = "Breakpoint (current EL)",
|
||||
[ESR_ELx_EC_SOFTSTP_LOW] = "Software Step (lower EL)",
|
||||
[ESR_ELx_EC_SOFTSTP_CUR] = "Software Step (current EL)",
|
||||
[ESR_ELx_EC_WATCHPT_LOW] = "Watchpoint (lower EL)",
|
||||
[ESR_ELx_EC_WATCHPT_CUR] = "Watchpoint (current EL)",
|
||||
[ESR_ELx_EC_BRK64] = "BRK (AArch64)",
|
||||
};
|
||||
|
||||
const char *esr_get_class_string(unsigned long esr)
|
||||
{
|
||||
return esr_class_str[ESR_ELx_EC(esr)];
|
||||
}
|
||||
21
arch/s390/kvm/arm64/inject_fault.c
Normal file
21
arch/s390/kvm/arm64/inject_fault.c
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <arm64/kvm_emulate.h>
|
||||
|
||||
/**
|
||||
* kvm_inject_undefined - inject an undefined instruction into the guest
|
||||
* @vcpu: The vCPU in which to inject the exception
|
||||
*
|
||||
* It is assumed that this code is called from the VCPU thread and that the
|
||||
* VCPU therefore is not currently executing guest code.
|
||||
*/
|
||||
void kvm_inject_undefined(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/* Stub until s390 supports arm64 sysregs TODO sysregs*/
|
||||
}
|
||||
|
||||
int kvm_inject_sea(struct kvm_vcpu *vcpu, bool iabt, u64 addr)
|
||||
{
|
||||
/* Stub until s390 supports arm64 sysregs TODO sysregs*/
|
||||
return 1;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user