18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PARISC64 Huge TLB page support. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This parisc implementation is heavily based on the SPARC and x86 code. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2015 Helge Deller <deller@gmx.de> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/fs.h> 118c2ecf20Sopenharmony_ci#include <linux/mm.h> 128c2ecf20Sopenharmony_ci#include <linux/sched/mm.h> 138c2ecf20Sopenharmony_ci#include <linux/hugetlb.h> 148c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 158c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <asm/mman.h> 188c2ecf20Sopenharmony_ci#include <asm/tlb.h> 198c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 208c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 218c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ciunsigned long 258c2ecf20Sopenharmony_cihugetlb_get_unmapped_area(struct file *file, unsigned long addr, 268c2ecf20Sopenharmony_ci unsigned long len, unsigned long pgoff, unsigned long flags) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct hstate *h = hstate_file(file); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (len & ~huge_page_mask(h)) 318c2ecf20Sopenharmony_ci return -EINVAL; 328c2ecf20Sopenharmony_ci if (len > TASK_SIZE) 338c2ecf20Sopenharmony_ci return -ENOMEM; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci if (flags & MAP_FIXED) 368c2ecf20Sopenharmony_ci if (prepare_hugepage_range(file, addr, len)) 378c2ecf20Sopenharmony_ci return -EINVAL; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (addr) 408c2ecf20Sopenharmony_ci addr = ALIGN(addr, huge_page_size(h)); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* we need to make sure the colouring is OK */ 438c2ecf20Sopenharmony_ci return arch_get_unmapped_area(file, addr, len, pgoff, flags); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cipte_t *huge_pte_alloc(struct mm_struct *mm, 488c2ecf20Sopenharmony_ci unsigned long addr, unsigned long sz) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci pgd_t *pgd; 518c2ecf20Sopenharmony_ci p4d_t *p4d; 528c2ecf20Sopenharmony_ci pud_t *pud; 538c2ecf20Sopenharmony_ci pmd_t *pmd; 548c2ecf20Sopenharmony_ci pte_t *pte = NULL; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* We must align the address, because our caller will run 578c2ecf20Sopenharmony_ci * set_huge_pte_at() on whatever we return, which writes out 588c2ecf20Sopenharmony_ci * all of the sub-ptes for the hugepage range. So we have 598c2ecf20Sopenharmony_ci * to give it the first such sub-pte. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci addr &= HPAGE_MASK; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci pgd = pgd_offset(mm, addr); 648c2ecf20Sopenharmony_ci p4d = p4d_offset(pgd, addr); 658c2ecf20Sopenharmony_ci pud = pud_alloc(mm, p4d, addr); 668c2ecf20Sopenharmony_ci if (pud) { 678c2ecf20Sopenharmony_ci pmd = pmd_alloc(mm, pud, addr); 688c2ecf20Sopenharmony_ci if (pmd) 698c2ecf20Sopenharmony_ci pte = pte_alloc_map(mm, pmd, addr); 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci return pte; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cipte_t *huge_pte_offset(struct mm_struct *mm, 758c2ecf20Sopenharmony_ci unsigned long addr, unsigned long sz) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci pgd_t *pgd; 788c2ecf20Sopenharmony_ci p4d_t *p4d; 798c2ecf20Sopenharmony_ci pud_t *pud; 808c2ecf20Sopenharmony_ci pmd_t *pmd; 818c2ecf20Sopenharmony_ci pte_t *pte = NULL; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci addr &= HPAGE_MASK; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci pgd = pgd_offset(mm, addr); 868c2ecf20Sopenharmony_ci if (!pgd_none(*pgd)) { 878c2ecf20Sopenharmony_ci p4d = p4d_offset(pgd, addr); 888c2ecf20Sopenharmony_ci if (!p4d_none(*p4d)) { 898c2ecf20Sopenharmony_ci pud = pud_offset(p4d, addr); 908c2ecf20Sopenharmony_ci if (!pud_none(*pud)) { 918c2ecf20Sopenharmony_ci pmd = pmd_offset(pud, addr); 928c2ecf20Sopenharmony_ci if (!pmd_none(*pmd)) 938c2ecf20Sopenharmony_ci pte = pte_offset_map(pmd, addr); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci return pte; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* Purge data and instruction TLB entries. Must be called holding 1018c2ecf20Sopenharmony_ci * the pa_tlb_lock. The TLB purge instructions are slow on SMP 1028c2ecf20Sopenharmony_ci * machines since the purge must be broadcast to all CPUs. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistatic inline void purge_tlb_entries_huge(struct mm_struct *mm, unsigned long addr) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci int i; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* We may use multiple physical huge pages (e.g. 2x1 MB) to emulate 1098c2ecf20Sopenharmony_ci * Linux standard huge pages (e.g. 2 MB) */ 1108c2ecf20Sopenharmony_ci BUILD_BUG_ON(REAL_HPAGE_SHIFT > HPAGE_SHIFT); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci addr &= HPAGE_MASK; 1138c2ecf20Sopenharmony_ci addr |= _HUGE_PAGE_SIZE_ENCODING_DEFAULT; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci for (i = 0; i < (1 << (HPAGE_SHIFT-REAL_HPAGE_SHIFT)); i++) { 1168c2ecf20Sopenharmony_ci purge_tlb_entries(mm, addr); 1178c2ecf20Sopenharmony_ci addr += (1UL << REAL_HPAGE_SHIFT); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* __set_huge_pte_at() must be called holding the pa_tlb_lock. */ 1228c2ecf20Sopenharmony_cistatic void __set_huge_pte_at(struct mm_struct *mm, unsigned long addr, 1238c2ecf20Sopenharmony_ci pte_t *ptep, pte_t entry) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci unsigned long addr_start; 1268c2ecf20Sopenharmony_ci int i; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci addr &= HPAGE_MASK; 1298c2ecf20Sopenharmony_ci addr_start = addr; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) { 1328c2ecf20Sopenharmony_ci set_pte(ptep, entry); 1338c2ecf20Sopenharmony_ci ptep++; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci addr += PAGE_SIZE; 1368c2ecf20Sopenharmony_ci pte_val(entry) += PAGE_SIZE; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci purge_tlb_entries_huge(mm, addr_start); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_civoid set_huge_pte_at(struct mm_struct *mm, unsigned long addr, 1438c2ecf20Sopenharmony_ci pte_t *ptep, pte_t entry) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci __set_huge_pte_at(mm, addr, ptep, entry); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cipte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, 1508c2ecf20Sopenharmony_ci pte_t *ptep) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci pte_t entry; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci entry = *ptep; 1558c2ecf20Sopenharmony_ci __set_huge_pte_at(mm, addr, ptep, __pte(0)); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return entry; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_civoid huge_ptep_set_wrprotect(struct mm_struct *mm, 1628c2ecf20Sopenharmony_ci unsigned long addr, pte_t *ptep) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci pte_t old_pte; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci old_pte = *ptep; 1678c2ecf20Sopenharmony_ci __set_huge_pte_at(mm, addr, ptep, pte_wrprotect(old_pte)); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ciint huge_ptep_set_access_flags(struct vm_area_struct *vma, 1718c2ecf20Sopenharmony_ci unsigned long addr, pte_t *ptep, 1728c2ecf20Sopenharmony_ci pte_t pte, int dirty) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci int changed; 1758c2ecf20Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci changed = !pte_same(*ptep, pte); 1788c2ecf20Sopenharmony_ci if (changed) { 1798c2ecf20Sopenharmony_ci __set_huge_pte_at(mm, addr, ptep, pte); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci return changed; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ciint pmd_huge(pmd_t pmd) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ciint pud_huge(pud_t pud) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci} 194