xref: /linux/arch/s390/kvm/dat.c (revision 8934827db5403eae57d4537114a9ff88b0a8460f)
112f2f61aSClaudio Imbrenda // SPDX-License-Identifier: GPL-2.0
212f2f61aSClaudio Imbrenda /*
312f2f61aSClaudio Imbrenda  *  KVM guest address space mapping code
412f2f61aSClaudio Imbrenda  *
512f2f61aSClaudio Imbrenda  *    Copyright IBM Corp. 2007, 2020, 2024
612f2f61aSClaudio Imbrenda  *    Author(s): Claudio Imbrenda <imbrenda@linux.ibm.com>
712f2f61aSClaudio Imbrenda  *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
812f2f61aSClaudio Imbrenda  *		 David Hildenbrand <david@redhat.com>
912f2f61aSClaudio Imbrenda  *		 Janosch Frank <frankja@linux.ibm.com>
1012f2f61aSClaudio Imbrenda  */
1112f2f61aSClaudio Imbrenda 
1212f2f61aSClaudio Imbrenda #include <linux/kernel.h>
1312f2f61aSClaudio Imbrenda #include <linux/pagewalk.h>
1412f2f61aSClaudio Imbrenda #include <linux/swap.h>
1512f2f61aSClaudio Imbrenda #include <linux/smp.h>
1612f2f61aSClaudio Imbrenda #include <linux/spinlock.h>
1712f2f61aSClaudio Imbrenda #include <linux/slab.h>
1812f2f61aSClaudio Imbrenda #include <linux/swapops.h>
1912f2f61aSClaudio Imbrenda #include <linux/ksm.h>
2012f2f61aSClaudio Imbrenda #include <linux/mm.h>
2112f2f61aSClaudio Imbrenda #include <linux/mman.h>
2212f2f61aSClaudio Imbrenda #include <linux/pgtable.h>
2312f2f61aSClaudio Imbrenda #include <linux/kvm_types.h>
2412f2f61aSClaudio Imbrenda #include <linux/kvm_host.h>
2512f2f61aSClaudio Imbrenda #include <linux/pgalloc.h>
2612f2f61aSClaudio Imbrenda 
2712f2f61aSClaudio Imbrenda #include <asm/page-states.h>
2812f2f61aSClaudio Imbrenda #include <asm/tlb.h>
2912f2f61aSClaudio Imbrenda #include "dat.h"
3012f2f61aSClaudio Imbrenda 
kvm_s390_mmu_cache_topup(struct kvm_s390_mmu_cache * mc)3112f2f61aSClaudio Imbrenda int kvm_s390_mmu_cache_topup(struct kvm_s390_mmu_cache *mc)
3212f2f61aSClaudio Imbrenda {
3312f2f61aSClaudio Imbrenda 	void *o;
3412f2f61aSClaudio Imbrenda 
3512f2f61aSClaudio Imbrenda 	for ( ; mc->n_crsts < KVM_S390_MMU_CACHE_N_CRSTS; mc->n_crsts++) {
3612f2f61aSClaudio Imbrenda 		o = (void *)__get_free_pages(GFP_KERNEL_ACCOUNT | __GFP_COMP, CRST_ALLOC_ORDER);
3712f2f61aSClaudio Imbrenda 		if (!o)
3812f2f61aSClaudio Imbrenda 			return -ENOMEM;
3912f2f61aSClaudio Imbrenda 		mc->crsts[mc->n_crsts] = o;
4012f2f61aSClaudio Imbrenda 	}
4112f2f61aSClaudio Imbrenda 	for ( ; mc->n_pts < KVM_S390_MMU_CACHE_N_PTS; mc->n_pts++) {
4212f2f61aSClaudio Imbrenda 		o = (void *)__get_free_page(GFP_KERNEL_ACCOUNT);
4312f2f61aSClaudio Imbrenda 		if (!o)
4412f2f61aSClaudio Imbrenda 			return -ENOMEM;
4512f2f61aSClaudio Imbrenda 		mc->pts[mc->n_pts] = o;
4612f2f61aSClaudio Imbrenda 	}
4712f2f61aSClaudio Imbrenda 	for ( ; mc->n_rmaps < KVM_S390_MMU_CACHE_N_RMAPS; mc->n_rmaps++) {
48*69050f8dSKees Cook 		o = kzalloc_obj(*mc->rmaps[0], GFP_KERNEL_ACCOUNT);
4912f2f61aSClaudio Imbrenda 		if (!o)
5012f2f61aSClaudio Imbrenda 			return -ENOMEM;
5112f2f61aSClaudio Imbrenda 		mc->rmaps[mc->n_rmaps] = o;
5212f2f61aSClaudio Imbrenda 	}
5312f2f61aSClaudio Imbrenda 	return 0;
5412f2f61aSClaudio Imbrenda }
5512f2f61aSClaudio Imbrenda 
dat_alloc_pt_noinit(struct kvm_s390_mmu_cache * mc)5612f2f61aSClaudio Imbrenda static inline struct page_table *dat_alloc_pt_noinit(struct kvm_s390_mmu_cache *mc)
5712f2f61aSClaudio Imbrenda {
5812f2f61aSClaudio Imbrenda 	struct page_table *res;
5912f2f61aSClaudio Imbrenda 
6012f2f61aSClaudio Imbrenda 	res = kvm_s390_mmu_cache_alloc_pt(mc);
6112f2f61aSClaudio Imbrenda 	if (res)
6212f2f61aSClaudio Imbrenda 		__arch_set_page_dat(res, 1);
6312f2f61aSClaudio Imbrenda 	return res;
6412f2f61aSClaudio Imbrenda }
6512f2f61aSClaudio Imbrenda 
dat_alloc_crst_noinit(struct kvm_s390_mmu_cache * mc)6612f2f61aSClaudio Imbrenda static inline struct crst_table *dat_alloc_crst_noinit(struct kvm_s390_mmu_cache *mc)
6712f2f61aSClaudio Imbrenda {
6812f2f61aSClaudio Imbrenda 	struct crst_table *res;
6912f2f61aSClaudio Imbrenda 
7012f2f61aSClaudio Imbrenda 	res = kvm_s390_mmu_cache_alloc_crst(mc);
7112f2f61aSClaudio Imbrenda 	if (res)
7212f2f61aSClaudio Imbrenda 		__arch_set_page_dat(res, 1UL << CRST_ALLOC_ORDER);
7312f2f61aSClaudio Imbrenda 	return res;
7412f2f61aSClaudio Imbrenda }
7512f2f61aSClaudio Imbrenda 
dat_alloc_crst_sleepable(unsigned long init)7612f2f61aSClaudio Imbrenda struct crst_table *dat_alloc_crst_sleepable(unsigned long init)
7712f2f61aSClaudio Imbrenda {
7812f2f61aSClaudio Imbrenda 	struct page *page;
7912f2f61aSClaudio Imbrenda 	void *virt;
8012f2f61aSClaudio Imbrenda 
8112f2f61aSClaudio Imbrenda 	page = alloc_pages(GFP_KERNEL_ACCOUNT | __GFP_COMP, CRST_ALLOC_ORDER);
8212f2f61aSClaudio Imbrenda 	if (!page)
8312f2f61aSClaudio Imbrenda 		return NULL;
8412f2f61aSClaudio Imbrenda 	virt = page_to_virt(page);
8512f2f61aSClaudio Imbrenda 	__arch_set_page_dat(virt, 1UL << CRST_ALLOC_ORDER);
8612f2f61aSClaudio Imbrenda 	crst_table_init(virt, init);
8712f2f61aSClaudio Imbrenda 	return virt;
8812f2f61aSClaudio Imbrenda }
8912f2f61aSClaudio Imbrenda 
dat_free_level(struct crst_table * table,bool owns_ptes)9012f2f61aSClaudio Imbrenda void dat_free_level(struct crst_table *table, bool owns_ptes)
9112f2f61aSClaudio Imbrenda {
9212f2f61aSClaudio Imbrenda 	unsigned int i;
9312f2f61aSClaudio Imbrenda 
9412f2f61aSClaudio Imbrenda 	for (i = 0; i < _CRST_ENTRIES; i++) {
9512f2f61aSClaudio Imbrenda 		if (table->crstes[i].h.fc || table->crstes[i].h.i)
9612f2f61aSClaudio Imbrenda 			continue;
9712f2f61aSClaudio Imbrenda 		if (!is_pmd(table->crstes[i]))
9812f2f61aSClaudio Imbrenda 			dat_free_level(dereference_crste(table->crstes[i]), owns_ptes);
9912f2f61aSClaudio Imbrenda 		else if (owns_ptes)
10012f2f61aSClaudio Imbrenda 			dat_free_pt(dereference_pmd(table->crstes[i].pmd));
10112f2f61aSClaudio Imbrenda 	}
10212f2f61aSClaudio Imbrenda 	dat_free_crst(table);
10312f2f61aSClaudio Imbrenda }
104589071eaSClaudio Imbrenda 
dat_set_asce_limit(struct kvm_s390_mmu_cache * mc,union asce * asce,int newtype)10594fd9b16SClaudio Imbrenda int dat_set_asce_limit(struct kvm_s390_mmu_cache *mc, union asce *asce, int newtype)
10694fd9b16SClaudio Imbrenda {
10794fd9b16SClaudio Imbrenda 	struct crst_table *table;
10894fd9b16SClaudio Imbrenda 	union crste crste;
10994fd9b16SClaudio Imbrenda 
11094fd9b16SClaudio Imbrenda 	while (asce->dt > newtype) {
11194fd9b16SClaudio Imbrenda 		table = dereference_asce(*asce);
11294fd9b16SClaudio Imbrenda 		crste = table->crstes[0];
11394fd9b16SClaudio Imbrenda 		if (crste.h.fc)
11494fd9b16SClaudio Imbrenda 			return 0;
11594fd9b16SClaudio Imbrenda 		if (!crste.h.i) {
11694fd9b16SClaudio Imbrenda 			asce->rsto = crste.h.fc0.to;
11794fd9b16SClaudio Imbrenda 			dat_free_crst(table);
11894fd9b16SClaudio Imbrenda 		} else {
11994fd9b16SClaudio Imbrenda 			crste.h.tt--;
12094fd9b16SClaudio Imbrenda 			crst_table_init((void *)table, crste.val);
12194fd9b16SClaudio Imbrenda 		}
12294fd9b16SClaudio Imbrenda 		asce->dt--;
12394fd9b16SClaudio Imbrenda 	}
12494fd9b16SClaudio Imbrenda 	while (asce->dt < newtype) {
12594fd9b16SClaudio Imbrenda 		crste = _crste_fc0(asce->rsto, asce->dt + 1);
12694fd9b16SClaudio Imbrenda 		table = dat_alloc_crst_noinit(mc);
12794fd9b16SClaudio Imbrenda 		if (!table)
12894fd9b16SClaudio Imbrenda 			return -ENOMEM;
12994fd9b16SClaudio Imbrenda 		crst_table_init((void *)table, _CRSTE_HOLE(crste.h.tt).val);
13094fd9b16SClaudio Imbrenda 		table->crstes[0] = crste;
13194fd9b16SClaudio Imbrenda 		asce->rsto = __pa(table) >> PAGE_SHIFT;
13294fd9b16SClaudio Imbrenda 		asce->dt++;
13394fd9b16SClaudio Imbrenda 	}
13494fd9b16SClaudio Imbrenda 	return 0;
13594fd9b16SClaudio Imbrenda }
13694fd9b16SClaudio Imbrenda 
137589071eaSClaudio Imbrenda /**
138589071eaSClaudio Imbrenda  * dat_crstep_xchg() - Exchange a gmap CRSTE with another.
139589071eaSClaudio Imbrenda  * @crstep: Pointer to the CRST entry
140589071eaSClaudio Imbrenda  * @new: Replacement entry.
141589071eaSClaudio Imbrenda  * @gfn: The affected guest address.
142589071eaSClaudio Imbrenda  * @asce: The ASCE of the address space.
143589071eaSClaudio Imbrenda  *
144589071eaSClaudio Imbrenda  * Context: This function is assumed to be called with kvm->mmu_lock held.
145589071eaSClaudio Imbrenda  */
dat_crstep_xchg(union crste * crstep,union crste new,gfn_t gfn,union asce asce)146589071eaSClaudio Imbrenda void dat_crstep_xchg(union crste *crstep, union crste new, gfn_t gfn, union asce asce)
147589071eaSClaudio Imbrenda {
148589071eaSClaudio Imbrenda 	if (crstep->h.i) {
149589071eaSClaudio Imbrenda 		WRITE_ONCE(*crstep, new);
150589071eaSClaudio Imbrenda 		return;
151589071eaSClaudio Imbrenda 	} else if (cpu_has_edat2()) {
152589071eaSClaudio Imbrenda 		crdte_crste(crstep, *crstep, new, gfn, asce);
153589071eaSClaudio Imbrenda 		return;
154589071eaSClaudio Imbrenda 	}
155589071eaSClaudio Imbrenda 
156589071eaSClaudio Imbrenda 	if (machine_has_tlb_guest())
157589071eaSClaudio Imbrenda 		idte_crste(crstep, gfn, IDTE_GUEST_ASCE, asce, IDTE_GLOBAL);
158589071eaSClaudio Imbrenda 	else
159589071eaSClaudio Imbrenda 		idte_crste(crstep, gfn, 0, NULL_ASCE, IDTE_GLOBAL);
160589071eaSClaudio Imbrenda 	WRITE_ONCE(*crstep, new);
161589071eaSClaudio Imbrenda }
162589071eaSClaudio Imbrenda 
163589071eaSClaudio Imbrenda /**
164589071eaSClaudio Imbrenda  * dat_crstep_xchg_atomic() - Atomically exchange a gmap CRSTE with another.
165589071eaSClaudio Imbrenda  * @crstep: Pointer to the CRST entry.
166589071eaSClaudio Imbrenda  * @old: Expected old value.
167589071eaSClaudio Imbrenda  * @new: Replacement entry.
168589071eaSClaudio Imbrenda  * @gfn: The affected guest address.
169589071eaSClaudio Imbrenda  * @asce: The asce of the address space.
170589071eaSClaudio Imbrenda  *
171589071eaSClaudio Imbrenda  * This function is needed to atomically exchange a CRSTE that potentially
172589071eaSClaudio Imbrenda  * maps a prefix area, without having to invalidate it inbetween.
173589071eaSClaudio Imbrenda  *
174589071eaSClaudio Imbrenda  * Context: This function is assumed to be called with kvm->mmu_lock held.
175589071eaSClaudio Imbrenda  *
176589071eaSClaudio Imbrenda  * Return: %true if the exchange was successful.
177589071eaSClaudio Imbrenda  */
dat_crstep_xchg_atomic(union crste * crstep,union crste old,union crste new,gfn_t gfn,union asce asce)178589071eaSClaudio Imbrenda bool dat_crstep_xchg_atomic(union crste *crstep, union crste old, union crste new, gfn_t gfn,
179589071eaSClaudio Imbrenda 			    union asce asce)
180589071eaSClaudio Imbrenda {
181589071eaSClaudio Imbrenda 	if (old.h.i)
182589071eaSClaudio Imbrenda 		return arch_try_cmpxchg((long *)crstep, &old.val, new.val);
183589071eaSClaudio Imbrenda 	if (cpu_has_edat2())
184589071eaSClaudio Imbrenda 		return crdte_crste(crstep, old, new, gfn, asce);
185589071eaSClaudio Imbrenda 	return cspg_crste(crstep, old, new);
186589071eaSClaudio Imbrenda }
187589071eaSClaudio Imbrenda 
dat_set_storage_key_from_pgste(union pte pte,union pgste pgste)188589071eaSClaudio Imbrenda static void dat_set_storage_key_from_pgste(union pte pte, union pgste pgste)
189589071eaSClaudio Imbrenda {
190589071eaSClaudio Imbrenda 	union skey nkey = { .acc = pgste.acc, .fp = pgste.fp };
191589071eaSClaudio Imbrenda 
192589071eaSClaudio Imbrenda 	page_set_storage_key(pte_origin(pte), nkey.skey, 0);
193589071eaSClaudio Imbrenda }
194589071eaSClaudio Imbrenda 
dat_move_storage_key(union pte old,union pte new)195589071eaSClaudio Imbrenda static void dat_move_storage_key(union pte old, union pte new)
196589071eaSClaudio Imbrenda {
197589071eaSClaudio Imbrenda 	page_set_storage_key(pte_origin(new), page_get_storage_key(pte_origin(old)), 1);
198589071eaSClaudio Imbrenda }
199589071eaSClaudio Imbrenda 
dat_save_storage_key_into_pgste(union pte pte,union pgste pgste)200589071eaSClaudio Imbrenda static union pgste dat_save_storage_key_into_pgste(union pte pte, union pgste pgste)
201589071eaSClaudio Imbrenda {
202589071eaSClaudio Imbrenda 	union skey skey;
203589071eaSClaudio Imbrenda 
204589071eaSClaudio Imbrenda 	skey.skey = page_get_storage_key(pte_origin(pte));
205589071eaSClaudio Imbrenda 
206589071eaSClaudio Imbrenda 	pgste.acc = skey.acc;
207589071eaSClaudio Imbrenda 	pgste.fp = skey.fp;
208589071eaSClaudio Imbrenda 	pgste.gr |= skey.r;
209589071eaSClaudio Imbrenda 	pgste.gc |= skey.c;
210589071eaSClaudio Imbrenda 
211589071eaSClaudio Imbrenda 	return pgste;
212589071eaSClaudio Imbrenda }
213589071eaSClaudio Imbrenda 
__dat_ptep_xchg(union pte * ptep,union pgste pgste,union pte new,gfn_t gfn,union asce asce,bool uses_skeys)214589071eaSClaudio Imbrenda union pgste __dat_ptep_xchg(union pte *ptep, union pgste pgste, union pte new, gfn_t gfn,
215589071eaSClaudio Imbrenda 			    union asce asce, bool uses_skeys)
216589071eaSClaudio Imbrenda {
217589071eaSClaudio Imbrenda 	union pte old = READ_ONCE(*ptep);
218589071eaSClaudio Imbrenda 
219589071eaSClaudio Imbrenda 	/* Updating only the software bits while holding the pgste lock. */
220589071eaSClaudio Imbrenda 	if (!((ptep->val ^ new.val) & ~_PAGE_SW_BITS)) {
221589071eaSClaudio Imbrenda 		WRITE_ONCE(ptep->swbyte, new.swbyte);
222589071eaSClaudio Imbrenda 		return pgste;
223589071eaSClaudio Imbrenda 	}
224589071eaSClaudio Imbrenda 
225589071eaSClaudio Imbrenda 	if (!old.h.i) {
226589071eaSClaudio Imbrenda 		unsigned long opts = IPTE_GUEST_ASCE | (pgste.nodat ? IPTE_NODAT : 0);
227589071eaSClaudio Imbrenda 
228589071eaSClaudio Imbrenda 		if (machine_has_tlb_guest())
229589071eaSClaudio Imbrenda 			__ptep_ipte(gfn_to_gpa(gfn), (void *)ptep, opts, asce.val, IPTE_GLOBAL);
230589071eaSClaudio Imbrenda 		else
231589071eaSClaudio Imbrenda 			__ptep_ipte(gfn_to_gpa(gfn), (void *)ptep, 0, 0, IPTE_GLOBAL);
232589071eaSClaudio Imbrenda 	}
233589071eaSClaudio Imbrenda 
234589071eaSClaudio Imbrenda 	if (uses_skeys) {
235589071eaSClaudio Imbrenda 		if (old.h.i && !new.h.i)
236589071eaSClaudio Imbrenda 			/* Invalid to valid: restore storage keys from PGSTE. */
237589071eaSClaudio Imbrenda 			dat_set_storage_key_from_pgste(new, pgste);
238589071eaSClaudio Imbrenda 		else if (!old.h.i && new.h.i)
239589071eaSClaudio Imbrenda 			/* Valid to invalid: save storage keys to PGSTE. */
240589071eaSClaudio Imbrenda 			pgste = dat_save_storage_key_into_pgste(old, pgste);
241589071eaSClaudio Imbrenda 		else if (!old.h.i && !new.h.i)
242589071eaSClaudio Imbrenda 			/* Valid to valid: move storage keys. */
243589071eaSClaudio Imbrenda 			if (old.h.pfra != new.h.pfra)
244589071eaSClaudio Imbrenda 				dat_move_storage_key(old, new);
245589071eaSClaudio Imbrenda 		/* Invalid to invalid: nothing to do. */
246589071eaSClaudio Imbrenda 	}
247589071eaSClaudio Imbrenda 
248589071eaSClaudio Imbrenda 	WRITE_ONCE(*ptep, new);
249589071eaSClaudio Imbrenda 	return pgste;
250589071eaSClaudio Imbrenda }
2512db149a0SClaudio Imbrenda 
2522db149a0SClaudio Imbrenda /*
2532db149a0SClaudio Imbrenda  * dat_split_ste() - Split a segment table entry into page table entries.
2542db149a0SClaudio Imbrenda  *
2552db149a0SClaudio Imbrenda  * Context: This function is assumed to be called with kvm->mmu_lock held.
2562db149a0SClaudio Imbrenda  *
2572db149a0SClaudio Imbrenda  * Return: 0 in case of success, -ENOMEM if running out of memory.
2582db149a0SClaudio Imbrenda  */
dat_split_ste(struct kvm_s390_mmu_cache * mc,union pmd * pmdp,gfn_t gfn,union asce asce,bool uses_skeys)2592db149a0SClaudio Imbrenda static int dat_split_ste(struct kvm_s390_mmu_cache *mc, union pmd *pmdp, gfn_t gfn,
2602db149a0SClaudio Imbrenda 			 union asce asce, bool uses_skeys)
2612db149a0SClaudio Imbrenda {
2622db149a0SClaudio Imbrenda 	union pgste pgste_init;
2632db149a0SClaudio Imbrenda 	struct page_table *pt;
2642db149a0SClaudio Imbrenda 	union pmd new, old;
2652db149a0SClaudio Imbrenda 	union pte init;
2662db149a0SClaudio Imbrenda 	int i;
2672db149a0SClaudio Imbrenda 
2682db149a0SClaudio Imbrenda 	BUG_ON(!mc);
2692db149a0SClaudio Imbrenda 	old = READ_ONCE(*pmdp);
2702db149a0SClaudio Imbrenda 
2712db149a0SClaudio Imbrenda 	/* Already split, nothing to do. */
2722db149a0SClaudio Imbrenda 	if (!old.h.i && !old.h.fc)
2732db149a0SClaudio Imbrenda 		return 0;
2742db149a0SClaudio Imbrenda 
2752db149a0SClaudio Imbrenda 	pt = dat_alloc_pt_noinit(mc);
2762db149a0SClaudio Imbrenda 	if (!pt)
2772db149a0SClaudio Imbrenda 		return -ENOMEM;
2782db149a0SClaudio Imbrenda 	new.val = virt_to_phys(pt);
2792db149a0SClaudio Imbrenda 
2802db149a0SClaudio Imbrenda 	while (old.h.i || old.h.fc) {
2812db149a0SClaudio Imbrenda 		init.val = pmd_origin_large(old);
2822db149a0SClaudio Imbrenda 		init.h.p = old.h.p;
2832db149a0SClaudio Imbrenda 		init.h.i = old.h.i;
2842db149a0SClaudio Imbrenda 		init.s.d = old.s.fc1.d;
2852db149a0SClaudio Imbrenda 		init.s.w = old.s.fc1.w;
2862db149a0SClaudio Imbrenda 		init.s.y = old.s.fc1.y;
2872db149a0SClaudio Imbrenda 		init.s.sd = old.s.fc1.sd;
2882db149a0SClaudio Imbrenda 		init.s.pr = old.s.fc1.pr;
2892db149a0SClaudio Imbrenda 		pgste_init.val = 0;
2902db149a0SClaudio Imbrenda 		if (old.h.fc) {
2912db149a0SClaudio Imbrenda 			for (i = 0; i < _PAGE_ENTRIES; i++)
2922db149a0SClaudio Imbrenda 				pt->ptes[i].val = init.val | i * PAGE_SIZE;
2932db149a0SClaudio Imbrenda 			/* No need to take locks as the page table is not installed yet. */
2942db149a0SClaudio Imbrenda 			pgste_init.prefix_notif = old.s.fc1.prefix_notif;
2952db149a0SClaudio Imbrenda 			pgste_init.pcl = uses_skeys && init.h.i;
2962db149a0SClaudio Imbrenda 			dat_init_pgstes(pt, pgste_init.val);
2972db149a0SClaudio Imbrenda 		} else {
2982db149a0SClaudio Imbrenda 			dat_init_page_table(pt, init.val, 0);
2992db149a0SClaudio Imbrenda 		}
3002db149a0SClaudio Imbrenda 
3012db149a0SClaudio Imbrenda 		if (dat_pmdp_xchg_atomic(pmdp, old, new, gfn, asce)) {
3022db149a0SClaudio Imbrenda 			if (!pgste_init.pcl)
3032db149a0SClaudio Imbrenda 				return 0;
3042db149a0SClaudio Imbrenda 			for (i = 0; i < _PAGE_ENTRIES; i++) {
3052db149a0SClaudio Imbrenda 				union pgste pgste = pt->pgstes[i];
3062db149a0SClaudio Imbrenda 
3072db149a0SClaudio Imbrenda 				pgste = dat_save_storage_key_into_pgste(pt->ptes[i], pgste);
3082db149a0SClaudio Imbrenda 				pgste_set_unlock(pt->ptes + i, pgste);
3092db149a0SClaudio Imbrenda 			}
3102db149a0SClaudio Imbrenda 			return 0;
3112db149a0SClaudio Imbrenda 		}
3122db149a0SClaudio Imbrenda 		old = READ_ONCE(*pmdp);
3132db149a0SClaudio Imbrenda 	}
3142db149a0SClaudio Imbrenda 
3152db149a0SClaudio Imbrenda 	dat_free_pt(pt);
3162db149a0SClaudio Imbrenda 	return 0;
3172db149a0SClaudio Imbrenda }
3182db149a0SClaudio Imbrenda 
3192db149a0SClaudio Imbrenda /*
3202db149a0SClaudio Imbrenda  * dat_split_crste() - Split a crste into smaller crstes.
3212db149a0SClaudio Imbrenda  *
3222db149a0SClaudio Imbrenda  * Context: This function is assumed to be called with kvm->mmu_lock held.
3232db149a0SClaudio Imbrenda  *
3242db149a0SClaudio Imbrenda  * Return: %0 in case of success, %-ENOMEM if running out of memory.
3252db149a0SClaudio Imbrenda  */
dat_split_crste(struct kvm_s390_mmu_cache * mc,union crste * crstep,gfn_t gfn,union asce asce,bool uses_skeys)3262db149a0SClaudio Imbrenda static int dat_split_crste(struct kvm_s390_mmu_cache *mc, union crste *crstep,
3272db149a0SClaudio Imbrenda 			   gfn_t gfn, union asce asce, bool uses_skeys)
3282db149a0SClaudio Imbrenda {
3292db149a0SClaudio Imbrenda 	struct crst_table *table;
3302db149a0SClaudio Imbrenda 	union crste old, new, init;
3312db149a0SClaudio Imbrenda 	int i;
3322db149a0SClaudio Imbrenda 
3332db149a0SClaudio Imbrenda 	old = READ_ONCE(*crstep);
3342db149a0SClaudio Imbrenda 	if (is_pmd(old))
3352db149a0SClaudio Imbrenda 		return dat_split_ste(mc, &crstep->pmd, gfn, asce, uses_skeys);
3362db149a0SClaudio Imbrenda 
3372db149a0SClaudio Imbrenda 	BUG_ON(!mc);
3382db149a0SClaudio Imbrenda 
3392db149a0SClaudio Imbrenda 	/* Already split, nothing to do. */
3402db149a0SClaudio Imbrenda 	if (!old.h.i && !old.h.fc)
3412db149a0SClaudio Imbrenda 		return 0;
3422db149a0SClaudio Imbrenda 
3432db149a0SClaudio Imbrenda 	table = dat_alloc_crst_noinit(mc);
3442db149a0SClaudio Imbrenda 	if (!table)
3452db149a0SClaudio Imbrenda 		return -ENOMEM;
3462db149a0SClaudio Imbrenda 
3472db149a0SClaudio Imbrenda 	new.val = virt_to_phys(table);
3482db149a0SClaudio Imbrenda 	new.h.tt = old.h.tt;
3492db149a0SClaudio Imbrenda 	new.h.fc0.tl = _REGION_ENTRY_LENGTH;
3502db149a0SClaudio Imbrenda 
3512db149a0SClaudio Imbrenda 	while (old.h.i || old.h.fc) {
3522db149a0SClaudio Imbrenda 		init = old;
3532db149a0SClaudio Imbrenda 		init.h.tt--;
3542db149a0SClaudio Imbrenda 		if (old.h.fc) {
3552db149a0SClaudio Imbrenda 			for (i = 0; i < _CRST_ENTRIES; i++)
3562db149a0SClaudio Imbrenda 				table->crstes[i].val = init.val | i * HPAGE_SIZE;
3572db149a0SClaudio Imbrenda 		} else {
3582db149a0SClaudio Imbrenda 			crst_table_init((void *)table, init.val);
3592db149a0SClaudio Imbrenda 		}
3602db149a0SClaudio Imbrenda 		if (dat_crstep_xchg_atomic(crstep, old, new, gfn, asce))
3612db149a0SClaudio Imbrenda 			return 0;
3622db149a0SClaudio Imbrenda 		old = READ_ONCE(*crstep);
3632db149a0SClaudio Imbrenda 	}
3642db149a0SClaudio Imbrenda 
3652db149a0SClaudio Imbrenda 	dat_free_crst(table);
3662db149a0SClaudio Imbrenda 	return 0;
3672db149a0SClaudio Imbrenda }
3682db149a0SClaudio Imbrenda 
3692db149a0SClaudio Imbrenda /**
3702db149a0SClaudio Imbrenda  * dat_entry_walk() - Walk the gmap page tables.
3712db149a0SClaudio Imbrenda  * @mc: Cache to use to allocate dat tables, if needed; can be NULL if neither
3722db149a0SClaudio Imbrenda  *      %DAT_WALK_SPLIT or %DAT_WALK_ALLOC is specified in @flags.
3732db149a0SClaudio Imbrenda  * @gfn: Guest frame.
3742db149a0SClaudio Imbrenda  * @asce: The ASCE of the address space.
3752db149a0SClaudio Imbrenda  * @flags: Flags from WALK_* macros.
3762db149a0SClaudio Imbrenda  * @walk_level: Level to walk to, from LEVEL_* macros.
3772db149a0SClaudio Imbrenda  * @last: Will be filled the last visited non-pte DAT entry.
3782db149a0SClaudio Imbrenda  * @ptepp: Will be filled the last visited pte entry, if any, otherwise NULL.
3792db149a0SClaudio Imbrenda  *
3802db149a0SClaudio Imbrenda  * Returns a table entry pointer for the given guest address and @walk_level.
3812db149a0SClaudio Imbrenda  *
3822db149a0SClaudio Imbrenda  * The @flags have the following meanings:
3832db149a0SClaudio Imbrenda  * * %DAT_WALK_IGN_HOLES: consider holes as normal table entries
3842db149a0SClaudio Imbrenda  * * %DAT_WALK_ALLOC: allocate new tables to reach the requested level, if needed
3852db149a0SClaudio Imbrenda  * * %DAT_WALK_SPLIT: split existing large pages to reach the requested level, if needed
3862db149a0SClaudio Imbrenda  * * %DAT_WALK_LEAF: return successfully whenever a large page is encountered
3872db149a0SClaudio Imbrenda  * * %DAT_WALK_ANY: return successfully even if the requested level could not be reached
3882db149a0SClaudio Imbrenda  * * %DAT_WALK_CONTINUE: walk to the requested level with the specified flags, and then try to
3892db149a0SClaudio Imbrenda  *                       continue walking to ptes with only DAT_WALK_ANY
3902db149a0SClaudio Imbrenda  * * %DAT_WALK_USES_SKEYS: storage keys are in use
3912db149a0SClaudio Imbrenda  *
3922db149a0SClaudio Imbrenda  * Context: called with kvm->mmu_lock held.
3932db149a0SClaudio Imbrenda  *
3942db149a0SClaudio Imbrenda  * Return:
3952db149a0SClaudio Imbrenda  * * %PGM_ADDRESSING if the requested address lies outside memory
3962db149a0SClaudio Imbrenda  * * a PIC number if the requested address lies in a memory hole of type _DAT_TOKEN_PIC
3972db149a0SClaudio Imbrenda  * * %-EFAULT if the requested address lies inside a memory hole of a different type
3982db149a0SClaudio Imbrenda  * * %-EINVAL if the given ASCE is not compatible with the requested level
3992db149a0SClaudio Imbrenda  * * %-EFBIG if the requested level could not be reached because a larger frame was found
4002db149a0SClaudio Imbrenda  * * %-ENOENT if the requested level could not be reached for other reasons
4012db149a0SClaudio Imbrenda  * * %-ENOMEM if running out of memory while allocating or splitting a table
4022db149a0SClaudio Imbrenda  */
dat_entry_walk(struct kvm_s390_mmu_cache * mc,gfn_t gfn,union asce asce,int flags,int walk_level,union crste ** last,union pte ** ptepp)4032db149a0SClaudio Imbrenda int dat_entry_walk(struct kvm_s390_mmu_cache *mc, gfn_t gfn, union asce asce, int flags,
4042db149a0SClaudio Imbrenda 		   int walk_level, union crste **last, union pte **ptepp)
4052db149a0SClaudio Imbrenda {
4062db149a0SClaudio Imbrenda 	union vaddress vaddr = { .addr = gfn_to_gpa(gfn) };
4072db149a0SClaudio Imbrenda 	bool continue_anyway = flags & DAT_WALK_CONTINUE;
4082db149a0SClaudio Imbrenda 	bool uses_skeys = flags & DAT_WALK_USES_SKEYS;
4092db149a0SClaudio Imbrenda 	bool ign_holes = flags & DAT_WALK_IGN_HOLES;
4102db149a0SClaudio Imbrenda 	bool allocate = flags & DAT_WALK_ALLOC;
4112db149a0SClaudio Imbrenda 	bool split = flags & DAT_WALK_SPLIT;
4122db149a0SClaudio Imbrenda 	bool leaf = flags & DAT_WALK_LEAF;
4132db149a0SClaudio Imbrenda 	bool any = flags & DAT_WALK_ANY;
4142db149a0SClaudio Imbrenda 	struct page_table *pgtable;
4152db149a0SClaudio Imbrenda 	struct crst_table *table;
4162db149a0SClaudio Imbrenda 	union crste entry;
4172db149a0SClaudio Imbrenda 	int rc;
4182db149a0SClaudio Imbrenda 
4192db149a0SClaudio Imbrenda 	*last = NULL;
4202db149a0SClaudio Imbrenda 	*ptepp = NULL;
4212db149a0SClaudio Imbrenda 	if (WARN_ON_ONCE(unlikely(!asce.val)))
4222db149a0SClaudio Imbrenda 		return -EINVAL;
4232db149a0SClaudio Imbrenda 	if (WARN_ON_ONCE(unlikely(walk_level > asce.dt)))
4242db149a0SClaudio Imbrenda 		return -EINVAL;
4252db149a0SClaudio Imbrenda 	if (!asce_contains_gfn(asce, gfn))
4262db149a0SClaudio Imbrenda 		return PGM_ADDRESSING;
4272db149a0SClaudio Imbrenda 
4282db149a0SClaudio Imbrenda 	table = dereference_asce(asce);
4292db149a0SClaudio Imbrenda 	if (asce.dt >= ASCE_TYPE_REGION1) {
4302db149a0SClaudio Imbrenda 		*last = table->crstes + vaddr.rfx;
4312db149a0SClaudio Imbrenda 		entry = READ_ONCE(**last);
4322db149a0SClaudio Imbrenda 		if (WARN_ON_ONCE(entry.h.tt != TABLE_TYPE_REGION1))
4332db149a0SClaudio Imbrenda 			return -EINVAL;
4342db149a0SClaudio Imbrenda 		if (crste_hole(entry) && !ign_holes)
4352db149a0SClaudio Imbrenda 			return entry.tok.type == _DAT_TOKEN_PIC ? entry.tok.par : -EFAULT;
4362db149a0SClaudio Imbrenda 		if (walk_level == TABLE_TYPE_REGION1)
4372db149a0SClaudio Imbrenda 			return 0;
4382db149a0SClaudio Imbrenda 		if (entry.pgd.h.i) {
4392db149a0SClaudio Imbrenda 			if (!allocate)
4402db149a0SClaudio Imbrenda 				return any ? 0 : -ENOENT;
4412db149a0SClaudio Imbrenda 			rc = dat_split_crste(mc, *last, gfn, asce, uses_skeys);
4422db149a0SClaudio Imbrenda 			if (rc)
4432db149a0SClaudio Imbrenda 				return rc;
4442db149a0SClaudio Imbrenda 			entry = READ_ONCE(**last);
4452db149a0SClaudio Imbrenda 		}
4462db149a0SClaudio Imbrenda 		table = dereference_crste(entry.pgd);
4472db149a0SClaudio Imbrenda 	}
4482db149a0SClaudio Imbrenda 
4492db149a0SClaudio Imbrenda 	if (asce.dt >= ASCE_TYPE_REGION2) {
4502db149a0SClaudio Imbrenda 		*last = table->crstes + vaddr.rsx;
4512db149a0SClaudio Imbrenda 		entry = READ_ONCE(**last);
4522db149a0SClaudio Imbrenda 		if (WARN_ON_ONCE(entry.h.tt != TABLE_TYPE_REGION2))
4532db149a0SClaudio Imbrenda 			return -EINVAL;
4542db149a0SClaudio Imbrenda 		if (crste_hole(entry) && !ign_holes)
4552db149a0SClaudio Imbrenda 			return entry.tok.type == _DAT_TOKEN_PIC ? entry.tok.par : -EFAULT;
4562db149a0SClaudio Imbrenda 		if (walk_level == TABLE_TYPE_REGION2)
4572db149a0SClaudio Imbrenda 			return 0;
4582db149a0SClaudio Imbrenda 		if (entry.p4d.h.i) {
4592db149a0SClaudio Imbrenda 			if (!allocate)
4602db149a0SClaudio Imbrenda 				return any ? 0 : -ENOENT;
4612db149a0SClaudio Imbrenda 			rc = dat_split_crste(mc, *last, gfn, asce, uses_skeys);
4622db149a0SClaudio Imbrenda 			if (rc)
4632db149a0SClaudio Imbrenda 				return rc;
4642db149a0SClaudio Imbrenda 			entry = READ_ONCE(**last);
4652db149a0SClaudio Imbrenda 		}
4662db149a0SClaudio Imbrenda 		table = dereference_crste(entry.p4d);
4672db149a0SClaudio Imbrenda 	}
4682db149a0SClaudio Imbrenda 
4692db149a0SClaudio Imbrenda 	if (asce.dt >= ASCE_TYPE_REGION3) {
4702db149a0SClaudio Imbrenda 		*last = table->crstes + vaddr.rtx;
4712db149a0SClaudio Imbrenda 		entry = READ_ONCE(**last);
4722db149a0SClaudio Imbrenda 		if (WARN_ON_ONCE(entry.h.tt != TABLE_TYPE_REGION3))
4732db149a0SClaudio Imbrenda 			return -EINVAL;
4742db149a0SClaudio Imbrenda 		if (crste_hole(entry) && !ign_holes)
4752db149a0SClaudio Imbrenda 			return entry.tok.type == _DAT_TOKEN_PIC ? entry.tok.par : -EFAULT;
4762db149a0SClaudio Imbrenda 		if (walk_level == TABLE_TYPE_REGION3 &&
4772db149a0SClaudio Imbrenda 		    continue_anyway && !entry.pud.h.fc && !entry.h.i) {
4782db149a0SClaudio Imbrenda 			walk_level = TABLE_TYPE_PAGE_TABLE;
4792db149a0SClaudio Imbrenda 			allocate = false;
4802db149a0SClaudio Imbrenda 		}
4812db149a0SClaudio Imbrenda 		if (walk_level == TABLE_TYPE_REGION3 || ((leaf || any) && entry.pud.h.fc))
4822db149a0SClaudio Imbrenda 			return 0;
4832db149a0SClaudio Imbrenda 		if (entry.pud.h.i && !entry.pud.h.fc) {
4842db149a0SClaudio Imbrenda 			if (!allocate)
4852db149a0SClaudio Imbrenda 				return any ? 0 : -ENOENT;
4862db149a0SClaudio Imbrenda 			rc = dat_split_crste(mc, *last, gfn, asce, uses_skeys);
4872db149a0SClaudio Imbrenda 			if (rc)
4882db149a0SClaudio Imbrenda 				return rc;
4892db149a0SClaudio Imbrenda 			entry = READ_ONCE(**last);
4902db149a0SClaudio Imbrenda 		}
4912db149a0SClaudio Imbrenda 		if (walk_level <= TABLE_TYPE_SEGMENT && entry.pud.h.fc) {
4922db149a0SClaudio Imbrenda 			if (!split)
4932db149a0SClaudio Imbrenda 				return -EFBIG;
4942db149a0SClaudio Imbrenda 			rc = dat_split_crste(mc, *last, gfn, asce, uses_skeys);
4952db149a0SClaudio Imbrenda 			if (rc)
4962db149a0SClaudio Imbrenda 				return rc;
4972db149a0SClaudio Imbrenda 			entry = READ_ONCE(**last);
4982db149a0SClaudio Imbrenda 		}
4992db149a0SClaudio Imbrenda 		table = dereference_crste(entry.pud);
5002db149a0SClaudio Imbrenda 	}
5012db149a0SClaudio Imbrenda 
5022db149a0SClaudio Imbrenda 	*last = table->crstes + vaddr.sx;
5032db149a0SClaudio Imbrenda 	entry = READ_ONCE(**last);
5042db149a0SClaudio Imbrenda 	if (WARN_ON_ONCE(entry.h.tt != TABLE_TYPE_SEGMENT))
5052db149a0SClaudio Imbrenda 		return -EINVAL;
5062db149a0SClaudio Imbrenda 	if (crste_hole(entry) && !ign_holes)
5072db149a0SClaudio Imbrenda 		return entry.tok.type == _DAT_TOKEN_PIC ? entry.tok.par : -EFAULT;
5082db149a0SClaudio Imbrenda 	if (continue_anyway && !entry.pmd.h.fc && !entry.h.i) {
5092db149a0SClaudio Imbrenda 		walk_level = TABLE_TYPE_PAGE_TABLE;
5102db149a0SClaudio Imbrenda 		allocate = false;
5112db149a0SClaudio Imbrenda 	}
5122db149a0SClaudio Imbrenda 	if (walk_level == TABLE_TYPE_SEGMENT || ((leaf || any) && entry.pmd.h.fc))
5132db149a0SClaudio Imbrenda 		return 0;
5142db149a0SClaudio Imbrenda 
5152db149a0SClaudio Imbrenda 	if (entry.pmd.h.i && !entry.pmd.h.fc) {
5162db149a0SClaudio Imbrenda 		if (!allocate)
5172db149a0SClaudio Imbrenda 			return any ? 0 : -ENOENT;
5182db149a0SClaudio Imbrenda 		rc = dat_split_ste(mc, &(*last)->pmd, gfn, asce, uses_skeys);
5192db149a0SClaudio Imbrenda 		if (rc)
5202db149a0SClaudio Imbrenda 			return rc;
5212db149a0SClaudio Imbrenda 		entry = READ_ONCE(**last);
5222db149a0SClaudio Imbrenda 	}
5232db149a0SClaudio Imbrenda 	if (walk_level <= TABLE_TYPE_PAGE_TABLE && entry.pmd.h.fc) {
5242db149a0SClaudio Imbrenda 		if (!split)
5252db149a0SClaudio Imbrenda 			return -EFBIG;
5262db149a0SClaudio Imbrenda 		rc = dat_split_ste(mc, &(*last)->pmd, gfn, asce, uses_skeys);
5272db149a0SClaudio Imbrenda 		if (rc)
5282db149a0SClaudio Imbrenda 			return rc;
5292db149a0SClaudio Imbrenda 		entry = READ_ONCE(**last);
5302db149a0SClaudio Imbrenda 	}
5312db149a0SClaudio Imbrenda 	pgtable = dereference_pmd(entry.pmd);
5322db149a0SClaudio Imbrenda 	*ptepp = pgtable->ptes + vaddr.px;
5332db149a0SClaudio Imbrenda 	if (pte_hole(**ptepp) && !ign_holes)
5342db149a0SClaudio Imbrenda 		return (*ptepp)->tok.type == _DAT_TOKEN_PIC ? (*ptepp)->tok.par : -EFAULT;
5352db149a0SClaudio Imbrenda 	return 0;
5362db149a0SClaudio Imbrenda }
5372db149a0SClaudio Imbrenda 
dat_pte_walk_range(gfn_t gfn,gfn_t end,struct page_table * table,struct dat_walk * w)5382db149a0SClaudio Imbrenda static long dat_pte_walk_range(gfn_t gfn, gfn_t end, struct page_table *table, struct dat_walk *w)
5392db149a0SClaudio Imbrenda {
5402db149a0SClaudio Imbrenda 	unsigned int idx = gfn & (_PAGE_ENTRIES - 1);
5412db149a0SClaudio Imbrenda 	long rc = 0;
5422db149a0SClaudio Imbrenda 
5432db149a0SClaudio Imbrenda 	for ( ; gfn < end; idx++, gfn++) {
5442db149a0SClaudio Imbrenda 		if (pte_hole(READ_ONCE(table->ptes[idx]))) {
5452db149a0SClaudio Imbrenda 			if (!(w->flags & DAT_WALK_IGN_HOLES))
5462db149a0SClaudio Imbrenda 				return -EFAULT;
5472db149a0SClaudio Imbrenda 			if (!(w->flags & DAT_WALK_ANY))
5482db149a0SClaudio Imbrenda 				continue;
5492db149a0SClaudio Imbrenda 		}
5502db149a0SClaudio Imbrenda 
5512db149a0SClaudio Imbrenda 		rc = w->ops->pte_entry(table->ptes + idx, gfn, gfn + 1, w);
5522db149a0SClaudio Imbrenda 		if (rc)
5532db149a0SClaudio Imbrenda 			break;
5542db149a0SClaudio Imbrenda 	}
5552db149a0SClaudio Imbrenda 	return rc;
5562db149a0SClaudio Imbrenda }
5572db149a0SClaudio Imbrenda 
dat_crste_walk_range(gfn_t start,gfn_t end,struct crst_table * table,struct dat_walk * walk)5582db149a0SClaudio Imbrenda static long dat_crste_walk_range(gfn_t start, gfn_t end, struct crst_table *table,
5592db149a0SClaudio Imbrenda 				 struct dat_walk *walk)
5602db149a0SClaudio Imbrenda {
5612db149a0SClaudio Imbrenda 	unsigned long idx, cur_shift, cur_size;
5622db149a0SClaudio Imbrenda 	dat_walk_op the_op;
5632db149a0SClaudio Imbrenda 	union crste crste;
5642db149a0SClaudio Imbrenda 	gfn_t cur, next;
5652db149a0SClaudio Imbrenda 	long rc = 0;
5662db149a0SClaudio Imbrenda 
5672db149a0SClaudio Imbrenda 	cur_shift = 8 + table->crstes[0].h.tt * 11;
5682db149a0SClaudio Imbrenda 	idx = (start >> cur_shift) & (_CRST_ENTRIES - 1);
5692db149a0SClaudio Imbrenda 	cur_size = 1UL << cur_shift;
5702db149a0SClaudio Imbrenda 
5712db149a0SClaudio Imbrenda 	for (cur = ALIGN_DOWN(start, cur_size); cur < end; idx++, cur = next) {
5722db149a0SClaudio Imbrenda 		next = cur + cur_size;
5732db149a0SClaudio Imbrenda 		walk->last = table->crstes + idx;
5742db149a0SClaudio Imbrenda 		crste = READ_ONCE(*walk->last);
5752db149a0SClaudio Imbrenda 
5762db149a0SClaudio Imbrenda 		if (crste_hole(crste)) {
5772db149a0SClaudio Imbrenda 			if (!(walk->flags & DAT_WALK_IGN_HOLES))
5782db149a0SClaudio Imbrenda 				return -EFAULT;
5792db149a0SClaudio Imbrenda 			if (!(walk->flags & DAT_WALK_ANY))
5802db149a0SClaudio Imbrenda 				continue;
5812db149a0SClaudio Imbrenda 		}
5822db149a0SClaudio Imbrenda 
5832db149a0SClaudio Imbrenda 		the_op = walk->ops->crste_ops[crste.h.tt];
5842db149a0SClaudio Imbrenda 		if (the_op) {
5852db149a0SClaudio Imbrenda 			rc = the_op(walk->last, cur, next, walk);
5862db149a0SClaudio Imbrenda 			crste = READ_ONCE(*walk->last);
5872db149a0SClaudio Imbrenda 		}
5882db149a0SClaudio Imbrenda 		if (rc)
5892db149a0SClaudio Imbrenda 			break;
5902db149a0SClaudio Imbrenda 		if (!crste.h.i && !crste.h.fc) {
5912db149a0SClaudio Imbrenda 			if (!is_pmd(crste))
5922db149a0SClaudio Imbrenda 				rc = dat_crste_walk_range(max(start, cur), min(end, next),
5932db149a0SClaudio Imbrenda 							  _dereference_crste(crste), walk);
5942db149a0SClaudio Imbrenda 			else if (walk->ops->pte_entry)
5952db149a0SClaudio Imbrenda 				rc = dat_pte_walk_range(max(start, cur), min(end, next),
5962db149a0SClaudio Imbrenda 							dereference_pmd(crste.pmd), walk);
5972db149a0SClaudio Imbrenda 		}
5982db149a0SClaudio Imbrenda 	}
5992db149a0SClaudio Imbrenda 	return rc;
6002db149a0SClaudio Imbrenda }
6012db149a0SClaudio Imbrenda 
6022db149a0SClaudio Imbrenda /**
6032db149a0SClaudio Imbrenda  * _dat_walk_gfn_range() - Walk DAT tables.
6042db149a0SClaudio Imbrenda  * @start: The first guest page frame to walk.
6052db149a0SClaudio Imbrenda  * @end: The guest page frame immediately after the last one to walk.
6062db149a0SClaudio Imbrenda  * @asce: The ASCE of the guest mapping.
6072db149a0SClaudio Imbrenda  * @ops: The gmap_walk_ops that will be used to perform the walk.
6082db149a0SClaudio Imbrenda  * @flags: Flags from WALK_* (currently only WALK_IGN_HOLES is supported).
6092db149a0SClaudio Imbrenda  * @priv: Will be passed as-is to the callbacks.
6102db149a0SClaudio Imbrenda  *
6112db149a0SClaudio Imbrenda  * Any callback returning non-zero causes the walk to stop immediately.
6122db149a0SClaudio Imbrenda  *
6132db149a0SClaudio Imbrenda  * Return: %-EINVAL in case of error, %-EFAULT if @start is too high for the
6142db149a0SClaudio Imbrenda  *         given ASCE unless the DAT_WALK_IGN_HOLES flag is specified,
6152db149a0SClaudio Imbrenda  *         otherwise it returns whatever the callbacks return.
6162db149a0SClaudio Imbrenda  */
_dat_walk_gfn_range(gfn_t start,gfn_t end,union asce asce,const struct dat_walk_ops * ops,int flags,void * priv)6172db149a0SClaudio Imbrenda long _dat_walk_gfn_range(gfn_t start, gfn_t end, union asce asce,
6182db149a0SClaudio Imbrenda 			 const struct dat_walk_ops *ops, int flags, void *priv)
6192db149a0SClaudio Imbrenda {
6202db149a0SClaudio Imbrenda 	struct crst_table *table = dereference_asce(asce);
6212db149a0SClaudio Imbrenda 	struct dat_walk walk = {
6222db149a0SClaudio Imbrenda 		.ops	= ops,
6232db149a0SClaudio Imbrenda 		.asce	= asce,
6242db149a0SClaudio Imbrenda 		.priv	= priv,
6252db149a0SClaudio Imbrenda 		.flags	= flags,
6262db149a0SClaudio Imbrenda 		.start	= start,
6272db149a0SClaudio Imbrenda 		.end	= end,
6282db149a0SClaudio Imbrenda 	};
6292db149a0SClaudio Imbrenda 
6302db149a0SClaudio Imbrenda 	if (WARN_ON_ONCE(unlikely(!asce.val)))
6312db149a0SClaudio Imbrenda 		return -EINVAL;
6322db149a0SClaudio Imbrenda 	if (!asce_contains_gfn(asce, start))
6332db149a0SClaudio Imbrenda 		return (flags & DAT_WALK_IGN_HOLES) ? 0 : -EFAULT;
6342db149a0SClaudio Imbrenda 
6352db149a0SClaudio Imbrenda 	return dat_crste_walk_range(start, min(end, asce_end(asce)), table, &walk);
6362db149a0SClaudio Imbrenda }
6378e03e831SClaudio Imbrenda 
dat_get_storage_key(union asce asce,gfn_t gfn,union skey * skey)6388e03e831SClaudio Imbrenda int dat_get_storage_key(union asce asce, gfn_t gfn, union skey *skey)
6398e03e831SClaudio Imbrenda {
6408e03e831SClaudio Imbrenda 	union crste *crstep;
6418e03e831SClaudio Imbrenda 	union pgste pgste;
6428e03e831SClaudio Imbrenda 	union pte *ptep;
6438e03e831SClaudio Imbrenda 	int rc;
6448e03e831SClaudio Imbrenda 
6458e03e831SClaudio Imbrenda 	skey->skey = 0;
6468e03e831SClaudio Imbrenda 	rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep);
6478e03e831SClaudio Imbrenda 	if (rc)
6488e03e831SClaudio Imbrenda 		return rc;
6498e03e831SClaudio Imbrenda 
6508e03e831SClaudio Imbrenda 	if (!ptep) {
6518e03e831SClaudio Imbrenda 		union crste crste;
6528e03e831SClaudio Imbrenda 
6538e03e831SClaudio Imbrenda 		crste = READ_ONCE(*crstep);
6548e03e831SClaudio Imbrenda 		if (!crste.h.fc || !crste.s.fc1.pr)
6558e03e831SClaudio Imbrenda 			return 0;
6568e03e831SClaudio Imbrenda 		skey->skey = page_get_storage_key(large_crste_to_phys(crste, gfn));
6578e03e831SClaudio Imbrenda 		return 0;
6588e03e831SClaudio Imbrenda 	}
6598e03e831SClaudio Imbrenda 	pgste = pgste_get_lock(ptep);
6608e03e831SClaudio Imbrenda 	if (ptep->h.i) {
6618e03e831SClaudio Imbrenda 		skey->acc = pgste.acc;
6628e03e831SClaudio Imbrenda 		skey->fp = pgste.fp;
6638e03e831SClaudio Imbrenda 	} else {
6648e03e831SClaudio Imbrenda 		skey->skey = page_get_storage_key(pte_origin(*ptep));
6658e03e831SClaudio Imbrenda 	}
6668e03e831SClaudio Imbrenda 	skey->r |= pgste.gr;
6678e03e831SClaudio Imbrenda 	skey->c |= pgste.gc;
6688e03e831SClaudio Imbrenda 	pgste_set_unlock(ptep, pgste);
6698e03e831SClaudio Imbrenda 	return 0;
6708e03e831SClaudio Imbrenda }
6718e03e831SClaudio Imbrenda 
dat_update_ptep_sd(union pgste old,union pgste pgste,union pte * ptep)6728e03e831SClaudio Imbrenda static void dat_update_ptep_sd(union pgste old, union pgste pgste, union pte *ptep)
6738e03e831SClaudio Imbrenda {
6748e03e831SClaudio Imbrenda 	if (pgste.acc != old.acc || pgste.fp != old.fp || pgste.gr != old.gr || pgste.gc != old.gc)
6758e03e831SClaudio Imbrenda 		__atomic64_or(_PAGE_SD, &ptep->val);
6768e03e831SClaudio Imbrenda }
6778e03e831SClaudio Imbrenda 
dat_set_storage_key(struct kvm_s390_mmu_cache * mc,union asce asce,gfn_t gfn,union skey skey,bool nq)6788e03e831SClaudio Imbrenda int dat_set_storage_key(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gfn,
6798e03e831SClaudio Imbrenda 			union skey skey, bool nq)
6808e03e831SClaudio Imbrenda {
6818e03e831SClaudio Imbrenda 	union pgste pgste, old;
6828e03e831SClaudio Imbrenda 	union crste *crstep;
6838e03e831SClaudio Imbrenda 	union pte *ptep;
6848e03e831SClaudio Imbrenda 	int rc;
6858e03e831SClaudio Imbrenda 
6868e03e831SClaudio Imbrenda 	rc = dat_entry_walk(mc, gfn, asce, DAT_WALK_LEAF_ALLOC, TABLE_TYPE_PAGE_TABLE,
6878e03e831SClaudio Imbrenda 			    &crstep, &ptep);
6888e03e831SClaudio Imbrenda 	if (rc)
6898e03e831SClaudio Imbrenda 		return rc;
6908e03e831SClaudio Imbrenda 
6918e03e831SClaudio Imbrenda 	if (!ptep) {
6928e03e831SClaudio Imbrenda 		page_set_storage_key(large_crste_to_phys(*crstep, gfn), skey.skey, !nq);
6938e03e831SClaudio Imbrenda 		return 0;
6948e03e831SClaudio Imbrenda 	}
6958e03e831SClaudio Imbrenda 
6968e03e831SClaudio Imbrenda 	old = pgste_get_lock(ptep);
6978e03e831SClaudio Imbrenda 	pgste = old;
6988e03e831SClaudio Imbrenda 
6998e03e831SClaudio Imbrenda 	pgste.acc = skey.acc;
7008e03e831SClaudio Imbrenda 	pgste.fp = skey.fp;
7018e03e831SClaudio Imbrenda 	pgste.gc = skey.c;
7028e03e831SClaudio Imbrenda 	pgste.gr = skey.r;
7038e03e831SClaudio Imbrenda 
7048e03e831SClaudio Imbrenda 	if (!ptep->h.i) {
7058e03e831SClaudio Imbrenda 		union skey old_skey;
7068e03e831SClaudio Imbrenda 
7078e03e831SClaudio Imbrenda 		old_skey.skey = page_get_storage_key(pte_origin(*ptep));
7088e03e831SClaudio Imbrenda 		pgste.hc |= old_skey.c;
7098e03e831SClaudio Imbrenda 		pgste.hr |= old_skey.r;
7108e03e831SClaudio Imbrenda 		old_skey.c = old.gc;
7118e03e831SClaudio Imbrenda 		old_skey.r = old.gr;
7128e03e831SClaudio Imbrenda 		skey.r = 0;
7138e03e831SClaudio Imbrenda 		skey.c = 0;
7148e03e831SClaudio Imbrenda 		page_set_storage_key(pte_origin(*ptep), skey.skey, !nq);
7158e03e831SClaudio Imbrenda 	}
7168e03e831SClaudio Imbrenda 
7178e03e831SClaudio Imbrenda 	dat_update_ptep_sd(old, pgste, ptep);
7188e03e831SClaudio Imbrenda 	pgste_set_unlock(ptep, pgste);
7198e03e831SClaudio Imbrenda 	return 0;
7208e03e831SClaudio Imbrenda }
7218e03e831SClaudio Imbrenda 
page_cond_set_storage_key(phys_addr_t paddr,union skey skey,union skey * oldkey,bool nq,bool mr,bool mc)7228e03e831SClaudio Imbrenda static bool page_cond_set_storage_key(phys_addr_t paddr, union skey skey, union skey *oldkey,
7238e03e831SClaudio Imbrenda 				      bool nq, bool mr, bool mc)
7248e03e831SClaudio Imbrenda {
7258e03e831SClaudio Imbrenda 	oldkey->skey = page_get_storage_key(paddr);
7268e03e831SClaudio Imbrenda 	if (oldkey->acc == skey.acc && oldkey->fp == skey.fp &&
7278e03e831SClaudio Imbrenda 	    (oldkey->r == skey.r || mr) && (oldkey->c == skey.c || mc))
7288e03e831SClaudio Imbrenda 		return false;
7298e03e831SClaudio Imbrenda 	page_set_storage_key(paddr, skey.skey, !nq);
7308e03e831SClaudio Imbrenda 	return true;
7318e03e831SClaudio Imbrenda }
7328e03e831SClaudio Imbrenda 
dat_cond_set_storage_key(struct kvm_s390_mmu_cache * mmc,union asce asce,gfn_t gfn,union skey skey,union skey * oldkey,bool nq,bool mr,bool mc)7338e03e831SClaudio Imbrenda int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gfn_t gfn,
7348e03e831SClaudio Imbrenda 			     union skey skey, union skey *oldkey, bool nq, bool mr, bool mc)
7358e03e831SClaudio Imbrenda {
7368e03e831SClaudio Imbrenda 	union pgste pgste, old;
7378e03e831SClaudio Imbrenda 	union crste *crstep;
7388e03e831SClaudio Imbrenda 	union skey prev;
7398e03e831SClaudio Imbrenda 	union pte *ptep;
7408e03e831SClaudio Imbrenda 	int rc;
7418e03e831SClaudio Imbrenda 
7428e03e831SClaudio Imbrenda 	rc = dat_entry_walk(mmc, gfn, asce, DAT_WALK_LEAF_ALLOC, TABLE_TYPE_PAGE_TABLE,
7438e03e831SClaudio Imbrenda 			    &crstep, &ptep);
7448e03e831SClaudio Imbrenda 	if (rc)
7458e03e831SClaudio Imbrenda 		return rc;
7468e03e831SClaudio Imbrenda 
7478e03e831SClaudio Imbrenda 	if (!ptep)
7488e03e831SClaudio Imbrenda 		return page_cond_set_storage_key(large_crste_to_phys(*crstep, gfn), skey, oldkey,
7498e03e831SClaudio Imbrenda 						 nq, mr, mc);
7508e03e831SClaudio Imbrenda 
7518e03e831SClaudio Imbrenda 	old = pgste_get_lock(ptep);
7528e03e831SClaudio Imbrenda 	pgste = old;
7538e03e831SClaudio Imbrenda 
7548e03e831SClaudio Imbrenda 	rc = 1;
7558e03e831SClaudio Imbrenda 	pgste.acc = skey.acc;
7568e03e831SClaudio Imbrenda 	pgste.fp = skey.fp;
7578e03e831SClaudio Imbrenda 	pgste.gc = skey.c;
7588e03e831SClaudio Imbrenda 	pgste.gr = skey.r;
7598e03e831SClaudio Imbrenda 
7608e03e831SClaudio Imbrenda 	if (!ptep->h.i) {
7618e03e831SClaudio Imbrenda 		rc = page_cond_set_storage_key(pte_origin(*ptep), skey, &prev, nq, mr, mc);
7628e03e831SClaudio Imbrenda 		pgste.hc |= prev.c;
7638e03e831SClaudio Imbrenda 		pgste.hr |= prev.r;
7648e03e831SClaudio Imbrenda 		prev.c |= old.gc;
7658e03e831SClaudio Imbrenda 		prev.r |= old.gr;
7668e03e831SClaudio Imbrenda 	} else {
7678e03e831SClaudio Imbrenda 		prev.acc = old.acc;
7688e03e831SClaudio Imbrenda 		prev.fp = old.fp;
7698e03e831SClaudio Imbrenda 		prev.c = old.gc;
7708e03e831SClaudio Imbrenda 		prev.r = old.gr;
7718e03e831SClaudio Imbrenda 	}
7728e03e831SClaudio Imbrenda 	if (oldkey)
7738e03e831SClaudio Imbrenda 		*oldkey = prev;
7748e03e831SClaudio Imbrenda 
7758e03e831SClaudio Imbrenda 	dat_update_ptep_sd(old, pgste, ptep);
7768e03e831SClaudio Imbrenda 	pgste_set_unlock(ptep, pgste);
7778e03e831SClaudio Imbrenda 	return rc;
7788e03e831SClaudio Imbrenda }
7798e03e831SClaudio Imbrenda 
dat_reset_reference_bit(union asce asce,gfn_t gfn)7808e03e831SClaudio Imbrenda int dat_reset_reference_bit(union asce asce, gfn_t gfn)
7818e03e831SClaudio Imbrenda {
7828e03e831SClaudio Imbrenda 	union pgste pgste, old;
7838e03e831SClaudio Imbrenda 	union crste *crstep;
7848e03e831SClaudio Imbrenda 	union pte *ptep;
7858e03e831SClaudio Imbrenda 	int rc;
7868e03e831SClaudio Imbrenda 
7878e03e831SClaudio Imbrenda 	rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep);
7888e03e831SClaudio Imbrenda 	if (rc)
7898e03e831SClaudio Imbrenda 		return rc;
7908e03e831SClaudio Imbrenda 
7918e03e831SClaudio Imbrenda 	if (!ptep) {
7928e03e831SClaudio Imbrenda 		union crste crste = READ_ONCE(*crstep);
7938e03e831SClaudio Imbrenda 
7948e03e831SClaudio Imbrenda 		if (!crste.h.fc || !crste.s.fc1.pr)
7958e03e831SClaudio Imbrenda 			return 0;
7968e03e831SClaudio Imbrenda 		return page_reset_referenced(large_crste_to_phys(*crstep, gfn));
7978e03e831SClaudio Imbrenda 	}
7988e03e831SClaudio Imbrenda 	old = pgste_get_lock(ptep);
7998e03e831SClaudio Imbrenda 	pgste = old;
8008e03e831SClaudio Imbrenda 
8018e03e831SClaudio Imbrenda 	if (!ptep->h.i) {
8028e03e831SClaudio Imbrenda 		rc = page_reset_referenced(pte_origin(*ptep));
8038e03e831SClaudio Imbrenda 		pgste.hr = rc >> 1;
8048e03e831SClaudio Imbrenda 	}
8058e03e831SClaudio Imbrenda 	rc |= (pgste.gr << 1) | pgste.gc;
8068e03e831SClaudio Imbrenda 	pgste.gr = 0;
8078e03e831SClaudio Imbrenda 
8088e03e831SClaudio Imbrenda 	dat_update_ptep_sd(old, pgste, ptep);
8098e03e831SClaudio Imbrenda 	pgste_set_unlock(ptep, pgste);
8108e03e831SClaudio Imbrenda 	return rc;
8118e03e831SClaudio Imbrenda }
8128e03e831SClaudio Imbrenda 
dat_reset_skeys_pte(union pte * ptep,gfn_t gfn,gfn_t next,struct dat_walk * walk)8138e03e831SClaudio Imbrenda static long dat_reset_skeys_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
8148e03e831SClaudio Imbrenda {
8158e03e831SClaudio Imbrenda 	union pgste pgste;
8168e03e831SClaudio Imbrenda 
8178e03e831SClaudio Imbrenda 	pgste = pgste_get_lock(ptep);
8188e03e831SClaudio Imbrenda 	pgste.acc = 0;
8198e03e831SClaudio Imbrenda 	pgste.fp = 0;
8208e03e831SClaudio Imbrenda 	pgste.gr = 0;
8218e03e831SClaudio Imbrenda 	pgste.gc = 0;
8228e03e831SClaudio Imbrenda 	if (ptep->s.pr)
8238e03e831SClaudio Imbrenda 		page_set_storage_key(pte_origin(*ptep), PAGE_DEFAULT_KEY, 1);
8248e03e831SClaudio Imbrenda 	pgste_set_unlock(ptep, pgste);
8258e03e831SClaudio Imbrenda 
8268e03e831SClaudio Imbrenda 	if (need_resched())
8278e03e831SClaudio Imbrenda 		return next;
8288e03e831SClaudio Imbrenda 	return 0;
8298e03e831SClaudio Imbrenda }
8308e03e831SClaudio Imbrenda 
dat_reset_skeys_crste(union crste * crstep,gfn_t gfn,gfn_t next,struct dat_walk * walk)8318e03e831SClaudio Imbrenda static long dat_reset_skeys_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
8328e03e831SClaudio Imbrenda {
8338e03e831SClaudio Imbrenda 	phys_addr_t addr, end, origin = crste_origin_large(*crstep);
8348e03e831SClaudio Imbrenda 
8358e03e831SClaudio Imbrenda 	if (!crstep->h.fc || !crstep->s.fc1.pr)
8368e03e831SClaudio Imbrenda 		return 0;
8378e03e831SClaudio Imbrenda 
8388e03e831SClaudio Imbrenda 	addr = ((max(gfn, walk->start) - gfn) << PAGE_SHIFT) + origin;
8398e03e831SClaudio Imbrenda 	end = ((min(next, walk->end) - gfn) << PAGE_SHIFT) + origin;
8408e03e831SClaudio Imbrenda 	while (ALIGN(addr + 1, _SEGMENT_SIZE) <= end)
8418e03e831SClaudio Imbrenda 		addr = sske_frame(addr, PAGE_DEFAULT_KEY);
8428e03e831SClaudio Imbrenda 	for ( ; addr < end; addr += PAGE_SIZE)
8438e03e831SClaudio Imbrenda 		page_set_storage_key(addr, PAGE_DEFAULT_KEY, 1);
8448e03e831SClaudio Imbrenda 
8458e03e831SClaudio Imbrenda 	if (need_resched())
8468e03e831SClaudio Imbrenda 		return next;
8478e03e831SClaudio Imbrenda 	return 0;
8488e03e831SClaudio Imbrenda }
8498e03e831SClaudio Imbrenda 
dat_reset_skeys(union asce asce,gfn_t start)8508e03e831SClaudio Imbrenda long dat_reset_skeys(union asce asce, gfn_t start)
8518e03e831SClaudio Imbrenda {
8528e03e831SClaudio Imbrenda 	const struct dat_walk_ops ops = {
8538e03e831SClaudio Imbrenda 		.pte_entry = dat_reset_skeys_pte,
8548e03e831SClaudio Imbrenda 		.pmd_entry = dat_reset_skeys_crste,
8558e03e831SClaudio Imbrenda 		.pud_entry = dat_reset_skeys_crste,
8568e03e831SClaudio Imbrenda 	};
8578e03e831SClaudio Imbrenda 
8588e03e831SClaudio Imbrenda 	return _dat_walk_gfn_range(start, asce_end(asce), asce, &ops, DAT_WALK_IGN_HOLES, NULL);
8598e03e831SClaudio Imbrenda }
86094fd9b16SClaudio Imbrenda 
86194fd9b16SClaudio Imbrenda struct slot_priv {
86294fd9b16SClaudio Imbrenda 	unsigned long token;
86394fd9b16SClaudio Imbrenda 	struct kvm_s390_mmu_cache *mc;
86494fd9b16SClaudio Imbrenda };
86594fd9b16SClaudio Imbrenda 
_dat_slot_pte(union pte * ptep,gfn_t gfn,gfn_t next,struct dat_walk * walk)86694fd9b16SClaudio Imbrenda static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
86794fd9b16SClaudio Imbrenda {
86894fd9b16SClaudio Imbrenda 	struct slot_priv *p = walk->priv;
86994fd9b16SClaudio Imbrenda 	union crste dummy = { .val = p->token };
87094fd9b16SClaudio Imbrenda 	union pte new_pte, pte = READ_ONCE(*ptep);
87194fd9b16SClaudio Imbrenda 
87294fd9b16SClaudio Imbrenda 	new_pte = _PTE_TOK(dummy.tok.type, dummy.tok.par);
87394fd9b16SClaudio Imbrenda 
87494fd9b16SClaudio Imbrenda 	/* Table entry already in the desired state. */
87594fd9b16SClaudio Imbrenda 	if (pte.val == new_pte.val)
87694fd9b16SClaudio Imbrenda 		return 0;
87794fd9b16SClaudio Imbrenda 
87894fd9b16SClaudio Imbrenda 	dat_ptep_xchg(ptep, new_pte, gfn, walk->asce, false);
87994fd9b16SClaudio Imbrenda 	return 0;
88094fd9b16SClaudio Imbrenda }
88194fd9b16SClaudio Imbrenda 
_dat_slot_crste(union crste * crstep,gfn_t gfn,gfn_t next,struct dat_walk * walk)88294fd9b16SClaudio Imbrenda static long _dat_slot_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
88394fd9b16SClaudio Imbrenda {
88494fd9b16SClaudio Imbrenda 	union crste new_crste, crste = READ_ONCE(*crstep);
88594fd9b16SClaudio Imbrenda 	struct slot_priv *p = walk->priv;
88694fd9b16SClaudio Imbrenda 
88794fd9b16SClaudio Imbrenda 	new_crste.val = p->token;
88894fd9b16SClaudio Imbrenda 	new_crste.h.tt = crste.h.tt;
88994fd9b16SClaudio Imbrenda 
89094fd9b16SClaudio Imbrenda 	/* Table entry already in the desired state. */
89194fd9b16SClaudio Imbrenda 	if (crste.val == new_crste.val)
89294fd9b16SClaudio Imbrenda 		return 0;
89394fd9b16SClaudio Imbrenda 
89494fd9b16SClaudio Imbrenda 	/* This table entry needs to be updated. */
89594fd9b16SClaudio Imbrenda 	if (walk->start <= gfn && walk->end >= next) {
89694fd9b16SClaudio Imbrenda 		dat_crstep_xchg_atomic(crstep, crste, new_crste, gfn, walk->asce);
89794fd9b16SClaudio Imbrenda 		/* A lower level table was present, needs to be freed. */
89894fd9b16SClaudio Imbrenda 		if (!crste.h.fc && !crste.h.i) {
89994fd9b16SClaudio Imbrenda 			if (is_pmd(crste))
90094fd9b16SClaudio Imbrenda 				dat_free_pt(dereference_pmd(crste.pmd));
90194fd9b16SClaudio Imbrenda 			else
90294fd9b16SClaudio Imbrenda 				dat_free_level(dereference_crste(crste), true);
90394fd9b16SClaudio Imbrenda 		}
90494fd9b16SClaudio Imbrenda 		return 0;
90594fd9b16SClaudio Imbrenda 	}
90694fd9b16SClaudio Imbrenda 
90794fd9b16SClaudio Imbrenda 	/* A lower level table is present, things will handled there. */
90894fd9b16SClaudio Imbrenda 	if (!crste.h.fc && !crste.h.i)
90994fd9b16SClaudio Imbrenda 		return 0;
91094fd9b16SClaudio Imbrenda 	/* Split (install a lower level table), and handle things there. */
91194fd9b16SClaudio Imbrenda 	return dat_split_crste(p->mc, crstep, gfn, walk->asce, false);
91294fd9b16SClaudio Imbrenda }
91394fd9b16SClaudio Imbrenda 
91494fd9b16SClaudio Imbrenda static const struct dat_walk_ops dat_slot_ops = {
91594fd9b16SClaudio Imbrenda 	.pte_entry = _dat_slot_pte,
91694fd9b16SClaudio Imbrenda 	.crste_ops = { _dat_slot_crste, _dat_slot_crste, _dat_slot_crste, _dat_slot_crste, },
91794fd9b16SClaudio Imbrenda };
91894fd9b16SClaudio Imbrenda 
dat_set_slot(struct kvm_s390_mmu_cache * mc,union asce asce,gfn_t start,gfn_t end,u16 type,u16 param)91994fd9b16SClaudio Imbrenda int dat_set_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start, gfn_t end,
92094fd9b16SClaudio Imbrenda 		 u16 type, u16 param)
92194fd9b16SClaudio Imbrenda {
92294fd9b16SClaudio Imbrenda 	struct slot_priv priv = {
92394fd9b16SClaudio Imbrenda 		.token = _CRSTE_TOK(0, type, param).val,
92494fd9b16SClaudio Imbrenda 		.mc = mc,
92594fd9b16SClaudio Imbrenda 	};
92694fd9b16SClaudio Imbrenda 
92794fd9b16SClaudio Imbrenda 	return _dat_walk_gfn_range(start, end, asce, &dat_slot_ops,
92894fd9b16SClaudio Imbrenda 				   DAT_WALK_IGN_HOLES | DAT_WALK_ANY, &priv);
92994fd9b16SClaudio Imbrenda }
93094fd9b16SClaudio Imbrenda 
pgste_set_unlock_multiple(union pte * first,int n,union pgste * pgstes)93194fd9b16SClaudio Imbrenda static void pgste_set_unlock_multiple(union pte *first, int n, union pgste *pgstes)
93294fd9b16SClaudio Imbrenda {
93394fd9b16SClaudio Imbrenda 	int i;
93494fd9b16SClaudio Imbrenda 
93594fd9b16SClaudio Imbrenda 	for (i = 0; i < n; i++) {
93694fd9b16SClaudio Imbrenda 		if (!pgstes[i].pcl)
93794fd9b16SClaudio Imbrenda 			break;
93894fd9b16SClaudio Imbrenda 		pgste_set_unlock(first + i, pgstes[i]);
93994fd9b16SClaudio Imbrenda 	}
94094fd9b16SClaudio Imbrenda }
94194fd9b16SClaudio Imbrenda 
pgste_get_trylock_multiple(union pte * first,int n,union pgste * pgstes)94294fd9b16SClaudio Imbrenda static bool pgste_get_trylock_multiple(union pte *first, int n, union pgste *pgstes)
94394fd9b16SClaudio Imbrenda {
94494fd9b16SClaudio Imbrenda 	int i;
94594fd9b16SClaudio Imbrenda 
94694fd9b16SClaudio Imbrenda 	for (i = 0; i < n; i++) {
94794fd9b16SClaudio Imbrenda 		if (!pgste_get_trylock(first + i, pgstes + i))
94894fd9b16SClaudio Imbrenda 			break;
94994fd9b16SClaudio Imbrenda 	}
95094fd9b16SClaudio Imbrenda 	if (i == n)
95194fd9b16SClaudio Imbrenda 		return true;
95294fd9b16SClaudio Imbrenda 	pgste_set_unlock_multiple(first, n, pgstes);
95394fd9b16SClaudio Imbrenda 	return false;
95494fd9b16SClaudio Imbrenda }
95594fd9b16SClaudio Imbrenda 
dat_get_ptval(struct page_table * table,struct ptval_param param)95694fd9b16SClaudio Imbrenda unsigned long dat_get_ptval(struct page_table *table, struct ptval_param param)
95794fd9b16SClaudio Imbrenda {
95894fd9b16SClaudio Imbrenda 	union pgste pgstes[4] = {};
95994fd9b16SClaudio Imbrenda 	unsigned long res = 0;
96094fd9b16SClaudio Imbrenda 	int i, n;
96194fd9b16SClaudio Imbrenda 
96294fd9b16SClaudio Imbrenda 	n = param.len + 1;
96394fd9b16SClaudio Imbrenda 
96494fd9b16SClaudio Imbrenda 	while (!pgste_get_trylock_multiple(table->ptes + param.offset, n, pgstes))
96594fd9b16SClaudio Imbrenda 		cpu_relax();
96694fd9b16SClaudio Imbrenda 
96794fd9b16SClaudio Imbrenda 	for (i = 0; i < n; i++)
96894fd9b16SClaudio Imbrenda 		res = res << 16 | pgstes[i].val16;
96994fd9b16SClaudio Imbrenda 
97094fd9b16SClaudio Imbrenda 	pgste_set_unlock_multiple(table->ptes + param.offset, n, pgstes);
97194fd9b16SClaudio Imbrenda 	return res;
97294fd9b16SClaudio Imbrenda }
97394fd9b16SClaudio Imbrenda 
dat_set_ptval(struct page_table * table,struct ptval_param param,unsigned long val)97494fd9b16SClaudio Imbrenda void dat_set_ptval(struct page_table *table, struct ptval_param param, unsigned long val)
97594fd9b16SClaudio Imbrenda {
97694fd9b16SClaudio Imbrenda 	union pgste pgstes[4] = {};
97794fd9b16SClaudio Imbrenda 	int i, n;
97894fd9b16SClaudio Imbrenda 
97994fd9b16SClaudio Imbrenda 	n = param.len + 1;
98094fd9b16SClaudio Imbrenda 
98194fd9b16SClaudio Imbrenda 	while (!pgste_get_trylock_multiple(table->ptes + param.offset, n, pgstes))
98294fd9b16SClaudio Imbrenda 		cpu_relax();
98394fd9b16SClaudio Imbrenda 
98494fd9b16SClaudio Imbrenda 	for (i = param.len; i >= 0; i--) {
98594fd9b16SClaudio Imbrenda 		pgstes[i].val16 = val;
98694fd9b16SClaudio Imbrenda 		val = val >> 16;
98794fd9b16SClaudio Imbrenda 	}
98894fd9b16SClaudio Imbrenda 
98994fd9b16SClaudio Imbrenda 	pgste_set_unlock_multiple(table->ptes + param.offset, n, pgstes);
99094fd9b16SClaudio Imbrenda }
99194fd9b16SClaudio Imbrenda 
_dat_test_young_pte(union pte * ptep,gfn_t start,gfn_t end,struct dat_walk * walk)99294fd9b16SClaudio Imbrenda static long _dat_test_young_pte(union pte *ptep, gfn_t start, gfn_t end, struct dat_walk *walk)
99394fd9b16SClaudio Imbrenda {
99494fd9b16SClaudio Imbrenda 	return ptep->s.y;
99594fd9b16SClaudio Imbrenda }
99694fd9b16SClaudio Imbrenda 
_dat_test_young_crste(union crste * crstep,gfn_t start,gfn_t end,struct dat_walk * walk)99794fd9b16SClaudio Imbrenda static long _dat_test_young_crste(union crste *crstep, gfn_t start, gfn_t end,
99894fd9b16SClaudio Imbrenda 				  struct dat_walk *walk)
99994fd9b16SClaudio Imbrenda {
100094fd9b16SClaudio Imbrenda 	return crstep->h.fc && crstep->s.fc1.y;
100194fd9b16SClaudio Imbrenda }
100294fd9b16SClaudio Imbrenda 
100394fd9b16SClaudio Imbrenda static const struct dat_walk_ops test_age_ops = {
100494fd9b16SClaudio Imbrenda 	.pte_entry = _dat_test_young_pte,
100594fd9b16SClaudio Imbrenda 	.pmd_entry = _dat_test_young_crste,
100694fd9b16SClaudio Imbrenda 	.pud_entry = _dat_test_young_crste,
100794fd9b16SClaudio Imbrenda };
100894fd9b16SClaudio Imbrenda 
100994fd9b16SClaudio Imbrenda /**
101094fd9b16SClaudio Imbrenda  * dat_test_age_gfn() - Test young.
101194fd9b16SClaudio Imbrenda  * @asce: The ASCE whose address range is to be tested.
101294fd9b16SClaudio Imbrenda  * @start: The first guest frame of the range to check.
101394fd9b16SClaudio Imbrenda  * @end: The guest frame after the last in the range.
101494fd9b16SClaudio Imbrenda  *
101594fd9b16SClaudio Imbrenda  * Context: called by KVM common code with the kvm mmu write lock held.
101694fd9b16SClaudio Imbrenda  *
101794fd9b16SClaudio Imbrenda  * Return: %true if any page in the given range is young, otherwise %false.
101894fd9b16SClaudio Imbrenda  */
dat_test_age_gfn(union asce asce,gfn_t start,gfn_t end)101994fd9b16SClaudio Imbrenda bool dat_test_age_gfn(union asce asce, gfn_t start, gfn_t end)
102094fd9b16SClaudio Imbrenda {
102194fd9b16SClaudio Imbrenda 	return _dat_walk_gfn_range(start, end, asce, &test_age_ops, 0, NULL) > 0;
102294fd9b16SClaudio Imbrenda }
102394fd9b16SClaudio Imbrenda 
dat_link(struct kvm_s390_mmu_cache * mc,union asce asce,int level,bool uses_skeys,struct guest_fault * f)102494fd9b16SClaudio Imbrenda int dat_link(struct kvm_s390_mmu_cache *mc, union asce asce, int level,
102594fd9b16SClaudio Imbrenda 	     bool uses_skeys, struct guest_fault *f)
102694fd9b16SClaudio Imbrenda {
102794fd9b16SClaudio Imbrenda 	union crste oldval, newval;
102894fd9b16SClaudio Imbrenda 	union pte newpte, oldpte;
102994fd9b16SClaudio Imbrenda 	union pgste pgste;
103094fd9b16SClaudio Imbrenda 	int rc = 0;
103194fd9b16SClaudio Imbrenda 
103294fd9b16SClaudio Imbrenda 	rc = dat_entry_walk(mc, f->gfn, asce, DAT_WALK_ALLOC_CONTINUE, level, &f->crstep, &f->ptep);
103394fd9b16SClaudio Imbrenda 	if (rc == -EINVAL || rc == -ENOMEM)
103494fd9b16SClaudio Imbrenda 		return rc;
103594fd9b16SClaudio Imbrenda 	if (rc)
103694fd9b16SClaudio Imbrenda 		return -EAGAIN;
103794fd9b16SClaudio Imbrenda 
103894fd9b16SClaudio Imbrenda 	if (WARN_ON_ONCE(unlikely(get_level(f->crstep, f->ptep) > level)))
103994fd9b16SClaudio Imbrenda 		return -EINVAL;
104094fd9b16SClaudio Imbrenda 
104194fd9b16SClaudio Imbrenda 	if (f->ptep) {
104294fd9b16SClaudio Imbrenda 		pgste = pgste_get_lock(f->ptep);
104394fd9b16SClaudio Imbrenda 		oldpte = *f->ptep;
104494fd9b16SClaudio Imbrenda 		newpte = _pte(f->pfn, f->writable, f->write_attempt | oldpte.s.d, !f->page);
104594fd9b16SClaudio Imbrenda 		newpte.s.sd = oldpte.s.sd;
104694fd9b16SClaudio Imbrenda 		oldpte.s.sd = 0;
104794fd9b16SClaudio Imbrenda 		if (oldpte.val == _PTE_EMPTY.val || oldpte.h.pfra == f->pfn) {
104894fd9b16SClaudio Imbrenda 			pgste = __dat_ptep_xchg(f->ptep, pgste, newpte, f->gfn, asce, uses_skeys);
104994fd9b16SClaudio Imbrenda 			if (f->callback)
105094fd9b16SClaudio Imbrenda 				f->callback(f);
105194fd9b16SClaudio Imbrenda 		} else {
105294fd9b16SClaudio Imbrenda 			rc = -EAGAIN;
105394fd9b16SClaudio Imbrenda 		}
105494fd9b16SClaudio Imbrenda 		pgste_set_unlock(f->ptep, pgste);
105594fd9b16SClaudio Imbrenda 	} else {
105694fd9b16SClaudio Imbrenda 		oldval = READ_ONCE(*f->crstep);
105794fd9b16SClaudio Imbrenda 		newval = _crste_fc1(f->pfn, oldval.h.tt, f->writable,
105894fd9b16SClaudio Imbrenda 				    f->write_attempt | oldval.s.fc1.d);
105994fd9b16SClaudio Imbrenda 		newval.s.fc1.sd = oldval.s.fc1.sd;
106094fd9b16SClaudio Imbrenda 		if (oldval.val != _CRSTE_EMPTY(oldval.h.tt).val &&
106194fd9b16SClaudio Imbrenda 		    crste_origin_large(oldval) != crste_origin_large(newval))
106294fd9b16SClaudio Imbrenda 			return -EAGAIN;
106394fd9b16SClaudio Imbrenda 		if (!dat_crstep_xchg_atomic(f->crstep, oldval, newval, f->gfn, asce))
106494fd9b16SClaudio Imbrenda 			return -EAGAIN;
106594fd9b16SClaudio Imbrenda 		if (f->callback)
106694fd9b16SClaudio Imbrenda 			f->callback(f);
106794fd9b16SClaudio Imbrenda 	}
106894fd9b16SClaudio Imbrenda 
106994fd9b16SClaudio Imbrenda 	return rc;
107094fd9b16SClaudio Imbrenda }
107194fd9b16SClaudio Imbrenda 
dat_set_pn_crste(union crste * crstep,gfn_t gfn,gfn_t next,struct dat_walk * walk)107294fd9b16SClaudio Imbrenda static long dat_set_pn_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
107394fd9b16SClaudio Imbrenda {
107494fd9b16SClaudio Imbrenda 	union crste crste = READ_ONCE(*crstep);
107594fd9b16SClaudio Imbrenda 	int *n = walk->priv;
107694fd9b16SClaudio Imbrenda 
107794fd9b16SClaudio Imbrenda 	if (!crste.h.fc || crste.h.i || crste.h.p)
107894fd9b16SClaudio Imbrenda 		return 0;
107994fd9b16SClaudio Imbrenda 
108094fd9b16SClaudio Imbrenda 	*n = 2;
108194fd9b16SClaudio Imbrenda 	if (crste.s.fc1.prefix_notif)
108294fd9b16SClaudio Imbrenda 		return 0;
108394fd9b16SClaudio Imbrenda 	crste.s.fc1.prefix_notif = 1;
108494fd9b16SClaudio Imbrenda 	dat_crstep_xchg(crstep, crste, gfn, walk->asce);
108594fd9b16SClaudio Imbrenda 	return 0;
108694fd9b16SClaudio Imbrenda }
108794fd9b16SClaudio Imbrenda 
dat_set_pn_pte(union pte * ptep,gfn_t gfn,gfn_t next,struct dat_walk * walk)108894fd9b16SClaudio Imbrenda static long dat_set_pn_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
108994fd9b16SClaudio Imbrenda {
109094fd9b16SClaudio Imbrenda 	int *n = walk->priv;
109194fd9b16SClaudio Imbrenda 	union pgste pgste;
109294fd9b16SClaudio Imbrenda 
109394fd9b16SClaudio Imbrenda 	pgste = pgste_get_lock(ptep);
109494fd9b16SClaudio Imbrenda 	if (!ptep->h.i && !ptep->h.p) {
109594fd9b16SClaudio Imbrenda 		pgste.prefix_notif = 1;
109694fd9b16SClaudio Imbrenda 		*n += 1;
109794fd9b16SClaudio Imbrenda 	}
109894fd9b16SClaudio Imbrenda 	pgste_set_unlock(ptep, pgste);
109994fd9b16SClaudio Imbrenda 	return 0;
110094fd9b16SClaudio Imbrenda }
110194fd9b16SClaudio Imbrenda 
dat_set_prefix_notif_bit(union asce asce,gfn_t gfn)110294fd9b16SClaudio Imbrenda int dat_set_prefix_notif_bit(union asce asce, gfn_t gfn)
110394fd9b16SClaudio Imbrenda {
110494fd9b16SClaudio Imbrenda 	static const struct dat_walk_ops ops = {
110594fd9b16SClaudio Imbrenda 		.pte_entry = dat_set_pn_pte,
110694fd9b16SClaudio Imbrenda 		.pmd_entry = dat_set_pn_crste,
110794fd9b16SClaudio Imbrenda 		.pud_entry = dat_set_pn_crste,
110894fd9b16SClaudio Imbrenda 	};
110994fd9b16SClaudio Imbrenda 
111094fd9b16SClaudio Imbrenda 	int n = 0;
111194fd9b16SClaudio Imbrenda 
111294fd9b16SClaudio Imbrenda 	_dat_walk_gfn_range(gfn, gfn + 2, asce, &ops, DAT_WALK_IGN_HOLES, &n);
111394fd9b16SClaudio Imbrenda 	if (n != 2)
111494fd9b16SClaudio Imbrenda 		return -EAGAIN;
111594fd9b16SClaudio Imbrenda 	return 0;
111694fd9b16SClaudio Imbrenda }
11177b368470SClaudio Imbrenda 
11187b368470SClaudio Imbrenda /**
11197b368470SClaudio Imbrenda  * dat_perform_essa() - Perform ESSA actions on the PGSTE.
11207b368470SClaudio Imbrenda  * @asce: The asce to operate on.
11217b368470SClaudio Imbrenda  * @gfn: The guest page frame to operate on.
11227b368470SClaudio Imbrenda  * @orc: The specific action to perform, see the ESSA_SET_* macros.
11237b368470SClaudio Imbrenda  * @state: The storage attributes to be returned to the guest.
11247b368470SClaudio Imbrenda  * @dirty: Returns whether the function dirtied a previously clean entry.
11257b368470SClaudio Imbrenda  *
11267b368470SClaudio Imbrenda  * Context: Called with kvm->mmu_lock held.
11277b368470SClaudio Imbrenda  *
11287b368470SClaudio Imbrenda  * Return:
11297b368470SClaudio Imbrenda  * * %1 if the page state has been altered and the page is to be added to the CBRL
11307b368470SClaudio Imbrenda  * * %0 if the page state has been altered, but the page is not to be added to the CBRL
11317b368470SClaudio Imbrenda  * * %-1 if the page state has not been altered and the page is not to be added to the CBRL
11327b368470SClaudio Imbrenda  */
dat_perform_essa(union asce asce,gfn_t gfn,int orc,union essa_state * state,bool * dirty)11337b368470SClaudio Imbrenda int dat_perform_essa(union asce asce, gfn_t gfn, int orc, union essa_state *state, bool *dirty)
11347b368470SClaudio Imbrenda {
11357b368470SClaudio Imbrenda 	union crste *crstep;
11367b368470SClaudio Imbrenda 	union pgste pgste;
11377b368470SClaudio Imbrenda 	union pte *ptep;
11387b368470SClaudio Imbrenda 	int res = 0;
11397b368470SClaudio Imbrenda 
11407b368470SClaudio Imbrenda 	if (dat_entry_walk(NULL, gfn, asce, 0, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep)) {
11417b368470SClaudio Imbrenda 		*state = (union essa_state) { .exception = 1 };
11427b368470SClaudio Imbrenda 		return -1;
11437b368470SClaudio Imbrenda 	}
11447b368470SClaudio Imbrenda 
11457b368470SClaudio Imbrenda 	pgste = pgste_get_lock(ptep);
11467b368470SClaudio Imbrenda 
11477b368470SClaudio Imbrenda 	*state = (union essa_state) {
11487b368470SClaudio Imbrenda 		.content = (ptep->h.i << 1) + (ptep->h.i && pgste.zero),
11497b368470SClaudio Imbrenda 		.nodat = pgste.nodat,
11507b368470SClaudio Imbrenda 		.usage = pgste.usage,
11517b368470SClaudio Imbrenda 		};
11527b368470SClaudio Imbrenda 
11537b368470SClaudio Imbrenda 	switch (orc) {
11547b368470SClaudio Imbrenda 	case ESSA_GET_STATE:
11557b368470SClaudio Imbrenda 		res = -1;
11567b368470SClaudio Imbrenda 		break;
11577b368470SClaudio Imbrenda 	case ESSA_SET_STABLE:
11587b368470SClaudio Imbrenda 		pgste.usage = PGSTE_GPS_USAGE_STABLE;
11597b368470SClaudio Imbrenda 		pgste.nodat = 0;
11607b368470SClaudio Imbrenda 		break;
11617b368470SClaudio Imbrenda 	case ESSA_SET_UNUSED:
11627b368470SClaudio Imbrenda 		pgste.usage = PGSTE_GPS_USAGE_UNUSED;
11637b368470SClaudio Imbrenda 		if (ptep->h.i)
11647b368470SClaudio Imbrenda 			res = 1;
11657b368470SClaudio Imbrenda 		break;
11667b368470SClaudio Imbrenda 	case ESSA_SET_VOLATILE:
11677b368470SClaudio Imbrenda 		pgste.usage = PGSTE_GPS_USAGE_VOLATILE;
11687b368470SClaudio Imbrenda 		if (ptep->h.i)
11697b368470SClaudio Imbrenda 			res = 1;
11707b368470SClaudio Imbrenda 		break;
11717b368470SClaudio Imbrenda 	case ESSA_SET_POT_VOLATILE:
11727b368470SClaudio Imbrenda 		if (!ptep->h.i) {
11737b368470SClaudio Imbrenda 			pgste.usage = PGSTE_GPS_USAGE_POT_VOLATILE;
11747b368470SClaudio Imbrenda 		} else if (pgste.zero) {
11757b368470SClaudio Imbrenda 			pgste.usage = PGSTE_GPS_USAGE_VOLATILE;
11767b368470SClaudio Imbrenda 		} else if (!pgste.gc) {
11777b368470SClaudio Imbrenda 			pgste.usage = PGSTE_GPS_USAGE_VOLATILE;
11787b368470SClaudio Imbrenda 			res = 1;
11797b368470SClaudio Imbrenda 		}
11807b368470SClaudio Imbrenda 		break;
11817b368470SClaudio Imbrenda 	case ESSA_SET_STABLE_RESIDENT:
11827b368470SClaudio Imbrenda 		pgste.usage = PGSTE_GPS_USAGE_STABLE;
11837b368470SClaudio Imbrenda 		/*
11847b368470SClaudio Imbrenda 		 * Since the resident state can go away any time after this
11857b368470SClaudio Imbrenda 		 * call, we will not make this page resident. We can revisit
11867b368470SClaudio Imbrenda 		 * this decision if a guest will ever start using this.
11877b368470SClaudio Imbrenda 		 */
11887b368470SClaudio Imbrenda 		break;
11897b368470SClaudio Imbrenda 	case ESSA_SET_STABLE_IF_RESIDENT:
11907b368470SClaudio Imbrenda 		if (!ptep->h.i)
11917b368470SClaudio Imbrenda 			pgste.usage = PGSTE_GPS_USAGE_STABLE;
11927b368470SClaudio Imbrenda 		break;
11937b368470SClaudio Imbrenda 	case ESSA_SET_STABLE_NODAT:
11947b368470SClaudio Imbrenda 		pgste.usage = PGSTE_GPS_USAGE_STABLE;
11957b368470SClaudio Imbrenda 		pgste.nodat = 1;
11967b368470SClaudio Imbrenda 		break;
11977b368470SClaudio Imbrenda 	default:
11987b368470SClaudio Imbrenda 		WARN_ONCE(1, "Invalid ORC!");
11997b368470SClaudio Imbrenda 		res = -1;
12007b368470SClaudio Imbrenda 		break;
12017b368470SClaudio Imbrenda 	}
12027b368470SClaudio Imbrenda 	/* If we are discarding a page, set it to logical zero. */
12037b368470SClaudio Imbrenda 	pgste.zero = res == 1;
12047b368470SClaudio Imbrenda 	if (orc > 0) {
12057b368470SClaudio Imbrenda 		*dirty = !pgste.cmma_d;
12067b368470SClaudio Imbrenda 		pgste.cmma_d = 1;
12077b368470SClaudio Imbrenda 	}
12087b368470SClaudio Imbrenda 
12097b368470SClaudio Imbrenda 	pgste_set_unlock(ptep, pgste);
12107b368470SClaudio Imbrenda 
12117b368470SClaudio Imbrenda 	return res;
12127b368470SClaudio Imbrenda }
12137b368470SClaudio Imbrenda 
dat_reset_cmma_pte(union pte * ptep,gfn_t gfn,gfn_t next,struct dat_walk * walk)12147b368470SClaudio Imbrenda static long dat_reset_cmma_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
12157b368470SClaudio Imbrenda {
12167b368470SClaudio Imbrenda 	union pgste pgste;
12177b368470SClaudio Imbrenda 
12187b368470SClaudio Imbrenda 	pgste = pgste_get_lock(ptep);
12197b368470SClaudio Imbrenda 	pgste.usage = 0;
12207b368470SClaudio Imbrenda 	pgste.nodat = 0;
12217b368470SClaudio Imbrenda 	pgste.cmma_d = 0;
12227b368470SClaudio Imbrenda 	pgste_set_unlock(ptep, pgste);
12237b368470SClaudio Imbrenda 	if (need_resched())
12247b368470SClaudio Imbrenda 		return next;
12257b368470SClaudio Imbrenda 	return 0;
12267b368470SClaudio Imbrenda }
12277b368470SClaudio Imbrenda 
dat_reset_cmma(union asce asce,gfn_t start)12287b368470SClaudio Imbrenda long dat_reset_cmma(union asce asce, gfn_t start)
12297b368470SClaudio Imbrenda {
12307b368470SClaudio Imbrenda 	const struct dat_walk_ops dat_reset_cmma_ops = {
12317b368470SClaudio Imbrenda 		.pte_entry = dat_reset_cmma_pte,
12327b368470SClaudio Imbrenda 	};
12337b368470SClaudio Imbrenda 
12347b368470SClaudio Imbrenda 	return _dat_walk_gfn_range(start, asce_end(asce), asce, &dat_reset_cmma_ops,
12357b368470SClaudio Imbrenda 				   DAT_WALK_IGN_HOLES, NULL);
12367b368470SClaudio Imbrenda }
12377b368470SClaudio Imbrenda 
12387b368470SClaudio Imbrenda struct dat_get_cmma_state {
12397b368470SClaudio Imbrenda 	gfn_t start;
12407b368470SClaudio Imbrenda 	gfn_t end;
12417b368470SClaudio Imbrenda 	unsigned int count;
12427b368470SClaudio Imbrenda 	u8 *values;
12437b368470SClaudio Imbrenda 	atomic64_t *remaining;
12447b368470SClaudio Imbrenda };
12457b368470SClaudio Imbrenda 
__dat_peek_cmma_pte(union pte * ptep,gfn_t gfn,gfn_t next,struct dat_walk * walk)12467b368470SClaudio Imbrenda static long __dat_peek_cmma_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
12477b368470SClaudio Imbrenda {
12487b368470SClaudio Imbrenda 	struct dat_get_cmma_state *state = walk->priv;
12497b368470SClaudio Imbrenda 	union pgste pgste;
12507b368470SClaudio Imbrenda 
12517b368470SClaudio Imbrenda 	pgste = pgste_get_lock(ptep);
12527b368470SClaudio Imbrenda 	state->values[gfn - walk->start] = pgste.usage | (pgste.nodat << 6);
12537b368470SClaudio Imbrenda 	pgste_set_unlock(ptep, pgste);
12547b368470SClaudio Imbrenda 	state->end = next;
12557b368470SClaudio Imbrenda 
12567b368470SClaudio Imbrenda 	return 0;
12577b368470SClaudio Imbrenda }
12587b368470SClaudio Imbrenda 
__dat_peek_cmma_crste(union crste * crstep,gfn_t gfn,gfn_t next,struct dat_walk * walk)12597b368470SClaudio Imbrenda static long __dat_peek_cmma_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
12607b368470SClaudio Imbrenda {
12617b368470SClaudio Imbrenda 	struct dat_get_cmma_state *state = walk->priv;
12627b368470SClaudio Imbrenda 
12637b368470SClaudio Imbrenda 	if (crstep->h.i)
12647b368470SClaudio Imbrenda 		state->end = min(walk->end, next);
12657b368470SClaudio Imbrenda 	return 0;
12667b368470SClaudio Imbrenda }
12677b368470SClaudio Imbrenda 
dat_peek_cmma(gfn_t start,union asce asce,unsigned int * count,u8 * values)12687b368470SClaudio Imbrenda int dat_peek_cmma(gfn_t start, union asce asce, unsigned int *count, u8 *values)
12697b368470SClaudio Imbrenda {
12707b368470SClaudio Imbrenda 	const struct dat_walk_ops ops = {
12717b368470SClaudio Imbrenda 		.pte_entry = __dat_peek_cmma_pte,
12727b368470SClaudio Imbrenda 		.pmd_entry = __dat_peek_cmma_crste,
12737b368470SClaudio Imbrenda 		.pud_entry = __dat_peek_cmma_crste,
12747b368470SClaudio Imbrenda 		.p4d_entry = __dat_peek_cmma_crste,
12757b368470SClaudio Imbrenda 		.pgd_entry = __dat_peek_cmma_crste,
12767b368470SClaudio Imbrenda 	};
12777b368470SClaudio Imbrenda 	struct dat_get_cmma_state state = { .values = values, };
12787b368470SClaudio Imbrenda 	int rc;
12797b368470SClaudio Imbrenda 
12807b368470SClaudio Imbrenda 	rc = _dat_walk_gfn_range(start, start + *count, asce, &ops, DAT_WALK_DEFAULT, &state);
12817b368470SClaudio Imbrenda 	*count = state.end - start;
12827b368470SClaudio Imbrenda 	/* Return success if at least one value was saved, otherwise an error. */
12837b368470SClaudio Imbrenda 	return (rc == -EFAULT && *count > 0) ? 0 : rc;
12847b368470SClaudio Imbrenda }
12857b368470SClaudio Imbrenda 
__dat_get_cmma_pte(union pte * ptep,gfn_t gfn,gfn_t next,struct dat_walk * walk)12867b368470SClaudio Imbrenda static long __dat_get_cmma_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
12877b368470SClaudio Imbrenda {
12887b368470SClaudio Imbrenda 	struct dat_get_cmma_state *state = walk->priv;
12897b368470SClaudio Imbrenda 	union pgste pgste;
12907b368470SClaudio Imbrenda 
12917b368470SClaudio Imbrenda 	if (state->start != -1) {
12927b368470SClaudio Imbrenda 		if ((gfn - state->end) > KVM_S390_MAX_BIT_DISTANCE)
12937b368470SClaudio Imbrenda 			return 1;
12947b368470SClaudio Imbrenda 		if (gfn - state->start >= state->count)
12957b368470SClaudio Imbrenda 			return 1;
12967b368470SClaudio Imbrenda 	}
12977b368470SClaudio Imbrenda 
12987b368470SClaudio Imbrenda 	if (!READ_ONCE(*pgste_of(ptep)).cmma_d)
12997b368470SClaudio Imbrenda 		return 0;
13007b368470SClaudio Imbrenda 
13017b368470SClaudio Imbrenda 	pgste = pgste_get_lock(ptep);
13027b368470SClaudio Imbrenda 	if (pgste.cmma_d) {
13037b368470SClaudio Imbrenda 		if (state->start == -1)
13047b368470SClaudio Imbrenda 			state->start = gfn;
13057b368470SClaudio Imbrenda 		pgste.cmma_d = 0;
13067b368470SClaudio Imbrenda 		atomic64_dec(state->remaining);
13077b368470SClaudio Imbrenda 		state->values[gfn - state->start] = pgste.usage | pgste.nodat << 6;
13087b368470SClaudio Imbrenda 		state->end = next;
13097b368470SClaudio Imbrenda 	}
13107b368470SClaudio Imbrenda 	pgste_set_unlock(ptep, pgste);
13117b368470SClaudio Imbrenda 	return 0;
13127b368470SClaudio Imbrenda }
13137b368470SClaudio Imbrenda 
dat_get_cmma(union asce asce,gfn_t * start,unsigned int * count,u8 * values,atomic64_t * rem)13147b368470SClaudio Imbrenda int dat_get_cmma(union asce asce, gfn_t *start, unsigned int *count, u8 *values, atomic64_t *rem)
13157b368470SClaudio Imbrenda {
13167b368470SClaudio Imbrenda 	const struct dat_walk_ops ops = { .pte_entry = __dat_get_cmma_pte, };
13177b368470SClaudio Imbrenda 	struct dat_get_cmma_state state = {
13187b368470SClaudio Imbrenda 		.remaining = rem,
13197b368470SClaudio Imbrenda 		.values = values,
13207b368470SClaudio Imbrenda 		.count = *count,
13217b368470SClaudio Imbrenda 		.start = -1,
13227b368470SClaudio Imbrenda 	};
13237b368470SClaudio Imbrenda 
13247b368470SClaudio Imbrenda 	_dat_walk_gfn_range(*start, asce_end(asce), asce, &ops, DAT_WALK_IGN_HOLES, &state);
13257b368470SClaudio Imbrenda 
13267b368470SClaudio Imbrenda 	if (state.start == -1) {
13277b368470SClaudio Imbrenda 		*count = 0;
13287b368470SClaudio Imbrenda 	} else {
13297b368470SClaudio Imbrenda 		*count = state.end - state.start;
13307b368470SClaudio Imbrenda 		*start = state.start;
13317b368470SClaudio Imbrenda 	}
13327b368470SClaudio Imbrenda 
13337b368470SClaudio Imbrenda 	return 0;
13347b368470SClaudio Imbrenda }
13357b368470SClaudio Imbrenda 
13367b368470SClaudio Imbrenda struct dat_set_cmma_state {
13377b368470SClaudio Imbrenda 	unsigned long mask;
13387b368470SClaudio Imbrenda 	const u8 *bits;
13397b368470SClaudio Imbrenda };
13407b368470SClaudio Imbrenda 
__dat_set_cmma_pte(union pte * ptep,gfn_t gfn,gfn_t next,struct dat_walk * walk)13417b368470SClaudio Imbrenda static long __dat_set_cmma_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
13427b368470SClaudio Imbrenda {
13437b368470SClaudio Imbrenda 	struct dat_set_cmma_state *state = walk->priv;
13447b368470SClaudio Imbrenda 	union pgste pgste, tmp;
13457b368470SClaudio Imbrenda 
13467b368470SClaudio Imbrenda 	tmp.val = (state->bits[gfn - walk->start] << 24) & state->mask;
13477b368470SClaudio Imbrenda 
13487b368470SClaudio Imbrenda 	pgste = pgste_get_lock(ptep);
13497b368470SClaudio Imbrenda 	pgste.usage = tmp.usage;
13507b368470SClaudio Imbrenda 	pgste.nodat = tmp.nodat;
13517b368470SClaudio Imbrenda 	pgste_set_unlock(ptep, pgste);
13527b368470SClaudio Imbrenda 
13537b368470SClaudio Imbrenda 	return 0;
13547b368470SClaudio Imbrenda }
13557b368470SClaudio Imbrenda 
13567b368470SClaudio Imbrenda /**
13577b368470SClaudio Imbrenda  * dat_set_cmma_bits() - Set CMMA bits for a range of guest pages.
13587b368470SClaudio Imbrenda  * @mc: Cache used for allocations.
13597b368470SClaudio Imbrenda  * @asce: The ASCE of the guest.
13607b368470SClaudio Imbrenda  * @gfn: The guest frame of the fist page whose CMMA bits are to set.
13617b368470SClaudio Imbrenda  * @count: How many pages need to be processed.
13627b368470SClaudio Imbrenda  * @mask: Which PGSTE bits should be set.
13637b368470SClaudio Imbrenda  * @bits: Points to an array with the CMMA attributes.
13647b368470SClaudio Imbrenda  *
13657b368470SClaudio Imbrenda  * This function sets the CMMA attributes for the given pages. If the input
13667b368470SClaudio Imbrenda  * buffer has zero length, no action is taken, otherwise the attributes are
13677b368470SClaudio Imbrenda  * set and the mm->context.uses_cmm flag is set.
13687b368470SClaudio Imbrenda  *
13697b368470SClaudio Imbrenda  * Each byte in @bits contains new values for bits 32-39 of the PGSTE.
13707b368470SClaudio Imbrenda  * Currently, only the fields NT and US are applied.
13717b368470SClaudio Imbrenda  *
13727b368470SClaudio Imbrenda  * Return: %0 in case of success, a negative error value otherwise.
13737b368470SClaudio Imbrenda  */
dat_set_cmma_bits(struct kvm_s390_mmu_cache * mc,union asce asce,gfn_t gfn,unsigned long count,unsigned long mask,const uint8_t * bits)13747b368470SClaudio Imbrenda int dat_set_cmma_bits(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gfn,
13757b368470SClaudio Imbrenda 		      unsigned long count, unsigned long mask, const uint8_t *bits)
13767b368470SClaudio Imbrenda {
13777b368470SClaudio Imbrenda 	const struct dat_walk_ops ops = { .pte_entry = __dat_set_cmma_pte, };
13787b368470SClaudio Imbrenda 	struct dat_set_cmma_state state = { .mask = mask, .bits = bits, };
13797b368470SClaudio Imbrenda 	union crste *crstep;
13807b368470SClaudio Imbrenda 	union pte *ptep;
13817b368470SClaudio Imbrenda 	gfn_t cur;
13827b368470SClaudio Imbrenda 	int rc;
13837b368470SClaudio Imbrenda 
13847b368470SClaudio Imbrenda 	for (cur = ALIGN_DOWN(gfn, _PAGE_ENTRIES); cur < gfn + count; cur += _PAGE_ENTRIES) {
13857b368470SClaudio Imbrenda 		rc = dat_entry_walk(mc, cur, asce, DAT_WALK_ALLOC, TABLE_TYPE_PAGE_TABLE,
13867b368470SClaudio Imbrenda 				    &crstep, &ptep);
13877b368470SClaudio Imbrenda 		if (rc)
13887b368470SClaudio Imbrenda 			return rc;
13897b368470SClaudio Imbrenda 	}
13907b368470SClaudio Imbrenda 	return _dat_walk_gfn_range(gfn, gfn + count, asce, &ops, DAT_WALK_IGN_HOLES, &state);
13917b368470SClaudio Imbrenda }
1392