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