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