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