mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 18:41:58 +09:00
ANDROID: KVM: arm64: Introduce lazy-ish state sync for non-protected VMs
Rather than blindly copying the register state between the hyp and host vCPU structures, abstract this code into some helpers which are called only for non-protected VMs running under pKVM. To faciliate host access to guest registers within a get/put sequence, introduce a new 'sync_state' hypercall to provide access to the registers of a non-protected VM when handling traps. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: Will Deacon <willdeacon@google.com> Bug: 233587962 Change-Id: I5b0d874d2d2184c4da95a91c0b9b57af500cbce3
This commit is contained in:
committed by
Will Deacon
parent
2edbb246c2
commit
ec5f16f968
@@ -81,6 +81,7 @@ enum __kvm_host_smccc_func {
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_teardown_vm,
|
||||
__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[]
|
||||
|
||||
@@ -627,6 +627,8 @@ struct kvm_vcpu_arch {
|
||||
#define DEBUG_STATE_SAVE_SPE __vcpu_single_flag(iflags, BIT(5))
|
||||
/* Save TRBE context if active */
|
||||
#define DEBUG_STATE_SAVE_TRBE __vcpu_single_flag(iflags, BIT(6))
|
||||
/* pKVM host vcpu state is dirty, needs resync */
|
||||
#define PKVM_HOST_STATE_DIRTY __vcpu_single_flag(iflags, BIT(7))
|
||||
|
||||
/* SVE enabled for host EL0 */
|
||||
#define HOST_SVE_ENABLED __vcpu_single_flag(sflags, BIT(0))
|
||||
|
||||
@@ -463,6 +463,10 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
|
||||
kvm_call_hyp(__vgic_v3_save_vmcr_aprs,
|
||||
&vcpu->arch.vgic_cpu.vgic_v3);
|
||||
kvm_call_hyp_nvhe(__pkvm_vcpu_put);
|
||||
|
||||
/* __pkvm_vcpu_put implies a sync of the state */
|
||||
if (!kvm_vm_is_protected(vcpu->kvm))
|
||||
vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);
|
||||
}
|
||||
|
||||
kvm_arch_vcpu_put_debug_state_flags(vcpu);
|
||||
@@ -605,6 +609,9 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
|
||||
return ret;
|
||||
|
||||
if (is_protected_kvm_enabled()) {
|
||||
/* Start with the vcpu in a dirty state */
|
||||
if (!kvm_vm_is_protected(vcpu->kvm))
|
||||
vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);
|
||||
ret = pkvm_create_hyp_vm(kvm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -235,6 +235,21 @@ static int handle_trap_exceptions(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int handled;
|
||||
|
||||
/*
|
||||
* If we run a non-protected VM when protection is enabled
|
||||
* system-wide, resync the state from the hypervisor and mark
|
||||
* it as dirty on the host side if it wasn't dirty already
|
||||
* (which could happen if preemption has taken place).
|
||||
*/
|
||||
if (is_protected_kvm_enabled() && !kvm_vm_is_protected(vcpu->kvm)) {
|
||||
preempt_disable();
|
||||
if (!(vcpu_get_flag(vcpu, PKVM_HOST_STATE_DIRTY))) {
|
||||
kvm_call_hyp_nvhe(__pkvm_vcpu_sync_state);
|
||||
vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);
|
||||
}
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
/*
|
||||
* See ARM ARM B1.14.1: "Hyp traps on instructions
|
||||
* that fail their condition code check"
|
||||
@@ -302,6 +317,13 @@ int handle_exit(struct kvm_vcpu *vcpu, int exception_index)
|
||||
/* For exit types that need handling before we can be preempted */
|
||||
void handle_exit_early(struct kvm_vcpu *vcpu, int exception_index)
|
||||
{
|
||||
/*
|
||||
* We just exited, so the state is clean from a hypervisor
|
||||
* perspective.
|
||||
*/
|
||||
if (is_protected_kvm_enabled())
|
||||
vcpu_clear_flag(vcpu, PKVM_HOST_STATE_DIRTY);
|
||||
|
||||
if (ARM_SERROR_PENDING(exception_index)) {
|
||||
if (this_cpu_has_cap(ARM64_HAS_RAS_EXTN)) {
|
||||
u64 disr = kvm_vcpu_get_disr(vcpu);
|
||||
|
||||
@@ -132,13 +132,53 @@ static void sync_hyp_timer_state(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
read_sysreg_el0(SYS_CNTV_CTL);
|
||||
}
|
||||
|
||||
static void __copy_vcpu_state(const 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 __sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
{
|
||||
__copy_vcpu_state(&hyp_vcpu->vcpu, hyp_vcpu->host_vcpu);
|
||||
}
|
||||
|
||||
static void __flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
{
|
||||
__copy_vcpu_state(hyp_vcpu->host_vcpu, &hyp_vcpu->vcpu);
|
||||
}
|
||||
|
||||
static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
{
|
||||
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
|
||||
hyp_entry_exit_handler_fn ec_handler;
|
||||
u8 esr_ec;
|
||||
|
||||
hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt;
|
||||
/*
|
||||
* If we deal with a non-protected guest and the state is potentially
|
||||
* dirty (from a host perspective), copy the state back into the hyp
|
||||
* vcpu.
|
||||
*/
|
||||
if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu) &&
|
||||
vcpu_get_flag(host_vcpu, PKVM_HOST_STATE_DIRTY)) {
|
||||
__flush_hyp_vcpu(hyp_vcpu);
|
||||
}
|
||||
|
||||
hyp_vcpu->vcpu.arch.sve_state = kern_hyp_va(host_vcpu->arch.sve_state);
|
||||
hyp_vcpu->vcpu.arch.sve_max_vl = host_vcpu->arch.sve_max_vl;
|
||||
@@ -253,8 +293,30 @@ static void handle___pkvm_vcpu_put(struct kvm_cpu_context *host_ctxt)
|
||||
return;
|
||||
|
||||
hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
|
||||
if (hyp_vcpu)
|
||||
if (hyp_vcpu) {
|
||||
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
|
||||
|
||||
if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu) &&
|
||||
!vcpu_get_flag(host_vcpu, PKVM_HOST_STATE_DIRTY)) {
|
||||
__sync_hyp_vcpu(hyp_vcpu);
|
||||
}
|
||||
|
||||
pkvm_put_hyp_vcpu(hyp_vcpu);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle___pkvm_vcpu_sync_state(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
struct pkvm_hyp_vcpu *hyp_vcpu;
|
||||
|
||||
if (!is_protected_kvm_enabled())
|
||||
return;
|
||||
|
||||
hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
|
||||
if (!hyp_vcpu || pkvm_hyp_vcpu_is_protected(hyp_vcpu))
|
||||
return;
|
||||
|
||||
__sync_hyp_vcpu(hyp_vcpu);
|
||||
}
|
||||
|
||||
static struct kvm_vcpu *__get_host_hyp_vcpus(struct kvm_vcpu *arg,
|
||||
@@ -578,6 +640,7 @@ static const hcall_t host_hcall[] = {
|
||||
HANDLE_FUNC(__pkvm_teardown_vm),
|
||||
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