xref: /linux/arch/s390/kvm/gmap/dat.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
11b09c13cSSteffen Eiden // SPDX-License-Identifier: GPL-2.0
21b09c13cSSteffen Eiden /*
31b09c13cSSteffen Eiden  *  KVM guest address space mapping code
41b09c13cSSteffen Eiden  *
51b09c13cSSteffen Eiden  *    Copyright IBM Corp. 2007, 2020, 2024
61b09c13cSSteffen Eiden  *    Author(s): Claudio Imbrenda <imbrenda@linux.ibm.com>
71b09c13cSSteffen Eiden  *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
81b09c13cSSteffen Eiden  *		 David Hildenbrand <david@redhat.com>
91b09c13cSSteffen Eiden  *		 Janosch Frank <frankja@linux.ibm.com>
101b09c13cSSteffen Eiden  */
111b09c13cSSteffen Eiden 
121b09c13cSSteffen Eiden #include <linux/kernel.h>
131b09c13cSSteffen Eiden #include <linux/pagewalk.h>
141b09c13cSSteffen Eiden #include <linux/swap.h>
151b09c13cSSteffen Eiden #include <linux/smp.h>
161b09c13cSSteffen Eiden #include <linux/spinlock.h>
171b09c13cSSteffen Eiden #include <linux/slab.h>
181b09c13cSSteffen Eiden #include <linux/swapops.h>
191b09c13cSSteffen Eiden #include <linux/ksm.h>
201b09c13cSSteffen Eiden #include <linux/mm.h>
211b09c13cSSteffen Eiden #include <linux/mman.h>
221b09c13cSSteffen Eiden #include <linux/pgtable.h>
231b09c13cSSteffen Eiden #include <linux/kvm_types.h>
241b09c13cSSteffen Eiden #include <linux/kvm_host.h>
251b09c13cSSteffen Eiden #include <linux/pgalloc.h>
261b09c13cSSteffen Eiden 
271b09c13cSSteffen Eiden #include <asm/page-states.h>
281b09c13cSSteffen Eiden #include <asm/tlb.h>
291b09c13cSSteffen Eiden #include "dat.h"
301b09c13cSSteffen Eiden 
311b09c13cSSteffen Eiden int kvm_s390_mmu_cache_topup(struct kvm_s390_mmu_cache *mc)
321b09c13cSSteffen Eiden {
331b09c13cSSteffen Eiden 	void *o;
341b09c13cSSteffen Eiden 
351b09c13cSSteffen Eiden 	for ( ; mc->n_crsts < KVM_S390_MMU_CACHE_N_CRSTS; mc->n_crsts++) {
361b09c13cSSteffen Eiden 		o = (void *)__get_free_pages(GFP_KERNEL_ACCOUNT | __GFP_COMP, CRST_ALLOC_ORDER);
371b09c13cSSteffen Eiden 		if (!o)
381b09c13cSSteffen Eiden 			return -ENOMEM;
391b09c13cSSteffen Eiden 		mc->crsts[mc->n_crsts] = o;
401b09c13cSSteffen Eiden 	}
411b09c13cSSteffen Eiden 	for ( ; mc->n_pts < KVM_S390_MMU_CACHE_N_PTS; mc->n_pts++) {
421b09c13cSSteffen Eiden 		o = (void *)__get_free_page(GFP_KERNEL_ACCOUNT);
431b09c13cSSteffen Eiden 		if (!o)
441b09c13cSSteffen Eiden 			return -ENOMEM;
451b09c13cSSteffen Eiden 		mc->pts[mc->n_pts] = o;
461b09c13cSSteffen Eiden 	}
471b09c13cSSteffen Eiden 	for ( ; mc->n_rmaps < KVM_S390_MMU_CACHE_N_RMAPS; mc->n_rmaps++) {
481b09c13cSSteffen Eiden 		o = kzalloc_obj(struct vsie_rmap, GFP_KERNEL_ACCOUNT);
491b09c13cSSteffen Eiden 		if (!o)
501b09c13cSSteffen Eiden 			return -ENOMEM;
511b09c13cSSteffen Eiden 		mc->rmaps[mc->n_rmaps] = o;
521b09c13cSSteffen Eiden 	}
531b09c13cSSteffen Eiden 	return 0;
541b09c13cSSteffen Eiden }
551b09c13cSSteffen Eiden 
561b09c13cSSteffen Eiden static inline struct page_table *dat_alloc_pt_noinit(struct kvm_s390_mmu_cache *mc)
571b09c13cSSteffen Eiden {
581b09c13cSSteffen Eiden 	struct page_table *res;
591b09c13cSSteffen Eiden 
601b09c13cSSteffen Eiden 	res = kvm_s390_mmu_cache_alloc_pt(mc);
611b09c13cSSteffen Eiden 	if (res)
621b09c13cSSteffen Eiden 		__arch_set_page_dat(res, 1);
631b09c13cSSteffen Eiden 	return res;
641b09c13cSSteffen Eiden }
651b09c13cSSteffen Eiden 
661b09c13cSSteffen Eiden static inline struct crst_table *dat_alloc_crst_noinit(struct kvm_s390_mmu_cache *mc)
671b09c13cSSteffen Eiden {
681b09c13cSSteffen Eiden 	struct crst_table *res;
691b09c13cSSteffen Eiden 
701b09c13cSSteffen Eiden 	res = kvm_s390_mmu_cache_alloc_crst(mc);
711b09c13cSSteffen Eiden 	if (res)
721b09c13cSSteffen Eiden 		__arch_set_page_dat(res, 1UL << CRST_ALLOC_ORDER);
731b09c13cSSteffen Eiden 	return res;
741b09c13cSSteffen Eiden }
751b09c13cSSteffen Eiden 
761b09c13cSSteffen Eiden struct crst_table *dat_alloc_crst_sleepable(unsigned long init)
771b09c13cSSteffen Eiden {
781b09c13cSSteffen Eiden 	struct page *page;
791b09c13cSSteffen Eiden 	void *virt;
801b09c13cSSteffen Eiden 
811b09c13cSSteffen Eiden 	page = alloc_pages(GFP_KERNEL_ACCOUNT | __GFP_COMP, CRST_ALLOC_ORDER);
821b09c13cSSteffen Eiden 	if (!page)
831b09c13cSSteffen Eiden 		return NULL;
841b09c13cSSteffen Eiden 	virt = page_to_virt(page);
851b09c13cSSteffen Eiden 	__arch_set_page_dat(virt, 1UL << CRST_ALLOC_ORDER);
861b09c13cSSteffen Eiden 	crst_table_init(virt, init);
871b09c13cSSteffen Eiden 	return virt;
881b09c13cSSteffen Eiden }
891b09c13cSSteffen Eiden 
901b09c13cSSteffen Eiden void dat_free_level(struct crst_table *table, bool owns_ptes)
911b09c13cSSteffen Eiden {
921b09c13cSSteffen Eiden 	unsigned int i;
931b09c13cSSteffen Eiden 
941b09c13cSSteffen Eiden 	for (i = 0; i < _CRST_ENTRIES; i++) {
951b09c13cSSteffen Eiden 		if (table->crstes[i].h.fc || table->crstes[i].h.i)
961b09c13cSSteffen Eiden 			continue;
971b09c13cSSteffen Eiden 		if (!is_pmd(table->crstes[i]))
981b09c13cSSteffen Eiden 			dat_free_level(dereference_crste(table->crstes[i]), owns_ptes);
991b09c13cSSteffen Eiden 		else if (owns_ptes)
1001b09c13cSSteffen Eiden 			dat_free_pt(dereference_pmd(table->crstes[i].pmd));
1011b09c13cSSteffen Eiden 	}
1021b09c13cSSteffen Eiden 	dat_free_crst(table);
1031b09c13cSSteffen Eiden }
1041b09c13cSSteffen Eiden 
1051b09c13cSSteffen Eiden int dat_set_asce_limit(struct kvm_s390_mmu_cache *mc, union asce *asce, int newtype)
1061b09c13cSSteffen Eiden {
1071b09c13cSSteffen Eiden 	struct crst_table *table;
1081b09c13cSSteffen Eiden 	union crste crste;
1091b09c13cSSteffen Eiden 
1101b09c13cSSteffen Eiden 	while (asce->dt > newtype) {
1111b09c13cSSteffen Eiden 		table = dereference_asce(*asce);
1121b09c13cSSteffen Eiden 		crste = table->crstes[0];
1131b09c13cSSteffen Eiden 		if (crste.h.fc)
1141b09c13cSSteffen Eiden 			return 0;
1151b09c13cSSteffen Eiden 		if (!crste.h.i) {
1161b09c13cSSteffen Eiden 			asce->rsto = crste.h.fc0.to;
1171b09c13cSSteffen Eiden 			dat_free_crst(table);
1181b09c13cSSteffen Eiden 		} else {
1191b09c13cSSteffen Eiden 			crste.h.tt--;
1201b09c13cSSteffen Eiden 			crst_table_init((void *)table, crste.val);
1211b09c13cSSteffen Eiden 		}
1221b09c13cSSteffen Eiden 		asce->dt--;
1231b09c13cSSteffen Eiden 	}
1241b09c13cSSteffen Eiden 	while (asce->dt < newtype) {
1251b09c13cSSteffen Eiden 		crste = _crste_fc0(asce->rsto, asce->dt + 1);
1261b09c13cSSteffen Eiden 		table = dat_alloc_crst_noinit(mc);
1271b09c13cSSteffen Eiden 		if (!table)
1281b09c13cSSteffen Eiden 			return -ENOMEM;
1291b09c13cSSteffen Eiden 		crst_table_init((void *)table, _CRSTE_HOLE(crste.h.tt).val);
1301b09c13cSSteffen Eiden 		table->crstes[0] = crste;
1311b09c13cSSteffen Eiden 		asce->rsto = __pa(table) >> PAGE_SHIFT;
1321b09c13cSSteffen Eiden 		asce->dt++;
1331b09c13cSSteffen Eiden 	}
1341b09c13cSSteffen Eiden 	return 0;
1351b09c13cSSteffen Eiden }
1361b09c13cSSteffen Eiden 
1371b09c13cSSteffen Eiden /**
1381b09c13cSSteffen Eiden  * dat_crstep_xchg_atomic() - Atomically exchange a gmap CRSTE with another.
1391b09c13cSSteffen Eiden  * @crstep: Pointer to the CRST entry.
1401b09c13cSSteffen Eiden  * @old: Expected old value.
1411b09c13cSSteffen Eiden  * @new: Replacement entry.
1421b09c13cSSteffen Eiden  * @gfn: The affected guest address.
1431b09c13cSSteffen Eiden  * @asce: The asce of the address space.
1441b09c13cSSteffen Eiden  *
1451b09c13cSSteffen Eiden  * This function is needed to atomically exchange a CRSTE that potentially
1461b09c13cSSteffen Eiden  * maps a prefix area, without having to invalidate it inbetween.
1471b09c13cSSteffen Eiden  *
1481b09c13cSSteffen Eiden  * Context: This function is assumed to be called with kvm->mmu_lock held.
1491b09c13cSSteffen Eiden  *
1501b09c13cSSteffen Eiden  * Return: %true if the exchange was successful.
1511b09c13cSSteffen Eiden  */
1521b09c13cSSteffen Eiden bool __must_check dat_crstep_xchg_atomic(union crste *crstep, union crste old, union crste new,
1531b09c13cSSteffen Eiden 					 gfn_t gfn, union asce asce)
1541b09c13cSSteffen Eiden {
1551b09c13cSSteffen Eiden 	if (old.h.i)
1561b09c13cSSteffen Eiden 		return arch_try_cmpxchg((long *)crstep, &old.val, new.val);
1571b09c13cSSteffen Eiden 	if (cpu_has_edat2())
1581b09c13cSSteffen Eiden 		return crdte_crste(crstep, old, new, gfn, asce);
1591b09c13cSSteffen Eiden 	return cspg_crste(crstep, old, new);
1601b09c13cSSteffen Eiden }
1611b09c13cSSteffen Eiden 
1621b09c13cSSteffen Eiden static void dat_set_storage_key_from_pgste(union pte pte, union pgste pgste)
1631b09c13cSSteffen Eiden {
1641b09c13cSSteffen Eiden 	union skey nkey = { .acc = pgste.acc, .fp = pgste.fp };
1651b09c13cSSteffen Eiden 
1661b09c13cSSteffen Eiden 	page_set_storage_key(pte_origin(pte), nkey.skey, 0);
1671b09c13cSSteffen Eiden }
1681b09c13cSSteffen Eiden 
1691b09c13cSSteffen Eiden static void dat_move_storage_key(union pte old, union pte new)
1701b09c13cSSteffen Eiden {
1711b09c13cSSteffen Eiden 	page_set_storage_key(pte_origin(new), page_get_storage_key(pte_origin(old)), 1);
1721b09c13cSSteffen Eiden }
1731b09c13cSSteffen Eiden 
1741b09c13cSSteffen Eiden static union pgste dat_save_storage_key_into_pgste(union pte pte, union pgste pgste)
1751b09c13cSSteffen Eiden {
1761b09c13cSSteffen Eiden 	union skey skey;
1771b09c13cSSteffen Eiden 
1781b09c13cSSteffen Eiden 	skey.skey = page_get_storage_key(pte_origin(pte));
1791b09c13cSSteffen Eiden 
1801b09c13cSSteffen Eiden 	pgste.acc = skey.acc;
1811b09c13cSSteffen Eiden 	pgste.fp = skey.fp;
1821b09c13cSSteffen Eiden 	pgste.gr |= skey.r;
1831b09c13cSSteffen Eiden 	pgste.gc |= skey.c;
1841b09c13cSSteffen Eiden 
1851b09c13cSSteffen Eiden 	return pgste;
1861b09c13cSSteffen Eiden }
1871b09c13cSSteffen Eiden 
1881b09c13cSSteffen Eiden union pgste __dat_ptep_xchg(union pte *ptep, union pgste pgste, union pte new, gfn_t gfn,
1891b09c13cSSteffen Eiden 			    union asce asce, bool uses_skeys)
1901b09c13cSSteffen Eiden {
1911b09c13cSSteffen Eiden 	union pte old = READ_ONCE(*ptep);
1921b09c13cSSteffen Eiden 
1931b09c13cSSteffen Eiden 	/* Updating only the software bits while holding the pgste lock. */
1941b09c13cSSteffen Eiden 	if (!((ptep->val ^ new.val) & ~_PAGE_SW_BITS)) {
1951b09c13cSSteffen Eiden 		WRITE_ONCE(ptep->swbyte, new.swbyte);
1961b09c13cSSteffen Eiden 		return pgste;
1971b09c13cSSteffen Eiden 	}
1981b09c13cSSteffen Eiden 
1991b09c13cSSteffen Eiden 	if (!old.h.i) {
2001b09c13cSSteffen Eiden 		unsigned long opts = IPTE_GUEST_ASCE | (pgste.nodat ? IPTE_NODAT : 0);
2011b09c13cSSteffen Eiden 
2021b09c13cSSteffen Eiden 		if (machine_has_tlb_guest())
2031b09c13cSSteffen Eiden 			__ptep_ipte(gfn_to_gpa(gfn), (void *)ptep, opts, asce.val, IPTE_GLOBAL);
2041b09c13cSSteffen Eiden 		else
2051b09c13cSSteffen Eiden 			__ptep_ipte(gfn_to_gpa(gfn), (void *)ptep, 0, 0, IPTE_GLOBAL);
2061b09c13cSSteffen Eiden 	}
2071b09c13cSSteffen Eiden 
2081b09c13cSSteffen Eiden 	if (uses_skeys) {
2091b09c13cSSteffen Eiden 		if (old.h.i && !new.h.i)
2101b09c13cSSteffen Eiden 			/* Invalid to valid: restore storage keys from PGSTE. */
2111b09c13cSSteffen Eiden 			dat_set_storage_key_from_pgste(new, pgste);
2121b09c13cSSteffen Eiden 		else if (!old.h.i && new.h.i)
2131b09c13cSSteffen Eiden 			/* Valid to invalid: save storage keys to PGSTE. */
2141b09c13cSSteffen Eiden 			pgste = dat_save_storage_key_into_pgste(old, pgste);
2151b09c13cSSteffen Eiden 		else if (!old.h.i && !new.h.i)
2161b09c13cSSteffen Eiden 			/* Valid to valid: move storage keys. */
2171b09c13cSSteffen Eiden 			if (old.h.pfra != new.h.pfra)
2181b09c13cSSteffen Eiden 				dat_move_storage_key(old, new);
2191b09c13cSSteffen Eiden 		/* Invalid to invalid: nothing to do. */
2201b09c13cSSteffen Eiden 	}
2211b09c13cSSteffen Eiden 
2221b09c13cSSteffen Eiden 	WRITE_ONCE(*ptep, new);
2231b09c13cSSteffen Eiden 	return pgste;
2241b09c13cSSteffen Eiden }
2251b09c13cSSteffen Eiden 
2261b09c13cSSteffen Eiden /*
2271b09c13cSSteffen Eiden  * dat_split_ste() - Split a segment table entry into page table entries.
2281b09c13cSSteffen Eiden  *
2291b09c13cSSteffen Eiden  * Context: This function is assumed to be called with kvm->mmu_lock held.
2301b09c13cSSteffen Eiden  *
2311b09c13cSSteffen Eiden  * Return: 0 in case of success, -ENOMEM if running out of memory.
2321b09c13cSSteffen Eiden  */
2331b09c13cSSteffen Eiden static int dat_split_ste(struct kvm_s390_mmu_cache *mc, union pmd *pmdp, gfn_t gfn,
2341b09c13cSSteffen Eiden 			 union asce asce, bool uses_skeys)
2351b09c13cSSteffen Eiden {
2361b09c13cSSteffen Eiden 	union pgste pgste_init;
2371b09c13cSSteffen Eiden 	struct page_table *pt;
2381b09c13cSSteffen Eiden 	union pmd new, old;
2391b09c13cSSteffen Eiden 	union pte init;
2401b09c13cSSteffen Eiden 	int i;
2411b09c13cSSteffen Eiden 
2421b09c13cSSteffen Eiden 	BUG_ON(!mc);
2431b09c13cSSteffen Eiden 	old = READ_ONCE(*pmdp);
2441b09c13cSSteffen Eiden 
2451b09c13cSSteffen Eiden 	/* Already split, nothing to do. */
2461b09c13cSSteffen Eiden 	if (!old.h.i && !old.h.fc)
2471b09c13cSSteffen Eiden 		return 0;
2481b09c13cSSteffen Eiden 
2491b09c13cSSteffen Eiden 	pt = dat_alloc_pt_noinit(mc);
2501b09c13cSSteffen Eiden 	if (!pt)
2511b09c13cSSteffen Eiden 		return -ENOMEM;
2521b09c13cSSteffen Eiden 	new.val = virt_to_phys(pt);
2531b09c13cSSteffen Eiden 
2541b09c13cSSteffen Eiden 	while (old.h.i || old.h.fc) {
2551b09c13cSSteffen Eiden 		init.val = pmd_origin_large(old);
2561b09c13cSSteffen Eiden 		init.h.p = old.h.p;
2571b09c13cSSteffen Eiden 		init.h.i = old.h.i;
2581b09c13cSSteffen Eiden 		init.s.d = old.s.fc1.d;
2591b09c13cSSteffen Eiden 		init.s.w = old.s.fc1.w;
2601b09c13cSSteffen Eiden 		init.s.y = old.s.fc1.y;
2611b09c13cSSteffen Eiden 		init.s.sd = old.s.fc1.sd;
2621b09c13cSSteffen Eiden 		init.s.pr = old.s.fc1.pr;
2631b09c13cSSteffen Eiden 		pgste_init.val = 0;
2641b09c13cSSteffen Eiden 		if (old.h.fc) {
2651b09c13cSSteffen Eiden 			for (i = 0; i < _PAGE_ENTRIES; i++)
2661b09c13cSSteffen Eiden 				pt->ptes[i].val = init.val | i * PAGE_SIZE;
2671b09c13cSSteffen Eiden 			/* No need to take locks as the page table is not installed yet. */
2681b09c13cSSteffen Eiden 			pgste_init.prefix_notif = old.s.fc1.prefix_notif;
2691b09c13cSSteffen Eiden 			pgste_init.vsie_notif = old.s.fc1.vsie_notif;
2701b09c13cSSteffen Eiden 			pgste_init.vsie_gmem = old.s.fc1.vsie_notif;
2711b09c13cSSteffen Eiden 			pgste_init.pcl = uses_skeys && init.h.i;
2721b09c13cSSteffen Eiden 			dat_init_pgstes(pt, pgste_init.val);
2731b09c13cSSteffen Eiden 		} else {
2741b09c13cSSteffen Eiden 			dat_init_page_table(pt, init.val, 0);
2751b09c13cSSteffen Eiden 		}
2761b09c13cSSteffen Eiden 
2771b09c13cSSteffen Eiden 		if (dat_pmdp_xchg_atomic(pmdp, old, new, gfn, asce)) {
2781b09c13cSSteffen Eiden 			if (!pgste_init.pcl)
2791b09c13cSSteffen Eiden 				return 0;
2801b09c13cSSteffen Eiden 			for (i = 0; i < _PAGE_ENTRIES; i++) {
2811b09c13cSSteffen Eiden 				union pgste pgste = pt->pgstes[i];
2821b09c13cSSteffen Eiden 
2831b09c13cSSteffen Eiden 				pgste = dat_save_storage_key_into_pgste(pt->ptes[i], pgste);
2841b09c13cSSteffen Eiden 				pgste_set_unlock(pt->ptes + i, pgste);
2851b09c13cSSteffen Eiden 			}
2861b09c13cSSteffen Eiden 			return 0;
2871b09c13cSSteffen Eiden 		}
2881b09c13cSSteffen Eiden 		old = READ_ONCE(*pmdp);
2891b09c13cSSteffen Eiden 	}
2901b09c13cSSteffen Eiden 
2911b09c13cSSteffen Eiden 	dat_free_pt(pt);
2921b09c13cSSteffen Eiden 	return 0;
2931b09c13cSSteffen Eiden }
2941b09c13cSSteffen Eiden 
2951b09c13cSSteffen Eiden /*
2961b09c13cSSteffen Eiden  * dat_split_crste() - Split a crste into smaller crstes.
2971b09c13cSSteffen Eiden  *
2981b09c13cSSteffen Eiden  * Context: This function is assumed to be called with kvm->mmu_lock held.
2991b09c13cSSteffen Eiden  *
3001b09c13cSSteffen Eiden  * Return: %0 in case of success, %-ENOMEM if running out of memory.
3011b09c13cSSteffen Eiden  */
3021b09c13cSSteffen Eiden static int dat_split_crste(struct kvm_s390_mmu_cache *mc, union crste *crstep,
3031b09c13cSSteffen Eiden 			   gfn_t gfn, union asce asce, bool uses_skeys)
3041b09c13cSSteffen Eiden {
3051b09c13cSSteffen Eiden 	struct crst_table *table;
3061b09c13cSSteffen Eiden 	union crste old, new, init;
3071b09c13cSSteffen Eiden 	int i;
3081b09c13cSSteffen Eiden 
3091b09c13cSSteffen Eiden 	old = READ_ONCE(*crstep);
3101b09c13cSSteffen Eiden 	if (is_pmd(old))
3111b09c13cSSteffen Eiden 		return dat_split_ste(mc, &crstep->pmd, gfn, asce, uses_skeys);
3121b09c13cSSteffen Eiden 
3131b09c13cSSteffen Eiden 	BUG_ON(!mc);
3141b09c13cSSteffen Eiden 
3151b09c13cSSteffen Eiden 	/* Already split, nothing to do. */
3161b09c13cSSteffen Eiden 	if (!old.h.i && !old.h.fc)
3171b09c13cSSteffen Eiden 		return 0;
3181b09c13cSSteffen Eiden 
3191b09c13cSSteffen Eiden 	table = dat_alloc_crst_noinit(mc);
3201b09c13cSSteffen Eiden 	if (!table)
3211b09c13cSSteffen Eiden 		return -ENOMEM;
3221b09c13cSSteffen Eiden 
3231b09c13cSSteffen Eiden 	new.val = virt_to_phys(table);
3241b09c13cSSteffen Eiden 	new.h.tt = old.h.tt;
3251b09c13cSSteffen Eiden 	new.h.fc0.tl = _REGION_ENTRY_LENGTH;
3261b09c13cSSteffen Eiden 
3271b09c13cSSteffen Eiden 	while (old.h.i || old.h.fc) {
3281b09c13cSSteffen Eiden 		init = old;
3291b09c13cSSteffen Eiden 		init.h.tt--;
3301b09c13cSSteffen Eiden 		if (old.h.fc) {
3311b09c13cSSteffen Eiden 			for (i = 0; i < _CRST_ENTRIES; i++)
3321b09c13cSSteffen Eiden 				table->crstes[i].val = init.val | i * HPAGE_SIZE;
3331b09c13cSSteffen Eiden 		} else {
3341b09c13cSSteffen Eiden 			crst_table_init((void *)table, init.val);
3351b09c13cSSteffen Eiden 		}
3361b09c13cSSteffen Eiden 		if (dat_crstep_xchg_atomic(crstep, old, new, gfn, asce))
3371b09c13cSSteffen Eiden 			return 0;
3381b09c13cSSteffen Eiden 		old = READ_ONCE(*crstep);
3391b09c13cSSteffen Eiden 	}
3401b09c13cSSteffen Eiden 
3411b09c13cSSteffen Eiden 	dat_free_crst(table);
3421b09c13cSSteffen Eiden 	return 0;
3431b09c13cSSteffen Eiden }
3441b09c13cSSteffen Eiden 
3451b09c13cSSteffen Eiden /**
3461b09c13cSSteffen Eiden  * dat_entry_walk() - Walk the gmap page tables.
3471b09c13cSSteffen Eiden  * @mc: Cache to use to allocate dat tables, if needed; can be NULL if neither
3481b09c13cSSteffen Eiden  *      %DAT_WALK_SPLIT or %DAT_WALK_ALLOC is specified in @flags.
3491b09c13cSSteffen Eiden  * @gfn: Guest frame.
3501b09c13cSSteffen Eiden  * @asce: The ASCE of the address space.
3511b09c13cSSteffen Eiden  * @flags: Flags from WALK_* macros.
3521b09c13cSSteffen Eiden  * @walk_level: Level to walk to, from LEVEL_* macros.
3531b09c13cSSteffen Eiden  * @last: Will be filled the last visited non-pte DAT entry.
3541b09c13cSSteffen Eiden  * @ptepp: Will be filled the last visited pte entry, if any, otherwise NULL.
3551b09c13cSSteffen Eiden  *
3561b09c13cSSteffen Eiden  * Returns a table entry pointer for the given guest address and @walk_level.
3571b09c13cSSteffen Eiden  *
3581b09c13cSSteffen Eiden  * The @flags have the following meanings:
3591b09c13cSSteffen Eiden  * * %DAT_WALK_IGN_HOLES: consider holes as normal table entries
3601b09c13cSSteffen Eiden  * * %DAT_WALK_ALLOC: allocate new tables to reach the requested level, if needed
3611b09c13cSSteffen Eiden  * * %DAT_WALK_SPLIT: split existing large pages to reach the requested level, if needed
3621b09c13cSSteffen Eiden  * * %DAT_WALK_LEAF: return successfully whenever a large page is encountered
3631b09c13cSSteffen Eiden  * * %DAT_WALK_ANY: return successfully even if the requested level could not be reached
3641b09c13cSSteffen Eiden  * * %DAT_WALK_CONTINUE: walk to the requested level with the specified flags, and then try to
3651b09c13cSSteffen Eiden  *                       continue walking to ptes with only DAT_WALK_ANY
3661b09c13cSSteffen Eiden  * * %DAT_WALK_USES_SKEYS: storage keys are in use
3671b09c13cSSteffen Eiden  *
3681b09c13cSSteffen Eiden  * Context: called with kvm->mmu_lock held.
3691b09c13cSSteffen Eiden  *
3701b09c13cSSteffen Eiden  * Return:
3711b09c13cSSteffen Eiden  * * %PGM_ADDRESSING if the requested address lies outside memory
3721b09c13cSSteffen Eiden  * * a PIC number if the requested address lies in a memory hole of type _DAT_TOKEN_PIC
3731b09c13cSSteffen Eiden  * * %-EFAULT if the requested address lies inside a memory hole of a different type
3741b09c13cSSteffen Eiden  * * %-EINVAL if the given ASCE is not compatible with the requested level
3751b09c13cSSteffen Eiden  * * %-EFBIG if the requested level could not be reached because a larger frame was found
3761b09c13cSSteffen Eiden  * * %-ENOENT if the requested level could not be reached for other reasons
3771b09c13cSSteffen Eiden  * * %-ENOMEM if running out of memory while allocating or splitting a table
3781b09c13cSSteffen Eiden  */
3791b09c13cSSteffen Eiden int dat_entry_walk(struct kvm_s390_mmu_cache *mc, gfn_t gfn, union asce asce, int flags,
3801b09c13cSSteffen Eiden 		   int walk_level, union crste **last, union pte **ptepp)
3811b09c13cSSteffen Eiden {
3821b09c13cSSteffen Eiden 	union vaddress vaddr = { .addr = gfn_to_gpa(gfn) };
3831b09c13cSSteffen Eiden 	bool continue_anyway = flags & DAT_WALK_CONTINUE;
3841b09c13cSSteffen Eiden 	bool uses_skeys = flags & DAT_WALK_USES_SKEYS;
3851b09c13cSSteffen Eiden 	bool ign_holes = flags & DAT_WALK_IGN_HOLES;
3861b09c13cSSteffen Eiden 	bool allocate = flags & DAT_WALK_ALLOC;
3871b09c13cSSteffen Eiden 	bool split = flags & DAT_WALK_SPLIT;
3881b09c13cSSteffen Eiden 	bool leaf = flags & DAT_WALK_LEAF;
3891b09c13cSSteffen Eiden 	bool any = flags & DAT_WALK_ANY;
3901b09c13cSSteffen Eiden 	struct page_table *pgtable;
3911b09c13cSSteffen Eiden 	struct crst_table *table;
3921b09c13cSSteffen Eiden 	union crste entry;
3931b09c13cSSteffen Eiden 	int rc;
3941b09c13cSSteffen Eiden 
3951b09c13cSSteffen Eiden 	*last = NULL;
3961b09c13cSSteffen Eiden 	*ptepp = NULL;
3971b09c13cSSteffen Eiden 	if (WARN_ON_ONCE(unlikely(!asce.val)))
3981b09c13cSSteffen Eiden 		return -EINVAL;
3991b09c13cSSteffen Eiden 	if (WARN_ON_ONCE(unlikely(walk_level > asce.dt)))
4001b09c13cSSteffen Eiden 		return -EINVAL;
4011b09c13cSSteffen Eiden 	if (!asce_contains_gfn(asce, gfn))
4021b09c13cSSteffen Eiden 		return PGM_ADDRESSING;
4031b09c13cSSteffen Eiden 
4041b09c13cSSteffen Eiden 	table = dereference_asce(asce);
4051b09c13cSSteffen Eiden 	if (asce.dt >= ASCE_TYPE_REGION1) {
4061b09c13cSSteffen Eiden 		*last = table->crstes + vaddr.rfx;
4071b09c13cSSteffen Eiden 		entry = READ_ONCE(**last);
4081b09c13cSSteffen Eiden 		if (WARN_ON_ONCE(entry.h.tt != TABLE_TYPE_REGION1))
4091b09c13cSSteffen Eiden 			return -EINVAL;
4101b09c13cSSteffen Eiden 		if (crste_hole(entry) && !ign_holes)
4111b09c13cSSteffen Eiden 			return entry.tok.type == _DAT_TOKEN_PIC ? entry.tok.par : -EFAULT;
4121b09c13cSSteffen Eiden 		if (walk_level == TABLE_TYPE_REGION1)
4131b09c13cSSteffen Eiden 			return 0;
4141b09c13cSSteffen Eiden 		if (entry.pgd.h.i) {
4151b09c13cSSteffen Eiden 			if (!allocate)
4161b09c13cSSteffen Eiden 				return any ? 0 : -ENOENT;
4171b09c13cSSteffen Eiden 			rc = dat_split_crste(mc, *last, gfn, asce, uses_skeys);
4181b09c13cSSteffen Eiden 			if (rc)
4191b09c13cSSteffen Eiden 				return rc;
4201b09c13cSSteffen Eiden 			entry = READ_ONCE(**last);
4211b09c13cSSteffen Eiden 		}
4221b09c13cSSteffen Eiden 		table = dereference_crste(entry.pgd);
4231b09c13cSSteffen Eiden 	}
4241b09c13cSSteffen Eiden 
4251b09c13cSSteffen Eiden 	if (asce.dt >= ASCE_TYPE_REGION2) {
4261b09c13cSSteffen Eiden 		*last = table->crstes + vaddr.rsx;
4271b09c13cSSteffen Eiden 		entry = READ_ONCE(**last);
4281b09c13cSSteffen Eiden 		if (WARN_ON_ONCE(entry.h.tt != TABLE_TYPE_REGION2))
4291b09c13cSSteffen Eiden 			return -EINVAL;
4301b09c13cSSteffen Eiden 		if (crste_hole(entry) && !ign_holes)
4311b09c13cSSteffen Eiden 			return entry.tok.type == _DAT_TOKEN_PIC ? entry.tok.par : -EFAULT;
4321b09c13cSSteffen Eiden 		if (walk_level == TABLE_TYPE_REGION2)
4331b09c13cSSteffen Eiden 			return 0;
4341b09c13cSSteffen Eiden 		if (entry.p4d.h.i) {
4351b09c13cSSteffen Eiden 			if (!allocate)
4361b09c13cSSteffen Eiden 				return any ? 0 : -ENOENT;
4371b09c13cSSteffen Eiden 			rc = dat_split_crste(mc, *last, gfn, asce, uses_skeys);
4381b09c13cSSteffen Eiden 			if (rc)
4391b09c13cSSteffen Eiden 				return rc;
4401b09c13cSSteffen Eiden 			entry = READ_ONCE(**last);
4411b09c13cSSteffen Eiden 		}
4421b09c13cSSteffen Eiden 		table = dereference_crste(entry.p4d);
4431b09c13cSSteffen Eiden 	}
4441b09c13cSSteffen Eiden 
4451b09c13cSSteffen Eiden 	if (asce.dt >= ASCE_TYPE_REGION3) {
4461b09c13cSSteffen Eiden 		*last = table->crstes + vaddr.rtx;
4471b09c13cSSteffen Eiden 		entry = READ_ONCE(**last);
4481b09c13cSSteffen Eiden 		if (WARN_ON_ONCE(entry.h.tt != TABLE_TYPE_REGION3))
4491b09c13cSSteffen Eiden 			return -EINVAL;
4501b09c13cSSteffen Eiden 		if (crste_hole(entry) && !ign_holes)
4511b09c13cSSteffen Eiden 			return entry.tok.type == _DAT_TOKEN_PIC ? entry.tok.par : -EFAULT;
4521b09c13cSSteffen Eiden 		if (walk_level == TABLE_TYPE_REGION3 &&
4531b09c13cSSteffen Eiden 		    continue_anyway && !entry.pud.h.fc && !entry.h.i) {
4541b09c13cSSteffen Eiden 			walk_level = TABLE_TYPE_PAGE_TABLE;
4551b09c13cSSteffen Eiden 			allocate = false;
4561b09c13cSSteffen Eiden 		}
4571b09c13cSSteffen Eiden 		if (walk_level == TABLE_TYPE_REGION3 || ((leaf || any) && entry.pud.h.fc))
4581b09c13cSSteffen Eiden 			return 0;
4591b09c13cSSteffen Eiden 		if (entry.pud.h.i && !entry.pud.h.fc) {
4601b09c13cSSteffen Eiden 			if (!allocate)
4611b09c13cSSteffen Eiden 				return any ? 0 : -ENOENT;
4621b09c13cSSteffen Eiden 			rc = dat_split_crste(mc, *last, gfn, asce, uses_skeys);
4631b09c13cSSteffen Eiden 			if (rc)
4641b09c13cSSteffen Eiden 				return rc;
4651b09c13cSSteffen Eiden 			entry = READ_ONCE(**last);
4661b09c13cSSteffen Eiden 		}
4671b09c13cSSteffen Eiden 		if (walk_level <= TABLE_TYPE_SEGMENT && entry.pud.h.fc) {
4681b09c13cSSteffen Eiden 			if (!split)
4691b09c13cSSteffen Eiden 				return -EFBIG;
4701b09c13cSSteffen Eiden 			rc = dat_split_crste(mc, *last, gfn, asce, uses_skeys);
4711b09c13cSSteffen Eiden 			if (rc)
4721b09c13cSSteffen Eiden 				return rc;
4731b09c13cSSteffen Eiden 			entry = READ_ONCE(**last);
4741b09c13cSSteffen Eiden 		}
4751b09c13cSSteffen Eiden 		table = dereference_crste(entry.pud);
4761b09c13cSSteffen Eiden 	}
4771b09c13cSSteffen Eiden 
4781b09c13cSSteffen Eiden 	*last = table->crstes + vaddr.sx;
4791b09c13cSSteffen Eiden 	entry = READ_ONCE(**last);
4801b09c13cSSteffen Eiden 	if (WARN_ON_ONCE(entry.h.tt != TABLE_TYPE_SEGMENT))
4811b09c13cSSteffen Eiden 		return -EINVAL;
4821b09c13cSSteffen Eiden 	if (crste_hole(entry) && !ign_holes)
4831b09c13cSSteffen Eiden 		return entry.tok.type == _DAT_TOKEN_PIC ? entry.tok.par : -EFAULT;
4841b09c13cSSteffen Eiden 	if (continue_anyway && !entry.pmd.h.fc && !entry.h.i) {
4851b09c13cSSteffen Eiden 		walk_level = TABLE_TYPE_PAGE_TABLE;
4861b09c13cSSteffen Eiden 		allocate = false;
4871b09c13cSSteffen Eiden 	}
4881b09c13cSSteffen Eiden 	if (walk_level == TABLE_TYPE_SEGMENT || ((leaf || any) && entry.pmd.h.fc))
4891b09c13cSSteffen Eiden 		return 0;
4901b09c13cSSteffen Eiden 
4911b09c13cSSteffen Eiden 	if (entry.pmd.h.i && !entry.pmd.h.fc) {
4921b09c13cSSteffen Eiden 		if (!allocate)
4931b09c13cSSteffen Eiden 			return any ? 0 : -ENOENT;
4941b09c13cSSteffen Eiden 		rc = dat_split_ste(mc, &(*last)->pmd, gfn, asce, uses_skeys);
4951b09c13cSSteffen Eiden 		if (rc)
4961b09c13cSSteffen Eiden 			return rc;
4971b09c13cSSteffen Eiden 		entry = READ_ONCE(**last);
4981b09c13cSSteffen Eiden 	}
4991b09c13cSSteffen Eiden 	if (walk_level <= TABLE_TYPE_PAGE_TABLE && entry.pmd.h.fc) {
5001b09c13cSSteffen Eiden 		if (!split)
5011b09c13cSSteffen Eiden 			return -EFBIG;
5021b09c13cSSteffen Eiden 		rc = dat_split_ste(mc, &(*last)->pmd, gfn, asce, uses_skeys);
5031b09c13cSSteffen Eiden 		if (rc)
5041b09c13cSSteffen Eiden 			return rc;
5051b09c13cSSteffen Eiden 		entry = READ_ONCE(**last);
5061b09c13cSSteffen Eiden 	}
5071b09c13cSSteffen Eiden 	pgtable = dereference_pmd(entry.pmd);
5081b09c13cSSteffen Eiden 	*ptepp = pgtable->ptes + vaddr.px;
5091b09c13cSSteffen Eiden 	if (pte_hole(**ptepp) && !ign_holes)
5101b09c13cSSteffen Eiden 		return (*ptepp)->tok.type == _DAT_TOKEN_PIC ? (*ptepp)->tok.par : -EFAULT;
5111b09c13cSSteffen Eiden 	return 0;
5121b09c13cSSteffen Eiden }
5131b09c13cSSteffen Eiden 
5141b09c13cSSteffen Eiden static long dat_pte_walk_range(gfn_t gfn, gfn_t end, struct page_table *table, struct dat_walk *w)
5151b09c13cSSteffen Eiden {
5161b09c13cSSteffen Eiden 	unsigned int idx = gfn & (_PAGE_ENTRIES - 1);
5171b09c13cSSteffen Eiden 	long rc = 0;
5181b09c13cSSteffen Eiden 
5191b09c13cSSteffen Eiden 	for ( ; gfn < end; idx++, gfn++) {
5201b09c13cSSteffen Eiden 		if (pte_hole(READ_ONCE(table->ptes[idx]))) {
5211b09c13cSSteffen Eiden 			if (!(w->flags & DAT_WALK_IGN_HOLES))
5221b09c13cSSteffen Eiden 				return -EFAULT;
5231b09c13cSSteffen Eiden 			if (!(w->flags & DAT_WALK_ANY))
5241b09c13cSSteffen Eiden 				continue;
5251b09c13cSSteffen Eiden 		}
5261b09c13cSSteffen Eiden 
5271b09c13cSSteffen Eiden 		rc = w->ops->pte_entry(table->ptes + idx, gfn, gfn + 1, w);
5281b09c13cSSteffen Eiden 		if (rc)
5291b09c13cSSteffen Eiden 			break;
5301b09c13cSSteffen Eiden 	}
5311b09c13cSSteffen Eiden 	return rc;
5321b09c13cSSteffen Eiden }
5331b09c13cSSteffen Eiden 
5341b09c13cSSteffen Eiden static long dat_crste_walk_range(gfn_t start, gfn_t end, struct crst_table *table,
5351b09c13cSSteffen Eiden 				 struct dat_walk *walk)
5361b09c13cSSteffen Eiden {
5371b09c13cSSteffen Eiden 	unsigned long idx, cur_shift, cur_size;
5381b09c13cSSteffen Eiden 	dat_walk_op the_op;
5391b09c13cSSteffen Eiden 	union crste crste;
5401b09c13cSSteffen Eiden 	gfn_t cur, next;
5411b09c13cSSteffen Eiden 	long rc = 0;
5421b09c13cSSteffen Eiden 
5431b09c13cSSteffen Eiden 	cur_shift = 8 + table->crstes[0].h.tt * 11;
5441b09c13cSSteffen Eiden 	idx = (start >> cur_shift) & (_CRST_ENTRIES - 1);
5451b09c13cSSteffen Eiden 	cur_size = 1UL << cur_shift;
5461b09c13cSSteffen Eiden 
5471b09c13cSSteffen Eiden 	for (cur = ALIGN_DOWN(start, cur_size); cur < end; idx++, cur = next) {
5481b09c13cSSteffen Eiden 		next = cur + cur_size;
5491b09c13cSSteffen Eiden 		walk->last = table->crstes + idx;
5501b09c13cSSteffen Eiden 		crste = READ_ONCE(*walk->last);
5511b09c13cSSteffen Eiden 
5521b09c13cSSteffen Eiden 		if (crste_hole(crste)) {
5531b09c13cSSteffen Eiden 			if (!(walk->flags & DAT_WALK_IGN_HOLES))
5541b09c13cSSteffen Eiden 				return -EFAULT;
5551b09c13cSSteffen Eiden 			if (!(walk->flags & DAT_WALK_ANY))
5561b09c13cSSteffen Eiden 				continue;
5571b09c13cSSteffen Eiden 		}
5581b09c13cSSteffen Eiden 
5591b09c13cSSteffen Eiden 		the_op = walk->ops->crste_ops[crste.h.tt];
5601b09c13cSSteffen Eiden 		if (the_op) {
5611b09c13cSSteffen Eiden 			rc = the_op(walk->last, cur, next, walk);
5621b09c13cSSteffen Eiden 			crste = READ_ONCE(*walk->last);
5631b09c13cSSteffen Eiden 		}
5641b09c13cSSteffen Eiden 		if (rc)
5651b09c13cSSteffen Eiden 			break;
5661b09c13cSSteffen Eiden 		if (!crste.h.i && !crste.h.fc) {
5671b09c13cSSteffen Eiden 			if (!is_pmd(crste))
5681b09c13cSSteffen Eiden 				rc = dat_crste_walk_range(max(start, cur), min(end, next),
5691b09c13cSSteffen Eiden 							  _dereference_crste(crste), walk);
5701b09c13cSSteffen Eiden 			else if (walk->ops->pte_entry)
5711b09c13cSSteffen Eiden 				rc = dat_pte_walk_range(max(start, cur), min(end, next),
5721b09c13cSSteffen Eiden 							dereference_pmd(crste.pmd), walk);
5731b09c13cSSteffen Eiden 			if (rc)
5741b09c13cSSteffen Eiden 				break;
5751b09c13cSSteffen Eiden 		}
5761b09c13cSSteffen Eiden 	}
5771b09c13cSSteffen Eiden 	return rc;
5781b09c13cSSteffen Eiden }
5791b09c13cSSteffen Eiden 
5801b09c13cSSteffen Eiden /**
5811b09c13cSSteffen Eiden  * _dat_walk_gfn_range() - Walk DAT tables.
5821b09c13cSSteffen Eiden  * @start: The first guest page frame to walk.
5831b09c13cSSteffen Eiden  * @end: The guest page frame immediately after the last one to walk.
5841b09c13cSSteffen Eiden  * @asce: The ASCE of the guest mapping.
5851b09c13cSSteffen Eiden  * @ops: The gmap_walk_ops that will be used to perform the walk.
5861b09c13cSSteffen Eiden  * @flags: Flags from WALK_* (currently only WALK_IGN_HOLES is supported).
5871b09c13cSSteffen Eiden  * @priv: Will be passed as-is to the callbacks.
5881b09c13cSSteffen Eiden  *
5891b09c13cSSteffen Eiden  * Any callback returning non-zero causes the walk to stop immediately.
5901b09c13cSSteffen Eiden  *
5911b09c13cSSteffen Eiden  * Return: %-EINVAL in case of error, %-EFAULT if @start is too high for the
5921b09c13cSSteffen Eiden  *         given ASCE unless the DAT_WALK_IGN_HOLES flag is specified,
5931b09c13cSSteffen Eiden  *         otherwise it returns whatever the callbacks return.
5941b09c13cSSteffen Eiden  */
5951b09c13cSSteffen Eiden long _dat_walk_gfn_range(gfn_t start, gfn_t end, union asce asce,
5961b09c13cSSteffen Eiden 			 const struct dat_walk_ops *ops, int flags, void *priv)
5971b09c13cSSteffen Eiden {
5981b09c13cSSteffen Eiden 	struct crst_table *table = dereference_asce(asce);
5991b09c13cSSteffen Eiden 	struct dat_walk walk = {
6001b09c13cSSteffen Eiden 		.ops	= ops,
6011b09c13cSSteffen Eiden 		.asce	= asce,
6021b09c13cSSteffen Eiden 		.priv	= priv,
6031b09c13cSSteffen Eiden 		.flags	= flags,
6041b09c13cSSteffen Eiden 		.start	= start,
6051b09c13cSSteffen Eiden 		.end	= end,
6061b09c13cSSteffen Eiden 	};
6071b09c13cSSteffen Eiden 
6081b09c13cSSteffen Eiden 	if (WARN_ON_ONCE(unlikely(!asce.val)))
6091b09c13cSSteffen Eiden 		return -EINVAL;
6101b09c13cSSteffen Eiden 	if (!asce_contains_gfn(asce, start))
6111b09c13cSSteffen Eiden 		return (flags & DAT_WALK_IGN_HOLES) ? 0 : -EFAULT;
6121b09c13cSSteffen Eiden 
6131b09c13cSSteffen Eiden 	return dat_crste_walk_range(start, min(end, asce_end(asce)), table, &walk);
6141b09c13cSSteffen Eiden }
6151b09c13cSSteffen Eiden 
616a07276d5SSteffen Eiden #if KVM_S390_MANAGES_S390_GUEST
6171b09c13cSSteffen Eiden int dat_get_storage_key(union asce asce, gfn_t gfn, union skey *skey)
6181b09c13cSSteffen Eiden {
6191b09c13cSSteffen Eiden 	union crste *crstep;
6201b09c13cSSteffen Eiden 	union pgste pgste;
6211b09c13cSSteffen Eiden 	union pte *ptep;
6221b09c13cSSteffen Eiden 	int rc;
6231b09c13cSSteffen Eiden 
6241b09c13cSSteffen Eiden 	skey->skey = 0;
6251b09c13cSSteffen Eiden 	rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep);
6261b09c13cSSteffen Eiden 	if (rc)
6271b09c13cSSteffen Eiden 		return rc;
6281b09c13cSSteffen Eiden 
6291b09c13cSSteffen Eiden 	if (!ptep) {
6301b09c13cSSteffen Eiden 		union crste crste;
6311b09c13cSSteffen Eiden 
6321b09c13cSSteffen Eiden 		crste = READ_ONCE(*crstep);
6331b09c13cSSteffen Eiden 		if (!crste.h.fc || !crste.s.fc1.pr)
6341b09c13cSSteffen Eiden 			return 0;
6351b09c13cSSteffen Eiden 		skey->skey = page_get_storage_key(large_crste_to_phys(crste, gfn));
6361b09c13cSSteffen Eiden 		return 0;
6371b09c13cSSteffen Eiden 	}
6381b09c13cSSteffen Eiden 	pgste = pgste_get_lock(ptep);
6391b09c13cSSteffen Eiden 	if (ptep->h.i) {
6401b09c13cSSteffen Eiden 		skey->acc = pgste.acc;
6411b09c13cSSteffen Eiden 		skey->fp = pgste.fp;
6421b09c13cSSteffen Eiden 	} else {
6431b09c13cSSteffen Eiden 		skey->skey = page_get_storage_key(pte_origin(*ptep));
6441b09c13cSSteffen Eiden 	}
6451b09c13cSSteffen Eiden 	skey->r |= pgste.gr;
6461b09c13cSSteffen Eiden 	skey->c |= pgste.gc;
6471b09c13cSSteffen Eiden 	pgste_set_unlock(ptep, pgste);
6481b09c13cSSteffen Eiden 	return 0;
6491b09c13cSSteffen Eiden }
6501b09c13cSSteffen Eiden 
6511b09c13cSSteffen Eiden static void dat_update_ptep_sd(union pgste old, union pgste pgste, union pte *ptep)
6521b09c13cSSteffen Eiden {
6531b09c13cSSteffen Eiden 	if (pgste.acc != old.acc || pgste.fp != old.fp || pgste.gr != old.gr || pgste.gc != old.gc)
6541b09c13cSSteffen Eiden 		__atomic64_or(_PAGE_SD, &ptep->val);
6551b09c13cSSteffen Eiden }
6561b09c13cSSteffen Eiden 
6571b09c13cSSteffen Eiden int dat_set_storage_key(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gfn,
6581b09c13cSSteffen Eiden 			union skey skey, bool nq)
6591b09c13cSSteffen Eiden {
6601b09c13cSSteffen Eiden 	union pgste pgste, old;
6611b09c13cSSteffen Eiden 	union crste *crstep;
6621b09c13cSSteffen Eiden 	union pte *ptep;
6631b09c13cSSteffen Eiden 	int rc;
6641b09c13cSSteffen Eiden 
6651b09c13cSSteffen Eiden 	rc = dat_entry_walk(mc, gfn, asce, DAT_WALK_LEAF_ALLOC, TABLE_TYPE_PAGE_TABLE,
6661b09c13cSSteffen Eiden 			    &crstep, &ptep);
6671b09c13cSSteffen Eiden 	if (rc)
6681b09c13cSSteffen Eiden 		return rc;
6691b09c13cSSteffen Eiden 
6701b09c13cSSteffen Eiden 	if (!ptep) {
6711b09c13cSSteffen Eiden 		page_set_storage_key(large_crste_to_phys(*crstep, gfn), skey.skey, !nq);
6721b09c13cSSteffen Eiden 		return 0;
6731b09c13cSSteffen Eiden 	}
6741b09c13cSSteffen Eiden 
6751b09c13cSSteffen Eiden 	old = pgste_get_lock(ptep);
6761b09c13cSSteffen Eiden 	pgste = old;
6771b09c13cSSteffen Eiden 
6781b09c13cSSteffen Eiden 	pgste.acc = skey.acc;
6791b09c13cSSteffen Eiden 	pgste.fp = skey.fp;
6801b09c13cSSteffen Eiden 	pgste.gc = skey.c;
6811b09c13cSSteffen Eiden 	pgste.gr = skey.r;
6821b09c13cSSteffen Eiden 
6831b09c13cSSteffen Eiden 	if (!ptep->h.i) {
6841b09c13cSSteffen Eiden 		union skey old_skey;
6851b09c13cSSteffen Eiden 
6861b09c13cSSteffen Eiden 		old_skey.skey = page_get_storage_key(pte_origin(*ptep));
6871b09c13cSSteffen Eiden 		pgste.hc |= old_skey.c;
6881b09c13cSSteffen Eiden 		pgste.hr |= old_skey.r;
6891b09c13cSSteffen Eiden 		old_skey.c = old.gc;
6901b09c13cSSteffen Eiden 		old_skey.r = old.gr;
6911b09c13cSSteffen Eiden 		skey.r = 0;
6921b09c13cSSteffen Eiden 		skey.c = 0;
6931b09c13cSSteffen Eiden 		page_set_storage_key(pte_origin(*ptep), skey.skey, !nq);
6941b09c13cSSteffen Eiden 	}
6951b09c13cSSteffen Eiden 
6961b09c13cSSteffen Eiden 	dat_update_ptep_sd(old, pgste, ptep);
6971b09c13cSSteffen Eiden 	pgste_set_unlock(ptep, pgste);
6981b09c13cSSteffen Eiden 	return 0;
6991b09c13cSSteffen Eiden }
7001b09c13cSSteffen Eiden 
7011b09c13cSSteffen Eiden static bool page_cond_set_storage_key(phys_addr_t paddr, union skey skey, union skey *oldkey,
7021b09c13cSSteffen Eiden 				      bool nq, bool mr, bool mc)
7031b09c13cSSteffen Eiden {
7041b09c13cSSteffen Eiden 	oldkey->skey = page_get_storage_key(paddr);
7051b09c13cSSteffen Eiden 	if (oldkey->acc == skey.acc && oldkey->fp == skey.fp &&
7061b09c13cSSteffen Eiden 	    (oldkey->r == skey.r || mr) && (oldkey->c == skey.c || mc))
7071b09c13cSSteffen Eiden 		return false;
7081b09c13cSSteffen Eiden 	page_set_storage_key(paddr, skey.skey, !nq);
7091b09c13cSSteffen Eiden 	return true;
7101b09c13cSSteffen Eiden }
7111b09c13cSSteffen Eiden 
7121b09c13cSSteffen Eiden int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gfn_t gfn,
7131b09c13cSSteffen Eiden 			     union skey skey, union skey *oldkey, bool nq, bool mr, bool mc)
7141b09c13cSSteffen Eiden {
7151b09c13cSSteffen Eiden 	union pgste pgste, old;
7161b09c13cSSteffen Eiden 	union crste *crstep;
7171b09c13cSSteffen Eiden 	union skey prev;
7181b09c13cSSteffen Eiden 	union pte *ptep;
7191b09c13cSSteffen Eiden 	int rc;
7201b09c13cSSteffen Eiden 
7211b09c13cSSteffen Eiden 	rc = dat_entry_walk(mmc, gfn, asce, DAT_WALK_LEAF_ALLOC, TABLE_TYPE_PAGE_TABLE,
7221b09c13cSSteffen Eiden 			    &crstep, &ptep);
7231b09c13cSSteffen Eiden 	if (rc)
7241b09c13cSSteffen Eiden 		return rc;
7251b09c13cSSteffen Eiden 
7261b09c13cSSteffen Eiden 	if (!ptep) {
7271b09c13cSSteffen Eiden 		if (!oldkey)
7281b09c13cSSteffen Eiden 			oldkey = &prev;
7291b09c13cSSteffen Eiden 		return page_cond_set_storage_key(large_crste_to_phys(*crstep, gfn), skey, oldkey,
7301b09c13cSSteffen Eiden 						 nq, mr, mc);
7311b09c13cSSteffen Eiden 	}
7321b09c13cSSteffen Eiden 
7331b09c13cSSteffen Eiden 	old = pgste_get_lock(ptep);
7341b09c13cSSteffen Eiden 	pgste = old;
7351b09c13cSSteffen Eiden 
7361b09c13cSSteffen Eiden 	rc = 1;
7371b09c13cSSteffen Eiden 	pgste.acc = skey.acc;
7381b09c13cSSteffen Eiden 	pgste.fp = skey.fp;
7391b09c13cSSteffen Eiden 	pgste.gc = skey.c;
7401b09c13cSSteffen Eiden 	pgste.gr = skey.r;
7411b09c13cSSteffen Eiden 	prev.skey = 0;
7421b09c13cSSteffen Eiden 
7431b09c13cSSteffen Eiden 	if (!ptep->h.i) {
7441b09c13cSSteffen Eiden 		rc = page_cond_set_storage_key(pte_origin(*ptep), skey, &prev, nq, mr, mc);
7451b09c13cSSteffen Eiden 		pgste.hc |= prev.c;
7461b09c13cSSteffen Eiden 		pgste.hr |= prev.r;
7471b09c13cSSteffen Eiden 		prev.c |= old.gc;
7481b09c13cSSteffen Eiden 		prev.r |= old.gr;
7491b09c13cSSteffen Eiden 	} else {
7501b09c13cSSteffen Eiden 		prev.acc = old.acc;
7511b09c13cSSteffen Eiden 		prev.fp = old.fp;
7521b09c13cSSteffen Eiden 		prev.c = old.gc;
7531b09c13cSSteffen Eiden 		prev.r = old.gr;
7541b09c13cSSteffen Eiden 	}
7551b09c13cSSteffen Eiden 	if (oldkey)
7561b09c13cSSteffen Eiden 		*oldkey = prev;
7571b09c13cSSteffen Eiden 
7581b09c13cSSteffen Eiden 	dat_update_ptep_sd(old, pgste, ptep);
7591b09c13cSSteffen Eiden 	pgste_set_unlock(ptep, pgste);
7601b09c13cSSteffen Eiden 	return rc;
7611b09c13cSSteffen Eiden }
7621b09c13cSSteffen Eiden 
7631b09c13cSSteffen Eiden int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey)
7641b09c13cSSteffen Eiden {
7651b09c13cSSteffen Eiden 	union pgste pgste, old;
7661b09c13cSSteffen Eiden 	union crste *crstep;
7671b09c13cSSteffen Eiden 	union pte *ptep;
7681b09c13cSSteffen Eiden 	int rc;
7691b09c13cSSteffen Eiden 
7701b09c13cSSteffen Eiden 	skey->skey = 0;
7711b09c13cSSteffen Eiden 
7721b09c13cSSteffen Eiden 	rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep);
7731b09c13cSSteffen Eiden 	if (rc)
7741b09c13cSSteffen Eiden 		return rc;
7751b09c13cSSteffen Eiden 
7761b09c13cSSteffen Eiden 	if (!ptep) {
7771b09c13cSSteffen Eiden 		union crste crste = READ_ONCE(*crstep);
7781b09c13cSSteffen Eiden 
7791b09c13cSSteffen Eiden 		if (!crste.h.fc || !crste.s.fc1.pr)
7801b09c13cSSteffen Eiden 			return 0;
7811b09c13cSSteffen Eiden 		skey->skey = page_reset_referenced(large_crste_to_phys(*crstep, gfn)) << 1;
7821b09c13cSSteffen Eiden 		return 0;
7831b09c13cSSteffen Eiden 	}
7841b09c13cSSteffen Eiden 	old = pgste_get_lock(ptep);
7851b09c13cSSteffen Eiden 	pgste = old;
7861b09c13cSSteffen Eiden 
7871b09c13cSSteffen Eiden 	if (!ptep->h.i) {
7881b09c13cSSteffen Eiden 		skey->skey = page_reset_referenced(pte_origin(*ptep)) << 1;
7891b09c13cSSteffen Eiden 		pgste.hr = skey->r;
7901b09c13cSSteffen Eiden 	}
7911b09c13cSSteffen Eiden 	skey->r |= pgste.gr;
7921b09c13cSSteffen Eiden 	skey->c |= pgste.gc;
7931b09c13cSSteffen Eiden 	pgste.gr = 0;
7941b09c13cSSteffen Eiden 
7951b09c13cSSteffen Eiden 	dat_update_ptep_sd(old, pgste, ptep);
7961b09c13cSSteffen Eiden 	pgste_set_unlock(ptep, pgste);
7971b09c13cSSteffen Eiden 	return 0;
7981b09c13cSSteffen Eiden }
7991b09c13cSSteffen Eiden 
8001b09c13cSSteffen Eiden static long dat_reset_skeys_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
8011b09c13cSSteffen Eiden {
8021b09c13cSSteffen Eiden 	union pgste pgste;
8031b09c13cSSteffen Eiden 
8041b09c13cSSteffen Eiden 	pgste = pgste_get_lock(ptep);
8051b09c13cSSteffen Eiden 	pgste.acc = 0;
8061b09c13cSSteffen Eiden 	pgste.fp = 0;
8071b09c13cSSteffen Eiden 	pgste.gr = 0;
8081b09c13cSSteffen Eiden 	pgste.gc = 0;
8091b09c13cSSteffen Eiden 	if (ptep->s.pr)
8101b09c13cSSteffen Eiden 		page_set_storage_key(pte_origin(*ptep), PAGE_DEFAULT_KEY, 1);
8111b09c13cSSteffen Eiden 	pgste_set_unlock(ptep, pgste);
8121b09c13cSSteffen Eiden 
8131b09c13cSSteffen Eiden 	if (need_resched())
8141b09c13cSSteffen Eiden 		return next;
8151b09c13cSSteffen Eiden 	return 0;
8161b09c13cSSteffen Eiden }
8171b09c13cSSteffen Eiden 
8181b09c13cSSteffen Eiden static long dat_reset_skeys_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
8191b09c13cSSteffen Eiden {
8201b09c13cSSteffen Eiden 	phys_addr_t addr, end, origin = crste_origin_large(*crstep);
8211b09c13cSSteffen Eiden 
8221b09c13cSSteffen Eiden 	if (!crstep->h.fc || !crstep->s.fc1.pr)
8231b09c13cSSteffen Eiden 		return 0;
8241b09c13cSSteffen Eiden 
8251b09c13cSSteffen Eiden 	addr = ((max(gfn, walk->start) - gfn) << PAGE_SHIFT) + origin;
8261b09c13cSSteffen Eiden 	end = ((min(next, walk->end) - gfn) << PAGE_SHIFT) + origin;
8271b09c13cSSteffen Eiden 	while (ALIGN(addr + 1, _SEGMENT_SIZE) <= end)
8281b09c13cSSteffen Eiden 		addr = sske_frame(addr, PAGE_DEFAULT_KEY);
8291b09c13cSSteffen Eiden 	for ( ; addr < end; addr += PAGE_SIZE)
8301b09c13cSSteffen Eiden 		page_set_storage_key(addr, PAGE_DEFAULT_KEY, 1);
8311b09c13cSSteffen Eiden 
8321b09c13cSSteffen Eiden 	if (need_resched())
8331b09c13cSSteffen Eiden 		return next;
8341b09c13cSSteffen Eiden 	return 0;
8351b09c13cSSteffen Eiden }
8361b09c13cSSteffen Eiden 
8371b09c13cSSteffen Eiden long dat_reset_skeys(union asce asce, gfn_t start)
8381b09c13cSSteffen Eiden {
8391b09c13cSSteffen Eiden 	const struct dat_walk_ops ops = {
8401b09c13cSSteffen Eiden 		.pte_entry = dat_reset_skeys_pte,
8411b09c13cSSteffen Eiden 		.pmd_entry = dat_reset_skeys_crste,
8421b09c13cSSteffen Eiden 		.pud_entry = dat_reset_skeys_crste,
8431b09c13cSSteffen Eiden 	};
8441b09c13cSSteffen Eiden 
8451b09c13cSSteffen Eiden 	return _dat_walk_gfn_range(start, asce_end(asce), asce, &ops, DAT_WALK_IGN_HOLES, NULL);
8461b09c13cSSteffen Eiden }
847a07276d5SSteffen Eiden #endif /* KVM_S390_MANAGES_S390_GUEST */
8481b09c13cSSteffen Eiden 
8491b09c13cSSteffen Eiden struct slot_priv {
8501b09c13cSSteffen Eiden 	unsigned long token;
8511b09c13cSSteffen Eiden 	struct kvm_s390_mmu_cache *mc;
8521b09c13cSSteffen Eiden };
8531b09c13cSSteffen Eiden 
8541b09c13cSSteffen Eiden static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
8551b09c13cSSteffen Eiden {
8561b09c13cSSteffen Eiden 	struct slot_priv *p = walk->priv;
8571b09c13cSSteffen Eiden 	union crste dummy = { .val = p->token };
8581b09c13cSSteffen Eiden 	union pte new_pte, pte = READ_ONCE(*ptep);
8591b09c13cSSteffen Eiden 	union pgste pgste;
8601b09c13cSSteffen Eiden 
8611b09c13cSSteffen Eiden 	new_pte = _PTE_TOK(dummy.tok.type, dummy.tok.par);
8621b09c13cSSteffen Eiden 
8631b09c13cSSteffen Eiden 	/* Table entry already in the desired state. */
8641b09c13cSSteffen Eiden 	if (pte.val == new_pte.val)
8651b09c13cSSteffen Eiden 		return 0;
8661b09c13cSSteffen Eiden 
8671b09c13cSSteffen Eiden 	pgste = pgste_get_lock(ptep);
8681b09c13cSSteffen Eiden 	pgste = __dat_ptep_xchg(ptep, pgste, new_pte, gfn, walk->asce, false);
8691b09c13cSSteffen Eiden 	pgste.cmma_d = 0;
8701b09c13cSSteffen Eiden 	pgste_set_unlock(ptep, pgste);
8711b09c13cSSteffen Eiden 
8721b09c13cSSteffen Eiden 	return 0;
8731b09c13cSSteffen Eiden }
8741b09c13cSSteffen Eiden 
8751b09c13cSSteffen Eiden static long _dat_slot_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
8761b09c13cSSteffen Eiden {
8771b09c13cSSteffen Eiden 	union crste new_crste, crste = READ_ONCE(*crstep);
8781b09c13cSSteffen Eiden 	struct slot_priv *p = walk->priv;
8791b09c13cSSteffen Eiden 
8801b09c13cSSteffen Eiden 	new_crste.val = p->token;
8811b09c13cSSteffen Eiden 	new_crste.h.tt = crste.h.tt;
8821b09c13cSSteffen Eiden 
8831b09c13cSSteffen Eiden 	/* Table entry already in the desired state. */
8841b09c13cSSteffen Eiden 	if (crste.val == new_crste.val)
8851b09c13cSSteffen Eiden 		return 0;
8861b09c13cSSteffen Eiden 
8871b09c13cSSteffen Eiden 	/* This table entry needs to be updated. */
8881b09c13cSSteffen Eiden 	if (walk->start <= gfn && walk->end >= next) {
8891b09c13cSSteffen Eiden 		if (!dat_crstep_xchg_atomic(crstep, crste, new_crste, gfn, walk->asce))
8901b09c13cSSteffen Eiden 			return -EINVAL;
8911b09c13cSSteffen Eiden 		/* A lower level table was present, needs to be freed. */
8921b09c13cSSteffen Eiden 		if (!crste.h.fc && !crste.h.i) {
8931b09c13cSSteffen Eiden 			if (is_pmd(crste))
8941b09c13cSSteffen Eiden 				dat_free_pt(dereference_pmd(crste.pmd));
8951b09c13cSSteffen Eiden 			else
8961b09c13cSSteffen Eiden 				dat_free_level(dereference_crste(crste), true);
8971b09c13cSSteffen Eiden 		}
8981b09c13cSSteffen Eiden 		return 0;
8991b09c13cSSteffen Eiden 	}
9001b09c13cSSteffen Eiden 
9011b09c13cSSteffen Eiden 	/* A lower level table is present, things will handled there. */
9021b09c13cSSteffen Eiden 	if (!crste.h.fc && !crste.h.i)
9031b09c13cSSteffen Eiden 		return 0;
9041b09c13cSSteffen Eiden 	/* Split (install a lower level table), and handle things there. */
9051b09c13cSSteffen Eiden 	return dat_split_crste(p->mc, crstep, gfn, walk->asce, false);
9061b09c13cSSteffen Eiden }
9071b09c13cSSteffen Eiden 
9081b09c13cSSteffen Eiden static const struct dat_walk_ops dat_slot_ops = {
9091b09c13cSSteffen Eiden 	.pte_entry = _dat_slot_pte,
9101b09c13cSSteffen Eiden 	.crste_ops = { _dat_slot_crste, _dat_slot_crste, _dat_slot_crste, _dat_slot_crste, },
9111b09c13cSSteffen Eiden };
9121b09c13cSSteffen Eiden 
9131b09c13cSSteffen Eiden int dat_set_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start, gfn_t end,
9141b09c13cSSteffen Eiden 		 u16 type, u16 param)
9151b09c13cSSteffen Eiden {
9161b09c13cSSteffen Eiden 	struct slot_priv priv = {
9171b09c13cSSteffen Eiden 		.token = _CRSTE_TOK(0, type, param).val,
9181b09c13cSSteffen Eiden 		.mc = mc,
9191b09c13cSSteffen Eiden 	};
9201b09c13cSSteffen Eiden 
9211b09c13cSSteffen Eiden 	return _dat_walk_gfn_range(start, end, asce, &dat_slot_ops,
9221b09c13cSSteffen Eiden 				   DAT_WALK_IGN_HOLES | DAT_WALK_ANY, &priv);
9231b09c13cSSteffen Eiden }
9241b09c13cSSteffen Eiden 
9251b09c13cSSteffen Eiden static void pgste_set_unlock_multiple(union pte *first, int n, union pgste *pgstes)
9261b09c13cSSteffen Eiden {
9271b09c13cSSteffen Eiden 	int i;
9281b09c13cSSteffen Eiden 
9291b09c13cSSteffen Eiden 	for (i = 0; i < n; i++)
9301b09c13cSSteffen Eiden 		pgste_set_unlock(first + i, pgstes[i]);
9311b09c13cSSteffen Eiden }
9321b09c13cSSteffen Eiden 
9331b09c13cSSteffen Eiden static bool pgste_get_trylock_multiple(union pte *first, int n, union pgste *pgstes)
9341b09c13cSSteffen Eiden {
9351b09c13cSSteffen Eiden 	int i;
9361b09c13cSSteffen Eiden 
9371b09c13cSSteffen Eiden 	for (i = 0; i < n; i++) {
9381b09c13cSSteffen Eiden 		if (!pgste_get_trylock(first + i, pgstes + i))
9391b09c13cSSteffen Eiden 			break;
9401b09c13cSSteffen Eiden 	}
9411b09c13cSSteffen Eiden 	if (i == n)
9421b09c13cSSteffen Eiden 		return true;
9431b09c13cSSteffen Eiden 	pgste_set_unlock_multiple(first, i, pgstes);
9441b09c13cSSteffen Eiden 	return false;
9451b09c13cSSteffen Eiden }
9461b09c13cSSteffen Eiden 
9471b09c13cSSteffen Eiden unsigned long dat_get_ptval(struct page_table *table, struct ptval_param param)
9481b09c13cSSteffen Eiden {
9491b09c13cSSteffen Eiden 	union pgste pgstes[4] = {};
9501b09c13cSSteffen Eiden 	unsigned long res = 0;
9511b09c13cSSteffen Eiden 	int i, n;
9521b09c13cSSteffen Eiden 
9531b09c13cSSteffen Eiden 	n = param.len + 1;
9541b09c13cSSteffen Eiden 
9551b09c13cSSteffen Eiden 	while (!pgste_get_trylock_multiple(table->ptes + param.offset, n, pgstes))
9561b09c13cSSteffen Eiden 		cpu_relax();
9571b09c13cSSteffen Eiden 
9581b09c13cSSteffen Eiden 	for (i = 0; i < n; i++)
9591b09c13cSSteffen Eiden 		res = res << 16 | pgstes[i].val16;
9601b09c13cSSteffen Eiden 
9611b09c13cSSteffen Eiden 	pgste_set_unlock_multiple(table->ptes + param.offset, n, pgstes);
9621b09c13cSSteffen Eiden 	return res;
9631b09c13cSSteffen Eiden }
9641b09c13cSSteffen Eiden 
9651b09c13cSSteffen Eiden void dat_set_ptval(struct page_table *table, struct ptval_param param, unsigned long val)
9661b09c13cSSteffen Eiden {
9671b09c13cSSteffen Eiden 	union pgste pgstes[4] = {};
9681b09c13cSSteffen Eiden 	int i, n;
9691b09c13cSSteffen Eiden 
9701b09c13cSSteffen Eiden 	n = param.len + 1;
9711b09c13cSSteffen Eiden 
9721b09c13cSSteffen Eiden 	while (!pgste_get_trylock_multiple(table->ptes + param.offset, n, pgstes))
9731b09c13cSSteffen Eiden 		cpu_relax();
9741b09c13cSSteffen Eiden 
9751b09c13cSSteffen Eiden 	for (i = param.len; i >= 0; i--) {
9761b09c13cSSteffen Eiden 		pgstes[i].val16 = val;
9771b09c13cSSteffen Eiden 		val = val >> 16;
9781b09c13cSSteffen Eiden 	}
9791b09c13cSSteffen Eiden 
9801b09c13cSSteffen Eiden 	pgste_set_unlock_multiple(table->ptes + param.offset, n, pgstes);
9811b09c13cSSteffen Eiden }
9821b09c13cSSteffen Eiden 
9831b09c13cSSteffen Eiden static long _dat_test_young_pte(union pte *ptep, gfn_t start, gfn_t end, struct dat_walk *walk)
9841b09c13cSSteffen Eiden {
9851b09c13cSSteffen Eiden 	return ptep->s.y;
9861b09c13cSSteffen Eiden }
9871b09c13cSSteffen Eiden 
9881b09c13cSSteffen Eiden static long _dat_test_young_crste(union crste *crstep, gfn_t start, gfn_t end,
9891b09c13cSSteffen Eiden 				  struct dat_walk *walk)
9901b09c13cSSteffen Eiden {
9911b09c13cSSteffen Eiden 	return crstep->h.fc && crstep->s.fc1.y;
9921b09c13cSSteffen Eiden }
9931b09c13cSSteffen Eiden 
9941b09c13cSSteffen Eiden static const struct dat_walk_ops test_age_ops = {
9951b09c13cSSteffen Eiden 	.pte_entry = _dat_test_young_pte,
9961b09c13cSSteffen Eiden 	.pmd_entry = _dat_test_young_crste,
9971b09c13cSSteffen Eiden 	.pud_entry = _dat_test_young_crste,
9981b09c13cSSteffen Eiden };
9991b09c13cSSteffen Eiden 
10001b09c13cSSteffen Eiden /**
10011b09c13cSSteffen Eiden  * dat_test_age_gfn() - Test young.
10021b09c13cSSteffen Eiden  * @asce: The ASCE whose address range is to be tested.
10031b09c13cSSteffen Eiden  * @start: The first guest frame of the range to check.
10041b09c13cSSteffen Eiden  * @end: The guest frame after the last in the range.
10051b09c13cSSteffen Eiden  *
10061b09c13cSSteffen Eiden  * Context: called by KVM common code with the kvm mmu write lock held.
10071b09c13cSSteffen Eiden  *
10081b09c13cSSteffen Eiden  * Return: %true if any page in the given range is young, otherwise %false.
10091b09c13cSSteffen Eiden  */
10101b09c13cSSteffen Eiden bool dat_test_age_gfn(union asce asce, gfn_t start, gfn_t end)
10111b09c13cSSteffen Eiden {
10121b09c13cSSteffen Eiden 	return _dat_walk_gfn_range(start, end, asce, &test_age_ops, 0, NULL) > 0;
10131b09c13cSSteffen Eiden }
10141b09c13cSSteffen Eiden 
1015*188a15ccSSteffen Eiden #if KVM_S390_MANAGES_S390_GUEST
10161b09c13cSSteffen Eiden static long dat_set_pn_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
10171b09c13cSSteffen Eiden {
10181b09c13cSSteffen Eiden 	union crste newcrste, oldcrste;
10191b09c13cSSteffen Eiden 	int *n = walk->priv;
10201b09c13cSSteffen Eiden 
10211b09c13cSSteffen Eiden 	do {
10221b09c13cSSteffen Eiden 		oldcrste = READ_ONCE(*crstep);
10231b09c13cSSteffen Eiden 		if (!oldcrste.h.fc || oldcrste.h.i || oldcrste.h.p)
10241b09c13cSSteffen Eiden 			return 0;
10251b09c13cSSteffen Eiden 		if (oldcrste.s.fc1.prefix_notif)
10261b09c13cSSteffen Eiden 			break;
10271b09c13cSSteffen Eiden 		newcrste = oldcrste;
10281b09c13cSSteffen Eiden 		newcrste.s.fc1.prefix_notif = 1;
10291b09c13cSSteffen Eiden 	} while (!dat_crstep_xchg_atomic(crstep, oldcrste, newcrste, gfn, walk->asce));
10301b09c13cSSteffen Eiden 	*n = 2;
10311b09c13cSSteffen Eiden 	return 0;
10321b09c13cSSteffen Eiden }
10331b09c13cSSteffen Eiden 
10341b09c13cSSteffen Eiden static long dat_set_pn_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
10351b09c13cSSteffen Eiden {
10361b09c13cSSteffen Eiden 	int *n = walk->priv;
10371b09c13cSSteffen Eiden 	union pgste pgste;
10381b09c13cSSteffen Eiden 
10391b09c13cSSteffen Eiden 	pgste = pgste_get_lock(ptep);
10401b09c13cSSteffen Eiden 	if (!ptep->h.i && !ptep->h.p) {
10411b09c13cSSteffen Eiden 		pgste.prefix_notif = 1;
10421b09c13cSSteffen Eiden 		*n += 1;
10431b09c13cSSteffen Eiden 	}
10441b09c13cSSteffen Eiden 	pgste_set_unlock(ptep, pgste);
10451b09c13cSSteffen Eiden 	return 0;
10461b09c13cSSteffen Eiden }
10471b09c13cSSteffen Eiden 
10481b09c13cSSteffen Eiden int dat_set_prefix_notif_bit(union asce asce, gfn_t gfn)
10491b09c13cSSteffen Eiden {
10501b09c13cSSteffen Eiden 	static const struct dat_walk_ops ops = {
10511b09c13cSSteffen Eiden 		.pte_entry = dat_set_pn_pte,
10521b09c13cSSteffen Eiden 		.pmd_entry = dat_set_pn_crste,
10531b09c13cSSteffen Eiden 		.pud_entry = dat_set_pn_crste,
10541b09c13cSSteffen Eiden 	};
10551b09c13cSSteffen Eiden 
10561b09c13cSSteffen Eiden 	int n = 0;
10571b09c13cSSteffen Eiden 
10581b09c13cSSteffen Eiden 	_dat_walk_gfn_range(gfn, gfn + 2, asce, &ops, DAT_WALK_IGN_HOLES, &n);
10591b09c13cSSteffen Eiden 	if (n != 2)
10601b09c13cSSteffen Eiden 		return -EAGAIN;
10611b09c13cSSteffen Eiden 	return 0;
10621b09c13cSSteffen Eiden }
10631b09c13cSSteffen Eiden 
10641b09c13cSSteffen Eiden /**
10651b09c13cSSteffen Eiden  * dat_perform_essa() - Perform ESSA actions on the PGSTE.
10661b09c13cSSteffen Eiden  * @asce: The asce to operate on.
10671b09c13cSSteffen Eiden  * @gfn: The guest page frame to operate on.
10681b09c13cSSteffen Eiden  * @orc: The specific action to perform, see the ESSA_SET_* macros.
10691b09c13cSSteffen Eiden  * @state: The storage attributes to be returned to the guest.
10701b09c13cSSteffen Eiden  * @dirty: Returns whether the function dirtied a previously clean entry.
10711b09c13cSSteffen Eiden  *
10721b09c13cSSteffen Eiden  * Context: Called with kvm->mmu_lock held.
10731b09c13cSSteffen Eiden  *
10741b09c13cSSteffen Eiden  * Return:
10751b09c13cSSteffen Eiden  * * %1 if the page state has been altered and the page is to be added to the CBRL
10761b09c13cSSteffen Eiden  * * %0 if the page state has been altered, but the page is not to be added to the CBRL
10771b09c13cSSteffen Eiden  * * %-1 if the page state has not been altered and the page is not to be added to the CBRL
10781b09c13cSSteffen Eiden  */
10791b09c13cSSteffen Eiden int dat_perform_essa(union asce asce, gfn_t gfn, int orc, union essa_state *state, bool *dirty)
10801b09c13cSSteffen Eiden {
10811b09c13cSSteffen Eiden 	union crste *crstep;
10821b09c13cSSteffen Eiden 	union pgste pgste;
10831b09c13cSSteffen Eiden 	union pte *ptep;
10841b09c13cSSteffen Eiden 	int res = 0;
10851b09c13cSSteffen Eiden 
10861b09c13cSSteffen Eiden 	if (dat_entry_walk(NULL, gfn, asce, 0, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep)) {
10871b09c13cSSteffen Eiden 		*state = (union essa_state) { .exception = 1 };
10881b09c13cSSteffen Eiden 		return -1;
10891b09c13cSSteffen Eiden 	}
10901b09c13cSSteffen Eiden 
10911b09c13cSSteffen Eiden 	pgste = pgste_get_lock(ptep);
10921b09c13cSSteffen Eiden 
10931b09c13cSSteffen Eiden 	*state = (union essa_state) {
10941b09c13cSSteffen Eiden 		.content = (ptep->h.i << 1) + (ptep->h.i && pgste.zero),
10951b09c13cSSteffen Eiden 		.nodat = pgste.nodat,
10961b09c13cSSteffen Eiden 		.usage = pgste.usage,
10971b09c13cSSteffen Eiden 		};
10981b09c13cSSteffen Eiden 
10991b09c13cSSteffen Eiden 	switch (orc) {
11001b09c13cSSteffen Eiden 	case ESSA_GET_STATE:
11011b09c13cSSteffen Eiden 		res = -1;
11021b09c13cSSteffen Eiden 		break;
11031b09c13cSSteffen Eiden 	case ESSA_SET_STABLE:
11041b09c13cSSteffen Eiden 		pgste.usage = PGSTE_GPS_USAGE_STABLE;
11051b09c13cSSteffen Eiden 		pgste.nodat = 0;
11061b09c13cSSteffen Eiden 		break;
11071b09c13cSSteffen Eiden 	case ESSA_SET_UNUSED:
11081b09c13cSSteffen Eiden 		pgste.usage = PGSTE_GPS_USAGE_UNUSED;
11091b09c13cSSteffen Eiden 		if (ptep->h.i)
11101b09c13cSSteffen Eiden 			res = 1;
11111b09c13cSSteffen Eiden 		break;
11121b09c13cSSteffen Eiden 	case ESSA_SET_VOLATILE:
11131b09c13cSSteffen Eiden 		pgste.usage = PGSTE_GPS_USAGE_VOLATILE;
11141b09c13cSSteffen Eiden 		if (ptep->h.i)
11151b09c13cSSteffen Eiden 			res = 1;
11161b09c13cSSteffen Eiden 		break;
11171b09c13cSSteffen Eiden 	case ESSA_SET_POT_VOLATILE:
11181b09c13cSSteffen Eiden 		if (!ptep->h.i) {
11191b09c13cSSteffen Eiden 			pgste.usage = PGSTE_GPS_USAGE_POT_VOLATILE;
11201b09c13cSSteffen Eiden 		} else if (pgste.zero) {
11211b09c13cSSteffen Eiden 			pgste.usage = PGSTE_GPS_USAGE_VOLATILE;
11221b09c13cSSteffen Eiden 		} else if (!pgste.gc) {
11231b09c13cSSteffen Eiden 			pgste.usage = PGSTE_GPS_USAGE_VOLATILE;
11241b09c13cSSteffen Eiden 			res = 1;
11251b09c13cSSteffen Eiden 		}
11261b09c13cSSteffen Eiden 		break;
11271b09c13cSSteffen Eiden 	case ESSA_SET_STABLE_RESIDENT:
11281b09c13cSSteffen Eiden 		pgste.usage = PGSTE_GPS_USAGE_STABLE;
11291b09c13cSSteffen Eiden 		/*
11301b09c13cSSteffen Eiden 		 * Since the resident state can go away any time after this
11311b09c13cSSteffen Eiden 		 * call, we will not make this page resident. We can revisit
11321b09c13cSSteffen Eiden 		 * this decision if a guest will ever start using this.
11331b09c13cSSteffen Eiden 		 */
11341b09c13cSSteffen Eiden 		break;
11351b09c13cSSteffen Eiden 	case ESSA_SET_STABLE_IF_RESIDENT:
11361b09c13cSSteffen Eiden 		if (!ptep->h.i)
11371b09c13cSSteffen Eiden 			pgste.usage = PGSTE_GPS_USAGE_STABLE;
11381b09c13cSSteffen Eiden 		break;
11391b09c13cSSteffen Eiden 	case ESSA_SET_STABLE_NODAT:
11401b09c13cSSteffen Eiden 		pgste.usage = PGSTE_GPS_USAGE_STABLE;
11411b09c13cSSteffen Eiden 		pgste.nodat = 1;
11421b09c13cSSteffen Eiden 		break;
11431b09c13cSSteffen Eiden 	default:
11441b09c13cSSteffen Eiden 		WARN_ONCE(1, "Invalid ORC!");
11451b09c13cSSteffen Eiden 		res = -1;
11461b09c13cSSteffen Eiden 		break;
11471b09c13cSSteffen Eiden 	}
11481b09c13cSSteffen Eiden 	/* If we are discarding a page, set it to logical zero. */
11491b09c13cSSteffen Eiden 	pgste.zero = res == 1;
11501b09c13cSSteffen Eiden 	if (orc > 0) {
11511b09c13cSSteffen Eiden 		*dirty = !pgste.cmma_d;
11521b09c13cSSteffen Eiden 		pgste.cmma_d = 1;
11531b09c13cSSteffen Eiden 	}
11541b09c13cSSteffen Eiden 
11551b09c13cSSteffen Eiden 	pgste_set_unlock(ptep, pgste);
11561b09c13cSSteffen Eiden 
11571b09c13cSSteffen Eiden 	return res;
11581b09c13cSSteffen Eiden }
11591b09c13cSSteffen Eiden 
11601b09c13cSSteffen Eiden static long dat_reset_cmma_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
11611b09c13cSSteffen Eiden {
11621b09c13cSSteffen Eiden 	union pgste pgste;
11631b09c13cSSteffen Eiden 
11641b09c13cSSteffen Eiden 	pgste = pgste_get_lock(ptep);
11651b09c13cSSteffen Eiden 	pgste.usage = 0;
11661b09c13cSSteffen Eiden 	pgste.nodat = 0;
11671b09c13cSSteffen Eiden 	pgste.cmma_d = 0;
11681b09c13cSSteffen Eiden 	pgste_set_unlock(ptep, pgste);
11691b09c13cSSteffen Eiden 	if (need_resched())
11701b09c13cSSteffen Eiden 		return next;
11711b09c13cSSteffen Eiden 	return 0;
11721b09c13cSSteffen Eiden }
11731b09c13cSSteffen Eiden 
11741b09c13cSSteffen Eiden long dat_reset_cmma(union asce asce, gfn_t start)
11751b09c13cSSteffen Eiden {
11761b09c13cSSteffen Eiden 	const struct dat_walk_ops dat_reset_cmma_ops = {
11771b09c13cSSteffen Eiden 		.pte_entry = dat_reset_cmma_pte,
11781b09c13cSSteffen Eiden 	};
11791b09c13cSSteffen Eiden 
11801b09c13cSSteffen Eiden 	return _dat_walk_gfn_range(start, asce_end(asce), asce, &dat_reset_cmma_ops,
11811b09c13cSSteffen Eiden 				   DAT_WALK_IGN_HOLES, NULL);
11821b09c13cSSteffen Eiden }
11831b09c13cSSteffen Eiden 
11841b09c13cSSteffen Eiden struct dat_get_cmma_state {
11851b09c13cSSteffen Eiden 	gfn_t start;
11861b09c13cSSteffen Eiden 	gfn_t end;
11871b09c13cSSteffen Eiden 	unsigned int count;
11881b09c13cSSteffen Eiden 	u8 *values;
11891b09c13cSSteffen Eiden 	atomic64_t *remaining;
11901b09c13cSSteffen Eiden };
11911b09c13cSSteffen Eiden 
11921b09c13cSSteffen Eiden static long __dat_peek_cmma_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
11931b09c13cSSteffen Eiden {
11941b09c13cSSteffen Eiden 	struct dat_get_cmma_state *state = walk->priv;
11951b09c13cSSteffen Eiden 	union pgste pgste;
11961b09c13cSSteffen Eiden 
11971b09c13cSSteffen Eiden 	pgste = pgste_get_lock(ptep);
11981b09c13cSSteffen Eiden 	state->values[gfn - walk->start] = pgste.usage | (pgste.nodat << 6);
11991b09c13cSSteffen Eiden 	pgste_set_unlock(ptep, pgste);
12001b09c13cSSteffen Eiden 	state->end = next;
12011b09c13cSSteffen Eiden 
12021b09c13cSSteffen Eiden 	return 0;
12031b09c13cSSteffen Eiden }
12041b09c13cSSteffen Eiden 
12051b09c13cSSteffen Eiden static long __dat_peek_cmma_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
12061b09c13cSSteffen Eiden {
12071b09c13cSSteffen Eiden 	struct dat_get_cmma_state *state = walk->priv;
12081b09c13cSSteffen Eiden 
12091b09c13cSSteffen Eiden 	if (crstep->h.i)
12101b09c13cSSteffen Eiden 		state->end = min(walk->end, next);
12111b09c13cSSteffen Eiden 	return 0;
12121b09c13cSSteffen Eiden }
12131b09c13cSSteffen Eiden 
12141b09c13cSSteffen Eiden int dat_peek_cmma(gfn_t start, union asce asce, unsigned int *count, u8 *values)
12151b09c13cSSteffen Eiden {
12161b09c13cSSteffen Eiden 	const struct dat_walk_ops ops = {
12171b09c13cSSteffen Eiden 		.pte_entry = __dat_peek_cmma_pte,
12181b09c13cSSteffen Eiden 		.pmd_entry = __dat_peek_cmma_crste,
12191b09c13cSSteffen Eiden 		.pud_entry = __dat_peek_cmma_crste,
12201b09c13cSSteffen Eiden 		.p4d_entry = __dat_peek_cmma_crste,
12211b09c13cSSteffen Eiden 		.pgd_entry = __dat_peek_cmma_crste,
12221b09c13cSSteffen Eiden 	};
12231b09c13cSSteffen Eiden 	struct dat_get_cmma_state state = { .values = values, };
12241b09c13cSSteffen Eiden 	int rc;
12251b09c13cSSteffen Eiden 
12261b09c13cSSteffen Eiden 	rc = _dat_walk_gfn_range(start, start + *count, asce, &ops, DAT_WALK_DEFAULT, &state);
12271b09c13cSSteffen Eiden 	*count = state.end >= start ? state.end - start : 0;
12281b09c13cSSteffen Eiden 	/* Return success if at least one value was saved, otherwise an error. */
12291b09c13cSSteffen Eiden 	return (rc == -EFAULT && *count > 0) ? 0 : rc;
12301b09c13cSSteffen Eiden }
12311b09c13cSSteffen Eiden 
12321b09c13cSSteffen Eiden static long __dat_get_cmma_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
12331b09c13cSSteffen Eiden {
12341b09c13cSSteffen Eiden 	struct dat_get_cmma_state *state = walk->priv;
12351b09c13cSSteffen Eiden 	union pgste pgste;
12361b09c13cSSteffen Eiden 
12371b09c13cSSteffen Eiden 	if (state->start != -1) {
12381b09c13cSSteffen Eiden 		if ((gfn - state->end) > KVM_S390_MAX_BIT_DISTANCE)
12391b09c13cSSteffen Eiden 			return 1;
12401b09c13cSSteffen Eiden 		if (gfn - state->start >= state->count)
12411b09c13cSSteffen Eiden 			return 1;
12421b09c13cSSteffen Eiden 	}
12431b09c13cSSteffen Eiden 
12441b09c13cSSteffen Eiden 	if (!READ_ONCE(*pgste_of(ptep)).cmma_d)
12451b09c13cSSteffen Eiden 		return 0;
12461b09c13cSSteffen Eiden 
12471b09c13cSSteffen Eiden 	pgste = pgste_get_lock(ptep);
12481b09c13cSSteffen Eiden 	if (pgste.cmma_d) {
12491b09c13cSSteffen Eiden 		if (state->start == -1)
12501b09c13cSSteffen Eiden 			state->start = gfn;
12511b09c13cSSteffen Eiden 		pgste.cmma_d = 0;
12521b09c13cSSteffen Eiden 		atomic64_dec(state->remaining);
12531b09c13cSSteffen Eiden 		state->values[gfn - state->start] = pgste.usage | pgste.nodat << 6;
12541b09c13cSSteffen Eiden 		state->end = next;
12551b09c13cSSteffen Eiden 	}
12561b09c13cSSteffen Eiden 	pgste_set_unlock(ptep, pgste);
12571b09c13cSSteffen Eiden 	return 0;
12581b09c13cSSteffen Eiden }
12591b09c13cSSteffen Eiden 
12601b09c13cSSteffen Eiden int dat_get_cmma(union asce asce, gfn_t *start, unsigned int *count, u8 *values, atomic64_t *rem)
12611b09c13cSSteffen Eiden {
12621b09c13cSSteffen Eiden 	const struct dat_walk_ops ops = { .pte_entry = __dat_get_cmma_pte, };
12631b09c13cSSteffen Eiden 	struct dat_get_cmma_state state = {
12641b09c13cSSteffen Eiden 		.remaining = rem,
12651b09c13cSSteffen Eiden 		.values = values,
12661b09c13cSSteffen Eiden 		.count = *count,
12671b09c13cSSteffen Eiden 		.start = -1,
12681b09c13cSSteffen Eiden 	};
12691b09c13cSSteffen Eiden 
12701b09c13cSSteffen Eiden 	_dat_walk_gfn_range(*start, asce_end(asce), asce, &ops, DAT_WALK_IGN_HOLES, &state);
12711b09c13cSSteffen Eiden 	/* If no dirty pages were found, wrap around and continue searching */
12721b09c13cSSteffen Eiden 	if (*start && state.start == -1)
12731b09c13cSSteffen Eiden 		_dat_walk_gfn_range(0, *start, asce, &ops, DAT_WALK_IGN_HOLES, &state);
12741b09c13cSSteffen Eiden 
12751b09c13cSSteffen Eiden 	if (state.start == -1) {
12761b09c13cSSteffen Eiden 		*count = 0;
12771b09c13cSSteffen Eiden 	} else {
12781b09c13cSSteffen Eiden 		*count = state.end - state.start;
12791b09c13cSSteffen Eiden 		*start = state.start;
12801b09c13cSSteffen Eiden 	}
12811b09c13cSSteffen Eiden 
12821b09c13cSSteffen Eiden 	return 0;
12831b09c13cSSteffen Eiden }
12841b09c13cSSteffen Eiden 
12851b09c13cSSteffen Eiden struct dat_set_cmma_state {
12861b09c13cSSteffen Eiden 	unsigned long mask;
12871b09c13cSSteffen Eiden 	const u8 *bits;
12881b09c13cSSteffen Eiden };
12891b09c13cSSteffen Eiden 
12901b09c13cSSteffen Eiden static long __dat_set_cmma_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
12911b09c13cSSteffen Eiden {
12921b09c13cSSteffen Eiden 	struct dat_set_cmma_state *state = walk->priv;
12931b09c13cSSteffen Eiden 	union pgste pgste, tmp;
12941b09c13cSSteffen Eiden 
12951b09c13cSSteffen Eiden 	tmp.val = (state->bits[gfn - walk->start] << 24) & state->mask;
12961b09c13cSSteffen Eiden 
12971b09c13cSSteffen Eiden 	pgste = pgste_get_lock(ptep);
12981b09c13cSSteffen Eiden 	pgste.usage = tmp.usage;
12991b09c13cSSteffen Eiden 	pgste.nodat = tmp.nodat;
13001b09c13cSSteffen Eiden 	pgste_set_unlock(ptep, pgste);
13011b09c13cSSteffen Eiden 
13021b09c13cSSteffen Eiden 	return 0;
13031b09c13cSSteffen Eiden }
13041b09c13cSSteffen Eiden 
13051b09c13cSSteffen Eiden /**
13061b09c13cSSteffen Eiden  * dat_set_cmma_bits() - Set CMMA bits for a range of guest pages.
13071b09c13cSSteffen Eiden  * @mc: Cache used for allocations.
13081b09c13cSSteffen Eiden  * @asce: The ASCE of the guest.
13091b09c13cSSteffen Eiden  * @gfn: The guest frame of the fist page whose CMMA bits are to set.
13101b09c13cSSteffen Eiden  * @count: How many pages need to be processed.
13111b09c13cSSteffen Eiden  * @mask: Which PGSTE bits should be set.
13121b09c13cSSteffen Eiden  * @bits: Points to an array with the CMMA attributes.
13131b09c13cSSteffen Eiden  *
13141b09c13cSSteffen Eiden  * This function sets the CMMA attributes for the given pages. If the input
13151b09c13cSSteffen Eiden  * buffer has zero length, no action is taken, otherwise the attributes are
13161b09c13cSSteffen Eiden  * set and the mm->context.uses_cmm flag is set.
13171b09c13cSSteffen Eiden  *
13181b09c13cSSteffen Eiden  * Each byte in @bits contains new values for bits 32-39 of the PGSTE.
13191b09c13cSSteffen Eiden  * Currently, only the fields NT and US are applied.
13201b09c13cSSteffen Eiden  *
13211b09c13cSSteffen Eiden  * Return: %0 in case of success, a negative error value otherwise.
13221b09c13cSSteffen Eiden  */
13231b09c13cSSteffen Eiden int dat_set_cmma_bits(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gfn,
13241b09c13cSSteffen Eiden 		      unsigned long count, unsigned long mask, const uint8_t *bits)
13251b09c13cSSteffen Eiden {
13261b09c13cSSteffen Eiden 	const struct dat_walk_ops ops = { .pte_entry = __dat_set_cmma_pte, };
13271b09c13cSSteffen Eiden 	struct dat_set_cmma_state state = { .mask = mask, .bits = bits, };
13281b09c13cSSteffen Eiden 	union crste *crstep;
13291b09c13cSSteffen Eiden 	union pte *ptep;
13301b09c13cSSteffen Eiden 	gfn_t cur;
13311b09c13cSSteffen Eiden 	int rc;
13321b09c13cSSteffen Eiden 
13331b09c13cSSteffen Eiden 	for (cur = ALIGN_DOWN(gfn, _PAGE_ENTRIES); cur < gfn + count; cur += _PAGE_ENTRIES) {
13341b09c13cSSteffen Eiden 		rc = dat_entry_walk(mc, cur, asce, DAT_WALK_ALLOC, TABLE_TYPE_PAGE_TABLE,
13351b09c13cSSteffen Eiden 				    &crstep, &ptep);
13361b09c13cSSteffen Eiden 		if (rc)
13371b09c13cSSteffen Eiden 			return rc;
13381b09c13cSSteffen Eiden 	}
13391b09c13cSSteffen Eiden 	return _dat_walk_gfn_range(gfn, gfn + count, asce, &ops, DAT_WALK_IGN_HOLES, &state);
13401b09c13cSSteffen Eiden }
1341ce4bee6aSSteffen Eiden #endif /* KVM_S390_MANAGES_S390_GUEST */
1342