Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / arch / arm / mm / idmap.c
diff --git a/kernel/arch/arm/mm/idmap.c b/kernel/arch/arm/mm/idmap.c
new file mode 100644 (file)
index 0000000..e7a81ce
--- /dev/null
@@ -0,0 +1,136 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include <asm/cputype.h>
+#include <asm/idmap.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/sections.h>
+#include <asm/system_info.h>
+
+/*
+ * Note: accesses outside of the kernel image and the identity map area
+ * are not supported on any CPU using the idmap tables as its current
+ * page tables.
+ */
+pgd_t *idmap_pgd;
+phys_addr_t (*arch_virt_to_idmap) (unsigned long x);
+
+#ifdef CONFIG_ARM_LPAE
+static void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end,
+       unsigned long prot)
+{
+       pmd_t *pmd;
+       unsigned long next;
+
+       if (pud_none_or_clear_bad(pud) || (pud_val(*pud) & L_PGD_SWAPPER)) {
+               pmd = pmd_alloc_one(&init_mm, addr);
+               if (!pmd) {
+                       pr_warn("Failed to allocate identity pmd.\n");
+                       return;
+               }
+               /*
+                * Copy the original PMD to ensure that the PMD entries for
+                * the kernel image are preserved.
+                */
+               if (!pud_none(*pud))
+                       memcpy(pmd, pmd_offset(pud, 0),
+                              PTRS_PER_PMD * sizeof(pmd_t));
+               pud_populate(&init_mm, pud, pmd);
+               pmd += pmd_index(addr);
+       } else
+               pmd = pmd_offset(pud, addr);
+
+       do {
+               next = pmd_addr_end(addr, end);
+               *pmd = __pmd((addr & PMD_MASK) | prot);
+               flush_pmd_entry(pmd);
+       } while (pmd++, addr = next, addr != end);
+}
+#else  /* !CONFIG_ARM_LPAE */
+static void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end,
+       unsigned long prot)
+{
+       pmd_t *pmd = pmd_offset(pud, addr);
+
+       addr = (addr & PMD_MASK) | prot;
+       pmd[0] = __pmd(addr);
+       addr += SECTION_SIZE;
+       pmd[1] = __pmd(addr);
+       flush_pmd_entry(pmd);
+}
+#endif /* CONFIG_ARM_LPAE */
+
+static void idmap_add_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
+       unsigned long prot)
+{
+       pud_t *pud = pud_offset(pgd, addr);
+       unsigned long next;
+
+       do {
+               next = pud_addr_end(addr, end);
+               idmap_add_pmd(pud, addr, next, prot);
+       } while (pud++, addr = next, addr != end);
+}
+
+static void identity_mapping_add(pgd_t *pgd, const char *text_start,
+                                const char *text_end, unsigned long prot)
+{
+       unsigned long addr, end;
+       unsigned long next;
+
+       addr = virt_to_idmap(text_start);
+       end = virt_to_idmap(text_end);
+       pr_info("Setting up static identity map for 0x%lx - 0x%lx\n", addr, end);
+
+       prot |= PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AF;
+
+       if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale())
+               prot |= PMD_BIT4;
+
+       pgd += pgd_index(addr);
+       do {
+               next = pgd_addr_end(addr, end);
+               idmap_add_pud(pgd, addr, next, prot);
+       } while (pgd++, addr = next, addr != end);
+}
+
+extern char  __idmap_text_start[], __idmap_text_end[];
+
+static int __init init_static_idmap(void)
+{
+       idmap_pgd = pgd_alloc(&init_mm);
+       if (!idmap_pgd)
+               return -ENOMEM;
+
+       identity_mapping_add(idmap_pgd, __idmap_text_start,
+                            __idmap_text_end, 0);
+
+       /* Flush L1 for the hardware to see this page table content */
+       flush_cache_louis();
+
+       return 0;
+}
+early_initcall(init_static_idmap);
+
+/*
+ * In order to soft-boot, we need to switch to a 1:1 mapping for the
+ * cpu_reset functions. This will then ensure that we have predictable
+ * results when turning off the mmu.
+ */
+void setup_mm_for_reboot(void)
+{
+       /* Switch to the identity mapping. */
+       cpu_switch_mm(idmap_pgd, &init_mm);
+       local_flush_bp_all();
+
+#ifdef CONFIG_CPU_HAS_ASID
+       /*
+        * We don't have a clean ASID for the identity mapping, which
+        * may clash with virtual addresses of the previous page tables
+        * and therefore potentially in the TLB.
+        */
+       local_flush_tlb_all();
+#endif
+}