xref: /linux/arch/loongarch/mm/pageattr.c (revision c7546e2c3cb739a3c1a2f5acaf9bb629d401afe5)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2024 Loongson Technology Corporation Limited
4  */
5 
6 #include <linux/pagewalk.h>
7 #include <linux/pgtable.h>
8 #include <asm/set_memory.h>
9 #include <asm/tlbflush.h>
10 
11 struct pageattr_masks {
12 	pgprot_t set_mask;
13 	pgprot_t clear_mask;
14 };
15 
16 static unsigned long set_pageattr_masks(unsigned long val, struct mm_walk *walk)
17 {
18 	unsigned long new_val = val;
19 	struct pageattr_masks *masks = walk->private;
20 
21 	new_val &= ~(pgprot_val(masks->clear_mask));
22 	new_val |= (pgprot_val(masks->set_mask));
23 
24 	return new_val;
25 }
26 
27 static int pageattr_pgd_entry(pgd_t *pgd, unsigned long addr,
28 			      unsigned long next, struct mm_walk *walk)
29 {
30 	pgd_t val = pgdp_get(pgd);
31 
32 	if (pgd_leaf(val)) {
33 		val = __pgd(set_pageattr_masks(pgd_val(val), walk));
34 		set_pgd(pgd, val);
35 	}
36 
37 	return 0;
38 }
39 
40 static int pageattr_p4d_entry(p4d_t *p4d, unsigned long addr,
41 			      unsigned long next, struct mm_walk *walk)
42 {
43 	p4d_t val = p4dp_get(p4d);
44 
45 	if (p4d_leaf(val)) {
46 		val = __p4d(set_pageattr_masks(p4d_val(val), walk));
47 		set_p4d(p4d, val);
48 	}
49 
50 	return 0;
51 }
52 
53 static int pageattr_pud_entry(pud_t *pud, unsigned long addr,
54 			      unsigned long next, struct mm_walk *walk)
55 {
56 	pud_t val = pudp_get(pud);
57 
58 	if (pud_leaf(val)) {
59 		val = __pud(set_pageattr_masks(pud_val(val), walk));
60 		set_pud(pud, val);
61 	}
62 
63 	return 0;
64 }
65 
66 static int pageattr_pmd_entry(pmd_t *pmd, unsigned long addr,
67 			      unsigned long next, struct mm_walk *walk)
68 {
69 	pmd_t val = pmdp_get(pmd);
70 
71 	if (pmd_leaf(val)) {
72 		val = __pmd(set_pageattr_masks(pmd_val(val), walk));
73 		set_pmd(pmd, val);
74 	}
75 
76 	return 0;
77 }
78 
79 static int pageattr_pte_entry(pte_t *pte, unsigned long addr,
80 			      unsigned long next, struct mm_walk *walk)
81 {
82 	pte_t val = ptep_get(pte);
83 
84 	val = __pte(set_pageattr_masks(pte_val(val), walk));
85 	set_pte(pte, val);
86 
87 	return 0;
88 }
89 
90 static int pageattr_pte_hole(unsigned long addr, unsigned long next,
91 			     int depth, struct mm_walk *walk)
92 {
93 	return 0;
94 }
95 
96 static const struct mm_walk_ops pageattr_ops = {
97 	.pgd_entry = pageattr_pgd_entry,
98 	.p4d_entry = pageattr_p4d_entry,
99 	.pud_entry = pageattr_pud_entry,
100 	.pmd_entry = pageattr_pmd_entry,
101 	.pte_entry = pageattr_pte_entry,
102 	.pte_hole = pageattr_pte_hole,
103 	.walk_lock = PGWALK_RDLOCK,
104 };
105 
106 static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, pgprot_t clear_mask)
107 {
108 	int ret;
109 	unsigned long start = addr;
110 	unsigned long end = start + PAGE_SIZE * numpages;
111 	struct pageattr_masks masks = {
112 		.set_mask = set_mask,
113 		.clear_mask = clear_mask
114 	};
115 
116 	if (!numpages)
117 		return 0;
118 
119 	mmap_write_lock(&init_mm);
120 	ret = walk_page_range_novma(&init_mm, start, end, &pageattr_ops, NULL, &masks);
121 	mmap_write_unlock(&init_mm);
122 
123 	flush_tlb_kernel_range(start, end);
124 
125 	return ret;
126 }
127 
128 int set_memory_x(unsigned long addr, int numpages)
129 {
130 	if (addr < vm_map_base)
131 		return 0;
132 
133 	return __set_memory(addr, numpages, __pgprot(0), __pgprot(_PAGE_NO_EXEC));
134 }
135 
136 int set_memory_nx(unsigned long addr, int numpages)
137 {
138 	if (addr < vm_map_base)
139 		return 0;
140 
141 	return __set_memory(addr, numpages, __pgprot(_PAGE_NO_EXEC), __pgprot(0));
142 }
143 
144 int set_memory_ro(unsigned long addr, int numpages)
145 {
146 	if (addr < vm_map_base)
147 		return 0;
148 
149 	return __set_memory(addr, numpages, __pgprot(0), __pgprot(_PAGE_WRITE | _PAGE_DIRTY));
150 }
151 
152 int set_memory_rw(unsigned long addr, int numpages)
153 {
154 	if (addr < vm_map_base)
155 		return 0;
156 
157 	return __set_memory(addr, numpages, __pgprot(_PAGE_WRITE | _PAGE_DIRTY), __pgprot(0));
158 }
159 
160 bool kernel_page_present(struct page *page)
161 {
162 	pgd_t *pgd;
163 	p4d_t *p4d;
164 	pud_t *pud;
165 	pmd_t *pmd;
166 	pte_t *pte;
167 	unsigned long addr = (unsigned long)page_address(page);
168 
169 	if (addr < vm_map_base)
170 		return true;
171 
172 	pgd = pgd_offset_k(addr);
173 	if (pgd_none(pgdp_get(pgd)))
174 		return false;
175 	if (pgd_leaf(pgdp_get(pgd)))
176 		return true;
177 
178 	p4d = p4d_offset(pgd, addr);
179 	if (p4d_none(p4dp_get(p4d)))
180 		return false;
181 	if (p4d_leaf(p4dp_get(p4d)))
182 		return true;
183 
184 	pud = pud_offset(p4d, addr);
185 	if (pud_none(pudp_get(pud)))
186 		return false;
187 	if (pud_leaf(pudp_get(pud)))
188 		return true;
189 
190 	pmd = pmd_offset(pud, addr);
191 	if (pmd_none(pmdp_get(pmd)))
192 		return false;
193 	if (pmd_leaf(pmdp_get(pmd)))
194 		return true;
195 
196 	pte = pte_offset_kernel(pmd, addr);
197 	return pte_present(ptep_get(pte));
198 }
199 
200 int set_direct_map_default_noflush(struct page *page)
201 {
202 	unsigned long addr = (unsigned long)page_address(page);
203 
204 	if (addr < vm_map_base)
205 		return 0;
206 
207 	return __set_memory(addr, 1, PAGE_KERNEL, __pgprot(0));
208 }
209 
210 int set_direct_map_invalid_noflush(struct page *page)
211 {
212 	unsigned long addr = (unsigned long)page_address(page);
213 
214 	if (addr < vm_map_base)
215 		return 0;
216 
217 	return __set_memory(addr, 1, __pgprot(0), __pgprot(_PAGE_PRESENT | _PAGE_VALID));
218 }
219