12
12
#include <linux/module.h>
13
13
#include <linux/of.h>
14
14
#include <linux/of_device.h>
15
+ #include <linux/of_fdt.h>
15
16
#include <linux/string.h>
16
17
#include <linux/ctype.h>
17
18
#include <linux/errno.h>
18
19
#include <linux/slab.h>
20
+ #include <linux/libfdt.h>
19
21
#include <linux/err.h>
20
22
#include <linux/idr.h>
21
23
@@ -33,7 +35,9 @@ struct fragment {
33
35
34
36
/**
35
37
* struct overlay_changeset
38
+ * @id: changeset identifier
36
39
* @ovcs_list: list on which we are located
40
+ * @fdt: FDT that was unflattened to create @overlay_tree
37
41
* @overlay_tree: expanded device tree that contains the fragment nodes
38
42
* @count: count of fragment structures
39
43
* @fragments: fragment nodes in the overlay expanded device tree
@@ -43,6 +47,7 @@ struct fragment {
43
47
struct overlay_changeset {
44
48
int id ;
45
49
struct list_head ovcs_list ;
50
+ const void * fdt ;
46
51
struct device_node * overlay_tree ;
47
52
int count ;
48
53
struct fragment * fragments ;
@@ -483,27 +488,38 @@ static int build_changeset(struct overlay_changeset *ovcs)
483
488
*/
484
489
static struct device_node * find_target_node (struct device_node * info_node )
485
490
{
491
+ struct device_node * node ;
486
492
const char * path ;
487
493
u32 val ;
488
494
int ret ;
489
495
490
496
ret = of_property_read_u32 (info_node , "target" , & val );
491
- if (!ret )
492
- return of_find_node_by_phandle (val );
497
+ if (!ret ) {
498
+ node = of_find_node_by_phandle (val );
499
+ if (!node )
500
+ pr_err ("find target, node: %pOF, phandle 0x%x not found\n" ,
501
+ info_node , val );
502
+ return node ;
503
+ }
493
504
494
505
ret = of_property_read_string (info_node , "target-path" , & path );
495
- if (!ret )
496
- return of_find_node_by_path (path );
506
+ if (!ret ) {
507
+ node = of_find_node_by_path (path );
508
+ if (!node )
509
+ pr_err ("find target, node: %pOF, path '%s' not found\n" ,
510
+ info_node , path );
511
+ return node ;
512
+ }
497
513
498
- pr_err ("Failed to find target for node %p (%s)\n" ,
499
- info_node , info_node -> name );
514
+ pr_err ("find target, node: %pOF, no target property\n" , info_node );
500
515
501
516
return NULL ;
502
517
}
503
518
504
519
/**
505
520
* init_overlay_changeset() - initialize overlay changeset from overlay tree
506
- * @ovcs Overlay changeset to build
521
+ * @ovcs: Overlay changeset to build
522
+ * @fdt: the FDT that was unflattened to create @tree
507
523
* @tree: Contains all the overlay fragments and overlay fixup nodes
508
524
*
509
525
* Initialize @ovcs. Populate @ovcs->fragments with node information from
@@ -514,7 +530,7 @@ static struct device_node *find_target_node(struct device_node *info_node)
514
530
* detected in @tree, or -ENOSPC if idr_alloc() error.
515
531
*/
516
532
static int init_overlay_changeset (struct overlay_changeset * ovcs ,
517
- struct device_node * tree )
533
+ const void * fdt , struct device_node * tree )
518
534
{
519
535
struct device_node * node , * overlay_node ;
520
536
struct fragment * fragment ;
@@ -535,6 +551,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
535
551
pr_debug ("%s() tree is not root\n" , __func__ );
536
552
537
553
ovcs -> overlay_tree = tree ;
554
+ ovcs -> fdt = fdt ;
538
555
539
556
INIT_LIST_HEAD (& ovcs -> ovcs_list );
540
557
@@ -606,6 +623,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
606
623
}
607
624
608
625
if (!cnt ) {
626
+ pr_err ("no fragments or symbols in overlay\n" );
609
627
ret = - EINVAL ;
610
628
goto err_free_fragments ;
611
629
}
@@ -642,11 +660,24 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
642
660
}
643
661
kfree (ovcs -> fragments );
644
662
663
+ /*
664
+ * TODO
665
+ *
666
+ * would like to: kfree(ovcs->overlay_tree);
667
+ * but can not since drivers may have pointers into this data
668
+ *
669
+ * would like to: kfree(ovcs->fdt);
670
+ * but can not since drivers may have pointers into this data
671
+ */
672
+
645
673
kfree (ovcs );
646
674
}
647
675
648
- /**
676
+ /*
677
+ * internal documentation
678
+ *
649
679
* of_overlay_apply() - Create and apply an overlay changeset
680
+ * @fdt: the FDT that was unflattened to create @tree
650
681
* @tree: Expanded overlay device tree
651
682
* @ovcs_id: Pointer to overlay changeset id
652
683
*
@@ -685,21 +716,29 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
685
716
* id is returned to *ovcs_id.
686
717
*/
687
718
688
- int of_overlay_apply (struct device_node * tree , int * ovcs_id )
719
+ static int of_overlay_apply (const void * fdt , struct device_node * tree ,
720
+ int * ovcs_id )
689
721
{
690
722
struct overlay_changeset * ovcs ;
691
723
int ret = 0 , ret_revert , ret_tmp ;
692
724
693
- * ovcs_id = 0 ;
725
+ /*
726
+ * As of this point, fdt and tree belong to the overlay changeset.
727
+ * overlay changeset code is responsible for freeing them.
728
+ */
694
729
695
730
if (devicetree_corrupt ()) {
696
731
pr_err ("devicetree state suspect, refuse to apply overlay\n" );
732
+ kfree (fdt );
733
+ kfree (tree );
697
734
ret = - EBUSY ;
698
735
goto out ;
699
736
}
700
737
701
738
ovcs = kzalloc (sizeof (* ovcs ), GFP_KERNEL );
702
739
if (!ovcs ) {
740
+ kfree (fdt );
741
+ kfree (tree );
703
742
ret = - ENOMEM ;
704
743
goto out ;
705
744
}
@@ -709,12 +748,17 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
709
748
710
749
ret = of_resolve_phandles (tree );
711
750
if (ret )
712
- goto err_free_overlay_changeset ;
751
+ goto err_free_tree ;
713
752
714
- ret = init_overlay_changeset (ovcs , tree );
753
+ ret = init_overlay_changeset (ovcs , fdt , tree );
715
754
if (ret )
716
- goto err_free_overlay_changeset ;
755
+ goto err_free_tree ;
717
756
757
+ /*
758
+ * after overlay_notify(), ovcs->overlay_tree related pointers may have
759
+ * leaked to drivers, so can not kfree() tree, aka ovcs->overlay_tree;
760
+ * and can not free fdt, aka ovcs->fdt
761
+ */
718
762
ret = overlay_notify (ovcs , OF_OVERLAY_PRE_APPLY );
719
763
if (ret ) {
720
764
pr_err ("overlay changeset pre-apply notify error %d\n" , ret );
@@ -754,6 +798,10 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
754
798
755
799
goto out_unlock ;
756
800
801
+ err_free_tree :
802
+ kfree (fdt );
803
+ kfree (tree );
804
+
757
805
err_free_overlay_changeset :
758
806
free_overlay_changeset (ovcs );
759
807
@@ -766,7 +814,63 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
766
814
767
815
return ret ;
768
816
}
769
- EXPORT_SYMBOL_GPL (of_overlay_apply );
817
+
818
+ int of_overlay_fdt_apply (const void * overlay_fdt , u32 overlay_fdt_size ,
819
+ int * ovcs_id )
820
+ {
821
+ const void * new_fdt ;
822
+ int ret ;
823
+ u32 size ;
824
+ struct device_node * overlay_root ;
825
+
826
+ * ovcs_id = 0 ;
827
+ ret = 0 ;
828
+
829
+ if (overlay_fdt_size < sizeof (struct fdt_header ) ||
830
+ fdt_check_header (overlay_fdt )) {
831
+ pr_err ("Invalid overlay_fdt header\n" );
832
+ return - EINVAL ;
833
+ }
834
+
835
+ size = fdt_totalsize (overlay_fdt );
836
+ if (overlay_fdt_size < size )
837
+ return - EINVAL ;
838
+
839
+ /*
840
+ * Must create permanent copy of FDT because of_fdt_unflatten_tree()
841
+ * will create pointers to the passed in FDT in the unflattened tree.
842
+ */
843
+ new_fdt = kmemdup (overlay_fdt , size , GFP_KERNEL );
844
+ if (!new_fdt )
845
+ return - ENOMEM ;
846
+
847
+ of_fdt_unflatten_tree (new_fdt , NULL , & overlay_root );
848
+ if (!overlay_root ) {
849
+ pr_err ("unable to unflatten overlay_fdt\n" );
850
+ ret = - EINVAL ;
851
+ goto out_free_new_fdt ;
852
+ }
853
+
854
+ ret = of_overlay_apply (new_fdt , overlay_root , ovcs_id );
855
+ if (ret < 0 ) {
856
+ /*
857
+ * new_fdt and overlay_root now belong to the overlay
858
+ * changeset.
859
+ * overlay changeset code is responsible for freeing them.
860
+ */
861
+ goto out ;
862
+ }
863
+
864
+ return 0 ;
865
+
866
+
867
+ out_free_new_fdt :
868
+ kfree (new_fdt );
869
+
870
+ out :
871
+ return ret ;
872
+ }
873
+ EXPORT_SYMBOL_GPL (of_overlay_fdt_apply );
770
874
771
875
/*
772
876
* Find @np in @tree.
0 commit comments