blob: 5ac09e8b4030fb54dd96ef74a76949a4fc226598 [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.
Will Deaconb5466f82012-06-15 14:47:31 +01005 * Copyright (C) 2012 ARM Limited
6 *
7 * Author: Will Deacon <will.deacon@arm.com>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13#include <linux/init.h>
14#include <linux/sched.h>
15#include <linux/mm.h>
Catalin Marinas11805bc2010-01-26 19:09:42 +010016#include <linux/smp.h>
17#include <linux/percpu.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018
19#include <asm/mmu_context.h>
Will Deaconb5466f82012-06-15 14:47:31 +010020#include <asm/smp_plat.h>
Will Deacon575320d2012-07-06 15:43:03 +010021#include <asm/thread_notify.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <asm/tlbflush.h>
23
Will Deaconb5466f82012-06-15 14:47:31 +010024/*
25 * On ARMv6, we have the following structure in the Context ID:
26 *
27 * 31 7 0
28 * +-------------------------+-----------+
29 * | process ID | ASID |
30 * +-------------------------+-----------+
31 * | context ID |
32 * +-------------------------------------+
33 *
34 * The ASID is used to tag entries in the CPU caches and TLBs.
35 * The context ID is used by debuggers and trace logic, and
36 * should be unique within all running processes.
37 */
38#define ASID_FIRST_VERSION (1ULL << ASID_BITS)
39
Thomas Gleixnerbd31b852009-07-03 08:44:46 -050040static DEFINE_RAW_SPINLOCK(cpu_asid_lock);
Will Deacon4b883162012-07-27 12:31:35 +010041static atomic64_t cpu_last_asid = ATOMIC64_INIT(ASID_FIRST_VERSION);
Will Deaconb5466f82012-06-15 14:47:31 +010042
Will Deacon4b883162012-07-27 12:31:35 +010043static DEFINE_PER_CPU(atomic64_t, active_asids);
Will Deaconb5466f82012-06-15 14:47:31 +010044static DEFINE_PER_CPU(u64, reserved_asids);
45static cpumask_t tlb_flush_pending;
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
Catalin Marinas14d8c952011-11-22 17:30:31 +000047#ifdef CONFIG_ARM_LPAE
Will Deaconb5466f82012-06-15 14:47:31 +010048static void cpu_set_reserved_ttbr0(void)
Will Deacon3c5f7e72011-05-31 15:38:43 +010049{
50 unsigned long ttbl = __pa(swapper_pg_dir);
51 unsigned long ttbh = 0;
52
53 /*
54 * Set TTBR0 to swapper_pg_dir which contains only global entries. The
55 * ASID is set to 0.
56 */
57 asm volatile(
58 " mcrr p15, 0, %0, %1, c2 @ set TTBR0\n"
59 :
60 : "r" (ttbl), "r" (ttbh));
61 isb();
Catalin Marinas14d8c952011-11-22 17:30:31 +000062}
63#else
Will Deaconb5466f82012-06-15 14:47:31 +010064static void cpu_set_reserved_ttbr0(void)
Will Deacon3c5f7e72011-05-31 15:38:43 +010065{
66 u32 ttb;
67 /* Copy TTBR1 into TTBR0 */
68 asm volatile(
69 " mrc p15, 0, %0, c2, c0, 1 @ read TTBR1\n"
70 " mcr p15, 0, %0, c2, c0, 0 @ set TTBR0\n"
71 : "=r" (ttb));
72 isb();
73}
Catalin Marinas14d8c952011-11-22 17:30:31 +000074#endif
75
Will Deacon575320d2012-07-06 15:43:03 +010076#ifdef CONFIG_PID_IN_CONTEXTIDR
77static int contextidr_notifier(struct notifier_block *unused, unsigned long cmd,
78 void *t)
79{
80 u32 contextidr;
81 pid_t pid;
82 struct thread_info *thread = t;
83
84 if (cmd != THREAD_NOTIFY_SWITCH)
85 return NOTIFY_DONE;
86
87 pid = task_pid_nr(thread->task) << ASID_BITS;
88 asm volatile(
89 " mrc p15, 0, %0, c13, c0, 1\n"
Will Deaconae3790b2012-08-24 15:21:52 +010090 " and %0, %0, %2\n"
91 " orr %0, %0, %1\n"
92 " mcr p15, 0, %0, c13, c0, 1\n"
Will Deacon575320d2012-07-06 15:43:03 +010093 : "=r" (contextidr), "+r" (pid)
Will Deaconae3790b2012-08-24 15:21:52 +010094 : "I" (~ASID_MASK));
Will Deacon575320d2012-07-06 15:43:03 +010095 isb();
96
97 return NOTIFY_OK;
98}
99
100static struct notifier_block contextidr_notifier_block = {
101 .notifier_call = contextidr_notifier,
102};
103
104static int __init contextidr_notifier_init(void)
105{
106 return thread_register_notifier(&contextidr_notifier_block);
107}
108arch_initcall(contextidr_notifier_init);
109#endif
110
Will Deaconb5466f82012-06-15 14:47:31 +0100111static void flush_context(unsigned int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112{
Will Deaconb5466f82012-06-15 14:47:31 +0100113 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114
Will Deaconb5466f82012-06-15 14:47:31 +0100115 /* Update the list of reserved ASIDs. */
Will Deaconb5466f82012-06-15 14:47:31 +0100116 for_each_possible_cpu(i)
Will Deacon4b883162012-07-27 12:31:35 +0100117 per_cpu(reserved_asids, i) =
118 atomic64_xchg(&per_cpu(active_asids, i), 0);
119 per_cpu(reserved_asids, cpu) = 0;
Will Deaconb5466f82012-06-15 14:47:31 +0100120
121 /* Queue a TLB invalidate and flush the I-cache if necessary. */
122 if (!tlb_ops_need_broadcast())
123 cpumask_set_cpu(cpu, &tlb_flush_pending);
124 else
125 cpumask_setall(&tlb_flush_pending);
126
127 if (icache_is_vivt_asid_tagged())
Catalin Marinas11805bc2010-01-26 19:09:42 +0100128 __flush_icache_all();
Catalin Marinas11805bc2010-01-26 19:09:42 +0100129}
130
Will Deaconb5466f82012-06-15 14:47:31 +0100131static int is_reserved_asid(u64 asid, u64 mask)
Catalin Marinas11805bc2010-01-26 19:09:42 +0100132{
Will Deaconb5466f82012-06-15 14:47:31 +0100133 int cpu;
134 for_each_possible_cpu(cpu)
135 if ((per_cpu(reserved_asids, cpu) & mask) == (asid & mask))
136 return 1;
137 return 0;
138}
Catalin Marinas11805bc2010-01-26 19:09:42 +0100139
Will Deaconb5466f82012-06-15 14:47:31 +0100140static void new_context(struct mm_struct *mm, unsigned int cpu)
141{
142 u64 asid = mm->context.id;
143
144 if (asid != 0 && is_reserved_asid(asid, ULLONG_MAX)) {
Catalin Marinas11805bc2010-01-26 19:09:42 +0100145 /*
Will Deaconb5466f82012-06-15 14:47:31 +0100146 * Our current ASID was active during a rollover, we can
147 * continue to use it and this was just a false alarm.
Catalin Marinas11805bc2010-01-26 19:09:42 +0100148 */
Will Deacon4b883162012-07-27 12:31:35 +0100149 asid = (atomic64_read(&cpu_last_asid) & ASID_MASK) | \
150 (asid & ~ASID_MASK);
Will Deaconb5466f82012-06-15 14:47:31 +0100151 } else {
152 /*
153 * Allocate a free ASID. If we can't find one, take a
154 * note of the currently active ASIDs and mark the TLBs
155 * as requiring flushes.
156 */
157 do {
Will Deacon4b883162012-07-27 12:31:35 +0100158 asid = atomic64_inc_return(&cpu_last_asid);
Will Deaconb5466f82012-06-15 14:47:31 +0100159 if ((asid & ~ASID_MASK) == 0)
160 flush_context(cpu);
161 } while (is_reserved_asid(asid, ~ASID_MASK));
Catalin Marinas11805bc2010-01-26 19:09:42 +0100162 cpumask_clear(mm_cpumask(mm));
163 }
Catalin Marinas11805bc2010-01-26 19:09:42 +0100164
Catalin Marinas11805bc2010-01-26 19:09:42 +0100165 mm->context.id = asid;
Catalin Marinas11805bc2010-01-26 19:09:42 +0100166}
167
Will Deaconb5466f82012-06-15 14:47:31 +0100168void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169{
Will Deaconb5466f82012-06-15 14:47:31 +0100170 unsigned long flags;
171 unsigned int cpu = smp_processor_id();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172
Will Deaconb5466f82012-06-15 14:47:31 +0100173 if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq))
174 __check_kvm_seq(mm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175
176 /*
Will Deaconb5466f82012-06-15 14:47:31 +0100177 * Required during context switch to avoid speculative page table
178 * walking with the wrong TTBR.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 */
Will Deaconb5466f82012-06-15 14:47:31 +0100180 cpu_set_reserved_ttbr0();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181
Will Deacon4b883162012-07-27 12:31:35 +0100182 if (!((mm->context.id ^ atomic64_read(&cpu_last_asid)) >> ASID_BITS)
183 && atomic64_xchg(&per_cpu(active_asids, cpu), mm->context.id))
184 goto switch_mm_fastpath;
185
Will Deaconb5466f82012-06-15 14:47:31 +0100186 raw_spin_lock_irqsave(&cpu_asid_lock, flags);
187 /* Check that our ASID belongs to the current generation. */
Will Deacon4b883162012-07-27 12:31:35 +0100188 if ((mm->context.id ^ atomic64_read(&cpu_last_asid)) >> ASID_BITS)
Will Deaconb5466f82012-06-15 14:47:31 +0100189 new_context(mm, cpu);
190
Will Deacon4b883162012-07-27 12:31:35 +0100191 atomic64_set(&per_cpu(active_asids, cpu), mm->context.id);
Will Deaconb5466f82012-06-15 14:47:31 +0100192 cpumask_set_cpu(cpu, mm_cpumask(mm));
193
194 if (cpumask_test_and_clear_cpu(cpu, &tlb_flush_pending))
195 local_flush_tlb_all();
196 raw_spin_unlock_irqrestore(&cpu_asid_lock, flags);
197
Will Deacon4b883162012-07-27 12:31:35 +0100198switch_mm_fastpath:
Will Deaconb5466f82012-06-15 14:47:31 +0100199 cpu_switch_mm(mm->pgd, mm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200}