KVM: s390: Add support for channel I/O instructions.
Add a new capability, KVM_CAP_S390_CSS_SUPPORT, which will pass intercepts for channel I/O instructions to userspace. Only I/O instructions interacting with I/O interrupts need to be handled in-kernel: - TEST PENDING INTERRUPTION (tpi) dequeues and stores pending interrupts entirely in-kernel. - TEST SUBCHANNEL (tsch) dequeues pending interrupts in-kernel and exits via KVM_EXIT_S390_TSCH to userspace for subchannel- related processing. Reviewed-by: Marcelo Tosatti <mtosatti@redhat.com> Reviewed-by: Alexander Graf <agraf@suse.de> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>hifive-unleashed-5.1
parent
d6712df95b
commit
fa6b7fe992
|
@ -2350,6 +2350,22 @@ The possible hypercalls are defined in the Power Architecture Platform
|
||||||
Requirements (PAPR) document available from www.power.org (free
|
Requirements (PAPR) document available from www.power.org (free
|
||||||
developer registration required to access it).
|
developer registration required to access it).
|
||||||
|
|
||||||
|
/* KVM_EXIT_S390_TSCH */
|
||||||
|
struct {
|
||||||
|
__u16 subchannel_id;
|
||||||
|
__u16 subchannel_nr;
|
||||||
|
__u32 io_int_parm;
|
||||||
|
__u32 io_int_word;
|
||||||
|
__u32 ipb;
|
||||||
|
__u8 dequeued;
|
||||||
|
} s390_tsch;
|
||||||
|
|
||||||
|
s390 specific. This exit occurs when KVM_CAP_S390_CSS_SUPPORT has been enabled
|
||||||
|
and TEST SUBCHANNEL was intercepted. If dequeued is set, a pending I/O
|
||||||
|
interrupt for the target subchannel has been dequeued and subchannel_id,
|
||||||
|
subchannel_nr, io_int_parm and io_int_word contain the parameters for that
|
||||||
|
interrupt. ipb is needed for instruction parameter decoding.
|
||||||
|
|
||||||
/* Fix the size of the union. */
|
/* Fix the size of the union. */
|
||||||
char padding[256];
|
char padding[256];
|
||||||
};
|
};
|
||||||
|
@ -2471,3 +2487,17 @@ For mmu types KVM_MMU_FSL_BOOKE_NOHV and KVM_MMU_FSL_BOOKE_HV:
|
||||||
where "num_sets" is the tlb_sizes[] value divided by the tlb_ways[] value.
|
where "num_sets" is the tlb_sizes[] value divided by the tlb_ways[] value.
|
||||||
- The tsize field of mas1 shall be set to 4K on TLB0, even though the
|
- The tsize field of mas1 shall be set to 4K on TLB0, even though the
|
||||||
hardware ignores this value for TLB0.
|
hardware ignores this value for TLB0.
|
||||||
|
|
||||||
|
6.4 KVM_CAP_S390_CSS_SUPPORT
|
||||||
|
|
||||||
|
Architectures: s390
|
||||||
|
Parameters: none
|
||||||
|
Returns: 0 on success; -1 on error
|
||||||
|
|
||||||
|
This capability enables support for handling of channel I/O instructions.
|
||||||
|
|
||||||
|
TEST PENDING INTERRUPTION and the interrupt portion of TEST SUBCHANNEL are
|
||||||
|
handled in-kernel, while the other I/O instructions are passed to userspace.
|
||||||
|
|
||||||
|
When this capability is enabled, KVM_EXIT_S390_TSCH will occur on TEST
|
||||||
|
SUBCHANNEL intercepts.
|
||||||
|
|
|
@ -262,6 +262,7 @@ struct kvm_arch{
|
||||||
debug_info_t *dbf;
|
debug_info_t *dbf;
|
||||||
struct kvm_s390_float_interrupt float_int;
|
struct kvm_s390_float_interrupt float_int;
|
||||||
struct gmap *gmap;
|
struct gmap *gmap;
|
||||||
|
int css_support;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int sie64a(struct kvm_s390_sie_block *, u64 *);
|
extern int sie64a(struct kvm_s390_sie_block *, u64 *);
|
||||||
|
|
|
@ -264,6 +264,7 @@ static const intercept_handler_t intercept_funcs[] = {
|
||||||
[0x0C >> 2] = handle_instruction_and_prog,
|
[0x0C >> 2] = handle_instruction_and_prog,
|
||||||
[0x10 >> 2] = handle_noop,
|
[0x10 >> 2] = handle_noop,
|
||||||
[0x14 >> 2] = handle_noop,
|
[0x14 >> 2] = handle_noop,
|
||||||
|
[0x18 >> 2] = handle_noop,
|
||||||
[0x1C >> 2] = kvm_s390_handle_wait,
|
[0x1C >> 2] = kvm_s390_handle_wait,
|
||||||
[0x20 >> 2] = handle_validity,
|
[0x20 >> 2] = handle_validity,
|
||||||
[0x28 >> 2] = handle_stop,
|
[0x28 >> 2] = handle_stop,
|
||||||
|
|
|
@ -709,6 +709,43 @@ int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm,
|
||||||
|
u64 cr6, u64 schid)
|
||||||
|
{
|
||||||
|
struct kvm_s390_float_interrupt *fi;
|
||||||
|
struct kvm_s390_interrupt_info *inti, *iter;
|
||||||
|
|
||||||
|
if ((!schid && !cr6) || (schid && cr6))
|
||||||
|
return NULL;
|
||||||
|
mutex_lock(&kvm->lock);
|
||||||
|
fi = &kvm->arch.float_int;
|
||||||
|
spin_lock(&fi->lock);
|
||||||
|
inti = NULL;
|
||||||
|
list_for_each_entry(iter, &fi->list, list) {
|
||||||
|
if (!is_ioint(iter->type))
|
||||||
|
continue;
|
||||||
|
if (cr6 && ((cr6 & iter->io.io_int_word) == 0))
|
||||||
|
continue;
|
||||||
|
if (schid) {
|
||||||
|
if (((schid & 0x00000000ffff0000) >> 16) !=
|
||||||
|
iter->io.subchannel_id)
|
||||||
|
continue;
|
||||||
|
if ((schid & 0x000000000000ffff) !=
|
||||||
|
iter->io.subchannel_nr)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
inti = iter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (inti)
|
||||||
|
list_del_init(&inti->list);
|
||||||
|
if (list_empty(&fi->list))
|
||||||
|
atomic_set(&fi->active, 0);
|
||||||
|
spin_unlock(&fi->lock);
|
||||||
|
mutex_unlock(&kvm->lock);
|
||||||
|
return inti;
|
||||||
|
}
|
||||||
|
|
||||||
int kvm_s390_inject_vm(struct kvm *kvm,
|
int kvm_s390_inject_vm(struct kvm *kvm,
|
||||||
struct kvm_s390_interrupt *s390int)
|
struct kvm_s390_interrupt *s390int)
|
||||||
{
|
{
|
||||||
|
|
|
@ -141,6 +141,7 @@ int kvm_dev_ioctl_check_extension(long ext)
|
||||||
case KVM_CAP_SYNC_REGS:
|
case KVM_CAP_SYNC_REGS:
|
||||||
case KVM_CAP_ONE_REG:
|
case KVM_CAP_ONE_REG:
|
||||||
case KVM_CAP_ENABLE_CAP:
|
case KVM_CAP_ENABLE_CAP:
|
||||||
|
case KVM_CAP_S390_CSS_SUPPORT:
|
||||||
r = 1;
|
r = 1;
|
||||||
break;
|
break;
|
||||||
case KVM_CAP_NR_VCPUS:
|
case KVM_CAP_NR_VCPUS:
|
||||||
|
@ -235,6 +236,9 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
||||||
if (!kvm->arch.gmap)
|
if (!kvm->arch.gmap)
|
||||||
goto out_nogmap;
|
goto out_nogmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kvm->arch.css_support = 0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
out_nogmap:
|
out_nogmap:
|
||||||
debug_unregister(kvm->arch.dbf);
|
debug_unregister(kvm->arch.dbf);
|
||||||
|
@ -658,6 +662,7 @@ rerun_vcpu:
|
||||||
case KVM_EXIT_INTR:
|
case KVM_EXIT_INTR:
|
||||||
case KVM_EXIT_S390_RESET:
|
case KVM_EXIT_S390_RESET:
|
||||||
case KVM_EXIT_S390_UCONTROL:
|
case KVM_EXIT_S390_UCONTROL:
|
||||||
|
case KVM_EXIT_S390_TSCH:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BUG();
|
BUG();
|
||||||
|
@ -818,6 +823,13 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
switch (cap->cap) {
|
switch (cap->cap) {
|
||||||
|
case KVM_CAP_S390_CSS_SUPPORT:
|
||||||
|
if (!vcpu->kvm->arch.css_support) {
|
||||||
|
vcpu->kvm->arch.css_support = 1;
|
||||||
|
trace_kvm_s390_enable_css(vcpu->kvm);
|
||||||
|
}
|
||||||
|
r = 0;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
r = -EINVAL;
|
r = -EINVAL;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -113,6 +113,8 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
|
||||||
struct kvm_s390_interrupt *s390int);
|
struct kvm_s390_interrupt *s390int);
|
||||||
int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code);
|
int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code);
|
||||||
int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action);
|
int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action);
|
||||||
|
struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm,
|
||||||
|
u64 cr6, u64 schid);
|
||||||
|
|
||||||
/* implemented in priv.c */
|
/* implemented in priv.c */
|
||||||
int kvm_s390_handle_b2(struct kvm_vcpu *vcpu);
|
int kvm_s390_handle_b2(struct kvm_vcpu *vcpu);
|
||||||
|
|
|
@ -127,13 +127,96 @@ static int handle_skey(struct kvm_vcpu *vcpu)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int handle_tpi(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
u64 addr;
|
||||||
|
struct kvm_s390_interrupt_info *inti;
|
||||||
|
int cc;
|
||||||
|
|
||||||
|
addr = kvm_s390_get_base_disp_s(vcpu);
|
||||||
|
|
||||||
|
inti = kvm_s390_get_io_int(vcpu->kvm, vcpu->run->s.regs.crs[6], 0);
|
||||||
|
if (inti) {
|
||||||
|
if (addr) {
|
||||||
|
/*
|
||||||
|
* Store the two-word I/O interruption code into the
|
||||||
|
* provided area.
|
||||||
|
*/
|
||||||
|
put_guest_u16(vcpu, addr, inti->io.subchannel_id);
|
||||||
|
put_guest_u16(vcpu, addr + 2, inti->io.subchannel_nr);
|
||||||
|
put_guest_u32(vcpu, addr + 4, inti->io.io_int_parm);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Store the three-word I/O interruption code into
|
||||||
|
* the appropriate lowcore area.
|
||||||
|
*/
|
||||||
|
put_guest_u16(vcpu, 184, inti->io.subchannel_id);
|
||||||
|
put_guest_u16(vcpu, 186, inti->io.subchannel_nr);
|
||||||
|
put_guest_u32(vcpu, 188, inti->io.io_int_parm);
|
||||||
|
put_guest_u32(vcpu, 192, inti->io.io_int_word);
|
||||||
|
}
|
||||||
|
cc = 1;
|
||||||
|
} else
|
||||||
|
cc = 0;
|
||||||
|
kfree(inti);
|
||||||
|
/* Set condition code and we're done. */
|
||||||
|
vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
|
||||||
|
vcpu->arch.sie_block->gpsw.mask |= (cc & 3ul) << 44;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_tsch(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
struct kvm_s390_interrupt_info *inti;
|
||||||
|
|
||||||
|
inti = kvm_s390_get_io_int(vcpu->kvm, 0,
|
||||||
|
vcpu->run->s.regs.gprs[1]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare exit to userspace.
|
||||||
|
* We indicate whether we dequeued a pending I/O interrupt
|
||||||
|
* so that userspace can re-inject it if the instruction gets
|
||||||
|
* a program check. While this may re-order the pending I/O
|
||||||
|
* interrupts, this is no problem since the priority is kept
|
||||||
|
* intact.
|
||||||
|
*/
|
||||||
|
vcpu->run->exit_reason = KVM_EXIT_S390_TSCH;
|
||||||
|
vcpu->run->s390_tsch.dequeued = !!inti;
|
||||||
|
if (inti) {
|
||||||
|
vcpu->run->s390_tsch.subchannel_id = inti->io.subchannel_id;
|
||||||
|
vcpu->run->s390_tsch.subchannel_nr = inti->io.subchannel_nr;
|
||||||
|
vcpu->run->s390_tsch.io_int_parm = inti->io.io_int_parm;
|
||||||
|
vcpu->run->s390_tsch.io_int_word = inti->io.io_int_word;
|
||||||
|
}
|
||||||
|
vcpu->run->s390_tsch.ipb = vcpu->arch.sie_block->ipb;
|
||||||
|
kfree(inti);
|
||||||
|
return -EREMOTE;
|
||||||
|
}
|
||||||
|
|
||||||
static int handle_io_inst(struct kvm_vcpu *vcpu)
|
static int handle_io_inst(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
VCPU_EVENT(vcpu, 4, "%s", "I/O instruction");
|
VCPU_EVENT(vcpu, 4, "%s", "I/O instruction");
|
||||||
/* condition code 3 */
|
|
||||||
vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
|
if (vcpu->kvm->arch.css_support) {
|
||||||
vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44;
|
/*
|
||||||
return 0;
|
* Most I/O instructions will be handled by userspace.
|
||||||
|
* Exceptions are tpi and the interrupt portion of tsch.
|
||||||
|
*/
|
||||||
|
if (vcpu->arch.sie_block->ipa == 0xb236)
|
||||||
|
return handle_tpi(vcpu);
|
||||||
|
if (vcpu->arch.sie_block->ipa == 0xb235)
|
||||||
|
return handle_tsch(vcpu);
|
||||||
|
/* Handle in userspace. */
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Set condition code 3 to stop the guest from issueing channel
|
||||||
|
* I/O instructions.
|
||||||
|
*/
|
||||||
|
vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
|
||||||
|
vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int handle_stfl(struct kvm_vcpu *vcpu)
|
static int handle_stfl(struct kvm_vcpu *vcpu)
|
||||||
|
|
|
@ -204,6 +204,26 @@ TRACE_EVENT(kvm_s390_stop_request,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trace point for enabling channel I/O instruction support.
|
||||||
|
*/
|
||||||
|
TRACE_EVENT(kvm_s390_enable_css,
|
||||||
|
TP_PROTO(void *kvm),
|
||||||
|
TP_ARGS(kvm),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field(void *, kvm)
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->kvm = kvm;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("enabling channel I/O support (kvm @ %p)\n",
|
||||||
|
__entry->kvm)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
#endif /* _TRACE_KVMS390_H */
|
#endif /* _TRACE_KVMS390_H */
|
||||||
|
|
||||||
/* This part must be outside protection */
|
/* This part must be outside protection */
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
ERSN(SHUTDOWN), ERSN(FAIL_ENTRY), ERSN(INTR), ERSN(SET_TPR), \
|
ERSN(SHUTDOWN), ERSN(FAIL_ENTRY), ERSN(INTR), ERSN(SET_TPR), \
|
||||||
ERSN(TPR_ACCESS), ERSN(S390_SIEIC), ERSN(S390_RESET), ERSN(DCR),\
|
ERSN(TPR_ACCESS), ERSN(S390_SIEIC), ERSN(S390_RESET), ERSN(DCR),\
|
||||||
ERSN(NMI), ERSN(INTERNAL_ERROR), ERSN(OSI), ERSN(PAPR_HCALL), \
|
ERSN(NMI), ERSN(INTERNAL_ERROR), ERSN(OSI), ERSN(PAPR_HCALL), \
|
||||||
ERSN(S390_UCONTROL)
|
ERSN(S390_UCONTROL), ERSN(S390_TSCH)
|
||||||
|
|
||||||
TRACE_EVENT(kvm_userspace_exit,
|
TRACE_EVENT(kvm_userspace_exit,
|
||||||
TP_PROTO(__u32 reason, int errno),
|
TP_PROTO(__u32 reason, int errno),
|
||||||
|
|
|
@ -168,6 +168,7 @@ struct kvm_pit_config {
|
||||||
#define KVM_EXIT_PAPR_HCALL 19
|
#define KVM_EXIT_PAPR_HCALL 19
|
||||||
#define KVM_EXIT_S390_UCONTROL 20
|
#define KVM_EXIT_S390_UCONTROL 20
|
||||||
#define KVM_EXIT_WATCHDOG 21
|
#define KVM_EXIT_WATCHDOG 21
|
||||||
|
#define KVM_EXIT_S390_TSCH 22
|
||||||
|
|
||||||
/* For KVM_EXIT_INTERNAL_ERROR */
|
/* For KVM_EXIT_INTERNAL_ERROR */
|
||||||
/* Emulate instruction failed. */
|
/* Emulate instruction failed. */
|
||||||
|
@ -285,6 +286,15 @@ struct kvm_run {
|
||||||
__u64 ret;
|
__u64 ret;
|
||||||
__u64 args[9];
|
__u64 args[9];
|
||||||
} papr_hcall;
|
} papr_hcall;
|
||||||
|
/* KVM_EXIT_S390_TSCH */
|
||||||
|
struct {
|
||||||
|
__u16 subchannel_id;
|
||||||
|
__u16 subchannel_nr;
|
||||||
|
__u32 io_int_parm;
|
||||||
|
__u32 io_int_word;
|
||||||
|
__u32 ipb;
|
||||||
|
__u8 dequeued;
|
||||||
|
} s390_tsch;
|
||||||
/* Fix the size of the union. */
|
/* Fix the size of the union. */
|
||||||
char padding[256];
|
char padding[256];
|
||||||
};
|
};
|
||||||
|
@ -645,6 +655,7 @@ struct kvm_ppc_smmu_info {
|
||||||
#define KVM_CAP_IRQFD_RESAMPLE 82
|
#define KVM_CAP_IRQFD_RESAMPLE 82
|
||||||
#define KVM_CAP_PPC_BOOKE_WATCHDOG 83
|
#define KVM_CAP_PPC_BOOKE_WATCHDOG 83
|
||||||
#define KVM_CAP_PPC_HTAB_FD 84
|
#define KVM_CAP_PPC_HTAB_FD 84
|
||||||
|
#define KVM_CAP_S390_CSS_SUPPORT 85
|
||||||
|
|
||||||
#ifdef KVM_CAP_IRQ_ROUTING
|
#ifdef KVM_CAP_IRQ_ROUTING
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue