fdtput.c revision 261215
1238737Simp/*
2238737Simp * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
3238737Simp *
4238737Simp * This program is free software; you can redistribute it and/or
5238737Simp * modify it under the terms of the GNU General Public License as
6238737Simp * published by the Free Software Foundation; either version 2 of
7238737Simp * the License, or (at your option) any later version.
8238737Simp *
9238737Simp * This program is distributed in the hope that it will be useful,
10238737Simp * but WITHOUT ANY WARRANTY; without even the implied warranty of
11238737Simp * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12238737Simp * GNU General Public License for more details.
13238737Simp *
14238737Simp * You should have received a copy of the GNU General Public License
15238737Simp * along with this program; if not, write to the Free Software
16238737Simp * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
17238737Simp * MA 02111-1307 USA
18238737Simp */
19238737Simp
20238737Simp#include <assert.h>
21238737Simp#include <ctype.h>
22238737Simp#include <getopt.h>
23238737Simp#include <stdio.h>
24238737Simp#include <stdlib.h>
25238737Simp#include <string.h>
26238737Simp
27238737Simp#include <libfdt.h>
28238737Simp
29238737Simp#include "util.h"
30238737Simp
31238737Simp/* These are the operations we support */
32238737Simpenum oper_type {
33238737Simp	OPER_WRITE_PROP,		/* Write a property in a node */
34238737Simp	OPER_CREATE_NODE,		/* Create a new node */
35238737Simp};
36238737Simp
37238737Simpstruct display_info {
38238737Simp	enum oper_type oper;	/* operation to perform */
39238737Simp	int type;		/* data type (s/i/u/x or 0 for default) */
40238737Simp	int size;		/* data size (1/2/4) */
41238737Simp	int verbose;		/* verbose output */
42238737Simp	int auto_path;		/* automatically create all path components */
43238737Simp};
44238737Simp
45238737Simp
46238737Simp/**
47238737Simp * Report an error with a particular node.
48238737Simp *
49238737Simp * @param name		Node name to report error on
50238737Simp * @param namelen	Length of node name, or -1 to use entire string
51238737Simp * @param err		Error number to report (-FDT_ERR_...)
52238737Simp */
53238737Simpstatic void report_error(const char *name, int namelen, int err)
54238737Simp{
55238737Simp	if (namelen == -1)
56238737Simp		namelen = strlen(name);
57238737Simp	fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name,
58238737Simp		fdt_strerror(err));
59238737Simp}
60238737Simp
61238737Simp/**
62238737Simp * Encode a series of arguments in a property value.
63238737Simp *
64238737Simp * @param disp		Display information / options
65238737Simp * @param arg		List of arguments from command line
66238737Simp * @param arg_count	Number of arguments (may be 0)
67238737Simp * @param valuep	Returns buffer containing value
68238737Simp * @param *value_len	Returns length of value encoded
69238737Simp */
70238737Simpstatic int encode_value(struct display_info *disp, char **arg, int arg_count,
71238737Simp			char **valuep, int *value_len)
72238737Simp{
73238737Simp	char *value = NULL;	/* holding area for value */
74238737Simp	int value_size = 0;	/* size of holding area */
75238737Simp	char *ptr;		/* pointer to current value position */
76238737Simp	int len;		/* length of this cell/string/byte */
77238737Simp	int ival;
78238737Simp	int upto;	/* the number of bytes we have written to buf */
79238737Simp	char fmt[3];
80238737Simp
81238737Simp	upto = 0;
82238737Simp
83238737Simp	if (disp->verbose)
84238737Simp		fprintf(stderr, "Decoding value:\n");
85238737Simp
86238737Simp	fmt[0] = '%';
87238737Simp	fmt[1] = disp->type ? disp->type : 'd';
88238737Simp	fmt[2] = '\0';
89238737Simp	for (; arg_count > 0; arg++, arg_count--, upto += len) {
90238737Simp		/* assume integer unless told otherwise */
91238737Simp		if (disp->type == 's')
92238737Simp			len = strlen(*arg) + 1;
93238737Simp		else
94238737Simp			len = disp->size == -1 ? 4 : disp->size;
95238737Simp
96238737Simp		/* enlarge our value buffer by a suitable margin if needed */
97238737Simp		if (upto + len > value_size) {
98238737Simp			value_size = (upto + len) + 500;
99238737Simp			value = realloc(value, value_size);
100238737Simp			if (!value) {
101238737Simp				fprintf(stderr, "Out of mmory: cannot alloc "
102238737Simp					"%d bytes\n", value_size);
103238737Simp				return -1;
104238737Simp			}
105238737Simp		}
106238737Simp
107238737Simp		ptr = value + upto;
108238737Simp		if (disp->type == 's') {
109238737Simp			memcpy(ptr, *arg, len);
110238737Simp			if (disp->verbose)
111238737Simp				fprintf(stderr, "\tstring: '%s'\n", ptr);
112238737Simp		} else {
113238737Simp			int *iptr = (int *)ptr;
114238737Simp			sscanf(*arg, fmt, &ival);
115238737Simp			if (len == 4)
116238737Simp				*iptr = cpu_to_fdt32(ival);
117238737Simp			else
118238737Simp				*ptr = (uint8_t)ival;
119238737Simp			if (disp->verbose) {
120238737Simp				fprintf(stderr, "\t%s: %d\n",
121238737Simp					disp->size == 1 ? "byte" :
122238737Simp					disp->size == 2 ? "short" : "int",
123238737Simp					ival);
124238737Simp			}
125238737Simp		}
126238737Simp	}
127238737Simp	*value_len = upto;
128238737Simp	*valuep = value;
129238737Simp	if (disp->verbose)
130238737Simp		fprintf(stderr, "Value size %d\n", upto);
131238737Simp	return 0;
132238737Simp}
133238737Simp
134261215Simp#define ALIGN(x)		(((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1))
135261215Simp
136261215Simpstatic char *_realloc_fdt(char *fdt, int delta)
137261215Simp{
138261215Simp	int new_sz = fdt_totalsize(fdt) + delta;
139261215Simp	fdt = xrealloc(fdt, new_sz);
140261215Simp	fdt_open_into(fdt, fdt, new_sz);
141261215Simp	return fdt;
142261215Simp}
143261215Simp
144261215Simpstatic char *realloc_node(char *fdt, const char *name)
145261215Simp{
146261215Simp	int delta;
147261215Simp	/* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */
148261215Simp	delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1)
149261215Simp			+ FDT_TAGSIZE;
150261215Simp	return _realloc_fdt(fdt, delta);
151261215Simp}
152261215Simp
153261215Simpstatic char *realloc_property(char *fdt, int nodeoffset,
154261215Simp		const char *name, int newlen)
155261215Simp{
156261215Simp	int delta = 0;
157261215Simp	int oldlen = 0;
158261215Simp
159261215Simp	if (!fdt_get_property(fdt, nodeoffset, name, &oldlen))
160261215Simp		/* strings + property header */
161261215Simp		delta = sizeof(struct fdt_property) + strlen(name) + 1;
162261215Simp
163261215Simp	if (newlen > oldlen)
164261215Simp		/* actual value in off_struct */
165261215Simp		delta += ALIGN(newlen) - ALIGN(oldlen);
166261215Simp
167261215Simp	return _realloc_fdt(fdt, delta);
168261215Simp}
169261215Simp
170261215Simpstatic int store_key_value(char **blob, const char *node_name,
171238737Simp		const char *property, const char *buf, int len)
172238737Simp{
173238737Simp	int node;
174238737Simp	int err;
175238737Simp
176261215Simp	node = fdt_path_offset(*blob, node_name);
177238737Simp	if (node < 0) {
178238737Simp		report_error(node_name, -1, node);
179238737Simp		return -1;
180238737Simp	}
181238737Simp
182261215Simp	err = fdt_setprop(*blob, node, property, buf, len);
183261215Simp	if (err == -FDT_ERR_NOSPACE) {
184261215Simp		*blob = realloc_property(*blob, node, property, len);
185261215Simp		err = fdt_setprop(*blob, node, property, buf, len);
186261215Simp	}
187238737Simp	if (err) {
188238737Simp		report_error(property, -1, err);
189238737Simp		return -1;
190238737Simp	}
191238737Simp	return 0;
192238737Simp}
193238737Simp
194238737Simp/**
195238737Simp * Create paths as needed for all components of a path
196238737Simp *
197238737Simp * Any components of the path that do not exist are created. Errors are
198238737Simp * reported.
199238737Simp *
200238737Simp * @param blob		FDT blob to write into
201238737Simp * @param in_path	Path to process
202238737Simp * @return 0 if ok, -1 on error
203238737Simp */
204261215Simpstatic int create_paths(char **blob, const char *in_path)
205238737Simp{
206238737Simp	const char *path = in_path;
207238737Simp	const char *sep;
208238737Simp	int node, offset = 0;
209238737Simp
210238737Simp	/* skip leading '/' */
211238737Simp	while (*path == '/')
212238737Simp		path++;
213238737Simp
214238737Simp	for (sep = path; *sep; path = sep + 1, offset = node) {
215238737Simp		/* equivalent to strchrnul(), but it requires _GNU_SOURCE */
216238737Simp		sep = strchr(path, '/');
217238737Simp		if (!sep)
218238737Simp			sep = path + strlen(path);
219238737Simp
220261215Simp		node = fdt_subnode_offset_namelen(*blob, offset, path,
221238737Simp				sep - path);
222238737Simp		if (node == -FDT_ERR_NOTFOUND) {
223261215Simp			*blob = realloc_node(*blob, path);
224261215Simp			node = fdt_add_subnode_namelen(*blob, offset, path,
225238737Simp						       sep - path);
226238737Simp		}
227238737Simp		if (node < 0) {
228238737Simp			report_error(path, sep - path, node);
229238737Simp			return -1;
230238737Simp		}
231238737Simp	}
232238737Simp
233238737Simp	return 0;
234238737Simp}
235238737Simp
236238737Simp/**
237238737Simp * Create a new node in the fdt.
238238737Simp *
239238737Simp * This will overwrite the node_name string. Any error is reported.
240238737Simp *
241238737Simp * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this.
242238737Simp *
243238737Simp * @param blob		FDT blob to write into
244238737Simp * @param node_name	Name of node to create
245238737Simp * @return new node offset if found, or -1 on failure
246238737Simp */
247261215Simpstatic int create_node(char **blob, const char *node_name)
248238737Simp{
249238737Simp	int node = 0;
250238737Simp	char *p;
251238737Simp
252238737Simp	p = strrchr(node_name, '/');
253238737Simp	if (!p) {
254238737Simp		report_error(node_name, -1, -FDT_ERR_BADPATH);
255238737Simp		return -1;
256238737Simp	}
257238737Simp	*p = '\0';
258238737Simp
259261215Simp	*blob = realloc_node(*blob, p + 1);
260261215Simp
261238737Simp	if (p > node_name) {
262261215Simp		node = fdt_path_offset(*blob, node_name);
263238737Simp		if (node < 0) {
264238737Simp			report_error(node_name, -1, node);
265238737Simp			return -1;
266238737Simp		}
267238737Simp	}
268238737Simp
269261215Simp	node = fdt_add_subnode(*blob, node, p + 1);
270238737Simp	if (node < 0) {
271238737Simp		report_error(p + 1, -1, node);
272238737Simp		return -1;
273238737Simp	}
274238737Simp
275238737Simp	return 0;
276238737Simp}
277238737Simp
278238737Simpstatic int do_fdtput(struct display_info *disp, const char *filename,
279238737Simp		    char **arg, int arg_count)
280238737Simp{
281238737Simp	char *value;
282238737Simp	char *blob;
283238737Simp	int len, ret = 0;
284238737Simp
285238737Simp	blob = utilfdt_read(filename);
286238737Simp	if (!blob)
287238737Simp		return -1;
288238737Simp
289238737Simp	switch (disp->oper) {
290238737Simp	case OPER_WRITE_PROP:
291238737Simp		/*
292238737Simp		 * Convert the arguments into a single binary value, then
293238737Simp		 * store them into the property.
294238737Simp		 */
295238737Simp		assert(arg_count >= 2);
296261215Simp		if (disp->auto_path && create_paths(&blob, *arg))
297238737Simp			return -1;
298238737Simp		if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
299261215Simp			store_key_value(&blob, *arg, arg[1], value, len))
300238737Simp			ret = -1;
301238737Simp		break;
302238737Simp	case OPER_CREATE_NODE:
303238737Simp		for (; ret >= 0 && arg_count--; arg++) {
304238737Simp			if (disp->auto_path)
305261215Simp				ret = create_paths(&blob, *arg);
306238737Simp			else
307261215Simp				ret = create_node(&blob, *arg);
308238737Simp		}
309238737Simp		break;
310238737Simp	}
311261215Simp	if (ret >= 0) {
312261215Simp		fdt_pack(blob);
313238737Simp		ret = utilfdt_write(filename, blob);
314261215Simp	}
315238737Simp
316238737Simp	free(blob);
317238737Simp	return ret;
318238737Simp}
319238737Simp
320261215Simp/* Usage related data. */
321261215Simpstatic const char usage_synopsis[] =
322261215Simp	"write a property value to a device tree\n"
323261215Simp	"	fdtput <options> <dt file> <node> <property> [<value>...]\n"
324261215Simp	"	fdtput -c <options> <dt file> [<node>...]\n"
325238737Simp	"\n"
326238737Simp	"The command line arguments are joined together into a single value.\n"
327238737Simp	USAGE_TYPE_MSG;
328261215Simpstatic const char usage_short_opts[] = "cpt:v" USAGE_COMMON_SHORT_OPTS;
329261215Simpstatic struct option const usage_long_opts[] = {
330261215Simp	{"create",           no_argument, NULL, 'c'},
331261215Simp	{"auto-path",        no_argument, NULL, 'p'},
332261215Simp	{"type",              a_argument, NULL, 't'},
333261215Simp	{"verbose",          no_argument, NULL, 'v'},
334261215Simp	USAGE_COMMON_LONG_OPTS,
335261215Simp};
336261215Simpstatic const char * const usage_opts_help[] = {
337261215Simp	"Create nodes if they don't already exist",
338261215Simp	"Automatically create nodes as needed for the node path",
339261215Simp	"Type of data",
340261215Simp	"Display each value decoded from command line",
341261215Simp	USAGE_COMMON_OPTS_HELP
342261215Simp};
343238737Simp
344238737Simpint main(int argc, char *argv[])
345238737Simp{
346261215Simp	int opt;
347238737Simp	struct display_info disp;
348238737Simp	char *filename = NULL;
349238737Simp
350238737Simp	memset(&disp, '\0', sizeof(disp));
351238737Simp	disp.size = -1;
352238737Simp	disp.oper = OPER_WRITE_PROP;
353261215Simp	while ((opt = util_getopt_long()) != EOF) {
354238737Simp		/*
355238737Simp		 * TODO: add options to:
356238737Simp		 * - delete property
357238737Simp		 * - delete node (optionally recursively)
358238737Simp		 * - rename node
359238737Simp		 * - pack fdt before writing
360238737Simp		 * - set amount of free space when writing
361238737Simp		 */
362261215Simp		switch (opt) {
363261215Simp		case_USAGE_COMMON_FLAGS
364261215Simp
365238737Simp		case 'c':
366238737Simp			disp.oper = OPER_CREATE_NODE;
367238737Simp			break;
368238737Simp		case 'p':
369238737Simp			disp.auto_path = 1;
370238737Simp			break;
371238737Simp		case 't':
372238737Simp			if (utilfdt_decode_type(optarg, &disp.type,
373238737Simp					&disp.size))
374238737Simp				usage("Invalid type string");
375238737Simp			break;
376238737Simp
377238737Simp		case 'v':
378238737Simp			disp.verbose = 1;
379238737Simp			break;
380238737Simp		}
381238737Simp	}
382238737Simp
383238737Simp	if (optind < argc)
384238737Simp		filename = argv[optind++];
385238737Simp	if (!filename)
386261215Simp		usage("missing filename");
387238737Simp
388238737Simp	argv += optind;
389238737Simp	argc -= optind;
390238737Simp
391238737Simp	if (disp.oper == OPER_WRITE_PROP) {
392238737Simp		if (argc < 1)
393261215Simp			usage("missing node");
394238737Simp		if (argc < 2)
395261215Simp			usage("missing property");
396238737Simp	}
397238737Simp
398238737Simp	if (do_fdtput(&disp, filename, argv, argc))
399238737Simp		return 1;
400238737Simp	return 0;
401238737Simp}
402