0001
0002
0003
0004
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
0033
0034
0035
0036
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
0057
0058
0059
0060
0061
0062
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
0086 return fdt_last_comp_version(fdt);
0087 }
0088
0089
0090
0091
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
0125
0126
0127
0128
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
0172
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;
0261
0262 memcpy(strtab - offset, s, len);
0263 fdt_set_size_dt_strings(fdt, strtabsize + len);
0264 return -offset;
0265 }
0266
0267
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
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
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
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
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
0377 fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
0378
0379
0380 fdt_set_last_comp_version(fdt, FDT_LAST_COMPATIBLE_VERSION);
0381 fdt_set_magic(fdt, FDT_MAGIC);
0382
0383 return 0;
0384 }