On Mon, 10 Dec 2012 08:51:11 +0100 Alexander Graf <agraf@xxxxxxx> wrote: > > On 07.12.2012, at 13:30, Cornelia Huck wrote: > > > Add support for injecting machine checks (only repressible > > conditions for now). > > > > This is a bit more involved than I/O interrupts, for these reasons: > > > > - Machine checks come in both floating and cpu varieties. > > - We don't have a bit for machine checks enabling, but have to use > > a roundabout approach with trapping PSW changing instructions and > > watching for opened machine checks. > > > > Reviewed-by: Marcelo Tosatti <mtosatti@xxxxxxxxxx> > > Signed-off-by: Cornelia Huck <cornelia.huck@xxxxxxxxxx> > > --- > > Documentation/virtual/kvm/api.txt | 4 ++ > > arch/s390/include/asm/kvm_host.h | 8 +++ > > arch/s390/kvm/intercept.c | 2 + > > arch/s390/kvm/interrupt.c | 112 ++++++++++++++++++++++++++++++++ > > arch/s390/kvm/kvm-s390.h | 3 + > > arch/s390/kvm/priv.c | 133 ++++++++++++++++++++++++++++++++++++++ > > arch/s390/kvm/trace-s390.h | 6 +- > > include/uapi/linux/kvm.h | 1 + > > 8 files changed, 266 insertions(+), 3 deletions(-) > > > > diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt > > index e298a72..8617339 100644 > > --- a/Documentation/virtual/kvm/api.txt > > +++ b/Documentation/virtual/kvm/api.txt > > @@ -2072,6 +2072,10 @@ KVM_S390_INT_IO(ai,cssid,ssid,schid) (vm) - compound value to indicate an > > I/O interrupt (ai - adapter interrupt; cssid,ssid,schid - subchannel); > > I/O interruption parameters in parm (subchannel) and parm64 (intparm, > > interruption subclass) > > +KVM_S390_MCHK (vm, vcpu) - machine check interrupt; cr 14 bits in parm, > > + machine check interrupt code in parm64 (note that > > + machine checks needing further payload are not > > + supported by this ioctl) > > > > Note that the vcpu ioctl is asynchronous to vcpu execution. > > > > diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h > > index e47f697..773859e 100644 > > --- a/arch/s390/include/asm/kvm_host.h > > +++ b/arch/s390/include/asm/kvm_host.h > > @@ -77,8 +77,10 @@ struct kvm_s390_sie_block { > > __u8 reserved40[4]; /* 0x0040 */ > > #define LCTL_CR0 0x8000 > > #define LCTL_CR6 0x0200 > > +#define LCTL_CR14 0x0002 > > __u16 lctl; /* 0x0044 */ > > __s16 icpua; /* 0x0046 */ > > +#define ICTL_LPSW 0x00400000 > > __u32 ictl; /* 0x0048 */ > > __u32 eca; /* 0x004c */ > > __u8 icptcode; /* 0x0050 */ > > @@ -189,6 +191,11 @@ struct kvm_s390_emerg_info { > > __u16 code; > > }; > > > > +struct kvm_s390_mchk_info { > > + __u64 cr14; > > + __u64 mcic; > > +}; > > + > > struct kvm_s390_interrupt_info { > > struct list_head list; > > u64 type; > > @@ -199,6 +206,7 @@ struct kvm_s390_interrupt_info { > > struct kvm_s390_emerg_info emerg; > > struct kvm_s390_extcall_info extcall; > > struct kvm_s390_prefix_info prefix; > > + struct kvm_s390_mchk_info mchk; > > }; > > }; > > > > diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c > > index 22798ec..ec1177f 100644 > > --- a/arch/s390/kvm/intercept.c > > +++ b/arch/s390/kvm/intercept.c > > @@ -106,10 +106,12 @@ static int handle_lctl(struct kvm_vcpu *vcpu) > > > > static intercept_handler_t instruction_handlers[256] = { > > [0x01] = kvm_s390_handle_01, > > + [0x82] = kvm_s390_handle_lpsw, > > [0x83] = kvm_s390_handle_diag, > > [0xae] = kvm_s390_handle_sigp, > > [0xb2] = kvm_s390_handle_b2, > > [0xb7] = handle_lctl, > > + [0xb9] = kvm_s390_handle_b9, > > [0xe5] = kvm_s390_handle_e5, > > [0xeb] = handle_lctlg, > > }; > > diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c > > index 070ba22..6b10267 100644 > > --- a/arch/s390/kvm/interrupt.c > > +++ b/arch/s390/kvm/interrupt.c > > @@ -41,6 +41,11 @@ static int psw_ioint_disabled(struct kvm_vcpu *vcpu) > > return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO); > > } > > > > +static int psw_mchk_disabled(struct kvm_vcpu *vcpu) > > +{ > > + return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_MCHECK); > > +} > > + > > static int psw_interrupts_disabled(struct kvm_vcpu *vcpu) > > { > > if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) || > > @@ -82,6 +87,12 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu, > > case KVM_S390_SIGP_SET_PREFIX: > > case KVM_S390_RESTART: > > return 1; > > + case KVM_S390_MCHK: > > + if (psw_mchk_disabled(vcpu)) > > + return 0; > > + if (vcpu->arch.sie_block->gcr[14] & inti->mchk.cr14) > > + return 1; > > + return 0; > > default: > > if (is_ioint(inti->type)) { > > if (psw_ioint_disabled(vcpu)) > > @@ -119,6 +130,7 @@ static void __reset_intercept_indicators(struct kvm_vcpu *vcpu) > > CPUSTAT_IO_INT | CPUSTAT_EXT_INT | CPUSTAT_STOP_INT, > > &vcpu->arch.sie_block->cpuflags); > > vcpu->arch.sie_block->lctl = 0x0000; > > + vcpu->arch.sie_block->ictl &= ~ICTL_LPSW; > > } > > > > static void __set_cpuflag(struct kvm_vcpu *vcpu, u32 flag) > > @@ -142,6 +154,12 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu, > > case KVM_S390_SIGP_STOP: > > __set_cpuflag(vcpu, CPUSTAT_STOP_INT); > > break; > > + case KVM_S390_MCHK: > > + if (psw_mchk_disabled(vcpu)) > > + vcpu->arch.sie_block->ictl |= ICTL_LPSW; > > + else > > + vcpu->arch.sie_block->lctl |= LCTL_CR14; > > + break; > > default: > > if (is_ioint(inti->type)) { > > if (psw_ioint_disabled(vcpu)) > > @@ -330,6 +348,32 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu, > > exception = 1; > > break; > > > > + case KVM_S390_MCHK: > > + VCPU_EVENT(vcpu, 4, "interrupt: machine check mcic=%llx", > > + inti->mchk.mcic); > > + trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, > > + inti->mchk.cr14, > > + inti->mchk.mcic); > > + rc = kvm_s390_vcpu_store_status(vcpu, > > + KVM_S390_STORE_STATUS_PREFIXED); > > + if (rc == -EFAULT) > > + exception = 1; > > + > > + rc = put_guest_u64(vcpu, __LC_MCCK_CODE, inti->mchk.mcic); > > + if (rc == -EFAULT) > > + exception = 1; > > + > > + rc = copy_to_guest(vcpu, __LC_MCK_OLD_PSW, > > + &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); > > + if (rc == -EFAULT) > > + exception = 1; > > + > > + rc = copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw, > > + __LC_MCK_NEW_PSW, sizeof(psw_t)); > > + if (rc == -EFAULT) > > + exception = 1; > > + break; > > + > > default: > > if (is_ioint(inti->type)) { > > __u32 param0 = ((__u32)inti->io.subchannel_id << 16) | > > @@ -593,6 +637,61 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu) > > } > > } > > > > +void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu) > > +{ > > + struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; > > + struct kvm_s390_float_interrupt *fi = vcpu->arch.local_int.float_int; > > + struct kvm_s390_interrupt_info *n, *inti = NULL; > > + int deliver; > > + > > + __reset_intercept_indicators(vcpu); > > + if (atomic_read(&li->active)) { > > + do { > > + deliver = 0; > > + spin_lock_bh(&li->lock); > > + list_for_each_entry_safe(inti, n, &li->list, list) { > > + if ((inti->type == KVM_S390_MCHK) && > > + __interrupt_is_deliverable(vcpu, inti)) { > > + list_del(&inti->list); > > + deliver = 1; > > + break; > > + } > > + __set_intercept_indicator(vcpu, inti); > > + } > > + if (list_empty(&li->list)) > > + atomic_set(&li->active, 0); > > + spin_unlock_bh(&li->lock); > > + if (deliver) { > > + __do_deliver_interrupt(vcpu, inti); > > + kfree(inti); > > + } > > + } while (deliver); > > + } > > + > > + if (atomic_read(&fi->active)) { > > + do { > > + deliver = 0; > > + spin_lock(&fi->lock); > > + list_for_each_entry_safe(inti, n, &fi->list, list) { > > + if ((inti->type == KVM_S390_MCHK) && > > + __interrupt_is_deliverable(vcpu, inti)) { > > + list_del(&inti->list); > > + deliver = 1; > > + break; > > + } > > + __set_intercept_indicator(vcpu, inti); > > + } > > + if (list_empty(&fi->list)) > > + atomic_set(&fi->active, 0); > > + spin_unlock(&fi->lock); > > + if (deliver) { > > + __do_deliver_interrupt(vcpu, inti); > > + kfree(inti); > > + } > > + } while (deliver); > > + } > > +} > > + > > int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code) > > { > > struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; > > @@ -646,6 +745,13 @@ int kvm_s390_inject_vm(struct kvm *kvm, > > case KVM_S390_INT_EMERGENCY: > > kfree(inti); > > return -EINVAL; > > + case KVM_S390_MCHK: > > + VM_EVENT(kvm, 5, "inject: machine check parm64:%llx", > > + s390int->parm64); > > + inti->type = s390int->type; > > + inti->mchk.cr14 = s390int->parm; /* upper bits are not used */ > > + inti->mchk.mcic = s390int->parm64; > > + break; > > default: > > if (!is_ioint(s390int->type)) { > > kfree(inti); > > @@ -755,6 +861,12 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, > > inti->type = s390int->type; > > inti->emerg.code = s390int->parm; > > break; > > + case KVM_S390_MCHK: > > + VCPU_EVENT(vcpu, 5, "inject: machine check parm64:%llx", > > + s390int->parm64); > > + inti->type = s390int->type; > > + inti->mchk.mcic = s390int->parm64; > > + break; > > case KVM_S390_INT_VIRTIO: > > case KVM_S390_INT_SERVICE: > > default: > > diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h > > index d75bc5e..b1e1cb6 100644 > > --- a/arch/s390/kvm/kvm-s390.h > > +++ b/arch/s390/kvm/kvm-s390.h > > @@ -69,6 +69,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu); > > enum hrtimer_restart kvm_s390_idle_wakeup(struct hrtimer *timer); > > void kvm_s390_tasklet(unsigned long parm); > > void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu); > > +void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu); > > int kvm_s390_inject_vm(struct kvm *kvm, > > struct kvm_s390_interrupt *s390int); > > int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, > > @@ -80,6 +81,8 @@ int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action); > > int kvm_s390_handle_b2(struct kvm_vcpu *vcpu); > > int kvm_s390_handle_e5(struct kvm_vcpu *vcpu); > > int kvm_s390_handle_01(struct kvm_vcpu *vcpu); > > +int kvm_s390_handle_b9(struct kvm_vcpu *vcpu); > > +int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu); > > > > /* implemented in sigp.c */ > > int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu); > > diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c > > index d768906..66258b9 100644 > > --- a/arch/s390/kvm/priv.c > > +++ b/arch/s390/kvm/priv.c > > @@ -176,6 +176,101 @@ static int handle_stfl(struct kvm_vcpu *vcpu) > > return 0; > > } > > > > +static void handle_new_psw(struct kvm_vcpu *vcpu) > > +{ > > + /* Check whether the new psw is enabled for machine checks. */ > > + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_MCHECK) > > + kvm_s390_deliver_pending_machine_checks(vcpu); > > +} > > + > > +int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu) > > +{ > > + int base2 = vcpu->arch.sie_block->ipb >> 28; > > + int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); > > + u64 addr; > > + u64 uninitialized_var(new_psw); > > + > > + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) > > + return kvm_s390_inject_program_int(vcpu, > > + PGM_PRIVILEGED_OPERATION); > > + > > + addr = disp2; > > + if (base2) > > + addr += vcpu->run->s.regs.gprs[base2]; > > This is a pretty common address offset scheme. Please extract it into a separate function (which the compiler will inline again). > > > + > > + if (addr & 7) { > > + kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); > > + goto out; > > + } > > + > > + if (get_guest_u64(vcpu, addr, &new_psw)) { > > + kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); > > + goto out; > > + } > > + > > + if (!(new_psw & 0x0008000000000000)) { > > Magic number? Doesn't this bit have a name? > > > + kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); > > + goto out; > > + } > > + > > + vcpu->arch.sie_block->gpsw.mask = new_psw & 0xfff7ffff80000000; > > + vcpu->arch.sie_block->gpsw.addr = new_psw & 0x000000007fffffff; > > + > > + if ((vcpu->arch.sie_block->gpsw.mask & 0xb80800fe00000000) || > > + (!(vcpu->arch.sie_block->gpsw.mask & 0x0000000180000000) && > > + (vcpu->arch.sie_block->gpsw.addr & 0x000000007ff00000)) || > > + ((vcpu->arch.sie_block->gpsw.mask & 0x0000000180000000) == > > + 0x0000000100000000)) { > > + kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); > > Lots of magic numbers :). I'm sure these have names too ;). I really like to be able to read this cod without looking at the POP all the time. I'll see what I can come up with. > > > + goto out; > > + } > > + > > + handle_new_psw(vcpu); > > +out: > > + return 0; > > +} > > + > > +static int handle_lpswe(struct kvm_vcpu *vcpu) > > +{ > > + int base2 = vcpu->arch.sie_block->ipb >> 28; > > + int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); > > + u64 addr; > > + psw_t new_psw; > > + > > + addr = disp2; > > + if (base2) > > + addr += vcpu->run->s.regs.gprs[base2]; > > + > > + if (addr & 7) { > > + kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); > > + goto out; > > + } > > + > > + if (copy_from_guest(vcpu, &new_psw, addr, sizeof(new_psw))) { > > + kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); > > + goto out; > > + } > > + > > + vcpu->arch.sie_block->gpsw.mask = new_psw.mask; > > + vcpu->arch.sie_block->gpsw.addr = new_psw.addr; > > + > > + if ((vcpu->arch.sie_block->gpsw.mask & 0xb80800fe7fffffff) || > > + (((vcpu->arch.sie_block->gpsw.mask & 0x0000000180000000) == > > + 0x0000000080000000) && > > + (vcpu->arch.sie_block->gpsw.addr & 0xffffffff80000000)) || > > + (!(vcpu->arch.sie_block->gpsw.mask & 0x0000000180000000) && > > + (vcpu->arch.sie_block->gpsw.addr & 0xfffffffffff00000)) || > > + ((vcpu->arch.sie_block->gpsw.mask & 0x0000000180000000) == > > + 0x0000000100000000)) { > > + kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); > > + goto out; > > + } > > That function looks quite similar to the one above. Any chance to move them together? The checks are not quite the same. > > Alex > > > + > > + handle_new_psw(vcpu); > > +out: > > + return 0; > > +} > > + > > static int handle_stidp(struct kvm_vcpu *vcpu) > > { > > int base2 = vcpu->arch.sie_block->ipb >> 28; > > @@ -309,6 +404,7 @@ static intercept_handler_t priv_handlers[256] = { > > [0x5f] = handle_chsc, > > [0x7d] = handle_stsi, > > [0xb1] = handle_stfl, > > + [0xb2] = handle_lpswe, > > }; > > > > int kvm_s390_handle_b2(struct kvm_vcpu *vcpu) > > @@ -333,6 +429,43 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu) > > return -EOPNOTSUPP; > > } > > > > +static int handle_epsw(struct kvm_vcpu *vcpu) > > +{ > > + int reg1, reg2; > > + > > + reg1 = (vcpu->arch.sie_block->ipb & 0x00f00000) >> 24; > > + reg2 = (vcpu->arch.sie_block->ipb & 0x000f0000) >> 16; > > + vcpu->run->s.regs.gprs[reg1] &= 0xffffffff00000000; > > + vcpu->run->s.regs.gprs[reg1] |= vcpu->arch.sie_block->gpsw.mask >> 32; > > + if (reg2) { > > + vcpu->run->s.regs.gprs[reg2] &= 0xffffffff00000000; > > + vcpu->run->s.regs.gprs[reg2] |= > > + vcpu->arch.sie_block->gpsw.mask & 0x00000000ffffffff; > > + } > > + return 0; > > +} > > + > > +static intercept_handler_t b9_handlers[256] = { > > + [0x8d] = handle_epsw, > > +}; > > + > > +int kvm_s390_handle_b9(struct kvm_vcpu *vcpu) > > +{ > > + intercept_handler_t handler; > > + > > + /* This is handled just as for the B2 instructions. */ > > + handler = b9_handlers[vcpu->arch.sie_block->ipa & 0x00ff]; > > + if (handler) { > > + if ((handler != handle_epsw) && > > + (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)) > > + return kvm_s390_inject_program_int(vcpu, > > + PGM_PRIVILEGED_OPERATION); > > + else > > + return handler(vcpu); > > + } > > + return -EOPNOTSUPP; > > +} > > + > > static int handle_tprot(struct kvm_vcpu *vcpu) > > { > > int base1 = (vcpu->arch.sie_block->ipb & 0xf0000000) >> 28; > > diff --git a/arch/s390/kvm/trace-s390.h b/arch/s390/kvm/trace-s390.h > > index 90fdf85..95fbc1a 100644 > > --- a/arch/s390/kvm/trace-s390.h > > +++ b/arch/s390/kvm/trace-s390.h > > @@ -141,13 +141,13 @@ TRACE_EVENT(kvm_s390_inject_vcpu, > > * Trace point for the actual delivery of interrupts. > > */ > > TRACE_EVENT(kvm_s390_deliver_interrupt, > > - TP_PROTO(unsigned int id, __u64 type, __u32 data0, __u64 data1), > > + TP_PROTO(unsigned int id, __u64 type, __u64 data0, __u64 data1), > > TP_ARGS(id, type, data0, data1), > > > > TP_STRUCT__entry( > > __field(int, id) > > __field(__u32, inttype) > > - __field(__u32, data0) > > + __field(__u64, data0) > > __field(__u64, data1) > > ), > > > > @@ -159,7 +159,7 @@ TRACE_EVENT(kvm_s390_deliver_interrupt, > > ), > > > > TP_printk("deliver interrupt (vcpu %d): type:%x (%s) " \ > > - "data:%08x %016llx", > > + "data:%08llx %016llx", > > __entry->id, __entry->inttype, > > __print_symbolic(__entry->inttype, kvm_s390_int_type), > > __entry->data0, __entry->data1) > > diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h > > index e676940..22859dc 100644 > > --- a/include/uapi/linux/kvm.h > > +++ b/include/uapi/linux/kvm.h > > @@ -397,6 +397,7 @@ struct kvm_s390_psw { > > #define KVM_S390_PROGRAM_INT 0xfffe0001u > > #define KVM_S390_SIGP_SET_PREFIX 0xfffe0002u > > #define KVM_S390_RESTART 0xfffe0003u > > +#define KVM_S390_MCHK 0xfffe1000u > > #define KVM_S390_INT_VIRTIO 0xffff2603u > > #define KVM_S390_INT_SERVICE 0xffff2401u > > #define KVM_S390_INT_EMERGENCY 0xffff1201u > > -- > > 1.7.12.4 > > > -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html