1*91f16700Schasinglulu // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) 2*91f16700Schasinglulu /* 3*91f16700Schasinglulu * libfdt - Flat Device Tree manipulation 4*91f16700Schasinglulu * Copyright (C) 2006 David Gibson, IBM Corporation. 5*91f16700Schasinglulu */ 6*91f16700Schasinglulu #include "libfdt_env.h" 7*91f16700Schasinglulu 8*91f16700Schasinglulu #include <fdt.h> 9*91f16700Schasinglulu #include <libfdt.h> 10*91f16700Schasinglulu 11*91f16700Schasinglulu #include "libfdt_internal.h" 12*91f16700Schasinglulu 13*91f16700Schasinglulu static int fdt_sw_probe_(void *fdt) 14*91f16700Schasinglulu { 15*91f16700Schasinglulu if (!can_assume(VALID_INPUT)) { 16*91f16700Schasinglulu if (fdt_magic(fdt) == FDT_MAGIC) 17*91f16700Schasinglulu return -FDT_ERR_BADSTATE; 18*91f16700Schasinglulu else if (fdt_magic(fdt) != FDT_SW_MAGIC) 19*91f16700Schasinglulu return -FDT_ERR_BADMAGIC; 20*91f16700Schasinglulu } 21*91f16700Schasinglulu 22*91f16700Schasinglulu return 0; 23*91f16700Schasinglulu } 24*91f16700Schasinglulu 25*91f16700Schasinglulu #define FDT_SW_PROBE(fdt) \ 26*91f16700Schasinglulu { \ 27*91f16700Schasinglulu int err; \ 28*91f16700Schasinglulu if ((err = fdt_sw_probe_(fdt)) != 0) \ 29*91f16700Schasinglulu return err; \ 30*91f16700Schasinglulu } 31*91f16700Schasinglulu 32*91f16700Schasinglulu /* 'memrsv' state: Initial state after fdt_create() 33*91f16700Schasinglulu * 34*91f16700Schasinglulu * Allowed functions: 35*91f16700Schasinglulu * fdt_add_reservemap_entry() 36*91f16700Schasinglulu * fdt_finish_reservemap() [moves to 'struct' state] 37*91f16700Schasinglulu */ 38*91f16700Schasinglulu static int fdt_sw_probe_memrsv_(void *fdt) 39*91f16700Schasinglulu { 40*91f16700Schasinglulu int err = fdt_sw_probe_(fdt); 41*91f16700Schasinglulu if (err) 42*91f16700Schasinglulu return err; 43*91f16700Schasinglulu 44*91f16700Schasinglulu if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0) 45*91f16700Schasinglulu return -FDT_ERR_BADSTATE; 46*91f16700Schasinglulu return 0; 47*91f16700Schasinglulu } 48*91f16700Schasinglulu 49*91f16700Schasinglulu #define FDT_SW_PROBE_MEMRSV(fdt) \ 50*91f16700Schasinglulu { \ 51*91f16700Schasinglulu int err; \ 52*91f16700Schasinglulu if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \ 53*91f16700Schasinglulu return err; \ 54*91f16700Schasinglulu } 55*91f16700Schasinglulu 56*91f16700Schasinglulu /* 'struct' state: Enter this state after fdt_finish_reservemap() 57*91f16700Schasinglulu * 58*91f16700Schasinglulu * Allowed functions: 59*91f16700Schasinglulu * fdt_begin_node() 60*91f16700Schasinglulu * fdt_end_node() 61*91f16700Schasinglulu * fdt_property*() 62*91f16700Schasinglulu * fdt_finish() [moves to 'complete' state] 63*91f16700Schasinglulu */ 64*91f16700Schasinglulu static int fdt_sw_probe_struct_(void *fdt) 65*91f16700Schasinglulu { 66*91f16700Schasinglulu int err = fdt_sw_probe_(fdt); 67*91f16700Schasinglulu if (err) 68*91f16700Schasinglulu return err; 69*91f16700Schasinglulu 70*91f16700Schasinglulu if (!can_assume(VALID_INPUT) && 71*91f16700Schasinglulu fdt_off_dt_strings(fdt) != fdt_totalsize(fdt)) 72*91f16700Schasinglulu return -FDT_ERR_BADSTATE; 73*91f16700Schasinglulu return 0; 74*91f16700Schasinglulu } 75*91f16700Schasinglulu 76*91f16700Schasinglulu #define FDT_SW_PROBE_STRUCT(fdt) \ 77*91f16700Schasinglulu { \ 78*91f16700Schasinglulu int err; \ 79*91f16700Schasinglulu if ((err = fdt_sw_probe_struct_(fdt)) != 0) \ 80*91f16700Schasinglulu return err; \ 81*91f16700Schasinglulu } 82*91f16700Schasinglulu 83*91f16700Schasinglulu static inline uint32_t sw_flags(void *fdt) 84*91f16700Schasinglulu { 85*91f16700Schasinglulu /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */ 86*91f16700Schasinglulu return fdt_last_comp_version(fdt); 87*91f16700Schasinglulu } 88*91f16700Schasinglulu 89*91f16700Schasinglulu /* 'complete' state: Enter this state after fdt_finish() 90*91f16700Schasinglulu * 91*91f16700Schasinglulu * Allowed functions: none 92*91f16700Schasinglulu */ 93*91f16700Schasinglulu 94*91f16700Schasinglulu static void *fdt_grab_space_(void *fdt, size_t len) 95*91f16700Schasinglulu { 96*91f16700Schasinglulu unsigned int offset = fdt_size_dt_struct(fdt); 97*91f16700Schasinglulu unsigned int spaceleft; 98*91f16700Schasinglulu 99*91f16700Schasinglulu spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) 100*91f16700Schasinglulu - fdt_size_dt_strings(fdt); 101*91f16700Schasinglulu 102*91f16700Schasinglulu if ((offset + len < offset) || (offset + len > spaceleft)) 103*91f16700Schasinglulu return NULL; 104*91f16700Schasinglulu 105*91f16700Schasinglulu fdt_set_size_dt_struct(fdt, offset + len); 106*91f16700Schasinglulu return fdt_offset_ptr_w_(fdt, offset); 107*91f16700Schasinglulu } 108*91f16700Schasinglulu 109*91f16700Schasinglulu int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags) 110*91f16700Schasinglulu { 111*91f16700Schasinglulu const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header), 112*91f16700Schasinglulu sizeof(struct fdt_reserve_entry)); 113*91f16700Schasinglulu void *fdt = buf; 114*91f16700Schasinglulu 115*91f16700Schasinglulu if (bufsize < hdrsize) 116*91f16700Schasinglulu return -FDT_ERR_NOSPACE; 117*91f16700Schasinglulu 118*91f16700Schasinglulu if (flags & ~FDT_CREATE_FLAGS_ALL) 119*91f16700Schasinglulu return -FDT_ERR_BADFLAGS; 120*91f16700Schasinglulu 121*91f16700Schasinglulu memset(buf, 0, bufsize); 122*91f16700Schasinglulu 123*91f16700Schasinglulu /* 124*91f16700Schasinglulu * magic and last_comp_version keep intermediate state during the fdt 125*91f16700Schasinglulu * creation process, which is replaced with the proper FDT format by 126*91f16700Schasinglulu * fdt_finish(). 127*91f16700Schasinglulu * 128*91f16700Schasinglulu * flags should be accessed with sw_flags(). 129*91f16700Schasinglulu */ 130*91f16700Schasinglulu fdt_set_magic(fdt, FDT_SW_MAGIC); 131*91f16700Schasinglulu fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); 132*91f16700Schasinglulu fdt_set_last_comp_version(fdt, flags); 133*91f16700Schasinglulu 134*91f16700Schasinglulu fdt_set_totalsize(fdt, bufsize); 135*91f16700Schasinglulu 136*91f16700Schasinglulu fdt_set_off_mem_rsvmap(fdt, hdrsize); 137*91f16700Schasinglulu fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); 138*91f16700Schasinglulu fdt_set_off_dt_strings(fdt, 0); 139*91f16700Schasinglulu 140*91f16700Schasinglulu return 0; 141*91f16700Schasinglulu } 142*91f16700Schasinglulu 143*91f16700Schasinglulu int fdt_create(void *buf, int bufsize) 144*91f16700Schasinglulu { 145*91f16700Schasinglulu return fdt_create_with_flags(buf, bufsize, 0); 146*91f16700Schasinglulu } 147*91f16700Schasinglulu 148*91f16700Schasinglulu int fdt_resize(void *fdt, void *buf, int bufsize) 149*91f16700Schasinglulu { 150*91f16700Schasinglulu size_t headsize, tailsize; 151*91f16700Schasinglulu char *oldtail, *newtail; 152*91f16700Schasinglulu 153*91f16700Schasinglulu FDT_SW_PROBE(fdt); 154*91f16700Schasinglulu 155*91f16700Schasinglulu if (bufsize < 0) 156*91f16700Schasinglulu return -FDT_ERR_NOSPACE; 157*91f16700Schasinglulu 158*91f16700Schasinglulu headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 159*91f16700Schasinglulu tailsize = fdt_size_dt_strings(fdt); 160*91f16700Schasinglulu 161*91f16700Schasinglulu if (!can_assume(VALID_DTB) && 162*91f16700Schasinglulu headsize + tailsize > fdt_totalsize(fdt)) 163*91f16700Schasinglulu return -FDT_ERR_INTERNAL; 164*91f16700Schasinglulu 165*91f16700Schasinglulu if ((headsize + tailsize) > (unsigned)bufsize) 166*91f16700Schasinglulu return -FDT_ERR_NOSPACE; 167*91f16700Schasinglulu 168*91f16700Schasinglulu oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; 169*91f16700Schasinglulu newtail = (char *)buf + bufsize - tailsize; 170*91f16700Schasinglulu 171*91f16700Schasinglulu /* Two cases to avoid clobbering data if the old and new 172*91f16700Schasinglulu * buffers partially overlap */ 173*91f16700Schasinglulu if (buf <= fdt) { 174*91f16700Schasinglulu memmove(buf, fdt, headsize); 175*91f16700Schasinglulu memmove(newtail, oldtail, tailsize); 176*91f16700Schasinglulu } else { 177*91f16700Schasinglulu memmove(newtail, oldtail, tailsize); 178*91f16700Schasinglulu memmove(buf, fdt, headsize); 179*91f16700Schasinglulu } 180*91f16700Schasinglulu 181*91f16700Schasinglulu fdt_set_totalsize(buf, bufsize); 182*91f16700Schasinglulu if (fdt_off_dt_strings(buf)) 183*91f16700Schasinglulu fdt_set_off_dt_strings(buf, bufsize); 184*91f16700Schasinglulu 185*91f16700Schasinglulu return 0; 186*91f16700Schasinglulu } 187*91f16700Schasinglulu 188*91f16700Schasinglulu int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) 189*91f16700Schasinglulu { 190*91f16700Schasinglulu struct fdt_reserve_entry *re; 191*91f16700Schasinglulu int offset; 192*91f16700Schasinglulu 193*91f16700Schasinglulu FDT_SW_PROBE_MEMRSV(fdt); 194*91f16700Schasinglulu 195*91f16700Schasinglulu offset = fdt_off_dt_struct(fdt); 196*91f16700Schasinglulu if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) 197*91f16700Schasinglulu return -FDT_ERR_NOSPACE; 198*91f16700Schasinglulu 199*91f16700Schasinglulu re = (struct fdt_reserve_entry *)((char *)fdt + offset); 200*91f16700Schasinglulu re->address = cpu_to_fdt64(addr); 201*91f16700Schasinglulu re->size = cpu_to_fdt64(size); 202*91f16700Schasinglulu 203*91f16700Schasinglulu fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); 204*91f16700Schasinglulu 205*91f16700Schasinglulu return 0; 206*91f16700Schasinglulu } 207*91f16700Schasinglulu 208*91f16700Schasinglulu int fdt_finish_reservemap(void *fdt) 209*91f16700Schasinglulu { 210*91f16700Schasinglulu int err = fdt_add_reservemap_entry(fdt, 0, 0); 211*91f16700Schasinglulu 212*91f16700Schasinglulu if (err) 213*91f16700Schasinglulu return err; 214*91f16700Schasinglulu 215*91f16700Schasinglulu fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt)); 216*91f16700Schasinglulu return 0; 217*91f16700Schasinglulu } 218*91f16700Schasinglulu 219*91f16700Schasinglulu int fdt_begin_node(void *fdt, const char *name) 220*91f16700Schasinglulu { 221*91f16700Schasinglulu struct fdt_node_header *nh; 222*91f16700Schasinglulu int namelen; 223*91f16700Schasinglulu 224*91f16700Schasinglulu FDT_SW_PROBE_STRUCT(fdt); 225*91f16700Schasinglulu 226*91f16700Schasinglulu namelen = strlen(name) + 1; 227*91f16700Schasinglulu nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); 228*91f16700Schasinglulu if (! nh) 229*91f16700Schasinglulu return -FDT_ERR_NOSPACE; 230*91f16700Schasinglulu 231*91f16700Schasinglulu nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); 232*91f16700Schasinglulu memcpy(nh->name, name, namelen); 233*91f16700Schasinglulu return 0; 234*91f16700Schasinglulu } 235*91f16700Schasinglulu 236*91f16700Schasinglulu int fdt_end_node(void *fdt) 237*91f16700Schasinglulu { 238*91f16700Schasinglulu fdt32_t *en; 239*91f16700Schasinglulu 240*91f16700Schasinglulu FDT_SW_PROBE_STRUCT(fdt); 241*91f16700Schasinglulu 242*91f16700Schasinglulu en = fdt_grab_space_(fdt, FDT_TAGSIZE); 243*91f16700Schasinglulu if (! en) 244*91f16700Schasinglulu return -FDT_ERR_NOSPACE; 245*91f16700Schasinglulu 246*91f16700Schasinglulu *en = cpu_to_fdt32(FDT_END_NODE); 247*91f16700Schasinglulu return 0; 248*91f16700Schasinglulu } 249*91f16700Schasinglulu 250*91f16700Schasinglulu static int fdt_add_string_(void *fdt, const char *s) 251*91f16700Schasinglulu { 252*91f16700Schasinglulu char *strtab = (char *)fdt + fdt_totalsize(fdt); 253*91f16700Schasinglulu unsigned int strtabsize = fdt_size_dt_strings(fdt); 254*91f16700Schasinglulu unsigned int len = strlen(s) + 1; 255*91f16700Schasinglulu unsigned int struct_top, offset; 256*91f16700Schasinglulu 257*91f16700Schasinglulu offset = strtabsize + len; 258*91f16700Schasinglulu struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 259*91f16700Schasinglulu if (fdt_totalsize(fdt) - offset < struct_top) 260*91f16700Schasinglulu return 0; /* no more room :( */ 261*91f16700Schasinglulu 262*91f16700Schasinglulu memcpy(strtab - offset, s, len); 263*91f16700Schasinglulu fdt_set_size_dt_strings(fdt, strtabsize + len); 264*91f16700Schasinglulu return -offset; 265*91f16700Schasinglulu } 266*91f16700Schasinglulu 267*91f16700Schasinglulu /* Must only be used to roll back in case of error */ 268*91f16700Schasinglulu static void fdt_del_last_string_(void *fdt, const char *s) 269*91f16700Schasinglulu { 270*91f16700Schasinglulu int strtabsize = fdt_size_dt_strings(fdt); 271*91f16700Schasinglulu int len = strlen(s) + 1; 272*91f16700Schasinglulu 273*91f16700Schasinglulu fdt_set_size_dt_strings(fdt, strtabsize - len); 274*91f16700Schasinglulu } 275*91f16700Schasinglulu 276*91f16700Schasinglulu static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) 277*91f16700Schasinglulu { 278*91f16700Schasinglulu char *strtab = (char *)fdt + fdt_totalsize(fdt); 279*91f16700Schasinglulu int strtabsize = fdt_size_dt_strings(fdt); 280*91f16700Schasinglulu const char *p; 281*91f16700Schasinglulu 282*91f16700Schasinglulu *allocated = 0; 283*91f16700Schasinglulu 284*91f16700Schasinglulu p = fdt_find_string_(strtab - strtabsize, strtabsize, s); 285*91f16700Schasinglulu if (p) 286*91f16700Schasinglulu return p - strtab; 287*91f16700Schasinglulu 288*91f16700Schasinglulu *allocated = 1; 289*91f16700Schasinglulu 290*91f16700Schasinglulu return fdt_add_string_(fdt, s); 291*91f16700Schasinglulu } 292*91f16700Schasinglulu 293*91f16700Schasinglulu int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) 294*91f16700Schasinglulu { 295*91f16700Schasinglulu struct fdt_property *prop; 296*91f16700Schasinglulu int nameoff; 297*91f16700Schasinglulu int allocated; 298*91f16700Schasinglulu 299*91f16700Schasinglulu FDT_SW_PROBE_STRUCT(fdt); 300*91f16700Schasinglulu 301*91f16700Schasinglulu /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */ 302*91f16700Schasinglulu if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) { 303*91f16700Schasinglulu allocated = 1; 304*91f16700Schasinglulu nameoff = fdt_add_string_(fdt, name); 305*91f16700Schasinglulu } else { 306*91f16700Schasinglulu nameoff = fdt_find_add_string_(fdt, name, &allocated); 307*91f16700Schasinglulu } 308*91f16700Schasinglulu if (nameoff == 0) 309*91f16700Schasinglulu return -FDT_ERR_NOSPACE; 310*91f16700Schasinglulu 311*91f16700Schasinglulu prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); 312*91f16700Schasinglulu if (! prop) { 313*91f16700Schasinglulu if (allocated) 314*91f16700Schasinglulu fdt_del_last_string_(fdt, name); 315*91f16700Schasinglulu return -FDT_ERR_NOSPACE; 316*91f16700Schasinglulu } 317*91f16700Schasinglulu 318*91f16700Schasinglulu prop->tag = cpu_to_fdt32(FDT_PROP); 319*91f16700Schasinglulu prop->nameoff = cpu_to_fdt32(nameoff); 320*91f16700Schasinglulu prop->len = cpu_to_fdt32(len); 321*91f16700Schasinglulu *valp = prop->data; 322*91f16700Schasinglulu return 0; 323*91f16700Schasinglulu } 324*91f16700Schasinglulu 325*91f16700Schasinglulu int fdt_property(void *fdt, const char *name, const void *val, int len) 326*91f16700Schasinglulu { 327*91f16700Schasinglulu void *ptr; 328*91f16700Schasinglulu int ret; 329*91f16700Schasinglulu 330*91f16700Schasinglulu ret = fdt_property_placeholder(fdt, name, len, &ptr); 331*91f16700Schasinglulu if (ret) 332*91f16700Schasinglulu return ret; 333*91f16700Schasinglulu memcpy(ptr, val, len); 334*91f16700Schasinglulu return 0; 335*91f16700Schasinglulu } 336*91f16700Schasinglulu 337*91f16700Schasinglulu int fdt_finish(void *fdt) 338*91f16700Schasinglulu { 339*91f16700Schasinglulu char *p = (char *)fdt; 340*91f16700Schasinglulu fdt32_t *end; 341*91f16700Schasinglulu int oldstroffset, newstroffset; 342*91f16700Schasinglulu uint32_t tag; 343*91f16700Schasinglulu int offset, nextoffset; 344*91f16700Schasinglulu 345*91f16700Schasinglulu FDT_SW_PROBE_STRUCT(fdt); 346*91f16700Schasinglulu 347*91f16700Schasinglulu /* Add terminator */ 348*91f16700Schasinglulu end = fdt_grab_space_(fdt, sizeof(*end)); 349*91f16700Schasinglulu if (! end) 350*91f16700Schasinglulu return -FDT_ERR_NOSPACE; 351*91f16700Schasinglulu *end = cpu_to_fdt32(FDT_END); 352*91f16700Schasinglulu 353*91f16700Schasinglulu /* Relocate the string table */ 354*91f16700Schasinglulu oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); 355*91f16700Schasinglulu newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 356*91f16700Schasinglulu memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); 357*91f16700Schasinglulu fdt_set_off_dt_strings(fdt, newstroffset); 358*91f16700Schasinglulu 359*91f16700Schasinglulu /* Walk the structure, correcting string offsets */ 360*91f16700Schasinglulu offset = 0; 361*91f16700Schasinglulu while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { 362*91f16700Schasinglulu if (tag == FDT_PROP) { 363*91f16700Schasinglulu struct fdt_property *prop = 364*91f16700Schasinglulu fdt_offset_ptr_w_(fdt, offset); 365*91f16700Schasinglulu int nameoff; 366*91f16700Schasinglulu 367*91f16700Schasinglulu nameoff = fdt32_to_cpu(prop->nameoff); 368*91f16700Schasinglulu nameoff += fdt_size_dt_strings(fdt); 369*91f16700Schasinglulu prop->nameoff = cpu_to_fdt32(nameoff); 370*91f16700Schasinglulu } 371*91f16700Schasinglulu offset = nextoffset; 372*91f16700Schasinglulu } 373*91f16700Schasinglulu if (nextoffset < 0) 374*91f16700Schasinglulu return nextoffset; 375*91f16700Schasinglulu 376*91f16700Schasinglulu /* Finally, adjust the header */ 377*91f16700Schasinglulu fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); 378*91f16700Schasinglulu 379*91f16700Schasinglulu /* And fix up fields that were keeping intermediate state. */ 380*91f16700Schasinglulu fdt_set_last_comp_version(fdt, FDT_LAST_COMPATIBLE_VERSION); 381*91f16700Schasinglulu fdt_set_magic(fdt, FDT_MAGIC); 382*91f16700Schasinglulu 383*91f16700Schasinglulu return 0; 384*91f16700Schasinglulu } 385