blob: 6fbd9205d3838c911eb353c552c9b20dd29fa94b [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Gerald Van Baren781e09e2007-03-31 12:22:10 -04002/*
3 * (C) Copyright 2007
4 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
5 * Based on code written by:
6 * Pantelis Antoniou <pantelis.antoniou@gmail.com> and
7 * Matthew McClintock <msm@freescale.com>
Gerald Van Baren781e09e2007-03-31 12:22:10 -04008 */
9
10#include <common.h>
11#include <command.h>
Simon Glassc7694dd2019-08-01 09:46:46 -060012#include <env.h>
Simon Glass4d72caa2020-05-10 11:40:01 -060013#include <image.h>
Gerald Van Baren781e09e2007-03-31 12:22:10 -040014#include <linux/ctype.h>
15#include <linux/types.h>
Gerald Van Baren781e09e2007-03-31 12:22:10 -040016#include <asm/global_data.h>
Masahiro Yamadab08c8c42018-03-05 01:20:11 +090017#include <linux/libfdt.h>
Gerald Van Baren64dbbd42007-04-06 14:19:43 -040018#include <fdt_support.h>
Joe Hershberger0eb25b62015-03-22 17:08:59 -050019#include <mapmem.h>
Simon Glassa92fd652013-04-20 08:42:45 +000020#include <asm/io.h>
Gerald Van Baren781e09e2007-03-31 12:22:10 -040021
22#define MAX_LEVEL 32 /* how deeply nested we will go */
Gerald Van Barenfd61e552007-06-25 23:25:28 -040023#define SCRATCHPAD 1024 /* bytes of scratchpad memory */
Gerald Van Baren781e09e2007-03-31 12:22:10 -040024
25/*
26 * Global data (for the gd->bd)
27 */
28DECLARE_GLOBAL_DATA_PTR;
29
Wolfgang Denk54841ab2010-06-28 22:00:46 +020030static int fdt_parse_prop(char *const*newval, int count, char *data, int *len);
Kumar Galadbaf07c2007-11-21 14:07:46 -060031static int fdt_print(const char *pathp, char *prop, int depth);
Joe Hershbergerbc802952012-08-17 10:34:37 +000032static int is_printable_string(const void *data, int len);
Gerald Van Baren781e09e2007-03-31 12:22:10 -040033
Gerald Van Baren781e09e2007-03-31 12:22:10 -040034/*
Gerald Van Barenae9e97f2008-06-10 22:15:58 -040035 * The working_fdt points to our working flattened device tree.
36 */
37struct fdt_header *working_fdt;
38
Joe Hershberger90fbee32015-02-04 21:56:53 -060039void set_working_fdt_addr(ulong addr)
Kumar Gala54f9c862008-08-15 08:24:39 -050040{
Simon Glassa92fd652013-04-20 08:42:45 +000041 void *buf;
42
Joe Hershberger90fbee32015-02-04 21:56:53 -060043 buf = map_sysmem(addr, 0);
Simon Glassa92fd652013-04-20 08:42:45 +000044 working_fdt = buf;
Simon Glass018f5302017-08-03 12:22:10 -060045 env_set_hex("fdtaddr", addr);
Kumar Gala54f9c862008-08-15 08:24:39 -050046}
47
Gerald Van Barenae9e97f2008-06-10 22:15:58 -040048/*
Joe Hershbergerbc802952012-08-17 10:34:37 +000049 * Get a value from the fdt and format it to be set in the environment
50 */
Marek Vasut13982ce2022-07-08 23:50:43 +020051static int fdt_value_env_set(const void *nodep, int len,
52 const char *var, int index)
Joe Hershbergerbc802952012-08-17 10:34:37 +000053{
Marek Vasut13982ce2022-07-08 23:50:43 +020054 if (is_printable_string(nodep, len)) {
55 const char *nodec = (const char *)nodep;
56 int i;
57
58 /*
59 * Iterate over all members in stringlist and find the one at
60 * offset $index. If no such index exists, indicate failure.
61 */
62 for (i = 0; i < len; i += strlen(nodec) + 1) {
63 if (index-- > 0)
64 continue;
65
66 env_set(var, nodec + i);
67 return 0;
68 }
69
70 return 1;
71 } else if (len == 4) {
Joe Hershbergerbc802952012-08-17 10:34:37 +000072 char buf[11];
73
Andreas Färberb05bf6c2017-01-09 16:08:02 +010074 sprintf(buf, "0x%08X", fdt32_to_cpu(*(fdt32_t *)nodep));
Simon Glass382bee52017-08-03 12:22:09 -060075 env_set(var, buf);
Joe Hershbergerbc802952012-08-17 10:34:37 +000076 } else if (len%4 == 0 && len <= 20) {
77 /* Needed to print things like sha1 hashes. */
78 char buf[41];
79 int i;
80
81 for (i = 0; i < len; i += sizeof(unsigned int))
82 sprintf(buf + (i * 2), "%08x",
83 *(unsigned int *)(nodep + i));
Simon Glass382bee52017-08-03 12:22:09 -060084 env_set(var, buf);
Joe Hershbergerbc802952012-08-17 10:34:37 +000085 } else {
86 printf("error: unprintable value\n");
87 return 1;
88 }
89 return 0;
90}
91
Heiko Schocher82441272018-11-15 06:06:06 +010092static const char * const fdt_member_table[] = {
93 "magic",
94 "totalsize",
95 "off_dt_struct",
96 "off_dt_strings",
97 "off_mem_rsvmap",
98 "version",
99 "last_comp_version",
100 "boot_cpuid_phys",
101 "size_dt_strings",
102 "size_dt_struct",
103};
104
Simon Glass09140112020-05-10 11:40:03 -0600105static int fdt_get_header_value(int argc, char *const argv[])
Heiko Schocher82441272018-11-15 06:06:06 +0100106{
107 fdt32_t *fdtp = (fdt32_t *)working_fdt;
108 ulong val;
109 int i;
110
111 if (argv[2][0] != 'g')
112 return CMD_RET_FAILURE;
113
114 for (i = 0; i < ARRAY_SIZE(fdt_member_table); i++) {
115 if (strcmp(fdt_member_table[i], argv[4]))
116 continue;
117
118 val = fdt32_to_cpu(fdtp[i]);
119 env_set_hex(argv[3], val);
120 return CMD_RET_SUCCESS;
121 }
122
123 return CMD_RET_FAILURE;
124}
125
Joe Hershbergerbc802952012-08-17 10:34:37 +0000126/*
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400127 * Flattened Device Tree command, see the help for parameter definitions.
128 */
Simon Glass09140112020-05-10 11:40:03 -0600129static int do_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400130{
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200131 if (argc < 2)
Simon Glass4c12eeb2011-12-10 08:44:01 +0000132 return CMD_RET_USAGE;
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400133
Simon Glass0c929632021-07-21 14:55:25 -0600134 /* fdt addr: Set the address of the fdt */
Maxime Ripardf0ed68e2016-07-05 10:26:34 +0200135 if (strncmp(argv[1], "ad", 2) == 0) {
Kumar Gala54f9c862008-08-15 08:24:39 -0500136 unsigned long addr;
Simon Glass4b578652013-04-20 08:42:44 +0000137 int control = 0;
Peter Hoyese9496ec2022-03-31 11:53:22 +0100138 int quiet = 0;
Simon Glass4b578652013-04-20 08:42:44 +0000139 struct fdt_header *blob;
Simon Glass0c929632021-07-21 14:55:25 -0600140
141 /* Set the address [and length] of the fdt */
Simon Glass4b578652013-04-20 08:42:44 +0000142 argc -= 2;
143 argv += 2;
Peter Hoyese9496ec2022-03-31 11:53:22 +0100144 while (argc > 0 && **argv == '-') {
145 char *arg = *argv;
146
147 while (*++arg) {
148 switch (*arg) {
149 case 'c':
150 control = 1;
151 break;
152 case 'q':
153 quiet = 1;
154 break;
155 default:
156 return CMD_RET_USAGE;
157 }
158 }
Simon Glass4b578652013-04-20 08:42:44 +0000159 argc--;
160 argv++;
161 }
Simon Glass4b578652013-04-20 08:42:44 +0000162 if (argc == 0) {
163 if (control)
164 blob = (struct fdt_header *)gd->fdt_blob;
165 else
166 blob = working_fdt;
167 if (!blob || !fdt_valid(&blob))
Kumar Gala7dbc38a2008-08-15 08:24:35 -0500168 return 1;
Simon Glassb29a0db2021-07-21 14:55:26 -0600169 printf("%s fdt: %08lx\n",
170 control ? "Control" : "Working",
Joe Hershbergerc71a0162015-02-04 21:56:54 -0600171 control ? (ulong)map_to_sysmem(blob) :
Simon Glassb29a0db2021-07-21 14:55:26 -0600172 env_get_hex("fdtaddr", 0));
Kumar Gala7dbc38a2008-08-15 08:24:35 -0500173 return 0;
174 }
175
Simon Glass7e5f4602021-07-24 09:03:29 -0600176 addr = hextoul(argv[0], NULL);
Simon Glassa92fd652013-04-20 08:42:45 +0000177 blob = map_sysmem(addr, 0);
Peter Hoyese9496ec2022-03-31 11:53:22 +0100178 if ((quiet && fdt_check_header(blob)) ||
179 (!quiet && !fdt_valid(&blob)))
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400180 return 1;
Simon Glass4b578652013-04-20 08:42:44 +0000181 if (control)
182 gd->fdt_blob = blob;
183 else
Joe Hershberger90fbee32015-02-04 21:56:53 -0600184 set_working_fdt_addr(addr);
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400185
Simon Glass4b578652013-04-20 08:42:44 +0000186 if (argc >= 2) {
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400187 int len;
188 int err;
Simon Glass0c929632021-07-21 14:55:25 -0600189
190 /* Optional new length */
Simon Glass7e5f4602021-07-24 09:03:29 -0600191 len = hextoul(argv[1], NULL);
Simon Glass4b578652013-04-20 08:42:44 +0000192 if (len < fdt_totalsize(blob)) {
Peter Hoyese9496ec2022-03-31 11:53:22 +0100193 if (!quiet)
194 printf("New length %d < existing length %d, ignoring\n",
195 len, fdt_totalsize(blob));
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400196 } else {
Simon Glass0c929632021-07-21 14:55:25 -0600197 /* Open in place with a new length */
Simon Glass4b578652013-04-20 08:42:44 +0000198 err = fdt_open_into(blob, blob, len);
Peter Hoyese9496ec2022-03-31 11:53:22 +0100199 if (!quiet && err != 0) {
Simon Glass0c929632021-07-21 14:55:25 -0600200 printf("libfdt fdt_open_into(): %s\n",
201 fdt_strerror(err));
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400202 }
203 }
204 }
205
Marek Vasute02c9452012-09-05 08:34:44 +0200206 return CMD_RET_SUCCESS;
207 }
208
209 if (!working_fdt) {
Simon Glass0c929632021-07-21 14:55:25 -0600210 puts("No FDT memory address configured. Please configure\n"
211 "the FDT address via \"fdt addr <address>\" command.\n"
212 "Aborting!\n");
Marek Vasute02c9452012-09-05 08:34:44 +0200213 return CMD_RET_FAILURE;
214 }
215
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200216 /*
Kim Phillipse489b9c2008-06-10 11:06:17 -0500217 * Move the working_fdt
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200218 */
Marek Vasute02c9452012-09-05 08:34:44 +0200219 if (strncmp(argv[1], "mo", 2) == 0) {
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400220 struct fdt_header *newaddr;
221 int len;
222 int err;
223
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200224 if (argc < 4)
Simon Glass4c12eeb2011-12-10 08:44:01 +0000225 return CMD_RET_USAGE;
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400226
227 /*
228 * Set the address and length of the fdt.
229 */
Simon Glass7e5f4602021-07-24 09:03:29 -0600230 working_fdt = (struct fdt_header *)hextoul(argv[2], NULL);
Simon Glassd14da912013-04-20 08:42:42 +0000231 if (!fdt_valid(&working_fdt))
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400232 return 1;
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400233
Simon Glass7e5f4602021-07-24 09:03:29 -0600234 newaddr = (struct fdt_header *)hextoul(argv[3], NULL);
Gerald Van Baren6be07cc2007-04-25 22:47:15 -0400235
236 /*
237 * If the user specifies a length, use that. Otherwise use the
238 * current length.
239 */
240 if (argc <= 4) {
Kim Phillipse489b9c2008-06-10 11:06:17 -0500241 len = fdt_totalsize(working_fdt);
Gerald Van Baren6be07cc2007-04-25 22:47:15 -0400242 } else {
Simon Glass7e5f4602021-07-24 09:03:29 -0600243 len = hextoul(argv[4], NULL);
Kim Phillipse489b9c2008-06-10 11:06:17 -0500244 if (len < fdt_totalsize(working_fdt)) {
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400245 printf ("New length 0x%X < existing length "
246 "0x%X, aborting.\n",
Kim Phillipse489b9c2008-06-10 11:06:17 -0500247 len, fdt_totalsize(working_fdt));
Gerald Van Baren6be07cc2007-04-25 22:47:15 -0400248 return 1;
249 }
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400250 }
251
252 /*
253 * Copy to the new location.
254 */
Kim Phillipse489b9c2008-06-10 11:06:17 -0500255 err = fdt_open_into(working_fdt, newaddr, len);
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400256 if (err != 0) {
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400257 printf ("libfdt fdt_open_into(): %s\n",
258 fdt_strerror(err));
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400259 return 1;
260 }
Hiroyuki Yokoyamab1a7e792018-10-18 20:43:54 +0200261 set_working_fdt_addr((ulong)newaddr);
Fabien Parentf7f191e2016-11-24 15:02:18 +0100262#ifdef CONFIG_OF_SYSTEM_SETUP
263 /* Call the board-specific fixup routine */
264 } else if (strncmp(argv[1], "sys", 3) == 0) {
265 int err = ft_system_setup(working_fdt, gd->bd);
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400266
Fabien Parentf7f191e2016-11-24 15:02:18 +0100267 if (err) {
268 printf("Failed to add system information to FDT: %s\n",
269 fdt_strerror(err));
270 return CMD_RET_FAILURE;
271 }
272#endif
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200273 /*
Gerald Van Baren25114032007-05-12 09:47:25 -0400274 * Make a new node
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200275 */
Gerald Van Baren2fb698b2008-06-09 21:02:17 -0400276 } else if (strncmp(argv[1], "mk", 2) == 0) {
Gerald Van Baren25114032007-05-12 09:47:25 -0400277 char *pathp; /* path */
278 char *nodep; /* new node to add */
279 int nodeoffset; /* node offset from libfdt */
280 int err;
281
282 /*
283 * Parameters: Node path, new node to be appended to the path.
284 */
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200285 if (argc < 4)
Simon Glass4c12eeb2011-12-10 08:44:01 +0000286 return CMD_RET_USAGE;
Gerald Van Baren25114032007-05-12 09:47:25 -0400287
288 pathp = argv[2];
289 nodep = argv[3];
290
Kim Phillipse489b9c2008-06-10 11:06:17 -0500291 nodeoffset = fdt_path_offset (working_fdt, pathp);
Gerald Van Baren25114032007-05-12 09:47:25 -0400292 if (nodeoffset < 0) {
293 /*
294 * Not found or something else bad happened.
295 */
Kumar Gala8d04f022007-10-24 11:04:22 -0500296 printf ("libfdt fdt_path_offset() returned %s\n",
Gerald Van Baren06e19a02007-05-21 23:27:16 -0400297 fdt_strerror(nodeoffset));
Gerald Van Baren25114032007-05-12 09:47:25 -0400298 return 1;
299 }
Kim Phillipse489b9c2008-06-10 11:06:17 -0500300 err = fdt_add_subnode(working_fdt, nodeoffset, nodep);
Gerald Van Baren25114032007-05-12 09:47:25 -0400301 if (err < 0) {
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400302 printf ("libfdt fdt_add_subnode(): %s\n",
303 fdt_strerror(err));
Gerald Van Baren25114032007-05-12 09:47:25 -0400304 return 1;
305 }
306
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200307 /*
Kim Phillipse489b9c2008-06-10 11:06:17 -0500308 * Set the value of a property in the working_fdt.
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200309 */
Tom Warren0688b752020-03-26 15:20:44 -0700310 } else if (strncmp(argv[1], "se", 2) == 0) {
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400311 char *pathp; /* path */
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400312 char *prop; /* property */
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400313 int nodeoffset; /* node offset from libfdt */
Bernhard Messerklinger6dfd65f2017-09-28 11:29:52 +0200314 static char data[SCRATCHPAD] __aligned(4);/* property storage */
Hannes Schmelzer9620d872017-05-30 15:05:44 +0200315 const void *ptmp;
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400316 int len; /* new length of the property */
317 int ret; /* return value */
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400318
319 /*
Gerald Van Barenea6d8be2008-01-05 14:52:04 -0500320 * Parameters: Node path, property, optional value.
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400321 */
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200322 if (argc < 4)
Simon Glass4c12eeb2011-12-10 08:44:01 +0000323 return CMD_RET_USAGE;
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400324
325 pathp = argv[2];
326 prop = argv[3];
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400327
Kim Phillipse489b9c2008-06-10 11:06:17 -0500328 nodeoffset = fdt_path_offset (working_fdt, pathp);
Gerald Van Baren25114032007-05-12 09:47:25 -0400329 if (nodeoffset < 0) {
330 /*
331 * Not found or something else bad happened.
332 */
Kumar Gala8d04f022007-10-24 11:04:22 -0500333 printf ("libfdt fdt_path_offset() returned %s\n",
Gerald Van Baren06e19a02007-05-21 23:27:16 -0400334 fdt_strerror(nodeoffset));
Gerald Van Baren25114032007-05-12 09:47:25 -0400335 return 1;
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400336 }
Gerald Van Baren25114032007-05-12 09:47:25 -0400337
Hannes Schmelzer9620d872017-05-30 15:05:44 +0200338 if (argc == 4) {
339 len = 0;
340 } else {
341 ptmp = fdt_getprop(working_fdt, nodeoffset, prop, &len);
342 if (len > SCRATCHPAD) {
343 printf("prop (%d) doesn't fit in scratchpad!\n",
344 len);
345 return 1;
346 }
Hannes Schmelzercee8c352017-08-18 14:41:14 +0200347 if (ptmp != NULL)
348 memcpy(data, ptmp, len);
349
Hannes Schmelzer9620d872017-05-30 15:05:44 +0200350 ret = fdt_parse_prop(&argv[4], argc - 4, data, &len);
351 if (ret != 0)
352 return ret;
353 }
354
Kim Phillipse489b9c2008-06-10 11:06:17 -0500355 ret = fdt_setprop(working_fdt, nodeoffset, prop, data, len);
Gerald Van Baren25114032007-05-12 09:47:25 -0400356 if (ret < 0) {
357 printf ("libfdt fdt_setprop(): %s\n", fdt_strerror(ret));
358 return 1;
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400359 }
360
Joe Hershbergerbc802952012-08-17 10:34:37 +0000361 /********************************************************************
362 * Get the value of a property in the working_fdt.
363 ********************************************************************/
364 } else if (argv[1][0] == 'g') {
365 char *subcmd; /* sub-command */
366 char *pathp; /* path */
367 char *prop; /* property */
368 char *var; /* variable to store result */
369 int nodeoffset; /* node offset from libfdt */
370 const void *nodep; /* property node pointer */
371 int len = 0; /* new length of the property */
372
373 /*
374 * Parameters: Node path, property, optional value.
375 */
376 if (argc < 5)
377 return CMD_RET_USAGE;
378
379 subcmd = argv[2];
380
381 if (argc < 6 && subcmd[0] != 's')
382 return CMD_RET_USAGE;
383
384 var = argv[3];
385 pathp = argv[4];
386 prop = argv[5];
387
388 nodeoffset = fdt_path_offset(working_fdt, pathp);
389 if (nodeoffset < 0) {
390 /*
391 * Not found or something else bad happened.
392 */
393 printf("libfdt fdt_path_offset() returned %s\n",
394 fdt_strerror(nodeoffset));
395 return 1;
396 }
397
398 if (subcmd[0] == 'n' || (subcmd[0] == 's' && argc == 5)) {
Simon Glass7e5f4602021-07-24 09:03:29 -0600399 int req_index = -1;
Joe Hershbergerbc802952012-08-17 10:34:37 +0000400 int startDepth = fdt_node_depth(
401 working_fdt, nodeoffset);
402 int curDepth = startDepth;
Simon Glass7e5f4602021-07-24 09:03:29 -0600403 int cur_index = -1;
Joe Hershbergerbc802952012-08-17 10:34:37 +0000404 int nextNodeOffset = fdt_next_node(
405 working_fdt, nodeoffset, &curDepth);
406
407 if (subcmd[0] == 'n')
Simon Glass7e5f4602021-07-24 09:03:29 -0600408 req_index = hextoul(argv[5], NULL);
Joe Hershbergerbc802952012-08-17 10:34:37 +0000409
410 while (curDepth > startDepth) {
411 if (curDepth == startDepth + 1)
Simon Glass7e5f4602021-07-24 09:03:29 -0600412 cur_index++;
413 if (subcmd[0] == 'n' &&
414 cur_index == req_index) {
Simon Glass382bee52017-08-03 12:22:09 -0600415 const char *node_name;
Joe Hershbergerbc802952012-08-17 10:34:37 +0000416
Simon Glass382bee52017-08-03 12:22:09 -0600417 node_name = fdt_get_name(working_fdt,
418 nextNodeOffset,
419 NULL);
420 env_set(var, node_name);
Joe Hershbergerbc802952012-08-17 10:34:37 +0000421 return 0;
422 }
423 nextNodeOffset = fdt_next_node(
424 working_fdt, nextNodeOffset, &curDepth);
425 if (nextNodeOffset < 0)
426 break;
427 }
428 if (subcmd[0] == 's') {
429 /* get the num nodes at this level */
Simon Glass7e5f4602021-07-24 09:03:29 -0600430 env_set_ulong(var, cur_index + 1);
Joe Hershbergerbc802952012-08-17 10:34:37 +0000431 } else {
432 /* node index not found */
433 printf("libfdt node not found\n");
434 return 1;
435 }
436 } else {
437 nodep = fdt_getprop(
438 working_fdt, nodeoffset, prop, &len);
439 if (len == 0) {
440 /* no property value */
Simon Glass382bee52017-08-03 12:22:09 -0600441 env_set(var, "");
Joe Hershbergerbc802952012-08-17 10:34:37 +0000442 return 0;
Simon Glass72c98ed2017-06-07 10:28:41 -0600443 } else if (nodep && len > 0) {
Joe Hershbergerbc802952012-08-17 10:34:37 +0000444 if (subcmd[0] == 'v') {
Marek Vasut13982ce2022-07-08 23:50:43 +0200445 int index = 0;
Joe Hershbergerbc802952012-08-17 10:34:37 +0000446 int ret;
447
Marek Vasut13982ce2022-07-08 23:50:43 +0200448 if (argc == 7)
449 index = simple_strtoul(argv[6], NULL, 10);
450
Simon Glass382bee52017-08-03 12:22:09 -0600451 ret = fdt_value_env_set(nodep, len,
Marek Vasut13982ce2022-07-08 23:50:43 +0200452 var, index);
Joe Hershbergerbc802952012-08-17 10:34:37 +0000453 if (ret != 0)
454 return ret;
455 } else if (subcmd[0] == 'a') {
456 /* Get address */
457 char buf[11];
458
Tom Rini085b9c32012-10-29 14:53:18 +0000459 sprintf(buf, "0x%p", nodep);
Simon Glass382bee52017-08-03 12:22:09 -0600460 env_set(var, buf);
Joe Hershbergerbc802952012-08-17 10:34:37 +0000461 } else if (subcmd[0] == 's') {
462 /* Get size */
463 char buf[11];
464
465 sprintf(buf, "0x%08X", len);
Simon Glass382bee52017-08-03 12:22:09 -0600466 env_set(var, buf);
Joe Hershbergerbc802952012-08-17 10:34:37 +0000467 } else
468 return CMD_RET_USAGE;
469 return 0;
470 } else {
471 printf("libfdt fdt_getprop(): %s\n",
472 fdt_strerror(len));
473 return 1;
474 }
475 }
476
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200477 /*
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400478 * Print (recursive) / List (single level)
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200479 */
Gerald Van Baren25114032007-05-12 09:47:25 -0400480 } else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) {
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400481 int depth = MAX_LEVEL; /* how deep to print */
482 char *pathp; /* path */
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400483 char *prop; /* property */
484 int ret; /* return value */
Kumar Galaf738b4a2007-10-25 16:15:07 -0500485 static char root[2] = "/";
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400486
487 /*
488 * list is an alias for print, but limited to 1 level
489 */
Gerald Van Baren25114032007-05-12 09:47:25 -0400490 if (argv[1][0] == 'l') {
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400491 depth = 1;
492 }
493
494 /*
495 * Get the starting path. The root node is an oddball,
496 * the offset is zero and has no name.
497 */
Kumar Galaf738b4a2007-10-25 16:15:07 -0500498 if (argc == 2)
499 pathp = root;
500 else
501 pathp = argv[2];
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400502 if (argc > 3)
503 prop = argv[3];
504 else
505 prop = NULL;
506
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400507 ret = fdt_print(pathp, prop, depth);
508 if (ret != 0)
509 return ret;
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400510
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200511 /*
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400512 * Remove a property/node
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200513 */
Gerald Van Baren2fb698b2008-06-09 21:02:17 -0400514 } else if (strncmp(argv[1], "rm", 2) == 0) {
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400515 int nodeoffset; /* node offset from libfdt */
516 int err;
517
518 /*
519 * Get the path. The root node is an oddball, the offset
520 * is zero and has no name.
521 */
Kim Phillipse489b9c2008-06-10 11:06:17 -0500522 nodeoffset = fdt_path_offset (working_fdt, argv[2]);
Gerald Van Baren25114032007-05-12 09:47:25 -0400523 if (nodeoffset < 0) {
524 /*
525 * Not found or something else bad happened.
526 */
Kumar Gala8d04f022007-10-24 11:04:22 -0500527 printf ("libfdt fdt_path_offset() returned %s\n",
Gerald Van Baren06e19a02007-05-21 23:27:16 -0400528 fdt_strerror(nodeoffset));
Gerald Van Baren25114032007-05-12 09:47:25 -0400529 return 1;
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400530 }
531 /*
532 * Do the delete. A fourth parameter means delete a property,
533 * otherwise delete the node.
534 */
535 if (argc > 3) {
Kim Phillipse489b9c2008-06-10 11:06:17 -0500536 err = fdt_delprop(working_fdt, nodeoffset, argv[3]);
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400537 if (err < 0) {
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400538 printf("libfdt fdt_delprop(): %s\n",
539 fdt_strerror(err));
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400540 return err;
541 }
542 } else {
Kim Phillipse489b9c2008-06-10 11:06:17 -0500543 err = fdt_del_node(working_fdt, nodeoffset);
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400544 if (err < 0) {
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400545 printf("libfdt fdt_del_node(): %s\n",
546 fdt_strerror(err));
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400547 return err;
548 }
549 }
Kumar Gala804887e2008-02-15 03:34:36 -0600550
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200551 /*
Kumar Gala804887e2008-02-15 03:34:36 -0600552 * Display header info
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200553 */
Kumar Gala804887e2008-02-15 03:34:36 -0600554 } else if (argv[1][0] == 'h') {
Heiko Schocher82441272018-11-15 06:06:06 +0100555 if (argc == 5)
556 return fdt_get_header_value(argc, argv);
557
Kim Phillipse489b9c2008-06-10 11:06:17 -0500558 u32 version = fdt_version(working_fdt);
559 printf("magic:\t\t\t0x%x\n", fdt_magic(working_fdt));
560 printf("totalsize:\t\t0x%x (%d)\n", fdt_totalsize(working_fdt),
561 fdt_totalsize(working_fdt));
562 printf("off_dt_struct:\t\t0x%x\n",
563 fdt_off_dt_struct(working_fdt));
564 printf("off_dt_strings:\t\t0x%x\n",
565 fdt_off_dt_strings(working_fdt));
566 printf("off_mem_rsvmap:\t\t0x%x\n",
567 fdt_off_mem_rsvmap(working_fdt));
Kumar Gala804887e2008-02-15 03:34:36 -0600568 printf("version:\t\t%d\n", version);
Kim Phillipse489b9c2008-06-10 11:06:17 -0500569 printf("last_comp_version:\t%d\n",
570 fdt_last_comp_version(working_fdt));
Kumar Gala804887e2008-02-15 03:34:36 -0600571 if (version >= 2)
572 printf("boot_cpuid_phys:\t0x%x\n",
Kim Phillipse489b9c2008-06-10 11:06:17 -0500573 fdt_boot_cpuid_phys(working_fdt));
Kumar Gala804887e2008-02-15 03:34:36 -0600574 if (version >= 3)
575 printf("size_dt_strings:\t0x%x\n",
Kim Phillipse489b9c2008-06-10 11:06:17 -0500576 fdt_size_dt_strings(working_fdt));
Kumar Gala804887e2008-02-15 03:34:36 -0600577 if (version >= 17)
578 printf("size_dt_struct:\t\t0x%x\n",
Kim Phillipse489b9c2008-06-10 11:06:17 -0500579 fdt_size_dt_struct(working_fdt));
580 printf("number mem_rsv:\t\t0x%x\n",
581 fdt_num_mem_rsv(working_fdt));
Kumar Gala804887e2008-02-15 03:34:36 -0600582 printf("\n");
583
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200584 /*
Kumar Gala804887e2008-02-15 03:34:36 -0600585 * Set boot cpu id
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200586 */
Gerald Van Baren2fb698b2008-06-09 21:02:17 -0400587 } else if (strncmp(argv[1], "boo", 3) == 0) {
Simon Glass7e5f4602021-07-24 09:03:29 -0600588 unsigned long tmp = hextoul(argv[2], NULL);
Kim Phillipse489b9c2008-06-10 11:06:17 -0500589 fdt_set_boot_cpuid_phys(working_fdt, tmp);
Kumar Gala804887e2008-02-15 03:34:36 -0600590
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200591 /*
Kumar Gala804887e2008-02-15 03:34:36 -0600592 * memory command
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200593 */
Gerald Van Baren2fb698b2008-06-09 21:02:17 -0400594 } else if (strncmp(argv[1], "me", 2) == 0) {
Kumar Gala804887e2008-02-15 03:34:36 -0600595 uint64_t addr, size;
596 int err;
Heiko Schocher4b142fe2009-12-03 11:21:21 +0100597 addr = simple_strtoull(argv[2], NULL, 16);
598 size = simple_strtoull(argv[3], NULL, 16);
Kim Phillipse489b9c2008-06-10 11:06:17 -0500599 err = fdt_fixup_memory(working_fdt, addr, size);
Kumar Gala804887e2008-02-15 03:34:36 -0600600 if (err < 0)
601 return err;
602
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200603 /*
Kumar Gala804887e2008-02-15 03:34:36 -0600604 * mem reserve commands
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200605 */
Gerald Van Baren2fb698b2008-06-09 21:02:17 -0400606 } else if (strncmp(argv[1], "rs", 2) == 0) {
Kumar Gala804887e2008-02-15 03:34:36 -0600607 if (argv[2][0] == 'p') {
608 uint64_t addr, size;
Kim Phillipse489b9c2008-06-10 11:06:17 -0500609 int total = fdt_num_mem_rsv(working_fdt);
Kumar Gala804887e2008-02-15 03:34:36 -0600610 int j, err;
611 printf("index\t\t start\t\t size\n");
612 printf("-------------------------------"
613 "-----------------\n");
614 for (j = 0; j < total; j++) {
Kim Phillipse489b9c2008-06-10 11:06:17 -0500615 err = fdt_get_mem_rsv(working_fdt, j, &addr, &size);
Kumar Gala804887e2008-02-15 03:34:36 -0600616 if (err < 0) {
617 printf("libfdt fdt_get_mem_rsv(): %s\n",
618 fdt_strerror(err));
619 return err;
620 }
621 printf(" %x\t%08x%08x\t%08x%08x\n", j,
622 (u32)(addr >> 32),
623 (u32)(addr & 0xffffffff),
624 (u32)(size >> 32),
625 (u32)(size & 0xffffffff));
626 }
627 } else if (argv[2][0] == 'a') {
628 uint64_t addr, size;
629 int err;
Kumar Gala804887e2008-02-15 03:34:36 -0600630 addr = simple_strtoull(argv[3], NULL, 16);
631 size = simple_strtoull(argv[4], NULL, 16);
Kim Phillipse489b9c2008-06-10 11:06:17 -0500632 err = fdt_add_mem_rsv(working_fdt, addr, size);
Kumar Gala804887e2008-02-15 03:34:36 -0600633
634 if (err < 0) {
635 printf("libfdt fdt_add_mem_rsv(): %s\n",
636 fdt_strerror(err));
637 return err;
638 }
639 } else if (argv[2][0] == 'd') {
Simon Glass7e5f4602021-07-24 09:03:29 -0600640 unsigned long idx = hextoul(argv[3], NULL);
Kim Phillipse489b9c2008-06-10 11:06:17 -0500641 int err = fdt_del_mem_rsv(working_fdt, idx);
Kumar Gala804887e2008-02-15 03:34:36 -0600642
643 if (err < 0) {
644 printf("libfdt fdt_del_mem_rsv(): %s\n",
645 fdt_strerror(err));
646 return err;
647 }
648 } else {
649 /* Unrecognized command */
Simon Glass4c12eeb2011-12-10 08:44:01 +0000650 return CMD_RET_USAGE;
Kumar Gala804887e2008-02-15 03:34:36 -0600651 }
Kim Phillips99dffca2007-07-17 13:57:04 -0500652 }
Gerald Van Barenfd61e552007-06-25 23:25:28 -0400653#ifdef CONFIG_OF_BOARD_SETUP
Kim Phillips99dffca2007-07-17 13:57:04 -0500654 /* Call the board-specific fixup routine */
Simon Glass4ba98dc2014-10-23 18:58:48 -0600655 else if (strncmp(argv[1], "boa", 3) == 0) {
656 int err = ft_board_setup(working_fdt, gd->bd);
657
658 if (err) {
659 printf("Failed to update board information in FDT: %s\n",
660 fdt_strerror(err));
661 return CMD_RET_FAILURE;
662 }
Tom Rinif899cc12021-09-12 20:32:32 -0400663#ifdef CONFIG_ARCH_KEYSTONE
Nicholas Faustini2c76d312018-10-03 12:58:48 +0200664 ft_board_setup_ex(working_fdt, gd->bd);
665#endif
Simon Glass4ba98dc2014-10-23 18:58:48 -0600666 }
Gerald Van Barenfd61e552007-06-25 23:25:28 -0400667#endif
Kim Phillips99dffca2007-07-17 13:57:04 -0500668 /* Create a chosen node */
Heiko Schocher097dd3e2014-03-03 12:19:24 +0100669 else if (strncmp(argv[1], "cho", 3) == 0) {
Kumar Galaf953d992008-08-15 08:24:34 -0500670 unsigned long initrd_start = 0, initrd_end = 0;
671
Wolfgang Denk47e26b12010-07-17 01:06:04 +0200672 if ((argc != 2) && (argc != 4))
Simon Glass4c12eeb2011-12-10 08:44:01 +0000673 return CMD_RET_USAGE;
Kumar Galaf953d992008-08-15 08:24:34 -0500674
675 if (argc == 4) {
Simon Glass7e5f4602021-07-24 09:03:29 -0600676 initrd_start = hextoul(argv[2], NULL);
Sean Andersondbf6f7c2022-03-22 16:59:21 -0400677 initrd_end = initrd_start + hextoul(argv[3], NULL) - 1;
Kumar Galaf953d992008-08-15 08:24:34 -0500678 }
679
Masahiro Yamadabc6ed0f2014-04-18 17:41:00 +0900680 fdt_chosen(working_fdt);
Masahiro Yamadadbe963a2014-04-18 17:40:59 +0900681 fdt_initrd(working_fdt, initrd_start, initrd_end);
Heiko Schocher097dd3e2014-03-03 12:19:24 +0100682
683#if defined(CONFIG_FIT_SIGNATURE)
684 } else if (strncmp(argv[1], "che", 3) == 0) {
685 int cfg_noffset;
686 int ret;
687 unsigned long addr;
688 struct fdt_header *blob;
689
690 if (!working_fdt)
691 return CMD_RET_FAILURE;
692
693 if (argc > 2) {
Simon Glass7e5f4602021-07-24 09:03:29 -0600694 addr = hextoul(argv[2], NULL);
Heiko Schocher097dd3e2014-03-03 12:19:24 +0100695 blob = map_sysmem(addr, 0);
696 } else {
697 blob = (struct fdt_header *)gd->fdt_blob;
698 }
699 if (!fdt_valid(&blob))
700 return 1;
701
702 gd->fdt_blob = blob;
703 cfg_noffset = fit_conf_get_node(working_fdt, NULL);
704 if (!cfg_noffset) {
705 printf("Could not find configuration node: %s\n",
706 fdt_strerror(cfg_noffset));
707 return CMD_RET_FAILURE;
708 }
709
710 ret = fit_config_verify(working_fdt, cfg_noffset);
Simon Glass12df2ab2014-06-12 07:24:45 -0600711 if (ret == 0)
Heiko Schocher097dd3e2014-03-03 12:19:24 +0100712 return CMD_RET_SUCCESS;
713 else
714 return CMD_RET_FAILURE;
715#endif
716
Kumar Gala40afac22008-08-15 08:24:44 -0500717 }
Maxime Riparde6628ad2016-07-05 10:26:45 +0200718#ifdef CONFIG_OF_LIBFDT_OVERLAY
719 /* apply an overlay */
720 else if (strncmp(argv[1], "ap", 2) == 0) {
721 unsigned long addr;
722 struct fdt_header *blob;
Stefan Agner082b1412016-12-20 15:58:45 +0100723 int ret;
Maxime Riparde6628ad2016-07-05 10:26:45 +0200724
725 if (argc != 3)
726 return CMD_RET_USAGE;
727
728 if (!working_fdt)
729 return CMD_RET_FAILURE;
730
Simon Glass7e5f4602021-07-24 09:03:29 -0600731 addr = hextoul(argv[2], NULL);
Maxime Riparde6628ad2016-07-05 10:26:45 +0200732 blob = map_sysmem(addr, 0);
733 if (!fdt_valid(&blob))
734 return CMD_RET_FAILURE;
735
Pantelis Antoniou81ecc5d2017-09-04 23:12:12 +0300736 /* apply method prints messages on error */
737 ret = fdt_overlay_apply_verbose(working_fdt, blob);
738 if (ret)
Maxime Riparde6628ad2016-07-05 10:26:45 +0200739 return CMD_RET_FAILURE;
740 }
741#endif
Kumar Gala40afac22008-08-15 08:24:44 -0500742 /* resize the fdt */
743 else if (strncmp(argv[1], "re", 2) == 0) {
Hannes Schmelzeref476832016-09-20 18:10:43 +0200744 uint extrasize;
745 if (argc > 2)
Simon Glass7e5f4602021-07-24 09:03:29 -0600746 extrasize = hextoul(argv[2], NULL);
Hannes Schmelzeref476832016-09-20 18:10:43 +0200747 else
748 extrasize = 0;
749 fdt_shrink_to_minimum(working_fdt, extrasize);
Kumar Gala40afac22008-08-15 08:24:44 -0500750 }
751 else {
Kim Phillips99dffca2007-07-17 13:57:04 -0500752 /* Unrecognized command */
Simon Glass4c12eeb2011-12-10 08:44:01 +0000753 return CMD_RET_USAGE;
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400754 }
755
756 return 0;
757}
758
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400759/****************************************************************************/
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400760
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400761/*
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400762 * Parse the user's input, partially heuristic. Valid formats:
Andy Fleming4abd8442008-03-31 20:45:56 -0500763 * <0x00112233 4 05> - an array of cells. Numbers follow standard
Wolfgang Denk53677ef2008-05-20 16:00:29 +0200764 * C conventions.
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400765 * [00 11 22 .. nn] - byte stream
766 * "string" - If the the value doesn't start with "<" or "[", it is
767 * treated as a string. Note that the quotes are
768 * stripped by the parser before we get the string.
Andy Fleming4abd8442008-03-31 20:45:56 -0500769 * newval: An array of strings containing the new property as specified
Wolfgang Denk53677ef2008-05-20 16:00:29 +0200770 * on the command line
Andy Fleming4abd8442008-03-31 20:45:56 -0500771 * count: The number of strings in the array
772 * data: A bytestream to be placed in the property
773 * len: The length of the resulting bytestream
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400774 */
Wolfgang Denk54841ab2010-06-28 22:00:46 +0200775static int fdt_parse_prop(char * const *newval, int count, char *data, int *len)
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400776{
777 char *cp; /* temporary char pointer */
Andy Fleming4abd8442008-03-31 20:45:56 -0500778 char *newp; /* temporary newval char pointer */
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400779 unsigned long tmp; /* holds converted values */
Andy Fleming4abd8442008-03-31 20:45:56 -0500780 int stridx = 0;
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400781
Andy Fleming4abd8442008-03-31 20:45:56 -0500782 *len = 0;
783 newp = newval[0];
784
785 /* An array of cells */
786 if (*newp == '<') {
787 newp++;
788 while ((*newp != '>') && (stridx < count)) {
789 /*
790 * Keep searching until we find that last ">"
791 * That way users don't have to escape the spaces
792 */
793 if (*newp == '\0') {
794 newp = newval[++stridx];
795 continue;
796 }
797
798 cp = newp;
799 tmp = simple_strtoul(cp, &newp, 0);
Hannes Schmelzer9620d872017-05-30 15:05:44 +0200800 if (*cp != '?')
801 *(fdt32_t *)data = cpu_to_fdt32(tmp);
802 else
803 newp++;
804
Andy Fleming4abd8442008-03-31 20:45:56 -0500805 data += 4;
806 *len += 4;
807
808 /* If the ptr didn't advance, something went wrong */
809 if ((newp - cp) <= 0) {
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400810 printf("Sorry, I could not convert \"%s\"\n",
811 cp);
812 return 1;
813 }
Andy Fleming4abd8442008-03-31 20:45:56 -0500814
815 while (*newp == ' ')
816 newp++;
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400817 }
Andy Fleming4abd8442008-03-31 20:45:56 -0500818
819 if (*newp != '>') {
820 printf("Unexpected character '%c'\n", *newp);
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400821 return 1;
822 }
Andy Fleming4abd8442008-03-31 20:45:56 -0500823 } else if (*newp == '[') {
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400824 /*
825 * Byte stream. Convert the values.
826 */
Andy Fleming4abd8442008-03-31 20:45:56 -0500827 newp++;
Ken MacLeod6e748ea2009-09-11 15:16:18 -0500828 while ((stridx < count) && (*newp != ']')) {
829 while (*newp == ' ')
830 newp++;
831 if (*newp == '\0') {
832 newp = newval[++stridx];
833 continue;
834 }
835 if (!isxdigit(*newp))
836 break;
Simon Glass7e5f4602021-07-24 09:03:29 -0600837 tmp = hextoul(newp, &newp);
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400838 *data++ = tmp & 0xFF;
Gerald Van Barenfd61e552007-06-25 23:25:28 -0400839 *len = *len + 1;
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400840 }
Andy Fleming4abd8442008-03-31 20:45:56 -0500841 if (*newp != ']') {
Andrew Klossnerdc4b0b32008-07-07 06:41:14 -0700842 printf("Unexpected character '%c'\n", *newp);
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400843 return 1;
844 }
845 } else {
846 /*
Ken MacLeod6e748ea2009-09-11 15:16:18 -0500847 * Assume it is one or more strings. Copy it into our
848 * data area for convenience (including the
849 * terminating '\0's).
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400850 */
Andy Fleming4abd8442008-03-31 20:45:56 -0500851 while (stridx < count) {
Ken MacLeod6e748ea2009-09-11 15:16:18 -0500852 size_t length = strlen(newp) + 1;
Andy Fleming4abd8442008-03-31 20:45:56 -0500853 strcpy(data, newp);
Ken MacLeod6e748ea2009-09-11 15:16:18 -0500854 data += length;
855 *len += length;
Andy Fleming4abd8442008-03-31 20:45:56 -0500856 newp = newval[++stridx];
857 }
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400858 }
859 return 0;
860}
861
862/****************************************************************************/
863
864/*
865 * Heuristic to guess if this is a string or concatenated strings.
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400866 */
867
868static int is_printable_string(const void *data, int len)
869{
870 const char *s = data;
871
872 /* zero length is not */
873 if (len == 0)
874 return 0;
875
Joe Hershberger8805bee2012-08-17 10:34:38 +0000876 /* must terminate with zero or '\n' */
877 if (s[len - 1] != '\0' && s[len - 1] != '\n')
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400878 return 0;
879
880 /* printable or a null byte (concatenated strings) */
Joe Hershberger8805bee2012-08-17 10:34:38 +0000881 while (((*s == '\0') || isprint(*s) || isspace(*s)) && (len > 0)) {
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400882 /*
883 * If we see a null, there are three possibilities:
884 * 1) If len == 1, it is the end of the string, printable
885 * 2) Next character also a null, not printable.
886 * 3) Next character not a null, continue to check.
887 */
888 if (s[0] == '\0') {
889 if (len == 1)
890 return 1;
891 if (s[1] == '\0')
892 return 0;
893 }
894 s++;
895 len--;
896 }
897
898 /* Not the null termination, or not done yet: not printable */
899 if (*s != '\0' || (len != 0))
900 return 0;
901
902 return 1;
903}
904
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400905
906/*
907 * Print the property in the best format, a heuristic guess. Print as
908 * a string, concatenated strings, a byte, word, double word, or (if all
909 * else fails) it is printed as a stream of bytes.
910 */
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400911static void print_data(const void *data, int len)
912{
913 int j;
Heinrich Schuchardt43913f02020-06-19 19:45:55 +0200914 const char *env_max_dump;
915 ulong max_dump = ULONG_MAX;
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400916
917 /* no data, don't print */
918 if (len == 0)
919 return;
920
Heinrich Schuchardt43913f02020-06-19 19:45:55 +0200921 env_max_dump = env_get("fdt_max_dump");
922 if (env_max_dump)
Simon Glass7e5f4602021-07-24 09:03:29 -0600923 max_dump = hextoul(env_max_dump, NULL);
Heinrich Schuchardt43913f02020-06-19 19:45:55 +0200924
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400925 /*
926 * It is a string, but it may have multiple strings (embedded '\0's).
927 */
928 if (is_printable_string(data, len)) {
929 puts("\"");
930 j = 0;
931 while (j < len) {
932 if (j > 0)
933 puts("\", \"");
934 puts(data);
935 j += strlen(data) + 1;
936 data += strlen(data) + 1;
937 }
938 puts("\"");
939 return;
940 }
941
Andy Fleming4abd8442008-03-31 20:45:56 -0500942 if ((len %4) == 0) {
Heinrich Schuchardt43913f02020-06-19 19:45:55 +0200943 if (len > max_dump)
Tom Rini085b9c32012-10-29 14:53:18 +0000944 printf("* 0x%p [0x%08x]", data, len);
Joe Hershbergerf0a29d42012-08-17 10:34:36 +0000945 else {
Kim Phillips088f1b12012-10-29 13:34:31 +0000946 const __be32 *p;
Andy Fleming4abd8442008-03-31 20:45:56 -0500947
Joe Hershbergerf0a29d42012-08-17 10:34:36 +0000948 printf("<");
949 for (j = 0, p = data; j < len/4; j++)
950 printf("0x%08x%s", fdt32_to_cpu(p[j]),
951 j < (len/4 - 1) ? " " : "");
952 printf(">");
953 }
Andy Fleming4abd8442008-03-31 20:45:56 -0500954 } else { /* anything else... hexdump */
Heinrich Schuchardt43913f02020-06-19 19:45:55 +0200955 if (len > max_dump)
Tom Rini085b9c32012-10-29 14:53:18 +0000956 printf("* 0x%p [0x%08x]", data, len);
Joe Hershbergerf0a29d42012-08-17 10:34:36 +0000957 else {
958 const u8 *s;
Andy Fleming4abd8442008-03-31 20:45:56 -0500959
Joe Hershbergerf0a29d42012-08-17 10:34:36 +0000960 printf("[");
961 for (j = 0, s = data; j < len; j++)
962 printf("%02x%s", s[j], j < len - 1 ? " " : "");
963 printf("]");
964 }
Gerald Van Baren781e09e2007-03-31 12:22:10 -0400965 }
966}
967
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400968/****************************************************************************/
969
970/*
Kim Phillipse489b9c2008-06-10 11:06:17 -0500971 * Recursively print (a portion of) the working_fdt. The depth parameter
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400972 * determines how deeply nested the fdt is printed.
973 */
Kumar Galadbaf07c2007-11-21 14:07:46 -0600974static int fdt_print(const char *pathp, char *prop, int depth)
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400975{
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400976 static char tabs[MAX_LEVEL+1] =
977 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
978 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
Kumar Galadbaf07c2007-11-21 14:07:46 -0600979 const void *nodep; /* property node pointer */
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400980 int nodeoffset; /* node offset from libfdt */
981 int nextoffset; /* next node offset from libfdt */
982 uint32_t tag; /* tag */
983 int len; /* length of the property */
984 int level = 0; /* keep track of nesting level */
Gerald Van Baren91623522007-11-22 17:23:23 -0500985 const struct fdt_property *fdt_prop;
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400986
Kim Phillipse489b9c2008-06-10 11:06:17 -0500987 nodeoffset = fdt_path_offset (working_fdt, pathp);
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400988 if (nodeoffset < 0) {
989 /*
990 * Not found or something else bad happened.
991 */
Kumar Gala8d04f022007-10-24 11:04:22 -0500992 printf ("libfdt fdt_path_offset() returned %s\n",
Gerald Van Baren06e19a02007-05-21 23:27:16 -0400993 fdt_strerror(nodeoffset));
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -0400994 return 1;
995 }
996 /*
997 * The user passed in a property as well as node path.
998 * Print only the given property and then return.
999 */
1000 if (prop) {
Kim Phillipse489b9c2008-06-10 11:06:17 -05001001 nodep = fdt_getprop (working_fdt, nodeoffset, prop, &len);
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -04001002 if (len == 0) {
1003 /* no property value */
1004 printf("%s %s\n", pathp, prop);
1005 return 0;
Simon Glass9f952672017-06-07 10:28:42 -06001006 } else if (nodep && len > 0) {
Gerald Van Baren28f384b2007-11-23 19:43:20 -05001007 printf("%s = ", prop);
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -04001008 print_data (nodep, len);
1009 printf("\n");
1010 return 0;
1011 } else {
1012 printf ("libfdt fdt_getprop(): %s\n",
1013 fdt_strerror(len));
1014 return 1;
1015 }
1016 }
1017
1018 /*
1019 * The user passed in a node path and no property,
1020 * print the node and all subnodes.
1021 */
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -04001022 while(level >= 0) {
Kim Phillipse489b9c2008-06-10 11:06:17 -05001023 tag = fdt_next_tag(working_fdt, nodeoffset, &nextoffset);
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -04001024 switch(tag) {
1025 case FDT_BEGIN_NODE:
Kim Phillipse489b9c2008-06-10 11:06:17 -05001026 pathp = fdt_get_name(working_fdt, nodeoffset, NULL);
Gerald Van Baren91623522007-11-22 17:23:23 -05001027 if (level <= depth) {
1028 if (pathp == NULL)
1029 pathp = "/* NULL pointer error */";
1030 if (*pathp == '\0')
1031 pathp = "/"; /* root is nameless */
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -04001032 printf("%s%s {\n",
1033 &tabs[MAX_LEVEL - level], pathp);
Gerald Van Baren91623522007-11-22 17:23:23 -05001034 }
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -04001035 level++;
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -04001036 if (level >= MAX_LEVEL) {
Gerald Van Baren91623522007-11-22 17:23:23 -05001037 printf("Nested too deep, aborting.\n");
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -04001038 return 1;
1039 }
1040 break;
1041 case FDT_END_NODE:
1042 level--;
Gerald Van Baren91623522007-11-22 17:23:23 -05001043 if (level <= depth)
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -04001044 printf("%s};\n", &tabs[MAX_LEVEL - level]);
1045 if (level == 0) {
1046 level = -1; /* exit the loop */
1047 }
1048 break;
1049 case FDT_PROP:
Kim Phillipse489b9c2008-06-10 11:06:17 -05001050 fdt_prop = fdt_offset_ptr(working_fdt, nodeoffset,
Gerald Van Baren91623522007-11-22 17:23:23 -05001051 sizeof(*fdt_prop));
Kim Phillipse489b9c2008-06-10 11:06:17 -05001052 pathp = fdt_string(working_fdt,
Gerald Van Baren91623522007-11-22 17:23:23 -05001053 fdt32_to_cpu(fdt_prop->nameoff));
1054 len = fdt32_to_cpu(fdt_prop->len);
1055 nodep = fdt_prop->data;
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -04001056 if (len < 0) {
1057 printf ("libfdt fdt_getprop(): %s\n",
1058 fdt_strerror(len));
1059 return 1;
1060 } else if (len == 0) {
1061 /* the property has no value */
Gerald Van Baren91623522007-11-22 17:23:23 -05001062 if (level <= depth)
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -04001063 printf("%s%s;\n",
1064 &tabs[MAX_LEVEL - level],
1065 pathp);
1066 } else {
Gerald Van Baren91623522007-11-22 17:23:23 -05001067 if (level <= depth) {
Gerald Van Baren28f384b2007-11-23 19:43:20 -05001068 printf("%s%s = ",
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -04001069 &tabs[MAX_LEVEL - level],
1070 pathp);
1071 print_data (nodep, len);
1072 printf(";\n");
1073 }
1074 }
1075 break;
1076 case FDT_NOP:
Andrew Klossnerdc4b0b32008-07-07 06:41:14 -07001077 printf("%s/* NOP */\n", &tabs[MAX_LEVEL - level]);
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -04001078 break;
1079 case FDT_END:
1080 return 1;
1081 default:
Gerald Van Baren91623522007-11-22 17:23:23 -05001082 if (level <= depth)
Gerald Van Barenaddd8ce2007-05-16 22:39:59 -04001083 printf("Unknown tag 0x%08X\n", tag);
1084 return 1;
1085 }
1086 nodeoffset = nextoffset;
1087 }
1088 return 0;
1089}
1090
Gerald Van Baren781e09e2007-03-31 12:22:10 -04001091/********************************************************************/
Kim Phillips088f1b12012-10-29 13:34:31 +00001092#ifdef CONFIG_SYS_LONGHELP
1093static char fdt_help_text[] =
Heinrich Schuchardte4269632022-04-25 18:35:05 +02001094 "addr [-c] [-q] <addr> [<size>] - Set the [control] fdt location to <addr>\n"
Maxime Riparde6628ad2016-07-05 10:26:45 +02001095#ifdef CONFIG_OF_LIBFDT_OVERLAY
1096 "fdt apply <addr> - Apply overlay to the DT\n"
1097#endif
Gerald Van Barenfd61e552007-06-25 23:25:28 -04001098#ifdef CONFIG_OF_BOARD_SETUP
1099 "fdt boardsetup - Do board-specific set up\n"
1100#endif
Simon Glassc654b512014-10-23 18:58:54 -06001101#ifdef CONFIG_OF_SYSTEM_SETUP
1102 "fdt systemsetup - Do system-specific set up\n"
1103#endif
Gerald Van Baren238cb7a2008-01-05 15:33:29 -05001104 "fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n"
Hannes Schmelzeref476832016-09-20 18:10:43 +02001105 "fdt resize [<extrasize>] - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed\n"
Gerald Van Baren781e09e2007-03-31 12:22:10 -04001106 "fdt print <path> [<prop>] - Recursive print starting at <path>\n"
1107 "fdt list <path> [<prop>] - Print one level starting at <path>\n"
Marek Vasut13982ce2022-07-08 23:50:43 +02001108 "fdt get value <var> <path> <prop> [<index>] - Get <property> and store in <var>\n"
1109 " In case of stringlist property, use optional <index>\n"
1110 " to select string within the stringlist. Default is 0.\n"
Joe Hershbergerbc802952012-08-17 10:34:37 +00001111 "fdt get name <var> <path> <index> - Get name of node <index> and store in <var>\n"
1112 "fdt get addr <var> <path> <prop> - Get start address of <property> and store in <var>\n"
1113 "fdt get size <var> <path> [<prop>] - Get size of [<property>] or num nodes and store in <var>\n"
Gerald Van Baren781e09e2007-03-31 12:22:10 -04001114 "fdt set <path> <prop> [<val>] - Set <property> [to <val>]\n"
1115 "fdt mknode <path> <node> - Create a new node after <path>\n"
1116 "fdt rm <path> [<prop>] - Delete the node or <property>\n"
Heiko Schocher82441272018-11-15 06:06:06 +01001117 "fdt header [get <var> <member>] - Display header info\n"
1118 " get - get header member <member> and store it in <var>\n"
Kumar Gala804887e2008-02-15 03:34:36 -06001119 "fdt bootcpu <id> - Set boot cpuid\n"
1120 "fdt memory <addr> <size> - Add/Update memory node\n"
1121 "fdt rsvmem print - Show current mem reserves\n"
1122 "fdt rsvmem add <addr> <size> - Add a mem reserve\n"
1123 "fdt rsvmem delete <index> - Delete a mem reserves\n"
Sean Andersondbf6f7c2022-03-22 16:59:21 -04001124 "fdt chosen [<start> <size>] - Add/update the /chosen branch in the tree\n"
1125 " <start>/<size> - initrd start addr/size\n"
Heiko Schocher097dd3e2014-03-03 12:19:24 +01001126#if defined(CONFIG_FIT_SIGNATURE)
1127 "fdt checksign [<addr>] - check FIT signature\n"
1128 " <start> - addr of key blob\n"
1129 " default gd->fdt_blob\n"
1130#endif
Robert P. J. Day1cc0a9f2016-05-04 04:47:31 -04001131 "NOTE: Dereference aliases by omitting the leading '/', "
Kim Phillips088f1b12012-10-29 13:34:31 +00001132 "e.g. fdt print ethernet0.";
1133#endif
1134
1135U_BOOT_CMD(
1136 fdt, 255, 0, do_fdt,
1137 "flattened device tree utility commands", fdt_help_text
Gerald Van Baren781e09e2007-03-31 12:22:10 -04001138);