1/* 2 * BK Id: %F% %I% %G% %U% %#% 3 */ 4/* 5 * This file contains the routines for TLB flushing. 6 * On machines where the MMU uses a hash table to store virtual to 7 * physical translations, these routines flush entries from the the 8 * hash table also. 9 * -- paulus 10 * 11 * Derived from arch/ppc/mm/init.c: 12 * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) 13 * 14 * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) 15 * and Cort Dougan (PReP) (cort@cs.nmt.edu) 16 * Copyright (C) 1996 Paul Mackerras 17 * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). 18 * 19 * Derived from "arch/i386/mm/init.c" 20 * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds 21 * 22 * This program is free software; you can redistribute it and/or 23 * modify it under the terms of the GNU General Public License 24 * as published by the Free Software Foundation; either version 25 * 2 of the License, or (at your option) any later version. 26 * 27 */ 28 29#include <linux/config.h> 30#include <linux/kernel.h> 31#include <linux/mm.h> 32#include <linux/init.h> 33 34#include "mmu_decl.h" 35 36/* 37 * TLB flushing: 38 * 39 * - flush_tlb_all() flushes all processes TLBs 40 * - flush_tlb_mm(mm) flushes the specified mm context TLB's 41 * - flush_tlb_page(vma, vmaddr) flushes one page 42 * - flush_tlb_range(mm, start, end) flushes a range of pages 43 * 44 * since the hardware hash table functions as an extension of the 45 * tlb as far as the linux tables are concerned, flush it too. 46 * -- Cort 47 */ 48 49/* 50 * Flush all tlb/hash table entries (except perhaps for those 51 * mapping RAM starting at PAGE_OFFSET, since they never change). 52 */ 53void 54local_flush_tlb_all(void) 55{ 56 /* aargh!!! */ 57 /* 58 * Just flush the kernel part of the address space, that's 59 * all that the current callers of this require. 60 * Eventually I hope to persuade the powers that be that 61 * we can and should dispense with flush_tlb_all(). 62 * -- paulus. 63 */ 64 local_flush_tlb_range(&init_mm, TASK_SIZE, ~0UL); 65 66#ifdef CONFIG_SMP 67 smp_send_tlb_invalidate(0); 68#endif /* CONFIG_SMP */ 69} 70 71/* 72 * Flush all the (user) entries for the address space described 73 * by mm. We can't rely on mm->mmap describing all the entries 74 * that might be in the hash table. 75 */ 76void 77local_flush_tlb_mm(struct mm_struct *mm) 78{ 79 if (Hash == 0) { 80 _tlbia(); 81 return; 82 } 83 84 if (mm->map_count) { 85 struct vm_area_struct *mp; 86 for (mp = mm->mmap; mp != NULL; mp = mp->vm_next) 87 local_flush_tlb_range(mm, mp->vm_start, mp->vm_end); 88 } else 89 local_flush_tlb_range(mm, 0, TASK_SIZE); 90 91#ifdef CONFIG_SMP 92 smp_send_tlb_invalidate(0); 93#endif 94} 95 96void 97local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) 98{ 99 struct mm_struct *mm; 100 pmd_t *pmd; 101 pte_t *pte; 102 103 if (Hash == 0) { 104 _tlbie(vmaddr); 105 return; 106 } 107 mm = (vmaddr < TASK_SIZE)? vma->vm_mm: &init_mm; 108 pmd = pmd_offset(pgd_offset(mm, vmaddr), vmaddr); 109 if (!pmd_none(*pmd)) { 110 pte = pte_offset(pmd, vmaddr); 111 if (pte_val(*pte) & _PAGE_HASHPTE) 112 flush_hash_page(mm->context, vmaddr, pte); 113 } 114#ifdef CONFIG_SMP 115 smp_send_tlb_invalidate(0); 116#endif 117} 118 119 120/* 121 * For each address in the range, find the pte for the address 122 * and check _PAGE_HASHPTE bit; if it is set, find and destroy 123 * the corresponding HPTE. 124 */ 125void 126local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) 127{ 128 pmd_t *pmd; 129 pte_t *pte; 130 unsigned long pmd_end; 131 unsigned int ctx = mm->context; 132 133 if (Hash == 0) { 134 _tlbia(); 135 return; 136 } 137 start &= PAGE_MASK; 138 if (start >= end) 139 return; 140 pmd = pmd_offset(pgd_offset(mm, start), start); 141 do { 142 pmd_end = (start + PGDIR_SIZE) & PGDIR_MASK; 143 if (!pmd_none(*pmd)) { 144 if (!pmd_end || pmd_end > end) 145 pmd_end = end; 146 pte = pte_offset(pmd, start); 147 do { 148 if ((pte_val(*pte) & _PAGE_HASHPTE) != 0) 149 flush_hash_page(ctx, start, pte); 150 start += PAGE_SIZE; 151 ++pte; 152 } while (start && start < pmd_end); 153 } else { 154 start = pmd_end; 155 } 156 ++pmd; 157 } while (start && start < end); 158 159#ifdef CONFIG_SMP 160 smp_send_tlb_invalidate(0); 161#endif 162} 163