]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1da177e4 LT |
2 | /* |
3 | * linux/arch/arm/mm/init.c | |
4 | * | |
90072059 | 5 | * Copyright (C) 1995-2005 Russell King |
1da177e4 | 6 | */ |
1da177e4 LT |
7 | #include <linux/kernel.h> |
8 | #include <linux/errno.h> | |
1da177e4 LT |
9 | #include <linux/swap.h> |
10 | #include <linux/init.h> | |
1da177e4 | 11 | #include <linux/mman.h> |
3f07c014 | 12 | #include <linux/sched/signal.h> |
29930025 | 13 | #include <linux/sched/task.h> |
dc28094b | 14 | #include <linux/export.h> |
1da177e4 LT |
15 | #include <linux/nodemask.h> |
16 | #include <linux/initrd.h> | |
9eb8f674 | 17 | #include <linux/of_fdt.h> |
3835f6cb | 18 | #include <linux/highmem.h> |
5a0e3ad6 | 19 | #include <linux/gfp.h> |
2778f620 | 20 | #include <linux/memblock.h> |
0b1abd1f | 21 | #include <linux/dma-map-ops.h> |
158e8bfe | 22 | #include <linux/sizes.h> |
08925c2f | 23 | #include <linux/stop_machine.h> |
ad3c7b18 | 24 | #include <linux/swiotlb.h> |
1da177e4 | 25 | |
b4b20ad8 | 26 | #include <asm/cp15.h> |
1da177e4 | 27 | #include <asm/mach-types.h> |
716a3dc2 | 28 | #include <asm/memblock.h> |
d2ca5f24 | 29 | #include <asm/memory.h> |
93c02ab4 | 30 | #include <asm/prom.h> |
37efe642 | 31 | #include <asm/sections.h> |
1da177e4 | 32 | #include <asm/setup.h> |
9110f3e7 | 33 | #include <asm/set_memory.h> |
1e6b4811 | 34 | #include <asm/system_info.h> |
1da177e4 | 35 | #include <asm/tlb.h> |
db9ef1af | 36 | #include <asm/fixmap.h> |
a8e53c15 | 37 | #include <asm/ptdump.h> |
1da177e4 LT |
38 | |
39 | #include <asm/mach/arch.h> | |
40 | #include <asm/mach/map.h> | |
41 | ||
1b2e2b73 RK |
42 | #include "mm.h" |
43 | ||
b4b20ad8 RK |
44 | #ifdef CONFIG_CPU_CP15_MMU |
45 | unsigned long __init __clear_cr(unsigned long mask) | |
46 | { | |
b4b20ad8 RK |
47 | cr_alignment = cr_alignment & ~mask; |
48 | return cr_alignment; | |
49 | } | |
50 | #endif | |
51 | ||
b1ab95c6 | 52 | #ifdef CONFIG_BLK_DEV_INITRD |
012d1f4a RK |
53 | static int __init parse_tag_initrd(const struct tag *tag) |
54 | { | |
4ed89f22 | 55 | pr_warn("ATAG_INITRD is deprecated; " |
012d1f4a RK |
56 | "please update your bootloader.\n"); |
57 | phys_initrd_start = __virt_to_phys(tag->u.initrd.start); | |
58 | phys_initrd_size = tag->u.initrd.size; | |
59 | return 0; | |
60 | } | |
61 | ||
62 | __tagtable(ATAG_INITRD, parse_tag_initrd); | |
63 | ||
64 | static int __init parse_tag_initrd2(const struct tag *tag) | |
65 | { | |
66 | phys_initrd_start = tag->u.initrd.start; | |
67 | phys_initrd_size = tag->u.initrd.size; | |
68 | return 0; | |
69 | } | |
70 | ||
71 | __tagtable(ATAG_INITRD2, parse_tag_initrd2); | |
b1ab95c6 | 72 | #endif |
1da177e4 | 73 | |
f25b4b4c | 74 | static void __init find_limits(unsigned long *min, unsigned long *max_low, |
27a3f0e9 | 75 | unsigned long *max_high) |
dde5828f | 76 | { |
1c2f87c2 LA |
77 | *max_low = PFN_DOWN(memblock_get_current_limit()); |
78 | *min = PFN_UP(memblock_start_of_DRAM()); | |
79 | *max_high = PFN_DOWN(memblock_end_of_DRAM()); | |
dde5828f RK |
80 | } |
81 | ||
be20902b | 82 | #ifdef CONFIG_ZONE_DMA |
65032018 | 83 | |
364230b9 | 84 | phys_addr_t arm_dma_zone_size __read_mostly; |
65032018 NP |
85 | EXPORT_SYMBOL(arm_dma_zone_size); |
86 | ||
022ae537 RK |
87 | /* |
88 | * The DMA mask corresponding to the maximum bus address allocatable | |
89 | * using GFP_DMA. The default here places no restriction on DMA | |
90 | * allocations. This must be the smallest DMA mask in the system, | |
91 | * so a successful GFP_DMA allocation will always satisfy this. | |
92 | */ | |
4986e5c7 | 93 | phys_addr_t arm_dma_limit; |
4dcfa600 | 94 | unsigned long arm_dma_pfn_limit; |
be20902b RK |
95 | #endif |
96 | ||
ff69a4c8 | 97 | void __init setup_dma_zone(const struct machine_desc *mdesc) |
c7909509 MS |
98 | { |
99 | #ifdef CONFIG_ZONE_DMA | |
100 | if (mdesc->dma_zone_size) { | |
101 | arm_dma_zone_size = mdesc->dma_zone_size; | |
6bcac805 | 102 | arm_dma_limit = PHYS_OFFSET + arm_dma_zone_size - 1; |
c7909509 MS |
103 | } else |
104 | arm_dma_limit = 0xffffffff; | |
4dcfa600 | 105 | arm_dma_pfn_limit = arm_dma_limit >> PAGE_SHIFT; |
c7909509 MS |
106 | #endif |
107 | } | |
108 | ||
84f452b1 | 109 | static void __init zone_sizes_init(unsigned long min, unsigned long max_low, |
a2c54d2a | 110 | unsigned long max_high) |
b7a69ac3 | 111 | { |
a32c1c61 | 112 | unsigned long max_zone_pfn[MAX_NR_ZONES] = { 0 }; |
b7a69ac3 | 113 | |
a32c1c61 MR |
114 | #ifdef CONFIG_ZONE_DMA |
115 | max_zone_pfn[ZONE_DMA] = min(arm_dma_pfn_limit, max_low); | |
dde5828f | 116 | #endif |
a32c1c61 | 117 | max_zone_pfn[ZONE_NORMAL] = max_low; |
dde5828f | 118 | #ifdef CONFIG_HIGHMEM |
a32c1c61 | 119 | max_zone_pfn[ZONE_HIGHMEM] = max_high; |
dde5828f | 120 | #endif |
a32c1c61 | 121 | free_area_init(max_zone_pfn); |
1da177e4 LT |
122 | } |
123 | ||
7b7bf499 | 124 | #ifdef CONFIG_HAVE_ARCH_PFN_VALID |
b7cfda9f RK |
125 | int pfn_valid(unsigned long pfn) |
126 | { | |
5b3efa4f | 127 | phys_addr_t addr = __pfn_to_phys(pfn); |
a4d5613c | 128 | unsigned long pageblock_size = PAGE_SIZE * pageblock_nr_pages; |
5b3efa4f | 129 | |
130 | if (__phys_to_pfn(addr) != pfn) | |
131 | return 0; | |
132 | ||
a4d5613c MR |
133 | /* |
134 | * If address less than pageblock_size bytes away from a present | |
135 | * memory chunk there still will be a memory map entry for it | |
136 | * because we round freed memory map to the pageblock boundaries. | |
137 | */ | |
138 | if (memblock_overlaps_region(&memblock.memory, | |
139 | ALIGN_DOWN(addr, pageblock_size), | |
140 | pageblock_size)) | |
141 | return 1; | |
142 | ||
143 | return 0; | |
b7cfda9f RK |
144 | } |
145 | EXPORT_SYMBOL(pfn_valid); | |
7b7bf499 | 146 | #endif |
657e12fd | 147 | |
716a3dc2 RK |
148 | static bool arm_memblock_steal_permitted = true; |
149 | ||
bc2827d0 | 150 | phys_addr_t __init arm_memblock_steal(phys_addr_t size, phys_addr_t align) |
716a3dc2 RK |
151 | { |
152 | phys_addr_t phys; | |
153 | ||
154 | BUG_ON(!arm_memblock_steal_permitted); | |
155 | ||
f240ec09 | 156 | phys = memblock_phys_alloc(size, align); |
ecc3e771 MR |
157 | if (!phys) |
158 | panic("Failed to steal %pa bytes at %pS\n", | |
159 | &size, (void *)_RET_IP_); | |
160 | ||
716a3dc2 RK |
161 | memblock_free(phys, size); |
162 | memblock_remove(phys, size); | |
163 | ||
164 | return phys; | |
165 | } | |
166 | ||
39286248 | 167 | static void __init arm_initrd_init(void) |
2778f620 | 168 | { |
2778f620 | 169 | #ifdef CONFIG_BLK_DEV_INITRD |
cdcc5fa0 RK |
170 | phys_addr_t start; |
171 | unsigned long size; | |
172 | ||
4c235cb9 | 173 | initrd_start = initrd_end = 0; |
68b32f36 RK |
174 | |
175 | if (!phys_initrd_size) | |
176 | return; | |
177 | ||
cdcc5fa0 RK |
178 | /* |
179 | * Round the memory region to page boundaries as per free_initrd_mem() | |
180 | * This allows us to detect whether the pages overlapping the initrd | |
181 | * are in use, but more importantly, reserves the entire set of pages | |
182 | * as we don't want these pages allocated for other purposes. | |
183 | */ | |
184 | start = round_down(phys_initrd_start, PAGE_SIZE); | |
185 | size = phys_initrd_size + (phys_initrd_start - start); | |
186 | size = round_up(size, PAGE_SIZE); | |
187 | ||
188 | if (!memblock_is_region_memory(start, size)) { | |
de22cc6e | 189 | pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region - disabling initrd\n", |
cdcc5fa0 | 190 | (u64)start, size); |
68b32f36 | 191 | return; |
8f4b8c76 | 192 | } |
68b32f36 | 193 | |
cdcc5fa0 | 194 | if (memblock_is_region_reserved(start, size)) { |
de22cc6e | 195 | pr_err("INITRD: 0x%08llx+0x%08lx overlaps in-use memory region - disabling initrd\n", |
cdcc5fa0 | 196 | (u64)start, size); |
68b32f36 | 197 | return; |
b0a2679d | 198 | } |
2778f620 | 199 | |
cdcc5fa0 | 200 | memblock_reserve(start, size); |
68b32f36 RK |
201 | |
202 | /* Now convert initrd to virtual addresses */ | |
203 | initrd_start = __phys_to_virt(phys_initrd_start); | |
204 | initrd_end = initrd_start + phys_initrd_size; | |
2778f620 | 205 | #endif |
39286248 RK |
206 | } |
207 | ||
5f41f919 MS |
208 | #ifdef CONFIG_CPU_ICACHE_MISMATCH_WORKAROUND |
209 | void check_cpu_icache_size(int cpuid) | |
210 | { | |
211 | u32 size, ctr; | |
212 | ||
213 | asm("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr)); | |
214 | ||
215 | size = 1 << ((ctr & 0xf) + 2); | |
216 | if (cpuid != 0 && icache_size != size) | |
217 | pr_info("CPU%u: detected I-Cache line size mismatch, workaround enabled\n", | |
218 | cpuid); | |
219 | if (icache_size > size) | |
220 | icache_size = size; | |
221 | } | |
222 | #endif | |
223 | ||
39286248 RK |
224 | void __init arm_memblock_init(const struct machine_desc *mdesc) |
225 | { | |
226 | /* Register the kernel text, kernel data and initrd with memblock. */ | |
227 | memblock_reserve(__pa(KERNEL_START), KERNEL_END - KERNEL_START); | |
228 | ||
229 | arm_initrd_init(); | |
2778f620 RK |
230 | |
231 | arm_mm_memblock_reserve(); | |
232 | ||
8d717a52 RK |
233 | /* reserve any platform specific memblock areas */ |
234 | if (mdesc->reserve) | |
235 | mdesc->reserve(); | |
236 | ||
bcedb5f9 MS |
237 | early_init_fdt_scan_reserved_mem(); |
238 | ||
99a468d7 | 239 | /* reserve memory for DMA contiguous allocations */ |
95b0e655 | 240 | dma_contiguous_reserve(arm_dma_limit); |
c7909509 | 241 | |
716a3dc2 | 242 | arm_memblock_steal_permitted = false; |
2778f620 RK |
243 | memblock_dump_all(); |
244 | } | |
245 | ||
8d717a52 | 246 | void __init bootmem_init(void) |
1da177e4 | 247 | { |
8e58caef | 248 | memblock_allow_resize(); |
dde5828f | 249 | |
071d184a | 250 | find_limits(&min_low_pfn, &max_low_pfn, &max_pfn); |
dde5828f | 251 | |
071d184a DB |
252 | early_memtest((phys_addr_t)min_low_pfn << PAGE_SHIFT, |
253 | (phys_addr_t)max_low_pfn << PAGE_SHIFT); | |
d30eae47 | 254 | |
be370302 | 255 | /* |
c89ab04f MR |
256 | * sparse_init() tries to allocate memory from memblock, so must be |
257 | * done after the fixed reservations | |
b7a69ac3 RK |
258 | */ |
259 | sparse_init(); | |
260 | ||
261 | /* | |
a32c1c61 | 262 | * Now free the memory - free_area_init needs |
b7a69ac3 RK |
263 | * the sparse mem_map arrays initialized by sparse_init() |
264 | * for memmap_init_zone(), otherwise all PFNs are invalid. | |
265 | */ | |
071d184a | 266 | zone_sizes_init(min_low_pfn, max_low_pfn, max_pfn); |
90072059 | 267 | } |
1da177e4 | 268 | |
54d52573 SB |
269 | /* |
270 | * Poison init memory with an undefined instruction (ARM) or a branch to an | |
271 | * undefined instruction (Thumb). | |
272 | */ | |
273 | static inline void poison_init_mem(void *s, size_t count) | |
274 | { | |
275 | u32 *p = (u32 *)s; | |
bf912d99 | 276 | for (; count != 0; count -= 4) |
54d52573 SB |
277 | *p++ = 0xe7fddef0; |
278 | } | |
279 | ||
d0e775af RK |
280 | static void __init free_highpages(void) |
281 | { | |
282 | #ifdef CONFIG_HIGHMEM | |
26ba47b1 | 283 | unsigned long max_low = max_low_pfn; |
cddb5ddf MR |
284 | phys_addr_t range_start, range_end; |
285 | u64 i; | |
d0e775af RK |
286 | |
287 | /* set highmem page free */ | |
cddb5ddf MR |
288 | for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, |
289 | &range_start, &range_end, NULL) { | |
b9bc3670 AB |
290 | unsigned long start = PFN_UP(range_start); |
291 | unsigned long end = PFN_DOWN(range_end); | |
df4f14c7 RK |
292 | |
293 | /* Ignore complete lowmem entries */ | |
294 | if (end <= max_low) | |
295 | continue; | |
296 | ||
297 | /* Truncate partial highmem entries */ | |
298 | if (start < max_low) | |
299 | start = max_low; | |
300 | ||
cddb5ddf MR |
301 | for (; start < end; start++) |
302 | free_highmem_page(pfn_to_page(start)); | |
d0e775af | 303 | } |
d0e775af RK |
304 | #endif |
305 | } | |
306 | ||
1da177e4 LT |
307 | /* |
308 | * mem_init() marks the free areas in the mem_map and tells us how much | |
309 | * memory is free. This is done after various parts of the system have | |
310 | * claimed their memory after the kernel image. | |
311 | */ | |
312 | void __init mem_init(void) | |
313 | { | |
ad3c7b18 | 314 | #ifdef CONFIG_ARM_LPAE |
fcf04489 FF |
315 | if (swiotlb_force == SWIOTLB_FORCE || |
316 | max_pfn > arm_dma_pfn_limit) | |
317 | swiotlb_init(1); | |
318 | else | |
319 | swiotlb_force = SWIOTLB_NO_FORCE; | |
ad3c7b18 CH |
320 | #endif |
321 | ||
b3ba41f2 | 322 | set_max_mapnr(pfn_to_page(max_pfn) - mem_map); |
1da177e4 | 323 | |
1da177e4 | 324 | /* this will put all unused low memory onto the freelists */ |
c6ffc5ca | 325 | memblock_free_all(); |
1da177e4 LT |
326 | |
327 | #ifdef CONFIG_SA1111 | |
328 | /* now that our DMA memory is actually so designated, we can free it */ | |
bfd65dd9 | 329 | free_reserved_area(__va(PHYS_OFFSET), swapper_pg_dir, -1, NULL); |
1da177e4 LT |
330 | #endif |
331 | ||
d0e775af | 332 | free_highpages(); |
3835f6cb | 333 | |
a1839272 FB |
334 | /* |
335 | * Check boundaries twice: Some fundamental inconsistencies can | |
336 | * be detected at build time already. | |
337 | */ | |
338 | #ifdef CONFIG_MMU | |
a1839272 FB |
339 | BUILD_BUG_ON(TASK_SIZE > MODULES_VADDR); |
340 | BUG_ON(TASK_SIZE > MODULES_VADDR); | |
341 | #endif | |
342 | ||
343 | #ifdef CONFIG_HIGHMEM | |
344 | BUILD_BUG_ON(PKMAP_BASE + LAST_PKMAP * PAGE_SIZE > PAGE_OFFSET); | |
345 | BUG_ON(PKMAP_BASE + LAST_PKMAP * PAGE_SIZE > PAGE_OFFSET); | |
346 | #endif | |
1da177e4 LT |
347 | } |
348 | ||
0f5bf6d0 | 349 | #ifdef CONFIG_STRICT_KERNEL_RWX |
1e6b4811 | 350 | struct section_perm { |
25362dc4 | 351 | const char *name; |
1e6b4811 KC |
352 | unsigned long start; |
353 | unsigned long end; | |
354 | pmdval_t mask; | |
355 | pmdval_t prot; | |
80d6b0c2 | 356 | pmdval_t clear; |
1e6b4811 KC |
357 | }; |
358 | ||
64ac2e74 KC |
359 | /* First section-aligned location at or after __start_rodata. */ |
360 | extern char __start_rodata_section_aligned[]; | |
361 | ||
80d6b0c2 | 362 | static struct section_perm nx_perms[] = { |
1e6b4811 KC |
363 | /* Make pages tables, etc before _stext RW (set NX). */ |
364 | { | |
25362dc4 | 365 | .name = "pre-text NX", |
1e6b4811 KC |
366 | .start = PAGE_OFFSET, |
367 | .end = (unsigned long)_stext, | |
368 | .mask = ~PMD_SECT_XN, | |
369 | .prot = PMD_SECT_XN, | |
370 | }, | |
371 | /* Make init RW (set NX). */ | |
372 | { | |
25362dc4 | 373 | .name = "init NX", |
1e6b4811 KC |
374 | .start = (unsigned long)__init_begin, |
375 | .end = (unsigned long)_sdata, | |
376 | .mask = ~PMD_SECT_XN, | |
377 | .prot = PMD_SECT_XN, | |
378 | }, | |
80d6b0c2 KC |
379 | /* Make rodata NX (set RO in ro_perms below). */ |
380 | { | |
25362dc4 | 381 | .name = "rodata NX", |
64ac2e74 | 382 | .start = (unsigned long)__start_rodata_section_aligned, |
80d6b0c2 KC |
383 | .end = (unsigned long)__init_begin, |
384 | .mask = ~PMD_SECT_XN, | |
385 | .prot = PMD_SECT_XN, | |
386 | }, | |
1e6b4811 KC |
387 | }; |
388 | ||
80d6b0c2 KC |
389 | static struct section_perm ro_perms[] = { |
390 | /* Make kernel code and rodata RX (set RO). */ | |
391 | { | |
25362dc4 | 392 | .name = "text/rodata RO", |
80d6b0c2 KC |
393 | .start = (unsigned long)_stext, |
394 | .end = (unsigned long)__init_begin, | |
395 | #ifdef CONFIG_ARM_LPAE | |
400eeffa PD |
396 | .mask = ~(L_PMD_SECT_RDONLY | PMD_SECT_AP2), |
397 | .prot = L_PMD_SECT_RDONLY | PMD_SECT_AP2, | |
80d6b0c2 KC |
398 | #else |
399 | .mask = ~(PMD_SECT_APX | PMD_SECT_AP_WRITE), | |
400 | .prot = PMD_SECT_APX | PMD_SECT_AP_WRITE, | |
401 | .clear = PMD_SECT_AP_WRITE, | |
402 | #endif | |
403 | }, | |
404 | }; | |
80d6b0c2 | 405 | |
1e6b4811 KC |
406 | /* |
407 | * Updates section permissions only for the current mm (sections are | |
408 | * copied into each mm). During startup, this is the init_mm. Is only | |
409 | * safe to be called with preemption disabled, as under stop_machine(). | |
410 | */ | |
411 | static inline void section_update(unsigned long addr, pmdval_t mask, | |
08925c2f | 412 | pmdval_t prot, struct mm_struct *mm) |
1e6b4811 | 413 | { |
1e6b4811 KC |
414 | pmd_t *pmd; |
415 | ||
84e6ffb2 | 416 | pmd = pmd_offset(pud_offset(p4d_offset(pgd_offset(mm, addr), addr), addr), addr); |
1e6b4811 KC |
417 | |
418 | #ifdef CONFIG_ARM_LPAE | |
419 | pmd[0] = __pmd((pmd_val(pmd[0]) & mask) | prot); | |
420 | #else | |
421 | if (addr & SECTION_SIZE) | |
422 | pmd[1] = __pmd((pmd_val(pmd[1]) & mask) | prot); | |
423 | else | |
424 | pmd[0] = __pmd((pmd_val(pmd[0]) & mask) | prot); | |
425 | #endif | |
426 | flush_pmd_entry(pmd); | |
427 | local_flush_tlb_kernel_range(addr, addr + SECTION_SIZE); | |
428 | } | |
429 | ||
430 | /* Make sure extended page tables are in use. */ | |
431 | static inline bool arch_has_strict_perms(void) | |
432 | { | |
433 | if (cpu_architecture() < CPU_ARCH_ARMv6) | |
434 | return false; | |
435 | ||
436 | return !!(get_cr() & CR_XP); | |
437 | } | |
438 | ||
ea5379be BDC |
439 | static void set_section_perms(struct section_perm *perms, int n, bool set, |
440 | struct mm_struct *mm) | |
08925c2f LA |
441 | { |
442 | size_t i; | |
443 | unsigned long addr; | |
444 | ||
445 | if (!arch_has_strict_perms()) | |
446 | return; | |
447 | ||
448 | for (i = 0; i < n; i++) { | |
449 | if (!IS_ALIGNED(perms[i].start, SECTION_SIZE) || | |
450 | !IS_ALIGNED(perms[i].end, SECTION_SIZE)) { | |
25362dc4 KC |
451 | pr_err("BUG: %s section %lx-%lx not aligned to %lx\n", |
452 | perms[i].name, perms[i].start, perms[i].end, | |
08925c2f LA |
453 | SECTION_SIZE); |
454 | continue; | |
455 | } | |
456 | ||
457 | for (addr = perms[i].start; | |
458 | addr < perms[i].end; | |
459 | addr += SECTION_SIZE) | |
460 | section_update(addr, perms[i].mask, | |
461 | set ? perms[i].prot : perms[i].clear, mm); | |
462 | } | |
463 | ||
1e6b4811 KC |
464 | } |
465 | ||
11ce4b33 GS |
466 | /** |
467 | * update_sections_early intended to be called only through stop_machine | |
468 | * framework and executed by only one CPU while all other CPUs will spin and | |
469 | * wait, so no locking is required in this function. | |
470 | */ | |
08925c2f | 471 | static void update_sections_early(struct section_perm perms[], int n) |
1e6b4811 | 472 | { |
08925c2f LA |
473 | struct task_struct *t, *s; |
474 | ||
08925c2f LA |
475 | for_each_process(t) { |
476 | if (t->flags & PF_KTHREAD) | |
477 | continue; | |
478 | for_each_thread(t, s) | |
c51bc12d DB |
479 | if (s->mm) |
480 | set_section_perms(perms, n, true, s->mm); | |
08925c2f | 481 | } |
08925c2f LA |
482 | set_section_perms(perms, n, true, current->active_mm); |
483 | set_section_perms(perms, n, true, &init_mm); | |
484 | } | |
485 | ||
11ce4b33 | 486 | static int __fix_kernmem_perms(void *unused) |
08925c2f LA |
487 | { |
488 | update_sections_early(nx_perms, ARRAY_SIZE(nx_perms)); | |
489 | return 0; | |
490 | } | |
491 | ||
11ce4b33 | 492 | static void fix_kernmem_perms(void) |
08925c2f LA |
493 | { |
494 | stop_machine(__fix_kernmem_perms, NULL, NULL); | |
1e6b4811 | 495 | } |
80d6b0c2 | 496 | |
11ce4b33 | 497 | static int __mark_rodata_ro(void *unused) |
08925c2f LA |
498 | { |
499 | update_sections_early(ro_perms, ARRAY_SIZE(ro_perms)); | |
500 | return 0; | |
501 | } | |
502 | ||
80d6b0c2 KC |
503 | void mark_rodata_ro(void) |
504 | { | |
08925c2f | 505 | stop_machine(__mark_rodata_ro, NULL, NULL); |
a8e53c15 | 506 | debug_checkwx(); |
80d6b0c2 KC |
507 | } |
508 | ||
1e6b4811 KC |
509 | #else |
510 | static inline void fix_kernmem_perms(void) { } | |
0f5bf6d0 | 511 | #endif /* CONFIG_STRICT_KERNEL_RWX */ |
1e6b4811 | 512 | |
1e6b4811 KC |
513 | void free_initmem(void) |
514 | { | |
515 | fix_kernmem_perms(); | |
bc581770 | 516 | |
54d52573 | 517 | poison_init_mem(__init_begin, __init_end - __init_begin); |
6db015e4 | 518 | if (!machine_is_integrator() && !machine_is_cintegrator()) |
dbe67df4 | 519 | free_initmem_default(-1); |
1da177e4 LT |
520 | } |
521 | ||
522 | #ifdef CONFIG_BLK_DEV_INITRD | |
1da177e4 LT |
523 | void free_initrd_mem(unsigned long start, unsigned long end) |
524 | { | |
d8ae8a37 CH |
525 | if (start == initrd_start) |
526 | start = round_down(start, PAGE_SIZE); | |
527 | if (end == initrd_end) | |
528 | end = round_up(end, PAGE_SIZE); | |
421520ba | 529 | |
d8ae8a37 CH |
530 | poison_init_mem((void *)start, PAGE_ALIGN(end) - start); |
531 | free_reserved_area((void *)start, (void *)end, -1, "initrd"); | |
1da177e4 | 532 | } |
1da177e4 | 533 | #endif |