summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King <rmk@flint.arm.linux.org.uk>2003-08-14 17:51:39 +0100
committerRussell King <rmk@flint.arm.linux.org.uk>2003-08-14 17:51:39 +0100
commitf1a3a362000963535d0be3d6fcac8f25c4569dcb (patch)
tree2a3fc4ad028dd3732011a2e6de00fdd3d35ca02d
parentaa5f1ea77b7d635db0345ee068b68f595c265193 (diff)
[ARM] Add ARMv6 MMU context handling.
Add MMU context ID handling.
-rw-r--r--arch/arm/mm/mmu.c46
-rw-r--r--include/asm-arm/mmu.h17
-rw-r--r--include/asm-arm/mmu_context.h40
3 files changed, 99 insertions, 4 deletions
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
new file mode 100644
index 000000000000..3fd17d897f57
--- /dev/null
+++ b/arch/arm/mm/mmu.c
@@ -0,0 +1,46 @@
+/*
+ * linux/arch/arm/mm/mmu.c
+ *
+ * Copyright (C) 2002-2003 Deep Blue Solutions Ltd, all rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+#include <asm/mmu_context.h>
+#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
+
+unsigned int cpu_last_asid = { 1 << ASID_BITS };
+
+/*
+ * We fork()ed a process, and we need a new context for the child
+ * to run in. We reserve version 0 for initial tasks so we will
+ * always allocate an ASID.
+ */
+void __init_new_context(struct task_struct *tsk, struct mm_struct *mm)
+{
+ mm->context.id = 0;
+}
+
+void __new_context(struct mm_struct *mm)
+{
+ unsigned int asid;
+
+ asid = ++cpu_last_asid;
+ if (asid == 0)
+ asid = cpu_last_asid = 1 << ASID_BITS;
+
+ /*
+ * If we've used up all our ASIDs, we need
+ * to start a new version and flush the TLB.
+ */
+ if ((asid & ~ASID_MASK) == 0)
+ flush_tlb_all();
+
+ mm->context.id = asid;
+}
diff --git a/include/asm-arm/mmu.h b/include/asm-arm/mmu.h
index 9b8d3d781a1e..c33797d6cc7b 100644
--- a/include/asm-arm/mmu.h
+++ b/include/asm-arm/mmu.h
@@ -1,9 +1,18 @@
#ifndef __ARM_MMU_H
#define __ARM_MMU_H
-/*
- * The ARM doesn't have a mmu context
- */
-typedef struct { } mm_context_t;
+#include <linux/config.h>
+
+typedef struct {
+#if __LINUX_ARM_ARCH__ >= 6
+ unsigned int id;
+#endif
+} mm_context_t;
+
+#if __LINUX_ARM_ARCH__ >= 6
+#define ASID(mm) ((mm)->context.id & 255)
+#else
+#define ASID(mm) (0)
+#endif
#endif
diff --git a/include/asm-arm/mmu_context.h b/include/asm-arm/mmu_context.h
index e0340f5fbf32..a4cb79241a73 100644
--- a/include/asm-arm/mmu_context.h
+++ b/include/asm-arm/mmu_context.h
@@ -15,7 +15,45 @@
#include <asm/proc-fns.h>
+#if __LINUX_ARM_ARCH__ >= 6
+
+/*
+ * On ARMv6, we have the following structure in the Context ID:
+ *
+ * 31 7 0
+ * +-------------------------+-----------+
+ * | process ID | ASID |
+ * +-------------------------+-----------+
+ * | context ID |
+ * +-------------------------------------+
+ *
+ * The ASID is used to tag entries in the CPU caches and TLBs.
+ * The context ID is used by debuggers and trace logic, and
+ * should be unique within all running processes.
+ */
+#define ASID_BITS 8
+#define ASID_MASK ((~0) << ASID_BITS)
+
+extern unsigned int cpu_last_asid;
+
+void __init_new_context(struct task_struct *tsk, struct mm_struct *mm);
+void __new_context(struct mm_struct *mm);
+
+static inline void check_context(struct mm_struct *mm)
+{
+ if (unlikely((mm->context.id ^ cpu_last_asid) >> ASID_BITS))
+ __new_context(mm);
+}
+
+#define init_new_context(tsk,mm) (__init_new_context(tsk,mm),0)
+
+#else
+
+#define check_context(mm) do { } while (0)
#define init_new_context(tsk,mm) 0
+
+#endif
+
#define destroy_context(mm) do { } while(0)
/*
@@ -43,6 +81,7 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *tsk)
{
if (prev != next) {
+ check_context(next);
cpu_switch_mm(next->pgd, next);
}
}
@@ -51,6 +90,7 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,
static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next)
{
+ check_context(next);
cpu_switch_mm(next->pgd, next);
}