1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/init.h>
28#include <linux/types.h>
29#include <linux/err.h>
30#include <linux/fs.h>
31#include <linux/miscdevice.h>
32#include <linux/mm.h>
33#include <linux/pagemap.h>
34#include <linux/slab.h>
35#include <linux/poll.h>
36#include <linux/of.h>
37#include <linux/of_irq.h>
38#include <linux/reboot.h>
39#include <linux/uaccess.h>
40#include <linux/notifier.h>
41#include <linux/interrupt.h>
42
43#include <linux/io.h>
44#include <asm/fsl_hcalls.h>
45
46#include <linux/fsl_hypervisor.h>
47
48static BLOCKING_NOTIFIER_HEAD(failover_subscribers);
49
50
51
52
53
54
55static long ioctl_restart(struct fsl_hv_ioctl_restart __user *p)
56{
57 struct fsl_hv_ioctl_restart param;
58
59
60 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_restart)))
61 return -EFAULT;
62
63 param.ret = fh_partition_restart(param.partition);
64
65 if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
66 return -EFAULT;
67
68 return 0;
69}
70
71
72
73
74
75
76static long ioctl_status(struct fsl_hv_ioctl_status __user *p)
77{
78 struct fsl_hv_ioctl_status param;
79 u32 status;
80
81
82 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_status)))
83 return -EFAULT;
84
85 param.ret = fh_partition_get_status(param.partition, &status);
86 if (!param.ret)
87 param.status = status;
88
89 if (copy_to_user(p, ¶m, sizeof(struct fsl_hv_ioctl_status)))
90 return -EFAULT;
91
92 return 0;
93}
94
95
96
97
98
99
100static long ioctl_start(struct fsl_hv_ioctl_start __user *p)
101{
102 struct fsl_hv_ioctl_start param;
103
104
105 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_start)))
106 return -EFAULT;
107
108 param.ret = fh_partition_start(param.partition, param.entry_point,
109 param.load);
110
111 if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
112 return -EFAULT;
113
114 return 0;
115}
116
117
118
119
120
121
122static long ioctl_stop(struct fsl_hv_ioctl_stop __user *p)
123{
124 struct fsl_hv_ioctl_stop param;
125
126
127 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_stop)))
128 return -EFAULT;
129
130 param.ret = fh_partition_stop(param.partition);
131
132 if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
133 return -EFAULT;
134
135 return 0;
136}
137
138
139
140
141
142
143
144
145
146
147static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p)
148{
149 struct fsl_hv_ioctl_memcpy param;
150
151 struct page **pages = NULL;
152 void *sg_list_unaligned = NULL;
153 struct fh_sg_list *sg_list = NULL;
154
155 unsigned int num_pages;
156 unsigned long lb_offset;
157
158 unsigned int i;
159 long ret = 0;
160 int num_pinned;
161 phys_addr_t remote_paddr;
162 uint32_t count;
163
164
165 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_memcpy)))
166 return -EFAULT;
167
168
169
170
171
172
173 if ((param.source == -1) == (param.target == -1))
174 return -EINVAL;
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217 lb_offset = param.local_vaddr & (PAGE_SIZE - 1);
218 num_pages = (param.count + lb_offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
219
220
221
222
223
224
225
226 pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL);
227 if (!pages) {
228 pr_debug("fsl-hv: could not allocate page list\n");
229 return -ENOMEM;
230 }
231
232
233
234
235
236 sg_list_unaligned = kmalloc(num_pages * sizeof(struct fh_sg_list) +
237 sizeof(struct fh_sg_list) - 1, GFP_KERNEL);
238 if (!sg_list_unaligned) {
239 pr_debug("fsl-hv: could not allocate S/G list\n");
240 ret = -ENOMEM;
241 goto exit;
242 }
243 sg_list = PTR_ALIGN(sg_list_unaligned, sizeof(struct fh_sg_list));
244
245
246 num_pinned = get_user_pages_fast(param.local_vaddr - lb_offset,
247 num_pages, param.source != -1, pages);
248
249 if (num_pinned != num_pages) {
250
251 pr_debug("fsl-hv: could not lock source buffer\n");
252 ret = (num_pinned < 0) ? num_pinned : -EFAULT;
253 goto exit;
254 }
255
256
257
258
259
260 if (param.source == -1) {
261 sg_list[0].source = page_to_phys(pages[0]) + lb_offset;
262 sg_list[0].target = param.remote_paddr;
263 } else {
264 sg_list[0].source = param.remote_paddr;
265 sg_list[0].target = page_to_phys(pages[0]) + lb_offset;
266 }
267 sg_list[0].size = min_t(uint64_t, param.count, PAGE_SIZE - lb_offset);
268
269 remote_paddr = param.remote_paddr + sg_list[0].size;
270 count = param.count - sg_list[0].size;
271
272 for (i = 1; i < num_pages; i++) {
273 if (param.source == -1) {
274
275 sg_list[i].source = page_to_phys(pages[i]);
276 sg_list[i].target = remote_paddr;
277 } else {
278
279 sg_list[i].source = remote_paddr;
280 sg_list[i].target = page_to_phys(pages[i]);
281 }
282 sg_list[i].size = min_t(uint64_t, count, PAGE_SIZE);
283
284 remote_paddr += sg_list[i].size;
285 count -= sg_list[i].size;
286 }
287
288 param.ret = fh_partition_memcpy(param.source, param.target,
289 virt_to_phys(sg_list), num_pages);
290
291exit:
292 if (pages) {
293 for (i = 0; i < num_pages; i++)
294 if (pages[i])
295 put_page(pages[i]);
296 }
297
298 kfree(sg_list_unaligned);
299 kfree(pages);
300
301 if (!ret)
302 if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
303 return -EFAULT;
304
305 return ret;
306}
307
308
309
310
311
312
313static long ioctl_doorbell(struct fsl_hv_ioctl_doorbell __user *p)
314{
315 struct fsl_hv_ioctl_doorbell param;
316
317
318 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_doorbell)))
319 return -EFAULT;
320
321 param.ret = ev_doorbell_send(param.doorbell);
322
323 if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
324 return -EFAULT;
325
326 return 0;
327}
328
329static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user *p, int set)
330{
331 struct fsl_hv_ioctl_prop param;
332 char __user *upath, *upropname;
333 void __user *upropval;
334 char *path = NULL, *propname = NULL;
335 void *propval = NULL;
336 int ret = 0;
337
338
339 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_prop)))
340 return -EFAULT;
341
342 upath = (char __user *)(uintptr_t)param.path;
343 upropname = (char __user *)(uintptr_t)param.propname;
344 upropval = (void __user *)(uintptr_t)param.propval;
345
346 path = strndup_user(upath, FH_DTPROP_MAX_PATHLEN);
347 if (IS_ERR(path)) {
348 ret = PTR_ERR(path);
349 goto out;
350 }
351
352 propname = strndup_user(upropname, FH_DTPROP_MAX_PATHLEN);
353 if (IS_ERR(propname)) {
354 ret = PTR_ERR(propname);
355 goto out;
356 }
357
358 if (param.proplen > FH_DTPROP_MAX_PROPLEN) {
359 ret = -EINVAL;
360 goto out;
361 }
362
363 propval = kmalloc(param.proplen, GFP_KERNEL);
364 if (!propval) {
365 ret = -ENOMEM;
366 goto out;
367 }
368
369 if (set) {
370 if (copy_from_user(propval, upropval, param.proplen)) {
371 ret = -EFAULT;
372 goto out;
373 }
374
375 param.ret = fh_partition_set_dtprop(param.handle,
376 virt_to_phys(path),
377 virt_to_phys(propname),
378 virt_to_phys(propval),
379 param.proplen);
380 } else {
381 param.ret = fh_partition_get_dtprop(param.handle,
382 virt_to_phys(path),
383 virt_to_phys(propname),
384 virt_to_phys(propval),
385 ¶m.proplen);
386
387 if (param.ret == 0) {
388 if (copy_to_user(upropval, propval, param.proplen) ||
389 put_user(param.proplen, &p->proplen)) {
390 ret = -EFAULT;
391 goto out;
392 }
393 }
394 }
395
396 if (put_user(param.ret, &p->ret))
397 ret = -EFAULT;
398
399out:
400 kfree(path);
401 kfree(propval);
402 kfree(propname);
403
404 return ret;
405}
406
407
408
409
410static long fsl_hv_ioctl(struct file *file, unsigned int cmd,
411 unsigned long argaddr)
412{
413 void __user *arg = (void __user *)argaddr;
414 long ret;
415
416 switch (cmd) {
417 case FSL_HV_IOCTL_PARTITION_RESTART:
418 ret = ioctl_restart(arg);
419 break;
420 case FSL_HV_IOCTL_PARTITION_GET_STATUS:
421 ret = ioctl_status(arg);
422 break;
423 case FSL_HV_IOCTL_PARTITION_START:
424 ret = ioctl_start(arg);
425 break;
426 case FSL_HV_IOCTL_PARTITION_STOP:
427 ret = ioctl_stop(arg);
428 break;
429 case FSL_HV_IOCTL_MEMCPY:
430 ret = ioctl_memcpy(arg);
431 break;
432 case FSL_HV_IOCTL_DOORBELL:
433 ret = ioctl_doorbell(arg);
434 break;
435 case FSL_HV_IOCTL_GETPROP:
436 ret = ioctl_dtprop(arg, 0);
437 break;
438 case FSL_HV_IOCTL_SETPROP:
439 ret = ioctl_dtprop(arg, 1);
440 break;
441 default:
442 pr_debug("fsl-hv: bad ioctl dir=%u type=%u cmd=%u size=%u\n",
443 _IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd),
444 _IOC_SIZE(cmd));
445 return -ENOTTY;
446 }
447
448 return ret;
449}
450
451
452static struct list_head db_list;
453
454
455static DEFINE_SPINLOCK(db_list_lock);
456
457
458#define QSIZE 16
459
460
461#define nextp(x) (((x) + 1) & (QSIZE - 1))
462
463
464struct doorbell_queue {
465 struct list_head list;
466 spinlock_t lock;
467 wait_queue_head_t wait;
468 unsigned int head;
469 unsigned int tail;
470 uint32_t q[QSIZE];
471};
472
473
474struct list_head isr_list;
475
476
477struct doorbell_isr {
478 struct list_head list;
479 unsigned int irq;
480 uint32_t doorbell;
481 uint32_t partition;
482};
483
484
485
486
487static void fsl_hv_queue_doorbell(uint32_t doorbell)
488{
489 struct doorbell_queue *dbq;
490 unsigned long flags;
491
492
493 spin_lock_irqsave(&db_list_lock, flags);
494
495 list_for_each_entry(dbq, &db_list, list) {
496 if (dbq->head != nextp(dbq->tail)) {
497 dbq->q[dbq->tail] = doorbell;
498
499
500
501
502 smp_wmb();
503 dbq->tail = nextp(dbq->tail);
504 wake_up_interruptible(&dbq->wait);
505 }
506 }
507
508 spin_unlock_irqrestore(&db_list_lock, flags);
509}
510
511
512
513
514
515
516
517
518static irqreturn_t fsl_hv_isr(int irq, void *data)
519{
520 fsl_hv_queue_doorbell((uintptr_t) data);
521
522 return IRQ_HANDLED;
523}
524
525
526
527
528
529
530
531
532
533
534
535static irqreturn_t fsl_hv_state_change_thread(int irq, void *data)
536{
537 struct doorbell_isr *dbisr = data;
538
539 blocking_notifier_call_chain(&failover_subscribers, dbisr->partition,
540 NULL);
541
542 return IRQ_HANDLED;
543}
544
545
546
547
548static irqreturn_t fsl_hv_state_change_isr(int irq, void *data)
549{
550 unsigned int status;
551 struct doorbell_isr *dbisr = data;
552 int ret;
553
554
555 fsl_hv_queue_doorbell(dbisr->doorbell);
556
557
558 ret = fh_partition_get_status(dbisr->partition, &status);
559 if (!ret && (status == FH_PARTITION_STOPPED))
560 return IRQ_WAKE_THREAD;
561
562 return IRQ_HANDLED;
563}
564
565
566
567
568static __poll_t fsl_hv_poll(struct file *filp, struct poll_table_struct *p)
569{
570 struct doorbell_queue *dbq = filp->private_data;
571 unsigned long flags;
572 __poll_t mask;
573
574 spin_lock_irqsave(&dbq->lock, flags);
575
576 poll_wait(filp, &dbq->wait, p);
577 mask = (dbq->head == dbq->tail) ? 0 : (EPOLLIN | EPOLLRDNORM);
578
579 spin_unlock_irqrestore(&dbq->lock, flags);
580
581 return mask;
582}
583
584
585
586
587
588
589
590
591static ssize_t fsl_hv_read(struct file *filp, char __user *buf, size_t len,
592 loff_t *off)
593{
594 struct doorbell_queue *dbq = filp->private_data;
595 uint32_t __user *p = (uint32_t __user *) buf;
596 unsigned long flags;
597 ssize_t count = 0;
598
599
600 while (len >= sizeof(uint32_t)) {
601 uint32_t dbell;
602
603 spin_lock_irqsave(&dbq->lock, flags);
604
605
606
607
608
609
610 if (dbq->head == dbq->tail) {
611 spin_unlock_irqrestore(&dbq->lock, flags);
612 if (count)
613 break;
614 if (filp->f_flags & O_NONBLOCK)
615 return -EAGAIN;
616 if (wait_event_interruptible(dbq->wait,
617 dbq->head != dbq->tail))
618 return -ERESTARTSYS;
619 continue;
620 }
621
622
623
624
625
626
627
628
629
630 smp_rmb();
631
632
633
634
635 dbell = dbq->q[dbq->head];
636 dbq->head = nextp(dbq->head);
637
638 spin_unlock_irqrestore(&dbq->lock, flags);
639
640 if (put_user(dbell, p))
641 return -EFAULT;
642 p++;
643 count += sizeof(uint32_t);
644 len -= sizeof(uint32_t);
645 }
646
647 return count;
648}
649
650
651
652
653
654
655
656static int fsl_hv_open(struct inode *inode, struct file *filp)
657{
658 struct doorbell_queue *dbq;
659 unsigned long flags;
660 int ret = 0;
661
662 dbq = kzalloc(sizeof(struct doorbell_queue), GFP_KERNEL);
663 if (!dbq) {
664 pr_err("fsl-hv: out of memory\n");
665 return -ENOMEM;
666 }
667
668 spin_lock_init(&dbq->lock);
669 init_waitqueue_head(&dbq->wait);
670
671 spin_lock_irqsave(&db_list_lock, flags);
672 list_add(&dbq->list, &db_list);
673 spin_unlock_irqrestore(&db_list_lock, flags);
674
675 filp->private_data = dbq;
676
677 return ret;
678}
679
680
681
682
683static int fsl_hv_close(struct inode *inode, struct file *filp)
684{
685 struct doorbell_queue *dbq = filp->private_data;
686 unsigned long flags;
687
688 int ret = 0;
689
690 spin_lock_irqsave(&db_list_lock, flags);
691 list_del(&dbq->list);
692 spin_unlock_irqrestore(&db_list_lock, flags);
693
694 kfree(dbq);
695
696 return ret;
697}
698
699static const struct file_operations fsl_hv_fops = {
700 .owner = THIS_MODULE,
701 .open = fsl_hv_open,
702 .release = fsl_hv_close,
703 .poll = fsl_hv_poll,
704 .read = fsl_hv_read,
705 .unlocked_ioctl = fsl_hv_ioctl,
706 .compat_ioctl = fsl_hv_ioctl,
707};
708
709static struct miscdevice fsl_hv_misc_dev = {
710 MISC_DYNAMIC_MINOR,
711 "fsl-hv",
712 &fsl_hv_fops
713};
714
715static irqreturn_t fsl_hv_shutdown_isr(int irq, void *data)
716{
717 orderly_poweroff(false);
718
719 return IRQ_HANDLED;
720}
721
722
723
724
725
726
727static int get_parent_handle(struct device_node *np)
728{
729 struct device_node *parent;
730 const uint32_t *prop;
731 uint32_t handle;
732 int len;
733
734 parent = of_get_parent(np);
735 if (!parent)
736
737 return -ENODEV;
738
739
740
741
742
743 prop = of_get_property(parent, "hv-handle", &len);
744 if (!prop)
745 prop = of_get_property(parent, "reg", &len);
746
747 if (!prop || (len != sizeof(uint32_t))) {
748
749 of_node_put(parent);
750 return -ENODEV;
751 }
752
753 handle = be32_to_cpup(prop);
754 of_node_put(parent);
755
756 return handle;
757}
758
759
760
761
762
763
764
765int fsl_hv_failover_register(struct notifier_block *nb)
766{
767 return blocking_notifier_chain_register(&failover_subscribers, nb);
768}
769EXPORT_SYMBOL(fsl_hv_failover_register);
770
771
772
773
774int fsl_hv_failover_unregister(struct notifier_block *nb)
775{
776 return blocking_notifier_chain_unregister(&failover_subscribers, nb);
777}
778EXPORT_SYMBOL(fsl_hv_failover_unregister);
779
780
781
782
783
784
785
786
787
788
789
790
791static int has_fsl_hypervisor(void)
792{
793 struct device_node *node;
794 int ret;
795
796 node = of_find_node_by_path("/hypervisor");
797 if (!node)
798 return 0;
799
800 ret = of_find_property(node, "fsl,hv-version", NULL) != NULL;
801
802 of_node_put(node);
803
804 return ret;
805}
806
807
808
809
810
811
812
813
814
815static int __init fsl_hypervisor_init(void)
816{
817 struct device_node *np;
818 struct doorbell_isr *dbisr, *n;
819 int ret;
820
821 pr_info("Freescale hypervisor management driver\n");
822
823 if (!has_fsl_hypervisor()) {
824 pr_info("fsl-hv: no hypervisor found\n");
825 return -ENODEV;
826 }
827
828 ret = misc_register(&fsl_hv_misc_dev);
829 if (ret) {
830 pr_err("fsl-hv: cannot register device\n");
831 return ret;
832 }
833
834 INIT_LIST_HEAD(&db_list);
835 INIT_LIST_HEAD(&isr_list);
836
837 for_each_compatible_node(np, NULL, "epapr,hv-receive-doorbell") {
838 unsigned int irq;
839 const uint32_t *handle;
840
841 handle = of_get_property(np, "interrupts", NULL);
842 irq = irq_of_parse_and_map(np, 0);
843 if (!handle || (irq == NO_IRQ)) {
844 pr_err("fsl-hv: no 'interrupts' property in %pOF node\n",
845 np);
846 continue;
847 }
848
849 dbisr = kzalloc(sizeof(*dbisr), GFP_KERNEL);
850 if (!dbisr)
851 goto out_of_memory;
852
853 dbisr->irq = irq;
854 dbisr->doorbell = be32_to_cpup(handle);
855
856 if (of_device_is_compatible(np, "fsl,hv-shutdown-doorbell")) {
857
858 ret = request_irq(irq, fsl_hv_shutdown_isr, 0,
859 np->name, NULL);
860 } else if (of_device_is_compatible(np,
861 "fsl,hv-state-change-doorbell")) {
862
863
864
865
866
867
868
869
870 dbisr->partition = ret = get_parent_handle(np);
871 if (ret < 0) {
872 pr_err("fsl-hv: node %pOF has missing or "
873 "malformed parent\n", np);
874 kfree(dbisr);
875 continue;
876 }
877 ret = request_threaded_irq(irq, fsl_hv_state_change_isr,
878 fsl_hv_state_change_thread,
879 0, np->name, dbisr);
880 } else
881 ret = request_irq(irq, fsl_hv_isr, 0, np->name, dbisr);
882
883 if (ret < 0) {
884 pr_err("fsl-hv: could not request irq %u for node %pOF\n",
885 irq, np);
886 kfree(dbisr);
887 continue;
888 }
889
890 list_add(&dbisr->list, &isr_list);
891
892 pr_info("fsl-hv: registered handler for doorbell %u\n",
893 dbisr->doorbell);
894 }
895
896 return 0;
897
898out_of_memory:
899 list_for_each_entry_safe(dbisr, n, &isr_list, list) {
900 free_irq(dbisr->irq, dbisr);
901 list_del(&dbisr->list);
902 kfree(dbisr);
903 }
904
905 misc_deregister(&fsl_hv_misc_dev);
906
907 return -ENOMEM;
908}
909
910
911
912
913
914
915static void __exit fsl_hypervisor_exit(void)
916{
917 struct doorbell_isr *dbisr, *n;
918
919 list_for_each_entry_safe(dbisr, n, &isr_list, list) {
920 free_irq(dbisr->irq, dbisr);
921 list_del(&dbisr->list);
922 kfree(dbisr);
923 }
924
925 misc_deregister(&fsl_hv_misc_dev);
926}
927
928module_init(fsl_hypervisor_init);
929module_exit(fsl_hypervisor_exit);
930
931MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
932MODULE_DESCRIPTION("Freescale hypervisor management driver");
933MODULE_LICENSE("GPL v2");
934