1 /* 2 * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu> 3 * Copyright (C) 2008-2009 PetaLogix 4 * Copyright (C) 2006 Atmark Techno, Inc. 5 * 6 * This file is subject to the terms and conditions of the GNU General Public 7 * License. See the file "COPYING" in the main directory of this archive 8 * for more details. 9 */ 10 11 #ifndef _ASM_MICROBLAZE_MMU_CONTEXT_H 12 #define _ASM_MICROBLAZE_MMU_CONTEXT_H 13 14 #include <linux/atomic.h> 15 #include <linux/mm_types.h> 16 #include <linux/sched.h> 17 18 #include <asm/bitops.h> 19 #include <asm/mmu.h> 20 #include <asm-generic/mm_hooks.h> 21 22 # ifdef __KERNEL__ 23 /* 24 * This function defines the mapping from contexts to VSIDs (virtual 25 * segment IDs). We use a skew on both the context and the high 4 bits 26 * of the 32-bit virtual address (the "effective segment ID") in order 27 * to spread out the entries in the MMU hash table. 28 */ 29 # define CTX_TO_VSID(ctx, va) (((ctx) * (897 * 16) + ((va) >> 28) * 0x111) \ 30 & 0xffffff) 31 32 /* 33 MicroBlaze has 256 contexts, so we can just rotate through these 34 as a way of "switching" contexts. If the TID of the TLB is zero, 35 the PID/TID comparison is disabled, so we can use a TID of zero 36 to represent all kernel pages as shared among all contexts. 37 */ 38 39 static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) 40 { 41 } 42 43 # define NO_CONTEXT 256 44 # define LAST_CONTEXT 255 45 # define FIRST_CONTEXT 1 46 47 /* 48 * Set the current MMU context. 49 * This is done byloading up the segment registers for the user part of the 50 * address space. 51 * 52 * Since the PGD is immediately available, it is much faster to simply 53 * pass this along as a second parameter, which is required for 8xx and 54 * can be used for debugging on all processors (if you happen to have 55 * an Abatron). 56 */ 57 extern void set_context(mm_context_t context, pgd_t *pgd); 58 59 /* 60 * Bitmap of contexts in use. 61 * The size of this bitmap is LAST_CONTEXT + 1 bits. 62 */ 63 extern unsigned long context_map[]; 64 65 /* 66 * This caches the next context number that we expect to be free. 67 * Its use is an optimization only, we can't rely on this context 68 * number to be free, but it usually will be. 69 */ 70 extern mm_context_t next_mmu_context; 71 72 /* 73 * Since we don't have sufficient contexts to give one to every task 74 * that could be in the system, we need to be able to steal contexts. 75 * These variables support that. 76 */ 77 extern atomic_t nr_free_contexts; 78 extern struct mm_struct *context_mm[LAST_CONTEXT+1]; 79 extern void steal_context(void); 80 81 /* 82 * Get a new mmu context for the address space described by `mm'. 83 */ 84 static inline void get_mmu_context(struct mm_struct *mm) 85 { 86 mm_context_t ctx; 87 88 if (mm->context != NO_CONTEXT) 89 return; 90 while (atomic_dec_if_positive(&nr_free_contexts) < 0) 91 steal_context(); 92 ctx = next_mmu_context; 93 while (test_and_set_bit(ctx, context_map)) { 94 ctx = find_next_zero_bit(context_map, LAST_CONTEXT+1, ctx); 95 if (ctx > LAST_CONTEXT) 96 ctx = 0; 97 } 98 next_mmu_context = (ctx + 1) & LAST_CONTEXT; 99 mm->context = ctx; 100 context_mm[ctx] = mm; 101 } 102 103 /* 104 * Set up the context for a new address space. 105 */ 106 # define init_new_context(tsk, mm) (((mm)->context = NO_CONTEXT), 0) 107 108 /* 109 * We're finished using the context for an address space. 110 */ 111 static inline void destroy_context(struct mm_struct *mm) 112 { 113 if (mm->context != NO_CONTEXT) { 114 clear_bit(mm->context, context_map); 115 mm->context = NO_CONTEXT; 116 atomic_inc(&nr_free_contexts); 117 } 118 } 119 120 static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, 121 struct task_struct *tsk) 122 { 123 tsk->thread.pgdir = next->pgd; 124 get_mmu_context(next); 125 set_context(next->context, next->pgd); 126 } 127 128 /* 129 * After we have set current->mm to a new value, this activates 130 * the context for the new mm so we see the new mappings. 131 */ 132 static inline void activate_mm(struct mm_struct *active_mm, 133 struct mm_struct *mm) 134 { 135 current->thread.pgdir = mm->pgd; 136 get_mmu_context(mm); 137 set_context(mm->context, mm->pgd); 138 } 139 140 extern void mmu_context_init(void); 141 142 # endif /* __KERNEL__ */ 143 #endif /* _ASM_MICROBLAZE_MMU_CONTEXT_H */ 144