1*243ce5d5SMadhukar Pappireddy // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) 2630b011fSAntonio Nino Diaz /* 3630b011fSAntonio Nino Diaz * libfdt - Flat Device Tree manipulation 4630b011fSAntonio Nino Diaz * Copyright (C) 2006 David Gibson, IBM Corporation. 5630b011fSAntonio Nino Diaz */ 6630b011fSAntonio Nino Diaz #include "libfdt_env.h" 7630b011fSAntonio Nino Diaz 8630b011fSAntonio Nino Diaz #include <fdt.h> 9630b011fSAntonio Nino Diaz #include <libfdt.h> 10630b011fSAntonio Nino Diaz 11630b011fSAntonio Nino Diaz #include "libfdt_internal.h" 12630b011fSAntonio Nino Diaz 13*243ce5d5SMadhukar Pappireddy /* 14*243ce5d5SMadhukar Pappireddy * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks 15*243ce5d5SMadhukar Pappireddy * that the given buffer contains what appears to be a flattened 16*243ce5d5SMadhukar Pappireddy * device tree with sane information in its header. 17*243ce5d5SMadhukar Pappireddy */ 18*243ce5d5SMadhukar Pappireddy int32_t fdt_ro_probe_(const void *fdt) 19630b011fSAntonio Nino Diaz { 20*243ce5d5SMadhukar Pappireddy uint32_t totalsize = fdt_totalsize(fdt); 21*243ce5d5SMadhukar Pappireddy 22*243ce5d5SMadhukar Pappireddy if (can_assume(VALID_DTB)) 23*243ce5d5SMadhukar Pappireddy return totalsize; 24*243ce5d5SMadhukar Pappireddy 25630b011fSAntonio Nino Diaz if (fdt_magic(fdt) == FDT_MAGIC) { 26630b011fSAntonio Nino Diaz /* Complete tree */ 27*243ce5d5SMadhukar Pappireddy if (!can_assume(LATEST)) { 28630b011fSAntonio Nino Diaz if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) 29630b011fSAntonio Nino Diaz return -FDT_ERR_BADVERSION; 30*243ce5d5SMadhukar Pappireddy if (fdt_last_comp_version(fdt) > 31*243ce5d5SMadhukar Pappireddy FDT_LAST_SUPPORTED_VERSION) 32630b011fSAntonio Nino Diaz return -FDT_ERR_BADVERSION; 33*243ce5d5SMadhukar Pappireddy } 34630b011fSAntonio Nino Diaz } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { 35630b011fSAntonio Nino Diaz /* Unfinished sequential-write blob */ 36*243ce5d5SMadhukar Pappireddy if (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0) 37630b011fSAntonio Nino Diaz return -FDT_ERR_BADSTATE; 38630b011fSAntonio Nino Diaz } else { 39630b011fSAntonio Nino Diaz return -FDT_ERR_BADMAGIC; 40630b011fSAntonio Nino Diaz } 41630b011fSAntonio Nino Diaz 42*243ce5d5SMadhukar Pappireddy if (totalsize < INT32_MAX) 43*243ce5d5SMadhukar Pappireddy return totalsize; 44*243ce5d5SMadhukar Pappireddy else 45*243ce5d5SMadhukar Pappireddy return -FDT_ERR_TRUNCATED; 46*243ce5d5SMadhukar Pappireddy } 47*243ce5d5SMadhukar Pappireddy 48*243ce5d5SMadhukar Pappireddy static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) 49*243ce5d5SMadhukar Pappireddy { 50*243ce5d5SMadhukar Pappireddy return (off >= hdrsize) && (off <= totalsize); 51*243ce5d5SMadhukar Pappireddy } 52*243ce5d5SMadhukar Pappireddy 53*243ce5d5SMadhukar Pappireddy static int check_block_(uint32_t hdrsize, uint32_t totalsize, 54*243ce5d5SMadhukar Pappireddy uint32_t base, uint32_t size) 55*243ce5d5SMadhukar Pappireddy { 56*243ce5d5SMadhukar Pappireddy if (!check_off_(hdrsize, totalsize, base)) 57*243ce5d5SMadhukar Pappireddy return 0; /* block start out of bounds */ 58*243ce5d5SMadhukar Pappireddy if ((base + size) < base) 59*243ce5d5SMadhukar Pappireddy return 0; /* overflow */ 60*243ce5d5SMadhukar Pappireddy if (!check_off_(hdrsize, totalsize, base + size)) 61*243ce5d5SMadhukar Pappireddy return 0; /* block end out of bounds */ 62*243ce5d5SMadhukar Pappireddy return 1; 63*243ce5d5SMadhukar Pappireddy } 64*243ce5d5SMadhukar Pappireddy 65*243ce5d5SMadhukar Pappireddy size_t fdt_header_size_(uint32_t version) 66*243ce5d5SMadhukar Pappireddy { 67*243ce5d5SMadhukar Pappireddy if (version <= 1) 68*243ce5d5SMadhukar Pappireddy return FDT_V1_SIZE; 69*243ce5d5SMadhukar Pappireddy else if (version <= 2) 70*243ce5d5SMadhukar Pappireddy return FDT_V2_SIZE; 71*243ce5d5SMadhukar Pappireddy else if (version <= 3) 72*243ce5d5SMadhukar Pappireddy return FDT_V3_SIZE; 73*243ce5d5SMadhukar Pappireddy else if (version <= 16) 74*243ce5d5SMadhukar Pappireddy return FDT_V16_SIZE; 75*243ce5d5SMadhukar Pappireddy else 76*243ce5d5SMadhukar Pappireddy return FDT_V17_SIZE; 77*243ce5d5SMadhukar Pappireddy } 78*243ce5d5SMadhukar Pappireddy 79*243ce5d5SMadhukar Pappireddy size_t fdt_header_size(const void *fdt) 80*243ce5d5SMadhukar Pappireddy { 81*243ce5d5SMadhukar Pappireddy return can_assume(LATEST) ? FDT_V17_SIZE : 82*243ce5d5SMadhukar Pappireddy fdt_header_size_(fdt_version(fdt)); 83*243ce5d5SMadhukar Pappireddy } 84*243ce5d5SMadhukar Pappireddy 85*243ce5d5SMadhukar Pappireddy int fdt_check_header(const void *fdt) 86*243ce5d5SMadhukar Pappireddy { 87*243ce5d5SMadhukar Pappireddy size_t hdrsize; 88*243ce5d5SMadhukar Pappireddy 89*243ce5d5SMadhukar Pappireddy if (fdt_magic(fdt) != FDT_MAGIC) 90*243ce5d5SMadhukar Pappireddy return -FDT_ERR_BADMAGIC; 91*243ce5d5SMadhukar Pappireddy if (!can_assume(LATEST)) { 92*243ce5d5SMadhukar Pappireddy if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) 93*243ce5d5SMadhukar Pappireddy || (fdt_last_comp_version(fdt) > 94*243ce5d5SMadhukar Pappireddy FDT_LAST_SUPPORTED_VERSION)) 95*243ce5d5SMadhukar Pappireddy return -FDT_ERR_BADVERSION; 96*243ce5d5SMadhukar Pappireddy if (fdt_version(fdt) < fdt_last_comp_version(fdt)) 97*243ce5d5SMadhukar Pappireddy return -FDT_ERR_BADVERSION; 98*243ce5d5SMadhukar Pappireddy } 99*243ce5d5SMadhukar Pappireddy hdrsize = fdt_header_size(fdt); 100*243ce5d5SMadhukar Pappireddy if (!can_assume(VALID_DTB)) { 101*243ce5d5SMadhukar Pappireddy 102*243ce5d5SMadhukar Pappireddy if ((fdt_totalsize(fdt) < hdrsize) 103*243ce5d5SMadhukar Pappireddy || (fdt_totalsize(fdt) > INT_MAX)) 104*243ce5d5SMadhukar Pappireddy return -FDT_ERR_TRUNCATED; 105*243ce5d5SMadhukar Pappireddy 106*243ce5d5SMadhukar Pappireddy /* Bounds check memrsv block */ 107*243ce5d5SMadhukar Pappireddy if (!check_off_(hdrsize, fdt_totalsize(fdt), 108*243ce5d5SMadhukar Pappireddy fdt_off_mem_rsvmap(fdt))) 109*243ce5d5SMadhukar Pappireddy return -FDT_ERR_TRUNCATED; 110*243ce5d5SMadhukar Pappireddy } 111*243ce5d5SMadhukar Pappireddy 112*243ce5d5SMadhukar Pappireddy if (!can_assume(VALID_DTB)) { 113*243ce5d5SMadhukar Pappireddy /* Bounds check structure block */ 114*243ce5d5SMadhukar Pappireddy if (!can_assume(LATEST) && fdt_version(fdt) < 17) { 115*243ce5d5SMadhukar Pappireddy if (!check_off_(hdrsize, fdt_totalsize(fdt), 116*243ce5d5SMadhukar Pappireddy fdt_off_dt_struct(fdt))) 117*243ce5d5SMadhukar Pappireddy return -FDT_ERR_TRUNCATED; 118*243ce5d5SMadhukar Pappireddy } else { 119*243ce5d5SMadhukar Pappireddy if (!check_block_(hdrsize, fdt_totalsize(fdt), 120*243ce5d5SMadhukar Pappireddy fdt_off_dt_struct(fdt), 121*243ce5d5SMadhukar Pappireddy fdt_size_dt_struct(fdt))) 122*243ce5d5SMadhukar Pappireddy return -FDT_ERR_TRUNCATED; 123*243ce5d5SMadhukar Pappireddy } 124*243ce5d5SMadhukar Pappireddy 125*243ce5d5SMadhukar Pappireddy /* Bounds check strings block */ 126*243ce5d5SMadhukar Pappireddy if (!check_block_(hdrsize, fdt_totalsize(fdt), 127*243ce5d5SMadhukar Pappireddy fdt_off_dt_strings(fdt), 128*243ce5d5SMadhukar Pappireddy fdt_size_dt_strings(fdt))) 129*243ce5d5SMadhukar Pappireddy return -FDT_ERR_TRUNCATED; 130*243ce5d5SMadhukar Pappireddy } 131*243ce5d5SMadhukar Pappireddy 132630b011fSAntonio Nino Diaz return 0; 133630b011fSAntonio Nino Diaz } 134630b011fSAntonio Nino Diaz 135630b011fSAntonio Nino Diaz const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) 136630b011fSAntonio Nino Diaz { 137630b011fSAntonio Nino Diaz unsigned absoffset = offset + fdt_off_dt_struct(fdt); 138630b011fSAntonio Nino Diaz 139*243ce5d5SMadhukar Pappireddy if (!can_assume(VALID_INPUT)) 140630b011fSAntonio Nino Diaz if ((absoffset < offset) 141630b011fSAntonio Nino Diaz || ((absoffset + len) < absoffset) 142630b011fSAntonio Nino Diaz || (absoffset + len) > fdt_totalsize(fdt)) 143630b011fSAntonio Nino Diaz return NULL; 144630b011fSAntonio Nino Diaz 145*243ce5d5SMadhukar Pappireddy if (can_assume(LATEST) || fdt_version(fdt) >= 0x11) 146630b011fSAntonio Nino Diaz if (((offset + len) < offset) 147630b011fSAntonio Nino Diaz || ((offset + len) > fdt_size_dt_struct(fdt))) 148630b011fSAntonio Nino Diaz return NULL; 149630b011fSAntonio Nino Diaz 150630b011fSAntonio Nino Diaz return fdt_offset_ptr_(fdt, offset); 151630b011fSAntonio Nino Diaz } 152630b011fSAntonio Nino Diaz 153630b011fSAntonio Nino Diaz uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) 154630b011fSAntonio Nino Diaz { 155630b011fSAntonio Nino Diaz const fdt32_t *tagp, *lenp; 156630b011fSAntonio Nino Diaz uint32_t tag; 157630b011fSAntonio Nino Diaz int offset = startoffset; 158630b011fSAntonio Nino Diaz const char *p; 159630b011fSAntonio Nino Diaz 160630b011fSAntonio Nino Diaz *nextoffset = -FDT_ERR_TRUNCATED; 161630b011fSAntonio Nino Diaz tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); 162*243ce5d5SMadhukar Pappireddy if (!can_assume(VALID_DTB) && !tagp) 163630b011fSAntonio Nino Diaz return FDT_END; /* premature end */ 164630b011fSAntonio Nino Diaz tag = fdt32_to_cpu(*tagp); 165630b011fSAntonio Nino Diaz offset += FDT_TAGSIZE; 166630b011fSAntonio Nino Diaz 167630b011fSAntonio Nino Diaz *nextoffset = -FDT_ERR_BADSTRUCTURE; 168630b011fSAntonio Nino Diaz switch (tag) { 169630b011fSAntonio Nino Diaz case FDT_BEGIN_NODE: 170630b011fSAntonio Nino Diaz /* skip name */ 171630b011fSAntonio Nino Diaz do { 172630b011fSAntonio Nino Diaz p = fdt_offset_ptr(fdt, offset++, 1); 173630b011fSAntonio Nino Diaz } while (p && (*p != '\0')); 174*243ce5d5SMadhukar Pappireddy if (!can_assume(VALID_DTB) && !p) 175630b011fSAntonio Nino Diaz return FDT_END; /* premature end */ 176630b011fSAntonio Nino Diaz break; 177630b011fSAntonio Nino Diaz 178630b011fSAntonio Nino Diaz case FDT_PROP: 179630b011fSAntonio Nino Diaz lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); 180*243ce5d5SMadhukar Pappireddy if (!can_assume(VALID_DTB) && !lenp) 181630b011fSAntonio Nino Diaz return FDT_END; /* premature end */ 182630b011fSAntonio Nino Diaz /* skip-name offset, length and value */ 183630b011fSAntonio Nino Diaz offset += sizeof(struct fdt_property) - FDT_TAGSIZE 184630b011fSAntonio Nino Diaz + fdt32_to_cpu(*lenp); 185*243ce5d5SMadhukar Pappireddy if (!can_assume(LATEST) && 186*243ce5d5SMadhukar Pappireddy fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 && 187630b011fSAntonio Nino Diaz ((offset - fdt32_to_cpu(*lenp)) % 8) != 0) 188630b011fSAntonio Nino Diaz offset += 4; 189630b011fSAntonio Nino Diaz break; 190630b011fSAntonio Nino Diaz 191630b011fSAntonio Nino Diaz case FDT_END: 192630b011fSAntonio Nino Diaz case FDT_END_NODE: 193630b011fSAntonio Nino Diaz case FDT_NOP: 194630b011fSAntonio Nino Diaz break; 195630b011fSAntonio Nino Diaz 196630b011fSAntonio Nino Diaz default: 197630b011fSAntonio Nino Diaz return FDT_END; 198630b011fSAntonio Nino Diaz } 199630b011fSAntonio Nino Diaz 200630b011fSAntonio Nino Diaz if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) 201630b011fSAntonio Nino Diaz return FDT_END; /* premature end */ 202630b011fSAntonio Nino Diaz 203630b011fSAntonio Nino Diaz *nextoffset = FDT_TAGALIGN(offset); 204630b011fSAntonio Nino Diaz return tag; 205630b011fSAntonio Nino Diaz } 206630b011fSAntonio Nino Diaz 207630b011fSAntonio Nino Diaz int fdt_check_node_offset_(const void *fdt, int offset) 208630b011fSAntonio Nino Diaz { 209*243ce5d5SMadhukar Pappireddy if (can_assume(VALID_INPUT)) 210*243ce5d5SMadhukar Pappireddy return offset; 211630b011fSAntonio Nino Diaz if ((offset < 0) || (offset % FDT_TAGSIZE) 212630b011fSAntonio Nino Diaz || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) 213630b011fSAntonio Nino Diaz return -FDT_ERR_BADOFFSET; 214630b011fSAntonio Nino Diaz 215630b011fSAntonio Nino Diaz return offset; 216630b011fSAntonio Nino Diaz } 217630b011fSAntonio Nino Diaz 218630b011fSAntonio Nino Diaz int fdt_check_prop_offset_(const void *fdt, int offset) 219630b011fSAntonio Nino Diaz { 220630b011fSAntonio Nino Diaz if ((offset < 0) || (offset % FDT_TAGSIZE) 221630b011fSAntonio Nino Diaz || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)) 222630b011fSAntonio Nino Diaz return -FDT_ERR_BADOFFSET; 223630b011fSAntonio Nino Diaz 224630b011fSAntonio Nino Diaz return offset; 225630b011fSAntonio Nino Diaz } 226630b011fSAntonio Nino Diaz 227630b011fSAntonio Nino Diaz int fdt_next_node(const void *fdt, int offset, int *depth) 228630b011fSAntonio Nino Diaz { 229630b011fSAntonio Nino Diaz int nextoffset = 0; 230630b011fSAntonio Nino Diaz uint32_t tag; 231630b011fSAntonio Nino Diaz 232630b011fSAntonio Nino Diaz if (offset >= 0) 233630b011fSAntonio Nino Diaz if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0) 234630b011fSAntonio Nino Diaz return nextoffset; 235630b011fSAntonio Nino Diaz 236630b011fSAntonio Nino Diaz do { 237630b011fSAntonio Nino Diaz offset = nextoffset; 238630b011fSAntonio Nino Diaz tag = fdt_next_tag(fdt, offset, &nextoffset); 239630b011fSAntonio Nino Diaz 240630b011fSAntonio Nino Diaz switch (tag) { 241630b011fSAntonio Nino Diaz case FDT_PROP: 242630b011fSAntonio Nino Diaz case FDT_NOP: 243630b011fSAntonio Nino Diaz break; 244630b011fSAntonio Nino Diaz 245630b011fSAntonio Nino Diaz case FDT_BEGIN_NODE: 246630b011fSAntonio Nino Diaz if (depth) 247630b011fSAntonio Nino Diaz (*depth)++; 248630b011fSAntonio Nino Diaz break; 249630b011fSAntonio Nino Diaz 250630b011fSAntonio Nino Diaz case FDT_END_NODE: 251630b011fSAntonio Nino Diaz if (depth && ((--(*depth)) < 0)) 252630b011fSAntonio Nino Diaz return nextoffset; 253630b011fSAntonio Nino Diaz break; 254630b011fSAntonio Nino Diaz 255630b011fSAntonio Nino Diaz case FDT_END: 256630b011fSAntonio Nino Diaz if ((nextoffset >= 0) 257630b011fSAntonio Nino Diaz || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) 258630b011fSAntonio Nino Diaz return -FDT_ERR_NOTFOUND; 259630b011fSAntonio Nino Diaz else 260630b011fSAntonio Nino Diaz return nextoffset; 261630b011fSAntonio Nino Diaz } 262630b011fSAntonio Nino Diaz } while (tag != FDT_BEGIN_NODE); 263630b011fSAntonio Nino Diaz 264630b011fSAntonio Nino Diaz return offset; 265630b011fSAntonio Nino Diaz } 266630b011fSAntonio Nino Diaz 267630b011fSAntonio Nino Diaz int fdt_first_subnode(const void *fdt, int offset) 268630b011fSAntonio Nino Diaz { 269630b011fSAntonio Nino Diaz int depth = 0; 270630b011fSAntonio Nino Diaz 271630b011fSAntonio Nino Diaz offset = fdt_next_node(fdt, offset, &depth); 272630b011fSAntonio Nino Diaz if (offset < 0 || depth != 1) 273630b011fSAntonio Nino Diaz return -FDT_ERR_NOTFOUND; 274630b011fSAntonio Nino Diaz 275630b011fSAntonio Nino Diaz return offset; 276630b011fSAntonio Nino Diaz } 277630b011fSAntonio Nino Diaz 278630b011fSAntonio Nino Diaz int fdt_next_subnode(const void *fdt, int offset) 279630b011fSAntonio Nino Diaz { 280630b011fSAntonio Nino Diaz int depth = 1; 281630b011fSAntonio Nino Diaz 282630b011fSAntonio Nino Diaz /* 283630b011fSAntonio Nino Diaz * With respect to the parent, the depth of the next subnode will be 284630b011fSAntonio Nino Diaz * the same as the last. 285630b011fSAntonio Nino Diaz */ 286630b011fSAntonio Nino Diaz do { 287630b011fSAntonio Nino Diaz offset = fdt_next_node(fdt, offset, &depth); 288630b011fSAntonio Nino Diaz if (offset < 0 || depth < 1) 289630b011fSAntonio Nino Diaz return -FDT_ERR_NOTFOUND; 290630b011fSAntonio Nino Diaz } while (depth > 1); 291630b011fSAntonio Nino Diaz 292630b011fSAntonio Nino Diaz return offset; 293630b011fSAntonio Nino Diaz } 294630b011fSAntonio Nino Diaz 295630b011fSAntonio Nino Diaz const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) 296630b011fSAntonio Nino Diaz { 297630b011fSAntonio Nino Diaz int len = strlen(s) + 1; 298630b011fSAntonio Nino Diaz const char *last = strtab + tabsize - len; 299630b011fSAntonio Nino Diaz const char *p; 300630b011fSAntonio Nino Diaz 301630b011fSAntonio Nino Diaz for (p = strtab; p <= last; p++) 302630b011fSAntonio Nino Diaz if (memcmp(p, s, len) == 0) 303630b011fSAntonio Nino Diaz return p; 304630b011fSAntonio Nino Diaz return NULL; 305630b011fSAntonio Nino Diaz } 306630b011fSAntonio Nino Diaz 307630b011fSAntonio Nino Diaz int fdt_move(const void *fdt, void *buf, int bufsize) 308630b011fSAntonio Nino Diaz { 309*243ce5d5SMadhukar Pappireddy FDT_RO_PROBE(fdt); 310630b011fSAntonio Nino Diaz 311630b011fSAntonio Nino Diaz if (fdt_totalsize(fdt) > bufsize) 312630b011fSAntonio Nino Diaz return -FDT_ERR_NOSPACE; 313630b011fSAntonio Nino Diaz 314630b011fSAntonio Nino Diaz memmove(buf, fdt, fdt_totalsize(fdt)); 315630b011fSAntonio Nino Diaz return 0; 316630b011fSAntonio Nino Diaz } 317