fdt.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
  2. /*
  3. * libfdt - Flat Device Tree manipulation
  4. * Copyright (C) 2006 David Gibson, IBM Corporation.
  5. */
  6. #include "libfdt_env.h"
  7. #include <fdt.h>
  8. #include <libfdt.h>
  9. #include "libfdt_internal.h"
  10. /*
  11. * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks
  12. * that the given buffer contains what appears to be a flattened
  13. * device tree with sane information in its header.
  14. */
  15. int32_t fdt_ro_probe_(const void *fdt)
  16. {
  17. uint32_t totalsize = fdt_totalsize(fdt);
  18. if (fdt_magic(fdt) == FDT_MAGIC) {
  19. /* Complete tree */
  20. if (fdt_chk_version()) {
  21. if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
  22. return -FDT_ERR_BADVERSION;
  23. if (fdt_last_comp_version(fdt) >
  24. FDT_LAST_SUPPORTED_VERSION)
  25. return -FDT_ERR_BADVERSION;
  26. }
  27. } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
  28. /* Unfinished sequential-write blob */
  29. if (fdt_size_dt_struct(fdt) == 0)
  30. return -FDT_ERR_BADSTATE;
  31. } else {
  32. return -FDT_ERR_BADMAGIC;
  33. }
  34. if (totalsize < INT32_MAX)
  35. return totalsize;
  36. else
  37. return -FDT_ERR_TRUNCATED;
  38. }
  39. static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off)
  40. {
  41. return (off >= hdrsize) && (off <= totalsize);
  42. }
  43. static int check_block_(uint32_t hdrsize, uint32_t totalsize,
  44. uint32_t base, uint32_t size)
  45. {
  46. if (!check_off_(hdrsize, totalsize, base))
  47. return 0; /* block start out of bounds */
  48. if ((base + size) < base)
  49. return 0; /* overflow */
  50. if (!check_off_(hdrsize, totalsize, base + size))
  51. return 0; /* block end out of bounds */
  52. return 1;
  53. }
  54. size_t fdt_header_size_(uint32_t version)
  55. {
  56. if (version <= 1)
  57. return FDT_V1_SIZE;
  58. else if (version <= 2)
  59. return FDT_V2_SIZE;
  60. else if (version <= 3)
  61. return FDT_V3_SIZE;
  62. else if (version <= 16)
  63. return FDT_V16_SIZE;
  64. else
  65. return FDT_V17_SIZE;
  66. }
  67. size_t fdt_header_size(const void *fdt)
  68. {
  69. return fdt_chk_version() ? fdt_header_size_(fdt_version(fdt)) :
  70. FDT_V17_SIZE;
  71. }
  72. int fdt_check_header(const void *fdt)
  73. {
  74. size_t hdrsize;
  75. if (fdt_magic(fdt) != FDT_MAGIC)
  76. return -FDT_ERR_BADMAGIC;
  77. if (fdt_chk_version()) {
  78. if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
  79. || (fdt_last_comp_version(fdt) >
  80. FDT_LAST_SUPPORTED_VERSION))
  81. return -FDT_ERR_BADVERSION;
  82. if (fdt_version(fdt) < fdt_last_comp_version(fdt))
  83. return -FDT_ERR_BADVERSION;
  84. }
  85. hdrsize = fdt_header_size(fdt);
  86. if (fdt_chk_basic()) {
  87. if ((fdt_totalsize(fdt) < hdrsize)
  88. || (fdt_totalsize(fdt) > INT_MAX))
  89. return -FDT_ERR_TRUNCATED;
  90. /* Bounds check memrsv block */
  91. if (!check_off_(hdrsize, fdt_totalsize(fdt),
  92. fdt_off_mem_rsvmap(fdt)))
  93. return -FDT_ERR_TRUNCATED;
  94. }
  95. if (fdt_chk_extra()) {
  96. /* Bounds check structure block */
  97. if (fdt_chk_version() && fdt_version(fdt) < 17) {
  98. if (!check_off_(hdrsize, fdt_totalsize(fdt),
  99. fdt_off_dt_struct(fdt)))
  100. return -FDT_ERR_TRUNCATED;
  101. } else {
  102. if (!check_block_(hdrsize, fdt_totalsize(fdt),
  103. fdt_off_dt_struct(fdt),
  104. fdt_size_dt_struct(fdt)))
  105. return -FDT_ERR_TRUNCATED;
  106. }
  107. /* Bounds check strings block */
  108. if (!check_block_(hdrsize, fdt_totalsize(fdt),
  109. fdt_off_dt_strings(fdt),
  110. fdt_size_dt_strings(fdt)))
  111. return -FDT_ERR_TRUNCATED;
  112. }
  113. return 0;
  114. }
  115. const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
  116. {
  117. unsigned absoffset = offset + fdt_off_dt_struct(fdt);
  118. if (fdt_chk_basic())
  119. if ((absoffset < offset)
  120. || ((absoffset + len) < absoffset)
  121. || (absoffset + len) > fdt_totalsize(fdt))
  122. return NULL;
  123. if (!fdt_chk_version() || fdt_version(fdt) >= 0x11)
  124. if (((offset + len) < offset)
  125. || ((offset + len) > fdt_size_dt_struct(fdt)))
  126. return NULL;
  127. return fdt_offset_ptr_(fdt, offset);
  128. }
  129. uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
  130. {
  131. const fdt32_t *tagp, *lenp;
  132. uint32_t tag;
  133. int offset = startoffset;
  134. const char *p;
  135. *nextoffset = -FDT_ERR_TRUNCATED;
  136. tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
  137. if (fdt_chk_basic() && !tagp)
  138. return FDT_END; /* premature end */
  139. tag = fdt32_to_cpu(*tagp);
  140. offset += FDT_TAGSIZE;
  141. *nextoffset = -FDT_ERR_BADSTRUCTURE;
  142. switch (tag) {
  143. case FDT_BEGIN_NODE:
  144. /* skip name */
  145. do {
  146. p = fdt_offset_ptr(fdt, offset++, 1);
  147. } while (p && (*p != '\0'));
  148. if (fdt_chk_basic() && !p)
  149. return FDT_END; /* premature end */
  150. break;
  151. case FDT_PROP:
  152. lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
  153. if (fdt_chk_basic() && !lenp)
  154. return FDT_END; /* premature end */
  155. /* skip-name offset, length and value */
  156. offset += sizeof(struct fdt_property) - FDT_TAGSIZE
  157. + fdt32_to_cpu(*lenp);
  158. if (fdt_chk_version() &&
  159. fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 &&
  160. ((offset - fdt32_to_cpu(*lenp)) % 8) != 0)
  161. offset += 4;
  162. break;
  163. case FDT_END:
  164. case FDT_END_NODE:
  165. case FDT_NOP:
  166. break;
  167. default:
  168. return FDT_END;
  169. }
  170. if (fdt_chk_basic() &&
  171. !fdt_offset_ptr(fdt, startoffset, offset - startoffset))
  172. return FDT_END; /* premature end */
  173. *nextoffset = FDT_TAGALIGN(offset);
  174. return tag;
  175. }
  176. int fdt_check_node_offset_(const void *fdt, int offset)
  177. {
  178. if ((offset < 0) || (offset % FDT_TAGSIZE)
  179. || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
  180. return -FDT_ERR_BADOFFSET;
  181. return offset;
  182. }
  183. int fdt_check_prop_offset_(const void *fdt, int offset)
  184. {
  185. if ((offset < 0) || (offset % FDT_TAGSIZE)
  186. || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
  187. return -FDT_ERR_BADOFFSET;
  188. return offset;
  189. }
  190. int fdt_next_node(const void *fdt, int offset, int *depth)
  191. {
  192. int nextoffset = 0;
  193. uint32_t tag;
  194. if (offset >= 0)
  195. if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0)
  196. return nextoffset;
  197. do {
  198. offset = nextoffset;
  199. tag = fdt_next_tag(fdt, offset, &nextoffset);
  200. switch (tag) {
  201. case FDT_PROP:
  202. case FDT_NOP:
  203. break;
  204. case FDT_BEGIN_NODE:
  205. if (depth)
  206. (*depth)++;
  207. break;
  208. case FDT_END_NODE:
  209. if (depth && ((--(*depth)) < 0))
  210. return nextoffset;
  211. break;
  212. case FDT_END:
  213. if ((nextoffset >= 0)
  214. || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
  215. return -FDT_ERR_NOTFOUND;
  216. else
  217. return nextoffset;
  218. }
  219. } while (tag != FDT_BEGIN_NODE);
  220. return offset;
  221. }
  222. int fdt_first_subnode(const void *fdt, int offset)
  223. {
  224. int depth = 0;
  225. offset = fdt_next_node(fdt, offset, &depth);
  226. if (offset < 0 || depth != 1)
  227. return -FDT_ERR_NOTFOUND;
  228. return offset;
  229. }
  230. int fdt_next_subnode(const void *fdt, int offset)
  231. {
  232. int depth = 1;
  233. /*
  234. * With respect to the parent, the depth of the next subnode will be
  235. * the same as the last.
  236. */
  237. do {
  238. offset = fdt_next_node(fdt, offset, &depth);
  239. if (offset < 0 || depth < 1)
  240. return -FDT_ERR_NOTFOUND;
  241. } while (depth > 1);
  242. return offset;
  243. }
  244. const char *fdt_find_string_(const char *strtab, int tabsize, const char *s)
  245. {
  246. int len = strlen(s) + 1;
  247. const char *last = strtab + tabsize - len;
  248. const char *p;
  249. for (p = strtab; p <= last; p++)
  250. if (memcmp(p, s, len) == 0)
  251. return p;
  252. return NULL;
  253. }
  254. int fdt_move(const void *fdt, void *buf, int bufsize)
  255. {
  256. FDT_RO_PROBE(fdt);
  257. if (fdt_totalsize(fdt) > bufsize)
  258. return -FDT_ERR_NOSPACE;
  259. memmove(buf, fdt, fdt_totalsize(fdt));
  260. return 0;
  261. }