mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
ANDROID: KVM: arm64: pkvm: State sync primitives for non-protected guests
In order for a non-protected guest to be functionnal, userspace has to be able to query its state, which means that the host view of the vcpu has to be kept up to date. In order to achieve this, we establish the following scheme for EL2: - On entering vcpu_run(), we check for the KVM_ARM64_PKVM_STATE_DIRTY flag in the host vcpu. If set, we sync the state *from* the host to the shadow version. - On exiting vcpu_run(), we don't do anything, but let the host issue a synch hypercall if required. - On vcpu_put(), we force a synchronisation *to* the host. The El1 host will have a complementary approach in the following patches. Signed-off-by: Marc Zyngier <maz@kernel.org> Bug: 209580772 Change-Id: I42811a25d2e176d6c7d9a66ade6e9149a96e9256 Signed-off-by: Will Deacon <willdeacon@google.com>
This commit is contained in:
committed by
Will Deacon
parent
20204f79de
commit
a282cd5146
@@ -79,6 +79,7 @@ enum __kvm_host_smccc_func {
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_teardown_shadow,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_vcpu_load,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_vcpu_put,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_vcpu_sync_state,
|
||||
};
|
||||
|
||||
#define DECLARE_KVM_VHE_SYM(sym) extern char sym[]
|
||||
|
||||
@@ -550,6 +550,7 @@ struct kvm_vcpu_arch {
|
||||
#define KVM_ARM64_DEBUG_STATE_SAVE_SPE (1 << 12) /* Save SPE context if active */
|
||||
#define KVM_ARM64_DEBUG_STATE_SAVE_TRBE (1 << 13) /* Save TRBE context if active */
|
||||
#define KVM_ARM64_FP_FOREIGN_FPSTATE (1 << 14)
|
||||
#define KVM_ARM64_PKVM_STATE_DIRTY (1 << 15)
|
||||
|
||||
#define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | \
|
||||
KVM_GUESTDBG_USE_SW_BP | \
|
||||
|
||||
@@ -475,6 +475,28 @@ static void sync_timer_state(struct pkvm_loaded_state *state)
|
||||
__vcpu_sys_reg(shadow_vcpu, CNTV_CTL_EL0) = read_sysreg_el0(SYS_CNTV_CTL);
|
||||
}
|
||||
|
||||
static void __sync_vcpu_state(struct kvm_vcpu *from_vcpu,
|
||||
struct kvm_vcpu *to_vcpu)
|
||||
{
|
||||
int i;
|
||||
|
||||
to_vcpu->arch.ctxt.regs = from_vcpu->arch.ctxt.regs;
|
||||
to_vcpu->arch.ctxt.spsr_abt = from_vcpu->arch.ctxt.spsr_abt;
|
||||
to_vcpu->arch.ctxt.spsr_und = from_vcpu->arch.ctxt.spsr_und;
|
||||
to_vcpu->arch.ctxt.spsr_irq = from_vcpu->arch.ctxt.spsr_irq;
|
||||
to_vcpu->arch.ctxt.spsr_fiq = from_vcpu->arch.ctxt.spsr_fiq;
|
||||
|
||||
/*
|
||||
* Copy the sysregs, but don't mess with the timer state which
|
||||
* is directly handled by EL1 and is expected to be preserved.
|
||||
*/
|
||||
for (i = 1; i < NR_SYS_REGS; i++) {
|
||||
if (i >= CNTVOFF_EL2 && i <= CNTP_CTL_EL0)
|
||||
continue;
|
||||
to_vcpu->arch.ctxt.sys_regs[i] = from_vcpu->arch.ctxt.sys_regs[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void flush_shadow_state(struct pkvm_loaded_state *state)
|
||||
{
|
||||
struct kvm_vcpu *shadow_vcpu = state->vcpu;
|
||||
@@ -485,6 +507,19 @@ static void flush_shadow_state(struct pkvm_loaded_state *state)
|
||||
if (READ_ONCE(shadow_vcpu->arch.pkvm.power_state) == PSCI_0_2_AFFINITY_LEVEL_ON_PENDING)
|
||||
pkvm_reset_vcpu(shadow_vcpu);
|
||||
|
||||
/*
|
||||
* If we deal with a non-protected guest and that the state is
|
||||
* dirty (from a host perspective), copy the state back into
|
||||
* the shadow.
|
||||
*/
|
||||
if (!state->is_protected) {
|
||||
if (READ_ONCE(host_vcpu->arch.flags) & KVM_ARM64_PKVM_STATE_DIRTY)
|
||||
__sync_vcpu_state(host_vcpu, shadow_vcpu);
|
||||
|
||||
state->vcpu->arch.hcr_el2 = HCR_GUEST_FLAGS & ~(HCR_RW | HCR_TWI | HCR_TWE);
|
||||
state->vcpu->arch.hcr_el2 |= host_vcpu->arch.hcr_el2;
|
||||
}
|
||||
|
||||
flush_vgic_state(host_vcpu, shadow_vcpu);
|
||||
flush_timer_state(state);
|
||||
|
||||
@@ -518,6 +553,10 @@ static void sync_shadow_state(struct pkvm_loaded_state *state, u32 exit_reason)
|
||||
u8 esr_ec;
|
||||
shadow_entry_exit_handler_fn ec_handler;
|
||||
|
||||
/*
|
||||
* Don't sync the vcpu GPR/sysreg state after a run. Instead,
|
||||
* leave it in the shadow until someone actually requires it.
|
||||
*/
|
||||
sync_vgic_state(host_vcpu, shadow_vcpu);
|
||||
sync_timer_state(state);
|
||||
|
||||
@@ -616,6 +655,10 @@ static void handle___pkvm_vcpu_put(struct kvm_cpu_context *host_ctxt)
|
||||
if (state->vcpu->arch.flags & KVM_ARM64_FP_ENABLED)
|
||||
fpsimd_host_restore();
|
||||
|
||||
if (!state->is_protected &&
|
||||
!(READ_ONCE(vcpu->arch.flags) & KVM_ARM64_PKVM_STATE_DIRTY))
|
||||
__sync_vcpu_state(state->vcpu, vcpu);
|
||||
|
||||
put_shadow_vcpu(state->vcpu);
|
||||
|
||||
/* "It's over and done with..." */
|
||||
@@ -624,6 +667,23 @@ static void handle___pkvm_vcpu_put(struct kvm_cpu_context *host_ctxt)
|
||||
}
|
||||
}
|
||||
|
||||
static void handle___pkvm_vcpu_sync_state(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(struct kvm_vcpu *, vcpu, host_ctxt, 1);
|
||||
|
||||
if (unlikely(is_protected_kvm_enabled())) {
|
||||
struct pkvm_loaded_state *state = this_cpu_ptr(&loaded_state);
|
||||
|
||||
vcpu = kern_hyp_va(vcpu);
|
||||
|
||||
if (!state->vcpu || state->is_protected ||
|
||||
state->vcpu->arch.pkvm.host_vcpu != vcpu)
|
||||
return;
|
||||
|
||||
__sync_vcpu_state(state->vcpu, vcpu);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(struct kvm_vcpu *, vcpu, host_ctxt, 1);
|
||||
@@ -932,6 +992,7 @@ static const hcall_t host_hcall[] = {
|
||||
HANDLE_FUNC(__pkvm_teardown_shadow),
|
||||
HANDLE_FUNC(__pkvm_vcpu_load),
|
||||
HANDLE_FUNC(__pkvm_vcpu_put),
|
||||
HANDLE_FUNC(__pkvm_vcpu_sync_state),
|
||||
};
|
||||
|
||||
static void handle_host_hcall(struct kvm_cpu_context *host_ctxt)
|
||||
|
||||
Reference in New Issue
Block a user