fdt_ro.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. /*
  2. * libfdt - Flat Device Tree manipulation
  3. * Copyright (C) 2006 David Gibson, IBM Corporation.
  4. * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause
  5. */
  6. #include <libfdt_env.h>
  7. #ifndef USE_HOSTCC
  8. #include <fdt.h>
  9. #include <libfdt.h>
  10. #else
  11. #include "fdt_host.h"
  12. #endif
  13. #include "libfdt_internal.h"
  14. static int _fdt_nodename_eq(const void *fdt, int offset,
  15. const char *s, int len)
  16. {
  17. const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
  18. if (!p)
  19. /* short match */
  20. return 0;
  21. if (memcmp(p, s, len) != 0)
  22. return 0;
  23. if (p[len] == '\0')
  24. return 1;
  25. else if (!memchr(s, '@', len) && (p[len] == '@'))
  26. return 1;
  27. else
  28. return 0;
  29. }
  30. const char *fdt_string(const void *fdt, int stroffset)
  31. {
  32. return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
  33. }
  34. static int _fdt_string_eq(const void *fdt, int stroffset,
  35. const char *s, int len)
  36. {
  37. const char *p = fdt_string(fdt, stroffset);
  38. return (strnlen(p, len + 1) == len) && (memcmp(p, s, len) == 0);
  39. }
  40. uint32_t fdt_get_max_phandle(const void *fdt)
  41. {
  42. uint32_t max_phandle = 0;
  43. int offset;
  44. for (offset = fdt_next_node(fdt, -1, NULL);;
  45. offset = fdt_next_node(fdt, offset, NULL)) {
  46. uint32_t phandle;
  47. if (offset == -FDT_ERR_NOTFOUND)
  48. return max_phandle;
  49. if (offset < 0)
  50. return (uint32_t)-1;
  51. phandle = fdt_get_phandle(fdt, offset);
  52. if (phandle == (uint32_t)-1)
  53. continue;
  54. if (phandle > max_phandle)
  55. max_phandle = phandle;
  56. }
  57. return 0;
  58. }
  59. int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
  60. {
  61. FDT_CHECK_HEADER(fdt);
  62. *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
  63. *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
  64. return 0;
  65. }
  66. int fdt_num_mem_rsv(const void *fdt)
  67. {
  68. int i = 0;
  69. while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
  70. i++;
  71. return i;
  72. }
  73. static int _nextprop(const void *fdt, int offset)
  74. {
  75. uint32_t tag;
  76. int nextoffset;
  77. do {
  78. tag = fdt_next_tag(fdt, offset, &nextoffset);
  79. switch (tag) {
  80. case FDT_END:
  81. if (nextoffset >= 0)
  82. return -FDT_ERR_BADSTRUCTURE;
  83. else
  84. return nextoffset;
  85. case FDT_PROP:
  86. return offset;
  87. }
  88. offset = nextoffset;
  89. } while (tag == FDT_NOP);
  90. return -FDT_ERR_NOTFOUND;
  91. }
  92. int fdt_subnode_offset_namelen(const void *fdt, int offset,
  93. const char *name, int namelen)
  94. {
  95. int depth;
  96. FDT_CHECK_HEADER(fdt);
  97. for (depth = 0;
  98. (offset >= 0) && (depth >= 0);
  99. offset = fdt_next_node(fdt, offset, &depth))
  100. if ((depth == 1)
  101. && _fdt_nodename_eq(fdt, offset, name, namelen))
  102. return offset;
  103. if (depth < 0)
  104. return -FDT_ERR_NOTFOUND;
  105. return offset; /* error */
  106. }
  107. int fdt_subnode_offset(const void *fdt, int parentoffset,
  108. const char *name)
  109. {
  110. return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
  111. }
  112. /*
  113. * Find the next of path separator, note we need to search for both '/' and ':'
  114. * and then take the first one so that we do the right thing for e.g.
  115. * "foo/bar:option" and "bar:option/otheroption", both of which happen, so
  116. * first searching for either ':' or '/' does not work.
  117. */
  118. static const char *fdt_path_next_separator(const char *path, int len)
  119. {
  120. const void *sep1 = memchr(path, '/', len);
  121. const void *sep2 = memchr(path, ':', len);
  122. if (sep1 && sep2)
  123. return (sep1 < sep2) ? sep1 : sep2;
  124. else if (sep1)
  125. return sep1;
  126. else
  127. return sep2;
  128. }
  129. int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
  130. {
  131. const char *end = path + namelen;
  132. const char *p = path;
  133. int offset = 0;
  134. FDT_CHECK_HEADER(fdt);
  135. /* see if we have an alias */
  136. if (*path != '/') {
  137. const char *q = fdt_path_next_separator(path, namelen);
  138. if (!q)
  139. q = end;
  140. p = fdt_get_alias_namelen(fdt, p, q - p);
  141. if (!p)
  142. return -FDT_ERR_BADPATH;
  143. offset = fdt_path_offset(fdt, p);
  144. p = q;
  145. }
  146. while (*p && (p < end)) {
  147. const char *q;
  148. while (*p == '/')
  149. p++;
  150. if (*p == '\0' || *p == ':')
  151. return offset;
  152. q = fdt_path_next_separator(p, end - p);
  153. if (!q)
  154. q = end;
  155. offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
  156. if (offset < 0)
  157. return offset;
  158. p = q;
  159. }
  160. return offset;
  161. }
  162. const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
  163. {
  164. const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
  165. int err;
  166. if (((err = fdt_check_header(fdt)) != 0)
  167. || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
  168. goto fail;
  169. if (len)
  170. *len = strlen(nh->name);
  171. return nh->name;
  172. fail:
  173. if (len)
  174. *len = err;
  175. return NULL;
  176. }
  177. int fdt_first_property_offset(const void *fdt, int nodeoffset)
  178. {
  179. int offset;
  180. if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
  181. return offset;
  182. return _nextprop(fdt, offset);
  183. }
  184. int fdt_next_property_offset(const void *fdt, int offset)
  185. {
  186. if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0)
  187. return offset;
  188. return _nextprop(fdt, offset);
  189. }
  190. const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
  191. int offset,
  192. int *lenp)
  193. {
  194. int err;
  195. const struct fdt_property *prop;
  196. if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
  197. if (lenp)
  198. *lenp = err;
  199. return NULL;
  200. }
  201. prop = _fdt_offset_ptr(fdt, offset);
  202. if (lenp)
  203. *lenp = fdt32_to_cpu(prop->len);
  204. return prop;
  205. }
  206. const struct fdt_property *fdt_get_property_namelen(const void *fdt,
  207. int offset,
  208. const char *name,
  209. int namelen, int *lenp)
  210. {
  211. for (offset = fdt_first_property_offset(fdt, offset);
  212. (offset >= 0);
  213. (offset = fdt_next_property_offset(fdt, offset))) {
  214. const struct fdt_property *prop;
  215. if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) {
  216. offset = -FDT_ERR_INTERNAL;
  217. break;
  218. }
  219. if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
  220. name, namelen))
  221. return prop;
  222. }
  223. if (lenp)
  224. *lenp = offset;
  225. return NULL;
  226. }
  227. const struct fdt_property *fdt_get_property(const void *fdt,
  228. int nodeoffset,
  229. const char *name, int *lenp)
  230. {
  231. return fdt_get_property_namelen(fdt, nodeoffset, name,
  232. strlen(name), lenp);
  233. }
  234. const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
  235. const char *name, int namelen, int *lenp)
  236. {
  237. const struct fdt_property *prop;
  238. prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
  239. if (!prop)
  240. return NULL;
  241. return prop->data;
  242. }
  243. const void *fdt_getprop_by_offset(const void *fdt, int offset,
  244. const char **namep, int *lenp)
  245. {
  246. const struct fdt_property *prop;
  247. prop = fdt_get_property_by_offset(fdt, offset, lenp);
  248. if (!prop)
  249. return NULL;
  250. if (namep)
  251. *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
  252. return prop->data;
  253. }
  254. const void *fdt_getprop(const void *fdt, int nodeoffset,
  255. const char *name, int *lenp)
  256. {
  257. return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
  258. }
  259. uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
  260. {
  261. const fdt32_t *php;
  262. int len;
  263. /* FIXME: This is a bit sub-optimal, since we potentially scan
  264. * over all the properties twice. */
  265. php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
  266. if (!php || (len != sizeof(*php))) {
  267. php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
  268. if (!php || (len != sizeof(*php)))
  269. return 0;
  270. }
  271. return fdt32_to_cpu(*php);
  272. }
  273. const char *fdt_get_alias_namelen(const void *fdt,
  274. const char *name, int namelen)
  275. {
  276. int aliasoffset;
  277. aliasoffset = fdt_path_offset(fdt, "/aliases");
  278. if (aliasoffset < 0)
  279. return NULL;
  280. return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
  281. }
  282. const char *fdt_get_alias(const void *fdt, const char *name)
  283. {
  284. return fdt_get_alias_namelen(fdt, name, strlen(name));
  285. }
  286. int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
  287. {
  288. int pdepth = 0, p = 0;
  289. int offset, depth, namelen;
  290. const char *name;
  291. FDT_CHECK_HEADER(fdt);
  292. if (buflen < 2)
  293. return -FDT_ERR_NOSPACE;
  294. for (offset = 0, depth = 0;
  295. (offset >= 0) && (offset <= nodeoffset);
  296. offset = fdt_next_node(fdt, offset, &depth)) {
  297. while (pdepth > depth) {
  298. do {
  299. p--;
  300. } while (buf[p-1] != '/');
  301. pdepth--;
  302. }
  303. if (pdepth >= depth) {
  304. name = fdt_get_name(fdt, offset, &namelen);
  305. if (!name)
  306. return namelen;
  307. if ((p + namelen + 1) <= buflen) {
  308. memcpy(buf + p, name, namelen);
  309. p += namelen;
  310. buf[p++] = '/';
  311. pdepth++;
  312. }
  313. }
  314. if (offset == nodeoffset) {
  315. if (pdepth < (depth + 1))
  316. return -FDT_ERR_NOSPACE;
  317. if (p > 1) /* special case so that root path is "/", not "" */
  318. p--;
  319. buf[p] = '\0';
  320. return 0;
  321. }
  322. }
  323. if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
  324. return -FDT_ERR_BADOFFSET;
  325. else if (offset == -FDT_ERR_BADOFFSET)
  326. return -FDT_ERR_BADSTRUCTURE;
  327. return offset; /* error from fdt_next_node() */
  328. }
  329. int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
  330. int supernodedepth, int *nodedepth)
  331. {
  332. int offset, depth;
  333. int supernodeoffset = -FDT_ERR_INTERNAL;
  334. FDT_CHECK_HEADER(fdt);
  335. if (supernodedepth < 0)
  336. return -FDT_ERR_NOTFOUND;
  337. for (offset = 0, depth = 0;
  338. (offset >= 0) && (offset <= nodeoffset);
  339. offset = fdt_next_node(fdt, offset, &depth)) {
  340. if (depth == supernodedepth)
  341. supernodeoffset = offset;
  342. if (offset == nodeoffset) {
  343. if (nodedepth)
  344. *nodedepth = depth;
  345. if (supernodedepth > depth)
  346. return -FDT_ERR_NOTFOUND;
  347. else
  348. return supernodeoffset;
  349. }
  350. }
  351. if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
  352. return -FDT_ERR_BADOFFSET;
  353. else if (offset == -FDT_ERR_BADOFFSET)
  354. return -FDT_ERR_BADSTRUCTURE;
  355. return offset; /* error from fdt_next_node() */
  356. }
  357. int fdt_node_depth(const void *fdt, int nodeoffset)
  358. {
  359. int nodedepth;
  360. int err;
  361. err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
  362. if (err)
  363. return (err < 0) ? err : -FDT_ERR_INTERNAL;
  364. return nodedepth;
  365. }
  366. int fdt_parent_offset(const void *fdt, int nodeoffset)
  367. {
  368. int nodedepth = fdt_node_depth(fdt, nodeoffset);
  369. if (nodedepth < 0)
  370. return nodedepth;
  371. return fdt_supernode_atdepth_offset(fdt, nodeoffset,
  372. nodedepth - 1, NULL);
  373. }
  374. int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
  375. const char *propname,
  376. const void *propval, int proplen)
  377. {
  378. int offset;
  379. const void *val;
  380. int len;
  381. FDT_CHECK_HEADER(fdt);
  382. /* FIXME: The algorithm here is pretty horrible: we scan each
  383. * property of a node in fdt_getprop(), then if that didn't
  384. * find what we want, we scan over them again making our way
  385. * to the next node. Still it's the easiest to implement
  386. * approach; performance can come later. */
  387. for (offset = fdt_next_node(fdt, startoffset, NULL);
  388. offset >= 0;
  389. offset = fdt_next_node(fdt, offset, NULL)) {
  390. val = fdt_getprop(fdt, offset, propname, &len);
  391. if (val && (len == proplen)
  392. && (memcmp(val, propval, len) == 0))
  393. return offset;
  394. }
  395. return offset; /* error from fdt_next_node() */
  396. }
  397. int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
  398. {
  399. int offset;
  400. if ((phandle == 0) || (phandle == -1))
  401. return -FDT_ERR_BADPHANDLE;
  402. FDT_CHECK_HEADER(fdt);
  403. /* FIXME: The algorithm here is pretty horrible: we
  404. * potentially scan each property of a node in
  405. * fdt_get_phandle(), then if that didn't find what
  406. * we want, we scan over them again making our way to the next
  407. * node. Still it's the easiest to implement approach;
  408. * performance can come later. */
  409. for (offset = fdt_next_node(fdt, -1, NULL);
  410. offset >= 0;
  411. offset = fdt_next_node(fdt, offset, NULL)) {
  412. if (fdt_get_phandle(fdt, offset) == phandle)
  413. return offset;
  414. }
  415. return offset; /* error from fdt_next_node() */
  416. }
  417. int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
  418. {
  419. int len = strlen(str);
  420. const char *p;
  421. while (listlen >= len) {
  422. if (memcmp(str, strlist, len+1) == 0)
  423. return 1;
  424. p = memchr(strlist, '\0', listlen);
  425. if (!p)
  426. return 0; /* malformed strlist.. */
  427. listlen -= (p-strlist) + 1;
  428. strlist = p + 1;
  429. }
  430. return 0;
  431. }
  432. int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
  433. {
  434. const char *list, *end;
  435. int length, count = 0;
  436. list = fdt_getprop(fdt, nodeoffset, property, &length);
  437. if (!list)
  438. return -length;
  439. end = list + length;
  440. while (list < end) {
  441. length = strnlen(list, end - list) + 1;
  442. /* Abort if the last string isn't properly NUL-terminated. */
  443. if (list + length > end)
  444. return -FDT_ERR_BADVALUE;
  445. list += length;
  446. count++;
  447. }
  448. return count;
  449. }
  450. int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
  451. const char *string)
  452. {
  453. int length, len, idx = 0;
  454. const char *list, *end;
  455. list = fdt_getprop(fdt, nodeoffset, property, &length);
  456. if (!list)
  457. return -length;
  458. len = strlen(string) + 1;
  459. end = list + length;
  460. while (list < end) {
  461. length = strnlen(list, end - list) + 1;
  462. /* Abort if the last string isn't properly NUL-terminated. */
  463. if (list + length > end)
  464. return -FDT_ERR_BADVALUE;
  465. if (length == len && memcmp(list, string, length) == 0)
  466. return idx;
  467. list += length;
  468. idx++;
  469. }
  470. return -FDT_ERR_NOTFOUND;
  471. }
  472. const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
  473. const char *property, int idx,
  474. int *lenp)
  475. {
  476. const char *list, *end;
  477. int length;
  478. list = fdt_getprop(fdt, nodeoffset, property, &length);
  479. if (!list) {
  480. if (lenp)
  481. *lenp = length;
  482. return NULL;
  483. }
  484. end = list + length;
  485. while (list < end) {
  486. length = strnlen(list, end - list) + 1;
  487. /* Abort if the last string isn't properly NUL-terminated. */
  488. if (list + length > end) {
  489. if (lenp)
  490. *lenp = -FDT_ERR_BADVALUE;
  491. return NULL;
  492. }
  493. if (idx == 0) {
  494. if (lenp)
  495. *lenp = length - 1;
  496. return list;
  497. }
  498. list += length;
  499. idx--;
  500. }
  501. if (lenp)
  502. *lenp = -FDT_ERR_NOTFOUND;
  503. return NULL;
  504. }
  505. int fdt_node_check_compatible(const void *fdt, int nodeoffset,
  506. const char *compatible)
  507. {
  508. const void *prop;
  509. int len;
  510. prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
  511. if (!prop)
  512. return len;
  513. return !fdt_stringlist_contains(prop, len, compatible);
  514. }
  515. int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
  516. const char *compatible)
  517. {
  518. int offset, err;
  519. FDT_CHECK_HEADER(fdt);
  520. /* FIXME: The algorithm here is pretty horrible: we scan each
  521. * property of a node in fdt_node_check_compatible(), then if
  522. * that didn't find what we want, we scan over them again
  523. * making our way to the next node. Still it's the easiest to
  524. * implement approach; performance can come later. */
  525. for (offset = fdt_next_node(fdt, startoffset, NULL);
  526. offset >= 0;
  527. offset = fdt_next_node(fdt, offset, NULL)) {
  528. err = fdt_node_check_compatible(fdt, offset, compatible);
  529. if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
  530. return err;
  531. else if (err == 0)
  532. return offset;
  533. }
  534. return offset; /* error from fdt_next_node() */
  535. }