1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include <linux/kernel.h>
25#include <linux/gfp.h>
26#include <linux/mm.h>
27#include <linux/percpu.h>
28#include <linux/hardirq.h>
29#include <linux/hugetlb.h>
30#include <asm/pgalloc.h>
31#include <asm/tlbflush.h>
32#include <asm/tlb.h>
33
34static inline int is_exec_fault(void)
35{
36 return current->thread.regs && TRAP(current->thread.regs) == 0x400;
37}
38
39
40
41
42
43
44static inline int pte_looks_normal(pte_t pte)
45{
46 return (pte_val(pte) &
47 (_PAGE_PRESENT | _PAGE_SPECIAL | _PAGE_NO_CACHE | _PAGE_USER)) ==
48 (_PAGE_PRESENT | _PAGE_USER);
49}
50
51struct page * maybe_pte_to_page(pte_t pte)
52{
53 unsigned long pfn = pte_pfn(pte);
54 struct page *page;
55
56 if (unlikely(!pfn_valid(pfn)))
57 return NULL;
58 page = pfn_to_page(pfn);
59 if (PageReserved(page))
60 return NULL;
61 return page;
62}
63
64#if defined(CONFIG_PPC_STD_MMU) || _PAGE_EXEC == 0
65
66
67
68
69
70
71
72static pte_t set_pte_filter(pte_t pte)
73{
74 pte = __pte(pte_val(pte) & ~_PAGE_HPTEFLAGS);
75 if (pte_looks_normal(pte) && !(cpu_has_feature(CPU_FTR_COHERENT_ICACHE) ||
76 cpu_has_feature(CPU_FTR_NOEXECUTE))) {
77 struct page *pg = maybe_pte_to_page(pte);
78 if (!pg)
79 return pte;
80 if (!test_bit(PG_arch_1, &pg->flags)) {
81 flush_dcache_icache_page(pg);
82 set_bit(PG_arch_1, &pg->flags);
83 }
84 }
85 return pte;
86}
87
88static pte_t set_access_flags_filter(pte_t pte, struct vm_area_struct *vma,
89 int dirty)
90{
91 return pte;
92}
93
94#else
95
96
97
98
99
100static pte_t set_pte_filter(pte_t pte)
101{
102 struct page *pg;
103
104
105 if (!(pte_val(pte) & _PAGE_EXEC) || !pte_looks_normal(pte))
106 return pte;
107
108
109 pg = maybe_pte_to_page(pte);
110 if (unlikely(!pg))
111 return pte;
112
113
114 if (test_bit(PG_arch_1, &pg->flags))
115 return pte;
116
117
118 if (is_exec_fault()) {
119 flush_dcache_icache_page(pg);
120 set_bit(PG_arch_1, &pg->flags);
121 return pte;
122 }
123
124
125 return __pte(pte_val(pte) & ~_PAGE_EXEC);
126}
127
128static pte_t set_access_flags_filter(pte_t pte, struct vm_area_struct *vma,
129 int dirty)
130{
131 struct page *pg;
132
133
134
135
136
137
138 if (dirty || (pte_val(pte) & _PAGE_EXEC) || !is_exec_fault())
139 return pte;
140
141#ifdef CONFIG_DEBUG_VM
142
143
144
145
146 if (WARN_ON(!(vma->vm_flags & VM_EXEC)))
147 return pte;
148#endif
149
150
151 pg = maybe_pte_to_page(pte);
152 if (unlikely(!pg))
153 goto bail;
154
155
156 if (test_bit(PG_arch_1, &pg->flags))
157 goto bail;
158
159
160 flush_dcache_icache_page(pg);
161 set_bit(PG_arch_1, &pg->flags);
162
163 bail:
164 return __pte(pte_val(pte) | _PAGE_EXEC);
165}
166
167#endif
168
169
170
171
172void set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep,
173 pte_t pte)
174{
175#ifdef CONFIG_DEBUG_VM
176 WARN_ON(pte_val(*ptep) & _PAGE_PRESENT);
177#endif
178
179
180
181
182 pte = set_pte_filter(pte);
183
184
185 __set_pte_at(mm, addr, ptep, pte, 0);
186}
187
188
189
190
191
192
193
194
195int ptep_set_access_flags(struct vm_area_struct *vma, unsigned long address,
196 pte_t *ptep, pte_t entry, int dirty)
197{
198 int changed;
199 entry = set_access_flags_filter(entry, vma, dirty);
200 changed = !pte_same(*(ptep), entry);
201 if (changed) {
202 if (!is_vm_hugetlb_page(vma))
203 assert_pte_locked(vma->vm_mm, address);
204 __ptep_set_access_flags(ptep, entry);
205 flush_tlb_page_nohash(vma, address);
206 }
207 return changed;
208}
209
210#ifdef CONFIG_DEBUG_VM
211void assert_pte_locked(struct mm_struct *mm, unsigned long addr)
212{
213 pgd_t *pgd;
214 pud_t *pud;
215 pmd_t *pmd;
216
217 if (mm == &init_mm)
218 return;
219 pgd = mm->pgd + pgd_index(addr);
220 BUG_ON(pgd_none(*pgd));
221 pud = pud_offset(pgd, addr);
222 BUG_ON(pud_none(*pud));
223 pmd = pmd_offset(pud, addr);
224
225
226
227
228
229
230 if (pmd_none(*pmd))
231 return;
232 BUG_ON(!pmd_present(*pmd));
233 assert_spin_locked(pte_lockptr(mm, pmd));
234}
235#endif
236
237