1
2
3
4
5
6
7
8
9
10#include <linux/init.h>
11#include <linux/bootmem.h>
12#include <linux/lguest_launcher.h>
13#include <linux/virtio.h>
14#include <linux/virtio_config.h>
15#include <linux/interrupt.h>
16#include <linux/virtio_ring.h>
17#include <linux/err.h>
18#include <linux/slab.h>
19#include <asm/io.h>
20#include <asm/paravirt.h>
21#include <asm/lguest_hcall.h>
22
23
24static void *lguest_devices;
25
26
27
28
29
30static inline void *lguest_map(unsigned long phys_addr, unsigned long pages)
31{
32 return (__force void *)ioremap_cache(phys_addr, PAGE_SIZE*pages);
33}
34
35static inline void lguest_unmap(void *addr)
36{
37 iounmap((__force void __iomem *)addr);
38}
39
40
41
42
43
44struct lguest_device {
45 struct virtio_device vdev;
46
47
48 struct lguest_device_desc *desc;
49};
50
51
52
53
54
55
56#define to_lgdev(vd) container_of(vd, struct lguest_device, vdev)
57
58
59
60
61
62
63
64
65
66
67
68
69static struct lguest_vqconfig *lg_vq(const struct lguest_device_desc *desc)
70{
71 return (void *)(desc + 1);
72}
73
74
75static u8 *lg_features(const struct lguest_device_desc *desc)
76{
77 return (void *)(lg_vq(desc) + desc->num_vq);
78}
79
80
81static u8 *lg_config(const struct lguest_device_desc *desc)
82{
83 return lg_features(desc) + desc->feature_len * 2;
84}
85
86
87static unsigned desc_size(const struct lguest_device_desc *desc)
88{
89 return sizeof(*desc)
90 + desc->num_vq * sizeof(struct lguest_vqconfig)
91 + desc->feature_len * 2
92 + desc->config_len;
93}
94
95
96static u32 lg_get_features(struct virtio_device *vdev)
97{
98 unsigned int i;
99 u32 features = 0;
100 struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
101 u8 *in_features = lg_features(desc);
102
103
104 for (i = 0; i < min(desc->feature_len * 8, 32); i++)
105 if (in_features[i / 8] & (1 << (i % 8)))
106 features |= (1 << i);
107
108 return features;
109}
110
111
112
113
114
115
116
117static void lg_finalize_features(struct virtio_device *vdev)
118{
119 unsigned int i, bits;
120 struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
121
122 u8 *out_features = lg_features(desc) + desc->feature_len;
123
124
125 vring_transport_features(vdev);
126
127
128
129
130
131
132 memset(out_features, 0, desc->feature_len);
133 bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8;
134 for (i = 0; i < bits; i++) {
135 if (test_bit(i, vdev->features))
136 out_features[i / 8] |= (1 << (i % 8));
137 }
138}
139
140
141static void lg_get(struct virtio_device *vdev, unsigned int offset,
142 void *buf, unsigned len)
143{
144 struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
145
146
147 BUG_ON(offset + len > desc->config_len);
148 memcpy(buf, lg_config(desc) + offset, len);
149}
150
151
152static void lg_set(struct virtio_device *vdev, unsigned int offset,
153 const void *buf, unsigned len)
154{
155 struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
156
157
158 BUG_ON(offset + len > desc->config_len);
159 memcpy(lg_config(desc) + offset, buf, len);
160}
161
162
163
164
165
166static u8 lg_get_status(struct virtio_device *vdev)
167{
168 return to_lgdev(vdev)->desc->status;
169}
170
171
172
173
174
175static void set_status(struct virtio_device *vdev, u8 status)
176{
177 unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices;
178
179
180 to_lgdev(vdev)->desc->status = status;
181 hcall(LHCALL_NOTIFY, (max_pfn << PAGE_SHIFT) + offset, 0, 0, 0);
182}
183
184static void lg_set_status(struct virtio_device *vdev, u8 status)
185{
186 BUG_ON(!status);
187 set_status(vdev, status);
188}
189
190static void lg_reset(struct virtio_device *vdev)
191{
192 set_status(vdev, 0);
193}
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211struct lguest_vq_info {
212
213 struct lguest_vqconfig config;
214
215
216 void *pages;
217};
218
219
220
221
222
223
224static void lg_notify(struct virtqueue *vq)
225{
226
227
228
229
230 struct lguest_vq_info *lvq = vq->priv;
231
232 hcall(LHCALL_NOTIFY, lvq->config.pfn << PAGE_SHIFT, 0, 0, 0);
233}
234
235
236extern void lguest_setup_irq(unsigned int irq);
237
238
239
240
241
242
243
244
245
246
247
248static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
249 unsigned index,
250 void (*callback)(struct virtqueue *vq),
251 const char *name)
252{
253 struct lguest_device *ldev = to_lgdev(vdev);
254 struct lguest_vq_info *lvq;
255 struct virtqueue *vq;
256 int err;
257
258
259 if (index >= ldev->desc->num_vq)
260 return ERR_PTR(-ENOENT);
261
262 lvq = kmalloc(sizeof(*lvq), GFP_KERNEL);
263 if (!lvq)
264 return ERR_PTR(-ENOMEM);
265
266
267
268
269
270
271 memcpy(&lvq->config, lg_vq(ldev->desc)+index, sizeof(lvq->config));
272
273 printk("Mapping virtqueue %i addr %lx\n", index,
274 (unsigned long)lvq->config.pfn << PAGE_SHIFT);
275
276 lvq->pages = lguest_map((unsigned long)lvq->config.pfn << PAGE_SHIFT,
277 DIV_ROUND_UP(vring_size(lvq->config.num,
278 LGUEST_VRING_ALIGN),
279 PAGE_SIZE));
280 if (!lvq->pages) {
281 err = -ENOMEM;
282 goto free_lvq;
283 }
284
285
286
287
288
289 vq = vring_new_virtqueue(lvq->config.num, LGUEST_VRING_ALIGN,
290 vdev, lvq->pages, lg_notify, callback, name);
291 if (!vq) {
292 err = -ENOMEM;
293 goto unmap;
294 }
295
296
297 lguest_setup_irq(lvq->config.irq);
298
299
300
301
302
303
304
305
306
307 err = request_irq(lvq->config.irq, vring_interrupt, IRQF_SHARED,
308 dev_name(&vdev->dev), vq);
309 if (err)
310 goto destroy_vring;
311
312
313
314
315
316 vq->priv = lvq;
317 return vq;
318
319destroy_vring:
320 vring_del_virtqueue(vq);
321unmap:
322 lguest_unmap(lvq->pages);
323free_lvq:
324 kfree(lvq);
325 return ERR_PTR(err);
326}
327
328
329
330static void lg_del_vq(struct virtqueue *vq)
331{
332 struct lguest_vq_info *lvq = vq->priv;
333
334
335 free_irq(lvq->config.irq, vq);
336
337 vring_del_virtqueue(vq);
338
339 lguest_unmap(lvq->pages);
340
341 kfree(lvq);
342}
343
344static void lg_del_vqs(struct virtio_device *vdev)
345{
346 struct virtqueue *vq, *n;
347
348 list_for_each_entry_safe(vq, n, &vdev->vqs, list)
349 lg_del_vq(vq);
350}
351
352static int lg_find_vqs(struct virtio_device *vdev, unsigned nvqs,
353 struct virtqueue *vqs[],
354 vq_callback_t *callbacks[],
355 const char *names[])
356{
357 struct lguest_device *ldev = to_lgdev(vdev);
358 int i;
359
360
361 if (nvqs > ldev->desc->num_vq)
362 return -ENOENT;
363
364 for (i = 0; i < nvqs; ++i) {
365 vqs[i] = lg_find_vq(vdev, i, callbacks[i], names[i]);
366 if (IS_ERR(vqs[i]))
367 goto error;
368 }
369 return 0;
370
371error:
372 lg_del_vqs(vdev);
373 return PTR_ERR(vqs[i]);
374}
375
376
377static struct virtio_config_ops lguest_config_ops = {
378 .get_features = lg_get_features,
379 .finalize_features = lg_finalize_features,
380 .get = lg_get,
381 .set = lg_set,
382 .get_status = lg_get_status,
383 .set_status = lg_set_status,
384 .reset = lg_reset,
385 .find_vqs = lg_find_vqs,
386 .del_vqs = lg_del_vqs,
387};
388
389
390
391
392
393static struct device *lguest_root;
394
395
396
397
398
399
400
401
402
403
404
405
406
407static void add_lguest_device(struct lguest_device_desc *d,
408 unsigned int offset)
409{
410 struct lguest_device *ldev;
411
412
413 ldev = kzalloc(sizeof(*ldev), GFP_KERNEL);
414 if (!ldev) {
415 printk(KERN_EMERG "Cannot allocate lguest dev %u type %u\n",
416 offset, d->type);
417 return;
418 }
419
420
421 ldev->vdev.dev.parent = lguest_root;
422
423
424
425
426
427 ldev->vdev.id.device = d->type;
428
429
430
431
432 ldev->vdev.config = &lguest_config_ops;
433
434 ldev->desc = d;
435
436
437
438
439
440
441 if (register_virtio_device(&ldev->vdev) != 0) {
442 printk(KERN_ERR "Failed to register lguest dev %u type %u\n",
443 offset, d->type);
444 kfree(ldev);
445 }
446}
447
448
449
450
451
452static void scan_devices(void)
453{
454 unsigned int i;
455 struct lguest_device_desc *d;
456
457
458 for (i = 0; i < PAGE_SIZE; i += desc_size(d)) {
459 d = lguest_devices + i;
460
461
462 if (d->type == 0)
463 break;
464
465 printk("Device at %i has size %u\n", i, desc_size(d));
466 add_lguest_device(d, i);
467 }
468}
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484static int __init lguest_devices_init(void)
485{
486 if (strcmp(pv_info.name, "lguest") != 0)
487 return 0;
488
489 lguest_root = root_device_register("lguest");
490 if (IS_ERR(lguest_root))
491 panic("Could not register lguest root");
492
493
494 lguest_devices = lguest_map(max_pfn<<PAGE_SHIFT, 1);
495
496 scan_devices();
497 return 0;
498}
499
500postcore_initcall(lguest_devices_init);
501
502
503
504
505
506
507
508
509
510
511
512