Path: blob/master/arch/powerpc/mm/nohash/e500_hugetlbpage.c
26481 views
// SPDX-License-Identifier: GPL-2.01/*2* PPC Huge TLB Page Support for Book3E MMU3*4* Copyright (C) 2009 David Gibson, IBM Corporation.5* Copyright (C) 2011 Becky Bruce, Freescale Semiconductor6*7*/8#include <linux/mm.h>9#include <linux/hugetlb.h>1011#include <asm/mmu.h>1213#ifdef CONFIG_PPC6414#include <asm/paca.h>1516static inline int tlb1_next(void)17{18struct paca_struct *paca = get_paca();19struct tlb_core_data *tcd;20int this, next;2122tcd = paca->tcd_ptr;23this = tcd->esel_next;2425next = this + 1;26if (next >= tcd->esel_max)27next = tcd->esel_first;2829tcd->esel_next = next;30return this;31}3233static inline void book3e_tlb_lock(void)34{35struct paca_struct *paca = get_paca();36unsigned long tmp;37int token = smp_processor_id() + 1;3839/*40* Besides being unnecessary in the absence of SMT, this41* check prevents trying to do lbarx/stbcx. on e5500 which42* doesn't implement either feature.43*/44if (!cpu_has_feature(CPU_FTR_SMT))45return;4647asm volatile(".machine push;"48".machine e6500;"49"1: lbarx %0, 0, %1;"50"cmpwi %0, 0;"51"bne 2f;"52"stbcx. %2, 0, %1;"53"bne 1b;"54"b 3f;"55"2: lbzx %0, 0, %1;"56"cmpwi %0, 0;"57"bne 2b;"58"b 1b;"59"3:"60".machine pop;"61: "=&r" (tmp)62: "r" (&paca->tcd_ptr->lock), "r" (token)63: "memory");64}6566static inline void book3e_tlb_unlock(void)67{68struct paca_struct *paca = get_paca();6970if (!cpu_has_feature(CPU_FTR_SMT))71return;7273isync();74paca->tcd_ptr->lock = 0;75}76#else77static inline int tlb1_next(void)78{79int index, ncams;8081ncams = mfspr(SPRN_TLB1CFG) & TLBnCFG_N_ENTRY;8283index = this_cpu_read(next_tlbcam_idx);8485/* Just round-robin the entries and wrap when we hit the end */86if (unlikely(index == ncams - 1))87__this_cpu_write(next_tlbcam_idx, tlbcam_index);88else89__this_cpu_inc(next_tlbcam_idx);9091return index;92}9394static inline void book3e_tlb_lock(void)95{96}9798static inline void book3e_tlb_unlock(void)99{100}101#endif102103static inline int book3e_tlb_exists(unsigned long ea, unsigned long pid)104{105int found = 0;106107mtspr(SPRN_MAS6, pid << 16);108asm volatile(109"tlbsx 0,%1\n"110"mfspr %0,0x271\n"111"srwi %0,%0,31\n"112: "=&r"(found) : "r"(ea));113114return found;115}116117static void118book3e_hugetlb_preload(struct vm_area_struct *vma, unsigned long ea, pte_t pte)119{120unsigned long mas1, mas2;121u64 mas7_3;122unsigned long psize, tsize, shift;123unsigned long flags;124struct mm_struct *mm;125int index;126127if (unlikely(is_kernel_addr(ea)))128return;129130mm = vma->vm_mm;131132psize = vma_mmu_pagesize(vma);133shift = __ilog2(psize);134tsize = shift - 10;135/*136* We can't be interrupted while we're setting up the MAS137* registers or after we've confirmed that no tlb exists.138*/139local_irq_save(flags);140141book3e_tlb_lock();142143if (unlikely(book3e_tlb_exists(ea, mm->context.id))) {144book3e_tlb_unlock();145local_irq_restore(flags);146return;147}148149/* We have to use the CAM(TLB1) on FSL parts for hugepages */150index = tlb1_next();151mtspr(SPRN_MAS0, MAS0_ESEL(index) | MAS0_TLBSEL(1));152153mas1 = MAS1_VALID | MAS1_TID(mm->context.id) | MAS1_TSIZE(tsize);154mas2 = ea & ~((1UL << shift) - 1);155mas2 |= (pte_val(pte) >> PTE_WIMGE_SHIFT) & MAS2_WIMGE_MASK;156mas7_3 = (u64)pte_pfn(pte) << PAGE_SHIFT;157mas7_3 |= (pte_val(pte) >> PTE_BAP_SHIFT) & MAS3_BAP_MASK;158if (!pte_dirty(pte))159mas7_3 &= ~(MAS3_SW|MAS3_UW);160161mtspr(SPRN_MAS1, mas1);162mtspr(SPRN_MAS2, mas2);163164if (mmu_has_feature(MMU_FTR_BIG_PHYS))165mtspr(SPRN_MAS7, upper_32_bits(mas7_3));166mtspr(SPRN_MAS3, lower_32_bits(mas7_3));167168asm volatile ("tlbwe");169170book3e_tlb_unlock();171local_irq_restore(flags);172}173174/*175* This is called at the end of handling a user page fault, when the176* fault has been handled by updating a PTE in the linux page tables.177*178* This must always be called with the pte lock held.179*/180void __update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)181{182if (is_vm_hugetlb_page(vma))183book3e_hugetlb_preload(vma, address, *ptep);184}185186void flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr)187{188struct hstate *hstate = hstate_file(vma->vm_file);189unsigned long tsize = huge_page_shift(hstate) - 10;190191__flush_tlb_page(vma->vm_mm, vmaddr, tsize, 0);192}193194195