Blame view
cmd/fdt.c
27.2 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> |
b08c8c487
|
16 |
#include <linux/libfdt.h> |
64dbbd40c
|
17 |
#include <fdt_support.h> |
0eb25b619
|
18 |
#include <mapmem.h> |
a92fd6577
|
19 |
#include <asm/io.h> |
781e09ee6
|
20 21 |
#define MAX_LEVEL 32 /* how deeply nested we will go */ |
fd61e55dd
|
22 |
#define SCRATCHPAD 1024 /* bytes of scratchpad memory */ |
5d927b428
|
23 |
#define CMD_FDT_MAX_DUMP 64 |
781e09ee6
|
24 25 26 27 28 |
/* * Global data (for the gd->bd) */ DECLARE_GLOBAL_DATA_PTR; |
d14da9130
|
29 |
static int fdt_valid(struct fdt_header **blobp); |
54841ab50
|
30 |
static int fdt_parse_prop(char *const*newval, int count, char *data, int *len); |
dbaf07ce6
|
31 |
static int fdt_print(const char *pathp, char *prop, int depth); |
bc80295b6
|
32 |
static int is_printable_string(const void *data, int len); |
781e09ee6
|
33 |
|
781e09ee6
|
34 |
/* |
ae9e97fa9
|
35 36 37 |
* The working_fdt points to our working flattened device tree. */ struct fdt_header *working_fdt; |
90fbee3e4
|
38 |
void set_working_fdt_addr(ulong addr) |
54f9c8669
|
39 |
{ |
a92fd6577
|
40 |
void *buf; |
90fbee3e4
|
41 |
buf = map_sysmem(addr, 0); |
a92fd6577
|
42 |
working_fdt = buf; |
018f53032
|
43 |
env_set_hex("fdtaddr", addr); |
54f9c8669
|
44 |
} |
ae9e97fa9
|
45 |
/* |
bc80295b6
|
46 47 |
* Get a value from the fdt and format it to be set in the environment */ |
382bee57f
|
48 |
static int fdt_value_env_set(const void *nodep, int len, const char *var) |
bc80295b6
|
49 50 |
{ if (is_printable_string(nodep, len)) |
382bee57f
|
51 |
env_set(var, (void *)nodep); |
bc80295b6
|
52 53 |
else if (len == 4) { char buf[11]; |
b05bf6c75
|
54 |
sprintf(buf, "0x%08X", fdt32_to_cpu(*(fdt32_t *)nodep)); |
382bee57f
|
55 |
env_set(var, buf); |
bc80295b6
|
56 57 58 59 60 61 62 63 |
} 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)); |
382bee57f
|
64 |
env_set(var, buf); |
bc80295b6
|
65 66 67 68 69 70 71 72 73 |
} else { printf("error: unprintable value "); return 1; } return 0; } /* |
781e09ee6
|
74 75 |
* Flattened Device Tree command, see the help for parameter definitions. */ |
088f1b199
|
76 |
static int do_fdt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
781e09ee6
|
77 |
{ |
47e26b1bf
|
78 |
if (argc < 2) |
4c12eeb8b
|
79 |
return CMD_RET_USAGE; |
781e09ee6
|
80 |
|
47e26b1bf
|
81 |
/* |
781e09ee6
|
82 |
* Set the address of the fdt |
47e26b1bf
|
83 |
*/ |
f0ed68e21
|
84 |
if (strncmp(argv[1], "ad", 2) == 0) { |
54f9c8669
|
85 |
unsigned long addr; |
4b5786550
|
86 87 |
int control = 0; struct fdt_header *blob; |
781e09ee6
|
88 89 90 |
/* * Set the address [and length] of the fdt. */ |
4b5786550
|
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
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
|
107 |
return 1; |
4b5786550
|
108 109 |
printf("The address of the fdt is %#08lx ", |
c71a0164d
|
110 |
control ? (ulong)map_to_sysmem(blob) : |
bfebc8c96
|
111 |
env_get_hex("fdtaddr", 0)); |
7dbc38ad9
|
112 113 |
return 0; } |
4b5786550
|
114 |
addr = simple_strtoul(argv[0], NULL, 16); |
a92fd6577
|
115 |
blob = map_sysmem(addr, 0); |
4b5786550
|
116 |
if (!fdt_valid(&blob)) |
781e09ee6
|
117 |
return 1; |
4b5786550
|
118 119 120 |
if (control) gd->fdt_blob = blob; else |
90fbee3e4
|
121 |
set_working_fdt_addr(addr); |
781e09ee6
|
122 |
|
4b5786550
|
123 |
if (argc >= 2) { |
781e09ee6
|
124 125 126 127 128 |
int len; int err; /* * Optional new length */ |
4b5786550
|
129 130 |
len = simple_strtoul(argv[1], NULL, 16); if (len < fdt_totalsize(blob)) { |
addd8ce83
|
131 132 133 |
printf ("New length %d < existing length %d, " "ignoring. ", |
4b5786550
|
134 |
len, fdt_totalsize(blob)); |
781e09ee6
|
135 136 137 138 |
} else { /* * Open in place with a new length. */ |
4b5786550
|
139 |
err = fdt_open_into(blob, blob, len); |
781e09ee6
|
140 |
if (err != 0) { |
addd8ce83
|
141 142 143 |
printf ("libfdt fdt_open_into(): %s ", fdt_strerror(err)); |
781e09ee6
|
144 145 146 |
} } } |
e02c94587
|
147 148 149 150 151 152 153 154 155 156 157 158 159 |
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
|
160 |
/* |
e489b9c07
|
161 |
* Move the working_fdt |
47e26b1bf
|
162 |
*/ |
e02c94587
|
163 |
if (strncmp(argv[1], "mo", 2) == 0) { |
781e09ee6
|
164 165 166 |
struct fdt_header *newaddr; int len; int err; |
47e26b1bf
|
167 |
if (argc < 4) |
4c12eeb8b
|
168 |
return CMD_RET_USAGE; |
781e09ee6
|
169 170 171 172 |
/* * Set the address and length of the fdt. */ |
e489b9c07
|
173 |
working_fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16); |
d14da9130
|
174 |
if (!fdt_valid(&working_fdt)) |
781e09ee6
|
175 |
return 1; |
781e09ee6
|
176 |
|
addd8ce83
|
177 |
newaddr = (struct fdt_header *)simple_strtoul(argv[3],NULL,16); |
6be07cc1c
|
178 179 180 181 182 183 |
/* * If the user specifies a length, use that. Otherwise use the * current length. */ if (argc <= 4) { |
e489b9c07
|
184 |
len = fdt_totalsize(working_fdt); |
6be07cc1c
|
185 186 |
} else { len = simple_strtoul(argv[4], NULL, 16); |
e489b9c07
|
187 |
if (len < fdt_totalsize(working_fdt)) { |
addd8ce83
|
188 189 190 |
printf ("New length 0x%X < existing length " "0x%X, aborting. ", |
e489b9c07
|
191 |
len, fdt_totalsize(working_fdt)); |
6be07cc1c
|
192 193 |
return 1; } |
781e09ee6
|
194 195 196 197 198 |
} /* * Copy to the new location. */ |
e489b9c07
|
199 |
err = fdt_open_into(working_fdt, newaddr, len); |
781e09ee6
|
200 |
if (err != 0) { |
addd8ce83
|
201 202 203 |
printf ("libfdt fdt_open_into(): %s ", fdt_strerror(err)); |
781e09ee6
|
204 205 |
return 1; } |
e489b9c07
|
206 |
working_fdt = newaddr; |
f7f191ee4
|
207 208 209 210 |
#ifdef CONFIG_OF_SYSTEM_SETUP /* Call the board-specific fixup routine */ } else if (strncmp(argv[1], "sys", 3) == 0) { int err = ft_system_setup(working_fdt, gd->bd); |
781e09ee6
|
211 |
|
f7f191ee4
|
212 213 214 215 216 217 218 |
if (err) { printf("Failed to add system information to FDT: %s ", fdt_strerror(err)); return CMD_RET_FAILURE; } #endif |
47e26b1bf
|
219 |
/* |
25114033a
|
220 |
* Make a new node |
47e26b1bf
|
221 |
*/ |
2fb698bf5
|
222 |
} else if (strncmp(argv[1], "mk", 2) == 0) { |
25114033a
|
223 224 225 226 227 228 229 230 |
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
|
231 |
if (argc < 4) |
4c12eeb8b
|
232 |
return CMD_RET_USAGE; |
25114033a
|
233 234 235 |
pathp = argv[2]; nodep = argv[3]; |
e489b9c07
|
236 |
nodeoffset = fdt_path_offset (working_fdt, pathp); |
25114033a
|
237 238 239 240 |
if (nodeoffset < 0) { /* * Not found or something else bad happened. */ |
8d04f02f6
|
241 242 |
printf ("libfdt fdt_path_offset() returned %s ", |
06e19a077
|
243 |
fdt_strerror(nodeoffset)); |
25114033a
|
244 245 |
return 1; } |
e489b9c07
|
246 |
err = fdt_add_subnode(working_fdt, nodeoffset, nodep); |
25114033a
|
247 |
if (err < 0) { |
addd8ce83
|
248 249 250 |
printf ("libfdt fdt_add_subnode(): %s ", fdt_strerror(err)); |
25114033a
|
251 252 |
return 1; } |
47e26b1bf
|
253 |
/* |
e489b9c07
|
254 |
* Set the value of a property in the working_fdt. |
47e26b1bf
|
255 |
*/ |
25114033a
|
256 |
} else if (argv[1][0] == 's') { |
781e09ee6
|
257 |
char *pathp; /* path */ |
addd8ce83
|
258 |
char *prop; /* property */ |
781e09ee6
|
259 |
int nodeoffset; /* node offset from libfdt */ |
6dfd65f81
|
260 |
static char data[SCRATCHPAD] __aligned(4);/* property storage */ |
9620d8725
|
261 |
const void *ptmp; |
addd8ce83
|
262 263 |
int len; /* new length of the property */ int ret; /* return value */ |
781e09ee6
|
264 265 |
/* |
ea6d8be15
|
266 |
* Parameters: Node path, property, optional value. |
781e09ee6
|
267 |
*/ |
47e26b1bf
|
268 |
if (argc < 4) |
4c12eeb8b
|
269 |
return CMD_RET_USAGE; |
781e09ee6
|
270 271 272 |
pathp = argv[2]; prop = argv[3]; |
781e09ee6
|
273 |
|
e489b9c07
|
274 |
nodeoffset = fdt_path_offset (working_fdt, pathp); |
25114033a
|
275 |
if (nodeoffset < 0) { |
781e09ee6
|
276 |
/* |
25114033a
|
277 |
* Not found or something else bad happened. |
781e09ee6
|
278 |
*/ |
8d04f02f6
|
279 280 |
printf ("libfdt fdt_path_offset() returned %s ", |
06e19a077
|
281 |
fdt_strerror(nodeoffset)); |
781e09ee6
|
282 |
return 1; |
25114033a
|
283 |
} |
25114033a
|
284 |
|
9620d8725
|
285 286 287 288 289 290 291 292 293 294 |
if (argc == 4) { len = 0; } else { ptmp = fdt_getprop(working_fdt, nodeoffset, prop, &len); if (len > SCRATCHPAD) { printf("prop (%d) doesn't fit in scratchpad! ", len); return 1; } |
cee8c35d1
|
295 296 |
if (ptmp != NULL) memcpy(data, ptmp, len); |
9620d8725
|
297 298 299 300 |
ret = fdt_parse_prop(&argv[4], argc - 4, data, &len); if (ret != 0) return ret; } |
e489b9c07
|
301 |
ret = fdt_setprop(working_fdt, nodeoffset, prop, data, len); |
25114033a
|
302 303 304 305 |
if (ret < 0) { printf ("libfdt fdt_setprop(): %s ", fdt_strerror(ret)); return 1; |
781e09ee6
|
306 |
} |
bc80295b6
|
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 356 357 358 359 360 |
/******************************************************************** * 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) { |
382bee57f
|
361 |
const char *node_name; |
bc80295b6
|
362 |
|
382bee57f
|
363 364 365 366 |
node_name = fdt_get_name(working_fdt, nextNodeOffset, NULL); env_set(var, node_name); |
bc80295b6
|
367 368 369 370 371 372 373 374 375 |
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 */ |
018f53032
|
376 |
env_set_ulong(var, curIndex + 1); |
bc80295b6
|
377 378 379 380 381 382 383 384 385 386 387 |
} 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 */ |
382bee57f
|
388 |
env_set(var, ""); |
bc80295b6
|
389 |
return 0; |
72c98ed1a
|
390 |
} else if (nodep && len > 0) { |
bc80295b6
|
391 392 |
if (subcmd[0] == 'v') { int ret; |
382bee57f
|
393 394 |
ret = fdt_value_env_set(nodep, len, var); |
bc80295b6
|
395 396 397 398 399 |
if (ret != 0) return ret; } else if (subcmd[0] == 'a') { /* Get address */ char buf[11]; |
085b9c3a1
|
400 |
sprintf(buf, "0x%p", nodep); |
382bee57f
|
401 |
env_set(var, buf); |
bc80295b6
|
402 403 404 405 406 |
} else if (subcmd[0] == 's') { /* Get size */ char buf[11]; sprintf(buf, "0x%08X", len); |
382bee57f
|
407 |
env_set(var, buf); |
bc80295b6
|
408 409 410 411 412 413 414 415 416 417 |
} else return CMD_RET_USAGE; return 0; } else { printf("libfdt fdt_getprop(): %s ", fdt_strerror(len)); return 1; } } |
47e26b1bf
|
418 |
/* |
781e09ee6
|
419 |
* Print (recursive) / List (single level) |
47e26b1bf
|
420 |
*/ |
25114033a
|
421 |
} else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) { |
781e09ee6
|
422 423 |
int depth = MAX_LEVEL; /* how deep to print */ char *pathp; /* path */ |
addd8ce83
|
424 425 |
char *prop; /* property */ int ret; /* return value */ |
f738b4a75
|
426 |
static char root[2] = "/"; |
781e09ee6
|
427 428 429 430 |
/* * list is an alias for print, but limited to 1 level */ |
25114033a
|
431 |
if (argv[1][0] == 'l') { |
781e09ee6
|
432 433 434 435 436 437 438 |
depth = 1; } /* * Get the starting path. The root node is an oddball, * the offset is zero and has no name. */ |
f738b4a75
|
439 440 441 442 |
if (argc == 2) pathp = root; else pathp = argv[2]; |
781e09ee6
|
443 444 445 446 |
if (argc > 3) prop = argv[3]; else prop = NULL; |
addd8ce83
|
447 448 449 |
ret = fdt_print(pathp, prop, depth); if (ret != 0) return ret; |
781e09ee6
|
450 |
|
47e26b1bf
|
451 |
/* |
781e09ee6
|
452 |
* Remove a property/node |
47e26b1bf
|
453 |
*/ |
2fb698bf5
|
454 |
} else if (strncmp(argv[1], "rm", 2) == 0) { |
781e09ee6
|
455 456 457 458 459 460 461 |
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
|
462 |
nodeoffset = fdt_path_offset (working_fdt, argv[2]); |
25114033a
|
463 464 465 466 |
if (nodeoffset < 0) { /* * Not found or something else bad happened. */ |
8d04f02f6
|
467 468 |
printf ("libfdt fdt_path_offset() returned %s ", |
06e19a077
|
469 |
fdt_strerror(nodeoffset)); |
25114033a
|
470 |
return 1; |
781e09ee6
|
471 472 473 474 475 476 |
} /* * Do the delete. A fourth parameter means delete a property, * otherwise delete the node. */ if (argc > 3) { |
e489b9c07
|
477 |
err = fdt_delprop(working_fdt, nodeoffset, argv[3]); |
781e09ee6
|
478 |
if (err < 0) { |
addd8ce83
|
479 480 481 |
printf("libfdt fdt_delprop(): %s ", fdt_strerror(err)); |
781e09ee6
|
482 483 484 |
return err; } } else { |
e489b9c07
|
485 |
err = fdt_del_node(working_fdt, nodeoffset); |
781e09ee6
|
486 |
if (err < 0) { |
addd8ce83
|
487 488 489 |
printf("libfdt fdt_del_node(): %s ", fdt_strerror(err)); |
781e09ee6
|
490 491 492 |
return err; } } |
804887e60
|
493 |
|
47e26b1bf
|
494 |
/* |
804887e60
|
495 |
* Display header info |
47e26b1bf
|
496 |
*/ |
804887e60
|
497 |
} else if (argv[1][0] == 'h') { |
e489b9c07
|
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 |
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
|
513 514 |
printf("version:\t\t%d ", version); |
e489b9c07
|
515 516 517 |
printf("last_comp_version:\t%d ", fdt_last_comp_version(working_fdt)); |
804887e60
|
518 519 520 |
if (version >= 2) printf("boot_cpuid_phys:\t0x%x ", |
e489b9c07
|
521 |
fdt_boot_cpuid_phys(working_fdt)); |
804887e60
|
522 523 524 |
if (version >= 3) printf("size_dt_strings:\t0x%x ", |
e489b9c07
|
525 |
fdt_size_dt_strings(working_fdt)); |
804887e60
|
526 527 528 |
if (version >= 17) printf("size_dt_struct:\t\t0x%x ", |
e489b9c07
|
529 530 531 532 |
fdt_size_dt_struct(working_fdt)); printf("number mem_rsv:\t\t0x%x ", fdt_num_mem_rsv(working_fdt)); |
804887e60
|
533 534 |
printf(" "); |
47e26b1bf
|
535 |
/* |
804887e60
|
536 |
* Set boot cpu id |
47e26b1bf
|
537 |
*/ |
2fb698bf5
|
538 |
} else if (strncmp(argv[1], "boo", 3) == 0) { |
804887e60
|
539 |
unsigned long tmp = simple_strtoul(argv[2], NULL, 16); |
e489b9c07
|
540 |
fdt_set_boot_cpuid_phys(working_fdt, tmp); |
804887e60
|
541 |
|
47e26b1bf
|
542 |
/* |
804887e60
|
543 |
* memory command |
47e26b1bf
|
544 |
*/ |
2fb698bf5
|
545 |
} else if (strncmp(argv[1], "me", 2) == 0) { |
804887e60
|
546 547 |
uint64_t addr, size; int err; |
4b142febf
|
548 549 |
addr = simple_strtoull(argv[2], NULL, 16); size = simple_strtoull(argv[3], NULL, 16); |
e489b9c07
|
550 |
err = fdt_fixup_memory(working_fdt, addr, size); |
804887e60
|
551 552 |
if (err < 0) return err; |
47e26b1bf
|
553 |
/* |
804887e60
|
554 |
* mem reserve commands |
47e26b1bf
|
555 |
*/ |
2fb698bf5
|
556 |
} else if (strncmp(argv[1], "rs", 2) == 0) { |
804887e60
|
557 558 |
if (argv[2][0] == 'p') { uint64_t addr, size; |
e489b9c07
|
559 |
int total = fdt_num_mem_rsv(working_fdt); |
804887e60
|
560 561 562 563 564 565 566 |
int j, err; printf("index\t\t start\t\t size "); printf("-------------------------------" "----------------- "); for (j = 0; j < total; j++) { |
e489b9c07
|
567 |
err = fdt_get_mem_rsv(working_fdt, j, &addr, &size); |
804887e60
|
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 |
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
|
584 585 |
addr = simple_strtoull(argv[3], NULL, 16); size = simple_strtoull(argv[4], NULL, 16); |
e489b9c07
|
586 |
err = fdt_add_mem_rsv(working_fdt, addr, size); |
804887e60
|
587 588 589 590 591 592 593 594 595 |
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
|
596 |
int err = fdt_del_mem_rsv(working_fdt, idx); |
804887e60
|
597 598 599 600 601 602 603 604 605 |
if (err < 0) { printf("libfdt fdt_del_mem_rsv(): %s ", fdt_strerror(err)); return err; } } else { /* Unrecognized command */ |
4c12eeb8b
|
606 |
return CMD_RET_USAGE; |
804887e60
|
607 |
} |
99dffca3b
|
608 |
} |
fd61e55dd
|
609 |
#ifdef CONFIG_OF_BOARD_SETUP |
99dffca3b
|
610 |
/* Call the board-specific fixup routine */ |
4ba98dc26
|
611 612 613 614 615 616 617 618 619 620 |
else if (strncmp(argv[1], "boa", 3) == 0) { int err = ft_board_setup(working_fdt, gd->bd); if (err) { printf("Failed to update board information in FDT: %s ", fdt_strerror(err)); return CMD_RET_FAILURE; } } |
fd61e55dd
|
621 |
#endif |
99dffca3b
|
622 |
/* Create a chosen node */ |
097dd3e0a
|
623 |
else if (strncmp(argv[1], "cho", 3) == 0) { |
f953d99fd
|
624 |
unsigned long initrd_start = 0, initrd_end = 0; |
47e26b1bf
|
625 |
if ((argc != 2) && (argc != 4)) |
4c12eeb8b
|
626 |
return CMD_RET_USAGE; |
f953d99fd
|
627 628 629 630 631 |
if (argc == 4) { initrd_start = simple_strtoul(argv[2], NULL, 16); initrd_end = simple_strtoul(argv[3], NULL, 16); } |
bc6ed0f9d
|
632 |
fdt_chosen(working_fdt); |
dbe963ae5
|
633 |
fdt_initrd(working_fdt, initrd_start, initrd_end); |
097dd3e0a
|
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 |
#if defined(CONFIG_FIT_SIGNATURE) } else if (strncmp(argv[1], "che", 3) == 0) { int cfg_noffset; int ret; unsigned long addr; struct fdt_header *blob; if (!working_fdt) return CMD_RET_FAILURE; if (argc > 2) { addr = simple_strtoul(argv[2], NULL, 16); blob = map_sysmem(addr, 0); } else { blob = (struct fdt_header *)gd->fdt_blob; } if (!fdt_valid(&blob)) return 1; gd->fdt_blob = blob; cfg_noffset = fit_conf_get_node(working_fdt, NULL); if (!cfg_noffset) { printf("Could not find configuration node: %s ", fdt_strerror(cfg_noffset)); return CMD_RET_FAILURE; } ret = fit_config_verify(working_fdt, cfg_noffset); |
12df2abe3
|
664 |
if (ret == 0) |
097dd3e0a
|
665 666 667 668 |
return CMD_RET_SUCCESS; else return CMD_RET_FAILURE; #endif |
40afac22a
|
669 |
} |
e6628ad7b
|
670 671 672 673 674 |
#ifdef CONFIG_OF_LIBFDT_OVERLAY /* apply an overlay */ else if (strncmp(argv[1], "ap", 2) == 0) { unsigned long addr; struct fdt_header *blob; |
082b1414e
|
675 |
int ret; |
e6628ad7b
|
676 677 678 679 680 681 682 683 684 685 686 |
if (argc != 3) return CMD_RET_USAGE; if (!working_fdt) return CMD_RET_FAILURE; addr = simple_strtoul(argv[2], NULL, 16); blob = map_sysmem(addr, 0); if (!fdt_valid(&blob)) return CMD_RET_FAILURE; |
81ecc5d92
|
687 688 689 |
/* apply method prints messages on error */ ret = fdt_overlay_apply_verbose(working_fdt, blob); if (ret) |
e6628ad7b
|
690 691 692 |
return CMD_RET_FAILURE; } #endif |
40afac22a
|
693 694 |
/* resize the fdt */ else if (strncmp(argv[1], "re", 2) == 0) { |
ef4768364
|
695 696 697 698 699 700 |
uint extrasize; if (argc > 2) extrasize = simple_strtoul(argv[2], NULL, 16); else extrasize = 0; fdt_shrink_to_minimum(working_fdt, extrasize); |
40afac22a
|
701 702 |
} else { |
99dffca3b
|
703 |
/* Unrecognized command */ |
4c12eeb8b
|
704 |
return CMD_RET_USAGE; |
781e09ee6
|
705 706 707 708 |
} return 0; } |
addd8ce83
|
709 |
/****************************************************************************/ |
781e09ee6
|
710 |
|
d14da9130
|
711 712 713 714 715 716 717 |
/** * 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
|
718 |
{ |
d14da9130
|
719 720 |
const void *blob = *blobp; int err; |
64dbbd40c
|
721 |
|
d14da9130
|
722 |
if (blob == NULL) { |
64dbbd40c
|
723 724 |
printf ("The address of the fdt is invalid (NULL). "); |
781e09ee6
|
725 726 |
return 0; } |
64dbbd40c
|
727 |
|
d14da9130
|
728 |
err = fdt_check_header(blob); |
64dbbd40c
|
729 730 731 732 |
if (err == 0) return 1; /* valid */ if (err < 0) { |
25114033a
|
733 |
printf("libfdt fdt_check_header(): %s", fdt_strerror(err)); |
64dbbd40c
|
734 735 736 737 |
/* * Be more informative on bad version. */ if (err == -FDT_ERR_BADVERSION) { |
d14da9130
|
738 |
if (fdt_version(blob) < |
e489b9c07
|
739 |
FDT_FIRST_SUPPORTED_VERSION) { |
dc4b0b38d
|
740 |
printf (" - too old, fdt %d < %d", |
d14da9130
|
741 |
fdt_version(blob), |
addd8ce83
|
742 |
FDT_FIRST_SUPPORTED_VERSION); |
64dbbd40c
|
743 |
} |
d14da9130
|
744 |
if (fdt_last_comp_version(blob) > |
e489b9c07
|
745 |
FDT_LAST_SUPPORTED_VERSION) { |
dc4b0b38d
|
746 |
printf (" - too new, fdt %d > %d", |
d14da9130
|
747 |
fdt_version(blob), |
addd8ce83
|
748 |
FDT_LAST_SUPPORTED_VERSION); |
64dbbd40c
|
749 |
} |
64dbbd40c
|
750 751 752 |
} printf(" "); |
d14da9130
|
753 |
*blobp = NULL; |
781e09ee6
|
754 755 756 757 |
return 0; } return 1; } |
addd8ce83
|
758 |
/****************************************************************************/ |
781e09ee6
|
759 760 |
/* |
addd8ce83
|
761 |
* Parse the user's input, partially heuristic. Valid formats: |
4abd844d8
|
762 |
* <0x00112233 4 05> - an array of cells. Numbers follow standard |
53677ef18
|
763 |
* C conventions. |
addd8ce83
|
764 765 766 767 |
* [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
|
768 |
* newval: An array of strings containing the new property as specified |
53677ef18
|
769 |
* on the command line |
4abd844d8
|
770 771 772 |
* 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
|
773 |
*/ |
54841ab50
|
774 |
static int fdt_parse_prop(char * const *newval, int count, char *data, int *len) |
addd8ce83
|
775 776 |
{ char *cp; /* temporary char pointer */ |
4abd844d8
|
777 |
char *newp; /* temporary newval char pointer */ |
addd8ce83
|
778 |
unsigned long tmp; /* holds converted values */ |
4abd844d8
|
779 |
int stridx = 0; |
addd8ce83
|
780 |
|
4abd844d8
|
781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 |
*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); |
9620d8725
|
799 800 801 802 |
if (*cp != '?') *(fdt32_t *)data = cpu_to_fdt32(tmp); else newp++; |
4abd844d8
|
803 804 805 806 807 |
data += 4; *len += 4; /* If the ptr didn't advance, something went wrong */ if ((newp - cp) <= 0) { |
addd8ce83
|
808 809 810 811 812 |
printf("Sorry, I could not convert \"%s\" ", cp); return 1; } |
4abd844d8
|
813 814 815 |
while (*newp == ' ') newp++; |
addd8ce83
|
816 |
} |
4abd844d8
|
817 818 819 820 |
if (*newp != '>') { printf("Unexpected character '%c' ", *newp); |
addd8ce83
|
821 822 |
return 1; } |
4abd844d8
|
823 |
} else if (*newp == '[') { |
addd8ce83
|
824 825 826 |
/* * Byte stream. Convert the values. */ |
4abd844d8
|
827 |
newp++; |
6e748ea00
|
828 |
while ((stridx < count) && (*newp != ']')) { |
4abd844d8
|
829 830 |
while (*newp == ' ') newp++; |
6e748ea00
|
831 |
if (*newp == '\0') { |
4abd844d8
|
832 |
newp = newval[++stridx]; |
6e748ea00
|
833 834 835 836 837 838 839 |
continue; } if (!isxdigit(*newp)) break; tmp = simple_strtoul(newp, &newp, 16); *data++ = tmp & 0xFF; *len = *len + 1; |
addd8ce83
|
840 |
} |
4abd844d8
|
841 |
if (*newp != ']') { |
dc4b0b38d
|
842 843 |
printf("Unexpected character '%c' ", *newp); |
addd8ce83
|
844 845 846 847 |
return 1; } } else { /* |
6e748ea00
|
848 849 850 |
* Assume it is one or more strings. Copy it into our * data area for convenience (including the * terminating '\0's). |
addd8ce83
|
851 |
*/ |
4abd844d8
|
852 |
while (stridx < count) { |
6e748ea00
|
853 |
size_t length = strlen(newp) + 1; |
4abd844d8
|
854 |
strcpy(data, newp); |
6e748ea00
|
855 856 |
data += length; *len += length; |
4abd844d8
|
857 858 |
newp = newval[++stridx]; } |
addd8ce83
|
859 860 861 862 863 864 865 866 |
} return 0; } /****************************************************************************/ /* * Heuristic to guess if this is a string or concatenated strings. |
781e09ee6
|
867 868 869 870 871 872 873 874 875 |
*/ static int is_printable_string(const void *data, int len) { const char *s = data; /* zero length is not */ if (len == 0) return 0; |
8805beec8
|
876 877 878 879 |
/* must terminate with zero or ' ' */ if (s[len - 1] != '\0' && s[len - 1] != ' ') |
781e09ee6
|
880 881 882 |
return 0; /* printable or a null byte (concatenated strings) */ |
8805beec8
|
883 |
while (((*s == '\0') || isprint(*s) || isspace(*s)) && (len > 0)) { |
781e09ee6
|
884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 |
/* * 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
|
906 907 908 909 910 911 |
/* * 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
|
912 913 914 |
static void print_data(const void *data, int len) { int j; |
781e09ee6
|
915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 |
/* 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
|
936 |
if ((len %4) == 0) { |
5d927b428
|
937 |
if (len > CMD_FDT_MAX_DUMP) |
085b9c3a1
|
938 |
printf("* 0x%p [0x%08x]", data, len); |
f0a29d433
|
939 |
else { |
088f1b199
|
940 |
const __be32 *p; |
f0a29d433
|
941 942 943 944 945 946 947 |
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
|
948 |
} else { /* anything else... hexdump */ |
5d927b428
|
949 |
if (len > CMD_FDT_MAX_DUMP) |
085b9c3a1
|
950 |
printf("* 0x%p [0x%08x]", data, len); |
f0a29d433
|
951 952 953 954 955 956 957 958 |
else { const u8 *s; printf("["); for (j = 0, s = data; j < len; j++) printf("%02x%s", s[j], j < len - 1 ? " " : ""); printf("]"); } |
781e09ee6
|
959 960 |
} } |
addd8ce83
|
961 962 963 |
/****************************************************************************/ /* |
e489b9c07
|
964 |
* Recursively print (a portion of) the working_fdt. The depth parameter |
addd8ce83
|
965 966 |
* determines how deeply nested the fdt is printed. */ |
dbaf07ce6
|
967 |
static int fdt_print(const char *pathp, char *prop, int depth) |
addd8ce83
|
968 |
{ |
addd8ce83
|
969 970 971 |
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
|
972 |
const void *nodep; /* property node pointer */ |
addd8ce83
|
973 974 975 976 977 |
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
|
978 |
const struct fdt_property *fdt_prop; |
addd8ce83
|
979 |
|
e489b9c07
|
980 |
nodeoffset = fdt_path_offset (working_fdt, pathp); |
addd8ce83
|
981 982 983 984 |
if (nodeoffset < 0) { /* * Not found or something else bad happened. */ |
8d04f02f6
|
985 986 |
printf ("libfdt fdt_path_offset() returned %s ", |
06e19a077
|
987 |
fdt_strerror(nodeoffset)); |
addd8ce83
|
988 989 990 991 992 993 994 |
return 1; } /* * The user passed in a property as well as node path. * Print only the given property and then return. */ if (prop) { |
e489b9c07
|
995 |
nodep = fdt_getprop (working_fdt, nodeoffset, prop, &len); |
addd8ce83
|
996 997 998 999 1000 |
if (len == 0) { /* no property value */ printf("%s %s ", pathp, prop); return 0; |
9f9526727
|
1001 |
} else if (nodep && len > 0) { |
28f384b17
|
1002 |
printf("%s = ", prop); |
addd8ce83
|
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 |
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
|
1019 |
while(level >= 0) { |
e489b9c07
|
1020 |
tag = fdt_next_tag(working_fdt, nodeoffset, &nextoffset); |
addd8ce83
|
1021 1022 |
switch(tag) { case FDT_BEGIN_NODE: |
e489b9c07
|
1023 |
pathp = fdt_get_name(working_fdt, nodeoffset, NULL); |
916235281
|
1024 1025 1026 1027 1028 |
if (level <= depth) { if (pathp == NULL) pathp = "/* NULL pointer error */"; if (*pathp == '\0') pathp = "/"; /* root is nameless */ |
addd8ce83
|
1029 1030 1031 |
printf("%s%s { ", &tabs[MAX_LEVEL - level], pathp); |
916235281
|
1032 |
} |
addd8ce83
|
1033 |
level++; |
addd8ce83
|
1034 |
if (level >= MAX_LEVEL) { |
916235281
|
1035 1036 |
printf("Nested too deep, aborting. "); |
addd8ce83
|
1037 1038 1039 1040 1041 |
return 1; } break; case FDT_END_NODE: level--; |
916235281
|
1042 |
if (level <= depth) |
addd8ce83
|
1043 1044 1045 1046 1047 1048 1049 |
printf("%s}; ", &tabs[MAX_LEVEL - level]); if (level == 0) { level = -1; /* exit the loop */ } break; case FDT_PROP: |
e489b9c07
|
1050 |
fdt_prop = fdt_offset_ptr(working_fdt, nodeoffset, |
916235281
|
1051 |
sizeof(*fdt_prop)); |
e489b9c07
|
1052 |
pathp = fdt_string(working_fdt, |
916235281
|
1053 1054 1055 |
fdt32_to_cpu(fdt_prop->nameoff)); len = fdt32_to_cpu(fdt_prop->len); nodep = fdt_prop->data; |
addd8ce83
|
1056 1057 1058 1059 1060 1061 1062 |
if (len < 0) { printf ("libfdt fdt_getprop(): %s ", fdt_strerror(len)); return 1; } else if (len == 0) { /* the property has no value */ |
916235281
|
1063 |
if (level <= depth) |
addd8ce83
|
1064 1065 1066 1067 1068 |
printf("%s%s; ", &tabs[MAX_LEVEL - level], pathp); } else { |
916235281
|
1069 |
if (level <= depth) { |
28f384b17
|
1070 |
printf("%s%s = ", |
addd8ce83
|
1071 1072 1073 1074 1075 1076 1077 1078 1079 |
&tabs[MAX_LEVEL - level], pathp); print_data (nodep, len); printf("; "); } } break; case FDT_NOP: |
dc4b0b38d
|
1080 1081 |
printf("%s/* NOP */ ", &tabs[MAX_LEVEL - level]); |
addd8ce83
|
1082 1083 1084 1085 |
break; case FDT_END: return 1; default: |
916235281
|
1086 |
if (level <= depth) |
addd8ce83
|
1087 1088 1089 1090 1091 1092 1093 1094 |
printf("Unknown tag 0x%08X ", tag); return 1; } nodeoffset = nextoffset; } return 0; } |
781e09ee6
|
1095 |
/********************************************************************/ |
088f1b199
|
1096 1097 |
#ifdef CONFIG_SYS_LONGHELP static char fdt_help_text[] = |
4b5786550
|
1098 1099 |
"addr [-c] <addr> [<length>] - Set the [control] fdt location to <addr> " |
e6628ad7b
|
1100 1101 1102 1103 |
#ifdef CONFIG_OF_LIBFDT_OVERLAY "fdt apply <addr> - Apply overlay to the DT " #endif |
fd61e55dd
|
1104 1105 1106 1107 |
#ifdef CONFIG_OF_BOARD_SETUP "fdt boardsetup - Do board-specific set up " #endif |
c654b5172
|
1108 1109 1110 1111 |
#ifdef CONFIG_OF_SYSTEM_SETUP "fdt systemsetup - Do system-specific set up " #endif |
238cb7a42
|
1112 1113 |
"fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active " |
ef4768364
|
1114 1115 |
"fdt resize [<extrasize>] - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed " |
781e09ee6
|
1116 1117 1118 1119 |
"fdt print <path> [<prop>] - Recursive print starting at <path> " "fdt list <path> [<prop>] - Print one level starting at <path> " |
bc80295b6
|
1120 1121 1122 1123 1124 1125 1126 1127 |
"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
|
1128 1129 1130 1131 1132 1133 |
"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
|
1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 |
"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
|
1146 1147 1148 1149 |
"fdt chosen [<start> <end>] - Add/update the /chosen branch in the tree " " <start>/<end> - initrd start/end addr " |
097dd3e0a
|
1150 1151 1152 1153 1154 1155 1156 1157 |
#if defined(CONFIG_FIT_SIGNATURE) "fdt checksign [<addr>] - check FIT signature " " <start> - addr of key blob " " default gd->fdt_blob " #endif |
1cc0a9f49
|
1158 |
"NOTE: Dereference aliases by omitting the leading '/', " |
088f1b199
|
1159 1160 1161 1162 1163 1164 |
"e.g. fdt print ethernet0."; #endif U_BOOT_CMD( fdt, 255, 0, do_fdt, "flattened device tree utility commands", fdt_help_text |
781e09ee6
|
1165 |
); |