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