Home | History | Annotate | Download | only in dtc
      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  */
     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  */
     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 			int *iptr = (int *)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 
    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 
    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 
    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 
    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  */
    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  */
    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  */
    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  */
    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 
    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);
    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 
    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