1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2bbe37e4cSDong Jia Shi /* 3bbe37e4cSDong Jia Shi * Finite state machine for vfio-ccw device handling 4bbe37e4cSDong Jia Shi * 5bbe37e4cSDong Jia Shi * Copyright IBM Corp. 2017 6bbe37e4cSDong Jia Shi * 7bbe37e4cSDong Jia Shi * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> 8bbe37e4cSDong Jia Shi */ 9bbe37e4cSDong Jia Shi 10bbe37e4cSDong Jia Shi #include <linux/vfio.h> 11bbe37e4cSDong Jia Shi #include <linux/mdev.h> 12bbe37e4cSDong Jia Shi 13bbe37e4cSDong Jia Shi #include "ioasm.h" 14bbe37e4cSDong Jia Shi #include "vfio_ccw_private.h" 15bbe37e4cSDong Jia Shi 163cd90214SHalil Pasic #define CREATE_TRACE_POINTS 173cd90214SHalil Pasic #include "vfio_ccw_trace.h" 183cd90214SHalil Pasic 19bbe37e4cSDong Jia Shi static int fsm_io_helper(struct vfio_ccw_private *private) 20bbe37e4cSDong Jia Shi { 21bbe37e4cSDong Jia Shi struct subchannel *sch; 22bbe37e4cSDong Jia Shi union orb *orb; 23bbe37e4cSDong Jia Shi int ccode; 24bbe37e4cSDong Jia Shi __u8 lpm; 25bbe37e4cSDong Jia Shi unsigned long flags; 263368e547SCornelia Huck int ret; 27bbe37e4cSDong Jia Shi 28bbe37e4cSDong Jia Shi sch = private->sch; 29bbe37e4cSDong Jia Shi 30bbe37e4cSDong Jia Shi spin_lock_irqsave(sch->lock, flags); 31bbe37e4cSDong Jia Shi 32bbe37e4cSDong Jia Shi orb = cp_get_orb(&private->cp, (u32)(addr_t)sch, sch->lpm); 3371189f26SCornelia Huck if (!orb) { 3471189f26SCornelia Huck ret = -EIO; 3571189f26SCornelia Huck goto out; 3671189f26SCornelia Huck } 37bbe37e4cSDong Jia Shi 38bbe37e4cSDong Jia Shi /* Issue "Start Subchannel" */ 39bbe37e4cSDong Jia Shi ccode = ssch(sch->schid, orb); 40bbe37e4cSDong Jia Shi 41bbe37e4cSDong Jia Shi switch (ccode) { 42bbe37e4cSDong Jia Shi case 0: 43bbe37e4cSDong Jia Shi /* 44bbe37e4cSDong Jia Shi * Initialize device status information 45bbe37e4cSDong Jia Shi */ 46bbe37e4cSDong Jia Shi sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND; 473368e547SCornelia Huck ret = 0; 48*690f6a15SCornelia Huck private->state = VFIO_CCW_STATE_CP_PENDING; 493368e547SCornelia Huck break; 50bbe37e4cSDong Jia Shi case 1: /* Status pending */ 51bbe37e4cSDong Jia Shi case 2: /* Busy */ 523368e547SCornelia Huck ret = -EBUSY; 533368e547SCornelia Huck break; 54bbe37e4cSDong Jia Shi case 3: /* Device/path not operational */ 55bbe37e4cSDong Jia Shi { 56bbe37e4cSDong Jia Shi lpm = orb->cmd.lpm; 57bbe37e4cSDong Jia Shi if (lpm != 0) 58bbe37e4cSDong Jia Shi sch->lpm &= ~lpm; 59bbe37e4cSDong Jia Shi else 60bbe37e4cSDong Jia Shi sch->lpm = 0; 61bbe37e4cSDong Jia Shi 62bbe37e4cSDong Jia Shi if (cio_update_schib(sch)) 633368e547SCornelia Huck ret = -ENODEV; 643368e547SCornelia Huck else 653368e547SCornelia Huck ret = sch->lpm ? -EACCES : -ENODEV; 663368e547SCornelia Huck break; 67bbe37e4cSDong Jia Shi } 68bbe37e4cSDong Jia Shi default: 693368e547SCornelia Huck ret = ccode; 70bbe37e4cSDong Jia Shi } 7171189f26SCornelia Huck out: 723368e547SCornelia Huck spin_unlock_irqrestore(sch->lock, flags); 733368e547SCornelia Huck return ret; 74bbe37e4cSDong Jia Shi } 75bbe37e4cSDong Jia Shi 76bbe37e4cSDong Jia Shi static void fsm_notoper(struct vfio_ccw_private *private, 77bbe37e4cSDong Jia Shi enum vfio_ccw_event event) 78bbe37e4cSDong Jia Shi { 79bbe37e4cSDong Jia Shi struct subchannel *sch = private->sch; 80bbe37e4cSDong Jia Shi 81bbe37e4cSDong Jia Shi /* 82bbe37e4cSDong Jia Shi * TODO: 83bbe37e4cSDong Jia Shi * Probably we should send the machine check to the guest. 84bbe37e4cSDong Jia Shi */ 85bbe37e4cSDong Jia Shi css_sched_sch_todo(sch, SCH_TODO_UNREG); 86bbe37e4cSDong Jia Shi private->state = VFIO_CCW_STATE_NOT_OPER; 87bbe37e4cSDong Jia Shi } 88bbe37e4cSDong Jia Shi 89bbe37e4cSDong Jia Shi /* 90bbe37e4cSDong Jia Shi * No operation action. 91bbe37e4cSDong Jia Shi */ 92bbe37e4cSDong Jia Shi static void fsm_nop(struct vfio_ccw_private *private, 93bbe37e4cSDong Jia Shi enum vfio_ccw_event event) 94bbe37e4cSDong Jia Shi { 95bbe37e4cSDong Jia Shi } 96bbe37e4cSDong Jia Shi 97bbe37e4cSDong Jia Shi static void fsm_io_error(struct vfio_ccw_private *private, 98bbe37e4cSDong Jia Shi enum vfio_ccw_event event) 99bbe37e4cSDong Jia Shi { 100bbe37e4cSDong Jia Shi pr_err("vfio-ccw: FSM: I/O request from state:%d\n", private->state); 101c98e16b2SEric Farman private->io_region->ret_code = -EIO; 102bbe37e4cSDong Jia Shi } 103bbe37e4cSDong Jia Shi 104bbe37e4cSDong Jia Shi static void fsm_io_busy(struct vfio_ccw_private *private, 105bbe37e4cSDong Jia Shi enum vfio_ccw_event event) 106bbe37e4cSDong Jia Shi { 107c98e16b2SEric Farman private->io_region->ret_code = -EBUSY; 108bbe37e4cSDong Jia Shi } 109bbe37e4cSDong Jia Shi 110*690f6a15SCornelia Huck static void fsm_io_retry(struct vfio_ccw_private *private, 111*690f6a15SCornelia Huck enum vfio_ccw_event event) 112*690f6a15SCornelia Huck { 113*690f6a15SCornelia Huck private->io_region->ret_code = -EAGAIN; 114*690f6a15SCornelia Huck } 115*690f6a15SCornelia Huck 116bbe37e4cSDong Jia Shi static void fsm_disabled_irq(struct vfio_ccw_private *private, 117bbe37e4cSDong Jia Shi enum vfio_ccw_event event) 118bbe37e4cSDong Jia Shi { 119bbe37e4cSDong Jia Shi struct subchannel *sch = private->sch; 120bbe37e4cSDong Jia Shi 121bbe37e4cSDong Jia Shi /* 122bbe37e4cSDong Jia Shi * An interrupt in a disabled state means a previous disable was not 123bbe37e4cSDong Jia Shi * successful - should not happen, but we try to disable again. 124bbe37e4cSDong Jia Shi */ 125bbe37e4cSDong Jia Shi cio_disable_subchannel(sch); 126bbe37e4cSDong Jia Shi } 1273cd90214SHalil Pasic inline struct subchannel_id get_schid(struct vfio_ccw_private *p) 1283cd90214SHalil Pasic { 1293cd90214SHalil Pasic return p->sch->schid; 1303cd90214SHalil Pasic } 131bbe37e4cSDong Jia Shi 132bbe37e4cSDong Jia Shi /* 133bbe37e4cSDong Jia Shi * Deal with the ccw command request from the userspace. 134bbe37e4cSDong Jia Shi */ 135bbe37e4cSDong Jia Shi static void fsm_io_request(struct vfio_ccw_private *private, 136bbe37e4cSDong Jia Shi enum vfio_ccw_event event) 137bbe37e4cSDong Jia Shi { 138bbe37e4cSDong Jia Shi union orb *orb; 139bbe37e4cSDong Jia Shi union scsw *scsw = &private->scsw; 140c98e16b2SEric Farman struct ccw_io_region *io_region = private->io_region; 141bbe37e4cSDong Jia Shi struct mdev_device *mdev = private->mdev; 1423cd90214SHalil Pasic char *errstr = "request"; 143bbe37e4cSDong Jia Shi 144*690f6a15SCornelia Huck private->state = VFIO_CCW_STATE_CP_PROCESSING; 145bbe37e4cSDong Jia Shi memcpy(scsw, io_region->scsw_area, sizeof(*scsw)); 146bbe37e4cSDong Jia Shi 147bbe37e4cSDong Jia Shi if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) { 148bbe37e4cSDong Jia Shi orb = (union orb *)io_region->orb_area; 149bbe37e4cSDong Jia Shi 1509851bc77SCornelia Huck /* Don't try to build a cp if transport mode is specified. */ 1519851bc77SCornelia Huck if (orb->tm.b) { 1529851bc77SCornelia Huck io_region->ret_code = -EOPNOTSUPP; 1533cd90214SHalil Pasic errstr = "transport mode"; 1549851bc77SCornelia Huck goto err_out; 1559851bc77SCornelia Huck } 156bbe37e4cSDong Jia Shi io_region->ret_code = cp_init(&private->cp, mdev_dev(mdev), 157bbe37e4cSDong Jia Shi orb); 1583cd90214SHalil Pasic if (io_region->ret_code) { 1593cd90214SHalil Pasic errstr = "cp init"; 160bbe37e4cSDong Jia Shi goto err_out; 1613cd90214SHalil Pasic } 162bbe37e4cSDong Jia Shi 163bbe37e4cSDong Jia Shi io_region->ret_code = cp_prefetch(&private->cp); 164bbe37e4cSDong Jia Shi if (io_region->ret_code) { 1653cd90214SHalil Pasic errstr = "cp prefetch"; 166bbe37e4cSDong Jia Shi cp_free(&private->cp); 167bbe37e4cSDong Jia Shi goto err_out; 168bbe37e4cSDong Jia Shi } 169bbe37e4cSDong Jia Shi 170bbe37e4cSDong Jia Shi /* Start channel program and wait for I/O interrupt. */ 171bbe37e4cSDong Jia Shi io_region->ret_code = fsm_io_helper(private); 172bbe37e4cSDong Jia Shi if (io_region->ret_code) { 1733cd90214SHalil Pasic errstr = "cp fsm_io_helper"; 174bbe37e4cSDong Jia Shi cp_free(&private->cp); 175bbe37e4cSDong Jia Shi goto err_out; 176bbe37e4cSDong Jia Shi } 177bbe37e4cSDong Jia Shi return; 178bbe37e4cSDong Jia Shi } else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) { 179bbe37e4cSDong Jia Shi /* XXX: Handle halt. */ 180bbe37e4cSDong Jia Shi io_region->ret_code = -EOPNOTSUPP; 181bbe37e4cSDong Jia Shi goto err_out; 182bbe37e4cSDong Jia Shi } else if (scsw->cmd.fctl & SCSW_FCTL_CLEAR_FUNC) { 183bbe37e4cSDong Jia Shi /* XXX: Handle clear. */ 184bbe37e4cSDong Jia Shi io_region->ret_code = -EOPNOTSUPP; 185bbe37e4cSDong Jia Shi goto err_out; 186bbe37e4cSDong Jia Shi } 187bbe37e4cSDong Jia Shi 188bbe37e4cSDong Jia Shi err_out: 1893cd90214SHalil Pasic trace_vfio_ccw_io_fctl(scsw->cmd.fctl, get_schid(private), 1903cd90214SHalil Pasic io_region->ret_code, errstr); 191bbe37e4cSDong Jia Shi } 192bbe37e4cSDong Jia Shi 193bbe37e4cSDong Jia Shi /* 194bbe37e4cSDong Jia Shi * Got an interrupt for a normal io (state busy). 195bbe37e4cSDong Jia Shi */ 196bbe37e4cSDong Jia Shi static void fsm_irq(struct vfio_ccw_private *private, 197bbe37e4cSDong Jia Shi enum vfio_ccw_event event) 198bbe37e4cSDong Jia Shi { 199c9c31b07SDong Jia Shi struct irb *irb = this_cpu_ptr(&cio_irb); 200bbe37e4cSDong Jia Shi 201bbe37e4cSDong Jia Shi memcpy(&private->irb, irb, sizeof(*irb)); 202bbe37e4cSDong Jia Shi 203bbe37e4cSDong Jia Shi queue_work(vfio_ccw_work_q, &private->io_work); 204bbe37e4cSDong Jia Shi 205bbe37e4cSDong Jia Shi if (private->completion) 206bbe37e4cSDong Jia Shi complete(private->completion); 207bbe37e4cSDong Jia Shi } 208bbe37e4cSDong Jia Shi 209bbe37e4cSDong Jia Shi /* 210bbe37e4cSDong Jia Shi * Device statemachine 211bbe37e4cSDong Jia Shi */ 212bbe37e4cSDong Jia Shi fsm_func_t *vfio_ccw_jumptable[NR_VFIO_CCW_STATES][NR_VFIO_CCW_EVENTS] = { 213bbe37e4cSDong Jia Shi [VFIO_CCW_STATE_NOT_OPER] = { 214bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_NOT_OPER] = fsm_nop, 215bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_IO_REQ] = fsm_io_error, 216bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_INTERRUPT] = fsm_disabled_irq, 217bbe37e4cSDong Jia Shi }, 218bbe37e4cSDong Jia Shi [VFIO_CCW_STATE_STANDBY] = { 219bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 220bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_IO_REQ] = fsm_io_error, 221bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 222bbe37e4cSDong Jia Shi }, 223bbe37e4cSDong Jia Shi [VFIO_CCW_STATE_IDLE] = { 224bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 225bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_IO_REQ] = fsm_io_request, 226bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 227bbe37e4cSDong Jia Shi }, 228*690f6a15SCornelia Huck [VFIO_CCW_STATE_CP_PROCESSING] = { 229*690f6a15SCornelia Huck [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 230*690f6a15SCornelia Huck [VFIO_CCW_EVENT_IO_REQ] = fsm_io_retry, 231*690f6a15SCornelia Huck [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 232*690f6a15SCornelia Huck }, 233*690f6a15SCornelia Huck [VFIO_CCW_STATE_CP_PENDING] = { 234bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 235bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_IO_REQ] = fsm_io_busy, 236bbe37e4cSDong Jia Shi [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 237bbe37e4cSDong Jia Shi }, 238bbe37e4cSDong Jia Shi }; 239