1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Channel path related status regions for vfio_ccw |
4 | * |
5 | * Copyright IBM Corp. 2020 |
6 | * |
7 | * Author(s): Farhan Ali <alifm@linux.ibm.com> |
8 | * Eric Farman <farman@linux.ibm.com> |
9 | */ |
10 | |
11 | #include <linux/slab.h> |
12 | #include <linux/vfio.h> |
13 | #include "vfio_ccw_private.h" |
14 | |
15 | static ssize_t vfio_ccw_schib_region_read(struct vfio_ccw_private *private, |
16 | char __user *buf, size_t count, |
17 | loff_t *ppos) |
18 | { |
19 | struct subchannel *sch = to_subchannel(private->vdev.dev->parent); |
20 | unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS; |
21 | loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; |
22 | struct ccw_schib_region *region; |
23 | int ret; |
24 | |
25 | if (pos + count > sizeof(*region)) |
26 | return -EINVAL; |
27 | |
28 | mutex_lock(&private->io_mutex); |
29 | region = private->region[i].data; |
30 | |
31 | if (cio_update_schib(sch)) { |
32 | ret = -ENODEV; |
33 | goto out; |
34 | } |
35 | |
36 | memcpy(region, &sch->schib, sizeof(*region)); |
37 | |
38 | if (copy_to_user(to: buf, from: (void *)region + pos, n: count)) { |
39 | ret = -EFAULT; |
40 | goto out; |
41 | } |
42 | |
43 | ret = count; |
44 | |
45 | out: |
46 | mutex_unlock(lock: &private->io_mutex); |
47 | return ret; |
48 | } |
49 | |
50 | static ssize_t vfio_ccw_schib_region_write(struct vfio_ccw_private *private, |
51 | const char __user *buf, size_t count, |
52 | loff_t *ppos) |
53 | { |
54 | return -EINVAL; |
55 | } |
56 | |
57 | |
58 | static void vfio_ccw_schib_region_release(struct vfio_ccw_private *private, |
59 | struct vfio_ccw_region *region) |
60 | { |
61 | |
62 | } |
63 | |
64 | static const struct vfio_ccw_regops vfio_ccw_schib_region_ops = { |
65 | .read = vfio_ccw_schib_region_read, |
66 | .write = vfio_ccw_schib_region_write, |
67 | .release = vfio_ccw_schib_region_release, |
68 | }; |
69 | |
70 | int vfio_ccw_register_schib_dev_regions(struct vfio_ccw_private *private) |
71 | { |
72 | return vfio_ccw_register_dev_region(private, |
73 | VFIO_REGION_SUBTYPE_CCW_SCHIB, |
74 | ops: &vfio_ccw_schib_region_ops, |
75 | size: sizeof(struct ccw_schib_region), |
76 | VFIO_REGION_INFO_FLAG_READ, |
77 | data: private->schib_region); |
78 | } |
79 | |
80 | static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private, |
81 | char __user *buf, size_t count, |
82 | loff_t *ppos) |
83 | { |
84 | unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS; |
85 | loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; |
86 | struct ccw_crw_region *region; |
87 | struct vfio_ccw_crw *crw; |
88 | int ret; |
89 | |
90 | if (pos + count > sizeof(*region)) |
91 | return -EINVAL; |
92 | |
93 | crw = list_first_entry_or_null(&private->crw, |
94 | struct vfio_ccw_crw, next); |
95 | |
96 | if (crw) |
97 | list_del(entry: &crw->next); |
98 | |
99 | mutex_lock(&private->io_mutex); |
100 | region = private->region[i].data; |
101 | |
102 | if (crw) |
103 | memcpy(®ion->crw, &crw->crw, sizeof(region->crw)); |
104 | |
105 | if (copy_to_user(to: buf, from: (void *)region + pos, n: count)) |
106 | ret = -EFAULT; |
107 | else |
108 | ret = count; |
109 | |
110 | region->crw = 0; |
111 | |
112 | mutex_unlock(lock: &private->io_mutex); |
113 | |
114 | kfree(objp: crw); |
115 | |
116 | /* Notify the guest if more CRWs are on our queue */ |
117 | if (!list_empty(head: &private->crw) && private->crw_trigger) |
118 | eventfd_signal(ctx: private->crw_trigger); |
119 | |
120 | return ret; |
121 | } |
122 | |
123 | static ssize_t vfio_ccw_crw_region_write(struct vfio_ccw_private *private, |
124 | const char __user *buf, size_t count, |
125 | loff_t *ppos) |
126 | { |
127 | return -EINVAL; |
128 | } |
129 | |
130 | static void vfio_ccw_crw_region_release(struct vfio_ccw_private *private, |
131 | struct vfio_ccw_region *region) |
132 | { |
133 | |
134 | } |
135 | |
136 | static const struct vfio_ccw_regops vfio_ccw_crw_region_ops = { |
137 | .read = vfio_ccw_crw_region_read, |
138 | .write = vfio_ccw_crw_region_write, |
139 | .release = vfio_ccw_crw_region_release, |
140 | }; |
141 | |
142 | int vfio_ccw_register_crw_dev_regions(struct vfio_ccw_private *private) |
143 | { |
144 | return vfio_ccw_register_dev_region(private, |
145 | VFIO_REGION_SUBTYPE_CCW_CRW, |
146 | ops: &vfio_ccw_crw_region_ops, |
147 | size: sizeof(struct ccw_crw_region), |
148 | VFIO_REGION_INFO_FLAG_READ, |
149 | data: private->crw_region); |
150 | } |
151 | |