]> git.proxmox.com Git - mirror_edk2.git/blame - EmbeddedPkg/Library/FdtLib/fdt_rw.c
EmbeddedPkg/FdtLib: Update FdtLib to v1.4.5
[mirror_edk2.git] / EmbeddedPkg / Library / FdtLib / fdt_rw.c
CommitLineData
1e57a462 1/*\r
2 * libfdt - Flat Device Tree manipulation\r
3 * Copyright (C) 2006 David Gibson, IBM Corporation.\r
4 *\r
5 * libfdt is dual licensed: you can use it either under the terms of\r
6 * the GPL, or the BSD license, at your option.\r
7 *\r
8 * a) This library is free software; you can redistribute it and/or\r
9 * modify it under the terms of the GNU General Public License as\r
10 * published by the Free Software Foundation; either version 2 of the\r
11 * License, or (at your option) any later version.\r
12 *\r
13 * This library is distributed in the hope that it will be useful,\r
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
16 * GNU General Public License for more details.\r
17 *\r
18 * You should have received a copy of the GNU General Public\r
19 * License along with this library; if not, write to the Free\r
20 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,\r
21 * MA 02110-1301 USA\r
22 *\r
23 * Alternatively,\r
24 *\r
25 * b) Redistribution and use in source and binary forms, with or\r
26 * without modification, are permitted provided that the following\r
27 * conditions are met:\r
28 *\r
29 * 1. Redistributions of source code must retain the above\r
30 * copyright notice, this list of conditions and the following\r
31 * disclaimer.\r
32 * 2. Redistributions in binary form must reproduce the above\r
33 * copyright notice, this list of conditions and the following\r
34 * disclaimer in the documentation and/or other materials\r
35 * provided with the distribution.\r
36 *\r
37 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\r
38 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,\r
39 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
40 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
41 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\r
42 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
43 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\r
44 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
45 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
47 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
48 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\r
49 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
50 */\r
51#include "libfdt_env.h"\r
52\r
53#include <fdt.h>\r
54#include <libfdt.h>\r
55\r
56#include "libfdt_internal.h"\r
57\r
58static int _fdt_blocks_misordered(const void *fdt,\r
59 int mem_rsv_size, int struct_size)\r
60{\r
61 return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8))\r
62 || (fdt_off_dt_struct(fdt) <\r
63 (fdt_off_mem_rsvmap(fdt) + mem_rsv_size))\r
64 || (fdt_off_dt_strings(fdt) <\r
65 (fdt_off_dt_struct(fdt) + struct_size))\r
66 || (fdt_totalsize(fdt) <\r
67 (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));\r
68}\r
69\r
70static int _fdt_rw_check_header(void *fdt)\r
71{\r
72 FDT_CHECK_HEADER(fdt);\r
73\r
74 if (fdt_version(fdt) < 17)\r
75 return -FDT_ERR_BADVERSION;\r
76 if (_fdt_blocks_misordered(fdt, sizeof(struct fdt_reserve_entry),\r
77 fdt_size_dt_struct(fdt)))\r
78 return -FDT_ERR_BADLAYOUT;\r
79 if (fdt_version(fdt) > 17)\r
80 fdt_set_version(fdt, 17);\r
81\r
82 return 0;\r
83}\r
84\r
85#define FDT_RW_CHECK_HEADER(fdt) \\r
86 { \\r
a0992390
PB
87 int __err; \\r
88 if ((__err = _fdt_rw_check_header(fdt)) != 0) \\r
89 return __err; \\r
1e57a462 90 }\r
91\r
92static inline int _fdt_data_size(void *fdt)\r
93{\r
94 return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);\r
95}\r
96\r
97static int _fdt_splice(void *fdt, void *splicepoint, int oldlen, int newlen)\r
98{\r
99 char *p = splicepoint;\r
100 char *end = (char *)fdt + _fdt_data_size(fdt);\r
101\r
102 if (((p + oldlen) < p) || ((p + oldlen) > end))\r
103 return -FDT_ERR_BADOFFSET;\r
a0992390
PB
104 if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt))\r
105 return -FDT_ERR_BADOFFSET;\r
1e57a462 106 if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt)))\r
107 return -FDT_ERR_NOSPACE;\r
108 memmove(p + newlen, p + oldlen, end - p - oldlen);\r
109 return 0;\r
110}\r
111\r
112static int _fdt_splice_mem_rsv(void *fdt, struct fdt_reserve_entry *p,\r
113 int oldn, int newn)\r
114{\r
115 int delta = (newn - oldn) * sizeof(*p);\r
116 int err;\r
117 err = _fdt_splice(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));\r
118 if (err)\r
119 return err;\r
120 fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);\r
121 fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);\r
122 return 0;\r
123}\r
124\r
125static int _fdt_splice_struct(void *fdt, void *p,\r
126 int oldlen, int newlen)\r
127{\r
128 int delta = newlen - oldlen;\r
129 int err;\r
130\r
a0992390 131 if ((err = _fdt_splice(fdt, p, oldlen, newlen)))\r
1e57a462 132 return err;\r
133\r
134 fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);\r
135 fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);\r
136 return 0;\r
137}\r
138\r
139static int _fdt_splice_string(void *fdt, int newlen)\r
140{\r
141 void *p = (char *)fdt\r
142 + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);\r
143 int err;\r
144\r
a0992390 145 if ((err = _fdt_splice(fdt, p, 0, newlen)))\r
1e57a462 146 return err;\r
147\r
148 fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);\r
149 return 0;\r
150}\r
151\r
152static int _fdt_find_add_string(void *fdt, const char *s)\r
153{\r
154 char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);\r
155 const char *p;\r
156 char *new;\r
157 int len = strlen(s) + 1;\r
158 int err;\r
159\r
160 p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s);\r
161 if (p)\r
162 /* found it */\r
163 return (p - strtab);\r
164\r
165 new = strtab + fdt_size_dt_strings(fdt);\r
166 err = _fdt_splice_string(fdt, len);\r
167 if (err)\r
168 return err;\r
169\r
170 memcpy(new, s, len);\r
171 return (new - strtab);\r
172}\r
173\r
174int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)\r
175{\r
176 struct fdt_reserve_entry *re;\r
177 int err;\r
178\r
179 FDT_RW_CHECK_HEADER(fdt);\r
180\r
181 re = _fdt_mem_rsv_w(fdt, fdt_num_mem_rsv(fdt));\r
182 err = _fdt_splice_mem_rsv(fdt, re, 0, 1);\r
183 if (err)\r
184 return err;\r
185\r
186 re->address = cpu_to_fdt64(address);\r
187 re->size = cpu_to_fdt64(size);\r
188 return 0;\r
189}\r
190\r
191int fdt_del_mem_rsv(void *fdt, int n)\r
192{\r
193 struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n);\r
1e57a462 194\r
195 FDT_RW_CHECK_HEADER(fdt);\r
196\r
197 if (n >= fdt_num_mem_rsv(fdt))\r
198 return -FDT_ERR_NOTFOUND;\r
199\r
a0992390 200 return _fdt_splice_mem_rsv(fdt, re, 1, 0);\r
1e57a462 201}\r
202\r
203static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name,\r
204 int len, struct fdt_property **prop)\r
205{\r
206 int oldlen;\r
207 int err;\r
208\r
209 *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);\r
a0992390 210 if (!*prop)\r
1e57a462 211 return oldlen;\r
212\r
a0992390
PB
213 if ((err = _fdt_splice_struct(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),\r
214 FDT_TAGALIGN(len))))\r
1e57a462 215 return err;\r
216\r
217 (*prop)->len = cpu_to_fdt32(len);\r
218 return 0;\r
219}\r
220\r
221static int _fdt_add_property(void *fdt, int nodeoffset, const char *name,\r
222 int len, struct fdt_property **prop)\r
223{\r
224 int proplen;\r
225 int nextoffset;\r
226 int namestroff;\r
227 int err;\r
228\r
229 if ((nextoffset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)\r
230 return nextoffset;\r
231\r
232 namestroff = _fdt_find_add_string(fdt, name);\r
233 if (namestroff < 0)\r
234 return namestroff;\r
235\r
236 *prop = _fdt_offset_ptr_w(fdt, nextoffset);\r
237 proplen = sizeof(**prop) + FDT_TAGALIGN(len);\r
238\r
239 err = _fdt_splice_struct(fdt, *prop, 0, proplen);\r
240 if (err)\r
241 return err;\r
242\r
243 (*prop)->tag = cpu_to_fdt32(FDT_PROP);\r
244 (*prop)->nameoff = cpu_to_fdt32(namestroff);\r
245 (*prop)->len = cpu_to_fdt32(len);\r
246 return 0;\r
247}\r
248\r
249int fdt_set_name(void *fdt, int nodeoffset, const char *name)\r
250{\r
251 char *namep;\r
252 int oldlen, newlen;\r
253 int err;\r
254\r
255 FDT_RW_CHECK_HEADER(fdt);\r
256\r
257 namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);\r
258 if (!namep)\r
259 return oldlen;\r
260\r
261 newlen = strlen(name);\r
262\r
263 err = _fdt_splice_struct(fdt, namep, FDT_TAGALIGN(oldlen+1),\r
264 FDT_TAGALIGN(newlen+1));\r
265 if (err)\r
266 return err;\r
267\r
268 memcpy(namep, name, newlen+1);\r
269 return 0;\r
270}\r
271\r
a0992390
PB
272int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name,\r
273 int len, void **prop_data)\r
1e57a462 274{\r
275 struct fdt_property *prop;\r
276 int err;\r
277\r
278 FDT_RW_CHECK_HEADER(fdt);\r
279\r
280 err = _fdt_resize_property(fdt, nodeoffset, name, len, &prop);\r
281 if (err == -FDT_ERR_NOTFOUND)\r
282 err = _fdt_add_property(fdt, nodeoffset, name, len, &prop);\r
283 if (err)\r
284 return err;\r
285\r
a0992390
PB
286 *prop_data = prop->data;\r
287 return 0;\r
288}\r
289\r
290int fdt_setprop(void *fdt, int nodeoffset, const char *name,\r
291 const void *val, int len)\r
292{\r
293 void *prop_data;\r
294 int err;\r
295\r
296 err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data);\r
297 if (err)\r
298 return err;\r
299\r
300 if (len)\r
301 memcpy(prop_data, val, len);\r
1e57a462 302 return 0;\r
303}\r
304\r
305int fdt_appendprop(void *fdt, int nodeoffset, const char *name,\r
306 const void *val, int len)\r
307{\r
308 struct fdt_property *prop;\r
309 int err, oldlen, newlen;\r
310\r
311 FDT_RW_CHECK_HEADER(fdt);\r
312\r
313 prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);\r
314 if (prop) {\r
315 newlen = len + oldlen;\r
316 err = _fdt_splice_struct(fdt, prop->data,\r
317 FDT_TAGALIGN(oldlen),\r
318 FDT_TAGALIGN(newlen));\r
319 if (err)\r
320 return err;\r
321 prop->len = cpu_to_fdt32(newlen);\r
322 memcpy(prop->data + oldlen, val, len);\r
323 } else {\r
324 err = _fdt_add_property(fdt, nodeoffset, name, len, &prop);\r
325 if (err)\r
326 return err;\r
327 memcpy(prop->data, val, len);\r
328 }\r
329 return 0;\r
330}\r
331\r
332int fdt_delprop(void *fdt, int nodeoffset, const char *name)\r
333{\r
334 struct fdt_property *prop;\r
335 int len, proplen;\r
336\r
337 FDT_RW_CHECK_HEADER(fdt);\r
338\r
339 prop = fdt_get_property_w(fdt, nodeoffset, name, &len);\r
a0992390 340 if (!prop)\r
1e57a462 341 return len;\r
342\r
343 proplen = sizeof(*prop) + FDT_TAGALIGN(len);\r
344 return _fdt_splice_struct(fdt, prop, proplen, 0);\r
345}\r
346\r
347int fdt_add_subnode_namelen(void *fdt, int parentoffset,\r
348 const char *name, int namelen)\r
349{\r
350 struct fdt_node_header *nh;\r
351 int offset, nextoffset;\r
352 int nodelen;\r
353 int err;\r
354 uint32_t tag;\r
3e8576dd 355 fdt32_t *endtag;\r
1e57a462 356\r
357 FDT_RW_CHECK_HEADER(fdt);\r
358\r
359 offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);\r
360 if (offset >= 0)\r
361 return -FDT_ERR_EXISTS;\r
362 else if (offset != -FDT_ERR_NOTFOUND)\r
363 return offset;\r
364\r
365 /* Try to place the new node after the parent's properties */\r
366 fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */\r
367 do {\r
368 offset = nextoffset;\r
369 tag = fdt_next_tag(fdt, offset, &nextoffset);\r
370 } while ((tag == FDT_PROP) || (tag == FDT_NOP));\r
371\r
372 nh = _fdt_offset_ptr_w(fdt, offset);\r
373 nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE;\r
374\r
375 err = _fdt_splice_struct(fdt, nh, 0, nodelen);\r
376 if (err)\r
377 return err;\r
378\r
379 nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);\r
380 memset(nh->name, 0, FDT_TAGALIGN(namelen+1));\r
381 memcpy(nh->name, name, namelen);\r
3e8576dd 382 endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE);\r
1e57a462 383 *endtag = cpu_to_fdt32(FDT_END_NODE);\r
384\r
385 return offset;\r
386}\r
387\r
388int fdt_add_subnode(void *fdt, int parentoffset, const char *name)\r
389{\r
390 return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));\r
391}\r
392\r
393int fdt_del_node(void *fdt, int nodeoffset)\r
394{\r
395 int endoffset;\r
396\r
397 FDT_RW_CHECK_HEADER(fdt);\r
398\r
399 endoffset = _fdt_node_end_offset(fdt, nodeoffset);\r
400 if (endoffset < 0)\r
401 return endoffset;\r
402\r
403 return _fdt_splice_struct(fdt, _fdt_offset_ptr_w(fdt, nodeoffset),\r
404 endoffset - nodeoffset, 0);\r
405}\r
406\r
407static void _fdt_packblocks(const char *old, char *new,\r
408 int mem_rsv_size, int struct_size)\r
409{\r
410 int mem_rsv_off, struct_off, strings_off;\r
411\r
412 mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);\r
413 struct_off = mem_rsv_off + mem_rsv_size;\r
414 strings_off = struct_off + struct_size;\r
415\r
416 memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);\r
417 fdt_set_off_mem_rsvmap(new, mem_rsv_off);\r
418\r
419 memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);\r
420 fdt_set_off_dt_struct(new, struct_off);\r
421 fdt_set_size_dt_struct(new, struct_size);\r
422\r
423 memmove(new + strings_off, old + fdt_off_dt_strings(old),\r
424 fdt_size_dt_strings(old));\r
425 fdt_set_off_dt_strings(new, strings_off);\r
426 fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));\r
427}\r
428\r
429int fdt_open_into(const void *fdt, void *buf, int bufsize)\r
430{\r
431 int err;\r
432 int mem_rsv_size, struct_size;\r
433 int newsize;\r
434 const char *fdtstart = fdt;\r
435 const char *fdtend = fdtstart + fdt_totalsize(fdt);\r
436 char *tmp;\r
437\r
438 FDT_CHECK_HEADER(fdt);\r
439\r
440 mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)\r
441 * sizeof(struct fdt_reserve_entry);\r
442\r
443 if (fdt_version(fdt) >= 17) {\r
444 struct_size = fdt_size_dt_struct(fdt);\r
445 } else {\r
446 struct_size = 0;\r
447 while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)\r
448 ;\r
449 if (struct_size < 0)\r
450 return struct_size;\r
451 }\r
452\r
453 if (!_fdt_blocks_misordered(fdt, mem_rsv_size, struct_size)) {\r
454 /* no further work necessary */\r
455 err = fdt_move(fdt, buf, bufsize);\r
456 if (err)\r
457 return err;\r
458 fdt_set_version(buf, 17);\r
459 fdt_set_size_dt_struct(buf, struct_size);\r
460 fdt_set_totalsize(buf, bufsize);\r
461 return 0;\r
462 }\r
463\r
464 /* Need to reorder */\r
465 newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size\r
466 + struct_size + fdt_size_dt_strings(fdt);\r
467\r
468 if (bufsize < newsize)\r
469 return -FDT_ERR_NOSPACE;\r
470\r
471 /* First attempt to build converted tree at beginning of buffer */\r
472 tmp = buf;\r
473 /* But if that overlaps with the old tree... */\r
474 if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {\r
475 /* Try right after the old tree instead */\r
476 tmp = (char *)(uintptr_t)fdtend;\r
477 if ((tmp + newsize) > ((char *)buf + bufsize))\r
478 return -FDT_ERR_NOSPACE;\r
479 }\r
480\r
481 _fdt_packblocks(fdt, tmp, mem_rsv_size, struct_size);\r
482 memmove(buf, tmp, newsize);\r
483\r
484 fdt_set_magic(buf, FDT_MAGIC);\r
485 fdt_set_totalsize(buf, bufsize);\r
486 fdt_set_version(buf, 17);\r
487 fdt_set_last_comp_version(buf, 16);\r
488 fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));\r
489\r
490 return 0;\r
491}\r
492\r
493int fdt_pack(void *fdt)\r
494{\r
495 int mem_rsv_size;\r
496\r
497 FDT_RW_CHECK_HEADER(fdt);\r
498\r
499 mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)\r
500 * sizeof(struct fdt_reserve_entry);\r
501 _fdt_packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt));\r
502 fdt_set_totalsize(fdt, _fdt_data_size(fdt));\r
503\r
504 return 0;\r
505}\r