fdtput.c revision 261215
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 */
32enum oper_type {
33	OPER_WRITE_PROP,		/* Write a property in a node */
34	OPER_CREATE_NODE,		/* Create a new node */
35};
36
37struct 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 */
53static 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 */
70static 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
134#define ALIGN(x)		(((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1))
135
136static char *_realloc_fdt(char *fdt, int delta)
137{
138	int new_sz = fdt_totalsize(fdt) + delta;
139	fdt = xrealloc(fdt, new_sz);
140	fdt_open_into(fdt, fdt, new_sz);
141	return fdt;
142}
143
144static char *realloc_node(char *fdt, const char *name)
145{
146	int delta;
147	/* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */
148	delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1)
149			+ FDT_TAGSIZE;
150	return _realloc_fdt(fdt, delta);
151}
152
153static char *realloc_property(char *fdt, int nodeoffset,
154		const char *name, int newlen)
155{
156	int delta = 0;
157	int oldlen = 0;
158
159	if (!fdt_get_property(fdt, nodeoffset, name, &oldlen))
160		/* strings + property header */
161		delta = sizeof(struct fdt_property) + strlen(name) + 1;
162
163	if (newlen > oldlen)
164		/* actual value in off_struct */
165		delta += ALIGN(newlen) - ALIGN(oldlen);
166
167	return _realloc_fdt(fdt, delta);
168}
169
170static int store_key_value(char **blob, const char *node_name,
171		const char *property, const char *buf, int len)
172{
173	int node;
174	int err;
175
176	node = fdt_path_offset(*blob, node_name);
177	if (node < 0) {
178		report_error(node_name, -1, node);
179		return -1;
180	}
181
182	err = fdt_setprop(*blob, node, property, buf, len);
183	if (err == -FDT_ERR_NOSPACE) {
184		*blob = realloc_property(*blob, node, property, len);
185		err = fdt_setprop(*blob, node, property, buf, len);
186	}
187	if (err) {
188		report_error(property, -1, err);
189		return -1;
190	}
191	return 0;
192}
193
194/**
195 * Create paths as needed for all components of a path
196 *
197 * Any components of the path that do not exist are created. Errors are
198 * reported.
199 *
200 * @param blob		FDT blob to write into
201 * @param in_path	Path to process
202 * @return 0 if ok, -1 on error
203 */
204static int create_paths(char **blob, const char *in_path)
205{
206	const char *path = in_path;
207	const char *sep;
208	int node, offset = 0;
209
210	/* skip leading '/' */
211	while (*path == '/')
212		path++;
213
214	for (sep = path; *sep; path = sep + 1, offset = node) {
215		/* equivalent to strchrnul(), but it requires _GNU_SOURCE */
216		sep = strchr(path, '/');
217		if (!sep)
218			sep = path + strlen(path);
219
220		node = fdt_subnode_offset_namelen(*blob, offset, path,
221				sep - path);
222		if (node == -FDT_ERR_NOTFOUND) {
223			*blob = realloc_node(*blob, path);
224			node = fdt_add_subnode_namelen(*blob, offset, path,
225						       sep - path);
226		}
227		if (node < 0) {
228			report_error(path, sep - path, node);
229			return -1;
230		}
231	}
232
233	return 0;
234}
235
236/**
237 * Create a new node in the fdt.
238 *
239 * This will overwrite the node_name string. Any error is reported.
240 *
241 * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this.
242 *
243 * @param blob		FDT blob to write into
244 * @param node_name	Name of node to create
245 * @return new node offset if found, or -1 on failure
246 */
247static int create_node(char **blob, const char *node_name)
248{
249	int node = 0;
250	char *p;
251
252	p = strrchr(node_name, '/');
253	if (!p) {
254		report_error(node_name, -1, -FDT_ERR_BADPATH);
255		return -1;
256	}
257	*p = '\0';
258
259	*blob = realloc_node(*blob, p + 1);
260
261	if (p > node_name) {
262		node = fdt_path_offset(*blob, node_name);
263		if (node < 0) {
264			report_error(node_name, -1, node);
265			return -1;
266		}
267	}
268
269	node = fdt_add_subnode(*blob, node, p + 1);
270	if (node < 0) {
271		report_error(p + 1, -1, node);
272		return -1;
273	}
274
275	return 0;
276}
277
278static int do_fdtput(struct display_info *disp, const char *filename,
279		    char **arg, int arg_count)
280{
281	char *value;
282	char *blob;
283	int len, ret = 0;
284
285	blob = utilfdt_read(filename);
286	if (!blob)
287		return -1;
288
289	switch (disp->oper) {
290	case OPER_WRITE_PROP:
291		/*
292		 * Convert the arguments into a single binary value, then
293		 * store them into the property.
294		 */
295		assert(arg_count >= 2);
296		if (disp->auto_path && create_paths(&blob, *arg))
297			return -1;
298		if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
299			store_key_value(&blob, *arg, arg[1], value, len))
300			ret = -1;
301		break;
302	case OPER_CREATE_NODE:
303		for (; ret >= 0 && arg_count--; arg++) {
304			if (disp->auto_path)
305				ret = create_paths(&blob, *arg);
306			else
307				ret = create_node(&blob, *arg);
308		}
309		break;
310	}
311	if (ret >= 0) {
312		fdt_pack(blob);
313		ret = utilfdt_write(filename, blob);
314	}
315
316	free(blob);
317	return ret;
318}
319
320/* Usage related data. */
321static const char usage_synopsis[] =
322	"write a property value to a device tree\n"
323	"	fdtput <options> <dt file> <node> <property> [<value>...]\n"
324	"	fdtput -c <options> <dt file> [<node>...]\n"
325	"\n"
326	"The command line arguments are joined together into a single value.\n"
327	USAGE_TYPE_MSG;
328static const char usage_short_opts[] = "cpt:v" USAGE_COMMON_SHORT_OPTS;
329static struct option const usage_long_opts[] = {
330	{"create",           no_argument, NULL, 'c'},
331	{"auto-path",        no_argument, NULL, 'p'},
332	{"type",              a_argument, NULL, 't'},
333	{"verbose",          no_argument, NULL, 'v'},
334	USAGE_COMMON_LONG_OPTS,
335};
336static const char * const usage_opts_help[] = {
337	"Create nodes if they don't already exist",
338	"Automatically create nodes as needed for the node path",
339	"Type of data",
340	"Display each value decoded from command line",
341	USAGE_COMMON_OPTS_HELP
342};
343
344int main(int argc, char *argv[])
345{
346	int opt;
347	struct display_info disp;
348	char *filename = NULL;
349
350	memset(&disp, '\0', sizeof(disp));
351	disp.size = -1;
352	disp.oper = OPER_WRITE_PROP;
353	while ((opt = util_getopt_long()) != EOF) {
354		/*
355		 * TODO: add options to:
356		 * - delete property
357		 * - delete node (optionally recursively)
358		 * - rename node
359		 * - pack fdt before writing
360		 * - set amount of free space when writing
361		 */
362		switch (opt) {
363		case_USAGE_COMMON_FLAGS
364
365		case 'c':
366			disp.oper = OPER_CREATE_NODE;
367			break;
368		case 'p':
369			disp.auto_path = 1;
370			break;
371		case 't':
372			if (utilfdt_decode_type(optarg, &disp.type,
373					&disp.size))
374				usage("Invalid type string");
375			break;
376
377		case 'v':
378			disp.verbose = 1;
379			break;
380		}
381	}
382
383	if (optind < argc)
384		filename = argv[optind++];
385	if (!filename)
386		usage("missing filename");
387
388	argv += optind;
389	argc -= optind;
390
391	if (disp.oper == OPER_WRITE_PROP) {
392		if (argc < 1)
393			usage("missing node");
394		if (argc < 2)
395			usage("missing property");
396	}
397
398	if (do_fdtput(&disp, filename, argv, argc))
399		return 1;
400	return 0;
401}
402