xref: /linux/arch/xtensa/mm/init.c (revision 827634added7f38b7d724cab1dccdb2b004c13c3)
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);
431