fdt_ro.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  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. #include <fdt.h>
  53. #include <libfdt.h>
  54. #include "libfdt_internal.h"
  55. #define CHECK_HEADER(fdt) \
  56. { \
  57. int err; \
  58. if ((err = fdt_check_header(fdt)) != 0) \
  59. return err; \
  60. }
  61. static int nodename_eq(const void *fdt, int offset,
  62. const char *s, int len)
  63. {
  64. const char *p = fdt_offset_ptr(fdt, offset, len+1);
  65. if (! p)
  66. /* short match */
  67. return 0;
  68. if (memcmp(p, s, len) != 0)
  69. return 0;
  70. if (p[len] == '\0')
  71. return 1;
  72. else if (!memchr(s, '@', len) && (p[len] == '@'))
  73. return 1;
  74. else
  75. return 0;
  76. }
  77. const char *fdt_string(const void *fdt, int stroffset)
  78. {
  79. return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
  80. }
  81. int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
  82. {
  83. CHECK_HEADER(fdt);
  84. *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
  85. *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
  86. return 0;
  87. }
  88. int fdt_num_mem_rsv(const void *fdt)
  89. {
  90. int i = 0;
  91. while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
  92. i++;
  93. return i;
  94. }
  95. int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
  96. const char *name, int namelen)
  97. {
  98. int level = 0;
  99. uint32_t tag;
  100. int offset, nextoffset;
  101. CHECK_HEADER(fdt);
  102. tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
  103. if (tag != FDT_BEGIN_NODE)
  104. return -FDT_ERR_BADOFFSET;
  105. do {
  106. offset = nextoffset;
  107. tag = fdt_next_tag(fdt, offset, &nextoffset);
  108. switch (tag) {
  109. case FDT_END:
  110. return -FDT_ERR_TRUNCATED;
  111. case FDT_BEGIN_NODE:
  112. level++;
  113. if (level != 1)
  114. continue;
  115. if (nodename_eq(fdt, offset+FDT_TAGSIZE, name, namelen))
  116. /* Found it! */
  117. return offset;
  118. break;
  119. case FDT_END_NODE:
  120. level--;
  121. break;
  122. case FDT_PROP:
  123. case FDT_NOP:
  124. break;
  125. default:
  126. return -FDT_ERR_BADSTRUCTURE;
  127. }
  128. } while (level >= 0);
  129. return -FDT_ERR_NOTFOUND;
  130. }
  131. int fdt_subnode_offset(const void *fdt, int parentoffset,
  132. const char *name)
  133. {
  134. return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
  135. }
  136. int fdt_path_offset(const void *fdt, const char *path)
  137. {
  138. const char *end = path + strlen(path);
  139. const char *p = path;
  140. int offset = 0;
  141. CHECK_HEADER(fdt);
  142. if (*path != '/')
  143. return -FDT_ERR_BADPATH;
  144. while (*p) {
  145. const char *q;
  146. while (*p == '/')
  147. p++;
  148. if (! *p)
  149. return offset;
  150. q = strchr(p, '/');
  151. if (! q)
  152. q = end;
  153. offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
  154. if (offset < 0)
  155. return offset;
  156. p = q;
  157. }
  158. return offset;
  159. }
  160. const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
  161. {
  162. const struct fdt_node_header *nh;
  163. int err;
  164. if ((err = fdt_check_header(fdt)) != 0)
  165. goto fail;
  166. err = -FDT_ERR_BADOFFSET;
  167. nh = fdt_offset_ptr(fdt, nodeoffset, sizeof(*nh));
  168. if (!nh || (fdt32_to_cpu(nh->tag) != FDT_BEGIN_NODE))
  169. goto fail;
  170. if (len)
  171. *len = strlen(nh->name);
  172. return nh->name;
  173. fail:
  174. if (len)
  175. *len = err;
  176. return NULL;
  177. }
  178. const struct fdt_property *fdt_get_property(const void *fdt,
  179. int nodeoffset,
  180. const char *name, int *lenp)
  181. {
  182. uint32_t tag;
  183. const struct fdt_property *prop;
  184. int namestroff;
  185. int offset, nextoffset;
  186. int err;
  187. if ((err = fdt_check_header(fdt)) != 0)
  188. goto fail;
  189. err = -FDT_ERR_BADOFFSET;
  190. if (nodeoffset % FDT_TAGSIZE)
  191. goto fail;
  192. tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
  193. if (tag != FDT_BEGIN_NODE)
  194. goto fail;
  195. do {
  196. offset = nextoffset;
  197. tag = fdt_next_tag(fdt, offset, &nextoffset);
  198. switch (tag) {
  199. case FDT_END:
  200. err = -FDT_ERR_TRUNCATED;
  201. goto fail;
  202. case FDT_BEGIN_NODE:
  203. case FDT_END_NODE:
  204. case FDT_NOP:
  205. break;
  206. case FDT_PROP:
  207. err = -FDT_ERR_BADSTRUCTURE;
  208. prop = fdt_offset_ptr(fdt, offset, sizeof(*prop));
  209. if (! prop)
  210. goto fail;
  211. namestroff = fdt32_to_cpu(prop->nameoff);
  212. if (streq(fdt_string(fdt, namestroff), name)) {
  213. /* Found it! */
  214. int len = fdt32_to_cpu(prop->len);
  215. prop = fdt_offset_ptr(fdt, offset,
  216. sizeof(*prop)+len);
  217. if (! prop)
  218. goto fail;
  219. if (lenp)
  220. *lenp = len;
  221. return prop;
  222. }
  223. break;
  224. default:
  225. err = -FDT_ERR_BADSTRUCTURE;
  226. goto fail;
  227. }
  228. } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
  229. err = -FDT_ERR_NOTFOUND;
  230. fail:
  231. if (lenp)
  232. *lenp = err;
  233. return NULL;
  234. }
  235. const void *fdt_getprop(const void *fdt, int nodeoffset,
  236. const char *name, int *lenp)
  237. {
  238. const struct fdt_property *prop;
  239. prop = fdt_get_property(fdt, nodeoffset, name, lenp);
  240. if (! prop)
  241. return NULL;
  242. return prop->data;
  243. }
  244. uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
  245. {
  246. const uint32_t *php;
  247. int len;
  248. php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
  249. if (!php || (len != sizeof(*php)))
  250. return 0;
  251. return fdt32_to_cpu(*php);
  252. }
  253. int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
  254. {
  255. uint32_t tag;
  256. int p = 0, overflow = 0;
  257. int offset, nextoffset, namelen;
  258. const char *name;
  259. CHECK_HEADER(fdt);
  260. tag = fdt_next_tag(fdt, 0, &nextoffset);
  261. if (tag != FDT_BEGIN_NODE)
  262. return -FDT_ERR_BADSTRUCTURE;
  263. if (buflen < 2)
  264. return -FDT_ERR_NOSPACE;
  265. buf[0] = '/';
  266. p = 1;
  267. while (nextoffset <= nodeoffset) {
  268. offset = nextoffset;
  269. tag = fdt_next_tag(fdt, offset, &nextoffset);
  270. switch (tag) {
  271. case FDT_END:
  272. return -FDT_ERR_BADOFFSET;
  273. case FDT_BEGIN_NODE:
  274. name = fdt_get_name(fdt, offset, &namelen);
  275. if (!name)
  276. return namelen;
  277. if (overflow || ((p + namelen + 1) > buflen)) {
  278. overflow++;
  279. break;
  280. }
  281. memcpy(buf + p, name, namelen);
  282. p += namelen;
  283. buf[p++] = '/';
  284. break;
  285. case FDT_END_NODE:
  286. if (overflow) {
  287. overflow--;
  288. break;
  289. }
  290. do {
  291. p--;
  292. } while (buf[p-1] != '/');
  293. break;
  294. case FDT_PROP:
  295. case FDT_NOP:
  296. break;
  297. default:
  298. return -FDT_ERR_BADSTRUCTURE;
  299. }
  300. }
  301. if (overflow)
  302. return -FDT_ERR_NOSPACE;
  303. if (p > 1) /* special case so that root path is "/", not "" */
  304. p--;
  305. buf[p] = '\0';
  306. return p;
  307. }
  308. int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
  309. int supernodedepth, int *nodedepth)
  310. {
  311. int level = -1;
  312. uint32_t tag;
  313. int offset, nextoffset = 0;
  314. int supernodeoffset = -FDT_ERR_INTERNAL;
  315. CHECK_HEADER(fdt);
  316. if (supernodedepth < 0)
  317. return -FDT_ERR_NOTFOUND;
  318. do {
  319. offset = nextoffset;
  320. tag = fdt_next_tag(fdt, offset, &nextoffset);
  321. switch (tag) {
  322. case FDT_END:
  323. return -FDT_ERR_BADOFFSET;
  324. case FDT_BEGIN_NODE:
  325. level++;
  326. if (level == supernodedepth)
  327. supernodeoffset = offset;
  328. break;
  329. case FDT_END_NODE:
  330. level--;
  331. break;
  332. case FDT_PROP:
  333. case FDT_NOP:
  334. break;
  335. default:
  336. return -FDT_ERR_BADSTRUCTURE;
  337. }
  338. } while (offset < nodeoffset);
  339. if (nodedepth)
  340. *nodedepth = level;
  341. if (supernodedepth > level)
  342. return -FDT_ERR_NOTFOUND;
  343. return supernodeoffset;
  344. }
  345. int fdt_node_depth(const void *fdt, int nodeoffset)
  346. {
  347. int nodedepth;
  348. int err;
  349. err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
  350. if (err)
  351. return (err < 0) ? err : -FDT_ERR_INTERNAL;
  352. return nodedepth;
  353. }
  354. int fdt_parent_offset(const void *fdt, int nodeoffset)
  355. {
  356. int nodedepth = fdt_node_depth(fdt, nodeoffset);
  357. if (nodedepth < 0)
  358. return nodedepth;
  359. return fdt_supernode_atdepth_offset(fdt, nodeoffset,
  360. nodedepth - 1, NULL);
  361. }
  362. int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
  363. const char *propname,
  364. const void *propval, int proplen)
  365. {
  366. uint32_t tag;
  367. int offset, nextoffset;
  368. const void *val;
  369. int len;
  370. CHECK_HEADER(fdt);
  371. if (startoffset >= 0) {
  372. tag = fdt_next_tag(fdt, startoffset, &nextoffset);
  373. if (tag != FDT_BEGIN_NODE)
  374. return -FDT_ERR_BADOFFSET;
  375. } else {
  376. nextoffset = 0;
  377. }
  378. /* FIXME: The algorithm here is pretty horrible: we scan each
  379. * property of a node in fdt_getprop(), then if that didn't
  380. * find what we want, we scan over them again making our way
  381. * to the next node. Still it's the easiest to implement
  382. * approach; performance can come later. */
  383. do {
  384. offset = nextoffset;
  385. tag = fdt_next_tag(fdt, offset, &nextoffset);
  386. switch (tag) {
  387. case FDT_BEGIN_NODE:
  388. val = fdt_getprop(fdt, offset, propname, &len);
  389. if (val
  390. && (len == proplen)
  391. && (memcmp(val, propval, len) == 0))
  392. return offset;
  393. break;
  394. case FDT_PROP:
  395. case FDT_END:
  396. case FDT_END_NODE:
  397. case FDT_NOP:
  398. break;
  399. default:
  400. return -FDT_ERR_BADSTRUCTURE;
  401. }
  402. } while (tag != FDT_END);
  403. return -FDT_ERR_NOTFOUND;
  404. }
  405. int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
  406. {
  407. if ((phandle == 0) || (phandle == -1))
  408. return -FDT_ERR_BADPHANDLE;
  409. phandle = cpu_to_fdt32(phandle);
  410. return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle",
  411. &phandle, sizeof(phandle));
  412. }
  413. int _stringlist_contains(const void *strlist, int listlen, const char *str)
  414. {
  415. int len = strlen(str);
  416. const void *p;
  417. while (listlen >= len) {
  418. if (memcmp(str, strlist, len+1) == 0)
  419. return 1;
  420. p = memchr(strlist, '\0', listlen);
  421. if (!p)
  422. return 0; /* malformed strlist.. */
  423. listlen -= (p-strlist) + 1;
  424. strlist = p + 1;
  425. }
  426. return 0;
  427. }
  428. int fdt_node_check_compatible(const void *fdt, int nodeoffset,
  429. const char *compatible)
  430. {
  431. const void *prop;
  432. int len;
  433. prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
  434. if (!prop)
  435. return len;
  436. if (_stringlist_contains(prop, len, compatible))
  437. return 0;
  438. else
  439. return 1;
  440. }
  441. int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
  442. const char *compatible)
  443. {
  444. uint32_t tag;
  445. int offset, nextoffset;
  446. int err;
  447. CHECK_HEADER(fdt);
  448. if (startoffset >= 0) {
  449. tag = fdt_next_tag(fdt, startoffset, &nextoffset);
  450. if (tag != FDT_BEGIN_NODE)
  451. return -FDT_ERR_BADOFFSET;
  452. } else {
  453. nextoffset = 0;
  454. }
  455. /* FIXME: The algorithm here is pretty horrible: we scan each
  456. * property of a node in fdt_node_check_compatible(), then if
  457. * that didn't find what we want, we scan over them again
  458. * making our way to the next node. Still it's the easiest to
  459. * implement approach; performance can come later. */
  460. do {
  461. offset = nextoffset;
  462. tag = fdt_next_tag(fdt, offset, &nextoffset);
  463. switch (tag) {
  464. case FDT_BEGIN_NODE:
  465. err = fdt_node_check_compatible(fdt, offset,
  466. compatible);
  467. if ((err < 0)
  468. && (err != -FDT_ERR_NOTFOUND))
  469. return err;
  470. else if (err == 0)
  471. return offset;
  472. break;
  473. case FDT_PROP:
  474. case FDT_END:
  475. case FDT_END_NODE:
  476. case FDT_NOP:
  477. break;
  478. default:
  479. return -FDT_ERR_BADSTRUCTURE;
  480. }
  481. } while (tag != FDT_END);
  482. return -FDT_ERR_NOTFOUND;
  483. }