18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/arch/arm/mm/nommu.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * ARM uCLinux supporting functions. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/mm.h> 98c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/memblock.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 158c2ecf20Sopenharmony_ci#include <asm/cp15.h> 168c2ecf20Sopenharmony_ci#include <asm/sections.h> 178c2ecf20Sopenharmony_ci#include <asm/page.h> 188c2ecf20Sopenharmony_ci#include <asm/setup.h> 198c2ecf20Sopenharmony_ci#include <asm/traps.h> 208c2ecf20Sopenharmony_ci#include <asm/mach/arch.h> 218c2ecf20Sopenharmony_ci#include <asm/cputype.h> 228c2ecf20Sopenharmony_ci#include <asm/mpu.h> 238c2ecf20Sopenharmony_ci#include <asm/procinfo.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "mm.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciunsigned long vectors_base; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * empty_zero_page is a special page that is used for 318c2ecf20Sopenharmony_ci * zero-initialized data and COW. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_cistruct page *empty_zero_page; 348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(empty_zero_page); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_MPU 378c2ecf20Sopenharmony_cistruct mpu_rgn_info mpu_rgn_info; 388c2ecf20Sopenharmony_ci#endif 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_CP15 418c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HIGH_VECTOR 428c2ecf20Sopenharmony_ciunsigned long setup_vectors_base(void) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci unsigned long reg = get_cr(); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci set_cr(reg | CR_V); 478c2ecf20Sopenharmony_ci return 0xffff0000; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci#else /* CONFIG_CPU_HIGH_VECTOR */ 508c2ecf20Sopenharmony_ci/* Write exception base address to VBAR */ 518c2ecf20Sopenharmony_cistatic inline void set_vbar(unsigned long val) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci asm("mcr p15, 0, %0, c12, c0, 0" : : "r" (val) : "cc"); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* 578c2ecf20Sopenharmony_ci * Security extensions, bits[7:4], permitted values, 588c2ecf20Sopenharmony_ci * 0b0000 - not implemented, 0b0001/0b0010 - implemented 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_cistatic inline bool security_extensions_enabled(void) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci /* Check CPUID Identification Scheme before ID_PFR1 read */ 638c2ecf20Sopenharmony_ci if ((read_cpuid_id() & 0x000f0000) == 0x000f0000) 648c2ecf20Sopenharmony_ci return cpuid_feature_extract(CPUID_EXT_PFR1, 4) || 658c2ecf20Sopenharmony_ci cpuid_feature_extract(CPUID_EXT_PFR1, 20); 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ciunsigned long setup_vectors_base(void) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci unsigned long base = 0, reg = get_cr(); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci set_cr(reg & ~CR_V); 748c2ecf20Sopenharmony_ci if (security_extensions_enabled()) { 758c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_REMAP_VECTORS_TO_RAM)) 768c2ecf20Sopenharmony_ci base = CONFIG_DRAM_BASE; 778c2ecf20Sopenharmony_ci set_vbar(base); 788c2ecf20Sopenharmony_ci } else if (IS_ENABLED(CONFIG_REMAP_VECTORS_TO_RAM)) { 798c2ecf20Sopenharmony_ci if (CONFIG_DRAM_BASE != 0) 808c2ecf20Sopenharmony_ci pr_err("Security extensions not enabled, vectors cannot be remapped to RAM, vectors base will be 0x00000000\n"); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return base; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci#endif /* CONFIG_CPU_HIGH_VECTOR */ 868c2ecf20Sopenharmony_ci#endif /* CONFIG_CPU_CP15 */ 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_civoid __init arm_mm_memblock_reserve(void) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci#ifndef CONFIG_CPU_V7M 918c2ecf20Sopenharmony_ci vectors_base = IS_ENABLED(CONFIG_CPU_CP15) ? setup_vectors_base() : 0; 928c2ecf20Sopenharmony_ci /* 938c2ecf20Sopenharmony_ci * Register the exception vector page. 948c2ecf20Sopenharmony_ci * some architectures which the DRAM is the exception vector to trap, 958c2ecf20Sopenharmony_ci * alloc_page breaks with error, although it is not NULL, but "0." 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci memblock_reserve(vectors_base, 2 * PAGE_SIZE); 988c2ecf20Sopenharmony_ci#else /* ifndef CONFIG_CPU_V7M */ 998c2ecf20Sopenharmony_ci /* 1008c2ecf20Sopenharmony_ci * There is no dedicated vector page on V7-M. So nothing needs to be 1018c2ecf20Sopenharmony_ci * reserved here. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci#endif 1048c2ecf20Sopenharmony_ci /* 1058c2ecf20Sopenharmony_ci * In any case, always ensure address 0 is never used as many things 1068c2ecf20Sopenharmony_ci * get very confused if 0 is returned as a legitimate address. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_ci memblock_reserve(0, 1); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void __init adjust_lowmem_bounds_mpu(void) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci unsigned long pmsa = read_cpuid_ext(CPUID_EXT_MMFR0) & MMFR0_PMSA; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci switch (pmsa) { 1168c2ecf20Sopenharmony_ci case MMFR0_PMSAv7: 1178c2ecf20Sopenharmony_ci pmsav7_adjust_lowmem_bounds(); 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci case MMFR0_PMSAv8: 1208c2ecf20Sopenharmony_ci pmsav8_adjust_lowmem_bounds(); 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci default: 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic void __init mpu_setup(void) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci unsigned long pmsa = read_cpuid_ext(CPUID_EXT_MMFR0) & MMFR0_PMSA; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci switch (pmsa) { 1328c2ecf20Sopenharmony_ci case MMFR0_PMSAv7: 1338c2ecf20Sopenharmony_ci pmsav7_setup(); 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci case MMFR0_PMSAv8: 1368c2ecf20Sopenharmony_ci pmsav8_setup(); 1378c2ecf20Sopenharmony_ci break; 1388c2ecf20Sopenharmony_ci default: 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_civoid __init adjust_lowmem_bounds(void) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci phys_addr_t end; 1468c2ecf20Sopenharmony_ci adjust_lowmem_bounds_mpu(); 1478c2ecf20Sopenharmony_ci end = memblock_end_of_DRAM(); 1488c2ecf20Sopenharmony_ci high_memory = __va(end - 1) + 1; 1498c2ecf20Sopenharmony_ci memblock_set_current_limit(end); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* 1538c2ecf20Sopenharmony_ci * paging_init() sets up the page tables, initialises the zone memory 1548c2ecf20Sopenharmony_ci * maps, and sets up the zero page, bad page and bad page tables. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_civoid __init paging_init(const struct machine_desc *mdesc) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci void *zero_page; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci early_trap_init((void *)vectors_base); 1618c2ecf20Sopenharmony_ci mpu_setup(); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* allocate the zero page. */ 1648c2ecf20Sopenharmony_ci zero_page = (void *)memblock_alloc(PAGE_SIZE, PAGE_SIZE); 1658c2ecf20Sopenharmony_ci if (!zero_page) 1668c2ecf20Sopenharmony_ci panic("%s: Failed to allocate %lu bytes align=0x%lx\n", 1678c2ecf20Sopenharmony_ci __func__, PAGE_SIZE, PAGE_SIZE); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci bootmem_init(); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci empty_zero_page = virt_to_page(zero_page); 1728c2ecf20Sopenharmony_ci flush_dcache_page(empty_zero_page); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/* 1768c2ecf20Sopenharmony_ci * We don't need to do anything here for nommu machines. 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_civoid setup_mm_for_reboot(void) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_civoid flush_dcache_page(struct page *page) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci __cpuc_flush_dcache_area(page_address(page), PAGE_SIZE); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(flush_dcache_page); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_civoid flush_kernel_dcache_page(struct page *page) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci __cpuc_flush_dcache_area(page_address(page), PAGE_SIZE); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(flush_kernel_dcache_page); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_civoid copy_to_user_page(struct vm_area_struct *vma, struct page *page, 1958c2ecf20Sopenharmony_ci unsigned long uaddr, void *dst, const void *src, 1968c2ecf20Sopenharmony_ci unsigned long len) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci memcpy(dst, src, len); 1998c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_EXEC) 2008c2ecf20Sopenharmony_ci __cpuc_coherent_user_range(uaddr, uaddr + len); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_civoid __iomem *__arm_ioremap_pfn(unsigned long pfn, unsigned long offset, 2048c2ecf20Sopenharmony_ci size_t size, unsigned int mtype) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci if (pfn >= (0x100000000ULL >> PAGE_SHIFT)) 2078c2ecf20Sopenharmony_ci return NULL; 2088c2ecf20Sopenharmony_ci return (void __iomem *) (offset + (pfn << PAGE_SHIFT)); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__arm_ioremap_pfn); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_civoid __iomem *__arm_ioremap_caller(phys_addr_t phys_addr, size_t size, 2138c2ecf20Sopenharmony_ci unsigned int mtype, void *caller) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci return (void __iomem *)phys_addr; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_civoid __iomem * (*arch_ioremap_caller)(phys_addr_t, size_t, unsigned int, void *); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_civoid __iomem *ioremap(resource_size_t res_cookie, size_t size) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci return __arm_ioremap_caller(res_cookie, size, MT_DEVICE, 2238c2ecf20Sopenharmony_ci __builtin_return_address(0)); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ioremap); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_civoid __iomem *ioremap_cache(resource_size_t res_cookie, size_t size) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci return __arm_ioremap_caller(res_cookie, size, MT_DEVICE_CACHED, 2308c2ecf20Sopenharmony_ci __builtin_return_address(0)); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ioremap_cache); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_civoid __iomem *ioremap_wc(resource_size_t res_cookie, size_t size) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci return __arm_ioremap_caller(res_cookie, size, MT_DEVICE_WC, 2378c2ecf20Sopenharmony_ci __builtin_return_address(0)); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ioremap_wc); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci#include <asm/mach/map.h> 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_civoid __iomem *pci_remap_cfgspace(resource_size_t res_cookie, size_t size) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci return arch_ioremap_caller(res_cookie, size, MT_UNCACHED, 2488c2ecf20Sopenharmony_ci __builtin_return_address(0)); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_remap_cfgspace); 2518c2ecf20Sopenharmony_ci#endif 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_civoid *arch_memremap_wb(phys_addr_t phys_addr, size_t size) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci return (void *)phys_addr; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_civoid __iounmap(volatile void __iomem *addr) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__iounmap); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_civoid (*arch_iounmap)(volatile void __iomem *); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_civoid iounmap(volatile void __iomem *addr) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iounmap); 269