xref: /netbsd-src/external/gpl2/dtc/dist/fdtput.c (revision 8ecbf5f02b752fcb7debe1a8fab1dc82602bc760)
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  */
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  */
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 
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 
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 
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 
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  */
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  */
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  */
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  */
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 
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 
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