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