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