blob: 797a82731159a5f9f584810f924adc3467b1e702 [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Dong Jia Shibbe37e42017-03-17 04:17:40 +01002/*
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
10#include <linux/vfio.h>
11#include <linux/mdev.h>
12
13#include "ioasm.h"
14#include "vfio_ccw_private.h"
15
Halil Pasic3cd90212018-05-23 04:56:45 +020016#define CREATE_TRACE_POINTS
17#include "vfio_ccw_trace.h"
18
Dong Jia Shibbe37e42017-03-17 04:17:40 +010019static int fsm_io_helper(struct vfio_ccw_private *private)
20{
21 struct subchannel *sch;
22 union orb *orb;
23 int ccode;
24 __u8 lpm;
25 unsigned long flags;
Cornelia Huck3368e542018-04-20 10:24:04 +020026 int ret;
Dong Jia Shibbe37e42017-03-17 04:17:40 +010027
28 sch = private->sch;
29
30 spin_lock_irqsave(sch->lock, flags);
31 private->state = VFIO_CCW_STATE_BUSY;
Dong Jia Shibbe37e42017-03-17 04:17:40 +010032
33 orb = cp_get_orb(&private->cp, (u32)(addr_t)sch, sch->lpm);
34
35 /* Issue "Start Subchannel" */
36 ccode = ssch(sch->schid, orb);
37
38 switch (ccode) {
39 case 0:
40 /*
41 * Initialize device status information
42 */
43 sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
Cornelia Huck3368e542018-04-20 10:24:04 +020044 ret = 0;
45 break;
Dong Jia Shibbe37e42017-03-17 04:17:40 +010046 case 1: /* Status pending */
47 case 2: /* Busy */
Cornelia Huck3368e542018-04-20 10:24:04 +020048 ret = -EBUSY;
49 break;
Dong Jia Shibbe37e42017-03-17 04:17:40 +010050 case 3: /* Device/path not operational */
51 {
52 lpm = orb->cmd.lpm;
53 if (lpm != 0)
54 sch->lpm &= ~lpm;
55 else
56 sch->lpm = 0;
57
58 if (cio_update_schib(sch))
Cornelia Huck3368e542018-04-20 10:24:04 +020059 ret = -ENODEV;
60 else
61 ret = sch->lpm ? -EACCES : -ENODEV;
62 break;
Dong Jia Shibbe37e42017-03-17 04:17:40 +010063 }
64 default:
Cornelia Huck3368e542018-04-20 10:24:04 +020065 ret = ccode;
Dong Jia Shibbe37e42017-03-17 04:17:40 +010066 }
Cornelia Huck3368e542018-04-20 10:24:04 +020067 spin_unlock_irqrestore(sch->lock, flags);
68 return ret;
Dong Jia Shibbe37e42017-03-17 04:17:40 +010069}
70
71static void fsm_notoper(struct vfio_ccw_private *private,
72 enum vfio_ccw_event event)
73{
74 struct subchannel *sch = private->sch;
75
76 /*
77 * TODO:
78 * Probably we should send the machine check to the guest.
79 */
80 css_sched_sch_todo(sch, SCH_TODO_UNREG);
81 private->state = VFIO_CCW_STATE_NOT_OPER;
82}
83
84/*
85 * No operation action.
86 */
87static void fsm_nop(struct vfio_ccw_private *private,
88 enum vfio_ccw_event event)
89{
90}
91
92static void fsm_io_error(struct vfio_ccw_private *private,
93 enum vfio_ccw_event event)
94{
95 pr_err("vfio-ccw: FSM: I/O request from state:%d\n", private->state);
96 private->io_region.ret_code = -EIO;
97}
98
99static void fsm_io_busy(struct vfio_ccw_private *private,
100 enum vfio_ccw_event event)
101{
102 private->io_region.ret_code = -EBUSY;
103}
104
105static void fsm_disabled_irq(struct vfio_ccw_private *private,
106 enum vfio_ccw_event event)
107{
108 struct subchannel *sch = private->sch;
109
110 /*
111 * An interrupt in a disabled state means a previous disable was not
112 * successful - should not happen, but we try to disable again.
113 */
114 cio_disable_subchannel(sch);
115}
Halil Pasic3cd90212018-05-23 04:56:45 +0200116inline struct subchannel_id get_schid(struct vfio_ccw_private *p)
117{
118 return p->sch->schid;
119}
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100120
121/*
122 * Deal with the ccw command request from the userspace.
123 */
124static void fsm_io_request(struct vfio_ccw_private *private,
125 enum vfio_ccw_event event)
126{
127 union orb *orb;
128 union scsw *scsw = &private->scsw;
129 struct ccw_io_region *io_region = &private->io_region;
130 struct mdev_device *mdev = private->mdev;
Halil Pasic3cd90212018-05-23 04:56:45 +0200131 char *errstr = "request";
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100132
133 private->state = VFIO_CCW_STATE_BOXED;
134
135 memcpy(scsw, io_region->scsw_area, sizeof(*scsw));
136
137 if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) {
138 orb = (union orb *)io_region->orb_area;
139
Cornelia Huck9851bc72018-02-22 15:35:43 +0100140 /* Don't try to build a cp if transport mode is specified. */
141 if (orb->tm.b) {
142 io_region->ret_code = -EOPNOTSUPP;
Halil Pasic3cd90212018-05-23 04:56:45 +0200143 errstr = "transport mode";
Cornelia Huck9851bc72018-02-22 15:35:43 +0100144 goto err_out;
145 }
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100146 io_region->ret_code = cp_init(&private->cp, mdev_dev(mdev),
147 orb);
Halil Pasic3cd90212018-05-23 04:56:45 +0200148 if (io_region->ret_code) {
149 errstr = "cp init";
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100150 goto err_out;
Halil Pasic3cd90212018-05-23 04:56:45 +0200151 }
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100152
153 io_region->ret_code = cp_prefetch(&private->cp);
154 if (io_region->ret_code) {
Halil Pasic3cd90212018-05-23 04:56:45 +0200155 errstr = "cp prefetch";
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100156 cp_free(&private->cp);
157 goto err_out;
158 }
159
160 /* Start channel program and wait for I/O interrupt. */
161 io_region->ret_code = fsm_io_helper(private);
162 if (io_region->ret_code) {
Halil Pasic3cd90212018-05-23 04:56:45 +0200163 errstr = "cp fsm_io_helper";
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100164 cp_free(&private->cp);
165 goto err_out;
166 }
167 return;
168 } else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) {
169 /* XXX: Handle halt. */
170 io_region->ret_code = -EOPNOTSUPP;
171 goto err_out;
172 } else if (scsw->cmd.fctl & SCSW_FCTL_CLEAR_FUNC) {
173 /* XXX: Handle clear. */
174 io_region->ret_code = -EOPNOTSUPP;
175 goto err_out;
176 }
177
178err_out:
179 private->state = VFIO_CCW_STATE_IDLE;
Halil Pasic3cd90212018-05-23 04:56:45 +0200180 trace_vfio_ccw_io_fctl(scsw->cmd.fctl, get_schid(private),
181 io_region->ret_code, errstr);
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100182}
183
184/*
185 * Got an interrupt for a normal io (state busy).
186 */
187static void fsm_irq(struct vfio_ccw_private *private,
188 enum vfio_ccw_event event)
189{
Dong Jia Shic9c31b02017-04-12 11:08:15 +0200190 struct irb *irb = this_cpu_ptr(&cio_irb);
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100191
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100192 memcpy(&private->irb, irb, sizeof(*irb));
193
194 queue_work(vfio_ccw_work_q, &private->io_work);
195
196 if (private->completion)
197 complete(private->completion);
198}
199
200/*
201 * Device statemachine
202 */
203fsm_func_t *vfio_ccw_jumptable[NR_VFIO_CCW_STATES][NR_VFIO_CCW_EVENTS] = {
204 [VFIO_CCW_STATE_NOT_OPER] = {
205 [VFIO_CCW_EVENT_NOT_OPER] = fsm_nop,
206 [VFIO_CCW_EVENT_IO_REQ] = fsm_io_error,
207 [VFIO_CCW_EVENT_INTERRUPT] = fsm_disabled_irq,
208 },
209 [VFIO_CCW_STATE_STANDBY] = {
210 [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper,
211 [VFIO_CCW_EVENT_IO_REQ] = fsm_io_error,
212 [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq,
213 },
214 [VFIO_CCW_STATE_IDLE] = {
215 [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper,
216 [VFIO_CCW_EVENT_IO_REQ] = fsm_io_request,
217 [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq,
218 },
219 [VFIO_CCW_STATE_BOXED] = {
220 [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper,
221 [VFIO_CCW_EVENT_IO_REQ] = fsm_io_busy,
222 [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq,
223 },
224 [VFIO_CCW_STATE_BUSY] = {
225 [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper,
226 [VFIO_CCW_EVENT_IO_REQ] = fsm_io_busy,
227 [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq,
228 },
229};