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