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