18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2014, The Linux Foundation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#include <linux/kernel.h>
68c2ecf20Sopenharmony_ci#include <linux/mm.h>
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/sched.h>
98c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
128c2ecf20Sopenharmony_ci#include <asm/set_memory.h>
138c2ecf20Sopenharmony_ci#include <asm/tlbflush.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistruct page_change_data {
168c2ecf20Sopenharmony_ci	pgprot_t set_mask;
178c2ecf20Sopenharmony_ci	pgprot_t clear_mask;
188c2ecf20Sopenharmony_ci};
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cibool rodata_full __ro_after_init = IS_ENABLED(CONFIG_RODATA_FULL_DEFAULT_ENABLED);
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic int change_page_range(pte_t *ptep, unsigned long addr, void *data)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	struct page_change_data *cdata = data;
258c2ecf20Sopenharmony_ci	pte_t pte = READ_ONCE(*ptep);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	pte = clear_pte_bit(pte, cdata->clear_mask);
288c2ecf20Sopenharmony_ci	pte = set_pte_bit(pte, cdata->set_mask);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	set_pte(ptep, pte);
318c2ecf20Sopenharmony_ci	return 0;
328c2ecf20Sopenharmony_ci}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/*
358c2ecf20Sopenharmony_ci * This function assumes that the range is mapped with PAGE_SIZE pages.
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_cistatic int __change_memory_common(unsigned long start, unsigned long size,
388c2ecf20Sopenharmony_ci				pgprot_t set_mask, pgprot_t clear_mask)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct page_change_data data;
418c2ecf20Sopenharmony_ci	int ret;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	data.set_mask = set_mask;
448c2ecf20Sopenharmony_ci	data.clear_mask = clear_mask;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	ret = apply_to_page_range(&init_mm, start, size, change_page_range,
478c2ecf20Sopenharmony_ci					&data);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	flush_tlb_kernel_range(start, start + size);
508c2ecf20Sopenharmony_ci	return ret;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int change_memory_common(unsigned long addr, int numpages,
548c2ecf20Sopenharmony_ci				pgprot_t set_mask, pgprot_t clear_mask)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	unsigned long start = addr;
578c2ecf20Sopenharmony_ci	unsigned long size = PAGE_SIZE * numpages;
588c2ecf20Sopenharmony_ci	unsigned long end = start + size;
598c2ecf20Sopenharmony_ci	struct vm_struct *area;
608c2ecf20Sopenharmony_ci	int i;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (!PAGE_ALIGNED(addr)) {
638c2ecf20Sopenharmony_ci		start &= PAGE_MASK;
648c2ecf20Sopenharmony_ci		end = start + size;
658c2ecf20Sopenharmony_ci		WARN_ON_ONCE(1);
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/*
698c2ecf20Sopenharmony_ci	 * Kernel VA mappings are always live, and splitting live section
708c2ecf20Sopenharmony_ci	 * mappings into page mappings may cause TLB conflicts. This means
718c2ecf20Sopenharmony_ci	 * we have to ensure that changing the permission bits of the range
728c2ecf20Sopenharmony_ci	 * we are operating on does not result in such splitting.
738c2ecf20Sopenharmony_ci	 *
748c2ecf20Sopenharmony_ci	 * Let's restrict ourselves to mappings created by vmalloc (or vmap).
758c2ecf20Sopenharmony_ci	 * Those are guaranteed to consist entirely of page mappings, and
768c2ecf20Sopenharmony_ci	 * splitting is never needed.
778c2ecf20Sopenharmony_ci	 *
788c2ecf20Sopenharmony_ci	 * So check whether the [addr, addr + size) interval is entirely
798c2ecf20Sopenharmony_ci	 * covered by precisely one VM area that has the VM_ALLOC flag set.
808c2ecf20Sopenharmony_ci	 */
818c2ecf20Sopenharmony_ci	area = find_vm_area((void *)addr);
828c2ecf20Sopenharmony_ci	if (!area ||
838c2ecf20Sopenharmony_ci	    end > (unsigned long)area->addr + area->size ||
848c2ecf20Sopenharmony_ci	    !(area->flags & VM_ALLOC))
858c2ecf20Sopenharmony_ci		return -EINVAL;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (!numpages)
888c2ecf20Sopenharmony_ci		return 0;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/*
918c2ecf20Sopenharmony_ci	 * If we are manipulating read-only permissions, apply the same
928c2ecf20Sopenharmony_ci	 * change to the linear mapping of the pages that back this VM area.
938c2ecf20Sopenharmony_ci	 */
948c2ecf20Sopenharmony_ci	if (rodata_full && (pgprot_val(set_mask) == PTE_RDONLY ||
958c2ecf20Sopenharmony_ci			    pgprot_val(clear_mask) == PTE_RDONLY)) {
968c2ecf20Sopenharmony_ci		for (i = 0; i < area->nr_pages; i++) {
978c2ecf20Sopenharmony_ci			__change_memory_common((u64)page_address(area->pages[i]),
988c2ecf20Sopenharmony_ci					       PAGE_SIZE, set_mask, clear_mask);
998c2ecf20Sopenharmony_ci		}
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/*
1038c2ecf20Sopenharmony_ci	 * Get rid of potentially aliasing lazily unmapped vm areas that may
1048c2ecf20Sopenharmony_ci	 * have permissions set that deviate from the ones we are setting here.
1058c2ecf20Sopenharmony_ci	 */
1068c2ecf20Sopenharmony_ci	vm_unmap_aliases();
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return __change_memory_common(start, size, set_mask, clear_mask);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ciint set_memory_ro(unsigned long addr, int numpages)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	return change_memory_common(addr, numpages,
1148c2ecf20Sopenharmony_ci					__pgprot(PTE_RDONLY),
1158c2ecf20Sopenharmony_ci					__pgprot(PTE_WRITE));
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ciint set_memory_rw(unsigned long addr, int numpages)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	return change_memory_common(addr, numpages,
1218c2ecf20Sopenharmony_ci					__pgprot(PTE_WRITE),
1228c2ecf20Sopenharmony_ci					__pgprot(PTE_RDONLY));
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ciint set_memory_nx(unsigned long addr, int numpages)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	return change_memory_common(addr, numpages,
1288c2ecf20Sopenharmony_ci					__pgprot(PTE_PXN),
1298c2ecf20Sopenharmony_ci					__pgprot(PTE_MAYBE_GP));
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ciint set_memory_x(unsigned long addr, int numpages)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	return change_memory_common(addr, numpages,
1358c2ecf20Sopenharmony_ci					__pgprot(PTE_MAYBE_GP),
1368c2ecf20Sopenharmony_ci					__pgprot(PTE_PXN));
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ciint set_memory_valid(unsigned long addr, int numpages, int enable)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	if (enable)
1428c2ecf20Sopenharmony_ci		return __change_memory_common(addr, PAGE_SIZE * numpages,
1438c2ecf20Sopenharmony_ci					__pgprot(PTE_VALID),
1448c2ecf20Sopenharmony_ci					__pgprot(0));
1458c2ecf20Sopenharmony_ci	else
1468c2ecf20Sopenharmony_ci		return __change_memory_common(addr, PAGE_SIZE * numpages,
1478c2ecf20Sopenharmony_ci					__pgprot(0),
1488c2ecf20Sopenharmony_ci					__pgprot(PTE_VALID));
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ciint set_direct_map_invalid_noflush(struct page *page)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct page_change_data data = {
1548c2ecf20Sopenharmony_ci		.set_mask = __pgprot(0),
1558c2ecf20Sopenharmony_ci		.clear_mask = __pgprot(PTE_VALID),
1568c2ecf20Sopenharmony_ci	};
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (!rodata_full)
1598c2ecf20Sopenharmony_ci		return 0;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return apply_to_page_range(&init_mm,
1628c2ecf20Sopenharmony_ci				   (unsigned long)page_address(page),
1638c2ecf20Sopenharmony_ci				   PAGE_SIZE, change_page_range, &data);
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ciint set_direct_map_default_noflush(struct page *page)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct page_change_data data = {
1698c2ecf20Sopenharmony_ci		.set_mask = __pgprot(PTE_VALID | PTE_WRITE),
1708c2ecf20Sopenharmony_ci		.clear_mask = __pgprot(PTE_RDONLY),
1718c2ecf20Sopenharmony_ci	};
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (!rodata_full)
1748c2ecf20Sopenharmony_ci		return 0;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return apply_to_page_range(&init_mm,
1778c2ecf20Sopenharmony_ci				   (unsigned long)page_address(page),
1788c2ecf20Sopenharmony_ci				   PAGE_SIZE, change_page_range, &data);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_civoid __kernel_map_pages(struct page *page, int numpages, int enable)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	if (!debug_pagealloc_enabled() && !rodata_full)
1848c2ecf20Sopenharmony_ci		return;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	set_memory_valid((unsigned long)page_address(page), numpages, enable);
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci/*
1908c2ecf20Sopenharmony_ci * This function is used to determine if a linear map page has been marked as
1918c2ecf20Sopenharmony_ci * not-valid. Walk the page table and check the PTE_VALID bit. This is based
1928c2ecf20Sopenharmony_ci * on kern_addr_valid(), which almost does what we need.
1938c2ecf20Sopenharmony_ci *
1948c2ecf20Sopenharmony_ci * Because this is only called on the kernel linear map,  p?d_sect() implies
1958c2ecf20Sopenharmony_ci * p?d_present(). When debug_pagealloc is enabled, sections mappings are
1968c2ecf20Sopenharmony_ci * disabled.
1978c2ecf20Sopenharmony_ci */
1988c2ecf20Sopenharmony_cibool kernel_page_present(struct page *page)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	pgd_t *pgdp;
2018c2ecf20Sopenharmony_ci	p4d_t *p4dp;
2028c2ecf20Sopenharmony_ci	pud_t *pudp, pud;
2038c2ecf20Sopenharmony_ci	pmd_t *pmdp, pmd;
2048c2ecf20Sopenharmony_ci	pte_t *ptep;
2058c2ecf20Sopenharmony_ci	unsigned long addr = (unsigned long)page_address(page);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (!debug_pagealloc_enabled() && !rodata_full)
2088c2ecf20Sopenharmony_ci		return true;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	pgdp = pgd_offset_k(addr);
2118c2ecf20Sopenharmony_ci	if (pgd_none(READ_ONCE(*pgdp)))
2128c2ecf20Sopenharmony_ci		return false;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	p4dp = p4d_offset(pgdp, addr);
2158c2ecf20Sopenharmony_ci	if (p4d_none(READ_ONCE(*p4dp)))
2168c2ecf20Sopenharmony_ci		return false;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	pudp = pud_offset(p4dp, addr);
2198c2ecf20Sopenharmony_ci	pud = READ_ONCE(*pudp);
2208c2ecf20Sopenharmony_ci	if (pud_none(pud))
2218c2ecf20Sopenharmony_ci		return false;
2228c2ecf20Sopenharmony_ci	if (pud_sect(pud))
2238c2ecf20Sopenharmony_ci		return true;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	pmdp = pmd_offset(pudp, addr);
2268c2ecf20Sopenharmony_ci	pmd = READ_ONCE(*pmdp);
2278c2ecf20Sopenharmony_ci	if (pmd_none(pmd))
2288c2ecf20Sopenharmony_ci		return false;
2298c2ecf20Sopenharmony_ci	if (pmd_sect(pmd))
2308c2ecf20Sopenharmony_ci		return true;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	ptep = pte_offset_kernel(pmdp, addr);
2338c2ecf20Sopenharmony_ci	return pte_valid(READ_ONCE(*ptep));
2348c2ecf20Sopenharmony_ci}
235