xref: /linux/arch/s390/kvm/gaccess.c (revision 304103736ba75be70991cd6336c28121d7bf14f5)
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>
14*30410373SHeiko Carstens #include <asm/access-regs.h>
1544ae7663SHeiko Carstens #include <asm/fault.h>
16aa17aa57SMartin Schwidefsky #include <asm/gmap.h>
1722938978SHeiko Carstens #include "kvm-s390.h"
1822938978SHeiko Carstens #include "gaccess.h"
1922938978SHeiko Carstens 
2022938978SHeiko Carstens union asce {
2122938978SHeiko Carstens 	unsigned long val;
2222938978SHeiko Carstens 	struct {
2322938978SHeiko Carstens 		unsigned long origin : 52; /* Region- or Segment-Table Origin */
2422938978SHeiko Carstens 		unsigned long	 : 2;
2522938978SHeiko Carstens 		unsigned long g  : 1; /* Subspace Group Control */
2622938978SHeiko Carstens 		unsigned long p  : 1; /* Private Space Control */
2722938978SHeiko Carstens 		unsigned long s  : 1; /* Storage-Alteration-Event Control */
2822938978SHeiko Carstens 		unsigned long x  : 1; /* Space-Switch-Event Control */
2922938978SHeiko Carstens 		unsigned long r  : 1; /* Real-Space Control */
3022938978SHeiko Carstens 		unsigned long	 : 1;
3122938978SHeiko Carstens 		unsigned long dt : 2; /* Designation-Type Control */
3222938978SHeiko Carstens 		unsigned long tl : 2; /* Region- or Segment-Table Length */
3322938978SHeiko Carstens 	};
3422938978SHeiko Carstens };
3522938978SHeiko Carstens 
3622938978SHeiko Carstens enum {
3722938978SHeiko Carstens 	ASCE_TYPE_SEGMENT = 0,
3822938978SHeiko Carstens 	ASCE_TYPE_REGION3 = 1,
3922938978SHeiko Carstens 	ASCE_TYPE_REGION2 = 2,
4022938978SHeiko Carstens 	ASCE_TYPE_REGION1 = 3
4122938978SHeiko Carstens };
4222938978SHeiko Carstens 
4322938978SHeiko Carstens union region1_table_entry {
4422938978SHeiko Carstens 	unsigned long val;
4522938978SHeiko Carstens 	struct {
4622938978SHeiko Carstens 		unsigned long rto: 52;/* Region-Table Origin */
4722938978SHeiko Carstens 		unsigned long	 : 2;
4822938978SHeiko Carstens 		unsigned long p  : 1; /* DAT-Protection Bit */
4922938978SHeiko Carstens 		unsigned long	 : 1;
5022938978SHeiko Carstens 		unsigned long tf : 2; /* Region-Second-Table Offset */
5122938978SHeiko Carstens 		unsigned long i  : 1; /* Region-Invalid Bit */
5222938978SHeiko Carstens 		unsigned long	 : 1;
5322938978SHeiko Carstens 		unsigned long tt : 2; /* Table-Type Bits */
5422938978SHeiko Carstens 		unsigned long tl : 2; /* Region-Second-Table Length */
5522938978SHeiko Carstens 	};
5622938978SHeiko Carstens };
5722938978SHeiko Carstens 
5822938978SHeiko Carstens union region2_table_entry {
5922938978SHeiko Carstens 	unsigned long val;
6022938978SHeiko Carstens 	struct {
6122938978SHeiko Carstens 		unsigned long rto: 52;/* Region-Table Origin */
6222938978SHeiko Carstens 		unsigned long	 : 2;
6322938978SHeiko Carstens 		unsigned long p  : 1; /* DAT-Protection Bit */
6422938978SHeiko Carstens 		unsigned long	 : 1;
6522938978SHeiko Carstens 		unsigned long tf : 2; /* Region-Third-Table Offset */
6622938978SHeiko Carstens 		unsigned long i  : 1; /* Region-Invalid Bit */
6722938978SHeiko Carstens 		unsigned long	 : 1;
6822938978SHeiko Carstens 		unsigned long tt : 2; /* Table-Type Bits */
6922938978SHeiko Carstens 		unsigned long tl : 2; /* Region-Third-Table Length */
7022938978SHeiko Carstens 	};
7122938978SHeiko Carstens };
7222938978SHeiko Carstens 
7322938978SHeiko Carstens struct region3_table_entry_fc0 {
7422938978SHeiko Carstens 	unsigned long sto: 52;/* Segment-Table Origin */
7522938978SHeiko Carstens 	unsigned long	 : 1;
7622938978SHeiko Carstens 	unsigned long fc : 1; /* Format-Control */
7722938978SHeiko Carstens 	unsigned long p  : 1; /* DAT-Protection Bit */
7822938978SHeiko Carstens 	unsigned long	 : 1;
7922938978SHeiko Carstens 	unsigned long tf : 2; /* Segment-Table Offset */
8022938978SHeiko Carstens 	unsigned long i  : 1; /* Region-Invalid Bit */
8122938978SHeiko Carstens 	unsigned long cr : 1; /* Common-Region Bit */
8222938978SHeiko Carstens 	unsigned long tt : 2; /* Table-Type Bits */
8322938978SHeiko Carstens 	unsigned long tl : 2; /* Segment-Table Length */
8422938978SHeiko Carstens };
8522938978SHeiko Carstens 
8622938978SHeiko Carstens struct region3_table_entry_fc1 {
8722938978SHeiko Carstens 	unsigned long rfaa : 33; /* Region-Frame Absolute Address */
8822938978SHeiko Carstens 	unsigned long	 : 14;
8922938978SHeiko Carstens 	unsigned long av : 1; /* ACCF-Validity Control */
9022938978SHeiko Carstens 	unsigned long acc: 4; /* Access-Control Bits */
9122938978SHeiko Carstens 	unsigned long f  : 1; /* Fetch-Protection Bit */
9222938978SHeiko Carstens 	unsigned long fc : 1; /* Format-Control */
9322938978SHeiko Carstens 	unsigned long p  : 1; /* DAT-Protection Bit */
946ae1574cSChristian Borntraeger 	unsigned long iep: 1; /* Instruction-Execution-Protection */
9522938978SHeiko Carstens 	unsigned long	 : 2;
9622938978SHeiko Carstens 	unsigned long i  : 1; /* Region-Invalid Bit */
9722938978SHeiko Carstens 	unsigned long cr : 1; /* Common-Region Bit */
9822938978SHeiko Carstens 	unsigned long tt : 2; /* Table-Type Bits */
9922938978SHeiko Carstens 	unsigned long	 : 2;
10022938978SHeiko Carstens };
10122938978SHeiko Carstens 
10222938978SHeiko Carstens union region3_table_entry {
10322938978SHeiko Carstens 	unsigned long val;
10422938978SHeiko Carstens 	struct region3_table_entry_fc0 fc0;
10522938978SHeiko Carstens 	struct region3_table_entry_fc1 fc1;
10622938978SHeiko Carstens 	struct {
10722938978SHeiko Carstens 		unsigned long	 : 53;
10822938978SHeiko Carstens 		unsigned long fc : 1; /* Format-Control */
10922938978SHeiko Carstens 		unsigned long	 : 4;
11022938978SHeiko Carstens 		unsigned long i  : 1; /* Region-Invalid Bit */
11122938978SHeiko Carstens 		unsigned long cr : 1; /* Common-Region Bit */
11222938978SHeiko Carstens 		unsigned long tt : 2; /* Table-Type Bits */
11322938978SHeiko Carstens 		unsigned long	 : 2;
11422938978SHeiko Carstens 	};
11522938978SHeiko Carstens };
11622938978SHeiko Carstens 
11722938978SHeiko Carstens struct segment_entry_fc0 {
11822938978SHeiko Carstens 	unsigned long pto: 53;/* Page-Table Origin */
11922938978SHeiko Carstens 	unsigned long fc : 1; /* Format-Control */
12022938978SHeiko Carstens 	unsigned long p  : 1; /* DAT-Protection Bit */
12122938978SHeiko Carstens 	unsigned long	 : 3;
12222938978SHeiko Carstens 	unsigned long i  : 1; /* Segment-Invalid Bit */
12322938978SHeiko Carstens 	unsigned long cs : 1; /* Common-Segment Bit */
12422938978SHeiko Carstens 	unsigned long tt : 2; /* Table-Type Bits */
12522938978SHeiko Carstens 	unsigned long	 : 2;
12622938978SHeiko Carstens };
12722938978SHeiko Carstens 
12822938978SHeiko Carstens struct segment_entry_fc1 {
12922938978SHeiko Carstens 	unsigned long sfaa : 44; /* Segment-Frame Absolute Address */
13022938978SHeiko Carstens 	unsigned long	 : 3;
13122938978SHeiko Carstens 	unsigned long av : 1; /* ACCF-Validity Control */
13222938978SHeiko Carstens 	unsigned long acc: 4; /* Access-Control Bits */
13322938978SHeiko Carstens 	unsigned long f  : 1; /* Fetch-Protection Bit */
13422938978SHeiko Carstens 	unsigned long fc : 1; /* Format-Control */
13522938978SHeiko Carstens 	unsigned long p  : 1; /* DAT-Protection Bit */
1366ae1574cSChristian Borntraeger 	unsigned long iep: 1; /* Instruction-Execution-Protection */
13722938978SHeiko Carstens 	unsigned long	 : 2;
13822938978SHeiko Carstens 	unsigned long i  : 1; /* Segment-Invalid Bit */
13922938978SHeiko Carstens 	unsigned long cs : 1; /* Common-Segment Bit */
14022938978SHeiko Carstens 	unsigned long tt : 2; /* Table-Type Bits */
14122938978SHeiko Carstens 	unsigned long	 : 2;
14222938978SHeiko Carstens };
14322938978SHeiko Carstens 
14422938978SHeiko Carstens union segment_table_entry {
14522938978SHeiko Carstens 	unsigned long val;
14622938978SHeiko Carstens 	struct segment_entry_fc0 fc0;
14722938978SHeiko Carstens 	struct segment_entry_fc1 fc1;
14822938978SHeiko Carstens 	struct {
14922938978SHeiko Carstens 		unsigned long	 : 53;
15022938978SHeiko Carstens 		unsigned long fc : 1; /* Format-Control */
15122938978SHeiko Carstens 		unsigned long	 : 4;
15222938978SHeiko Carstens 		unsigned long i  : 1; /* Segment-Invalid Bit */
15322938978SHeiko Carstens 		unsigned long cs : 1; /* Common-Segment Bit */
15422938978SHeiko Carstens 		unsigned long tt : 2; /* Table-Type Bits */
15522938978SHeiko Carstens 		unsigned long	 : 2;
15622938978SHeiko Carstens 	};
15722938978SHeiko Carstens };
15822938978SHeiko Carstens 
15922938978SHeiko Carstens enum {
16022938978SHeiko Carstens 	TABLE_TYPE_SEGMENT = 0,
16122938978SHeiko Carstens 	TABLE_TYPE_REGION3 = 1,
16222938978SHeiko Carstens 	TABLE_TYPE_REGION2 = 2,
16322938978SHeiko Carstens 	TABLE_TYPE_REGION1 = 3
16422938978SHeiko Carstens };
16522938978SHeiko Carstens 
16622938978SHeiko Carstens union page_table_entry {
16722938978SHeiko Carstens 	unsigned long val;
16822938978SHeiko Carstens 	struct {
16922938978SHeiko Carstens 		unsigned long pfra : 52; /* Page-Frame Real Address */
17022938978SHeiko Carstens 		unsigned long z  : 1; /* Zero Bit */
17122938978SHeiko Carstens 		unsigned long i  : 1; /* Page-Invalid Bit */
17222938978SHeiko Carstens 		unsigned long p  : 1; /* DAT-Protection Bit */
1736ae1574cSChristian Borntraeger 		unsigned long iep: 1; /* Instruction-Execution-Protection */
1746ae1574cSChristian Borntraeger 		unsigned long	 : 8;
17522938978SHeiko Carstens 	};
17622938978SHeiko Carstens };
17722938978SHeiko Carstens 
17822938978SHeiko Carstens /*
17922938978SHeiko Carstens  * vaddress union in order to easily decode a virtual address into its
18022938978SHeiko Carstens  * region first index, region second index etc. parts.
18122938978SHeiko Carstens  */
18222938978SHeiko Carstens union vaddress {
18322938978SHeiko Carstens 	unsigned long addr;
18422938978SHeiko Carstens 	struct {
18522938978SHeiko Carstens 		unsigned long rfx : 11;
18622938978SHeiko Carstens 		unsigned long rsx : 11;
18722938978SHeiko Carstens 		unsigned long rtx : 11;
18822938978SHeiko Carstens 		unsigned long sx  : 11;
18922938978SHeiko Carstens 		unsigned long px  : 8;
19022938978SHeiko Carstens 		unsigned long bx  : 12;
19122938978SHeiko Carstens 	};
19222938978SHeiko Carstens 	struct {
19322938978SHeiko Carstens 		unsigned long rfx01 : 2;
19422938978SHeiko Carstens 		unsigned long	    : 9;
19522938978SHeiko Carstens 		unsigned long rsx01 : 2;
19622938978SHeiko Carstens 		unsigned long	    : 9;
19722938978SHeiko Carstens 		unsigned long rtx01 : 2;
19822938978SHeiko Carstens 		unsigned long	    : 9;
19922938978SHeiko Carstens 		unsigned long sx01  : 2;
20022938978SHeiko Carstens 		unsigned long	    : 29;
20122938978SHeiko Carstens 	};
20222938978SHeiko Carstens };
20322938978SHeiko Carstens 
20422938978SHeiko Carstens /*
20522938978SHeiko Carstens  * raddress union which will contain the result (real or absolute address)
20622938978SHeiko Carstens  * after a page table walk. The rfaa, sfaa and pfra members are used to
20722938978SHeiko Carstens  * simply assign them the value of a region, segment or page table entry.
20822938978SHeiko Carstens  */
20922938978SHeiko Carstens union raddress {
21022938978SHeiko Carstens 	unsigned long addr;
21122938978SHeiko Carstens 	unsigned long rfaa : 33; /* Region-Frame Absolute Address */
21222938978SHeiko Carstens 	unsigned long sfaa : 44; /* Segment-Frame Absolute Address */
21322938978SHeiko Carstens 	unsigned long pfra : 52; /* Page-Frame Real Address */
21422938978SHeiko Carstens };
21522938978SHeiko Carstens 
216664b4973SAlexander Yarygin union alet {
217664b4973SAlexander Yarygin 	u32 val;
218664b4973SAlexander Yarygin 	struct {
219664b4973SAlexander Yarygin 		u32 reserved : 7;
220664b4973SAlexander Yarygin 		u32 p        : 1;
221664b4973SAlexander Yarygin 		u32 alesn    : 8;
222664b4973SAlexander Yarygin 		u32 alen     : 16;
223664b4973SAlexander Yarygin 	};
224664b4973SAlexander Yarygin };
225664b4973SAlexander Yarygin 
226664b4973SAlexander Yarygin union ald {
227664b4973SAlexander Yarygin 	u32 val;
228664b4973SAlexander Yarygin 	struct {
229664b4973SAlexander Yarygin 		u32     : 1;
230664b4973SAlexander Yarygin 		u32 alo : 24;
231664b4973SAlexander Yarygin 		u32 all : 7;
232664b4973SAlexander Yarygin 	};
233664b4973SAlexander Yarygin };
234664b4973SAlexander Yarygin 
235664b4973SAlexander Yarygin struct ale {
236664b4973SAlexander Yarygin 	unsigned long i      : 1; /* ALEN-Invalid Bit */
237664b4973SAlexander Yarygin 	unsigned long        : 5;
238664b4973SAlexander Yarygin 	unsigned long fo     : 1; /* Fetch-Only Bit */
239664b4973SAlexander Yarygin 	unsigned long p      : 1; /* Private Bit */
240664b4973SAlexander Yarygin 	unsigned long alesn  : 8; /* Access-List-Entry Sequence Number */
241664b4973SAlexander Yarygin 	unsigned long aleax  : 16; /* Access-List-Entry Authorization Index */
242664b4973SAlexander Yarygin 	unsigned long        : 32;
243664b4973SAlexander Yarygin 	unsigned long        : 1;
244664b4973SAlexander Yarygin 	unsigned long asteo  : 25; /* ASN-Second-Table-Entry Origin */
245664b4973SAlexander Yarygin 	unsigned long        : 6;
246664b4973SAlexander Yarygin 	unsigned long astesn : 32; /* ASTE Sequence Number */
2471cae0255SMartin Schwidefsky };
248664b4973SAlexander Yarygin 
249664b4973SAlexander Yarygin struct aste {
250664b4973SAlexander Yarygin 	unsigned long i      : 1; /* ASX-Invalid Bit */
251664b4973SAlexander Yarygin 	unsigned long ato    : 29; /* Authority-Table Origin */
252664b4973SAlexander Yarygin 	unsigned long        : 1;
253664b4973SAlexander Yarygin 	unsigned long b      : 1; /* Base-Space Bit */
254664b4973SAlexander Yarygin 	unsigned long ax     : 16; /* Authorization Index */
255664b4973SAlexander Yarygin 	unsigned long atl    : 12; /* Authority-Table Length */
256664b4973SAlexander Yarygin 	unsigned long        : 2;
257664b4973SAlexander Yarygin 	unsigned long ca     : 1; /* Controlled-ASN Bit */
258664b4973SAlexander Yarygin 	unsigned long ra     : 1; /* Reusable-ASN Bit */
259664b4973SAlexander Yarygin 	unsigned long asce   : 64; /* Address-Space-Control Element */
260664b4973SAlexander Yarygin 	unsigned long ald    : 32;
261664b4973SAlexander Yarygin 	unsigned long astesn : 32;
262664b4973SAlexander Yarygin 	/* .. more fields there */
2631cae0255SMartin Schwidefsky };
2648a242234SHeiko Carstens 
2650130337eSPierre Morel int ipte_lock_held(struct kvm *kvm)
2668a242234SHeiko Carstens {
2670130337eSPierre Morel 	if (sclp.has_siif) {
2685e044315SEugene (jno) Dvurechenski 		int rc;
2698a242234SHeiko Carstens 
2700130337eSPierre Morel 		read_lock(&kvm->arch.sca_lock);
2710130337eSPierre Morel 		rc = kvm_s390_get_ipte_control(kvm)->kh != 0;
2720130337eSPierre Morel 		read_unlock(&kvm->arch.sca_lock);
2735e044315SEugene (jno) Dvurechenski 		return rc;
2745e044315SEugene (jno) Dvurechenski 	}
2750130337eSPierre Morel 	return kvm->arch.ipte_lock_count != 0;
2768a242234SHeiko Carstens }
2778a242234SHeiko Carstens 
2780130337eSPierre Morel static void ipte_lock_simple(struct kvm *kvm)
2798a242234SHeiko Carstens {
2808a242234SHeiko Carstens 	union ipte_control old, new, *ic;
2818a242234SHeiko Carstens 
2820130337eSPierre Morel 	mutex_lock(&kvm->arch.ipte_mutex);
2830130337eSPierre Morel 	kvm->arch.ipte_lock_count++;
2840130337eSPierre Morel 	if (kvm->arch.ipte_lock_count > 1)
2858a242234SHeiko Carstens 		goto out;
2865e044315SEugene (jno) Dvurechenski retry:
2870130337eSPierre Morel 	read_lock(&kvm->arch.sca_lock);
2880130337eSPierre Morel 	ic = kvm_s390_get_ipte_control(kvm);
2898a242234SHeiko Carstens 	do {
2905de72a22SChristian Borntraeger 		old = READ_ONCE(*ic);
2915e044315SEugene (jno) Dvurechenski 		if (old.k) {
2920130337eSPierre Morel 			read_unlock(&kvm->arch.sca_lock);
2938a242234SHeiko Carstens 			cond_resched();
2945e044315SEugene (jno) Dvurechenski 			goto retry;
2958a242234SHeiko Carstens 		}
2968a242234SHeiko Carstens 		new = old;
2978a242234SHeiko Carstens 		new.k = 1;
2988a242234SHeiko Carstens 	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
2990130337eSPierre Morel 	read_unlock(&kvm->arch.sca_lock);
3008a242234SHeiko Carstens out:
3010130337eSPierre Morel 	mutex_unlock(&kvm->arch.ipte_mutex);
3028a242234SHeiko Carstens }
3038a242234SHeiko Carstens 
3040130337eSPierre Morel static void ipte_unlock_simple(struct kvm *kvm)
3058a242234SHeiko Carstens {
3068a242234SHeiko Carstens 	union ipte_control old, new, *ic;
3078a242234SHeiko Carstens 
3080130337eSPierre Morel 	mutex_lock(&kvm->arch.ipte_mutex);
3090130337eSPierre Morel 	kvm->arch.ipte_lock_count--;
3100130337eSPierre Morel 	if (kvm->arch.ipte_lock_count)
3118a242234SHeiko Carstens 		goto out;
3120130337eSPierre Morel 	read_lock(&kvm->arch.sca_lock);
3130130337eSPierre Morel 	ic = kvm_s390_get_ipte_control(kvm);
3148a242234SHeiko Carstens 	do {
3155de72a22SChristian Borntraeger 		old = READ_ONCE(*ic);
3161365039dSChristian Borntraeger 		new = old;
3178a242234SHeiko Carstens 		new.k = 0;
3188a242234SHeiko Carstens 	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
3190130337eSPierre Morel 	read_unlock(&kvm->arch.sca_lock);
3200130337eSPierre Morel 	wake_up(&kvm->arch.ipte_wq);
3218a242234SHeiko Carstens out:
3220130337eSPierre Morel 	mutex_unlock(&kvm->arch.ipte_mutex);
3238a242234SHeiko Carstens }
3248a242234SHeiko Carstens 
3250130337eSPierre Morel static void ipte_lock_siif(struct kvm *kvm)
3268a242234SHeiko Carstens {
3278a242234SHeiko Carstens 	union ipte_control old, new, *ic;
3288a242234SHeiko Carstens 
3295e044315SEugene (jno) Dvurechenski retry:
3300130337eSPierre Morel 	read_lock(&kvm->arch.sca_lock);
3310130337eSPierre Morel 	ic = kvm_s390_get_ipte_control(kvm);
3328a242234SHeiko Carstens 	do {
3335de72a22SChristian Borntraeger 		old = READ_ONCE(*ic);
3345e044315SEugene (jno) Dvurechenski 		if (old.kg) {
3350130337eSPierre Morel 			read_unlock(&kvm->arch.sca_lock);
3368a242234SHeiko Carstens 			cond_resched();
3375e044315SEugene (jno) Dvurechenski 			goto retry;
3388a242234SHeiko Carstens 		}
3398a242234SHeiko Carstens 		new = old;
3408a242234SHeiko Carstens 		new.k = 1;
3418a242234SHeiko Carstens 		new.kh++;
3428a242234SHeiko Carstens 	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
3430130337eSPierre Morel 	read_unlock(&kvm->arch.sca_lock);
3448a242234SHeiko Carstens }
3458a242234SHeiko Carstens 
3460130337eSPierre Morel static void ipte_unlock_siif(struct kvm *kvm)
3478a242234SHeiko Carstens {
3488a242234SHeiko Carstens 	union ipte_control old, new, *ic;
3498a242234SHeiko Carstens 
3500130337eSPierre Morel 	read_lock(&kvm->arch.sca_lock);
3510130337eSPierre Morel 	ic = kvm_s390_get_ipte_control(kvm);
3528a242234SHeiko Carstens 	do {
3535de72a22SChristian Borntraeger 		old = READ_ONCE(*ic);
3541365039dSChristian Borntraeger 		new = old;
3558a242234SHeiko Carstens 		new.kh--;
3568a242234SHeiko Carstens 		if (!new.kh)
3578a242234SHeiko Carstens 			new.k = 0;
3588a242234SHeiko Carstens 	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
3590130337eSPierre Morel 	read_unlock(&kvm->arch.sca_lock);
3608a242234SHeiko Carstens 	if (!new.kh)
3610130337eSPierre Morel 		wake_up(&kvm->arch.ipte_wq);
3628a242234SHeiko Carstens }
3638a242234SHeiko Carstens 
3640130337eSPierre Morel void ipte_lock(struct kvm *kvm)
3658a242234SHeiko Carstens {
3660130337eSPierre Morel 	if (sclp.has_siif)
3670130337eSPierre Morel 		ipte_lock_siif(kvm);
3688a242234SHeiko Carstens 	else
3690130337eSPierre Morel 		ipte_lock_simple(kvm);
3708a242234SHeiko Carstens }
3718a242234SHeiko Carstens 
3720130337eSPierre Morel void ipte_unlock(struct kvm *kvm)
3738a242234SHeiko Carstens {
3740130337eSPierre Morel 	if (sclp.has_siif)
3750130337eSPierre Morel 		ipte_unlock_siif(kvm);
3768a242234SHeiko Carstens 	else
3770130337eSPierre Morel 		ipte_unlock_simple(kvm);
3788a242234SHeiko Carstens }
3798a242234SHeiko Carstens 
38027f67f87SChristian Borntraeger static int ar_translation(struct kvm_vcpu *vcpu, union asce *asce, u8 ar,
38192c96321SDavid Hildenbrand 			  enum gacc_mode mode)
38222938978SHeiko Carstens {
383664b4973SAlexander Yarygin 	union alet alet;
384664b4973SAlexander Yarygin 	struct ale ale;
385664b4973SAlexander Yarygin 	struct aste aste;
386664b4973SAlexander Yarygin 	unsigned long ald_addr, authority_table_addr;
387664b4973SAlexander Yarygin 	union ald ald;
388664b4973SAlexander Yarygin 	int eax, rc;
389664b4973SAlexander Yarygin 	u8 authority_table;
390664b4973SAlexander Yarygin 
391664b4973SAlexander Yarygin 	if (ar >= NUM_ACRS)
392664b4973SAlexander Yarygin 		return -EINVAL;
393664b4973SAlexander Yarygin 
394664b4973SAlexander Yarygin 	save_access_regs(vcpu->run->s.regs.acrs);
395664b4973SAlexander Yarygin 	alet.val = vcpu->run->s.regs.acrs[ar];
396664b4973SAlexander Yarygin 
397664b4973SAlexander Yarygin 	if (ar == 0 || alet.val == 0) {
398664b4973SAlexander Yarygin 		asce->val = vcpu->arch.sie_block->gcr[1];
399664b4973SAlexander Yarygin 		return 0;
400664b4973SAlexander Yarygin 	} else if (alet.val == 1) {
401664b4973SAlexander Yarygin 		asce->val = vcpu->arch.sie_block->gcr[7];
402664b4973SAlexander Yarygin 		return 0;
403664b4973SAlexander Yarygin 	}
404664b4973SAlexander Yarygin 
405664b4973SAlexander Yarygin 	if (alet.reserved)
406664b4973SAlexander Yarygin 		return PGM_ALET_SPECIFICATION;
407664b4973SAlexander Yarygin 
408664b4973SAlexander Yarygin 	if (alet.p)
409664b4973SAlexander Yarygin 		ald_addr = vcpu->arch.sie_block->gcr[5];
410664b4973SAlexander Yarygin 	else
411664b4973SAlexander Yarygin 		ald_addr = vcpu->arch.sie_block->gcr[2];
412664b4973SAlexander Yarygin 	ald_addr &= 0x7fffffc0;
413664b4973SAlexander Yarygin 
414664b4973SAlexander Yarygin 	rc = read_guest_real(vcpu, ald_addr + 16, &ald.val, sizeof(union ald));
415664b4973SAlexander Yarygin 	if (rc)
416664b4973SAlexander Yarygin 		return rc;
417664b4973SAlexander Yarygin 
418664b4973SAlexander Yarygin 	if (alet.alen / 8 > ald.all)
419664b4973SAlexander Yarygin 		return PGM_ALEN_TRANSLATION;
420664b4973SAlexander Yarygin 
421664b4973SAlexander Yarygin 	if (0x7fffffff - ald.alo * 128 < alet.alen * 16)
422664b4973SAlexander Yarygin 		return PGM_ADDRESSING;
423664b4973SAlexander Yarygin 
424664b4973SAlexander Yarygin 	rc = read_guest_real(vcpu, ald.alo * 128 + alet.alen * 16, &ale,
425664b4973SAlexander Yarygin 			     sizeof(struct ale));
426664b4973SAlexander Yarygin 	if (rc)
427664b4973SAlexander Yarygin 		return rc;
428664b4973SAlexander Yarygin 
429664b4973SAlexander Yarygin 	if (ale.i == 1)
430664b4973SAlexander Yarygin 		return PGM_ALEN_TRANSLATION;
431664b4973SAlexander Yarygin 	if (ale.alesn != alet.alesn)
432664b4973SAlexander Yarygin 		return PGM_ALE_SEQUENCE;
433664b4973SAlexander Yarygin 
434664b4973SAlexander Yarygin 	rc = read_guest_real(vcpu, ale.asteo * 64, &aste, sizeof(struct aste));
435664b4973SAlexander Yarygin 	if (rc)
436664b4973SAlexander Yarygin 		return rc;
437664b4973SAlexander Yarygin 
438664b4973SAlexander Yarygin 	if (aste.i)
439664b4973SAlexander Yarygin 		return PGM_ASTE_VALIDITY;
440664b4973SAlexander Yarygin 	if (aste.astesn != ale.astesn)
441664b4973SAlexander Yarygin 		return PGM_ASTE_SEQUENCE;
442664b4973SAlexander Yarygin 
443664b4973SAlexander Yarygin 	if (ale.p == 1) {
444664b4973SAlexander Yarygin 		eax = (vcpu->arch.sie_block->gcr[8] >> 16) & 0xffff;
445664b4973SAlexander Yarygin 		if (ale.aleax != eax) {
446664b4973SAlexander Yarygin 			if (eax / 16 > aste.atl)
447664b4973SAlexander Yarygin 				return PGM_EXTENDED_AUTHORITY;
448664b4973SAlexander Yarygin 
449664b4973SAlexander Yarygin 			authority_table_addr = aste.ato * 4 + eax / 4;
450664b4973SAlexander Yarygin 
451664b4973SAlexander Yarygin 			rc = read_guest_real(vcpu, authority_table_addr,
452664b4973SAlexander Yarygin 					     &authority_table,
453664b4973SAlexander Yarygin 					     sizeof(u8));
454664b4973SAlexander Yarygin 			if (rc)
455664b4973SAlexander Yarygin 				return rc;
456664b4973SAlexander Yarygin 
457664b4973SAlexander Yarygin 			if ((authority_table & (0x40 >> ((eax & 3) * 2))) == 0)
458664b4973SAlexander Yarygin 				return PGM_EXTENDED_AUTHORITY;
459664b4973SAlexander Yarygin 		}
460664b4973SAlexander Yarygin 	}
461664b4973SAlexander Yarygin 
46292c96321SDavid Hildenbrand 	if (ale.fo == 1 && mode == GACC_STORE)
463664b4973SAlexander Yarygin 		return PGM_PROTECTION;
464664b4973SAlexander Yarygin 
465664b4973SAlexander Yarygin 	asce->val = aste.asce;
466664b4973SAlexander Yarygin 	return 0;
467664b4973SAlexander Yarygin }
468664b4973SAlexander Yarygin 
469d03193deSDavid Hildenbrand enum prot_type {
470d03193deSDavid Hildenbrand 	PROT_TYPE_LA   = 0,
471d03193deSDavid Hildenbrand 	PROT_TYPE_KEYC = 1,
472d03193deSDavid Hildenbrand 	PROT_TYPE_ALC  = 2,
473d03193deSDavid Hildenbrand 	PROT_TYPE_DAT  = 3,
4746ae1574cSChristian Borntraeger 	PROT_TYPE_IEP  = 4,
475b3cefd6bSJanis Schoetterl-Glausch 	/* Dummy value for passing an initialized value when code != PGM_PROTECTION */
476b3cefd6bSJanis Schoetterl-Glausch 	PROT_NONE,
477d03193deSDavid Hildenbrand };
478d03193deSDavid Hildenbrand 
479c783631bSJanis Schoetterl-Glausch static int trans_exc_ending(struct kvm_vcpu *vcpu, int code, unsigned long gva, u8 ar,
480c783631bSJanis Schoetterl-Glausch 			    enum gacc_mode mode, enum prot_type prot, bool terminate)
481d03193deSDavid Hildenbrand {
482d03193deSDavid Hildenbrand 	struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm;
48344ae7663SHeiko Carstens 	union teid *teid;
484d03193deSDavid Hildenbrand 
485d03193deSDavid Hildenbrand 	memset(pgm, 0, sizeof(*pgm));
486d03193deSDavid Hildenbrand 	pgm->code = code;
48744ae7663SHeiko Carstens 	teid = (union teid *)&pgm->trans_exc_code;
488d03193deSDavid Hildenbrand 
489d03193deSDavid Hildenbrand 	switch (code) {
490c14b88d7SJanosch Frank 	case PGM_PROTECTION:
491c14b88d7SJanosch Frank 		switch (prot) {
492b3cefd6bSJanis Schoetterl-Glausch 		case PROT_NONE:
493b3cefd6bSJanis Schoetterl-Glausch 			/* We should never get here, acts like termination */
494b3cefd6bSJanis Schoetterl-Glausch 			WARN_ON_ONCE(1);
495b3cefd6bSJanis Schoetterl-Glausch 			break;
4966ae1574cSChristian Borntraeger 		case PROT_TYPE_IEP:
49744ae7663SHeiko Carstens 			teid->b61 = 1;
4983b684a42SJoe Perches 			fallthrough;
499a679c547SChristian Borntraeger 		case PROT_TYPE_LA:
50044ae7663SHeiko Carstens 			teid->b56 = 1;
501a679c547SChristian Borntraeger 			break;
502a679c547SChristian Borntraeger 		case PROT_TYPE_KEYC:
50344ae7663SHeiko Carstens 			teid->b60 = 1;
504a679c547SChristian Borntraeger 			break;
505c14b88d7SJanosch Frank 		case PROT_TYPE_ALC:
50644ae7663SHeiko Carstens 			teid->b60 = 1;
5073b684a42SJoe Perches 			fallthrough;
508c14b88d7SJanosch Frank 		case PROT_TYPE_DAT:
50944ae7663SHeiko Carstens 			teid->b61 = 1;
510c14b88d7SJanosch Frank 			break;
511c14b88d7SJanosch Frank 		}
512c783631bSJanis Schoetterl-Glausch 		if (terminate) {
51344ae7663SHeiko Carstens 			teid->b56 = 0;
51444ae7663SHeiko Carstens 			teid->b60 = 0;
51544ae7663SHeiko Carstens 			teid->b61 = 0;
516c783631bSJanis Schoetterl-Glausch 		}
5173b684a42SJoe Perches 		fallthrough;
518d03193deSDavid Hildenbrand 	case PGM_ASCE_TYPE:
519d03193deSDavid Hildenbrand 	case PGM_PAGE_TRANSLATION:
520d03193deSDavid Hildenbrand 	case PGM_REGION_FIRST_TRANS:
521d03193deSDavid Hildenbrand 	case PGM_REGION_SECOND_TRANS:
522d03193deSDavid Hildenbrand 	case PGM_REGION_THIRD_TRANS:
523d03193deSDavid Hildenbrand 	case PGM_SEGMENT_TRANSLATION:
524d03193deSDavid Hildenbrand 		/*
525d03193deSDavid Hildenbrand 		 * op_access_id only applies to MOVE_PAGE -> set bit 61
526d03193deSDavid Hildenbrand 		 * exc_access_id has to be set to 0 for some instructions. Both
527c14b88d7SJanosch Frank 		 * cases have to be handled by the caller.
528d03193deSDavid Hildenbrand 		 */
52944ae7663SHeiko Carstens 		teid->addr = gva >> PAGE_SHIFT;
53044ae7663SHeiko Carstens 		teid->fsi = mode == GACC_STORE ? TEID_FSI_STORE : TEID_FSI_FETCH;
53144ae7663SHeiko Carstens 		teid->as = psw_bits(vcpu->arch.sie_block->gpsw).as;
5323b684a42SJoe Perches 		fallthrough;
533d03193deSDavid Hildenbrand 	case PGM_ALEN_TRANSLATION:
534d03193deSDavid Hildenbrand 	case PGM_ALE_SEQUENCE:
535d03193deSDavid Hildenbrand 	case PGM_ASTE_VALIDITY:
536d03193deSDavid Hildenbrand 	case PGM_ASTE_SEQUENCE:
537d03193deSDavid Hildenbrand 	case PGM_EXTENDED_AUTHORITY:
538c14b88d7SJanosch Frank 		/*
539c14b88d7SJanosch Frank 		 * We can always store exc_access_id, as it is
540c14b88d7SJanosch Frank 		 * undefined for non-ar cases. It is undefined for
541c14b88d7SJanosch Frank 		 * most DAT protection exceptions.
542c14b88d7SJanosch Frank 		 */
543d03193deSDavid Hildenbrand 		pgm->exc_access_id = ar;
544d03193deSDavid Hildenbrand 		break;
545d03193deSDavid Hildenbrand 	}
546d03193deSDavid Hildenbrand 	return code;
547d03193deSDavid Hildenbrand }
548d03193deSDavid Hildenbrand 
549c783631bSJanis Schoetterl-Glausch static int trans_exc(struct kvm_vcpu *vcpu, int code, unsigned long gva, u8 ar,
550c783631bSJanis Schoetterl-Glausch 		     enum gacc_mode mode, enum prot_type prot)
551c783631bSJanis Schoetterl-Glausch {
552c783631bSJanis Schoetterl-Glausch 	return trans_exc_ending(vcpu, code, gva, ar, mode, prot, false);
553c783631bSJanis Schoetterl-Glausch }
554c783631bSJanis Schoetterl-Glausch 
555664b4973SAlexander Yarygin static int get_vcpu_asce(struct kvm_vcpu *vcpu, union asce *asce,
55627f67f87SChristian Borntraeger 			 unsigned long ga, u8 ar, enum gacc_mode mode)
557664b4973SAlexander Yarygin {
558664b4973SAlexander Yarygin 	int rc;
55934346b9aSDavid Hildenbrand 	struct psw_bits psw = psw_bits(vcpu->arch.sie_block->gpsw);
560664b4973SAlexander Yarygin 
561a7525982SHeiko Carstens 	if (!psw.dat) {
562664b4973SAlexander Yarygin 		asce->val = 0;
563664b4973SAlexander Yarygin 		asce->r = 1;
564664b4973SAlexander Yarygin 		return 0;
565664b4973SAlexander Yarygin 	}
566664b4973SAlexander Yarygin 
5678bb3fdd6SHeiko Carstens 	if ((mode == GACC_IFETCH) && (psw.as != PSW_BITS_AS_HOME))
5688bb3fdd6SHeiko Carstens 		psw.as = PSW_BITS_AS_PRIMARY;
56934346b9aSDavid Hildenbrand 
57034346b9aSDavid Hildenbrand 	switch (psw.as) {
5718bb3fdd6SHeiko Carstens 	case PSW_BITS_AS_PRIMARY:
572664b4973SAlexander Yarygin 		asce->val = vcpu->arch.sie_block->gcr[1];
573664b4973SAlexander Yarygin 		return 0;
5748bb3fdd6SHeiko Carstens 	case PSW_BITS_AS_SECONDARY:
575664b4973SAlexander Yarygin 		asce->val = vcpu->arch.sie_block->gcr[7];
576664b4973SAlexander Yarygin 		return 0;
5778bb3fdd6SHeiko Carstens 	case PSW_BITS_AS_HOME:
578664b4973SAlexander Yarygin 		asce->val = vcpu->arch.sie_block->gcr[13];
579664b4973SAlexander Yarygin 		return 0;
5808bb3fdd6SHeiko Carstens 	case PSW_BITS_AS_ACCREG:
58192c96321SDavid Hildenbrand 		rc = ar_translation(vcpu, asce, ar, mode);
582664b4973SAlexander Yarygin 		if (rc > 0)
583bcfa01d7SDavid Hildenbrand 			return trans_exc(vcpu, rc, ga, ar, mode, PROT_TYPE_ALC);
584664b4973SAlexander Yarygin 		return rc;
58522938978SHeiko Carstens 	}
58622938978SHeiko Carstens 	return 0;
58722938978SHeiko Carstens }
58822938978SHeiko Carstens 
58922938978SHeiko Carstens static int deref_table(struct kvm *kvm, unsigned long gpa, unsigned long *val)
59022938978SHeiko Carstens {
59122938978SHeiko Carstens 	return kvm_read_guest(kvm, gpa, val, sizeof(*val));
59222938978SHeiko Carstens }
59322938978SHeiko Carstens 
59422938978SHeiko Carstens /**
59522938978SHeiko Carstens  * guest_translate - translate a guest virtual into a guest absolute address
59622938978SHeiko Carstens  * @vcpu: virtual cpu
59722938978SHeiko Carstens  * @gva: guest virtual address
59822938978SHeiko Carstens  * @gpa: points to where guest physical (absolute) address should be stored
59975a18122SAlexander Yarygin  * @asce: effective asce
60092c96321SDavid Hildenbrand  * @mode: indicates the access mode to be used
6016ae1574cSChristian Borntraeger  * @prot: returns the type for protection exceptions
60222938978SHeiko Carstens  *
60322938978SHeiko Carstens  * Translate a guest virtual address into a guest absolute address by means
60416b0fc13SYannick Guerrini  * of dynamic address translation as specified by the architecture.
60522938978SHeiko Carstens  * If the resulting absolute address is not available in the configuration
60622938978SHeiko Carstens  * an addressing exception is indicated and @gpa will not be changed.
60722938978SHeiko Carstens  *
60822938978SHeiko Carstens  * Returns: - zero on success; @gpa contains the resulting absolute address
60922938978SHeiko Carstens  *	    - a negative value if guest access failed due to e.g. broken
61022938978SHeiko Carstens  *	      guest mapping
611cada938aSHeiko Carstens  *	    - a positive value if an access exception happened. In this case
61222938978SHeiko Carstens  *	      the returned value is the program interruption code as defined
61322938978SHeiko Carstens  *	      by the architecture
61422938978SHeiko Carstens  */
61522938978SHeiko Carstens static unsigned long guest_translate(struct kvm_vcpu *vcpu, unsigned long gva,
61675a18122SAlexander Yarygin 				     unsigned long *gpa, const union asce asce,
6176ae1574cSChristian Borntraeger 				     enum gacc_mode mode, enum prot_type *prot)
61822938978SHeiko Carstens {
61922938978SHeiko Carstens 	union vaddress vaddr = {.addr = gva};
62022938978SHeiko Carstens 	union raddress raddr = {.addr = gva};
62122938978SHeiko Carstens 	union page_table_entry pte;
62222938978SHeiko Carstens 	int dat_protection = 0;
6236ae1574cSChristian Borntraeger 	int iep_protection = 0;
62422938978SHeiko Carstens 	union ctlreg0 ctlreg0;
62522938978SHeiko Carstens 	unsigned long ptr;
6266ae1574cSChristian Borntraeger 	int edat1, edat2, iep;
62722938978SHeiko Carstens 
62822938978SHeiko Carstens 	ctlreg0.val = vcpu->arch.sie_block->gcr[0];
6299d8d5786SMichael Mueller 	edat1 = ctlreg0.edat && test_kvm_facility(vcpu->kvm, 8);
6309d8d5786SMichael Mueller 	edat2 = edat1 && test_kvm_facility(vcpu->kvm, 78);
6316ae1574cSChristian Borntraeger 	iep = ctlreg0.iep && test_kvm_facility(vcpu->kvm, 130);
63222938978SHeiko Carstens 	if (asce.r)
63322938978SHeiko Carstens 		goto real_address;
63458cdf5ebSHeiko Carstens 	ptr = asce.origin * PAGE_SIZE;
63522938978SHeiko Carstens 	switch (asce.dt) {
63622938978SHeiko Carstens 	case ASCE_TYPE_REGION1:
63722938978SHeiko Carstens 		if (vaddr.rfx01 > asce.tl)
63822938978SHeiko Carstens 			return PGM_REGION_FIRST_TRANS;
63922938978SHeiko Carstens 		ptr += vaddr.rfx * 8;
64022938978SHeiko Carstens 		break;
64122938978SHeiko Carstens 	case ASCE_TYPE_REGION2:
64222938978SHeiko Carstens 		if (vaddr.rfx)
64322938978SHeiko Carstens 			return PGM_ASCE_TYPE;
64422938978SHeiko Carstens 		if (vaddr.rsx01 > asce.tl)
64522938978SHeiko Carstens 			return PGM_REGION_SECOND_TRANS;
64622938978SHeiko Carstens 		ptr += vaddr.rsx * 8;
64722938978SHeiko Carstens 		break;
64822938978SHeiko Carstens 	case ASCE_TYPE_REGION3:
64922938978SHeiko Carstens 		if (vaddr.rfx || vaddr.rsx)
65022938978SHeiko Carstens 			return PGM_ASCE_TYPE;
65122938978SHeiko Carstens 		if (vaddr.rtx01 > asce.tl)
65222938978SHeiko Carstens 			return PGM_REGION_THIRD_TRANS;
65322938978SHeiko Carstens 		ptr += vaddr.rtx * 8;
65422938978SHeiko Carstens 		break;
65522938978SHeiko Carstens 	case ASCE_TYPE_SEGMENT:
65622938978SHeiko Carstens 		if (vaddr.rfx || vaddr.rsx || vaddr.rtx)
65722938978SHeiko Carstens 			return PGM_ASCE_TYPE;
65822938978SHeiko Carstens 		if (vaddr.sx01 > asce.tl)
65922938978SHeiko Carstens 			return PGM_SEGMENT_TRANSLATION;
66022938978SHeiko Carstens 		ptr += vaddr.sx * 8;
66122938978SHeiko Carstens 		break;
66222938978SHeiko Carstens 	}
66322938978SHeiko Carstens 	switch (asce.dt) {
66422938978SHeiko Carstens 	case ASCE_TYPE_REGION1:	{
66522938978SHeiko Carstens 		union region1_table_entry rfte;
66622938978SHeiko Carstens 
66722938978SHeiko Carstens 		if (kvm_is_error_gpa(vcpu->kvm, ptr))
66822938978SHeiko Carstens 			return PGM_ADDRESSING;
66922938978SHeiko Carstens 		if (deref_table(vcpu->kvm, ptr, &rfte.val))
67022938978SHeiko Carstens 			return -EFAULT;
67122938978SHeiko Carstens 		if (rfte.i)
67222938978SHeiko Carstens 			return PGM_REGION_FIRST_TRANS;
67322938978SHeiko Carstens 		if (rfte.tt != TABLE_TYPE_REGION1)
67422938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
67522938978SHeiko Carstens 		if (vaddr.rsx01 < rfte.tf || vaddr.rsx01 > rfte.tl)
67622938978SHeiko Carstens 			return PGM_REGION_SECOND_TRANS;
67722938978SHeiko Carstens 		if (edat1)
67822938978SHeiko Carstens 			dat_protection |= rfte.p;
67958cdf5ebSHeiko Carstens 		ptr = rfte.rto * PAGE_SIZE + vaddr.rsx * 8;
68022938978SHeiko Carstens 	}
6813b684a42SJoe Perches 		fallthrough;
68222938978SHeiko Carstens 	case ASCE_TYPE_REGION2: {
68322938978SHeiko Carstens 		union region2_table_entry rste;
68422938978SHeiko Carstens 
68522938978SHeiko Carstens 		if (kvm_is_error_gpa(vcpu->kvm, ptr))
68622938978SHeiko Carstens 			return PGM_ADDRESSING;
68722938978SHeiko Carstens 		if (deref_table(vcpu->kvm, ptr, &rste.val))
68822938978SHeiko Carstens 			return -EFAULT;
68922938978SHeiko Carstens 		if (rste.i)
69022938978SHeiko Carstens 			return PGM_REGION_SECOND_TRANS;
69122938978SHeiko Carstens 		if (rste.tt != TABLE_TYPE_REGION2)
69222938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
69322938978SHeiko Carstens 		if (vaddr.rtx01 < rste.tf || vaddr.rtx01 > rste.tl)
69422938978SHeiko Carstens 			return PGM_REGION_THIRD_TRANS;
69522938978SHeiko Carstens 		if (edat1)
69622938978SHeiko Carstens 			dat_protection |= rste.p;
69758cdf5ebSHeiko Carstens 		ptr = rste.rto * PAGE_SIZE + vaddr.rtx * 8;
69822938978SHeiko Carstens 	}
6993b684a42SJoe Perches 		fallthrough;
70022938978SHeiko Carstens 	case ASCE_TYPE_REGION3: {
70122938978SHeiko Carstens 		union region3_table_entry rtte;
70222938978SHeiko Carstens 
70322938978SHeiko Carstens 		if (kvm_is_error_gpa(vcpu->kvm, ptr))
70422938978SHeiko Carstens 			return PGM_ADDRESSING;
70522938978SHeiko Carstens 		if (deref_table(vcpu->kvm, ptr, &rtte.val))
70622938978SHeiko Carstens 			return -EFAULT;
70722938978SHeiko Carstens 		if (rtte.i)
70822938978SHeiko Carstens 			return PGM_REGION_THIRD_TRANS;
70922938978SHeiko Carstens 		if (rtte.tt != TABLE_TYPE_REGION3)
71022938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
71122938978SHeiko Carstens 		if (rtte.cr && asce.p && edat2)
71222938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
71322938978SHeiko Carstens 		if (rtte.fc && edat2) {
71422938978SHeiko Carstens 			dat_protection |= rtte.fc1.p;
7156ae1574cSChristian Borntraeger 			iep_protection = rtte.fc1.iep;
71622938978SHeiko Carstens 			raddr.rfaa = rtte.fc1.rfaa;
71722938978SHeiko Carstens 			goto absolute_address;
71822938978SHeiko Carstens 		}
71922938978SHeiko Carstens 		if (vaddr.sx01 < rtte.fc0.tf)
72022938978SHeiko Carstens 			return PGM_SEGMENT_TRANSLATION;
72122938978SHeiko Carstens 		if (vaddr.sx01 > rtte.fc0.tl)
72222938978SHeiko Carstens 			return PGM_SEGMENT_TRANSLATION;
72322938978SHeiko Carstens 		if (edat1)
72422938978SHeiko Carstens 			dat_protection |= rtte.fc0.p;
72558cdf5ebSHeiko Carstens 		ptr = rtte.fc0.sto * PAGE_SIZE + vaddr.sx * 8;
72622938978SHeiko Carstens 	}
7273b684a42SJoe Perches 		fallthrough;
72822938978SHeiko Carstens 	case ASCE_TYPE_SEGMENT: {
72922938978SHeiko Carstens 		union segment_table_entry ste;
73022938978SHeiko Carstens 
73122938978SHeiko Carstens 		if (kvm_is_error_gpa(vcpu->kvm, ptr))
73222938978SHeiko Carstens 			return PGM_ADDRESSING;
73322938978SHeiko Carstens 		if (deref_table(vcpu->kvm, ptr, &ste.val))
73422938978SHeiko Carstens 			return -EFAULT;
73522938978SHeiko Carstens 		if (ste.i)
73622938978SHeiko Carstens 			return PGM_SEGMENT_TRANSLATION;
73722938978SHeiko Carstens 		if (ste.tt != TABLE_TYPE_SEGMENT)
73822938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
73922938978SHeiko Carstens 		if (ste.cs && asce.p)
74022938978SHeiko Carstens 			return PGM_TRANSLATION_SPEC;
74122938978SHeiko Carstens 		if (ste.fc && edat1) {
74222938978SHeiko Carstens 			dat_protection |= ste.fc1.p;
7436ae1574cSChristian Borntraeger 			iep_protection = ste.fc1.iep;
74422938978SHeiko Carstens 			raddr.sfaa = ste.fc1.sfaa;
74522938978SHeiko Carstens 			goto absolute_address;
74622938978SHeiko Carstens 		}
74722938978SHeiko Carstens 		dat_protection |= ste.fc0.p;
74858cdf5ebSHeiko Carstens 		ptr = ste.fc0.pto * (PAGE_SIZE / 2) + vaddr.px * 8;
74922938978SHeiko Carstens 	}
75022938978SHeiko Carstens 	}
75122938978SHeiko Carstens 	if (kvm_is_error_gpa(vcpu->kvm, ptr))
75222938978SHeiko Carstens 		return PGM_ADDRESSING;
75322938978SHeiko Carstens 	if (deref_table(vcpu->kvm, ptr, &pte.val))
75422938978SHeiko Carstens 		return -EFAULT;
75522938978SHeiko Carstens 	if (pte.i)
75622938978SHeiko Carstens 		return PGM_PAGE_TRANSLATION;
75722938978SHeiko Carstens 	if (pte.z)
75822938978SHeiko Carstens 		return PGM_TRANSLATION_SPEC;
75922938978SHeiko Carstens 	dat_protection |= pte.p;
7606ae1574cSChristian Borntraeger 	iep_protection = pte.iep;
76122938978SHeiko Carstens 	raddr.pfra = pte.pfra;
76222938978SHeiko Carstens real_address:
76322938978SHeiko Carstens 	raddr.addr = kvm_s390_real_to_abs(vcpu, raddr.addr);
76422938978SHeiko Carstens absolute_address:
7656ae1574cSChristian Borntraeger 	if (mode == GACC_STORE && dat_protection) {
7666ae1574cSChristian Borntraeger 		*prot = PROT_TYPE_DAT;
76722938978SHeiko Carstens 		return PGM_PROTECTION;
7686ae1574cSChristian Borntraeger 	}
7696ae1574cSChristian Borntraeger 	if (mode == GACC_IFETCH && iep_protection && iep) {
7706ae1574cSChristian Borntraeger 		*prot = PROT_TYPE_IEP;
7716ae1574cSChristian Borntraeger 		return PGM_PROTECTION;
7726ae1574cSChristian Borntraeger 	}
77322938978SHeiko Carstens 	if (kvm_is_error_gpa(vcpu->kvm, raddr.addr))
77422938978SHeiko Carstens 		return PGM_ADDRESSING;
77522938978SHeiko Carstens 	*gpa = raddr.addr;
77622938978SHeiko Carstens 	return 0;
77722938978SHeiko Carstens }
77822938978SHeiko Carstens 
77922938978SHeiko Carstens static inline int is_low_address(unsigned long ga)
78022938978SHeiko Carstens {
78122938978SHeiko Carstens 	/* Check for address ranges 0..511 and 4096..4607 */
78222938978SHeiko Carstens 	return (ga & ~0x11fful) == 0;
78322938978SHeiko Carstens }
78422938978SHeiko Carstens 
78575a18122SAlexander Yarygin static int low_address_protection_enabled(struct kvm_vcpu *vcpu,
78675a18122SAlexander Yarygin 					  const union asce asce)
78722938978SHeiko Carstens {
78822938978SHeiko Carstens 	union ctlreg0 ctlreg0 = {.val = vcpu->arch.sie_block->gcr[0]};
78922938978SHeiko Carstens 	psw_t *psw = &vcpu->arch.sie_block->gpsw;
79022938978SHeiko Carstens 
79122938978SHeiko Carstens 	if (!ctlreg0.lap)
79222938978SHeiko Carstens 		return 0;
793a7525982SHeiko Carstens 	if (psw_bits(*psw).dat && asce.p)
79422938978SHeiko Carstens 		return 0;
79522938978SHeiko Carstens 	return 1;
79622938978SHeiko Carstens }
79722938978SHeiko Carstens 
798ef11c946SJanis Schoetterl-Glausch static int vm_check_access_key(struct kvm *kvm, u8 access_key,
799ef11c946SJanis Schoetterl-Glausch 			       enum gacc_mode mode, gpa_t gpa)
800ef11c946SJanis Schoetterl-Glausch {
801ef11c946SJanis Schoetterl-Glausch 	u8 storage_key, access_control;
802ef11c946SJanis Schoetterl-Glausch 	bool fetch_protected;
803ef11c946SJanis Schoetterl-Glausch 	unsigned long hva;
804ef11c946SJanis Schoetterl-Glausch 	int r;
805ef11c946SJanis Schoetterl-Glausch 
806ef11c946SJanis Schoetterl-Glausch 	if (access_key == 0)
807ef11c946SJanis Schoetterl-Glausch 		return 0;
808ef11c946SJanis Schoetterl-Glausch 
809ef11c946SJanis Schoetterl-Glausch 	hva = gfn_to_hva(kvm, gpa_to_gfn(gpa));
810ef11c946SJanis Schoetterl-Glausch 	if (kvm_is_error_hva(hva))
811ef11c946SJanis Schoetterl-Glausch 		return PGM_ADDRESSING;
812ef11c946SJanis Schoetterl-Glausch 
813ef11c946SJanis Schoetterl-Glausch 	mmap_read_lock(current->mm);
814ef11c946SJanis Schoetterl-Glausch 	r = get_guest_storage_key(current->mm, hva, &storage_key);
815ef11c946SJanis Schoetterl-Glausch 	mmap_read_unlock(current->mm);
816ef11c946SJanis Schoetterl-Glausch 	if (r)
817ef11c946SJanis Schoetterl-Glausch 		return r;
818ef11c946SJanis Schoetterl-Glausch 	access_control = FIELD_GET(_PAGE_ACC_BITS, storage_key);
819ef11c946SJanis Schoetterl-Glausch 	if (access_control == access_key)
820ef11c946SJanis Schoetterl-Glausch 		return 0;
821ef11c946SJanis Schoetterl-Glausch 	fetch_protected = storage_key & _PAGE_FP_BIT;
822ef11c946SJanis Schoetterl-Glausch 	if ((mode == GACC_FETCH || mode == GACC_IFETCH) && !fetch_protected)
823ef11c946SJanis Schoetterl-Glausch 		return 0;
824ef11c946SJanis Schoetterl-Glausch 	return PGM_PROTECTION;
825ef11c946SJanis Schoetterl-Glausch }
826ef11c946SJanis Schoetterl-Glausch 
827e613d834SJanis Schoetterl-Glausch static bool fetch_prot_override_applicable(struct kvm_vcpu *vcpu, enum gacc_mode mode,
828e613d834SJanis Schoetterl-Glausch 					   union asce asce)
829e613d834SJanis Schoetterl-Glausch {
830e613d834SJanis Schoetterl-Glausch 	psw_t *psw = &vcpu->arch.sie_block->gpsw;
831e613d834SJanis Schoetterl-Glausch 	unsigned long override;
832e613d834SJanis Schoetterl-Glausch 
833e613d834SJanis Schoetterl-Glausch 	if (mode == GACC_FETCH || mode == GACC_IFETCH) {
834e613d834SJanis Schoetterl-Glausch 		/* check if fetch protection override enabled */
835e613d834SJanis Schoetterl-Glausch 		override = vcpu->arch.sie_block->gcr[0];
836e613d834SJanis Schoetterl-Glausch 		override &= CR0_FETCH_PROTECTION_OVERRIDE;
837e613d834SJanis Schoetterl-Glausch 		/* not applicable if subject to DAT && private space */
838e613d834SJanis Schoetterl-Glausch 		override = override && !(psw_bits(*psw).dat && asce.p);
839e613d834SJanis Schoetterl-Glausch 		return override;
840e613d834SJanis Schoetterl-Glausch 	}
841e613d834SJanis Schoetterl-Glausch 	return false;
842e613d834SJanis Schoetterl-Glausch }
843e613d834SJanis Schoetterl-Glausch 
844e613d834SJanis Schoetterl-Glausch static bool fetch_prot_override_applies(unsigned long ga, unsigned int len)
845e613d834SJanis Schoetterl-Glausch {
846e613d834SJanis Schoetterl-Glausch 	return ga < 2048 && ga + len <= 2048;
847e613d834SJanis Schoetterl-Glausch }
848e613d834SJanis Schoetterl-Glausch 
849e613d834SJanis Schoetterl-Glausch static bool storage_prot_override_applicable(struct kvm_vcpu *vcpu)
850e613d834SJanis Schoetterl-Glausch {
851e613d834SJanis Schoetterl-Glausch 	/* check if storage protection override enabled */
852e613d834SJanis Schoetterl-Glausch 	return vcpu->arch.sie_block->gcr[0] & CR0_STORAGE_PROTECTION_OVERRIDE;
853e613d834SJanis Schoetterl-Glausch }
854e613d834SJanis Schoetterl-Glausch 
855e613d834SJanis Schoetterl-Glausch static bool storage_prot_override_applies(u8 access_control)
856e613d834SJanis Schoetterl-Glausch {
857e613d834SJanis Schoetterl-Glausch 	/* matches special storage protection override key (9) -> allow */
858e613d834SJanis Schoetterl-Glausch 	return access_control == PAGE_SPO_ACC;
859e613d834SJanis Schoetterl-Glausch }
860e613d834SJanis Schoetterl-Glausch 
861e613d834SJanis Schoetterl-Glausch static int vcpu_check_access_key(struct kvm_vcpu *vcpu, u8 access_key,
862e613d834SJanis Schoetterl-Glausch 				 enum gacc_mode mode, union asce asce, gpa_t gpa,
863e613d834SJanis Schoetterl-Glausch 				 unsigned long ga, unsigned int len)
864e613d834SJanis Schoetterl-Glausch {
865e613d834SJanis Schoetterl-Glausch 	u8 storage_key, access_control;
866e613d834SJanis Schoetterl-Glausch 	unsigned long hva;
867e613d834SJanis Schoetterl-Glausch 	int r;
868e613d834SJanis Schoetterl-Glausch 
869e613d834SJanis Schoetterl-Glausch 	/* access key 0 matches any storage key -> allow */
870e613d834SJanis Schoetterl-Glausch 	if (access_key == 0)
871e613d834SJanis Schoetterl-Glausch 		return 0;
872e613d834SJanis Schoetterl-Glausch 	/*
873e613d834SJanis Schoetterl-Glausch 	 * caller needs to ensure that gfn is accessible, so we can
874e613d834SJanis Schoetterl-Glausch 	 * assume that this cannot fail
875e613d834SJanis Schoetterl-Glausch 	 */
876e613d834SJanis Schoetterl-Glausch 	hva = gfn_to_hva(vcpu->kvm, gpa_to_gfn(gpa));
877e613d834SJanis Schoetterl-Glausch 	mmap_read_lock(current->mm);
878e613d834SJanis Schoetterl-Glausch 	r = get_guest_storage_key(current->mm, hva, &storage_key);
879e613d834SJanis Schoetterl-Glausch 	mmap_read_unlock(current->mm);
880e613d834SJanis Schoetterl-Glausch 	if (r)
881e613d834SJanis Schoetterl-Glausch 		return r;
882e613d834SJanis Schoetterl-Glausch 	access_control = FIELD_GET(_PAGE_ACC_BITS, storage_key);
883e613d834SJanis Schoetterl-Glausch 	/* access key matches storage key -> allow */
884e613d834SJanis Schoetterl-Glausch 	if (access_control == access_key)
885e613d834SJanis Schoetterl-Glausch 		return 0;
886e613d834SJanis Schoetterl-Glausch 	if (mode == GACC_FETCH || mode == GACC_IFETCH) {
887e613d834SJanis Schoetterl-Glausch 		/* it is a fetch and fetch protection is off -> allow */
888e613d834SJanis Schoetterl-Glausch 		if (!(storage_key & _PAGE_FP_BIT))
889e613d834SJanis Schoetterl-Glausch 			return 0;
890e613d834SJanis Schoetterl-Glausch 		if (fetch_prot_override_applicable(vcpu, mode, asce) &&
891e613d834SJanis Schoetterl-Glausch 		    fetch_prot_override_applies(ga, len))
892e613d834SJanis Schoetterl-Glausch 			return 0;
893e613d834SJanis Schoetterl-Glausch 	}
894e613d834SJanis Schoetterl-Glausch 	if (storage_prot_override_applicable(vcpu) &&
895e613d834SJanis Schoetterl-Glausch 	    storage_prot_override_applies(access_control))
896e613d834SJanis Schoetterl-Glausch 		return 0;
897e613d834SJanis Schoetterl-Glausch 	return PGM_PROTECTION;
898e613d834SJanis Schoetterl-Glausch }
899e613d834SJanis Schoetterl-Glausch 
9007faa543dSJanis Schoetterl-Glausch /**
9017faa543dSJanis Schoetterl-Glausch  * guest_range_to_gpas() - Calculate guest physical addresses of page fragments
9027faa543dSJanis Schoetterl-Glausch  * covering a logical range
9037faa543dSJanis Schoetterl-Glausch  * @vcpu: virtual cpu
9047faa543dSJanis Schoetterl-Glausch  * @ga: guest address, start of range
9057faa543dSJanis Schoetterl-Glausch  * @ar: access register
9067faa543dSJanis Schoetterl-Glausch  * @gpas: output argument, may be NULL
9077faa543dSJanis Schoetterl-Glausch  * @len: length of range in bytes
9087faa543dSJanis Schoetterl-Glausch  * @asce: address-space-control element to use for translation
9097faa543dSJanis Schoetterl-Glausch  * @mode: access mode
910e613d834SJanis Schoetterl-Glausch  * @access_key: access key to mach the range's storage keys against
9117faa543dSJanis Schoetterl-Glausch  *
9127faa543dSJanis Schoetterl-Glausch  * Translate a logical range to a series of guest absolute addresses,
9137faa543dSJanis Schoetterl-Glausch  * such that the concatenation of page fragments starting at each gpa make up
9147faa543dSJanis Schoetterl-Glausch  * the whole range.
9157faa543dSJanis Schoetterl-Glausch  * The translation is performed as if done by the cpu for the given @asce, @ar,
9167faa543dSJanis Schoetterl-Glausch  * @mode and state of the @vcpu.
9177faa543dSJanis Schoetterl-Glausch  * If the translation causes an exception, its program interruption code is
9187faa543dSJanis Schoetterl-Glausch  * returned and the &struct kvm_s390_pgm_info pgm member of @vcpu is modified
9197faa543dSJanis Schoetterl-Glausch  * such that a subsequent call to kvm_s390_inject_prog_vcpu() will inject
9207faa543dSJanis Schoetterl-Glausch  * a correct exception into the guest.
9217faa543dSJanis Schoetterl-Glausch  * The resulting gpas are stored into @gpas, unless it is NULL.
9227faa543dSJanis Schoetterl-Glausch  *
9237faa543dSJanis Schoetterl-Glausch  * Note: All fragments except the first one start at the beginning of a page.
9247faa543dSJanis Schoetterl-Glausch  *	 When deriving the boundaries of a fragment from a gpa, all but the last
9257faa543dSJanis Schoetterl-Glausch  *	 fragment end at the end of the page.
9267faa543dSJanis Schoetterl-Glausch  *
9277faa543dSJanis Schoetterl-Glausch  * Return:
9287faa543dSJanis Schoetterl-Glausch  * * 0		- success
9297faa543dSJanis Schoetterl-Glausch  * * <0		- translation could not be performed, for example if  guest
9307faa543dSJanis Schoetterl-Glausch  *		  memory could not be accessed
9317faa543dSJanis Schoetterl-Glausch  * * >0		- an access exception occurred. In this case the returned value
9327faa543dSJanis Schoetterl-Glausch  *		  is the program interruption code and the contents of pgm may
9337faa543dSJanis Schoetterl-Glausch  *		  be used to inject an exception into the guest.
9347faa543dSJanis Schoetterl-Glausch  */
9357faa543dSJanis Schoetterl-Glausch static int guest_range_to_gpas(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar,
9367faa543dSJanis Schoetterl-Glausch 			       unsigned long *gpas, unsigned long len,
937e613d834SJanis Schoetterl-Glausch 			       const union asce asce, enum gacc_mode mode,
938e613d834SJanis Schoetterl-Glausch 			       u8 access_key)
93922938978SHeiko Carstens {
94022938978SHeiko Carstens 	psw_t *psw = &vcpu->arch.sie_block->gpsw;
9417faa543dSJanis Schoetterl-Glausch 	unsigned int offset = offset_in_page(ga);
9427faa543dSJanis Schoetterl-Glausch 	unsigned int fragment_len;
943cde0dcfbSDavid Hildenbrand 	int lap_enabled, rc = 0;
9446ae1574cSChristian Borntraeger 	enum prot_type prot;
9457faa543dSJanis Schoetterl-Glausch 	unsigned long gpa;
94622938978SHeiko Carstens 
94775a18122SAlexander Yarygin 	lap_enabled = low_address_protection_enabled(vcpu, asce);
9487faa543dSJanis Schoetterl-Glausch 	while (min(PAGE_SIZE - offset, len) > 0) {
9497faa543dSJanis Schoetterl-Glausch 		fragment_len = min(PAGE_SIZE - offset, len);
95022938978SHeiko Carstens 		ga = kvm_s390_logical_to_effective(vcpu, ga);
951cde0dcfbSDavid Hildenbrand 		if (mode == GACC_STORE && lap_enabled && is_low_address(ga))
952cde0dcfbSDavid Hildenbrand 			return trans_exc(vcpu, PGM_PROTECTION, ga, ar, mode,
953cde0dcfbSDavid Hildenbrand 					 PROT_TYPE_LA);
954a7525982SHeiko Carstens 		if (psw_bits(*psw).dat) {
9557faa543dSJanis Schoetterl-Glausch 			rc = guest_translate(vcpu, ga, &gpa, asce, mode, &prot);
95622938978SHeiko Carstens 			if (rc < 0)
95722938978SHeiko Carstens 				return rc;
95822938978SHeiko Carstens 		} else {
9597faa543dSJanis Schoetterl-Glausch 			gpa = kvm_s390_real_to_abs(vcpu, ga);
960b3cefd6bSJanis Schoetterl-Glausch 			if (kvm_is_error_gpa(vcpu->kvm, gpa)) {
961cde0dcfbSDavid Hildenbrand 				rc = PGM_ADDRESSING;
962b3cefd6bSJanis Schoetterl-Glausch 				prot = PROT_NONE;
963b3cefd6bSJanis Schoetterl-Glausch 			}
96422938978SHeiko Carstens 		}
965cde0dcfbSDavid Hildenbrand 		if (rc)
9666ae1574cSChristian Borntraeger 			return trans_exc(vcpu, rc, ga, ar, mode, prot);
967e613d834SJanis Schoetterl-Glausch 		rc = vcpu_check_access_key(vcpu, access_key, mode, asce, gpa, ga,
968e613d834SJanis Schoetterl-Glausch 					   fragment_len);
969e613d834SJanis Schoetterl-Glausch 		if (rc)
970e613d834SJanis Schoetterl-Glausch 			return trans_exc(vcpu, rc, ga, ar, mode, PROT_TYPE_KEYC);
9717faa543dSJanis Schoetterl-Glausch 		if (gpas)
9727faa543dSJanis Schoetterl-Glausch 			*gpas++ = gpa;
9737faa543dSJanis Schoetterl-Glausch 		offset = 0;
9747faa543dSJanis Schoetterl-Glausch 		ga += fragment_len;
9757faa543dSJanis Schoetterl-Glausch 		len -= fragment_len;
97622938978SHeiko Carstens 	}
97722938978SHeiko Carstens 	return 0;
97822938978SHeiko Carstens }
97922938978SHeiko Carstens 
980bad13799SJanis Schoetterl-Glausch static int access_guest_page(struct kvm *kvm, enum gacc_mode mode, gpa_t gpa,
981bad13799SJanis Schoetterl-Glausch 			     void *data, unsigned int len)
982bad13799SJanis Schoetterl-Glausch {
983bad13799SJanis Schoetterl-Glausch 	const unsigned int offset = offset_in_page(gpa);
984bad13799SJanis Schoetterl-Glausch 	const gfn_t gfn = gpa_to_gfn(gpa);
985bad13799SJanis Schoetterl-Glausch 	int rc;
986bad13799SJanis Schoetterl-Glausch 
987bad13799SJanis Schoetterl-Glausch 	if (mode == GACC_STORE)
988bad13799SJanis Schoetterl-Glausch 		rc = kvm_write_guest_page(kvm, gfn, data, offset, len);
989bad13799SJanis Schoetterl-Glausch 	else
990bad13799SJanis Schoetterl-Glausch 		rc = kvm_read_guest_page(kvm, gfn, data, offset, len);
991bad13799SJanis Schoetterl-Glausch 	return rc;
992bad13799SJanis Schoetterl-Glausch }
993bad13799SJanis Schoetterl-Glausch 
994e613d834SJanis Schoetterl-Glausch static int
995e613d834SJanis Schoetterl-Glausch access_guest_page_with_key(struct kvm *kvm, enum gacc_mode mode, gpa_t gpa,
996e613d834SJanis Schoetterl-Glausch 			   void *data, unsigned int len, u8 access_key)
997e613d834SJanis Schoetterl-Glausch {
998e613d834SJanis Schoetterl-Glausch 	struct kvm_memory_slot *slot;
999e613d834SJanis Schoetterl-Glausch 	bool writable;
1000e613d834SJanis Schoetterl-Glausch 	gfn_t gfn;
1001e613d834SJanis Schoetterl-Glausch 	hva_t hva;
1002e613d834SJanis Schoetterl-Glausch 	int rc;
1003e613d834SJanis Schoetterl-Glausch 
1004e613d834SJanis Schoetterl-Glausch 	gfn = gpa >> PAGE_SHIFT;
1005e613d834SJanis Schoetterl-Glausch 	slot = gfn_to_memslot(kvm, gfn);
1006e613d834SJanis Schoetterl-Glausch 	hva = gfn_to_hva_memslot_prot(slot, gfn, &writable);
1007e613d834SJanis Schoetterl-Glausch 
1008e613d834SJanis Schoetterl-Glausch 	if (kvm_is_error_hva(hva))
1009e613d834SJanis Schoetterl-Glausch 		return PGM_ADDRESSING;
1010e613d834SJanis Schoetterl-Glausch 	/*
1011e613d834SJanis Schoetterl-Glausch 	 * Check if it's a ro memslot, even tho that can't occur (they're unsupported).
1012e613d834SJanis Schoetterl-Glausch 	 * Don't try to actually handle that case.
1013e613d834SJanis Schoetterl-Glausch 	 */
1014e613d834SJanis Schoetterl-Glausch 	if (!writable && mode == GACC_STORE)
1015e613d834SJanis Schoetterl-Glausch 		return -EOPNOTSUPP;
1016e613d834SJanis Schoetterl-Glausch 	hva += offset_in_page(gpa);
1017e613d834SJanis Schoetterl-Glausch 	if (mode == GACC_STORE)
1018e613d834SJanis Schoetterl-Glausch 		rc = copy_to_user_key((void __user *)hva, data, len, access_key);
1019e613d834SJanis Schoetterl-Glausch 	else
1020e613d834SJanis Schoetterl-Glausch 		rc = copy_from_user_key(data, (void __user *)hva, len, access_key);
1021e613d834SJanis Schoetterl-Glausch 	if (rc)
1022e613d834SJanis Schoetterl-Glausch 		return PGM_PROTECTION;
1023e613d834SJanis Schoetterl-Glausch 	if (mode == GACC_STORE)
1024e613d834SJanis Schoetterl-Glausch 		mark_page_dirty_in_slot(kvm, slot, gfn);
1025e613d834SJanis Schoetterl-Glausch 	return 0;
1026e613d834SJanis Schoetterl-Glausch }
1027e613d834SJanis Schoetterl-Glausch 
1028ef11c946SJanis Schoetterl-Glausch int access_guest_abs_with_key(struct kvm *kvm, gpa_t gpa, void *data,
1029ef11c946SJanis Schoetterl-Glausch 			      unsigned long len, enum gacc_mode mode, u8 access_key)
1030ef11c946SJanis Schoetterl-Glausch {
1031ef11c946SJanis Schoetterl-Glausch 	int offset = offset_in_page(gpa);
1032ef11c946SJanis Schoetterl-Glausch 	int fragment_len;
1033ef11c946SJanis Schoetterl-Glausch 	int rc;
1034ef11c946SJanis Schoetterl-Glausch 
1035ef11c946SJanis Schoetterl-Glausch 	while (min(PAGE_SIZE - offset, len) > 0) {
1036ef11c946SJanis Schoetterl-Glausch 		fragment_len = min(PAGE_SIZE - offset, len);
1037ef11c946SJanis Schoetterl-Glausch 		rc = access_guest_page_with_key(kvm, mode, gpa, data, fragment_len, access_key);
1038ef11c946SJanis Schoetterl-Glausch 		if (rc)
1039ef11c946SJanis Schoetterl-Glausch 			return rc;
1040ef11c946SJanis Schoetterl-Glausch 		offset = 0;
1041ef11c946SJanis Schoetterl-Glausch 		len -= fragment_len;
1042ef11c946SJanis Schoetterl-Glausch 		data += fragment_len;
1043ef11c946SJanis Schoetterl-Glausch 		gpa += fragment_len;
1044ef11c946SJanis Schoetterl-Glausch 	}
1045ef11c946SJanis Schoetterl-Glausch 	return 0;
1046ef11c946SJanis Schoetterl-Glausch }
1047ef11c946SJanis Schoetterl-Glausch 
1048e613d834SJanis Schoetterl-Glausch int access_guest_with_key(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar,
1049e613d834SJanis Schoetterl-Glausch 			  void *data, unsigned long len, enum gacc_mode mode,
1050e613d834SJanis Schoetterl-Glausch 			  u8 access_key)
105122938978SHeiko Carstens {
105222938978SHeiko Carstens 	psw_t *psw = &vcpu->arch.sie_block->gpsw;
10537faa543dSJanis Schoetterl-Glausch 	unsigned long nr_pages, idx;
10547faa543dSJanis Schoetterl-Glausch 	unsigned long gpa_array[2];
1055416e7f0cSJanis Schoetterl-Glausch 	unsigned int fragment_len;
10567faa543dSJanis Schoetterl-Glausch 	unsigned long *gpas;
1057e613d834SJanis Schoetterl-Glausch 	enum prot_type prot;
10588a242234SHeiko Carstens 	int need_ipte_lock;
10598a242234SHeiko Carstens 	union asce asce;
1060e613d834SJanis Schoetterl-Glausch 	bool try_storage_prot_override;
1061e613d834SJanis Schoetterl-Glausch 	bool try_fetch_prot_override;
106222938978SHeiko Carstens 	int rc;
106322938978SHeiko Carstens 
106422938978SHeiko Carstens 	if (!len)
106522938978SHeiko Carstens 		return 0;
10666167375bSDavid Hildenbrand 	ga = kvm_s390_logical_to_effective(vcpu, ga);
10676167375bSDavid Hildenbrand 	rc = get_vcpu_asce(vcpu, &asce, ga, ar, mode);
1068664b4973SAlexander Yarygin 	if (rc)
1069664b4973SAlexander Yarygin 		return rc;
107022938978SHeiko Carstens 	nr_pages = (((ga & ~PAGE_MASK) + len - 1) >> PAGE_SHIFT) + 1;
10717faa543dSJanis Schoetterl-Glausch 	gpas = gpa_array;
10727faa543dSJanis Schoetterl-Glausch 	if (nr_pages > ARRAY_SIZE(gpa_array))
10737faa543dSJanis Schoetterl-Glausch 		gpas = vmalloc(array_size(nr_pages, sizeof(unsigned long)));
10747faa543dSJanis Schoetterl-Glausch 	if (!gpas)
107522938978SHeiko Carstens 		return -ENOMEM;
1076e613d834SJanis Schoetterl-Glausch 	try_fetch_prot_override = fetch_prot_override_applicable(vcpu, mode, asce);
1077e613d834SJanis Schoetterl-Glausch 	try_storage_prot_override = storage_prot_override_applicable(vcpu);
1078a7525982SHeiko Carstens 	need_ipte_lock = psw_bits(*psw).dat && !asce.r;
10798a242234SHeiko Carstens 	if (need_ipte_lock)
10800130337eSPierre Morel 		ipte_lock(vcpu->kvm);
1081e613d834SJanis Schoetterl-Glausch 	/*
1082e613d834SJanis Schoetterl-Glausch 	 * Since we do the access further down ultimately via a move instruction
1083e613d834SJanis Schoetterl-Glausch 	 * that does key checking and returns an error in case of a protection
1084e613d834SJanis Schoetterl-Glausch 	 * violation, we don't need to do the check during address translation.
1085e613d834SJanis Schoetterl-Glausch 	 * Skip it by passing access key 0, which matches any storage key,
1086e613d834SJanis Schoetterl-Glausch 	 * obviating the need for any further checks. As a result the check is
1087e613d834SJanis Schoetterl-Glausch 	 * handled entirely in hardware on access, we only need to take care to
1088e613d834SJanis Schoetterl-Glausch 	 * forego key protection checking if fetch protection override applies or
1089e613d834SJanis Schoetterl-Glausch 	 * retry with the special key 9 in case of storage protection override.
1090e613d834SJanis Schoetterl-Glausch 	 */
1091e613d834SJanis Schoetterl-Glausch 	rc = guest_range_to_gpas(vcpu, ga, ar, gpas, len, asce, mode, 0);
1092e613d834SJanis Schoetterl-Glausch 	if (rc)
1093e613d834SJanis Schoetterl-Glausch 		goto out_unlock;
1094e613d834SJanis Schoetterl-Glausch 	for (idx = 0; idx < nr_pages; idx++) {
10957faa543dSJanis Schoetterl-Glausch 		fragment_len = min(PAGE_SIZE - offset_in_page(gpas[idx]), len);
1096e613d834SJanis Schoetterl-Glausch 		if (try_fetch_prot_override && fetch_prot_override_applies(ga, fragment_len)) {
1097e613d834SJanis Schoetterl-Glausch 			rc = access_guest_page(vcpu->kvm, mode, gpas[idx],
1098e613d834SJanis Schoetterl-Glausch 					       data, fragment_len);
1099e613d834SJanis Schoetterl-Glausch 		} else {
1100e613d834SJanis Schoetterl-Glausch 			rc = access_guest_page_with_key(vcpu->kvm, mode, gpas[idx],
1101e613d834SJanis Schoetterl-Glausch 							data, fragment_len, access_key);
1102e613d834SJanis Schoetterl-Glausch 		}
1103e613d834SJanis Schoetterl-Glausch 		if (rc == PGM_PROTECTION && try_storage_prot_override)
1104e613d834SJanis Schoetterl-Glausch 			rc = access_guest_page_with_key(vcpu->kvm, mode, gpas[idx],
1105e613d834SJanis Schoetterl-Glausch 							data, fragment_len, PAGE_SPO_ACC);
1106e613d834SJanis Schoetterl-Glausch 		if (rc)
1107e613d834SJanis Schoetterl-Glausch 			break;
1108416e7f0cSJanis Schoetterl-Glausch 		len -= fragment_len;
1109416e7f0cSJanis Schoetterl-Glausch 		data += fragment_len;
1110e613d834SJanis Schoetterl-Glausch 		ga = kvm_s390_logical_to_effective(vcpu, ga + fragment_len);
111122938978SHeiko Carstens 	}
1112c783631bSJanis Schoetterl-Glausch 	if (rc > 0) {
1113c783631bSJanis Schoetterl-Glausch 		bool terminate = (mode == GACC_STORE) && (idx > 0);
1114c783631bSJanis Schoetterl-Glausch 
1115b3cefd6bSJanis Schoetterl-Glausch 		if (rc == PGM_PROTECTION)
1116b3cefd6bSJanis Schoetterl-Glausch 			prot = PROT_TYPE_KEYC;
1117b3cefd6bSJanis Schoetterl-Glausch 		else
1118b3cefd6bSJanis Schoetterl-Glausch 			prot = PROT_NONE;
1119c783631bSJanis Schoetterl-Glausch 		rc = trans_exc_ending(vcpu, rc, ga, ar, mode, prot, terminate);
1120c783631bSJanis Schoetterl-Glausch 	}
1121e613d834SJanis Schoetterl-Glausch out_unlock:
11228a242234SHeiko Carstens 	if (need_ipte_lock)
11230130337eSPierre Morel 		ipte_unlock(vcpu->kvm);
11247faa543dSJanis Schoetterl-Glausch 	if (nr_pages > ARRAY_SIZE(gpa_array))
11257faa543dSJanis Schoetterl-Glausch 		vfree(gpas);
112622938978SHeiko Carstens 	return rc;
112722938978SHeiko Carstens }
112822938978SHeiko Carstens 
112922938978SHeiko Carstens int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
113092c96321SDavid Hildenbrand 		      void *data, unsigned long len, enum gacc_mode mode)
113122938978SHeiko Carstens {
1132416e7f0cSJanis Schoetterl-Glausch 	unsigned int fragment_len;
1133416e7f0cSJanis Schoetterl-Glausch 	unsigned long gpa;
113422938978SHeiko Carstens 	int rc = 0;
113522938978SHeiko Carstens 
113622938978SHeiko Carstens 	while (len && !rc) {
113722938978SHeiko Carstens 		gpa = kvm_s390_real_to_abs(vcpu, gra);
1138416e7f0cSJanis Schoetterl-Glausch 		fragment_len = min(PAGE_SIZE - offset_in_page(gpa), len);
1139bad13799SJanis Schoetterl-Glausch 		rc = access_guest_page(vcpu->kvm, mode, gpa, data, fragment_len);
1140416e7f0cSJanis Schoetterl-Glausch 		len -= fragment_len;
1141416e7f0cSJanis Schoetterl-Glausch 		gra += fragment_len;
1142416e7f0cSJanis Schoetterl-Glausch 		data += fragment_len;
114322938978SHeiko Carstens 	}
114422938978SHeiko Carstens 	return rc;
114522938978SHeiko Carstens }
1146f8232c8cSThomas Huth 
1147f8232c8cSThomas Huth /**
11483fd49805SJanis Schoetterl-Glausch  * cmpxchg_guest_abs_with_key() - Perform cmpxchg on guest absolute address.
11493fd49805SJanis Schoetterl-Glausch  * @kvm: Virtual machine instance.
11503fd49805SJanis Schoetterl-Glausch  * @gpa: Absolute guest address of the location to be changed.
11513fd49805SJanis Schoetterl-Glausch  * @len: Operand length of the cmpxchg, required: 1 <= len <= 16. Providing a
11523fd49805SJanis Schoetterl-Glausch  *       non power of two will result in failure.
11533fd49805SJanis Schoetterl-Glausch  * @old_addr: Pointer to old value. If the location at @gpa contains this value,
11543fd49805SJanis Schoetterl-Glausch  *            the exchange will succeed. After calling cmpxchg_guest_abs_with_key()
11553fd49805SJanis Schoetterl-Glausch  *            *@old_addr contains the value at @gpa before the attempt to
11563fd49805SJanis Schoetterl-Glausch  *            exchange the value.
11573fd49805SJanis Schoetterl-Glausch  * @new: The value to place at @gpa.
11583fd49805SJanis Schoetterl-Glausch  * @access_key: The access key to use for the guest access.
11593fd49805SJanis Schoetterl-Glausch  * @success: output value indicating if an exchange occurred.
11603fd49805SJanis Schoetterl-Glausch  *
11613fd49805SJanis Schoetterl-Glausch  * Atomically exchange the value at @gpa by @new, if it contains *@old.
11623fd49805SJanis Schoetterl-Glausch  * Honors storage keys.
11633fd49805SJanis Schoetterl-Glausch  *
11643fd49805SJanis Schoetterl-Glausch  * Return: * 0: successful exchange
11653fd49805SJanis Schoetterl-Glausch  *         * >0: a program interruption code indicating the reason cmpxchg could
11663fd49805SJanis Schoetterl-Glausch  *               not be attempted
11673fd49805SJanis Schoetterl-Glausch  *         * -EINVAL: address misaligned or len not power of two
11683fd49805SJanis Schoetterl-Glausch  *         * -EAGAIN: transient failure (len 1 or 2)
11693fd49805SJanis Schoetterl-Glausch  *         * -EOPNOTSUPP: read-only memslot (should never occur)
11703fd49805SJanis Schoetterl-Glausch  */
11713fd49805SJanis Schoetterl-Glausch int cmpxchg_guest_abs_with_key(struct kvm *kvm, gpa_t gpa, int len,
11723fd49805SJanis Schoetterl-Glausch 			       __uint128_t *old_addr, __uint128_t new,
11733fd49805SJanis Schoetterl-Glausch 			       u8 access_key, bool *success)
11743fd49805SJanis Schoetterl-Glausch {
11753fd49805SJanis Schoetterl-Glausch 	gfn_t gfn = gpa_to_gfn(gpa);
11763fd49805SJanis Schoetterl-Glausch 	struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn);
11773fd49805SJanis Schoetterl-Glausch 	bool writable;
11783fd49805SJanis Schoetterl-Glausch 	hva_t hva;
11793fd49805SJanis Schoetterl-Glausch 	int ret;
11803fd49805SJanis Schoetterl-Glausch 
11813fd49805SJanis Schoetterl-Glausch 	if (!IS_ALIGNED(gpa, len))
11823fd49805SJanis Schoetterl-Glausch 		return -EINVAL;
11833fd49805SJanis Schoetterl-Glausch 
11843fd49805SJanis Schoetterl-Glausch 	hva = gfn_to_hva_memslot_prot(slot, gfn, &writable);
11853fd49805SJanis Schoetterl-Glausch 	if (kvm_is_error_hva(hva))
11863fd49805SJanis Schoetterl-Glausch 		return PGM_ADDRESSING;
11873fd49805SJanis Schoetterl-Glausch 	/*
11883fd49805SJanis Schoetterl-Glausch 	 * Check if it's a read-only memslot, even though that cannot occur
11893fd49805SJanis Schoetterl-Glausch 	 * since those are unsupported.
11903fd49805SJanis Schoetterl-Glausch 	 * Don't try to actually handle that case.
11913fd49805SJanis Schoetterl-Glausch 	 */
11923fd49805SJanis Schoetterl-Glausch 	if (!writable)
11933fd49805SJanis Schoetterl-Glausch 		return -EOPNOTSUPP;
11943fd49805SJanis Schoetterl-Glausch 
11953fd49805SJanis Schoetterl-Glausch 	hva += offset_in_page(gpa);
11963fd49805SJanis Schoetterl-Glausch 	/*
11973fd49805SJanis Schoetterl-Glausch 	 * The cmpxchg_user_key macro depends on the type of "old", so we need
11983fd49805SJanis Schoetterl-Glausch 	 * a case for each valid length and get some code duplication as long
11993fd49805SJanis Schoetterl-Glausch 	 * as we don't introduce a new macro.
12003fd49805SJanis Schoetterl-Glausch 	 */
12013fd49805SJanis Schoetterl-Glausch 	switch (len) {
12023fd49805SJanis Schoetterl-Glausch 	case 1: {
12033fd49805SJanis Schoetterl-Glausch 		u8 old;
12043fd49805SJanis Schoetterl-Glausch 
12053fd49805SJanis Schoetterl-Glausch 		ret = cmpxchg_user_key((u8 __user *)hva, &old, *old_addr, new, access_key);
12063fd49805SJanis Schoetterl-Glausch 		*success = !ret && old == *old_addr;
12073fd49805SJanis Schoetterl-Glausch 		*old_addr = old;
12083fd49805SJanis Schoetterl-Glausch 		break;
12093fd49805SJanis Schoetterl-Glausch 	}
12103fd49805SJanis Schoetterl-Glausch 	case 2: {
12113fd49805SJanis Schoetterl-Glausch 		u16 old;
12123fd49805SJanis Schoetterl-Glausch 
12133fd49805SJanis Schoetterl-Glausch 		ret = cmpxchg_user_key((u16 __user *)hva, &old, *old_addr, new, access_key);
12143fd49805SJanis Schoetterl-Glausch 		*success = !ret && old == *old_addr;
12153fd49805SJanis Schoetterl-Glausch 		*old_addr = old;
12163fd49805SJanis Schoetterl-Glausch 		break;
12173fd49805SJanis Schoetterl-Glausch 	}
12183fd49805SJanis Schoetterl-Glausch 	case 4: {
12193fd49805SJanis Schoetterl-Glausch 		u32 old;
12203fd49805SJanis Schoetterl-Glausch 
12213fd49805SJanis Schoetterl-Glausch 		ret = cmpxchg_user_key((u32 __user *)hva, &old, *old_addr, new, access_key);
12223fd49805SJanis Schoetterl-Glausch 		*success = !ret && old == *old_addr;
12233fd49805SJanis Schoetterl-Glausch 		*old_addr = old;
12243fd49805SJanis Schoetterl-Glausch 		break;
12253fd49805SJanis Schoetterl-Glausch 	}
12263fd49805SJanis Schoetterl-Glausch 	case 8: {
12273fd49805SJanis Schoetterl-Glausch 		u64 old;
12283fd49805SJanis Schoetterl-Glausch 
12293fd49805SJanis Schoetterl-Glausch 		ret = cmpxchg_user_key((u64 __user *)hva, &old, *old_addr, new, access_key);
12303fd49805SJanis Schoetterl-Glausch 		*success = !ret && old == *old_addr;
12313fd49805SJanis Schoetterl-Glausch 		*old_addr = old;
12323fd49805SJanis Schoetterl-Glausch 		break;
12333fd49805SJanis Schoetterl-Glausch 	}
12343fd49805SJanis Schoetterl-Glausch 	case 16: {
12353fd49805SJanis Schoetterl-Glausch 		__uint128_t old;
12363fd49805SJanis Schoetterl-Glausch 
12373fd49805SJanis Schoetterl-Glausch 		ret = cmpxchg_user_key((__uint128_t __user *)hva, &old, *old_addr, new, access_key);
12383fd49805SJanis Schoetterl-Glausch 		*success = !ret && old == *old_addr;
12393fd49805SJanis Schoetterl-Glausch 		*old_addr = old;
12403fd49805SJanis Schoetterl-Glausch 		break;
12413fd49805SJanis Schoetterl-Glausch 	}
12423fd49805SJanis Schoetterl-Glausch 	default:
12433fd49805SJanis Schoetterl-Glausch 		return -EINVAL;
12443fd49805SJanis Schoetterl-Glausch 	}
12453fd49805SJanis Schoetterl-Glausch 	if (*success)
12463fd49805SJanis Schoetterl-Glausch 		mark_page_dirty_in_slot(kvm, slot, gfn);
12473fd49805SJanis Schoetterl-Glausch 	/*
12483fd49805SJanis Schoetterl-Glausch 	 * Assume that the fault is caused by protection, either key protection
12493fd49805SJanis Schoetterl-Glausch 	 * or user page write protection.
12503fd49805SJanis Schoetterl-Glausch 	 */
12513fd49805SJanis Schoetterl-Glausch 	if (ret == -EFAULT)
12523fd49805SJanis Schoetterl-Glausch 		ret = PGM_PROTECTION;
12533fd49805SJanis Schoetterl-Glausch 	return ret;
12543fd49805SJanis Schoetterl-Glausch }
12553fd49805SJanis Schoetterl-Glausch 
12563fd49805SJanis Schoetterl-Glausch /**
1257e613d834SJanis Schoetterl-Glausch  * guest_translate_address_with_key - translate guest logical into guest absolute address
125825b5476aSJanosch Frank  * @vcpu: virtual cpu
125925b5476aSJanosch Frank  * @gva: Guest virtual address
126025b5476aSJanosch Frank  * @ar: Access register
126125b5476aSJanosch Frank  * @gpa: Guest physical address
126225b5476aSJanosch Frank  * @mode: Translation access mode
1263e613d834SJanis Schoetterl-Glausch  * @access_key: access key to mach the storage key with
12649fbc0276SThomas Huth  *
12659fbc0276SThomas Huth  * Parameter semantics are the same as the ones from guest_translate.
12669fbc0276SThomas Huth  * The memory contents at the guest address are not changed.
12679fbc0276SThomas Huth  *
12689fbc0276SThomas Huth  * Note: The IPTE lock is not taken during this function, so the caller
12699fbc0276SThomas Huth  * has to take care of this.
12709fbc0276SThomas Huth  */
1271e613d834SJanis Schoetterl-Glausch int guest_translate_address_with_key(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
1272e613d834SJanis Schoetterl-Glausch 				     unsigned long *gpa, enum gacc_mode mode,
1273e613d834SJanis Schoetterl-Glausch 				     u8 access_key)
12749fbc0276SThomas Huth {
12759fbc0276SThomas Huth 	union asce asce;
12769fbc0276SThomas Huth 	int rc;
12779fbc0276SThomas Huth 
12789fbc0276SThomas Huth 	gva = kvm_s390_logical_to_effective(vcpu, gva);
12796167375bSDavid Hildenbrand 	rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode);
1280664b4973SAlexander Yarygin 	if (rc)
1281664b4973SAlexander Yarygin 		return rc;
1282e613d834SJanis Schoetterl-Glausch 	return guest_range_to_gpas(vcpu, gva, ar, gpa, 1, asce, mode,
1283e613d834SJanis Schoetterl-Glausch 				   access_key);
1284e613d834SJanis Schoetterl-Glausch }
1285e613d834SJanis Schoetterl-Glausch 
12869fbc0276SThomas Huth /**
128741408c28SThomas Huth  * check_gva_range - test a range of guest virtual addresses for accessibility
128825b5476aSJanosch Frank  * @vcpu: virtual cpu
128925b5476aSJanosch Frank  * @gva: Guest virtual address
129025b5476aSJanosch Frank  * @ar: Access register
129125b5476aSJanosch Frank  * @length: Length of test range
129225b5476aSJanosch Frank  * @mode: Translation access mode
1293e613d834SJanis Schoetterl-Glausch  * @access_key: access key to mach the storage keys with
129441408c28SThomas Huth  */
129527f67f87SChristian Borntraeger int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
1296e613d834SJanis Schoetterl-Glausch 		    unsigned long length, enum gacc_mode mode, u8 access_key)
129741408c28SThomas Huth {
12987faa543dSJanis Schoetterl-Glausch 	union asce asce;
129941408c28SThomas Huth 	int rc = 0;
130041408c28SThomas Huth 
13017faa543dSJanis Schoetterl-Glausch 	rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode);
13027faa543dSJanis Schoetterl-Glausch 	if (rc)
13037faa543dSJanis Schoetterl-Glausch 		return rc;
13040130337eSPierre Morel 	ipte_lock(vcpu->kvm);
1305e613d834SJanis Schoetterl-Glausch 	rc = guest_range_to_gpas(vcpu, gva, ar, NULL, length, asce, mode,
1306e613d834SJanis Schoetterl-Glausch 				 access_key);
13070130337eSPierre Morel 	ipte_unlock(vcpu->kvm);
130841408c28SThomas Huth 
130941408c28SThomas Huth 	return rc;
131041408c28SThomas Huth }
131141408c28SThomas Huth 
131241408c28SThomas Huth /**
1313ef11c946SJanis Schoetterl-Glausch  * check_gpa_range - test a range of guest physical addresses for accessibility
1314ef11c946SJanis Schoetterl-Glausch  * @kvm: virtual machine instance
1315ef11c946SJanis Schoetterl-Glausch  * @gpa: guest physical address
1316ef11c946SJanis Schoetterl-Glausch  * @length: length of test range
1317ef11c946SJanis Schoetterl-Glausch  * @mode: access mode to test, relevant for storage keys
1318ef11c946SJanis Schoetterl-Glausch  * @access_key: access key to mach the storage keys with
1319ef11c946SJanis Schoetterl-Glausch  */
1320ef11c946SJanis Schoetterl-Glausch int check_gpa_range(struct kvm *kvm, unsigned long gpa, unsigned long length,
1321ef11c946SJanis Schoetterl-Glausch 		    enum gacc_mode mode, u8 access_key)
1322ef11c946SJanis Schoetterl-Glausch {
1323ef11c946SJanis Schoetterl-Glausch 	unsigned int fragment_len;
1324ef11c946SJanis Schoetterl-Glausch 	int rc = 0;
1325ef11c946SJanis Schoetterl-Glausch 
1326ef11c946SJanis Schoetterl-Glausch 	while (length && !rc) {
1327ef11c946SJanis Schoetterl-Glausch 		fragment_len = min(PAGE_SIZE - offset_in_page(gpa), length);
1328ef11c946SJanis Schoetterl-Glausch 		rc = vm_check_access_key(kvm, access_key, mode, gpa);
1329ef11c946SJanis Schoetterl-Glausch 		length -= fragment_len;
1330ef11c946SJanis Schoetterl-Glausch 		gpa += fragment_len;
1331ef11c946SJanis Schoetterl-Glausch 	}
1332ef11c946SJanis Schoetterl-Glausch 	return rc;
1333ef11c946SJanis Schoetterl-Glausch }
1334ef11c946SJanis Schoetterl-Glausch 
1335ef11c946SJanis Schoetterl-Glausch /**
1336dd9e5b7bSAlexander Yarygin  * kvm_s390_check_low_addr_prot_real - check for low-address protection
133725b5476aSJanosch Frank  * @vcpu: virtual cpu
1338dd9e5b7bSAlexander Yarygin  * @gra: Guest real address
1339f8232c8cSThomas Huth  *
1340f8232c8cSThomas Huth  * Checks whether an address is subject to low-address protection and set
1341f8232c8cSThomas Huth  * up vcpu->arch.pgm accordingly if necessary.
1342f8232c8cSThomas Huth  *
1343f8232c8cSThomas Huth  * Return: 0 if no protection exception, or PGM_PROTECTION if protected.
1344f8232c8cSThomas Huth  */
1345dd9e5b7bSAlexander Yarygin int kvm_s390_check_low_addr_prot_real(struct kvm_vcpu *vcpu, unsigned long gra)
1346f8232c8cSThomas Huth {
1347dd9e5b7bSAlexander Yarygin 	union ctlreg0 ctlreg0 = {.val = vcpu->arch.sie_block->gcr[0]};
1348f8232c8cSThomas Huth 
1349dd9e5b7bSAlexander Yarygin 	if (!ctlreg0.lap || !is_low_address(gra))
1350f8232c8cSThomas Huth 		return 0;
13513e3c67f6SDavid Hildenbrand 	return trans_exc(vcpu, PGM_PROTECTION, gra, 0, GACC_STORE, PROT_TYPE_LA);
1352f8232c8cSThomas Huth }
1353aa17aa57SMartin Schwidefsky 
1354aa17aa57SMartin Schwidefsky /**
1355aa17aa57SMartin Schwidefsky  * kvm_s390_shadow_tables - walk the guest page table and create shadow tables
1356aa17aa57SMartin Schwidefsky  * @sg: pointer to the shadow guest address space structure
1357aa17aa57SMartin Schwidefsky  * @saddr: faulting address in the shadow gmap
13585ac14bacSClaudio Imbrenda  * @pgt: pointer to the beginning of the page table for the given address if
13595ac14bacSClaudio Imbrenda  *	 successful (return value 0), or to the first invalid DAT entry in
13605ac14bacSClaudio Imbrenda  *	 case of exceptions (return value > 0)
136125b5476aSJanosch Frank  * @dat_protection: referenced memory is write protected
1362fd8d4e3aSDavid Hildenbrand  * @fake: pgt references contiguous guest memory block, not a pgtable
1363aa17aa57SMartin Schwidefsky  */
1364aa17aa57SMartin Schwidefsky static int kvm_s390_shadow_tables(struct gmap *sg, unsigned long saddr,
1365fd8d4e3aSDavid Hildenbrand 				  unsigned long *pgt, int *dat_protection,
1366fd8d4e3aSDavid Hildenbrand 				  int *fake)
1367aa17aa57SMartin Schwidefsky {
1368c3235e2dSNico Boehr 	struct kvm *kvm;
1369aa17aa57SMartin Schwidefsky 	struct gmap *parent;
1370aa17aa57SMartin Schwidefsky 	union asce asce;
1371aa17aa57SMartin Schwidefsky 	union vaddress vaddr;
1372aa17aa57SMartin Schwidefsky 	unsigned long ptr;
1373aa17aa57SMartin Schwidefsky 	int rc;
1374aa17aa57SMartin Schwidefsky 
1375fd8d4e3aSDavid Hildenbrand 	*fake = 0;
13761c65781bSDavid Hildenbrand 	*dat_protection = 0;
1377c3235e2dSNico Boehr 	kvm = sg->private;
1378aa17aa57SMartin Schwidefsky 	parent = sg->parent;
1379aa17aa57SMartin Schwidefsky 	vaddr.addr = saddr;
1380aa17aa57SMartin Schwidefsky 	asce.val = sg->orig_asce;
138158cdf5ebSHeiko Carstens 	ptr = asce.origin * PAGE_SIZE;
13823218f709SDavid Hildenbrand 	if (asce.r) {
13833218f709SDavid Hildenbrand 		*fake = 1;
1384addb63c1SHeiko Carstens 		ptr = 0;
13853218f709SDavid Hildenbrand 		asce.dt = ASCE_TYPE_REGION1;
13863218f709SDavid Hildenbrand 	}
1387aa17aa57SMartin Schwidefsky 	switch (asce.dt) {
1388aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION1:
1389addb63c1SHeiko Carstens 		if (vaddr.rfx01 > asce.tl && !*fake)
1390aa17aa57SMartin Schwidefsky 			return PGM_REGION_FIRST_TRANS;
1391aa17aa57SMartin Schwidefsky 		break;
1392aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION2:
1393aa17aa57SMartin Schwidefsky 		if (vaddr.rfx)
1394aa17aa57SMartin Schwidefsky 			return PGM_ASCE_TYPE;
1395aa17aa57SMartin Schwidefsky 		if (vaddr.rsx01 > asce.tl)
1396aa17aa57SMartin Schwidefsky 			return PGM_REGION_SECOND_TRANS;
1397aa17aa57SMartin Schwidefsky 		break;
1398aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION3:
1399aa17aa57SMartin Schwidefsky 		if (vaddr.rfx || vaddr.rsx)
1400aa17aa57SMartin Schwidefsky 			return PGM_ASCE_TYPE;
1401aa17aa57SMartin Schwidefsky 		if (vaddr.rtx01 > asce.tl)
1402aa17aa57SMartin Schwidefsky 			return PGM_REGION_THIRD_TRANS;
1403aa17aa57SMartin Schwidefsky 		break;
1404aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_SEGMENT:
1405aa17aa57SMartin Schwidefsky 		if (vaddr.rfx || vaddr.rsx || vaddr.rtx)
1406aa17aa57SMartin Schwidefsky 			return PGM_ASCE_TYPE;
1407aa17aa57SMartin Schwidefsky 		if (vaddr.sx01 > asce.tl)
1408aa17aa57SMartin Schwidefsky 			return PGM_SEGMENT_TRANSLATION;
1409aa17aa57SMartin Schwidefsky 		break;
1410aa17aa57SMartin Schwidefsky 	}
1411aa17aa57SMartin Schwidefsky 
1412aa17aa57SMartin Schwidefsky 	switch (asce.dt) {
1413aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION1: {
1414aa17aa57SMartin Schwidefsky 		union region1_table_entry rfte;
1415aa17aa57SMartin Schwidefsky 
14163218f709SDavid Hildenbrand 		if (*fake) {
141758cdf5ebSHeiko Carstens 			ptr += vaddr.rfx * _REGION1_SIZE;
14183218f709SDavid Hildenbrand 			rfte.val = ptr;
14193218f709SDavid Hildenbrand 			goto shadow_r2t;
14203218f709SDavid Hildenbrand 		}
14215ac14bacSClaudio Imbrenda 		*pgt = ptr + vaddr.rfx * 8;
1422aa17aa57SMartin Schwidefsky 		rc = gmap_read_table(parent, ptr + vaddr.rfx * 8, &rfte.val);
1423aa17aa57SMartin Schwidefsky 		if (rc)
1424aa17aa57SMartin Schwidefsky 			return rc;
1425aa17aa57SMartin Schwidefsky 		if (rfte.i)
1426aa17aa57SMartin Schwidefsky 			return PGM_REGION_FIRST_TRANS;
1427aa17aa57SMartin Schwidefsky 		if (rfte.tt != TABLE_TYPE_REGION1)
1428aa17aa57SMartin Schwidefsky 			return PGM_TRANSLATION_SPEC;
1429aa17aa57SMartin Schwidefsky 		if (vaddr.rsx01 < rfte.tf || vaddr.rsx01 > rfte.tl)
1430aa17aa57SMartin Schwidefsky 			return PGM_REGION_SECOND_TRANS;
14311c65781bSDavid Hildenbrand 		if (sg->edat_level >= 1)
14321c65781bSDavid Hildenbrand 			*dat_protection |= rfte.p;
143358cdf5ebSHeiko Carstens 		ptr = rfte.rto * PAGE_SIZE;
14343218f709SDavid Hildenbrand shadow_r2t:
14353218f709SDavid Hildenbrand 		rc = gmap_shadow_r2t(sg, saddr, rfte.val, *fake);
1436aa17aa57SMartin Schwidefsky 		if (rc)
1437aa17aa57SMartin Schwidefsky 			return rc;
1438c3235e2dSNico Boehr 		kvm->stat.gmap_shadow_r1_entry++;
14393b684a42SJoe Perches 	}
14403b684a42SJoe Perches 		fallthrough;
1441aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION2: {
1442aa17aa57SMartin Schwidefsky 		union region2_table_entry rste;
1443aa17aa57SMartin Schwidefsky 
14443218f709SDavid Hildenbrand 		if (*fake) {
144558cdf5ebSHeiko Carstens 			ptr += vaddr.rsx * _REGION2_SIZE;
14463218f709SDavid Hildenbrand 			rste.val = ptr;
14473218f709SDavid Hildenbrand 			goto shadow_r3t;
14483218f709SDavid Hildenbrand 		}
14495ac14bacSClaudio Imbrenda 		*pgt = ptr + vaddr.rsx * 8;
1450aa17aa57SMartin Schwidefsky 		rc = gmap_read_table(parent, ptr + vaddr.rsx * 8, &rste.val);
1451aa17aa57SMartin Schwidefsky 		if (rc)
1452aa17aa57SMartin Schwidefsky 			return rc;
1453aa17aa57SMartin Schwidefsky 		if (rste.i)
1454aa17aa57SMartin Schwidefsky 			return PGM_REGION_SECOND_TRANS;
1455aa17aa57SMartin Schwidefsky 		if (rste.tt != TABLE_TYPE_REGION2)
1456aa17aa57SMartin Schwidefsky 			return PGM_TRANSLATION_SPEC;
1457aa17aa57SMartin Schwidefsky 		if (vaddr.rtx01 < rste.tf || vaddr.rtx01 > rste.tl)
1458aa17aa57SMartin Schwidefsky 			return PGM_REGION_THIRD_TRANS;
14591c65781bSDavid Hildenbrand 		if (sg->edat_level >= 1)
14601c65781bSDavid Hildenbrand 			*dat_protection |= rste.p;
146158cdf5ebSHeiko Carstens 		ptr = rste.rto * PAGE_SIZE;
14623218f709SDavid Hildenbrand shadow_r3t:
14631c65781bSDavid Hildenbrand 		rste.p |= *dat_protection;
14643218f709SDavid Hildenbrand 		rc = gmap_shadow_r3t(sg, saddr, rste.val, *fake);
1465aa17aa57SMartin Schwidefsky 		if (rc)
1466aa17aa57SMartin Schwidefsky 			return rc;
1467c3235e2dSNico Boehr 		kvm->stat.gmap_shadow_r2_entry++;
14683b684a42SJoe Perches 	}
14693b684a42SJoe Perches 		fallthrough;
1470aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_REGION3: {
1471aa17aa57SMartin Schwidefsky 		union region3_table_entry rtte;
1472aa17aa57SMartin Schwidefsky 
14733218f709SDavid Hildenbrand 		if (*fake) {
147458cdf5ebSHeiko Carstens 			ptr += vaddr.rtx * _REGION3_SIZE;
14753218f709SDavid Hildenbrand 			rtte.val = ptr;
14763218f709SDavid Hildenbrand 			goto shadow_sgt;
14773218f709SDavid Hildenbrand 		}
14785ac14bacSClaudio Imbrenda 		*pgt = ptr + vaddr.rtx * 8;
1479aa17aa57SMartin Schwidefsky 		rc = gmap_read_table(parent, ptr + vaddr.rtx * 8, &rtte.val);
1480aa17aa57SMartin Schwidefsky 		if (rc)
1481aa17aa57SMartin Schwidefsky 			return rc;
1482aa17aa57SMartin Schwidefsky 		if (rtte.i)
1483aa17aa57SMartin Schwidefsky 			return PGM_REGION_THIRD_TRANS;
1484aa17aa57SMartin Schwidefsky 		if (rtte.tt != TABLE_TYPE_REGION3)
1485aa17aa57SMartin Schwidefsky 			return PGM_TRANSLATION_SPEC;
148618b89809SDavid Hildenbrand 		if (rtte.cr && asce.p && sg->edat_level >= 2)
148718b89809SDavid Hildenbrand 			return PGM_TRANSLATION_SPEC;
148818b89809SDavid Hildenbrand 		if (rtte.fc && sg->edat_level >= 2) {
14891c65781bSDavid Hildenbrand 			*dat_protection |= rtte.fc0.p;
149018b89809SDavid Hildenbrand 			*fake = 1;
149158cdf5ebSHeiko Carstens 			ptr = rtte.fc1.rfaa * _REGION3_SIZE;
149218b89809SDavid Hildenbrand 			rtte.val = ptr;
149318b89809SDavid Hildenbrand 			goto shadow_sgt;
149418b89809SDavid Hildenbrand 		}
1495aa17aa57SMartin Schwidefsky 		if (vaddr.sx01 < rtte.fc0.tf || vaddr.sx01 > rtte.fc0.tl)
1496aa17aa57SMartin Schwidefsky 			return PGM_SEGMENT_TRANSLATION;
14971c65781bSDavid Hildenbrand 		if (sg->edat_level >= 1)
14981c65781bSDavid Hildenbrand 			*dat_protection |= rtte.fc0.p;
149958cdf5ebSHeiko Carstens 		ptr = rtte.fc0.sto * PAGE_SIZE;
150018b89809SDavid Hildenbrand shadow_sgt:
15011c65781bSDavid Hildenbrand 		rtte.fc0.p |= *dat_protection;
150218b89809SDavid Hildenbrand 		rc = gmap_shadow_sgt(sg, saddr, rtte.val, *fake);
1503aa17aa57SMartin Schwidefsky 		if (rc)
1504aa17aa57SMartin Schwidefsky 			return rc;
1505c3235e2dSNico Boehr 		kvm->stat.gmap_shadow_r3_entry++;
15063b684a42SJoe Perches 	}
15073b684a42SJoe Perches 		fallthrough;
1508aa17aa57SMartin Schwidefsky 	case ASCE_TYPE_SEGMENT: {
1509aa17aa57SMartin Schwidefsky 		union segment_table_entry ste;
1510aa17aa57SMartin Schwidefsky 
151118b89809SDavid Hildenbrand 		if (*fake) {
151258cdf5ebSHeiko Carstens 			ptr += vaddr.sx * _SEGMENT_SIZE;
151318b89809SDavid Hildenbrand 			ste.val = ptr;
151418b89809SDavid Hildenbrand 			goto shadow_pgt;
151518b89809SDavid Hildenbrand 		}
15165ac14bacSClaudio Imbrenda 		*pgt = ptr + vaddr.sx * 8;
1517aa17aa57SMartin Schwidefsky 		rc = gmap_read_table(parent, ptr + vaddr.sx * 8, &ste.val);
1518aa17aa57SMartin Schwidefsky 		if (rc)
1519aa17aa57SMartin Schwidefsky 			return rc;
1520aa17aa57SMartin Schwidefsky 		if (ste.i)
1521aa17aa57SMartin Schwidefsky 			return PGM_SEGMENT_TRANSLATION;
1522aa17aa57SMartin Schwidefsky 		if (ste.tt != TABLE_TYPE_SEGMENT)
1523aa17aa57SMartin Schwidefsky 			return PGM_TRANSLATION_SPEC;
1524aa17aa57SMartin Schwidefsky 		if (ste.cs && asce.p)
1525aa17aa57SMartin Schwidefsky 			return PGM_TRANSLATION_SPEC;
15261c65781bSDavid Hildenbrand 		*dat_protection |= ste.fc0.p;
1527fd8d4e3aSDavid Hildenbrand 		if (ste.fc && sg->edat_level >= 1) {
1528fd8d4e3aSDavid Hildenbrand 			*fake = 1;
152958cdf5ebSHeiko Carstens 			ptr = ste.fc1.sfaa * _SEGMENT_SIZE;
1530fd8d4e3aSDavid Hildenbrand 			ste.val = ptr;
1531fd8d4e3aSDavid Hildenbrand 			goto shadow_pgt;
1532fd8d4e3aSDavid Hildenbrand 		}
153358cdf5ebSHeiko Carstens 		ptr = ste.fc0.pto * (PAGE_SIZE / 2);
1534fd8d4e3aSDavid Hildenbrand shadow_pgt:
15351c65781bSDavid Hildenbrand 		ste.fc0.p |= *dat_protection;
1536fd8d4e3aSDavid Hildenbrand 		rc = gmap_shadow_pgt(sg, saddr, ste.val, *fake);
1537aa17aa57SMartin Schwidefsky 		if (rc)
1538aa17aa57SMartin Schwidefsky 			return rc;
1539c3235e2dSNico Boehr 		kvm->stat.gmap_shadow_sg_entry++;
1540aa17aa57SMartin Schwidefsky 	}
1541aa17aa57SMartin Schwidefsky 	}
1542aa17aa57SMartin Schwidefsky 	/* Return the parent address of the page table */
1543aa17aa57SMartin Schwidefsky 	*pgt = ptr;
1544aa17aa57SMartin Schwidefsky 	return 0;
1545aa17aa57SMartin Schwidefsky }
1546aa17aa57SMartin Schwidefsky 
1547aa17aa57SMartin Schwidefsky /**
1548aa17aa57SMartin Schwidefsky  * kvm_s390_shadow_fault - handle fault on a shadow page table
1549f4debb40SDavid Hildenbrand  * @vcpu: virtual cpu
1550aa17aa57SMartin Schwidefsky  * @sg: pointer to the shadow guest address space structure
1551aa17aa57SMartin Schwidefsky  * @saddr: faulting address in the shadow gmap
15525ac14bacSClaudio Imbrenda  * @datptr: will contain the address of the faulting DAT table entry, or of
15535ac14bacSClaudio Imbrenda  *	    the valid leaf, plus some flags
1554aa17aa57SMartin Schwidefsky  *
1555aa17aa57SMartin Schwidefsky  * Returns: - 0 if the shadow fault was successfully resolved
1556aa17aa57SMartin Schwidefsky  *	    - > 0 (pgm exception code) on exceptions while faulting
1557aa17aa57SMartin Schwidefsky  *	    - -EAGAIN if the caller can retry immediately
1558aa17aa57SMartin Schwidefsky  *	    - -EFAULT when accessing invalid guest addresses
1559aa17aa57SMartin Schwidefsky  *	    - -ENOMEM if out of memory
1560aa17aa57SMartin Schwidefsky  */
1561f4debb40SDavid Hildenbrand int kvm_s390_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg,
15625ac14bacSClaudio Imbrenda 			  unsigned long saddr, unsigned long *datptr)
1563aa17aa57SMartin Schwidefsky {
1564aa17aa57SMartin Schwidefsky 	union vaddress vaddr;
1565aa17aa57SMartin Schwidefsky 	union page_table_entry pte;
15665ac14bacSClaudio Imbrenda 	unsigned long pgt = 0;
1567fd8d4e3aSDavid Hildenbrand 	int dat_protection, fake;
1568aa17aa57SMartin Schwidefsky 	int rc;
1569aa17aa57SMartin Schwidefsky 
1570d8ed45c5SMichel Lespinasse 	mmap_read_lock(sg->mm);
1571f4debb40SDavid Hildenbrand 	/*
1572f4debb40SDavid Hildenbrand 	 * We don't want any guest-2 tables to change - so the parent
1573f4debb40SDavid Hildenbrand 	 * tables/pointers we read stay valid - unshadowing is however
1574f4debb40SDavid Hildenbrand 	 * always possible - only guest_table_lock protects us.
1575f4debb40SDavid Hildenbrand 	 */
15760130337eSPierre Morel 	ipte_lock(vcpu->kvm);
1577e52f8b61SDavid Hildenbrand 
1578fd8d4e3aSDavid Hildenbrand 	rc = gmap_shadow_pgt_lookup(sg, saddr, &pgt, &dat_protection, &fake);
1579aa17aa57SMartin Schwidefsky 	if (rc)
1580fd8d4e3aSDavid Hildenbrand 		rc = kvm_s390_shadow_tables(sg, saddr, &pgt, &dat_protection,
1581fd8d4e3aSDavid Hildenbrand 					    &fake);
1582aa17aa57SMartin Schwidefsky 
1583aa17aa57SMartin Schwidefsky 	vaddr.addr = saddr;
1584fd8d4e3aSDavid Hildenbrand 	if (fake) {
158558cdf5ebSHeiko Carstens 		pte.val = pgt + vaddr.px * PAGE_SIZE;
1586fd8d4e3aSDavid Hildenbrand 		goto shadow_page;
1587fd8d4e3aSDavid Hildenbrand 	}
15885ac14bacSClaudio Imbrenda 
15895ac14bacSClaudio Imbrenda 	switch (rc) {
15905ac14bacSClaudio Imbrenda 	case PGM_SEGMENT_TRANSLATION:
15915ac14bacSClaudio Imbrenda 	case PGM_REGION_THIRD_TRANS:
15925ac14bacSClaudio Imbrenda 	case PGM_REGION_SECOND_TRANS:
15935ac14bacSClaudio Imbrenda 	case PGM_REGION_FIRST_TRANS:
15945ac14bacSClaudio Imbrenda 		pgt |= PEI_NOT_PTE;
15955ac14bacSClaudio Imbrenda 		break;
15965ac14bacSClaudio Imbrenda 	case 0:
15975ac14bacSClaudio Imbrenda 		pgt += vaddr.px * 8;
15985ac14bacSClaudio Imbrenda 		rc = gmap_read_table(sg->parent, pgt, &pte.val);
15995ac14bacSClaudio Imbrenda 	}
16005ac14bacSClaudio Imbrenda 	if (datptr)
16015ac14bacSClaudio Imbrenda 		*datptr = pgt | dat_protection * PEI_DAT_PROT;
1602e52f8b61SDavid Hildenbrand 	if (!rc && pte.i)
1603e52f8b61SDavid Hildenbrand 		rc = PGM_PAGE_TRANSLATION;
1604232b8e3bSHeiko Carstens 	if (!rc && pte.z)
1605e52f8b61SDavid Hildenbrand 		rc = PGM_TRANSLATION_SPEC;
1606fd8d4e3aSDavid Hildenbrand shadow_page:
160700fc062dSDavid Hildenbrand 	pte.p |= dat_protection;
1608e52f8b61SDavid Hildenbrand 	if (!rc)
1609a9d23e71SDavid Hildenbrand 		rc = gmap_shadow_page(sg, saddr, __pte(pte.val));
1610c3235e2dSNico Boehr 	vcpu->kvm->stat.gmap_shadow_pg_entry++;
16110130337eSPierre Morel 	ipte_unlock(vcpu->kvm);
1612d8ed45c5SMichel Lespinasse 	mmap_read_unlock(sg->mm);
1613aa17aa57SMartin Schwidefsky 	return rc;
1614aa17aa57SMartin Schwidefsky }
1615