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/mm.h> 14 #include <linux/module.h> 15 16 #include <asm/pgtable.h> 17 #include <asm/tlbflush.h> 18 19 struct page_change_data { 20 pgprot_t set_mask; 21 pgprot_t clear_mask; 22 }; 23 24 static int change_page_range(pte_t *ptep, pgtable_t token, unsigned long addr, 25 void *data) 26 { 27 struct page_change_data *cdata = data; 28 pte_t pte = *ptep; 29 30 pte = clear_pte_bit(pte, cdata->clear_mask); 31 pte = set_pte_bit(pte, cdata->set_mask); 32 33 set_pte_ext(ptep, pte, 0); 34 return 0; 35 } 36 37 static bool in_range(unsigned long start, unsigned long size, 38 unsigned long range_start, unsigned long range_end) 39 { 40 return start >= range_start && start < range_end && 41 size <= range_end - start; 42 } 43 44 static int change_memory_common(unsigned long addr, int numpages, 45 pgprot_t set_mask, pgprot_t clear_mask) 46 { 47 unsigned long start = addr & PAGE_MASK; 48 unsigned long end = PAGE_ALIGN(addr) + numpages * PAGE_SIZE; 49 unsigned long size = end - start; 50 int ret; 51 struct page_change_data data; 52 53 WARN_ON_ONCE(start != addr); 54 55 if (!size) 56 return 0; 57 58 if (!in_range(start, size, MODULES_VADDR, MODULES_END) && 59 !in_range(start, size, VMALLOC_START, VMALLOC_END)) 60 return -EINVAL; 61 62 data.set_mask = set_mask; 63 data.clear_mask = clear_mask; 64 65 ret = apply_to_page_range(&init_mm, start, size, change_page_range, 66 &data); 67 68 flush_tlb_kernel_range(start, end); 69 return ret; 70 } 71 72 int set_memory_ro(unsigned long addr, int numpages) 73 { 74 return change_memory_common(addr, numpages, 75 __pgprot(L_PTE_RDONLY), 76 __pgprot(0)); 77 } 78 79 int set_memory_rw(unsigned long addr, int numpages) 80 { 81 return change_memory_common(addr, numpages, 82 __pgprot(0), 83 __pgprot(L_PTE_RDONLY)); 84 } 85 86 int set_memory_nx(unsigned long addr, int numpages) 87 { 88 return change_memory_common(addr, numpages, 89 __pgprot(L_PTE_XN), 90 __pgprot(0)); 91 } 92 93 int set_memory_x(unsigned long addr, int numpages) 94 { 95 return change_memory_common(addr, numpages, 96 __pgprot(0), 97 __pgprot(L_PTE_XN)); 98 } 99