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