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 = kzalloc(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 down_read(¤t->mm->mmap_sem);
247 num_pinned = get_user_pages(current, current->mm,
248 param.local_vaddr - lb_offset, num_pages,
249 (param.source == -1) ? READ : WRITE,
250 0, pages, NULL);
251 up_read(¤t->mm->mmap_sem);
252
253 if (num_pinned != num_pages) {
254
255 pr_debug("fsl-hv: could not lock source buffer\n");
256 ret = (num_pinned < 0) ? num_pinned : -EFAULT;
257 goto exit;
258 }
259
260
261
262
263
264 if (param.source == -1) {
265 sg_list[0].source = page_to_phys(pages[0]) + lb_offset;
266 sg_list[0].target = param.remote_paddr;
267 } else {
268 sg_list[0].source = param.remote_paddr;
269 sg_list[0].target = page_to_phys(pages[0]) + lb_offset;
270 }
271 sg_list[0].size = min_t(uint64_t, param.count, PAGE_SIZE - lb_offset);
272
273 remote_paddr = param.remote_paddr + sg_list[0].size;
274 count = param.count - sg_list[0].size;
275
276 for (i = 1; i < num_pages; i++) {
277 if (param.source == -1) {
278
279 sg_list[i].source = page_to_phys(pages[i]);
280 sg_list[i].target = remote_paddr;
281 } else {
282
283 sg_list[i].source = remote_paddr;
284 sg_list[i].target = page_to_phys(pages[i]);
285 }
286 sg_list[i].size = min_t(uint64_t, count, PAGE_SIZE);
287
288 remote_paddr += sg_list[i].size;
289 count -= sg_list[i].size;
290 }
291
292 param.ret = fh_partition_memcpy(param.source, param.target,
293 virt_to_phys(sg_list), num_pages);
294
295exit:
296 if (pages) {
297 for (i = 0; i < num_pages; i++)
298 if (pages[i])
299 put_page(pages[i]);
300 }
301
302 kfree(sg_list_unaligned);
303 kfree(pages);
304
305 if (!ret)
306 if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
307 return -EFAULT;
308
309 return ret;
310}
311
312
313
314
315
316
317static long ioctl_doorbell(struct fsl_hv_ioctl_doorbell __user *p)
318{
319 struct fsl_hv_ioctl_doorbell param;
320
321
322 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_doorbell)))
323 return -EFAULT;
324
325 param.ret = ev_doorbell_send(param.doorbell);
326
327 if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
328 return -EFAULT;
329
330 return 0;
331}
332
333static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user *p, int set)
334{
335 struct fsl_hv_ioctl_prop param;
336 char __user *upath, *upropname;
337 void __user *upropval;
338 char *path = NULL, *propname = NULL;
339 void *propval = NULL;
340 int ret = 0;
341
342
343 if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_prop)))
344 return -EFAULT;
345
346 upath = (char __user *)(uintptr_t)param.path;
347 upropname = (char __user *)(uintptr_t)param.propname;
348 upropval = (void __user *)(uintptr_t)param.propval;
349
350 path = strndup_user(upath, FH_DTPROP_MAX_PATHLEN);
351 if (IS_ERR(path)) {
352 ret = PTR_ERR(path);
353 goto out;
354 }
355
356 propname = strndup_user(upropname, FH_DTPROP_MAX_PATHLEN);
357 if (IS_ERR(propname)) {
358 ret = PTR_ERR(propname);
359 goto out;
360 }
361
362 if (param.proplen > FH_DTPROP_MAX_PROPLEN) {
363 ret = -EINVAL;
364 goto out;
365 }
366
367 propval = kmalloc(param.proplen, GFP_KERNEL);
368 if (!propval) {
369 ret = -ENOMEM;
370 goto out;
371 }
372
373 if (set) {
374 if (copy_from_user(propval, upropval, param.proplen)) {
375 ret = -EFAULT;
376 goto out;
377 }
378
379 param.ret = fh_partition_set_dtprop(param.handle,
380 virt_to_phys(path),
381 virt_to_phys(propname),
382 virt_to_phys(propval),
383 param.proplen);
384 } else {
385 param.ret = fh_partition_get_dtprop(param.handle,
386 virt_to_phys(path),
387 virt_to_phys(propname),
388 virt_to_phys(propval),
389 ¶m.proplen);
390
391 if (param.ret == 0) {
392 if (copy_to_user(upropval, propval, param.proplen) ||
393 put_user(param.proplen, &p->proplen)) {
394 ret = -EFAULT;
395 goto out;
396 }
397 }
398 }
399
400 if (put_user(param.ret, &p->ret))
401 ret = -EFAULT;
402
403out:
404 kfree(path);
405 kfree(propval);
406 kfree(propname);
407
408 return ret;
409}
410
411
412
413
414static long fsl_hv_ioctl(struct file *file, unsigned int cmd,
415 unsigned long argaddr)
416{
417 void __user *arg = (void __user *)argaddr;
418 long ret;
419
420 switch (cmd) {
421 case FSL_HV_IOCTL_PARTITION_RESTART:
422 ret = ioctl_restart(arg);
423 break;
424 case FSL_HV_IOCTL_PARTITION_GET_STATUS:
425 ret = ioctl_status(arg);
426 break;
427 case FSL_HV_IOCTL_PARTITION_START:
428 ret = ioctl_start(arg);
429 break;
430 case FSL_HV_IOCTL_PARTITION_STOP:
431 ret = ioctl_stop(arg);
432 break;
433 case FSL_HV_IOCTL_MEMCPY:
434 ret = ioctl_memcpy(arg);
435 break;
436 case FSL_HV_IOCTL_DOORBELL:
437 ret = ioctl_doorbell(arg);
438 break;
439 case FSL_HV_IOCTL_GETPROP:
440 ret = ioctl_dtprop(arg, 0);
441 break;
442 case FSL_HV_IOCTL_SETPROP:
443 ret = ioctl_dtprop(arg, 1);
444 break;
445 default:
446 pr_debug("fsl-hv: bad ioctl dir=%u type=%u cmd=%u size=%u\n",
447 _IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd),
448 _IOC_SIZE(cmd));
449 return -ENOTTY;
450 }
451
452 return ret;
453}
454
455
456static struct list_head db_list;
457
458
459static DEFINE_SPINLOCK(db_list_lock);
460
461
462#define QSIZE 16
463
464
465#define nextp(x) (((x) + 1) & (QSIZE - 1))
466
467
468struct doorbell_queue {
469 struct list_head list;
470 spinlock_t lock;
471 wait_queue_head_t wait;
472 unsigned int head;
473 unsigned int tail;
474 uint32_t q[QSIZE];
475};
476
477
478struct list_head isr_list;
479
480
481struct doorbell_isr {
482 struct list_head list;
483 unsigned int irq;
484 uint32_t doorbell;
485 uint32_t partition;
486};
487
488
489
490
491static void fsl_hv_queue_doorbell(uint32_t doorbell)
492{
493 struct doorbell_queue *dbq;
494 unsigned long flags;
495
496
497 spin_lock_irqsave(&db_list_lock, flags);
498
499 list_for_each_entry(dbq, &db_list, list) {
500 if (dbq->head != nextp(dbq->tail)) {
501 dbq->q[dbq->tail] = doorbell;
502
503
504
505
506 smp_wmb();
507 dbq->tail = nextp(dbq->tail);
508 wake_up_interruptible(&dbq->wait);
509 }
510 }
511
512 spin_unlock_irqrestore(&db_list_lock, flags);
513}
514
515
516
517
518
519
520
521
522static irqreturn_t fsl_hv_isr(int irq, void *data)
523{
524 fsl_hv_queue_doorbell((uintptr_t) data);
525
526 return IRQ_HANDLED;
527}
528
529
530
531
532
533
534
535
536
537
538
539static irqreturn_t fsl_hv_state_change_thread(int irq, void *data)
540{
541 struct doorbell_isr *dbisr = data;
542
543 blocking_notifier_call_chain(&failover_subscribers, dbisr->partition,
544 NULL);
545
546 return IRQ_HANDLED;
547}
548
549
550
551
552static irqreturn_t fsl_hv_state_change_isr(int irq, void *data)
553{
554 unsigned int status;
555 struct doorbell_isr *dbisr = data;
556 int ret;
557
558
559 fsl_hv_queue_doorbell(dbisr->doorbell);
560
561
562 ret = fh_partition_get_status(dbisr->partition, &status);
563 if (!ret && (status == FH_PARTITION_STOPPED))
564 return IRQ_WAKE_THREAD;
565
566 return IRQ_HANDLED;
567}
568
569
570
571
572static unsigned int fsl_hv_poll(struct file *filp, struct poll_table_struct *p)
573{
574 struct doorbell_queue *dbq = filp->private_data;
575 unsigned long flags;
576 unsigned int mask;
577
578 spin_lock_irqsave(&dbq->lock, flags);
579
580 poll_wait(filp, &dbq->wait, p);
581 mask = (dbq->head == dbq->tail) ? 0 : (POLLIN | POLLRDNORM);
582
583 spin_unlock_irqrestore(&dbq->lock, flags);
584
585 return mask;
586}
587
588
589
590
591
592
593
594
595static ssize_t fsl_hv_read(struct file *filp, char __user *buf, size_t len,
596 loff_t *off)
597{
598 struct doorbell_queue *dbq = filp->private_data;
599 uint32_t __user *p = (uint32_t __user *) buf;
600 unsigned long flags;
601 ssize_t count = 0;
602
603
604 while (len >= sizeof(uint32_t)) {
605 uint32_t dbell;
606
607 spin_lock_irqsave(&dbq->lock, flags);
608
609
610
611
612
613
614 if (dbq->head == dbq->tail) {
615 spin_unlock_irqrestore(&dbq->lock, flags);
616 if (count)
617 break;
618 if (filp->f_flags & O_NONBLOCK)
619 return -EAGAIN;
620 if (wait_event_interruptible(dbq->wait,
621 dbq->head != dbq->tail))
622 return -ERESTARTSYS;
623 continue;
624 }
625
626
627
628
629
630
631
632
633
634 smp_rmb();
635
636
637
638
639 dbell = dbq->q[dbq->head];
640 dbq->head = nextp(dbq->head);
641
642 spin_unlock_irqrestore(&dbq->lock, flags);
643
644 if (put_user(dbell, p))
645 return -EFAULT;
646 p++;
647 count += sizeof(uint32_t);
648 len -= sizeof(uint32_t);
649 }
650
651 return count;
652}
653
654
655
656
657
658
659
660static int fsl_hv_open(struct inode *inode, struct file *filp)
661{
662 struct doorbell_queue *dbq;
663 unsigned long flags;
664 int ret = 0;
665
666 dbq = kzalloc(sizeof(struct doorbell_queue), GFP_KERNEL);
667 if (!dbq) {
668 pr_err("fsl-hv: out of memory\n");
669 return -ENOMEM;
670 }
671
672 spin_lock_init(&dbq->lock);
673 init_waitqueue_head(&dbq->wait);
674
675 spin_lock_irqsave(&db_list_lock, flags);
676 list_add(&dbq->list, &db_list);
677 spin_unlock_irqrestore(&db_list_lock, flags);
678
679 filp->private_data = dbq;
680
681 return ret;
682}
683
684
685
686
687static int fsl_hv_close(struct inode *inode, struct file *filp)
688{
689 struct doorbell_queue *dbq = filp->private_data;
690 unsigned long flags;
691
692 int ret = 0;
693
694 spin_lock_irqsave(&db_list_lock, flags);
695 list_del(&dbq->list);
696 spin_unlock_irqrestore(&db_list_lock, flags);
697
698 kfree(dbq);
699
700 return ret;
701}
702
703static const struct file_operations fsl_hv_fops = {
704 .owner = THIS_MODULE,
705 .open = fsl_hv_open,
706 .release = fsl_hv_close,
707 .poll = fsl_hv_poll,
708 .read = fsl_hv_read,
709 .unlocked_ioctl = fsl_hv_ioctl,
710 .compat_ioctl = fsl_hv_ioctl,
711};
712
713static struct miscdevice fsl_hv_misc_dev = {
714 MISC_DYNAMIC_MINOR,
715 "fsl-hv",
716 &fsl_hv_fops
717};
718
719static irqreturn_t fsl_hv_shutdown_isr(int irq, void *data)
720{
721 orderly_poweroff(false);
722
723 return IRQ_HANDLED;
724}
725
726
727
728
729
730
731static int get_parent_handle(struct device_node *np)
732{
733 struct device_node *parent;
734 const uint32_t *prop;
735 uint32_t handle;
736 int len;
737
738 parent = of_get_parent(np);
739 if (!parent)
740
741 return -ENODEV;
742
743
744
745
746
747 prop = of_get_property(parent, "hv-handle", &len);
748 if (!prop)
749 prop = of_get_property(parent, "reg", &len);
750
751 if (!prop || (len != sizeof(uint32_t))) {
752
753 of_node_put(parent);
754 return -ENODEV;
755 }
756
757 handle = be32_to_cpup(prop);
758 of_node_put(parent);
759
760 return handle;
761}
762
763
764
765
766
767
768
769int fsl_hv_failover_register(struct notifier_block *nb)
770{
771 return blocking_notifier_chain_register(&failover_subscribers, nb);
772}
773EXPORT_SYMBOL(fsl_hv_failover_register);
774
775
776
777
778int fsl_hv_failover_unregister(struct notifier_block *nb)
779{
780 return blocking_notifier_chain_unregister(&failover_subscribers, nb);
781}
782EXPORT_SYMBOL(fsl_hv_failover_unregister);
783
784
785
786
787
788
789
790
791
792
793
794
795static int has_fsl_hypervisor(void)
796{
797 struct device_node *node;
798 int ret;
799
800 node = of_find_node_by_path("/hypervisor");
801 if (!node)
802 return 0;
803
804 ret = of_find_property(node, "fsl,hv-version", NULL) != NULL;
805
806 of_node_put(node);
807
808 return ret;
809}
810
811
812
813
814
815
816
817
818
819static int __init fsl_hypervisor_init(void)
820{
821 struct device_node *np;
822 struct doorbell_isr *dbisr, *n;
823 int ret;
824
825 pr_info("Freescale hypervisor management driver\n");
826
827 if (!has_fsl_hypervisor()) {
828 pr_info("fsl-hv: no hypervisor found\n");
829 return -ENODEV;
830 }
831
832 ret = misc_register(&fsl_hv_misc_dev);
833 if (ret) {
834 pr_err("fsl-hv: cannot register device\n");
835 return ret;
836 }
837
838 INIT_LIST_HEAD(&db_list);
839 INIT_LIST_HEAD(&isr_list);
840
841 for_each_compatible_node(np, NULL, "epapr,hv-receive-doorbell") {
842 unsigned int irq;
843 const uint32_t *handle;
844
845 handle = of_get_property(np, "interrupts", NULL);
846 irq = irq_of_parse_and_map(np, 0);
847 if (!handle || (irq == NO_IRQ)) {
848 pr_err("fsl-hv: no 'interrupts' property in %s node\n",
849 np->full_name);
850 continue;
851 }
852
853 dbisr = kzalloc(sizeof(*dbisr), GFP_KERNEL);
854 if (!dbisr)
855 goto out_of_memory;
856
857 dbisr->irq = irq;
858 dbisr->doorbell = be32_to_cpup(handle);
859
860 if (of_device_is_compatible(np, "fsl,hv-shutdown-doorbell")) {
861
862 ret = request_irq(irq, fsl_hv_shutdown_isr, 0,
863 np->name, NULL);
864 } else if (of_device_is_compatible(np,
865 "fsl,hv-state-change-doorbell")) {
866
867
868
869
870
871
872
873
874 dbisr->partition = ret = get_parent_handle(np);
875 if (ret < 0) {
876 pr_err("fsl-hv: node %s has missing or "
877 "malformed parent\n", np->full_name);
878 kfree(dbisr);
879 continue;
880 }
881 ret = request_threaded_irq(irq, fsl_hv_state_change_isr,
882 fsl_hv_state_change_thread,
883 0, np->name, dbisr);
884 } else
885 ret = request_irq(irq, fsl_hv_isr, 0, np->name, dbisr);
886
887 if (ret < 0) {
888 pr_err("fsl-hv: could not request irq %u for node %s\n",
889 irq, np->full_name);
890 kfree(dbisr);
891 continue;
892 }
893
894 list_add(&dbisr->list, &isr_list);
895
896 pr_info("fsl-hv: registered handler for doorbell %u\n",
897 dbisr->doorbell);
898 }
899
900 return 0;
901
902out_of_memory:
903 list_for_each_entry_safe(dbisr, n, &isr_list, list) {
904 free_irq(dbisr->irq, dbisr);
905 list_del(&dbisr->list);
906 kfree(dbisr);
907 }
908
909 misc_deregister(&fsl_hv_misc_dev);
910
911 return -ENOMEM;
912}
913
914
915
916
917
918
919static void __exit fsl_hypervisor_exit(void)
920{
921 struct doorbell_isr *dbisr, *n;
922
923 list_for_each_entry_safe(dbisr, n, &isr_list, list) {
924 free_irq(dbisr->irq, dbisr);
925 list_del(&dbisr->list);
926 kfree(dbisr);
927 }
928
929 misc_deregister(&fsl_hv_misc_dev);
930}
931
932module_init(fsl_hypervisor_init);
933module_exit(fsl_hypervisor_exit);
934
935MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
936MODULE_DESCRIPTION("Freescale hypervisor management driver");
937MODULE_LICENSE("GPL v2");
938