]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/dtc/fdtput.c
Implement pci_enable_msi() and pci_disable_msi() in the LinuxKPI.
[FreeBSD/FreeBSD.git] / contrib / 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         OPER_REMOVE_NODE,               /* Delete a node */
36         OPER_DELETE_PROP,               /* Delete a property in a node */
37 };
38
39 struct display_info {
40         enum oper_type oper;    /* operation to perform */
41         int type;               /* data type (s/i/u/x or 0 for default) */
42         int size;               /* data size (1/2/4) */
43         int verbose;            /* verbose output */
44         int auto_path;          /* automatically create all path components */
45 };
46
47
48 /**
49  * Report an error with a particular node.
50  *
51  * @param name          Node name to report error on
52  * @param namelen       Length of node name, or -1 to use entire string
53  * @param err           Error number to report (-FDT_ERR_...)
54  */
55 static void report_error(const char *name, int namelen, int err)
56 {
57         if (namelen == -1)
58                 namelen = strlen(name);
59         fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name,
60                 fdt_strerror(err));
61 }
62
63 /**
64  * Encode a series of arguments in a property value.
65  *
66  * @param disp          Display information / options
67  * @param arg           List of arguments from command line
68  * @param arg_count     Number of arguments (may be 0)
69  * @param valuep        Returns buffer containing value
70  * @param *value_len    Returns length of value encoded
71  */
72 static int encode_value(struct display_info *disp, char **arg, int arg_count,
73                         char **valuep, int *value_len)
74 {
75         char *value = NULL;     /* holding area for value */
76         int value_size = 0;     /* size of holding area */
77         char *ptr;              /* pointer to current value position */
78         int len;                /* length of this cell/string/byte */
79         int ival;
80         int upto;       /* the number of bytes we have written to buf */
81         char fmt[3];
82
83         upto = 0;
84
85         if (disp->verbose)
86                 fprintf(stderr, "Decoding value:\n");
87
88         fmt[0] = '%';
89         fmt[1] = disp->type ? disp->type : 'd';
90         fmt[2] = '\0';
91         for (; arg_count > 0; arg++, arg_count--, upto += len) {
92                 /* assume integer unless told otherwise */
93                 if (disp->type == 's')
94                         len = strlen(*arg) + 1;
95                 else
96                         len = disp->size == -1 ? 4 : disp->size;
97
98                 /* enlarge our value buffer by a suitable margin if needed */
99                 if (upto + len > value_size) {
100                         value_size = (upto + len) + 500;
101                         value = xrealloc(value, value_size);
102                 }
103
104                 ptr = value + upto;
105                 if (disp->type == 's') {
106                         memcpy(ptr, *arg, len);
107                         if (disp->verbose)
108                                 fprintf(stderr, "\tstring: '%s'\n", ptr);
109                 } else {
110                         int *iptr = (int *)ptr;
111                         sscanf(*arg, fmt, &ival);
112                         if (len == 4)
113                                 *iptr = cpu_to_fdt32(ival);
114                         else
115                                 *ptr = (uint8_t)ival;
116                         if (disp->verbose) {
117                                 fprintf(stderr, "\t%s: %d\n",
118                                         disp->size == 1 ? "byte" :
119                                         disp->size == 2 ? "short" : "int",
120                                         ival);
121                         }
122                 }
123         }
124         *value_len = upto;
125         *valuep = value;
126         if (disp->verbose)
127                 fprintf(stderr, "Value size %d\n", upto);
128         return 0;
129 }
130
131 #define ALIGN(x)                (((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1))
132
133 static char *_realloc_fdt(char *fdt, int delta)
134 {
135         int new_sz = fdt_totalsize(fdt) + delta;
136         fdt = xrealloc(fdt, new_sz);
137         fdt_open_into(fdt, fdt, new_sz);
138         return fdt;
139 }
140
141 static char *realloc_node(char *fdt, const char *name)
142 {
143         int delta;
144         /* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */
145         delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1)
146                         + FDT_TAGSIZE;
147         return _realloc_fdt(fdt, delta);
148 }
149
150 static char *realloc_property(char *fdt, int nodeoffset,
151                 const char *name, int newlen)
152 {
153         int delta = 0;
154         int oldlen = 0;
155
156         if (!fdt_get_property(fdt, nodeoffset, name, &oldlen))
157                 /* strings + property header */
158                 delta = sizeof(struct fdt_property) + strlen(name) + 1;
159
160         if (newlen > oldlen)
161                 /* actual value in off_struct */
162                 delta += ALIGN(newlen) - ALIGN(oldlen);
163
164         return _realloc_fdt(fdt, delta);
165 }
166
167 static int store_key_value(char **blob, const char *node_name,
168                 const char *property, const char *buf, int len)
169 {
170         int node;
171         int err;
172
173         node = fdt_path_offset(*blob, node_name);
174         if (node < 0) {
175                 report_error(node_name, -1, node);
176                 return -1;
177         }
178
179         err = fdt_setprop(*blob, node, property, buf, len);
180         if (err == -FDT_ERR_NOSPACE) {
181                 *blob = realloc_property(*blob, node, property, len);
182                 err = fdt_setprop(*blob, node, property, buf, len);
183         }
184         if (err) {
185                 report_error(property, -1, err);
186                 return -1;
187         }
188         return 0;
189 }
190
191 /**
192  * Create paths as needed for all components of a path
193  *
194  * Any components of the path that do not exist are created. Errors are
195  * reported.
196  *
197  * @param blob          FDT blob to write into
198  * @param in_path       Path to process
199  * @return 0 if ok, -1 on error
200  */
201 static int create_paths(char **blob, const char *in_path)
202 {
203         const char *path = in_path;
204         const char *sep;
205         int node, offset = 0;
206
207         /* skip leading '/' */
208         while (*path == '/')
209                 path++;
210
211         for (sep = path; *sep; path = sep + 1, offset = node) {
212                 /* equivalent to strchrnul(), but it requires _GNU_SOURCE */
213                 sep = strchr(path, '/');
214                 if (!sep)
215                         sep = path + strlen(path);
216
217                 node = fdt_subnode_offset_namelen(*blob, offset, path,
218                                 sep - path);
219                 if (node == -FDT_ERR_NOTFOUND) {
220                         *blob = realloc_node(*blob, path);
221                         node = fdt_add_subnode_namelen(*blob, offset, path,
222                                                        sep - path);
223                 }
224                 if (node < 0) {
225                         report_error(path, sep - path, node);
226                         return -1;
227                 }
228         }
229
230         return 0;
231 }
232
233 /**
234  * Create a new node in the fdt.
235  *
236  * This will overwrite the node_name string. Any error is reported.
237  *
238  * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this.
239  *
240  * @param blob          FDT blob to write into
241  * @param node_name     Name of node to create
242  * @return new node offset if found, or -1 on failure
243  */
244 static int create_node(char **blob, const char *node_name)
245 {
246         int node = 0;
247         char *p;
248
249         p = strrchr(node_name, '/');
250         if (!p) {
251                 report_error(node_name, -1, -FDT_ERR_BADPATH);
252                 return -1;
253         }
254         *p = '\0';
255
256         *blob = realloc_node(*blob, p + 1);
257
258         if (p > node_name) {
259                 node = fdt_path_offset(*blob, node_name);
260                 if (node < 0) {
261                         report_error(node_name, -1, node);
262                         return -1;
263                 }
264         }
265
266         node = fdt_add_subnode(*blob, node, p + 1);
267         if (node < 0) {
268                 report_error(p + 1, -1, node);
269                 return -1;
270         }
271
272         return 0;
273 }
274
275 /**
276  * Delete a property of a node in the fdt.
277  *
278  * @param blob          FDT blob to write into
279  * @param node_name     Path to node containing the property to delete
280  * @param prop_name     Name of property to delete
281  * @return 0 on success, or -1 on failure
282  */
283 static int delete_prop(char *blob, const char *node_name, const char *prop_name)
284 {
285         int node = 0;
286
287         node = fdt_path_offset(blob, node_name);
288         if (node < 0) {
289                 report_error(node_name, -1, node);
290                 return -1;
291         }
292
293         node = fdt_delprop(blob, node, prop_name);
294         if (node < 0) {
295                 report_error(node_name, -1, node);
296                 return -1;
297         }
298
299         return 0;
300 }
301
302 /**
303  * Delete a node in the fdt.
304  *
305  * @param blob          FDT blob to write into
306  * @param node_name     Name of node to delete
307  * @return 0 on success, or -1 on failure
308  */
309 static int delete_node(char *blob, const char *node_name)
310 {
311         int node = 0;
312
313         node = fdt_path_offset(blob, node_name);
314         if (node < 0) {
315                 report_error(node_name, -1, node);
316                 return -1;
317         }
318
319         node = fdt_del_node(blob, node);
320         if (node < 0) {
321                 report_error(node_name, -1, node);
322                 return -1;
323         }
324
325         return 0;
326 }
327
328 static int do_fdtput(struct display_info *disp, const char *filename,
329                     char **arg, int arg_count)
330 {
331         char *value = NULL;
332         char *blob;
333         char *node;
334         int len, ret = 0;
335
336         blob = utilfdt_read(filename);
337         if (!blob)
338                 return -1;
339
340         switch (disp->oper) {
341         case OPER_WRITE_PROP:
342                 /*
343                  * Convert the arguments into a single binary value, then
344                  * store them into the property.
345                  */
346                 assert(arg_count >= 2);
347                 if (disp->auto_path && create_paths(&blob, *arg))
348                         return -1;
349                 if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
350                         store_key_value(&blob, *arg, arg[1], value, len))
351                         ret = -1;
352                 break;
353         case OPER_CREATE_NODE:
354                 for (; ret >= 0 && arg_count--; arg++) {
355                         if (disp->auto_path)
356                                 ret = create_paths(&blob, *arg);
357                         else
358                                 ret = create_node(&blob, *arg);
359                 }
360                 break;
361         case OPER_REMOVE_NODE:
362                 for (; ret >= 0 && arg_count--; arg++)
363                         ret = delete_node(blob, *arg);
364                 break;
365         case OPER_DELETE_PROP:
366                 node = *arg;
367                 for (arg++; ret >= 0 && arg_count-- > 1; arg++)
368                         ret = delete_prop(blob, node, *arg);
369                 break;
370         }
371         if (ret >= 0) {
372                 fdt_pack(blob);
373                 ret = utilfdt_write(filename, blob);
374         }
375
376         free(blob);
377
378         if (value) {
379                 free(value);
380         }
381
382         return ret;
383 }
384
385 /* Usage related data. */
386 static const char usage_synopsis[] =
387         "write a property value to a device tree\n"
388         "       fdtput <options> <dt file> <node> <property> [<value>...]\n"
389         "       fdtput -c <options> <dt file> [<node>...]\n"
390         "       fdtput -r <options> <dt file> [<node>...]\n"
391         "       fdtput -d <options> <dt file> <node> [<property>...]\n"
392         "\n"
393         "The command line arguments are joined together into a single value.\n"
394         USAGE_TYPE_MSG;
395 static const char usage_short_opts[] = "crdpt:v" USAGE_COMMON_SHORT_OPTS;
396 static struct option const usage_long_opts[] = {
397         {"create",           no_argument, NULL, 'c'},
398         {"remove",           no_argument, NULL, 'r'},
399         {"delete",           no_argument, NULL, 'd'},
400         {"auto-path",        no_argument, NULL, 'p'},
401         {"type",              a_argument, NULL, 't'},
402         {"verbose",          no_argument, NULL, 'v'},
403         USAGE_COMMON_LONG_OPTS,
404 };
405 static const char * const usage_opts_help[] = {
406         "Create nodes if they don't already exist",
407         "Delete nodes (and any subnodes) if they already exist",
408         "Delete properties if they already exist",
409         "Automatically create nodes as needed for the node path",
410         "Type of data",
411         "Display each value decoded from command line",
412         USAGE_COMMON_OPTS_HELP
413 };
414
415 int main(int argc, char *argv[])
416 {
417         int opt;
418         struct display_info disp;
419         char *filename = NULL;
420
421         memset(&disp, '\0', sizeof(disp));
422         disp.size = -1;
423         disp.oper = OPER_WRITE_PROP;
424         while ((opt = util_getopt_long()) != EOF) {
425                 /*
426                  * TODO: add options to:
427                  * - rename node
428                  * - pack fdt before writing
429                  * - set amount of free space when writing
430                  */
431                 switch (opt) {
432                 case_USAGE_COMMON_FLAGS
433
434                 case 'c':
435                         disp.oper = OPER_CREATE_NODE;
436                         break;
437                 case 'r':
438                         disp.oper = OPER_REMOVE_NODE;
439                         break;
440                 case 'd':
441                         disp.oper = OPER_DELETE_PROP;
442                         break;
443                 case 'p':
444                         disp.auto_path = 1;
445                         break;
446                 case 't':
447                         if (utilfdt_decode_type(optarg, &disp.type,
448                                         &disp.size))
449                                 usage("Invalid type string");
450                         break;
451
452                 case 'v':
453                         disp.verbose = 1;
454                         break;
455                 }
456         }
457
458         if (optind < argc)
459                 filename = argv[optind++];
460         if (!filename)
461                 usage("missing filename");
462
463         argv += optind;
464         argc -= optind;
465
466         if (disp.oper == OPER_WRITE_PROP) {
467                 if (argc < 1)
468                         usage("missing node");
469                 if (argc < 2)
470                         usage("missing property");
471         }
472
473         if (disp.oper == OPER_DELETE_PROP)
474                 if (argc < 1)
475                         usage("missing node");
476
477         if (do_fdtput(&disp, filename, argv, argc))
478                 return 1;
479         return 0;
480 }