blob: aaa291fc072ed27ad9055cc096e4a60a4c772b4f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Russell Kingd84b4712006-08-21 19:23:38 +01002 * linux/arch/arm/mm/context.c
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 *
4 * Copyright (C) 2002-2003 Deep Blue Solutions Ltd, all rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10#include <linux/init.h>
11#include <linux/sched.h>
12#include <linux/mm.h>
Catalin Marinas11805bc2010-01-26 19:09:42 +010013#include <linux/smp.h>
14#include <linux/percpu.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015
16#include <asm/mmu_context.h>
17#include <asm/tlbflush.h>
18
Thomas Gleixnerbd31b852009-07-03 08:44:46 -050019static DEFINE_RAW_SPINLOCK(cpu_asid_lock);
Russell King8678c1f2007-05-08 20:03:09 +010020unsigned int cpu_last_asid = ASID_FIRST_VERSION;
Catalin Marinas11805bc2010-01-26 19:09:42 +010021#ifdef CONFIG_SMP
22DEFINE_PER_CPU(struct mm_struct *, current_mm);
23#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
Catalin Marinas14d8c952011-11-22 17:30:31 +000025#ifdef CONFIG_ARM_LPAE
Will Deacon3c5f7e72011-05-31 15:38:43 +010026static void cpu_set_reserved_ttbr0(void)
27{
28 unsigned long ttbl = __pa(swapper_pg_dir);
29 unsigned long ttbh = 0;
30
31 /*
32 * Set TTBR0 to swapper_pg_dir which contains only global entries. The
33 * ASID is set to 0.
34 */
35 asm volatile(
36 " mcrr p15, 0, %0, %1, c2 @ set TTBR0\n"
37 :
38 : "r" (ttbl), "r" (ttbh));
39 isb();
Catalin Marinas14d8c952011-11-22 17:30:31 +000040}
41#else
Will Deacon3c5f7e72011-05-31 15:38:43 +010042static void cpu_set_reserved_ttbr0(void)
43{
44 u32 ttb;
45 /* Copy TTBR1 into TTBR0 */
46 asm volatile(
47 " mrc p15, 0, %0, c2, c0, 1 @ read TTBR1\n"
48 " mcr p15, 0, %0, c2, c0, 0 @ set TTBR0\n"
49 : "=r" (ttb));
50 isb();
51}
Catalin Marinas14d8c952011-11-22 17:30:31 +000052#endif
53
Linus Torvalds1da177e2005-04-16 15:20:36 -070054/*
55 * We fork()ed a process, and we need a new context for the child
Will Deacon3c5f7e72011-05-31 15:38:43 +010056 * to run in.
Linus Torvalds1da177e2005-04-16 15:20:36 -070057 */
58void __init_new_context(struct task_struct *tsk, struct mm_struct *mm)
59{
60 mm->context.id = 0;
Thomas Gleixnerbd31b852009-07-03 08:44:46 -050061 raw_spin_lock_init(&mm->context.id_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070062}
63
Catalin Marinas11805bc2010-01-26 19:09:42 +010064static void flush_context(void)
65{
Will Deacon3c5f7e72011-05-31 15:38:43 +010066 cpu_set_reserved_ttbr0();
Catalin Marinas11805bc2010-01-26 19:09:42 +010067 local_flush_tlb_all();
68 if (icache_is_vivt_asid_tagged()) {
69 __flush_icache_all();
70 dsb();
71 }
72}
73
74#ifdef CONFIG_SMP
75
76static void set_mm_context(struct mm_struct *mm, unsigned int asid)
77{
78 unsigned long flags;
79
80 /*
81 * Locking needed for multi-threaded applications where the
82 * same mm->context.id could be set from different CPUs during
83 * the broadcast. This function is also called via IPI so the
84 * mm->context.id_lock has to be IRQ-safe.
85 */
Thomas Gleixnerbd31b852009-07-03 08:44:46 -050086 raw_spin_lock_irqsave(&mm->context.id_lock, flags);
Catalin Marinas11805bc2010-01-26 19:09:42 +010087 if (likely((mm->context.id ^ cpu_last_asid) >> ASID_BITS)) {
88 /*
89 * Old version of ASID found. Set the new one and
90 * reset mm_cpumask(mm).
91 */
92 mm->context.id = asid;
93 cpumask_clear(mm_cpumask(mm));
94 }
Thomas Gleixnerbd31b852009-07-03 08:44:46 -050095 raw_spin_unlock_irqrestore(&mm->context.id_lock, flags);
Catalin Marinas11805bc2010-01-26 19:09:42 +010096
97 /*
98 * Set the mm_cpumask(mm) bit for the current CPU.
99 */
100 cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));
101}
102
103/*
104 * Reset the ASID on the current CPU. This function call is broadcast
105 * from the CPU handling the ASID rollover and holding cpu_asid_lock.
106 */
107static void reset_context(void *info)
108{
109 unsigned int asid;
110 unsigned int cpu = smp_processor_id();
111 struct mm_struct *mm = per_cpu(current_mm, cpu);
112
113 /*
114 * Check if a current_mm was set on this CPU as it might still
115 * be in the early booting stages and using the reserved ASID.
116 */
117 if (!mm)
118 return;
119
120 smp_rmb();
Russell Kinga0a54d32011-06-09 10:12:41 +0100121 asid = cpu_last_asid + cpu + 1;
Catalin Marinas11805bc2010-01-26 19:09:42 +0100122
123 flush_context();
124 set_mm_context(mm, asid);
125
126 /* set the new ASID */
Will Deacon3c5f7e72011-05-31 15:38:43 +0100127 cpu_switch_mm(mm->pgd, mm);
Catalin Marinas11805bc2010-01-26 19:09:42 +0100128}
129
130#else
131
132static inline void set_mm_context(struct mm_struct *mm, unsigned int asid)
133{
134 mm->context.id = asid;
135 cpumask_copy(mm_cpumask(mm), cpumask_of(smp_processor_id()));
136}
137
138#endif
139
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140void __new_context(struct mm_struct *mm)
141{
142 unsigned int asid;
143
Thomas Gleixnerbd31b852009-07-03 08:44:46 -0500144 raw_spin_lock(&cpu_asid_lock);
Catalin Marinas11805bc2010-01-26 19:09:42 +0100145#ifdef CONFIG_SMP
146 /*
147 * Check the ASID again, in case the change was broadcast from
148 * another CPU before we acquired the lock.
149 */
150 if (unlikely(((mm->context.id ^ cpu_last_asid) >> ASID_BITS) == 0)) {
151 cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));
Thomas Gleixnerbd31b852009-07-03 08:44:46 -0500152 raw_spin_unlock(&cpu_asid_lock);
Catalin Marinas11805bc2010-01-26 19:09:42 +0100153 return;
154 }
155#endif
156 /*
157 * At this point, it is guaranteed that the current mm (with
158 * an old ASID) isn't active on any other CPU since the ASIDs
159 * are changed simultaneously via IPI.
160 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 asid = ++cpu_last_asid;
162 if (asid == 0)
Russell King8678c1f2007-05-08 20:03:09 +0100163 asid = cpu_last_asid = ASID_FIRST_VERSION;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164
165 /*
166 * If we've used up all our ASIDs, we need
167 * to start a new version and flush the TLB.
168 */
Russell King8678c1f2007-05-08 20:03:09 +0100169 if (unlikely((asid & ~ASID_MASK) == 0)) {
Russell Kinga0a54d32011-06-09 10:12:41 +0100170 asid = cpu_last_asid + smp_processor_id() + 1;
Catalin Marinas11805bc2010-01-26 19:09:42 +0100171 flush_context();
172#ifdef CONFIG_SMP
173 smp_wmb();
174 smp_call_function(reset_context, NULL, 1);
175#endif
Russell Kinga0a54d32011-06-09 10:12:41 +0100176 cpu_last_asid += NR_CPUS;
Catalin Marinas9d99df42007-02-05 14:47:40 +0100177 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178
Catalin Marinas11805bc2010-01-26 19:09:42 +0100179 set_mm_context(mm, asid);
Thomas Gleixnerbd31b852009-07-03 08:44:46 -0500180 raw_spin_unlock(&cpu_asid_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181}