arm64: mm: move vmemmap region right below the linear region
[linux-2.6-block.git] / arch / arm64 / mm / init.c
index 7802f216a67a588ab778b7d2b7649a2979e68b87..d55d720dba79ecef05f7c03685382315182d1a0e 100644 (file)
 #include <linux/efi.h>
 #include <linux/swiotlb.h>
 
+#include <asm/boot.h>
 #include <asm/fixmap.h>
+#include <asm/kasan.h>
+#include <asm/kernel-pgtable.h>
 #include <asm/memory.h>
 #include <asm/sections.h>
 #include <asm/setup.h>
 
 #include "mm.h"
 
-phys_addr_t memstart_addr __read_mostly = 0;
+/*
+ * We need to be able to catch inadvertent references to memstart_addr
+ * that occur (potentially in generic code) before arm64_memblock_init()
+ * executes, which assigns it its actual value. So use a default value
+ * that cannot be mistaken for a real physical address.
+ */
+s64 memstart_addr __read_mostly = -1;
 phys_addr_t arm64_dma_phys_limit __read_mostly;
 
 #ifdef CONFIG_BLK_DEV_INITRD
@@ -58,8 +67,8 @@ static int __init early_initrd(char *p)
        if (*endp == ',') {
                size = memparse(endp + 1, NULL);
 
-               initrd_start = (unsigned long)__va(start);
-               initrd_end = (unsigned long)__va(start + size);
+               initrd_start = start;
+               initrd_end = start + size;
        }
        return 0;
 }
@@ -159,7 +168,90 @@ early_param("mem", early_mem);
 
 void __init arm64_memblock_init(void)
 {
-       memblock_enforce_memory_limit(memory_limit);
+       const s64 linear_region_size = -(s64)PAGE_OFFSET;
+
+       /*
+        * Ensure that the linear region takes up exactly half of the kernel
+        * virtual address space. This way, we can distinguish a linear address
+        * from a kernel/module/vmalloc address by testing a single bit.
+        */
+       BUILD_BUG_ON(linear_region_size != BIT(VA_BITS - 1));
+
+       /*
+        * Select a suitable value for the base of physical memory.
+        */
+       memstart_addr = round_down(memblock_start_of_DRAM(),
+                                  ARM64_MEMSTART_ALIGN);
+
+       /*
+        * Remove the memory that we will not be able to cover with the
+        * linear mapping. Take care not to clip the kernel which may be
+        * high in memory.
+        */
+       memblock_remove(max_t(u64, memstart_addr + linear_region_size, __pa(_end)),
+                       ULLONG_MAX);
+       if (memstart_addr + linear_region_size < memblock_end_of_DRAM()) {
+               /* ensure that memstart_addr remains sufficiently aligned */
+               memstart_addr = round_up(memblock_end_of_DRAM() - linear_region_size,
+                                        ARM64_MEMSTART_ALIGN);
+               memblock_remove(0, memstart_addr);
+       }
+
+       /*
+        * Apply the memory limit if it was set. Since the kernel may be loaded
+        * high up in memory, add back the kernel region that must be accessible
+        * via the linear mapping.
+        */
+       if (memory_limit != (phys_addr_t)ULLONG_MAX) {
+               memblock_enforce_memory_limit(memory_limit);
+               memblock_add(__pa(_text), (u64)(_end - _text));
+       }
+
+       if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && initrd_start) {
+               /*
+                * Add back the memory we just removed if it results in the
+                * initrd to become inaccessible via the linear mapping.
+                * Otherwise, this is a no-op
+                */
+               u64 base = initrd_start & PAGE_MASK;
+               u64 size = PAGE_ALIGN(initrd_end) - base;
+
+               /*
+                * We can only add back the initrd memory if we don't end up
+                * with more memory than we can address via the linear mapping.
+                * It is up to the bootloader to position the kernel and the
+                * initrd reasonably close to each other (i.e., within 32 GB of
+                * each other) so that all granule/#levels combinations can
+                * always access both.
+                */
+               if (WARN(base < memblock_start_of_DRAM() ||
+                        base + size > memblock_start_of_DRAM() +
+                                      linear_region_size,
+                       "initrd not fully accessible via the linear mapping -- please check your bootloader ...\n")) {
+                       initrd_start = 0;
+               } else {
+                       memblock_remove(base, size); /* clear MEMBLOCK_ flags */
+                       memblock_add(base, size);
+                       memblock_reserve(base, size);
+               }
+       }
+
+       if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
+               extern u16 memstart_offset_seed;
+               u64 range = linear_region_size -
+                           (memblock_end_of_DRAM() - memblock_start_of_DRAM());
+
+               /*
+                * If the size of the linear region exceeds, by a sufficient
+                * margin, the size of the region that the available physical
+                * memory spans, randomize the linear region as well.
+                */
+               if (memstart_offset_seed > 0 && range >= ARM64_MEMSTART_ALIGN) {
+                       range = range / ARM64_MEMSTART_ALIGN + 1;
+                       memstart_addr -= ARM64_MEMSTART_ALIGN *
+                                        ((range * memstart_offset_seed) >> 16);
+               }
+       }
 
        /*
         * Register the kernel text, kernel data, initrd, and initial
@@ -167,8 +259,13 @@ void __init arm64_memblock_init(void)
         */
        memblock_reserve(__pa(_text), _end - _text);
 #ifdef CONFIG_BLK_DEV_INITRD
-       if (initrd_start)
-               memblock_reserve(__virt_to_phys(initrd_start), initrd_end - initrd_start);
+       if (initrd_start) {
+               memblock_reserve(initrd_start, initrd_end - initrd_start);
+
+               /* the generic initrd code expects virtual addresses */
+               initrd_start = __phys_to_virt(initrd_start);
+               initrd_end = __phys_to_virt(initrd_end);
+       }
 #endif
 
        early_init_fdt_scan_reserved_mem();
@@ -298,39 +395,38 @@ void __init mem_init(void)
 #define MLG(b, t) b, t, ((t) - (b)) >> 30
 #define MLK_ROUNDUP(b, t) b, t, DIV_ROUND_UP(((t) - (b)), SZ_1K)
 
-       pr_notice("Virtual kernel memory layout:\n"
+       pr_notice("Virtual kernel memory layout:\n");
 #ifdef CONFIG_KASAN
-                 "    kasan   : 0x%16lx - 0x%16lx   (%6ld GB)\n"
+       pr_cont("    kasan   : 0x%16lx - 0x%16lx   (%6ld GB)\n",
+               MLG(KASAN_SHADOW_START, KASAN_SHADOW_END));
 #endif
-                 "    vmalloc : 0x%16lx - 0x%16lx   (%6ld GB)\n"
+       pr_cont("    modules : 0x%16lx - 0x%16lx   (%6ld MB)\n",
+               MLM(MODULES_VADDR, MODULES_END));
+       pr_cont("    vmalloc : 0x%16lx - 0x%16lx   (%6ld GB)\n",
+               MLG(VMALLOC_START, VMALLOC_END));
+       pr_cont("      .text : 0x%p" " - 0x%p" "   (%6ld KB)\n"
+               "    .rodata : 0x%p" " - 0x%p" "   (%6ld KB)\n"
+               "      .init : 0x%p" " - 0x%p" "   (%6ld KB)\n"
+               "      .data : 0x%p" " - 0x%p" "   (%6ld KB)\n",
+               MLK_ROUNDUP(_text, __start_rodata),
+               MLK_ROUNDUP(__start_rodata, _etext),
+               MLK_ROUNDUP(__init_begin, __init_end),
+               MLK_ROUNDUP(_sdata, _edata));
+       pr_cont("    fixed   : 0x%16lx - 0x%16lx   (%6ld KB)\n",
+               MLK(FIXADDR_START, FIXADDR_TOP));
+       pr_cont("    PCI I/O : 0x%16lx - 0x%16lx   (%6ld MB)\n",
+               MLM(PCI_IO_START, PCI_IO_END));
 #ifdef CONFIG_SPARSEMEM_VMEMMAP
-                 "    vmemmap : 0x%16lx - 0x%16lx   (%6ld GB maximum)\n"
-                 "              0x%16lx - 0x%16lx   (%6ld MB actual)\n"
+       pr_cont("    vmemmap : 0x%16lx - 0x%16lx   (%6ld GB maximum)\n"
+               "              0x%16lx - 0x%16lx   (%6ld MB actual)\n",
+               MLG(VMEMMAP_START,
+                   VMEMMAP_START + VMEMMAP_SIZE),
+               MLM((unsigned long)phys_to_page(memblock_start_of_DRAM()),
+                   (unsigned long)virt_to_page(high_memory)));
 #endif
-                 "    fixed   : 0x%16lx - 0x%16lx   (%6ld KB)\n"
-                 "    PCI I/O : 0x%16lx - 0x%16lx   (%6ld MB)\n"
-                 "    modules : 0x%16lx - 0x%16lx   (%6ld MB)\n"
-                 "    memory  : 0x%16lx - 0x%16lx   (%6ld MB)\n"
-                 "      .init : 0x%p" " - 0x%p" "   (%6ld KB)\n"
-                 "      .text : 0x%p" " - 0x%p" "   (%6ld KB)\n"
-                 "      .data : 0x%p" " - 0x%p" "   (%6ld KB)\n",
-#ifdef CONFIG_KASAN
-                 MLG(KASAN_SHADOW_START, KASAN_SHADOW_END),
-#endif
-                 MLG(VMALLOC_START, VMALLOC_END),
-#ifdef CONFIG_SPARSEMEM_VMEMMAP
-                 MLG(VMEMMAP_START,
-                     VMEMMAP_START + VMEMMAP_SIZE),
-                 MLM((unsigned long)virt_to_page(PAGE_OFFSET),
-                     (unsigned long)virt_to_page(high_memory)),
-#endif
-                 MLK(FIXADDR_START, FIXADDR_TOP),
-                 MLM(PCI_IO_START, PCI_IO_END),
-                 MLM(MODULES_VADDR, MODULES_END),
-                 MLM(PAGE_OFFSET, (unsigned long)high_memory),
-                 MLK_ROUNDUP(__init_begin, __init_end),
-                 MLK_ROUNDUP(_text, _etext),
-                 MLK_ROUNDUP(_sdata, _edata));
+       pr_cont("    memory  : 0x%16lx - 0x%16lx   (%6ld MB)\n",
+               MLM(__phys_to_virt(memblock_start_of_DRAM()),
+                   (unsigned long)high_memory));
 
 #undef MLK
 #undef MLM
@@ -343,8 +439,12 @@ void __init mem_init(void)
 #ifdef CONFIG_COMPAT
        BUILD_BUG_ON(TASK_SIZE_32                       > TASK_SIZE_64);
 #endif
-       BUILD_BUG_ON(TASK_SIZE_64                       > MODULES_VADDR);
-       BUG_ON(TASK_SIZE_64                             > MODULES_VADDR);
+
+       /*
+        * Make sure we chose the upper bound of sizeof(struct page)
+        * correctly.
+        */
+       BUILD_BUG_ON(sizeof(struct page) > (1 << STRUCT_PAGE_MAX_SHIFT));
 
        if (PAGE_SIZE >= 16384 && get_num_physpages() <= 128) {
                extern int sysctl_overcommit_memory;
@@ -358,8 +458,9 @@ void __init mem_init(void)
 
 void free_initmem(void)
 {
+       free_reserved_area(__va(__pa(__init_begin)), __va(__pa(__init_end)),
+                          0, "unused kernel");
        fixup_init();
-       free_initmem_default(0);
 }
 
 #ifdef CONFIG_BLK_DEV_INITRD
@@ -380,3 +481,28 @@ static int __init keepinitrd_setup(char *__unused)
 
 __setup("keepinitrd", keepinitrd_setup);
 #endif
+
+/*
+ * Dump out memory limit information on panic.
+ */
+static int dump_mem_limit(struct notifier_block *self, unsigned long v, void *p)
+{
+       if (memory_limit != (phys_addr_t)ULLONG_MAX) {
+               pr_emerg("Memory Limit: %llu MB\n", memory_limit >> 20);
+       } else {
+               pr_emerg("Memory Limit: none\n");
+       }
+       return 0;
+}
+
+static struct notifier_block mem_limit_notifier = {
+       .notifier_call = dump_mem_limit,
+};
+
+static int __init register_mem_limit_dumper(void)
+{
+       atomic_notifier_chain_register(&panic_notifier_list,
+                                      &mem_limit_notifier);
+       return 0;
+}
+__initcall(register_mem_limit_dumper);