xref: /kernel/linux/linux-5.10/arch/s390/kvm/gaccess.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * guest access functions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2014
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
108c2ecf20Sopenharmony_ci#include <linux/mm_types.h>
118c2ecf20Sopenharmony_ci#include <linux/err.h>
128c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <asm/gmap.h>
158c2ecf20Sopenharmony_ci#include "kvm-s390.h"
168c2ecf20Sopenharmony_ci#include "gaccess.h"
178c2ecf20Sopenharmony_ci#include <asm/switch_to.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ciunion asce {
208c2ecf20Sopenharmony_ci	unsigned long val;
218c2ecf20Sopenharmony_ci	struct {
228c2ecf20Sopenharmony_ci		unsigned long origin : 52; /* Region- or Segment-Table Origin */
238c2ecf20Sopenharmony_ci		unsigned long	 : 2;
248c2ecf20Sopenharmony_ci		unsigned long g  : 1; /* Subspace Group Control */
258c2ecf20Sopenharmony_ci		unsigned long p  : 1; /* Private Space Control */
268c2ecf20Sopenharmony_ci		unsigned long s  : 1; /* Storage-Alteration-Event Control */
278c2ecf20Sopenharmony_ci		unsigned long x  : 1; /* Space-Switch-Event Control */
288c2ecf20Sopenharmony_ci		unsigned long r  : 1; /* Real-Space Control */
298c2ecf20Sopenharmony_ci		unsigned long	 : 1;
308c2ecf20Sopenharmony_ci		unsigned long dt : 2; /* Designation-Type Control */
318c2ecf20Sopenharmony_ci		unsigned long tl : 2; /* Region- or Segment-Table Length */
328c2ecf20Sopenharmony_ci	};
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cienum {
368c2ecf20Sopenharmony_ci	ASCE_TYPE_SEGMENT = 0,
378c2ecf20Sopenharmony_ci	ASCE_TYPE_REGION3 = 1,
388c2ecf20Sopenharmony_ci	ASCE_TYPE_REGION2 = 2,
398c2ecf20Sopenharmony_ci	ASCE_TYPE_REGION1 = 3
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ciunion region1_table_entry {
438c2ecf20Sopenharmony_ci	unsigned long val;
448c2ecf20Sopenharmony_ci	struct {
458c2ecf20Sopenharmony_ci		unsigned long rto: 52;/* Region-Table Origin */
468c2ecf20Sopenharmony_ci		unsigned long	 : 2;
478c2ecf20Sopenharmony_ci		unsigned long p  : 1; /* DAT-Protection Bit */
488c2ecf20Sopenharmony_ci		unsigned long	 : 1;
498c2ecf20Sopenharmony_ci		unsigned long tf : 2; /* Region-Second-Table Offset */
508c2ecf20Sopenharmony_ci		unsigned long i  : 1; /* Region-Invalid Bit */
518c2ecf20Sopenharmony_ci		unsigned long	 : 1;
528c2ecf20Sopenharmony_ci		unsigned long tt : 2; /* Table-Type Bits */
538c2ecf20Sopenharmony_ci		unsigned long tl : 2; /* Region-Second-Table Length */
548c2ecf20Sopenharmony_ci	};
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ciunion region2_table_entry {
588c2ecf20Sopenharmony_ci	unsigned long val;
598c2ecf20Sopenharmony_ci	struct {
608c2ecf20Sopenharmony_ci		unsigned long rto: 52;/* Region-Table Origin */
618c2ecf20Sopenharmony_ci		unsigned long	 : 2;
628c2ecf20Sopenharmony_ci		unsigned long p  : 1; /* DAT-Protection Bit */
638c2ecf20Sopenharmony_ci		unsigned long	 : 1;
648c2ecf20Sopenharmony_ci		unsigned long tf : 2; /* Region-Third-Table Offset */
658c2ecf20Sopenharmony_ci		unsigned long i  : 1; /* Region-Invalid Bit */
668c2ecf20Sopenharmony_ci		unsigned long	 : 1;
678c2ecf20Sopenharmony_ci		unsigned long tt : 2; /* Table-Type Bits */
688c2ecf20Sopenharmony_ci		unsigned long tl : 2; /* Region-Third-Table Length */
698c2ecf20Sopenharmony_ci	};
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct region3_table_entry_fc0 {
738c2ecf20Sopenharmony_ci	unsigned long sto: 52;/* Segment-Table Origin */
748c2ecf20Sopenharmony_ci	unsigned long	 : 1;
758c2ecf20Sopenharmony_ci	unsigned long fc : 1; /* Format-Control */
768c2ecf20Sopenharmony_ci	unsigned long p  : 1; /* DAT-Protection Bit */
778c2ecf20Sopenharmony_ci	unsigned long	 : 1;
788c2ecf20Sopenharmony_ci	unsigned long tf : 2; /* Segment-Table Offset */
798c2ecf20Sopenharmony_ci	unsigned long i  : 1; /* Region-Invalid Bit */
808c2ecf20Sopenharmony_ci	unsigned long cr : 1; /* Common-Region Bit */
818c2ecf20Sopenharmony_ci	unsigned long tt : 2; /* Table-Type Bits */
828c2ecf20Sopenharmony_ci	unsigned long tl : 2; /* Segment-Table Length */
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistruct region3_table_entry_fc1 {
868c2ecf20Sopenharmony_ci	unsigned long rfaa : 33; /* Region-Frame Absolute Address */
878c2ecf20Sopenharmony_ci	unsigned long	 : 14;
888c2ecf20Sopenharmony_ci	unsigned long av : 1; /* ACCF-Validity Control */
898c2ecf20Sopenharmony_ci	unsigned long acc: 4; /* Access-Control Bits */
908c2ecf20Sopenharmony_ci	unsigned long f  : 1; /* Fetch-Protection Bit */
918c2ecf20Sopenharmony_ci	unsigned long fc : 1; /* Format-Control */
928c2ecf20Sopenharmony_ci	unsigned long p  : 1; /* DAT-Protection Bit */
938c2ecf20Sopenharmony_ci	unsigned long iep: 1; /* Instruction-Execution-Protection */
948c2ecf20Sopenharmony_ci	unsigned long	 : 2;
958c2ecf20Sopenharmony_ci	unsigned long i  : 1; /* Region-Invalid Bit */
968c2ecf20Sopenharmony_ci	unsigned long cr : 1; /* Common-Region Bit */
978c2ecf20Sopenharmony_ci	unsigned long tt : 2; /* Table-Type Bits */
988c2ecf20Sopenharmony_ci	unsigned long	 : 2;
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ciunion region3_table_entry {
1028c2ecf20Sopenharmony_ci	unsigned long val;
1038c2ecf20Sopenharmony_ci	struct region3_table_entry_fc0 fc0;
1048c2ecf20Sopenharmony_ci	struct region3_table_entry_fc1 fc1;
1058c2ecf20Sopenharmony_ci	struct {
1068c2ecf20Sopenharmony_ci		unsigned long	 : 53;
1078c2ecf20Sopenharmony_ci		unsigned long fc : 1; /* Format-Control */
1088c2ecf20Sopenharmony_ci		unsigned long	 : 4;
1098c2ecf20Sopenharmony_ci		unsigned long i  : 1; /* Region-Invalid Bit */
1108c2ecf20Sopenharmony_ci		unsigned long cr : 1; /* Common-Region Bit */
1118c2ecf20Sopenharmony_ci		unsigned long tt : 2; /* Table-Type Bits */
1128c2ecf20Sopenharmony_ci		unsigned long	 : 2;
1138c2ecf20Sopenharmony_ci	};
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistruct segment_entry_fc0 {
1178c2ecf20Sopenharmony_ci	unsigned long pto: 53;/* Page-Table Origin */
1188c2ecf20Sopenharmony_ci	unsigned long fc : 1; /* Format-Control */
1198c2ecf20Sopenharmony_ci	unsigned long p  : 1; /* DAT-Protection Bit */
1208c2ecf20Sopenharmony_ci	unsigned long	 : 3;
1218c2ecf20Sopenharmony_ci	unsigned long i  : 1; /* Segment-Invalid Bit */
1228c2ecf20Sopenharmony_ci	unsigned long cs : 1; /* Common-Segment Bit */
1238c2ecf20Sopenharmony_ci	unsigned long tt : 2; /* Table-Type Bits */
1248c2ecf20Sopenharmony_ci	unsigned long	 : 2;
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistruct segment_entry_fc1 {
1288c2ecf20Sopenharmony_ci	unsigned long sfaa : 44; /* Segment-Frame Absolute Address */
1298c2ecf20Sopenharmony_ci	unsigned long	 : 3;
1308c2ecf20Sopenharmony_ci	unsigned long av : 1; /* ACCF-Validity Control */
1318c2ecf20Sopenharmony_ci	unsigned long acc: 4; /* Access-Control Bits */
1328c2ecf20Sopenharmony_ci	unsigned long f  : 1; /* Fetch-Protection Bit */
1338c2ecf20Sopenharmony_ci	unsigned long fc : 1; /* Format-Control */
1348c2ecf20Sopenharmony_ci	unsigned long p  : 1; /* DAT-Protection Bit */
1358c2ecf20Sopenharmony_ci	unsigned long iep: 1; /* Instruction-Execution-Protection */
1368c2ecf20Sopenharmony_ci	unsigned long	 : 2;
1378c2ecf20Sopenharmony_ci	unsigned long i  : 1; /* Segment-Invalid Bit */
1388c2ecf20Sopenharmony_ci	unsigned long cs : 1; /* Common-Segment Bit */
1398c2ecf20Sopenharmony_ci	unsigned long tt : 2; /* Table-Type Bits */
1408c2ecf20Sopenharmony_ci	unsigned long	 : 2;
1418c2ecf20Sopenharmony_ci};
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ciunion segment_table_entry {
1448c2ecf20Sopenharmony_ci	unsigned long val;
1458c2ecf20Sopenharmony_ci	struct segment_entry_fc0 fc0;
1468c2ecf20Sopenharmony_ci	struct segment_entry_fc1 fc1;
1478c2ecf20Sopenharmony_ci	struct {
1488c2ecf20Sopenharmony_ci		unsigned long	 : 53;
1498c2ecf20Sopenharmony_ci		unsigned long fc : 1; /* Format-Control */
1508c2ecf20Sopenharmony_ci		unsigned long	 : 4;
1518c2ecf20Sopenharmony_ci		unsigned long i  : 1; /* Segment-Invalid Bit */
1528c2ecf20Sopenharmony_ci		unsigned long cs : 1; /* Common-Segment Bit */
1538c2ecf20Sopenharmony_ci		unsigned long tt : 2; /* Table-Type Bits */
1548c2ecf20Sopenharmony_ci		unsigned long	 : 2;
1558c2ecf20Sopenharmony_ci	};
1568c2ecf20Sopenharmony_ci};
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cienum {
1598c2ecf20Sopenharmony_ci	TABLE_TYPE_SEGMENT = 0,
1608c2ecf20Sopenharmony_ci	TABLE_TYPE_REGION3 = 1,
1618c2ecf20Sopenharmony_ci	TABLE_TYPE_REGION2 = 2,
1628c2ecf20Sopenharmony_ci	TABLE_TYPE_REGION1 = 3
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ciunion page_table_entry {
1668c2ecf20Sopenharmony_ci	unsigned long val;
1678c2ecf20Sopenharmony_ci	struct {
1688c2ecf20Sopenharmony_ci		unsigned long pfra : 52; /* Page-Frame Real Address */
1698c2ecf20Sopenharmony_ci		unsigned long z  : 1; /* Zero Bit */
1708c2ecf20Sopenharmony_ci		unsigned long i  : 1; /* Page-Invalid Bit */
1718c2ecf20Sopenharmony_ci		unsigned long p  : 1; /* DAT-Protection Bit */
1728c2ecf20Sopenharmony_ci		unsigned long iep: 1; /* Instruction-Execution-Protection */
1738c2ecf20Sopenharmony_ci		unsigned long	 : 8;
1748c2ecf20Sopenharmony_ci	};
1758c2ecf20Sopenharmony_ci};
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci/*
1788c2ecf20Sopenharmony_ci * vaddress union in order to easily decode a virtual address into its
1798c2ecf20Sopenharmony_ci * region first index, region second index etc. parts.
1808c2ecf20Sopenharmony_ci */
1818c2ecf20Sopenharmony_ciunion vaddress {
1828c2ecf20Sopenharmony_ci	unsigned long addr;
1838c2ecf20Sopenharmony_ci	struct {
1848c2ecf20Sopenharmony_ci		unsigned long rfx : 11;
1858c2ecf20Sopenharmony_ci		unsigned long rsx : 11;
1868c2ecf20Sopenharmony_ci		unsigned long rtx : 11;
1878c2ecf20Sopenharmony_ci		unsigned long sx  : 11;
1888c2ecf20Sopenharmony_ci		unsigned long px  : 8;
1898c2ecf20Sopenharmony_ci		unsigned long bx  : 12;
1908c2ecf20Sopenharmony_ci	};
1918c2ecf20Sopenharmony_ci	struct {
1928c2ecf20Sopenharmony_ci		unsigned long rfx01 : 2;
1938c2ecf20Sopenharmony_ci		unsigned long	    : 9;
1948c2ecf20Sopenharmony_ci		unsigned long rsx01 : 2;
1958c2ecf20Sopenharmony_ci		unsigned long	    : 9;
1968c2ecf20Sopenharmony_ci		unsigned long rtx01 : 2;
1978c2ecf20Sopenharmony_ci		unsigned long	    : 9;
1988c2ecf20Sopenharmony_ci		unsigned long sx01  : 2;
1998c2ecf20Sopenharmony_ci		unsigned long	    : 29;
2008c2ecf20Sopenharmony_ci	};
2018c2ecf20Sopenharmony_ci};
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/*
2048c2ecf20Sopenharmony_ci * raddress union which will contain the result (real or absolute address)
2058c2ecf20Sopenharmony_ci * after a page table walk. The rfaa, sfaa and pfra members are used to
2068c2ecf20Sopenharmony_ci * simply assign them the value of a region, segment or page table entry.
2078c2ecf20Sopenharmony_ci */
2088c2ecf20Sopenharmony_ciunion raddress {
2098c2ecf20Sopenharmony_ci	unsigned long addr;
2108c2ecf20Sopenharmony_ci	unsigned long rfaa : 33; /* Region-Frame Absolute Address */
2118c2ecf20Sopenharmony_ci	unsigned long sfaa : 44; /* Segment-Frame Absolute Address */
2128c2ecf20Sopenharmony_ci	unsigned long pfra : 52; /* Page-Frame Real Address */
2138c2ecf20Sopenharmony_ci};
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ciunion alet {
2168c2ecf20Sopenharmony_ci	u32 val;
2178c2ecf20Sopenharmony_ci	struct {
2188c2ecf20Sopenharmony_ci		u32 reserved : 7;
2198c2ecf20Sopenharmony_ci		u32 p        : 1;
2208c2ecf20Sopenharmony_ci		u32 alesn    : 8;
2218c2ecf20Sopenharmony_ci		u32 alen     : 16;
2228c2ecf20Sopenharmony_ci	};
2238c2ecf20Sopenharmony_ci};
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ciunion ald {
2268c2ecf20Sopenharmony_ci	u32 val;
2278c2ecf20Sopenharmony_ci	struct {
2288c2ecf20Sopenharmony_ci		u32     : 1;
2298c2ecf20Sopenharmony_ci		u32 alo : 24;
2308c2ecf20Sopenharmony_ci		u32 all : 7;
2318c2ecf20Sopenharmony_ci	};
2328c2ecf20Sopenharmony_ci};
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistruct ale {
2358c2ecf20Sopenharmony_ci	unsigned long i      : 1; /* ALEN-Invalid Bit */
2368c2ecf20Sopenharmony_ci	unsigned long        : 5;
2378c2ecf20Sopenharmony_ci	unsigned long fo     : 1; /* Fetch-Only Bit */
2388c2ecf20Sopenharmony_ci	unsigned long p      : 1; /* Private Bit */
2398c2ecf20Sopenharmony_ci	unsigned long alesn  : 8; /* Access-List-Entry Sequence Number */
2408c2ecf20Sopenharmony_ci	unsigned long aleax  : 16; /* Access-List-Entry Authorization Index */
2418c2ecf20Sopenharmony_ci	unsigned long        : 32;
2428c2ecf20Sopenharmony_ci	unsigned long        : 1;
2438c2ecf20Sopenharmony_ci	unsigned long asteo  : 25; /* ASN-Second-Table-Entry Origin */
2448c2ecf20Sopenharmony_ci	unsigned long        : 6;
2458c2ecf20Sopenharmony_ci	unsigned long astesn : 32; /* ASTE Sequence Number */
2468c2ecf20Sopenharmony_ci};
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistruct aste {
2498c2ecf20Sopenharmony_ci	unsigned long i      : 1; /* ASX-Invalid Bit */
2508c2ecf20Sopenharmony_ci	unsigned long ato    : 29; /* Authority-Table Origin */
2518c2ecf20Sopenharmony_ci	unsigned long        : 1;
2528c2ecf20Sopenharmony_ci	unsigned long b      : 1; /* Base-Space Bit */
2538c2ecf20Sopenharmony_ci	unsigned long ax     : 16; /* Authorization Index */
2548c2ecf20Sopenharmony_ci	unsigned long atl    : 12; /* Authority-Table Length */
2558c2ecf20Sopenharmony_ci	unsigned long        : 2;
2568c2ecf20Sopenharmony_ci	unsigned long ca     : 1; /* Controlled-ASN Bit */
2578c2ecf20Sopenharmony_ci	unsigned long ra     : 1; /* Reusable-ASN Bit */
2588c2ecf20Sopenharmony_ci	unsigned long asce   : 64; /* Address-Space-Control Element */
2598c2ecf20Sopenharmony_ci	unsigned long ald    : 32;
2608c2ecf20Sopenharmony_ci	unsigned long astesn : 32;
2618c2ecf20Sopenharmony_ci	/* .. more fields there */
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ciint ipte_lock_held(struct kvm_vcpu *vcpu)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->eca & ECA_SII) {
2678c2ecf20Sopenharmony_ci		int rc;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci		read_lock(&vcpu->kvm->arch.sca_lock);
2708c2ecf20Sopenharmony_ci		rc = kvm_s390_get_ipte_control(vcpu->kvm)->kh != 0;
2718c2ecf20Sopenharmony_ci		read_unlock(&vcpu->kvm->arch.sca_lock);
2728c2ecf20Sopenharmony_ci		return rc;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci	return vcpu->kvm->arch.ipte_lock_count != 0;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic void ipte_lock_simple(struct kvm_vcpu *vcpu)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	union ipte_control old, new, *ic;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	mutex_lock(&vcpu->kvm->arch.ipte_mutex);
2828c2ecf20Sopenharmony_ci	vcpu->kvm->arch.ipte_lock_count++;
2838c2ecf20Sopenharmony_ci	if (vcpu->kvm->arch.ipte_lock_count > 1)
2848c2ecf20Sopenharmony_ci		goto out;
2858c2ecf20Sopenharmony_ciretry:
2868c2ecf20Sopenharmony_ci	read_lock(&vcpu->kvm->arch.sca_lock);
2878c2ecf20Sopenharmony_ci	ic = kvm_s390_get_ipte_control(vcpu->kvm);
2888c2ecf20Sopenharmony_ci	do {
2898c2ecf20Sopenharmony_ci		old = READ_ONCE(*ic);
2908c2ecf20Sopenharmony_ci		if (old.k) {
2918c2ecf20Sopenharmony_ci			read_unlock(&vcpu->kvm->arch.sca_lock);
2928c2ecf20Sopenharmony_ci			cond_resched();
2938c2ecf20Sopenharmony_ci			goto retry;
2948c2ecf20Sopenharmony_ci		}
2958c2ecf20Sopenharmony_ci		new = old;
2968c2ecf20Sopenharmony_ci		new.k = 1;
2978c2ecf20Sopenharmony_ci	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
2988c2ecf20Sopenharmony_ci	read_unlock(&vcpu->kvm->arch.sca_lock);
2998c2ecf20Sopenharmony_ciout:
3008c2ecf20Sopenharmony_ci	mutex_unlock(&vcpu->kvm->arch.ipte_mutex);
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic void ipte_unlock_simple(struct kvm_vcpu *vcpu)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	union ipte_control old, new, *ic;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	mutex_lock(&vcpu->kvm->arch.ipte_mutex);
3088c2ecf20Sopenharmony_ci	vcpu->kvm->arch.ipte_lock_count--;
3098c2ecf20Sopenharmony_ci	if (vcpu->kvm->arch.ipte_lock_count)
3108c2ecf20Sopenharmony_ci		goto out;
3118c2ecf20Sopenharmony_ci	read_lock(&vcpu->kvm->arch.sca_lock);
3128c2ecf20Sopenharmony_ci	ic = kvm_s390_get_ipte_control(vcpu->kvm);
3138c2ecf20Sopenharmony_ci	do {
3148c2ecf20Sopenharmony_ci		old = READ_ONCE(*ic);
3158c2ecf20Sopenharmony_ci		new = old;
3168c2ecf20Sopenharmony_ci		new.k = 0;
3178c2ecf20Sopenharmony_ci	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
3188c2ecf20Sopenharmony_ci	read_unlock(&vcpu->kvm->arch.sca_lock);
3198c2ecf20Sopenharmony_ci	wake_up(&vcpu->kvm->arch.ipte_wq);
3208c2ecf20Sopenharmony_ciout:
3218c2ecf20Sopenharmony_ci	mutex_unlock(&vcpu->kvm->arch.ipte_mutex);
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic void ipte_lock_siif(struct kvm_vcpu *vcpu)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	union ipte_control old, new, *ic;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ciretry:
3298c2ecf20Sopenharmony_ci	read_lock(&vcpu->kvm->arch.sca_lock);
3308c2ecf20Sopenharmony_ci	ic = kvm_s390_get_ipte_control(vcpu->kvm);
3318c2ecf20Sopenharmony_ci	do {
3328c2ecf20Sopenharmony_ci		old = READ_ONCE(*ic);
3338c2ecf20Sopenharmony_ci		if (old.kg) {
3348c2ecf20Sopenharmony_ci			read_unlock(&vcpu->kvm->arch.sca_lock);
3358c2ecf20Sopenharmony_ci			cond_resched();
3368c2ecf20Sopenharmony_ci			goto retry;
3378c2ecf20Sopenharmony_ci		}
3388c2ecf20Sopenharmony_ci		new = old;
3398c2ecf20Sopenharmony_ci		new.k = 1;
3408c2ecf20Sopenharmony_ci		new.kh++;
3418c2ecf20Sopenharmony_ci	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
3428c2ecf20Sopenharmony_ci	read_unlock(&vcpu->kvm->arch.sca_lock);
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic void ipte_unlock_siif(struct kvm_vcpu *vcpu)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	union ipte_control old, new, *ic;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	read_lock(&vcpu->kvm->arch.sca_lock);
3508c2ecf20Sopenharmony_ci	ic = kvm_s390_get_ipte_control(vcpu->kvm);
3518c2ecf20Sopenharmony_ci	do {
3528c2ecf20Sopenharmony_ci		old = READ_ONCE(*ic);
3538c2ecf20Sopenharmony_ci		new = old;
3548c2ecf20Sopenharmony_ci		new.kh--;
3558c2ecf20Sopenharmony_ci		if (!new.kh)
3568c2ecf20Sopenharmony_ci			new.k = 0;
3578c2ecf20Sopenharmony_ci	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
3588c2ecf20Sopenharmony_ci	read_unlock(&vcpu->kvm->arch.sca_lock);
3598c2ecf20Sopenharmony_ci	if (!new.kh)
3608c2ecf20Sopenharmony_ci		wake_up(&vcpu->kvm->arch.ipte_wq);
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_civoid ipte_lock(struct kvm_vcpu *vcpu)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->eca & ECA_SII)
3668c2ecf20Sopenharmony_ci		ipte_lock_siif(vcpu);
3678c2ecf20Sopenharmony_ci	else
3688c2ecf20Sopenharmony_ci		ipte_lock_simple(vcpu);
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_civoid ipte_unlock(struct kvm_vcpu *vcpu)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	if (vcpu->arch.sie_block->eca & ECA_SII)
3748c2ecf20Sopenharmony_ci		ipte_unlock_siif(vcpu);
3758c2ecf20Sopenharmony_ci	else
3768c2ecf20Sopenharmony_ci		ipte_unlock_simple(vcpu);
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic int ar_translation(struct kvm_vcpu *vcpu, union asce *asce, u8 ar,
3808c2ecf20Sopenharmony_ci			  enum gacc_mode mode)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	union alet alet;
3838c2ecf20Sopenharmony_ci	struct ale ale;
3848c2ecf20Sopenharmony_ci	struct aste aste;
3858c2ecf20Sopenharmony_ci	unsigned long ald_addr, authority_table_addr;
3868c2ecf20Sopenharmony_ci	union ald ald;
3878c2ecf20Sopenharmony_ci	int eax, rc;
3888c2ecf20Sopenharmony_ci	u8 authority_table;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (ar >= NUM_ACRS)
3918c2ecf20Sopenharmony_ci		return -EINVAL;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	save_access_regs(vcpu->run->s.regs.acrs);
3948c2ecf20Sopenharmony_ci	alet.val = vcpu->run->s.regs.acrs[ar];
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if (ar == 0 || alet.val == 0) {
3978c2ecf20Sopenharmony_ci		asce->val = vcpu->arch.sie_block->gcr[1];
3988c2ecf20Sopenharmony_ci		return 0;
3998c2ecf20Sopenharmony_ci	} else if (alet.val == 1) {
4008c2ecf20Sopenharmony_ci		asce->val = vcpu->arch.sie_block->gcr[7];
4018c2ecf20Sopenharmony_ci		return 0;
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	if (alet.reserved)
4058c2ecf20Sopenharmony_ci		return PGM_ALET_SPECIFICATION;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	if (alet.p)
4088c2ecf20Sopenharmony_ci		ald_addr = vcpu->arch.sie_block->gcr[5];
4098c2ecf20Sopenharmony_ci	else
4108c2ecf20Sopenharmony_ci		ald_addr = vcpu->arch.sie_block->gcr[2];
4118c2ecf20Sopenharmony_ci	ald_addr &= 0x7fffffc0;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	rc = read_guest_real(vcpu, ald_addr + 16, &ald.val, sizeof(union ald));
4148c2ecf20Sopenharmony_ci	if (rc)
4158c2ecf20Sopenharmony_ci		return rc;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	if (alet.alen / 8 > ald.all)
4188c2ecf20Sopenharmony_ci		return PGM_ALEN_TRANSLATION;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (0x7fffffff - ald.alo * 128 < alet.alen * 16)
4218c2ecf20Sopenharmony_ci		return PGM_ADDRESSING;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	rc = read_guest_real(vcpu, ald.alo * 128 + alet.alen * 16, &ale,
4248c2ecf20Sopenharmony_ci			     sizeof(struct ale));
4258c2ecf20Sopenharmony_ci	if (rc)
4268c2ecf20Sopenharmony_ci		return rc;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (ale.i == 1)
4298c2ecf20Sopenharmony_ci		return PGM_ALEN_TRANSLATION;
4308c2ecf20Sopenharmony_ci	if (ale.alesn != alet.alesn)
4318c2ecf20Sopenharmony_ci		return PGM_ALE_SEQUENCE;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	rc = read_guest_real(vcpu, ale.asteo * 64, &aste, sizeof(struct aste));
4348c2ecf20Sopenharmony_ci	if (rc)
4358c2ecf20Sopenharmony_ci		return rc;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (aste.i)
4388c2ecf20Sopenharmony_ci		return PGM_ASTE_VALIDITY;
4398c2ecf20Sopenharmony_ci	if (aste.astesn != ale.astesn)
4408c2ecf20Sopenharmony_ci		return PGM_ASTE_SEQUENCE;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	if (ale.p == 1) {
4438c2ecf20Sopenharmony_ci		eax = (vcpu->arch.sie_block->gcr[8] >> 16) & 0xffff;
4448c2ecf20Sopenharmony_ci		if (ale.aleax != eax) {
4458c2ecf20Sopenharmony_ci			if (eax / 16 > aste.atl)
4468c2ecf20Sopenharmony_ci				return PGM_EXTENDED_AUTHORITY;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci			authority_table_addr = aste.ato * 4 + eax / 4;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci			rc = read_guest_real(vcpu, authority_table_addr,
4518c2ecf20Sopenharmony_ci					     &authority_table,
4528c2ecf20Sopenharmony_ci					     sizeof(u8));
4538c2ecf20Sopenharmony_ci			if (rc)
4548c2ecf20Sopenharmony_ci				return rc;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci			if ((authority_table & (0x40 >> ((eax & 3) * 2))) == 0)
4578c2ecf20Sopenharmony_ci				return PGM_EXTENDED_AUTHORITY;
4588c2ecf20Sopenharmony_ci		}
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (ale.fo == 1 && mode == GACC_STORE)
4628c2ecf20Sopenharmony_ci		return PGM_PROTECTION;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	asce->val = aste.asce;
4658c2ecf20Sopenharmony_ci	return 0;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistruct trans_exc_code_bits {
4698c2ecf20Sopenharmony_ci	unsigned long addr : 52; /* Translation-exception Address */
4708c2ecf20Sopenharmony_ci	unsigned long fsi  : 2;  /* Access Exception Fetch/Store Indication */
4718c2ecf20Sopenharmony_ci	unsigned long	   : 2;
4728c2ecf20Sopenharmony_ci	unsigned long b56  : 1;
4738c2ecf20Sopenharmony_ci	unsigned long	   : 3;
4748c2ecf20Sopenharmony_ci	unsigned long b60  : 1;
4758c2ecf20Sopenharmony_ci	unsigned long b61  : 1;
4768c2ecf20Sopenharmony_ci	unsigned long as   : 2;  /* ASCE Identifier */
4778c2ecf20Sopenharmony_ci};
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cienum {
4808c2ecf20Sopenharmony_ci	FSI_UNKNOWN = 0, /* Unknown wether fetch or store */
4818c2ecf20Sopenharmony_ci	FSI_STORE   = 1, /* Exception was due to store operation */
4828c2ecf20Sopenharmony_ci	FSI_FETCH   = 2  /* Exception was due to fetch operation */
4838c2ecf20Sopenharmony_ci};
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cienum prot_type {
4868c2ecf20Sopenharmony_ci	PROT_TYPE_LA   = 0,
4878c2ecf20Sopenharmony_ci	PROT_TYPE_KEYC = 1,
4888c2ecf20Sopenharmony_ci	PROT_TYPE_ALC  = 2,
4898c2ecf20Sopenharmony_ci	PROT_TYPE_DAT  = 3,
4908c2ecf20Sopenharmony_ci	PROT_TYPE_IEP  = 4,
4918c2ecf20Sopenharmony_ci};
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cistatic int trans_exc(struct kvm_vcpu *vcpu, int code, unsigned long gva,
4948c2ecf20Sopenharmony_ci		     u8 ar, enum gacc_mode mode, enum prot_type prot)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm;
4978c2ecf20Sopenharmony_ci	struct trans_exc_code_bits *tec;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	memset(pgm, 0, sizeof(*pgm));
5008c2ecf20Sopenharmony_ci	pgm->code = code;
5018c2ecf20Sopenharmony_ci	tec = (struct trans_exc_code_bits *)&pgm->trans_exc_code;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	switch (code) {
5048c2ecf20Sopenharmony_ci	case PGM_PROTECTION:
5058c2ecf20Sopenharmony_ci		switch (prot) {
5068c2ecf20Sopenharmony_ci		case PROT_TYPE_IEP:
5078c2ecf20Sopenharmony_ci			tec->b61 = 1;
5088c2ecf20Sopenharmony_ci			fallthrough;
5098c2ecf20Sopenharmony_ci		case PROT_TYPE_LA:
5108c2ecf20Sopenharmony_ci			tec->b56 = 1;
5118c2ecf20Sopenharmony_ci			break;
5128c2ecf20Sopenharmony_ci		case PROT_TYPE_KEYC:
5138c2ecf20Sopenharmony_ci			tec->b60 = 1;
5148c2ecf20Sopenharmony_ci			break;
5158c2ecf20Sopenharmony_ci		case PROT_TYPE_ALC:
5168c2ecf20Sopenharmony_ci			tec->b60 = 1;
5178c2ecf20Sopenharmony_ci			fallthrough;
5188c2ecf20Sopenharmony_ci		case PROT_TYPE_DAT:
5198c2ecf20Sopenharmony_ci			tec->b61 = 1;
5208c2ecf20Sopenharmony_ci			break;
5218c2ecf20Sopenharmony_ci		}
5228c2ecf20Sopenharmony_ci		fallthrough;
5238c2ecf20Sopenharmony_ci	case PGM_ASCE_TYPE:
5248c2ecf20Sopenharmony_ci	case PGM_PAGE_TRANSLATION:
5258c2ecf20Sopenharmony_ci	case PGM_REGION_FIRST_TRANS:
5268c2ecf20Sopenharmony_ci	case PGM_REGION_SECOND_TRANS:
5278c2ecf20Sopenharmony_ci	case PGM_REGION_THIRD_TRANS:
5288c2ecf20Sopenharmony_ci	case PGM_SEGMENT_TRANSLATION:
5298c2ecf20Sopenharmony_ci		/*
5308c2ecf20Sopenharmony_ci		 * op_access_id only applies to MOVE_PAGE -> set bit 61
5318c2ecf20Sopenharmony_ci		 * exc_access_id has to be set to 0 for some instructions. Both
5328c2ecf20Sopenharmony_ci		 * cases have to be handled by the caller.
5338c2ecf20Sopenharmony_ci		 */
5348c2ecf20Sopenharmony_ci		tec->addr = gva >> PAGE_SHIFT;
5358c2ecf20Sopenharmony_ci		tec->fsi = mode == GACC_STORE ? FSI_STORE : FSI_FETCH;
5368c2ecf20Sopenharmony_ci		tec->as = psw_bits(vcpu->arch.sie_block->gpsw).as;
5378c2ecf20Sopenharmony_ci		fallthrough;
5388c2ecf20Sopenharmony_ci	case PGM_ALEN_TRANSLATION:
5398c2ecf20Sopenharmony_ci	case PGM_ALE_SEQUENCE:
5408c2ecf20Sopenharmony_ci	case PGM_ASTE_VALIDITY:
5418c2ecf20Sopenharmony_ci	case PGM_ASTE_SEQUENCE:
5428c2ecf20Sopenharmony_ci	case PGM_EXTENDED_AUTHORITY:
5438c2ecf20Sopenharmony_ci		/*
5448c2ecf20Sopenharmony_ci		 * We can always store exc_access_id, as it is
5458c2ecf20Sopenharmony_ci		 * undefined for non-ar cases. It is undefined for
5468c2ecf20Sopenharmony_ci		 * most DAT protection exceptions.
5478c2ecf20Sopenharmony_ci		 */
5488c2ecf20Sopenharmony_ci		pgm->exc_access_id = ar;
5498c2ecf20Sopenharmony_ci		break;
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci	return code;
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cistatic int get_vcpu_asce(struct kvm_vcpu *vcpu, union asce *asce,
5558c2ecf20Sopenharmony_ci			 unsigned long ga, u8 ar, enum gacc_mode mode)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	int rc;
5588c2ecf20Sopenharmony_ci	struct psw_bits psw = psw_bits(vcpu->arch.sie_block->gpsw);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	if (!psw.dat) {
5618c2ecf20Sopenharmony_ci		asce->val = 0;
5628c2ecf20Sopenharmony_ci		asce->r = 1;
5638c2ecf20Sopenharmony_ci		return 0;
5648c2ecf20Sopenharmony_ci	}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	if ((mode == GACC_IFETCH) && (psw.as != PSW_BITS_AS_HOME))
5678c2ecf20Sopenharmony_ci		psw.as = PSW_BITS_AS_PRIMARY;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	switch (psw.as) {
5708c2ecf20Sopenharmony_ci	case PSW_BITS_AS_PRIMARY:
5718c2ecf20Sopenharmony_ci		asce->val = vcpu->arch.sie_block->gcr[1];
5728c2ecf20Sopenharmony_ci		return 0;
5738c2ecf20Sopenharmony_ci	case PSW_BITS_AS_SECONDARY:
5748c2ecf20Sopenharmony_ci		asce->val = vcpu->arch.sie_block->gcr[7];
5758c2ecf20Sopenharmony_ci		return 0;
5768c2ecf20Sopenharmony_ci	case PSW_BITS_AS_HOME:
5778c2ecf20Sopenharmony_ci		asce->val = vcpu->arch.sie_block->gcr[13];
5788c2ecf20Sopenharmony_ci		return 0;
5798c2ecf20Sopenharmony_ci	case PSW_BITS_AS_ACCREG:
5808c2ecf20Sopenharmony_ci		rc = ar_translation(vcpu, asce, ar, mode);
5818c2ecf20Sopenharmony_ci		if (rc > 0)
5828c2ecf20Sopenharmony_ci			return trans_exc(vcpu, rc, ga, ar, mode, PROT_TYPE_ALC);
5838c2ecf20Sopenharmony_ci		return rc;
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci	return 0;
5868c2ecf20Sopenharmony_ci}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cistatic int deref_table(struct kvm *kvm, unsigned long gpa, unsigned long *val)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	return kvm_read_guest(kvm, gpa, val, sizeof(*val));
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci/**
5948c2ecf20Sopenharmony_ci * guest_translate - translate a guest virtual into a guest absolute address
5958c2ecf20Sopenharmony_ci * @vcpu: virtual cpu
5968c2ecf20Sopenharmony_ci * @gva: guest virtual address
5978c2ecf20Sopenharmony_ci * @gpa: points to where guest physical (absolute) address should be stored
5988c2ecf20Sopenharmony_ci * @asce: effective asce
5998c2ecf20Sopenharmony_ci * @mode: indicates the access mode to be used
6008c2ecf20Sopenharmony_ci * @prot: returns the type for protection exceptions
6018c2ecf20Sopenharmony_ci *
6028c2ecf20Sopenharmony_ci * Translate a guest virtual address into a guest absolute address by means
6038c2ecf20Sopenharmony_ci * of dynamic address translation as specified by the architecture.
6048c2ecf20Sopenharmony_ci * If the resulting absolute address is not available in the configuration
6058c2ecf20Sopenharmony_ci * an addressing exception is indicated and @gpa will not be changed.
6068c2ecf20Sopenharmony_ci *
6078c2ecf20Sopenharmony_ci * Returns: - zero on success; @gpa contains the resulting absolute address
6088c2ecf20Sopenharmony_ci *	    - a negative value if guest access failed due to e.g. broken
6098c2ecf20Sopenharmony_ci *	      guest mapping
6108c2ecf20Sopenharmony_ci *	    - a positve value if an access exception happened. In this case
6118c2ecf20Sopenharmony_ci *	      the returned value is the program interruption code as defined
6128c2ecf20Sopenharmony_ci *	      by the architecture
6138c2ecf20Sopenharmony_ci */
6148c2ecf20Sopenharmony_cistatic unsigned long guest_translate(struct kvm_vcpu *vcpu, unsigned long gva,
6158c2ecf20Sopenharmony_ci				     unsigned long *gpa, const union asce asce,
6168c2ecf20Sopenharmony_ci				     enum gacc_mode mode, enum prot_type *prot)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	union vaddress vaddr = {.addr = gva};
6198c2ecf20Sopenharmony_ci	union raddress raddr = {.addr = gva};
6208c2ecf20Sopenharmony_ci	union page_table_entry pte;
6218c2ecf20Sopenharmony_ci	int dat_protection = 0;
6228c2ecf20Sopenharmony_ci	int iep_protection = 0;
6238c2ecf20Sopenharmony_ci	union ctlreg0 ctlreg0;
6248c2ecf20Sopenharmony_ci	unsigned long ptr;
6258c2ecf20Sopenharmony_ci	int edat1, edat2, iep;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	ctlreg0.val = vcpu->arch.sie_block->gcr[0];
6288c2ecf20Sopenharmony_ci	edat1 = ctlreg0.edat && test_kvm_facility(vcpu->kvm, 8);
6298c2ecf20Sopenharmony_ci	edat2 = edat1 && test_kvm_facility(vcpu->kvm, 78);
6308c2ecf20Sopenharmony_ci	iep = ctlreg0.iep && test_kvm_facility(vcpu->kvm, 130);
6318c2ecf20Sopenharmony_ci	if (asce.r)
6328c2ecf20Sopenharmony_ci		goto real_address;
6338c2ecf20Sopenharmony_ci	ptr = asce.origin * PAGE_SIZE;
6348c2ecf20Sopenharmony_ci	switch (asce.dt) {
6358c2ecf20Sopenharmony_ci	case ASCE_TYPE_REGION1:
6368c2ecf20Sopenharmony_ci		if (vaddr.rfx01 > asce.tl)
6378c2ecf20Sopenharmony_ci			return PGM_REGION_FIRST_TRANS;
6388c2ecf20Sopenharmony_ci		ptr += vaddr.rfx * 8;
6398c2ecf20Sopenharmony_ci		break;
6408c2ecf20Sopenharmony_ci	case ASCE_TYPE_REGION2:
6418c2ecf20Sopenharmony_ci		if (vaddr.rfx)
6428c2ecf20Sopenharmony_ci			return PGM_ASCE_TYPE;
6438c2ecf20Sopenharmony_ci		if (vaddr.rsx01 > asce.tl)
6448c2ecf20Sopenharmony_ci			return PGM_REGION_SECOND_TRANS;
6458c2ecf20Sopenharmony_ci		ptr += vaddr.rsx * 8;
6468c2ecf20Sopenharmony_ci		break;
6478c2ecf20Sopenharmony_ci	case ASCE_TYPE_REGION3:
6488c2ecf20Sopenharmony_ci		if (vaddr.rfx || vaddr.rsx)
6498c2ecf20Sopenharmony_ci			return PGM_ASCE_TYPE;
6508c2ecf20Sopenharmony_ci		if (vaddr.rtx01 > asce.tl)
6518c2ecf20Sopenharmony_ci			return PGM_REGION_THIRD_TRANS;
6528c2ecf20Sopenharmony_ci		ptr += vaddr.rtx * 8;
6538c2ecf20Sopenharmony_ci		break;
6548c2ecf20Sopenharmony_ci	case ASCE_TYPE_SEGMENT:
6558c2ecf20Sopenharmony_ci		if (vaddr.rfx || vaddr.rsx || vaddr.rtx)
6568c2ecf20Sopenharmony_ci			return PGM_ASCE_TYPE;
6578c2ecf20Sopenharmony_ci		if (vaddr.sx01 > asce.tl)
6588c2ecf20Sopenharmony_ci			return PGM_SEGMENT_TRANSLATION;
6598c2ecf20Sopenharmony_ci		ptr += vaddr.sx * 8;
6608c2ecf20Sopenharmony_ci		break;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci	switch (asce.dt) {
6638c2ecf20Sopenharmony_ci	case ASCE_TYPE_REGION1:	{
6648c2ecf20Sopenharmony_ci		union region1_table_entry rfte;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci		if (kvm_is_error_gpa(vcpu->kvm, ptr))
6678c2ecf20Sopenharmony_ci			return PGM_ADDRESSING;
6688c2ecf20Sopenharmony_ci		if (deref_table(vcpu->kvm, ptr, &rfte.val))
6698c2ecf20Sopenharmony_ci			return -EFAULT;
6708c2ecf20Sopenharmony_ci		if (rfte.i)
6718c2ecf20Sopenharmony_ci			return PGM_REGION_FIRST_TRANS;
6728c2ecf20Sopenharmony_ci		if (rfte.tt != TABLE_TYPE_REGION1)
6738c2ecf20Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
6748c2ecf20Sopenharmony_ci		if (vaddr.rsx01 < rfte.tf || vaddr.rsx01 > rfte.tl)
6758c2ecf20Sopenharmony_ci			return PGM_REGION_SECOND_TRANS;
6768c2ecf20Sopenharmony_ci		if (edat1)
6778c2ecf20Sopenharmony_ci			dat_protection |= rfte.p;
6788c2ecf20Sopenharmony_ci		ptr = rfte.rto * PAGE_SIZE + vaddr.rsx * 8;
6798c2ecf20Sopenharmony_ci	}
6808c2ecf20Sopenharmony_ci		fallthrough;
6818c2ecf20Sopenharmony_ci	case ASCE_TYPE_REGION2: {
6828c2ecf20Sopenharmony_ci		union region2_table_entry rste;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci		if (kvm_is_error_gpa(vcpu->kvm, ptr))
6858c2ecf20Sopenharmony_ci			return PGM_ADDRESSING;
6868c2ecf20Sopenharmony_ci		if (deref_table(vcpu->kvm, ptr, &rste.val))
6878c2ecf20Sopenharmony_ci			return -EFAULT;
6888c2ecf20Sopenharmony_ci		if (rste.i)
6898c2ecf20Sopenharmony_ci			return PGM_REGION_SECOND_TRANS;
6908c2ecf20Sopenharmony_ci		if (rste.tt != TABLE_TYPE_REGION2)
6918c2ecf20Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
6928c2ecf20Sopenharmony_ci		if (vaddr.rtx01 < rste.tf || vaddr.rtx01 > rste.tl)
6938c2ecf20Sopenharmony_ci			return PGM_REGION_THIRD_TRANS;
6948c2ecf20Sopenharmony_ci		if (edat1)
6958c2ecf20Sopenharmony_ci			dat_protection |= rste.p;
6968c2ecf20Sopenharmony_ci		ptr = rste.rto * PAGE_SIZE + vaddr.rtx * 8;
6978c2ecf20Sopenharmony_ci	}
6988c2ecf20Sopenharmony_ci		fallthrough;
6998c2ecf20Sopenharmony_ci	case ASCE_TYPE_REGION3: {
7008c2ecf20Sopenharmony_ci		union region3_table_entry rtte;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci		if (kvm_is_error_gpa(vcpu->kvm, ptr))
7038c2ecf20Sopenharmony_ci			return PGM_ADDRESSING;
7048c2ecf20Sopenharmony_ci		if (deref_table(vcpu->kvm, ptr, &rtte.val))
7058c2ecf20Sopenharmony_ci			return -EFAULT;
7068c2ecf20Sopenharmony_ci		if (rtte.i)
7078c2ecf20Sopenharmony_ci			return PGM_REGION_THIRD_TRANS;
7088c2ecf20Sopenharmony_ci		if (rtte.tt != TABLE_TYPE_REGION3)
7098c2ecf20Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
7108c2ecf20Sopenharmony_ci		if (rtte.cr && asce.p && edat2)
7118c2ecf20Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
7128c2ecf20Sopenharmony_ci		if (rtte.fc && edat2) {
7138c2ecf20Sopenharmony_ci			dat_protection |= rtte.fc1.p;
7148c2ecf20Sopenharmony_ci			iep_protection = rtte.fc1.iep;
7158c2ecf20Sopenharmony_ci			raddr.rfaa = rtte.fc1.rfaa;
7168c2ecf20Sopenharmony_ci			goto absolute_address;
7178c2ecf20Sopenharmony_ci		}
7188c2ecf20Sopenharmony_ci		if (vaddr.sx01 < rtte.fc0.tf)
7198c2ecf20Sopenharmony_ci			return PGM_SEGMENT_TRANSLATION;
7208c2ecf20Sopenharmony_ci		if (vaddr.sx01 > rtte.fc0.tl)
7218c2ecf20Sopenharmony_ci			return PGM_SEGMENT_TRANSLATION;
7228c2ecf20Sopenharmony_ci		if (edat1)
7238c2ecf20Sopenharmony_ci			dat_protection |= rtte.fc0.p;
7248c2ecf20Sopenharmony_ci		ptr = rtte.fc0.sto * PAGE_SIZE + vaddr.sx * 8;
7258c2ecf20Sopenharmony_ci	}
7268c2ecf20Sopenharmony_ci		fallthrough;
7278c2ecf20Sopenharmony_ci	case ASCE_TYPE_SEGMENT: {
7288c2ecf20Sopenharmony_ci		union segment_table_entry ste;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci		if (kvm_is_error_gpa(vcpu->kvm, ptr))
7318c2ecf20Sopenharmony_ci			return PGM_ADDRESSING;
7328c2ecf20Sopenharmony_ci		if (deref_table(vcpu->kvm, ptr, &ste.val))
7338c2ecf20Sopenharmony_ci			return -EFAULT;
7348c2ecf20Sopenharmony_ci		if (ste.i)
7358c2ecf20Sopenharmony_ci			return PGM_SEGMENT_TRANSLATION;
7368c2ecf20Sopenharmony_ci		if (ste.tt != TABLE_TYPE_SEGMENT)
7378c2ecf20Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
7388c2ecf20Sopenharmony_ci		if (ste.cs && asce.p)
7398c2ecf20Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
7408c2ecf20Sopenharmony_ci		if (ste.fc && edat1) {
7418c2ecf20Sopenharmony_ci			dat_protection |= ste.fc1.p;
7428c2ecf20Sopenharmony_ci			iep_protection = ste.fc1.iep;
7438c2ecf20Sopenharmony_ci			raddr.sfaa = ste.fc1.sfaa;
7448c2ecf20Sopenharmony_ci			goto absolute_address;
7458c2ecf20Sopenharmony_ci		}
7468c2ecf20Sopenharmony_ci		dat_protection |= ste.fc0.p;
7478c2ecf20Sopenharmony_ci		ptr = ste.fc0.pto * (PAGE_SIZE / 2) + vaddr.px * 8;
7488c2ecf20Sopenharmony_ci	}
7498c2ecf20Sopenharmony_ci	}
7508c2ecf20Sopenharmony_ci	if (kvm_is_error_gpa(vcpu->kvm, ptr))
7518c2ecf20Sopenharmony_ci		return PGM_ADDRESSING;
7528c2ecf20Sopenharmony_ci	if (deref_table(vcpu->kvm, ptr, &pte.val))
7538c2ecf20Sopenharmony_ci		return -EFAULT;
7548c2ecf20Sopenharmony_ci	if (pte.i)
7558c2ecf20Sopenharmony_ci		return PGM_PAGE_TRANSLATION;
7568c2ecf20Sopenharmony_ci	if (pte.z)
7578c2ecf20Sopenharmony_ci		return PGM_TRANSLATION_SPEC;
7588c2ecf20Sopenharmony_ci	dat_protection |= pte.p;
7598c2ecf20Sopenharmony_ci	iep_protection = pte.iep;
7608c2ecf20Sopenharmony_ci	raddr.pfra = pte.pfra;
7618c2ecf20Sopenharmony_cireal_address:
7628c2ecf20Sopenharmony_ci	raddr.addr = kvm_s390_real_to_abs(vcpu, raddr.addr);
7638c2ecf20Sopenharmony_ciabsolute_address:
7648c2ecf20Sopenharmony_ci	if (mode == GACC_STORE && dat_protection) {
7658c2ecf20Sopenharmony_ci		*prot = PROT_TYPE_DAT;
7668c2ecf20Sopenharmony_ci		return PGM_PROTECTION;
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci	if (mode == GACC_IFETCH && iep_protection && iep) {
7698c2ecf20Sopenharmony_ci		*prot = PROT_TYPE_IEP;
7708c2ecf20Sopenharmony_ci		return PGM_PROTECTION;
7718c2ecf20Sopenharmony_ci	}
7728c2ecf20Sopenharmony_ci	if (kvm_is_error_gpa(vcpu->kvm, raddr.addr))
7738c2ecf20Sopenharmony_ci		return PGM_ADDRESSING;
7748c2ecf20Sopenharmony_ci	*gpa = raddr.addr;
7758c2ecf20Sopenharmony_ci	return 0;
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_cistatic inline int is_low_address(unsigned long ga)
7798c2ecf20Sopenharmony_ci{
7808c2ecf20Sopenharmony_ci	/* Check for address ranges 0..511 and 4096..4607 */
7818c2ecf20Sopenharmony_ci	return (ga & ~0x11fful) == 0;
7828c2ecf20Sopenharmony_ci}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_cistatic int low_address_protection_enabled(struct kvm_vcpu *vcpu,
7858c2ecf20Sopenharmony_ci					  const union asce asce)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	union ctlreg0 ctlreg0 = {.val = vcpu->arch.sie_block->gcr[0]};
7888c2ecf20Sopenharmony_ci	psw_t *psw = &vcpu->arch.sie_block->gpsw;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	if (!ctlreg0.lap)
7918c2ecf20Sopenharmony_ci		return 0;
7928c2ecf20Sopenharmony_ci	if (psw_bits(*psw).dat && asce.p)
7938c2ecf20Sopenharmony_ci		return 0;
7948c2ecf20Sopenharmony_ci	return 1;
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_cistatic int guest_page_range(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar,
7988c2ecf20Sopenharmony_ci			    unsigned long *pages, unsigned long nr_pages,
7998c2ecf20Sopenharmony_ci			    const union asce asce, enum gacc_mode mode)
8008c2ecf20Sopenharmony_ci{
8018c2ecf20Sopenharmony_ci	psw_t *psw = &vcpu->arch.sie_block->gpsw;
8028c2ecf20Sopenharmony_ci	int lap_enabled, rc = 0;
8038c2ecf20Sopenharmony_ci	enum prot_type prot;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	lap_enabled = low_address_protection_enabled(vcpu, asce);
8068c2ecf20Sopenharmony_ci	while (nr_pages) {
8078c2ecf20Sopenharmony_ci		ga = kvm_s390_logical_to_effective(vcpu, ga);
8088c2ecf20Sopenharmony_ci		if (mode == GACC_STORE && lap_enabled && is_low_address(ga))
8098c2ecf20Sopenharmony_ci			return trans_exc(vcpu, PGM_PROTECTION, ga, ar, mode,
8108c2ecf20Sopenharmony_ci					 PROT_TYPE_LA);
8118c2ecf20Sopenharmony_ci		ga &= PAGE_MASK;
8128c2ecf20Sopenharmony_ci		if (psw_bits(*psw).dat) {
8138c2ecf20Sopenharmony_ci			rc = guest_translate(vcpu, ga, pages, asce, mode, &prot);
8148c2ecf20Sopenharmony_ci			if (rc < 0)
8158c2ecf20Sopenharmony_ci				return rc;
8168c2ecf20Sopenharmony_ci		} else {
8178c2ecf20Sopenharmony_ci			*pages = kvm_s390_real_to_abs(vcpu, ga);
8188c2ecf20Sopenharmony_ci			if (kvm_is_error_gpa(vcpu->kvm, *pages))
8198c2ecf20Sopenharmony_ci				rc = PGM_ADDRESSING;
8208c2ecf20Sopenharmony_ci		}
8218c2ecf20Sopenharmony_ci		if (rc)
8228c2ecf20Sopenharmony_ci			return trans_exc(vcpu, rc, ga, ar, mode, prot);
8238c2ecf20Sopenharmony_ci		ga += PAGE_SIZE;
8248c2ecf20Sopenharmony_ci		pages++;
8258c2ecf20Sopenharmony_ci		nr_pages--;
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci	return 0;
8288c2ecf20Sopenharmony_ci}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ciint access_guest(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, void *data,
8318c2ecf20Sopenharmony_ci		 unsigned long len, enum gacc_mode mode)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci	psw_t *psw = &vcpu->arch.sie_block->gpsw;
8348c2ecf20Sopenharmony_ci	unsigned long _len, nr_pages, gpa, idx;
8358c2ecf20Sopenharmony_ci	unsigned long pages_array[2];
8368c2ecf20Sopenharmony_ci	unsigned long *pages;
8378c2ecf20Sopenharmony_ci	int need_ipte_lock;
8388c2ecf20Sopenharmony_ci	union asce asce;
8398c2ecf20Sopenharmony_ci	int rc;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	if (!len)
8428c2ecf20Sopenharmony_ci		return 0;
8438c2ecf20Sopenharmony_ci	ga = kvm_s390_logical_to_effective(vcpu, ga);
8448c2ecf20Sopenharmony_ci	rc = get_vcpu_asce(vcpu, &asce, ga, ar, mode);
8458c2ecf20Sopenharmony_ci	if (rc)
8468c2ecf20Sopenharmony_ci		return rc;
8478c2ecf20Sopenharmony_ci	nr_pages = (((ga & ~PAGE_MASK) + len - 1) >> PAGE_SHIFT) + 1;
8488c2ecf20Sopenharmony_ci	pages = pages_array;
8498c2ecf20Sopenharmony_ci	if (nr_pages > ARRAY_SIZE(pages_array))
8508c2ecf20Sopenharmony_ci		pages = vmalloc(array_size(nr_pages, sizeof(unsigned long)));
8518c2ecf20Sopenharmony_ci	if (!pages)
8528c2ecf20Sopenharmony_ci		return -ENOMEM;
8538c2ecf20Sopenharmony_ci	need_ipte_lock = psw_bits(*psw).dat && !asce.r;
8548c2ecf20Sopenharmony_ci	if (need_ipte_lock)
8558c2ecf20Sopenharmony_ci		ipte_lock(vcpu);
8568c2ecf20Sopenharmony_ci	rc = guest_page_range(vcpu, ga, ar, pages, nr_pages, asce, mode);
8578c2ecf20Sopenharmony_ci	for (idx = 0; idx < nr_pages && !rc; idx++) {
8588c2ecf20Sopenharmony_ci		gpa = *(pages + idx) + (ga & ~PAGE_MASK);
8598c2ecf20Sopenharmony_ci		_len = min(PAGE_SIZE - (gpa & ~PAGE_MASK), len);
8608c2ecf20Sopenharmony_ci		if (mode == GACC_STORE)
8618c2ecf20Sopenharmony_ci			rc = kvm_write_guest(vcpu->kvm, gpa, data, _len);
8628c2ecf20Sopenharmony_ci		else
8638c2ecf20Sopenharmony_ci			rc = kvm_read_guest(vcpu->kvm, gpa, data, _len);
8648c2ecf20Sopenharmony_ci		len -= _len;
8658c2ecf20Sopenharmony_ci		ga += _len;
8668c2ecf20Sopenharmony_ci		data += _len;
8678c2ecf20Sopenharmony_ci	}
8688c2ecf20Sopenharmony_ci	if (need_ipte_lock)
8698c2ecf20Sopenharmony_ci		ipte_unlock(vcpu);
8708c2ecf20Sopenharmony_ci	if (nr_pages > ARRAY_SIZE(pages_array))
8718c2ecf20Sopenharmony_ci		vfree(pages);
8728c2ecf20Sopenharmony_ci	return rc;
8738c2ecf20Sopenharmony_ci}
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ciint access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
8768c2ecf20Sopenharmony_ci		      void *data, unsigned long len, enum gacc_mode mode)
8778c2ecf20Sopenharmony_ci{
8788c2ecf20Sopenharmony_ci	unsigned long _len, gpa;
8798c2ecf20Sopenharmony_ci	int rc = 0;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	while (len && !rc) {
8828c2ecf20Sopenharmony_ci		gpa = kvm_s390_real_to_abs(vcpu, gra);
8838c2ecf20Sopenharmony_ci		_len = min(PAGE_SIZE - (gpa & ~PAGE_MASK), len);
8848c2ecf20Sopenharmony_ci		if (mode)
8858c2ecf20Sopenharmony_ci			rc = write_guest_abs(vcpu, gpa, data, _len);
8868c2ecf20Sopenharmony_ci		else
8878c2ecf20Sopenharmony_ci			rc = read_guest_abs(vcpu, gpa, data, _len);
8888c2ecf20Sopenharmony_ci		len -= _len;
8898c2ecf20Sopenharmony_ci		gra += _len;
8908c2ecf20Sopenharmony_ci		data += _len;
8918c2ecf20Sopenharmony_ci	}
8928c2ecf20Sopenharmony_ci	return rc;
8938c2ecf20Sopenharmony_ci}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci/**
8968c2ecf20Sopenharmony_ci * guest_translate_address - translate guest logical into guest absolute address
8978c2ecf20Sopenharmony_ci *
8988c2ecf20Sopenharmony_ci * Parameter semantics are the same as the ones from guest_translate.
8998c2ecf20Sopenharmony_ci * The memory contents at the guest address are not changed.
9008c2ecf20Sopenharmony_ci *
9018c2ecf20Sopenharmony_ci * Note: The IPTE lock is not taken during this function, so the caller
9028c2ecf20Sopenharmony_ci * has to take care of this.
9038c2ecf20Sopenharmony_ci */
9048c2ecf20Sopenharmony_ciint guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
9058c2ecf20Sopenharmony_ci			    unsigned long *gpa, enum gacc_mode mode)
9068c2ecf20Sopenharmony_ci{
9078c2ecf20Sopenharmony_ci	psw_t *psw = &vcpu->arch.sie_block->gpsw;
9088c2ecf20Sopenharmony_ci	enum prot_type prot;
9098c2ecf20Sopenharmony_ci	union asce asce;
9108c2ecf20Sopenharmony_ci	int rc;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	gva = kvm_s390_logical_to_effective(vcpu, gva);
9138c2ecf20Sopenharmony_ci	rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode);
9148c2ecf20Sopenharmony_ci	if (rc)
9158c2ecf20Sopenharmony_ci		return rc;
9168c2ecf20Sopenharmony_ci	if (is_low_address(gva) && low_address_protection_enabled(vcpu, asce)) {
9178c2ecf20Sopenharmony_ci		if (mode == GACC_STORE)
9188c2ecf20Sopenharmony_ci			return trans_exc(vcpu, PGM_PROTECTION, gva, 0,
9198c2ecf20Sopenharmony_ci					 mode, PROT_TYPE_LA);
9208c2ecf20Sopenharmony_ci	}
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	if (psw_bits(*psw).dat && !asce.r) {	/* Use DAT? */
9238c2ecf20Sopenharmony_ci		rc = guest_translate(vcpu, gva, gpa, asce, mode, &prot);
9248c2ecf20Sopenharmony_ci		if (rc > 0)
9258c2ecf20Sopenharmony_ci			return trans_exc(vcpu, rc, gva, 0, mode, prot);
9268c2ecf20Sopenharmony_ci	} else {
9278c2ecf20Sopenharmony_ci		*gpa = kvm_s390_real_to_abs(vcpu, gva);
9288c2ecf20Sopenharmony_ci		if (kvm_is_error_gpa(vcpu->kvm, *gpa))
9298c2ecf20Sopenharmony_ci			return trans_exc(vcpu, rc, gva, PGM_ADDRESSING, mode, 0);
9308c2ecf20Sopenharmony_ci	}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	return rc;
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci/**
9368c2ecf20Sopenharmony_ci * check_gva_range - test a range of guest virtual addresses for accessibility
9378c2ecf20Sopenharmony_ci */
9388c2ecf20Sopenharmony_ciint check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
9398c2ecf20Sopenharmony_ci		    unsigned long length, enum gacc_mode mode)
9408c2ecf20Sopenharmony_ci{
9418c2ecf20Sopenharmony_ci	unsigned long gpa;
9428c2ecf20Sopenharmony_ci	unsigned long currlen;
9438c2ecf20Sopenharmony_ci	int rc = 0;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	ipte_lock(vcpu);
9468c2ecf20Sopenharmony_ci	while (length > 0 && !rc) {
9478c2ecf20Sopenharmony_ci		currlen = min(length, PAGE_SIZE - (gva % PAGE_SIZE));
9488c2ecf20Sopenharmony_ci		rc = guest_translate_address(vcpu, gva, ar, &gpa, mode);
9498c2ecf20Sopenharmony_ci		gva += currlen;
9508c2ecf20Sopenharmony_ci		length -= currlen;
9518c2ecf20Sopenharmony_ci	}
9528c2ecf20Sopenharmony_ci	ipte_unlock(vcpu);
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	return rc;
9558c2ecf20Sopenharmony_ci}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci/**
9588c2ecf20Sopenharmony_ci * kvm_s390_check_low_addr_prot_real - check for low-address protection
9598c2ecf20Sopenharmony_ci * @gra: Guest real address
9608c2ecf20Sopenharmony_ci *
9618c2ecf20Sopenharmony_ci * Checks whether an address is subject to low-address protection and set
9628c2ecf20Sopenharmony_ci * up vcpu->arch.pgm accordingly if necessary.
9638c2ecf20Sopenharmony_ci *
9648c2ecf20Sopenharmony_ci * Return: 0 if no protection exception, or PGM_PROTECTION if protected.
9658c2ecf20Sopenharmony_ci */
9668c2ecf20Sopenharmony_ciint kvm_s390_check_low_addr_prot_real(struct kvm_vcpu *vcpu, unsigned long gra)
9678c2ecf20Sopenharmony_ci{
9688c2ecf20Sopenharmony_ci	union ctlreg0 ctlreg0 = {.val = vcpu->arch.sie_block->gcr[0]};
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	if (!ctlreg0.lap || !is_low_address(gra))
9718c2ecf20Sopenharmony_ci		return 0;
9728c2ecf20Sopenharmony_ci	return trans_exc(vcpu, PGM_PROTECTION, gra, 0, GACC_STORE, PROT_TYPE_LA);
9738c2ecf20Sopenharmony_ci}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci/**
9768c2ecf20Sopenharmony_ci * kvm_s390_shadow_tables - walk the guest page table and create shadow tables
9778c2ecf20Sopenharmony_ci * @sg: pointer to the shadow guest address space structure
9788c2ecf20Sopenharmony_ci * @saddr: faulting address in the shadow gmap
9798c2ecf20Sopenharmony_ci * @pgt: pointer to the beginning of the page table for the given address if
9808c2ecf20Sopenharmony_ci *	 successful (return value 0), or to the first invalid DAT entry in
9818c2ecf20Sopenharmony_ci *	 case of exceptions (return value > 0)
9828c2ecf20Sopenharmony_ci * @fake: pgt references contiguous guest memory block, not a pgtable
9838c2ecf20Sopenharmony_ci */
9848c2ecf20Sopenharmony_cistatic int kvm_s390_shadow_tables(struct gmap *sg, unsigned long saddr,
9858c2ecf20Sopenharmony_ci				  unsigned long *pgt, int *dat_protection,
9868c2ecf20Sopenharmony_ci				  int *fake)
9878c2ecf20Sopenharmony_ci{
9888c2ecf20Sopenharmony_ci	struct gmap *parent;
9898c2ecf20Sopenharmony_ci	union asce asce;
9908c2ecf20Sopenharmony_ci	union vaddress vaddr;
9918c2ecf20Sopenharmony_ci	unsigned long ptr;
9928c2ecf20Sopenharmony_ci	int rc;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	*fake = 0;
9958c2ecf20Sopenharmony_ci	*dat_protection = 0;
9968c2ecf20Sopenharmony_ci	parent = sg->parent;
9978c2ecf20Sopenharmony_ci	vaddr.addr = saddr;
9988c2ecf20Sopenharmony_ci	asce.val = sg->orig_asce;
9998c2ecf20Sopenharmony_ci	ptr = asce.origin * PAGE_SIZE;
10008c2ecf20Sopenharmony_ci	if (asce.r) {
10018c2ecf20Sopenharmony_ci		*fake = 1;
10028c2ecf20Sopenharmony_ci		ptr = 0;
10038c2ecf20Sopenharmony_ci		asce.dt = ASCE_TYPE_REGION1;
10048c2ecf20Sopenharmony_ci	}
10058c2ecf20Sopenharmony_ci	switch (asce.dt) {
10068c2ecf20Sopenharmony_ci	case ASCE_TYPE_REGION1:
10078c2ecf20Sopenharmony_ci		if (vaddr.rfx01 > asce.tl && !*fake)
10088c2ecf20Sopenharmony_ci			return PGM_REGION_FIRST_TRANS;
10098c2ecf20Sopenharmony_ci		break;
10108c2ecf20Sopenharmony_ci	case ASCE_TYPE_REGION2:
10118c2ecf20Sopenharmony_ci		if (vaddr.rfx)
10128c2ecf20Sopenharmony_ci			return PGM_ASCE_TYPE;
10138c2ecf20Sopenharmony_ci		if (vaddr.rsx01 > asce.tl)
10148c2ecf20Sopenharmony_ci			return PGM_REGION_SECOND_TRANS;
10158c2ecf20Sopenharmony_ci		break;
10168c2ecf20Sopenharmony_ci	case ASCE_TYPE_REGION3:
10178c2ecf20Sopenharmony_ci		if (vaddr.rfx || vaddr.rsx)
10188c2ecf20Sopenharmony_ci			return PGM_ASCE_TYPE;
10198c2ecf20Sopenharmony_ci		if (vaddr.rtx01 > asce.tl)
10208c2ecf20Sopenharmony_ci			return PGM_REGION_THIRD_TRANS;
10218c2ecf20Sopenharmony_ci		break;
10228c2ecf20Sopenharmony_ci	case ASCE_TYPE_SEGMENT:
10238c2ecf20Sopenharmony_ci		if (vaddr.rfx || vaddr.rsx || vaddr.rtx)
10248c2ecf20Sopenharmony_ci			return PGM_ASCE_TYPE;
10258c2ecf20Sopenharmony_ci		if (vaddr.sx01 > asce.tl)
10268c2ecf20Sopenharmony_ci			return PGM_SEGMENT_TRANSLATION;
10278c2ecf20Sopenharmony_ci		break;
10288c2ecf20Sopenharmony_ci	}
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	switch (asce.dt) {
10318c2ecf20Sopenharmony_ci	case ASCE_TYPE_REGION1: {
10328c2ecf20Sopenharmony_ci		union region1_table_entry rfte;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci		if (*fake) {
10358c2ecf20Sopenharmony_ci			ptr += vaddr.rfx * _REGION1_SIZE;
10368c2ecf20Sopenharmony_ci			rfte.val = ptr;
10378c2ecf20Sopenharmony_ci			goto shadow_r2t;
10388c2ecf20Sopenharmony_ci		}
10398c2ecf20Sopenharmony_ci		*pgt = ptr + vaddr.rfx * 8;
10408c2ecf20Sopenharmony_ci		rc = gmap_read_table(parent, ptr + vaddr.rfx * 8, &rfte.val);
10418c2ecf20Sopenharmony_ci		if (rc)
10428c2ecf20Sopenharmony_ci			return rc;
10438c2ecf20Sopenharmony_ci		if (rfte.i)
10448c2ecf20Sopenharmony_ci			return PGM_REGION_FIRST_TRANS;
10458c2ecf20Sopenharmony_ci		if (rfte.tt != TABLE_TYPE_REGION1)
10468c2ecf20Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
10478c2ecf20Sopenharmony_ci		if (vaddr.rsx01 < rfte.tf || vaddr.rsx01 > rfte.tl)
10488c2ecf20Sopenharmony_ci			return PGM_REGION_SECOND_TRANS;
10498c2ecf20Sopenharmony_ci		if (sg->edat_level >= 1)
10508c2ecf20Sopenharmony_ci			*dat_protection |= rfte.p;
10518c2ecf20Sopenharmony_ci		ptr = rfte.rto * PAGE_SIZE;
10528c2ecf20Sopenharmony_cishadow_r2t:
10538c2ecf20Sopenharmony_ci		rc = gmap_shadow_r2t(sg, saddr, rfte.val, *fake);
10548c2ecf20Sopenharmony_ci		if (rc)
10558c2ecf20Sopenharmony_ci			return rc;
10568c2ecf20Sopenharmony_ci	}
10578c2ecf20Sopenharmony_ci		fallthrough;
10588c2ecf20Sopenharmony_ci	case ASCE_TYPE_REGION2: {
10598c2ecf20Sopenharmony_ci		union region2_table_entry rste;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci		if (*fake) {
10628c2ecf20Sopenharmony_ci			ptr += vaddr.rsx * _REGION2_SIZE;
10638c2ecf20Sopenharmony_ci			rste.val = ptr;
10648c2ecf20Sopenharmony_ci			goto shadow_r3t;
10658c2ecf20Sopenharmony_ci		}
10668c2ecf20Sopenharmony_ci		*pgt = ptr + vaddr.rsx * 8;
10678c2ecf20Sopenharmony_ci		rc = gmap_read_table(parent, ptr + vaddr.rsx * 8, &rste.val);
10688c2ecf20Sopenharmony_ci		if (rc)
10698c2ecf20Sopenharmony_ci			return rc;
10708c2ecf20Sopenharmony_ci		if (rste.i)
10718c2ecf20Sopenharmony_ci			return PGM_REGION_SECOND_TRANS;
10728c2ecf20Sopenharmony_ci		if (rste.tt != TABLE_TYPE_REGION2)
10738c2ecf20Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
10748c2ecf20Sopenharmony_ci		if (vaddr.rtx01 < rste.tf || vaddr.rtx01 > rste.tl)
10758c2ecf20Sopenharmony_ci			return PGM_REGION_THIRD_TRANS;
10768c2ecf20Sopenharmony_ci		if (sg->edat_level >= 1)
10778c2ecf20Sopenharmony_ci			*dat_protection |= rste.p;
10788c2ecf20Sopenharmony_ci		ptr = rste.rto * PAGE_SIZE;
10798c2ecf20Sopenharmony_cishadow_r3t:
10808c2ecf20Sopenharmony_ci		rste.p |= *dat_protection;
10818c2ecf20Sopenharmony_ci		rc = gmap_shadow_r3t(sg, saddr, rste.val, *fake);
10828c2ecf20Sopenharmony_ci		if (rc)
10838c2ecf20Sopenharmony_ci			return rc;
10848c2ecf20Sopenharmony_ci	}
10858c2ecf20Sopenharmony_ci		fallthrough;
10868c2ecf20Sopenharmony_ci	case ASCE_TYPE_REGION3: {
10878c2ecf20Sopenharmony_ci		union region3_table_entry rtte;
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci		if (*fake) {
10908c2ecf20Sopenharmony_ci			ptr += vaddr.rtx * _REGION3_SIZE;
10918c2ecf20Sopenharmony_ci			rtte.val = ptr;
10928c2ecf20Sopenharmony_ci			goto shadow_sgt;
10938c2ecf20Sopenharmony_ci		}
10948c2ecf20Sopenharmony_ci		*pgt = ptr + vaddr.rtx * 8;
10958c2ecf20Sopenharmony_ci		rc = gmap_read_table(parent, ptr + vaddr.rtx * 8, &rtte.val);
10968c2ecf20Sopenharmony_ci		if (rc)
10978c2ecf20Sopenharmony_ci			return rc;
10988c2ecf20Sopenharmony_ci		if (rtte.i)
10998c2ecf20Sopenharmony_ci			return PGM_REGION_THIRD_TRANS;
11008c2ecf20Sopenharmony_ci		if (rtte.tt != TABLE_TYPE_REGION3)
11018c2ecf20Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
11028c2ecf20Sopenharmony_ci		if (rtte.cr && asce.p && sg->edat_level >= 2)
11038c2ecf20Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
11048c2ecf20Sopenharmony_ci		if (rtte.fc && sg->edat_level >= 2) {
11058c2ecf20Sopenharmony_ci			*dat_protection |= rtte.fc0.p;
11068c2ecf20Sopenharmony_ci			*fake = 1;
11078c2ecf20Sopenharmony_ci			ptr = rtte.fc1.rfaa * _REGION3_SIZE;
11088c2ecf20Sopenharmony_ci			rtte.val = ptr;
11098c2ecf20Sopenharmony_ci			goto shadow_sgt;
11108c2ecf20Sopenharmony_ci		}
11118c2ecf20Sopenharmony_ci		if (vaddr.sx01 < rtte.fc0.tf || vaddr.sx01 > rtte.fc0.tl)
11128c2ecf20Sopenharmony_ci			return PGM_SEGMENT_TRANSLATION;
11138c2ecf20Sopenharmony_ci		if (sg->edat_level >= 1)
11148c2ecf20Sopenharmony_ci			*dat_protection |= rtte.fc0.p;
11158c2ecf20Sopenharmony_ci		ptr = rtte.fc0.sto * PAGE_SIZE;
11168c2ecf20Sopenharmony_cishadow_sgt:
11178c2ecf20Sopenharmony_ci		rtte.fc0.p |= *dat_protection;
11188c2ecf20Sopenharmony_ci		rc = gmap_shadow_sgt(sg, saddr, rtte.val, *fake);
11198c2ecf20Sopenharmony_ci		if (rc)
11208c2ecf20Sopenharmony_ci			return rc;
11218c2ecf20Sopenharmony_ci	}
11228c2ecf20Sopenharmony_ci		fallthrough;
11238c2ecf20Sopenharmony_ci	case ASCE_TYPE_SEGMENT: {
11248c2ecf20Sopenharmony_ci		union segment_table_entry ste;
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci		if (*fake) {
11278c2ecf20Sopenharmony_ci			ptr += vaddr.sx * _SEGMENT_SIZE;
11288c2ecf20Sopenharmony_ci			ste.val = ptr;
11298c2ecf20Sopenharmony_ci			goto shadow_pgt;
11308c2ecf20Sopenharmony_ci		}
11318c2ecf20Sopenharmony_ci		*pgt = ptr + vaddr.sx * 8;
11328c2ecf20Sopenharmony_ci		rc = gmap_read_table(parent, ptr + vaddr.sx * 8, &ste.val);
11338c2ecf20Sopenharmony_ci		if (rc)
11348c2ecf20Sopenharmony_ci			return rc;
11358c2ecf20Sopenharmony_ci		if (ste.i)
11368c2ecf20Sopenharmony_ci			return PGM_SEGMENT_TRANSLATION;
11378c2ecf20Sopenharmony_ci		if (ste.tt != TABLE_TYPE_SEGMENT)
11388c2ecf20Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
11398c2ecf20Sopenharmony_ci		if (ste.cs && asce.p)
11408c2ecf20Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
11418c2ecf20Sopenharmony_ci		*dat_protection |= ste.fc0.p;
11428c2ecf20Sopenharmony_ci		if (ste.fc && sg->edat_level >= 1) {
11438c2ecf20Sopenharmony_ci			*fake = 1;
11448c2ecf20Sopenharmony_ci			ptr = ste.fc1.sfaa * _SEGMENT_SIZE;
11458c2ecf20Sopenharmony_ci			ste.val = ptr;
11468c2ecf20Sopenharmony_ci			goto shadow_pgt;
11478c2ecf20Sopenharmony_ci		}
11488c2ecf20Sopenharmony_ci		ptr = ste.fc0.pto * (PAGE_SIZE / 2);
11498c2ecf20Sopenharmony_cishadow_pgt:
11508c2ecf20Sopenharmony_ci		ste.fc0.p |= *dat_protection;
11518c2ecf20Sopenharmony_ci		rc = gmap_shadow_pgt(sg, saddr, ste.val, *fake);
11528c2ecf20Sopenharmony_ci		if (rc)
11538c2ecf20Sopenharmony_ci			return rc;
11548c2ecf20Sopenharmony_ci	}
11558c2ecf20Sopenharmony_ci	}
11568c2ecf20Sopenharmony_ci	/* Return the parent address of the page table */
11578c2ecf20Sopenharmony_ci	*pgt = ptr;
11588c2ecf20Sopenharmony_ci	return 0;
11598c2ecf20Sopenharmony_ci}
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci/**
11628c2ecf20Sopenharmony_ci * kvm_s390_shadow_fault - handle fault on a shadow page table
11638c2ecf20Sopenharmony_ci * @vcpu: virtual cpu
11648c2ecf20Sopenharmony_ci * @sg: pointer to the shadow guest address space structure
11658c2ecf20Sopenharmony_ci * @saddr: faulting address in the shadow gmap
11668c2ecf20Sopenharmony_ci * @datptr: will contain the address of the faulting DAT table entry, or of
11678c2ecf20Sopenharmony_ci *	    the valid leaf, plus some flags
11688c2ecf20Sopenharmony_ci *
11698c2ecf20Sopenharmony_ci * Returns: - 0 if the shadow fault was successfully resolved
11708c2ecf20Sopenharmony_ci *	    - > 0 (pgm exception code) on exceptions while faulting
11718c2ecf20Sopenharmony_ci *	    - -EAGAIN if the caller can retry immediately
11728c2ecf20Sopenharmony_ci *	    - -EFAULT when accessing invalid guest addresses
11738c2ecf20Sopenharmony_ci *	    - -ENOMEM if out of memory
11748c2ecf20Sopenharmony_ci */
11758c2ecf20Sopenharmony_ciint kvm_s390_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg,
11768c2ecf20Sopenharmony_ci			  unsigned long saddr, unsigned long *datptr)
11778c2ecf20Sopenharmony_ci{
11788c2ecf20Sopenharmony_ci	union vaddress vaddr;
11798c2ecf20Sopenharmony_ci	union page_table_entry pte;
11808c2ecf20Sopenharmony_ci	unsigned long pgt = 0;
11818c2ecf20Sopenharmony_ci	int dat_protection, fake;
11828c2ecf20Sopenharmony_ci	int rc;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	mmap_read_lock(sg->mm);
11858c2ecf20Sopenharmony_ci	/*
11868c2ecf20Sopenharmony_ci	 * We don't want any guest-2 tables to change - so the parent
11878c2ecf20Sopenharmony_ci	 * tables/pointers we read stay valid - unshadowing is however
11888c2ecf20Sopenharmony_ci	 * always possible - only guest_table_lock protects us.
11898c2ecf20Sopenharmony_ci	 */
11908c2ecf20Sopenharmony_ci	ipte_lock(vcpu);
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	rc = gmap_shadow_pgt_lookup(sg, saddr, &pgt, &dat_protection, &fake);
11938c2ecf20Sopenharmony_ci	if (rc)
11948c2ecf20Sopenharmony_ci		rc = kvm_s390_shadow_tables(sg, saddr, &pgt, &dat_protection,
11958c2ecf20Sopenharmony_ci					    &fake);
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	vaddr.addr = saddr;
11988c2ecf20Sopenharmony_ci	if (fake) {
11998c2ecf20Sopenharmony_ci		pte.val = pgt + vaddr.px * PAGE_SIZE;
12008c2ecf20Sopenharmony_ci		goto shadow_page;
12018c2ecf20Sopenharmony_ci	}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	switch (rc) {
12048c2ecf20Sopenharmony_ci	case PGM_SEGMENT_TRANSLATION:
12058c2ecf20Sopenharmony_ci	case PGM_REGION_THIRD_TRANS:
12068c2ecf20Sopenharmony_ci	case PGM_REGION_SECOND_TRANS:
12078c2ecf20Sopenharmony_ci	case PGM_REGION_FIRST_TRANS:
12088c2ecf20Sopenharmony_ci		pgt |= PEI_NOT_PTE;
12098c2ecf20Sopenharmony_ci		break;
12108c2ecf20Sopenharmony_ci	case 0:
12118c2ecf20Sopenharmony_ci		pgt += vaddr.px * 8;
12128c2ecf20Sopenharmony_ci		rc = gmap_read_table(sg->parent, pgt, &pte.val);
12138c2ecf20Sopenharmony_ci	}
12148c2ecf20Sopenharmony_ci	if (datptr)
12158c2ecf20Sopenharmony_ci		*datptr = pgt | dat_protection * PEI_DAT_PROT;
12168c2ecf20Sopenharmony_ci	if (!rc && pte.i)
12178c2ecf20Sopenharmony_ci		rc = PGM_PAGE_TRANSLATION;
12188c2ecf20Sopenharmony_ci	if (!rc && pte.z)
12198c2ecf20Sopenharmony_ci		rc = PGM_TRANSLATION_SPEC;
12208c2ecf20Sopenharmony_cishadow_page:
12218c2ecf20Sopenharmony_ci	pte.p |= dat_protection;
12228c2ecf20Sopenharmony_ci	if (!rc)
12238c2ecf20Sopenharmony_ci		rc = gmap_shadow_page(sg, saddr, __pte(pte.val));
12248c2ecf20Sopenharmony_ci	ipte_unlock(vcpu);
12258c2ecf20Sopenharmony_ci	mmap_read_unlock(sg->mm);
12268c2ecf20Sopenharmony_ci	return rc;
12278c2ecf20Sopenharmony_ci}
1228