xref: /kernel/linux/linux-6.6/arch/arm64/include/asm/tlb.h (revision 62306a36)
162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Based on arch/arm/include/asm/tlb.h
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2002 Russell King
662306a36Sopenharmony_ci * Copyright (C) 2012 ARM Ltd.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#ifndef __ASM_TLB_H
962306a36Sopenharmony_ci#define __ASM_TLB_H
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/pagemap.h>
1262306a36Sopenharmony_ci#include <linux/swap.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic inline void __tlb_remove_table(void *_table)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	free_page_and_swap_cache((struct page *)_table);
1762306a36Sopenharmony_ci}
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define tlb_flush tlb_flush
2062306a36Sopenharmony_cistatic void tlb_flush(struct mmu_gather *tlb);
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <asm-generic/tlb.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/*
2562306a36Sopenharmony_ci * get the tlbi levels in arm64.  Default value is 0 if more than one
2662306a36Sopenharmony_ci * of cleared_* is set or neither is set.
2762306a36Sopenharmony_ci * Arm64 doesn't support p4ds now.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_cistatic inline int tlb_get_level(struct mmu_gather *tlb)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	/* The TTL field is only valid for the leaf entry. */
3262306a36Sopenharmony_ci	if (tlb->freed_tables)
3362306a36Sopenharmony_ci		return 0;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	if (tlb->cleared_ptes && !(tlb->cleared_pmds ||
3662306a36Sopenharmony_ci				   tlb->cleared_puds ||
3762306a36Sopenharmony_ci				   tlb->cleared_p4ds))
3862306a36Sopenharmony_ci		return 3;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (tlb->cleared_pmds && !(tlb->cleared_ptes ||
4162306a36Sopenharmony_ci				   tlb->cleared_puds ||
4262306a36Sopenharmony_ci				   tlb->cleared_p4ds))
4362306a36Sopenharmony_ci		return 2;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (tlb->cleared_puds && !(tlb->cleared_ptes ||
4662306a36Sopenharmony_ci				   tlb->cleared_pmds ||
4762306a36Sopenharmony_ci				   tlb->cleared_p4ds))
4862306a36Sopenharmony_ci		return 1;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	return 0;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic inline void tlb_flush(struct mmu_gather *tlb)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	struct vm_area_struct vma = TLB_FLUSH_VMA(tlb->mm, 0);
5662306a36Sopenharmony_ci	bool last_level = !tlb->freed_tables;
5762306a36Sopenharmony_ci	unsigned long stride = tlb_get_unmap_size(tlb);
5862306a36Sopenharmony_ci	int tlb_level = tlb_get_level(tlb);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/*
6162306a36Sopenharmony_ci	 * If we're tearing down the address space then we only care about
6262306a36Sopenharmony_ci	 * invalidating the walk-cache, since the ASID allocator won't
6362306a36Sopenharmony_ci	 * reallocate our ASID without invalidating the entire TLB.
6462306a36Sopenharmony_ci	 */
6562306a36Sopenharmony_ci	if (tlb->fullmm) {
6662306a36Sopenharmony_ci		if (!last_level)
6762306a36Sopenharmony_ci			flush_tlb_mm(tlb->mm);
6862306a36Sopenharmony_ci		return;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	__flush_tlb_range(&vma, tlb->start, tlb->end, stride,
7262306a36Sopenharmony_ci			  last_level, tlb_level);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
7662306a36Sopenharmony_ci				  unsigned long addr)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct ptdesc *ptdesc = page_ptdesc(pte);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	pagetable_pte_dtor(ptdesc);
8162306a36Sopenharmony_ci	tlb_remove_ptdesc(tlb, ptdesc);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS > 2
8562306a36Sopenharmony_cistatic inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
8662306a36Sopenharmony_ci				  unsigned long addr)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct ptdesc *ptdesc = virt_to_ptdesc(pmdp);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	pagetable_pmd_dtor(ptdesc);
9162306a36Sopenharmony_ci	tlb_remove_ptdesc(tlb, ptdesc);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci#endif
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS > 3
9662306a36Sopenharmony_cistatic inline void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pudp,
9762306a36Sopenharmony_ci				  unsigned long addr)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	tlb_remove_ptdesc(tlb, virt_to_ptdesc(pudp));
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci#endif
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#endif
104