Skip to content

Commit 256b466

Browse files
Fuad TabbaMarc Zyngier
authored andcommitted
KVM: arm64: Introduce separate hypercalls for pKVM VM reservation and initialization
The existing __pkvm_init_vm hypercall performs both the reservation of a VM table entry and the initialization of the hypervisor VM state in a single operation. This design prevents the host from obtaining a VM handle from the hypervisor until all preparation for the creation and the initialization of the VM is done, which is on the first vCPU run operation. To support more flexible VM lifecycle management, the host needs the ability to reserve a handle early, before the first vCPU run. Refactor the hypercall interface to enable this, splitting the single hypercall into a two-stage process: - __pkvm_reserve_vm: A new hypercall that allocates a slot in the hypervisor's vm_table, marks it as reserved, and returns a unique handle to the host. - __pkvm_unreserve_vm: A corresponding cleanup hypercall to safely release the reservation if the host fails to proceed with full initialization. - __pkvm_init_vm: The existing hypercall is modified to no longer allocate a slot. It now expects a pre-reserved handle and commits the donated VM memory to that slot. For now, the host-side code in __pkvm_create_hyp_vm calls the new reserve and init hypercalls back-to-back to maintain existing behavior. This paves the way for subsequent patches to separate the reservation and initialization steps in the VM's lifecycle. Signed-off-by: Fuad Tabba <tabba@google.com> Tested-by: Mark Brown <broonie@kernel.org> Signed-off-by: Marc Zyngier <maz@kernel.org>
1 parent 814fd6b commit 256b466

5 files changed

Lines changed: 108 additions & 24 deletions

File tree

arch/arm64/include/asm/kvm_asm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ enum __kvm_host_smccc_func {
8181
__KVM_HOST_SMCCC_FUNC___kvm_timer_set_cntvoff,
8282
__KVM_HOST_SMCCC_FUNC___vgic_v3_save_vmcr_aprs,
8383
__KVM_HOST_SMCCC_FUNC___vgic_v3_restore_vmcr_aprs,
84+
__KVM_HOST_SMCCC_FUNC___pkvm_reserve_vm,
85+
__KVM_HOST_SMCCC_FUNC___pkvm_unreserve_vm,
8486
__KVM_HOST_SMCCC_FUNC___pkvm_init_vm,
8587
__KVM_HOST_SMCCC_FUNC___pkvm_init_vcpu,
8688
__KVM_HOST_SMCCC_FUNC___pkvm_teardown_vm,

arch/arm64/kvm/hyp/include/nvhe/pkvm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ static inline bool pkvm_hyp_vm_is_protected(struct pkvm_hyp_vm *hyp_vm)
6767

6868
void pkvm_hyp_vm_table_init(void *tbl);
6969

70+
int __pkvm_reserve_vm(void);
71+
void __pkvm_unreserve_vm(pkvm_handle_t handle);
7072
int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
7173
unsigned long pgd_hva);
7274
int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,

arch/arm64/kvm/hyp/nvhe/hyp-main.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,18 @@ static void handle___pkvm_prot_finalize(struct kvm_cpu_context *host_ctxt)
546546
cpu_reg(host_ctxt, 1) = __pkvm_prot_finalize();
547547
}
548548

549+
static void handle___pkvm_reserve_vm(struct kvm_cpu_context *host_ctxt)
550+
{
551+
cpu_reg(host_ctxt, 1) = __pkvm_reserve_vm();
552+
}
553+
554+
static void handle___pkvm_unreserve_vm(struct kvm_cpu_context *host_ctxt)
555+
{
556+
DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
557+
558+
__pkvm_unreserve_vm(handle);
559+
}
560+
549561
static void handle___pkvm_init_vm(struct kvm_cpu_context *host_ctxt)
550562
{
551563
DECLARE_REG(struct kvm *, host_kvm, host_ctxt, 1);
@@ -606,6 +618,8 @@ static const hcall_t host_hcall[] = {
606618
HANDLE_FUNC(__kvm_timer_set_cntvoff),
607619
HANDLE_FUNC(__vgic_v3_save_vmcr_aprs),
608620
HANDLE_FUNC(__vgic_v3_restore_vmcr_aprs),
621+
HANDLE_FUNC(__pkvm_reserve_vm),
622+
HANDLE_FUNC(__pkvm_unreserve_vm),
609623
HANDLE_FUNC(__pkvm_init_vm),
610624
HANDLE_FUNC(__pkvm_init_vcpu),
611625
HANDLE_FUNC(__pkvm_teardown_vm),

arch/arm64/kvm/hyp/nvhe/pkvm.c

Lines changed: 80 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,33 @@ static int allocate_vm_table_entry(void)
542542
return idx;
543543
}
544544

545+
static int __insert_vm_table_entry(pkvm_handle_t handle,
546+
struct pkvm_hyp_vm *hyp_vm)
547+
{
548+
unsigned int idx;
549+
550+
hyp_assert_lock_held(&vm_table_lock);
551+
552+
/*
553+
* Initializing protected state might have failed, yet a malicious
554+
* host could trigger this function. Thus, ensure that 'vm_table'
555+
* exists.
556+
*/
557+
if (unlikely(!vm_table))
558+
return -EINVAL;
559+
560+
idx = vm_handle_to_idx(handle);
561+
if (unlikely(idx >= KVM_MAX_PVMS))
562+
return -EINVAL;
563+
564+
if (unlikely(vm_table[idx] != RESERVED_ENTRY))
565+
return -EINVAL;
566+
567+
vm_table[idx] = hyp_vm;
568+
569+
return 0;
570+
}
571+
545572
/*
546573
* Insert a pointer to the initialized VM into the VM table.
547574
*
@@ -550,13 +577,13 @@ static int allocate_vm_table_entry(void)
550577
static int insert_vm_table_entry(pkvm_handle_t handle,
551578
struct pkvm_hyp_vm *hyp_vm)
552579
{
553-
unsigned int idx;
580+
int ret;
554581

555-
hyp_assert_lock_held(&vm_table_lock);
556-
idx = vm_handle_to_idx(handle);
557-
vm_table[idx] = hyp_vm;
582+
hyp_spin_lock(&vm_table_lock);
583+
ret = __insert_vm_table_entry(handle, hyp_vm);
584+
hyp_spin_unlock(&vm_table_lock);
558585

559-
return 0;
586+
return ret;
560587
}
561588

562589
/*
@@ -622,8 +649,45 @@ static void unmap_donated_memory_noclear(void *va, size_t size)
622649
__unmap_donated_memory(va, size);
623650
}
624651

652+
/*
653+
* Reserves an entry in the hypervisor for a new VM in protected mode.
654+
*
655+
* Return a unique handle to the VM on success, negative error code on failure.
656+
*/
657+
int __pkvm_reserve_vm(void)
658+
{
659+
int ret;
660+
661+
hyp_spin_lock(&vm_table_lock);
662+
ret = allocate_vm_table_entry();
663+
hyp_spin_unlock(&vm_table_lock);
664+
665+
if (ret < 0)
666+
return ret;
667+
668+
return idx_to_vm_handle(ret);
669+
}
670+
671+
/*
672+
* Removes a reserved entry, but only if is hasn't been used yet.
673+
* Otherwise, the VM needs to be destroyed.
674+
*/
675+
void __pkvm_unreserve_vm(pkvm_handle_t handle)
676+
{
677+
unsigned int idx = vm_handle_to_idx(handle);
678+
679+
if (unlikely(!vm_table))
680+
return;
681+
682+
hyp_spin_lock(&vm_table_lock);
683+
if (likely(idx < KVM_MAX_PVMS && vm_table[idx] == RESERVED_ENTRY))
684+
remove_vm_table_entry(handle);
685+
hyp_spin_unlock(&vm_table_lock);
686+
}
687+
625688
/*
626689
* Initialize the hypervisor copy of the VM state using host-donated memory.
690+
*
627691
* Unmap the donated memory from the host at stage 2.
628692
*
629693
* host_kvm: A pointer to the host's struct kvm.
@@ -633,8 +697,7 @@ static void unmap_donated_memory_noclear(void *va, size_t size)
633697
* the VM. Must be page aligned. Its size is implied by the VM's
634698
* VTCR.
635699
*
636-
* Return a unique handle to the VM on success,
637-
* negative error code on failure.
700+
* Return 0 success, negative error code on failure.
638701
*/
639702
int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
640703
unsigned long pgd_hva)
@@ -656,6 +719,12 @@ int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
656719
goto err_unpin_kvm;
657720
}
658721

722+
handle = READ_ONCE(host_kvm->arch.pkvm.handle);
723+
if (unlikely(handle < HANDLE_OFFSET)) {
724+
ret = -EINVAL;
725+
goto err_unpin_kvm;
726+
}
727+
659728
vm_size = pkvm_get_hyp_vm_size(nr_vcpus);
660729
pgd_size = kvm_pgtable_stage2_pgd_size(host_mmu.arch.mmu.vtcr);
661730

@@ -669,30 +738,19 @@ int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
669738
if (!pgd)
670739
goto err_remove_mappings;
671740

672-
hyp_spin_lock(&vm_table_lock);
673-
ret = allocate_vm_table_entry();
674-
if (ret < 0)
675-
goto err_unlock;
676-
677-
handle = idx_to_vm_handle(ret);
678-
679741
init_pkvm_hyp_vm(host_kvm, hyp_vm, nr_vcpus, handle);
680742

681743
ret = kvm_guest_prepare_stage2(hyp_vm, pgd);
682744
if (ret)
683-
goto err_remove_vm_table_entry;
745+
goto err_remove_mappings;
684746

747+
/* Must be called last since this publishes the VM. */
685748
ret = insert_vm_table_entry(handle, hyp_vm);
686749
if (ret)
687-
goto err_remove_vm_table_entry;
688-
hyp_spin_unlock(&vm_table_lock);
750+
goto err_remove_mappings;
689751

690-
return handle;
752+
return 0;
691753

692-
err_remove_vm_table_entry:
693-
remove_vm_table_entry(handle);
694-
err_unlock:
695-
hyp_spin_unlock(&vm_table_lock);
696754
err_remove_mappings:
697755
unmap_donated_memory(hyp_vm, vm_size);
698756
unmap_donated_memory(pgd, pgd_size);

arch/arm64/kvm/pkvm.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,17 +160,25 @@ static int __pkvm_create_hyp_vm(struct kvm *kvm)
160160
goto free_pgd;
161161
}
162162

163-
/* Donate the VM memory to hyp and let hyp initialize it. */
164-
ret = kvm_call_hyp_nvhe(__pkvm_init_vm, kvm, hyp_vm, pgd);
163+
/* Reserve the VM in hyp and obtain a hyp handle for the VM. */
164+
ret = kvm_call_hyp_nvhe(__pkvm_reserve_vm);
165165
if (ret < 0)
166166
goto free_vm;
167167

168168
kvm->arch.pkvm.handle = ret;
169+
170+
/* Donate the VM memory to hyp and let hyp initialize it. */
171+
ret = kvm_call_hyp_nvhe(__pkvm_init_vm, kvm, hyp_vm, pgd);
172+
if (ret)
173+
goto unreserve_vm;
174+
169175
kvm->arch.pkvm.is_created = true;
170176
kvm->arch.pkvm.stage2_teardown_mc.flags |= HYP_MEMCACHE_ACCOUNT_STAGE2;
171177
kvm_account_pgtable_pages(pgd, pgd_sz / PAGE_SIZE);
172178

173179
return 0;
180+
unreserve_vm:
181+
kvm_call_hyp_nvhe(__pkvm_unreserve_vm, kvm->arch.pkvm.handle);
174182
free_vm:
175183
free_pages_exact(hyp_vm, hyp_vm_sz);
176184
free_pgd:

0 commit comments

Comments
 (0)