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