18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/arch/arm/mm/init.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1995-2005 Russell King 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/errno.h> 98c2ecf20Sopenharmony_ci#include <linux/swap.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/mman.h> 128c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 138c2ecf20Sopenharmony_ci#include <linux/sched/task.h> 148c2ecf20Sopenharmony_ci#include <linux/export.h> 158c2ecf20Sopenharmony_ci#include <linux/nodemask.h> 168c2ecf20Sopenharmony_ci#include <linux/initrd.h> 178c2ecf20Sopenharmony_ci#include <linux/of_fdt.h> 188c2ecf20Sopenharmony_ci#include <linux/highmem.h> 198c2ecf20Sopenharmony_ci#include <linux/gfp.h> 208c2ecf20Sopenharmony_ci#include <linux/memblock.h> 218c2ecf20Sopenharmony_ci#include <linux/dma-map-ops.h> 228c2ecf20Sopenharmony_ci#include <linux/sizes.h> 238c2ecf20Sopenharmony_ci#include <linux/stop_machine.h> 248c2ecf20Sopenharmony_ci#include <linux/swiotlb.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <asm/cp15.h> 278c2ecf20Sopenharmony_ci#include <asm/mach-types.h> 288c2ecf20Sopenharmony_ci#include <asm/memblock.h> 298c2ecf20Sopenharmony_ci#include <asm/memory.h> 308c2ecf20Sopenharmony_ci#include <asm/prom.h> 318c2ecf20Sopenharmony_ci#include <asm/sections.h> 328c2ecf20Sopenharmony_ci#include <asm/setup.h> 338c2ecf20Sopenharmony_ci#include <asm/set_memory.h> 348c2ecf20Sopenharmony_ci#include <asm/system_info.h> 358c2ecf20Sopenharmony_ci#include <asm/tlb.h> 368c2ecf20Sopenharmony_ci#include <asm/fixmap.h> 378c2ecf20Sopenharmony_ci#include <asm/ptdump.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include <asm/mach/arch.h> 408c2ecf20Sopenharmony_ci#include <asm/mach/map.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include "mm.h" 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_CP15_MMU 458c2ecf20Sopenharmony_ciunsigned long __init __clear_cr(unsigned long mask) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci cr_alignment = cr_alignment & ~mask; 488c2ecf20Sopenharmony_ci return cr_alignment; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci#endif 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#ifdef CONFIG_BLK_DEV_INITRD 538c2ecf20Sopenharmony_cistatic int __init parse_tag_initrd(const struct tag *tag) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci pr_warn("ATAG_INITRD is deprecated; " 568c2ecf20Sopenharmony_ci "please update your bootloader.\n"); 578c2ecf20Sopenharmony_ci phys_initrd_start = __virt_to_phys(tag->u.initrd.start); 588c2ecf20Sopenharmony_ci phys_initrd_size = tag->u.initrd.size; 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci__tagtable(ATAG_INITRD, parse_tag_initrd); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int __init parse_tag_initrd2(const struct tag *tag) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci phys_initrd_start = tag->u.initrd.start; 678c2ecf20Sopenharmony_ci phys_initrd_size = tag->u.initrd.size; 688c2ecf20Sopenharmony_ci return 0; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci__tagtable(ATAG_INITRD2, parse_tag_initrd2); 728c2ecf20Sopenharmony_ci#endif 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void __init find_limits(unsigned long *min, unsigned long *max_low, 758c2ecf20Sopenharmony_ci unsigned long *max_high) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci *max_low = PFN_DOWN(memblock_get_current_limit()); 788c2ecf20Sopenharmony_ci *min = PFN_UP(memblock_start_of_DRAM()); 798c2ecf20Sopenharmony_ci *max_high = PFN_DOWN(memblock_end_of_DRAM()); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#ifdef CONFIG_ZONE_DMA 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ciphys_addr_t arm_dma_zone_size __read_mostly; 858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(arm_dma_zone_size); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* 888c2ecf20Sopenharmony_ci * The DMA mask corresponding to the maximum bus address allocatable 898c2ecf20Sopenharmony_ci * using GFP_DMA. The default here places no restriction on DMA 908c2ecf20Sopenharmony_ci * allocations. This must be the smallest DMA mask in the system, 918c2ecf20Sopenharmony_ci * so a successful GFP_DMA allocation will always satisfy this. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ciphys_addr_t arm_dma_limit; 948c2ecf20Sopenharmony_ciunsigned long arm_dma_pfn_limit; 958c2ecf20Sopenharmony_ci#endif 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_civoid __init setup_dma_zone(const struct machine_desc *mdesc) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci#ifdef CONFIG_ZONE_DMA 1008c2ecf20Sopenharmony_ci if (mdesc->dma_zone_size) { 1018c2ecf20Sopenharmony_ci arm_dma_zone_size = mdesc->dma_zone_size; 1028c2ecf20Sopenharmony_ci arm_dma_limit = PHYS_OFFSET + arm_dma_zone_size - 1; 1038c2ecf20Sopenharmony_ci } else 1048c2ecf20Sopenharmony_ci arm_dma_limit = 0xffffffff; 1058c2ecf20Sopenharmony_ci arm_dma_pfn_limit = arm_dma_limit >> PAGE_SHIFT; 1068c2ecf20Sopenharmony_ci#endif 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void __init zone_sizes_init(unsigned long min, unsigned long max_low, 1108c2ecf20Sopenharmony_ci unsigned long max_high) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci unsigned long max_zone_pfn[MAX_NR_ZONES] = { 0 }; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#ifdef CONFIG_ZONE_DMA 1158c2ecf20Sopenharmony_ci max_zone_pfn[ZONE_DMA] = min(arm_dma_pfn_limit, max_low); 1168c2ecf20Sopenharmony_ci#endif 1178c2ecf20Sopenharmony_ci max_zone_pfn[ZONE_NORMAL] = max_low; 1188c2ecf20Sopenharmony_ci#ifdef CONFIG_HIGHMEM 1198c2ecf20Sopenharmony_ci max_zone_pfn[ZONE_HIGHMEM] = max_high; 1208c2ecf20Sopenharmony_ci#endif 1218c2ecf20Sopenharmony_ci free_area_init(max_zone_pfn); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#ifdef CONFIG_HAVE_ARCH_PFN_VALID 1258c2ecf20Sopenharmony_ciint pfn_valid(unsigned long pfn) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci phys_addr_t addr = __pfn_to_phys(pfn); 1288c2ecf20Sopenharmony_ci unsigned long pageblock_size = PAGE_SIZE * pageblock_nr_pages; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (__phys_to_pfn(addr) != pfn) 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* 1348c2ecf20Sopenharmony_ci * If address less than pageblock_size bytes away from a present 1358c2ecf20Sopenharmony_ci * memory chunk there still will be a memory map entry for it 1368c2ecf20Sopenharmony_ci * because we round freed memory map to the pageblock boundaries. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci if (memblock_overlaps_region(&memblock.memory, 1398c2ecf20Sopenharmony_ci ALIGN_DOWN(addr, pageblock_size), 1408c2ecf20Sopenharmony_ci pageblock_size)) 1418c2ecf20Sopenharmony_ci return 1; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pfn_valid); 1468c2ecf20Sopenharmony_ci#endif 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic bool arm_memblock_steal_permitted = true; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ciphys_addr_t __init arm_memblock_steal(phys_addr_t size, phys_addr_t align) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci phys_addr_t phys; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci BUG_ON(!arm_memblock_steal_permitted); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci phys = memblock_phys_alloc(size, align); 1578c2ecf20Sopenharmony_ci if (!phys) 1588c2ecf20Sopenharmony_ci panic("Failed to steal %pa bytes at %pS\n", 1598c2ecf20Sopenharmony_ci &size, (void *)_RET_IP_); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci memblock_free(phys, size); 1628c2ecf20Sopenharmony_ci memblock_remove(phys, size); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return phys; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void __init arm_initrd_init(void) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci#ifdef CONFIG_BLK_DEV_INITRD 1708c2ecf20Sopenharmony_ci phys_addr_t start; 1718c2ecf20Sopenharmony_ci unsigned long size; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci initrd_start = initrd_end = 0; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (!phys_initrd_size) 1768c2ecf20Sopenharmony_ci return; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* 1798c2ecf20Sopenharmony_ci * Round the memory region to page boundaries as per free_initrd_mem() 1808c2ecf20Sopenharmony_ci * This allows us to detect whether the pages overlapping the initrd 1818c2ecf20Sopenharmony_ci * are in use, but more importantly, reserves the entire set of pages 1828c2ecf20Sopenharmony_ci * as we don't want these pages allocated for other purposes. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci start = round_down(phys_initrd_start, PAGE_SIZE); 1858c2ecf20Sopenharmony_ci size = phys_initrd_size + (phys_initrd_start - start); 1868c2ecf20Sopenharmony_ci size = round_up(size, PAGE_SIZE); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (!memblock_is_region_memory(start, size)) { 1898c2ecf20Sopenharmony_ci pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region - disabling initrd\n", 1908c2ecf20Sopenharmony_ci (u64)start, size); 1918c2ecf20Sopenharmony_ci return; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (memblock_is_region_reserved(start, size)) { 1958c2ecf20Sopenharmony_ci pr_err("INITRD: 0x%08llx+0x%08lx overlaps in-use memory region - disabling initrd\n", 1968c2ecf20Sopenharmony_ci (u64)start, size); 1978c2ecf20Sopenharmony_ci return; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci memblock_reserve(start, size); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Now convert initrd to virtual addresses */ 2038c2ecf20Sopenharmony_ci initrd_start = __phys_to_virt(phys_initrd_start); 2048c2ecf20Sopenharmony_ci initrd_end = initrd_start + phys_initrd_size; 2058c2ecf20Sopenharmony_ci#endif 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_ICACHE_MISMATCH_WORKAROUND 2098c2ecf20Sopenharmony_civoid check_cpu_icache_size(int cpuid) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci u32 size, ctr; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci asm("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr)); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci size = 1 << ((ctr & 0xf) + 2); 2168c2ecf20Sopenharmony_ci if (cpuid != 0 && icache_size != size) 2178c2ecf20Sopenharmony_ci pr_info("CPU%u: detected I-Cache line size mismatch, workaround enabled\n", 2188c2ecf20Sopenharmony_ci cpuid); 2198c2ecf20Sopenharmony_ci if (icache_size > size) 2208c2ecf20Sopenharmony_ci icache_size = size; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci#endif 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_civoid __init arm_memblock_init(const struct machine_desc *mdesc) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci /* Register the kernel text, kernel data and initrd with memblock. */ 2278c2ecf20Sopenharmony_ci memblock_reserve(__pa(KERNEL_START), KERNEL_END - KERNEL_START); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci arm_initrd_init(); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci arm_mm_memblock_reserve(); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* reserve any platform specific memblock areas */ 2348c2ecf20Sopenharmony_ci if (mdesc->reserve) 2358c2ecf20Sopenharmony_ci mdesc->reserve(); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci early_init_fdt_scan_reserved_mem(); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* reserve memory for DMA contiguous allocations */ 2408c2ecf20Sopenharmony_ci dma_contiguous_reserve(arm_dma_limit); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci arm_memblock_steal_permitted = false; 2438c2ecf20Sopenharmony_ci memblock_dump_all(); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_civoid __init bootmem_init(void) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci memblock_allow_resize(); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci find_limits(&min_low_pfn, &max_low_pfn, &max_pfn); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci early_memtest((phys_addr_t)min_low_pfn << PAGE_SHIFT, 2538c2ecf20Sopenharmony_ci (phys_addr_t)max_low_pfn << PAGE_SHIFT); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* 2568c2ecf20Sopenharmony_ci * sparse_init() tries to allocate memory from memblock, so must be 2578c2ecf20Sopenharmony_ci * done after the fixed reservations 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci sparse_init(); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* 2628c2ecf20Sopenharmony_ci * Now free the memory - free_area_init needs 2638c2ecf20Sopenharmony_ci * the sparse mem_map arrays initialized by sparse_init() 2648c2ecf20Sopenharmony_ci * for memmap_init_zone(), otherwise all PFNs are invalid. 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ci zone_sizes_init(min_low_pfn, max_low_pfn, max_pfn); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* 2708c2ecf20Sopenharmony_ci * Poison init memory with an undefined instruction (ARM) or a branch to an 2718c2ecf20Sopenharmony_ci * undefined instruction (Thumb). 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_cistatic inline void poison_init_mem(void *s, size_t count) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci u32 *p = (u32 *)s; 2768c2ecf20Sopenharmony_ci for (; count != 0; count -= 4) 2778c2ecf20Sopenharmony_ci *p++ = 0xe7fddef0; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic inline void __init 2818c2ecf20Sopenharmony_cifree_memmap(unsigned long start_pfn, unsigned long end_pfn) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct page *start_pg, *end_pg; 2848c2ecf20Sopenharmony_ci phys_addr_t pg, pgend; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* 2878c2ecf20Sopenharmony_ci * Convert start_pfn/end_pfn to a struct page pointer. 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_ci start_pg = pfn_to_page(start_pfn - 1) + 1; 2908c2ecf20Sopenharmony_ci end_pg = pfn_to_page(end_pfn - 1) + 1; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* 2938c2ecf20Sopenharmony_ci * Convert to physical addresses, and 2948c2ecf20Sopenharmony_ci * round start upwards and end downwards. 2958c2ecf20Sopenharmony_ci */ 2968c2ecf20Sopenharmony_ci pg = PAGE_ALIGN(__pa(start_pg)); 2978c2ecf20Sopenharmony_ci pgend = __pa(end_pg) & PAGE_MASK; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* 3008c2ecf20Sopenharmony_ci * If there are free pages between these, 3018c2ecf20Sopenharmony_ci * free the section of the memmap array. 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_ci if (pg < pgend) 3048c2ecf20Sopenharmony_ci memblock_free_early(pg, pgend - pg); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci/* 3088c2ecf20Sopenharmony_ci * The mem_map array can get very big. Free the unused area of the memory map. 3098c2ecf20Sopenharmony_ci */ 3108c2ecf20Sopenharmony_cistatic void __init free_unused_memmap(void) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci unsigned long start, end, prev_end = 0; 3138c2ecf20Sopenharmony_ci int i; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* 3168c2ecf20Sopenharmony_ci * This relies on each bank being in address order. 3178c2ecf20Sopenharmony_ci * The banks are sorted previously in bootmem_init(). 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci for_each_mem_pfn_range(i, MAX_NUMNODES, &start, &end, NULL) { 3208c2ecf20Sopenharmony_ci#ifdef CONFIG_SPARSEMEM 3218c2ecf20Sopenharmony_ci /* 3228c2ecf20Sopenharmony_ci * Take care not to free memmap entries that don't exist 3238c2ecf20Sopenharmony_ci * due to SPARSEMEM sections which aren't present. 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ci start = min(start, 3268c2ecf20Sopenharmony_ci ALIGN(prev_end, PAGES_PER_SECTION)); 3278c2ecf20Sopenharmony_ci#endif 3288c2ecf20Sopenharmony_ci /* 3298c2ecf20Sopenharmony_ci * Align down here since many operations in VM subsystem 3308c2ecf20Sopenharmony_ci * presume that there are no holes in the memory map inside 3318c2ecf20Sopenharmony_ci * a pageblock 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci start = round_down(start, pageblock_nr_pages); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* 3368c2ecf20Sopenharmony_ci * If we had a previous bank, and there is a space 3378c2ecf20Sopenharmony_ci * between the current bank and the previous, free it. 3388c2ecf20Sopenharmony_ci */ 3398c2ecf20Sopenharmony_ci if (prev_end && prev_end < start) 3408c2ecf20Sopenharmony_ci free_memmap(prev_end, start); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* 3438c2ecf20Sopenharmony_ci * Align up here since many operations in VM subsystem 3448c2ecf20Sopenharmony_ci * presume that there are no holes in the memory map inside 3458c2ecf20Sopenharmony_ci * a pageblock 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_ci prev_end = ALIGN(end, pageblock_nr_pages); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci#ifdef CONFIG_SPARSEMEM 3518c2ecf20Sopenharmony_ci if (!IS_ALIGNED(prev_end, PAGES_PER_SECTION)) { 3528c2ecf20Sopenharmony_ci prev_end = ALIGN(end, pageblock_nr_pages); 3538c2ecf20Sopenharmony_ci free_memmap(prev_end, 3548c2ecf20Sopenharmony_ci ALIGN(prev_end, PAGES_PER_SECTION)); 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci#endif 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic void __init free_highpages(void) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci#ifdef CONFIG_HIGHMEM 3628c2ecf20Sopenharmony_ci unsigned long max_low = max_low_pfn; 3638c2ecf20Sopenharmony_ci phys_addr_t range_start, range_end; 3648c2ecf20Sopenharmony_ci u64 i; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* set highmem page free */ 3678c2ecf20Sopenharmony_ci for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, 3688c2ecf20Sopenharmony_ci &range_start, &range_end, NULL) { 3698c2ecf20Sopenharmony_ci unsigned long start = PFN_UP(range_start); 3708c2ecf20Sopenharmony_ci unsigned long end = PFN_DOWN(range_end); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* Ignore complete lowmem entries */ 3738c2ecf20Sopenharmony_ci if (end <= max_low) 3748c2ecf20Sopenharmony_ci continue; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* Truncate partial highmem entries */ 3778c2ecf20Sopenharmony_ci if (start < max_low) 3788c2ecf20Sopenharmony_ci start = max_low; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci for (; start < end; start++) 3818c2ecf20Sopenharmony_ci free_highmem_page(pfn_to_page(start)); 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci#endif 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* 3878c2ecf20Sopenharmony_ci * mem_init() marks the free areas in the mem_map and tells us how much 3888c2ecf20Sopenharmony_ci * memory is free. This is done after various parts of the system have 3898c2ecf20Sopenharmony_ci * claimed their memory after the kernel image. 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_civoid __init mem_init(void) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_LPAE 3948c2ecf20Sopenharmony_ci if (swiotlb_force == SWIOTLB_FORCE || 3958c2ecf20Sopenharmony_ci max_pfn > arm_dma_pfn_limit) 3968c2ecf20Sopenharmony_ci swiotlb_init(1); 3978c2ecf20Sopenharmony_ci else 3988c2ecf20Sopenharmony_ci swiotlb_force = SWIOTLB_NO_FORCE; 3998c2ecf20Sopenharmony_ci#endif 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci set_max_mapnr(pfn_to_page(max_pfn) - mem_map); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* this will put all unused low memory onto the freelists */ 4048c2ecf20Sopenharmony_ci free_unused_memmap(); 4058c2ecf20Sopenharmony_ci memblock_free_all(); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci#ifdef CONFIG_SA1111 4088c2ecf20Sopenharmony_ci /* now that our DMA memory is actually so designated, we can free it */ 4098c2ecf20Sopenharmony_ci free_reserved_area(__va(PHYS_OFFSET), swapper_pg_dir, -1, NULL); 4108c2ecf20Sopenharmony_ci#endif 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci free_highpages(); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci mem_init_print_info(NULL); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* 4178c2ecf20Sopenharmony_ci * Check boundaries twice: Some fundamental inconsistencies can 4188c2ecf20Sopenharmony_ci * be detected at build time already. 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU 4218c2ecf20Sopenharmony_ci BUILD_BUG_ON(TASK_SIZE > MODULES_VADDR); 4228c2ecf20Sopenharmony_ci BUG_ON(TASK_SIZE > MODULES_VADDR); 4238c2ecf20Sopenharmony_ci#endif 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci#ifdef CONFIG_HIGHMEM 4268c2ecf20Sopenharmony_ci BUILD_BUG_ON(PKMAP_BASE + LAST_PKMAP * PAGE_SIZE > PAGE_OFFSET); 4278c2ecf20Sopenharmony_ci BUG_ON(PKMAP_BASE + LAST_PKMAP * PAGE_SIZE > PAGE_OFFSET); 4288c2ecf20Sopenharmony_ci#endif 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci#ifdef CONFIG_STRICT_KERNEL_RWX 4328c2ecf20Sopenharmony_cistruct section_perm { 4338c2ecf20Sopenharmony_ci const char *name; 4348c2ecf20Sopenharmony_ci unsigned long start; 4358c2ecf20Sopenharmony_ci unsigned long end; 4368c2ecf20Sopenharmony_ci pmdval_t mask; 4378c2ecf20Sopenharmony_ci pmdval_t prot; 4388c2ecf20Sopenharmony_ci pmdval_t clear; 4398c2ecf20Sopenharmony_ci}; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/* First section-aligned location at or after __start_rodata. */ 4428c2ecf20Sopenharmony_ciextern char __start_rodata_section_aligned[]; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic struct section_perm nx_perms[] = { 4458c2ecf20Sopenharmony_ci /* Make pages tables, etc before _stext RW (set NX). */ 4468c2ecf20Sopenharmony_ci { 4478c2ecf20Sopenharmony_ci .name = "pre-text NX", 4488c2ecf20Sopenharmony_ci .start = PAGE_OFFSET, 4498c2ecf20Sopenharmony_ci .end = (unsigned long)_stext, 4508c2ecf20Sopenharmony_ci .mask = ~PMD_SECT_XN, 4518c2ecf20Sopenharmony_ci .prot = PMD_SECT_XN, 4528c2ecf20Sopenharmony_ci }, 4538c2ecf20Sopenharmony_ci /* Make init RW (set NX). */ 4548c2ecf20Sopenharmony_ci { 4558c2ecf20Sopenharmony_ci .name = "init NX", 4568c2ecf20Sopenharmony_ci .start = (unsigned long)__init_begin, 4578c2ecf20Sopenharmony_ci .end = (unsigned long)_sdata, 4588c2ecf20Sopenharmony_ci .mask = ~PMD_SECT_XN, 4598c2ecf20Sopenharmony_ci .prot = PMD_SECT_XN, 4608c2ecf20Sopenharmony_ci }, 4618c2ecf20Sopenharmony_ci /* Make rodata NX (set RO in ro_perms below). */ 4628c2ecf20Sopenharmony_ci { 4638c2ecf20Sopenharmony_ci .name = "rodata NX", 4648c2ecf20Sopenharmony_ci .start = (unsigned long)__start_rodata_section_aligned, 4658c2ecf20Sopenharmony_ci .end = (unsigned long)__init_begin, 4668c2ecf20Sopenharmony_ci .mask = ~PMD_SECT_XN, 4678c2ecf20Sopenharmony_ci .prot = PMD_SECT_XN, 4688c2ecf20Sopenharmony_ci }, 4698c2ecf20Sopenharmony_ci}; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic struct section_perm ro_perms[] = { 4728c2ecf20Sopenharmony_ci /* Make kernel code and rodata RX (set RO). */ 4738c2ecf20Sopenharmony_ci { 4748c2ecf20Sopenharmony_ci .name = "text/rodata RO", 4758c2ecf20Sopenharmony_ci .start = (unsigned long)_stext, 4768c2ecf20Sopenharmony_ci .end = (unsigned long)__init_begin, 4778c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_LPAE 4788c2ecf20Sopenharmony_ci .mask = ~(L_PMD_SECT_RDONLY | PMD_SECT_AP2), 4798c2ecf20Sopenharmony_ci .prot = L_PMD_SECT_RDONLY | PMD_SECT_AP2, 4808c2ecf20Sopenharmony_ci#else 4818c2ecf20Sopenharmony_ci .mask = ~(PMD_SECT_APX | PMD_SECT_AP_WRITE), 4828c2ecf20Sopenharmony_ci .prot = PMD_SECT_APX | PMD_SECT_AP_WRITE, 4838c2ecf20Sopenharmony_ci .clear = PMD_SECT_AP_WRITE, 4848c2ecf20Sopenharmony_ci#endif 4858c2ecf20Sopenharmony_ci }, 4868c2ecf20Sopenharmony_ci}; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci/* 4898c2ecf20Sopenharmony_ci * Updates section permissions only for the current mm (sections are 4908c2ecf20Sopenharmony_ci * copied into each mm). During startup, this is the init_mm. Is only 4918c2ecf20Sopenharmony_ci * safe to be called with preemption disabled, as under stop_machine(). 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_cistatic inline void section_update(unsigned long addr, pmdval_t mask, 4948c2ecf20Sopenharmony_ci pmdval_t prot, struct mm_struct *mm) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci pmd_t *pmd; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci pmd = pmd_offset(pud_offset(p4d_offset(pgd_offset(mm, addr), addr), addr), addr); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_LPAE 5018c2ecf20Sopenharmony_ci pmd[0] = __pmd((pmd_val(pmd[0]) & mask) | prot); 5028c2ecf20Sopenharmony_ci#else 5038c2ecf20Sopenharmony_ci if (addr & SECTION_SIZE) 5048c2ecf20Sopenharmony_ci pmd[1] = __pmd((pmd_val(pmd[1]) & mask) | prot); 5058c2ecf20Sopenharmony_ci else 5068c2ecf20Sopenharmony_ci pmd[0] = __pmd((pmd_val(pmd[0]) & mask) | prot); 5078c2ecf20Sopenharmony_ci#endif 5088c2ecf20Sopenharmony_ci flush_pmd_entry(pmd); 5098c2ecf20Sopenharmony_ci local_flush_tlb_kernel_range(addr, addr + SECTION_SIZE); 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci/* Make sure extended page tables are in use. */ 5138c2ecf20Sopenharmony_cistatic inline bool arch_has_strict_perms(void) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci if (cpu_architecture() < CPU_ARCH_ARMv6) 5168c2ecf20Sopenharmony_ci return false; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return !!(get_cr() & CR_XP); 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic void set_section_perms(struct section_perm *perms, int n, bool set, 5228c2ecf20Sopenharmony_ci struct mm_struct *mm) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci size_t i; 5258c2ecf20Sopenharmony_ci unsigned long addr; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (!arch_has_strict_perms()) 5288c2ecf20Sopenharmony_ci return; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 5318c2ecf20Sopenharmony_ci if (!IS_ALIGNED(perms[i].start, SECTION_SIZE) || 5328c2ecf20Sopenharmony_ci !IS_ALIGNED(perms[i].end, SECTION_SIZE)) { 5338c2ecf20Sopenharmony_ci pr_err("BUG: %s section %lx-%lx not aligned to %lx\n", 5348c2ecf20Sopenharmony_ci perms[i].name, perms[i].start, perms[i].end, 5358c2ecf20Sopenharmony_ci SECTION_SIZE); 5368c2ecf20Sopenharmony_ci continue; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci for (addr = perms[i].start; 5408c2ecf20Sopenharmony_ci addr < perms[i].end; 5418c2ecf20Sopenharmony_ci addr += SECTION_SIZE) 5428c2ecf20Sopenharmony_ci section_update(addr, perms[i].mask, 5438c2ecf20Sopenharmony_ci set ? perms[i].prot : perms[i].clear, mm); 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci/** 5498c2ecf20Sopenharmony_ci * update_sections_early intended to be called only through stop_machine 5508c2ecf20Sopenharmony_ci * framework and executed by only one CPU while all other CPUs will spin and 5518c2ecf20Sopenharmony_ci * wait, so no locking is required in this function. 5528c2ecf20Sopenharmony_ci */ 5538c2ecf20Sopenharmony_cistatic void update_sections_early(struct section_perm perms[], int n) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci struct task_struct *t, *s; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci for_each_process(t) { 5588c2ecf20Sopenharmony_ci if (t->flags & PF_KTHREAD) 5598c2ecf20Sopenharmony_ci continue; 5608c2ecf20Sopenharmony_ci for_each_thread(t, s) 5618c2ecf20Sopenharmony_ci if (s->mm) 5628c2ecf20Sopenharmony_ci set_section_perms(perms, n, true, s->mm); 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci set_section_perms(perms, n, true, current->active_mm); 5658c2ecf20Sopenharmony_ci set_section_perms(perms, n, true, &init_mm); 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic int __fix_kernmem_perms(void *unused) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci update_sections_early(nx_perms, ARRAY_SIZE(nx_perms)); 5718c2ecf20Sopenharmony_ci return 0; 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic void fix_kernmem_perms(void) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci stop_machine(__fix_kernmem_perms, NULL, NULL); 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic int __mark_rodata_ro(void *unused) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci update_sections_early(ro_perms, ARRAY_SIZE(ro_perms)); 5828c2ecf20Sopenharmony_ci return 0; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic int kernel_set_to_readonly __read_mostly; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_civoid mark_rodata_ro(void) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci kernel_set_to_readonly = 1; 5908c2ecf20Sopenharmony_ci stop_machine(__mark_rodata_ro, NULL, NULL); 5918c2ecf20Sopenharmony_ci debug_checkwx(); 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_civoid set_kernel_text_rw(void) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci if (!kernel_set_to_readonly) 5978c2ecf20Sopenharmony_ci return; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci set_section_perms(ro_perms, ARRAY_SIZE(ro_perms), false, 6008c2ecf20Sopenharmony_ci current->active_mm); 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_civoid set_kernel_text_ro(void) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci if (!kernel_set_to_readonly) 6068c2ecf20Sopenharmony_ci return; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci set_section_perms(ro_perms, ARRAY_SIZE(ro_perms), true, 6098c2ecf20Sopenharmony_ci current->active_mm); 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci#else 6138c2ecf20Sopenharmony_cistatic inline void fix_kernmem_perms(void) { } 6148c2ecf20Sopenharmony_ci#endif /* CONFIG_STRICT_KERNEL_RWX */ 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_civoid free_initmem(void) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci fix_kernmem_perms(); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci poison_init_mem(__init_begin, __init_end - __init_begin); 6218c2ecf20Sopenharmony_ci if (!machine_is_integrator() && !machine_is_cintegrator()) 6228c2ecf20Sopenharmony_ci free_initmem_default(-1); 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci#ifdef CONFIG_BLK_DEV_INITRD 6268c2ecf20Sopenharmony_civoid free_initrd_mem(unsigned long start, unsigned long end) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci if (start == initrd_start) 6298c2ecf20Sopenharmony_ci start = round_down(start, PAGE_SIZE); 6308c2ecf20Sopenharmony_ci if (end == initrd_end) 6318c2ecf20Sopenharmony_ci end = round_up(end, PAGE_SIZE); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci poison_init_mem((void *)start, PAGE_ALIGN(end) - start); 6348c2ecf20Sopenharmony_ci free_reserved_area((void *)start, (void *)end, -1, "initrd"); 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci#endif 637