.. | .. |
---|
| 1 | +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) |
---|
1 | 2 | /* |
---|
2 | 3 | * libfdt - Flat Device Tree manipulation |
---|
3 | 4 | * Copyright (C) 2006 David Gibson, IBM Corporation. |
---|
4 | | - * |
---|
5 | | - * libfdt is dual licensed: you can use it either under the terms of |
---|
6 | | - * the GPL, or the BSD license, at your option. |
---|
7 | | - * |
---|
8 | | - * a) This library is free software; you can redistribute it and/or |
---|
9 | | - * modify it under the terms of the GNU General Public License as |
---|
10 | | - * published by the Free Software Foundation; either version 2 of the |
---|
11 | | - * License, or (at your option) any later version. |
---|
12 | | - * |
---|
13 | | - * This library is distributed in the hope that it will be useful, |
---|
14 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
15 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
16 | | - * GNU General Public License for more details. |
---|
17 | | - * |
---|
18 | | - * You should have received a copy of the GNU General Public |
---|
19 | | - * License along with this library; if not, write to the Free |
---|
20 | | - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, |
---|
21 | | - * MA 02110-1301 USA |
---|
22 | | - * |
---|
23 | | - * Alternatively, |
---|
24 | | - * |
---|
25 | | - * b) Redistribution and use in source and binary forms, with or |
---|
26 | | - * without modification, are permitted provided that the following |
---|
27 | | - * conditions are met: |
---|
28 | | - * |
---|
29 | | - * 1. Redistributions of source code must retain the above |
---|
30 | | - * copyright notice, this list of conditions and the following |
---|
31 | | - * disclaimer. |
---|
32 | | - * 2. Redistributions in binary form must reproduce the above |
---|
33 | | - * copyright notice, this list of conditions and the following |
---|
34 | | - * disclaimer in the documentation and/or other materials |
---|
35 | | - * provided with the distribution. |
---|
36 | | - * |
---|
37 | | - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
---|
38 | | - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
---|
39 | | - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
---|
40 | | - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
---|
41 | | - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
---|
42 | | - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
---|
43 | | - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
---|
44 | | - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
---|
45 | | - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
46 | | - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
---|
47 | | - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
---|
48 | | - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
---|
49 | | - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
---|
50 | 5 | */ |
---|
51 | 6 | #include "libfdt_env.h" |
---|
52 | 7 | |
---|
.. | .. |
---|
55 | 10 | |
---|
56 | 11 | #include "libfdt_internal.h" |
---|
57 | 12 | |
---|
58 | | -static int fdt_sw_check_header_(void *fdt) |
---|
| 13 | +static int fdt_sw_probe_(void *fdt) |
---|
59 | 14 | { |
---|
60 | | - if (fdt_magic(fdt) != FDT_SW_MAGIC) |
---|
61 | | - return -FDT_ERR_BADMAGIC; |
---|
62 | | - /* FIXME: should check more details about the header state */ |
---|
| 15 | + if (!can_assume(VALID_INPUT)) { |
---|
| 16 | + if (fdt_magic(fdt) == FDT_MAGIC) |
---|
| 17 | + return -FDT_ERR_BADSTATE; |
---|
| 18 | + else if (fdt_magic(fdt) != FDT_SW_MAGIC) |
---|
| 19 | + return -FDT_ERR_BADMAGIC; |
---|
| 20 | + } |
---|
| 21 | + |
---|
63 | 22 | return 0; |
---|
64 | 23 | } |
---|
65 | 24 | |
---|
66 | | -#define FDT_SW_CHECK_HEADER(fdt) \ |
---|
| 25 | +#define FDT_SW_PROBE(fdt) \ |
---|
67 | 26 | { \ |
---|
68 | 27 | int err; \ |
---|
69 | | - if ((err = fdt_sw_check_header_(fdt)) != 0) \ |
---|
| 28 | + if ((err = fdt_sw_probe_(fdt)) != 0) \ |
---|
70 | 29 | return err; \ |
---|
71 | 30 | } |
---|
72 | 31 | |
---|
| 32 | +/* 'memrsv' state: Initial state after fdt_create() |
---|
| 33 | + * |
---|
| 34 | + * Allowed functions: |
---|
| 35 | + * fdt_add_reservemap_entry() |
---|
| 36 | + * fdt_finish_reservemap() [moves to 'struct' state] |
---|
| 37 | + */ |
---|
| 38 | +static int fdt_sw_probe_memrsv_(void *fdt) |
---|
| 39 | +{ |
---|
| 40 | + int err = fdt_sw_probe_(fdt); |
---|
| 41 | + if (err) |
---|
| 42 | + return err; |
---|
| 43 | + |
---|
| 44 | + if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0) |
---|
| 45 | + return -FDT_ERR_BADSTATE; |
---|
| 46 | + return 0; |
---|
| 47 | +} |
---|
| 48 | + |
---|
| 49 | +#define FDT_SW_PROBE_MEMRSV(fdt) \ |
---|
| 50 | + { \ |
---|
| 51 | + int err; \ |
---|
| 52 | + if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \ |
---|
| 53 | + return err; \ |
---|
| 54 | + } |
---|
| 55 | + |
---|
| 56 | +/* 'struct' state: Enter this state after fdt_finish_reservemap() |
---|
| 57 | + * |
---|
| 58 | + * Allowed functions: |
---|
| 59 | + * fdt_begin_node() |
---|
| 60 | + * fdt_end_node() |
---|
| 61 | + * fdt_property*() |
---|
| 62 | + * fdt_finish() [moves to 'complete' state] |
---|
| 63 | + */ |
---|
| 64 | +static int fdt_sw_probe_struct_(void *fdt) |
---|
| 65 | +{ |
---|
| 66 | + int err = fdt_sw_probe_(fdt); |
---|
| 67 | + if (err) |
---|
| 68 | + return err; |
---|
| 69 | + |
---|
| 70 | + if (!can_assume(VALID_INPUT) && |
---|
| 71 | + fdt_off_dt_strings(fdt) != fdt_totalsize(fdt)) |
---|
| 72 | + return -FDT_ERR_BADSTATE; |
---|
| 73 | + return 0; |
---|
| 74 | +} |
---|
| 75 | + |
---|
| 76 | +#define FDT_SW_PROBE_STRUCT(fdt) \ |
---|
| 77 | + { \ |
---|
| 78 | + int err; \ |
---|
| 79 | + if ((err = fdt_sw_probe_struct_(fdt)) != 0) \ |
---|
| 80 | + return err; \ |
---|
| 81 | + } |
---|
| 82 | + |
---|
| 83 | +static inline uint32_t sw_flags(void *fdt) |
---|
| 84 | +{ |
---|
| 85 | + /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */ |
---|
| 86 | + return fdt_last_comp_version(fdt); |
---|
| 87 | +} |
---|
| 88 | + |
---|
| 89 | +/* 'complete' state: Enter this state after fdt_finish() |
---|
| 90 | + * |
---|
| 91 | + * Allowed functions: none |
---|
| 92 | + */ |
---|
| 93 | + |
---|
73 | 94 | static void *fdt_grab_space_(void *fdt, size_t len) |
---|
74 | 95 | { |
---|
75 | | - int offset = fdt_size_dt_struct(fdt); |
---|
76 | | - int spaceleft; |
---|
| 96 | + unsigned int offset = fdt_size_dt_struct(fdt); |
---|
| 97 | + unsigned int spaceleft; |
---|
77 | 98 | |
---|
78 | 99 | spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) |
---|
79 | 100 | - fdt_size_dt_strings(fdt); |
---|
.. | .. |
---|
85 | 106 | return fdt_offset_ptr_w_(fdt, offset); |
---|
86 | 107 | } |
---|
87 | 108 | |
---|
88 | | -int fdt_create(void *buf, int bufsize) |
---|
| 109 | +int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags) |
---|
89 | 110 | { |
---|
| 111 | + const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header), |
---|
| 112 | + sizeof(struct fdt_reserve_entry)); |
---|
90 | 113 | void *fdt = buf; |
---|
91 | 114 | |
---|
92 | | - if (bufsize < sizeof(struct fdt_header)) |
---|
| 115 | + if (bufsize < hdrsize) |
---|
93 | 116 | return -FDT_ERR_NOSPACE; |
---|
| 117 | + |
---|
| 118 | + if (flags & ~FDT_CREATE_FLAGS_ALL) |
---|
| 119 | + return -FDT_ERR_BADFLAGS; |
---|
94 | 120 | |
---|
95 | 121 | memset(buf, 0, bufsize); |
---|
96 | 122 | |
---|
| 123 | + /* |
---|
| 124 | + * magic and last_comp_version keep intermediate state during the fdt |
---|
| 125 | + * creation process, which is replaced with the proper FDT format by |
---|
| 126 | + * fdt_finish(). |
---|
| 127 | + * |
---|
| 128 | + * flags should be accessed with sw_flags(). |
---|
| 129 | + */ |
---|
97 | 130 | fdt_set_magic(fdt, FDT_SW_MAGIC); |
---|
98 | 131 | fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); |
---|
99 | | - fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); |
---|
| 132 | + fdt_set_last_comp_version(fdt, flags); |
---|
| 133 | + |
---|
100 | 134 | fdt_set_totalsize(fdt, bufsize); |
---|
101 | 135 | |
---|
102 | | - fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header), |
---|
103 | | - sizeof(struct fdt_reserve_entry))); |
---|
| 136 | + fdt_set_off_mem_rsvmap(fdt, hdrsize); |
---|
104 | 137 | fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); |
---|
105 | | - fdt_set_off_dt_strings(fdt, bufsize); |
---|
| 138 | + fdt_set_off_dt_strings(fdt, 0); |
---|
106 | 139 | |
---|
107 | 140 | return 0; |
---|
| 141 | +} |
---|
| 142 | + |
---|
| 143 | +int fdt_create(void *buf, int bufsize) |
---|
| 144 | +{ |
---|
| 145 | + return fdt_create_with_flags(buf, bufsize, 0); |
---|
108 | 146 | } |
---|
109 | 147 | |
---|
110 | 148 | int fdt_resize(void *fdt, void *buf, int bufsize) |
---|
.. | .. |
---|
112 | 150 | size_t headsize, tailsize; |
---|
113 | 151 | char *oldtail, *newtail; |
---|
114 | 152 | |
---|
115 | | - FDT_SW_CHECK_HEADER(fdt); |
---|
| 153 | + FDT_SW_PROBE(fdt); |
---|
116 | 154 | |
---|
117 | | - headsize = fdt_off_dt_struct(fdt); |
---|
| 155 | + if (bufsize < 0) |
---|
| 156 | + return -FDT_ERR_NOSPACE; |
---|
| 157 | + |
---|
| 158 | + headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); |
---|
118 | 159 | tailsize = fdt_size_dt_strings(fdt); |
---|
119 | 160 | |
---|
120 | | - if ((headsize + tailsize) > bufsize) |
---|
| 161 | + if (!can_assume(VALID_DTB) && |
---|
| 162 | + headsize + tailsize > fdt_totalsize(fdt)) |
---|
| 163 | + return -FDT_ERR_INTERNAL; |
---|
| 164 | + |
---|
| 165 | + if ((headsize + tailsize) > (unsigned)bufsize) |
---|
121 | 166 | return -FDT_ERR_NOSPACE; |
---|
122 | 167 | |
---|
123 | 168 | oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; |
---|
.. | .. |
---|
133 | 178 | memmove(buf, fdt, headsize); |
---|
134 | 179 | } |
---|
135 | 180 | |
---|
136 | | - fdt_set_off_dt_strings(buf, bufsize); |
---|
137 | 181 | fdt_set_totalsize(buf, bufsize); |
---|
| 182 | + if (fdt_off_dt_strings(buf)) |
---|
| 183 | + fdt_set_off_dt_strings(buf, bufsize); |
---|
138 | 184 | |
---|
139 | 185 | return 0; |
---|
140 | 186 | } |
---|
.. | .. |
---|
144 | 190 | struct fdt_reserve_entry *re; |
---|
145 | 191 | int offset; |
---|
146 | 192 | |
---|
147 | | - FDT_SW_CHECK_HEADER(fdt); |
---|
148 | | - |
---|
149 | | - if (fdt_size_dt_struct(fdt)) |
---|
150 | | - return -FDT_ERR_BADSTATE; |
---|
| 193 | + FDT_SW_PROBE_MEMRSV(fdt); |
---|
151 | 194 | |
---|
152 | 195 | offset = fdt_off_dt_struct(fdt); |
---|
153 | 196 | if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) |
---|
.. | .. |
---|
164 | 207 | |
---|
165 | 208 | int fdt_finish_reservemap(void *fdt) |
---|
166 | 209 | { |
---|
167 | | - return fdt_add_reservemap_entry(fdt, 0, 0); |
---|
| 210 | + int err = fdt_add_reservemap_entry(fdt, 0, 0); |
---|
| 211 | + |
---|
| 212 | + if (err) |
---|
| 213 | + return err; |
---|
| 214 | + |
---|
| 215 | + fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt)); |
---|
| 216 | + return 0; |
---|
168 | 217 | } |
---|
169 | 218 | |
---|
170 | 219 | int fdt_begin_node(void *fdt, const char *name) |
---|
171 | 220 | { |
---|
172 | 221 | struct fdt_node_header *nh; |
---|
173 | | - int namelen = strlen(name) + 1; |
---|
| 222 | + int namelen; |
---|
174 | 223 | |
---|
175 | | - FDT_SW_CHECK_HEADER(fdt); |
---|
| 224 | + FDT_SW_PROBE_STRUCT(fdt); |
---|
176 | 225 | |
---|
| 226 | + namelen = strlen(name) + 1; |
---|
177 | 227 | nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); |
---|
178 | 228 | if (! nh) |
---|
179 | 229 | return -FDT_ERR_NOSPACE; |
---|
.. | .. |
---|
187 | 237 | { |
---|
188 | 238 | fdt32_t *en; |
---|
189 | 239 | |
---|
190 | | - FDT_SW_CHECK_HEADER(fdt); |
---|
| 240 | + FDT_SW_PROBE_STRUCT(fdt); |
---|
191 | 241 | |
---|
192 | 242 | en = fdt_grab_space_(fdt, FDT_TAGSIZE); |
---|
193 | 243 | if (! en) |
---|
.. | .. |
---|
197 | 247 | return 0; |
---|
198 | 248 | } |
---|
199 | 249 | |
---|
200 | | -static int fdt_find_add_string_(void *fdt, const char *s) |
---|
| 250 | +static int fdt_add_string_(void *fdt, const char *s) |
---|
201 | 251 | { |
---|
202 | 252 | char *strtab = (char *)fdt + fdt_totalsize(fdt); |
---|
203 | | - const char *p; |
---|
| 253 | + unsigned int strtabsize = fdt_size_dt_strings(fdt); |
---|
| 254 | + unsigned int len = strlen(s) + 1; |
---|
| 255 | + unsigned int struct_top, offset; |
---|
| 256 | + |
---|
| 257 | + offset = strtabsize + len; |
---|
| 258 | + struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); |
---|
| 259 | + if (fdt_totalsize(fdt) - offset < struct_top) |
---|
| 260 | + return 0; /* no more room :( */ |
---|
| 261 | + |
---|
| 262 | + memcpy(strtab - offset, s, len); |
---|
| 263 | + fdt_set_size_dt_strings(fdt, strtabsize + len); |
---|
| 264 | + return -offset; |
---|
| 265 | +} |
---|
| 266 | + |
---|
| 267 | +/* Must only be used to roll back in case of error */ |
---|
| 268 | +static void fdt_del_last_string_(void *fdt, const char *s) |
---|
| 269 | +{ |
---|
204 | 270 | int strtabsize = fdt_size_dt_strings(fdt); |
---|
205 | 271 | int len = strlen(s) + 1; |
---|
206 | | - int struct_top, offset; |
---|
| 272 | + |
---|
| 273 | + fdt_set_size_dt_strings(fdt, strtabsize - len); |
---|
| 274 | +} |
---|
| 275 | + |
---|
| 276 | +static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) |
---|
| 277 | +{ |
---|
| 278 | + char *strtab = (char *)fdt + fdt_totalsize(fdt); |
---|
| 279 | + int strtabsize = fdt_size_dt_strings(fdt); |
---|
| 280 | + const char *p; |
---|
| 281 | + |
---|
| 282 | + *allocated = 0; |
---|
207 | 283 | |
---|
208 | 284 | p = fdt_find_string_(strtab - strtabsize, strtabsize, s); |
---|
209 | 285 | if (p) |
---|
210 | 286 | return p - strtab; |
---|
211 | 287 | |
---|
212 | | - /* Add it */ |
---|
213 | | - offset = -strtabsize - len; |
---|
214 | | - struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); |
---|
215 | | - if (fdt_totalsize(fdt) + offset < struct_top) |
---|
216 | | - return 0; /* no more room :( */ |
---|
| 288 | + *allocated = 1; |
---|
217 | 289 | |
---|
218 | | - memcpy(strtab + offset, s, len); |
---|
219 | | - fdt_set_size_dt_strings(fdt, strtabsize + len); |
---|
220 | | - return offset; |
---|
| 290 | + return fdt_add_string_(fdt, s); |
---|
221 | 291 | } |
---|
222 | 292 | |
---|
223 | 293 | int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) |
---|
224 | 294 | { |
---|
225 | 295 | struct fdt_property *prop; |
---|
226 | 296 | int nameoff; |
---|
| 297 | + int allocated; |
---|
227 | 298 | |
---|
228 | | - FDT_SW_CHECK_HEADER(fdt); |
---|
| 299 | + FDT_SW_PROBE_STRUCT(fdt); |
---|
229 | 300 | |
---|
230 | | - nameoff = fdt_find_add_string_(fdt, name); |
---|
| 301 | + /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */ |
---|
| 302 | + if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) { |
---|
| 303 | + allocated = 1; |
---|
| 304 | + nameoff = fdt_add_string_(fdt, name); |
---|
| 305 | + } else { |
---|
| 306 | + nameoff = fdt_find_add_string_(fdt, name, &allocated); |
---|
| 307 | + } |
---|
231 | 308 | if (nameoff == 0) |
---|
232 | 309 | return -FDT_ERR_NOSPACE; |
---|
233 | 310 | |
---|
234 | 311 | prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); |
---|
235 | | - if (! prop) |
---|
| 312 | + if (! prop) { |
---|
| 313 | + if (allocated) |
---|
| 314 | + fdt_del_last_string_(fdt, name); |
---|
236 | 315 | return -FDT_ERR_NOSPACE; |
---|
| 316 | + } |
---|
237 | 317 | |
---|
238 | 318 | prop->tag = cpu_to_fdt32(FDT_PROP); |
---|
239 | 319 | prop->nameoff = cpu_to_fdt32(nameoff); |
---|
.. | .. |
---|
262 | 342 | uint32_t tag; |
---|
263 | 343 | int offset, nextoffset; |
---|
264 | 344 | |
---|
265 | | - FDT_SW_CHECK_HEADER(fdt); |
---|
| 345 | + FDT_SW_PROBE_STRUCT(fdt); |
---|
266 | 346 | |
---|
267 | 347 | /* Add terminator */ |
---|
268 | 348 | end = fdt_grab_space_(fdt, sizeof(*end)); |
---|
.. | .. |
---|
295 | 375 | |
---|
296 | 376 | /* Finally, adjust the header */ |
---|
297 | 377 | fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); |
---|
| 378 | + |
---|
| 379 | + /* And fix up fields that were keeping intermediate state. */ |
---|
| 380 | + fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); |
---|
298 | 381 | fdt_set_magic(fdt, FDT_MAGIC); |
---|
| 382 | + |
---|
299 | 383 | return 0; |
---|
300 | 384 | } |
---|