112869ecdSRob Herring // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) 29fffb55fSDavid Gibson /* 39fffb55fSDavid Gibson * libfdt - Flat Device Tree manipulation 49fffb55fSDavid Gibson * Copyright (C) 2006 David Gibson, IBM Corporation. 59fffb55fSDavid Gibson */ 69fffb55fSDavid Gibson #include "libfdt_env.h" 79fffb55fSDavid Gibson 89fffb55fSDavid Gibson #include <fdt.h> 99fffb55fSDavid Gibson #include <libfdt.h> 109fffb55fSDavid Gibson 119fffb55fSDavid Gibson #include "libfdt_internal.h" 129fffb55fSDavid Gibson 13f858927fSRob Herring static int fdt_sw_probe_(void *fdt) 149fffb55fSDavid Gibson { 15d047cd8aSRob Herring if (!can_assume(VALID_INPUT)) { 16f858927fSRob Herring if (fdt_magic(fdt) == FDT_MAGIC) 17f858927fSRob Herring return -FDT_ERR_BADSTATE; 18f858927fSRob Herring else if (fdt_magic(fdt) != FDT_SW_MAGIC) 199fffb55fSDavid Gibson return -FDT_ERR_BADMAGIC; 20d047cd8aSRob Herring } 21d047cd8aSRob Herring 229fffb55fSDavid Gibson return 0; 239fffb55fSDavid Gibson } 249fffb55fSDavid Gibson 25f858927fSRob Herring #define FDT_SW_PROBE(fdt) \ 269fffb55fSDavid Gibson { \ 279fffb55fSDavid Gibson int err; \ 28f858927fSRob Herring if ((err = fdt_sw_probe_(fdt)) != 0) \ 299fffb55fSDavid Gibson return err; \ 309fffb55fSDavid Gibson } 319fffb55fSDavid Gibson 32f858927fSRob Herring /* 'memrsv' state: Initial state after fdt_create() 33f858927fSRob Herring * 34f858927fSRob Herring * Allowed functions: 353eb619b2SRob Herring * fdt_add_reservemap_entry() 36f858927fSRob Herring * fdt_finish_reservemap() [moves to 'struct' state] 37f858927fSRob Herring */ 38f858927fSRob Herring static int fdt_sw_probe_memrsv_(void *fdt) 39f858927fSRob Herring { 40f858927fSRob Herring int err = fdt_sw_probe_(fdt); 41f858927fSRob Herring if (err) 42f858927fSRob Herring return err; 43f858927fSRob Herring 44d047cd8aSRob Herring if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0) 45f858927fSRob Herring return -FDT_ERR_BADSTATE; 46f858927fSRob Herring return 0; 47f858927fSRob Herring } 48f858927fSRob Herring 49f858927fSRob Herring #define FDT_SW_PROBE_MEMRSV(fdt) \ 50f858927fSRob Herring { \ 51f858927fSRob Herring int err; \ 52f858927fSRob Herring if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \ 53f858927fSRob Herring return err; \ 54f858927fSRob Herring } 55f858927fSRob Herring 56f858927fSRob Herring /* 'struct' state: Enter this state after fdt_finish_reservemap() 57f858927fSRob Herring * 58f858927fSRob Herring * Allowed functions: 59f858927fSRob Herring * fdt_begin_node() 60f858927fSRob Herring * fdt_end_node() 61f858927fSRob Herring * fdt_property*() 62f858927fSRob Herring * fdt_finish() [moves to 'complete' state] 63f858927fSRob Herring */ 64f858927fSRob Herring static int fdt_sw_probe_struct_(void *fdt) 65f858927fSRob Herring { 66f858927fSRob Herring int err = fdt_sw_probe_(fdt); 67f858927fSRob Herring if (err) 68f858927fSRob Herring return err; 69f858927fSRob Herring 70d047cd8aSRob Herring if (!can_assume(VALID_INPUT) && 71d047cd8aSRob Herring fdt_off_dt_strings(fdt) != fdt_totalsize(fdt)) 72f858927fSRob Herring return -FDT_ERR_BADSTATE; 73f858927fSRob Herring return 0; 74f858927fSRob Herring } 75f858927fSRob Herring 76f858927fSRob Herring #define FDT_SW_PROBE_STRUCT(fdt) \ 77f858927fSRob Herring { \ 78f858927fSRob Herring int err; \ 79f858927fSRob Herring if ((err = fdt_sw_probe_struct_(fdt)) != 0) \ 80f858927fSRob Herring return err; \ 81f858927fSRob Herring } 82f858927fSRob Herring 839bb9c6a1SRob Herring static inline uint32_t sw_flags(void *fdt) 849bb9c6a1SRob Herring { 859bb9c6a1SRob Herring /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */ 869bb9c6a1SRob Herring return fdt_last_comp_version(fdt); 879bb9c6a1SRob Herring } 889bb9c6a1SRob Herring 89f858927fSRob Herring /* 'complete' state: Enter this state after fdt_finish() 90f858927fSRob Herring * 91f858927fSRob Herring * Allowed functions: none 92f858927fSRob Herring */ 93f858927fSRob Herring 949130ba88SRob Herring static void *fdt_grab_space_(void *fdt, size_t len) 959fffb55fSDavid Gibson { 96*6e9c9686SRob Herring unsigned int offset = fdt_size_dt_struct(fdt); 97*6e9c9686SRob Herring unsigned int spaceleft; 989fffb55fSDavid Gibson 999fffb55fSDavid Gibson spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) 1009fffb55fSDavid Gibson - fdt_size_dt_strings(fdt); 1019fffb55fSDavid Gibson 1029fffb55fSDavid Gibson if ((offset + len < offset) || (offset + len > spaceleft)) 1039fffb55fSDavid Gibson return NULL; 1049fffb55fSDavid Gibson 1059fffb55fSDavid Gibson fdt_set_size_dt_struct(fdt, offset + len); 1069130ba88SRob Herring return fdt_offset_ptr_w_(fdt, offset); 1079fffb55fSDavid Gibson } 1089fffb55fSDavid Gibson 1099bb9c6a1SRob Herring int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags) 1109fffb55fSDavid Gibson { 111*6e9c9686SRob Herring const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header), 112f858927fSRob Herring sizeof(struct fdt_reserve_entry)); 1139fffb55fSDavid Gibson void *fdt = buf; 1149fffb55fSDavid Gibson 115f858927fSRob Herring if (bufsize < hdrsize) 1169fffb55fSDavid Gibson return -FDT_ERR_NOSPACE; 1179fffb55fSDavid Gibson 1189bb9c6a1SRob Herring if (flags & ~FDT_CREATE_FLAGS_ALL) 1199bb9c6a1SRob Herring return -FDT_ERR_BADFLAGS; 1209bb9c6a1SRob Herring 1219fffb55fSDavid Gibson memset(buf, 0, bufsize); 1229fffb55fSDavid Gibson 1239bb9c6a1SRob Herring /* 1249bb9c6a1SRob Herring * magic and last_comp_version keep intermediate state during the fdt 1259bb9c6a1SRob Herring * creation process, which is replaced with the proper FDT format by 1269bb9c6a1SRob Herring * fdt_finish(). 1279bb9c6a1SRob Herring * 1289bb9c6a1SRob Herring * flags should be accessed with sw_flags(). 1299bb9c6a1SRob Herring */ 1309fffb55fSDavid Gibson fdt_set_magic(fdt, FDT_SW_MAGIC); 1319fffb55fSDavid Gibson fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); 1329bb9c6a1SRob Herring fdt_set_last_comp_version(fdt, flags); 1339bb9c6a1SRob Herring 1349fffb55fSDavid Gibson fdt_set_totalsize(fdt, bufsize); 1359fffb55fSDavid Gibson 136f858927fSRob Herring fdt_set_off_mem_rsvmap(fdt, hdrsize); 1379fffb55fSDavid Gibson fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); 138f858927fSRob Herring fdt_set_off_dt_strings(fdt, 0); 1399fffb55fSDavid Gibson 1409fffb55fSDavid Gibson return 0; 1419fffb55fSDavid Gibson } 1429fffb55fSDavid Gibson 1439bb9c6a1SRob Herring int fdt_create(void *buf, int bufsize) 1449bb9c6a1SRob Herring { 1459bb9c6a1SRob Herring return fdt_create_with_flags(buf, bufsize, 0); 1469bb9c6a1SRob Herring } 1479bb9c6a1SRob Herring 14847605971SRob Herring int fdt_resize(void *fdt, void *buf, int bufsize) 14947605971SRob Herring { 15047605971SRob Herring size_t headsize, tailsize; 15147605971SRob Herring char *oldtail, *newtail; 15247605971SRob Herring 153f858927fSRob Herring FDT_SW_PROBE(fdt); 15447605971SRob Herring 155*6e9c9686SRob Herring if (bufsize < 0) 156*6e9c9686SRob Herring return -FDT_ERR_NOSPACE; 157*6e9c9686SRob Herring 158f858927fSRob Herring headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 15947605971SRob Herring tailsize = fdt_size_dt_strings(fdt); 16047605971SRob Herring 161d047cd8aSRob Herring if (!can_assume(VALID_DTB) && 162d047cd8aSRob Herring headsize + tailsize > fdt_totalsize(fdt)) 163f858927fSRob Herring return -FDT_ERR_INTERNAL; 164f858927fSRob Herring 165*6e9c9686SRob Herring if ((headsize + tailsize) > (unsigned)bufsize) 16647605971SRob Herring return -FDT_ERR_NOSPACE; 16747605971SRob Herring 16847605971SRob Herring oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; 16947605971SRob Herring newtail = (char *)buf + bufsize - tailsize; 17047605971SRob Herring 17147605971SRob Herring /* Two cases to avoid clobbering data if the old and new 17247605971SRob Herring * buffers partially overlap */ 17347605971SRob Herring if (buf <= fdt) { 17447605971SRob Herring memmove(buf, fdt, headsize); 17547605971SRob Herring memmove(newtail, oldtail, tailsize); 17647605971SRob Herring } else { 17747605971SRob Herring memmove(newtail, oldtail, tailsize); 17847605971SRob Herring memmove(buf, fdt, headsize); 17947605971SRob Herring } 18047605971SRob Herring 18147605971SRob Herring fdt_set_totalsize(buf, bufsize); 182f858927fSRob Herring if (fdt_off_dt_strings(buf)) 183f858927fSRob Herring fdt_set_off_dt_strings(buf, bufsize); 18447605971SRob Herring 18547605971SRob Herring return 0; 18647605971SRob Herring } 18747605971SRob Herring 1889fffb55fSDavid Gibson int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) 1899fffb55fSDavid Gibson { 1909fffb55fSDavid Gibson struct fdt_reserve_entry *re; 1919fffb55fSDavid Gibson int offset; 1929fffb55fSDavid Gibson 193f858927fSRob Herring FDT_SW_PROBE_MEMRSV(fdt); 1949fffb55fSDavid Gibson 1959fffb55fSDavid Gibson offset = fdt_off_dt_struct(fdt); 1969fffb55fSDavid Gibson if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) 1979fffb55fSDavid Gibson return -FDT_ERR_NOSPACE; 1989fffb55fSDavid Gibson 1999fffb55fSDavid Gibson re = (struct fdt_reserve_entry *)((char *)fdt + offset); 2009fffb55fSDavid Gibson re->address = cpu_to_fdt64(addr); 2019fffb55fSDavid Gibson re->size = cpu_to_fdt64(size); 2029fffb55fSDavid Gibson 2039fffb55fSDavid Gibson fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); 2049fffb55fSDavid Gibson 2059fffb55fSDavid Gibson return 0; 2069fffb55fSDavid Gibson } 2079fffb55fSDavid Gibson 2089fffb55fSDavid Gibson int fdt_finish_reservemap(void *fdt) 2099fffb55fSDavid Gibson { 210f858927fSRob Herring int err = fdt_add_reservemap_entry(fdt, 0, 0); 211f858927fSRob Herring 212f858927fSRob Herring if (err) 213f858927fSRob Herring return err; 214f858927fSRob Herring 215f858927fSRob Herring fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt)); 216f858927fSRob Herring return 0; 2179fffb55fSDavid Gibson } 2189fffb55fSDavid Gibson 2199fffb55fSDavid Gibson int fdt_begin_node(void *fdt, const char *name) 2209fffb55fSDavid Gibson { 2219fffb55fSDavid Gibson struct fdt_node_header *nh; 222f858927fSRob Herring int namelen; 2239fffb55fSDavid Gibson 224f858927fSRob Herring FDT_SW_PROBE_STRUCT(fdt); 2259fffb55fSDavid Gibson 226f858927fSRob Herring namelen = strlen(name) + 1; 2279130ba88SRob Herring nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); 2289fffb55fSDavid Gibson if (! nh) 2299fffb55fSDavid Gibson return -FDT_ERR_NOSPACE; 2309fffb55fSDavid Gibson 2319fffb55fSDavid Gibson nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); 2329fffb55fSDavid Gibson memcpy(nh->name, name, namelen); 2339fffb55fSDavid Gibson return 0; 2349fffb55fSDavid Gibson } 2359fffb55fSDavid Gibson 2369fffb55fSDavid Gibson int fdt_end_node(void *fdt) 2379fffb55fSDavid Gibson { 23847605971SRob Herring fdt32_t *en; 2399fffb55fSDavid Gibson 240f858927fSRob Herring FDT_SW_PROBE_STRUCT(fdt); 2419fffb55fSDavid Gibson 2429130ba88SRob Herring en = fdt_grab_space_(fdt, FDT_TAGSIZE); 2439fffb55fSDavid Gibson if (! en) 2449fffb55fSDavid Gibson return -FDT_ERR_NOSPACE; 2459fffb55fSDavid Gibson 2469fffb55fSDavid Gibson *en = cpu_to_fdt32(FDT_END_NODE); 2479fffb55fSDavid Gibson return 0; 2489fffb55fSDavid Gibson } 2499fffb55fSDavid Gibson 2509bb9c6a1SRob Herring static int fdt_add_string_(void *fdt, const char *s) 2519fffb55fSDavid Gibson { 2529fffb55fSDavid Gibson char *strtab = (char *)fdt + fdt_totalsize(fdt); 253*6e9c9686SRob Herring unsigned int strtabsize = fdt_size_dt_strings(fdt); 254*6e9c9686SRob Herring unsigned int len = strlen(s) + 1; 255*6e9c9686SRob Herring unsigned int struct_top, offset; 2569fffb55fSDavid Gibson 257*6e9c9686SRob Herring offset = strtabsize + len; 2589fffb55fSDavid Gibson struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 259*6e9c9686SRob Herring if (fdt_totalsize(fdt) - offset < struct_top) 2609fffb55fSDavid Gibson return 0; /* no more room :( */ 2619fffb55fSDavid Gibson 262*6e9c9686SRob Herring memcpy(strtab - offset, s, len); 2639fffb55fSDavid Gibson fdt_set_size_dt_strings(fdt, strtabsize + len); 264*6e9c9686SRob Herring return -offset; 2659fffb55fSDavid Gibson } 2669fffb55fSDavid Gibson 2679bb9c6a1SRob Herring /* Must only be used to roll back in case of error */ 2689bb9c6a1SRob Herring static void fdt_del_last_string_(void *fdt, const char *s) 2699bb9c6a1SRob Herring { 2709bb9c6a1SRob Herring int strtabsize = fdt_size_dt_strings(fdt); 2719bb9c6a1SRob Herring int len = strlen(s) + 1; 2729bb9c6a1SRob Herring 2739bb9c6a1SRob Herring fdt_set_size_dt_strings(fdt, strtabsize - len); 2749bb9c6a1SRob Herring } 2759bb9c6a1SRob Herring 2769bb9c6a1SRob Herring static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) 2779bb9c6a1SRob Herring { 2789bb9c6a1SRob Herring char *strtab = (char *)fdt + fdt_totalsize(fdt); 2799bb9c6a1SRob Herring int strtabsize = fdt_size_dt_strings(fdt); 2809bb9c6a1SRob Herring const char *p; 2819bb9c6a1SRob Herring 2829bb9c6a1SRob Herring *allocated = 0; 2839bb9c6a1SRob Herring 2849bb9c6a1SRob Herring p = fdt_find_string_(strtab - strtabsize, strtabsize, s); 2859bb9c6a1SRob Herring if (p) 2869bb9c6a1SRob Herring return p - strtab; 2879bb9c6a1SRob Herring 2889bb9c6a1SRob Herring *allocated = 1; 2899bb9c6a1SRob Herring 2909bb9c6a1SRob Herring return fdt_add_string_(fdt, s); 2919bb9c6a1SRob Herring } 2929bb9c6a1SRob Herring 2934201d057SRob Herring int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) 2949fffb55fSDavid Gibson { 2959fffb55fSDavid Gibson struct fdt_property *prop; 2969fffb55fSDavid Gibson int nameoff; 2979bb9c6a1SRob Herring int allocated; 2989fffb55fSDavid Gibson 299f858927fSRob Herring FDT_SW_PROBE_STRUCT(fdt); 3009fffb55fSDavid Gibson 3019bb9c6a1SRob Herring /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */ 3029bb9c6a1SRob Herring if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) { 3039bb9c6a1SRob Herring allocated = 1; 3049bb9c6a1SRob Herring nameoff = fdt_add_string_(fdt, name); 3059bb9c6a1SRob Herring } else { 3069bb9c6a1SRob Herring nameoff = fdt_find_add_string_(fdt, name, &allocated); 3079bb9c6a1SRob Herring } 3089fffb55fSDavid Gibson if (nameoff == 0) 3099fffb55fSDavid Gibson return -FDT_ERR_NOSPACE; 3109fffb55fSDavid Gibson 3119130ba88SRob Herring prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); 3129bb9c6a1SRob Herring if (! prop) { 3139bb9c6a1SRob Herring if (allocated) 3149bb9c6a1SRob Herring fdt_del_last_string_(fdt, name); 3159fffb55fSDavid Gibson return -FDT_ERR_NOSPACE; 3169bb9c6a1SRob Herring } 3179fffb55fSDavid Gibson 3189fffb55fSDavid Gibson prop->tag = cpu_to_fdt32(FDT_PROP); 3199fffb55fSDavid Gibson prop->nameoff = cpu_to_fdt32(nameoff); 3209fffb55fSDavid Gibson prop->len = cpu_to_fdt32(len); 3214201d057SRob Herring *valp = prop->data; 3224201d057SRob Herring return 0; 3234201d057SRob Herring } 3244201d057SRob Herring 3254201d057SRob Herring int fdt_property(void *fdt, const char *name, const void *val, int len) 3264201d057SRob Herring { 3274201d057SRob Herring void *ptr; 3284201d057SRob Herring int ret; 3294201d057SRob Herring 3304201d057SRob Herring ret = fdt_property_placeholder(fdt, name, len, &ptr); 3314201d057SRob Herring if (ret) 3324201d057SRob Herring return ret; 3334201d057SRob Herring memcpy(ptr, val, len); 3349fffb55fSDavid Gibson return 0; 3359fffb55fSDavid Gibson } 3369fffb55fSDavid Gibson 3379fffb55fSDavid Gibson int fdt_finish(void *fdt) 3389fffb55fSDavid Gibson { 3399fffb55fSDavid Gibson char *p = (char *)fdt; 34047605971SRob Herring fdt32_t *end; 3419fffb55fSDavid Gibson int oldstroffset, newstroffset; 3429fffb55fSDavid Gibson uint32_t tag; 3439fffb55fSDavid Gibson int offset, nextoffset; 3449fffb55fSDavid Gibson 345f858927fSRob Herring FDT_SW_PROBE_STRUCT(fdt); 3469fffb55fSDavid Gibson 3479fffb55fSDavid Gibson /* Add terminator */ 3489130ba88SRob Herring end = fdt_grab_space_(fdt, sizeof(*end)); 3499fffb55fSDavid Gibson if (! end) 3509fffb55fSDavid Gibson return -FDT_ERR_NOSPACE; 3519fffb55fSDavid Gibson *end = cpu_to_fdt32(FDT_END); 3529fffb55fSDavid Gibson 3539fffb55fSDavid Gibson /* Relocate the string table */ 3549fffb55fSDavid Gibson oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); 3559fffb55fSDavid Gibson newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 3569fffb55fSDavid Gibson memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); 3579fffb55fSDavid Gibson fdt_set_off_dt_strings(fdt, newstroffset); 3589fffb55fSDavid Gibson 3599fffb55fSDavid Gibson /* Walk the structure, correcting string offsets */ 3609fffb55fSDavid Gibson offset = 0; 3619fffb55fSDavid Gibson while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { 3629fffb55fSDavid Gibson if (tag == FDT_PROP) { 3639fffb55fSDavid Gibson struct fdt_property *prop = 3649130ba88SRob Herring fdt_offset_ptr_w_(fdt, offset); 3659fffb55fSDavid Gibson int nameoff; 3669fffb55fSDavid Gibson 3679fffb55fSDavid Gibson nameoff = fdt32_to_cpu(prop->nameoff); 3689fffb55fSDavid Gibson nameoff += fdt_size_dt_strings(fdt); 3699fffb55fSDavid Gibson prop->nameoff = cpu_to_fdt32(nameoff); 3709fffb55fSDavid Gibson } 3719fffb55fSDavid Gibson offset = nextoffset; 3729fffb55fSDavid Gibson } 373cd296721SStephen Warren if (nextoffset < 0) 374cd296721SStephen Warren return nextoffset; 3759fffb55fSDavid Gibson 3769fffb55fSDavid Gibson /* Finally, adjust the header */ 3779fffb55fSDavid Gibson fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); 3789bb9c6a1SRob Herring 3799bb9c6a1SRob Herring /* And fix up fields that were keeping intermediate state. */ 3809bb9c6a1SRob Herring fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); 3819fffb55fSDavid Gibson fdt_set_magic(fdt, FDT_MAGIC); 3829bb9c6a1SRob Herring 3839fffb55fSDavid Gibson return 0; 3849fffb55fSDavid Gibson } 385