162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * guest access functions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright IBM Corp. 2014
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/vmalloc.h>
1062306a36Sopenharmony_ci#include <linux/mm_types.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/pgtable.h>
1362306a36Sopenharmony_ci#include <linux/bitfield.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <asm/gmap.h>
1662306a36Sopenharmony_ci#include "kvm-s390.h"
1762306a36Sopenharmony_ci#include "gaccess.h"
1862306a36Sopenharmony_ci#include <asm/switch_to.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciunion asce {
2162306a36Sopenharmony_ci	unsigned long val;
2262306a36Sopenharmony_ci	struct {
2362306a36Sopenharmony_ci		unsigned long origin : 52; /* Region- or Segment-Table Origin */
2462306a36Sopenharmony_ci		unsigned long	 : 2;
2562306a36Sopenharmony_ci		unsigned long g  : 1; /* Subspace Group Control */
2662306a36Sopenharmony_ci		unsigned long p  : 1; /* Private Space Control */
2762306a36Sopenharmony_ci		unsigned long s  : 1; /* Storage-Alteration-Event Control */
2862306a36Sopenharmony_ci		unsigned long x  : 1; /* Space-Switch-Event Control */
2962306a36Sopenharmony_ci		unsigned long r  : 1; /* Real-Space Control */
3062306a36Sopenharmony_ci		unsigned long	 : 1;
3162306a36Sopenharmony_ci		unsigned long dt : 2; /* Designation-Type Control */
3262306a36Sopenharmony_ci		unsigned long tl : 2; /* Region- or Segment-Table Length */
3362306a36Sopenharmony_ci	};
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cienum {
3762306a36Sopenharmony_ci	ASCE_TYPE_SEGMENT = 0,
3862306a36Sopenharmony_ci	ASCE_TYPE_REGION3 = 1,
3962306a36Sopenharmony_ci	ASCE_TYPE_REGION2 = 2,
4062306a36Sopenharmony_ci	ASCE_TYPE_REGION1 = 3
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ciunion region1_table_entry {
4462306a36Sopenharmony_ci	unsigned long val;
4562306a36Sopenharmony_ci	struct {
4662306a36Sopenharmony_ci		unsigned long rto: 52;/* Region-Table Origin */
4762306a36Sopenharmony_ci		unsigned long	 : 2;
4862306a36Sopenharmony_ci		unsigned long p  : 1; /* DAT-Protection Bit */
4962306a36Sopenharmony_ci		unsigned long	 : 1;
5062306a36Sopenharmony_ci		unsigned long tf : 2; /* Region-Second-Table Offset */
5162306a36Sopenharmony_ci		unsigned long i  : 1; /* Region-Invalid Bit */
5262306a36Sopenharmony_ci		unsigned long	 : 1;
5362306a36Sopenharmony_ci		unsigned long tt : 2; /* Table-Type Bits */
5462306a36Sopenharmony_ci		unsigned long tl : 2; /* Region-Second-Table Length */
5562306a36Sopenharmony_ci	};
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ciunion region2_table_entry {
5962306a36Sopenharmony_ci	unsigned long val;
6062306a36Sopenharmony_ci	struct {
6162306a36Sopenharmony_ci		unsigned long rto: 52;/* Region-Table Origin */
6262306a36Sopenharmony_ci		unsigned long	 : 2;
6362306a36Sopenharmony_ci		unsigned long p  : 1; /* DAT-Protection Bit */
6462306a36Sopenharmony_ci		unsigned long	 : 1;
6562306a36Sopenharmony_ci		unsigned long tf : 2; /* Region-Third-Table Offset */
6662306a36Sopenharmony_ci		unsigned long i  : 1; /* Region-Invalid Bit */
6762306a36Sopenharmony_ci		unsigned long	 : 1;
6862306a36Sopenharmony_ci		unsigned long tt : 2; /* Table-Type Bits */
6962306a36Sopenharmony_ci		unsigned long tl : 2; /* Region-Third-Table Length */
7062306a36Sopenharmony_ci	};
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistruct region3_table_entry_fc0 {
7462306a36Sopenharmony_ci	unsigned long sto: 52;/* Segment-Table Origin */
7562306a36Sopenharmony_ci	unsigned long	 : 1;
7662306a36Sopenharmony_ci	unsigned long fc : 1; /* Format-Control */
7762306a36Sopenharmony_ci	unsigned long p  : 1; /* DAT-Protection Bit */
7862306a36Sopenharmony_ci	unsigned long	 : 1;
7962306a36Sopenharmony_ci	unsigned long tf : 2; /* Segment-Table Offset */
8062306a36Sopenharmony_ci	unsigned long i  : 1; /* Region-Invalid Bit */
8162306a36Sopenharmony_ci	unsigned long cr : 1; /* Common-Region Bit */
8262306a36Sopenharmony_ci	unsigned long tt : 2; /* Table-Type Bits */
8362306a36Sopenharmony_ci	unsigned long tl : 2; /* Segment-Table Length */
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistruct region3_table_entry_fc1 {
8762306a36Sopenharmony_ci	unsigned long rfaa : 33; /* Region-Frame Absolute Address */
8862306a36Sopenharmony_ci	unsigned long	 : 14;
8962306a36Sopenharmony_ci	unsigned long av : 1; /* ACCF-Validity Control */
9062306a36Sopenharmony_ci	unsigned long acc: 4; /* Access-Control Bits */
9162306a36Sopenharmony_ci	unsigned long f  : 1; /* Fetch-Protection Bit */
9262306a36Sopenharmony_ci	unsigned long fc : 1; /* Format-Control */
9362306a36Sopenharmony_ci	unsigned long p  : 1; /* DAT-Protection Bit */
9462306a36Sopenharmony_ci	unsigned long iep: 1; /* Instruction-Execution-Protection */
9562306a36Sopenharmony_ci	unsigned long	 : 2;
9662306a36Sopenharmony_ci	unsigned long i  : 1; /* Region-Invalid Bit */
9762306a36Sopenharmony_ci	unsigned long cr : 1; /* Common-Region Bit */
9862306a36Sopenharmony_ci	unsigned long tt : 2; /* Table-Type Bits */
9962306a36Sopenharmony_ci	unsigned long	 : 2;
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ciunion region3_table_entry {
10362306a36Sopenharmony_ci	unsigned long val;
10462306a36Sopenharmony_ci	struct region3_table_entry_fc0 fc0;
10562306a36Sopenharmony_ci	struct region3_table_entry_fc1 fc1;
10662306a36Sopenharmony_ci	struct {
10762306a36Sopenharmony_ci		unsigned long	 : 53;
10862306a36Sopenharmony_ci		unsigned long fc : 1; /* Format-Control */
10962306a36Sopenharmony_ci		unsigned long	 : 4;
11062306a36Sopenharmony_ci		unsigned long i  : 1; /* Region-Invalid Bit */
11162306a36Sopenharmony_ci		unsigned long cr : 1; /* Common-Region Bit */
11262306a36Sopenharmony_ci		unsigned long tt : 2; /* Table-Type Bits */
11362306a36Sopenharmony_ci		unsigned long	 : 2;
11462306a36Sopenharmony_ci	};
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistruct segment_entry_fc0 {
11862306a36Sopenharmony_ci	unsigned long pto: 53;/* Page-Table Origin */
11962306a36Sopenharmony_ci	unsigned long fc : 1; /* Format-Control */
12062306a36Sopenharmony_ci	unsigned long p  : 1; /* DAT-Protection Bit */
12162306a36Sopenharmony_ci	unsigned long	 : 3;
12262306a36Sopenharmony_ci	unsigned long i  : 1; /* Segment-Invalid Bit */
12362306a36Sopenharmony_ci	unsigned long cs : 1; /* Common-Segment Bit */
12462306a36Sopenharmony_ci	unsigned long tt : 2; /* Table-Type Bits */
12562306a36Sopenharmony_ci	unsigned long	 : 2;
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistruct segment_entry_fc1 {
12962306a36Sopenharmony_ci	unsigned long sfaa : 44; /* Segment-Frame Absolute Address */
13062306a36Sopenharmony_ci	unsigned long	 : 3;
13162306a36Sopenharmony_ci	unsigned long av : 1; /* ACCF-Validity Control */
13262306a36Sopenharmony_ci	unsigned long acc: 4; /* Access-Control Bits */
13362306a36Sopenharmony_ci	unsigned long f  : 1; /* Fetch-Protection Bit */
13462306a36Sopenharmony_ci	unsigned long fc : 1; /* Format-Control */
13562306a36Sopenharmony_ci	unsigned long p  : 1; /* DAT-Protection Bit */
13662306a36Sopenharmony_ci	unsigned long iep: 1; /* Instruction-Execution-Protection */
13762306a36Sopenharmony_ci	unsigned long	 : 2;
13862306a36Sopenharmony_ci	unsigned long i  : 1; /* Segment-Invalid Bit */
13962306a36Sopenharmony_ci	unsigned long cs : 1; /* Common-Segment Bit */
14062306a36Sopenharmony_ci	unsigned long tt : 2; /* Table-Type Bits */
14162306a36Sopenharmony_ci	unsigned long	 : 2;
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ciunion segment_table_entry {
14562306a36Sopenharmony_ci	unsigned long val;
14662306a36Sopenharmony_ci	struct segment_entry_fc0 fc0;
14762306a36Sopenharmony_ci	struct segment_entry_fc1 fc1;
14862306a36Sopenharmony_ci	struct {
14962306a36Sopenharmony_ci		unsigned long	 : 53;
15062306a36Sopenharmony_ci		unsigned long fc : 1; /* Format-Control */
15162306a36Sopenharmony_ci		unsigned long	 : 4;
15262306a36Sopenharmony_ci		unsigned long i  : 1; /* Segment-Invalid Bit */
15362306a36Sopenharmony_ci		unsigned long cs : 1; /* Common-Segment Bit */
15462306a36Sopenharmony_ci		unsigned long tt : 2; /* Table-Type Bits */
15562306a36Sopenharmony_ci		unsigned long	 : 2;
15662306a36Sopenharmony_ci	};
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cienum {
16062306a36Sopenharmony_ci	TABLE_TYPE_SEGMENT = 0,
16162306a36Sopenharmony_ci	TABLE_TYPE_REGION3 = 1,
16262306a36Sopenharmony_ci	TABLE_TYPE_REGION2 = 2,
16362306a36Sopenharmony_ci	TABLE_TYPE_REGION1 = 3
16462306a36Sopenharmony_ci};
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ciunion page_table_entry {
16762306a36Sopenharmony_ci	unsigned long val;
16862306a36Sopenharmony_ci	struct {
16962306a36Sopenharmony_ci		unsigned long pfra : 52; /* Page-Frame Real Address */
17062306a36Sopenharmony_ci		unsigned long z  : 1; /* Zero Bit */
17162306a36Sopenharmony_ci		unsigned long i  : 1; /* Page-Invalid Bit */
17262306a36Sopenharmony_ci		unsigned long p  : 1; /* DAT-Protection Bit */
17362306a36Sopenharmony_ci		unsigned long iep: 1; /* Instruction-Execution-Protection */
17462306a36Sopenharmony_ci		unsigned long	 : 8;
17562306a36Sopenharmony_ci	};
17662306a36Sopenharmony_ci};
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/*
17962306a36Sopenharmony_ci * vaddress union in order to easily decode a virtual address into its
18062306a36Sopenharmony_ci * region first index, region second index etc. parts.
18162306a36Sopenharmony_ci */
18262306a36Sopenharmony_ciunion vaddress {
18362306a36Sopenharmony_ci	unsigned long addr;
18462306a36Sopenharmony_ci	struct {
18562306a36Sopenharmony_ci		unsigned long rfx : 11;
18662306a36Sopenharmony_ci		unsigned long rsx : 11;
18762306a36Sopenharmony_ci		unsigned long rtx : 11;
18862306a36Sopenharmony_ci		unsigned long sx  : 11;
18962306a36Sopenharmony_ci		unsigned long px  : 8;
19062306a36Sopenharmony_ci		unsigned long bx  : 12;
19162306a36Sopenharmony_ci	};
19262306a36Sopenharmony_ci	struct {
19362306a36Sopenharmony_ci		unsigned long rfx01 : 2;
19462306a36Sopenharmony_ci		unsigned long	    : 9;
19562306a36Sopenharmony_ci		unsigned long rsx01 : 2;
19662306a36Sopenharmony_ci		unsigned long	    : 9;
19762306a36Sopenharmony_ci		unsigned long rtx01 : 2;
19862306a36Sopenharmony_ci		unsigned long	    : 9;
19962306a36Sopenharmony_ci		unsigned long sx01  : 2;
20062306a36Sopenharmony_ci		unsigned long	    : 29;
20162306a36Sopenharmony_ci	};
20262306a36Sopenharmony_ci};
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci/*
20562306a36Sopenharmony_ci * raddress union which will contain the result (real or absolute address)
20662306a36Sopenharmony_ci * after a page table walk. The rfaa, sfaa and pfra members are used to
20762306a36Sopenharmony_ci * simply assign them the value of a region, segment or page table entry.
20862306a36Sopenharmony_ci */
20962306a36Sopenharmony_ciunion raddress {
21062306a36Sopenharmony_ci	unsigned long addr;
21162306a36Sopenharmony_ci	unsigned long rfaa : 33; /* Region-Frame Absolute Address */
21262306a36Sopenharmony_ci	unsigned long sfaa : 44; /* Segment-Frame Absolute Address */
21362306a36Sopenharmony_ci	unsigned long pfra : 52; /* Page-Frame Real Address */
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ciunion alet {
21762306a36Sopenharmony_ci	u32 val;
21862306a36Sopenharmony_ci	struct {
21962306a36Sopenharmony_ci		u32 reserved : 7;
22062306a36Sopenharmony_ci		u32 p        : 1;
22162306a36Sopenharmony_ci		u32 alesn    : 8;
22262306a36Sopenharmony_ci		u32 alen     : 16;
22362306a36Sopenharmony_ci	};
22462306a36Sopenharmony_ci};
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ciunion ald {
22762306a36Sopenharmony_ci	u32 val;
22862306a36Sopenharmony_ci	struct {
22962306a36Sopenharmony_ci		u32     : 1;
23062306a36Sopenharmony_ci		u32 alo : 24;
23162306a36Sopenharmony_ci		u32 all : 7;
23262306a36Sopenharmony_ci	};
23362306a36Sopenharmony_ci};
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistruct ale {
23662306a36Sopenharmony_ci	unsigned long i      : 1; /* ALEN-Invalid Bit */
23762306a36Sopenharmony_ci	unsigned long        : 5;
23862306a36Sopenharmony_ci	unsigned long fo     : 1; /* Fetch-Only Bit */
23962306a36Sopenharmony_ci	unsigned long p      : 1; /* Private Bit */
24062306a36Sopenharmony_ci	unsigned long alesn  : 8; /* Access-List-Entry Sequence Number */
24162306a36Sopenharmony_ci	unsigned long aleax  : 16; /* Access-List-Entry Authorization Index */
24262306a36Sopenharmony_ci	unsigned long        : 32;
24362306a36Sopenharmony_ci	unsigned long        : 1;
24462306a36Sopenharmony_ci	unsigned long asteo  : 25; /* ASN-Second-Table-Entry Origin */
24562306a36Sopenharmony_ci	unsigned long        : 6;
24662306a36Sopenharmony_ci	unsigned long astesn : 32; /* ASTE Sequence Number */
24762306a36Sopenharmony_ci};
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistruct aste {
25062306a36Sopenharmony_ci	unsigned long i      : 1; /* ASX-Invalid Bit */
25162306a36Sopenharmony_ci	unsigned long ato    : 29; /* Authority-Table Origin */
25262306a36Sopenharmony_ci	unsigned long        : 1;
25362306a36Sopenharmony_ci	unsigned long b      : 1; /* Base-Space Bit */
25462306a36Sopenharmony_ci	unsigned long ax     : 16; /* Authorization Index */
25562306a36Sopenharmony_ci	unsigned long atl    : 12; /* Authority-Table Length */
25662306a36Sopenharmony_ci	unsigned long        : 2;
25762306a36Sopenharmony_ci	unsigned long ca     : 1; /* Controlled-ASN Bit */
25862306a36Sopenharmony_ci	unsigned long ra     : 1; /* Reusable-ASN Bit */
25962306a36Sopenharmony_ci	unsigned long asce   : 64; /* Address-Space-Control Element */
26062306a36Sopenharmony_ci	unsigned long ald    : 32;
26162306a36Sopenharmony_ci	unsigned long astesn : 32;
26262306a36Sopenharmony_ci	/* .. more fields there */
26362306a36Sopenharmony_ci};
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ciint ipte_lock_held(struct kvm *kvm)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	if (sclp.has_siif) {
26862306a36Sopenharmony_ci		int rc;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		read_lock(&kvm->arch.sca_lock);
27162306a36Sopenharmony_ci		rc = kvm_s390_get_ipte_control(kvm)->kh != 0;
27262306a36Sopenharmony_ci		read_unlock(&kvm->arch.sca_lock);
27362306a36Sopenharmony_ci		return rc;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci	return kvm->arch.ipte_lock_count != 0;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic void ipte_lock_simple(struct kvm *kvm)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	union ipte_control old, new, *ic;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	mutex_lock(&kvm->arch.ipte_mutex);
28362306a36Sopenharmony_ci	kvm->arch.ipte_lock_count++;
28462306a36Sopenharmony_ci	if (kvm->arch.ipte_lock_count > 1)
28562306a36Sopenharmony_ci		goto out;
28662306a36Sopenharmony_ciretry:
28762306a36Sopenharmony_ci	read_lock(&kvm->arch.sca_lock);
28862306a36Sopenharmony_ci	ic = kvm_s390_get_ipte_control(kvm);
28962306a36Sopenharmony_ci	do {
29062306a36Sopenharmony_ci		old = READ_ONCE(*ic);
29162306a36Sopenharmony_ci		if (old.k) {
29262306a36Sopenharmony_ci			read_unlock(&kvm->arch.sca_lock);
29362306a36Sopenharmony_ci			cond_resched();
29462306a36Sopenharmony_ci			goto retry;
29562306a36Sopenharmony_ci		}
29662306a36Sopenharmony_ci		new = old;
29762306a36Sopenharmony_ci		new.k = 1;
29862306a36Sopenharmony_ci	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
29962306a36Sopenharmony_ci	read_unlock(&kvm->arch.sca_lock);
30062306a36Sopenharmony_ciout:
30162306a36Sopenharmony_ci	mutex_unlock(&kvm->arch.ipte_mutex);
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic void ipte_unlock_simple(struct kvm *kvm)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	union ipte_control old, new, *ic;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	mutex_lock(&kvm->arch.ipte_mutex);
30962306a36Sopenharmony_ci	kvm->arch.ipte_lock_count--;
31062306a36Sopenharmony_ci	if (kvm->arch.ipte_lock_count)
31162306a36Sopenharmony_ci		goto out;
31262306a36Sopenharmony_ci	read_lock(&kvm->arch.sca_lock);
31362306a36Sopenharmony_ci	ic = kvm_s390_get_ipte_control(kvm);
31462306a36Sopenharmony_ci	do {
31562306a36Sopenharmony_ci		old = READ_ONCE(*ic);
31662306a36Sopenharmony_ci		new = old;
31762306a36Sopenharmony_ci		new.k = 0;
31862306a36Sopenharmony_ci	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
31962306a36Sopenharmony_ci	read_unlock(&kvm->arch.sca_lock);
32062306a36Sopenharmony_ci	wake_up(&kvm->arch.ipte_wq);
32162306a36Sopenharmony_ciout:
32262306a36Sopenharmony_ci	mutex_unlock(&kvm->arch.ipte_mutex);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic void ipte_lock_siif(struct kvm *kvm)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	union ipte_control old, new, *ic;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ciretry:
33062306a36Sopenharmony_ci	read_lock(&kvm->arch.sca_lock);
33162306a36Sopenharmony_ci	ic = kvm_s390_get_ipte_control(kvm);
33262306a36Sopenharmony_ci	do {
33362306a36Sopenharmony_ci		old = READ_ONCE(*ic);
33462306a36Sopenharmony_ci		if (old.kg) {
33562306a36Sopenharmony_ci			read_unlock(&kvm->arch.sca_lock);
33662306a36Sopenharmony_ci			cond_resched();
33762306a36Sopenharmony_ci			goto retry;
33862306a36Sopenharmony_ci		}
33962306a36Sopenharmony_ci		new = old;
34062306a36Sopenharmony_ci		new.k = 1;
34162306a36Sopenharmony_ci		new.kh++;
34262306a36Sopenharmony_ci	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
34362306a36Sopenharmony_ci	read_unlock(&kvm->arch.sca_lock);
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic void ipte_unlock_siif(struct kvm *kvm)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	union ipte_control old, new, *ic;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	read_lock(&kvm->arch.sca_lock);
35162306a36Sopenharmony_ci	ic = kvm_s390_get_ipte_control(kvm);
35262306a36Sopenharmony_ci	do {
35362306a36Sopenharmony_ci		old = READ_ONCE(*ic);
35462306a36Sopenharmony_ci		new = old;
35562306a36Sopenharmony_ci		new.kh--;
35662306a36Sopenharmony_ci		if (!new.kh)
35762306a36Sopenharmony_ci			new.k = 0;
35862306a36Sopenharmony_ci	} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
35962306a36Sopenharmony_ci	read_unlock(&kvm->arch.sca_lock);
36062306a36Sopenharmony_ci	if (!new.kh)
36162306a36Sopenharmony_ci		wake_up(&kvm->arch.ipte_wq);
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_civoid ipte_lock(struct kvm *kvm)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	if (sclp.has_siif)
36762306a36Sopenharmony_ci		ipte_lock_siif(kvm);
36862306a36Sopenharmony_ci	else
36962306a36Sopenharmony_ci		ipte_lock_simple(kvm);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_civoid ipte_unlock(struct kvm *kvm)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	if (sclp.has_siif)
37562306a36Sopenharmony_ci		ipte_unlock_siif(kvm);
37662306a36Sopenharmony_ci	else
37762306a36Sopenharmony_ci		ipte_unlock_simple(kvm);
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic int ar_translation(struct kvm_vcpu *vcpu, union asce *asce, u8 ar,
38162306a36Sopenharmony_ci			  enum gacc_mode mode)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	union alet alet;
38462306a36Sopenharmony_ci	struct ale ale;
38562306a36Sopenharmony_ci	struct aste aste;
38662306a36Sopenharmony_ci	unsigned long ald_addr, authority_table_addr;
38762306a36Sopenharmony_ci	union ald ald;
38862306a36Sopenharmony_ci	int eax, rc;
38962306a36Sopenharmony_ci	u8 authority_table;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (ar >= NUM_ACRS)
39262306a36Sopenharmony_ci		return -EINVAL;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	save_access_regs(vcpu->run->s.regs.acrs);
39562306a36Sopenharmony_ci	alet.val = vcpu->run->s.regs.acrs[ar];
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (ar == 0 || alet.val == 0) {
39862306a36Sopenharmony_ci		asce->val = vcpu->arch.sie_block->gcr[1];
39962306a36Sopenharmony_ci		return 0;
40062306a36Sopenharmony_ci	} else if (alet.val == 1) {
40162306a36Sopenharmony_ci		asce->val = vcpu->arch.sie_block->gcr[7];
40262306a36Sopenharmony_ci		return 0;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (alet.reserved)
40662306a36Sopenharmony_ci		return PGM_ALET_SPECIFICATION;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (alet.p)
40962306a36Sopenharmony_ci		ald_addr = vcpu->arch.sie_block->gcr[5];
41062306a36Sopenharmony_ci	else
41162306a36Sopenharmony_ci		ald_addr = vcpu->arch.sie_block->gcr[2];
41262306a36Sopenharmony_ci	ald_addr &= 0x7fffffc0;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	rc = read_guest_real(vcpu, ald_addr + 16, &ald.val, sizeof(union ald));
41562306a36Sopenharmony_ci	if (rc)
41662306a36Sopenharmony_ci		return rc;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (alet.alen / 8 > ald.all)
41962306a36Sopenharmony_ci		return PGM_ALEN_TRANSLATION;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	if (0x7fffffff - ald.alo * 128 < alet.alen * 16)
42262306a36Sopenharmony_ci		return PGM_ADDRESSING;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	rc = read_guest_real(vcpu, ald.alo * 128 + alet.alen * 16, &ale,
42562306a36Sopenharmony_ci			     sizeof(struct ale));
42662306a36Sopenharmony_ci	if (rc)
42762306a36Sopenharmony_ci		return rc;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (ale.i == 1)
43062306a36Sopenharmony_ci		return PGM_ALEN_TRANSLATION;
43162306a36Sopenharmony_ci	if (ale.alesn != alet.alesn)
43262306a36Sopenharmony_ci		return PGM_ALE_SEQUENCE;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	rc = read_guest_real(vcpu, ale.asteo * 64, &aste, sizeof(struct aste));
43562306a36Sopenharmony_ci	if (rc)
43662306a36Sopenharmony_ci		return rc;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (aste.i)
43962306a36Sopenharmony_ci		return PGM_ASTE_VALIDITY;
44062306a36Sopenharmony_ci	if (aste.astesn != ale.astesn)
44162306a36Sopenharmony_ci		return PGM_ASTE_SEQUENCE;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (ale.p == 1) {
44462306a36Sopenharmony_ci		eax = (vcpu->arch.sie_block->gcr[8] >> 16) & 0xffff;
44562306a36Sopenharmony_ci		if (ale.aleax != eax) {
44662306a36Sopenharmony_ci			if (eax / 16 > aste.atl)
44762306a36Sopenharmony_ci				return PGM_EXTENDED_AUTHORITY;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci			authority_table_addr = aste.ato * 4 + eax / 4;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci			rc = read_guest_real(vcpu, authority_table_addr,
45262306a36Sopenharmony_ci					     &authority_table,
45362306a36Sopenharmony_ci					     sizeof(u8));
45462306a36Sopenharmony_ci			if (rc)
45562306a36Sopenharmony_ci				return rc;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci			if ((authority_table & (0x40 >> ((eax & 3) * 2))) == 0)
45862306a36Sopenharmony_ci				return PGM_EXTENDED_AUTHORITY;
45962306a36Sopenharmony_ci		}
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	if (ale.fo == 1 && mode == GACC_STORE)
46362306a36Sopenharmony_ci		return PGM_PROTECTION;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	asce->val = aste.asce;
46662306a36Sopenharmony_ci	return 0;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistruct trans_exc_code_bits {
47062306a36Sopenharmony_ci	unsigned long addr : 52; /* Translation-exception Address */
47162306a36Sopenharmony_ci	unsigned long fsi  : 2;  /* Access Exception Fetch/Store Indication */
47262306a36Sopenharmony_ci	unsigned long	   : 2;
47362306a36Sopenharmony_ci	unsigned long b56  : 1;
47462306a36Sopenharmony_ci	unsigned long	   : 3;
47562306a36Sopenharmony_ci	unsigned long b60  : 1;
47662306a36Sopenharmony_ci	unsigned long b61  : 1;
47762306a36Sopenharmony_ci	unsigned long as   : 2;  /* ASCE Identifier */
47862306a36Sopenharmony_ci};
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cienum {
48162306a36Sopenharmony_ci	FSI_UNKNOWN = 0, /* Unknown whether fetch or store */
48262306a36Sopenharmony_ci	FSI_STORE   = 1, /* Exception was due to store operation */
48362306a36Sopenharmony_ci	FSI_FETCH   = 2  /* Exception was due to fetch operation */
48462306a36Sopenharmony_ci};
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cienum prot_type {
48762306a36Sopenharmony_ci	PROT_TYPE_LA   = 0,
48862306a36Sopenharmony_ci	PROT_TYPE_KEYC = 1,
48962306a36Sopenharmony_ci	PROT_TYPE_ALC  = 2,
49062306a36Sopenharmony_ci	PROT_TYPE_DAT  = 3,
49162306a36Sopenharmony_ci	PROT_TYPE_IEP  = 4,
49262306a36Sopenharmony_ci	/* Dummy value for passing an initialized value when code != PGM_PROTECTION */
49362306a36Sopenharmony_ci	PROT_NONE,
49462306a36Sopenharmony_ci};
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic int trans_exc_ending(struct kvm_vcpu *vcpu, int code, unsigned long gva, u8 ar,
49762306a36Sopenharmony_ci			    enum gacc_mode mode, enum prot_type prot, bool terminate)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm;
50062306a36Sopenharmony_ci	struct trans_exc_code_bits *tec;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	memset(pgm, 0, sizeof(*pgm));
50362306a36Sopenharmony_ci	pgm->code = code;
50462306a36Sopenharmony_ci	tec = (struct trans_exc_code_bits *)&pgm->trans_exc_code;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	switch (code) {
50762306a36Sopenharmony_ci	case PGM_PROTECTION:
50862306a36Sopenharmony_ci		switch (prot) {
50962306a36Sopenharmony_ci		case PROT_NONE:
51062306a36Sopenharmony_ci			/* We should never get here, acts like termination */
51162306a36Sopenharmony_ci			WARN_ON_ONCE(1);
51262306a36Sopenharmony_ci			break;
51362306a36Sopenharmony_ci		case PROT_TYPE_IEP:
51462306a36Sopenharmony_ci			tec->b61 = 1;
51562306a36Sopenharmony_ci			fallthrough;
51662306a36Sopenharmony_ci		case PROT_TYPE_LA:
51762306a36Sopenharmony_ci			tec->b56 = 1;
51862306a36Sopenharmony_ci			break;
51962306a36Sopenharmony_ci		case PROT_TYPE_KEYC:
52062306a36Sopenharmony_ci			tec->b60 = 1;
52162306a36Sopenharmony_ci			break;
52262306a36Sopenharmony_ci		case PROT_TYPE_ALC:
52362306a36Sopenharmony_ci			tec->b60 = 1;
52462306a36Sopenharmony_ci			fallthrough;
52562306a36Sopenharmony_ci		case PROT_TYPE_DAT:
52662306a36Sopenharmony_ci			tec->b61 = 1;
52762306a36Sopenharmony_ci			break;
52862306a36Sopenharmony_ci		}
52962306a36Sopenharmony_ci		if (terminate) {
53062306a36Sopenharmony_ci			tec->b56 = 0;
53162306a36Sopenharmony_ci			tec->b60 = 0;
53262306a36Sopenharmony_ci			tec->b61 = 0;
53362306a36Sopenharmony_ci		}
53462306a36Sopenharmony_ci		fallthrough;
53562306a36Sopenharmony_ci	case PGM_ASCE_TYPE:
53662306a36Sopenharmony_ci	case PGM_PAGE_TRANSLATION:
53762306a36Sopenharmony_ci	case PGM_REGION_FIRST_TRANS:
53862306a36Sopenharmony_ci	case PGM_REGION_SECOND_TRANS:
53962306a36Sopenharmony_ci	case PGM_REGION_THIRD_TRANS:
54062306a36Sopenharmony_ci	case PGM_SEGMENT_TRANSLATION:
54162306a36Sopenharmony_ci		/*
54262306a36Sopenharmony_ci		 * op_access_id only applies to MOVE_PAGE -> set bit 61
54362306a36Sopenharmony_ci		 * exc_access_id has to be set to 0 for some instructions. Both
54462306a36Sopenharmony_ci		 * cases have to be handled by the caller.
54562306a36Sopenharmony_ci		 */
54662306a36Sopenharmony_ci		tec->addr = gva >> PAGE_SHIFT;
54762306a36Sopenharmony_ci		tec->fsi = mode == GACC_STORE ? FSI_STORE : FSI_FETCH;
54862306a36Sopenharmony_ci		tec->as = psw_bits(vcpu->arch.sie_block->gpsw).as;
54962306a36Sopenharmony_ci		fallthrough;
55062306a36Sopenharmony_ci	case PGM_ALEN_TRANSLATION:
55162306a36Sopenharmony_ci	case PGM_ALE_SEQUENCE:
55262306a36Sopenharmony_ci	case PGM_ASTE_VALIDITY:
55362306a36Sopenharmony_ci	case PGM_ASTE_SEQUENCE:
55462306a36Sopenharmony_ci	case PGM_EXTENDED_AUTHORITY:
55562306a36Sopenharmony_ci		/*
55662306a36Sopenharmony_ci		 * We can always store exc_access_id, as it is
55762306a36Sopenharmony_ci		 * undefined for non-ar cases. It is undefined for
55862306a36Sopenharmony_ci		 * most DAT protection exceptions.
55962306a36Sopenharmony_ci		 */
56062306a36Sopenharmony_ci		pgm->exc_access_id = ar;
56162306a36Sopenharmony_ci		break;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci	return code;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic int trans_exc(struct kvm_vcpu *vcpu, int code, unsigned long gva, u8 ar,
56762306a36Sopenharmony_ci		     enum gacc_mode mode, enum prot_type prot)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	return trans_exc_ending(vcpu, code, gva, ar, mode, prot, false);
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic int get_vcpu_asce(struct kvm_vcpu *vcpu, union asce *asce,
57362306a36Sopenharmony_ci			 unsigned long ga, u8 ar, enum gacc_mode mode)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	int rc;
57662306a36Sopenharmony_ci	struct psw_bits psw = psw_bits(vcpu->arch.sie_block->gpsw);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	if (!psw.dat) {
57962306a36Sopenharmony_ci		asce->val = 0;
58062306a36Sopenharmony_ci		asce->r = 1;
58162306a36Sopenharmony_ci		return 0;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	if ((mode == GACC_IFETCH) && (psw.as != PSW_BITS_AS_HOME))
58562306a36Sopenharmony_ci		psw.as = PSW_BITS_AS_PRIMARY;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	switch (psw.as) {
58862306a36Sopenharmony_ci	case PSW_BITS_AS_PRIMARY:
58962306a36Sopenharmony_ci		asce->val = vcpu->arch.sie_block->gcr[1];
59062306a36Sopenharmony_ci		return 0;
59162306a36Sopenharmony_ci	case PSW_BITS_AS_SECONDARY:
59262306a36Sopenharmony_ci		asce->val = vcpu->arch.sie_block->gcr[7];
59362306a36Sopenharmony_ci		return 0;
59462306a36Sopenharmony_ci	case PSW_BITS_AS_HOME:
59562306a36Sopenharmony_ci		asce->val = vcpu->arch.sie_block->gcr[13];
59662306a36Sopenharmony_ci		return 0;
59762306a36Sopenharmony_ci	case PSW_BITS_AS_ACCREG:
59862306a36Sopenharmony_ci		rc = ar_translation(vcpu, asce, ar, mode);
59962306a36Sopenharmony_ci		if (rc > 0)
60062306a36Sopenharmony_ci			return trans_exc(vcpu, rc, ga, ar, mode, PROT_TYPE_ALC);
60162306a36Sopenharmony_ci		return rc;
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci	return 0;
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic int deref_table(struct kvm *kvm, unsigned long gpa, unsigned long *val)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	return kvm_read_guest(kvm, gpa, val, sizeof(*val));
60962306a36Sopenharmony_ci}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci/**
61262306a36Sopenharmony_ci * guest_translate - translate a guest virtual into a guest absolute address
61362306a36Sopenharmony_ci * @vcpu: virtual cpu
61462306a36Sopenharmony_ci * @gva: guest virtual address
61562306a36Sopenharmony_ci * @gpa: points to where guest physical (absolute) address should be stored
61662306a36Sopenharmony_ci * @asce: effective asce
61762306a36Sopenharmony_ci * @mode: indicates the access mode to be used
61862306a36Sopenharmony_ci * @prot: returns the type for protection exceptions
61962306a36Sopenharmony_ci *
62062306a36Sopenharmony_ci * Translate a guest virtual address into a guest absolute address by means
62162306a36Sopenharmony_ci * of dynamic address translation as specified by the architecture.
62262306a36Sopenharmony_ci * If the resulting absolute address is not available in the configuration
62362306a36Sopenharmony_ci * an addressing exception is indicated and @gpa will not be changed.
62462306a36Sopenharmony_ci *
62562306a36Sopenharmony_ci * Returns: - zero on success; @gpa contains the resulting absolute address
62662306a36Sopenharmony_ci *	    - a negative value if guest access failed due to e.g. broken
62762306a36Sopenharmony_ci *	      guest mapping
62862306a36Sopenharmony_ci *	    - a positive value if an access exception happened. In this case
62962306a36Sopenharmony_ci *	      the returned value is the program interruption code as defined
63062306a36Sopenharmony_ci *	      by the architecture
63162306a36Sopenharmony_ci */
63262306a36Sopenharmony_cistatic unsigned long guest_translate(struct kvm_vcpu *vcpu, unsigned long gva,
63362306a36Sopenharmony_ci				     unsigned long *gpa, const union asce asce,
63462306a36Sopenharmony_ci				     enum gacc_mode mode, enum prot_type *prot)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	union vaddress vaddr = {.addr = gva};
63762306a36Sopenharmony_ci	union raddress raddr = {.addr = gva};
63862306a36Sopenharmony_ci	union page_table_entry pte;
63962306a36Sopenharmony_ci	int dat_protection = 0;
64062306a36Sopenharmony_ci	int iep_protection = 0;
64162306a36Sopenharmony_ci	union ctlreg0 ctlreg0;
64262306a36Sopenharmony_ci	unsigned long ptr;
64362306a36Sopenharmony_ci	int edat1, edat2, iep;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	ctlreg0.val = vcpu->arch.sie_block->gcr[0];
64662306a36Sopenharmony_ci	edat1 = ctlreg0.edat && test_kvm_facility(vcpu->kvm, 8);
64762306a36Sopenharmony_ci	edat2 = edat1 && test_kvm_facility(vcpu->kvm, 78);
64862306a36Sopenharmony_ci	iep = ctlreg0.iep && test_kvm_facility(vcpu->kvm, 130);
64962306a36Sopenharmony_ci	if (asce.r)
65062306a36Sopenharmony_ci		goto real_address;
65162306a36Sopenharmony_ci	ptr = asce.origin * PAGE_SIZE;
65262306a36Sopenharmony_ci	switch (asce.dt) {
65362306a36Sopenharmony_ci	case ASCE_TYPE_REGION1:
65462306a36Sopenharmony_ci		if (vaddr.rfx01 > asce.tl)
65562306a36Sopenharmony_ci			return PGM_REGION_FIRST_TRANS;
65662306a36Sopenharmony_ci		ptr += vaddr.rfx * 8;
65762306a36Sopenharmony_ci		break;
65862306a36Sopenharmony_ci	case ASCE_TYPE_REGION2:
65962306a36Sopenharmony_ci		if (vaddr.rfx)
66062306a36Sopenharmony_ci			return PGM_ASCE_TYPE;
66162306a36Sopenharmony_ci		if (vaddr.rsx01 > asce.tl)
66262306a36Sopenharmony_ci			return PGM_REGION_SECOND_TRANS;
66362306a36Sopenharmony_ci		ptr += vaddr.rsx * 8;
66462306a36Sopenharmony_ci		break;
66562306a36Sopenharmony_ci	case ASCE_TYPE_REGION3:
66662306a36Sopenharmony_ci		if (vaddr.rfx || vaddr.rsx)
66762306a36Sopenharmony_ci			return PGM_ASCE_TYPE;
66862306a36Sopenharmony_ci		if (vaddr.rtx01 > asce.tl)
66962306a36Sopenharmony_ci			return PGM_REGION_THIRD_TRANS;
67062306a36Sopenharmony_ci		ptr += vaddr.rtx * 8;
67162306a36Sopenharmony_ci		break;
67262306a36Sopenharmony_ci	case ASCE_TYPE_SEGMENT:
67362306a36Sopenharmony_ci		if (vaddr.rfx || vaddr.rsx || vaddr.rtx)
67462306a36Sopenharmony_ci			return PGM_ASCE_TYPE;
67562306a36Sopenharmony_ci		if (vaddr.sx01 > asce.tl)
67662306a36Sopenharmony_ci			return PGM_SEGMENT_TRANSLATION;
67762306a36Sopenharmony_ci		ptr += vaddr.sx * 8;
67862306a36Sopenharmony_ci		break;
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci	switch (asce.dt) {
68162306a36Sopenharmony_ci	case ASCE_TYPE_REGION1:	{
68262306a36Sopenharmony_ci		union region1_table_entry rfte;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci		if (kvm_is_error_gpa(vcpu->kvm, ptr))
68562306a36Sopenharmony_ci			return PGM_ADDRESSING;
68662306a36Sopenharmony_ci		if (deref_table(vcpu->kvm, ptr, &rfte.val))
68762306a36Sopenharmony_ci			return -EFAULT;
68862306a36Sopenharmony_ci		if (rfte.i)
68962306a36Sopenharmony_ci			return PGM_REGION_FIRST_TRANS;
69062306a36Sopenharmony_ci		if (rfte.tt != TABLE_TYPE_REGION1)
69162306a36Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
69262306a36Sopenharmony_ci		if (vaddr.rsx01 < rfte.tf || vaddr.rsx01 > rfte.tl)
69362306a36Sopenharmony_ci			return PGM_REGION_SECOND_TRANS;
69462306a36Sopenharmony_ci		if (edat1)
69562306a36Sopenharmony_ci			dat_protection |= rfte.p;
69662306a36Sopenharmony_ci		ptr = rfte.rto * PAGE_SIZE + vaddr.rsx * 8;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci		fallthrough;
69962306a36Sopenharmony_ci	case ASCE_TYPE_REGION2: {
70062306a36Sopenharmony_ci		union region2_table_entry rste;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci		if (kvm_is_error_gpa(vcpu->kvm, ptr))
70362306a36Sopenharmony_ci			return PGM_ADDRESSING;
70462306a36Sopenharmony_ci		if (deref_table(vcpu->kvm, ptr, &rste.val))
70562306a36Sopenharmony_ci			return -EFAULT;
70662306a36Sopenharmony_ci		if (rste.i)
70762306a36Sopenharmony_ci			return PGM_REGION_SECOND_TRANS;
70862306a36Sopenharmony_ci		if (rste.tt != TABLE_TYPE_REGION2)
70962306a36Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
71062306a36Sopenharmony_ci		if (vaddr.rtx01 < rste.tf || vaddr.rtx01 > rste.tl)
71162306a36Sopenharmony_ci			return PGM_REGION_THIRD_TRANS;
71262306a36Sopenharmony_ci		if (edat1)
71362306a36Sopenharmony_ci			dat_protection |= rste.p;
71462306a36Sopenharmony_ci		ptr = rste.rto * PAGE_SIZE + vaddr.rtx * 8;
71562306a36Sopenharmony_ci	}
71662306a36Sopenharmony_ci		fallthrough;
71762306a36Sopenharmony_ci	case ASCE_TYPE_REGION3: {
71862306a36Sopenharmony_ci		union region3_table_entry rtte;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci		if (kvm_is_error_gpa(vcpu->kvm, ptr))
72162306a36Sopenharmony_ci			return PGM_ADDRESSING;
72262306a36Sopenharmony_ci		if (deref_table(vcpu->kvm, ptr, &rtte.val))
72362306a36Sopenharmony_ci			return -EFAULT;
72462306a36Sopenharmony_ci		if (rtte.i)
72562306a36Sopenharmony_ci			return PGM_REGION_THIRD_TRANS;
72662306a36Sopenharmony_ci		if (rtte.tt != TABLE_TYPE_REGION3)
72762306a36Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
72862306a36Sopenharmony_ci		if (rtte.cr && asce.p && edat2)
72962306a36Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
73062306a36Sopenharmony_ci		if (rtte.fc && edat2) {
73162306a36Sopenharmony_ci			dat_protection |= rtte.fc1.p;
73262306a36Sopenharmony_ci			iep_protection = rtte.fc1.iep;
73362306a36Sopenharmony_ci			raddr.rfaa = rtte.fc1.rfaa;
73462306a36Sopenharmony_ci			goto absolute_address;
73562306a36Sopenharmony_ci		}
73662306a36Sopenharmony_ci		if (vaddr.sx01 < rtte.fc0.tf)
73762306a36Sopenharmony_ci			return PGM_SEGMENT_TRANSLATION;
73862306a36Sopenharmony_ci		if (vaddr.sx01 > rtte.fc0.tl)
73962306a36Sopenharmony_ci			return PGM_SEGMENT_TRANSLATION;
74062306a36Sopenharmony_ci		if (edat1)
74162306a36Sopenharmony_ci			dat_protection |= rtte.fc0.p;
74262306a36Sopenharmony_ci		ptr = rtte.fc0.sto * PAGE_SIZE + vaddr.sx * 8;
74362306a36Sopenharmony_ci	}
74462306a36Sopenharmony_ci		fallthrough;
74562306a36Sopenharmony_ci	case ASCE_TYPE_SEGMENT: {
74662306a36Sopenharmony_ci		union segment_table_entry ste;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci		if (kvm_is_error_gpa(vcpu->kvm, ptr))
74962306a36Sopenharmony_ci			return PGM_ADDRESSING;
75062306a36Sopenharmony_ci		if (deref_table(vcpu->kvm, ptr, &ste.val))
75162306a36Sopenharmony_ci			return -EFAULT;
75262306a36Sopenharmony_ci		if (ste.i)
75362306a36Sopenharmony_ci			return PGM_SEGMENT_TRANSLATION;
75462306a36Sopenharmony_ci		if (ste.tt != TABLE_TYPE_SEGMENT)
75562306a36Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
75662306a36Sopenharmony_ci		if (ste.cs && asce.p)
75762306a36Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
75862306a36Sopenharmony_ci		if (ste.fc && edat1) {
75962306a36Sopenharmony_ci			dat_protection |= ste.fc1.p;
76062306a36Sopenharmony_ci			iep_protection = ste.fc1.iep;
76162306a36Sopenharmony_ci			raddr.sfaa = ste.fc1.sfaa;
76262306a36Sopenharmony_ci			goto absolute_address;
76362306a36Sopenharmony_ci		}
76462306a36Sopenharmony_ci		dat_protection |= ste.fc0.p;
76562306a36Sopenharmony_ci		ptr = ste.fc0.pto * (PAGE_SIZE / 2) + vaddr.px * 8;
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci	if (kvm_is_error_gpa(vcpu->kvm, ptr))
76962306a36Sopenharmony_ci		return PGM_ADDRESSING;
77062306a36Sopenharmony_ci	if (deref_table(vcpu->kvm, ptr, &pte.val))
77162306a36Sopenharmony_ci		return -EFAULT;
77262306a36Sopenharmony_ci	if (pte.i)
77362306a36Sopenharmony_ci		return PGM_PAGE_TRANSLATION;
77462306a36Sopenharmony_ci	if (pte.z)
77562306a36Sopenharmony_ci		return PGM_TRANSLATION_SPEC;
77662306a36Sopenharmony_ci	dat_protection |= pte.p;
77762306a36Sopenharmony_ci	iep_protection = pte.iep;
77862306a36Sopenharmony_ci	raddr.pfra = pte.pfra;
77962306a36Sopenharmony_cireal_address:
78062306a36Sopenharmony_ci	raddr.addr = kvm_s390_real_to_abs(vcpu, raddr.addr);
78162306a36Sopenharmony_ciabsolute_address:
78262306a36Sopenharmony_ci	if (mode == GACC_STORE && dat_protection) {
78362306a36Sopenharmony_ci		*prot = PROT_TYPE_DAT;
78462306a36Sopenharmony_ci		return PGM_PROTECTION;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci	if (mode == GACC_IFETCH && iep_protection && iep) {
78762306a36Sopenharmony_ci		*prot = PROT_TYPE_IEP;
78862306a36Sopenharmony_ci		return PGM_PROTECTION;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci	if (kvm_is_error_gpa(vcpu->kvm, raddr.addr))
79162306a36Sopenharmony_ci		return PGM_ADDRESSING;
79262306a36Sopenharmony_ci	*gpa = raddr.addr;
79362306a36Sopenharmony_ci	return 0;
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic inline int is_low_address(unsigned long ga)
79762306a36Sopenharmony_ci{
79862306a36Sopenharmony_ci	/* Check for address ranges 0..511 and 4096..4607 */
79962306a36Sopenharmony_ci	return (ga & ~0x11fful) == 0;
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic int low_address_protection_enabled(struct kvm_vcpu *vcpu,
80362306a36Sopenharmony_ci					  const union asce asce)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	union ctlreg0 ctlreg0 = {.val = vcpu->arch.sie_block->gcr[0]};
80662306a36Sopenharmony_ci	psw_t *psw = &vcpu->arch.sie_block->gpsw;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	if (!ctlreg0.lap)
80962306a36Sopenharmony_ci		return 0;
81062306a36Sopenharmony_ci	if (psw_bits(*psw).dat && asce.p)
81162306a36Sopenharmony_ci		return 0;
81262306a36Sopenharmony_ci	return 1;
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cistatic int vm_check_access_key(struct kvm *kvm, u8 access_key,
81662306a36Sopenharmony_ci			       enum gacc_mode mode, gpa_t gpa)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	u8 storage_key, access_control;
81962306a36Sopenharmony_ci	bool fetch_protected;
82062306a36Sopenharmony_ci	unsigned long hva;
82162306a36Sopenharmony_ci	int r;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	if (access_key == 0)
82462306a36Sopenharmony_ci		return 0;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	hva = gfn_to_hva(kvm, gpa_to_gfn(gpa));
82762306a36Sopenharmony_ci	if (kvm_is_error_hva(hva))
82862306a36Sopenharmony_ci		return PGM_ADDRESSING;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	mmap_read_lock(current->mm);
83162306a36Sopenharmony_ci	r = get_guest_storage_key(current->mm, hva, &storage_key);
83262306a36Sopenharmony_ci	mmap_read_unlock(current->mm);
83362306a36Sopenharmony_ci	if (r)
83462306a36Sopenharmony_ci		return r;
83562306a36Sopenharmony_ci	access_control = FIELD_GET(_PAGE_ACC_BITS, storage_key);
83662306a36Sopenharmony_ci	if (access_control == access_key)
83762306a36Sopenharmony_ci		return 0;
83862306a36Sopenharmony_ci	fetch_protected = storage_key & _PAGE_FP_BIT;
83962306a36Sopenharmony_ci	if ((mode == GACC_FETCH || mode == GACC_IFETCH) && !fetch_protected)
84062306a36Sopenharmony_ci		return 0;
84162306a36Sopenharmony_ci	return PGM_PROTECTION;
84262306a36Sopenharmony_ci}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_cistatic bool fetch_prot_override_applicable(struct kvm_vcpu *vcpu, enum gacc_mode mode,
84562306a36Sopenharmony_ci					   union asce asce)
84662306a36Sopenharmony_ci{
84762306a36Sopenharmony_ci	psw_t *psw = &vcpu->arch.sie_block->gpsw;
84862306a36Sopenharmony_ci	unsigned long override;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	if (mode == GACC_FETCH || mode == GACC_IFETCH) {
85162306a36Sopenharmony_ci		/* check if fetch protection override enabled */
85262306a36Sopenharmony_ci		override = vcpu->arch.sie_block->gcr[0];
85362306a36Sopenharmony_ci		override &= CR0_FETCH_PROTECTION_OVERRIDE;
85462306a36Sopenharmony_ci		/* not applicable if subject to DAT && private space */
85562306a36Sopenharmony_ci		override = override && !(psw_bits(*psw).dat && asce.p);
85662306a36Sopenharmony_ci		return override;
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci	return false;
85962306a36Sopenharmony_ci}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cistatic bool fetch_prot_override_applies(unsigned long ga, unsigned int len)
86262306a36Sopenharmony_ci{
86362306a36Sopenharmony_ci	return ga < 2048 && ga + len <= 2048;
86462306a36Sopenharmony_ci}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_cistatic bool storage_prot_override_applicable(struct kvm_vcpu *vcpu)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	/* check if storage protection override enabled */
86962306a36Sopenharmony_ci	return vcpu->arch.sie_block->gcr[0] & CR0_STORAGE_PROTECTION_OVERRIDE;
87062306a36Sopenharmony_ci}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_cistatic bool storage_prot_override_applies(u8 access_control)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	/* matches special storage protection override key (9) -> allow */
87562306a36Sopenharmony_ci	return access_control == PAGE_SPO_ACC;
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic int vcpu_check_access_key(struct kvm_vcpu *vcpu, u8 access_key,
87962306a36Sopenharmony_ci				 enum gacc_mode mode, union asce asce, gpa_t gpa,
88062306a36Sopenharmony_ci				 unsigned long ga, unsigned int len)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	u8 storage_key, access_control;
88362306a36Sopenharmony_ci	unsigned long hva;
88462306a36Sopenharmony_ci	int r;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	/* access key 0 matches any storage key -> allow */
88762306a36Sopenharmony_ci	if (access_key == 0)
88862306a36Sopenharmony_ci		return 0;
88962306a36Sopenharmony_ci	/*
89062306a36Sopenharmony_ci	 * caller needs to ensure that gfn is accessible, so we can
89162306a36Sopenharmony_ci	 * assume that this cannot fail
89262306a36Sopenharmony_ci	 */
89362306a36Sopenharmony_ci	hva = gfn_to_hva(vcpu->kvm, gpa_to_gfn(gpa));
89462306a36Sopenharmony_ci	mmap_read_lock(current->mm);
89562306a36Sopenharmony_ci	r = get_guest_storage_key(current->mm, hva, &storage_key);
89662306a36Sopenharmony_ci	mmap_read_unlock(current->mm);
89762306a36Sopenharmony_ci	if (r)
89862306a36Sopenharmony_ci		return r;
89962306a36Sopenharmony_ci	access_control = FIELD_GET(_PAGE_ACC_BITS, storage_key);
90062306a36Sopenharmony_ci	/* access key matches storage key -> allow */
90162306a36Sopenharmony_ci	if (access_control == access_key)
90262306a36Sopenharmony_ci		return 0;
90362306a36Sopenharmony_ci	if (mode == GACC_FETCH || mode == GACC_IFETCH) {
90462306a36Sopenharmony_ci		/* it is a fetch and fetch protection is off -> allow */
90562306a36Sopenharmony_ci		if (!(storage_key & _PAGE_FP_BIT))
90662306a36Sopenharmony_ci			return 0;
90762306a36Sopenharmony_ci		if (fetch_prot_override_applicable(vcpu, mode, asce) &&
90862306a36Sopenharmony_ci		    fetch_prot_override_applies(ga, len))
90962306a36Sopenharmony_ci			return 0;
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci	if (storage_prot_override_applicable(vcpu) &&
91262306a36Sopenharmony_ci	    storage_prot_override_applies(access_control))
91362306a36Sopenharmony_ci		return 0;
91462306a36Sopenharmony_ci	return PGM_PROTECTION;
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci/**
91862306a36Sopenharmony_ci * guest_range_to_gpas() - Calculate guest physical addresses of page fragments
91962306a36Sopenharmony_ci * covering a logical range
92062306a36Sopenharmony_ci * @vcpu: virtual cpu
92162306a36Sopenharmony_ci * @ga: guest address, start of range
92262306a36Sopenharmony_ci * @ar: access register
92362306a36Sopenharmony_ci * @gpas: output argument, may be NULL
92462306a36Sopenharmony_ci * @len: length of range in bytes
92562306a36Sopenharmony_ci * @asce: address-space-control element to use for translation
92662306a36Sopenharmony_ci * @mode: access mode
92762306a36Sopenharmony_ci * @access_key: access key to mach the range's storage keys against
92862306a36Sopenharmony_ci *
92962306a36Sopenharmony_ci * Translate a logical range to a series of guest absolute addresses,
93062306a36Sopenharmony_ci * such that the concatenation of page fragments starting at each gpa make up
93162306a36Sopenharmony_ci * the whole range.
93262306a36Sopenharmony_ci * The translation is performed as if done by the cpu for the given @asce, @ar,
93362306a36Sopenharmony_ci * @mode and state of the @vcpu.
93462306a36Sopenharmony_ci * If the translation causes an exception, its program interruption code is
93562306a36Sopenharmony_ci * returned and the &struct kvm_s390_pgm_info pgm member of @vcpu is modified
93662306a36Sopenharmony_ci * such that a subsequent call to kvm_s390_inject_prog_vcpu() will inject
93762306a36Sopenharmony_ci * a correct exception into the guest.
93862306a36Sopenharmony_ci * The resulting gpas are stored into @gpas, unless it is NULL.
93962306a36Sopenharmony_ci *
94062306a36Sopenharmony_ci * Note: All fragments except the first one start at the beginning of a page.
94162306a36Sopenharmony_ci *	 When deriving the boundaries of a fragment from a gpa, all but the last
94262306a36Sopenharmony_ci *	 fragment end at the end of the page.
94362306a36Sopenharmony_ci *
94462306a36Sopenharmony_ci * Return:
94562306a36Sopenharmony_ci * * 0		- success
94662306a36Sopenharmony_ci * * <0		- translation could not be performed, for example if  guest
94762306a36Sopenharmony_ci *		  memory could not be accessed
94862306a36Sopenharmony_ci * * >0		- an access exception occurred. In this case the returned value
94962306a36Sopenharmony_ci *		  is the program interruption code and the contents of pgm may
95062306a36Sopenharmony_ci *		  be used to inject an exception into the guest.
95162306a36Sopenharmony_ci */
95262306a36Sopenharmony_cistatic int guest_range_to_gpas(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar,
95362306a36Sopenharmony_ci			       unsigned long *gpas, unsigned long len,
95462306a36Sopenharmony_ci			       const union asce asce, enum gacc_mode mode,
95562306a36Sopenharmony_ci			       u8 access_key)
95662306a36Sopenharmony_ci{
95762306a36Sopenharmony_ci	psw_t *psw = &vcpu->arch.sie_block->gpsw;
95862306a36Sopenharmony_ci	unsigned int offset = offset_in_page(ga);
95962306a36Sopenharmony_ci	unsigned int fragment_len;
96062306a36Sopenharmony_ci	int lap_enabled, rc = 0;
96162306a36Sopenharmony_ci	enum prot_type prot;
96262306a36Sopenharmony_ci	unsigned long gpa;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	lap_enabled = low_address_protection_enabled(vcpu, asce);
96562306a36Sopenharmony_ci	while (min(PAGE_SIZE - offset, len) > 0) {
96662306a36Sopenharmony_ci		fragment_len = min(PAGE_SIZE - offset, len);
96762306a36Sopenharmony_ci		ga = kvm_s390_logical_to_effective(vcpu, ga);
96862306a36Sopenharmony_ci		if (mode == GACC_STORE && lap_enabled && is_low_address(ga))
96962306a36Sopenharmony_ci			return trans_exc(vcpu, PGM_PROTECTION, ga, ar, mode,
97062306a36Sopenharmony_ci					 PROT_TYPE_LA);
97162306a36Sopenharmony_ci		if (psw_bits(*psw).dat) {
97262306a36Sopenharmony_ci			rc = guest_translate(vcpu, ga, &gpa, asce, mode, &prot);
97362306a36Sopenharmony_ci			if (rc < 0)
97462306a36Sopenharmony_ci				return rc;
97562306a36Sopenharmony_ci		} else {
97662306a36Sopenharmony_ci			gpa = kvm_s390_real_to_abs(vcpu, ga);
97762306a36Sopenharmony_ci			if (kvm_is_error_gpa(vcpu->kvm, gpa)) {
97862306a36Sopenharmony_ci				rc = PGM_ADDRESSING;
97962306a36Sopenharmony_ci				prot = PROT_NONE;
98062306a36Sopenharmony_ci			}
98162306a36Sopenharmony_ci		}
98262306a36Sopenharmony_ci		if (rc)
98362306a36Sopenharmony_ci			return trans_exc(vcpu, rc, ga, ar, mode, prot);
98462306a36Sopenharmony_ci		rc = vcpu_check_access_key(vcpu, access_key, mode, asce, gpa, ga,
98562306a36Sopenharmony_ci					   fragment_len);
98662306a36Sopenharmony_ci		if (rc)
98762306a36Sopenharmony_ci			return trans_exc(vcpu, rc, ga, ar, mode, PROT_TYPE_KEYC);
98862306a36Sopenharmony_ci		if (gpas)
98962306a36Sopenharmony_ci			*gpas++ = gpa;
99062306a36Sopenharmony_ci		offset = 0;
99162306a36Sopenharmony_ci		ga += fragment_len;
99262306a36Sopenharmony_ci		len -= fragment_len;
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci	return 0;
99562306a36Sopenharmony_ci}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_cistatic int access_guest_page(struct kvm *kvm, enum gacc_mode mode, gpa_t gpa,
99862306a36Sopenharmony_ci			     void *data, unsigned int len)
99962306a36Sopenharmony_ci{
100062306a36Sopenharmony_ci	const unsigned int offset = offset_in_page(gpa);
100162306a36Sopenharmony_ci	const gfn_t gfn = gpa_to_gfn(gpa);
100262306a36Sopenharmony_ci	int rc;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	if (mode == GACC_STORE)
100562306a36Sopenharmony_ci		rc = kvm_write_guest_page(kvm, gfn, data, offset, len);
100662306a36Sopenharmony_ci	else
100762306a36Sopenharmony_ci		rc = kvm_read_guest_page(kvm, gfn, data, offset, len);
100862306a36Sopenharmony_ci	return rc;
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_cistatic int
101262306a36Sopenharmony_ciaccess_guest_page_with_key(struct kvm *kvm, enum gacc_mode mode, gpa_t gpa,
101362306a36Sopenharmony_ci			   void *data, unsigned int len, u8 access_key)
101462306a36Sopenharmony_ci{
101562306a36Sopenharmony_ci	struct kvm_memory_slot *slot;
101662306a36Sopenharmony_ci	bool writable;
101762306a36Sopenharmony_ci	gfn_t gfn;
101862306a36Sopenharmony_ci	hva_t hva;
101962306a36Sopenharmony_ci	int rc;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	gfn = gpa >> PAGE_SHIFT;
102262306a36Sopenharmony_ci	slot = gfn_to_memslot(kvm, gfn);
102362306a36Sopenharmony_ci	hva = gfn_to_hva_memslot_prot(slot, gfn, &writable);
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	if (kvm_is_error_hva(hva))
102662306a36Sopenharmony_ci		return PGM_ADDRESSING;
102762306a36Sopenharmony_ci	/*
102862306a36Sopenharmony_ci	 * Check if it's a ro memslot, even tho that can't occur (they're unsupported).
102962306a36Sopenharmony_ci	 * Don't try to actually handle that case.
103062306a36Sopenharmony_ci	 */
103162306a36Sopenharmony_ci	if (!writable && mode == GACC_STORE)
103262306a36Sopenharmony_ci		return -EOPNOTSUPP;
103362306a36Sopenharmony_ci	hva += offset_in_page(gpa);
103462306a36Sopenharmony_ci	if (mode == GACC_STORE)
103562306a36Sopenharmony_ci		rc = copy_to_user_key((void __user *)hva, data, len, access_key);
103662306a36Sopenharmony_ci	else
103762306a36Sopenharmony_ci		rc = copy_from_user_key(data, (void __user *)hva, len, access_key);
103862306a36Sopenharmony_ci	if (rc)
103962306a36Sopenharmony_ci		return PGM_PROTECTION;
104062306a36Sopenharmony_ci	if (mode == GACC_STORE)
104162306a36Sopenharmony_ci		mark_page_dirty_in_slot(kvm, slot, gfn);
104262306a36Sopenharmony_ci	return 0;
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ciint access_guest_abs_with_key(struct kvm *kvm, gpa_t gpa, void *data,
104662306a36Sopenharmony_ci			      unsigned long len, enum gacc_mode mode, u8 access_key)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	int offset = offset_in_page(gpa);
104962306a36Sopenharmony_ci	int fragment_len;
105062306a36Sopenharmony_ci	int rc;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	while (min(PAGE_SIZE - offset, len) > 0) {
105362306a36Sopenharmony_ci		fragment_len = min(PAGE_SIZE - offset, len);
105462306a36Sopenharmony_ci		rc = access_guest_page_with_key(kvm, mode, gpa, data, fragment_len, access_key);
105562306a36Sopenharmony_ci		if (rc)
105662306a36Sopenharmony_ci			return rc;
105762306a36Sopenharmony_ci		offset = 0;
105862306a36Sopenharmony_ci		len -= fragment_len;
105962306a36Sopenharmony_ci		data += fragment_len;
106062306a36Sopenharmony_ci		gpa += fragment_len;
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci	return 0;
106362306a36Sopenharmony_ci}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ciint access_guest_with_key(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar,
106662306a36Sopenharmony_ci			  void *data, unsigned long len, enum gacc_mode mode,
106762306a36Sopenharmony_ci			  u8 access_key)
106862306a36Sopenharmony_ci{
106962306a36Sopenharmony_ci	psw_t *psw = &vcpu->arch.sie_block->gpsw;
107062306a36Sopenharmony_ci	unsigned long nr_pages, idx;
107162306a36Sopenharmony_ci	unsigned long gpa_array[2];
107262306a36Sopenharmony_ci	unsigned int fragment_len;
107362306a36Sopenharmony_ci	unsigned long *gpas;
107462306a36Sopenharmony_ci	enum prot_type prot;
107562306a36Sopenharmony_ci	int need_ipte_lock;
107662306a36Sopenharmony_ci	union asce asce;
107762306a36Sopenharmony_ci	bool try_storage_prot_override;
107862306a36Sopenharmony_ci	bool try_fetch_prot_override;
107962306a36Sopenharmony_ci	int rc;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	if (!len)
108262306a36Sopenharmony_ci		return 0;
108362306a36Sopenharmony_ci	ga = kvm_s390_logical_to_effective(vcpu, ga);
108462306a36Sopenharmony_ci	rc = get_vcpu_asce(vcpu, &asce, ga, ar, mode);
108562306a36Sopenharmony_ci	if (rc)
108662306a36Sopenharmony_ci		return rc;
108762306a36Sopenharmony_ci	nr_pages = (((ga & ~PAGE_MASK) + len - 1) >> PAGE_SHIFT) + 1;
108862306a36Sopenharmony_ci	gpas = gpa_array;
108962306a36Sopenharmony_ci	if (nr_pages > ARRAY_SIZE(gpa_array))
109062306a36Sopenharmony_ci		gpas = vmalloc(array_size(nr_pages, sizeof(unsigned long)));
109162306a36Sopenharmony_ci	if (!gpas)
109262306a36Sopenharmony_ci		return -ENOMEM;
109362306a36Sopenharmony_ci	try_fetch_prot_override = fetch_prot_override_applicable(vcpu, mode, asce);
109462306a36Sopenharmony_ci	try_storage_prot_override = storage_prot_override_applicable(vcpu);
109562306a36Sopenharmony_ci	need_ipte_lock = psw_bits(*psw).dat && !asce.r;
109662306a36Sopenharmony_ci	if (need_ipte_lock)
109762306a36Sopenharmony_ci		ipte_lock(vcpu->kvm);
109862306a36Sopenharmony_ci	/*
109962306a36Sopenharmony_ci	 * Since we do the access further down ultimately via a move instruction
110062306a36Sopenharmony_ci	 * that does key checking and returns an error in case of a protection
110162306a36Sopenharmony_ci	 * violation, we don't need to do the check during address translation.
110262306a36Sopenharmony_ci	 * Skip it by passing access key 0, which matches any storage key,
110362306a36Sopenharmony_ci	 * obviating the need for any further checks. As a result the check is
110462306a36Sopenharmony_ci	 * handled entirely in hardware on access, we only need to take care to
110562306a36Sopenharmony_ci	 * forego key protection checking if fetch protection override applies or
110662306a36Sopenharmony_ci	 * retry with the special key 9 in case of storage protection override.
110762306a36Sopenharmony_ci	 */
110862306a36Sopenharmony_ci	rc = guest_range_to_gpas(vcpu, ga, ar, gpas, len, asce, mode, 0);
110962306a36Sopenharmony_ci	if (rc)
111062306a36Sopenharmony_ci		goto out_unlock;
111162306a36Sopenharmony_ci	for (idx = 0; idx < nr_pages; idx++) {
111262306a36Sopenharmony_ci		fragment_len = min(PAGE_SIZE - offset_in_page(gpas[idx]), len);
111362306a36Sopenharmony_ci		if (try_fetch_prot_override && fetch_prot_override_applies(ga, fragment_len)) {
111462306a36Sopenharmony_ci			rc = access_guest_page(vcpu->kvm, mode, gpas[idx],
111562306a36Sopenharmony_ci					       data, fragment_len);
111662306a36Sopenharmony_ci		} else {
111762306a36Sopenharmony_ci			rc = access_guest_page_with_key(vcpu->kvm, mode, gpas[idx],
111862306a36Sopenharmony_ci							data, fragment_len, access_key);
111962306a36Sopenharmony_ci		}
112062306a36Sopenharmony_ci		if (rc == PGM_PROTECTION && try_storage_prot_override)
112162306a36Sopenharmony_ci			rc = access_guest_page_with_key(vcpu->kvm, mode, gpas[idx],
112262306a36Sopenharmony_ci							data, fragment_len, PAGE_SPO_ACC);
112362306a36Sopenharmony_ci		if (rc)
112462306a36Sopenharmony_ci			break;
112562306a36Sopenharmony_ci		len -= fragment_len;
112662306a36Sopenharmony_ci		data += fragment_len;
112762306a36Sopenharmony_ci		ga = kvm_s390_logical_to_effective(vcpu, ga + fragment_len);
112862306a36Sopenharmony_ci	}
112962306a36Sopenharmony_ci	if (rc > 0) {
113062306a36Sopenharmony_ci		bool terminate = (mode == GACC_STORE) && (idx > 0);
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci		if (rc == PGM_PROTECTION)
113362306a36Sopenharmony_ci			prot = PROT_TYPE_KEYC;
113462306a36Sopenharmony_ci		else
113562306a36Sopenharmony_ci			prot = PROT_NONE;
113662306a36Sopenharmony_ci		rc = trans_exc_ending(vcpu, rc, ga, ar, mode, prot, terminate);
113762306a36Sopenharmony_ci	}
113862306a36Sopenharmony_ciout_unlock:
113962306a36Sopenharmony_ci	if (need_ipte_lock)
114062306a36Sopenharmony_ci		ipte_unlock(vcpu->kvm);
114162306a36Sopenharmony_ci	if (nr_pages > ARRAY_SIZE(gpa_array))
114262306a36Sopenharmony_ci		vfree(gpas);
114362306a36Sopenharmony_ci	return rc;
114462306a36Sopenharmony_ci}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ciint access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
114762306a36Sopenharmony_ci		      void *data, unsigned long len, enum gacc_mode mode)
114862306a36Sopenharmony_ci{
114962306a36Sopenharmony_ci	unsigned int fragment_len;
115062306a36Sopenharmony_ci	unsigned long gpa;
115162306a36Sopenharmony_ci	int rc = 0;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	while (len && !rc) {
115462306a36Sopenharmony_ci		gpa = kvm_s390_real_to_abs(vcpu, gra);
115562306a36Sopenharmony_ci		fragment_len = min(PAGE_SIZE - offset_in_page(gpa), len);
115662306a36Sopenharmony_ci		rc = access_guest_page(vcpu->kvm, mode, gpa, data, fragment_len);
115762306a36Sopenharmony_ci		len -= fragment_len;
115862306a36Sopenharmony_ci		gra += fragment_len;
115962306a36Sopenharmony_ci		data += fragment_len;
116062306a36Sopenharmony_ci	}
116162306a36Sopenharmony_ci	return rc;
116262306a36Sopenharmony_ci}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci/**
116562306a36Sopenharmony_ci * cmpxchg_guest_abs_with_key() - Perform cmpxchg on guest absolute address.
116662306a36Sopenharmony_ci * @kvm: Virtual machine instance.
116762306a36Sopenharmony_ci * @gpa: Absolute guest address of the location to be changed.
116862306a36Sopenharmony_ci * @len: Operand length of the cmpxchg, required: 1 <= len <= 16. Providing a
116962306a36Sopenharmony_ci *       non power of two will result in failure.
117062306a36Sopenharmony_ci * @old_addr: Pointer to old value. If the location at @gpa contains this value,
117162306a36Sopenharmony_ci *            the exchange will succeed. After calling cmpxchg_guest_abs_with_key()
117262306a36Sopenharmony_ci *            *@old_addr contains the value at @gpa before the attempt to
117362306a36Sopenharmony_ci *            exchange the value.
117462306a36Sopenharmony_ci * @new: The value to place at @gpa.
117562306a36Sopenharmony_ci * @access_key: The access key to use for the guest access.
117662306a36Sopenharmony_ci * @success: output value indicating if an exchange occurred.
117762306a36Sopenharmony_ci *
117862306a36Sopenharmony_ci * Atomically exchange the value at @gpa by @new, if it contains *@old.
117962306a36Sopenharmony_ci * Honors storage keys.
118062306a36Sopenharmony_ci *
118162306a36Sopenharmony_ci * Return: * 0: successful exchange
118262306a36Sopenharmony_ci *         * >0: a program interruption code indicating the reason cmpxchg could
118362306a36Sopenharmony_ci *               not be attempted
118462306a36Sopenharmony_ci *         * -EINVAL: address misaligned or len not power of two
118562306a36Sopenharmony_ci *         * -EAGAIN: transient failure (len 1 or 2)
118662306a36Sopenharmony_ci *         * -EOPNOTSUPP: read-only memslot (should never occur)
118762306a36Sopenharmony_ci */
118862306a36Sopenharmony_ciint cmpxchg_guest_abs_with_key(struct kvm *kvm, gpa_t gpa, int len,
118962306a36Sopenharmony_ci			       __uint128_t *old_addr, __uint128_t new,
119062306a36Sopenharmony_ci			       u8 access_key, bool *success)
119162306a36Sopenharmony_ci{
119262306a36Sopenharmony_ci	gfn_t gfn = gpa_to_gfn(gpa);
119362306a36Sopenharmony_ci	struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn);
119462306a36Sopenharmony_ci	bool writable;
119562306a36Sopenharmony_ci	hva_t hva;
119662306a36Sopenharmony_ci	int ret;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	if (!IS_ALIGNED(gpa, len))
119962306a36Sopenharmony_ci		return -EINVAL;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	hva = gfn_to_hva_memslot_prot(slot, gfn, &writable);
120262306a36Sopenharmony_ci	if (kvm_is_error_hva(hva))
120362306a36Sopenharmony_ci		return PGM_ADDRESSING;
120462306a36Sopenharmony_ci	/*
120562306a36Sopenharmony_ci	 * Check if it's a read-only memslot, even though that cannot occur
120662306a36Sopenharmony_ci	 * since those are unsupported.
120762306a36Sopenharmony_ci	 * Don't try to actually handle that case.
120862306a36Sopenharmony_ci	 */
120962306a36Sopenharmony_ci	if (!writable)
121062306a36Sopenharmony_ci		return -EOPNOTSUPP;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	hva += offset_in_page(gpa);
121362306a36Sopenharmony_ci	/*
121462306a36Sopenharmony_ci	 * The cmpxchg_user_key macro depends on the type of "old", so we need
121562306a36Sopenharmony_ci	 * a case for each valid length and get some code duplication as long
121662306a36Sopenharmony_ci	 * as we don't introduce a new macro.
121762306a36Sopenharmony_ci	 */
121862306a36Sopenharmony_ci	switch (len) {
121962306a36Sopenharmony_ci	case 1: {
122062306a36Sopenharmony_ci		u8 old;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci		ret = cmpxchg_user_key((u8 __user *)hva, &old, *old_addr, new, access_key);
122362306a36Sopenharmony_ci		*success = !ret && old == *old_addr;
122462306a36Sopenharmony_ci		*old_addr = old;
122562306a36Sopenharmony_ci		break;
122662306a36Sopenharmony_ci	}
122762306a36Sopenharmony_ci	case 2: {
122862306a36Sopenharmony_ci		u16 old;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci		ret = cmpxchg_user_key((u16 __user *)hva, &old, *old_addr, new, access_key);
123162306a36Sopenharmony_ci		*success = !ret && old == *old_addr;
123262306a36Sopenharmony_ci		*old_addr = old;
123362306a36Sopenharmony_ci		break;
123462306a36Sopenharmony_ci	}
123562306a36Sopenharmony_ci	case 4: {
123662306a36Sopenharmony_ci		u32 old;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci		ret = cmpxchg_user_key((u32 __user *)hva, &old, *old_addr, new, access_key);
123962306a36Sopenharmony_ci		*success = !ret && old == *old_addr;
124062306a36Sopenharmony_ci		*old_addr = old;
124162306a36Sopenharmony_ci		break;
124262306a36Sopenharmony_ci	}
124362306a36Sopenharmony_ci	case 8: {
124462306a36Sopenharmony_ci		u64 old;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci		ret = cmpxchg_user_key((u64 __user *)hva, &old, *old_addr, new, access_key);
124762306a36Sopenharmony_ci		*success = !ret && old == *old_addr;
124862306a36Sopenharmony_ci		*old_addr = old;
124962306a36Sopenharmony_ci		break;
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci	case 16: {
125262306a36Sopenharmony_ci		__uint128_t old;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci		ret = cmpxchg_user_key((__uint128_t __user *)hva, &old, *old_addr, new, access_key);
125562306a36Sopenharmony_ci		*success = !ret && old == *old_addr;
125662306a36Sopenharmony_ci		*old_addr = old;
125762306a36Sopenharmony_ci		break;
125862306a36Sopenharmony_ci	}
125962306a36Sopenharmony_ci	default:
126062306a36Sopenharmony_ci		return -EINVAL;
126162306a36Sopenharmony_ci	}
126262306a36Sopenharmony_ci	if (*success)
126362306a36Sopenharmony_ci		mark_page_dirty_in_slot(kvm, slot, gfn);
126462306a36Sopenharmony_ci	/*
126562306a36Sopenharmony_ci	 * Assume that the fault is caused by protection, either key protection
126662306a36Sopenharmony_ci	 * or user page write protection.
126762306a36Sopenharmony_ci	 */
126862306a36Sopenharmony_ci	if (ret == -EFAULT)
126962306a36Sopenharmony_ci		ret = PGM_PROTECTION;
127062306a36Sopenharmony_ci	return ret;
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci/**
127462306a36Sopenharmony_ci * guest_translate_address_with_key - translate guest logical into guest absolute address
127562306a36Sopenharmony_ci * @vcpu: virtual cpu
127662306a36Sopenharmony_ci * @gva: Guest virtual address
127762306a36Sopenharmony_ci * @ar: Access register
127862306a36Sopenharmony_ci * @gpa: Guest physical address
127962306a36Sopenharmony_ci * @mode: Translation access mode
128062306a36Sopenharmony_ci * @access_key: access key to mach the storage key with
128162306a36Sopenharmony_ci *
128262306a36Sopenharmony_ci * Parameter semantics are the same as the ones from guest_translate.
128362306a36Sopenharmony_ci * The memory contents at the guest address are not changed.
128462306a36Sopenharmony_ci *
128562306a36Sopenharmony_ci * Note: The IPTE lock is not taken during this function, so the caller
128662306a36Sopenharmony_ci * has to take care of this.
128762306a36Sopenharmony_ci */
128862306a36Sopenharmony_ciint guest_translate_address_with_key(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
128962306a36Sopenharmony_ci				     unsigned long *gpa, enum gacc_mode mode,
129062306a36Sopenharmony_ci				     u8 access_key)
129162306a36Sopenharmony_ci{
129262306a36Sopenharmony_ci	union asce asce;
129362306a36Sopenharmony_ci	int rc;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	gva = kvm_s390_logical_to_effective(vcpu, gva);
129662306a36Sopenharmony_ci	rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode);
129762306a36Sopenharmony_ci	if (rc)
129862306a36Sopenharmony_ci		return rc;
129962306a36Sopenharmony_ci	return guest_range_to_gpas(vcpu, gva, ar, gpa, 1, asce, mode,
130062306a36Sopenharmony_ci				   access_key);
130162306a36Sopenharmony_ci}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci/**
130462306a36Sopenharmony_ci * check_gva_range - test a range of guest virtual addresses for accessibility
130562306a36Sopenharmony_ci * @vcpu: virtual cpu
130662306a36Sopenharmony_ci * @gva: Guest virtual address
130762306a36Sopenharmony_ci * @ar: Access register
130862306a36Sopenharmony_ci * @length: Length of test range
130962306a36Sopenharmony_ci * @mode: Translation access mode
131062306a36Sopenharmony_ci * @access_key: access key to mach the storage keys with
131162306a36Sopenharmony_ci */
131262306a36Sopenharmony_ciint check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
131362306a36Sopenharmony_ci		    unsigned long length, enum gacc_mode mode, u8 access_key)
131462306a36Sopenharmony_ci{
131562306a36Sopenharmony_ci	union asce asce;
131662306a36Sopenharmony_ci	int rc = 0;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode);
131962306a36Sopenharmony_ci	if (rc)
132062306a36Sopenharmony_ci		return rc;
132162306a36Sopenharmony_ci	ipte_lock(vcpu->kvm);
132262306a36Sopenharmony_ci	rc = guest_range_to_gpas(vcpu, gva, ar, NULL, length, asce, mode,
132362306a36Sopenharmony_ci				 access_key);
132462306a36Sopenharmony_ci	ipte_unlock(vcpu->kvm);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	return rc;
132762306a36Sopenharmony_ci}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci/**
133062306a36Sopenharmony_ci * check_gpa_range - test a range of guest physical addresses for accessibility
133162306a36Sopenharmony_ci * @kvm: virtual machine instance
133262306a36Sopenharmony_ci * @gpa: guest physical address
133362306a36Sopenharmony_ci * @length: length of test range
133462306a36Sopenharmony_ci * @mode: access mode to test, relevant for storage keys
133562306a36Sopenharmony_ci * @access_key: access key to mach the storage keys with
133662306a36Sopenharmony_ci */
133762306a36Sopenharmony_ciint check_gpa_range(struct kvm *kvm, unsigned long gpa, unsigned long length,
133862306a36Sopenharmony_ci		    enum gacc_mode mode, u8 access_key)
133962306a36Sopenharmony_ci{
134062306a36Sopenharmony_ci	unsigned int fragment_len;
134162306a36Sopenharmony_ci	int rc = 0;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	while (length && !rc) {
134462306a36Sopenharmony_ci		fragment_len = min(PAGE_SIZE - offset_in_page(gpa), length);
134562306a36Sopenharmony_ci		rc = vm_check_access_key(kvm, access_key, mode, gpa);
134662306a36Sopenharmony_ci		length -= fragment_len;
134762306a36Sopenharmony_ci		gpa += fragment_len;
134862306a36Sopenharmony_ci	}
134962306a36Sopenharmony_ci	return rc;
135062306a36Sopenharmony_ci}
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci/**
135362306a36Sopenharmony_ci * kvm_s390_check_low_addr_prot_real - check for low-address protection
135462306a36Sopenharmony_ci * @vcpu: virtual cpu
135562306a36Sopenharmony_ci * @gra: Guest real address
135662306a36Sopenharmony_ci *
135762306a36Sopenharmony_ci * Checks whether an address is subject to low-address protection and set
135862306a36Sopenharmony_ci * up vcpu->arch.pgm accordingly if necessary.
135962306a36Sopenharmony_ci *
136062306a36Sopenharmony_ci * Return: 0 if no protection exception, or PGM_PROTECTION if protected.
136162306a36Sopenharmony_ci */
136262306a36Sopenharmony_ciint kvm_s390_check_low_addr_prot_real(struct kvm_vcpu *vcpu, unsigned long gra)
136362306a36Sopenharmony_ci{
136462306a36Sopenharmony_ci	union ctlreg0 ctlreg0 = {.val = vcpu->arch.sie_block->gcr[0]};
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	if (!ctlreg0.lap || !is_low_address(gra))
136762306a36Sopenharmony_ci		return 0;
136862306a36Sopenharmony_ci	return trans_exc(vcpu, PGM_PROTECTION, gra, 0, GACC_STORE, PROT_TYPE_LA);
136962306a36Sopenharmony_ci}
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci/**
137262306a36Sopenharmony_ci * kvm_s390_shadow_tables - walk the guest page table and create shadow tables
137362306a36Sopenharmony_ci * @sg: pointer to the shadow guest address space structure
137462306a36Sopenharmony_ci * @saddr: faulting address in the shadow gmap
137562306a36Sopenharmony_ci * @pgt: pointer to the beginning of the page table for the given address if
137662306a36Sopenharmony_ci *	 successful (return value 0), or to the first invalid DAT entry in
137762306a36Sopenharmony_ci *	 case of exceptions (return value > 0)
137862306a36Sopenharmony_ci * @dat_protection: referenced memory is write protected
137962306a36Sopenharmony_ci * @fake: pgt references contiguous guest memory block, not a pgtable
138062306a36Sopenharmony_ci */
138162306a36Sopenharmony_cistatic int kvm_s390_shadow_tables(struct gmap *sg, unsigned long saddr,
138262306a36Sopenharmony_ci				  unsigned long *pgt, int *dat_protection,
138362306a36Sopenharmony_ci				  int *fake)
138462306a36Sopenharmony_ci{
138562306a36Sopenharmony_ci	struct kvm *kvm;
138662306a36Sopenharmony_ci	struct gmap *parent;
138762306a36Sopenharmony_ci	union asce asce;
138862306a36Sopenharmony_ci	union vaddress vaddr;
138962306a36Sopenharmony_ci	unsigned long ptr;
139062306a36Sopenharmony_ci	int rc;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	*fake = 0;
139362306a36Sopenharmony_ci	*dat_protection = 0;
139462306a36Sopenharmony_ci	kvm = sg->private;
139562306a36Sopenharmony_ci	parent = sg->parent;
139662306a36Sopenharmony_ci	vaddr.addr = saddr;
139762306a36Sopenharmony_ci	asce.val = sg->orig_asce;
139862306a36Sopenharmony_ci	ptr = asce.origin * PAGE_SIZE;
139962306a36Sopenharmony_ci	if (asce.r) {
140062306a36Sopenharmony_ci		*fake = 1;
140162306a36Sopenharmony_ci		ptr = 0;
140262306a36Sopenharmony_ci		asce.dt = ASCE_TYPE_REGION1;
140362306a36Sopenharmony_ci	}
140462306a36Sopenharmony_ci	switch (asce.dt) {
140562306a36Sopenharmony_ci	case ASCE_TYPE_REGION1:
140662306a36Sopenharmony_ci		if (vaddr.rfx01 > asce.tl && !*fake)
140762306a36Sopenharmony_ci			return PGM_REGION_FIRST_TRANS;
140862306a36Sopenharmony_ci		break;
140962306a36Sopenharmony_ci	case ASCE_TYPE_REGION2:
141062306a36Sopenharmony_ci		if (vaddr.rfx)
141162306a36Sopenharmony_ci			return PGM_ASCE_TYPE;
141262306a36Sopenharmony_ci		if (vaddr.rsx01 > asce.tl)
141362306a36Sopenharmony_ci			return PGM_REGION_SECOND_TRANS;
141462306a36Sopenharmony_ci		break;
141562306a36Sopenharmony_ci	case ASCE_TYPE_REGION3:
141662306a36Sopenharmony_ci		if (vaddr.rfx || vaddr.rsx)
141762306a36Sopenharmony_ci			return PGM_ASCE_TYPE;
141862306a36Sopenharmony_ci		if (vaddr.rtx01 > asce.tl)
141962306a36Sopenharmony_ci			return PGM_REGION_THIRD_TRANS;
142062306a36Sopenharmony_ci		break;
142162306a36Sopenharmony_ci	case ASCE_TYPE_SEGMENT:
142262306a36Sopenharmony_ci		if (vaddr.rfx || vaddr.rsx || vaddr.rtx)
142362306a36Sopenharmony_ci			return PGM_ASCE_TYPE;
142462306a36Sopenharmony_ci		if (vaddr.sx01 > asce.tl)
142562306a36Sopenharmony_ci			return PGM_SEGMENT_TRANSLATION;
142662306a36Sopenharmony_ci		break;
142762306a36Sopenharmony_ci	}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	switch (asce.dt) {
143062306a36Sopenharmony_ci	case ASCE_TYPE_REGION1: {
143162306a36Sopenharmony_ci		union region1_table_entry rfte;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci		if (*fake) {
143462306a36Sopenharmony_ci			ptr += vaddr.rfx * _REGION1_SIZE;
143562306a36Sopenharmony_ci			rfte.val = ptr;
143662306a36Sopenharmony_ci			goto shadow_r2t;
143762306a36Sopenharmony_ci		}
143862306a36Sopenharmony_ci		*pgt = ptr + vaddr.rfx * 8;
143962306a36Sopenharmony_ci		rc = gmap_read_table(parent, ptr + vaddr.rfx * 8, &rfte.val);
144062306a36Sopenharmony_ci		if (rc)
144162306a36Sopenharmony_ci			return rc;
144262306a36Sopenharmony_ci		if (rfte.i)
144362306a36Sopenharmony_ci			return PGM_REGION_FIRST_TRANS;
144462306a36Sopenharmony_ci		if (rfte.tt != TABLE_TYPE_REGION1)
144562306a36Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
144662306a36Sopenharmony_ci		if (vaddr.rsx01 < rfte.tf || vaddr.rsx01 > rfte.tl)
144762306a36Sopenharmony_ci			return PGM_REGION_SECOND_TRANS;
144862306a36Sopenharmony_ci		if (sg->edat_level >= 1)
144962306a36Sopenharmony_ci			*dat_protection |= rfte.p;
145062306a36Sopenharmony_ci		ptr = rfte.rto * PAGE_SIZE;
145162306a36Sopenharmony_cishadow_r2t:
145262306a36Sopenharmony_ci		rc = gmap_shadow_r2t(sg, saddr, rfte.val, *fake);
145362306a36Sopenharmony_ci		if (rc)
145462306a36Sopenharmony_ci			return rc;
145562306a36Sopenharmony_ci		kvm->stat.gmap_shadow_r1_entry++;
145662306a36Sopenharmony_ci	}
145762306a36Sopenharmony_ci		fallthrough;
145862306a36Sopenharmony_ci	case ASCE_TYPE_REGION2: {
145962306a36Sopenharmony_ci		union region2_table_entry rste;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci		if (*fake) {
146262306a36Sopenharmony_ci			ptr += vaddr.rsx * _REGION2_SIZE;
146362306a36Sopenharmony_ci			rste.val = ptr;
146462306a36Sopenharmony_ci			goto shadow_r3t;
146562306a36Sopenharmony_ci		}
146662306a36Sopenharmony_ci		*pgt = ptr + vaddr.rsx * 8;
146762306a36Sopenharmony_ci		rc = gmap_read_table(parent, ptr + vaddr.rsx * 8, &rste.val);
146862306a36Sopenharmony_ci		if (rc)
146962306a36Sopenharmony_ci			return rc;
147062306a36Sopenharmony_ci		if (rste.i)
147162306a36Sopenharmony_ci			return PGM_REGION_SECOND_TRANS;
147262306a36Sopenharmony_ci		if (rste.tt != TABLE_TYPE_REGION2)
147362306a36Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
147462306a36Sopenharmony_ci		if (vaddr.rtx01 < rste.tf || vaddr.rtx01 > rste.tl)
147562306a36Sopenharmony_ci			return PGM_REGION_THIRD_TRANS;
147662306a36Sopenharmony_ci		if (sg->edat_level >= 1)
147762306a36Sopenharmony_ci			*dat_protection |= rste.p;
147862306a36Sopenharmony_ci		ptr = rste.rto * PAGE_SIZE;
147962306a36Sopenharmony_cishadow_r3t:
148062306a36Sopenharmony_ci		rste.p |= *dat_protection;
148162306a36Sopenharmony_ci		rc = gmap_shadow_r3t(sg, saddr, rste.val, *fake);
148262306a36Sopenharmony_ci		if (rc)
148362306a36Sopenharmony_ci			return rc;
148462306a36Sopenharmony_ci		kvm->stat.gmap_shadow_r2_entry++;
148562306a36Sopenharmony_ci	}
148662306a36Sopenharmony_ci		fallthrough;
148762306a36Sopenharmony_ci	case ASCE_TYPE_REGION3: {
148862306a36Sopenharmony_ci		union region3_table_entry rtte;
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci		if (*fake) {
149162306a36Sopenharmony_ci			ptr += vaddr.rtx * _REGION3_SIZE;
149262306a36Sopenharmony_ci			rtte.val = ptr;
149362306a36Sopenharmony_ci			goto shadow_sgt;
149462306a36Sopenharmony_ci		}
149562306a36Sopenharmony_ci		*pgt = ptr + vaddr.rtx * 8;
149662306a36Sopenharmony_ci		rc = gmap_read_table(parent, ptr + vaddr.rtx * 8, &rtte.val);
149762306a36Sopenharmony_ci		if (rc)
149862306a36Sopenharmony_ci			return rc;
149962306a36Sopenharmony_ci		if (rtte.i)
150062306a36Sopenharmony_ci			return PGM_REGION_THIRD_TRANS;
150162306a36Sopenharmony_ci		if (rtte.tt != TABLE_TYPE_REGION3)
150262306a36Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
150362306a36Sopenharmony_ci		if (rtte.cr && asce.p && sg->edat_level >= 2)
150462306a36Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
150562306a36Sopenharmony_ci		if (rtte.fc && sg->edat_level >= 2) {
150662306a36Sopenharmony_ci			*dat_protection |= rtte.fc0.p;
150762306a36Sopenharmony_ci			*fake = 1;
150862306a36Sopenharmony_ci			ptr = rtte.fc1.rfaa * _REGION3_SIZE;
150962306a36Sopenharmony_ci			rtte.val = ptr;
151062306a36Sopenharmony_ci			goto shadow_sgt;
151162306a36Sopenharmony_ci		}
151262306a36Sopenharmony_ci		if (vaddr.sx01 < rtte.fc0.tf || vaddr.sx01 > rtte.fc0.tl)
151362306a36Sopenharmony_ci			return PGM_SEGMENT_TRANSLATION;
151462306a36Sopenharmony_ci		if (sg->edat_level >= 1)
151562306a36Sopenharmony_ci			*dat_protection |= rtte.fc0.p;
151662306a36Sopenharmony_ci		ptr = rtte.fc0.sto * PAGE_SIZE;
151762306a36Sopenharmony_cishadow_sgt:
151862306a36Sopenharmony_ci		rtte.fc0.p |= *dat_protection;
151962306a36Sopenharmony_ci		rc = gmap_shadow_sgt(sg, saddr, rtte.val, *fake);
152062306a36Sopenharmony_ci		if (rc)
152162306a36Sopenharmony_ci			return rc;
152262306a36Sopenharmony_ci		kvm->stat.gmap_shadow_r3_entry++;
152362306a36Sopenharmony_ci	}
152462306a36Sopenharmony_ci		fallthrough;
152562306a36Sopenharmony_ci	case ASCE_TYPE_SEGMENT: {
152662306a36Sopenharmony_ci		union segment_table_entry ste;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci		if (*fake) {
152962306a36Sopenharmony_ci			ptr += vaddr.sx * _SEGMENT_SIZE;
153062306a36Sopenharmony_ci			ste.val = ptr;
153162306a36Sopenharmony_ci			goto shadow_pgt;
153262306a36Sopenharmony_ci		}
153362306a36Sopenharmony_ci		*pgt = ptr + vaddr.sx * 8;
153462306a36Sopenharmony_ci		rc = gmap_read_table(parent, ptr + vaddr.sx * 8, &ste.val);
153562306a36Sopenharmony_ci		if (rc)
153662306a36Sopenharmony_ci			return rc;
153762306a36Sopenharmony_ci		if (ste.i)
153862306a36Sopenharmony_ci			return PGM_SEGMENT_TRANSLATION;
153962306a36Sopenharmony_ci		if (ste.tt != TABLE_TYPE_SEGMENT)
154062306a36Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
154162306a36Sopenharmony_ci		if (ste.cs && asce.p)
154262306a36Sopenharmony_ci			return PGM_TRANSLATION_SPEC;
154362306a36Sopenharmony_ci		*dat_protection |= ste.fc0.p;
154462306a36Sopenharmony_ci		if (ste.fc && sg->edat_level >= 1) {
154562306a36Sopenharmony_ci			*fake = 1;
154662306a36Sopenharmony_ci			ptr = ste.fc1.sfaa * _SEGMENT_SIZE;
154762306a36Sopenharmony_ci			ste.val = ptr;
154862306a36Sopenharmony_ci			goto shadow_pgt;
154962306a36Sopenharmony_ci		}
155062306a36Sopenharmony_ci		ptr = ste.fc0.pto * (PAGE_SIZE / 2);
155162306a36Sopenharmony_cishadow_pgt:
155262306a36Sopenharmony_ci		ste.fc0.p |= *dat_protection;
155362306a36Sopenharmony_ci		rc = gmap_shadow_pgt(sg, saddr, ste.val, *fake);
155462306a36Sopenharmony_ci		if (rc)
155562306a36Sopenharmony_ci			return rc;
155662306a36Sopenharmony_ci		kvm->stat.gmap_shadow_sg_entry++;
155762306a36Sopenharmony_ci	}
155862306a36Sopenharmony_ci	}
155962306a36Sopenharmony_ci	/* Return the parent address of the page table */
156062306a36Sopenharmony_ci	*pgt = ptr;
156162306a36Sopenharmony_ci	return 0;
156262306a36Sopenharmony_ci}
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci/**
156562306a36Sopenharmony_ci * kvm_s390_shadow_fault - handle fault on a shadow page table
156662306a36Sopenharmony_ci * @vcpu: virtual cpu
156762306a36Sopenharmony_ci * @sg: pointer to the shadow guest address space structure
156862306a36Sopenharmony_ci * @saddr: faulting address in the shadow gmap
156962306a36Sopenharmony_ci * @datptr: will contain the address of the faulting DAT table entry, or of
157062306a36Sopenharmony_ci *	    the valid leaf, plus some flags
157162306a36Sopenharmony_ci *
157262306a36Sopenharmony_ci * Returns: - 0 if the shadow fault was successfully resolved
157362306a36Sopenharmony_ci *	    - > 0 (pgm exception code) on exceptions while faulting
157462306a36Sopenharmony_ci *	    - -EAGAIN if the caller can retry immediately
157562306a36Sopenharmony_ci *	    - -EFAULT when accessing invalid guest addresses
157662306a36Sopenharmony_ci *	    - -ENOMEM if out of memory
157762306a36Sopenharmony_ci */
157862306a36Sopenharmony_ciint kvm_s390_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg,
157962306a36Sopenharmony_ci			  unsigned long saddr, unsigned long *datptr)
158062306a36Sopenharmony_ci{
158162306a36Sopenharmony_ci	union vaddress vaddr;
158262306a36Sopenharmony_ci	union page_table_entry pte;
158362306a36Sopenharmony_ci	unsigned long pgt = 0;
158462306a36Sopenharmony_ci	int dat_protection, fake;
158562306a36Sopenharmony_ci	int rc;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	mmap_read_lock(sg->mm);
158862306a36Sopenharmony_ci	/*
158962306a36Sopenharmony_ci	 * We don't want any guest-2 tables to change - so the parent
159062306a36Sopenharmony_ci	 * tables/pointers we read stay valid - unshadowing is however
159162306a36Sopenharmony_ci	 * always possible - only guest_table_lock protects us.
159262306a36Sopenharmony_ci	 */
159362306a36Sopenharmony_ci	ipte_lock(vcpu->kvm);
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	rc = gmap_shadow_pgt_lookup(sg, saddr, &pgt, &dat_protection, &fake);
159662306a36Sopenharmony_ci	if (rc)
159762306a36Sopenharmony_ci		rc = kvm_s390_shadow_tables(sg, saddr, &pgt, &dat_protection,
159862306a36Sopenharmony_ci					    &fake);
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	vaddr.addr = saddr;
160162306a36Sopenharmony_ci	if (fake) {
160262306a36Sopenharmony_ci		pte.val = pgt + vaddr.px * PAGE_SIZE;
160362306a36Sopenharmony_ci		goto shadow_page;
160462306a36Sopenharmony_ci	}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	switch (rc) {
160762306a36Sopenharmony_ci	case PGM_SEGMENT_TRANSLATION:
160862306a36Sopenharmony_ci	case PGM_REGION_THIRD_TRANS:
160962306a36Sopenharmony_ci	case PGM_REGION_SECOND_TRANS:
161062306a36Sopenharmony_ci	case PGM_REGION_FIRST_TRANS:
161162306a36Sopenharmony_ci		pgt |= PEI_NOT_PTE;
161262306a36Sopenharmony_ci		break;
161362306a36Sopenharmony_ci	case 0:
161462306a36Sopenharmony_ci		pgt += vaddr.px * 8;
161562306a36Sopenharmony_ci		rc = gmap_read_table(sg->parent, pgt, &pte.val);
161662306a36Sopenharmony_ci	}
161762306a36Sopenharmony_ci	if (datptr)
161862306a36Sopenharmony_ci		*datptr = pgt | dat_protection * PEI_DAT_PROT;
161962306a36Sopenharmony_ci	if (!rc && pte.i)
162062306a36Sopenharmony_ci		rc = PGM_PAGE_TRANSLATION;
162162306a36Sopenharmony_ci	if (!rc && pte.z)
162262306a36Sopenharmony_ci		rc = PGM_TRANSLATION_SPEC;
162362306a36Sopenharmony_cishadow_page:
162462306a36Sopenharmony_ci	pte.p |= dat_protection;
162562306a36Sopenharmony_ci	if (!rc)
162662306a36Sopenharmony_ci		rc = gmap_shadow_page(sg, saddr, __pte(pte.val));
162762306a36Sopenharmony_ci	vcpu->kvm->stat.gmap_shadow_pg_entry++;
162862306a36Sopenharmony_ci	ipte_unlock(vcpu->kvm);
162962306a36Sopenharmony_ci	mmap_read_unlock(sg->mm);
163062306a36Sopenharmony_ci	return rc;
163162306a36Sopenharmony_ci}
1632