]>
Commit | Line | Data |
---|---|---|
35748177 GVB |
1 | /* |
2 | * libfdt - Flat Device Tree manipulation | |
3 | * Copyright (C) 2006 David Gibson, IBM Corporation. | |
35084760 | 4 | * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause |
35748177 | 5 | */ |
6feed2a5 | 6 | #include <libfdt_env.h> |
35748177 GVB |
7 | #include <fdt.h> |
8 | #include <libfdt.h> | |
9 | ||
10 | #include "libfdt_internal.h" | |
11 | ||
fc7758ee | 12 | static int _fdt_sw_check_header(void *fdt) |
35748177 | 13 | { |
fc7758ee | 14 | if (fdt_magic(fdt) != FDT_SW_MAGIC) |
35748177 | 15 | return -FDT_ERR_BADMAGIC; |
2f08bfa9 | 16 | /* FIXME: should check more details about the header state */ |
35748177 GVB |
17 | return 0; |
18 | } | |
19 | ||
fc7758ee | 20 | #define FDT_SW_CHECK_HEADER(fdt) \ |
2f08bfa9 DG |
21 | { \ |
22 | int err; \ | |
fc7758ee | 23 | if ((err = _fdt_sw_check_header(fdt)) != 0) \ |
2f08bfa9 DG |
24 | return err; \ |
25 | } | |
26 | ||
13d93f38 | 27 | static void *_fdt_grab_space(void *fdt, size_t len) |
35748177 GVB |
28 | { |
29 | int offset = fdt_size_dt_struct(fdt); | |
30 | int spaceleft; | |
31 | ||
32 | spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) | |
33 | - fdt_size_dt_strings(fdt); | |
34 | ||
35 | if ((offset + len < offset) || (offset + len > spaceleft)) | |
36 | return NULL; | |
37 | ||
8d04f02f | 38 | fdt_set_size_dt_struct(fdt, offset + len); |
a22d9cfb | 39 | return _fdt_offset_ptr_w(fdt, offset); |
35748177 GVB |
40 | } |
41 | ||
42 | int fdt_create(void *buf, int bufsize) | |
43 | { | |
44 | void *fdt = buf; | |
45 | ||
46 | if (bufsize < sizeof(struct fdt_header)) | |
47 | return -FDT_ERR_NOSPACE; | |
48 | ||
49 | memset(buf, 0, bufsize); | |
50 | ||
fc7758ee | 51 | fdt_set_magic(fdt, FDT_SW_MAGIC); |
8d04f02f KG |
52 | fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); |
53 | fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); | |
54 | fdt_set_totalsize(fdt, bufsize); | |
35748177 | 55 | |
fc7758ee DG |
56 | fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header), |
57 | sizeof(struct fdt_reserve_entry))); | |
8d04f02f KG |
58 | fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); |
59 | fdt_set_off_dt_strings(fdt, bufsize); | |
35748177 GVB |
60 | |
61 | return 0; | |
62 | } | |
63 | ||
0596d35d SG |
64 | int fdt_resize(void *fdt, void *buf, int bufsize) |
65 | { | |
66 | size_t headsize, tailsize; | |
67 | char *oldtail, *newtail; | |
68 | ||
69 | FDT_SW_CHECK_HEADER(fdt); | |
70 | ||
71 | headsize = fdt_off_dt_struct(fdt); | |
72 | tailsize = fdt_size_dt_strings(fdt); | |
73 | ||
74 | if ((headsize + tailsize) > bufsize) | |
75 | return -FDT_ERR_NOSPACE; | |
76 | ||
77 | oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; | |
78 | newtail = (char *)buf + bufsize - tailsize; | |
79 | ||
80 | /* Two cases to avoid clobbering data if the old and new | |
81 | * buffers partially overlap */ | |
82 | if (buf <= fdt) { | |
83 | memmove(buf, fdt, headsize); | |
84 | memmove(newtail, oldtail, tailsize); | |
85 | } else { | |
86 | memmove(newtail, oldtail, tailsize); | |
87 | memmove(buf, fdt, headsize); | |
88 | } | |
89 | ||
90 | fdt_set_off_dt_strings(buf, bufsize); | |
91 | fdt_set_totalsize(buf, bufsize); | |
92 | ||
93 | return 0; | |
94 | } | |
95 | ||
35748177 GVB |
96 | int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) |
97 | { | |
98 | struct fdt_reserve_entry *re; | |
35748177 GVB |
99 | int offset; |
100 | ||
fc7758ee | 101 | FDT_SW_CHECK_HEADER(fdt); |
2f08bfa9 | 102 | |
35748177 GVB |
103 | if (fdt_size_dt_struct(fdt)) |
104 | return -FDT_ERR_BADSTATE; | |
105 | ||
106 | offset = fdt_off_dt_struct(fdt); | |
107 | if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) | |
108 | return -FDT_ERR_NOSPACE; | |
109 | ||
ef4e8ce1 | 110 | re = (struct fdt_reserve_entry *)((char *)fdt + offset); |
35748177 GVB |
111 | re->address = cpu_to_fdt64(addr); |
112 | re->size = cpu_to_fdt64(size); | |
113 | ||
8d04f02f | 114 | fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); |
35748177 GVB |
115 | |
116 | return 0; | |
117 | } | |
118 | ||
119 | int fdt_finish_reservemap(void *fdt) | |
120 | { | |
121 | return fdt_add_reservemap_entry(fdt, 0, 0); | |
122 | } | |
123 | ||
124 | int fdt_begin_node(void *fdt, const char *name) | |
125 | { | |
126 | struct fdt_node_header *nh; | |
35748177 GVB |
127 | int namelen = strlen(name) + 1; |
128 | ||
fc7758ee | 129 | FDT_SW_CHECK_HEADER(fdt); |
35748177 | 130 | |
fc7758ee | 131 | nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); |
35748177 GVB |
132 | if (! nh) |
133 | return -FDT_ERR_NOSPACE; | |
134 | ||
135 | nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); | |
136 | memcpy(nh->name, name, namelen); | |
137 | return 0; | |
138 | } | |
139 | ||
140 | int fdt_end_node(void *fdt) | |
141 | { | |
b2ba62a1 | 142 | fdt32_t *en; |
35748177 | 143 | |
fc7758ee | 144 | FDT_SW_CHECK_HEADER(fdt); |
35748177 | 145 | |
fc7758ee | 146 | en = _fdt_grab_space(fdt, FDT_TAGSIZE); |
35748177 GVB |
147 | if (! en) |
148 | return -FDT_ERR_NOSPACE; | |
149 | ||
150 | *en = cpu_to_fdt32(FDT_END_NODE); | |
151 | return 0; | |
152 | } | |
153 | ||
fc7758ee | 154 | static int _fdt_find_add_string(void *fdt, const char *s) |
35748177 GVB |
155 | { |
156 | char *strtab = (char *)fdt + fdt_totalsize(fdt); | |
157 | const char *p; | |
158 | int strtabsize = fdt_size_dt_strings(fdt); | |
159 | int len = strlen(s) + 1; | |
160 | int struct_top, offset; | |
161 | ||
162 | p = _fdt_find_string(strtab - strtabsize, strtabsize, s); | |
163 | if (p) | |
164 | return p - strtab; | |
165 | ||
166 | /* Add it */ | |
167 | offset = -strtabsize - len; | |
168 | struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); | |
169 | if (fdt_totalsize(fdt) + offset < struct_top) | |
170 | return 0; /* no more room :( */ | |
171 | ||
172 | memcpy(strtab + offset, s, len); | |
8d04f02f | 173 | fdt_set_size_dt_strings(fdt, strtabsize + len); |
35748177 GVB |
174 | return offset; |
175 | } | |
176 | ||
f8a2d7a4 | 177 | int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) |
35748177 GVB |
178 | { |
179 | struct fdt_property *prop; | |
35748177 GVB |
180 | int nameoff; |
181 | ||
fc7758ee | 182 | FDT_SW_CHECK_HEADER(fdt); |
35748177 | 183 | |
fc7758ee | 184 | nameoff = _fdt_find_add_string(fdt, name); |
35748177 GVB |
185 | if (nameoff == 0) |
186 | return -FDT_ERR_NOSPACE; | |
187 | ||
fc7758ee | 188 | prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); |
35748177 GVB |
189 | if (! prop) |
190 | return -FDT_ERR_NOSPACE; | |
191 | ||
192 | prop->tag = cpu_to_fdt32(FDT_PROP); | |
193 | prop->nameoff = cpu_to_fdt32(nameoff); | |
194 | prop->len = cpu_to_fdt32(len); | |
f8a2d7a4 SG |
195 | *valp = prop->data; |
196 | return 0; | |
197 | } | |
198 | ||
199 | int fdt_property(void *fdt, const char *name, const void *val, int len) | |
200 | { | |
201 | void *ptr; | |
202 | int ret; | |
203 | ||
204 | ret = fdt_property_placeholder(fdt, name, len, &ptr); | |
205 | if (ret) | |
206 | return ret; | |
207 | memcpy(ptr, val, len); | |
35748177 GVB |
208 | return 0; |
209 | } | |
210 | ||
211 | int fdt_finish(void *fdt) | |
212 | { | |
35748177 | 213 | char *p = (char *)fdt; |
b2ba62a1 | 214 | fdt32_t *end; |
35748177 GVB |
215 | int oldstroffset, newstroffset; |
216 | uint32_t tag; | |
217 | int offset, nextoffset; | |
218 | ||
fc7758ee | 219 | FDT_SW_CHECK_HEADER(fdt); |
35748177 GVB |
220 | |
221 | /* Add terminator */ | |
fc7758ee | 222 | end = _fdt_grab_space(fdt, sizeof(*end)); |
35748177 GVB |
223 | if (! end) |
224 | return -FDT_ERR_NOSPACE; | |
225 | *end = cpu_to_fdt32(FDT_END); | |
226 | ||
227 | /* Relocate the string table */ | |
228 | oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); | |
229 | newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); | |
230 | memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); | |
8d04f02f | 231 | fdt_set_off_dt_strings(fdt, newstroffset); |
35748177 GVB |
232 | |
233 | /* Walk the structure, correcting string offsets */ | |
234 | offset = 0; | |
8d04f02f | 235 | while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { |
35748177 | 236 | if (tag == FDT_PROP) { |
8d04f02f | 237 | struct fdt_property *prop = |
a22d9cfb | 238 | _fdt_offset_ptr_w(fdt, offset); |
35748177 GVB |
239 | int nameoff; |
240 | ||
35748177 GVB |
241 | nameoff = fdt32_to_cpu(prop->nameoff); |
242 | nameoff += fdt_size_dt_strings(fdt); | |
243 | prop->nameoff = cpu_to_fdt32(nameoff); | |
244 | } | |
245 | offset = nextoffset; | |
246 | } | |
a22d9cfb DG |
247 | if (nextoffset < 0) |
248 | return nextoffset; | |
35748177 GVB |
249 | |
250 | /* Finally, adjust the header */ | |
8d04f02f KG |
251 | fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); |
252 | fdt_set_magic(fdt, FDT_MAGIC); | |
35748177 GVB |
253 | return 0; |
254 | } |