xref: /openbmc/linux/arch/xtensa/mm/init.c (revision d39af902)
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 = min(max_pfn, MAX_LOW_PFN);
270 
271 	/* Find an area to use for the bootmem bitmap. */
272 
273 	bootmap_size = bootmem_bootmap_pages(max_low_pfn - min_low_pfn);
274 	bootmap_size <<= PAGE_SHIFT;
275 	bootmap_start = ~0;
276 
277 	for (i=0; i<sysmem.nr_banks; i++)
278 		if (sysmem.bank[i].end - sysmem.bank[i].start >= bootmap_size) {
279 			bootmap_start = sysmem.bank[i].start;
280 			break;
281 		}
282 
283 	if (bootmap_start == ~0UL)
284 		panic("Cannot find %ld bytes for bootmap\n", bootmap_size);
285 
286 	/* Reserve the bootmem bitmap area */
287 
288 	mem_reserve(bootmap_start, bootmap_start + bootmap_size, 1);
289 	bootmap_size = init_bootmem_node(NODE_DATA(0),
290 					 bootmap_start >> PAGE_SHIFT,
291 					 min_low_pfn,
292 					 max_low_pfn);
293 
294 	/* Add all remaining memory pieces into the bootmem map */
295 
296 	for (i = 0; i < sysmem.nr_banks; i++) {
297 		if (sysmem.bank[i].start >> PAGE_SHIFT < max_low_pfn) {
298 			unsigned long end = min(max_low_pfn << PAGE_SHIFT,
299 						sysmem.bank[i].end);
300 			free_bootmem(sysmem.bank[i].start,
301 				     end - sysmem.bank[i].start);
302 		}
303 	}
304 
305 }
306 
307 
308 void __init zones_init(void)
309 {
310 	/* All pages are DMA-able, so we put them all in the DMA zone. */
311 	unsigned long zones_size[MAX_NR_ZONES] = {
312 		[ZONE_DMA] = max_low_pfn - ARCH_PFN_OFFSET,
313 #ifdef CONFIG_HIGHMEM
314 		[ZONE_HIGHMEM] = max_pfn - max_low_pfn,
315 #endif
316 	};
317 	free_area_init_node(0, zones_size, ARCH_PFN_OFFSET, NULL);
318 }
319 
320 /*
321  * Initialize memory pages.
322  */
323 
324 void __init mem_init(void)
325 {
326 #ifdef CONFIG_HIGHMEM
327 	unsigned long tmp;
328 
329 	reset_all_zones_managed_pages();
330 	for (tmp = max_low_pfn; tmp < max_pfn; tmp++)
331 		free_highmem_page(pfn_to_page(tmp));
332 #endif
333 
334 	max_mapnr = max_pfn - ARCH_PFN_OFFSET;
335 	high_memory = (void *)__va(max_low_pfn << PAGE_SHIFT);
336 
337 	free_all_bootmem();
338 
339 	mem_init_print_info(NULL);
340 	pr_info("virtual kernel memory layout:\n"
341 #ifdef CONFIG_HIGHMEM
342 		"    pkmap   : 0x%08lx - 0x%08lx  (%5lu kB)\n"
343 		"    fixmap  : 0x%08lx - 0x%08lx  (%5lu kB)\n"
344 #endif
345 #ifdef CONFIG_MMU
346 		"    vmalloc : 0x%08lx - 0x%08lx  (%5lu MB)\n"
347 #endif
348 		"    lowmem  : 0x%08lx - 0x%08lx  (%5lu MB)\n",
349 #ifdef CONFIG_HIGHMEM
350 		PKMAP_BASE, PKMAP_BASE + LAST_PKMAP * PAGE_SIZE,
351 		(LAST_PKMAP*PAGE_SIZE) >> 10,
352 		FIXADDR_START, FIXADDR_TOP,
353 		(FIXADDR_TOP - FIXADDR_START) >> 10,
354 #endif
355 #ifdef CONFIG_MMU
356 		VMALLOC_START, VMALLOC_END,
357 		(VMALLOC_END - VMALLOC_START) >> 20,
358 		PAGE_OFFSET, PAGE_OFFSET +
359 		(max_low_pfn - min_low_pfn) * PAGE_SIZE,
360 #else
361 		min_low_pfn * PAGE_SIZE, max_low_pfn * PAGE_SIZE,
362 #endif
363 		((max_low_pfn - min_low_pfn) * PAGE_SIZE) >> 20);
364 }
365 
366 #ifdef CONFIG_BLK_DEV_INITRD
367 extern int initrd_is_mapped;
368 
369 void free_initrd_mem(unsigned long start, unsigned long end)
370 {
371 	if (initrd_is_mapped)
372 		free_reserved_area((void *)start, (void *)end, -1, "initrd");
373 }
374 #endif
375 
376 void free_initmem(void)
377 {
378 	free_initmem_default(-1);
379 }
380 
381 static void __init parse_memmap_one(char *p)
382 {
383 	char *oldp;
384 	unsigned long start_at, mem_size;
385 
386 	if (!p)
387 		return;
388 
389 	oldp = p;
390 	mem_size = memparse(p, &p);
391 	if (p == oldp)
392 		return;
393 
394 	switch (*p) {
395 	case '@':
396 		start_at = memparse(p + 1, &p);
397 		add_sysmem_bank(start_at, start_at + mem_size);
398 		break;
399 
400 	case '$':
401 		start_at = memparse(p + 1, &p);
402 		mem_reserve(start_at, start_at + mem_size, 0);
403 		break;
404 
405 	case 0:
406 		mem_reserve(mem_size, 0, 0);
407 		break;
408 
409 	default:
410 		pr_warn("Unrecognized memmap syntax: %s\n", p);
411 		break;
412 	}
413 }
414 
415 static int __init parse_memmap_opt(char *str)
416 {
417 	while (str) {
418 		char *k = strchr(str, ',');
419 
420 		if (k)
421 			*k++ = 0;
422 
423 		parse_memmap_one(str);
424 		str = k;
425 	}
426 
427 	return 0;
428 }
429 early_param("memmap", parse_memmap_opt);
430