1 | /* |
2 | * MMU context handling. |
3 | * |
4 | * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch> |
5 | * Copyright (C) 2009 Wind River Systems Inc |
6 | * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com |
7 | * |
8 | * This file is subject to the terms and conditions of the GNU General Public |
9 | * License. See the file "COPYING" in the main directory of this archive |
10 | * for more details. |
11 | */ |
12 | |
13 | #include <linux/mm.h> |
14 | |
15 | #include <asm/cpuinfo.h> |
16 | #include <asm/mmu_context.h> |
17 | #include <asm/tlb.h> |
18 | |
19 | /* The pids position and mask in context */ |
20 | #define PID_SHIFT 0 |
21 | #define PID_BITS (cpuinfo.tlb_pid_num_bits) |
22 | #define PID_MASK ((1UL << PID_BITS) - 1) |
23 | |
24 | /* The versions position and mask in context */ |
25 | #define VERSION_BITS (32 - PID_BITS) |
26 | #define VERSION_SHIFT (PID_SHIFT + PID_BITS) |
27 | #define VERSION_MASK ((1UL << VERSION_BITS) - 1) |
28 | |
29 | /* Return the version part of a context */ |
30 | #define CTX_VERSION(c) (((c) >> VERSION_SHIFT) & VERSION_MASK) |
31 | |
32 | /* Return the pid part of a context */ |
33 | #define CTX_PID(c) (((c) >> PID_SHIFT) & PID_MASK) |
34 | |
35 | /* Value of the first context (version 1, pid 0) */ |
36 | #define FIRST_CTX ((1UL << VERSION_SHIFT) | (0 << PID_SHIFT)) |
37 | |
38 | static mm_context_t next_mmu_context; |
39 | |
40 | /* |
41 | * Initialize MMU context management stuff. |
42 | */ |
43 | void __init mmu_context_init(void) |
44 | { |
45 | /* We need to set this here because the value depends on runtime data |
46 | * from cpuinfo */ |
47 | next_mmu_context = FIRST_CTX; |
48 | } |
49 | |
50 | /* |
51 | * Set new context (pid), keep way |
52 | */ |
53 | static void set_context(mm_context_t context) |
54 | { |
55 | set_mmu_pid(CTX_PID(context)); |
56 | } |
57 | |
58 | static mm_context_t get_new_context(void) |
59 | { |
60 | /* Return the next pid */ |
61 | next_mmu_context += (1UL << PID_SHIFT); |
62 | |
63 | /* If the pid field wraps around we increase the version and |
64 | * flush the tlb */ |
65 | if (unlikely(CTX_PID(next_mmu_context) == 0)) { |
66 | /* Version is incremented since the pid increment above |
67 | * overflows info version */ |
68 | flush_cache_all(); |
69 | flush_tlb_all(); |
70 | } |
71 | |
72 | /* If the version wraps we start over with the first generation, we do |
73 | * not need to flush the tlb here since it's always done above */ |
74 | if (unlikely(CTX_VERSION(next_mmu_context) == 0)) |
75 | next_mmu_context = FIRST_CTX; |
76 | |
77 | return next_mmu_context; |
78 | } |
79 | |
80 | void switch_mm(struct mm_struct *prev, struct mm_struct *next, |
81 | struct task_struct *tsk) |
82 | { |
83 | unsigned long flags; |
84 | |
85 | local_irq_save(flags); |
86 | |
87 | /* If the process context we are swapping in has a different context |
88 | * generation then we have it should get a new generation/pid */ |
89 | if (unlikely(CTX_VERSION(next->context) != |
90 | CTX_VERSION(next_mmu_context))) |
91 | next->context = get_new_context(); |
92 | |
93 | /* Save the current pgd so the fast tlb handler can find it */ |
94 | pgd_current = next->pgd; |
95 | |
96 | /* Set the current context */ |
97 | set_context(next->context); |
98 | |
99 | local_irq_restore(flags); |
100 | } |
101 | |
102 | /* |
103 | * After we have set current->mm to a new value, this activates |
104 | * the context for the new mm so we see the new mappings. |
105 | */ |
106 | void activate_mm(struct mm_struct *prev, struct mm_struct *next) |
107 | { |
108 | next->context = get_new_context(); |
109 | set_context(next->context); |
110 | pgd_current = next->pgd; |
111 | } |
112 | |
113 | unsigned long get_pid_from_context(mm_context_t *context) |
114 | { |
115 | return CTX_PID((*context)); |
116 | } |
117 | |