mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
KVM: s390: Changes for 7.4
- rework page allocation for secure guest variable storage - selftests - documentation - replace get_zeroed_page with kzalloc - fix floating interrupt inject for corner cases - initial arm64 on s390 support This also contains the patch "arm64: Use proper include variant" as a build fix. It is also part of the arm tree. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE+SKTgaM0CPnbq/vKEXu8gLWmHHwFAmrQw/gACgkQEXu8gLWm HHwfkhAAgTzwSx9QbBPFMyCsHbKZNgK5x4vieL9E04NV8XbvcEBAXE3xgbVadMNK sbqQ6kjuUrJIa9XG4BCjZwfAKqwGh9DGVGAvIbOjtIP17mt2dqVPZeGrimbromaa K6h7uKHTxm5SyQ6Ld/OpzTOJAf/S2uKwuoKcHh05NNNcjC1CFnV0PV8PIHIPVGsw PLUlZ/MfsPjUH6RA1CjstHRRSAv6DajIriCqS6mGCj03VJIph1uGgdEhbA0iiUyy NPHnpikLct9BsyqNL3IVCh0KwYeFzBTzcLOfbZRq4zu3wxbPU/F1Ee+G3AnjNMzW iz6V+xZcmq2c9pN45vIAzsU68UJ+3CWSyh41ivj9rZngoHhTCPy6iywIxwioh6ki 5BCunmZ4wOjXmoirlhaW6bytsFVKeHNxI2uz1FkRAy+AkzFwTHWE401yPZYJI7dd F+7d4toXkVC8sI35diRACcHy2SLtwRjPYxC7MF1dU3cCvpwPNkXJdJc4WYHuSROB syk+JmG3birkvJgcJE+yxyU4ZEzEaQOPX68rVKkWrtYIr7PGReJWKUADhinZc1dn g9zOY3x1E5s6eL3dM/mGbKJF3HYcTNWmaqSTVhniGWD4NKcgpT7s0hEiy39zBjKB mNrrYcUT1HJaa034u15BhgNH5z1IKClzdwjSYK6r/junsAuIZhc= =e48p -----END PGP SIGNATURE----- Merge tag 'kvm-s390-next-7.4-2' of git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux into HEAD KVM: s390: Changes for 7.4 - rework page allocation for secure guest variable storage - selftests - documentation - replace get_zeroed_page with kzalloc - fix floating interrupt inject for corner cases - initial arm64 on s390 support This also contains the patch "arm64: Use proper include variant" as a build fix. It is also part of the arm tree.
This commit is contained in:
commit
814607cd40
|
|
@ -280,6 +280,24 @@ Allows user space to disable dea key wrapping, clearing the wrapping key.
|
|||
:Parameters: none
|
||||
:Returns: 0
|
||||
|
||||
4.5. ATTRIBUTE: KVM_S390_VM_CRYPTO_ENABLE_APIE (w/o)
|
||||
-------------------------------------------------------
|
||||
|
||||
Allows user space to enable AP interpretive execution.
|
||||
|
||||
:Parameters: none
|
||||
:Returns: 0 for success
|
||||
-EOPNOTSUPP if AP instructions not available for guest
|
||||
|
||||
4.6. ATTRIBUTE: KVM_S390_VM_CRYPTO_DISABLE_APIE (w/o)
|
||||
-------------------------------------------------------
|
||||
|
||||
Allows user space to disable AP interpretive execution.
|
||||
|
||||
:Parameters: none
|
||||
:Returns: 0 for success
|
||||
-EOPNOTSUPP if AP instructions not available for guest
|
||||
|
||||
5. GROUP: KVM_S390_VM_MIGRATION
|
||||
===============================
|
||||
|
||||
|
|
|
|||
17
MAINTAINERS
17
MAINTAINERS
|
|
@ -14377,6 +14377,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*
|
||||
|
|
@ -14389,6 +14390,22 @@ F: tools/testing/selftests/drivers/s390x/uvdevice/
|
|||
F: tools/testing/selftests/kvm/*/s390/
|
||||
F: tools/testing/selftests/kvm/s390/
|
||||
|
||||
KERNEL VIRTUAL MACHINE for arm-on-s390 (KVM/s390)
|
||||
R: Steffen Eiden <seiden@linux.ibm.com>
|
||||
R: Andreas Grapentin <gra@linux.ibm.com>
|
||||
R: Marc Zyngier <maz@kernel.org>
|
||||
L: kvm@vger.kernel.org
|
||||
L: linux-s390@vger.kernel.org
|
||||
S: Supported
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux.git
|
||||
F: arch/s390/include/arm64/
|
||||
F: arch/s390/include/asm/aef.h
|
||||
F: arch/s390/include/asm/kvm_host_arm64*
|
||||
F: arch/s390/include/asm/sae*
|
||||
F: arch/s390/kernel/aef.c
|
||||
F: arch/s390/kvm/arm64/
|
||||
F: arch/s390/tools/*arm64*
|
||||
|
||||
KERNEL VIRTUAL MACHINE FOR X86 (KVM/x86)
|
||||
M: Sean Christopherson <seanjc@google.com>
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@
|
|||
* come from here. The header relies on the definition of sys_reg()
|
||||
* earlier in this file.
|
||||
*/
|
||||
#include "asm/sysreg-defs.h"
|
||||
#include <asm/sysreg-defs.h>
|
||||
|
||||
/*
|
||||
* System registers, organised loosely by encoding but grouped together
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ config S390
|
|||
select GENERIC_ENTRY
|
||||
select GENERIC_GETTIMEOFDAY
|
||||
select GENERIC_SMP_IDLE_THREAD
|
||||
select GENERIC_IOREMAP if PCI
|
||||
select GENERIC_IOREMAP
|
||||
select GLOB
|
||||
select HAVE_ALIGNED_STRUCT_PAGE
|
||||
select HAVE_ARCH_AUDITSYSCALL
|
||||
|
|
@ -768,9 +768,6 @@ config PCI_NR_FUNCTIONS
|
|||
|
||||
endif # PCI
|
||||
|
||||
config HAS_IOMEM
|
||||
def_bool PCI
|
||||
|
||||
config CHSC_SCH
|
||||
def_tristate m
|
||||
prompt "Support for CHSC subchannels"
|
||||
|
|
|
|||
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__ */
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
#include <asm/bug.h>
|
||||
#include <asm/fpu.h>
|
||||
#include <asm/nospec-branch.h>
|
||||
#include <asm/sae.h>
|
||||
#include <asm-generic/asm-prototypes.h>
|
||||
|
||||
#endif /* _ASM_S390_PROTOTYPES_H */
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ enum {
|
|||
HWCAP_NR_NNPA = 20,
|
||||
HWCAP_NR_PCI_MIO = 21,
|
||||
HWCAP_NR_SIE = 22,
|
||||
HWCAP_NR_SAE = 23,
|
||||
HWCAP_NR_MAX
|
||||
};
|
||||
|
||||
|
|
@ -150,6 +151,7 @@ enum {
|
|||
#define HWCAP_NNPA BIT(HWCAP_NR_NNPA)
|
||||
#define HWCAP_PCI_MIO BIT(HWCAP_NR_PCI_MIO)
|
||||
#define HWCAP_SIE BIT(HWCAP_NR_SIE)
|
||||
#define HWCAP_SAE BIT(HWCAP_NR_SAE)
|
||||
|
||||
/*
|
||||
* These are used to set parameters in the core dumps.
|
||||
|
|
|
|||
|
|
@ -27,14 +27,8 @@ void unxlate_dev_mem_ptr(phys_addr_t phys, void *addr);
|
|||
/*
|
||||
* I/O memory mapping functions.
|
||||
*/
|
||||
#define ioremap_prot ioremap_prot
|
||||
#define iounmap iounmap
|
||||
|
||||
#define _PAGE_IOREMAP pgprot_val(PAGE_KERNEL)
|
||||
|
||||
#define ioremap_wc(addr, size) \
|
||||
ioremap_prot((addr), (size), pgprot_writecombine(PAGE_KERNEL))
|
||||
|
||||
static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
|
||||
{
|
||||
return NULL;
|
||||
|
|
@ -46,6 +40,12 @@ static inline void ioport_unmap(void __iomem *p)
|
|||
|
||||
#ifdef CONFIG_PCI
|
||||
|
||||
#define ioremap_prot ioremap_prot
|
||||
#define iounmap iounmap
|
||||
|
||||
#define ioremap_wc(addr, size) \
|
||||
ioremap_prot((addr), (size), pgprot_writecombine(PAGE_KERNEL))
|
||||
|
||||
/*
|
||||
* s390 needs a private implementation of pci_iomap since ioremap with its
|
||||
* offset parameter isn't sufficient. That's because BAR spaces are not
|
||||
|
|
|
|||
6
arch/s390/include/asm/kvm.h
Normal file
6
arch/s390/include/asm/kvm.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifdef KVM_S390_ARM64
|
||||
#include <uapi/arm64/kvm.h>
|
||||
#else
|
||||
#include <uapi/asm/kvm.h>
|
||||
#endif
|
||||
|
|
@ -3,7 +3,11 @@
|
|||
#ifndef ASM_KVM_HOST_H
|
||||
#define ASM_KVM_HOST_H
|
||||
|
||||
#ifdef KVM_S390_ARM64
|
||||
#include <asm/kvm_host_arm64.h>
|
||||
#else
|
||||
#include <asm/kvm_host_s390.h>
|
||||
#endif
|
||||
|
||||
#define PGM_OPERATION 0x01
|
||||
#define PGM_PRIVILEGED_OP 0x02
|
||||
|
|
|
|||
209
arch/s390/include/asm/kvm_host_arm64.h
Normal file
209
arch/s390/include/asm/kvm_host_arm64.h
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef ASM_KVM_HOST_ARM64_H
|
||||
#define ASM_KVM_HOST_ARM64_H
|
||||
|
||||
#include <linux/bug.h>
|
||||
|
||||
#include <asm/kvm_host_types.h>
|
||||
#include <asm/debug.h>
|
||||
|
||||
#define vcpu_gp_regs(v) ((v)->arch.sae_block.gpr)
|
||||
|
||||
#include <arm64/kvm_host.h>
|
||||
#include <arm64/ptrace.h>
|
||||
|
||||
#include <asm/sae.h>
|
||||
|
||||
#define KVM_HAVE_MMU_RWLOCK
|
||||
#define KVM_MAX_VCPUS 1
|
||||
#define __KVM_HAVE_VCPU_RUN_PID_CHANGE
|
||||
|
||||
#define KVM_S390_ARM64_IMPL_FEATURES 0
|
||||
#define KVM_HALT_POLL_NS_DEFAULT 50000
|
||||
|
||||
#define KVM_S390_MANAGES_S390_GUEST 0
|
||||
|
||||
/* Minimal (=no) vgic definitions */
|
||||
#define KVM_IRQCHIP_NUM_PINS 1
|
||||
#define irqchip_in_kernel(_k) false
|
||||
|
||||
#define __ctxt_sys_reg(ctx, reg) NULL
|
||||
struct kvm_cpu_context {
|
||||
/*
|
||||
* These are just for 32 bit, which we don't have, making them RES0.
|
||||
* They are exposed to user space.
|
||||
*/
|
||||
u64 spsr_abt;
|
||||
u64 spsr_und;
|
||||
u64 spsr_irq;
|
||||
u64 spsr_fiq;
|
||||
|
||||
__vector128 __aligned(16) vregs[32];
|
||||
};
|
||||
|
||||
struct kvm_vcpu_arch {
|
||||
struct kvm_sae_block sae_block;
|
||||
struct kvm_sae_save_area save_area;
|
||||
struct kvm_cpu_context ctxt;
|
||||
|
||||
u32 host_acrs[NUM_ACRS];
|
||||
|
||||
/* Hypervisor Configuration Register */
|
||||
u64 hcr_el2;
|
||||
|
||||
/* Configuration flags, set once and for all before the vcpu can run */
|
||||
u8 cflags;
|
||||
|
||||
/* Input flags to the hypervisor code, potentially cleared after use */
|
||||
u8 iflags;
|
||||
|
||||
/* State flags for kernel bookkeeping, unused by the hypervisor code */
|
||||
u8 sflags;
|
||||
|
||||
/*
|
||||
* Don't run the guest (internal implementation need).
|
||||
*
|
||||
* Contrary to the flags above, this is set/cleared outside of
|
||||
* a vcpu context, and thus cannot be mixed with the flags
|
||||
* themselves (or the flag accesses need to be made atomic).
|
||||
*/
|
||||
bool pause;
|
||||
|
||||
/* vcpu power state */
|
||||
struct kvm_mp_state mp_state;
|
||||
/* lock for mp_state & reset_state.reset */
|
||||
spinlock_t mp_state_lock;
|
||||
|
||||
/* vcpu reset state */
|
||||
struct vcpu_reset_state reset_state;
|
||||
|
||||
struct kvm_s390_mmu_cache *mc;
|
||||
|
||||
void *debugfs_state_data;
|
||||
};
|
||||
|
||||
struct kvm_vcpu_stat {
|
||||
struct kvm_vcpu_stat_generic generic;
|
||||
/* ARM64 stats */
|
||||
u64 hvc_exit_stat;
|
||||
u64 wfe_exit_stat;
|
||||
u64 wfi_exit_stat;
|
||||
u64 mmio_exit_user;
|
||||
u64 mmio_exit_kernel;
|
||||
u64 signal_exits;
|
||||
u64 exits;
|
||||
/* GMAP stats */
|
||||
u64 pfault_sync;
|
||||
};
|
||||
|
||||
#define kvm_has_mte(_kvm) false
|
||||
#define vcpu_has_sve(_vcpu) false
|
||||
#define vcpu_has_ptrauth(_vcpu) false
|
||||
|
||||
struct kvm_arch_memory_slot {
|
||||
};
|
||||
|
||||
struct kvm_arch {
|
||||
struct gmap *gmap;
|
||||
u64 guest_phys_size;
|
||||
|
||||
/* VM-wide vCPU feature set */
|
||||
unsigned long flags;
|
||||
|
||||
/* Protects VM-scoped configuration data */
|
||||
struct mutex config_lock;
|
||||
|
||||
debug_info_t *dbf;
|
||||
|
||||
DECLARE_BITMAP(vcpu_features, KVM_VCPU_MAX_FEATURES);
|
||||
|
||||
unsigned long mem_limit;
|
||||
};
|
||||
|
||||
static inline bool __vcpu_has_feature(const struct kvm_arch *ka, int feature)
|
||||
{
|
||||
return test_bit(feature, ka->vcpu_features);
|
||||
}
|
||||
|
||||
struct kvm_vm_stat {
|
||||
struct kvm_vm_stat_generic generic;
|
||||
};
|
||||
|
||||
static inline bool system_has_full_ptr_auth(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#define kvm_vm_is_protected(_kvm) false
|
||||
#define vcpu_is_protected(_vcpu) false
|
||||
|
||||
#define vcpu_is_loaded(_vcpu) ((_vcpu)->cpu != -1)
|
||||
|
||||
#define KVM_HVA_ERR_BAD (-1UL)
|
||||
#define KVM_HVA_ERR_RO_BAD (-2UL)
|
||||
|
||||
static inline bool kvm_is_error_hva(unsigned long addr)
|
||||
{
|
||||
return IS_ERR_VALUE(addr);
|
||||
}
|
||||
|
||||
u32 get_kvm_ipa_limit(void);
|
||||
|
||||
/* unused, but required functions */
|
||||
static inline void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *slot) {}
|
||||
static inline void kvm_arch_memslots_updated(struct kvm *kvm, u64 gen) {}
|
||||
static inline void kvm_arch_flush_shadow_all(struct kvm *kvm) {}
|
||||
static inline void kvm_arch_flush_shadow_memslot(struct kvm *kvm, struct kvm_memory_slot *slot) {}
|
||||
static inline void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) {}
|
||||
static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu) {}
|
||||
static inline void kvm_arch_sync_events(struct kvm *kvm) {}
|
||||
static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {}
|
||||
static inline void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
|
||||
struct kvm_memory_slot *slot,
|
||||
gfn_t gfn_offset,
|
||||
unsigned long mask) {}
|
||||
|
||||
int kvm_handle_guest_abort(struct kvm_vcpu *vcpu);
|
||||
void kvm_reset_vcpu(struct kvm_vcpu *vcpu);
|
||||
|
||||
/* arm64 guests do not use async-pf. Defined because Kbuild requires it as s390 kvm turns it on. */
|
||||
#define ASYNC_PF_PER_VCPU 0
|
||||
struct kvm_arch_async_pf {
|
||||
unsigned long pfault_token;
|
||||
};
|
||||
|
||||
#define __unsupp_async_call(fn) WARN_ONCE(true, "async not supported on kvm-arm64 %s", fn)
|
||||
|
||||
static inline bool kvm_arch_can_dequeue_async_page_present(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
__unsupp_async_call(__func__);
|
||||
return false;
|
||||
};
|
||||
|
||||
static inline void kvm_arch_async_page_ready(struct kvm_vcpu *vcpu,
|
||||
struct kvm_async_pf *work)
|
||||
{
|
||||
__unsupp_async_call(__func__);
|
||||
};
|
||||
|
||||
static inline bool kvm_arch_async_page_not_present(struct kvm_vcpu *vcpu,
|
||||
struct kvm_async_pf *work)
|
||||
{
|
||||
__unsupp_async_call(__func__);
|
||||
return false;
|
||||
};
|
||||
|
||||
static inline void kvm_arch_async_page_present(struct kvm_vcpu *vcpu,
|
||||
struct kvm_async_pf *work)
|
||||
{
|
||||
__unsupp_async_call(__func__);
|
||||
};
|
||||
|
||||
static inline void kvm_arch_async_page_present_queued(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
__unsupp_async_call(__func__);
|
||||
};
|
||||
|
||||
#define kvm_supports_32bit_el0() false
|
||||
|
||||
#endif /* ASM_KVM_HOST_ARM64_H */
|
||||
124
arch/s390/include/asm/kvm_host_arm64_types.h
Normal file
124
arch/s390/include/asm/kvm_host_arm64_types.h
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef ASM_KVM_HOST_ARM64_TYPES_H
|
||||
#define ASM_KVM_HOST_ARM64_TYPES_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kvm_types.h>
|
||||
#include <linux/compiler_attributes.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/fault.h>
|
||||
|
||||
struct kvm_sae_block {
|
||||
u64 _0000[16]; /* 0x0000 */
|
||||
#define SAE_ICPTR_SPURIOUS 0x00
|
||||
#define SAE_ICPTR_VALIDITY 0x01
|
||||
#define SAE_ICPTR_HOST_ACCESS_EXCEPTION 0x02
|
||||
#define SAE_ICPTR_SYNCHRONOUS_EXCEPTION 0x03
|
||||
#define SAE_ICPTR_TIMER 0x04
|
||||
#define SAE_ICPTR_PE_INTERCOMM 0x05
|
||||
#define SAE_ICPTR_GUEST_ADDRESS_SIZE 0x06
|
||||
#define SAE_ICPTR_STOP 0x07
|
||||
#define SAE_ICPTR_MIO_ADDRESS 0x08
|
||||
#define SAE_ICPTR_PMU 0x09
|
||||
#define SAE_ICPTR_MAINTENANCE 0x0a
|
||||
u8 icptr; /* 0x0080 */
|
||||
u8 _0081[7]; /* 0x0081 */
|
||||
u64 scad; /* 0x0088 */
|
||||
u64 _0090[16]; /* 0x0090 */
|
||||
u32 cntp_ctl; /* 0x0110 */
|
||||
u32 cntv_ctl; /* 0x0114 */
|
||||
u8 irq_ctl; /* 0x0118 */
|
||||
u8 _0119[7]; /* 0x0119 */
|
||||
struct {
|
||||
u64 ich_hcr_el2; /* 0x0120 */
|
||||
u64 ich_vmcr_el2; /* 0x0128 */
|
||||
u64 ich_ap0r0_el2; /* 0x0130 */
|
||||
u64 ich_ap1r0_el2; /* 0x0138 */
|
||||
u64 _0140[2]; /* 0x0140 */
|
||||
u64 ich_lrn_el2[4]; /* 0x0150 */
|
||||
u64 _0170[4]; /* 0x0170 */
|
||||
} ic_regs;
|
||||
u64 _0190[12]; /* 0x0190 */
|
||||
u64 contextidr_el1; /* 0x01f0 */
|
||||
u32 wip; /* 0x01f8 */
|
||||
u32 _01fc; /* 0x01fc */
|
||||
#define SAE_SD_FORMAT_0 0x00
|
||||
u8 sdf; /* 0x0200 */
|
||||
u8 _0201[7]; /* 0x0201 */
|
||||
u64 mso; /* 0x0208 */
|
||||
u64 msl; /* 0x0210 */
|
||||
u64 hbasce; /* 0x0218 */
|
||||
u64 _0220; /* 0x0220 */
|
||||
u64 gpto; /* 0x0228 */
|
||||
u64 ic; /* 0x0230 */
|
||||
u64 ec; /* 0x0238 */
|
||||
u64 save_area; /* 0x0240 */
|
||||
u64 _0248[7]; /* 0x0248 */
|
||||
u8 _0280[6]; /* 0x0280 */
|
||||
u16 lrcpua; /* 0x0286 */
|
||||
u64 pstate; /* 0x0288 */
|
||||
u64 pc; /* 0x0290 */
|
||||
u64 sp_el0; /* 0x0298 */
|
||||
u64 sp_el1; /* 0x02a0 */
|
||||
u64 _02a8; /* 0x02a8 */
|
||||
struct { /* 0x02b0 */
|
||||
u32 _fpcr_res0;
|
||||
u32 fpcr;
|
||||
};
|
||||
struct { /* 0x02b8 */
|
||||
u32 _fpsr_res0;
|
||||
u32 fpsr;
|
||||
};
|
||||
u16 sve_pregs[16]; /* 0x02c0 */
|
||||
u16 sve_ffr; /* 0x02e0 */
|
||||
u8 _02e2[6]; /* 0x02e2 */
|
||||
u64 _02e8[3]; /* 0x02e8 */
|
||||
|
||||
u64 gpr[31]; /* 0x0300 */
|
||||
u64 _03f8; /* 0x03f8 */
|
||||
|
||||
union {
|
||||
u64 icptd[8]; /* 0x0400 */
|
||||
/* validity-interception reason; icptr 0x01 */
|
||||
u16 vir; /* 0x0400 */
|
||||
/* host access interception details; icptr 0x02 */
|
||||
struct {
|
||||
u64 esr_el2; /* 0x0400 */
|
||||
u8 _0408[6]; /* 0x0408 */
|
||||
u16 pic; /* 0x040e */
|
||||
union teid teid; /* 0x0410 */
|
||||
gva_t far_el2; /* 0x0418 */
|
||||
gva_t vaddr; /* 0x0420 */
|
||||
u64 suppl; /* 0x0428 */
|
||||
u8 gltl; /* 0x0430 */
|
||||
u8 _0431[7]; /* 0x0431 */
|
||||
u64 _0438; /* 0x0438 */
|
||||
} hai;
|
||||
/* exception-interception details; icptr 0x03 */
|
||||
struct {
|
||||
u64 esr_el2; /* 0x0400 */
|
||||
u64 _0408[2]; /* 0x0408 */
|
||||
gva_t far_el2; /* 0x0418 */
|
||||
} trap;
|
||||
/* timer-interception reason; icptr 0x04 */
|
||||
#define SAE_IR_TIMER_ID_VIRT BIT(6)
|
||||
#define SAE_IR_TIMER_ID_PHYS BIT(7)
|
||||
u8 tir; /* 0x0400 */
|
||||
};
|
||||
u64 _0440[376]; /* 0x0440 */
|
||||
} __packed __aligned(PAGE_SIZE);
|
||||
static_assert(sizeof(struct kvm_sae_block) == PAGE_SIZE);
|
||||
|
||||
struct kvm_sae_save_area {
|
||||
#define SAE_SAVE_AREA_FORMAT_0 0x00
|
||||
u8 saf; /* 0x0000 */
|
||||
u8 _0001[5]; /* 0x0001 */
|
||||
#define SAE_SAS_VALID BIT(0)
|
||||
u16 sas; /* 0x0006 */
|
||||
u64 sdo; /* 0x0008 */
|
||||
u64 _0010[2]; /* 0x0010 */
|
||||
u64 regs[508]; /* 0x0020 */
|
||||
} __packed __aligned(PAGE_SIZE);
|
||||
static_assert(sizeof(struct kvm_sae_save_area) == PAGE_SIZE);
|
||||
|
||||
#endif /* ASM_KVM_HOST_ARM64_TYPES_H */
|
||||
|
|
@ -3,6 +3,10 @@
|
|||
#ifndef ASM_KVM_HOST_TYPES_H
|
||||
#define ASM_KVM_HOST_TYPES_H
|
||||
|
||||
#ifdef KVM_S390_ARM64
|
||||
#include <asm/kvm_host_arm64_types.h>
|
||||
#else
|
||||
#include <asm/kvm_host_s390_types.h>
|
||||
#endif /* KVM_S390_ARM64 */
|
||||
|
||||
#endif /* ASM_KVM_HOST_TYPES_H */
|
||||
|
|
|
|||
49
arch/s390/include/asm/sae.h
Normal file
49
arch/s390/include/asm/sae.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __ASM_S390_SAE_H
|
||||
#define __ASM_S390_SAE_H
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
/* defined in arch/s390/kernel/entry.S */
|
||||
asmlinkage void sae(phys_addr_t sae_block_phys);
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <asm/kvm_host_arm64_types.h>
|
||||
|
||||
/**
|
||||
* stiasrm() - STore and Invalidate Arm System Register Multiple
|
||||
* @save_area: Pointer to SAE save area
|
||||
*
|
||||
* Store the guest system registers to the save area.
|
||||
* The values in the guest are no longer valid.
|
||||
*/
|
||||
static __always_inline void stiasrm(struct kvm_sae_save_area *save_area)
|
||||
{
|
||||
asm_inline volatile(
|
||||
" .insn rre,0xb9a70000,%[r1],0\n"
|
||||
: "+m" (*save_area)
|
||||
: [r1] "a" (save_area)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* lasrm() - Load Arm System Register Multiple
|
||||
*
|
||||
* @save_area: Pointer to SAE save area
|
||||
*
|
||||
* Load the system registers from save_area into the guest.
|
||||
*/
|
||||
static __always_inline void lasrm(struct kvm_sae_save_area *save_area)
|
||||
{
|
||||
asm_inline volatile(
|
||||
" .insn rre,0xb9a60000,%[r1],0\n"
|
||||
:
|
||||
: "m" (*save_area), [r1] "a" (save_area)
|
||||
);
|
||||
}
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
#endif /* __ASM_S390_SAE_H */
|
||||
|
|
@ -56,7 +56,9 @@ struct sclp_core_entry {
|
|||
u8 siif : 1;
|
||||
u8 sigpif : 1;
|
||||
u8 : 3;
|
||||
u8 reserved2[3];
|
||||
u8 aef: 1;
|
||||
u8 : 7;
|
||||
u8 reserved2[2];
|
||||
u8 : 2;
|
||||
u8 ib : 1;
|
||||
u8 cei : 1;
|
||||
|
|
@ -109,6 +111,7 @@ struct sclp_info {
|
|||
unsigned char has_aeni : 1;
|
||||
unsigned char has_aisi : 1;
|
||||
unsigned char has_astfleie2 : 1;
|
||||
unsigned char has_aef : 1;
|
||||
unsigned int ibc;
|
||||
unsigned int mtid;
|
||||
unsigned int mtid_cp;
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ static inline bool on_stack(struct stack_info *info,
|
|||
struct stack_frame {
|
||||
union {
|
||||
unsigned long empty[9];
|
||||
/* SIE stack frame */
|
||||
struct {
|
||||
unsigned long sie_control_block;
|
||||
unsigned long sie_savearea;
|
||||
|
|
@ -68,6 +69,10 @@ struct stack_frame {
|
|||
unsigned long sie_guest_asce;
|
||||
unsigned long sie_irq;
|
||||
};
|
||||
/* SAE stack frame */
|
||||
struct {
|
||||
unsigned long sae_bear;
|
||||
};
|
||||
};
|
||||
unsigned long gprs[10];
|
||||
unsigned long back_chain;
|
||||
|
|
|
|||
6
arch/s390/include/uapi/Kbuild
Normal file
6
arch/s390/include/uapi/Kbuild
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# The ARM64 uapi headers under generated/uapi/arm64/ are private copies used
|
||||
# internally by the s390 KVM ARM64 implementation. They contain #line
|
||||
# directives and rewritten include paths and must not be shipped to userspace.
|
||||
no-export-headers += arm64/%
|
||||
|
|
@ -70,6 +70,7 @@ int main(void)
|
|||
OFFSET(__SF_SIE_CONTROL_PHYS, stack_frame, sie_control_block_phys);
|
||||
OFFSET(__SF_SIE_GUEST_ASCE, stack_frame, sie_guest_asce);
|
||||
OFFSET(__SF_SIE_IRQ, stack_frame, sie_irq);
|
||||
OFFSET(__SF_SAE_BEAR, stack_frame, sae_bear);
|
||||
DEFINE(STACK_FRAME_OVERHEAD, sizeof(struct stack_frame));
|
||||
BLANK();
|
||||
OFFSET(__SFUSER_BACKCHAIN, stack_frame_user, back_chain);
|
||||
|
|
|
|||
|
|
@ -245,6 +245,28 @@ EXPORT_SYMBOL(__sie64a)
|
|||
EXPORT_SYMBOL(sie_exit)
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_KVM)
|
||||
/*
|
||||
* SAE calling convention:
|
||||
* %r2 physical address of the SAE control block
|
||||
*/
|
||||
SYM_FUNC_START(sae)
|
||||
stmg %r6,%r14,__SF_GPRS(%r15) # store kernel registers
|
||||
STBEAR __SF_SAE_BEAR(%r15) # save breaking event address register
|
||||
.insn rre,0xb9a50000,%r2,0 # Start Arm Execution
|
||||
LBEAR __SF_SAE_BEAR(%r15) # restore breaking event address register
|
||||
lmg %r6,%r14,__SF_GPRS(%r15) # restore kernel registers
|
||||
xgr %r0,%r0 # clear guest registers to
|
||||
xgr %r1,%r1 # prevent speculative use
|
||||
xgr %r2,%r2
|
||||
xgr %r3,%r3
|
||||
xgr %r4,%r4
|
||||
xgr %r5,%r5
|
||||
BR_EX %r14
|
||||
SYM_FUNC_END(sae)
|
||||
EXPORT_SYMBOL(sae)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SVC interrupt handler routine. System calls are synchronous events and
|
||||
* are entered with interrupts disabled.
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ static void show_cpu_summary(struct seq_file *m, void *v)
|
|||
[HWCAP_NR_NNPA] = "nnpa",
|
||||
[HWCAP_NR_PCI_MIO] = "pcimio",
|
||||
[HWCAP_NR_SIE] = "sie",
|
||||
[HWCAP_NR_SAE] = "sae",
|
||||
};
|
||||
int i, cpu;
|
||||
|
||||
|
|
@ -254,6 +255,8 @@ static int __init setup_hwcaps(void)
|
|||
/* virtualization support */
|
||||
if (sclp.has_sief2)
|
||||
elf_hwcap |= HWCAP_SIE;
|
||||
if (sclp.has_aef)
|
||||
elf_hwcap |= HWCAP_SAE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/swap.h>
|
||||
#include <linux/pagewalk.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <asm/facility.h>
|
||||
#include <asm/sections.h>
|
||||
|
|
@ -242,34 +243,38 @@ void uv_free_stor_var(void *stor_var)
|
|||
}
|
||||
EXPORT_SYMBOL_FOR_MODULES(uv_free_stor_var, "kvm");
|
||||
|
||||
static int uv_alloc_range_cb(pte_t *ptep, unsigned long addr, void *data)
|
||||
{
|
||||
struct page *page;
|
||||
pte_t pte;
|
||||
|
||||
page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
pte = __pte(page_to_phys(page) | pgprot_val(PAGE_KERNEL));
|
||||
set_pte(ptep, pte);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *uv_alloc_stor_var(unsigned long size)
|
||||
{
|
||||
unsigned long i, nr_pages, addr;
|
||||
struct vm_struct *area;
|
||||
unsigned long addr;
|
||||
struct page **pages;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
nr_pages = size >> PAGE_SHIFT;
|
||||
area = get_vm_area(size, VM_SPARSE);
|
||||
if (!area)
|
||||
return NULL;
|
||||
pages = kvcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL_ACCOUNT);
|
||||
if (!pages)
|
||||
goto free_area;
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
pages[i] = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
|
||||
if (!pages[i])
|
||||
goto free_pages;
|
||||
}
|
||||
addr = (unsigned long)area->addr;
|
||||
if (apply_to_page_range(&init_mm, addr, size, uv_alloc_range_cb, NULL))
|
||||
goto out;
|
||||
if (vm_area_map_pages(area, addr, addr + size, pages))
|
||||
goto free_pages;
|
||||
kvfree(pages);
|
||||
return area->addr;
|
||||
out:
|
||||
uv_free_stor_var(area->addr);
|
||||
free_pages:
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
if (pages[i])
|
||||
__free_page(pages[i]);
|
||||
}
|
||||
kvfree(pages);
|
||||
free_area:
|
||||
free_vm_area(area);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_FOR_MODULES(uv_alloc_stor_var, "kvm");
|
||||
|
|
|
|||
|
|
@ -30,15 +30,22 @@ config KVM
|
|||
select KVM_MMU_LOCKLESS_AGING
|
||||
select KVM_GENERIC_PRE_FAULT_MEMORY
|
||||
help
|
||||
Support hosting paravirtualized guest machines using the SIE
|
||||
virtualization capability on the mainframe. This should work
|
||||
on any 64bit machine.
|
||||
Enable support for hosting virtualized, hardware-accelerated
|
||||
virtual machines via two modules: kvm and kvm_arm64.
|
||||
|
||||
This module provides access to the hardware capabilities through
|
||||
a character device node named /dev/kvm.
|
||||
The kvm module provides access to host hardware-accelerated s390
|
||||
guests using the Start Interpretive Execution (SIE) instruction via a
|
||||
character device node named /dev/kvm. This requires a 64-bit machine
|
||||
(hardware model).
|
||||
|
||||
To compile this as a module, choose M here: the module
|
||||
will be called kvm.
|
||||
The kvm_arm64 module provides access to host hardware-accelerated
|
||||
arm64 guests using the Start ARM Execution (SAE) instruction via a
|
||||
character device node named /dev/kvm_arm64. This requires hardware
|
||||
models that support the Arm execution facility (AEF).
|
||||
|
||||
To compile this driver as a module, choose M here. The module will be
|
||||
called kvm for the SIE acceleration and kvm_arm64 for the SAE
|
||||
acceleration.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@
|
|||
# Copyright IBM Corp. 2008
|
||||
|
||||
obj-$(CONFIG_KVM) += s390/
|
||||
obj-$(CONFIG_KVM) += arm64/
|
||||
|
|
|
|||
2
arch/s390/kvm/arm64/.gitignore
vendored
Normal file
2
arch/s390/kvm/arm64/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
generated/*
|
||||
110
arch/s390/kvm/arm64/Makefile
Normal file
110
arch/s390/kvm/arm64/Makefile
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
KVM := ../../../../virt/kvm
|
||||
KVM_DEV_NAME = kvm_arm64
|
||||
KVM_DEV_MINOR = MISC_DYNAMIC_MINOR
|
||||
include $(srctree)/virt/kvm/Makefile.kvm
|
||||
include $(srctree)/arch/$(SRCARCH)/kvm/gmap/Makefile
|
||||
include $(src)/Makefile.gen
|
||||
|
||||
ccflags-y += -I $(src) -I $(objtree)/$(obj) -I$(srctree)/arch/$(SRCARCH)/kvm/gmap -DKVM_S390_ARM64
|
||||
|
||||
kvm-arm64-local := \
|
||||
arm.o \
|
||||
guest.o \
|
||||
handle_exit.o \
|
||||
inject_fault.o \
|
||||
mmio.o \
|
||||
mmu.o \
|
||||
reset.o
|
||||
|
||||
kvm-arm64-obj := $(kvm-arm64-local)
|
||||
kvm-arm64-obj += $(patsubst %.o,%-arm64.o,$(kvm-y))
|
||||
kvm-arm64-obj += $(patsubst %.o,%-arm64.o,$(gmap-y))
|
||||
|
||||
targets += $(kvm-arm64-obj)
|
||||
|
||||
$(obj)/%-arm64.o: $(src)/%.c FORCE
|
||||
@mkdir -p $(dir $@)
|
||||
$(call if_changed_rule,cc_o_c)
|
||||
$(call cmd,force_checksrc)
|
||||
|
||||
# %-arm64.o stems don't match any kvm-arm64-y entry so Kbuild derives wrong
|
||||
# KBUILD_MODNAME/KBUILD_MODFILE. Force include a generated header to override.
|
||||
KVM_ARM64_MODNAME_H := $(obj)/generated/kvm_arm64_modname.h
|
||||
ccflags-y += -include $(KVM_ARM64_MODNAME_H)
|
||||
|
||||
$(addprefix $(obj)/,$(kvm-arm64-obj)): $(KVM_ARM64_MODNAME_H)
|
||||
$(foreach sfx, i s ll lst, \
|
||||
$(eval $(patsubst %.o,%.$(sfx),$(addprefix $(obj)/,$(kvm-arm64-local))): $(KVM_ARM64_MODNAME_H)))
|
||||
|
||||
targets += generated/kvm_arm64_modname.h
|
||||
|
||||
define filechk_kvm_arm64_modname
|
||||
echo '#ifndef __KVM_ARM64_MODNAME_H'; \
|
||||
echo '#define __KVM_ARM64_MODNAME_H'; \
|
||||
echo '#undef KBUILD_MODNAME'; \
|
||||
echo '#define KBUILD_MODNAME "$(KVM_DEV_NAME)"'; \
|
||||
echo '#undef __KBUILD_MODNAME'; \
|
||||
echo '#define __KBUILD_MODNAME $(KVM_DEV_NAME)'; \
|
||||
echo '#undef KBUILD_MODFILE'; \
|
||||
echo '#define KBUILD_MODFILE "$(obj)/kvm-arm64"'; \
|
||||
echo '#endif /* __KVM_ARM64_MODNAME_H */'
|
||||
endef
|
||||
|
||||
$(KVM_ARM64_MODNAME_H): FORCE
|
||||
$(call filechk,kvm_arm64_modname)
|
||||
|
||||
# Members are not in real-obj-y. Set part-of-builtin to enable instrumentation.
|
||||
$(addprefix $(obj)/,$(kvm-arm64-obj)): private part-of-builtin := y
|
||||
|
||||
ifeq ($(CONFIG_KVM),m)
|
||||
|
||||
kvm-arm64-y = $(kvm-arm64-obj)
|
||||
|
||||
else ifeq ($(CONFIG_KVM),y)
|
||||
|
||||
prereq-o-cmd = $(foreach o, $(filter %.o, $^), $(dir $(o)).$(notdir $(o)).cmd)
|
||||
cmd_gen_symversions_o = \
|
||||
grep --no-filename "^\#SYMVER" $(prereq-o-cmd) >> $(dot-target).cmd || true
|
||||
|
||||
cmd_ld_o_o = $(cmd_ld)
|
||||
define rule_ld_o_o
|
||||
$(call cmd_and_savecmd,ld)
|
||||
$(call cmd,gen_symversions_o)
|
||||
endef
|
||||
|
||||
LDFLAGS_kvm-unnamespaced.o := -r
|
||||
$(obj)/kvm-unnamespaced.o: $(addprefix $(obj)/,$(kvm-arm64-obj)) FORCE
|
||||
$(call if_changed_rule,ld_o_o)
|
||||
|
||||
quiet_cmd_nm_filter = NMFLTR $@
|
||||
cmd_nm_filter = $(NM) --defined-only --format=just-symbols $< | sort -u > $@
|
||||
|
||||
$(obj)/kvm_symbol_list: $(obj)/kvm-unnamespaced.o FORCE
|
||||
$(call if_changed,nm_filter)
|
||||
|
||||
# cmd_oc_o_o carries -DKBUILD_MODFILE so generate_builtin_ranges.awk
|
||||
# can attribute kvm-namespaced.o to kvm_arm64 via its .cmd file.
|
||||
cmd_oc_o_o = $(cmd_objcopy) -DKBUILD_MODFILE='"$(obj)/kvm-arm64"'
|
||||
define rule_oc_o_o
|
||||
$(call cmd,objcopy)
|
||||
@printf '%s\n' 'savedcmd_$@ := $(call make-cmd,oc_o_o)' > $(dot-target).cmd
|
||||
$(call cmd,gen_objtooldep)
|
||||
$(call cmd,gen_symversions_o)
|
||||
endef
|
||||
|
||||
OBJCOPYFLAGS_kvm-namespaced.o := -O $(LD_BFD) --localize-symbols=$(obj)/kvm_symbol_list
|
||||
$(obj)/kvm-namespaced.o: $(obj)/kvm-unnamespaced.o $(obj)/kvm_symbol_list FORCE
|
||||
$(call if_changed_rule,oc_o_o)
|
||||
|
||||
kvm-arm64-y = kvm-namespaced.o
|
||||
|
||||
targets += kvm-unnamespaced.o kvm_symbol_list kvm-namespaced.o
|
||||
|
||||
endif
|
||||
|
||||
clean-files += kvm-unnamespaced.o kvm_symbol_list kvm-namespaced.o
|
||||
|
||||
obj-$(CONFIG_KVM) += kvm-arm64.o
|
||||
|
||||
28
arch/s390/kvm/arm64/Makefile.gen
Normal file
28
arch/s390/kvm/arm64/Makefile.gen
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Extracts ARM64 marked sections to .inc files that are included by main source
|
||||
# files
|
||||
|
||||
# List of ARM64 C files to extract
|
||||
ARM64_CFILES := \
|
||||
arm.c \
|
||||
guest.c \
|
||||
mmio.c
|
||||
|
||||
quiet_cmd_extract_inc = GEN $@
|
||||
cmd_extract_inc = mkdir -p $(dir $@); \
|
||||
$(AWK) -f $(src)/copy-arm64c.awk $< > $@ || \
|
||||
{ echo "Error: No or unbalanced ARM64_S390_COMMON markers in $<" >&2; rm -f $@; exit 1; }
|
||||
|
||||
ARM64_INC_FILES := $(foreach cfile,$(ARM64_CFILES),generated/$(basename $(cfile)).inc)
|
||||
|
||||
$(addprefix $(obj)/,$(ARM64_INC_FILES)): $(obj)/generated/%.inc: $(srctree)/arch/arm64/kvm/%.c $(src)/copy-arm64c.awk FORCE
|
||||
$(call if_changed,extract_inc)
|
||||
|
||||
targets += $(ARM64_INC_FILES)
|
||||
clean-files += generated/*.inc
|
||||
|
||||
$(foreach cfile,$(basename $(ARM64_CFILES)),\
|
||||
$(eval $(obj)/$(cfile).o $(obj)/$(cfile).i $(obj)/$(cfile).s \
|
||||
$(obj)/$(cfile).ll $(obj)/$(cfile).lst: \
|
||||
$(obj)/generated/$(cfile).inc))
|
||||
754
arch/s390/kvm/arm64/arm.c
Normal file
754
arch/s390/kvm/arm64/arm.c
Normal file
|
|
@ -0,0 +1,754 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/kvm_types.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/fpu.h>
|
||||
|
||||
#include <asm/access-regs.h>
|
||||
|
||||
#include <arm64/kvm_emulate.h>
|
||||
#include <arm64/sysreg.h>
|
||||
|
||||
#include <gmap.h>
|
||||
#include <kvm_mmu.h>
|
||||
|
||||
#include "arm.h"
|
||||
#include "handle_exit.h"
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace.h"
|
||||
|
||||
static unsigned long system_supported_vcpu_features(struct kvm_vcpu *vcpu);
|
||||
|
||||
#define __INCL_GEN_ARM_FILE
|
||||
#include "generated/arm.inc"
|
||||
#undef __INCL_GEN_ARM_FILE
|
||||
|
||||
#define KVM_PHYS_SHIFT (40)
|
||||
|
||||
int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (ext) {
|
||||
case KVM_CAP_NR_VCPUS:
|
||||
case KVM_CAP_MAX_VCPUS:
|
||||
case KVM_CAP_MAX_VCPU_ID:
|
||||
ret = KVM_MAX_VCPUS;
|
||||
break;
|
||||
case KVM_CAP_ARM_VM_IPA_SIZE:
|
||||
ret = get_kvm_ipa_limit();
|
||||
break;
|
||||
case KVM_CAP_IOEVENTFD:
|
||||
case KVM_CAP_PRE_FAULT_MEMORY:
|
||||
ret = 1;
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u64 kvm_max_guest_address(void)
|
||||
{
|
||||
u64 max_addr;
|
||||
|
||||
max_addr = min_t(u64, TASK_SIZE_MAX, sclp.hamax);
|
||||
max_addr = max_t(u64, max_addr, SZ_1G - 1);
|
||||
return ALIGN_DOWN(max_addr + 1, SZ_1G) - 1;
|
||||
}
|
||||
|
||||
static int kvm_gmap_init(struct kvm *kvm)
|
||||
{
|
||||
struct crst_table *table;
|
||||
|
||||
kvm->arch.gmap = gmap_new(kvm, gpa_to_gfn(kvm->arch.guest_phys_size));
|
||||
|
||||
if (!kvm->arch.gmap)
|
||||
return -ENOMEM;
|
||||
|
||||
/* arm64 (on s390) do not have pfault */
|
||||
clear_bit(GMAP_FLAG_PFAULT_ENABLED, &kvm->arch.gmap->flags);
|
||||
set_bit(GMAP_FLAG_ALLOW_HPAGE_1M, &kvm->arch.gmap->flags);
|
||||
|
||||
table = dereference_asce(kvm->arch.gmap->asce);
|
||||
crst_table_init((void *)table, _CRSTE_HOLE(table->crstes[0].h.tt).val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvm_vm_type_ipa_size_shift(unsigned long type)
|
||||
{
|
||||
int phys_shift;
|
||||
|
||||
phys_shift = KVM_VM_TYPE_ARM_IPA_SIZE(type);
|
||||
if (phys_shift) {
|
||||
if (phys_shift > get_kvm_ipa_limit() ||
|
||||
phys_shift < ARM64_MIN_PARANGE_BITS)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
phys_shift = KVM_PHYS_SHIFT;
|
||||
if (phys_shift > get_kvm_ipa_limit()) {
|
||||
pr_warn_once("%s using unsupported default IPA limit\n",
|
||||
current->comm);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return phys_shift;
|
||||
}
|
||||
|
||||
int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
||||
{
|
||||
char debug_name[32];
|
||||
int ret;
|
||||
|
||||
if (type & ~KVM_VM_TYPE_ARM_IPA_SIZE_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
ret = kvm_vm_type_ipa_size_shift(type);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
kvm->arch.guest_phys_size = 1UL << ret;
|
||||
|
||||
mutex_init(&kvm->arch.config_lock);
|
||||
bitmap_zero(kvm->arch.vcpu_features, KVM_VCPU_MAX_FEATURES);
|
||||
|
||||
snprintf(debug_name, sizeof(debug_name), "kvm-arm64-%u-%u",
|
||||
current->pid, get_random_u32());
|
||||
kvm->arch.dbf = debug_register(debug_name, 32, 1, 7 * sizeof(long));
|
||||
if (!kvm->arch.dbf)
|
||||
return -ENOMEM;
|
||||
debug_register_view(kvm->arch.dbf, &debug_sprintf_view);
|
||||
|
||||
ret = kvm_gmap_init(kvm);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
kvm->arch.mem_limit = kvm->arch.guest_phys_size;
|
||||
|
||||
VM_EVENT(kvm, 3, "vm created with type %lu", type);
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
debug_unregister(kvm->arch.dbf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
vm_fault_t kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
|
||||
{
|
||||
return VM_FAULT_SIGBUS;
|
||||
}
|
||||
|
||||
long kvm_arch_dev_ioctl(struct file *filp,
|
||||
unsigned int ioctl, unsigned long arg)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void kvm_arch_destroy_vm(struct kvm *kvm)
|
||||
{
|
||||
kvm_destroy_vcpus(kvm);
|
||||
debug_unregister(kvm->arch.dbf);
|
||||
kvm->arch.gmap = gmap_put(kvm->arch.gmap);
|
||||
}
|
||||
|
||||
u32 get_kvm_ipa_limit(void)
|
||||
{
|
||||
return fls64(kvm_max_guest_address() + 1) - 1;
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_sae_block *sae_block = &vcpu->arch.sae_block;
|
||||
struct kvm_sae_save_area *save_area = &vcpu->arch.save_area;
|
||||
|
||||
spin_lock_init(&vcpu->arch.mp_state_lock);
|
||||
|
||||
/* Force users to call KVM_ARM_VCPU_INIT */
|
||||
vcpu_clear_flag(vcpu, VCPU_INITIALIZED);
|
||||
|
||||
vcpu->arch.mc = kvm_s390_new_mmu_cache();
|
||||
if (!vcpu->arch.mc)
|
||||
return -ENOMEM;
|
||||
|
||||
save_area->sdo = virt_to_phys(sae_block);
|
||||
|
||||
sae_block->hbasce = vcpu->kvm->arch.gmap->asce.val;
|
||||
sae_block->mso = 0L;
|
||||
sae_block->msl = vcpu->kvm->arch.mem_limit - 1;
|
||||
sae_block->save_area = virt_to_phys(save_area);
|
||||
|
||||
VM_EVENT(vcpu->kvm, 3,
|
||||
"create cpu %d at 0x%p, sae block at 0x%p, satellite at 0x%p",
|
||||
vcpu->vcpu_id, vcpu, &vcpu->arch.sae_block,
|
||||
&vcpu->arch.save_area);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
}
|
||||
|
||||
void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
kvm_s390_free_mmu_cache(vcpu->arch.mc);
|
||||
|
||||
VCPU_EVENT(vcpu, 3, "%s", "free cpu");
|
||||
}
|
||||
|
||||
void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
||||
{
|
||||
save_access_regs(&vcpu->arch.host_acrs[0]);
|
||||
vcpu->cpu = cpu;
|
||||
|
||||
lasrm(&vcpu->arch.save_area);
|
||||
}
|
||||
|
||||
void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
stiasrm(&vcpu->arch.save_area);
|
||||
|
||||
vcpu->cpu = -1;
|
||||
restore_access_regs(&vcpu->arch.host_acrs[0]);
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
|
||||
struct kvm_mp_state *mp_state)
|
||||
{
|
||||
*mp_state = READ_ONCE(vcpu->arch.mp_state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
|
||||
struct kvm_mp_state *mp_state)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long system_supported_vcpu_features(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long features = KVM_S390_ARM64_IMPL_FEATURES;
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu_mode_priv(vcpu);
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
|
||||
if (!kvm_vcpu_initialized(vcpu))
|
||||
return -ENOEXEC;
|
||||
|
||||
if (!kvm_arm_vcpu_is_finalized(vcpu))
|
||||
return -EPERM;
|
||||
|
||||
if (likely(READ_ONCE(vcpu->pid)))
|
||||
return 0;
|
||||
|
||||
scoped_guard(mutex, &kvm->arch.config_lock)
|
||||
set_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* check_vcpu_requests - check and handle pending vCPU requests
|
||||
* @vcpu: the VCPU pointer
|
||||
*
|
||||
* Return: 1 if we should enter the guest
|
||||
* 0 if we should exit to userspace
|
||||
* < 0 if we should exit to userspace, where the return value indicates
|
||||
* an error
|
||||
*/
|
||||
static int check_vcpu_requests(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (kvm_request_pending(vcpu)) {
|
||||
if (kvm_test_request(KVM_REQ_VM_DEAD, vcpu))
|
||||
return -EIO;
|
||||
if (kvm_check_request(KVM_REQ_VCPU_RESET, vcpu))
|
||||
kvm_reset_vcpu(vcpu);
|
||||
/*
|
||||
* Clear IRQ_PENDING requests that were made to guarantee
|
||||
* that a VCPU sees new virtual interrupts.
|
||||
*/
|
||||
kvm_check_request(KVM_REQ_IRQ_PENDING, vcpu);
|
||||
|
||||
if (kvm_check_request(KVM_REQ_TLB_FLUSH, vcpu))
|
||||
asce_flush_tlb(vcpu->kvm->arch.gmap->asce);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int kvm_vcpu_initialize(struct kvm_vcpu *vcpu,
|
||||
const struct kvm_vcpu_init *init)
|
||||
{
|
||||
unsigned long features = init->features[0];
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
|
||||
guard(mutex)(&kvm->arch.config_lock);
|
||||
if (test_bit(KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED, &kvm->arch.flags) &&
|
||||
kvm_vcpu_init_changed(vcpu, init))
|
||||
return -EINVAL;
|
||||
|
||||
bitmap_copy(kvm->arch.vcpu_features, &features, KVM_VCPU_MAX_FEATURES);
|
||||
|
||||
kvm_reset_vcpu(vcpu);
|
||||
|
||||
set_bit(KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED, &kvm->arch.flags);
|
||||
vcpu_set_flag(vcpu, VCPU_INITIALIZED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvm_vcpu_set_target(struct kvm_vcpu *vcpu,
|
||||
const struct kvm_vcpu_init *init)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (init->target != KVM_ARM_TARGET_GENERIC_V8)
|
||||
return -EINVAL;
|
||||
|
||||
ret = kvm_vcpu_init_check_features(vcpu, init);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!kvm_vcpu_initialized(vcpu))
|
||||
return kvm_vcpu_initialize(vcpu, init);
|
||||
|
||||
if (kvm_vcpu_init_changed(vcpu, init))
|
||||
return -EINVAL;
|
||||
|
||||
kvm_reset_vcpu(vcpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
|
||||
struct kvm_vcpu_init *init)
|
||||
{
|
||||
struct kvm_sae_save_area *save_area = &vcpu->arch.save_area;
|
||||
struct kvm_sae_block *sae_block = &vcpu->arch.sae_block;
|
||||
int ret;
|
||||
|
||||
sae_block->save_area = virt_to_phys(save_area);
|
||||
save_area->sdo = virt_to_phys(sae_block);
|
||||
|
||||
ret = kvm_vcpu_set_target(vcpu, init);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scoped_guard(spinlock, &vcpu->arch.mp_state_lock) {
|
||||
WRITE_ONCE(vcpu->arch.mp_state.mp_state, KVM_MP_STATE_RUNNABLE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
|
||||
bool line_status)
|
||||
{
|
||||
/* stub for now*/
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void adjust_pc(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (vcpu_get_flag(vcpu, INCREMENT_PC)) {
|
||||
kvm_skip_instr(vcpu);
|
||||
vcpu_clear_flag(vcpu, INCREMENT_PC);
|
||||
}
|
||||
}
|
||||
|
||||
static noinstr void arm_vcpu_enter_exit(phys_addr_t sae_block)
|
||||
{
|
||||
guest_timing_enter_irqoff();
|
||||
guest_state_enter_irqoff();
|
||||
|
||||
/*
|
||||
* External interrupts may occur while the SAE instruction is executing
|
||||
* and must preempt guest execution to maintain responsiveness of the
|
||||
* host.
|
||||
*
|
||||
* As a temporary workaround, run SAE with interrupts enabled so that any
|
||||
* interrupt is taken via the normal host interrupt path rather than
|
||||
* being subsumed inside the SAE window.
|
||||
*/
|
||||
raw_local_irq_enable();
|
||||
sae(sae_block);
|
||||
raw_local_irq_disable();
|
||||
|
||||
guest_state_exit_irqoff();
|
||||
guest_timing_exit_irqoff();
|
||||
}
|
||||
|
||||
/** kvm_arch_vcpu_ioctl_run() - run arm64 vCPU
|
||||
*
|
||||
* Execute arm64 guest instructions using SAE.
|
||||
*
|
||||
* Returns:
|
||||
* 1 enter the guest (should not be observed by userspace)
|
||||
* 0 exit to userspace
|
||||
* < 0 exit to userspace, where the return value indicates an error
|
||||
*
|
||||
*
|
||||
*/
|
||||
int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
DECLARE_KERNEL_FPU_ONSTACK32(fpu_save);
|
||||
struct kvm_run *kvm_run = vcpu->run;
|
||||
int ret;
|
||||
|
||||
if (kvm_run->exit_reason == KVM_EXIT_MMIO) {
|
||||
ret = kvm_handle_mmio_return(vcpu);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
vcpu_load(vcpu);
|
||||
|
||||
kernel_fpu_begin(&fpu_save, KERNEL_FPC | KERNEL_VXR);
|
||||
load_vx_regs((vcpu->arch.ctxt.vregs));
|
||||
|
||||
if (!vcpu->wants_to_run) {
|
||||
ret = -EINTR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
kvm_sigset_activate(vcpu);
|
||||
|
||||
might_fault();
|
||||
|
||||
ret = 1;
|
||||
kvm_run->exit_reason = KVM_EXIT_UNKNOWN;
|
||||
while (ret > 0) {
|
||||
ret = kvm_xfer_to_guest_mode_handle_work(vcpu);
|
||||
if (!ret)
|
||||
ret = 1;
|
||||
|
||||
if (ret > 0)
|
||||
ret = check_vcpu_requests(vcpu);
|
||||
|
||||
vcpu->arch.sae_block.icptr = 0;
|
||||
|
||||
local_irq_disable();
|
||||
xfer_to_guest_mode_prepare();
|
||||
/*
|
||||
* The full memory barrier in smp_store_mb() orders the mode
|
||||
* store against the kvm_request_pending() check below, so
|
||||
* any pending kick is visible before we decide to enter the
|
||||
* guest.
|
||||
*/
|
||||
smp_store_mb(vcpu->mode, IN_GUEST_MODE);
|
||||
|
||||
if (ret <= 0 || kvm_request_pending(vcpu) ||
|
||||
xfer_to_guest_mode_work_pending()) {
|
||||
vcpu->mode = OUTSIDE_GUEST_MODE;
|
||||
local_irq_enable();
|
||||
continue;
|
||||
}
|
||||
|
||||
trace_kvm_entry(*vcpu_pc(vcpu));
|
||||
|
||||
adjust_pc(vcpu);
|
||||
|
||||
arm_vcpu_enter_exit(virt_to_phys(&vcpu->arch.sae_block));
|
||||
|
||||
vcpu->mode = OUTSIDE_GUEST_MODE;
|
||||
vcpu->stat.exits++;
|
||||
|
||||
local_irq_enable();
|
||||
trace_kvm_exit(kvm_vcpu_trap_get_class(vcpu), *vcpu_pc(vcpu));
|
||||
|
||||
ret = handle_exit(vcpu);
|
||||
}
|
||||
|
||||
kvm_sigset_deactivate(vcpu);
|
||||
out:
|
||||
if (unlikely(vcpu_get_flag(vcpu, INCREMENT_PC)))
|
||||
adjust_pc(vcpu);
|
||||
|
||||
save_vx_regs(vcpu->arch.ctxt.vregs);
|
||||
kernel_fpu_end(&fpu_save, KERNEL_FPC | KERNEL_VXR);
|
||||
vcpu_put(vcpu);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kvm_arm_vcpu_set_attr(struct kvm_vcpu *vcpu,
|
||||
struct kvm_device_attr *attr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (attr->group) {
|
||||
default:
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kvm_arm_vcpu_get_attr(struct kvm_vcpu *vcpu,
|
||||
struct kvm_device_attr *attr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (attr->group) {
|
||||
default:
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kvm_arm_vcpu_has_attr(struct kvm_vcpu *vcpu,
|
||||
struct kvm_device_attr *attr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (attr->group) {
|
||||
default:
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = filp->private_data;
|
||||
void __user *argp = (void __user *)arg;
|
||||
struct kvm_device_attr attr;
|
||||
int ret;
|
||||
|
||||
switch (ioctl) {
|
||||
case KVM_ARM_VCPU_INIT: {
|
||||
struct kvm_vcpu_init init;
|
||||
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(&init, argp, sizeof(init)))
|
||||
break;
|
||||
|
||||
ret = kvm_arch_vcpu_ioctl_vcpu_init(vcpu, &init);
|
||||
break;
|
||||
}
|
||||
case KVM_SET_ONE_REG:
|
||||
case KVM_GET_ONE_REG: {
|
||||
struct kvm_one_reg reg;
|
||||
|
||||
ret = -ENOEXEC;
|
||||
if (unlikely(!kvm_vcpu_initialized(vcpu)))
|
||||
break;
|
||||
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(®, argp, sizeof(reg)))
|
||||
break;
|
||||
|
||||
if (kvm_check_request(KVM_REQ_VCPU_RESET, vcpu))
|
||||
kvm_reset_vcpu(vcpu);
|
||||
|
||||
if (ioctl == KVM_SET_ONE_REG)
|
||||
ret = kvm_arm_set_reg(vcpu, ®);
|
||||
else
|
||||
ret = kvm_arm_get_reg(vcpu, ®);
|
||||
break;
|
||||
}
|
||||
case KVM_GET_REG_LIST: {
|
||||
struct kvm_reg_list __user *user_list = argp;
|
||||
struct kvm_reg_list reg_list;
|
||||
unsigned int n;
|
||||
|
||||
ret = -ENOEXEC;
|
||||
if (unlikely(!kvm_vcpu_initialized(vcpu)))
|
||||
break;
|
||||
ret = -EPERM;
|
||||
if (!kvm_arm_vcpu_is_finalized(vcpu))
|
||||
break;
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(®_list, user_list, sizeof(reg_list)))
|
||||
break;
|
||||
n = reg_list.n;
|
||||
reg_list.n = kvm_arm_num_regs(vcpu);
|
||||
if (copy_to_user(user_list, ®_list, sizeof(reg_list)))
|
||||
break;
|
||||
ret = -E2BIG;
|
||||
if (n < reg_list.n)
|
||||
break;
|
||||
ret = kvm_arm_copy_reg_indices(vcpu, user_list->reg);
|
||||
break;
|
||||
}
|
||||
case KVM_ARM_VCPU_FINALIZE: {
|
||||
int what;
|
||||
|
||||
if (!kvm_vcpu_initialized(vcpu))
|
||||
return -ENOEXEC;
|
||||
|
||||
if (get_user(what, (const int __user *)argp))
|
||||
return -EFAULT;
|
||||
|
||||
ret = kvm_arm_vcpu_finalize(vcpu, what);
|
||||
break;
|
||||
}
|
||||
case KVM_SET_DEVICE_ATTR: {
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(&attr, argp, sizeof(attr)))
|
||||
break;
|
||||
ret = kvm_arm_vcpu_set_attr(vcpu, &attr);
|
||||
break;
|
||||
}
|
||||
case KVM_GET_DEVICE_ATTR: {
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(&attr, argp, sizeof(attr)))
|
||||
break;
|
||||
ret = kvm_arm_vcpu_get_attr(vcpu, &attr);
|
||||
break;
|
||||
}
|
||||
case KVM_HAS_DEVICE_ATTR: {
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(&attr, argp, sizeof(attr)))
|
||||
break;
|
||||
ret = kvm_arm_vcpu_has_attr(vcpu, &attr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
|
||||
struct kvm_dirty_log *log)
|
||||
{
|
||||
return s390_kvm_mmu_get_dirty_log(kvm, log);
|
||||
}
|
||||
|
||||
bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
|
||||
{
|
||||
scoped_guard(read_lock, &kvm->mmu_lock)
|
||||
return gmap_age_gfn(kvm->arch.gmap, range->start, range->end);
|
||||
}
|
||||
|
||||
void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
|
||||
{
|
||||
gfn_t last_gfn = memslot->base_gfn + memslot->npages;
|
||||
|
||||
scoped_guard(read_lock, &kvm->mmu_lock)
|
||||
gmap_sync_dirty_log(kvm->arch.gmap, memslot->base_gfn, last_gfn);
|
||||
}
|
||||
|
||||
int kvm_arch_prepare_memory_region(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *old,
|
||||
struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change)
|
||||
{
|
||||
return s390_kvm_mmu_prepare_memory_region(kvm, old, new, change);
|
||||
}
|
||||
|
||||
void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_memory_slot *old,
|
||||
const struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change)
|
||||
{
|
||||
}
|
||||
|
||||
bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
|
||||
{
|
||||
return gmap_unmap_gfn_range(kvm->arch.gmap, range->slot, range->start, range->end);
|
||||
}
|
||||
|
||||
bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
|
||||
{
|
||||
scoped_guard(read_lock, &kvm->mmu_lock)
|
||||
return dat_test_age_gfn(kvm->arch.gmap->asce, range->start, range->end);
|
||||
}
|
||||
|
||||
int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
|
||||
switch (ioctl) {
|
||||
case KVM_ARM_PREFERRED_TARGET: {
|
||||
struct kvm_vcpu_init init = {
|
||||
.target = KVM_ARM_TARGET_GENERIC_V8,
|
||||
};
|
||||
|
||||
if (copy_to_user(argp, &init, sizeof(init)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
bool kvm_arch_irqchip_in_kernel(struct kvm *kvm)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm,
|
||||
int irq_source_id, int level, bool line_status)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int kvm_set_routing_entry(struct kvm *kvm,
|
||||
struct kvm_kernel_irq_routing_entry *e,
|
||||
const struct kvm_irq_routing_entry *ue)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
long kvm_arch_vcpu_unlocked_ioctl(struct file *filp, unsigned int ioctl,
|
||||
unsigned long arg)
|
||||
{
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
static int __init kvm_s390_arm64_init(void)
|
||||
{
|
||||
if (!sclp.has_aef)
|
||||
return -ENODEV;
|
||||
|
||||
return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE);
|
||||
}
|
||||
|
||||
static __exit void kvm_s390_arm64_exit(void)
|
||||
{
|
||||
kvm_exit();
|
||||
}
|
||||
|
||||
module_init(kvm_s390_arm64_init);
|
||||
module_exit(kvm_s390_arm64_exit);
|
||||
58
arch/s390/kvm/arm64/arm.h
Normal file
58
arch/s390/kvm/arm64/arm.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef ARCH_S390_KVM_ARM64_H
|
||||
#define ARCH_S390_KVM_ARM64_H
|
||||
|
||||
#include <linux/compiler_attributes.h>
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#define VM_EVENT(d_kvm, d_loglevel, d_string, d_args...) \
|
||||
debug_sprintf_event((d_kvm)->arch.dbf, d_loglevel, \
|
||||
__stringify(KVM_DEV_NAME) ": " d_string "\n", \
|
||||
d_args)
|
||||
|
||||
#define VCPU_EVENT(d_vcpu, d_loglevel, d_string, d_args...) \
|
||||
debug_sprintf_event((d_vcpu)->kvm->arch.dbf, d_loglevel, \
|
||||
__stringify(KVM_DEV_NAME) " %02d[%016llx-%016llx]: " d_string "\n", \
|
||||
(d_vcpu)->vcpu_id, (d_vcpu)->arch.sae_block.pstate, \
|
||||
(d_vcpu)->arch.sae_block.pc, d_args)
|
||||
|
||||
static __always_inline int kvm_is_ucontrol(struct kvm *kvm)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __always_inline int __kvm_s390_pv_destroy_page(struct page *page)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __always_inline void kvm_s390_vsie_gmap_notifier(struct gmap *gmap, gpa_t start, gpa_t end)
|
||||
{
|
||||
}
|
||||
|
||||
static __always_inline int kvm_s390_pv_get_handle(struct kvm *kvm)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __always_inline int kvm_s390_is_migration_mode(struct kvm *kvm)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static __always_inline bool kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static __always_inline void kvm_s390_update_cmma_dirty(struct kvm *kvm, const struct kvm_memory_slot *old)
|
||||
{
|
||||
}
|
||||
|
||||
/* should never be called */
|
||||
static __always_inline int kvm_s390_vm_stop_migration(struct kvm *kvm)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#endif /* ARCH_S390_KVM_ARM64_H */
|
||||
82
arch/s390/kvm/arm64/copy-arm64c.awk
Normal file
82
arch/s390/kvm/arm64/copy-arm64c.awk
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#!/usr/bin/awk -f
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Extract marked sections from ARM64 C files for sharing with s390 KVM
|
||||
#
|
||||
# Usage: copy-arm64c.awk <input_file>
|
||||
#
|
||||
# Extracts all sections between start/end markers. If no markers found, signals failure.
|
||||
|
||||
BEGIN {
|
||||
# Constants
|
||||
start_pattern = "^#if ARM64_S390_COMMON == 1$"
|
||||
end_pattern = "^#endif /\\* ARM64_S390_COMMON == 1 \\*/$"
|
||||
|
||||
# State variables
|
||||
copying = found_marker = 0
|
||||
file_header_done = 0
|
||||
depth = 0
|
||||
}
|
||||
|
||||
!file_header_done {
|
||||
if (/^\/\*/ || /^\/\/ SPDX-License-Identifier:/) {
|
||||
print
|
||||
next
|
||||
}
|
||||
if (/[[:space:]]\*([[:space:]]|$)/) {
|
||||
print
|
||||
next
|
||||
}
|
||||
if (/\*\//) {
|
||||
print " *"
|
||||
} else {
|
||||
print "/*"
|
||||
}
|
||||
|
||||
print " * This file was automatically generated from " FILENAME
|
||||
print " * Do not modify this file directly."
|
||||
print " */"
|
||||
print ""
|
||||
print "#ifndef __INCL_GEN_ARM_FILE"
|
||||
print "#error do not include this file directly."
|
||||
print "#endif /* __INCL_GEN_ARM_FILE */"
|
||||
print "#undef __INCL_GEN_ARM_FILE"
|
||||
print ""
|
||||
|
||||
file_header_done = 1
|
||||
}
|
||||
|
||||
$0 ~ start_pattern {
|
||||
if (copying) { error = 1; exit }
|
||||
depth = copying = found_marker = 1
|
||||
# Map subsequent lines back to the original ARM64 source file so that
|
||||
# compiler diagnostics point there rather than at the generated copy.
|
||||
print "#line " (NR + 1) " \"" FILENAME "\""
|
||||
next
|
||||
}
|
||||
|
||||
copying && /^[ \t]*#[ \t]*if(def|ndef)?([ \t]|$)/ { depth++ }
|
||||
|
||||
$0 ~ /^[ \t]*#[ \t]*endif/ {
|
||||
depth--
|
||||
if (depth == 0) {
|
||||
if ($0 ~ end_pattern) {
|
||||
copying = 0
|
||||
next
|
||||
}
|
||||
error = 1; exit
|
||||
}
|
||||
}
|
||||
|
||||
/ARM64_S390_COMMON/ { error = 1; exit }
|
||||
|
||||
copying {
|
||||
gsub(/#include <asm\//, "#include <arm64/")
|
||||
gsub(/#include <uapi\/asm\//, "#include <uapi/arm64/")
|
||||
print
|
||||
next
|
||||
}
|
||||
|
||||
END {
|
||||
exit error || !found_marker || copying
|
||||
}
|
||||
135
arch/s390/kvm/arm64/guest.c
Normal file
135
arch/s390/kvm/arm64/guest.c
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/kvm.h>
|
||||
|
||||
#include <arm64/kvm_emulate.h>
|
||||
#include <arm64/kvm_nested.h>
|
||||
|
||||
#define __INCL_GEN_ARM_FILE
|
||||
#include "generated/guest.inc"
|
||||
#undef __INCL_GEN_ARM_FILE
|
||||
|
||||
const struct kvm_stats_desc kvm_vm_stats_desc[] = {
|
||||
KVM_GENERIC_VM_STATS()
|
||||
};
|
||||
|
||||
const struct kvm_stats_header kvm_vm_stats_header = {
|
||||
.name_size = KVM_STATS_NAME_SIZE,
|
||||
.num_desc = ARRAY_SIZE(kvm_vm_stats_desc),
|
||||
.id_offset = sizeof(struct kvm_stats_header),
|
||||
.desc_offset = sizeof(struct kvm_stats_header) + KVM_STATS_NAME_SIZE,
|
||||
.data_offset = sizeof(struct kvm_stats_header) + KVM_STATS_NAME_SIZE +
|
||||
sizeof(kvm_vm_stats_desc),
|
||||
};
|
||||
|
||||
const struct kvm_stats_desc kvm_vcpu_stats_desc[] = {
|
||||
KVM_GENERIC_VCPU_STATS(),
|
||||
/* ARM64 stats */
|
||||
STATS_DESC_COUNTER(VCPU, hvc_exit_stat),
|
||||
STATS_DESC_COUNTER(VCPU, wfe_exit_stat),
|
||||
STATS_DESC_COUNTER(VCPU, wfi_exit_stat),
|
||||
STATS_DESC_COUNTER(VCPU, mmio_exit_user),
|
||||
STATS_DESC_COUNTER(VCPU, mmio_exit_kernel),
|
||||
STATS_DESC_COUNTER(VCPU, signal_exits),
|
||||
STATS_DESC_COUNTER(VCPU, exits),
|
||||
/* GMAP stats */
|
||||
STATS_DESC_COUNTER(VCPU, pfault_sync),
|
||||
};
|
||||
|
||||
const struct kvm_stats_header kvm_vcpu_stats_header = {
|
||||
.name_size = KVM_STATS_NAME_SIZE,
|
||||
.num_desc = ARRAY_SIZE(kvm_vcpu_stats_desc),
|
||||
.id_offset = sizeof(struct kvm_stats_header),
|
||||
.desc_offset = sizeof(struct kvm_stats_header) + KVM_STATS_NAME_SIZE,
|
||||
.data_offset = sizeof(struct kvm_stats_header) + KVM_STATS_NAME_SIZE +
|
||||
sizeof(kvm_vcpu_stats_desc),
|
||||
};
|
||||
|
||||
int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = copy_core_reg_indices(vcpu, uindices);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
uindices += ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return num_core_regs(vcpu);
|
||||
}
|
||||
|
||||
int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||
{
|
||||
/* We currently use nothing arch-specific in upper 32 bits */
|
||||
if ((reg->id & ~KVM_REG_SIZE_MASK) >> 32 != KVM_REG_ARM64 >> 32)
|
||||
return -EINVAL;
|
||||
|
||||
switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
|
||||
case KVM_REG_ARM_CORE:
|
||||
return get_core_reg(vcpu, reg);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||
{
|
||||
/* We currently use nothing arch-specific in upper 32 bits */
|
||||
if ((reg->id & ~KVM_REG_SIZE_MASK) >> 32 != KVM_REG_ARM64 >> 32)
|
||||
return -EINVAL;
|
||||
|
||||
switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
|
||||
case KVM_REG_ARM_CORE:
|
||||
return set_core_reg(vcpu, reg);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
|
||||
struct kvm_sregs *sregs)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
|
||||
struct kvm_sregs *sregs)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
|
||||
struct kvm_translation *tr)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
|
||||
struct kvm_guest_debug *dbg)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
161
arch/s390/kvm/arm64/handle_exit.c
Normal file
161
arch/s390/kvm/arm64/handle_exit.c
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include <arm64/esr.h>
|
||||
#include <arm64/kvm_emulate.h>
|
||||
|
||||
#include "handle_exit.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,
|
||||
[ESR_ELx_EC_IABT_LOW] = kvm_handle_guest_abort,
|
||||
[ESR_ELx_EC_DABT_LOW] = kvm_handle_guest_abort,
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on
|
||||
* proper exit to userspace.
|
||||
*/
|
||||
int handle_exit(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u8 icptr = vcpu->arch.sae_block.icptr;
|
||||
int ret = 1;
|
||||
|
||||
switch (icptr) {
|
||||
case SAE_ICPTR_SPURIOUS:
|
||||
break;
|
||||
case SAE_ICPTR_VALIDITY:
|
||||
WARN_ONCE(true, "SAE: validity intercept. vir: 0x%04x",
|
||||
vcpu->arch.sae_block.vir);
|
||||
vcpu->run->exit_reason = KVM_EXIT_FAIL_ENTRY;
|
||||
ret = 0;
|
||||
break;
|
||||
case SAE_ICPTR_GUEST_ADDRESS_SIZE:
|
||||
case SAE_ICPTR_HOST_ACCESS_EXCEPTION:
|
||||
case SAE_ICPTR_SYNCHRONOUS_EXCEPTION:
|
||||
ret = handle_trap_exceptions(vcpu);
|
||||
break;
|
||||
default:
|
||||
vcpu->run->exit_reason = KVM_EXIT_FAIL_ENTRY;
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 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)];
|
||||
}
|
||||
9
arch/s390/kvm/arm64/handle_exit.h
Normal file
9
arch/s390/kvm/arm64/handle_exit.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#ifndef KVM_ARM64_HANDLE_EXIT_H
|
||||
#define KVM_ARM64_HANDLE_EXIT_H
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
int handle_exit(struct kvm_vcpu *vcpu);
|
||||
|
||||
#endif /* KVM_ARM64_HANDLE_EXIT_H */
|
||||
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;
|
||||
}
|
||||
15
arch/s390/kvm/arm64/mmio.c
Normal file
15
arch/s390/kvm/arm64/mmio.c
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#include <trace/events/kvm.h>
|
||||
|
||||
#include <arm64/kvm_emulate.h>
|
||||
#include <arm64/sysreg.h>
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
#define __INCL_GEN_ARM_FILE
|
||||
#include "generated/mmio.inc"
|
||||
#undef __INCL_GEN_ARM_FILE
|
||||
210
arch/s390/kvm/arm64/mmu.c
Normal file
210
arch/s390/kvm/arm64/mmu.c
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include <arm64/kvm_emulate.h>
|
||||
|
||||
#include <faultin.h>
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
static inline bool kvm_s390_cur_gmap_fault_is_write(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return (vcpu->arch.sae_block.hai.pic & PGM_INT_CODE_MASK) == PGM_PROTECTION ||
|
||||
vcpu->arch.sae_block.hai.teid.fsi == TEID_FSI_STORE;
|
||||
}
|
||||
|
||||
/*
|
||||
* user_mem_abort() - handle a dat fault for the gmap of a vcpu
|
||||
*
|
||||
* Return: 0 on success, < 0 in case of error.
|
||||
* Context: The mm lock must not be held before calling. May sleep.
|
||||
*/
|
||||
static int user_mem_abort(struct kvm_vcpu *vcpu, gpa_t fault_ipa,
|
||||
struct kvm_memory_slot *slot, hva_t hva)
|
||||
{
|
||||
struct guest_fault f = { };
|
||||
int ret;
|
||||
|
||||
if (kvm_s390_cur_gmap_fault_is_write(vcpu))
|
||||
f.write_attempt = FOLL_WRITE;
|
||||
f.gfn = gpa_to_gfn(fault_ipa);
|
||||
|
||||
ret = kvm_s390_faultin_gfn(vcpu, NULL, &f);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
if (ret == PGM_ADDRESSING)
|
||||
/*
|
||||
* Without the relevant sysregs we cannot do anything for now.
|
||||
* Go back to userspace with an error. TODO sysreg handling
|
||||
*/
|
||||
return -ENOEXEC;
|
||||
KVM_BUG_ON(ret, vcpu->kvm);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int kvm_handle_pic(struct kvm_vcpu *vcpu, bool *translation)
|
||||
{
|
||||
switch (kvm_vcpu_fault_pic(vcpu)) {
|
||||
/* expected cases: */
|
||||
case PGM_ASCE_TYPE:
|
||||
case PGM_REGION_FIRST_TRANS:
|
||||
case PGM_REGION_SECOND_TRANS:
|
||||
case PGM_REGION_THIRD_TRANS:
|
||||
case PGM_SEGMENT_TRANSLATION:
|
||||
case PGM_PAGE_TRANSLATION:
|
||||
*translation = true;
|
||||
break;
|
||||
case PGM_PROTECTION:
|
||||
break;
|
||||
/* unexpected cases: */
|
||||
case 0:
|
||||
KVM_BUG(1, vcpu->kvm, "On MMU fault path but no fault occurred");
|
||||
return -EFAULT;
|
||||
default:
|
||||
KVM_BUG(1, vcpu->kvm, "Unexpected program interrupt 0x%x, TEID 0x%016lx",
|
||||
vcpu->arch.sae_block.hai.pic, vcpu->arch.sae_block.hai.teid.val);
|
||||
send_sig(SIGSEGV, current, 0);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_memory_slot *memslot;
|
||||
bool translation = false;
|
||||
phys_addr_t fault_ipa;
|
||||
unsigned long hva;
|
||||
unsigned long esr;
|
||||
bool write_fault;
|
||||
bool guest_size_err;
|
||||
bool writable;
|
||||
bool is_iabt;
|
||||
int ret;
|
||||
gfn_t gfn;
|
||||
|
||||
esr = kvm_vcpu_get_esr(vcpu);
|
||||
fault_ipa = kvm_vcpu_get_fault_ipa(vcpu);
|
||||
is_iabt = kvm_vcpu_trap_is_iabt(vcpu);
|
||||
guest_size_err = vcpu->arch.sae_block.icptr == SAE_ICPTR_GUEST_ADDRESS_SIZE;
|
||||
|
||||
if (guest_size_err) {
|
||||
translation = true;
|
||||
} else {
|
||||
ret = kvm_handle_pic(vcpu, &translation);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (translation) {
|
||||
/*
|
||||
* For both cases:
|
||||
* Without the relevant sysregs we cannot do anything for now.
|
||||
* Go back to userspace with an error. TODO sysreg handling
|
||||
*/
|
||||
if (fault_ipa >= BIT_ULL(get_kvm_ipa_limit()))
|
||||
return -ENOEXEC;
|
||||
|
||||
if (fault_ipa >= vcpu->kvm->arch.guest_phys_size)
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
trace_kvm_guest_fault(*vcpu_pc(vcpu), kvm_vcpu_get_esr(vcpu),
|
||||
kvm_vcpu_get_hfar(vcpu), fault_ipa);
|
||||
|
||||
kvm_vcpu_srcu_read_lock(vcpu);
|
||||
|
||||
gfn = fault_ipa >> PAGE_SHIFT;
|
||||
|
||||
memslot = gfn_to_memslot(vcpu->kvm, gfn);
|
||||
hva = gfn_to_hva_memslot_prot(memslot, gfn, &writable);
|
||||
write_fault = kvm_s390_cur_gmap_fault_is_write(vcpu);
|
||||
if (kvm_is_error_hva(hva) || (write_fault && !writable)) {
|
||||
ret = -ENOEXEC;
|
||||
/*
|
||||
* The guest has put either its instructions or its page-tables
|
||||
* somewhere it shouldn't have. Userspace won't be able to do
|
||||
* anything about this (there's no syndrome for a start).
|
||||
*
|
||||
* Without the relevant sysregs we cannot do anything for now.
|
||||
* Go back to userspace with an error. TODO sysreg handling
|
||||
*/
|
||||
if (is_iabt)
|
||||
goto out_unlock;
|
||||
|
||||
if (kvm_vcpu_abt_iss1tw(vcpu)) {
|
||||
/*
|
||||
* Without the relevant sysregs we cannot do anything for now.
|
||||
* Go back to userspace with an error. TODO sysreg handling
|
||||
*/
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for a cache maintenance operation. Assume the guest is
|
||||
* cautious and skip instruction
|
||||
*/
|
||||
if (kvm_is_error_hva(hva) && esr_dabt_is_cm(esr)) {
|
||||
kvm_incr_pc(vcpu);
|
||||
ret = 1;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* The IPA is reported as [MAX:12], so we need to
|
||||
* complement it with the bottom 12 bits from the
|
||||
* faulting VA. This is always 12 bits, irrespective
|
||||
* of the page size.
|
||||
*/
|
||||
fault_ipa |= kvm_vcpu_get_hfar(vcpu) & ((1 << 12) - 1);
|
||||
ret = io_mem_abort(vcpu, fault_ipa);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* Userspace should not be able to register out-of-bounds IPAs */
|
||||
VM_BUG_ON(fault_ipa >= vcpu->kvm->arch.guest_phys_size);
|
||||
/*
|
||||
* Proper guest size faults have been injected.
|
||||
* In theory it's fine to have device memory higher than MSL,
|
||||
* even if not currently possible, but that would have been handled above.
|
||||
* So if we get here with a guest size intercept, we have a bug somewhere.
|
||||
*/
|
||||
VM_BUG_ON(guest_size_err);
|
||||
|
||||
ret = user_mem_abort(vcpu, fault_ipa, memslot, hva);
|
||||
if (!ret)
|
||||
ret = 1;
|
||||
out_unlock:
|
||||
kvm_vcpu_srcu_read_unlock(vcpu);
|
||||
return ret;
|
||||
}
|
||||
|
||||
long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu, struct kvm_pre_fault_memory *range)
|
||||
{
|
||||
struct guest_fault f = { .gfn = gpa_to_gfn(range->gpa), };
|
||||
gpa_t end;
|
||||
int rc;
|
||||
|
||||
rc = kvm_s390_faultin_gfn(vcpu, NULL, &f);
|
||||
if (rc == PGM_ADDRESSING)
|
||||
return -ENOENT;
|
||||
if (rc > 0)
|
||||
return -EIO;
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (f.ptep)
|
||||
return PAGE_SIZE;
|
||||
|
||||
end = ALIGN(range->gpa + PAGE_SIZE, f.crste_region3 ? _REGION3_SIZE : HPAGE_SIZE);
|
||||
return min(range->size, end - range->gpa);
|
||||
}
|
||||
|
||||
int kvm_arch_pre_fault_allowed(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (!kvm_vcpu_initialized(vcpu))
|
||||
return -ENOEXEC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
71
arch/s390/kvm/arm64/reset.c
Normal file
71
arch/s390/kvm/arm64/reset.c
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/fpu.h>
|
||||
|
||||
#include <arm64/kvm_emulate.h>
|
||||
#include <arm64/kvm_nested.h>
|
||||
|
||||
bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void vcpu_reset_hcr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu->arch.hcr_el2 = HCR_EL2_E2H | HCR_EL2_RW | HCR_EL2_AMO |
|
||||
HCR_EL2_IMO | HCR_EL2_FMO | HCR_EL2_PTW;
|
||||
/* traps */
|
||||
vcpu->arch.hcr_el2 |= HCR_EL2_TSC | HCR_EL2_TID1 | HCR_EL2_TID2 |
|
||||
HCR_EL2_TID3 | HCR_EL2_TID4 | HCR_EL2_TID5 |
|
||||
HCR_EL2_TIDCP | HCR_EL2_TLOR;
|
||||
}
|
||||
|
||||
void kvm_reset_vcpu(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vcpu_reset_state reset_state;
|
||||
bool loaded;
|
||||
|
||||
scoped_guard(spinlock, &vcpu->arch.mp_state_lock) {
|
||||
reset_state = vcpu->arch.reset_state;
|
||||
vcpu->arch.reset_state.reset = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable preemption around the vcpu reset as we might otherwise race with
|
||||
* preempt notifiers which call stiasrm/lasrm from put/load
|
||||
*/
|
||||
preempt_disable();
|
||||
|
||||
/* The reset must run with an unloaded save area */
|
||||
loaded = vcpu_is_loaded(vcpu);
|
||||
if (loaded)
|
||||
vcpu_put(vcpu);
|
||||
|
||||
kvm_reset_vcpu_core(vcpu);
|
||||
|
||||
/* Reset special registers */
|
||||
vcpu_reset_hcr(vcpu);
|
||||
|
||||
if (reset_state.reset) {
|
||||
*vcpu_pc(vcpu) = reset_state.pc;
|
||||
vcpu_clear_flag(vcpu, PENDING_EXCEPTION);
|
||||
vcpu_clear_flag(vcpu, EXCEPT_MASK);
|
||||
vcpu_clear_flag(vcpu, INCREMENT_PC);
|
||||
vcpu_set_reg(vcpu, 0, reset_state.r0);
|
||||
}
|
||||
|
||||
/* Load new vx-regs into HW if they are currently loaded */
|
||||
if (current->thread.kfpu_flags)
|
||||
load_vx_regs(vcpu->arch.ctxt.vregs);
|
||||
|
||||
if (loaded)
|
||||
vcpu_load(vcpu);
|
||||
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
int kvm_arm_vcpu_finalize(struct kvm_vcpu *vcpu, int feature)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
101
arch/s390/kvm/arm64/trace.h
Normal file
101
arch/s390/kvm/arm64/trace.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#if !defined(KVM_ARM64_TRACE_KVM_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define KVM_ARM64_TRACE_KVM_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM kvm_arm64
|
||||
#undef TRACE_SYSTEM_VAR
|
||||
#define TRACE_SYSTEM_VAR kvm_arm64
|
||||
|
||||
TRACE_EVENT(kvm_entry,
|
||||
TP_PROTO(unsigned long vcpu_pc),
|
||||
TP_ARGS(vcpu_pc),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned long, vcpu_pc)
|
||||
),
|
||||
|
||||
TP_fast_assign(__entry->vcpu_pc = vcpu_pc;),
|
||||
|
||||
TP_printk("PC: 0x%016lx", __entry->vcpu_pc)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_exit,
|
||||
TP_PROTO(unsigned int esr_ec, unsigned long vcpu_pc),
|
||||
TP_ARGS(esr_ec, vcpu_pc),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, esr_ec )
|
||||
__field(unsigned long, vcpu_pc )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->esr_ec = esr_ec;
|
||||
__entry->vcpu_pc = vcpu_pc;
|
||||
),
|
||||
|
||||
TP_printk("HSR_EC: 0x%04x, PC: 0x%016lx",
|
||||
__entry->esr_ec,
|
||||
__entry->vcpu_pc)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_mmio_nisv,
|
||||
TP_PROTO(unsigned long vcpu_pc, unsigned long esr,
|
||||
unsigned long far, unsigned long ipa),
|
||||
TP_ARGS(vcpu_pc, esr, far, ipa),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, vcpu_pc )
|
||||
__field( unsigned long, esr )
|
||||
__field( unsigned long, far )
|
||||
__field( unsigned long, ipa )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu_pc = vcpu_pc;
|
||||
__entry->esr = esr;
|
||||
__entry->far = far;
|
||||
__entry->ipa = ipa;
|
||||
),
|
||||
|
||||
TP_printk("ipa %#016lx, esr %#016lx, far %#016lx, pc %#016lx",
|
||||
__entry->ipa, __entry->esr,
|
||||
__entry->far, __entry->vcpu_pc)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_guest_fault,
|
||||
TP_PROTO(unsigned long vcpu_pc, unsigned long hsr,
|
||||
unsigned long hxfar,
|
||||
unsigned long long ipa),
|
||||
TP_ARGS(vcpu_pc, hsr, hxfar, ipa),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, vcpu_pc )
|
||||
__field( unsigned long, hsr )
|
||||
__field( unsigned long, hxfar )
|
||||
__field( unsigned long long, ipa )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu_pc = vcpu_pc;
|
||||
__entry->hsr = hsr;
|
||||
__entry->hxfar = hxfar;
|
||||
__entry->ipa = ipa;
|
||||
),
|
||||
|
||||
TP_printk("ipa %#llx, hsr %#08lx, hxfar %#08lx, pc %#016lx",
|
||||
__entry->ipa, __entry->hsr,
|
||||
__entry->hxfar, __entry->vcpu_pc)
|
||||
);
|
||||
|
||||
#endif /* KVM_ARM64_TRACE_KVM_H */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE trace
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
||||
|
|
@ -10,6 +10,11 @@
|
|||
|
||||
#include "gmap.h"
|
||||
#include "faultin.h"
|
||||
#ifdef KVM_S390_ARM64
|
||||
#include "arm.h"
|
||||
#else
|
||||
#include "s390.h"
|
||||
#endif
|
||||
|
||||
bool kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu);
|
||||
#define CREATE_TRACE_POINTS
|
||||
|
|
|
|||
|
|
@ -21,7 +21,11 @@
|
|||
|
||||
#include "dat.h"
|
||||
#include "gmap.h"
|
||||
#ifdef KVM_S390_ARM64
|
||||
#include "arm.h"
|
||||
#else
|
||||
#include "s390.h"
|
||||
#endif
|
||||
#include "faultin.h"
|
||||
|
||||
static int gmap_limit_to_type(gfn_t limit)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@
|
|||
#include <linux/kvm_types.h>
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#ifdef KVM_S390_ARM64
|
||||
#include "arm.h"
|
||||
#else
|
||||
#include "s390.h"
|
||||
#endif
|
||||
#include "gmap.h"
|
||||
#include "dat.h"
|
||||
#include "kvm_mmu.h"
|
||||
|
|
@ -76,7 +80,7 @@ int s390_kvm_mmu_prepare_memory_region(struct kvm *kvm,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (kvm->arch.migration_mode) {
|
||||
if (kvm_s390_is_migration_mode(kvm)) {
|
||||
/*
|
||||
* Turn off migration mode when:
|
||||
* - userspace creates a new memslot with dirty logging off,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,18 @@
|
|||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE trace_gmap
|
||||
|
||||
#ifdef KVM_S390_ARM64
|
||||
#define __KVM_FIELDS \
|
||||
__field(unsigned long, pstate) \
|
||||
__field(unsigned long, pc)
|
||||
#define __KVM_ASSIGN ({\
|
||||
__entry->pstate = vcpu->arch.sae_block.pstate; \
|
||||
__entry->pc = vcpu->arch.sae_block.pc; \
|
||||
})
|
||||
#define __KVM_PRINT \
|
||||
__entry->pstate, \
|
||||
__entry->pc
|
||||
#else
|
||||
#define __KVM_FIELDS \
|
||||
__field(unsigned long, pswmask) \
|
||||
__field(unsigned long, pswaddr)
|
||||
|
|
@ -23,6 +35,7 @@
|
|||
#define __KVM_PRINT \
|
||||
__entry->pswmask,\
|
||||
__entry->pswaddr
|
||||
#endif
|
||||
|
||||
TRACE_EVENT(kvm_s390_major_guest_pfault,
|
||||
TP_PROTO(struct kvm_vcpu *vcpu),
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
#include <linux/kvm_host.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/irq.h>
|
||||
|
|
@ -411,9 +413,9 @@ static int handle_partial_execution(struct kvm_vcpu *vcpu)
|
|||
*/
|
||||
int handle_sthyi(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct sthyi_sctns *sctns __free(kfree) = NULL;
|
||||
int reg1, reg2, cc = 0, r = 0;
|
||||
u64 code, addr, rc = 0;
|
||||
struct sthyi_sctns *sctns = NULL;
|
||||
|
||||
if (!test_kvm_facility(vcpu->kvm, 74))
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
|
||||
|
|
@ -430,37 +432,32 @@ int handle_sthyi(struct kvm_vcpu *vcpu)
|
|||
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
||||
|
||||
if (code & 0xffff) {
|
||||
cc = 3;
|
||||
rc = 4;
|
||||
goto out;
|
||||
vcpu->run->s.regs.gprs[reg2 + 1] = 4;
|
||||
kvm_s390_set_psw_cc(vcpu, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!kvm_s390_pv_cpu_is_protected(vcpu) && (addr & ~PAGE_MASK))
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
||||
|
||||
sctns = (void *)get_zeroed_page(GFP_KERNEL_ACCOUNT);
|
||||
sctns = kzalloc(PAGE_SIZE, GFP_KERNEL_ACCOUNT);
|
||||
if (!sctns)
|
||||
return -ENOMEM;
|
||||
|
||||
cc = sthyi_fill(sctns, &rc);
|
||||
if (cc < 0) {
|
||||
free_page((unsigned long)sctns);
|
||||
if (cc < 0)
|
||||
return cc;
|
||||
}
|
||||
out:
|
||||
|
||||
if (!cc) {
|
||||
if (kvm_s390_pv_cpu_is_protected(vcpu)) {
|
||||
memcpy(sida_addr(vcpu->arch.sie_block), sctns, PAGE_SIZE);
|
||||
} else {
|
||||
r = write_guest(vcpu, addr, reg2, sctns, PAGE_SIZE);
|
||||
if (r) {
|
||||
free_page((unsigned long)sctns);
|
||||
if (r)
|
||||
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;
|
||||
|
|
@ -536,6 +533,15 @@ static int handle_pv_sclp(struct kvm_vcpu *vcpu)
|
|||
set_bit(IRQ_PEND_EXT_SERVICE, &fi->pending_irqs);
|
||||
clear_bit(IRQ_PEND_EXT_SERVICE, &fi->masked_irqs);
|
||||
spin_unlock_irqrestore(&fi->lock, flags);
|
||||
|
||||
/*
|
||||
* We missed the floating IRQ kick since we can only inject
|
||||
* when we end up here and not when the irq was injected via
|
||||
* the FLIC.
|
||||
*
|
||||
* Now that we have cleared the masking we can kick cpus.
|
||||
*/
|
||||
kvm_s390_pv_sclp_kick(vcpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1918,49 +1918,126 @@ static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u64 inti_to_irq_pend_mask(u64 type, int isc)
|
||||
{
|
||||
switch (type) {
|
||||
case KVM_S390_MCHK:
|
||||
/* Only repressible machine checks are floating */
|
||||
return BIT(IRQ_PEND_MCHK_REP);
|
||||
case KVM_S390_INT_VIRTIO:
|
||||
return BIT(IRQ_PEND_VIRTIO);
|
||||
case KVM_S390_INT_SERVICE:
|
||||
return BIT(IRQ_PEND_EXT_SERVICE) |
|
||||
BIT(IRQ_PEND_EXT_SERVICE_EV);
|
||||
case KVM_S390_INT_PFAULT_DONE:
|
||||
return BIT(IRQ_PEND_PFAULT_DONE);
|
||||
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
|
||||
return BIT(isc_to_irq_type(isc));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a destination VCPU for a floating irq and kick it.
|
||||
* Setup intervention masks to catch running vcpus that hopefully open
|
||||
* their masks soonish and kick sleeping vcpus to motivate them to
|
||||
* take IRQs.
|
||||
*/
|
||||
static void __floating_irq_kick(struct kvm *kvm, u64 type)
|
||||
static void vcpu_intervention_kick(struct kvm_vcpu *vcpu, u64 type)
|
||||
{
|
||||
/* make the VCPU drop out of the SIE, or wake it up if sleeping */
|
||||
switch (type) {
|
||||
case KVM_S390_MCHK:
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_STOP_INT);
|
||||
break;
|
||||
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
|
||||
if (!(type & KVM_S390_INT_IO_AI_MASK &&
|
||||
vcpu->kvm->arch.gisa_int.origin) ||
|
||||
kvm_s390_pv_cpu_get_handle(vcpu))
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_IO_INT);
|
||||
break;
|
||||
default:
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_EXT_INT);
|
||||
break;
|
||||
}
|
||||
kvm_s390_vcpu_wakeup(vcpu);
|
||||
}
|
||||
|
||||
static void kick_cpu_irq(struct kvm *kvm, u64 type, u64 parm)
|
||||
{
|
||||
struct kvm_vcpu *dst_vcpu;
|
||||
int sigcpu, online_vcpus, nr_tries = 0;
|
||||
u64 irq_pend_mask;
|
||||
unsigned long i;
|
||||
|
||||
online_vcpus = atomic_read(&kvm->online_vcpus);
|
||||
if (!online_vcpus)
|
||||
return;
|
||||
|
||||
irq_pend_mask = inti_to_irq_pend_mask(type, parm);
|
||||
for (sigcpu = kvm->arch.float_int.last_sleep_cpu; ; sigcpu++) {
|
||||
sigcpu %= online_vcpus;
|
||||
dst_vcpu = kvm_get_vcpu(kvm, sigcpu);
|
||||
if (!is_vcpu_stopped(dst_vcpu))
|
||||
if (!is_vcpu_stopped(dst_vcpu) &&
|
||||
deliverable_irqs(dst_vcpu) & irq_pend_mask)
|
||||
break;
|
||||
/* avoid endless loops if all vcpus are stopped */
|
||||
if (nr_tries++ >= online_vcpus)
|
||||
return;
|
||||
if (nr_tries++ >= online_vcpus) {
|
||||
dst_vcpu = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* make the VCPU drop out of the SIE, or wake it up if sleeping */
|
||||
switch (type) {
|
||||
case KVM_S390_MCHK:
|
||||
kvm_s390_set_cpuflags(dst_vcpu, CPUSTAT_STOP_INT);
|
||||
break;
|
||||
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
|
||||
if (!(type & KVM_S390_INT_IO_AI_MASK &&
|
||||
kvm->arch.gisa_int.origin) ||
|
||||
kvm_s390_pv_cpu_get_handle(dst_vcpu))
|
||||
kvm_s390_set_cpuflags(dst_vcpu, CPUSTAT_IO_INT);
|
||||
break;
|
||||
default:
|
||||
kvm_s390_set_cpuflags(dst_vcpu, CPUSTAT_EXT_INT);
|
||||
break;
|
||||
/* Nobody was enabled, time to wake all of them */
|
||||
if (!dst_vcpu) {
|
||||
kvm_for_each_vcpu(i, dst_vcpu, kvm)
|
||||
vcpu_intervention_kick(dst_vcpu, type);
|
||||
return;
|
||||
}
|
||||
kvm_s390_vcpu_wakeup(dst_vcpu);
|
||||
|
||||
vcpu_intervention_kick(dst_vcpu, type);
|
||||
}
|
||||
|
||||
void kvm_s390_pv_sclp_kick(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* The cpu that called sclp likely will also take the IRQ, no
|
||||
* need to kick anyone.
|
||||
*/
|
||||
if (deliverable_irqs(vcpu) & BIT(IRQ_PEND_EXT_SERVICE))
|
||||
return;
|
||||
|
||||
/*
|
||||
* For the other cases we might have sleeping cpus with open
|
||||
* masks. Time to find and kick them.
|
||||
*/
|
||||
kick_cpu_irq(vcpu->kvm, KVM_S390_INT_SERVICE, -1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a destination VCPU for a floating irq and kick it.
|
||||
*/
|
||||
static void __floating_irq_kick(struct kvm *kvm, u64 type, u64 parm)
|
||||
{
|
||||
struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int;
|
||||
|
||||
/*
|
||||
* No need to kick on non-ev service IRQs for PV VMs, we're
|
||||
* not allowed to inject anyway. We need to wait for the sclp
|
||||
* instruction notification AFTER re-entry of the vcpu that
|
||||
* handled the instruction intercept.
|
||||
*/
|
||||
if (type == KVM_S390_INT_SERVICE && !(parm & SCCB_EVENT_PENDING) &&
|
||||
test_bit(IRQ_PEND_EXT_SERVICE, &fi->masked_irqs))
|
||||
return;
|
||||
|
||||
kick_cpu_irq(kvm, type, parm);
|
||||
}
|
||||
|
||||
static int __inject_vm(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
|
||||
{
|
||||
u64 type = READ_ONCE(inti->type);
|
||||
u64 parm = -1;
|
||||
int rc;
|
||||
|
||||
switch (type) {
|
||||
|
|
@ -1971,12 +2048,15 @@ static int __inject_vm(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
|
|||
rc = __inject_virtio(kvm, inti);
|
||||
break;
|
||||
case KVM_S390_INT_SERVICE:
|
||||
parm = inti->ext.ext_params & SCCB_EVENT_PENDING;
|
||||
rc = __inject_service(kvm, inti);
|
||||
break;
|
||||
case KVM_S390_INT_PFAULT_DONE:
|
||||
rc = __inject_pfault_done(kvm, inti);
|
||||
break;
|
||||
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
|
||||
/* Grab isc here since __inject_io() might free inti */
|
||||
parm = int_word_to_isc(inti->io.io_int_word);
|
||||
rc = __inject_io(kvm, inti);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -1985,7 +2065,7 @@ static int __inject_vm(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
__floating_irq_kick(kvm, type);
|
||||
__floating_irq_kick(kvm, type, parm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -3690,7 +3770,7 @@ void kvm_s390_gib_destroy(void)
|
|||
}
|
||||
chsc_sgib(0);
|
||||
unregister_adapter_interrupt(&gib_alert_irq);
|
||||
free_page((unsigned long)gib);
|
||||
kfree(gib);
|
||||
gib = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -3704,7 +3784,7 @@ int __init kvm_s390_gib_init(u8 nisc)
|
|||
goto out;
|
||||
}
|
||||
|
||||
gib = (struct kvm_s390_gib *)get_zeroed_page(GFP_KERNEL_ACCOUNT | GFP_DMA);
|
||||
gib = kzalloc(PAGE_SIZE, GFP_KERNEL_ACCOUNT | GFP_DMA);
|
||||
if (!gib) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
|
|
@ -3723,7 +3803,7 @@ int __init kvm_s390_gib_init(u8 nisc)
|
|||
gib_origin = virt_to_phys(gib);
|
||||
if (chsc_sgib(gib_origin)) {
|
||||
pr_err("Associating the GIB with the AIV facility failed\n");
|
||||
free_page((unsigned long)gib);
|
||||
kfree(gib);
|
||||
gib = NULL;
|
||||
rc = -EIO;
|
||||
goto out_unreg_gal;
|
||||
|
|
@ -3743,7 +3823,7 @@ int __init kvm_s390_gib_init(u8 nisc)
|
|||
out_unreg_gal:
|
||||
unregister_adapter_interrupt(&gib_alert_irq);
|
||||
out_free_gib:
|
||||
free_page((unsigned long)gib);
|
||||
kfree(gib);
|
||||
gib = NULL;
|
||||
out:
|
||||
return rc;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/mm_types.h>
|
||||
#include <linux/pgtable.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/facility.h>
|
||||
#include <asm/current.h>
|
||||
|
|
@ -869,7 +870,7 @@ static int handle_stsi(struct kvm_vcpu *vcpu)
|
|||
int fc = (vcpu->run->s.regs.gprs[0] & 0xf0000000) >> 28;
|
||||
int sel1 = vcpu->run->s.regs.gprs[0] & 0xff;
|
||||
int sel2 = vcpu->run->s.regs.gprs[1] & 0xffff;
|
||||
unsigned long mem = 0;
|
||||
void *mem = NULL;
|
||||
u64 operand2;
|
||||
int rc = 0;
|
||||
u8 ar;
|
||||
|
|
@ -911,19 +912,19 @@ static int handle_stsi(struct kvm_vcpu *vcpu)
|
|||
switch (fc) {
|
||||
case 1: /* same handling for 1 and 2 */
|
||||
case 2:
|
||||
mem = get_zeroed_page(GFP_KERNEL_ACCOUNT);
|
||||
mem = kzalloc(PAGE_SIZE, GFP_KERNEL_ACCOUNT);
|
||||
if (!mem)
|
||||
goto out_no_data;
|
||||
if (stsi((void *) mem, fc, sel1, sel2))
|
||||
if (stsi(mem, fc, sel1, sel2))
|
||||
goto out_no_data;
|
||||
break;
|
||||
case 3:
|
||||
if (sel1 != 2 || sel2 != 2)
|
||||
goto out_no_data;
|
||||
mem = get_zeroed_page(GFP_KERNEL_ACCOUNT);
|
||||
mem = kzalloc(PAGE_SIZE, GFP_KERNEL_ACCOUNT);
|
||||
if (!mem)
|
||||
goto out_no_data;
|
||||
handle_stsi_3_2_2(vcpu, (void *) mem);
|
||||
handle_stsi_3_2_2(vcpu, mem);
|
||||
break;
|
||||
case 15: /* fc 15 is fully handled in userspace */
|
||||
insert_stsi_usr_data(vcpu, operand2, ar, fc, sel1, sel2);
|
||||
|
|
@ -931,10 +932,10 @@ static int handle_stsi(struct kvm_vcpu *vcpu)
|
|||
return -EREMOTE;
|
||||
}
|
||||
if (kvm_s390_pv_cpu_is_protected(vcpu)) {
|
||||
memcpy(sida_addr(vcpu->arch.sie_block), (void *)mem, PAGE_SIZE);
|
||||
memcpy(sida_addr(vcpu->arch.sie_block), mem, PAGE_SIZE);
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = write_guest(vcpu, operand2, ar, (void *)mem, PAGE_SIZE);
|
||||
rc = write_guest(vcpu, operand2, ar, mem, PAGE_SIZE);
|
||||
}
|
||||
if (rc) {
|
||||
rc = kvm_s390_inject_prog_cond(vcpu, rc);
|
||||
|
|
@ -945,14 +946,14 @@ static int handle_stsi(struct kvm_vcpu *vcpu)
|
|||
rc = -EREMOTE;
|
||||
}
|
||||
trace_kvm_s390_handle_stsi(vcpu, fc, sel1, sel2, operand2);
|
||||
free_page(mem);
|
||||
kfree(mem);
|
||||
kvm_s390_set_psw_cc(vcpu, 0);
|
||||
vcpu->run->s.regs.gprs[0] = 0;
|
||||
return rc;
|
||||
out_no_data:
|
||||
kvm_s390_set_psw_cc(vcpu, 3);
|
||||
out:
|
||||
free_page(mem);
|
||||
kfree(mem);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3273,8 +3273,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
|||
goto out_err;
|
||||
|
||||
BUILD_BUG_ON(sizeof(struct sie_page2) != 4096);
|
||||
kvm->arch.sie_page2 =
|
||||
(struct sie_page2 *) get_zeroed_page(GFP_KERNEL_ACCOUNT | GFP_DMA);
|
||||
kvm->arch.sie_page2 = kzalloc_obj(*kvm->arch.sie_page2,
|
||||
GFP_KERNEL_ACCOUNT | GFP_DMA);
|
||||
if (!kvm->arch.sie_page2)
|
||||
goto out_err;
|
||||
|
||||
|
|
@ -3369,7 +3369,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
|||
|
||||
return 0;
|
||||
out_err:
|
||||
free_page((unsigned long)kvm->arch.sie_page2);
|
||||
kfree(kvm->arch.sie_page2);
|
||||
debug_unregister(kvm->arch.dbf);
|
||||
sca_dispose(kvm);
|
||||
KVM_EVENT(3, "creation of vm failed: %d", rc);
|
||||
|
|
@ -3427,7 +3427,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
|
|||
mmu_notifier_unregister(&kvm->arch.pv.mmu_notifier, kvm->mm);
|
||||
|
||||
debug_unregister(kvm->arch.dbf);
|
||||
free_page((unsigned long)kvm->arch.sie_page2);
|
||||
kfree(kvm->arch.sie_page2);
|
||||
kvm_s390_destroy_adapters(kvm);
|
||||
kvm_s390_clear_float_irqs(kvm);
|
||||
kvm_s390_vsie_destroy(kvm);
|
||||
|
|
@ -3656,13 +3656,13 @@ static void kvm_s390_vcpu_crypto_setup(struct kvm_vcpu *vcpu)
|
|||
void kvm_s390_vcpu_unsetup_cmma(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (vcpu->arch.sie_block->cbrlo)
|
||||
free_page((unsigned long)phys_to_virt(vcpu->arch.sie_block->cbrlo));
|
||||
kfree(phys_to_virt(vcpu->arch.sie_block->cbrlo & PAGE_MASK));
|
||||
vcpu->arch.sie_block->cbrlo = 0;
|
||||
}
|
||||
|
||||
int kvm_s390_vcpu_setup_cmma(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
void *cbrlo_page = (void *)get_zeroed_page(GFP_KERNEL_ACCOUNT);
|
||||
void *cbrlo_page = kzalloc(PAGE_SIZE, GFP_KERNEL_ACCOUNT);
|
||||
|
||||
if (!cbrlo_page)
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
|
|
@ -375,6 +375,7 @@ enum hrtimer_restart kvm_s390_idle_wakeup(struct hrtimer *timer);
|
|||
int __must_check kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu);
|
||||
void kvm_s390_clear_local_irqs(struct kvm_vcpu *vcpu);
|
||||
void kvm_s390_clear_float_irqs(struct kvm *kvm);
|
||||
void kvm_s390_pv_sclp_kick(struct kvm_vcpu *vcpu);
|
||||
int __must_check kvm_s390_inject_vm(struct kvm *kvm,
|
||||
struct kvm_s390_interrupt *s390int,
|
||||
struct kvm_s390_interrupt_info *inti);
|
||||
|
|
|
|||
|
|
@ -3,12 +3,16 @@
|
|||
# Makefile for s390 specific build tools
|
||||
#
|
||||
|
||||
include $(srctree)/arch/s390/tools/Makefile.arm64h
|
||||
|
||||
kapi := arch/$(ARCH)/include/generated/asm
|
||||
kapi-hdrs-y := $(kapi)/facility-defs.h $(kapi)/dis-defs.h
|
||||
|
||||
kapi-hdrs-$(CONFIG_KVM) += $(ARM64_TARGETS)
|
||||
|
||||
PHONY += kapi
|
||||
|
||||
kapi: $(kapi-hdrs-y)
|
||||
kapi: $(kapi-hdrs-y) $(kapi-hdrs-m)
|
||||
|
||||
hostprogs += gen_facilities
|
||||
hostprogs += gen_opcode_table
|
||||
|
|
|
|||
55
arch/s390/tools/Makefile.arm64h
Normal file
55
arch/s390/tools/Makefile.arm64h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Share ARM64 headers with s390 KVM
|
||||
#
|
||||
|
||||
arm64api := $(objtree)/arch/$(ARCH)/include/generated/arm64
|
||||
arm64uapi := $(objtree)/arch/$(ARCH)/include/generated/uapi/arm64
|
||||
|
||||
# List of ARM64 headers to share with s390 KVM
|
||||
# Format: [uapi:]header.h or header-gen.h
|
||||
#
|
||||
# * header.h - from arch/arm64/include/asm/ to arm64/
|
||||
# * uapi:header.h - from arch/arm64/include/uapi/asm/ to uapi/arm64/
|
||||
# * header-gen.h - generated as header-gen.h from ARM64 header header.h
|
||||
# If a header contains partial markers (#if ARM64_S390_COMMON == 1) only this part will be copied.
|
||||
# If the file does not contain any markers the whole file will be copied.
|
||||
ARM64_SHARED_HEADERS := \
|
||||
brk-imm.h \
|
||||
esr.h \
|
||||
kvm_arm.h \
|
||||
kvm_emulate-gen.h \
|
||||
kvm_host.h \
|
||||
ptrace-gen.h \
|
||||
sysreg-gen.h \
|
||||
uapi:hwcap.h \
|
||||
uapi:kvm.h \
|
||||
uapi:ptrace.h \
|
||||
uapi:sve_context.h
|
||||
|
||||
filechk_gen_arm_hdr = \
|
||||
$(AWK) -f $(src)/copy-arm64h.awk $< || \
|
||||
$(AWK) -f $(src)/copy-arm64h-full.awk $< || \
|
||||
{ echo "Error: Unbalanced ARM64_S390_COMMON markers in $<" >&2; exit 1; }
|
||||
|
||||
arm64-type = $(firstword $(subst :, ,$(1)))
|
||||
arm64-base = $(or $(word 2,$(subst :, ,$(1))),$(1))
|
||||
arm64-src-base = $(patsubst %-gen.h,%.h,$(call arm64-base,$(1)))
|
||||
|
||||
arm64-src = $(srctree)/arch/arm64/include/$(if $(filter uapi,$(call arm64-type,$(1))),uapi/)asm/$(call arm64-src-base,$(1))
|
||||
arm64-dst = $(if $(filter uapi,$(call arm64-type,$(1))),$(arm64uapi),$(arm64api))/$(call arm64-base,$(1))
|
||||
|
||||
$(foreach hdr,$(ARM64_SHARED_HEADERS),\
|
||||
$(eval $(call arm64-dst,$(hdr)): $(call arm64-src,$(hdr)) $(src)/copy-arm64h.awk $(src)/copy-arm64h-full.awk FORCE))
|
||||
|
||||
ARM64_TARGETS := $(sort $(foreach hdr,$(ARM64_SHARED_HEADERS),$(call arm64-dst,$(hdr))))
|
||||
$(ARM64_TARGETS):
|
||||
$(call filechk,gen_arm_hdr)
|
||||
|
||||
filechk_gen_sysreg_arm64 = \
|
||||
$(AWK) -f $(srctree)/arch/arm64/tools/gen-sysreg.awk $(srctree)/arch/arm64/tools/sysreg
|
||||
|
||||
$(arm64api)/sysreg-defs.h: $(srctree)/arch/arm64/tools/gen-sysreg.awk $(srctree)/arch/arm64/tools/sysreg FORCE
|
||||
$(call filechk,gen_sysreg_arm64)
|
||||
|
||||
ARM64_TARGETS += $(arm64api)/sysreg-defs.h
|
||||
50
arch/s390/tools/copy-arm64h-full.awk
Normal file
50
arch/s390/tools/copy-arm64h-full.awk
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#!/usr/bin/awk -f
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Process entire ARM64 headers for sharing with s390 KVM
|
||||
#
|
||||
# Usage: copy-arm64h-full.awk <input_file>
|
||||
#
|
||||
# This processes the entire file (unlike copy-arm64h.awk which only extracts marked sections)
|
||||
|
||||
BEGIN {
|
||||
max_guard_line = 25
|
||||
guard_found = 0
|
||||
header_added = 0
|
||||
}
|
||||
|
||||
# Find and process the include guard in the first few lines
|
||||
NR <= max_guard_line && !guard_found && /^#ifndef [A-Z_0-9]+$/ {
|
||||
guard_name = $2
|
||||
guard_found = 1
|
||||
print "/* This header was copied from " FILENAME " */"
|
||||
print ""
|
||||
# Map subsequent lines back to the original ARM64 source file so that
|
||||
# compiler diagnostics point there rather than at the generated copy.
|
||||
print "#line " NR " \"" FILENAME "\""
|
||||
print
|
||||
header_added = 1
|
||||
next
|
||||
}
|
||||
|
||||
# Transform include directives
|
||||
/^#include <uapi\/asm\// {
|
||||
sub(/<uapi\/asm\//, "<uapi/arm64/")
|
||||
print
|
||||
next
|
||||
}
|
||||
|
||||
/^#include <asm\// {
|
||||
sub(/<asm\//, "<arm64/")
|
||||
print
|
||||
next
|
||||
}
|
||||
|
||||
/ARM64_S390_COMMON/ {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Pass through all other lines
|
||||
{
|
||||
print
|
||||
}
|
||||
83
arch/s390/tools/copy-arm64h.awk
Normal file
83
arch/s390/tools/copy-arm64h.awk
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/awk -f
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Extract marked sections from ARM64 headers for sharing with s390 KVM
|
||||
#
|
||||
# Usage: copy-arm64h.awk <input_file>
|
||||
#
|
||||
# Extracts all sections between start/end markers. If no markers found, signals for fallback.
|
||||
BEGIN {
|
||||
# Constants
|
||||
start_pattern = "^#if ARM64_S390_COMMON == 1$"
|
||||
end_pattern = "^#endif \\/\\* ARM64_S390_COMMON == 1 \\*\\/$"
|
||||
max_guard_line = 25
|
||||
|
||||
# State variables
|
||||
copying = found_marker = error = 0
|
||||
guard_name = ""
|
||||
file_header_done = 0
|
||||
depth = 0
|
||||
}
|
||||
|
||||
NR <= max_guard_line && !guard_name && /^#ifndef [A-Za-z0-9_]+$/ {
|
||||
guard_name = $2
|
||||
next
|
||||
}
|
||||
|
||||
NR > max_guard_line && !guard_name && !file_header_done {
|
||||
print "error: no include guard found in first " max_guard_line " lines" > "/dev/stderr"
|
||||
file_header_done = 1
|
||||
error = 1; exit
|
||||
}
|
||||
|
||||
$0 ~ start_pattern {
|
||||
if (copying) { error = 1; exit }
|
||||
if (!found_marker) {
|
||||
print "/*"
|
||||
print " * This header was automatically generated from " FILENAME
|
||||
print " * Do not modify this file directly."
|
||||
print " */"
|
||||
print "#ifndef " guard_name
|
||||
print "#define " guard_name
|
||||
print ""
|
||||
}
|
||||
depth = copying= found_marker = 1
|
||||
# Map subsequent lines back to the original ARM64 source file so that
|
||||
# compiler diagnostics point there rather than at the generated copy.
|
||||
print "#line " (NR + 1) " \"" FILENAME "\""
|
||||
next
|
||||
}
|
||||
|
||||
guard_name && !found_marker {
|
||||
next
|
||||
}
|
||||
|
||||
$0 ~ /^[ \t]*#[ \t]*endif/ {
|
||||
depth--
|
||||
if (depth == 0) {
|
||||
if ($0 ~ end_pattern ) {
|
||||
copying = 0
|
||||
next
|
||||
}
|
||||
error = 1; exit
|
||||
}
|
||||
}
|
||||
|
||||
/ARM64_S390_COMMON/ { error = 1; exit }
|
||||
|
||||
copying {
|
||||
if ($0 ~ /^[ \t]*#[ \t]*if(def|ndef)?([ \t]|$)/) depth++
|
||||
gsub(/#include <asm\//, "#include <arm64/")
|
||||
gsub(/#include <uapi\/asm\//, "#include <uapi/arm64/")
|
||||
print
|
||||
next
|
||||
}
|
||||
|
||||
END {
|
||||
if (error) exit 1
|
||||
if (found_marker) {
|
||||
print ""
|
||||
print "#endif /* " guard_name " */"
|
||||
}
|
||||
exit !found_marker || copying
|
||||
}
|
||||
|
|
@ -594,6 +594,9 @@ b9a0 clp RRF_U0RR
|
|||
b9a1 tpei RRE_RR
|
||||
b9a2 ptf RRE_R0
|
||||
b9a4 uvc RRF_URR
|
||||
b9a5 sae RRE_R0
|
||||
b9a6 lasrm RRE_R0
|
||||
b9a7 stiasrm RRE_R0
|
||||
b9aa lptea RRF_RURR2
|
||||
b9ab essa RRF_U0RR
|
||||
b9ac irbm RRE_RR
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ static void __init sclp_early_facilities_detect(void)
|
|||
sclp.has_ib = cpue->ib;
|
||||
sclp.has_cei = cpue->cei;
|
||||
sclp.has_skey = cpue->skey;
|
||||
sclp.has_aef = cpue->aef;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -231,6 +231,7 @@ TEST_GEN_PROGS_s390 += rseq_test
|
|||
TEST_GEN_PROGS_s390 += s390/irq_routing
|
||||
TEST_GEN_PROGS_s390 += mmu_stress_test
|
||||
TEST_GEN_PROGS_s390 += pre_fault_memory_test
|
||||
TEST_GEN_PROGS_s390 += s390/irq_injection
|
||||
|
||||
TEST_GEN_PROGS_riscv = $(TEST_GEN_PROGS_COMMON)
|
||||
TEST_GEN_PROGS_riscv += riscv/sbi_pmu_test
|
||||
|
|
|
|||
74
tools/testing/selftests/kvm/s390/irq_injection.c
Normal file
74
tools/testing/selftests/kvm/s390/irq_injection.c
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Test for KVM_S390_IRQ - s390x IRQ injection API
|
||||
*
|
||||
* Verifies that VM-level interrupt types are correctly rejected when
|
||||
* injected via the vCPU file descriptor, and that invalid arguments
|
||||
* to vCPU-level interrupts are also rejected.
|
||||
*
|
||||
* Copyright IBM Corp. 2026
|
||||
* Author: Gabriella Seifert <gseifert@linux.ibm.com>
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "kvm_util.h"
|
||||
#include "test_util.h"
|
||||
#include <linux/kvm.h>
|
||||
|
||||
static void test_vm_irq_on_vcpu_fails(struct kvm_vcpu *vcpu, __u64 type)
|
||||
{
|
||||
struct kvm_s390_irq irq = { .type = type };
|
||||
int rc;
|
||||
|
||||
rc = __vcpu_ioctl(vcpu, KVM_S390_IRQ, &irq);
|
||||
ksft_test_result(rc == -1 && errno == EINVAL,
|
||||
"VM on VCPU IRQ type 0x%llx: expected %d, got %d\n",
|
||||
type, EINVAL, errno);
|
||||
}
|
||||
|
||||
static void test_vcpu_irq_bad_param_fails(struct kvm_vcpu *vcpu,
|
||||
struct kvm_s390_irq *irq)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = __vcpu_ioctl(vcpu, KVM_S390_IRQ, irq);
|
||||
ksft_test_result(rc == -1 && errno == EINVAL,
|
||||
"VCPU IRQ bad param type 0x%llx: expected %d got %d\n",
|
||||
irq->type, EINVAL, errno);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct kvm_s390_irq irq;
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm;
|
||||
|
||||
ksft_print_header();
|
||||
ksft_set_plan(6);
|
||||
vm = vm_create_with_one_vcpu(&vcpu, NULL);
|
||||
|
||||
test_vm_irq_on_vcpu_fails(vcpu, KVM_S390_INT_VIRTIO);
|
||||
test_vm_irq_on_vcpu_fails(vcpu, KVM_S390_INT_SERVICE);
|
||||
test_vm_irq_on_vcpu_fails(vcpu, KVM_S390_INT_IO_MIN);
|
||||
|
||||
memset(&irq, 0, sizeof(irq));
|
||||
irq.type = KVM_S390_INT_EMERGENCY;
|
||||
irq.u.emerg.code = 0xffff;
|
||||
test_vcpu_irq_bad_param_fails(vcpu, &irq);
|
||||
|
||||
memset(&irq, 0, sizeof(irq));
|
||||
irq.type = KVM_S390_INT_EXTERNAL_CALL;
|
||||
irq.u.extcall.code = 0xffff;
|
||||
test_vcpu_irq_bad_param_fails(vcpu, &irq);
|
||||
|
||||
memset(&irq, 0, sizeof(irq));
|
||||
irq.type = 0xdeadbeef;
|
||||
test_vcpu_irq_bad_param_fails(vcpu, &irq);
|
||||
|
||||
kvm_vm_free(vm);
|
||||
ksft_finished();
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user