Commit 5e4e84f1 authored by Paolo Bonzini's avatar Paolo Bonzini

Merge tag 'kvm-s390-next-5.17-1' of...

Merge tag 'kvm-s390-next-5.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux into HEAD

KVM: s390: Fix and cleanup

- fix sigp sense/start/stop/inconsistency
- cleanups
parents 855fb038 812de046
...@@ -3701,7 +3701,7 @@ KVM with the currently defined set of flags. ...@@ -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
...@@ -3720,7 +3720,7 @@ you want to get. ...@@ -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
...@@ -3744,7 +3744,7 @@ you want to set. ...@@ -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
......
...@@ -91,23 +91,23 @@ struct uv_cb_header { ...@@ -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 */
......
...@@ -794,46 +794,100 @@ static int low_address_protection_enabled(struct kvm_vcpu *vcpu, ...@@ -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
const union asce asce, enum gacc_mode mode) * 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)
{ {
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;
...@@ -845,49 +899,42 @@ int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, void *data, ...@@ -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;
} }
...@@ -909,8 +956,6 @@ int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, ...@@ -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;
...@@ -918,23 +963,7 @@ int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar, ...@@ -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;
} }
/** /**
...@@ -948,17 +977,14 @@ int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar, ...@@ -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;
......
...@@ -2116,6 +2116,13 @@ int kvm_s390_is_stop_irq_pending(struct kvm_vcpu *vcpu) ...@@ -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;
......
...@@ -4599,10 +4599,15 @@ int kvm_s390_vcpu_stop(struct kvm_vcpu *vcpu) ...@@ -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++) {
......
...@@ -441,6 +441,7 @@ void kvm_s390_destroy_adapters(struct kvm *kvm); ...@@ -441,6 +441,7 @@ void kvm_s390_destroy_adapters(struct kvm *kvm);
int kvm_s390_ext_call_pending(struct kvm_vcpu *vcpu); int kvm_s390_ext_call_pending(struct kvm_vcpu *vcpu);
extern struct kvm_device_ops kvm_flic_ops; extern struct kvm_device_ops kvm_flic_ops;
int kvm_s390_is_stop_irq_pending(struct kvm_vcpu *vcpu); int kvm_s390_is_stop_irq_pending(struct kvm_vcpu *vcpu);
int kvm_s390_is_restart_irq_pending(struct kvm_vcpu *vcpu);
void kvm_s390_clear_stop_irq(struct kvm_vcpu *vcpu); void kvm_s390_clear_stop_irq(struct kvm_vcpu *vcpu);
int kvm_s390_set_irq_state(struct kvm_vcpu *vcpu, int kvm_s390_set_irq_state(struct kvm_vcpu *vcpu,
void __user *buf, int len); void __user *buf, int len);
......
...@@ -276,6 +276,34 @@ static int handle_sigp_dst(struct kvm_vcpu *vcpu, u8 order_code, ...@@ -276,6 +276,34 @@ static int handle_sigp_dst(struct kvm_vcpu *vcpu, u8 order_code,
if (!dst_vcpu) if (!dst_vcpu)
return SIGP_CC_NOT_OPERATIONAL; return SIGP_CC_NOT_OPERATIONAL;
/*
* SIGP RESTART, SIGP STOP, and SIGP STOP AND STORE STATUS orders
* are processed asynchronously. Until the affected VCPU finishes
* its work and calls back into KVM to clear the (RESTART or STOP)
* interrupt, we need to return any new non-reset orders "busy".
*
* This is important because a single VCPU could issue:
* 1) SIGP STOP $DESTINATION
* 2) SIGP SENSE $DESTINATION
*
* If the SIGP SENSE would not be rejected as "busy", it could
* return an incorrect answer as to whether the VCPU is STOPPED
* or OPERATING.
*/
if (order_code != SIGP_INITIAL_CPU_RESET &&
order_code != SIGP_CPU_RESET) {
/*
* Lockless check. Both SIGP STOP and SIGP (RE)START
* properly synchronize everything while processing
* their orders, while the guest cannot observe a
* difference when issuing other orders from two
* different VCPUs.
*/
if (kvm_s390_is_stop_irq_pending(dst_vcpu) ||
kvm_s390_is_restart_irq_pending(dst_vcpu))
return SIGP_CC_BUSY;
}
switch (order_code) { switch (order_code) {
case SIGP_SENSE: case SIGP_SENSE:
vcpu->stat.instruction_sigp_sense++; vcpu->stat.instruction_sigp_sense++;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment