diff options
-rw-r--r-- | arch/s390/kvm/gaccess.c | 4 | ||||
-rw-r--r-- | arch/s390/kvm/gaccess.h | 2 | ||||
-rw-r--r-- | arch/s390/kvm/priv.c | 56 |
3 files changed, 37 insertions, 25 deletions
diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c index 5f73826992f2..4653ac6e182b 100644 --- a/arch/s390/kvm/gaccess.c +++ b/arch/s390/kvm/gaccess.c | |||
@@ -292,7 +292,7 @@ static void ipte_unlock_siif(struct kvm_vcpu *vcpu) | |||
292 | wake_up(&vcpu->kvm->arch.ipte_wq); | 292 | wake_up(&vcpu->kvm->arch.ipte_wq); |
293 | } | 293 | } |
294 | 294 | ||
295 | static void ipte_lock(struct kvm_vcpu *vcpu) | 295 | void ipte_lock(struct kvm_vcpu *vcpu) |
296 | { | 296 | { |
297 | if (vcpu->arch.sie_block->eca & 1) | 297 | if (vcpu->arch.sie_block->eca & 1) |
298 | ipte_lock_siif(vcpu); | 298 | ipte_lock_siif(vcpu); |
@@ -300,7 +300,7 @@ static void ipte_lock(struct kvm_vcpu *vcpu) | |||
300 | ipte_lock_simple(vcpu); | 300 | ipte_lock_simple(vcpu); |
301 | } | 301 | } |
302 | 302 | ||
303 | static void ipte_unlock(struct kvm_vcpu *vcpu) | 303 | void ipte_unlock(struct kvm_vcpu *vcpu) |
304 | { | 304 | { |
305 | if (vcpu->arch.sie_block->eca & 1) | 305 | if (vcpu->arch.sie_block->eca & 1) |
306 | ipte_unlock_siif(vcpu); | 306 | ipte_unlock_siif(vcpu); |
diff --git a/arch/s390/kvm/gaccess.h b/arch/s390/kvm/gaccess.h index 2d37a46195e2..0149cf15058a 100644 --- a/arch/s390/kvm/gaccess.h +++ b/arch/s390/kvm/gaccess.h | |||
@@ -327,6 +327,8 @@ int read_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, void *data, | |||
327 | return access_guest_real(vcpu, gra, data, len, 0); | 327 | return access_guest_real(vcpu, gra, data, len, 0); |
328 | } | 328 | } |
329 | 329 | ||
330 | void ipte_lock(struct kvm_vcpu *vcpu); | ||
331 | void ipte_unlock(struct kvm_vcpu *vcpu); | ||
330 | int ipte_lock_held(struct kvm_vcpu *vcpu); | 332 | int ipte_lock_held(struct kvm_vcpu *vcpu); |
331 | int kvm_s390_check_low_addr_protection(struct kvm_vcpu *vcpu, unsigned long ga); | 333 | int kvm_s390_check_low_addr_protection(struct kvm_vcpu *vcpu, unsigned long ga); |
332 | 334 | ||
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) |