ANDROID: KVM: arm64: Copy pvmfw into guest pages during donation from the host

When the host donates a page to a protected guest at an IPA which
coincides with the PVM firmware load address, copy-in the relevant
firmware page after unmapping it from the host but before mapping it
into the guest.

Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Will Deacon <willdeacon@google.com>
Bug: 254819795
Change-Id: I8cec813fa52938945f3122655deb785523a96ec8
Signed-off-by: Quentin Perret <qperret@google.com>
This commit is contained in:
Will Deacon
2021-12-09 11:59:38 +00:00
committed by Quentin Perret
parent 5ae1450bd0
commit fffed9c143
5 changed files with 75 additions and 2 deletions

View File

@@ -183,6 +183,7 @@ struct kvm_protected_vm {
pkvm_handle_t handle;
struct kvm_hyp_memcache teardown_mc;
struct list_head pinned_pages;
gpa_t pvmfw_load_addr;
bool enabled;
};

View File

@@ -15,6 +15,7 @@
#define KVM_MAX_PVMS 255
#define HYP_MEMBLOCK_REGIONS 128
#define PVMFW_INVALID_LOAD_ADDR (-1)
int pkvm_init_host_vm(struct kvm *kvm, unsigned long type);
int pkvm_create_hyp_vm(struct kvm *kvm);

View File

@@ -84,6 +84,9 @@ static inline bool pkvm_hyp_vcpu_is_protected(struct pkvm_hyp_vcpu *hyp_vcpu)
return vcpu_is_protected(&hyp_vcpu->vcpu);
}
extern phys_addr_t pvmfw_base;
extern phys_addr_t pvmfw_size;
void pkvm_hyp_vm_table_init(void *tbl);
int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
@@ -109,6 +112,23 @@ bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code);
struct pkvm_hyp_vcpu *pkvm_mpidr_to_hyp_vcpu(struct pkvm_hyp_vm *vm, u64 mpidr);
static inline bool pkvm_hyp_vm_has_pvmfw(struct pkvm_hyp_vm *vm)
{
return vm->kvm.arch.pkvm.pvmfw_load_addr != PVMFW_INVALID_LOAD_ADDR;
}
static inline bool pkvm_ipa_in_pvmfw_region(struct pkvm_hyp_vm *vm, u64 ipa)
{
struct kvm_protected_vm *pkvm = &vm->kvm.arch.pkvm;
if (!pkvm_hyp_vm_has_pvmfw(vm))
return false;
return ipa - pkvm->pvmfw_load_addr < pvmfw_size;
}
int pkvm_load_pvmfw_pages(struct pkvm_hyp_vm *vm, u64 ipa, phys_addr_t phys,
u64 size);
void pkvm_clear_pvmfw_pages(void);
#endif /* __ARM64_KVM_NVHE_PKVM_H__ */

View File

@@ -993,10 +993,22 @@ static int guest_complete_donation(u64 addr, const struct pkvm_mem_transition *t
enum kvm_pgtable_prot prot = pkvm_mkstate(KVM_PGTABLE_PROT_RWX, PKVM_PAGE_OWNED);
struct pkvm_hyp_vcpu *vcpu = tx->completer.guest.hyp_vcpu;
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
phys_addr_t phys = tx->completer.guest.phys;
u64 size = tx->nr_pages * PAGE_SIZE;
int err;
return kvm_pgtable_stage2_map(&vm->pgt, addr, size, tx->completer.guest.phys,
prot, &vcpu->vcpu.arch.pkvm_memcache);
if (tx->initiator.id == PKVM_ID_HOST &&
pkvm_ipa_in_pvmfw_region(vm, addr)) {
if (WARN_ON(!pkvm_hyp_vcpu_is_protected(vcpu)))
return -EPERM;
err = pkvm_load_pvmfw_pages(vm, addr, phys, size);
if (err)
return err;
}
return kvm_pgtable_stage2_map(&vm->pgt, addr, size, phys, prot,
&vcpu->vcpu.arch.pkvm_memcache);
}
static int __guest_get_completer_addr(u64 *completer_addr, phys_addr_t phys,

View File

@@ -14,6 +14,7 @@
#include <nvhe/mem_protect.h>
#include <nvhe/memory.h>
#include <nvhe/mm.h>
#include <nvhe/pkvm.h>
#include <nvhe/trap_handler.h>
@@ -448,6 +449,7 @@ static void init_pkvm_hyp_vm(struct kvm *host_kvm, struct pkvm_hyp_vm *hyp_vm,
hyp_vm->host_kvm = host_kvm;
hyp_vm->kvm.created_vcpus = nr_vcpus;
hyp_vm->kvm.arch.vtcr = host_mmu.arch.vtcr;
hyp_vm->kvm.arch.pkvm.pvmfw_load_addr = PVMFW_INVALID_LOAD_ADDR;
hyp_vm->kvm.arch.pkvm.enabled = READ_ONCE(host_kvm->arch.pkvm.enabled);
hyp_vm->kvm.arch.mmu.last_vcpu_ran = last_ran;
memset(hyp_vm->kvm.arch.mmu.last_vcpu_ran, -1, pkvm_get_last_ran_size());
@@ -825,6 +827,43 @@ err_unlock:
return err;
}
int pkvm_load_pvmfw_pages(struct pkvm_hyp_vm *vm, u64 ipa, phys_addr_t phys,
u64 size)
{
struct kvm_protected_vm *pkvm = &vm->kvm.arch.pkvm;
u64 npages, offset = ipa - pkvm->pvmfw_load_addr;
void *src = hyp_phys_to_virt(pvmfw_base) + offset;
if (offset >= pvmfw_size)
return -EINVAL;
size = min(size, pvmfw_size - offset);
if (!PAGE_ALIGNED(size) || !PAGE_ALIGNED(src))
return -EINVAL;
npages = size >> PAGE_SHIFT;
while (npages--) {
void *dst;
dst = hyp_fixmap_map(phys);
if (!dst)
return -EINVAL;
/*
* No need for cache maintenance here, as the pgtable code will
* take care of this when installing the pte in the guest's
* stage-2 page table.
*/
memcpy(dst, src, PAGE_SIZE);
hyp_fixmap_unmap();
src += PAGE_SIZE;
phys += PAGE_SIZE;
}
return 0;
}
void pkvm_clear_pvmfw_pages(void)
{
void *addr = hyp_phys_to_virt(pvmfw_base);