1 /* 2 * arch/xtensa/mm/init.c 3 * 4 * Derived from MIPS, PPC. 5 * 6 * This file is subject to the terms and conditions of the GNU General Public 7 * License. See the file "COPYING" in the main directory of this archive 8 * for more details. 9 * 10 * Copyright (C) 2001 - 2005 Tensilica Inc. 11 * Copyright (C) 2014 Cadence Design Systems Inc. 12 * 13 * Chris Zankel <chris@zankel.net> 14 * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com> 15 * Marc Gauthier 16 * Kevin Chea 17 */ 18 19 #include <linux/kernel.h> 20 #include <linux/errno.h> 21 #include <linux/bootmem.h> 22 #include <linux/gfp.h> 23 #include <linux/swap.h> 24 #include <linux/mman.h> 25 #include <linux/nodemask.h> 26 #include <linux/mm.h> 27 28 #include <asm/bootparam.h> 29 #include <asm/page.h> 30 #include <asm/sections.h> 31 #include <asm/sysmem.h> 32 33 struct sysmem_info sysmem __initdata; 34 35 /* 36 * Find bank with maximal .start such that bank.start <= start 37 */ 38 static inline struct meminfo * __init find_bank(unsigned long start) 39 { 40 unsigned i; 41 struct meminfo *it = NULL; 42 43 for (i = 0; i < sysmem.nr_banks; ++i) 44 if (sysmem.bank[i].start <= start) 45 it = sysmem.bank + i; 46 else 47 break; 48 return it; 49 } 50 51 /* 52 * Move all memory banks starting at 'from' to a new place at 'to', 53 * adjust nr_banks accordingly. 54 * Both 'from' and 'to' must be inside the sysmem.bank. 55 * 56 * Returns: 0 (success), -ENOMEM (not enough space in the sysmem.bank). 57 */ 58 static int __init move_banks(struct meminfo *to, struct meminfo *from) 59 { 60 unsigned n = sysmem.nr_banks - (from - sysmem.bank); 61 62 if (to > from && to - from + sysmem.nr_banks > SYSMEM_BANKS_MAX) 63 return -ENOMEM; 64 if (to != from) 65 memmove(to, from, n * sizeof(struct meminfo)); 66 sysmem.nr_banks += to - from; 67 return 0; 68 } 69 70 /* 71 * Add new bank to sysmem. Resulting sysmem is the union of bytes of the 72 * original sysmem and the new bank. 73 * 74 * Returns: 0 (success), < 0 (error) 75 */ 76 int __init add_sysmem_bank(unsigned long start, unsigned long end) 77 { 78 unsigned i; 79 struct meminfo *it = NULL; 80 unsigned long sz; 81 unsigned long bank_sz = 0; 82 83 if (start == end || 84 (start < end) != (PAGE_ALIGN(start) < (end & PAGE_MASK))) { 85 pr_warn("Ignoring small memory bank 0x%08lx size: %ld bytes\n", 86 start, end - start); 87 return -EINVAL; 88 } 89 90 start = PAGE_ALIGN(start); 91 end &= PAGE_MASK; 92 sz = end - start; 93 94 it = find_bank(start); 95 96 if (it) 97 bank_sz = it->end - it->start; 98 99 if (it && bank_sz >= start - it->start) { 100 if (end - it->start > bank_sz) 101 it->end = end; 102 else 103 return 0; 104 } else { 105 if (!it) 106 it = sysmem.bank; 107 else 108 ++it; 109 110 if (it - sysmem.bank < sysmem.nr_banks && 111 it->start - start <= sz) { 112 it->start = start; 113 if (it->end - it->start < sz) 114 it->end = end; 115 else 116 return 0; 117 } else { 118 if (move_banks(it + 1, it) < 0) { 119 pr_warn("Ignoring memory bank 0x%08lx size %ld bytes\n", 120 start, end - start); 121 return -EINVAL; 122 } 123 it->start = start; 124 it->end = end; 125 return 0; 126 } 127 } 128 sz = it->end - it->start; 129 for (i = it + 1 - sysmem.bank; i < sysmem.nr_banks; ++i) 130 if (sysmem.bank[i].start - it->start <= sz) { 131 if (sz < sysmem.bank[i].end - it->start) 132 it->end = sysmem.bank[i].end; 133 } else { 134 break; 135 } 136 137 move_banks(it + 1, sysmem.bank + i); 138 return 0; 139 } 140 141 /* 142 * mem_reserve(start, end, must_exist) 143 * 144 * Reserve some memory from the memory pool. 145 * 146 * Parameters: 147 * start Start of region, 148 * end End of region, 149 * must_exist Must exist in memory pool. 150 * 151 * Returns: 152 * 0 (memory area couldn't be mapped) 153 * -1 (success) 154 */ 155 156 int __init mem_reserve(unsigned long start, unsigned long end, int must_exist) 157 { 158 int i; 159 160 if (start == end) 161 return 0; 162 163 start = start & PAGE_MASK; 164 end = PAGE_ALIGN(end); 165 166 for (i = 0; i < sysmem.nr_banks; i++) 167 if (start < sysmem.bank[i].end 168 && end >= sysmem.bank[i].start) 169 break; 170 171 if (i == sysmem.nr_banks) { 172 if (must_exist) 173 printk (KERN_WARNING "mem_reserve: [0x%0lx, 0x%0lx) " 174 "not in any region!\n", start, end); 175 return 0; 176 } 177 178 if (start > sysmem.bank[i].start) { 179 if (end < sysmem.bank[i].end) { 180 /* split entry */ 181 if (sysmem.nr_banks >= SYSMEM_BANKS_MAX) 182 panic("meminfo overflow\n"); 183 sysmem.bank[sysmem.nr_banks].start = end; 184 sysmem.bank[sysmem.nr_banks].end = sysmem.bank[i].end; 185 sysmem.nr_banks++; 186 } 187 sysmem.bank[i].end = start; 188 189 } else if (end < sysmem.bank[i].end) { 190 sysmem.bank[i].start = end; 191 192 } else { 193 /* remove entry */ 194 sysmem.nr_banks--; 195 sysmem.bank[i].start = sysmem.bank[sysmem.nr_banks].start; 196 sysmem.bank[i].end = sysmem.bank[sysmem.nr_banks].end; 197 } 198 return -1; 199 } 200 201 202 /* 203 * Initialize the bootmem system and give it all low memory we have available. 204 */ 205 206 void __init bootmem_init(void) 207 { 208 unsigned long pfn; 209 unsigned long bootmap_start, bootmap_size; 210 int i; 211 212 max_low_pfn = max_pfn = 0; 213 min_low_pfn = ~0; 214 215 for (i=0; i < sysmem.nr_banks; i++) { 216 pfn = PAGE_ALIGN(sysmem.bank[i].start) >> PAGE_SHIFT; 217 if (pfn < min_low_pfn) 218 min_low_pfn = pfn; 219 pfn = PAGE_ALIGN(sysmem.bank[i].end - 1) >> PAGE_SHIFT; 220 if (pfn > max_pfn) 221 max_pfn = pfn; 222 } 223 224 if (min_low_pfn > max_pfn) 225 panic("No memory found!\n"); 226 227 max_low_pfn = max_pfn < MAX_MEM_PFN >> PAGE_SHIFT ? 228 max_pfn : MAX_MEM_PFN >> PAGE_SHIFT; 229 230 /* Find an area to use for the bootmem bitmap. */ 231 232 bootmap_size = bootmem_bootmap_pages(max_low_pfn - min_low_pfn); 233 bootmap_size <<= PAGE_SHIFT; 234 bootmap_start = ~0; 235 236 for (i=0; i<sysmem.nr_banks; i++) 237 if (sysmem.bank[i].end - sysmem.bank[i].start >= bootmap_size) { 238 bootmap_start = sysmem.bank[i].start; 239 break; 240 } 241 242 if (bootmap_start == ~0UL) 243 panic("Cannot find %ld bytes for bootmap\n", bootmap_size); 244 245 /* Reserve the bootmem bitmap area */ 246 247 mem_reserve(bootmap_start, bootmap_start + bootmap_size, 1); 248 bootmap_size = init_bootmem_node(NODE_DATA(0), 249 bootmap_start >> PAGE_SHIFT, 250 min_low_pfn, 251 max_low_pfn); 252 253 /* Add all remaining memory pieces into the bootmem map */ 254 255 for (i = 0; i < sysmem.nr_banks; i++) { 256 if (sysmem.bank[i].start >> PAGE_SHIFT < max_low_pfn) { 257 unsigned long end = min(max_low_pfn << PAGE_SHIFT, 258 sysmem.bank[i].end); 259 free_bootmem(sysmem.bank[i].start, 260 end - sysmem.bank[i].start); 261 } 262 } 263 264 } 265 266 267 void __init zones_init(void) 268 { 269 unsigned long zones_size[MAX_NR_ZONES]; 270 int i; 271 272 /* All pages are DMA-able, so we put them all in the DMA zone. */ 273 274 zones_size[ZONE_DMA] = max_low_pfn - ARCH_PFN_OFFSET; 275 for (i = 1; i < MAX_NR_ZONES; i++) 276 zones_size[i] = 0; 277 278 #ifdef CONFIG_HIGHMEM 279 zones_size[ZONE_HIGHMEM] = max_pfn - max_low_pfn; 280 #endif 281 282 free_area_init_node(0, zones_size, ARCH_PFN_OFFSET, NULL); 283 } 284 285 /* 286 * Initialize memory pages. 287 */ 288 289 void __init mem_init(void) 290 { 291 max_mapnr = max_low_pfn - ARCH_PFN_OFFSET; 292 high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT); 293 294 #ifdef CONFIG_HIGHMEM 295 #error HIGHGMEM not implemented in init.c 296 #endif 297 298 free_all_bootmem(); 299 300 mem_init_print_info(NULL); 301 } 302 303 #ifdef CONFIG_BLK_DEV_INITRD 304 extern int initrd_is_mapped; 305 306 void free_initrd_mem(unsigned long start, unsigned long end) 307 { 308 if (initrd_is_mapped) 309 free_reserved_area((void *)start, (void *)end, -1, "initrd"); 310 } 311 #endif 312 313 void free_initmem(void) 314 { 315 free_initmem_default(-1); 316 } 317