18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Stand-alone page-table allocator for hyp stage-1 and guest stage-2.
48c2ecf20Sopenharmony_ci * No bombay mix was harmed in the writing of this file.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2020 Google LLC
78c2ecf20Sopenharmony_ci * Author: Will Deacon <will@kernel.org>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
118c2ecf20Sopenharmony_ci#include <asm/kvm_pgtable.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define KVM_PGTABLE_MAX_LEVELS		4U
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define KVM_PTE_VALID			BIT(0)
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define KVM_PTE_TYPE			BIT(1)
188c2ecf20Sopenharmony_ci#define KVM_PTE_TYPE_BLOCK		0
198c2ecf20Sopenharmony_ci#define KVM_PTE_TYPE_PAGE		1
208c2ecf20Sopenharmony_ci#define KVM_PTE_TYPE_TABLE		1
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define KVM_PTE_ADDR_MASK		GENMASK(47, PAGE_SHIFT)
238c2ecf20Sopenharmony_ci#define KVM_PTE_ADDR_51_48		GENMASK(15, 12)
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_LO		GENMASK(11, 2)
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_LO_S1_ATTRIDX	GENMASK(4, 2)
288c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_LO_S1_AP	GENMASK(7, 6)
298c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_LO_S1_AP_RO	3
308c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_LO_S1_AP_RW	1
318c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_LO_S1_SH	GENMASK(9, 8)
328c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_LO_S1_SH_IS	3
338c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_LO_S1_AF	BIT(10)
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_LO_S2_MEMATTR	GENMASK(5, 2)
368c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R	BIT(6)
378c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W	BIT(7)
388c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_LO_S2_SH	GENMASK(9, 8)
398c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_LO_S2_SH_IS	3
408c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_LO_S2_AF	BIT(10)
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_HI		GENMASK(63, 51)
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_HI_S1_XN	BIT(54)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define KVM_PTE_LEAF_ATTR_HI_S2_XN	BIT(54)
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct kvm_pgtable_walk_data {
498c2ecf20Sopenharmony_ci	struct kvm_pgtable		*pgt;
508c2ecf20Sopenharmony_ci	struct kvm_pgtable_walker	*walker;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	u64				addr;
538c2ecf20Sopenharmony_ci	u64				end;
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic u64 kvm_granule_shift(u32 level)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	/* Assumes KVM_PGTABLE_MAX_LEVELS is 4 */
598c2ecf20Sopenharmony_ci	return ARM64_HW_PGTABLE_LEVEL_SHIFT(level);
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic u64 kvm_granule_size(u32 level)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	return BIT(kvm_granule_shift(level));
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic bool kvm_block_mapping_supported(u64 addr, u64 end, u64 phys, u32 level)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	u64 granule = kvm_granule_size(level);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	/*
728c2ecf20Sopenharmony_ci	 * Reject invalid block mappings and don't bother with 4TB mappings for
738c2ecf20Sopenharmony_ci	 * 52-bit PAs.
748c2ecf20Sopenharmony_ci	 */
758c2ecf20Sopenharmony_ci	if (level == 0 || (PAGE_SIZE != SZ_4K && level == 1))
768c2ecf20Sopenharmony_ci		return false;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (granule > (end - addr))
798c2ecf20Sopenharmony_ci		return false;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return IS_ALIGNED(addr, granule) && IS_ALIGNED(phys, granule);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic u32 kvm_pgtable_idx(struct kvm_pgtable_walk_data *data, u32 level)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	u64 shift = kvm_granule_shift(level);
878c2ecf20Sopenharmony_ci	u64 mask = BIT(PAGE_SHIFT - 3) - 1;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return (data->addr >> shift) & mask;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic u32 __kvm_pgd_page_idx(struct kvm_pgtable *pgt, u64 addr)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	u64 shift = kvm_granule_shift(pgt->start_level - 1); /* May underflow */
958c2ecf20Sopenharmony_ci	u64 mask = BIT(pgt->ia_bits) - 1;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return (addr & mask) >> shift;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic u32 kvm_pgd_page_idx(struct kvm_pgtable_walk_data *data)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	return __kvm_pgd_page_idx(data->pgt, data->addr);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic u32 kvm_pgd_pages(u32 ia_bits, u32 start_level)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct kvm_pgtable pgt = {
1088c2ecf20Sopenharmony_ci		.ia_bits	= ia_bits,
1098c2ecf20Sopenharmony_ci		.start_level	= start_level,
1108c2ecf20Sopenharmony_ci	};
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return __kvm_pgd_page_idx(&pgt, -1ULL) + 1;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic bool kvm_pte_valid(kvm_pte_t pte)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	return pte & KVM_PTE_VALID;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic bool kvm_pte_table(kvm_pte_t pte, u32 level)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	if (level == KVM_PGTABLE_MAX_LEVELS - 1)
1238c2ecf20Sopenharmony_ci		return false;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (!kvm_pte_valid(pte))
1268c2ecf20Sopenharmony_ci		return false;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	return FIELD_GET(KVM_PTE_TYPE, pte) == KVM_PTE_TYPE_TABLE;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic u64 kvm_pte_to_phys(kvm_pte_t pte)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	u64 pa = pte & KVM_PTE_ADDR_MASK;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (PAGE_SHIFT == 16)
1368c2ecf20Sopenharmony_ci		pa |= FIELD_GET(KVM_PTE_ADDR_51_48, pte) << 48;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return pa;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic kvm_pte_t kvm_phys_to_pte(u64 pa)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	kvm_pte_t pte = pa & KVM_PTE_ADDR_MASK;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (PAGE_SHIFT == 16)
1468c2ecf20Sopenharmony_ci		pte |= FIELD_PREP(KVM_PTE_ADDR_51_48, pa >> 48);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return pte;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic kvm_pte_t *kvm_pte_follow(kvm_pte_t pte)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	return __va(kvm_pte_to_phys(pte));
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic void kvm_set_invalid_pte(kvm_pte_t *ptep)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	kvm_pte_t pte = *ptep;
1598c2ecf20Sopenharmony_ci	WRITE_ONCE(*ptep, pte & ~KVM_PTE_VALID);
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic void kvm_set_table_pte(kvm_pte_t *ptep, kvm_pte_t *childp)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	kvm_pte_t old = *ptep, pte = kvm_phys_to_pte(__pa(childp));
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	pte |= FIELD_PREP(KVM_PTE_TYPE, KVM_PTE_TYPE_TABLE);
1678c2ecf20Sopenharmony_ci	pte |= KVM_PTE_VALID;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	WARN_ON(kvm_pte_valid(old));
1708c2ecf20Sopenharmony_ci	smp_store_release(ptep, pte);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic bool kvm_set_valid_leaf_pte(kvm_pte_t *ptep, u64 pa, kvm_pte_t attr,
1748c2ecf20Sopenharmony_ci				   u32 level)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	kvm_pte_t old = *ptep, pte = kvm_phys_to_pte(pa);
1778c2ecf20Sopenharmony_ci	u64 type = (level == KVM_PGTABLE_MAX_LEVELS - 1) ? KVM_PTE_TYPE_PAGE :
1788c2ecf20Sopenharmony_ci							   KVM_PTE_TYPE_BLOCK;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	pte |= attr & (KVM_PTE_LEAF_ATTR_LO | KVM_PTE_LEAF_ATTR_HI);
1818c2ecf20Sopenharmony_ci	pte |= FIELD_PREP(KVM_PTE_TYPE, type);
1828c2ecf20Sopenharmony_ci	pte |= KVM_PTE_VALID;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/* Tolerate KVM recreating the exact same mapping. */
1858c2ecf20Sopenharmony_ci	if (kvm_pte_valid(old))
1868c2ecf20Sopenharmony_ci		return old == pte;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	smp_store_release(ptep, pte);
1898c2ecf20Sopenharmony_ci	return true;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int kvm_pgtable_visitor_cb(struct kvm_pgtable_walk_data *data, u64 addr,
1938c2ecf20Sopenharmony_ci				  u32 level, kvm_pte_t *ptep,
1948c2ecf20Sopenharmony_ci				  enum kvm_pgtable_walk_flags flag)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct kvm_pgtable_walker *walker = data->walker;
1978c2ecf20Sopenharmony_ci	return walker->cb(addr, data->end, level, ptep, flag, walker->arg);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic int __kvm_pgtable_walk(struct kvm_pgtable_walk_data *data,
2018c2ecf20Sopenharmony_ci			      kvm_pte_t *pgtable, u32 level);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic inline int __kvm_pgtable_visit(struct kvm_pgtable_walk_data *data,
2048c2ecf20Sopenharmony_ci				      kvm_pte_t *ptep, u32 level)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	int ret = 0;
2078c2ecf20Sopenharmony_ci	u64 addr = data->addr;
2088c2ecf20Sopenharmony_ci	kvm_pte_t *childp, pte = *ptep;
2098c2ecf20Sopenharmony_ci	bool table = kvm_pte_table(pte, level);
2108c2ecf20Sopenharmony_ci	enum kvm_pgtable_walk_flags flags = data->walker->flags;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (table && (flags & KVM_PGTABLE_WALK_TABLE_PRE)) {
2138c2ecf20Sopenharmony_ci		ret = kvm_pgtable_visitor_cb(data, addr, level, ptep,
2148c2ecf20Sopenharmony_ci					     KVM_PGTABLE_WALK_TABLE_PRE);
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (!table && (flags & KVM_PGTABLE_WALK_LEAF)) {
2188c2ecf20Sopenharmony_ci		ret = kvm_pgtable_visitor_cb(data, addr, level, ptep,
2198c2ecf20Sopenharmony_ci					     KVM_PGTABLE_WALK_LEAF);
2208c2ecf20Sopenharmony_ci		pte = *ptep;
2218c2ecf20Sopenharmony_ci		table = kvm_pte_table(pte, level);
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	if (ret)
2258c2ecf20Sopenharmony_ci		goto out;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (!table) {
2288c2ecf20Sopenharmony_ci		data->addr = ALIGN_DOWN(data->addr, kvm_granule_size(level));
2298c2ecf20Sopenharmony_ci		data->addr += kvm_granule_size(level);
2308c2ecf20Sopenharmony_ci		goto out;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	childp = kvm_pte_follow(pte);
2348c2ecf20Sopenharmony_ci	ret = __kvm_pgtable_walk(data, childp, level + 1);
2358c2ecf20Sopenharmony_ci	if (ret)
2368c2ecf20Sopenharmony_ci		goto out;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (flags & KVM_PGTABLE_WALK_TABLE_POST) {
2398c2ecf20Sopenharmony_ci		ret = kvm_pgtable_visitor_cb(data, addr, level, ptep,
2408c2ecf20Sopenharmony_ci					     KVM_PGTABLE_WALK_TABLE_POST);
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ciout:
2448c2ecf20Sopenharmony_ci	return ret;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic int __kvm_pgtable_walk(struct kvm_pgtable_walk_data *data,
2488c2ecf20Sopenharmony_ci			      kvm_pte_t *pgtable, u32 level)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	u32 idx;
2518c2ecf20Sopenharmony_ci	int ret = 0;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(level >= KVM_PGTABLE_MAX_LEVELS))
2548c2ecf20Sopenharmony_ci		return -EINVAL;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	for (idx = kvm_pgtable_idx(data, level); idx < PTRS_PER_PTE; ++idx) {
2578c2ecf20Sopenharmony_ci		kvm_pte_t *ptep = &pgtable[idx];
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci		if (data->addr >= data->end)
2608c2ecf20Sopenharmony_ci			break;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		ret = __kvm_pgtable_visit(data, ptep, level);
2638c2ecf20Sopenharmony_ci		if (ret)
2648c2ecf20Sopenharmony_ci			break;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	return ret;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic int _kvm_pgtable_walk(struct kvm_pgtable_walk_data *data)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	u32 idx;
2738c2ecf20Sopenharmony_ci	int ret = 0;
2748c2ecf20Sopenharmony_ci	struct kvm_pgtable *pgt = data->pgt;
2758c2ecf20Sopenharmony_ci	u64 limit = BIT(pgt->ia_bits);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (data->addr > limit || data->end > limit)
2788c2ecf20Sopenharmony_ci		return -ERANGE;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (!pgt->pgd)
2818c2ecf20Sopenharmony_ci		return -EINVAL;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	for (idx = kvm_pgd_page_idx(data); data->addr < data->end; ++idx) {
2848c2ecf20Sopenharmony_ci		kvm_pte_t *ptep = &pgt->pgd[idx * PTRS_PER_PTE];
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		ret = __kvm_pgtable_walk(data, ptep, pgt->start_level);
2878c2ecf20Sopenharmony_ci		if (ret)
2888c2ecf20Sopenharmony_ci			break;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	return ret;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ciint kvm_pgtable_walk(struct kvm_pgtable *pgt, u64 addr, u64 size,
2958c2ecf20Sopenharmony_ci		     struct kvm_pgtable_walker *walker)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	struct kvm_pgtable_walk_data walk_data = {
2988c2ecf20Sopenharmony_ci		.pgt	= pgt,
2998c2ecf20Sopenharmony_ci		.addr	= ALIGN_DOWN(addr, PAGE_SIZE),
3008c2ecf20Sopenharmony_ci		.end	= PAGE_ALIGN(walk_data.addr + size),
3018c2ecf20Sopenharmony_ci		.walker	= walker,
3028c2ecf20Sopenharmony_ci	};
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return _kvm_pgtable_walk(&walk_data);
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistruct hyp_map_data {
3088c2ecf20Sopenharmony_ci	u64		phys;
3098c2ecf20Sopenharmony_ci	kvm_pte_t	attr;
3108c2ecf20Sopenharmony_ci};
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic int hyp_map_set_prot_attr(enum kvm_pgtable_prot prot,
3138c2ecf20Sopenharmony_ci				 struct hyp_map_data *data)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	bool device = prot & KVM_PGTABLE_PROT_DEVICE;
3168c2ecf20Sopenharmony_ci	u32 mtype = device ? MT_DEVICE_nGnRE : MT_NORMAL;
3178c2ecf20Sopenharmony_ci	kvm_pte_t attr = FIELD_PREP(KVM_PTE_LEAF_ATTR_LO_S1_ATTRIDX, mtype);
3188c2ecf20Sopenharmony_ci	u32 sh = KVM_PTE_LEAF_ATTR_LO_S1_SH_IS;
3198c2ecf20Sopenharmony_ci	u32 ap = (prot & KVM_PGTABLE_PROT_W) ? KVM_PTE_LEAF_ATTR_LO_S1_AP_RW :
3208c2ecf20Sopenharmony_ci					       KVM_PTE_LEAF_ATTR_LO_S1_AP_RO;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (!(prot & KVM_PGTABLE_PROT_R))
3238c2ecf20Sopenharmony_ci		return -EINVAL;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (prot & KVM_PGTABLE_PROT_X) {
3268c2ecf20Sopenharmony_ci		if (prot & KVM_PGTABLE_PROT_W)
3278c2ecf20Sopenharmony_ci			return -EINVAL;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci		if (device)
3308c2ecf20Sopenharmony_ci			return -EINVAL;
3318c2ecf20Sopenharmony_ci	} else {
3328c2ecf20Sopenharmony_ci		attr |= KVM_PTE_LEAF_ATTR_HI_S1_XN;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	attr |= FIELD_PREP(KVM_PTE_LEAF_ATTR_LO_S1_AP, ap);
3368c2ecf20Sopenharmony_ci	attr |= FIELD_PREP(KVM_PTE_LEAF_ATTR_LO_S1_SH, sh);
3378c2ecf20Sopenharmony_ci	attr |= KVM_PTE_LEAF_ATTR_LO_S1_AF;
3388c2ecf20Sopenharmony_ci	data->attr = attr;
3398c2ecf20Sopenharmony_ci	return 0;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic bool hyp_map_walker_try_leaf(u64 addr, u64 end, u32 level,
3438c2ecf20Sopenharmony_ci				    kvm_pte_t *ptep, struct hyp_map_data *data)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	u64 granule = kvm_granule_size(level), phys = data->phys;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (!kvm_block_mapping_supported(addr, end, phys, level))
3488c2ecf20Sopenharmony_ci		return false;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	WARN_ON(!kvm_set_valid_leaf_pte(ptep, phys, data->attr, level));
3518c2ecf20Sopenharmony_ci	data->phys += granule;
3528c2ecf20Sopenharmony_ci	return true;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic int hyp_map_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
3568c2ecf20Sopenharmony_ci			  enum kvm_pgtable_walk_flags flag, void * const arg)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	kvm_pte_t *childp;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (hyp_map_walker_try_leaf(addr, end, level, ptep, arg))
3618c2ecf20Sopenharmony_ci		return 0;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	if (WARN_ON(level == KVM_PGTABLE_MAX_LEVELS - 1))
3648c2ecf20Sopenharmony_ci		return -EINVAL;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	childp = (kvm_pte_t *)get_zeroed_page(GFP_KERNEL);
3678c2ecf20Sopenharmony_ci	if (!childp)
3688c2ecf20Sopenharmony_ci		return -ENOMEM;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	kvm_set_table_pte(ptep, childp);
3718c2ecf20Sopenharmony_ci	return 0;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ciint kvm_pgtable_hyp_map(struct kvm_pgtable *pgt, u64 addr, u64 size, u64 phys,
3758c2ecf20Sopenharmony_ci			enum kvm_pgtable_prot prot)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	int ret;
3788c2ecf20Sopenharmony_ci	struct hyp_map_data map_data = {
3798c2ecf20Sopenharmony_ci		.phys	= ALIGN_DOWN(phys, PAGE_SIZE),
3808c2ecf20Sopenharmony_ci	};
3818c2ecf20Sopenharmony_ci	struct kvm_pgtable_walker walker = {
3828c2ecf20Sopenharmony_ci		.cb	= hyp_map_walker,
3838c2ecf20Sopenharmony_ci		.flags	= KVM_PGTABLE_WALK_LEAF,
3848c2ecf20Sopenharmony_ci		.arg	= &map_data,
3858c2ecf20Sopenharmony_ci	};
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	ret = hyp_map_set_prot_attr(prot, &map_data);
3888c2ecf20Sopenharmony_ci	if (ret)
3898c2ecf20Sopenharmony_ci		return ret;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	ret = kvm_pgtable_walk(pgt, addr, size, &walker);
3928c2ecf20Sopenharmony_ci	dsb(ishst);
3938c2ecf20Sopenharmony_ci	isb();
3948c2ecf20Sopenharmony_ci	return ret;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ciint kvm_pgtable_hyp_init(struct kvm_pgtable *pgt, u32 va_bits)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	u64 levels = ARM64_HW_PGTABLE_LEVELS(va_bits);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	pgt->pgd = (kvm_pte_t *)get_zeroed_page(GFP_KERNEL);
4028c2ecf20Sopenharmony_ci	if (!pgt->pgd)
4038c2ecf20Sopenharmony_ci		return -ENOMEM;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	pgt->ia_bits		= va_bits;
4068c2ecf20Sopenharmony_ci	pgt->start_level	= KVM_PGTABLE_MAX_LEVELS - levels;
4078c2ecf20Sopenharmony_ci	pgt->mmu		= NULL;
4088c2ecf20Sopenharmony_ci	return 0;
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic int hyp_free_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
4128c2ecf20Sopenharmony_ci			   enum kvm_pgtable_walk_flags flag, void * const arg)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	free_page((unsigned long)kvm_pte_follow(*ptep));
4158c2ecf20Sopenharmony_ci	return 0;
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_civoid kvm_pgtable_hyp_destroy(struct kvm_pgtable *pgt)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	struct kvm_pgtable_walker walker = {
4218c2ecf20Sopenharmony_ci		.cb	= hyp_free_walker,
4228c2ecf20Sopenharmony_ci		.flags	= KVM_PGTABLE_WALK_TABLE_POST,
4238c2ecf20Sopenharmony_ci	};
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	WARN_ON(kvm_pgtable_walk(pgt, 0, BIT(pgt->ia_bits), &walker));
4268c2ecf20Sopenharmony_ci	free_page((unsigned long)pgt->pgd);
4278c2ecf20Sopenharmony_ci	pgt->pgd = NULL;
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistruct stage2_map_data {
4318c2ecf20Sopenharmony_ci	u64				phys;
4328c2ecf20Sopenharmony_ci	kvm_pte_t			attr;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	kvm_pte_t			*anchor;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	struct kvm_s2_mmu		*mmu;
4378c2ecf20Sopenharmony_ci	struct kvm_mmu_memory_cache	*memcache;
4388c2ecf20Sopenharmony_ci};
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic int stage2_map_set_prot_attr(enum kvm_pgtable_prot prot,
4418c2ecf20Sopenharmony_ci				    struct stage2_map_data *data)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	bool device = prot & KVM_PGTABLE_PROT_DEVICE;
4448c2ecf20Sopenharmony_ci	kvm_pte_t attr = device ? PAGE_S2_MEMATTR(DEVICE_nGnRE) :
4458c2ecf20Sopenharmony_ci			    PAGE_S2_MEMATTR(NORMAL);
4468c2ecf20Sopenharmony_ci	u32 sh = KVM_PTE_LEAF_ATTR_LO_S2_SH_IS;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if (!(prot & KVM_PGTABLE_PROT_X))
4498c2ecf20Sopenharmony_ci		attr |= KVM_PTE_LEAF_ATTR_HI_S2_XN;
4508c2ecf20Sopenharmony_ci	else if (device)
4518c2ecf20Sopenharmony_ci		return -EINVAL;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if (prot & KVM_PGTABLE_PROT_R)
4548c2ecf20Sopenharmony_ci		attr |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	if (prot & KVM_PGTABLE_PROT_W)
4578c2ecf20Sopenharmony_ci		attr |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	attr |= FIELD_PREP(KVM_PTE_LEAF_ATTR_LO_S2_SH, sh);
4608c2ecf20Sopenharmony_ci	attr |= KVM_PTE_LEAF_ATTR_LO_S2_AF;
4618c2ecf20Sopenharmony_ci	data->attr = attr;
4628c2ecf20Sopenharmony_ci	return 0;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic bool stage2_map_walker_try_leaf(u64 addr, u64 end, u32 level,
4668c2ecf20Sopenharmony_ci				       kvm_pte_t *ptep,
4678c2ecf20Sopenharmony_ci				       struct stage2_map_data *data)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	u64 granule = kvm_granule_size(level), phys = data->phys;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if (!kvm_block_mapping_supported(addr, end, phys, level))
4728c2ecf20Sopenharmony_ci		return false;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/*
4758c2ecf20Sopenharmony_ci	 * If the PTE was already valid, drop the refcount on the table
4768c2ecf20Sopenharmony_ci	 * early, as it will be bumped-up again in stage2_map_walk_leaf().
4778c2ecf20Sopenharmony_ci	 * This ensures that the refcount stays constant across a valid to
4788c2ecf20Sopenharmony_ci	 * valid PTE update.
4798c2ecf20Sopenharmony_ci	 */
4808c2ecf20Sopenharmony_ci	if (kvm_pte_valid(*ptep))
4818c2ecf20Sopenharmony_ci		put_page(virt_to_page(ptep));
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (kvm_set_valid_leaf_pte(ptep, phys, data->attr, level))
4848c2ecf20Sopenharmony_ci		goto out;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	/* There's an existing valid leaf entry, so perform break-before-make */
4878c2ecf20Sopenharmony_ci	kvm_set_invalid_pte(ptep);
4888c2ecf20Sopenharmony_ci	kvm_call_hyp(__kvm_tlb_flush_vmid_ipa, data->mmu, addr, level);
4898c2ecf20Sopenharmony_ci	kvm_set_valid_leaf_pte(ptep, phys, data->attr, level);
4908c2ecf20Sopenharmony_ciout:
4918c2ecf20Sopenharmony_ci	data->phys += granule;
4928c2ecf20Sopenharmony_ci	return true;
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic int stage2_map_walk_table_pre(u64 addr, u64 end, u32 level,
4968c2ecf20Sopenharmony_ci				     kvm_pte_t *ptep,
4978c2ecf20Sopenharmony_ci				     struct stage2_map_data *data)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	if (data->anchor)
5008c2ecf20Sopenharmony_ci		return 0;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	if (!kvm_block_mapping_supported(addr, end, data->phys, level))
5038c2ecf20Sopenharmony_ci		return 0;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	kvm_set_invalid_pte(ptep);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	/*
5088c2ecf20Sopenharmony_ci	 * Invalidate the whole stage-2, as we may have numerous leaf
5098c2ecf20Sopenharmony_ci	 * entries below us which would otherwise need invalidating
5108c2ecf20Sopenharmony_ci	 * individually.
5118c2ecf20Sopenharmony_ci	 */
5128c2ecf20Sopenharmony_ci	kvm_call_hyp(__kvm_tlb_flush_vmid, data->mmu);
5138c2ecf20Sopenharmony_ci	data->anchor = ptep;
5148c2ecf20Sopenharmony_ci	return 0;
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic int stage2_map_walk_leaf(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
5188c2ecf20Sopenharmony_ci				struct stage2_map_data *data)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	kvm_pte_t *childp, pte = *ptep;
5218c2ecf20Sopenharmony_ci	struct page *page = virt_to_page(ptep);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	if (data->anchor) {
5248c2ecf20Sopenharmony_ci		if (kvm_pte_valid(pte))
5258c2ecf20Sopenharmony_ci			put_page(page);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci		return 0;
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	if (stage2_map_walker_try_leaf(addr, end, level, ptep, data))
5318c2ecf20Sopenharmony_ci		goto out_get_page;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	if (WARN_ON(level == KVM_PGTABLE_MAX_LEVELS - 1))
5348c2ecf20Sopenharmony_ci		return -EINVAL;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	if (!data->memcache)
5378c2ecf20Sopenharmony_ci		return -ENOMEM;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	childp = kvm_mmu_memory_cache_alloc(data->memcache);
5408c2ecf20Sopenharmony_ci	if (!childp)
5418c2ecf20Sopenharmony_ci		return -ENOMEM;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	/*
5448c2ecf20Sopenharmony_ci	 * If we've run into an existing block mapping then replace it with
5458c2ecf20Sopenharmony_ci	 * a table. Accesses beyond 'end' that fall within the new table
5468c2ecf20Sopenharmony_ci	 * will be mapped lazily.
5478c2ecf20Sopenharmony_ci	 */
5488c2ecf20Sopenharmony_ci	if (kvm_pte_valid(pte)) {
5498c2ecf20Sopenharmony_ci		kvm_set_invalid_pte(ptep);
5508c2ecf20Sopenharmony_ci		kvm_call_hyp(__kvm_tlb_flush_vmid_ipa, data->mmu, addr, level);
5518c2ecf20Sopenharmony_ci		put_page(page);
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	kvm_set_table_pte(ptep, childp);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ciout_get_page:
5578c2ecf20Sopenharmony_ci	get_page(page);
5588c2ecf20Sopenharmony_ci	return 0;
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic int stage2_map_walk_table_post(u64 addr, u64 end, u32 level,
5628c2ecf20Sopenharmony_ci				      kvm_pte_t *ptep,
5638c2ecf20Sopenharmony_ci				      struct stage2_map_data *data)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	int ret = 0;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	if (!data->anchor)
5688c2ecf20Sopenharmony_ci		return 0;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	free_page((unsigned long)kvm_pte_follow(*ptep));
5718c2ecf20Sopenharmony_ci	put_page(virt_to_page(ptep));
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	if (data->anchor == ptep) {
5748c2ecf20Sopenharmony_ci		data->anchor = NULL;
5758c2ecf20Sopenharmony_ci		ret = stage2_map_walk_leaf(addr, end, level, ptep, data);
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	return ret;
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci/*
5828c2ecf20Sopenharmony_ci * This is a little fiddly, as we use all three of the walk flags. The idea
5838c2ecf20Sopenharmony_ci * is that the TABLE_PRE callback runs for table entries on the way down,
5848c2ecf20Sopenharmony_ci * looking for table entries which we could conceivably replace with a
5858c2ecf20Sopenharmony_ci * block entry for this mapping. If it finds one, then it sets the 'anchor'
5868c2ecf20Sopenharmony_ci * field in 'struct stage2_map_data' to point at the table entry, before
5878c2ecf20Sopenharmony_ci * clearing the entry to zero and descending into the now detached table.
5888c2ecf20Sopenharmony_ci *
5898c2ecf20Sopenharmony_ci * The behaviour of the LEAF callback then depends on whether or not the
5908c2ecf20Sopenharmony_ci * anchor has been set. If not, then we're not using a block mapping higher
5918c2ecf20Sopenharmony_ci * up the table and we perform the mapping at the existing leaves instead.
5928c2ecf20Sopenharmony_ci * If, on the other hand, the anchor _is_ set, then we drop references to
5938c2ecf20Sopenharmony_ci * all valid leaves so that the pages beneath the anchor can be freed.
5948c2ecf20Sopenharmony_ci *
5958c2ecf20Sopenharmony_ci * Finally, the TABLE_POST callback does nothing if the anchor has not
5968c2ecf20Sopenharmony_ci * been set, but otherwise frees the page-table pages while walking back up
5978c2ecf20Sopenharmony_ci * the page-table, installing the block entry when it revisits the anchor
5988c2ecf20Sopenharmony_ci * pointer and clearing the anchor to NULL.
5998c2ecf20Sopenharmony_ci */
6008c2ecf20Sopenharmony_cistatic int stage2_map_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
6018c2ecf20Sopenharmony_ci			     enum kvm_pgtable_walk_flags flag, void * const arg)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	struct stage2_map_data *data = arg;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	switch (flag) {
6068c2ecf20Sopenharmony_ci	case KVM_PGTABLE_WALK_TABLE_PRE:
6078c2ecf20Sopenharmony_ci		return stage2_map_walk_table_pre(addr, end, level, ptep, data);
6088c2ecf20Sopenharmony_ci	case KVM_PGTABLE_WALK_LEAF:
6098c2ecf20Sopenharmony_ci		return stage2_map_walk_leaf(addr, end, level, ptep, data);
6108c2ecf20Sopenharmony_ci	case KVM_PGTABLE_WALK_TABLE_POST:
6118c2ecf20Sopenharmony_ci		return stage2_map_walk_table_post(addr, end, level, ptep, data);
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	return -EINVAL;
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ciint kvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
6188c2ecf20Sopenharmony_ci			   u64 phys, enum kvm_pgtable_prot prot,
6198c2ecf20Sopenharmony_ci			   struct kvm_mmu_memory_cache *mc)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	int ret;
6228c2ecf20Sopenharmony_ci	struct stage2_map_data map_data = {
6238c2ecf20Sopenharmony_ci		.phys		= ALIGN_DOWN(phys, PAGE_SIZE),
6248c2ecf20Sopenharmony_ci		.mmu		= pgt->mmu,
6258c2ecf20Sopenharmony_ci		.memcache	= mc,
6268c2ecf20Sopenharmony_ci	};
6278c2ecf20Sopenharmony_ci	struct kvm_pgtable_walker walker = {
6288c2ecf20Sopenharmony_ci		.cb		= stage2_map_walker,
6298c2ecf20Sopenharmony_ci		.flags		= KVM_PGTABLE_WALK_TABLE_PRE |
6308c2ecf20Sopenharmony_ci				  KVM_PGTABLE_WALK_LEAF |
6318c2ecf20Sopenharmony_ci				  KVM_PGTABLE_WALK_TABLE_POST,
6328c2ecf20Sopenharmony_ci		.arg		= &map_data,
6338c2ecf20Sopenharmony_ci	};
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	ret = stage2_map_set_prot_attr(prot, &map_data);
6368c2ecf20Sopenharmony_ci	if (ret)
6378c2ecf20Sopenharmony_ci		return ret;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	ret = kvm_pgtable_walk(pgt, addr, size, &walker);
6408c2ecf20Sopenharmony_ci	dsb(ishst);
6418c2ecf20Sopenharmony_ci	return ret;
6428c2ecf20Sopenharmony_ci}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_cistatic void stage2_flush_dcache(void *addr, u64 size)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	if (cpus_have_const_cap(ARM64_HAS_STAGE2_FWB))
6478c2ecf20Sopenharmony_ci		return;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	__flush_dcache_area(addr, size);
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic bool stage2_pte_cacheable(kvm_pte_t pte)
6538c2ecf20Sopenharmony_ci{
6548c2ecf20Sopenharmony_ci	u64 memattr = pte & KVM_PTE_LEAF_ATTR_LO_S2_MEMATTR;
6558c2ecf20Sopenharmony_ci	return memattr == PAGE_S2_MEMATTR(NORMAL);
6568c2ecf20Sopenharmony_ci}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_cistatic int stage2_unmap_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
6598c2ecf20Sopenharmony_ci			       enum kvm_pgtable_walk_flags flag,
6608c2ecf20Sopenharmony_ci			       void * const arg)
6618c2ecf20Sopenharmony_ci{
6628c2ecf20Sopenharmony_ci	struct kvm_s2_mmu *mmu = arg;
6638c2ecf20Sopenharmony_ci	kvm_pte_t pte = *ptep, *childp = NULL;
6648c2ecf20Sopenharmony_ci	bool need_flush = false;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	if (!kvm_pte_valid(pte))
6678c2ecf20Sopenharmony_ci		return 0;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	if (kvm_pte_table(pte, level)) {
6708c2ecf20Sopenharmony_ci		childp = kvm_pte_follow(pte);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci		if (page_count(virt_to_page(childp)) != 1)
6738c2ecf20Sopenharmony_ci			return 0;
6748c2ecf20Sopenharmony_ci	} else if (stage2_pte_cacheable(pte)) {
6758c2ecf20Sopenharmony_ci		need_flush = true;
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	/*
6798c2ecf20Sopenharmony_ci	 * This is similar to the map() path in that we unmap the entire
6808c2ecf20Sopenharmony_ci	 * block entry and rely on the remaining portions being faulted
6818c2ecf20Sopenharmony_ci	 * back lazily.
6828c2ecf20Sopenharmony_ci	 */
6838c2ecf20Sopenharmony_ci	kvm_set_invalid_pte(ptep);
6848c2ecf20Sopenharmony_ci	kvm_call_hyp(__kvm_tlb_flush_vmid_ipa, mmu, addr, level);
6858c2ecf20Sopenharmony_ci	put_page(virt_to_page(ptep));
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	if (need_flush) {
6888c2ecf20Sopenharmony_ci		stage2_flush_dcache(kvm_pte_follow(pte),
6898c2ecf20Sopenharmony_ci				    kvm_granule_size(level));
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	if (childp)
6938c2ecf20Sopenharmony_ci		free_page((unsigned long)childp);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	return 0;
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ciint kvm_pgtable_stage2_unmap(struct kvm_pgtable *pgt, u64 addr, u64 size)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	struct kvm_pgtable_walker walker = {
7018c2ecf20Sopenharmony_ci		.cb	= stage2_unmap_walker,
7028c2ecf20Sopenharmony_ci		.arg	= pgt->mmu,
7038c2ecf20Sopenharmony_ci		.flags	= KVM_PGTABLE_WALK_LEAF | KVM_PGTABLE_WALK_TABLE_POST,
7048c2ecf20Sopenharmony_ci	};
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	return kvm_pgtable_walk(pgt, addr, size, &walker);
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_cistruct stage2_attr_data {
7108c2ecf20Sopenharmony_ci	kvm_pte_t	attr_set;
7118c2ecf20Sopenharmony_ci	kvm_pte_t	attr_clr;
7128c2ecf20Sopenharmony_ci	kvm_pte_t	pte;
7138c2ecf20Sopenharmony_ci	u32		level;
7148c2ecf20Sopenharmony_ci};
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_cistatic int stage2_attr_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
7178c2ecf20Sopenharmony_ci			      enum kvm_pgtable_walk_flags flag,
7188c2ecf20Sopenharmony_ci			      void * const arg)
7198c2ecf20Sopenharmony_ci{
7208c2ecf20Sopenharmony_ci	kvm_pte_t pte = *ptep;
7218c2ecf20Sopenharmony_ci	struct stage2_attr_data *data = arg;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	if (!kvm_pte_valid(pte))
7248c2ecf20Sopenharmony_ci		return 0;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	data->level = level;
7278c2ecf20Sopenharmony_ci	data->pte = pte;
7288c2ecf20Sopenharmony_ci	pte &= ~data->attr_clr;
7298c2ecf20Sopenharmony_ci	pte |= data->attr_set;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	/*
7328c2ecf20Sopenharmony_ci	 * We may race with the CPU trying to set the access flag here,
7338c2ecf20Sopenharmony_ci	 * but worst-case the access flag update gets lost and will be
7348c2ecf20Sopenharmony_ci	 * set on the next access instead.
7358c2ecf20Sopenharmony_ci	 */
7368c2ecf20Sopenharmony_ci	if (data->pte != pte)
7378c2ecf20Sopenharmony_ci		WRITE_ONCE(*ptep, pte);
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	return 0;
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_cistatic int stage2_update_leaf_attrs(struct kvm_pgtable *pgt, u64 addr,
7438c2ecf20Sopenharmony_ci				    u64 size, kvm_pte_t attr_set,
7448c2ecf20Sopenharmony_ci				    kvm_pte_t attr_clr, kvm_pte_t *orig_pte,
7458c2ecf20Sopenharmony_ci				    u32 *level)
7468c2ecf20Sopenharmony_ci{
7478c2ecf20Sopenharmony_ci	int ret;
7488c2ecf20Sopenharmony_ci	kvm_pte_t attr_mask = KVM_PTE_LEAF_ATTR_LO | KVM_PTE_LEAF_ATTR_HI;
7498c2ecf20Sopenharmony_ci	struct stage2_attr_data data = {
7508c2ecf20Sopenharmony_ci		.attr_set	= attr_set & attr_mask,
7518c2ecf20Sopenharmony_ci		.attr_clr	= attr_clr & attr_mask,
7528c2ecf20Sopenharmony_ci	};
7538c2ecf20Sopenharmony_ci	struct kvm_pgtable_walker walker = {
7548c2ecf20Sopenharmony_ci		.cb		= stage2_attr_walker,
7558c2ecf20Sopenharmony_ci		.arg		= &data,
7568c2ecf20Sopenharmony_ci		.flags		= KVM_PGTABLE_WALK_LEAF,
7578c2ecf20Sopenharmony_ci	};
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	ret = kvm_pgtable_walk(pgt, addr, size, &walker);
7608c2ecf20Sopenharmony_ci	if (ret)
7618c2ecf20Sopenharmony_ci		return ret;
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	if (orig_pte)
7648c2ecf20Sopenharmony_ci		*orig_pte = data.pte;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	if (level)
7678c2ecf20Sopenharmony_ci		*level = data.level;
7688c2ecf20Sopenharmony_ci	return 0;
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ciint kvm_pgtable_stage2_wrprotect(struct kvm_pgtable *pgt, u64 addr, u64 size)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	return stage2_update_leaf_attrs(pgt, addr, size, 0,
7748c2ecf20Sopenharmony_ci					KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W,
7758c2ecf20Sopenharmony_ci					NULL, NULL);
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_cikvm_pte_t kvm_pgtable_stage2_mkyoung(struct kvm_pgtable *pgt, u64 addr)
7798c2ecf20Sopenharmony_ci{
7808c2ecf20Sopenharmony_ci	kvm_pte_t pte = 0;
7818c2ecf20Sopenharmony_ci	stage2_update_leaf_attrs(pgt, addr, 1, KVM_PTE_LEAF_ATTR_LO_S2_AF, 0,
7828c2ecf20Sopenharmony_ci				 &pte, NULL);
7838c2ecf20Sopenharmony_ci	dsb(ishst);
7848c2ecf20Sopenharmony_ci	return pte;
7858c2ecf20Sopenharmony_ci}
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_cikvm_pte_t kvm_pgtable_stage2_mkold(struct kvm_pgtable *pgt, u64 addr)
7888c2ecf20Sopenharmony_ci{
7898c2ecf20Sopenharmony_ci	kvm_pte_t pte = 0;
7908c2ecf20Sopenharmony_ci	stage2_update_leaf_attrs(pgt, addr, 1, 0, KVM_PTE_LEAF_ATTR_LO_S2_AF,
7918c2ecf20Sopenharmony_ci				 &pte, NULL);
7928c2ecf20Sopenharmony_ci	/*
7938c2ecf20Sopenharmony_ci	 * "But where's the TLBI?!", you scream.
7948c2ecf20Sopenharmony_ci	 * "Over in the core code", I sigh.
7958c2ecf20Sopenharmony_ci	 *
7968c2ecf20Sopenharmony_ci	 * See the '->clear_flush_young()' callback on the KVM mmu notifier.
7978c2ecf20Sopenharmony_ci	 */
7988c2ecf20Sopenharmony_ci	return pte;
7998c2ecf20Sopenharmony_ci}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_cibool kvm_pgtable_stage2_is_young(struct kvm_pgtable *pgt, u64 addr)
8028c2ecf20Sopenharmony_ci{
8038c2ecf20Sopenharmony_ci	kvm_pte_t pte = 0;
8048c2ecf20Sopenharmony_ci	stage2_update_leaf_attrs(pgt, addr, 1, 0, 0, &pte, NULL);
8058c2ecf20Sopenharmony_ci	return pte & KVM_PTE_LEAF_ATTR_LO_S2_AF;
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ciint kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr,
8098c2ecf20Sopenharmony_ci				   enum kvm_pgtable_prot prot)
8108c2ecf20Sopenharmony_ci{
8118c2ecf20Sopenharmony_ci	int ret;
8128c2ecf20Sopenharmony_ci	u32 level;
8138c2ecf20Sopenharmony_ci	kvm_pte_t set = 0, clr = 0;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	if (prot & KVM_PGTABLE_PROT_R)
8168c2ecf20Sopenharmony_ci		set |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	if (prot & KVM_PGTABLE_PROT_W)
8198c2ecf20Sopenharmony_ci		set |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	if (prot & KVM_PGTABLE_PROT_X)
8228c2ecf20Sopenharmony_ci		clr |= KVM_PTE_LEAF_ATTR_HI_S2_XN;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	ret = stage2_update_leaf_attrs(pgt, addr, 1, set, clr, NULL, &level);
8258c2ecf20Sopenharmony_ci	if (!ret)
8268c2ecf20Sopenharmony_ci		kvm_call_hyp(__kvm_tlb_flush_vmid_ipa, pgt->mmu, addr, level);
8278c2ecf20Sopenharmony_ci	return ret;
8288c2ecf20Sopenharmony_ci}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_cistatic int stage2_flush_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
8318c2ecf20Sopenharmony_ci			       enum kvm_pgtable_walk_flags flag,
8328c2ecf20Sopenharmony_ci			       void * const arg)
8338c2ecf20Sopenharmony_ci{
8348c2ecf20Sopenharmony_ci	kvm_pte_t pte = *ptep;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	if (!kvm_pte_valid(pte) || !stage2_pte_cacheable(pte))
8378c2ecf20Sopenharmony_ci		return 0;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	stage2_flush_dcache(kvm_pte_follow(pte), kvm_granule_size(level));
8408c2ecf20Sopenharmony_ci	return 0;
8418c2ecf20Sopenharmony_ci}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ciint kvm_pgtable_stage2_flush(struct kvm_pgtable *pgt, u64 addr, u64 size)
8448c2ecf20Sopenharmony_ci{
8458c2ecf20Sopenharmony_ci	struct kvm_pgtable_walker walker = {
8468c2ecf20Sopenharmony_ci		.cb	= stage2_flush_walker,
8478c2ecf20Sopenharmony_ci		.flags	= KVM_PGTABLE_WALK_LEAF,
8488c2ecf20Sopenharmony_ci	};
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	if (cpus_have_const_cap(ARM64_HAS_STAGE2_FWB))
8518c2ecf20Sopenharmony_ci		return 0;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	return kvm_pgtable_walk(pgt, addr, size, &walker);
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ciint kvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm *kvm)
8578c2ecf20Sopenharmony_ci{
8588c2ecf20Sopenharmony_ci	size_t pgd_sz;
8598c2ecf20Sopenharmony_ci	u64 vtcr = kvm->arch.vtcr;
8608c2ecf20Sopenharmony_ci	u32 ia_bits = VTCR_EL2_IPA(vtcr);
8618c2ecf20Sopenharmony_ci	u32 sl0 = FIELD_GET(VTCR_EL2_SL0_MASK, vtcr);
8628c2ecf20Sopenharmony_ci	u32 start_level = VTCR_EL2_TGRAN_SL0_BASE - sl0;
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	pgd_sz = kvm_pgd_pages(ia_bits, start_level) * PAGE_SIZE;
8658c2ecf20Sopenharmony_ci	pgt->pgd = alloc_pages_exact(pgd_sz, GFP_KERNEL_ACCOUNT | __GFP_ZERO);
8668c2ecf20Sopenharmony_ci	if (!pgt->pgd)
8678c2ecf20Sopenharmony_ci		return -ENOMEM;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	pgt->ia_bits		= ia_bits;
8708c2ecf20Sopenharmony_ci	pgt->start_level	= start_level;
8718c2ecf20Sopenharmony_ci	pgt->mmu		= &kvm->arch.mmu;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	/* Ensure zeroed PGD pages are visible to the hardware walker */
8748c2ecf20Sopenharmony_ci	dsb(ishst);
8758c2ecf20Sopenharmony_ci	return 0;
8768c2ecf20Sopenharmony_ci}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_cistatic int stage2_free_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
8798c2ecf20Sopenharmony_ci			      enum kvm_pgtable_walk_flags flag,
8808c2ecf20Sopenharmony_ci			      void * const arg)
8818c2ecf20Sopenharmony_ci{
8828c2ecf20Sopenharmony_ci	kvm_pte_t pte = *ptep;
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	if (!kvm_pte_valid(pte))
8858c2ecf20Sopenharmony_ci		return 0;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	put_page(virt_to_page(ptep));
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	if (kvm_pte_table(pte, level))
8908c2ecf20Sopenharmony_ci		free_page((unsigned long)kvm_pte_follow(pte));
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	return 0;
8938c2ecf20Sopenharmony_ci}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_civoid kvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt)
8968c2ecf20Sopenharmony_ci{
8978c2ecf20Sopenharmony_ci	size_t pgd_sz;
8988c2ecf20Sopenharmony_ci	struct kvm_pgtable_walker walker = {
8998c2ecf20Sopenharmony_ci		.cb	= stage2_free_walker,
9008c2ecf20Sopenharmony_ci		.flags	= KVM_PGTABLE_WALK_LEAF |
9018c2ecf20Sopenharmony_ci			  KVM_PGTABLE_WALK_TABLE_POST,
9028c2ecf20Sopenharmony_ci	};
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	WARN_ON(kvm_pgtable_walk(pgt, 0, BIT(pgt->ia_bits), &walker));
9058c2ecf20Sopenharmony_ci	pgd_sz = kvm_pgd_pages(pgt->ia_bits, pgt->start_level) * PAGE_SIZE;
9068c2ecf20Sopenharmony_ci	free_pages_exact(pgt->pgd, pgd_sz);
9078c2ecf20Sopenharmony_ci	pgt->pgd = NULL;
9088c2ecf20Sopenharmony_ci}
909