162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020 Google LLC 462306a36Sopenharmony_ci * Author: Quentin Perret <qperret@google.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/kvm_host.h> 862306a36Sopenharmony_ci#include <asm/kvm_emulate.h> 962306a36Sopenharmony_ci#include <asm/kvm_hyp.h> 1062306a36Sopenharmony_ci#include <asm/kvm_mmu.h> 1162306a36Sopenharmony_ci#include <asm/kvm_pgtable.h> 1262306a36Sopenharmony_ci#include <asm/kvm_pkvm.h> 1362306a36Sopenharmony_ci#include <asm/stage2_pgtable.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <hyp/fault.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <nvhe/gfp.h> 1862306a36Sopenharmony_ci#include <nvhe/memory.h> 1962306a36Sopenharmony_ci#include <nvhe/mem_protect.h> 2062306a36Sopenharmony_ci#include <nvhe/mm.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define KVM_HOST_S2_FLAGS (KVM_PGTABLE_S2_NOFWB | KVM_PGTABLE_S2_IDMAP) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct host_mmu host_mmu; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic struct hyp_pool host_s2_pool; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct pkvm_hyp_vm *, __current_vm); 2962306a36Sopenharmony_ci#define current_vm (*this_cpu_ptr(&__current_vm)) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic void guest_lock_component(struct pkvm_hyp_vm *vm) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci hyp_spin_lock(&vm->lock); 3462306a36Sopenharmony_ci current_vm = vm; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void guest_unlock_component(struct pkvm_hyp_vm *vm) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci current_vm = NULL; 4062306a36Sopenharmony_ci hyp_spin_unlock(&vm->lock); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void host_lock_component(void) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci hyp_spin_lock(&host_mmu.lock); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void host_unlock_component(void) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci hyp_spin_unlock(&host_mmu.lock); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void hyp_lock_component(void) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci hyp_spin_lock(&pkvm_pgd_lock); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic void hyp_unlock_component(void) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci hyp_spin_unlock(&pkvm_pgd_lock); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void *host_s2_zalloc_pages_exact(size_t size) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci void *addr = hyp_alloc_pages(&host_s2_pool, get_order(size)); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci hyp_split_page(hyp_virt_to_page(addr)); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* 7062306a36Sopenharmony_ci * The size of concatenated PGDs is always a power of two of PAGE_SIZE, 7162306a36Sopenharmony_ci * so there should be no need to free any of the tail pages to make the 7262306a36Sopenharmony_ci * allocation exact. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci WARN_ON(size != (PAGE_SIZE << get_order(size))); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return addr; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void *host_s2_zalloc_page(void *pool) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci return hyp_alloc_pages(pool, 0); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic void host_s2_get_page(void *addr) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci hyp_get_page(&host_s2_pool, addr); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void host_s2_put_page(void *addr) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci hyp_put_page(&host_s2_pool, addr); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void host_s2_free_unlinked_table(void *addr, u32 level) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci kvm_pgtable_stage2_free_unlinked(&host_mmu.mm_ops, addr, level); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int prepare_s2_pool(void *pgt_pool_base) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci unsigned long nr_pages, pfn; 10262306a36Sopenharmony_ci int ret; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci pfn = hyp_virt_to_pfn(pgt_pool_base); 10562306a36Sopenharmony_ci nr_pages = host_s2_pgtable_pages(); 10662306a36Sopenharmony_ci ret = hyp_pool_init(&host_s2_pool, pfn, nr_pages, 0); 10762306a36Sopenharmony_ci if (ret) 10862306a36Sopenharmony_ci return ret; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci host_mmu.mm_ops = (struct kvm_pgtable_mm_ops) { 11162306a36Sopenharmony_ci .zalloc_pages_exact = host_s2_zalloc_pages_exact, 11262306a36Sopenharmony_ci .zalloc_page = host_s2_zalloc_page, 11362306a36Sopenharmony_ci .free_unlinked_table = host_s2_free_unlinked_table, 11462306a36Sopenharmony_ci .phys_to_virt = hyp_phys_to_virt, 11562306a36Sopenharmony_ci .virt_to_phys = hyp_virt_to_phys, 11662306a36Sopenharmony_ci .page_count = hyp_page_count, 11762306a36Sopenharmony_ci .get_page = host_s2_get_page, 11862306a36Sopenharmony_ci .put_page = host_s2_put_page, 11962306a36Sopenharmony_ci }; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic void prepare_host_vtcr(void) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci u32 parange, phys_shift; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* The host stage 2 is id-mapped, so use parange for T0SZ */ 12962306a36Sopenharmony_ci parange = kvm_get_parange(id_aa64mmfr0_el1_sys_val); 13062306a36Sopenharmony_ci phys_shift = id_aa64mmfr0_parange_to_phys_shift(parange); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci host_mmu.arch.vtcr = kvm_get_vtcr(id_aa64mmfr0_el1_sys_val, 13362306a36Sopenharmony_ci id_aa64mmfr1_el1_sys_val, phys_shift); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic bool host_stage2_force_pte_cb(u64 addr, u64 end, enum kvm_pgtable_prot prot); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ciint kvm_host_prepare_stage2(void *pgt_pool_base) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct kvm_s2_mmu *mmu = &host_mmu.arch.mmu; 14162306a36Sopenharmony_ci int ret; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci prepare_host_vtcr(); 14462306a36Sopenharmony_ci hyp_spin_lock_init(&host_mmu.lock); 14562306a36Sopenharmony_ci mmu->arch = &host_mmu.arch; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ret = prepare_s2_pool(pgt_pool_base); 14862306a36Sopenharmony_ci if (ret) 14962306a36Sopenharmony_ci return ret; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci ret = __kvm_pgtable_stage2_init(&host_mmu.pgt, mmu, 15262306a36Sopenharmony_ci &host_mmu.mm_ops, KVM_HOST_S2_FLAGS, 15362306a36Sopenharmony_ci host_stage2_force_pte_cb); 15462306a36Sopenharmony_ci if (ret) 15562306a36Sopenharmony_ci return ret; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci mmu->pgd_phys = __hyp_pa(host_mmu.pgt.pgd); 15862306a36Sopenharmony_ci mmu->pgt = &host_mmu.pgt; 15962306a36Sopenharmony_ci atomic64_set(&mmu->vmid.id, 0); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic bool guest_stage2_force_pte_cb(u64 addr, u64 end, 16562306a36Sopenharmony_ci enum kvm_pgtable_prot prot) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci return true; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void *guest_s2_zalloc_pages_exact(size_t size) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci void *addr = hyp_alloc_pages(¤t_vm->pool, get_order(size)); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci WARN_ON(size != (PAGE_SIZE << get_order(size))); 17562306a36Sopenharmony_ci hyp_split_page(hyp_virt_to_page(addr)); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return addr; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void guest_s2_free_pages_exact(void *addr, unsigned long size) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci u8 order = get_order(size); 18362306a36Sopenharmony_ci unsigned int i; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci for (i = 0; i < (1 << order); i++) 18662306a36Sopenharmony_ci hyp_put_page(¤t_vm->pool, addr + (i * PAGE_SIZE)); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic void *guest_s2_zalloc_page(void *mc) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct hyp_page *p; 19262306a36Sopenharmony_ci void *addr; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci addr = hyp_alloc_pages(¤t_vm->pool, 0); 19562306a36Sopenharmony_ci if (addr) 19662306a36Sopenharmony_ci return addr; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci addr = pop_hyp_memcache(mc, hyp_phys_to_virt); 19962306a36Sopenharmony_ci if (!addr) 20062306a36Sopenharmony_ci return addr; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci memset(addr, 0, PAGE_SIZE); 20362306a36Sopenharmony_ci p = hyp_virt_to_page(addr); 20462306a36Sopenharmony_ci memset(p, 0, sizeof(*p)); 20562306a36Sopenharmony_ci p->refcount = 1; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return addr; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic void guest_s2_get_page(void *addr) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci hyp_get_page(¤t_vm->pool, addr); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic void guest_s2_put_page(void *addr) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci hyp_put_page(¤t_vm->pool, addr); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void clean_dcache_guest_page(void *va, size_t size) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci __clean_dcache_guest_page(hyp_fixmap_map(__hyp_pa(va)), size); 22362306a36Sopenharmony_ci hyp_fixmap_unmap(); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic void invalidate_icache_guest_page(void *va, size_t size) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci __invalidate_icache_guest_page(hyp_fixmap_map(__hyp_pa(va)), size); 22962306a36Sopenharmony_ci hyp_fixmap_unmap(); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ciint kvm_guest_prepare_stage2(struct pkvm_hyp_vm *vm, void *pgd) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct kvm_s2_mmu *mmu = &vm->kvm.arch.mmu; 23562306a36Sopenharmony_ci unsigned long nr_pages; 23662306a36Sopenharmony_ci int ret; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci nr_pages = kvm_pgtable_stage2_pgd_size(vm->kvm.arch.vtcr) >> PAGE_SHIFT; 23962306a36Sopenharmony_ci ret = hyp_pool_init(&vm->pool, hyp_virt_to_pfn(pgd), nr_pages, 0); 24062306a36Sopenharmony_ci if (ret) 24162306a36Sopenharmony_ci return ret; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci hyp_spin_lock_init(&vm->lock); 24462306a36Sopenharmony_ci vm->mm_ops = (struct kvm_pgtable_mm_ops) { 24562306a36Sopenharmony_ci .zalloc_pages_exact = guest_s2_zalloc_pages_exact, 24662306a36Sopenharmony_ci .free_pages_exact = guest_s2_free_pages_exact, 24762306a36Sopenharmony_ci .zalloc_page = guest_s2_zalloc_page, 24862306a36Sopenharmony_ci .phys_to_virt = hyp_phys_to_virt, 24962306a36Sopenharmony_ci .virt_to_phys = hyp_virt_to_phys, 25062306a36Sopenharmony_ci .page_count = hyp_page_count, 25162306a36Sopenharmony_ci .get_page = guest_s2_get_page, 25262306a36Sopenharmony_ci .put_page = guest_s2_put_page, 25362306a36Sopenharmony_ci .dcache_clean_inval_poc = clean_dcache_guest_page, 25462306a36Sopenharmony_ci .icache_inval_pou = invalidate_icache_guest_page, 25562306a36Sopenharmony_ci }; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci guest_lock_component(vm); 25862306a36Sopenharmony_ci ret = __kvm_pgtable_stage2_init(mmu->pgt, mmu, &vm->mm_ops, 0, 25962306a36Sopenharmony_ci guest_stage2_force_pte_cb); 26062306a36Sopenharmony_ci guest_unlock_component(vm); 26162306a36Sopenharmony_ci if (ret) 26262306a36Sopenharmony_ci return ret; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci vm->kvm.arch.mmu.pgd_phys = __hyp_pa(vm->pgt.pgd); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_civoid reclaim_guest_pages(struct pkvm_hyp_vm *vm, struct kvm_hyp_memcache *mc) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci void *addr; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Dump all pgtable pages in the hyp_pool */ 27462306a36Sopenharmony_ci guest_lock_component(vm); 27562306a36Sopenharmony_ci kvm_pgtable_stage2_destroy(&vm->pgt); 27662306a36Sopenharmony_ci vm->kvm.arch.mmu.pgd_phys = 0ULL; 27762306a36Sopenharmony_ci guest_unlock_component(vm); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* Drain the hyp_pool into the memcache */ 28062306a36Sopenharmony_ci addr = hyp_alloc_pages(&vm->pool, 0); 28162306a36Sopenharmony_ci while (addr) { 28262306a36Sopenharmony_ci memset(hyp_virt_to_page(addr), 0, sizeof(struct hyp_page)); 28362306a36Sopenharmony_ci push_hyp_memcache(mc, addr, hyp_virt_to_phys); 28462306a36Sopenharmony_ci WARN_ON(__pkvm_hyp_donate_host(hyp_virt_to_pfn(addr), 1)); 28562306a36Sopenharmony_ci addr = hyp_alloc_pages(&vm->pool, 0); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ciint __pkvm_prot_finalize(void) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct kvm_s2_mmu *mmu = &host_mmu.arch.mmu; 29262306a36Sopenharmony_ci struct kvm_nvhe_init_params *params = this_cpu_ptr(&kvm_init_params); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (params->hcr_el2 & HCR_VM) 29562306a36Sopenharmony_ci return -EPERM; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci params->vttbr = kvm_get_vttbr(mmu); 29862306a36Sopenharmony_ci params->vtcr = host_mmu.arch.vtcr; 29962306a36Sopenharmony_ci params->hcr_el2 |= HCR_VM; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* 30262306a36Sopenharmony_ci * The CMO below not only cleans the updated params to the 30362306a36Sopenharmony_ci * PoC, but also provides the DSB that ensures ongoing 30462306a36Sopenharmony_ci * page-table walks that have started before we trapped to EL2 30562306a36Sopenharmony_ci * have completed. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_ci kvm_flush_dcache_to_poc(params, sizeof(*params)); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci write_sysreg(params->hcr_el2, hcr_el2); 31062306a36Sopenharmony_ci __load_stage2(&host_mmu.arch.mmu, &host_mmu.arch); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * Make sure to have an ISB before the TLB maintenance below but only 31462306a36Sopenharmony_ci * when __load_stage2() doesn't include one already. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci asm(ALTERNATIVE("isb", "nop", ARM64_WORKAROUND_SPECULATIVE_AT)); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Invalidate stale HCR bits that may be cached in TLBs */ 31962306a36Sopenharmony_ci __tlbi(vmalls12e1); 32062306a36Sopenharmony_ci dsb(nsh); 32162306a36Sopenharmony_ci isb(); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int host_stage2_unmap_dev_all(void) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct kvm_pgtable *pgt = &host_mmu.pgt; 32962306a36Sopenharmony_ci struct memblock_region *reg; 33062306a36Sopenharmony_ci u64 addr = 0; 33162306a36Sopenharmony_ci int i, ret; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* Unmap all non-memory regions to recycle the pages */ 33462306a36Sopenharmony_ci for (i = 0; i < hyp_memblock_nr; i++, addr = reg->base + reg->size) { 33562306a36Sopenharmony_ci reg = &hyp_memory[i]; 33662306a36Sopenharmony_ci ret = kvm_pgtable_stage2_unmap(pgt, addr, reg->base - addr); 33762306a36Sopenharmony_ci if (ret) 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci return kvm_pgtable_stage2_unmap(pgt, addr, BIT(pgt->ia_bits) - addr); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistruct kvm_mem_range { 34462306a36Sopenharmony_ci u64 start; 34562306a36Sopenharmony_ci u64 end; 34662306a36Sopenharmony_ci}; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic struct memblock_region *find_mem_range(phys_addr_t addr, struct kvm_mem_range *range) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci int cur, left = 0, right = hyp_memblock_nr; 35162306a36Sopenharmony_ci struct memblock_region *reg; 35262306a36Sopenharmony_ci phys_addr_t end; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci range->start = 0; 35562306a36Sopenharmony_ci range->end = ULONG_MAX; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* The list of memblock regions is sorted, binary search it */ 35862306a36Sopenharmony_ci while (left < right) { 35962306a36Sopenharmony_ci cur = (left + right) >> 1; 36062306a36Sopenharmony_ci reg = &hyp_memory[cur]; 36162306a36Sopenharmony_ci end = reg->base + reg->size; 36262306a36Sopenharmony_ci if (addr < reg->base) { 36362306a36Sopenharmony_ci right = cur; 36462306a36Sopenharmony_ci range->end = reg->base; 36562306a36Sopenharmony_ci } else if (addr >= end) { 36662306a36Sopenharmony_ci left = cur + 1; 36762306a36Sopenharmony_ci range->start = end; 36862306a36Sopenharmony_ci } else { 36962306a36Sopenharmony_ci range->start = reg->base; 37062306a36Sopenharmony_ci range->end = end; 37162306a36Sopenharmony_ci return reg; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return NULL; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cibool addr_is_memory(phys_addr_t phys) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct kvm_mem_range range; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return !!find_mem_range(phys, &range); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic bool addr_is_allowed_memory(phys_addr_t phys) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct memblock_region *reg; 38862306a36Sopenharmony_ci struct kvm_mem_range range; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci reg = find_mem_range(phys, &range); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return reg && !(reg->flags & MEMBLOCK_NOMAP); 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic bool is_in_mem_range(u64 addr, struct kvm_mem_range *range) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci return range->start <= addr && addr < range->end; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic bool range_is_memory(u64 start, u64 end) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct kvm_mem_range r; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (!find_mem_range(start, &r)) 40562306a36Sopenharmony_ci return false; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return is_in_mem_range(end - 1, &r); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic inline int __host_stage2_idmap(u64 start, u64 end, 41162306a36Sopenharmony_ci enum kvm_pgtable_prot prot) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci return kvm_pgtable_stage2_map(&host_mmu.pgt, start, end - start, start, 41462306a36Sopenharmony_ci prot, &host_s2_pool, 0); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/* 41862306a36Sopenharmony_ci * The pool has been provided with enough pages to cover all of memory with 41962306a36Sopenharmony_ci * page granularity, but it is difficult to know how much of the MMIO range 42062306a36Sopenharmony_ci * we will need to cover upfront, so we may need to 'recycle' the pages if we 42162306a36Sopenharmony_ci * run out. 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_ci#define host_stage2_try(fn, ...) \ 42462306a36Sopenharmony_ci ({ \ 42562306a36Sopenharmony_ci int __ret; \ 42662306a36Sopenharmony_ci hyp_assert_lock_held(&host_mmu.lock); \ 42762306a36Sopenharmony_ci __ret = fn(__VA_ARGS__); \ 42862306a36Sopenharmony_ci if (__ret == -ENOMEM) { \ 42962306a36Sopenharmony_ci __ret = host_stage2_unmap_dev_all(); \ 43062306a36Sopenharmony_ci if (!__ret) \ 43162306a36Sopenharmony_ci __ret = fn(__VA_ARGS__); \ 43262306a36Sopenharmony_ci } \ 43362306a36Sopenharmony_ci __ret; \ 43462306a36Sopenharmony_ci }) 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic inline bool range_included(struct kvm_mem_range *child, 43762306a36Sopenharmony_ci struct kvm_mem_range *parent) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci return parent->start <= child->start && child->end <= parent->end; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic int host_stage2_adjust_range(u64 addr, struct kvm_mem_range *range) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci struct kvm_mem_range cur; 44562306a36Sopenharmony_ci kvm_pte_t pte; 44662306a36Sopenharmony_ci u32 level; 44762306a36Sopenharmony_ci int ret; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci hyp_assert_lock_held(&host_mmu.lock); 45062306a36Sopenharmony_ci ret = kvm_pgtable_get_leaf(&host_mmu.pgt, addr, &pte, &level); 45162306a36Sopenharmony_ci if (ret) 45262306a36Sopenharmony_ci return ret; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (kvm_pte_valid(pte)) 45562306a36Sopenharmony_ci return -EAGAIN; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (pte) 45862306a36Sopenharmony_ci return -EPERM; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci do { 46162306a36Sopenharmony_ci u64 granule = kvm_granule_size(level); 46262306a36Sopenharmony_ci cur.start = ALIGN_DOWN(addr, granule); 46362306a36Sopenharmony_ci cur.end = cur.start + granule; 46462306a36Sopenharmony_ci level++; 46562306a36Sopenharmony_ci } while ((level < KVM_PGTABLE_MAX_LEVELS) && 46662306a36Sopenharmony_ci !(kvm_level_supports_block_mapping(level) && 46762306a36Sopenharmony_ci range_included(&cur, range))); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci *range = cur; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ciint host_stage2_idmap_locked(phys_addr_t addr, u64 size, 47562306a36Sopenharmony_ci enum kvm_pgtable_prot prot) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci return host_stage2_try(__host_stage2_idmap, addr, addr + size, prot); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ciint host_stage2_set_owner_locked(phys_addr_t addr, u64 size, u8 owner_id) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci return host_stage2_try(kvm_pgtable_stage2_set_owner, &host_mmu.pgt, 48362306a36Sopenharmony_ci addr, size, &host_s2_pool, owner_id); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic bool host_stage2_force_pte_cb(u64 addr, u64 end, enum kvm_pgtable_prot prot) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci /* 48962306a36Sopenharmony_ci * Block mappings must be used with care in the host stage-2 as a 49062306a36Sopenharmony_ci * kvm_pgtable_stage2_map() operation targeting a page in the range of 49162306a36Sopenharmony_ci * an existing block will delete the block under the assumption that 49262306a36Sopenharmony_ci * mappings in the rest of the block range can always be rebuilt lazily. 49362306a36Sopenharmony_ci * That assumption is correct for the host stage-2 with RWX mappings 49462306a36Sopenharmony_ci * targeting memory or RW mappings targeting MMIO ranges (see 49562306a36Sopenharmony_ci * host_stage2_idmap() below which implements some of the host memory 49662306a36Sopenharmony_ci * abort logic). However, this is not safe for any other mappings where 49762306a36Sopenharmony_ci * the host stage-2 page-table is in fact the only place where this 49862306a36Sopenharmony_ci * state is stored. In all those cases, it is safer to use page-level 49962306a36Sopenharmony_ci * mappings, hence avoiding to lose the state because of side-effects in 50062306a36Sopenharmony_ci * kvm_pgtable_stage2_map(). 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_ci if (range_is_memory(addr, end)) 50362306a36Sopenharmony_ci return prot != PKVM_HOST_MEM_PROT; 50462306a36Sopenharmony_ci else 50562306a36Sopenharmony_ci return prot != PKVM_HOST_MMIO_PROT; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic int host_stage2_idmap(u64 addr) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct kvm_mem_range range; 51162306a36Sopenharmony_ci bool is_memory = !!find_mem_range(addr, &range); 51262306a36Sopenharmony_ci enum kvm_pgtable_prot prot; 51362306a36Sopenharmony_ci int ret; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci prot = is_memory ? PKVM_HOST_MEM_PROT : PKVM_HOST_MMIO_PROT; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci host_lock_component(); 51862306a36Sopenharmony_ci ret = host_stage2_adjust_range(addr, &range); 51962306a36Sopenharmony_ci if (ret) 52062306a36Sopenharmony_ci goto unlock; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci ret = host_stage2_idmap_locked(range.start, range.end - range.start, prot); 52362306a36Sopenharmony_ciunlock: 52462306a36Sopenharmony_ci host_unlock_component(); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return ret; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_civoid handle_host_mem_abort(struct kvm_cpu_context *host_ctxt) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct kvm_vcpu_fault_info fault; 53262306a36Sopenharmony_ci u64 esr, addr; 53362306a36Sopenharmony_ci int ret = 0; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci esr = read_sysreg_el2(SYS_ESR); 53662306a36Sopenharmony_ci BUG_ON(!__get_fault_info(esr, &fault)); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci addr = (fault.hpfar_el2 & HPFAR_MASK) << 8; 53962306a36Sopenharmony_ci ret = host_stage2_idmap(addr); 54062306a36Sopenharmony_ci BUG_ON(ret && ret != -EAGAIN); 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistruct pkvm_mem_transition { 54462306a36Sopenharmony_ci u64 nr_pages; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci struct { 54762306a36Sopenharmony_ci enum pkvm_component_id id; 54862306a36Sopenharmony_ci /* Address in the initiator's address space */ 54962306a36Sopenharmony_ci u64 addr; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci union { 55262306a36Sopenharmony_ci struct { 55362306a36Sopenharmony_ci /* Address in the completer's address space */ 55462306a36Sopenharmony_ci u64 completer_addr; 55562306a36Sopenharmony_ci } host; 55662306a36Sopenharmony_ci struct { 55762306a36Sopenharmony_ci u64 completer_addr; 55862306a36Sopenharmony_ci } hyp; 55962306a36Sopenharmony_ci }; 56062306a36Sopenharmony_ci } initiator; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci struct { 56362306a36Sopenharmony_ci enum pkvm_component_id id; 56462306a36Sopenharmony_ci } completer; 56562306a36Sopenharmony_ci}; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistruct pkvm_mem_share { 56862306a36Sopenharmony_ci const struct pkvm_mem_transition tx; 56962306a36Sopenharmony_ci const enum kvm_pgtable_prot completer_prot; 57062306a36Sopenharmony_ci}; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistruct pkvm_mem_donation { 57362306a36Sopenharmony_ci const struct pkvm_mem_transition tx; 57462306a36Sopenharmony_ci}; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistruct check_walk_data { 57762306a36Sopenharmony_ci enum pkvm_page_state desired; 57862306a36Sopenharmony_ci enum pkvm_page_state (*get_page_state)(kvm_pte_t pte, u64 addr); 57962306a36Sopenharmony_ci}; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic int __check_page_state_visitor(const struct kvm_pgtable_visit_ctx *ctx, 58262306a36Sopenharmony_ci enum kvm_pgtable_walk_flags visit) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct check_walk_data *d = ctx->arg; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return d->get_page_state(ctx->old, ctx->addr) == d->desired ? 0 : -EPERM; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic int check_page_state_range(struct kvm_pgtable *pgt, u64 addr, u64 size, 59062306a36Sopenharmony_ci struct check_walk_data *data) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct kvm_pgtable_walker walker = { 59362306a36Sopenharmony_ci .cb = __check_page_state_visitor, 59462306a36Sopenharmony_ci .arg = data, 59562306a36Sopenharmony_ci .flags = KVM_PGTABLE_WALK_LEAF, 59662306a36Sopenharmony_ci }; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return kvm_pgtable_walk(pgt, addr, size, &walker); 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic enum pkvm_page_state host_get_page_state(kvm_pte_t pte, u64 addr) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci if (!addr_is_allowed_memory(addr)) 60462306a36Sopenharmony_ci return PKVM_NOPAGE; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (!kvm_pte_valid(pte) && pte) 60762306a36Sopenharmony_ci return PKVM_NOPAGE; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci return pkvm_getstate(kvm_pgtable_stage2_pte_prot(pte)); 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic int __host_check_page_state_range(u64 addr, u64 size, 61362306a36Sopenharmony_ci enum pkvm_page_state state) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct check_walk_data d = { 61662306a36Sopenharmony_ci .desired = state, 61762306a36Sopenharmony_ci .get_page_state = host_get_page_state, 61862306a36Sopenharmony_ci }; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci hyp_assert_lock_held(&host_mmu.lock); 62162306a36Sopenharmony_ci return check_page_state_range(&host_mmu.pgt, addr, size, &d); 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic int __host_set_page_state_range(u64 addr, u64 size, 62562306a36Sopenharmony_ci enum pkvm_page_state state) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci enum kvm_pgtable_prot prot = pkvm_mkstate(PKVM_HOST_MEM_PROT, state); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci return host_stage2_idmap_locked(addr, size, prot); 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic int host_request_owned_transition(u64 *completer_addr, 63362306a36Sopenharmony_ci const struct pkvm_mem_transition *tx) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci u64 size = tx->nr_pages * PAGE_SIZE; 63662306a36Sopenharmony_ci u64 addr = tx->initiator.addr; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci *completer_addr = tx->initiator.host.completer_addr; 63962306a36Sopenharmony_ci return __host_check_page_state_range(addr, size, PKVM_PAGE_OWNED); 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic int host_request_unshare(u64 *completer_addr, 64362306a36Sopenharmony_ci const struct pkvm_mem_transition *tx) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci u64 size = tx->nr_pages * PAGE_SIZE; 64662306a36Sopenharmony_ci u64 addr = tx->initiator.addr; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci *completer_addr = tx->initiator.host.completer_addr; 64962306a36Sopenharmony_ci return __host_check_page_state_range(addr, size, PKVM_PAGE_SHARED_OWNED); 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic int host_initiate_share(u64 *completer_addr, 65362306a36Sopenharmony_ci const struct pkvm_mem_transition *tx) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci u64 size = tx->nr_pages * PAGE_SIZE; 65662306a36Sopenharmony_ci u64 addr = tx->initiator.addr; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci *completer_addr = tx->initiator.host.completer_addr; 65962306a36Sopenharmony_ci return __host_set_page_state_range(addr, size, PKVM_PAGE_SHARED_OWNED); 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic int host_initiate_unshare(u64 *completer_addr, 66362306a36Sopenharmony_ci const struct pkvm_mem_transition *tx) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci u64 size = tx->nr_pages * PAGE_SIZE; 66662306a36Sopenharmony_ci u64 addr = tx->initiator.addr; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci *completer_addr = tx->initiator.host.completer_addr; 66962306a36Sopenharmony_ci return __host_set_page_state_range(addr, size, PKVM_PAGE_OWNED); 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic int host_initiate_donation(u64 *completer_addr, 67362306a36Sopenharmony_ci const struct pkvm_mem_transition *tx) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci u8 owner_id = tx->completer.id; 67662306a36Sopenharmony_ci u64 size = tx->nr_pages * PAGE_SIZE; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci *completer_addr = tx->initiator.host.completer_addr; 67962306a36Sopenharmony_ci return host_stage2_set_owner_locked(tx->initiator.addr, size, owner_id); 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic bool __host_ack_skip_pgtable_check(const struct pkvm_mem_transition *tx) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci return !(IS_ENABLED(CONFIG_NVHE_EL2_DEBUG) || 68562306a36Sopenharmony_ci tx->initiator.id != PKVM_ID_HYP); 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int __host_ack_transition(u64 addr, const struct pkvm_mem_transition *tx, 68962306a36Sopenharmony_ci enum pkvm_page_state state) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci u64 size = tx->nr_pages * PAGE_SIZE; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (__host_ack_skip_pgtable_check(tx)) 69462306a36Sopenharmony_ci return 0; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci return __host_check_page_state_range(addr, size, state); 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic int host_ack_donation(u64 addr, const struct pkvm_mem_transition *tx) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci return __host_ack_transition(addr, tx, PKVM_NOPAGE); 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic int host_complete_donation(u64 addr, const struct pkvm_mem_transition *tx) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci u64 size = tx->nr_pages * PAGE_SIZE; 70762306a36Sopenharmony_ci u8 host_id = tx->completer.id; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci return host_stage2_set_owner_locked(addr, size, host_id); 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic enum pkvm_page_state hyp_get_page_state(kvm_pte_t pte, u64 addr) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci if (!kvm_pte_valid(pte)) 71562306a36Sopenharmony_ci return PKVM_NOPAGE; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci return pkvm_getstate(kvm_pgtable_hyp_pte_prot(pte)); 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic int __hyp_check_page_state_range(u64 addr, u64 size, 72162306a36Sopenharmony_ci enum pkvm_page_state state) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci struct check_walk_data d = { 72462306a36Sopenharmony_ci .desired = state, 72562306a36Sopenharmony_ci .get_page_state = hyp_get_page_state, 72662306a36Sopenharmony_ci }; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci hyp_assert_lock_held(&pkvm_pgd_lock); 72962306a36Sopenharmony_ci return check_page_state_range(&pkvm_pgtable, addr, size, &d); 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic int hyp_request_donation(u64 *completer_addr, 73362306a36Sopenharmony_ci const struct pkvm_mem_transition *tx) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci u64 size = tx->nr_pages * PAGE_SIZE; 73662306a36Sopenharmony_ci u64 addr = tx->initiator.addr; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci *completer_addr = tx->initiator.hyp.completer_addr; 73962306a36Sopenharmony_ci return __hyp_check_page_state_range(addr, size, PKVM_PAGE_OWNED); 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic int hyp_initiate_donation(u64 *completer_addr, 74362306a36Sopenharmony_ci const struct pkvm_mem_transition *tx) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci u64 size = tx->nr_pages * PAGE_SIZE; 74662306a36Sopenharmony_ci int ret; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci *completer_addr = tx->initiator.hyp.completer_addr; 74962306a36Sopenharmony_ci ret = kvm_pgtable_hyp_unmap(&pkvm_pgtable, tx->initiator.addr, size); 75062306a36Sopenharmony_ci return (ret != size) ? -EFAULT : 0; 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic bool __hyp_ack_skip_pgtable_check(const struct pkvm_mem_transition *tx) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci return !(IS_ENABLED(CONFIG_NVHE_EL2_DEBUG) || 75662306a36Sopenharmony_ci tx->initiator.id != PKVM_ID_HOST); 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic int hyp_ack_share(u64 addr, const struct pkvm_mem_transition *tx, 76062306a36Sopenharmony_ci enum kvm_pgtable_prot perms) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci u64 size = tx->nr_pages * PAGE_SIZE; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (perms != PAGE_HYP) 76562306a36Sopenharmony_ci return -EPERM; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (__hyp_ack_skip_pgtable_check(tx)) 76862306a36Sopenharmony_ci return 0; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return __hyp_check_page_state_range(addr, size, PKVM_NOPAGE); 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic int hyp_ack_unshare(u64 addr, const struct pkvm_mem_transition *tx) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci u64 size = tx->nr_pages * PAGE_SIZE; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (tx->initiator.id == PKVM_ID_HOST && hyp_page_count((void *)addr)) 77862306a36Sopenharmony_ci return -EBUSY; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (__hyp_ack_skip_pgtable_check(tx)) 78162306a36Sopenharmony_ci return 0; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci return __hyp_check_page_state_range(addr, size, 78462306a36Sopenharmony_ci PKVM_PAGE_SHARED_BORROWED); 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic int hyp_ack_donation(u64 addr, const struct pkvm_mem_transition *tx) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci u64 size = tx->nr_pages * PAGE_SIZE; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (__hyp_ack_skip_pgtable_check(tx)) 79262306a36Sopenharmony_ci return 0; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci return __hyp_check_page_state_range(addr, size, PKVM_NOPAGE); 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic int hyp_complete_share(u64 addr, const struct pkvm_mem_transition *tx, 79862306a36Sopenharmony_ci enum kvm_pgtable_prot perms) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci void *start = (void *)addr, *end = start + (tx->nr_pages * PAGE_SIZE); 80162306a36Sopenharmony_ci enum kvm_pgtable_prot prot; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci prot = pkvm_mkstate(perms, PKVM_PAGE_SHARED_BORROWED); 80462306a36Sopenharmony_ci return pkvm_create_mappings_locked(start, end, prot); 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic int hyp_complete_unshare(u64 addr, const struct pkvm_mem_transition *tx) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci u64 size = tx->nr_pages * PAGE_SIZE; 81062306a36Sopenharmony_ci int ret = kvm_pgtable_hyp_unmap(&pkvm_pgtable, addr, size); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci return (ret != size) ? -EFAULT : 0; 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic int hyp_complete_donation(u64 addr, 81662306a36Sopenharmony_ci const struct pkvm_mem_transition *tx) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci void *start = (void *)addr, *end = start + (tx->nr_pages * PAGE_SIZE); 81962306a36Sopenharmony_ci enum kvm_pgtable_prot prot = pkvm_mkstate(PAGE_HYP, PKVM_PAGE_OWNED); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci return pkvm_create_mappings_locked(start, end, prot); 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic int check_share(struct pkvm_mem_share *share) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci const struct pkvm_mem_transition *tx = &share->tx; 82762306a36Sopenharmony_ci u64 completer_addr; 82862306a36Sopenharmony_ci int ret; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci switch (tx->initiator.id) { 83162306a36Sopenharmony_ci case PKVM_ID_HOST: 83262306a36Sopenharmony_ci ret = host_request_owned_transition(&completer_addr, tx); 83362306a36Sopenharmony_ci break; 83462306a36Sopenharmony_ci default: 83562306a36Sopenharmony_ci ret = -EINVAL; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (ret) 83962306a36Sopenharmony_ci return ret; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci switch (tx->completer.id) { 84262306a36Sopenharmony_ci case PKVM_ID_HYP: 84362306a36Sopenharmony_ci ret = hyp_ack_share(completer_addr, tx, share->completer_prot); 84462306a36Sopenharmony_ci break; 84562306a36Sopenharmony_ci case PKVM_ID_FFA: 84662306a36Sopenharmony_ci /* 84762306a36Sopenharmony_ci * We only check the host; the secure side will check the other 84862306a36Sopenharmony_ci * end when we forward the FFA call. 84962306a36Sopenharmony_ci */ 85062306a36Sopenharmony_ci ret = 0; 85162306a36Sopenharmony_ci break; 85262306a36Sopenharmony_ci default: 85362306a36Sopenharmony_ci ret = -EINVAL; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci return ret; 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic int __do_share(struct pkvm_mem_share *share) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci const struct pkvm_mem_transition *tx = &share->tx; 86262306a36Sopenharmony_ci u64 completer_addr; 86362306a36Sopenharmony_ci int ret; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci switch (tx->initiator.id) { 86662306a36Sopenharmony_ci case PKVM_ID_HOST: 86762306a36Sopenharmony_ci ret = host_initiate_share(&completer_addr, tx); 86862306a36Sopenharmony_ci break; 86962306a36Sopenharmony_ci default: 87062306a36Sopenharmony_ci ret = -EINVAL; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci if (ret) 87462306a36Sopenharmony_ci return ret; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci switch (tx->completer.id) { 87762306a36Sopenharmony_ci case PKVM_ID_HYP: 87862306a36Sopenharmony_ci ret = hyp_complete_share(completer_addr, tx, share->completer_prot); 87962306a36Sopenharmony_ci break; 88062306a36Sopenharmony_ci case PKVM_ID_FFA: 88162306a36Sopenharmony_ci /* 88262306a36Sopenharmony_ci * We're not responsible for any secure page-tables, so there's 88362306a36Sopenharmony_ci * nothing to do here. 88462306a36Sopenharmony_ci */ 88562306a36Sopenharmony_ci ret = 0; 88662306a36Sopenharmony_ci break; 88762306a36Sopenharmony_ci default: 88862306a36Sopenharmony_ci ret = -EINVAL; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci return ret; 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci/* 89562306a36Sopenharmony_ci * do_share(): 89662306a36Sopenharmony_ci * 89762306a36Sopenharmony_ci * The page owner grants access to another component with a given set 89862306a36Sopenharmony_ci * of permissions. 89962306a36Sopenharmony_ci * 90062306a36Sopenharmony_ci * Initiator: OWNED => SHARED_OWNED 90162306a36Sopenharmony_ci * Completer: NOPAGE => SHARED_BORROWED 90262306a36Sopenharmony_ci */ 90362306a36Sopenharmony_cistatic int do_share(struct pkvm_mem_share *share) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci int ret; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci ret = check_share(share); 90862306a36Sopenharmony_ci if (ret) 90962306a36Sopenharmony_ci return ret; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci return WARN_ON(__do_share(share)); 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cistatic int check_unshare(struct pkvm_mem_share *share) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci const struct pkvm_mem_transition *tx = &share->tx; 91762306a36Sopenharmony_ci u64 completer_addr; 91862306a36Sopenharmony_ci int ret; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci switch (tx->initiator.id) { 92162306a36Sopenharmony_ci case PKVM_ID_HOST: 92262306a36Sopenharmony_ci ret = host_request_unshare(&completer_addr, tx); 92362306a36Sopenharmony_ci break; 92462306a36Sopenharmony_ci default: 92562306a36Sopenharmony_ci ret = -EINVAL; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci if (ret) 92962306a36Sopenharmony_ci return ret; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci switch (tx->completer.id) { 93262306a36Sopenharmony_ci case PKVM_ID_HYP: 93362306a36Sopenharmony_ci ret = hyp_ack_unshare(completer_addr, tx); 93462306a36Sopenharmony_ci break; 93562306a36Sopenharmony_ci case PKVM_ID_FFA: 93662306a36Sopenharmony_ci /* See check_share() */ 93762306a36Sopenharmony_ci ret = 0; 93862306a36Sopenharmony_ci break; 93962306a36Sopenharmony_ci default: 94062306a36Sopenharmony_ci ret = -EINVAL; 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci return ret; 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic int __do_unshare(struct pkvm_mem_share *share) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci const struct pkvm_mem_transition *tx = &share->tx; 94962306a36Sopenharmony_ci u64 completer_addr; 95062306a36Sopenharmony_ci int ret; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci switch (tx->initiator.id) { 95362306a36Sopenharmony_ci case PKVM_ID_HOST: 95462306a36Sopenharmony_ci ret = host_initiate_unshare(&completer_addr, tx); 95562306a36Sopenharmony_ci break; 95662306a36Sopenharmony_ci default: 95762306a36Sopenharmony_ci ret = -EINVAL; 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci if (ret) 96162306a36Sopenharmony_ci return ret; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci switch (tx->completer.id) { 96462306a36Sopenharmony_ci case PKVM_ID_HYP: 96562306a36Sopenharmony_ci ret = hyp_complete_unshare(completer_addr, tx); 96662306a36Sopenharmony_ci break; 96762306a36Sopenharmony_ci case PKVM_ID_FFA: 96862306a36Sopenharmony_ci /* See __do_share() */ 96962306a36Sopenharmony_ci ret = 0; 97062306a36Sopenharmony_ci break; 97162306a36Sopenharmony_ci default: 97262306a36Sopenharmony_ci ret = -EINVAL; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci return ret; 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci/* 97962306a36Sopenharmony_ci * do_unshare(): 98062306a36Sopenharmony_ci * 98162306a36Sopenharmony_ci * The page owner revokes access from another component for a range of 98262306a36Sopenharmony_ci * pages which were previously shared using do_share(). 98362306a36Sopenharmony_ci * 98462306a36Sopenharmony_ci * Initiator: SHARED_OWNED => OWNED 98562306a36Sopenharmony_ci * Completer: SHARED_BORROWED => NOPAGE 98662306a36Sopenharmony_ci */ 98762306a36Sopenharmony_cistatic int do_unshare(struct pkvm_mem_share *share) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci int ret; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci ret = check_unshare(share); 99262306a36Sopenharmony_ci if (ret) 99362306a36Sopenharmony_ci return ret; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci return WARN_ON(__do_unshare(share)); 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic int check_donation(struct pkvm_mem_donation *donation) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci const struct pkvm_mem_transition *tx = &donation->tx; 100162306a36Sopenharmony_ci u64 completer_addr; 100262306a36Sopenharmony_ci int ret; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci switch (tx->initiator.id) { 100562306a36Sopenharmony_ci case PKVM_ID_HOST: 100662306a36Sopenharmony_ci ret = host_request_owned_transition(&completer_addr, tx); 100762306a36Sopenharmony_ci break; 100862306a36Sopenharmony_ci case PKVM_ID_HYP: 100962306a36Sopenharmony_ci ret = hyp_request_donation(&completer_addr, tx); 101062306a36Sopenharmony_ci break; 101162306a36Sopenharmony_ci default: 101262306a36Sopenharmony_ci ret = -EINVAL; 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (ret) 101662306a36Sopenharmony_ci return ret; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci switch (tx->completer.id) { 101962306a36Sopenharmony_ci case PKVM_ID_HOST: 102062306a36Sopenharmony_ci ret = host_ack_donation(completer_addr, tx); 102162306a36Sopenharmony_ci break; 102262306a36Sopenharmony_ci case PKVM_ID_HYP: 102362306a36Sopenharmony_ci ret = hyp_ack_donation(completer_addr, tx); 102462306a36Sopenharmony_ci break; 102562306a36Sopenharmony_ci default: 102662306a36Sopenharmony_ci ret = -EINVAL; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci return ret; 103062306a36Sopenharmony_ci} 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_cistatic int __do_donate(struct pkvm_mem_donation *donation) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci const struct pkvm_mem_transition *tx = &donation->tx; 103562306a36Sopenharmony_ci u64 completer_addr; 103662306a36Sopenharmony_ci int ret; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci switch (tx->initiator.id) { 103962306a36Sopenharmony_ci case PKVM_ID_HOST: 104062306a36Sopenharmony_ci ret = host_initiate_donation(&completer_addr, tx); 104162306a36Sopenharmony_ci break; 104262306a36Sopenharmony_ci case PKVM_ID_HYP: 104362306a36Sopenharmony_ci ret = hyp_initiate_donation(&completer_addr, tx); 104462306a36Sopenharmony_ci break; 104562306a36Sopenharmony_ci default: 104662306a36Sopenharmony_ci ret = -EINVAL; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci if (ret) 105062306a36Sopenharmony_ci return ret; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci switch (tx->completer.id) { 105362306a36Sopenharmony_ci case PKVM_ID_HOST: 105462306a36Sopenharmony_ci ret = host_complete_donation(completer_addr, tx); 105562306a36Sopenharmony_ci break; 105662306a36Sopenharmony_ci case PKVM_ID_HYP: 105762306a36Sopenharmony_ci ret = hyp_complete_donation(completer_addr, tx); 105862306a36Sopenharmony_ci break; 105962306a36Sopenharmony_ci default: 106062306a36Sopenharmony_ci ret = -EINVAL; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci return ret; 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci/* 106762306a36Sopenharmony_ci * do_donate(): 106862306a36Sopenharmony_ci * 106962306a36Sopenharmony_ci * The page owner transfers ownership to another component, losing access 107062306a36Sopenharmony_ci * as a consequence. 107162306a36Sopenharmony_ci * 107262306a36Sopenharmony_ci * Initiator: OWNED => NOPAGE 107362306a36Sopenharmony_ci * Completer: NOPAGE => OWNED 107462306a36Sopenharmony_ci */ 107562306a36Sopenharmony_cistatic int do_donate(struct pkvm_mem_donation *donation) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci int ret; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci ret = check_donation(donation); 108062306a36Sopenharmony_ci if (ret) 108162306a36Sopenharmony_ci return ret; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci return WARN_ON(__do_donate(donation)); 108462306a36Sopenharmony_ci} 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ciint __pkvm_host_share_hyp(u64 pfn) 108762306a36Sopenharmony_ci{ 108862306a36Sopenharmony_ci int ret; 108962306a36Sopenharmony_ci u64 host_addr = hyp_pfn_to_phys(pfn); 109062306a36Sopenharmony_ci u64 hyp_addr = (u64)__hyp_va(host_addr); 109162306a36Sopenharmony_ci struct pkvm_mem_share share = { 109262306a36Sopenharmony_ci .tx = { 109362306a36Sopenharmony_ci .nr_pages = 1, 109462306a36Sopenharmony_ci .initiator = { 109562306a36Sopenharmony_ci .id = PKVM_ID_HOST, 109662306a36Sopenharmony_ci .addr = host_addr, 109762306a36Sopenharmony_ci .host = { 109862306a36Sopenharmony_ci .completer_addr = hyp_addr, 109962306a36Sopenharmony_ci }, 110062306a36Sopenharmony_ci }, 110162306a36Sopenharmony_ci .completer = { 110262306a36Sopenharmony_ci .id = PKVM_ID_HYP, 110362306a36Sopenharmony_ci }, 110462306a36Sopenharmony_ci }, 110562306a36Sopenharmony_ci .completer_prot = PAGE_HYP, 110662306a36Sopenharmony_ci }; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci host_lock_component(); 110962306a36Sopenharmony_ci hyp_lock_component(); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci ret = do_share(&share); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci hyp_unlock_component(); 111462306a36Sopenharmony_ci host_unlock_component(); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci return ret; 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ciint __pkvm_host_unshare_hyp(u64 pfn) 112062306a36Sopenharmony_ci{ 112162306a36Sopenharmony_ci int ret; 112262306a36Sopenharmony_ci u64 host_addr = hyp_pfn_to_phys(pfn); 112362306a36Sopenharmony_ci u64 hyp_addr = (u64)__hyp_va(host_addr); 112462306a36Sopenharmony_ci struct pkvm_mem_share share = { 112562306a36Sopenharmony_ci .tx = { 112662306a36Sopenharmony_ci .nr_pages = 1, 112762306a36Sopenharmony_ci .initiator = { 112862306a36Sopenharmony_ci .id = PKVM_ID_HOST, 112962306a36Sopenharmony_ci .addr = host_addr, 113062306a36Sopenharmony_ci .host = { 113162306a36Sopenharmony_ci .completer_addr = hyp_addr, 113262306a36Sopenharmony_ci }, 113362306a36Sopenharmony_ci }, 113462306a36Sopenharmony_ci .completer = { 113562306a36Sopenharmony_ci .id = PKVM_ID_HYP, 113662306a36Sopenharmony_ci }, 113762306a36Sopenharmony_ci }, 113862306a36Sopenharmony_ci .completer_prot = PAGE_HYP, 113962306a36Sopenharmony_ci }; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci host_lock_component(); 114262306a36Sopenharmony_ci hyp_lock_component(); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci ret = do_unshare(&share); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci hyp_unlock_component(); 114762306a36Sopenharmony_ci host_unlock_component(); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci return ret; 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ciint __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci int ret; 115562306a36Sopenharmony_ci u64 host_addr = hyp_pfn_to_phys(pfn); 115662306a36Sopenharmony_ci u64 hyp_addr = (u64)__hyp_va(host_addr); 115762306a36Sopenharmony_ci struct pkvm_mem_donation donation = { 115862306a36Sopenharmony_ci .tx = { 115962306a36Sopenharmony_ci .nr_pages = nr_pages, 116062306a36Sopenharmony_ci .initiator = { 116162306a36Sopenharmony_ci .id = PKVM_ID_HOST, 116262306a36Sopenharmony_ci .addr = host_addr, 116362306a36Sopenharmony_ci .host = { 116462306a36Sopenharmony_ci .completer_addr = hyp_addr, 116562306a36Sopenharmony_ci }, 116662306a36Sopenharmony_ci }, 116762306a36Sopenharmony_ci .completer = { 116862306a36Sopenharmony_ci .id = PKVM_ID_HYP, 116962306a36Sopenharmony_ci }, 117062306a36Sopenharmony_ci }, 117162306a36Sopenharmony_ci }; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci host_lock_component(); 117462306a36Sopenharmony_ci hyp_lock_component(); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci ret = do_donate(&donation); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci hyp_unlock_component(); 117962306a36Sopenharmony_ci host_unlock_component(); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci return ret; 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ciint __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci int ret; 118762306a36Sopenharmony_ci u64 host_addr = hyp_pfn_to_phys(pfn); 118862306a36Sopenharmony_ci u64 hyp_addr = (u64)__hyp_va(host_addr); 118962306a36Sopenharmony_ci struct pkvm_mem_donation donation = { 119062306a36Sopenharmony_ci .tx = { 119162306a36Sopenharmony_ci .nr_pages = nr_pages, 119262306a36Sopenharmony_ci .initiator = { 119362306a36Sopenharmony_ci .id = PKVM_ID_HYP, 119462306a36Sopenharmony_ci .addr = hyp_addr, 119562306a36Sopenharmony_ci .hyp = { 119662306a36Sopenharmony_ci .completer_addr = host_addr, 119762306a36Sopenharmony_ci }, 119862306a36Sopenharmony_ci }, 119962306a36Sopenharmony_ci .completer = { 120062306a36Sopenharmony_ci .id = PKVM_ID_HOST, 120162306a36Sopenharmony_ci }, 120262306a36Sopenharmony_ci }, 120362306a36Sopenharmony_ci }; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci host_lock_component(); 120662306a36Sopenharmony_ci hyp_lock_component(); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci ret = do_donate(&donation); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci hyp_unlock_component(); 121162306a36Sopenharmony_ci host_unlock_component(); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci return ret; 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ciint hyp_pin_shared_mem(void *from, void *to) 121762306a36Sopenharmony_ci{ 121862306a36Sopenharmony_ci u64 cur, start = ALIGN_DOWN((u64)from, PAGE_SIZE); 121962306a36Sopenharmony_ci u64 end = PAGE_ALIGN((u64)to); 122062306a36Sopenharmony_ci u64 size = end - start; 122162306a36Sopenharmony_ci int ret; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci host_lock_component(); 122462306a36Sopenharmony_ci hyp_lock_component(); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci ret = __host_check_page_state_range(__hyp_pa(start), size, 122762306a36Sopenharmony_ci PKVM_PAGE_SHARED_OWNED); 122862306a36Sopenharmony_ci if (ret) 122962306a36Sopenharmony_ci goto unlock; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci ret = __hyp_check_page_state_range(start, size, 123262306a36Sopenharmony_ci PKVM_PAGE_SHARED_BORROWED); 123362306a36Sopenharmony_ci if (ret) 123462306a36Sopenharmony_ci goto unlock; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci for (cur = start; cur < end; cur += PAGE_SIZE) 123762306a36Sopenharmony_ci hyp_page_ref_inc(hyp_virt_to_page(cur)); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ciunlock: 124062306a36Sopenharmony_ci hyp_unlock_component(); 124162306a36Sopenharmony_ci host_unlock_component(); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci return ret; 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_civoid hyp_unpin_shared_mem(void *from, void *to) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci u64 cur, start = ALIGN_DOWN((u64)from, PAGE_SIZE); 124962306a36Sopenharmony_ci u64 end = PAGE_ALIGN((u64)to); 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci host_lock_component(); 125262306a36Sopenharmony_ci hyp_lock_component(); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci for (cur = start; cur < end; cur += PAGE_SIZE) 125562306a36Sopenharmony_ci hyp_page_ref_dec(hyp_virt_to_page(cur)); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci hyp_unlock_component(); 125862306a36Sopenharmony_ci host_unlock_component(); 125962306a36Sopenharmony_ci} 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ciint __pkvm_host_share_ffa(u64 pfn, u64 nr_pages) 126262306a36Sopenharmony_ci{ 126362306a36Sopenharmony_ci int ret; 126462306a36Sopenharmony_ci struct pkvm_mem_share share = { 126562306a36Sopenharmony_ci .tx = { 126662306a36Sopenharmony_ci .nr_pages = nr_pages, 126762306a36Sopenharmony_ci .initiator = { 126862306a36Sopenharmony_ci .id = PKVM_ID_HOST, 126962306a36Sopenharmony_ci .addr = hyp_pfn_to_phys(pfn), 127062306a36Sopenharmony_ci }, 127162306a36Sopenharmony_ci .completer = { 127262306a36Sopenharmony_ci .id = PKVM_ID_FFA, 127362306a36Sopenharmony_ci }, 127462306a36Sopenharmony_ci }, 127562306a36Sopenharmony_ci }; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci host_lock_component(); 127862306a36Sopenharmony_ci ret = do_share(&share); 127962306a36Sopenharmony_ci host_unlock_component(); 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci return ret; 128262306a36Sopenharmony_ci} 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ciint __pkvm_host_unshare_ffa(u64 pfn, u64 nr_pages) 128562306a36Sopenharmony_ci{ 128662306a36Sopenharmony_ci int ret; 128762306a36Sopenharmony_ci struct pkvm_mem_share share = { 128862306a36Sopenharmony_ci .tx = { 128962306a36Sopenharmony_ci .nr_pages = nr_pages, 129062306a36Sopenharmony_ci .initiator = { 129162306a36Sopenharmony_ci .id = PKVM_ID_HOST, 129262306a36Sopenharmony_ci .addr = hyp_pfn_to_phys(pfn), 129362306a36Sopenharmony_ci }, 129462306a36Sopenharmony_ci .completer = { 129562306a36Sopenharmony_ci .id = PKVM_ID_FFA, 129662306a36Sopenharmony_ci }, 129762306a36Sopenharmony_ci }, 129862306a36Sopenharmony_ci }; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci host_lock_component(); 130162306a36Sopenharmony_ci ret = do_unshare(&share); 130262306a36Sopenharmony_ci host_unlock_component(); 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci return ret; 130562306a36Sopenharmony_ci} 1306