1243ce5d5SMadhukar 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 13243ce5d5SMadhukar Pappireddy /* 14243ce5d5SMadhukar Pappireddy * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks 15243ce5d5SMadhukar Pappireddy * that the given buffer contains what appears to be a flattened 16243ce5d5SMadhukar Pappireddy * device tree with sane information in its header. 17243ce5d5SMadhukar Pappireddy */ 18243ce5d5SMadhukar Pappireddy int32_t fdt_ro_probe_(const void *fdt) 19630b011fSAntonio Nino Diaz { 20243ce5d5SMadhukar Pappireddy uint32_t totalsize = fdt_totalsize(fdt); 21243ce5d5SMadhukar Pappireddy 22243ce5d5SMadhukar Pappireddy if (can_assume(VALID_DTB)) 23243ce5d5SMadhukar Pappireddy return totalsize; 24243ce5d5SMadhukar Pappireddy 25630b011fSAntonio Nino Diaz if (fdt_magic(fdt) == FDT_MAGIC) { 26630b011fSAntonio Nino Diaz /* Complete tree */ 27243ce5d5SMadhukar Pappireddy if (!can_assume(LATEST)) { 28630b011fSAntonio Nino Diaz if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) 29630b011fSAntonio Nino Diaz return -FDT_ERR_BADVERSION; 30243ce5d5SMadhukar Pappireddy if (fdt_last_comp_version(fdt) > 31243ce5d5SMadhukar Pappireddy FDT_LAST_SUPPORTED_VERSION) 32630b011fSAntonio Nino Diaz return -FDT_ERR_BADVERSION; 33243ce5d5SMadhukar Pappireddy } 34630b011fSAntonio Nino Diaz } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { 35630b011fSAntonio Nino Diaz /* Unfinished sequential-write blob */ 36243ce5d5SMadhukar 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 42243ce5d5SMadhukar Pappireddy if (totalsize < INT32_MAX) 43243ce5d5SMadhukar Pappireddy return totalsize; 44243ce5d5SMadhukar Pappireddy else 45243ce5d5SMadhukar Pappireddy return -FDT_ERR_TRUNCATED; 46243ce5d5SMadhukar Pappireddy } 47243ce5d5SMadhukar Pappireddy 48243ce5d5SMadhukar Pappireddy static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) 49243ce5d5SMadhukar Pappireddy { 50243ce5d5SMadhukar Pappireddy return (off >= hdrsize) && (off <= totalsize); 51243ce5d5SMadhukar Pappireddy } 52243ce5d5SMadhukar Pappireddy 53243ce5d5SMadhukar Pappireddy static int check_block_(uint32_t hdrsize, uint32_t totalsize, 54243ce5d5SMadhukar Pappireddy uint32_t base, uint32_t size) 55243ce5d5SMadhukar Pappireddy { 56243ce5d5SMadhukar Pappireddy if (!check_off_(hdrsize, totalsize, base)) 57243ce5d5SMadhukar Pappireddy return 0; /* block start out of bounds */ 58243ce5d5SMadhukar Pappireddy if ((base + size) < base) 59243ce5d5SMadhukar Pappireddy return 0; /* overflow */ 60243ce5d5SMadhukar Pappireddy if (!check_off_(hdrsize, totalsize, base + size)) 61243ce5d5SMadhukar Pappireddy return 0; /* block end out of bounds */ 62243ce5d5SMadhukar Pappireddy return 1; 63243ce5d5SMadhukar Pappireddy } 64243ce5d5SMadhukar Pappireddy 65243ce5d5SMadhukar Pappireddy size_t fdt_header_size_(uint32_t version) 66243ce5d5SMadhukar Pappireddy { 67243ce5d5SMadhukar Pappireddy if (version <= 1) 68243ce5d5SMadhukar Pappireddy return FDT_V1_SIZE; 69243ce5d5SMadhukar Pappireddy else if (version <= 2) 70243ce5d5SMadhukar Pappireddy return FDT_V2_SIZE; 71243ce5d5SMadhukar Pappireddy else if (version <= 3) 72243ce5d5SMadhukar Pappireddy return FDT_V3_SIZE; 73243ce5d5SMadhukar Pappireddy else if (version <= 16) 74243ce5d5SMadhukar Pappireddy return FDT_V16_SIZE; 75243ce5d5SMadhukar Pappireddy else 76243ce5d5SMadhukar Pappireddy return FDT_V17_SIZE; 77243ce5d5SMadhukar Pappireddy } 78243ce5d5SMadhukar Pappireddy 79243ce5d5SMadhukar Pappireddy size_t fdt_header_size(const void *fdt) 80243ce5d5SMadhukar Pappireddy { 81243ce5d5SMadhukar Pappireddy return can_assume(LATEST) ? FDT_V17_SIZE : 82243ce5d5SMadhukar Pappireddy fdt_header_size_(fdt_version(fdt)); 83243ce5d5SMadhukar Pappireddy } 84243ce5d5SMadhukar Pappireddy 85243ce5d5SMadhukar Pappireddy int fdt_check_header(const void *fdt) 86243ce5d5SMadhukar Pappireddy { 87243ce5d5SMadhukar Pappireddy size_t hdrsize; 88243ce5d5SMadhukar Pappireddy 89243ce5d5SMadhukar Pappireddy if (fdt_magic(fdt) != FDT_MAGIC) 90243ce5d5SMadhukar Pappireddy return -FDT_ERR_BADMAGIC; 91243ce5d5SMadhukar Pappireddy if (!can_assume(LATEST)) { 92243ce5d5SMadhukar Pappireddy if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) 93243ce5d5SMadhukar Pappireddy || (fdt_last_comp_version(fdt) > 94243ce5d5SMadhukar Pappireddy FDT_LAST_SUPPORTED_VERSION)) 95243ce5d5SMadhukar Pappireddy return -FDT_ERR_BADVERSION; 96243ce5d5SMadhukar Pappireddy if (fdt_version(fdt) < fdt_last_comp_version(fdt)) 97243ce5d5SMadhukar Pappireddy return -FDT_ERR_BADVERSION; 98243ce5d5SMadhukar Pappireddy } 99243ce5d5SMadhukar Pappireddy hdrsize = fdt_header_size(fdt); 100243ce5d5SMadhukar Pappireddy if (!can_assume(VALID_DTB)) { 101243ce5d5SMadhukar Pappireddy 102243ce5d5SMadhukar Pappireddy if ((fdt_totalsize(fdt) < hdrsize) 103243ce5d5SMadhukar Pappireddy || (fdt_totalsize(fdt) > INT_MAX)) 104243ce5d5SMadhukar Pappireddy return -FDT_ERR_TRUNCATED; 105243ce5d5SMadhukar Pappireddy 106243ce5d5SMadhukar Pappireddy /* Bounds check memrsv block */ 107243ce5d5SMadhukar Pappireddy if (!check_off_(hdrsize, fdt_totalsize(fdt), 108243ce5d5SMadhukar Pappireddy fdt_off_mem_rsvmap(fdt))) 109243ce5d5SMadhukar Pappireddy return -FDT_ERR_TRUNCATED; 110243ce5d5SMadhukar Pappireddy } 111243ce5d5SMadhukar Pappireddy 112243ce5d5SMadhukar Pappireddy if (!can_assume(VALID_DTB)) { 113243ce5d5SMadhukar Pappireddy /* Bounds check structure block */ 114243ce5d5SMadhukar Pappireddy if (!can_assume(LATEST) && fdt_version(fdt) < 17) { 115243ce5d5SMadhukar Pappireddy if (!check_off_(hdrsize, fdt_totalsize(fdt), 116243ce5d5SMadhukar Pappireddy fdt_off_dt_struct(fdt))) 117243ce5d5SMadhukar Pappireddy return -FDT_ERR_TRUNCATED; 118243ce5d5SMadhukar Pappireddy } else { 119243ce5d5SMadhukar Pappireddy if (!check_block_(hdrsize, fdt_totalsize(fdt), 120243ce5d5SMadhukar Pappireddy fdt_off_dt_struct(fdt), 121243ce5d5SMadhukar Pappireddy fdt_size_dt_struct(fdt))) 122243ce5d5SMadhukar Pappireddy return -FDT_ERR_TRUNCATED; 123243ce5d5SMadhukar Pappireddy } 124243ce5d5SMadhukar Pappireddy 125243ce5d5SMadhukar Pappireddy /* Bounds check strings block */ 126243ce5d5SMadhukar Pappireddy if (!check_block_(hdrsize, fdt_totalsize(fdt), 127243ce5d5SMadhukar Pappireddy fdt_off_dt_strings(fdt), 128243ce5d5SMadhukar Pappireddy fdt_size_dt_strings(fdt))) 129243ce5d5SMadhukar Pappireddy return -FDT_ERR_TRUNCATED; 130243ce5d5SMadhukar Pappireddy } 131243ce5d5SMadhukar 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 { 137*3b456661SAndre Przywara unsigned int uoffset = offset; 138*3b456661SAndre Przywara unsigned int absoffset = offset + fdt_off_dt_struct(fdt); 139*3b456661SAndre Przywara 140*3b456661SAndre Przywara if (offset < 0) 141*3b456661SAndre Przywara return NULL; 142630b011fSAntonio Nino Diaz 143243ce5d5SMadhukar Pappireddy if (!can_assume(VALID_INPUT)) 144*3b456661SAndre Przywara if ((absoffset < uoffset) 145630b011fSAntonio Nino Diaz || ((absoffset + len) < absoffset) 146630b011fSAntonio Nino Diaz || (absoffset + len) > fdt_totalsize(fdt)) 147630b011fSAntonio Nino Diaz return NULL; 148630b011fSAntonio Nino Diaz 149243ce5d5SMadhukar Pappireddy if (can_assume(LATEST) || fdt_version(fdt) >= 0x11) 150*3b456661SAndre Przywara if (((uoffset + len) < uoffset) 151630b011fSAntonio Nino Diaz || ((offset + len) > fdt_size_dt_struct(fdt))) 152630b011fSAntonio Nino Diaz return NULL; 153630b011fSAntonio Nino Diaz 154630b011fSAntonio Nino Diaz return fdt_offset_ptr_(fdt, offset); 155630b011fSAntonio Nino Diaz } 156630b011fSAntonio Nino Diaz 157630b011fSAntonio Nino Diaz uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) 158630b011fSAntonio Nino Diaz { 159630b011fSAntonio Nino Diaz const fdt32_t *tagp, *lenp; 160630b011fSAntonio Nino Diaz uint32_t tag; 161630b011fSAntonio Nino Diaz int offset = startoffset; 162630b011fSAntonio Nino Diaz const char *p; 163630b011fSAntonio Nino Diaz 164630b011fSAntonio Nino Diaz *nextoffset = -FDT_ERR_TRUNCATED; 165630b011fSAntonio Nino Diaz tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); 166243ce5d5SMadhukar Pappireddy if (!can_assume(VALID_DTB) && !tagp) 167630b011fSAntonio Nino Diaz return FDT_END; /* premature end */ 168630b011fSAntonio Nino Diaz tag = fdt32_to_cpu(*tagp); 169630b011fSAntonio Nino Diaz offset += FDT_TAGSIZE; 170630b011fSAntonio Nino Diaz 171630b011fSAntonio Nino Diaz *nextoffset = -FDT_ERR_BADSTRUCTURE; 172630b011fSAntonio Nino Diaz switch (tag) { 173630b011fSAntonio Nino Diaz case FDT_BEGIN_NODE: 174630b011fSAntonio Nino Diaz /* skip name */ 175630b011fSAntonio Nino Diaz do { 176630b011fSAntonio Nino Diaz p = fdt_offset_ptr(fdt, offset++, 1); 177630b011fSAntonio Nino Diaz } while (p && (*p != '\0')); 178243ce5d5SMadhukar Pappireddy if (!can_assume(VALID_DTB) && !p) 179630b011fSAntonio Nino Diaz return FDT_END; /* premature end */ 180630b011fSAntonio Nino Diaz break; 181630b011fSAntonio Nino Diaz 182630b011fSAntonio Nino Diaz case FDT_PROP: 183630b011fSAntonio Nino Diaz lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); 184243ce5d5SMadhukar Pappireddy if (!can_assume(VALID_DTB) && !lenp) 185630b011fSAntonio Nino Diaz return FDT_END; /* premature end */ 186630b011fSAntonio Nino Diaz /* skip-name offset, length and value */ 187630b011fSAntonio Nino Diaz offset += sizeof(struct fdt_property) - FDT_TAGSIZE 188630b011fSAntonio Nino Diaz + fdt32_to_cpu(*lenp); 189243ce5d5SMadhukar Pappireddy if (!can_assume(LATEST) && 190243ce5d5SMadhukar Pappireddy fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 && 191630b011fSAntonio Nino Diaz ((offset - fdt32_to_cpu(*lenp)) % 8) != 0) 192630b011fSAntonio Nino Diaz offset += 4; 193630b011fSAntonio Nino Diaz break; 194630b011fSAntonio Nino Diaz 195630b011fSAntonio Nino Diaz case FDT_END: 196630b011fSAntonio Nino Diaz case FDT_END_NODE: 197630b011fSAntonio Nino Diaz case FDT_NOP: 198630b011fSAntonio Nino Diaz break; 199630b011fSAntonio Nino Diaz 200630b011fSAntonio Nino Diaz default: 201630b011fSAntonio Nino Diaz return FDT_END; 202630b011fSAntonio Nino Diaz } 203630b011fSAntonio Nino Diaz 204630b011fSAntonio Nino Diaz if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) 205630b011fSAntonio Nino Diaz return FDT_END; /* premature end */ 206630b011fSAntonio Nino Diaz 207630b011fSAntonio Nino Diaz *nextoffset = FDT_TAGALIGN(offset); 208630b011fSAntonio Nino Diaz return tag; 209630b011fSAntonio Nino Diaz } 210630b011fSAntonio Nino Diaz 211630b011fSAntonio Nino Diaz int fdt_check_node_offset_(const void *fdt, int offset) 212630b011fSAntonio Nino Diaz { 213*3b456661SAndre Przywara if (!can_assume(VALID_INPUT) 214*3b456661SAndre Przywara && ((offset < 0) || (offset % FDT_TAGSIZE))) 215*3b456661SAndre Przywara return -FDT_ERR_BADOFFSET; 216*3b456661SAndre Przywara 217*3b456661SAndre Przywara if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE) 218630b011fSAntonio Nino Diaz return -FDT_ERR_BADOFFSET; 219630b011fSAntonio Nino Diaz 220630b011fSAntonio Nino Diaz return offset; 221630b011fSAntonio Nino Diaz } 222630b011fSAntonio Nino Diaz 223630b011fSAntonio Nino Diaz int fdt_check_prop_offset_(const void *fdt, int offset) 224630b011fSAntonio Nino Diaz { 225*3b456661SAndre Przywara if (!can_assume(VALID_INPUT) 226*3b456661SAndre Przywara && ((offset < 0) || (offset % FDT_TAGSIZE))) 227*3b456661SAndre Przywara return -FDT_ERR_BADOFFSET; 228*3b456661SAndre Przywara 229*3b456661SAndre Przywara if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP) 230630b011fSAntonio Nino Diaz return -FDT_ERR_BADOFFSET; 231630b011fSAntonio Nino Diaz 232630b011fSAntonio Nino Diaz return offset; 233630b011fSAntonio Nino Diaz } 234630b011fSAntonio Nino Diaz 235630b011fSAntonio Nino Diaz int fdt_next_node(const void *fdt, int offset, int *depth) 236630b011fSAntonio Nino Diaz { 237630b011fSAntonio Nino Diaz int nextoffset = 0; 238630b011fSAntonio Nino Diaz uint32_t tag; 239630b011fSAntonio Nino Diaz 240630b011fSAntonio Nino Diaz if (offset >= 0) 241630b011fSAntonio Nino Diaz if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0) 242630b011fSAntonio Nino Diaz return nextoffset; 243630b011fSAntonio Nino Diaz 244630b011fSAntonio Nino Diaz do { 245630b011fSAntonio Nino Diaz offset = nextoffset; 246630b011fSAntonio Nino Diaz tag = fdt_next_tag(fdt, offset, &nextoffset); 247630b011fSAntonio Nino Diaz 248630b011fSAntonio Nino Diaz switch (tag) { 249630b011fSAntonio Nino Diaz case FDT_PROP: 250630b011fSAntonio Nino Diaz case FDT_NOP: 251630b011fSAntonio Nino Diaz break; 252630b011fSAntonio Nino Diaz 253630b011fSAntonio Nino Diaz case FDT_BEGIN_NODE: 254630b011fSAntonio Nino Diaz if (depth) 255630b011fSAntonio Nino Diaz (*depth)++; 256630b011fSAntonio Nino Diaz break; 257630b011fSAntonio Nino Diaz 258630b011fSAntonio Nino Diaz case FDT_END_NODE: 259630b011fSAntonio Nino Diaz if (depth && ((--(*depth)) < 0)) 260630b011fSAntonio Nino Diaz return nextoffset; 261630b011fSAntonio Nino Diaz break; 262630b011fSAntonio Nino Diaz 263630b011fSAntonio Nino Diaz case FDT_END: 264630b011fSAntonio Nino Diaz if ((nextoffset >= 0) 265630b011fSAntonio Nino Diaz || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) 266630b011fSAntonio Nino Diaz return -FDT_ERR_NOTFOUND; 267630b011fSAntonio Nino Diaz else 268630b011fSAntonio Nino Diaz return nextoffset; 269630b011fSAntonio Nino Diaz } 270630b011fSAntonio Nino Diaz } while (tag != FDT_BEGIN_NODE); 271630b011fSAntonio Nino Diaz 272630b011fSAntonio Nino Diaz return offset; 273630b011fSAntonio Nino Diaz } 274630b011fSAntonio Nino Diaz 275630b011fSAntonio Nino Diaz int fdt_first_subnode(const void *fdt, int offset) 276630b011fSAntonio Nino Diaz { 277630b011fSAntonio Nino Diaz int depth = 0; 278630b011fSAntonio Nino Diaz 279630b011fSAntonio Nino Diaz offset = fdt_next_node(fdt, offset, &depth); 280630b011fSAntonio Nino Diaz if (offset < 0 || depth != 1) 281630b011fSAntonio Nino Diaz return -FDT_ERR_NOTFOUND; 282630b011fSAntonio Nino Diaz 283630b011fSAntonio Nino Diaz return offset; 284630b011fSAntonio Nino Diaz } 285630b011fSAntonio Nino Diaz 286630b011fSAntonio Nino Diaz int fdt_next_subnode(const void *fdt, int offset) 287630b011fSAntonio Nino Diaz { 288630b011fSAntonio Nino Diaz int depth = 1; 289630b011fSAntonio Nino Diaz 290630b011fSAntonio Nino Diaz /* 291630b011fSAntonio Nino Diaz * With respect to the parent, the depth of the next subnode will be 292630b011fSAntonio Nino Diaz * the same as the last. 293630b011fSAntonio Nino Diaz */ 294630b011fSAntonio Nino Diaz do { 295630b011fSAntonio Nino Diaz offset = fdt_next_node(fdt, offset, &depth); 296630b011fSAntonio Nino Diaz if (offset < 0 || depth < 1) 297630b011fSAntonio Nino Diaz return -FDT_ERR_NOTFOUND; 298630b011fSAntonio Nino Diaz } while (depth > 1); 299630b011fSAntonio Nino Diaz 300630b011fSAntonio Nino Diaz return offset; 301630b011fSAntonio Nino Diaz } 302630b011fSAntonio Nino Diaz 303630b011fSAntonio Nino Diaz const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) 304630b011fSAntonio Nino Diaz { 305630b011fSAntonio Nino Diaz int len = strlen(s) + 1; 306630b011fSAntonio Nino Diaz const char *last = strtab + tabsize - len; 307630b011fSAntonio Nino Diaz const char *p; 308630b011fSAntonio Nino Diaz 309630b011fSAntonio Nino Diaz for (p = strtab; p <= last; p++) 310630b011fSAntonio Nino Diaz if (memcmp(p, s, len) == 0) 311630b011fSAntonio Nino Diaz return p; 312630b011fSAntonio Nino Diaz return NULL; 313630b011fSAntonio Nino Diaz } 314630b011fSAntonio Nino Diaz 315630b011fSAntonio Nino Diaz int fdt_move(const void *fdt, void *buf, int bufsize) 316630b011fSAntonio Nino Diaz { 317*3b456661SAndre Przywara if (!can_assume(VALID_INPUT) && bufsize < 0) 318*3b456661SAndre Przywara return -FDT_ERR_NOSPACE; 319*3b456661SAndre Przywara 320243ce5d5SMadhukar Pappireddy FDT_RO_PROBE(fdt); 321630b011fSAntonio Nino Diaz 322*3b456661SAndre Przywara if (fdt_totalsize(fdt) > (unsigned int)bufsize) 323630b011fSAntonio Nino Diaz return -FDT_ERR_NOSPACE; 324630b011fSAntonio Nino Diaz 325630b011fSAntonio Nino Diaz memmove(buf, fdt, fdt_totalsize(fdt)); 326630b011fSAntonio Nino Diaz return 0; 327630b011fSAntonio Nino Diaz } 328