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
134266130Sian#define ALIGN(x)		(((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1))
135266130Sian
136266130Sianstatic char *_realloc_fdt(char *fdt, int delta)
137266130Sian{
138266130Sian	int new_sz = fdt_totalsize(fdt) + delta;
139266130Sian	fdt = xrealloc(fdt, new_sz);
140266130Sian	fdt_open_into(fdt, fdt, new_sz);
141266130Sian	return fdt;
142266130Sian}
143266130Sian
144266130Sianstatic char *realloc_node(char *fdt, const char *name)
145266130Sian{
146266130Sian	int delta;
147266130Sian	/* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */
148266130Sian	delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1)
149266130Sian			+ FDT_TAGSIZE;
150266130Sian	return _realloc_fdt(fdt, delta);
151266130Sian}
152266130Sian
153266130Sianstatic char *realloc_property(char *fdt, int nodeoffset,
154266130Sian		const char *name, int newlen)
155266130Sian{
156266130Sian	int delta = 0;
157266130Sian	int oldlen = 0;
158266130Sian
159266130Sian	if (!fdt_get_property(fdt, nodeoffset, name, &oldlen))
160266130Sian		/* strings + property header */
161266130Sian		delta = sizeof(struct fdt_property) + strlen(name) + 1;
162266130Sian
163266130Sian	if (newlen > oldlen)
164266130Sian		/* actual value in off_struct */
165266130Sian		delta += ALIGN(newlen) - ALIGN(oldlen);
166266130Sian
167266130Sian	return _realloc_fdt(fdt, delta);
168266130Sian}
169266130Sian
170266130Sianstatic 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
176266130Sian	node = fdt_path_offset(*blob, node_name);
177238737Simp	if (node < 0) {
178238737Simp		report_error(node_name, -1, node);
179238737Simp		return -1;
180238737Simp	}
181238737Simp
182266130Sian	err = fdt_setprop(*blob, node, property, buf, len);
183266130Sian	if (err == -FDT_ERR_NOSPACE) {
184266130Sian		*blob = realloc_property(*blob, node, property, len);
185266130Sian		err = fdt_setprop(*blob, node, property, buf, len);
186266130Sian	}
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 */
204266130Sianstatic 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
220266130Sian		node = fdt_subnode_offset_namelen(*blob, offset, path,
221238737Simp				sep - path);
222238737Simp		if (node == -FDT_ERR_NOTFOUND) {
223266130Sian			*blob = realloc_node(*blob, path);
224266130Sian			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 */
247266130Sianstatic 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
259266130Sian	*blob = realloc_node(*blob, p + 1);
260266130Sian
261238737Simp	if (p > node_name) {
262266130Sian		node = fdt_path_offset(*blob, node_name);
263238737Simp		if (node < 0) {
264238737Simp			report_error(node_name, -1, node);
265238737Simp			return -1;
266238737Simp		}
267238737Simp	}
268238737Simp
269266130Sian	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);
296266130Sian		if (disp->auto_path && create_paths(&blob, *arg))
297238737Simp			return -1;
298238737Simp		if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
299266130Sian			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)
305266130Sian				ret = create_paths(&blob, *arg);
306238737Simp			else
307266130Sian				ret = create_node(&blob, *arg);
308238737Simp		}
309238737Simp		break;
310238737Simp	}
311266130Sian	if (ret >= 0) {
312266130Sian		fdt_pack(blob);
313238737Simp		ret = utilfdt_write(filename, blob);
314266130Sian	}
315238737Simp
316238737Simp	free(blob);
317238737Simp	return ret;
318238737Simp}
319238737Simp
320266130Sian/* Usage related data. */
321266130Sianstatic const char usage_synopsis[] =
322266130Sian	"write a property value to a device tree\n"
323266130Sian	"	fdtput <options> <dt file> <node> <property> [<value>...]\n"
324266130Sian	"	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;
328266130Sianstatic const char usage_short_opts[] = "cpt:v" USAGE_COMMON_SHORT_OPTS;
329266130Sianstatic struct option const usage_long_opts[] = {
330266130Sian	{"create",           no_argument, NULL, 'c'},
331266130Sian	{"auto-path",        no_argument, NULL, 'p'},
332266130Sian	{"type",              a_argument, NULL, 't'},
333266130Sian	{"verbose",          no_argument, NULL, 'v'},
334266130Sian	USAGE_COMMON_LONG_OPTS,
335266130Sian};
336266130Sianstatic const char * const usage_opts_help[] = {
337266130Sian	"Create nodes if they don't already exist",
338266130Sian	"Automatically create nodes as needed for the node path",
339266130Sian	"Type of data",
340266130Sian	"Display each value decoded from command line",
341266130Sian	USAGE_COMMON_OPTS_HELP
342266130Sian};
343238737Simp
344238737Simpint main(int argc, char *argv[])
345238737Simp{
346266130Sian	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;
353266130Sian	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		 */
362266130Sian		switch (opt) {
363266130Sian		case_USAGE_COMMON_FLAGS
364266130Sian
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)
386266130Sian		usage("missing filename");
387238737Simp
388238737Simp	argv += optind;
389238737Simp	argc -= optind;
390238737Simp
391238737Simp	if (disp.oper == OPER_WRITE_PROP) {
392238737Simp		if (argc < 1)
393266130Sian			usage("missing node");
394238737Simp		if (argc < 2)
395266130Sian			usage("missing property");
396238737Simp	}
397238737Simp
398238737Simp	if (do_fdtput(&disp, filename, argv, argc))
399238737Simp		return 1;
400238737Simp	return 0;
401238737Simp}
402