blob: 190d0c65904ae0051f133d05f253d4b8c3c99cec [file] [log] [blame]
Martin Schwidefsky3610cce2007-10-22 12:52:47 +02001/*
Heiko Carstensa53c8fa2012-07-20 11:15:04 +02002 * Copyright IBM Corp. 2007, 2011
Martin Schwidefsky3610cce2007-10-22 12:52:47 +02003 * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
4 */
5
6#include <linux/sched.h>
7#include <linux/kernel.h>
8#include <linux/errno.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +09009#include <linux/gfp.h>
Martin Schwidefsky3610cce2007-10-22 12:52:47 +020010#include <linux/mm.h>
11#include <linux/swap.h>
12#include <linux/smp.h>
Martin Schwidefsky3610cce2007-10-22 12:52:47 +020013#include <linux/spinlock.h>
Martin Schwidefsky80217142010-10-25 16:10:11 +020014#include <linux/rcupdate.h>
Martin Schwidefskye5992f22011-07-24 10:48:20 +020015#include <linux/slab.h>
Konstantin Weitzb31288f2013-04-17 17:36:29 +020016#include <linux/swapops.h>
Martin Schwidefsky0b46e0a2015-04-15 13:23:26 +020017#include <linux/sysctl.h>
Dominik Dingel3ac8e382014-10-23 12:09:17 +020018#include <linux/ksm.h>
19#include <linux/mman.h>
Martin Schwidefsky3610cce2007-10-22 12:52:47 +020020
Martin Schwidefsky3610cce2007-10-22 12:52:47 +020021#include <asm/pgtable.h>
22#include <asm/pgalloc.h>
23#include <asm/tlb.h>
24#include <asm/tlbflush.h>
Martin Schwidefsky6252d702008-02-09 18:24:37 +010025#include <asm/mmu_context.h>
Martin Schwidefsky3610cce2007-10-22 12:52:47 +020026
Martin Schwidefskyebde7652016-03-08 11:08:09 +010027static inline pte_t ptep_flush_direct(struct mm_struct *mm,
28 unsigned long addr, pte_t *ptep)
29{
Martin Schwidefskyebde7652016-03-08 11:08:09 +010030 pte_t old;
31
32 old = *ptep;
33 if (unlikely(pte_val(old) & _PAGE_INVALID))
34 return old;
Martin Schwidefsky64f31d52016-05-25 09:45:26 +020035 atomic_inc(&mm->context.flush_count);
36 if (MACHINE_HAS_TLB_LC &&
Martin Schwidefskyebde7652016-03-08 11:08:09 +010037 cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id())))
Martin Schwidefsky34eeaf32016-06-14 12:38:40 +020038 __ptep_ipte(addr, ptep, IPTE_LOCAL);
Martin Schwidefskyebde7652016-03-08 11:08:09 +010039 else
Martin Schwidefsky34eeaf32016-06-14 12:38:40 +020040 __ptep_ipte(addr, ptep, IPTE_GLOBAL);
Martin Schwidefsky64f31d52016-05-25 09:45:26 +020041 atomic_dec(&mm->context.flush_count);
Martin Schwidefskyebde7652016-03-08 11:08:09 +010042 return old;
43}
44
45static inline pte_t ptep_flush_lazy(struct mm_struct *mm,
46 unsigned long addr, pte_t *ptep)
47{
Martin Schwidefskyebde7652016-03-08 11:08:09 +010048 pte_t old;
49
50 old = *ptep;
51 if (unlikely(pte_val(old) & _PAGE_INVALID))
52 return old;
Martin Schwidefsky64f31d52016-05-25 09:45:26 +020053 atomic_inc(&mm->context.flush_count);
54 if (cpumask_equal(&mm->context.cpu_attach_mask,
55 cpumask_of(smp_processor_id()))) {
Martin Schwidefskyebde7652016-03-08 11:08:09 +010056 pte_val(*ptep) |= _PAGE_INVALID;
57 mm->context.flush_mm = 1;
58 } else
Martin Schwidefsky34eeaf32016-06-14 12:38:40 +020059 __ptep_ipte(addr, ptep, IPTE_GLOBAL);
Martin Schwidefsky64f31d52016-05-25 09:45:26 +020060 atomic_dec(&mm->context.flush_count);
Martin Schwidefskyebde7652016-03-08 11:08:09 +010061 return old;
62}
63
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +010064static inline pgste_t pgste_get_lock(pte_t *ptep)
65{
66 unsigned long new = 0;
67#ifdef CONFIG_PGSTE
68 unsigned long old;
69
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +010070 asm(
71 " lg %0,%2\n"
72 "0: lgr %1,%0\n"
73 " nihh %0,0xff7f\n" /* clear PCL bit in old */
74 " oihh %1,0x0080\n" /* set PCL bit in new */
75 " csg %0,%1,%2\n"
76 " jl 0b\n"
77 : "=&d" (old), "=&d" (new), "=Q" (ptep[PTRS_PER_PTE])
78 : "Q" (ptep[PTRS_PER_PTE]) : "cc", "memory");
79#endif
80 return __pgste(new);
81}
82
83static inline void pgste_set_unlock(pte_t *ptep, pgste_t pgste)
84{
85#ifdef CONFIG_PGSTE
86 asm(
87 " nihh %1,0xff7f\n" /* clear PCL bit */
88 " stg %1,%0\n"
89 : "=Q" (ptep[PTRS_PER_PTE])
90 : "d" (pgste_val(pgste)), "Q" (ptep[PTRS_PER_PTE])
91 : "cc", "memory");
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +010092#endif
93}
94
95static inline pgste_t pgste_get(pte_t *ptep)
96{
97 unsigned long pgste = 0;
98#ifdef CONFIG_PGSTE
99 pgste = *(unsigned long *)(ptep + PTRS_PER_PTE);
100#endif
101 return __pgste(pgste);
102}
103
104static inline void pgste_set(pte_t *ptep, pgste_t pgste)
105{
106#ifdef CONFIG_PGSTE
107 *(pgste_t *)(ptep + PTRS_PER_PTE) = pgste;
108#endif
109}
110
Martin Schwidefskyebde7652016-03-08 11:08:09 +0100111static inline pgste_t pgste_update_all(pte_t pte, pgste_t pgste,
112 struct mm_struct *mm)
113{
114#ifdef CONFIG_PGSTE
115 unsigned long address, bits, skey;
116
117 if (!mm_use_skey(mm) || pte_val(pte) & _PAGE_INVALID)
118 return pgste;
119 address = pte_val(pte) & PAGE_MASK;
120 skey = (unsigned long) page_get_storage_key(address);
121 bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED);
122 /* Transfer page changed & referenced bit to guest bits in pgste */
123 pgste_val(pgste) |= bits << 48; /* GR bit & GC bit */
124 /* Copy page access key and fetch protection bit to pgste */
125 pgste_val(pgste) &= ~(PGSTE_ACC_BITS | PGSTE_FP_BIT);
126 pgste_val(pgste) |= (skey & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56;
127#endif
128 return pgste;
129
130}
131
132static inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry,
133 struct mm_struct *mm)
134{
135#ifdef CONFIG_PGSTE
136 unsigned long address;
137 unsigned long nkey;
138
139 if (!mm_use_skey(mm) || pte_val(entry) & _PAGE_INVALID)
140 return;
141 VM_BUG_ON(!(pte_val(*ptep) & _PAGE_INVALID));
142 address = pte_val(entry) & PAGE_MASK;
143 /*
144 * Set page access key and fetch protection bit from pgste.
145 * The guest C/R information is still in the PGSTE, set real
146 * key C/R to 0.
147 */
148 nkey = (pgste_val(pgste) & (PGSTE_ACC_BITS | PGSTE_FP_BIT)) >> 56;
149 nkey |= (pgste_val(pgste) & (PGSTE_GR_BIT | PGSTE_GC_BIT)) >> 48;
150 page_set_storage_key(address, nkey, 0);
151#endif
152}
153
154static inline pgste_t pgste_set_pte(pte_t *ptep, pgste_t pgste, pte_t entry)
155{
156#ifdef CONFIG_PGSTE
157 if ((pte_val(entry) & _PAGE_PRESENT) &&
158 (pte_val(entry) & _PAGE_WRITE) &&
159 !(pte_val(entry) & _PAGE_INVALID)) {
160 if (!MACHINE_HAS_ESOP) {
161 /*
162 * Without enhanced suppression-on-protection force
163 * the dirty bit on for all writable ptes.
164 */
165 pte_val(entry) |= _PAGE_DIRTY;
166 pte_val(entry) &= ~_PAGE_PROTECT;
167 }
168 if (!(pte_val(entry) & _PAGE_PROTECT))
169 /* This pte allows write access, set user-dirty */
170 pgste_val(pgste) |= PGSTE_UC_BIT;
171 }
172#endif
173 *ptep = entry;
174 return pgste;
175}
176
Martin Schwidefskyb2d73b22016-03-08 11:54:42 +0100177static inline pgste_t pgste_pte_notify(struct mm_struct *mm,
178 unsigned long addr,
179 pte_t *ptep, pgste_t pgste)
Martin Schwidefskyebde7652016-03-08 11:08:09 +0100180{
181#ifdef CONFIG_PGSTE
Martin Schwidefsky4be130a2016-03-08 12:12:18 +0100182 unsigned long bits;
183
184 bits = pgste_val(pgste) & (PGSTE_IN_BIT | PGSTE_VSIE_BIT);
185 if (bits) {
186 pgste_val(pgste) ^= bits;
187 ptep_notify(mm, addr, ptep, bits);
Martin Schwidefskyebde7652016-03-08 11:08:09 +0100188 }
189#endif
190 return pgste;
191}
192
Martin Schwidefskyebde7652016-03-08 11:08:09 +0100193static inline pgste_t ptep_xchg_start(struct mm_struct *mm,
194 unsigned long addr, pte_t *ptep)
195{
196 pgste_t pgste = __pgste(0);
197
198 if (mm_has_pgste(mm)) {
199 pgste = pgste_get_lock(ptep);
Martin Schwidefskyb2d73b22016-03-08 11:54:42 +0100200 pgste = pgste_pte_notify(mm, addr, ptep, pgste);
Martin Schwidefskyebde7652016-03-08 11:08:09 +0100201 }
202 return pgste;
203}
204
205static inline void ptep_xchg_commit(struct mm_struct *mm,
206 unsigned long addr, pte_t *ptep,
207 pgste_t pgste, pte_t old, pte_t new)
208{
209 if (mm_has_pgste(mm)) {
210 if (pte_val(old) & _PAGE_INVALID)
211 pgste_set_key(ptep, pgste, new, mm);
212 if (pte_val(new) & _PAGE_INVALID) {
213 pgste = pgste_update_all(old, pgste, mm);
214 if ((pgste_val(pgste) & _PGSTE_GPS_USAGE_MASK) ==
215 _PGSTE_GPS_USAGE_UNUSED)
216 pte_val(old) |= _PAGE_UNUSED;
217 }
218 pgste = pgste_set_pte(ptep, pgste, new);
219 pgste_set_unlock(ptep, pgste);
220 } else {
221 *ptep = new;
222 }
223}
224
225pte_t ptep_xchg_direct(struct mm_struct *mm, unsigned long addr,
226 pte_t *ptep, pte_t new)
227{
228 pgste_t pgste;
229 pte_t old;
230
Martin Schwidefskya9809402016-06-06 10:30:45 +0200231 preempt_disable();
Martin Schwidefskyebde7652016-03-08 11:08:09 +0100232 pgste = ptep_xchg_start(mm, addr, ptep);
233 old = ptep_flush_direct(mm, addr, ptep);
234 ptep_xchg_commit(mm, addr, ptep, pgste, old, new);
Martin Schwidefskya9809402016-06-06 10:30:45 +0200235 preempt_enable();
Martin Schwidefskyebde7652016-03-08 11:08:09 +0100236 return old;
237}
238EXPORT_SYMBOL(ptep_xchg_direct);
239
240pte_t ptep_xchg_lazy(struct mm_struct *mm, unsigned long addr,
241 pte_t *ptep, pte_t new)
242{
243 pgste_t pgste;
244 pte_t old;
245
Martin Schwidefskya9809402016-06-06 10:30:45 +0200246 preempt_disable();
Martin Schwidefskyebde7652016-03-08 11:08:09 +0100247 pgste = ptep_xchg_start(mm, addr, ptep);
248 old = ptep_flush_lazy(mm, addr, ptep);
249 ptep_xchg_commit(mm, addr, ptep, pgste, old, new);
Martin Schwidefskya9809402016-06-06 10:30:45 +0200250 preempt_enable();
Martin Schwidefskyebde7652016-03-08 11:08:09 +0100251 return old;
252}
253EXPORT_SYMBOL(ptep_xchg_lazy);
254
255pte_t ptep_modify_prot_start(struct mm_struct *mm, unsigned long addr,
256 pte_t *ptep)
257{
258 pgste_t pgste;
259 pte_t old;
260
Martin Schwidefskya9809402016-06-06 10:30:45 +0200261 preempt_disable();
Martin Schwidefskyebde7652016-03-08 11:08:09 +0100262 pgste = ptep_xchg_start(mm, addr, ptep);
263 old = ptep_flush_lazy(mm, addr, ptep);
264 if (mm_has_pgste(mm)) {
265 pgste = pgste_update_all(old, pgste, mm);
266 pgste_set(ptep, pgste);
267 }
268 return old;
269}
270EXPORT_SYMBOL(ptep_modify_prot_start);
271
272void ptep_modify_prot_commit(struct mm_struct *mm, unsigned long addr,
273 pte_t *ptep, pte_t pte)
274{
275 pgste_t pgste;
276
Martin Schwidefsky57d7f932016-03-22 10:54:24 +0100277 if (!MACHINE_HAS_NX)
278 pte_val(pte) &= ~_PAGE_NOEXEC;
Martin Schwidefskyebde7652016-03-08 11:08:09 +0100279 if (mm_has_pgste(mm)) {
280 pgste = pgste_get(ptep);
281 pgste_set_key(ptep, pgste, pte, mm);
282 pgste = pgste_set_pte(ptep, pgste, pte);
283 pgste_set_unlock(ptep, pgste);
284 } else {
285 *ptep = pte;
286 }
Martin Schwidefskya9809402016-06-06 10:30:45 +0200287 preempt_enable();
Martin Schwidefskyebde7652016-03-08 11:08:09 +0100288}
289EXPORT_SYMBOL(ptep_modify_prot_commit);
290
Martin Schwidefsky227be792016-03-08 11:09:25 +0100291static inline pmd_t pmdp_flush_direct(struct mm_struct *mm,
292 unsigned long addr, pmd_t *pmdp)
293{
Martin Schwidefsky227be792016-03-08 11:09:25 +0100294 pmd_t old;
295
296 old = *pmdp;
297 if (pmd_val(old) & _SEGMENT_ENTRY_INVALID)
298 return old;
299 if (!MACHINE_HAS_IDTE) {
300 __pmdp_csp(pmdp);
301 return old;
302 }
Martin Schwidefsky64f31d52016-05-25 09:45:26 +0200303 atomic_inc(&mm->context.flush_count);
304 if (MACHINE_HAS_TLB_LC &&
Martin Schwidefsky227be792016-03-08 11:09:25 +0100305 cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id())))
Martin Schwidefsky47e4d852016-06-14 12:41:35 +0200306 __pmdp_idte(addr, pmdp, IDTE_LOCAL);
Martin Schwidefsky227be792016-03-08 11:09:25 +0100307 else
Martin Schwidefsky47e4d852016-06-14 12:41:35 +0200308 __pmdp_idte(addr, pmdp, IDTE_GLOBAL);
Martin Schwidefsky64f31d52016-05-25 09:45:26 +0200309 atomic_dec(&mm->context.flush_count);
Martin Schwidefsky227be792016-03-08 11:09:25 +0100310 return old;
311}
312
313static inline pmd_t pmdp_flush_lazy(struct mm_struct *mm,
314 unsigned long addr, pmd_t *pmdp)
315{
Martin Schwidefsky227be792016-03-08 11:09:25 +0100316 pmd_t old;
317
318 old = *pmdp;
319 if (pmd_val(old) & _SEGMENT_ENTRY_INVALID)
320 return old;
Martin Schwidefsky64f31d52016-05-25 09:45:26 +0200321 atomic_inc(&mm->context.flush_count);
322 if (cpumask_equal(&mm->context.cpu_attach_mask,
323 cpumask_of(smp_processor_id()))) {
Martin Schwidefsky227be792016-03-08 11:09:25 +0100324 pmd_val(*pmdp) |= _SEGMENT_ENTRY_INVALID;
325 mm->context.flush_mm = 1;
326 } else if (MACHINE_HAS_IDTE)
Martin Schwidefsky47e4d852016-06-14 12:41:35 +0200327 __pmdp_idte(addr, pmdp, IDTE_GLOBAL);
Martin Schwidefsky227be792016-03-08 11:09:25 +0100328 else
329 __pmdp_csp(pmdp);
Martin Schwidefsky64f31d52016-05-25 09:45:26 +0200330 atomic_dec(&mm->context.flush_count);
Martin Schwidefsky227be792016-03-08 11:09:25 +0100331 return old;
332}
333
334pmd_t pmdp_xchg_direct(struct mm_struct *mm, unsigned long addr,
335 pmd_t *pmdp, pmd_t new)
336{
337 pmd_t old;
338
Martin Schwidefskya9809402016-06-06 10:30:45 +0200339 preempt_disable();
Martin Schwidefsky227be792016-03-08 11:09:25 +0100340 old = pmdp_flush_direct(mm, addr, pmdp);
341 *pmdp = new;
Martin Schwidefskya9809402016-06-06 10:30:45 +0200342 preempt_enable();
Martin Schwidefsky227be792016-03-08 11:09:25 +0100343 return old;
344}
345EXPORT_SYMBOL(pmdp_xchg_direct);
346
347pmd_t pmdp_xchg_lazy(struct mm_struct *mm, unsigned long addr,
348 pmd_t *pmdp, pmd_t new)
349{
350 pmd_t old;
351
Martin Schwidefskya9809402016-06-06 10:30:45 +0200352 preempt_disable();
Martin Schwidefsky227be792016-03-08 11:09:25 +0100353 old = pmdp_flush_lazy(mm, addr, pmdp);
354 *pmdp = new;
Martin Schwidefskya9809402016-06-06 10:30:45 +0200355 preempt_enable();
Martin Schwidefsky227be792016-03-08 11:09:25 +0100356 return old;
357}
358EXPORT_SYMBOL(pmdp_xchg_lazy);
359
Gerald Schaeferd08de8e2016-07-04 14:47:01 +0200360static inline pud_t pudp_flush_direct(struct mm_struct *mm,
361 unsigned long addr, pud_t *pudp)
362{
363 pud_t old;
364
365 old = *pudp;
366 if (pud_val(old) & _REGION_ENTRY_INVALID)
367 return old;
368 if (!MACHINE_HAS_IDTE) {
369 /*
370 * Invalid bit position is the same for pmd and pud, so we can
371 * re-use _pmd_csp() here
372 */
373 __pmdp_csp((pmd_t *) pudp);
374 return old;
375 }
376 atomic_inc(&mm->context.flush_count);
377 if (MACHINE_HAS_TLB_LC &&
378 cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id())))
Martin Schwidefsky47e4d852016-06-14 12:41:35 +0200379 __pudp_idte(addr, pudp, IDTE_LOCAL);
Gerald Schaeferd08de8e2016-07-04 14:47:01 +0200380 else
Martin Schwidefsky47e4d852016-06-14 12:41:35 +0200381 __pudp_idte(addr, pudp, IDTE_GLOBAL);
Gerald Schaeferd08de8e2016-07-04 14:47:01 +0200382 atomic_dec(&mm->context.flush_count);
383 return old;
384}
385
386pud_t pudp_xchg_direct(struct mm_struct *mm, unsigned long addr,
387 pud_t *pudp, pud_t new)
388{
389 pud_t old;
390
391 preempt_disable();
392 old = pudp_flush_direct(mm, addr, pudp);
393 *pudp = new;
394 preempt_enable();
395 return old;
396}
397EXPORT_SYMBOL(pudp_xchg_direct);
398
Gerald Schaefer75077af2012-10-08 16:30:15 -0700399#ifdef CONFIG_TRANSPARENT_HUGEPAGE
Aneesh Kumar K.V6b0b50b2013-06-05 17:14:02 -0700400void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
401 pgtable_t pgtable)
Gerald Schaefer9501d092012-10-08 16:30:18 -0700402{
403 struct list_head *lh = (struct list_head *) pgtable;
404
Martin Schwidefskyec66ad62014-02-12 14:16:18 +0100405 assert_spin_locked(pmd_lockptr(mm, pmdp));
Gerald Schaefer9501d092012-10-08 16:30:18 -0700406
407 /* FIFO */
Kirill A. Shutemovc389a252013-11-14 14:30:59 -0800408 if (!pmd_huge_pte(mm, pmdp))
Gerald Schaefer9501d092012-10-08 16:30:18 -0700409 INIT_LIST_HEAD(lh);
410 else
Kirill A. Shutemovc389a252013-11-14 14:30:59 -0800411 list_add(lh, (struct list_head *) pmd_huge_pte(mm, pmdp));
412 pmd_huge_pte(mm, pmdp) = pgtable;
Gerald Schaefer9501d092012-10-08 16:30:18 -0700413}
414
Aneesh Kumar K.V6b0b50b2013-06-05 17:14:02 -0700415pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp)
Gerald Schaefer9501d092012-10-08 16:30:18 -0700416{
417 struct list_head *lh;
418 pgtable_t pgtable;
419 pte_t *ptep;
420
Martin Schwidefskyec66ad62014-02-12 14:16:18 +0100421 assert_spin_locked(pmd_lockptr(mm, pmdp));
Gerald Schaefer9501d092012-10-08 16:30:18 -0700422
423 /* FIFO */
Kirill A. Shutemovc389a252013-11-14 14:30:59 -0800424 pgtable = pmd_huge_pte(mm, pmdp);
Gerald Schaefer9501d092012-10-08 16:30:18 -0700425 lh = (struct list_head *) pgtable;
426 if (list_empty(lh))
Kirill A. Shutemovc389a252013-11-14 14:30:59 -0800427 pmd_huge_pte(mm, pmdp) = NULL;
Gerald Schaefer9501d092012-10-08 16:30:18 -0700428 else {
Kirill A. Shutemovc389a252013-11-14 14:30:59 -0800429 pmd_huge_pte(mm, pmdp) = (pgtable_t) lh->next;
Gerald Schaefer9501d092012-10-08 16:30:18 -0700430 list_del(lh);
431 }
432 ptep = (pte_t *) pgtable;
Martin Schwidefskye5098612013-07-23 20:57:57 +0200433 pte_val(*ptep) = _PAGE_INVALID;
Gerald Schaefer9501d092012-10-08 16:30:18 -0700434 ptep++;
Martin Schwidefskye5098612013-07-23 20:57:57 +0200435 pte_val(*ptep) = _PAGE_INVALID;
Gerald Schaefer9501d092012-10-08 16:30:18 -0700436 return pgtable;
437}
Gerald Schaefer75077af2012-10-08 16:30:15 -0700438#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100439
440#ifdef CONFIG_PGSTE
441void ptep_set_pte_at(struct mm_struct *mm, unsigned long addr,
442 pte_t *ptep, pte_t entry)
443{
444 pgste_t pgste;
445
446 /* the mm_has_pgste() check is done in set_pte_at() */
Martin Schwidefskya9809402016-06-06 10:30:45 +0200447 preempt_disable();
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100448 pgste = pgste_get_lock(ptep);
449 pgste_val(pgste) &= ~_PGSTE_GPS_ZERO;
450 pgste_set_key(ptep, pgste, entry, mm);
451 pgste = pgste_set_pte(ptep, pgste, entry);
452 pgste_set_unlock(ptep, pgste);
Martin Schwidefskya9809402016-06-06 10:30:45 +0200453 preempt_enable();
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100454}
455
456void ptep_set_notify(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
457{
458 pgste_t pgste;
459
Martin Schwidefskya9809402016-06-06 10:30:45 +0200460 preempt_disable();
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100461 pgste = pgste_get_lock(ptep);
462 pgste_val(pgste) |= PGSTE_IN_BIT;
463 pgste_set_unlock(ptep, pgste);
Martin Schwidefskya9809402016-06-06 10:30:45 +0200464 preempt_enable();
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100465}
466
Martin Schwidefskyb2d73b22016-03-08 11:54:42 +0100467/**
468 * ptep_force_prot - change access rights of a locked pte
469 * @mm: pointer to the process mm_struct
470 * @addr: virtual address in the guest address space
471 * @ptep: pointer to the page table entry
472 * @prot: indicates guest access rights: PROT_NONE, PROT_READ or PROT_WRITE
Martin Schwidefsky4be130a2016-03-08 12:12:18 +0100473 * @bit: pgste bit to set (e.g. for notification)
Martin Schwidefskyb2d73b22016-03-08 11:54:42 +0100474 *
475 * Returns 0 if the access rights were changed and -EAGAIN if the current
476 * and requested access rights are incompatible.
477 */
478int ptep_force_prot(struct mm_struct *mm, unsigned long addr,
Martin Schwidefsky4be130a2016-03-08 12:12:18 +0100479 pte_t *ptep, int prot, unsigned long bit)
Martin Schwidefskyb2d73b22016-03-08 11:54:42 +0100480{
481 pte_t entry;
482 pgste_t pgste;
483 int pte_i, pte_p;
484
485 pgste = pgste_get_lock(ptep);
486 entry = *ptep;
487 /* Check pte entry after all locks have been acquired */
488 pte_i = pte_val(entry) & _PAGE_INVALID;
489 pte_p = pte_val(entry) & _PAGE_PROTECT;
490 if ((pte_i && (prot != PROT_NONE)) ||
491 (pte_p && (prot & PROT_WRITE))) {
492 pgste_set_unlock(ptep, pgste);
493 return -EAGAIN;
494 }
Martin Schwidefsky4be130a2016-03-08 12:12:18 +0100495 /* Change access rights and set pgste bit */
Martin Schwidefskyb2d73b22016-03-08 11:54:42 +0100496 if (prot == PROT_NONE && !pte_i) {
497 ptep_flush_direct(mm, addr, ptep);
498 pgste = pgste_update_all(entry, pgste, mm);
499 pte_val(entry) |= _PAGE_INVALID;
500 }
501 if (prot == PROT_READ && !pte_p) {
502 ptep_flush_direct(mm, addr, ptep);
503 pte_val(entry) &= ~_PAGE_INVALID;
504 pte_val(entry) |= _PAGE_PROTECT;
505 }
Martin Schwidefsky4be130a2016-03-08 12:12:18 +0100506 pgste_val(pgste) |= bit;
Martin Schwidefskyb2d73b22016-03-08 11:54:42 +0100507 pgste = pgste_set_pte(ptep, pgste, entry);
508 pgste_set_unlock(ptep, pgste);
509 return 0;
510}
511
Martin Schwidefsky4be130a2016-03-08 12:12:18 +0100512int ptep_shadow_pte(struct mm_struct *mm, unsigned long saddr,
David Hildenbranda9d23e72016-03-08 12:21:41 +0100513 pte_t *sptep, pte_t *tptep, pte_t pte)
Martin Schwidefsky4be130a2016-03-08 12:12:18 +0100514{
515 pgste_t spgste, tpgste;
516 pte_t spte, tpte;
517 int rc = -EAGAIN;
518
David Hildenbranda9d23e72016-03-08 12:21:41 +0100519 if (!(pte_val(*tptep) & _PAGE_INVALID))
520 return 0; /* already shadowed */
Martin Schwidefsky4be130a2016-03-08 12:12:18 +0100521 spgste = pgste_get_lock(sptep);
522 spte = *sptep;
523 if (!(pte_val(spte) & _PAGE_INVALID) &&
David Hildenbranda9d23e72016-03-08 12:21:41 +0100524 !((pte_val(spte) & _PAGE_PROTECT) &&
525 !(pte_val(pte) & _PAGE_PROTECT))) {
Martin Schwidefsky4be130a2016-03-08 12:12:18 +0100526 pgste_val(spgste) |= PGSTE_VSIE_BIT;
527 tpgste = pgste_get_lock(tptep);
528 pte_val(tpte) = (pte_val(spte) & PAGE_MASK) |
David Hildenbranda9d23e72016-03-08 12:21:41 +0100529 (pte_val(pte) & _PAGE_PROTECT);
Martin Schwidefsky4be130a2016-03-08 12:12:18 +0100530 /* don't touch the storage key - it belongs to parent pgste */
531 tpgste = pgste_set_pte(tptep, tpgste, tpte);
532 pgste_set_unlock(tptep, tpgste);
David Hildenbranda9d23e72016-03-08 12:21:41 +0100533 rc = 1;
Martin Schwidefsky4be130a2016-03-08 12:12:18 +0100534 }
535 pgste_set_unlock(sptep, spgste);
536 return rc;
537}
538
539void ptep_unshadow_pte(struct mm_struct *mm, unsigned long saddr, pte_t *ptep)
540{
541 pgste_t pgste;
542
543 pgste = pgste_get_lock(ptep);
544 /* notifier is called by the caller */
545 ptep_flush_direct(mm, saddr, ptep);
546 /* don't touch the storage key - it belongs to parent pgste */
547 pgste = pgste_set_pte(ptep, pgste, __pte(_PAGE_INVALID));
548 pgste_set_unlock(ptep, pgste);
549}
550
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100551static void ptep_zap_swap_entry(struct mm_struct *mm, swp_entry_t entry)
552{
553 if (!non_swap_entry(entry))
554 dec_mm_counter(mm, MM_SWAPENTS);
555 else if (is_migration_entry(entry)) {
556 struct page *page = migration_entry_to_page(entry);
557
558 dec_mm_counter(mm, mm_counter(page));
559 }
560 free_swap_and_cache(entry);
561}
562
563void ptep_zap_unused(struct mm_struct *mm, unsigned long addr,
564 pte_t *ptep, int reset)
565{
566 unsigned long pgstev;
567 pgste_t pgste;
568 pte_t pte;
569
570 /* Zap unused and logically-zero pages */
Martin Schwidefskya9809402016-06-06 10:30:45 +0200571 preempt_disable();
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100572 pgste = pgste_get_lock(ptep);
573 pgstev = pgste_val(pgste);
574 pte = *ptep;
Christian Borntraeger1c343f72016-06-13 13:14:56 +0200575 if (!reset && pte_swap(pte) &&
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100576 ((pgstev & _PGSTE_GPS_USAGE_MASK) == _PGSTE_GPS_USAGE_UNUSED ||
577 (pgstev & _PGSTE_GPS_ZERO))) {
578 ptep_zap_swap_entry(mm, pte_to_swp_entry(pte));
579 pte_clear(mm, addr, ptep);
580 }
581 if (reset)
582 pgste_val(pgste) &= ~_PGSTE_GPS_USAGE_MASK;
583 pgste_set_unlock(ptep, pgste);
Martin Schwidefskya9809402016-06-06 10:30:45 +0200584 preempt_enable();
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100585}
586
587void ptep_zap_key(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
588{
589 unsigned long ptev;
590 pgste_t pgste;
591
592 /* Clear storage key */
Martin Schwidefskya9809402016-06-06 10:30:45 +0200593 preempt_disable();
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100594 pgste = pgste_get_lock(ptep);
595 pgste_val(pgste) &= ~(PGSTE_ACC_BITS | PGSTE_FP_BIT |
596 PGSTE_GR_BIT | PGSTE_GC_BIT);
597 ptev = pte_val(*ptep);
598 if (!(ptev & _PAGE_INVALID) && (ptev & _PAGE_WRITE))
599 page_set_storage_key(ptev & PAGE_MASK, PAGE_DEFAULT_KEY, 1);
600 pgste_set_unlock(ptep, pgste);
Martin Schwidefskya9809402016-06-06 10:30:45 +0200601 preempt_enable();
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100602}
603
604/*
605 * Test and reset if a guest page is dirty
606 */
607bool test_and_clear_guest_dirty(struct mm_struct *mm, unsigned long addr)
608{
609 spinlock_t *ptl;
610 pgste_t pgste;
611 pte_t *ptep;
612 pte_t pte;
613 bool dirty;
614
615 ptep = get_locked_pte(mm, addr, &ptl);
616 if (unlikely(!ptep))
617 return false;
618
619 pgste = pgste_get_lock(ptep);
620 dirty = !!(pgste_val(pgste) & PGSTE_UC_BIT);
621 pgste_val(pgste) &= ~PGSTE_UC_BIT;
622 pte = *ptep;
623 if (dirty && (pte_val(pte) & _PAGE_PRESENT)) {
Martin Schwidefskyb2d73b22016-03-08 11:54:42 +0100624 pgste = pgste_pte_notify(mm, addr, ptep, pgste);
Martin Schwidefsky34eeaf32016-06-14 12:38:40 +0200625 __ptep_ipte(addr, ptep, IPTE_GLOBAL);
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100626 if (MACHINE_HAS_ESOP || !(pte_val(pte) & _PAGE_WRITE))
627 pte_val(pte) |= _PAGE_PROTECT;
628 else
629 pte_val(pte) |= _PAGE_INVALID;
630 *ptep = pte;
631 }
632 pgste_set_unlock(ptep, pgste);
633
634 spin_unlock(ptl);
635 return dirty;
636}
637EXPORT_SYMBOL_GPL(test_and_clear_guest_dirty);
638
639int set_guest_storage_key(struct mm_struct *mm, unsigned long addr,
640 unsigned char key, bool nq)
641{
642 unsigned long keyul;
643 spinlock_t *ptl;
644 pgste_t old, new;
645 pte_t *ptep;
646
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100647 ptep = get_locked_pte(mm, addr, &ptl);
Martin Schwidefskyd3ed1ce2016-03-08 11:53:35 +0100648 if (unlikely(!ptep))
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100649 return -EFAULT;
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100650
651 new = old = pgste_get_lock(ptep);
652 pgste_val(new) &= ~(PGSTE_GR_BIT | PGSTE_GC_BIT |
653 PGSTE_ACC_BITS | PGSTE_FP_BIT);
654 keyul = (unsigned long) key;
655 pgste_val(new) |= (keyul & (_PAGE_CHANGED | _PAGE_REFERENCED)) << 48;
656 pgste_val(new) |= (keyul & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56;
657 if (!(pte_val(*ptep) & _PAGE_INVALID)) {
658 unsigned long address, bits, skey;
659
660 address = pte_val(*ptep) & PAGE_MASK;
661 skey = (unsigned long) page_get_storage_key(address);
662 bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED);
663 skey = key & (_PAGE_ACC_BITS | _PAGE_FP_BIT);
664 /* Set storage key ACC and FP */
665 page_set_storage_key(address, skey, !nq);
666 /* Merge host changed & referenced into pgste */
667 pgste_val(new) |= bits << 52;
668 }
669 /* changing the guest storage key is considered a change of the page */
670 if ((pgste_val(new) ^ pgste_val(old)) &
671 (PGSTE_ACC_BITS | PGSTE_FP_BIT | PGSTE_GR_BIT | PGSTE_GC_BIT))
672 pgste_val(new) |= PGSTE_UC_BIT;
673
674 pgste_set_unlock(ptep, new);
675 pte_unmap_unlock(ptep, ptl);
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100676 return 0;
677}
678EXPORT_SYMBOL(set_guest_storage_key);
679
David Hildenbrand1824c722016-05-10 09:43:11 +0200680/**
681 * Conditionally set a guest storage key (handling csske).
682 * oldkey will be updated when either mr or mc is set and a pointer is given.
683 *
684 * Returns 0 if a guests storage key update wasn't necessary, 1 if the guest
685 * storage key was updated and -EFAULT on access errors.
686 */
687int cond_set_guest_storage_key(struct mm_struct *mm, unsigned long addr,
688 unsigned char key, unsigned char *oldkey,
689 bool nq, bool mr, bool mc)
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100690{
David Hildenbrand1824c722016-05-10 09:43:11 +0200691 unsigned char tmp, mask = _PAGE_ACC_BITS | _PAGE_FP_BIT;
692 int rc;
693
694 /* we can drop the pgste lock between getting and setting the key */
695 if (mr | mc) {
696 rc = get_guest_storage_key(current->mm, addr, &tmp);
697 if (rc)
698 return rc;
699 if (oldkey)
700 *oldkey = tmp;
701 if (!mr)
702 mask |= _PAGE_REFERENCED;
703 if (!mc)
704 mask |= _PAGE_CHANGED;
705 if (!((tmp ^ key) & mask))
706 return 0;
707 }
708 rc = set_guest_storage_key(current->mm, addr, key, nq);
709 return rc < 0 ? rc : 1;
710}
711EXPORT_SYMBOL(cond_set_guest_storage_key);
712
David Hildenbranda7e19ab2016-05-10 09:50:21 +0200713/**
714 * Reset a guest reference bit (rrbe), returning the reference and changed bit.
715 *
716 * Returns < 0 in case of error, otherwise the cc to be reported to the guest.
717 */
718int reset_guest_reference_bit(struct mm_struct *mm, unsigned long addr)
719{
720 spinlock_t *ptl;
721 pgste_t old, new;
722 pte_t *ptep;
723 int cc = 0;
724
725 ptep = get_locked_pte(mm, addr, &ptl);
726 if (unlikely(!ptep))
727 return -EFAULT;
728
729 new = old = pgste_get_lock(ptep);
730 /* Reset guest reference bit only */
731 pgste_val(new) &= ~PGSTE_GR_BIT;
732
733 if (!(pte_val(*ptep) & _PAGE_INVALID)) {
734 cc = page_reset_referenced(pte_val(*ptep) & PAGE_MASK);
735 /* Merge real referenced bit into host-set */
736 pgste_val(new) |= ((unsigned long) cc << 53) & PGSTE_HR_BIT;
737 }
738 /* Reflect guest's logical view, not physical */
739 cc |= (pgste_val(old) & (PGSTE_GR_BIT | PGSTE_GC_BIT)) >> 49;
740 /* Changing the guest storage key is considered a change of the page */
741 if ((pgste_val(new) ^ pgste_val(old)) & PGSTE_GR_BIT)
742 pgste_val(new) |= PGSTE_UC_BIT;
743
744 pgste_set_unlock(ptep, new);
745 pte_unmap_unlock(ptep, ptl);
746 return 0;
747}
748EXPORT_SYMBOL(reset_guest_reference_bit);
749
David Hildenbrand154c8c12016-05-09 11:22:34 +0200750int get_guest_storage_key(struct mm_struct *mm, unsigned long addr,
751 unsigned char *key)
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100752{
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100753 spinlock_t *ptl;
754 pgste_t pgste;
755 pte_t *ptep;
756
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100757 ptep = get_locked_pte(mm, addr, &ptl);
Martin Schwidefskyd3ed1ce2016-03-08 11:53:35 +0100758 if (unlikely(!ptep))
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100759 return -EFAULT;
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100760
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100761 pgste = pgste_get_lock(ptep);
David Hildenbrand154c8c12016-05-09 11:22:34 +0200762 *key = (pgste_val(pgste) & (PGSTE_ACC_BITS | PGSTE_FP_BIT)) >> 56;
David Hildenbrand8d6037a2016-05-09 11:15:32 +0200763 if (!(pte_val(*ptep) & _PAGE_INVALID))
David Hildenbrand154c8c12016-05-09 11:22:34 +0200764 *key = page_get_storage_key(pte_val(*ptep) & PAGE_MASK);
David Hildenbrand8d6037a2016-05-09 11:15:32 +0200765 /* Reflect guest's logical view, not physical */
David Hildenbrand154c8c12016-05-09 11:22:34 +0200766 *key |= (pgste_val(pgste) & (PGSTE_GR_BIT | PGSTE_GC_BIT)) >> 48;
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100767 pgste_set_unlock(ptep, pgste);
768 pte_unmap_unlock(ptep, ptl);
David Hildenbrand154c8c12016-05-09 11:22:34 +0200769 return 0;
Martin Schwidefsky1e133ab2016-03-08 11:49:57 +0100770}
771EXPORT_SYMBOL(get_guest_storage_key);
772#endif