13f65ce4dSChris Zankel /* 23f65ce4dSChris Zankel * arch/xtensa/mm/init.c 33f65ce4dSChris Zankel * 43f65ce4dSChris Zankel * Derived from MIPS, PPC. 53f65ce4dSChris Zankel * 63f65ce4dSChris Zankel * This file is subject to the terms and conditions of the GNU General Public 73f65ce4dSChris Zankel * License. See the file "COPYING" in the main directory of this archive 83f65ce4dSChris Zankel * for more details. 93f65ce4dSChris Zankel * 103f65ce4dSChris Zankel * Copyright (C) 2001 - 2005 Tensilica Inc. 119d4b52dfSMax Filippov * Copyright (C) 2014 Cadence Design Systems Inc. 123f65ce4dSChris Zankel * 133f65ce4dSChris Zankel * Chris Zankel <chris@zankel.net> 143f65ce4dSChris Zankel * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com> 153f65ce4dSChris Zankel * Marc Gauthier 163f65ce4dSChris Zankel * Kevin Chea 173f65ce4dSChris Zankel */ 183f65ce4dSChris Zankel 193f65ce4dSChris Zankel #include <linux/kernel.h> 203f65ce4dSChris Zankel #include <linux/errno.h> 213f65ce4dSChris Zankel #include <linux/bootmem.h> 225a0e3ad6STejun Heo #include <linux/gfp.h> 233f65ce4dSChris Zankel #include <linux/swap.h> 246656920bSChris Zankel #include <linux/mman.h> 256656920bSChris Zankel #include <linux/nodemask.h> 266656920bSChris Zankel #include <linux/mm.h> 273f65ce4dSChris Zankel 283f65ce4dSChris Zankel #include <asm/bootparam.h> 293f65ce4dSChris Zankel #include <asm/page.h> 30f022d0faSGeert Uytterhoeven #include <asm/sections.h> 319ba067f9SMax Filippov #include <asm/sysmem.h> 329ba067f9SMax Filippov 339ba067f9SMax Filippov struct sysmem_info sysmem __initdata; 349ba067f9SMax Filippov 359d4b52dfSMax Filippov /* 369d4b52dfSMax Filippov * Find bank with maximal .start such that bank.start <= start 379d4b52dfSMax Filippov */ 389d4b52dfSMax Filippov static inline struct meminfo * __init find_bank(unsigned long start) 399d4b52dfSMax Filippov { 409d4b52dfSMax Filippov unsigned i; 419d4b52dfSMax Filippov struct meminfo *it = NULL; 429d4b52dfSMax Filippov 439d4b52dfSMax Filippov for (i = 0; i < sysmem.nr_banks; ++i) 449d4b52dfSMax Filippov if (sysmem.bank[i].start <= start) 459d4b52dfSMax Filippov it = sysmem.bank + i; 469d4b52dfSMax Filippov else 479d4b52dfSMax Filippov break; 489d4b52dfSMax Filippov return it; 499d4b52dfSMax Filippov } 509d4b52dfSMax Filippov 519d4b52dfSMax Filippov /* 529d4b52dfSMax Filippov * Move all memory banks starting at 'from' to a new place at 'to', 539d4b52dfSMax Filippov * adjust nr_banks accordingly. 549d4b52dfSMax Filippov * Both 'from' and 'to' must be inside the sysmem.bank. 559d4b52dfSMax Filippov * 569d4b52dfSMax Filippov * Returns: 0 (success), -ENOMEM (not enough space in the sysmem.bank). 579d4b52dfSMax Filippov */ 589d4b52dfSMax Filippov static int __init move_banks(struct meminfo *to, struct meminfo *from) 599d4b52dfSMax Filippov { 609d4b52dfSMax Filippov unsigned n = sysmem.nr_banks - (from - sysmem.bank); 619d4b52dfSMax Filippov 629d4b52dfSMax Filippov if (to > from && to - from + sysmem.nr_banks > SYSMEM_BANKS_MAX) 639d4b52dfSMax Filippov return -ENOMEM; 649d4b52dfSMax Filippov if (to != from) 659d4b52dfSMax Filippov memmove(to, from, n * sizeof(struct meminfo)); 669d4b52dfSMax Filippov sysmem.nr_banks += to - from; 679d4b52dfSMax Filippov return 0; 689d4b52dfSMax Filippov } 699d4b52dfSMax Filippov 709d4b52dfSMax Filippov /* 719d4b52dfSMax Filippov * Add new bank to sysmem. Resulting sysmem is the union of bytes of the 729d4b52dfSMax Filippov * original sysmem and the new bank. 739d4b52dfSMax Filippov * 749d4b52dfSMax Filippov * Returns: 0 (success), < 0 (error) 759d4b52dfSMax Filippov */ 769ba067f9SMax Filippov int __init add_sysmem_bank(unsigned long start, unsigned long end) 779ba067f9SMax Filippov { 789d4b52dfSMax Filippov unsigned i; 799d4b52dfSMax Filippov struct meminfo *it = NULL; 809d4b52dfSMax Filippov unsigned long sz; 819d4b52dfSMax Filippov unsigned long bank_sz = 0; 829d4b52dfSMax Filippov 839d4b52dfSMax Filippov if (start == end || 849d4b52dfSMax Filippov (start < end) != (PAGE_ALIGN(start) < (end & PAGE_MASK))) { 859d4b52dfSMax Filippov pr_warn("Ignoring small memory bank 0x%08lx size: %ld bytes\n", 869ba067f9SMax Filippov start, end - start); 879ba067f9SMax Filippov return -EINVAL; 889ba067f9SMax Filippov } 899ba067f9SMax Filippov 909d4b52dfSMax Filippov start = PAGE_ALIGN(start); 919d4b52dfSMax Filippov end &= PAGE_MASK; 929d4b52dfSMax Filippov sz = end - start; 939d4b52dfSMax Filippov 949d4b52dfSMax Filippov it = find_bank(start); 959d4b52dfSMax Filippov 969d4b52dfSMax Filippov if (it) 979d4b52dfSMax Filippov bank_sz = it->end - it->start; 989d4b52dfSMax Filippov 999d4b52dfSMax Filippov if (it && bank_sz >= start - it->start) { 1009d4b52dfSMax Filippov if (end - it->start > bank_sz) 1019d4b52dfSMax Filippov it->end = end; 1029d4b52dfSMax Filippov else 1039d4b52dfSMax Filippov return 0; 1049d4b52dfSMax Filippov } else { 1059d4b52dfSMax Filippov if (!it) 1069d4b52dfSMax Filippov it = sysmem.bank; 1079d4b52dfSMax Filippov else 1089d4b52dfSMax Filippov ++it; 1099d4b52dfSMax Filippov 1109d4b52dfSMax Filippov if (it - sysmem.bank < sysmem.nr_banks && 1119d4b52dfSMax Filippov it->start - start <= sz) { 1129d4b52dfSMax Filippov it->start = start; 1139d4b52dfSMax Filippov if (it->end - it->start < sz) 1149d4b52dfSMax Filippov it->end = end; 1159d4b52dfSMax Filippov else 1169d4b52dfSMax Filippov return 0; 1179d4b52dfSMax Filippov } else { 1189d4b52dfSMax Filippov if (move_banks(it + 1, it) < 0) { 1199d4b52dfSMax Filippov pr_warn("Ignoring memory bank 0x%08lx size %ld bytes\n", 1209d4b52dfSMax Filippov start, end - start); 1219d4b52dfSMax Filippov return -EINVAL; 1229d4b52dfSMax Filippov } 1239d4b52dfSMax Filippov it->start = start; 1249d4b52dfSMax Filippov it->end = end; 1259d4b52dfSMax Filippov return 0; 1269d4b52dfSMax Filippov } 1279d4b52dfSMax Filippov } 1289d4b52dfSMax Filippov sz = it->end - it->start; 1299d4b52dfSMax Filippov for (i = it + 1 - sysmem.bank; i < sysmem.nr_banks; ++i) 1309d4b52dfSMax Filippov if (sysmem.bank[i].start - it->start <= sz) { 1319d4b52dfSMax Filippov if (sz < sysmem.bank[i].end - it->start) 1329d4b52dfSMax Filippov it->end = sysmem.bank[i].end; 1339d4b52dfSMax Filippov } else { 1349d4b52dfSMax Filippov break; 1359d4b52dfSMax Filippov } 1369d4b52dfSMax Filippov 1379d4b52dfSMax Filippov move_banks(it + 1, sysmem.bank + i); 1389ba067f9SMax Filippov return 0; 1399ba067f9SMax Filippov } 1403f65ce4dSChris Zankel 1413f65ce4dSChris Zankel /* 1423f65ce4dSChris Zankel * mem_reserve(start, end, must_exist) 1433f65ce4dSChris Zankel * 1443f65ce4dSChris Zankel * Reserve some memory from the memory pool. 14562327918SMax Filippov * If must_exist is set and a part of the region being reserved does not exist 14662327918SMax Filippov * memory map is not altered. 1473f65ce4dSChris Zankel * 1483f65ce4dSChris Zankel * Parameters: 1493f65ce4dSChris Zankel * start Start of region, 1503f65ce4dSChris Zankel * end End of region, 1513f65ce4dSChris Zankel * must_exist Must exist in memory pool. 1523f65ce4dSChris Zankel * 1533f65ce4dSChris Zankel * Returns: 15462327918SMax Filippov * 0 (success) 15562327918SMax Filippov * < 0 (error) 1563f65ce4dSChris Zankel */ 1573f65ce4dSChris Zankel 1583f65ce4dSChris Zankel int __init mem_reserve(unsigned long start, unsigned long end, int must_exist) 1593f65ce4dSChris Zankel { 16062327918SMax Filippov struct meminfo *it; 16162327918SMax Filippov struct meminfo *rm = NULL; 16262327918SMax Filippov unsigned long sz; 16362327918SMax Filippov unsigned long bank_sz = 0; 1643f65ce4dSChris Zankel 1653f65ce4dSChris Zankel start = start & PAGE_MASK; 1663f65ce4dSChris Zankel end = PAGE_ALIGN(end); 16762327918SMax Filippov sz = end - start; 16862327918SMax Filippov if (!sz) 16962327918SMax Filippov return -EINVAL; 1703f65ce4dSChris Zankel 17162327918SMax Filippov it = find_bank(start); 1723f65ce4dSChris Zankel 17362327918SMax Filippov if (it) 17462327918SMax Filippov bank_sz = it->end - it->start; 17562327918SMax Filippov 17662327918SMax Filippov if ((!it || end - it->start > bank_sz) && must_exist) { 17762327918SMax Filippov pr_warn("mem_reserve: [0x%0lx, 0x%0lx) not in any region!\n", 17862327918SMax Filippov start, end); 17962327918SMax Filippov return -EINVAL; 18062327918SMax Filippov } 18162327918SMax Filippov 18262327918SMax Filippov if (it && start - it->start < bank_sz) { 18362327918SMax Filippov if (start == it->start) { 18462327918SMax Filippov if (end - it->start < bank_sz) { 18562327918SMax Filippov it->start = end; 1863f65ce4dSChris Zankel return 0; 187c4c4594bSChris Zankel } else { 18862327918SMax Filippov rm = it; 1893f65ce4dSChris Zankel } 19062327918SMax Filippov } else { 19162327918SMax Filippov it->end = start; 19262327918SMax Filippov if (end - it->start < bank_sz) 19362327918SMax Filippov return add_sysmem_bank(end, 19462327918SMax Filippov it->start + bank_sz); 19562327918SMax Filippov ++it; 19662327918SMax Filippov } 19762327918SMax Filippov } 19862327918SMax Filippov 19962327918SMax Filippov if (!it) 20062327918SMax Filippov it = sysmem.bank; 20162327918SMax Filippov 20262327918SMax Filippov for (; it < sysmem.bank + sysmem.nr_banks; ++it) { 20362327918SMax Filippov if (it->end - start <= sz) { 20462327918SMax Filippov if (!rm) 20562327918SMax Filippov rm = it; 20662327918SMax Filippov } else { 20762327918SMax Filippov if (it->start - start < sz) 20862327918SMax Filippov it->start = end; 20962327918SMax Filippov break; 21062327918SMax Filippov } 21162327918SMax Filippov } 21262327918SMax Filippov 21362327918SMax Filippov if (rm) 21462327918SMax Filippov move_banks(rm, it); 21562327918SMax Filippov 21662327918SMax Filippov return 0; 2173f65ce4dSChris Zankel } 2183f65ce4dSChris Zankel 2193f65ce4dSChris Zankel 2203f65ce4dSChris Zankel /* 221e9d6dca5SMax Filippov * Initialize the bootmem system and give it all low memory we have available. 2223f65ce4dSChris Zankel */ 2233f65ce4dSChris Zankel 2243f65ce4dSChris Zankel void __init bootmem_init(void) 2253f65ce4dSChris Zankel { 2263f65ce4dSChris Zankel unsigned long pfn; 2273f65ce4dSChris Zankel unsigned long bootmap_start, bootmap_size; 2283f65ce4dSChris Zankel int i; 2293f65ce4dSChris Zankel 2303f65ce4dSChris Zankel max_low_pfn = max_pfn = 0; 2313f65ce4dSChris Zankel min_low_pfn = ~0; 2323f65ce4dSChris Zankel 2333f65ce4dSChris Zankel for (i=0; i < sysmem.nr_banks; i++) { 2343f65ce4dSChris Zankel pfn = PAGE_ALIGN(sysmem.bank[i].start) >> PAGE_SHIFT; 2353f65ce4dSChris Zankel if (pfn < min_low_pfn) 2363f65ce4dSChris Zankel min_low_pfn = pfn; 2373f65ce4dSChris Zankel pfn = PAGE_ALIGN(sysmem.bank[i].end - 1) >> PAGE_SHIFT; 2383f65ce4dSChris Zankel if (pfn > max_pfn) 2393f65ce4dSChris Zankel max_pfn = pfn; 2403f65ce4dSChris Zankel } 2413f65ce4dSChris Zankel 2423f65ce4dSChris Zankel if (min_low_pfn > max_pfn) 2433f65ce4dSChris Zankel panic("No memory found!\n"); 2443f65ce4dSChris Zankel 245173d6681SChris Zankel max_low_pfn = max_pfn < MAX_MEM_PFN >> PAGE_SHIFT ? 246173d6681SChris Zankel max_pfn : MAX_MEM_PFN >> PAGE_SHIFT; 2473f65ce4dSChris Zankel 2483f65ce4dSChris Zankel /* Find an area to use for the bootmem bitmap. */ 2493f65ce4dSChris Zankel 250264da9f7SJohannes Weiner bootmap_size = bootmem_bootmap_pages(max_low_pfn - min_low_pfn); 251264da9f7SJohannes Weiner bootmap_size <<= PAGE_SHIFT; 2523f65ce4dSChris Zankel bootmap_start = ~0; 2533f65ce4dSChris Zankel 2543f65ce4dSChris Zankel for (i=0; i<sysmem.nr_banks; i++) 2553f65ce4dSChris Zankel if (sysmem.bank[i].end - sysmem.bank[i].start >= bootmap_size) { 2563f65ce4dSChris Zankel bootmap_start = sysmem.bank[i].start; 2573f65ce4dSChris Zankel break; 2583f65ce4dSChris Zankel } 2593f65ce4dSChris Zankel 2603f65ce4dSChris Zankel if (bootmap_start == ~0UL) 2613f65ce4dSChris Zankel panic("Cannot find %ld bytes for bootmap\n", bootmap_size); 2623f65ce4dSChris Zankel 2633f65ce4dSChris Zankel /* Reserve the bootmem bitmap area */ 2643f65ce4dSChris Zankel 2653f65ce4dSChris Zankel mem_reserve(bootmap_start, bootmap_start + bootmap_size, 1); 2660bef42e5SJohannes Weiner bootmap_size = init_bootmem_node(NODE_DATA(0), 2673f65ce4dSChris Zankel bootmap_start >> PAGE_SHIFT, 2680bef42e5SJohannes Weiner min_low_pfn, 2693f65ce4dSChris Zankel max_low_pfn); 2703f65ce4dSChris Zankel 2713f65ce4dSChris Zankel /* Add all remaining memory pieces into the bootmem map */ 2723f65ce4dSChris Zankel 273e9d6dca5SMax Filippov for (i = 0; i < sysmem.nr_banks; i++) { 274e9d6dca5SMax Filippov if (sysmem.bank[i].start >> PAGE_SHIFT < max_low_pfn) { 275e9d6dca5SMax Filippov unsigned long end = min(max_low_pfn << PAGE_SHIFT, 276e9d6dca5SMax Filippov sysmem.bank[i].end); 2773f65ce4dSChris Zankel free_bootmem(sysmem.bank[i].start, 278e9d6dca5SMax Filippov end - sysmem.bank[i].start); 279e9d6dca5SMax Filippov } 280e9d6dca5SMax Filippov } 2813f65ce4dSChris Zankel 2823f65ce4dSChris Zankel } 2833f65ce4dSChris Zankel 2843f65ce4dSChris Zankel 285e5083a63SJohannes Weiner void __init zones_init(void) 2863f65ce4dSChris Zankel { 2873f65ce4dSChris Zankel unsigned long zones_size[MAX_NR_ZONES]; 2883f65ce4dSChris Zankel int i; 2893f65ce4dSChris Zankel 2903f65ce4dSChris Zankel /* All pages are DMA-able, so we put them all in the DMA zone. */ 2913f65ce4dSChris Zankel 292c947a585SJohannes Weiner zones_size[ZONE_DMA] = max_low_pfn - ARCH_PFN_OFFSET; 2933f65ce4dSChris Zankel for (i = 1; i < MAX_NR_ZONES; i++) 2943f65ce4dSChris Zankel zones_size[i] = 0; 2953f65ce4dSChris Zankel 2963f65ce4dSChris Zankel #ifdef CONFIG_HIGHMEM 2973f65ce4dSChris Zankel zones_size[ZONE_HIGHMEM] = max_pfn - max_low_pfn; 2983f65ce4dSChris Zankel #endif 2993f65ce4dSChris Zankel 300c947a585SJohannes Weiner free_area_init_node(0, zones_size, ARCH_PFN_OFFSET, NULL); 3013f65ce4dSChris Zankel } 3023f65ce4dSChris Zankel 3033f65ce4dSChris Zankel /* 3043f65ce4dSChris Zankel * Initialize memory pages. 3053f65ce4dSChris Zankel */ 3063f65ce4dSChris Zankel 3073f65ce4dSChris Zankel void __init mem_init(void) 3083f65ce4dSChris Zankel { 309808c2c37SJiang Liu max_mapnr = max_low_pfn - ARCH_PFN_OFFSET; 310c947a585SJohannes Weiner high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT); 3113f65ce4dSChris Zankel 312288a60cfSChris Zankel #ifdef CONFIG_HIGHMEM 3133f65ce4dSChris Zankel #error HIGHGMEM not implemented in init.c 3143f65ce4dSChris Zankel #endif 3153f65ce4dSChris Zankel 3160c988534SJiang Liu free_all_bootmem(); 3173f65ce4dSChris Zankel 318808c2c37SJiang Liu mem_init_print_info(NULL); 3193f65ce4dSChris Zankel } 3203f65ce4dSChris Zankel 3213f65ce4dSChris Zankel #ifdef CONFIG_BLK_DEV_INITRD 3223f65ce4dSChris Zankel extern int initrd_is_mapped; 3233f65ce4dSChris Zankel 3243f65ce4dSChris Zankel void free_initrd_mem(unsigned long start, unsigned long end) 3253f65ce4dSChris Zankel { 3267acb2c2eSJiang Liu if (initrd_is_mapped) 327dbe67df4SJiang Liu free_reserved_area((void *)start, (void *)end, -1, "initrd"); 3283f65ce4dSChris Zankel } 3293f65ce4dSChris Zankel #endif 3303f65ce4dSChris Zankel 3313f65ce4dSChris Zankel void free_initmem(void) 3323f65ce4dSChris Zankel { 333dbe67df4SJiang Liu free_initmem_default(-1); 3343f65ce4dSChris Zankel } 335