blob: c2fecf45a977230d3701a10cc3b9f2097c3ea64c [file] [log] [blame]
Rob Herringacfe84f2019-06-20 15:19:38 -06001// SPDX-License-Identifier: GPL-2.0-or-later
Simon Glass1ede50c2012-01-21 10:14:48 -08002/*
3 * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
Simon Glass1ede50c2012-01-21 10:14:48 -08004 */
5
6#include <assert.h>
7#include <ctype.h>
8#include <getopt.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
13#include <libfdt.h>
14
15#include "util.h"
16
Simon Glassf58dff52012-07-12 08:52:48 -070017/* These are the operations we support */
18enum oper_type {
19 OPER_WRITE_PROP, /* Write a property in a node */
Simon Glassd46c2de2012-07-12 08:52:49 -070020 OPER_CREATE_NODE, /* Create a new node */
Wang Longaa719612015-01-23 01:25:20 +000021 OPER_REMOVE_NODE, /* Delete a node */
22 OPER_DELETE_PROP, /* Delete a property in a node */
Simon Glassf58dff52012-07-12 08:52:48 -070023};
24
Simon Glass1ede50c2012-01-21 10:14:48 -080025struct display_info {
Simon Glassf58dff52012-07-12 08:52:48 -070026 enum oper_type oper; /* operation to perform */
Simon Glass1ede50c2012-01-21 10:14:48 -080027 int type; /* data type (s/i/u/x or 0 for default) */
28 int size; /* data size (1/2/4) */
29 int verbose; /* verbose output */
Simon Glassf807af12012-07-12 08:52:51 -070030 int auto_path; /* automatically create all path components */
Simon Glass1ede50c2012-01-21 10:14:48 -080031};
32
Simon Glass3553dfa2012-07-10 05:56:47 -070033
34/**
35 * Report an error with a particular node.
36 *
37 * @param name Node name to report error on
38 * @param namelen Length of node name, or -1 to use entire string
39 * @param err Error number to report (-FDT_ERR_...)
40 */
41static void report_error(const char *name, int namelen, int err)
Simon Glass1ede50c2012-01-21 10:14:48 -080042{
Simon Glass3553dfa2012-07-10 05:56:47 -070043 if (namelen == -1)
44 namelen = strlen(name);
45 fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name,
46 fdt_strerror(err));
Simon Glass1ede50c2012-01-21 10:14:48 -080047}
48
49/**
50 * Encode a series of arguments in a property value.
51 *
52 * @param disp Display information / options
53 * @param arg List of arguments from command line
54 * @param arg_count Number of arguments (may be 0)
55 * @param valuep Returns buffer containing value
Nicolas Ioossc17a8112017-03-04 14:26:47 +010056 * @param value_len Returns length of value encoded
Simon Glass1ede50c2012-01-21 10:14:48 -080057 */
58static int encode_value(struct display_info *disp, char **arg, int arg_count,
59 char **valuep, int *value_len)
60{
61 char *value = NULL; /* holding area for value */
62 int value_size = 0; /* size of holding area */
63 char *ptr; /* pointer to current value position */
64 int len; /* length of this cell/string/byte */
65 int ival;
66 int upto; /* the number of bytes we have written to buf */
67 char fmt[3];
68
69 upto = 0;
70
71 if (disp->verbose)
72 fprintf(stderr, "Decoding value:\n");
73
74 fmt[0] = '%';
75 fmt[1] = disp->type ? disp->type : 'd';
76 fmt[2] = '\0';
77 for (; arg_count > 0; arg++, arg_count--, upto += len) {
78 /* assume integer unless told otherwise */
79 if (disp->type == 's')
80 len = strlen(*arg) + 1;
81 else
82 len = disp->size == -1 ? 4 : disp->size;
83
84 /* enlarge our value buffer by a suitable margin if needed */
85 if (upto + len > value_size) {
86 value_size = (upto + len) + 500;
Heinrich Schuchardt8ce36472014-03-02 21:39:01 +010087 value = xrealloc(value, value_size);
Simon Glass1ede50c2012-01-21 10:14:48 -080088 }
89
90 ptr = value + upto;
91 if (disp->type == 's') {
92 memcpy(ptr, *arg, len);
93 if (disp->verbose)
94 fprintf(stderr, "\tstring: '%s'\n", ptr);
95 } else {
David Gibsonbad5b282017-03-06 12:08:53 +110096 fdt32_t *iptr = (fdt32_t *)ptr;
Simon Glass1ede50c2012-01-21 10:14:48 -080097 sscanf(*arg, fmt, &ival);
98 if (len == 4)
99 *iptr = cpu_to_fdt32(ival);
100 else
101 *ptr = (uint8_t)ival;
102 if (disp->verbose) {
103 fprintf(stderr, "\t%s: %d\n",
104 disp->size == 1 ? "byte" :
105 disp->size == 2 ? "short" : "int",
106 ival);
107 }
108 }
109 }
110 *value_len = upto;
111 *valuep = value;
112 if (disp->verbose)
113 fprintf(stderr, "Value size %d\n", upto);
114 return 0;
115}
116
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000117#define ALIGN(x) (((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1))
118
David Gibson3b62fda2017-10-18 16:59:43 +1100119static char *realloc_fdt(char *fdt, int delta)
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000120{
121 int new_sz = fdt_totalsize(fdt) + delta;
122 fdt = xrealloc(fdt, new_sz);
123 fdt_open_into(fdt, fdt, new_sz);
124 return fdt;
125}
126
127static char *realloc_node(char *fdt, const char *name)
128{
129 int delta;
130 /* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */
131 delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1)
132 + FDT_TAGSIZE;
David Gibson3b62fda2017-10-18 16:59:43 +1100133 return realloc_fdt(fdt, delta);
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000134}
135
136static char *realloc_property(char *fdt, int nodeoffset,
137 const char *name, int newlen)
138{
139 int delta = 0;
140 int oldlen = 0;
141
142 if (!fdt_get_property(fdt, nodeoffset, name, &oldlen))
143 /* strings + property header */
144 delta = sizeof(struct fdt_property) + strlen(name) + 1;
145
146 if (newlen > oldlen)
147 /* actual value in off_struct */
148 delta += ALIGN(newlen) - ALIGN(oldlen);
149
David Gibson3b62fda2017-10-18 16:59:43 +1100150 return realloc_fdt(fdt, delta);
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000151}
152
153static int store_key_value(char **blob, const char *node_name,
Simon Glass1ede50c2012-01-21 10:14:48 -0800154 const char *property, const char *buf, int len)
155{
156 int node;
157 int err;
158
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000159 node = fdt_path_offset(*blob, node_name);
Simon Glass1ede50c2012-01-21 10:14:48 -0800160 if (node < 0) {
Simon Glass3553dfa2012-07-10 05:56:47 -0700161 report_error(node_name, -1, node);
Simon Glass1ede50c2012-01-21 10:14:48 -0800162 return -1;
163 }
164
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000165 err = fdt_setprop(*blob, node, property, buf, len);
166 if (err == -FDT_ERR_NOSPACE) {
167 *blob = realloc_property(*blob, node, property, len);
168 err = fdt_setprop(*blob, node, property, buf, len);
169 }
Simon Glass1ede50c2012-01-21 10:14:48 -0800170 if (err) {
Simon Glass3553dfa2012-07-10 05:56:47 -0700171 report_error(property, -1, err);
Simon Glass1ede50c2012-01-21 10:14:48 -0800172 return -1;
173 }
174 return 0;
175}
176
Simon Glassd46c2de2012-07-12 08:52:49 -0700177/**
Simon Glassf807af12012-07-12 08:52:51 -0700178 * Create paths as needed for all components of a path
179 *
180 * Any components of the path that do not exist are created. Errors are
181 * reported.
182 *
183 * @param blob FDT blob to write into
184 * @param in_path Path to process
185 * @return 0 if ok, -1 on error
186 */
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000187static int create_paths(char **blob, const char *in_path)
Simon Glassf807af12012-07-12 08:52:51 -0700188{
189 const char *path = in_path;
190 const char *sep;
191 int node, offset = 0;
192
193 /* skip leading '/' */
194 while (*path == '/')
195 path++;
196
197 for (sep = path; *sep; path = sep + 1, offset = node) {
198 /* equivalent to strchrnul(), but it requires _GNU_SOURCE */
199 sep = strchr(path, '/');
200 if (!sep)
201 sep = path + strlen(path);
202
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000203 node = fdt_subnode_offset_namelen(*blob, offset, path,
Simon Glassf807af12012-07-12 08:52:51 -0700204 sep - path);
205 if (node == -FDT_ERR_NOTFOUND) {
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000206 *blob = realloc_node(*blob, path);
207 node = fdt_add_subnode_namelen(*blob, offset, path,
Simon Glassf807af12012-07-12 08:52:51 -0700208 sep - path);
209 }
210 if (node < 0) {
211 report_error(path, sep - path, node);
212 return -1;
213 }
214 }
215
216 return 0;
217}
218
219/**
Simon Glassd46c2de2012-07-12 08:52:49 -0700220 * Create a new node in the fdt.
221 *
222 * This will overwrite the node_name string. Any error is reported.
223 *
224 * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this.
225 *
226 * @param blob FDT blob to write into
227 * @param node_name Name of node to create
228 * @return new node offset if found, or -1 on failure
229 */
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000230static int create_node(char **blob, const char *node_name)
Simon Glassd46c2de2012-07-12 08:52:49 -0700231{
232 int node = 0;
233 char *p;
234
235 p = strrchr(node_name, '/');
236 if (!p) {
Simon Glass3553dfa2012-07-10 05:56:47 -0700237 report_error(node_name, -1, -FDT_ERR_BADPATH);
Simon Glassd46c2de2012-07-12 08:52:49 -0700238 return -1;
239 }
240 *p = '\0';
241
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000242 *blob = realloc_node(*blob, p + 1);
243
Simon Glassd46c2de2012-07-12 08:52:49 -0700244 if (p > node_name) {
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000245 node = fdt_path_offset(*blob, node_name);
Simon Glassd46c2de2012-07-12 08:52:49 -0700246 if (node < 0) {
Simon Glass3553dfa2012-07-10 05:56:47 -0700247 report_error(node_name, -1, node);
Simon Glassd46c2de2012-07-12 08:52:49 -0700248 return -1;
249 }
250 }
251
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000252 node = fdt_add_subnode(*blob, node, p + 1);
Simon Glassd46c2de2012-07-12 08:52:49 -0700253 if (node < 0) {
Simon Glass3553dfa2012-07-10 05:56:47 -0700254 report_error(p + 1, -1, node);
Simon Glassd46c2de2012-07-12 08:52:49 -0700255 return -1;
256 }
257
258 return 0;
259}
260
Wang Longaa719612015-01-23 01:25:20 +0000261/**
262 * Delete a property of a node in the fdt.
263 *
264 * @param blob FDT blob to write into
265 * @param node_name Path to node containing the property to delete
266 * @param prop_name Name of property to delete
267 * @return 0 on success, or -1 on failure
268 */
269static int delete_prop(char *blob, const char *node_name, const char *prop_name)
270{
271 int node = 0;
272
273 node = fdt_path_offset(blob, node_name);
274 if (node < 0) {
275 report_error(node_name, -1, node);
276 return -1;
277 }
278
279 node = fdt_delprop(blob, node, prop_name);
280 if (node < 0) {
281 report_error(node_name, -1, node);
282 return -1;
283 }
284
285 return 0;
286}
287
288/**
289 * Delete a node in the fdt.
290 *
291 * @param blob FDT blob to write into
292 * @param node_name Name of node to delete
293 * @return 0 on success, or -1 on failure
294 */
295static int delete_node(char *blob, const char *node_name)
296{
297 int node = 0;
298
299 node = fdt_path_offset(blob, node_name);
300 if (node < 0) {
301 report_error(node_name, -1, node);
302 return -1;
303 }
304
305 node = fdt_del_node(blob, node);
306 if (node < 0) {
307 report_error(node_name, -1, node);
308 return -1;
309 }
310
311 return 0;
312}
313
Simon Glass1ede50c2012-01-21 10:14:48 -0800314static int do_fdtput(struct display_info *disp, const char *filename,
315 char **arg, int arg_count)
316{
Jean-Christophe Duboisc5390752016-07-13 00:36:21 +0200317 char *value = NULL;
Simon Glass1ede50c2012-01-21 10:14:48 -0800318 char *blob;
Wang Longaa719612015-01-23 01:25:20 +0000319 char *node;
Simon Glass1ede50c2012-01-21 10:14:48 -0800320 int len, ret = 0;
321
David Gibson6473a212018-03-16 18:27:03 +1100322 blob = utilfdt_read(filename, NULL);
Simon Glass1ede50c2012-01-21 10:14:48 -0800323 if (!blob)
324 return -1;
325
Simon Glassf58dff52012-07-12 08:52:48 -0700326 switch (disp->oper) {
327 case OPER_WRITE_PROP:
328 /*
329 * Convert the arguments into a single binary value, then
330 * store them into the property.
331 */
332 assert(arg_count >= 2);
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000333 if (disp->auto_path && create_paths(&blob, *arg))
Simon Glassf807af12012-07-12 08:52:51 -0700334 return -1;
Simon Glassf58dff52012-07-12 08:52:48 -0700335 if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000336 store_key_value(&blob, *arg, arg[1], value, len))
Simon Glassf58dff52012-07-12 08:52:48 -0700337 ret = -1;
338 break;
Simon Glassd46c2de2012-07-12 08:52:49 -0700339 case OPER_CREATE_NODE:
Simon Glassf807af12012-07-12 08:52:51 -0700340 for (; ret >= 0 && arg_count--; arg++) {
341 if (disp->auto_path)
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000342 ret = create_paths(&blob, *arg);
Simon Glassf807af12012-07-12 08:52:51 -0700343 else
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000344 ret = create_node(&blob, *arg);
Simon Glassf807af12012-07-12 08:52:51 -0700345 }
Simon Glassd46c2de2012-07-12 08:52:49 -0700346 break;
Wang Longaa719612015-01-23 01:25:20 +0000347 case OPER_REMOVE_NODE:
348 for (; ret >= 0 && arg_count--; arg++)
349 ret = delete_node(blob, *arg);
350 break;
351 case OPER_DELETE_PROP:
352 node = *arg;
353 for (arg++; ret >= 0 && arg_count-- > 1; arg++)
354 ret = delete_prop(blob, node, *arg);
355 break;
Simon Glassf58dff52012-07-12 08:52:48 -0700356 }
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000357 if (ret >= 0) {
358 fdt_pack(blob);
Simon Glass1ede50c2012-01-21 10:14:48 -0800359 ret = utilfdt_write(filename, blob);
Srinivas Kandagatlad2146552013-05-29 12:47:38 +1000360 }
Simon Glass1ede50c2012-01-21 10:14:48 -0800361
362 free(blob);
Jean-Christophe Duboisc5390752016-07-13 00:36:21 +0200363
364 if (value) {
365 free(value);
366 }
367
Simon Glass1ede50c2012-01-21 10:14:48 -0800368 return ret;
369}
370
Mike Frysinger03449b82013-05-24 18:02:35 +1000371/* Usage related data. */
372static const char usage_synopsis[] =
373 "write a property value to a device tree\n"
Simon Glassbb21f0a2012-07-10 05:56:44 -0700374 " fdtput <options> <dt file> <node> <property> [<value>...]\n"
Simon Glassd46c2de2012-07-12 08:52:49 -0700375 " fdtput -c <options> <dt file> [<node>...]\n"
Wang Longaa719612015-01-23 01:25:20 +0000376 " fdtput -r <options> <dt file> [<node>...]\n"
377 " fdtput -d <options> <dt file> <node> [<property>...]\n"
Mike Frysinger03449b82013-05-24 18:02:35 +1000378 "\n"
379 "The command line arguments are joined together into a single value.\n"
Simon Glass1ede50c2012-01-21 10:14:48 -0800380 USAGE_TYPE_MSG;
Wang Longaa719612015-01-23 01:25:20 +0000381static const char usage_short_opts[] = "crdpt:v" USAGE_COMMON_SHORT_OPTS;
Mike Frysinger03449b82013-05-24 18:02:35 +1000382static struct option const usage_long_opts[] = {
383 {"create", no_argument, NULL, 'c'},
Wang Longaa719612015-01-23 01:25:20 +0000384 {"remove", no_argument, NULL, 'r'},
385 {"delete", no_argument, NULL, 'd'},
Mike Frysinger03449b82013-05-24 18:02:35 +1000386 {"auto-path", no_argument, NULL, 'p'},
387 {"type", a_argument, NULL, 't'},
388 {"verbose", no_argument, NULL, 'v'},
389 USAGE_COMMON_LONG_OPTS,
390};
391static const char * const usage_opts_help[] = {
392 "Create nodes if they don't already exist",
Wang Longaa719612015-01-23 01:25:20 +0000393 "Delete nodes (and any subnodes) if they already exist",
394 "Delete properties if they already exist",
Mike Frysinger03449b82013-05-24 18:02:35 +1000395 "Automatically create nodes as needed for the node path",
396 "Type of data",
397 "Display each value decoded from command line",
398 USAGE_COMMON_OPTS_HELP
399};
Simon Glass1ede50c2012-01-21 10:14:48 -0800400
401int main(int argc, char *argv[])
402{
Mike Frysinger03449b82013-05-24 18:02:35 +1000403 int opt;
Simon Glass1ede50c2012-01-21 10:14:48 -0800404 struct display_info disp;
405 char *filename = NULL;
406
407 memset(&disp, '\0', sizeof(disp));
408 disp.size = -1;
Simon Glassf58dff52012-07-12 08:52:48 -0700409 disp.oper = OPER_WRITE_PROP;
Mike Frysinger03449b82013-05-24 18:02:35 +1000410 while ((opt = util_getopt_long()) != EOF) {
Simon Glass1ede50c2012-01-21 10:14:48 -0800411 /*
412 * TODO: add options to:
Simon Glass1ede50c2012-01-21 10:14:48 -0800413 * - rename node
414 * - pack fdt before writing
415 * - set amount of free space when writing
Simon Glass1ede50c2012-01-21 10:14:48 -0800416 */
Mike Frysinger03449b82013-05-24 18:02:35 +1000417 switch (opt) {
418 case_USAGE_COMMON_FLAGS
419
Simon Glassd46c2de2012-07-12 08:52:49 -0700420 case 'c':
421 disp.oper = OPER_CREATE_NODE;
422 break;
Wang Longaa719612015-01-23 01:25:20 +0000423 case 'r':
424 disp.oper = OPER_REMOVE_NODE;
425 break;
426 case 'd':
427 disp.oper = OPER_DELETE_PROP;
428 break;
Simon Glassf807af12012-07-12 08:52:51 -0700429 case 'p':
430 disp.auto_path = 1;
431 break;
Simon Glass1ede50c2012-01-21 10:14:48 -0800432 case 't':
433 if (utilfdt_decode_type(optarg, &disp.type,
434 &disp.size))
Mike Frysingerb9e80652013-05-24 18:04:43 +1000435 usage("Invalid type string");
Pierre-Clément Tosief1978a2022-05-30 20:57:34 +0100436 if (disp.type == 'r')
437 usage("Unsupported raw data type");
Simon Glass1ede50c2012-01-21 10:14:48 -0800438 break;
439
440 case 'v':
441 disp.verbose = 1;
442 break;
443 }
444 }
445
446 if (optind < argc)
447 filename = argv[optind++];
448 if (!filename)
Mike Frysingerb9e80652013-05-24 18:04:43 +1000449 usage("missing filename");
Simon Glass1ede50c2012-01-21 10:14:48 -0800450
451 argv += optind;
452 argc -= optind;
453
Simon Glassf58dff52012-07-12 08:52:48 -0700454 if (disp.oper == OPER_WRITE_PROP) {
455 if (argc < 1)
Mike Frysingerb9e80652013-05-24 18:04:43 +1000456 usage("missing node");
Simon Glassf58dff52012-07-12 08:52:48 -0700457 if (argc < 2)
Mike Frysingerb9e80652013-05-24 18:04:43 +1000458 usage("missing property");
Simon Glassf58dff52012-07-12 08:52:48 -0700459 }
Simon Glass1ede50c2012-01-21 10:14:48 -0800460
Wang Longaa719612015-01-23 01:25:20 +0000461 if (disp.oper == OPER_DELETE_PROP)
462 if (argc < 1)
463 usage("missing node");
464
Simon Glass1ede50c2012-01-21 10:14:48 -0800465 if (do_fdtput(&disp, filename, argv, argc))
466 return 1;
467 return 0;
468}