Merge tag 'staging-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
[sfrench/cifs-2.6.git] / arch / xtensa / mm / init.c
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/highmem.h>
24 #include <linux/swap.h>
25 #include <linux/mman.h>
26 #include <linux/nodemask.h>
27 #include <linux/mm.h>
28
29 #include <asm/bootparam.h>
30 #include <asm/page.h>
31 #include <asm/sections.h>
32 #include <asm/sysmem.h>
33
34 struct sysmem_info sysmem __initdata;
35
36 static void __init sysmem_dump(void)
37 {
38         unsigned i;
39
40         pr_debug("Sysmem:\n");
41         for (i = 0; i < sysmem.nr_banks; ++i)
42                 pr_debug("  0x%08lx - 0x%08lx (%ldK)\n",
43                          sysmem.bank[i].start, sysmem.bank[i].end,
44                          (sysmem.bank[i].end - sysmem.bank[i].start) >> 10);
45 }
46
47 /*
48  * Find bank with maximal .start such that bank.start <= start
49  */
50 static inline struct meminfo * __init find_bank(unsigned long start)
51 {
52         unsigned i;
53         struct meminfo *it = NULL;
54
55         for (i = 0; i < sysmem.nr_banks; ++i)
56                 if (sysmem.bank[i].start <= start)
57                         it = sysmem.bank + i;
58                 else
59                         break;
60         return it;
61 }
62
63 /*
64  * Move all memory banks starting at 'from' to a new place at 'to',
65  * adjust nr_banks accordingly.
66  * Both 'from' and 'to' must be inside the sysmem.bank.
67  *
68  * Returns: 0 (success), -ENOMEM (not enough space in the sysmem.bank).
69  */
70 static int __init move_banks(struct meminfo *to, struct meminfo *from)
71 {
72         unsigned n = sysmem.nr_banks - (from - sysmem.bank);
73
74         if (to > from && to - from + sysmem.nr_banks > SYSMEM_BANKS_MAX)
75                 return -ENOMEM;
76         if (to != from)
77                 memmove(to, from, n * sizeof(struct meminfo));
78         sysmem.nr_banks += to - from;
79         return 0;
80 }
81
82 /*
83  * Add new bank to sysmem. Resulting sysmem is the union of bytes of the
84  * original sysmem and the new bank.
85  *
86  * Returns: 0 (success), < 0 (error)
87  */
88 int __init add_sysmem_bank(unsigned long start, unsigned long end)
89 {
90         unsigned i;
91         struct meminfo *it = NULL;
92         unsigned long sz;
93         unsigned long bank_sz = 0;
94
95         if (start == end ||
96             (start < end) != (PAGE_ALIGN(start) < (end & PAGE_MASK))) {
97                 pr_warn("Ignoring small memory bank 0x%08lx size: %ld bytes\n",
98                         start, end - start);
99                 return -EINVAL;
100         }
101
102         start = PAGE_ALIGN(start);
103         end &= PAGE_MASK;
104         sz = end - start;
105
106         it = find_bank(start);
107
108         if (it)
109                 bank_sz = it->end - it->start;
110
111         if (it && bank_sz >= start - it->start) {
112                 if (end - it->start > bank_sz)
113                         it->end = end;
114                 else
115                         return 0;
116         } else {
117                 if (!it)
118                         it = sysmem.bank;
119                 else
120                         ++it;
121
122                 if (it - sysmem.bank < sysmem.nr_banks &&
123                     it->start - start <= sz) {
124                         it->start = start;
125                         if (it->end - it->start < sz)
126                                 it->end = end;
127                         else
128                                 return 0;
129                 } else {
130                         if (move_banks(it + 1, it) < 0) {
131                                 pr_warn("Ignoring memory bank 0x%08lx size %ld bytes\n",
132                                         start, end - start);
133                                 return -EINVAL;
134                         }
135                         it->start = start;
136                         it->end = end;
137                         return 0;
138                 }
139         }
140         sz = it->end - it->start;
141         for (i = it + 1 - sysmem.bank; i < sysmem.nr_banks; ++i)
142                 if (sysmem.bank[i].start - it->start <= sz) {
143                         if (sz < sysmem.bank[i].end - it->start)
144                                 it->end = sysmem.bank[i].end;
145                 } else {
146                         break;
147                 }
148
149         move_banks(it + 1, sysmem.bank + i);
150         return 0;
151 }
152
153 /*
154  * mem_reserve(start, end, must_exist)
155  *
156  * Reserve some memory from the memory pool.
157  * If must_exist is set and a part of the region being reserved does not exist
158  * memory map is not altered.
159  *
160  * Parameters:
161  *  start       Start of region,
162  *  end         End of region,
163  *  must_exist  Must exist in memory pool.
164  *
165  * Returns:
166  *  0 (success)
167  *  < 0 (error)
168  */
169
170 int __init mem_reserve(unsigned long start, unsigned long end, int must_exist)
171 {
172         struct meminfo *it;
173         struct meminfo *rm = NULL;
174         unsigned long sz;
175         unsigned long bank_sz = 0;
176
177         start = start & PAGE_MASK;
178         end = PAGE_ALIGN(end);
179         sz = end - start;
180         if (!sz)
181                 return -EINVAL;
182
183         it = find_bank(start);
184
185         if (it)
186                 bank_sz = it->end - it->start;
187
188         if ((!it || end - it->start > bank_sz) && must_exist) {
189                 pr_warn("mem_reserve: [0x%0lx, 0x%0lx) not in any region!\n",
190                         start, end);
191                 return -EINVAL;
192         }
193
194         if (it && start - it->start <= bank_sz) {
195                 if (start == it->start) {
196                         if (end - it->start < bank_sz) {
197                                 it->start = end;
198                                 return 0;
199                         } else {
200                                 rm = it;
201                         }
202                 } else {
203                         it->end = start;
204                         if (end - it->start < bank_sz)
205                                 return add_sysmem_bank(end,
206                                                        it->start + bank_sz);
207                         ++it;
208                 }
209         }
210
211         if (!it)
212                 it = sysmem.bank;
213
214         for (; it < sysmem.bank + sysmem.nr_banks; ++it) {
215                 if (it->end - start <= sz) {
216                         if (!rm)
217                                 rm = it;
218                 } else {
219                         if (it->start - start < sz)
220                                 it->start = end;
221                         break;
222                 }
223         }
224
225         if (rm)
226                 move_banks(rm, it);
227
228         return 0;
229 }
230
231
232 /*
233  * Initialize the bootmem system and give it all low memory we have available.
234  */
235
236 void __init bootmem_init(void)
237 {
238         unsigned long pfn;
239         unsigned long bootmap_start, bootmap_size;
240         int i;
241
242         /* Reserve all memory below PLATFORM_DEFAULT_MEM_START, as memory
243          * accounting doesn't work for pages below that address.
244          *
245          * If PLATFORM_DEFAULT_MEM_START is zero reserve page at address 0:
246          * successfull allocations should never return NULL.
247          */
248         if (PLATFORM_DEFAULT_MEM_START)
249                 mem_reserve(0, PLATFORM_DEFAULT_MEM_START, 0);
250         else
251                 mem_reserve(0, 1, 0);
252
253         sysmem_dump();
254         max_low_pfn = max_pfn = 0;
255         min_low_pfn = ~0;
256
257         for (i=0; i < sysmem.nr_banks; i++) {
258                 pfn = PAGE_ALIGN(sysmem.bank[i].start) >> PAGE_SHIFT;
259                 if (pfn < min_low_pfn)
260                         min_low_pfn = pfn;
261                 pfn = PAGE_ALIGN(sysmem.bank[i].end - 1) >> PAGE_SHIFT;
262                 if (pfn > max_pfn)
263                         max_pfn = pfn;
264         }
265
266         if (min_low_pfn > max_pfn)
267                 panic("No memory found!\n");
268
269         max_low_pfn = max_pfn < MAX_MEM_PFN >> PAGE_SHIFT ?
270                 max_pfn : MAX_MEM_PFN >> PAGE_SHIFT;
271
272         /* Find an area to use for the bootmem bitmap. */
273
274         bootmap_size = bootmem_bootmap_pages(max_low_pfn - min_low_pfn);
275         bootmap_size <<= PAGE_SHIFT;
276         bootmap_start = ~0;
277
278         for (i=0; i<sysmem.nr_banks; i++)
279                 if (sysmem.bank[i].end - sysmem.bank[i].start >= bootmap_size) {
280                         bootmap_start = sysmem.bank[i].start;
281                         break;
282                 }
283
284         if (bootmap_start == ~0UL)
285                 panic("Cannot find %ld bytes for bootmap\n", bootmap_size);
286
287         /* Reserve the bootmem bitmap area */
288
289         mem_reserve(bootmap_start, bootmap_start + bootmap_size, 1);
290         bootmap_size = init_bootmem_node(NODE_DATA(0),
291                                          bootmap_start >> PAGE_SHIFT,
292                                          min_low_pfn,
293                                          max_low_pfn);
294
295         /* Add all remaining memory pieces into the bootmem map */
296
297         for (i = 0; i < sysmem.nr_banks; i++) {
298                 if (sysmem.bank[i].start >> PAGE_SHIFT < max_low_pfn) {
299                         unsigned long end = min(max_low_pfn << PAGE_SHIFT,
300                                                 sysmem.bank[i].end);
301                         free_bootmem(sysmem.bank[i].start,
302                                      end - sysmem.bank[i].start);
303                 }
304         }
305
306 }
307
308
309 void __init zones_init(void)
310 {
311         /* All pages are DMA-able, so we put them all in the DMA zone. */
312         unsigned long zones_size[MAX_NR_ZONES] = {
313                 [ZONE_DMA] = max_low_pfn - ARCH_PFN_OFFSET,
314 #ifdef CONFIG_HIGHMEM
315                 [ZONE_HIGHMEM] = max_pfn - max_low_pfn,
316 #endif
317         };
318         free_area_init_node(0, zones_size, ARCH_PFN_OFFSET, NULL);
319 }
320
321 /*
322  * Initialize memory pages.
323  */
324
325 void __init mem_init(void)
326 {
327 #ifdef CONFIG_HIGHMEM
328         unsigned long tmp;
329
330         reset_all_zones_managed_pages();
331         for (tmp = max_low_pfn; tmp < max_pfn; tmp++)
332                 free_highmem_page(pfn_to_page(tmp));
333 #endif
334
335         max_mapnr = max_pfn - ARCH_PFN_OFFSET;
336         high_memory = (void *)__va(max_low_pfn << PAGE_SHIFT);
337
338         free_all_bootmem();
339
340         mem_init_print_info(NULL);
341         pr_info("virtual kernel memory layout:\n"
342 #ifdef CONFIG_HIGHMEM
343                 "    pkmap   : 0x%08lx - 0x%08lx  (%5lu kB)\n"
344                 "    fixmap  : 0x%08lx - 0x%08lx  (%5lu kB)\n"
345 #endif
346 #ifdef CONFIG_MMU
347                 "    vmalloc : 0x%08x - 0x%08x  (%5u MB)\n"
348 #endif
349                 "    lowmem  : 0x%08lx - 0x%08lx  (%5lu MB)\n",
350 #ifdef CONFIG_HIGHMEM
351                 PKMAP_BASE, PKMAP_BASE + LAST_PKMAP * PAGE_SIZE,
352                 (LAST_PKMAP*PAGE_SIZE) >> 10,
353                 FIXADDR_START, FIXADDR_TOP,
354                 (FIXADDR_TOP - FIXADDR_START) >> 10,
355 #endif
356 #ifdef CONFIG_MMU
357                 VMALLOC_START, VMALLOC_END,
358                 (VMALLOC_END - VMALLOC_START) >> 20,
359                 PAGE_OFFSET, PAGE_OFFSET +
360                 (max_low_pfn - min_low_pfn) * PAGE_SIZE,
361 #else
362                 min_low_pfn * PAGE_SIZE, max_low_pfn * PAGE_SIZE,
363 #endif
364                 ((max_low_pfn - min_low_pfn) * PAGE_SIZE) >> 20);
365 }
366
367 #ifdef CONFIG_BLK_DEV_INITRD
368 extern int initrd_is_mapped;
369
370 void free_initrd_mem(unsigned long start, unsigned long end)
371 {
372         if (initrd_is_mapped)
373                 free_reserved_area((void *)start, (void *)end, -1, "initrd");
374 }
375 #endif
376
377 void free_initmem(void)
378 {
379         free_initmem_default(-1);
380 }
381
382 static void __init parse_memmap_one(char *p)
383 {
384         char *oldp;
385         unsigned long start_at, mem_size;
386
387         if (!p)
388                 return;
389
390         oldp = p;
391         mem_size = memparse(p, &p);
392         if (p == oldp)
393                 return;
394
395         switch (*p) {
396         case '@':
397                 start_at = memparse(p + 1, &p);
398                 add_sysmem_bank(start_at, start_at + mem_size);
399                 break;
400
401         case '$':
402                 start_at = memparse(p + 1, &p);
403                 mem_reserve(start_at, start_at + mem_size, 0);
404                 break;
405
406         case 0:
407                 mem_reserve(mem_size, 0, 0);
408                 break;
409
410         default:
411                 pr_warn("Unrecognized memmap syntax: %s\n", p);
412                 break;
413         }
414 }
415
416 static int __init parse_memmap_opt(char *str)
417 {
418         while (str) {
419                 char *k = strchr(str, ',');
420
421                 if (k)
422                         *k++ = 0;
423
424                 parse_memmap_one(str);
425                 str = k;
426         }
427
428         return 0;
429 }
430 early_param("memmap", parse_memmap_opt);