1
2
3
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
12struct page_change_data {
13 pgprot_t set_mask;
14 pgprot_t clear_mask;
15};
16
17static 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
30static 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
37static 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
65int 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
72int 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
79int 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
86int 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