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