1/* 2 * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of 7 * the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 17 * MA 02111-1307 USA 18 */ 19 20#include <assert.h> 21#include <ctype.h> 22#include <getopt.h> 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26 27#include <libfdt.h> 28 29#include "util.h" 30 31/* These are the operations we support */ 32enum oper_type { 33 OPER_WRITE_PROP, /* Write a property in a node */ 34 OPER_CREATE_NODE, /* Create a new node */ 35 OPER_REMOVE_NODE, /* Delete a node */ 36 OPER_DELETE_PROP, /* Delete a property in a node */ 37}; 38 39struct display_info { 40 enum oper_type oper; /* operation to perform */ 41 int type; /* data type (s/i/u/x or 0 for default) */ 42 int size; /* data size (1/2/4) */ 43 int verbose; /* verbose output */ 44 int auto_path; /* automatically create all path components */ 45}; 46 47 48/** 49 * Report an error with a particular node. 50 * 51 * @param name Node name to report error on 52 * @param namelen Length of node name, or -1 to use entire string 53 * @param err Error number to report (-FDT_ERR_...) 54 */ 55static void report_error(const char *name, int namelen, int err) 56{ 57 if (namelen == -1) 58 namelen = strlen(name); 59 fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name, 60 fdt_strerror(err)); 61} 62 63/** 64 * Encode a series of arguments in a property value. 65 * 66 * @param disp Display information / options 67 * @param arg List of arguments from command line 68 * @param arg_count Number of arguments (may be 0) 69 * @param valuep Returns buffer containing value 70 * @param value_len Returns length of value encoded 71 */ 72static int encode_value(struct display_info *disp, char **arg, int arg_count, 73 char **valuep, int *value_len) 74{ 75 char *value = NULL; /* holding area for value */ 76 int value_size = 0; /* size of holding area */ 77 char *ptr; /* pointer to current value position */ 78 int len; /* length of this cell/string/byte */ 79 int ival; 80 int upto; /* the number of bytes we have written to buf */ 81 char fmt[3]; 82 83 upto = 0; 84 85 if (disp->verbose) 86 fprintf(stderr, "Decoding value:\n"); 87 88 fmt[0] = '%'; 89 fmt[1] = disp->type ? disp->type : 'd'; 90 fmt[2] = '\0'; 91 for (; arg_count > 0; arg++, arg_count--, upto += len) { 92 /* assume integer unless told otherwise */ 93 if (disp->type == 's') 94 len = strlen(*arg) + 1; 95 else 96 len = disp->size == -1 ? 4 : disp->size; 97 98 /* enlarge our value buffer by a suitable margin if needed */ 99 if (upto + len > value_size) { 100 value_size = (upto + len) + 500; 101 value = xrealloc(value, value_size); 102 } 103 104 ptr = value + upto; 105 if (disp->type == 's') { 106 memcpy(ptr, *arg, len); 107 if (disp->verbose) 108 fprintf(stderr, "\tstring: '%s'\n", ptr); 109 } else { 110 fdt32_t *iptr = (fdt32_t *)ptr; 111 sscanf(*arg, fmt, &ival); 112 if (len == 4) 113 *iptr = cpu_to_fdt32(ival); 114 else 115 *ptr = (uint8_t)ival; 116 if (disp->verbose) { 117 fprintf(stderr, "\t%s: %d\n", 118 disp->size == 1 ? "byte" : 119 disp->size == 2 ? "short" : "int", 120 ival); 121 } 122 } 123 } 124 *value_len = upto; 125 *valuep = value; 126 if (disp->verbose) 127 fprintf(stderr, "Value size %d\n", upto); 128 return 0; 129} 130 131#define ALIGN(x) (((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1)) 132 133static char *_realloc_fdt(char *fdt, int delta) 134{ 135 int new_sz = fdt_totalsize(fdt) + delta; 136 fdt = xrealloc(fdt, new_sz); 137 fdt_open_into(fdt, fdt, new_sz); 138 return fdt; 139} 140 141static char *realloc_node(char *fdt, const char *name) 142{ 143 int delta; 144 /* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */ 145 delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1) 146 + FDT_TAGSIZE; 147 return _realloc_fdt(fdt, delta); 148} 149 150static char *realloc_property(char *fdt, int nodeoffset, 151 const char *name, int newlen) 152{ 153 int delta = 0; 154 int oldlen = 0; 155 156 if (!fdt_get_property(fdt, nodeoffset, name, &oldlen)) 157 /* strings + property header */ 158 delta = sizeof(struct fdt_property) + strlen(name) + 1; 159 160 if (newlen > oldlen) 161 /* actual value in off_struct */ 162 delta += ALIGN(newlen) - ALIGN(oldlen); 163 164 return _realloc_fdt(fdt, delta); 165} 166 167static int store_key_value(char **blob, const char *node_name, 168 const char *property, const char *buf, int len) 169{ 170 int node; 171 int err; 172 173 node = fdt_path_offset(*blob, node_name); 174 if (node < 0) { 175 report_error(node_name, -1, node); 176 return -1; 177 } 178 179 err = fdt_setprop(*blob, node, property, buf, len); 180 if (err == -FDT_ERR_NOSPACE) { 181 *blob = realloc_property(*blob, node, property, len); 182 err = fdt_setprop(*blob, node, property, buf, len); 183 } 184 if (err) { 185 report_error(property, -1, err); 186 return -1; 187 } 188 return 0; 189} 190 191/** 192 * Create paths as needed for all components of a path 193 * 194 * Any components of the path that do not exist are created. Errors are 195 * reported. 196 * 197 * @param blob FDT blob to write into 198 * @param in_path Path to process 199 * @return 0 if ok, -1 on error 200 */ 201static int create_paths(char **blob, const char *in_path) 202{ 203 const char *path = in_path; 204 const char *sep; 205 int node, offset = 0; 206 207 /* skip leading '/' */ 208 while (*path == '/') 209 path++; 210 211 for (sep = path; *sep; path = sep + 1, offset = node) { 212 /* equivalent to strchrnul(), but it requires _GNU_SOURCE */ 213 sep = strchr(path, '/'); 214 if (!sep) 215 sep = path + strlen(path); 216 217 node = fdt_subnode_offset_namelen(*blob, offset, path, 218 sep - path); 219 if (node == -FDT_ERR_NOTFOUND) { 220 *blob = realloc_node(*blob, path); 221 node = fdt_add_subnode_namelen(*blob, offset, path, 222 sep - path); 223 } 224 if (node < 0) { 225 report_error(path, sep - path, node); 226 return -1; 227 } 228 } 229 230 return 0; 231} 232 233/** 234 * Create a new node in the fdt. 235 * 236 * This will overwrite the node_name string. Any error is reported. 237 * 238 * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this. 239 * 240 * @param blob FDT blob to write into 241 * @param node_name Name of node to create 242 * @return new node offset if found, or -1 on failure 243 */ 244static int create_node(char **blob, const char *node_name) 245{ 246 int node = 0; 247 char *p; 248 249 p = strrchr(node_name, '/'); 250 if (!p) { 251 report_error(node_name, -1, -FDT_ERR_BADPATH); 252 return -1; 253 } 254 *p = '\0'; 255 256 *blob = realloc_node(*blob, p + 1); 257 258 if (p > node_name) { 259 node = fdt_path_offset(*blob, node_name); 260 if (node < 0) { 261 report_error(node_name, -1, node); 262 return -1; 263 } 264 } 265 266 node = fdt_add_subnode(*blob, node, p + 1); 267 if (node < 0) { 268 report_error(p + 1, -1, node); 269 return -1; 270 } 271 272 return 0; 273} 274 275/** 276 * Delete a property of a node in the fdt. 277 * 278 * @param blob FDT blob to write into 279 * @param node_name Path to node containing the property to delete 280 * @param prop_name Name of property to delete 281 * @return 0 on success, or -1 on failure 282 */ 283static int delete_prop(char *blob, const char *node_name, const char *prop_name) 284{ 285 int node = 0; 286 287 node = fdt_path_offset(blob, node_name); 288 if (node < 0) { 289 report_error(node_name, -1, node); 290 return -1; 291 } 292 293 node = fdt_delprop(blob, node, prop_name); 294 if (node < 0) { 295 report_error(node_name, -1, node); 296 return -1; 297 } 298 299 return 0; 300} 301 302/** 303 * Delete a node in the fdt. 304 * 305 * @param blob FDT blob to write into 306 * @param node_name Name of node to delete 307 * @return 0 on success, or -1 on failure 308 */ 309static int delete_node(char *blob, const char *node_name) 310{ 311 int node = 0; 312 313 node = fdt_path_offset(blob, node_name); 314 if (node < 0) { 315 report_error(node_name, -1, node); 316 return -1; 317 } 318 319 node = fdt_del_node(blob, node); 320 if (node < 0) { 321 report_error(node_name, -1, node); 322 return -1; 323 } 324 325 return 0; 326} 327 328static int do_fdtput(struct display_info *disp, const char *filename, 329 char **arg, int arg_count) 330{ 331 char *value = NULL; 332 char *blob; 333 char *node; 334 int len, ret = 0; 335 336 blob = utilfdt_read(filename); 337 if (!blob) 338 return -1; 339 340 switch (disp->oper) { 341 case OPER_WRITE_PROP: 342 /* 343 * Convert the arguments into a single binary value, then 344 * store them into the property. 345 */ 346 assert(arg_count >= 2); 347 if (disp->auto_path && create_paths(&blob, *arg)) 348 return -1; 349 if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) || 350 store_key_value(&blob, *arg, arg[1], value, len)) 351 ret = -1; 352 break; 353 case OPER_CREATE_NODE: 354 for (; ret >= 0 && arg_count--; arg++) { 355 if (disp->auto_path) 356 ret = create_paths(&blob, *arg); 357 else 358 ret = create_node(&blob, *arg); 359 } 360 break; 361 case OPER_REMOVE_NODE: 362 for (; ret >= 0 && arg_count--; arg++) 363 ret = delete_node(blob, *arg); 364 break; 365 case OPER_DELETE_PROP: 366 node = *arg; 367 for (arg++; ret >= 0 && arg_count-- > 1; arg++) 368 ret = delete_prop(blob, node, *arg); 369 break; 370 } 371 if (ret >= 0) { 372 fdt_pack(blob); 373 ret = utilfdt_write(filename, blob); 374 } 375 376 free(blob); 377 378 if (value) { 379 free(value); 380 } 381 382 return ret; 383} 384 385/* Usage related data. */ 386static const char usage_synopsis[] = 387 "write a property value to a device tree\n" 388 " fdtput <options> <dt file> <node> <property> [<value>...]\n" 389 " fdtput -c <options> <dt file> [<node>...]\n" 390 " fdtput -r <options> <dt file> [<node>...]\n" 391 " fdtput -d <options> <dt file> <node> [<property>...]\n" 392 "\n" 393 "The command line arguments are joined together into a single value.\n" 394 USAGE_TYPE_MSG; 395static const char usage_short_opts[] = "crdpt:v" USAGE_COMMON_SHORT_OPTS; 396static struct option const usage_long_opts[] = { 397 {"create", no_argument, NULL, 'c'}, 398 {"remove", no_argument, NULL, 'r'}, 399 {"delete", no_argument, NULL, 'd'}, 400 {"auto-path", no_argument, NULL, 'p'}, 401 {"type", a_argument, NULL, 't'}, 402 {"verbose", no_argument, NULL, 'v'}, 403 USAGE_COMMON_LONG_OPTS, 404}; 405static const char * const usage_opts_help[] = { 406 "Create nodes if they don't already exist", 407 "Delete nodes (and any subnodes) if they already exist", 408 "Delete properties if they already exist", 409 "Automatically create nodes as needed for the node path", 410 "Type of data", 411 "Display each value decoded from command line", 412 USAGE_COMMON_OPTS_HELP 413}; 414 415int main(int argc, char *argv[]) 416{ 417 int opt; 418 struct display_info disp; 419 char *filename = NULL; 420 421 memset(&disp, '\0', sizeof(disp)); 422 disp.size = -1; 423 disp.oper = OPER_WRITE_PROP; 424 while ((opt = util_getopt_long()) != EOF) { 425 /* 426 * TODO: add options to: 427 * - rename node 428 * - pack fdt before writing 429 * - set amount of free space when writing 430 */ 431 switch (opt) { 432 case_USAGE_COMMON_FLAGS 433 434 case 'c': 435 disp.oper = OPER_CREATE_NODE; 436 break; 437 case 'r': 438 disp.oper = OPER_REMOVE_NODE; 439 break; 440 case 'd': 441 disp.oper = OPER_DELETE_PROP; 442 break; 443 case 'p': 444 disp.auto_path = 1; 445 break; 446 case 't': 447 if (utilfdt_decode_type(optarg, &disp.type, 448 &disp.size)) 449 usage("Invalid type string"); 450 break; 451 452 case 'v': 453 disp.verbose = 1; 454 break; 455 } 456 } 457 458 if (optind < argc) 459 filename = argv[optind++]; 460 if (!filename) 461 usage("missing filename"); 462 463 argv += optind; 464 argc -= optind; 465 466 if (disp.oper == OPER_WRITE_PROP) { 467 if (argc < 1) 468 usage("missing node"); 469 if (argc < 2) 470 usage("missing property"); 471 } 472 473 if (disp.oper == OPER_DELETE_PROP) 474 if (argc < 1) 475 usage("missing node"); 476 477 if (do_fdtput(&disp, filename, argv, argc)) 478 return 1; 479 return 0; 480} 481