diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index bfcc36c22345..aeede8de9339 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -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; }; diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h index 542cf02ec6b0..e44c79558625 100644 --- a/arch/arm64/include/asm/kvm_pkvm.h +++ b/arch/arm64/include/asm/kvm_pkvm.h @@ -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); diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h index 69f33647ce05..ea1f340b63a3 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h +++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h @@ -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__ */ diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 85289934cccd..5ac5ad879419 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -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, diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index 1f15646c3f98..34d6c2d55d62 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -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);