1 /* 2 * Copyright (c) 2014, The Linux Foundation. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 and 6 * only version 2 as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 #include <linux/kernel.h> 14 #include <linux/mm.h> 15 #include <linux/module.h> 16 #include <linux/sched.h> 17 18 #include <asm/pgtable.h> 19 #include <asm/tlbflush.h> 20 21 struct page_change_data { 22 pgprot_t set_mask; 23 pgprot_t clear_mask; 24 }; 25 26 static int change_page_range(pte_t *ptep, pgtable_t token, unsigned long addr, 27 void *data) 28 { 29 struct page_change_data *cdata = data; 30 pte_t pte = *ptep; 31 32 pte = clear_pte_bit(pte, cdata->clear_mask); 33 pte = set_pte_bit(pte, cdata->set_mask); 34 35 set_pte(ptep, pte); 36 return 0; 37 } 38 39 static int change_memory_common(unsigned long addr, int numpages, 40 pgprot_t set_mask, pgprot_t clear_mask) 41 { 42 unsigned long start = addr; 43 unsigned long size = PAGE_SIZE*numpages; 44 unsigned long end = start + size; 45 int ret; 46 struct page_change_data data; 47 48 if (!IS_ALIGNED(addr, PAGE_SIZE)) { 49 start &= PAGE_MASK; 50 end = start + size; 51 WARN_ON_ONCE(1); 52 } 53 54 if (!is_module_address(start) || !is_module_address(end - 1)) 55 return -EINVAL; 56 57 data.set_mask = set_mask; 58 data.clear_mask = clear_mask; 59 60 ret = apply_to_page_range(&init_mm, start, size, change_page_range, 61 &data); 62 63 flush_tlb_kernel_range(start, end); 64 return ret; 65 } 66 67 int set_memory_ro(unsigned long addr, int numpages) 68 { 69 return change_memory_common(addr, numpages, 70 __pgprot(PTE_RDONLY), 71 __pgprot(PTE_WRITE)); 72 } 73 EXPORT_SYMBOL_GPL(set_memory_ro); 74 75 int set_memory_rw(unsigned long addr, int numpages) 76 { 77 return change_memory_common(addr, numpages, 78 __pgprot(PTE_WRITE), 79 __pgprot(PTE_RDONLY)); 80 } 81 EXPORT_SYMBOL_GPL(set_memory_rw); 82 83 int set_memory_nx(unsigned long addr, int numpages) 84 { 85 return change_memory_common(addr, numpages, 86 __pgprot(PTE_PXN), 87 __pgprot(0)); 88 } 89 EXPORT_SYMBOL_GPL(set_memory_nx); 90 91 int set_memory_x(unsigned long addr, int numpages) 92 { 93 return change_memory_common(addr, numpages, 94 __pgprot(0), 95 __pgprot(PTE_PXN)); 96 } 97 EXPORT_SYMBOL_GPL(set_memory_x); 98