18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef _S390_TLBFLUSH_H
38c2ecf20Sopenharmony_ci#define _S390_TLBFLUSH_H
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/mm.h>
68c2ecf20Sopenharmony_ci#include <linux/sched.h>
78c2ecf20Sopenharmony_ci#include <asm/processor.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci * Flush all TLB entries on the local CPU.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_cistatic inline void __tlb_flush_local(void)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	asm volatile("ptlb" : : : "memory");
158c2ecf20Sopenharmony_ci}
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/*
188c2ecf20Sopenharmony_ci * Flush TLB entries for a specific ASCE on all CPUs
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_cistatic inline void __tlb_flush_idte(unsigned long asce)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	unsigned long opt;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	opt = IDTE_PTOA;
258c2ecf20Sopenharmony_ci	if (MACHINE_HAS_TLB_GUEST)
268c2ecf20Sopenharmony_ci		opt |= IDTE_GUEST_ASCE;
278c2ecf20Sopenharmony_ci	/* Global TLB flush for the mm */
288c2ecf20Sopenharmony_ci	asm volatile(
298c2ecf20Sopenharmony_ci		"	.insn	rrf,0xb98e0000,0,%0,%1,0"
308c2ecf20Sopenharmony_ci		: : "a" (opt), "a" (asce) : "cc");
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * Flush all TLB entries on all CPUs.
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_cistatic inline void __tlb_flush_global(void)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	unsigned int dummy = 0;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	csp(&dummy, 0, 0);
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/*
448c2ecf20Sopenharmony_ci * Flush TLB entries for a specific mm on all CPUs (in case gmap is used
458c2ecf20Sopenharmony_ci * this implicates multiple ASCEs!).
468c2ecf20Sopenharmony_ci */
478c2ecf20Sopenharmony_cistatic inline void __tlb_flush_mm(struct mm_struct *mm)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	unsigned long gmap_asce;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/*
528c2ecf20Sopenharmony_ci	 * If the machine has IDTE we prefer to do a per mm flush
538c2ecf20Sopenharmony_ci	 * on all cpus instead of doing a local flush if the mm
548c2ecf20Sopenharmony_ci	 * only ran on the local cpu.
558c2ecf20Sopenharmony_ci	 */
568c2ecf20Sopenharmony_ci	preempt_disable();
578c2ecf20Sopenharmony_ci	atomic_inc(&mm->context.flush_count);
588c2ecf20Sopenharmony_ci	/* Reset TLB flush mask */
598c2ecf20Sopenharmony_ci	cpumask_copy(mm_cpumask(mm), &mm->context.cpu_attach_mask);
608c2ecf20Sopenharmony_ci	barrier();
618c2ecf20Sopenharmony_ci	gmap_asce = READ_ONCE(mm->context.gmap_asce);
628c2ecf20Sopenharmony_ci	if (MACHINE_HAS_IDTE && gmap_asce != -1UL) {
638c2ecf20Sopenharmony_ci		if (gmap_asce)
648c2ecf20Sopenharmony_ci			__tlb_flush_idte(gmap_asce);
658c2ecf20Sopenharmony_ci		__tlb_flush_idte(mm->context.asce);
668c2ecf20Sopenharmony_ci	} else {
678c2ecf20Sopenharmony_ci		/* Global TLB flush */
688c2ecf20Sopenharmony_ci		__tlb_flush_global();
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci	atomic_dec(&mm->context.flush_count);
718c2ecf20Sopenharmony_ci	preempt_enable();
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic inline void __tlb_flush_kernel(void)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	if (MACHINE_HAS_IDTE)
778c2ecf20Sopenharmony_ci		__tlb_flush_idte(init_mm.context.asce);
788c2ecf20Sopenharmony_ci	else
798c2ecf20Sopenharmony_ci		__tlb_flush_global();
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic inline void __tlb_flush_mm_lazy(struct mm_struct * mm)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	spin_lock(&mm->context.lock);
858c2ecf20Sopenharmony_ci	if (mm->context.flush_mm) {
868c2ecf20Sopenharmony_ci		mm->context.flush_mm = 0;
878c2ecf20Sopenharmony_ci		__tlb_flush_mm(mm);
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci	spin_unlock(&mm->context.lock);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/*
938c2ecf20Sopenharmony_ci * TLB flushing:
948c2ecf20Sopenharmony_ci *  flush_tlb() - flushes the current mm struct TLBs
958c2ecf20Sopenharmony_ci *  flush_tlb_all() - flushes all processes TLBs
968c2ecf20Sopenharmony_ci *  flush_tlb_mm(mm) - flushes the specified mm context TLB's
978c2ecf20Sopenharmony_ci *  flush_tlb_page(vma, vmaddr) - flushes one page
988c2ecf20Sopenharmony_ci *  flush_tlb_range(vma, start, end) - flushes a range of pages
998c2ecf20Sopenharmony_ci *  flush_tlb_kernel_range(start, end) - flushes a range of kernel pages
1008c2ecf20Sopenharmony_ci */
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/*
1038c2ecf20Sopenharmony_ci * flush_tlb_mm goes together with ptep_set_wrprotect for the
1048c2ecf20Sopenharmony_ci * copy_page_range operation and flush_tlb_range is related to
1058c2ecf20Sopenharmony_ci * ptep_get_and_clear for change_protection. ptep_set_wrprotect and
1068c2ecf20Sopenharmony_ci * ptep_get_and_clear do not flush the TLBs directly if the mm has
1078c2ecf20Sopenharmony_ci * only one user. At the end of the update the flush_tlb_mm and
1088c2ecf20Sopenharmony_ci * flush_tlb_range functions need to do the flush.
1098c2ecf20Sopenharmony_ci */
1108c2ecf20Sopenharmony_ci#define flush_tlb()				do { } while (0)
1118c2ecf20Sopenharmony_ci#define flush_tlb_all()				do { } while (0)
1128c2ecf20Sopenharmony_ci#define flush_tlb_page(vma, addr)		do { } while (0)
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic inline void flush_tlb_mm(struct mm_struct *mm)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	__tlb_flush_mm_lazy(mm);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic inline void flush_tlb_range(struct vm_area_struct *vma,
1208c2ecf20Sopenharmony_ci				   unsigned long start, unsigned long end)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	__tlb_flush_mm_lazy(vma->vm_mm);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic inline void flush_tlb_kernel_range(unsigned long start,
1268c2ecf20Sopenharmony_ci					  unsigned long end)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	__tlb_flush_kernel();
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci#endif /* _S390_TLBFLUSH_H */
132