BACKPORT: FROMLIST: KVM: arm64: Create each pKVM hyp vcpu after its corresponding host vcpu

Instead of creating and initializing _all_ hyp vcpus in pKVM when
the first host vcpu runs for the first time, initialize _each_
hyp vcpu in conjunction with its corresponding host vcpu.

Some of the host vcpu state (e.g., system registers and traps
values) are not initialized until the first time the host vcpu is
run. Therefore, initializing a hyp vcpu before its corresponding
host vcpu has run for the first time might not view the complete
host state of these vcpus.

Additionally, this behavior is inline with non-protected modes.

Bug: 394097954
Bug: 357781595
Link: https://lore.kernel.org/all/20250214150258.464798-4-tabba@google.com/
[tabba@: minor conflicts and added checks for hyp_vcpu in code
 not upstream yet]
Change-Id: I4c2b4a9f1272e70e1c81db41345a6950ee6e911a
Signed-off-by: Fuad Tabba <tabba@google.com>
This commit is contained in:
Fuad Tabba
2025-02-13 10:07:03 +00:00
committed by Will Deacon
parent 579220d751
commit 9bcaa7a08d
6 changed files with 60 additions and 42 deletions
+2
View File
@@ -973,6 +973,8 @@ struct kvm_vcpu_arch {
#define VCPU_INITIALIZED __vcpu_single_flag(cflags, BIT(0))
/* SVE config completed */
#define VCPU_SVE_FINALIZED __vcpu_single_flag(cflags, BIT(1))
/* pKVM VCPU setup completed */
#define VCPU_PKVM_FINALIZED __vcpu_single_flag(cflags, BIT(2))
/* Exception pending */
#define PENDING_EXCEPTION __vcpu_single_flag(iflags, BIT(0))
+1
View File
@@ -24,6 +24,7 @@ int pkvm_init_host_vm(struct kvm *kvm, unsigned long type);
int pkvm_create_hyp_vm(struct kvm *kvm);
void pkvm_destroy_hyp_vm(struct kvm *kvm);
bool pkvm_is_hyp_created(struct kvm *kvm);
int pkvm_create_hyp_vcpu(struct kvm_vcpu *vcpu);
void pkvm_host_reclaim_page(struct kvm *host_kvm, phys_addr_t ipa);
/*
+5
View File
@@ -871,9 +871,14 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
/* 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;
ret = pkvm_create_hyp_vcpu(vcpu);
if (ret)
return ret;
}
mutex_lock(&kvm->arch.config_lock);
-4
View File
@@ -61,10 +61,6 @@ struct pkvm_hyp_vm {
unsigned short refcount;
/*
* The number of vcpus initialized and ready to run.
*/
unsigned int nr_vcpus;
hyp_spinlock_t vcpus_lock;
/*
+28 -17
View File
@@ -386,10 +386,12 @@ struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,
hyp_read_lock(&vm_table_lock);
hyp_vm = get_vm_by_handle(handle);
if (!hyp_vm || hyp_vm->is_dying || READ_ONCE(hyp_vm->nr_vcpus) <= vcpu_idx)
if (!hyp_vm || hyp_vm->is_dying || hyp_vm->kvm.created_vcpus <= vcpu_idx)
goto unlock;
hyp_vcpu = hyp_vm->vcpus[vcpu_idx];
if (!hyp_vcpu)
goto unlock;
/* Ensure vcpu isn't loaded on more than one cpu simultaneously. */
if (unlikely(cmpxchg_relaxed(&hyp_vcpu->loaded_hyp_vcpu, NULL,
@@ -553,6 +555,9 @@ static void unpin_host_vcpus(struct pkvm_hyp_vcpu *hyp_vcpus[],
for (i = 0; i < nr_vcpus; i++) {
struct pkvm_hyp_vcpu *hyp_vcpu = hyp_vcpus[i];
if (!hyp_vcpu)
continue;
unpin_host_vcpu(hyp_vcpu);
if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu))
@@ -631,8 +636,7 @@ static int pkvm_vcpu_init_sve(struct pkvm_hyp_vcpu *hyp_vcpu, struct kvm_vcpu *h
static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
struct pkvm_hyp_vm *hyp_vm,
struct kvm_vcpu *host_vcpu,
unsigned int vcpu_idx)
struct kvm_vcpu *host_vcpu)
{
int ret = 0;
u32 mp_state;
@@ -647,11 +651,6 @@ static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
return -EBUSY;
}
if (host_vcpu->vcpu_idx != vcpu_idx) {
ret = -EINVAL;
goto done;
}
mp_state = READ_ONCE(host_vcpu->arch.mp_state.mp_state);
if (mp_state != KVM_MP_STATE_RUNNABLE && mp_state != KVM_MP_STATE_STOPPED) {
ret = -EINVAL;
@@ -662,7 +661,7 @@ static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
hyp_vcpu->vcpu.kvm = &hyp_vm->kvm;
hyp_vcpu->vcpu.vcpu_id = READ_ONCE(host_vcpu->vcpu_id);
hyp_vcpu->vcpu.vcpu_idx = vcpu_idx;
hyp_vcpu->vcpu.vcpu_idx = READ_ONCE(host_vcpu->vcpu_idx);
hyp_vcpu->vcpu.arch.hw_mmu = &hyp_vm->kvm.arch.mmu;
hyp_vcpu->vcpu.arch.cflags = READ_ONCE(host_vcpu->arch.cflags);
@@ -867,18 +866,22 @@ int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu)
}
hyp_spin_lock(&hyp_vm->vcpus_lock);
idx = hyp_vm->nr_vcpus;
ret = init_pkvm_hyp_vcpu(hyp_vcpu, hyp_vm, host_vcpu);
if (ret)
goto unlock_vcpus;
idx = hyp_vcpu->vcpu.vcpu_idx;
if (idx >= hyp_vm->kvm.created_vcpus) {
ret = -EINVAL;
goto unlock_vcpus;
}
ret = init_pkvm_hyp_vcpu(hyp_vcpu, hyp_vm, host_vcpu, idx);
if (ret)
if (hyp_vm->vcpus[idx]) {
ret = -EINVAL;
goto unlock_vcpus;
}
hyp_vm->vcpus[idx] = hyp_vcpu;
hyp_vm->nr_vcpus++;
unlock_vcpus:
hyp_spin_unlock(&hyp_vm->vcpus_lock);
@@ -954,14 +957,17 @@ int __pkvm_finalize_teardown_vm(pkvm_handle_t handle)
mc = &host_kvm->arch.pkvm.stage2_teardown_mc;
destroy_hyp_vm_pgt(hyp_vm);
drain_hyp_pool(hyp_vm, mc);
unpin_host_vcpus(hyp_vm->vcpus, hyp_vm->nr_vcpus);
unpin_host_vcpus(hyp_vm->vcpus, hyp_vm->kvm.created_vcpus);
/* Push the metadata pages to the teardown memcache */
for (idx = 0; idx < hyp_vm->nr_vcpus; ++idx) {
for (idx = 0; idx < hyp_vm->kvm.created_vcpus; ++idx) {
struct pkvm_hyp_vcpu *hyp_vcpu = hyp_vm->vcpus[idx];
struct kvm_hyp_memcache *vcpu_mc;
void *addr;
if (!hyp_vcpu)
continue;
vcpu_mc = &hyp_vcpu->vcpu.arch.stage2_mc;
while (vcpu_mc->nr_pages) {
unsigned long order;
@@ -1120,8 +1126,10 @@ struct pkvm_hyp_vcpu *pkvm_mpidr_to_hyp_vcpu(struct pkvm_hyp_vm *hyp_vm,
mpidr &= MPIDR_HWID_BITMASK;
hyp_spin_lock(&hyp_vm->vcpus_lock);
for (i = 0; i < hyp_vm->nr_vcpus; i++) {
for (i = 0; i < hyp_vm->kvm.created_vcpus; i++) {
hyp_vcpu = hyp_vm->vcpus[i];
if (!hyp_vcpu)
continue;
if (mpidr == kvm_vcpu_get_mpidr_aff(&hyp_vcpu->vcpu))
goto unlock;
@@ -1229,9 +1237,12 @@ static bool pvm_psci_vcpu_affinity_info(struct pkvm_hyp_vcpu *hyp_vcpu)
* Otherwise, return OFF.
*/
hyp_spin_lock(&hyp_vm->vcpus_lock);
for (i = 0; i < hyp_vm->nr_vcpus; i++) {
for (i = 0; i < hyp_vm->kvm.created_vcpus; i++) {
struct pkvm_hyp_vcpu *target = hyp_vm->vcpus[i];
if (!target)
continue;
mpidr = kvm_vcpu_get_mpidr_aff(&target->vcpu);
if ((mpidr & target_affinity_mask) == target_affinity) {
+24 -21
View File
@@ -244,6 +244,15 @@ void __init kvm_hyp_reserve(void)
hyp_mem_base);
}
static void __pkvm_vcpu_hyp_created(struct kvm_vcpu *vcpu)
{
if (kvm_vm_is_protected(vcpu->kvm))
vcpu->arch.sve_state = NULL;
vcpu_set_flag(vcpu, VCPU_PKVM_FINALIZED);
}
static int __pkvm_create_hyp_vcpu(struct kvm_vcpu *host_vcpu)
{
pkvm_handle_t handle = host_vcpu->kvm->arch.pkvm.handle;
@@ -262,8 +271,10 @@ static int __pkvm_create_hyp_vcpu(struct kvm_vcpu *host_vcpu)
host_vcpu->arch.hyp_reqs = hyp_reqs;
ret = kvm_call_refill_hyp_nvhe(__pkvm_init_vcpu, handle, host_vcpu);
if (!ret)
if (!ret) {
__pkvm_vcpu_hyp_created(host_vcpu);
return 0;
}
kvm_unshare_hyp(hyp_reqs, hyp_reqs + 1);
err_free_reqs:
@@ -400,12 +411,6 @@ out_free:
}
}
static void __pkvm_vcpu_hyp_created(struct kvm_vcpu *vcpu)
{
if (kvm_vm_is_protected(vcpu->kvm))
vcpu->arch.sve_state = NULL;
}
/*
* Allocates and donates memory for hypervisor VM structs at EL2.
*
@@ -418,8 +423,6 @@ static void __pkvm_vcpu_hyp_created(struct kvm_vcpu *vcpu)
*/
static int __pkvm_create_hyp_vm(struct kvm *host_kvm)
{
struct kvm_vcpu *host_vcpu;
unsigned long idx;
size_t pgd_sz;
void *pgd;
int ret;
@@ -448,21 +451,9 @@ static int __pkvm_create_hyp_vm(struct kvm *host_kvm)
WRITE_ONCE(host_kvm->arch.pkvm.handle, ret);
/* Donate memory for the vcpus at hyp and initialize it. */
kvm_for_each_vcpu(idx, host_vcpu, host_kvm) {
ret = __pkvm_create_hyp_vcpu(host_vcpu);
if (ret)
goto destroy_vm;
__pkvm_vcpu_hyp_created(host_vcpu);
}
kvm_account_pgtable_pages(pgd, pgd_sz >> PAGE_SHIFT);
return 0;
destroy_vm:
__pkvm_destroy_hyp_vm(host_kvm);
return ret;
free_pgd:
free_pages_exact(pgd, pgd_sz);
atomic64_sub(pgd_sz, &host_kvm->stat.protected_hyp_mem);
@@ -487,6 +478,18 @@ int pkvm_create_hyp_vm(struct kvm *host_kvm)
return ret;
}
int pkvm_create_hyp_vcpu(struct kvm_vcpu *vcpu)
{
int ret = 0;
mutex_lock(&vcpu->kvm->arch.config_lock);
if (!vcpu_get_flag(vcpu, VCPU_PKVM_FINALIZED))
ret = __pkvm_create_hyp_vcpu(vcpu);
mutex_unlock(&vcpu->kvm->arch.config_lock);
return ret;
}
void pkvm_destroy_hyp_vm(struct kvm *host_kvm)
{
mutex_lock(&host_kvm->arch.config_lock);