init_64.c 9.12 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
 *  PowerPC version
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 *
 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 *    Copyright (C) 1996 Paul Mackerras
 *
 *  Derived from "arch/i386/mm/init.c"
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 *
 *  Dave Engebretsen <engebret@us.ibm.com>
 *      Rework for PPC64 port.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version
 *  2 of the License, or (at your option) any later version.
 *
 */

22
23
#undef DEBUG

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/stddef.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/highmem.h>
#include <linux/idr.h>
#include <linux/nodemask.h>
#include <linux/module.h>
41
#include <linux/poison.h>
Yinghai Lu's avatar
Yinghai Lu committed
42
#include <linux/memblock.h>
43
#include <linux/hugetlb.h>
44
#include <linux/slab.h>
45
46
47
48
49
50
51
52
53

#include <asm/pgalloc.h>
#include <asm/page.h>
#include <asm/prom.h>
#include <asm/rtas.h>
#include <asm/io.h>
#include <asm/mmu_context.h>
#include <asm/pgtable.h>
#include <asm/mmu.h>
54
#include <linux/uaccess.h>
55
56
57
58
59
60
61
62
63
64
#include <asm/smp.h>
#include <asm/machdep.h>
#include <asm/tlb.h>
#include <asm/eeh.h>
#include <asm/processor.h>
#include <asm/mmzone.h>
#include <asm/cputable.h>
#include <asm/sections.h>
#include <asm/iommu.h>
#include <asm/vdso.h>
65
66

#include "mmu_decl.h"
67

68
#ifdef CONFIG_PPC_STD_MMU_64
69
#if H_PGTABLE_RANGE > USER_VSID_RANGE
70
71
72
#warning Limited user VSID range means pagetable space is wasted
#endif

73
#if (TASK_SIZE_USER64 < H_PGTABLE_RANGE) && (TASK_SIZE_USER64 < USER_VSID_RANGE)
74
75
#warning TASK_SIZE is smaller than it needs to be.
#endif
76
#endif /* CONFIG_PPC_STD_MMU_64 */
77

78
phys_addr_t memstart_addr = ~0;
79
EXPORT_SYMBOL_GPL(memstart_addr);
80
phys_addr_t kernstart_addr;
81
EXPORT_SYMBOL_GPL(kernstart_addr);
82

83
84
85
86
87
88
89
#ifdef CONFIG_SPARSEMEM_VMEMMAP
/*
 * Given an address within the vmemmap, determine the pfn of the page that
 * represents the start of the section it is within.  Note that we have to
 * do this by hand as the proffered address may not be correctly aligned.
 * Subtraction of non-aligned pointers produces undefined results.
 */
90
static unsigned long __meminit vmemmap_section_start(unsigned long page)
91
92
93
94
95
96
97
98
99
100
101
102
{
	unsigned long offset = page - ((unsigned long)(vmemmap));

	/* Return the pfn of the start of the section. */
	return (offset / sizeof(struct page)) & PAGE_SECTION_MASK;
}

/*
 * Check if this vmemmap page is already initialised.  If any section
 * which overlaps this vmemmap page is initialised then this page is
 * initialised already.
 */
103
static int __meminit vmemmap_populated(unsigned long start, int page_size)
104
105
{
	unsigned long end = start + page_size;
106
	start = (unsigned long)(pfn_to_page(vmemmap_section_start(start)));
107
108

	for (; start < end; start += (PAGES_PER_SECTION * sizeof(struct page)))
109
		if (pfn_valid(page_to_pfn((struct page *)start)))
110
111
112
113
114
			return 1;

	return 0;
}

115
struct vmemmap_backing *vmemmap_list;
116
117
118
static struct vmemmap_backing *next;
static int num_left;
static int num_freed;
119
120
121

static __meminit struct vmemmap_backing * vmemmap_list_alloc(int node)
{
122
123
124
125
126
127
128
129
130
	struct vmemmap_backing *vmem_back;
	/* get from freed entries first */
	if (num_freed) {
		num_freed--;
		vmem_back = next;
		next = next->list;

		return vmem_back;
	}
131
132

	/* allocate a page when required and hand out chunks */
133
	if (!num_left) {
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
		next = vmemmap_alloc_block(PAGE_SIZE, node);
		if (unlikely(!next)) {
			WARN_ON(1);
			return NULL;
		}
		num_left = PAGE_SIZE / sizeof(struct vmemmap_backing);
	}

	num_left--;

	return next++;
}

static __meminit void vmemmap_list_populate(unsigned long phys,
					    unsigned long start,
					    int node)
{
	struct vmemmap_backing *vmem_back;

	vmem_back = vmemmap_list_alloc(node);
	if (unlikely(!vmem_back)) {
		WARN_ON(1);
		return;
	}

	vmem_back->phys = phys;
	vmem_back->virt_addr = start;
	vmem_back->list = vmemmap_list;

	vmemmap_list = vmem_back;
}

Li Zhong's avatar
Li Zhong committed
166
167
168
169
170
171
172
173
174
175
176
int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node)
{
	unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift;

	/* Align to the page size of the linear mapping. */
	start = _ALIGN_DOWN(start, page_size);

	pr_debug("vmemmap_populate %lx..%lx, node %d\n", start, end, node);

	for (; start < end; start += page_size) {
		void *p;
177
		int rc;
Li Zhong's avatar
Li Zhong committed
178
179
180
181
182
183
184
185
186
187
188
189
190

		if (vmemmap_populated(start, page_size))
			continue;

		p = vmemmap_alloc_block(page_size, node);
		if (!p)
			return -ENOMEM;

		vmemmap_list_populate(__pa(p), start, node);

		pr_debug("      * %016lx..%016lx allocated at %p\n",
			 start, start + page_size, p);

191
192
193
194
195
196
197
		rc = vmemmap_create_mapping(start, page_size, __pa(p));
		if (rc < 0) {
			pr_warning(
				"vmemmap_populate: Unable to create vmemmap mapping: %d\n",
				rc);
			return -EFAULT;
		}
Li Zhong's avatar
Li Zhong committed
198
199
200
201
202
203
	}

	return 0;
}

#ifdef CONFIG_MEMORY_HOTPLUG
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
static unsigned long vmemmap_list_free(unsigned long start)
{
	struct vmemmap_backing *vmem_back, *vmem_back_prev;

	vmem_back_prev = vmem_back = vmemmap_list;

	/* look for it with prev pointer recorded */
	for (; vmem_back; vmem_back = vmem_back->list) {
		if (vmem_back->virt_addr == start)
			break;
		vmem_back_prev = vmem_back;
	}

	if (unlikely(!vmem_back)) {
		WARN_ON(1);
		return 0;
	}

	/* remove it from vmemmap_list */
	if (vmem_back == vmemmap_list) /* remove head */
		vmemmap_list = vmem_back->list;
	else
		vmem_back_prev->list = vmem_back->list;

	/* next point to this freed entry */
	vmem_back->list = next;
	next = vmem_back;
	num_freed++;

	return vmem_back->phys;
}

Li Zhong's avatar
Li Zhong committed
236
void __ref vmemmap_free(unsigned long start, unsigned long end)
237
{
238
	unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift;
239
240
241

	start = _ALIGN_DOWN(start, page_size);

Li Zhong's avatar
Li Zhong committed
242
	pr_debug("vmemmap_free %lx...%lx\n", start, end);
243

244
	for (; start < end; start += page_size) {
Li Zhong's avatar
Li Zhong committed
245
		unsigned long addr;
246

Li Zhong's avatar
Li Zhong committed
247
248
249
250
251
		/*
		 * the section has already be marked as invalid, so
		 * vmemmap_populated() true means some other sections still
		 * in this page, so skip it.
		 */
252
253
254
		if (vmemmap_populated(start, page_size))
			continue;

Li Zhong's avatar
Li Zhong committed
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
		addr = vmemmap_list_free(start);
		if (addr) {
			struct page *page = pfn_to_page(addr >> PAGE_SHIFT);

			if (PageReserved(page)) {
				/* allocated from bootmem */
				if (page_size < PAGE_SIZE) {
					/*
					 * this shouldn't happen, but if it is
					 * the case, leave the memory there
					 */
					WARN_ON_ONCE(1);
				} else {
					unsigned int nr_pages =
						1 << get_order(page_size);
					while (nr_pages--)
						free_reserved_page(page++);
				}
			} else
				free_pages((unsigned long)(__va(addr)),
							get_order(page_size));

			vmemmap_remove_mapping(start, page_size);
		}
279
	}
280
}
Li Zhong's avatar
Li Zhong committed
281
#endif
282
283
284
285
void register_page_bootmem_memmap(unsigned long section_nr,
				  struct page *start_page, unsigned long size)
{
}
286

287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/*
 * We do not have access to the sparsemem vmemmap, so we fallback to
 * walking the list of sparsemem blocks which we already maintain for
 * the sake of crashdump. In the long run, we might want to maintain
 * a tree if performance of that linear walk becomes a problem.
 *
 * realmode_pfn_to_page functions can fail due to:
 * 1) As real sparsemem blocks do not lay in RAM continously (they
 * are in virtual address space which is not available in the real mode),
 * the requested page struct can be split between blocks so get_page/put_page
 * may fail.
 * 2) When huge pages are used, the get_page/put_page API will fail
 * in real mode as the linked addresses in the page struct are virtual
 * too.
 */
struct page *realmode_pfn_to_page(unsigned long pfn)
{
	struct vmemmap_backing *vmem_back;
	struct page *page;
	unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift;
	unsigned long pg_va = (unsigned long) pfn_to_page(pfn);

	for (vmem_back = vmemmap_list; vmem_back; vmem_back = vmem_back->list) {
		if (pg_va < vmem_back->virt_addr)
			continue;

313
314
315
316
		/* After vmemmap_list entry free is possible, need check all */
		if ((pg_va + sizeof(struct page)) <=
				(vmem_back->virt_addr + page_size)) {
			page = (struct page *) (vmem_back->phys + pg_va -
317
				vmem_back->virt_addr);
318
319
			return page;
		}
320
321
	}

322
	/* Probably that page struct is split between real pages */
323
324
325
326
327
328
329
330
331
332
333
334
335
336
	return NULL;
}
EXPORT_SYMBOL_GPL(realmode_pfn_to_page);

#elif defined(CONFIG_FLATMEM)

struct page *realmode_pfn_to_page(unsigned long pfn)
{
	struct page *page = pfn_to_page(pfn);
	return page;
}
EXPORT_SYMBOL_GPL(realmode_pfn_to_page);

#endif /* CONFIG_SPARSEMEM_VMEMMAP/CONFIG_FLATMEM */
337
338

#ifdef CONFIG_PPC_STD_MMU_64
339
340
341
342
343
344
345
346
static bool disable_radix;
static int __init parse_disable_radix(char *p)
{
	disable_radix = true;
	return 0;
}
early_param("disable_radix", parse_disable_radix);

347
348
void __init mmu_early_init_devtree(void)
{
349
	/* Disable radix mode based on kernel command line. */
350
351
	/* We don't yet have the machinery to do radix as a guest. */
	if (disable_radix || !(mfmsr() & MSR_HV))
352
		cur_cpu_spec->mmu_features &= ~MMU_FTR_TYPE_RADIX;
353

354
	if (early_radix_enabled())
355
356
		radix__early_init_devtree();
	else
357
		hash__early_init_devtree();
358
359
}
#endif /* CONFIG_PPC_STD_MMU_64 */