ARM: memblock: setup lowmem mappings using memblock
authorRussell King <rmk+kernel@arm.linux.org.uk>
Wed, 27 Oct 2010 18:57:38 +0000 (19:57 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Thu, 28 Oct 2010 12:54:45 +0000 (13:54 +0100)
Use memblock information to setup lowmem mappings rather than the
membank array.

This allows platforms to manipulate the memblock information during
initialization to reserve (and remove) memory from the kernel's view
of memory - and thus allowing platforms to setup their own private
mappings for this memory without causing problems with multiple
aliasing mappings:

size = min(size, SZ_2M);
base = memblock_alloc(size, min(align, SZ_2M));
memblock_free(base, size);
memblock_remove(base, size);

This is needed because multiple mappings of regions with differing
attributes (sharability, type, cache) are not permitted with ARMv6
and above.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

arch/arm/mm/mmu.c

index 5222d38..72ad3e1 100644 (file)
@@ -744,11 +744,14 @@ static int __init early_vmalloc(char *arg)
 }
 early_param("vmalloc", early_vmalloc);
 
+static phys_addr_t lowmem_limit __initdata = 0;
+
 static void __init sanity_check_meminfo(void)
 {
        int i, j, highmem = 0;
 
-       memblock_set_current_limit(__pa(vmalloc_min - 1) + 1);
+       lowmem_limit = __pa(vmalloc_min - 1) + 1;
+       memblock_set_current_limit(lowmem_limit);
 
        for (i = 0, j = 0; i < meminfo.nr_banks; i++) {
                struct membank *bank = &meminfo.bank[j];
@@ -849,6 +852,7 @@ static void __init sanity_check_meminfo(void)
 static inline void prepare_page_table(void)
 {
        unsigned long addr;
+       phys_addr_t end;
 
        /*
         * Clear out all the mappings below the kernel image.
@@ -864,10 +868,17 @@ static inline void prepare_page_table(void)
                pmd_clear(pmd_off_k(addr));
 
        /*
+        * Find the end of the first block of lowmem.
+        */
+       end = memblock.memory.regions[0].base + memblock.memory.regions[0].size;
+       if (end >= lowmem_limit)
+               end = lowmem_limit;
+
+       /*
         * Clear out all the kernel space mappings, except for the first
         * memory bank, up to the end of the vmalloc region.
         */
-       for (addr = __phys_to_virt(bank_phys_end(&meminfo.bank[0]));
+       for (addr = __phys_to_virt(end);
             addr < VMALLOC_END; addr += PGDIR_SIZE)
                pmd_clear(pmd_off_k(addr));
 }
@@ -984,29 +995,27 @@ static void __init kmap_init(void)
 #endif
 }
 
-static inline void map_memory_bank(struct membank *bank)
-{
-       struct map_desc map;
-
-       map.pfn = bank_pfn_start(bank);
-       map.virtual = __phys_to_virt(bank_phys_start(bank));
-       map.length = bank_phys_size(bank);
-       map.type = MT_MEMORY;
-
-       create_mapping(&map);
-}
-
 static void __init map_lowmem(void)
 {
-       struct meminfo *mi = &meminfo;
-       int i;
+       struct memblock_region *reg;
 
        /* Map all the lowmem memory banks. */
-       for (i = 0; i < mi->nr_banks; i++) {
-               struct membank *bank = &mi->bank[i];
+       for_each_memblock(memory, reg) {
+               phys_addr_t start = reg->base;
+               phys_addr_t end = start + reg->size;
+               struct map_desc map;
+
+               if (end > lowmem_limit)
+                       end = lowmem_limit;
+               if (start >= end)
+                       break;
+
+               map.pfn = __phys_to_pfn(start);
+               map.virtual = __phys_to_virt(start);
+               map.length = end - start;
+               map.type = MT_MEMORY;
 
-               if (!bank->highmem)
-                       map_memory_bank(bank);
+               create_mapping(&map);
        }
 }