1*bbe37e4cSDong Jia Shi /* 2*bbe37e4cSDong Jia Shi * Finite state machine for vfio-ccw device handling 3*bbe37e4cSDong Jia Shi * 4*bbe37e4cSDong Jia Shi * Copyright IBM Corp. 2017 5*bbe37e4cSDong Jia Shi * 6*bbe37e4cSDong Jia Shi * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> 7*bbe37e4cSDong Jia Shi */ 8*bbe37e4cSDong Jia Shi 9*bbe37e4cSDong Jia Shi #include <linux/vfio.h> 10*bbe37e4cSDong Jia Shi #include <linux/mdev.h> 11*bbe37e4cSDong Jia Shi 12*bbe37e4cSDong Jia Shi #include "ioasm.h" 13*bbe37e4cSDong Jia Shi #include "vfio_ccw_private.h" 14*bbe37e4cSDong Jia Shi 15*bbe37e4cSDong Jia Shi static int fsm_io_helper(struct vfio_ccw_private *private) 16*bbe37e4cSDong Jia Shi { 17*bbe37e4cSDong Jia Shi struct subchannel *sch; 18*bbe37e4cSDong Jia Shi union orb *orb; 19*bbe37e4cSDong Jia Shi int ccode; 20*bbe37e4cSDong Jia Shi __u8 lpm; 21*bbe37e4cSDong Jia Shi unsigned long flags; 22*bbe37e4cSDong Jia Shi 23*bbe37e4cSDong Jia Shi sch = private->sch; 24*bbe37e4cSDong Jia Shi 25*bbe37e4cSDong Jia Shi spin_lock_irqsave(sch->lock, flags); 26*bbe37e4cSDong Jia Shi private->state = VFIO_CCW_STATE_BUSY; 27*bbe37e4cSDong Jia Shi spin_unlock_irqrestore(sch->lock, flags); 28*bbe37e4cSDong Jia Shi 29*bbe37e4cSDong Jia Shi orb = cp_get_orb(&private->cp, (u32)(addr_t)sch, sch->lpm); 30*bbe37e4cSDong Jia Shi 31*bbe37e4cSDong Jia Shi /* Issue "Start Subchannel" */ 32*bbe37e4cSDong Jia Shi ccode = ssch(sch->schid, orb); 33*bbe37e4cSDong Jia Shi 34*bbe37e4cSDong Jia Shi switch (ccode) { 35*bbe37e4cSDong Jia Shi case 0: 36*bbe37e4cSDong Jia Shi /* 37*bbe37e4cSDong Jia Shi * Initialize device status information 38*bbe37e4cSDong Jia Shi */ 39*bbe37e4cSDong Jia Shi sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND; 40*bbe37e4cSDong Jia Shi return 0; 41*bbe37e4cSDong Jia Shi case 1: /* Status pending */ 42*bbe37e4cSDong Jia Shi case 2: /* Busy */ 43*bbe37e4cSDong Jia Shi return -EBUSY; 44*bbe37e4cSDong Jia Shi case 3: /* Device/path not operational */ 45*bbe37e4cSDong Jia Shi { 46*bbe37e4cSDong Jia Shi lpm = orb->cmd.lpm; 47*bbe37e4cSDong Jia Shi if (lpm != 0) 48*bbe37e4cSDong Jia Shi sch->lpm &= ~lpm; 49*bbe37e4cSDong Jia Shi else 50*bbe37e4cSDong Jia Shi sch->lpm = 0; 51*bbe37e4cSDong Jia Shi 52*bbe37e4cSDong Jia Shi if (cio_update_schib(sch)) 53*bbe37e4cSDong Jia Shi return -ENODEV; 54*bbe37e4cSDong Jia Shi 55*bbe37e4cSDong Jia Shi return sch->lpm ? -EACCES : -ENODEV; 56*bbe37e4cSDong Jia Shi } 57*bbe37e4cSDong Jia Shi default: 58*bbe37e4cSDong Jia Shi return ccode; 59*bbe37e4cSDong Jia Shi } 60*bbe37e4cSDong Jia Shi } 61*bbe37e4cSDong Jia Shi 62*bbe37e4cSDong Jia Shi static void fsm_notoper(struct vfio_ccw_private *private, 63*bbe37e4cSDong Jia Shi enum vfio_ccw_event event) 64*bbe37e4cSDong Jia Shi { 65*bbe37e4cSDong Jia Shi struct subchannel *sch = private->sch; 66*bbe37e4cSDong Jia Shi 67*bbe37e4cSDong Jia Shi /* 68*bbe37e4cSDong Jia Shi * TODO: 69*bbe37e4cSDong Jia Shi * Probably we should send the machine check to the guest. 70*bbe37e4cSDong Jia Shi */ 71*bbe37e4cSDong Jia Shi css_sched_sch_todo(sch, SCH_TODO_UNREG); 72*bbe37e4cSDong Jia Shi private->state = VFIO_CCW_STATE_NOT_OPER; 73*bbe37e4cSDong Jia Shi } 74*bbe37e4cSDong Jia Shi 75*bbe37e4cSDong Jia Shi /* 76*bbe37e4cSDong Jia Shi * No operation action. 77*bbe37e4cSDong Jia Shi */ 78*bbe37e4cSDong Jia Shi static void fsm_nop(struct vfio_ccw_private *private, 79*bbe37e4cSDong Jia Shi enum vfio_ccw_event event) 80*bbe37e4cSDong Jia Shi { 81*bbe37e4cSDong Jia Shi } 82*bbe37e4cSDong Jia Shi 83*bbe37e4cSDong Jia Shi static void fsm_io_error(struct vfio_ccw_private *private, 84*bbe37e4cSDong Jia Shi enum vfio_ccw_event event) 85*bbe37e4cSDong Jia Shi { 86*bbe37e4cSDong Jia Shi pr_err("vfio-ccw: FSM: I/O request from state:%d\n", private->state); 87*bbe37e4cSDong Jia Shi private->io_region.ret_code = -EIO; 88*bbe37e4cSDong Jia Shi } 89*bbe37e4cSDong Jia Shi 90*bbe37e4cSDong Jia Shi static void fsm_io_busy(struct vfio_ccw_private *private, 91*bbe37e4cSDong Jia Shi enum vfio_ccw_event event) 92*bbe37e4cSDong Jia Shi { 93*bbe37e4cSDong Jia Shi private->io_region.ret_code = -EBUSY; 94*bbe37e4cSDong Jia Shi } 95*bbe37e4cSDong Jia Shi 96*bbe37e4cSDong Jia Shi static void fsm_disabled_irq(struct vfio_ccw_private *private, 97*bbe37e4cSDong Jia Shi enum vfio_ccw_event event) 98*bbe37e4cSDong Jia Shi { 99*bbe37e4cSDong Jia Shi struct subchannel *sch = private->sch; 100*bbe37e4cSDong Jia Shi 101*bbe37e4cSDong Jia Shi /* 102*bbe37e4cSDong Jia Shi * An interrupt in a disabled state means a previous disable was not 103*bbe37e4cSDong Jia Shi * successful - should not happen, but we try to disable again. 104*bbe37e4cSDong Jia Shi */ 105*bbe37e4cSDong Jia Shi cio_disable_subchannel(sch); 106*bbe37e4cSDong Jia Shi } 107*bbe37e4cSDong Jia Shi 108*bbe37e4cSDong Jia Shi /* 109*bbe37e4cSDong Jia Shi * Deal with the ccw command request from the userspace. 110*bbe37e4cSDong Jia Shi */ 111*bbe37e4cSDong Jia Shi static void fsm_io_request(struct vfio_ccw_private *private, 112*bbe37e4cSDong Jia Shi enum vfio_ccw_event event) 113*bbe37e4cSDong Jia Shi { 114*bbe37e4cSDong Jia Shi union orb *orb; 115*bbe37e4cSDong Jia Shi union scsw *scsw = &private->scsw; 116*bbe37e4cSDong Jia Shi struct ccw_io_region *io_region = &private->io_region; 117*bbe37e4cSDong Jia Shi struct mdev_device *mdev = private->mdev; 118*bbe37e4cSDong Jia Shi 119*bbe37e4cSDong Jia Shi private->state = VFIO_CCW_STATE_BOXED; 120*bbe37e4cSDong Jia Shi 121*bbe37e4cSDong Jia Shi memcpy(scsw, io_region->scsw_area, sizeof(*scsw)); 122*bbe37e4cSDong Jia Shi 123*bbe37e4cSDong Jia Shi if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) { 124*bbe37e4cSDong Jia Shi orb = (union orb *)io_region->orb_area; 125*bbe37e4cSDong Jia Shi 126*bbe37e4cSDong Jia Shi io_region->ret_code = cp_init(&private->cp, mdev_dev(mdev), 127*bbe37e4cSDong Jia Shi orb); 128*bbe37e4cSDong Jia Shi if (io_region->ret_code) 129*bbe37e4cSDong Jia Shi goto err_out; 130*bbe37e4cSDong Jia Shi 131*bbe37e4cSDong Jia Shi io_region->ret_code = cp_prefetch(&private->cp); 132*bbe37e4cSDong Jia Shi if (io_region->ret_code) { 133*bbe37e4cSDong Jia Shi cp_free(&private->cp); 134*bbe37e4cSDong Jia Shi goto err_out; 135*bbe37e4cSDong Jia Shi } 136*bbe37e4cSDong Jia Shi 137*bbe37e4cSDong Jia Shi /* Start channel program and wait for I/O interrupt. */ 138*bbe37e4cSDong Jia Shi io_region->ret_code = fsm_io_helper(private); 139*bbe37e4cSDong Jia Shi if (io_region->ret_code) { 140*bbe37e4cSDong Jia Shi cp_free(&private->cp); 141*bbe37e4cSDong Jia Shi goto err_out; 142*bbe37e4cSDong Jia Shi } 143*bbe37e4cSDong Jia Shi return; 144*bbe37e4cSDong Jia Shi } else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) { 145*bbe37e4cSDong Jia Shi /* XXX: Handle halt. */ 146*bbe37e4cSDong Jia Shi io_region->ret_code = -EOPNOTSUPP; 147*bbe37e4cSDong Jia Shi goto err_out; 148*bbe37e4cSDong Jia Shi } else if (scsw->cmd.fctl & SCSW_FCTL_CLEAR_FUNC) { 149*bbe37e4cSDong Jia Shi /* XXX: Handle clear. */ 150*bbe37e4cSDong Jia Shi io_region->ret_code = -EOPNOTSUPP; 151*bbe37e4cSDong Jia Shi goto err_out; 152*bbe37e4cSDong Jia Shi } 153*bbe37e4cSDong Jia Shi 154*bbe37e4cSDong Jia Shi err_out: 155*bbe37e4cSDong Jia Shi private->state = VFIO_CCW_STATE_IDLE; 156*bbe37e4cSDong Jia Shi } 157*bbe37e4cSDong Jia Shi 158*bbe37e4cSDong Jia Shi /* 159*bbe37e4cSDong Jia Shi * Got an interrupt for a normal io (state busy). 160*bbe37e4cSDong Jia Shi */ 161*bbe37e4cSDong Jia Shi static void fsm_irq(struct vfio_ccw_private *private, 162*bbe37e4cSDong Jia Shi enum vfio_ccw_event event) 163*bbe37e4cSDong Jia Shi { 164*bbe37e4cSDong Jia Shi struct irb *irb; 165*bbe37e4cSDong Jia Shi 166*bbe37e4cSDong Jia Shi if (!private) 167*bbe37e4cSDong Jia Shi return; 168*bbe37e4cSDong Jia Shi 169*bbe37e4cSDong Jia Shi irb = this_cpu_ptr(&cio_irb); 170*bbe37e4cSDong Jia Shi memcpy(&private->irb, irb, sizeof(*irb)); 171*bbe37e4cSDong Jia Shi 172*bbe37e4cSDong Jia Shi queue_work(vfio_ccw_work_q, &private->io_work); 173*bbe37e4cSDong Jia Shi 174*bbe37e4cSDong Jia Shi if (private->completion) 175*bbe37e4cSDong Jia Shi complete(private->completion); 176*bbe37e4cSDong Jia Shi } 177*bbe37e4cSDong Jia Shi 178*bbe37e4cSDong Jia Shi /* 179*bbe37e4cSDong Jia Shi * Device statemachine 180*bbe37e4cSDong Jia Shi */ 181*bbe37e4cSDong Jia Shi fsm_func_t *vfio_ccw_jumptable[NR_VFIO_CCW_STATES][NR_VFIO_CCW_EVENTS] = { 182*bbe37e4cSDong Jia Shi [VFIO_CCW_STATE_NOT_OPER] = { 183*bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_NOT_OPER] = fsm_nop, 184*bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_IO_REQ] = fsm_io_error, 185*bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_INTERRUPT] = fsm_disabled_irq, 186*bbe37e4cSDong Jia Shi }, 187*bbe37e4cSDong Jia Shi [VFIO_CCW_STATE_STANDBY] = { 188*bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 189*bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_IO_REQ] = fsm_io_error, 190*bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 191*bbe37e4cSDong Jia Shi }, 192*bbe37e4cSDong Jia Shi [VFIO_CCW_STATE_IDLE] = { 193*bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 194*bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_IO_REQ] = fsm_io_request, 195*bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 196*bbe37e4cSDong Jia Shi }, 197*bbe37e4cSDong Jia Shi [VFIO_CCW_STATE_BOXED] = { 198*bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 199*bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_IO_REQ] = fsm_io_busy, 200*bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 201*bbe37e4cSDong Jia Shi }, 202*bbe37e4cSDong Jia Shi [VFIO_CCW_STATE_BUSY] = { 203*bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 204*bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_IO_REQ] = fsm_io_busy, 205*bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 206*bbe37e4cSDong Jia Shi }, 207*bbe37e4cSDong Jia Shi }; 208