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