PPC:
* Improvements and bugfixes for secure VM support, giving reduced startup time and memory hotplug support. * Locking fixes in nested KVM code * Increase number of guests supported by HV KVM to 4094 * Preliminary POWER10 support ARM: * Split the VHE and nVHE hypervisor code bases, build the EL2 code separately, allowing for the VHE code to now be built with instrumentation * Level-based TLB invalidation support * Restructure of the vcpu register storage to accomodate the NV code * Pointer Authentication available for guests on nVHE hosts * Simplification of the system register table parsing * MMU cleanups and fixes * A number of post-32bit cleanups and other fixes MIPS: * compilation fixes x86: * bugfixes * support for the SERIALIZE instruction -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAl8yfuQUHHBib256aW5p QHJlZGhhdC5jb20ACgkQv/vSX3jHroNweQgAiEycRbpifAueihK3ScKwYcCFhbHg n6KLiFCY3sJRg+ORNb9EuFPJgGygV8DPKbEMvKaGDhNpX3rOpSIrpi5QQ5Hx+WOj WHg+aX8Eyy1ys7V84UbiMeZKUbKDDRr0/UOUtJEsF4hiD7s0FgobbQhC/3+awp5k sdSTMYlXelep+pjdFX7cNIgjrBNFtqH0ECeuDCcQzDg2zlH+poEPyLaC5+U4RF6r pfvcxd6xp50fobo8ro7kMuBeclG3JxLjqqdNrkkHcF1DxROMLLKN7CjHZchYC/BK c+S7JHLFnafxiTncMLhv3s4viey05mohW6SxeLw4qcWHfFlz+qyfZwMvZA== =d/GI -----END PGP SIGNATURE----- Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm Pull more KVM updates from Paolo Bonzini: "PPC: - Improvements and bugfixes for secure VM support, giving reduced startup time and memory hotplug support. - Locking fixes in nested KVM code - Increase number of guests supported by HV KVM to 4094 - Preliminary POWER10 support ARM: - Split the VHE and nVHE hypervisor code bases, build the EL2 code separately, allowing for the VHE code to now be built with instrumentation - Level-based TLB invalidation support - Restructure of the vcpu register storage to accomodate the NV code - Pointer Authentication available for guests on nVHE hosts - Simplification of the system register table parsing - MMU cleanups and fixes - A number of post-32bit cleanups and other fixes MIPS: - compilation fixes x86: - bugfixes - support for the SERIALIZE instruction" * tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm: (70 commits) KVM: MIPS/VZ: Fix build error caused by 'kvm_run' cleanup x86/kvm/hyper-v: Synic default SCONTROL MSR needs to be enabled MIPS: KVM: Convert a fallthrough comment to fallthrough MIPS: VZ: Only include loongson_regs.h for CPU_LOONGSON64 x86: Expose SERIALIZE for supported cpuid KVM: x86: Don't attempt to load PDPTRs when 64-bit mode is enabled KVM: arm64: Move S1PTW S2 fault logic out of io_mem_abort() KVM: arm64: Don't skip cache maintenance for read-only memslots KVM: arm64: Handle data and instruction external aborts the same way KVM: arm64: Rename kvm_vcpu_dabt_isextabt() KVM: arm: Add trace name for ARM_NISV KVM: arm64: Ensure that all nVHE hyp code is in .hyp.text KVM: arm64: Substitute RANDOMIZE_BASE for HARDEN_EL2_VECTORS KVM: arm64: Make nVHE ASLR conditional on RANDOMIZE_BASE KVM: PPC: Book3S HV: Rework secure mem slot dropping KVM: PPC: Book3S HV: Move kvmppc_svm_page_out up KVM: PPC: Book3S HV: Migrate hot plugged memory KVM: PPC: Book3S HV: In H_SVM_INIT_DONE, migrate remaining normal-GFNs to secure-GFNs KVM: PPC: Book3S HV: Track the state GFNs associated with secure VMs KVM: PPC: Book3S HV: Disable page merging in H_SVM_INIT_START ...
This commit is contained in:
commit
8cd84b7096
|
@ -895,6 +895,7 @@ Return values
|
|||
One of the following values:
|
||||
|
||||
* H_SUCCESS on success.
|
||||
* H_STATE if the VM is not in a position to switch to secure.
|
||||
|
||||
Description
|
||||
~~~~~~~~~~~
|
||||
|
@ -933,6 +934,8 @@ Return values
|
|||
* H_UNSUPPORTED if called from the wrong context (e.g.
|
||||
from an SVM or before an H_SVM_INIT_START
|
||||
hypercall).
|
||||
* H_STATE if the hypervisor could not successfully
|
||||
transition the VM to Secure VM.
|
||||
|
||||
Description
|
||||
~~~~~~~~~~~
|
||||
|
|
|
@ -1182,22 +1182,6 @@ config HARDEN_BRANCH_PREDICTOR
|
|||
|
||||
If unsure, say Y.
|
||||
|
||||
config HARDEN_EL2_VECTORS
|
||||
bool "Harden EL2 vector mapping against system register leak" if EXPERT
|
||||
default y
|
||||
help
|
||||
Speculation attacks against some high-performance processors can
|
||||
be used to leak privileged information such as the vector base
|
||||
register, resulting in a potential defeat of the EL2 layout
|
||||
randomization.
|
||||
|
||||
This config option will map the vectors to a fixed location,
|
||||
independent of the EL2 code mapping, so that revealing VBAR_EL2
|
||||
to an attacker does not give away any extra information. This
|
||||
only gets enabled on affected CPUs.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config ARM64_SSBD
|
||||
bool "Speculative Store Bypass Disable" if EXPERT
|
||||
default y
|
||||
|
@ -1520,7 +1504,6 @@ menu "ARMv8.3 architectural features"
|
|||
config ARM64_PTR_AUTH
|
||||
bool "Enable support for pointer authentication"
|
||||
default y
|
||||
depends on !KVM || ARM64_VHE
|
||||
depends on (CC_HAS_SIGN_RETURN_ADDRESS || CC_HAS_BRANCH_PROT_PAC_RET) && AS_HAS_PAC
|
||||
# Modern compilers insert a .note.gnu.property section note for PAC
|
||||
# which is only understood by binutils starting with version 2.33.1.
|
||||
|
@ -1547,8 +1530,7 @@ config ARM64_PTR_AUTH
|
|||
|
||||
The feature is detected at runtime. If the feature is not present in
|
||||
hardware it will not be advertised to userspace/KVM guest nor will it
|
||||
be enabled. However, KVM guest also require VHE mode and hence
|
||||
CONFIG_ARM64_VHE=y option to use this feature.
|
||||
be enabled.
|
||||
|
||||
If the feature is present on the boot CPU but not on a late CPU, then
|
||||
the late CPU will be parked. Also, if the boot CPU does not have
|
||||
|
|
|
@ -42,33 +42,81 @@
|
|||
|
||||
#include <linux/mm.h>
|
||||
|
||||
/* Translate a kernel address of @sym into its equivalent linear mapping */
|
||||
#define kvm_ksym_ref(sym) \
|
||||
/*
|
||||
* Translate name of a symbol defined in nVHE hyp to the name seen
|
||||
* by kernel proper. All nVHE symbols are prefixed by the build system
|
||||
* to avoid clashes with the VHE variants.
|
||||
*/
|
||||
#define kvm_nvhe_sym(sym) __kvm_nvhe_##sym
|
||||
|
||||
#define DECLARE_KVM_VHE_SYM(sym) extern char sym[]
|
||||
#define DECLARE_KVM_NVHE_SYM(sym) extern char kvm_nvhe_sym(sym)[]
|
||||
|
||||
/*
|
||||
* Define a pair of symbols sharing the same name but one defined in
|
||||
* VHE and the other in nVHE hyp implementations.
|
||||
*/
|
||||
#define DECLARE_KVM_HYP_SYM(sym) \
|
||||
DECLARE_KVM_VHE_SYM(sym); \
|
||||
DECLARE_KVM_NVHE_SYM(sym)
|
||||
|
||||
#define CHOOSE_VHE_SYM(sym) sym
|
||||
#define CHOOSE_NVHE_SYM(sym) kvm_nvhe_sym(sym)
|
||||
|
||||
#ifndef __KVM_NVHE_HYPERVISOR__
|
||||
/*
|
||||
* BIG FAT WARNINGS:
|
||||
*
|
||||
* - Don't be tempted to change the following is_kernel_in_hyp_mode()
|
||||
* to has_vhe(). has_vhe() is implemented as a *final* capability,
|
||||
* while this is used early at boot time, when the capabilities are
|
||||
* not final yet....
|
||||
*
|
||||
* - Don't let the nVHE hypervisor have access to this, as it will
|
||||
* pick the *wrong* symbol (yes, it runs at EL2...).
|
||||
*/
|
||||
#define CHOOSE_HYP_SYM(sym) (is_kernel_in_hyp_mode() ? CHOOSE_VHE_SYM(sym) \
|
||||
: CHOOSE_NVHE_SYM(sym))
|
||||
#else
|
||||
/* The nVHE hypervisor shouldn't even try to access anything */
|
||||
extern void *__nvhe_undefined_symbol;
|
||||
#define CHOOSE_HYP_SYM(sym) __nvhe_undefined_symbol
|
||||
#endif
|
||||
|
||||
/* Translate a kernel address @ptr into its equivalent linear mapping */
|
||||
#define kvm_ksym_ref(ptr) \
|
||||
({ \
|
||||
void *val = &sym; \
|
||||
void *val = (ptr); \
|
||||
if (!is_kernel_in_hyp_mode()) \
|
||||
val = lm_alias(&sym); \
|
||||
val = lm_alias((ptr)); \
|
||||
val; \
|
||||
})
|
||||
#define kvm_ksym_ref_nvhe(sym) kvm_ksym_ref(kvm_nvhe_sym(sym))
|
||||
|
||||
struct kvm;
|
||||
struct kvm_vcpu;
|
||||
struct kvm_s2_mmu;
|
||||
|
||||
extern char __kvm_hyp_init[];
|
||||
extern char __kvm_hyp_init_end[];
|
||||
DECLARE_KVM_NVHE_SYM(__kvm_hyp_init);
|
||||
DECLARE_KVM_HYP_SYM(__kvm_hyp_vector);
|
||||
#define __kvm_hyp_init CHOOSE_NVHE_SYM(__kvm_hyp_init)
|
||||
#define __kvm_hyp_vector CHOOSE_HYP_SYM(__kvm_hyp_vector)
|
||||
|
||||
extern char __kvm_hyp_vector[];
|
||||
#ifdef CONFIG_KVM_INDIRECT_VECTORS
|
||||
extern atomic_t arm64_el2_vector_last_slot;
|
||||
DECLARE_KVM_HYP_SYM(__bp_harden_hyp_vecs);
|
||||
#define __bp_harden_hyp_vecs CHOOSE_HYP_SYM(__bp_harden_hyp_vecs)
|
||||
#endif
|
||||
|
||||
extern void __kvm_flush_vm_context(void);
|
||||
extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
|
||||
extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
|
||||
extern void __kvm_tlb_flush_local_vmid(struct kvm_vcpu *vcpu);
|
||||
extern void __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu, phys_addr_t ipa,
|
||||
int level);
|
||||
extern void __kvm_tlb_flush_vmid(struct kvm_s2_mmu *mmu);
|
||||
extern void __kvm_tlb_flush_local_vmid(struct kvm_s2_mmu *mmu);
|
||||
|
||||
extern void __kvm_timer_set_cntvoff(u64 cntvoff);
|
||||
|
||||
extern int kvm_vcpu_run_vhe(struct kvm_vcpu *vcpu);
|
||||
|
||||
extern int __kvm_vcpu_run_nvhe(struct kvm_vcpu *vcpu);
|
||||
extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
|
||||
|
||||
extern void __kvm_enable_ssbs(void);
|
||||
|
||||
|
@ -143,7 +191,6 @@ extern char __smccc_workaround_1_smc[__SMCCC_WORKAROUND_1_SMC_SZ];
|
|||
.macro get_vcpu_ptr vcpu, ctxt
|
||||
get_host_ctxt \ctxt, \vcpu
|
||||
ldr \vcpu, [\ctxt, #HOST_CONTEXT_VCPU]
|
||||
kern_hyp_va \vcpu
|
||||
.endm
|
||||
|
||||
#endif
|
||||
|
|
|
@ -19,14 +19,6 @@ struct kvm_sys_reg_table {
|
|||
size_t num;
|
||||
};
|
||||
|
||||
struct kvm_sys_reg_target_table {
|
||||
struct kvm_sys_reg_table table64;
|
||||
struct kvm_sys_reg_table table32;
|
||||
};
|
||||
|
||||
void kvm_register_target_sys_reg_table(unsigned int target,
|
||||
struct kvm_sys_reg_target_table *table);
|
||||
|
||||
int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu);
|
||||
int kvm_handle_cp14_32(struct kvm_vcpu *vcpu);
|
||||
int kvm_handle_cp14_64(struct kvm_vcpu *vcpu);
|
||||
|
|
|
@ -124,33 +124,12 @@ static inline void vcpu_set_vsesr(struct kvm_vcpu *vcpu, u64 vsesr)
|
|||
|
||||
static __always_inline unsigned long *vcpu_pc(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return (unsigned long *)&vcpu_gp_regs(vcpu)->regs.pc;
|
||||
}
|
||||
|
||||
static inline unsigned long *__vcpu_elr_el1(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return (unsigned long *)&vcpu_gp_regs(vcpu)->elr_el1;
|
||||
}
|
||||
|
||||
static inline unsigned long vcpu_read_elr_el1(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (vcpu->arch.sysregs_loaded_on_cpu)
|
||||
return read_sysreg_el1(SYS_ELR);
|
||||
else
|
||||
return *__vcpu_elr_el1(vcpu);
|
||||
}
|
||||
|
||||
static inline void vcpu_write_elr_el1(const struct kvm_vcpu *vcpu, unsigned long v)
|
||||
{
|
||||
if (vcpu->arch.sysregs_loaded_on_cpu)
|
||||
write_sysreg_el1(v, SYS_ELR);
|
||||
else
|
||||
*__vcpu_elr_el1(vcpu) = v;
|
||||
return (unsigned long *)&vcpu_gp_regs(vcpu)->pc;
|
||||
}
|
||||
|
||||
static __always_inline unsigned long *vcpu_cpsr(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return (unsigned long *)&vcpu_gp_regs(vcpu)->regs.pstate;
|
||||
return (unsigned long *)&vcpu_gp_regs(vcpu)->pstate;
|
||||
}
|
||||
|
||||
static __always_inline bool vcpu_mode_is_32bit(const struct kvm_vcpu *vcpu)
|
||||
|
@ -179,14 +158,14 @@ static inline void vcpu_set_thumb(struct kvm_vcpu *vcpu)
|
|||
static __always_inline unsigned long vcpu_get_reg(const struct kvm_vcpu *vcpu,
|
||||
u8 reg_num)
|
||||
{
|
||||
return (reg_num == 31) ? 0 : vcpu_gp_regs(vcpu)->regs.regs[reg_num];
|
||||
return (reg_num == 31) ? 0 : vcpu_gp_regs(vcpu)->regs[reg_num];
|
||||
}
|
||||
|
||||
static __always_inline void vcpu_set_reg(struct kvm_vcpu *vcpu, u8 reg_num,
|
||||
unsigned long val)
|
||||
{
|
||||
if (reg_num != 31)
|
||||
vcpu_gp_regs(vcpu)->regs.regs[reg_num] = val;
|
||||
vcpu_gp_regs(vcpu)->regs[reg_num] = val;
|
||||
}
|
||||
|
||||
static inline unsigned long vcpu_read_spsr(const struct kvm_vcpu *vcpu)
|
||||
|
@ -197,7 +176,7 @@ static inline unsigned long vcpu_read_spsr(const struct kvm_vcpu *vcpu)
|
|||
if (vcpu->arch.sysregs_loaded_on_cpu)
|
||||
return read_sysreg_el1(SYS_SPSR);
|
||||
else
|
||||
return vcpu_gp_regs(vcpu)->spsr[KVM_SPSR_EL1];
|
||||
return __vcpu_sys_reg(vcpu, SPSR_EL1);
|
||||
}
|
||||
|
||||
static inline void vcpu_write_spsr(struct kvm_vcpu *vcpu, unsigned long v)
|
||||
|
@ -210,7 +189,7 @@ static inline void vcpu_write_spsr(struct kvm_vcpu *vcpu, unsigned long v)
|
|||
if (vcpu->arch.sysregs_loaded_on_cpu)
|
||||
write_sysreg_el1(v, SYS_SPSR);
|
||||
else
|
||||
vcpu_gp_regs(vcpu)->spsr[KVM_SPSR_EL1] = v;
|
||||
__vcpu_sys_reg(vcpu, SPSR_EL1) = v;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -259,14 +238,14 @@ static inline bool vcpu_mode_priv(const struct kvm_vcpu *vcpu)
|
|||
return mode != PSR_MODE_EL0t;
|
||||
}
|
||||
|
||||
static __always_inline u32 kvm_vcpu_get_hsr(const struct kvm_vcpu *vcpu)
|
||||
static __always_inline u32 kvm_vcpu_get_esr(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->arch.fault.esr_el2;
|
||||
}
|
||||
|
||||
static __always_inline int kvm_vcpu_get_condition(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 esr = kvm_vcpu_get_hsr(vcpu);
|
||||
u32 esr = kvm_vcpu_get_esr(vcpu);
|
||||
|
||||
if (esr & ESR_ELx_CV)
|
||||
return (esr & ESR_ELx_COND_MASK) >> ESR_ELx_COND_SHIFT;
|
||||
|
@ -291,64 +270,64 @@ static inline u64 kvm_vcpu_get_disr(const struct kvm_vcpu *vcpu)
|
|||
|
||||
static inline u32 kvm_vcpu_hvc_get_imm(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return kvm_vcpu_get_hsr(vcpu) & ESR_ELx_xVC_IMM_MASK;
|
||||
return kvm_vcpu_get_esr(vcpu) & ESR_ELx_xVC_IMM_MASK;
|
||||
}
|
||||
|
||||
static __always_inline bool kvm_vcpu_dabt_isvalid(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return !!(kvm_vcpu_get_hsr(vcpu) & ESR_ELx_ISV);
|
||||
return !!(kvm_vcpu_get_esr(vcpu) & ESR_ELx_ISV);
|
||||
}
|
||||
|
||||
static inline unsigned long kvm_vcpu_dabt_iss_nisv_sanitized(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return kvm_vcpu_get_hsr(vcpu) & (ESR_ELx_CM | ESR_ELx_WNR | ESR_ELx_FSC);
|
||||
return kvm_vcpu_get_esr(vcpu) & (ESR_ELx_CM | ESR_ELx_WNR | ESR_ELx_FSC);
|
||||
}
|
||||
|
||||
static inline bool kvm_vcpu_dabt_issext(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return !!(kvm_vcpu_get_hsr(vcpu) & ESR_ELx_SSE);
|
||||
return !!(kvm_vcpu_get_esr(vcpu) & ESR_ELx_SSE);
|
||||
}
|
||||
|
||||
static inline bool kvm_vcpu_dabt_issf(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return !!(kvm_vcpu_get_hsr(vcpu) & ESR_ELx_SF);
|
||||
return !!(kvm_vcpu_get_esr(vcpu) & ESR_ELx_SF);
|
||||
}
|
||||
|
||||
static __always_inline int kvm_vcpu_dabt_get_rd(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return (kvm_vcpu_get_hsr(vcpu) & ESR_ELx_SRT_MASK) >> ESR_ELx_SRT_SHIFT;
|
||||
return (kvm_vcpu_get_esr(vcpu) & ESR_ELx_SRT_MASK) >> ESR_ELx_SRT_SHIFT;
|
||||
}
|
||||
|
||||
static __always_inline bool kvm_vcpu_dabt_iss1tw(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return !!(kvm_vcpu_get_hsr(vcpu) & ESR_ELx_S1PTW);
|
||||
return !!(kvm_vcpu_get_esr(vcpu) & ESR_ELx_S1PTW);
|
||||
}
|
||||
|
||||
static __always_inline bool kvm_vcpu_dabt_iswrite(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return !!(kvm_vcpu_get_hsr(vcpu) & ESR_ELx_WNR) ||
|
||||
return !!(kvm_vcpu_get_esr(vcpu) & ESR_ELx_WNR) ||
|
||||
kvm_vcpu_dabt_iss1tw(vcpu); /* AF/DBM update */
|
||||
}
|
||||
|
||||
static inline bool kvm_vcpu_dabt_is_cm(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return !!(kvm_vcpu_get_hsr(vcpu) & ESR_ELx_CM);
|
||||
return !!(kvm_vcpu_get_esr(vcpu) & ESR_ELx_CM);
|
||||
}
|
||||
|
||||
static __always_inline unsigned int kvm_vcpu_dabt_get_as(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return 1 << ((kvm_vcpu_get_hsr(vcpu) & ESR_ELx_SAS) >> ESR_ELx_SAS_SHIFT);
|
||||
return 1 << ((kvm_vcpu_get_esr(vcpu) & ESR_ELx_SAS) >> ESR_ELx_SAS_SHIFT);
|
||||
}
|
||||
|
||||
/* This one is not specific to Data Abort */
|
||||
static __always_inline bool kvm_vcpu_trap_il_is32bit(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return !!(kvm_vcpu_get_hsr(vcpu) & ESR_ELx_IL);
|
||||
return !!(kvm_vcpu_get_esr(vcpu) & ESR_ELx_IL);
|
||||
}
|
||||
|
||||
static __always_inline u8 kvm_vcpu_trap_get_class(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return ESR_ELx_EC(kvm_vcpu_get_hsr(vcpu));
|
||||
return ESR_ELx_EC(kvm_vcpu_get_esr(vcpu));
|
||||
}
|
||||
|
||||
static inline bool kvm_vcpu_trap_is_iabt(const struct kvm_vcpu *vcpu)
|
||||
|
@ -358,15 +337,15 @@ static inline bool kvm_vcpu_trap_is_iabt(const struct kvm_vcpu *vcpu)
|
|||
|
||||
static __always_inline u8 kvm_vcpu_trap_get_fault(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return kvm_vcpu_get_hsr(vcpu) & ESR_ELx_FSC;
|
||||
return kvm_vcpu_get_esr(vcpu) & ESR_ELx_FSC;
|
||||
}
|
||||
|
||||
static __always_inline u8 kvm_vcpu_trap_get_fault_type(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return kvm_vcpu_get_hsr(vcpu) & ESR_ELx_FSC_TYPE;
|
||||
return kvm_vcpu_get_esr(vcpu) & ESR_ELx_FSC_TYPE;
|
||||
}
|
||||
|
||||
static __always_inline bool kvm_vcpu_dabt_isextabt(const struct kvm_vcpu *vcpu)
|
||||
static __always_inline bool kvm_vcpu_abt_issea(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
switch (kvm_vcpu_trap_get_fault(vcpu)) {
|
||||
case FSC_SEA:
|
||||
|
@ -387,7 +366,7 @@ static __always_inline bool kvm_vcpu_dabt_isextabt(const struct kvm_vcpu *vcpu)
|
|||
|
||||
static __always_inline int kvm_vcpu_sys_get_rt(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 esr = kvm_vcpu_get_hsr(vcpu);
|
||||
u32 esr = kvm_vcpu_get_esr(vcpu);
|
||||
return ESR_ELx_SYS64_ISS_RT(esr);
|
||||
}
|
||||
|
||||
|
@ -516,14 +495,14 @@ static __always_inline void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_i
|
|||
* Skip an instruction which has been emulated at hyp while most guest sysregs
|
||||
* are live.
|
||||
*/
|
||||
static __always_inline void __hyp_text __kvm_skip_instr(struct kvm_vcpu *vcpu)
|
||||
static __always_inline void __kvm_skip_instr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
*vcpu_pc(vcpu) = read_sysreg_el2(SYS_ELR);
|
||||
vcpu->arch.ctxt.gp_regs.regs.pstate = read_sysreg_el2(SYS_SPSR);
|
||||
vcpu_gp_regs(vcpu)->pstate = read_sysreg_el2(SYS_SPSR);
|
||||
|
||||
kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
|
||||
|
||||
write_sysreg_el2(vcpu->arch.ctxt.gp_regs.regs.pstate, SYS_SPSR);
|
||||
write_sysreg_el2(vcpu_gp_regs(vcpu)->pstate, SYS_SPSR);
|
||||
write_sysreg_el2(*vcpu_pc(vcpu), SYS_ELR);
|
||||
}
|
||||
|
||||
|
|
|
@ -66,19 +66,34 @@ struct kvm_vmid {
|
|||
u32 vmid;
|
||||
};
|
||||
|
||||
struct kvm_arch {
|
||||
struct kvm_s2_mmu {
|
||||
struct kvm_vmid vmid;
|
||||
|
||||
/* stage2 entry level table */
|
||||
pgd_t *pgd;
|
||||
phys_addr_t pgd_phys;
|
||||
|
||||
/* VTCR_EL2 value for this VM */
|
||||
u64 vtcr;
|
||||
/*
|
||||
* stage2 entry level table
|
||||
*
|
||||
* Two kvm_s2_mmu structures in the same VM can point to the same
|
||||
* pgd here. This happens when running a guest using a
|
||||
* translation regime that isn't affected by its own stage-2
|
||||
* translation, such as a non-VHE hypervisor running at vEL2, or
|
||||
* for vEL1/EL0 with vHCR_EL2.VM == 0. In that case, we use the
|
||||
* canonical stage-2 page tables.
|
||||
*/
|
||||
pgd_t *pgd;
|
||||
phys_addr_t pgd_phys;
|
||||
|
||||
/* The last vcpu id that ran on each physical CPU */
|
||||
int __percpu *last_vcpu_ran;
|
||||
|
||||
struct kvm *kvm;
|
||||
};
|
||||
|
||||
struct kvm_arch {
|
||||
struct kvm_s2_mmu mmu;
|
||||
|
||||
/* VTCR_EL2 value for this VM */
|
||||
u64 vtcr;
|
||||
|
||||
/* The maximum number of vCPUs depends on the used GIC model */
|
||||
int max_vcpus;
|
||||
|
||||
|
@ -159,6 +174,16 @@ enum vcpu_sysreg {
|
|||
APGAKEYLO_EL1,
|
||||
APGAKEYHI_EL1,
|
||||
|
||||
ELR_EL1,
|
||||
SP_EL1,
|
||||
SPSR_EL1,
|
||||
|
||||
CNTVOFF_EL2,
|
||||
CNTV_CVAL_EL0,
|
||||
CNTV_CTL_EL0,
|
||||
CNTP_CVAL_EL0,
|
||||
CNTP_CTL_EL0,
|
||||
|
||||
/* 32bit specific registers. Keep them at the end of the range */
|
||||
DACR32_EL2, /* Domain Access Control Register */
|
||||
IFSR32_EL2, /* Instruction Fault Status Register */
|
||||
|
@ -210,7 +235,15 @@ enum vcpu_sysreg {
|
|||
#define NR_COPRO_REGS (NR_SYS_REGS * 2)
|
||||
|
||||
struct kvm_cpu_context {
|
||||
struct kvm_regs gp_regs;
|
||||
struct user_pt_regs regs; /* sp = sp_el0 */
|
||||
|
||||
u64 spsr_abt;
|
||||
u64 spsr_und;
|
||||
u64 spsr_irq;
|
||||
u64 spsr_fiq;
|
||||
|
||||
struct user_fpsimd_state fp_regs;
|
||||
|
||||
union {
|
||||
u64 sys_regs[NR_SYS_REGS];
|
||||
u32 copro[NR_COPRO_REGS];
|
||||
|
@ -243,6 +276,9 @@ struct kvm_vcpu_arch {
|
|||
void *sve_state;
|
||||
unsigned int sve_max_vl;
|
||||
|
||||
/* Stage 2 paging state used by the hardware on next switch */
|
||||
struct kvm_s2_mmu *hw_mmu;
|
||||
|
||||
/* HYP configuration */
|
||||
u64 hcr_el2;
|
||||
u32 mdcr_el2;
|
||||
|
@ -327,7 +363,7 @@ struct kvm_vcpu_arch {
|
|||
struct vcpu_reset_state reset_state;
|
||||
|
||||
/* True when deferrable sysregs are loaded on the physical CPU,
|
||||
* see kvm_vcpu_load_sysregs and kvm_vcpu_put_sysregs. */
|
||||
* see kvm_vcpu_load_sysregs_vhe and kvm_vcpu_put_sysregs_vhe. */
|
||||
bool sysregs_loaded_on_cpu;
|
||||
|
||||
/* Guest PV state */
|
||||
|
@ -378,15 +414,20 @@ struct kvm_vcpu_arch {
|
|||
#define vcpu_has_ptrauth(vcpu) false
|
||||
#endif
|
||||
|
||||
#define vcpu_gp_regs(v) (&(v)->arch.ctxt.gp_regs)
|
||||
#define vcpu_gp_regs(v) (&(v)->arch.ctxt.regs)
|
||||
|
||||
/*
|
||||
* Only use __vcpu_sys_reg if you know you want the memory backed version of a
|
||||
* register, and not the one most recently accessed by a running VCPU. For
|
||||
* example, for userspace access or for system registers that are never context
|
||||
* switched, but only emulated.
|
||||
* Only use __vcpu_sys_reg/ctxt_sys_reg if you know you want the
|
||||
* memory backed version of a register, and not the one most recently
|
||||
* accessed by a running VCPU. For example, for userspace access or
|
||||
* for system registers that are never context switched, but only
|
||||
* emulated.
|
||||
*/
|
||||
#define __vcpu_sys_reg(v,r) ((v)->arch.ctxt.sys_regs[(r)])
|
||||
#define __ctxt_sys_reg(c,r) (&(c)->sys_regs[(r)])
|
||||
|
||||
#define ctxt_sys_reg(c,r) (*__ctxt_sys_reg(c,r))
|
||||
|
||||
#define __vcpu_sys_reg(v,r) (ctxt_sys_reg(&(v)->arch.ctxt, (r)))
|
||||
|
||||
u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg);
|
||||
void vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, int reg);
|
||||
|
@ -442,6 +483,18 @@ void kvm_arm_resume_guest(struct kvm *kvm);
|
|||
|
||||
u64 __kvm_call_hyp(void *hypfn, ...);
|
||||
|
||||
#define kvm_call_hyp_nvhe(f, ...) \
|
||||
do { \
|
||||
DECLARE_KVM_NVHE_SYM(f); \
|
||||
__kvm_call_hyp(kvm_ksym_ref_nvhe(f), ##__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#define kvm_call_hyp_nvhe_ret(f, ...) \
|
||||
({ \
|
||||
DECLARE_KVM_NVHE_SYM(f); \
|
||||
__kvm_call_hyp(kvm_ksym_ref_nvhe(f), ##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
/*
|
||||
* The couple of isb() below are there to guarantee the same behaviour
|
||||
* on VHE as on !VHE, where the eret to EL1 acts as a context
|
||||
|
@ -453,7 +506,7 @@ u64 __kvm_call_hyp(void *hypfn, ...);
|
|||
f(__VA_ARGS__); \
|
||||
isb(); \
|
||||
} else { \
|
||||
__kvm_call_hyp(kvm_ksym_ref(f), ##__VA_ARGS__); \
|
||||
kvm_call_hyp_nvhe(f, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -465,8 +518,7 @@ u64 __kvm_call_hyp(void *hypfn, ...);
|
|||
ret = f(__VA_ARGS__); \
|
||||
isb(); \
|
||||
} else { \
|
||||
ret = __kvm_call_hyp(kvm_ksym_ref(f), \
|
||||
##__VA_ARGS__); \
|
||||
ret = kvm_call_hyp_nvhe_ret(f, ##__VA_ARGS__); \
|
||||
} \
|
||||
\
|
||||
ret; \
|
||||
|
@ -518,7 +570,7 @@ DECLARE_PER_CPU(kvm_host_data_t, kvm_host_data);
|
|||
static inline void kvm_init_host_cpu_context(struct kvm_cpu_context *cpu_ctxt)
|
||||
{
|
||||
/* The host's MPIDR is immutable, so let's set it up at boot time */
|
||||
cpu_ctxt->sys_regs[MPIDR_EL1] = read_cpuid_mpidr();
|
||||
ctxt_sys_reg(cpu_ctxt, MPIDR_EL1) = read_cpuid_mpidr();
|
||||
}
|
||||
|
||||
static inline bool kvm_arch_requires_vhe(void)
|
||||
|
@ -619,8 +671,8 @@ static inline int kvm_arm_have_ssbd(void)
|
|||
}
|
||||
}
|
||||
|
||||
void kvm_vcpu_load_sysregs(struct kvm_vcpu *vcpu);
|
||||
void kvm_vcpu_put_sysregs(struct kvm_vcpu *vcpu);
|
||||
void kvm_vcpu_load_sysregs_vhe(struct kvm_vcpu *vcpu);
|
||||
void kvm_vcpu_put_sysregs_vhe(struct kvm_vcpu *vcpu);
|
||||
|
||||
int kvm_set_ipa_limit(void);
|
||||
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#include <asm/alternative.h>
|
||||
#include <asm/sysreg.h>
|
||||
|
||||
#define __hyp_text __section(.hyp.text) notrace __noscs
|
||||
|
||||
#define read_sysreg_elx(r,nvh,vh) \
|
||||
({ \
|
||||
u64 reg; \
|
||||
|
@ -63,17 +61,20 @@ void __vgic_v3_save_aprs(struct vgic_v3_cpu_if *cpu_if);
|
|||
void __vgic_v3_restore_aprs(struct vgic_v3_cpu_if *cpu_if);
|
||||
int __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu);
|
||||
|
||||
#ifdef __KVM_NVHE_HYPERVISOR__
|
||||
void __timer_enable_traps(struct kvm_vcpu *vcpu);
|
||||
void __timer_disable_traps(struct kvm_vcpu *vcpu);
|
||||
#endif
|
||||
|
||||
#ifdef __KVM_NVHE_HYPERVISOR__
|
||||
void __sysreg_save_state_nvhe(struct kvm_cpu_context *ctxt);
|
||||
void __sysreg_restore_state_nvhe(struct kvm_cpu_context *ctxt);
|
||||
#else
|
||||
void sysreg_save_host_state_vhe(struct kvm_cpu_context *ctxt);
|
||||
void sysreg_restore_host_state_vhe(struct kvm_cpu_context *ctxt);
|
||||
void sysreg_save_guest_state_vhe(struct kvm_cpu_context *ctxt);
|
||||
void sysreg_restore_guest_state_vhe(struct kvm_cpu_context *ctxt);
|
||||
void __sysreg32_save_state(struct kvm_vcpu *vcpu);
|
||||
void __sysreg32_restore_state(struct kvm_vcpu *vcpu);
|
||||
#endif
|
||||
|
||||
void __debug_switch_to_guest(struct kvm_vcpu *vcpu);
|
||||
void __debug_switch_to_host(struct kvm_vcpu *vcpu);
|
||||
|
@ -81,11 +82,17 @@ void __debug_switch_to_host(struct kvm_vcpu *vcpu);
|
|||
void __fpsimd_save_state(struct user_fpsimd_state *fp_regs);
|
||||
void __fpsimd_restore_state(struct user_fpsimd_state *fp_regs);
|
||||
|
||||
#ifndef __KVM_NVHE_HYPERVISOR__
|
||||
void activate_traps_vhe_load(struct kvm_vcpu *vcpu);
|
||||
void deactivate_traps_vhe_put(void);
|
||||
#endif
|
||||
|
||||
u64 __guest_enter(struct kvm_vcpu *vcpu, struct kvm_cpu_context *host_ctxt);
|
||||
|
||||
void __noreturn hyp_panic(struct kvm_cpu_context *host_ctxt);
|
||||
#ifdef __KVM_NVHE_HYPERVISOR__
|
||||
void __noreturn __hyp_do_panic(unsigned long, ...);
|
||||
#endif
|
||||
|
||||
#endif /* __ARM64_KVM_HYP_H__ */
|
||||
|
||||
|
|
|
@ -134,8 +134,8 @@ int create_hyp_exec_mappings(phys_addr_t phys_addr, size_t size,
|
|||
void free_hyp_pgds(void);
|
||||
|
||||
void stage2_unmap_vm(struct kvm *kvm);
|
||||
int kvm_alloc_stage2_pgd(struct kvm *kvm);
|
||||
void kvm_free_stage2_pgd(struct kvm *kvm);
|
||||
int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu);
|
||||
void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu);
|
||||
int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
|
||||
phys_addr_t pa, unsigned long size, bool writable);
|
||||
|
||||
|
@ -577,13 +577,13 @@ static inline u64 kvm_vttbr_baddr_mask(struct kvm *kvm)
|
|||
return vttbr_baddr_mask(kvm_phys_shift(kvm), kvm_stage2_levels(kvm));
|
||||
}
|
||||
|
||||
static __always_inline u64 kvm_get_vttbr(struct kvm *kvm)
|
||||
static __always_inline u64 kvm_get_vttbr(struct kvm_s2_mmu *mmu)
|
||||
{
|
||||
struct kvm_vmid *vmid = &kvm->arch.vmid;
|
||||
struct kvm_vmid *vmid = &mmu->vmid;
|
||||
u64 vmid_field, baddr;
|
||||
u64 cnp = system_supports_cnp() ? VTTBR_CNP_BIT : 0;
|
||||
|
||||
baddr = kvm->arch.pgd_phys;
|
||||
baddr = mmu->pgd_phys;
|
||||
vmid_field = (u64)vmid->vmid << VTTBR_VMID_SHIFT;
|
||||
return kvm_phys_to_vttbr(baddr) | vmid_field | cnp;
|
||||
}
|
||||
|
@ -592,10 +592,10 @@ static __always_inline u64 kvm_get_vttbr(struct kvm *kvm)
|
|||
* Must be called from hyp code running at EL2 with an updated VTTBR
|
||||
* and interrupts disabled.
|
||||
*/
|
||||
static __always_inline void __load_guest_stage2(struct kvm *kvm)
|
||||
static __always_inline void __load_guest_stage2(struct kvm_s2_mmu *mmu)
|
||||
{
|
||||
write_sysreg(kvm->arch.vtcr, vtcr_el2);
|
||||
write_sysreg(kvm_get_vttbr(kvm), vttbr_el2);
|
||||
write_sysreg(kern_hyp_va(mmu->kvm)->arch.vtcr, vtcr_el2);
|
||||
write_sysreg(kvm_get_vttbr(mmu), vttbr_el2);
|
||||
|
||||
/*
|
||||
* ARM errata 1165522 and 1530923 require the actual execution of the
|
||||
|
|
|
@ -61,44 +61,36 @@
|
|||
|
||||
/*
|
||||
* Both ptrauth_switch_to_guest and ptrauth_switch_to_host macros will
|
||||
* check for the presence of one of the cpufeature flag
|
||||
* ARM64_HAS_ADDRESS_AUTH_ARCH or ARM64_HAS_ADDRESS_AUTH_IMP_DEF and
|
||||
* check for the presence ARM64_HAS_ADDRESS_AUTH, which is defined as
|
||||
* (ARM64_HAS_ADDRESS_AUTH_ARCH || ARM64_HAS_ADDRESS_AUTH_IMP_DEF) and
|
||||
* then proceed ahead with the save/restore of Pointer Authentication
|
||||
* key registers.
|
||||
* key registers if enabled for the guest.
|
||||
*/
|
||||
.macro ptrauth_switch_to_guest g_ctxt, reg1, reg2, reg3
|
||||
alternative_if ARM64_HAS_ADDRESS_AUTH_ARCH
|
||||
b 1000f
|
||||
alternative_if_not ARM64_HAS_ADDRESS_AUTH
|
||||
b .L__skip_switch\@
|
||||
alternative_else_nop_endif
|
||||
alternative_if_not ARM64_HAS_ADDRESS_AUTH_IMP_DEF
|
||||
b 1001f
|
||||
alternative_else_nop_endif
|
||||
1000:
|
||||
ldr \reg1, [\g_ctxt, #(VCPU_HCR_EL2 - VCPU_CONTEXT)]
|
||||
mrs \reg1, hcr_el2
|
||||
and \reg1, \reg1, #(HCR_API | HCR_APK)
|
||||
cbz \reg1, 1001f
|
||||
cbz \reg1, .L__skip_switch\@
|
||||
add \reg1, \g_ctxt, #CPU_APIAKEYLO_EL1
|
||||
ptrauth_restore_state \reg1, \reg2, \reg3
|
||||
1001:
|
||||
.L__skip_switch\@:
|
||||
.endm
|
||||
|
||||
.macro ptrauth_switch_to_host g_ctxt, h_ctxt, reg1, reg2, reg3
|
||||
alternative_if ARM64_HAS_ADDRESS_AUTH_ARCH
|
||||
b 2000f
|
||||
alternative_if_not ARM64_HAS_ADDRESS_AUTH
|
||||
b .L__skip_switch\@
|
||||
alternative_else_nop_endif
|
||||
alternative_if_not ARM64_HAS_ADDRESS_AUTH_IMP_DEF
|
||||
b 2001f
|
||||
alternative_else_nop_endif
|
||||
2000:
|
||||
ldr \reg1, [\g_ctxt, #(VCPU_HCR_EL2 - VCPU_CONTEXT)]
|
||||
mrs \reg1, hcr_el2
|
||||
and \reg1, \reg1, #(HCR_API | HCR_APK)
|
||||
cbz \reg1, 2001f
|
||||
cbz \reg1, .L__skip_switch\@
|
||||
add \reg1, \g_ctxt, #CPU_APIAKEYLO_EL1
|
||||
ptrauth_save_state \reg1, \reg2, \reg3
|
||||
add \reg1, \h_ctxt, #CPU_APIAKEYLO_EL1
|
||||
ptrauth_restore_state \reg1, \reg2, \reg3
|
||||
isb
|
||||
2001:
|
||||
.L__skip_switch\@:
|
||||
.endm
|
||||
|
||||
#else /* !CONFIG_ARM64_PTR_AUTH */
|
||||
|
|
|
@ -45,13 +45,6 @@ struct bp_hardening_data {
|
|||
bp_hardening_cb_t fn;
|
||||
};
|
||||
|
||||
#if (defined(CONFIG_HARDEN_BRANCH_PREDICTOR) || \
|
||||
defined(CONFIG_HARDEN_EL2_VECTORS))
|
||||
|
||||
extern char __bp_harden_hyp_vecs[];
|
||||
extern atomic_t arm64_el2_vector_last_slot;
|
||||
#endif /* CONFIG_HARDEN_BRANCH_PREDICTOR || CONFIG_HARDEN_EL2_VECTORS */
|
||||
|
||||
#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR
|
||||
DECLARE_PER_CPU_READ_MOSTLY(struct bp_hardening_data, bp_hardening_data);
|
||||
|
||||
|
|
|
@ -85,10 +85,17 @@ static inline bool is_kernel_in_hyp_mode(void)
|
|||
|
||||
static __always_inline bool has_vhe(void)
|
||||
{
|
||||
if (cpus_have_final_cap(ARM64_HAS_VIRT_HOST_EXTN))
|
||||
/*
|
||||
* The following macros are defined for code specic to VHE/nVHE.
|
||||
* If has_vhe() is inlined into those compilation units, it can
|
||||
* be determined statically. Otherwise fall back to caps.
|
||||
*/
|
||||
if (__is_defined(__KVM_VHE_HYPERVISOR__))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
else if (__is_defined(__KVM_NVHE_HYPERVISOR__))
|
||||
return false;
|
||||
else
|
||||
return cpus_have_final_cap(ARM64_HAS_VIRT_HOST_EXTN);
|
||||
}
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
|
|
@ -102,13 +102,12 @@ int main(void)
|
|||
DEFINE(VCPU_FAULT_DISR, offsetof(struct kvm_vcpu, arch.fault.disr_el1));
|
||||
DEFINE(VCPU_WORKAROUND_FLAGS, offsetof(struct kvm_vcpu, arch.workaround_flags));
|
||||
DEFINE(VCPU_HCR_EL2, offsetof(struct kvm_vcpu, arch.hcr_el2));
|
||||
DEFINE(CPU_GP_REGS, offsetof(struct kvm_cpu_context, gp_regs));
|
||||
DEFINE(CPU_USER_PT_REGS, offsetof(struct kvm_cpu_context, regs));
|
||||
DEFINE(CPU_APIAKEYLO_EL1, offsetof(struct kvm_cpu_context, sys_regs[APIAKEYLO_EL1]));
|
||||
DEFINE(CPU_APIBKEYLO_EL1, offsetof(struct kvm_cpu_context, sys_regs[APIBKEYLO_EL1]));
|
||||
DEFINE(CPU_APDAKEYLO_EL1, offsetof(struct kvm_cpu_context, sys_regs[APDAKEYLO_EL1]));
|
||||
DEFINE(CPU_APDBKEYLO_EL1, offsetof(struct kvm_cpu_context, sys_regs[APDBKEYLO_EL1]));
|
||||
DEFINE(CPU_APGAKEYLO_EL1, offsetof(struct kvm_cpu_context, sys_regs[APGAKEYLO_EL1]));
|
||||
DEFINE(CPU_USER_PT_REGS, offsetof(struct kvm_regs, regs));
|
||||
DEFINE(HOST_CONTEXT_VCPU, offsetof(struct kvm_cpu_context, __hyp_running_vcpu));
|
||||
DEFINE(HOST_DATA_CONTEXT, offsetof(struct kvm_host_data, host_ctxt));
|
||||
#endif
|
||||
|
|
|
@ -632,7 +632,7 @@ has_neoverse_n1_erratum_1542419(const struct arm64_cpu_capabilities *entry,
|
|||
return is_midr_in_range(midr, &range) && has_dic;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_HARDEN_EL2_VECTORS)
|
||||
#ifdef CONFIG_RANDOMIZE_BASE
|
||||
|
||||
static const struct midr_range ca57_a72[] = {
|
||||
MIDR_ALL_VERSIONS(MIDR_CORTEX_A57),
|
||||
|
@ -891,7 +891,7 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
|
|||
.type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM,
|
||||
.matches = check_branch_predictor,
|
||||
},
|
||||
#ifdef CONFIG_HARDEN_EL2_VECTORS
|
||||
#ifdef CONFIG_RANDOMIZE_BASE
|
||||
{
|
||||
.desc = "EL2 vector hardening",
|
||||
.capability = ARM64_HARDEN_EL2_VECTORS,
|
||||
|
|
|
@ -51,4 +51,58 @@ __efistub__ctype = _ctype;
|
|||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KVM
|
||||
|
||||
/*
|
||||
* KVM nVHE code has its own symbol namespace prefixed with __kvm_nvhe_, to
|
||||
* separate it from the kernel proper. The following symbols are legally
|
||||
* accessed by it, therefore provide aliases to make them linkable.
|
||||
* Do not include symbols which may not be safely accessed under hypervisor
|
||||
* memory mappings.
|
||||
*/
|
||||
|
||||
#define KVM_NVHE_ALIAS(sym) __kvm_nvhe_##sym = sym;
|
||||
|
||||
/* Alternative callbacks for init-time patching of nVHE hyp code. */
|
||||
KVM_NVHE_ALIAS(arm64_enable_wa2_handling);
|
||||
KVM_NVHE_ALIAS(kvm_patch_vector_branch);
|
||||
KVM_NVHE_ALIAS(kvm_update_va_mask);
|
||||
|
||||
/* Global kernel state accessed by nVHE hyp code. */
|
||||
KVM_NVHE_ALIAS(arm64_ssbd_callback_required);
|
||||
KVM_NVHE_ALIAS(kvm_host_data);
|
||||
KVM_NVHE_ALIAS(kvm_vgic_global_state);
|
||||
|
||||
/* Kernel constant needed to compute idmap addresses. */
|
||||
KVM_NVHE_ALIAS(kimage_voffset);
|
||||
|
||||
/* Kernel symbols used to call panic() from nVHE hyp code (via ERET). */
|
||||
KVM_NVHE_ALIAS(__hyp_panic_string);
|
||||
KVM_NVHE_ALIAS(panic);
|
||||
|
||||
/* Vectors installed by hyp-init on reset HVC. */
|
||||
KVM_NVHE_ALIAS(__hyp_stub_vectors);
|
||||
|
||||
/* IDMAP TCR_EL1.T0SZ as computed by the EL1 init code */
|
||||
KVM_NVHE_ALIAS(idmap_t0sz);
|
||||
|
||||
/* Kernel symbol used by icache_is_vpipt(). */
|
||||
KVM_NVHE_ALIAS(__icache_flags);
|
||||
|
||||
/* Kernel symbols needed for cpus_have_final/const_caps checks. */
|
||||
KVM_NVHE_ALIAS(arm64_const_caps_ready);
|
||||
KVM_NVHE_ALIAS(cpu_hwcap_keys);
|
||||
KVM_NVHE_ALIAS(cpu_hwcaps);
|
||||
|
||||
/* Static keys which are set if a vGIC trap should be handled in hyp. */
|
||||
KVM_NVHE_ALIAS(vgic_v2_cpuif_trap);
|
||||
KVM_NVHE_ALIAS(vgic_v3_cpuif_trap);
|
||||
|
||||
/* Static key checked in pmr_sync(). */
|
||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
||||
KVM_NVHE_ALIAS(gic_pmr_sync);
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_KVM */
|
||||
|
||||
#endif /* __ARM64_KERNEL_IMAGE_VARS_H */
|
||||
|
|
|
@ -58,7 +58,7 @@ config KVM_ARM_PMU
|
|||
virtual machines.
|
||||
|
||||
config KVM_INDIRECT_VECTORS
|
||||
def_bool HARDEN_BRANCH_PREDICTOR || HARDEN_EL2_VECTORS
|
||||
def_bool HARDEN_BRANCH_PREDICTOR || RANDOMIZE_BASE
|
||||
|
||||
endif # KVM
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ obj-$(CONFIG_KVM) += hyp/
|
|||
kvm-y := $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/eventfd.o \
|
||||
$(KVM)/vfio.o $(KVM)/irqchip.o \
|
||||
arm.o mmu.o mmio.o psci.o perf.o hypercalls.o pvtime.o \
|
||||
inject_fault.o regmap.o va_layout.o hyp.o hyp-init.o handle_exit.o \
|
||||
guest.o debug.o reset.o sys_regs.o sys_regs_generic_v8.o \
|
||||
inject_fault.o regmap.o va_layout.o hyp.o handle_exit.o \
|
||||
guest.o debug.o reset.o sys_regs.o \
|
||||
vgic-sys-reg-v3.o fpsimd.o pmu.o \
|
||||
aarch32.o arch_timer.o \
|
||||
vgic/vgic.o vgic/vgic-init.o \
|
||||
|
|
|
@ -51,6 +51,93 @@ static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,
|
|||
struct arch_timer_context *timer,
|
||||
enum kvm_arch_timer_regs treg);
|
||||
|
||||
u32 timer_get_ctl(struct arch_timer_context *ctxt)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = ctxt->vcpu;
|
||||
|
||||
switch(arch_timer_ctx_index(ctxt)) {
|
||||
case TIMER_VTIMER:
|
||||
return __vcpu_sys_reg(vcpu, CNTV_CTL_EL0);
|
||||
case TIMER_PTIMER:
|
||||
return __vcpu_sys_reg(vcpu, CNTP_CTL_EL0);
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
u64 timer_get_cval(struct arch_timer_context *ctxt)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = ctxt->vcpu;
|
||||
|
||||
switch(arch_timer_ctx_index(ctxt)) {
|
||||
case TIMER_VTIMER:
|
||||
return __vcpu_sys_reg(vcpu, CNTV_CVAL_EL0);
|
||||
case TIMER_PTIMER:
|
||||
return __vcpu_sys_reg(vcpu, CNTP_CVAL_EL0);
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static u64 timer_get_offset(struct arch_timer_context *ctxt)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = ctxt->vcpu;
|
||||
|
||||
switch(arch_timer_ctx_index(ctxt)) {
|
||||
case TIMER_VTIMER:
|
||||
return __vcpu_sys_reg(vcpu, CNTVOFF_EL2);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void timer_set_ctl(struct arch_timer_context *ctxt, u32 ctl)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = ctxt->vcpu;
|
||||
|
||||
switch(arch_timer_ctx_index(ctxt)) {
|
||||
case TIMER_VTIMER:
|
||||
__vcpu_sys_reg(vcpu, CNTV_CTL_EL0) = ctl;
|
||||
break;
|
||||
case TIMER_PTIMER:
|
||||
__vcpu_sys_reg(vcpu, CNTP_CTL_EL0) = ctl;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void timer_set_cval(struct arch_timer_context *ctxt, u64 cval)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = ctxt->vcpu;
|
||||
|
||||
switch(arch_timer_ctx_index(ctxt)) {
|
||||
case TIMER_VTIMER:
|
||||
__vcpu_sys_reg(vcpu, CNTV_CVAL_EL0) = cval;
|
||||
break;
|
||||
case TIMER_PTIMER:
|
||||
__vcpu_sys_reg(vcpu, CNTP_CVAL_EL0) = cval;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void timer_set_offset(struct arch_timer_context *ctxt, u64 offset)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = ctxt->vcpu;
|
||||
|
||||
switch(arch_timer_ctx_index(ctxt)) {
|
||||
case TIMER_VTIMER:
|
||||
__vcpu_sys_reg(vcpu, CNTVOFF_EL2) = offset;
|
||||
break;
|
||||
default:
|
||||
WARN(offset, "timer %ld\n", arch_timer_ctx_index(ctxt));
|
||||
}
|
||||
}
|
||||
|
||||
u64 kvm_phys_timer_read(void)
|
||||
{
|
||||
return timecounter->cc->read(timecounter->cc);
|
||||
|
@ -124,8 +211,8 @@ static u64 kvm_timer_compute_delta(struct arch_timer_context *timer_ctx)
|
|||
{
|
||||
u64 cval, now;
|
||||
|
||||
cval = timer_ctx->cnt_cval;
|
||||
now = kvm_phys_timer_read() - timer_ctx->cntvoff;
|
||||
cval = timer_get_cval(timer_ctx);
|
||||
now = kvm_phys_timer_read() - timer_get_offset(timer_ctx);
|
||||
|
||||
if (now < cval) {
|
||||
u64 ns;
|
||||
|
@ -144,8 +231,8 @@ static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx)
|
|||
{
|
||||
WARN_ON(timer_ctx && timer_ctx->loaded);
|
||||
return timer_ctx &&
|
||||
!(timer_ctx->cnt_ctl & ARCH_TIMER_CTRL_IT_MASK) &&
|
||||
(timer_ctx->cnt_ctl & ARCH_TIMER_CTRL_ENABLE);
|
||||
((timer_get_ctl(timer_ctx) &
|
||||
(ARCH_TIMER_CTRL_IT_MASK | ARCH_TIMER_CTRL_ENABLE)) == ARCH_TIMER_CTRL_ENABLE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -256,8 +343,8 @@ static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx)
|
|||
if (!kvm_timer_irq_can_fire(timer_ctx))
|
||||
return false;
|
||||
|
||||
cval = timer_ctx->cnt_cval;
|
||||
now = kvm_phys_timer_read() - timer_ctx->cntvoff;
|
||||
cval = timer_get_cval(timer_ctx);
|
||||
now = kvm_phys_timer_read() - timer_get_offset(timer_ctx);
|
||||
|
||||
return cval <= now;
|
||||
}
|
||||
|
@ -350,8 +437,8 @@ static void timer_save_state(struct arch_timer_context *ctx)
|
|||
|
||||
switch (index) {
|
||||
case TIMER_VTIMER:
|
||||
ctx->cnt_ctl = read_sysreg_el0(SYS_CNTV_CTL);
|
||||
ctx->cnt_cval = read_sysreg_el0(SYS_CNTV_CVAL);
|
||||
timer_set_ctl(ctx, read_sysreg_el0(SYS_CNTV_CTL));
|
||||
timer_set_cval(ctx, read_sysreg_el0(SYS_CNTV_CVAL));
|
||||
|
||||
/* Disable the timer */
|
||||
write_sysreg_el0(0, SYS_CNTV_CTL);
|
||||
|
@ -359,8 +446,8 @@ static void timer_save_state(struct arch_timer_context *ctx)
|
|||
|
||||
break;
|
||||
case TIMER_PTIMER:
|
||||
ctx->cnt_ctl = read_sysreg_el0(SYS_CNTP_CTL);
|
||||
ctx->cnt_cval = read_sysreg_el0(SYS_CNTP_CVAL);
|
||||
timer_set_ctl(ctx, read_sysreg_el0(SYS_CNTP_CTL));
|
||||
timer_set_cval(ctx, read_sysreg_el0(SYS_CNTP_CVAL));
|
||||
|
||||
/* Disable the timer */
|
||||
write_sysreg_el0(0, SYS_CNTP_CTL);
|
||||
|
@ -429,14 +516,14 @@ static void timer_restore_state(struct arch_timer_context *ctx)
|
|||
|
||||
switch (index) {
|
||||
case TIMER_VTIMER:
|
||||
write_sysreg_el0(ctx->cnt_cval, SYS_CNTV_CVAL);
|
||||
write_sysreg_el0(timer_get_cval(ctx), SYS_CNTV_CVAL);
|
||||
isb();
|
||||
write_sysreg_el0(ctx->cnt_ctl, SYS_CNTV_CTL);
|
||||
write_sysreg_el0(timer_get_ctl(ctx), SYS_CNTV_CTL);
|
||||
break;
|
||||
case TIMER_PTIMER:
|
||||
write_sysreg_el0(ctx->cnt_cval, SYS_CNTP_CVAL);
|
||||
write_sysreg_el0(timer_get_cval(ctx), SYS_CNTP_CVAL);
|
||||
isb();
|
||||
write_sysreg_el0(ctx->cnt_ctl, SYS_CNTP_CTL);
|
||||
write_sysreg_el0(timer_get_ctl(ctx), SYS_CNTP_CTL);
|
||||
break;
|
||||
case NR_KVM_TIMERS:
|
||||
BUG();
|
||||
|
@ -528,7 +615,7 @@ void kvm_timer_vcpu_load(struct kvm_vcpu *vcpu)
|
|||
kvm_timer_vcpu_load_nogic(vcpu);
|
||||
}
|
||||
|
||||
set_cntvoff(map.direct_vtimer->cntvoff);
|
||||
set_cntvoff(timer_get_offset(map.direct_vtimer));
|
||||
|
||||
kvm_timer_unblocking(vcpu);
|
||||
|
||||
|
@ -615,7 +702,7 @@ static void unmask_vtimer_irq_user(struct kvm_vcpu *vcpu)
|
|||
}
|
||||
}
|
||||
|
||||
void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
|
||||
void kvm_timer_sync_user(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct arch_timer_cpu *timer = vcpu_timer(vcpu);
|
||||
|
||||
|
@ -639,8 +726,8 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu)
|
|||
* resets the timer to be disabled and unmasked and is compliant with
|
||||
* the ARMv7 architecture.
|
||||
*/
|
||||
vcpu_vtimer(vcpu)->cnt_ctl = 0;
|
||||
vcpu_ptimer(vcpu)->cnt_ctl = 0;
|
||||
timer_set_ctl(vcpu_vtimer(vcpu), 0);
|
||||
timer_set_ctl(vcpu_ptimer(vcpu), 0);
|
||||
|
||||
if (timer->enabled) {
|
||||
kvm_timer_update_irq(vcpu, false, vcpu_vtimer(vcpu));
|
||||
|
@ -668,13 +755,13 @@ static void update_vtimer_cntvoff(struct kvm_vcpu *vcpu, u64 cntvoff)
|
|||
|
||||
mutex_lock(&kvm->lock);
|
||||
kvm_for_each_vcpu(i, tmp, kvm)
|
||||
vcpu_vtimer(tmp)->cntvoff = cntvoff;
|
||||
timer_set_offset(vcpu_vtimer(tmp), cntvoff);
|
||||
|
||||
/*
|
||||
* When called from the vcpu create path, the CPU being created is not
|
||||
* included in the loop above, so we just set it here as well.
|
||||
*/
|
||||
vcpu_vtimer(vcpu)->cntvoff = cntvoff;
|
||||
timer_set_offset(vcpu_vtimer(vcpu), cntvoff);
|
||||
mutex_unlock(&kvm->lock);
|
||||
}
|
||||
|
||||
|
@ -684,9 +771,12 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu)
|
|||
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
|
||||
struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
|
||||
|
||||
vtimer->vcpu = vcpu;
|
||||
ptimer->vcpu = vcpu;
|
||||
|
||||
/* Synchronize cntvoff across all vtimers of a VM. */
|
||||
update_vtimer_cntvoff(vcpu, kvm_phys_timer_read());
|
||||
ptimer->cntvoff = 0;
|
||||
timer_set_offset(ptimer, 0);
|
||||
|
||||
hrtimer_init(&timer->bg_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
|
||||
timer->bg_timer.function = kvm_bg_timer_expire;
|
||||
|
@ -704,9 +794,6 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu)
|
|||
|
||||
vtimer->host_timer_irq_flags = host_vtimer_irq_flags;
|
||||
ptimer->host_timer_irq_flags = host_ptimer_irq_flags;
|
||||
|
||||
vtimer->vcpu = vcpu;
|
||||
ptimer->vcpu = vcpu;
|
||||
}
|
||||
|
||||
static void kvm_timer_init_interrupt(void *info)
|
||||
|
@ -756,10 +843,12 @@ static u64 read_timer_ctl(struct arch_timer_context *timer)
|
|||
* UNKNOWN when ENABLE bit is 0, so we chose to set ISTATUS bit
|
||||
* regardless of ENABLE bit for our implementation convenience.
|
||||
*/
|
||||
u32 ctl = timer_get_ctl(timer);
|
||||
|
||||
if (!kvm_timer_compute_delta(timer))
|
||||
return timer->cnt_ctl | ARCH_TIMER_CTRL_IT_STAT;
|
||||
else
|
||||
return timer->cnt_ctl;
|
||||
ctl |= ARCH_TIMER_CTRL_IT_STAT;
|
||||
|
||||
return ctl;
|
||||
}
|
||||
|
||||
u64 kvm_arm_timer_get_reg(struct kvm_vcpu *vcpu, u64 regid)
|
||||
|
@ -795,8 +884,8 @@ static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,
|
|||
|
||||
switch (treg) {
|
||||
case TIMER_REG_TVAL:
|
||||
val = timer->cnt_cval - kvm_phys_timer_read() + timer->cntvoff;
|
||||
val &= lower_32_bits(val);
|
||||
val = timer_get_cval(timer) - kvm_phys_timer_read() + timer_get_offset(timer);
|
||||
val = lower_32_bits(val);
|
||||
break;
|
||||
|
||||
case TIMER_REG_CTL:
|
||||
|
@ -804,11 +893,11 @@ static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,
|
|||
break;
|
||||
|
||||
case TIMER_REG_CVAL:
|
||||
val = timer->cnt_cval;
|
||||
val = timer_get_cval(timer);
|
||||
break;
|
||||
|
||||
case TIMER_REG_CNT:
|
||||
val = kvm_phys_timer_read() - timer->cntvoff;
|
||||
val = kvm_phys_timer_read() - timer_get_offset(timer);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -842,15 +931,15 @@ static void kvm_arm_timer_write(struct kvm_vcpu *vcpu,
|
|||
{
|
||||
switch (treg) {
|
||||
case TIMER_REG_TVAL:
|
||||
timer->cnt_cval = kvm_phys_timer_read() - timer->cntvoff + (s32)val;
|
||||
timer_set_cval(timer, kvm_phys_timer_read() - timer_get_offset(timer) + (s32)val);
|
||||
break;
|
||||
|
||||
case TIMER_REG_CTL:
|
||||
timer->cnt_ctl = val & ~ARCH_TIMER_CTRL_IT_STAT;
|
||||
timer_set_ctl(timer, val & ~ARCH_TIMER_CTRL_IT_STAT);
|
||||
break;
|
||||
|
||||
case TIMER_REG_CVAL:
|
||||
timer->cnt_cval = val;
|
||||
timer_set_cval(timer, val);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -106,22 +106,15 @@ static int kvm_arm_default_max_vcpus(void)
|
|||
*/
|
||||
int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
||||
{
|
||||
int ret, cpu;
|
||||
int ret;
|
||||
|
||||
ret = kvm_arm_setup_stage2(kvm, type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
kvm->arch.last_vcpu_ran = alloc_percpu(typeof(*kvm->arch.last_vcpu_ran));
|
||||
if (!kvm->arch.last_vcpu_ran)
|
||||
return -ENOMEM;
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
*per_cpu_ptr(kvm->arch.last_vcpu_ran, cpu) = -1;
|
||||
|
||||
ret = kvm_alloc_stage2_pgd(kvm);
|
||||
ret = kvm_init_stage2_mmu(kvm, &kvm->arch.mmu);
|
||||
if (ret)
|
||||
goto out_fail_alloc;
|
||||
return ret;
|
||||
|
||||
ret = create_hyp_mappings(kvm, kvm + 1, PAGE_HYP);
|
||||
if (ret)
|
||||
|
@ -129,18 +122,12 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
|||
|
||||
kvm_vgic_early_init(kvm);
|
||||
|
||||
/* Mark the initial VMID generation invalid */
|
||||
kvm->arch.vmid.vmid_gen = 0;
|
||||
|
||||
/* The maximum number of VCPUs is limited by the host's GIC model */
|
||||
kvm->arch.max_vcpus = kvm_arm_default_max_vcpus();
|
||||
|
||||
return ret;
|
||||
out_free_stage2_pgd:
|
||||
kvm_free_stage2_pgd(kvm);
|
||||
out_fail_alloc:
|
||||
free_percpu(kvm->arch.last_vcpu_ran);
|
||||
kvm->arch.last_vcpu_ran = NULL;
|
||||
kvm_free_stage2_pgd(&kvm->arch.mmu);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -160,9 +147,6 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
|
|||
|
||||
kvm_vgic_destroy(kvm);
|
||||
|
||||
free_percpu(kvm->arch.last_vcpu_ran);
|
||||
kvm->arch.last_vcpu_ran = NULL;
|
||||
|
||||
for (i = 0; i < KVM_MAX_VCPUS; ++i) {
|
||||
if (kvm->vcpus[i]) {
|
||||
kvm_vcpu_destroy(kvm->vcpus[i]);
|
||||
|
@ -281,6 +265,8 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
|
|||
|
||||
kvm_arm_pvtime_vcpu_init(&vcpu->arch);
|
||||
|
||||
vcpu->arch.hw_mmu = &vcpu->kvm->arch.mmu;
|
||||
|
||||
err = kvm_vgic_vcpu_init(vcpu);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -336,16 +322,18 @@ void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu)
|
|||
|
||||
void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
||||
{
|
||||
struct kvm_s2_mmu *mmu;
|
||||
int *last_ran;
|
||||
|
||||
last_ran = this_cpu_ptr(vcpu->kvm->arch.last_vcpu_ran);
|
||||
mmu = vcpu->arch.hw_mmu;
|
||||
last_ran = this_cpu_ptr(mmu->last_vcpu_ran);
|
||||
|
||||
/*
|
||||
* We might get preempted before the vCPU actually runs, but
|
||||
* over-invalidation doesn't affect correctness.
|
||||
*/
|
||||
if (*last_ran != vcpu->vcpu_id) {
|
||||
kvm_call_hyp(__kvm_tlb_flush_local_vmid, vcpu);
|
||||
kvm_call_hyp(__kvm_tlb_flush_local_vmid, mmu);
|
||||
*last_ran = vcpu->vcpu_id;
|
||||
}
|
||||
|
||||
|
@ -353,7 +341,8 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
|||
|
||||
kvm_vgic_load(vcpu);
|
||||
kvm_timer_vcpu_load(vcpu);
|
||||
kvm_vcpu_load_sysregs(vcpu);
|
||||
if (has_vhe())
|
||||
kvm_vcpu_load_sysregs_vhe(vcpu);
|
||||
kvm_arch_vcpu_load_fp(vcpu);
|
||||
kvm_vcpu_pmu_restore_guest(vcpu);
|
||||
if (kvm_arm_is_pvtime_enabled(&vcpu->arch))
|
||||
|
@ -371,7 +360,8 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
|||
void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
kvm_arch_vcpu_put_fp(vcpu);
|
||||
kvm_vcpu_put_sysregs(vcpu);
|
||||
if (has_vhe())
|
||||
kvm_vcpu_put_sysregs_vhe(vcpu);
|
||||
kvm_timer_vcpu_put(vcpu);
|
||||
kvm_vgic_put(vcpu);
|
||||
kvm_vcpu_pmu_restore_host(vcpu);
|
||||
|
@ -468,7 +458,6 @@ static bool need_new_vmid_gen(struct kvm_vmid *vmid)
|
|||
|
||||
/**
|
||||
* update_vmid - Update the vmid with a valid VMID for the current generation
|
||||
* @kvm: The guest that struct vmid belongs to
|
||||
* @vmid: The stage-2 VMID information struct
|
||||
*/
|
||||
static void update_vmid(struct kvm_vmid *vmid)
|
||||
|
@ -680,7 +669,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
|||
*/
|
||||
cond_resched();
|
||||
|
||||
update_vmid(&vcpu->kvm->arch.vmid);
|
||||
update_vmid(&vcpu->arch.hw_mmu->vmid);
|
||||
|
||||
check_vcpu_requests(vcpu);
|
||||
|
||||
|
@ -729,13 +718,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
|||
*/
|
||||
smp_store_mb(vcpu->mode, IN_GUEST_MODE);
|
||||
|
||||
if (ret <= 0 || need_new_vmid_gen(&vcpu->kvm->arch.vmid) ||
|
||||
if (ret <= 0 || need_new_vmid_gen(&vcpu->arch.hw_mmu->vmid) ||
|
||||
kvm_request_pending(vcpu)) {
|
||||
vcpu->mode = OUTSIDE_GUEST_MODE;
|
||||
isb(); /* Ensure work in x_flush_hwstate is committed */
|
||||
kvm_pmu_sync_hwstate(vcpu);
|
||||
if (static_branch_unlikely(&userspace_irqchip_in_use))
|
||||
kvm_timer_sync_hwstate(vcpu);
|
||||
kvm_timer_sync_user(vcpu);
|
||||
kvm_vgic_sync_hwstate(vcpu);
|
||||
local_irq_enable();
|
||||
preempt_enable();
|
||||
|
@ -750,11 +739,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
|||
trace_kvm_entry(*vcpu_pc(vcpu));
|
||||
guest_enter_irqoff();
|
||||
|
||||
if (has_vhe()) {
|
||||
ret = kvm_vcpu_run_vhe(vcpu);
|
||||
} else {
|
||||
ret = kvm_call_hyp_ret(__kvm_vcpu_run_nvhe, vcpu);
|
||||
}
|
||||
ret = kvm_call_hyp_ret(__kvm_vcpu_run, vcpu);
|
||||
|
||||
vcpu->mode = OUTSIDE_GUEST_MODE;
|
||||
vcpu->stat.exits++;
|
||||
|
@ -784,7 +769,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
|||
* timer virtual interrupt state.
|
||||
*/
|
||||
if (static_branch_unlikely(&userspace_irqchip_in_use))
|
||||
kvm_timer_sync_hwstate(vcpu);
|
||||
kvm_timer_sync_user(vcpu);
|
||||
|
||||
kvm_arch_vcpu_ctxsync_fp(vcpu);
|
||||
|
||||
|
@ -1287,7 +1272,7 @@ static void cpu_init_hyp_mode(void)
|
|||
* so that we can use adr_l to access per-cpu variables in EL2.
|
||||
*/
|
||||
tpidr_el2 = ((unsigned long)this_cpu_ptr(&kvm_host_data) -
|
||||
(unsigned long)kvm_ksym_ref(kvm_host_data));
|
||||
(unsigned long)kvm_ksym_ref(&kvm_host_data));
|
||||
|
||||
pgd_ptr = kvm_mmu_get_httbr();
|
||||
hyp_stack_ptr = __this_cpu_read(kvm_arm_hyp_stack_page) + PAGE_SIZE;
|
||||
|
@ -1308,7 +1293,7 @@ static void cpu_init_hyp_mode(void)
|
|||
*/
|
||||
if (this_cpu_has_cap(ARM64_SSBS) &&
|
||||
arm64_get_ssbd_state() == ARM64_SSBD_FORCE_DISABLE) {
|
||||
kvm_call_hyp(__kvm_enable_ssbs);
|
||||
kvm_call_hyp_nvhe(__kvm_enable_ssbs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu)
|
|||
WARN_ON_ONCE(!irqs_disabled());
|
||||
|
||||
if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED) {
|
||||
fpsimd_bind_state_to_cpu(&vcpu->arch.ctxt.gp_regs.fp_regs,
|
||||
fpsimd_bind_state_to_cpu(&vcpu->arch.ctxt.fp_regs,
|
||||
vcpu->arch.sve_state,
|
||||
vcpu->arch.sve_max_vl);
|
||||
|
||||
|
@ -109,12 +109,10 @@ void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu)
|
|||
local_irq_save(flags);
|
||||
|
||||
if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED) {
|
||||
u64 *guest_zcr = &vcpu->arch.ctxt.sys_regs[ZCR_EL1];
|
||||
|
||||
fpsimd_save_and_flush_cpu_state();
|
||||
|
||||
if (guest_has_sve)
|
||||
*guest_zcr = read_sysreg_s(SYS_ZCR_EL12);
|
||||
__vcpu_sys_reg(vcpu, ZCR_EL1) = read_sysreg_s(SYS_ZCR_EL12);
|
||||
} else if (host_has_sve) {
|
||||
/*
|
||||
* The FPSIMD/SVE state in the CPU has not been touched, and we
|
||||
|
|
|
@ -101,19 +101,69 @@ static int core_reg_size_from_offset(const struct kvm_vcpu *vcpu, u64 off)
|
|||
return size;
|
||||
}
|
||||
|
||||
static int validate_core_offset(const struct kvm_vcpu *vcpu,
|
||||
const struct kvm_one_reg *reg)
|
||||
static void *core_reg_addr(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||
{
|
||||
u64 off = core_reg_offset_from_id(reg->id);
|
||||
int size = core_reg_size_from_offset(vcpu, off);
|
||||
|
||||
if (size < 0)
|
||||
return -EINVAL;
|
||||
return NULL;
|
||||
|
||||
if (KVM_REG_SIZE(reg->id) != size)
|
||||
return -EINVAL;
|
||||
return NULL;
|
||||
|
||||
return 0;
|
||||
switch (off) {
|
||||
case KVM_REG_ARM_CORE_REG(regs.regs[0]) ...
|
||||
KVM_REG_ARM_CORE_REG(regs.regs[30]):
|
||||
off -= KVM_REG_ARM_CORE_REG(regs.regs[0]);
|
||||
off /= 2;
|
||||
return &vcpu->arch.ctxt.regs.regs[off];
|
||||
|
||||
case KVM_REG_ARM_CORE_REG(regs.sp):
|
||||
return &vcpu->arch.ctxt.regs.sp;
|
||||
|
||||
case KVM_REG_ARM_CORE_REG(regs.pc):
|
||||
return &vcpu->arch.ctxt.regs.pc;
|
||||
|
||||
case KVM_REG_ARM_CORE_REG(regs.pstate):
|
||||
return &vcpu->arch.ctxt.regs.pstate;
|
||||
|
||||
case KVM_REG_ARM_CORE_REG(sp_el1):
|
||||
return __ctxt_sys_reg(&vcpu->arch.ctxt, SP_EL1);
|
||||
|
||||
case KVM_REG_ARM_CORE_REG(elr_el1):
|
||||
return __ctxt_sys_reg(&vcpu->arch.ctxt, ELR_EL1);
|
||||
|
||||
case KVM_REG_ARM_CORE_REG(spsr[KVM_SPSR_EL1]):
|
||||
return __ctxt_sys_reg(&vcpu->arch.ctxt, SPSR_EL1);
|
||||
|
||||
case KVM_REG_ARM_CORE_REG(spsr[KVM_SPSR_ABT]):
|
||||
return &vcpu->arch.ctxt.spsr_abt;
|
||||
|
||||
case KVM_REG_ARM_CORE_REG(spsr[KVM_SPSR_UND]):
|
||||
return &vcpu->arch.ctxt.spsr_und;
|
||||
|
||||
case KVM_REG_ARM_CORE_REG(spsr[KVM_SPSR_IRQ]):
|
||||
return &vcpu->arch.ctxt.spsr_irq;
|
||||
|
||||
case KVM_REG_ARM_CORE_REG(spsr[KVM_SPSR_FIQ]):
|
||||
return &vcpu->arch.ctxt.spsr_fiq;
|
||||
|
||||
case KVM_REG_ARM_CORE_REG(fp_regs.vregs[0]) ...
|
||||
KVM_REG_ARM_CORE_REG(fp_regs.vregs[31]):
|
||||
off -= KVM_REG_ARM_CORE_REG(fp_regs.vregs[0]);
|
||||
off /= 4;
|
||||
return &vcpu->arch.ctxt.fp_regs.vregs[off];
|
||||
|
||||
case KVM_REG_ARM_CORE_REG(fp_regs.fpsr):
|
||||
return &vcpu->arch.ctxt.fp_regs.fpsr;
|
||||
|
||||
case KVM_REG_ARM_CORE_REG(fp_regs.fpcr):
|
||||
return &vcpu->arch.ctxt.fp_regs.fpcr;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||
|
@ -125,8 +175,8 @@ static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
|||
* off the index in the "array".
|
||||
*/
|
||||
__u32 __user *uaddr = (__u32 __user *)(unsigned long)reg->addr;
|
||||
struct kvm_regs *regs = vcpu_gp_regs(vcpu);
|
||||
int nr_regs = sizeof(*regs) / sizeof(__u32);
|
||||
int nr_regs = sizeof(struct kvm_regs) / sizeof(__u32);
|
||||
void *addr;
|
||||
u32 off;
|
||||
|
||||
/* Our ID is an index into the kvm_regs struct. */
|
||||
|
@ -135,10 +185,11 @@ static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
|||
(off + (KVM_REG_SIZE(reg->id) / sizeof(__u32))) >= nr_regs)
|
||||
return -ENOENT;
|
||||
|
||||
if (validate_core_offset(vcpu, reg))
|
||||
addr = core_reg_addr(vcpu, reg);
|
||||
if (!addr)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_to_user(uaddr, ((u32 *)regs) + off, KVM_REG_SIZE(reg->id)))
|
||||
if (copy_to_user(uaddr, addr, KVM_REG_SIZE(reg->id)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
|
@ -147,10 +198,9 @@ static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
|||
static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||
{
|
||||
__u32 __user *uaddr = (__u32 __user *)(unsigned long)reg->addr;
|
||||
struct kvm_regs *regs = vcpu_gp_regs(vcpu);
|
||||
int nr_regs = sizeof(*regs) / sizeof(__u32);
|
||||
int nr_regs = sizeof(struct kvm_regs) / sizeof(__u32);
|
||||
__uint128_t tmp;
|
||||
void *valp = &tmp;
|
||||
void *valp = &tmp, *addr;
|
||||
u64 off;
|
||||
int err = 0;
|
||||
|
||||
|
@ -160,7 +210,8 @@ static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
|||
(off + (KVM_REG_SIZE(reg->id) / sizeof(__u32))) >= nr_regs)
|
||||
return -ENOENT;
|
||||
|
||||
if (validate_core_offset(vcpu, reg))
|
||||
addr = core_reg_addr(vcpu, reg);
|
||||
if (!addr)
|
||||
return -EINVAL;
|
||||
|
||||
if (KVM_REG_SIZE(reg->id) > sizeof(tmp))
|
||||
|
@ -198,7 +249,7 @@ static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
|||
}
|
||||
}
|
||||
|
||||
memcpy((u32 *)regs + off, valp, KVM_REG_SIZE(reg->id));
|
||||
memcpy(addr, valp, KVM_REG_SIZE(reg->id));
|
||||
|
||||
if (*vcpu_cpsr(vcpu) & PSR_MODE32_BIT) {
|
||||
int i;
|
||||
|
|
|
@ -89,7 +89,7 @@ static int handle_no_fpsimd(struct kvm_vcpu *vcpu)
|
|||
*/
|
||||
static int kvm_handle_wfx(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (kvm_vcpu_get_hsr(vcpu) & ESR_ELx_WFx_ISS_WFE) {
|
||||
if (kvm_vcpu_get_esr(vcpu) & ESR_ELx_WFx_ISS_WFE) {
|
||||
trace_kvm_wfx_arm64(*vcpu_pc(vcpu), true);
|
||||
vcpu->stat.wfe_exit_stat++;
|
||||
kvm_vcpu_on_spin(vcpu, vcpu_mode_priv(vcpu));
|
||||
|
@ -119,13 +119,13 @@ static int kvm_handle_wfx(struct kvm_vcpu *vcpu)
|
|||
static int kvm_handle_guest_debug(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_run *run = vcpu->run;
|
||||
u32 hsr = kvm_vcpu_get_hsr(vcpu);
|
||||
u32 esr = kvm_vcpu_get_esr(vcpu);
|
||||
int ret = 0;
|
||||
|
||||
run->exit_reason = KVM_EXIT_DEBUG;
|
||||
run->debug.arch.hsr = hsr;
|
||||
run->debug.arch.hsr = esr;
|
||||
|
||||
switch (ESR_ELx_EC(hsr)) {
|
||||
switch (ESR_ELx_EC(esr)) {
|
||||
case ESR_ELx_EC_WATCHPT_LOW:
|
||||
run->debug.arch.far = vcpu->arch.fault.far_el2;
|
||||
/* fall through */
|
||||
|
@ -135,8 +135,8 @@ static int kvm_handle_guest_debug(struct kvm_vcpu *vcpu)
|
|||
case ESR_ELx_EC_BRK64:
|
||||
break;
|
||||
default:
|
||||
kvm_err("%s: un-handled case hsr: %#08x\n",
|
||||
__func__, (unsigned int) hsr);
|
||||
kvm_err("%s: un-handled case esr: %#08x\n",
|
||||
__func__, (unsigned int) esr);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
@ -146,10 +146,10 @@ static int kvm_handle_guest_debug(struct kvm_vcpu *vcpu)
|
|||
|
||||
static int kvm_handle_unknown_ec(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 hsr = kvm_vcpu_get_hsr(vcpu);
|
||||
u32 esr = kvm_vcpu_get_esr(vcpu);
|
||||
|
||||
kvm_pr_unimpl("Unknown exception class: hsr: %#08x -- %s\n",
|
||||
hsr, esr_get_class_string(hsr));
|
||||
kvm_pr_unimpl("Unknown exception class: esr: %#08x -- %s\n",
|
||||
esr, esr_get_class_string(esr));
|
||||
|
||||
kvm_inject_undefined(vcpu);
|
||||
return 1;
|
||||
|
@ -200,10 +200,10 @@ static exit_handle_fn arm_exit_handlers[] = {
|
|||
|
||||
static exit_handle_fn kvm_get_exit_handler(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 hsr = kvm_vcpu_get_hsr(vcpu);
|
||||
u8 hsr_ec = ESR_ELx_EC(hsr);
|
||||
u32 esr = kvm_vcpu_get_esr(vcpu);
|
||||
u8 esr_ec = ESR_ELx_EC(esr);
|
||||
|
||||
return arm_exit_handlers[hsr_ec];
|
||||
return arm_exit_handlers[esr_ec];
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -242,15 +242,15 @@ int handle_exit(struct kvm_vcpu *vcpu, int exception_index)
|
|||
struct kvm_run *run = vcpu->run;
|
||||
|
||||
if (ARM_SERROR_PENDING(exception_index)) {
|
||||
u8 hsr_ec = ESR_ELx_EC(kvm_vcpu_get_hsr(vcpu));
|
||||
u8 esr_ec = ESR_ELx_EC(kvm_vcpu_get_esr(vcpu));
|
||||
|
||||
/*
|
||||
* HVC/SMC already have an adjusted PC, which we need
|
||||
* to correct in order to return to after having
|
||||
* injected the SError.
|
||||
*/
|
||||
if (hsr_ec == ESR_ELx_EC_HVC32 || hsr_ec == ESR_ELx_EC_HVC64 ||
|
||||
hsr_ec == ESR_ELx_EC_SMC32 || hsr_ec == ESR_ELx_EC_SMC64) {
|
||||
if (esr_ec == ESR_ELx_EC_HVC32 || esr_ec == ESR_ELx_EC_HVC64 ||
|
||||
esr_ec == ESR_ELx_EC_SMC32 || esr_ec == ESR_ELx_EC_SMC64) {
|
||||
u32 adj = kvm_vcpu_trap_il_is32bit(vcpu) ? 4 : 2;
|
||||
*vcpu_pc(vcpu) -= adj;
|
||||
}
|
||||
|
@ -307,5 +307,5 @@ void handle_exit_early(struct kvm_vcpu *vcpu, int exception_index)
|
|||
exception_index = ARM_EXCEPTION_CODE(exception_index);
|
||||
|
||||
if (exception_index == ARM_EXCEPTION_EL1_SERROR)
|
||||
kvm_handle_guest_serror(vcpu, kvm_vcpu_get_hsr(vcpu));
|
||||
kvm_handle_guest_serror(vcpu, kvm_vcpu_get_esr(vcpu));
|
||||
}
|
||||
|
|
|
@ -3,18 +3,12 @@
|
|||
# Makefile for Kernel-based Virtual Machine module, HYP part
|
||||
#
|
||||
|
||||
ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING \
|
||||
$(DISABLE_STACKLEAK_PLUGIN)
|
||||
incdir := $(srctree)/$(src)/include
|
||||
subdir-asflags-y := -I$(incdir)
|
||||
subdir-ccflags-y := -I$(incdir) \
|
||||
-fno-stack-protector \
|
||||
-DDISABLE_BRANCH_PROFILING \
|
||||
$(DISABLE_STACKLEAK_PLUGIN)
|
||||
|
||||
obj-$(CONFIG_KVM) += hyp.o
|
||||
|
||||
hyp-y := vgic-v3-sr.o timer-sr.o aarch32.o vgic-v2-cpuif-proxy.o sysreg-sr.o \
|
||||
debug-sr.o entry.o switch.o fpsimd.o tlb.o hyp-entry.o
|
||||
|
||||
# KVM code is run at a different exception code with a different map, so
|
||||
# compiler instrumentation that inserts callbacks or checks into the code may
|
||||
# cause crashes. Just disable it.
|
||||
GCOV_PROFILE := n
|
||||
KASAN_SANITIZE := n
|
||||
UBSAN_SANITIZE := n
|
||||
KCOV_INSTRUMENT := n
|
||||
obj-$(CONFIG_KVM) += vhe/ nvhe/
|
||||
obj-$(CONFIG_KVM_INDIRECT_VECTORS) += smccc_wa.o
|
||||
|
|
|
@ -44,14 +44,14 @@ static const unsigned short cc_map[16] = {
|
|||
/*
|
||||
* Check if a trapped instruction should have been executed or not.
|
||||
*/
|
||||
bool __hyp_text kvm_condition_valid32(const struct kvm_vcpu *vcpu)
|
||||
bool kvm_condition_valid32(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long cpsr;
|
||||
u32 cpsr_cond;
|
||||
int cond;
|
||||
|
||||
/* Top two bits non-zero? Unconditional. */
|
||||
if (kvm_vcpu_get_hsr(vcpu) >> 30)
|
||||
if (kvm_vcpu_get_esr(vcpu) >> 30)
|
||||
return true;
|
||||
|
||||
/* Is condition field valid? */
|
||||
|
@ -93,7 +93,7 @@ bool __hyp_text kvm_condition_valid32(const struct kvm_vcpu *vcpu)
|
|||
*
|
||||
* IT[7:0] -> CPSR[26:25],CPSR[15:10]
|
||||
*/
|
||||
static void __hyp_text kvm_adjust_itstate(struct kvm_vcpu *vcpu)
|
||||
static void kvm_adjust_itstate(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long itbits, cond;
|
||||
unsigned long cpsr = *vcpu_cpsr(vcpu);
|
||||
|
@ -123,7 +123,7 @@ static void __hyp_text kvm_adjust_itstate(struct kvm_vcpu *vcpu)
|
|||
* kvm_skip_instr - skip a trapped instruction and proceed to the next
|
||||
* @vcpu: The vcpu pointer
|
||||
*/
|
||||
void __hyp_text kvm_skip_instr32(struct kvm_vcpu *vcpu, bool is_wide_instr)
|
||||
void kvm_skip_instr32(struct kvm_vcpu *vcpu, bool is_wide_instr)
|
||||
{
|
||||
u32 pc = *vcpu_pc(vcpu);
|
||||
bool is_thumb;
|
||||
|
|
|
@ -16,12 +16,10 @@
|
|||
#include <asm/kvm_mmu.h>
|
||||
#include <asm/kvm_ptrauth.h>
|
||||
|
||||
#define CPU_GP_REG_OFFSET(x) (CPU_GP_REGS + x)
|
||||
#define CPU_XREG_OFFSET(x) CPU_GP_REG_OFFSET(CPU_USER_PT_REGS + 8*x)
|
||||
#define CPU_XREG_OFFSET(x) (CPU_USER_PT_REGS + 8*x)
|
||||
#define CPU_SP_EL0_OFFSET (CPU_XREG_OFFSET(30) + 8)
|
||||
|
||||
.text
|
||||
.pushsection .hyp.text, "ax"
|
||||
|
||||
/*
|
||||
* We treat x18 as callee-saved as the host may use it as a platform
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <asm/fpsimdmacros.h>
|
||||
|
||||
.text
|
||||
.pushsection .hyp.text, "ax"
|
||||
|
||||
SYM_FUNC_START(__fpsimd_save_state)
|
||||
fpsimd_save x0, 1
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <asm/mmu.h>
|
||||
|
||||
.text
|
||||
.pushsection .hyp.text, "ax"
|
||||
|
||||
.macro do_el2_call
|
||||
/*
|
||||
|
@ -40,6 +39,7 @@ el1_sync: // Guest trapped into EL2
|
|||
ccmp x0, #ESR_ELx_EC_HVC32, #4, ne
|
||||
b.ne el1_trap
|
||||
|
||||
#ifdef __KVM_NVHE_HYPERVISOR__
|
||||
mrs x1, vttbr_el2 // If vttbr is valid, the guest
|
||||
cbnz x1, el1_hvc_guest // called HVC
|
||||
|
||||
|
@ -74,6 +74,7 @@ el1_sync: // Guest trapped into EL2
|
|||
|
||||
eret
|
||||
sb
|
||||
#endif /* __KVM_NVHE_HYPERVISOR__ */
|
||||
|
||||
el1_hvc_guest:
|
||||
/*
|
||||
|
@ -180,6 +181,7 @@ el2_error:
|
|||
eret
|
||||
sb
|
||||
|
||||
#ifdef __KVM_NVHE_HYPERVISOR__
|
||||
SYM_FUNC_START(__hyp_do_panic)
|
||||
mov lr, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
|
||||
PSR_MODE_EL1h)
|
||||
|
@ -189,6 +191,7 @@ SYM_FUNC_START(__hyp_do_panic)
|
|||
eret
|
||||
sb
|
||||
SYM_FUNC_END(__hyp_do_panic)
|
||||
#endif
|
||||
|
||||
SYM_CODE_START(__hyp_panic)
|
||||
get_host_ctxt x0, x1
|
||||
|
@ -318,20 +321,4 @@ SYM_CODE_START(__bp_harden_hyp_vecs)
|
|||
1: .org __bp_harden_hyp_vecs + __BP_HARDEN_HYP_VECS_SZ
|
||||
.org 1b
|
||||
SYM_CODE_END(__bp_harden_hyp_vecs)
|
||||
|
||||
.popsection
|
||||
|
||||
SYM_CODE_START(__smccc_workaround_1_smc)
|
||||
esb
|
||||
sub sp, sp, #(8 * 4)
|
||||
stp x2, x3, [sp, #(8 * 0)]
|
||||
stp x0, x1, [sp, #(8 * 2)]
|
||||
mov w0, #ARM_SMCCC_ARCH_WORKAROUND_1
|
||||
smc #0
|
||||
ldp x2, x3, [sp, #(8 * 0)]
|
||||
ldp x0, x1, [sp, #(8 * 2)]
|
||||
add sp, sp, #(8 * 4)
|
||||
1: .org __smccc_workaround_1_smc + __SMCCC_WORKAROUND_1_SMC_SZ
|
||||
.org 1b
|
||||
SYM_CODE_END(__smccc_workaround_1_smc)
|
||||
#endif
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#ifndef __ARM64_KVM_HYP_DEBUG_SR_H__
|
||||
#define __ARM64_KVM_HYP_DEBUG_SR_H__
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
|
@ -85,53 +88,8 @@
|
|||
default: write_debug(ptr[0], reg, 0); \
|
||||
}
|
||||
|
||||
static void __hyp_text __debug_save_spe_nvhe(u64 *pmscr_el1)
|
||||
{
|
||||
u64 reg;
|
||||
|
||||
/* Clear pmscr in case of early return */
|
||||
*pmscr_el1 = 0;
|
||||
|
||||
/* SPE present on this CPU? */
|
||||
if (!cpuid_feature_extract_unsigned_field(read_sysreg(id_aa64dfr0_el1),
|
||||
ID_AA64DFR0_PMSVER_SHIFT))
|
||||
return;
|
||||
|
||||
/* Yes; is it owned by EL3? */
|
||||
reg = read_sysreg_s(SYS_PMBIDR_EL1);
|
||||
if (reg & BIT(SYS_PMBIDR_EL1_P_SHIFT))
|
||||
return;
|
||||
|
||||
/* No; is the host actually using the thing? */
|
||||
reg = read_sysreg_s(SYS_PMBLIMITR_EL1);
|
||||
if (!(reg & BIT(SYS_PMBLIMITR_EL1_E_SHIFT)))
|
||||
return;
|
||||
|
||||
/* Yes; save the control register and disable data generation */
|
||||
*pmscr_el1 = read_sysreg_s(SYS_PMSCR_EL1);
|
||||
write_sysreg_s(0, SYS_PMSCR_EL1);
|
||||
isb();
|
||||
|
||||
/* Now drain all buffered data to memory */
|
||||
psb_csync();
|
||||
dsb(nsh);
|
||||
}
|
||||
|
||||
static void __hyp_text __debug_restore_spe_nvhe(u64 pmscr_el1)
|
||||
{
|
||||
if (!pmscr_el1)
|
||||
return;
|
||||
|
||||
/* The host page table is installed, but not yet synchronised */
|
||||
isb();
|
||||
|
||||
/* Re-enable data generation */
|
||||
write_sysreg_s(pmscr_el1, SYS_PMSCR_EL1);
|
||||
}
|
||||
|
||||
static void __hyp_text __debug_save_state(struct kvm_vcpu *vcpu,
|
||||
struct kvm_guest_debug_arch *dbg,
|
||||
struct kvm_cpu_context *ctxt)
|
||||
static void __debug_save_state(struct kvm_guest_debug_arch *dbg,
|
||||
struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
u64 aa64dfr0;
|
||||
int brps, wrps;
|
||||
|
@ -145,12 +103,11 @@ static void __hyp_text __debug_save_state(struct kvm_vcpu *vcpu,
|
|||
save_debug(dbg->dbg_wcr, dbgwcr, wrps);
|
||||
save_debug(dbg->dbg_wvr, dbgwvr, wrps);
|
||||
|
||||
ctxt->sys_regs[MDCCINT_EL1] = read_sysreg(mdccint_el1);
|
||||
ctxt_sys_reg(ctxt, MDCCINT_EL1) = read_sysreg(mdccint_el1);
|
||||
}
|
||||
|
||||
static void __hyp_text __debug_restore_state(struct kvm_vcpu *vcpu,
|
||||
struct kvm_guest_debug_arch *dbg,
|
||||
struct kvm_cpu_context *ctxt)
|
||||
static void __debug_restore_state(struct kvm_guest_debug_arch *dbg,
|
||||
struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
u64 aa64dfr0;
|
||||
int brps, wrps;
|
||||
|
@ -165,23 +122,16 @@ static void __hyp_text __debug_restore_state(struct kvm_vcpu *vcpu,
|
|||
restore_debug(dbg->dbg_wcr, dbgwcr, wrps);
|
||||
restore_debug(dbg->dbg_wvr, dbgwvr, wrps);
|
||||
|
||||
write_sysreg(ctxt->sys_regs[MDCCINT_EL1], mdccint_el1);
|
||||
write_sysreg(ctxt_sys_reg(ctxt, MDCCINT_EL1), mdccint_el1);
|
||||
}
|
||||
|
||||
void __hyp_text __debug_switch_to_guest(struct kvm_vcpu *vcpu)
|
||||
static inline void __debug_switch_to_guest_common(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_cpu_context *host_ctxt;
|
||||
struct kvm_cpu_context *guest_ctxt;
|
||||
struct kvm_guest_debug_arch *host_dbg;
|
||||
struct kvm_guest_debug_arch *guest_dbg;
|
||||
|
||||
/*
|
||||
* Non-VHE: Disable and flush SPE data generation
|
||||
* VHE: The vcpu can run, but it can't hide.
|
||||
*/
|
||||
if (!has_vhe())
|
||||
__debug_save_spe_nvhe(&vcpu->arch.host_debug_state.pmscr_el1);
|
||||
|
||||
if (!(vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY))
|
||||
return;
|
||||
|
||||
|
@ -190,20 +140,17 @@ void __hyp_text __debug_switch_to_guest(struct kvm_vcpu *vcpu)
|
|||
host_dbg = &vcpu->arch.host_debug_state.regs;
|
||||
guest_dbg = kern_hyp_va(vcpu->arch.debug_ptr);
|
||||
|
||||
__debug_save_state(vcpu, host_dbg, host_ctxt);
|
||||
__debug_restore_state(vcpu, guest_dbg, guest_ctxt);
|
||||
__debug_save_state(host_dbg, host_ctxt);
|
||||
__debug_restore_state(guest_dbg, guest_ctxt);
|
||||
}
|
||||
|
||||
void __hyp_text __debug_switch_to_host(struct kvm_vcpu *vcpu)
|
||||
static inline void __debug_switch_to_host_common(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_cpu_context *host_ctxt;
|
||||
struct kvm_cpu_context *guest_ctxt;
|
||||
struct kvm_guest_debug_arch *host_dbg;
|
||||
struct kvm_guest_debug_arch *guest_dbg;
|
||||
|
||||
if (!has_vhe())
|
||||
__debug_restore_spe_nvhe(vcpu->arch.host_debug_state.pmscr_el1);
|
||||
|
||||
if (!(vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY))
|
||||
return;
|
||||
|
||||
|
@ -212,13 +159,10 @@ void __hyp_text __debug_switch_to_host(struct kvm_vcpu *vcpu)
|
|||
host_dbg = &vcpu->arch.host_debug_state.regs;
|
||||
guest_dbg = kern_hyp_va(vcpu->arch.debug_ptr);
|
||||
|
||||
__debug_save_state(vcpu, guest_dbg, guest_ctxt);
|
||||
__debug_restore_state(vcpu, host_dbg, host_ctxt);
|
||||
__debug_save_state(guest_dbg, guest_ctxt);
|
||||
__debug_restore_state(host_dbg, host_ctxt);
|
||||
|
||||
vcpu->arch.flags &= ~KVM_ARM64_DEBUG_DIRTY;
|
||||
}
|
||||
|
||||
u32 __hyp_text __kvm_get_mdcr_el2(void)
|
||||
{
|
||||
return read_sysreg(mdcr_el2);
|
||||
}
|
||||
#endif /* __ARM64_KVM_HYP_DEBUG_SR_H__ */
|
|
@ -0,0 +1,511 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#ifndef __ARM64_KVM_HYP_SWITCH_H__
|
||||
#define __ARM64_KVM_HYP_SWITCH_H__
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <uapi/linux/psci.h>
|
||||
|
||||
#include <kvm/arm_psci.h>
|
||||
|
||||
#include <asm/barrier.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/kprobes.h>
|
||||
#include <asm/kvm_asm.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_hyp.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
#include <asm/fpsimd.h>
|
||||
#include <asm/debug-monitors.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/thread_info.h>
|
||||
|
||||
extern const char __hyp_panic_string[];
|
||||
|
||||
/* Check whether the FP regs were dirtied while in the host-side run loop: */
|
||||
static inline bool update_fp_enabled(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* When the system doesn't support FP/SIMD, we cannot rely on
|
||||
* the _TIF_FOREIGN_FPSTATE flag. However, we always inject an
|
||||
* abort on the very first access to FP and thus we should never
|
||||
* see KVM_ARM64_FP_ENABLED. For added safety, make sure we always
|
||||
* trap the accesses.
|
||||
*/
|
||||
if (!system_supports_fpsimd() ||
|
||||
vcpu->arch.host_thread_info->flags & _TIF_FOREIGN_FPSTATE)
|
||||
vcpu->arch.flags &= ~(KVM_ARM64_FP_ENABLED |
|
||||
KVM_ARM64_FP_HOST);
|
||||
|
||||
return !!(vcpu->arch.flags & KVM_ARM64_FP_ENABLED);
|
||||
}
|
||||
|
||||
/* Save the 32-bit only FPSIMD system register state */
|
||||
static inline void __fpsimd_save_fpexc32(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (!vcpu_el1_is_32bit(vcpu))
|
||||
return;
|
||||
|
||||
__vcpu_sys_reg(vcpu, FPEXC32_EL2) = read_sysreg(fpexc32_el2);
|
||||
}
|
||||
|
||||
static inline void __activate_traps_fpsimd32(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* We are about to set CPTR_EL2.TFP to trap all floating point
|
||||
* register accesses to EL2, however, the ARM ARM clearly states that
|
||||
* traps are only taken to EL2 if the operation would not otherwise
|
||||
* trap to EL1. Therefore, always make sure that for 32-bit guests,
|
||||
* we set FPEXC.EN to prevent traps to EL1, when setting the TFP bit.
|
||||
* If FP/ASIMD is not implemented, FPEXC is UNDEFINED and any access to
|
||||
* it will cause an exception.
|
||||
*/
|
||||
if (vcpu_el1_is_32bit(vcpu) && system_supports_fpsimd()) {
|
||||
write_sysreg(1 << 30, fpexc32_el2);
|
||||
isb();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void __activate_traps_common(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/* Trap on AArch32 cp15 c15 (impdef sysregs) accesses (EL1 or EL0) */
|
||||
write_sysreg(1 << 15, hstr_el2);
|
||||
|
||||
/*
|
||||
* Make sure we trap PMU access from EL0 to EL2. Also sanitize
|
||||
* PMSELR_EL0 to make sure it never contains the cycle
|
||||
* counter, which could make a PMXEVCNTR_EL0 access UNDEF at
|
||||
* EL1 instead of being trapped to EL2.
|
||||
*/
|
||||
write_sysreg(0, pmselr_el0);
|
||||
write_sysreg(ARMV8_PMU_USERENR_MASK, pmuserenr_el0);
|
||||
write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2);
|
||||
}
|
||||
|
||||
static inline void __deactivate_traps_common(void)
|
||||
{
|
||||
write_sysreg(0, hstr_el2);
|
||||
write_sysreg(0, pmuserenr_el0);
|
||||
}
|
||||
|
||||
static inline void ___activate_traps(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 hcr = vcpu->arch.hcr_el2;
|
||||
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_CAVIUM_TX2_219_TVM))
|
||||
hcr |= HCR_TVM;
|
||||
|
||||
write_sysreg(hcr, hcr_el2);
|
||||
|
||||
if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN) && (hcr & HCR_VSE))
|
||||
write_sysreg_s(vcpu->arch.vsesr_el2, SYS_VSESR_EL2);
|
||||
}
|
||||
|
||||
static inline void ___deactivate_traps(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* If we pended a virtual abort, preserve it until it gets
|
||||
* cleared. See D1.14.3 (Virtual Interrupts) for details, but
|
||||
* the crucial bit is "On taking a vSError interrupt,
|
||||
* HCR_EL2.VSE is cleared to 0."
|
||||
*/
|
||||
if (vcpu->arch.hcr_el2 & HCR_VSE) {
|
||||
vcpu->arch.hcr_el2 &= ~HCR_VSE;
|
||||
vcpu->arch.hcr_el2 |= read_sysreg(hcr_el2) & HCR_VSE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void __activate_vm(struct kvm_s2_mmu *mmu)
|
||||
{
|
||||
__load_guest_stage2(mmu);
|
||||
}
|
||||
|
||||
static inline bool __translate_far_to_hpfar(u64 far, u64 *hpfar)
|
||||
{
|
||||
u64 par, tmp;
|
||||
|
||||
/*
|
||||
* Resolve the IPA the hard way using the guest VA.
|
||||
*
|
||||
* Stage-1 translation already validated the memory access
|
||||
* rights. As such, we can use the EL1 translation regime, and
|
||||
* don't have to distinguish between EL0 and EL1 access.
|
||||
*
|
||||
* We do need to save/restore PAR_EL1 though, as we haven't
|
||||
* saved the guest context yet, and we may return early...
|
||||
*/
|
||||
par = read_sysreg(par_el1);
|
||||
asm volatile("at s1e1r, %0" : : "r" (far));
|
||||
isb();
|
||||
|
||||
tmp = read_sysreg(par_el1);
|
||||
write_sysreg(par, par_el1);
|
||||
|
||||
if (unlikely(tmp & SYS_PAR_EL1_F))
|
||||
return false; /* Translation failed, back to guest */
|
||||
|
||||
/* Convert PAR to HPFAR format */
|
||||
*hpfar = PAR_TO_HPFAR(tmp);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool __populate_fault_info(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u8 ec;
|
||||
u64 esr;
|
||||
u64 hpfar, far;
|
||||
|
||||
esr = vcpu->arch.fault.esr_el2;
|
||||
ec = ESR_ELx_EC(esr);
|
||||
|
||||
if (ec != ESR_ELx_EC_DABT_LOW && ec != ESR_ELx_EC_IABT_LOW)
|
||||
return true;
|
||||
|
||||
far = read_sysreg_el2(SYS_FAR);
|
||||
|
||||
/*
|
||||
* The HPFAR can be invalid if the stage 2 fault did not
|
||||
* happen during a stage 1 page table walk (the ESR_EL2.S1PTW
|
||||
* bit is clear) and one of the two following cases are true:
|
||||
* 1. The fault was due to a permission fault
|
||||
* 2. The processor carries errata 834220
|
||||
*
|
||||
* Therefore, for all non S1PTW faults where we either have a
|
||||
* permission fault or the errata workaround is enabled, we
|
||||
* resolve the IPA using the AT instruction.
|
||||
*/
|
||||
if (!(esr & ESR_ELx_S1PTW) &&
|
||||
(cpus_have_final_cap(ARM64_WORKAROUND_834220) ||
|
||||
(esr & ESR_ELx_FSC_TYPE) == FSC_PERM)) {
|
||||
if (!__translate_far_to_hpfar(far, &hpfar))
|
||||
return false;
|
||||
} else {
|
||||
hpfar = read_sysreg(hpfar_el2);
|
||||
}
|
||||
|
||||
vcpu->arch.fault.far_el2 = far;
|
||||
vcpu->arch.fault.hpfar_el2 = hpfar;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check for an FPSIMD/SVE trap and handle as appropriate */
|
||||
static inline bool __hyp_handle_fpsimd(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
bool vhe, sve_guest, sve_host;
|
||||
u8 esr_ec;
|
||||
|
||||
if (!system_supports_fpsimd())
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Currently system_supports_sve() currently implies has_vhe(),
|
||||
* so the check is redundant. However, has_vhe() can be determined
|
||||
* statically and helps the compiler remove dead code.
|
||||
*/
|
||||
if (has_vhe() && system_supports_sve()) {
|
||||
sve_guest = vcpu_has_sve(vcpu);
|
||||
sve_host = vcpu->arch.flags & KVM_ARM64_HOST_SVE_IN_USE;
|
||||
vhe = true;
|
||||
} else {
|
||||
sve_guest = false;
|
||||
sve_host = false;
|
||||
vhe = has_vhe();
|
||||
}
|
||||
|
||||
esr_ec = kvm_vcpu_trap_get_class(vcpu);
|
||||
if (esr_ec != ESR_ELx_EC_FP_ASIMD &&
|
||||
esr_ec != ESR_ELx_EC_SVE)
|
||||
return false;
|
||||
|
||||
/* Don't handle SVE traps for non-SVE vcpus here: */
|
||||
if (!sve_guest)
|
||||
if (esr_ec != ESR_ELx_EC_FP_ASIMD)
|
||||
return false;
|
||||
|
||||
/* Valid trap. Switch the context: */
|
||||
|
||||
if (vhe) {
|
||||
u64 reg = read_sysreg(cpacr_el1) | CPACR_EL1_FPEN;
|
||||
|
||||
if (sve_guest)
|
||||
reg |= CPACR_EL1_ZEN;
|
||||
|
||||
write_sysreg(reg, cpacr_el1);
|
||||
} else {
|
||||
write_sysreg(read_sysreg(cptr_el2) & ~(u64)CPTR_EL2_TFP,
|
||||
cptr_el2);
|
||||
}
|
||||
|
||||
isb();
|
||||
|
||||
if (vcpu->arch.flags & KVM_ARM64_FP_HOST) {
|
||||
/*
|
||||
* In the SVE case, VHE is assumed: it is enforced by
|
||||
* Kconfig and kvm_arch_init().
|
||||
*/
|
||||
if (sve_host) {
|
||||
struct thread_struct *thread = container_of(
|
||||
vcpu->arch.host_fpsimd_state,
|
||||
struct thread_struct, uw.fpsimd_state);
|
||||
|
||||
sve_save_state(sve_pffr(thread),
|
||||
&vcpu->arch.host_fpsimd_state->fpsr);
|
||||
} else {
|
||||
__fpsimd_save_state(vcpu->arch.host_fpsimd_state);
|
||||
}
|
||||
|
||||
vcpu->arch.flags &= ~KVM_ARM64_FP_HOST;
|
||||
}
|
||||
|
||||
if (sve_guest) {
|
||||
sve_load_state(vcpu_sve_pffr(vcpu),
|
||||
&vcpu->arch.ctxt.fp_regs.fpsr,
|
||||
sve_vq_from_vl(vcpu->arch.sve_max_vl) - 1);
|
||||
write_sysreg_s(__vcpu_sys_reg(vcpu, ZCR_EL1), SYS_ZCR_EL12);
|
||||
} else {
|
||||
__fpsimd_restore_state(&vcpu->arch.ctxt.fp_regs);
|
||||
}
|
||||
|
||||
/* Skip restoring fpexc32 for AArch64 guests */
|
||||
if (!(read_sysreg(hcr_el2) & HCR_RW))
|
||||
write_sysreg(__vcpu_sys_reg(vcpu, FPEXC32_EL2), fpexc32_el2);
|
||||
|
||||
vcpu->arch.flags |= KVM_ARM64_FP_ENABLED;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool handle_tx2_tvm(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 sysreg = esr_sys64_to_sysreg(kvm_vcpu_get_esr(vcpu));
|
||||
int rt = kvm_vcpu_sys_get_rt(vcpu);
|
||||
u64 val = vcpu_get_reg(vcpu, rt);
|
||||
|
||||
/*
|
||||
* The normal sysreg handling code expects to see the traps,
|
||||
* let's not do anything here.
|
||||
*/
|
||||
if (vcpu->arch.hcr_el2 & HCR_TVM)
|
||||
return false;
|
||||
|
||||
switch (sysreg) {
|
||||
case SYS_SCTLR_EL1:
|
||||
write_sysreg_el1(val, SYS_SCTLR);
|
||||
break;
|
||||
case SYS_TTBR0_EL1:
|
||||
write_sysreg_el1(val, SYS_TTBR0);
|
||||
break;
|
||||
case SYS_TTBR1_EL1:
|
||||
write_sysreg_el1(val, SYS_TTBR1);
|
||||
break;
|
||||
case SYS_TCR_EL1:
|
||||
write_sysreg_el1(val, SYS_TCR);
|
||||
break;
|
||||
case SYS_ESR_EL1:
|
||||
write_sysreg_el1(val, SYS_ESR);
|
||||
break;
|
||||
case SYS_FAR_EL1:
|
||||
write_sysreg_el1(val, SYS_FAR);
|
||||
break;
|
||||
case SYS_AFSR0_EL1:
|
||||
write_sysreg_el1(val, SYS_AFSR0);
|
||||
break;
|
||||
case SYS_AFSR1_EL1:
|
||||
write_sysreg_el1(val, SYS_AFSR1);
|
||||
break;
|
||||
case SYS_MAIR_EL1:
|
||||
write_sysreg_el1(val, SYS_MAIR);
|
||||
break;
|
||||
case SYS_AMAIR_EL1:
|
||||
write_sysreg_el1(val, SYS_AMAIR);
|
||||
break;
|
||||
case SYS_CONTEXTIDR_EL1:
|
||||
write_sysreg_el1(val, SYS_CONTEXTIDR);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
__kvm_skip_instr(vcpu);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool esr_is_ptrauth_trap(u32 esr)
|
||||
{
|
||||
u32 ec = ESR_ELx_EC(esr);
|
||||
|
||||
if (ec == ESR_ELx_EC_PAC)
|
||||
return true;
|
||||
|
||||
if (ec != ESR_ELx_EC_SYS64)
|
||||
return false;
|
||||
|
||||
switch (esr_sys64_to_sysreg(esr)) {
|
||||
case SYS_APIAKEYLO_EL1:
|
||||
case SYS_APIAKEYHI_EL1:
|
||||
case SYS_APIBKEYLO_EL1:
|
||||
case SYS_APIBKEYHI_EL1:
|
||||
case SYS_APDAKEYLO_EL1:
|
||||
case SYS_APDAKEYHI_EL1:
|
||||
case SYS_APDBKEYLO_EL1:
|
||||
case SYS_APDBKEYHI_EL1:
|
||||
case SYS_APGAKEYLO_EL1:
|
||||
case SYS_APGAKEYHI_EL1:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#define __ptrauth_save_key(ctxt, key) \
|
||||
do { \
|
||||
u64 __val; \
|
||||
__val = read_sysreg_s(SYS_ ## key ## KEYLO_EL1); \
|
||||
ctxt_sys_reg(ctxt, key ## KEYLO_EL1) = __val; \
|
||||
__val = read_sysreg_s(SYS_ ## key ## KEYHI_EL1); \
|
||||
ctxt_sys_reg(ctxt, key ## KEYHI_EL1) = __val; \
|
||||
} while(0)
|
||||
|
||||
static inline bool __hyp_handle_ptrauth(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_cpu_context *ctxt;
|
||||
u64 val;
|
||||
|
||||
if (!vcpu_has_ptrauth(vcpu) ||
|
||||
!esr_is_ptrauth_trap(kvm_vcpu_get_esr(vcpu)))
|
||||
return false;
|
||||
|
||||
ctxt = &__hyp_this_cpu_ptr(kvm_host_data)->host_ctxt;
|
||||
__ptrauth_save_key(ctxt, APIA);
|
||||
__ptrauth_save_key(ctxt, APIB);
|
||||
__ptrauth_save_key(ctxt, APDA);
|
||||
__ptrauth_save_key(ctxt, APDB);
|
||||
__ptrauth_save_key(ctxt, APGA);
|
||||
|
||||
vcpu_ptrauth_enable(vcpu);
|
||||
|
||||
val = read_sysreg(hcr_el2);
|
||||
val |= (HCR_API | HCR_APK);
|
||||
write_sysreg(val, hcr_el2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true when we were able to fixup the guest exit and should return to
|
||||
* the guest, false when we should restore the host state and return to the
|
||||
* main run loop.
|
||||
*/
|
||||
static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
{
|
||||
if (ARM_EXCEPTION_CODE(*exit_code) != ARM_EXCEPTION_IRQ)
|
||||
vcpu->arch.fault.esr_el2 = read_sysreg_el2(SYS_ESR);
|
||||
|
||||
/*
|
||||
* We're using the raw exception code in order to only process
|
||||
* the trap if no SError is pending. We will come back to the
|
||||
* same PC once the SError has been injected, and replay the
|
||||
* trapping instruction.
|
||||
*/
|
||||
if (*exit_code != ARM_EXCEPTION_TRAP)
|
||||
goto exit;
|
||||
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_CAVIUM_TX2_219_TVM) &&
|
||||
kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_SYS64 &&
|
||||
handle_tx2_tvm(vcpu))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* We trap the first access to the FP/SIMD to save the host context
|
||||
* and restore the guest context lazily.
|
||||
* If FP/SIMD is not implemented, handle the trap and inject an
|
||||
* undefined instruction exception to the guest.
|
||||
* Similarly for trapped SVE accesses.
|
||||
*/
|
||||
if (__hyp_handle_fpsimd(vcpu))
|
||||
return true;
|
||||
|
||||
if (__hyp_handle_ptrauth(vcpu))
|
||||
return true;
|
||||
|
||||
if (!__populate_fault_info(vcpu))
|
||||
return true;
|
||||
|
||||
if (static_branch_unlikely(&vgic_v2_cpuif_trap)) {
|
||||
bool valid;
|
||||
|
||||
valid = kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_DABT_LOW &&
|
||||
kvm_vcpu_trap_get_fault_type(vcpu) == FSC_FAULT &&
|
||||
kvm_vcpu_dabt_isvalid(vcpu) &&
|
||||
!kvm_vcpu_abt_issea(vcpu) &&
|
||||
!kvm_vcpu_dabt_iss1tw(vcpu);
|
||||
|
||||
if (valid) {
|
||||
int ret = __vgic_v2_perform_cpuif_access(vcpu);
|
||||
|
||||
if (ret == 1)
|
||||
return true;
|
||||
|
||||
/* Promote an illegal access to an SError.*/
|
||||
if (ret == -1)
|
||||
*exit_code = ARM_EXCEPTION_EL1_SERROR;
|
||||
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (static_branch_unlikely(&vgic_v3_cpuif_trap) &&
|
||||
(kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_SYS64 ||
|
||||
kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_CP15_32)) {
|
||||
int ret = __vgic_v3_perform_cpuif_access(vcpu);
|
||||
|
||||
if (ret == 1)
|
||||
return true;
|
||||
}
|
||||
|
||||
exit:
|
||||
/* Return to the host kernel and handle the exit */
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool __needs_ssbd_off(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (!cpus_have_final_cap(ARM64_SSBD))
|
||||
return false;
|
||||
|
||||
return !(vcpu->arch.workaround_flags & VCPU_WORKAROUND_2_FLAG);
|
||||
}
|
||||
|
||||
static inline void __set_guest_arch_workaround_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
#ifdef CONFIG_ARM64_SSBD
|
||||
/*
|
||||
* The host runs with the workaround always present. If the
|
||||
* guest wants it disabled, so be it...
|
||||
*/
|
||||
if (__needs_ssbd_off(vcpu) &&
|
||||
__hyp_this_cpu_read(arm64_ssbd_callback_required))
|
||||
arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_2, 0, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __set_host_arch_workaround_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
#ifdef CONFIG_ARM64_SSBD
|
||||
/*
|
||||
* If the guest has disabled the workaround, bring it back on.
|
||||
*/
|
||||
if (__needs_ssbd_off(vcpu) &&
|
||||
__hyp_this_cpu_read(arm64_ssbd_callback_required))
|
||||
arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_2, 1, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* __ARM64_KVM_HYP_SWITCH_H__ */
|
|
@ -0,0 +1,193 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2012-2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#ifndef __ARM64_KVM_HYP_SYSREG_SR_H__
|
||||
#define __ARM64_KVM_HYP_SYSREG_SR_H__
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include <asm/kprobes.h>
|
||||
#include <asm/kvm_asm.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_hyp.h>
|
||||
|
||||
static inline void __sysreg_save_common_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
ctxt_sys_reg(ctxt, MDSCR_EL1) = read_sysreg(mdscr_el1);
|
||||
}
|
||||
|
||||
static inline void __sysreg_save_user_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
ctxt_sys_reg(ctxt, TPIDR_EL0) = read_sysreg(tpidr_el0);
|
||||
ctxt_sys_reg(ctxt, TPIDRRO_EL0) = read_sysreg(tpidrro_el0);
|
||||
}
|
||||
|
||||
static inline void __sysreg_save_el1_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
ctxt_sys_reg(ctxt, CSSELR_EL1) = read_sysreg(csselr_el1);
|
||||
ctxt_sys_reg(ctxt, SCTLR_EL1) = read_sysreg_el1(SYS_SCTLR);
|
||||
ctxt_sys_reg(ctxt, CPACR_EL1) = read_sysreg_el1(SYS_CPACR);
|
||||
ctxt_sys_reg(ctxt, TTBR0_EL1) = read_sysreg_el1(SYS_TTBR0);
|
||||
ctxt_sys_reg(ctxt, TTBR1_EL1) = read_sysreg_el1(SYS_TTBR1);
|
||||
ctxt_sys_reg(ctxt, TCR_EL1) = read_sysreg_el1(SYS_TCR);
|
||||
ctxt_sys_reg(ctxt, ESR_EL1) = read_sysreg_el1(SYS_ESR);
|
||||
ctxt_sys_reg(ctxt, AFSR0_EL1) = read_sysreg_el1(SYS_AFSR0);
|
||||
ctxt_sys_reg(ctxt, AFSR1_EL1) = read_sysreg_el1(SYS_AFSR1);
|
||||
ctxt_sys_reg(ctxt, FAR_EL1) = read_sysreg_el1(SYS_FAR);
|
||||
ctxt_sys_reg(ctxt, MAIR_EL1) = read_sysreg_el1(SYS_MAIR);
|
||||
ctxt_sys_reg(ctxt, VBAR_EL1) = read_sysreg_el1(SYS_VBAR);
|
||||
ctxt_sys_reg(ctxt, CONTEXTIDR_EL1) = read_sysreg_el1(SYS_CONTEXTIDR);
|
||||
ctxt_sys_reg(ctxt, AMAIR_EL1) = read_sysreg_el1(SYS_AMAIR);
|
||||
ctxt_sys_reg(ctxt, CNTKCTL_EL1) = read_sysreg_el1(SYS_CNTKCTL);
|
||||
ctxt_sys_reg(ctxt, PAR_EL1) = read_sysreg(par_el1);
|
||||
ctxt_sys_reg(ctxt, TPIDR_EL1) = read_sysreg(tpidr_el1);
|
||||
|
||||
ctxt_sys_reg(ctxt, SP_EL1) = read_sysreg(sp_el1);
|
||||
ctxt_sys_reg(ctxt, ELR_EL1) = read_sysreg_el1(SYS_ELR);
|
||||
ctxt_sys_reg(ctxt, SPSR_EL1) = read_sysreg_el1(SYS_SPSR);
|
||||
}
|
||||
|
||||
static inline void __sysreg_save_el2_return_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
ctxt->regs.pc = read_sysreg_el2(SYS_ELR);
|
||||
ctxt->regs.pstate = read_sysreg_el2(SYS_SPSR);
|
||||
|
||||
if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN))
|
||||
ctxt_sys_reg(ctxt, DISR_EL1) = read_sysreg_s(SYS_VDISR_EL2);
|
||||
}
|
||||
|
||||
static inline void __sysreg_restore_common_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
write_sysreg(ctxt_sys_reg(ctxt, MDSCR_EL1), mdscr_el1);
|
||||
}
|
||||
|
||||
static inline void __sysreg_restore_user_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
write_sysreg(ctxt_sys_reg(ctxt, TPIDR_EL0), tpidr_el0);
|
||||
write_sysreg(ctxt_sys_reg(ctxt, TPIDRRO_EL0), tpidrro_el0);
|
||||
}
|
||||
|
||||
static inline void __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
write_sysreg(ctxt_sys_reg(ctxt, MPIDR_EL1), vmpidr_el2);
|
||||
write_sysreg(ctxt_sys_reg(ctxt, CSSELR_EL1), csselr_el1);
|
||||
|
||||
if (has_vhe() ||
|
||||
!cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, SCTLR_EL1), SYS_SCTLR);
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, TCR_EL1), SYS_TCR);
|
||||
} else if (!ctxt->__hyp_running_vcpu) {
|
||||
/*
|
||||
* Must only be done for guest registers, hence the context
|
||||
* test. We're coming from the host, so SCTLR.M is already
|
||||
* set. Pairs with nVHE's __activate_traps().
|
||||
*/
|
||||
write_sysreg_el1((ctxt_sys_reg(ctxt, TCR_EL1) |
|
||||
TCR_EPD1_MASK | TCR_EPD0_MASK),
|
||||
SYS_TCR);
|
||||
isb();
|
||||
}
|
||||
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, CPACR_EL1), SYS_CPACR);
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, TTBR0_EL1), SYS_TTBR0);
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, TTBR1_EL1), SYS_TTBR1);
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, ESR_EL1), SYS_ESR);
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, AFSR0_EL1), SYS_AFSR0);
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, AFSR1_EL1), SYS_AFSR1);
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, FAR_EL1), SYS_FAR);
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, MAIR_EL1), SYS_MAIR);
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, VBAR_EL1), SYS_VBAR);
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, CONTEXTIDR_EL1), SYS_CONTEXTIDR);
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, AMAIR_EL1), SYS_AMAIR);
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, CNTKCTL_EL1), SYS_CNTKCTL);
|
||||
write_sysreg(ctxt_sys_reg(ctxt, PAR_EL1), par_el1);
|
||||
write_sysreg(ctxt_sys_reg(ctxt, TPIDR_EL1), tpidr_el1);
|
||||
|
||||
if (!has_vhe() &&
|
||||
cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT) &&
|
||||
ctxt->__hyp_running_vcpu) {
|
||||
/*
|
||||
* Must only be done for host registers, hence the context
|
||||
* test. Pairs with nVHE's __deactivate_traps().
|
||||
*/
|
||||
isb();
|
||||
/*
|
||||
* At this stage, and thanks to the above isb(), S2 is
|
||||
* deconfigured and disabled. We can now restore the host's
|
||||
* S1 configuration: SCTLR, and only then TCR.
|
||||
*/
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, SCTLR_EL1), SYS_SCTLR);
|
||||
isb();
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, TCR_EL1), SYS_TCR);
|
||||
}
|
||||
|
||||
write_sysreg(ctxt_sys_reg(ctxt, SP_EL1), sp_el1);
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, ELR_EL1), SYS_ELR);
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, SPSR_EL1), SYS_SPSR);
|
||||
}
|
||||
|
||||
static inline void __sysreg_restore_el2_return_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
u64 pstate = ctxt->regs.pstate;
|
||||
u64 mode = pstate & PSR_AA32_MODE_MASK;
|
||||
|
||||
/*
|
||||
* Safety check to ensure we're setting the CPU up to enter the guest
|
||||
* in a less privileged mode.
|
||||
*
|
||||
* If we are attempting a return to EL2 or higher in AArch64 state,
|
||||
* program SPSR_EL2 with M=EL2h and the IL bit set which ensures that
|
||||
* we'll take an illegal exception state exception immediately after
|
||||
* the ERET to the guest. Attempts to return to AArch32 Hyp will
|
||||
* result in an illegal exception return because EL2's execution state
|
||||
* is determined by SCR_EL3.RW.
|
||||
*/
|
||||
if (!(mode & PSR_MODE32_BIT) && mode >= PSR_MODE_EL2t)
|
||||
pstate = PSR_MODE_EL2h | PSR_IL_BIT;
|
||||
|
||||
write_sysreg_el2(ctxt->regs.pc, SYS_ELR);
|
||||
write_sysreg_el2(pstate, SYS_SPSR);
|
||||
|
||||
if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN))
|
||||
write_sysreg_s(ctxt_sys_reg(ctxt, DISR_EL1), SYS_VDISR_EL2);
|
||||
}
|
||||
|
||||
static inline void __sysreg32_save_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (!vcpu_el1_is_32bit(vcpu))
|
||||
return;
|
||||
|
||||
vcpu->arch.ctxt.spsr_abt = read_sysreg(spsr_abt);
|
||||
vcpu->arch.ctxt.spsr_und = read_sysreg(spsr_und);
|
||||
vcpu->arch.ctxt.spsr_irq = read_sysreg(spsr_irq);
|
||||
vcpu->arch.ctxt.spsr_fiq = read_sysreg(spsr_fiq);
|
||||
|
||||
__vcpu_sys_reg(vcpu, DACR32_EL2) = read_sysreg(dacr32_el2);
|
||||
__vcpu_sys_reg(vcpu, IFSR32_EL2) = read_sysreg(ifsr32_el2);
|
||||
|
||||
if (has_vhe() || vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY)
|
||||
__vcpu_sys_reg(vcpu, DBGVCR32_EL2) = read_sysreg(dbgvcr32_el2);
|
||||
}
|
||||
|
||||
static inline void __sysreg32_restore_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (!vcpu_el1_is_32bit(vcpu))
|
||||
return;
|
||||
|
||||
write_sysreg(vcpu->arch.ctxt.spsr_abt, spsr_abt);
|
||||
write_sysreg(vcpu->arch.ctxt.spsr_und, spsr_und);
|
||||
write_sysreg(vcpu->arch.ctxt.spsr_irq, spsr_irq);
|
||||
write_sysreg(vcpu->arch.ctxt.spsr_fiq, spsr_fiq);
|
||||
|
||||
write_sysreg(__vcpu_sys_reg(vcpu, DACR32_EL2), dacr32_el2);
|
||||
write_sysreg(__vcpu_sys_reg(vcpu, IFSR32_EL2), ifsr32_el2);
|
||||
|
||||
if (has_vhe() || vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY)
|
||||
write_sysreg(__vcpu_sys_reg(vcpu, DBGVCR32_EL2), dbgvcr32_el2);
|
||||
}
|
||||
|
||||
#endif /* __ARM64_KVM_HYP_SYSREG_SR_H__ */
|
|
@ -0,0 +1,62 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for Kernel-based Virtual Machine module, HYP/nVHE part
|
||||
#
|
||||
|
||||
asflags-y := -D__KVM_NVHE_HYPERVISOR__
|
||||
ccflags-y := -D__KVM_NVHE_HYPERVISOR__
|
||||
|
||||
obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o hyp-init.o
|
||||
obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \
|
||||
../fpsimd.o ../hyp-entry.o
|
||||
|
||||
obj-y := $(patsubst %.o,%.hyp.o,$(obj-y))
|
||||
extra-y := $(patsubst %.hyp.o,%.hyp.tmp.o,$(obj-y))
|
||||
|
||||
$(obj)/%.hyp.tmp.o: $(src)/%.c FORCE
|
||||
$(call if_changed_rule,cc_o_c)
|
||||
$(obj)/%.hyp.tmp.o: $(src)/%.S FORCE
|
||||
$(call if_changed_rule,as_o_S)
|
||||
$(obj)/%.hyp.o: $(obj)/%.hyp.tmp.o FORCE
|
||||
$(call if_changed,hypcopy)
|
||||
|
||||
# Disable reordering functions by GCC (enabled at -O2).
|
||||
# This pass puts functions into '.text.*' sections to aid the linker
|
||||
# in optimizing ELF layout. See HYPCOPY comment below for more info.
|
||||
ccflags-y += $(call cc-option,-fno-reorder-functions)
|
||||
|
||||
# The HYPCOPY command uses `objcopy` to prefix all ELF symbol names
|
||||
# and relevant ELF section names to avoid clashes with VHE code/data.
|
||||
#
|
||||
# Hyp code is assumed to be in the '.text' section of the input object
|
||||
# files (with the exception of specialized sections such as
|
||||
# '.hyp.idmap.text'). This assumption may be broken by a compiler that
|
||||
# divides code into sections like '.text.unlikely' so as to optimize
|
||||
# ELF layout. HYPCOPY checks that no such sections exist in the input
|
||||
# using `objdump`, otherwise they would be linked together with other
|
||||
# kernel code and not memory-mapped correctly at runtime.
|
||||
quiet_cmd_hypcopy = HYPCOPY $@
|
||||
cmd_hypcopy = \
|
||||
if $(OBJDUMP) -h $< | grep -F '.text.'; then \
|
||||
echo "$@: function reordering not supported in nVHE hyp code" >&2; \
|
||||
/bin/false; \
|
||||
fi; \
|
||||
$(OBJCOPY) --prefix-symbols=__kvm_nvhe_ \
|
||||
--rename-section=.text=.hyp.text \
|
||||
$< $@
|
||||
|
||||
# Remove ftrace and Shadow Call Stack CFLAGS.
|
||||
# This is equivalent to the 'notrace' and '__noscs' annotations.
|
||||
KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS), $(KBUILD_CFLAGS))
|
||||
|
||||
# KVM nVHE code is run at a different exception code with a different map, so
|
||||
# compiler instrumentation that inserts callbacks or checks into the code may
|
||||
# cause crashes. Just disable it.
|
||||
GCOV_PROFILE := n
|
||||
KASAN_SANITIZE := n
|
||||
UBSAN_SANITIZE := n
|
||||
KCOV_INSTRUMENT := n
|
||||
|
||||
# Skip objtool checking for this directory because nVHE code is compiled with
|
||||
# non-standard build rules.
|
||||
OBJECT_FILES_NON_STANDARD := y
|
|
@ -0,0 +1,77 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <hyp/debug-sr.h>
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include <asm/debug-monitors.h>
|
||||
#include <asm/kvm_asm.h>
|
||||
#include <asm/kvm_hyp.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
|
||||
static void __debug_save_spe(u64 *pmscr_el1)
|
||||
{
|
||||
u64 reg;
|
||||
|
||||
/* Clear pmscr in case of early return */
|
||||
*pmscr_el1 = 0;
|
||||
|
||||
/* SPE present on this CPU? */
|
||||
if (!cpuid_feature_extract_unsigned_field(read_sysreg(id_aa64dfr0_el1),
|
||||
ID_AA64DFR0_PMSVER_SHIFT))
|
||||
return;
|
||||
|
||||
/* Yes; is it owned by EL3? */
|
||||
reg = read_sysreg_s(SYS_PMBIDR_EL1);
|
||||
if (reg & BIT(SYS_PMBIDR_EL1_P_SHIFT))
|
||||
return;
|
||||
|
||||
/* No; is the host actually using the thing? */
|
||||
reg = read_sysreg_s(SYS_PMBLIMITR_EL1);
|
||||
if (!(reg & BIT(SYS_PMBLIMITR_EL1_E_SHIFT)))
|
||||
return;
|
||||
|
||||
/* Yes; save the control register and disable data generation */
|
||||
*pmscr_el1 = read_sysreg_s(SYS_PMSCR_EL1);
|
||||
write_sysreg_s(0, SYS_PMSCR_EL1);
|
||||
isb();
|
||||
|
||||
/* Now drain all buffered data to memory */
|
||||
psb_csync();
|
||||
dsb(nsh);
|
||||
}
|
||||
|
||||
static void __debug_restore_spe(u64 pmscr_el1)
|
||||
{
|
||||
if (!pmscr_el1)
|
||||
return;
|
||||
|
||||
/* The host page table is installed, but not yet synchronised */
|
||||
isb();
|
||||
|
||||
/* Re-enable data generation */
|
||||
write_sysreg_s(pmscr_el1, SYS_PMSCR_EL1);
|
||||
}
|
||||
|
||||
void __debug_switch_to_guest(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/* Disable and flush SPE data generation */
|
||||
__debug_save_spe(&vcpu->arch.host_debug_state.pmscr_el1);
|
||||
__debug_switch_to_guest_common(vcpu);
|
||||
}
|
||||
|
||||
void __debug_switch_to_host(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
__debug_restore_spe(vcpu->arch.host_debug_state.pmscr_el1);
|
||||
__debug_switch_to_host_common(vcpu);
|
||||
}
|
||||
|
||||
u32 __kvm_get_mdcr_el2(void)
|
||||
{
|
||||
return read_sysreg(mdcr_el2);
|
||||
}
|
|
@ -105,6 +105,11 @@ alternative_else_nop_endif
|
|||
*/
|
||||
mov_q x4, (SCTLR_EL2_RES1 | (SCTLR_ELx_FLAGS & ~SCTLR_ELx_A))
|
||||
CPU_BE( orr x4, x4, #SCTLR_ELx_EE)
|
||||
alternative_if ARM64_HAS_ADDRESS_AUTH
|
||||
mov_q x5, (SCTLR_ELx_ENIA | SCTLR_ELx_ENIB | \
|
||||
SCTLR_ELx_ENDA | SCTLR_ELx_ENDB)
|
||||
orr x4, x4, x5
|
||||
alternative_else_nop_endif
|
||||
msr sctlr_el2, x4
|
||||
isb
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <hyp/switch.h>
|
||||
#include <hyp/sysreg-sr.h>
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <uapi/linux/psci.h>
|
||||
|
||||
#include <kvm/arm_psci.h>
|
||||
|
||||
#include <asm/barrier.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/kprobes.h>
|
||||
#include <asm/kvm_asm.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_hyp.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
#include <asm/fpsimd.h>
|
||||
#include <asm/debug-monitors.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/thread_info.h>
|
||||
|
||||
static void __activate_traps(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
___activate_traps(vcpu);
|
||||
__activate_traps_common(vcpu);
|
||||
|
||||
val = CPTR_EL2_DEFAULT;
|
||||
val |= CPTR_EL2_TTA | CPTR_EL2_TZ | CPTR_EL2_TAM;
|
||||
if (!update_fp_enabled(vcpu)) {
|
||||
val |= CPTR_EL2_TFP;
|
||||
__activate_traps_fpsimd32(vcpu);
|
||||
}
|
||||
|
||||
write_sysreg(val, cptr_el2);
|
||||
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||
struct kvm_cpu_context *ctxt = &vcpu->arch.ctxt;
|
||||
|
||||
isb();
|
||||
/*
|
||||
* At this stage, and thanks to the above isb(), S2 is
|
||||
* configured and enabled. We can now restore the guest's S1
|
||||
* configuration: SCTLR, and only then TCR.
|
||||
*/
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, SCTLR_EL1), SYS_SCTLR);
|
||||
isb();
|
||||
write_sysreg_el1(ctxt_sys_reg(ctxt, TCR_EL1), SYS_TCR);
|
||||
}
|
||||
}
|
||||
|
||||
static void __deactivate_traps(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 mdcr_el2;
|
||||
|
||||
___deactivate_traps(vcpu);
|
||||
|
||||
mdcr_el2 = read_sysreg(mdcr_el2);
|
||||
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||
u64 val;
|
||||
|
||||
/*
|
||||
* Set the TCR and SCTLR registers in the exact opposite
|
||||
* sequence as __activate_traps (first prevent walks,
|
||||
* then force the MMU on). A generous sprinkling of isb()
|
||||
* ensure that things happen in this exact order.
|
||||
*/
|
||||
val = read_sysreg_el1(SYS_TCR);
|
||||
write_sysreg_el1(val | TCR_EPD1_MASK | TCR_EPD0_MASK, SYS_TCR);
|
||||
isb();
|
||||
val = read_sysreg_el1(SYS_SCTLR);
|
||||
write_sysreg_el1(val | SCTLR_ELx_M, SYS_SCTLR);
|
||||
isb();
|
||||
}
|
||||
|
||||
__deactivate_traps_common();
|
||||
|
||||
mdcr_el2 &= MDCR_EL2_HPMN_MASK;
|
||||
mdcr_el2 |= MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT;
|
||||
|
||||
write_sysreg(mdcr_el2, mdcr_el2);
|
||||
write_sysreg(HCR_HOST_NVHE_FLAGS, hcr_el2);
|
||||
write_sysreg(CPTR_EL2_DEFAULT, cptr_el2);
|
||||
}
|
||||
|
||||
static void __deactivate_vm(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
write_sysreg(0, vttbr_el2);
|
||||
}
|
||||
|
||||
/* Save VGICv3 state on non-VHE systems */
|
||||
static void __hyp_vgic_save_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) {
|
||||
__vgic_v3_save_state(&vcpu->arch.vgic_cpu.vgic_v3);
|
||||
__vgic_v3_deactivate_traps(&vcpu->arch.vgic_cpu.vgic_v3);
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore VGICv3 state on non_VEH systems */
|
||||
static void __hyp_vgic_restore_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) {
|
||||
__vgic_v3_activate_traps(&vcpu->arch.vgic_cpu.vgic_v3);
|
||||
__vgic_v3_restore_state(&vcpu->arch.vgic_cpu.vgic_v3);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable host events, enable guest events
|
||||
*/
|
||||
static bool __pmu_switch_to_guest(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
struct kvm_host_data *host;
|
||||
struct kvm_pmu_events *pmu;
|
||||
|
||||
host = container_of(host_ctxt, struct kvm_host_data, host_ctxt);
|
||||
pmu = &host->pmu_events;
|
||||
|
||||
if (pmu->events_host)
|
||||
write_sysreg(pmu->events_host, pmcntenclr_el0);
|
||||
|
||||
if (pmu->events_guest)
|
||||
write_sysreg(pmu->events_guest, pmcntenset_el0);
|
||||
|
||||
return (pmu->events_host || pmu->events_guest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable guest events, enable host events
|
||||
*/
|
||||
static void __pmu_switch_to_host(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
struct kvm_host_data *host;
|
||||
struct kvm_pmu_events *pmu;
|
||||
|
||||
host = container_of(host_ctxt, struct kvm_host_data, host_ctxt);
|
||||
pmu = &host->pmu_events;
|
||||
|
||||
if (pmu->events_guest)
|
||||
write_sysreg(pmu->events_guest, pmcntenclr_el0);
|
||||
|
||||
if (pmu->events_host)
|
||||
write_sysreg(pmu->events_host, pmcntenset_el0);
|
||||
}
|
||||
|
||||
/* Switch to the guest for legacy non-VHE systems */
|
||||
int __kvm_vcpu_run(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_cpu_context *host_ctxt;
|
||||
struct kvm_cpu_context *guest_ctxt;
|
||||
bool pmu_switch_needed;
|
||||
u64 exit_code;
|
||||
|
||||
/*
|
||||
* Having IRQs masked via PMR when entering the guest means the GIC
|
||||
* will not signal the CPU of interrupts of lower priority, and the
|
||||
* only way to get out will be via guest exceptions.
|
||||
* Naturally, we want to avoid this.
|
||||
*/
|
||||
if (system_uses_irq_prio_masking()) {
|
||||
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);
|
||||
pmr_sync();
|
||||
}
|
||||
|
||||
vcpu = kern_hyp_va(vcpu);
|
||||
|
||||
host_ctxt = &__hyp_this_cpu_ptr(kvm_host_data)->host_ctxt;
|
||||
host_ctxt->__hyp_running_vcpu = vcpu;
|
||||
guest_ctxt = &vcpu->arch.ctxt;
|
||||
|
||||
pmu_switch_needed = __pmu_switch_to_guest(host_ctxt);
|
||||
|
||||
__sysreg_save_state_nvhe(host_ctxt);
|
||||
|
||||
/*
|
||||
* We must restore the 32-bit state before the sysregs, thanks
|
||||
* to erratum #852523 (Cortex-A57) or #853709 (Cortex-A72).
|
||||
*
|
||||
* Also, and in order to be able to deal with erratum #1319537 (A57)
|
||||
* and #1319367 (A72), we must ensure that all VM-related sysreg are
|
||||
* restored before we enable S2 translation.
|
||||
*/
|
||||
__sysreg32_restore_state(vcpu);
|
||||
__sysreg_restore_state_nvhe(guest_ctxt);
|
||||
|
||||
__activate_vm(kern_hyp_va(vcpu->arch.hw_mmu));
|
||||
__activate_traps(vcpu);
|
||||
|
||||
__hyp_vgic_restore_state(vcpu);
|
||||
__timer_enable_traps(vcpu);
|
||||
|
||||
__debug_switch_to_guest(vcpu);
|
||||
|
||||
__set_guest_arch_workaround_state(vcpu);
|
||||
|
||||
do {
|
||||
/* Jump in the fire! */
|
||||
exit_code = __guest_enter(vcpu, host_ctxt);
|
||||
|
||||
/* And we're baaack! */
|
||||
} while (fixup_guest_exit(vcpu, &exit_code));
|
||||
|
||||
__set_host_arch_workaround_state(vcpu);
|
||||
|
||||
__sysreg_save_state_nvhe(guest_ctxt);
|
||||
__sysreg32_save_state(vcpu);
|
||||
__timer_disable_traps(vcpu);
|
||||
__hyp_vgic_save_state(vcpu);
|
||||
|
||||
__deactivate_traps(vcpu);
|
||||
__deactivate_vm(vcpu);
|
||||
|
||||
__sysreg_restore_state_nvhe(host_ctxt);
|
||||
|
||||
if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED)
|
||||
__fpsimd_save_fpexc32(vcpu);
|
||||
|
||||
/*
|
||||
* This must come after restoring the host sysregs, since a non-VHE
|
||||
* system may enable SPE here and make use of the TTBRs.
|
||||
*/
|
||||
__debug_switch_to_host(vcpu);
|
||||
|
||||
if (pmu_switch_needed)
|
||||
__pmu_switch_to_host(host_ctxt);
|
||||
|
||||
/* Returning to host will clear PSR.I, remask PMR if needed */
|
||||
if (system_uses_irq_prio_masking())
|
||||
gic_write_pmr(GIC_PRIO_IRQOFF);
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
void __noreturn hyp_panic(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
u64 spsr = read_sysreg_el2(SYS_SPSR);
|
||||
u64 elr = read_sysreg_el2(SYS_ELR);
|
||||
u64 par = read_sysreg(par_el1);
|
||||
struct kvm_vcpu *vcpu = host_ctxt->__hyp_running_vcpu;
|
||||
unsigned long str_va;
|
||||
|
||||
if (read_sysreg(vttbr_el2)) {
|
||||
__timer_disable_traps(vcpu);
|
||||
__deactivate_traps(vcpu);
|
||||
__deactivate_vm(vcpu);
|
||||
__sysreg_restore_state_nvhe(host_ctxt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Force the panic string to be loaded from the literal pool,
|
||||
* making sure it is a kernel address and not a PC-relative
|
||||
* reference.
|
||||
*/
|
||||
asm volatile("ldr %0, =%1" : "=r" (str_va) : "S" (__hyp_panic_string));
|
||||
|
||||
__hyp_do_panic(str_va,
|
||||
spsr, elr,
|
||||
read_sysreg(esr_el2), read_sysreg_el2(SYS_FAR),
|
||||
read_sysreg(hpfar_el2), par, vcpu);
|
||||
unreachable();
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2012-2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <hyp/sysreg-sr.h>
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include <asm/kprobes.h>
|
||||
#include <asm/kvm_asm.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_hyp.h>
|
||||
|
||||
/*
|
||||
* Non-VHE: Both host and guest must save everything.
|
||||
*/
|
||||
|
||||
void __sysreg_save_state_nvhe(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
__sysreg_save_el1_state(ctxt);
|
||||
__sysreg_save_common_state(ctxt);
|
||||
__sysreg_save_user_state(ctxt);
|
||||
__sysreg_save_el2_return_state(ctxt);
|
||||
}
|
||||
|
||||
void __sysreg_restore_state_nvhe(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
__sysreg_restore_el1_state(ctxt);
|
||||
__sysreg_restore_common_state(ctxt);
|
||||
__sysreg_restore_user_state(ctxt);
|
||||
__sysreg_restore_el2_return_state(ctxt);
|
||||
}
|
||||
|
||||
void __kvm_enable_ssbs(void)
|
||||
{
|
||||
u64 tmp;
|
||||
|
||||
asm volatile(
|
||||
"mrs %0, sctlr_el2\n"
|
||||
"orr %0, %0, %1\n"
|
||||
"msr sctlr_el2, %0"
|
||||
: "=&r" (tmp) : "L" (SCTLR_ELx_DSSBS));
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#include <asm/kvm_hyp.h>
|
||||
|
||||
void __hyp_text __kvm_timer_set_cntvoff(u64 cntvoff)
|
||||
void __kvm_timer_set_cntvoff(u64 cntvoff)
|
||||
{
|
||||
write_sysreg(cntvoff, cntvoff_el2);
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ void __hyp_text __kvm_timer_set_cntvoff(u64 cntvoff)
|
|||
* Should only be called on non-VHE systems.
|
||||
* VHE systems use EL2 timers and configure EL1 timers in kvm_timer_init_vhe().
|
||||
*/
|
||||
void __hyp_text __timer_disable_traps(struct kvm_vcpu *vcpu)
|
||||
void __timer_disable_traps(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
|
@ -33,7 +33,7 @@ void __hyp_text __timer_disable_traps(struct kvm_vcpu *vcpu)
|
|||
* Should only be called on non-VHE systems.
|
||||
* VHE systems use EL2 timers and configure EL1 timers in kvm_timer_init_vhe().
|
||||
*/
|
||||
void __hyp_text __timer_enable_traps(struct kvm_vcpu *vcpu)
|
||||
void __timer_enable_traps(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 val;
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <asm/kvm_hyp.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
struct tlb_inv_context {
|
||||
u64 tcr;
|
||||
};
|
||||
|
||||
static void __tlb_switch_to_guest(struct kvm_s2_mmu *mmu,
|
||||
struct tlb_inv_context *cxt)
|
||||
{
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||
u64 val;
|
||||
|
||||
/*
|
||||
* For CPUs that are affected by ARM 1319367, we need to
|
||||
* avoid a host Stage-1 walk while we have the guest's
|
||||
* VMID set in the VTTBR in order to invalidate TLBs.
|
||||
* We're guaranteed that the S1 MMU is enabled, so we can
|
||||
* simply set the EPD bits to avoid any further TLB fill.
|
||||
*/
|
||||
val = cxt->tcr = read_sysreg_el1(SYS_TCR);
|
||||
val |= TCR_EPD1_MASK | TCR_EPD0_MASK;
|
||||
write_sysreg_el1(val, SYS_TCR);
|
||||
isb();
|
||||
}
|
||||
|
||||
__load_guest_stage2(mmu);
|
||||
}
|
||||
|
||||
static void __tlb_switch_to_host(struct tlb_inv_context *cxt)
|
||||
{
|
||||
write_sysreg(0, vttbr_el2);
|
||||
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||
/* Ensure write of the host VMID */
|
||||
isb();
|
||||
/* Restore the host's TCR_EL1 */
|
||||
write_sysreg_el1(cxt->tcr, SYS_TCR);
|
||||
}
|
||||
}
|
||||
|
||||
void __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu,
|
||||
phys_addr_t ipa, int level)
|
||||
{
|
||||
struct tlb_inv_context cxt;
|
||||
|
||||
dsb(ishst);
|
||||
|
||||
/* Switch to requested VMID */
|
||||
mmu = kern_hyp_va(mmu);
|
||||
__tlb_switch_to_guest(mmu, &cxt);
|
||||
|
||||
/*
|
||||
* We could do so much better if we had the VA as well.
|
||||
* Instead, we invalidate Stage-2 for this IPA, and the
|
||||
* whole of Stage-1. Weep...
|
||||
*/
|
||||
ipa >>= 12;
|
||||
__tlbi_level(ipas2e1is, ipa, level);
|
||||
|
||||
/*
|
||||
* We have to ensure completion of the invalidation at Stage-2,
|
||||
* since a table walk on another CPU could refill a TLB with a
|
||||
* complete (S1 + S2) walk based on the old Stage-2 mapping if
|
||||
* the Stage-1 invalidation happened first.
|
||||
*/
|
||||
dsb(ish);
|
||||
__tlbi(vmalle1is);
|
||||
dsb(ish);
|
||||
isb();
|
||||
|
||||
/*
|
||||
* If the host is running at EL1 and we have a VPIPT I-cache,
|
||||
* then we must perform I-cache maintenance at EL2 in order for
|
||||
* it to have an effect on the guest. Since the guest cannot hit
|
||||
* I-cache lines allocated with a different VMID, we don't need
|
||||
* to worry about junk out of guest reset (we nuke the I-cache on
|
||||
* VMID rollover), but we do need to be careful when remapping
|
||||
* executable pages for the same guest. This can happen when KSM
|
||||
* takes a CoW fault on an executable page, copies the page into
|
||||
* a page that was previously mapped in the guest and then needs
|
||||
* to invalidate the guest view of the I-cache for that page
|
||||
* from EL1. To solve this, we invalidate the entire I-cache when
|
||||
* unmapping a page from a guest if we have a VPIPT I-cache but
|
||||
* the host is running at EL1. As above, we could do better if
|
||||
* we had the VA.
|
||||
*
|
||||
* The moral of this story is: if you have a VPIPT I-cache, then
|
||||
* you should be running with VHE enabled.
|
||||
*/
|
||||
if (icache_is_vpipt())
|
||||
__flush_icache_all();
|
||||
|
||||
__tlb_switch_to_host(&cxt);
|
||||
}
|
||||
|
||||
void __kvm_tlb_flush_vmid(struct kvm_s2_mmu *mmu)
|
||||
{
|
||||
struct tlb_inv_context cxt;
|
||||
|
||||
dsb(ishst);
|
||||
|
||||
/* Switch to requested VMID */
|
||||
mmu = kern_hyp_va(mmu);
|
||||
__tlb_switch_to_guest(mmu, &cxt);
|
||||
|
||||
__tlbi(vmalls12e1is);
|
||||
dsb(ish);
|
||||
isb();
|
||||
|
||||
__tlb_switch_to_host(&cxt);
|
||||
}
|
||||
|
||||
void __kvm_tlb_flush_local_vmid(struct kvm_s2_mmu *mmu)
|
||||
{
|
||||
struct tlb_inv_context cxt;
|
||||
|
||||
/* Switch to requested VMID */
|
||||
mmu = kern_hyp_va(mmu);
|
||||
__tlb_switch_to_guest(mmu, &cxt);
|
||||
|
||||
__tlbi(vmalle1);
|
||||
dsb(nsh);
|
||||
isb();
|
||||
|
||||
__tlb_switch_to_host(&cxt);
|
||||
}
|
||||
|
||||
void __kvm_flush_vm_context(void)
|
||||
{
|
||||
dsb(ishst);
|
||||
__tlbi(alle1is);
|
||||
|
||||
/*
|
||||
* VIPT and PIPT caches are not affected by VMID, so no maintenance
|
||||
* is necessary across a VMID rollover.
|
||||
*
|
||||
* VPIPT caches constrain lookup and maintenance to the active VMID,
|
||||
* so we need to invalidate lines with a stale VMID to avoid an ABA
|
||||
* race after multiple rollovers.
|
||||
*
|
||||
*/
|
||||
if (icache_is_vpipt())
|
||||
asm volatile("ic ialluis");
|
||||
|
||||
dsb(ish);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2015-2018 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/linkage.h>
|
||||
|
||||
#include <asm/kvm_asm.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
|
||||
/*
|
||||
* This is not executed directly and is instead copied into the vectors
|
||||
* by install_bp_hardening_cb().
|
||||
*/
|
||||
.data
|
||||
.pushsection .rodata
|
||||
.global __smccc_workaround_1_smc
|
||||
SYM_DATA_START(__smccc_workaround_1_smc)
|
||||
esb
|
||||
sub sp, sp, #(8 * 4)
|
||||
stp x2, x3, [sp, #(8 * 0)]
|
||||
stp x0, x1, [sp, #(8 * 2)]
|
||||
mov w0, #ARM_SMCCC_ARCH_WORKAROUND_1
|
||||
smc #0
|
||||
ldp x2, x3, [sp, #(8 * 0)]
|
||||
ldp x0, x1, [sp, #(8 * 2)]
|
||||
add sp, sp, #(8 * 4)
|
||||
1: .org __smccc_workaround_1_smc + __SMCCC_WORKAROUND_1_SMC_SZ
|
||||
.org 1b
|
||||
SYM_DATA_END(__smccc_workaround_1_smc)
|
|
@ -1,936 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <uapi/linux/psci.h>
|
||||
|
||||
#include <kvm/arm_psci.h>
|
||||
|
||||
#include <asm/barrier.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/kprobes.h>
|
||||
#include <asm/kvm_asm.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_hyp.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
#include <asm/fpsimd.h>
|
||||
#include <asm/debug-monitors.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/thread_info.h>
|
||||
|
||||
/* Check whether the FP regs were dirtied while in the host-side run loop: */
|
||||
static bool __hyp_text update_fp_enabled(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* When the system doesn't support FP/SIMD, we cannot rely on
|
||||
* the _TIF_FOREIGN_FPSTATE flag. However, we always inject an
|
||||
* abort on the very first access to FP and thus we should never
|
||||
* see KVM_ARM64_FP_ENABLED. For added safety, make sure we always
|
||||
* trap the accesses.
|
||||
*/
|
||||
if (!system_supports_fpsimd() ||
|
||||
vcpu->arch.host_thread_info->flags & _TIF_FOREIGN_FPSTATE)
|
||||
vcpu->arch.flags &= ~(KVM_ARM64_FP_ENABLED |
|
||||
KVM_ARM64_FP_HOST);
|
||||
|
||||
return !!(vcpu->arch.flags & KVM_ARM64_FP_ENABLED);
|
||||
}
|
||||
|
||||
/* Save the 32-bit only FPSIMD system register state */
|
||||
static void __hyp_text __fpsimd_save_fpexc32(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (!vcpu_el1_is_32bit(vcpu))
|
||||
return;
|
||||
|
||||
vcpu->arch.ctxt.sys_regs[FPEXC32_EL2] = read_sysreg(fpexc32_el2);
|
||||
}
|
||||
|
||||
static void __hyp_text __activate_traps_fpsimd32(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* We are about to set CPTR_EL2.TFP to trap all floating point
|
||||
* register accesses to EL2, however, the ARM ARM clearly states that
|
||||
* traps are only taken to EL2 if the operation would not otherwise
|
||||
* trap to EL1. Therefore, always make sure that for 32-bit guests,
|
||||
* we set FPEXC.EN to prevent traps to EL1, when setting the TFP bit.
|
||||
* If FP/ASIMD is not implemented, FPEXC is UNDEFINED and any access to
|
||||
* it will cause an exception.
|
||||
*/
|
||||
if (vcpu_el1_is_32bit(vcpu) && system_supports_fpsimd()) {
|
||||
write_sysreg(1 << 30, fpexc32_el2);
|
||||
isb();
|
||||
}
|
||||
}
|
||||
|
||||
static void __hyp_text __activate_traps_common(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/* Trap on AArch32 cp15 c15 (impdef sysregs) accesses (EL1 or EL0) */
|
||||
write_sysreg(1 << 15, hstr_el2);
|
||||
|
||||
/*
|
||||
* Make sure we trap PMU access from EL0 to EL2. Also sanitize
|
||||
* PMSELR_EL0 to make sure it never contains the cycle
|
||||
* counter, which could make a PMXEVCNTR_EL0 access UNDEF at
|
||||
* EL1 instead of being trapped to EL2.
|
||||
*/
|
||||
write_sysreg(0, pmselr_el0);
|
||||
write_sysreg(ARMV8_PMU_USERENR_MASK, pmuserenr_el0);
|
||||
write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2);
|
||||
}
|
||||
|
||||
static void __hyp_text __deactivate_traps_common(void)
|
||||
{
|
||||
write_sysreg(0, hstr_el2);
|
||||
write_sysreg(0, pmuserenr_el0);
|
||||
}
|
||||
|
||||
static void activate_traps_vhe(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
val = read_sysreg(cpacr_el1);
|
||||
val |= CPACR_EL1_TTA;
|
||||
val &= ~CPACR_EL1_ZEN;
|
||||
|
||||
/*
|
||||
* With VHE (HCR.E2H == 1), accesses to CPACR_EL1 are routed to
|
||||
* CPTR_EL2. In general, CPACR_EL1 has the same layout as CPTR_EL2,
|
||||
* except for some missing controls, such as TAM.
|
||||
* In this case, CPTR_EL2.TAM has the same position with or without
|
||||
* VHE (HCR.E2H == 1) which allows us to use here the CPTR_EL2.TAM
|
||||
* shift value for trapping the AMU accesses.
|
||||
*/
|
||||
|
||||
val |= CPTR_EL2_TAM;
|
||||
|
||||
if (update_fp_enabled(vcpu)) {
|
||||
if (vcpu_has_sve(vcpu))
|
||||
val |= CPACR_EL1_ZEN;
|
||||
} else {
|
||||
val &= ~CPACR_EL1_FPEN;
|
||||
__activate_traps_fpsimd32(vcpu);
|
||||
}
|
||||
|
||||
write_sysreg(val, cpacr_el1);
|
||||
|
||||
write_sysreg(kvm_get_hyp_vector(), vbar_el1);
|
||||
}
|
||||
NOKPROBE_SYMBOL(activate_traps_vhe);
|
||||
|
||||
static void __hyp_text __activate_traps_nvhe(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
__activate_traps_common(vcpu);
|
||||
|
||||
val = CPTR_EL2_DEFAULT;
|
||||
val |= CPTR_EL2_TTA | CPTR_EL2_TZ | CPTR_EL2_TAM;
|
||||
if (!update_fp_enabled(vcpu)) {
|
||||
val |= CPTR_EL2_TFP;
|
||||
__activate_traps_fpsimd32(vcpu);
|
||||
}
|
||||
|
||||
write_sysreg(val, cptr_el2);
|
||||
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||
struct kvm_cpu_context *ctxt = &vcpu->arch.ctxt;
|
||||
|
||||
isb();
|
||||
/*
|
||||
* At this stage, and thanks to the above isb(), S2 is
|
||||
* configured and enabled. We can now restore the guest's S1
|
||||
* configuration: SCTLR, and only then TCR.
|
||||
*/
|
||||
write_sysreg_el1(ctxt->sys_regs[SCTLR_EL1], SYS_SCTLR);
|
||||
isb();
|
||||
write_sysreg_el1(ctxt->sys_regs[TCR_EL1], SYS_TCR);
|
||||
}
|
||||
}
|
||||
|
||||
static void __hyp_text __activate_traps(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 hcr = vcpu->arch.hcr_el2;
|
||||
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_CAVIUM_TX2_219_TVM))
|
||||
hcr |= HCR_TVM;
|
||||
|
||||
write_sysreg(hcr, hcr_el2);
|
||||
|
||||
if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN) && (hcr & HCR_VSE))
|
||||
write_sysreg_s(vcpu->arch.vsesr_el2, SYS_VSESR_EL2);
|
||||
|
||||
if (has_vhe())
|
||||
activate_traps_vhe(vcpu);
|
||||
else
|
||||
__activate_traps_nvhe(vcpu);
|
||||
}
|
||||
|
||||
static void deactivate_traps_vhe(void)
|
||||
{
|
||||
extern char vectors[]; /* kernel exception vectors */
|
||||
write_sysreg(HCR_HOST_VHE_FLAGS, hcr_el2);
|
||||
|
||||
/*
|
||||
* ARM errata 1165522 and 1530923 require the actual execution of the
|
||||
* above before we can switch to the EL2/EL0 translation regime used by
|
||||
* the host.
|
||||
*/
|
||||
asm(ALTERNATIVE("nop", "isb", ARM64_WORKAROUND_SPECULATIVE_AT));
|
||||
|
||||
write_sysreg(CPACR_EL1_DEFAULT, cpacr_el1);
|
||||
write_sysreg(vectors, vbar_el1);
|
||||
}
|
||||
NOKPROBE_SYMBOL(deactivate_traps_vhe);
|
||||
|
||||
static void __hyp_text __deactivate_traps_nvhe(void)
|
||||
{
|
||||
u64 mdcr_el2 = read_sysreg(mdcr_el2);
|
||||
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||
u64 val;
|
||||
|
||||
/*
|
||||
* Set the TCR and SCTLR registers in the exact opposite
|
||||
* sequence as __activate_traps_nvhe (first prevent walks,
|
||||
* then force the MMU on). A generous sprinkling of isb()
|
||||
* ensure that things happen in this exact order.
|
||||
*/
|
||||
val = read_sysreg_el1(SYS_TCR);
|
||||
write_sysreg_el1(val | TCR_EPD1_MASK | TCR_EPD0_MASK, SYS_TCR);
|
||||
isb();
|
||||
val = read_sysreg_el1(SYS_SCTLR);
|
||||
write_sysreg_el1(val | SCTLR_ELx_M, SYS_SCTLR);
|
||||
isb();
|
||||
}
|
||||
|
||||
__deactivate_traps_common();
|
||||
|
||||
mdcr_el2 &= MDCR_EL2_HPMN_MASK;
|
||||
mdcr_el2 |= MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT;
|
||||
|
||||
write_sysreg(mdcr_el2, mdcr_el2);
|
||||
write_sysreg(HCR_HOST_NVHE_FLAGS, hcr_el2);
|
||||
write_sysreg(CPTR_EL2_DEFAULT, cptr_el2);
|
||||
}
|
||||
|
||||
static void __hyp_text __deactivate_traps(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* If we pended a virtual abort, preserve it until it gets
|
||||
* cleared. See D1.14.3 (Virtual Interrupts) for details, but
|
||||
* the crucial bit is "On taking a vSError interrupt,
|
||||
* HCR_EL2.VSE is cleared to 0."
|
||||
*/
|
||||
if (vcpu->arch.hcr_el2 & HCR_VSE) {
|
||||
vcpu->arch.hcr_el2 &= ~HCR_VSE;
|
||||
vcpu->arch.hcr_el2 |= read_sysreg(hcr_el2) & HCR_VSE;
|
||||
}
|
||||
|
||||
if (has_vhe())
|
||||
deactivate_traps_vhe();
|
||||
else
|
||||
__deactivate_traps_nvhe();
|
||||
}
|
||||
|
||||
void activate_traps_vhe_load(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
__activate_traps_common(vcpu);
|
||||
}
|
||||
|
||||
void deactivate_traps_vhe_put(void)
|
||||
{
|
||||
u64 mdcr_el2 = read_sysreg(mdcr_el2);
|
||||
|
||||
mdcr_el2 &= MDCR_EL2_HPMN_MASK |
|
||||
MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT |
|
||||
MDCR_EL2_TPMS;
|
||||
|
||||
write_sysreg(mdcr_el2, mdcr_el2);
|
||||
|
||||
__deactivate_traps_common();
|
||||
}
|
||||
|
||||
static void __hyp_text __activate_vm(struct kvm *kvm)
|
||||
{
|
||||
__load_guest_stage2(kvm);
|
||||
}
|
||||
|
||||
static void __hyp_text __deactivate_vm(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
write_sysreg(0, vttbr_el2);
|
||||
}
|
||||
|
||||
/* Save VGICv3 state on non-VHE systems */
|
||||
static void __hyp_text __hyp_vgic_save_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) {
|
||||
__vgic_v3_save_state(&vcpu->arch.vgic_cpu.vgic_v3);
|
||||
__vgic_v3_deactivate_traps(&vcpu->arch.vgic_cpu.vgic_v3);
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore VGICv3 state on non_VEH systems */
|
||||
static void __hyp_text __hyp_vgic_restore_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) {
|
||||
__vgic_v3_activate_traps(&vcpu->arch.vgic_cpu.vgic_v3);
|
||||
__vgic_v3_restore_state(&vcpu->arch.vgic_cpu.vgic_v3);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __hyp_text __translate_far_to_hpfar(u64 far, u64 *hpfar)
|
||||
{
|
||||
u64 par, tmp;
|
||||
|
||||
/*
|
||||
* Resolve the IPA the hard way using the guest VA.
|
||||
*
|
||||
* Stage-1 translation already validated the memory access
|
||||
* rights. As such, we can use the EL1 translation regime, and
|
||||
* don't have to distinguish between EL0 and EL1 access.
|
||||
*
|
||||
* We do need to save/restore PAR_EL1 though, as we haven't
|
||||
* saved the guest context yet, and we may return early...
|
||||
*/
|
||||
par = read_sysreg(par_el1);
|
||||
asm volatile("at s1e1r, %0" : : "r" (far));
|
||||
isb();
|
||||
|
||||
tmp = read_sysreg(par_el1);
|
||||
write_sysreg(par, par_el1);
|
||||
|
||||
if (unlikely(tmp & SYS_PAR_EL1_F))
|
||||
return false; /* Translation failed, back to guest */
|
||||
|
||||
/* Convert PAR to HPFAR format */
|
||||
*hpfar = PAR_TO_HPFAR(tmp);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __hyp_text __populate_fault_info(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u8 ec;
|
||||
u64 esr;
|
||||
u64 hpfar, far;
|
||||
|
||||
esr = vcpu->arch.fault.esr_el2;
|
||||
ec = ESR_ELx_EC(esr);
|
||||
|
||||
if (ec != ESR_ELx_EC_DABT_LOW && ec != ESR_ELx_EC_IABT_LOW)
|
||||
return true;
|
||||
|
||||
far = read_sysreg_el2(SYS_FAR);
|
||||
|
||||
/*
|
||||
* The HPFAR can be invalid if the stage 2 fault did not
|
||||
* happen during a stage 1 page table walk (the ESR_EL2.S1PTW
|
||||
* bit is clear) and one of the two following cases are true:
|
||||
* 1. The fault was due to a permission fault
|
||||
* 2. The processor carries errata 834220
|
||||
*
|
||||
* Therefore, for all non S1PTW faults where we either have a
|
||||
* permission fault or the errata workaround is enabled, we
|
||||
* resolve the IPA using the AT instruction.
|
||||
*/
|
||||
if (!(esr & ESR_ELx_S1PTW) &&
|
||||
(cpus_have_final_cap(ARM64_WORKAROUND_834220) ||
|
||||
(esr & ESR_ELx_FSC_TYPE) == FSC_PERM)) {
|
||||
if (!__translate_far_to_hpfar(far, &hpfar))
|
||||
return false;
|
||||
} else {
|
||||
hpfar = read_sysreg(hpfar_el2);
|
||||
}
|
||||
|
||||
vcpu->arch.fault.far_el2 = far;
|
||||
vcpu->arch.fault.hpfar_el2 = hpfar;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check for an FPSIMD/SVE trap and handle as appropriate */
|
||||
static bool __hyp_text __hyp_handle_fpsimd(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
bool vhe, sve_guest, sve_host;
|
||||
u8 hsr_ec;
|
||||
|
||||
if (!system_supports_fpsimd())
|
||||
return false;
|
||||
|
||||
if (system_supports_sve()) {
|
||||
sve_guest = vcpu_has_sve(vcpu);
|
||||
sve_host = vcpu->arch.flags & KVM_ARM64_HOST_SVE_IN_USE;
|
||||
vhe = true;
|
||||
} else {
|
||||
sve_guest = false;
|
||||
sve_host = false;
|
||||
vhe = has_vhe();
|
||||
}
|
||||
|
||||
hsr_ec = kvm_vcpu_trap_get_class(vcpu);
|
||||
if (hsr_ec != ESR_ELx_EC_FP_ASIMD &&
|
||||
hsr_ec != ESR_ELx_EC_SVE)
|
||||
return false;
|
||||
|
||||
/* Don't handle SVE traps for non-SVE vcpus here: */
|
||||
if (!sve_guest)
|
||||
if (hsr_ec != ESR_ELx_EC_FP_ASIMD)
|
||||
return false;
|
||||
|
||||
/* Valid trap. Switch the context: */
|
||||
|
||||
if (vhe) {
|
||||
u64 reg = read_sysreg(cpacr_el1) | CPACR_EL1_FPEN;
|
||||
|
||||
if (sve_guest)
|
||||
reg |= CPACR_EL1_ZEN;
|
||||
|
||||
write_sysreg(reg, cpacr_el1);
|
||||
} else {
|
||||
write_sysreg(read_sysreg(cptr_el2) & ~(u64)CPTR_EL2_TFP,
|
||||
cptr_el2);
|
||||
}
|
||||
|
||||
isb();
|
||||
|
||||
if (vcpu->arch.flags & KVM_ARM64_FP_HOST) {
|
||||
/*
|
||||
* In the SVE case, VHE is assumed: it is enforced by
|
||||
* Kconfig and kvm_arch_init().
|
||||
*/
|
||||
if (sve_host) {
|
||||
struct thread_struct *thread = container_of(
|
||||
vcpu->arch.host_fpsimd_state,
|
||||
struct thread_struct, uw.fpsimd_state);
|
||||
|
||||
sve_save_state(sve_pffr(thread),
|
||||
&vcpu->arch.host_fpsimd_state->fpsr);
|
||||
} else {
|
||||
__fpsimd_save_state(vcpu->arch.host_fpsimd_state);
|
||||
}
|
||||
|
||||
vcpu->arch.flags &= ~KVM_ARM64_FP_HOST;
|
||||
}
|
||||
|
||||
if (sve_guest) {
|
||||
sve_load_state(vcpu_sve_pffr(vcpu),
|
||||
&vcpu->arch.ctxt.gp_regs.fp_regs.fpsr,
|
||||
sve_vq_from_vl(vcpu->arch.sve_max_vl) - 1);
|
||||
write_sysreg_s(vcpu->arch.ctxt.sys_regs[ZCR_EL1], SYS_ZCR_EL12);
|
||||
} else {
|
||||
__fpsimd_restore_state(&vcpu->arch.ctxt.gp_regs.fp_regs);
|
||||
}
|
||||
|
||||
/* Skip restoring fpexc32 for AArch64 guests */
|
||||
if (!(read_sysreg(hcr_el2) & HCR_RW))
|
||||
write_sysreg(vcpu->arch.ctxt.sys_regs[FPEXC32_EL2],
|
||||
fpexc32_el2);
|
||||
|
||||
vcpu->arch.flags |= KVM_ARM64_FP_ENABLED;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __hyp_text handle_tx2_tvm(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 sysreg = esr_sys64_to_sysreg(kvm_vcpu_get_hsr(vcpu));
|
||||
int rt = kvm_vcpu_sys_get_rt(vcpu);
|
||||
u64 val = vcpu_get_reg(vcpu, rt);
|
||||
|
||||
/*
|
||||
* The normal sysreg handling code expects to see the traps,
|
||||
* let's not do anything here.
|
||||
*/
|
||||
if (vcpu->arch.hcr_el2 & HCR_TVM)
|
||||
return false;
|
||||
|
||||
switch (sysreg) {
|
||||
case SYS_SCTLR_EL1:
|
||||
write_sysreg_el1(val, SYS_SCTLR);
|
||||
break;
|
||||
case SYS_TTBR0_EL1:
|
||||
write_sysreg_el1(val, SYS_TTBR0);
|
||||
break;
|
||||
case SYS_TTBR1_EL1:
|
||||
write_sysreg_el1(val, SYS_TTBR1);
|
||||
break;
|
||||
case SYS_TCR_EL1:
|
||||
write_sysreg_el1(val, SYS_TCR);
|
||||
break;
|
||||
case SYS_ESR_EL1:
|
||||
write_sysreg_el1(val, SYS_ESR);
|
||||
break;
|
||||
case SYS_FAR_EL1:
|
||||
write_sysreg_el1(val, SYS_FAR);
|
||||
break;
|
||||
case SYS_AFSR0_EL1:
|
||||
write_sysreg_el1(val, SYS_AFSR0);
|
||||
break;
|
||||
case SYS_AFSR1_EL1:
|
||||
write_sysreg_el1(val, SYS_AFSR1);
|
||||
break;
|
||||
case SYS_MAIR_EL1:
|
||||
write_sysreg_el1(val, SYS_MAIR);
|
||||
break;
|
||||
case SYS_AMAIR_EL1:
|
||||
write_sysreg_el1(val, SYS_AMAIR);
|
||||
break;
|
||||
case SYS_CONTEXTIDR_EL1:
|
||||
write_sysreg_el1(val, SYS_CONTEXTIDR);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
__kvm_skip_instr(vcpu);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __hyp_text esr_is_ptrauth_trap(u32 esr)
|
||||
{
|
||||
u32 ec = ESR_ELx_EC(esr);
|
||||
|
||||
if (ec == ESR_ELx_EC_PAC)
|
||||
return true;
|
||||
|
||||
if (ec != ESR_ELx_EC_SYS64)
|
||||
return false;
|
||||
|
||||
switch (esr_sys64_to_sysreg(esr)) {
|
||||
case SYS_APIAKEYLO_EL1:
|
||||
case SYS_APIAKEYHI_EL1:
|
||||
case SYS_APIBKEYLO_EL1:
|
||||
case SYS_APIBKEYHI_EL1:
|
||||
case SYS_APDAKEYLO_EL1:
|
||||
case SYS_APDAKEYHI_EL1:
|
||||
case SYS_APDBKEYLO_EL1:
|
||||
case SYS_APDBKEYHI_EL1:
|
||||
case SYS_APGAKEYLO_EL1:
|
||||
case SYS_APGAKEYHI_EL1:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#define __ptrauth_save_key(regs, key) \
|
||||
({ \
|
||||
regs[key ## KEYLO_EL1] = read_sysreg_s(SYS_ ## key ## KEYLO_EL1); \
|
||||
regs[key ## KEYHI_EL1] = read_sysreg_s(SYS_ ## key ## KEYHI_EL1); \
|
||||
})
|
||||
|
||||
static bool __hyp_text __hyp_handle_ptrauth(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_cpu_context *ctxt;
|
||||
u64 val;
|
||||
|
||||
if (!vcpu_has_ptrauth(vcpu) ||
|
||||
!esr_is_ptrauth_trap(kvm_vcpu_get_hsr(vcpu)))
|
||||
return false;
|
||||
|
||||
ctxt = &__hyp_this_cpu_ptr(kvm_host_data)->host_ctxt;
|
||||
__ptrauth_save_key(ctxt->sys_regs, APIA);
|
||||
__ptrauth_save_key(ctxt->sys_regs, APIB);
|
||||
__ptrauth_save_key(ctxt->sys_regs, APDA);
|
||||
__ptrauth_save_key(ctxt->sys_regs, APDB);
|
||||
__ptrauth_save_key(ctxt->sys_regs, APGA);
|
||||
|
||||
vcpu_ptrauth_enable(vcpu);
|
||||
|
||||
val = read_sysreg(hcr_el2);
|
||||
val |= (HCR_API | HCR_APK);
|
||||
write_sysreg(val, hcr_el2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true when we were able to fixup the guest exit and should return to
|
||||
* the guest, false when we should restore the host state and return to the
|
||||
* main run loop.
|
||||
*/
|
||||
static bool __hyp_text fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
{
|
||||
if (ARM_EXCEPTION_CODE(*exit_code) != ARM_EXCEPTION_IRQ)
|
||||
vcpu->arch.fault.esr_el2 = read_sysreg_el2(SYS_ESR);
|
||||
|
||||
/*
|
||||
* We're using the raw exception code in order to only process
|
||||
* the trap if no SError is pending. We will come back to the
|
||||
* same PC once the SError has been injected, and replay the
|
||||
* trapping instruction.
|
||||
*/
|
||||
if (*exit_code != ARM_EXCEPTION_TRAP)
|
||||
goto exit;
|
||||
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_CAVIUM_TX2_219_TVM) &&
|
||||
kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_SYS64 &&
|
||||
handle_tx2_tvm(vcpu))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* We trap the first access to the FP/SIMD to save the host context
|
||||
* and restore the guest context lazily.
|
||||
* If FP/SIMD is not implemented, handle the trap and inject an
|
||||
* undefined instruction exception to the guest.
|
||||
* Similarly for trapped SVE accesses.
|
||||
*/
|
||||
if (__hyp_handle_fpsimd(vcpu))
|
||||
return true;
|
||||
|
||||
if (__hyp_handle_ptrauth(vcpu))
|
||||
return true;
|
||||
|
||||
if (!__populate_fault_info(vcpu))
|
||||
return true;
|
||||
|
||||
if (static_branch_unlikely(&vgic_v2_cpuif_trap)) {
|
||||
bool valid;
|
||||
|
||||
valid = kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_DABT_LOW &&
|
||||
kvm_vcpu_trap_get_fault_type(vcpu) == FSC_FAULT &&
|
||||
kvm_vcpu_dabt_isvalid(vcpu) &&
|
||||
!kvm_vcpu_dabt_isextabt(vcpu) &&
|
||||
!kvm_vcpu_dabt_iss1tw(vcpu);
|
||||
|
||||
if (valid) {
|
||||
int ret = __vgic_v2_perform_cpuif_access(vcpu);
|
||||
|
||||
if (ret == 1)
|
||||
return true;
|
||||
|
||||
/* Promote an illegal access to an SError.*/
|
||||
if (ret == -1)
|
||||
*exit_code = ARM_EXCEPTION_EL1_SERROR;
|
||||
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (static_branch_unlikely(&vgic_v3_cpuif_trap) &&
|
||||
(kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_SYS64 ||
|
||||
kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_CP15_32)) {
|
||||
int ret = __vgic_v3_perform_cpuif_access(vcpu);
|
||||
|
||||
if (ret == 1)
|
||||
return true;
|
||||
}
|
||||
|
||||
exit:
|
||||
/* Return to the host kernel and handle the exit */
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool __hyp_text __needs_ssbd_off(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (!cpus_have_final_cap(ARM64_SSBD))
|
||||
return false;
|
||||
|
||||
return !(vcpu->arch.workaround_flags & VCPU_WORKAROUND_2_FLAG);
|
||||
}
|
||||
|
||||
static void __hyp_text __set_guest_arch_workaround_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
#ifdef CONFIG_ARM64_SSBD
|
||||
/*
|
||||
* The host runs with the workaround always present. If the
|
||||
* guest wants it disabled, so be it...
|
||||
*/
|
||||
if (__needs_ssbd_off(vcpu) &&
|
||||
__hyp_this_cpu_read(arm64_ssbd_callback_required))
|
||||
arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_2, 0, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __hyp_text __set_host_arch_workaround_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
#ifdef CONFIG_ARM64_SSBD
|
||||
/*
|
||||
* If the guest has disabled the workaround, bring it back on.
|
||||
*/
|
||||
if (__needs_ssbd_off(vcpu) &&
|
||||
__hyp_this_cpu_read(arm64_ssbd_callback_required))
|
||||
arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_2, 1, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable host events, enable guest events
|
||||
*/
|
||||
static bool __hyp_text __pmu_switch_to_guest(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
struct kvm_host_data *host;
|
||||
struct kvm_pmu_events *pmu;
|
||||
|
||||
host = container_of(host_ctxt, struct kvm_host_data, host_ctxt);
|
||||
pmu = &host->pmu_events;
|
||||
|
||||
if (pmu->events_host)
|
||||
write_sysreg(pmu->events_host, pmcntenclr_el0);
|
||||
|
||||
if (pmu->events_guest)
|
||||
write_sysreg(pmu->events_guest, pmcntenset_el0);
|
||||
|
||||
return (pmu->events_host || pmu->events_guest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable guest events, enable host events
|
||||
*/
|
||||
static void __hyp_text __pmu_switch_to_host(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
struct kvm_host_data *host;
|
||||
struct kvm_pmu_events *pmu;
|
||||
|
||||
host = container_of(host_ctxt, struct kvm_host_data, host_ctxt);
|
||||
pmu = &host->pmu_events;
|
||||
|
||||
if (pmu->events_guest)
|
||||
write_sysreg(pmu->events_guest, pmcntenclr_el0);
|
||||
|
||||
if (pmu->events_host)
|
||||
write_sysreg(pmu->events_host, pmcntenset_el0);
|
||||
}
|
||||
|
||||
/* Switch to the guest for VHE systems running in EL2 */
|
||||
static int __kvm_vcpu_run_vhe(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_cpu_context *host_ctxt;
|
||||
struct kvm_cpu_context *guest_ctxt;
|
||||
u64 exit_code;
|
||||
|
||||
host_ctxt = &__hyp_this_cpu_ptr(kvm_host_data)->host_ctxt;
|
||||
host_ctxt->__hyp_running_vcpu = vcpu;
|
||||
guest_ctxt = &vcpu->arch.ctxt;
|
||||
|
||||
sysreg_save_host_state_vhe(host_ctxt);
|
||||
|
||||
/*
|
||||
* ARM erratum 1165522 requires us to configure both stage 1 and
|
||||
* stage 2 translation for the guest context before we clear
|
||||
* HCR_EL2.TGE.
|
||||
*
|
||||
* We have already configured the guest's stage 1 translation in
|
||||
* kvm_vcpu_load_sysregs above. We must now call __activate_vm
|
||||
* before __activate_traps, because __activate_vm configures
|
||||
* stage 2 translation, and __activate_traps clear HCR_EL2.TGE
|
||||
* (among other things).
|
||||
*/
|
||||
__activate_vm(vcpu->kvm);
|
||||
__activate_traps(vcpu);
|
||||
|
||||
sysreg_restore_guest_state_vhe(guest_ctxt);
|
||||
__debug_switch_to_guest(vcpu);
|
||||
|
||||
__set_guest_arch_workaround_state(vcpu);
|
||||
|
||||
do {
|
||||
/* Jump in the fire! */
|
||||
exit_code = __guest_enter(vcpu, host_ctxt);
|
||||
|
||||
/* And we're baaack! */
|
||||
} while (fixup_guest_exit(vcpu, &exit_code));
|
||||
|
||||
__set_host_arch_workaround_state(vcpu);
|
||||
|
||||
sysreg_save_guest_state_vhe(guest_ctxt);
|
||||
|
||||
__deactivate_traps(vcpu);
|
||||
|
||||
sysreg_restore_host_state_vhe(host_ctxt);
|
||||
|
||||
if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED)
|
||||
__fpsimd_save_fpexc32(vcpu);
|
||||
|
||||
__debug_switch_to_host(vcpu);
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
NOKPROBE_SYMBOL(__kvm_vcpu_run_vhe);
|
||||
|
||||
int kvm_vcpu_run_vhe(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
local_daif_mask();
|
||||
|
||||
/*
|
||||
* Having IRQs masked via PMR when entering the guest means the GIC
|
||||
* will not signal the CPU of interrupts of lower priority, and the
|
||||
* only way to get out will be via guest exceptions.
|
||||
* Naturally, we want to avoid this.
|
||||
*
|
||||
* local_daif_mask() already sets GIC_PRIO_PSR_I_SET, we just need a
|
||||
* dsb to ensure the redistributor is forwards EL2 IRQs to the CPU.
|
||||
*/
|
||||
pmr_sync();
|
||||
|
||||
ret = __kvm_vcpu_run_vhe(vcpu);
|
||||
|
||||
/*
|
||||
* local_daif_restore() takes care to properly restore PSTATE.DAIF
|
||||
* and the GIC PMR if the host is using IRQ priorities.
|
||||
*/
|
||||
local_daif_restore(DAIF_PROCCTX_NOIRQ);
|
||||
|
||||
/*
|
||||
* When we exit from the guest we change a number of CPU configuration
|
||||
* parameters, such as traps. Make sure these changes take effect
|
||||
* before running the host or additional guests.
|
||||
*/
|
||||
isb();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Switch to the guest for legacy non-VHE systems */
|
||||
int __hyp_text __kvm_vcpu_run_nvhe(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_cpu_context *host_ctxt;
|
||||
struct kvm_cpu_context *guest_ctxt;
|
||||
bool pmu_switch_needed;
|
||||
u64 exit_code;
|
||||
|
||||
/*
|
||||
* Having IRQs masked via PMR when entering the guest means the GIC
|
||||
* will not signal the CPU of interrupts of lower priority, and the
|
||||
* only way to get out will be via guest exceptions.
|
||||
* Naturally, we want to avoid this.
|
||||
*/
|
||||
if (system_uses_irq_prio_masking()) {
|
||||
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);
|
||||
pmr_sync();
|
||||
}
|
||||
|
||||
vcpu = kern_hyp_va(vcpu);
|
||||
|
||||
host_ctxt = &__hyp_this_cpu_ptr(kvm_host_data)->host_ctxt;
|
||||
host_ctxt->__hyp_running_vcpu = vcpu;
|
||||
guest_ctxt = &vcpu->arch.ctxt;
|
||||
|
||||
pmu_switch_needed = __pmu_switch_to_guest(host_ctxt);
|
||||
|
||||
__sysreg_save_state_nvhe(host_ctxt);
|
||||
|
||||
/*
|
||||
* We must restore the 32-bit state before the sysregs, thanks
|
||||
* to erratum #852523 (Cortex-A57) or #853709 (Cortex-A72).
|
||||
*
|
||||
* Also, and in order to be able to deal with erratum #1319537 (A57)
|
||||
* and #1319367 (A72), we must ensure that all VM-related sysreg are
|
||||
* restored before we enable S2 translation.
|
||||
*/
|
||||
__sysreg32_restore_state(vcpu);
|
||||
__sysreg_restore_state_nvhe(guest_ctxt);
|
||||
|
||||
__activate_vm(kern_hyp_va(vcpu->kvm));
|
||||
__activate_traps(vcpu);
|
||||
|
||||
__hyp_vgic_restore_state(vcpu);
|
||||
__timer_enable_traps(vcpu);
|
||||
|
||||
__debug_switch_to_guest(vcpu);
|
||||
|
||||
__set_guest_arch_workaround_state(vcpu);
|
||||
|
||||
do {
|
||||
/* Jump in the fire! */
|
||||
exit_code = __guest_enter(vcpu, host_ctxt);
|
||||
|
||||
/* And we're baaack! */
|
||||
} while (fixup_guest_exit(vcpu, &exit_code));
|
||||
|
||||
__set_host_arch_workaround_state(vcpu);
|
||||
|
||||
__sysreg_save_state_nvhe(guest_ctxt);
|
||||
__sysreg32_save_state(vcpu);
|
||||
__timer_disable_traps(vcpu);
|
||||
__hyp_vgic_save_state(vcpu);
|
||||
|
||||
__deactivate_traps(vcpu);
|
||||
__deactivate_vm(vcpu);
|
||||
|
||||
__sysreg_restore_state_nvhe(host_ctxt);
|
||||
|
||||
if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED)
|
||||
__fpsimd_save_fpexc32(vcpu);
|
||||
|
||||
/*
|
||||
* This must come after restoring the host sysregs, since a non-VHE
|
||||
* system may enable SPE here and make use of the TTBRs.
|
||||
*/
|
||||
__debug_switch_to_host(vcpu);
|
||||
|
||||
if (pmu_switch_needed)
|
||||
__pmu_switch_to_host(host_ctxt);
|
||||
|
||||
/* Returning to host will clear PSR.I, remask PMR if needed */
|
||||
if (system_uses_irq_prio_masking())
|
||||
gic_write_pmr(GIC_PRIO_IRQOFF);
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
static const char __hyp_panic_string[] = "HYP panic:\nPS:%08llx PC:%016llx ESR:%08llx\nFAR:%016llx HPFAR:%016llx PAR:%016llx\nVCPU:%p\n";
|
||||
|
||||
static void __hyp_text __hyp_call_panic_nvhe(u64 spsr, u64 elr, u64 par,
|
||||
struct kvm_cpu_context *__host_ctxt)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
unsigned long str_va;
|
||||
|
||||
vcpu = __host_ctxt->__hyp_running_vcpu;
|
||||
|
||||
if (read_sysreg(vttbr_el2)) {
|
||||
__timer_disable_traps(vcpu);
|
||||
__deactivate_traps(vcpu);
|
||||
__deactivate_vm(vcpu);
|
||||
__sysreg_restore_state_nvhe(__host_ctxt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Force the panic string to be loaded from the literal pool,
|
||||
* making sure it is a kernel address and not a PC-relative
|
||||
* reference.
|
||||
*/
|
||||
asm volatile("ldr %0, =__hyp_panic_string" : "=r" (str_va));
|
||||
|
||||
__hyp_do_panic(str_va,
|
||||
spsr, elr,
|
||||
read_sysreg(esr_el2), read_sysreg_el2(SYS_FAR),
|
||||
read_sysreg(hpfar_el2), par, vcpu);
|
||||
}
|
||||
|
||||
static void __hyp_call_panic_vhe(u64 spsr, u64 elr, u64 par,
|
||||
struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
vcpu = host_ctxt->__hyp_running_vcpu;
|
||||
|
||||
__deactivate_traps(vcpu);
|
||||
sysreg_restore_host_state_vhe(host_ctxt);
|
||||
|
||||
panic(__hyp_panic_string,
|
||||
spsr, elr,
|
||||
read_sysreg_el2(SYS_ESR), read_sysreg_el2(SYS_FAR),
|
||||
read_sysreg(hpfar_el2), par, vcpu);
|
||||
}
|
||||
NOKPROBE_SYMBOL(__hyp_call_panic_vhe);
|
||||
|
||||
void __hyp_text __noreturn hyp_panic(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
u64 spsr = read_sysreg_el2(SYS_SPSR);
|
||||
u64 elr = read_sysreg_el2(SYS_ELR);
|
||||
u64 par = read_sysreg(par_el1);
|
||||
|
||||
if (!has_vhe())
|
||||
__hyp_call_panic_nvhe(spsr, elr, par, host_ctxt);
|
||||
else
|
||||
__hyp_call_panic_vhe(spsr, elr, par, host_ctxt);
|
||||
|
||||
unreachable();
|
||||
}
|
|
@ -1,333 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2012-2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include <asm/kprobes.h>
|
||||
#include <asm/kvm_asm.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_hyp.h>
|
||||
|
||||
/*
|
||||
* Non-VHE: Both host and guest must save everything.
|
||||
*
|
||||
* VHE: Host and guest must save mdscr_el1 and sp_el0 (and the PC and
|
||||
* pstate, which are handled as part of the el2 return state) on every
|
||||
* switch (sp_el0 is being dealt with in the assembly code).
|
||||
* tpidr_el0 and tpidrro_el0 only need to be switched when going
|
||||
* to host userspace or a different VCPU. EL1 registers only need to be
|
||||
* switched when potentially going to run a different VCPU. The latter two
|
||||
* classes are handled as part of kvm_arch_vcpu_load and kvm_arch_vcpu_put.
|
||||
*/
|
||||
|
||||
static void __hyp_text __sysreg_save_common_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
ctxt->sys_regs[MDSCR_EL1] = read_sysreg(mdscr_el1);
|
||||
}
|
||||
|
||||
static void __hyp_text __sysreg_save_user_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
ctxt->sys_regs[TPIDR_EL0] = read_sysreg(tpidr_el0);
|
||||
ctxt->sys_regs[TPIDRRO_EL0] = read_sysreg(tpidrro_el0);
|
||||
}
|
||||
|
||||
static void __hyp_text __sysreg_save_el1_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
ctxt->sys_regs[CSSELR_EL1] = read_sysreg(csselr_el1);
|
||||
ctxt->sys_regs[SCTLR_EL1] = read_sysreg_el1(SYS_SCTLR);
|
||||
ctxt->sys_regs[CPACR_EL1] = read_sysreg_el1(SYS_CPACR);
|
||||
ctxt->sys_regs[TTBR0_EL1] = read_sysreg_el1(SYS_TTBR0);
|
||||
ctxt->sys_regs[TTBR1_EL1] = read_sysreg_el1(SYS_TTBR1);
|
||||
ctxt->sys_regs[TCR_EL1] = read_sysreg_el1(SYS_TCR);
|
||||
ctxt->sys_regs[ESR_EL1] = read_sysreg_el1(SYS_ESR);
|
||||
ctxt->sys_regs[AFSR0_EL1] = read_sysreg_el1(SYS_AFSR0);
|
||||
ctxt->sys_regs[AFSR1_EL1] = read_sysreg_el1(SYS_AFSR1);
|
||||
ctxt->sys_regs[FAR_EL1] = read_sysreg_el1(SYS_FAR);
|
||||
ctxt->sys_regs[MAIR_EL1] = read_sysreg_el1(SYS_MAIR);
|
||||
ctxt->sys_regs[VBAR_EL1] = read_sysreg_el1(SYS_VBAR);
|
||||
ctxt->sys_regs[CONTEXTIDR_EL1] = read_sysreg_el1(SYS_CONTEXTIDR);
|
||||
ctxt->sys_regs[AMAIR_EL1] = read_sysreg_el1(SYS_AMAIR);
|
||||
ctxt->sys_regs[CNTKCTL_EL1] = read_sysreg_el1(SYS_CNTKCTL);
|
||||
ctxt->sys_regs[PAR_EL1] = read_sysreg(par_el1);
|
||||
ctxt->sys_regs[TPIDR_EL1] = read_sysreg(tpidr_el1);
|
||||
|
||||
ctxt->gp_regs.sp_el1 = read_sysreg(sp_el1);
|
||||
ctxt->gp_regs.elr_el1 = read_sysreg_el1(SYS_ELR);
|
||||
ctxt->gp_regs.spsr[KVM_SPSR_EL1]= read_sysreg_el1(SYS_SPSR);
|
||||
}
|
||||
|
||||
static void __hyp_text __sysreg_save_el2_return_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
ctxt->gp_regs.regs.pc = read_sysreg_el2(SYS_ELR);
|
||||
ctxt->gp_regs.regs.pstate = read_sysreg_el2(SYS_SPSR);
|
||||
|
||||
if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN))
|
||||
ctxt->sys_regs[DISR_EL1] = read_sysreg_s(SYS_VDISR_EL2);
|
||||
}
|
||||
|
||||
void __hyp_text __sysreg_save_state_nvhe(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
__sysreg_save_el1_state(ctxt);
|
||||
__sysreg_save_common_state(ctxt);
|
||||
__sysreg_save_user_state(ctxt);
|
||||
__sysreg_save_el2_return_state(ctxt);
|
||||
}
|
||||
|
||||
void sysreg_save_host_state_vhe(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
__sysreg_save_common_state(ctxt);
|
||||
}
|
||||
NOKPROBE_SYMBOL(sysreg_save_host_state_vhe);
|
||||
|
||||
void sysreg_save_guest_state_vhe(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
__sysreg_save_common_state(ctxt);
|
||||
__sysreg_save_el2_return_state(ctxt);
|
||||
}
|
||||
NOKPROBE_SYMBOL(sysreg_save_guest_state_vhe);
|
||||
|
||||
static void __hyp_text __sysreg_restore_common_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
write_sysreg(ctxt->sys_regs[MDSCR_EL1], mdscr_el1);
|
||||
}
|
||||
|
||||
static void __hyp_text __sysreg_restore_user_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
write_sysreg(ctxt->sys_regs[TPIDR_EL0], tpidr_el0);
|
||||
write_sysreg(ctxt->sys_regs[TPIDRRO_EL0], tpidrro_el0);
|
||||
}
|
||||
|
||||
static void __hyp_text __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
write_sysreg(ctxt->sys_regs[MPIDR_EL1], vmpidr_el2);
|
||||
write_sysreg(ctxt->sys_regs[CSSELR_EL1], csselr_el1);
|
||||
|
||||
if (has_vhe() ||
|
||||
!cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||
write_sysreg_el1(ctxt->sys_regs[SCTLR_EL1], SYS_SCTLR);
|
||||
write_sysreg_el1(ctxt->sys_regs[TCR_EL1], SYS_TCR);
|
||||
} else if (!ctxt->__hyp_running_vcpu) {
|
||||
/*
|
||||
* Must only be done for guest registers, hence the context
|
||||
* test. We're coming from the host, so SCTLR.M is already
|
||||
* set. Pairs with __activate_traps_nvhe().
|
||||
*/
|
||||
write_sysreg_el1((ctxt->sys_regs[TCR_EL1] |
|
||||
TCR_EPD1_MASK | TCR_EPD0_MASK),
|
||||
SYS_TCR);
|
||||
isb();
|
||||
}
|
||||
|
||||
write_sysreg_el1(ctxt->sys_regs[CPACR_EL1], SYS_CPACR);
|
||||
write_sysreg_el1(ctxt->sys_regs[TTBR0_EL1], SYS_TTBR0);
|
||||
write_sysreg_el1(ctxt->sys_regs[TTBR1_EL1], SYS_TTBR1);
|
||||
write_sysreg_el1(ctxt->sys_regs[ESR_EL1], SYS_ESR);
|
||||
write_sysreg_el1(ctxt->sys_regs[AFSR0_EL1], SYS_AFSR0);
|
||||
write_sysreg_el1(ctxt->sys_regs[AFSR1_EL1], SYS_AFSR1);
|
||||
write_sysreg_el1(ctxt->sys_regs[FAR_EL1], SYS_FAR);
|
||||
write_sysreg_el1(ctxt->sys_regs[MAIR_EL1], SYS_MAIR);
|
||||
write_sysreg_el1(ctxt->sys_regs[VBAR_EL1], SYS_VBAR);
|
||||
write_sysreg_el1(ctxt->sys_regs[CONTEXTIDR_EL1],SYS_CONTEXTIDR);
|
||||
write_sysreg_el1(ctxt->sys_regs[AMAIR_EL1], SYS_AMAIR);
|
||||
write_sysreg_el1(ctxt->sys_regs[CNTKCTL_EL1], SYS_CNTKCTL);
|
||||
write_sysreg(ctxt->sys_regs[PAR_EL1], par_el1);
|
||||
write_sysreg(ctxt->sys_regs[TPIDR_EL1], tpidr_el1);
|
||||
|
||||
if (!has_vhe() &&
|
||||
cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT) &&
|
||||
ctxt->__hyp_running_vcpu) {
|
||||
/*
|
||||
* Must only be done for host registers, hence the context
|
||||
* test. Pairs with __deactivate_traps_nvhe().
|
||||
*/
|
||||
isb();
|
||||
/*
|
||||
* At this stage, and thanks to the above isb(), S2 is
|
||||
* deconfigured and disabled. We can now restore the host's
|
||||
* S1 configuration: SCTLR, and only then TCR.
|
||||
*/
|
||||
write_sysreg_el1(ctxt->sys_regs[SCTLR_EL1], SYS_SCTLR);
|
||||
isb();
|
||||
write_sysreg_el1(ctxt->sys_regs[TCR_EL1], SYS_TCR);
|
||||
}
|
||||
|
||||
write_sysreg(ctxt->gp_regs.sp_el1, sp_el1);
|
||||
write_sysreg_el1(ctxt->gp_regs.elr_el1, SYS_ELR);
|
||||
write_sysreg_el1(ctxt->gp_regs.spsr[KVM_SPSR_EL1],SYS_SPSR);
|
||||
}
|
||||
|
||||
static void __hyp_text
|
||||
__sysreg_restore_el2_return_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
u64 pstate = ctxt->gp_regs.regs.pstate;
|
||||
u64 mode = pstate & PSR_AA32_MODE_MASK;
|
||||
|
||||
/*
|
||||
* Safety check to ensure we're setting the CPU up to enter the guest
|
||||
* in a less privileged mode.
|
||||
*
|
||||
* If we are attempting a return to EL2 or higher in AArch64 state,
|
||||
* program SPSR_EL2 with M=EL2h and the IL bit set which ensures that
|
||||
* we'll take an illegal exception state exception immediately after
|
||||
* the ERET to the guest. Attempts to return to AArch32 Hyp will
|
||||
* result in an illegal exception return because EL2's execution state
|
||||
* is determined by SCR_EL3.RW.
|
||||
*/
|
||||
if (!(mode & PSR_MODE32_BIT) && mode >= PSR_MODE_EL2t)
|
||||
pstate = PSR_MODE_EL2h | PSR_IL_BIT;
|
||||
|
||||
write_sysreg_el2(ctxt->gp_regs.regs.pc, SYS_ELR);
|
||||
write_sysreg_el2(pstate, SYS_SPSR);
|
||||
|
||||
if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN))
|
||||
write_sysreg_s(ctxt->sys_regs[DISR_EL1], SYS_VDISR_EL2);
|
||||
}
|
||||
|
||||
void __hyp_text __sysreg_restore_state_nvhe(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
__sysreg_restore_el1_state(ctxt);
|
||||
__sysreg_restore_common_state(ctxt);
|
||||
__sysreg_restore_user_state(ctxt);
|
||||
__sysreg_restore_el2_return_state(ctxt);
|
||||
}
|
||||
|
||||
void sysreg_restore_host_state_vhe(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
__sysreg_restore_common_state(ctxt);
|
||||
}
|
||||
NOKPROBE_SYMBOL(sysreg_restore_host_state_vhe);
|
||||
|
||||
void sysreg_restore_guest_state_vhe(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
__sysreg_restore_common_state(ctxt);
|
||||
__sysreg_restore_el2_return_state(ctxt);
|
||||
}
|
||||
NOKPROBE_SYMBOL(sysreg_restore_guest_state_vhe);
|
||||
|
||||
void __hyp_text __sysreg32_save_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 *spsr, *sysreg;
|
||||
|
||||
if (!vcpu_el1_is_32bit(vcpu))
|
||||
return;
|
||||
|
||||
spsr = vcpu->arch.ctxt.gp_regs.spsr;
|
||||
sysreg = vcpu->arch.ctxt.sys_regs;
|
||||
|
||||
spsr[KVM_SPSR_ABT] = read_sysreg(spsr_abt);
|
||||
spsr[KVM_SPSR_UND] = read_sysreg(spsr_und);
|
||||
spsr[KVM_SPSR_IRQ] = read_sysreg(spsr_irq);
|
||||
spsr[KVM_SPSR_FIQ] = read_sysreg(spsr_fiq);
|
||||
|
||||
sysreg[DACR32_EL2] = read_sysreg(dacr32_el2);
|
||||
sysreg[IFSR32_EL2] = read_sysreg(ifsr32_el2);
|
||||
|
||||
if (has_vhe() || vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY)
|
||||
sysreg[DBGVCR32_EL2] = read_sysreg(dbgvcr32_el2);
|
||||
}
|
||||
|
||||
void __hyp_text __sysreg32_restore_state(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 *spsr, *sysreg;
|
||||
|
||||
if (!vcpu_el1_is_32bit(vcpu))
|
||||
return;
|
||||
|
||||
spsr = vcpu->arch.ctxt.gp_regs.spsr;
|
||||
sysreg = vcpu->arch.ctxt.sys_regs;
|
||||
|
||||
write_sysreg(spsr[KVM_SPSR_ABT], spsr_abt);
|
||||
write_sysreg(spsr[KVM_SPSR_UND], spsr_und);
|
||||
write_sysreg(spsr[KVM_SPSR_IRQ], spsr_irq);
|
||||
write_sysreg(spsr[KVM_SPSR_FIQ], spsr_fiq);
|
||||
|
||||
write_sysreg(sysreg[DACR32_EL2], dacr32_el2);
|
||||
write_sysreg(sysreg[IFSR32_EL2], ifsr32_el2);
|
||||
|
||||
if (has_vhe() || vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY)
|
||||
write_sysreg(sysreg[DBGVCR32_EL2], dbgvcr32_el2);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_vcpu_load_sysregs - Load guest system registers to the physical CPU
|
||||
*
|
||||
* @vcpu: The VCPU pointer
|
||||
*
|
||||
* Load system registers that do not affect the host's execution, for
|
||||
* example EL1 system registers on a VHE system where the host kernel
|
||||
* runs at EL2. This function is called from KVM's vcpu_load() function
|
||||
* and loading system register state early avoids having to load them on
|
||||
* every entry to the VM.
|
||||
*/
|
||||
void kvm_vcpu_load_sysregs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_cpu_context *guest_ctxt = &vcpu->arch.ctxt;
|
||||
struct kvm_cpu_context *host_ctxt;
|
||||
|
||||
if (!has_vhe())
|
||||
return;
|
||||
|
||||
host_ctxt = &__hyp_this_cpu_ptr(kvm_host_data)->host_ctxt;
|
||||
__sysreg_save_user_state(host_ctxt);
|
||||
|
||||
/*
|
||||
* Load guest EL1 and user state
|
||||
*
|
||||
* We must restore the 32-bit state before the sysregs, thanks
|
||||
* to erratum #852523 (Cortex-A57) or #853709 (Cortex-A72).
|
||||
*/
|
||||
__sysreg32_restore_state(vcpu);
|
||||
__sysreg_restore_user_state(guest_ctxt);
|
||||
__sysreg_restore_el1_state(guest_ctxt);
|
||||
|
||||
vcpu->arch.sysregs_loaded_on_cpu = true;
|
||||
|
||||
activate_traps_vhe_load(vcpu);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_vcpu_put_sysregs - Restore host system registers to the physical CPU
|
||||
*
|
||||
* @vcpu: The VCPU pointer
|
||||
*
|
||||
* Save guest system registers that do not affect the host's execution, for
|
||||
* example EL1 system registers on a VHE system where the host kernel
|
||||
* runs at EL2. This function is called from KVM's vcpu_put() function
|
||||
* and deferring saving system register state until we're no longer running the
|
||||
* VCPU avoids having to save them on every exit from the VM.
|
||||
*/
|
||||
void kvm_vcpu_put_sysregs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_cpu_context *guest_ctxt = &vcpu->arch.ctxt;
|
||||
struct kvm_cpu_context *host_ctxt;
|
||||
|
||||
if (!has_vhe())
|
||||
return;
|
||||
|
||||
host_ctxt = &__hyp_this_cpu_ptr(kvm_host_data)->host_ctxt;
|
||||
deactivate_traps_vhe_put();
|
||||
|
||||
__sysreg_save_el1_state(guest_ctxt);
|
||||
__sysreg_save_user_state(guest_ctxt);
|
||||
__sysreg32_save_state(vcpu);
|
||||
|
||||
/* Restore host user state */
|
||||
__sysreg_restore_user_state(host_ctxt);
|
||||
|
||||
vcpu->arch.sysregs_loaded_on_cpu = false;
|
||||
}
|
||||
|
||||
void __hyp_text __kvm_enable_ssbs(void)
|
||||
{
|
||||
u64 tmp;
|
||||
|
||||
asm volatile(
|
||||
"mrs %0, sctlr_el2\n"
|
||||
"orr %0, %0, %1\n"
|
||||
"msr sctlr_el2, %0"
|
||||
: "=&r" (tmp) : "L" (SCTLR_ELx_DSSBS));
|
||||
}
|
|
@ -1,242 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <linux/irqflags.h>
|
||||
|
||||
#include <asm/kvm_hyp.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
struct tlb_inv_context {
|
||||
unsigned long flags;
|
||||
u64 tcr;
|
||||
u64 sctlr;
|
||||
};
|
||||
|
||||
static void __hyp_text __tlb_switch_to_guest_vhe(struct kvm *kvm,
|
||||
struct tlb_inv_context *cxt)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
local_irq_save(cxt->flags);
|
||||
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||
/*
|
||||
* For CPUs that are affected by ARM errata 1165522 or 1530923,
|
||||
* we cannot trust stage-1 to be in a correct state at that
|
||||
* point. Since we do not want to force a full load of the
|
||||
* vcpu state, we prevent the EL1 page-table walker to
|
||||
* allocate new TLBs. This is done by setting the EPD bits
|
||||
* in the TCR_EL1 register. We also need to prevent it to
|
||||
* allocate IPA->PA walks, so we enable the S1 MMU...
|
||||
*/
|
||||
val = cxt->tcr = read_sysreg_el1(SYS_TCR);
|
||||
val |= TCR_EPD1_MASK | TCR_EPD0_MASK;
|
||||
write_sysreg_el1(val, SYS_TCR);
|
||||
val = cxt->sctlr = read_sysreg_el1(SYS_SCTLR);
|
||||
val |= SCTLR_ELx_M;
|
||||
write_sysreg_el1(val, SYS_SCTLR);
|
||||
}
|
||||
|
||||
/*
|
||||
* With VHE enabled, we have HCR_EL2.{E2H,TGE} = {1,1}, and
|
||||
* most TLB operations target EL2/EL0. In order to affect the
|
||||
* guest TLBs (EL1/EL0), we need to change one of these two
|
||||
* bits. Changing E2H is impossible (goodbye TTBR1_EL2), so
|
||||
* let's flip TGE before executing the TLB operation.
|
||||
*
|
||||
* ARM erratum 1165522 requires some special handling (again),
|
||||
* as we need to make sure both stages of translation are in
|
||||
* place before clearing TGE. __load_guest_stage2() already
|
||||
* has an ISB in order to deal with this.
|
||||
*/
|
||||
__load_guest_stage2(kvm);
|
||||
val = read_sysreg(hcr_el2);
|
||||
val &= ~HCR_TGE;
|
||||
write_sysreg(val, hcr_el2);
|
||||
isb();
|
||||
}
|
||||
|
||||
static void __hyp_text __tlb_switch_to_guest_nvhe(struct kvm *kvm,
|
||||
struct tlb_inv_context *cxt)
|
||||
{
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||
u64 val;
|
||||
|
||||
/*
|
||||
* For CPUs that are affected by ARM 1319367, we need to
|
||||
* avoid a host Stage-1 walk while we have the guest's
|
||||
* VMID set in the VTTBR in order to invalidate TLBs.
|
||||
* We're guaranteed that the S1 MMU is enabled, so we can
|
||||
* simply set the EPD bits to avoid any further TLB fill.
|
||||
*/
|
||||
val = cxt->tcr = read_sysreg_el1(SYS_TCR);
|
||||
val |= TCR_EPD1_MASK | TCR_EPD0_MASK;
|
||||
write_sysreg_el1(val, SYS_TCR);
|
||||
isb();
|
||||
}
|
||||
|
||||
/* __load_guest_stage2() includes an ISB for the workaround. */
|
||||
__load_guest_stage2(kvm);
|
||||
asm(ALTERNATIVE("isb", "nop", ARM64_WORKAROUND_SPECULATIVE_AT));
|
||||
}
|
||||
|
||||
static void __hyp_text __tlb_switch_to_guest(struct kvm *kvm,
|
||||
struct tlb_inv_context *cxt)
|
||||
{
|
||||
if (has_vhe())
|
||||
__tlb_switch_to_guest_vhe(kvm, cxt);
|
||||
else
|
||||
__tlb_switch_to_guest_nvhe(kvm, cxt);
|
||||
}
|
||||
|
||||
static void __hyp_text __tlb_switch_to_host_vhe(struct kvm *kvm,
|
||||
struct tlb_inv_context *cxt)
|
||||
{
|
||||
/*
|
||||
* We're done with the TLB operation, let's restore the host's
|
||||
* view of HCR_EL2.
|
||||
*/
|
||||
write_sysreg(0, vttbr_el2);
|
||||
write_sysreg(HCR_HOST_VHE_FLAGS, hcr_el2);
|
||||
isb();
|
||||
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||
/* Restore the registers to what they were */
|
||||
write_sysreg_el1(cxt->tcr, SYS_TCR);
|
||||
write_sysreg_el1(cxt->sctlr, SYS_SCTLR);
|
||||
}
|
||||
|
||||
local_irq_restore(cxt->flags);
|
||||
}
|
||||
|
||||
static void __hyp_text __tlb_switch_to_host_nvhe(struct kvm *kvm,
|
||||
struct tlb_inv_context *cxt)
|
||||
{
|
||||
write_sysreg(0, vttbr_el2);
|
||||
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||
/* Ensure write of the host VMID */
|
||||
isb();
|
||||
/* Restore the host's TCR_EL1 */
|
||||
write_sysreg_el1(cxt->tcr, SYS_TCR);
|
||||
}
|
||||
}
|
||||
|
||||
static void __hyp_text __tlb_switch_to_host(struct kvm *kvm,
|
||||
struct tlb_inv_context *cxt)
|
||||
{
|
||||
if (has_vhe())
|
||||
__tlb_switch_to_host_vhe(kvm, cxt);
|
||||
else
|
||||
__tlb_switch_to_host_nvhe(kvm, cxt);
|
||||
}
|
||||
|
||||
void __hyp_text __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa)
|
||||
{
|
||||
struct tlb_inv_context cxt;
|
||||
|
||||
dsb(ishst);
|
||||
|
||||
/* Switch to requested VMID */
|
||||
kvm = kern_hyp_va(kvm);
|
||||
__tlb_switch_to_guest(kvm, &cxt);
|
||||
|
||||
/*
|
||||
* We could do so much better if we had the VA as well.
|
||||
* Instead, we invalidate Stage-2 for this IPA, and the
|
||||
* whole of Stage-1. Weep...
|
||||
*/
|
||||
ipa >>= 12;
|
||||
__tlbi(ipas2e1is, ipa);
|
||||
|
||||
/*
|
||||
* We have to ensure completion of the invalidation at Stage-2,
|
||||
* since a table walk on another CPU could refill a TLB with a
|
||||
* complete (S1 + S2) walk based on the old Stage-2 mapping if
|
||||
* the Stage-1 invalidation happened first.
|
||||
*/
|
||||
dsb(ish);
|
||||
__tlbi(vmalle1is);
|
||||
dsb(ish);
|
||||
isb();
|
||||
|
||||
/*
|
||||
* If the host is running at EL1 and we have a VPIPT I-cache,
|
||||
* then we must perform I-cache maintenance at EL2 in order for
|
||||
* it to have an effect on the guest. Since the guest cannot hit
|
||||
* I-cache lines allocated with a different VMID, we don't need
|
||||
* to worry about junk out of guest reset (we nuke the I-cache on
|
||||
* VMID rollover), but we do need to be careful when remapping
|
||||
* executable pages for the same guest. This can happen when KSM
|
||||
* takes a CoW fault on an executable page, copies the page into
|
||||
* a page that was previously mapped in the guest and then needs
|
||||
* to invalidate the guest view of the I-cache for that page
|
||||
* from EL1. To solve this, we invalidate the entire I-cache when
|
||||
* unmapping a page from a guest if we have a VPIPT I-cache but
|
||||
* the host is running at EL1. As above, we could do better if
|
||||
* we had the VA.
|
||||
*
|
||||
* The moral of this story is: if you have a VPIPT I-cache, then
|
||||
* you should be running with VHE enabled.
|
||||
*/
|
||||
if (!has_vhe() && icache_is_vpipt())
|
||||
__flush_icache_all();
|
||||
|
||||
__tlb_switch_to_host(kvm, &cxt);
|
||||
}
|
||||
|
||||
void __hyp_text __kvm_tlb_flush_vmid(struct kvm *kvm)
|
||||
{
|
||||
struct tlb_inv_context cxt;
|
||||
|
||||
dsb(ishst);
|
||||
|
||||
/* Switch to requested VMID */
|
||||
kvm = kern_hyp_va(kvm);
|
||||
__tlb_switch_to_guest(kvm, &cxt);
|
||||
|
||||
__tlbi(vmalls12e1is);
|
||||
dsb(ish);
|
||||
isb();
|
||||
|
||||
__tlb_switch_to_host(kvm, &cxt);
|
||||
}
|
||||
|
||||
void __hyp_text __kvm_tlb_flush_local_vmid(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm *kvm = kern_hyp_va(kern_hyp_va(vcpu)->kvm);
|
||||
struct tlb_inv_context cxt;
|
||||
|
||||
/* Switch to requested VMID */
|
||||
__tlb_switch_to_guest(kvm, &cxt);
|
||||
|
||||
__tlbi(vmalle1);
|
||||
dsb(nsh);
|
||||
isb();
|
||||
|
||||
__tlb_switch_to_host(kvm, &cxt);
|
||||
}
|
||||
|
||||
void __hyp_text __kvm_flush_vm_context(void)
|
||||
{
|
||||
dsb(ishst);
|
||||
__tlbi(alle1is);
|
||||
|
||||
/*
|
||||
* VIPT and PIPT caches are not affected by VMID, so no maintenance
|
||||
* is necessary across a VMID rollover.
|
||||
*
|
||||
* VPIPT caches constrain lookup and maintenance to the active VMID,
|
||||
* so we need to invalidate lines with a stale VMID to avoid an ABA
|
||||
* race after multiple rollovers.
|
||||
*
|
||||
*/
|
||||
if (icache_is_vpipt())
|
||||
asm volatile("ic ialluis");
|
||||
|
||||
dsb(ish);
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
#include <asm/kvm_hyp.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
|
||||
static bool __hyp_text __is_be(struct kvm_vcpu *vcpu)
|
||||
static bool __is_be(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (vcpu_mode_is_32bit(vcpu))
|
||||
return !!(read_sysreg_el2(SYS_SPSR) & PSR_AA32_E_BIT);
|
||||
|
@ -32,7 +32,7 @@ static bool __hyp_text __is_be(struct kvm_vcpu *vcpu)
|
|||
* 0: Not a GICV access
|
||||
* -1: Illegal GICV access successfully performed
|
||||
*/
|
||||
int __hyp_text __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu)
|
||||
int __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm *kvm = kern_hyp_va(vcpu->kvm);
|
||||
struct vgic_dist *vgic = &kvm->arch.vgic;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#define vtr_to_nr_pre_bits(v) ((((u32)(v) >> 26) & 7) + 1)
|
||||
#define vtr_to_nr_apr_regs(v) (1 << (vtr_to_nr_pre_bits(v) - 5))
|
||||
|
||||
static u64 __hyp_text __gic_v3_get_lr(unsigned int lr)
|
||||
static u64 __gic_v3_get_lr(unsigned int lr)
|
||||
{
|
||||
switch (lr & 0xf) {
|
||||
case 0:
|
||||
|
@ -56,7 +56,7 @@ static u64 __hyp_text __gic_v3_get_lr(unsigned int lr)
|
|||
unreachable();
|
||||
}
|
||||
|
||||
static void __hyp_text __gic_v3_set_lr(u64 val, int lr)
|
||||
static void __gic_v3_set_lr(u64 val, int lr)
|
||||
{
|
||||
switch (lr & 0xf) {
|
||||
case 0:
|
||||
|
@ -110,7 +110,7 @@ static void __hyp_text __gic_v3_set_lr(u64 val, int lr)
|
|||
}
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_write_ap0rn(u32 val, int n)
|
||||
static void __vgic_v3_write_ap0rn(u32 val, int n)
|
||||
{
|
||||
switch (n) {
|
||||
case 0:
|
||||
|
@ -128,7 +128,7 @@ static void __hyp_text __vgic_v3_write_ap0rn(u32 val, int n)
|
|||
}
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_write_ap1rn(u32 val, int n)
|
||||
static void __vgic_v3_write_ap1rn(u32 val, int n)
|
||||
{
|
||||
switch (n) {
|
||||
case 0:
|
||||
|
@ -146,7 +146,7 @@ static void __hyp_text __vgic_v3_write_ap1rn(u32 val, int n)
|
|||
}
|
||||
}
|
||||
|
||||
static u32 __hyp_text __vgic_v3_read_ap0rn(int n)
|
||||
static u32 __vgic_v3_read_ap0rn(int n)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
|
@ -170,7 +170,7 @@ static u32 __hyp_text __vgic_v3_read_ap0rn(int n)
|
|||
return val;
|
||||
}
|
||||
|
||||
static u32 __hyp_text __vgic_v3_read_ap1rn(int n)
|
||||
static u32 __vgic_v3_read_ap1rn(int n)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
|
@ -194,7 +194,7 @@ static u32 __hyp_text __vgic_v3_read_ap1rn(int n)
|
|||
return val;
|
||||
}
|
||||
|
||||
void __hyp_text __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if)
|
||||
void __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if)
|
||||
{
|
||||
u64 used_lrs = cpu_if->used_lrs;
|
||||
|
||||
|
@ -229,7 +229,7 @@ void __hyp_text __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if)
|
|||
}
|
||||
}
|
||||
|
||||
void __hyp_text __vgic_v3_restore_state(struct vgic_v3_cpu_if *cpu_if)
|
||||
void __vgic_v3_restore_state(struct vgic_v3_cpu_if *cpu_if)
|
||||
{
|
||||
u64 used_lrs = cpu_if->used_lrs;
|
||||
int i;
|
||||
|
@ -255,7 +255,7 @@ void __hyp_text __vgic_v3_restore_state(struct vgic_v3_cpu_if *cpu_if)
|
|||
}
|
||||
}
|
||||
|
||||
void __hyp_text __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if)
|
||||
void __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if)
|
||||
{
|
||||
/*
|
||||
* VFIQEn is RES1 if ICC_SRE_EL1.SRE is 1. This causes a
|
||||
|
@ -302,7 +302,7 @@ void __hyp_text __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if)
|
|||
write_gicreg(cpu_if->vgic_hcr, ICH_HCR_EL2);
|
||||
}
|
||||
|
||||
void __hyp_text __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if)
|
||||
void __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
|
@ -328,7 +328,7 @@ void __hyp_text __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if)
|
|||
write_gicreg(0, ICH_HCR_EL2);
|
||||
}
|
||||
|
||||
void __hyp_text __vgic_v3_save_aprs(struct vgic_v3_cpu_if *cpu_if)
|
||||
void __vgic_v3_save_aprs(struct vgic_v3_cpu_if *cpu_if)
|
||||
{
|
||||
u64 val;
|
||||
u32 nr_pre_bits;
|
||||
|
@ -361,7 +361,7 @@ void __hyp_text __vgic_v3_save_aprs(struct vgic_v3_cpu_if *cpu_if)
|
|||
}
|
||||
}
|
||||
|
||||
void __hyp_text __vgic_v3_restore_aprs(struct vgic_v3_cpu_if *cpu_if)
|
||||
void __vgic_v3_restore_aprs(struct vgic_v3_cpu_if *cpu_if)
|
||||
{
|
||||
u64 val;
|
||||
u32 nr_pre_bits;
|
||||
|
@ -394,7 +394,7 @@ void __hyp_text __vgic_v3_restore_aprs(struct vgic_v3_cpu_if *cpu_if)
|
|||
}
|
||||
}
|
||||
|
||||
void __hyp_text __vgic_v3_init_lrs(void)
|
||||
void __vgic_v3_init_lrs(void)
|
||||
{
|
||||
int max_lr_idx = vtr_to_max_lr_idx(read_gicreg(ICH_VTR_EL2));
|
||||
int i;
|
||||
|
@ -403,30 +403,30 @@ void __hyp_text __vgic_v3_init_lrs(void)
|
|||
__gic_v3_set_lr(0, i);
|
||||
}
|
||||
|
||||
u64 __hyp_text __vgic_v3_get_ich_vtr_el2(void)
|
||||
u64 __vgic_v3_get_ich_vtr_el2(void)
|
||||
{
|
||||
return read_gicreg(ICH_VTR_EL2);
|
||||
}
|
||||
|
||||
u64 __hyp_text __vgic_v3_read_vmcr(void)
|
||||
u64 __vgic_v3_read_vmcr(void)
|
||||
{
|
||||
return read_gicreg(ICH_VMCR_EL2);
|
||||
}
|
||||
|
||||
void __hyp_text __vgic_v3_write_vmcr(u32 vmcr)
|
||||
void __vgic_v3_write_vmcr(u32 vmcr)
|
||||
{
|
||||
write_gicreg(vmcr, ICH_VMCR_EL2);
|
||||
}
|
||||
|
||||
static int __hyp_text __vgic_v3_bpr_min(void)
|
||||
static int __vgic_v3_bpr_min(void)
|
||||
{
|
||||
/* See Pseudocode for VPriorityGroup */
|
||||
return 8 - vtr_to_nr_pre_bits(read_gicreg(ICH_VTR_EL2));
|
||||
}
|
||||
|
||||
static int __hyp_text __vgic_v3_get_group(struct kvm_vcpu *vcpu)
|
||||
static int __vgic_v3_get_group(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 esr = kvm_vcpu_get_hsr(vcpu);
|
||||
u32 esr = kvm_vcpu_get_esr(vcpu);
|
||||
u8 crm = (esr & ESR_ELx_SYS64_ISS_CRM_MASK) >> ESR_ELx_SYS64_ISS_CRM_SHIFT;
|
||||
|
||||
return crm != 8;
|
||||
|
@ -434,9 +434,8 @@ static int __hyp_text __vgic_v3_get_group(struct kvm_vcpu *vcpu)
|
|||
|
||||
#define GICv3_IDLE_PRIORITY 0xff
|
||||
|
||||
static int __hyp_text __vgic_v3_highest_priority_lr(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr,
|
||||
u64 *lr_val)
|
||||
static int __vgic_v3_highest_priority_lr(struct kvm_vcpu *vcpu, u32 vmcr,
|
||||
u64 *lr_val)
|
||||
{
|
||||
unsigned int used_lrs = vcpu->arch.vgic_cpu.vgic_v3.used_lrs;
|
||||
u8 priority = GICv3_IDLE_PRIORITY;
|
||||
|
@ -474,8 +473,8 @@ static int __hyp_text __vgic_v3_highest_priority_lr(struct kvm_vcpu *vcpu,
|
|||
return lr;
|
||||
}
|
||||
|
||||
static int __hyp_text __vgic_v3_find_active_lr(struct kvm_vcpu *vcpu,
|
||||
int intid, u64 *lr_val)
|
||||
static int __vgic_v3_find_active_lr(struct kvm_vcpu *vcpu, int intid,
|
||||
u64 *lr_val)
|
||||
{
|
||||
unsigned int used_lrs = vcpu->arch.vgic_cpu.vgic_v3.used_lrs;
|
||||
int i;
|
||||
|
@ -494,7 +493,7 @@ static int __hyp_text __vgic_v3_find_active_lr(struct kvm_vcpu *vcpu,
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int __hyp_text __vgic_v3_get_highest_active_priority(void)
|
||||
static int __vgic_v3_get_highest_active_priority(void)
|
||||
{
|
||||
u8 nr_apr_regs = vtr_to_nr_apr_regs(read_gicreg(ICH_VTR_EL2));
|
||||
u32 hap = 0;
|
||||
|
@ -526,12 +525,12 @@ static int __hyp_text __vgic_v3_get_highest_active_priority(void)
|
|||
return GICv3_IDLE_PRIORITY;
|
||||
}
|
||||
|
||||
static unsigned int __hyp_text __vgic_v3_get_bpr0(u32 vmcr)
|
||||
static unsigned int __vgic_v3_get_bpr0(u32 vmcr)
|
||||
{
|
||||
return (vmcr & ICH_VMCR_BPR0_MASK) >> ICH_VMCR_BPR0_SHIFT;
|
||||
}
|
||||
|
||||
static unsigned int __hyp_text __vgic_v3_get_bpr1(u32 vmcr)
|
||||
static unsigned int __vgic_v3_get_bpr1(u32 vmcr)
|
||||
{
|
||||
unsigned int bpr;
|
||||
|
||||
|
@ -550,7 +549,7 @@ static unsigned int __hyp_text __vgic_v3_get_bpr1(u32 vmcr)
|
|||
* Convert a priority to a preemption level, taking the relevant BPR
|
||||
* into account by zeroing the sub-priority bits.
|
||||
*/
|
||||
static u8 __hyp_text __vgic_v3_pri_to_pre(u8 pri, u32 vmcr, int grp)
|
||||
static u8 __vgic_v3_pri_to_pre(u8 pri, u32 vmcr, int grp)
|
||||
{
|
||||
unsigned int bpr;
|
||||
|
||||
|
@ -568,7 +567,7 @@ static u8 __hyp_text __vgic_v3_pri_to_pre(u8 pri, u32 vmcr, int grp)
|
|||
* matter what the guest does with its BPR, we can always set/get the
|
||||
* same value of a priority.
|
||||
*/
|
||||
static void __hyp_text __vgic_v3_set_active_priority(u8 pri, u32 vmcr, int grp)
|
||||
static void __vgic_v3_set_active_priority(u8 pri, u32 vmcr, int grp)
|
||||
{
|
||||
u8 pre, ap;
|
||||
u32 val;
|
||||
|
@ -587,7 +586,7 @@ static void __hyp_text __vgic_v3_set_active_priority(u8 pri, u32 vmcr, int grp)
|
|||
}
|
||||
}
|
||||
|
||||
static int __hyp_text __vgic_v3_clear_highest_active_priority(void)
|
||||
static int __vgic_v3_clear_highest_active_priority(void)
|
||||
{
|
||||
u8 nr_apr_regs = vtr_to_nr_apr_regs(read_gicreg(ICH_VTR_EL2));
|
||||
u32 hap = 0;
|
||||
|
@ -625,7 +624,7 @@ static int __hyp_text __vgic_v3_clear_highest_active_priority(void)
|
|||
return GICv3_IDLE_PRIORITY;
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_read_iar(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
static void __vgic_v3_read_iar(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
u64 lr_val;
|
||||
u8 lr_prio, pmr;
|
||||
|
@ -661,7 +660,7 @@ spurious:
|
|||
vcpu_set_reg(vcpu, rt, ICC_IAR1_EL1_SPURIOUS);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_clear_active_lr(int lr, u64 lr_val)
|
||||
static void __vgic_v3_clear_active_lr(int lr, u64 lr_val)
|
||||
{
|
||||
lr_val &= ~ICH_LR_ACTIVE_BIT;
|
||||
if (lr_val & ICH_LR_HW) {
|
||||
|
@ -674,7 +673,7 @@ static void __hyp_text __vgic_v3_clear_active_lr(int lr, u64 lr_val)
|
|||
__gic_v3_set_lr(lr_val, lr);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_bump_eoicount(void)
|
||||
static void __vgic_v3_bump_eoicount(void)
|
||||
{
|
||||
u32 hcr;
|
||||
|
||||
|
@ -683,8 +682,7 @@ static void __hyp_text __vgic_v3_bump_eoicount(void)
|
|||
write_gicreg(hcr, ICH_HCR_EL2);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_write_dir(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr, int rt)
|
||||
static void __vgic_v3_write_dir(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
u32 vid = vcpu_get_reg(vcpu, rt);
|
||||
u64 lr_val;
|
||||
|
@ -707,7 +705,7 @@ static void __hyp_text __vgic_v3_write_dir(struct kvm_vcpu *vcpu,
|
|||
__vgic_v3_clear_active_lr(lr, lr_val);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_write_eoir(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
static void __vgic_v3_write_eoir(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
u32 vid = vcpu_get_reg(vcpu, rt);
|
||||
u64 lr_val;
|
||||
|
@ -744,17 +742,17 @@ static void __hyp_text __vgic_v3_write_eoir(struct kvm_vcpu *vcpu, u32 vmcr, int
|
|||
__vgic_v3_clear_active_lr(lr, lr_val);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_read_igrpen0(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
static void __vgic_v3_read_igrpen0(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
vcpu_set_reg(vcpu, rt, !!(vmcr & ICH_VMCR_ENG0_MASK));
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_read_igrpen1(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
static void __vgic_v3_read_igrpen1(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
vcpu_set_reg(vcpu, rt, !!(vmcr & ICH_VMCR_ENG1_MASK));
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_write_igrpen0(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
static void __vgic_v3_write_igrpen0(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
u64 val = vcpu_get_reg(vcpu, rt);
|
||||
|
||||
|
@ -766,7 +764,7 @@ static void __hyp_text __vgic_v3_write_igrpen0(struct kvm_vcpu *vcpu, u32 vmcr,
|
|||
__vgic_v3_write_vmcr(vmcr);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_write_igrpen1(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
static void __vgic_v3_write_igrpen1(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
u64 val = vcpu_get_reg(vcpu, rt);
|
||||
|
||||
|
@ -778,17 +776,17 @@ static void __hyp_text __vgic_v3_write_igrpen1(struct kvm_vcpu *vcpu, u32 vmcr,
|
|||
__vgic_v3_write_vmcr(vmcr);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_read_bpr0(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
static void __vgic_v3_read_bpr0(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
vcpu_set_reg(vcpu, rt, __vgic_v3_get_bpr0(vmcr));
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_read_bpr1(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
static void __vgic_v3_read_bpr1(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
vcpu_set_reg(vcpu, rt, __vgic_v3_get_bpr1(vmcr));
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_write_bpr0(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
static void __vgic_v3_write_bpr0(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
u64 val = vcpu_get_reg(vcpu, rt);
|
||||
u8 bpr_min = __vgic_v3_bpr_min() - 1;
|
||||
|
@ -805,7 +803,7 @@ static void __hyp_text __vgic_v3_write_bpr0(struct kvm_vcpu *vcpu, u32 vmcr, int
|
|||
__vgic_v3_write_vmcr(vmcr);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_write_bpr1(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
static void __vgic_v3_write_bpr1(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
u64 val = vcpu_get_reg(vcpu, rt);
|
||||
u8 bpr_min = __vgic_v3_bpr_min();
|
||||
|
@ -825,7 +823,7 @@ static void __hyp_text __vgic_v3_write_bpr1(struct kvm_vcpu *vcpu, u32 vmcr, int
|
|||
__vgic_v3_write_vmcr(vmcr);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_read_apxrn(struct kvm_vcpu *vcpu, int rt, int n)
|
||||
static void __vgic_v3_read_apxrn(struct kvm_vcpu *vcpu, int rt, int n)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
|
@ -837,7 +835,7 @@ static void __hyp_text __vgic_v3_read_apxrn(struct kvm_vcpu *vcpu, int rt, int n
|
|||
vcpu_set_reg(vcpu, rt, val);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_write_apxrn(struct kvm_vcpu *vcpu, int rt, int n)
|
||||
static void __vgic_v3_write_apxrn(struct kvm_vcpu *vcpu, int rt, int n)
|
||||
{
|
||||
u32 val = vcpu_get_reg(vcpu, rt);
|
||||
|
||||
|
@ -847,56 +845,49 @@ static void __hyp_text __vgic_v3_write_apxrn(struct kvm_vcpu *vcpu, int rt, int
|
|||
__vgic_v3_write_ap1rn(val, n);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_read_apxr0(struct kvm_vcpu *vcpu,
|
||||
static void __vgic_v3_read_apxr0(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr, int rt)
|
||||
{
|
||||
__vgic_v3_read_apxrn(vcpu, rt, 0);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_read_apxr1(struct kvm_vcpu *vcpu,
|
||||
static void __vgic_v3_read_apxr1(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr, int rt)
|
||||
{
|
||||
__vgic_v3_read_apxrn(vcpu, rt, 1);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_read_apxr2(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr, int rt)
|
||||
static void __vgic_v3_read_apxr2(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
__vgic_v3_read_apxrn(vcpu, rt, 2);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_read_apxr3(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr, int rt)
|
||||
static void __vgic_v3_read_apxr3(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
__vgic_v3_read_apxrn(vcpu, rt, 3);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_write_apxr0(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr, int rt)
|
||||
static void __vgic_v3_write_apxr0(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
__vgic_v3_write_apxrn(vcpu, rt, 0);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_write_apxr1(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr, int rt)
|
||||
static void __vgic_v3_write_apxr1(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
__vgic_v3_write_apxrn(vcpu, rt, 1);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_write_apxr2(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr, int rt)
|
||||
static void __vgic_v3_write_apxr2(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
__vgic_v3_write_apxrn(vcpu, rt, 2);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_write_apxr3(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr, int rt)
|
||||
static void __vgic_v3_write_apxr3(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
__vgic_v3_write_apxrn(vcpu, rt, 3);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_read_hppir(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr, int rt)
|
||||
static void __vgic_v3_read_hppir(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
u64 lr_val;
|
||||
int lr, lr_grp, grp;
|
||||
|
@ -915,16 +906,14 @@ spurious:
|
|||
vcpu_set_reg(vcpu, rt, lr_val & ICH_LR_VIRTUAL_ID_MASK);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_read_pmr(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr, int rt)
|
||||
static void __vgic_v3_read_pmr(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
vmcr &= ICH_VMCR_PMR_MASK;
|
||||
vmcr >>= ICH_VMCR_PMR_SHIFT;
|
||||
vcpu_set_reg(vcpu, rt, vmcr);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_write_pmr(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr, int rt)
|
||||
static void __vgic_v3_write_pmr(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
u32 val = vcpu_get_reg(vcpu, rt);
|
||||
|
||||
|
@ -936,15 +925,13 @@ static void __hyp_text __vgic_v3_write_pmr(struct kvm_vcpu *vcpu,
|
|||
write_gicreg(vmcr, ICH_VMCR_EL2);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_read_rpr(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr, int rt)
|
||||
static void __vgic_v3_read_rpr(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
u32 val = __vgic_v3_get_highest_active_priority();
|
||||
vcpu_set_reg(vcpu, rt, val);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_read_ctlr(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr, int rt)
|
||||
static void __vgic_v3_read_ctlr(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
u32 vtr, val;
|
||||
|
||||
|
@ -965,8 +952,7 @@ static void __hyp_text __vgic_v3_read_ctlr(struct kvm_vcpu *vcpu,
|
|||
vcpu_set_reg(vcpu, rt, val);
|
||||
}
|
||||
|
||||
static void __hyp_text __vgic_v3_write_ctlr(struct kvm_vcpu *vcpu,
|
||||
u32 vmcr, int rt)
|
||||
static void __vgic_v3_write_ctlr(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
|
||||
{
|
||||
u32 val = vcpu_get_reg(vcpu, rt);
|
||||
|
||||
|
@ -983,7 +969,7 @@ static void __hyp_text __vgic_v3_write_ctlr(struct kvm_vcpu *vcpu,
|
|||
write_gicreg(vmcr, ICH_VMCR_EL2);
|
||||
}
|
||||
|
||||
int __hyp_text __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu)
|
||||
int __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int rt;
|
||||
u32 esr;
|
||||
|
@ -992,7 +978,7 @@ int __hyp_text __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu)
|
|||
bool is_read;
|
||||
u32 sysreg;
|
||||
|
||||
esr = kvm_vcpu_get_hsr(vcpu);
|
||||
esr = kvm_vcpu_get_esr(vcpu);
|
||||
if (vcpu_mode_is_32bit(vcpu)) {
|
||||
if (!kvm_condition_valid(vcpu)) {
|
||||
__kvm_skip_instr(vcpu);
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for Kernel-based Virtual Machine module, HYP/nVHE part
|
||||
#
|
||||
|
||||
asflags-y := -D__KVM_VHE_HYPERVISOR__
|
||||
ccflags-y := -D__KVM_VHE_HYPERVISOR__
|
||||
|
||||
obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o
|
||||
obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \
|
||||
../fpsimd.o ../hyp-entry.o
|
|
@ -0,0 +1,26 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <hyp/debug-sr.h>
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include <asm/kvm_hyp.h>
|
||||
|
||||
void __debug_switch_to_guest(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
__debug_switch_to_guest_common(vcpu);
|
||||
}
|
||||
|
||||
void __debug_switch_to_host(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
__debug_switch_to_host_common(vcpu);
|
||||
}
|
||||
|
||||
u32 __kvm_get_mdcr_el2(void)
|
||||
{
|
||||
return read_sysreg(mdcr_el2);
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <hyp/switch.h>
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <uapi/linux/psci.h>
|
||||
|
||||
#include <kvm/arm_psci.h>
|
||||
|
||||
#include <asm/barrier.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/kprobes.h>
|
||||
#include <asm/kvm_asm.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_hyp.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
#include <asm/fpsimd.h>
|
||||
#include <asm/debug-monitors.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/thread_info.h>
|
||||
|
||||
const char __hyp_panic_string[] = "HYP panic:\nPS:%08llx PC:%016llx ESR:%08llx\nFAR:%016llx HPFAR:%016llx PAR:%016llx\nVCPU:%p\n";
|
||||
|
||||
static void __activate_traps(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
___activate_traps(vcpu);
|
||||
|
||||
val = read_sysreg(cpacr_el1);
|
||||
val |= CPACR_EL1_TTA;
|
||||
val &= ~CPACR_EL1_ZEN;
|
||||
|
||||
/*
|
||||
* With VHE (HCR.E2H == 1), accesses to CPACR_EL1 are routed to
|
||||
* CPTR_EL2. In general, CPACR_EL1 has the same layout as CPTR_EL2,
|
||||
* except for some missing controls, such as TAM.
|
||||
* In this case, CPTR_EL2.TAM has the same position with or without
|
||||
* VHE (HCR.E2H == 1) which allows us to use here the CPTR_EL2.TAM
|
||||
* shift value for trapping the AMU accesses.
|
||||
*/
|
||||
|
||||
val |= CPTR_EL2_TAM;
|
||||
|
||||
if (update_fp_enabled(vcpu)) {
|
||||
if (vcpu_has_sve(vcpu))
|
||||
val |= CPACR_EL1_ZEN;
|
||||
} else {
|
||||
val &= ~CPACR_EL1_FPEN;
|
||||
__activate_traps_fpsimd32(vcpu);
|
||||
}
|
||||
|
||||
write_sysreg(val, cpacr_el1);
|
||||
|
||||
write_sysreg(kvm_get_hyp_vector(), vbar_el1);
|
||||
}
|
||||
NOKPROBE_SYMBOL(__activate_traps);
|
||||
|
||||
static void __deactivate_traps(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
extern char vectors[]; /* kernel exception vectors */
|
||||
|
||||
___deactivate_traps(vcpu);
|
||||
|
||||
write_sysreg(HCR_HOST_VHE_FLAGS, hcr_el2);
|
||||
|
||||
/*
|
||||
* ARM errata 1165522 and 1530923 require the actual execution of the
|
||||
* above before we can switch to the EL2/EL0 translation regime used by
|
||||
* the host.
|
||||
*/
|
||||
asm(ALTERNATIVE("nop", "isb", ARM64_WORKAROUND_SPECULATIVE_AT));
|
||||
|
||||
write_sysreg(CPACR_EL1_DEFAULT, cpacr_el1);
|
||||
write_sysreg(vectors, vbar_el1);
|
||||
}
|
||||
NOKPROBE_SYMBOL(__deactivate_traps);
|
||||
|
||||
void activate_traps_vhe_load(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
__activate_traps_common(vcpu);
|
||||
}
|
||||
|
||||
void deactivate_traps_vhe_put(void)
|
||||
{
|
||||
u64 mdcr_el2 = read_sysreg(mdcr_el2);
|
||||
|
||||
mdcr_el2 &= MDCR_EL2_HPMN_MASK |
|
||||
MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT |
|
||||
MDCR_EL2_TPMS;
|
||||
|
||||
write_sysreg(mdcr_el2, mdcr_el2);
|
||||
|
||||
__deactivate_traps_common();
|
||||
}
|
||||
|
||||
/* Switch to the guest for VHE systems running in EL2 */
|
||||
static int __kvm_vcpu_run_vhe(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_cpu_context *host_ctxt;
|
||||
struct kvm_cpu_context *guest_ctxt;
|
||||
u64 exit_code;
|
||||
|
||||
host_ctxt = &__hyp_this_cpu_ptr(kvm_host_data)->host_ctxt;
|
||||
host_ctxt->__hyp_running_vcpu = vcpu;
|
||||
guest_ctxt = &vcpu->arch.ctxt;
|
||||
|
||||
sysreg_save_host_state_vhe(host_ctxt);
|
||||
|
||||
/*
|
||||
* ARM erratum 1165522 requires us to configure both stage 1 and
|
||||
* stage 2 translation for the guest context before we clear
|
||||
* HCR_EL2.TGE.
|
||||
*
|
||||
* We have already configured the guest's stage 1 translation in
|
||||
* kvm_vcpu_load_sysregs_vhe above. We must now call __activate_vm
|
||||
* before __activate_traps, because __activate_vm configures
|
||||
* stage 2 translation, and __activate_traps clear HCR_EL2.TGE
|
||||
* (among other things).
|
||||
*/
|
||||
__activate_vm(vcpu->arch.hw_mmu);
|
||||
__activate_traps(vcpu);
|
||||
|
||||
sysreg_restore_guest_state_vhe(guest_ctxt);
|
||||
__debug_switch_to_guest(vcpu);
|
||||
|
||||
__set_guest_arch_workaround_state(vcpu);
|
||||
|
||||
do {
|
||||
/* Jump in the fire! */
|
||||
exit_code = __guest_enter(vcpu, host_ctxt);
|
||||
|
||||
/* And we're baaack! */
|
||||
} while (fixup_guest_exit(vcpu, &exit_code));
|
||||
|
||||
__set_host_arch_workaround_state(vcpu);
|
||||
|
||||
sysreg_save_guest_state_vhe(guest_ctxt);
|
||||
|
||||
__deactivate_traps(vcpu);
|
||||
|
||||
sysreg_restore_host_state_vhe(host_ctxt);
|
||||
|
||||
if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED)
|
||||
__fpsimd_save_fpexc32(vcpu);
|
||||
|
||||
__debug_switch_to_host(vcpu);
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
NOKPROBE_SYMBOL(__kvm_vcpu_run_vhe);
|
||||
|
||||
int __kvm_vcpu_run(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
local_daif_mask();
|
||||
|
||||
/*
|
||||
* Having IRQs masked via PMR when entering the guest means the GIC
|
||||
* will not signal the CPU of interrupts of lower priority, and the
|
||||
* only way to get out will be via guest exceptions.
|
||||
* Naturally, we want to avoid this.
|
||||
*
|
||||
* local_daif_mask() already sets GIC_PRIO_PSR_I_SET, we just need a
|
||||
* dsb to ensure the redistributor is forwards EL2 IRQs to the CPU.
|
||||
*/
|
||||
pmr_sync();
|
||||
|
||||
ret = __kvm_vcpu_run_vhe(vcpu);
|
||||
|
||||
/*
|
||||
* local_daif_restore() takes care to properly restore PSTATE.DAIF
|
||||
* and the GIC PMR if the host is using IRQ priorities.
|
||||
*/
|
||||
local_daif_restore(DAIF_PROCCTX_NOIRQ);
|
||||
|
||||
/*
|
||||
* When we exit from the guest we change a number of CPU configuration
|
||||
* parameters, such as traps. Make sure these changes take effect
|
||||
* before running the host or additional guests.
|
||||
*/
|
||||
isb();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __hyp_call_panic(u64 spsr, u64 elr, u64 par,
|
||||
struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
vcpu = host_ctxt->__hyp_running_vcpu;
|
||||
|
||||
__deactivate_traps(vcpu);
|
||||
sysreg_restore_host_state_vhe(host_ctxt);
|
||||
|
||||
panic(__hyp_panic_string,
|
||||
spsr, elr,
|
||||
read_sysreg_el2(SYS_ESR), read_sysreg_el2(SYS_FAR),
|
||||
read_sysreg(hpfar_el2), par, vcpu);
|
||||
}
|
||||
NOKPROBE_SYMBOL(__hyp_call_panic);
|
||||
|
||||
void __noreturn hyp_panic(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
u64 spsr = read_sysreg_el2(SYS_SPSR);
|
||||
u64 elr = read_sysreg_el2(SYS_ELR);
|
||||
u64 par = read_sysreg(par_el1);
|
||||
|
||||
__hyp_call_panic(spsr, elr, par, host_ctxt);
|
||||
unreachable();
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2012-2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <hyp/sysreg-sr.h>
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include <asm/kprobes.h>
|
||||
#include <asm/kvm_asm.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_hyp.h>
|
||||
|
||||
/*
|
||||
* VHE: Host and guest must save mdscr_el1 and sp_el0 (and the PC and
|
||||
* pstate, which are handled as part of the el2 return state) on every
|
||||
* switch (sp_el0 is being dealt with in the assembly code).
|
||||
* tpidr_el0 and tpidrro_el0 only need to be switched when going
|
||||
* to host userspace or a different VCPU. EL1 registers only need to be
|
||||
* switched when potentially going to run a different VCPU. The latter two
|
||||
* classes are handled as part of kvm_arch_vcpu_load and kvm_arch_vcpu_put.
|
||||
*/
|
||||
|
||||
void sysreg_save_host_state_vhe(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
__sysreg_save_common_state(ctxt);
|
||||
}
|
||||
NOKPROBE_SYMBOL(sysreg_save_host_state_vhe);
|
||||
|
||||
void sysreg_save_guest_state_vhe(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
__sysreg_save_common_state(ctxt);
|
||||
__sysreg_save_el2_return_state(ctxt);
|
||||
}
|
||||
NOKPROBE_SYMBOL(sysreg_save_guest_state_vhe);
|
||||
|
||||
void sysreg_restore_host_state_vhe(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
__sysreg_restore_common_state(ctxt);
|
||||
}
|
||||
NOKPROBE_SYMBOL(sysreg_restore_host_state_vhe);
|
||||
|
||||
void sysreg_restore_guest_state_vhe(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
__sysreg_restore_common_state(ctxt);
|
||||
__sysreg_restore_el2_return_state(ctxt);
|
||||
}
|
||||
NOKPROBE_SYMBOL(sysreg_restore_guest_state_vhe);
|
||||
|
||||
/**
|
||||
* kvm_vcpu_load_sysregs_vhe - Load guest system registers to the physical CPU
|
||||
*
|
||||
* @vcpu: The VCPU pointer
|
||||
*
|
||||
* Load system registers that do not affect the host's execution, for
|
||||
* example EL1 system registers on a VHE system where the host kernel
|
||||
* runs at EL2. This function is called from KVM's vcpu_load() function
|
||||
* and loading system register state early avoids having to load them on
|
||||
* every entry to the VM.
|
||||
*/
|
||||
void kvm_vcpu_load_sysregs_vhe(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_cpu_context *guest_ctxt = &vcpu->arch.ctxt;
|
||||
struct kvm_cpu_context *host_ctxt;
|
||||
|
||||
host_ctxt = &__hyp_this_cpu_ptr(kvm_host_data)->host_ctxt;
|
||||
__sysreg_save_user_state(host_ctxt);
|
||||
|
||||
/*
|
||||
* Load guest EL1 and user state
|
||||
*
|
||||
* We must restore the 32-bit state before the sysregs, thanks
|
||||
* to erratum #852523 (Cortex-A57) or #853709 (Cortex-A72).
|
||||
*/
|
||||
__sysreg32_restore_state(vcpu);
|
||||
__sysreg_restore_user_state(guest_ctxt);
|
||||
__sysreg_restore_el1_state(guest_ctxt);
|
||||
|
||||
vcpu->arch.sysregs_loaded_on_cpu = true;
|
||||
|
||||
activate_traps_vhe_load(vcpu);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_vcpu_put_sysregs_vhe - Restore host system registers to the physical CPU
|
||||
*
|
||||
* @vcpu: The VCPU pointer
|
||||
*
|
||||
* Save guest system registers that do not affect the host's execution, for
|
||||
* example EL1 system registers on a VHE system where the host kernel
|
||||
* runs at EL2. This function is called from KVM's vcpu_put() function
|
||||
* and deferring saving system register state until we're no longer running the
|
||||
* VCPU avoids having to save them on every exit from the VM.
|
||||
*/
|
||||
void kvm_vcpu_put_sysregs_vhe(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_cpu_context *guest_ctxt = &vcpu->arch.ctxt;
|
||||
struct kvm_cpu_context *host_ctxt;
|
||||
|
||||
host_ctxt = &__hyp_this_cpu_ptr(kvm_host_data)->host_ctxt;
|
||||
deactivate_traps_vhe_put();
|
||||
|
||||
__sysreg_save_el1_state(guest_ctxt);
|
||||
__sysreg_save_user_state(guest_ctxt);
|
||||
__sysreg32_save_state(vcpu);
|
||||
|
||||
/* Restore host user state */
|
||||
__sysreg_restore_user_state(host_ctxt);
|
||||
|
||||
vcpu->arch.sysregs_loaded_on_cpu = false;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2012-2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <asm/kvm_hyp.h>
|
||||
|
||||
void __kvm_timer_set_cntvoff(u64 cntvoff)
|
||||
{
|
||||
write_sysreg(cntvoff, cntvoff_el2);
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2015 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
|
||||
#include <linux/irqflags.h>
|
||||
|
||||
#include <asm/kvm_hyp.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
struct tlb_inv_context {
|
||||
unsigned long flags;
|
||||
u64 tcr;
|
||||
u64 sctlr;
|
||||
};
|
||||
|
||||
static void __tlb_switch_to_guest(struct kvm_s2_mmu *mmu,
|
||||
struct tlb_inv_context *cxt)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
local_irq_save(cxt->flags);
|
||||
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||
/*
|
||||
* For CPUs that are affected by ARM errata 1165522 or 1530923,
|
||||
* we cannot trust stage-1 to be in a correct state at that
|
||||
* point. Since we do not want to force a full load of the
|
||||
* vcpu state, we prevent the EL1 page-table walker to
|
||||
* allocate new TLBs. This is done by setting the EPD bits
|
||||
* in the TCR_EL1 register. We also need to prevent it to
|
||||
* allocate IPA->PA walks, so we enable the S1 MMU...
|
||||
*/
|
||||
val = cxt->tcr = read_sysreg_el1(SYS_TCR);
|
||||
val |= TCR_EPD1_MASK | TCR_EPD0_MASK;
|
||||
write_sysreg_el1(val, SYS_TCR);
|
||||
val = cxt->sctlr = read_sysreg_el1(SYS_SCTLR);
|
||||
val |= SCTLR_ELx_M;
|
||||
write_sysreg_el1(val, SYS_SCTLR);
|
||||
}
|
||||
|
||||
/*
|
||||
* With VHE enabled, we have HCR_EL2.{E2H,TGE} = {1,1}, and
|
||||
* most TLB operations target EL2/EL0. In order to affect the
|
||||
* guest TLBs (EL1/EL0), we need to change one of these two
|
||||
* bits. Changing E2H is impossible (goodbye TTBR1_EL2), so
|
||||
* let's flip TGE before executing the TLB operation.
|
||||
*
|
||||
* ARM erratum 1165522 requires some special handling (again),
|
||||
* as we need to make sure both stages of translation are in
|
||||
* place before clearing TGE. __load_guest_stage2() already
|
||||
* has an ISB in order to deal with this.
|
||||
*/
|
||||
__load_guest_stage2(mmu);
|
||||
val = read_sysreg(hcr_el2);
|
||||
val &= ~HCR_TGE;
|
||||
write_sysreg(val, hcr_el2);
|
||||
isb();
|
||||
}
|
||||
|
||||
static void __tlb_switch_to_host(struct tlb_inv_context *cxt)
|
||||
{
|
||||
/*
|
||||
* We're done with the TLB operation, let's restore the host's
|
||||
* view of HCR_EL2.
|
||||
*/
|
||||
write_sysreg(0, vttbr_el2);
|
||||
write_sysreg(HCR_HOST_VHE_FLAGS, hcr_el2);
|
||||
isb();
|
||||
|
||||
if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
|
||||
/* Restore the registers to what they were */
|
||||
write_sysreg_el1(cxt->tcr, SYS_TCR);
|
||||
write_sysreg_el1(cxt->sctlr, SYS_SCTLR);
|
||||
}
|
||||
|
||||
local_irq_restore(cxt->flags);
|
||||
}
|
||||
|
||||
void __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu,
|
||||
phys_addr_t ipa, int level)
|
||||
{
|
||||
struct tlb_inv_context cxt;
|
||||
|
||||
dsb(ishst);
|
||||
|
||||
/* Switch to requested VMID */
|
||||
__tlb_switch_to_guest(mmu, &cxt);
|
||||
|
||||
/*
|
||||
* We could do so much better if we had the VA as well.
|
||||
* Instead, we invalidate Stage-2 for this IPA, and the
|
||||
* whole of Stage-1. Weep...
|
||||
*/
|
||||
ipa >>= 12;
|
||||
__tlbi_level(ipas2e1is, ipa, level);
|
||||
|
||||
/*
|
||||
* We have to ensure completion of the invalidation at Stage-2,
|
||||
* since a table walk on another CPU could refill a TLB with a
|
||||
* complete (S1 + S2) walk based on the old Stage-2 mapping if
|
||||
* the Stage-1 invalidation happened first.
|
||||
*/
|
||||
dsb(ish);
|
||||
__tlbi(vmalle1is);
|
||||
dsb(ish);
|
||||
isb();
|
||||
|
||||
__tlb_switch_to_host(&cxt);
|
||||
}
|
||||
|
||||
void __kvm_tlb_flush_vmid(struct kvm_s2_mmu *mmu)
|
||||
{
|
||||
struct tlb_inv_context cxt;
|
||||
|
||||
dsb(ishst);
|
||||
|
||||
/* Switch to requested VMID */
|
||||
__tlb_switch_to_guest(mmu, &cxt);
|
||||
|
||||
__tlbi(vmalls12e1is);
|
||||
dsb(ish);
|
||||
isb();
|
||||
|
||||
__tlb_switch_to_host(&cxt);
|
||||
}
|
||||
|
||||
void __kvm_tlb_flush_local_vmid(struct kvm_s2_mmu *mmu)
|
||||
{
|
||||
struct tlb_inv_context cxt;
|
||||
|
||||
/* Switch to requested VMID */
|
||||
__tlb_switch_to_guest(mmu, &cxt);
|
||||
|
||||
__tlbi(vmalle1);
|
||||
dsb(nsh);
|
||||
isb();
|
||||
|
||||
__tlb_switch_to_host(&cxt);
|
||||
}
|
||||
|
||||
void __kvm_flush_vm_context(void)
|
||||
{
|
||||
dsb(ishst);
|
||||
__tlbi(alle1is);
|
||||
|
||||
/*
|
||||
* VIPT and PIPT caches are not affected by VMID, so no maintenance
|
||||
* is necessary across a VMID rollover.
|
||||
*
|
||||
* VPIPT caches constrain lookup and maintenance to the active VMID,
|
||||
* so we need to invalidate lines with a stale VMID to avoid an ABA
|
||||
* race after multiple rollovers.
|
||||
*
|
||||
*/
|
||||
if (icache_is_vpipt())
|
||||
asm volatile("ic ialluis");
|
||||
|
||||
dsb(ish);
|
||||
}
|
|
@ -64,7 +64,7 @@ static void enter_exception64(struct kvm_vcpu *vcpu, unsigned long target_mode,
|
|||
case PSR_MODE_EL1h:
|
||||
vbar = vcpu_read_sys_reg(vcpu, VBAR_EL1);
|
||||
sctlr = vcpu_read_sys_reg(vcpu, SCTLR_EL1);
|
||||
vcpu_write_elr_el1(vcpu, *vcpu_pc(vcpu));
|
||||
vcpu_write_sys_reg(vcpu, *vcpu_pc(vcpu), ELR_EL1);
|
||||
break;
|
||||
default:
|
||||
/* Don't do that */
|
||||
|
|
|
@ -146,12 +146,6 @@ int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
|
|||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/* Page table accesses IO mem: tell guest to fix its TTBR */
|
||||
if (kvm_vcpu_dabt_iss1tw(vcpu)) {
|
||||
kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare MMIO operation. First decode the syndrome data we get
|
||||
* from the CPU. Then try if some in-kernel emulation feels
|
||||
|
|
|
@ -55,12 +55,13 @@ static bool memslot_is_logging(struct kvm_memory_slot *memslot)
|
|||
*/
|
||||
void kvm_flush_remote_tlbs(struct kvm *kvm)
|
||||
{
|
||||
kvm_call_hyp(__kvm_tlb_flush_vmid, kvm);
|
||||
kvm_call_hyp(__kvm_tlb_flush_vmid, &kvm->arch.mmu);
|
||||
}
|
||||
|
||||
static void kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa)
|
||||
static void kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu, phys_addr_t ipa,
|
||||
int level)
|
||||
{
|
||||
kvm_call_hyp(__kvm_tlb_flush_vmid_ipa, kvm, ipa);
|
||||
kvm_call_hyp(__kvm_tlb_flush_vmid_ipa, mmu, ipa, level);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -90,74 +91,80 @@ static bool kvm_is_device_pfn(unsigned long pfn)
|
|||
|
||||
/**
|
||||
* stage2_dissolve_pmd() - clear and flush huge PMD entry
|
||||
* @kvm: pointer to kvm structure.
|
||||
* @mmu: pointer to mmu structure to operate on
|
||||
* @addr: IPA
|
||||
* @pmd: pmd pointer for IPA
|
||||
*
|
||||
* Function clears a PMD entry, flushes addr 1st and 2nd stage TLBs.
|
||||
*/
|
||||
static void stage2_dissolve_pmd(struct kvm *kvm, phys_addr_t addr, pmd_t *pmd)
|
||||
static void stage2_dissolve_pmd(struct kvm_s2_mmu *mmu, phys_addr_t addr, pmd_t *pmd)
|
||||
{
|
||||
if (!pmd_thp_or_huge(*pmd))
|
||||
return;
|
||||
|
||||
pmd_clear(pmd);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
kvm_tlb_flush_vmid_ipa(mmu, addr, S2_PMD_LEVEL);
|
||||
put_page(virt_to_page(pmd));
|
||||
}
|
||||
|
||||
/**
|
||||
* stage2_dissolve_pud() - clear and flush huge PUD entry
|
||||
* @kvm: pointer to kvm structure.
|
||||
* @mmu: pointer to mmu structure to operate on
|
||||
* @addr: IPA
|
||||
* @pud: pud pointer for IPA
|
||||
*
|
||||
* Function clears a PUD entry, flushes addr 1st and 2nd stage TLBs.
|
||||
*/
|
||||
static void stage2_dissolve_pud(struct kvm *kvm, phys_addr_t addr, pud_t *pudp)
|
||||
static void stage2_dissolve_pud(struct kvm_s2_mmu *mmu, phys_addr_t addr, pud_t *pudp)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
|
||||
if (!stage2_pud_huge(kvm, *pudp))
|
||||
return;
|
||||
|
||||
stage2_pud_clear(kvm, pudp);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
kvm_tlb_flush_vmid_ipa(mmu, addr, S2_PUD_LEVEL);
|
||||
put_page(virt_to_page(pudp));
|
||||
}
|
||||
|
||||
static void clear_stage2_pgd_entry(struct kvm *kvm, pgd_t *pgd, phys_addr_t addr)
|
||||
static void clear_stage2_pgd_entry(struct kvm_s2_mmu *mmu, pgd_t *pgd, phys_addr_t addr)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
p4d_t *p4d_table __maybe_unused = stage2_p4d_offset(kvm, pgd, 0UL);
|
||||
stage2_pgd_clear(kvm, pgd);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
kvm_tlb_flush_vmid_ipa(mmu, addr, S2_NO_LEVEL_HINT);
|
||||
stage2_p4d_free(kvm, p4d_table);
|
||||
put_page(virt_to_page(pgd));
|
||||
}
|
||||
|
||||
static void clear_stage2_p4d_entry(struct kvm *kvm, p4d_t *p4d, phys_addr_t addr)
|
||||
static void clear_stage2_p4d_entry(struct kvm_s2_mmu *mmu, p4d_t *p4d, phys_addr_t addr)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
pud_t *pud_table __maybe_unused = stage2_pud_offset(kvm, p4d, 0);
|
||||
stage2_p4d_clear(kvm, p4d);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
kvm_tlb_flush_vmid_ipa(mmu, addr, S2_NO_LEVEL_HINT);
|
||||
stage2_pud_free(kvm, pud_table);
|
||||
put_page(virt_to_page(p4d));
|
||||
}
|
||||
|
||||
static void clear_stage2_pud_entry(struct kvm *kvm, pud_t *pud, phys_addr_t addr)
|
||||
static void clear_stage2_pud_entry(struct kvm_s2_mmu *mmu, pud_t *pud, phys_addr_t addr)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
pmd_t *pmd_table __maybe_unused = stage2_pmd_offset(kvm, pud, 0);
|
||||
|
||||
VM_BUG_ON(stage2_pud_huge(kvm, *pud));
|
||||
stage2_pud_clear(kvm, pud);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
kvm_tlb_flush_vmid_ipa(mmu, addr, S2_NO_LEVEL_HINT);
|
||||
stage2_pmd_free(kvm, pmd_table);
|
||||
put_page(virt_to_page(pud));
|
||||
}
|
||||
|
||||
static void clear_stage2_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr)
|
||||
static void clear_stage2_pmd_entry(struct kvm_s2_mmu *mmu, pmd_t *pmd, phys_addr_t addr)
|
||||
{
|
||||
pte_t *pte_table = pte_offset_kernel(pmd, 0);
|
||||
VM_BUG_ON(pmd_thp_or_huge(*pmd));
|
||||
pmd_clear(pmd);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
kvm_tlb_flush_vmid_ipa(mmu, addr, S2_NO_LEVEL_HINT);
|
||||
free_page((unsigned long)pte_table);
|
||||
put_page(virt_to_page(pmd));
|
||||
}
|
||||
|
@ -223,7 +230,7 @@ static inline void kvm_pgd_populate(pgd_t *pgdp, p4d_t *p4dp)
|
|||
* we then fully enforce cacheability of RAM, no matter what the guest
|
||||
* does.
|
||||
*/
|
||||
static void unmap_stage2_ptes(struct kvm *kvm, pmd_t *pmd,
|
||||
static void unmap_stage2_ptes(struct kvm_s2_mmu *mmu, pmd_t *pmd,
|
||||
phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
phys_addr_t start_addr = addr;
|
||||
|
@ -235,7 +242,7 @@ static void unmap_stage2_ptes(struct kvm *kvm, pmd_t *pmd,
|
|||
pte_t old_pte = *pte;
|
||||
|
||||
kvm_set_pte(pte, __pte(0));
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
kvm_tlb_flush_vmid_ipa(mmu, addr, S2_PTE_LEVEL);
|
||||
|
||||
/* No need to invalidate the cache for device mappings */
|
||||
if (!kvm_is_device_pfn(pte_pfn(old_pte)))
|
||||
|
@ -245,13 +252,14 @@ static void unmap_stage2_ptes(struct kvm *kvm, pmd_t *pmd,
|
|||
}
|
||||
} while (pte++, addr += PAGE_SIZE, addr != end);
|
||||
|
||||
if (stage2_pte_table_empty(kvm, start_pte))
|
||||
clear_stage2_pmd_entry(kvm, pmd, start_addr);
|
||||
if (stage2_pte_table_empty(mmu->kvm, start_pte))
|
||||
clear_stage2_pmd_entry(mmu, pmd, start_addr);
|
||||
}
|
||||
|
||||
static void unmap_stage2_pmds(struct kvm *kvm, pud_t *pud,
|
||||
static void unmap_stage2_pmds(struct kvm_s2_mmu *mmu, pud_t *pud,
|
||||
phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
phys_addr_t next, start_addr = addr;
|
||||
pmd_t *pmd, *start_pmd;
|
||||
|
||||
|
@ -263,24 +271,25 @@ static void unmap_stage2_pmds(struct kvm *kvm, pud_t *pud,
|
|||
pmd_t old_pmd = *pmd;
|
||||
|
||||
pmd_clear(pmd);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
kvm_tlb_flush_vmid_ipa(mmu, addr, S2_PMD_LEVEL);
|
||||
|
||||
kvm_flush_dcache_pmd(old_pmd);
|
||||
|
||||
put_page(virt_to_page(pmd));
|
||||
} else {
|
||||
unmap_stage2_ptes(kvm, pmd, addr, next);
|
||||
unmap_stage2_ptes(mmu, pmd, addr, next);
|
||||
}
|
||||
}
|
||||
} while (pmd++, addr = next, addr != end);
|
||||
|
||||
if (stage2_pmd_table_empty(kvm, start_pmd))
|
||||
clear_stage2_pud_entry(kvm, pud, start_addr);
|
||||
clear_stage2_pud_entry(mmu, pud, start_addr);
|
||||
}
|
||||
|
||||
static void unmap_stage2_puds(struct kvm *kvm, p4d_t *p4d,
|
||||
static void unmap_stage2_puds(struct kvm_s2_mmu *mmu, p4d_t *p4d,
|
||||
phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
phys_addr_t next, start_addr = addr;
|
||||
pud_t *pud, *start_pud;
|
||||
|
||||
|
@ -292,22 +301,23 @@ static void unmap_stage2_puds(struct kvm *kvm, p4d_t *p4d,
|
|||
pud_t old_pud = *pud;
|
||||
|
||||
stage2_pud_clear(kvm, pud);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
kvm_tlb_flush_vmid_ipa(mmu, addr, S2_PUD_LEVEL);
|
||||
kvm_flush_dcache_pud(old_pud);
|
||||
put_page(virt_to_page(pud));
|
||||
} else {
|
||||
unmap_stage2_pmds(kvm, pud, addr, next);
|
||||
unmap_stage2_pmds(mmu, pud, addr, next);
|
||||
}
|
||||
}
|
||||
} while (pud++, addr = next, addr != end);
|
||||
|
||||
if (stage2_pud_table_empty(kvm, start_pud))
|
||||
clear_stage2_p4d_entry(kvm, p4d, start_addr);
|
||||
clear_stage2_p4d_entry(mmu, p4d, start_addr);
|
||||
}
|
||||
|
||||
static void unmap_stage2_p4ds(struct kvm *kvm, pgd_t *pgd,
|
||||
static void unmap_stage2_p4ds(struct kvm_s2_mmu *mmu, pgd_t *pgd,
|
||||
phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
phys_addr_t next, start_addr = addr;
|
||||
p4d_t *p4d, *start_p4d;
|
||||
|
||||
|
@ -315,11 +325,11 @@ static void unmap_stage2_p4ds(struct kvm *kvm, pgd_t *pgd,
|
|||
do {
|
||||
next = stage2_p4d_addr_end(kvm, addr, end);
|
||||
if (!stage2_p4d_none(kvm, *p4d))
|
||||
unmap_stage2_puds(kvm, p4d, addr, next);
|
||||
unmap_stage2_puds(mmu, p4d, addr, next);
|
||||
} while (p4d++, addr = next, addr != end);
|
||||
|
||||
if (stage2_p4d_table_empty(kvm, start_p4d))
|
||||
clear_stage2_pgd_entry(kvm, pgd, start_addr);
|
||||
clear_stage2_pgd_entry(mmu, pgd, start_addr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -333,8 +343,9 @@ static void unmap_stage2_p4ds(struct kvm *kvm, pgd_t *pgd,
|
|||
* destroying the VM), otherwise another faulting VCPU may come in and mess
|
||||
* with things behind our backs.
|
||||
*/
|
||||
static void unmap_stage2_range(struct kvm *kvm, phys_addr_t start, u64 size)
|
||||
static void unmap_stage2_range(struct kvm_s2_mmu *mmu, phys_addr_t start, u64 size)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
pgd_t *pgd;
|
||||
phys_addr_t addr = start, end = start + size;
|
||||
phys_addr_t next;
|
||||
|
@ -342,18 +353,18 @@ static void unmap_stage2_range(struct kvm *kvm, phys_addr_t start, u64 size)
|
|||
assert_spin_locked(&kvm->mmu_lock);
|
||||
WARN_ON(size & ~PAGE_MASK);
|
||||
|
||||
pgd = kvm->arch.pgd + stage2_pgd_index(kvm, addr);
|
||||
pgd = mmu->pgd + stage2_pgd_index(kvm, addr);
|
||||
do {
|
||||
/*
|
||||
* Make sure the page table is still active, as another thread
|
||||
* could have possibly freed the page table, while we released
|
||||
* the lock.
|
||||
*/
|
||||
if (!READ_ONCE(kvm->arch.pgd))
|
||||
if (!READ_ONCE(mmu->pgd))
|
||||
break;
|
||||
next = stage2_pgd_addr_end(kvm, addr, end);
|
||||
if (!stage2_pgd_none(kvm, *pgd))
|
||||
unmap_stage2_p4ds(kvm, pgd, addr, next);
|
||||
unmap_stage2_p4ds(mmu, pgd, addr, next);
|
||||
/*
|
||||
* If the range is too large, release the kvm->mmu_lock
|
||||
* to prevent starvation and lockup detector warnings.
|
||||
|
@ -363,7 +374,7 @@ static void unmap_stage2_range(struct kvm *kvm, phys_addr_t start, u64 size)
|
|||
} while (pgd++, addr = next, addr != end);
|
||||
}
|
||||
|
||||
static void stage2_flush_ptes(struct kvm *kvm, pmd_t *pmd,
|
||||
static void stage2_flush_ptes(struct kvm_s2_mmu *mmu, pmd_t *pmd,
|
||||
phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
pte_t *pte;
|
||||
|
@ -375,9 +386,10 @@ static void stage2_flush_ptes(struct kvm *kvm, pmd_t *pmd,
|
|||
} while (pte++, addr += PAGE_SIZE, addr != end);
|
||||
}
|
||||
|
||||
static void stage2_flush_pmds(struct kvm *kvm, pud_t *pud,
|
||||
static void stage2_flush_pmds(struct kvm_s2_mmu *mmu, pud_t *pud,
|
||||
phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
pmd_t *pmd;
|
||||
phys_addr_t next;
|
||||
|
||||
|
@ -388,14 +400,15 @@ static void stage2_flush_pmds(struct kvm *kvm, pud_t *pud,
|
|||
if (pmd_thp_or_huge(*pmd))
|
||||
kvm_flush_dcache_pmd(*pmd);
|
||||
else
|
||||
stage2_flush_ptes(kvm, pmd, addr, next);
|
||||
stage2_flush_ptes(mmu, pmd, addr, next);
|
||||
}
|
||||
} while (pmd++, addr = next, addr != end);
|
||||
}
|
||||
|
||||
static void stage2_flush_puds(struct kvm *kvm, p4d_t *p4d,
|
||||
static void stage2_flush_puds(struct kvm_s2_mmu *mmu, p4d_t *p4d,
|
||||
phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
pud_t *pud;
|
||||
phys_addr_t next;
|
||||
|
||||
|
@ -406,14 +419,15 @@ static void stage2_flush_puds(struct kvm *kvm, p4d_t *p4d,
|
|||
if (stage2_pud_huge(kvm, *pud))
|
||||
kvm_flush_dcache_pud(*pud);
|
||||
else
|
||||
stage2_flush_pmds(kvm, pud, addr, next);
|
||||
stage2_flush_pmds(mmu, pud, addr, next);
|
||||
}
|
||||
} while (pud++, addr = next, addr != end);
|
||||
}
|
||||
|
||||
static void stage2_flush_p4ds(struct kvm *kvm, pgd_t *pgd,
|
||||
static void stage2_flush_p4ds(struct kvm_s2_mmu *mmu, pgd_t *pgd,
|
||||
phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
p4d_t *p4d;
|
||||
phys_addr_t next;
|
||||
|
||||
|
@ -421,23 +435,24 @@ static void stage2_flush_p4ds(struct kvm *kvm, pgd_t *pgd,
|
|||
do {
|
||||
next = stage2_p4d_addr_end(kvm, addr, end);
|
||||
if (!stage2_p4d_none(kvm, *p4d))
|
||||
stage2_flush_puds(kvm, p4d, addr, next);
|
||||
stage2_flush_puds(mmu, p4d, addr, next);
|
||||
} while (p4d++, addr = next, addr != end);
|
||||
}
|
||||
|
||||
static void stage2_flush_memslot(struct kvm *kvm,
|
||||
struct kvm_memory_slot *memslot)
|
||||
{
|
||||
struct kvm_s2_mmu *mmu = &kvm->arch.mmu;
|
||||
phys_addr_t addr = memslot->base_gfn << PAGE_SHIFT;
|
||||
phys_addr_t end = addr + PAGE_SIZE * memslot->npages;
|
||||
phys_addr_t next;
|
||||
pgd_t *pgd;
|
||||
|
||||
pgd = kvm->arch.pgd + stage2_pgd_index(kvm, addr);
|
||||
pgd = mmu->pgd + stage2_pgd_index(kvm, addr);
|
||||
do {
|
||||
next = stage2_pgd_addr_end(kvm, addr, end);
|
||||
if (!stage2_pgd_none(kvm, *pgd))
|
||||
stage2_flush_p4ds(kvm, pgd, addr, next);
|
||||
stage2_flush_p4ds(mmu, pgd, addr, next);
|
||||
|
||||
if (next != end)
|
||||
cond_resched_lock(&kvm->mmu_lock);
|
||||
|
@ -964,21 +979,23 @@ int create_hyp_exec_mappings(phys_addr_t phys_addr, size_t size,
|
|||
}
|
||||
|
||||
/**
|
||||
* kvm_alloc_stage2_pgd - allocate level-1 table for stage-2 translation.
|
||||
* @kvm: The KVM struct pointer for the VM.
|
||||
* kvm_init_stage2_mmu - Initialise a S2 MMU strucrure
|
||||
* @kvm: The pointer to the KVM structure
|
||||
* @mmu: The pointer to the s2 MMU structure
|
||||
*
|
||||
* Allocates only the stage-2 HW PGD level table(s) of size defined by
|
||||
* stage2_pgd_size(kvm).
|
||||
* stage2_pgd_size(mmu->kvm).
|
||||
*
|
||||
* Note we don't need locking here as this is only called when the VM is
|
||||
* created, which can only be done once.
|
||||
*/
|
||||
int kvm_alloc_stage2_pgd(struct kvm *kvm)
|
||||
int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu)
|
||||
{
|
||||
phys_addr_t pgd_phys;
|
||||
pgd_t *pgd;
|
||||
int cpu;
|
||||
|
||||
if (kvm->arch.pgd != NULL) {
|
||||
if (mmu->pgd != NULL) {
|
||||
kvm_err("kvm_arch already initialized?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -992,8 +1009,20 @@ int kvm_alloc_stage2_pgd(struct kvm *kvm)
|
|||
if (WARN_ON(pgd_phys & ~kvm_vttbr_baddr_mask(kvm)))
|
||||
return -EINVAL;
|
||||
|
||||
kvm->arch.pgd = pgd;
|
||||
kvm->arch.pgd_phys = pgd_phys;
|
||||
mmu->last_vcpu_ran = alloc_percpu(typeof(*mmu->last_vcpu_ran));
|
||||
if (!mmu->last_vcpu_ran) {
|
||||
free_pages_exact(pgd, stage2_pgd_size(kvm));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
*per_cpu_ptr(mmu->last_vcpu_ran, cpu) = -1;
|
||||
|
||||
mmu->kvm = kvm;
|
||||
mmu->pgd = pgd;
|
||||
mmu->pgd_phys = pgd_phys;
|
||||
mmu->vmid.vmid_gen = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1032,7 +1061,7 @@ static void stage2_unmap_memslot(struct kvm *kvm,
|
|||
|
||||
if (!(vma->vm_flags & VM_PFNMAP)) {
|
||||
gpa_t gpa = addr + (vm_start - memslot->userspace_addr);
|
||||
unmap_stage2_range(kvm, gpa, vm_end - vm_start);
|
||||
unmap_stage2_range(&kvm->arch.mmu, gpa, vm_end - vm_start);
|
||||
}
|
||||
hva = vm_end;
|
||||
} while (hva < reg_end);
|
||||
|
@ -1064,39 +1093,34 @@ void stage2_unmap_vm(struct kvm *kvm)
|
|||
srcu_read_unlock(&kvm->srcu, idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_free_stage2_pgd - free all stage-2 tables
|
||||
* @kvm: The KVM struct pointer for the VM.
|
||||
*
|
||||
* Walks the level-1 page table pointed to by kvm->arch.pgd and frees all
|
||||
* underlying level-2 and level-3 tables before freeing the actual level-1 table
|
||||
* and setting the struct pointer to NULL.
|
||||
*/
|
||||
void kvm_free_stage2_pgd(struct kvm *kvm)
|
||||
void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
void *pgd = NULL;
|
||||
|
||||
spin_lock(&kvm->mmu_lock);
|
||||
if (kvm->arch.pgd) {
|
||||
unmap_stage2_range(kvm, 0, kvm_phys_size(kvm));
|
||||
pgd = READ_ONCE(kvm->arch.pgd);
|
||||
kvm->arch.pgd = NULL;
|
||||
kvm->arch.pgd_phys = 0;
|
||||
if (mmu->pgd) {
|
||||
unmap_stage2_range(mmu, 0, kvm_phys_size(kvm));
|
||||
pgd = READ_ONCE(mmu->pgd);
|
||||
mmu->pgd = NULL;
|
||||
}
|
||||
spin_unlock(&kvm->mmu_lock);
|
||||
|
||||
/* Free the HW pgd, one page at a time */
|
||||
if (pgd)
|
||||
if (pgd) {
|
||||
free_pages_exact(pgd, stage2_pgd_size(kvm));
|
||||
free_percpu(mmu->last_vcpu_ran);
|
||||
}
|
||||
}
|
||||
|
||||
static p4d_t *stage2_get_p4d(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
|
||||
static p4d_t *stage2_get_p4d(struct kvm_s2_mmu *mmu, struct kvm_mmu_memory_cache *cache,
|
||||
phys_addr_t addr)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
pgd_t *pgd;
|
||||
p4d_t *p4d;
|
||||
|
||||
pgd = kvm->arch.pgd + stage2_pgd_index(kvm, addr);
|
||||
pgd = mmu->pgd + stage2_pgd_index(kvm, addr);
|
||||
if (stage2_pgd_none(kvm, *pgd)) {
|
||||
if (!cache)
|
||||
return NULL;
|
||||
|
@ -1108,13 +1132,14 @@ static p4d_t *stage2_get_p4d(struct kvm *kvm, struct kvm_mmu_memory_cache *cache
|
|||
return stage2_p4d_offset(kvm, pgd, addr);
|
||||
}
|
||||
|
||||
static pud_t *stage2_get_pud(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
|
||||
static pud_t *stage2_get_pud(struct kvm_s2_mmu *mmu, struct kvm_mmu_memory_cache *cache,
|
||||
phys_addr_t addr)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
p4d_t *p4d;
|
||||
pud_t *pud;
|
||||
|
||||
p4d = stage2_get_p4d(kvm, cache, addr);
|
||||
p4d = stage2_get_p4d(mmu, cache, addr);
|
||||
if (stage2_p4d_none(kvm, *p4d)) {
|
||||
if (!cache)
|
||||
return NULL;
|
||||
|
@ -1126,13 +1151,14 @@ static pud_t *stage2_get_pud(struct kvm *kvm, struct kvm_mmu_memory_cache *cache
|
|||
return stage2_pud_offset(kvm, p4d, addr);
|
||||
}
|
||||
|
||||
static pmd_t *stage2_get_pmd(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
|
||||
static pmd_t *stage2_get_pmd(struct kvm_s2_mmu *mmu, struct kvm_mmu_memory_cache *cache,
|
||||
phys_addr_t addr)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
|
||||
pud = stage2_get_pud(kvm, cache, addr);
|
||||
pud = stage2_get_pud(mmu, cache, addr);
|
||||
if (!pud || stage2_pud_huge(kvm, *pud))
|
||||
return NULL;
|
||||
|
||||
|
@ -1147,13 +1173,14 @@ static pmd_t *stage2_get_pmd(struct kvm *kvm, struct kvm_mmu_memory_cache *cache
|
|||
return stage2_pmd_offset(kvm, pud, addr);
|
||||
}
|
||||
|
||||
static int stage2_set_pmd_huge(struct kvm *kvm, struct kvm_mmu_memory_cache
|
||||
*cache, phys_addr_t addr, const pmd_t *new_pmd)
|
||||
static int stage2_set_pmd_huge(struct kvm_s2_mmu *mmu,
|
||||
struct kvm_mmu_memory_cache *cache,
|
||||
phys_addr_t addr, const pmd_t *new_pmd)
|
||||
{
|
||||
pmd_t *pmd, old_pmd;
|
||||
|
||||
retry:
|
||||
pmd = stage2_get_pmd(kvm, cache, addr);
|
||||
pmd = stage2_get_pmd(mmu, cache, addr);
|
||||
VM_BUG_ON(!pmd);
|
||||
|
||||
old_pmd = *pmd;
|
||||
|
@ -1186,7 +1213,7 @@ retry:
|
|||
* get handled accordingly.
|
||||
*/
|
||||
if (!pmd_thp_or_huge(old_pmd)) {
|
||||
unmap_stage2_range(kvm, addr & S2_PMD_MASK, S2_PMD_SIZE);
|
||||
unmap_stage2_range(mmu, addr & S2_PMD_MASK, S2_PMD_SIZE);
|
||||
goto retry;
|
||||
}
|
||||
/*
|
||||
|
@ -1202,7 +1229,7 @@ retry:
|
|||
*/
|
||||
WARN_ON_ONCE(pmd_pfn(old_pmd) != pmd_pfn(*new_pmd));
|
||||
pmd_clear(pmd);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
kvm_tlb_flush_vmid_ipa(mmu, addr, S2_PMD_LEVEL);
|
||||
} else {
|
||||
get_page(virt_to_page(pmd));
|
||||
}
|
||||
|
@ -1211,13 +1238,15 @@ retry:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int stage2_set_pud_huge(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
|
||||
static int stage2_set_pud_huge(struct kvm_s2_mmu *mmu,
|
||||
struct kvm_mmu_memory_cache *cache,
|
||||
phys_addr_t addr, const pud_t *new_pudp)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
pud_t *pudp, old_pud;
|
||||
|
||||
retry:
|
||||
pudp = stage2_get_pud(kvm, cache, addr);
|
||||
pudp = stage2_get_pud(mmu, cache, addr);
|
||||
VM_BUG_ON(!pudp);
|
||||
|
||||
old_pud = *pudp;
|
||||
|
@ -1236,13 +1265,13 @@ retry:
|
|||
* the range for this block and retry.
|
||||
*/
|
||||
if (!stage2_pud_huge(kvm, old_pud)) {
|
||||
unmap_stage2_range(kvm, addr & S2_PUD_MASK, S2_PUD_SIZE);
|
||||
unmap_stage2_range(mmu, addr & S2_PUD_MASK, S2_PUD_SIZE);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
WARN_ON_ONCE(kvm_pud_pfn(old_pud) != kvm_pud_pfn(*new_pudp));
|
||||
stage2_pud_clear(kvm, pudp);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
kvm_tlb_flush_vmid_ipa(mmu, addr, S2_PUD_LEVEL);
|
||||
} else {
|
||||
get_page(virt_to_page(pudp));
|
||||
}
|
||||
|
@ -1257,9 +1286,10 @@ retry:
|
|||
* leaf-entry is returned in the appropriate level variable - pudpp,
|
||||
* pmdpp, ptepp.
|
||||
*/
|
||||
static bool stage2_get_leaf_entry(struct kvm *kvm, phys_addr_t addr,
|
||||
static bool stage2_get_leaf_entry(struct kvm_s2_mmu *mmu, phys_addr_t addr,
|
||||
pud_t **pudpp, pmd_t **pmdpp, pte_t **ptepp)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
pud_t *pudp;
|
||||
pmd_t *pmdp;
|
||||
pte_t *ptep;
|
||||
|
@ -1268,7 +1298,7 @@ static bool stage2_get_leaf_entry(struct kvm *kvm, phys_addr_t addr,
|
|||
*pmdpp = NULL;
|
||||
*ptepp = NULL;
|
||||
|
||||
pudp = stage2_get_pud(kvm, NULL, addr);
|
||||
pudp = stage2_get_pud(mmu, NULL, addr);
|
||||
if (!pudp || stage2_pud_none(kvm, *pudp) || !stage2_pud_present(kvm, *pudp))
|
||||
return false;
|
||||
|
||||
|
@ -1294,14 +1324,14 @@ static bool stage2_get_leaf_entry(struct kvm *kvm, phys_addr_t addr,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool stage2_is_exec(struct kvm *kvm, phys_addr_t addr, unsigned long sz)
|
||||
static bool stage2_is_exec(struct kvm_s2_mmu *mmu, phys_addr_t addr, unsigned long sz)
|
||||
{
|
||||
pud_t *pudp;
|
||||
pmd_t *pmdp;
|
||||
pte_t *ptep;
|
||||
bool found;
|
||||
|
||||
found = stage2_get_leaf_entry(kvm, addr, &pudp, &pmdp, &ptep);
|
||||
found = stage2_get_leaf_entry(mmu, addr, &pudp, &pmdp, &ptep);
|
||||
if (!found)
|
||||
return false;
|
||||
|
||||
|
@ -1313,10 +1343,12 @@ static bool stage2_is_exec(struct kvm *kvm, phys_addr_t addr, unsigned long sz)
|
|||
return sz == PAGE_SIZE && kvm_s2pte_exec(ptep);
|
||||
}
|
||||
|
||||
static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
|
||||
static int stage2_set_pte(struct kvm_s2_mmu *mmu,
|
||||
struct kvm_mmu_memory_cache *cache,
|
||||
phys_addr_t addr, const pte_t *new_pte,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
pte_t *pte, old_pte;
|
||||
|
@ -1326,7 +1358,7 @@ static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
|
|||
VM_BUG_ON(logging_active && !cache);
|
||||
|
||||
/* Create stage-2 page table mapping - Levels 0 and 1 */
|
||||
pud = stage2_get_pud(kvm, cache, addr);
|
||||
pud = stage2_get_pud(mmu, cache, addr);
|
||||
if (!pud) {
|
||||
/*
|
||||
* Ignore calls from kvm_set_spte_hva for unallocated
|
||||
|
@ -1340,7 +1372,7 @@ static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
|
|||
* on to allocate page.
|
||||
*/
|
||||
if (logging_active)
|
||||
stage2_dissolve_pud(kvm, addr, pud);
|
||||
stage2_dissolve_pud(mmu, addr, pud);
|
||||
|
||||
if (stage2_pud_none(kvm, *pud)) {
|
||||
if (!cache)
|
||||
|
@ -1364,7 +1396,7 @@ static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
|
|||
* allocate page.
|
||||
*/
|
||||
if (logging_active)
|
||||
stage2_dissolve_pmd(kvm, addr, pmd);
|
||||
stage2_dissolve_pmd(mmu, addr, pmd);
|
||||
|
||||
/* Create stage-2 page mappings - Level 2 */
|
||||
if (pmd_none(*pmd)) {
|
||||
|
@ -1388,7 +1420,7 @@ static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
|
|||
return 0;
|
||||
|
||||
kvm_set_pte(pte, __pte(0));
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
kvm_tlb_flush_vmid_ipa(mmu, addr, S2_PTE_LEVEL);
|
||||
} else {
|
||||
get_page(virt_to_page(pte));
|
||||
}
|
||||
|
@ -1453,8 +1485,8 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
|
|||
if (ret)
|
||||
goto out;
|
||||
spin_lock(&kvm->mmu_lock);
|
||||
ret = stage2_set_pte(kvm, &cache, addr, &pte,
|
||||
KVM_S2PTE_FLAG_IS_IOMAP);
|
||||
ret = stage2_set_pte(&kvm->arch.mmu, &cache, addr, &pte,
|
||||
KVM_S2PTE_FLAG_IS_IOMAP);
|
||||
spin_unlock(&kvm->mmu_lock);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
@ -1493,9 +1525,10 @@ static void stage2_wp_ptes(pmd_t *pmd, phys_addr_t addr, phys_addr_t end)
|
|||
* @addr: range start address
|
||||
* @end: range end address
|
||||
*/
|
||||
static void stage2_wp_pmds(struct kvm *kvm, pud_t *pud,
|
||||
static void stage2_wp_pmds(struct kvm_s2_mmu *mmu, pud_t *pud,
|
||||
phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
pmd_t *pmd;
|
||||
phys_addr_t next;
|
||||
|
||||
|
@ -1516,13 +1549,14 @@ static void stage2_wp_pmds(struct kvm *kvm, pud_t *pud,
|
|||
|
||||
/**
|
||||
* stage2_wp_puds - write protect P4D range
|
||||
* @pgd: pointer to pgd entry
|
||||
* @p4d: pointer to p4d entry
|
||||
* @addr: range start address
|
||||
* @end: range end address
|
||||
*/
|
||||
static void stage2_wp_puds(struct kvm *kvm, p4d_t *p4d,
|
||||
static void stage2_wp_puds(struct kvm_s2_mmu *mmu, p4d_t *p4d,
|
||||
phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
pud_t *pud;
|
||||
phys_addr_t next;
|
||||
|
||||
|
@ -1534,7 +1568,7 @@ static void stage2_wp_puds(struct kvm *kvm, p4d_t *p4d,
|
|||
if (!kvm_s2pud_readonly(pud))
|
||||
kvm_set_s2pud_readonly(pud);
|
||||
} else {
|
||||
stage2_wp_pmds(kvm, pud, addr, next);
|
||||
stage2_wp_pmds(mmu, pud, addr, next);
|
||||
}
|
||||
}
|
||||
} while (pud++, addr = next, addr != end);
|
||||
|
@ -1546,9 +1580,10 @@ static void stage2_wp_puds(struct kvm *kvm, p4d_t *p4d,
|
|||
* @addr: range start address
|
||||
* @end: range end address
|
||||
*/
|
||||
static void stage2_wp_p4ds(struct kvm *kvm, pgd_t *pgd,
|
||||
static void stage2_wp_p4ds(struct kvm_s2_mmu *mmu, pgd_t *pgd,
|
||||
phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
p4d_t *p4d;
|
||||
phys_addr_t next;
|
||||
|
||||
|
@ -1556,7 +1591,7 @@ static void stage2_wp_p4ds(struct kvm *kvm, pgd_t *pgd,
|
|||
do {
|
||||
next = stage2_p4d_addr_end(kvm, addr, end);
|
||||
if (!stage2_p4d_none(kvm, *p4d))
|
||||
stage2_wp_puds(kvm, p4d, addr, next);
|
||||
stage2_wp_puds(mmu, p4d, addr, next);
|
||||
} while (p4d++, addr = next, addr != end);
|
||||
}
|
||||
|
||||
|
@ -1566,12 +1601,13 @@ static void stage2_wp_p4ds(struct kvm *kvm, pgd_t *pgd,
|
|||
* @addr: Start address of range
|
||||
* @end: End address of range
|
||||
*/
|
||||
static void stage2_wp_range(struct kvm *kvm, phys_addr_t addr, phys_addr_t end)
|
||||
static void stage2_wp_range(struct kvm_s2_mmu *mmu, phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
struct kvm *kvm = mmu->kvm;
|
||||
pgd_t *pgd;
|
||||
phys_addr_t next;
|
||||
|
||||
pgd = kvm->arch.pgd + stage2_pgd_index(kvm, addr);
|
||||
pgd = mmu->pgd + stage2_pgd_index(kvm, addr);
|
||||
do {
|
||||
/*
|
||||
* Release kvm_mmu_lock periodically if the memory region is
|
||||
|
@ -1583,11 +1619,11 @@ static void stage2_wp_range(struct kvm *kvm, phys_addr_t addr, phys_addr_t end)
|
|||
* the lock.
|
||||
*/
|
||||
cond_resched_lock(&kvm->mmu_lock);
|
||||
if (!READ_ONCE(kvm->arch.pgd))
|
||||
if (!READ_ONCE(mmu->pgd))
|
||||
break;
|
||||
next = stage2_pgd_addr_end(kvm, addr, end);
|
||||
if (stage2_pgd_present(kvm, *pgd))
|
||||
stage2_wp_p4ds(kvm, pgd, addr, next);
|
||||
stage2_wp_p4ds(mmu, pgd, addr, next);
|
||||
} while (pgd++, addr = next, addr != end);
|
||||
}
|
||||
|
||||
|
@ -1617,7 +1653,7 @@ void kvm_mmu_wp_memory_region(struct kvm *kvm, int slot)
|
|||
end = (memslot->base_gfn + memslot->npages) << PAGE_SHIFT;
|
||||
|
||||
spin_lock(&kvm->mmu_lock);
|
||||
stage2_wp_range(kvm, start, end);
|
||||
stage2_wp_range(&kvm->arch.mmu, start, end);
|
||||
spin_unlock(&kvm->mmu_lock);
|
||||
kvm_flush_remote_tlbs(kvm);
|
||||
}
|
||||
|
@ -1641,7 +1677,7 @@ static void kvm_mmu_write_protect_pt_masked(struct kvm *kvm,
|
|||
phys_addr_t start = (base_gfn + __ffs(mask)) << PAGE_SHIFT;
|
||||
phys_addr_t end = (base_gfn + __fls(mask) + 1) << PAGE_SHIFT;
|
||||
|
||||
stage2_wp_range(kvm, start, end);
|
||||
stage2_wp_range(&kvm->arch.mmu, start, end);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1804,6 +1840,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||
pgprot_t mem_type = PAGE_S2;
|
||||
bool logging_active = memslot_is_logging(memslot);
|
||||
unsigned long vma_pagesize, flags = 0;
|
||||
struct kvm_s2_mmu *mmu = vcpu->arch.hw_mmu;
|
||||
|
||||
write_fault = kvm_is_write_fault(vcpu);
|
||||
exec_fault = kvm_vcpu_trap_is_iabt(vcpu);
|
||||
|
@ -1925,7 +1962,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||
*/
|
||||
needs_exec = exec_fault ||
|
||||
(fault_status == FSC_PERM &&
|
||||
stage2_is_exec(kvm, fault_ipa, vma_pagesize));
|
||||
stage2_is_exec(mmu, fault_ipa, vma_pagesize));
|
||||
|
||||
if (vma_pagesize == PUD_SIZE) {
|
||||
pud_t new_pud = kvm_pfn_pud(pfn, mem_type);
|
||||
|
@ -1937,7 +1974,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||
if (needs_exec)
|
||||
new_pud = kvm_s2pud_mkexec(new_pud);
|
||||
|
||||
ret = stage2_set_pud_huge(kvm, memcache, fault_ipa, &new_pud);
|
||||
ret = stage2_set_pud_huge(mmu, memcache, fault_ipa, &new_pud);
|
||||
} else if (vma_pagesize == PMD_SIZE) {
|
||||
pmd_t new_pmd = kvm_pfn_pmd(pfn, mem_type);
|
||||
|
||||
|
@ -1949,7 +1986,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||
if (needs_exec)
|
||||
new_pmd = kvm_s2pmd_mkexec(new_pmd);
|
||||
|
||||
ret = stage2_set_pmd_huge(kvm, memcache, fault_ipa, &new_pmd);
|
||||
ret = stage2_set_pmd_huge(mmu, memcache, fault_ipa, &new_pmd);
|
||||
} else {
|
||||
pte_t new_pte = kvm_pfn_pte(pfn, mem_type);
|
||||
|
||||
|
@ -1961,7 +1998,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||
if (needs_exec)
|
||||
new_pte = kvm_s2pte_mkexec(new_pte);
|
||||
|
||||
ret = stage2_set_pte(kvm, memcache, fault_ipa, &new_pte, flags);
|
||||
ret = stage2_set_pte(mmu, memcache, fault_ipa, &new_pte, flags);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
|
@ -1990,7 +2027,7 @@ static void handle_access_fault(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
|
|||
|
||||
spin_lock(&vcpu->kvm->mmu_lock);
|
||||
|
||||
if (!stage2_get_leaf_entry(vcpu->kvm, fault_ipa, &pud, &pmd, &pte))
|
||||
if (!stage2_get_leaf_entry(vcpu->arch.hw_mmu, fault_ipa, &pud, &pmd, &pte))
|
||||
goto out;
|
||||
|
||||
if (pud) { /* HugeTLB */
|
||||
|
@ -2040,21 +2077,18 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
|
|||
is_iabt = kvm_vcpu_trap_is_iabt(vcpu);
|
||||
|
||||
/* Synchronous External Abort? */
|
||||
if (kvm_vcpu_dabt_isextabt(vcpu)) {
|
||||
if (kvm_vcpu_abt_issea(vcpu)) {
|
||||
/*
|
||||
* For RAS the host kernel may handle this abort.
|
||||
* There is no need to pass the error into the guest.
|
||||
*/
|
||||
if (!kvm_handle_guest_sea(fault_ipa, kvm_vcpu_get_hsr(vcpu)))
|
||||
return 1;
|
||||
|
||||
if (unlikely(!is_iabt)) {
|
||||
if (kvm_handle_guest_sea(fault_ipa, kvm_vcpu_get_esr(vcpu)))
|
||||
kvm_inject_vabt(vcpu);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
trace_kvm_guest_fault(*vcpu_pc(vcpu), kvm_vcpu_get_hsr(vcpu),
|
||||
trace_kvm_guest_fault(*vcpu_pc(vcpu), kvm_vcpu_get_esr(vcpu),
|
||||
kvm_vcpu_get_hfar(vcpu), fault_ipa);
|
||||
|
||||
/* Check the stage-2 fault is trans. fault or write fault */
|
||||
|
@ -2063,7 +2097,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
|
|||
kvm_err("Unsupported FSC: EC=%#x xFSC=%#lx ESR_EL2=%#lx\n",
|
||||
kvm_vcpu_trap_get_class(vcpu),
|
||||
(unsigned long)kvm_vcpu_trap_get_fault(vcpu),
|
||||
(unsigned long)kvm_vcpu_get_hsr(vcpu));
|
||||
(unsigned long)kvm_vcpu_get_esr(vcpu));
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
|
@ -2074,12 +2108,23 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
|
|||
hva = gfn_to_hva_memslot_prot(memslot, gfn, &writable);
|
||||
write_fault = kvm_is_write_fault(vcpu);
|
||||
if (kvm_is_error_hva(hva) || (write_fault && !writable)) {
|
||||
/*
|
||||
* 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), so
|
||||
* re-inject the abort back into the guest.
|
||||
*/
|
||||
if (is_iabt) {
|
||||
/* Prefetch Abort on I/O address */
|
||||
ret = -ENOEXEC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (kvm_vcpu_dabt_iss1tw(vcpu)) {
|
||||
kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
|
||||
ret = 1;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for a cache maintenance operation. Since we
|
||||
* ended-up here, we know it is outside of any memory
|
||||
|
@ -2090,7 +2135,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
|
|||
* So let's assume that the guest is just being
|
||||
* cautious, and skip the instruction.
|
||||
*/
|
||||
if (kvm_vcpu_dabt_is_cm(vcpu)) {
|
||||
if (kvm_is_error_hva(hva) && kvm_vcpu_dabt_is_cm(vcpu)) {
|
||||
kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
|
||||
ret = 1;
|
||||
goto out_unlock;
|
||||
|
@ -2163,14 +2208,14 @@ static int handle_hva_to_gpa(struct kvm *kvm,
|
|||
|
||||
static int kvm_unmap_hva_handler(struct kvm *kvm, gpa_t gpa, u64 size, void *data)
|
||||
{
|
||||
unmap_stage2_range(kvm, gpa, size);
|
||||
unmap_stage2_range(&kvm->arch.mmu, gpa, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_unmap_hva_range(struct kvm *kvm,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
if (!kvm->arch.pgd)
|
||||
if (!kvm->arch.mmu.pgd)
|
||||
return 0;
|
||||
|
||||
trace_kvm_unmap_hva_range(start, end);
|
||||
|
@ -2190,7 +2235,7 @@ static int kvm_set_spte_handler(struct kvm *kvm, gpa_t gpa, u64 size, void *data
|
|||
* therefore stage2_set_pte() never needs to clear out a huge PMD
|
||||
* through this calling path.
|
||||
*/
|
||||
stage2_set_pte(kvm, NULL, gpa, pte, 0);
|
||||
stage2_set_pte(&kvm->arch.mmu, NULL, gpa, pte, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2201,7 +2246,7 @@ int kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte)
|
|||
kvm_pfn_t pfn = pte_pfn(pte);
|
||||
pte_t stage2_pte;
|
||||
|
||||
if (!kvm->arch.pgd)
|
||||
if (!kvm->arch.mmu.pgd)
|
||||
return 0;
|
||||
|
||||
trace_kvm_set_spte_hva(hva);
|
||||
|
@ -2224,7 +2269,7 @@ static int kvm_age_hva_handler(struct kvm *kvm, gpa_t gpa, u64 size, void *data)
|
|||
pte_t *pte;
|
||||
|
||||
WARN_ON(size != PAGE_SIZE && size != PMD_SIZE && size != PUD_SIZE);
|
||||
if (!stage2_get_leaf_entry(kvm, gpa, &pud, &pmd, &pte))
|
||||
if (!stage2_get_leaf_entry(&kvm->arch.mmu, gpa, &pud, &pmd, &pte))
|
||||
return 0;
|
||||
|
||||
if (pud)
|
||||
|
@ -2242,7 +2287,7 @@ static int kvm_test_age_hva_handler(struct kvm *kvm, gpa_t gpa, u64 size, void *
|
|||
pte_t *pte;
|
||||
|
||||
WARN_ON(size != PAGE_SIZE && size != PMD_SIZE && size != PUD_SIZE);
|
||||
if (!stage2_get_leaf_entry(kvm, gpa, &pud, &pmd, &pte))
|
||||
if (!stage2_get_leaf_entry(&kvm->arch.mmu, gpa, &pud, &pmd, &pte))
|
||||
return 0;
|
||||
|
||||
if (pud)
|
||||
|
@ -2255,7 +2300,7 @@ static int kvm_test_age_hva_handler(struct kvm *kvm, gpa_t gpa, u64 size, void *
|
|||
|
||||
int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end)
|
||||
{
|
||||
if (!kvm->arch.pgd)
|
||||
if (!kvm->arch.mmu.pgd)
|
||||
return 0;
|
||||
trace_kvm_age_hva(start, end);
|
||||
return handle_hva_to_gpa(kvm, start, end, kvm_age_hva_handler, NULL);
|
||||
|
@ -2263,7 +2308,7 @@ int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end)
|
|||
|
||||
int kvm_test_age_hva(struct kvm *kvm, unsigned long hva)
|
||||
{
|
||||
if (!kvm->arch.pgd)
|
||||
if (!kvm->arch.mmu.pgd)
|
||||
return 0;
|
||||
trace_kvm_test_age_hva(hva);
|
||||
return handle_hva_to_gpa(kvm, hva, hva + PAGE_SIZE,
|
||||
|
@ -2476,7 +2521,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
|
|||
|
||||
spin_lock(&kvm->mmu_lock);
|
||||
if (ret)
|
||||
unmap_stage2_range(kvm, mem->guest_phys_addr, mem->memory_size);
|
||||
unmap_stage2_range(&kvm->arch.mmu, mem->guest_phys_addr, mem->memory_size);
|
||||
else
|
||||
stage2_flush_memslot(kvm, memslot);
|
||||
spin_unlock(&kvm->mmu_lock);
|
||||
|
@ -2495,7 +2540,7 @@ void kvm_arch_memslots_updated(struct kvm *kvm, u64 gen)
|
|||
|
||||
void kvm_arch_flush_shadow_all(struct kvm *kvm)
|
||||
{
|
||||
kvm_free_stage2_pgd(kvm);
|
||||
kvm_free_stage2_pgd(&kvm->arch.mmu);
|
||||
}
|
||||
|
||||
void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
|
||||
|
@ -2505,7 +2550,7 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
|
|||
phys_addr_t size = slot->npages << PAGE_SHIFT;
|
||||
|
||||
spin_lock(&kvm->mmu_lock);
|
||||
unmap_stage2_range(kvm, gpa, size);
|
||||
unmap_stage2_range(&kvm->arch.mmu, gpa, size);
|
||||
spin_unlock(&kvm->mmu_lock);
|
||||
}
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ static const unsigned long vcpu_reg_offsets[VCPU_NR_MODES][16] = {
|
|||
*/
|
||||
unsigned long *vcpu_reg32(const struct kvm_vcpu *vcpu, u8 reg_num)
|
||||
{
|
||||
unsigned long *reg_array = (unsigned long *)&vcpu->arch.ctxt.gp_regs.regs;
|
||||
unsigned long *reg_array = (unsigned long *)&vcpu->arch.ctxt.regs;
|
||||
unsigned long mode = *vcpu_cpsr(vcpu) & PSR_AA32_MODE_MASK;
|
||||
|
||||
switch (mode) {
|
||||
|
@ -147,8 +147,20 @@ unsigned long vcpu_read_spsr32(const struct kvm_vcpu *vcpu)
|
|||
{
|
||||
int spsr_idx = vcpu_spsr32_mode(vcpu);
|
||||
|
||||
if (!vcpu->arch.sysregs_loaded_on_cpu)
|
||||
return vcpu_gp_regs(vcpu)->spsr[spsr_idx];
|
||||
if (!vcpu->arch.sysregs_loaded_on_cpu) {
|
||||
switch (spsr_idx) {
|
||||
case KVM_SPSR_SVC:
|
||||
return __vcpu_sys_reg(vcpu, SPSR_EL1);
|
||||
case KVM_SPSR_ABT:
|
||||
return vcpu->arch.ctxt.spsr_abt;
|
||||
case KVM_SPSR_UND:
|
||||
return vcpu->arch.ctxt.spsr_und;
|
||||
case KVM_SPSR_IRQ:
|
||||
return vcpu->arch.ctxt.spsr_irq;
|
||||
case KVM_SPSR_FIQ:
|
||||
return vcpu->arch.ctxt.spsr_fiq;
|
||||
}
|
||||
}
|
||||
|
||||
switch (spsr_idx) {
|
||||
case KVM_SPSR_SVC:
|
||||
|
@ -171,7 +183,24 @@ void vcpu_write_spsr32(struct kvm_vcpu *vcpu, unsigned long v)
|
|||
int spsr_idx = vcpu_spsr32_mode(vcpu);
|
||||
|
||||
if (!vcpu->arch.sysregs_loaded_on_cpu) {
|
||||
vcpu_gp_regs(vcpu)->spsr[spsr_idx] = v;
|
||||
switch (spsr_idx) {
|
||||
case KVM_SPSR_SVC:
|
||||
__vcpu_sys_reg(vcpu, SPSR_EL1) = v;
|
||||
break;
|
||||
case KVM_SPSR_ABT:
|
||||
vcpu->arch.ctxt.spsr_abt = v;
|
||||
break;
|
||||
case KVM_SPSR_UND:
|
||||
vcpu->arch.ctxt.spsr_und = v;
|
||||
break;
|
||||
case KVM_SPSR_IRQ:
|
||||
vcpu->arch.ctxt.spsr_irq = v;
|
||||
break;
|
||||
case KVM_SPSR_FIQ:
|
||||
vcpu->arch.ctxt.spsr_fiq = v;
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,11 @@ static u32 kvm_ipa_limit;
|
|||
#define VCPU_RESET_PSTATE_SVC (PSR_AA32_MODE_SVC | PSR_AA32_A_BIT | \
|
||||
PSR_AA32_I_BIT | PSR_AA32_F_BIT)
|
||||
|
||||
static bool system_has_full_ptr_auth(void)
|
||||
{
|
||||
return system_supports_address_auth() && system_supports_generic_auth();
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_arch_vm_ioctl_check_extension
|
||||
*
|
||||
|
@ -80,8 +85,7 @@ int kvm_arch_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
|||
break;
|
||||
case KVM_CAP_ARM_PTRAUTH_ADDRESS:
|
||||
case KVM_CAP_ARM_PTRAUTH_GENERIC:
|
||||
r = has_vhe() && system_supports_address_auth() &&
|
||||
system_supports_generic_auth();
|
||||
r = system_has_full_ptr_auth();
|
||||
break;
|
||||
default:
|
||||
r = 0;
|
||||
|
@ -205,19 +209,14 @@ static void kvm_vcpu_reset_sve(struct kvm_vcpu *vcpu)
|
|||
|
||||
static int kvm_vcpu_enable_ptrauth(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/* Support ptrauth only if the system supports these capabilities. */
|
||||
if (!has_vhe())
|
||||
return -EINVAL;
|
||||
|
||||
if (!system_supports_address_auth() ||
|
||||
!system_supports_generic_auth())
|
||||
return -EINVAL;
|
||||
/*
|
||||
* For now make sure that both address/generic pointer authentication
|
||||
* features are requested by the userspace together.
|
||||
* features are requested by the userspace together and the system
|
||||
* supports these capabilities.
|
||||
*/
|
||||
if (!test_bit(KVM_ARM_VCPU_PTRAUTH_ADDRESS, vcpu->arch.features) ||
|
||||
!test_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, vcpu->arch.features))
|
||||
!test_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, vcpu->arch.features) ||
|
||||
!system_has_full_ptr_auth())
|
||||
return -EINVAL;
|
||||
|
||||
vcpu->arch.flags |= KVM_ARM64_GUEST_HAS_PTRAUTH;
|
||||
|
@ -292,7 +291,7 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
|
|||
|
||||
/* Reset core registers */
|
||||
memset(vcpu_gp_regs(vcpu), 0, sizeof(*vcpu_gp_regs(vcpu)));
|
||||
vcpu_gp_regs(vcpu)->regs.pstate = pstate;
|
||||
vcpu_gp_regs(vcpu)->pstate = pstate;
|
||||
|
||||
/* Reset system registers */
|
||||
kvm_reset_sys_regs(vcpu);
|
||||
|
|
|
@ -94,6 +94,7 @@ static bool __vcpu_read_sys_reg_from_cpu(int reg, u64 *val)
|
|||
case TPIDR_EL1: *val = read_sysreg_s(SYS_TPIDR_EL1); break;
|
||||
case AMAIR_EL1: *val = read_sysreg_s(SYS_AMAIR_EL12); break;
|
||||
case CNTKCTL_EL1: *val = read_sysreg_s(SYS_CNTKCTL_EL12); break;
|
||||
case ELR_EL1: *val = read_sysreg_s(SYS_ELR_EL12); break;
|
||||
case PAR_EL1: *val = read_sysreg_s(SYS_PAR_EL1); break;
|
||||
case DACR32_EL2: *val = read_sysreg_s(SYS_DACR32_EL2); break;
|
||||
case IFSR32_EL2: *val = read_sysreg_s(SYS_IFSR32_EL2); break;
|
||||
|
@ -133,6 +134,7 @@ static bool __vcpu_write_sys_reg_to_cpu(u64 val, int reg)
|
|||
case TPIDR_EL1: write_sysreg_s(val, SYS_TPIDR_EL1); break;
|
||||
case AMAIR_EL1: write_sysreg_s(val, SYS_AMAIR_EL12); break;
|
||||
case CNTKCTL_EL1: write_sysreg_s(val, SYS_CNTKCTL_EL12); break;
|
||||
case ELR_EL1: write_sysreg_s(val, SYS_ELR_EL12); break;
|
||||
case PAR_EL1: write_sysreg_s(val, SYS_PAR_EL1); break;
|
||||
case DACR32_EL2: write_sysreg_s(val, SYS_DACR32_EL2); break;
|
||||
case IFSR32_EL2: write_sysreg_s(val, SYS_IFSR32_EL2); break;
|
||||
|
@ -242,6 +244,25 @@ static bool access_vm_reg(struct kvm_vcpu *vcpu,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool access_actlr(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
if (p->is_write)
|
||||
return ignore_write(vcpu, p);
|
||||
|
||||
p->regval = vcpu_read_sys_reg(vcpu, ACTLR_EL1);
|
||||
|
||||
if (p->is_aarch32) {
|
||||
if (r->Op2 & 2)
|
||||
p->regval = upper_32_bits(p->regval);
|
||||
else
|
||||
p->regval = lower_32_bits(p->regval);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Trap handler for the GICv3 SGI generation system register.
|
||||
* Forward the request to the VGIC emulation.
|
||||
|
@ -615,6 +636,12 @@ static void reset_amair_el1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
|||
vcpu_write_sys_reg(vcpu, amair, AMAIR_EL1);
|
||||
}
|
||||
|
||||
static void reset_actlr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
||||
{
|
||||
u64 actlr = read_sysreg(actlr_el1);
|
||||
vcpu_write_sys_reg(vcpu, actlr, ACTLR_EL1);
|
||||
}
|
||||
|
||||
static void reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
||||
{
|
||||
u64 mpidr;
|
||||
|
@ -1518,6 +1545,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||
ID_UNALLOCATED(7,7),
|
||||
|
||||
{ SYS_DESC(SYS_SCTLR_EL1), access_vm_reg, reset_val, SCTLR_EL1, 0x00C50078 },
|
||||
{ SYS_DESC(SYS_ACTLR_EL1), access_actlr, reset_actlr, ACTLR_EL1 },
|
||||
{ SYS_DESC(SYS_CPACR_EL1), NULL, reset_val, CPACR_EL1, 0 },
|
||||
{ SYS_DESC(SYS_ZCR_EL1), NULL, reset_val, ZCR_EL1, 0, .visibility = sve_visibility },
|
||||
{ SYS_DESC(SYS_TTBR0_EL1), access_vm_reg, reset_unknown, TTBR0_EL1 },
|
||||
|
@ -1957,6 +1985,8 @@ static const struct sys_reg_desc cp14_64_regs[] = {
|
|||
static const struct sys_reg_desc cp15_regs[] = {
|
||||
{ Op1( 0), CRn( 0), CRm( 0), Op2( 1), access_ctr },
|
||||
{ Op1( 0), CRn( 1), CRm( 0), Op2( 0), access_vm_reg, NULL, c1_SCTLR },
|
||||
{ Op1( 0), CRn( 1), CRm( 0), Op2( 1), access_actlr },
|
||||
{ Op1( 0), CRn( 1), CRm( 0), Op2( 3), access_actlr },
|
||||
{ Op1( 0), CRn( 2), CRm( 0), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
|
||||
{ Op1( 0), CRn( 2), CRm( 0), Op2( 1), access_vm_reg, NULL, c2_TTBR1 },
|
||||
{ Op1( 0), CRn( 2), CRm( 0), Op2( 2), access_vm_reg, NULL, c2_TTBCR },
|
||||
|
@ -2109,36 +2139,6 @@ static int check_sysreg_table(const struct sys_reg_desc *table, unsigned int n,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Target specific emulation tables */
|
||||
static struct kvm_sys_reg_target_table *target_tables[KVM_ARM_NUM_TARGETS];
|
||||
|
||||
void kvm_register_target_sys_reg_table(unsigned int target,
|
||||
struct kvm_sys_reg_target_table *table)
|
||||
{
|
||||
if (check_sysreg_table(table->table64.table, table->table64.num, false) ||
|
||||
check_sysreg_table(table->table32.table, table->table32.num, true))
|
||||
return;
|
||||
|
||||
target_tables[target] = table;
|
||||
}
|
||||
|
||||
/* Get specific register table for this target. */
|
||||
static const struct sys_reg_desc *get_target_table(unsigned target,
|
||||
bool mode_is_64,
|
||||
size_t *num)
|
||||
{
|
||||
struct kvm_sys_reg_target_table *table;
|
||||
|
||||
table = target_tables[target];
|
||||
if (mode_is_64) {
|
||||
*num = table->table64.num;
|
||||
return table->table64.table;
|
||||
} else {
|
||||
*num = table->table32.num;
|
||||
return table->table32.table;
|
||||
}
|
||||
}
|
||||
|
||||
static int match_sys_reg(const void *key, const void *elt)
|
||||
{
|
||||
const unsigned long pval = (unsigned long)key;
|
||||
|
@ -2220,10 +2220,10 @@ static int emulate_cp(struct kvm_vcpu *vcpu,
|
|||
static void unhandled_cp_access(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *params)
|
||||
{
|
||||
u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu);
|
||||
u8 esr_ec = kvm_vcpu_trap_get_class(vcpu);
|
||||
int cp = -1;
|
||||
|
||||
switch(hsr_ec) {
|
||||
switch (esr_ec) {
|
||||
case ESR_ELx_EC_CP15_32:
|
||||
case ESR_ELx_EC_CP15_64:
|
||||
cp = 15;
|
||||
|
@ -2249,22 +2249,20 @@ static void unhandled_cp_access(struct kvm_vcpu *vcpu,
|
|||
*/
|
||||
static int kvm_handle_cp_64(struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *global,
|
||||
size_t nr_global,
|
||||
const struct sys_reg_desc *target_specific,
|
||||
size_t nr_specific)
|
||||
size_t nr_global)
|
||||
{
|
||||
struct sys_reg_params params;
|
||||
u32 hsr = kvm_vcpu_get_hsr(vcpu);
|
||||
u32 esr = kvm_vcpu_get_esr(vcpu);
|
||||
int Rt = kvm_vcpu_sys_get_rt(vcpu);
|
||||
int Rt2 = (hsr >> 10) & 0x1f;
|
||||
int Rt2 = (esr >> 10) & 0x1f;
|
||||
|
||||
params.is_aarch32 = true;
|
||||
params.is_32bit = false;
|
||||
params.CRm = (hsr >> 1) & 0xf;
|
||||
params.is_write = ((hsr & 1) == 0);
|
||||
params.CRm = (esr >> 1) & 0xf;
|
||||
params.is_write = ((esr & 1) == 0);
|
||||
|
||||
params.Op0 = 0;
|
||||
params.Op1 = (hsr >> 16) & 0xf;
|
||||
params.Op1 = (esr >> 16) & 0xf;
|
||||
params.Op2 = 0;
|
||||
params.CRn = 0;
|
||||
|
||||
|
@ -2278,14 +2276,11 @@ static int kvm_handle_cp_64(struct kvm_vcpu *vcpu,
|
|||
}
|
||||
|
||||
/*
|
||||
* Try to emulate the coprocessor access using the target
|
||||
* specific table first, and using the global table afterwards.
|
||||
* If either of the tables contains a handler, handle the
|
||||
* If the table contains a handler, handle the
|
||||
* potential register operation in the case of a read and return
|
||||
* with success.
|
||||
*/
|
||||
if (!emulate_cp(vcpu, ¶ms, target_specific, nr_specific) ||
|
||||
!emulate_cp(vcpu, ¶ms, global, nr_global)) {
|
||||
if (!emulate_cp(vcpu, ¶ms, global, nr_global)) {
|
||||
/* Split up the value between registers for the read side */
|
||||
if (!params.is_write) {
|
||||
vcpu_set_reg(vcpu, Rt, lower_32_bits(params.regval));
|
||||
|
@ -2306,26 +2301,23 @@ static int kvm_handle_cp_64(struct kvm_vcpu *vcpu,
|
|||
*/
|
||||
static int kvm_handle_cp_32(struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *global,
|
||||
size_t nr_global,
|
||||
const struct sys_reg_desc *target_specific,
|
||||
size_t nr_specific)
|
||||
size_t nr_global)
|
||||
{
|
||||
struct sys_reg_params params;
|
||||
u32 hsr = kvm_vcpu_get_hsr(vcpu);
|
||||
u32 esr = kvm_vcpu_get_esr(vcpu);
|
||||
int Rt = kvm_vcpu_sys_get_rt(vcpu);
|
||||
|
||||
params.is_aarch32 = true;
|
||||
params.is_32bit = true;
|
||||
params.CRm = (hsr >> 1) & 0xf;
|
||||
params.CRm = (esr >> 1) & 0xf;
|
||||
params.regval = vcpu_get_reg(vcpu, Rt);
|
||||
params.is_write = ((hsr & 1) == 0);
|
||||
params.CRn = (hsr >> 10) & 0xf;
|
||||
params.is_write = ((esr & 1) == 0);
|
||||
params.CRn = (esr >> 10) & 0xf;
|
||||
params.Op0 = 0;
|
||||
params.Op1 = (hsr >> 14) & 0x7;
|
||||
params.Op2 = (hsr >> 17) & 0x7;
|
||||
params.Op1 = (esr >> 14) & 0x7;
|
||||
params.Op2 = (esr >> 17) & 0x7;
|
||||
|
||||
if (!emulate_cp(vcpu, ¶ms, target_specific, nr_specific) ||
|
||||
!emulate_cp(vcpu, ¶ms, global, nr_global)) {
|
||||
if (!emulate_cp(vcpu, ¶ms, global, nr_global)) {
|
||||
if (!params.is_write)
|
||||
vcpu_set_reg(vcpu, Rt, params.regval);
|
||||
return 1;
|
||||
|
@ -2337,38 +2329,22 @@ static int kvm_handle_cp_32(struct kvm_vcpu *vcpu,
|
|||
|
||||
int kvm_handle_cp15_64(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
const struct sys_reg_desc *target_specific;
|
||||
size_t num;
|
||||
|
||||
target_specific = get_target_table(vcpu->arch.target, false, &num);
|
||||
return kvm_handle_cp_64(vcpu,
|
||||
cp15_64_regs, ARRAY_SIZE(cp15_64_regs),
|
||||
target_specific, num);
|
||||
return kvm_handle_cp_64(vcpu, cp15_64_regs, ARRAY_SIZE(cp15_64_regs));
|
||||
}
|
||||
|
||||
int kvm_handle_cp15_32(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
const struct sys_reg_desc *target_specific;
|
||||
size_t num;
|
||||
|
||||
target_specific = get_target_table(vcpu->arch.target, false, &num);
|
||||
return kvm_handle_cp_32(vcpu,
|
||||
cp15_regs, ARRAY_SIZE(cp15_regs),
|
||||
target_specific, num);
|
||||
return kvm_handle_cp_32(vcpu, cp15_regs, ARRAY_SIZE(cp15_regs));
|
||||
}
|
||||
|
||||
int kvm_handle_cp14_64(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return kvm_handle_cp_64(vcpu,
|
||||
cp14_64_regs, ARRAY_SIZE(cp14_64_regs),
|
||||
NULL, 0);
|
||||
return kvm_handle_cp_64(vcpu, cp14_64_regs, ARRAY_SIZE(cp14_64_regs));
|
||||
}
|
||||
|
||||
int kvm_handle_cp14_32(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return kvm_handle_cp_32(vcpu,
|
||||
cp14_regs, ARRAY_SIZE(cp14_regs),
|
||||
NULL, 0);
|
||||
return kvm_handle_cp_32(vcpu, cp14_regs, ARRAY_SIZE(cp14_regs));
|
||||
}
|
||||
|
||||
static bool is_imp_def_sys_reg(struct sys_reg_params *params)
|
||||
|
@ -2380,15 +2356,9 @@ static bool is_imp_def_sys_reg(struct sys_reg_params *params)
|
|||
static int emulate_sys_reg(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *params)
|
||||
{
|
||||
size_t num;
|
||||
const struct sys_reg_desc *table, *r;
|
||||
const struct sys_reg_desc *r;
|
||||
|
||||
table = get_target_table(vcpu->arch.target, true, &num);
|
||||
|
||||
/* Search target-specific then generic table. */
|
||||
r = find_reg(params, table, num);
|
||||
if (!r)
|
||||
r = find_reg(params, sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
|
||||
r = find_reg(params, sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
|
||||
|
||||
if (likely(r)) {
|
||||
perform_access(vcpu, params, r);
|
||||
|
@ -2403,14 +2373,20 @@ static int emulate_sys_reg(struct kvm_vcpu *vcpu,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void reset_sys_reg_descs(struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *table, size_t num)
|
||||
/**
|
||||
* kvm_reset_sys_regs - sets system registers to reset value
|
||||
* @vcpu: The VCPU pointer
|
||||
*
|
||||
* This function finds the right table above and sets the registers on the
|
||||
* virtual CPU struct to their architecturally defined reset values.
|
||||
*/
|
||||
void kvm_reset_sys_regs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
if (table[i].reset)
|
||||
table[i].reset(vcpu, &table[i]);
|
||||
for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++)
|
||||
if (sys_reg_descs[i].reset)
|
||||
sys_reg_descs[i].reset(vcpu, &sys_reg_descs[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2420,7 +2396,7 @@ static void reset_sys_reg_descs(struct kvm_vcpu *vcpu,
|
|||
int kvm_handle_sys_reg(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct sys_reg_params params;
|
||||
unsigned long esr = kvm_vcpu_get_hsr(vcpu);
|
||||
unsigned long esr = kvm_vcpu_get_esr(vcpu);
|
||||
int Rt = kvm_vcpu_sys_get_rt(vcpu);
|
||||
int ret;
|
||||
|
||||
|
@ -2491,8 +2467,7 @@ const struct sys_reg_desc *find_reg_by_id(u64 id,
|
|||
static const struct sys_reg_desc *index_to_sys_reg_desc(struct kvm_vcpu *vcpu,
|
||||
u64 id)
|
||||
{
|
||||
size_t num;
|
||||
const struct sys_reg_desc *table, *r;
|
||||
const struct sys_reg_desc *r;
|
||||
struct sys_reg_params params;
|
||||
|
||||
/* We only do sys_reg for now. */
|
||||
|
@ -2502,10 +2477,7 @@ static const struct sys_reg_desc *index_to_sys_reg_desc(struct kvm_vcpu *vcpu,
|
|||
if (!index_to_params(id, ¶ms))
|
||||
return NULL;
|
||||
|
||||
table = get_target_table(vcpu->arch.target, true, &num);
|
||||
r = find_reg(¶ms, table, num);
|
||||
if (!r)
|
||||
r = find_reg(¶ms, sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
|
||||
r = find_reg(¶ms, sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
|
||||
|
||||
/* Not saved in the sys_reg array and not otherwise accessible? */
|
||||
if (r && !(r->reg || r->get_user))
|
||||
|
@ -2805,35 +2777,17 @@ static int walk_one_sys_reg(const struct kvm_vcpu *vcpu,
|
|||
/* Assumed ordered tables, see kvm_sys_reg_table_init. */
|
||||
static int walk_sys_regs(struct kvm_vcpu *vcpu, u64 __user *uind)
|
||||
{
|
||||
const struct sys_reg_desc *i1, *i2, *end1, *end2;
|
||||
const struct sys_reg_desc *i2, *end2;
|
||||
unsigned int total = 0;
|
||||
size_t num;
|
||||
int err;
|
||||
|
||||
/* We check for duplicates here, to allow arch-specific overrides. */
|
||||
i1 = get_target_table(vcpu->arch.target, true, &num);
|
||||
end1 = i1 + num;
|
||||
i2 = sys_reg_descs;
|
||||
end2 = sys_reg_descs + ARRAY_SIZE(sys_reg_descs);
|
||||
|
||||
BUG_ON(i1 == end1 || i2 == end2);
|
||||
|
||||
/* Walk carefully, as both tables may refer to the same register. */
|
||||
while (i1 || i2) {
|
||||
int cmp = cmp_sys_reg(i1, i2);
|
||||
/* target-specific overrides generic entry. */
|
||||
if (cmp <= 0)
|
||||
err = walk_one_sys_reg(vcpu, i1, &uind, &total);
|
||||
else
|
||||
err = walk_one_sys_reg(vcpu, i2, &uind, &total);
|
||||
|
||||
while (i2 != end2) {
|
||||
err = walk_one_sys_reg(vcpu, i2++, &uind, &total);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cmp <= 0 && ++i1 == end1)
|
||||
i1 = NULL;
|
||||
if (cmp >= 0 && ++i2 == end2)
|
||||
i2 = NULL;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
@ -2900,22 +2854,3 @@ void kvm_sys_reg_table_init(void)
|
|||
/* Clear all higher bits. */
|
||||
cache_levels &= (1 << (i*3))-1;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_reset_sys_regs - sets system registers to reset value
|
||||
* @vcpu: The VCPU pointer
|
||||
*
|
||||
* This function finds the right table above and sets the registers on the
|
||||
* virtual CPU struct to their architecturally defined reset values.
|
||||
*/
|
||||
void kvm_reset_sys_regs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
size_t num;
|
||||
const struct sys_reg_desc *table;
|
||||
|
||||
/* Generic chip reset first (so target could override). */
|
||||
reset_sys_reg_descs(vcpu, sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
|
||||
|
||||
table = get_target_table(vcpu->arch.target, true, &num);
|
||||
reset_sys_reg_descs(vcpu, table, num);
|
||||
}
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2012,2013 - ARM Ltd
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*
|
||||
* Based on arch/arm/kvm/coproc_a15.c:
|
||||
* Copyright (C) 2012 - Virtual Open Systems and Columbia University
|
||||
* Authors: Rusty Russell <rusty@rustcorp.au>
|
||||
* Christoffer Dall <c.dall@virtualopensystems.com>
|
||||
*/
|
||||
#include <linux/kvm_host.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/kvm_arm.h>
|
||||
#include <asm/kvm_asm.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_coproc.h>
|
||||
#include <asm/sysreg.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include "sys_regs.h"
|
||||
|
||||
static bool access_actlr(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
if (p->is_write)
|
||||
return ignore_write(vcpu, p);
|
||||
|
||||
p->regval = vcpu_read_sys_reg(vcpu, ACTLR_EL1);
|
||||
|
||||
if (p->is_aarch32) {
|
||||
if (r->Op2 & 2)
|
||||
p->regval = upper_32_bits(p->regval);
|
||||
else
|
||||
p->regval = lower_32_bits(p->regval);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void reset_actlr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
||||
{
|
||||
__vcpu_sys_reg(vcpu, ACTLR_EL1) = read_sysreg(actlr_el1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation specific sys-reg registers.
|
||||
* Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2
|
||||
*/
|
||||
static const struct sys_reg_desc genericv8_sys_regs[] = {
|
||||
{ SYS_DESC(SYS_ACTLR_EL1), access_actlr, reset_actlr, ACTLR_EL1 },
|
||||
};
|
||||
|
||||
static const struct sys_reg_desc genericv8_cp15_regs[] = {
|
||||
/* ACTLR */
|
||||
{ Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b001),
|
||||
access_actlr },
|
||||
{ Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b011),
|
||||
access_actlr },
|
||||
};
|
||||
|
||||
static struct kvm_sys_reg_target_table genericv8_target_table = {
|
||||
.table64 = {
|
||||
.table = genericv8_sys_regs,
|
||||
.num = ARRAY_SIZE(genericv8_sys_regs),
|
||||
},
|
||||
.table32 = {
|
||||
.table = genericv8_cp15_regs,
|
||||
.num = ARRAY_SIZE(genericv8_cp15_regs),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init sys_reg_genericv8_init(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 1; i < ARRAY_SIZE(genericv8_sys_regs); i++)
|
||||
BUG_ON(cmp_sys_reg(&genericv8_sys_regs[i-1],
|
||||
&genericv8_sys_regs[i]) >= 0);
|
||||
|
||||
kvm_register_target_sys_reg_table(KVM_ARM_TARGET_AEM_V8,
|
||||
&genericv8_target_table);
|
||||
kvm_register_target_sys_reg_table(KVM_ARM_TARGET_FOUNDATION_V8,
|
||||
&genericv8_target_table);
|
||||
kvm_register_target_sys_reg_table(KVM_ARM_TARGET_CORTEX_A53,
|
||||
&genericv8_target_table);
|
||||
kvm_register_target_sys_reg_table(KVM_ARM_TARGET_CORTEX_A57,
|
||||
&genericv8_target_table);
|
||||
kvm_register_target_sys_reg_table(KVM_ARM_TARGET_XGENE_POTENZA,
|
||||
&genericv8_target_table);
|
||||
kvm_register_target_sys_reg_table(KVM_ARM_TARGET_GENERIC_V8,
|
||||
&genericv8_target_table);
|
||||
|
||||
return 0;
|
||||
}
|
||||
late_initcall(sys_reg_genericv8_init);
|
|
@ -301,8 +301,8 @@ TRACE_EVENT(kvm_timer_save_state,
|
|||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->ctl = ctx->cnt_ctl;
|
||||
__entry->cval = ctx->cnt_cval;
|
||||
__entry->ctl = timer_get_ctl(ctx);
|
||||
__entry->cval = timer_get_cval(ctx);
|
||||
__entry->timer_idx = arch_timer_ctx_index(ctx);
|
||||
),
|
||||
|
||||
|
@ -323,8 +323,8 @@ TRACE_EVENT(kvm_timer_restore_state,
|
|||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->ctl = ctx->cnt_ctl;
|
||||
__entry->cval = ctx->cnt_cval;
|
||||
__entry->ctl = timer_get_ctl(ctx);
|
||||
__entry->cval = timer_get_cval(ctx);
|
||||
__entry->timer_idx = arch_timer_ctx_index(ctx);
|
||||
),
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ __init void kvm_compute_layout(void)
|
|||
va_mask = GENMASK_ULL(tag_lsb - 1, 0);
|
||||
tag_val = hyp_va_msb;
|
||||
|
||||
if (tag_lsb != (vabits_actual - 1)) {
|
||||
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && tag_lsb != (vabits_actual - 1)) {
|
||||
/* We have some free bits to insert a random tag. */
|
||||
tag_val |= get_random_long() & GENMASK_ULL(vabits_actual - 2, tag_lsb);
|
||||
}
|
||||
|
|
|
@ -100,19 +100,33 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
|
|||
|
||||
/**
|
||||
* kvm_arch_set_irq_inatomic: fast-path for irqfd injection
|
||||
*
|
||||
* Currently only direct MSI injection is supported.
|
||||
*/
|
||||
int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
|
||||
struct kvm *kvm, int irq_source_id, int level,
|
||||
bool line_status)
|
||||
{
|
||||
if (e->type == KVM_IRQ_ROUTING_MSI && vgic_has_its(kvm) && level) {
|
||||
if (!level)
|
||||
return -EWOULDBLOCK;
|
||||
|
||||
switch (e->type) {
|
||||
case KVM_IRQ_ROUTING_MSI: {
|
||||
struct kvm_msi msi;
|
||||
|
||||
if (!vgic_has_its(kvm))
|
||||
break;
|
||||
|
||||
kvm_populate_msi(e, &msi);
|
||||
if (!vgic_its_inject_cached_translation(kvm, &msi))
|
||||
return 0;
|
||||
return vgic_its_inject_cached_translation(kvm, &msi);
|
||||
}
|
||||
|
||||
case KVM_IRQ_ROUTING_IRQCHIP:
|
||||
/*
|
||||
* Injecting SPIs is always possible in atomic context
|
||||
* as long as the damn vgic is initialized.
|
||||
*/
|
||||
if (unlikely(!vgic_initialized(kvm)))
|
||||
break;
|
||||
return vgic_irqfd_set_irq(e, kvm, irq_source_id, 1, line_status);
|
||||
}
|
||||
|
||||
return -EWOULDBLOCK;
|
||||
|
|
|
@ -757,9 +757,8 @@ int vgic_its_inject_cached_translation(struct kvm *kvm, struct kvm_msi *msi)
|
|||
|
||||
db = (u64)msi->address_hi << 32 | msi->address_lo;
|
||||
irq = vgic_its_check_cache(kvm, db, msi->devid, msi->data);
|
||||
|
||||
if (!irq)
|
||||
return -1;
|
||||
return -EWOULDBLOCK;
|
||||
|
||||
raw_spin_lock_irqsave(&irq->irq_lock, flags);
|
||||
irq->pending_latch = true;
|
||||
|
|
|
@ -389,7 +389,7 @@ u64 vgic_sanitise_outer_cacheability(u64 field)
|
|||
case GIC_BASER_CACHE_nC:
|
||||
return field;
|
||||
default:
|
||||
return GIC_BASER_CACHE_nC;
|
||||
return GIC_BASER_CACHE_SameAsInner;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1935,7 +1935,7 @@ enum emulation_result kvm_mips_emulate_load(union mips_instruction inst,
|
|||
|
||||
case lwu_op:
|
||||
vcpu->mmio_needed = 1; /* unsigned */
|
||||
/* fall through */
|
||||
fallthrough;
|
||||
#endif
|
||||
case lw_op:
|
||||
run->mmio.len = 4;
|
||||
|
|
|
@ -29,7 +29,9 @@
|
|||
#include <linux/kvm_host.h>
|
||||
|
||||
#include "interrupt.h"
|
||||
#ifdef CONFIG_CPU_LOONGSON64
|
||||
#include "loongson_regs.h"
|
||||
#endif
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
|
@ -1142,7 +1144,6 @@ static enum emulation_result kvm_vz_gpsi_cache(union mips_instruction inst,
|
|||
#ifdef CONFIG_CPU_LOONGSON64
|
||||
static enum emulation_result kvm_vz_gpsi_lwc2(union mips_instruction inst,
|
||||
u32 *opc, u32 cause,
|
||||
struct kvm_run *run,
|
||||
struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned int rs, rd;
|
||||
|
@ -1240,7 +1241,7 @@ static enum emulation_result kvm_trap_vz_handle_gpsi(u32 cause, u32 *opc,
|
|||
#endif
|
||||
#ifdef CONFIG_CPU_LOONGSON64
|
||||
case lwc2_op:
|
||||
er = kvm_vz_gpsi_lwc2(inst, opc, cause, run, vcpu);
|
||||
er = kvm_vz_gpsi_lwc2(inst, opc, cause, vcpu);
|
||||
break;
|
||||
#endif
|
||||
case spec3_op:
|
||||
|
|
|
@ -23,6 +23,10 @@ int kvmppc_send_page_to_uv(struct kvm *kvm, unsigned long gfn);
|
|||
unsigned long kvmppc_h_svm_init_abort(struct kvm *kvm);
|
||||
void kvmppc_uvmem_drop_pages(const struct kvm_memory_slot *free,
|
||||
struct kvm *kvm, bool skip_page_out);
|
||||
int kvmppc_uvmem_memslot_create(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *new);
|
||||
void kvmppc_uvmem_memslot_delete(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *old);
|
||||
#else
|
||||
static inline int kvmppc_uvmem_init(void)
|
||||
{
|
||||
|
@ -82,5 +86,15 @@ static inline int kvmppc_send_page_to_uv(struct kvm *kvm, unsigned long gfn)
|
|||
static inline void
|
||||
kvmppc_uvmem_drop_pages(const struct kvm_memory_slot *free,
|
||||
struct kvm *kvm, bool skip_page_out) { }
|
||||
|
||||
static inline int kvmppc_uvmem_memslot_create(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *new)
|
||||
{
|
||||
return H_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static inline void kvmppc_uvmem_memslot_delete(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *old) { }
|
||||
|
||||
#endif /* CONFIG_PPC_UV */
|
||||
#endif /* __ASM_KVM_BOOK3S_UVMEM_H__ */
|
||||
|
|
|
@ -59,7 +59,7 @@ enum xlate_readwrite {
|
|||
};
|
||||
|
||||
extern int kvmppc_vcpu_run(struct kvm_vcpu *vcpu);
|
||||
extern int __kvmppc_vcpu_run(struct kvm_run *run, struct kvm_vcpu *vcpu);
|
||||
extern int __kvmppc_vcpu_run(struct kvm_vcpu *vcpu);
|
||||
extern void kvmppc_handler_highmem(void);
|
||||
|
||||
extern void kvmppc_dump_vcpu(struct kvm_vcpu *vcpu);
|
||||
|
|
|
@ -474,7 +474,8 @@
|
|||
#ifndef SPRN_LPID
|
||||
#define SPRN_LPID 0x13F /* Logical Partition Identifier */
|
||||
#endif
|
||||
#define LPID_RSVD 0x3ff /* Reserved LPID for partn switching */
|
||||
#define LPID_RSVD_POWER7 0x3ff /* Reserved LPID for partn switching */
|
||||
#define LPID_RSVD 0xfff /* Reserved LPID for partn switching */
|
||||
#define SPRN_HMER 0x150 /* Hypervisor maintenance exception reg */
|
||||
#define HMER_DEBUG_TRIG (1ul << (63 - 17)) /* Debug trigger */
|
||||
#define SPRN_HMEER 0x151 /* Hyp maintenance exception enable reg */
|
||||
|
@ -1362,6 +1363,7 @@
|
|||
#define PVR_ARCH_206p 0x0f100003
|
||||
#define PVR_ARCH_207 0x0f000004
|
||||
#define PVR_ARCH_300 0x0f000005
|
||||
#define PVR_ARCH_31 0x0f000006
|
||||
|
||||
/* Macros for setting and retrieving special purpose registers */
|
||||
#ifndef __ASSEMBLY__
|
||||
|
|
|
@ -260,11 +260,15 @@ int kvmppc_mmu_hv_init(void)
|
|||
if (!mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE))
|
||||
return -EINVAL;
|
||||
|
||||
/* POWER7 has 10-bit LPIDs (12-bit in POWER8) */
|
||||
host_lpid = 0;
|
||||
if (cpu_has_feature(CPU_FTR_HVMODE))
|
||||
host_lpid = mfspr(SPRN_LPID);
|
||||
rsvd_lpid = LPID_RSVD;
|
||||
|
||||
/* POWER8 and above have 12-bit LPIDs (10-bit in POWER7) */
|
||||
if (cpu_has_feature(CPU_FTR_ARCH_207S))
|
||||
rsvd_lpid = LPID_RSVD;
|
||||
else
|
||||
rsvd_lpid = LPID_RSVD_POWER7;
|
||||
|
||||
kvmppc_init_lpid(rsvd_lpid + 1);
|
||||
|
||||
|
|
|
@ -161,7 +161,9 @@ int kvmppc_mmu_walk_radix_tree(struct kvm_vcpu *vcpu, gva_t eaddr,
|
|||
return -EINVAL;
|
||||
/* Read the entry from guest memory */
|
||||
addr = base + (index * sizeof(rpte));
|
||||
vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);
|
||||
ret = kvm_read_guest(kvm, addr, &rpte, sizeof(rpte));
|
||||
srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
|
||||
if (ret) {
|
||||
if (pte_ret_p)
|
||||
*pte_ret_p = addr;
|
||||
|
@ -237,7 +239,9 @@ int kvmppc_mmu_radix_translate_table(struct kvm_vcpu *vcpu, gva_t eaddr,
|
|||
|
||||
/* Read the table to find the root of the radix tree */
|
||||
ptbl = (table & PRTB_MASK) + (table_index * sizeof(entry));
|
||||
vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);
|
||||
ret = kvm_read_guest(kvm, ptbl, &entry, sizeof(entry));
|
||||
srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -343,13 +343,18 @@ static void kvmppc_set_pvr_hv(struct kvm_vcpu *vcpu, u32 pvr)
|
|||
vcpu->arch.pvr = pvr;
|
||||
}
|
||||
|
||||
/* Dummy value used in computing PCR value below */
|
||||
#define PCR_ARCH_31 (PCR_ARCH_300 << 1)
|
||||
|
||||
static int kvmppc_set_arch_compat(struct kvm_vcpu *vcpu, u32 arch_compat)
|
||||
{
|
||||
unsigned long host_pcr_bit = 0, guest_pcr_bit = 0;
|
||||
struct kvmppc_vcore *vc = vcpu->arch.vcore;
|
||||
|
||||
/* We can (emulate) our own architecture version and anything older */
|
||||
if (cpu_has_feature(CPU_FTR_ARCH_300))
|
||||
if (cpu_has_feature(CPU_FTR_ARCH_31))
|
||||
host_pcr_bit = PCR_ARCH_31;
|
||||
else if (cpu_has_feature(CPU_FTR_ARCH_300))
|
||||
host_pcr_bit = PCR_ARCH_300;
|
||||
else if (cpu_has_feature(CPU_FTR_ARCH_207S))
|
||||
host_pcr_bit = PCR_ARCH_207;
|
||||
|
@ -375,6 +380,9 @@ static int kvmppc_set_arch_compat(struct kvm_vcpu *vcpu, u32 arch_compat)
|
|||
case PVR_ARCH_300:
|
||||
guest_pcr_bit = PCR_ARCH_300;
|
||||
break;
|
||||
case PVR_ARCH_31:
|
||||
guest_pcr_bit = PCR_ARCH_31;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -2355,7 +2363,7 @@ static int kvmppc_core_vcpu_create_hv(struct kvm_vcpu *vcpu)
|
|||
* to trap and then we emulate them.
|
||||
*/
|
||||
vcpu->arch.hfscr = HFSCR_TAR | HFSCR_EBB | HFSCR_PM | HFSCR_BHRB |
|
||||
HFSCR_DSCR | HFSCR_VECVSX | HFSCR_FP;
|
||||
HFSCR_DSCR | HFSCR_VECVSX | HFSCR_FP | HFSCR_PREFIX;
|
||||
if (cpu_has_feature(CPU_FTR_HVMODE)) {
|
||||
vcpu->arch.hfscr &= mfspr(SPRN_HFSCR);
|
||||
if (cpu_has_feature(CPU_FTR_P9_TM_HV_ASSIST))
|
||||
|
@ -4552,16 +4560,14 @@ static void kvmppc_core_commit_memory_region_hv(struct kvm *kvm,
|
|||
|
||||
switch (change) {
|
||||
case KVM_MR_CREATE:
|
||||
if (kvmppc_uvmem_slot_init(kvm, new))
|
||||
return;
|
||||
uv_register_mem_slot(kvm->arch.lpid,
|
||||
new->base_gfn << PAGE_SHIFT,
|
||||
new->npages * PAGE_SIZE,
|
||||
0, new->id);
|
||||
/*
|
||||
* @TODO kvmppc_uvmem_memslot_create() can fail and
|
||||
* return error. Fix this.
|
||||
*/
|
||||
kvmppc_uvmem_memslot_create(kvm, new);
|
||||
break;
|
||||
case KVM_MR_DELETE:
|
||||
uv_unregister_mem_slot(kvm->arch.lpid, old->id);
|
||||
kvmppc_uvmem_slot_free(kvm, old);
|
||||
kvmppc_uvmem_memslot_delete(kvm, old);
|
||||
break;
|
||||
default:
|
||||
/* TODO: Handle KVM_MR_MOVE */
|
||||
|
|
|
@ -233,20 +233,21 @@ long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
|
|||
|
||||
/* copy parameters in */
|
||||
hv_ptr = kvmppc_get_gpr(vcpu, 4);
|
||||
regs_ptr = kvmppc_get_gpr(vcpu, 5);
|
||||
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
|
||||
err = kvm_vcpu_read_guest(vcpu, hv_ptr, &l2_hv,
|
||||
sizeof(struct hv_guest_state));
|
||||
sizeof(struct hv_guest_state)) ||
|
||||
kvm_vcpu_read_guest(vcpu, regs_ptr, &l2_regs,
|
||||
sizeof(struct pt_regs));
|
||||
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
|
||||
if (err)
|
||||
return H_PARAMETER;
|
||||
|
||||
if (kvmppc_need_byteswap(vcpu))
|
||||
byteswap_hv_regs(&l2_hv);
|
||||
if (l2_hv.version != HV_GUEST_STATE_VERSION)
|
||||
return H_P2;
|
||||
|
||||
regs_ptr = kvmppc_get_gpr(vcpu, 5);
|
||||
err = kvm_vcpu_read_guest(vcpu, regs_ptr, &l2_regs,
|
||||
sizeof(struct pt_regs));
|
||||
if (err)
|
||||
return H_PARAMETER;
|
||||
if (kvmppc_need_byteswap(vcpu))
|
||||
byteswap_pt_regs(&l2_regs);
|
||||
if (l2_hv.vcpu_token >= NR_CPUS)
|
||||
|
@ -323,12 +324,12 @@ long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
|
|||
byteswap_hv_regs(&l2_hv);
|
||||
byteswap_pt_regs(&l2_regs);
|
||||
}
|
||||
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
|
||||
err = kvm_vcpu_write_guest(vcpu, hv_ptr, &l2_hv,
|
||||
sizeof(struct hv_guest_state));
|
||||
if (err)
|
||||
return H_AUTHORITY;
|
||||
err = kvm_vcpu_write_guest(vcpu, regs_ptr, &l2_regs,
|
||||
sizeof(struct hv_guest_state)) ||
|
||||
kvm_vcpu_write_guest(vcpu, regs_ptr, &l2_regs,
|
||||
sizeof(struct pt_regs));
|
||||
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
|
||||
if (err)
|
||||
return H_AUTHORITY;
|
||||
|
||||
|
@ -508,12 +509,16 @@ long kvmhv_copy_tofrom_guest_nested(struct kvm_vcpu *vcpu)
|
|||
goto not_found;
|
||||
|
||||
/* Write what was loaded into our buffer back to the L1 guest */
|
||||
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
|
||||
rc = kvm_vcpu_write_guest(vcpu, gp_to, buf, n);
|
||||
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
|
||||
if (rc)
|
||||
goto not_found;
|
||||
} else {
|
||||
/* Load the data to be stored from the L1 guest into our buf */
|
||||
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
|
||||
rc = kvm_vcpu_read_guest(vcpu, gp_from, buf, n);
|
||||
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
|
||||
if (rc)
|
||||
goto not_found;
|
||||
|
||||
|
@ -548,9 +553,12 @@ static void kvmhv_update_ptbl_cache(struct kvm_nested_guest *gp)
|
|||
|
||||
ret = -EFAULT;
|
||||
ptbl_addr = (kvm->arch.l1_ptcr & PRTB_MASK) + (gp->l1_lpid << 4);
|
||||
if (gp->l1_lpid < (1ul << ((kvm->arch.l1_ptcr & PRTS_MASK) + 8)))
|
||||
if (gp->l1_lpid < (1ul << ((kvm->arch.l1_ptcr & PRTS_MASK) + 8))) {
|
||||
int srcu_idx = srcu_read_lock(&kvm->srcu);
|
||||
ret = kvm_read_guest(kvm, ptbl_addr,
|
||||
&ptbl_entry, sizeof(ptbl_entry));
|
||||
srcu_read_unlock(&kvm->srcu, srcu_idx);
|
||||
}
|
||||
if (ret) {
|
||||
gp->l1_gr_to_hr = 0;
|
||||
gp->process_table = 0;
|
||||
|
|
|
@ -93,12 +93,133 @@
|
|||
#include <asm/ultravisor.h>
|
||||
#include <asm/mman.h>
|
||||
#include <asm/kvm_ppc.h>
|
||||
#include <asm/kvm_book3s_uvmem.h>
|
||||
|
||||
static struct dev_pagemap kvmppc_uvmem_pgmap;
|
||||
static unsigned long *kvmppc_uvmem_bitmap;
|
||||
static DEFINE_SPINLOCK(kvmppc_uvmem_bitmap_lock);
|
||||
|
||||
#define KVMPPC_UVMEM_PFN (1UL << 63)
|
||||
/*
|
||||
* States of a GFN
|
||||
* ---------------
|
||||
* The GFN can be in one of the following states.
|
||||
*
|
||||
* (a) Secure - The GFN is secure. The GFN is associated with
|
||||
* a Secure VM, the contents of the GFN is not accessible
|
||||
* to the Hypervisor. This GFN can be backed by a secure-PFN,
|
||||
* or can be backed by a normal-PFN with contents encrypted.
|
||||
* The former is true when the GFN is paged-in into the
|
||||
* ultravisor. The latter is true when the GFN is paged-out
|
||||
* of the ultravisor.
|
||||
*
|
||||
* (b) Shared - The GFN is shared. The GFN is associated with a
|
||||
* a secure VM. The contents of the GFN is accessible to
|
||||
* Hypervisor. This GFN is backed by a normal-PFN and its
|
||||
* content is un-encrypted.
|
||||
*
|
||||
* (c) Normal - The GFN is a normal. The GFN is associated with
|
||||
* a normal VM. The contents of the GFN is accesible to
|
||||
* the Hypervisor. Its content is never encrypted.
|
||||
*
|
||||
* States of a VM.
|
||||
* ---------------
|
||||
*
|
||||
* Normal VM: A VM whose contents are always accessible to
|
||||
* the hypervisor. All its GFNs are normal-GFNs.
|
||||
*
|
||||
* Secure VM: A VM whose contents are not accessible to the
|
||||
* hypervisor without the VM's consent. Its GFNs are
|
||||
* either Shared-GFN or Secure-GFNs.
|
||||
*
|
||||
* Transient VM: A Normal VM that is transitioning to secure VM.
|
||||
* The transition starts on successful return of
|
||||
* H_SVM_INIT_START, and ends on successful return
|
||||
* of H_SVM_INIT_DONE. This transient VM, can have GFNs
|
||||
* in any of the three states; i.e Secure-GFN, Shared-GFN,
|
||||
* and Normal-GFN. The VM never executes in this state
|
||||
* in supervisor-mode.
|
||||
*
|
||||
* Memory slot State.
|
||||
* -----------------------------
|
||||
* The state of a memory slot mirrors the state of the
|
||||
* VM the memory slot is associated with.
|
||||
*
|
||||
* VM State transition.
|
||||
* --------------------
|
||||
*
|
||||
* A VM always starts in Normal Mode.
|
||||
*
|
||||
* H_SVM_INIT_START moves the VM into transient state. During this
|
||||
* time the Ultravisor may request some of its GFNs to be shared or
|
||||
* secured. So its GFNs can be in one of the three GFN states.
|
||||
*
|
||||
* H_SVM_INIT_DONE moves the VM entirely from transient state to
|
||||
* secure-state. At this point any left-over normal-GFNs are
|
||||
* transitioned to Secure-GFN.
|
||||
*
|
||||
* H_SVM_INIT_ABORT moves the transient VM back to normal VM.
|
||||
* All its GFNs are moved to Normal-GFNs.
|
||||
*
|
||||
* UV_TERMINATE transitions the secure-VM back to normal-VM. All
|
||||
* the secure-GFN and shared-GFNs are tranistioned to normal-GFN
|
||||
* Note: The contents of the normal-GFN is undefined at this point.
|
||||
*
|
||||
* GFN state implementation:
|
||||
* -------------------------
|
||||
*
|
||||
* Secure GFN is associated with a secure-PFN; also called uvmem_pfn,
|
||||
* when the GFN is paged-in. Its pfn[] has KVMPPC_GFN_UVMEM_PFN flag
|
||||
* set, and contains the value of the secure-PFN.
|
||||
* It is associated with a normal-PFN; also called mem_pfn, when
|
||||
* the GFN is pagedout. Its pfn[] has KVMPPC_GFN_MEM_PFN flag set.
|
||||
* The value of the normal-PFN is not tracked.
|
||||
*
|
||||
* Shared GFN is associated with a normal-PFN. Its pfn[] has
|
||||
* KVMPPC_UVMEM_SHARED_PFN flag set. The value of the normal-PFN
|
||||
* is not tracked.
|
||||
*
|
||||
* Normal GFN is associated with normal-PFN. Its pfn[] has
|
||||
* no flag set. The value of the normal-PFN is not tracked.
|
||||
*
|
||||
* Life cycle of a GFN
|
||||
* --------------------
|
||||
*
|
||||
* --------------------------------------------------------------
|
||||
* | | Share | Unshare | SVM |H_SVM_INIT_DONE|
|
||||
* | |operation |operation | abort/ | |
|
||||
* | | | | terminate | |
|
||||
* -------------------------------------------------------------
|
||||
* | | | | | |
|
||||
* | Secure | Shared | Secure |Normal |Secure |
|
||||
* | | | | | |
|
||||
* | Shared | Shared | Secure |Normal |Shared |
|
||||
* | | | | | |
|
||||
* | Normal | Shared | Secure |Normal |Secure |
|
||||
* --------------------------------------------------------------
|
||||
*
|
||||
* Life cycle of a VM
|
||||
* --------------------
|
||||
*
|
||||
* --------------------------------------------------------------------
|
||||
* | | start | H_SVM_ |H_SVM_ |H_SVM_ |UV_SVM_ |
|
||||
* | | VM |INIT_START|INIT_DONE|INIT_ABORT |TERMINATE |
|
||||
* | | | | | | |
|
||||
* --------- ----------------------------------------------------------
|
||||
* | | | | | | |
|
||||
* | Normal | Normal | Transient|Error |Error |Normal |
|
||||
* | | | | | | |
|
||||
* | Secure | Error | Error |Error |Error |Normal |
|
||||
* | | | | | | |
|
||||
* |Transient| N/A | Error |Secure |Normal |Normal |
|
||||
* --------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define KVMPPC_GFN_UVMEM_PFN (1UL << 63)
|
||||
#define KVMPPC_GFN_MEM_PFN (1UL << 62)
|
||||
#define KVMPPC_GFN_SHARED (1UL << 61)
|
||||
#define KVMPPC_GFN_SECURE (KVMPPC_GFN_UVMEM_PFN | KVMPPC_GFN_MEM_PFN)
|
||||
#define KVMPPC_GFN_FLAG_MASK (KVMPPC_GFN_SECURE | KVMPPC_GFN_SHARED)
|
||||
#define KVMPPC_GFN_PFN_MASK (~KVMPPC_GFN_FLAG_MASK)
|
||||
|
||||
struct kvmppc_uvmem_slot {
|
||||
struct list_head list;
|
||||
|
@ -106,11 +227,11 @@ struct kvmppc_uvmem_slot {
|
|||
unsigned long base_pfn;
|
||||
unsigned long *pfns;
|
||||
};
|
||||
|
||||
struct kvmppc_uvmem_page_pvt {
|
||||
struct kvm *kvm;
|
||||
unsigned long gpa;
|
||||
bool skip_page_out;
|
||||
bool remove_gfn;
|
||||
};
|
||||
|
||||
bool kvmppc_uvmem_available(void)
|
||||
|
@ -163,8 +284,8 @@ void kvmppc_uvmem_slot_free(struct kvm *kvm, const struct kvm_memory_slot *slot)
|
|||
mutex_unlock(&kvm->arch.uvmem_lock);
|
||||
}
|
||||
|
||||
static void kvmppc_uvmem_pfn_insert(unsigned long gfn, unsigned long uvmem_pfn,
|
||||
struct kvm *kvm)
|
||||
static void kvmppc_mark_gfn(unsigned long gfn, struct kvm *kvm,
|
||||
unsigned long flag, unsigned long uvmem_pfn)
|
||||
{
|
||||
struct kvmppc_uvmem_slot *p;
|
||||
|
||||
|
@ -172,24 +293,41 @@ static void kvmppc_uvmem_pfn_insert(unsigned long gfn, unsigned long uvmem_pfn,
|
|||
if (gfn >= p->base_pfn && gfn < p->base_pfn + p->nr_pfns) {
|
||||
unsigned long index = gfn - p->base_pfn;
|
||||
|
||||
p->pfns[index] = uvmem_pfn | KVMPPC_UVMEM_PFN;
|
||||
if (flag == KVMPPC_GFN_UVMEM_PFN)
|
||||
p->pfns[index] = uvmem_pfn | flag;
|
||||
else
|
||||
p->pfns[index] = flag;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void kvmppc_uvmem_pfn_remove(unsigned long gfn, struct kvm *kvm)
|
||||
/* mark the GFN as secure-GFN associated with @uvmem pfn device-PFN. */
|
||||
static void kvmppc_gfn_secure_uvmem_pfn(unsigned long gfn,
|
||||
unsigned long uvmem_pfn, struct kvm *kvm)
|
||||
{
|
||||
struct kvmppc_uvmem_slot *p;
|
||||
|
||||
list_for_each_entry(p, &kvm->arch.uvmem_pfns, list) {
|
||||
if (gfn >= p->base_pfn && gfn < p->base_pfn + p->nr_pfns) {
|
||||
p->pfns[gfn - p->base_pfn] = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
kvmppc_mark_gfn(gfn, kvm, KVMPPC_GFN_UVMEM_PFN, uvmem_pfn);
|
||||
}
|
||||
|
||||
/* mark the GFN as secure-GFN associated with a memory-PFN. */
|
||||
static void kvmppc_gfn_secure_mem_pfn(unsigned long gfn, struct kvm *kvm)
|
||||
{
|
||||
kvmppc_mark_gfn(gfn, kvm, KVMPPC_GFN_MEM_PFN, 0);
|
||||
}
|
||||
|
||||
/* mark the GFN as a shared GFN. */
|
||||
static void kvmppc_gfn_shared(unsigned long gfn, struct kvm *kvm)
|
||||
{
|
||||
kvmppc_mark_gfn(gfn, kvm, KVMPPC_GFN_SHARED, 0);
|
||||
}
|
||||
|
||||
/* mark the GFN as a non-existent GFN. */
|
||||
static void kvmppc_gfn_remove(unsigned long gfn, struct kvm *kvm)
|
||||
{
|
||||
kvmppc_mark_gfn(gfn, kvm, 0, 0);
|
||||
}
|
||||
|
||||
/* return true, if the GFN is a secure-GFN backed by a secure-PFN */
|
||||
static bool kvmppc_gfn_is_uvmem_pfn(unsigned long gfn, struct kvm *kvm,
|
||||
unsigned long *uvmem_pfn)
|
||||
{
|
||||
|
@ -199,10 +337,10 @@ static bool kvmppc_gfn_is_uvmem_pfn(unsigned long gfn, struct kvm *kvm,
|
|||
if (gfn >= p->base_pfn && gfn < p->base_pfn + p->nr_pfns) {
|
||||
unsigned long index = gfn - p->base_pfn;
|
||||
|
||||
if (p->pfns[index] & KVMPPC_UVMEM_PFN) {
|
||||
if (p->pfns[index] & KVMPPC_GFN_UVMEM_PFN) {
|
||||
if (uvmem_pfn)
|
||||
*uvmem_pfn = p->pfns[index] &
|
||||
~KVMPPC_UVMEM_PFN;
|
||||
KVMPPC_GFN_PFN_MASK;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
|
@ -211,10 +349,114 @@ static bool kvmppc_gfn_is_uvmem_pfn(unsigned long gfn, struct kvm *kvm,
|
|||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* starting from *gfn search for the next available GFN that is not yet
|
||||
* transitioned to a secure GFN. return the value of that GFN in *gfn. If a
|
||||
* GFN is found, return true, else return false
|
||||
*
|
||||
* Must be called with kvm->arch.uvmem_lock held.
|
||||
*/
|
||||
static bool kvmppc_next_nontransitioned_gfn(const struct kvm_memory_slot *memslot,
|
||||
struct kvm *kvm, unsigned long *gfn)
|
||||
{
|
||||
struct kvmppc_uvmem_slot *p;
|
||||
bool ret = false;
|
||||
unsigned long i;
|
||||
|
||||
list_for_each_entry(p, &kvm->arch.uvmem_pfns, list)
|
||||
if (*gfn >= p->base_pfn && *gfn < p->base_pfn + p->nr_pfns)
|
||||
break;
|
||||
if (!p)
|
||||
return ret;
|
||||
/*
|
||||
* The code below assumes, one to one correspondence between
|
||||
* kvmppc_uvmem_slot and memslot.
|
||||
*/
|
||||
for (i = *gfn; i < p->base_pfn + p->nr_pfns; i++) {
|
||||
unsigned long index = i - p->base_pfn;
|
||||
|
||||
if (!(p->pfns[index] & KVMPPC_GFN_FLAG_MASK)) {
|
||||
*gfn = i;
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kvmppc_memslot_page_merge(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *memslot, bool merge)
|
||||
{
|
||||
unsigned long gfn = memslot->base_gfn;
|
||||
unsigned long end, start = gfn_to_hva(kvm, gfn);
|
||||
int ret = 0;
|
||||
struct vm_area_struct *vma;
|
||||
int merge_flag = (merge) ? MADV_MERGEABLE : MADV_UNMERGEABLE;
|
||||
|
||||
if (kvm_is_error_hva(start))
|
||||
return H_STATE;
|
||||
|
||||
end = start + (memslot->npages << PAGE_SHIFT);
|
||||
|
||||
mmap_write_lock(kvm->mm);
|
||||
do {
|
||||
vma = find_vma_intersection(kvm->mm, start, end);
|
||||
if (!vma) {
|
||||
ret = H_STATE;
|
||||
break;
|
||||
}
|
||||
ret = ksm_madvise(vma, vma->vm_start, vma->vm_end,
|
||||
merge_flag, &vma->vm_flags);
|
||||
if (ret) {
|
||||
ret = H_STATE;
|
||||
break;
|
||||
}
|
||||
start = vma->vm_end;
|
||||
} while (end > vma->vm_end);
|
||||
|
||||
mmap_write_unlock(kvm->mm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __kvmppc_uvmem_memslot_delete(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *memslot)
|
||||
{
|
||||
uv_unregister_mem_slot(kvm->arch.lpid, memslot->id);
|
||||
kvmppc_uvmem_slot_free(kvm, memslot);
|
||||
kvmppc_memslot_page_merge(kvm, memslot, true);
|
||||
}
|
||||
|
||||
static int __kvmppc_uvmem_memslot_create(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *memslot)
|
||||
{
|
||||
int ret = H_PARAMETER;
|
||||
|
||||
if (kvmppc_memslot_page_merge(kvm, memslot, false))
|
||||
return ret;
|
||||
|
||||
if (kvmppc_uvmem_slot_init(kvm, memslot))
|
||||
goto out1;
|
||||
|
||||
ret = uv_register_mem_slot(kvm->arch.lpid,
|
||||
memslot->base_gfn << PAGE_SHIFT,
|
||||
memslot->npages * PAGE_SIZE,
|
||||
0, memslot->id);
|
||||
if (ret < 0) {
|
||||
ret = H_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
return 0;
|
||||
out:
|
||||
kvmppc_uvmem_slot_free(kvm, memslot);
|
||||
out1:
|
||||
kvmppc_memslot_page_merge(kvm, memslot, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned long kvmppc_h_svm_init_start(struct kvm *kvm)
|
||||
{
|
||||
struct kvm_memslots *slots;
|
||||
struct kvm_memory_slot *memslot;
|
||||
struct kvm_memory_slot *memslot, *m;
|
||||
int ret = H_SUCCESS;
|
||||
int srcu_idx;
|
||||
|
||||
|
@ -232,35 +474,117 @@ unsigned long kvmppc_h_svm_init_start(struct kvm *kvm)
|
|||
return H_AUTHORITY;
|
||||
|
||||
srcu_idx = srcu_read_lock(&kvm->srcu);
|
||||
|
||||
/* register the memslot */
|
||||
slots = kvm_memslots(kvm);
|
||||
kvm_for_each_memslot(memslot, slots) {
|
||||
if (kvmppc_uvmem_slot_init(kvm, memslot)) {
|
||||
ret = H_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
ret = uv_register_mem_slot(kvm->arch.lpid,
|
||||
memslot->base_gfn << PAGE_SHIFT,
|
||||
memslot->npages * PAGE_SIZE,
|
||||
0, memslot->id);
|
||||
if (ret < 0) {
|
||||
kvmppc_uvmem_slot_free(kvm, memslot);
|
||||
ret = H_PARAMETER;
|
||||
goto out;
|
||||
ret = __kvmppc_uvmem_memslot_create(kvm, memslot);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
slots = kvm_memslots(kvm);
|
||||
kvm_for_each_memslot(m, slots) {
|
||||
if (m == memslot)
|
||||
break;
|
||||
__kvmppc_uvmem_memslot_delete(kvm, memslot);
|
||||
}
|
||||
}
|
||||
out:
|
||||
|
||||
srcu_read_unlock(&kvm->srcu, srcu_idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned long kvmppc_h_svm_init_done(struct kvm *kvm)
|
||||
/*
|
||||
* Provision a new page on HV side and copy over the contents
|
||||
* from secure memory using UV_PAGE_OUT uvcall.
|
||||
* Caller must held kvm->arch.uvmem_lock.
|
||||
*/
|
||||
static int __kvmppc_svm_page_out(struct vm_area_struct *vma,
|
||||
unsigned long start,
|
||||
unsigned long end, unsigned long page_shift,
|
||||
struct kvm *kvm, unsigned long gpa)
|
||||
{
|
||||
if (!(kvm->arch.secure_guest & KVMPPC_SECURE_INIT_START))
|
||||
return H_UNSUPPORTED;
|
||||
unsigned long src_pfn, dst_pfn = 0;
|
||||
struct migrate_vma mig;
|
||||
struct page *dpage, *spage;
|
||||
struct kvmppc_uvmem_page_pvt *pvt;
|
||||
unsigned long pfn;
|
||||
int ret = U_SUCCESS;
|
||||
|
||||
kvm->arch.secure_guest |= KVMPPC_SECURE_INIT_DONE;
|
||||
pr_info("LPID %d went secure\n", kvm->arch.lpid);
|
||||
return H_SUCCESS;
|
||||
memset(&mig, 0, sizeof(mig));
|
||||
mig.vma = vma;
|
||||
mig.start = start;
|
||||
mig.end = end;
|
||||
mig.src = &src_pfn;
|
||||
mig.dst = &dst_pfn;
|
||||
mig.pgmap_owner = &kvmppc_uvmem_pgmap;
|
||||
mig.flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE;
|
||||
|
||||
/* The requested page is already paged-out, nothing to do */
|
||||
if (!kvmppc_gfn_is_uvmem_pfn(gpa >> page_shift, kvm, NULL))
|
||||
return ret;
|
||||
|
||||
ret = migrate_vma_setup(&mig);
|
||||
if (ret)
|
||||
return -1;
|
||||
|
||||
spage = migrate_pfn_to_page(*mig.src);
|
||||
if (!spage || !(*mig.src & MIGRATE_PFN_MIGRATE))
|
||||
goto out_finalize;
|
||||
|
||||
if (!is_zone_device_page(spage))
|
||||
goto out_finalize;
|
||||
|
||||
dpage = alloc_page_vma(GFP_HIGHUSER, vma, start);
|
||||
if (!dpage) {
|
||||
ret = -1;
|
||||
goto out_finalize;
|
||||
}
|
||||
|
||||
lock_page(dpage);
|
||||
pvt = spage->zone_device_data;
|
||||
pfn = page_to_pfn(dpage);
|
||||
|
||||
/*
|
||||
* This function is used in two cases:
|
||||
* - When HV touches a secure page, for which we do UV_PAGE_OUT
|
||||
* - When a secure page is converted to shared page, we *get*
|
||||
* the page to essentially unmap the device page. In this
|
||||
* case we skip page-out.
|
||||
*/
|
||||
if (!pvt->skip_page_out)
|
||||
ret = uv_page_out(kvm->arch.lpid, pfn << page_shift,
|
||||
gpa, 0, page_shift);
|
||||
|
||||
if (ret == U_SUCCESS)
|
||||
*mig.dst = migrate_pfn(pfn) | MIGRATE_PFN_LOCKED;
|
||||
else {
|
||||
unlock_page(dpage);
|
||||
__free_page(dpage);
|
||||
goto out_finalize;
|
||||
}
|
||||
|
||||
migrate_vma_pages(&mig);
|
||||
|
||||
out_finalize:
|
||||
migrate_vma_finalize(&mig);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int kvmppc_svm_page_out(struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end,
|
||||
unsigned long page_shift,
|
||||
struct kvm *kvm, unsigned long gpa)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&kvm->arch.uvmem_lock);
|
||||
ret = __kvmppc_svm_page_out(vma, start, end, page_shift, kvm, gpa);
|
||||
mutex_unlock(&kvm->arch.uvmem_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -271,33 +595,53 @@ unsigned long kvmppc_h_svm_init_done(struct kvm *kvm)
|
|||
* fault on them, do fault time migration to replace the device PTEs in
|
||||
* QEMU page table with normal PTEs from newly allocated pages.
|
||||
*/
|
||||
void kvmppc_uvmem_drop_pages(const struct kvm_memory_slot *free,
|
||||
void kvmppc_uvmem_drop_pages(const struct kvm_memory_slot *slot,
|
||||
struct kvm *kvm, bool skip_page_out)
|
||||
{
|
||||
int i;
|
||||
struct kvmppc_uvmem_page_pvt *pvt;
|
||||
unsigned long pfn, uvmem_pfn;
|
||||
unsigned long gfn = free->base_gfn;
|
||||
struct page *uvmem_page;
|
||||
struct vm_area_struct *vma = NULL;
|
||||
unsigned long uvmem_pfn, gfn;
|
||||
unsigned long addr;
|
||||
|
||||
for (i = free->npages; i; --i, ++gfn) {
|
||||
struct page *uvmem_page;
|
||||
mmap_read_lock(kvm->mm);
|
||||
|
||||
mutex_lock(&kvm->arch.uvmem_lock);
|
||||
if (!kvmppc_gfn_is_uvmem_pfn(gfn, kvm, &uvmem_pfn)) {
|
||||
mutex_unlock(&kvm->arch.uvmem_lock);
|
||||
continue;
|
||||
addr = slot->userspace_addr;
|
||||
|
||||
gfn = slot->base_gfn;
|
||||
for (i = slot->npages; i; --i, ++gfn, addr += PAGE_SIZE) {
|
||||
|
||||
/* Fetch the VMA if addr is not in the latest fetched one */
|
||||
if (!vma || addr >= vma->vm_end) {
|
||||
vma = find_vma_intersection(kvm->mm, addr, addr+1);
|
||||
if (!vma) {
|
||||
pr_err("Can't find VMA for gfn:0x%lx\n", gfn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uvmem_page = pfn_to_page(uvmem_pfn);
|
||||
pvt = uvmem_page->zone_device_data;
|
||||
pvt->skip_page_out = skip_page_out;
|
||||
mutex_unlock(&kvm->arch.uvmem_lock);
|
||||
mutex_lock(&kvm->arch.uvmem_lock);
|
||||
|
||||
pfn = gfn_to_pfn(kvm, gfn);
|
||||
if (is_error_noslot_pfn(pfn))
|
||||
continue;
|
||||
kvm_release_pfn_clean(pfn);
|
||||
if (kvmppc_gfn_is_uvmem_pfn(gfn, kvm, &uvmem_pfn)) {
|
||||
uvmem_page = pfn_to_page(uvmem_pfn);
|
||||
pvt = uvmem_page->zone_device_data;
|
||||
pvt->skip_page_out = skip_page_out;
|
||||
pvt->remove_gfn = true;
|
||||
|
||||
if (__kvmppc_svm_page_out(vma, addr, addr + PAGE_SIZE,
|
||||
PAGE_SHIFT, kvm, pvt->gpa))
|
||||
pr_err("Can't page out gpa:0x%lx addr:0x%lx\n",
|
||||
pvt->gpa, addr);
|
||||
} else {
|
||||
/* Remove the shared flag if any */
|
||||
kvmppc_gfn_remove(gfn, kvm);
|
||||
}
|
||||
|
||||
mutex_unlock(&kvm->arch.uvmem_lock);
|
||||
}
|
||||
|
||||
mmap_read_unlock(kvm->mm);
|
||||
}
|
||||
|
||||
unsigned long kvmppc_h_svm_init_abort(struct kvm *kvm)
|
||||
|
@ -360,7 +704,7 @@ static struct page *kvmppc_uvmem_get_page(unsigned long gpa, struct kvm *kvm)
|
|||
goto out_clear;
|
||||
|
||||
uvmem_pfn = bit + pfn_first;
|
||||
kvmppc_uvmem_pfn_insert(gpa >> PAGE_SHIFT, uvmem_pfn, kvm);
|
||||
kvmppc_gfn_secure_uvmem_pfn(gpa >> PAGE_SHIFT, uvmem_pfn, kvm);
|
||||
|
||||
pvt->gpa = gpa;
|
||||
pvt->kvm = kvm;
|
||||
|
@ -379,13 +723,14 @@ out:
|
|||
}
|
||||
|
||||
/*
|
||||
* Alloc a PFN from private device memory pool and copy page from normal
|
||||
* memory to secure memory using UV_PAGE_IN uvcall.
|
||||
* Alloc a PFN from private device memory pool. If @pagein is true,
|
||||
* copy page from normal memory to secure memory using UV_PAGE_IN uvcall.
|
||||
*/
|
||||
static int
|
||||
kvmppc_svm_page_in(struct vm_area_struct *vma, unsigned long start,
|
||||
unsigned long end, unsigned long gpa, struct kvm *kvm,
|
||||
unsigned long page_shift, bool *downgrade)
|
||||
static int kvmppc_svm_page_in(struct vm_area_struct *vma,
|
||||
unsigned long start,
|
||||
unsigned long end, unsigned long gpa, struct kvm *kvm,
|
||||
unsigned long page_shift,
|
||||
bool pagein)
|
||||
{
|
||||
unsigned long src_pfn, dst_pfn = 0;
|
||||
struct migrate_vma mig;
|
||||
|
@ -402,18 +747,6 @@ kvmppc_svm_page_in(struct vm_area_struct *vma, unsigned long start,
|
|||
mig.dst = &dst_pfn;
|
||||
mig.flags = MIGRATE_VMA_SELECT_SYSTEM;
|
||||
|
||||
/*
|
||||
* We come here with mmap_lock write lock held just for
|
||||
* ksm_madvise(), otherwise we only need read mmap_lock.
|
||||
* Hence downgrade to read lock once ksm_madvise() is done.
|
||||
*/
|
||||
ret = ksm_madvise(vma, vma->vm_start, vma->vm_end,
|
||||
MADV_UNMERGEABLE, &vma->vm_flags);
|
||||
mmap_write_downgrade(kvm->mm);
|
||||
*downgrade = true;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = migrate_vma_setup(&mig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -429,11 +762,16 @@ kvmppc_svm_page_in(struct vm_area_struct *vma, unsigned long start,
|
|||
goto out_finalize;
|
||||
}
|
||||
|
||||
pfn = *mig.src >> MIGRATE_PFN_SHIFT;
|
||||
spage = migrate_pfn_to_page(*mig.src);
|
||||
if (spage)
|
||||
uv_page_in(kvm->arch.lpid, pfn << page_shift, gpa, 0,
|
||||
page_shift);
|
||||
if (pagein) {
|
||||
pfn = *mig.src >> MIGRATE_PFN_SHIFT;
|
||||
spage = migrate_pfn_to_page(*mig.src);
|
||||
if (spage) {
|
||||
ret = uv_page_in(kvm->arch.lpid, pfn << page_shift,
|
||||
gpa, 0, page_shift);
|
||||
if (ret)
|
||||
goto out_finalize;
|
||||
}
|
||||
}
|
||||
|
||||
*mig.dst = migrate_pfn(page_to_pfn(dpage)) | MIGRATE_PFN_LOCKED;
|
||||
migrate_vma_pages(&mig);
|
||||
|
@ -442,6 +780,80 @@ out_finalize:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int kvmppc_uv_migrate_mem_slot(struct kvm *kvm,
|
||||
const struct kvm_memory_slot *memslot)
|
||||
{
|
||||
unsigned long gfn = memslot->base_gfn;
|
||||
struct vm_area_struct *vma;
|
||||
unsigned long start, end;
|
||||
int ret = 0;
|
||||
|
||||
mmap_read_lock(kvm->mm);
|
||||
mutex_lock(&kvm->arch.uvmem_lock);
|
||||
while (kvmppc_next_nontransitioned_gfn(memslot, kvm, &gfn)) {
|
||||
ret = H_STATE;
|
||||
start = gfn_to_hva(kvm, gfn);
|
||||
if (kvm_is_error_hva(start))
|
||||
break;
|
||||
|
||||
end = start + (1UL << PAGE_SHIFT);
|
||||
vma = find_vma_intersection(kvm->mm, start, end);
|
||||
if (!vma || vma->vm_start > start || vma->vm_end < end)
|
||||
break;
|
||||
|
||||
ret = kvmppc_svm_page_in(vma, start, end,
|
||||
(gfn << PAGE_SHIFT), kvm, PAGE_SHIFT, false);
|
||||
if (ret) {
|
||||
ret = H_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* relinquish the cpu if needed */
|
||||
cond_resched();
|
||||
}
|
||||
mutex_unlock(&kvm->arch.uvmem_lock);
|
||||
mmap_read_unlock(kvm->mm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned long kvmppc_h_svm_init_done(struct kvm *kvm)
|
||||
{
|
||||
struct kvm_memslots *slots;
|
||||
struct kvm_memory_slot *memslot;
|
||||
int srcu_idx;
|
||||
long ret = H_SUCCESS;
|
||||
|
||||
if (!(kvm->arch.secure_guest & KVMPPC_SECURE_INIT_START))
|
||||
return H_UNSUPPORTED;
|
||||
|
||||
/* migrate any unmoved normal pfn to device pfns*/
|
||||
srcu_idx = srcu_read_lock(&kvm->srcu);
|
||||
slots = kvm_memslots(kvm);
|
||||
kvm_for_each_memslot(memslot, slots) {
|
||||
ret = kvmppc_uv_migrate_mem_slot(kvm, memslot);
|
||||
if (ret) {
|
||||
/*
|
||||
* The pages will remain transitioned.
|
||||
* Its the callers responsibility to
|
||||
* terminate the VM, which will undo
|
||||
* all state of the VM. Till then
|
||||
* this VM is in a erroneous state.
|
||||
* Its KVMPPC_SECURE_INIT_DONE will
|
||||
* remain unset.
|
||||
*/
|
||||
ret = H_STATE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
kvm->arch.secure_guest |= KVMPPC_SECURE_INIT_DONE;
|
||||
pr_info("LPID %d went secure\n", kvm->arch.lpid);
|
||||
|
||||
out:
|
||||
srcu_read_unlock(&kvm->srcu, srcu_idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shares the page with HV, thus making it a normal page.
|
||||
*
|
||||
|
@ -451,8 +863,8 @@ out_finalize:
|
|||
* In the former case, uses dev_pagemap_ops.migrate_to_ram handler
|
||||
* to unmap the device page from QEMU's page tables.
|
||||
*/
|
||||
static unsigned long
|
||||
kvmppc_share_page(struct kvm *kvm, unsigned long gpa, unsigned long page_shift)
|
||||
static unsigned long kvmppc_share_page(struct kvm *kvm, unsigned long gpa,
|
||||
unsigned long page_shift)
|
||||
{
|
||||
|
||||
int ret = H_PARAMETER;
|
||||
|
@ -469,6 +881,11 @@ kvmppc_share_page(struct kvm *kvm, unsigned long gpa, unsigned long page_shift)
|
|||
uvmem_page = pfn_to_page(uvmem_pfn);
|
||||
pvt = uvmem_page->zone_device_data;
|
||||
pvt->skip_page_out = true;
|
||||
/*
|
||||
* do not drop the GFN. It is a valid GFN
|
||||
* that is transitioned to a shared GFN.
|
||||
*/
|
||||
pvt->remove_gfn = false;
|
||||
}
|
||||
|
||||
retry:
|
||||
|
@ -482,12 +899,16 @@ retry:
|
|||
uvmem_page = pfn_to_page(uvmem_pfn);
|
||||
pvt = uvmem_page->zone_device_data;
|
||||
pvt->skip_page_out = true;
|
||||
pvt->remove_gfn = false; /* it continues to be a valid GFN */
|
||||
kvm_release_pfn_clean(pfn);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (!uv_page_in(kvm->arch.lpid, pfn << page_shift, gpa, 0, page_shift))
|
||||
if (!uv_page_in(kvm->arch.lpid, pfn << page_shift, gpa, 0,
|
||||
page_shift)) {
|
||||
kvmppc_gfn_shared(gfn, kvm);
|
||||
ret = H_SUCCESS;
|
||||
}
|
||||
kvm_release_pfn_clean(pfn);
|
||||
mutex_unlock(&kvm->arch.uvmem_lock);
|
||||
out:
|
||||
|
@ -501,11 +922,10 @@ out:
|
|||
* H_PAGE_IN_SHARED flag makes the page shared which means that the same
|
||||
* memory in is visible from both UV and HV.
|
||||
*/
|
||||
unsigned long
|
||||
kvmppc_h_svm_page_in(struct kvm *kvm, unsigned long gpa,
|
||||
unsigned long flags, unsigned long page_shift)
|
||||
unsigned long kvmppc_h_svm_page_in(struct kvm *kvm, unsigned long gpa,
|
||||
unsigned long flags,
|
||||
unsigned long page_shift)
|
||||
{
|
||||
bool downgrade = false;
|
||||
unsigned long start, end;
|
||||
struct vm_area_struct *vma;
|
||||
int srcu_idx;
|
||||
|
@ -526,7 +946,7 @@ kvmppc_h_svm_page_in(struct kvm *kvm, unsigned long gpa,
|
|||
|
||||
ret = H_PARAMETER;
|
||||
srcu_idx = srcu_read_lock(&kvm->srcu);
|
||||
mmap_write_lock(kvm->mm);
|
||||
mmap_read_lock(kvm->mm);
|
||||
|
||||
start = gfn_to_hva(kvm, gfn);
|
||||
if (kvm_is_error_hva(start))
|
||||
|
@ -542,97 +962,20 @@ kvmppc_h_svm_page_in(struct kvm *kvm, unsigned long gpa,
|
|||
if (!vma || vma->vm_start > start || vma->vm_end < end)
|
||||
goto out_unlock;
|
||||
|
||||
if (!kvmppc_svm_page_in(vma, start, end, gpa, kvm, page_shift,
|
||||
&downgrade))
|
||||
ret = H_SUCCESS;
|
||||
if (kvmppc_svm_page_in(vma, start, end, gpa, kvm, page_shift,
|
||||
true))
|
||||
goto out_unlock;
|
||||
|
||||
ret = H_SUCCESS;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&kvm->arch.uvmem_lock);
|
||||
out:
|
||||
if (downgrade)
|
||||
mmap_read_unlock(kvm->mm);
|
||||
else
|
||||
mmap_write_unlock(kvm->mm);
|
||||
mmap_read_unlock(kvm->mm);
|
||||
srcu_read_unlock(&kvm->srcu, srcu_idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Provision a new page on HV side and copy over the contents
|
||||
* from secure memory using UV_PAGE_OUT uvcall.
|
||||
*/
|
||||
static int
|
||||
kvmppc_svm_page_out(struct vm_area_struct *vma, unsigned long start,
|
||||
unsigned long end, unsigned long page_shift,
|
||||
struct kvm *kvm, unsigned long gpa)
|
||||
{
|
||||
unsigned long src_pfn, dst_pfn = 0;
|
||||
struct migrate_vma mig;
|
||||
struct page *dpage, *spage;
|
||||
struct kvmppc_uvmem_page_pvt *pvt;
|
||||
unsigned long pfn;
|
||||
int ret = U_SUCCESS;
|
||||
|
||||
memset(&mig, 0, sizeof(mig));
|
||||
mig.vma = vma;
|
||||
mig.start = start;
|
||||
mig.end = end;
|
||||
mig.src = &src_pfn;
|
||||
mig.dst = &dst_pfn;
|
||||
mig.pgmap_owner = &kvmppc_uvmem_pgmap;
|
||||
mig.flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE;
|
||||
|
||||
mutex_lock(&kvm->arch.uvmem_lock);
|
||||
/* The requested page is already paged-out, nothing to do */
|
||||
if (!kvmppc_gfn_is_uvmem_pfn(gpa >> page_shift, kvm, NULL))
|
||||
goto out;
|
||||
|
||||
ret = migrate_vma_setup(&mig);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
spage = migrate_pfn_to_page(*mig.src);
|
||||
if (!spage || !(*mig.src & MIGRATE_PFN_MIGRATE))
|
||||
goto out_finalize;
|
||||
|
||||
if (!is_zone_device_page(spage))
|
||||
goto out_finalize;
|
||||
|
||||
dpage = alloc_page_vma(GFP_HIGHUSER, vma, start);
|
||||
if (!dpage) {
|
||||
ret = -1;
|
||||
goto out_finalize;
|
||||
}
|
||||
|
||||
lock_page(dpage);
|
||||
pvt = spage->zone_device_data;
|
||||
pfn = page_to_pfn(dpage);
|
||||
|
||||
/*
|
||||
* This function is used in two cases:
|
||||
* - When HV touches a secure page, for which we do UV_PAGE_OUT
|
||||
* - When a secure page is converted to shared page, we *get*
|
||||
* the page to essentially unmap the device page. In this
|
||||
* case we skip page-out.
|
||||
*/
|
||||
if (!pvt->skip_page_out)
|
||||
ret = uv_page_out(kvm->arch.lpid, pfn << page_shift,
|
||||
gpa, 0, page_shift);
|
||||
|
||||
if (ret == U_SUCCESS)
|
||||
*mig.dst = migrate_pfn(pfn) | MIGRATE_PFN_LOCKED;
|
||||
else {
|
||||
unlock_page(dpage);
|
||||
__free_page(dpage);
|
||||
goto out_finalize;
|
||||
}
|
||||
|
||||
migrate_vma_pages(&mig);
|
||||
out_finalize:
|
||||
migrate_vma_finalize(&mig);
|
||||
out:
|
||||
mutex_unlock(&kvm->arch.uvmem_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fault handler callback that gets called when HV touches any page that
|
||||
|
@ -657,7 +1000,8 @@ static vm_fault_t kvmppc_uvmem_migrate_to_ram(struct vm_fault *vmf)
|
|||
/*
|
||||
* Release the device PFN back to the pool
|
||||
*
|
||||
* Gets called when secure page becomes a normal page during H_SVM_PAGE_OUT.
|
||||
* Gets called when secure GFN tranistions from a secure-PFN
|
||||
* to a normal PFN during H_SVM_PAGE_OUT.
|
||||
* Gets called with kvm->arch.uvmem_lock held.
|
||||
*/
|
||||
static void kvmppc_uvmem_page_free(struct page *page)
|
||||
|
@ -672,7 +1016,10 @@ static void kvmppc_uvmem_page_free(struct page *page)
|
|||
|
||||
pvt = page->zone_device_data;
|
||||
page->zone_device_data = NULL;
|
||||
kvmppc_uvmem_pfn_remove(pvt->gpa >> PAGE_SHIFT, pvt->kvm);
|
||||
if (pvt->remove_gfn)
|
||||
kvmppc_gfn_remove(pvt->gpa >> PAGE_SHIFT, pvt->kvm);
|
||||
else
|
||||
kvmppc_gfn_secure_mem_pfn(pvt->gpa >> PAGE_SHIFT, pvt->kvm);
|
||||
kfree(pvt);
|
||||
}
|
||||
|
||||
|
@ -744,6 +1091,21 @@ out:
|
|||
return (ret == U_SUCCESS) ? RESUME_GUEST : -EFAULT;
|
||||
}
|
||||
|
||||
int kvmppc_uvmem_memslot_create(struct kvm *kvm, const struct kvm_memory_slot *new)
|
||||
{
|
||||
int ret = __kvmppc_uvmem_memslot_create(kvm, new);
|
||||
|
||||
if (!ret)
|
||||
ret = kvmppc_uv_migrate_mem_slot(kvm, new);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void kvmppc_uvmem_memslot_delete(struct kvm *kvm, const struct kvm_memory_slot *old)
|
||||
{
|
||||
__kvmppc_uvmem_memslot_delete(kvm, old);
|
||||
}
|
||||
|
||||
static u64 kvmppc_get_secmem_size(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
|
|
@ -55,8 +55,7 @@
|
|||
****************************************************************************/
|
||||
|
||||
/* Registers:
|
||||
* r3: kvm_run pointer
|
||||
* r4: vcpu pointer
|
||||
* r3: vcpu pointer
|
||||
*/
|
||||
_GLOBAL(__kvmppc_vcpu_run)
|
||||
|
||||
|
@ -68,8 +67,8 @@ kvm_start_entry:
|
|||
/* Save host state to the stack */
|
||||
PPC_STLU r1, -SWITCH_FRAME_SIZE(r1)
|
||||
|
||||
/* Save r3 (kvm_run) and r4 (vcpu) */
|
||||
SAVE_2GPRS(3, r1)
|
||||
/* Save r3 (vcpu) */
|
||||
SAVE_GPR(3, r1)
|
||||
|
||||
/* Save non-volatile registers (r14 - r31) */
|
||||
SAVE_NVGPRS(r1)
|
||||
|
@ -82,47 +81,46 @@ kvm_start_entry:
|
|||
PPC_STL r0, _LINK(r1)
|
||||
|
||||
/* Load non-volatile guest state from the vcpu */
|
||||
VCPU_LOAD_NVGPRS(r4)
|
||||
VCPU_LOAD_NVGPRS(r3)
|
||||
|
||||
kvm_start_lightweight:
|
||||
/* Copy registers into shadow vcpu so we can access them in real mode */
|
||||
mr r3, r4
|
||||
bl FUNC(kvmppc_copy_to_svcpu)
|
||||
nop
|
||||
REST_GPR(4, r1)
|
||||
REST_GPR(3, r1)
|
||||
|
||||
#ifdef CONFIG_PPC_BOOK3S_64
|
||||
/* Get the dcbz32 flag */
|
||||
PPC_LL r3, VCPU_HFLAGS(r4)
|
||||
rldicl r3, r3, 0, 63 /* r3 &= 1 */
|
||||
stb r3, HSTATE_RESTORE_HID5(r13)
|
||||
PPC_LL r0, VCPU_HFLAGS(r3)
|
||||
rldicl r0, r0, 0, 63 /* r3 &= 1 */
|
||||
stb r0, HSTATE_RESTORE_HID5(r13)
|
||||
|
||||
/* Load up guest SPRG3 value, since it's user readable */
|
||||
lwz r3, VCPU_SHAREDBE(r4)
|
||||
cmpwi r3, 0
|
||||
ld r5, VCPU_SHARED(r4)
|
||||
lbz r4, VCPU_SHAREDBE(r3)
|
||||
cmpwi r4, 0
|
||||
ld r5, VCPU_SHARED(r3)
|
||||
beq sprg3_little_endian
|
||||
sprg3_big_endian:
|
||||
#ifdef __BIG_ENDIAN__
|
||||
ld r3, VCPU_SHARED_SPRG3(r5)
|
||||
ld r4, VCPU_SHARED_SPRG3(r5)
|
||||
#else
|
||||
addi r5, r5, VCPU_SHARED_SPRG3
|
||||
ldbrx r3, 0, r5
|
||||
ldbrx r4, 0, r5
|
||||
#endif
|
||||
b after_sprg3_load
|
||||
sprg3_little_endian:
|
||||
#ifdef __LITTLE_ENDIAN__
|
||||
ld r3, VCPU_SHARED_SPRG3(r5)
|
||||
ld r4, VCPU_SHARED_SPRG3(r5)
|
||||
#else
|
||||
addi r5, r5, VCPU_SHARED_SPRG3
|
||||
ldbrx r3, 0, r5
|
||||
ldbrx r4, 0, r5
|
||||
#endif
|
||||
|
||||
after_sprg3_load:
|
||||
mtspr SPRN_SPRG3, r3
|
||||
mtspr SPRN_SPRG3, r4
|
||||
#endif /* CONFIG_PPC_BOOK3S_64 */
|
||||
|
||||
PPC_LL r4, VCPU_SHADOW_MSR(r4) /* get shadow_msr */
|
||||
PPC_LL r4, VCPU_SHADOW_MSR(r3) /* get shadow_msr */
|
||||
|
||||
/* Jump to segment patching handler and into our guest */
|
||||
bl FUNC(kvmppc_entry_trampoline)
|
||||
|
@ -146,7 +144,7 @@ after_sprg3_load:
|
|||
*
|
||||
*/
|
||||
|
||||
PPC_LL r3, GPR4(r1) /* vcpu pointer */
|
||||
PPC_LL r3, GPR3(r1) /* vcpu pointer */
|
||||
|
||||
/*
|
||||
* kvmppc_copy_from_svcpu can clobber volatile registers, save
|
||||
|
@ -169,7 +167,7 @@ after_sprg3_load:
|
|||
#endif /* CONFIG_PPC_BOOK3S_64 */
|
||||
|
||||
/* R7 = vcpu */
|
||||
PPC_LL r7, GPR4(r1)
|
||||
PPC_LL r7, GPR3(r1)
|
||||
|
||||
PPC_STL r14, VCPU_GPR(R14)(r7)
|
||||
PPC_STL r15, VCPU_GPR(R15)(r7)
|
||||
|
@ -190,11 +188,11 @@ after_sprg3_load:
|
|||
PPC_STL r30, VCPU_GPR(R30)(r7)
|
||||
PPC_STL r31, VCPU_GPR(R31)(r7)
|
||||
|
||||
/* Pass the exit number as 3rd argument to kvmppc_handle_exit */
|
||||
lwz r5, VCPU_TRAP(r7)
|
||||
/* Pass the exit number as 2nd argument to kvmppc_handle_exit */
|
||||
lwz r4, VCPU_TRAP(r7)
|
||||
|
||||
/* Restore r3 (kvm_run) and r4 (vcpu) */
|
||||
REST_2GPRS(3, r1)
|
||||
/* Restore r3 (vcpu) */
|
||||
REST_GPR(3, r1)
|
||||
bl FUNC(kvmppc_handle_exit_pr)
|
||||
|
||||
/* If RESUME_GUEST, get back in the loop */
|
||||
|
@ -223,11 +221,11 @@ kvm_loop_heavyweight:
|
|||
PPC_LL r4, _LINK(r1)
|
||||
PPC_STL r4, (PPC_LR_STKOFF + SWITCH_FRAME_SIZE)(r1)
|
||||
|
||||
/* Load vcpu and cpu_run */
|
||||
REST_2GPRS(3, r1)
|
||||
/* Load vcpu */
|
||||
REST_GPR(3, r1)
|
||||
|
||||
/* Load non-volatile guest state from the vcpu */
|
||||
VCPU_LOAD_NVGPRS(r4)
|
||||
VCPU_LOAD_NVGPRS(r3)
|
||||
|
||||
/* Jump back into the beginning of this function */
|
||||
b kvm_start_lightweight
|
||||
|
@ -235,7 +233,7 @@ kvm_loop_heavyweight:
|
|||
kvm_loop_lightweight:
|
||||
|
||||
/* We'll need the vcpu pointer */
|
||||
REST_GPR(4, r1)
|
||||
REST_GPR(3, r1)
|
||||
|
||||
/* Jump back into the beginning of this function */
|
||||
b kvm_start_lightweight
|
||||
|
|
|
@ -1151,9 +1151,9 @@ static int kvmppc_exit_pr_progint(struct kvm_vcpu *vcpu, unsigned int exit_nr)
|
|||
return r;
|
||||
}
|
||||
|
||||
int kvmppc_handle_exit_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
||||
unsigned int exit_nr)
|
||||
int kvmppc_handle_exit_pr(struct kvm_vcpu *vcpu, unsigned int exit_nr)
|
||||
{
|
||||
struct kvm_run *run = vcpu->run;
|
||||
int r = RESUME_HOST;
|
||||
int s;
|
||||
|
||||
|
@ -1826,12 +1826,11 @@ static void kvmppc_core_vcpu_free_pr(struct kvm_vcpu *vcpu)
|
|||
|
||||
static int kvmppc_vcpu_run_pr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_run *run = vcpu->run;
|
||||
int ret;
|
||||
|
||||
/* Check if we can run the vcpu at all */
|
||||
if (!vcpu->arch.sane) {
|
||||
run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
|
||||
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1858,7 +1857,7 @@ static int kvmppc_vcpu_run_pr(struct kvm_vcpu *vcpu)
|
|||
|
||||
kvmppc_fix_ee_before_entry();
|
||||
|
||||
ret = __kvmppc_vcpu_run(run, vcpu);
|
||||
ret = __kvmppc_vcpu_run(vcpu);
|
||||
|
||||
kvmppc_clear_debug(vcpu);
|
||||
|
||||
|
|
|
@ -229,7 +229,9 @@ int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu)
|
|||
*/
|
||||
args_phys = kvmppc_get_gpr(vcpu, 4) & KVM_PAM;
|
||||
|
||||
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
|
||||
rc = kvm_read_guest(vcpu->kvm, args_phys, &args, sizeof(args));
|
||||
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
|
||||
if (rc)
|
||||
goto fail;
|
||||
|
||||
|
|
|
@ -731,12 +731,11 @@ int kvmppc_core_check_requests(struct kvm_vcpu *vcpu)
|
|||
|
||||
int kvmppc_vcpu_run(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_run *run = vcpu->run;
|
||||
int ret, s;
|
||||
struct debug_reg debug;
|
||||
|
||||
if (!vcpu->arch.sane) {
|
||||
run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
|
||||
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -778,7 +777,7 @@ int kvmppc_vcpu_run(struct kvm_vcpu *vcpu)
|
|||
vcpu->arch.pgdir = vcpu->kvm->mm->pgd;
|
||||
kvmppc_fix_ee_before_entry();
|
||||
|
||||
ret = __kvmppc_vcpu_run(run, vcpu);
|
||||
ret = __kvmppc_vcpu_run(vcpu);
|
||||
|
||||
/* No need for guest_exit. It's done in handle_exit.
|
||||
We also get here with interrupts enabled. */
|
||||
|
@ -982,9 +981,9 @@ static int kvmppc_resume_inst_load(struct kvm_vcpu *vcpu,
|
|||
*
|
||||
* Return value is in the form (errcode<<2 | RESUME_FLAG_HOST | RESUME_FLAG_NV)
|
||||
*/
|
||||
int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
||||
unsigned int exit_nr)
|
||||
int kvmppc_handle_exit(struct kvm_vcpu *vcpu, unsigned int exit_nr)
|
||||
{
|
||||
struct kvm_run *run = vcpu->run;
|
||||
int r = RESUME_HOST;
|
||||
int s;
|
||||
int idx;
|
||||
|
|
|
@ -237,7 +237,7 @@ _GLOBAL(kvmppc_resume_host)
|
|||
/* Switch to kernel stack and jump to handler. */
|
||||
LOAD_REG_ADDR(r3, kvmppc_handle_exit)
|
||||
mtctr r3
|
||||
lwz r3, HOST_RUN(r1)
|
||||
mr r3, r4
|
||||
lwz r2, HOST_R2(r1)
|
||||
mr r14, r4 /* Save vcpu pointer. */
|
||||
|
||||
|
@ -337,15 +337,14 @@ heavyweight_exit:
|
|||
|
||||
|
||||
/* Registers:
|
||||
* r3: kvm_run pointer
|
||||
* r4: vcpu pointer
|
||||
* r3: vcpu pointer
|
||||
*/
|
||||
_GLOBAL(__kvmppc_vcpu_run)
|
||||
stwu r1, -HOST_STACK_SIZE(r1)
|
||||
stw r1, VCPU_HOST_STACK(r4) /* Save stack pointer to vcpu. */
|
||||
stw r1, VCPU_HOST_STACK(r3) /* Save stack pointer to vcpu. */
|
||||
|
||||
/* Save host state to stack. */
|
||||
stw r3, HOST_RUN(r1)
|
||||
mr r4, r3
|
||||
mflr r3
|
||||
stw r3, HOST_STACK_LR(r1)
|
||||
mfcr r5
|
||||
|
|
|
@ -434,9 +434,10 @@ _GLOBAL(kvmppc_resume_host)
|
|||
#endif
|
||||
|
||||
/* Switch to kernel stack and jump to handler. */
|
||||
PPC_LL r3, HOST_RUN(r1)
|
||||
mr r3, r4
|
||||
mr r5, r14 /* intno */
|
||||
mr r14, r4 /* Save vcpu pointer. */
|
||||
mr r4, r5
|
||||
bl kvmppc_handle_exit
|
||||
|
||||
/* Restore vcpu pointer and the nonvolatiles we used. */
|
||||
|
@ -525,15 +526,14 @@ heavyweight_exit:
|
|||
blr
|
||||
|
||||
/* Registers:
|
||||
* r3: kvm_run pointer
|
||||
* r4: vcpu pointer
|
||||
* r3: vcpu pointer
|
||||
*/
|
||||
_GLOBAL(__kvmppc_vcpu_run)
|
||||
stwu r1, -HOST_STACK_SIZE(r1)
|
||||
PPC_STL r1, VCPU_HOST_STACK(r4) /* Save stack pointer to vcpu. */
|
||||
PPC_STL r1, VCPU_HOST_STACK(r3) /* Save stack pointer to vcpu. */
|
||||
|
||||
/* Save host state to stack. */
|
||||
PPC_STL r3, HOST_RUN(r1)
|
||||
mr r4, r3
|
||||
mflr r3
|
||||
mfcr r5
|
||||
PPC_STL r3, HOST_STACK_LR(r1)
|
||||
|
|
|
@ -403,7 +403,10 @@ int kvmppc_ld(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr,
|
|||
return EMULATE_DONE;
|
||||
}
|
||||
|
||||
if (kvm_read_guest(vcpu->kvm, pte.raddr, ptr, size))
|
||||
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
|
||||
rc = kvm_read_guest(vcpu->kvm, pte.raddr, ptr, size);
|
||||
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
|
||||
if (rc)
|
||||
return EMULATE_DO_MMIO;
|
||||
|
||||
return EMULATE_DONE;
|
||||
|
|
|
@ -370,7 +370,8 @@ void kvm_set_cpu_caps(void)
|
|||
kvm_cpu_cap_mask(CPUID_7_EDX,
|
||||
F(AVX512_4VNNIW) | F(AVX512_4FMAPS) | F(SPEC_CTRL) |
|
||||
F(SPEC_CTRL_SSBD) | F(ARCH_CAPABILITIES) | F(INTEL_STIBP) |
|
||||
F(MD_CLEAR) | F(AVX512_VP2INTERSECT) | F(FSRM)
|
||||
F(MD_CLEAR) | F(AVX512_VP2INTERSECT) | F(FSRM) |
|
||||
F(SERIALIZE)
|
||||
);
|
||||
|
||||
/* TSC_ADJUST and ARCH_CAPABILITIES are emulated in software. */
|
||||
|
|
|
@ -900,6 +900,7 @@ int kvm_hv_activate_synic(struct kvm_vcpu *vcpu, bool dont_zero_synic_pages)
|
|||
kvm_request_apicv_update(vcpu->kvm, false, APICV_INHIBIT_REASON_HYPERV);
|
||||
synic->active = true;
|
||||
synic->dont_zero_synic_pages = dont_zero_synic_pages;
|
||||
synic->control = HV_SYNIC_CONTROL_ENABLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -820,22 +820,22 @@ int kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
|
|||
if ((cr0 & X86_CR0_PG) && !(cr0 & X86_CR0_PE))
|
||||
return 1;
|
||||
|
||||
if (cr0 & X86_CR0_PG) {
|
||||
#ifdef CONFIG_X86_64
|
||||
if (!is_paging(vcpu) && (vcpu->arch.efer & EFER_LME)) {
|
||||
int cs_db, cs_l;
|
||||
if ((vcpu->arch.efer & EFER_LME) && !is_paging(vcpu) &&
|
||||
(cr0 & X86_CR0_PG)) {
|
||||
int cs_db, cs_l;
|
||||
|
||||
if (!is_pae(vcpu))
|
||||
return 1;
|
||||
kvm_x86_ops.get_cs_db_l_bits(vcpu, &cs_db, &cs_l);
|
||||
if (cs_l)
|
||||
return 1;
|
||||
} else
|
||||
#endif
|
||||
if (is_pae(vcpu) && ((cr0 ^ old_cr0) & pdptr_bits) &&
|
||||
!load_pdptrs(vcpu, vcpu->arch.walk_mmu, kvm_read_cr3(vcpu)))
|
||||
if (!is_pae(vcpu))
|
||||
return 1;
|
||||
kvm_x86_ops.get_cs_db_l_bits(vcpu, &cs_db, &cs_l);
|
||||
if (cs_l)
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
if (!(vcpu->arch.efer & EFER_LME) && (cr0 & X86_CR0_PG) &&
|
||||
is_pae(vcpu) && ((cr0 ^ old_cr0) & pdptr_bits) &&
|
||||
!load_pdptrs(vcpu, vcpu->arch.walk_mmu, kvm_read_cr3(vcpu)))
|
||||
return 1;
|
||||
|
||||
if (!(cr0 & X86_CR0_PG) && kvm_read_cr4_bits(vcpu, X86_CR4_PCIDE))
|
||||
return 1;
|
||||
|
|
|
@ -26,16 +26,9 @@ enum kvm_arch_timer_regs {
|
|||
struct arch_timer_context {
|
||||
struct kvm_vcpu *vcpu;
|
||||
|
||||
/* Registers: control register, timer value */
|
||||
u32 cnt_ctl;
|
||||
u64 cnt_cval;
|
||||
|
||||
/* Timer IRQ */
|
||||
struct kvm_irq_level irq;
|
||||
|
||||
/* Virtual offset */
|
||||
u64 cntvoff;
|
||||
|
||||
/* Emulated Timer (may be unused) */
|
||||
struct hrtimer hrtimer;
|
||||
|
||||
|
@ -71,7 +64,7 @@ int kvm_timer_hyp_init(bool);
|
|||
int kvm_timer_enable(struct kvm_vcpu *vcpu);
|
||||
int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu);
|
||||
void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu);
|
||||
void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu);
|
||||
void kvm_timer_sync_user(struct kvm_vcpu *vcpu);
|
||||
bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu);
|
||||
void kvm_timer_update_run(struct kvm_vcpu *vcpu);
|
||||
void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu);
|
||||
|
@ -109,4 +102,8 @@ void kvm_arm_timer_write_sysreg(struct kvm_vcpu *vcpu,
|
|||
enum kvm_arch_timer_regs treg,
|
||||
u64 val);
|
||||
|
||||
/* Needed for tracing */
|
||||
u32 timer_get_ctl(struct arch_timer_context *ctxt);
|
||||
u64 timer_get_cval(struct arch_timer_context *ctxt);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
ERSN(NMI), ERSN(INTERNAL_ERROR), ERSN(OSI), ERSN(PAPR_HCALL), \
|
||||
ERSN(S390_UCONTROL), ERSN(WATCHDOG), ERSN(S390_TSCH), ERSN(EPR),\
|
||||
ERSN(SYSTEM_EVENT), ERSN(S390_STSI), ERSN(IOAPIC_EOI), \
|
||||
ERSN(HYPERV)
|
||||
ERSN(HYPERV), ERSN(ARM_NISV)
|
||||
|
||||
TRACE_EVENT(kvm_userspace_exit,
|
||||
TP_PROTO(__u32 reason, int errno),
|
||||
|
|
|
@ -109,6 +109,7 @@ static bool is_ignored_symbol(const char *name, char type)
|
|||
".LASANPC", /* s390 kasan local symbols */
|
||||
"__crc_", /* modversions */
|
||||
"__efistub_", /* arm64 EFI stub namespace */
|
||||
"__kvm_nvhe_", /* arm64 non-VHE KVM namespace */
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue