Add qemu 2.4.0
[kvmfornfv.git] / qemu / dtc / fdtput.c
1 /*
2  * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
17  * MA 02111-1307 USA
18  */
19
20 #include <assert.h>
21 #include <ctype.h>
22 #include <getopt.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <libfdt.h>
28
29 #include "util.h"
30
31 /* These are the operations we support */
32 enum oper_type {
33         OPER_WRITE_PROP,                /* Write a property in a node */
34         OPER_CREATE_NODE,               /* Create a new node */
35 };
36
37 struct display_info {
38         enum oper_type oper;    /* operation to perform */
39         int type;               /* data type (s/i/u/x or 0 for default) */
40         int size;               /* data size (1/2/4) */
41         int verbose;            /* verbose output */
42         int auto_path;          /* automatically create all path components */
43 };
44
45
46 /**
47  * Report an error with a particular node.
48  *
49  * @param name          Node name to report error on
50  * @param namelen       Length of node name, or -1 to use entire string
51  * @param err           Error number to report (-FDT_ERR_...)
52  */
53 static void report_error(const char *name, int namelen, int err)
54 {
55         if (namelen == -1)
56                 namelen = strlen(name);
57         fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name,
58                 fdt_strerror(err));
59 }
60
61 /**
62  * Encode a series of arguments in a property value.
63  *
64  * @param disp          Display information / options
65  * @param arg           List of arguments from command line
66  * @param arg_count     Number of arguments (may be 0)
67  * @param valuep        Returns buffer containing value
68  * @param *value_len    Returns length of value encoded
69  */
70 static int encode_value(struct display_info *disp, char **arg, int arg_count,
71                         char **valuep, int *value_len)
72 {
73         char *value = NULL;     /* holding area for value */
74         int value_size = 0;     /* size of holding area */
75         char *ptr;              /* pointer to current value position */
76         int len;                /* length of this cell/string/byte */
77         int ival;
78         int upto;       /* the number of bytes we have written to buf */
79         char fmt[3];
80
81         upto = 0;
82
83         if (disp->verbose)
84                 fprintf(stderr, "Decoding value:\n");
85
86         fmt[0] = '%';
87         fmt[1] = disp->type ? disp->type : 'd';
88         fmt[2] = '\0';
89         for (; arg_count > 0; arg++, arg_count--, upto += len) {
90                 /* assume integer unless told otherwise */
91                 if (disp->type == 's')
92                         len = strlen(*arg) + 1;
93                 else
94                         len = disp->size == -1 ? 4 : disp->size;
95
96                 /* enlarge our value buffer by a suitable margin if needed */
97                 if (upto + len > value_size) {
98                         value_size = (upto + len) + 500;
99                         value = realloc(value, value_size);
100                         if (!value) {
101                                 fprintf(stderr, "Out of mmory: cannot alloc "
102                                         "%d bytes\n", value_size);
103                                 return -1;
104                         }
105                 }
106
107                 ptr = value + upto;
108                 if (disp->type == 's') {
109                         memcpy(ptr, *arg, len);
110                         if (disp->verbose)
111                                 fprintf(stderr, "\tstring: '%s'\n", ptr);
112                 } else {
113                         int *iptr = (int *)ptr;
114                         sscanf(*arg, fmt, &ival);
115                         if (len == 4)
116                                 *iptr = cpu_to_fdt32(ival);
117                         else
118                                 *ptr = (uint8_t)ival;
119                         if (disp->verbose) {
120                                 fprintf(stderr, "\t%s: %d\n",
121                                         disp->size == 1 ? "byte" :
122                                         disp->size == 2 ? "short" : "int",
123                                         ival);
124                         }
125                 }
126         }
127         *value_len = upto;
128         *valuep = value;
129         if (disp->verbose)
130                 fprintf(stderr, "Value size %d\n", upto);
131         return 0;
132 }
133
134 #define ALIGN(x)                (((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1))
135
136 static char *_realloc_fdt(char *fdt, int delta)
137 {
138         int new_sz = fdt_totalsize(fdt) + delta;
139         fdt = xrealloc(fdt, new_sz);
140         fdt_open_into(fdt, fdt, new_sz);
141         return fdt;
142 }
143
144 static char *realloc_node(char *fdt, const char *name)
145 {
146         int delta;
147         /* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */
148         delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1)
149                         + FDT_TAGSIZE;
150         return _realloc_fdt(fdt, delta);
151 }
152
153 static char *realloc_property(char *fdt, int nodeoffset,
154                 const char *name, int newlen)
155 {
156         int delta = 0;
157         int oldlen = 0;
158
159         if (!fdt_get_property(fdt, nodeoffset, name, &oldlen))
160                 /* strings + property header */
161                 delta = sizeof(struct fdt_property) + strlen(name) + 1;
162
163         if (newlen > oldlen)
164                 /* actual value in off_struct */
165                 delta += ALIGN(newlen) - ALIGN(oldlen);
166
167         return _realloc_fdt(fdt, delta);
168 }
169
170 static int store_key_value(char **blob, const char *node_name,
171                 const char *property, const char *buf, int len)
172 {
173         int node;
174         int err;
175
176         node = fdt_path_offset(*blob, node_name);
177         if (node < 0) {
178                 report_error(node_name, -1, node);
179                 return -1;
180         }
181
182         err = fdt_setprop(*blob, node, property, buf, len);
183         if (err == -FDT_ERR_NOSPACE) {
184                 *blob = realloc_property(*blob, node, property, len);
185                 err = fdt_setprop(*blob, node, property, buf, len);
186         }
187         if (err) {
188                 report_error(property, -1, err);
189                 return -1;
190         }
191         return 0;
192 }
193
194 /**
195  * Create paths as needed for all components of a path
196  *
197  * Any components of the path that do not exist are created. Errors are
198  * reported.
199  *
200  * @param blob          FDT blob to write into
201  * @param in_path       Path to process
202  * @return 0 if ok, -1 on error
203  */
204 static int create_paths(char **blob, const char *in_path)
205 {
206         const char *path = in_path;
207         const char *sep;
208         int node, offset = 0;
209
210         /* skip leading '/' */
211         while (*path == '/')
212                 path++;
213
214         for (sep = path; *sep; path = sep + 1, offset = node) {
215                 /* equivalent to strchrnul(), but it requires _GNU_SOURCE */
216                 sep = strchr(path, '/');
217                 if (!sep)
218                         sep = path + strlen(path);
219
220                 node = fdt_subnode_offset_namelen(*blob, offset, path,
221                                 sep - path);
222                 if (node == -FDT_ERR_NOTFOUND) {
223                         *blob = realloc_node(*blob, path);
224                         node = fdt_add_subnode_namelen(*blob, offset, path,
225                                                        sep - path);
226                 }
227                 if (node < 0) {
228                         report_error(path, sep - path, node);
229                         return -1;
230                 }
231         }
232
233         return 0;
234 }
235
236 /**
237  * Create a new node in the fdt.
238  *
239  * This will overwrite the node_name string. Any error is reported.
240  *
241  * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this.
242  *
243  * @param blob          FDT blob to write into
244  * @param node_name     Name of node to create
245  * @return new node offset if found, or -1 on failure
246  */
247 static int create_node(char **blob, const char *node_name)
248 {
249         int node = 0;
250         char *p;
251
252         p = strrchr(node_name, '/');
253         if (!p) {
254                 report_error(node_name, -1, -FDT_ERR_BADPATH);
255                 return -1;
256         }
257         *p = '\0';
258
259         *blob = realloc_node(*blob, p + 1);
260
261         if (p > node_name) {
262                 node = fdt_path_offset(*blob, node_name);
263                 if (node < 0) {
264                         report_error(node_name, -1, node);
265                         return -1;
266                 }
267         }
268
269         node = fdt_add_subnode(*blob, node, p + 1);
270         if (node < 0) {
271                 report_error(p + 1, -1, node);
272                 return -1;
273         }
274
275         return 0;
276 }
277
278 static int do_fdtput(struct display_info *disp, const char *filename,
279                     char **arg, int arg_count)
280 {
281         char *value;
282         char *blob;
283         int len, ret = 0;
284
285         blob = utilfdt_read(filename);
286         if (!blob)
287                 return -1;
288
289         switch (disp->oper) {
290         case OPER_WRITE_PROP:
291                 /*
292                  * Convert the arguments into a single binary value, then
293                  * store them into the property.
294                  */
295                 assert(arg_count >= 2);
296                 if (disp->auto_path && create_paths(&blob, *arg))
297                         return -1;
298                 if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
299                         store_key_value(&blob, *arg, arg[1], value, len))
300                         ret = -1;
301                 break;
302         case OPER_CREATE_NODE:
303                 for (; ret >= 0 && arg_count--; arg++) {
304                         if (disp->auto_path)
305                                 ret = create_paths(&blob, *arg);
306                         else
307                                 ret = create_node(&blob, *arg);
308                 }
309                 break;
310         }
311         if (ret >= 0) {
312                 fdt_pack(blob);
313                 ret = utilfdt_write(filename, blob);
314         }
315
316         free(blob);
317         return ret;
318 }
319
320 /* Usage related data. */
321 static const char usage_synopsis[] =
322         "write a property value to a device tree\n"
323         "       fdtput <options> <dt file> <node> <property> [<value>...]\n"
324         "       fdtput -c <options> <dt file> [<node>...]\n"
325         "\n"
326         "The command line arguments are joined together into a single value.\n"
327         USAGE_TYPE_MSG;
328 static const char usage_short_opts[] = "cpt:v" USAGE_COMMON_SHORT_OPTS;
329 static struct option const usage_long_opts[] = {
330         {"create",           no_argument, NULL, 'c'},
331         {"auto-path",        no_argument, NULL, 'p'},
332         {"type",              a_argument, NULL, 't'},
333         {"verbose",          no_argument, NULL, 'v'},
334         USAGE_COMMON_LONG_OPTS,
335 };
336 static const char * const usage_opts_help[] = {
337         "Create nodes if they don't already exist",
338         "Automatically create nodes as needed for the node path",
339         "Type of data",
340         "Display each value decoded from command line",
341         USAGE_COMMON_OPTS_HELP
342 };
343
344 int main(int argc, char *argv[])
345 {
346         int opt;
347         struct display_info disp;
348         char *filename = NULL;
349
350         memset(&disp, '\0', sizeof(disp));
351         disp.size = -1;
352         disp.oper = OPER_WRITE_PROP;
353         while ((opt = util_getopt_long()) != EOF) {
354                 /*
355                  * TODO: add options to:
356                  * - delete property
357                  * - delete node (optionally recursively)
358                  * - rename node
359                  * - pack fdt before writing
360                  * - set amount of free space when writing
361                  */
362                 switch (opt) {
363                 case_USAGE_COMMON_FLAGS
364
365                 case 'c':
366                         disp.oper = OPER_CREATE_NODE;
367                         break;
368                 case 'p':
369                         disp.auto_path = 1;
370                         break;
371                 case 't':
372                         if (utilfdt_decode_type(optarg, &disp.type,
373                                         &disp.size))
374                                 usage("Invalid type string");
375                         break;
376
377                 case 'v':
378                         disp.verbose = 1;
379                         break;
380                 }
381         }
382
383         if (optind < argc)
384                 filename = argv[optind++];
385         if (!filename)
386                 usage("missing filename");
387
388         argv += optind;
389         argc -= optind;
390
391         if (disp.oper == OPER_WRITE_PROP) {
392                 if (argc < 1)
393                         usage("missing node");
394                 if (argc < 2)
395                         usage("missing property");
396         }
397
398         if (do_fdtput(&disp, filename, argv, argc))
399                 return 1;
400         return 0;
401 }