Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
0002 /*
0003  * libfdt - Flat Device Tree manipulation
0004  * Copyright (C) 2006 David Gibson, IBM Corporation.
0005  */
0006 #include "libfdt_env.h"
0007 
0008 #include <fdt.h>
0009 #include <libfdt.h>
0010 
0011 #include "libfdt_internal.h"
0012 
0013 static int fdt_sw_probe_(void *fdt)
0014 {
0015     if (!can_assume(VALID_INPUT)) {
0016         if (fdt_magic(fdt) == FDT_MAGIC)
0017             return -FDT_ERR_BADSTATE;
0018         else if (fdt_magic(fdt) != FDT_SW_MAGIC)
0019             return -FDT_ERR_BADMAGIC;
0020     }
0021 
0022     return 0;
0023 }
0024 
0025 #define FDT_SW_PROBE(fdt) \
0026     { \
0027         int err; \
0028         if ((err = fdt_sw_probe_(fdt)) != 0) \
0029             return err; \
0030     }
0031 
0032 /* 'memrsv' state:  Initial state after fdt_create()
0033  *
0034  * Allowed functions:
0035  *  fdt_add_reservemap_entry()
0036  *  fdt_finish_reservemap()     [moves to 'struct' state]
0037  */
0038 static int fdt_sw_probe_memrsv_(void *fdt)
0039 {
0040     int err = fdt_sw_probe_(fdt);
0041     if (err)
0042         return err;
0043 
0044     if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0)
0045         return -FDT_ERR_BADSTATE;
0046     return 0;
0047 }
0048 
0049 #define FDT_SW_PROBE_MEMRSV(fdt) \
0050     { \
0051         int err; \
0052         if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \
0053             return err; \
0054     }
0055 
0056 /* 'struct' state:  Enter this state after fdt_finish_reservemap()
0057  *
0058  * Allowed functions:
0059  *  fdt_begin_node()
0060  *  fdt_end_node()
0061  *  fdt_property*()
0062  *  fdt_finish()            [moves to 'complete' state]
0063  */
0064 static int fdt_sw_probe_struct_(void *fdt)
0065 {
0066     int err = fdt_sw_probe_(fdt);
0067     if (err)
0068         return err;
0069 
0070     if (!can_assume(VALID_INPUT) &&
0071         fdt_off_dt_strings(fdt) != fdt_totalsize(fdt))
0072         return -FDT_ERR_BADSTATE;
0073     return 0;
0074 }
0075 
0076 #define FDT_SW_PROBE_STRUCT(fdt) \
0077     { \
0078         int err; \
0079         if ((err = fdt_sw_probe_struct_(fdt)) != 0) \
0080             return err; \
0081     }
0082 
0083 static inline uint32_t sw_flags(void *fdt)
0084 {
0085     /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */
0086     return fdt_last_comp_version(fdt);
0087 }
0088 
0089 /* 'complete' state:    Enter this state after fdt_finish()
0090  *
0091  * Allowed functions: none
0092  */
0093 
0094 static void *fdt_grab_space_(void *fdt, size_t len)
0095 {
0096     unsigned int offset = fdt_size_dt_struct(fdt);
0097     unsigned int spaceleft;
0098 
0099     spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
0100         - fdt_size_dt_strings(fdt);
0101 
0102     if ((offset + len < offset) || (offset + len > spaceleft))
0103         return NULL;
0104 
0105     fdt_set_size_dt_struct(fdt, offset + len);
0106     return fdt_offset_ptr_w_(fdt, offset);
0107 }
0108 
0109 int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags)
0110 {
0111     const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header),
0112                       sizeof(struct fdt_reserve_entry));
0113     void *fdt = buf;
0114 
0115     if (bufsize < hdrsize)
0116         return -FDT_ERR_NOSPACE;
0117 
0118     if (flags & ~FDT_CREATE_FLAGS_ALL)
0119         return -FDT_ERR_BADFLAGS;
0120 
0121     memset(buf, 0, bufsize);
0122 
0123     /*
0124      * magic and last_comp_version keep intermediate state during the fdt
0125      * creation process, which is replaced with the proper FDT format by
0126      * fdt_finish().
0127      *
0128      * flags should be accessed with sw_flags().
0129      */
0130     fdt_set_magic(fdt, FDT_SW_MAGIC);
0131     fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
0132     fdt_set_last_comp_version(fdt, flags);
0133 
0134     fdt_set_totalsize(fdt,  bufsize);
0135 
0136     fdt_set_off_mem_rsvmap(fdt, hdrsize);
0137     fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
0138     fdt_set_off_dt_strings(fdt, 0);
0139 
0140     return 0;
0141 }
0142 
0143 int fdt_create(void *buf, int bufsize)
0144 {
0145     return fdt_create_with_flags(buf, bufsize, 0);
0146 }
0147 
0148 int fdt_resize(void *fdt, void *buf, int bufsize)
0149 {
0150     size_t headsize, tailsize;
0151     char *oldtail, *newtail;
0152 
0153     FDT_SW_PROBE(fdt);
0154 
0155     if (bufsize < 0)
0156         return -FDT_ERR_NOSPACE;
0157 
0158     headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
0159     tailsize = fdt_size_dt_strings(fdt);
0160 
0161     if (!can_assume(VALID_DTB) &&
0162         headsize + tailsize > fdt_totalsize(fdt))
0163         return -FDT_ERR_INTERNAL;
0164 
0165     if ((headsize + tailsize) > (unsigned)bufsize)
0166         return -FDT_ERR_NOSPACE;
0167 
0168     oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
0169     newtail = (char *)buf + bufsize - tailsize;
0170 
0171     /* Two cases to avoid clobbering data if the old and new
0172      * buffers partially overlap */
0173     if (buf <= fdt) {
0174         memmove(buf, fdt, headsize);
0175         memmove(newtail, oldtail, tailsize);
0176     } else {
0177         memmove(newtail, oldtail, tailsize);
0178         memmove(buf, fdt, headsize);
0179     }
0180 
0181     fdt_set_totalsize(buf, bufsize);
0182     if (fdt_off_dt_strings(buf))
0183         fdt_set_off_dt_strings(buf, bufsize);
0184 
0185     return 0;
0186 }
0187 
0188 int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
0189 {
0190     struct fdt_reserve_entry *re;
0191     int offset;
0192 
0193     FDT_SW_PROBE_MEMRSV(fdt);
0194 
0195     offset = fdt_off_dt_struct(fdt);
0196     if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
0197         return -FDT_ERR_NOSPACE;
0198 
0199     re = (struct fdt_reserve_entry *)((char *)fdt + offset);
0200     re->address = cpu_to_fdt64(addr);
0201     re->size = cpu_to_fdt64(size);
0202 
0203     fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
0204 
0205     return 0;
0206 }
0207 
0208 int fdt_finish_reservemap(void *fdt)
0209 {
0210     int err = fdt_add_reservemap_entry(fdt, 0, 0);
0211 
0212     if (err)
0213         return err;
0214 
0215     fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt));
0216     return 0;
0217 }
0218 
0219 int fdt_begin_node(void *fdt, const char *name)
0220 {
0221     struct fdt_node_header *nh;
0222     int namelen;
0223 
0224     FDT_SW_PROBE_STRUCT(fdt);
0225 
0226     namelen = strlen(name) + 1;
0227     nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
0228     if (! nh)
0229         return -FDT_ERR_NOSPACE;
0230 
0231     nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
0232     memcpy(nh->name, name, namelen);
0233     return 0;
0234 }
0235 
0236 int fdt_end_node(void *fdt)
0237 {
0238     fdt32_t *en;
0239 
0240     FDT_SW_PROBE_STRUCT(fdt);
0241 
0242     en = fdt_grab_space_(fdt, FDT_TAGSIZE);
0243     if (! en)
0244         return -FDT_ERR_NOSPACE;
0245 
0246     *en = cpu_to_fdt32(FDT_END_NODE);
0247     return 0;
0248 }
0249 
0250 static int fdt_add_string_(void *fdt, const char *s)
0251 {
0252     char *strtab = (char *)fdt + fdt_totalsize(fdt);
0253     unsigned int strtabsize = fdt_size_dt_strings(fdt);
0254     unsigned int len = strlen(s) + 1;
0255     unsigned int struct_top, offset;
0256 
0257     offset = strtabsize + len;
0258     struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
0259     if (fdt_totalsize(fdt) - offset < struct_top)
0260         return 0; /* no more room :( */
0261 
0262     memcpy(strtab - offset, s, len);
0263     fdt_set_size_dt_strings(fdt, strtabsize + len);
0264     return -offset;
0265 }
0266 
0267 /* Must only be used to roll back in case of error */
0268 static void fdt_del_last_string_(void *fdt, const char *s)
0269 {
0270     int strtabsize = fdt_size_dt_strings(fdt);
0271     int len = strlen(s) + 1;
0272 
0273     fdt_set_size_dt_strings(fdt, strtabsize - len);
0274 }
0275 
0276 static int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
0277 {
0278     char *strtab = (char *)fdt + fdt_totalsize(fdt);
0279     int strtabsize = fdt_size_dt_strings(fdt);
0280     const char *p;
0281 
0282     *allocated = 0;
0283 
0284     p = fdt_find_string_(strtab - strtabsize, strtabsize, s);
0285     if (p)
0286         return p - strtab;
0287 
0288     *allocated = 1;
0289 
0290     return fdt_add_string_(fdt, s);
0291 }
0292 
0293 int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp)
0294 {
0295     struct fdt_property *prop;
0296     int nameoff;
0297     int allocated;
0298 
0299     FDT_SW_PROBE_STRUCT(fdt);
0300 
0301     /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */
0302     if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) {
0303         allocated = 1;
0304         nameoff = fdt_add_string_(fdt, name);
0305     } else {
0306         nameoff = fdt_find_add_string_(fdt, name, &allocated);
0307     }
0308     if (nameoff == 0)
0309         return -FDT_ERR_NOSPACE;
0310 
0311     prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
0312     if (! prop) {
0313         if (allocated)
0314             fdt_del_last_string_(fdt, name);
0315         return -FDT_ERR_NOSPACE;
0316     }
0317 
0318     prop->tag = cpu_to_fdt32(FDT_PROP);
0319     prop->nameoff = cpu_to_fdt32(nameoff);
0320     prop->len = cpu_to_fdt32(len);
0321     *valp = prop->data;
0322     return 0;
0323 }
0324 
0325 int fdt_property(void *fdt, const char *name, const void *val, int len)
0326 {
0327     void *ptr;
0328     int ret;
0329 
0330     ret = fdt_property_placeholder(fdt, name, len, &ptr);
0331     if (ret)
0332         return ret;
0333     memcpy(ptr, val, len);
0334     return 0;
0335 }
0336 
0337 int fdt_finish(void *fdt)
0338 {
0339     char *p = (char *)fdt;
0340     fdt32_t *end;
0341     int oldstroffset, newstroffset;
0342     uint32_t tag;
0343     int offset, nextoffset;
0344 
0345     FDT_SW_PROBE_STRUCT(fdt);
0346 
0347     /* Add terminator */
0348     end = fdt_grab_space_(fdt, sizeof(*end));
0349     if (! end)
0350         return -FDT_ERR_NOSPACE;
0351     *end = cpu_to_fdt32(FDT_END);
0352 
0353     /* Relocate the string table */
0354     oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
0355     newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
0356     memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
0357     fdt_set_off_dt_strings(fdt, newstroffset);
0358 
0359     /* Walk the structure, correcting string offsets */
0360     offset = 0;
0361     while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
0362         if (tag == FDT_PROP) {
0363             struct fdt_property *prop =
0364                 fdt_offset_ptr_w_(fdt, offset);
0365             int nameoff;
0366 
0367             nameoff = fdt32_to_cpu(prop->nameoff);
0368             nameoff += fdt_size_dt_strings(fdt);
0369             prop->nameoff = cpu_to_fdt32(nameoff);
0370         }
0371         offset = nextoffset;
0372     }
0373     if (nextoffset < 0)
0374         return nextoffset;
0375 
0376     /* Finally, adjust the header */
0377     fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
0378 
0379     /* And fix up fields that were keeping intermediate state. */
0380     fdt_set_last_comp_version(fdt, FDT_LAST_COMPATIBLE_VERSION);
0381     fdt_set_magic(fdt, FDT_MAGIC);
0382 
0383     return 0;
0384 }