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 21struct page_change_data { 22 pgprot_t set_mask; 23 pgprot_t clear_mask; 24}; 25 26static 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 39static 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 (start < MODULES_VADDR || start >= MODULES_END) 55 return -EINVAL; 56 57 if (end < MODULES_VADDR || end >= MODULES_END) 58 return -EINVAL; 59 60 if (!numpages) 61 return 0; 62 63 data.set_mask = set_mask; 64 data.clear_mask = clear_mask; 65 66 ret = apply_to_page_range(&init_mm, start, size, change_page_range, 67 &data); 68 69 flush_tlb_kernel_range(start, end); 70 return ret; 71} 72 73int set_memory_ro(unsigned long addr, int numpages) 74{ 75 return change_memory_common(addr, numpages, 76 __pgprot(PTE_RDONLY), 77 __pgprot(PTE_WRITE)); 78} 79 80int set_memory_rw(unsigned long addr, int numpages) 81{ 82 return change_memory_common(addr, numpages, 83 __pgprot(PTE_WRITE), 84 __pgprot(PTE_RDONLY)); 85} 86 87int set_memory_nx(unsigned long addr, int numpages) 88{ 89 return change_memory_common(addr, numpages, 90 __pgprot(PTE_PXN), 91 __pgprot(0)); 92} 93EXPORT_SYMBOL_GPL(set_memory_nx); 94 95int set_memory_x(unsigned long addr, int numpages) 96{ 97 return change_memory_common(addr, numpages, 98 __pgprot(0), 99 __pgprot(PTE_PXN)); 100} 101EXPORT_SYMBOL_GPL(set_memory_x); 102