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