162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * address space "slices" (meta-segments) support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007 Benjamin Herrenschmidt, IBM Corporation. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on hugetlb implementation 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (C) 2003 David Gibson, IBM Corporation. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#undef DEBUG 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/mm.h> 1662306a36Sopenharmony_ci#include <linux/pagemap.h> 1762306a36Sopenharmony_ci#include <linux/err.h> 1862306a36Sopenharmony_ci#include <linux/spinlock.h> 1962306a36Sopenharmony_ci#include <linux/export.h> 2062306a36Sopenharmony_ci#include <linux/hugetlb.h> 2162306a36Sopenharmony_ci#include <linux/sched/mm.h> 2262306a36Sopenharmony_ci#include <linux/security.h> 2362306a36Sopenharmony_ci#include <asm/mman.h> 2462306a36Sopenharmony_ci#include <asm/mmu.h> 2562306a36Sopenharmony_ci#include <asm/copro.h> 2662306a36Sopenharmony_ci#include <asm/hugetlb.h> 2762306a36Sopenharmony_ci#include <asm/mmu_context.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(slice_convert_lock); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#ifdef DEBUG 3262306a36Sopenharmony_ciint _slice_debug = 1; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic void slice_print_mask(const char *label, const struct slice_mask *mask) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci if (!_slice_debug) 3762306a36Sopenharmony_ci return; 3862306a36Sopenharmony_ci pr_devel("%s low_slice: %*pbl\n", label, 3962306a36Sopenharmony_ci (int)SLICE_NUM_LOW, &mask->low_slices); 4062306a36Sopenharmony_ci pr_devel("%s high_slice: %*pbl\n", label, 4162306a36Sopenharmony_ci (int)SLICE_NUM_HIGH, mask->high_slices); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define slice_dbg(fmt...) do { if (_slice_debug) pr_devel(fmt); } while (0) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#else 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void slice_print_mask(const char *label, const struct slice_mask *mask) {} 4962306a36Sopenharmony_ci#define slice_dbg(fmt...) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#endif 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic inline notrace bool slice_addr_is_low(unsigned long addr) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci u64 tmp = (u64)addr; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return tmp < SLICE_LOW_TOP; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void slice_range_to_mask(unsigned long start, unsigned long len, 6162306a36Sopenharmony_ci struct slice_mask *ret) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci unsigned long end = start + len - 1; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci ret->low_slices = 0; 6662306a36Sopenharmony_ci if (SLICE_NUM_HIGH) 6762306a36Sopenharmony_ci bitmap_zero(ret->high_slices, SLICE_NUM_HIGH); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (slice_addr_is_low(start)) { 7062306a36Sopenharmony_ci unsigned long mend = min(end, 7162306a36Sopenharmony_ci (unsigned long)(SLICE_LOW_TOP - 1)); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ret->low_slices = (1u << (GET_LOW_SLICE_INDEX(mend) + 1)) 7462306a36Sopenharmony_ci - (1u << GET_LOW_SLICE_INDEX(start)); 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (SLICE_NUM_HIGH && !slice_addr_is_low(end)) { 7862306a36Sopenharmony_ci unsigned long start_index = GET_HIGH_SLICE_INDEX(start); 7962306a36Sopenharmony_ci unsigned long align_end = ALIGN(end, (1UL << SLICE_HIGH_SHIFT)); 8062306a36Sopenharmony_ci unsigned long count = GET_HIGH_SLICE_INDEX(align_end) - start_index; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci bitmap_set(ret->high_slices, start_index, count); 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int slice_area_is_free(struct mm_struct *mm, unsigned long addr, 8762306a36Sopenharmony_ci unsigned long len) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct vm_area_struct *vma; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if ((mm_ctx_slb_addr_limit(&mm->context) - len) < addr) 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci vma = find_vma(mm, addr); 9462306a36Sopenharmony_ci return (!vma || (addr + len) <= vm_start_gap(vma)); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int slice_low_has_vma(struct mm_struct *mm, unsigned long slice) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci return !slice_area_is_free(mm, slice << SLICE_LOW_SHIFT, 10062306a36Sopenharmony_ci 1ul << SLICE_LOW_SHIFT); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int slice_high_has_vma(struct mm_struct *mm, unsigned long slice) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci unsigned long start = slice << SLICE_HIGH_SHIFT; 10662306a36Sopenharmony_ci unsigned long end = start + (1ul << SLICE_HIGH_SHIFT); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Hack, so that each addresses is controlled by exactly one 10962306a36Sopenharmony_ci * of the high or low area bitmaps, the first high area starts 11062306a36Sopenharmony_ci * at 4GB, not 0 */ 11162306a36Sopenharmony_ci if (start == 0) 11262306a36Sopenharmony_ci start = (unsigned long)SLICE_LOW_TOP; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return !slice_area_is_free(mm, start, end - start); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void slice_mask_for_free(struct mm_struct *mm, struct slice_mask *ret, 11862306a36Sopenharmony_ci unsigned long high_limit) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci unsigned long i; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci ret->low_slices = 0; 12362306a36Sopenharmony_ci if (SLICE_NUM_HIGH) 12462306a36Sopenharmony_ci bitmap_zero(ret->high_slices, SLICE_NUM_HIGH); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci for (i = 0; i < SLICE_NUM_LOW; i++) 12762306a36Sopenharmony_ci if (!slice_low_has_vma(mm, i)) 12862306a36Sopenharmony_ci ret->low_slices |= 1u << i; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (slice_addr_is_low(high_limit - 1)) 13162306a36Sopenharmony_ci return; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci for (i = 0; i < GET_HIGH_SLICE_INDEX(high_limit); i++) 13462306a36Sopenharmony_ci if (!slice_high_has_vma(mm, i)) 13562306a36Sopenharmony_ci __set_bit(i, ret->high_slices); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic bool slice_check_range_fits(struct mm_struct *mm, 13962306a36Sopenharmony_ci const struct slice_mask *available, 14062306a36Sopenharmony_ci unsigned long start, unsigned long len) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci unsigned long end = start + len - 1; 14362306a36Sopenharmony_ci u64 low_slices = 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (slice_addr_is_low(start)) { 14662306a36Sopenharmony_ci unsigned long mend = min(end, 14762306a36Sopenharmony_ci (unsigned long)(SLICE_LOW_TOP - 1)); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci low_slices = (1u << (GET_LOW_SLICE_INDEX(mend) + 1)) 15062306a36Sopenharmony_ci - (1u << GET_LOW_SLICE_INDEX(start)); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci if ((low_slices & available->low_slices) != low_slices) 15362306a36Sopenharmony_ci return false; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (SLICE_NUM_HIGH && !slice_addr_is_low(end)) { 15662306a36Sopenharmony_ci unsigned long start_index = GET_HIGH_SLICE_INDEX(start); 15762306a36Sopenharmony_ci unsigned long align_end = ALIGN(end, (1UL << SLICE_HIGH_SHIFT)); 15862306a36Sopenharmony_ci unsigned long count = GET_HIGH_SLICE_INDEX(align_end) - start_index; 15962306a36Sopenharmony_ci unsigned long i; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci for (i = start_index; i < start_index + count; i++) { 16262306a36Sopenharmony_ci if (!test_bit(i, available->high_slices)) 16362306a36Sopenharmony_ci return false; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return true; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void slice_flush_segments(void *parm) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci#ifdef CONFIG_PPC64 17362306a36Sopenharmony_ci struct mm_struct *mm = parm; 17462306a36Sopenharmony_ci unsigned long flags; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (mm != current->active_mm) 17762306a36Sopenharmony_ci return; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci copy_mm_to_paca(current->active_mm); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci local_irq_save(flags); 18262306a36Sopenharmony_ci slb_flush_and_restore_bolted(); 18362306a36Sopenharmony_ci local_irq_restore(flags); 18462306a36Sopenharmony_ci#endif 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void slice_convert(struct mm_struct *mm, 18862306a36Sopenharmony_ci const struct slice_mask *mask, int psize) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci int index, mask_index; 19162306a36Sopenharmony_ci /* Write the new slice psize bits */ 19262306a36Sopenharmony_ci unsigned char *hpsizes, *lpsizes; 19362306a36Sopenharmony_ci struct slice_mask *psize_mask, *old_mask; 19462306a36Sopenharmony_ci unsigned long i, flags; 19562306a36Sopenharmony_ci int old_psize; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci slice_dbg("slice_convert(mm=%p, psize=%d)\n", mm, psize); 19862306a36Sopenharmony_ci slice_print_mask(" mask", mask); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci psize_mask = slice_mask_for_size(&mm->context, psize); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* We need to use a spinlock here to protect against 20362306a36Sopenharmony_ci * concurrent 64k -> 4k demotion ... 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci spin_lock_irqsave(&slice_convert_lock, flags); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci lpsizes = mm_ctx_low_slices(&mm->context); 20862306a36Sopenharmony_ci for (i = 0; i < SLICE_NUM_LOW; i++) { 20962306a36Sopenharmony_ci if (!(mask->low_slices & (1u << i))) 21062306a36Sopenharmony_ci continue; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci mask_index = i & 0x1; 21362306a36Sopenharmony_ci index = i >> 1; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Update the slice_mask */ 21662306a36Sopenharmony_ci old_psize = (lpsizes[index] >> (mask_index * 4)) & 0xf; 21762306a36Sopenharmony_ci old_mask = slice_mask_for_size(&mm->context, old_psize); 21862306a36Sopenharmony_ci old_mask->low_slices &= ~(1u << i); 21962306a36Sopenharmony_ci psize_mask->low_slices |= 1u << i; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Update the sizes array */ 22262306a36Sopenharmony_ci lpsizes[index] = (lpsizes[index] & ~(0xf << (mask_index * 4))) | 22362306a36Sopenharmony_ci (((unsigned long)psize) << (mask_index * 4)); 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci hpsizes = mm_ctx_high_slices(&mm->context); 22762306a36Sopenharmony_ci for (i = 0; i < GET_HIGH_SLICE_INDEX(mm_ctx_slb_addr_limit(&mm->context)); i++) { 22862306a36Sopenharmony_ci if (!test_bit(i, mask->high_slices)) 22962306a36Sopenharmony_ci continue; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci mask_index = i & 0x1; 23262306a36Sopenharmony_ci index = i >> 1; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Update the slice_mask */ 23562306a36Sopenharmony_ci old_psize = (hpsizes[index] >> (mask_index * 4)) & 0xf; 23662306a36Sopenharmony_ci old_mask = slice_mask_for_size(&mm->context, old_psize); 23762306a36Sopenharmony_ci __clear_bit(i, old_mask->high_slices); 23862306a36Sopenharmony_ci __set_bit(i, psize_mask->high_slices); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Update the sizes array */ 24162306a36Sopenharmony_ci hpsizes[index] = (hpsizes[index] & ~(0xf << (mask_index * 4))) | 24262306a36Sopenharmony_ci (((unsigned long)psize) << (mask_index * 4)); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci slice_dbg(" lsps=%lx, hsps=%lx\n", 24662306a36Sopenharmony_ci (unsigned long)mm_ctx_low_slices(&mm->context), 24762306a36Sopenharmony_ci (unsigned long)mm_ctx_high_slices(&mm->context)); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci spin_unlock_irqrestore(&slice_convert_lock, flags); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci copro_flush_all_slbs(mm); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/* 25562306a36Sopenharmony_ci * Compute which slice addr is part of; 25662306a36Sopenharmony_ci * set *boundary_addr to the start or end boundary of that slice 25762306a36Sopenharmony_ci * (depending on 'end' parameter); 25862306a36Sopenharmony_ci * return boolean indicating if the slice is marked as available in the 25962306a36Sopenharmony_ci * 'available' slice_mark. 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_cistatic bool slice_scan_available(unsigned long addr, 26262306a36Sopenharmony_ci const struct slice_mask *available, 26362306a36Sopenharmony_ci int end, unsigned long *boundary_addr) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci unsigned long slice; 26662306a36Sopenharmony_ci if (slice_addr_is_low(addr)) { 26762306a36Sopenharmony_ci slice = GET_LOW_SLICE_INDEX(addr); 26862306a36Sopenharmony_ci *boundary_addr = (slice + end) << SLICE_LOW_SHIFT; 26962306a36Sopenharmony_ci return !!(available->low_slices & (1u << slice)); 27062306a36Sopenharmony_ci } else { 27162306a36Sopenharmony_ci slice = GET_HIGH_SLICE_INDEX(addr); 27262306a36Sopenharmony_ci *boundary_addr = (slice + end) ? 27362306a36Sopenharmony_ci ((slice + end) << SLICE_HIGH_SHIFT) : SLICE_LOW_TOP; 27462306a36Sopenharmony_ci return !!test_bit(slice, available->high_slices); 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic unsigned long slice_find_area_bottomup(struct mm_struct *mm, 27962306a36Sopenharmony_ci unsigned long addr, unsigned long len, 28062306a36Sopenharmony_ci const struct slice_mask *available, 28162306a36Sopenharmony_ci int psize, unsigned long high_limit) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT); 28462306a36Sopenharmony_ci unsigned long found, next_end; 28562306a36Sopenharmony_ci struct vm_unmapped_area_info info; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci info.flags = 0; 28862306a36Sopenharmony_ci info.length = len; 28962306a36Sopenharmony_ci info.align_mask = PAGE_MASK & ((1ul << pshift) - 1); 29062306a36Sopenharmony_ci info.align_offset = 0; 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * Check till the allow max value for this mmap request 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci while (addr < high_limit) { 29562306a36Sopenharmony_ci info.low_limit = addr; 29662306a36Sopenharmony_ci if (!slice_scan_available(addr, available, 1, &addr)) 29762306a36Sopenharmony_ci continue; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci next_slice: 30062306a36Sopenharmony_ci /* 30162306a36Sopenharmony_ci * At this point [info.low_limit; addr) covers 30262306a36Sopenharmony_ci * available slices only and ends at a slice boundary. 30362306a36Sopenharmony_ci * Check if we need to reduce the range, or if we can 30462306a36Sopenharmony_ci * extend it to cover the next available slice. 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ci if (addr >= high_limit) 30762306a36Sopenharmony_ci addr = high_limit; 30862306a36Sopenharmony_ci else if (slice_scan_available(addr, available, 1, &next_end)) { 30962306a36Sopenharmony_ci addr = next_end; 31062306a36Sopenharmony_ci goto next_slice; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci info.high_limit = addr; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci found = vm_unmapped_area(&info); 31562306a36Sopenharmony_ci if (!(found & ~PAGE_MASK)) 31662306a36Sopenharmony_ci return found; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return -ENOMEM; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic unsigned long slice_find_area_topdown(struct mm_struct *mm, 32362306a36Sopenharmony_ci unsigned long addr, unsigned long len, 32462306a36Sopenharmony_ci const struct slice_mask *available, 32562306a36Sopenharmony_ci int psize, unsigned long high_limit) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT); 32862306a36Sopenharmony_ci unsigned long found, prev; 32962306a36Sopenharmony_ci struct vm_unmapped_area_info info; 33062306a36Sopenharmony_ci unsigned long min_addr = max(PAGE_SIZE, mmap_min_addr); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci info.flags = VM_UNMAPPED_AREA_TOPDOWN; 33362306a36Sopenharmony_ci info.length = len; 33462306a36Sopenharmony_ci info.align_mask = PAGE_MASK & ((1ul << pshift) - 1); 33562306a36Sopenharmony_ci info.align_offset = 0; 33662306a36Sopenharmony_ci /* 33762306a36Sopenharmony_ci * If we are trying to allocate above DEFAULT_MAP_WINDOW 33862306a36Sopenharmony_ci * Add the different to the mmap_base. 33962306a36Sopenharmony_ci * Only for that request for which high_limit is above 34062306a36Sopenharmony_ci * DEFAULT_MAP_WINDOW we should apply this. 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_ci if (high_limit > DEFAULT_MAP_WINDOW) 34362306a36Sopenharmony_ci addr += mm_ctx_slb_addr_limit(&mm->context) - DEFAULT_MAP_WINDOW; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci while (addr > min_addr) { 34662306a36Sopenharmony_ci info.high_limit = addr; 34762306a36Sopenharmony_ci if (!slice_scan_available(addr - 1, available, 0, &addr)) 34862306a36Sopenharmony_ci continue; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci prev_slice: 35162306a36Sopenharmony_ci /* 35262306a36Sopenharmony_ci * At this point [addr; info.high_limit) covers 35362306a36Sopenharmony_ci * available slices only and starts at a slice boundary. 35462306a36Sopenharmony_ci * Check if we need to reduce the range, or if we can 35562306a36Sopenharmony_ci * extend it to cover the previous available slice. 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_ci if (addr < min_addr) 35862306a36Sopenharmony_ci addr = min_addr; 35962306a36Sopenharmony_ci else if (slice_scan_available(addr - 1, available, 0, &prev)) { 36062306a36Sopenharmony_ci addr = prev; 36162306a36Sopenharmony_ci goto prev_slice; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci info.low_limit = addr; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci found = vm_unmapped_area(&info); 36662306a36Sopenharmony_ci if (!(found & ~PAGE_MASK)) 36762306a36Sopenharmony_ci return found; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* 37162306a36Sopenharmony_ci * A failed mmap() very likely causes application failure, 37262306a36Sopenharmony_ci * so fall back to the bottom-up function here. This scenario 37362306a36Sopenharmony_ci * can happen with large stack limits and large mmap() 37462306a36Sopenharmony_ci * allocations. 37562306a36Sopenharmony_ci */ 37662306a36Sopenharmony_ci return slice_find_area_bottomup(mm, TASK_UNMAPPED_BASE, len, available, psize, high_limit); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic unsigned long slice_find_area(struct mm_struct *mm, unsigned long len, 38162306a36Sopenharmony_ci const struct slice_mask *mask, int psize, 38262306a36Sopenharmony_ci int topdown, unsigned long high_limit) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci if (topdown) 38562306a36Sopenharmony_ci return slice_find_area_topdown(mm, mm->mmap_base, len, mask, psize, high_limit); 38662306a36Sopenharmony_ci else 38762306a36Sopenharmony_ci return slice_find_area_bottomup(mm, mm->mmap_base, len, mask, psize, high_limit); 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic inline void slice_copy_mask(struct slice_mask *dst, 39162306a36Sopenharmony_ci const struct slice_mask *src) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci dst->low_slices = src->low_slices; 39462306a36Sopenharmony_ci if (!SLICE_NUM_HIGH) 39562306a36Sopenharmony_ci return; 39662306a36Sopenharmony_ci bitmap_copy(dst->high_slices, src->high_slices, SLICE_NUM_HIGH); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic inline void slice_or_mask(struct slice_mask *dst, 40062306a36Sopenharmony_ci const struct slice_mask *src1, 40162306a36Sopenharmony_ci const struct slice_mask *src2) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci dst->low_slices = src1->low_slices | src2->low_slices; 40462306a36Sopenharmony_ci if (!SLICE_NUM_HIGH) 40562306a36Sopenharmony_ci return; 40662306a36Sopenharmony_ci bitmap_or(dst->high_slices, src1->high_slices, src2->high_slices, SLICE_NUM_HIGH); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic inline void slice_andnot_mask(struct slice_mask *dst, 41062306a36Sopenharmony_ci const struct slice_mask *src1, 41162306a36Sopenharmony_ci const struct slice_mask *src2) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci dst->low_slices = src1->low_slices & ~src2->low_slices; 41462306a36Sopenharmony_ci if (!SLICE_NUM_HIGH) 41562306a36Sopenharmony_ci return; 41662306a36Sopenharmony_ci bitmap_andnot(dst->high_slices, src1->high_slices, src2->high_slices, SLICE_NUM_HIGH); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci#ifdef CONFIG_PPC_64K_PAGES 42062306a36Sopenharmony_ci#define MMU_PAGE_BASE MMU_PAGE_64K 42162306a36Sopenharmony_ci#else 42262306a36Sopenharmony_ci#define MMU_PAGE_BASE MMU_PAGE_4K 42362306a36Sopenharmony_ci#endif 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ciunsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len, 42662306a36Sopenharmony_ci unsigned long flags, unsigned int psize, 42762306a36Sopenharmony_ci int topdown) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct slice_mask good_mask; 43062306a36Sopenharmony_ci struct slice_mask potential_mask; 43162306a36Sopenharmony_ci const struct slice_mask *maskp; 43262306a36Sopenharmony_ci const struct slice_mask *compat_maskp = NULL; 43362306a36Sopenharmony_ci int fixed = (flags & MAP_FIXED); 43462306a36Sopenharmony_ci int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT); 43562306a36Sopenharmony_ci unsigned long page_size = 1UL << pshift; 43662306a36Sopenharmony_ci struct mm_struct *mm = current->mm; 43762306a36Sopenharmony_ci unsigned long newaddr; 43862306a36Sopenharmony_ci unsigned long high_limit; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci high_limit = DEFAULT_MAP_WINDOW; 44162306a36Sopenharmony_ci if (addr >= high_limit || (fixed && (addr + len > high_limit))) 44262306a36Sopenharmony_ci high_limit = TASK_SIZE; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (len > high_limit) 44562306a36Sopenharmony_ci return -ENOMEM; 44662306a36Sopenharmony_ci if (len & (page_size - 1)) 44762306a36Sopenharmony_ci return -EINVAL; 44862306a36Sopenharmony_ci if (fixed) { 44962306a36Sopenharmony_ci if (addr & (page_size - 1)) 45062306a36Sopenharmony_ci return -EINVAL; 45162306a36Sopenharmony_ci if (addr > high_limit - len) 45262306a36Sopenharmony_ci return -ENOMEM; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (high_limit > mm_ctx_slb_addr_limit(&mm->context)) { 45662306a36Sopenharmony_ci /* 45762306a36Sopenharmony_ci * Increasing the slb_addr_limit does not require 45862306a36Sopenharmony_ci * slice mask cache to be recalculated because it should 45962306a36Sopenharmony_ci * be already initialised beyond the old address limit. 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci mm_ctx_set_slb_addr_limit(&mm->context, high_limit); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci on_each_cpu(slice_flush_segments, mm, 1); 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* Sanity checks */ 46762306a36Sopenharmony_ci BUG_ON(mm->task_size == 0); 46862306a36Sopenharmony_ci BUG_ON(mm_ctx_slb_addr_limit(&mm->context) == 0); 46962306a36Sopenharmony_ci VM_BUG_ON(radix_enabled()); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci slice_dbg("slice_get_unmapped_area(mm=%p, psize=%d...\n", mm, psize); 47262306a36Sopenharmony_ci slice_dbg(" addr=%lx, len=%lx, flags=%lx, topdown=%d\n", 47362306a36Sopenharmony_ci addr, len, flags, topdown); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* If hint, make sure it matches our alignment restrictions */ 47662306a36Sopenharmony_ci if (!fixed && addr) { 47762306a36Sopenharmony_ci addr = ALIGN(addr, page_size); 47862306a36Sopenharmony_ci slice_dbg(" aligned addr=%lx\n", addr); 47962306a36Sopenharmony_ci /* Ignore hint if it's too large or overlaps a VMA */ 48062306a36Sopenharmony_ci if (addr > high_limit - len || addr < mmap_min_addr || 48162306a36Sopenharmony_ci !slice_area_is_free(mm, addr, len)) 48262306a36Sopenharmony_ci addr = 0; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* First make up a "good" mask of slices that have the right size 48662306a36Sopenharmony_ci * already 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_ci maskp = slice_mask_for_size(&mm->context, psize); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* 49162306a36Sopenharmony_ci * Here "good" means slices that are already the right page size, 49262306a36Sopenharmony_ci * "compat" means slices that have a compatible page size (i.e. 49362306a36Sopenharmony_ci * 4k in a 64k pagesize kernel), and "free" means slices without 49462306a36Sopenharmony_ci * any VMAs. 49562306a36Sopenharmony_ci * 49662306a36Sopenharmony_ci * If MAP_FIXED: 49762306a36Sopenharmony_ci * check if fits in good | compat => OK 49862306a36Sopenharmony_ci * check if fits in good | compat | free => convert free 49962306a36Sopenharmony_ci * else bad 50062306a36Sopenharmony_ci * If have hint: 50162306a36Sopenharmony_ci * check if hint fits in good => OK 50262306a36Sopenharmony_ci * check if hint fits in good | free => convert free 50362306a36Sopenharmony_ci * Otherwise: 50462306a36Sopenharmony_ci * search in good, found => OK 50562306a36Sopenharmony_ci * search in good | free, found => convert free 50662306a36Sopenharmony_ci * search in good | compat | free, found => convert free. 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* 51062306a36Sopenharmony_ci * If we support combo pages, we can allow 64k pages in 4k slices 51162306a36Sopenharmony_ci * The mask copies could be avoided in most cases here if we had 51262306a36Sopenharmony_ci * a pointer to good mask for the next code to use. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_64K_PAGES) && psize == MMU_PAGE_64K) { 51562306a36Sopenharmony_ci compat_maskp = slice_mask_for_size(&mm->context, MMU_PAGE_4K); 51662306a36Sopenharmony_ci if (fixed) 51762306a36Sopenharmony_ci slice_or_mask(&good_mask, maskp, compat_maskp); 51862306a36Sopenharmony_ci else 51962306a36Sopenharmony_ci slice_copy_mask(&good_mask, maskp); 52062306a36Sopenharmony_ci } else { 52162306a36Sopenharmony_ci slice_copy_mask(&good_mask, maskp); 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci slice_print_mask(" good_mask", &good_mask); 52562306a36Sopenharmony_ci if (compat_maskp) 52662306a36Sopenharmony_ci slice_print_mask(" compat_mask", compat_maskp); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* First check hint if it's valid or if we have MAP_FIXED */ 52962306a36Sopenharmony_ci if (addr != 0 || fixed) { 53062306a36Sopenharmony_ci /* Check if we fit in the good mask. If we do, we just return, 53162306a36Sopenharmony_ci * nothing else to do 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_ci if (slice_check_range_fits(mm, &good_mask, addr, len)) { 53462306a36Sopenharmony_ci slice_dbg(" fits good !\n"); 53562306a36Sopenharmony_ci newaddr = addr; 53662306a36Sopenharmony_ci goto return_addr; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci } else { 53962306a36Sopenharmony_ci /* Now let's see if we can find something in the existing 54062306a36Sopenharmony_ci * slices for that size 54162306a36Sopenharmony_ci */ 54262306a36Sopenharmony_ci newaddr = slice_find_area(mm, len, &good_mask, 54362306a36Sopenharmony_ci psize, topdown, high_limit); 54462306a36Sopenharmony_ci if (newaddr != -ENOMEM) { 54562306a36Sopenharmony_ci /* Found within the good mask, we don't have to setup, 54662306a36Sopenharmony_ci * we thus return directly 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci slice_dbg(" found area at 0x%lx\n", newaddr); 54962306a36Sopenharmony_ci goto return_addr; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci /* 55362306a36Sopenharmony_ci * We don't fit in the good mask, check what other slices are 55462306a36Sopenharmony_ci * empty and thus can be converted 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci slice_mask_for_free(mm, &potential_mask, high_limit); 55762306a36Sopenharmony_ci slice_or_mask(&potential_mask, &potential_mask, &good_mask); 55862306a36Sopenharmony_ci slice_print_mask(" potential", &potential_mask); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (addr != 0 || fixed) { 56162306a36Sopenharmony_ci if (slice_check_range_fits(mm, &potential_mask, addr, len)) { 56262306a36Sopenharmony_ci slice_dbg(" fits potential !\n"); 56362306a36Sopenharmony_ci newaddr = addr; 56462306a36Sopenharmony_ci goto convert; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* If we have MAP_FIXED and failed the above steps, then error out */ 56962306a36Sopenharmony_ci if (fixed) 57062306a36Sopenharmony_ci return -EBUSY; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci slice_dbg(" search...\n"); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* If we had a hint that didn't work out, see if we can fit 57562306a36Sopenharmony_ci * anywhere in the good area. 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_ci if (addr) { 57862306a36Sopenharmony_ci newaddr = slice_find_area(mm, len, &good_mask, 57962306a36Sopenharmony_ci psize, topdown, high_limit); 58062306a36Sopenharmony_ci if (newaddr != -ENOMEM) { 58162306a36Sopenharmony_ci slice_dbg(" found area at 0x%lx\n", newaddr); 58262306a36Sopenharmony_ci goto return_addr; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* Now let's see if we can find something in the existing slices 58762306a36Sopenharmony_ci * for that size plus free slices 58862306a36Sopenharmony_ci */ 58962306a36Sopenharmony_ci newaddr = slice_find_area(mm, len, &potential_mask, 59062306a36Sopenharmony_ci psize, topdown, high_limit); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_64K_PAGES) && newaddr == -ENOMEM && 59362306a36Sopenharmony_ci psize == MMU_PAGE_64K) { 59462306a36Sopenharmony_ci /* retry the search with 4k-page slices included */ 59562306a36Sopenharmony_ci slice_or_mask(&potential_mask, &potential_mask, compat_maskp); 59662306a36Sopenharmony_ci newaddr = slice_find_area(mm, len, &potential_mask, 59762306a36Sopenharmony_ci psize, topdown, high_limit); 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (newaddr == -ENOMEM) 60162306a36Sopenharmony_ci return -ENOMEM; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci slice_range_to_mask(newaddr, len, &potential_mask); 60462306a36Sopenharmony_ci slice_dbg(" found potential area at 0x%lx\n", newaddr); 60562306a36Sopenharmony_ci slice_print_mask(" mask", &potential_mask); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci convert: 60862306a36Sopenharmony_ci /* 60962306a36Sopenharmony_ci * Try to allocate the context before we do slice convert 61062306a36Sopenharmony_ci * so that we handle the context allocation failure gracefully. 61162306a36Sopenharmony_ci */ 61262306a36Sopenharmony_ci if (need_extra_context(mm, newaddr)) { 61362306a36Sopenharmony_ci if (alloc_extended_context(mm, newaddr) < 0) 61462306a36Sopenharmony_ci return -ENOMEM; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci slice_andnot_mask(&potential_mask, &potential_mask, &good_mask); 61862306a36Sopenharmony_ci if (compat_maskp && !fixed) 61962306a36Sopenharmony_ci slice_andnot_mask(&potential_mask, &potential_mask, compat_maskp); 62062306a36Sopenharmony_ci if (potential_mask.low_slices || 62162306a36Sopenharmony_ci (SLICE_NUM_HIGH && 62262306a36Sopenharmony_ci !bitmap_empty(potential_mask.high_slices, SLICE_NUM_HIGH))) { 62362306a36Sopenharmony_ci slice_convert(mm, &potential_mask, psize); 62462306a36Sopenharmony_ci if (psize > MMU_PAGE_BASE) 62562306a36Sopenharmony_ci on_each_cpu(slice_flush_segments, mm, 1); 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci return newaddr; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cireturn_addr: 63062306a36Sopenharmony_ci if (need_extra_context(mm, newaddr)) { 63162306a36Sopenharmony_ci if (alloc_extended_context(mm, newaddr) < 0) 63262306a36Sopenharmony_ci return -ENOMEM; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci return newaddr; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(slice_get_unmapped_area); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ciunsigned long arch_get_unmapped_area(struct file *filp, 63962306a36Sopenharmony_ci unsigned long addr, 64062306a36Sopenharmony_ci unsigned long len, 64162306a36Sopenharmony_ci unsigned long pgoff, 64262306a36Sopenharmony_ci unsigned long flags) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci if (radix_enabled()) 64562306a36Sopenharmony_ci return generic_get_unmapped_area(filp, addr, len, pgoff, flags); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci return slice_get_unmapped_area(addr, len, flags, 64862306a36Sopenharmony_ci mm_ctx_user_psize(¤t->mm->context), 0); 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ciunsigned long arch_get_unmapped_area_topdown(struct file *filp, 65262306a36Sopenharmony_ci const unsigned long addr0, 65362306a36Sopenharmony_ci const unsigned long len, 65462306a36Sopenharmony_ci const unsigned long pgoff, 65562306a36Sopenharmony_ci const unsigned long flags) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci if (radix_enabled()) 65862306a36Sopenharmony_ci return generic_get_unmapped_area_topdown(filp, addr0, len, pgoff, flags); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci return slice_get_unmapped_area(addr0, len, flags, 66162306a36Sopenharmony_ci mm_ctx_user_psize(¤t->mm->context), 1); 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ciunsigned int notrace get_slice_psize(struct mm_struct *mm, unsigned long addr) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci unsigned char *psizes; 66762306a36Sopenharmony_ci int index, mask_index; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci VM_BUG_ON(radix_enabled()); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (slice_addr_is_low(addr)) { 67262306a36Sopenharmony_ci psizes = mm_ctx_low_slices(&mm->context); 67362306a36Sopenharmony_ci index = GET_LOW_SLICE_INDEX(addr); 67462306a36Sopenharmony_ci } else { 67562306a36Sopenharmony_ci psizes = mm_ctx_high_slices(&mm->context); 67662306a36Sopenharmony_ci index = GET_HIGH_SLICE_INDEX(addr); 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci mask_index = index & 0x1; 67962306a36Sopenharmony_ci return (psizes[index >> 1] >> (mask_index * 4)) & 0xf; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(get_slice_psize); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_civoid slice_init_new_context_exec(struct mm_struct *mm) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci unsigned char *hpsizes, *lpsizes; 68662306a36Sopenharmony_ci struct slice_mask *mask; 68762306a36Sopenharmony_ci unsigned int psize = mmu_virtual_psize; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci slice_dbg("slice_init_new_context_exec(mm=%p)\n", mm); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* 69262306a36Sopenharmony_ci * In the case of exec, use the default limit. In the 69362306a36Sopenharmony_ci * case of fork it is just inherited from the mm being 69462306a36Sopenharmony_ci * duplicated. 69562306a36Sopenharmony_ci */ 69662306a36Sopenharmony_ci mm_ctx_set_slb_addr_limit(&mm->context, SLB_ADDR_LIMIT_DEFAULT); 69762306a36Sopenharmony_ci mm_ctx_set_user_psize(&mm->context, psize); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* 70062306a36Sopenharmony_ci * Set all slice psizes to the default. 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_ci lpsizes = mm_ctx_low_slices(&mm->context); 70362306a36Sopenharmony_ci memset(lpsizes, (psize << 4) | psize, SLICE_NUM_LOW >> 1); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci hpsizes = mm_ctx_high_slices(&mm->context); 70662306a36Sopenharmony_ci memset(hpsizes, (psize << 4) | psize, SLICE_NUM_HIGH >> 1); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* 70962306a36Sopenharmony_ci * Slice mask cache starts zeroed, fill the default size cache. 71062306a36Sopenharmony_ci */ 71162306a36Sopenharmony_ci mask = slice_mask_for_size(&mm->context, psize); 71262306a36Sopenharmony_ci mask->low_slices = ~0UL; 71362306a36Sopenharmony_ci if (SLICE_NUM_HIGH) 71462306a36Sopenharmony_ci bitmap_fill(mask->high_slices, SLICE_NUM_HIGH); 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_civoid slice_setup_new_exec(void) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct mm_struct *mm = current->mm; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci slice_dbg("slice_setup_new_exec(mm=%p)\n", mm); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (!is_32bit_task()) 72462306a36Sopenharmony_ci return; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci mm_ctx_set_slb_addr_limit(&mm->context, DEFAULT_MAP_WINDOW); 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_civoid slice_set_range_psize(struct mm_struct *mm, unsigned long start, 73062306a36Sopenharmony_ci unsigned long len, unsigned int psize) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci struct slice_mask mask; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci VM_BUG_ON(radix_enabled()); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci slice_range_to_mask(start, len, &mask); 73762306a36Sopenharmony_ci slice_convert(mm, &mask, psize); 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci#ifdef CONFIG_HUGETLB_PAGE 74162306a36Sopenharmony_ci/* 74262306a36Sopenharmony_ci * is_hugepage_only_range() is used by generic code to verify whether 74362306a36Sopenharmony_ci * a normal mmap mapping (non hugetlbfs) is valid on a given area. 74462306a36Sopenharmony_ci * 74562306a36Sopenharmony_ci * until the generic code provides a more generic hook and/or starts 74662306a36Sopenharmony_ci * calling arch get_unmapped_area for MAP_FIXED (which our implementation 74762306a36Sopenharmony_ci * here knows how to deal with), we hijack it to keep standard mappings 74862306a36Sopenharmony_ci * away from us. 74962306a36Sopenharmony_ci * 75062306a36Sopenharmony_ci * because of that generic code limitation, MAP_FIXED mapping cannot 75162306a36Sopenharmony_ci * "convert" back a slice with no VMAs to the standard page size, only 75262306a36Sopenharmony_ci * get_unmapped_area() can. It would be possible to fix it here but I 75362306a36Sopenharmony_ci * prefer working on fixing the generic code instead. 75462306a36Sopenharmony_ci * 75562306a36Sopenharmony_ci * WARNING: This will not work if hugetlbfs isn't enabled since the 75662306a36Sopenharmony_ci * generic code will redefine that function as 0 in that. This is ok 75762306a36Sopenharmony_ci * for now as we only use slices with hugetlbfs enabled. This should 75862306a36Sopenharmony_ci * be fixed as the generic code gets fixed. 75962306a36Sopenharmony_ci */ 76062306a36Sopenharmony_ciint slice_is_hugepage_only_range(struct mm_struct *mm, unsigned long addr, 76162306a36Sopenharmony_ci unsigned long len) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci const struct slice_mask *maskp; 76462306a36Sopenharmony_ci unsigned int psize = mm_ctx_user_psize(&mm->context); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci VM_BUG_ON(radix_enabled()); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci maskp = slice_mask_for_size(&mm->context, psize); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* We need to account for 4k slices too */ 77162306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_64K_PAGES) && psize == MMU_PAGE_64K) { 77262306a36Sopenharmony_ci const struct slice_mask *compat_maskp; 77362306a36Sopenharmony_ci struct slice_mask available; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci compat_maskp = slice_mask_for_size(&mm->context, MMU_PAGE_4K); 77662306a36Sopenharmony_ci slice_or_mask(&available, maskp, compat_maskp); 77762306a36Sopenharmony_ci return !slice_check_range_fits(mm, &available, addr, len); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci return !slice_check_range_fits(mm, maskp, addr, len); 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ciunsigned long vma_mmu_pagesize(struct vm_area_struct *vma) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci /* With radix we don't use slice, so derive it from vma*/ 78662306a36Sopenharmony_ci if (radix_enabled()) 78762306a36Sopenharmony_ci return vma_kernel_pagesize(vma); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci return 1UL << mmu_psize_to_shift(get_slice_psize(vma->vm_mm, vma->vm_start)); 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic int file_to_psize(struct file *file) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci struct hstate *hstate = hstate_file(file); 79562306a36Sopenharmony_ci return shift_to_mmu_psize(huge_page_shift(hstate)); 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ciunsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, 79962306a36Sopenharmony_ci unsigned long len, unsigned long pgoff, 80062306a36Sopenharmony_ci unsigned long flags) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci if (radix_enabled()) 80362306a36Sopenharmony_ci return generic_hugetlb_get_unmapped_area(file, addr, len, pgoff, flags); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci return slice_get_unmapped_area(addr, len, flags, file_to_psize(file), 1); 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci#endif 808