18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 38c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 48c2ecf20Sopenharmony_ci * for more details. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2011 Wind River Systems, 78c2ecf20Sopenharmony_ci * written by Ralf Baechle <ralf@linux-mips.org> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/compiler.h> 108c2ecf20Sopenharmony_ci#include <linux/elf-randomize.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/mm.h> 138c2ecf20Sopenharmony_ci#include <linux/mman.h> 148c2ecf20Sopenharmony_ci#include <linux/export.h> 158c2ecf20Sopenharmony_ci#include <linux/personality.h> 168c2ecf20Sopenharmony_ci#include <linux/random.h> 178c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 188c2ecf20Sopenharmony_ci#include <linux/sched/mm.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciunsigned long shm_align_mask = PAGE_SIZE - 1; /* Sane caches */ 218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(shm_align_mask); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define COLOUR_ALIGN(addr, pgoff) \ 248c2ecf20Sopenharmony_ci ((((addr) + shm_align_mask) & ~shm_align_mask) + \ 258c2ecf20Sopenharmony_ci (((pgoff) << PAGE_SHIFT) & shm_align_mask)) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cienum mmap_allocation_direction {UP, DOWN}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic unsigned long arch_get_unmapped_area_common(struct file *filp, 308c2ecf20Sopenharmony_ci unsigned long addr0, unsigned long len, unsigned long pgoff, 318c2ecf20Sopenharmony_ci unsigned long flags, enum mmap_allocation_direction dir) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct mm_struct *mm = current->mm; 348c2ecf20Sopenharmony_ci struct vm_area_struct *vma; 358c2ecf20Sopenharmony_ci unsigned long addr = addr0; 368c2ecf20Sopenharmony_ci int do_color_align; 378c2ecf20Sopenharmony_ci struct vm_unmapped_area_info info; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (unlikely(len > TASK_SIZE)) 408c2ecf20Sopenharmony_ci return -ENOMEM; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (flags & MAP_FIXED) { 438c2ecf20Sopenharmony_ci /* Even MAP_FIXED mappings must reside within TASK_SIZE */ 448c2ecf20Sopenharmony_ci if (TASK_SIZE - len < addr) 458c2ecf20Sopenharmony_ci return -EINVAL; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci /* 488c2ecf20Sopenharmony_ci * We do not accept a shared mapping if it would violate 498c2ecf20Sopenharmony_ci * cache aliasing constraints. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci if ((flags & MAP_SHARED) && 528c2ecf20Sopenharmony_ci ((addr - (pgoff << PAGE_SHIFT)) & shm_align_mask)) 538c2ecf20Sopenharmony_ci return -EINVAL; 548c2ecf20Sopenharmony_ci return addr; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci do_color_align = 0; 588c2ecf20Sopenharmony_ci if (filp || (flags & MAP_SHARED)) 598c2ecf20Sopenharmony_ci do_color_align = 1; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* requesting a specific address */ 628c2ecf20Sopenharmony_ci if (addr) { 638c2ecf20Sopenharmony_ci if (do_color_align) 648c2ecf20Sopenharmony_ci addr = COLOUR_ALIGN(addr, pgoff); 658c2ecf20Sopenharmony_ci else 668c2ecf20Sopenharmony_ci addr = PAGE_ALIGN(addr); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci vma = find_vma(mm, addr); 698c2ecf20Sopenharmony_ci if (TASK_SIZE - len >= addr && 708c2ecf20Sopenharmony_ci (!vma || addr + len <= vm_start_gap(vma))) 718c2ecf20Sopenharmony_ci return addr; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci info.length = len; 758c2ecf20Sopenharmony_ci info.align_mask = do_color_align ? (PAGE_MASK & shm_align_mask) : 0; 768c2ecf20Sopenharmony_ci info.align_offset = pgoff << PAGE_SHIFT; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (dir == DOWN) { 798c2ecf20Sopenharmony_ci info.flags = VM_UNMAPPED_AREA_TOPDOWN; 808c2ecf20Sopenharmony_ci info.low_limit = PAGE_SIZE; 818c2ecf20Sopenharmony_ci info.high_limit = mm->mmap_base; 828c2ecf20Sopenharmony_ci addr = vm_unmapped_area(&info); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (!(addr & ~PAGE_MASK)) 858c2ecf20Sopenharmony_ci return addr; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* 888c2ecf20Sopenharmony_ci * A failed mmap() very likely causes application failure, 898c2ecf20Sopenharmony_ci * so fall back to the bottom-up function here. This scenario 908c2ecf20Sopenharmony_ci * can happen with large stack limits and large mmap() 918c2ecf20Sopenharmony_ci * allocations. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci info.flags = 0; 968c2ecf20Sopenharmony_ci info.low_limit = mm->mmap_base; 978c2ecf20Sopenharmony_ci info.high_limit = TASK_SIZE; 988c2ecf20Sopenharmony_ci return vm_unmapped_area(&info); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ciunsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr0, 1028c2ecf20Sopenharmony_ci unsigned long len, unsigned long pgoff, unsigned long flags) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci return arch_get_unmapped_area_common(filp, 1058c2ecf20Sopenharmony_ci addr0, len, pgoff, flags, UP); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* 1098c2ecf20Sopenharmony_ci * There is no need to export this but sched.h declares the function as 1108c2ecf20Sopenharmony_ci * extern so making it static here results in an error. 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_ciunsigned long arch_get_unmapped_area_topdown(struct file *filp, 1138c2ecf20Sopenharmony_ci unsigned long addr0, unsigned long len, unsigned long pgoff, 1148c2ecf20Sopenharmony_ci unsigned long flags) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci return arch_get_unmapped_area_common(filp, 1178c2ecf20Sopenharmony_ci addr0, len, pgoff, flags, DOWN); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cibool __virt_addr_valid(const volatile void *kaddr) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci unsigned long vaddr = (unsigned long)kaddr; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if ((vaddr < PAGE_OFFSET) || (vaddr >= MAP_BASE)) 1258c2ecf20Sopenharmony_ci return false; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return pfn_valid(PFN_DOWN(virt_to_phys(kaddr))); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__virt_addr_valid); 130