18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PPC Huge TLB Page Support for Book3E MMU 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 David Gibson, IBM Corporation. 68c2ecf20Sopenharmony_ci * Copyright (C) 2011 Becky Bruce, Freescale Semiconductor 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/mm.h> 108c2ecf20Sopenharmony_ci#include <linux/hugetlb.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <asm/mmu.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64 158c2ecf20Sopenharmony_ci#include <asm/paca.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic inline int tlb1_next(void) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci struct paca_struct *paca = get_paca(); 208c2ecf20Sopenharmony_ci struct tlb_core_data *tcd; 218c2ecf20Sopenharmony_ci int this, next; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci tcd = paca->tcd_ptr; 248c2ecf20Sopenharmony_ci this = tcd->esel_next; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci next = this + 1; 278c2ecf20Sopenharmony_ci if (next >= tcd->esel_max) 288c2ecf20Sopenharmony_ci next = tcd->esel_first; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci tcd->esel_next = next; 318c2ecf20Sopenharmony_ci return this; 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic inline void book3e_tlb_lock(void) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct paca_struct *paca = get_paca(); 378c2ecf20Sopenharmony_ci unsigned long tmp; 388c2ecf20Sopenharmony_ci int token = smp_processor_id() + 1; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci /* 418c2ecf20Sopenharmony_ci * Besides being unnecessary in the absence of SMT, this 428c2ecf20Sopenharmony_ci * check prevents trying to do lbarx/stbcx. on e5500 which 438c2ecf20Sopenharmony_ci * doesn't implement either feature. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_SMT)) 468c2ecf20Sopenharmony_ci return; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci asm volatile("1: lbarx %0, 0, %1;" 498c2ecf20Sopenharmony_ci "cmpwi %0, 0;" 508c2ecf20Sopenharmony_ci "bne 2f;" 518c2ecf20Sopenharmony_ci "stbcx. %2, 0, %1;" 528c2ecf20Sopenharmony_ci "bne 1b;" 538c2ecf20Sopenharmony_ci "b 3f;" 548c2ecf20Sopenharmony_ci "2: lbzx %0, 0, %1;" 558c2ecf20Sopenharmony_ci "cmpwi %0, 0;" 568c2ecf20Sopenharmony_ci "bne 2b;" 578c2ecf20Sopenharmony_ci "b 1b;" 588c2ecf20Sopenharmony_ci "3:" 598c2ecf20Sopenharmony_ci : "=&r" (tmp) 608c2ecf20Sopenharmony_ci : "r" (&paca->tcd_ptr->lock), "r" (token) 618c2ecf20Sopenharmony_ci : "memory"); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic inline void book3e_tlb_unlock(void) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct paca_struct *paca = get_paca(); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_SMT)) 698c2ecf20Sopenharmony_ci return; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci isync(); 728c2ecf20Sopenharmony_ci paca->tcd_ptr->lock = 0; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci#else 758c2ecf20Sopenharmony_cistatic inline int tlb1_next(void) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci int index, ncams; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci ncams = mfspr(SPRN_TLB1CFG) & TLBnCFG_N_ENTRY; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci index = this_cpu_read(next_tlbcam_idx); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* Just round-robin the entries and wrap when we hit the end */ 848c2ecf20Sopenharmony_ci if (unlikely(index == ncams - 1)) 858c2ecf20Sopenharmony_ci __this_cpu_write(next_tlbcam_idx, tlbcam_index); 868c2ecf20Sopenharmony_ci else 878c2ecf20Sopenharmony_ci __this_cpu_inc(next_tlbcam_idx); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return index; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic inline void book3e_tlb_lock(void) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic inline void book3e_tlb_unlock(void) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci#endif 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic inline int book3e_tlb_exists(unsigned long ea, unsigned long pid) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci int found = 0; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci mtspr(SPRN_MAS6, pid << 16); 1068c2ecf20Sopenharmony_ci if (mmu_has_feature(MMU_FTR_USE_TLBRSRV)) { 1078c2ecf20Sopenharmony_ci asm volatile( 1088c2ecf20Sopenharmony_ci "li %0,0\n" 1098c2ecf20Sopenharmony_ci "tlbsx. 0,%1\n" 1108c2ecf20Sopenharmony_ci "bne 1f\n" 1118c2ecf20Sopenharmony_ci "li %0,1\n" 1128c2ecf20Sopenharmony_ci "1:\n" 1138c2ecf20Sopenharmony_ci : "=&r"(found) : "r"(ea)); 1148c2ecf20Sopenharmony_ci } else { 1158c2ecf20Sopenharmony_ci asm volatile( 1168c2ecf20Sopenharmony_ci "tlbsx 0,%1\n" 1178c2ecf20Sopenharmony_ci "mfspr %0,0x271\n" 1188c2ecf20Sopenharmony_ci "srwi %0,%0,31\n" 1198c2ecf20Sopenharmony_ci : "=&r"(found) : "r"(ea)); 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return found; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void 1268c2ecf20Sopenharmony_cibook3e_hugetlb_preload(struct vm_area_struct *vma, unsigned long ea, pte_t pte) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci unsigned long mas1, mas2; 1298c2ecf20Sopenharmony_ci u64 mas7_3; 1308c2ecf20Sopenharmony_ci unsigned long psize, tsize, shift; 1318c2ecf20Sopenharmony_ci unsigned long flags; 1328c2ecf20Sopenharmony_ci struct mm_struct *mm; 1338c2ecf20Sopenharmony_ci int index; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (unlikely(is_kernel_addr(ea))) 1368c2ecf20Sopenharmony_ci return; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci mm = vma->vm_mm; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci psize = vma_mmu_pagesize(vma); 1418c2ecf20Sopenharmony_ci shift = __ilog2(psize); 1428c2ecf20Sopenharmony_ci tsize = shift - 10; 1438c2ecf20Sopenharmony_ci /* 1448c2ecf20Sopenharmony_ci * We can't be interrupted while we're setting up the MAS 1458c2ecf20Sopenharmony_ci * regusters or after we've confirmed that no tlb exists. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci local_irq_save(flags); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci book3e_tlb_lock(); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (unlikely(book3e_tlb_exists(ea, mm->context.id))) { 1528c2ecf20Sopenharmony_ci book3e_tlb_unlock(); 1538c2ecf20Sopenharmony_ci local_irq_restore(flags); 1548c2ecf20Sopenharmony_ci return; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* We have to use the CAM(TLB1) on FSL parts for hugepages */ 1588c2ecf20Sopenharmony_ci index = tlb1_next(); 1598c2ecf20Sopenharmony_ci mtspr(SPRN_MAS0, MAS0_ESEL(index) | MAS0_TLBSEL(1)); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci mas1 = MAS1_VALID | MAS1_TID(mm->context.id) | MAS1_TSIZE(tsize); 1628c2ecf20Sopenharmony_ci mas2 = ea & ~((1UL << shift) - 1); 1638c2ecf20Sopenharmony_ci mas2 |= (pte_val(pte) >> PTE_WIMGE_SHIFT) & MAS2_WIMGE_MASK; 1648c2ecf20Sopenharmony_ci mas7_3 = (u64)pte_pfn(pte) << PAGE_SHIFT; 1658c2ecf20Sopenharmony_ci mas7_3 |= (pte_val(pte) >> PTE_BAP_SHIFT) & MAS3_BAP_MASK; 1668c2ecf20Sopenharmony_ci if (!pte_dirty(pte)) 1678c2ecf20Sopenharmony_ci mas7_3 &= ~(MAS3_SW|MAS3_UW); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci mtspr(SPRN_MAS1, mas1); 1708c2ecf20Sopenharmony_ci mtspr(SPRN_MAS2, mas2); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (mmu_has_feature(MMU_FTR_USE_PAIRED_MAS)) { 1738c2ecf20Sopenharmony_ci mtspr(SPRN_MAS7_MAS3, mas7_3); 1748c2ecf20Sopenharmony_ci } else { 1758c2ecf20Sopenharmony_ci if (mmu_has_feature(MMU_FTR_BIG_PHYS)) 1768c2ecf20Sopenharmony_ci mtspr(SPRN_MAS7, upper_32_bits(mas7_3)); 1778c2ecf20Sopenharmony_ci mtspr(SPRN_MAS3, lower_32_bits(mas7_3)); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci asm volatile ("tlbwe"); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci book3e_tlb_unlock(); 1838c2ecf20Sopenharmony_ci local_irq_restore(flags); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* 1878c2ecf20Sopenharmony_ci * This is called at the end of handling a user page fault, when the 1888c2ecf20Sopenharmony_ci * fault has been handled by updating a PTE in the linux page tables. 1898c2ecf20Sopenharmony_ci * 1908c2ecf20Sopenharmony_ci * This must always be called with the pte lock held. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_civoid update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t *ptep) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci if (is_vm_hugetlb_page(vma)) 1958c2ecf20Sopenharmony_ci book3e_hugetlb_preload(vma, address, *ptep); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_civoid flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct hstate *hstate = hstate_file(vma->vm_file); 2018c2ecf20Sopenharmony_ci unsigned long tsize = huge_page_shift(hstate) - 10; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci __flush_tlb_page(vma->vm_mm, vmaddr, tsize, 0); 2048c2ecf20Sopenharmony_ci} 205