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