Commit | Line | Data |
---|---|---|
7e64e059 CH |
1 | /* |
2 | * ccw based virtio transport | |
3 | * | |
4 | * Copyright IBM Corp. 2012 | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License (version 2 only) | |
8 | * as published by the Free Software Foundation. | |
9 | * | |
10 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> | |
11 | */ | |
12 | ||
13 | #include <linux/kernel_stat.h> | |
14 | #include <linux/init.h> | |
15 | #include <linux/bootmem.h> | |
16 | #include <linux/err.h> | |
17 | #include <linux/virtio.h> | |
18 | #include <linux/virtio_config.h> | |
19 | #include <linux/slab.h> | |
20 | #include <linux/interrupt.h> | |
21 | #include <linux/virtio_ring.h> | |
22 | #include <linux/pfn.h> | |
23 | #include <linux/async.h> | |
24 | #include <linux/wait.h> | |
25 | #include <linux/list.h> | |
26 | #include <linux/bitops.h> | |
27 | #include <linux/module.h> | |
28 | #include <linux/io.h> | |
29 | #include <linux/kvm_para.h> | |
30 | #include <asm/setup.h> | |
31 | #include <asm/irq.h> | |
32 | #include <asm/cio.h> | |
33 | #include <asm/ccwdev.h> | |
6a773cb8 | 34 | #include <asm/virtio-ccw.h> |
7e64e059 CH |
35 | |
36 | /* | |
37 | * virtio related functions | |
38 | */ | |
39 | ||
40 | struct vq_config_block { | |
41 | __u16 index; | |
42 | __u16 num; | |
43 | } __packed; | |
44 | ||
45 | #define VIRTIO_CCW_CONFIG_SIZE 0x100 | |
46 | /* same as PCI config space size, should be enough for all drivers */ | |
47 | ||
48 | struct virtio_ccw_device { | |
49 | struct virtio_device vdev; | |
73fa21ea | 50 | __u8 *status; |
7e64e059 CH |
51 | __u8 config[VIRTIO_CCW_CONFIG_SIZE]; |
52 | struct ccw_device *cdev; | |
7e64e059 CH |
53 | __u32 curr_io; |
54 | int err; | |
55 | wait_queue_head_t wait_q; | |
56 | spinlock_t lock; | |
57 | struct list_head virtqueues; | |
58 | unsigned long indicators; | |
59 | unsigned long indicators2; | |
60 | struct vq_config_block *config_block; | |
61 | }; | |
62 | ||
63 | struct vq_info_block { | |
64 | __u64 queue; | |
65 | __u32 align; | |
66 | __u16 index; | |
67 | __u16 num; | |
68 | } __packed; | |
69 | ||
70 | struct virtio_feature_desc { | |
71 | __u32 features; | |
72 | __u8 index; | |
73 | } __packed; | |
74 | ||
75 | struct virtio_ccw_vq_info { | |
76 | struct virtqueue *vq; | |
77 | int num; | |
78 | void *queue; | |
79 | struct vq_info_block *info_block; | |
80 | struct list_head node; | |
07e16933 | 81 | long cookie; |
7e64e059 CH |
82 | }; |
83 | ||
7e64e059 CH |
84 | #define CCW_CMD_SET_VQ 0x13 |
85 | #define CCW_CMD_VDEV_RESET 0x33 | |
86 | #define CCW_CMD_SET_IND 0x43 | |
87 | #define CCW_CMD_SET_CONF_IND 0x53 | |
88 | #define CCW_CMD_READ_FEAT 0x12 | |
89 | #define CCW_CMD_WRITE_FEAT 0x11 | |
90 | #define CCW_CMD_READ_CONF 0x22 | |
91 | #define CCW_CMD_WRITE_CONF 0x21 | |
92 | #define CCW_CMD_WRITE_STATUS 0x31 | |
93 | #define CCW_CMD_READ_VQ_CONF 0x32 | |
94 | ||
95 | #define VIRTIO_CCW_DOING_SET_VQ 0x00010000 | |
96 | #define VIRTIO_CCW_DOING_RESET 0x00040000 | |
97 | #define VIRTIO_CCW_DOING_READ_FEAT 0x00080000 | |
98 | #define VIRTIO_CCW_DOING_WRITE_FEAT 0x00100000 | |
99 | #define VIRTIO_CCW_DOING_READ_CONFIG 0x00200000 | |
100 | #define VIRTIO_CCW_DOING_WRITE_CONFIG 0x00400000 | |
101 | #define VIRTIO_CCW_DOING_WRITE_STATUS 0x00800000 | |
102 | #define VIRTIO_CCW_DOING_SET_IND 0x01000000 | |
103 | #define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000 | |
104 | #define VIRTIO_CCW_DOING_SET_CONF_IND 0x04000000 | |
105 | #define VIRTIO_CCW_INTPARM_MASK 0xffff0000 | |
106 | ||
107 | static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev) | |
108 | { | |
109 | return container_of(vdev, struct virtio_ccw_device, vdev); | |
110 | } | |
111 | ||
112 | static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag) | |
113 | { | |
114 | unsigned long flags; | |
115 | __u32 ret; | |
116 | ||
117 | spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); | |
118 | if (vcdev->err) | |
119 | ret = 0; | |
120 | else | |
121 | ret = vcdev->curr_io & flag; | |
122 | spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); | |
123 | return ret; | |
124 | } | |
125 | ||
73fa21ea CH |
126 | static int ccw_io_helper(struct virtio_ccw_device *vcdev, |
127 | struct ccw1 *ccw, __u32 intparm) | |
7e64e059 CH |
128 | { |
129 | int ret; | |
130 | unsigned long flags; | |
131 | int flag = intparm & VIRTIO_CCW_INTPARM_MASK; | |
132 | ||
b26ba22b CB |
133 | do { |
134 | spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); | |
135 | ret = ccw_device_start(vcdev->cdev, ccw, intparm, 0, 0); | |
99437a27 CH |
136 | if (!ret) { |
137 | if (!vcdev->curr_io) | |
138 | vcdev->err = 0; | |
b26ba22b | 139 | vcdev->curr_io |= flag; |
99437a27 | 140 | } |
b26ba22b CB |
141 | spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); |
142 | cpu_relax(); | |
143 | } while (ret == -EBUSY); | |
7e64e059 CH |
144 | wait_event(vcdev->wait_q, doing_io(vcdev, flag) == 0); |
145 | return ret ? ret : vcdev->err; | |
146 | } | |
147 | ||
148 | static inline long do_kvm_notify(struct subchannel_id schid, | |
07e16933 MT |
149 | unsigned long queue_index, |
150 | long cookie) | |
7e64e059 CH |
151 | { |
152 | register unsigned long __nr asm("1") = KVM_S390_VIRTIO_CCW_NOTIFY; | |
153 | register struct subchannel_id __schid asm("2") = schid; | |
154 | register unsigned long __index asm("3") = queue_index; | |
155 | register long __rc asm("2"); | |
07e16933 | 156 | register long __cookie asm("4") = cookie; |
7e64e059 CH |
157 | |
158 | asm volatile ("diag 2,4,0x500\n" | |
07e16933 MT |
159 | : "=d" (__rc) : "d" (__nr), "d" (__schid), "d" (__index), |
160 | "d"(__cookie) | |
7e64e059 CH |
161 | : "memory", "cc"); |
162 | return __rc; | |
163 | } | |
164 | ||
46f9c2b9 | 165 | static bool virtio_ccw_kvm_notify(struct virtqueue *vq) |
7e64e059 CH |
166 | { |
167 | struct virtio_ccw_vq_info *info = vq->priv; | |
168 | struct virtio_ccw_device *vcdev; | |
169 | struct subchannel_id schid; | |
170 | ||
171 | vcdev = to_vc_device(info->vq->vdev); | |
172 | ccw_device_get_schid(vcdev->cdev, &schid); | |
01227a88 | 173 | info->cookie = do_kvm_notify(schid, vq->index, info->cookie); |
46f9c2b9 HG |
174 | if (info->cookie < 0) |
175 | return false; | |
176 | return true; | |
7e64e059 CH |
177 | } |
178 | ||
73fa21ea CH |
179 | static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, |
180 | struct ccw1 *ccw, int index) | |
7e64e059 CH |
181 | { |
182 | vcdev->config_block->index = index; | |
73fa21ea CH |
183 | ccw->cmd_code = CCW_CMD_READ_VQ_CONF; |
184 | ccw->flags = 0; | |
185 | ccw->count = sizeof(struct vq_config_block); | |
186 | ccw->cda = (__u32)(unsigned long)(vcdev->config_block); | |
187 | ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_VQ_CONF); | |
7e64e059 CH |
188 | return vcdev->config_block->num; |
189 | } | |
190 | ||
73fa21ea | 191 | static void virtio_ccw_del_vq(struct virtqueue *vq, struct ccw1 *ccw) |
7e64e059 CH |
192 | { |
193 | struct virtio_ccw_device *vcdev = to_vc_device(vq->vdev); | |
194 | struct virtio_ccw_vq_info *info = vq->priv; | |
195 | unsigned long flags; | |
196 | unsigned long size; | |
197 | int ret; | |
9d0ca6ed | 198 | unsigned int index = vq->index; |
7e64e059 CH |
199 | |
200 | /* Remove from our list. */ | |
201 | spin_lock_irqsave(&vcdev->lock, flags); | |
202 | list_del(&info->node); | |
203 | spin_unlock_irqrestore(&vcdev->lock, flags); | |
204 | ||
205 | /* Release from host. */ | |
206 | info->info_block->queue = 0; | |
207 | info->info_block->align = 0; | |
208 | info->info_block->index = index; | |
209 | info->info_block->num = 0; | |
73fa21ea CH |
210 | ccw->cmd_code = CCW_CMD_SET_VQ; |
211 | ccw->flags = 0; | |
212 | ccw->count = sizeof(*info->info_block); | |
213 | ccw->cda = (__u32)(unsigned long)(info->info_block); | |
214 | ret = ccw_io_helper(vcdev, ccw, | |
215 | VIRTIO_CCW_DOING_SET_VQ | index); | |
7e64e059 CH |
216 | /* |
217 | * -ENODEV isn't considered an error: The device is gone anyway. | |
218 | * This may happen on device detach. | |
219 | */ | |
220 | if (ret && (ret != -ENODEV)) | |
221 | dev_warn(&vq->vdev->dev, "Error %d while deleting queue %d", | |
222 | ret, index); | |
223 | ||
224 | vring_del_virtqueue(vq); | |
225 | size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN)); | |
226 | free_pages_exact(info->queue, size); | |
227 | kfree(info->info_block); | |
228 | kfree(info); | |
229 | } | |
230 | ||
231 | static void virtio_ccw_del_vqs(struct virtio_device *vdev) | |
232 | { | |
233 | struct virtqueue *vq, *n; | |
73fa21ea CH |
234 | struct ccw1 *ccw; |
235 | ||
236 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | |
237 | if (!ccw) | |
238 | return; | |
239 | ||
7e64e059 CH |
240 | |
241 | list_for_each_entry_safe(vq, n, &vdev->vqs, list) | |
73fa21ea CH |
242 | virtio_ccw_del_vq(vq, ccw); |
243 | ||
244 | kfree(ccw); | |
7e64e059 CH |
245 | } |
246 | ||
247 | static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, | |
248 | int i, vq_callback_t *callback, | |
73fa21ea CH |
249 | const char *name, |
250 | struct ccw1 *ccw) | |
7e64e059 CH |
251 | { |
252 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | |
253 | int err; | |
c98d3683 | 254 | struct virtqueue *vq = NULL; |
7e64e059 | 255 | struct virtio_ccw_vq_info *info; |
c98d3683 | 256 | unsigned long size = 0; /* silence the compiler */ |
7e64e059 CH |
257 | unsigned long flags; |
258 | ||
259 | /* Allocate queue. */ | |
260 | info = kzalloc(sizeof(struct virtio_ccw_vq_info), GFP_KERNEL); | |
261 | if (!info) { | |
262 | dev_warn(&vcdev->cdev->dev, "no info\n"); | |
263 | err = -ENOMEM; | |
264 | goto out_err; | |
265 | } | |
266 | info->info_block = kzalloc(sizeof(*info->info_block), | |
267 | GFP_DMA | GFP_KERNEL); | |
268 | if (!info->info_block) { | |
269 | dev_warn(&vcdev->cdev->dev, "no info block\n"); | |
270 | err = -ENOMEM; | |
271 | goto out_err; | |
272 | } | |
73fa21ea | 273 | info->num = virtio_ccw_read_vq_conf(vcdev, ccw, i); |
7e64e059 CH |
274 | size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN)); |
275 | info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); | |
276 | if (info->queue == NULL) { | |
277 | dev_warn(&vcdev->cdev->dev, "no queue\n"); | |
278 | err = -ENOMEM; | |
279 | goto out_err; | |
280 | } | |
281 | ||
282 | vq = vring_new_virtqueue(i, info->num, KVM_VIRTIO_CCW_RING_ALIGN, vdev, | |
283 | true, info->queue, virtio_ccw_kvm_notify, | |
284 | callback, name); | |
285 | if (!vq) { | |
286 | /* For now, we fail if we can't get the requested size. */ | |
287 | dev_warn(&vcdev->cdev->dev, "no vq\n"); | |
288 | err = -ENOMEM; | |
7e64e059 CH |
289 | goto out_err; |
290 | } | |
7e64e059 CH |
291 | |
292 | /* Register it with the host. */ | |
293 | info->info_block->queue = (__u64)info->queue; | |
294 | info->info_block->align = KVM_VIRTIO_CCW_RING_ALIGN; | |
295 | info->info_block->index = i; | |
296 | info->info_block->num = info->num; | |
73fa21ea CH |
297 | ccw->cmd_code = CCW_CMD_SET_VQ; |
298 | ccw->flags = 0; | |
299 | ccw->count = sizeof(*info->info_block); | |
300 | ccw->cda = (__u32)(unsigned long)(info->info_block); | |
301 | err = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_VQ | i); | |
7e64e059 CH |
302 | if (err) { |
303 | dev_warn(&vcdev->cdev->dev, "SET_VQ failed\n"); | |
7e64e059 CH |
304 | goto out_err; |
305 | } | |
306 | ||
c98d3683 CH |
307 | info->vq = vq; |
308 | vq->priv = info; | |
309 | ||
7e64e059 CH |
310 | /* Save it to our list. */ |
311 | spin_lock_irqsave(&vcdev->lock, flags); | |
312 | list_add(&info->node, &vcdev->virtqueues); | |
313 | spin_unlock_irqrestore(&vcdev->lock, flags); | |
314 | ||
315 | return vq; | |
316 | ||
317 | out_err: | |
c98d3683 CH |
318 | if (vq) |
319 | vring_del_virtqueue(vq); | |
320 | if (info) { | |
321 | if (info->queue) | |
322 | free_pages_exact(info->queue, size); | |
7e64e059 | 323 | kfree(info->info_block); |
c98d3683 | 324 | } |
7e64e059 CH |
325 | kfree(info); |
326 | return ERR_PTR(err); | |
327 | } | |
328 | ||
329 | static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, | |
330 | struct virtqueue *vqs[], | |
331 | vq_callback_t *callbacks[], | |
332 | const char *names[]) | |
333 | { | |
334 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | |
335 | unsigned long *indicatorp = NULL; | |
336 | int ret, i; | |
73fa21ea CH |
337 | struct ccw1 *ccw; |
338 | ||
339 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | |
340 | if (!ccw) | |
341 | return -ENOMEM; | |
7e64e059 CH |
342 | |
343 | for (i = 0; i < nvqs; ++i) { | |
73fa21ea CH |
344 | vqs[i] = virtio_ccw_setup_vq(vdev, i, callbacks[i], names[i], |
345 | ccw); | |
7e64e059 CH |
346 | if (IS_ERR(vqs[i])) { |
347 | ret = PTR_ERR(vqs[i]); | |
348 | vqs[i] = NULL; | |
349 | goto out; | |
350 | } | |
351 | } | |
352 | ret = -ENOMEM; | |
353 | /* We need a data area under 2G to communicate. */ | |
354 | indicatorp = kmalloc(sizeof(&vcdev->indicators), GFP_DMA | GFP_KERNEL); | |
355 | if (!indicatorp) | |
356 | goto out; | |
357 | *indicatorp = (unsigned long) &vcdev->indicators; | |
358 | /* Register queue indicators with host. */ | |
359 | vcdev->indicators = 0; | |
73fa21ea CH |
360 | ccw->cmd_code = CCW_CMD_SET_IND; |
361 | ccw->flags = 0; | |
362 | ccw->count = sizeof(vcdev->indicators); | |
363 | ccw->cda = (__u32)(unsigned long) indicatorp; | |
364 | ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND); | |
7e64e059 CH |
365 | if (ret) |
366 | goto out; | |
367 | /* Register indicators2 with host for config changes */ | |
368 | *indicatorp = (unsigned long) &vcdev->indicators2; | |
369 | vcdev->indicators2 = 0; | |
73fa21ea CH |
370 | ccw->cmd_code = CCW_CMD_SET_CONF_IND; |
371 | ccw->flags = 0; | |
372 | ccw->count = sizeof(vcdev->indicators2); | |
373 | ccw->cda = (__u32)(unsigned long) indicatorp; | |
374 | ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_CONF_IND); | |
7e64e059 CH |
375 | if (ret) |
376 | goto out; | |
377 | ||
378 | kfree(indicatorp); | |
73fa21ea | 379 | kfree(ccw); |
7e64e059 CH |
380 | return 0; |
381 | out: | |
382 | kfree(indicatorp); | |
73fa21ea | 383 | kfree(ccw); |
7e64e059 CH |
384 | virtio_ccw_del_vqs(vdev); |
385 | return ret; | |
386 | } | |
387 | ||
388 | static void virtio_ccw_reset(struct virtio_device *vdev) | |
389 | { | |
390 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | |
73fa21ea CH |
391 | struct ccw1 *ccw; |
392 | ||
393 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | |
394 | if (!ccw) | |
395 | return; | |
7e64e059 CH |
396 | |
397 | /* Zero status bits. */ | |
73fa21ea | 398 | *vcdev->status = 0; |
7e64e059 CH |
399 | |
400 | /* Send a reset ccw on device. */ | |
73fa21ea CH |
401 | ccw->cmd_code = CCW_CMD_VDEV_RESET; |
402 | ccw->flags = 0; | |
403 | ccw->count = 0; | |
404 | ccw->cda = 0; | |
405 | ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_RESET); | |
406 | kfree(ccw); | |
7e64e059 CH |
407 | } |
408 | ||
409 | static u32 virtio_ccw_get_features(struct virtio_device *vdev) | |
410 | { | |
411 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | |
73fa21ea CH |
412 | struct virtio_feature_desc *features; |
413 | int ret, rc; | |
414 | struct ccw1 *ccw; | |
7e64e059 | 415 | |
73fa21ea CH |
416 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); |
417 | if (!ccw) | |
418 | return 0; | |
419 | ||
420 | features = kzalloc(sizeof(*features), GFP_DMA | GFP_KERNEL); | |
421 | if (!features) { | |
422 | rc = 0; | |
423 | goto out_free; | |
424 | } | |
7e64e059 CH |
425 | /* Read the feature bits from the host. */ |
426 | /* TODO: Features > 32 bits */ | |
73fa21ea CH |
427 | features->index = 0; |
428 | ccw->cmd_code = CCW_CMD_READ_FEAT; | |
429 | ccw->flags = 0; | |
430 | ccw->count = sizeof(*features); | |
431 | ccw->cda = (__u32)(unsigned long)features; | |
432 | ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_FEAT); | |
433 | if (ret) { | |
434 | rc = 0; | |
435 | goto out_free; | |
436 | } | |
437 | ||
438 | rc = le32_to_cpu(features->features); | |
7e64e059 | 439 | |
73fa21ea CH |
440 | out_free: |
441 | kfree(features); | |
442 | kfree(ccw); | |
443 | return rc; | |
7e64e059 CH |
444 | } |
445 | ||
446 | static void virtio_ccw_finalize_features(struct virtio_device *vdev) | |
447 | { | |
448 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | |
73fa21ea | 449 | struct virtio_feature_desc *features; |
7e64e059 | 450 | int i; |
73fa21ea CH |
451 | struct ccw1 *ccw; |
452 | ||
453 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | |
454 | if (!ccw) | |
455 | return; | |
456 | ||
457 | features = kzalloc(sizeof(*features), GFP_DMA | GFP_KERNEL); | |
458 | if (!features) | |
459 | goto out_free; | |
7e64e059 CH |
460 | |
461 | /* Give virtio_ring a chance to accept features. */ | |
462 | vring_transport_features(vdev); | |
463 | ||
73fa21ea | 464 | for (i = 0; i < sizeof(*vdev->features) / sizeof(features->features); |
7e64e059 CH |
465 | i++) { |
466 | int highbits = i % 2 ? 32 : 0; | |
73fa21ea CH |
467 | features->index = i; |
468 | features->features = cpu_to_le32(vdev->features[i / 2] | |
469 | >> highbits); | |
7e64e059 | 470 | /* Write the feature bits to the host. */ |
73fa21ea CH |
471 | ccw->cmd_code = CCW_CMD_WRITE_FEAT; |
472 | ccw->flags = 0; | |
473 | ccw->count = sizeof(*features); | |
474 | ccw->cda = (__u32)(unsigned long)features; | |
475 | ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_FEAT); | |
7e64e059 | 476 | } |
73fa21ea CH |
477 | out_free: |
478 | kfree(features); | |
479 | kfree(ccw); | |
7e64e059 CH |
480 | } |
481 | ||
482 | static void virtio_ccw_get_config(struct virtio_device *vdev, | |
483 | unsigned int offset, void *buf, unsigned len) | |
484 | { | |
485 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | |
486 | int ret; | |
73fa21ea CH |
487 | struct ccw1 *ccw; |
488 | void *config_area; | |
489 | ||
490 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | |
491 | if (!ccw) | |
492 | return; | |
493 | ||
494 | config_area = kzalloc(VIRTIO_CCW_CONFIG_SIZE, GFP_DMA | GFP_KERNEL); | |
495 | if (!config_area) | |
496 | goto out_free; | |
7e64e059 CH |
497 | |
498 | /* Read the config area from the host. */ | |
73fa21ea CH |
499 | ccw->cmd_code = CCW_CMD_READ_CONF; |
500 | ccw->flags = 0; | |
501 | ccw->count = offset + len; | |
502 | ccw->cda = (__u32)(unsigned long)config_area; | |
503 | ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_CONFIG); | |
7e64e059 | 504 | if (ret) |
73fa21ea | 505 | goto out_free; |
7e64e059 | 506 | |
73fa21ea | 507 | memcpy(vcdev->config, config_area, sizeof(vcdev->config)); |
7e64e059 | 508 | memcpy(buf, &vcdev->config[offset], len); |
73fa21ea CH |
509 | |
510 | out_free: | |
511 | kfree(config_area); | |
512 | kfree(ccw); | |
7e64e059 CH |
513 | } |
514 | ||
515 | static void virtio_ccw_set_config(struct virtio_device *vdev, | |
516 | unsigned int offset, const void *buf, | |
517 | unsigned len) | |
518 | { | |
519 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | |
73fa21ea CH |
520 | struct ccw1 *ccw; |
521 | void *config_area; | |
522 | ||
523 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | |
524 | if (!ccw) | |
525 | return; | |
526 | ||
527 | config_area = kzalloc(VIRTIO_CCW_CONFIG_SIZE, GFP_DMA | GFP_KERNEL); | |
528 | if (!config_area) | |
529 | goto out_free; | |
7e64e059 CH |
530 | |
531 | memcpy(&vcdev->config[offset], buf, len); | |
532 | /* Write the config area to the host. */ | |
73fa21ea CH |
533 | memcpy(config_area, vcdev->config, sizeof(vcdev->config)); |
534 | ccw->cmd_code = CCW_CMD_WRITE_CONF; | |
535 | ccw->flags = 0; | |
536 | ccw->count = offset + len; | |
537 | ccw->cda = (__u32)(unsigned long)config_area; | |
538 | ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_CONFIG); | |
539 | ||
540 | out_free: | |
541 | kfree(config_area); | |
542 | kfree(ccw); | |
7e64e059 CH |
543 | } |
544 | ||
545 | static u8 virtio_ccw_get_status(struct virtio_device *vdev) | |
546 | { | |
547 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | |
548 | ||
73fa21ea | 549 | return *vcdev->status; |
7e64e059 CH |
550 | } |
551 | ||
552 | static void virtio_ccw_set_status(struct virtio_device *vdev, u8 status) | |
553 | { | |
554 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | |
73fa21ea CH |
555 | struct ccw1 *ccw; |
556 | ||
557 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | |
558 | if (!ccw) | |
559 | return; | |
7e64e059 CH |
560 | |
561 | /* Write the status to the host. */ | |
73fa21ea CH |
562 | *vcdev->status = status; |
563 | ccw->cmd_code = CCW_CMD_WRITE_STATUS; | |
564 | ccw->flags = 0; | |
565 | ccw->count = sizeof(status); | |
566 | ccw->cda = (__u32)(unsigned long)vcdev->status; | |
567 | ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_STATUS); | |
568 | kfree(ccw); | |
7e64e059 CH |
569 | } |
570 | ||
571 | static struct virtio_config_ops virtio_ccw_config_ops = { | |
572 | .get_features = virtio_ccw_get_features, | |
573 | .finalize_features = virtio_ccw_finalize_features, | |
574 | .get = virtio_ccw_get_config, | |
575 | .set = virtio_ccw_set_config, | |
576 | .get_status = virtio_ccw_get_status, | |
577 | .set_status = virtio_ccw_set_status, | |
578 | .reset = virtio_ccw_reset, | |
579 | .find_vqs = virtio_ccw_find_vqs, | |
580 | .del_vqs = virtio_ccw_del_vqs, | |
581 | }; | |
582 | ||
583 | ||
584 | /* | |
585 | * ccw bus driver related functions | |
586 | */ | |
587 | ||
588 | static void virtio_ccw_release_dev(struct device *_d) | |
589 | { | |
590 | struct virtio_device *dev = container_of(_d, struct virtio_device, | |
591 | dev); | |
592 | struct virtio_ccw_device *vcdev = to_vc_device(dev); | |
593 | ||
73fa21ea | 594 | kfree(vcdev->status); |
7e64e059 | 595 | kfree(vcdev->config_block); |
7e64e059 CH |
596 | kfree(vcdev); |
597 | } | |
598 | ||
599 | static int irb_is_error(struct irb *irb) | |
600 | { | |
601 | if (scsw_cstat(&irb->scsw) != 0) | |
602 | return 1; | |
603 | if (scsw_dstat(&irb->scsw) & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) | |
604 | return 1; | |
605 | if (scsw_cc(&irb->scsw) != 0) | |
606 | return 1; | |
607 | return 0; | |
608 | } | |
609 | ||
610 | static struct virtqueue *virtio_ccw_vq_by_ind(struct virtio_ccw_device *vcdev, | |
611 | int index) | |
612 | { | |
613 | struct virtio_ccw_vq_info *info; | |
614 | unsigned long flags; | |
615 | struct virtqueue *vq; | |
616 | ||
617 | vq = NULL; | |
618 | spin_lock_irqsave(&vcdev->lock, flags); | |
619 | list_for_each_entry(info, &vcdev->virtqueues, node) { | |
9d0ca6ed | 620 | if (info->vq->index == index) { |
7e64e059 CH |
621 | vq = info->vq; |
622 | break; | |
623 | } | |
624 | } | |
625 | spin_unlock_irqrestore(&vcdev->lock, flags); | |
626 | return vq; | |
627 | } | |
628 | ||
629 | static void virtio_ccw_int_handler(struct ccw_device *cdev, | |
630 | unsigned long intparm, | |
631 | struct irb *irb) | |
632 | { | |
633 | __u32 activity = intparm & VIRTIO_CCW_INTPARM_MASK; | |
634 | struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); | |
635 | int i; | |
636 | struct virtqueue *vq; | |
637 | struct virtio_driver *drv; | |
638 | ||
2e021043 HG |
639 | if (!vcdev) |
640 | return; | |
7e64e059 CH |
641 | /* Check if it's a notification from the host. */ |
642 | if ((intparm == 0) && | |
643 | (scsw_stctl(&irb->scsw) == | |
644 | (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))) { | |
645 | /* OK */ | |
646 | } | |
19e4735b CH |
647 | if (irb_is_error(irb)) { |
648 | /* Command reject? */ | |
649 | if ((scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) && | |
650 | (irb->ecw[0] & SNS0_CMD_REJECT)) | |
651 | vcdev->err = -EOPNOTSUPP; | |
652 | else | |
653 | /* Map everything else to -EIO. */ | |
654 | vcdev->err = -EIO; | |
655 | } | |
7e64e059 CH |
656 | if (vcdev->curr_io & activity) { |
657 | switch (activity) { | |
658 | case VIRTIO_CCW_DOING_READ_FEAT: | |
659 | case VIRTIO_CCW_DOING_WRITE_FEAT: | |
660 | case VIRTIO_CCW_DOING_READ_CONFIG: | |
661 | case VIRTIO_CCW_DOING_WRITE_CONFIG: | |
662 | case VIRTIO_CCW_DOING_WRITE_STATUS: | |
663 | case VIRTIO_CCW_DOING_SET_VQ: | |
664 | case VIRTIO_CCW_DOING_SET_IND: | |
665 | case VIRTIO_CCW_DOING_SET_CONF_IND: | |
666 | case VIRTIO_CCW_DOING_RESET: | |
667 | case VIRTIO_CCW_DOING_READ_VQ_CONF: | |
668 | vcdev->curr_io &= ~activity; | |
669 | wake_up(&vcdev->wait_q); | |
670 | break; | |
671 | default: | |
672 | /* don't know what to do... */ | |
673 | dev_warn(&cdev->dev, "Suspicious activity '%08x'\n", | |
674 | activity); | |
675 | WARN_ON(1); | |
676 | break; | |
677 | } | |
678 | } | |
679 | for_each_set_bit(i, &vcdev->indicators, | |
680 | sizeof(vcdev->indicators) * BITS_PER_BYTE) { | |
681 | /* The bit clear must happen before the vring kick. */ | |
682 | clear_bit(i, &vcdev->indicators); | |
683 | barrier(); | |
684 | vq = virtio_ccw_vq_by_ind(vcdev, i); | |
685 | vring_interrupt(0, vq); | |
686 | } | |
687 | if (test_bit(0, &vcdev->indicators2)) { | |
688 | drv = container_of(vcdev->vdev.dev.driver, | |
689 | struct virtio_driver, driver); | |
690 | ||
691 | if (drv && drv->config_changed) | |
692 | drv->config_changed(&vcdev->vdev); | |
693 | clear_bit(0, &vcdev->indicators2); | |
694 | } | |
695 | } | |
696 | ||
697 | /* | |
698 | * We usually want to autoonline all devices, but give the admin | |
699 | * a way to exempt devices from this. | |
700 | */ | |
701 | #define __DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \ | |
702 | (8*sizeof(long))) | |
703 | static unsigned long devs_no_auto[__MAX_SSID + 1][__DEV_WORDS]; | |
704 | ||
705 | static char *no_auto = ""; | |
706 | ||
707 | module_param(no_auto, charp, 0444); | |
708 | MODULE_PARM_DESC(no_auto, "list of ccw bus id ranges not to be auto-onlined"); | |
709 | ||
710 | static int virtio_ccw_check_autoonline(struct ccw_device *cdev) | |
711 | { | |
712 | struct ccw_dev_id id; | |
713 | ||
714 | ccw_device_get_id(cdev, &id); | |
715 | if (test_bit(id.devno, devs_no_auto[id.ssid])) | |
716 | return 0; | |
717 | return 1; | |
718 | } | |
719 | ||
720 | static void virtio_ccw_auto_online(void *data, async_cookie_t cookie) | |
721 | { | |
722 | struct ccw_device *cdev = data; | |
723 | int ret; | |
724 | ||
725 | ret = ccw_device_set_online(cdev); | |
726 | if (ret) | |
727 | dev_warn(&cdev->dev, "Failed to set online: %d\n", ret); | |
728 | } | |
729 | ||
730 | static int virtio_ccw_probe(struct ccw_device *cdev) | |
731 | { | |
732 | cdev->handler = virtio_ccw_int_handler; | |
733 | ||
734 | if (virtio_ccw_check_autoonline(cdev)) | |
735 | async_schedule(virtio_ccw_auto_online, cdev); | |
736 | return 0; | |
737 | } | |
738 | ||
2e021043 HG |
739 | static struct virtio_ccw_device *virtio_grab_drvdata(struct ccw_device *cdev) |
740 | { | |
741 | unsigned long flags; | |
742 | struct virtio_ccw_device *vcdev; | |
743 | ||
744 | spin_lock_irqsave(get_ccwdev_lock(cdev), flags); | |
745 | vcdev = dev_get_drvdata(&cdev->dev); | |
746 | if (!vcdev) { | |
747 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | |
748 | return NULL; | |
749 | } | |
750 | dev_set_drvdata(&cdev->dev, NULL); | |
751 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | |
752 | return vcdev; | |
753 | } | |
754 | ||
7e64e059 CH |
755 | static void virtio_ccw_remove(struct ccw_device *cdev) |
756 | { | |
2e021043 | 757 | struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); |
7e64e059 | 758 | |
2e021043 | 759 | if (vcdev && cdev->online) |
7e64e059 | 760 | unregister_virtio_device(&vcdev->vdev); |
7e64e059 CH |
761 | cdev->handler = NULL; |
762 | } | |
763 | ||
764 | static int virtio_ccw_offline(struct ccw_device *cdev) | |
765 | { | |
2e021043 | 766 | struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); |
7e64e059 | 767 | |
2e021043 HG |
768 | if (vcdev) |
769 | unregister_virtio_device(&vcdev->vdev); | |
7e64e059 CH |
770 | return 0; |
771 | } | |
772 | ||
773 | ||
7e64e059 CH |
774 | static int virtio_ccw_online(struct ccw_device *cdev) |
775 | { | |
776 | int ret; | |
777 | struct virtio_ccw_device *vcdev; | |
2e021043 | 778 | unsigned long flags; |
7e64e059 CH |
779 | |
780 | vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL); | |
781 | if (!vcdev) { | |
782 | dev_warn(&cdev->dev, "Could not get memory for virtio\n"); | |
783 | ret = -ENOMEM; | |
784 | goto out_free; | |
785 | } | |
7e64e059 CH |
786 | vcdev->config_block = kzalloc(sizeof(*vcdev->config_block), |
787 | GFP_DMA | GFP_KERNEL); | |
788 | if (!vcdev->config_block) { | |
789 | ret = -ENOMEM; | |
790 | goto out_free; | |
791 | } | |
73fa21ea CH |
792 | vcdev->status = kzalloc(sizeof(*vcdev->status), GFP_DMA | GFP_KERNEL); |
793 | if (!vcdev->status) { | |
7e64e059 CH |
794 | ret = -ENOMEM; |
795 | goto out_free; | |
796 | } | |
797 | ||
798 | vcdev->vdev.dev.parent = &cdev->dev; | |
799 | vcdev->vdev.dev.release = virtio_ccw_release_dev; | |
800 | vcdev->vdev.config = &virtio_ccw_config_ops; | |
801 | vcdev->cdev = cdev; | |
802 | init_waitqueue_head(&vcdev->wait_q); | |
803 | INIT_LIST_HEAD(&vcdev->virtqueues); | |
804 | spin_lock_init(&vcdev->lock); | |
805 | ||
2e021043 | 806 | spin_lock_irqsave(get_ccwdev_lock(cdev), flags); |
7e64e059 | 807 | dev_set_drvdata(&cdev->dev, vcdev); |
2e021043 | 808 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); |
7e64e059 CH |
809 | vcdev->vdev.id.vendor = cdev->id.cu_type; |
810 | vcdev->vdev.id.device = cdev->id.cu_model; | |
811 | ret = register_virtio_device(&vcdev->vdev); | |
812 | if (ret) { | |
813 | dev_warn(&cdev->dev, "Failed to register virtio device: %d\n", | |
814 | ret); | |
815 | goto out_put; | |
816 | } | |
817 | return 0; | |
818 | out_put: | |
2e021043 | 819 | spin_lock_irqsave(get_ccwdev_lock(cdev), flags); |
7e64e059 | 820 | dev_set_drvdata(&cdev->dev, NULL); |
2e021043 | 821 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); |
7e64e059 CH |
822 | put_device(&vcdev->vdev.dev); |
823 | return ret; | |
824 | out_free: | |
825 | if (vcdev) { | |
73fa21ea | 826 | kfree(vcdev->status); |
7e64e059 | 827 | kfree(vcdev->config_block); |
7e64e059 CH |
828 | } |
829 | kfree(vcdev); | |
830 | return ret; | |
831 | } | |
832 | ||
833 | static int virtio_ccw_cio_notify(struct ccw_device *cdev, int event) | |
834 | { | |
835 | /* TODO: Check whether we need special handling here. */ | |
836 | return 0; | |
837 | } | |
838 | ||
839 | static struct ccw_device_id virtio_ids[] = { | |
840 | { CCW_DEVICE(0x3832, 0) }, | |
841 | {}, | |
842 | }; | |
843 | MODULE_DEVICE_TABLE(ccw, virtio_ids); | |
844 | ||
845 | static struct ccw_driver virtio_ccw_driver = { | |
846 | .driver = { | |
847 | .owner = THIS_MODULE, | |
848 | .name = "virtio_ccw", | |
849 | }, | |
850 | .ids = virtio_ids, | |
851 | .probe = virtio_ccw_probe, | |
852 | .remove = virtio_ccw_remove, | |
853 | .set_offline = virtio_ccw_offline, | |
854 | .set_online = virtio_ccw_online, | |
855 | .notify = virtio_ccw_cio_notify, | |
89f88337 | 856 | .int_class = IRQIO_VIR, |
7e64e059 CH |
857 | }; |
858 | ||
859 | static int __init pure_hex(char **cp, unsigned int *val, int min_digit, | |
860 | int max_digit, int max_val) | |
861 | { | |
862 | int diff; | |
863 | ||
864 | diff = 0; | |
865 | *val = 0; | |
866 | ||
867 | while (diff <= max_digit) { | |
868 | int value = hex_to_bin(**cp); | |
869 | ||
870 | if (value < 0) | |
871 | break; | |
872 | *val = *val * 16 + value; | |
873 | (*cp)++; | |
874 | diff++; | |
875 | } | |
876 | ||
877 | if ((diff < min_digit) || (diff > max_digit) || (*val > max_val)) | |
878 | return 1; | |
879 | ||
880 | return 0; | |
881 | } | |
882 | ||
883 | static int __init parse_busid(char *str, unsigned int *cssid, | |
884 | unsigned int *ssid, unsigned int *devno) | |
885 | { | |
886 | char *str_work; | |
887 | int rc, ret; | |
888 | ||
889 | rc = 1; | |
890 | ||
891 | if (*str == '\0') | |
892 | goto out; | |
893 | ||
894 | str_work = str; | |
895 | ret = pure_hex(&str_work, cssid, 1, 2, __MAX_CSSID); | |
896 | if (ret || (str_work[0] != '.')) | |
897 | goto out; | |
898 | str_work++; | |
899 | ret = pure_hex(&str_work, ssid, 1, 1, __MAX_SSID); | |
900 | if (ret || (str_work[0] != '.')) | |
901 | goto out; | |
902 | str_work++; | |
903 | ret = pure_hex(&str_work, devno, 4, 4, __MAX_SUBCHANNEL); | |
904 | if (ret || (str_work[0] != '\0')) | |
905 | goto out; | |
906 | ||
907 | rc = 0; | |
908 | out: | |
909 | return rc; | |
910 | } | |
911 | ||
912 | static void __init no_auto_parse(void) | |
913 | { | |
914 | unsigned int from_cssid, to_cssid, from_ssid, to_ssid, from, to; | |
915 | char *parm, *str; | |
916 | int rc; | |
917 | ||
918 | str = no_auto; | |
919 | while ((parm = strsep(&str, ","))) { | |
920 | rc = parse_busid(strsep(&parm, "-"), &from_cssid, | |
921 | &from_ssid, &from); | |
922 | if (rc) | |
923 | continue; | |
924 | if (parm != NULL) { | |
925 | rc = parse_busid(parm, &to_cssid, | |
926 | &to_ssid, &to); | |
927 | if ((from_ssid > to_ssid) || | |
928 | ((from_ssid == to_ssid) && (from > to))) | |
929 | rc = -EINVAL; | |
930 | } else { | |
931 | to_cssid = from_cssid; | |
932 | to_ssid = from_ssid; | |
933 | to = from; | |
934 | } | |
935 | if (rc) | |
936 | continue; | |
937 | while ((from_ssid < to_ssid) || | |
938 | ((from_ssid == to_ssid) && (from <= to))) { | |
939 | set_bit(from, devs_no_auto[from_ssid]); | |
940 | from++; | |
941 | if (from > __MAX_SUBCHANNEL) { | |
942 | from_ssid++; | |
943 | from = 0; | |
944 | } | |
945 | } | |
946 | } | |
947 | } | |
948 | ||
949 | static int __init virtio_ccw_init(void) | |
950 | { | |
951 | /* parse no_auto string before we do anything further */ | |
952 | no_auto_parse(); | |
953 | return ccw_driver_register(&virtio_ccw_driver); | |
954 | } | |
955 | module_init(virtio_ccw_init); | |
956 | ||
957 | static void __exit virtio_ccw_exit(void) | |
958 | { | |
959 | ccw_driver_unregister(&virtio_ccw_driver); | |
960 | } | |
961 | module_exit(virtio_ccw_exit); |