Blame view
common/cmd_fdt.c
24.5 KB
781e09ee6
|
1 2 3 4 5 6 7 |
/* * (C) Copyright 2007 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com * Based on code written by: * Pantelis Antoniou <pantelis.antoniou@gmail.com> and * Matthew McClintock <msm@freescale.com> * |
1a4596601
|
8 |
* SPDX-License-Identifier: GPL-2.0+ |
781e09ee6
|
9 10 11 12 13 14 |
*/ #include <common.h> #include <command.h> #include <linux/ctype.h> #include <linux/types.h> |
781e09ee6
|
15 |
#include <asm/global_data.h> |
781e09ee6
|
16 |
#include <libfdt.h> |
64dbbd40c
|
17 |
#include <fdt_support.h> |
a92fd6577
|
18 |
#include <asm/io.h> |
781e09ee6
|
19 20 |
#define MAX_LEVEL 32 /* how deeply nested we will go */ |
fd61e55dd
|
21 |
#define SCRATCHPAD 1024 /* bytes of scratchpad memory */ |
f0a29d433
|
22 23 24 |
#ifndef CONFIG_CMD_FDT_MAX_DUMP #define CONFIG_CMD_FDT_MAX_DUMP 64 #endif |
781e09ee6
|
25 26 27 28 29 |
/* * Global data (for the gd->bd) */ DECLARE_GLOBAL_DATA_PTR; |
d14da9130
|
30 |
static int fdt_valid(struct fdt_header **blobp); |
54841ab50
|
31 |
static int fdt_parse_prop(char *const*newval, int count, char *data, int *len); |
dbaf07ce6
|
32 |
static int fdt_print(const char *pathp, char *prop, int depth); |
bc80295b6
|
33 |
static int is_printable_string(const void *data, int len); |
781e09ee6
|
34 |
|
781e09ee6
|
35 |
/* |
ae9e97fa9
|
36 37 38 |
* The working_fdt points to our working flattened device tree. */ struct fdt_header *working_fdt; |
54f9c8669
|
39 40 |
void set_working_fdt_addr(void *addr) { |
a92fd6577
|
41 42 43 44 |
void *buf; buf = map_sysmem((ulong)addr, 0); working_fdt = buf; |
bfc599664
|
45 |
setenv_addr("fdtaddr", addr); |
54f9c8669
|
46 |
} |
ae9e97fa9
|
47 |
/* |
bc80295b6
|
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
* Get a value from the fdt and format it to be set in the environment */ static int fdt_value_setenv(const void *nodep, int len, const char *var) { if (is_printable_string(nodep, len)) setenv(var, (void *)nodep); else if (len == 4) { char buf[11]; sprintf(buf, "0x%08X", *(uint32_t *)nodep); setenv(var, buf); } else if (len%4 == 0 && len <= 20) { /* Needed to print things like sha1 hashes. */ char buf[41]; int i; for (i = 0; i < len; i += sizeof(unsigned int)) sprintf(buf + (i * 2), "%08x", *(unsigned int *)(nodep + i)); setenv(var, buf); } else { printf("error: unprintable value "); return 1; } return 0; } /* |
781e09ee6
|
77 78 |
* Flattened Device Tree command, see the help for parameter definitions. */ |
088f1b199
|
79 |
static int do_fdt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
781e09ee6
|
80 |
{ |
47e26b1bf
|
81 |
if (argc < 2) |
4c12eeb8b
|
82 |
return CMD_RET_USAGE; |
781e09ee6
|
83 |
|
47e26b1bf
|
84 |
/* |
781e09ee6
|
85 |
* Set the address of the fdt |
47e26b1bf
|
86 |
*/ |
25114033a
|
87 |
if (argv[1][0] == 'a') { |
54f9c8669
|
88 |
unsigned long addr; |
4b5786550
|
89 90 |
int control = 0; struct fdt_header *blob; |
781e09ee6
|
91 92 93 |
/* * Set the address [and length] of the fdt. */ |
4b5786550
|
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
argc -= 2; argv += 2; /* Temporary #ifdef - some archs don't have fdt_blob yet */ #ifdef CONFIG_OF_CONTROL if (argc && !strcmp(*argv, "-c")) { control = 1; argc--; argv++; } #endif if (argc == 0) { if (control) blob = (struct fdt_header *)gd->fdt_blob; else blob = working_fdt; if (!blob || !fdt_valid(&blob)) |
7dbc38ad9
|
110 |
return 1; |
4b5786550
|
111 112 113 114 |
printf("The address of the fdt is %#08lx ", control ? (ulong)blob : getenv_hex("fdtaddr", 0)); |
7dbc38ad9
|
115 116 |
return 0; } |
4b5786550
|
117 |
addr = simple_strtoul(argv[0], NULL, 16); |
a92fd6577
|
118 |
blob = map_sysmem(addr, 0); |
4b5786550
|
119 |
if (!fdt_valid(&blob)) |
781e09ee6
|
120 |
return 1; |
4b5786550
|
121 122 123 |
if (control) gd->fdt_blob = blob; else |
a92fd6577
|
124 |
set_working_fdt_addr(blob); |
781e09ee6
|
125 |
|
4b5786550
|
126 |
if (argc >= 2) { |
781e09ee6
|
127 128 129 130 131 |
int len; int err; /* * Optional new length */ |
4b5786550
|
132 133 |
len = simple_strtoul(argv[1], NULL, 16); if (len < fdt_totalsize(blob)) { |
addd8ce83
|
134 135 136 |
printf ("New length %d < existing length %d, " "ignoring. ", |
4b5786550
|
137 |
len, fdt_totalsize(blob)); |
781e09ee6
|
138 139 140 141 |
} else { /* * Open in place with a new length. */ |
4b5786550
|
142 |
err = fdt_open_into(blob, blob, len); |
781e09ee6
|
143 |
if (err != 0) { |
addd8ce83
|
144 145 146 |
printf ("libfdt fdt_open_into(): %s ", fdt_strerror(err)); |
781e09ee6
|
147 148 149 |
} } } |
e02c94587
|
150 151 152 153 154 155 156 157 158 159 160 161 162 |
return CMD_RET_SUCCESS; } if (!working_fdt) { puts( "No FDT memory address configured. Please configure " "the FDT address via \"fdt addr <address>\" command. " "Aborting! "); return CMD_RET_FAILURE; } |
47e26b1bf
|
163 |
/* |
e489b9c07
|
164 |
* Move the working_fdt |
47e26b1bf
|
165 |
*/ |
e02c94587
|
166 |
if (strncmp(argv[1], "mo", 2) == 0) { |
781e09ee6
|
167 168 169 |
struct fdt_header *newaddr; int len; int err; |
47e26b1bf
|
170 |
if (argc < 4) |
4c12eeb8b
|
171 |
return CMD_RET_USAGE; |
781e09ee6
|
172 173 174 175 |
/* * Set the address and length of the fdt. */ |
e489b9c07
|
176 |
working_fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16); |
d14da9130
|
177 |
if (!fdt_valid(&working_fdt)) |
781e09ee6
|
178 |
return 1; |
781e09ee6
|
179 |
|
addd8ce83
|
180 |
newaddr = (struct fdt_header *)simple_strtoul(argv[3],NULL,16); |
6be07cc1c
|
181 182 183 184 185 186 |
/* * If the user specifies a length, use that. Otherwise use the * current length. */ if (argc <= 4) { |
e489b9c07
|
187 |
len = fdt_totalsize(working_fdt); |
6be07cc1c
|
188 189 |
} else { len = simple_strtoul(argv[4], NULL, 16); |
e489b9c07
|
190 |
if (len < fdt_totalsize(working_fdt)) { |
addd8ce83
|
191 192 193 |
printf ("New length 0x%X < existing length " "0x%X, aborting. ", |
e489b9c07
|
194 |
len, fdt_totalsize(working_fdt)); |
6be07cc1c
|
195 196 |
return 1; } |
781e09ee6
|
197 198 199 200 201 |
} /* * Copy to the new location. */ |
e489b9c07
|
202 |
err = fdt_open_into(working_fdt, newaddr, len); |
781e09ee6
|
203 |
if (err != 0) { |
addd8ce83
|
204 205 206 |
printf ("libfdt fdt_open_into(): %s ", fdt_strerror(err)); |
781e09ee6
|
207 208 |
return 1; } |
e489b9c07
|
209 |
working_fdt = newaddr; |
781e09ee6
|
210 |
|
47e26b1bf
|
211 |
/* |
25114033a
|
212 |
* Make a new node |
47e26b1bf
|
213 |
*/ |
2fb698bf5
|
214 |
} else if (strncmp(argv[1], "mk", 2) == 0) { |
25114033a
|
215 216 217 218 219 220 221 222 |
char *pathp; /* path */ char *nodep; /* new node to add */ int nodeoffset; /* node offset from libfdt */ int err; /* * Parameters: Node path, new node to be appended to the path. */ |
47e26b1bf
|
223 |
if (argc < 4) |
4c12eeb8b
|
224 |
return CMD_RET_USAGE; |
25114033a
|
225 226 227 |
pathp = argv[2]; nodep = argv[3]; |
e489b9c07
|
228 |
nodeoffset = fdt_path_offset (working_fdt, pathp); |
25114033a
|
229 230 231 232 |
if (nodeoffset < 0) { /* * Not found or something else bad happened. */ |
8d04f02f6
|
233 234 |
printf ("libfdt fdt_path_offset() returned %s ", |
06e19a077
|
235 |
fdt_strerror(nodeoffset)); |
25114033a
|
236 237 |
return 1; } |
e489b9c07
|
238 |
err = fdt_add_subnode(working_fdt, nodeoffset, nodep); |
25114033a
|
239 |
if (err < 0) { |
addd8ce83
|
240 241 242 |
printf ("libfdt fdt_add_subnode(): %s ", fdt_strerror(err)); |
25114033a
|
243 244 |
return 1; } |
47e26b1bf
|
245 |
/* |
e489b9c07
|
246 |
* Set the value of a property in the working_fdt. |
47e26b1bf
|
247 |
*/ |
25114033a
|
248 |
} else if (argv[1][0] == 's') { |
781e09ee6
|
249 |
char *pathp; /* path */ |
addd8ce83
|
250 |
char *prop; /* property */ |
781e09ee6
|
251 |
int nodeoffset; /* node offset from libfdt */ |
addd8ce83
|
252 253 254 |
static char data[SCRATCHPAD]; /* storage for the property */ int len; /* new length of the property */ int ret; /* return value */ |
781e09ee6
|
255 256 |
/* |
ea6d8be15
|
257 |
* Parameters: Node path, property, optional value. |
781e09ee6
|
258 |
*/ |
47e26b1bf
|
259 |
if (argc < 4) |
4c12eeb8b
|
260 |
return CMD_RET_USAGE; |
781e09ee6
|
261 262 263 |
pathp = argv[2]; prop = argv[3]; |
ea6d8be15
|
264 265 266 |
if (argc == 4) { len = 0; } else { |
4abd844d8
|
267 |
ret = fdt_parse_prop(&argv[4], argc - 4, data, &len); |
ea6d8be15
|
268 269 270 |
if (ret != 0) return ret; } |
781e09ee6
|
271 |
|
e489b9c07
|
272 |
nodeoffset = fdt_path_offset (working_fdt, pathp); |
25114033a
|
273 |
if (nodeoffset < 0) { |
781e09ee6
|
274 |
/* |
25114033a
|
275 |
* Not found or something else bad happened. |
781e09ee6
|
276 |
*/ |
8d04f02f6
|
277 278 |
printf ("libfdt fdt_path_offset() returned %s ", |
06e19a077
|
279 |
fdt_strerror(nodeoffset)); |
781e09ee6
|
280 |
return 1; |
25114033a
|
281 |
} |
25114033a
|
282 |
|
e489b9c07
|
283 |
ret = fdt_setprop(working_fdt, nodeoffset, prop, data, len); |
25114033a
|
284 285 286 287 |
if (ret < 0) { printf ("libfdt fdt_setprop(): %s ", fdt_strerror(ret)); return 1; |
781e09ee6
|
288 |
} |
bc80295b6
|
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
/******************************************************************** * Get the value of a property in the working_fdt. ********************************************************************/ } else if (argv[1][0] == 'g') { char *subcmd; /* sub-command */ char *pathp; /* path */ char *prop; /* property */ char *var; /* variable to store result */ int nodeoffset; /* node offset from libfdt */ const void *nodep; /* property node pointer */ int len = 0; /* new length of the property */ /* * Parameters: Node path, property, optional value. */ if (argc < 5) return CMD_RET_USAGE; subcmd = argv[2]; if (argc < 6 && subcmd[0] != 's') return CMD_RET_USAGE; var = argv[3]; pathp = argv[4]; prop = argv[5]; nodeoffset = fdt_path_offset(working_fdt, pathp); if (nodeoffset < 0) { /* * Not found or something else bad happened. */ printf("libfdt fdt_path_offset() returned %s ", fdt_strerror(nodeoffset)); return 1; } if (subcmd[0] == 'n' || (subcmd[0] == 's' && argc == 5)) { int reqIndex = -1; int startDepth = fdt_node_depth( working_fdt, nodeoffset); int curDepth = startDepth; int curIndex = -1; int nextNodeOffset = fdt_next_node( working_fdt, nodeoffset, &curDepth); if (subcmd[0] == 'n') reqIndex = simple_strtoul(argv[5], NULL, 16); while (curDepth > startDepth) { if (curDepth == startDepth + 1) curIndex++; if (subcmd[0] == 'n' && curIndex == reqIndex) { const char *nodeName = fdt_get_name( working_fdt, nextNodeOffset, NULL); setenv(var, (char *)nodeName); return 0; } nextNodeOffset = fdt_next_node( working_fdt, nextNodeOffset, &curDepth); if (nextNodeOffset < 0) break; } if (subcmd[0] == 's') { /* get the num nodes at this level */ |
bfc599664
|
356 |
setenv_ulong(var, curIndex + 1); |
bc80295b6
|
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 |
} else { /* node index not found */ printf("libfdt node not found "); return 1; } } else { nodep = fdt_getprop( working_fdt, nodeoffset, prop, &len); if (len == 0) { /* no property value */ setenv(var, ""); return 0; } else if (len > 0) { if (subcmd[0] == 'v') { int ret; ret = fdt_value_setenv(nodep, len, var); if (ret != 0) return ret; } else if (subcmd[0] == 'a') { /* Get address */ char buf[11]; |
085b9c3a1
|
380 |
sprintf(buf, "0x%p", nodep); |
bc80295b6
|
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
setenv(var, buf); } else if (subcmd[0] == 's') { /* Get size */ char buf[11]; sprintf(buf, "0x%08X", len); setenv(var, buf); } else return CMD_RET_USAGE; return 0; } else { printf("libfdt fdt_getprop(): %s ", fdt_strerror(len)); return 1; } } |
47e26b1bf
|
398 |
/* |
781e09ee6
|
399 |
* Print (recursive) / List (single level) |
47e26b1bf
|
400 |
*/ |
25114033a
|
401 |
} else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) { |
781e09ee6
|
402 403 |
int depth = MAX_LEVEL; /* how deep to print */ char *pathp; /* path */ |
addd8ce83
|
404 405 |
char *prop; /* property */ int ret; /* return value */ |
f738b4a75
|
406 |
static char root[2] = "/"; |
781e09ee6
|
407 408 409 410 |
/* * list is an alias for print, but limited to 1 level */ |
25114033a
|
411 |
if (argv[1][0] == 'l') { |
781e09ee6
|
412 413 414 415 416 417 418 |
depth = 1; } /* * Get the starting path. The root node is an oddball, * the offset is zero and has no name. */ |
f738b4a75
|
419 420 421 422 |
if (argc == 2) pathp = root; else pathp = argv[2]; |
781e09ee6
|
423 424 425 426 |
if (argc > 3) prop = argv[3]; else prop = NULL; |
addd8ce83
|
427 428 429 |
ret = fdt_print(pathp, prop, depth); if (ret != 0) return ret; |
781e09ee6
|
430 |
|
47e26b1bf
|
431 |
/* |
781e09ee6
|
432 |
* Remove a property/node |
47e26b1bf
|
433 |
*/ |
2fb698bf5
|
434 |
} else if (strncmp(argv[1], "rm", 2) == 0) { |
781e09ee6
|
435 436 437 438 439 440 441 |
int nodeoffset; /* node offset from libfdt */ int err; /* * Get the path. The root node is an oddball, the offset * is zero and has no name. */ |
e489b9c07
|
442 |
nodeoffset = fdt_path_offset (working_fdt, argv[2]); |
25114033a
|
443 444 445 446 |
if (nodeoffset < 0) { /* * Not found or something else bad happened. */ |
8d04f02f6
|
447 448 |
printf ("libfdt fdt_path_offset() returned %s ", |
06e19a077
|
449 |
fdt_strerror(nodeoffset)); |
25114033a
|
450 |
return 1; |
781e09ee6
|
451 452 453 454 455 456 |
} /* * Do the delete. A fourth parameter means delete a property, * otherwise delete the node. */ if (argc > 3) { |
e489b9c07
|
457 |
err = fdt_delprop(working_fdt, nodeoffset, argv[3]); |
781e09ee6
|
458 |
if (err < 0) { |
addd8ce83
|
459 460 461 |
printf("libfdt fdt_delprop(): %s ", fdt_strerror(err)); |
781e09ee6
|
462 463 464 |
return err; } } else { |
e489b9c07
|
465 |
err = fdt_del_node(working_fdt, nodeoffset); |
781e09ee6
|
466 |
if (err < 0) { |
addd8ce83
|
467 468 469 |
printf("libfdt fdt_del_node(): %s ", fdt_strerror(err)); |
781e09ee6
|
470 471 472 |
return err; } } |
804887e60
|
473 |
|
47e26b1bf
|
474 |
/* |
804887e60
|
475 |
* Display header info |
47e26b1bf
|
476 |
*/ |
804887e60
|
477 |
} else if (argv[1][0] == 'h') { |
e489b9c07
|
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 |
u32 version = fdt_version(working_fdt); printf("magic:\t\t\t0x%x ", fdt_magic(working_fdt)); printf("totalsize:\t\t0x%x (%d) ", fdt_totalsize(working_fdt), fdt_totalsize(working_fdt)); printf("off_dt_struct:\t\t0x%x ", fdt_off_dt_struct(working_fdt)); printf("off_dt_strings:\t\t0x%x ", fdt_off_dt_strings(working_fdt)); printf("off_mem_rsvmap:\t\t0x%x ", fdt_off_mem_rsvmap(working_fdt)); |
804887e60
|
493 494 |
printf("version:\t\t%d ", version); |
e489b9c07
|
495 496 497 |
printf("last_comp_version:\t%d ", fdt_last_comp_version(working_fdt)); |
804887e60
|
498 499 500 |
if (version >= 2) printf("boot_cpuid_phys:\t0x%x ", |
e489b9c07
|
501 |
fdt_boot_cpuid_phys(working_fdt)); |
804887e60
|
502 503 504 |
if (version >= 3) printf("size_dt_strings:\t0x%x ", |
e489b9c07
|
505 |
fdt_size_dt_strings(working_fdt)); |
804887e60
|
506 507 508 |
if (version >= 17) printf("size_dt_struct:\t\t0x%x ", |
e489b9c07
|
509 510 511 512 |
fdt_size_dt_struct(working_fdt)); printf("number mem_rsv:\t\t0x%x ", fdt_num_mem_rsv(working_fdt)); |
804887e60
|
513 514 |
printf(" "); |
47e26b1bf
|
515 |
/* |
804887e60
|
516 |
* Set boot cpu id |
47e26b1bf
|
517 |
*/ |
2fb698bf5
|
518 |
} else if (strncmp(argv[1], "boo", 3) == 0) { |
804887e60
|
519 |
unsigned long tmp = simple_strtoul(argv[2], NULL, 16); |
e489b9c07
|
520 |
fdt_set_boot_cpuid_phys(working_fdt, tmp); |
804887e60
|
521 |
|
47e26b1bf
|
522 |
/* |
804887e60
|
523 |
* memory command |
47e26b1bf
|
524 |
*/ |
2fb698bf5
|
525 |
} else if (strncmp(argv[1], "me", 2) == 0) { |
804887e60
|
526 527 |
uint64_t addr, size; int err; |
4b142febf
|
528 529 |
addr = simple_strtoull(argv[2], NULL, 16); size = simple_strtoull(argv[3], NULL, 16); |
e489b9c07
|
530 |
err = fdt_fixup_memory(working_fdt, addr, size); |
804887e60
|
531 532 |
if (err < 0) return err; |
47e26b1bf
|
533 |
/* |
804887e60
|
534 |
* mem reserve commands |
47e26b1bf
|
535 |
*/ |
2fb698bf5
|
536 |
} else if (strncmp(argv[1], "rs", 2) == 0) { |
804887e60
|
537 538 |
if (argv[2][0] == 'p') { uint64_t addr, size; |
e489b9c07
|
539 |
int total = fdt_num_mem_rsv(working_fdt); |
804887e60
|
540 541 542 543 544 545 546 |
int j, err; printf("index\t\t start\t\t size "); printf("-------------------------------" "----------------- "); for (j = 0; j < total; j++) { |
e489b9c07
|
547 |
err = fdt_get_mem_rsv(working_fdt, j, &addr, &size); |
804887e60
|
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 |
if (err < 0) { printf("libfdt fdt_get_mem_rsv(): %s ", fdt_strerror(err)); return err; } printf(" %x\t%08x%08x\t%08x%08x ", j, (u32)(addr >> 32), (u32)(addr & 0xffffffff), (u32)(size >> 32), (u32)(size & 0xffffffff)); } } else if (argv[2][0] == 'a') { uint64_t addr, size; int err; |
804887e60
|
564 565 |
addr = simple_strtoull(argv[3], NULL, 16); size = simple_strtoull(argv[4], NULL, 16); |
e489b9c07
|
566 |
err = fdt_add_mem_rsv(working_fdt, addr, size); |
804887e60
|
567 568 569 570 571 572 573 574 575 |
if (err < 0) { printf("libfdt fdt_add_mem_rsv(): %s ", fdt_strerror(err)); return err; } } else if (argv[2][0] == 'd') { unsigned long idx = simple_strtoul(argv[3], NULL, 16); |
e489b9c07
|
576 |
int err = fdt_del_mem_rsv(working_fdt, idx); |
804887e60
|
577 578 579 580 581 582 583 584 585 |
if (err < 0) { printf("libfdt fdt_del_mem_rsv(): %s ", fdt_strerror(err)); return err; } } else { /* Unrecognized command */ |
4c12eeb8b
|
586 |
return CMD_RET_USAGE; |
804887e60
|
587 |
} |
99dffca3b
|
588 |
} |
fd61e55dd
|
589 |
#ifdef CONFIG_OF_BOARD_SETUP |
99dffca3b
|
590 |
/* Call the board-specific fixup routine */ |
2fb698bf5
|
591 |
else if (strncmp(argv[1], "boa", 3) == 0) |
e489b9c07
|
592 |
ft_board_setup(working_fdt, gd->bd); |
fd61e55dd
|
593 |
#endif |
99dffca3b
|
594 |
/* Create a chosen node */ |
f953d99fd
|
595 596 |
else if (argv[1][0] == 'c') { unsigned long initrd_start = 0, initrd_end = 0; |
47e26b1bf
|
597 |
if ((argc != 2) && (argc != 4)) |
4c12eeb8b
|
598 |
return CMD_RET_USAGE; |
f953d99fd
|
599 600 601 602 603 |
if (argc == 4) { initrd_start = simple_strtoul(argv[2], NULL, 16); initrd_end = simple_strtoul(argv[3], NULL, 16); } |
56844a22b
|
604 605 |
fdt_chosen(working_fdt, 1); fdt_initrd(working_fdt, initrd_start, initrd_end, 1); |
40afac22a
|
606 607 608 609 610 611 |
} /* resize the fdt */ else if (strncmp(argv[1], "re", 2) == 0) { fdt_resize(working_fdt); } else { |
99dffca3b
|
612 |
/* Unrecognized command */ |
4c12eeb8b
|
613 |
return CMD_RET_USAGE; |
781e09ee6
|
614 615 616 617 |
} return 0; } |
addd8ce83
|
618 |
/****************************************************************************/ |
781e09ee6
|
619 |
|
d14da9130
|
620 621 622 623 624 625 626 |
/** * fdt_valid() - Check if an FDT is valid. If not, change it to NULL * * @blobp: Pointer to FDT pointer * @return 1 if OK, 0 if bad (in which case *blobp is set to NULL) */ static int fdt_valid(struct fdt_header **blobp) |
781e09ee6
|
627 |
{ |
d14da9130
|
628 629 |
const void *blob = *blobp; int err; |
64dbbd40c
|
630 |
|
d14da9130
|
631 |
if (blob == NULL) { |
64dbbd40c
|
632 633 |
printf ("The address of the fdt is invalid (NULL). "); |
781e09ee6
|
634 635 |
return 0; } |
64dbbd40c
|
636 |
|
d14da9130
|
637 |
err = fdt_check_header(blob); |
64dbbd40c
|
638 639 640 641 |
if (err == 0) return 1; /* valid */ if (err < 0) { |
25114033a
|
642 |
printf("libfdt fdt_check_header(): %s", fdt_strerror(err)); |
64dbbd40c
|
643 644 645 646 |
/* * Be more informative on bad version. */ if (err == -FDT_ERR_BADVERSION) { |
d14da9130
|
647 |
if (fdt_version(blob) < |
e489b9c07
|
648 |
FDT_FIRST_SUPPORTED_VERSION) { |
dc4b0b38d
|
649 |
printf (" - too old, fdt %d < %d", |
d14da9130
|
650 |
fdt_version(blob), |
addd8ce83
|
651 |
FDT_FIRST_SUPPORTED_VERSION); |
64dbbd40c
|
652 |
} |
d14da9130
|
653 |
if (fdt_last_comp_version(blob) > |
e489b9c07
|
654 |
FDT_LAST_SUPPORTED_VERSION) { |
dc4b0b38d
|
655 |
printf (" - too new, fdt %d > %d", |
d14da9130
|
656 |
fdt_version(blob), |
addd8ce83
|
657 |
FDT_LAST_SUPPORTED_VERSION); |
64dbbd40c
|
658 |
} |
64dbbd40c
|
659 660 661 |
} printf(" "); |
d14da9130
|
662 |
*blobp = NULL; |
781e09ee6
|
663 664 665 666 |
return 0; } return 1; } |
addd8ce83
|
667 |
/****************************************************************************/ |
781e09ee6
|
668 669 |
/* |
addd8ce83
|
670 |
* Parse the user's input, partially heuristic. Valid formats: |
4abd844d8
|
671 |
* <0x00112233 4 05> - an array of cells. Numbers follow standard |
53677ef18
|
672 |
* C conventions. |
addd8ce83
|
673 674 675 676 |
* [00 11 22 .. nn] - byte stream * "string" - If the the value doesn't start with "<" or "[", it is * treated as a string. Note that the quotes are * stripped by the parser before we get the string. |
4abd844d8
|
677 |
* newval: An array of strings containing the new property as specified |
53677ef18
|
678 |
* on the command line |
4abd844d8
|
679 680 681 |
* count: The number of strings in the array * data: A bytestream to be placed in the property * len: The length of the resulting bytestream |
addd8ce83
|
682 |
*/ |
54841ab50
|
683 |
static int fdt_parse_prop(char * const *newval, int count, char *data, int *len) |
addd8ce83
|
684 685 |
{ char *cp; /* temporary char pointer */ |
4abd844d8
|
686 |
char *newp; /* temporary newval char pointer */ |
addd8ce83
|
687 |
unsigned long tmp; /* holds converted values */ |
4abd844d8
|
688 |
int stridx = 0; |
addd8ce83
|
689 |
|
4abd844d8
|
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 |
*len = 0; newp = newval[0]; /* An array of cells */ if (*newp == '<') { newp++; while ((*newp != '>') && (stridx < count)) { /* * Keep searching until we find that last ">" * That way users don't have to escape the spaces */ if (*newp == '\0') { newp = newval[++stridx]; continue; } cp = newp; tmp = simple_strtoul(cp, &newp, 0); |
088f1b199
|
708 |
*(__be32 *)data = __cpu_to_be32(tmp); |
4abd844d8
|
709 710 711 712 713 |
data += 4; *len += 4; /* If the ptr didn't advance, something went wrong */ if ((newp - cp) <= 0) { |
addd8ce83
|
714 715 716 717 718 |
printf("Sorry, I could not convert \"%s\" ", cp); return 1; } |
4abd844d8
|
719 720 721 |
while (*newp == ' ') newp++; |
addd8ce83
|
722 |
} |
4abd844d8
|
723 724 725 726 |
if (*newp != '>') { printf("Unexpected character '%c' ", *newp); |
addd8ce83
|
727 728 |
return 1; } |
4abd844d8
|
729 |
} else if (*newp == '[') { |
addd8ce83
|
730 731 732 |
/* * Byte stream. Convert the values. */ |
4abd844d8
|
733 |
newp++; |
6e748ea00
|
734 |
while ((stridx < count) && (*newp != ']')) { |
4abd844d8
|
735 736 |
while (*newp == ' ') newp++; |
6e748ea00
|
737 |
if (*newp == '\0') { |
4abd844d8
|
738 |
newp = newval[++stridx]; |
6e748ea00
|
739 740 741 742 743 744 745 |
continue; } if (!isxdigit(*newp)) break; tmp = simple_strtoul(newp, &newp, 16); *data++ = tmp & 0xFF; *len = *len + 1; |
addd8ce83
|
746 |
} |
4abd844d8
|
747 |
if (*newp != ']') { |
dc4b0b38d
|
748 749 |
printf("Unexpected character '%c' ", *newp); |
addd8ce83
|
750 751 752 753 |
return 1; } } else { /* |
6e748ea00
|
754 755 756 |
* Assume it is one or more strings. Copy it into our * data area for convenience (including the * terminating '\0's). |
addd8ce83
|
757 |
*/ |
4abd844d8
|
758 |
while (stridx < count) { |
6e748ea00
|
759 |
size_t length = strlen(newp) + 1; |
4abd844d8
|
760 |
strcpy(data, newp); |
6e748ea00
|
761 762 |
data += length; *len += length; |
4abd844d8
|
763 764 |
newp = newval[++stridx]; } |
addd8ce83
|
765 766 767 768 769 770 771 772 |
} return 0; } /****************************************************************************/ /* * Heuristic to guess if this is a string or concatenated strings. |
781e09ee6
|
773 774 775 776 777 778 779 780 781 |
*/ static int is_printable_string(const void *data, int len) { const char *s = data; /* zero length is not */ if (len == 0) return 0; |
8805beec8
|
782 783 784 785 |
/* must terminate with zero or ' ' */ if (s[len - 1] != '\0' && s[len - 1] != ' ') |
781e09ee6
|
786 787 788 |
return 0; /* printable or a null byte (concatenated strings) */ |
8805beec8
|
789 |
while (((*s == '\0') || isprint(*s) || isspace(*s)) && (len > 0)) { |
781e09ee6
|
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 |
/* * If we see a null, there are three possibilities: * 1) If len == 1, it is the end of the string, printable * 2) Next character also a null, not printable. * 3) Next character not a null, continue to check. */ if (s[0] == '\0') { if (len == 1) return 1; if (s[1] == '\0') return 0; } s++; len--; } /* Not the null termination, or not done yet: not printable */ if (*s != '\0' || (len != 0)) return 0; return 1; } |
addd8ce83
|
812 813 814 815 816 817 |
/* * Print the property in the best format, a heuristic guess. Print as * a string, concatenated strings, a byte, word, double word, or (if all * else fails) it is printed as a stream of bytes. */ |
781e09ee6
|
818 819 820 |
static void print_data(const void *data, int len) { int j; |
781e09ee6
|
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 |
/* no data, don't print */ if (len == 0) return; /* * It is a string, but it may have multiple strings (embedded '\0's). */ if (is_printable_string(data, len)) { puts("\""); j = 0; while (j < len) { if (j > 0) puts("\", \""); puts(data); j += strlen(data) + 1; data += strlen(data) + 1; } puts("\""); return; } |
4abd844d8
|
842 |
if ((len %4) == 0) { |
f0a29d433
|
843 |
if (len > CONFIG_CMD_FDT_MAX_DUMP) |
085b9c3a1
|
844 |
printf("* 0x%p [0x%08x]", data, len); |
f0a29d433
|
845 |
else { |
088f1b199
|
846 |
const __be32 *p; |
f0a29d433
|
847 848 849 850 851 852 853 |
printf("<"); for (j = 0, p = data; j < len/4; j++) printf("0x%08x%s", fdt32_to_cpu(p[j]), j < (len/4 - 1) ? " " : ""); printf(">"); } |
4abd844d8
|
854 |
} else { /* anything else... hexdump */ |
f0a29d433
|
855 |
if (len > CONFIG_CMD_FDT_MAX_DUMP) |
085b9c3a1
|
856 |
printf("* 0x%p [0x%08x]", data, len); |
f0a29d433
|
857 858 859 860 861 862 863 864 |
else { const u8 *s; printf("["); for (j = 0, s = data; j < len; j++) printf("%02x%s", s[j], j < len - 1 ? " " : ""); printf("]"); } |
781e09ee6
|
865 866 |
} } |
addd8ce83
|
867 868 869 |
/****************************************************************************/ /* |
e489b9c07
|
870 |
* Recursively print (a portion of) the working_fdt. The depth parameter |
addd8ce83
|
871 872 |
* determines how deeply nested the fdt is printed. */ |
dbaf07ce6
|
873 |
static int fdt_print(const char *pathp, char *prop, int depth) |
addd8ce83
|
874 |
{ |
addd8ce83
|
875 876 877 |
static char tabs[MAX_LEVEL+1] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; |
dbaf07ce6
|
878 |
const void *nodep; /* property node pointer */ |
addd8ce83
|
879 880 881 882 883 |
int nodeoffset; /* node offset from libfdt */ int nextoffset; /* next node offset from libfdt */ uint32_t tag; /* tag */ int len; /* length of the property */ int level = 0; /* keep track of nesting level */ |
916235281
|
884 |
const struct fdt_property *fdt_prop; |
addd8ce83
|
885 |
|
e489b9c07
|
886 |
nodeoffset = fdt_path_offset (working_fdt, pathp); |
addd8ce83
|
887 888 889 890 |
if (nodeoffset < 0) { /* * Not found or something else bad happened. */ |
8d04f02f6
|
891 892 |
printf ("libfdt fdt_path_offset() returned %s ", |
06e19a077
|
893 |
fdt_strerror(nodeoffset)); |
addd8ce83
|
894 895 896 897 898 899 900 |
return 1; } /* * The user passed in a property as well as node path. * Print only the given property and then return. */ if (prop) { |
e489b9c07
|
901 |
nodep = fdt_getprop (working_fdt, nodeoffset, prop, &len); |
addd8ce83
|
902 903 904 905 906 907 |
if (len == 0) { /* no property value */ printf("%s %s ", pathp, prop); return 0; } else if (len > 0) { |
28f384b17
|
908 |
printf("%s = ", prop); |
addd8ce83
|
909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 |
print_data (nodep, len); printf(" "); return 0; } else { printf ("libfdt fdt_getprop(): %s ", fdt_strerror(len)); return 1; } } /* * The user passed in a node path and no property, * print the node and all subnodes. */ |
addd8ce83
|
925 |
while(level >= 0) { |
e489b9c07
|
926 |
tag = fdt_next_tag(working_fdt, nodeoffset, &nextoffset); |
addd8ce83
|
927 928 |
switch(tag) { case FDT_BEGIN_NODE: |
e489b9c07
|
929 |
pathp = fdt_get_name(working_fdt, nodeoffset, NULL); |
916235281
|
930 931 932 933 934 |
if (level <= depth) { if (pathp == NULL) pathp = "/* NULL pointer error */"; if (*pathp == '\0') pathp = "/"; /* root is nameless */ |
addd8ce83
|
935 936 937 |
printf("%s%s { ", &tabs[MAX_LEVEL - level], pathp); |
916235281
|
938 |
} |
addd8ce83
|
939 |
level++; |
addd8ce83
|
940 |
if (level >= MAX_LEVEL) { |
916235281
|
941 942 |
printf("Nested too deep, aborting. "); |
addd8ce83
|
943 944 945 946 947 |
return 1; } break; case FDT_END_NODE: level--; |
916235281
|
948 |
if (level <= depth) |
addd8ce83
|
949 950 951 952 953 954 955 |
printf("%s}; ", &tabs[MAX_LEVEL - level]); if (level == 0) { level = -1; /* exit the loop */ } break; case FDT_PROP: |
e489b9c07
|
956 |
fdt_prop = fdt_offset_ptr(working_fdt, nodeoffset, |
916235281
|
957 |
sizeof(*fdt_prop)); |
e489b9c07
|
958 |
pathp = fdt_string(working_fdt, |
916235281
|
959 960 961 |
fdt32_to_cpu(fdt_prop->nameoff)); len = fdt32_to_cpu(fdt_prop->len); nodep = fdt_prop->data; |
addd8ce83
|
962 963 964 965 966 967 968 |
if (len < 0) { printf ("libfdt fdt_getprop(): %s ", fdt_strerror(len)); return 1; } else if (len == 0) { /* the property has no value */ |
916235281
|
969 |
if (level <= depth) |
addd8ce83
|
970 971 972 973 974 |
printf("%s%s; ", &tabs[MAX_LEVEL - level], pathp); } else { |
916235281
|
975 |
if (level <= depth) { |
28f384b17
|
976 |
printf("%s%s = ", |
addd8ce83
|
977 978 979 980 981 982 983 984 985 |
&tabs[MAX_LEVEL - level], pathp); print_data (nodep, len); printf("; "); } } break; case FDT_NOP: |
dc4b0b38d
|
986 987 |
printf("%s/* NOP */ ", &tabs[MAX_LEVEL - level]); |
addd8ce83
|
988 989 990 991 |
break; case FDT_END: return 1; default: |
916235281
|
992 |
if (level <= depth) |
addd8ce83
|
993 994 995 996 997 998 999 1000 |
printf("Unknown tag 0x%08X ", tag); return 1; } nodeoffset = nextoffset; } return 0; } |
781e09ee6
|
1001 |
/********************************************************************/ |
088f1b199
|
1002 1003 |
#ifdef CONFIG_SYS_LONGHELP static char fdt_help_text[] = |
4b5786550
|
1004 1005 |
"addr [-c] <addr> [<length>] - Set the [control] fdt location to <addr> " |
fd61e55dd
|
1006 1007 1008 1009 |
#ifdef CONFIG_OF_BOARD_SETUP "fdt boardsetup - Do board-specific set up " #endif |
238cb7a42
|
1010 1011 |
"fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active " |
40afac22a
|
1012 1013 |
"fdt resize - Resize fdt to size + padding to 4k addr " |
781e09ee6
|
1014 1015 1016 1017 |
"fdt print <path> [<prop>] - Recursive print starting at <path> " "fdt list <path> [<prop>] - Print one level starting at <path> " |
bc80295b6
|
1018 1019 1020 1021 1022 1023 1024 1025 |
"fdt get value <var> <path> <prop> - Get <property> and store in <var> " "fdt get name <var> <path> <index> - Get name of node <index> and store in <var> " "fdt get addr <var> <path> <prop> - Get start address of <property> and store in <var> " "fdt get size <var> <path> [<prop>] - Get size of [<property>] or num nodes and store in <var> " |
781e09ee6
|
1026 1027 1028 1029 1030 1031 |
"fdt set <path> <prop> [<val>] - Set <property> [to <val>] " "fdt mknode <path> <node> - Create a new node after <path> " "fdt rm <path> [<prop>] - Delete the node or <property> " |
804887e60
|
1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 |
"fdt header - Display header info " "fdt bootcpu <id> - Set boot cpuid " "fdt memory <addr> <size> - Add/Update memory node " "fdt rsvmem print - Show current mem reserves " "fdt rsvmem add <addr> <size> - Add a mem reserve " "fdt rsvmem delete <index> - Delete a mem reserves " |
f953d99fd
|
1044 1045 1046 1047 |
"fdt chosen [<start> <end>] - Add/update the /chosen branch in the tree " " <start>/<end> - initrd start/end addr " |
109c30fb8
|
1048 |
"NOTE: Dereference aliases by omiting the leading '/', " |
088f1b199
|
1049 1050 1051 1052 1053 1054 |
"e.g. fdt print ethernet0."; #endif U_BOOT_CMD( fdt, 255, 0, do_fdt, "flattened device tree utility commands", fdt_help_text |
781e09ee6
|
1055 |
); |