11408fca0SChristophe Leroy // SPDX-License-Identifier: GPL-2.0-or-later
21408fca0SChristophe Leroy /*
31408fca0SChristophe Leroy * address space "slices" (meta-segments) support
41408fca0SChristophe Leroy *
51408fca0SChristophe Leroy * Copyright (C) 2007 Benjamin Herrenschmidt, IBM Corporation.
61408fca0SChristophe Leroy *
71408fca0SChristophe Leroy * Based on hugetlb implementation
81408fca0SChristophe Leroy *
91408fca0SChristophe Leroy * Copyright (C) 2003 David Gibson, IBM Corporation.
101408fca0SChristophe Leroy */
111408fca0SChristophe Leroy
121408fca0SChristophe Leroy #undef DEBUG
131408fca0SChristophe Leroy
141408fca0SChristophe Leroy #include <linux/kernel.h>
151408fca0SChristophe Leroy #include <linux/mm.h>
161408fca0SChristophe Leroy #include <linux/pagemap.h>
171408fca0SChristophe Leroy #include <linux/err.h>
181408fca0SChristophe Leroy #include <linux/spinlock.h>
191408fca0SChristophe Leroy #include <linux/export.h>
201408fca0SChristophe Leroy #include <linux/hugetlb.h>
211408fca0SChristophe Leroy #include <linux/sched/mm.h>
221408fca0SChristophe Leroy #include <linux/security.h>
231408fca0SChristophe Leroy #include <asm/mman.h>
241408fca0SChristophe Leroy #include <asm/mmu.h>
251408fca0SChristophe Leroy #include <asm/copro.h>
261408fca0SChristophe Leroy #include <asm/hugetlb.h>
271408fca0SChristophe Leroy #include <asm/mmu_context.h>
281408fca0SChristophe Leroy
291408fca0SChristophe Leroy static DEFINE_SPINLOCK(slice_convert_lock);
301408fca0SChristophe Leroy
311408fca0SChristophe Leroy #ifdef DEBUG
321408fca0SChristophe Leroy int _slice_debug = 1;
331408fca0SChristophe Leroy
slice_print_mask(const char * label,const struct slice_mask * mask)341408fca0SChristophe Leroy static void slice_print_mask(const char *label, const struct slice_mask *mask)
351408fca0SChristophe Leroy {
361408fca0SChristophe Leroy if (!_slice_debug)
371408fca0SChristophe Leroy return;
381408fca0SChristophe Leroy pr_devel("%s low_slice: %*pbl\n", label,
391408fca0SChristophe Leroy (int)SLICE_NUM_LOW, &mask->low_slices);
401408fca0SChristophe Leroy pr_devel("%s high_slice: %*pbl\n", label,
411408fca0SChristophe Leroy (int)SLICE_NUM_HIGH, mask->high_slices);
421408fca0SChristophe Leroy }
431408fca0SChristophe Leroy
441408fca0SChristophe Leroy #define slice_dbg(fmt...) do { if (_slice_debug) pr_devel(fmt); } while (0)
451408fca0SChristophe Leroy
461408fca0SChristophe Leroy #else
471408fca0SChristophe Leroy
slice_print_mask(const char * label,const struct slice_mask * mask)481408fca0SChristophe Leroy static void slice_print_mask(const char *label, const struct slice_mask *mask) {}
491408fca0SChristophe Leroy #define slice_dbg(fmt...)
501408fca0SChristophe Leroy
511408fca0SChristophe Leroy #endif
521408fca0SChristophe Leroy
slice_addr_is_low(unsigned long addr)531408fca0SChristophe Leroy static inline notrace bool slice_addr_is_low(unsigned long addr)
541408fca0SChristophe Leroy {
551408fca0SChristophe Leroy u64 tmp = (u64)addr;
561408fca0SChristophe Leroy
571408fca0SChristophe Leroy return tmp < SLICE_LOW_TOP;
581408fca0SChristophe Leroy }
591408fca0SChristophe Leroy
slice_range_to_mask(unsigned long start,unsigned long len,struct slice_mask * ret)601408fca0SChristophe Leroy static void slice_range_to_mask(unsigned long start, unsigned long len,
611408fca0SChristophe Leroy struct slice_mask *ret)
621408fca0SChristophe Leroy {
631408fca0SChristophe Leroy unsigned long end = start + len - 1;
641408fca0SChristophe Leroy
651408fca0SChristophe Leroy ret->low_slices = 0;
661408fca0SChristophe Leroy if (SLICE_NUM_HIGH)
671408fca0SChristophe Leroy bitmap_zero(ret->high_slices, SLICE_NUM_HIGH);
681408fca0SChristophe Leroy
691408fca0SChristophe Leroy if (slice_addr_is_low(start)) {
701408fca0SChristophe Leroy unsigned long mend = min(end,
711408fca0SChristophe Leroy (unsigned long)(SLICE_LOW_TOP - 1));
721408fca0SChristophe Leroy
731408fca0SChristophe Leroy ret->low_slices = (1u << (GET_LOW_SLICE_INDEX(mend) + 1))
741408fca0SChristophe Leroy - (1u << GET_LOW_SLICE_INDEX(start));
751408fca0SChristophe Leroy }
761408fca0SChristophe Leroy
771408fca0SChristophe Leroy if (SLICE_NUM_HIGH && !slice_addr_is_low(end)) {
781408fca0SChristophe Leroy unsigned long start_index = GET_HIGH_SLICE_INDEX(start);
791408fca0SChristophe Leroy unsigned long align_end = ALIGN(end, (1UL << SLICE_HIGH_SHIFT));
801408fca0SChristophe Leroy unsigned long count = GET_HIGH_SLICE_INDEX(align_end) - start_index;
811408fca0SChristophe Leroy
821408fca0SChristophe Leroy bitmap_set(ret->high_slices, start_index, count);
831408fca0SChristophe Leroy }
841408fca0SChristophe Leroy }
851408fca0SChristophe Leroy
slice_area_is_free(struct mm_struct * mm,unsigned long addr,unsigned long len)861408fca0SChristophe Leroy static int slice_area_is_free(struct mm_struct *mm, unsigned long addr,
871408fca0SChristophe Leroy unsigned long len)
881408fca0SChristophe Leroy {
891408fca0SChristophe Leroy struct vm_area_struct *vma;
901408fca0SChristophe Leroy
911408fca0SChristophe Leroy if ((mm_ctx_slb_addr_limit(&mm->context) - len) < addr)
921408fca0SChristophe Leroy return 0;
931408fca0SChristophe Leroy vma = find_vma(mm, addr);
941408fca0SChristophe Leroy return (!vma || (addr + len) <= vm_start_gap(vma));
951408fca0SChristophe Leroy }
961408fca0SChristophe Leroy
slice_low_has_vma(struct mm_struct * mm,unsigned long slice)971408fca0SChristophe Leroy static int slice_low_has_vma(struct mm_struct *mm, unsigned long slice)
981408fca0SChristophe Leroy {
991408fca0SChristophe Leroy return !slice_area_is_free(mm, slice << SLICE_LOW_SHIFT,
1001408fca0SChristophe Leroy 1ul << SLICE_LOW_SHIFT);
1011408fca0SChristophe Leroy }
1021408fca0SChristophe Leroy
slice_high_has_vma(struct mm_struct * mm,unsigned long slice)1031408fca0SChristophe Leroy static int slice_high_has_vma(struct mm_struct *mm, unsigned long slice)
1041408fca0SChristophe Leroy {
1051408fca0SChristophe Leroy unsigned long start = slice << SLICE_HIGH_SHIFT;
1061408fca0SChristophe Leroy unsigned long end = start + (1ul << SLICE_HIGH_SHIFT);
1071408fca0SChristophe Leroy
1081408fca0SChristophe Leroy /* Hack, so that each addresses is controlled by exactly one
1091408fca0SChristophe Leroy * of the high or low area bitmaps, the first high area starts
1101408fca0SChristophe Leroy * at 4GB, not 0 */
1111408fca0SChristophe Leroy if (start == 0)
1121408fca0SChristophe Leroy start = (unsigned long)SLICE_LOW_TOP;
1131408fca0SChristophe Leroy
1141408fca0SChristophe Leroy return !slice_area_is_free(mm, start, end - start);
1151408fca0SChristophe Leroy }
1161408fca0SChristophe Leroy
slice_mask_for_free(struct mm_struct * mm,struct slice_mask * ret,unsigned long high_limit)1171408fca0SChristophe Leroy static void slice_mask_for_free(struct mm_struct *mm, struct slice_mask *ret,
1181408fca0SChristophe Leroy unsigned long high_limit)
1191408fca0SChristophe Leroy {
1201408fca0SChristophe Leroy unsigned long i;
1211408fca0SChristophe Leroy
1221408fca0SChristophe Leroy ret->low_slices = 0;
1231408fca0SChristophe Leroy if (SLICE_NUM_HIGH)
1241408fca0SChristophe Leroy bitmap_zero(ret->high_slices, SLICE_NUM_HIGH);
1251408fca0SChristophe Leroy
1261408fca0SChristophe Leroy for (i = 0; i < SLICE_NUM_LOW; i++)
1271408fca0SChristophe Leroy if (!slice_low_has_vma(mm, i))
1281408fca0SChristophe Leroy ret->low_slices |= 1u << i;
1291408fca0SChristophe Leroy
1301408fca0SChristophe Leroy if (slice_addr_is_low(high_limit - 1))
1311408fca0SChristophe Leroy return;
1321408fca0SChristophe Leroy
1331408fca0SChristophe Leroy for (i = 0; i < GET_HIGH_SLICE_INDEX(high_limit); i++)
1341408fca0SChristophe Leroy if (!slice_high_has_vma(mm, i))
1351408fca0SChristophe Leroy __set_bit(i, ret->high_slices);
1361408fca0SChristophe Leroy }
1371408fca0SChristophe Leroy
slice_check_range_fits(struct mm_struct * mm,const struct slice_mask * available,unsigned long start,unsigned long len)1381408fca0SChristophe Leroy static bool slice_check_range_fits(struct mm_struct *mm,
1391408fca0SChristophe Leroy const struct slice_mask *available,
1401408fca0SChristophe Leroy unsigned long start, unsigned long len)
1411408fca0SChristophe Leroy {
1421408fca0SChristophe Leroy unsigned long end = start + len - 1;
1431408fca0SChristophe Leroy u64 low_slices = 0;
1441408fca0SChristophe Leroy
1451408fca0SChristophe Leroy if (slice_addr_is_low(start)) {
1461408fca0SChristophe Leroy unsigned long mend = min(end,
1471408fca0SChristophe Leroy (unsigned long)(SLICE_LOW_TOP - 1));
1481408fca0SChristophe Leroy
1491408fca0SChristophe Leroy low_slices = (1u << (GET_LOW_SLICE_INDEX(mend) + 1))
1501408fca0SChristophe Leroy - (1u << GET_LOW_SLICE_INDEX(start));
1511408fca0SChristophe Leroy }
1521408fca0SChristophe Leroy if ((low_slices & available->low_slices) != low_slices)
1531408fca0SChristophe Leroy return false;
1541408fca0SChristophe Leroy
1551408fca0SChristophe Leroy if (SLICE_NUM_HIGH && !slice_addr_is_low(end)) {
1561408fca0SChristophe Leroy unsigned long start_index = GET_HIGH_SLICE_INDEX(start);
1571408fca0SChristophe Leroy unsigned long align_end = ALIGN(end, (1UL << SLICE_HIGH_SHIFT));
1581408fca0SChristophe Leroy unsigned long count = GET_HIGH_SLICE_INDEX(align_end) - start_index;
1591408fca0SChristophe Leroy unsigned long i;
1601408fca0SChristophe Leroy
1611408fca0SChristophe Leroy for (i = start_index; i < start_index + count; i++) {
1621408fca0SChristophe Leroy if (!test_bit(i, available->high_slices))
1631408fca0SChristophe Leroy return false;
1641408fca0SChristophe Leroy }
1651408fca0SChristophe Leroy }
1661408fca0SChristophe Leroy
1671408fca0SChristophe Leroy return true;
1681408fca0SChristophe Leroy }
1691408fca0SChristophe Leroy
slice_flush_segments(void * parm)1701408fca0SChristophe Leroy static void slice_flush_segments(void *parm)
1711408fca0SChristophe Leroy {
1721408fca0SChristophe Leroy #ifdef CONFIG_PPC64
1731408fca0SChristophe Leroy struct mm_struct *mm = parm;
1741408fca0SChristophe Leroy unsigned long flags;
1751408fca0SChristophe Leroy
1761408fca0SChristophe Leroy if (mm != current->active_mm)
1771408fca0SChristophe Leroy return;
1781408fca0SChristophe Leroy
1791408fca0SChristophe Leroy copy_mm_to_paca(current->active_mm);
1801408fca0SChristophe Leroy
1811408fca0SChristophe Leroy local_irq_save(flags);
1821408fca0SChristophe Leroy slb_flush_and_restore_bolted();
1831408fca0SChristophe Leroy local_irq_restore(flags);
1841408fca0SChristophe Leroy #endif
1851408fca0SChristophe Leroy }
1861408fca0SChristophe Leroy
slice_convert(struct mm_struct * mm,const struct slice_mask * mask,int psize)1871408fca0SChristophe Leroy static void slice_convert(struct mm_struct *mm,
1881408fca0SChristophe Leroy const struct slice_mask *mask, int psize)
1891408fca0SChristophe Leroy {
1901408fca0SChristophe Leroy int index, mask_index;
1911408fca0SChristophe Leroy /* Write the new slice psize bits */
1921408fca0SChristophe Leroy unsigned char *hpsizes, *lpsizes;
1931408fca0SChristophe Leroy struct slice_mask *psize_mask, *old_mask;
1941408fca0SChristophe Leroy unsigned long i, flags;
1951408fca0SChristophe Leroy int old_psize;
1961408fca0SChristophe Leroy
1971408fca0SChristophe Leroy slice_dbg("slice_convert(mm=%p, psize=%d)\n", mm, psize);
1981408fca0SChristophe Leroy slice_print_mask(" mask", mask);
1991408fca0SChristophe Leroy
2001408fca0SChristophe Leroy psize_mask = slice_mask_for_size(&mm->context, psize);
2011408fca0SChristophe Leroy
2021408fca0SChristophe Leroy /* We need to use a spinlock here to protect against
2031408fca0SChristophe Leroy * concurrent 64k -> 4k demotion ...
2041408fca0SChristophe Leroy */
2051408fca0SChristophe Leroy spin_lock_irqsave(&slice_convert_lock, flags);
2061408fca0SChristophe Leroy
2071408fca0SChristophe Leroy lpsizes = mm_ctx_low_slices(&mm->context);
2081408fca0SChristophe Leroy for (i = 0; i < SLICE_NUM_LOW; i++) {
2091408fca0SChristophe Leroy if (!(mask->low_slices & (1u << i)))
2101408fca0SChristophe Leroy continue;
2111408fca0SChristophe Leroy
2121408fca0SChristophe Leroy mask_index = i & 0x1;
2131408fca0SChristophe Leroy index = i >> 1;
2141408fca0SChristophe Leroy
2151408fca0SChristophe Leroy /* Update the slice_mask */
2161408fca0SChristophe Leroy old_psize = (lpsizes[index] >> (mask_index * 4)) & 0xf;
2171408fca0SChristophe Leroy old_mask = slice_mask_for_size(&mm->context, old_psize);
2181408fca0SChristophe Leroy old_mask->low_slices &= ~(1u << i);
2191408fca0SChristophe Leroy psize_mask->low_slices |= 1u << i;
2201408fca0SChristophe Leroy
2211408fca0SChristophe Leroy /* Update the sizes array */
2221408fca0SChristophe Leroy lpsizes[index] = (lpsizes[index] & ~(0xf << (mask_index * 4))) |
2231408fca0SChristophe Leroy (((unsigned long)psize) << (mask_index * 4));
2241408fca0SChristophe Leroy }
2251408fca0SChristophe Leroy
2261408fca0SChristophe Leroy hpsizes = mm_ctx_high_slices(&mm->context);
2271408fca0SChristophe Leroy for (i = 0; i < GET_HIGH_SLICE_INDEX(mm_ctx_slb_addr_limit(&mm->context)); i++) {
2281408fca0SChristophe Leroy if (!test_bit(i, mask->high_slices))
2291408fca0SChristophe Leroy continue;
2301408fca0SChristophe Leroy
2311408fca0SChristophe Leroy mask_index = i & 0x1;
2321408fca0SChristophe Leroy index = i >> 1;
2331408fca0SChristophe Leroy
2341408fca0SChristophe Leroy /* Update the slice_mask */
2351408fca0SChristophe Leroy old_psize = (hpsizes[index] >> (mask_index * 4)) & 0xf;
2361408fca0SChristophe Leroy old_mask = slice_mask_for_size(&mm->context, old_psize);
2371408fca0SChristophe Leroy __clear_bit(i, old_mask->high_slices);
2381408fca0SChristophe Leroy __set_bit(i, psize_mask->high_slices);
2391408fca0SChristophe Leroy
2401408fca0SChristophe Leroy /* Update the sizes array */
2411408fca0SChristophe Leroy hpsizes[index] = (hpsizes[index] & ~(0xf << (mask_index * 4))) |
2421408fca0SChristophe Leroy (((unsigned long)psize) << (mask_index * 4));
2431408fca0SChristophe Leroy }
2441408fca0SChristophe Leroy
2451408fca0SChristophe Leroy slice_dbg(" lsps=%lx, hsps=%lx\n",
2461408fca0SChristophe Leroy (unsigned long)mm_ctx_low_slices(&mm->context),
2471408fca0SChristophe Leroy (unsigned long)mm_ctx_high_slices(&mm->context));
2481408fca0SChristophe Leroy
2491408fca0SChristophe Leroy spin_unlock_irqrestore(&slice_convert_lock, flags);
2501408fca0SChristophe Leroy
2511408fca0SChristophe Leroy copro_flush_all_slbs(mm);
2521408fca0SChristophe Leroy }
2531408fca0SChristophe Leroy
2541408fca0SChristophe Leroy /*
2551408fca0SChristophe Leroy * Compute which slice addr is part of;
2561408fca0SChristophe Leroy * set *boundary_addr to the start or end boundary of that slice
2571408fca0SChristophe Leroy * (depending on 'end' parameter);
2581408fca0SChristophe Leroy * return boolean indicating if the slice is marked as available in the
2591408fca0SChristophe Leroy * 'available' slice_mark.
2601408fca0SChristophe Leroy */
slice_scan_available(unsigned long addr,const struct slice_mask * available,int end,unsigned long * boundary_addr)2611408fca0SChristophe Leroy static bool slice_scan_available(unsigned long addr,
2621408fca0SChristophe Leroy const struct slice_mask *available,
2631408fca0SChristophe Leroy int end, unsigned long *boundary_addr)
2641408fca0SChristophe Leroy {
2651408fca0SChristophe Leroy unsigned long slice;
2661408fca0SChristophe Leroy if (slice_addr_is_low(addr)) {
2671408fca0SChristophe Leroy slice = GET_LOW_SLICE_INDEX(addr);
2681408fca0SChristophe Leroy *boundary_addr = (slice + end) << SLICE_LOW_SHIFT;
2691408fca0SChristophe Leroy return !!(available->low_slices & (1u << slice));
2701408fca0SChristophe Leroy } else {
2711408fca0SChristophe Leroy slice = GET_HIGH_SLICE_INDEX(addr);
2721408fca0SChristophe Leroy *boundary_addr = (slice + end) ?
2731408fca0SChristophe Leroy ((slice + end) << SLICE_HIGH_SHIFT) : SLICE_LOW_TOP;
2741408fca0SChristophe Leroy return !!test_bit(slice, available->high_slices);
2751408fca0SChristophe Leroy }
2761408fca0SChristophe Leroy }
2771408fca0SChristophe Leroy
slice_find_area_bottomup(struct mm_struct * mm,unsigned long addr,unsigned long len,const struct slice_mask * available,int psize,unsigned long high_limit)2781408fca0SChristophe Leroy static unsigned long slice_find_area_bottomup(struct mm_struct *mm,
279*5cf7f9a0SChristophe Leroy unsigned long addr, unsigned long len,
2801408fca0SChristophe Leroy const struct slice_mask *available,
2811408fca0SChristophe Leroy int psize, unsigned long high_limit)
2821408fca0SChristophe Leroy {
2831408fca0SChristophe Leroy int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT);
284*5cf7f9a0SChristophe Leroy unsigned long found, next_end;
2851408fca0SChristophe Leroy struct vm_unmapped_area_info info;
2861408fca0SChristophe Leroy
2871408fca0SChristophe Leroy info.flags = 0;
2881408fca0SChristophe Leroy info.length = len;
2891408fca0SChristophe Leroy info.align_mask = PAGE_MASK & ((1ul << pshift) - 1);
2901408fca0SChristophe Leroy info.align_offset = 0;
2911408fca0SChristophe Leroy /*
2921408fca0SChristophe Leroy * Check till the allow max value for this mmap request
2931408fca0SChristophe Leroy */
2941408fca0SChristophe Leroy while (addr < high_limit) {
2951408fca0SChristophe Leroy info.low_limit = addr;
2961408fca0SChristophe Leroy if (!slice_scan_available(addr, available, 1, &addr))
2971408fca0SChristophe Leroy continue;
2981408fca0SChristophe Leroy
2991408fca0SChristophe Leroy next_slice:
3001408fca0SChristophe Leroy /*
3011408fca0SChristophe Leroy * At this point [info.low_limit; addr) covers
3021408fca0SChristophe Leroy * available slices only and ends at a slice boundary.
3031408fca0SChristophe Leroy * Check if we need to reduce the range, or if we can
3041408fca0SChristophe Leroy * extend it to cover the next available slice.
3051408fca0SChristophe Leroy */
3061408fca0SChristophe Leroy if (addr >= high_limit)
3071408fca0SChristophe Leroy addr = high_limit;
3081408fca0SChristophe Leroy else if (slice_scan_available(addr, available, 1, &next_end)) {
3091408fca0SChristophe Leroy addr = next_end;
3101408fca0SChristophe Leroy goto next_slice;
3111408fca0SChristophe Leroy }
3121408fca0SChristophe Leroy info.high_limit = addr;
3131408fca0SChristophe Leroy
3141408fca0SChristophe Leroy found = vm_unmapped_area(&info);
3151408fca0SChristophe Leroy if (!(found & ~PAGE_MASK))
3161408fca0SChristophe Leroy return found;
3171408fca0SChristophe Leroy }
3181408fca0SChristophe Leroy
3191408fca0SChristophe Leroy return -ENOMEM;
3201408fca0SChristophe Leroy }
3211408fca0SChristophe Leroy
slice_find_area_topdown(struct mm_struct * mm,unsigned long addr,unsigned long len,const struct slice_mask * available,int psize,unsigned long high_limit)3221408fca0SChristophe Leroy static unsigned long slice_find_area_topdown(struct mm_struct *mm,
323*5cf7f9a0SChristophe Leroy unsigned long addr, unsigned long len,
3241408fca0SChristophe Leroy const struct slice_mask *available,
3251408fca0SChristophe Leroy int psize, unsigned long high_limit)
3261408fca0SChristophe Leroy {
3271408fca0SChristophe Leroy int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT);
328*5cf7f9a0SChristophe Leroy unsigned long found, prev;
3291408fca0SChristophe Leroy struct vm_unmapped_area_info info;
3301408fca0SChristophe Leroy unsigned long min_addr = max(PAGE_SIZE, mmap_min_addr);
3311408fca0SChristophe Leroy
3321408fca0SChristophe Leroy info.flags = VM_UNMAPPED_AREA_TOPDOWN;
3331408fca0SChristophe Leroy info.length = len;
3341408fca0SChristophe Leroy info.align_mask = PAGE_MASK & ((1ul << pshift) - 1);
3351408fca0SChristophe Leroy info.align_offset = 0;
3361408fca0SChristophe Leroy /*
3371408fca0SChristophe Leroy * If we are trying to allocate above DEFAULT_MAP_WINDOW
3381408fca0SChristophe Leroy * Add the different to the mmap_base.
3391408fca0SChristophe Leroy * Only for that request for which high_limit is above
3401408fca0SChristophe Leroy * DEFAULT_MAP_WINDOW we should apply this.
3411408fca0SChristophe Leroy */
3421408fca0SChristophe Leroy if (high_limit > DEFAULT_MAP_WINDOW)
3431408fca0SChristophe Leroy addr += mm_ctx_slb_addr_limit(&mm->context) - DEFAULT_MAP_WINDOW;
3441408fca0SChristophe Leroy
3451408fca0SChristophe Leroy while (addr > min_addr) {
3461408fca0SChristophe Leroy info.high_limit = addr;
3471408fca0SChristophe Leroy if (!slice_scan_available(addr - 1, available, 0, &addr))
3481408fca0SChristophe Leroy continue;
3491408fca0SChristophe Leroy
3501408fca0SChristophe Leroy prev_slice:
3511408fca0SChristophe Leroy /*
3521408fca0SChristophe Leroy * At this point [addr; info.high_limit) covers
3531408fca0SChristophe Leroy * available slices only and starts at a slice boundary.
3541408fca0SChristophe Leroy * Check if we need to reduce the range, or if we can
3551408fca0SChristophe Leroy * extend it to cover the previous available slice.
3561408fca0SChristophe Leroy */
3571408fca0SChristophe Leroy if (addr < min_addr)
3581408fca0SChristophe Leroy addr = min_addr;
3591408fca0SChristophe Leroy else if (slice_scan_available(addr - 1, available, 0, &prev)) {
3601408fca0SChristophe Leroy addr = prev;
3611408fca0SChristophe Leroy goto prev_slice;
3621408fca0SChristophe Leroy }
3631408fca0SChristophe Leroy info.low_limit = addr;
3641408fca0SChristophe Leroy
3651408fca0SChristophe Leroy found = vm_unmapped_area(&info);
3661408fca0SChristophe Leroy if (!(found & ~PAGE_MASK))
3671408fca0SChristophe Leroy return found;
3681408fca0SChristophe Leroy }
3691408fca0SChristophe Leroy
3701408fca0SChristophe Leroy /*
3711408fca0SChristophe Leroy * A failed mmap() very likely causes application failure,
3721408fca0SChristophe Leroy * so fall back to the bottom-up function here. This scenario
3731408fca0SChristophe Leroy * can happen with large stack limits and large mmap()
3741408fca0SChristophe Leroy * allocations.
3751408fca0SChristophe Leroy */
376*5cf7f9a0SChristophe Leroy return slice_find_area_bottomup(mm, TASK_UNMAPPED_BASE, len, available, psize, high_limit);
3771408fca0SChristophe Leroy }
3781408fca0SChristophe Leroy
3791408fca0SChristophe Leroy
slice_find_area(struct mm_struct * mm,unsigned long len,const struct slice_mask * mask,int psize,int topdown,unsigned long high_limit)3801408fca0SChristophe Leroy static unsigned long slice_find_area(struct mm_struct *mm, unsigned long len,
3811408fca0SChristophe Leroy const struct slice_mask *mask, int psize,
3821408fca0SChristophe Leroy int topdown, unsigned long high_limit)
3831408fca0SChristophe Leroy {
3841408fca0SChristophe Leroy if (topdown)
385*5cf7f9a0SChristophe Leroy return slice_find_area_topdown(mm, mm->mmap_base, len, mask, psize, high_limit);
3861408fca0SChristophe Leroy else
387*5cf7f9a0SChristophe Leroy return slice_find_area_bottomup(mm, mm->mmap_base, len, mask, psize, high_limit);
3881408fca0SChristophe Leroy }
3891408fca0SChristophe Leroy
slice_copy_mask(struct slice_mask * dst,const struct slice_mask * src)3901408fca0SChristophe Leroy static inline void slice_copy_mask(struct slice_mask *dst,
3911408fca0SChristophe Leroy const struct slice_mask *src)
3921408fca0SChristophe Leroy {
3931408fca0SChristophe Leroy dst->low_slices = src->low_slices;
3941408fca0SChristophe Leroy if (!SLICE_NUM_HIGH)
3951408fca0SChristophe Leroy return;
3961408fca0SChristophe Leroy bitmap_copy(dst->high_slices, src->high_slices, SLICE_NUM_HIGH);
3971408fca0SChristophe Leroy }
3981408fca0SChristophe Leroy
slice_or_mask(struct slice_mask * dst,const struct slice_mask * src1,const struct slice_mask * src2)3991408fca0SChristophe Leroy static inline void slice_or_mask(struct slice_mask *dst,
4001408fca0SChristophe Leroy const struct slice_mask *src1,
4011408fca0SChristophe Leroy const struct slice_mask *src2)
4021408fca0SChristophe Leroy {
4031408fca0SChristophe Leroy dst->low_slices = src1->low_slices | src2->low_slices;
4041408fca0SChristophe Leroy if (!SLICE_NUM_HIGH)
4051408fca0SChristophe Leroy return;
4061408fca0SChristophe Leroy bitmap_or(dst->high_slices, src1->high_slices, src2->high_slices, SLICE_NUM_HIGH);
4071408fca0SChristophe Leroy }
4081408fca0SChristophe Leroy
slice_andnot_mask(struct slice_mask * dst,const struct slice_mask * src1,const struct slice_mask * src2)4091408fca0SChristophe Leroy static inline void slice_andnot_mask(struct slice_mask *dst,
4101408fca0SChristophe Leroy const struct slice_mask *src1,
4111408fca0SChristophe Leroy const struct slice_mask *src2)
4121408fca0SChristophe Leroy {
4131408fca0SChristophe Leroy dst->low_slices = src1->low_slices & ~src2->low_slices;
4141408fca0SChristophe Leroy if (!SLICE_NUM_HIGH)
4151408fca0SChristophe Leroy return;
4161408fca0SChristophe Leroy bitmap_andnot(dst->high_slices, src1->high_slices, src2->high_slices, SLICE_NUM_HIGH);
4171408fca0SChristophe Leroy }
4181408fca0SChristophe Leroy
4191408fca0SChristophe Leroy #ifdef CONFIG_PPC_64K_PAGES
4201408fca0SChristophe Leroy #define MMU_PAGE_BASE MMU_PAGE_64K
4211408fca0SChristophe Leroy #else
4221408fca0SChristophe Leroy #define MMU_PAGE_BASE MMU_PAGE_4K
4231408fca0SChristophe Leroy #endif
4241408fca0SChristophe Leroy
slice_get_unmapped_area(unsigned long addr,unsigned long len,unsigned long flags,unsigned int psize,int topdown)4251408fca0SChristophe Leroy unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
4261408fca0SChristophe Leroy unsigned long flags, unsigned int psize,
4271408fca0SChristophe Leroy int topdown)
4281408fca0SChristophe Leroy {
4291408fca0SChristophe Leroy struct slice_mask good_mask;
4301408fca0SChristophe Leroy struct slice_mask potential_mask;
4311408fca0SChristophe Leroy const struct slice_mask *maskp;
4321408fca0SChristophe Leroy const struct slice_mask *compat_maskp = NULL;
4331408fca0SChristophe Leroy int fixed = (flags & MAP_FIXED);
4341408fca0SChristophe Leroy int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT);
4351408fca0SChristophe Leroy unsigned long page_size = 1UL << pshift;
4361408fca0SChristophe Leroy struct mm_struct *mm = current->mm;
4371408fca0SChristophe Leroy unsigned long newaddr;
4381408fca0SChristophe Leroy unsigned long high_limit;
4391408fca0SChristophe Leroy
4401408fca0SChristophe Leroy high_limit = DEFAULT_MAP_WINDOW;
4411408fca0SChristophe Leroy if (addr >= high_limit || (fixed && (addr + len > high_limit)))
4421408fca0SChristophe Leroy high_limit = TASK_SIZE;
4431408fca0SChristophe Leroy
4441408fca0SChristophe Leroy if (len > high_limit)
4451408fca0SChristophe Leroy return -ENOMEM;
4461408fca0SChristophe Leroy if (len & (page_size - 1))
4471408fca0SChristophe Leroy return -EINVAL;
4481408fca0SChristophe Leroy if (fixed) {
4491408fca0SChristophe Leroy if (addr & (page_size - 1))
4501408fca0SChristophe Leroy return -EINVAL;
4511408fca0SChristophe Leroy if (addr > high_limit - len)
4521408fca0SChristophe Leroy return -ENOMEM;
4531408fca0SChristophe Leroy }
4541408fca0SChristophe Leroy
4551408fca0SChristophe Leroy if (high_limit > mm_ctx_slb_addr_limit(&mm->context)) {
4561408fca0SChristophe Leroy /*
4571408fca0SChristophe Leroy * Increasing the slb_addr_limit does not require
4581408fca0SChristophe Leroy * slice mask cache to be recalculated because it should
4591408fca0SChristophe Leroy * be already initialised beyond the old address limit.
4601408fca0SChristophe Leroy */
4611408fca0SChristophe Leroy mm_ctx_set_slb_addr_limit(&mm->context, high_limit);
4621408fca0SChristophe Leroy
4631408fca0SChristophe Leroy on_each_cpu(slice_flush_segments, mm, 1);
4641408fca0SChristophe Leroy }
4651408fca0SChristophe Leroy
4661408fca0SChristophe Leroy /* Sanity checks */
4671408fca0SChristophe Leroy BUG_ON(mm->task_size == 0);
4681408fca0SChristophe Leroy BUG_ON(mm_ctx_slb_addr_limit(&mm->context) == 0);
4691408fca0SChristophe Leroy VM_BUG_ON(radix_enabled());
4701408fca0SChristophe Leroy
4711408fca0SChristophe Leroy slice_dbg("slice_get_unmapped_area(mm=%p, psize=%d...\n", mm, psize);
4721408fca0SChristophe Leroy slice_dbg(" addr=%lx, len=%lx, flags=%lx, topdown=%d\n",
4731408fca0SChristophe Leroy addr, len, flags, topdown);
4741408fca0SChristophe Leroy
4751408fca0SChristophe Leroy /* If hint, make sure it matches our alignment restrictions */
4761408fca0SChristophe Leroy if (!fixed && addr) {
4771408fca0SChristophe Leroy addr = ALIGN(addr, page_size);
4781408fca0SChristophe Leroy slice_dbg(" aligned addr=%lx\n", addr);
4791408fca0SChristophe Leroy /* Ignore hint if it's too large or overlaps a VMA */
4801408fca0SChristophe Leroy if (addr > high_limit - len || addr < mmap_min_addr ||
4811408fca0SChristophe Leroy !slice_area_is_free(mm, addr, len))
4821408fca0SChristophe Leroy addr = 0;
4831408fca0SChristophe Leroy }
4841408fca0SChristophe Leroy
4851408fca0SChristophe Leroy /* First make up a "good" mask of slices that have the right size
4861408fca0SChristophe Leroy * already
4871408fca0SChristophe Leroy */
4881408fca0SChristophe Leroy maskp = slice_mask_for_size(&mm->context, psize);
4891408fca0SChristophe Leroy
4901408fca0SChristophe Leroy /*
4911408fca0SChristophe Leroy * Here "good" means slices that are already the right page size,
4921408fca0SChristophe Leroy * "compat" means slices that have a compatible page size (i.e.
4931408fca0SChristophe Leroy * 4k in a 64k pagesize kernel), and "free" means slices without
4941408fca0SChristophe Leroy * any VMAs.
4951408fca0SChristophe Leroy *
4961408fca0SChristophe Leroy * If MAP_FIXED:
4971408fca0SChristophe Leroy * check if fits in good | compat => OK
4981408fca0SChristophe Leroy * check if fits in good | compat | free => convert free
4991408fca0SChristophe Leroy * else bad
5001408fca0SChristophe Leroy * If have hint:
5011408fca0SChristophe Leroy * check if hint fits in good => OK
5021408fca0SChristophe Leroy * check if hint fits in good | free => convert free
5031408fca0SChristophe Leroy * Otherwise:
5041408fca0SChristophe Leroy * search in good, found => OK
5051408fca0SChristophe Leroy * search in good | free, found => convert free
5061408fca0SChristophe Leroy * search in good | compat | free, found => convert free.
5071408fca0SChristophe Leroy */
5081408fca0SChristophe Leroy
5091408fca0SChristophe Leroy /*
5101408fca0SChristophe Leroy * If we support combo pages, we can allow 64k pages in 4k slices
5111408fca0SChristophe Leroy * The mask copies could be avoided in most cases here if we had
5121408fca0SChristophe Leroy * a pointer to good mask for the next code to use.
5131408fca0SChristophe Leroy */
5141408fca0SChristophe Leroy if (IS_ENABLED(CONFIG_PPC_64K_PAGES) && psize == MMU_PAGE_64K) {
5151408fca0SChristophe Leroy compat_maskp = slice_mask_for_size(&mm->context, MMU_PAGE_4K);
5161408fca0SChristophe Leroy if (fixed)
5171408fca0SChristophe Leroy slice_or_mask(&good_mask, maskp, compat_maskp);
5181408fca0SChristophe Leroy else
5191408fca0SChristophe Leroy slice_copy_mask(&good_mask, maskp);
5201408fca0SChristophe Leroy } else {
5211408fca0SChristophe Leroy slice_copy_mask(&good_mask, maskp);
5221408fca0SChristophe Leroy }
5231408fca0SChristophe Leroy
5241408fca0SChristophe Leroy slice_print_mask(" good_mask", &good_mask);
5251408fca0SChristophe Leroy if (compat_maskp)
5261408fca0SChristophe Leroy slice_print_mask(" compat_mask", compat_maskp);
5271408fca0SChristophe Leroy
5281408fca0SChristophe Leroy /* First check hint if it's valid or if we have MAP_FIXED */
5291408fca0SChristophe Leroy if (addr != 0 || fixed) {
5301408fca0SChristophe Leroy /* Check if we fit in the good mask. If we do, we just return,
5311408fca0SChristophe Leroy * nothing else to do
5321408fca0SChristophe Leroy */
5331408fca0SChristophe Leroy if (slice_check_range_fits(mm, &good_mask, addr, len)) {
5341408fca0SChristophe Leroy slice_dbg(" fits good !\n");
5351408fca0SChristophe Leroy newaddr = addr;
5361408fca0SChristophe Leroy goto return_addr;
5371408fca0SChristophe Leroy }
5381408fca0SChristophe Leroy } else {
5391408fca0SChristophe Leroy /* Now let's see if we can find something in the existing
5401408fca0SChristophe Leroy * slices for that size
5411408fca0SChristophe Leroy */
5421408fca0SChristophe Leroy newaddr = slice_find_area(mm, len, &good_mask,
5431408fca0SChristophe Leroy psize, topdown, high_limit);
5441408fca0SChristophe Leroy if (newaddr != -ENOMEM) {
5451408fca0SChristophe Leroy /* Found within the good mask, we don't have to setup,
5461408fca0SChristophe Leroy * we thus return directly
5471408fca0SChristophe Leroy */
5481408fca0SChristophe Leroy slice_dbg(" found area at 0x%lx\n", newaddr);
5491408fca0SChristophe Leroy goto return_addr;
5501408fca0SChristophe Leroy }
5511408fca0SChristophe Leroy }
5521408fca0SChristophe Leroy /*
5531408fca0SChristophe Leroy * We don't fit in the good mask, check what other slices are
5541408fca0SChristophe Leroy * empty and thus can be converted
5551408fca0SChristophe Leroy */
5561408fca0SChristophe Leroy slice_mask_for_free(mm, &potential_mask, high_limit);
5571408fca0SChristophe Leroy slice_or_mask(&potential_mask, &potential_mask, &good_mask);
5581408fca0SChristophe Leroy slice_print_mask(" potential", &potential_mask);
5591408fca0SChristophe Leroy
5601408fca0SChristophe Leroy if (addr != 0 || fixed) {
5611408fca0SChristophe Leroy if (slice_check_range_fits(mm, &potential_mask, addr, len)) {
5621408fca0SChristophe Leroy slice_dbg(" fits potential !\n");
5631408fca0SChristophe Leroy newaddr = addr;
5641408fca0SChristophe Leroy goto convert;
5651408fca0SChristophe Leroy }
5661408fca0SChristophe Leroy }
5671408fca0SChristophe Leroy
5681408fca0SChristophe Leroy /* If we have MAP_FIXED and failed the above steps, then error out */
5691408fca0SChristophe Leroy if (fixed)
5701408fca0SChristophe Leroy return -EBUSY;
5711408fca0SChristophe Leroy
5721408fca0SChristophe Leroy slice_dbg(" search...\n");
5731408fca0SChristophe Leroy
5741408fca0SChristophe Leroy /* If we had a hint that didn't work out, see if we can fit
5751408fca0SChristophe Leroy * anywhere in the good area.
5761408fca0SChristophe Leroy */
5771408fca0SChristophe Leroy if (addr) {
5781408fca0SChristophe Leroy newaddr = slice_find_area(mm, len, &good_mask,
5791408fca0SChristophe Leroy psize, topdown, high_limit);
5801408fca0SChristophe Leroy if (newaddr != -ENOMEM) {
5811408fca0SChristophe Leroy slice_dbg(" found area at 0x%lx\n", newaddr);
5821408fca0SChristophe Leroy goto return_addr;
5831408fca0SChristophe Leroy }
5841408fca0SChristophe Leroy }
5851408fca0SChristophe Leroy
5861408fca0SChristophe Leroy /* Now let's see if we can find something in the existing slices
5871408fca0SChristophe Leroy * for that size plus free slices
5881408fca0SChristophe Leroy */
5891408fca0SChristophe Leroy newaddr = slice_find_area(mm, len, &potential_mask,
5901408fca0SChristophe Leroy psize, topdown, high_limit);
5911408fca0SChristophe Leroy
5921408fca0SChristophe Leroy if (IS_ENABLED(CONFIG_PPC_64K_PAGES) && newaddr == -ENOMEM &&
5931408fca0SChristophe Leroy psize == MMU_PAGE_64K) {
5941408fca0SChristophe Leroy /* retry the search with 4k-page slices included */
5951408fca0SChristophe Leroy slice_or_mask(&potential_mask, &potential_mask, compat_maskp);
5961408fca0SChristophe Leroy newaddr = slice_find_area(mm, len, &potential_mask,
5971408fca0SChristophe Leroy psize, topdown, high_limit);
5981408fca0SChristophe Leroy }
5991408fca0SChristophe Leroy
6001408fca0SChristophe Leroy if (newaddr == -ENOMEM)
6011408fca0SChristophe Leroy return -ENOMEM;
6021408fca0SChristophe Leroy
6031408fca0SChristophe Leroy slice_range_to_mask(newaddr, len, &potential_mask);
6041408fca0SChristophe Leroy slice_dbg(" found potential area at 0x%lx\n", newaddr);
6051408fca0SChristophe Leroy slice_print_mask(" mask", &potential_mask);
6061408fca0SChristophe Leroy
6071408fca0SChristophe Leroy convert:
6081408fca0SChristophe Leroy /*
6091408fca0SChristophe Leroy * Try to allocate the context before we do slice convert
6101408fca0SChristophe Leroy * so that we handle the context allocation failure gracefully.
6111408fca0SChristophe Leroy */
6121408fca0SChristophe Leroy if (need_extra_context(mm, newaddr)) {
6131408fca0SChristophe Leroy if (alloc_extended_context(mm, newaddr) < 0)
6141408fca0SChristophe Leroy return -ENOMEM;
6151408fca0SChristophe Leroy }
6161408fca0SChristophe Leroy
6171408fca0SChristophe Leroy slice_andnot_mask(&potential_mask, &potential_mask, &good_mask);
6181408fca0SChristophe Leroy if (compat_maskp && !fixed)
6191408fca0SChristophe Leroy slice_andnot_mask(&potential_mask, &potential_mask, compat_maskp);
6201408fca0SChristophe Leroy if (potential_mask.low_slices ||
6211408fca0SChristophe Leroy (SLICE_NUM_HIGH &&
6221408fca0SChristophe Leroy !bitmap_empty(potential_mask.high_slices, SLICE_NUM_HIGH))) {
6231408fca0SChristophe Leroy slice_convert(mm, &potential_mask, psize);
6241408fca0SChristophe Leroy if (psize > MMU_PAGE_BASE)
6251408fca0SChristophe Leroy on_each_cpu(slice_flush_segments, mm, 1);
6261408fca0SChristophe Leroy }
6271408fca0SChristophe Leroy return newaddr;
6281408fca0SChristophe Leroy
6291408fca0SChristophe Leroy return_addr:
6301408fca0SChristophe Leroy if (need_extra_context(mm, newaddr)) {
6311408fca0SChristophe Leroy if (alloc_extended_context(mm, newaddr) < 0)
6321408fca0SChristophe Leroy return -ENOMEM;
6331408fca0SChristophe Leroy }
6341408fca0SChristophe Leroy return newaddr;
6351408fca0SChristophe Leroy }
6361408fca0SChristophe Leroy EXPORT_SYMBOL_GPL(slice_get_unmapped_area);
6371408fca0SChristophe Leroy
arch_get_unmapped_area(struct file * filp,unsigned long addr,unsigned long len,unsigned long pgoff,unsigned long flags)638ab57bd75SChristophe Leroy unsigned long arch_get_unmapped_area(struct file *filp,
639ab57bd75SChristophe Leroy unsigned long addr,
640ab57bd75SChristophe Leroy unsigned long len,
641ab57bd75SChristophe Leroy unsigned long pgoff,
642ab57bd75SChristophe Leroy unsigned long flags)
643ab57bd75SChristophe Leroy {
644ab57bd75SChristophe Leroy if (radix_enabled())
645ab57bd75SChristophe Leroy return generic_get_unmapped_area(filp, addr, len, pgoff, flags);
646ab57bd75SChristophe Leroy
647ab57bd75SChristophe Leroy return slice_get_unmapped_area(addr, len, flags,
648ab57bd75SChristophe Leroy mm_ctx_user_psize(¤t->mm->context), 0);
649ab57bd75SChristophe Leroy }
650ab57bd75SChristophe Leroy
arch_get_unmapped_area_topdown(struct file * filp,const unsigned long addr0,const unsigned long len,const unsigned long pgoff,const unsigned long flags)651ab57bd75SChristophe Leroy unsigned long arch_get_unmapped_area_topdown(struct file *filp,
652ab57bd75SChristophe Leroy const unsigned long addr0,
653ab57bd75SChristophe Leroy const unsigned long len,
654ab57bd75SChristophe Leroy const unsigned long pgoff,
655ab57bd75SChristophe Leroy const unsigned long flags)
656ab57bd75SChristophe Leroy {
657ab57bd75SChristophe Leroy if (radix_enabled())
658ab57bd75SChristophe Leroy return generic_get_unmapped_area_topdown(filp, addr0, len, pgoff, flags);
659ab57bd75SChristophe Leroy
660ab57bd75SChristophe Leroy return slice_get_unmapped_area(addr0, len, flags,
661ab57bd75SChristophe Leroy mm_ctx_user_psize(¤t->mm->context), 1);
662ab57bd75SChristophe Leroy }
663ab57bd75SChristophe Leroy
get_slice_psize(struct mm_struct * mm,unsigned long addr)6641408fca0SChristophe Leroy unsigned int notrace get_slice_psize(struct mm_struct *mm, unsigned long addr)
6651408fca0SChristophe Leroy {
6661408fca0SChristophe Leroy unsigned char *psizes;
6671408fca0SChristophe Leroy int index, mask_index;
6681408fca0SChristophe Leroy
6691408fca0SChristophe Leroy VM_BUG_ON(radix_enabled());
6701408fca0SChristophe Leroy
6711408fca0SChristophe Leroy if (slice_addr_is_low(addr)) {
6721408fca0SChristophe Leroy psizes = mm_ctx_low_slices(&mm->context);
6731408fca0SChristophe Leroy index = GET_LOW_SLICE_INDEX(addr);
6741408fca0SChristophe Leroy } else {
6751408fca0SChristophe Leroy psizes = mm_ctx_high_slices(&mm->context);
6761408fca0SChristophe Leroy index = GET_HIGH_SLICE_INDEX(addr);
6771408fca0SChristophe Leroy }
6781408fca0SChristophe Leroy mask_index = index & 0x1;
6791408fca0SChristophe Leroy return (psizes[index >> 1] >> (mask_index * 4)) & 0xf;
6801408fca0SChristophe Leroy }
6811408fca0SChristophe Leroy EXPORT_SYMBOL_GPL(get_slice_psize);
6821408fca0SChristophe Leroy
slice_init_new_context_exec(struct mm_struct * mm)6831408fca0SChristophe Leroy void slice_init_new_context_exec(struct mm_struct *mm)
6841408fca0SChristophe Leroy {
6851408fca0SChristophe Leroy unsigned char *hpsizes, *lpsizes;
6861408fca0SChristophe Leroy struct slice_mask *mask;
6871408fca0SChristophe Leroy unsigned int psize = mmu_virtual_psize;
6881408fca0SChristophe Leroy
6891408fca0SChristophe Leroy slice_dbg("slice_init_new_context_exec(mm=%p)\n", mm);
6901408fca0SChristophe Leroy
6911408fca0SChristophe Leroy /*
6921408fca0SChristophe Leroy * In the case of exec, use the default limit. In the
6931408fca0SChristophe Leroy * case of fork it is just inherited from the mm being
6941408fca0SChristophe Leroy * duplicated.
6951408fca0SChristophe Leroy */
6961408fca0SChristophe Leroy mm_ctx_set_slb_addr_limit(&mm->context, SLB_ADDR_LIMIT_DEFAULT);
6971408fca0SChristophe Leroy mm_ctx_set_user_psize(&mm->context, psize);
6981408fca0SChristophe Leroy
6991408fca0SChristophe Leroy /*
7001408fca0SChristophe Leroy * Set all slice psizes to the default.
7011408fca0SChristophe Leroy */
7021408fca0SChristophe Leroy lpsizes = mm_ctx_low_slices(&mm->context);
7031408fca0SChristophe Leroy memset(lpsizes, (psize << 4) | psize, SLICE_NUM_LOW >> 1);
7041408fca0SChristophe Leroy
7051408fca0SChristophe Leroy hpsizes = mm_ctx_high_slices(&mm->context);
7061408fca0SChristophe Leroy memset(hpsizes, (psize << 4) | psize, SLICE_NUM_HIGH >> 1);
7071408fca0SChristophe Leroy
7081408fca0SChristophe Leroy /*
7091408fca0SChristophe Leroy * Slice mask cache starts zeroed, fill the default size cache.
7101408fca0SChristophe Leroy */
7111408fca0SChristophe Leroy mask = slice_mask_for_size(&mm->context, psize);
7121408fca0SChristophe Leroy mask->low_slices = ~0UL;
7131408fca0SChristophe Leroy if (SLICE_NUM_HIGH)
7141408fca0SChristophe Leroy bitmap_fill(mask->high_slices, SLICE_NUM_HIGH);
7151408fca0SChristophe Leroy }
7161408fca0SChristophe Leroy
slice_setup_new_exec(void)7171408fca0SChristophe Leroy void slice_setup_new_exec(void)
7181408fca0SChristophe Leroy {
7191408fca0SChristophe Leroy struct mm_struct *mm = current->mm;
7201408fca0SChristophe Leroy
7211408fca0SChristophe Leroy slice_dbg("slice_setup_new_exec(mm=%p)\n", mm);
7221408fca0SChristophe Leroy
7231408fca0SChristophe Leroy if (!is_32bit_task())
7241408fca0SChristophe Leroy return;
7251408fca0SChristophe Leroy
7261408fca0SChristophe Leroy mm_ctx_set_slb_addr_limit(&mm->context, DEFAULT_MAP_WINDOW);
7271408fca0SChristophe Leroy }
7281408fca0SChristophe Leroy
slice_set_range_psize(struct mm_struct * mm,unsigned long start,unsigned long len,unsigned int psize)7291408fca0SChristophe Leroy void slice_set_range_psize(struct mm_struct *mm, unsigned long start,
7301408fca0SChristophe Leroy unsigned long len, unsigned int psize)
7311408fca0SChristophe Leroy {
7321408fca0SChristophe Leroy struct slice_mask mask;
7331408fca0SChristophe Leroy
7341408fca0SChristophe Leroy VM_BUG_ON(radix_enabled());
7351408fca0SChristophe Leroy
7361408fca0SChristophe Leroy slice_range_to_mask(start, len, &mask);
7371408fca0SChristophe Leroy slice_convert(mm, &mask, psize);
7381408fca0SChristophe Leroy }
7391408fca0SChristophe Leroy
7401408fca0SChristophe Leroy #ifdef CONFIG_HUGETLB_PAGE
7411408fca0SChristophe Leroy /*
7421408fca0SChristophe Leroy * is_hugepage_only_range() is used by generic code to verify whether
7431408fca0SChristophe Leroy * a normal mmap mapping (non hugetlbfs) is valid on a given area.
7441408fca0SChristophe Leroy *
7451408fca0SChristophe Leroy * until the generic code provides a more generic hook and/or starts
7461408fca0SChristophe Leroy * calling arch get_unmapped_area for MAP_FIXED (which our implementation
7471408fca0SChristophe Leroy * here knows how to deal with), we hijack it to keep standard mappings
7481408fca0SChristophe Leroy * away from us.
7491408fca0SChristophe Leroy *
7501408fca0SChristophe Leroy * because of that generic code limitation, MAP_FIXED mapping cannot
7511408fca0SChristophe Leroy * "convert" back a slice with no VMAs to the standard page size, only
7521408fca0SChristophe Leroy * get_unmapped_area() can. It would be possible to fix it here but I
7531408fca0SChristophe Leroy * prefer working on fixing the generic code instead.
7541408fca0SChristophe Leroy *
7551408fca0SChristophe Leroy * WARNING: This will not work if hugetlbfs isn't enabled since the
7561408fca0SChristophe Leroy * generic code will redefine that function as 0 in that. This is ok
7571408fca0SChristophe Leroy * for now as we only use slices with hugetlbfs enabled. This should
7581408fca0SChristophe Leroy * be fixed as the generic code gets fixed.
7591408fca0SChristophe Leroy */
slice_is_hugepage_only_range(struct mm_struct * mm,unsigned long addr,unsigned long len)7601408fca0SChristophe Leroy int slice_is_hugepage_only_range(struct mm_struct *mm, unsigned long addr,
7611408fca0SChristophe Leroy unsigned long len)
7621408fca0SChristophe Leroy {
7631408fca0SChristophe Leroy const struct slice_mask *maskp;
7641408fca0SChristophe Leroy unsigned int psize = mm_ctx_user_psize(&mm->context);
7651408fca0SChristophe Leroy
7661408fca0SChristophe Leroy VM_BUG_ON(radix_enabled());
7671408fca0SChristophe Leroy
7681408fca0SChristophe Leroy maskp = slice_mask_for_size(&mm->context, psize);
7691408fca0SChristophe Leroy
7701408fca0SChristophe Leroy /* We need to account for 4k slices too */
7711408fca0SChristophe Leroy if (IS_ENABLED(CONFIG_PPC_64K_PAGES) && psize == MMU_PAGE_64K) {
7721408fca0SChristophe Leroy const struct slice_mask *compat_maskp;
7731408fca0SChristophe Leroy struct slice_mask available;
7741408fca0SChristophe Leroy
7751408fca0SChristophe Leroy compat_maskp = slice_mask_for_size(&mm->context, MMU_PAGE_4K);
7761408fca0SChristophe Leroy slice_or_mask(&available, maskp, compat_maskp);
7771408fca0SChristophe Leroy return !slice_check_range_fits(mm, &available, addr, len);
7781408fca0SChristophe Leroy }
7791408fca0SChristophe Leroy
7801408fca0SChristophe Leroy return !slice_check_range_fits(mm, maskp, addr, len);
7811408fca0SChristophe Leroy }
7821408fca0SChristophe Leroy
vma_mmu_pagesize(struct vm_area_struct * vma)7831408fca0SChristophe Leroy unsigned long vma_mmu_pagesize(struct vm_area_struct *vma)
7841408fca0SChristophe Leroy {
7851408fca0SChristophe Leroy /* With radix we don't use slice, so derive it from vma*/
7861408fca0SChristophe Leroy if (radix_enabled())
7871408fca0SChristophe Leroy return vma_kernel_pagesize(vma);
7881408fca0SChristophe Leroy
7891408fca0SChristophe Leroy return 1UL << mmu_psize_to_shift(get_slice_psize(vma->vm_mm, vma->vm_start));
7901408fca0SChristophe Leroy }
791ab57bd75SChristophe Leroy
file_to_psize(struct file * file)792ab57bd75SChristophe Leroy static int file_to_psize(struct file *file)
793ab57bd75SChristophe Leroy {
794ab57bd75SChristophe Leroy struct hstate *hstate = hstate_file(file);
795ab57bd75SChristophe Leroy return shift_to_mmu_psize(huge_page_shift(hstate));
796ab57bd75SChristophe Leroy }
797ab57bd75SChristophe Leroy
hugetlb_get_unmapped_area(struct file * file,unsigned long addr,unsigned long len,unsigned long pgoff,unsigned long flags)798ab57bd75SChristophe Leroy unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
799ab57bd75SChristophe Leroy unsigned long len, unsigned long pgoff,
800ab57bd75SChristophe Leroy unsigned long flags)
801ab57bd75SChristophe Leroy {
802ab57bd75SChristophe Leroy if (radix_enabled())
803ab57bd75SChristophe Leroy return generic_hugetlb_get_unmapped_area(file, addr, len, pgoff, flags);
804ab57bd75SChristophe Leroy
805ab57bd75SChristophe Leroy return slice_get_unmapped_area(addr, len, flags, file_to_psize(file), 1);
806ab57bd75SChristophe Leroy }
8071408fca0SChristophe Leroy #endif
808