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