]> Git Repo - J-linux.git/blob - arch/loongarch/mm/pageattr.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / arch / loongarch / mm / pageattr.c
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
220 int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid)
221 {
222         unsigned long addr = (unsigned long)page_address(page);
223         pgprot_t set, clear;
224
225         if (addr < vm_map_base)
226                 return 0;
227
228         if (valid) {
229                 set = PAGE_KERNEL;
230                 clear = __pgprot(0);
231         } else {
232                 set = __pgprot(0);
233                 clear = __pgprot(_PAGE_PRESENT | _PAGE_VALID);
234         }
235
236         return __set_memory(addr, 1, set, clear);
237 }
This page took 0.038156 seconds and 4 git commands to generate.