xref: /linux/arch/s390/kvm/gaccess.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
222938978SHeiko Carstens /*
322938978SHeiko Carstens  * guest access functions
422938978SHeiko Carstens  *
522938978SHeiko Carstens  * Copyright IBM Corp. 2014
622938978SHeiko Carstens  *
722938978SHeiko Carstens  */
822938978SHeiko Carstens 
922938978SHeiko Carstens #include <linux/vmalloc.h>
10589ee628SIngo Molnar #include <linux/mm_types.h>
1122938978SHeiko Carstens #include <linux/err.h>
12ca5999fdSMike Rapoport #include <linux/pgtable.h>
13e613d834SJanis Schoetterl-Glausch #include <linux/bitfield.h>
1430410373SHeiko Carstens #include <asm/access-regs.h>
1544ae7663SHeiko Carstens #include <asm/fault.h>
16aa17aa57SMartin Schwidefsky #include <asm/gmap.h>
17*275d05ceSClaudio Imbrenda #include <asm/dat-bits.h>
1822938978SHeiko Carstens #include "kvm-s390.h"
1922938978SHeiko Carstens #include "gaccess.h"
2022938978SHeiko Carstens 
2122938978SHeiko Carstens /*
2222938978SHeiko Carstens  * vaddress union in order to easily decode a virtual address into its
2322938978SHeiko Carstens  * region first index, region second index etc. parts.
2422938978SHeiko Carstens  */
2522938978SHeiko Carstens union vaddress {
2622938978SHeiko Carstens 	unsigned long addr;
2722938978SHeiko Carstens 	struct {
2822938978SHeiko Carstens 		unsigned long rfx : 11;
2922938978SHeiko Carstens 		unsigned long rsx : 11;
3022938978SHeiko Carstens 		unsigned long rtx : 11;
3122938978SHeiko Carstens 		unsigned long sx  : 11;
3222938978SHeiko Carstens 		unsigned long px  : 8;
3322938978SHeiko Carstens 		unsigned long bx  : 12;
3422938978SHeiko Carstens 	};
3522938978SHeiko Carstens 	struct {
3622938978SHeiko Carstens 		unsigned long rfx01 : 2;
3722938978SHeiko Carstens 		unsigned long	    : 9;
3822938978SHeiko Carstens 		unsigned long rsx01 : 2;
3922938978SHeiko Carstens 		unsigned long	    : 9;
4022938978SHeiko Carstens 		unsigned long rtx01 : 2;
4122938978SHeiko Carstens 		unsigned long	    : 9;
4222938978SHeiko Carstens 		unsigned long sx01  : 2;
4322938978SHeiko Carstens 		unsigned long	    : 29;
4422938978SHeiko Carstens 	};
4522938978SHeiko Carstens };
4622938978SHeiko Carstens 
4722938978SHeiko Carstens /*
4822938978SHeiko Carstens  * raddress union which will contain the result (real or absolute address)
4922938978SHeiko Carstens  * after a page table walk. The rfaa, sfaa and pfra members are used to
5022938978SHeiko Carstens  * simply assign them the value of a region, segment or page table entry.
5122938978SHeiko Carstens  */
5222938978SHeiko Carstens union raddress {
5322938978SHeiko Carstens 	unsigned long addr;
5422938978SHeiko Carstens 	unsigned long rfaa : 33; /* Region-Frame Absolute Address */
5522938978SHeiko Carstens 	unsigned long sfaa : 44; /* Segment-Frame Absolute Address */
5622938978SHeiko Carstens 	unsigned long pfra : 52; /* Page-Frame Real Address */
5722938978SHeiko Carstens };
5822938978SHeiko Carstens 
59664b4973SAlexander Yarygin union alet {
60664b4973SAlexander Yarygin 	u32 val;
61664b4973SAlexander Yarygin 	struct {
62664b4973SAlexander Yarygin 		u32 reserved : 7;
63664b4973SAlexander Yarygin 		u32 p        : 1;
64664b4973SAlexander Yarygin 		u32 alesn    : 8;
65664b4973SAlexander Yarygin 		u32 alen     : 16;
66664b4973SAlexander Yarygin 	};
67664b4973SAlexander Yarygin };
68664b4973SAlexander Yarygin 
69664b4973SAlexander Yarygin union ald {
70664b4973SAlexander Yarygin 	u32 val;
71664b4973SAlexander Yarygin 	struct {
72664b4973SAlexander Yarygin 		u32     : 1;
73664b4973SAlexander Yarygin 		u32 alo : 24;
74664b4973SAlexander Yarygin 		u32 all : 7;
75664b4973SAlexander Yarygin 	};
76664b4973SAlexander Yarygin };
77664b4973SAlexander Yarygin 
78664b4973SAlexander Yarygin struct ale {
79664b4973SAlexander Yarygin 	unsigned long i      : 1; /* ALEN-Invalid Bit */
80664b4973SAlexander Yarygin 	unsigned long        : 5;
81664b4973SAlexander Yarygin 	unsigned long fo     : 1; /* Fetch-Only Bit */
82664b4973SAlexander Yarygin 	unsigned long p      : 1; /* Private Bit */
83664b4973SAlexander Yarygin 	unsigned long alesn  : 8; /* Access-List-Entry Sequence Number */
84664b4973SAlexander Yarygin 	unsigned long aleax  : 16; /* Access-List-Entry Authorization Index */
85664b4973SAlexander Yarygin 	unsigned long        : 32;
86664b4973SAlexander Yarygin 	unsigned long        : 1;
87664b4973SAlexander Yarygin 	unsigned long asteo  : 25; /* ASN-Second-Table-Entry Origin */
88664b4973SAlexander Yarygin 	unsigned long        : 6;
89664b4973SAlexander Yarygin 	unsigned long astesn : 32; /* ASTE Sequence Number */
901cae0255SMartin Schwidefsky };
91664b4973SAlexander Yarygin 
92664b4973SAlexander Yarygin struct aste {
93664b4973SAlexander Yarygin 	unsigned long i      : 1; /* ASX-Invalid Bit */
94664b4973SAlexander Yarygin 	unsigned long ato    : 29; /* Authority-Table Origin */
95664b4973SAlexander Yarygin 	unsigned long        : 1;
96664b4973SAlexander Yarygin 	unsigned long b      : 1; /* Base-Space Bit */
97664b4973SAlexander Yarygin 	unsigned long ax     : 16; /* Authorization Index */
98664b4973SAlexander Yarygin 	unsigned long atl    : 12; /* Authority-Table Length */
99664b4973SAlexander Yarygin 	unsigned long        : 2;
100664b4973SAlexander Yarygin 	unsigned long ca     : 1; /* Controlled-ASN Bit */
101664b4973SAlexander Yarygin 	unsigned long ra     : 1; /* Reusable-ASN Bit */
102664b4973SAlexander Yarygin 	unsigned long asce   : 64; /* Address-Space-Control Element */
103664b4973SAlexander Yarygin 	unsigned long ald    : 32;
104664b4973SAlexander Yarygin 	unsigned long astesn : 32;
105664b4973SAlexander Yarygin 	/* .. more fields there */
1061cae0255SMartin Schwidefsky };
1078a242234SHeiko Carstens 
ipte_lock_held(struct kvm * kvm)1080130337eSPierre Morel int ipte_lock_held(struct kvm *kvm)
1098a242234SHeiko Carstens {
1100130337eSPierre Morel 	if (sclp.has_siif) {
1115e044315SEugene (jno) Dvurechenski 		int rc;
1128a242234SHeiko Carstens 
1130130337eSPierre Morel 		read_lock(&kvm->arch.sca_lock);
1140130337eSPierre Morel 		rc = kvm_s390_get_ipte_control(kvm)->kh != 0;
1150130337eSPierre Morel 		read_unlock(&kvm->arch.sca_lock);
1165e044315SEugene (jno) Dvurechenski 		return rc;
1175e044315SEugene (jno) Dvurechenski 	}
1180130337eSPierre Morel 	return kvm->arch.ipte_lock_count != 0;
1198a242234SHeiko Carstens }
1208a242234SHeiko Carstens 
ipte_lock_simple(struct kvm * kvm)1210130337eSPierre Morel static void ipte_lock_simple(struct kvm *kvm)
1228a242234SHeiko Carstens {
1238a242234SHeiko Carstens 	union ipte_control old, new, *ic;
1248a242234SHeiko Carstens 
1250130337eSPierre Morel 	mutex_lock(&kvm->arch.ipte_mutex);
1260130337eSPierre Morel 	kvm->arch.ipte_lock_count++;
1270130337eSPierre Morel 	if (kvm->arch.ipte_lock_count > 1)
1288a242234SHeiko Carstens 		goto out;
1295e044315SEugene (jno) Dvurechenski retry:
1300130337eSPierre Morel 	read_lock(&kvm->arch.sca_lock);
1310130337eSPierre Morel 	ic = kvm_s390_get_ipte_control(kvm);
1328a242234SHeiko Carstens 	do {
1335de72a22SChristian Borntraeger 		old = READ_ONCE(*ic);
1345e044315SEugene (jno) Dvurechenski 		if (old.k) {
1350130337eSPierre Morel 			read_unlock(&kvm->arch.sca_lock);
1368a242234SHeiko Carstens 			cond_resched();
1375e044315SEugene (jno) Dvurechenski 			goto retry;
1388a242234SHeiko Carstens 		}
1398a242234SHeiko Carstens 		new = old;
1408a242234SHeiko Carstens 		new.k = 1;
1418a242234SHeiko Carstens 	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
1420130337eSPierre Morel 	read_unlock(&kvm->arch.sca_lock);
1438a242234SHeiko Carstens out:
1440130337eSPierre Morel 	mutex_unlock(&kvm->arch.ipte_mutex);
1458a242234SHeiko Carstens }
1468a242234SHeiko Carstens 
ipte_unlock_simple(struct kvm * kvm)1470130337eSPierre Morel static void ipte_unlock_simple(struct kvm *kvm)
1488a242234SHeiko Carstens {
1498a242234SHeiko Carstens 	union ipte_control old, new, *ic;
1508a242234SHeiko Carstens 
1510130337eSPierre Morel 	mutex_lock(&kvm->arch.ipte_mutex);
1520130337eSPierre Morel 	kvm->arch.ipte_lock_count--;
1530130337eSPierre Morel 	if (kvm->arch.ipte_lock_count)
1548a242234SHeiko Carstens 		goto out;
1550130337eSPierre Morel 	read_lock(&kvm->arch.sca_lock);
1560130337eSPierre Morel 	ic = kvm_s390_get_ipte_control(kvm);
1578a242234SHeiko Carstens 	do {
1585de72a22SChristian Borntraeger 		old = READ_ONCE(*ic);
1591365039dSChristian Borntraeger 		new = old;
1608a242234SHeiko Carstens 		new.k = 0;
1618a242234SHeiko Carstens 	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
1620130337eSPierre Morel 	read_unlock(&kvm->arch.sca_lock);
1630130337eSPierre Morel 	wake_up(&kvm->arch.ipte_wq);
1648a242234SHeiko Carstens out:
1650130337eSPierre Morel 	mutex_unlock(&kvm->arch.ipte_mutex);
1668a242234SHeiko Carstens }
1678a242234SHeiko Carstens 
ipte_lock_siif(struct kvm * kvm)1680130337eSPierre Morel static void ipte_lock_siif(struct kvm *kvm)
1698a242234SHeiko Carstens {
1708a242234SHeiko Carstens 	union ipte_control old, new, *ic;
1718a242234SHeiko Carstens 
1725e044315SEugene (jno) Dvurechenski retry:
1730130337eSPierre Morel 	read_lock(&kvm->arch.sca_lock);
1740130337eSPierre Morel 	ic = kvm_s390_get_ipte_control(kvm);
1758a242234SHeiko Carstens 	do {
1765de72a22SChristian Borntraeger 		old = READ_ONCE(*ic);
1775e044315SEugene (jno) Dvurechenski 		if (old.kg) {
1780130337eSPierre Morel 			read_unlock(&kvm->arch.sca_lock);
1798a242234SHeiko Carstens 			cond_resched();
1805e044315SEugene (jno) Dvurechenski 			goto retry;
1818a242234SHeiko Carstens 		}
1828a242234SHeiko Carstens 		new = old;
1838a242234SHeiko Carstens 		new.k = 1;
1848a242234SHeiko Carstens 		new.kh++;
1858a242234SHeiko Carstens 	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
1860130337eSPierre Morel 	read_unlock(&kvm->arch.sca_lock);
1878a242234SHeiko Carstens }
1888a242234SHeiko Carstens 
ipte_unlock_siif(struct kvm * kvm)1890130337eSPierre Morel static void ipte_unlock_siif(struct kvm *kvm)
1908a242234SHeiko Carstens {
1918a242234SHeiko Carstens 	union ipte_control old, new, *ic;
1928a242234SHeiko Carstens 
1930130337eSPierre Morel 	read_lock(&kvm->arch.sca_lock);
1940130337eSPierre Morel 	ic = kvm_s390_get_ipte_control(kvm);
1958a242234SHeiko Carstens 	do {
1965de72a22SChristian Borntraeger 		old = READ_ONCE(*ic);
1971365039dSChristian Borntraeger 		new = old;
1988a242234SHeiko Carstens 		new.kh--;
1998a242234SHeiko Carstens 		if (!new.kh)
2008a242234SHeiko Carstens 			new.k = 0;
2018a242234SHeiko Carstens 	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
2020130337eSPierre Morel 	read_unlock(&kvm->arch.sca_lock);
2038a242234SHeiko Carstens 	if (!new.kh)
2040130337eSPierre Morel 		wake_up(&kvm->arch.ipte_wq);
2058a242234SHeiko Carstens }
2068a242234SHeiko Carstens 
ipte_lock(struct kvm * kvm)2070130337eSPierre Morel void ipte_lock(struct kvm *kvm)
2088a242234SHeiko Carstens {
2090130337eSPierre Morel 	if (sclp.has_siif)
2100130337eSPierre Morel 		ipte_lock_siif(kvm);
2118a242234SHeiko Carstens 	else
2120130337eSPierre Morel 		ipte_lock_simple(kvm);
2138a242234SHeiko Carstens }
2148a242234SHeiko Carstens 
ipte_unlock(struct kvm * kvm)2150130337eSPierre Morel void ipte_unlock(struct kvm *kvm)
2168a242234SHeiko Carstens {
2170130337eSPierre Morel 	if (sclp.has_siif)
2180130337eSPierre Morel 		ipte_unlock_siif(kvm);
2198a242234SHeiko Carstens 	else
2200130337eSPierre Morel 		ipte_unlock_simple(kvm);
2218a242234SHeiko Carstens }
2228a242234SHeiko Carstens 
ar_translation(struct kvm_vcpu * vcpu,union asce * asce,u8 ar,enum gacc_mode mode)22327f67f87SChristian Borntraeger static int ar_translation(struct kvm_vcpu *vcpu, union asce *asce, u8 ar,
22492c96321SDavid Hildenbrand 			  enum gacc_mode mode)
22522938978SHeiko Carstens {
226664b4973SAlexander Yarygin 	union alet alet;
227664b4973SAlexander Yarygin 	struct ale ale;
228664b4973SAlexander Yarygin 	struct aste aste;
229664b4973SAlexander Yarygin 	unsigned long ald_addr, authority_table_addr;
230664b4973SAlexander Yarygin 	union ald ald;
231664b4973SAlexander Yarygin 	int eax, rc;
232664b4973SAlexander Yarygin 	u8 authority_table;
233664b4973SAlexander Yarygin 
234664b4973SAlexander Yarygin 	if (ar >= NUM_ACRS)
235664b4973SAlexander Yarygin 		return -EINVAL;
236664b4973SAlexander Yarygin 
23701be7f53SEric Farman 	if (vcpu->arch.acrs_loaded)
238664b4973SAlexander Yarygin 		save_access_regs(vcpu->run->s.regs.acrs);
239664b4973SAlexander Yarygin 	alet.val = vcpu->run->s.regs.acrs[ar];
240664b4973SAlexander Yarygin 
241664b4973SAlexander Yarygin 	if (ar == 0 || alet.val == 0) {
242664b4973SAlexander Yarygin 		asce->val = vcpu->arch.sie_block->gcr[1];
243664b4973SAlexander Yarygin 		return 0;
244664b4973SAlexander Yarygin 	} else if (alet.val == 1) {
245664b4973SAlexander Yarygin 		asce->val = vcpu->arch.sie_block->gcr[7];
246664b4973SAlexander Yarygin 		return 0;
247664b4973SAlexander Yarygin 	}
248664b4973SAlexander Yarygin 
249664b4973SAlexander Yarygin 	if (alet.reserved)
250664b4973SAlexander Yarygin 		return PGM_ALET_SPECIFICATION;
251664b4973SAlexander Yarygin 
252664b4973SAlexander Yarygin 	if (alet.p)
253664b4973SAlexander Yarygin 		ald_addr = vcpu->arch.sie_block->gcr[5];
254664b4973SAlexander Yarygin 	else
255664b4973SAlexander Yarygin 		ald_addr = vcpu->arch.sie_block->gcr[2];
256664b4973SAlexander Yarygin 	ald_addr &= 0x7fffffc0;
257664b4973SAlexander Yarygin 
258664b4973SAlexander Yarygin 	rc = read_guest_real(vcpu, ald_addr + 16, &ald.val, sizeof(union ald));
259664b4973SAlexander Yarygin 	if (rc)
260664b4973SAlexander Yarygin 		return rc;
261664b4973SAlexander Yarygin 
262664b4973SAlexander Yarygin 	if (alet.alen / 8 > ald.all)
263664b4973SAlexander Yarygin 		return PGM_ALEN_TRANSLATION;
264664b4973SAlexander Yarygin 
265664b4973SAlexander Yarygin 	if (0x7fffffff - ald.alo * 128 < alet.alen * 16)
266664b4973SAlexander Yarygin 		return PGM_ADDRESSING;
267664b4973SAlexander Yarygin 
268664b4973SAlexander Yarygin 	rc = read_guest_real(vcpu, ald.alo * 128 + alet.alen * 16, &ale,
269664b4973SAlexander Yarygin 			     sizeof(struct ale));
270664b4973SAlexander Yarygin 	if (rc)
271664b4973SAlexander Yarygin 		return rc;
272664b4973SAlexander Yarygin 
273664b4973SAlexander Yarygin 	if (ale.i == 1)
274664b4973SAlexander Yarygin 		return PGM_ALEN_TRANSLATION;
275664b4973SAlexander Yarygin 	if (ale.alesn != alet.alesn)
276664b4973SAlexander Yarygin 		return PGM_ALE_SEQUENCE;
277664b4973SAlexander Yarygin 
278664b4973SAlexander Yarygin 	rc = read_guest_real(vcpu, ale.asteo * 64, &aste, sizeof(struct aste));
279664b4973SAlexander Yarygin 	if (rc)
280664b4973SAlexander Yarygin 		return rc;
281664b4973SAlexander Yarygin 
282664b4973SAlexander Yarygin 	if (aste.i)
283664b4973SAlexander Yarygin 		return PGM_ASTE_VALIDITY;
284664b4973SAlexander Yarygin 	if (aste.astesn != ale.astesn)
285664b4973SAlexander Yarygin 		return PGM_ASTE_SEQUENCE;
286664b4973SAlexander Yarygin 
287664b4973SAlexander Yarygin 	if (ale.p == 1) {
288664b4973SAlexander Yarygin 		eax = (vcpu->arch.sie_block->gcr[8] >> 16) & 0xffff;
289664b4973SAlexander Yarygin 		if (ale.aleax != eax) {
290664b4973SAlexander Yarygin 			if (eax / 16 > aste.atl)
291664b4973SAlexander Yarygin 				return PGM_EXTENDED_AUTHORITY;
292664b4973SAlexander Yarygin 
293664b4973SAlexander Yarygin 			authority_table_addr = aste.ato * 4 + eax / 4;
294664b4973SAlexander Yarygin 
295664b4973SAlexander Yarygin 			rc = read_guest_real(vcpu, authority_table_addr,
296664b4973SAlexander Yarygin 					     &authority_table,
297664b4973SAlexander Yarygin 					     sizeof(u8));
298664b4973SAlexander Yarygin 			if (rc)
299664b4973SAlexander Yarygin 				return rc;
300664b4973SAlexander Yarygin 
301664b4973SAlexander Yarygin 			if ((authority_table & (0x40 >> ((eax & 3) * 2))) == 0)
302664b4973SAlexander Yarygin 				return PGM_EXTENDED_AUTHORITY;
303664b4973SAlexander Yarygin 		}
304664b4973SAlexander Yarygin 	}
305664b4973SAlexander Yarygin 
30692c96321SDavid Hildenbrand 	if (ale.fo == 1 && mode == GACC_STORE)
307664b4973SAlexander Yarygin 		return PGM_PROTECTION;
308664b4973SAlexander Yarygin 
309664b4973SAlexander Yarygin 	asce->val = aste.asce;
310664b4973SAlexander Yarygin 	return 0;
311664b4973SAlexander Yarygin }
312664b4973SAlexander Yarygin 
313d03193deSDavid Hildenbrand enum prot_type {
314d03193deSDavid Hildenbrand 	PROT_TYPE_LA   = 0,
315d03193deSDavid Hildenbrand 	PROT_TYPE_KEYC = 1,
316d03193deSDavid Hildenbrand 	PROT_TYPE_ALC  = 2,
317d03193deSDavid Hildenbrand 	PROT_TYPE_DAT  = 3,
3186ae1574cSChristian Borntraeger 	PROT_TYPE_IEP  = 4,
319b3cefd6bSJanis Schoetterl-Glausch 	/* Dummy value for passing an initialized value when code != PGM_PROTECTION */
320b3cefd6bSJanis Schoetterl-Glausch 	PROT_NONE,
321d03193deSDavid Hildenbrand };
322d03193deSDavid Hildenbrand 
trans_exc_ending(struct kvm_vcpu * vcpu,int code,unsigned long gva,u8 ar,enum gacc_mode mode,enum prot_type prot,bool terminate)323c783631bSJanis Schoetterl-Glausch static int trans_exc_ending(struct kvm_vcpu *vcpu, int code, unsigned long gva, u8 ar,
324c783631bSJanis Schoetterl-Glausch 			    enum gacc_mode mode, enum prot_type prot, bool terminate)
325d03193deSDavid Hildenbrand {
326d03193deSDavid Hildenbrand 	struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm;
32744ae7663SHeiko Carstens 	union teid *teid;
328d03193deSDavid Hildenbrand 
329d03193deSDavid Hildenbrand 	memset(pgm, 0, sizeof(*pgm));
330d03193deSDavid Hildenbrand 	pgm->code = code;
33144ae7663SHeiko Carstens 	teid = (union teid *)&pgm->trans_exc_code;
332d03193deSDavid Hildenbrand 
333d03193deSDavid Hildenbrand 	switch (code) {
334c14b88d7SJanosch Frank 	case PGM_PROTECTION:
335c14b88d7SJanosch Frank 		switch (prot) {
336b3cefd6bSJanis Schoetterl-Glausch 		case PROT_NONE:
337b3cefd6bSJanis Schoetterl-Glausch 			/* We should never get here, acts like termination */
338b3cefd6bSJanis Schoetterl-Glausch 			WARN_ON_ONCE(1);
339b3cefd6bSJanis Schoetterl-Glausch 			break;
3406ae1574cSChristian Borntraeger 		case PROT_TYPE_IEP:
34144ae7663SHeiko Carstens 			teid->b61 = 1;
3423b684a42SJoe Perches 			fallthrough;
343a679c547SChristian Borntraeger 		case PROT_TYPE_LA:
34444ae7663SHeiko Carstens 			teid->b56 = 1;
345a679c547SChristian Borntraeger 			break;
346a679c547SChristian Borntraeger 		case PROT_TYPE_KEYC:
34744ae7663SHeiko Carstens 			teid->b60 = 1;
348a679c547SChristian Borntraeger 			break;
349c14b88d7SJanosch Frank 		case PROT_TYPE_ALC:
35044ae7663SHeiko Carstens 			teid->b60 = 1;
3513b684a42SJoe Perches 			fallthrough;
352c14b88d7SJanosch Frank 		case PROT_TYPE_DAT:
35344ae7663SHeiko Carstens 			teid->b61 = 1;
354c14b88d7SJanosch Frank 			break;
355c14b88d7SJanosch Frank 		}
356c783631bSJanis Schoetterl-Glausch 		if (terminate) {
35744ae7663SHeiko Carstens 			teid->b56 = 0;
35844ae7663SHeiko Carstens 			teid->b60 = 0;
35944ae7663SHeiko Carstens 			teid->b61 = 0;
360c783631bSJanis Schoetterl-Glausch 		}
3613b684a42SJoe Perches 		fallthrough;
362d03193deSDavid Hildenbrand 	case PGM_ASCE_TYPE:
363d03193deSDavid Hildenbrand 	case PGM_PAGE_TRANSLATION:
364d03193deSDavid Hildenbrand 	case PGM_REGION_FIRST_TRANS:
365d03193deSDavid Hildenbrand 	case PGM_REGION_SECOND_TRANS:
366d03193deSDavid Hildenbrand 	case PGM_REGION_THIRD_TRANS:
367d03193deSDavid Hildenbrand 	case PGM_SEGMENT_TRANSLATION:
368d03193deSDavid Hildenbrand 		/*
369d03193deSDavid Hildenbrand 		 * op_access_id only applies to MOVE_PAGE -> set bit 61
370d03193deSDavid Hildenbrand 		 * exc_access_id has to be set to 0 for some instructions. Both
371c14b88d7SJanosch Frank 		 * cases have to be handled by the caller.
372d03193deSDavid Hildenbrand 		 */
37344ae7663SHeiko Carstens 		teid->addr = gva >> PAGE_SHIFT;
37444ae7663SHeiko Carstens 		teid->fsi = mode == GACC_STORE ? TEID_FSI_STORE : TEID_FSI_FETCH;
37544ae7663SHeiko Carstens 		teid->as = psw_bits(vcpu->arch.sie_block->gpsw).as;
3763b684a42SJoe Perches 		fallthrough;
377d03193deSDavid Hildenbrand 	case PGM_ALEN_TRANSLATION:
378d03193deSDavid Hildenbrand 	case PGM_ALE_SEQUENCE:
379d03193deSDavid Hildenbrand 	case PGM_ASTE_VALIDITY:
380d03193deSDavid Hildenbrand 	case PGM_ASTE_SEQUENCE:
381d03193deSDavid Hildenbrand 	case PGM_EXTENDED_AUTHORITY:
382c14b88d7SJanosch Frank 		/*
383c14b88d7SJanosch Frank 		 * We can always store exc_access_id, as it is
384c14b88d7SJanosch Frank 		 * undefined for non-ar cases. It is undefined for
385c14b88d7SJanosch Frank 		 * most DAT protection exceptions.
386c14b88d7SJanosch Frank 		 */
387d03193deSDavid Hildenbrand 		pgm->exc_access_id = ar;
388d03193deSDavid Hildenbrand 		break;
389d03193deSDavid Hildenbrand 	}
390d03193deSDavid Hildenbrand 	return code;
391d03193deSDavid Hildenbrand }
392d03193deSDavid Hildenbrand 
trans_exc(struct kvm_vcpu * vcpu,int code,unsigned long gva,u8 ar,enum gacc_mode mode,enum prot_type prot)393c783631bSJanis Schoetterl-Glausch static int trans_exc(struct kvm_vcpu *vcpu, int code, unsigned long gva, u8 ar,
394c783631bSJanis Schoetterl-Glausch 		     enum gacc_mode mode, enum prot_type prot)
395c783631bSJanis Schoetterl-Glausch {
396c783631bSJanis Schoetterl-Glausch 	return trans_exc_ending(vcpu, code, gva, ar, mode, prot, false);
397c783631bSJanis Schoetterl-Glausch }
398c783631bSJanis Schoetterl-Glausch 
get_vcpu_asce(struct kvm_vcpu * vcpu,union asce * asce,unsigned long ga,u8 ar,enum gacc_mode mode)399664b4973SAlexander Yarygin static int get_vcpu_asce(struct kvm_vcpu *vcpu, union asce *asce,
40027f67f87SChristian Borntraeger 			 unsigned long ga, u8 ar, enum gacc_mode mode)
401664b4973SAlexander Yarygin {
402664b4973SAlexander Yarygin 	int rc;
40334346b9aSDavid Hildenbrand 	struct psw_bits psw = psw_bits(vcpu->arch.sie_block->gpsw);
404664b4973SAlexander Yarygin 
405a7525982SHeiko Carstens 	if (!psw.dat) {
406664b4973SAlexander Yarygin 		asce->val = 0;
407664b4973SAlexander Yarygin 		asce->r = 1;
408664b4973SAlexander Yarygin 		return 0;
409664b4973SAlexander Yarygin 	}
410664b4973SAlexander Yarygin 
4118bb3fdd6SHeiko Carstens 	if ((mode == GACC_IFETCH) && (psw.as != PSW_BITS_AS_HOME))
4128bb3fdd6SHeiko Carstens 		psw.as = PSW_BITS_AS_PRIMARY;
41334346b9aSDavid Hildenbrand 
41434346b9aSDavid Hildenbrand 	switch (psw.as) {
4158bb3fdd6SHeiko Carstens 	case PSW_BITS_AS_PRIMARY:
416664b4973SAlexander Yarygin 		asce->val = vcpu->arch.sie_block->gcr[1];
417664b4973SAlexander Yarygin 		return 0;
4188bb3fdd6SHeiko Carstens 	case PSW_BITS_AS_SECONDARY:
419664b4973SAlexander Yarygin 		asce->val = vcpu->arch.sie_block->gcr[7];
420664b4973SAlexander Yarygin 		return 0;
4218bb3fdd6SHeiko Carstens 	case PSW_BITS_AS_HOME:
422664b4973SAlexander Yarygin 		asce->val = vcpu->arch.sie_block->gcr[13];
423664b4973SAlexander Yarygin 		return 0;
4248bb3fdd6SHeiko Carstens 	case PSW_BITS_AS_ACCREG:
42592c96321SDavid Hildenbrand 		rc = ar_translation(vcpu, asce, ar, mode);
426664b4973SAlexander Yarygin 		if (rc > 0)
427bcfa01d7SDavid Hildenbrand 			return trans_exc(vcpu, rc, ga, ar, mode, PROT_TYPE_ALC);
428664b4973SAlexander Yarygin 		return rc;
42922938978SHeiko Carstens 	}
43022938978SHeiko Carstens 	return 0;
43122938978SHeiko Carstens }
43222938978SHeiko Carstens 
deref_table(struct kvm * kvm,unsigned long gpa,unsigned long * val)43322938978SHeiko Carstens static int deref_table(struct kvm *kvm, unsigned long gpa, unsigned long *val)
43422938978SHeiko Carstens {
43522938978SHeiko Carstens 	return kvm_read_guest(kvm, gpa, val, sizeof(*val));
43622938978SHeiko Carstens }
43722938978SHeiko Carstens 
43822938978SHeiko Carstens /**
43922938978SHeiko Carstens  * guest_translate - translate a guest virtual into a guest absolute address
44022938978SHeiko Carstens  * @vcpu: virtual cpu
44122938978SHeiko Carstens  * @gva: guest virtual address
44222938978SHeiko Carstens  * @gpa: points to where guest physical (absolute) address should be stored
44375a18122SAlexander Yarygin  * @asce: effective asce
44492c96321SDavid Hildenbrand  * @mode: indicates the access mode to be used
4456ae1574cSChristian Borntraeger  * @prot: returns the type for protection exceptions
44622938978SHeiko Carstens  *
44722938978SHeiko Carstens  * Translate a guest virtual address into a guest absolute address by means
44816b0fc13SYannick Guerrini  * of dynamic address translation as specified by the architecture.
44922938978SHeiko Carstens  * If the resulting absolute address is not available in the configuration
45022938978SHeiko Carstens  * an addressing exception is indicated and @gpa will not be changed.
45122938978SHeiko Carstens  *
45222938978SHeiko Carstens  * Returns: - zero on success; @gpa contains the resulting absolute address
45322938978SHeiko Carstens  *	    - a negative value if guest access failed due to e.g. broken
45422938978SHeiko Carstens  *	      guest mapping
455cada938aSHeiko Carstens  *	    - a positive value if an access exception happened. In this case
45622938978SHeiko Carstens  *	      the returned value is the program interruption code as defined
45722938978SHeiko Carstens  *	      by the architecture
45822938978SHeiko Carstens  */
guest_translate(struct kvm_vcpu * vcpu,unsigned long gva,unsigned long * gpa,const union asce asce,enum gacc_mode mode,enum prot_type * prot)45922938978SHeiko Carstens static unsigned long guest_translate(struct kvm_vcpu *vcpu, unsigned long gva,
46075a18122SAlexander Yarygin 				     unsigned long *gpa, const union asce asce,
4616ae1574cSChristian Borntraeger 				     enum gacc_mode mode, enum prot_type *prot)
46222938978SHeiko Carstens {
46322938978SHeiko Carstens 	union vaddress vaddr = {.addr = gva};
46422938978SHeiko Carstens 	union raddress raddr = {.addr = gva};
46522938978SHeiko Carstens 	union page_table_entry pte;
46622938978SHeiko Carstens 	int dat_protection = 0;
4676ae1574cSChristian Borntraeger 	int iep_protection = 0;
46822938978SHeiko Carstens 	union ctlreg0 ctlreg0;
46922938978SHeiko Carstens 	unsigned long ptr;
4706ae1574cSChristian Borntraeger 	int edat1, edat2, iep;
47122938978SHeiko Carstens 
47222938978SHeiko Carstens 	ctlreg0.val = vcpu->arch.sie_block->gcr[0];
4739d8d5786SMichael Mueller 	edat1 = ctlreg0.edat && test_kvm_facility(vcpu->kvm, 8);
4749d8d5786SMichael Mueller 	edat2 = edat1 && test_kvm_facility(vcpu->kvm, 78);
4756ae1574cSChristian Borntraeger 	iep = ctlreg0.iep && test_kvm_facility(vcpu->kvm, 130);
47622938978SHeiko Carstens 	if (asce.r)
47722938978SHeiko Carstens 		goto real_address;
478*275d05ceSClaudio Imbrenda 	ptr = asce.rsto * PAGE_SIZE;
47922938978SHeiko Carstens 	switch (asce.dt) {
48022938978SHeiko Carstens 	case ASCE_TYPE_REGION1:
48122938978SHeiko Carstens 		if (vaddr.rfx01 > asce.tl)
48222938978SHeiko Carstens 			return PGM_REGION_FIRST_TRANS;
48322938978SHeiko Carstens 		ptr += vaddr.rfx * 8;
48422938978SHeiko Carstens 		break;
48522938978SHeiko Carstens 	case ASCE_TYPE_REGION2:
48622938978SHeiko Carstens 		if (vaddr.rfx)
48722938978SHeiko Carstens 			return PGM_ASCE_TYPE;
48822938978SHeiko Carstens 		if (vaddr.rsx01 > asce.tl)
48922938978SHeiko Carstens 			return PGM_REGION_SECOND_TRANS;
49022938978SHeiko Carstens 		ptr += vaddr.rsx * 8;
49122938978SHeiko Carstens 		break;
49222938978SHeiko Carstens 	case ASCE_TYPE_REGION3:
49322938978SHeiko Carstens 		if (vaddr.rfx || vaddr.rsx)
49422938978SHeiko Carstens 			return PGM_ASCE_TYPE;
49522938978SHeiko Carstens 		if (vaddr.rtx01 > asce.tl)
49622938978SHeiko Carstens 			return PGM_REGION_THIRD_TRANS;
49722938978SHeiko Carstens 		ptr += vaddr.rtx * 8;
49822938978SHeiko Carstens 		break;
49922938978SHeiko Carstens 	case ASCE_TYPE_SEGMENT:
50022938978SHeiko Carstens 		if (vaddr.rfx || vaddr.rsx || vaddr.rtx)
50122938978SHeiko Carstens 			return PGM_ASCE_TYPE;
50222938978SHeiko Carstens 		if (vaddr.sx01 > asce.tl)
50322938978SHeiko Carstens 			return PGM_SEGMENT_TRANSLATION;
50422938978SHeiko Carstens 		ptr += vaddr.sx * 8;
50522938978SHeiko Carstens 		break;
50622938978SHeiko Carstens 	}
50722938978SHeiko Carstens 	switch (asce.dt) {
50822938978SHeiko Carstens 	case ASCE_TYPE_REGION1:	{
50922938978SHeiko Carstens 		union region1_table_entry rfte;
51022938978SHeiko Carstens 
5119e7325acSSean Christopherson 		if (!kvm_is_gpa_in_memslot(vcpu->kvm, ptr))
51222938978SHeiko Carstens 			return PGM_ADDRESSING;
51322938978SHeiko Carstens 		if (deref_table(vcpu->kvm, ptr, &rfte.val))
51422938978SHeiko Carstens 			return -EFAULT;
51522938978SHeiko Carstens 		if (rfte.i)
51622938978SHeiko Carstens 			return PGM_REGION_FIRST_TRANS;
51722938978SHeiko Carstens 		if (rfte.tt != TABLE_TYPE_REGION1)
51822938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
51922938978SHeiko Carstens 		if (vaddr.rsx01 < rfte.tf || vaddr.rsx01 > rfte.tl)
52022938978SHeiko Carstens 			return PGM_REGION_SECOND_TRANS;
52122938978SHeiko Carstens 		if (edat1)
52222938978SHeiko Carstens 			dat_protection |= rfte.p;
52358cdf5ebSHeiko Carstens 		ptr = rfte.rto * PAGE_SIZE + vaddr.rsx * 8;
52422938978SHeiko Carstens 	}
5253b684a42SJoe Perches 		fallthrough;
52622938978SHeiko Carstens 	case ASCE_TYPE_REGION2: {
52722938978SHeiko Carstens 		union region2_table_entry rste;
52822938978SHeiko Carstens 
5299e7325acSSean Christopherson 		if (!kvm_is_gpa_in_memslot(vcpu->kvm, ptr))
53022938978SHeiko Carstens 			return PGM_ADDRESSING;
53122938978SHeiko Carstens 		if (deref_table(vcpu->kvm, ptr, &rste.val))
53222938978SHeiko Carstens 			return -EFAULT;
53322938978SHeiko Carstens 		if (rste.i)
53422938978SHeiko Carstens 			return PGM_REGION_SECOND_TRANS;
53522938978SHeiko Carstens 		if (rste.tt != TABLE_TYPE_REGION2)
53622938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
53722938978SHeiko Carstens 		if (vaddr.rtx01 < rste.tf || vaddr.rtx01 > rste.tl)
53822938978SHeiko Carstens 			return PGM_REGION_THIRD_TRANS;
53922938978SHeiko Carstens 		if (edat1)
54022938978SHeiko Carstens 			dat_protection |= rste.p;
54158cdf5ebSHeiko Carstens 		ptr = rste.rto * PAGE_SIZE + vaddr.rtx * 8;
54222938978SHeiko Carstens 	}
5433b684a42SJoe Perches 		fallthrough;
54422938978SHeiko Carstens 	case ASCE_TYPE_REGION3: {
54522938978SHeiko Carstens 		union region3_table_entry rtte;
54622938978SHeiko Carstens 
5479e7325acSSean Christopherson 		if (!kvm_is_gpa_in_memslot(vcpu->kvm, ptr))
54822938978SHeiko Carstens 			return PGM_ADDRESSING;
54922938978SHeiko Carstens 		if (deref_table(vcpu->kvm, ptr, &rtte.val))
55022938978SHeiko Carstens 			return -EFAULT;
55122938978SHeiko Carstens 		if (rtte.i)
55222938978SHeiko Carstens 			return PGM_REGION_THIRD_TRANS;
55322938978SHeiko Carstens 		if (rtte.tt != TABLE_TYPE_REGION3)
55422938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
55522938978SHeiko Carstens 		if (rtte.cr && asce.p && edat2)
55622938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
55722938978SHeiko Carstens 		if (rtte.fc && edat2) {
55822938978SHeiko Carstens 			dat_protection |= rtte.fc1.p;
5596ae1574cSChristian Borntraeger 			iep_protection = rtte.fc1.iep;
56022938978SHeiko Carstens 			raddr.rfaa = rtte.fc1.rfaa;
56122938978SHeiko Carstens 			goto absolute_address;
56222938978SHeiko Carstens 		}
56322938978SHeiko Carstens 		if (vaddr.sx01 < rtte.fc0.tf)
56422938978SHeiko Carstens 			return PGM_SEGMENT_TRANSLATION;
56522938978SHeiko Carstens 		if (vaddr.sx01 > rtte.fc0.tl)
56622938978SHeiko Carstens 			return PGM_SEGMENT_TRANSLATION;
56722938978SHeiko Carstens 		if (edat1)
56822938978SHeiko Carstens 			dat_protection |= rtte.fc0.p;
56958cdf5ebSHeiko Carstens 		ptr = rtte.fc0.sto * PAGE_SIZE + vaddr.sx * 8;
57022938978SHeiko Carstens 	}
5713b684a42SJoe Perches 		fallthrough;
57222938978SHeiko Carstens 	case ASCE_TYPE_SEGMENT: {
57322938978SHeiko Carstens 		union segment_table_entry ste;
57422938978SHeiko Carstens 
5759e7325acSSean Christopherson 		if (!kvm_is_gpa_in_memslot(vcpu->kvm, ptr))
57622938978SHeiko Carstens 			return PGM_ADDRESSING;
57722938978SHeiko Carstens 		if (deref_table(vcpu->kvm, ptr, &ste.val))
57822938978SHeiko Carstens 			return -EFAULT;
57922938978SHeiko Carstens 		if (ste.i)
58022938978SHeiko Carstens 			return PGM_SEGMENT_TRANSLATION;
58122938978SHeiko Carstens 		if (ste.tt != TABLE_TYPE_SEGMENT)
58222938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
58322938978SHeiko Carstens 		if (ste.cs && asce.p)
58422938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
58522938978SHeiko Carstens 		if (ste.fc && edat1) {
58622938978SHeiko Carstens 			dat_protection |= ste.fc1.p;
5876ae1574cSChristian Borntraeger 			iep_protection = ste.fc1.iep;
58822938978SHeiko Carstens 			raddr.sfaa = ste.fc1.sfaa;
58922938978SHeiko Carstens 			goto absolute_address;
59022938978SHeiko Carstens 		}
59122938978SHeiko Carstens 		dat_protection |= ste.fc0.p;
59258cdf5ebSHeiko Carstens 		ptr = ste.fc0.pto * (PAGE_SIZE / 2) + vaddr.px * 8;
59322938978SHeiko Carstens 	}
59422938978SHeiko Carstens 	}
5959e7325acSSean Christopherson 	if (!kvm_is_gpa_in_memslot(vcpu->kvm, ptr))
59622938978SHeiko Carstens 		return PGM_ADDRESSING;
59722938978SHeiko Carstens 	if (deref_table(vcpu->kvm, ptr, &pte.val))
59822938978SHeiko Carstens 		return -EFAULT;
59922938978SHeiko Carstens 	if (pte.i)
60022938978SHeiko Carstens 		return PGM_PAGE_TRANSLATION;
60122938978SHeiko Carstens 	if (pte.z)
60222938978SHeiko Carstens 		return PGM_TRANSLATION_SPEC;
60322938978SHeiko Carstens 	dat_protection |= pte.p;
6046ae1574cSChristian Borntraeger 	iep_protection = pte.iep;
60522938978SHeiko Carstens 	raddr.pfra = pte.pfra;
60622938978SHeiko Carstens real_address:
60722938978SHeiko Carstens 	raddr.addr = kvm_s390_real_to_abs(vcpu, raddr.addr);
60822938978SHeiko Carstens absolute_address:
6096ae1574cSChristian Borntraeger 	if (mode == GACC_STORE && dat_protection) {
6106ae1574cSChristian Borntraeger 		*prot = PROT_TYPE_DAT;
61122938978SHeiko Carstens 		return PGM_PROTECTION;
6126ae1574cSChristian Borntraeger 	}
6136ae1574cSChristian Borntraeger 	if (mode == GACC_IFETCH && iep_protection && iep) {
6146ae1574cSChristian Borntraeger 		*prot = PROT_TYPE_IEP;
6156ae1574cSChristian Borntraeger 		return PGM_PROTECTION;
6166ae1574cSChristian Borntraeger 	}
6179e7325acSSean Christopherson 	if (!kvm_is_gpa_in_memslot(vcpu->kvm, raddr.addr))
61822938978SHeiko Carstens 		return PGM_ADDRESSING;
61922938978SHeiko Carstens 	*gpa = raddr.addr;
62022938978SHeiko Carstens 	return 0;
62122938978SHeiko Carstens }
62222938978SHeiko Carstens 
is_low_address(unsigned long ga)62322938978SHeiko Carstens static inline int is_low_address(unsigned long ga)
62422938978SHeiko Carstens {
62522938978SHeiko Carstens 	/* Check for address ranges 0..511 and 4096..4607 */
62622938978SHeiko Carstens 	return (ga & ~0x11fful) == 0;
62722938978SHeiko Carstens }
62822938978SHeiko Carstens 
low_address_protection_enabled(struct kvm_vcpu * vcpu,const union asce asce)62975a18122SAlexander Yarygin static int low_address_protection_enabled(struct kvm_vcpu *vcpu,
63075a18122SAlexander Yarygin 					  const union asce asce)
63122938978SHeiko Carstens {
63222938978SHeiko Carstens 	union ctlreg0 ctlreg0 = {.val = vcpu->arch.sie_block->gcr[0]};
63322938978SHeiko Carstens 	psw_t *psw = &vcpu->arch.sie_block->gpsw;
63422938978SHeiko Carstens 
63522938978SHeiko Carstens 	if (!ctlreg0.lap)
63622938978SHeiko Carstens 		return 0;
637a7525982SHeiko Carstens 	if (psw_bits(*psw).dat && asce.p)
63822938978SHeiko Carstens 		return 0;
63922938978SHeiko Carstens 	return 1;
64022938978SHeiko Carstens }
64122938978SHeiko Carstens 
vm_check_access_key(struct kvm * kvm,u8 access_key,enum gacc_mode mode,gpa_t gpa)642ef11c946SJanis Schoetterl-Glausch static int vm_check_access_key(struct kvm *kvm, u8 access_key,
643ef11c946SJanis Schoetterl-Glausch 			       enum gacc_mode mode, gpa_t gpa)
644ef11c946SJanis Schoetterl-Glausch {
645ef11c946SJanis Schoetterl-Glausch 	u8 storage_key, access_control;
646ef11c946SJanis Schoetterl-Glausch 	bool fetch_protected;
647ef11c946SJanis Schoetterl-Glausch 	unsigned long hva;
648ef11c946SJanis Schoetterl-Glausch 	int r;
649ef11c946SJanis Schoetterl-Glausch 
650ef11c946SJanis Schoetterl-Glausch 	if (access_key == 0)
651ef11c946SJanis Schoetterl-Glausch 		return 0;
652ef11c946SJanis Schoetterl-Glausch 
653ef11c946SJanis Schoetterl-Glausch 	hva = gfn_to_hva(kvm, gpa_to_gfn(gpa));
654ef11c946SJanis Schoetterl-Glausch 	if (kvm_is_error_hva(hva))
655ef11c946SJanis Schoetterl-Glausch 		return PGM_ADDRESSING;
656ef11c946SJanis Schoetterl-Glausch 
657ef11c946SJanis Schoetterl-Glausch 	mmap_read_lock(current->mm);
658ef11c946SJanis Schoetterl-Glausch 	r = get_guest_storage_key(current->mm, hva, &storage_key);
659ef11c946SJanis Schoetterl-Glausch 	mmap_read_unlock(current->mm);
660ef11c946SJanis Schoetterl-Glausch 	if (r)
661ef11c946SJanis Schoetterl-Glausch 		return r;
662ef11c946SJanis Schoetterl-Glausch 	access_control = FIELD_GET(_PAGE_ACC_BITS, storage_key);
663ef11c946SJanis Schoetterl-Glausch 	if (access_control == access_key)
664ef11c946SJanis Schoetterl-Glausch 		return 0;
665ef11c946SJanis Schoetterl-Glausch 	fetch_protected = storage_key & _PAGE_FP_BIT;
666ef11c946SJanis Schoetterl-Glausch 	if ((mode == GACC_FETCH || mode == GACC_IFETCH) && !fetch_protected)
667ef11c946SJanis Schoetterl-Glausch 		return 0;
668ef11c946SJanis Schoetterl-Glausch 	return PGM_PROTECTION;
669ef11c946SJanis Schoetterl-Glausch }
670ef11c946SJanis Schoetterl-Glausch 
fetch_prot_override_applicable(struct kvm_vcpu * vcpu,enum gacc_mode mode,union asce asce)671e613d834SJanis Schoetterl-Glausch static bool fetch_prot_override_applicable(struct kvm_vcpu *vcpu, enum gacc_mode mode,
672e613d834SJanis Schoetterl-Glausch 					   union asce asce)
673e613d834SJanis Schoetterl-Glausch {
674e613d834SJanis Schoetterl-Glausch 	psw_t *psw = &vcpu->arch.sie_block->gpsw;
675e613d834SJanis Schoetterl-Glausch 	unsigned long override;
676e613d834SJanis Schoetterl-Glausch 
677e613d834SJanis Schoetterl-Glausch 	if (mode == GACC_FETCH || mode == GACC_IFETCH) {
678e613d834SJanis Schoetterl-Glausch 		/* check if fetch protection override enabled */
679e613d834SJanis Schoetterl-Glausch 		override = vcpu->arch.sie_block->gcr[0];
680e613d834SJanis Schoetterl-Glausch 		override &= CR0_FETCH_PROTECTION_OVERRIDE;
681e613d834SJanis Schoetterl-Glausch 		/* not applicable if subject to DAT && private space */
682e613d834SJanis Schoetterl-Glausch 		override = override && !(psw_bits(*psw).dat && asce.p);
683e613d834SJanis Schoetterl-Glausch 		return override;
684e613d834SJanis Schoetterl-Glausch 	}
685e613d834SJanis Schoetterl-Glausch 	return false;
686e613d834SJanis Schoetterl-Glausch }
687e613d834SJanis Schoetterl-Glausch 
fetch_prot_override_applies(unsigned long ga,unsigned int len)688e613d834SJanis Schoetterl-Glausch static bool fetch_prot_override_applies(unsigned long ga, unsigned int len)
689e613d834SJanis Schoetterl-Glausch {
690e613d834SJanis Schoetterl-Glausch 	return ga < 2048 && ga + len <= 2048;
691e613d834SJanis Schoetterl-Glausch }
692e613d834SJanis Schoetterl-Glausch 
storage_prot_override_applicable(struct kvm_vcpu * vcpu)693e613d834SJanis Schoetterl-Glausch static bool storage_prot_override_applicable(struct kvm_vcpu *vcpu)
694e613d834SJanis Schoetterl-Glausch {
695e613d834SJanis Schoetterl-Glausch 	/* check if storage protection override enabled */
696e613d834SJanis Schoetterl-Glausch 	return vcpu->arch.sie_block->gcr[0] & CR0_STORAGE_PROTECTION_OVERRIDE;
697e613d834SJanis Schoetterl-Glausch }
698e613d834SJanis Schoetterl-Glausch 
storage_prot_override_applies(u8 access_control)699e613d834SJanis Schoetterl-Glausch static bool storage_prot_override_applies(u8 access_control)
700e613d834SJanis Schoetterl-Glausch {
701e613d834SJanis Schoetterl-Glausch 	/* matches special storage protection override key (9) -> allow */
702e613d834SJanis Schoetterl-Glausch 	return access_control == PAGE_SPO_ACC;
703e613d834SJanis Schoetterl-Glausch }
704e613d834SJanis Schoetterl-Glausch 
vcpu_check_access_key(struct kvm_vcpu * vcpu,u8 access_key,enum gacc_mode mode,union asce asce,gpa_t gpa,unsigned long ga,unsigned int len)705e613d834SJanis Schoetterl-Glausch static int vcpu_check_access_key(struct kvm_vcpu *vcpu, u8 access_key,
706e613d834SJanis Schoetterl-Glausch 				 enum gacc_mode mode, union asce asce, gpa_t gpa,
707e613d834SJanis Schoetterl-Glausch 				 unsigned long ga, unsigned int len)
708e613d834SJanis Schoetterl-Glausch {
709e613d834SJanis Schoetterl-Glausch 	u8 storage_key, access_control;
710e613d834SJanis Schoetterl-Glausch 	unsigned long hva;
711e613d834SJanis Schoetterl-Glausch 	int r;
712e613d834SJanis Schoetterl-Glausch 
713e613d834SJanis Schoetterl-Glausch 	/* access key 0 matches any storage key -> allow */
714e613d834SJanis Schoetterl-Glausch 	if (access_key == 0)
715e613d834SJanis Schoetterl-Glausch 		return 0;
716e613d834SJanis Schoetterl-Glausch 	/*
717e613d834SJanis Schoetterl-Glausch 	 * caller needs to ensure that gfn is accessible, so we can
718e613d834SJanis Schoetterl-Glausch 	 * assume that this cannot fail
719e613d834SJanis Schoetterl-Glausch 	 */
720e613d834SJanis Schoetterl-Glausch 	hva = gfn_to_hva(vcpu->kvm, gpa_to_gfn(gpa));
721e613d834SJanis Schoetterl-Glausch 	mmap_read_lock(current->mm);
722e613d834SJanis Schoetterl-Glausch 	r = get_guest_storage_key(current->mm, hva, &storage_key);
723e613d834SJanis Schoetterl-Glausch 	mmap_read_unlock(current->mm);
724e613d834SJanis Schoetterl-Glausch 	if (r)
725e613d834SJanis Schoetterl-Glausch 		return r;
726e613d834SJanis Schoetterl-Glausch 	access_control = FIELD_GET(_PAGE_ACC_BITS, storage_key);
727e613d834SJanis Schoetterl-Glausch 	/* access key matches storage key -> allow */
728e613d834SJanis Schoetterl-Glausch 	if (access_control == access_key)
729e613d834SJanis Schoetterl-Glausch 		return 0;
730e613d834SJanis Schoetterl-Glausch 	if (mode == GACC_FETCH || mode == GACC_IFETCH) {
731e613d834SJanis Schoetterl-Glausch 		/* it is a fetch and fetch protection is off -> allow */
732e613d834SJanis Schoetterl-Glausch 		if (!(storage_key & _PAGE_FP_BIT))
733e613d834SJanis Schoetterl-Glausch 			return 0;
734e613d834SJanis Schoetterl-Glausch 		if (fetch_prot_override_applicable(vcpu, mode, asce) &&
735e613d834SJanis Schoetterl-Glausch 		    fetch_prot_override_applies(ga, len))
736e613d834SJanis Schoetterl-Glausch 			return 0;
737e613d834SJanis Schoetterl-Glausch 	}
738e613d834SJanis Schoetterl-Glausch 	if (storage_prot_override_applicable(vcpu) &&
739e613d834SJanis Schoetterl-Glausch 	    storage_prot_override_applies(access_control))
740e613d834SJanis Schoetterl-Glausch 		return 0;
741e613d834SJanis Schoetterl-Glausch 	return PGM_PROTECTION;
742e613d834SJanis Schoetterl-Glausch }
743e613d834SJanis Schoetterl-Glausch 
7447faa543dSJanis Schoetterl-Glausch /**
7457faa543dSJanis Schoetterl-Glausch  * guest_range_to_gpas() - Calculate guest physical addresses of page fragments
7467faa543dSJanis Schoetterl-Glausch  * covering a logical range
7477faa543dSJanis Schoetterl-Glausch  * @vcpu: virtual cpu
7487faa543dSJanis Schoetterl-Glausch  * @ga: guest address, start of range
7497faa543dSJanis Schoetterl-Glausch  * @ar: access register
7507faa543dSJanis Schoetterl-Glausch  * @gpas: output argument, may be NULL
7517faa543dSJanis Schoetterl-Glausch  * @len: length of range in bytes
7527faa543dSJanis Schoetterl-Glausch  * @asce: address-space-control element to use for translation
7537faa543dSJanis Schoetterl-Glausch  * @mode: access mode
754e613d834SJanis Schoetterl-Glausch  * @access_key: access key to mach the range's storage keys against
7557faa543dSJanis Schoetterl-Glausch  *
7567faa543dSJanis Schoetterl-Glausch  * Translate a logical range to a series of guest absolute addresses,
7577faa543dSJanis Schoetterl-Glausch  * such that the concatenation of page fragments starting at each gpa make up
7587faa543dSJanis Schoetterl-Glausch  * the whole range.
7597faa543dSJanis Schoetterl-Glausch  * The translation is performed as if done by the cpu for the given @asce, @ar,
7607faa543dSJanis Schoetterl-Glausch  * @mode and state of the @vcpu.
7617faa543dSJanis Schoetterl-Glausch  * If the translation causes an exception, its program interruption code is
7627faa543dSJanis Schoetterl-Glausch  * returned and the &struct kvm_s390_pgm_info pgm member of @vcpu is modified
7637faa543dSJanis Schoetterl-Glausch  * such that a subsequent call to kvm_s390_inject_prog_vcpu() will inject
7647faa543dSJanis Schoetterl-Glausch  * a correct exception into the guest.
7657faa543dSJanis Schoetterl-Glausch  * The resulting gpas are stored into @gpas, unless it is NULL.
7667faa543dSJanis Schoetterl-Glausch  *
7677faa543dSJanis Schoetterl-Glausch  * Note: All fragments except the first one start at the beginning of a page.
7687faa543dSJanis Schoetterl-Glausch  *	 When deriving the boundaries of a fragment from a gpa, all but the last
7697faa543dSJanis Schoetterl-Glausch  *	 fragment end at the end of the page.
7707faa543dSJanis Schoetterl-Glausch  *
7717faa543dSJanis Schoetterl-Glausch  * Return:
7727faa543dSJanis Schoetterl-Glausch  * * 0		- success
7737faa543dSJanis Schoetterl-Glausch  * * <0		- translation could not be performed, for example if  guest
7747faa543dSJanis Schoetterl-Glausch  *		  memory could not be accessed
7757faa543dSJanis Schoetterl-Glausch  * * >0		- an access exception occurred. In this case the returned value
7767faa543dSJanis Schoetterl-Glausch  *		  is the program interruption code and the contents of pgm may
7777faa543dSJanis Schoetterl-Glausch  *		  be used to inject an exception into the guest.
7787faa543dSJanis Schoetterl-Glausch  */
guest_range_to_gpas(struct kvm_vcpu * vcpu,unsigned long ga,u8 ar,unsigned long * gpas,unsigned long len,const union asce asce,enum gacc_mode mode,u8 access_key)7797faa543dSJanis Schoetterl-Glausch static int guest_range_to_gpas(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar,
7807faa543dSJanis Schoetterl-Glausch 			       unsigned long *gpas, unsigned long len,
781e613d834SJanis Schoetterl-Glausch 			       const union asce asce, enum gacc_mode mode,
782e613d834SJanis Schoetterl-Glausch 			       u8 access_key)
78322938978SHeiko Carstens {
78422938978SHeiko Carstens 	psw_t *psw = &vcpu->arch.sie_block->gpsw;
7857faa543dSJanis Schoetterl-Glausch 	unsigned int offset = offset_in_page(ga);
7867faa543dSJanis Schoetterl-Glausch 	unsigned int fragment_len;
787cde0dcfbSDavid Hildenbrand 	int lap_enabled, rc = 0;
7886ae1574cSChristian Borntraeger 	enum prot_type prot;
7897faa543dSJanis Schoetterl-Glausch 	unsigned long gpa;
79022938978SHeiko Carstens 
79175a18122SAlexander Yarygin 	lap_enabled = low_address_protection_enabled(vcpu, asce);
7927faa543dSJanis Schoetterl-Glausch 	while (min(PAGE_SIZE - offset, len) > 0) {
7937faa543dSJanis Schoetterl-Glausch 		fragment_len = min(PAGE_SIZE - offset, len);
79422938978SHeiko Carstens 		ga = kvm_s390_logical_to_effective(vcpu, ga);
795cde0dcfbSDavid Hildenbrand 		if (mode == GACC_STORE && lap_enabled && is_low_address(ga))
796cde0dcfbSDavid Hildenbrand 			return trans_exc(vcpu, PGM_PROTECTION, ga, ar, mode,
797cde0dcfbSDavid Hildenbrand 					 PROT_TYPE_LA);
798a7525982SHeiko Carstens 		if (psw_bits(*psw).dat) {
7997faa543dSJanis Schoetterl-Glausch 			rc = guest_translate(vcpu, ga, &gpa, asce, mode, &prot);
80022938978SHeiko Carstens 			if (rc < 0)
80122938978SHeiko Carstens 				return rc;
80222938978SHeiko Carstens 		} else {
8037faa543dSJanis Schoetterl-Glausch 			gpa = kvm_s390_real_to_abs(vcpu, ga);
8049e7325acSSean Christopherson 			if (!kvm_is_gpa_in_memslot(vcpu->kvm, gpa)) {
805cde0dcfbSDavid Hildenbrand 				rc = PGM_ADDRESSING;
806b3cefd6bSJanis Schoetterl-Glausch 				prot = PROT_NONE;
807b3cefd6bSJanis Schoetterl-Glausch 			}
80822938978SHeiko Carstens 		}
809cde0dcfbSDavid Hildenbrand 		if (rc)
8106ae1574cSChristian Borntraeger 			return trans_exc(vcpu, rc, ga, ar, mode, prot);
811e613d834SJanis Schoetterl-Glausch 		rc = vcpu_check_access_key(vcpu, access_key, mode, asce, gpa, ga,
812e613d834SJanis Schoetterl-Glausch 					   fragment_len);
813e613d834SJanis Schoetterl-Glausch 		if (rc)
814e613d834SJanis Schoetterl-Glausch 			return trans_exc(vcpu, rc, ga, ar, mode, PROT_TYPE_KEYC);
8157faa543dSJanis Schoetterl-Glausch 		if (gpas)
8167faa543dSJanis Schoetterl-Glausch 			*gpas++ = gpa;
8177faa543dSJanis Schoetterl-Glausch 		offset = 0;
8187faa543dSJanis Schoetterl-Glausch 		ga += fragment_len;
8197faa543dSJanis Schoetterl-Glausch 		len -= fragment_len;
82022938978SHeiko Carstens 	}
82122938978SHeiko Carstens 	return 0;
82222938978SHeiko Carstens }
82322938978SHeiko Carstens 
access_guest_page(struct kvm * kvm,enum gacc_mode mode,gpa_t gpa,void * data,unsigned int len)824bad13799SJanis Schoetterl-Glausch static int access_guest_page(struct kvm *kvm, enum gacc_mode mode, gpa_t gpa,
825bad13799SJanis Schoetterl-Glausch 			     void *data, unsigned int len)
826bad13799SJanis Schoetterl-Glausch {
827bad13799SJanis Schoetterl-Glausch 	const unsigned int offset = offset_in_page(gpa);
828bad13799SJanis Schoetterl-Glausch 	const gfn_t gfn = gpa_to_gfn(gpa);
829bad13799SJanis Schoetterl-Glausch 	int rc;
830bad13799SJanis Schoetterl-Glausch 
831bad13799SJanis Schoetterl-Glausch 	if (mode == GACC_STORE)
832bad13799SJanis Schoetterl-Glausch 		rc = kvm_write_guest_page(kvm, gfn, data, offset, len);
833bad13799SJanis Schoetterl-Glausch 	else
834bad13799SJanis Schoetterl-Glausch 		rc = kvm_read_guest_page(kvm, gfn, data, offset, len);
835bad13799SJanis Schoetterl-Glausch 	return rc;
836bad13799SJanis Schoetterl-Glausch }
837bad13799SJanis Schoetterl-Glausch 
838e613d834SJanis Schoetterl-Glausch static int
access_guest_page_with_key(struct kvm * kvm,enum gacc_mode mode,gpa_t gpa,void * data,unsigned int len,u8 access_key)839e613d834SJanis Schoetterl-Glausch access_guest_page_with_key(struct kvm *kvm, enum gacc_mode mode, gpa_t gpa,
840e613d834SJanis Schoetterl-Glausch 			   void *data, unsigned int len, u8 access_key)
841e613d834SJanis Schoetterl-Glausch {
842e613d834SJanis Schoetterl-Glausch 	struct kvm_memory_slot *slot;
843e613d834SJanis Schoetterl-Glausch 	bool writable;
844e613d834SJanis Schoetterl-Glausch 	gfn_t gfn;
845e613d834SJanis Schoetterl-Glausch 	hva_t hva;
846e613d834SJanis Schoetterl-Glausch 	int rc;
847e613d834SJanis Schoetterl-Glausch 
848e613d834SJanis Schoetterl-Glausch 	gfn = gpa >> PAGE_SHIFT;
849e613d834SJanis Schoetterl-Glausch 	slot = gfn_to_memslot(kvm, gfn);
850e613d834SJanis Schoetterl-Glausch 	hva = gfn_to_hva_memslot_prot(slot, gfn, &writable);
851e613d834SJanis Schoetterl-Glausch 
852e613d834SJanis Schoetterl-Glausch 	if (kvm_is_error_hva(hva))
853e613d834SJanis Schoetterl-Glausch 		return PGM_ADDRESSING;
854e613d834SJanis Schoetterl-Glausch 	/*
855e613d834SJanis Schoetterl-Glausch 	 * Check if it's a ro memslot, even tho that can't occur (they're unsupported).
856e613d834SJanis Schoetterl-Glausch 	 * Don't try to actually handle that case.
857e613d834SJanis Schoetterl-Glausch 	 */
858e613d834SJanis Schoetterl-Glausch 	if (!writable && mode == GACC_STORE)
859e613d834SJanis Schoetterl-Glausch 		return -EOPNOTSUPP;
860e613d834SJanis Schoetterl-Glausch 	hva += offset_in_page(gpa);
861e613d834SJanis Schoetterl-Glausch 	if (mode == GACC_STORE)
862e613d834SJanis Schoetterl-Glausch 		rc = copy_to_user_key((void __user *)hva, data, len, access_key);
863e613d834SJanis Schoetterl-Glausch 	else
864e613d834SJanis Schoetterl-Glausch 		rc = copy_from_user_key(data, (void __user *)hva, len, access_key);
865e613d834SJanis Schoetterl-Glausch 	if (rc)
866e613d834SJanis Schoetterl-Glausch 		return PGM_PROTECTION;
867e613d834SJanis Schoetterl-Glausch 	if (mode == GACC_STORE)
868e613d834SJanis Schoetterl-Glausch 		mark_page_dirty_in_slot(kvm, slot, gfn);
869e613d834SJanis Schoetterl-Glausch 	return 0;
870e613d834SJanis Schoetterl-Glausch }
871e613d834SJanis Schoetterl-Glausch 
access_guest_abs_with_key(struct kvm * kvm,gpa_t gpa,void * data,unsigned long len,enum gacc_mode mode,u8 access_key)872ef11c946SJanis Schoetterl-Glausch int access_guest_abs_with_key(struct kvm *kvm, gpa_t gpa, void *data,
873ef11c946SJanis Schoetterl-Glausch 			      unsigned long len, enum gacc_mode mode, u8 access_key)
874ef11c946SJanis Schoetterl-Glausch {
875ef11c946SJanis Schoetterl-Glausch 	int offset = offset_in_page(gpa);
876ef11c946SJanis Schoetterl-Glausch 	int fragment_len;
877ef11c946SJanis Schoetterl-Glausch 	int rc;
878ef11c946SJanis Schoetterl-Glausch 
879ef11c946SJanis Schoetterl-Glausch 	while (min(PAGE_SIZE - offset, len) > 0) {
880ef11c946SJanis Schoetterl-Glausch 		fragment_len = min(PAGE_SIZE - offset, len);
881ef11c946SJanis Schoetterl-Glausch 		rc = access_guest_page_with_key(kvm, mode, gpa, data, fragment_len, access_key);
882ef11c946SJanis Schoetterl-Glausch 		if (rc)
883ef11c946SJanis Schoetterl-Glausch 			return rc;
884ef11c946SJanis Schoetterl-Glausch 		offset = 0;
885ef11c946SJanis Schoetterl-Glausch 		len -= fragment_len;
886ef11c946SJanis Schoetterl-Glausch 		data += fragment_len;
887ef11c946SJanis Schoetterl-Glausch 		gpa += fragment_len;
888ef11c946SJanis Schoetterl-Glausch 	}
889ef11c946SJanis Schoetterl-Glausch 	return 0;
890ef11c946SJanis Schoetterl-Glausch }
891ef11c946SJanis Schoetterl-Glausch 
access_guest_with_key(struct kvm_vcpu * vcpu,unsigned long ga,u8 ar,void * data,unsigned long len,enum gacc_mode mode,u8 access_key)892e613d834SJanis Schoetterl-Glausch int access_guest_with_key(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar,
893e613d834SJanis Schoetterl-Glausch 			  void *data, unsigned long len, enum gacc_mode mode,
894e613d834SJanis Schoetterl-Glausch 			  u8 access_key)
89522938978SHeiko Carstens {
89622938978SHeiko Carstens 	psw_t *psw = &vcpu->arch.sie_block->gpsw;
8977faa543dSJanis Schoetterl-Glausch 	unsigned long nr_pages, idx;
8987faa543dSJanis Schoetterl-Glausch 	unsigned long gpa_array[2];
899416e7f0cSJanis Schoetterl-Glausch 	unsigned int fragment_len;
9007faa543dSJanis Schoetterl-Glausch 	unsigned long *gpas;
901e613d834SJanis Schoetterl-Glausch 	enum prot_type prot;
9028a242234SHeiko Carstens 	int need_ipte_lock;
9038a242234SHeiko Carstens 	union asce asce;
904e613d834SJanis Schoetterl-Glausch 	bool try_storage_prot_override;
905e613d834SJanis Schoetterl-Glausch 	bool try_fetch_prot_override;
90622938978SHeiko Carstens 	int rc;
90722938978SHeiko Carstens 
90822938978SHeiko Carstens 	if (!len)
90922938978SHeiko Carstens 		return 0;
9106167375bSDavid Hildenbrand 	ga = kvm_s390_logical_to_effective(vcpu, ga);
9116167375bSDavid Hildenbrand 	rc = get_vcpu_asce(vcpu, &asce, ga, ar, mode);
912664b4973SAlexander Yarygin 	if (rc)
913664b4973SAlexander Yarygin 		return rc;
91422938978SHeiko Carstens 	nr_pages = (((ga & ~PAGE_MASK) + len - 1) >> PAGE_SHIFT) + 1;
9157faa543dSJanis Schoetterl-Glausch 	gpas = gpa_array;
9167faa543dSJanis Schoetterl-Glausch 	if (nr_pages > ARRAY_SIZE(gpa_array))
9177faa543dSJanis Schoetterl-Glausch 		gpas = vmalloc(array_size(nr_pages, sizeof(unsigned long)));
9187faa543dSJanis Schoetterl-Glausch 	if (!gpas)
91922938978SHeiko Carstens 		return -ENOMEM;
920e613d834SJanis Schoetterl-Glausch 	try_fetch_prot_override = fetch_prot_override_applicable(vcpu, mode, asce);
921e613d834SJanis Schoetterl-Glausch 	try_storage_prot_override = storage_prot_override_applicable(vcpu);
922a7525982SHeiko Carstens 	need_ipte_lock = psw_bits(*psw).dat && !asce.r;
9238a242234SHeiko Carstens 	if (need_ipte_lock)
9240130337eSPierre Morel 		ipte_lock(vcpu->kvm);
925e613d834SJanis Schoetterl-Glausch 	/*
926e613d834SJanis Schoetterl-Glausch 	 * Since we do the access further down ultimately via a move instruction
927e613d834SJanis Schoetterl-Glausch 	 * that does key checking and returns an error in case of a protection
928e613d834SJanis Schoetterl-Glausch 	 * violation, we don't need to do the check during address translation.
929e613d834SJanis Schoetterl-Glausch 	 * Skip it by passing access key 0, which matches any storage key,
930e613d834SJanis Schoetterl-Glausch 	 * obviating the need for any further checks. As a result the check is
931e613d834SJanis Schoetterl-Glausch 	 * handled entirely in hardware on access, we only need to take care to
932e613d834SJanis Schoetterl-Glausch 	 * forego key protection checking if fetch protection override applies or
933e613d834SJanis Schoetterl-Glausch 	 * retry with the special key 9 in case of storage protection override.
934e613d834SJanis Schoetterl-Glausch 	 */
935e613d834SJanis Schoetterl-Glausch 	rc = guest_range_to_gpas(vcpu, ga, ar, gpas, len, asce, mode, 0);
936e613d834SJanis Schoetterl-Glausch 	if (rc)
937e613d834SJanis Schoetterl-Glausch 		goto out_unlock;
938e613d834SJanis Schoetterl-Glausch 	for (idx = 0; idx < nr_pages; idx++) {
9397faa543dSJanis Schoetterl-Glausch 		fragment_len = min(PAGE_SIZE - offset_in_page(gpas[idx]), len);
940e613d834SJanis Schoetterl-Glausch 		if (try_fetch_prot_override && fetch_prot_override_applies(ga, fragment_len)) {
941e613d834SJanis Schoetterl-Glausch 			rc = access_guest_page(vcpu->kvm, mode, gpas[idx],
942e613d834SJanis Schoetterl-Glausch 					       data, fragment_len);
943e613d834SJanis Schoetterl-Glausch 		} else {
944e613d834SJanis Schoetterl-Glausch 			rc = access_guest_page_with_key(vcpu->kvm, mode, gpas[idx],
945e613d834SJanis Schoetterl-Glausch 							data, fragment_len, access_key);
946e613d834SJanis Schoetterl-Glausch 		}
947e613d834SJanis Schoetterl-Glausch 		if (rc == PGM_PROTECTION && try_storage_prot_override)
948e613d834SJanis Schoetterl-Glausch 			rc = access_guest_page_with_key(vcpu->kvm, mode, gpas[idx],
949e613d834SJanis Schoetterl-Glausch 							data, fragment_len, PAGE_SPO_ACC);
950e613d834SJanis Schoetterl-Glausch 		if (rc)
951e613d834SJanis Schoetterl-Glausch 			break;
952416e7f0cSJanis Schoetterl-Glausch 		len -= fragment_len;
953416e7f0cSJanis Schoetterl-Glausch 		data += fragment_len;
954e613d834SJanis Schoetterl-Glausch 		ga = kvm_s390_logical_to_effective(vcpu, ga + fragment_len);
95522938978SHeiko Carstens 	}
956c783631bSJanis Schoetterl-Glausch 	if (rc > 0) {
957c783631bSJanis Schoetterl-Glausch 		bool terminate = (mode == GACC_STORE) && (idx > 0);
958c783631bSJanis Schoetterl-Glausch 
959b3cefd6bSJanis Schoetterl-Glausch 		if (rc == PGM_PROTECTION)
960b3cefd6bSJanis Schoetterl-Glausch 			prot = PROT_TYPE_KEYC;
961b3cefd6bSJanis Schoetterl-Glausch 		else
962b3cefd6bSJanis Schoetterl-Glausch 			prot = PROT_NONE;
963c783631bSJanis Schoetterl-Glausch 		rc = trans_exc_ending(vcpu, rc, ga, ar, mode, prot, terminate);
964c783631bSJanis Schoetterl-Glausch 	}
965e613d834SJanis Schoetterl-Glausch out_unlock:
9668a242234SHeiko Carstens 	if (need_ipte_lock)
9670130337eSPierre Morel 		ipte_unlock(vcpu->kvm);
9687faa543dSJanis Schoetterl-Glausch 	if (nr_pages > ARRAY_SIZE(gpa_array))
9697faa543dSJanis Schoetterl-Glausch 		vfree(gpas);
97022938978SHeiko Carstens 	return rc;
97122938978SHeiko Carstens }
97222938978SHeiko Carstens 
access_guest_real(struct kvm_vcpu * vcpu,unsigned long gra,void * data,unsigned long len,enum gacc_mode mode)97322938978SHeiko Carstens int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
97492c96321SDavid Hildenbrand 		      void *data, unsigned long len, enum gacc_mode mode)
97522938978SHeiko Carstens {
976416e7f0cSJanis Schoetterl-Glausch 	unsigned int fragment_len;
977416e7f0cSJanis Schoetterl-Glausch 	unsigned long gpa;
97822938978SHeiko Carstens 	int rc = 0;
97922938978SHeiko Carstens 
98022938978SHeiko Carstens 	while (len && !rc) {
98122938978SHeiko Carstens 		gpa = kvm_s390_real_to_abs(vcpu, gra);
982416e7f0cSJanis Schoetterl-Glausch 		fragment_len = min(PAGE_SIZE - offset_in_page(gpa), len);
983bad13799SJanis Schoetterl-Glausch 		rc = access_guest_page(vcpu->kvm, mode, gpa, data, fragment_len);
984416e7f0cSJanis Schoetterl-Glausch 		len -= fragment_len;
985416e7f0cSJanis Schoetterl-Glausch 		gra += fragment_len;
986416e7f0cSJanis Schoetterl-Glausch 		data += fragment_len;
98722938978SHeiko Carstens 	}
98822938978SHeiko Carstens 	return rc;
98922938978SHeiko Carstens }
990f8232c8cSThomas Huth 
991f8232c8cSThomas Huth /**
9923fd49805SJanis Schoetterl-Glausch  * cmpxchg_guest_abs_with_key() - Perform cmpxchg on guest absolute address.
9933fd49805SJanis Schoetterl-Glausch  * @kvm: Virtual machine instance.
9943fd49805SJanis Schoetterl-Glausch  * @gpa: Absolute guest address of the location to be changed.
9953fd49805SJanis Schoetterl-Glausch  * @len: Operand length of the cmpxchg, required: 1 <= len <= 16. Providing a
9963fd49805SJanis Schoetterl-Glausch  *       non power of two will result in failure.
9973fd49805SJanis Schoetterl-Glausch  * @old_addr: Pointer to old value. If the location at @gpa contains this value,
9983fd49805SJanis Schoetterl-Glausch  *            the exchange will succeed. After calling cmpxchg_guest_abs_with_key()
9993fd49805SJanis Schoetterl-Glausch  *            *@old_addr contains the value at @gpa before the attempt to
10003fd49805SJanis Schoetterl-Glausch  *            exchange the value.
10013fd49805SJanis Schoetterl-Glausch  * @new: The value to place at @gpa.
10023fd49805SJanis Schoetterl-Glausch  * @access_key: The access key to use for the guest access.
10033fd49805SJanis Schoetterl-Glausch  * @success: output value indicating if an exchange occurred.
10043fd49805SJanis Schoetterl-Glausch  *
10053fd49805SJanis Schoetterl-Glausch  * Atomically exchange the value at @gpa by @new, if it contains *@old.
10063fd49805SJanis Schoetterl-Glausch  * Honors storage keys.
10073fd49805SJanis Schoetterl-Glausch  *
10083fd49805SJanis Schoetterl-Glausch  * Return: * 0: successful exchange
10093fd49805SJanis Schoetterl-Glausch  *         * >0: a program interruption code indicating the reason cmpxchg could
10103fd49805SJanis Schoetterl-Glausch  *               not be attempted
10113fd49805SJanis Schoetterl-Glausch  *         * -EINVAL: address misaligned or len not power of two
10123fd49805SJanis Schoetterl-Glausch  *         * -EAGAIN: transient failure (len 1 or 2)
10133fd49805SJanis Schoetterl-Glausch  *         * -EOPNOTSUPP: read-only memslot (should never occur)
10143fd49805SJanis Schoetterl-Glausch  */
cmpxchg_guest_abs_with_key(struct kvm * kvm,gpa_t gpa,int len,__uint128_t * old_addr,__uint128_t new,u8 access_key,bool * success)10153fd49805SJanis Schoetterl-Glausch int cmpxchg_guest_abs_with_key(struct kvm *kvm, gpa_t gpa, int len,
10163fd49805SJanis Schoetterl-Glausch 			       __uint128_t *old_addr, __uint128_t new,
10173fd49805SJanis Schoetterl-Glausch 			       u8 access_key, bool *success)
10183fd49805SJanis Schoetterl-Glausch {
10193fd49805SJanis Schoetterl-Glausch 	gfn_t gfn = gpa_to_gfn(gpa);
10203fd49805SJanis Schoetterl-Glausch 	struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn);
10213fd49805SJanis Schoetterl-Glausch 	bool writable;
10223fd49805SJanis Schoetterl-Glausch 	hva_t hva;
10233fd49805SJanis Schoetterl-Glausch 	int ret;
10243fd49805SJanis Schoetterl-Glausch 
10253fd49805SJanis Schoetterl-Glausch 	if (!IS_ALIGNED(gpa, len))
10263fd49805SJanis Schoetterl-Glausch 		return -EINVAL;
10273fd49805SJanis Schoetterl-Glausch 
10283fd49805SJanis Schoetterl-Glausch 	hva = gfn_to_hva_memslot_prot(slot, gfn, &writable);
10293fd49805SJanis Schoetterl-Glausch 	if (kvm_is_error_hva(hva))
10303fd49805SJanis Schoetterl-Glausch 		return PGM_ADDRESSING;
10313fd49805SJanis Schoetterl-Glausch 	/*
10323fd49805SJanis Schoetterl-Glausch 	 * Check if it's a read-only memslot, even though that cannot occur
10333fd49805SJanis Schoetterl-Glausch 	 * since those are unsupported.
10343fd49805SJanis Schoetterl-Glausch 	 * Don't try to actually handle that case.
10353fd49805SJanis Schoetterl-Glausch 	 */
10363fd49805SJanis Schoetterl-Glausch 	if (!writable)
10373fd49805SJanis Schoetterl-Glausch 		return -EOPNOTSUPP;
10383fd49805SJanis Schoetterl-Glausch 
10393fd49805SJanis Schoetterl-Glausch 	hva += offset_in_page(gpa);
10403fd49805SJanis Schoetterl-Glausch 	/*
10413fd49805SJanis Schoetterl-Glausch 	 * The cmpxchg_user_key macro depends on the type of "old", so we need
10423fd49805SJanis Schoetterl-Glausch 	 * a case for each valid length and get some code duplication as long
10433fd49805SJanis Schoetterl-Glausch 	 * as we don't introduce a new macro.
10443fd49805SJanis Schoetterl-Glausch 	 */
10453fd49805SJanis Schoetterl-Glausch 	switch (len) {
10463fd49805SJanis Schoetterl-Glausch 	case 1: {
10473fd49805SJanis Schoetterl-Glausch 		u8 old;
10483fd49805SJanis Schoetterl-Glausch 
10493fd49805SJanis Schoetterl-Glausch 		ret = cmpxchg_user_key((u8 __user *)hva, &old, *old_addr, new, access_key);
10503fd49805SJanis Schoetterl-Glausch 		*success = !ret && old == *old_addr;
10513fd49805SJanis Schoetterl-Glausch 		*old_addr = old;
10523fd49805SJanis Schoetterl-Glausch 		break;
10533fd49805SJanis Schoetterl-Glausch 	}
10543fd49805SJanis Schoetterl-Glausch 	case 2: {
10553fd49805SJanis Schoetterl-Glausch 		u16 old;
10563fd49805SJanis Schoetterl-Glausch 
10573fd49805SJanis Schoetterl-Glausch 		ret = cmpxchg_user_key((u16 __user *)hva, &old, *old_addr, new, access_key);
10583fd49805SJanis Schoetterl-Glausch 		*success = !ret && old == *old_addr;
10593fd49805SJanis Schoetterl-Glausch 		*old_addr = old;
10603fd49805SJanis Schoetterl-Glausch 		break;
10613fd49805SJanis Schoetterl-Glausch 	}
10623fd49805SJanis Schoetterl-Glausch 	case 4: {
10633fd49805SJanis Schoetterl-Glausch 		u32 old;
10643fd49805SJanis Schoetterl-Glausch 
10653fd49805SJanis Schoetterl-Glausch 		ret = cmpxchg_user_key((u32 __user *)hva, &old, *old_addr, new, access_key);
10663fd49805SJanis Schoetterl-Glausch 		*success = !ret && old == *old_addr;
10673fd49805SJanis Schoetterl-Glausch 		*old_addr = old;
10683fd49805SJanis Schoetterl-Glausch 		break;
10693fd49805SJanis Schoetterl-Glausch 	}
10703fd49805SJanis Schoetterl-Glausch 	case 8: {
10713fd49805SJanis Schoetterl-Glausch 		u64 old;
10723fd49805SJanis Schoetterl-Glausch 
10733fd49805SJanis Schoetterl-Glausch 		ret = cmpxchg_user_key((u64 __user *)hva, &old, *old_addr, new, access_key);
10743fd49805SJanis Schoetterl-Glausch 		*success = !ret && old == *old_addr;
10753fd49805SJanis Schoetterl-Glausch 		*old_addr = old;
10763fd49805SJanis Schoetterl-Glausch 		break;
10773fd49805SJanis Schoetterl-Glausch 	}
10783fd49805SJanis Schoetterl-Glausch 	case 16: {
10793fd49805SJanis Schoetterl-Glausch 		__uint128_t old;
10803fd49805SJanis Schoetterl-Glausch 
10813fd49805SJanis Schoetterl-Glausch 		ret = cmpxchg_user_key((__uint128_t __user *)hva, &old, *old_addr, new, access_key);
10823fd49805SJanis Schoetterl-Glausch 		*success = !ret && old == *old_addr;
10833fd49805SJanis Schoetterl-Glausch 		*old_addr = old;
10843fd49805SJanis Schoetterl-Glausch 		break;
10853fd49805SJanis Schoetterl-Glausch 	}
10863fd49805SJanis Schoetterl-Glausch 	default:
10873fd49805SJanis Schoetterl-Glausch 		return -EINVAL;
10883fd49805SJanis Schoetterl-Glausch 	}
10893fd49805SJanis Schoetterl-Glausch 	if (*success)
10903fd49805SJanis Schoetterl-Glausch 		mark_page_dirty_in_slot(kvm, slot, gfn);
10913fd49805SJanis Schoetterl-Glausch 	/*
10923fd49805SJanis Schoetterl-Glausch 	 * Assume that the fault is caused by protection, either key protection
10933fd49805SJanis Schoetterl-Glausch 	 * or user page write protection.
10943fd49805SJanis Schoetterl-Glausch 	 */
10953fd49805SJanis Schoetterl-Glausch 	if (ret == -EFAULT)
10963fd49805SJanis Schoetterl-Glausch 		ret = PGM_PROTECTION;
10973fd49805SJanis Schoetterl-Glausch 	return ret;
10983fd49805SJanis Schoetterl-Glausch }
10993fd49805SJanis Schoetterl-Glausch 
11003fd49805SJanis Schoetterl-Glausch /**
1101e613d834SJanis Schoetterl-Glausch  * guest_translate_address_with_key - translate guest logical into guest absolute address
110225b5476aSJanosch Frank  * @vcpu: virtual cpu
110325b5476aSJanosch Frank  * @gva: Guest virtual address
110425b5476aSJanosch Frank  * @ar: Access register
110525b5476aSJanosch Frank  * @gpa: Guest physical address
110625b5476aSJanosch Frank  * @mode: Translation access mode
1107e613d834SJanis Schoetterl-Glausch  * @access_key: access key to mach the storage key with
11089fbc0276SThomas Huth  *
11099fbc0276SThomas Huth  * Parameter semantics are the same as the ones from guest_translate.
11109fbc0276SThomas Huth  * The memory contents at the guest address are not changed.
11119fbc0276SThomas Huth  *
11129fbc0276SThomas Huth  * Note: The IPTE lock is not taken during this function, so the caller
11139fbc0276SThomas Huth  * has to take care of this.
11149fbc0276SThomas Huth  */
guest_translate_address_with_key(struct kvm_vcpu * vcpu,unsigned long gva,u8 ar,unsigned long * gpa,enum gacc_mode mode,u8 access_key)1115e613d834SJanis Schoetterl-Glausch int guest_translate_address_with_key(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
1116e613d834SJanis Schoetterl-Glausch 				     unsigned long *gpa, enum gacc_mode mode,
1117e613d834SJanis Schoetterl-Glausch 				     u8 access_key)
11189fbc0276SThomas Huth {
11199fbc0276SThomas Huth 	union asce asce;
11209fbc0276SThomas Huth 	int rc;
11219fbc0276SThomas Huth 
11229fbc0276SThomas Huth 	gva = kvm_s390_logical_to_effective(vcpu, gva);
11236167375bSDavid Hildenbrand 	rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode);
1124664b4973SAlexander Yarygin 	if (rc)
1125664b4973SAlexander Yarygin 		return rc;
1126e613d834SJanis Schoetterl-Glausch 	return guest_range_to_gpas(vcpu, gva, ar, gpa, 1, asce, mode,
1127e613d834SJanis Schoetterl-Glausch 				   access_key);
1128e613d834SJanis Schoetterl-Glausch }
1129e613d834SJanis Schoetterl-Glausch 
11309fbc0276SThomas Huth /**
113141408c28SThomas Huth  * check_gva_range - test a range of guest virtual addresses for accessibility
113225b5476aSJanosch Frank  * @vcpu: virtual cpu
113325b5476aSJanosch Frank  * @gva: Guest virtual address
113425b5476aSJanosch Frank  * @ar: Access register
113525b5476aSJanosch Frank  * @length: Length of test range
113625b5476aSJanosch Frank  * @mode: Translation access mode
1137e613d834SJanis Schoetterl-Glausch  * @access_key: access key to mach the storage keys with
113841408c28SThomas Huth  */
check_gva_range(struct kvm_vcpu * vcpu,unsigned long gva,u8 ar,unsigned long length,enum gacc_mode mode,u8 access_key)113927f67f87SChristian Borntraeger int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
1140e613d834SJanis Schoetterl-Glausch 		    unsigned long length, enum gacc_mode mode, u8 access_key)
114141408c28SThomas Huth {
11427faa543dSJanis Schoetterl-Glausch 	union asce asce;
114341408c28SThomas Huth 	int rc = 0;
114441408c28SThomas Huth 
11457faa543dSJanis Schoetterl-Glausch 	rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode);
11467faa543dSJanis Schoetterl-Glausch 	if (rc)
11477faa543dSJanis Schoetterl-Glausch 		return rc;
11480130337eSPierre Morel 	ipte_lock(vcpu->kvm);
1149e613d834SJanis Schoetterl-Glausch 	rc = guest_range_to_gpas(vcpu, gva, ar, NULL, length, asce, mode,
1150e613d834SJanis Schoetterl-Glausch 				 access_key);
11510130337eSPierre Morel 	ipte_unlock(vcpu->kvm);
115241408c28SThomas Huth 
115341408c28SThomas Huth 	return rc;
115441408c28SThomas Huth }
115541408c28SThomas Huth 
115641408c28SThomas Huth /**
1157ef11c946SJanis Schoetterl-Glausch  * check_gpa_range - test a range of guest physical addresses for accessibility
1158ef11c946SJanis Schoetterl-Glausch  * @kvm: virtual machine instance
1159ef11c946SJanis Schoetterl-Glausch  * @gpa: guest physical address
1160ef11c946SJanis Schoetterl-Glausch  * @length: length of test range
1161ef11c946SJanis Schoetterl-Glausch  * @mode: access mode to test, relevant for storage keys
1162ef11c946SJanis Schoetterl-Glausch  * @access_key: access key to mach the storage keys with
1163ef11c946SJanis Schoetterl-Glausch  */
check_gpa_range(struct kvm * kvm,unsigned long gpa,unsigned long length,enum gacc_mode mode,u8 access_key)1164ef11c946SJanis Schoetterl-Glausch int check_gpa_range(struct kvm *kvm, unsigned long gpa, unsigned long length,
1165ef11c946SJanis Schoetterl-Glausch 		    enum gacc_mode mode, u8 access_key)
1166ef11c946SJanis Schoetterl-Glausch {
1167ef11c946SJanis Schoetterl-Glausch 	unsigned int fragment_len;
1168ef11c946SJanis Schoetterl-Glausch 	int rc = 0;
1169ef11c946SJanis Schoetterl-Glausch 
1170ef11c946SJanis Schoetterl-Glausch 	while (length && !rc) {
1171ef11c946SJanis Schoetterl-Glausch 		fragment_len = min(PAGE_SIZE - offset_in_page(gpa), length);
1172ef11c946SJanis Schoetterl-Glausch 		rc = vm_check_access_key(kvm, access_key, mode, gpa);
1173ef11c946SJanis Schoetterl-Glausch 		length -= fragment_len;
1174ef11c946SJanis Schoetterl-Glausch 		gpa += fragment_len;
1175ef11c946SJanis Schoetterl-Glausch 	}
1176ef11c946SJanis Schoetterl-Glausch 	return rc;
1177ef11c946SJanis Schoetterl-Glausch }
1178ef11c946SJanis Schoetterl-Glausch 
1179ef11c946SJanis Schoetterl-Glausch /**
1180dd9e5b7bSAlexander Yarygin  * kvm_s390_check_low_addr_prot_real - check for low-address protection
118125b5476aSJanosch Frank  * @vcpu: virtual cpu
1182dd9e5b7bSAlexander Yarygin  * @gra: Guest real address
1183f8232c8cSThomas Huth  *
1184f8232c8cSThomas Huth  * Checks whether an address is subject to low-address protection and set
1185f8232c8cSThomas Huth  * up vcpu->arch.pgm accordingly if necessary.
1186f8232c8cSThomas Huth  *
1187f8232c8cSThomas Huth  * Return: 0 if no protection exception, or PGM_PROTECTION if protected.
1188f8232c8cSThomas Huth  */
kvm_s390_check_low_addr_prot_real(struct kvm_vcpu * vcpu,unsigned long gra)1189dd9e5b7bSAlexander Yarygin int kvm_s390_check_low_addr_prot_real(struct kvm_vcpu *vcpu, unsigned long gra)
1190f8232c8cSThomas Huth {
1191dd9e5b7bSAlexander Yarygin 	union ctlreg0 ctlreg0 = {.val = vcpu->arch.sie_block->gcr[0]};
1192f8232c8cSThomas Huth 
1193dd9e5b7bSAlexander Yarygin 	if (!ctlreg0.lap || !is_low_address(gra))
1194f8232c8cSThomas Huth 		return 0;
11953e3c67f6SDavid Hildenbrand 	return trans_exc(vcpu, PGM_PROTECTION, gra, 0, GACC_STORE, PROT_TYPE_LA);
1196f8232c8cSThomas Huth }
1197aa17aa57SMartin Schwidefsky 
1198aa17aa57SMartin Schwidefsky /**
1199aa17aa57SMartin Schwidefsky  * kvm_s390_shadow_tables - walk the guest page table and create shadow tables
1200aa17aa57SMartin Schwidefsky  * @sg: pointer to the shadow guest address space structure
1201aa17aa57SMartin Schwidefsky  * @saddr: faulting address in the shadow gmap
12025ac14bacSClaudio Imbrenda  * @pgt: pointer to the beginning of the page table for the given address if
12035ac14bacSClaudio Imbrenda  *	 successful (return value 0), or to the first invalid DAT entry in
12045ac14bacSClaudio Imbrenda  *	 case of exceptions (return value > 0)
120525b5476aSJanosch Frank  * @dat_protection: referenced memory is write protected
1206fd8d4e3aSDavid Hildenbrand  * @fake: pgt references contiguous guest memory block, not a pgtable
1207aa17aa57SMartin Schwidefsky  */
kvm_s390_shadow_tables(struct gmap * sg,unsigned long saddr,unsigned long * pgt,int * dat_protection,int * fake)1208aa17aa57SMartin Schwidefsky static int kvm_s390_shadow_tables(struct gmap *sg, unsigned long saddr,
1209fd8d4e3aSDavid Hildenbrand 				  unsigned long *pgt, int *dat_protection,
1210fd8d4e3aSDavid Hildenbrand 				  int *fake)
1211aa17aa57SMartin Schwidefsky {
1212c3235e2dSNico Boehr 	struct kvm *kvm;
1213aa17aa57SMartin Schwidefsky 	struct gmap *parent;
1214aa17aa57SMartin Schwidefsky 	union asce asce;
1215aa17aa57SMartin Schwidefsky 	union vaddress vaddr;
1216aa17aa57SMartin Schwidefsky 	unsigned long ptr;
1217aa17aa57SMartin Schwidefsky 	int rc;
1218aa17aa57SMartin Schwidefsky 
1219fd8d4e3aSDavid Hildenbrand 	*fake = 0;
12201c65781bSDavid Hildenbrand 	*dat_protection = 0;
1221c3235e2dSNico Boehr 	kvm = sg->private;
1222aa17aa57SMartin Schwidefsky 	parent = sg->parent;
1223aa17aa57SMartin Schwidefsky 	vaddr.addr = saddr;
1224aa17aa57SMartin Schwidefsky 	asce.val = sg->orig_asce;
1225*275d05ceSClaudio Imbrenda 	ptr = asce.rsto * PAGE_SIZE;
12263218f709SDavid Hildenbrand 	if (asce.r) {
12273218f709SDavid Hildenbrand 		*fake = 1;
1228addb63c1SHeiko Carstens 		ptr = 0;
12293218f709SDavid Hildenbrand 		asce.dt = ASCE_TYPE_REGION1;
12303218f709SDavid Hildenbrand 	}
1231aa17aa57SMartin Schwidefsky 	switch (asce.dt) {
1232aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION1:
1233addb63c1SHeiko Carstens 		if (vaddr.rfx01 > asce.tl && !*fake)
1234aa17aa57SMartin Schwidefsky 			return PGM_REGION_FIRST_TRANS;
1235aa17aa57SMartin Schwidefsky 		break;
1236aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION2:
1237aa17aa57SMartin Schwidefsky 		if (vaddr.rfx)
1238aa17aa57SMartin Schwidefsky 			return PGM_ASCE_TYPE;
1239aa17aa57SMartin Schwidefsky 		if (vaddr.rsx01 > asce.tl)
1240aa17aa57SMartin Schwidefsky 			return PGM_REGION_SECOND_TRANS;
1241aa17aa57SMartin Schwidefsky 		break;
1242aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION3:
1243aa17aa57SMartin Schwidefsky 		if (vaddr.rfx || vaddr.rsx)
1244aa17aa57SMartin Schwidefsky 			return PGM_ASCE_TYPE;
1245aa17aa57SMartin Schwidefsky 		if (vaddr.rtx01 > asce.tl)
1246aa17aa57SMartin Schwidefsky 			return PGM_REGION_THIRD_TRANS;
1247aa17aa57SMartin Schwidefsky 		break;
1248aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_SEGMENT:
1249aa17aa57SMartin Schwidefsky 		if (vaddr.rfx || vaddr.rsx || vaddr.rtx)
1250aa17aa57SMartin Schwidefsky 			return PGM_ASCE_TYPE;
1251aa17aa57SMartin Schwidefsky 		if (vaddr.sx01 > asce.tl)
1252aa17aa57SMartin Schwidefsky 			return PGM_SEGMENT_TRANSLATION;
1253aa17aa57SMartin Schwidefsky 		break;
1254aa17aa57SMartin Schwidefsky 	}
1255aa17aa57SMartin Schwidefsky 
1256aa17aa57SMartin Schwidefsky 	switch (asce.dt) {
1257aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION1: {
1258aa17aa57SMartin Schwidefsky 		union region1_table_entry rfte;
1259aa17aa57SMartin Schwidefsky 
12603218f709SDavid Hildenbrand 		if (*fake) {
126158cdf5ebSHeiko Carstens 			ptr += vaddr.rfx * _REGION1_SIZE;
12623218f709SDavid Hildenbrand 			rfte.val = ptr;
12633218f709SDavid Hildenbrand 			goto shadow_r2t;
12643218f709SDavid Hildenbrand 		}
12655ac14bacSClaudio Imbrenda 		*pgt = ptr + vaddr.rfx * 8;
1266aa17aa57SMartin Schwidefsky 		rc = gmap_read_table(parent, ptr + vaddr.rfx * 8, &rfte.val);
1267aa17aa57SMartin Schwidefsky 		if (rc)
1268aa17aa57SMartin Schwidefsky 			return rc;
1269aa17aa57SMartin Schwidefsky 		if (rfte.i)
1270aa17aa57SMartin Schwidefsky 			return PGM_REGION_FIRST_TRANS;
1271aa17aa57SMartin Schwidefsky 		if (rfte.tt != TABLE_TYPE_REGION1)
1272aa17aa57SMartin Schwidefsky 			return PGM_TRANSLATION_SPEC;
1273aa17aa57SMartin Schwidefsky 		if (vaddr.rsx01 < rfte.tf || vaddr.rsx01 > rfte.tl)
1274aa17aa57SMartin Schwidefsky 			return PGM_REGION_SECOND_TRANS;
12751c65781bSDavid Hildenbrand 		if (sg->edat_level >= 1)
12761c65781bSDavid Hildenbrand 			*dat_protection |= rfte.p;
127758cdf5ebSHeiko Carstens 		ptr = rfte.rto * PAGE_SIZE;
12783218f709SDavid Hildenbrand shadow_r2t:
12793218f709SDavid Hildenbrand 		rc = gmap_shadow_r2t(sg, saddr, rfte.val, *fake);
1280aa17aa57SMartin Schwidefsky 		if (rc)
1281aa17aa57SMartin Schwidefsky 			return rc;
1282c3235e2dSNico Boehr 		kvm->stat.gmap_shadow_r1_entry++;
12833b684a42SJoe Perches 	}
12843b684a42SJoe Perches 		fallthrough;
1285aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION2: {
1286aa17aa57SMartin Schwidefsky 		union region2_table_entry rste;
1287aa17aa57SMartin Schwidefsky 
12883218f709SDavid Hildenbrand 		if (*fake) {
128958cdf5ebSHeiko Carstens 			ptr += vaddr.rsx * _REGION2_SIZE;
12903218f709SDavid Hildenbrand 			rste.val = ptr;
12913218f709SDavid Hildenbrand 			goto shadow_r3t;
12923218f709SDavid Hildenbrand 		}
12935ac14bacSClaudio Imbrenda 		*pgt = ptr + vaddr.rsx * 8;
1294aa17aa57SMartin Schwidefsky 		rc = gmap_read_table(parent, ptr + vaddr.rsx * 8, &rste.val);
1295aa17aa57SMartin Schwidefsky 		if (rc)
1296aa17aa57SMartin Schwidefsky 			return rc;
1297aa17aa57SMartin Schwidefsky 		if (rste.i)
1298aa17aa57SMartin Schwidefsky 			return PGM_REGION_SECOND_TRANS;
1299aa17aa57SMartin Schwidefsky 		if (rste.tt != TABLE_TYPE_REGION2)
1300aa17aa57SMartin Schwidefsky 			return PGM_TRANSLATION_SPEC;
1301aa17aa57SMartin Schwidefsky 		if (vaddr.rtx01 < rste.tf || vaddr.rtx01 > rste.tl)
1302aa17aa57SMartin Schwidefsky 			return PGM_REGION_THIRD_TRANS;
13031c65781bSDavid Hildenbrand 		if (sg->edat_level >= 1)
13041c65781bSDavid Hildenbrand 			*dat_protection |= rste.p;
130558cdf5ebSHeiko Carstens 		ptr = rste.rto * PAGE_SIZE;
13063218f709SDavid Hildenbrand shadow_r3t:
13071c65781bSDavid Hildenbrand 		rste.p |= *dat_protection;
13083218f709SDavid Hildenbrand 		rc = gmap_shadow_r3t(sg, saddr, rste.val, *fake);
1309aa17aa57SMartin Schwidefsky 		if (rc)
1310aa17aa57SMartin Schwidefsky 			return rc;
1311c3235e2dSNico Boehr 		kvm->stat.gmap_shadow_r2_entry++;
13123b684a42SJoe Perches 	}
13133b684a42SJoe Perches 		fallthrough;
1314aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION3: {
1315aa17aa57SMartin Schwidefsky 		union region3_table_entry rtte;
1316aa17aa57SMartin Schwidefsky 
13173218f709SDavid Hildenbrand 		if (*fake) {
131858cdf5ebSHeiko Carstens 			ptr += vaddr.rtx * _REGION3_SIZE;
13193218f709SDavid Hildenbrand 			rtte.val = ptr;
13203218f709SDavid Hildenbrand 			goto shadow_sgt;
13213218f709SDavid Hildenbrand 		}
13225ac14bacSClaudio Imbrenda 		*pgt = ptr + vaddr.rtx * 8;
1323aa17aa57SMartin Schwidefsky 		rc = gmap_read_table(parent, ptr + vaddr.rtx * 8, &rtte.val);
1324aa17aa57SMartin Schwidefsky 		if (rc)
1325aa17aa57SMartin Schwidefsky 			return rc;
1326aa17aa57SMartin Schwidefsky 		if (rtte.i)
1327aa17aa57SMartin Schwidefsky 			return PGM_REGION_THIRD_TRANS;
1328aa17aa57SMartin Schwidefsky 		if (rtte.tt != TABLE_TYPE_REGION3)
1329aa17aa57SMartin Schwidefsky 			return PGM_TRANSLATION_SPEC;
133018b89809SDavid Hildenbrand 		if (rtte.cr && asce.p && sg->edat_level >= 2)
133118b89809SDavid Hildenbrand 			return PGM_TRANSLATION_SPEC;
133218b89809SDavid Hildenbrand 		if (rtte.fc && sg->edat_level >= 2) {
13331c65781bSDavid Hildenbrand 			*dat_protection |= rtte.fc0.p;
133418b89809SDavid Hildenbrand 			*fake = 1;
133558cdf5ebSHeiko Carstens 			ptr = rtte.fc1.rfaa * _REGION3_SIZE;
133618b89809SDavid Hildenbrand 			rtte.val = ptr;
133718b89809SDavid Hildenbrand 			goto shadow_sgt;
133818b89809SDavid Hildenbrand 		}
1339aa17aa57SMartin Schwidefsky 		if (vaddr.sx01 < rtte.fc0.tf || vaddr.sx01 > rtte.fc0.tl)
1340aa17aa57SMartin Schwidefsky 			return PGM_SEGMENT_TRANSLATION;
13411c65781bSDavid Hildenbrand 		if (sg->edat_level >= 1)
13421c65781bSDavid Hildenbrand 			*dat_protection |= rtte.fc0.p;
134358cdf5ebSHeiko Carstens 		ptr = rtte.fc0.sto * PAGE_SIZE;
134418b89809SDavid Hildenbrand shadow_sgt:
13451c65781bSDavid Hildenbrand 		rtte.fc0.p |= *dat_protection;
134618b89809SDavid Hildenbrand 		rc = gmap_shadow_sgt(sg, saddr, rtte.val, *fake);
1347aa17aa57SMartin Schwidefsky 		if (rc)
1348aa17aa57SMartin Schwidefsky 			return rc;
1349c3235e2dSNico Boehr 		kvm->stat.gmap_shadow_r3_entry++;
13503b684a42SJoe Perches 	}
13513b684a42SJoe Perches 		fallthrough;
1352aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_SEGMENT: {
1353aa17aa57SMartin Schwidefsky 		union segment_table_entry ste;
1354aa17aa57SMartin Schwidefsky 
135518b89809SDavid Hildenbrand 		if (*fake) {
135658cdf5ebSHeiko Carstens 			ptr += vaddr.sx * _SEGMENT_SIZE;
135718b89809SDavid Hildenbrand 			ste.val = ptr;
135818b89809SDavid Hildenbrand 			goto shadow_pgt;
135918b89809SDavid Hildenbrand 		}
13605ac14bacSClaudio Imbrenda 		*pgt = ptr + vaddr.sx * 8;
1361aa17aa57SMartin Schwidefsky 		rc = gmap_read_table(parent, ptr + vaddr.sx * 8, &ste.val);
1362aa17aa57SMartin Schwidefsky 		if (rc)
1363aa17aa57SMartin Schwidefsky 			return rc;
1364aa17aa57SMartin Schwidefsky 		if (ste.i)
1365aa17aa57SMartin Schwidefsky 			return PGM_SEGMENT_TRANSLATION;
1366aa17aa57SMartin Schwidefsky 		if (ste.tt != TABLE_TYPE_SEGMENT)
1367aa17aa57SMartin Schwidefsky 			return PGM_TRANSLATION_SPEC;
1368aa17aa57SMartin Schwidefsky 		if (ste.cs && asce.p)
1369aa17aa57SMartin Schwidefsky 			return PGM_TRANSLATION_SPEC;
13701c65781bSDavid Hildenbrand 		*dat_protection |= ste.fc0.p;
1371fd8d4e3aSDavid Hildenbrand 		if (ste.fc && sg->edat_level >= 1) {
1372fd8d4e3aSDavid Hildenbrand 			*fake = 1;
137358cdf5ebSHeiko Carstens 			ptr = ste.fc1.sfaa * _SEGMENT_SIZE;
1374fd8d4e3aSDavid Hildenbrand 			ste.val = ptr;
1375fd8d4e3aSDavid Hildenbrand 			goto shadow_pgt;
1376fd8d4e3aSDavid Hildenbrand 		}
137758cdf5ebSHeiko Carstens 		ptr = ste.fc0.pto * (PAGE_SIZE / 2);
1378fd8d4e3aSDavid Hildenbrand shadow_pgt:
13791c65781bSDavid Hildenbrand 		ste.fc0.p |= *dat_protection;
1380fd8d4e3aSDavid Hildenbrand 		rc = gmap_shadow_pgt(sg, saddr, ste.val, *fake);
1381aa17aa57SMartin Schwidefsky 		if (rc)
1382aa17aa57SMartin Schwidefsky 			return rc;
1383c3235e2dSNico Boehr 		kvm->stat.gmap_shadow_sg_entry++;
1384aa17aa57SMartin Schwidefsky 	}
1385aa17aa57SMartin Schwidefsky 	}
1386aa17aa57SMartin Schwidefsky 	/* Return the parent address of the page table */
1387aa17aa57SMartin Schwidefsky 	*pgt = ptr;
1388aa17aa57SMartin Schwidefsky 	return 0;
1389aa17aa57SMartin Schwidefsky }
1390aa17aa57SMartin Schwidefsky 
1391aa17aa57SMartin Schwidefsky /**
1392aa17aa57SMartin Schwidefsky  * kvm_s390_shadow_fault - handle fault on a shadow page table
1393f4debb40SDavid Hildenbrand  * @vcpu: virtual cpu
1394aa17aa57SMartin Schwidefsky  * @sg: pointer to the shadow guest address space structure
1395aa17aa57SMartin Schwidefsky  * @saddr: faulting address in the shadow gmap
13965ac14bacSClaudio Imbrenda  * @datptr: will contain the address of the faulting DAT table entry, or of
13975ac14bacSClaudio Imbrenda  *	    the valid leaf, plus some flags
1398aa17aa57SMartin Schwidefsky  *
1399aa17aa57SMartin Schwidefsky  * Returns: - 0 if the shadow fault was successfully resolved
1400aa17aa57SMartin Schwidefsky  *	    - > 0 (pgm exception code) on exceptions while faulting
1401aa17aa57SMartin Schwidefsky  *	    - -EAGAIN if the caller can retry immediately
1402aa17aa57SMartin Schwidefsky  *	    - -EFAULT when accessing invalid guest addresses
1403aa17aa57SMartin Schwidefsky  *	    - -ENOMEM if out of memory
1404aa17aa57SMartin Schwidefsky  */
kvm_s390_shadow_fault(struct kvm_vcpu * vcpu,struct gmap * sg,unsigned long saddr,unsigned long * datptr)1405f4debb40SDavid Hildenbrand int kvm_s390_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg,
14065ac14bacSClaudio Imbrenda 			  unsigned long saddr, unsigned long *datptr)
1407aa17aa57SMartin Schwidefsky {
1408aa17aa57SMartin Schwidefsky 	union vaddress vaddr;
1409aa17aa57SMartin Schwidefsky 	union page_table_entry pte;
14105ac14bacSClaudio Imbrenda 	unsigned long pgt = 0;
1411fd8d4e3aSDavid Hildenbrand 	int dat_protection, fake;
1412aa17aa57SMartin Schwidefsky 	int rc;
1413aa17aa57SMartin Schwidefsky 
1414d8ed45c5SMichel Lespinasse 	mmap_read_lock(sg->mm);
1415f4debb40SDavid Hildenbrand 	/*
1416f4debb40SDavid Hildenbrand 	 * We don't want any guest-2 tables to change - so the parent
1417f4debb40SDavid Hildenbrand 	 * tables/pointers we read stay valid - unshadowing is however
1418f4debb40SDavid Hildenbrand 	 * always possible - only guest_table_lock protects us.
1419f4debb40SDavid Hildenbrand 	 */
14200130337eSPierre Morel 	ipte_lock(vcpu->kvm);
1421e52f8b61SDavid Hildenbrand 
1422fd8d4e3aSDavid Hildenbrand 	rc = gmap_shadow_pgt_lookup(sg, saddr, &pgt, &dat_protection, &fake);
1423aa17aa57SMartin Schwidefsky 	if (rc)
1424fd8d4e3aSDavid Hildenbrand 		rc = kvm_s390_shadow_tables(sg, saddr, &pgt, &dat_protection,
1425fd8d4e3aSDavid Hildenbrand 					    &fake);
1426aa17aa57SMartin Schwidefsky 
1427aa17aa57SMartin Schwidefsky 	vaddr.addr = saddr;
1428fd8d4e3aSDavid Hildenbrand 	if (fake) {
142958cdf5ebSHeiko Carstens 		pte.val = pgt + vaddr.px * PAGE_SIZE;
1430fd8d4e3aSDavid Hildenbrand 		goto shadow_page;
1431fd8d4e3aSDavid Hildenbrand 	}
14325ac14bacSClaudio Imbrenda 
14335ac14bacSClaudio Imbrenda 	switch (rc) {
14345ac14bacSClaudio Imbrenda 	case PGM_SEGMENT_TRANSLATION:
14355ac14bacSClaudio Imbrenda 	case PGM_REGION_THIRD_TRANS:
14365ac14bacSClaudio Imbrenda 	case PGM_REGION_SECOND_TRANS:
14375ac14bacSClaudio Imbrenda 	case PGM_REGION_FIRST_TRANS:
14385ac14bacSClaudio Imbrenda 		pgt |= PEI_NOT_PTE;
14395ac14bacSClaudio Imbrenda 		break;
14405ac14bacSClaudio Imbrenda 	case 0:
14415ac14bacSClaudio Imbrenda 		pgt += vaddr.px * 8;
14425ac14bacSClaudio Imbrenda 		rc = gmap_read_table(sg->parent, pgt, &pte.val);
14435ac14bacSClaudio Imbrenda 	}
14445ac14bacSClaudio Imbrenda 	if (datptr)
14455ac14bacSClaudio Imbrenda 		*datptr = pgt | dat_protection * PEI_DAT_PROT;
1446e52f8b61SDavid Hildenbrand 	if (!rc && pte.i)
1447e52f8b61SDavid Hildenbrand 		rc = PGM_PAGE_TRANSLATION;
1448232b8e3bSHeiko Carstens 	if (!rc && pte.z)
1449e52f8b61SDavid Hildenbrand 		rc = PGM_TRANSLATION_SPEC;
1450fd8d4e3aSDavid Hildenbrand shadow_page:
145100fc062dSDavid Hildenbrand 	pte.p |= dat_protection;
1452e52f8b61SDavid Hildenbrand 	if (!rc)
1453a9d23e71SDavid Hildenbrand 		rc = gmap_shadow_page(sg, saddr, __pte(pte.val));
1454c3235e2dSNico Boehr 	vcpu->kvm->stat.gmap_shadow_pg_entry++;
14550130337eSPierre Morel 	ipte_unlock(vcpu->kvm);
1456d8ed45c5SMichel Lespinasse 	mmap_read_unlock(sg->mm);
1457aa17aa57SMartin Schwidefsky 	return rc;
1458aa17aa57SMartin Schwidefsky }
1459