blob: 473dd430e30632c8a38ff629b007a77b3363d857 [file] [log] [blame]
Paul Mackerrasfa282372008-01-24 08:35:13 +11001/*
2 * Copyright 2007-2008 Paul Mackerras, IBM Corp.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9
10#include <linux/errno.h>
11#include <linux/kernel.h>
12#include <linux/gfp.h>
Paul Mackerrasfa282372008-01-24 08:35:13 +110013#include <linux/types.h>
14#include <linux/mm.h>
15#include <linux/hugetlb.h>
Al Viro3691d612018-05-02 23:20:46 +100016#include <linux/syscalls.h>
Paul Mackerrasfa282372008-01-24 08:35:13 +110017
18#include <asm/pgtable.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080019#include <linux/uaccess.h>
Paul Mackerrasfa282372008-01-24 08:35:13 +110020
21/*
22 * Free all pages allocated for subpage protection maps and pointers.
23 * Also makes sure that the subpage_prot_table structure is
24 * reinitialized for the next user.
25 */
David Gibsond28513b2009-11-26 18:56:04 +000026void subpage_prot_free(struct mm_struct *mm)
Paul Mackerrasfa282372008-01-24 08:35:13 +110027{
Aneesh Kumar K.V60458fb2019-04-17 18:33:48 +053028 struct subpage_prot_table *spt = mm_ctx_subpage_prot(&mm->context);
Paul Mackerrasfa282372008-01-24 08:35:13 +110029 unsigned long i, j, addr;
30 u32 **p;
31
Aneesh Kumar K.Vef629cc2019-04-17 18:33:51 +053032 if (!spt)
33 return;
34
Paul Mackerrasfa282372008-01-24 08:35:13 +110035 for (i = 0; i < 4; ++i) {
36 if (spt->low_prot[i]) {
37 free_page((unsigned long)spt->low_prot[i]);
38 spt->low_prot[i] = NULL;
39 }
40 }
41 addr = 0;
Aneesh Kumar K.V0da12a72017-06-17 20:00:55 +053042 for (i = 0; i < (TASK_SIZE_USER64 >> 43); ++i) {
Paul Mackerrasfa282372008-01-24 08:35:13 +110043 p = spt->protptrs[i];
44 if (!p)
45 continue;
46 spt->protptrs[i] = NULL;
47 for (j = 0; j < SBP_L2_COUNT && addr < spt->maxaddr;
48 ++j, addr += PAGE_SIZE)
49 if (p[j])
50 free_page((unsigned long)p[j]);
51 free_page((unsigned long)p);
52 }
53 spt->maxaddr = 0;
Aneesh Kumar K.Vef629cc2019-04-17 18:33:51 +053054 kfree(spt);
David Gibsond28513b2009-11-26 18:56:04 +000055}
56
Paul Mackerrasfa282372008-01-24 08:35:13 +110057static void hpte_flush_range(struct mm_struct *mm, unsigned long addr,
58 int npages)
59{
60 pgd_t *pgd;
61 pud_t *pud;
62 pmd_t *pmd;
63 pte_t *pte;
64 spinlock_t *ptl;
65
66 pgd = pgd_offset(mm, addr);
67 if (pgd_none(*pgd))
68 return;
69 pud = pud_offset(pgd, addr);
70 if (pud_none(*pud))
71 return;
72 pmd = pmd_offset(pud, addr);
73 if (pmd_none(*pmd))
74 return;
75 pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
76 arch_enter_lazy_mmu_mode();
77 for (; npages > 0; --npages) {
Aneesh Kumar K.V88247e82014-02-12 09:13:36 +053078 pte_update(mm, addr, pte, 0, 0, 0);
Paul Mackerrasfa282372008-01-24 08:35:13 +110079 addr += PAGE_SIZE;
80 ++pte;
81 }
82 arch_leave_lazy_mmu_mode();
83 pte_unmap_unlock(pte - 1, ptl);
84}
85
86/*
87 * Clear the subpage protection map for an address range, allowing
88 * all accesses that are allowed by the pte permissions.
89 */
90static void subpage_prot_clear(unsigned long addr, unsigned long len)
91{
92 struct mm_struct *mm = current->mm;
Aneesh Kumar K.V2c474c02019-04-30 13:29:07 +053093 struct subpage_prot_table *spt;
Paul Mackerrasfa282372008-01-24 08:35:13 +110094 u32 **spm, *spp;
Joe MacDonald6b5e7222012-08-21 08:22:28 +000095 unsigned long i;
96 size_t nw;
Paul Mackerrasfa282372008-01-24 08:35:13 +110097 unsigned long next, limit;
98
99 down_write(&mm->mmap_sem);
Aneesh Kumar K.V2c474c02019-04-30 13:29:07 +0530100
101 spt = mm_ctx_subpage_prot(&mm->context);
102 if (!spt)
103 goto err_out;
104
Paul Mackerrasfa282372008-01-24 08:35:13 +1100105 limit = addr + len;
106 if (limit > spt->maxaddr)
107 limit = spt->maxaddr;
108 for (; addr < limit; addr = next) {
109 next = pmd_addr_end(addr, limit);
Anton Blanchardb0d436c2013-08-07 02:01:24 +1000110 if (addr < 0x100000000UL) {
Paul Mackerrasfa282372008-01-24 08:35:13 +1100111 spm = spt->low_prot;
112 } else {
113 spm = spt->protptrs[addr >> SBP_L3_SHIFT];
114 if (!spm)
115 continue;
116 }
117 spp = spm[(addr >> SBP_L2_SHIFT) & (SBP_L2_COUNT - 1)];
118 if (!spp)
119 continue;
120 spp += (addr >> PAGE_SHIFT) & (SBP_L1_COUNT - 1);
121
122 i = (addr >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
123 nw = PTRS_PER_PTE - i;
124 if (addr + (nw << PAGE_SHIFT) > next)
125 nw = (next - addr) >> PAGE_SHIFT;
126
127 memset(spp, 0, nw * sizeof(u32));
128
129 /* now flush any existing HPTEs for the range */
130 hpte_flush_range(mm, addr, nw);
131 }
Aneesh Kumar K.V2c474c02019-04-30 13:29:07 +0530132
133err_out:
Paul Mackerrasfa282372008-01-24 08:35:13 +1100134 up_write(&mm->mmap_sem);
135}
136
Aneesh Kumar K.Vd8e355a2013-06-20 14:30:25 +0530137#ifdef CONFIG_TRANSPARENT_HUGEPAGE
138static int subpage_walk_pmd_entry(pmd_t *pmd, unsigned long addr,
139 unsigned long end, struct mm_walk *walk)
140{
Naoya Horiguchi1757bbd2015-02-11 15:28:00 -0800141 struct vm_area_struct *vma = walk->vma;
Kirill A. Shutemov78ddc532016-01-15 16:52:42 -0800142 split_huge_pmd(vma, pmd, addr);
Aneesh Kumar K.Vd8e355a2013-06-20 14:30:25 +0530143 return 0;
144}
145
146static void subpage_mark_vma_nohuge(struct mm_struct *mm, unsigned long addr,
147 unsigned long len)
148{
149 struct vm_area_struct *vma;
150 struct mm_walk subpage_proto_walk = {
151 .mm = mm,
152 .pmd_entry = subpage_walk_pmd_entry,
153 };
154
155 /*
156 * We don't try too hard, we just mark all the vma in that range
157 * VM_NOHUGEPAGE and split them.
158 */
159 vma = find_vma(mm, addr);
160 /*
161 * If the range is in unmapped range, just return
162 */
163 if (vma && ((addr + len) <= vma->vm_start))
164 return;
165
166 while (vma) {
167 if (vma->vm_start >= (addr + len))
168 break;
169 vma->vm_flags |= VM_NOHUGEPAGE;
Naoya Horiguchi1757bbd2015-02-11 15:28:00 -0800170 walk_page_vma(vma, &subpage_proto_walk);
Aneesh Kumar K.Vd8e355a2013-06-20 14:30:25 +0530171 vma = vma->vm_next;
172 }
173}
174#else
175static void subpage_mark_vma_nohuge(struct mm_struct *mm, unsigned long addr,
176 unsigned long len)
177{
178 return;
179}
180#endif
181
Paul Mackerrasfa282372008-01-24 08:35:13 +1100182/*
183 * Copy in a subpage protection map for an address range.
184 * The map has 2 bits per 4k subpage, so 32 bits per 64k page.
185 * Each 2-bit field is 0 to allow any access, 1 to prevent writes,
186 * 2 or 3 to prevent all accesses.
187 * Note that the normal page protections also apply; the subpage
188 * protection mechanism is an additional constraint, so putting 0
189 * in a 2-bit field won't allow writes to a page that is otherwise
190 * write-protected.
191 */
Al Viro3691d612018-05-02 23:20:46 +1000192SYSCALL_DEFINE3(subpage_prot, unsigned long, addr,
193 unsigned long, len, u32 __user *, map)
Paul Mackerrasfa282372008-01-24 08:35:13 +1100194{
195 struct mm_struct *mm = current->mm;
Aneesh Kumar K.V2c474c02019-04-30 13:29:07 +0530196 struct subpage_prot_table *spt;
Paul Mackerrasfa282372008-01-24 08:35:13 +1100197 u32 **spm, *spp;
Joe MacDonald6b5e7222012-08-21 08:22:28 +0000198 unsigned long i;
199 size_t nw;
Paul Mackerrasfa282372008-01-24 08:35:13 +1100200 unsigned long next, limit;
201 int err;
202
Anshuman Khandual5b2b8072017-12-04 11:19:22 +0530203 if (radix_enabled())
204 return -ENOENT;
205
Paul Mackerrasfa282372008-01-24 08:35:13 +1100206 /* Check parameters */
207 if ((addr & ~PAGE_MASK) || (len & ~PAGE_MASK) ||
Aneesh Kumar K.Vbe77e992017-04-14 00:48:21 +0530208 addr >= mm->task_size || len >= mm->task_size ||
209 addr + len > mm->task_size)
Paul Mackerrasfa282372008-01-24 08:35:13 +1100210 return -EINVAL;
211
212 if (is_hugepage_only_range(mm, addr, len))
213 return -EINVAL;
214
215 if (!map) {
216 /* Clear out the protection map for the address range */
217 subpage_prot_clear(addr, len);
218 return 0;
219 }
220
Linus Torvalds96d4f262019-01-03 18:57:57 -0800221 if (!access_ok(map, (len >> PAGE_SHIFT) * sizeof(u32)))
Paul Mackerrasfa282372008-01-24 08:35:13 +1100222 return -EFAULT;
223
224 down_write(&mm->mmap_sem);
Aneesh Kumar K.Vef629cc2019-04-17 18:33:51 +0530225
Aneesh Kumar K.V2c474c02019-04-30 13:29:07 +0530226 spt = mm_ctx_subpage_prot(&mm->context);
Aneesh Kumar K.Vef629cc2019-04-17 18:33:51 +0530227 if (!spt) {
228 /*
229 * Allocate subpage prot table if not already done.
230 * Do this with mmap_sem held
231 */
232 spt = kzalloc(sizeof(struct subpage_prot_table), GFP_KERNEL);
233 if (!spt) {
234 err = -ENOMEM;
235 goto out;
236 }
237 mm->context.hash_context->spt = spt;
238 }
239
Aneesh Kumar K.Vd8e355a2013-06-20 14:30:25 +0530240 subpage_mark_vma_nohuge(mm, addr, len);
Paul Mackerrasfa282372008-01-24 08:35:13 +1100241 for (limit = addr + len; addr < limit; addr = next) {
242 next = pmd_addr_end(addr, limit);
243 err = -ENOMEM;
Anton Blanchardb0d436c2013-08-07 02:01:24 +1000244 if (addr < 0x100000000UL) {
Paul Mackerrasfa282372008-01-24 08:35:13 +1100245 spm = spt->low_prot;
246 } else {
247 spm = spt->protptrs[addr >> SBP_L3_SHIFT];
248 if (!spm) {
249 spm = (u32 **)get_zeroed_page(GFP_KERNEL);
250 if (!spm)
251 goto out;
252 spt->protptrs[addr >> SBP_L3_SHIFT] = spm;
253 }
254 }
255 spm += (addr >> SBP_L2_SHIFT) & (SBP_L2_COUNT - 1);
256 spp = *spm;
257 if (!spp) {
258 spp = (u32 *)get_zeroed_page(GFP_KERNEL);
259 if (!spp)
260 goto out;
261 *spm = spp;
262 }
263 spp += (addr >> PAGE_SHIFT) & (SBP_L1_COUNT - 1);
264
265 local_irq_disable();
266 demote_segment_4k(mm, addr);
267 local_irq_enable();
268
269 i = (addr >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
270 nw = PTRS_PER_PTE - i;
271 if (addr + (nw << PAGE_SHIFT) > next)
272 nw = (next - addr) >> PAGE_SHIFT;
273
274 up_write(&mm->mmap_sem);
Paul Mackerrasfa282372008-01-24 08:35:13 +1100275 if (__copy_from_user(spp, map, nw * sizeof(u32)))
Markus Elfringa967f162017-01-21 16:10:50 +0100276 return -EFAULT;
Paul Mackerrasfa282372008-01-24 08:35:13 +1100277 map += nw;
278 down_write(&mm->mmap_sem);
279
280 /* now flush any existing HPTEs for the range */
281 hpte_flush_range(mm, addr, nw);
282 }
283 if (limit > spt->maxaddr)
284 spt->maxaddr = limit;
285 err = 0;
286 out:
287 up_write(&mm->mmap_sem);
Paul Mackerrasfa282372008-01-24 08:35:13 +1100288 return err;
289}