162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2014, The Linux Foundation. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/kernel.h>
662306a36Sopenharmony_ci#include <linux/mm.h>
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/sched.h>
962306a36Sopenharmony_ci#include <linux/vmalloc.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <asm/cacheflush.h>
1262306a36Sopenharmony_ci#include <asm/set_memory.h>
1362306a36Sopenharmony_ci#include <asm/tlbflush.h>
1462306a36Sopenharmony_ci#include <asm/kfence.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistruct page_change_data {
1762306a36Sopenharmony_ci	pgprot_t set_mask;
1862306a36Sopenharmony_ci	pgprot_t clear_mask;
1962306a36Sopenharmony_ci};
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cibool rodata_full __ro_after_init = IS_ENABLED(CONFIG_RODATA_FULL_DEFAULT_ENABLED);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cibool can_set_direct_map(void)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	/*
2662306a36Sopenharmony_ci	 * rodata_full and DEBUG_PAGEALLOC require linear map to be
2762306a36Sopenharmony_ci	 * mapped at page granularity, so that it is possible to
2862306a36Sopenharmony_ci	 * protect/unprotect single pages.
2962306a36Sopenharmony_ci	 *
3062306a36Sopenharmony_ci	 * KFENCE pool requires page-granular mapping if initialized late.
3162306a36Sopenharmony_ci	 */
3262306a36Sopenharmony_ci	return rodata_full || debug_pagealloc_enabled() ||
3362306a36Sopenharmony_ci	       arm64_kfence_can_set_direct_map();
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic int change_page_range(pte_t *ptep, unsigned long addr, void *data)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	struct page_change_data *cdata = data;
3962306a36Sopenharmony_ci	pte_t pte = READ_ONCE(*ptep);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	pte = clear_pte_bit(pte, cdata->clear_mask);
4262306a36Sopenharmony_ci	pte = set_pte_bit(pte, cdata->set_mask);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	set_pte(ptep, pte);
4562306a36Sopenharmony_ci	return 0;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * This function assumes that the range is mapped with PAGE_SIZE pages.
5062306a36Sopenharmony_ci */
5162306a36Sopenharmony_cistatic int __change_memory_common(unsigned long start, unsigned long size,
5262306a36Sopenharmony_ci				pgprot_t set_mask, pgprot_t clear_mask)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct page_change_data data;
5562306a36Sopenharmony_ci	int ret;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	data.set_mask = set_mask;
5862306a36Sopenharmony_ci	data.clear_mask = clear_mask;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	ret = apply_to_page_range(&init_mm, start, size, change_page_range,
6162306a36Sopenharmony_ci					&data);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	flush_tlb_kernel_range(start, start + size);
6462306a36Sopenharmony_ci	return ret;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int change_memory_common(unsigned long addr, int numpages,
6862306a36Sopenharmony_ci				pgprot_t set_mask, pgprot_t clear_mask)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	unsigned long start = addr;
7162306a36Sopenharmony_ci	unsigned long size = PAGE_SIZE * numpages;
7262306a36Sopenharmony_ci	unsigned long end = start + size;
7362306a36Sopenharmony_ci	struct vm_struct *area;
7462306a36Sopenharmony_ci	int i;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (!PAGE_ALIGNED(addr)) {
7762306a36Sopenharmony_ci		start &= PAGE_MASK;
7862306a36Sopenharmony_ci		end = start + size;
7962306a36Sopenharmony_ci		WARN_ON_ONCE(1);
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/*
8362306a36Sopenharmony_ci	 * Kernel VA mappings are always live, and splitting live section
8462306a36Sopenharmony_ci	 * mappings into page mappings may cause TLB conflicts. This means
8562306a36Sopenharmony_ci	 * we have to ensure that changing the permission bits of the range
8662306a36Sopenharmony_ci	 * we are operating on does not result in such splitting.
8762306a36Sopenharmony_ci	 *
8862306a36Sopenharmony_ci	 * Let's restrict ourselves to mappings created by vmalloc (or vmap).
8962306a36Sopenharmony_ci	 * Those are guaranteed to consist entirely of page mappings, and
9062306a36Sopenharmony_ci	 * splitting is never needed.
9162306a36Sopenharmony_ci	 *
9262306a36Sopenharmony_ci	 * So check whether the [addr, addr + size) interval is entirely
9362306a36Sopenharmony_ci	 * covered by precisely one VM area that has the VM_ALLOC flag set.
9462306a36Sopenharmony_ci	 */
9562306a36Sopenharmony_ci	area = find_vm_area((void *)addr);
9662306a36Sopenharmony_ci	if (!area ||
9762306a36Sopenharmony_ci	    end > (unsigned long)kasan_reset_tag(area->addr) + area->size ||
9862306a36Sopenharmony_ci	    !(area->flags & VM_ALLOC))
9962306a36Sopenharmony_ci		return -EINVAL;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (!numpages)
10262306a36Sopenharmony_ci		return 0;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/*
10562306a36Sopenharmony_ci	 * If we are manipulating read-only permissions, apply the same
10662306a36Sopenharmony_ci	 * change to the linear mapping of the pages that back this VM area.
10762306a36Sopenharmony_ci	 */
10862306a36Sopenharmony_ci	if (rodata_full && (pgprot_val(set_mask) == PTE_RDONLY ||
10962306a36Sopenharmony_ci			    pgprot_val(clear_mask) == PTE_RDONLY)) {
11062306a36Sopenharmony_ci		for (i = 0; i < area->nr_pages; i++) {
11162306a36Sopenharmony_ci			__change_memory_common((u64)page_address(area->pages[i]),
11262306a36Sopenharmony_ci					       PAGE_SIZE, set_mask, clear_mask);
11362306a36Sopenharmony_ci		}
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	/*
11762306a36Sopenharmony_ci	 * Get rid of potentially aliasing lazily unmapped vm areas that may
11862306a36Sopenharmony_ci	 * have permissions set that deviate from the ones we are setting here.
11962306a36Sopenharmony_ci	 */
12062306a36Sopenharmony_ci	vm_unmap_aliases();
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return __change_memory_common(start, size, set_mask, clear_mask);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ciint set_memory_ro(unsigned long addr, int numpages)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	return change_memory_common(addr, numpages,
12862306a36Sopenharmony_ci					__pgprot(PTE_RDONLY),
12962306a36Sopenharmony_ci					__pgprot(PTE_WRITE));
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ciint set_memory_rw(unsigned long addr, int numpages)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	return change_memory_common(addr, numpages,
13562306a36Sopenharmony_ci					__pgprot(PTE_WRITE),
13662306a36Sopenharmony_ci					__pgprot(PTE_RDONLY));
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ciint set_memory_nx(unsigned long addr, int numpages)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	return change_memory_common(addr, numpages,
14262306a36Sopenharmony_ci					__pgprot(PTE_PXN),
14362306a36Sopenharmony_ci					__pgprot(PTE_MAYBE_GP));
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ciint set_memory_x(unsigned long addr, int numpages)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	return change_memory_common(addr, numpages,
14962306a36Sopenharmony_ci					__pgprot(PTE_MAYBE_GP),
15062306a36Sopenharmony_ci					__pgprot(PTE_PXN));
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ciint set_memory_valid(unsigned long addr, int numpages, int enable)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	if (enable)
15662306a36Sopenharmony_ci		return __change_memory_common(addr, PAGE_SIZE * numpages,
15762306a36Sopenharmony_ci					__pgprot(PTE_VALID),
15862306a36Sopenharmony_ci					__pgprot(0));
15962306a36Sopenharmony_ci	else
16062306a36Sopenharmony_ci		return __change_memory_common(addr, PAGE_SIZE * numpages,
16162306a36Sopenharmony_ci					__pgprot(0),
16262306a36Sopenharmony_ci					__pgprot(PTE_VALID));
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ciint set_direct_map_invalid_noflush(struct page *page)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct page_change_data data = {
16862306a36Sopenharmony_ci		.set_mask = __pgprot(0),
16962306a36Sopenharmony_ci		.clear_mask = __pgprot(PTE_VALID),
17062306a36Sopenharmony_ci	};
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (!can_set_direct_map())
17362306a36Sopenharmony_ci		return 0;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return apply_to_page_range(&init_mm,
17662306a36Sopenharmony_ci				   (unsigned long)page_address(page),
17762306a36Sopenharmony_ci				   PAGE_SIZE, change_page_range, &data);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ciint set_direct_map_default_noflush(struct page *page)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct page_change_data data = {
18362306a36Sopenharmony_ci		.set_mask = __pgprot(PTE_VALID | PTE_WRITE),
18462306a36Sopenharmony_ci		.clear_mask = __pgprot(PTE_RDONLY),
18562306a36Sopenharmony_ci	};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (!can_set_direct_map())
18862306a36Sopenharmony_ci		return 0;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	return apply_to_page_range(&init_mm,
19162306a36Sopenharmony_ci				   (unsigned long)page_address(page),
19262306a36Sopenharmony_ci				   PAGE_SIZE, change_page_range, &data);
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_PAGEALLOC
19662306a36Sopenharmony_civoid __kernel_map_pages(struct page *page, int numpages, int enable)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	if (!can_set_direct_map())
19962306a36Sopenharmony_ci		return;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	set_memory_valid((unsigned long)page_address(page), numpages, enable);
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci#endif /* CONFIG_DEBUG_PAGEALLOC */
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci/*
20662306a36Sopenharmony_ci * This function is used to determine if a linear map page has been marked as
20762306a36Sopenharmony_ci * not-valid. Walk the page table and check the PTE_VALID bit.
20862306a36Sopenharmony_ci *
20962306a36Sopenharmony_ci * Because this is only called on the kernel linear map,  p?d_sect() implies
21062306a36Sopenharmony_ci * p?d_present(). When debug_pagealloc is enabled, sections mappings are
21162306a36Sopenharmony_ci * disabled.
21262306a36Sopenharmony_ci */
21362306a36Sopenharmony_cibool kernel_page_present(struct page *page)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	pgd_t *pgdp;
21662306a36Sopenharmony_ci	p4d_t *p4dp;
21762306a36Sopenharmony_ci	pud_t *pudp, pud;
21862306a36Sopenharmony_ci	pmd_t *pmdp, pmd;
21962306a36Sopenharmony_ci	pte_t *ptep;
22062306a36Sopenharmony_ci	unsigned long addr = (unsigned long)page_address(page);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if (!can_set_direct_map())
22362306a36Sopenharmony_ci		return true;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	pgdp = pgd_offset_k(addr);
22662306a36Sopenharmony_ci	if (pgd_none(READ_ONCE(*pgdp)))
22762306a36Sopenharmony_ci		return false;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	p4dp = p4d_offset(pgdp, addr);
23062306a36Sopenharmony_ci	if (p4d_none(READ_ONCE(*p4dp)))
23162306a36Sopenharmony_ci		return false;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	pudp = pud_offset(p4dp, addr);
23462306a36Sopenharmony_ci	pud = READ_ONCE(*pudp);
23562306a36Sopenharmony_ci	if (pud_none(pud))
23662306a36Sopenharmony_ci		return false;
23762306a36Sopenharmony_ci	if (pud_sect(pud))
23862306a36Sopenharmony_ci		return true;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	pmdp = pmd_offset(pudp, addr);
24162306a36Sopenharmony_ci	pmd = READ_ONCE(*pmdp);
24262306a36Sopenharmony_ci	if (pmd_none(pmd))
24362306a36Sopenharmony_ci		return false;
24462306a36Sopenharmony_ci	if (pmd_sect(pmd))
24562306a36Sopenharmony_ci		return true;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	ptep = pte_offset_kernel(pmdp, addr);
24862306a36Sopenharmony_ci	return pte_valid(READ_ONCE(*ptep));
24962306a36Sopenharmony_ci}
250