]> git.proxmox.com Git - mirror_edk2.git/blob - EmbeddedPkg/Library/FdtLib/fdt_ro.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / EmbeddedPkg / Library / FdtLib / fdt_ro.c
1 /*
2 * libfdt - Flat Device Tree manipulation
3 * Copyright (C) 2006 David Gibson, IBM Corporation.
4 *
5 * libfdt is dual licensed: you can use it either under the terms of
6 * the GPL, or the BSD license, at your option.
7 *
8 * a) This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this library; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
21 * MA 02110-1301 USA
22 *
23 * Alternatively,
24 *
25 * b) Redistribution and use in source and binary forms, with or
26 * without modification, are permitted provided that the following
27 * conditions are met:
28 *
29 * 1. Redistributions of source code must retain the above
30 * copyright notice, this list of conditions and the following
31 * disclaimer.
32 * 2. Redistributions in binary form must reproduce the above
33 * copyright notice, this list of conditions and the following
34 * disclaimer in the documentation and/or other materials
35 * provided with the distribution.
36 *
37 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
38 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
39 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
40 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
41 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
42 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
48 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
49 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50 */
51 #include "libfdt_env.h"
52
53 #include <fdt.h>
54 #include <libfdt.h>
55
56 #include "libfdt_internal.h"
57
58 static int
59 _fdt_nodename_eq (
60 const void *fdt,
61 int offset,
62 const char *s,
63 int len
64 )
65 {
66 const char *p = fdt_offset_ptr (fdt, offset + FDT_TAGSIZE, len+1);
67
68 if (!p) {
69 /* short match */
70 return 0;
71 }
72
73 if (memcmp (p, s, len) != 0) {
74 return 0;
75 }
76
77 if (p[len] == '\0') {
78 return 1;
79 } else if (!memchr (s, '@', len) && (p[len] == '@')) {
80 return 1;
81 } else {
82 return 0;
83 }
84 }
85
86 const char *
87 fdt_string (
88 const void *fdt,
89 int stroffset
90 )
91 {
92 return (const char *)fdt + fdt_off_dt_strings (fdt) + stroffset;
93 }
94
95 static int
96 _fdt_string_eq (
97 const void *fdt,
98 int stroffset,
99 const char *s,
100 int len
101 )
102 {
103 const char *p = fdt_string (fdt, stroffset);
104
105 return (strlen (p) == len) && (memcmp (p, s, len) == 0);
106 }
107
108 uint32_t
109 fdt_get_max_phandle (
110 const void *fdt
111 )
112 {
113 uint32_t max_phandle = 0;
114 int offset;
115
116 for (offset = fdt_next_node (fdt, -1, NULL); ;
117 offset = fdt_next_node (fdt, offset, NULL))
118 {
119 uint32_t phandle;
120
121 if (offset == -FDT_ERR_NOTFOUND) {
122 return max_phandle;
123 }
124
125 if (offset < 0) {
126 return (uint32_t)-1;
127 }
128
129 phandle = fdt_get_phandle (fdt, offset);
130 if (phandle == (uint32_t)-1) {
131 continue;
132 }
133
134 if (phandle > max_phandle) {
135 max_phandle = phandle;
136 }
137 }
138
139 return 0;
140 }
141
142 int
143 fdt_get_mem_rsv (
144 const void *fdt,
145 int n,
146 uint64_t *address,
147 uint64_t *size
148 )
149 {
150 FDT_CHECK_HEADER (fdt);
151 *address = fdt64_to_cpu (_fdt_mem_rsv (fdt, n)->address);
152 *size = fdt64_to_cpu (_fdt_mem_rsv (fdt, n)->size);
153 return 0;
154 }
155
156 int
157 fdt_num_mem_rsv (
158 const void *fdt
159 )
160 {
161 int i = 0;
162
163 while (fdt64_to_cpu (_fdt_mem_rsv (fdt, i)->size) != 0) {
164 i++;
165 }
166
167 return i;
168 }
169
170 static int
171 _nextprop (
172 const void *fdt,
173 int offset
174 )
175 {
176 uint32_t tag;
177 int nextoffset;
178
179 do {
180 tag = fdt_next_tag (fdt, offset, &nextoffset);
181
182 switch (tag) {
183 case FDT_END:
184 if (nextoffset >= 0) {
185 return -FDT_ERR_BADSTRUCTURE;
186 } else {
187 return nextoffset;
188 }
189
190 case FDT_PROP:
191 return offset;
192 }
193
194 offset = nextoffset;
195 } while (tag == FDT_NOP);
196
197 return -FDT_ERR_NOTFOUND;
198 }
199
200 int
201 fdt_subnode_offset_namelen (
202 const void *fdt,
203 int offset,
204 const char *name,
205 int namelen
206 )
207 {
208 int depth;
209
210 FDT_CHECK_HEADER (fdt);
211
212 for (depth = 0;
213 (offset >= 0) && (depth >= 0);
214 offset = fdt_next_node (fdt, offset, &depth))
215 {
216 if ( (depth == 1)
217 && _fdt_nodename_eq (fdt, offset, name, namelen))
218 {
219 return offset;
220 }
221 }
222
223 if (depth < 0) {
224 return -FDT_ERR_NOTFOUND;
225 }
226
227 return offset; /* error */
228 }
229
230 int
231 fdt_subnode_offset (
232 const void *fdt,
233 int parentoffset,
234 const char *name
235 )
236 {
237 return fdt_subnode_offset_namelen (fdt, parentoffset, name, strlen (name));
238 }
239
240 int
241 fdt_path_offset_namelen (
242 const void *fdt,
243 const char *path,
244 int namelen
245 )
246 {
247 const char *end = path + namelen;
248 const char *p = path;
249 int offset = 0;
250
251 FDT_CHECK_HEADER (fdt);
252
253 /* see if we have an alias */
254 if (*path != '/') {
255 const char *q = memchr (path, '/', end - p);
256
257 if (!q) {
258 q = end;
259 }
260
261 p = fdt_get_alias_namelen (fdt, p, q - p);
262 if (!p) {
263 return -FDT_ERR_BADPATH;
264 }
265
266 offset = fdt_path_offset (fdt, p);
267
268 p = q;
269 }
270
271 while (p < end) {
272 const char *q;
273
274 while (*p == '/') {
275 p++;
276 if (p == end) {
277 return offset;
278 }
279 }
280
281 q = memchr (p, '/', end - p);
282 if (!q) {
283 q = end;
284 }
285
286 offset = fdt_subnode_offset_namelen (fdt, offset, p, q-p);
287 if (offset < 0) {
288 return offset;
289 }
290
291 p = q;
292 }
293
294 return offset;
295 }
296
297 int
298 fdt_path_offset (
299 const void *fdt,
300 const char *path
301 )
302 {
303 return fdt_path_offset_namelen (fdt, path, strlen (path));
304 }
305
306 const char *
307 fdt_get_name (
308 const void *fdt,
309 int nodeoffset,
310 int *len
311 )
312 {
313 const struct fdt_node_header *nh = _fdt_offset_ptr (fdt, nodeoffset);
314 int err;
315
316 if ( ((err = fdt_check_header (fdt)) != 0)
317 || ((err = _fdt_check_node_offset (fdt, nodeoffset)) < 0))
318 {
319 goto fail;
320 }
321
322 if (len) {
323 *len = strlen (nh->name);
324 }
325
326 return nh->name;
327
328 fail:
329 if (len) {
330 *len = err;
331 }
332
333 return NULL;
334 }
335
336 int
337 fdt_first_property_offset (
338 const void *fdt,
339 int nodeoffset
340 )
341 {
342 int offset;
343
344 if ((offset = _fdt_check_node_offset (fdt, nodeoffset)) < 0) {
345 return offset;
346 }
347
348 return _nextprop (fdt, offset);
349 }
350
351 int
352 fdt_next_property_offset (
353 const void *fdt,
354 int offset
355 )
356 {
357 if ((offset = _fdt_check_prop_offset (fdt, offset)) < 0) {
358 return offset;
359 }
360
361 return _nextprop (fdt, offset);
362 }
363
364 const struct fdt_property *
365 fdt_get_property_by_offset (
366 const void *fdt,
367 int offset,
368 int *lenp
369 )
370 {
371 int err;
372 const struct fdt_property *prop;
373
374 if ((err = _fdt_check_prop_offset (fdt, offset)) < 0) {
375 if (lenp) {
376 *lenp = err;
377 }
378
379 return NULL;
380 }
381
382 prop = _fdt_offset_ptr (fdt, offset);
383
384 if (lenp) {
385 *lenp = fdt32_to_cpu (prop->len);
386 }
387
388 return prop;
389 }
390
391 const struct fdt_property *
392 fdt_get_property_namelen (
393 const void *fdt,
394 int offset,
395 const char *name,
396 int namelen,
397 int *lenp
398 )
399 {
400 for (offset = fdt_first_property_offset (fdt, offset);
401 (offset >= 0);
402 (offset = fdt_next_property_offset (fdt, offset)))
403 {
404 const struct fdt_property *prop;
405
406 if (!(prop = fdt_get_property_by_offset (fdt, offset, lenp))) {
407 offset = -FDT_ERR_INTERNAL;
408 break;
409 }
410
411 if (_fdt_string_eq (
412 fdt,
413 fdt32_to_cpu (prop->nameoff),
414 name,
415 namelen
416 ))
417 {
418 return prop;
419 }
420 }
421
422 if (lenp) {
423 *lenp = offset;
424 }
425
426 return NULL;
427 }
428
429 const struct fdt_property *
430 fdt_get_property (
431 const void *fdt,
432 int nodeoffset,
433 const char *name,
434 int *lenp
435 )
436 {
437 return fdt_get_property_namelen (
438 fdt,
439 nodeoffset,
440 name,
441 strlen (name),
442 lenp
443 );
444 }
445
446 const void *
447 fdt_getprop_namelen (
448 const void *fdt,
449 int nodeoffset,
450 const char *name,
451 int namelen,
452 int *lenp
453 )
454 {
455 const struct fdt_property *prop;
456
457 prop = fdt_get_property_namelen (fdt, nodeoffset, name, namelen, lenp);
458 if (!prop) {
459 return NULL;
460 }
461
462 return prop->data;
463 }
464
465 const void *
466 fdt_getprop_by_offset (
467 const void *fdt,
468 int offset,
469 const char **namep,
470 int *lenp
471 )
472 {
473 const struct fdt_property *prop;
474
475 prop = fdt_get_property_by_offset (fdt, offset, lenp);
476 if (!prop) {
477 return NULL;
478 }
479
480 if (namep) {
481 *namep = fdt_string (fdt, fdt32_to_cpu (prop->nameoff));
482 }
483
484 return prop->data;
485 }
486
487 const void *
488 fdt_getprop (
489 const void *fdt,
490 int nodeoffset,
491 const char *name,
492 int *lenp
493 )
494 {
495 return fdt_getprop_namelen (fdt, nodeoffset, name, strlen (name), lenp);
496 }
497
498 uint32_t
499 fdt_get_phandle (
500 const void *fdt,
501 int nodeoffset
502 )
503 {
504 const fdt32_t *php;
505 int len;
506
507 /* FIXME: This is a bit sub-optimal, since we potentially scan
508 * over all the properties twice. */
509 php = fdt_getprop (fdt, nodeoffset, "phandle", &len);
510 if (!php || (len != sizeof (*php))) {
511 php = fdt_getprop (fdt, nodeoffset, "linux,phandle", &len);
512 if (!php || (len != sizeof (*php))) {
513 return 0;
514 }
515 }
516
517 return fdt32_to_cpu (*php);
518 }
519
520 const char *
521 fdt_get_alias_namelen (
522 const void *fdt,
523 const char *name,
524 int namelen
525 )
526 {
527 int aliasoffset;
528
529 aliasoffset = fdt_path_offset (fdt, "/aliases");
530 if (aliasoffset < 0) {
531 return NULL;
532 }
533
534 return fdt_getprop_namelen (fdt, aliasoffset, name, namelen, NULL);
535 }
536
537 const char *
538 fdt_get_alias (
539 const void *fdt,
540 const char *name
541 )
542 {
543 return fdt_get_alias_namelen (fdt, name, strlen (name));
544 }
545
546 int
547 fdt_get_path (
548 const void *fdt,
549 int nodeoffset,
550 char *buf,
551 int buflen
552 )
553 {
554 int pdepth = 0, p = 0;
555 int offset, depth, namelen;
556 const char *name;
557
558 FDT_CHECK_HEADER (fdt);
559
560 if (buflen < 2) {
561 return -FDT_ERR_NOSPACE;
562 }
563
564 for (offset = 0, depth = 0;
565 (offset >= 0) && (offset <= nodeoffset);
566 offset = fdt_next_node (fdt, offset, &depth))
567 {
568 while (pdepth > depth) {
569 do {
570 p--;
571 } while (buf[p-1] != '/');
572
573 pdepth--;
574 }
575
576 if (pdepth >= depth) {
577 name = fdt_get_name (fdt, offset, &namelen);
578 if (!name) {
579 return namelen;
580 }
581
582 if ((p + namelen + 1) <= buflen) {
583 memcpy (buf + p, name, namelen);
584 p += namelen;
585 buf[p++] = '/';
586 pdepth++;
587 }
588 }
589
590 if (offset == nodeoffset) {
591 if (pdepth < (depth + 1)) {
592 return -FDT_ERR_NOSPACE;
593 }
594
595 if (p > 1) {
596 /* special case so that root path is "/", not "" */
597 p--;
598 }
599
600 buf[p] = '\0';
601 return 0;
602 }
603 }
604
605 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) {
606 return -FDT_ERR_BADOFFSET;
607 } else if (offset == -FDT_ERR_BADOFFSET) {
608 return -FDT_ERR_BADSTRUCTURE;
609 }
610
611 return offset; /* error from fdt_next_node() */
612 }
613
614 int
615 fdt_supernode_atdepth_offset (
616 const void *fdt,
617 int nodeoffset,
618 int supernodedepth,
619 int *nodedepth
620 )
621 {
622 int offset, depth;
623 int supernodeoffset = -FDT_ERR_INTERNAL;
624
625 FDT_CHECK_HEADER (fdt);
626
627 if (supernodedepth < 0) {
628 return -FDT_ERR_NOTFOUND;
629 }
630
631 for (offset = 0, depth = 0;
632 (offset >= 0) && (offset <= nodeoffset);
633 offset = fdt_next_node (fdt, offset, &depth))
634 {
635 if (depth == supernodedepth) {
636 supernodeoffset = offset;
637 }
638
639 if (offset == nodeoffset) {
640 if (nodedepth) {
641 *nodedepth = depth;
642 }
643
644 if (supernodedepth > depth) {
645 return -FDT_ERR_NOTFOUND;
646 } else {
647 return supernodeoffset;
648 }
649 }
650 }
651
652 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) {
653 return -FDT_ERR_BADOFFSET;
654 } else if (offset == -FDT_ERR_BADOFFSET) {
655 return -FDT_ERR_BADSTRUCTURE;
656 }
657
658 return offset; /* error from fdt_next_node() */
659 }
660
661 int
662 fdt_node_depth (
663 const void *fdt,
664 int nodeoffset
665 )
666 {
667 int nodedepth;
668 int err;
669
670 err = fdt_supernode_atdepth_offset (fdt, nodeoffset, 0, &nodedepth);
671 if (err) {
672 return (err < 0) ? err : -FDT_ERR_INTERNAL;
673 }
674
675 return nodedepth;
676 }
677
678 int
679 fdt_parent_offset (
680 const void *fdt,
681 int nodeoffset
682 )
683 {
684 int nodedepth = fdt_node_depth (fdt, nodeoffset);
685
686 if (nodedepth < 0) {
687 return nodedepth;
688 }
689
690 return fdt_supernode_atdepth_offset (
691 fdt,
692 nodeoffset,
693 nodedepth - 1,
694 NULL
695 );
696 }
697
698 int
699 fdt_node_offset_by_prop_value (
700 const void *fdt,
701 int startoffset,
702 const char *propname,
703 const void *propval,
704 int proplen
705 )
706 {
707 int offset;
708 const void *val;
709 int len;
710
711 FDT_CHECK_HEADER (fdt);
712
713 /* FIXME: The algorithm here is pretty horrible: we scan each
714 * property of a node in fdt_getprop(), then if that didn't
715 * find what we want, we scan over them again making our way
716 * to the next node. Still it's the easiest to implement
717 * approach; performance can come later. */
718 for (offset = fdt_next_node (fdt, startoffset, NULL);
719 offset >= 0;
720 offset = fdt_next_node (fdt, offset, NULL))
721 {
722 val = fdt_getprop (fdt, offset, propname, &len);
723 if ( val && (len == proplen)
724 && (memcmp (val, propval, len) == 0))
725 {
726 return offset;
727 }
728 }
729
730 return offset; /* error from fdt_next_node() */
731 }
732
733 int
734 fdt_node_offset_by_phandle (
735 const void *fdt,
736 uint32_t phandle
737 )
738 {
739 int offset;
740
741 if ((phandle == 0) || (phandle == -1)) {
742 return -FDT_ERR_BADPHANDLE;
743 }
744
745 FDT_CHECK_HEADER (fdt);
746
747 /* FIXME: The algorithm here is pretty horrible: we
748 * potentially scan each property of a node in
749 * fdt_get_phandle(), then if that didn't find what
750 * we want, we scan over them again making our way to the next
751 * node. Still it's the easiest to implement approach;
752 * performance can come later. */
753 for (offset = fdt_next_node (fdt, -1, NULL);
754 offset >= 0;
755 offset = fdt_next_node (fdt, offset, NULL))
756 {
757 if (fdt_get_phandle (fdt, offset) == phandle) {
758 return offset;
759 }
760 }
761
762 return offset; /* error from fdt_next_node() */
763 }
764
765 int
766 fdt_stringlist_contains (
767 const char *strlist,
768 int listlen,
769 const char *str
770 )
771 {
772 int len = strlen (str);
773 const char *p;
774
775 while (listlen >= len) {
776 if (memcmp (str, strlist, len+1) == 0) {
777 return 1;
778 }
779
780 p = memchr (strlist, '\0', listlen);
781 if (!p) {
782 return 0; /* malformed strlist.. */
783 }
784
785 listlen -= (p-strlist) + 1;
786 strlist = p + 1;
787 }
788
789 return 0;
790 }
791
792 int
793 fdt_stringlist_count (
794 const void *fdt,
795 int nodeoffset,
796 const char *property
797 )
798 {
799 const char *list, *end;
800 int length, count = 0;
801
802 list = fdt_getprop (fdt, nodeoffset, property, &length);
803 if (!list) {
804 return length;
805 }
806
807 end = list + length;
808
809 while (list < end) {
810 length = strnlen (list, end - list) + 1;
811
812 /* Abort if the last string isn't properly NUL-terminated. */
813 if (list + length > end) {
814 return -FDT_ERR_BADVALUE;
815 }
816
817 list += length;
818 count++;
819 }
820
821 return count;
822 }
823
824 int
825 fdt_stringlist_search (
826 const void *fdt,
827 int nodeoffset,
828 const char *property,
829 const char *string
830 )
831 {
832 int length, len, idx = 0;
833 const char *list, *end;
834
835 list = fdt_getprop (fdt, nodeoffset, property, &length);
836 if (!list) {
837 return length;
838 }
839
840 len = strlen (string) + 1;
841 end = list + length;
842
843 while (list < end) {
844 length = strnlen (list, end - list) + 1;
845
846 /* Abort if the last string isn't properly NUL-terminated. */
847 if (list + length > end) {
848 return -FDT_ERR_BADVALUE;
849 }
850
851 if ((length == len) && (memcmp (list, string, length) == 0)) {
852 return idx;
853 }
854
855 list += length;
856 idx++;
857 }
858
859 return -FDT_ERR_NOTFOUND;
860 }
861
862 const char *
863 fdt_stringlist_get (
864 const void *fdt,
865 int nodeoffset,
866 const char *property,
867 int idx,
868 int *lenp
869 )
870 {
871 const char *list, *end;
872 int length;
873
874 list = fdt_getprop (fdt, nodeoffset, property, &length);
875 if (!list) {
876 if (lenp) {
877 *lenp = length;
878 }
879
880 return NULL;
881 }
882
883 end = list + length;
884
885 while (list < end) {
886 length = strnlen (list, end - list) + 1;
887
888 /* Abort if the last string isn't properly NUL-terminated. */
889 if (list + length > end) {
890 if (lenp) {
891 *lenp = -FDT_ERR_BADVALUE;
892 }
893
894 return NULL;
895 }
896
897 if (idx == 0) {
898 if (lenp) {
899 *lenp = length - 1;
900 }
901
902 return list;
903 }
904
905 list += length;
906 idx--;
907 }
908
909 if (lenp) {
910 *lenp = -FDT_ERR_NOTFOUND;
911 }
912
913 return NULL;
914 }
915
916 int
917 fdt_node_check_compatible (
918 const void *fdt,
919 int nodeoffset,
920 const char *compatible
921 )
922 {
923 const void *prop;
924 int len;
925
926 prop = fdt_getprop (fdt, nodeoffset, "compatible", &len);
927 if (!prop) {
928 return len;
929 }
930
931 return !fdt_stringlist_contains (prop, len, compatible);
932 }
933
934 int
935 fdt_node_offset_by_compatible (
936 const void *fdt,
937 int startoffset,
938 const char *compatible
939 )
940 {
941 int offset, err;
942
943 FDT_CHECK_HEADER (fdt);
944
945 /* FIXME: The algorithm here is pretty horrible: we scan each
946 * property of a node in fdt_node_check_compatible(), then if
947 * that didn't find what we want, we scan over them again
948 * making our way to the next node. Still it's the easiest to
949 * implement approach; performance can come later. */
950 for (offset = fdt_next_node (fdt, startoffset, NULL);
951 offset >= 0;
952 offset = fdt_next_node (fdt, offset, NULL))
953 {
954 err = fdt_node_check_compatible (fdt, offset, compatible);
955 if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) {
956 return err;
957 } else if (err == 0) {
958 return offset;
959 }
960 }
961
962 return offset; /* error from fdt_next_node() */
963 }