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