18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * arch/arm64/mm/hugetlbpage.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Linaro Ltd. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on arch/x86/mm/hugetlbpage.c. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/fs.h> 128c2ecf20Sopenharmony_ci#include <linux/mm.h> 138c2ecf20Sopenharmony_ci#include <linux/hugetlb.h> 148c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 178c2ecf20Sopenharmony_ci#include <asm/mman.h> 188c2ecf20Sopenharmony_ci#include <asm/tlb.h> 198c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * HugeTLB Support Matrix 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * --------------------------------------------------- 258c2ecf20Sopenharmony_ci * | Page Size | CONT PTE | PMD | CONT PMD | PUD | 268c2ecf20Sopenharmony_ci * --------------------------------------------------- 278c2ecf20Sopenharmony_ci * | 4K | 64K | 2M | 32M | 1G | 288c2ecf20Sopenharmony_ci * | 16K | 2M | 32M | 1G | | 298c2ecf20Sopenharmony_ci * | 64K | 2M | 512M | 16G | | 308c2ecf20Sopenharmony_ci * --------------------------------------------------- 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * Reserve CMA areas for the largest supported gigantic 358c2ecf20Sopenharmony_ci * huge page when requested. Any other smaller gigantic 368c2ecf20Sopenharmony_ci * huge pages could still be served from those areas. 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci#ifdef CONFIG_CMA 398c2ecf20Sopenharmony_civoid __init arm64_hugetlb_cma_reserve(void) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci int order; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64_4K_PAGES 448c2ecf20Sopenharmony_ci order = PUD_SHIFT - PAGE_SHIFT; 458c2ecf20Sopenharmony_ci#else 468c2ecf20Sopenharmony_ci order = CONT_PMD_SHIFT - PAGE_SHIFT; 478c2ecf20Sopenharmony_ci#endif 488c2ecf20Sopenharmony_ci /* 498c2ecf20Sopenharmony_ci * HugeTLB CMA reservation is required for gigantic 508c2ecf20Sopenharmony_ci * huge pages which could not be allocated via the 518c2ecf20Sopenharmony_ci * page allocator. Just warn if there is any change 528c2ecf20Sopenharmony_ci * breaking this assumption. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci WARN_ON(order <= MAX_ORDER); 558c2ecf20Sopenharmony_ci hugetlb_cma_reserve(order); 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci#endif /* CONFIG_CMA */ 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION 608c2ecf20Sopenharmony_cibool arch_hugetlb_migration_supported(struct hstate *h) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci size_t pagesize = huge_page_size(h); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci switch (pagesize) { 658c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64_4K_PAGES 668c2ecf20Sopenharmony_ci case PUD_SIZE: 678c2ecf20Sopenharmony_ci#endif 688c2ecf20Sopenharmony_ci case PMD_SIZE: 698c2ecf20Sopenharmony_ci case CONT_PMD_SIZE: 708c2ecf20Sopenharmony_ci case CONT_PTE_SIZE: 718c2ecf20Sopenharmony_ci return true; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci pr_warn("%s: unrecognized huge page size 0x%lx\n", 748c2ecf20Sopenharmony_ci __func__, pagesize); 758c2ecf20Sopenharmony_ci return false; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci#endif 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ciint pmd_huge(pmd_t pmd) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci return pmd_val(pmd) && !(pmd_val(pmd) & PMD_TABLE_BIT); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ciint pud_huge(pud_t pud) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED 878c2ecf20Sopenharmony_ci return pud_val(pud) && !(pud_val(pud) & PUD_TABLE_BIT); 888c2ecf20Sopenharmony_ci#else 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci#endif 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* 948c2ecf20Sopenharmony_ci * Select all bits except the pfn 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_cistatic inline pgprot_t pte_pgprot(pte_t pte) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci unsigned long pfn = pte_pfn(pte); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return __pgprot(pte_val(pfn_pte(pfn, __pgprot(0))) ^ pte_val(pte)); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int find_num_contig(struct mm_struct *mm, unsigned long addr, 1048c2ecf20Sopenharmony_ci pte_t *ptep, size_t *pgsize) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci pgd_t *pgdp = pgd_offset(mm, addr); 1078c2ecf20Sopenharmony_ci p4d_t *p4dp; 1088c2ecf20Sopenharmony_ci pud_t *pudp; 1098c2ecf20Sopenharmony_ci pmd_t *pmdp; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci *pgsize = PAGE_SIZE; 1128c2ecf20Sopenharmony_ci p4dp = p4d_offset(pgdp, addr); 1138c2ecf20Sopenharmony_ci pudp = pud_offset(p4dp, addr); 1148c2ecf20Sopenharmony_ci pmdp = pmd_offset(pudp, addr); 1158c2ecf20Sopenharmony_ci if ((pte_t *)pmdp == ptep) { 1168c2ecf20Sopenharmony_ci *pgsize = PMD_SIZE; 1178c2ecf20Sopenharmony_ci return CONT_PMDS; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci return CONT_PTES; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic inline int num_contig_ptes(unsigned long size, size_t *pgsize) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci int contig_ptes = 0; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci *pgsize = size; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci switch (size) { 1298c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64_4K_PAGES 1308c2ecf20Sopenharmony_ci case PUD_SIZE: 1318c2ecf20Sopenharmony_ci#endif 1328c2ecf20Sopenharmony_ci case PMD_SIZE: 1338c2ecf20Sopenharmony_ci contig_ptes = 1; 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci case CONT_PMD_SIZE: 1368c2ecf20Sopenharmony_ci *pgsize = PMD_SIZE; 1378c2ecf20Sopenharmony_ci contig_ptes = CONT_PMDS; 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci case CONT_PTE_SIZE: 1408c2ecf20Sopenharmony_ci *pgsize = PAGE_SIZE; 1418c2ecf20Sopenharmony_ci contig_ptes = CONT_PTES; 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return contig_ptes; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* 1498c2ecf20Sopenharmony_ci * Changing some bits of contiguous entries requires us to follow a 1508c2ecf20Sopenharmony_ci * Break-Before-Make approach, breaking the whole contiguous set 1518c2ecf20Sopenharmony_ci * before we can change any entries. See ARM DDI 0487A.k_iss10775, 1528c2ecf20Sopenharmony_ci * "Misprogramming of the Contiguous bit", page D4-1762. 1538c2ecf20Sopenharmony_ci * 1548c2ecf20Sopenharmony_ci * This helper performs the break step. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_cistatic pte_t get_clear_flush(struct mm_struct *mm, 1578c2ecf20Sopenharmony_ci unsigned long addr, 1588c2ecf20Sopenharmony_ci pte_t *ptep, 1598c2ecf20Sopenharmony_ci unsigned long pgsize, 1608c2ecf20Sopenharmony_ci unsigned long ncontig) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci pte_t orig_pte = huge_ptep_get(ptep); 1638c2ecf20Sopenharmony_ci bool valid = pte_valid(orig_pte); 1648c2ecf20Sopenharmony_ci unsigned long i, saddr = addr; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci for (i = 0; i < ncontig; i++, addr += pgsize, ptep++) { 1678c2ecf20Sopenharmony_ci pte_t pte = ptep_get_and_clear(mm, addr, ptep); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * If HW_AFDBM is enabled, then the HW could turn on 1718c2ecf20Sopenharmony_ci * the dirty or accessed bit for any page in the set, 1728c2ecf20Sopenharmony_ci * so check them all. 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci if (pte_dirty(pte)) 1758c2ecf20Sopenharmony_ci orig_pte = pte_mkdirty(orig_pte); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (pte_young(pte)) 1788c2ecf20Sopenharmony_ci orig_pte = pte_mkyoung(orig_pte); 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (valid) { 1828c2ecf20Sopenharmony_ci struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0); 1838c2ecf20Sopenharmony_ci flush_tlb_range(&vma, saddr, addr); 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci return orig_pte; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* 1898c2ecf20Sopenharmony_ci * Changing some bits of contiguous entries requires us to follow a 1908c2ecf20Sopenharmony_ci * Break-Before-Make approach, breaking the whole contiguous set 1918c2ecf20Sopenharmony_ci * before we can change any entries. See ARM DDI 0487A.k_iss10775, 1928c2ecf20Sopenharmony_ci * "Misprogramming of the Contiguous bit", page D4-1762. 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * This helper performs the break step for use cases where the 1958c2ecf20Sopenharmony_ci * original pte is not needed. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_cistatic void clear_flush(struct mm_struct *mm, 1988c2ecf20Sopenharmony_ci unsigned long addr, 1998c2ecf20Sopenharmony_ci pte_t *ptep, 2008c2ecf20Sopenharmony_ci unsigned long pgsize, 2018c2ecf20Sopenharmony_ci unsigned long ncontig) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0); 2048c2ecf20Sopenharmony_ci unsigned long i, saddr = addr; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci for (i = 0; i < ncontig; i++, addr += pgsize, ptep++) 2078c2ecf20Sopenharmony_ci pte_clear(mm, addr, ptep); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci flush_tlb_range(&vma, saddr, addr); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_civoid set_huge_pte_at(struct mm_struct *mm, unsigned long addr, 2138c2ecf20Sopenharmony_ci pte_t *ptep, pte_t pte) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci size_t pgsize; 2168c2ecf20Sopenharmony_ci int i; 2178c2ecf20Sopenharmony_ci int ncontig; 2188c2ecf20Sopenharmony_ci unsigned long pfn, dpfn; 2198c2ecf20Sopenharmony_ci pgprot_t hugeprot; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* 2228c2ecf20Sopenharmony_ci * Code needs to be expanded to handle huge swap and migration 2238c2ecf20Sopenharmony_ci * entries. Needed for HUGETLB and MEMORY_FAILURE. 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci WARN_ON(!pte_present(pte)); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (!pte_cont(pte)) { 2288c2ecf20Sopenharmony_ci set_pte_at(mm, addr, ptep, pte); 2298c2ecf20Sopenharmony_ci return; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci ncontig = find_num_contig(mm, addr, ptep, &pgsize); 2338c2ecf20Sopenharmony_ci pfn = pte_pfn(pte); 2348c2ecf20Sopenharmony_ci dpfn = pgsize >> PAGE_SHIFT; 2358c2ecf20Sopenharmony_ci hugeprot = pte_pgprot(pte); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci clear_flush(mm, addr, ptep, pgsize, ncontig); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn) 2408c2ecf20Sopenharmony_ci set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot)); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_civoid set_huge_swap_pte_at(struct mm_struct *mm, unsigned long addr, 2448c2ecf20Sopenharmony_ci pte_t *ptep, pte_t pte, unsigned long sz) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci int i, ncontig; 2478c2ecf20Sopenharmony_ci size_t pgsize; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci ncontig = num_contig_ptes(sz, &pgsize); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci for (i = 0; i < ncontig; i++, ptep++) 2528c2ecf20Sopenharmony_ci set_pte(ptep, pte); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cipte_t *huge_pte_alloc(struct mm_struct *mm, 2568c2ecf20Sopenharmony_ci unsigned long addr, unsigned long sz) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci pgd_t *pgdp; 2598c2ecf20Sopenharmony_ci p4d_t *p4dp; 2608c2ecf20Sopenharmony_ci pud_t *pudp; 2618c2ecf20Sopenharmony_ci pmd_t *pmdp; 2628c2ecf20Sopenharmony_ci pte_t *ptep = NULL; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci pgdp = pgd_offset(mm, addr); 2658c2ecf20Sopenharmony_ci p4dp = p4d_offset(pgdp, addr); 2668c2ecf20Sopenharmony_ci pudp = pud_alloc(mm, p4dp, addr); 2678c2ecf20Sopenharmony_ci if (!pudp) 2688c2ecf20Sopenharmony_ci return NULL; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (sz == PUD_SIZE) { 2718c2ecf20Sopenharmony_ci ptep = (pte_t *)pudp; 2728c2ecf20Sopenharmony_ci } else if (sz == (CONT_PTE_SIZE)) { 2738c2ecf20Sopenharmony_ci pmdp = pmd_alloc(mm, pudp, addr); 2748c2ecf20Sopenharmony_ci if (!pmdp) 2758c2ecf20Sopenharmony_ci return NULL; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci WARN_ON(addr & (sz - 1)); 2788c2ecf20Sopenharmony_ci /* 2798c2ecf20Sopenharmony_ci * Note that if this code were ever ported to the 2808c2ecf20Sopenharmony_ci * 32-bit arm platform then it will cause trouble in 2818c2ecf20Sopenharmony_ci * the case where CONFIG_HIGHPTE is set, since there 2828c2ecf20Sopenharmony_ci * will be no pte_unmap() to correspond with this 2838c2ecf20Sopenharmony_ci * pte_alloc_map(). 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_ci ptep = pte_alloc_map(mm, pmdp, addr); 2868c2ecf20Sopenharmony_ci } else if (sz == PMD_SIZE) { 2878c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARCH_WANT_HUGE_PMD_SHARE) && 2888c2ecf20Sopenharmony_ci pud_none(READ_ONCE(*pudp))) 2898c2ecf20Sopenharmony_ci ptep = huge_pmd_share(mm, addr, pudp); 2908c2ecf20Sopenharmony_ci else 2918c2ecf20Sopenharmony_ci ptep = (pte_t *)pmd_alloc(mm, pudp, addr); 2928c2ecf20Sopenharmony_ci } else if (sz == (CONT_PMD_SIZE)) { 2938c2ecf20Sopenharmony_ci pmdp = pmd_alloc(mm, pudp, addr); 2948c2ecf20Sopenharmony_ci WARN_ON(addr & (sz - 1)); 2958c2ecf20Sopenharmony_ci return (pte_t *)pmdp; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return ptep; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cipte_t *huge_pte_offset(struct mm_struct *mm, 3028c2ecf20Sopenharmony_ci unsigned long addr, unsigned long sz) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci pgd_t *pgdp; 3058c2ecf20Sopenharmony_ci p4d_t *p4dp; 3068c2ecf20Sopenharmony_ci pud_t *pudp, pud; 3078c2ecf20Sopenharmony_ci pmd_t *pmdp, pmd; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci pgdp = pgd_offset(mm, addr); 3108c2ecf20Sopenharmony_ci if (!pgd_present(READ_ONCE(*pgdp))) 3118c2ecf20Sopenharmony_ci return NULL; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci p4dp = p4d_offset(pgdp, addr); 3148c2ecf20Sopenharmony_ci if (!p4d_present(READ_ONCE(*p4dp))) 3158c2ecf20Sopenharmony_ci return NULL; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci pudp = pud_offset(p4dp, addr); 3188c2ecf20Sopenharmony_ci pud = READ_ONCE(*pudp); 3198c2ecf20Sopenharmony_ci if (sz != PUD_SIZE && pud_none(pud)) 3208c2ecf20Sopenharmony_ci return NULL; 3218c2ecf20Sopenharmony_ci /* hugepage or swap? */ 3228c2ecf20Sopenharmony_ci if (pud_huge(pud) || !pud_present(pud)) 3238c2ecf20Sopenharmony_ci return (pte_t *)pudp; 3248c2ecf20Sopenharmony_ci /* table; check the next level */ 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (sz == CONT_PMD_SIZE) 3278c2ecf20Sopenharmony_ci addr &= CONT_PMD_MASK; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci pmdp = pmd_offset(pudp, addr); 3308c2ecf20Sopenharmony_ci pmd = READ_ONCE(*pmdp); 3318c2ecf20Sopenharmony_ci if (!(sz == PMD_SIZE || sz == CONT_PMD_SIZE) && 3328c2ecf20Sopenharmony_ci pmd_none(pmd)) 3338c2ecf20Sopenharmony_ci return NULL; 3348c2ecf20Sopenharmony_ci if (pmd_huge(pmd) || !pmd_present(pmd)) 3358c2ecf20Sopenharmony_ci return (pte_t *)pmdp; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (sz == CONT_PTE_SIZE) 3388c2ecf20Sopenharmony_ci return pte_offset_kernel(pmdp, (addr & CONT_PTE_MASK)); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return NULL; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cipte_t arch_make_huge_pte(pte_t entry, struct vm_area_struct *vma, 3448c2ecf20Sopenharmony_ci struct page *page, int writable) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci size_t pagesize = huge_page_size(hstate_vma(vma)); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (pagesize == CONT_PTE_SIZE) { 3498c2ecf20Sopenharmony_ci entry = pte_mkcont(entry); 3508c2ecf20Sopenharmony_ci } else if (pagesize == CONT_PMD_SIZE) { 3518c2ecf20Sopenharmony_ci entry = pmd_pte(pmd_mkcont(pte_pmd(entry))); 3528c2ecf20Sopenharmony_ci } else if (pagesize != PUD_SIZE && pagesize != PMD_SIZE) { 3538c2ecf20Sopenharmony_ci pr_warn("%s: unrecognized huge page size 0x%lx\n", 3548c2ecf20Sopenharmony_ci __func__, pagesize); 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci return entry; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_civoid huge_pte_clear(struct mm_struct *mm, unsigned long addr, 3608c2ecf20Sopenharmony_ci pte_t *ptep, unsigned long sz) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci int i, ncontig; 3638c2ecf20Sopenharmony_ci size_t pgsize; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ncontig = num_contig_ptes(sz, &pgsize); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci for (i = 0; i < ncontig; i++, addr += pgsize, ptep++) 3688c2ecf20Sopenharmony_ci pte_clear(mm, addr, ptep); 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cipte_t huge_ptep_get_and_clear(struct mm_struct *mm, 3728c2ecf20Sopenharmony_ci unsigned long addr, pte_t *ptep) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci int ncontig; 3758c2ecf20Sopenharmony_ci size_t pgsize; 3768c2ecf20Sopenharmony_ci pte_t orig_pte = huge_ptep_get(ptep); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (!pte_cont(orig_pte)) 3798c2ecf20Sopenharmony_ci return ptep_get_and_clear(mm, addr, ptep); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci ncontig = find_num_contig(mm, addr, ptep, &pgsize); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return get_clear_flush(mm, addr, ptep, pgsize, ncontig); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* 3878c2ecf20Sopenharmony_ci * huge_ptep_set_access_flags will update access flags (dirty, accesssed) 3888c2ecf20Sopenharmony_ci * and write permission. 3898c2ecf20Sopenharmony_ci * 3908c2ecf20Sopenharmony_ci * For a contiguous huge pte range we need to check whether or not write 3918c2ecf20Sopenharmony_ci * permission has to change only on the first pte in the set. Then for 3928c2ecf20Sopenharmony_ci * all the contiguous ptes we need to check whether or not there is a 3938c2ecf20Sopenharmony_ci * discrepancy between dirty or young. 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_cistatic int __cont_access_flags_changed(pte_t *ptep, pte_t pte, int ncontig) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci int i; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (pte_write(pte) != pte_write(huge_ptep_get(ptep))) 4008c2ecf20Sopenharmony_ci return 1; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci for (i = 0; i < ncontig; i++) { 4038c2ecf20Sopenharmony_ci pte_t orig_pte = huge_ptep_get(ptep + i); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (pte_dirty(pte) != pte_dirty(orig_pte)) 4068c2ecf20Sopenharmony_ci return 1; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (pte_young(pte) != pte_young(orig_pte)) 4098c2ecf20Sopenharmony_ci return 1; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci return 0; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ciint huge_ptep_set_access_flags(struct vm_area_struct *vma, 4168c2ecf20Sopenharmony_ci unsigned long addr, pte_t *ptep, 4178c2ecf20Sopenharmony_ci pte_t pte, int dirty) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci int ncontig, i; 4208c2ecf20Sopenharmony_ci size_t pgsize = 0; 4218c2ecf20Sopenharmony_ci unsigned long pfn = pte_pfn(pte), dpfn; 4228c2ecf20Sopenharmony_ci pgprot_t hugeprot; 4238c2ecf20Sopenharmony_ci pte_t orig_pte; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (!pte_cont(pte)) 4268c2ecf20Sopenharmony_ci return ptep_set_access_flags(vma, addr, ptep, pte, dirty); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ncontig = find_num_contig(vma->vm_mm, addr, ptep, &pgsize); 4298c2ecf20Sopenharmony_ci dpfn = pgsize >> PAGE_SHIFT; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (!__cont_access_flags_changed(ptep, pte, ncontig)) 4328c2ecf20Sopenharmony_ci return 0; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci orig_pte = get_clear_flush(vma->vm_mm, addr, ptep, pgsize, ncontig); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* Make sure we don't lose the dirty or young state */ 4378c2ecf20Sopenharmony_ci if (pte_dirty(orig_pte)) 4388c2ecf20Sopenharmony_ci pte = pte_mkdirty(pte); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (pte_young(orig_pte)) 4418c2ecf20Sopenharmony_ci pte = pte_mkyoung(pte); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci hugeprot = pte_pgprot(pte); 4448c2ecf20Sopenharmony_ci for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn) 4458c2ecf20Sopenharmony_ci set_pte_at(vma->vm_mm, addr, ptep, pfn_pte(pfn, hugeprot)); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return 1; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_civoid huge_ptep_set_wrprotect(struct mm_struct *mm, 4518c2ecf20Sopenharmony_ci unsigned long addr, pte_t *ptep) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci unsigned long pfn, dpfn; 4548c2ecf20Sopenharmony_ci pgprot_t hugeprot; 4558c2ecf20Sopenharmony_ci int ncontig, i; 4568c2ecf20Sopenharmony_ci size_t pgsize; 4578c2ecf20Sopenharmony_ci pte_t pte; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (!pte_cont(READ_ONCE(*ptep))) { 4608c2ecf20Sopenharmony_ci ptep_set_wrprotect(mm, addr, ptep); 4618c2ecf20Sopenharmony_ci return; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci ncontig = find_num_contig(mm, addr, ptep, &pgsize); 4658c2ecf20Sopenharmony_ci dpfn = pgsize >> PAGE_SHIFT; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci pte = get_clear_flush(mm, addr, ptep, pgsize, ncontig); 4688c2ecf20Sopenharmony_ci pte = pte_wrprotect(pte); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci hugeprot = pte_pgprot(pte); 4718c2ecf20Sopenharmony_ci pfn = pte_pfn(pte); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn) 4748c2ecf20Sopenharmony_ci set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot)); 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_civoid huge_ptep_clear_flush(struct vm_area_struct *vma, 4788c2ecf20Sopenharmony_ci unsigned long addr, pte_t *ptep) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci size_t pgsize; 4818c2ecf20Sopenharmony_ci int ncontig; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (!pte_cont(READ_ONCE(*ptep))) { 4848c2ecf20Sopenharmony_ci ptep_clear_flush(vma, addr, ptep); 4858c2ecf20Sopenharmony_ci return; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci ncontig = find_num_contig(vma->vm_mm, addr, ptep, &pgsize); 4898c2ecf20Sopenharmony_ci clear_flush(vma->vm_mm, addr, ptep, pgsize, ncontig); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic int __init hugetlbpage_init(void) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64_4K_PAGES 4958c2ecf20Sopenharmony_ci hugetlb_add_hstate(PUD_SHIFT - PAGE_SHIFT); 4968c2ecf20Sopenharmony_ci#endif 4978c2ecf20Sopenharmony_ci hugetlb_add_hstate(CONT_PMD_SHIFT - PAGE_SHIFT); 4988c2ecf20Sopenharmony_ci hugetlb_add_hstate(PMD_SHIFT - PAGE_SHIFT); 4998c2ecf20Sopenharmony_ci hugetlb_add_hstate(CONT_PTE_SHIFT - PAGE_SHIFT); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci return 0; 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ciarch_initcall(hugetlbpage_init); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cibool __init arch_hugetlb_valid_size(unsigned long size) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci switch (size) { 5088c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64_4K_PAGES 5098c2ecf20Sopenharmony_ci case PUD_SIZE: 5108c2ecf20Sopenharmony_ci#endif 5118c2ecf20Sopenharmony_ci case CONT_PMD_SIZE: 5128c2ecf20Sopenharmony_ci case PMD_SIZE: 5138c2ecf20Sopenharmony_ci case CONT_PTE_SIZE: 5148c2ecf20Sopenharmony_ci return true; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return false; 5188c2ecf20Sopenharmony_ci} 519