1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2014, The Linux Foundation. All rights reserved. 4 */ 5 #include <linux/mm.h> 6 #include <linux/module.h> 7 8 #include <asm/pgtable.h> 9 #include <asm/tlbflush.h> 10 #include <asm/set_memory.h> 11 12 struct page_change_data { 13 pgprot_t set_mask; 14 pgprot_t clear_mask; 15 }; 16 17 static int change_page_range(pte_t *ptep, pgtable_t token, unsigned long addr, 18 void *data) 19 { 20 struct page_change_data *cdata = data; 21 pte_t pte = *ptep; 22 23 pte = clear_pte_bit(pte, cdata->clear_mask); 24 pte = set_pte_bit(pte, cdata->set_mask); 25 26 set_pte_ext(ptep, pte, 0); 27 return 0; 28 } 29 30 static bool in_range(unsigned long start, unsigned long size, 31 unsigned long range_start, unsigned long range_end) 32 { 33 return start >= range_start && start < range_end && 34 size <= range_end - start; 35 } 36 37 static int change_memory_common(unsigned long addr, int numpages, 38 pgprot_t set_mask, pgprot_t clear_mask) 39 { 40 unsigned long start = addr & PAGE_MASK; 41 unsigned long end = PAGE_ALIGN(addr) + numpages * PAGE_SIZE; 42 unsigned long size = end - start; 43 int ret; 44 struct page_change_data data; 45 46 WARN_ON_ONCE(start != addr); 47 48 if (!size) 49 return 0; 50 51 if (!in_range(start, size, MODULES_VADDR, MODULES_END) && 52 !in_range(start, size, VMALLOC_START, VMALLOC_END)) 53 return -EINVAL; 54 55 data.set_mask = set_mask; 56 data.clear_mask = clear_mask; 57 58 ret = apply_to_page_range(&init_mm, start, size, change_page_range, 59 &data); 60 61 flush_tlb_kernel_range(start, end); 62 return ret; 63 } 64 65 int set_memory_ro(unsigned long addr, int numpages) 66 { 67 return change_memory_common(addr, numpages, 68 __pgprot(L_PTE_RDONLY), 69 __pgprot(0)); 70 } 71 72 int set_memory_rw(unsigned long addr, int numpages) 73 { 74 return change_memory_common(addr, numpages, 75 __pgprot(0), 76 __pgprot(L_PTE_RDONLY)); 77 } 78 79 int set_memory_nx(unsigned long addr, int numpages) 80 { 81 return change_memory_common(addr, numpages, 82 __pgprot(L_PTE_XN), 83 __pgprot(0)); 84 } 85 86 int set_memory_x(unsigned long addr, int numpages) 87 { 88 return change_memory_common(addr, numpages, 89 __pgprot(0), 90 __pgprot(L_PTE_XN)); 91 } 92