18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2007, 2011 48c2ecf20Sopenharmony_ci * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/sched.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/gfp.h> 118c2ecf20Sopenharmony_ci#include <linux/mm.h> 128c2ecf20Sopenharmony_ci#include <linux/swap.h> 138c2ecf20Sopenharmony_ci#include <linux/smp.h> 148c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 158c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/swapops.h> 188c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 198c2ecf20Sopenharmony_ci#include <linux/ksm.h> 208c2ecf20Sopenharmony_ci#include <linux/mman.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <asm/tlb.h> 238c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 248c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 258c2ecf20Sopenharmony_ci#include <asm/page-states.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cipgprot_t pgprot_writecombine(pgprot_t prot) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci /* 308c2ecf20Sopenharmony_ci * mio_wb_bit_mask may be set on a different CPU, but it is only set 318c2ecf20Sopenharmony_ci * once at init and only read afterwards. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci return __pgprot(pgprot_val(prot) | mio_wb_bit_mask); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pgprot_writecombine); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cipgprot_t pgprot_writethrough(pgprot_t prot) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci /* 408c2ecf20Sopenharmony_ci * mio_wb_bit_mask may be set on a different CPU, but it is only set 418c2ecf20Sopenharmony_ci * once at init and only read afterwards. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci return __pgprot(pgprot_val(prot) & ~mio_wb_bit_mask); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pgprot_writethrough); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic inline void ptep_ipte_local(struct mm_struct *mm, unsigned long addr, 488c2ecf20Sopenharmony_ci pte_t *ptep, int nodat) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci unsigned long opt, asce; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (MACHINE_HAS_TLB_GUEST) { 538c2ecf20Sopenharmony_ci opt = 0; 548c2ecf20Sopenharmony_ci asce = READ_ONCE(mm->context.gmap_asce); 558c2ecf20Sopenharmony_ci if (asce == 0UL || nodat) 568c2ecf20Sopenharmony_ci opt |= IPTE_NODAT; 578c2ecf20Sopenharmony_ci if (asce != -1UL) { 588c2ecf20Sopenharmony_ci asce = asce ? : mm->context.asce; 598c2ecf20Sopenharmony_ci opt |= IPTE_GUEST_ASCE; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci __ptep_ipte(addr, ptep, opt, asce, IPTE_LOCAL); 628c2ecf20Sopenharmony_ci } else { 638c2ecf20Sopenharmony_ci __ptep_ipte(addr, ptep, 0, 0, IPTE_LOCAL); 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic inline void ptep_ipte_global(struct mm_struct *mm, unsigned long addr, 688c2ecf20Sopenharmony_ci pte_t *ptep, int nodat) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci unsigned long opt, asce; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (MACHINE_HAS_TLB_GUEST) { 738c2ecf20Sopenharmony_ci opt = 0; 748c2ecf20Sopenharmony_ci asce = READ_ONCE(mm->context.gmap_asce); 758c2ecf20Sopenharmony_ci if (asce == 0UL || nodat) 768c2ecf20Sopenharmony_ci opt |= IPTE_NODAT; 778c2ecf20Sopenharmony_ci if (asce != -1UL) { 788c2ecf20Sopenharmony_ci asce = asce ? : mm->context.asce; 798c2ecf20Sopenharmony_ci opt |= IPTE_GUEST_ASCE; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci __ptep_ipte(addr, ptep, opt, asce, IPTE_GLOBAL); 828c2ecf20Sopenharmony_ci } else { 838c2ecf20Sopenharmony_ci __ptep_ipte(addr, ptep, 0, 0, IPTE_GLOBAL); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic inline pte_t ptep_flush_direct(struct mm_struct *mm, 888c2ecf20Sopenharmony_ci unsigned long addr, pte_t *ptep, 898c2ecf20Sopenharmony_ci int nodat) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci pte_t old; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci old = *ptep; 948c2ecf20Sopenharmony_ci if (unlikely(pte_val(old) & _PAGE_INVALID)) 958c2ecf20Sopenharmony_ci return old; 968c2ecf20Sopenharmony_ci atomic_inc(&mm->context.flush_count); 978c2ecf20Sopenharmony_ci if (MACHINE_HAS_TLB_LC && 988c2ecf20Sopenharmony_ci cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id()))) 998c2ecf20Sopenharmony_ci ptep_ipte_local(mm, addr, ptep, nodat); 1008c2ecf20Sopenharmony_ci else 1018c2ecf20Sopenharmony_ci ptep_ipte_global(mm, addr, ptep, nodat); 1028c2ecf20Sopenharmony_ci atomic_dec(&mm->context.flush_count); 1038c2ecf20Sopenharmony_ci return old; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic inline pte_t ptep_flush_lazy(struct mm_struct *mm, 1078c2ecf20Sopenharmony_ci unsigned long addr, pte_t *ptep, 1088c2ecf20Sopenharmony_ci int nodat) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci pte_t old; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci old = *ptep; 1138c2ecf20Sopenharmony_ci if (unlikely(pte_val(old) & _PAGE_INVALID)) 1148c2ecf20Sopenharmony_ci return old; 1158c2ecf20Sopenharmony_ci atomic_inc(&mm->context.flush_count); 1168c2ecf20Sopenharmony_ci if (cpumask_equal(&mm->context.cpu_attach_mask, 1178c2ecf20Sopenharmony_ci cpumask_of(smp_processor_id()))) { 1188c2ecf20Sopenharmony_ci pte_val(*ptep) |= _PAGE_INVALID; 1198c2ecf20Sopenharmony_ci mm->context.flush_mm = 1; 1208c2ecf20Sopenharmony_ci } else 1218c2ecf20Sopenharmony_ci ptep_ipte_global(mm, addr, ptep, nodat); 1228c2ecf20Sopenharmony_ci atomic_dec(&mm->context.flush_count); 1238c2ecf20Sopenharmony_ci return old; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic inline pgste_t pgste_get_lock(pte_t *ptep) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci unsigned long new = 0; 1298c2ecf20Sopenharmony_ci#ifdef CONFIG_PGSTE 1308c2ecf20Sopenharmony_ci unsigned long old; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci asm( 1338c2ecf20Sopenharmony_ci " lg %0,%2\n" 1348c2ecf20Sopenharmony_ci "0: lgr %1,%0\n" 1358c2ecf20Sopenharmony_ci " nihh %0,0xff7f\n" /* clear PCL bit in old */ 1368c2ecf20Sopenharmony_ci " oihh %1,0x0080\n" /* set PCL bit in new */ 1378c2ecf20Sopenharmony_ci " csg %0,%1,%2\n" 1388c2ecf20Sopenharmony_ci " jl 0b\n" 1398c2ecf20Sopenharmony_ci : "=&d" (old), "=&d" (new), "=Q" (ptep[PTRS_PER_PTE]) 1408c2ecf20Sopenharmony_ci : "Q" (ptep[PTRS_PER_PTE]) : "cc", "memory"); 1418c2ecf20Sopenharmony_ci#endif 1428c2ecf20Sopenharmony_ci return __pgste(new); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic inline void pgste_set_unlock(pte_t *ptep, pgste_t pgste) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci#ifdef CONFIG_PGSTE 1488c2ecf20Sopenharmony_ci asm( 1498c2ecf20Sopenharmony_ci " nihh %1,0xff7f\n" /* clear PCL bit */ 1508c2ecf20Sopenharmony_ci " stg %1,%0\n" 1518c2ecf20Sopenharmony_ci : "=Q" (ptep[PTRS_PER_PTE]) 1528c2ecf20Sopenharmony_ci : "d" (pgste_val(pgste)), "Q" (ptep[PTRS_PER_PTE]) 1538c2ecf20Sopenharmony_ci : "cc", "memory"); 1548c2ecf20Sopenharmony_ci#endif 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic inline pgste_t pgste_get(pte_t *ptep) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci unsigned long pgste = 0; 1608c2ecf20Sopenharmony_ci#ifdef CONFIG_PGSTE 1618c2ecf20Sopenharmony_ci pgste = *(unsigned long *)(ptep + PTRS_PER_PTE); 1628c2ecf20Sopenharmony_ci#endif 1638c2ecf20Sopenharmony_ci return __pgste(pgste); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic inline void pgste_set(pte_t *ptep, pgste_t pgste) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci#ifdef CONFIG_PGSTE 1698c2ecf20Sopenharmony_ci *(pgste_t *)(ptep + PTRS_PER_PTE) = pgste; 1708c2ecf20Sopenharmony_ci#endif 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic inline pgste_t pgste_update_all(pte_t pte, pgste_t pgste, 1748c2ecf20Sopenharmony_ci struct mm_struct *mm) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci#ifdef CONFIG_PGSTE 1778c2ecf20Sopenharmony_ci unsigned long address, bits, skey; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (!mm_uses_skeys(mm) || pte_val(pte) & _PAGE_INVALID) 1808c2ecf20Sopenharmony_ci return pgste; 1818c2ecf20Sopenharmony_ci address = pte_val(pte) & PAGE_MASK; 1828c2ecf20Sopenharmony_ci skey = (unsigned long) page_get_storage_key(address); 1838c2ecf20Sopenharmony_ci bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED); 1848c2ecf20Sopenharmony_ci /* Transfer page changed & referenced bit to guest bits in pgste */ 1858c2ecf20Sopenharmony_ci pgste_val(pgste) |= bits << 48; /* GR bit & GC bit */ 1868c2ecf20Sopenharmony_ci /* Copy page access key and fetch protection bit to pgste */ 1878c2ecf20Sopenharmony_ci pgste_val(pgste) &= ~(PGSTE_ACC_BITS | PGSTE_FP_BIT); 1888c2ecf20Sopenharmony_ci pgste_val(pgste) |= (skey & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56; 1898c2ecf20Sopenharmony_ci#endif 1908c2ecf20Sopenharmony_ci return pgste; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry, 1958c2ecf20Sopenharmony_ci struct mm_struct *mm) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci#ifdef CONFIG_PGSTE 1988c2ecf20Sopenharmony_ci unsigned long address; 1998c2ecf20Sopenharmony_ci unsigned long nkey; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (!mm_uses_skeys(mm) || pte_val(entry) & _PAGE_INVALID) 2028c2ecf20Sopenharmony_ci return; 2038c2ecf20Sopenharmony_ci VM_BUG_ON(!(pte_val(*ptep) & _PAGE_INVALID)); 2048c2ecf20Sopenharmony_ci address = pte_val(entry) & PAGE_MASK; 2058c2ecf20Sopenharmony_ci /* 2068c2ecf20Sopenharmony_ci * Set page access key and fetch protection bit from pgste. 2078c2ecf20Sopenharmony_ci * The guest C/R information is still in the PGSTE, set real 2088c2ecf20Sopenharmony_ci * key C/R to 0. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci nkey = (pgste_val(pgste) & (PGSTE_ACC_BITS | PGSTE_FP_BIT)) >> 56; 2118c2ecf20Sopenharmony_ci nkey |= (pgste_val(pgste) & (PGSTE_GR_BIT | PGSTE_GC_BIT)) >> 48; 2128c2ecf20Sopenharmony_ci page_set_storage_key(address, nkey, 0); 2138c2ecf20Sopenharmony_ci#endif 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic inline pgste_t pgste_set_pte(pte_t *ptep, pgste_t pgste, pte_t entry) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci#ifdef CONFIG_PGSTE 2198c2ecf20Sopenharmony_ci if ((pte_val(entry) & _PAGE_PRESENT) && 2208c2ecf20Sopenharmony_ci (pte_val(entry) & _PAGE_WRITE) && 2218c2ecf20Sopenharmony_ci !(pte_val(entry) & _PAGE_INVALID)) { 2228c2ecf20Sopenharmony_ci if (!MACHINE_HAS_ESOP) { 2238c2ecf20Sopenharmony_ci /* 2248c2ecf20Sopenharmony_ci * Without enhanced suppression-on-protection force 2258c2ecf20Sopenharmony_ci * the dirty bit on for all writable ptes. 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_ci pte_val(entry) |= _PAGE_DIRTY; 2288c2ecf20Sopenharmony_ci pte_val(entry) &= ~_PAGE_PROTECT; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci if (!(pte_val(entry) & _PAGE_PROTECT)) 2318c2ecf20Sopenharmony_ci /* This pte allows write access, set user-dirty */ 2328c2ecf20Sopenharmony_ci pgste_val(pgste) |= PGSTE_UC_BIT; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci#endif 2358c2ecf20Sopenharmony_ci *ptep = entry; 2368c2ecf20Sopenharmony_ci return pgste; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic inline pgste_t pgste_pte_notify(struct mm_struct *mm, 2408c2ecf20Sopenharmony_ci unsigned long addr, 2418c2ecf20Sopenharmony_ci pte_t *ptep, pgste_t pgste) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci#ifdef CONFIG_PGSTE 2448c2ecf20Sopenharmony_ci unsigned long bits; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci bits = pgste_val(pgste) & (PGSTE_IN_BIT | PGSTE_VSIE_BIT); 2478c2ecf20Sopenharmony_ci if (bits) { 2488c2ecf20Sopenharmony_ci pgste_val(pgste) ^= bits; 2498c2ecf20Sopenharmony_ci ptep_notify(mm, addr, ptep, bits); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci#endif 2528c2ecf20Sopenharmony_ci return pgste; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic inline pgste_t ptep_xchg_start(struct mm_struct *mm, 2568c2ecf20Sopenharmony_ci unsigned long addr, pte_t *ptep) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci pgste_t pgste = __pgste(0); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (mm_has_pgste(mm)) { 2618c2ecf20Sopenharmony_ci pgste = pgste_get_lock(ptep); 2628c2ecf20Sopenharmony_ci pgste = pgste_pte_notify(mm, addr, ptep, pgste); 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci return pgste; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic inline pte_t ptep_xchg_commit(struct mm_struct *mm, 2688c2ecf20Sopenharmony_ci unsigned long addr, pte_t *ptep, 2698c2ecf20Sopenharmony_ci pgste_t pgste, pte_t old, pte_t new) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci if (mm_has_pgste(mm)) { 2728c2ecf20Sopenharmony_ci if (pte_val(old) & _PAGE_INVALID) 2738c2ecf20Sopenharmony_ci pgste_set_key(ptep, pgste, new, mm); 2748c2ecf20Sopenharmony_ci if (pte_val(new) & _PAGE_INVALID) { 2758c2ecf20Sopenharmony_ci pgste = pgste_update_all(old, pgste, mm); 2768c2ecf20Sopenharmony_ci if ((pgste_val(pgste) & _PGSTE_GPS_USAGE_MASK) == 2778c2ecf20Sopenharmony_ci _PGSTE_GPS_USAGE_UNUSED) 2788c2ecf20Sopenharmony_ci pte_val(old) |= _PAGE_UNUSED; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci pgste = pgste_set_pte(ptep, pgste, new); 2818c2ecf20Sopenharmony_ci pgste_set_unlock(ptep, pgste); 2828c2ecf20Sopenharmony_ci } else { 2838c2ecf20Sopenharmony_ci *ptep = new; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci return old; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cipte_t ptep_xchg_direct(struct mm_struct *mm, unsigned long addr, 2898c2ecf20Sopenharmony_ci pte_t *ptep, pte_t new) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci pgste_t pgste; 2928c2ecf20Sopenharmony_ci pte_t old; 2938c2ecf20Sopenharmony_ci int nodat; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci preempt_disable(); 2968c2ecf20Sopenharmony_ci pgste = ptep_xchg_start(mm, addr, ptep); 2978c2ecf20Sopenharmony_ci nodat = !!(pgste_val(pgste) & _PGSTE_GPS_NODAT); 2988c2ecf20Sopenharmony_ci old = ptep_flush_direct(mm, addr, ptep, nodat); 2998c2ecf20Sopenharmony_ci old = ptep_xchg_commit(mm, addr, ptep, pgste, old, new); 3008c2ecf20Sopenharmony_ci preempt_enable(); 3018c2ecf20Sopenharmony_ci return old; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ptep_xchg_direct); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cipte_t ptep_xchg_lazy(struct mm_struct *mm, unsigned long addr, 3068c2ecf20Sopenharmony_ci pte_t *ptep, pte_t new) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci pgste_t pgste; 3098c2ecf20Sopenharmony_ci pte_t old; 3108c2ecf20Sopenharmony_ci int nodat; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci preempt_disable(); 3138c2ecf20Sopenharmony_ci pgste = ptep_xchg_start(mm, addr, ptep); 3148c2ecf20Sopenharmony_ci nodat = !!(pgste_val(pgste) & _PGSTE_GPS_NODAT); 3158c2ecf20Sopenharmony_ci old = ptep_flush_lazy(mm, addr, ptep, nodat); 3168c2ecf20Sopenharmony_ci old = ptep_xchg_commit(mm, addr, ptep, pgste, old, new); 3178c2ecf20Sopenharmony_ci preempt_enable(); 3188c2ecf20Sopenharmony_ci return old; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ptep_xchg_lazy); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cipte_t ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr, 3238c2ecf20Sopenharmony_ci pte_t *ptep) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci pgste_t pgste; 3268c2ecf20Sopenharmony_ci pte_t old; 3278c2ecf20Sopenharmony_ci int nodat; 3288c2ecf20Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci preempt_disable(); 3318c2ecf20Sopenharmony_ci pgste = ptep_xchg_start(mm, addr, ptep); 3328c2ecf20Sopenharmony_ci nodat = !!(pgste_val(pgste) & _PGSTE_GPS_NODAT); 3338c2ecf20Sopenharmony_ci old = ptep_flush_lazy(mm, addr, ptep, nodat); 3348c2ecf20Sopenharmony_ci if (mm_has_pgste(mm)) { 3358c2ecf20Sopenharmony_ci pgste = pgste_update_all(old, pgste, mm); 3368c2ecf20Sopenharmony_ci pgste_set(ptep, pgste); 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci return old; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_civoid ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, 3428c2ecf20Sopenharmony_ci pte_t *ptep, pte_t old_pte, pte_t pte) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci pgste_t pgste; 3458c2ecf20Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (!MACHINE_HAS_NX) 3488c2ecf20Sopenharmony_ci pte_val(pte) &= ~_PAGE_NOEXEC; 3498c2ecf20Sopenharmony_ci if (mm_has_pgste(mm)) { 3508c2ecf20Sopenharmony_ci pgste = pgste_get(ptep); 3518c2ecf20Sopenharmony_ci pgste_set_key(ptep, pgste, pte, mm); 3528c2ecf20Sopenharmony_ci pgste = pgste_set_pte(ptep, pgste, pte); 3538c2ecf20Sopenharmony_ci pgste_set_unlock(ptep, pgste); 3548c2ecf20Sopenharmony_ci } else { 3558c2ecf20Sopenharmony_ci *ptep = pte; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci preempt_enable(); 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic inline void pmdp_idte_local(struct mm_struct *mm, 3618c2ecf20Sopenharmony_ci unsigned long addr, pmd_t *pmdp) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci if (MACHINE_HAS_TLB_GUEST) 3648c2ecf20Sopenharmony_ci __pmdp_idte(addr, pmdp, IDTE_NODAT | IDTE_GUEST_ASCE, 3658c2ecf20Sopenharmony_ci mm->context.asce, IDTE_LOCAL); 3668c2ecf20Sopenharmony_ci else 3678c2ecf20Sopenharmony_ci __pmdp_idte(addr, pmdp, 0, 0, IDTE_LOCAL); 3688c2ecf20Sopenharmony_ci if (mm_has_pgste(mm) && mm->context.allow_gmap_hpage_1m) 3698c2ecf20Sopenharmony_ci gmap_pmdp_idte_local(mm, addr); 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic inline void pmdp_idte_global(struct mm_struct *mm, 3738c2ecf20Sopenharmony_ci unsigned long addr, pmd_t *pmdp) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci if (MACHINE_HAS_TLB_GUEST) { 3768c2ecf20Sopenharmony_ci __pmdp_idte(addr, pmdp, IDTE_NODAT | IDTE_GUEST_ASCE, 3778c2ecf20Sopenharmony_ci mm->context.asce, IDTE_GLOBAL); 3788c2ecf20Sopenharmony_ci if (mm_has_pgste(mm) && mm->context.allow_gmap_hpage_1m) 3798c2ecf20Sopenharmony_ci gmap_pmdp_idte_global(mm, addr); 3808c2ecf20Sopenharmony_ci } else if (MACHINE_HAS_IDTE) { 3818c2ecf20Sopenharmony_ci __pmdp_idte(addr, pmdp, 0, 0, IDTE_GLOBAL); 3828c2ecf20Sopenharmony_ci if (mm_has_pgste(mm) && mm->context.allow_gmap_hpage_1m) 3838c2ecf20Sopenharmony_ci gmap_pmdp_idte_global(mm, addr); 3848c2ecf20Sopenharmony_ci } else { 3858c2ecf20Sopenharmony_ci __pmdp_csp(pmdp); 3868c2ecf20Sopenharmony_ci if (mm_has_pgste(mm) && mm->context.allow_gmap_hpage_1m) 3878c2ecf20Sopenharmony_ci gmap_pmdp_csp(mm, addr); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic inline pmd_t pmdp_flush_direct(struct mm_struct *mm, 3928c2ecf20Sopenharmony_ci unsigned long addr, pmd_t *pmdp) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci pmd_t old; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci old = *pmdp; 3978c2ecf20Sopenharmony_ci if (pmd_val(old) & _SEGMENT_ENTRY_INVALID) 3988c2ecf20Sopenharmony_ci return old; 3998c2ecf20Sopenharmony_ci atomic_inc(&mm->context.flush_count); 4008c2ecf20Sopenharmony_ci if (MACHINE_HAS_TLB_LC && 4018c2ecf20Sopenharmony_ci cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id()))) 4028c2ecf20Sopenharmony_ci pmdp_idte_local(mm, addr, pmdp); 4038c2ecf20Sopenharmony_ci else 4048c2ecf20Sopenharmony_ci pmdp_idte_global(mm, addr, pmdp); 4058c2ecf20Sopenharmony_ci atomic_dec(&mm->context.flush_count); 4068c2ecf20Sopenharmony_ci return old; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic inline pmd_t pmdp_flush_lazy(struct mm_struct *mm, 4108c2ecf20Sopenharmony_ci unsigned long addr, pmd_t *pmdp) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci pmd_t old; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci old = *pmdp; 4158c2ecf20Sopenharmony_ci if (pmd_val(old) & _SEGMENT_ENTRY_INVALID) 4168c2ecf20Sopenharmony_ci return old; 4178c2ecf20Sopenharmony_ci atomic_inc(&mm->context.flush_count); 4188c2ecf20Sopenharmony_ci if (cpumask_equal(&mm->context.cpu_attach_mask, 4198c2ecf20Sopenharmony_ci cpumask_of(smp_processor_id()))) { 4208c2ecf20Sopenharmony_ci pmd_val(*pmdp) |= _SEGMENT_ENTRY_INVALID; 4218c2ecf20Sopenharmony_ci mm->context.flush_mm = 1; 4228c2ecf20Sopenharmony_ci if (mm_has_pgste(mm)) 4238c2ecf20Sopenharmony_ci gmap_pmdp_invalidate(mm, addr); 4248c2ecf20Sopenharmony_ci } else { 4258c2ecf20Sopenharmony_ci pmdp_idte_global(mm, addr, pmdp); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci atomic_dec(&mm->context.flush_count); 4288c2ecf20Sopenharmony_ci return old; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci#ifdef CONFIG_PGSTE 4328c2ecf20Sopenharmony_cistatic pmd_t *pmd_alloc_map(struct mm_struct *mm, unsigned long addr) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci pgd_t *pgd; 4358c2ecf20Sopenharmony_ci p4d_t *p4d; 4368c2ecf20Sopenharmony_ci pud_t *pud; 4378c2ecf20Sopenharmony_ci pmd_t *pmd; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci pgd = pgd_offset(mm, addr); 4408c2ecf20Sopenharmony_ci p4d = p4d_alloc(mm, pgd, addr); 4418c2ecf20Sopenharmony_ci if (!p4d) 4428c2ecf20Sopenharmony_ci return NULL; 4438c2ecf20Sopenharmony_ci pud = pud_alloc(mm, p4d, addr); 4448c2ecf20Sopenharmony_ci if (!pud) 4458c2ecf20Sopenharmony_ci return NULL; 4468c2ecf20Sopenharmony_ci pmd = pmd_alloc(mm, pud, addr); 4478c2ecf20Sopenharmony_ci return pmd; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci#endif 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cipmd_t pmdp_xchg_direct(struct mm_struct *mm, unsigned long addr, 4528c2ecf20Sopenharmony_ci pmd_t *pmdp, pmd_t new) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci pmd_t old; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci preempt_disable(); 4578c2ecf20Sopenharmony_ci old = pmdp_flush_direct(mm, addr, pmdp); 4588c2ecf20Sopenharmony_ci *pmdp = new; 4598c2ecf20Sopenharmony_ci preempt_enable(); 4608c2ecf20Sopenharmony_ci return old; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pmdp_xchg_direct); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cipmd_t pmdp_xchg_lazy(struct mm_struct *mm, unsigned long addr, 4658c2ecf20Sopenharmony_ci pmd_t *pmdp, pmd_t new) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci pmd_t old; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci preempt_disable(); 4708c2ecf20Sopenharmony_ci old = pmdp_flush_lazy(mm, addr, pmdp); 4718c2ecf20Sopenharmony_ci *pmdp = new; 4728c2ecf20Sopenharmony_ci preempt_enable(); 4738c2ecf20Sopenharmony_ci return old; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pmdp_xchg_lazy); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic inline void pudp_idte_local(struct mm_struct *mm, 4788c2ecf20Sopenharmony_ci unsigned long addr, pud_t *pudp) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci if (MACHINE_HAS_TLB_GUEST) 4818c2ecf20Sopenharmony_ci __pudp_idte(addr, pudp, IDTE_NODAT | IDTE_GUEST_ASCE, 4828c2ecf20Sopenharmony_ci mm->context.asce, IDTE_LOCAL); 4838c2ecf20Sopenharmony_ci else 4848c2ecf20Sopenharmony_ci __pudp_idte(addr, pudp, 0, 0, IDTE_LOCAL); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic inline void pudp_idte_global(struct mm_struct *mm, 4888c2ecf20Sopenharmony_ci unsigned long addr, pud_t *pudp) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci if (MACHINE_HAS_TLB_GUEST) 4918c2ecf20Sopenharmony_ci __pudp_idte(addr, pudp, IDTE_NODAT | IDTE_GUEST_ASCE, 4928c2ecf20Sopenharmony_ci mm->context.asce, IDTE_GLOBAL); 4938c2ecf20Sopenharmony_ci else if (MACHINE_HAS_IDTE) 4948c2ecf20Sopenharmony_ci __pudp_idte(addr, pudp, 0, 0, IDTE_GLOBAL); 4958c2ecf20Sopenharmony_ci else 4968c2ecf20Sopenharmony_ci /* 4978c2ecf20Sopenharmony_ci * Invalid bit position is the same for pmd and pud, so we can 4988c2ecf20Sopenharmony_ci * re-use _pmd_csp() here 4998c2ecf20Sopenharmony_ci */ 5008c2ecf20Sopenharmony_ci __pmdp_csp((pmd_t *) pudp); 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic inline pud_t pudp_flush_direct(struct mm_struct *mm, 5048c2ecf20Sopenharmony_ci unsigned long addr, pud_t *pudp) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci pud_t old; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci old = *pudp; 5098c2ecf20Sopenharmony_ci if (pud_val(old) & _REGION_ENTRY_INVALID) 5108c2ecf20Sopenharmony_ci return old; 5118c2ecf20Sopenharmony_ci atomic_inc(&mm->context.flush_count); 5128c2ecf20Sopenharmony_ci if (MACHINE_HAS_TLB_LC && 5138c2ecf20Sopenharmony_ci cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id()))) 5148c2ecf20Sopenharmony_ci pudp_idte_local(mm, addr, pudp); 5158c2ecf20Sopenharmony_ci else 5168c2ecf20Sopenharmony_ci pudp_idte_global(mm, addr, pudp); 5178c2ecf20Sopenharmony_ci atomic_dec(&mm->context.flush_count); 5188c2ecf20Sopenharmony_ci return old; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cipud_t pudp_xchg_direct(struct mm_struct *mm, unsigned long addr, 5228c2ecf20Sopenharmony_ci pud_t *pudp, pud_t new) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci pud_t old; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci preempt_disable(); 5278c2ecf20Sopenharmony_ci old = pudp_flush_direct(mm, addr, pudp); 5288c2ecf20Sopenharmony_ci *pudp = new; 5298c2ecf20Sopenharmony_ci preempt_enable(); 5308c2ecf20Sopenharmony_ci return old; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pudp_xchg_direct); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 5358c2ecf20Sopenharmony_civoid pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp, 5368c2ecf20Sopenharmony_ci pgtable_t pgtable) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct list_head *lh = (struct list_head *) pgtable; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci assert_spin_locked(pmd_lockptr(mm, pmdp)); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci /* FIFO */ 5438c2ecf20Sopenharmony_ci if (!pmd_huge_pte(mm, pmdp)) 5448c2ecf20Sopenharmony_ci INIT_LIST_HEAD(lh); 5458c2ecf20Sopenharmony_ci else 5468c2ecf20Sopenharmony_ci list_add(lh, (struct list_head *) pmd_huge_pte(mm, pmdp)); 5478c2ecf20Sopenharmony_ci pmd_huge_pte(mm, pmdp) = pgtable; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cipgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct list_head *lh; 5538c2ecf20Sopenharmony_ci pgtable_t pgtable; 5548c2ecf20Sopenharmony_ci pte_t *ptep; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci assert_spin_locked(pmd_lockptr(mm, pmdp)); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* FIFO */ 5598c2ecf20Sopenharmony_ci pgtable = pmd_huge_pte(mm, pmdp); 5608c2ecf20Sopenharmony_ci lh = (struct list_head *) pgtable; 5618c2ecf20Sopenharmony_ci if (list_empty(lh)) 5628c2ecf20Sopenharmony_ci pmd_huge_pte(mm, pmdp) = NULL; 5638c2ecf20Sopenharmony_ci else { 5648c2ecf20Sopenharmony_ci pmd_huge_pte(mm, pmdp) = (pgtable_t) lh->next; 5658c2ecf20Sopenharmony_ci list_del(lh); 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci ptep = (pte_t *) pgtable; 5688c2ecf20Sopenharmony_ci pte_val(*ptep) = _PAGE_INVALID; 5698c2ecf20Sopenharmony_ci ptep++; 5708c2ecf20Sopenharmony_ci pte_val(*ptep) = _PAGE_INVALID; 5718c2ecf20Sopenharmony_ci return pgtable; 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci#ifdef CONFIG_PGSTE 5768c2ecf20Sopenharmony_civoid ptep_set_pte_at(struct mm_struct *mm, unsigned long addr, 5778c2ecf20Sopenharmony_ci pte_t *ptep, pte_t entry) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci pgste_t pgste; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* the mm_has_pgste() check is done in set_pte_at() */ 5828c2ecf20Sopenharmony_ci preempt_disable(); 5838c2ecf20Sopenharmony_ci pgste = pgste_get_lock(ptep); 5848c2ecf20Sopenharmony_ci pgste_val(pgste) &= ~_PGSTE_GPS_ZERO; 5858c2ecf20Sopenharmony_ci pgste_set_key(ptep, pgste, entry, mm); 5868c2ecf20Sopenharmony_ci pgste = pgste_set_pte(ptep, pgste, entry); 5878c2ecf20Sopenharmony_ci pgste_set_unlock(ptep, pgste); 5888c2ecf20Sopenharmony_ci preempt_enable(); 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_civoid ptep_set_notify(struct mm_struct *mm, unsigned long addr, pte_t *ptep) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci pgste_t pgste; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci preempt_disable(); 5968c2ecf20Sopenharmony_ci pgste = pgste_get_lock(ptep); 5978c2ecf20Sopenharmony_ci pgste_val(pgste) |= PGSTE_IN_BIT; 5988c2ecf20Sopenharmony_ci pgste_set_unlock(ptep, pgste); 5998c2ecf20Sopenharmony_ci preempt_enable(); 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci/** 6038c2ecf20Sopenharmony_ci * ptep_force_prot - change access rights of a locked pte 6048c2ecf20Sopenharmony_ci * @mm: pointer to the process mm_struct 6058c2ecf20Sopenharmony_ci * @addr: virtual address in the guest address space 6068c2ecf20Sopenharmony_ci * @ptep: pointer to the page table entry 6078c2ecf20Sopenharmony_ci * @prot: indicates guest access rights: PROT_NONE, PROT_READ or PROT_WRITE 6088c2ecf20Sopenharmony_ci * @bit: pgste bit to set (e.g. for notification) 6098c2ecf20Sopenharmony_ci * 6108c2ecf20Sopenharmony_ci * Returns 0 if the access rights were changed and -EAGAIN if the current 6118c2ecf20Sopenharmony_ci * and requested access rights are incompatible. 6128c2ecf20Sopenharmony_ci */ 6138c2ecf20Sopenharmony_ciint ptep_force_prot(struct mm_struct *mm, unsigned long addr, 6148c2ecf20Sopenharmony_ci pte_t *ptep, int prot, unsigned long bit) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci pte_t entry; 6178c2ecf20Sopenharmony_ci pgste_t pgste; 6188c2ecf20Sopenharmony_ci int pte_i, pte_p, nodat; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci pgste = pgste_get_lock(ptep); 6218c2ecf20Sopenharmony_ci entry = *ptep; 6228c2ecf20Sopenharmony_ci /* Check pte entry after all locks have been acquired */ 6238c2ecf20Sopenharmony_ci pte_i = pte_val(entry) & _PAGE_INVALID; 6248c2ecf20Sopenharmony_ci pte_p = pte_val(entry) & _PAGE_PROTECT; 6258c2ecf20Sopenharmony_ci if ((pte_i && (prot != PROT_NONE)) || 6268c2ecf20Sopenharmony_ci (pte_p && (prot & PROT_WRITE))) { 6278c2ecf20Sopenharmony_ci pgste_set_unlock(ptep, pgste); 6288c2ecf20Sopenharmony_ci return -EAGAIN; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci /* Change access rights and set pgste bit */ 6318c2ecf20Sopenharmony_ci nodat = !!(pgste_val(pgste) & _PGSTE_GPS_NODAT); 6328c2ecf20Sopenharmony_ci if (prot == PROT_NONE && !pte_i) { 6338c2ecf20Sopenharmony_ci ptep_flush_direct(mm, addr, ptep, nodat); 6348c2ecf20Sopenharmony_ci pgste = pgste_update_all(entry, pgste, mm); 6358c2ecf20Sopenharmony_ci pte_val(entry) |= _PAGE_INVALID; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci if (prot == PROT_READ && !pte_p) { 6388c2ecf20Sopenharmony_ci ptep_flush_direct(mm, addr, ptep, nodat); 6398c2ecf20Sopenharmony_ci pte_val(entry) &= ~_PAGE_INVALID; 6408c2ecf20Sopenharmony_ci pte_val(entry) |= _PAGE_PROTECT; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci pgste_val(pgste) |= bit; 6438c2ecf20Sopenharmony_ci pgste = pgste_set_pte(ptep, pgste, entry); 6448c2ecf20Sopenharmony_ci pgste_set_unlock(ptep, pgste); 6458c2ecf20Sopenharmony_ci return 0; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ciint ptep_shadow_pte(struct mm_struct *mm, unsigned long saddr, 6498c2ecf20Sopenharmony_ci pte_t *sptep, pte_t *tptep, pte_t pte) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci pgste_t spgste, tpgste; 6528c2ecf20Sopenharmony_ci pte_t spte, tpte; 6538c2ecf20Sopenharmony_ci int rc = -EAGAIN; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (!(pte_val(*tptep) & _PAGE_INVALID)) 6568c2ecf20Sopenharmony_ci return 0; /* already shadowed */ 6578c2ecf20Sopenharmony_ci spgste = pgste_get_lock(sptep); 6588c2ecf20Sopenharmony_ci spte = *sptep; 6598c2ecf20Sopenharmony_ci if (!(pte_val(spte) & _PAGE_INVALID) && 6608c2ecf20Sopenharmony_ci !((pte_val(spte) & _PAGE_PROTECT) && 6618c2ecf20Sopenharmony_ci !(pte_val(pte) & _PAGE_PROTECT))) { 6628c2ecf20Sopenharmony_ci pgste_val(spgste) |= PGSTE_VSIE_BIT; 6638c2ecf20Sopenharmony_ci tpgste = pgste_get_lock(tptep); 6648c2ecf20Sopenharmony_ci pte_val(tpte) = (pte_val(spte) & PAGE_MASK) | 6658c2ecf20Sopenharmony_ci (pte_val(pte) & _PAGE_PROTECT); 6668c2ecf20Sopenharmony_ci /* don't touch the storage key - it belongs to parent pgste */ 6678c2ecf20Sopenharmony_ci tpgste = pgste_set_pte(tptep, tpgste, tpte); 6688c2ecf20Sopenharmony_ci pgste_set_unlock(tptep, tpgste); 6698c2ecf20Sopenharmony_ci rc = 1; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci pgste_set_unlock(sptep, spgste); 6728c2ecf20Sopenharmony_ci return rc; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_civoid ptep_unshadow_pte(struct mm_struct *mm, unsigned long saddr, pte_t *ptep) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci pgste_t pgste; 6788c2ecf20Sopenharmony_ci int nodat; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci pgste = pgste_get_lock(ptep); 6818c2ecf20Sopenharmony_ci /* notifier is called by the caller */ 6828c2ecf20Sopenharmony_ci nodat = !!(pgste_val(pgste) & _PGSTE_GPS_NODAT); 6838c2ecf20Sopenharmony_ci ptep_flush_direct(mm, saddr, ptep, nodat); 6848c2ecf20Sopenharmony_ci /* don't touch the storage key - it belongs to parent pgste */ 6858c2ecf20Sopenharmony_ci pgste = pgste_set_pte(ptep, pgste, __pte(_PAGE_INVALID)); 6868c2ecf20Sopenharmony_ci pgste_set_unlock(ptep, pgste); 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_cistatic void ptep_zap_swap_entry(struct mm_struct *mm, swp_entry_t entry) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci if (!non_swap_entry(entry)) 6928c2ecf20Sopenharmony_ci dec_mm_counter(mm, MM_SWAPENTS); 6938c2ecf20Sopenharmony_ci else if (is_migration_entry(entry)) { 6948c2ecf20Sopenharmony_ci struct page *page = migration_entry_to_page(entry); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci dec_mm_counter(mm, mm_counter(page)); 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci free_swap_and_cache(entry); 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_civoid ptep_zap_unused(struct mm_struct *mm, unsigned long addr, 7028c2ecf20Sopenharmony_ci pte_t *ptep, int reset) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci unsigned long pgstev; 7058c2ecf20Sopenharmony_ci pgste_t pgste; 7068c2ecf20Sopenharmony_ci pte_t pte; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* Zap unused and logically-zero pages */ 7098c2ecf20Sopenharmony_ci preempt_disable(); 7108c2ecf20Sopenharmony_ci pgste = pgste_get_lock(ptep); 7118c2ecf20Sopenharmony_ci pgstev = pgste_val(pgste); 7128c2ecf20Sopenharmony_ci pte = *ptep; 7138c2ecf20Sopenharmony_ci if (!reset && pte_swap(pte) && 7148c2ecf20Sopenharmony_ci ((pgstev & _PGSTE_GPS_USAGE_MASK) == _PGSTE_GPS_USAGE_UNUSED || 7158c2ecf20Sopenharmony_ci (pgstev & _PGSTE_GPS_ZERO))) { 7168c2ecf20Sopenharmony_ci ptep_zap_swap_entry(mm, pte_to_swp_entry(pte)); 7178c2ecf20Sopenharmony_ci pte_clear(mm, addr, ptep); 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci if (reset) 7208c2ecf20Sopenharmony_ci pgste_val(pgste) &= ~(_PGSTE_GPS_USAGE_MASK | _PGSTE_GPS_NODAT); 7218c2ecf20Sopenharmony_ci pgste_set_unlock(ptep, pgste); 7228c2ecf20Sopenharmony_ci preempt_enable(); 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_civoid ptep_zap_key(struct mm_struct *mm, unsigned long addr, pte_t *ptep) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci unsigned long ptev; 7288c2ecf20Sopenharmony_ci pgste_t pgste; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* Clear storage key ACC and F, but set R/C */ 7318c2ecf20Sopenharmony_ci preempt_disable(); 7328c2ecf20Sopenharmony_ci pgste = pgste_get_lock(ptep); 7338c2ecf20Sopenharmony_ci pgste_val(pgste) &= ~(PGSTE_ACC_BITS | PGSTE_FP_BIT); 7348c2ecf20Sopenharmony_ci pgste_val(pgste) |= PGSTE_GR_BIT | PGSTE_GC_BIT; 7358c2ecf20Sopenharmony_ci ptev = pte_val(*ptep); 7368c2ecf20Sopenharmony_ci if (!(ptev & _PAGE_INVALID) && (ptev & _PAGE_WRITE)) 7378c2ecf20Sopenharmony_ci page_set_storage_key(ptev & PAGE_MASK, PAGE_DEFAULT_KEY, 0); 7388c2ecf20Sopenharmony_ci pgste_set_unlock(ptep, pgste); 7398c2ecf20Sopenharmony_ci preempt_enable(); 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci/* 7438c2ecf20Sopenharmony_ci * Test and reset if a guest page is dirty 7448c2ecf20Sopenharmony_ci */ 7458c2ecf20Sopenharmony_cibool ptep_test_and_clear_uc(struct mm_struct *mm, unsigned long addr, 7468c2ecf20Sopenharmony_ci pte_t *ptep) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci pgste_t pgste; 7498c2ecf20Sopenharmony_ci pte_t pte; 7508c2ecf20Sopenharmony_ci bool dirty; 7518c2ecf20Sopenharmony_ci int nodat; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci pgste = pgste_get_lock(ptep); 7548c2ecf20Sopenharmony_ci dirty = !!(pgste_val(pgste) & PGSTE_UC_BIT); 7558c2ecf20Sopenharmony_ci pgste_val(pgste) &= ~PGSTE_UC_BIT; 7568c2ecf20Sopenharmony_ci pte = *ptep; 7578c2ecf20Sopenharmony_ci if (dirty && (pte_val(pte) & _PAGE_PRESENT)) { 7588c2ecf20Sopenharmony_ci pgste = pgste_pte_notify(mm, addr, ptep, pgste); 7598c2ecf20Sopenharmony_ci nodat = !!(pgste_val(pgste) & _PGSTE_GPS_NODAT); 7608c2ecf20Sopenharmony_ci ptep_ipte_global(mm, addr, ptep, nodat); 7618c2ecf20Sopenharmony_ci if (MACHINE_HAS_ESOP || !(pte_val(pte) & _PAGE_WRITE)) 7628c2ecf20Sopenharmony_ci pte_val(pte) |= _PAGE_PROTECT; 7638c2ecf20Sopenharmony_ci else 7648c2ecf20Sopenharmony_ci pte_val(pte) |= _PAGE_INVALID; 7658c2ecf20Sopenharmony_ci *ptep = pte; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci pgste_set_unlock(ptep, pgste); 7688c2ecf20Sopenharmony_ci return dirty; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ptep_test_and_clear_uc); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ciint set_guest_storage_key(struct mm_struct *mm, unsigned long addr, 7738c2ecf20Sopenharmony_ci unsigned char key, bool nq) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci unsigned long keyul, paddr; 7768c2ecf20Sopenharmony_ci spinlock_t *ptl; 7778c2ecf20Sopenharmony_ci pgste_t old, new; 7788c2ecf20Sopenharmony_ci pmd_t *pmdp; 7798c2ecf20Sopenharmony_ci pte_t *ptep; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci pmdp = pmd_alloc_map(mm, addr); 7828c2ecf20Sopenharmony_ci if (unlikely(!pmdp)) 7838c2ecf20Sopenharmony_ci return -EFAULT; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci ptl = pmd_lock(mm, pmdp); 7868c2ecf20Sopenharmony_ci if (!pmd_present(*pmdp)) { 7878c2ecf20Sopenharmony_ci spin_unlock(ptl); 7888c2ecf20Sopenharmony_ci return -EFAULT; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci if (pmd_large(*pmdp)) { 7928c2ecf20Sopenharmony_ci paddr = pmd_val(*pmdp) & HPAGE_MASK; 7938c2ecf20Sopenharmony_ci paddr |= addr & ~HPAGE_MASK; 7948c2ecf20Sopenharmony_ci /* 7958c2ecf20Sopenharmony_ci * Huge pmds need quiescing operations, they are 7968c2ecf20Sopenharmony_ci * always mapped. 7978c2ecf20Sopenharmony_ci */ 7988c2ecf20Sopenharmony_ci page_set_storage_key(paddr, key, 1); 7998c2ecf20Sopenharmony_ci spin_unlock(ptl); 8008c2ecf20Sopenharmony_ci return 0; 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci spin_unlock(ptl); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci ptep = pte_alloc_map_lock(mm, pmdp, addr, &ptl); 8058c2ecf20Sopenharmony_ci if (unlikely(!ptep)) 8068c2ecf20Sopenharmony_ci return -EFAULT; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci new = old = pgste_get_lock(ptep); 8098c2ecf20Sopenharmony_ci pgste_val(new) &= ~(PGSTE_GR_BIT | PGSTE_GC_BIT | 8108c2ecf20Sopenharmony_ci PGSTE_ACC_BITS | PGSTE_FP_BIT); 8118c2ecf20Sopenharmony_ci keyul = (unsigned long) key; 8128c2ecf20Sopenharmony_ci pgste_val(new) |= (keyul & (_PAGE_CHANGED | _PAGE_REFERENCED)) << 48; 8138c2ecf20Sopenharmony_ci pgste_val(new) |= (keyul & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56; 8148c2ecf20Sopenharmony_ci if (!(pte_val(*ptep) & _PAGE_INVALID)) { 8158c2ecf20Sopenharmony_ci unsigned long bits, skey; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci paddr = pte_val(*ptep) & PAGE_MASK; 8188c2ecf20Sopenharmony_ci skey = (unsigned long) page_get_storage_key(paddr); 8198c2ecf20Sopenharmony_ci bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED); 8208c2ecf20Sopenharmony_ci skey = key & (_PAGE_ACC_BITS | _PAGE_FP_BIT); 8218c2ecf20Sopenharmony_ci /* Set storage key ACC and FP */ 8228c2ecf20Sopenharmony_ci page_set_storage_key(paddr, skey, !nq); 8238c2ecf20Sopenharmony_ci /* Merge host changed & referenced into pgste */ 8248c2ecf20Sopenharmony_ci pgste_val(new) |= bits << 52; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci /* changing the guest storage key is considered a change of the page */ 8278c2ecf20Sopenharmony_ci if ((pgste_val(new) ^ pgste_val(old)) & 8288c2ecf20Sopenharmony_ci (PGSTE_ACC_BITS | PGSTE_FP_BIT | PGSTE_GR_BIT | PGSTE_GC_BIT)) 8298c2ecf20Sopenharmony_ci pgste_val(new) |= PGSTE_UC_BIT; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci pgste_set_unlock(ptep, new); 8328c2ecf20Sopenharmony_ci pte_unmap_unlock(ptep, ptl); 8338c2ecf20Sopenharmony_ci return 0; 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(set_guest_storage_key); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci/** 8388c2ecf20Sopenharmony_ci * Conditionally set a guest storage key (handling csske). 8398c2ecf20Sopenharmony_ci * oldkey will be updated when either mr or mc is set and a pointer is given. 8408c2ecf20Sopenharmony_ci * 8418c2ecf20Sopenharmony_ci * Returns 0 if a guests storage key update wasn't necessary, 1 if the guest 8428c2ecf20Sopenharmony_ci * storage key was updated and -EFAULT on access errors. 8438c2ecf20Sopenharmony_ci */ 8448c2ecf20Sopenharmony_ciint cond_set_guest_storage_key(struct mm_struct *mm, unsigned long addr, 8458c2ecf20Sopenharmony_ci unsigned char key, unsigned char *oldkey, 8468c2ecf20Sopenharmony_ci bool nq, bool mr, bool mc) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci unsigned char tmp, mask = _PAGE_ACC_BITS | _PAGE_FP_BIT; 8498c2ecf20Sopenharmony_ci int rc; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci /* we can drop the pgste lock between getting and setting the key */ 8528c2ecf20Sopenharmony_ci if (mr | mc) { 8538c2ecf20Sopenharmony_ci rc = get_guest_storage_key(current->mm, addr, &tmp); 8548c2ecf20Sopenharmony_ci if (rc) 8558c2ecf20Sopenharmony_ci return rc; 8568c2ecf20Sopenharmony_ci if (oldkey) 8578c2ecf20Sopenharmony_ci *oldkey = tmp; 8588c2ecf20Sopenharmony_ci if (!mr) 8598c2ecf20Sopenharmony_ci mask |= _PAGE_REFERENCED; 8608c2ecf20Sopenharmony_ci if (!mc) 8618c2ecf20Sopenharmony_ci mask |= _PAGE_CHANGED; 8628c2ecf20Sopenharmony_ci if (!((tmp ^ key) & mask)) 8638c2ecf20Sopenharmony_ci return 0; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci rc = set_guest_storage_key(current->mm, addr, key, nq); 8668c2ecf20Sopenharmony_ci return rc < 0 ? rc : 1; 8678c2ecf20Sopenharmony_ci} 8688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cond_set_guest_storage_key); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci/** 8718c2ecf20Sopenharmony_ci * Reset a guest reference bit (rrbe), returning the reference and changed bit. 8728c2ecf20Sopenharmony_ci * 8738c2ecf20Sopenharmony_ci * Returns < 0 in case of error, otherwise the cc to be reported to the guest. 8748c2ecf20Sopenharmony_ci */ 8758c2ecf20Sopenharmony_ciint reset_guest_reference_bit(struct mm_struct *mm, unsigned long addr) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci spinlock_t *ptl; 8788c2ecf20Sopenharmony_ci unsigned long paddr; 8798c2ecf20Sopenharmony_ci pgste_t old, new; 8808c2ecf20Sopenharmony_ci pmd_t *pmdp; 8818c2ecf20Sopenharmony_ci pte_t *ptep; 8828c2ecf20Sopenharmony_ci int cc = 0; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci pmdp = pmd_alloc_map(mm, addr); 8858c2ecf20Sopenharmony_ci if (unlikely(!pmdp)) 8868c2ecf20Sopenharmony_ci return -EFAULT; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci ptl = pmd_lock(mm, pmdp); 8898c2ecf20Sopenharmony_ci if (!pmd_present(*pmdp)) { 8908c2ecf20Sopenharmony_ci spin_unlock(ptl); 8918c2ecf20Sopenharmony_ci return -EFAULT; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (pmd_large(*pmdp)) { 8958c2ecf20Sopenharmony_ci paddr = pmd_val(*pmdp) & HPAGE_MASK; 8968c2ecf20Sopenharmony_ci paddr |= addr & ~HPAGE_MASK; 8978c2ecf20Sopenharmony_ci cc = page_reset_referenced(paddr); 8988c2ecf20Sopenharmony_ci spin_unlock(ptl); 8998c2ecf20Sopenharmony_ci return cc; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci spin_unlock(ptl); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci ptep = pte_alloc_map_lock(mm, pmdp, addr, &ptl); 9048c2ecf20Sopenharmony_ci if (unlikely(!ptep)) 9058c2ecf20Sopenharmony_ci return -EFAULT; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci new = old = pgste_get_lock(ptep); 9088c2ecf20Sopenharmony_ci /* Reset guest reference bit only */ 9098c2ecf20Sopenharmony_ci pgste_val(new) &= ~PGSTE_GR_BIT; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (!(pte_val(*ptep) & _PAGE_INVALID)) { 9128c2ecf20Sopenharmony_ci paddr = pte_val(*ptep) & PAGE_MASK; 9138c2ecf20Sopenharmony_ci cc = page_reset_referenced(paddr); 9148c2ecf20Sopenharmony_ci /* Merge real referenced bit into host-set */ 9158c2ecf20Sopenharmony_ci pgste_val(new) |= ((unsigned long) cc << 53) & PGSTE_HR_BIT; 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci /* Reflect guest's logical view, not physical */ 9188c2ecf20Sopenharmony_ci cc |= (pgste_val(old) & (PGSTE_GR_BIT | PGSTE_GC_BIT)) >> 49; 9198c2ecf20Sopenharmony_ci /* Changing the guest storage key is considered a change of the page */ 9208c2ecf20Sopenharmony_ci if ((pgste_val(new) ^ pgste_val(old)) & PGSTE_GR_BIT) 9218c2ecf20Sopenharmony_ci pgste_val(new) |= PGSTE_UC_BIT; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci pgste_set_unlock(ptep, new); 9248c2ecf20Sopenharmony_ci pte_unmap_unlock(ptep, ptl); 9258c2ecf20Sopenharmony_ci return cc; 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(reset_guest_reference_bit); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ciint get_guest_storage_key(struct mm_struct *mm, unsigned long addr, 9308c2ecf20Sopenharmony_ci unsigned char *key) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci unsigned long paddr; 9338c2ecf20Sopenharmony_ci spinlock_t *ptl; 9348c2ecf20Sopenharmony_ci pgste_t pgste; 9358c2ecf20Sopenharmony_ci pmd_t *pmdp; 9368c2ecf20Sopenharmony_ci pte_t *ptep; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci pmdp = pmd_alloc_map(mm, addr); 9398c2ecf20Sopenharmony_ci if (unlikely(!pmdp)) 9408c2ecf20Sopenharmony_ci return -EFAULT; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci ptl = pmd_lock(mm, pmdp); 9438c2ecf20Sopenharmony_ci if (!pmd_present(*pmdp)) { 9448c2ecf20Sopenharmony_ci /* Not yet mapped memory has a zero key */ 9458c2ecf20Sopenharmony_ci spin_unlock(ptl); 9468c2ecf20Sopenharmony_ci *key = 0; 9478c2ecf20Sopenharmony_ci return 0; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci if (pmd_large(*pmdp)) { 9518c2ecf20Sopenharmony_ci paddr = pmd_val(*pmdp) & HPAGE_MASK; 9528c2ecf20Sopenharmony_ci paddr |= addr & ~HPAGE_MASK; 9538c2ecf20Sopenharmony_ci *key = page_get_storage_key(paddr); 9548c2ecf20Sopenharmony_ci spin_unlock(ptl); 9558c2ecf20Sopenharmony_ci return 0; 9568c2ecf20Sopenharmony_ci } 9578c2ecf20Sopenharmony_ci spin_unlock(ptl); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci ptep = pte_alloc_map_lock(mm, pmdp, addr, &ptl); 9608c2ecf20Sopenharmony_ci if (unlikely(!ptep)) 9618c2ecf20Sopenharmony_ci return -EFAULT; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci pgste = pgste_get_lock(ptep); 9648c2ecf20Sopenharmony_ci *key = (pgste_val(pgste) & (PGSTE_ACC_BITS | PGSTE_FP_BIT)) >> 56; 9658c2ecf20Sopenharmony_ci paddr = pte_val(*ptep) & PAGE_MASK; 9668c2ecf20Sopenharmony_ci if (!(pte_val(*ptep) & _PAGE_INVALID)) 9678c2ecf20Sopenharmony_ci *key = page_get_storage_key(paddr); 9688c2ecf20Sopenharmony_ci /* Reflect guest's logical view, not physical */ 9698c2ecf20Sopenharmony_ci *key |= (pgste_val(pgste) & (PGSTE_GR_BIT | PGSTE_GC_BIT)) >> 48; 9708c2ecf20Sopenharmony_ci pgste_set_unlock(ptep, pgste); 9718c2ecf20Sopenharmony_ci pte_unmap_unlock(ptep, ptl); 9728c2ecf20Sopenharmony_ci return 0; 9738c2ecf20Sopenharmony_ci} 9748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_guest_storage_key); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci/** 9778c2ecf20Sopenharmony_ci * pgste_perform_essa - perform ESSA actions on the PGSTE. 9788c2ecf20Sopenharmony_ci * @mm: the memory context. It must have PGSTEs, no check is performed here! 9798c2ecf20Sopenharmony_ci * @hva: the host virtual address of the page whose PGSTE is to be processed 9808c2ecf20Sopenharmony_ci * @orc: the specific action to perform, see the ESSA_SET_* macros. 9818c2ecf20Sopenharmony_ci * @oldpte: the PTE will be saved there if the pointer is not NULL. 9828c2ecf20Sopenharmony_ci * @oldpgste: the old PGSTE will be saved there if the pointer is not NULL. 9838c2ecf20Sopenharmony_ci * 9848c2ecf20Sopenharmony_ci * Return: 1 if the page is to be added to the CBRL, otherwise 0, 9858c2ecf20Sopenharmony_ci * or < 0 in case of error. -EINVAL is returned for invalid values 9868c2ecf20Sopenharmony_ci * of orc, -EFAULT for invalid addresses. 9878c2ecf20Sopenharmony_ci */ 9888c2ecf20Sopenharmony_ciint pgste_perform_essa(struct mm_struct *mm, unsigned long hva, int orc, 9898c2ecf20Sopenharmony_ci unsigned long *oldpte, unsigned long *oldpgste) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci struct vm_area_struct *vma; 9928c2ecf20Sopenharmony_ci unsigned long pgstev; 9938c2ecf20Sopenharmony_ci spinlock_t *ptl; 9948c2ecf20Sopenharmony_ci pgste_t pgste; 9958c2ecf20Sopenharmony_ci pte_t *ptep; 9968c2ecf20Sopenharmony_ci int res = 0; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci WARN_ON_ONCE(orc > ESSA_MAX); 9998c2ecf20Sopenharmony_ci if (unlikely(orc > ESSA_MAX)) 10008c2ecf20Sopenharmony_ci return -EINVAL; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci vma = find_vma(mm, hva); 10038c2ecf20Sopenharmony_ci if (!vma || hva < vma->vm_start || is_vm_hugetlb_page(vma)) 10048c2ecf20Sopenharmony_ci return -EFAULT; 10058c2ecf20Sopenharmony_ci ptep = get_locked_pte(mm, hva, &ptl); 10068c2ecf20Sopenharmony_ci if (unlikely(!ptep)) 10078c2ecf20Sopenharmony_ci return -EFAULT; 10088c2ecf20Sopenharmony_ci pgste = pgste_get_lock(ptep); 10098c2ecf20Sopenharmony_ci pgstev = pgste_val(pgste); 10108c2ecf20Sopenharmony_ci if (oldpte) 10118c2ecf20Sopenharmony_ci *oldpte = pte_val(*ptep); 10128c2ecf20Sopenharmony_ci if (oldpgste) 10138c2ecf20Sopenharmony_ci *oldpgste = pgstev; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci switch (orc) { 10168c2ecf20Sopenharmony_ci case ESSA_GET_STATE: 10178c2ecf20Sopenharmony_ci break; 10188c2ecf20Sopenharmony_ci case ESSA_SET_STABLE: 10198c2ecf20Sopenharmony_ci pgstev &= ~(_PGSTE_GPS_USAGE_MASK | _PGSTE_GPS_NODAT); 10208c2ecf20Sopenharmony_ci pgstev |= _PGSTE_GPS_USAGE_STABLE; 10218c2ecf20Sopenharmony_ci break; 10228c2ecf20Sopenharmony_ci case ESSA_SET_UNUSED: 10238c2ecf20Sopenharmony_ci pgstev &= ~_PGSTE_GPS_USAGE_MASK; 10248c2ecf20Sopenharmony_ci pgstev |= _PGSTE_GPS_USAGE_UNUSED; 10258c2ecf20Sopenharmony_ci if (pte_val(*ptep) & _PAGE_INVALID) 10268c2ecf20Sopenharmony_ci res = 1; 10278c2ecf20Sopenharmony_ci break; 10288c2ecf20Sopenharmony_ci case ESSA_SET_VOLATILE: 10298c2ecf20Sopenharmony_ci pgstev &= ~_PGSTE_GPS_USAGE_MASK; 10308c2ecf20Sopenharmony_ci pgstev |= _PGSTE_GPS_USAGE_VOLATILE; 10318c2ecf20Sopenharmony_ci if (pte_val(*ptep) & _PAGE_INVALID) 10328c2ecf20Sopenharmony_ci res = 1; 10338c2ecf20Sopenharmony_ci break; 10348c2ecf20Sopenharmony_ci case ESSA_SET_POT_VOLATILE: 10358c2ecf20Sopenharmony_ci pgstev &= ~_PGSTE_GPS_USAGE_MASK; 10368c2ecf20Sopenharmony_ci if (!(pte_val(*ptep) & _PAGE_INVALID)) { 10378c2ecf20Sopenharmony_ci pgstev |= _PGSTE_GPS_USAGE_POT_VOLATILE; 10388c2ecf20Sopenharmony_ci break; 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci if (pgstev & _PGSTE_GPS_ZERO) { 10418c2ecf20Sopenharmony_ci pgstev |= _PGSTE_GPS_USAGE_VOLATILE; 10428c2ecf20Sopenharmony_ci break; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci if (!(pgstev & PGSTE_GC_BIT)) { 10458c2ecf20Sopenharmony_ci pgstev |= _PGSTE_GPS_USAGE_VOLATILE; 10468c2ecf20Sopenharmony_ci res = 1; 10478c2ecf20Sopenharmony_ci break; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci break; 10508c2ecf20Sopenharmony_ci case ESSA_SET_STABLE_RESIDENT: 10518c2ecf20Sopenharmony_ci pgstev &= ~_PGSTE_GPS_USAGE_MASK; 10528c2ecf20Sopenharmony_ci pgstev |= _PGSTE_GPS_USAGE_STABLE; 10538c2ecf20Sopenharmony_ci /* 10548c2ecf20Sopenharmony_ci * Since the resident state can go away any time after this 10558c2ecf20Sopenharmony_ci * call, we will not make this page resident. We can revisit 10568c2ecf20Sopenharmony_ci * this decision if a guest will ever start using this. 10578c2ecf20Sopenharmony_ci */ 10588c2ecf20Sopenharmony_ci break; 10598c2ecf20Sopenharmony_ci case ESSA_SET_STABLE_IF_RESIDENT: 10608c2ecf20Sopenharmony_ci if (!(pte_val(*ptep) & _PAGE_INVALID)) { 10618c2ecf20Sopenharmony_ci pgstev &= ~_PGSTE_GPS_USAGE_MASK; 10628c2ecf20Sopenharmony_ci pgstev |= _PGSTE_GPS_USAGE_STABLE; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci break; 10658c2ecf20Sopenharmony_ci case ESSA_SET_STABLE_NODAT: 10668c2ecf20Sopenharmony_ci pgstev &= ~_PGSTE_GPS_USAGE_MASK; 10678c2ecf20Sopenharmony_ci pgstev |= _PGSTE_GPS_USAGE_STABLE | _PGSTE_GPS_NODAT; 10688c2ecf20Sopenharmony_ci break; 10698c2ecf20Sopenharmony_ci default: 10708c2ecf20Sopenharmony_ci /* we should never get here! */ 10718c2ecf20Sopenharmony_ci break; 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci /* If we are discarding a page, set it to logical zero */ 10748c2ecf20Sopenharmony_ci if (res) 10758c2ecf20Sopenharmony_ci pgstev |= _PGSTE_GPS_ZERO; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci pgste_val(pgste) = pgstev; 10788c2ecf20Sopenharmony_ci pgste_set_unlock(ptep, pgste); 10798c2ecf20Sopenharmony_ci pte_unmap_unlock(ptep, ptl); 10808c2ecf20Sopenharmony_ci return res; 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pgste_perform_essa); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci/** 10858c2ecf20Sopenharmony_ci * set_pgste_bits - set specific PGSTE bits. 10868c2ecf20Sopenharmony_ci * @mm: the memory context. It must have PGSTEs, no check is performed here! 10878c2ecf20Sopenharmony_ci * @hva: the host virtual address of the page whose PGSTE is to be processed 10888c2ecf20Sopenharmony_ci * @bits: a bitmask representing the bits that will be touched 10898c2ecf20Sopenharmony_ci * @value: the values of the bits to be written. Only the bits in the mask 10908c2ecf20Sopenharmony_ci * will be written. 10918c2ecf20Sopenharmony_ci * 10928c2ecf20Sopenharmony_ci * Return: 0 on success, < 0 in case of error. 10938c2ecf20Sopenharmony_ci */ 10948c2ecf20Sopenharmony_ciint set_pgste_bits(struct mm_struct *mm, unsigned long hva, 10958c2ecf20Sopenharmony_ci unsigned long bits, unsigned long value) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci struct vm_area_struct *vma; 10988c2ecf20Sopenharmony_ci spinlock_t *ptl; 10998c2ecf20Sopenharmony_ci pgste_t new; 11008c2ecf20Sopenharmony_ci pte_t *ptep; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci vma = find_vma(mm, hva); 11038c2ecf20Sopenharmony_ci if (!vma || hva < vma->vm_start || is_vm_hugetlb_page(vma)) 11048c2ecf20Sopenharmony_ci return -EFAULT; 11058c2ecf20Sopenharmony_ci ptep = get_locked_pte(mm, hva, &ptl); 11068c2ecf20Sopenharmony_ci if (unlikely(!ptep)) 11078c2ecf20Sopenharmony_ci return -EFAULT; 11088c2ecf20Sopenharmony_ci new = pgste_get_lock(ptep); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci pgste_val(new) &= ~bits; 11118c2ecf20Sopenharmony_ci pgste_val(new) |= value & bits; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci pgste_set_unlock(ptep, new); 11148c2ecf20Sopenharmony_ci pte_unmap_unlock(ptep, ptl); 11158c2ecf20Sopenharmony_ci return 0; 11168c2ecf20Sopenharmony_ci} 11178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(set_pgste_bits); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci/** 11208c2ecf20Sopenharmony_ci * get_pgste - get the current PGSTE for the given address. 11218c2ecf20Sopenharmony_ci * @mm: the memory context. It must have PGSTEs, no check is performed here! 11228c2ecf20Sopenharmony_ci * @hva: the host virtual address of the page whose PGSTE is to be processed 11238c2ecf20Sopenharmony_ci * @pgstep: will be written with the current PGSTE for the given address. 11248c2ecf20Sopenharmony_ci * 11258c2ecf20Sopenharmony_ci * Return: 0 on success, < 0 in case of error. 11268c2ecf20Sopenharmony_ci */ 11278c2ecf20Sopenharmony_ciint get_pgste(struct mm_struct *mm, unsigned long hva, unsigned long *pgstep) 11288c2ecf20Sopenharmony_ci{ 11298c2ecf20Sopenharmony_ci struct vm_area_struct *vma; 11308c2ecf20Sopenharmony_ci spinlock_t *ptl; 11318c2ecf20Sopenharmony_ci pte_t *ptep; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci vma = find_vma(mm, hva); 11348c2ecf20Sopenharmony_ci if (!vma || hva < vma->vm_start || is_vm_hugetlb_page(vma)) 11358c2ecf20Sopenharmony_ci return -EFAULT; 11368c2ecf20Sopenharmony_ci ptep = get_locked_pte(mm, hva, &ptl); 11378c2ecf20Sopenharmony_ci if (unlikely(!ptep)) 11388c2ecf20Sopenharmony_ci return -EFAULT; 11398c2ecf20Sopenharmony_ci *pgstep = pgste_val(pgste_get(ptep)); 11408c2ecf20Sopenharmony_ci pte_unmap_unlock(ptep, ptl); 11418c2ecf20Sopenharmony_ci return 0; 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_pgste); 11448c2ecf20Sopenharmony_ci#endif 1145