mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 03:40:35 +09:00
ANDROID: KVM: arm64: pkvm: Lazy host FP save/restore
Implement lazy save/restore of the FPSIMD and SVE registers for the host so that the guest register state remains live for a vCPU with KVM_ARM64_FP_ENABLED set and the state is only switched in response to a trap from the host. Signed-off-by: Marc Zyngier <maz@kernel.org> Bug: 209580772 Change-Id: I6977740727964e297d1e658e8eaec4f0125cce48 Signed-off-by: Will Deacon <willdeacon@google.com>
This commit is contained in:
committed by
Will Deacon
parent
7d81b2a5e6
commit
dc2fae6338
@@ -337,12 +337,43 @@ static void sync_shadow_state(struct kvm_vcpu *shadow_vcpu, u32 exit_reason)
|
||||
|
||||
struct pkvm_loaded_state {
|
||||
/* loaded vcpu is HYP VA */
|
||||
struct kvm_vcpu *vcpu;
|
||||
bool is_shadow;
|
||||
struct kvm_vcpu *vcpu;
|
||||
bool is_shadow;
|
||||
|
||||
/*
|
||||
* Host FPSIMD state. Written to when the guest accesses its
|
||||
* own FPSIMD state, and read when the guest state is live and
|
||||
* that it needs to be switched back to the host.
|
||||
*
|
||||
* Only valid when the KVM_ARM64_FP_ENABLED flag is set in the
|
||||
* shadow structure.
|
||||
*/
|
||||
struct user_fpsimd_state host_fpsimd_state;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct pkvm_loaded_state, loaded_state);
|
||||
|
||||
static void fpsimd_host_restore(void)
|
||||
{
|
||||
sysreg_clear_set(cptr_el2, CPTR_EL2_TZ | CPTR_EL2_TFP, 0);
|
||||
isb();
|
||||
|
||||
if (unlikely(is_protected_kvm_enabled())) {
|
||||
struct pkvm_loaded_state *state = this_cpu_ptr(&loaded_state);
|
||||
|
||||
if (state->is_shadow) {
|
||||
__fpsimd_save_state(&state->vcpu->arch.ctxt.fp_regs);
|
||||
__fpsimd_restore_state(&state->host_fpsimd_state);
|
||||
|
||||
state->vcpu->arch.flags &= ~KVM_ARM64_FP_ENABLED;
|
||||
state->vcpu->arch.flags |= KVM_ARM64_FP_HOST;
|
||||
}
|
||||
}
|
||||
|
||||
if (system_supports_sve())
|
||||
sve_cond_update_zcr_vq(ZCR_ELx_LEN_MASK, SYS_ZCR_EL2);
|
||||
}
|
||||
|
||||
static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(struct kvm_vcpu *, vcpu, host_ctxt, 1);
|
||||
@@ -364,8 +395,8 @@ static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt)
|
||||
state->is_shadow = state->vcpu != vcpu;
|
||||
|
||||
if (state->is_shadow) {
|
||||
/* FIXME: we can't trust the validity of these pointers */
|
||||
state->vcpu->arch.host_fpsimd_state = vcpu->arch.host_fpsimd_state;
|
||||
state->vcpu->arch.host_fpsimd_state = &state->host_fpsimd_state;
|
||||
state->vcpu->arch.flags |= KVM_ARM64_FP_HOST;
|
||||
|
||||
/* Propagate WFx trapping flags, trap ptrauth */
|
||||
state->vcpu->arch.hcr_el2 &= ~(HCR_TWE | HCR_TWI |
|
||||
@@ -376,10 +407,20 @@ static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt)
|
||||
|
||||
static void handle___pkvm_vcpu_put(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
struct pkvm_loaded_state *state = this_cpu_ptr(&loaded_state);
|
||||
DECLARE_REG(struct kvm_vcpu *, vcpu, host_ctxt, 1);
|
||||
|
||||
/* "It's over and done with..." */
|
||||
state->vcpu = NULL;
|
||||
if (unlikely(is_protected_kvm_enabled())) {
|
||||
struct pkvm_loaded_state *state = this_cpu_ptr(&loaded_state);
|
||||
|
||||
if (state->vcpu && state->is_shadow &&
|
||||
state->vcpu->arch.pkvm.host_vcpu == kern_hyp_va(vcpu)) {
|
||||
if (state->vcpu->arch.flags & KVM_ARM64_FP_ENABLED)
|
||||
fpsimd_host_restore();
|
||||
|
||||
/* "It's over and done with..." */
|
||||
state->vcpu = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
|
||||
@@ -397,6 +438,18 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
|
||||
|
||||
if (state->is_shadow)
|
||||
sync_shadow_state(state->vcpu, ret);
|
||||
|
||||
if (state->vcpu->arch.flags & KVM_ARM64_FP_ENABLED) {
|
||||
/*
|
||||
* The guest has used the FP, trap all accesses
|
||||
* from the host (both FP and SVE).
|
||||
*/
|
||||
u64 reg = CPTR_EL2_TFP;
|
||||
if (system_supports_sve())
|
||||
reg |= CPTR_EL2_TZ;
|
||||
|
||||
sysreg_clear_set(cptr_el2, 0, reg);
|
||||
}
|
||||
} else {
|
||||
ret = __kvm_vcpu_run(kern_hyp_va(vcpu));
|
||||
}
|
||||
@@ -722,10 +775,9 @@ void handle_trap(struct kvm_cpu_context *host_ctxt)
|
||||
case ESR_ELx_EC_SMC64:
|
||||
handle_host_smc(host_ctxt);
|
||||
break;
|
||||
case ESR_ELx_EC_FP_ASIMD:
|
||||
case ESR_ELx_EC_SVE:
|
||||
sysreg_clear_set(cptr_el2, CPTR_EL2_TZ, 0);
|
||||
isb();
|
||||
sve_cond_update_zcr_vq(ZCR_ELx_LEN_MASK, SYS_ZCR_EL2);
|
||||
fpsimd_host_restore();
|
||||
break;
|
||||
case ESR_ELx_EC_IABT_LOW:
|
||||
case ESR_ELx_EC_DABT_LOW:
|
||||
|
||||
Reference in New Issue
Block a user