162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file contains the routines for TLB flushing. 462306a36Sopenharmony_ci * On machines where the MMU uses a hash table to store virtual to 562306a36Sopenharmony_ci * physical translations, these routines flush entries from the 662306a36Sopenharmony_ci * hash table also. 762306a36Sopenharmony_ci * -- paulus 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Derived from arch/ppc/mm/init.c: 1062306a36Sopenharmony_ci * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) 1362306a36Sopenharmony_ci * and Cort Dougan (PReP) (cort@cs.nmt.edu) 1462306a36Sopenharmony_ci * Copyright (C) 1996 Paul Mackerras 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Derived from "arch/i386/mm/init.c" 1762306a36Sopenharmony_ci * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/mm.h> 2262306a36Sopenharmony_ci#include <linux/init.h> 2362306a36Sopenharmony_ci#include <linux/highmem.h> 2462306a36Sopenharmony_ci#include <linux/pagemap.h> 2562306a36Sopenharmony_ci#include <linux/export.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <asm/tlbflush.h> 2862306a36Sopenharmony_ci#include <asm/tlb.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <mm/mmu_decl.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci * TLB flushing: 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * - flush_tlb_mm(mm) flushes the specified mm context TLB's 3662306a36Sopenharmony_ci * - flush_tlb_page(vma, vmaddr) flushes one page 3762306a36Sopenharmony_ci * - flush_tlb_range(vma, start, end) flushes a range of pages 3862306a36Sopenharmony_ci * - flush_tlb_kernel_range(start, end) flushes kernel pages 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * since the hardware hash table functions as an extension of the 4162306a36Sopenharmony_ci * tlb as far as the linux tables are concerned, flush it too. 4262306a36Sopenharmony_ci * -- Cort 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * For each address in the range, find the pte for the address 4762306a36Sopenharmony_ci * and check _PAGE_HASHPTE bit; if it is set, find and destroy 4862306a36Sopenharmony_ci * the corresponding HPTE. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_civoid hash__flush_range(struct mm_struct *mm, unsigned long start, unsigned long end) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci pmd_t *pmd; 5362306a36Sopenharmony_ci unsigned long pmd_end; 5462306a36Sopenharmony_ci int count; 5562306a36Sopenharmony_ci unsigned int ctx = mm->context.id; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci start &= PAGE_MASK; 5862306a36Sopenharmony_ci if (start >= end) 5962306a36Sopenharmony_ci return; 6062306a36Sopenharmony_ci end = (end - 1) | ~PAGE_MASK; 6162306a36Sopenharmony_ci pmd = pmd_off(mm, start); 6262306a36Sopenharmony_ci for (;;) { 6362306a36Sopenharmony_ci pmd_end = ((start + PGDIR_SIZE) & PGDIR_MASK) - 1; 6462306a36Sopenharmony_ci if (pmd_end > end) 6562306a36Sopenharmony_ci pmd_end = end; 6662306a36Sopenharmony_ci if (!pmd_none(*pmd)) { 6762306a36Sopenharmony_ci count = ((pmd_end - start) >> PAGE_SHIFT) + 1; 6862306a36Sopenharmony_ci flush_hash_pages(ctx, start, pmd_val(*pmd), count); 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci if (pmd_end == end) 7162306a36Sopenharmony_ci break; 7262306a36Sopenharmony_ci start = pmd_end + 1; 7362306a36Sopenharmony_ci ++pmd; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ciEXPORT_SYMBOL(hash__flush_range); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * Flush all the (user) entries for the address space described by mm. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_civoid hash__flush_tlb_mm(struct mm_struct *mm) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct vm_area_struct *mp; 8462306a36Sopenharmony_ci VMA_ITERATOR(vmi, mm, 0); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * It is safe to iterate the vmas when called from dup_mmap, 8862306a36Sopenharmony_ci * holding mmap_lock. It would also be safe from unmap_region 8962306a36Sopenharmony_ci * or exit_mmap, but not from vmtruncate on SMP - but it seems 9062306a36Sopenharmony_ci * dup_mmap is the only SMP case which gets here. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci for_each_vma(vmi, mp) 9362306a36Sopenharmony_ci hash__flush_range(mp->vm_mm, mp->vm_start, mp->vm_end); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ciEXPORT_SYMBOL(hash__flush_tlb_mm); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_civoid hash__flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct mm_struct *mm; 10062306a36Sopenharmony_ci pmd_t *pmd; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci mm = (vmaddr < TASK_SIZE)? vma->vm_mm: &init_mm; 10362306a36Sopenharmony_ci pmd = pmd_off(mm, vmaddr); 10462306a36Sopenharmony_ci if (!pmd_none(*pmd)) 10562306a36Sopenharmony_ci flush_hash_pages(mm->context.id, vmaddr, pmd_val(*pmd), 1); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ciEXPORT_SYMBOL(hash__flush_tlb_page); 108