2 * Copyright (c) 2009-2010 The FreeBSD Foundation
5 * This software was developed by Semihalf under sponsorship from
6 * the FreeBSD Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
37 #include "bootstrap.h"
43 #define debugf(fmt, args...) do { printf("%s(): ", __func__); \
44 printf(fmt,##args); } while (0)
46 #define debugf(fmt, args...)
49 #define FDT_CWD_LEN 256
50 #define FDT_MAX_DEPTH 6
52 #define FDT_PROP_SEP " = "
54 #define STR(number) #number
55 #define STRINGIFY(number) STR(number)
57 #define MIN(num1, num2) (((num1) < (num2)) ? (num1):(num2))
59 static struct fdt_header *fdtp = NULL;
61 static int fdt_cmd_nyi(int argc, char *argv[]);
63 static int fdt_cmd_mkprop(int argc, char *argv[]);
64 static int fdt_cmd_cd(int argc, char *argv[]);
65 static int fdt_cmd_hdr(int argc, char *argv[]);
66 static int fdt_cmd_ls(int argc, char *argv[]);
67 static int fdt_cmd_prop(int argc, char *argv[]);
68 static int fdt_cmd_pwd(int argc, char *argv[]);
69 static int fdt_cmd_rm(int argc, char *argv[]);
70 static int fdt_cmd_mknode(int argc, char *argv[]);
72 typedef int cmdf_t(int, char *[]);
79 static const struct cmdtab commands[] = {
80 { "alias", &fdt_cmd_nyi },
81 { "cd", &fdt_cmd_cd },
82 { "header", &fdt_cmd_hdr },
83 { "ls", &fdt_cmd_ls },
84 { "mknode", &fdt_cmd_mknode },
85 { "mkprop", &fdt_cmd_mkprop },
86 { "mres", &fdt_cmd_nyi },
87 { "prop", &fdt_cmd_prop },
88 { "pwd", &fdt_cmd_pwd },
89 { "rm", &fdt_cmd_rm },
93 static char cwd[FDT_CWD_LEN] = "/";
98 struct preloaded_file *bfp;
102 * Find the device tree blob.
104 bfp = file_findfile(NULL, "dtb");
106 command_errmsg = "no device tree blob loaded";
109 fdtp = (struct fdt_header *)bfp->f_addr;
114 err = fdt_check_header(fdtp);
116 if (err == -FDT_ERR_BADVERSION)
117 sprintf(command_errbuf,
118 "incompatible blob version: %d, should be: %d",
119 fdt_version(fdtp), FDT_LAST_SUPPORTED_VERSION);
122 sprintf(command_errbuf, "error validating blob: %s",
129 #define fdt_strtovect(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
130 (cellbuf), (lim), (cellsize), 0);
132 /* Force using base 16 */
133 #define fdt_strtovectx(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
134 (cellbuf), (lim), (cellsize), 16);
137 _fdt_strtovect(char *str, void *cellbuf, int lim, unsigned char cellsize,
141 char *end = str + strlen(str) - 2;
142 uint32_t *u32buf = NULL;
143 uint8_t *u8buf = NULL;
146 if (cellsize == sizeof(uint32_t))
147 u32buf = (uint32_t *)cellbuf;
149 u8buf = (uint8_t *)cellbuf;
156 /* Skip white whitespace(s)/separators */
157 while (!isxdigit(*buf) && buf < end)
162 cpu_to_fdt32((uint32_t)strtol(buf, NULL, base));
165 u8buf[cnt] = (uint8_t)strtol(buf, NULL, base);
167 if (cnt + 1 <= lim - 1)
172 /* Find another number */
173 while ((isxdigit(*buf) || *buf == 'x') && buf < end)
179 #define TMP_MAX_ETH 8
182 fixup_ethernet(const char *env, char *ethstr, int *eth_no, int len)
188 /* Extract interface number */
189 i = strtol(env + 3, &end, 10);
190 if (end == (env + 3))
191 /* 'ethaddr' means interface 0 address */
199 str = ub_env_get(env);
201 /* Convert macaddr string into a vector of uints */
202 fdt_strtovectx(str, &tmp_addr, 6, sizeof(uint8_t));
205 strncpy(ethstr + 8, env + 3, i);
207 /* Set actual property to a value from vect */
208 fdt_setprop(fdtp, fdt_path_offset(fdtp, ethstr),
209 "local-mac-address", &tmp_addr, 6 * sizeof(uint8_t));
211 /* Clear ethernet..XXXX.. string */
212 bzero(ethstr + 8, len - 8);
219 fixup_cpubusfreqs(unsigned long cpufreq, unsigned long busfreq)
221 int lo, o = 0, o2, maxo = 0, depth;
222 const uint32_t zero = 0;
224 /* We want to modify every subnode of /cpus */
225 o = fdt_path_offset(fdtp, "/cpus");
227 /* maxo should contain offset of node next to /cpus */
231 maxo = fdt_next_node(fdtp, maxo, &depth);
233 /* Find CPU frequency properties */
234 o = fdt_node_offset_by_prop_value(fdtp, o, "clock-frequency",
235 &zero, sizeof(uint32_t));
237 o2 = fdt_node_offset_by_prop_value(fdtp, o, "bus-frequency", &zero,
242 while (o != -FDT_ERR_NOTFOUND && o2 != -FDT_ERR_NOTFOUND) {
244 o = fdt_node_offset_by_prop_value(fdtp, lo,
245 "clock-frequency", &zero, sizeof(uint32_t));
247 o2 = fdt_node_offset_by_prop_value(fdtp, lo, "bus-frequency",
248 &zero, sizeof(uint32_t));
250 /* We're only interested in /cpus subnode(s) */
254 fdt_setprop_inplace_cell(fdtp, lo, "clock-frequency",
257 fdt_setprop_inplace_cell(fdtp, lo, "bus-frequency",
265 fdt_reg_valid(uint32_t *reg, int len, int addr_cells, int size_cells)
267 int cells_in_tuple, i, tuples, tuple_size;
268 uint32_t cur_start, cur_size;
270 cells_in_tuple = (addr_cells + size_cells);
271 tuple_size = cells_in_tuple * sizeof(uint32_t);
272 tuples = len / tuple_size;
276 for (i = 0; i < tuples; i++) {
278 cur_start = fdt64_to_cpu(reg[i * cells_in_tuple]);
280 cur_start = fdt32_to_cpu(reg[i * cells_in_tuple]);
283 cur_size = fdt64_to_cpu(reg[i * cells_in_tuple + 2]);
285 cur_size = fdt32_to_cpu(reg[i * cells_in_tuple + 1]);
290 debugf(" reg#%d (start: 0x%0x size: 0x%0x) valid!\n",
291 i, cur_start, cur_size);
297 fixup_memory(struct sys_info *si)
299 struct mem_region *curmr;
300 uint32_t addr_cells, size_cells;
301 uint32_t *addr_cellsp, *reg, *size_cellsp;
302 int err, i, len, memory, realmrno, root;
305 root = fdt_path_offset(fdtp, "/");
307 sprintf(command_errbuf, "Could not find root node !");
311 memory = fdt_path_offset(fdtp, "/memory");
313 /* Create proper '/memory' node. */
314 memory = fdt_add_subnode(fdtp, root, "memory");
316 sprintf(command_errbuf, "Could not fixup '/memory' "
317 "node, error code : %d!\n", memory);
321 err = fdt_setprop(fdtp, memory, "device_type", "memory",
328 addr_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#address-cells",
330 size_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#size-cells", NULL);
332 if (addr_cellsp == NULL || size_cellsp == NULL) {
333 sprintf(command_errbuf, "Could not fixup '/memory' node : "
334 "%s %s property not found in root node!\n",
335 (!addr_cellsp) ? "#address-cells" : "",
336 (!size_cellsp) ? "#size-cells" : "");
340 addr_cells = fdt32_to_cpu(*addr_cellsp);
341 size_cells = fdt32_to_cpu(*size_cellsp);
343 /* Count valid memory regions entries in sysinfo. */
344 realmrno = si->mr_no;
345 for (i = 0; i < si->mr_no; i++)
346 if (si->mr[i].start == 0 && si->mr[i].size == 0)
350 sprintf(command_errbuf, "Could not fixup '/memory' node : "
351 "sysinfo doesn't contain valid memory regions info!\n");
355 if ((reg = (uint32_t *)fdt_getprop(fdtp, memory, "reg",
358 if (fdt_reg_valid(reg, len, addr_cells, size_cells) == 0)
360 * Do not apply fixup if existing 'reg' property
366 len = (addr_cells + size_cells) * realmrno * sizeof(uint32_t);
367 sb = buf = (uint8_t *)malloc(len);
373 for (i = 0; i < si->mr_no; i++) {
375 if (curmr->size != 0) {
376 /* Ensure endianess, and put cells into a buffer */
379 cpu_to_fdt64(curmr->start);
382 cpu_to_fdt32(curmr->start);
384 buf += sizeof(uint32_t) * addr_cells;
387 cpu_to_fdt64(curmr->size);
390 cpu_to_fdt32(curmr->size);
392 buf += sizeof(uint32_t) * size_cells;
397 if ((err = fdt_setprop(fdtp, memory, "reg", sb, len)) < 0)
398 sprintf(command_errbuf, "Could not fixup '/memory' node.\n");
402 fixup_stdout(const char *env)
408 const struct fdt_property *prop;
411 str = ub_env_get(env);
412 ptr = (char *)str + strlen(str) - 1;
413 while (ptr > str && isdigit(*(str - 1)))
419 serialno = (int)strtol(ptr, NULL, 0);
420 no = fdt_path_offset(fdtp, "/chosen");
424 prop = fdt_get_property(fdtp, no, "stdout", &len);
426 /* If /chosen/stdout does not extist, create it */
427 if (prop == NULL || (prop != NULL && len == 0)) {
429 bzero(tmp, 10 * sizeof(char));
430 strcpy((char *)&tmp, "serial");
432 /* Serial number too long */
435 strncpy((char *)tmp + 6, ptr, 3);
436 sero = fdt_path_offset(fdtp, (const char *)tmp);
439 * If serial device we're trying to assign
440 * stdout to doesn't exist in DT -- return.
444 fdt_setprop(fdtp, no, "stdout", &tmp,
445 strlen((char *)&tmp) + 1);
446 fdt_setprop(fdtp, no, "stdin", &tmp,
447 strlen((char *)&tmp) + 1);
456 int chosen, err, eth_no, len;
465 err = fdt_setup_fdtp();
467 sprintf(command_errbuf, "Could not perform blob "
468 "fixups. Error code: %d\n", err);
473 /* Create /chosen node (if not exists) */
474 if ((chosen = fdt_subnode_offset(fdtp, 0, "chosen")) ==
476 chosen = fdt_add_subnode(fdtp, 0, "chosen");
478 /* Value assigned to fixup-applied does not matter. */
479 if (fdt_getprop(fdtp, chosen, "fixup-applied", NULL))
482 /* Acquire sys_info */
483 si = ub_get_sys_info();
485 while ((env = ub_env_enum(env)) != NULL) {
486 if (strncmp(env, "eth", 3) == 0 &&
487 strncmp(env + (strlen(env) - 4), "addr", 4) == 0) {
489 * Handle Ethernet addrs: parse uboot env eth%daddr
494 * Check how many chars we will need to store
495 * maximal eth iface number.
497 len = strlen(STRINGIFY(TMP_MAX_ETH)) +
501 * Reserve mem for string "ethernet" and len
502 * chars for iface no.
504 ethstr = (char *)malloc(len * sizeof(char));
505 bzero(ethstr, len * sizeof(char));
506 strcpy(ethstr, "ethernet0");
510 fixup_ethernet(env, ethstr, ð_no, len);
512 } else if (strcmp(env, "consoledev") == 0)
516 /* Modify cpu(s) and bus clock frequenties in /cpus node [Hz] */
517 fixup_cpubusfreqs(si->clk_cpu, si->clk_bus);
519 /* Fixup memory regions */
522 fdt_setprop(fdtp, chosen, "fixup-applied", NULL, 0);
528 command_fdt_internal(int argc, char *argv[])
535 command_errmsg = "usage is 'fdt <command> [<args>]";
540 * Check if uboot env vars were parsed already. If not, do it now.
545 * Validate fdt <command>.
547 cmd = strdup(argv[1]);
550 while (!(commands[i].name == NULL)) {
551 if (strcmp(cmd, commands[i].name) == 0) {
553 cmdh = commands[i].handler;
559 command_errmsg = "unknown command";
564 if (fdt_setup_fdtp())
568 * Call command handler.
570 err = (*cmdh)(argc, argv);
576 fdt_cmd_cd(int argc, char *argv[])
579 char tmp[FDT_CWD_LEN];
582 path = (argc > 2) ? argv[2] : "/";
584 if (path[0] == '/') {
586 if (len >= FDT_CWD_LEN)
589 /* Handle path specification relative to cwd */
590 len = strlen(cwd) + strlen(path) + 1;
591 if (len >= FDT_CWD_LEN)
600 o = fdt_path_offset(fdtp, path);
602 sprintf(command_errbuf, "could not find node: '%s'", path);
610 sprintf(command_errbuf, "path too long: %d, max allowed: %d",
611 len, FDT_CWD_LEN - 1);
616 fdt_cmd_hdr(int argc __unused, char *argv[] __unused)
622 command_errmsg = "no device tree blob pointer?!";
626 ver = fdt_version(fdtp);
628 sprintf(line, "\nFlattened device tree header (%p):\n", fdtp);
630 sprintf(line, " magic = 0x%08x\n", fdt_magic(fdtp));
632 sprintf(line, " size = %d\n", fdt_totalsize(fdtp));
634 sprintf(line, " off_dt_struct = 0x%08x\n",
635 fdt_off_dt_struct(fdtp));
637 sprintf(line, " off_dt_strings = 0x%08x\n",
638 fdt_off_dt_strings(fdtp));
640 sprintf(line, " off_mem_rsvmap = 0x%08x\n",
641 fdt_off_mem_rsvmap(fdtp));
643 sprintf(line, " version = %d\n", ver);
645 sprintf(line, " last compatible version = %d\n",
646 fdt_last_comp_version(fdtp));
649 sprintf(line, " boot_cpuid = %d\n",
650 fdt_boot_cpuid_phys(fdtp));
654 sprintf(line, " size_dt_strings = %d\n",
655 fdt_size_dt_strings(fdtp));
659 sprintf(line, " size_dt_struct = %d\n",
660 fdt_size_dt_struct(fdtp));
669 fdt_cmd_ls(int argc, char *argv[])
671 const char *prevname[FDT_MAX_DEPTH] = { NULL };
674 int i, o, depth, len;
676 path = (argc > 2) ? argv[2] : NULL;
680 o = fdt_path_offset(fdtp, path);
682 sprintf(command_errbuf, "could not find node: '%s'", path);
687 (o >= 0) && (depth >= 0);
688 o = fdt_next_node(fdtp, o, &depth)) {
690 name = fdt_get_name(fdtp, o, &len);
692 if (depth > FDT_MAX_DEPTH) {
693 printf("max depth exceeded: %d\n", depth);
697 prevname[depth] = name;
699 /* Skip root (i = 1) when printing devices */
700 for (i = 1; i <= depth; i++) {
701 if (prevname[i] == NULL)
704 if (strcmp(cwd, "/") == 0)
706 printf("%s", prevname[i]);
718 return (c >= ' ' && c <= 0x7e);
722 fdt_isprint(const void *data, int len, int *count)
731 d = (const char *)data;
732 if (d[len - 1] != '\0')
737 for (i = 0; i < len; i++) {
739 if (isprint(ch) || (ch == '\0' && i > 0)) {
754 fdt_data_str(const void *data, int len, int count, char **buf)
761 * Calculate the length for the string and allocate memory.
763 * Note len already includes at least one terminator.
768 * Each token had already a terminator buried in 'len', but we
769 * only need one eventually, don't count space for these.
773 /* Each consecutive token requires a ", " separator. */
776 /* Space for surrounding double quotes. */
779 b = (char *)malloc(l);
785 * Now that we have space, format the string.
789 d = (const char *)data + i;
792 sprintf(tmp, "\"%s\"%s", d,
793 (i + l) < len ? ", " : "");
805 fdt_data_cell(const void *data, int len, char **buf)
811 /* Number of cells */
815 * Calculate the length for the string and allocate memory.
818 /* Each byte translates to 2 output characters */
821 /* Each consecutive cell requires a " " separator. */
822 l += (count - 1) * 1;
824 /* Each cell will have a "0x" prefix */
826 /* Space for surrounding <> and terminator */
829 b = (char *)malloc(l);
836 for (i = 0; i < len; i += 4) {
837 c = (const uint32_t *)((const uint8_t *)data + i);
838 sprintf(tmp, "0x%08x%s", fdt32_to_cpu(*c),
839 i < (len - 4) ? " " : "");
849 fdt_data_bytes(const void *data, int len, char **buf)
856 * Calculate the length for the string and allocate memory.
859 /* Each byte translates to 2 output characters */
862 /* Each consecutive byte requires a " " separator. */
864 /* Each byte will have a "0x" prefix */
866 /* Space for surrounding [] and terminator. */
869 b = (char *)malloc(l);
876 for (i = 0, d = data; i < len; i++) {
877 sprintf(tmp, "0x%02x%s", d[i], i < len - 1 ? " " : "");
887 fdt_data_fmt(const void *data, int len, char **buf)
896 if (fdt_isprint(data, len, &count))
897 return (fdt_data_str(data, len, count, buf));
899 else if ((len % 4) == 0)
900 return (fdt_data_cell(data, len, buf));
903 return (fdt_data_bytes(data, len, buf));
910 const struct fdt_property *prop;
916 prop = fdt_offset_ptr(fdtp, offset, sizeof(*prop));
920 name = fdt_string(fdtp, fdt32_to_cpu(prop->nameoff));
921 len = fdt32_to_cpu(prop->len);
926 /* Property without value */
927 line = (char *)malloc(strlen(name) + 2);
932 sprintf(line, "%s\n", name);
937 * Process property with value
941 if (fdt_data_fmt(data, len, &buf) != 0) {
946 line = (char *)malloc(strlen(name) + strlen(FDT_PROP_SEP) +
949 sprintf(command_errbuf, "could not allocate space for string");
954 sprintf(line, "%s" FDT_PROP_SEP "%s\n", name, buf);
972 fdt_modprop(int nodeoff, char *propname, void *value, char mode)
977 const struct fdt_property *p;
979 p = fdt_get_property(fdtp, nodeoff, propname, NULL);
983 /* Adding inexistant value in mode 1 is forbidden */
984 sprintf(command_errbuf, "property already exists!");
987 } else if (mode == 0) {
988 sprintf(command_errbuf, "property does not exist!");
1001 len = fdt_strtovect(buf, (void *)&cells, 100,
1004 rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
1005 len * sizeof(uint32_t));
1009 len = fdt_strtovect(buf, (void *)&cells, 100,
1012 rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
1013 len * sizeof(uint8_t));
1017 /* Default -- string */
1018 rv = fdt_setprop_string(fdtp, nodeoff, propname, value);
1025 /* Merge strings from argv into a single string */
1027 fdt_merge_strings(int argc, char *argv[], int start, char **buffer)
1035 for (i = start; i < argc; i++)
1036 sz += strlen(argv[i]);
1038 /* Additional bytes for whitespaces between args */
1041 buf = (char *)malloc(sizeof(char) * sz);
1042 bzero(buf, sizeof(char) * sz);
1045 sprintf(command_errbuf, "could not allocate space "
1051 for (i = start, idx = 0; i < argc; i++) {
1052 strcpy(buf + idx, argv[i]);
1053 idx += strlen(argv[i]);
1062 /* Extract offset and name of node/property from a given path */
1064 fdt_extract_nameloc(char **pathp, char **namep, int *nodeoff)
1067 char *path = *pathp, *name = NULL, *subpath = NULL;
1069 subpath = strrchr(path, '/');
1070 if (subpath == NULL) {
1071 o = fdt_path_offset(fdtp, cwd);
1073 path = (char *)&cwd;
1076 if (strlen(path) == 0)
1080 o = fdt_path_offset(fdtp, path);
1083 if (strlen(name) == 0) {
1084 sprintf(command_errbuf, "name not specified");
1088 sprintf(command_errbuf, "could not find node: '%s'", path);
1098 fdt_cmd_prop(int argc, char *argv[])
1100 char *path, *propname, *value;
1101 int o, next, depth, rv;
1104 path = (argc > 2) ? argv[2] : NULL;
1109 /* Merge property value strings into one */
1110 if (fdt_merge_strings(argc, argv, 3, &value) != 0)
1121 /* If value is specified -- try to modify prop. */
1122 if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1125 rv = fdt_modprop(o, propname, value, 0);
1131 /* User wants to display properties */
1132 o = fdt_path_offset(fdtp, path);
1135 sprintf(command_errbuf, "could not find node: '%s'", path);
1141 while (depth >= 0) {
1142 tag = fdt_next_tag(fdtp, o, &next);
1148 /* Don't process properties of nested nodes */
1151 if (fdt_prop(o) != 0) {
1152 sprintf(command_errbuf, "could not process "
1158 case FDT_BEGIN_NODE:
1160 if (depth > FDT_MAX_DEPTH) {
1161 printf("warning: nesting too deep: %d\n",
1170 * This is the end of our starting node, force
1183 fdt_cmd_mkprop(int argc, char *argv[])
1186 char *path, *propname, *value;
1188 path = (argc > 2) ? argv[2] : NULL;
1193 /* Merge property value strings into one */
1194 if (fdt_merge_strings(argc, argv, 3, &value) != 0)
1199 if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1202 if (fdt_modprop(o, propname, value, 1))
1209 fdt_cmd_rm(int argc, char *argv[])
1212 char *path = NULL, *propname;
1217 sprintf(command_errbuf, "no node/property name specified");
1221 o = fdt_path_offset(fdtp, path);
1223 /* If node not found -- try to find & delete property */
1224 if (fdt_extract_nameloc(&path, &propname, &o) != 0)
1227 if ((rv = fdt_delprop(fdtp, o, propname)) != 0) {
1228 sprintf(command_errbuf, "could not delete"
1229 "%s\n", (rv == -FDT_ERR_NOTFOUND) ?
1230 "(property/node does not exist)" : "");
1236 /* If node exists -- remove node */
1237 rv = fdt_del_node(fdtp, o);
1239 sprintf(command_errbuf, "could not delete node");
1246 fdt_cmd_mknode(int argc, char *argv[])
1249 char *path = NULL, *nodename = NULL;
1254 sprintf(command_errbuf, "no node name specified");
1258 if (fdt_extract_nameloc(&path, &nodename, &o) != 0)
1261 rv = fdt_add_subnode(fdtp, o, nodename);
1264 sprintf(command_errbuf, "could not delete node %s\n",
1265 (rv == -FDT_ERR_NOTFOUND) ?
1266 "(node does not exist)" : "");
1273 fdt_cmd_pwd(int argc, char *argv[])
1278 sprintf(line, "%s\n", cwd);
1285 fdt_cmd_nyi(int argc, char *argv[])
1288 printf("command not yet implemented\n");