1
2
3
4
5
6
7
8#include <linux/sched.h>
9#include <linux/mm_types.h>
10#include <linux/mm.h>
11
12#include <asm/sections.h>
13#include <asm/mmu.h>
14#include <asm/tlb.h>
15
16#include <mm/mmu_decl.h>
17
18#define CREATE_TRACE_POINTS
19#include <trace/events/thp.h>
20
21#if H_PGTABLE_RANGE > (USER_VSID_RANGE * (TASK_SIZE_USER64 / TASK_CONTEXT_SIZE))
22#warning Limited user VSID range means pagetable space is wasted
23#endif
24
25#ifdef CONFIG_SPARSEMEM_VMEMMAP
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105int __meminit hash__vmemmap_create_mapping(unsigned long start,
106 unsigned long page_size,
107 unsigned long phys)
108{
109 int rc;
110
111 if ((start + page_size) >= H_VMEMMAP_END) {
112 pr_warn("Outside the supported range\n");
113 return -1;
114 }
115
116 rc = htab_bolt_mapping(start, start + page_size, phys,
117 pgprot_val(PAGE_KERNEL),
118 mmu_vmemmap_psize, mmu_kernel_ssize);
119 if (rc < 0) {
120 int rc2 = htab_remove_mapping(start, start + page_size,
121 mmu_vmemmap_psize,
122 mmu_kernel_ssize);
123 BUG_ON(rc2 && (rc2 != -ENOENT));
124 }
125 return rc;
126}
127
128#ifdef CONFIG_MEMORY_HOTPLUG
129void hash__vmemmap_remove_mapping(unsigned long start,
130 unsigned long page_size)
131{
132 int rc = htab_remove_mapping(start, start + page_size,
133 mmu_vmemmap_psize,
134 mmu_kernel_ssize);
135 BUG_ON((rc < 0) && (rc != -ENOENT));
136 WARN_ON(rc == -ENOENT);
137}
138#endif
139#endif
140
141
142
143
144
145
146int hash__map_kernel_page(unsigned long ea, unsigned long pa, pgprot_t prot)
147{
148 pgd_t *pgdp;
149 p4d_t *p4dp;
150 pud_t *pudp;
151 pmd_t *pmdp;
152 pte_t *ptep;
153
154 BUILD_BUG_ON(TASK_SIZE_USER64 > H_PGTABLE_RANGE);
155 if (slab_is_available()) {
156 pgdp = pgd_offset_k(ea);
157 p4dp = p4d_offset(pgdp, ea);
158 pudp = pud_alloc(&init_mm, p4dp, ea);
159 if (!pudp)
160 return -ENOMEM;
161 pmdp = pmd_alloc(&init_mm, pudp, ea);
162 if (!pmdp)
163 return -ENOMEM;
164 ptep = pte_alloc_kernel(pmdp, ea);
165 if (!ptep)
166 return -ENOMEM;
167 set_pte_at(&init_mm, ea, ptep, pfn_pte(pa >> PAGE_SHIFT, prot));
168 } else {
169
170
171
172
173
174
175 if (htab_bolt_mapping(ea, ea + PAGE_SIZE, pa, pgprot_val(prot),
176 mmu_io_psize, mmu_kernel_ssize)) {
177 printk(KERN_ERR "Failed to do bolted mapping IO "
178 "memory at %016lx !\n", pa);
179 return -ENOMEM;
180 }
181 }
182
183 smp_wmb();
184 return 0;
185}
186
187#ifdef CONFIG_TRANSPARENT_HUGEPAGE
188
189unsigned long hash__pmd_hugepage_update(struct mm_struct *mm, unsigned long addr,
190 pmd_t *pmdp, unsigned long clr,
191 unsigned long set)
192{
193 __be64 old_be, tmp;
194 unsigned long old;
195
196#ifdef CONFIG_DEBUG_VM
197 WARN_ON(!hash__pmd_trans_huge(*pmdp) && !pmd_devmap(*pmdp));
198 assert_spin_locked(pmd_lockptr(mm, pmdp));
199#endif
200
201 __asm__ __volatile__(
202 "1: ldarx %0,0,%3\n\
203 and. %1,%0,%6\n\
204 bne- 1b \n\
205 andc %1,%0,%4 \n\
206 or %1,%1,%7\n\
207 stdcx. %1,0,%3 \n\
208 bne- 1b"
209 : "=&r" (old_be), "=&r" (tmp), "=m" (*pmdp)
210 : "r" (pmdp), "r" (cpu_to_be64(clr)), "m" (*pmdp),
211 "r" (cpu_to_be64(H_PAGE_BUSY)), "r" (cpu_to_be64(set))
212 : "cc" );
213
214 old = be64_to_cpu(old_be);
215
216 trace_hugepage_update(addr, old, clr, set);
217 if (old & H_PAGE_HASHPTE)
218 hpte_do_hugepage_flush(mm, addr, pmdp, old);
219 return old;
220}
221
222pmd_t hash__pmdp_collapse_flush(struct vm_area_struct *vma, unsigned long address,
223 pmd_t *pmdp)
224{
225 pmd_t pmd;
226
227 VM_BUG_ON(address & ~HPAGE_PMD_MASK);
228 VM_BUG_ON(pmd_trans_huge(*pmdp));
229 VM_BUG_ON(pmd_devmap(*pmdp));
230
231 pmd = *pmdp;
232 pmd_clear(pmdp);
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248 serialize_against_pte_lookup(vma->vm_mm);
249
250
251
252
253
254
255
256
257
258 flush_tlb_pmd_range(vma->vm_mm, &pmd, address);
259 return pmd;
260}
261
262
263
264
265
266void hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
267 pgtable_t pgtable)
268{
269 pgtable_t *pgtable_slot;
270
271 assert_spin_locked(pmd_lockptr(mm, pmdp));
272
273
274
275 pgtable_slot = (pgtable_t *)pmdp + PTRS_PER_PMD;
276 *pgtable_slot = pgtable;
277
278
279
280
281
282
283 smp_wmb();
284}
285
286pgtable_t hash__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp)
287{
288 pgtable_t pgtable;
289 pgtable_t *pgtable_slot;
290
291 assert_spin_locked(pmd_lockptr(mm, pmdp));
292
293 pgtable_slot = (pgtable_t *)pmdp + PTRS_PER_PMD;
294 pgtable = *pgtable_slot;
295
296
297
298 *pgtable_slot = NULL;
299
300
301
302
303 memset(pgtable, 0, PTE_FRAG_SIZE);
304 return pgtable;
305}
306
307
308
309
310
311void hpte_do_hugepage_flush(struct mm_struct *mm, unsigned long addr,
312 pmd_t *pmdp, unsigned long old_pmd)
313{
314 int ssize;
315 unsigned int psize;
316 unsigned long vsid;
317 unsigned long flags = 0;
318
319
320#ifdef CONFIG_DEBUG_VM
321 psize = get_slice_psize(mm, addr);
322 BUG_ON(psize == MMU_PAGE_16M);
323#endif
324 if (old_pmd & H_PAGE_COMBO)
325 psize = MMU_PAGE_4K;
326 else
327 psize = MMU_PAGE_64K;
328
329 if (!is_kernel_addr(addr)) {
330 ssize = user_segment_size(addr);
331 vsid = get_user_vsid(&mm->context, addr, ssize);
332 WARN_ON(vsid == 0);
333 } else {
334 vsid = get_kernel_vsid(addr, mmu_kernel_ssize);
335 ssize = mmu_kernel_ssize;
336 }
337
338 if (mm_is_thread_local(mm))
339 flags |= HPTE_LOCAL_UPDATE;
340
341 return flush_hash_hugepage(vsid, addr, pmdp, psize, ssize, flags);
342}
343
344pmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
345 unsigned long addr, pmd_t *pmdp)
346{
347 pmd_t old_pmd;
348 pgtable_t pgtable;
349 unsigned long old;
350 pgtable_t *pgtable_slot;
351
352 old = pmd_hugepage_update(mm, addr, pmdp, ~0UL, 0);
353 old_pmd = __pmd(old);
354
355
356
357
358
359 pgtable_slot = (pgtable_t *)pmdp + PTRS_PER_PMD;
360 pgtable = *pgtable_slot;
361
362
363
364
365 memset(pgtable, 0, PTE_FRAG_SIZE);
366 return old_pmd;
367}
368
369int hash__has_transparent_hugepage(void)
370{
371
372 if (!mmu_has_feature(MMU_FTR_16M_PAGE))
373 return 0;
374
375
376
377 if (mmu_psize_defs[MMU_PAGE_16M].shift != PMD_SHIFT)
378 return 0;
379
380
381
382
383
384
385
386
387 if (mmu_psize_defs[MMU_PAGE_64K].shift &&
388 (mmu_psize_defs[MMU_PAGE_64K].penc[MMU_PAGE_16M] == -1))
389 return 0;
390
391
392
393 if (mmu_psize_defs[MMU_PAGE_4K].penc[MMU_PAGE_16M] == -1)
394 return 0;
395
396 return 1;
397}
398EXPORT_SYMBOL_GPL(hash__has_transparent_hugepage);
399
400#endif
401
402#ifdef CONFIG_STRICT_KERNEL_RWX
403static bool hash__change_memory_range(unsigned long start, unsigned long end,
404 unsigned long newpp)
405{
406 unsigned long idx;
407 unsigned int step, shift;
408
409 shift = mmu_psize_defs[mmu_linear_psize].shift;
410 step = 1 << shift;
411
412 start = ALIGN_DOWN(start, step);
413 end = ALIGN(end, step);
414
415 if (start >= end)
416 return false;
417
418 pr_debug("Changing page protection on range 0x%lx-0x%lx, to 0x%lx, step 0x%x\n",
419 start, end, newpp, step);
420
421 for (idx = start; idx < end; idx += step)
422
423 mmu_hash_ops.hpte_updateboltedpp(newpp, idx, mmu_linear_psize,
424 mmu_kernel_ssize);
425
426 return true;
427}
428
429void hash__mark_rodata_ro(void)
430{
431 unsigned long start, end;
432
433 start = (unsigned long)_stext;
434 end = (unsigned long)__init_begin;
435
436 WARN_ON(!hash__change_memory_range(start, end, PP_RXXX));
437}
438
439void hash__mark_initmem_nx(void)
440{
441 unsigned long start, end, pp;
442
443 start = (unsigned long)__init_begin;
444 end = (unsigned long)__init_end;
445
446 pp = htab_convert_pte_flags(pgprot_val(PAGE_KERNEL));
447
448 WARN_ON(!hash__change_memory_range(start, end, pp));
449}
450#endif
451