1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
4 */
5
6 #include <assert.h>
7 #include <ctype.h>
8 #include <getopt.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13 #include <libfdt.h>
14
15 #include "util.h"
16
17 /* These are the operations we support */
18 enum oper_type {
19 OPER_WRITE_PROP, /* Write a property in a node */
20 OPER_CREATE_NODE, /* Create a new node */
21 OPER_REMOVE_NODE, /* Delete a node */
22 OPER_DELETE_PROP, /* Delete a property in a node */
23 };
24
25 struct display_info {
26 enum oper_type oper; /* operation to perform */
27 int type; /* data type (s/i/u/x or 0 for default) */
28 int size; /* data size (1/2/4) */
29 int verbose; /* verbose output */
30 int auto_path; /* automatically create all path components */
31 };
32
33
34 /**
35 * Report an error with a particular node.
36 *
37 * @param name Node name to report error on
38 * @param namelen Length of node name, or -1 to use entire string
39 * @param err Error number to report (-FDT_ERR_...)
40 */
report_error(const char * name,int namelen,int err)41 static void report_error(const char *name, int namelen, int err)
42 {
43 if (namelen == -1)
44 namelen = strlen(name);
45 fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name,
46 fdt_strerror(err));
47 }
48
49 /**
50 * Encode a series of arguments in a property value.
51 *
52 * @param disp Display information / options
53 * @param arg List of arguments from command line
54 * @param arg_count Number of arguments (may be 0)
55 * @param valuep Returns buffer containing value
56 * @param value_len Returns length of value encoded
57 */
encode_value(struct display_info * disp,char ** arg,int arg_count,char ** valuep,int * value_len)58 static int encode_value(struct display_info *disp, char **arg, int arg_count,
59 char **valuep, int *value_len)
60 {
61 char *value = NULL; /* holding area for value */
62 int value_size = 0; /* size of holding area */
63 char *ptr; /* pointer to current value position */
64 int len; /* length of this cell/string/byte */
65 int ival;
66 int upto; /* the number of bytes we have written to buf */
67 char fmt[3];
68
69 upto = 0;
70
71 if (disp->verbose)
72 fprintf(stderr, "Decoding value:\n");
73
74 fmt[0] = '%';
75 fmt[1] = disp->type ? disp->type : 'd';
76 fmt[2] = '\0';
77 for (; arg_count > 0; arg++, arg_count--, upto += len) {
78 /* assume integer unless told otherwise */
79 if (disp->type == 's')
80 len = strlen(*arg) + 1;
81 else
82 len = disp->size == -1 ? 4 : disp->size;
83
84 /* enlarge our value buffer by a suitable margin if needed */
85 if (upto + len > value_size) {
86 value_size = (upto + len) + 500;
87 value = xrealloc(value, value_size);
88 }
89
90 ptr = value + upto;
91 if (disp->type == 's') {
92 memcpy(ptr, *arg, len);
93 if (disp->verbose)
94 fprintf(stderr, "\tstring: '%s'\n", ptr);
95 } else {
96 fdt32_t *iptr = (fdt32_t *)ptr;
97 sscanf(*arg, fmt, &ival);
98 if (len == 4)
99 *iptr = cpu_to_fdt32(ival);
100 else
101 *ptr = (uint8_t)ival;
102 if (disp->verbose) {
103 fprintf(stderr, "\t%s: %d\n",
104 disp->size == 1 ? "byte" :
105 disp->size == 2 ? "short" : "int",
106 ival);
107 }
108 }
109 }
110 *value_len = upto;
111 *valuep = value;
112 if (disp->verbose)
113 fprintf(stderr, "Value size %d\n", upto);
114 return 0;
115 }
116
117 #define ALIGN(x) (((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1))
118
realloc_fdt(char * fdt,int delta)119 static char *realloc_fdt(char *fdt, int delta)
120 {
121 int new_sz = fdt_totalsize(fdt) + delta;
122 fdt = xrealloc(fdt, new_sz);
123 fdt_open_into(fdt, fdt, new_sz);
124 return fdt;
125 }
126
realloc_node(char * fdt,const char * name)127 static char *realloc_node(char *fdt, const char *name)
128 {
129 int delta;
130 /* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */
131 delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1)
132 + FDT_TAGSIZE;
133 return realloc_fdt(fdt, delta);
134 }
135
realloc_property(char * fdt,int nodeoffset,const char * name,int newlen)136 static char *realloc_property(char *fdt, int nodeoffset,
137 const char *name, int newlen)
138 {
139 int delta = 0;
140 int oldlen = 0;
141
142 if (!fdt_get_property(fdt, nodeoffset, name, &oldlen))
143 /* strings + property header */
144 delta = sizeof(struct fdt_property) + strlen(name) + 1;
145
146 if (newlen > oldlen)
147 /* actual value in off_struct */
148 delta += ALIGN(newlen) - ALIGN(oldlen);
149
150 return realloc_fdt(fdt, delta);
151 }
152
store_key_value(char ** blob,const char * node_name,const char * property,const char * buf,int len)153 static int store_key_value(char **blob, const char *node_name,
154 const char *property, const char *buf, int len)
155 {
156 int node;
157 int err;
158
159 node = fdt_path_offset(*blob, node_name);
160 if (node < 0) {
161 report_error(node_name, -1, node);
162 return -1;
163 }
164
165 err = fdt_setprop(*blob, node, property, buf, len);
166 if (err == -FDT_ERR_NOSPACE) {
167 *blob = realloc_property(*blob, node, property, len);
168 err = fdt_setprop(*blob, node, property, buf, len);
169 }
170 if (err) {
171 report_error(property, -1, err);
172 return -1;
173 }
174 return 0;
175 }
176
177 /**
178 * Create paths as needed for all components of a path
179 *
180 * Any components of the path that do not exist are created. Errors are
181 * reported.
182 *
183 * @param blob FDT blob to write into
184 * @param in_path Path to process
185 * @return 0 if ok, -1 on error
186 */
create_paths(char ** blob,const char * in_path)187 static int create_paths(char **blob, const char *in_path)
188 {
189 const char *path = in_path;
190 const char *sep;
191 int node, offset = 0;
192
193 /* skip leading '/' */
194 while (*path == '/')
195 path++;
196
197 for (sep = path; *sep; path = sep + 1, offset = node) {
198 /* equivalent to strchrnul(), but it requires _GNU_SOURCE */
199 sep = strchr(path, '/');
200 if (!sep)
201 sep = path + strlen(path);
202
203 node = fdt_subnode_offset_namelen(*blob, offset, path,
204 sep - path);
205 if (node == -FDT_ERR_NOTFOUND) {
206 *blob = realloc_node(*blob, path);
207 node = fdt_add_subnode_namelen(*blob, offset, path,
208 sep - path);
209 }
210 if (node < 0) {
211 report_error(path, sep - path, node);
212 return -1;
213 }
214 }
215
216 return 0;
217 }
218
219 /**
220 * Create a new node in the fdt.
221 *
222 * This will overwrite the node_name string. Any error is reported.
223 *
224 * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this.
225 *
226 * @param blob FDT blob to write into
227 * @param node_name Name of node to create
228 * @return new node offset if found, or -1 on failure
229 */
create_node(char ** blob,const char * node_name)230 static int create_node(char **blob, const char *node_name)
231 {
232 int node = 0;
233 char *p;
234
235 p = strrchr(node_name, '/');
236 if (!p) {
237 report_error(node_name, -1, -FDT_ERR_BADPATH);
238 return -1;
239 }
240 *p = '\0';
241
242 *blob = realloc_node(*blob, p + 1);
243
244 if (p > node_name) {
245 node = fdt_path_offset(*blob, node_name);
246 if (node < 0) {
247 report_error(node_name, -1, node);
248 return -1;
249 }
250 }
251
252 node = fdt_add_subnode(*blob, node, p + 1);
253 if (node < 0) {
254 report_error(p + 1, -1, node);
255 return -1;
256 }
257
258 return 0;
259 }
260
261 /**
262 * Delete a property of a node in the fdt.
263 *
264 * @param blob FDT blob to write into
265 * @param node_name Path to node containing the property to delete
266 * @param prop_name Name of property to delete
267 * @return 0 on success, or -1 on failure
268 */
delete_prop(char * blob,const char * node_name,const char * prop_name)269 static int delete_prop(char *blob, const char *node_name, const char *prop_name)
270 {
271 int node = 0;
272
273 node = fdt_path_offset(blob, node_name);
274 if (node < 0) {
275 report_error(node_name, -1, node);
276 return -1;
277 }
278
279 node = fdt_delprop(blob, node, prop_name);
280 if (node < 0) {
281 report_error(node_name, -1, node);
282 return -1;
283 }
284
285 return 0;
286 }
287
288 /**
289 * Delete a node in the fdt.
290 *
291 * @param blob FDT blob to write into
292 * @param node_name Name of node to delete
293 * @return 0 on success, or -1 on failure
294 */
delete_node(char * blob,const char * node_name)295 static int delete_node(char *blob, const char *node_name)
296 {
297 int node = 0;
298
299 node = fdt_path_offset(blob, node_name);
300 if (node < 0) {
301 report_error(node_name, -1, node);
302 return -1;
303 }
304
305 node = fdt_del_node(blob, node);
306 if (node < 0) {
307 report_error(node_name, -1, node);
308 return -1;
309 }
310
311 return 0;
312 }
313
do_fdtput(struct display_info * disp,const char * filename,char ** arg,int arg_count)314 static int do_fdtput(struct display_info *disp, const char *filename,
315 char **arg, int arg_count)
316 {
317 char *value = NULL;
318 char *blob;
319 char *node;
320 int len, ret = 0;
321
322 blob = utilfdt_read(filename, NULL);
323 if (!blob)
324 return -1;
325
326 switch (disp->oper) {
327 case OPER_WRITE_PROP:
328 /*
329 * Convert the arguments into a single binary value, then
330 * store them into the property.
331 */
332 assert(arg_count >= 2);
333 if (disp->auto_path && create_paths(&blob, *arg))
334 return -1;
335 if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
336 store_key_value(&blob, *arg, arg[1], value, len))
337 ret = -1;
338 break;
339 case OPER_CREATE_NODE:
340 for (; ret >= 0 && arg_count--; arg++) {
341 if (disp->auto_path)
342 ret = create_paths(&blob, *arg);
343 else
344 ret = create_node(&blob, *arg);
345 }
346 break;
347 case OPER_REMOVE_NODE:
348 for (; ret >= 0 && arg_count--; arg++)
349 ret = delete_node(blob, *arg);
350 break;
351 case OPER_DELETE_PROP:
352 node = *arg;
353 for (arg++; ret >= 0 && arg_count-- > 1; arg++)
354 ret = delete_prop(blob, node, *arg);
355 break;
356 }
357 if (ret >= 0) {
358 fdt_pack(blob);
359 ret = utilfdt_write(filename, blob);
360 }
361
362 free(blob);
363
364 if (value) {
365 free(value);
366 }
367
368 return ret;
369 }
370
371 /* Usage related data. */
372 static const char usage_synopsis[] =
373 "write a property value to a device tree\n"
374 " fdtput <options> <dt file> <node> <property> [<value>...]\n"
375 " fdtput -c <options> <dt file> [<node>...]\n"
376 " fdtput -r <options> <dt file> [<node>...]\n"
377 " fdtput -d <options> <dt file> <node> [<property>...]\n"
378 "\n"
379 "The command line arguments are joined together into a single value.\n"
380 USAGE_TYPE_MSG;
381 static const char usage_short_opts[] = "crdpt:v" USAGE_COMMON_SHORT_OPTS;
382 static struct option const usage_long_opts[] = {
383 {"create", no_argument, NULL, 'c'},
384 {"remove", no_argument, NULL, 'r'},
385 {"delete", no_argument, NULL, 'd'},
386 {"auto-path", no_argument, NULL, 'p'},
387 {"type", a_argument, NULL, 't'},
388 {"verbose", no_argument, NULL, 'v'},
389 USAGE_COMMON_LONG_OPTS,
390 };
391 static const char * const usage_opts_help[] = {
392 "Create nodes if they don't already exist",
393 "Delete nodes (and any subnodes) if they already exist",
394 "Delete properties if they already exist",
395 "Automatically create nodes as needed for the node path",
396 "Type of data",
397 "Display each value decoded from command line",
398 USAGE_COMMON_OPTS_HELP
399 };
400
main(int argc,char * argv[])401 int main(int argc, char *argv[])
402 {
403 int opt;
404 struct display_info disp;
405 char *filename = NULL;
406
407 memset(&disp, '\0', sizeof(disp));
408 disp.size = -1;
409 disp.oper = OPER_WRITE_PROP;
410 while ((opt = util_getopt_long()) != EOF) {
411 /*
412 * TODO: add options to:
413 * - rename node
414 * - pack fdt before writing
415 * - set amount of free space when writing
416 */
417 switch (opt) {
418 case_USAGE_COMMON_FLAGS
419
420 case 'c':
421 disp.oper = OPER_CREATE_NODE;
422 break;
423 case 'r':
424 disp.oper = OPER_REMOVE_NODE;
425 break;
426 case 'd':
427 disp.oper = OPER_DELETE_PROP;
428 break;
429 case 'p':
430 disp.auto_path = 1;
431 break;
432 case 't':
433 if (utilfdt_decode_type(optarg, &disp.type,
434 &disp.size))
435 usage("Invalid type string");
436 break;
437
438 case 'v':
439 disp.verbose = 1;
440 break;
441 }
442 }
443
444 if (optind < argc)
445 filename = argv[optind++];
446 if (!filename)
447 usage("missing filename");
448
449 argv += optind;
450 argc -= optind;
451
452 if (disp.oper == OPER_WRITE_PROP) {
453 if (argc < 1)
454 usage("missing node");
455 if (argc < 2)
456 usage("missing property");
457 }
458
459 if (disp.oper == OPER_DELETE_PROP)
460 if (argc < 1)
461 usage("missing node");
462
463 if (do_fdtput(&disp, filename, argv, argc))
464 return 1;
465 return 0;
466 }
467