| /* | |
| * arch/sh/mm/tlb-flush_64.c | |
| * | |
| * Copyright (C) 2000, 2001 Paolo Alberelli | |
| * Copyright (C) 2003 Richard Curnow (/proc/tlb, bug fixes) | |
| * Copyright (C) 2003 - 2012 Paul Mundt | |
| * | |
| * This file is subject to the terms and conditions of the GNU General Public | |
| * License. See the file "COPYING" in the main directory of this archive | |
| * for more details. | |
| */ | |
| #include <linux/signal.h> | |
| #include <linux/rwsem.h> | |
| #include <linux/sched.h> | |
| #include <linux/kernel.h> | |
| #include <linux/errno.h> | |
| #include <linux/string.h> | |
| #include <linux/types.h> | |
| #include <linux/ptrace.h> | |
| #include <linux/mman.h> | |
| #include <linux/mm.h> | |
| #include <linux/smp.h> | |
| #include <linux/perf_event.h> | |
| #include <linux/interrupt.h> | |
| #include <asm/io.h> | |
| #include <asm/tlb.h> | |
| #include <asm/uaccess.h> | |
| #include <asm/pgalloc.h> | |
| #include <asm/mmu_context.h> | |
| void local_flush_tlb_one(unsigned long asid, unsigned long page) | |
| { | |
| unsigned long long match, pteh=0, lpage; | |
| unsigned long tlb; | |
| /* | |
| * Sign-extend based on neff. | |
| */ | |
| lpage = neff_sign_extend(page); | |
| match = (asid << PTEH_ASID_SHIFT) | PTEH_VALID; | |
| match |= lpage; | |
| for_each_itlb_entry(tlb) { | |
| asm volatile ("getcfg %1, 0, %0" | |
| : "=r" (pteh) | |
| : "r" (tlb) ); | |
| if (pteh == match) { | |
| __flush_tlb_slot(tlb); | |
| break; | |
| } | |
| } | |
| for_each_dtlb_entry(tlb) { | |
| asm volatile ("getcfg %1, 0, %0" | |
| : "=r" (pteh) | |
| : "r" (tlb) ); | |
| if (pteh == match) { | |
| __flush_tlb_slot(tlb); | |
| break; | |
| } | |
| } | |
| } | |
| void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) | |
| { | |
| unsigned long flags; | |
| if (vma->vm_mm) { | |
| page &= PAGE_MASK; | |
| local_irq_save(flags); | |
| local_flush_tlb_one(get_asid(), page); | |
| local_irq_restore(flags); | |
| } | |
| } | |
| void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | |
| unsigned long end) | |
| { | |
| unsigned long flags; | |
| unsigned long long match, pteh=0, pteh_epn, pteh_low; | |
| unsigned long tlb; | |
| unsigned int cpu = smp_processor_id(); | |
| struct mm_struct *mm; | |
| mm = vma->vm_mm; | |
| if (cpu_context(cpu, mm) == NO_CONTEXT) | |
| return; | |
| local_irq_save(flags); | |
| start &= PAGE_MASK; | |
| end &= PAGE_MASK; | |
| match = (cpu_asid(cpu, mm) << PTEH_ASID_SHIFT) | PTEH_VALID; | |
| /* Flush ITLB */ | |
| for_each_itlb_entry(tlb) { | |
| asm volatile ("getcfg %1, 0, %0" | |
| : "=r" (pteh) | |
| : "r" (tlb) ); | |
| pteh_epn = pteh & PAGE_MASK; | |
| pteh_low = pteh & ~PAGE_MASK; | |
| if (pteh_low == match && pteh_epn >= start && pteh_epn <= end) | |
| __flush_tlb_slot(tlb); | |
| } | |
| /* Flush DTLB */ | |
| for_each_dtlb_entry(tlb) { | |
| asm volatile ("getcfg %1, 0, %0" | |
| : "=r" (pteh) | |
| : "r" (tlb) ); | |
| pteh_epn = pteh & PAGE_MASK; | |
| pteh_low = pteh & ~PAGE_MASK; | |
| if (pteh_low == match && pteh_epn >= start && pteh_epn <= end) | |
| __flush_tlb_slot(tlb); | |
| } | |
| local_irq_restore(flags); | |
| } | |
| void local_flush_tlb_mm(struct mm_struct *mm) | |
| { | |
| unsigned long flags; | |
| unsigned int cpu = smp_processor_id(); | |
| if (cpu_context(cpu, mm) == NO_CONTEXT) | |
| return; | |
| local_irq_save(flags); | |
| cpu_context(cpu, mm) = NO_CONTEXT; | |
| if (mm == current->mm) | |
| activate_context(mm, cpu); | |
| local_irq_restore(flags); | |
| } | |
| void local_flush_tlb_all(void) | |
| { | |
| /* Invalidate all, including shared pages, excluding fixed TLBs */ | |
| unsigned long flags, tlb; | |
| local_irq_save(flags); | |
| /* Flush each ITLB entry */ | |
| for_each_itlb_entry(tlb) | |
| __flush_tlb_slot(tlb); | |
| /* Flush each DTLB entry */ | |
| for_each_dtlb_entry(tlb) | |
| __flush_tlb_slot(tlb); | |
| local_irq_restore(flags); | |
| } | |
| void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) | |
| { | |
| /* FIXME: Optimize this later.. */ | |
| flush_tlb_all(); | |
| } | |
| void __flush_tlb_global(void) | |
| { | |
| flush_tlb_all(); | |
| } |