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. 1453f65ce4dSChris Zankel * 1463f65ce4dSChris Zankel * Parameters: 1473f65ce4dSChris Zankel * start Start of region, 1483f65ce4dSChris Zankel * end End of region, 1493f65ce4dSChris Zankel * must_exist Must exist in memory pool. 1503f65ce4dSChris Zankel * 1513f65ce4dSChris Zankel * Returns: 1523f65ce4dSChris Zankel * 0 (memory area couldn't be mapped) 1533f65ce4dSChris Zankel * -1 (success) 1543f65ce4dSChris Zankel */ 1553f65ce4dSChris Zankel 1563f65ce4dSChris Zankel int __init mem_reserve(unsigned long start, unsigned long end, int must_exist) 1573f65ce4dSChris Zankel { 1583f65ce4dSChris Zankel int i; 1593f65ce4dSChris Zankel 1603f65ce4dSChris Zankel if (start == end) 1613f65ce4dSChris Zankel return 0; 1623f65ce4dSChris Zankel 1633f65ce4dSChris Zankel start = start & PAGE_MASK; 1643f65ce4dSChris Zankel end = PAGE_ALIGN(end); 1653f65ce4dSChris Zankel 1663f65ce4dSChris Zankel for (i = 0; i < sysmem.nr_banks; i++) 1673f65ce4dSChris Zankel if (start < sysmem.bank[i].end 1683f65ce4dSChris Zankel && end >= sysmem.bank[i].start) 1693f65ce4dSChris Zankel break; 1703f65ce4dSChris Zankel 1713f65ce4dSChris Zankel if (i == sysmem.nr_banks) { 1723f65ce4dSChris Zankel if (must_exist) 1733f65ce4dSChris Zankel printk (KERN_WARNING "mem_reserve: [0x%0lx, 0x%0lx) " 1743f65ce4dSChris Zankel "not in any region!\n", start, end); 1753f65ce4dSChris Zankel return 0; 1763f65ce4dSChris Zankel } 1773f65ce4dSChris Zankel 1783f65ce4dSChris Zankel if (start > sysmem.bank[i].start) { 1793f65ce4dSChris Zankel if (end < sysmem.bank[i].end) { 1803f65ce4dSChris Zankel /* split entry */ 1813f65ce4dSChris Zankel if (sysmem.nr_banks >= SYSMEM_BANKS_MAX) 1823f65ce4dSChris Zankel panic("meminfo overflow\n"); 1833f65ce4dSChris Zankel sysmem.bank[sysmem.nr_banks].start = end; 1843f65ce4dSChris Zankel sysmem.bank[sysmem.nr_banks].end = sysmem.bank[i].end; 1853f65ce4dSChris Zankel sysmem.nr_banks++; 1863f65ce4dSChris Zankel } 1873f65ce4dSChris Zankel sysmem.bank[i].end = start; 188c4c4594bSChris Zankel 189c4c4594bSChris Zankel } else if (end < sysmem.bank[i].end) { 1903f65ce4dSChris Zankel sysmem.bank[i].start = end; 191c4c4594bSChris Zankel 192c4c4594bSChris Zankel } else { 1933f65ce4dSChris Zankel /* remove entry */ 1943f65ce4dSChris Zankel sysmem.nr_banks--; 1953f65ce4dSChris Zankel sysmem.bank[i].start = sysmem.bank[sysmem.nr_banks].start; 1963f65ce4dSChris Zankel sysmem.bank[i].end = sysmem.bank[sysmem.nr_banks].end; 1973f65ce4dSChris Zankel } 1983f65ce4dSChris Zankel return -1; 1993f65ce4dSChris Zankel } 2003f65ce4dSChris Zankel 2013f65ce4dSChris Zankel 2023f65ce4dSChris Zankel /* 203e9d6dca5SMax Filippov * Initialize the bootmem system and give it all low memory we have available. 2043f65ce4dSChris Zankel */ 2053f65ce4dSChris Zankel 2063f65ce4dSChris Zankel void __init bootmem_init(void) 2073f65ce4dSChris Zankel { 2083f65ce4dSChris Zankel unsigned long pfn; 2093f65ce4dSChris Zankel unsigned long bootmap_start, bootmap_size; 2103f65ce4dSChris Zankel int i; 2113f65ce4dSChris Zankel 2123f65ce4dSChris Zankel max_low_pfn = max_pfn = 0; 2133f65ce4dSChris Zankel min_low_pfn = ~0; 2143f65ce4dSChris Zankel 2153f65ce4dSChris Zankel for (i=0; i < sysmem.nr_banks; i++) { 2163f65ce4dSChris Zankel pfn = PAGE_ALIGN(sysmem.bank[i].start) >> PAGE_SHIFT; 2173f65ce4dSChris Zankel if (pfn < min_low_pfn) 2183f65ce4dSChris Zankel min_low_pfn = pfn; 2193f65ce4dSChris Zankel pfn = PAGE_ALIGN(sysmem.bank[i].end - 1) >> PAGE_SHIFT; 2203f65ce4dSChris Zankel if (pfn > max_pfn) 2213f65ce4dSChris Zankel max_pfn = pfn; 2223f65ce4dSChris Zankel } 2233f65ce4dSChris Zankel 2243f65ce4dSChris Zankel if (min_low_pfn > max_pfn) 2253f65ce4dSChris Zankel panic("No memory found!\n"); 2263f65ce4dSChris Zankel 227173d6681SChris Zankel max_low_pfn = max_pfn < MAX_MEM_PFN >> PAGE_SHIFT ? 228173d6681SChris Zankel max_pfn : MAX_MEM_PFN >> PAGE_SHIFT; 2293f65ce4dSChris Zankel 2303f65ce4dSChris Zankel /* Find an area to use for the bootmem bitmap. */ 2313f65ce4dSChris Zankel 232264da9f7SJohannes Weiner bootmap_size = bootmem_bootmap_pages(max_low_pfn - min_low_pfn); 233264da9f7SJohannes Weiner bootmap_size <<= PAGE_SHIFT; 2343f65ce4dSChris Zankel bootmap_start = ~0; 2353f65ce4dSChris Zankel 2363f65ce4dSChris Zankel for (i=0; i<sysmem.nr_banks; i++) 2373f65ce4dSChris Zankel if (sysmem.bank[i].end - sysmem.bank[i].start >= bootmap_size) { 2383f65ce4dSChris Zankel bootmap_start = sysmem.bank[i].start; 2393f65ce4dSChris Zankel break; 2403f65ce4dSChris Zankel } 2413f65ce4dSChris Zankel 2423f65ce4dSChris Zankel if (bootmap_start == ~0UL) 2433f65ce4dSChris Zankel panic("Cannot find %ld bytes for bootmap\n", bootmap_size); 2443f65ce4dSChris Zankel 2453f65ce4dSChris Zankel /* Reserve the bootmem bitmap area */ 2463f65ce4dSChris Zankel 2473f65ce4dSChris Zankel mem_reserve(bootmap_start, bootmap_start + bootmap_size, 1); 2480bef42e5SJohannes Weiner bootmap_size = init_bootmem_node(NODE_DATA(0), 2493f65ce4dSChris Zankel bootmap_start >> PAGE_SHIFT, 2500bef42e5SJohannes Weiner min_low_pfn, 2513f65ce4dSChris Zankel max_low_pfn); 2523f65ce4dSChris Zankel 2533f65ce4dSChris Zankel /* Add all remaining memory pieces into the bootmem map */ 2543f65ce4dSChris Zankel 255e9d6dca5SMax Filippov for (i = 0; i < sysmem.nr_banks; i++) { 256e9d6dca5SMax Filippov if (sysmem.bank[i].start >> PAGE_SHIFT < max_low_pfn) { 257e9d6dca5SMax Filippov unsigned long end = min(max_low_pfn << PAGE_SHIFT, 258e9d6dca5SMax Filippov sysmem.bank[i].end); 2593f65ce4dSChris Zankel free_bootmem(sysmem.bank[i].start, 260e9d6dca5SMax Filippov end - sysmem.bank[i].start); 261e9d6dca5SMax Filippov } 262e9d6dca5SMax Filippov } 2633f65ce4dSChris Zankel 2643f65ce4dSChris Zankel } 2653f65ce4dSChris Zankel 2663f65ce4dSChris Zankel 267e5083a63SJohannes Weiner void __init zones_init(void) 2683f65ce4dSChris Zankel { 2693f65ce4dSChris Zankel unsigned long zones_size[MAX_NR_ZONES]; 2703f65ce4dSChris Zankel int i; 2713f65ce4dSChris Zankel 2723f65ce4dSChris Zankel /* All pages are DMA-able, so we put them all in the DMA zone. */ 2733f65ce4dSChris Zankel 274c947a585SJohannes Weiner zones_size[ZONE_DMA] = max_low_pfn - ARCH_PFN_OFFSET; 2753f65ce4dSChris Zankel for (i = 1; i < MAX_NR_ZONES; i++) 2763f65ce4dSChris Zankel zones_size[i] = 0; 2773f65ce4dSChris Zankel 2783f65ce4dSChris Zankel #ifdef CONFIG_HIGHMEM 2793f65ce4dSChris Zankel zones_size[ZONE_HIGHMEM] = max_pfn - max_low_pfn; 2803f65ce4dSChris Zankel #endif 2813f65ce4dSChris Zankel 282c947a585SJohannes Weiner free_area_init_node(0, zones_size, ARCH_PFN_OFFSET, NULL); 2833f65ce4dSChris Zankel } 2843f65ce4dSChris Zankel 2853f65ce4dSChris Zankel /* 2863f65ce4dSChris Zankel * Initialize memory pages. 2873f65ce4dSChris Zankel */ 2883f65ce4dSChris Zankel 2893f65ce4dSChris Zankel void __init mem_init(void) 2903f65ce4dSChris Zankel { 291808c2c37SJiang Liu max_mapnr = max_low_pfn - ARCH_PFN_OFFSET; 292c947a585SJohannes Weiner high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT); 2933f65ce4dSChris Zankel 294288a60cfSChris Zankel #ifdef CONFIG_HIGHMEM 2953f65ce4dSChris Zankel #error HIGHGMEM not implemented in init.c 2963f65ce4dSChris Zankel #endif 2973f65ce4dSChris Zankel 2980c988534SJiang Liu free_all_bootmem(); 2993f65ce4dSChris Zankel 300808c2c37SJiang Liu mem_init_print_info(NULL); 3013f65ce4dSChris Zankel } 3023f65ce4dSChris Zankel 3033f65ce4dSChris Zankel #ifdef CONFIG_BLK_DEV_INITRD 3043f65ce4dSChris Zankel extern int initrd_is_mapped; 3053f65ce4dSChris Zankel 3063f65ce4dSChris Zankel void free_initrd_mem(unsigned long start, unsigned long end) 3073f65ce4dSChris Zankel { 3087acb2c2eSJiang Liu if (initrd_is_mapped) 309dbe67df4SJiang Liu free_reserved_area((void *)start, (void *)end, -1, "initrd"); 3103f65ce4dSChris Zankel } 3113f65ce4dSChris Zankel #endif 3123f65ce4dSChris Zankel 3133f65ce4dSChris Zankel void free_initmem(void) 3143f65ce4dSChris Zankel { 315dbe67df4SJiang Liu free_initmem_default(-1); 3163f65ce4dSChris Zankel } 317