Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
88278ca2 | 2 | /* |
1da177e4 LT |
3 | * linux/arch/sparc/mm/init.c |
4 | * | |
5 | * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) | |
6 | * Copyright (C) 1995 Eddie C. Dost (ecd@skynet.be) | |
7 | * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) | |
8 | * Copyright (C) 2000 Anton Blanchard (anton@samba.org) | |
9 | */ | |
10 | ||
1da177e4 LT |
11 | #include <linux/module.h> |
12 | #include <linux/signal.h> | |
13 | #include <linux/sched.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/errno.h> | |
16 | #include <linux/string.h> | |
17 | #include <linux/types.h> | |
18 | #include <linux/ptrace.h> | |
19 | #include <linux/mman.h> | |
20 | #include <linux/mm.h> | |
21 | #include <linux/swap.h> | |
22 | #include <linux/initrd.h> | |
23 | #include <linux/init.h> | |
24 | #include <linux/highmem.h> | |
cca079ef | 25 | #include <linux/memblock.h> |
f538f3df | 26 | #include <linux/pagemap.h> |
1aa0365f | 27 | #include <linux/poison.h> |
5a0e3ad6 | 28 | #include <linux/gfp.h> |
1da177e4 | 29 | |
86ed40bd | 30 | #include <asm/sections.h> |
1da177e4 | 31 | #include <asm/page.h> |
1da177e4 | 32 | #include <asm/vaddrs.h> |
d191723f | 33 | #include <asm/setup.h> |
1da177e4 | 34 | #include <asm/tlb.h> |
942a6bdd | 35 | #include <asm/prom.h> |
0fd7ef1f | 36 | #include <asm/leon.h> |
1da177e4 | 37 | |
e8c29c83 SR |
38 | #include "mm_32.h" |
39 | ||
e025ab84 | 40 | static unsigned long *sparc_valid_addr_bitmap; |
1da177e4 LT |
41 | |
42 | unsigned long phys_base; | |
6943f3da SR |
43 | EXPORT_SYMBOL(phys_base); |
44 | ||
1da177e4 | 45 | unsigned long pfn_base; |
6943f3da | 46 | EXPORT_SYMBOL(pfn_base); |
1da177e4 | 47 | |
1da177e4 | 48 | struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS+1]; |
1da177e4 | 49 | |
1da177e4 LT |
50 | /* Initial ramdisk setup */ |
51 | extern unsigned int sparc_ramdisk_image; | |
52 | extern unsigned int sparc_ramdisk_size; | |
53 | ||
54 | unsigned long highstart_pfn, highend_pfn; | |
55 | ||
1da177e4 LT |
56 | unsigned long last_valid_pfn; |
57 | ||
58 | unsigned long calc_highpages(void) | |
59 | { | |
60 | int i; | |
61 | int nr = 0; | |
62 | ||
63 | for (i = 0; sp_banks[i].num_bytes != 0; i++) { | |
64 | unsigned long start_pfn = sp_banks[i].base_addr >> PAGE_SHIFT; | |
65 | unsigned long end_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT; | |
66 | ||
67 | if (end_pfn <= max_low_pfn) | |
68 | continue; | |
69 | ||
70 | if (start_pfn < max_low_pfn) | |
71 | start_pfn = max_low_pfn; | |
72 | ||
73 | nr += end_pfn - start_pfn; | |
74 | } | |
75 | ||
76 | return nr; | |
77 | } | |
78 | ||
50215d65 | 79 | static unsigned long calc_max_low_pfn(void) |
1da177e4 LT |
80 | { |
81 | int i; | |
82 | unsigned long tmp = pfn_base + (SRMMU_MAXMEM >> PAGE_SHIFT); | |
83 | unsigned long curr_pfn, last_pfn; | |
84 | ||
85 | last_pfn = (sp_banks[0].base_addr + sp_banks[0].num_bytes) >> PAGE_SHIFT; | |
86 | for (i = 1; sp_banks[i].num_bytes != 0; i++) { | |
87 | curr_pfn = sp_banks[i].base_addr >> PAGE_SHIFT; | |
88 | ||
89 | if (curr_pfn >= tmp) { | |
90 | if (last_pfn < tmp) | |
91 | tmp = last_pfn; | |
92 | break; | |
93 | } | |
94 | ||
95 | last_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT; | |
96 | } | |
97 | ||
98 | return tmp; | |
99 | } | |
100 | ||
8818d89c MR |
101 | static void __init find_ramdisk(unsigned long end_of_phys_memory) |
102 | { | |
103 | #ifdef CONFIG_BLK_DEV_INITRD | |
104 | unsigned long size; | |
105 | ||
106 | /* Now have to check initial ramdisk, so that it won't pass | |
107 | * the end of memory | |
108 | */ | |
109 | if (sparc_ramdisk_image) { | |
110 | if (sparc_ramdisk_image >= (unsigned long)&_end - 2 * PAGE_SIZE) | |
111 | sparc_ramdisk_image -= KERNBASE; | |
112 | initrd_start = sparc_ramdisk_image + phys_base; | |
113 | initrd_end = initrd_start + sparc_ramdisk_size; | |
114 | if (initrd_end > end_of_phys_memory) { | |
115 | printk(KERN_CRIT "initrd extends beyond end of memory " | |
116 | "(0x%016lx > 0x%016lx)\ndisabling initrd\n", | |
117 | initrd_end, end_of_phys_memory); | |
118 | initrd_start = 0; | |
119 | } else { | |
120 | /* Reserve the initrd image area. */ | |
121 | size = initrd_end - initrd_start; | |
122 | memblock_reserve(initrd_start, size); | |
123 | ||
124 | initrd_start = (initrd_start - phys_base) + PAGE_OFFSET; | |
125 | initrd_end = (initrd_end - phys_base) + PAGE_OFFSET; | |
126 | } | |
127 | } | |
128 | #endif | |
129 | } | |
130 | ||
1da177e4 LT |
131 | unsigned long __init bootmem_init(unsigned long *pages_avail) |
132 | { | |
cca079ef MR |
133 | unsigned long start_pfn, bytes_avail, size; |
134 | unsigned long end_of_phys_memory = 0; | |
135 | unsigned long high_pages = 0; | |
1da177e4 LT |
136 | int i; |
137 | ||
cca079ef MR |
138 | memblock_set_bottom_up(true); |
139 | memblock_allow_resize(); | |
140 | ||
1da177e4 LT |
141 | bytes_avail = 0UL; |
142 | for (i = 0; sp_banks[i].num_bytes != 0; i++) { | |
143 | end_of_phys_memory = sp_banks[i].base_addr + | |
144 | sp_banks[i].num_bytes; | |
145 | bytes_avail += sp_banks[i].num_bytes; | |
146 | if (cmdline_memory_size) { | |
147 | if (bytes_avail > cmdline_memory_size) { | |
148 | unsigned long slack = bytes_avail - cmdline_memory_size; | |
149 | ||
150 | bytes_avail -= slack; | |
151 | end_of_phys_memory -= slack; | |
152 | ||
153 | sp_banks[i].num_bytes -= slack; | |
154 | if (sp_banks[i].num_bytes == 0) { | |
155 | sp_banks[i].base_addr = 0xdeadbeef; | |
156 | } else { | |
cca079ef MR |
157 | memblock_add(sp_banks[i].base_addr, |
158 | sp_banks[i].num_bytes); | |
1da177e4 LT |
159 | sp_banks[i+1].num_bytes = 0; |
160 | sp_banks[i+1].base_addr = 0xdeadbeef; | |
161 | } | |
162 | break; | |
163 | } | |
164 | } | |
cca079ef | 165 | memblock_add(sp_banks[i].base_addr, sp_banks[i].num_bytes); |
1da177e4 LT |
166 | } |
167 | ||
168 | /* Start with page aligned address of last symbol in kernel | |
60ef8616 | 169 | * image. |
1da177e4 LT |
170 | */ |
171 | start_pfn = (unsigned long)__pa(PAGE_ALIGN((unsigned long) &_end)); | |
172 | ||
173 | /* Now shift down to get the real physical page frame number. */ | |
174 | start_pfn >>= PAGE_SHIFT; | |
175 | ||
1da177e4 LT |
176 | max_pfn = end_of_phys_memory >> PAGE_SHIFT; |
177 | ||
178 | max_low_pfn = max_pfn; | |
179 | highstart_pfn = highend_pfn = max_pfn; | |
180 | ||
181 | if (max_low_pfn > pfn_base + (SRMMU_MAXMEM >> PAGE_SHIFT)) { | |
182 | highstart_pfn = pfn_base + (SRMMU_MAXMEM >> PAGE_SHIFT); | |
183 | max_low_pfn = calc_max_low_pfn(); | |
cca079ef | 184 | high_pages = calc_highpages(); |
1da177e4 | 185 | printk(KERN_NOTICE "%ldMB HIGHMEM available.\n", |
cca079ef | 186 | high_pages >> (20 - PAGE_SHIFT)); |
1da177e4 LT |
187 | } |
188 | ||
8818d89c | 189 | find_ramdisk(end_of_phys_memory); |
1da177e4 | 190 | |
1da177e4 LT |
191 | /* Reserve the kernel text/data/bss. */ |
192 | size = (start_pfn << PAGE_SHIFT) - phys_base; | |
cca079ef | 193 | memblock_reserve(phys_base, size); |
4360dfa9 | 194 | memblock_add(phys_base, size); |
1da177e4 | 195 | |
cca079ef MR |
196 | size = memblock_phys_mem_size() - memblock_reserved_size(); |
197 | *pages_avail = (size >> PAGE_SHIFT) - high_pages; | |
1da177e4 | 198 | |
bda16693 AL |
199 | /* Only allow low memory to be allocated via memblock allocation */ |
200 | memblock_set_current_limit(max_low_pfn << PAGE_SHIFT); | |
201 | ||
1da177e4 LT |
202 | return max_pfn; |
203 | } | |
204 | ||
1da177e4 LT |
205 | /* |
206 | * paging_init() sets up the page tables: We call the MMU specific | |
207 | * init routine based upon the Sun model type on the Sparc. | |
208 | * | |
209 | */ | |
1da177e4 LT |
210 | void __init paging_init(void) |
211 | { | |
cc52aea9 | 212 | srmmu_paging_init(); |
942a6bdd | 213 | prom_build_devicetree(); |
890db403 | 214 | of_fill_in_cpu_data(); |
1da177e4 LT |
215 | device_scan(); |
216 | } | |
217 | ||
1da177e4 LT |
218 | static void __init taint_real_pages(void) |
219 | { | |
220 | int i; | |
221 | ||
222 | for (i = 0; sp_banks[i].num_bytes; i++) { | |
223 | unsigned long start, end; | |
224 | ||
225 | start = sp_banks[i].base_addr; | |
226 | end = start + sp_banks[i].num_bytes; | |
227 | ||
228 | while (start < end) { | |
229 | set_bit(start >> 20, sparc_valid_addr_bitmap); | |
230 | start += PAGE_SIZE; | |
231 | } | |
232 | } | |
233 | } | |
234 | ||
50215d65 | 235 | static void map_high_region(unsigned long start_pfn, unsigned long end_pfn) |
1da177e4 LT |
236 | { |
237 | unsigned long tmp; | |
238 | ||
239 | #ifdef CONFIG_DEBUG_HIGHMEM | |
240 | printk("mapping high region %08lx - %08lx\n", start_pfn, end_pfn); | |
241 | #endif | |
242 | ||
32e1a109 JL |
243 | for (tmp = start_pfn; tmp < end_pfn; tmp++) |
244 | free_highmem_page(pfn_to_page(tmp)); | |
1da177e4 LT |
245 | } |
246 | ||
247 | void __init mem_init(void) | |
248 | { | |
1da177e4 LT |
249 | int i; |
250 | ||
251 | if (PKMAP_BASE+LAST_PKMAP*PAGE_SIZE >= FIXADDR_START) { | |
252 | prom_printf("BUG: fixmap and pkmap areas overlap\n"); | |
253 | prom_printf("pkbase: 0x%lx pkend: 0x%lx fixstart 0x%lx\n", | |
254 | PKMAP_BASE, | |
255 | (unsigned long)PKMAP_BASE+LAST_PKMAP*PAGE_SIZE, | |
256 | FIXADDR_START); | |
257 | prom_printf("Please mail sparclinux@vger.kernel.org.\n"); | |
258 | prom_halt(); | |
259 | } | |
260 | ||
261 | ||
262 | /* Saves us work later. */ | |
deba804c | 263 | memset((void *)empty_zero_page, 0, PAGE_SIZE); |
1da177e4 LT |
264 | |
265 | i = last_valid_pfn >> ((20 - PAGE_SHIFT) + 5); | |
266 | i += 1; | |
267 | sparc_valid_addr_bitmap = (unsigned long *) | |
9415673e | 268 | memblock_alloc(i << 2, SMP_CACHE_BYTES); |
1da177e4 LT |
269 | |
270 | if (sparc_valid_addr_bitmap == NULL) { | |
271 | prom_printf("mem_init: Cannot alloc valid_addr_bitmap.\n"); | |
272 | prom_halt(); | |
273 | } | |
274 | memset(sparc_valid_addr_bitmap, 0, i << 2); | |
275 | ||
276 | taint_real_pages(); | |
277 | ||
278 | max_mapnr = last_valid_pfn - pfn_base; | |
279 | high_memory = __va(max_low_pfn << PAGE_SHIFT); | |
c6ffc5ca | 280 | memblock_free_all(); |
1da177e4 LT |
281 | |
282 | for (i = 0; sp_banks[i].num_bytes != 0; i++) { | |
283 | unsigned long start_pfn = sp_banks[i].base_addr >> PAGE_SHIFT; | |
284 | unsigned long end_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT; | |
285 | ||
1da177e4 LT |
286 | if (end_pfn <= highstart_pfn) |
287 | continue; | |
288 | ||
289 | if (start_pfn < highstart_pfn) | |
290 | start_pfn = highstart_pfn; | |
291 | ||
292 | map_high_region(start_pfn, end_pfn); | |
293 | } | |
1da177e4 LT |
294 | } |
295 | ||
1da177e4 LT |
296 | void sparc_flush_page_to_ram(struct page *page) |
297 | { | |
298 | unsigned long vaddr = (unsigned long)page_address(page); | |
299 | ||
300 | if (vaddr) | |
301 | __flush_page_to_ram(vaddr); | |
302 | } | |
6943f3da | 303 | EXPORT_SYMBOL(sparc_flush_page_to_ram); |
25740d31 AK |
304 | |
305 | static const pgprot_t protection_map[16] = { | |
306 | [VM_NONE] = PAGE_NONE, | |
307 | [VM_READ] = PAGE_READONLY, | |
308 | [VM_WRITE] = PAGE_COPY, | |
309 | [VM_WRITE | VM_READ] = PAGE_COPY, | |
310 | [VM_EXEC] = PAGE_READONLY, | |
311 | [VM_EXEC | VM_READ] = PAGE_READONLY, | |
312 | [VM_EXEC | VM_WRITE] = PAGE_COPY, | |
313 | [VM_EXEC | VM_WRITE | VM_READ] = PAGE_COPY, | |
314 | [VM_SHARED] = PAGE_NONE, | |
315 | [VM_SHARED | VM_READ] = PAGE_READONLY, | |
316 | [VM_SHARED | VM_WRITE] = PAGE_SHARED, | |
317 | [VM_SHARED | VM_WRITE | VM_READ] = PAGE_SHARED, | |
318 | [VM_SHARED | VM_EXEC] = PAGE_READONLY, | |
319 | [VM_SHARED | VM_EXEC | VM_READ] = PAGE_READONLY, | |
320 | [VM_SHARED | VM_EXEC | VM_WRITE] = PAGE_SHARED, | |
321 | [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = PAGE_SHARED | |
322 | }; | |
323 | DECLARE_VM_GET_PAGE_PROT |