|
@@ -0,0 +1,193 @@
|
|
|
+/*
|
|
|
+ * linux/arch/arm/mm/mmu.c
|
|
|
+ *
|
|
|
+ * Copyright (C) 1995-2005 Russell King
|
|
|
+ *
|
|
|
+ * 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/module.h>
|
|
|
+#include <linux/kernel.h>
|
|
|
+#include <linux/errno.h>
|
|
|
+#include <linux/init.h>
|
|
|
+#include <linux/mman.h>
|
|
|
+#include <linux/nodemask.h>
|
|
|
+#include <linux/memblock.h>
|
|
|
+#include <linux/fs.h>
|
|
|
+#include <linux/vmalloc.h>
|
|
|
+#include <linux/sizes.h>
|
|
|
+
|
|
|
+#include <asm/cp15.h>
|
|
|
+#include <asm/cputype.h>
|
|
|
+#include <asm/sections.h>
|
|
|
+#include <asm/cachetype.h>
|
|
|
+#include <asm/setup.h>
|
|
|
+#include <asm/smp_plat.h>
|
|
|
+#include <asm/tlb.h>
|
|
|
+#include <asm/highmem.h>
|
|
|
+#include <asm/system_info.h>
|
|
|
+#include <asm/traps.h>
|
|
|
+
|
|
|
+#include <asm/mach/arch.h>
|
|
|
+#include <asm/mach/map.h>
|
|
|
+#include <asm/mach/pci.h>
|
|
|
+
|
|
|
+#include "mm.h"
|
|
|
+
|
|
|
+/*
|
|
|
+ * empty_zero_page is a special page that is used for
|
|
|
+ * zero-initialized data and COW.
|
|
|
+ */
|
|
|
+struct page *empty_zero_page;
|
|
|
+EXPORT_SYMBOL(empty_zero_page);
|
|
|
+
|
|
|
+/*
|
|
|
+ * The pmd table for the upper-most set of pages.
|
|
|
+ */
|
|
|
+pmd_t *top_pmd;
|
|
|
+
|
|
|
+#define CPOLICY_UNCACHED 0
|
|
|
+#define CPOLICY_BUFFERED 1
|
|
|
+#define CPOLICY_WRITETHROUGH 2
|
|
|
+#define CPOLICY_WRITEBACK 3
|
|
|
+#define CPOLICY_WRITEALLOC 4
|
|
|
+
|
|
|
+static unsigned int cachepolicy __initdata = CPOLICY_WRITEBACK;
|
|
|
+static unsigned int ecc_mask __initdata = 0;
|
|
|
+pgprot_t pgprot_user;
|
|
|
+pgprot_t pgprot_kernel;
|
|
|
+
|
|
|
+EXPORT_SYMBOL(pgprot_user);
|
|
|
+EXPORT_SYMBOL(pgprot_kernel);
|
|
|
+
|
|
|
+struct cachepolicy {
|
|
|
+ const char policy[16];
|
|
|
+ unsigned int cr_mask;
|
|
|
+ pmdval_t pmd;
|
|
|
+ pteval_t pte;
|
|
|
+};
|
|
|
+
|
|
|
+static struct cachepolicy cache_policies[] __initdata = {
|
|
|
+ {
|
|
|
+ .policy = "uncached",
|
|
|
+ .cr_mask = CR_W|CR_C,
|
|
|
+ .pmd = PMD_SECT_UNCACHED,
|
|
|
+ .pte = L_PTE_MT_UNCACHED,
|
|
|
+ }, {
|
|
|
+ .policy = "buffered",
|
|
|
+ .cr_mask = CR_C,
|
|
|
+ .pmd = PMD_SECT_BUFFERED,
|
|
|
+ .pte = L_PTE_MT_BUFFERABLE,
|
|
|
+ }, {
|
|
|
+ .policy = "writethrough",
|
|
|
+ .cr_mask = 0,
|
|
|
+ .pmd = PMD_SECT_WT,
|
|
|
+ .pte = L_PTE_MT_WRITETHROUGH,
|
|
|
+ }, {
|
|
|
+ .policy = "writeback",
|
|
|
+ .cr_mask = 0,
|
|
|
+ .pmd = PMD_SECT_WB,
|
|
|
+ .pte = L_PTE_MT_WRITEBACK,
|
|
|
+ }, {
|
|
|
+ .policy = "writealloc",
|
|
|
+ .cr_mask = 0,
|
|
|
+ .pmd = PMD_SECT_WBWA,
|
|
|
+ .pte = L_PTE_MT_WRITEALLOC,
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * These are useful for identifying cache coherency
|
|
|
+ * problems by allowing the cache or the cache and
|
|
|
+ * writebuffer to be turned off. (Note: the write
|
|
|
+ * buffer should not be on and the cache off).
|
|
|
+ */
|
|
|
+static int __init early_cachepolicy(char *p)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(cache_policies); i++) {
|
|
|
+ int len = strlen(cache_policies[i].policy);
|
|
|
+
|
|
|
+ if (memcmp(p, cache_policies[i].policy, len) == 0) {
|
|
|
+ cachepolicy = i;
|
|
|
+ cr_alignment &= ~cache_policies[i].cr_mask;
|
|
|
+ cr_no_alignment &= ~cache_policies[i].cr_mask;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (i == ARRAY_SIZE(cache_policies))
|
|
|
+ printk(KERN_ERR "ERROR: unknown or unsupported cache policy\n");
|
|
|
+ /*
|
|
|
+ * This restriction is partly to do with the way we boot; it is
|
|
|
+ * unpredictable to have memory mapped using two different sets of
|
|
|
+ * memory attributes (shared, type, and cache attribs). We can not
|
|
|
+ * change these attributes once the initial assembly has setup the
|
|
|
+ * page tables.
|
|
|
+ */
|
|
|
+ if (cpu_architecture() >= CPU_ARCH_ARMv6) {
|
|
|
+ printk(KERN_WARNING "Only cachepolicy=writeback supported on ARMv6 and later\n");
|
|
|
+ cachepolicy = CPOLICY_WRITEBACK;
|
|
|
+ }
|
|
|
+ flush_cache_all();
|
|
|
+ set_cr(cr_alignment);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+early_param("cachepolicy", early_cachepolicy);
|
|
|
+
|
|
|
+static int __init early_nocache(char *__unused)
|
|
|
+{
|
|
|
+ char *p = "buffered";
|
|
|
+ printk(KERN_WARNING "nocache is deprecated; use cachepolicy=%s\n", p);
|
|
|
+ early_cachepolicy(p);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+early_param("nocache", early_nocache);
|
|
|
+
|
|
|
+static int __init early_nowrite(char *__unused)
|
|
|
+{
|
|
|
+ char *p = "uncached";
|
|
|
+ printk(KERN_WARNING "nowb is deprecated; use cachepolicy=%s\n", p);
|
|
|
+ early_cachepolicy(p);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+early_param("nowb", early_nowrite);
|
|
|
+
|
|
|
+#ifndef CONFIG_ARM_LPAE
|
|
|
+static int __init early_ecc(char *p)
|
|
|
+{
|
|
|
+ if (memcmp(p, "on", 2) == 0)
|
|
|
+ ecc_mask = PMD_PROTECTION;
|
|
|
+ else if (memcmp(p, "off", 3) == 0)
|
|
|
+ ecc_mask = 0;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+early_param("ecc", early_ecc);
|
|
|
+#endif
|
|
|
+
|
|
|
+static int __init noalign_setup(char *__unused)
|
|
|
+{
|
|
|
+ cr_alignment &= ~CR_A;
|
|
|
+ cr_no_alignment &= ~CR_A;
|
|
|
+ set_cr(cr_alignment);
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+__setup("noalign", noalign_setup);
|
|
|
+
|
|
|
+#ifndef CONFIG_SMP
|
|
|
+void adjust_cr(unsigned long mask, unsigned long set)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ mask &= ~CR_A;
|
|
|
+
|
|
|
+ set &= mask;
|
|
|
+
|
|
|
+ local_irq_save(flags);
|
|
|
+
|
|
|
+ cr_no_alignment = (cr_no_alignment & ~mask) | set;
|
|
|
+ cr_alignment = (cr_alignment & ~mask) | set;
|
|
|
+
|
|
|
+ set_cr((get_cr() & ~mask) | set);
|
|
|
+
|