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 { 15*d047cd8aSRob 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; 20*d047cd8aSRob Herring } 21*d047cd8aSRob 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: 35f858927fSRob Herring * fdt_add_reservmap_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 44*d047cd8aSRob 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 70*d047cd8aSRob Herring if (!can_assume(VALID_INPUT) && 71*d047cd8aSRob 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 { 969fffb55fSDavid Gibson int offset = fdt_size_dt_struct(fdt); 979fffb55fSDavid Gibson 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 { 111f858927fSRob Herring const size_t 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 155f858927fSRob Herring headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 15647605971SRob Herring tailsize = fdt_size_dt_strings(fdt); 15747605971SRob Herring 158*d047cd8aSRob Herring if (!can_assume(VALID_DTB) && 159*d047cd8aSRob Herring headsize + tailsize > fdt_totalsize(fdt)) 160f858927fSRob Herring return -FDT_ERR_INTERNAL; 161f858927fSRob Herring 16247605971SRob Herring if ((headsize + tailsize) > bufsize) 16347605971SRob Herring return -FDT_ERR_NOSPACE; 16447605971SRob Herring 16547605971SRob Herring oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; 16647605971SRob Herring newtail = (char *)buf + bufsize - tailsize; 16747605971SRob Herring 16847605971SRob Herring /* Two cases to avoid clobbering data if the old and new 16947605971SRob Herring * buffers partially overlap */ 17047605971SRob Herring if (buf <= fdt) { 17147605971SRob Herring memmove(buf, fdt, headsize); 17247605971SRob Herring memmove(newtail, oldtail, tailsize); 17347605971SRob Herring } else { 17447605971SRob Herring memmove(newtail, oldtail, tailsize); 17547605971SRob Herring memmove(buf, fdt, headsize); 17647605971SRob Herring } 17747605971SRob Herring 17847605971SRob Herring fdt_set_totalsize(buf, bufsize); 179f858927fSRob Herring if (fdt_off_dt_strings(buf)) 180f858927fSRob Herring fdt_set_off_dt_strings(buf, bufsize); 18147605971SRob Herring 18247605971SRob Herring return 0; 18347605971SRob Herring } 18447605971SRob Herring 1859fffb55fSDavid Gibson int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) 1869fffb55fSDavid Gibson { 1879fffb55fSDavid Gibson struct fdt_reserve_entry *re; 1889fffb55fSDavid Gibson int offset; 1899fffb55fSDavid Gibson 190f858927fSRob Herring FDT_SW_PROBE_MEMRSV(fdt); 1919fffb55fSDavid Gibson 1929fffb55fSDavid Gibson offset = fdt_off_dt_struct(fdt); 1939fffb55fSDavid Gibson if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) 1949fffb55fSDavid Gibson return -FDT_ERR_NOSPACE; 1959fffb55fSDavid Gibson 1969fffb55fSDavid Gibson re = (struct fdt_reserve_entry *)((char *)fdt + offset); 1979fffb55fSDavid Gibson re->address = cpu_to_fdt64(addr); 1989fffb55fSDavid Gibson re->size = cpu_to_fdt64(size); 1999fffb55fSDavid Gibson 2009fffb55fSDavid Gibson fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); 2019fffb55fSDavid Gibson 2029fffb55fSDavid Gibson return 0; 2039fffb55fSDavid Gibson } 2049fffb55fSDavid Gibson 2059fffb55fSDavid Gibson int fdt_finish_reservemap(void *fdt) 2069fffb55fSDavid Gibson { 207f858927fSRob Herring int err = fdt_add_reservemap_entry(fdt, 0, 0); 208f858927fSRob Herring 209f858927fSRob Herring if (err) 210f858927fSRob Herring return err; 211f858927fSRob Herring 212f858927fSRob Herring fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt)); 213f858927fSRob Herring return 0; 2149fffb55fSDavid Gibson } 2159fffb55fSDavid Gibson 2169fffb55fSDavid Gibson int fdt_begin_node(void *fdt, const char *name) 2179fffb55fSDavid Gibson { 2189fffb55fSDavid Gibson struct fdt_node_header *nh; 219f858927fSRob Herring int namelen; 2209fffb55fSDavid Gibson 221f858927fSRob Herring FDT_SW_PROBE_STRUCT(fdt); 2229fffb55fSDavid Gibson 223f858927fSRob Herring namelen = strlen(name) + 1; 2249130ba88SRob Herring nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); 2259fffb55fSDavid Gibson if (! nh) 2269fffb55fSDavid Gibson return -FDT_ERR_NOSPACE; 2279fffb55fSDavid Gibson 2289fffb55fSDavid Gibson nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); 2299fffb55fSDavid Gibson memcpy(nh->name, name, namelen); 2309fffb55fSDavid Gibson return 0; 2319fffb55fSDavid Gibson } 2329fffb55fSDavid Gibson 2339fffb55fSDavid Gibson int fdt_end_node(void *fdt) 2349fffb55fSDavid Gibson { 23547605971SRob Herring fdt32_t *en; 2369fffb55fSDavid Gibson 237f858927fSRob Herring FDT_SW_PROBE_STRUCT(fdt); 2389fffb55fSDavid Gibson 2399130ba88SRob Herring en = fdt_grab_space_(fdt, FDT_TAGSIZE); 2409fffb55fSDavid Gibson if (! en) 2419fffb55fSDavid Gibson return -FDT_ERR_NOSPACE; 2429fffb55fSDavid Gibson 2439fffb55fSDavid Gibson *en = cpu_to_fdt32(FDT_END_NODE); 2449fffb55fSDavid Gibson return 0; 2459fffb55fSDavid Gibson } 2469fffb55fSDavid Gibson 2479bb9c6a1SRob Herring static int fdt_add_string_(void *fdt, const char *s) 2489fffb55fSDavid Gibson { 2499fffb55fSDavid Gibson char *strtab = (char *)fdt + fdt_totalsize(fdt); 2509fffb55fSDavid Gibson int strtabsize = fdt_size_dt_strings(fdt); 2519fffb55fSDavid Gibson int len = strlen(s) + 1; 2529fffb55fSDavid Gibson int struct_top, offset; 2539fffb55fSDavid Gibson 2549fffb55fSDavid Gibson offset = -strtabsize - len; 2559fffb55fSDavid Gibson struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 2569fffb55fSDavid Gibson if (fdt_totalsize(fdt) + offset < struct_top) 2579fffb55fSDavid Gibson return 0; /* no more room :( */ 2589fffb55fSDavid Gibson 2599fffb55fSDavid Gibson memcpy(strtab + offset, s, len); 2609fffb55fSDavid Gibson fdt_set_size_dt_strings(fdt, strtabsize + len); 2619fffb55fSDavid Gibson return offset; 2629fffb55fSDavid Gibson } 2639fffb55fSDavid Gibson 2649bb9c6a1SRob Herring /* Must only be used to roll back in case of error */ 2659bb9c6a1SRob Herring static void fdt_del_last_string_(void *fdt, const char *s) 2669bb9c6a1SRob Herring { 2679bb9c6a1SRob Herring int strtabsize = fdt_size_dt_strings(fdt); 2689bb9c6a1SRob Herring int len = strlen(s) + 1; 2699bb9c6a1SRob Herring 2709bb9c6a1SRob Herring fdt_set_size_dt_strings(fdt, strtabsize - len); 2719bb9c6a1SRob Herring } 2729bb9c6a1SRob Herring 2739bb9c6a1SRob Herring static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) 2749bb9c6a1SRob Herring { 2759bb9c6a1SRob Herring char *strtab = (char *)fdt + fdt_totalsize(fdt); 2769bb9c6a1SRob Herring int strtabsize = fdt_size_dt_strings(fdt); 2779bb9c6a1SRob Herring const char *p; 2789bb9c6a1SRob Herring 2799bb9c6a1SRob Herring *allocated = 0; 2809bb9c6a1SRob Herring 2819bb9c6a1SRob Herring p = fdt_find_string_(strtab - strtabsize, strtabsize, s); 2829bb9c6a1SRob Herring if (p) 2839bb9c6a1SRob Herring return p - strtab; 2849bb9c6a1SRob Herring 2859bb9c6a1SRob Herring *allocated = 1; 2869bb9c6a1SRob Herring 2879bb9c6a1SRob Herring return fdt_add_string_(fdt, s); 2889bb9c6a1SRob Herring } 2899bb9c6a1SRob Herring 2904201d057SRob Herring int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) 2919fffb55fSDavid Gibson { 2929fffb55fSDavid Gibson struct fdt_property *prop; 2939fffb55fSDavid Gibson int nameoff; 2949bb9c6a1SRob Herring int allocated; 2959fffb55fSDavid Gibson 296f858927fSRob Herring FDT_SW_PROBE_STRUCT(fdt); 2979fffb55fSDavid Gibson 2989bb9c6a1SRob Herring /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */ 2999bb9c6a1SRob Herring if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) { 3009bb9c6a1SRob Herring allocated = 1; 3019bb9c6a1SRob Herring nameoff = fdt_add_string_(fdt, name); 3029bb9c6a1SRob Herring } else { 3039bb9c6a1SRob Herring nameoff = fdt_find_add_string_(fdt, name, &allocated); 3049bb9c6a1SRob Herring } 3059fffb55fSDavid Gibson if (nameoff == 0) 3069fffb55fSDavid Gibson return -FDT_ERR_NOSPACE; 3079fffb55fSDavid Gibson 3089130ba88SRob Herring prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); 3099bb9c6a1SRob Herring if (! prop) { 3109bb9c6a1SRob Herring if (allocated) 3119bb9c6a1SRob Herring fdt_del_last_string_(fdt, name); 3129fffb55fSDavid Gibson return -FDT_ERR_NOSPACE; 3139bb9c6a1SRob Herring } 3149fffb55fSDavid Gibson 3159fffb55fSDavid Gibson prop->tag = cpu_to_fdt32(FDT_PROP); 3169fffb55fSDavid Gibson prop->nameoff = cpu_to_fdt32(nameoff); 3179fffb55fSDavid Gibson prop->len = cpu_to_fdt32(len); 3184201d057SRob Herring *valp = prop->data; 3194201d057SRob Herring return 0; 3204201d057SRob Herring } 3214201d057SRob Herring 3224201d057SRob Herring int fdt_property(void *fdt, const char *name, const void *val, int len) 3234201d057SRob Herring { 3244201d057SRob Herring void *ptr; 3254201d057SRob Herring int ret; 3264201d057SRob Herring 3274201d057SRob Herring ret = fdt_property_placeholder(fdt, name, len, &ptr); 3284201d057SRob Herring if (ret) 3294201d057SRob Herring return ret; 3304201d057SRob Herring memcpy(ptr, val, len); 3319fffb55fSDavid Gibson return 0; 3329fffb55fSDavid Gibson } 3339fffb55fSDavid Gibson 3349fffb55fSDavid Gibson int fdt_finish(void *fdt) 3359fffb55fSDavid Gibson { 3369fffb55fSDavid Gibson char *p = (char *)fdt; 33747605971SRob Herring fdt32_t *end; 3389fffb55fSDavid Gibson int oldstroffset, newstroffset; 3399fffb55fSDavid Gibson uint32_t tag; 3409fffb55fSDavid Gibson int offset, nextoffset; 3419fffb55fSDavid Gibson 342f858927fSRob Herring FDT_SW_PROBE_STRUCT(fdt); 3439fffb55fSDavid Gibson 3449fffb55fSDavid Gibson /* Add terminator */ 3459130ba88SRob Herring end = fdt_grab_space_(fdt, sizeof(*end)); 3469fffb55fSDavid Gibson if (! end) 3479fffb55fSDavid Gibson return -FDT_ERR_NOSPACE; 3489fffb55fSDavid Gibson *end = cpu_to_fdt32(FDT_END); 3499fffb55fSDavid Gibson 3509fffb55fSDavid Gibson /* Relocate the string table */ 3519fffb55fSDavid Gibson oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); 3529fffb55fSDavid Gibson newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 3539fffb55fSDavid Gibson memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); 3549fffb55fSDavid Gibson fdt_set_off_dt_strings(fdt, newstroffset); 3559fffb55fSDavid Gibson 3569fffb55fSDavid Gibson /* Walk the structure, correcting string offsets */ 3579fffb55fSDavid Gibson offset = 0; 3589fffb55fSDavid Gibson while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { 3599fffb55fSDavid Gibson if (tag == FDT_PROP) { 3609fffb55fSDavid Gibson struct fdt_property *prop = 3619130ba88SRob Herring fdt_offset_ptr_w_(fdt, offset); 3629fffb55fSDavid Gibson int nameoff; 3639fffb55fSDavid Gibson 3649fffb55fSDavid Gibson nameoff = fdt32_to_cpu(prop->nameoff); 3659fffb55fSDavid Gibson nameoff += fdt_size_dt_strings(fdt); 3669fffb55fSDavid Gibson prop->nameoff = cpu_to_fdt32(nameoff); 3679fffb55fSDavid Gibson } 3689fffb55fSDavid Gibson offset = nextoffset; 3699fffb55fSDavid Gibson } 370cd296721SStephen Warren if (nextoffset < 0) 371cd296721SStephen Warren return nextoffset; 3729fffb55fSDavid Gibson 3739fffb55fSDavid Gibson /* Finally, adjust the header */ 3749fffb55fSDavid Gibson fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); 3759bb9c6a1SRob Herring 3769bb9c6a1SRob Herring /* And fix up fields that were keeping intermediate state. */ 3779bb9c6a1SRob Herring fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); 3789fffb55fSDavid Gibson fdt_set_magic(fdt, FDT_MAGIC); 3799bb9c6a1SRob Herring 3809fffb55fSDavid Gibson return 0; 3819fffb55fSDavid Gibson } 382