diff options
Diffstat (limited to 'arch/s390/kvm/priv.c')
-rw-r--r-- | arch/s390/kvm/priv.c | 56 |
1 files changed, 33 insertions, 23 deletions
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index 6296159ac883..f89c1cd67751 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c | |||
@@ -930,8 +930,9 @@ int kvm_s390_handle_eb(struct kvm_vcpu *vcpu) | |||
930 | static int handle_tprot(struct kvm_vcpu *vcpu) | 930 | static int handle_tprot(struct kvm_vcpu *vcpu) |
931 | { | 931 | { |
932 | u64 address1, address2; | 932 | u64 address1, address2; |
933 | struct vm_area_struct *vma; | 933 | unsigned long hva, gpa; |
934 | unsigned long user_address; | 934 | int ret = 0, cc = 0; |
935 | bool writable; | ||
935 | 936 | ||
936 | vcpu->stat.instruction_tprot++; | 937 | vcpu->stat.instruction_tprot++; |
937 | 938 | ||
@@ -942,32 +943,41 @@ static int handle_tprot(struct kvm_vcpu *vcpu) | |||
942 | 943 | ||
943 | /* we only handle the Linux memory detection case: | 944 | /* we only handle the Linux memory detection case: |
944 | * access key == 0 | 945 | * access key == 0 |
945 | * guest DAT == off | ||
946 | * everything else goes to userspace. */ | 946 | * everything else goes to userspace. */ |
947 | if (address2 & 0xf0) | 947 | if (address2 & 0xf0) |
948 | return -EOPNOTSUPP; | 948 | return -EOPNOTSUPP; |
949 | if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_DAT) | 949 | if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_DAT) |
950 | return -EOPNOTSUPP; | 950 | ipte_lock(vcpu); |
951 | 951 | ret = guest_translate_address(vcpu, address1, &gpa, 1); | |
952 | down_read(¤t->mm->mmap_sem); | 952 | if (ret == PGM_PROTECTION) { |
953 | user_address = __gmap_translate(address1, vcpu->arch.gmap); | 953 | /* Write protected? Try again with read-only... */ |
954 | if (IS_ERR_VALUE(user_address)) | 954 | cc = 1; |
955 | goto out_inject; | 955 | ret = guest_translate_address(vcpu, address1, &gpa, 0); |
956 | vma = find_vma(current->mm, user_address); | 956 | } |
957 | if (!vma) | 957 | if (ret) { |
958 | goto out_inject; | 958 | if (ret == PGM_ADDRESSING || ret == PGM_TRANSLATION_SPEC) { |
959 | vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); | 959 | ret = kvm_s390_inject_program_int(vcpu, ret); |
960 | if (!(vma->vm_flags & VM_WRITE) && (vma->vm_flags & VM_READ)) | 960 | } else if (ret > 0) { |
961 | vcpu->arch.sie_block->gpsw.mask |= (1ul << 44); | 961 | /* Translation not available */ |
962 | if (!(vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_READ)) | 962 | kvm_s390_set_psw_cc(vcpu, 3); |
963 | vcpu->arch.sie_block->gpsw.mask |= (2ul << 44); | 963 | ret = 0; |
964 | 964 | } | |
965 | up_read(¤t->mm->mmap_sem); | 965 | goto out_unlock; |
966 | return 0; | 966 | } |
967 | 967 | ||
968 | out_inject: | 968 | hva = gfn_to_hva_prot(vcpu->kvm, gpa_to_gfn(gpa), &writable); |
969 | up_read(¤t->mm->mmap_sem); | 969 | if (kvm_is_error_hva(hva)) { |
970 | return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | 970 | ret = kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); |
971 | } else { | ||
972 | if (!writable) | ||
973 | cc = 1; /* Write not permitted ==> read-only */ | ||
974 | kvm_s390_set_psw_cc(vcpu, cc); | ||
975 | /* Note: CC2 only occurs for storage keys (not supported yet) */ | ||
976 | } | ||
977 | out_unlock: | ||
978 | if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_DAT) | ||
979 | ipte_unlock(vcpu); | ||
980 | return ret; | ||
971 | } | 981 | } |
972 | 982 | ||
973 | int kvm_s390_handle_e5(struct kvm_vcpu *vcpu) | 983 | int kvm_s390_handle_e5(struct kvm_vcpu *vcpu) |