vfio_ccw_fsm.c 4.8 KB

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