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 */
35318102Sgonzo	OPER_REMOVE_NODE,		/* Delete a node */
36318102Sgonzo	OPER_DELETE_PROP,		/* Delete a property in a node */
37238737Simp};
38238737Simp
39238737Simpstruct display_info {
40238737Simp	enum oper_type oper;	/* operation to perform */
41238737Simp	int type;		/* data type (s/i/u/x or 0 for default) */
42238737Simp	int size;		/* data size (1/2/4) */
43238737Simp	int verbose;		/* verbose output */
44238737Simp	int auto_path;		/* automatically create all path components */
45238737Simp};
46238737Simp
47238737Simp
48238737Simp/**
49238737Simp * Report an error with a particular node.
50238737Simp *
51238737Simp * @param name		Node name to report error on
52238737Simp * @param namelen	Length of node name, or -1 to use entire string
53238737Simp * @param err		Error number to report (-FDT_ERR_...)
54238737Simp */
55238737Simpstatic void report_error(const char *name, int namelen, int err)
56238737Simp{
57238737Simp	if (namelen == -1)
58238737Simp		namelen = strlen(name);
59238737Simp	fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name,
60238737Simp		fdt_strerror(err));
61238737Simp}
62238737Simp
63238737Simp/**
64238737Simp * Encode a series of arguments in a property value.
65238737Simp *
66238737Simp * @param disp		Display information / options
67238737Simp * @param arg		List of arguments from command line
68238737Simp * @param arg_count	Number of arguments (may be 0)
69238737Simp * @param valuep	Returns buffer containing value
70238737Simp * @param *value_len	Returns length of value encoded
71238737Simp */
72238737Simpstatic int encode_value(struct display_info *disp, char **arg, int arg_count,
73238737Simp			char **valuep, int *value_len)
74238737Simp{
75238737Simp	char *value = NULL;	/* holding area for value */
76238737Simp	int value_size = 0;	/* size of holding area */
77238737Simp	char *ptr;		/* pointer to current value position */
78238737Simp	int len;		/* length of this cell/string/byte */
79238737Simp	int ival;
80238737Simp	int upto;	/* the number of bytes we have written to buf */
81238737Simp	char fmt[3];
82238737Simp
83238737Simp	upto = 0;
84238737Simp
85238737Simp	if (disp->verbose)
86238737Simp		fprintf(stderr, "Decoding value:\n");
87238737Simp
88238737Simp	fmt[0] = '%';
89238737Simp	fmt[1] = disp->type ? disp->type : 'd';
90238737Simp	fmt[2] = '\0';
91238737Simp	for (; arg_count > 0; arg++, arg_count--, upto += len) {
92238737Simp		/* assume integer unless told otherwise */
93238737Simp		if (disp->type == 's')
94238737Simp			len = strlen(*arg) + 1;
95238737Simp		else
96238737Simp			len = disp->size == -1 ? 4 : disp->size;
97238737Simp
98238737Simp		/* enlarge our value buffer by a suitable margin if needed */
99238737Simp		if (upto + len > value_size) {
100238737Simp			value_size = (upto + len) + 500;
101318102Sgonzo			value = xrealloc(value, value_size);
102238737Simp		}
103238737Simp
104238737Simp		ptr = value + upto;
105238737Simp		if (disp->type == 's') {
106238737Simp			memcpy(ptr, *arg, len);
107238737Simp			if (disp->verbose)
108238737Simp				fprintf(stderr, "\tstring: '%s'\n", ptr);
109238737Simp		} else {
110238737Simp			int *iptr = (int *)ptr;
111238737Simp			sscanf(*arg, fmt, &ival);
112238737Simp			if (len == 4)
113238737Simp				*iptr = cpu_to_fdt32(ival);
114238737Simp			else
115238737Simp				*ptr = (uint8_t)ival;
116238737Simp			if (disp->verbose) {
117238737Simp				fprintf(stderr, "\t%s: %d\n",
118238737Simp					disp->size == 1 ? "byte" :
119238737Simp					disp->size == 2 ? "short" : "int",
120238737Simp					ival);
121238737Simp			}
122238737Simp		}
123238737Simp	}
124238737Simp	*value_len = upto;
125238737Simp	*valuep = value;
126238737Simp	if (disp->verbose)
127238737Simp		fprintf(stderr, "Value size %d\n", upto);
128238737Simp	return 0;
129238737Simp}
130238737Simp
131261215Simp#define ALIGN(x)		(((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1))
132261215Simp
133261215Simpstatic char *_realloc_fdt(char *fdt, int delta)
134261215Simp{
135261215Simp	int new_sz = fdt_totalsize(fdt) + delta;
136261215Simp	fdt = xrealloc(fdt, new_sz);
137261215Simp	fdt_open_into(fdt, fdt, new_sz);
138261215Simp	return fdt;
139261215Simp}
140261215Simp
141261215Simpstatic char *realloc_node(char *fdt, const char *name)
142261215Simp{
143261215Simp	int delta;
144261215Simp	/* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */
145261215Simp	delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1)
146261215Simp			+ FDT_TAGSIZE;
147261215Simp	return _realloc_fdt(fdt, delta);
148261215Simp}
149261215Simp
150261215Simpstatic char *realloc_property(char *fdt, int nodeoffset,
151261215Simp		const char *name, int newlen)
152261215Simp{
153261215Simp	int delta = 0;
154261215Simp	int oldlen = 0;
155261215Simp
156261215Simp	if (!fdt_get_property(fdt, nodeoffset, name, &oldlen))
157261215Simp		/* strings + property header */
158261215Simp		delta = sizeof(struct fdt_property) + strlen(name) + 1;
159261215Simp
160261215Simp	if (newlen > oldlen)
161261215Simp		/* actual value in off_struct */
162261215Simp		delta += ALIGN(newlen) - ALIGN(oldlen);
163261215Simp
164261215Simp	return _realloc_fdt(fdt, delta);
165261215Simp}
166261215Simp
167261215Simpstatic int store_key_value(char **blob, const char *node_name,
168238737Simp		const char *property, const char *buf, int len)
169238737Simp{
170238737Simp	int node;
171238737Simp	int err;
172238737Simp
173261215Simp	node = fdt_path_offset(*blob, node_name);
174238737Simp	if (node < 0) {
175238737Simp		report_error(node_name, -1, node);
176238737Simp		return -1;
177238737Simp	}
178238737Simp
179261215Simp	err = fdt_setprop(*blob, node, property, buf, len);
180261215Simp	if (err == -FDT_ERR_NOSPACE) {
181261215Simp		*blob = realloc_property(*blob, node, property, len);
182261215Simp		err = fdt_setprop(*blob, node, property, buf, len);
183261215Simp	}
184238737Simp	if (err) {
185238737Simp		report_error(property, -1, err);
186238737Simp		return -1;
187238737Simp	}
188238737Simp	return 0;
189238737Simp}
190238737Simp
191238737Simp/**
192238737Simp * Create paths as needed for all components of a path
193238737Simp *
194238737Simp * Any components of the path that do not exist are created. Errors are
195238737Simp * reported.
196238737Simp *
197238737Simp * @param blob		FDT blob to write into
198238737Simp * @param in_path	Path to process
199238737Simp * @return 0 if ok, -1 on error
200238737Simp */
201261215Simpstatic int create_paths(char **blob, const char *in_path)
202238737Simp{
203238737Simp	const char *path = in_path;
204238737Simp	const char *sep;
205238737Simp	int node, offset = 0;
206238737Simp
207238737Simp	/* skip leading '/' */
208238737Simp	while (*path == '/')
209238737Simp		path++;
210238737Simp
211238737Simp	for (sep = path; *sep; path = sep + 1, offset = node) {
212238737Simp		/* equivalent to strchrnul(), but it requires _GNU_SOURCE */
213238737Simp		sep = strchr(path, '/');
214238737Simp		if (!sep)
215238737Simp			sep = path + strlen(path);
216238737Simp
217261215Simp		node = fdt_subnode_offset_namelen(*blob, offset, path,
218238737Simp				sep - path);
219238737Simp		if (node == -FDT_ERR_NOTFOUND) {
220261215Simp			*blob = realloc_node(*blob, path);
221261215Simp			node = fdt_add_subnode_namelen(*blob, offset, path,
222238737Simp						       sep - path);
223238737Simp		}
224238737Simp		if (node < 0) {
225238737Simp			report_error(path, sep - path, node);
226238737Simp			return -1;
227238737Simp		}
228238737Simp	}
229238737Simp
230238737Simp	return 0;
231238737Simp}
232238737Simp
233238737Simp/**
234238737Simp * Create a new node in the fdt.
235238737Simp *
236238737Simp * This will overwrite the node_name string. Any error is reported.
237238737Simp *
238238737Simp * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this.
239238737Simp *
240238737Simp * @param blob		FDT blob to write into
241238737Simp * @param node_name	Name of node to create
242238737Simp * @return new node offset if found, or -1 on failure
243238737Simp */
244261215Simpstatic int create_node(char **blob, const char *node_name)
245238737Simp{
246238737Simp	int node = 0;
247238737Simp	char *p;
248238737Simp
249238737Simp	p = strrchr(node_name, '/');
250238737Simp	if (!p) {
251238737Simp		report_error(node_name, -1, -FDT_ERR_BADPATH);
252238737Simp		return -1;
253238737Simp	}
254238737Simp	*p = '\0';
255238737Simp
256261215Simp	*blob = realloc_node(*blob, p + 1);
257261215Simp
258238737Simp	if (p > node_name) {
259261215Simp		node = fdt_path_offset(*blob, node_name);
260238737Simp		if (node < 0) {
261238737Simp			report_error(node_name, -1, node);
262238737Simp			return -1;
263238737Simp		}
264238737Simp	}
265238737Simp
266261215Simp	node = fdt_add_subnode(*blob, node, p + 1);
267238737Simp	if (node < 0) {
268238737Simp		report_error(p + 1, -1, node);
269238737Simp		return -1;
270238737Simp	}
271238737Simp
272238737Simp	return 0;
273238737Simp}
274238737Simp
275318102Sgonzo/**
276318102Sgonzo * Delete a property of a node in the fdt.
277318102Sgonzo *
278318102Sgonzo * @param blob		FDT blob to write into
279318102Sgonzo * @param node_name	Path to node containing the property to delete
280318102Sgonzo * @param prop_name	Name of property to delete
281318102Sgonzo * @return 0 on success, or -1 on failure
282318102Sgonzo */
283318102Sgonzostatic int delete_prop(char *blob, const char *node_name, const char *prop_name)
284318102Sgonzo{
285318102Sgonzo	int node = 0;
286318102Sgonzo
287318102Sgonzo	node = fdt_path_offset(blob, node_name);
288318102Sgonzo	if (node < 0) {
289318102Sgonzo		report_error(node_name, -1, node);
290318102Sgonzo		return -1;
291318102Sgonzo	}
292318102Sgonzo
293318102Sgonzo	node = fdt_delprop(blob, node, prop_name);
294318102Sgonzo	if (node < 0) {
295318102Sgonzo		report_error(node_name, -1, node);
296318102Sgonzo		return -1;
297318102Sgonzo	}
298318102Sgonzo
299318102Sgonzo	return 0;
300318102Sgonzo}
301318102Sgonzo
302318102Sgonzo/**
303318102Sgonzo * Delete a node in the fdt.
304318102Sgonzo *
305318102Sgonzo * @param blob		FDT blob to write into
306318102Sgonzo * @param node_name	Name of node to delete
307318102Sgonzo * @return 0 on success, or -1 on failure
308318102Sgonzo */
309318102Sgonzostatic int delete_node(char *blob, const char *node_name)
310318102Sgonzo{
311318102Sgonzo	int node = 0;
312318102Sgonzo
313318102Sgonzo	node = fdt_path_offset(blob, node_name);
314318102Sgonzo	if (node < 0) {
315318102Sgonzo		report_error(node_name, -1, node);
316318102Sgonzo		return -1;
317318102Sgonzo	}
318318102Sgonzo
319318102Sgonzo	node = fdt_del_node(blob, node);
320318102Sgonzo	if (node < 0) {
321318102Sgonzo		report_error(node_name, -1, node);
322318102Sgonzo		return -1;
323318102Sgonzo	}
324318102Sgonzo
325318102Sgonzo	return 0;
326318102Sgonzo}
327318102Sgonzo
328238737Simpstatic int do_fdtput(struct display_info *disp, const char *filename,
329238737Simp		    char **arg, int arg_count)
330238737Simp{
331318102Sgonzo	char *value = NULL;
332238737Simp	char *blob;
333318102Sgonzo	char *node;
334238737Simp	int len, ret = 0;
335238737Simp
336238737Simp	blob = utilfdt_read(filename);
337238737Simp	if (!blob)
338238737Simp		return -1;
339238737Simp
340238737Simp	switch (disp->oper) {
341238737Simp	case OPER_WRITE_PROP:
342238737Simp		/*
343238737Simp		 * Convert the arguments into a single binary value, then
344238737Simp		 * store them into the property.
345238737Simp		 */
346238737Simp		assert(arg_count >= 2);
347261215Simp		if (disp->auto_path && create_paths(&blob, *arg))
348238737Simp			return -1;
349238737Simp		if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
350261215Simp			store_key_value(&blob, *arg, arg[1], value, len))
351238737Simp			ret = -1;
352238737Simp		break;
353238737Simp	case OPER_CREATE_NODE:
354238737Simp		for (; ret >= 0 && arg_count--; arg++) {
355238737Simp			if (disp->auto_path)
356261215Simp				ret = create_paths(&blob, *arg);
357238737Simp			else
358261215Simp				ret = create_node(&blob, *arg);
359238737Simp		}
360238737Simp		break;
361318102Sgonzo	case OPER_REMOVE_NODE:
362318102Sgonzo		for (; ret >= 0 && arg_count--; arg++)
363318102Sgonzo			ret = delete_node(blob, *arg);
364318102Sgonzo		break;
365318102Sgonzo	case OPER_DELETE_PROP:
366318102Sgonzo		node = *arg;
367318102Sgonzo		for (arg++; ret >= 0 && arg_count-- > 1; arg++)
368318102Sgonzo			ret = delete_prop(blob, node, *arg);
369318102Sgonzo		break;
370238737Simp	}
371261215Simp	if (ret >= 0) {
372261215Simp		fdt_pack(blob);
373238737Simp		ret = utilfdt_write(filename, blob);
374261215Simp	}
375238737Simp
376238737Simp	free(blob);
377318102Sgonzo
378318102Sgonzo	if (value) {
379318102Sgonzo		free(value);
380318102Sgonzo	}
381318102Sgonzo
382238737Simp	return ret;
383238737Simp}
384238737Simp
385261215Simp/* Usage related data. */
386261215Simpstatic const char usage_synopsis[] =
387261215Simp	"write a property value to a device tree\n"
388261215Simp	"	fdtput <options> <dt file> <node> <property> [<value>...]\n"
389261215Simp	"	fdtput -c <options> <dt file> [<node>...]\n"
390318102Sgonzo	"	fdtput -r <options> <dt file> [<node>...]\n"
391318102Sgonzo	"	fdtput -d <options> <dt file> <node> [<property>...]\n"
392238737Simp	"\n"
393238737Simp	"The command line arguments are joined together into a single value.\n"
394238737Simp	USAGE_TYPE_MSG;
395318102Sgonzostatic const char usage_short_opts[] = "crdpt:v" USAGE_COMMON_SHORT_OPTS;
396261215Simpstatic struct option const usage_long_opts[] = {
397261215Simp	{"create",           no_argument, NULL, 'c'},
398318102Sgonzo	{"remove",	     no_argument, NULL, 'r'},
399318102Sgonzo	{"delete",	     no_argument, NULL, 'd'},
400261215Simp	{"auto-path",        no_argument, NULL, 'p'},
401261215Simp	{"type",              a_argument, NULL, 't'},
402261215Simp	{"verbose",          no_argument, NULL, 'v'},
403261215Simp	USAGE_COMMON_LONG_OPTS,
404261215Simp};
405261215Simpstatic const char * const usage_opts_help[] = {
406261215Simp	"Create nodes if they don't already exist",
407318102Sgonzo	"Delete nodes (and any subnodes) if they already exist",
408318102Sgonzo	"Delete properties if they already exist",
409261215Simp	"Automatically create nodes as needed for the node path",
410261215Simp	"Type of data",
411261215Simp	"Display each value decoded from command line",
412261215Simp	USAGE_COMMON_OPTS_HELP
413261215Simp};
414238737Simp
415238737Simpint main(int argc, char *argv[])
416238737Simp{
417261215Simp	int opt;
418238737Simp	struct display_info disp;
419238737Simp	char *filename = NULL;
420238737Simp
421238737Simp	memset(&disp, '\0', sizeof(disp));
422238737Simp	disp.size = -1;
423238737Simp	disp.oper = OPER_WRITE_PROP;
424261215Simp	while ((opt = util_getopt_long()) != EOF) {
425238737Simp		/*
426238737Simp		 * TODO: add options to:
427238737Simp		 * - rename node
428238737Simp		 * - pack fdt before writing
429238737Simp		 * - set amount of free space when writing
430238737Simp		 */
431261215Simp		switch (opt) {
432261215Simp		case_USAGE_COMMON_FLAGS
433261215Simp
434238737Simp		case 'c':
435238737Simp			disp.oper = OPER_CREATE_NODE;
436238737Simp			break;
437318102Sgonzo		case 'r':
438318102Sgonzo			disp.oper = OPER_REMOVE_NODE;
439318102Sgonzo			break;
440318102Sgonzo		case 'd':
441318102Sgonzo			disp.oper = OPER_DELETE_PROP;
442318102Sgonzo			break;
443238737Simp		case 'p':
444238737Simp			disp.auto_path = 1;
445238737Simp			break;
446238737Simp		case 't':
447238737Simp			if (utilfdt_decode_type(optarg, &disp.type,
448238737Simp					&disp.size))
449238737Simp				usage("Invalid type string");
450238737Simp			break;
451238737Simp
452238737Simp		case 'v':
453238737Simp			disp.verbose = 1;
454238737Simp			break;
455238737Simp		}
456238737Simp	}
457238737Simp
458238737Simp	if (optind < argc)
459238737Simp		filename = argv[optind++];
460238737Simp	if (!filename)
461261215Simp		usage("missing filename");
462238737Simp
463238737Simp	argv += optind;
464238737Simp	argc -= optind;
465238737Simp
466238737Simp	if (disp.oper == OPER_WRITE_PROP) {
467238737Simp		if (argc < 1)
468261215Simp			usage("missing node");
469238737Simp		if (argc < 2)
470261215Simp			usage("missing property");
471238737Simp	}
472238737Simp
473318102Sgonzo	if (disp.oper == OPER_DELETE_PROP)
474318102Sgonzo		if (argc < 1)
475318102Sgonzo			usage("missing node");
476318102Sgonzo
477238737Simp	if (do_fdtput(&disp, filename, argv, argc))
478238737Simp		return 1;
479238737Simp	return 0;
480238737Simp}
481