xref: /linux/arch/s390/kvm/gaccess.c (revision 3218f7094b6b583f4f01bffcf84572c6beacdcc2)
122938978SHeiko Carstens /*
222938978SHeiko Carstens  * guest access functions
322938978SHeiko Carstens  *
422938978SHeiko Carstens  * Copyright IBM Corp. 2014
522938978SHeiko Carstens  *
622938978SHeiko Carstens  */
722938978SHeiko Carstens 
822938978SHeiko Carstens #include <linux/vmalloc.h>
922938978SHeiko Carstens #include <linux/err.h>
1022938978SHeiko Carstens #include <asm/pgtable.h>
11aa17aa57SMartin Schwidefsky #include <asm/gmap.h>
1222938978SHeiko Carstens #include "kvm-s390.h"
1322938978SHeiko Carstens #include "gaccess.h"
14664b4973SAlexander Yarygin #include <asm/switch_to.h>
1522938978SHeiko Carstens 
1622938978SHeiko Carstens union asce {
1722938978SHeiko Carstens 	unsigned long val;
1822938978SHeiko Carstens 	struct {
1922938978SHeiko Carstens 		unsigned long origin : 52; /* Region- or Segment-Table Origin */
2022938978SHeiko Carstens 		unsigned long	 : 2;
2122938978SHeiko Carstens 		unsigned long g  : 1; /* Subspace Group Control */
2222938978SHeiko Carstens 		unsigned long p  : 1; /* Private Space Control */
2322938978SHeiko Carstens 		unsigned long s  : 1; /* Storage-Alteration-Event Control */
2422938978SHeiko Carstens 		unsigned long x  : 1; /* Space-Switch-Event Control */
2522938978SHeiko Carstens 		unsigned long r  : 1; /* Real-Space Control */
2622938978SHeiko Carstens 		unsigned long	 : 1;
2722938978SHeiko Carstens 		unsigned long dt : 2; /* Designation-Type Control */
2822938978SHeiko Carstens 		unsigned long tl : 2; /* Region- or Segment-Table Length */
2922938978SHeiko Carstens 	};
3022938978SHeiko Carstens };
3122938978SHeiko Carstens 
3222938978SHeiko Carstens enum {
3322938978SHeiko Carstens 	ASCE_TYPE_SEGMENT = 0,
3422938978SHeiko Carstens 	ASCE_TYPE_REGION3 = 1,
3522938978SHeiko Carstens 	ASCE_TYPE_REGION2 = 2,
3622938978SHeiko Carstens 	ASCE_TYPE_REGION1 = 3
3722938978SHeiko Carstens };
3822938978SHeiko Carstens 
3922938978SHeiko Carstens union region1_table_entry {
4022938978SHeiko Carstens 	unsigned long val;
4122938978SHeiko Carstens 	struct {
4222938978SHeiko Carstens 		unsigned long rto: 52;/* Region-Table Origin */
4322938978SHeiko Carstens 		unsigned long	 : 2;
4422938978SHeiko Carstens 		unsigned long p  : 1; /* DAT-Protection Bit */
4522938978SHeiko Carstens 		unsigned long	 : 1;
4622938978SHeiko Carstens 		unsigned long tf : 2; /* Region-Second-Table Offset */
4722938978SHeiko Carstens 		unsigned long i  : 1; /* Region-Invalid Bit */
4822938978SHeiko Carstens 		unsigned long	 : 1;
4922938978SHeiko Carstens 		unsigned long tt : 2; /* Table-Type Bits */
5022938978SHeiko Carstens 		unsigned long tl : 2; /* Region-Second-Table Length */
5122938978SHeiko Carstens 	};
5222938978SHeiko Carstens };
5322938978SHeiko Carstens 
5422938978SHeiko Carstens union region2_table_entry {
5522938978SHeiko Carstens 	unsigned long val;
5622938978SHeiko Carstens 	struct {
5722938978SHeiko Carstens 		unsigned long rto: 52;/* Region-Table Origin */
5822938978SHeiko Carstens 		unsigned long	 : 2;
5922938978SHeiko Carstens 		unsigned long p  : 1; /* DAT-Protection Bit */
6022938978SHeiko Carstens 		unsigned long	 : 1;
6122938978SHeiko Carstens 		unsigned long tf : 2; /* Region-Third-Table Offset */
6222938978SHeiko Carstens 		unsigned long i  : 1; /* Region-Invalid Bit */
6322938978SHeiko Carstens 		unsigned long	 : 1;
6422938978SHeiko Carstens 		unsigned long tt : 2; /* Table-Type Bits */
6522938978SHeiko Carstens 		unsigned long tl : 2; /* Region-Third-Table Length */
6622938978SHeiko Carstens 	};
6722938978SHeiko Carstens };
6822938978SHeiko Carstens 
6922938978SHeiko Carstens struct region3_table_entry_fc0 {
7022938978SHeiko Carstens 	unsigned long sto: 52;/* Segment-Table Origin */
7122938978SHeiko Carstens 	unsigned long	 : 1;
7222938978SHeiko Carstens 	unsigned long fc : 1; /* Format-Control */
7322938978SHeiko Carstens 	unsigned long p  : 1; /* DAT-Protection Bit */
7422938978SHeiko Carstens 	unsigned long	 : 1;
7522938978SHeiko Carstens 	unsigned long tf : 2; /* Segment-Table Offset */
7622938978SHeiko Carstens 	unsigned long i  : 1; /* Region-Invalid Bit */
7722938978SHeiko Carstens 	unsigned long cr : 1; /* Common-Region Bit */
7822938978SHeiko Carstens 	unsigned long tt : 2; /* Table-Type Bits */
7922938978SHeiko Carstens 	unsigned long tl : 2; /* Segment-Table Length */
8022938978SHeiko Carstens };
8122938978SHeiko Carstens 
8222938978SHeiko Carstens struct region3_table_entry_fc1 {
8322938978SHeiko Carstens 	unsigned long rfaa : 33; /* Region-Frame Absolute Address */
8422938978SHeiko Carstens 	unsigned long	 : 14;
8522938978SHeiko Carstens 	unsigned long av : 1; /* ACCF-Validity Control */
8622938978SHeiko Carstens 	unsigned long acc: 4; /* Access-Control Bits */
8722938978SHeiko Carstens 	unsigned long f  : 1; /* Fetch-Protection Bit */
8822938978SHeiko Carstens 	unsigned long fc : 1; /* Format-Control */
8922938978SHeiko Carstens 	unsigned long p  : 1; /* DAT-Protection Bit */
9022938978SHeiko Carstens 	unsigned long co : 1; /* Change-Recording Override */
9122938978SHeiko Carstens 	unsigned long	 : 2;
9222938978SHeiko Carstens 	unsigned long i  : 1; /* Region-Invalid Bit */
9322938978SHeiko Carstens 	unsigned long cr : 1; /* Common-Region Bit */
9422938978SHeiko Carstens 	unsigned long tt : 2; /* Table-Type Bits */
9522938978SHeiko Carstens 	unsigned long	 : 2;
9622938978SHeiko Carstens };
9722938978SHeiko Carstens 
9822938978SHeiko Carstens union region3_table_entry {
9922938978SHeiko Carstens 	unsigned long val;
10022938978SHeiko Carstens 	struct region3_table_entry_fc0 fc0;
10122938978SHeiko Carstens 	struct region3_table_entry_fc1 fc1;
10222938978SHeiko Carstens 	struct {
10322938978SHeiko Carstens 		unsigned long	 : 53;
10422938978SHeiko Carstens 		unsigned long fc : 1; /* Format-Control */
10522938978SHeiko Carstens 		unsigned long	 : 4;
10622938978SHeiko Carstens 		unsigned long i  : 1; /* Region-Invalid Bit */
10722938978SHeiko Carstens 		unsigned long cr : 1; /* Common-Region Bit */
10822938978SHeiko Carstens 		unsigned long tt : 2; /* Table-Type Bits */
10922938978SHeiko Carstens 		unsigned long	 : 2;
11022938978SHeiko Carstens 	};
11122938978SHeiko Carstens };
11222938978SHeiko Carstens 
11322938978SHeiko Carstens struct segment_entry_fc0 {
11422938978SHeiko Carstens 	unsigned long pto: 53;/* Page-Table Origin */
11522938978SHeiko Carstens 	unsigned long fc : 1; /* Format-Control */
11622938978SHeiko Carstens 	unsigned long p  : 1; /* DAT-Protection Bit */
11722938978SHeiko Carstens 	unsigned long	 : 3;
11822938978SHeiko Carstens 	unsigned long i  : 1; /* Segment-Invalid Bit */
11922938978SHeiko Carstens 	unsigned long cs : 1; /* Common-Segment Bit */
12022938978SHeiko Carstens 	unsigned long tt : 2; /* Table-Type Bits */
12122938978SHeiko Carstens 	unsigned long	 : 2;
12222938978SHeiko Carstens };
12322938978SHeiko Carstens 
12422938978SHeiko Carstens struct segment_entry_fc1 {
12522938978SHeiko Carstens 	unsigned long sfaa : 44; /* Segment-Frame Absolute Address */
12622938978SHeiko Carstens 	unsigned long	 : 3;
12722938978SHeiko Carstens 	unsigned long av : 1; /* ACCF-Validity Control */
12822938978SHeiko Carstens 	unsigned long acc: 4; /* Access-Control Bits */
12922938978SHeiko Carstens 	unsigned long f  : 1; /* Fetch-Protection Bit */
13022938978SHeiko Carstens 	unsigned long fc : 1; /* Format-Control */
13122938978SHeiko Carstens 	unsigned long p  : 1; /* DAT-Protection Bit */
13222938978SHeiko Carstens 	unsigned long co : 1; /* Change-Recording Override */
13322938978SHeiko Carstens 	unsigned long	 : 2;
13422938978SHeiko Carstens 	unsigned long i  : 1; /* Segment-Invalid Bit */
13522938978SHeiko Carstens 	unsigned long cs : 1; /* Common-Segment Bit */
13622938978SHeiko Carstens 	unsigned long tt : 2; /* Table-Type Bits */
13722938978SHeiko Carstens 	unsigned long	 : 2;
13822938978SHeiko Carstens };
13922938978SHeiko Carstens 
14022938978SHeiko Carstens union segment_table_entry {
14122938978SHeiko Carstens 	unsigned long val;
14222938978SHeiko Carstens 	struct segment_entry_fc0 fc0;
14322938978SHeiko Carstens 	struct segment_entry_fc1 fc1;
14422938978SHeiko Carstens 	struct {
14522938978SHeiko Carstens 		unsigned long	 : 53;
14622938978SHeiko Carstens 		unsigned long fc : 1; /* Format-Control */
14722938978SHeiko Carstens 		unsigned long	 : 4;
14822938978SHeiko Carstens 		unsigned long i  : 1; /* Segment-Invalid Bit */
14922938978SHeiko Carstens 		unsigned long cs : 1; /* Common-Segment Bit */
15022938978SHeiko Carstens 		unsigned long tt : 2; /* Table-Type Bits */
15122938978SHeiko Carstens 		unsigned long	 : 2;
15222938978SHeiko Carstens 	};
15322938978SHeiko Carstens };
15422938978SHeiko Carstens 
15522938978SHeiko Carstens enum {
15622938978SHeiko Carstens 	TABLE_TYPE_SEGMENT = 0,
15722938978SHeiko Carstens 	TABLE_TYPE_REGION3 = 1,
15822938978SHeiko Carstens 	TABLE_TYPE_REGION2 = 2,
15922938978SHeiko Carstens 	TABLE_TYPE_REGION1 = 3
16022938978SHeiko Carstens };
16122938978SHeiko Carstens 
16222938978SHeiko Carstens union page_table_entry {
16322938978SHeiko Carstens 	unsigned long val;
16422938978SHeiko Carstens 	struct {
16522938978SHeiko Carstens 		unsigned long pfra : 52; /* Page-Frame Real Address */
16622938978SHeiko Carstens 		unsigned long z  : 1; /* Zero Bit */
16722938978SHeiko Carstens 		unsigned long i  : 1; /* Page-Invalid Bit */
16822938978SHeiko Carstens 		unsigned long p  : 1; /* DAT-Protection Bit */
16922938978SHeiko Carstens 		unsigned long co : 1; /* Change-Recording Override */
17022938978SHeiko Carstens 		unsigned long	 : 8;
17122938978SHeiko Carstens 	};
17222938978SHeiko Carstens };
17322938978SHeiko Carstens 
17422938978SHeiko Carstens /*
17522938978SHeiko Carstens  * vaddress union in order to easily decode a virtual address into its
17622938978SHeiko Carstens  * region first index, region second index etc. parts.
17722938978SHeiko Carstens  */
17822938978SHeiko Carstens union vaddress {
17922938978SHeiko Carstens 	unsigned long addr;
18022938978SHeiko Carstens 	struct {
18122938978SHeiko Carstens 		unsigned long rfx : 11;
18222938978SHeiko Carstens 		unsigned long rsx : 11;
18322938978SHeiko Carstens 		unsigned long rtx : 11;
18422938978SHeiko Carstens 		unsigned long sx  : 11;
18522938978SHeiko Carstens 		unsigned long px  : 8;
18622938978SHeiko Carstens 		unsigned long bx  : 12;
18722938978SHeiko Carstens 	};
18822938978SHeiko Carstens 	struct {
18922938978SHeiko Carstens 		unsigned long rfx01 : 2;
19022938978SHeiko Carstens 		unsigned long	    : 9;
19122938978SHeiko Carstens 		unsigned long rsx01 : 2;
19222938978SHeiko Carstens 		unsigned long	    : 9;
19322938978SHeiko Carstens 		unsigned long rtx01 : 2;
19422938978SHeiko Carstens 		unsigned long	    : 9;
19522938978SHeiko Carstens 		unsigned long sx01  : 2;
19622938978SHeiko Carstens 		unsigned long	    : 29;
19722938978SHeiko Carstens 	};
19822938978SHeiko Carstens };
19922938978SHeiko Carstens 
20022938978SHeiko Carstens /*
20122938978SHeiko Carstens  * raddress union which will contain the result (real or absolute address)
20222938978SHeiko Carstens  * after a page table walk. The rfaa, sfaa and pfra members are used to
20322938978SHeiko Carstens  * simply assign them the value of a region, segment or page table entry.
20422938978SHeiko Carstens  */
20522938978SHeiko Carstens union raddress {
20622938978SHeiko Carstens 	unsigned long addr;
20722938978SHeiko Carstens 	unsigned long rfaa : 33; /* Region-Frame Absolute Address */
20822938978SHeiko Carstens 	unsigned long sfaa : 44; /* Segment-Frame Absolute Address */
20922938978SHeiko Carstens 	unsigned long pfra : 52; /* Page-Frame Real Address */
21022938978SHeiko Carstens };
21122938978SHeiko Carstens 
212664b4973SAlexander Yarygin union alet {
213664b4973SAlexander Yarygin 	u32 val;
214664b4973SAlexander Yarygin 	struct {
215664b4973SAlexander Yarygin 		u32 reserved : 7;
216664b4973SAlexander Yarygin 		u32 p        : 1;
217664b4973SAlexander Yarygin 		u32 alesn    : 8;
218664b4973SAlexander Yarygin 		u32 alen     : 16;
219664b4973SAlexander Yarygin 	};
220664b4973SAlexander Yarygin };
221664b4973SAlexander Yarygin 
222664b4973SAlexander Yarygin union ald {
223664b4973SAlexander Yarygin 	u32 val;
224664b4973SAlexander Yarygin 	struct {
225664b4973SAlexander Yarygin 		u32     : 1;
226664b4973SAlexander Yarygin 		u32 alo : 24;
227664b4973SAlexander Yarygin 		u32 all : 7;
228664b4973SAlexander Yarygin 	};
229664b4973SAlexander Yarygin };
230664b4973SAlexander Yarygin 
231664b4973SAlexander Yarygin struct ale {
232664b4973SAlexander Yarygin 	unsigned long i      : 1; /* ALEN-Invalid Bit */
233664b4973SAlexander Yarygin 	unsigned long        : 5;
234664b4973SAlexander Yarygin 	unsigned long fo     : 1; /* Fetch-Only Bit */
235664b4973SAlexander Yarygin 	unsigned long p      : 1; /* Private Bit */
236664b4973SAlexander Yarygin 	unsigned long alesn  : 8; /* Access-List-Entry Sequence Number */
237664b4973SAlexander Yarygin 	unsigned long aleax  : 16; /* Access-List-Entry Authorization Index */
238664b4973SAlexander Yarygin 	unsigned long        : 32;
239664b4973SAlexander Yarygin 	unsigned long        : 1;
240664b4973SAlexander Yarygin 	unsigned long asteo  : 25; /* ASN-Second-Table-Entry Origin */
241664b4973SAlexander Yarygin 	unsigned long        : 6;
242664b4973SAlexander Yarygin 	unsigned long astesn : 32; /* ASTE Sequence Number */
243664b4973SAlexander Yarygin } __packed;
244664b4973SAlexander Yarygin 
245664b4973SAlexander Yarygin struct aste {
246664b4973SAlexander Yarygin 	unsigned long i      : 1; /* ASX-Invalid Bit */
247664b4973SAlexander Yarygin 	unsigned long ato    : 29; /* Authority-Table Origin */
248664b4973SAlexander Yarygin 	unsigned long        : 1;
249664b4973SAlexander Yarygin 	unsigned long b      : 1; /* Base-Space Bit */
250664b4973SAlexander Yarygin 	unsigned long ax     : 16; /* Authorization Index */
251664b4973SAlexander Yarygin 	unsigned long atl    : 12; /* Authority-Table Length */
252664b4973SAlexander Yarygin 	unsigned long        : 2;
253664b4973SAlexander Yarygin 	unsigned long ca     : 1; /* Controlled-ASN Bit */
254664b4973SAlexander Yarygin 	unsigned long ra     : 1; /* Reusable-ASN Bit */
255664b4973SAlexander Yarygin 	unsigned long asce   : 64; /* Address-Space-Control Element */
256664b4973SAlexander Yarygin 	unsigned long ald    : 32;
257664b4973SAlexander Yarygin 	unsigned long astesn : 32;
258664b4973SAlexander Yarygin 	/* .. more fields there */
259664b4973SAlexander Yarygin } __packed;
2608a242234SHeiko Carstens 
2618a242234SHeiko Carstens int ipte_lock_held(struct kvm_vcpu *vcpu)
2628a242234SHeiko Carstens {
2635e044315SEugene (jno) Dvurechenski 	if (vcpu->arch.sie_block->eca & 1) {
2645e044315SEugene (jno) Dvurechenski 		int rc;
2658a242234SHeiko Carstens 
2665e044315SEugene (jno) Dvurechenski 		read_lock(&vcpu->kvm->arch.sca_lock);
2675e044315SEugene (jno) Dvurechenski 		rc = kvm_s390_get_ipte_control(vcpu->kvm)->kh != 0;
2685e044315SEugene (jno) Dvurechenski 		read_unlock(&vcpu->kvm->arch.sca_lock);
2695e044315SEugene (jno) Dvurechenski 		return rc;
2705e044315SEugene (jno) Dvurechenski 	}
271a6b7e459SThomas Huth 	return vcpu->kvm->arch.ipte_lock_count != 0;
2728a242234SHeiko Carstens }
2738a242234SHeiko Carstens 
2748a242234SHeiko Carstens static void ipte_lock_simple(struct kvm_vcpu *vcpu)
2758a242234SHeiko Carstens {
2768a242234SHeiko Carstens 	union ipte_control old, new, *ic;
2778a242234SHeiko Carstens 
278a6b7e459SThomas Huth 	mutex_lock(&vcpu->kvm->arch.ipte_mutex);
279a6b7e459SThomas Huth 	vcpu->kvm->arch.ipte_lock_count++;
280a6b7e459SThomas Huth 	if (vcpu->kvm->arch.ipte_lock_count > 1)
2818a242234SHeiko Carstens 		goto out;
2825e044315SEugene (jno) Dvurechenski retry:
2835e044315SEugene (jno) Dvurechenski 	read_lock(&vcpu->kvm->arch.sca_lock);
28460514510SEugene (jno) Dvurechenski 	ic = kvm_s390_get_ipte_control(vcpu->kvm);
2858a242234SHeiko Carstens 	do {
2865de72a22SChristian Borntraeger 		old = READ_ONCE(*ic);
2875e044315SEugene (jno) Dvurechenski 		if (old.k) {
2885e044315SEugene (jno) Dvurechenski 			read_unlock(&vcpu->kvm->arch.sca_lock);
2898a242234SHeiko Carstens 			cond_resched();
2905e044315SEugene (jno) Dvurechenski 			goto retry;
2918a242234SHeiko Carstens 		}
2928a242234SHeiko Carstens 		new = old;
2938a242234SHeiko Carstens 		new.k = 1;
2948a242234SHeiko Carstens 	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
2955e044315SEugene (jno) Dvurechenski 	read_unlock(&vcpu->kvm->arch.sca_lock);
2968a242234SHeiko Carstens out:
297a6b7e459SThomas Huth 	mutex_unlock(&vcpu->kvm->arch.ipte_mutex);
2988a242234SHeiko Carstens }
2998a242234SHeiko Carstens 
3008a242234SHeiko Carstens static void ipte_unlock_simple(struct kvm_vcpu *vcpu)
3018a242234SHeiko Carstens {
3028a242234SHeiko Carstens 	union ipte_control old, new, *ic;
3038a242234SHeiko Carstens 
304a6b7e459SThomas Huth 	mutex_lock(&vcpu->kvm->arch.ipte_mutex);
305a6b7e459SThomas Huth 	vcpu->kvm->arch.ipte_lock_count--;
306a6b7e459SThomas Huth 	if (vcpu->kvm->arch.ipte_lock_count)
3078a242234SHeiko Carstens 		goto out;
3085e044315SEugene (jno) Dvurechenski 	read_lock(&vcpu->kvm->arch.sca_lock);
30960514510SEugene (jno) Dvurechenski 	ic = kvm_s390_get_ipte_control(vcpu->kvm);
3108a242234SHeiko Carstens 	do {
3115de72a22SChristian Borntraeger 		old = READ_ONCE(*ic);
3121365039dSChristian Borntraeger 		new = old;
3138a242234SHeiko Carstens 		new.k = 0;
3148a242234SHeiko Carstens 	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
3155e044315SEugene (jno) Dvurechenski 	read_unlock(&vcpu->kvm->arch.sca_lock);
3168a242234SHeiko Carstens 	wake_up(&vcpu->kvm->arch.ipte_wq);
3178a242234SHeiko Carstens out:
318a6b7e459SThomas Huth 	mutex_unlock(&vcpu->kvm->arch.ipte_mutex);
3198a242234SHeiko Carstens }
3208a242234SHeiko Carstens 
3218a242234SHeiko Carstens static void ipte_lock_siif(struct kvm_vcpu *vcpu)
3228a242234SHeiko Carstens {
3238a242234SHeiko Carstens 	union ipte_control old, new, *ic;
3248a242234SHeiko Carstens 
3255e044315SEugene (jno) Dvurechenski retry:
3265e044315SEugene (jno) Dvurechenski 	read_lock(&vcpu->kvm->arch.sca_lock);
32760514510SEugene (jno) Dvurechenski 	ic = kvm_s390_get_ipte_control(vcpu->kvm);
3288a242234SHeiko Carstens 	do {
3295de72a22SChristian Borntraeger 		old = READ_ONCE(*ic);
3305e044315SEugene (jno) Dvurechenski 		if (old.kg) {
3315e044315SEugene (jno) Dvurechenski 			read_unlock(&vcpu->kvm->arch.sca_lock);
3328a242234SHeiko Carstens 			cond_resched();
3335e044315SEugene (jno) Dvurechenski 			goto retry;
3348a242234SHeiko Carstens 		}
3358a242234SHeiko Carstens 		new = old;
3368a242234SHeiko Carstens 		new.k = 1;
3378a242234SHeiko Carstens 		new.kh++;
3388a242234SHeiko Carstens 	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
3395e044315SEugene (jno) Dvurechenski 	read_unlock(&vcpu->kvm->arch.sca_lock);
3408a242234SHeiko Carstens }
3418a242234SHeiko Carstens 
3428a242234SHeiko Carstens static void ipte_unlock_siif(struct kvm_vcpu *vcpu)
3438a242234SHeiko Carstens {
3448a242234SHeiko Carstens 	union ipte_control old, new, *ic;
3458a242234SHeiko Carstens 
3465e044315SEugene (jno) Dvurechenski 	read_lock(&vcpu->kvm->arch.sca_lock);
34760514510SEugene (jno) Dvurechenski 	ic = kvm_s390_get_ipte_control(vcpu->kvm);
3488a242234SHeiko Carstens 	do {
3495de72a22SChristian Borntraeger 		old = READ_ONCE(*ic);
3501365039dSChristian Borntraeger 		new = old;
3518a242234SHeiko Carstens 		new.kh--;
3528a242234SHeiko Carstens 		if (!new.kh)
3538a242234SHeiko Carstens 			new.k = 0;
3548a242234SHeiko Carstens 	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
3555e044315SEugene (jno) Dvurechenski 	read_unlock(&vcpu->kvm->arch.sca_lock);
3568a242234SHeiko Carstens 	if (!new.kh)
3578a242234SHeiko Carstens 		wake_up(&vcpu->kvm->arch.ipte_wq);
3588a242234SHeiko Carstens }
3598a242234SHeiko Carstens 
360a0465f9aSThomas Huth void ipte_lock(struct kvm_vcpu *vcpu)
3618a242234SHeiko Carstens {
3628a242234SHeiko Carstens 	if (vcpu->arch.sie_block->eca & 1)
3638a242234SHeiko Carstens 		ipte_lock_siif(vcpu);
3648a242234SHeiko Carstens 	else
3658a242234SHeiko Carstens 		ipte_lock_simple(vcpu);
3668a242234SHeiko Carstens }
3678a242234SHeiko Carstens 
368a0465f9aSThomas Huth void ipte_unlock(struct kvm_vcpu *vcpu)
3698a242234SHeiko Carstens {
3708a242234SHeiko Carstens 	if (vcpu->arch.sie_block->eca & 1)
3718a242234SHeiko Carstens 		ipte_unlock_siif(vcpu);
3728a242234SHeiko Carstens 	else
3738a242234SHeiko Carstens 		ipte_unlock_simple(vcpu);
3748a242234SHeiko Carstens }
3758a242234SHeiko Carstens 
376664b4973SAlexander Yarygin static int ar_translation(struct kvm_vcpu *vcpu, union asce *asce, ar_t ar,
37792c96321SDavid Hildenbrand 			  enum gacc_mode mode)
37822938978SHeiko Carstens {
379664b4973SAlexander Yarygin 	union alet alet;
380664b4973SAlexander Yarygin 	struct ale ale;
381664b4973SAlexander Yarygin 	struct aste aste;
382664b4973SAlexander Yarygin 	unsigned long ald_addr, authority_table_addr;
383664b4973SAlexander Yarygin 	union ald ald;
384664b4973SAlexander Yarygin 	int eax, rc;
385664b4973SAlexander Yarygin 	u8 authority_table;
386664b4973SAlexander Yarygin 
387664b4973SAlexander Yarygin 	if (ar >= NUM_ACRS)
388664b4973SAlexander Yarygin 		return -EINVAL;
389664b4973SAlexander Yarygin 
390664b4973SAlexander Yarygin 	save_access_regs(vcpu->run->s.regs.acrs);
391664b4973SAlexander Yarygin 	alet.val = vcpu->run->s.regs.acrs[ar];
392664b4973SAlexander Yarygin 
393664b4973SAlexander Yarygin 	if (ar == 0 || alet.val == 0) {
394664b4973SAlexander Yarygin 		asce->val = vcpu->arch.sie_block->gcr[1];
395664b4973SAlexander Yarygin 		return 0;
396664b4973SAlexander Yarygin 	} else if (alet.val == 1) {
397664b4973SAlexander Yarygin 		asce->val = vcpu->arch.sie_block->gcr[7];
398664b4973SAlexander Yarygin 		return 0;
399664b4973SAlexander Yarygin 	}
400664b4973SAlexander Yarygin 
401664b4973SAlexander Yarygin 	if (alet.reserved)
402664b4973SAlexander Yarygin 		return PGM_ALET_SPECIFICATION;
403664b4973SAlexander Yarygin 
404664b4973SAlexander Yarygin 	if (alet.p)
405664b4973SAlexander Yarygin 		ald_addr = vcpu->arch.sie_block->gcr[5];
406664b4973SAlexander Yarygin 	else
407664b4973SAlexander Yarygin 		ald_addr = vcpu->arch.sie_block->gcr[2];
408664b4973SAlexander Yarygin 	ald_addr &= 0x7fffffc0;
409664b4973SAlexander Yarygin 
410664b4973SAlexander Yarygin 	rc = read_guest_real(vcpu, ald_addr + 16, &ald.val, sizeof(union ald));
411664b4973SAlexander Yarygin 	if (rc)
412664b4973SAlexander Yarygin 		return rc;
413664b4973SAlexander Yarygin 
414664b4973SAlexander Yarygin 	if (alet.alen / 8 > ald.all)
415664b4973SAlexander Yarygin 		return PGM_ALEN_TRANSLATION;
416664b4973SAlexander Yarygin 
417664b4973SAlexander Yarygin 	if (0x7fffffff - ald.alo * 128 < alet.alen * 16)
418664b4973SAlexander Yarygin 		return PGM_ADDRESSING;
419664b4973SAlexander Yarygin 
420664b4973SAlexander Yarygin 	rc = read_guest_real(vcpu, ald.alo * 128 + alet.alen * 16, &ale,
421664b4973SAlexander Yarygin 			     sizeof(struct ale));
422664b4973SAlexander Yarygin 	if (rc)
423664b4973SAlexander Yarygin 		return rc;
424664b4973SAlexander Yarygin 
425664b4973SAlexander Yarygin 	if (ale.i == 1)
426664b4973SAlexander Yarygin 		return PGM_ALEN_TRANSLATION;
427664b4973SAlexander Yarygin 	if (ale.alesn != alet.alesn)
428664b4973SAlexander Yarygin 		return PGM_ALE_SEQUENCE;
429664b4973SAlexander Yarygin 
430664b4973SAlexander Yarygin 	rc = read_guest_real(vcpu, ale.asteo * 64, &aste, sizeof(struct aste));
431664b4973SAlexander Yarygin 	if (rc)
432664b4973SAlexander Yarygin 		return rc;
433664b4973SAlexander Yarygin 
434664b4973SAlexander Yarygin 	if (aste.i)
435664b4973SAlexander Yarygin 		return PGM_ASTE_VALIDITY;
436664b4973SAlexander Yarygin 	if (aste.astesn != ale.astesn)
437664b4973SAlexander Yarygin 		return PGM_ASTE_SEQUENCE;
438664b4973SAlexander Yarygin 
439664b4973SAlexander Yarygin 	if (ale.p == 1) {
440664b4973SAlexander Yarygin 		eax = (vcpu->arch.sie_block->gcr[8] >> 16) & 0xffff;
441664b4973SAlexander Yarygin 		if (ale.aleax != eax) {
442664b4973SAlexander Yarygin 			if (eax / 16 > aste.atl)
443664b4973SAlexander Yarygin 				return PGM_EXTENDED_AUTHORITY;
444664b4973SAlexander Yarygin 
445664b4973SAlexander Yarygin 			authority_table_addr = aste.ato * 4 + eax / 4;
446664b4973SAlexander Yarygin 
447664b4973SAlexander Yarygin 			rc = read_guest_real(vcpu, authority_table_addr,
448664b4973SAlexander Yarygin 					     &authority_table,
449664b4973SAlexander Yarygin 					     sizeof(u8));
450664b4973SAlexander Yarygin 			if (rc)
451664b4973SAlexander Yarygin 				return rc;
452664b4973SAlexander Yarygin 
453664b4973SAlexander Yarygin 			if ((authority_table & (0x40 >> ((eax & 3) * 2))) == 0)
454664b4973SAlexander Yarygin 				return PGM_EXTENDED_AUTHORITY;
455664b4973SAlexander Yarygin 		}
456664b4973SAlexander Yarygin 	}
457664b4973SAlexander Yarygin 
45892c96321SDavid Hildenbrand 	if (ale.fo == 1 && mode == GACC_STORE)
459664b4973SAlexander Yarygin 		return PGM_PROTECTION;
460664b4973SAlexander Yarygin 
461664b4973SAlexander Yarygin 	asce->val = aste.asce;
462664b4973SAlexander Yarygin 	return 0;
463664b4973SAlexander Yarygin }
464664b4973SAlexander Yarygin 
465664b4973SAlexander Yarygin struct trans_exc_code_bits {
466664b4973SAlexander Yarygin 	unsigned long addr : 52; /* Translation-exception Address */
467664b4973SAlexander Yarygin 	unsigned long fsi  : 2;  /* Access Exception Fetch/Store Indication */
468664b4973SAlexander Yarygin 	unsigned long	   : 6;
469664b4973SAlexander Yarygin 	unsigned long b60  : 1;
470664b4973SAlexander Yarygin 	unsigned long b61  : 1;
471664b4973SAlexander Yarygin 	unsigned long as   : 2;  /* ASCE Identifier */
472664b4973SAlexander Yarygin };
473664b4973SAlexander Yarygin 
474664b4973SAlexander Yarygin enum {
475664b4973SAlexander Yarygin 	FSI_UNKNOWN = 0, /* Unknown wether fetch or store */
476664b4973SAlexander Yarygin 	FSI_STORE   = 1, /* Exception was due to store operation */
477664b4973SAlexander Yarygin 	FSI_FETCH   = 2  /* Exception was due to fetch operation */
478664b4973SAlexander Yarygin };
479664b4973SAlexander Yarygin 
480d03193deSDavid Hildenbrand enum prot_type {
481d03193deSDavid Hildenbrand 	PROT_TYPE_LA   = 0,
482d03193deSDavid Hildenbrand 	PROT_TYPE_KEYC = 1,
483d03193deSDavid Hildenbrand 	PROT_TYPE_ALC  = 2,
484d03193deSDavid Hildenbrand 	PROT_TYPE_DAT  = 3,
485d03193deSDavid Hildenbrand };
486d03193deSDavid Hildenbrand 
487d03193deSDavid Hildenbrand static int trans_exc(struct kvm_vcpu *vcpu, int code, unsigned long gva,
488d03193deSDavid Hildenbrand 		     ar_t ar, enum gacc_mode mode, enum prot_type prot)
489d03193deSDavid Hildenbrand {
490d03193deSDavid Hildenbrand 	struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm;
491d03193deSDavid Hildenbrand 	struct trans_exc_code_bits *tec;
492d03193deSDavid Hildenbrand 
493d03193deSDavid Hildenbrand 	memset(pgm, 0, sizeof(*pgm));
494d03193deSDavid Hildenbrand 	pgm->code = code;
495d03193deSDavid Hildenbrand 	tec = (struct trans_exc_code_bits *)&pgm->trans_exc_code;
496d03193deSDavid Hildenbrand 
497d03193deSDavid Hildenbrand 	switch (code) {
498d03193deSDavid Hildenbrand 	case PGM_ASCE_TYPE:
499d03193deSDavid Hildenbrand 	case PGM_PAGE_TRANSLATION:
500d03193deSDavid Hildenbrand 	case PGM_REGION_FIRST_TRANS:
501d03193deSDavid Hildenbrand 	case PGM_REGION_SECOND_TRANS:
502d03193deSDavid Hildenbrand 	case PGM_REGION_THIRD_TRANS:
503d03193deSDavid Hildenbrand 	case PGM_SEGMENT_TRANSLATION:
504d03193deSDavid Hildenbrand 		/*
505d03193deSDavid Hildenbrand 		 * op_access_id only applies to MOVE_PAGE -> set bit 61
506d03193deSDavid Hildenbrand 		 * exc_access_id has to be set to 0 for some instructions. Both
507d03193deSDavid Hildenbrand 		 * cases have to be handled by the caller. We can always store
508d03193deSDavid Hildenbrand 		 * exc_access_id, as it is undefined for non-ar cases.
509d03193deSDavid Hildenbrand 		 */
510d03193deSDavid Hildenbrand 		tec->addr = gva >> PAGE_SHIFT;
511d03193deSDavid Hildenbrand 		tec->fsi = mode == GACC_STORE ? FSI_STORE : FSI_FETCH;
512d03193deSDavid Hildenbrand 		tec->as = psw_bits(vcpu->arch.sie_block->gpsw).as;
513d03193deSDavid Hildenbrand 		/* FALL THROUGH */
514d03193deSDavid Hildenbrand 	case PGM_ALEN_TRANSLATION:
515d03193deSDavid Hildenbrand 	case PGM_ALE_SEQUENCE:
516d03193deSDavid Hildenbrand 	case PGM_ASTE_VALIDITY:
517d03193deSDavid Hildenbrand 	case PGM_ASTE_SEQUENCE:
518d03193deSDavid Hildenbrand 	case PGM_EXTENDED_AUTHORITY:
519d03193deSDavid Hildenbrand 		pgm->exc_access_id = ar;
520d03193deSDavid Hildenbrand 		break;
521d03193deSDavid Hildenbrand 	case PGM_PROTECTION:
522d03193deSDavid Hildenbrand 		switch (prot) {
523d03193deSDavid Hildenbrand 		case PROT_TYPE_ALC:
524d03193deSDavid Hildenbrand 			tec->b60 = 1;
525d03193deSDavid Hildenbrand 			/* FALL THROUGH */
526d03193deSDavid Hildenbrand 		case PROT_TYPE_DAT:
527d03193deSDavid Hildenbrand 			tec->b61 = 1;
528d03193deSDavid Hildenbrand 			tec->addr = gva >> PAGE_SHIFT;
529d03193deSDavid Hildenbrand 			tec->fsi = mode == GACC_STORE ? FSI_STORE : FSI_FETCH;
530d03193deSDavid Hildenbrand 			tec->as = psw_bits(vcpu->arch.sie_block->gpsw).as;
531d03193deSDavid Hildenbrand 			/* exc_access_id is undefined for most cases */
532d03193deSDavid Hildenbrand 			pgm->exc_access_id = ar;
533d03193deSDavid Hildenbrand 			break;
534d03193deSDavid Hildenbrand 		default: /* LA and KEYC set b61 to 0, other params undefined */
535d03193deSDavid Hildenbrand 			break;
536d03193deSDavid Hildenbrand 		}
537d03193deSDavid Hildenbrand 		break;
538d03193deSDavid Hildenbrand 	}
539d03193deSDavid Hildenbrand 	return code;
540d03193deSDavid Hildenbrand }
541d03193deSDavid Hildenbrand 
542664b4973SAlexander Yarygin static int get_vcpu_asce(struct kvm_vcpu *vcpu, union asce *asce,
5436167375bSDavid Hildenbrand 			 unsigned long ga, ar_t ar, enum gacc_mode mode)
544664b4973SAlexander Yarygin {
545664b4973SAlexander Yarygin 	int rc;
54634346b9aSDavid Hildenbrand 	struct psw_bits psw = psw_bits(vcpu->arch.sie_block->gpsw);
547664b4973SAlexander Yarygin 
54834346b9aSDavid Hildenbrand 	if (!psw.t) {
549664b4973SAlexander Yarygin 		asce->val = 0;
550664b4973SAlexander Yarygin 		asce->r = 1;
551664b4973SAlexander Yarygin 		return 0;
552664b4973SAlexander Yarygin 	}
553664b4973SAlexander Yarygin 
55434346b9aSDavid Hildenbrand 	if (mode == GACC_IFETCH)
55534346b9aSDavid Hildenbrand 		psw.as = psw.as == PSW_AS_HOME ? PSW_AS_HOME : PSW_AS_PRIMARY;
55634346b9aSDavid Hildenbrand 
55734346b9aSDavid Hildenbrand 	switch (psw.as) {
55822938978SHeiko Carstens 	case PSW_AS_PRIMARY:
559664b4973SAlexander Yarygin 		asce->val = vcpu->arch.sie_block->gcr[1];
560664b4973SAlexander Yarygin 		return 0;
56122938978SHeiko Carstens 	case PSW_AS_SECONDARY:
562664b4973SAlexander Yarygin 		asce->val = vcpu->arch.sie_block->gcr[7];
563664b4973SAlexander Yarygin 		return 0;
56422938978SHeiko Carstens 	case PSW_AS_HOME:
565664b4973SAlexander Yarygin 		asce->val = vcpu->arch.sie_block->gcr[13];
566664b4973SAlexander Yarygin 		return 0;
567664b4973SAlexander Yarygin 	case PSW_AS_ACCREG:
56892c96321SDavid Hildenbrand 		rc = ar_translation(vcpu, asce, ar, mode);
569664b4973SAlexander Yarygin 		if (rc > 0)
570bcfa01d7SDavid Hildenbrand 			return trans_exc(vcpu, rc, ga, ar, mode, PROT_TYPE_ALC);
571664b4973SAlexander Yarygin 		return rc;
57222938978SHeiko Carstens 	}
57322938978SHeiko Carstens 	return 0;
57422938978SHeiko Carstens }
57522938978SHeiko Carstens 
57622938978SHeiko Carstens static int deref_table(struct kvm *kvm, unsigned long gpa, unsigned long *val)
57722938978SHeiko Carstens {
57822938978SHeiko Carstens 	return kvm_read_guest(kvm, gpa, val, sizeof(*val));
57922938978SHeiko Carstens }
58022938978SHeiko Carstens 
58122938978SHeiko Carstens /**
58222938978SHeiko Carstens  * guest_translate - translate a guest virtual into a guest absolute address
58322938978SHeiko Carstens  * @vcpu: virtual cpu
58422938978SHeiko Carstens  * @gva: guest virtual address
58522938978SHeiko Carstens  * @gpa: points to where guest physical (absolute) address should be stored
58675a18122SAlexander Yarygin  * @asce: effective asce
58792c96321SDavid Hildenbrand  * @mode: indicates the access mode to be used
58822938978SHeiko Carstens  *
58922938978SHeiko Carstens  * Translate a guest virtual address into a guest absolute address by means
59016b0fc13SYannick Guerrini  * of dynamic address translation as specified by the architecture.
59122938978SHeiko Carstens  * If the resulting absolute address is not available in the configuration
59222938978SHeiko Carstens  * an addressing exception is indicated and @gpa will not be changed.
59322938978SHeiko Carstens  *
59422938978SHeiko Carstens  * Returns: - zero on success; @gpa contains the resulting absolute address
59522938978SHeiko Carstens  *	    - a negative value if guest access failed due to e.g. broken
59622938978SHeiko Carstens  *	      guest mapping
59722938978SHeiko Carstens  *	    - a positve value if an access exception happened. In this case
59822938978SHeiko Carstens  *	      the returned value is the program interruption code as defined
59922938978SHeiko Carstens  *	      by the architecture
60022938978SHeiko Carstens  */
60122938978SHeiko Carstens static unsigned long guest_translate(struct kvm_vcpu *vcpu, unsigned long gva,
60275a18122SAlexander Yarygin 				     unsigned long *gpa, const union asce asce,
60392c96321SDavid Hildenbrand 				     enum gacc_mode mode)
60422938978SHeiko Carstens {
60522938978SHeiko Carstens 	union vaddress vaddr = {.addr = gva};
60622938978SHeiko Carstens 	union raddress raddr = {.addr = gva};
60722938978SHeiko Carstens 	union page_table_entry pte;
60822938978SHeiko Carstens 	int dat_protection = 0;
60922938978SHeiko Carstens 	union ctlreg0 ctlreg0;
61022938978SHeiko Carstens 	unsigned long ptr;
61122938978SHeiko Carstens 	int edat1, edat2;
61222938978SHeiko Carstens 
61322938978SHeiko Carstens 	ctlreg0.val = vcpu->arch.sie_block->gcr[0];
6149d8d5786SMichael Mueller 	edat1 = ctlreg0.edat && test_kvm_facility(vcpu->kvm, 8);
6159d8d5786SMichael Mueller 	edat2 = edat1 && test_kvm_facility(vcpu->kvm, 78);
61622938978SHeiko Carstens 	if (asce.r)
61722938978SHeiko Carstens 		goto real_address;
61822938978SHeiko Carstens 	ptr = asce.origin * 4096;
61922938978SHeiko Carstens 	switch (asce.dt) {
62022938978SHeiko Carstens 	case ASCE_TYPE_REGION1:
62122938978SHeiko Carstens 		if (vaddr.rfx01 > asce.tl)
62222938978SHeiko Carstens 			return PGM_REGION_FIRST_TRANS;
62322938978SHeiko Carstens 		ptr += vaddr.rfx * 8;
62422938978SHeiko Carstens 		break;
62522938978SHeiko Carstens 	case ASCE_TYPE_REGION2:
62622938978SHeiko Carstens 		if (vaddr.rfx)
62722938978SHeiko Carstens 			return PGM_ASCE_TYPE;
62822938978SHeiko Carstens 		if (vaddr.rsx01 > asce.tl)
62922938978SHeiko Carstens 			return PGM_REGION_SECOND_TRANS;
63022938978SHeiko Carstens 		ptr += vaddr.rsx * 8;
63122938978SHeiko Carstens 		break;
63222938978SHeiko Carstens 	case ASCE_TYPE_REGION3:
63322938978SHeiko Carstens 		if (vaddr.rfx || vaddr.rsx)
63422938978SHeiko Carstens 			return PGM_ASCE_TYPE;
63522938978SHeiko Carstens 		if (vaddr.rtx01 > asce.tl)
63622938978SHeiko Carstens 			return PGM_REGION_THIRD_TRANS;
63722938978SHeiko Carstens 		ptr += vaddr.rtx * 8;
63822938978SHeiko Carstens 		break;
63922938978SHeiko Carstens 	case ASCE_TYPE_SEGMENT:
64022938978SHeiko Carstens 		if (vaddr.rfx || vaddr.rsx || vaddr.rtx)
64122938978SHeiko Carstens 			return PGM_ASCE_TYPE;
64222938978SHeiko Carstens 		if (vaddr.sx01 > asce.tl)
64322938978SHeiko Carstens 			return PGM_SEGMENT_TRANSLATION;
64422938978SHeiko Carstens 		ptr += vaddr.sx * 8;
64522938978SHeiko Carstens 		break;
64622938978SHeiko Carstens 	}
64722938978SHeiko Carstens 	switch (asce.dt) {
64822938978SHeiko Carstens 	case ASCE_TYPE_REGION1:	{
64922938978SHeiko Carstens 		union region1_table_entry rfte;
65022938978SHeiko Carstens 
65122938978SHeiko Carstens 		if (kvm_is_error_gpa(vcpu->kvm, ptr))
65222938978SHeiko Carstens 			return PGM_ADDRESSING;
65322938978SHeiko Carstens 		if (deref_table(vcpu->kvm, ptr, &rfte.val))
65422938978SHeiko Carstens 			return -EFAULT;
65522938978SHeiko Carstens 		if (rfte.i)
65622938978SHeiko Carstens 			return PGM_REGION_FIRST_TRANS;
65722938978SHeiko Carstens 		if (rfte.tt != TABLE_TYPE_REGION1)
65822938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
65922938978SHeiko Carstens 		if (vaddr.rsx01 < rfte.tf || vaddr.rsx01 > rfte.tl)
66022938978SHeiko Carstens 			return PGM_REGION_SECOND_TRANS;
66122938978SHeiko Carstens 		if (edat1)
66222938978SHeiko Carstens 			dat_protection |= rfte.p;
66322938978SHeiko Carstens 		ptr = rfte.rto * 4096 + vaddr.rsx * 8;
66422938978SHeiko Carstens 	}
66522938978SHeiko Carstens 		/* fallthrough */
66622938978SHeiko Carstens 	case ASCE_TYPE_REGION2: {
66722938978SHeiko Carstens 		union region2_table_entry rste;
66822938978SHeiko Carstens 
66922938978SHeiko Carstens 		if (kvm_is_error_gpa(vcpu->kvm, ptr))
67022938978SHeiko Carstens 			return PGM_ADDRESSING;
67122938978SHeiko Carstens 		if (deref_table(vcpu->kvm, ptr, &rste.val))
67222938978SHeiko Carstens 			return -EFAULT;
67322938978SHeiko Carstens 		if (rste.i)
67422938978SHeiko Carstens 			return PGM_REGION_SECOND_TRANS;
67522938978SHeiko Carstens 		if (rste.tt != TABLE_TYPE_REGION2)
67622938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
67722938978SHeiko Carstens 		if (vaddr.rtx01 < rste.tf || vaddr.rtx01 > rste.tl)
67822938978SHeiko Carstens 			return PGM_REGION_THIRD_TRANS;
67922938978SHeiko Carstens 		if (edat1)
68022938978SHeiko Carstens 			dat_protection |= rste.p;
68122938978SHeiko Carstens 		ptr = rste.rto * 4096 + vaddr.rtx * 8;
68222938978SHeiko Carstens 	}
68322938978SHeiko Carstens 		/* fallthrough */
68422938978SHeiko Carstens 	case ASCE_TYPE_REGION3: {
68522938978SHeiko Carstens 		union region3_table_entry rtte;
68622938978SHeiko Carstens 
68722938978SHeiko Carstens 		if (kvm_is_error_gpa(vcpu->kvm, ptr))
68822938978SHeiko Carstens 			return PGM_ADDRESSING;
68922938978SHeiko Carstens 		if (deref_table(vcpu->kvm, ptr, &rtte.val))
69022938978SHeiko Carstens 			return -EFAULT;
69122938978SHeiko Carstens 		if (rtte.i)
69222938978SHeiko Carstens 			return PGM_REGION_THIRD_TRANS;
69322938978SHeiko Carstens 		if (rtte.tt != TABLE_TYPE_REGION3)
69422938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
69522938978SHeiko Carstens 		if (rtte.cr && asce.p && edat2)
69622938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
69722938978SHeiko Carstens 		if (rtte.fc && edat2) {
69822938978SHeiko Carstens 			dat_protection |= rtte.fc1.p;
69922938978SHeiko Carstens 			raddr.rfaa = rtte.fc1.rfaa;
70022938978SHeiko Carstens 			goto absolute_address;
70122938978SHeiko Carstens 		}
70222938978SHeiko Carstens 		if (vaddr.sx01 < rtte.fc0.tf)
70322938978SHeiko Carstens 			return PGM_SEGMENT_TRANSLATION;
70422938978SHeiko Carstens 		if (vaddr.sx01 > rtte.fc0.tl)
70522938978SHeiko Carstens 			return PGM_SEGMENT_TRANSLATION;
70622938978SHeiko Carstens 		if (edat1)
70722938978SHeiko Carstens 			dat_protection |= rtte.fc0.p;
70822938978SHeiko Carstens 		ptr = rtte.fc0.sto * 4096 + vaddr.sx * 8;
70922938978SHeiko Carstens 	}
71022938978SHeiko Carstens 		/* fallthrough */
71122938978SHeiko Carstens 	case ASCE_TYPE_SEGMENT: {
71222938978SHeiko Carstens 		union segment_table_entry ste;
71322938978SHeiko Carstens 
71422938978SHeiko Carstens 		if (kvm_is_error_gpa(vcpu->kvm, ptr))
71522938978SHeiko Carstens 			return PGM_ADDRESSING;
71622938978SHeiko Carstens 		if (deref_table(vcpu->kvm, ptr, &ste.val))
71722938978SHeiko Carstens 			return -EFAULT;
71822938978SHeiko Carstens 		if (ste.i)
71922938978SHeiko Carstens 			return PGM_SEGMENT_TRANSLATION;
72022938978SHeiko Carstens 		if (ste.tt != TABLE_TYPE_SEGMENT)
72122938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
72222938978SHeiko Carstens 		if (ste.cs && asce.p)
72322938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
72422938978SHeiko Carstens 		if (ste.fc && edat1) {
72522938978SHeiko Carstens 			dat_protection |= ste.fc1.p;
72622938978SHeiko Carstens 			raddr.sfaa = ste.fc1.sfaa;
72722938978SHeiko Carstens 			goto absolute_address;
72822938978SHeiko Carstens 		}
72922938978SHeiko Carstens 		dat_protection |= ste.fc0.p;
73022938978SHeiko Carstens 		ptr = ste.fc0.pto * 2048 + vaddr.px * 8;
73122938978SHeiko Carstens 	}
73222938978SHeiko Carstens 	}
73322938978SHeiko Carstens 	if (kvm_is_error_gpa(vcpu->kvm, ptr))
73422938978SHeiko Carstens 		return PGM_ADDRESSING;
73522938978SHeiko Carstens 	if (deref_table(vcpu->kvm, ptr, &pte.val))
73622938978SHeiko Carstens 		return -EFAULT;
73722938978SHeiko Carstens 	if (pte.i)
73822938978SHeiko Carstens 		return PGM_PAGE_TRANSLATION;
73922938978SHeiko Carstens 	if (pte.z)
74022938978SHeiko Carstens 		return PGM_TRANSLATION_SPEC;
74122938978SHeiko Carstens 	if (pte.co && !edat1)
74222938978SHeiko Carstens 		return PGM_TRANSLATION_SPEC;
74322938978SHeiko Carstens 	dat_protection |= pte.p;
74422938978SHeiko Carstens 	raddr.pfra = pte.pfra;
74522938978SHeiko Carstens real_address:
74622938978SHeiko Carstens 	raddr.addr = kvm_s390_real_to_abs(vcpu, raddr.addr);
74722938978SHeiko Carstens absolute_address:
74892c96321SDavid Hildenbrand 	if (mode == GACC_STORE && dat_protection)
74922938978SHeiko Carstens 		return PGM_PROTECTION;
75022938978SHeiko Carstens 	if (kvm_is_error_gpa(vcpu->kvm, raddr.addr))
75122938978SHeiko Carstens 		return PGM_ADDRESSING;
75222938978SHeiko Carstens 	*gpa = raddr.addr;
75322938978SHeiko Carstens 	return 0;
75422938978SHeiko Carstens }
75522938978SHeiko Carstens 
75622938978SHeiko Carstens static inline int is_low_address(unsigned long ga)
75722938978SHeiko Carstens {
75822938978SHeiko Carstens 	/* Check for address ranges 0..511 and 4096..4607 */
75922938978SHeiko Carstens 	return (ga & ~0x11fful) == 0;
76022938978SHeiko Carstens }
76122938978SHeiko Carstens 
76275a18122SAlexander Yarygin static int low_address_protection_enabled(struct kvm_vcpu *vcpu,
76375a18122SAlexander Yarygin 					  const union asce asce)
76422938978SHeiko Carstens {
76522938978SHeiko Carstens 	union ctlreg0 ctlreg0 = {.val = vcpu->arch.sie_block->gcr[0]};
76622938978SHeiko Carstens 	psw_t *psw = &vcpu->arch.sie_block->gpsw;
76722938978SHeiko Carstens 
76822938978SHeiko Carstens 	if (!ctlreg0.lap)
76922938978SHeiko Carstens 		return 0;
77022938978SHeiko Carstens 	if (psw_bits(*psw).t && asce.p)
77122938978SHeiko Carstens 		return 0;
77222938978SHeiko Carstens 	return 1;
77322938978SHeiko Carstens }
77422938978SHeiko Carstens 
775cde0dcfbSDavid Hildenbrand static int guest_page_range(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar,
77622938978SHeiko Carstens 			    unsigned long *pages, unsigned long nr_pages,
77792c96321SDavid Hildenbrand 			    const union asce asce, enum gacc_mode mode)
77822938978SHeiko Carstens {
77922938978SHeiko Carstens 	psw_t *psw = &vcpu->arch.sie_block->gpsw;
780cde0dcfbSDavid Hildenbrand 	int lap_enabled, rc = 0;
78122938978SHeiko Carstens 
78275a18122SAlexander Yarygin 	lap_enabled = low_address_protection_enabled(vcpu, asce);
78322938978SHeiko Carstens 	while (nr_pages) {
78422938978SHeiko Carstens 		ga = kvm_s390_logical_to_effective(vcpu, ga);
785cde0dcfbSDavid Hildenbrand 		if (mode == GACC_STORE && lap_enabled && is_low_address(ga))
786cde0dcfbSDavid Hildenbrand 			return trans_exc(vcpu, PGM_PROTECTION, ga, ar, mode,
787cde0dcfbSDavid Hildenbrand 					 PROT_TYPE_LA);
78822938978SHeiko Carstens 		ga &= PAGE_MASK;
78922938978SHeiko Carstens 		if (psw_bits(*psw).t) {
79092c96321SDavid Hildenbrand 			rc = guest_translate(vcpu, ga, pages, asce, mode);
79122938978SHeiko Carstens 			if (rc < 0)
79222938978SHeiko Carstens 				return rc;
79322938978SHeiko Carstens 		} else {
79422938978SHeiko Carstens 			*pages = kvm_s390_real_to_abs(vcpu, ga);
79522938978SHeiko Carstens 			if (kvm_is_error_gpa(vcpu->kvm, *pages))
796cde0dcfbSDavid Hildenbrand 				rc = PGM_ADDRESSING;
79722938978SHeiko Carstens 		}
798cde0dcfbSDavid Hildenbrand 		if (rc)
799cde0dcfbSDavid Hildenbrand 			return trans_exc(vcpu, rc, ga, ar, mode, PROT_TYPE_DAT);
80022938978SHeiko Carstens 		ga += PAGE_SIZE;
80122938978SHeiko Carstens 		pages++;
80222938978SHeiko Carstens 		nr_pages--;
80322938978SHeiko Carstens 	}
80422938978SHeiko Carstens 	return 0;
80522938978SHeiko Carstens }
80622938978SHeiko Carstens 
8078ae04b8fSAlexander Yarygin int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, ar_t ar, void *data,
80892c96321SDavid Hildenbrand 		 unsigned long len, enum gacc_mode mode)
80922938978SHeiko Carstens {
81022938978SHeiko Carstens 	psw_t *psw = &vcpu->arch.sie_block->gpsw;
81122938978SHeiko Carstens 	unsigned long _len, nr_pages, gpa, idx;
81222938978SHeiko Carstens 	unsigned long pages_array[2];
81322938978SHeiko Carstens 	unsigned long *pages;
8148a242234SHeiko Carstens 	int need_ipte_lock;
8158a242234SHeiko Carstens 	union asce asce;
81622938978SHeiko Carstens 	int rc;
81722938978SHeiko Carstens 
81822938978SHeiko Carstens 	if (!len)
81922938978SHeiko Carstens 		return 0;
8206167375bSDavid Hildenbrand 	ga = kvm_s390_logical_to_effective(vcpu, ga);
8216167375bSDavid Hildenbrand 	rc = get_vcpu_asce(vcpu, &asce, ga, ar, mode);
822664b4973SAlexander Yarygin 	if (rc)
823664b4973SAlexander Yarygin 		return rc;
82422938978SHeiko Carstens 	nr_pages = (((ga & ~PAGE_MASK) + len - 1) >> PAGE_SHIFT) + 1;
82522938978SHeiko Carstens 	pages = pages_array;
82622938978SHeiko Carstens 	if (nr_pages > ARRAY_SIZE(pages_array))
82722938978SHeiko Carstens 		pages = vmalloc(nr_pages * sizeof(unsigned long));
82822938978SHeiko Carstens 	if (!pages)
82922938978SHeiko Carstens 		return -ENOMEM;
8308a242234SHeiko Carstens 	need_ipte_lock = psw_bits(*psw).t && !asce.r;
8318a242234SHeiko Carstens 	if (need_ipte_lock)
8328a242234SHeiko Carstens 		ipte_lock(vcpu);
833cde0dcfbSDavid Hildenbrand 	rc = guest_page_range(vcpu, ga, ar, pages, nr_pages, asce, mode);
83422938978SHeiko Carstens 	for (idx = 0; idx < nr_pages && !rc; idx++) {
83522938978SHeiko Carstens 		gpa = *(pages + idx) + (ga & ~PAGE_MASK);
83622938978SHeiko Carstens 		_len = min(PAGE_SIZE - (gpa & ~PAGE_MASK), len);
83792c96321SDavid Hildenbrand 		if (mode == GACC_STORE)
83822938978SHeiko Carstens 			rc = kvm_write_guest(vcpu->kvm, gpa, data, _len);
83922938978SHeiko Carstens 		else
84022938978SHeiko Carstens 			rc = kvm_read_guest(vcpu->kvm, gpa, data, _len);
84122938978SHeiko Carstens 		len -= _len;
84222938978SHeiko Carstens 		ga += _len;
84322938978SHeiko Carstens 		data += _len;
84422938978SHeiko Carstens 	}
8458a242234SHeiko Carstens 	if (need_ipte_lock)
8468a242234SHeiko Carstens 		ipte_unlock(vcpu);
84722938978SHeiko Carstens 	if (nr_pages > ARRAY_SIZE(pages_array))
84822938978SHeiko Carstens 		vfree(pages);
84922938978SHeiko Carstens 	return rc;
85022938978SHeiko Carstens }
85122938978SHeiko Carstens 
85222938978SHeiko Carstens int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
85392c96321SDavid Hildenbrand 		      void *data, unsigned long len, enum gacc_mode mode)
85422938978SHeiko Carstens {
85522938978SHeiko Carstens 	unsigned long _len, gpa;
85622938978SHeiko Carstens 	int rc = 0;
85722938978SHeiko Carstens 
85822938978SHeiko Carstens 	while (len && !rc) {
85922938978SHeiko Carstens 		gpa = kvm_s390_real_to_abs(vcpu, gra);
86022938978SHeiko Carstens 		_len = min(PAGE_SIZE - (gpa & ~PAGE_MASK), len);
86192c96321SDavid Hildenbrand 		if (mode)
86222938978SHeiko Carstens 			rc = write_guest_abs(vcpu, gpa, data, _len);
86322938978SHeiko Carstens 		else
86422938978SHeiko Carstens 			rc = read_guest_abs(vcpu, gpa, data, _len);
86522938978SHeiko Carstens 		len -= _len;
86622938978SHeiko Carstens 		gra += _len;
86722938978SHeiko Carstens 		data += _len;
86822938978SHeiko Carstens 	}
86922938978SHeiko Carstens 	return rc;
87022938978SHeiko Carstens }
871f8232c8cSThomas Huth 
872f8232c8cSThomas Huth /**
8739fbc0276SThomas Huth  * guest_translate_address - translate guest logical into guest absolute address
8749fbc0276SThomas Huth  *
8759fbc0276SThomas Huth  * Parameter semantics are the same as the ones from guest_translate.
8769fbc0276SThomas Huth  * The memory contents at the guest address are not changed.
8779fbc0276SThomas Huth  *
8789fbc0276SThomas Huth  * Note: The IPTE lock is not taken during this function, so the caller
8799fbc0276SThomas Huth  * has to take care of this.
8809fbc0276SThomas Huth  */
8818ae04b8fSAlexander Yarygin int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar,
88292c96321SDavid Hildenbrand 			    unsigned long *gpa, enum gacc_mode mode)
8839fbc0276SThomas Huth {
8849fbc0276SThomas Huth 	psw_t *psw = &vcpu->arch.sie_block->gpsw;
8859fbc0276SThomas Huth 	union asce asce;
8869fbc0276SThomas Huth 	int rc;
8879fbc0276SThomas Huth 
8889fbc0276SThomas Huth 	gva = kvm_s390_logical_to_effective(vcpu, gva);
8896167375bSDavid Hildenbrand 	rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode);
890664b4973SAlexander Yarygin 	if (rc)
891664b4973SAlexander Yarygin 		return rc;
89275a18122SAlexander Yarygin 	if (is_low_address(gva) && low_address_protection_enabled(vcpu, asce)) {
893fbcb7d51SDavid Hildenbrand 		if (mode == GACC_STORE)
894fbcb7d51SDavid Hildenbrand 			return trans_exc(vcpu, PGM_PROTECTION, gva, 0,
895fbcb7d51SDavid Hildenbrand 					 mode, PROT_TYPE_LA);
8969fbc0276SThomas Huth 	}
8979fbc0276SThomas Huth 
8989fbc0276SThomas Huth 	if (psw_bits(*psw).t && !asce.r) {	/* Use DAT? */
89992c96321SDavid Hildenbrand 		rc = guest_translate(vcpu, gva, gpa, asce, mode);
900fbcb7d51SDavid Hildenbrand 		if (rc > 0)
901fbcb7d51SDavid Hildenbrand 			return trans_exc(vcpu, rc, gva, 0, mode, PROT_TYPE_DAT);
9029fbc0276SThomas Huth 	} else {
9039fbc0276SThomas Huth 		*gpa = kvm_s390_real_to_abs(vcpu, gva);
9049fbc0276SThomas Huth 		if (kvm_is_error_gpa(vcpu->kvm, *gpa))
905fbcb7d51SDavid Hildenbrand 			return trans_exc(vcpu, rc, gva, PGM_ADDRESSING, mode, 0);
9069fbc0276SThomas Huth 	}
9079fbc0276SThomas Huth 
9089fbc0276SThomas Huth 	return rc;
9099fbc0276SThomas Huth }
9109fbc0276SThomas Huth 
9119fbc0276SThomas Huth /**
91241408c28SThomas Huth  * check_gva_range - test a range of guest virtual addresses for accessibility
91341408c28SThomas Huth  */
91441408c28SThomas Huth int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, ar_t ar,
91592c96321SDavid Hildenbrand 		    unsigned long length, enum gacc_mode mode)
91641408c28SThomas Huth {
91741408c28SThomas Huth 	unsigned long gpa;
91841408c28SThomas Huth 	unsigned long currlen;
91941408c28SThomas Huth 	int rc = 0;
92041408c28SThomas Huth 
92141408c28SThomas Huth 	ipte_lock(vcpu);
92241408c28SThomas Huth 	while (length > 0 && !rc) {
92341408c28SThomas Huth 		currlen = min(length, PAGE_SIZE - (gva % PAGE_SIZE));
92492c96321SDavid Hildenbrand 		rc = guest_translate_address(vcpu, gva, ar, &gpa, mode);
92541408c28SThomas Huth 		gva += currlen;
92641408c28SThomas Huth 		length -= currlen;
92741408c28SThomas Huth 	}
92841408c28SThomas Huth 	ipte_unlock(vcpu);
92941408c28SThomas Huth 
93041408c28SThomas Huth 	return rc;
93141408c28SThomas Huth }
93241408c28SThomas Huth 
93341408c28SThomas Huth /**
934dd9e5b7bSAlexander Yarygin  * kvm_s390_check_low_addr_prot_real - check for low-address protection
935dd9e5b7bSAlexander Yarygin  * @gra: Guest real address
936f8232c8cSThomas Huth  *
937f8232c8cSThomas Huth  * Checks whether an address is subject to low-address protection and set
938f8232c8cSThomas Huth  * up vcpu->arch.pgm accordingly if necessary.
939f8232c8cSThomas Huth  *
940f8232c8cSThomas Huth  * Return: 0 if no protection exception, or PGM_PROTECTION if protected.
941f8232c8cSThomas Huth  */
942dd9e5b7bSAlexander Yarygin int kvm_s390_check_low_addr_prot_real(struct kvm_vcpu *vcpu, unsigned long gra)
943f8232c8cSThomas Huth {
944dd9e5b7bSAlexander Yarygin 	union ctlreg0 ctlreg0 = {.val = vcpu->arch.sie_block->gcr[0]};
945f8232c8cSThomas Huth 
946dd9e5b7bSAlexander Yarygin 	if (!ctlreg0.lap || !is_low_address(gra))
947f8232c8cSThomas Huth 		return 0;
9483e3c67f6SDavid Hildenbrand 	return trans_exc(vcpu, PGM_PROTECTION, gra, 0, GACC_STORE, PROT_TYPE_LA);
949f8232c8cSThomas Huth }
950aa17aa57SMartin Schwidefsky 
951aa17aa57SMartin Schwidefsky /**
952aa17aa57SMartin Schwidefsky  * kvm_s390_shadow_tables - walk the guest page table and create shadow tables
953aa17aa57SMartin Schwidefsky  * @sg: pointer to the shadow guest address space structure
954aa17aa57SMartin Schwidefsky  * @saddr: faulting address in the shadow gmap
955aa17aa57SMartin Schwidefsky  * @pgt: pointer to the page table address result
956fd8d4e3aSDavid Hildenbrand  * @fake: pgt references contiguous guest memory block, not a pgtable
957aa17aa57SMartin Schwidefsky  */
958aa17aa57SMartin Schwidefsky static int kvm_s390_shadow_tables(struct gmap *sg, unsigned long saddr,
959fd8d4e3aSDavid Hildenbrand 				  unsigned long *pgt, int *dat_protection,
960fd8d4e3aSDavid Hildenbrand 				  int *fake)
961aa17aa57SMartin Schwidefsky {
962aa17aa57SMartin Schwidefsky 	struct gmap *parent;
963aa17aa57SMartin Schwidefsky 	union asce asce;
964aa17aa57SMartin Schwidefsky 	union vaddress vaddr;
965aa17aa57SMartin Schwidefsky 	unsigned long ptr;
966aa17aa57SMartin Schwidefsky 	int rc;
967aa17aa57SMartin Schwidefsky 
968fd8d4e3aSDavid Hildenbrand 	*fake = 0;
9691c65781bSDavid Hildenbrand 	*dat_protection = 0;
970aa17aa57SMartin Schwidefsky 	parent = sg->parent;
971aa17aa57SMartin Schwidefsky 	vaddr.addr = saddr;
972aa17aa57SMartin Schwidefsky 	asce.val = sg->orig_asce;
973aa17aa57SMartin Schwidefsky 	ptr = asce.origin * 4096;
974*3218f709SDavid Hildenbrand 	if (asce.r) {
975*3218f709SDavid Hildenbrand 		*fake = 1;
976*3218f709SDavid Hildenbrand 		asce.dt = ASCE_TYPE_REGION1;
977*3218f709SDavid Hildenbrand 	}
978aa17aa57SMartin Schwidefsky 	switch (asce.dt) {
979aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION1:
980*3218f709SDavid Hildenbrand 		if (vaddr.rfx01 > asce.tl && !asce.r)
981aa17aa57SMartin Schwidefsky 			return PGM_REGION_FIRST_TRANS;
982aa17aa57SMartin Schwidefsky 		break;
983aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION2:
984aa17aa57SMartin Schwidefsky 		if (vaddr.rfx)
985aa17aa57SMartin Schwidefsky 			return PGM_ASCE_TYPE;
986aa17aa57SMartin Schwidefsky 		if (vaddr.rsx01 > asce.tl)
987aa17aa57SMartin Schwidefsky 			return PGM_REGION_SECOND_TRANS;
988aa17aa57SMartin Schwidefsky 		break;
989aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION3:
990aa17aa57SMartin Schwidefsky 		if (vaddr.rfx || vaddr.rsx)
991aa17aa57SMartin Schwidefsky 			return PGM_ASCE_TYPE;
992aa17aa57SMartin Schwidefsky 		if (vaddr.rtx01 > asce.tl)
993aa17aa57SMartin Schwidefsky 			return PGM_REGION_THIRD_TRANS;
994aa17aa57SMartin Schwidefsky 		break;
995aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_SEGMENT:
996aa17aa57SMartin Schwidefsky 		if (vaddr.rfx || vaddr.rsx || vaddr.rtx)
997aa17aa57SMartin Schwidefsky 			return PGM_ASCE_TYPE;
998aa17aa57SMartin Schwidefsky 		if (vaddr.sx01 > asce.tl)
999aa17aa57SMartin Schwidefsky 			return PGM_SEGMENT_TRANSLATION;
1000aa17aa57SMartin Schwidefsky 		break;
1001aa17aa57SMartin Schwidefsky 	}
1002aa17aa57SMartin Schwidefsky 
1003aa17aa57SMartin Schwidefsky 	switch (asce.dt) {
1004aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION1: {
1005aa17aa57SMartin Schwidefsky 		union region1_table_entry rfte;
1006aa17aa57SMartin Schwidefsky 
1007*3218f709SDavid Hildenbrand 		if (*fake) {
1008*3218f709SDavid Hildenbrand 			/* offset in 16EB guest memory block */
1009*3218f709SDavid Hildenbrand 			ptr = ptr + ((unsigned long) vaddr.rsx << 53UL);
1010*3218f709SDavid Hildenbrand 			rfte.val = ptr;
1011*3218f709SDavid Hildenbrand 			goto shadow_r2t;
1012*3218f709SDavid Hildenbrand 		}
1013aa17aa57SMartin Schwidefsky 		rc = gmap_read_table(parent, ptr + vaddr.rfx * 8, &rfte.val);
1014aa17aa57SMartin Schwidefsky 		if (rc)
1015aa17aa57SMartin Schwidefsky 			return rc;
1016aa17aa57SMartin Schwidefsky 		if (rfte.i)
1017aa17aa57SMartin Schwidefsky 			return PGM_REGION_FIRST_TRANS;
1018aa17aa57SMartin Schwidefsky 		if (rfte.tt != TABLE_TYPE_REGION1)
1019aa17aa57SMartin Schwidefsky 			return PGM_TRANSLATION_SPEC;
1020aa17aa57SMartin Schwidefsky 		if (vaddr.rsx01 < rfte.tf || vaddr.rsx01 > rfte.tl)
1021aa17aa57SMartin Schwidefsky 			return PGM_REGION_SECOND_TRANS;
10221c65781bSDavid Hildenbrand 		if (sg->edat_level >= 1)
10231c65781bSDavid Hildenbrand 			*dat_protection |= rfte.p;
1024*3218f709SDavid Hildenbrand 		ptr = rfte.rto << 12UL;
1025*3218f709SDavid Hildenbrand shadow_r2t:
1026*3218f709SDavid Hildenbrand 		rc = gmap_shadow_r2t(sg, saddr, rfte.val, *fake);
1027aa17aa57SMartin Schwidefsky 		if (rc)
1028aa17aa57SMartin Schwidefsky 			return rc;
1029aa17aa57SMartin Schwidefsky 		/* fallthrough */
1030aa17aa57SMartin Schwidefsky 	}
1031aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION2: {
1032aa17aa57SMartin Schwidefsky 		union region2_table_entry rste;
1033aa17aa57SMartin Schwidefsky 
1034*3218f709SDavid Hildenbrand 		if (*fake) {
1035*3218f709SDavid Hildenbrand 			/* offset in 8PB guest memory block */
1036*3218f709SDavid Hildenbrand 			ptr = ptr + ((unsigned long) vaddr.rtx << 42UL);
1037*3218f709SDavid Hildenbrand 			rste.val = ptr;
1038*3218f709SDavid Hildenbrand 			goto shadow_r3t;
1039*3218f709SDavid Hildenbrand 		}
1040aa17aa57SMartin Schwidefsky 		rc = gmap_read_table(parent, ptr + vaddr.rsx * 8, &rste.val);
1041aa17aa57SMartin Schwidefsky 		if (rc)
1042aa17aa57SMartin Schwidefsky 			return rc;
1043aa17aa57SMartin Schwidefsky 		if (rste.i)
1044aa17aa57SMartin Schwidefsky 			return PGM_REGION_SECOND_TRANS;
1045aa17aa57SMartin Schwidefsky 		if (rste.tt != TABLE_TYPE_REGION2)
1046aa17aa57SMartin Schwidefsky 			return PGM_TRANSLATION_SPEC;
1047aa17aa57SMartin Schwidefsky 		if (vaddr.rtx01 < rste.tf || vaddr.rtx01 > rste.tl)
1048aa17aa57SMartin Schwidefsky 			return PGM_REGION_THIRD_TRANS;
10491c65781bSDavid Hildenbrand 		if (sg->edat_level >= 1)
10501c65781bSDavid Hildenbrand 			*dat_protection |= rste.p;
1051*3218f709SDavid Hildenbrand 		ptr = rste.rto << 12UL;
1052*3218f709SDavid Hildenbrand shadow_r3t:
10531c65781bSDavid Hildenbrand 		rste.p |= *dat_protection;
1054*3218f709SDavid Hildenbrand 		rc = gmap_shadow_r3t(sg, saddr, rste.val, *fake);
1055aa17aa57SMartin Schwidefsky 		if (rc)
1056aa17aa57SMartin Schwidefsky 			return rc;
1057aa17aa57SMartin Schwidefsky 		/* fallthrough */
1058aa17aa57SMartin Schwidefsky 	}
1059aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION3: {
1060aa17aa57SMartin Schwidefsky 		union region3_table_entry rtte;
1061aa17aa57SMartin Schwidefsky 
1062*3218f709SDavid Hildenbrand 		if (*fake) {
1063*3218f709SDavid Hildenbrand 			/* offset in 4TB guest memory block */
1064*3218f709SDavid Hildenbrand 			ptr = ptr + ((unsigned long) vaddr.sx << 31UL);
1065*3218f709SDavid Hildenbrand 			rtte.val = ptr;
1066*3218f709SDavid Hildenbrand 			goto shadow_sgt;
1067*3218f709SDavid Hildenbrand 		}
1068aa17aa57SMartin Schwidefsky 		rc = gmap_read_table(parent, ptr + vaddr.rtx * 8, &rtte.val);
1069aa17aa57SMartin Schwidefsky 		if (rc)
1070aa17aa57SMartin Schwidefsky 			return rc;
1071aa17aa57SMartin Schwidefsky 		if (rtte.i)
1072aa17aa57SMartin Schwidefsky 			return PGM_REGION_THIRD_TRANS;
1073aa17aa57SMartin Schwidefsky 		if (rtte.tt != TABLE_TYPE_REGION3)
1074aa17aa57SMartin Schwidefsky 			return PGM_TRANSLATION_SPEC;
107518b89809SDavid Hildenbrand 		if (rtte.cr && asce.p && sg->edat_level >= 2)
107618b89809SDavid Hildenbrand 			return PGM_TRANSLATION_SPEC;
107718b89809SDavid Hildenbrand 		if (rtte.fc && sg->edat_level >= 2) {
10781c65781bSDavid Hildenbrand 			*dat_protection |= rtte.fc0.p;
107918b89809SDavid Hildenbrand 			*fake = 1;
108018b89809SDavid Hildenbrand 			ptr = rtte.fc1.rfaa << 31UL;
108118b89809SDavid Hildenbrand 			rtte.val = ptr;
108218b89809SDavid Hildenbrand 			goto shadow_sgt;
108318b89809SDavid Hildenbrand 		}
1084aa17aa57SMartin Schwidefsky 		if (vaddr.sx01 < rtte.fc0.tf || vaddr.sx01 > rtte.fc0.tl)
1085aa17aa57SMartin Schwidefsky 			return PGM_SEGMENT_TRANSLATION;
10861c65781bSDavid Hildenbrand 		if (sg->edat_level >= 1)
10871c65781bSDavid Hildenbrand 			*dat_protection |= rtte.fc0.p;
108818b89809SDavid Hildenbrand 		ptr = rtte.fc0.sto << 12UL;
108918b89809SDavid Hildenbrand shadow_sgt:
10901c65781bSDavid Hildenbrand 		rtte.fc0.p |= *dat_protection;
109118b89809SDavid Hildenbrand 		rc = gmap_shadow_sgt(sg, saddr, rtte.val, *fake);
1092aa17aa57SMartin Schwidefsky 		if (rc)
1093aa17aa57SMartin Schwidefsky 			return rc;
1094aa17aa57SMartin Schwidefsky 		/* fallthrough */
1095aa17aa57SMartin Schwidefsky 	}
1096aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_SEGMENT: {
1097aa17aa57SMartin Schwidefsky 		union segment_table_entry ste;
1098aa17aa57SMartin Schwidefsky 
109918b89809SDavid Hildenbrand 		if (*fake) {
110018b89809SDavid Hildenbrand 			/* offset in 2G guest memory block */
110118b89809SDavid Hildenbrand 			ptr = ptr + ((unsigned long) vaddr.sx << 20UL);
110218b89809SDavid Hildenbrand 			ste.val = ptr;
110318b89809SDavid Hildenbrand 			goto shadow_pgt;
110418b89809SDavid Hildenbrand 		}
1105aa17aa57SMartin Schwidefsky 		rc = gmap_read_table(parent, ptr + vaddr.sx * 8, &ste.val);
1106aa17aa57SMartin Schwidefsky 		if (rc)
1107aa17aa57SMartin Schwidefsky 			return rc;
1108aa17aa57SMartin Schwidefsky 		if (ste.i)
1109aa17aa57SMartin Schwidefsky 			return PGM_SEGMENT_TRANSLATION;
1110aa17aa57SMartin Schwidefsky 		if (ste.tt != TABLE_TYPE_SEGMENT)
1111aa17aa57SMartin Schwidefsky 			return PGM_TRANSLATION_SPEC;
1112aa17aa57SMartin Schwidefsky 		if (ste.cs && asce.p)
1113aa17aa57SMartin Schwidefsky 			return PGM_TRANSLATION_SPEC;
11141c65781bSDavid Hildenbrand 		*dat_protection |= ste.fc0.p;
1115fd8d4e3aSDavid Hildenbrand 		if (ste.fc && sg->edat_level >= 1) {
1116fd8d4e3aSDavid Hildenbrand 			*fake = 1;
1117fd8d4e3aSDavid Hildenbrand 			ptr = ste.fc1.sfaa << 20UL;
1118fd8d4e3aSDavid Hildenbrand 			ste.val = ptr;
1119fd8d4e3aSDavid Hildenbrand 			goto shadow_pgt;
1120fd8d4e3aSDavid Hildenbrand 		}
1121fd8d4e3aSDavid Hildenbrand 		ptr = ste.fc0.pto << 11UL;
1122fd8d4e3aSDavid Hildenbrand shadow_pgt:
11231c65781bSDavid Hildenbrand 		ste.fc0.p |= *dat_protection;
1124fd8d4e3aSDavid Hildenbrand 		rc = gmap_shadow_pgt(sg, saddr, ste.val, *fake);
1125aa17aa57SMartin Schwidefsky 		if (rc)
1126aa17aa57SMartin Schwidefsky 			return rc;
1127aa17aa57SMartin Schwidefsky 	}
1128aa17aa57SMartin Schwidefsky 	}
1129aa17aa57SMartin Schwidefsky 	/* Return the parent address of the page table */
1130aa17aa57SMartin Schwidefsky 	*pgt = ptr;
1131aa17aa57SMartin Schwidefsky 	return 0;
1132aa17aa57SMartin Schwidefsky }
1133aa17aa57SMartin Schwidefsky 
1134aa17aa57SMartin Schwidefsky /**
1135aa17aa57SMartin Schwidefsky  * kvm_s390_shadow_fault - handle fault on a shadow page table
1136f4debb40SDavid Hildenbrand  * @vcpu: virtual cpu
1137aa17aa57SMartin Schwidefsky  * @sg: pointer to the shadow guest address space structure
1138aa17aa57SMartin Schwidefsky  * @saddr: faulting address in the shadow gmap
1139aa17aa57SMartin Schwidefsky  *
1140aa17aa57SMartin Schwidefsky  * Returns: - 0 if the shadow fault was successfully resolved
1141aa17aa57SMartin Schwidefsky  *	    - > 0 (pgm exception code) on exceptions while faulting
1142aa17aa57SMartin Schwidefsky  *	    - -EAGAIN if the caller can retry immediately
1143aa17aa57SMartin Schwidefsky  *	    - -EFAULT when accessing invalid guest addresses
1144aa17aa57SMartin Schwidefsky  *	    - -ENOMEM if out of memory
1145aa17aa57SMartin Schwidefsky  */
1146f4debb40SDavid Hildenbrand int kvm_s390_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg,
1147f4debb40SDavid Hildenbrand 			  unsigned long saddr)
1148aa17aa57SMartin Schwidefsky {
1149aa17aa57SMartin Schwidefsky 	union vaddress vaddr;
1150aa17aa57SMartin Schwidefsky 	union page_table_entry pte;
1151aa17aa57SMartin Schwidefsky 	unsigned long pgt;
1152fd8d4e3aSDavid Hildenbrand 	int dat_protection, fake;
1153aa17aa57SMartin Schwidefsky 	int rc;
1154aa17aa57SMartin Schwidefsky 
1155e52f8b61SDavid Hildenbrand 	down_read(&sg->mm->mmap_sem);
1156f4debb40SDavid Hildenbrand 	/*
1157f4debb40SDavid Hildenbrand 	 * We don't want any guest-2 tables to change - so the parent
1158f4debb40SDavid Hildenbrand 	 * tables/pointers we read stay valid - unshadowing is however
1159f4debb40SDavid Hildenbrand 	 * always possible - only guest_table_lock protects us.
1160f4debb40SDavid Hildenbrand 	 */
1161f4debb40SDavid Hildenbrand 	ipte_lock(vcpu);
1162e52f8b61SDavid Hildenbrand 
1163fd8d4e3aSDavid Hildenbrand 	rc = gmap_shadow_pgt_lookup(sg, saddr, &pgt, &dat_protection, &fake);
1164aa17aa57SMartin Schwidefsky 	if (rc)
1165fd8d4e3aSDavid Hildenbrand 		rc = kvm_s390_shadow_tables(sg, saddr, &pgt, &dat_protection,
1166fd8d4e3aSDavid Hildenbrand 					    &fake);
1167aa17aa57SMartin Schwidefsky 
1168aa17aa57SMartin Schwidefsky 	vaddr.addr = saddr;
1169fd8d4e3aSDavid Hildenbrand 	if (fake) {
1170fd8d4e3aSDavid Hildenbrand 		/* offset in 1MB guest memory block */
1171fd8d4e3aSDavid Hildenbrand 		pte.val = pgt + ((unsigned long) vaddr.px << 12UL);
1172fd8d4e3aSDavid Hildenbrand 		goto shadow_page;
1173fd8d4e3aSDavid Hildenbrand 	}
1174e52f8b61SDavid Hildenbrand 	if (!rc)
1175aa17aa57SMartin Schwidefsky 		rc = gmap_read_table(sg->parent, pgt + vaddr.px * 8, &pte.val);
1176e52f8b61SDavid Hildenbrand 	if (!rc && pte.i)
1177e52f8b61SDavid Hildenbrand 		rc = PGM_PAGE_TRANSLATION;
1178fd8d4e3aSDavid Hildenbrand 	if (!rc && (pte.z || (pte.co && sg->edat_level < 1)))
1179e52f8b61SDavid Hildenbrand 		rc = PGM_TRANSLATION_SPEC;
1180fd8d4e3aSDavid Hildenbrand shadow_page:
118100fc062dSDavid Hildenbrand 	pte.p |= dat_protection;
1182e52f8b61SDavid Hildenbrand 	if (!rc)
1183a9d23e71SDavid Hildenbrand 		rc = gmap_shadow_page(sg, saddr, __pte(pte.val));
1184f4debb40SDavid Hildenbrand 	ipte_unlock(vcpu);
1185e52f8b61SDavid Hildenbrand 	up_read(&sg->mm->mmap_sem);
1186aa17aa57SMartin Schwidefsky 	return rc;
1187aa17aa57SMartin Schwidefsky }
1188