mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 10:58:48 +09:00
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:
committed by
Quentin Perret
parent
5ae1450bd0
commit
fffed9c143
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user