Commit | Line | Data |
---|---|---|
97fb5e8d | 1 | // SPDX-License-Identifier: GPL-2.0-only |
4e802cfd JL |
2 | /* |
3 | * Copyright (c) 2014, The Linux Foundation. All rights reserved. | |
4e802cfd JL |
4 | */ |
5 | #include <linux/mm.h> | |
6 | #include <linux/module.h> | |
7 | ||
4e802cfd | 8 | #include <asm/tlbflush.h> |
74d86a70 | 9 | #include <asm/set_memory.h> |
4e802cfd JL |
10 | |
11 | struct page_change_data { | |
12 | pgprot_t set_mask; | |
13 | pgprot_t clear_mask; | |
14 | }; | |
15 | ||
8b1e0f81 | 16 | static int change_page_range(pte_t *ptep, unsigned long addr, void *data) |
4e802cfd JL |
17 | { |
18 | struct page_change_data *cdata = data; | |
19 | pte_t pte = *ptep; | |
20 | ||
21 | pte = clear_pte_bit(pte, cdata->clear_mask); | |
22 | pte = set_pte_bit(pte, cdata->set_mask); | |
23 | ||
24 | set_pte_ext(ptep, pte, 0); | |
25 | return 0; | |
26 | } | |
27 | ||
f9bff0e3 | 28 | static bool range_in_range(unsigned long start, unsigned long size, |
580218f9 RK |
29 | unsigned long range_start, unsigned long range_end) |
30 | { | |
31 | return start >= range_start && start < range_end && | |
32 | size <= range_end - start; | |
33 | } | |
34 | ||
0ba8695e WK |
35 | /* |
36 | * This function assumes that the range is mapped with PAGE_SIZE pages. | |
37 | */ | |
38 | static int __change_memory_common(unsigned long start, unsigned long size, | |
39 | pgprot_t set_mask, pgprot_t clear_mask) | |
40 | { | |
41 | struct page_change_data data; | |
42 | int ret; | |
43 | ||
44 | data.set_mask = set_mask; | |
45 | data.clear_mask = clear_mask; | |
46 | ||
47 | ret = apply_to_page_range(&init_mm, start, size, change_page_range, | |
48 | &data); | |
49 | ||
50 | flush_tlb_kernel_range(start, start + size); | |
51 | return ret; | |
52 | } | |
53 | ||
4e802cfd JL |
54 | static int change_memory_common(unsigned long addr, int numpages, |
55 | pgprot_t set_mask, pgprot_t clear_mask) | |
56 | { | |
580218f9 RK |
57 | unsigned long start = addr & PAGE_MASK; |
58 | unsigned long end = PAGE_ALIGN(addr) + numpages * PAGE_SIZE; | |
59 | unsigned long size = end - start; | |
4e802cfd | 60 | |
580218f9 | 61 | WARN_ON_ONCE(start != addr); |
4e802cfd | 62 | |
580218f9 | 63 | if (!size) |
f474c8c8 MP |
64 | return 0; |
65 | ||
f9bff0e3 MWO |
66 | if (!range_in_range(start, size, MODULES_VADDR, MODULES_END) && |
67 | !range_in_range(start, size, VMALLOC_START, VMALLOC_END)) | |
4e802cfd JL |
68 | return -EINVAL; |
69 | ||
0ba8695e | 70 | return __change_memory_common(start, size, set_mask, clear_mask); |
4e802cfd JL |
71 | } |
72 | ||
73 | int set_memory_ro(unsigned long addr, int numpages) | |
74 | { | |
75 | return change_memory_common(addr, numpages, | |
76 | __pgprot(L_PTE_RDONLY), | |
77 | __pgprot(0)); | |
78 | } | |
79 | ||
80 | int set_memory_rw(unsigned long addr, int numpages) | |
81 | { | |
82 | return change_memory_common(addr, numpages, | |
83 | __pgprot(0), | |
84 | __pgprot(L_PTE_RDONLY)); | |
85 | } | |
86 | ||
87 | int set_memory_nx(unsigned long addr, int numpages) | |
88 | { | |
89 | return change_memory_common(addr, numpages, | |
90 | __pgprot(L_PTE_XN), | |
91 | __pgprot(0)); | |
92 | } | |
93 | ||
94 | int set_memory_x(unsigned long addr, int numpages) | |
95 | { | |
96 | return change_memory_common(addr, numpages, | |
97 | __pgprot(0), | |
98 | __pgprot(L_PTE_XN)); | |
99 | } | |
0ba8695e WK |
100 | |
101 | int set_memory_valid(unsigned long addr, int numpages, int enable) | |
102 | { | |
103 | if (enable) | |
104 | return __change_memory_common(addr, PAGE_SIZE * numpages, | |
105 | __pgprot(L_PTE_VALID), | |
106 | __pgprot(0)); | |
107 | else | |
108 | return __change_memory_common(addr, PAGE_SIZE * numpages, | |
109 | __pgprot(0), | |
110 | __pgprot(L_PTE_VALID)); | |
111 | } |