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