1*5a485aa9Sskrll /* $NetBSD: fdt_sw.c,v 1.1.1.3 2019/12/22 12:30:38 skrll Exp $ */
2fc885a42Sskrll
3*5a485aa9Sskrll // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
46233fbe7Smacallan /*
56233fbe7Smacallan * libfdt - Flat Device Tree manipulation
66233fbe7Smacallan * Copyright (C) 2006 David Gibson, IBM Corporation.
76233fbe7Smacallan */
86233fbe7Smacallan #include "libfdt_env.h"
96233fbe7Smacallan
106233fbe7Smacallan #include <fdt.h>
116233fbe7Smacallan #include <libfdt.h>
126233fbe7Smacallan
136233fbe7Smacallan #include "libfdt_internal.h"
146233fbe7Smacallan
fdt_sw_probe_(void * fdt)15*5a485aa9Sskrll static int fdt_sw_probe_(void *fdt)
166233fbe7Smacallan {
17*5a485aa9Sskrll if (fdt_magic(fdt) == FDT_MAGIC)
18*5a485aa9Sskrll return -FDT_ERR_BADSTATE;
19*5a485aa9Sskrll else if (fdt_magic(fdt) != FDT_SW_MAGIC)
206233fbe7Smacallan return -FDT_ERR_BADMAGIC;
216233fbe7Smacallan return 0;
226233fbe7Smacallan }
236233fbe7Smacallan
24*5a485aa9Sskrll #define FDT_SW_PROBE(fdt) \
256233fbe7Smacallan { \
266233fbe7Smacallan int err; \
27*5a485aa9Sskrll if ((err = fdt_sw_probe_(fdt)) != 0) \
286233fbe7Smacallan return err; \
296233fbe7Smacallan }
306233fbe7Smacallan
31*5a485aa9Sskrll /* 'memrsv' state: Initial state after fdt_create()
32*5a485aa9Sskrll *
33*5a485aa9Sskrll * Allowed functions:
34*5a485aa9Sskrll * fdt_add_reservmap_entry()
35*5a485aa9Sskrll * fdt_finish_reservemap() [moves to 'struct' state]
36*5a485aa9Sskrll */
fdt_sw_probe_memrsv_(void * fdt)37*5a485aa9Sskrll static int fdt_sw_probe_memrsv_(void *fdt)
38*5a485aa9Sskrll {
39*5a485aa9Sskrll int err = fdt_sw_probe_(fdt);
40*5a485aa9Sskrll if (err)
41*5a485aa9Sskrll return err;
42*5a485aa9Sskrll
43*5a485aa9Sskrll if (fdt_off_dt_strings(fdt) != 0)
44*5a485aa9Sskrll return -FDT_ERR_BADSTATE;
45*5a485aa9Sskrll return 0;
46*5a485aa9Sskrll }
47*5a485aa9Sskrll
48*5a485aa9Sskrll #define FDT_SW_PROBE_MEMRSV(fdt) \
49*5a485aa9Sskrll { \
50*5a485aa9Sskrll int err; \
51*5a485aa9Sskrll if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \
52*5a485aa9Sskrll return err; \
53*5a485aa9Sskrll }
54*5a485aa9Sskrll
55*5a485aa9Sskrll /* 'struct' state: Enter this state after fdt_finish_reservemap()
56*5a485aa9Sskrll *
57*5a485aa9Sskrll * Allowed functions:
58*5a485aa9Sskrll * fdt_begin_node()
59*5a485aa9Sskrll * fdt_end_node()
60*5a485aa9Sskrll * fdt_property*()
61*5a485aa9Sskrll * fdt_finish() [moves to 'complete' state]
62*5a485aa9Sskrll */
fdt_sw_probe_struct_(void * fdt)63*5a485aa9Sskrll static int fdt_sw_probe_struct_(void *fdt)
64*5a485aa9Sskrll {
65*5a485aa9Sskrll int err = fdt_sw_probe_(fdt);
66*5a485aa9Sskrll if (err)
67*5a485aa9Sskrll return err;
68*5a485aa9Sskrll
69*5a485aa9Sskrll if (fdt_off_dt_strings(fdt) != fdt_totalsize(fdt))
70*5a485aa9Sskrll return -FDT_ERR_BADSTATE;
71*5a485aa9Sskrll return 0;
72*5a485aa9Sskrll }
73*5a485aa9Sskrll
74*5a485aa9Sskrll #define FDT_SW_PROBE_STRUCT(fdt) \
75*5a485aa9Sskrll { \
76*5a485aa9Sskrll int err; \
77*5a485aa9Sskrll if ((err = fdt_sw_probe_struct_(fdt)) != 0) \
78*5a485aa9Sskrll return err; \
79*5a485aa9Sskrll }
80*5a485aa9Sskrll
sw_flags(void * fdt)81*5a485aa9Sskrll static inline uint32_t sw_flags(void *fdt)
82*5a485aa9Sskrll {
83*5a485aa9Sskrll /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */
84*5a485aa9Sskrll return fdt_last_comp_version(fdt);
85*5a485aa9Sskrll }
86*5a485aa9Sskrll
87*5a485aa9Sskrll /* 'complete' state: Enter this state after fdt_finish()
88*5a485aa9Sskrll *
89*5a485aa9Sskrll * Allowed functions: none
90*5a485aa9Sskrll */
91*5a485aa9Sskrll
fdt_grab_space_(void * fdt,size_t len)92*5a485aa9Sskrll static void *fdt_grab_space_(void *fdt, size_t len)
936233fbe7Smacallan {
946233fbe7Smacallan int offset = fdt_size_dt_struct(fdt);
956233fbe7Smacallan int spaceleft;
966233fbe7Smacallan
976233fbe7Smacallan spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
986233fbe7Smacallan - fdt_size_dt_strings(fdt);
996233fbe7Smacallan
1006233fbe7Smacallan if ((offset + len < offset) || (offset + len > spaceleft))
1016233fbe7Smacallan return NULL;
1026233fbe7Smacallan
1036233fbe7Smacallan fdt_set_size_dt_struct(fdt, offset + len);
104*5a485aa9Sskrll return fdt_offset_ptr_w_(fdt, offset);
105*5a485aa9Sskrll }
106*5a485aa9Sskrll
fdt_create_with_flags(void * buf,int bufsize,uint32_t flags)107*5a485aa9Sskrll int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags)
108*5a485aa9Sskrll {
109*5a485aa9Sskrll const size_t hdrsize = FDT_ALIGN(sizeof(struct fdt_header),
110*5a485aa9Sskrll sizeof(struct fdt_reserve_entry));
111*5a485aa9Sskrll void *fdt = buf;
112*5a485aa9Sskrll
113*5a485aa9Sskrll if (bufsize < hdrsize)
114*5a485aa9Sskrll return -FDT_ERR_NOSPACE;
115*5a485aa9Sskrll
116*5a485aa9Sskrll if (flags & ~FDT_CREATE_FLAGS_ALL)
117*5a485aa9Sskrll return -FDT_ERR_BADFLAGS;
118*5a485aa9Sskrll
119*5a485aa9Sskrll memset(buf, 0, bufsize);
120*5a485aa9Sskrll
121*5a485aa9Sskrll /*
122*5a485aa9Sskrll * magic and last_comp_version keep intermediate state during the fdt
123*5a485aa9Sskrll * creation process, which is replaced with the proper FDT format by
124*5a485aa9Sskrll * fdt_finish().
125*5a485aa9Sskrll *
126*5a485aa9Sskrll * flags should be accessed with sw_flags().
127*5a485aa9Sskrll */
128*5a485aa9Sskrll fdt_set_magic(fdt, FDT_SW_MAGIC);
129*5a485aa9Sskrll fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
130*5a485aa9Sskrll fdt_set_last_comp_version(fdt, flags);
131*5a485aa9Sskrll
132*5a485aa9Sskrll fdt_set_totalsize(fdt, bufsize);
133*5a485aa9Sskrll
134*5a485aa9Sskrll fdt_set_off_mem_rsvmap(fdt, hdrsize);
135*5a485aa9Sskrll fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
136*5a485aa9Sskrll fdt_set_off_dt_strings(fdt, 0);
137*5a485aa9Sskrll
138*5a485aa9Sskrll return 0;
1396233fbe7Smacallan }
1406233fbe7Smacallan
fdt_create(void * buf,int bufsize)1416233fbe7Smacallan int fdt_create(void *buf, int bufsize)
1426233fbe7Smacallan {
143*5a485aa9Sskrll return fdt_create_with_flags(buf, bufsize, 0);
1446233fbe7Smacallan }
1456233fbe7Smacallan
fdt_resize(void * fdt,void * buf,int bufsize)1466233fbe7Smacallan int fdt_resize(void *fdt, void *buf, int bufsize)
1476233fbe7Smacallan {
1486233fbe7Smacallan size_t headsize, tailsize;
1496233fbe7Smacallan char *oldtail, *newtail;
1506233fbe7Smacallan
151*5a485aa9Sskrll FDT_SW_PROBE(fdt);
1526233fbe7Smacallan
153*5a485aa9Sskrll headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
1546233fbe7Smacallan tailsize = fdt_size_dt_strings(fdt);
1556233fbe7Smacallan
156*5a485aa9Sskrll if ((headsize + tailsize) > fdt_totalsize(fdt))
157*5a485aa9Sskrll return -FDT_ERR_INTERNAL;
158*5a485aa9Sskrll
1596233fbe7Smacallan if ((headsize + tailsize) > bufsize)
1606233fbe7Smacallan return -FDT_ERR_NOSPACE;
1616233fbe7Smacallan
1626233fbe7Smacallan oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
1636233fbe7Smacallan newtail = (char *)buf + bufsize - tailsize;
1646233fbe7Smacallan
1656233fbe7Smacallan /* Two cases to avoid clobbering data if the old and new
1666233fbe7Smacallan * buffers partially overlap */
1676233fbe7Smacallan if (buf <= fdt) {
1686233fbe7Smacallan memmove(buf, fdt, headsize);
1696233fbe7Smacallan memmove(newtail, oldtail, tailsize);
1706233fbe7Smacallan } else {
1716233fbe7Smacallan memmove(newtail, oldtail, tailsize);
1726233fbe7Smacallan memmove(buf, fdt, headsize);
1736233fbe7Smacallan }
1746233fbe7Smacallan
1756233fbe7Smacallan fdt_set_totalsize(buf, bufsize);
176*5a485aa9Sskrll if (fdt_off_dt_strings(buf))
177*5a485aa9Sskrll fdt_set_off_dt_strings(buf, bufsize);
1786233fbe7Smacallan
1796233fbe7Smacallan return 0;
1806233fbe7Smacallan }
1816233fbe7Smacallan
fdt_add_reservemap_entry(void * fdt,uint64_t addr,uint64_t size)1826233fbe7Smacallan int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
1836233fbe7Smacallan {
1846233fbe7Smacallan struct fdt_reserve_entry *re;
1856233fbe7Smacallan int offset;
1866233fbe7Smacallan
187*5a485aa9Sskrll FDT_SW_PROBE_MEMRSV(fdt);
1886233fbe7Smacallan
1896233fbe7Smacallan offset = fdt_off_dt_struct(fdt);
1906233fbe7Smacallan if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
1916233fbe7Smacallan return -FDT_ERR_NOSPACE;
1926233fbe7Smacallan
1936233fbe7Smacallan re = (struct fdt_reserve_entry *)((char *)fdt + offset);
1946233fbe7Smacallan re->address = cpu_to_fdt64(addr);
1956233fbe7Smacallan re->size = cpu_to_fdt64(size);
1966233fbe7Smacallan
1976233fbe7Smacallan fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
1986233fbe7Smacallan
1996233fbe7Smacallan return 0;
2006233fbe7Smacallan }
2016233fbe7Smacallan
fdt_finish_reservemap(void * fdt)2026233fbe7Smacallan int fdt_finish_reservemap(void *fdt)
2036233fbe7Smacallan {
204*5a485aa9Sskrll int err = fdt_add_reservemap_entry(fdt, 0, 0);
205*5a485aa9Sskrll
206*5a485aa9Sskrll if (err)
207*5a485aa9Sskrll return err;
208*5a485aa9Sskrll
209*5a485aa9Sskrll fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt));
210*5a485aa9Sskrll return 0;
2116233fbe7Smacallan }
2126233fbe7Smacallan
fdt_begin_node(void * fdt,const char * name)2136233fbe7Smacallan int fdt_begin_node(void *fdt, const char *name)
2146233fbe7Smacallan {
2156233fbe7Smacallan struct fdt_node_header *nh;
216*5a485aa9Sskrll int namelen;
2176233fbe7Smacallan
218*5a485aa9Sskrll FDT_SW_PROBE_STRUCT(fdt);
2196233fbe7Smacallan
220*5a485aa9Sskrll namelen = strlen(name) + 1;
221*5a485aa9Sskrll nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
2226233fbe7Smacallan if (! nh)
2236233fbe7Smacallan return -FDT_ERR_NOSPACE;
2246233fbe7Smacallan
2256233fbe7Smacallan nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
2266233fbe7Smacallan memcpy(nh->name, name, namelen);
2276233fbe7Smacallan return 0;
2286233fbe7Smacallan }
2296233fbe7Smacallan
fdt_end_node(void * fdt)2306233fbe7Smacallan int fdt_end_node(void *fdt)
2316233fbe7Smacallan {
2326233fbe7Smacallan fdt32_t *en;
2336233fbe7Smacallan
234*5a485aa9Sskrll FDT_SW_PROBE_STRUCT(fdt);
2356233fbe7Smacallan
236*5a485aa9Sskrll en = fdt_grab_space_(fdt, FDT_TAGSIZE);
2376233fbe7Smacallan if (! en)
2386233fbe7Smacallan return -FDT_ERR_NOSPACE;
2396233fbe7Smacallan
2406233fbe7Smacallan *en = cpu_to_fdt32(FDT_END_NODE);
2416233fbe7Smacallan return 0;
2426233fbe7Smacallan }
2436233fbe7Smacallan
fdt_add_string_(void * fdt,const char * s)244*5a485aa9Sskrll static int fdt_add_string_(void *fdt, const char *s)
2456233fbe7Smacallan {
2466233fbe7Smacallan char *strtab = (char *)fdt + fdt_totalsize(fdt);
2476233fbe7Smacallan int strtabsize = fdt_size_dt_strings(fdt);
2486233fbe7Smacallan int len = strlen(s) + 1;
2496233fbe7Smacallan int struct_top, offset;
2506233fbe7Smacallan
2516233fbe7Smacallan offset = -strtabsize - len;
2526233fbe7Smacallan struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
2536233fbe7Smacallan if (fdt_totalsize(fdt) + offset < struct_top)
2546233fbe7Smacallan return 0; /* no more room :( */
2556233fbe7Smacallan
2566233fbe7Smacallan memcpy(strtab + offset, s, len);
2576233fbe7Smacallan fdt_set_size_dt_strings(fdt, strtabsize + len);
2586233fbe7Smacallan return offset;
2596233fbe7Smacallan }
2606233fbe7Smacallan
261*5a485aa9Sskrll /* Must only be used to roll back in case of error */
fdt_del_last_string_(void * fdt,const char * s)262*5a485aa9Sskrll static void fdt_del_last_string_(void *fdt, const char *s)
263*5a485aa9Sskrll {
264*5a485aa9Sskrll int strtabsize = fdt_size_dt_strings(fdt);
265*5a485aa9Sskrll int len = strlen(s) + 1;
266*5a485aa9Sskrll
267*5a485aa9Sskrll fdt_set_size_dt_strings(fdt, strtabsize - len);
268*5a485aa9Sskrll }
269*5a485aa9Sskrll
fdt_find_add_string_(void * fdt,const char * s,int * allocated)270*5a485aa9Sskrll static int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
271*5a485aa9Sskrll {
272*5a485aa9Sskrll char *strtab = (char *)fdt + fdt_totalsize(fdt);
273*5a485aa9Sskrll int strtabsize = fdt_size_dt_strings(fdt);
274*5a485aa9Sskrll const char *p;
275*5a485aa9Sskrll
276*5a485aa9Sskrll *allocated = 0;
277*5a485aa9Sskrll
278*5a485aa9Sskrll p = fdt_find_string_(strtab - strtabsize, strtabsize, s);
279*5a485aa9Sskrll if (p)
280*5a485aa9Sskrll return p - strtab;
281*5a485aa9Sskrll
282*5a485aa9Sskrll *allocated = 1;
283*5a485aa9Sskrll
284*5a485aa9Sskrll return fdt_add_string_(fdt, s);
285*5a485aa9Sskrll }
286*5a485aa9Sskrll
fdt_property_placeholder(void * fdt,const char * name,int len,void ** valp)287*5a485aa9Sskrll int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp)
2886233fbe7Smacallan {
2896233fbe7Smacallan struct fdt_property *prop;
2906233fbe7Smacallan int nameoff;
291*5a485aa9Sskrll int allocated;
2926233fbe7Smacallan
293*5a485aa9Sskrll FDT_SW_PROBE_STRUCT(fdt);
2946233fbe7Smacallan
295*5a485aa9Sskrll /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */
296*5a485aa9Sskrll if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) {
297*5a485aa9Sskrll allocated = 1;
298*5a485aa9Sskrll nameoff = fdt_add_string_(fdt, name);
299*5a485aa9Sskrll } else {
300*5a485aa9Sskrll nameoff = fdt_find_add_string_(fdt, name, &allocated);
301*5a485aa9Sskrll }
3026233fbe7Smacallan if (nameoff == 0)
3036233fbe7Smacallan return -FDT_ERR_NOSPACE;
3046233fbe7Smacallan
305*5a485aa9Sskrll prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
306*5a485aa9Sskrll if (! prop) {
307*5a485aa9Sskrll if (allocated)
308*5a485aa9Sskrll fdt_del_last_string_(fdt, name);
3096233fbe7Smacallan return -FDT_ERR_NOSPACE;
310*5a485aa9Sskrll }
3116233fbe7Smacallan
3126233fbe7Smacallan prop->tag = cpu_to_fdt32(FDT_PROP);
3136233fbe7Smacallan prop->nameoff = cpu_to_fdt32(nameoff);
3146233fbe7Smacallan prop->len = cpu_to_fdt32(len);
315*5a485aa9Sskrll *valp = prop->data;
316*5a485aa9Sskrll return 0;
317*5a485aa9Sskrll }
318*5a485aa9Sskrll
fdt_property(void * fdt,const char * name,const void * val,int len)319*5a485aa9Sskrll int fdt_property(void *fdt, const char *name, const void *val, int len)
320*5a485aa9Sskrll {
321*5a485aa9Sskrll void *ptr;
322*5a485aa9Sskrll int ret;
323*5a485aa9Sskrll
324*5a485aa9Sskrll ret = fdt_property_placeholder(fdt, name, len, &ptr);
325*5a485aa9Sskrll if (ret)
326*5a485aa9Sskrll return ret;
327*5a485aa9Sskrll memcpy(ptr, val, len);
3286233fbe7Smacallan return 0;
3296233fbe7Smacallan }
3306233fbe7Smacallan
fdt_finish(void * fdt)3316233fbe7Smacallan int fdt_finish(void *fdt)
3326233fbe7Smacallan {
3336233fbe7Smacallan char *p = (char *)fdt;
3346233fbe7Smacallan fdt32_t *end;
3356233fbe7Smacallan int oldstroffset, newstroffset;
3366233fbe7Smacallan uint32_t tag;
3376233fbe7Smacallan int offset, nextoffset;
3386233fbe7Smacallan
339*5a485aa9Sskrll FDT_SW_PROBE_STRUCT(fdt);
3406233fbe7Smacallan
3416233fbe7Smacallan /* Add terminator */
342*5a485aa9Sskrll end = fdt_grab_space_(fdt, sizeof(*end));
3436233fbe7Smacallan if (! end)
3446233fbe7Smacallan return -FDT_ERR_NOSPACE;
3456233fbe7Smacallan *end = cpu_to_fdt32(FDT_END);
3466233fbe7Smacallan
3476233fbe7Smacallan /* Relocate the string table */
3486233fbe7Smacallan oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
3496233fbe7Smacallan newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
3506233fbe7Smacallan memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
3516233fbe7Smacallan fdt_set_off_dt_strings(fdt, newstroffset);
3526233fbe7Smacallan
3536233fbe7Smacallan /* Walk the structure, correcting string offsets */
3546233fbe7Smacallan offset = 0;
3556233fbe7Smacallan while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
3566233fbe7Smacallan if (tag == FDT_PROP) {
3576233fbe7Smacallan struct fdt_property *prop =
358*5a485aa9Sskrll fdt_offset_ptr_w_(fdt, offset);
3596233fbe7Smacallan int nameoff;
3606233fbe7Smacallan
3616233fbe7Smacallan nameoff = fdt32_to_cpu(prop->nameoff);
3626233fbe7Smacallan nameoff += fdt_size_dt_strings(fdt);
3636233fbe7Smacallan prop->nameoff = cpu_to_fdt32(nameoff);
3646233fbe7Smacallan }
3656233fbe7Smacallan offset = nextoffset;
3666233fbe7Smacallan }
3676233fbe7Smacallan if (nextoffset < 0)
3686233fbe7Smacallan return nextoffset;
3696233fbe7Smacallan
3706233fbe7Smacallan /* Finally, adjust the header */
3716233fbe7Smacallan fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
372*5a485aa9Sskrll
373*5a485aa9Sskrll /* And fix up fields that were keeping intermediate state. */
374*5a485aa9Sskrll fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
3756233fbe7Smacallan fdt_set_magic(fdt, FDT_MAGIC);
376*5a485aa9Sskrll
3776233fbe7Smacallan return 0;
3786233fbe7Smacallan }
379