Loading Documentation/virt/kvm/api.rst +3 −3 Original line number Original line Diff line number Diff line Loading @@ -3701,7 +3701,7 @@ KVM with the currently defined set of flags. :Architectures: s390 :Architectures: s390 :Type: vm ioctl :Type: vm ioctl :Parameters: struct kvm_s390_skeys :Parameters: struct kvm_s390_skeys :Returns: 0 on success, KVM_S390_GET_KEYS_NONE if guest is not using storage :Returns: 0 on success, KVM_S390_GET_SKEYS_NONE if guest is not using storage keys, negative value on error keys, negative value on error This ioctl is used to get guest storage key values on the s390 This ioctl is used to get guest storage key values on the s390 Loading @@ -3720,7 +3720,7 @@ you want to get. The count field is the number of consecutive frames (starting from start_gfn) The count field is the number of consecutive frames (starting from start_gfn) whose storage keys to get. The count field must be at least 1 and the maximum whose storage keys to get. The count field must be at least 1 and the maximum allowed value is defined as KVM_S390_SKEYS_ALLOC_MAX. Values outside this range allowed value is defined as KVM_S390_SKEYS_MAX. Values outside this range will cause the ioctl to return -EINVAL. will cause the ioctl to return -EINVAL. The skeydata_addr field is the address to a buffer large enough to hold count The skeydata_addr field is the address to a buffer large enough to hold count Loading @@ -3744,7 +3744,7 @@ you want to set. The count field is the number of consecutive frames (starting from start_gfn) The count field is the number of consecutive frames (starting from start_gfn) whose storage keys to get. The count field must be at least 1 and the maximum whose storage keys to get. The count field must be at least 1 and the maximum allowed value is defined as KVM_S390_SKEYS_ALLOC_MAX. Values outside this range allowed value is defined as KVM_S390_SKEYS_MAX. Values outside this range will cause the ioctl to return -EINVAL. will cause the ioctl to return -EINVAL. The skeydata_addr field is the address to a buffer containing count bytes of The skeydata_addr field is the address to a buffer containing count bytes of Loading arch/s390/include/asm/uv.h +17 −17 Original line number Original line Diff line number Diff line Loading @@ -91,23 +91,23 @@ struct uv_cb_header { /* Query Ultravisor Information */ /* Query Ultravisor Information */ struct uv_cb_qui { struct uv_cb_qui { struct uv_cb_header header; struct uv_cb_header header; /* 0x0000 */ u64 reserved08; u64 reserved08; /* 0x0008 */ u64 inst_calls_list[4]; u64 inst_calls_list[4]; /* 0x0010 */ u64 reserved30[2]; u64 reserved30[2]; /* 0x0030 */ u64 uv_base_stor_len; u64 uv_base_stor_len; /* 0x0040 */ u64 reserved48; u64 reserved48; /* 0x0048 */ u64 conf_base_phys_stor_len; u64 conf_base_phys_stor_len; /* 0x0050 */ u64 conf_base_virt_stor_len; u64 conf_base_virt_stor_len; /* 0x0058 */ u64 conf_virt_var_stor_len; u64 conf_virt_var_stor_len; /* 0x0060 */ u64 cpu_stor_len; u64 cpu_stor_len; /* 0x0068 */ u32 reserved70[3]; u32 reserved70[3]; /* 0x0070 */ u32 max_num_sec_conf; u32 max_num_sec_conf; /* 0x007c */ u64 max_guest_stor_addr; u64 max_guest_stor_addr; /* 0x0080 */ u8 reserved88[158 - 136]; u8 reserved88[158 - 136]; /* 0x0088 */ u16 max_guest_cpu_id; u16 max_guest_cpu_id; /* 0x009e */ u64 uv_feature_indications; u64 uv_feature_indications; /* 0x00a0 */ u8 reserveda0[200 - 168]; u8 reserveda8[200 - 168]; /* 0x00a8 */ } __packed __aligned(8); } __packed __aligned(8); /* Initialize Ultravisor */ /* Initialize Ultravisor */ Loading arch/s390/kvm/gaccess.c +92 −66 Original line number Original line Diff line number Diff line Loading @@ -794,46 +794,100 @@ static int low_address_protection_enabled(struct kvm_vcpu *vcpu, return 1; return 1; } } static int guest_page_range(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, /** unsigned long *pages, unsigned long nr_pages, * guest_range_to_gpas() - Calculate guest physical addresses of page fragments * covering a logical range * @vcpu: virtual cpu * @ga: guest address, start of range * @ar: access register * @gpas: output argument, may be NULL * @len: length of range in bytes * @asce: address-space-control element to use for translation * @mode: access mode * * Translate a logical range to a series of guest absolute addresses, * such that the concatenation of page fragments starting at each gpa make up * the whole range. * The translation is performed as if done by the cpu for the given @asce, @ar, * @mode and state of the @vcpu. * If the translation causes an exception, its program interruption code is * returned and the &struct kvm_s390_pgm_info pgm member of @vcpu is modified * such that a subsequent call to kvm_s390_inject_prog_vcpu() will inject * a correct exception into the guest. * The resulting gpas are stored into @gpas, unless it is NULL. * * Note: All fragments except the first one start at the beginning of a page. * When deriving the boundaries of a fragment from a gpa, all but the last * fragment end at the end of the page. * * Return: * * 0 - success * * <0 - translation could not be performed, for example if guest * memory could not be accessed * * >0 - an access exception occurred. In this case the returned value * is the program interruption code and the contents of pgm may * be used to inject an exception into the guest. */ static int guest_range_to_gpas(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, unsigned long *gpas, unsigned long len, const union asce asce, enum gacc_mode mode) const union asce asce, enum gacc_mode mode) { { psw_t *psw = &vcpu->arch.sie_block->gpsw; psw_t *psw = &vcpu->arch.sie_block->gpsw; unsigned int offset = offset_in_page(ga); unsigned int fragment_len; int lap_enabled, rc = 0; int lap_enabled, rc = 0; enum prot_type prot; enum prot_type prot; unsigned long gpa; lap_enabled = low_address_protection_enabled(vcpu, asce); lap_enabled = low_address_protection_enabled(vcpu, asce); while (nr_pages) { while (min(PAGE_SIZE - offset, len) > 0) { fragment_len = min(PAGE_SIZE - offset, len); ga = kvm_s390_logical_to_effective(vcpu, ga); ga = kvm_s390_logical_to_effective(vcpu, ga); if (mode == GACC_STORE && lap_enabled && is_low_address(ga)) if (mode == GACC_STORE && lap_enabled && is_low_address(ga)) return trans_exc(vcpu, PGM_PROTECTION, ga, ar, mode, return trans_exc(vcpu, PGM_PROTECTION, ga, ar, mode, PROT_TYPE_LA); PROT_TYPE_LA); ga &= PAGE_MASK; if (psw_bits(*psw).dat) { if (psw_bits(*psw).dat) { rc = guest_translate(vcpu, ga, pages, asce, mode, &prot); rc = guest_translate(vcpu, ga, &gpa, asce, mode, &prot); if (rc < 0) if (rc < 0) return rc; return rc; } else { } else { *pages = kvm_s390_real_to_abs(vcpu, ga); gpa = kvm_s390_real_to_abs(vcpu, ga); if (kvm_is_error_gpa(vcpu->kvm, *pages)) if (kvm_is_error_gpa(vcpu->kvm, gpa)) rc = PGM_ADDRESSING; rc = PGM_ADDRESSING; } } if (rc) if (rc) return trans_exc(vcpu, rc, ga, ar, mode, prot); return trans_exc(vcpu, rc, ga, ar, mode, prot); ga += PAGE_SIZE; if (gpas) pages++; *gpas++ = gpa; nr_pages--; offset = 0; ga += fragment_len; len -= fragment_len; } } return 0; return 0; } } static int access_guest_page(struct kvm *kvm, enum gacc_mode mode, gpa_t gpa, void *data, unsigned int len) { const unsigned int offset = offset_in_page(gpa); const gfn_t gfn = gpa_to_gfn(gpa); int rc; if (mode == GACC_STORE) rc = kvm_write_guest_page(kvm, gfn, data, offset, len); else rc = kvm_read_guest_page(kvm, gfn, data, offset, len); return rc; } int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, void *data, int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, void *data, unsigned long len, enum gacc_mode mode) unsigned long len, enum gacc_mode mode) { { psw_t *psw = &vcpu->arch.sie_block->gpsw; psw_t *psw = &vcpu->arch.sie_block->gpsw; unsigned long _len, nr_pages, gpa, idx; unsigned long nr_pages, idx; unsigned long pages_array[2]; unsigned long gpa_array[2]; unsigned long *pages; unsigned int fragment_len; unsigned long *gpas; int need_ipte_lock; int need_ipte_lock; union asce asce; union asce asce; int rc; int rc; Loading @@ -845,49 +899,42 @@ int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, void *data, if (rc) if (rc) return rc; return rc; nr_pages = (((ga & ~PAGE_MASK) + len - 1) >> PAGE_SHIFT) + 1; nr_pages = (((ga & ~PAGE_MASK) + len - 1) >> PAGE_SHIFT) + 1; pages = pages_array; gpas = gpa_array; if (nr_pages > ARRAY_SIZE(pages_array)) if (nr_pages > ARRAY_SIZE(gpa_array)) pages = vmalloc(array_size(nr_pages, sizeof(unsigned long))); gpas = vmalloc(array_size(nr_pages, sizeof(unsigned long))); if (!pages) if (!gpas) return -ENOMEM; return -ENOMEM; need_ipte_lock = psw_bits(*psw).dat && !asce.r; need_ipte_lock = psw_bits(*psw).dat && !asce.r; if (need_ipte_lock) if (need_ipte_lock) ipte_lock(vcpu); ipte_lock(vcpu); rc = guest_page_range(vcpu, ga, ar, pages, nr_pages, asce, mode); rc = guest_range_to_gpas(vcpu, ga, ar, gpas, len, asce, mode); for (idx = 0; idx < nr_pages && !rc; idx++) { for (idx = 0; idx < nr_pages && !rc; idx++) { gpa = *(pages + idx) + (ga & ~PAGE_MASK); fragment_len = min(PAGE_SIZE - offset_in_page(gpas[idx]), len); _len = min(PAGE_SIZE - (gpa & ~PAGE_MASK), len); rc = access_guest_page(vcpu->kvm, mode, gpas[idx], data, fragment_len); if (mode == GACC_STORE) len -= fragment_len; rc = kvm_write_guest(vcpu->kvm, gpa, data, _len); data += fragment_len; else rc = kvm_read_guest(vcpu->kvm, gpa, data, _len); len -= _len; ga += _len; data += _len; } } if (need_ipte_lock) if (need_ipte_lock) ipte_unlock(vcpu); ipte_unlock(vcpu); if (nr_pages > ARRAY_SIZE(pages_array)) if (nr_pages > ARRAY_SIZE(gpa_array)) vfree(pages); vfree(gpas); return rc; return rc; } } int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, void *data, unsigned long len, enum gacc_mode mode) void *data, unsigned long len, enum gacc_mode mode) { { unsigned long _len, gpa; unsigned int fragment_len; unsigned long gpa; int rc = 0; int rc = 0; while (len && !rc) { while (len && !rc) { gpa = kvm_s390_real_to_abs(vcpu, gra); gpa = kvm_s390_real_to_abs(vcpu, gra); _len = min(PAGE_SIZE - (gpa & ~PAGE_MASK), len); fragment_len = min(PAGE_SIZE - offset_in_page(gpa), len); if (mode) rc = access_guest_page(vcpu->kvm, mode, gpa, data, fragment_len); rc = write_guest_abs(vcpu, gpa, data, _len); len -= fragment_len; else gra += fragment_len; rc = read_guest_abs(vcpu, gpa, data, _len); data += fragment_len; len -= _len; gra += _len; data += _len; } } return rc; return rc; } } Loading @@ -909,8 +956,6 @@ int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar, int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar, unsigned long *gpa, enum gacc_mode mode) unsigned long *gpa, enum gacc_mode mode) { { psw_t *psw = &vcpu->arch.sie_block->gpsw; enum prot_type prot; union asce asce; union asce asce; int rc; int rc; Loading @@ -918,23 +963,7 @@ int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar, rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode); rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode); if (rc) if (rc) return rc; return rc; if (is_low_address(gva) && low_address_protection_enabled(vcpu, asce)) { return guest_range_to_gpas(vcpu, gva, ar, gpa, 1, asce, mode); if (mode == GACC_STORE) return trans_exc(vcpu, PGM_PROTECTION, gva, 0, mode, PROT_TYPE_LA); } if (psw_bits(*psw).dat && !asce.r) { /* Use DAT? */ rc = guest_translate(vcpu, gva, gpa, asce, mode, &prot); if (rc > 0) return trans_exc(vcpu, rc, gva, 0, mode, prot); } else { *gpa = kvm_s390_real_to_abs(vcpu, gva); if (kvm_is_error_gpa(vcpu->kvm, *gpa)) return trans_exc(vcpu, rc, gva, PGM_ADDRESSING, mode, 0); } return rc; } } /** /** Loading @@ -948,17 +977,14 @@ int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar, int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar, int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar, unsigned long length, enum gacc_mode mode) unsigned long length, enum gacc_mode mode) { { unsigned long gpa; union asce asce; unsigned long currlen; int rc = 0; int rc = 0; rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode); if (rc) return rc; ipte_lock(vcpu); ipte_lock(vcpu); while (length > 0 && !rc) { rc = guest_range_to_gpas(vcpu, gva, ar, NULL, length, asce, mode); currlen = min(length, PAGE_SIZE - (gva % PAGE_SIZE)); rc = guest_translate_address(vcpu, gva, ar, &gpa, mode); gva += currlen; length -= currlen; } ipte_unlock(vcpu); ipte_unlock(vcpu); return rc; return rc; Loading arch/s390/kvm/interrupt.c +7 −0 Original line number Original line Diff line number Diff line Loading @@ -2116,6 +2116,13 @@ int kvm_s390_is_stop_irq_pending(struct kvm_vcpu *vcpu) return test_bit(IRQ_PEND_SIGP_STOP, &li->pending_irqs); return test_bit(IRQ_PEND_SIGP_STOP, &li->pending_irqs); } } int kvm_s390_is_restart_irq_pending(struct kvm_vcpu *vcpu) { struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; return test_bit(IRQ_PEND_RESTART, &li->pending_irqs); } void kvm_s390_clear_stop_irq(struct kvm_vcpu *vcpu) void kvm_s390_clear_stop_irq(struct kvm_vcpu *vcpu) { { struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; Loading arch/s390/kvm/kvm-s390.c +7 −2 Original line number Original line Diff line number Diff line Loading @@ -4599,10 +4599,15 @@ int kvm_s390_vcpu_stop(struct kvm_vcpu *vcpu) } } } } /* SIGP STOP and SIGP STOP AND STORE STATUS has been fully processed */ /* * Set the VCPU to STOPPED and THEN clear the interrupt flag, * now that the SIGP STOP and SIGP STOP AND STORE STATUS orders * have been fully processed. This will ensure that the VCPU * is kept BUSY if another VCPU is inquiring with SIGP SENSE. */ kvm_s390_set_cpuflags(vcpu, CPUSTAT_STOPPED); kvm_s390_clear_stop_irq(vcpu); kvm_s390_clear_stop_irq(vcpu); kvm_s390_set_cpuflags(vcpu, CPUSTAT_STOPPED); __disable_ibs_on_vcpu(vcpu); __disable_ibs_on_vcpu(vcpu); for (i = 0; i < online_vcpus; i++) { for (i = 0; i < online_vcpus; i++) { Loading Loading
Documentation/virt/kvm/api.rst +3 −3 Original line number Original line Diff line number Diff line Loading @@ -3701,7 +3701,7 @@ KVM with the currently defined set of flags. :Architectures: s390 :Architectures: s390 :Type: vm ioctl :Type: vm ioctl :Parameters: struct kvm_s390_skeys :Parameters: struct kvm_s390_skeys :Returns: 0 on success, KVM_S390_GET_KEYS_NONE if guest is not using storage :Returns: 0 on success, KVM_S390_GET_SKEYS_NONE if guest is not using storage keys, negative value on error keys, negative value on error This ioctl is used to get guest storage key values on the s390 This ioctl is used to get guest storage key values on the s390 Loading @@ -3720,7 +3720,7 @@ you want to get. The count field is the number of consecutive frames (starting from start_gfn) The count field is the number of consecutive frames (starting from start_gfn) whose storage keys to get. The count field must be at least 1 and the maximum whose storage keys to get. The count field must be at least 1 and the maximum allowed value is defined as KVM_S390_SKEYS_ALLOC_MAX. Values outside this range allowed value is defined as KVM_S390_SKEYS_MAX. Values outside this range will cause the ioctl to return -EINVAL. will cause the ioctl to return -EINVAL. The skeydata_addr field is the address to a buffer large enough to hold count The skeydata_addr field is the address to a buffer large enough to hold count Loading @@ -3744,7 +3744,7 @@ you want to set. The count field is the number of consecutive frames (starting from start_gfn) The count field is the number of consecutive frames (starting from start_gfn) whose storage keys to get. The count field must be at least 1 and the maximum whose storage keys to get. The count field must be at least 1 and the maximum allowed value is defined as KVM_S390_SKEYS_ALLOC_MAX. Values outside this range allowed value is defined as KVM_S390_SKEYS_MAX. Values outside this range will cause the ioctl to return -EINVAL. will cause the ioctl to return -EINVAL. The skeydata_addr field is the address to a buffer containing count bytes of The skeydata_addr field is the address to a buffer containing count bytes of Loading
arch/s390/include/asm/uv.h +17 −17 Original line number Original line Diff line number Diff line Loading @@ -91,23 +91,23 @@ struct uv_cb_header { /* Query Ultravisor Information */ /* Query Ultravisor Information */ struct uv_cb_qui { struct uv_cb_qui { struct uv_cb_header header; struct uv_cb_header header; /* 0x0000 */ u64 reserved08; u64 reserved08; /* 0x0008 */ u64 inst_calls_list[4]; u64 inst_calls_list[4]; /* 0x0010 */ u64 reserved30[2]; u64 reserved30[2]; /* 0x0030 */ u64 uv_base_stor_len; u64 uv_base_stor_len; /* 0x0040 */ u64 reserved48; u64 reserved48; /* 0x0048 */ u64 conf_base_phys_stor_len; u64 conf_base_phys_stor_len; /* 0x0050 */ u64 conf_base_virt_stor_len; u64 conf_base_virt_stor_len; /* 0x0058 */ u64 conf_virt_var_stor_len; u64 conf_virt_var_stor_len; /* 0x0060 */ u64 cpu_stor_len; u64 cpu_stor_len; /* 0x0068 */ u32 reserved70[3]; u32 reserved70[3]; /* 0x0070 */ u32 max_num_sec_conf; u32 max_num_sec_conf; /* 0x007c */ u64 max_guest_stor_addr; u64 max_guest_stor_addr; /* 0x0080 */ u8 reserved88[158 - 136]; u8 reserved88[158 - 136]; /* 0x0088 */ u16 max_guest_cpu_id; u16 max_guest_cpu_id; /* 0x009e */ u64 uv_feature_indications; u64 uv_feature_indications; /* 0x00a0 */ u8 reserveda0[200 - 168]; u8 reserveda8[200 - 168]; /* 0x00a8 */ } __packed __aligned(8); } __packed __aligned(8); /* Initialize Ultravisor */ /* Initialize Ultravisor */ Loading
arch/s390/kvm/gaccess.c +92 −66 Original line number Original line Diff line number Diff line Loading @@ -794,46 +794,100 @@ static int low_address_protection_enabled(struct kvm_vcpu *vcpu, return 1; return 1; } } static int guest_page_range(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, /** unsigned long *pages, unsigned long nr_pages, * guest_range_to_gpas() - Calculate guest physical addresses of page fragments * covering a logical range * @vcpu: virtual cpu * @ga: guest address, start of range * @ar: access register * @gpas: output argument, may be NULL * @len: length of range in bytes * @asce: address-space-control element to use for translation * @mode: access mode * * Translate a logical range to a series of guest absolute addresses, * such that the concatenation of page fragments starting at each gpa make up * the whole range. * The translation is performed as if done by the cpu for the given @asce, @ar, * @mode and state of the @vcpu. * If the translation causes an exception, its program interruption code is * returned and the &struct kvm_s390_pgm_info pgm member of @vcpu is modified * such that a subsequent call to kvm_s390_inject_prog_vcpu() will inject * a correct exception into the guest. * The resulting gpas are stored into @gpas, unless it is NULL. * * Note: All fragments except the first one start at the beginning of a page. * When deriving the boundaries of a fragment from a gpa, all but the last * fragment end at the end of the page. * * Return: * * 0 - success * * <0 - translation could not be performed, for example if guest * memory could not be accessed * * >0 - an access exception occurred. In this case the returned value * is the program interruption code and the contents of pgm may * be used to inject an exception into the guest. */ static int guest_range_to_gpas(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, unsigned long *gpas, unsigned long len, const union asce asce, enum gacc_mode mode) const union asce asce, enum gacc_mode mode) { { psw_t *psw = &vcpu->arch.sie_block->gpsw; psw_t *psw = &vcpu->arch.sie_block->gpsw; unsigned int offset = offset_in_page(ga); unsigned int fragment_len; int lap_enabled, rc = 0; int lap_enabled, rc = 0; enum prot_type prot; enum prot_type prot; unsigned long gpa; lap_enabled = low_address_protection_enabled(vcpu, asce); lap_enabled = low_address_protection_enabled(vcpu, asce); while (nr_pages) { while (min(PAGE_SIZE - offset, len) > 0) { fragment_len = min(PAGE_SIZE - offset, len); ga = kvm_s390_logical_to_effective(vcpu, ga); ga = kvm_s390_logical_to_effective(vcpu, ga); if (mode == GACC_STORE && lap_enabled && is_low_address(ga)) if (mode == GACC_STORE && lap_enabled && is_low_address(ga)) return trans_exc(vcpu, PGM_PROTECTION, ga, ar, mode, return trans_exc(vcpu, PGM_PROTECTION, ga, ar, mode, PROT_TYPE_LA); PROT_TYPE_LA); ga &= PAGE_MASK; if (psw_bits(*psw).dat) { if (psw_bits(*psw).dat) { rc = guest_translate(vcpu, ga, pages, asce, mode, &prot); rc = guest_translate(vcpu, ga, &gpa, asce, mode, &prot); if (rc < 0) if (rc < 0) return rc; return rc; } else { } else { *pages = kvm_s390_real_to_abs(vcpu, ga); gpa = kvm_s390_real_to_abs(vcpu, ga); if (kvm_is_error_gpa(vcpu->kvm, *pages)) if (kvm_is_error_gpa(vcpu->kvm, gpa)) rc = PGM_ADDRESSING; rc = PGM_ADDRESSING; } } if (rc) if (rc) return trans_exc(vcpu, rc, ga, ar, mode, prot); return trans_exc(vcpu, rc, ga, ar, mode, prot); ga += PAGE_SIZE; if (gpas) pages++; *gpas++ = gpa; nr_pages--; offset = 0; ga += fragment_len; len -= fragment_len; } } return 0; return 0; } } static int access_guest_page(struct kvm *kvm, enum gacc_mode mode, gpa_t gpa, void *data, unsigned int len) { const unsigned int offset = offset_in_page(gpa); const gfn_t gfn = gpa_to_gfn(gpa); int rc; if (mode == GACC_STORE) rc = kvm_write_guest_page(kvm, gfn, data, offset, len); else rc = kvm_read_guest_page(kvm, gfn, data, offset, len); return rc; } int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, void *data, int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, void *data, unsigned long len, enum gacc_mode mode) unsigned long len, enum gacc_mode mode) { { psw_t *psw = &vcpu->arch.sie_block->gpsw; psw_t *psw = &vcpu->arch.sie_block->gpsw; unsigned long _len, nr_pages, gpa, idx; unsigned long nr_pages, idx; unsigned long pages_array[2]; unsigned long gpa_array[2]; unsigned long *pages; unsigned int fragment_len; unsigned long *gpas; int need_ipte_lock; int need_ipte_lock; union asce asce; union asce asce; int rc; int rc; Loading @@ -845,49 +899,42 @@ int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, void *data, if (rc) if (rc) return rc; return rc; nr_pages = (((ga & ~PAGE_MASK) + len - 1) >> PAGE_SHIFT) + 1; nr_pages = (((ga & ~PAGE_MASK) + len - 1) >> PAGE_SHIFT) + 1; pages = pages_array; gpas = gpa_array; if (nr_pages > ARRAY_SIZE(pages_array)) if (nr_pages > ARRAY_SIZE(gpa_array)) pages = vmalloc(array_size(nr_pages, sizeof(unsigned long))); gpas = vmalloc(array_size(nr_pages, sizeof(unsigned long))); if (!pages) if (!gpas) return -ENOMEM; return -ENOMEM; need_ipte_lock = psw_bits(*psw).dat && !asce.r; need_ipte_lock = psw_bits(*psw).dat && !asce.r; if (need_ipte_lock) if (need_ipte_lock) ipte_lock(vcpu); ipte_lock(vcpu); rc = guest_page_range(vcpu, ga, ar, pages, nr_pages, asce, mode); rc = guest_range_to_gpas(vcpu, ga, ar, gpas, len, asce, mode); for (idx = 0; idx < nr_pages && !rc; idx++) { for (idx = 0; idx < nr_pages && !rc; idx++) { gpa = *(pages + idx) + (ga & ~PAGE_MASK); fragment_len = min(PAGE_SIZE - offset_in_page(gpas[idx]), len); _len = min(PAGE_SIZE - (gpa & ~PAGE_MASK), len); rc = access_guest_page(vcpu->kvm, mode, gpas[idx], data, fragment_len); if (mode == GACC_STORE) len -= fragment_len; rc = kvm_write_guest(vcpu->kvm, gpa, data, _len); data += fragment_len; else rc = kvm_read_guest(vcpu->kvm, gpa, data, _len); len -= _len; ga += _len; data += _len; } } if (need_ipte_lock) if (need_ipte_lock) ipte_unlock(vcpu); ipte_unlock(vcpu); if (nr_pages > ARRAY_SIZE(pages_array)) if (nr_pages > ARRAY_SIZE(gpa_array)) vfree(pages); vfree(gpas); return rc; return rc; } } int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, void *data, unsigned long len, enum gacc_mode mode) void *data, unsigned long len, enum gacc_mode mode) { { unsigned long _len, gpa; unsigned int fragment_len; unsigned long gpa; int rc = 0; int rc = 0; while (len && !rc) { while (len && !rc) { gpa = kvm_s390_real_to_abs(vcpu, gra); gpa = kvm_s390_real_to_abs(vcpu, gra); _len = min(PAGE_SIZE - (gpa & ~PAGE_MASK), len); fragment_len = min(PAGE_SIZE - offset_in_page(gpa), len); if (mode) rc = access_guest_page(vcpu->kvm, mode, gpa, data, fragment_len); rc = write_guest_abs(vcpu, gpa, data, _len); len -= fragment_len; else gra += fragment_len; rc = read_guest_abs(vcpu, gpa, data, _len); data += fragment_len; len -= _len; gra += _len; data += _len; } } return rc; return rc; } } Loading @@ -909,8 +956,6 @@ int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar, int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar, unsigned long *gpa, enum gacc_mode mode) unsigned long *gpa, enum gacc_mode mode) { { psw_t *psw = &vcpu->arch.sie_block->gpsw; enum prot_type prot; union asce asce; union asce asce; int rc; int rc; Loading @@ -918,23 +963,7 @@ int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar, rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode); rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode); if (rc) if (rc) return rc; return rc; if (is_low_address(gva) && low_address_protection_enabled(vcpu, asce)) { return guest_range_to_gpas(vcpu, gva, ar, gpa, 1, asce, mode); if (mode == GACC_STORE) return trans_exc(vcpu, PGM_PROTECTION, gva, 0, mode, PROT_TYPE_LA); } if (psw_bits(*psw).dat && !asce.r) { /* Use DAT? */ rc = guest_translate(vcpu, gva, gpa, asce, mode, &prot); if (rc > 0) return trans_exc(vcpu, rc, gva, 0, mode, prot); } else { *gpa = kvm_s390_real_to_abs(vcpu, gva); if (kvm_is_error_gpa(vcpu->kvm, *gpa)) return trans_exc(vcpu, rc, gva, PGM_ADDRESSING, mode, 0); } return rc; } } /** /** Loading @@ -948,17 +977,14 @@ int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar, int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar, int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar, unsigned long length, enum gacc_mode mode) unsigned long length, enum gacc_mode mode) { { unsigned long gpa; union asce asce; unsigned long currlen; int rc = 0; int rc = 0; rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode); if (rc) return rc; ipte_lock(vcpu); ipte_lock(vcpu); while (length > 0 && !rc) { rc = guest_range_to_gpas(vcpu, gva, ar, NULL, length, asce, mode); currlen = min(length, PAGE_SIZE - (gva % PAGE_SIZE)); rc = guest_translate_address(vcpu, gva, ar, &gpa, mode); gva += currlen; length -= currlen; } ipte_unlock(vcpu); ipte_unlock(vcpu); return rc; return rc; Loading
arch/s390/kvm/interrupt.c +7 −0 Original line number Original line Diff line number Diff line Loading @@ -2116,6 +2116,13 @@ int kvm_s390_is_stop_irq_pending(struct kvm_vcpu *vcpu) return test_bit(IRQ_PEND_SIGP_STOP, &li->pending_irqs); return test_bit(IRQ_PEND_SIGP_STOP, &li->pending_irqs); } } int kvm_s390_is_restart_irq_pending(struct kvm_vcpu *vcpu) { struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; return test_bit(IRQ_PEND_RESTART, &li->pending_irqs); } void kvm_s390_clear_stop_irq(struct kvm_vcpu *vcpu) void kvm_s390_clear_stop_irq(struct kvm_vcpu *vcpu) { { struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; Loading
arch/s390/kvm/kvm-s390.c +7 −2 Original line number Original line Diff line number Diff line Loading @@ -4599,10 +4599,15 @@ int kvm_s390_vcpu_stop(struct kvm_vcpu *vcpu) } } } } /* SIGP STOP and SIGP STOP AND STORE STATUS has been fully processed */ /* * Set the VCPU to STOPPED and THEN clear the interrupt flag, * now that the SIGP STOP and SIGP STOP AND STORE STATUS orders * have been fully processed. This will ensure that the VCPU * is kept BUSY if another VCPU is inquiring with SIGP SENSE. */ kvm_s390_set_cpuflags(vcpu, CPUSTAT_STOPPED); kvm_s390_clear_stop_irq(vcpu); kvm_s390_clear_stop_irq(vcpu); kvm_s390_set_cpuflags(vcpu, CPUSTAT_STOPPED); __disable_ibs_on_vcpu(vcpu); __disable_ibs_on_vcpu(vcpu); for (i = 0; i < online_vcpus; i++) { for (i = 0; i < online_vcpus; i++) { Loading