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 13630b011fSAntonio Nino Diaz static int fdt_blocks_misordered_(const void *fdt, 14630b011fSAntonio Nino Diaz int mem_rsv_size, int struct_size) 15630b011fSAntonio Nino Diaz { 16630b011fSAntonio Nino Diaz return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8)) 17630b011fSAntonio Nino Diaz || (fdt_off_dt_struct(fdt) < 18630b011fSAntonio Nino Diaz (fdt_off_mem_rsvmap(fdt) + mem_rsv_size)) 19630b011fSAntonio Nino Diaz || (fdt_off_dt_strings(fdt) < 20630b011fSAntonio Nino Diaz (fdt_off_dt_struct(fdt) + struct_size)) 21630b011fSAntonio Nino Diaz || (fdt_totalsize(fdt) < 22630b011fSAntonio Nino Diaz (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); 23630b011fSAntonio Nino Diaz } 24630b011fSAntonio Nino Diaz 25*243ce5d5SMadhukar Pappireddy static int fdt_rw_probe_(void *fdt) 26630b011fSAntonio Nino Diaz { 27*243ce5d5SMadhukar Pappireddy if (can_assume(VALID_DTB)) 28*243ce5d5SMadhukar Pappireddy return 0; 29*243ce5d5SMadhukar Pappireddy FDT_RO_PROBE(fdt); 30630b011fSAntonio Nino Diaz 31*243ce5d5SMadhukar Pappireddy if (!can_assume(LATEST) && fdt_version(fdt) < 17) 32630b011fSAntonio Nino Diaz return -FDT_ERR_BADVERSION; 33630b011fSAntonio Nino Diaz if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry), 34630b011fSAntonio Nino Diaz fdt_size_dt_struct(fdt))) 35630b011fSAntonio Nino Diaz return -FDT_ERR_BADLAYOUT; 36*243ce5d5SMadhukar Pappireddy if (!can_assume(LATEST) && fdt_version(fdt) > 17) 37630b011fSAntonio Nino Diaz fdt_set_version(fdt, 17); 38630b011fSAntonio Nino Diaz 39630b011fSAntonio Nino Diaz return 0; 40630b011fSAntonio Nino Diaz } 41630b011fSAntonio Nino Diaz 42*243ce5d5SMadhukar Pappireddy #define FDT_RW_PROBE(fdt) \ 43630b011fSAntonio Nino Diaz { \ 44630b011fSAntonio Nino Diaz int err_; \ 45*243ce5d5SMadhukar Pappireddy if ((err_ = fdt_rw_probe_(fdt)) != 0) \ 46630b011fSAntonio Nino Diaz return err_; \ 47630b011fSAntonio Nino Diaz } 48630b011fSAntonio Nino Diaz 49*243ce5d5SMadhukar Pappireddy static inline unsigned int fdt_data_size_(void *fdt) 50630b011fSAntonio Nino Diaz { 51630b011fSAntonio Nino Diaz return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); 52630b011fSAntonio Nino Diaz } 53630b011fSAntonio Nino Diaz 54630b011fSAntonio Nino Diaz static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen) 55630b011fSAntonio Nino Diaz { 56630b011fSAntonio Nino Diaz char *p = splicepoint; 57*243ce5d5SMadhukar Pappireddy unsigned int dsize = fdt_data_size_(fdt); 58*243ce5d5SMadhukar Pappireddy size_t soff = p - (char *)fdt; 59630b011fSAntonio Nino Diaz 60*243ce5d5SMadhukar Pappireddy if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize)) 61630b011fSAntonio Nino Diaz return -FDT_ERR_BADOFFSET; 62*243ce5d5SMadhukar Pappireddy if ((p < (char *)fdt) || (dsize + newlen < oldlen)) 63630b011fSAntonio Nino Diaz return -FDT_ERR_BADOFFSET; 64*243ce5d5SMadhukar Pappireddy if (dsize - oldlen + newlen > fdt_totalsize(fdt)) 65630b011fSAntonio Nino Diaz return -FDT_ERR_NOSPACE; 66*243ce5d5SMadhukar Pappireddy memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (p + oldlen)); 67630b011fSAntonio Nino Diaz return 0; 68630b011fSAntonio Nino Diaz } 69630b011fSAntonio Nino Diaz 70630b011fSAntonio Nino Diaz static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p, 71630b011fSAntonio Nino Diaz int oldn, int newn) 72630b011fSAntonio Nino Diaz { 73630b011fSAntonio Nino Diaz int delta = (newn - oldn) * sizeof(*p); 74630b011fSAntonio Nino Diaz int err; 75630b011fSAntonio Nino Diaz err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p)); 76630b011fSAntonio Nino Diaz if (err) 77630b011fSAntonio Nino Diaz return err; 78630b011fSAntonio Nino Diaz fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta); 79630b011fSAntonio Nino Diaz fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); 80630b011fSAntonio Nino Diaz return 0; 81630b011fSAntonio Nino Diaz } 82630b011fSAntonio Nino Diaz 83630b011fSAntonio Nino Diaz static int fdt_splice_struct_(void *fdt, void *p, 84630b011fSAntonio Nino Diaz int oldlen, int newlen) 85630b011fSAntonio Nino Diaz { 86630b011fSAntonio Nino Diaz int delta = newlen - oldlen; 87630b011fSAntonio Nino Diaz int err; 88630b011fSAntonio Nino Diaz 89630b011fSAntonio Nino Diaz if ((err = fdt_splice_(fdt, p, oldlen, newlen))) 90630b011fSAntonio Nino Diaz return err; 91630b011fSAntonio Nino Diaz 92630b011fSAntonio Nino Diaz fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta); 93630b011fSAntonio Nino Diaz fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); 94630b011fSAntonio Nino Diaz return 0; 95630b011fSAntonio Nino Diaz } 96630b011fSAntonio Nino Diaz 97*243ce5d5SMadhukar Pappireddy /* Must only be used to roll back in case of error */ 98*243ce5d5SMadhukar Pappireddy static void fdt_del_last_string_(void *fdt, const char *s) 99*243ce5d5SMadhukar Pappireddy { 100*243ce5d5SMadhukar Pappireddy int newlen = strlen(s) + 1; 101*243ce5d5SMadhukar Pappireddy 102*243ce5d5SMadhukar Pappireddy fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen); 103*243ce5d5SMadhukar Pappireddy } 104*243ce5d5SMadhukar Pappireddy 105630b011fSAntonio Nino Diaz static int fdt_splice_string_(void *fdt, int newlen) 106630b011fSAntonio Nino Diaz { 107630b011fSAntonio Nino Diaz void *p = (char *)fdt 108630b011fSAntonio Nino Diaz + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); 109630b011fSAntonio Nino Diaz int err; 110630b011fSAntonio Nino Diaz 111630b011fSAntonio Nino Diaz if ((err = fdt_splice_(fdt, p, 0, newlen))) 112630b011fSAntonio Nino Diaz return err; 113630b011fSAntonio Nino Diaz 114630b011fSAntonio Nino Diaz fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen); 115630b011fSAntonio Nino Diaz return 0; 116630b011fSAntonio Nino Diaz } 117630b011fSAntonio Nino Diaz 118*243ce5d5SMadhukar Pappireddy /** 119*243ce5d5SMadhukar Pappireddy * fdt_find_add_string_() - Find or allocate a string 120*243ce5d5SMadhukar Pappireddy * 121*243ce5d5SMadhukar Pappireddy * @fdt: pointer to the device tree to check/adjust 122*243ce5d5SMadhukar Pappireddy * @s: string to find/add 123*243ce5d5SMadhukar Pappireddy * @allocated: Set to 0 if the string was found, 1 if not found and so 124*243ce5d5SMadhukar Pappireddy * allocated. Ignored if can_assume(NO_ROLLBACK) 125*243ce5d5SMadhukar Pappireddy * @return offset of string in the string table (whether found or added) 126*243ce5d5SMadhukar Pappireddy */ 127*243ce5d5SMadhukar Pappireddy static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) 128630b011fSAntonio Nino Diaz { 129630b011fSAntonio Nino Diaz char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); 130630b011fSAntonio Nino Diaz const char *p; 131630b011fSAntonio Nino Diaz char *new; 132630b011fSAntonio Nino Diaz int len = strlen(s) + 1; 133630b011fSAntonio Nino Diaz int err; 134630b011fSAntonio Nino Diaz 135*243ce5d5SMadhukar Pappireddy if (!can_assume(NO_ROLLBACK)) 136*243ce5d5SMadhukar Pappireddy *allocated = 0; 137*243ce5d5SMadhukar Pappireddy 138630b011fSAntonio Nino Diaz p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s); 139630b011fSAntonio Nino Diaz if (p) 140630b011fSAntonio Nino Diaz /* found it */ 141630b011fSAntonio Nino Diaz return (p - strtab); 142630b011fSAntonio Nino Diaz 143630b011fSAntonio Nino Diaz new = strtab + fdt_size_dt_strings(fdt); 144630b011fSAntonio Nino Diaz err = fdt_splice_string_(fdt, len); 145630b011fSAntonio Nino Diaz if (err) 146630b011fSAntonio Nino Diaz return err; 147630b011fSAntonio Nino Diaz 148*243ce5d5SMadhukar Pappireddy if (!can_assume(NO_ROLLBACK)) 149*243ce5d5SMadhukar Pappireddy *allocated = 1; 150*243ce5d5SMadhukar Pappireddy 151630b011fSAntonio Nino Diaz memcpy(new, s, len); 152630b011fSAntonio Nino Diaz return (new - strtab); 153630b011fSAntonio Nino Diaz } 154630b011fSAntonio Nino Diaz 155630b011fSAntonio Nino Diaz int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) 156630b011fSAntonio Nino Diaz { 157630b011fSAntonio Nino Diaz struct fdt_reserve_entry *re; 158630b011fSAntonio Nino Diaz int err; 159630b011fSAntonio Nino Diaz 160*243ce5d5SMadhukar Pappireddy FDT_RW_PROBE(fdt); 161630b011fSAntonio Nino Diaz 162630b011fSAntonio Nino Diaz re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt)); 163630b011fSAntonio Nino Diaz err = fdt_splice_mem_rsv_(fdt, re, 0, 1); 164630b011fSAntonio Nino Diaz if (err) 165630b011fSAntonio Nino Diaz return err; 166630b011fSAntonio Nino Diaz 167630b011fSAntonio Nino Diaz re->address = cpu_to_fdt64(address); 168630b011fSAntonio Nino Diaz re->size = cpu_to_fdt64(size); 169630b011fSAntonio Nino Diaz return 0; 170630b011fSAntonio Nino Diaz } 171630b011fSAntonio Nino Diaz 172630b011fSAntonio Nino Diaz int fdt_del_mem_rsv(void *fdt, int n) 173630b011fSAntonio Nino Diaz { 174630b011fSAntonio Nino Diaz struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n); 175630b011fSAntonio Nino Diaz 176*243ce5d5SMadhukar Pappireddy FDT_RW_PROBE(fdt); 177630b011fSAntonio Nino Diaz 178630b011fSAntonio Nino Diaz if (n >= fdt_num_mem_rsv(fdt)) 179630b011fSAntonio Nino Diaz return -FDT_ERR_NOTFOUND; 180630b011fSAntonio Nino Diaz 181630b011fSAntonio Nino Diaz return fdt_splice_mem_rsv_(fdt, re, 1, 0); 182630b011fSAntonio Nino Diaz } 183630b011fSAntonio Nino Diaz 184630b011fSAntonio Nino Diaz static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name, 185630b011fSAntonio Nino Diaz int len, struct fdt_property **prop) 186630b011fSAntonio Nino Diaz { 187630b011fSAntonio Nino Diaz int oldlen; 188630b011fSAntonio Nino Diaz int err; 189630b011fSAntonio Nino Diaz 190630b011fSAntonio Nino Diaz *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); 191630b011fSAntonio Nino Diaz if (!*prop) 192630b011fSAntonio Nino Diaz return oldlen; 193630b011fSAntonio Nino Diaz 194630b011fSAntonio Nino Diaz if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen), 195630b011fSAntonio Nino Diaz FDT_TAGALIGN(len)))) 196630b011fSAntonio Nino Diaz return err; 197630b011fSAntonio Nino Diaz 198630b011fSAntonio Nino Diaz (*prop)->len = cpu_to_fdt32(len); 199630b011fSAntonio Nino Diaz return 0; 200630b011fSAntonio Nino Diaz } 201630b011fSAntonio Nino Diaz 202630b011fSAntonio Nino Diaz static int fdt_add_property_(void *fdt, int nodeoffset, const char *name, 203630b011fSAntonio Nino Diaz int len, struct fdt_property **prop) 204630b011fSAntonio Nino Diaz { 205630b011fSAntonio Nino Diaz int proplen; 206630b011fSAntonio Nino Diaz int nextoffset; 207630b011fSAntonio Nino Diaz int namestroff; 208630b011fSAntonio Nino Diaz int err; 209*243ce5d5SMadhukar Pappireddy int allocated; 210630b011fSAntonio Nino Diaz 211630b011fSAntonio Nino Diaz if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) 212630b011fSAntonio Nino Diaz return nextoffset; 213630b011fSAntonio Nino Diaz 214*243ce5d5SMadhukar Pappireddy namestroff = fdt_find_add_string_(fdt, name, &allocated); 215630b011fSAntonio Nino Diaz if (namestroff < 0) 216630b011fSAntonio Nino Diaz return namestroff; 217630b011fSAntonio Nino Diaz 218630b011fSAntonio Nino Diaz *prop = fdt_offset_ptr_w_(fdt, nextoffset); 219630b011fSAntonio Nino Diaz proplen = sizeof(**prop) + FDT_TAGALIGN(len); 220630b011fSAntonio Nino Diaz 221630b011fSAntonio Nino Diaz err = fdt_splice_struct_(fdt, *prop, 0, proplen); 222*243ce5d5SMadhukar Pappireddy if (err) { 223*243ce5d5SMadhukar Pappireddy /* Delete the string if we failed to add it */ 224*243ce5d5SMadhukar Pappireddy if (!can_assume(NO_ROLLBACK) && allocated) 225*243ce5d5SMadhukar Pappireddy fdt_del_last_string_(fdt, name); 226630b011fSAntonio Nino Diaz return err; 227*243ce5d5SMadhukar Pappireddy } 228630b011fSAntonio Nino Diaz 229630b011fSAntonio Nino Diaz (*prop)->tag = cpu_to_fdt32(FDT_PROP); 230630b011fSAntonio Nino Diaz (*prop)->nameoff = cpu_to_fdt32(namestroff); 231630b011fSAntonio Nino Diaz (*prop)->len = cpu_to_fdt32(len); 232630b011fSAntonio Nino Diaz return 0; 233630b011fSAntonio Nino Diaz } 234630b011fSAntonio Nino Diaz 235630b011fSAntonio Nino Diaz int fdt_set_name(void *fdt, int nodeoffset, const char *name) 236630b011fSAntonio Nino Diaz { 237630b011fSAntonio Nino Diaz char *namep; 238630b011fSAntonio Nino Diaz int oldlen, newlen; 239630b011fSAntonio Nino Diaz int err; 240630b011fSAntonio Nino Diaz 241*243ce5d5SMadhukar Pappireddy FDT_RW_PROBE(fdt); 242630b011fSAntonio Nino Diaz 243630b011fSAntonio Nino Diaz namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen); 244630b011fSAntonio Nino Diaz if (!namep) 245630b011fSAntonio Nino Diaz return oldlen; 246630b011fSAntonio Nino Diaz 247630b011fSAntonio Nino Diaz newlen = strlen(name); 248630b011fSAntonio Nino Diaz 249630b011fSAntonio Nino Diaz err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1), 250630b011fSAntonio Nino Diaz FDT_TAGALIGN(newlen+1)); 251630b011fSAntonio Nino Diaz if (err) 252630b011fSAntonio Nino Diaz return err; 253630b011fSAntonio Nino Diaz 254630b011fSAntonio Nino Diaz memcpy(namep, name, newlen+1); 255630b011fSAntonio Nino Diaz return 0; 256630b011fSAntonio Nino Diaz } 257630b011fSAntonio Nino Diaz 258630b011fSAntonio Nino Diaz int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, 259630b011fSAntonio Nino Diaz int len, void **prop_data) 260630b011fSAntonio Nino Diaz { 261630b011fSAntonio Nino Diaz struct fdt_property *prop; 262630b011fSAntonio Nino Diaz int err; 263630b011fSAntonio Nino Diaz 264*243ce5d5SMadhukar Pappireddy FDT_RW_PROBE(fdt); 265630b011fSAntonio Nino Diaz 266630b011fSAntonio Nino Diaz err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop); 267630b011fSAntonio Nino Diaz if (err == -FDT_ERR_NOTFOUND) 268630b011fSAntonio Nino Diaz err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); 269630b011fSAntonio Nino Diaz if (err) 270630b011fSAntonio Nino Diaz return err; 271630b011fSAntonio Nino Diaz 272630b011fSAntonio Nino Diaz *prop_data = prop->data; 273630b011fSAntonio Nino Diaz return 0; 274630b011fSAntonio Nino Diaz } 275630b011fSAntonio Nino Diaz 276630b011fSAntonio Nino Diaz int fdt_setprop(void *fdt, int nodeoffset, const char *name, 277630b011fSAntonio Nino Diaz const void *val, int len) 278630b011fSAntonio Nino Diaz { 279630b011fSAntonio Nino Diaz void *prop_data; 280630b011fSAntonio Nino Diaz int err; 281630b011fSAntonio Nino Diaz 282630b011fSAntonio Nino Diaz err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data); 283630b011fSAntonio Nino Diaz if (err) 284630b011fSAntonio Nino Diaz return err; 285630b011fSAntonio Nino Diaz 286630b011fSAntonio Nino Diaz if (len) 287630b011fSAntonio Nino Diaz memcpy(prop_data, val, len); 288630b011fSAntonio Nino Diaz return 0; 289630b011fSAntonio Nino Diaz } 290630b011fSAntonio Nino Diaz 291630b011fSAntonio Nino Diaz int fdt_appendprop(void *fdt, int nodeoffset, const char *name, 292630b011fSAntonio Nino Diaz const void *val, int len) 293630b011fSAntonio Nino Diaz { 294630b011fSAntonio Nino Diaz struct fdt_property *prop; 295630b011fSAntonio Nino Diaz int err, oldlen, newlen; 296630b011fSAntonio Nino Diaz 297*243ce5d5SMadhukar Pappireddy FDT_RW_PROBE(fdt); 298630b011fSAntonio Nino Diaz 299630b011fSAntonio Nino Diaz prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); 300630b011fSAntonio Nino Diaz if (prop) { 301630b011fSAntonio Nino Diaz newlen = len + oldlen; 302630b011fSAntonio Nino Diaz err = fdt_splice_struct_(fdt, prop->data, 303630b011fSAntonio Nino Diaz FDT_TAGALIGN(oldlen), 304630b011fSAntonio Nino Diaz FDT_TAGALIGN(newlen)); 305630b011fSAntonio Nino Diaz if (err) 306630b011fSAntonio Nino Diaz return err; 307630b011fSAntonio Nino Diaz prop->len = cpu_to_fdt32(newlen); 308630b011fSAntonio Nino Diaz memcpy(prop->data + oldlen, val, len); 309630b011fSAntonio Nino Diaz } else { 310630b011fSAntonio Nino Diaz err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); 311630b011fSAntonio Nino Diaz if (err) 312630b011fSAntonio Nino Diaz return err; 313630b011fSAntonio Nino Diaz memcpy(prop->data, val, len); 314630b011fSAntonio Nino Diaz } 315630b011fSAntonio Nino Diaz return 0; 316630b011fSAntonio Nino Diaz } 317630b011fSAntonio Nino Diaz 318630b011fSAntonio Nino Diaz int fdt_delprop(void *fdt, int nodeoffset, const char *name) 319630b011fSAntonio Nino Diaz { 320630b011fSAntonio Nino Diaz struct fdt_property *prop; 321630b011fSAntonio Nino Diaz int len, proplen; 322630b011fSAntonio Nino Diaz 323*243ce5d5SMadhukar Pappireddy FDT_RW_PROBE(fdt); 324630b011fSAntonio Nino Diaz 325630b011fSAntonio Nino Diaz prop = fdt_get_property_w(fdt, nodeoffset, name, &len); 326630b011fSAntonio Nino Diaz if (!prop) 327630b011fSAntonio Nino Diaz return len; 328630b011fSAntonio Nino Diaz 329630b011fSAntonio Nino Diaz proplen = sizeof(*prop) + FDT_TAGALIGN(len); 330630b011fSAntonio Nino Diaz return fdt_splice_struct_(fdt, prop, proplen, 0); 331630b011fSAntonio Nino Diaz } 332630b011fSAntonio Nino Diaz 333630b011fSAntonio Nino Diaz int fdt_add_subnode_namelen(void *fdt, int parentoffset, 334630b011fSAntonio Nino Diaz const char *name, int namelen) 335630b011fSAntonio Nino Diaz { 336630b011fSAntonio Nino Diaz struct fdt_node_header *nh; 337630b011fSAntonio Nino Diaz int offset, nextoffset; 338630b011fSAntonio Nino Diaz int nodelen; 339630b011fSAntonio Nino Diaz int err; 340630b011fSAntonio Nino Diaz uint32_t tag; 341630b011fSAntonio Nino Diaz fdt32_t *endtag; 342630b011fSAntonio Nino Diaz 343*243ce5d5SMadhukar Pappireddy FDT_RW_PROBE(fdt); 344630b011fSAntonio Nino Diaz 345630b011fSAntonio Nino Diaz offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen); 346630b011fSAntonio Nino Diaz if (offset >= 0) 347630b011fSAntonio Nino Diaz return -FDT_ERR_EXISTS; 348630b011fSAntonio Nino Diaz else if (offset != -FDT_ERR_NOTFOUND) 349630b011fSAntonio Nino Diaz return offset; 350630b011fSAntonio Nino Diaz 351630b011fSAntonio Nino Diaz /* Try to place the new node after the parent's properties */ 352630b011fSAntonio Nino Diaz fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ 353630b011fSAntonio Nino Diaz do { 354630b011fSAntonio Nino Diaz offset = nextoffset; 355630b011fSAntonio Nino Diaz tag = fdt_next_tag(fdt, offset, &nextoffset); 356630b011fSAntonio Nino Diaz } while ((tag == FDT_PROP) || (tag == FDT_NOP)); 357630b011fSAntonio Nino Diaz 358630b011fSAntonio Nino Diaz nh = fdt_offset_ptr_w_(fdt, offset); 359630b011fSAntonio Nino Diaz nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE; 360630b011fSAntonio Nino Diaz 361630b011fSAntonio Nino Diaz err = fdt_splice_struct_(fdt, nh, 0, nodelen); 362630b011fSAntonio Nino Diaz if (err) 363630b011fSAntonio Nino Diaz return err; 364630b011fSAntonio Nino Diaz 365630b011fSAntonio Nino Diaz nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); 366630b011fSAntonio Nino Diaz memset(nh->name, 0, FDT_TAGALIGN(namelen+1)); 367630b011fSAntonio Nino Diaz memcpy(nh->name, name, namelen); 368630b011fSAntonio Nino Diaz endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE); 369630b011fSAntonio Nino Diaz *endtag = cpu_to_fdt32(FDT_END_NODE); 370630b011fSAntonio Nino Diaz 371630b011fSAntonio Nino Diaz return offset; 372630b011fSAntonio Nino Diaz } 373630b011fSAntonio Nino Diaz 374630b011fSAntonio Nino Diaz int fdt_add_subnode(void *fdt, int parentoffset, const char *name) 375630b011fSAntonio Nino Diaz { 376630b011fSAntonio Nino Diaz return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name)); 377630b011fSAntonio Nino Diaz } 378630b011fSAntonio Nino Diaz 379630b011fSAntonio Nino Diaz int fdt_del_node(void *fdt, int nodeoffset) 380630b011fSAntonio Nino Diaz { 381630b011fSAntonio Nino Diaz int endoffset; 382630b011fSAntonio Nino Diaz 383*243ce5d5SMadhukar Pappireddy FDT_RW_PROBE(fdt); 384630b011fSAntonio Nino Diaz 385630b011fSAntonio Nino Diaz endoffset = fdt_node_end_offset_(fdt, nodeoffset); 386630b011fSAntonio Nino Diaz if (endoffset < 0) 387630b011fSAntonio Nino Diaz return endoffset; 388630b011fSAntonio Nino Diaz 389630b011fSAntonio Nino Diaz return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset), 390630b011fSAntonio Nino Diaz endoffset - nodeoffset, 0); 391630b011fSAntonio Nino Diaz } 392630b011fSAntonio Nino Diaz 393630b011fSAntonio Nino Diaz static void fdt_packblocks_(const char *old, char *new, 394630b011fSAntonio Nino Diaz int mem_rsv_size, int struct_size) 395630b011fSAntonio Nino Diaz { 396630b011fSAntonio Nino Diaz int mem_rsv_off, struct_off, strings_off; 397630b011fSAntonio Nino Diaz 398630b011fSAntonio Nino Diaz mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8); 399630b011fSAntonio Nino Diaz struct_off = mem_rsv_off + mem_rsv_size; 400630b011fSAntonio Nino Diaz strings_off = struct_off + struct_size; 401630b011fSAntonio Nino Diaz 402630b011fSAntonio Nino Diaz memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size); 403630b011fSAntonio Nino Diaz fdt_set_off_mem_rsvmap(new, mem_rsv_off); 404630b011fSAntonio Nino Diaz 405630b011fSAntonio Nino Diaz memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size); 406630b011fSAntonio Nino Diaz fdt_set_off_dt_struct(new, struct_off); 407630b011fSAntonio Nino Diaz fdt_set_size_dt_struct(new, struct_size); 408630b011fSAntonio Nino Diaz 409630b011fSAntonio Nino Diaz memmove(new + strings_off, old + fdt_off_dt_strings(old), 410630b011fSAntonio Nino Diaz fdt_size_dt_strings(old)); 411630b011fSAntonio Nino Diaz fdt_set_off_dt_strings(new, strings_off); 412630b011fSAntonio Nino Diaz fdt_set_size_dt_strings(new, fdt_size_dt_strings(old)); 413630b011fSAntonio Nino Diaz } 414630b011fSAntonio Nino Diaz 415630b011fSAntonio Nino Diaz int fdt_open_into(const void *fdt, void *buf, int bufsize) 416630b011fSAntonio Nino Diaz { 417630b011fSAntonio Nino Diaz int err; 418630b011fSAntonio Nino Diaz int mem_rsv_size, struct_size; 419630b011fSAntonio Nino Diaz int newsize; 420630b011fSAntonio Nino Diaz const char *fdtstart = fdt; 421630b011fSAntonio Nino Diaz const char *fdtend = fdtstart + fdt_totalsize(fdt); 422630b011fSAntonio Nino Diaz char *tmp; 423630b011fSAntonio Nino Diaz 424*243ce5d5SMadhukar Pappireddy FDT_RO_PROBE(fdt); 425630b011fSAntonio Nino Diaz 426630b011fSAntonio Nino Diaz mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) 427630b011fSAntonio Nino Diaz * sizeof(struct fdt_reserve_entry); 428630b011fSAntonio Nino Diaz 429*243ce5d5SMadhukar Pappireddy if (can_assume(LATEST) || fdt_version(fdt) >= 17) { 430630b011fSAntonio Nino Diaz struct_size = fdt_size_dt_struct(fdt); 431630b011fSAntonio Nino Diaz } else { 432630b011fSAntonio Nino Diaz struct_size = 0; 433630b011fSAntonio Nino Diaz while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) 434630b011fSAntonio Nino Diaz ; 435630b011fSAntonio Nino Diaz if (struct_size < 0) 436630b011fSAntonio Nino Diaz return struct_size; 437630b011fSAntonio Nino Diaz } 438630b011fSAntonio Nino Diaz 439*243ce5d5SMadhukar Pappireddy if (can_assume(LIBFDT_ORDER) || 440*243ce5d5SMadhukar Pappireddy !fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) { 441630b011fSAntonio Nino Diaz /* no further work necessary */ 442630b011fSAntonio Nino Diaz err = fdt_move(fdt, buf, bufsize); 443630b011fSAntonio Nino Diaz if (err) 444630b011fSAntonio Nino Diaz return err; 445630b011fSAntonio Nino Diaz fdt_set_version(buf, 17); 446630b011fSAntonio Nino Diaz fdt_set_size_dt_struct(buf, struct_size); 447630b011fSAntonio Nino Diaz fdt_set_totalsize(buf, bufsize); 448630b011fSAntonio Nino Diaz return 0; 449630b011fSAntonio Nino Diaz } 450630b011fSAntonio Nino Diaz 451630b011fSAntonio Nino Diaz /* Need to reorder */ 452630b011fSAntonio Nino Diaz newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size 453630b011fSAntonio Nino Diaz + struct_size + fdt_size_dt_strings(fdt); 454630b011fSAntonio Nino Diaz 455630b011fSAntonio Nino Diaz if (bufsize < newsize) 456630b011fSAntonio Nino Diaz return -FDT_ERR_NOSPACE; 457630b011fSAntonio Nino Diaz 458630b011fSAntonio Nino Diaz /* First attempt to build converted tree at beginning of buffer */ 459630b011fSAntonio Nino Diaz tmp = buf; 460630b011fSAntonio Nino Diaz /* But if that overlaps with the old tree... */ 461630b011fSAntonio Nino Diaz if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) { 462630b011fSAntonio Nino Diaz /* Try right after the old tree instead */ 463630b011fSAntonio Nino Diaz tmp = (char *)(uintptr_t)fdtend; 464630b011fSAntonio Nino Diaz if ((tmp + newsize) > ((char *)buf + bufsize)) 465630b011fSAntonio Nino Diaz return -FDT_ERR_NOSPACE; 466630b011fSAntonio Nino Diaz } 467630b011fSAntonio Nino Diaz 468630b011fSAntonio Nino Diaz fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size); 469630b011fSAntonio Nino Diaz memmove(buf, tmp, newsize); 470630b011fSAntonio Nino Diaz 471630b011fSAntonio Nino Diaz fdt_set_magic(buf, FDT_MAGIC); 472630b011fSAntonio Nino Diaz fdt_set_totalsize(buf, bufsize); 473630b011fSAntonio Nino Diaz fdt_set_version(buf, 17); 474630b011fSAntonio Nino Diaz fdt_set_last_comp_version(buf, 16); 475630b011fSAntonio Nino Diaz fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); 476630b011fSAntonio Nino Diaz 477630b011fSAntonio Nino Diaz return 0; 478630b011fSAntonio Nino Diaz } 479630b011fSAntonio Nino Diaz 480630b011fSAntonio Nino Diaz int fdt_pack(void *fdt) 481630b011fSAntonio Nino Diaz { 482630b011fSAntonio Nino Diaz int mem_rsv_size; 483630b011fSAntonio Nino Diaz 484*243ce5d5SMadhukar Pappireddy FDT_RW_PROBE(fdt); 485630b011fSAntonio Nino Diaz 486630b011fSAntonio Nino Diaz mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) 487630b011fSAntonio Nino Diaz * sizeof(struct fdt_reserve_entry); 488630b011fSAntonio Nino Diaz fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); 489630b011fSAntonio Nino Diaz fdt_set_totalsize(fdt, fdt_data_size_(fdt)); 490630b011fSAntonio Nino Diaz 491630b011fSAntonio Nino Diaz return 0; 492630b011fSAntonio Nino Diaz } 493