• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 };
36 
37 struct display_info {
38 	enum oper_type oper;	/* operation to perform */
39 	int type;		/* data type (s/i/u/x or 0 for default) */
40 	int size;		/* data size (1/2/4) */
41 	int verbose;		/* verbose output */
42 	int auto_path;		/* automatically create all path components */
43 };
44 
45 
46 /**
47  * Report an error with a particular node.
48  *
49  * @param name		Node name to report error on
50  * @param namelen	Length of node name, or -1 to use entire string
51  * @param err		Error number to report (-FDT_ERR_...)
52  */
report_error(const char * name,int namelen,int err)53 static void report_error(const char *name, int namelen, int err)
54 {
55 	if (namelen == -1)
56 		namelen = strlen(name);
57 	fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name,
58 		fdt_strerror(err));
59 }
60 
61 /**
62  * Encode a series of arguments in a property value.
63  *
64  * @param disp		Display information / options
65  * @param arg		List of arguments from command line
66  * @param arg_count	Number of arguments (may be 0)
67  * @param valuep	Returns buffer containing value
68  * @param *value_len	Returns length of value encoded
69  */
encode_value(struct display_info * disp,char ** arg,int arg_count,char ** valuep,int * value_len)70 static int encode_value(struct display_info *disp, char **arg, int arg_count,
71 			char **valuep, int *value_len)
72 {
73 	char *value = NULL;	/* holding area for value */
74 	int value_size = 0;	/* size of holding area */
75 	char *ptr;		/* pointer to current value position */
76 	int len;		/* length of this cell/string/byte */
77 	int ival;
78 	int upto;	/* the number of bytes we have written to buf */
79 	char fmt[3];
80 
81 	upto = 0;
82 
83 	if (disp->verbose)
84 		fprintf(stderr, "Decoding value:\n");
85 
86 	fmt[0] = '%';
87 	fmt[1] = disp->type ? disp->type : 'd';
88 	fmt[2] = '\0';
89 	for (; arg_count > 0; arg++, arg_count--, upto += len) {
90 		/* assume integer unless told otherwise */
91 		if (disp->type == 's')
92 			len = strlen(*arg) + 1;
93 		else
94 			len = disp->size == -1 ? 4 : disp->size;
95 
96 		/* enlarge our value buffer by a suitable margin if needed */
97 		if (upto + len > value_size) {
98 			value_size = (upto + len) + 500;
99 			value = realloc(value, value_size);
100 			if (!value) {
101 				fprintf(stderr, "Out of mmory: cannot alloc "
102 					"%d bytes\n", value_size);
103 				return -1;
104 			}
105 		}
106 
107 		ptr = value + upto;
108 		if (disp->type == 's') {
109 			memcpy(ptr, *arg, len);
110 			if (disp->verbose)
111 				fprintf(stderr, "\tstring: '%s'\n", ptr);
112 		} else {
113 			int *iptr = (int *)ptr;
114 			sscanf(*arg, fmt, &ival);
115 			if (len == 4)
116 				*iptr = cpu_to_fdt32(ival);
117 			else
118 				*ptr = (uint8_t)ival;
119 			if (disp->verbose) {
120 				fprintf(stderr, "\t%s: %d\n",
121 					disp->size == 1 ? "byte" :
122 					disp->size == 2 ? "short" : "int",
123 					ival);
124 			}
125 		}
126 	}
127 	*value_len = upto;
128 	*valuep = value;
129 	if (disp->verbose)
130 		fprintf(stderr, "Value size %d\n", upto);
131 	return 0;
132 }
133 
store_key_value(void * blob,const char * node_name,const char * property,const char * buf,int len)134 static int store_key_value(void *blob, const char *node_name,
135 		const char *property, const char *buf, int len)
136 {
137 	int node;
138 	int err;
139 
140 	node = fdt_path_offset(blob, node_name);
141 	if (node < 0) {
142 		report_error(node_name, -1, node);
143 		return -1;
144 	}
145 
146 	err = fdt_setprop(blob, node, property, buf, len);
147 	if (err) {
148 		report_error(property, -1, err);
149 		return -1;
150 	}
151 	return 0;
152 }
153 
154 /**
155  * Create paths as needed for all components of a path
156  *
157  * Any components of the path that do not exist are created. Errors are
158  * reported.
159  *
160  * @param blob		FDT blob to write into
161  * @param in_path	Path to process
162  * @return 0 if ok, -1 on error
163  */
create_paths(void * blob,const char * in_path)164 static int create_paths(void *blob, const char *in_path)
165 {
166 	const char *path = in_path;
167 	const char *sep;
168 	int node, offset = 0;
169 
170 	/* skip leading '/' */
171 	while (*path == '/')
172 		path++;
173 
174 	for (sep = path; *sep; path = sep + 1, offset = node) {
175 		/* equivalent to strchrnul(), but it requires _GNU_SOURCE */
176 		sep = strchr(path, '/');
177 		if (!sep)
178 			sep = path + strlen(path);
179 
180 		node = fdt_subnode_offset_namelen(blob, offset, path,
181 				sep - path);
182 		if (node == -FDT_ERR_NOTFOUND) {
183 			node = fdt_add_subnode_namelen(blob, offset, path,
184 						       sep - path);
185 		}
186 		if (node < 0) {
187 			report_error(path, sep - path, node);
188 			return -1;
189 		}
190 	}
191 
192 	return 0;
193 }
194 
195 /**
196  * Create a new node in the fdt.
197  *
198  * This will overwrite the node_name string. Any error is reported.
199  *
200  * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this.
201  *
202  * @param blob		FDT blob to write into
203  * @param node_name	Name of node to create
204  * @return new node offset if found, or -1 on failure
205  */
create_node(void * blob,const char * node_name)206 static int create_node(void *blob, const char *node_name)
207 {
208 	int node = 0;
209 	char *p;
210 
211 	p = strrchr(node_name, '/');
212 	if (!p) {
213 		report_error(node_name, -1, -FDT_ERR_BADPATH);
214 		return -1;
215 	}
216 	*p = '\0';
217 
218 	if (p > node_name) {
219 		node = fdt_path_offset(blob, node_name);
220 		if (node < 0) {
221 			report_error(node_name, -1, node);
222 			return -1;
223 		}
224 	}
225 
226 	node = fdt_add_subnode(blob, node, p + 1);
227 	if (node < 0) {
228 		report_error(p + 1, -1, node);
229 		return -1;
230 	}
231 
232 	return 0;
233 }
234 
do_fdtput(struct display_info * disp,const char * filename,char ** arg,int arg_count)235 static int do_fdtput(struct display_info *disp, const char *filename,
236 		    char **arg, int arg_count)
237 {
238 	char *value;
239 	char *blob;
240 	int len, ret = 0;
241 
242 	blob = utilfdt_read(filename);
243 	if (!blob)
244 		return -1;
245 
246 	switch (disp->oper) {
247 	case OPER_WRITE_PROP:
248 		/*
249 		 * Convert the arguments into a single binary value, then
250 		 * store them into the property.
251 		 */
252 		assert(arg_count >= 2);
253 		if (disp->auto_path && create_paths(blob, *arg))
254 			return -1;
255 		if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
256 			store_key_value(blob, *arg, arg[1], value, len))
257 			ret = -1;
258 		break;
259 	case OPER_CREATE_NODE:
260 		for (; ret >= 0 && arg_count--; arg++) {
261 			if (disp->auto_path)
262 				ret = create_paths(blob, *arg);
263 			else
264 				ret = create_node(blob, *arg);
265 		}
266 		break;
267 	}
268 	if (ret >= 0)
269 		ret = utilfdt_write(filename, blob);
270 
271 	free(blob);
272 	return ret;
273 }
274 
275 static const char *usage_msg =
276 	"fdtput - write a property value to a device tree\n"
277 	"\n"
278 	"The command line arguments are joined together into a single value.\n"
279 	"\n"
280 	"Usage:\n"
281 	"	fdtput <options> <dt file> <node> <property> [<value>...]\n"
282 	"	fdtput -c <options> <dt file> [<node>...]\n"
283 	"Options:\n"
284 	"\t-c\t\tCreate nodes if they don't already exist\n"
285 	"\t-p\t\tAutomatically create nodes as needed for the node path\n"
286 	"\t-t <type>\tType of data\n"
287 	"\t-v\t\tVerbose: display each value decoded from command line\n"
288 	"\t-h\t\tPrint this help\n\n"
289 	USAGE_TYPE_MSG;
290 
usage(const char * msg)291 static void usage(const char *msg)
292 {
293 	if (msg)
294 		fprintf(stderr, "Error: %s\n\n", msg);
295 
296 	fprintf(stderr, "%s", usage_msg);
297 	exit(2);
298 }
299 
main(int argc,char * argv[])300 int main(int argc, char *argv[])
301 {
302 	struct display_info disp;
303 	char *filename = NULL;
304 
305 	memset(&disp, '\0', sizeof(disp));
306 	disp.size = -1;
307 	disp.oper = OPER_WRITE_PROP;
308 	for (;;) {
309 		int c = getopt(argc, argv, "chpt:v");
310 		if (c == -1)
311 			break;
312 
313 		/*
314 		 * TODO: add options to:
315 		 * - delete property
316 		 * - delete node (optionally recursively)
317 		 * - rename node
318 		 * - pack fdt before writing
319 		 * - set amount of free space when writing
320 		 * - expand fdt if value doesn't fit
321 		 */
322 		switch (c) {
323 		case 'c':
324 			disp.oper = OPER_CREATE_NODE;
325 			break;
326 		case 'h':
327 		case '?':
328 			usage(NULL);
329 		case 'p':
330 			disp.auto_path = 1;
331 			break;
332 		case 't':
333 			if (utilfdt_decode_type(optarg, &disp.type,
334 					&disp.size))
335 				usage("Invalid type string");
336 			break;
337 
338 		case 'v':
339 			disp.verbose = 1;
340 			break;
341 		}
342 	}
343 
344 	if (optind < argc)
345 		filename = argv[optind++];
346 	if (!filename)
347 		usage("Missing filename");
348 
349 	argv += optind;
350 	argc -= optind;
351 
352 	if (disp.oper == OPER_WRITE_PROP) {
353 		if (argc < 1)
354 			usage("Missing node");
355 		if (argc < 2)
356 			usage("Missing property");
357 	}
358 
359 	if (do_fdtput(&disp, filename, argv, argc))
360 		return 1;
361 	return 0;
362 }
363