18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  mm/mprotect.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  (C) Copyright 1994 Linus Torvalds
68c2ecf20Sopenharmony_ci *  (C) Copyright 2002 Christoph Hellwig
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *  Address space accounting code	<alan@lxorguk.ukuu.org.uk>
98c2ecf20Sopenharmony_ci *  (C) Copyright 2002 Red Hat Inc, All Rights Reserved
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/pagewalk.h>
138c2ecf20Sopenharmony_ci#include <linux/hugetlb.h>
148c2ecf20Sopenharmony_ci#include <linux/shm.h>
158c2ecf20Sopenharmony_ci#include <linux/mman.h>
168c2ecf20Sopenharmony_ci#include <linux/fs.h>
178c2ecf20Sopenharmony_ci#include <linux/highmem.h>
188c2ecf20Sopenharmony_ci#include <linux/security.h>
198c2ecf20Sopenharmony_ci#include <linux/mempolicy.h>
208c2ecf20Sopenharmony_ci#include <linux/personality.h>
218c2ecf20Sopenharmony_ci#include <linux/syscalls.h>
228c2ecf20Sopenharmony_ci#include <linux/swap.h>
238c2ecf20Sopenharmony_ci#include <linux/swapops.h>
248c2ecf20Sopenharmony_ci#include <linux/mmu_notifier.h>
258c2ecf20Sopenharmony_ci#include <linux/migrate.h>
268c2ecf20Sopenharmony_ci#include <linux/perf_event.h>
278c2ecf20Sopenharmony_ci#include <linux/pkeys.h>
288c2ecf20Sopenharmony_ci#include <linux/ksm.h>
298c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
308c2ecf20Sopenharmony_ci#include <linux/mm_inline.h>
318c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
328c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
338c2ecf20Sopenharmony_ci#include <asm/mmu_context.h>
348c2ecf20Sopenharmony_ci#include <asm/tlbflush.h>
358c2ecf20Sopenharmony_ci#include <linux/xpm.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include <trace/hooks/mm.h>
388c2ecf20Sopenharmony_ci#include "internal.h"
398c2ecf20Sopenharmony_ci#include <linux/hck/lite_hck_jit_memory.h>
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
428c2ecf20Sopenharmony_ci		unsigned long addr, unsigned long end, pgprot_t newprot,
438c2ecf20Sopenharmony_ci		unsigned long cp_flags)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	pte_t *pte, oldpte;
468c2ecf20Sopenharmony_ci	spinlock_t *ptl;
478c2ecf20Sopenharmony_ci	unsigned long pages = 0;
488c2ecf20Sopenharmony_ci	int target_node = NUMA_NO_NODE;
498c2ecf20Sopenharmony_ci	bool dirty_accountable = cp_flags & MM_CP_DIRTY_ACCT;
508c2ecf20Sopenharmony_ci	bool prot_numa = cp_flags & MM_CP_PROT_NUMA;
518c2ecf20Sopenharmony_ci	bool uffd_wp = cp_flags & MM_CP_UFFD_WP;
528c2ecf20Sopenharmony_ci	bool uffd_wp_resolve = cp_flags & MM_CP_UFFD_WP_RESOLVE;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	/*
558c2ecf20Sopenharmony_ci	 * Can be called with only the mmap_lock for reading by
568c2ecf20Sopenharmony_ci	 * prot_numa so we must check the pmd isn't constantly
578c2ecf20Sopenharmony_ci	 * changing from under us from pmd_none to pmd_trans_huge
588c2ecf20Sopenharmony_ci	 * and/or the other way around.
598c2ecf20Sopenharmony_ci	 */
608c2ecf20Sopenharmony_ci	if (pmd_trans_unstable(pmd))
618c2ecf20Sopenharmony_ci		return 0;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	/*
648c2ecf20Sopenharmony_ci	 * The pmd points to a regular pte so the pmd can't change
658c2ecf20Sopenharmony_ci	 * from under us even if the mmap_lock is only hold for
668c2ecf20Sopenharmony_ci	 * reading.
678c2ecf20Sopenharmony_ci	 */
688c2ecf20Sopenharmony_ci	pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/* Get target node for single threaded private VMAs */
718c2ecf20Sopenharmony_ci	if (prot_numa && !(vma->vm_flags & VM_SHARED) &&
728c2ecf20Sopenharmony_ci	    atomic_read(&vma->vm_mm->mm_users) == 1)
738c2ecf20Sopenharmony_ci		target_node = numa_node_id();
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	flush_tlb_batched_pending(vma->vm_mm);
768c2ecf20Sopenharmony_ci	arch_enter_lazy_mmu_mode();
778c2ecf20Sopenharmony_ci	do {
788c2ecf20Sopenharmony_ci		oldpte = *pte;
798c2ecf20Sopenharmony_ci		if (pte_present(oldpte)) {
808c2ecf20Sopenharmony_ci			pte_t ptent;
818c2ecf20Sopenharmony_ci			bool preserve_write = prot_numa && pte_write(oldpte);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci			/*
848c2ecf20Sopenharmony_ci			 * Avoid trapping faults against the zero or KSM
858c2ecf20Sopenharmony_ci			 * pages. See similar comment in change_huge_pmd.
868c2ecf20Sopenharmony_ci			 */
878c2ecf20Sopenharmony_ci			if (prot_numa) {
888c2ecf20Sopenharmony_ci				struct page *page;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci				/* Avoid TLB flush if possible */
918c2ecf20Sopenharmony_ci				if (pte_protnone(oldpte))
928c2ecf20Sopenharmony_ci					continue;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci				page = vm_normal_page(vma, addr, oldpte);
958c2ecf20Sopenharmony_ci				if (!page || PageKsm(page))
968c2ecf20Sopenharmony_ci					continue;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci				/* Also skip shared copy-on-write pages */
998c2ecf20Sopenharmony_ci				if (is_cow_mapping(vma->vm_flags) &&
1008c2ecf20Sopenharmony_ci				    page_count(page) != 1)
1018c2ecf20Sopenharmony_ci					continue;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci				/*
1048c2ecf20Sopenharmony_ci				 * While migration can move some dirty pages,
1058c2ecf20Sopenharmony_ci				 * it cannot move them all from MIGRATE_ASYNC
1068c2ecf20Sopenharmony_ci				 * context.
1078c2ecf20Sopenharmony_ci				 */
1088c2ecf20Sopenharmony_ci				if (page_is_file_lru(page) && PageDirty(page))
1098c2ecf20Sopenharmony_ci					continue;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci				/*
1128c2ecf20Sopenharmony_ci				 * Don't mess with PTEs if page is already on the node
1138c2ecf20Sopenharmony_ci				 * a single-threaded process is running on.
1148c2ecf20Sopenharmony_ci				 */
1158c2ecf20Sopenharmony_ci				if (target_node == page_to_nid(page))
1168c2ecf20Sopenharmony_ci					continue;
1178c2ecf20Sopenharmony_ci			}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci			oldpte = ptep_modify_prot_start(vma, addr, pte);
1208c2ecf20Sopenharmony_ci			ptent = pte_modify(oldpte, newprot);
1218c2ecf20Sopenharmony_ci			if (preserve_write)
1228c2ecf20Sopenharmony_ci				ptent = pte_mk_savedwrite(ptent);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci			if (uffd_wp) {
1258c2ecf20Sopenharmony_ci				ptent = pte_wrprotect(ptent);
1268c2ecf20Sopenharmony_ci				ptent = pte_mkuffd_wp(ptent);
1278c2ecf20Sopenharmony_ci			} else if (uffd_wp_resolve) {
1288c2ecf20Sopenharmony_ci				/*
1298c2ecf20Sopenharmony_ci				 * Leave the write bit to be handled
1308c2ecf20Sopenharmony_ci				 * by PF interrupt handler, then
1318c2ecf20Sopenharmony_ci				 * things like COW could be properly
1328c2ecf20Sopenharmony_ci				 * handled.
1338c2ecf20Sopenharmony_ci				 */
1348c2ecf20Sopenharmony_ci				ptent = pte_clear_uffd_wp(ptent);
1358c2ecf20Sopenharmony_ci			}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci			/* Avoid taking write faults for known dirty pages */
1388c2ecf20Sopenharmony_ci			if (dirty_accountable && pte_dirty(ptent) &&
1398c2ecf20Sopenharmony_ci					(pte_soft_dirty(ptent) ||
1408c2ecf20Sopenharmony_ci					 !(vma->vm_flags & VM_SOFTDIRTY))) {
1418c2ecf20Sopenharmony_ci				ptent = pte_mkwrite(ptent);
1428c2ecf20Sopenharmony_ci			}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci			/* if exec added, check xpm integrity before set pte */
1458c2ecf20Sopenharmony_ci			if(pte_user_mkexec(oldpte, ptent) &&
1468c2ecf20Sopenharmony_ci				unlikely(xpm_integrity_validate_hook(vma, 0, addr,
1478c2ecf20Sopenharmony_ci					vm_normal_page(vma, addr, oldpte))))
1488c2ecf20Sopenharmony_ci				continue;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci			ptep_modify_prot_commit(vma, addr, pte, oldpte, ptent);
1518c2ecf20Sopenharmony_ci			pages++;
1528c2ecf20Sopenharmony_ci		} else if (is_swap_pte(oldpte)) {
1538c2ecf20Sopenharmony_ci			swp_entry_t entry = pte_to_swp_entry(oldpte);
1548c2ecf20Sopenharmony_ci			pte_t newpte;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci			if (is_write_migration_entry(entry)) {
1578c2ecf20Sopenharmony_ci				/*
1588c2ecf20Sopenharmony_ci				 * A protection check is difficult so
1598c2ecf20Sopenharmony_ci				 * just be safe and disable write
1608c2ecf20Sopenharmony_ci				 */
1618c2ecf20Sopenharmony_ci				make_migration_entry_read(&entry);
1628c2ecf20Sopenharmony_ci				newpte = swp_entry_to_pte(entry);
1638c2ecf20Sopenharmony_ci				if (pte_swp_soft_dirty(oldpte))
1648c2ecf20Sopenharmony_ci					newpte = pte_swp_mksoft_dirty(newpte);
1658c2ecf20Sopenharmony_ci				if (pte_swp_uffd_wp(oldpte))
1668c2ecf20Sopenharmony_ci					newpte = pte_swp_mkuffd_wp(newpte);
1678c2ecf20Sopenharmony_ci			} else if (is_write_device_private_entry(entry)) {
1688c2ecf20Sopenharmony_ci				/*
1698c2ecf20Sopenharmony_ci				 * We do not preserve soft-dirtiness. See
1708c2ecf20Sopenharmony_ci				 * copy_one_pte() for explanation.
1718c2ecf20Sopenharmony_ci				 */
1728c2ecf20Sopenharmony_ci				make_device_private_entry_read(&entry);
1738c2ecf20Sopenharmony_ci				newpte = swp_entry_to_pte(entry);
1748c2ecf20Sopenharmony_ci				if (pte_swp_uffd_wp(oldpte))
1758c2ecf20Sopenharmony_ci					newpte = pte_swp_mkuffd_wp(newpte);
1768c2ecf20Sopenharmony_ci			} else {
1778c2ecf20Sopenharmony_ci				newpte = oldpte;
1788c2ecf20Sopenharmony_ci			}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci			if (uffd_wp)
1818c2ecf20Sopenharmony_ci				newpte = pte_swp_mkuffd_wp(newpte);
1828c2ecf20Sopenharmony_ci			else if (uffd_wp_resolve)
1838c2ecf20Sopenharmony_ci				newpte = pte_swp_clear_uffd_wp(newpte);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci			if (!pte_same(oldpte, newpte)) {
1868c2ecf20Sopenharmony_ci				set_pte_at(vma->vm_mm, addr, pte, newpte);
1878c2ecf20Sopenharmony_ci				pages++;
1888c2ecf20Sopenharmony_ci			}
1898c2ecf20Sopenharmony_ci		}
1908c2ecf20Sopenharmony_ci	} while (pte++, addr += PAGE_SIZE, addr != end);
1918c2ecf20Sopenharmony_ci	arch_leave_lazy_mmu_mode();
1928c2ecf20Sopenharmony_ci	pte_unmap_unlock(pte - 1, ptl);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return pages;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/*
1988c2ecf20Sopenharmony_ci * Used when setting automatic NUMA hinting protection where it is
1998c2ecf20Sopenharmony_ci * critical that a numa hinting PMD is not confused with a bad PMD.
2008c2ecf20Sopenharmony_ci */
2018c2ecf20Sopenharmony_cistatic inline int pmd_none_or_clear_bad_unless_trans_huge(pmd_t *pmd)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	pmd_t pmdval = pmd_read_atomic(pmd);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/* See pmd_none_or_trans_huge_or_clear_bad for info on barrier */
2068c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE
2078c2ecf20Sopenharmony_ci	barrier();
2088c2ecf20Sopenharmony_ci#endif
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (pmd_none(pmdval))
2118c2ecf20Sopenharmony_ci		return 1;
2128c2ecf20Sopenharmony_ci	if (pmd_trans_huge(pmdval))
2138c2ecf20Sopenharmony_ci		return 0;
2148c2ecf20Sopenharmony_ci	if (unlikely(pmd_bad(pmdval))) {
2158c2ecf20Sopenharmony_ci		pmd_clear_bad(pmd);
2168c2ecf20Sopenharmony_ci		return 1;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return 0;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic inline unsigned long change_pmd_range(struct vm_area_struct *vma,
2238c2ecf20Sopenharmony_ci		pud_t *pud, unsigned long addr, unsigned long end,
2248c2ecf20Sopenharmony_ci		pgprot_t newprot, unsigned long cp_flags)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	pmd_t *pmd;
2278c2ecf20Sopenharmony_ci	unsigned long next;
2288c2ecf20Sopenharmony_ci	unsigned long pages = 0;
2298c2ecf20Sopenharmony_ci	unsigned long nr_huge_updates = 0;
2308c2ecf20Sopenharmony_ci	struct mmu_notifier_range range;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	range.start = 0;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	pmd = pmd_offset(pud, addr);
2358c2ecf20Sopenharmony_ci	do {
2368c2ecf20Sopenharmony_ci		unsigned long this_pages;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci		next = pmd_addr_end(addr, end);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		/*
2418c2ecf20Sopenharmony_ci		 * Automatic NUMA balancing walks the tables with mmap_lock
2428c2ecf20Sopenharmony_ci		 * held for read. It's possible a parallel update to occur
2438c2ecf20Sopenharmony_ci		 * between pmd_trans_huge() and a pmd_none_or_clear_bad()
2448c2ecf20Sopenharmony_ci		 * check leading to a false positive and clearing.
2458c2ecf20Sopenharmony_ci		 * Hence, it's necessary to atomically read the PMD value
2468c2ecf20Sopenharmony_ci		 * for all the checks.
2478c2ecf20Sopenharmony_ci		 */
2488c2ecf20Sopenharmony_ci		if (!is_swap_pmd(*pmd) && !pmd_devmap(*pmd) &&
2498c2ecf20Sopenharmony_ci		     pmd_none_or_clear_bad_unless_trans_huge(pmd))
2508c2ecf20Sopenharmony_ci			goto next;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci		/* invoke the mmu notifier if the pmd is populated */
2538c2ecf20Sopenharmony_ci		if (!range.start) {
2548c2ecf20Sopenharmony_ci			mmu_notifier_range_init(&range,
2558c2ecf20Sopenharmony_ci				MMU_NOTIFY_PROTECTION_VMA, 0,
2568c2ecf20Sopenharmony_ci				vma, vma->vm_mm, addr, end);
2578c2ecf20Sopenharmony_ci			mmu_notifier_invalidate_range_start(&range);
2588c2ecf20Sopenharmony_ci		}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		if (is_swap_pmd(*pmd) || pmd_trans_huge(*pmd) || pmd_devmap(*pmd)) {
2618c2ecf20Sopenharmony_ci			if (next - addr != HPAGE_PMD_SIZE) {
2628c2ecf20Sopenharmony_ci				__split_huge_pmd(vma, pmd, addr, false, NULL);
2638c2ecf20Sopenharmony_ci			} else {
2648c2ecf20Sopenharmony_ci				int nr_ptes = change_huge_pmd(vma, pmd, addr,
2658c2ecf20Sopenharmony_ci							      newprot, cp_flags);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci				if (nr_ptes) {
2688c2ecf20Sopenharmony_ci					if (nr_ptes == HPAGE_PMD_NR) {
2698c2ecf20Sopenharmony_ci						pages += HPAGE_PMD_NR;
2708c2ecf20Sopenharmony_ci						nr_huge_updates++;
2718c2ecf20Sopenharmony_ci					}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci					/* huge pmd was handled */
2748c2ecf20Sopenharmony_ci					goto next;
2758c2ecf20Sopenharmony_ci				}
2768c2ecf20Sopenharmony_ci			}
2778c2ecf20Sopenharmony_ci			/* fall through, the trans huge pmd just split */
2788c2ecf20Sopenharmony_ci		}
2798c2ecf20Sopenharmony_ci		this_pages = change_pte_range(vma, pmd, addr, next, newprot,
2808c2ecf20Sopenharmony_ci					      cp_flags);
2818c2ecf20Sopenharmony_ci		pages += this_pages;
2828c2ecf20Sopenharmony_cinext:
2838c2ecf20Sopenharmony_ci		cond_resched();
2848c2ecf20Sopenharmony_ci	} while (pmd++, addr = next, addr != end);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (range.start)
2878c2ecf20Sopenharmony_ci		mmu_notifier_invalidate_range_end(&range);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (nr_huge_updates)
2908c2ecf20Sopenharmony_ci		count_vm_numa_events(NUMA_HUGE_PTE_UPDATES, nr_huge_updates);
2918c2ecf20Sopenharmony_ci	return pages;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic inline unsigned long change_pud_range(struct vm_area_struct *vma,
2958c2ecf20Sopenharmony_ci		p4d_t *p4d, unsigned long addr, unsigned long end,
2968c2ecf20Sopenharmony_ci		pgprot_t newprot, unsigned long cp_flags)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	pud_t *pud;
2998c2ecf20Sopenharmony_ci	unsigned long next;
3008c2ecf20Sopenharmony_ci	unsigned long pages = 0;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	pud = pud_offset(p4d, addr);
3038c2ecf20Sopenharmony_ci	do {
3048c2ecf20Sopenharmony_ci		next = pud_addr_end(addr, end);
3058c2ecf20Sopenharmony_ci		if (pud_none_or_clear_bad(pud))
3068c2ecf20Sopenharmony_ci			continue;
3078c2ecf20Sopenharmony_ci		pages += change_pmd_range(vma, pud, addr, next, newprot,
3088c2ecf20Sopenharmony_ci					  cp_flags);
3098c2ecf20Sopenharmony_ci	} while (pud++, addr = next, addr != end);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	return pages;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic inline unsigned long change_p4d_range(struct vm_area_struct *vma,
3158c2ecf20Sopenharmony_ci		pgd_t *pgd, unsigned long addr, unsigned long end,
3168c2ecf20Sopenharmony_ci		pgprot_t newprot, unsigned long cp_flags)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	p4d_t *p4d;
3198c2ecf20Sopenharmony_ci	unsigned long next;
3208c2ecf20Sopenharmony_ci	unsigned long pages = 0;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	p4d = p4d_offset(pgd, addr);
3238c2ecf20Sopenharmony_ci	do {
3248c2ecf20Sopenharmony_ci		next = p4d_addr_end(addr, end);
3258c2ecf20Sopenharmony_ci		if (p4d_none_or_clear_bad(p4d))
3268c2ecf20Sopenharmony_ci			continue;
3278c2ecf20Sopenharmony_ci		pages += change_pud_range(vma, p4d, addr, next, newprot,
3288c2ecf20Sopenharmony_ci					  cp_flags);
3298c2ecf20Sopenharmony_ci	} while (p4d++, addr = next, addr != end);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	return pages;
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic unsigned long change_protection_range(struct vm_area_struct *vma,
3358c2ecf20Sopenharmony_ci		unsigned long addr, unsigned long end, pgprot_t newprot,
3368c2ecf20Sopenharmony_ci		unsigned long cp_flags)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct mm_struct *mm = vma->vm_mm;
3398c2ecf20Sopenharmony_ci	pgd_t *pgd;
3408c2ecf20Sopenharmony_ci	unsigned long next;
3418c2ecf20Sopenharmony_ci	unsigned long start = addr;
3428c2ecf20Sopenharmony_ci	unsigned long pages = 0;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	BUG_ON(addr >= end);
3458c2ecf20Sopenharmony_ci	pgd = pgd_offset(mm, addr);
3468c2ecf20Sopenharmony_ci	flush_cache_range(vma, addr, end);
3478c2ecf20Sopenharmony_ci	inc_tlb_flush_pending(mm);
3488c2ecf20Sopenharmony_ci	do {
3498c2ecf20Sopenharmony_ci		next = pgd_addr_end(addr, end);
3508c2ecf20Sopenharmony_ci		if (pgd_none_or_clear_bad(pgd))
3518c2ecf20Sopenharmony_ci			continue;
3528c2ecf20Sopenharmony_ci		pages += change_p4d_range(vma, pgd, addr, next, newprot,
3538c2ecf20Sopenharmony_ci					  cp_flags);
3548c2ecf20Sopenharmony_ci	} while (pgd++, addr = next, addr != end);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* Only flush the TLB if we actually modified any entries: */
3578c2ecf20Sopenharmony_ci	if (pages)
3588c2ecf20Sopenharmony_ci		flush_tlb_range(vma, start, end);
3598c2ecf20Sopenharmony_ci	dec_tlb_flush_pending(mm);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	return pages;
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ciunsigned long change_protection(struct vm_area_struct *vma, unsigned long start,
3658c2ecf20Sopenharmony_ci		       unsigned long end, pgprot_t newprot,
3668c2ecf20Sopenharmony_ci		       unsigned long cp_flags)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	unsigned long pages;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	BUG_ON((cp_flags & MM_CP_UFFD_WP_ALL) == MM_CP_UFFD_WP_ALL);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (is_vm_hugetlb_page(vma))
3738c2ecf20Sopenharmony_ci		pages = hugetlb_change_protection(vma, start, end, newprot);
3748c2ecf20Sopenharmony_ci	else
3758c2ecf20Sopenharmony_ci		pages = change_protection_range(vma, start, end, newprot,
3768c2ecf20Sopenharmony_ci						cp_flags);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	return pages;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic int prot_none_pte_entry(pte_t *pte, unsigned long addr,
3828c2ecf20Sopenharmony_ci			       unsigned long next, struct mm_walk *walk)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	return pfn_modify_allowed(pte_pfn(*pte), *(pgprot_t *)(walk->private)) ?
3858c2ecf20Sopenharmony_ci		0 : -EACCES;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic int prot_none_hugetlb_entry(pte_t *pte, unsigned long hmask,
3898c2ecf20Sopenharmony_ci				   unsigned long addr, unsigned long next,
3908c2ecf20Sopenharmony_ci				   struct mm_walk *walk)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	return pfn_modify_allowed(pte_pfn(*pte), *(pgprot_t *)(walk->private)) ?
3938c2ecf20Sopenharmony_ci		0 : -EACCES;
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic int prot_none_test(unsigned long addr, unsigned long next,
3978c2ecf20Sopenharmony_ci			  struct mm_walk *walk)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	return 0;
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic const struct mm_walk_ops prot_none_walk_ops = {
4038c2ecf20Sopenharmony_ci	.pte_entry		= prot_none_pte_entry,
4048c2ecf20Sopenharmony_ci	.hugetlb_entry		= prot_none_hugetlb_entry,
4058c2ecf20Sopenharmony_ci	.test_walk		= prot_none_test,
4068c2ecf20Sopenharmony_ci};
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ciint
4098c2ecf20Sopenharmony_cimprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
4108c2ecf20Sopenharmony_ci	unsigned long start, unsigned long end, unsigned long newflags)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct mm_struct *mm = vma->vm_mm;
4138c2ecf20Sopenharmony_ci	unsigned long oldflags = vma->vm_flags;
4148c2ecf20Sopenharmony_ci	long nrpages = (end - start) >> PAGE_SHIFT;
4158c2ecf20Sopenharmony_ci	unsigned long charged = 0;
4168c2ecf20Sopenharmony_ci	pgoff_t pgoff;
4178c2ecf20Sopenharmony_ci	int error;
4188c2ecf20Sopenharmony_ci	int dirty_accountable = 0;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (newflags == oldflags) {
4218c2ecf20Sopenharmony_ci		*pprev = vma;
4228c2ecf20Sopenharmony_ci		return 0;
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/*
4268c2ecf20Sopenharmony_ci	 * Do PROT_NONE PFN permission checks here when we can still
4278c2ecf20Sopenharmony_ci	 * bail out without undoing a lot of state. This is a rather
4288c2ecf20Sopenharmony_ci	 * uncommon case, so doesn't need to be very optimized.
4298c2ecf20Sopenharmony_ci	 */
4308c2ecf20Sopenharmony_ci	if (arch_has_pfn_modify_check() &&
4318c2ecf20Sopenharmony_ci	    (vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)) &&
4328c2ecf20Sopenharmony_ci	    (newflags & VM_ACCESS_FLAGS) == 0) {
4338c2ecf20Sopenharmony_ci		pgprot_t new_pgprot = vm_get_page_prot(newflags);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci		error = walk_page_range(current->mm, start, end,
4368c2ecf20Sopenharmony_ci				&prot_none_walk_ops, &new_pgprot);
4378c2ecf20Sopenharmony_ci		if (error)
4388c2ecf20Sopenharmony_ci			return error;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/*
4428c2ecf20Sopenharmony_ci	 * If we make a private mapping writable we increase our commit;
4438c2ecf20Sopenharmony_ci	 * but (without finer accounting) cannot reduce our commit if we
4448c2ecf20Sopenharmony_ci	 * make it unwritable again. hugetlb mapping were accounted for
4458c2ecf20Sopenharmony_ci	 * even if read-only so there is no need to account for them here
4468c2ecf20Sopenharmony_ci	 */
4478c2ecf20Sopenharmony_ci	if (newflags & VM_WRITE) {
4488c2ecf20Sopenharmony_ci		/* Check space limits when area turns into data. */
4498c2ecf20Sopenharmony_ci		if (!may_expand_vm(mm, newflags, nrpages) &&
4508c2ecf20Sopenharmony_ci				may_expand_vm(mm, oldflags, nrpages))
4518c2ecf20Sopenharmony_ci			return -ENOMEM;
4528c2ecf20Sopenharmony_ci		if (!(oldflags & (VM_ACCOUNT|VM_WRITE|VM_HUGETLB|
4538c2ecf20Sopenharmony_ci						VM_SHARED|VM_NORESERVE))) {
4548c2ecf20Sopenharmony_ci			charged = nrpages;
4558c2ecf20Sopenharmony_ci			if (security_vm_enough_memory_mm(mm, charged))
4568c2ecf20Sopenharmony_ci				return -ENOMEM;
4578c2ecf20Sopenharmony_ci			newflags |= VM_ACCOUNT;
4588c2ecf20Sopenharmony_ci		}
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	/*
4628c2ecf20Sopenharmony_ci	 * First try to merge with previous and/or next vma.
4638c2ecf20Sopenharmony_ci	 */
4648c2ecf20Sopenharmony_ci	pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
4658c2ecf20Sopenharmony_ci	*pprev = vma_merge(mm, *pprev, start, end, newflags,
4668c2ecf20Sopenharmony_ci			   vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma),
4678c2ecf20Sopenharmony_ci			   vma->vm_userfaultfd_ctx, anon_vma_name(vma));
4688c2ecf20Sopenharmony_ci	if (*pprev) {
4698c2ecf20Sopenharmony_ci		vma = *pprev;
4708c2ecf20Sopenharmony_ci		VM_WARN_ON((vma->vm_flags ^ newflags) & ~VM_SOFTDIRTY);
4718c2ecf20Sopenharmony_ci		goto success;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	*pprev = vma;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	if (start != vma->vm_start) {
4778c2ecf20Sopenharmony_ci		error = split_vma(mm, vma, start, 1);
4788c2ecf20Sopenharmony_ci		if (error)
4798c2ecf20Sopenharmony_ci			goto fail;
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	if (end != vma->vm_end) {
4838c2ecf20Sopenharmony_ci		error = split_vma(mm, vma, end, 0);
4848c2ecf20Sopenharmony_ci		if (error)
4858c2ecf20Sopenharmony_ci			goto fail;
4868c2ecf20Sopenharmony_ci	}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cisuccess:
4898c2ecf20Sopenharmony_ci	/*
4908c2ecf20Sopenharmony_ci	 * vm_flags and vm_page_prot are protected by the mmap_lock
4918c2ecf20Sopenharmony_ci	 * held in write mode.
4928c2ecf20Sopenharmony_ci	 */
4938c2ecf20Sopenharmony_ci	vma->vm_flags = newflags;
4948c2ecf20Sopenharmony_ci	dirty_accountable = vma_wants_writenotify(vma, vma->vm_page_prot);
4958c2ecf20Sopenharmony_ci	vma_set_page_prot(vma);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	change_protection(vma, start, end, vma->vm_page_prot,
4988c2ecf20Sopenharmony_ci			  dirty_accountable ? MM_CP_DIRTY_ACCT : 0);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	/*
5018c2ecf20Sopenharmony_ci	 * Private VM_LOCKED VMA becoming writable: trigger COW to avoid major
5028c2ecf20Sopenharmony_ci	 * fault on access.
5038c2ecf20Sopenharmony_ci	 */
5048c2ecf20Sopenharmony_ci	if ((oldflags & (VM_WRITE | VM_SHARED | VM_LOCKED)) == VM_LOCKED &&
5058c2ecf20Sopenharmony_ci			(newflags & VM_WRITE)) {
5068c2ecf20Sopenharmony_ci		populate_vma_page_range(vma, start, end, NULL);
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	vm_stat_account(mm, oldflags, -nrpages);
5108c2ecf20Sopenharmony_ci	vm_stat_account(mm, newflags, nrpages);
5118c2ecf20Sopenharmony_ci	perf_event_mmap(vma);
5128c2ecf20Sopenharmony_ci	return 0;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cifail:
5158c2ecf20Sopenharmony_ci	vm_unacct_memory(charged);
5168c2ecf20Sopenharmony_ci	return error;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci/*
5208c2ecf20Sopenharmony_ci * pkey==-1 when doing a legacy mprotect()
5218c2ecf20Sopenharmony_ci */
5228c2ecf20Sopenharmony_cistatic int do_mprotect_pkey(unsigned long start, size_t len,
5238c2ecf20Sopenharmony_ci		unsigned long prot, int pkey)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	unsigned long nstart, end, tmp, reqprot;
5268c2ecf20Sopenharmony_ci	struct vm_area_struct *vma, *prev;
5278c2ecf20Sopenharmony_ci	int error = -EINVAL;
5288c2ecf20Sopenharmony_ci	const int grows = prot & (PROT_GROWSDOWN|PROT_GROWSUP);
5298c2ecf20Sopenharmony_ci	const bool rier = (current->personality & READ_IMPLIES_EXEC) &&
5308c2ecf20Sopenharmony_ci				(prot & PROT_READ);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	error = 0;
5338c2ecf20Sopenharmony_ci	trace_vendor_do_mprotect_pkey(prot, &error);
5348c2ecf20Sopenharmony_ci	if (error)
5358c2ecf20Sopenharmony_ci		return error;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	start = untagged_addr(start);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	if (prot & PROT_EXEC) {
5408c2ecf20Sopenharmony_ci		CALL_HCK_LITE_HOOK(find_jit_memory_lhck, current, start, len, &error);
5418c2ecf20Sopenharmony_ci		if (error) {
5428c2ecf20Sopenharmony_ci			pr_info("JITINFO: mprotect protection triggered");
5438c2ecf20Sopenharmony_ci			return error;
5448c2ecf20Sopenharmony_ci		}
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	prot &= ~(PROT_GROWSDOWN|PROT_GROWSUP);
5488c2ecf20Sopenharmony_ci	if (grows == (PROT_GROWSDOWN|PROT_GROWSUP)) /* can't be both */
5498c2ecf20Sopenharmony_ci		return -EINVAL;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	if (start & ~PAGE_MASK)
5528c2ecf20Sopenharmony_ci		return -EINVAL;
5538c2ecf20Sopenharmony_ci	if (!len)
5548c2ecf20Sopenharmony_ci		return 0;
5558c2ecf20Sopenharmony_ci	len = PAGE_ALIGN(len);
5568c2ecf20Sopenharmony_ci	end = start + len;
5578c2ecf20Sopenharmony_ci	if (end <= start)
5588c2ecf20Sopenharmony_ci		return -ENOMEM;
5598c2ecf20Sopenharmony_ci	if (!arch_validate_prot(prot, start))
5608c2ecf20Sopenharmony_ci		return -EINVAL;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	reqprot = prot;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	if (mmap_write_lock_killable(current->mm))
5658c2ecf20Sopenharmony_ci		return -EINTR;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	/*
5688c2ecf20Sopenharmony_ci	 * If userspace did not allocate the pkey, do not let
5698c2ecf20Sopenharmony_ci	 * them use it here.
5708c2ecf20Sopenharmony_ci	 */
5718c2ecf20Sopenharmony_ci	error = -EINVAL;
5728c2ecf20Sopenharmony_ci	if ((pkey != -1) && !mm_pkey_is_allocated(current->mm, pkey))
5738c2ecf20Sopenharmony_ci		goto out;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	vma = find_vma(current->mm, start);
5768c2ecf20Sopenharmony_ci	error = -ENOMEM;
5778c2ecf20Sopenharmony_ci	if (!vma)
5788c2ecf20Sopenharmony_ci		goto out;
5798c2ecf20Sopenharmony_ci	prev = vma->vm_prev;
5808c2ecf20Sopenharmony_ci	if (unlikely(grows & PROT_GROWSDOWN)) {
5818c2ecf20Sopenharmony_ci		if (vma->vm_start >= end)
5828c2ecf20Sopenharmony_ci			goto out;
5838c2ecf20Sopenharmony_ci		start = vma->vm_start;
5848c2ecf20Sopenharmony_ci		error = -EINVAL;
5858c2ecf20Sopenharmony_ci		if (!(vma->vm_flags & VM_GROWSDOWN))
5868c2ecf20Sopenharmony_ci			goto out;
5878c2ecf20Sopenharmony_ci	} else {
5888c2ecf20Sopenharmony_ci		if (vma->vm_start > start)
5898c2ecf20Sopenharmony_ci			goto out;
5908c2ecf20Sopenharmony_ci		if (unlikely(grows & PROT_GROWSUP)) {
5918c2ecf20Sopenharmony_ci			end = vma->vm_end;
5928c2ecf20Sopenharmony_ci			error = -EINVAL;
5938c2ecf20Sopenharmony_ci			if (!(vma->vm_flags & VM_GROWSUP))
5948c2ecf20Sopenharmony_ci				goto out;
5958c2ecf20Sopenharmony_ci		}
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci	if (start > vma->vm_start)
5988c2ecf20Sopenharmony_ci		prev = vma;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	for (nstart = start ; ; ) {
6018c2ecf20Sopenharmony_ci		unsigned long mask_off_old_flags;
6028c2ecf20Sopenharmony_ci		unsigned long newflags;
6038c2ecf20Sopenharmony_ci		int new_vma_pkey;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci		/* Here we know that vma->vm_start <= nstart < vma->vm_end. */
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci		/* Does the application expect PROT_READ to imply PROT_EXEC */
6088c2ecf20Sopenharmony_ci		if (rier && (vma->vm_flags & VM_MAYEXEC))
6098c2ecf20Sopenharmony_ci			prot |= PROT_EXEC;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci		/*
6128c2ecf20Sopenharmony_ci		 * Each mprotect() call explicitly passes r/w/x permissions.
6138c2ecf20Sopenharmony_ci		 * If a permission is not passed to mprotect(), it must be
6148c2ecf20Sopenharmony_ci		 * cleared from the VMA.
6158c2ecf20Sopenharmony_ci		 */
6168c2ecf20Sopenharmony_ci		mask_off_old_flags = VM_READ | VM_WRITE | VM_EXEC |
6178c2ecf20Sopenharmony_ci					VM_FLAGS_CLEAR;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci		new_vma_pkey = arch_override_mprotect_pkey(vma, prot, pkey);
6208c2ecf20Sopenharmony_ci		newflags = calc_vm_prot_bits(prot, new_vma_pkey);
6218c2ecf20Sopenharmony_ci		newflags |= (vma->vm_flags & ~mask_off_old_flags);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci		/* newflags >> 4 shift VM_MAY% in place of VM_% */
6248c2ecf20Sopenharmony_ci		if ((newflags & ~(newflags >> 4)) & VM_ACCESS_FLAGS) {
6258c2ecf20Sopenharmony_ci			error = -EACCES;
6268c2ecf20Sopenharmony_ci			goto out;
6278c2ecf20Sopenharmony_ci		}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci		/* Allow architectures to sanity-check the new flags */
6308c2ecf20Sopenharmony_ci		if (!arch_validate_flags(newflags)) {
6318c2ecf20Sopenharmony_ci			error = -EINVAL;
6328c2ecf20Sopenharmony_ci			goto out;
6338c2ecf20Sopenharmony_ci		}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci		error = security_file_mprotect(vma, reqprot, prot);
6368c2ecf20Sopenharmony_ci		if (error)
6378c2ecf20Sopenharmony_ci			goto out;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci		tmp = vma->vm_end;
6408c2ecf20Sopenharmony_ci		if (tmp > end)
6418c2ecf20Sopenharmony_ci			tmp = end;
6428c2ecf20Sopenharmony_ci		error = mprotect_fixup(vma, &prev, nstart, tmp, newflags);
6438c2ecf20Sopenharmony_ci		if (error)
6448c2ecf20Sopenharmony_ci			goto out;
6458c2ecf20Sopenharmony_ci		nstart = tmp;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci		if (nstart < prev->vm_end)
6488c2ecf20Sopenharmony_ci			nstart = prev->vm_end;
6498c2ecf20Sopenharmony_ci		if (nstart >= end)
6508c2ecf20Sopenharmony_ci			goto out;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci		vma = prev->vm_next;
6538c2ecf20Sopenharmony_ci		if (!vma || vma->vm_start != nstart) {
6548c2ecf20Sopenharmony_ci			error = -ENOMEM;
6558c2ecf20Sopenharmony_ci			goto out;
6568c2ecf20Sopenharmony_ci		}
6578c2ecf20Sopenharmony_ci		prot = reqprot;
6588c2ecf20Sopenharmony_ci	}
6598c2ecf20Sopenharmony_ciout:
6608c2ecf20Sopenharmony_ci	mmap_write_unlock(current->mm);
6618c2ecf20Sopenharmony_ci	return error;
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ciSYSCALL_DEFINE3(mprotect, unsigned long, start, size_t, len,
6658c2ecf20Sopenharmony_ci		unsigned long, prot)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	return do_mprotect_pkey(start, len, prot, -1);
6688c2ecf20Sopenharmony_ci}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_HAS_PKEYS
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ciSYSCALL_DEFINE4(pkey_mprotect, unsigned long, start, size_t, len,
6738c2ecf20Sopenharmony_ci		unsigned long, prot, int, pkey)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	return do_mprotect_pkey(start, len, prot, pkey);
6768c2ecf20Sopenharmony_ci}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ciSYSCALL_DEFINE2(pkey_alloc, unsigned long, flags, unsigned long, init_val)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	int pkey;
6818c2ecf20Sopenharmony_ci	int ret;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	/* No flags supported yet. */
6848c2ecf20Sopenharmony_ci	if (flags)
6858c2ecf20Sopenharmony_ci		return -EINVAL;
6868c2ecf20Sopenharmony_ci	/* check for unsupported init values */
6878c2ecf20Sopenharmony_ci	if (init_val & ~PKEY_ACCESS_MASK)
6888c2ecf20Sopenharmony_ci		return -EINVAL;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	mmap_write_lock(current->mm);
6918c2ecf20Sopenharmony_ci	pkey = mm_pkey_alloc(current->mm);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	ret = -ENOSPC;
6948c2ecf20Sopenharmony_ci	if (pkey == -1)
6958c2ecf20Sopenharmony_ci		goto out;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	ret = arch_set_user_pkey_access(current, pkey, init_val);
6988c2ecf20Sopenharmony_ci	if (ret) {
6998c2ecf20Sopenharmony_ci		mm_pkey_free(current->mm, pkey);
7008c2ecf20Sopenharmony_ci		goto out;
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci	ret = pkey;
7038c2ecf20Sopenharmony_ciout:
7048c2ecf20Sopenharmony_ci	mmap_write_unlock(current->mm);
7058c2ecf20Sopenharmony_ci	return ret;
7068c2ecf20Sopenharmony_ci}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ciSYSCALL_DEFINE1(pkey_free, int, pkey)
7098c2ecf20Sopenharmony_ci{
7108c2ecf20Sopenharmony_ci	int ret;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	mmap_write_lock(current->mm);
7138c2ecf20Sopenharmony_ci	ret = mm_pkey_free(current->mm, pkey);
7148c2ecf20Sopenharmony_ci	mmap_write_unlock(current->mm);
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	/*
7178c2ecf20Sopenharmony_ci	 * We could provie warnings or errors if any VMA still
7188c2ecf20Sopenharmony_ci	 * has the pkey set here.
7198c2ecf20Sopenharmony_ci	 */
7208c2ecf20Sopenharmony_ci	return ret;
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci#endif /* CONFIG_ARCH_HAS_PKEYS */
724