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(&current_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(&current_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(&current_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(&current_vm->pool, addr);
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic void guest_s2_put_page(void *addr)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	hyp_put_page(&current_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