arm64: add early_ioremap support
authorMark Salter <msalter@redhat.com>
Mon, 7 Apr 2014 22:39:52 +0000 (15:39 -0700)
committerMark Brown <broonie@linaro.org>
Fri, 23 May 2014 18:27:34 +0000 (19:27 +0100)
Add support for early IO or memory mappings which are needed before the
normal ioremap() is usable.  This also adds fixmap support for permanent
fixed mappings such as that used by the earlyprintk device register
region.

Signed-off-by: Mark Salter <msalter@redhat.com>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Borislav Petkov <borislav.petkov@amd.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
(cherry picked from commit bf4b558eba920a38f91beb5ee62a8ce2628c92f7)
Signed-off-by: Mark Brown <broonie@linaro.org>
Conflicts:
arch/arm64/Kconfig
arch/arm64/mm/ioremap.c

Documentation/arm64/memory.txt
arch/arm64/Kconfig
arch/arm64/include/asm/Kbuild
arch/arm64/include/asm/fixmap.h [new file with mode: 0644]
arch/arm64/include/asm/io.h
arch/arm64/include/asm/memory.h
arch/arm64/kernel/early_printk.c
arch/arm64/kernel/head.S
arch/arm64/kernel/setup.c
arch/arm64/mm/ioremap.c
arch/arm64/mm/mmu.c

index 7835a54b55b0bebb45d43c11e5f0699e1fd0751b..c6941f815f15a2567a838bba036b4ad46e1055e0 100644 (file)
@@ -39,7 +39,7 @@ ffffffbffa000000      ffffffbffaffffff          16MB          PCI I/O space
 
 ffffffbffb000000       ffffffbffbbfffff          12MB          [guard]
 
-ffffffbffbc00000       ffffffbffbdfffff           2MB          earlyprintk device
+ffffffbffbc00000       ffffffbffbdfffff           2MB          fixed mappings
 
 ffffffbffbe00000       ffffffbffbffffff           2MB          [guard]
 
@@ -66,7 +66,7 @@ fffffdfffa000000      fffffdfffaffffff          16MB          PCI I/O space
 
 fffffdfffb000000       fffffdfffbbfffff          12MB          [guard]
 
-fffffdfffbc00000       fffffdfffbdfffff           2MB          earlyprintk device
+fffffdfffbc00000       fffffdfffbdfffff           2MB          fixed mappings
 
 fffffdfffbe00000       fffffdfffbffffff           2MB          [guard]
 
index 8540eef3db8b4be64832cc167ad35c930b03e1be..1eded479dfd785ff975f76363cb7a2a2a7200025 100644 (file)
@@ -13,6 +13,7 @@ config ARM64
        select COMMON_CLK
        select DCACHE_WORD_ACCESS
        select GENERIC_CLOCKEVENTS
+       select GENERIC_EARLY_IOREMAP
        select GENERIC_IOMAP
        select GENERIC_IRQ_PROBE
        select GENERIC_IRQ_SHOW
index 53c4a29464441c82ab8447581b4d7f93c17b7a8e..bc5da00f8d8458f7fd20dd818715ac5a86caac70 100644 (file)
@@ -10,6 +10,7 @@ generic-y += delay.h
 generic-y += div64.h
 generic-y += dma.h
 generic-y += emergency-restart.h
+generic-y += early_ioremap.h
 generic-y += errno.h
 generic-y += ftrace.h
 generic-y += hw_irq.h
diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h
new file mode 100644 (file)
index 0000000..5f7bfe6
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * fixmap.h: compile-time virtual memory allocation
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1998 Ingo Molnar
+ * Copyright (C) 2013 Mark Salter <msalter@redhat.com>
+ *
+ * Adapted from arch/x86_64 version.
+ *
+ */
+
+#ifndef _ASM_ARM64_FIXMAP_H
+#define _ASM_ARM64_FIXMAP_H
+
+#ifndef __ASSEMBLY__
+#include <linux/kernel.h>
+#include <asm/page.h>
+
+/*
+ * Here we define all the compile-time 'special' virtual
+ * addresses. The point is to have a constant address at
+ * compile time, but to set the physical address only
+ * in the boot process.
+ *
+ * These 'compile-time allocated' memory buffers are
+ * page-sized. Use set_fixmap(idx,phys) to associate
+ * physical memory with fixmap indices.
+ *
+ */
+enum fixed_addresses {
+       FIX_EARLYCON_MEM_BASE,
+       __end_of_permanent_fixed_addresses,
+
+       /*
+        * Temporary boot-time mappings, used by early_ioremap(),
+        * before ioremap() is functional.
+        */
+#ifdef CONFIG_ARM64_64K_PAGES
+#define NR_FIX_BTMAPS          4
+#else
+#define NR_FIX_BTMAPS          64
+#endif
+#define FIX_BTMAPS_SLOTS       7
+#define TOTAL_FIX_BTMAPS       (NR_FIX_BTMAPS * FIX_BTMAPS_SLOTS)
+
+       FIX_BTMAP_END = __end_of_permanent_fixed_addresses,
+       FIX_BTMAP_BEGIN = FIX_BTMAP_END + TOTAL_FIX_BTMAPS - 1,
+       __end_of_fixed_addresses
+};
+
+#define FIXADDR_SIZE   (__end_of_permanent_fixed_addresses << PAGE_SHIFT)
+#define FIXADDR_START  (FIXADDR_TOP - FIXADDR_SIZE)
+
+#define FIXMAP_PAGE_IO     __pgprot(PROT_DEVICE_nGnRE)
+
+extern void __early_set_fixmap(enum fixed_addresses idx,
+                              phys_addr_t phys, pgprot_t flags);
+
+#define __set_fixmap __early_set_fixmap
+
+#include <asm-generic/fixmap.h>
+
+#endif /* !__ASSEMBLY__ */
+#endif /* _ASM_ARM64_FIXMAP_H */
index ad80e05825dff98a5b82934d9b0fc55d8ee26d11..b1ee8f0958ee73449f4fd345f657f1c4fb4cce3d 100644 (file)
@@ -26,6 +26,7 @@
 #include <asm/byteorder.h>
 #include <asm/barrier.h>
 #include <asm/pgtable.h>
+#include <asm/early_ioremap.h>
 
 /*
  * Generic IO read/write.  These perform native-endian accesses.
index 9abb57d743b94b68c8216024affd0361ce506404..83138d2fb90d5f4d5aaa2c381569f59d7634d6ea 100644 (file)
@@ -49,7 +49,7 @@
 #define PAGE_OFFSET            (UL(0xffffffffffffffff) << (VA_BITS - 1))
 #define MODULES_END            (PAGE_OFFSET)
 #define MODULES_VADDR          (MODULES_END - SZ_64M)
-#define EARLYCON_IOBASE                (MODULES_VADDR - SZ_4M)
+#define FIXADDR_TOP            (MODULES_VADDR - SZ_2M - PAGE_SIZE)
 #define TASK_SIZE_64           (UL(1) << VA_BITS)
 
 #ifdef CONFIG_COMPAT
index fbb6e18436598142cf0e4ea7429b63965f2ada4b..ffbbdde7aba10480c12b41d552d1fb41da6097df 100644 (file)
@@ -26,6 +26,8 @@
 #include <linux/amba/serial.h>
 #include <linux/serial_reg.h>
 
+#include <asm/fixmap.h>
+
 static void __iomem *early_base;
 static void (*printch)(char ch);
 
@@ -141,8 +143,10 @@ static int __init setup_early_printk(char *buf)
        }
        /* no options parsing yet */
 
-       if (paddr)
-               early_base = early_io_map(paddr, EARLYCON_IOBASE);
+       if (paddr) {
+               set_fixmap_io(FIX_EARLYCON_MEM_BASE, paddr);
+               early_base = (void __iomem *)fix_to_virt(FIX_EARLYCON_MEM_BASE);
+       }
 
        printch = match->printch;
        early_console = &early_console_dev;
index 6fc9fc2b722044fe118d3687d2bec590bb6e30cf..1445f7869cf326d38afc5f131684667f81597782 100644 (file)
@@ -376,7 +376,7 @@ ENDPROC(__calc_phys_offset)
  *   - identity mapping to enable the MMU (low address, TTBR0)
  *   - first few MB of the kernel linear mapping to jump to once the MMU has
  *     been enabled, including the FDT blob (TTBR1)
- *   - UART mapping if CONFIG_EARLY_PRINTK is enabled (TTBR1)
+ *   - pgd entry for fixed mappings (TTBR1)
  */
 __create_page_tables:
        pgtbl   x25, x26, x24                   // idmap_pg_dir and swapper_pg_dir addresses
@@ -433,15 +433,12 @@ __create_page_tables:
        sub     x6, x6, #1                      // inclusive range
        create_block_map x0, x7, x3, x5, x6
 1:
-#ifdef CONFIG_EARLY_PRINTK
        /*
-        * Create the pgd entry for the UART mapping. The full mapping is done
-        * later based earlyprintk kernel parameter.
+        * Create the pgd entry for the fixed mappings.
         */
-       ldr     x5, =EARLYCON_IOBASE            // UART virtual address
+       ldr     x5, =FIXADDR_TOP                // Fixed mapping virtual address
        add     x0, x26, #2 * PAGE_SIZE         // section table address
        create_pgd_entry x26, x0, x5, x6, x7
-#endif
        ret
 ENDPROC(__create_page_tables)
        .ltorg
index 6c87fe0f6857c55a2fbf26325d720a37630fb446..5f2e5f2a17631c8de9586875192da7c778c0c3b9 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/of_fdt.h>
 #include <linux/of_platform.h>
 
+#include <asm/fixmap.h>
 #include <asm/cputype.h>
 #include <asm/elf.h>
 #include <asm/cputable.h>
@@ -269,6 +270,7 @@ void __init setup_arch(char **cmdline_p)
        *cmdline_p = boot_command_line;
 
        init_mem_pgprot();
+       early_ioremap_init();
 
        parse_early_param();
 
index 1725cd6db37a8f5e6a76f84192ef704838efb446..00d315ae1de9715c4428dd2a753132b0288b6209 100644 (file)
 #include <linux/vmalloc.h>
 #include <linux/io.h>
 
+#include <asm/fixmap.h>
+#include <asm/tlbflush.h>
+#include <asm/pgalloc.h>
+
 static void __iomem *__ioremap_caller(phys_addr_t phys_addr, size_t size,
                                      pgprot_t prot, void *caller)
 {
@@ -82,3 +86,95 @@ void __iounmap(volatile void __iomem *io_addr)
        vunmap(addr);
 }
 EXPORT_SYMBOL(__iounmap);
+
+void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size)
+{
+       /* For normal memory we already have a cacheable mapping. */
+       if (pfn_valid(__phys_to_pfn(phys_addr)))
+               return (void __iomem *)__phys_to_virt(phys_addr);
+
+       return __ioremap_caller(phys_addr, size, __pgprot(PROT_NORMAL),
+                               __builtin_return_address(0));
+}
+EXPORT_SYMBOL(ioremap_cache);
+
+#ifndef CONFIG_ARM64_64K_PAGES
+static pte_t bm_pte[PTRS_PER_PTE] __page_aligned_bss;
+#endif
+
+static inline pmd_t * __init early_ioremap_pmd(unsigned long addr)
+{
+       pgd_t *pgd;
+       pud_t *pud;
+
+       pgd = pgd_offset_k(addr);
+       BUG_ON(pgd_none(*pgd) || pgd_bad(*pgd));
+
+       pud = pud_offset(pgd, addr);
+       BUG_ON(pud_none(*pud) || pud_bad(*pud));
+
+       return pmd_offset(pud, addr);
+}
+
+static inline pte_t * __init early_ioremap_pte(unsigned long addr)
+{
+       pmd_t *pmd = early_ioremap_pmd(addr);
+
+       BUG_ON(pmd_none(*pmd) || pmd_bad(*pmd));
+
+       return pte_offset_kernel(pmd, addr);
+}
+
+void __init early_ioremap_init(void)
+{
+       pmd_t *pmd;
+
+       pmd = early_ioremap_pmd(fix_to_virt(FIX_BTMAP_BEGIN));
+#ifndef CONFIG_ARM64_64K_PAGES
+       /* need to populate pmd for 4k pagesize only */
+       pmd_populate_kernel(&init_mm, pmd, bm_pte);
+#endif
+       /*
+        * The boot-ioremap range spans multiple pmds, for which
+        * we are not prepared:
+        */
+       BUILD_BUG_ON((__fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT)
+                    != (__fix_to_virt(FIX_BTMAP_END) >> PMD_SHIFT));
+
+       if (pmd != early_ioremap_pmd(fix_to_virt(FIX_BTMAP_END))) {
+               WARN_ON(1);
+               pr_warn("pmd %p != %p\n",
+                       pmd, early_ioremap_pmd(fix_to_virt(FIX_BTMAP_END)));
+               pr_warn("fix_to_virt(FIX_BTMAP_BEGIN): %08lx\n",
+                       fix_to_virt(FIX_BTMAP_BEGIN));
+               pr_warn("fix_to_virt(FIX_BTMAP_END):   %08lx\n",
+                       fix_to_virt(FIX_BTMAP_END));
+
+               pr_warn("FIX_BTMAP_END:       %d\n", FIX_BTMAP_END);
+               pr_warn("FIX_BTMAP_BEGIN:     %d\n",
+                       FIX_BTMAP_BEGIN);
+       }
+
+       early_ioremap_setup();
+}
+
+void __init __early_set_fixmap(enum fixed_addresses idx,
+                              phys_addr_t phys, pgprot_t flags)
+{
+       unsigned long addr = __fix_to_virt(idx);
+       pte_t *pte;
+
+       if (idx >= __end_of_fixed_addresses) {
+               BUG();
+               return;
+       }
+
+       pte = early_ioremap_pte(addr);
+
+       if (pgprot_val(flags))
+               set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, flags));
+       else {
+               pte_clear(&init_mm, addr, pte);
+               flush_tlb_kernel_range(addr, addr+PAGE_SIZE);
+       }
+}
index ba259a0e385ebad9bfcadb050f8ba4c8c6420f43..6b7e89569a3a9ff8518e7c6ee856603f1e9fb93b 100644 (file)
@@ -260,47 +260,6 @@ static void __init create_mapping(phys_addr_t phys, unsigned long virt,
        } while (pgd++, addr = next, addr != end);
 }
 
-#ifdef CONFIG_EARLY_PRINTK
-/*
- * Create an early I/O mapping using the pgd/pmd entries already populated
- * in head.S as this function is called too early to allocated any memory. The
- * mapping size is 2MB with 4KB pages or 64KB or 64KB pages.
- */
-void __iomem * __init early_io_map(phys_addr_t phys, unsigned long virt)
-{
-       unsigned long size, mask;
-       bool page64k = IS_ENABLED(CONFIG_ARM64_64K_PAGES);
-       pgd_t *pgd;
-       pud_t *pud;
-       pmd_t *pmd;
-       pte_t *pte;
-
-       /*
-        * No early pte entries with !ARM64_64K_PAGES configuration, so using
-        * sections (pmd).
-        */
-       size = page64k ? PAGE_SIZE : SECTION_SIZE;
-       mask = ~(size - 1);
-
-       pgd = pgd_offset_k(virt);
-       pud = pud_offset(pgd, virt);
-       if (pud_none(*pud))
-               return NULL;
-       pmd = pmd_offset(pud, virt);
-
-       if (page64k) {
-               if (pmd_none(*pmd))
-                       return NULL;
-               pte = pte_offset_kernel(pmd, virt);
-               set_pte(pte, __pte((phys & mask) | PROT_DEVICE_nGnRE));
-       } else {
-               set_pmd(pmd, __pmd((phys & mask) | PROT_SECT_DEVICE_nGnRE));
-       }
-
-       return (void __iomem *)((virt & mask) + (phys & ~mask));
-}
-#endif
-
 static void __init map_mem(void)
 {
        struct memblock_region *reg;